END_TEST START_TEST (encode_supports_telnet_iac_test) { register unsigned int i; int res; const char *charset, *encoding; const char *non_iac_encodings[] = { "cp1251", "cp866", "iso-8859-1", "koi8-r", "windows-1251", NULL }; res = pr_encode_supports_telnet_iac(); fail_unless(res == TRUE, "Expected TRUE, got %d", res); charset = "us-ascii"; for (i = 0; non_iac_encodings[i]; i++) { encoding = non_iac_encodings[i]; res = pr_encode_set_charset_encoding(charset, encoding); fail_unless(res == 0, "Failed to set charset '%s', encoding '%s': %s", charset, encoding, strerror(errno)); res = pr_encode_supports_telnet_iac(); fail_unless(res == FALSE, "Expected FALSE, got %d", res); } encoding = "utf-8"; res = pr_encode_set_charset_encoding(charset, encoding); fail_unless(res == 0, "Failed to set charset '%s', encoding '%s': %s", charset, encoding, strerror(errno)); }
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; }