char *pr_netio_telnet_gets(char *buf, size_t buflen, pr_netio_stream_t *in_nstrm, pr_netio_stream_t *out_nstrm) { char *bp = buf; unsigned char cp; int toread, handle_iac = TRUE, saw_newline = FALSE; pr_buffer_t *pbuf = NULL; if (buflen == 0 || in_nstrm == NULL || out_nstrm == NULL) { errno = EINVAL; return NULL; } #ifdef PR_USE_NLS handle_iac = pr_encode_supports_telnet_iac(); #endif /* PR_USE_NLS */ buflen--; if (in_nstrm->strm_buf) { pbuf = in_nstrm->strm_buf; } else { pbuf = pr_netio_buffer_alloc(in_nstrm); } while (buflen > 0) { pr_signals_handle(); /* Is the buffer empty? */ if (pbuf->current == NULL || pbuf->remaining == pbuf->buflen) { toread = pr_netio_read(in_nstrm, pbuf->buf, (buflen < pbuf->buflen ? buflen : pbuf->buflen), 1); if (toread <= 0) { if (bp != buf) { *bp = '\0'; return buf; } return NULL; } pbuf->remaining = pbuf->buflen - toread; pbuf->current = pbuf->buf; /* Before we begin iterating through the data read in from the * network, handing any Telnet characters and such, generate an event * for any listeners which may want to examine this data as well. */ pr_event_generate("core.ctrl-read", pbuf); } toread = pbuf->buflen - pbuf->remaining; while (buflen > 0 && toread > 0 && *pbuf->current != '\n' && toread--) { pr_signals_handle(); cp = *pbuf->current++; pbuf->remaining++; if (handle_iac == TRUE) { switch (telnet_mode) { case TELNET_IAC: switch (cp) { case TELNET_WILL: case TELNET_WONT: case TELNET_DO: case TELNET_DONT: case TELNET_IP: case TELNET_DM: /* Why do we do this crazy thing where we set the "telnet mode" * to be the action, and let the while loop, on the next pass, * handle that action? It's because we don't know, right now, * whether there actually a "next byte" in the input buffer. * There _should_ be -- but we can't be sure. And that next * byte is needed for properly responding with WONT/DONT * responses. */ telnet_mode = cp; continue; case TELNET_IAC: /* In this case, we know that the previous byte was TELNET_IAC, * and that the current byte is another TELNET_IAC. The * first TELNET_IAC thus "escapes" the second, telling us * that the current byte (TELNET_IAC) should be written out * as is (Bug#3697). */ telnet_mode = 0; break; default: /* In this case, we know that the previous byte was TELNET_IAC, * but the current byte is not a value we care about. So * write the TELNET_IAC into the output buffer, break out of * of the switch, and let that handle the writing of the * current byte into the output buffer. */ *bp++ = TELNET_IAC; buflen--; telnet_mode = 0; break; } break; case TELNET_WILL: case TELNET_WONT: pr_netio_printf(out_nstrm, "%c%c%c", TELNET_IAC, TELNET_DONT, cp); telnet_mode = 0; continue; case TELNET_DO: case TELNET_DONT: pr_netio_printf(out_nstrm, "%c%c%c", TELNET_IAC, TELNET_WONT, cp); telnet_mode = 0; continue; case TELNET_IP: case TELNET_DM: default: if (cp == TELNET_IAC) { telnet_mode = cp; continue; } break; } } /* In the situation where the previous byte was an IAC, we wrote IAC * into the output buffer, and decremented buflen (size of the output * buffer remaining). Thus we need to check here if buflen is zero, * before trying to decrement buflen again (and possibly underflowing * the buflen size_t data type). */ if (buflen == 0) { break; } *bp++ = cp; buflen--; } if (buflen > 0 && toread > 0 && *pbuf->current == '\n') { buflen--; toread--; *bp++ = *pbuf->current++; pbuf->remaining++; saw_newline = TRUE; break; } if (toread == 0) { /* No more input? Set pbuf->current to null, so that at the top of * the loop, we read more. */ pbuf->current = NULL; } } if (!saw_newline) { /* If we haven't seen a newline, then assume the client is deliberately * sending a too-long command, trying to exploit buffer sizes and make * the server make some possibly bad assumptions. */ properly_terminated_prev_command = FALSE; errno = E2BIG; return NULL; } if (!properly_terminated_prev_command) { properly_terminated_prev_command = TRUE; pr_log_pri(PR_LOG_NOTICE, "client sent too-long command, ignoring"); errno = E2BIG; return NULL; } properly_terminated_prev_command = TRUE; *bp = '\0'; return buf; }
static char *ftp_telnet_gets(char *buf, size_t buflen, pr_netio_stream_t *nstrm, conn_t *conn) { char *buf_ptr = buf; unsigned char cp; int nread, saw_newline = FALSE; pr_buffer_t *pbuf = NULL; if (buflen == 0) { errno = EINVAL; return NULL; } buflen--; if (nstrm->strm_buf != NULL) { pbuf = nstrm->strm_buf; } else { pbuf = pr_netio_buffer_alloc(nstrm); } while (buflen > 0) { /* Is the buffer empty? */ if (pbuf->current == NULL || pbuf->remaining == pbuf->buflen) { nread = proxy_netio_read(nstrm, pbuf->buf, (buflen < pbuf->buflen ? buflen : pbuf->buflen), 4); if (nread <= 0) { if (buf_ptr != buf) { *buf_ptr = '\0'; return buf; } if (nread == 0) { (void) pr_log_writefile(proxy_logfd, MOD_PROXY_VERSION, "read EOF from %s", conn->remote_name); errno = EPERM; } return NULL; } pbuf->remaining = pbuf->buflen - nread; pbuf->current = pbuf->buf; pr_event_generate("mod_proxy.ctrl-read", pbuf); } nread = pbuf->buflen - pbuf->remaining; /* Expensive copying of bytes while we look for the trailing LF. */ while (buflen > 0 && nread > 0 && *pbuf->current != '\n' && nread--) { pr_signals_handle(); cp = *pbuf->current++; pbuf->remaining++; *buf_ptr++ = cp; buflen--; } if (buflen > 0 && nread > 0 && *pbuf->current == '\n') { buflen--; nread--; *buf_ptr++ = *pbuf->current++; pbuf->remaining++; saw_newline = TRUE; break; } if (nread == 0) { pbuf->current = NULL; } } if (saw_newline == FALSE) { /* If we haven't seen a newline, then assume the server is deliberately * sending a too-long response, trying to exploit buffer sizes and make * the proxy make some possibly bad assumptions. */ errno = E2BIG; return NULL; } *buf_ptr = '\0'; return buf; }
char *pr_netio_gets(char *buf, size_t buflen, pr_netio_stream_t *nstrm) { char *bp = buf; int toread; pr_buffer_t *pbuf = NULL; if (buflen == 0) { errno = EINVAL; return NULL; } buflen--; if (nstrm->strm_buf) { pbuf = nstrm->strm_buf; } else { pbuf = pr_netio_buffer_alloc(nstrm); } while (buflen) { /* Is the buffer empty? */ if (!pbuf->current || pbuf->remaining == pbuf->buflen) { toread = pr_netio_read(nstrm, pbuf->buf, (buflen < pbuf->buflen ? buflen : pbuf->buflen), 1); if (toread <= 0) { if (bp != buf) { *bp = '\0'; return buf; } else return NULL; } pbuf->remaining = pbuf->buflen - toread; pbuf->current = pbuf->buf; pbuf->remaining = pbuf->buflen - toread; pbuf->current = pbuf->buf; /* Before we begin iterating through the data read in from the * network, generate an event for any listeners which may want to * examine this data as well. */ pr_event_generate("core.othr-read", pbuf); } toread = pbuf->buflen - pbuf->remaining; while (buflen && *pbuf->current != '\n' && toread--) { if (*pbuf->current & 0x80) pbuf->current++; else { *bp++ = *pbuf->current++; buflen--; } pbuf->remaining++; } if (buflen && toread && *pbuf->current == '\n') { buflen--; toread--; *bp++ = *pbuf->current++; pbuf->remaining++; break; } if (!toread) pbuf->current = NULL; } *bp = '\0'; return buf; }