diff options
Diffstat (limited to 'Source/CTest/Curl/telnet.c')
-rw-r--r-- | Source/CTest/Curl/telnet.c | 1213 |
1 files changed, 1213 insertions, 0 deletions
diff --git a/Source/CTest/Curl/telnet.c b/Source/CTest/Curl/telnet.c new file mode 100644 index 0000000..29c2110 --- /dev/null +++ b/Source/CTest/Curl/telnet.c @@ -0,0 +1,1213 @@ +/***************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 2000, Daniel Stenberg, <daniel@haxx.se>, et al. + * + * In order to be useful for every potential user, curl and libcurl are + * dual-licensed under the MPL and the MIT/X-derivate licenses. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the MPL or the MIT/X-derivate + * licenses. You may pick one of these licenses. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * $Id$ + *****************************************************************************/ + +#include "setup.h" + +/* -- WIN32 approved -- */ +#include <stdio.h> +#include <string.h> +#include <stdarg.h> +#include <stdlib.h> +#include <ctype.h> +#include <sys/types.h> +#include <sys/stat.h> + +#include <errno.h> + +#if defined(WIN32) && !defined(__GNUC__) || defined(__MINGW32__) +#include <winsock2.h> +#include <time.h> +#include <io.h> +#else +#ifdef HAVE_SYS_SOCKET_H +#include <sys/socket.h> +#endif +#include <netinet/in.h> +#include <sys/time.h> +#include <sys/resource.h> +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif +#include <netdb.h> +#ifdef HAVE_ARPA_INET_H +#include <arpa/inet.h> +#endif +#ifdef HAVE_NET_IF_H +#include <net/if.h> +#endif +#include <sys/ioctl.h> +#include <signal.h> + +#ifdef HAVE_SYS_PARAM_H +#include <sys/param.h> +#endif + +#ifdef HAVE_SYS_SELECT_H +#include <sys/select.h> +#endif + + +#endif + +#include "urldata.h" +#include <curl/curl.h> +#include "transfer.h" +#include "sendf.h" + +#define _MPRINTF_REPLACE /* use our functions only */ +#include <curl/mprintf.h> + +#define TELOPTS +#define TELCMDS + +#include "arpa_telnet.h" + +/* The last #include file should be: */ +#ifdef MALLOCDEBUG +#include "memdebug.h" +#endif + +#define SUBBUFSIZE 512 + +#define SB_CLEAR(x) x->subpointer = x->subbuffer; +#define SB_TERM(x) { x->subend = x->subpointer; SB_CLEAR(x); } +#define SB_ACCUM(x,c) if (x->subpointer < (x->subbuffer+sizeof x->subbuffer)) { \ + *x->subpointer++ = (c); \ + } + +#define SB_GET(x) ((*x->subpointer++)&0xff) +#define SB_PEEK(x) ((*x->subpointer)&0xff) +#define SB_EOF(x) (x->subpointer >= x->subend) +#define SB_LEN(x) (x->subend - x->subpointer) + +static +void telrcv(struct connectdata *, + unsigned char *inbuf, /* Data received from socket */ + int count); /* Number of bytes received */ + +static void printoption(struct SessionHandle *data, + const char *direction, + int cmd, int option); + +static void negotiate(struct connectdata *); +static void send_negotiation(struct connectdata *, int cmd, int option); +static void set_local_option(struct connectdata *, int cmd, int option); +static void set_remote_option(struct connectdata *, int cmd, int option); + +static void printsub(struct SessionHandle *data, + int direction, unsigned char *pointer, int length); +static void suboption(struct connectdata *); + +/* For negotiation compliant to RFC 1143 */ +#define NO 0 +#define YES 1 +#define WANTYES 2 +#define WANTNO 3 + +#define EMPTY 0 +#define OPPOSITE 1 + +/* + * Telnet receiver states for fsm + */ +typedef enum +{ + TS_DATA = 0, + TS_IAC, + TS_WILL, + TS_WONT, + TS_DO, + TS_DONT, + TS_CR, + TS_SB, /* sub-option collection */ + TS_SE /* looking for sub-option end */ +} TelnetReceive; + +struct TELNET { + int please_negotiate; + int already_negotiated; + int us[256]; + int usq[256]; + int us_preferred[256]; + int him[256]; + int himq[256]; + int him_preferred[256]; + char subopt_ttype[32]; /* Set with suboption TTYPE */ + char subopt_xdisploc[128]; /* Set with suboption XDISPLOC */ + struct curl_slist *telnet_vars; /* Environment variables */ + + /* suboptions */ + char subbuffer[SUBBUFSIZE]; + char *subpointer, *subend; /* buffer for sub-options */ + + TelnetReceive telrcv_state; +}; + +static +CURLcode init_telnet(struct connectdata *conn) +{ + struct TELNET *tn; + + tn = (struct TELNET *)malloc(sizeof(struct TELNET)); + if(!tn) + return CURLE_OUT_OF_MEMORY; + + conn->proto.telnet = (void *)tn; /* make us known */ + + memset(tn, 0, sizeof(struct TELNET)); + + tn->telrcv_state = TS_DATA; + + /* Init suboptions */ + SB_CLEAR(tn); + + /* Set all options to NO */ +#if 0 + /* NO is zero => default fill pattern */ + memset(tn->us, NO, 256); + memset(tn->usq, NO, 256); + memset(tn->us_preferred, NO, 256); + memset(tn->him, NO, 256); + memset(tn->himq, NO, 256); + memset(tn->him_preferred, NO, 256); +#endif + /* Set the options we want by default */ + tn->us_preferred[TELOPT_BINARY] = YES; + tn->us_preferred[TELOPT_SGA] = YES; + tn->him_preferred[TELOPT_BINARY] = YES; + tn->him_preferred[TELOPT_SGA] = YES; + + return CURLE_OK; +} + +static void negotiate(struct connectdata *conn) +{ + int i; + struct TELNET *tn = (struct TELNET *)conn->proto.telnet; + + for(i = 0;i < NTELOPTS;i++) + { + if(tn->us_preferred[i] == YES) + set_local_option(conn, i, YES); + + if(tn->him_preferred[i] == YES) + set_remote_option(conn, i, YES); + } +} + +static void printoption(struct SessionHandle *data, + const char *direction, int cmd, int option) +{ + const char *fmt; + const char *opt; + + if (data->set.verbose) + { + if (cmd == IAC) + { + if (TELCMD_OK(option)) + Curl_infof(data, "%s IAC %s\n", direction, TELCMD(option)); + else + Curl_infof(data, "%s IAC %d\n", direction, option); + } + else + { + fmt = (cmd == WILL) ? "WILL" : (cmd == WONT) ? "WONT" : + (cmd == DO) ? "DO" : (cmd == DONT) ? "DONT" : 0; + if (fmt) + { + if (TELOPT_OK(option)) + opt = TELOPT(option); + else if (option == TELOPT_EXOPL) + opt = "EXOPL"; + else + opt = NULL; + + if(opt) + Curl_infof(data, "%s %s %s\n", direction, fmt, opt); + else + Curl_infof(data, "%s %s %d\n", direction, fmt, option); + } + else + Curl_infof(data, "%s %d %d\n", direction, cmd, option); + } + } +} + +static void send_negotiation(struct connectdata *conn, int cmd, int option) +{ + unsigned char buf[3]; + + buf[0] = IAC; + buf[1] = cmd; + buf[2] = option; + + swrite(conn->firstsocket, buf, 3); + + printoption(conn->data, "SENT", cmd, option); +} + +static +void set_remote_option(struct connectdata *conn, int option, int newstate) +{ + struct TELNET *tn = (struct TELNET *)conn->proto.telnet; + if(newstate == YES) + { + switch(tn->him[option]) + { + case NO: + tn->him[option] = WANTYES; + send_negotiation(conn, DO, option); + break; + + case YES: + /* Already enabled */ + break; + + case WANTNO: + switch(tn->himq[option]) + { + case EMPTY: + /* Already negotiating for YES, queue the request */ + tn->himq[option] = OPPOSITE; + break; + case OPPOSITE: + /* Error: already queued an enable request */ + break; + } + break; + + case WANTYES: + switch(tn->himq[option]) + { + case EMPTY: + /* Error: already negotiating for enable */ + break; + case OPPOSITE: + tn->himq[option] = EMPTY; + break; + } + break; + } + } + else /* NO */ + { + switch(tn->him[option]) + { + case NO: + /* Already disabled */ + break; + + case YES: + tn->him[option] = WANTNO; + send_negotiation(conn, DONT, option); + break; + + case WANTNO: + switch(tn->himq[option]) + { + case EMPTY: + /* Already negotiating for NO */ + break; + case OPPOSITE: + tn->himq[option] = EMPTY; + break; + } + break; + + case WANTYES: + switch(tn->himq[option]) + { + case EMPTY: + tn->himq[option] = OPPOSITE; + break; + case OPPOSITE: + break; + } + break; + } + } +} + +static +void rec_will(struct connectdata *conn, int option) +{ + struct TELNET *tn = (struct TELNET *)conn->proto.telnet; + switch(tn->him[option]) + { + case NO: + if(tn->him_preferred[option] == YES) + { + tn->him[option] = YES; + send_negotiation(conn, DO, option); + } + else + { + send_negotiation(conn, DONT, option); + } + break; + + case YES: + /* Already enabled */ + break; + + case WANTNO: + switch(tn->himq[option]) + { + case EMPTY: + /* Error: DONT answered by WILL */ + tn->him[option] = NO; + break; + case OPPOSITE: + /* Error: DONT answered by WILL */ + tn->him[option] = YES; + tn->himq[option] = EMPTY; + break; + } + break; + + case WANTYES: + switch(tn->himq[option]) + { + case EMPTY: + tn->him[option] = YES; + break; + case OPPOSITE: + tn->him[option] = WANTNO; + tn->himq[option] = EMPTY; + send_negotiation(conn, DONT, option); + break; + } + break; + } +} + +static +void rec_wont(struct connectdata *conn, int option) +{ + struct TELNET *tn = (struct TELNET *)conn->proto.telnet; + switch(tn->him[option]) + { + case NO: + /* Already disabled */ + break; + + case YES: + tn->him[option] = NO; + send_negotiation(conn, DONT, option); + break; + + case WANTNO: + switch(tn->himq[option]) + { + case EMPTY: + tn->him[option] = NO; + break; + + case OPPOSITE: + tn->him[option] = WANTYES; + tn->himq[option] = EMPTY; + send_negotiation(conn, DO, option); + break; + } + break; + + case WANTYES: + switch(tn->himq[option]) + { + case EMPTY: + tn->him[option] = NO; + break; + case OPPOSITE: + tn->him[option] = NO; + tn->himq[option] = EMPTY; + break; + } + break; + } +} + +void set_local_option(struct connectdata *conn, int option, int newstate) +{ + struct TELNET *tn = (struct TELNET *)conn->proto.telnet; + if(newstate == YES) + { + switch(tn->us[option]) + { + case NO: + tn->us[option] = WANTYES; + send_negotiation(conn, WILL, option); + break; + + case YES: + /* Already enabled */ + break; + + case WANTNO: + switch(tn->usq[option]) + { + case EMPTY: + /* Already negotiating for YES, queue the request */ + tn->usq[option] = OPPOSITE; + break; + case OPPOSITE: + /* Error: already queued an enable request */ + break; + } + break; + + case WANTYES: + switch(tn->usq[option]) + { + case EMPTY: + /* Error: already negotiating for enable */ + break; + case OPPOSITE: + tn->usq[option] = EMPTY; + break; + } + break; + } + } + else /* NO */ + { + switch(tn->us[option]) + { + case NO: + /* Already disabled */ + break; + + case YES: + tn->us[option] = WANTNO; + send_negotiation(conn, WONT, option); + break; + + case WANTNO: + switch(tn->usq[option]) + { + case EMPTY: + /* Already negotiating for NO */ + break; + case OPPOSITE: + tn->usq[option] = EMPTY; + break; + } + break; + + case WANTYES: + switch(tn->usq[option]) + { + case EMPTY: + tn->usq[option] = OPPOSITE; + break; + case OPPOSITE: + break; + } + break; + } + } +} + +static +void rec_do(struct connectdata *conn, int option) +{ + struct TELNET *tn = (struct TELNET *)conn->proto.telnet; + switch(tn->us[option]) + { + case NO: + if(tn->us_preferred[option] == YES) + { + tn->us[option] = YES; + send_negotiation(conn, WILL, option); + } + else + { + send_negotiation(conn, WONT, option); + } + break; + + case YES: + /* Already enabled */ + break; + + case WANTNO: + switch(tn->usq[option]) + { + case EMPTY: + /* Error: DONT answered by WILL */ + tn->us[option] = NO; + break; + case OPPOSITE: + /* Error: DONT answered by WILL */ + tn->us[option] = YES; + tn->usq[option] = EMPTY; + break; + } + break; + + case WANTYES: + switch(tn->usq[option]) + { + case EMPTY: + tn->us[option] = YES; + break; + case OPPOSITE: + tn->us[option] = WANTNO; + tn->himq[option] = EMPTY; + send_negotiation(conn, WONT, option); + break; + } + break; + } +} + +static +void rec_dont(struct connectdata *conn, int option) +{ + struct TELNET *tn = (struct TELNET *)conn->proto.telnet; + switch(tn->us[option]) + { + case NO: + /* Already disabled */ + break; + + case YES: + tn->us[option] = NO; + send_negotiation(conn, WONT, option); + break; + + case WANTNO: + switch(tn->usq[option]) + { + case EMPTY: + tn->us[option] = NO; + break; + + case OPPOSITE: + tn->us[option] = WANTYES; + tn->usq[option] = EMPTY; + send_negotiation(conn, WILL, option); + break; + } + break; + + case WANTYES: + switch(tn->usq[option]) + { + case EMPTY: + tn->us[option] = NO; + break; + case OPPOSITE: + tn->us[option] = NO; + tn->usq[option] = EMPTY; + break; + } + break; + } +} + + +static void printsub(struct SessionHandle *data, + int direction, /* '<' or '>' */ + unsigned char *pointer, /* where suboption data is */ + int length) /* length of suboption data */ +{ + int i = 0; + + if (data->set.verbose) + { + if (direction) + { + Curl_infof(data, "%s IAC SB ", (direction == '<')? "RCVD":"SENT"); + if (length >= 3) + { + int j; + + i = pointer[length-2]; + j = pointer[length-1]; + + if (i != IAC || j != SE) + { + Curl_infof(data, "(terminated by "); + if (TELOPT_OK(i)) + Curl_infof(data, "%s ", TELOPT(i)); + else if (TELCMD_OK(i)) + Curl_infof(data, "%s ", TELCMD(i)); + else + Curl_infof(data, "%d ", i); + if (TELOPT_OK(j)) + Curl_infof(data, "%s", TELOPT(j)); + else if (TELCMD_OK(j)) + Curl_infof(data, "%s", TELCMD(j)); + else + Curl_infof(data, "%d", j); + Curl_infof(data, ", not IAC SE!) "); + } + } + length -= 2; + } + if (length < 1) + { + Curl_infof(data, "(Empty suboption?)"); + return; + } + + if (TELOPT_OK(pointer[0])) { + switch(pointer[0]) { + case TELOPT_TTYPE: + case TELOPT_XDISPLOC: + case TELOPT_NEW_ENVIRON: + Curl_infof(data, "%s", TELOPT(pointer[0])); + break; + default: + Curl_infof(data, "%s (unsupported)", TELOPT(pointer[0])); + break; + } + } + else + Curl_infof(data, "%d (unknown)", pointer[i]); + + switch(pointer[1]) { + case TELQUAL_IS: + Curl_infof(data, " IS"); + break; + case TELQUAL_SEND: + Curl_infof(data, " SEND"); + break; + case TELQUAL_INFO: + Curl_infof(data, " INFO/REPLY"); + break; + case TELQUAL_NAME: + Curl_infof(data, " NAME"); + break; + } + + switch(pointer[0]) { + case TELOPT_TTYPE: + case TELOPT_XDISPLOC: + pointer[length] = 0; + Curl_infof(data, " \"%s\"", &pointer[2]); + break; + case TELOPT_NEW_ENVIRON: + if(pointer[1] == TELQUAL_IS) { + Curl_infof(data, " "); + for(i = 3;i < length;i++) { + switch(pointer[i]) { + case NEW_ENV_VAR: + Curl_infof(data, ", "); + break; + case NEW_ENV_VALUE: + Curl_infof(data, " = "); + break; + default: + Curl_infof(data, "%c", pointer[i]); + break; + } + } + } + break; + default: + for (i = 2; i < length; i++) + Curl_infof(data, " %.2x", pointer[i]); + break; + } + + if (direction) + { + Curl_infof(data, "\n"); + } + } +} + +static int check_telnet_options(struct connectdata *conn) +{ + struct curl_slist *head; + char option_keyword[128]; + char option_arg[256]; + char *buf; + struct SessionHandle *data = conn->data; + struct TELNET *tn = (struct TELNET *)conn->proto.telnet; + + /* Add the user name as an environment variable if it + was given on the command line */ + if(conn->bits.user_passwd) + { + char *buf = malloc(256); + sprintf(buf, "USER,%s", data->state.user); + tn->telnet_vars = curl_slist_append(tn->telnet_vars, buf); + + tn->us_preferred[TELOPT_NEW_ENVIRON] = YES; + } + + for(head = data->set.telnet_options; head; head=head->next) { + if(sscanf(head->data, "%127[^= ]%*[ =]%255s", + option_keyword, option_arg) == 2) { + + /* Terminal type */ + if(strequal(option_keyword, "TTYPE")) { + strncpy(tn->subopt_ttype, option_arg, 31); + tn->subopt_ttype[31] = 0; /* String termination */ + tn->us_preferred[TELOPT_TTYPE] = YES; + continue; + } + + /* Display variable */ + if(strequal(option_keyword, "XDISPLOC")) { + strncpy(tn->subopt_xdisploc, option_arg, 127); + tn->subopt_xdisploc[127] = 0; /* String termination */ + tn->us_preferred[TELOPT_XDISPLOC] = YES; + continue; + } + + /* Environment variable */ + if(strequal(option_keyword, "NEW_ENV")) { + buf = strdup(option_arg); + if(!buf) + return CURLE_OUT_OF_MEMORY; + tn->telnet_vars = curl_slist_append(tn->telnet_vars, buf); + tn->us_preferred[TELOPT_NEW_ENVIRON] = YES; + continue; + } + + failf(data, "Unknown telnet option %s", head->data); + return CURLE_UNKNOWN_TELNET_OPTION; + } else { + failf(data, "Syntax error in telnet option: %s", head->data); + return CURLE_TELNET_OPTION_SYNTAX; + } + } + + return CURLE_OK; +} + +/* + * suboption() + * + * Look at the sub-option buffer, and try to be helpful to the other + * side. + */ + +static void suboption(struct connectdata *conn) +{ + struct curl_slist *v; + unsigned char subchar; + unsigned char temp[2048]; + int len; + int tmplen; + char varname[128]; + char varval[128]; + struct SessionHandle *data = conn->data; + struct TELNET *tn = (struct TELNET *)conn->proto.telnet; + + printsub(data, '<', (unsigned char *)tn->subbuffer, SB_LEN(tn)+2); + switch (subchar = SB_GET(tn)) { + case TELOPT_TTYPE: + len = strlen(tn->subopt_ttype) + 4 + 2; + snprintf((char *)temp, sizeof(temp), + "%c%c%c%c%s%c%c", IAC, SB, TELOPT_TTYPE, + TELQUAL_IS, tn->subopt_ttype, IAC, SE); + swrite(conn->firstsocket, temp, len); + printsub(data, '>', &temp[2], len-2); + break; + case TELOPT_XDISPLOC: + len = strlen(tn->subopt_xdisploc) + 4 + 2; + snprintf((char *)temp, sizeof(temp), + "%c%c%c%c%s%c%c", IAC, SB, TELOPT_XDISPLOC, + TELQUAL_IS, tn->subopt_xdisploc, IAC, SE); + swrite(conn->firstsocket, temp, len); + printsub(data, '>', &temp[2], len-2); + break; + case TELOPT_NEW_ENVIRON: + snprintf((char *)temp, sizeof(temp), + "%c%c%c%c", IAC, SB, TELOPT_NEW_ENVIRON, TELQUAL_IS); + len = 4; + + for(v = tn->telnet_vars;v;v = v->next) { + tmplen = (strlen(v->data) + 1); + /* Add the variable only if it fits */ + if(len + tmplen < (int)sizeof(temp)-6) { + sscanf(v->data, "%127[^,],%s", varname, varval); + snprintf((char *)&temp[len], sizeof(temp) - len, + "%c%s%c%s", NEW_ENV_VAR, varname, + NEW_ENV_VALUE, varval); + len += tmplen; + } + } + snprintf((char *)&temp[len], sizeof(temp) - len, + "%c%c", IAC, SE); + len += 2; + swrite(conn->firstsocket, temp, len); + printsub(data, '>', &temp[2], len-2); + break; + } + return; +} + +static +void telrcv(struct connectdata *conn, + unsigned char *inbuf, /* Data received from socket */ + int count) /* Number of bytes received */ +{ + unsigned char c; + int index = 0; + struct SessionHandle *data = conn->data; + struct TELNET *tn = (struct TELNET *)conn->proto.telnet; + + while(count--) + { + c = inbuf[index++]; + + switch (tn->telrcv_state) + { + case TS_CR: + tn->telrcv_state = TS_DATA; + if (c == '\0') + { + break; /* Ignore \0 after CR */ + } + + Curl_client_write(data, CLIENTWRITE_BODY, (char *)&c, 1); + continue; + + case TS_DATA: + if (c == IAC) + { + tn->telrcv_state = TS_IAC; + break; + } + else if(c == '\r') + { + tn->telrcv_state = TS_CR; + } + + Curl_client_write(data, CLIENTWRITE_BODY, (char *)&c, 1); + continue; + + case TS_IAC: + process_iac: + switch (c) + { + case WILL: + tn->telrcv_state = TS_WILL; + continue; + case WONT: + tn->telrcv_state = TS_WONT; + continue; + case DO: + tn->telrcv_state = TS_DO; + continue; + case DONT: + tn->telrcv_state = TS_DONT; + continue; + case SB: + SB_CLEAR(tn); + tn->telrcv_state = TS_SB; + continue; + case IAC: + Curl_client_write(data, CLIENTWRITE_BODY, (char *)&c, 1); + break; + case DM: + case NOP: + case GA: + default: + printoption(data, "RCVD", IAC, c); + break; + } + tn->telrcv_state = TS_DATA; + continue; + + case TS_WILL: + printoption(data, "RCVD", WILL, c); + tn->please_negotiate = 1; + rec_will(conn, c); + tn->telrcv_state = TS_DATA; + continue; + + case TS_WONT: + printoption(data, "RCVD", WONT, c); + tn->please_negotiate = 1; + rec_wont(conn, c); + tn->telrcv_state = TS_DATA; + continue; + + case TS_DO: + printoption(data, "RCVD", DO, c); + tn->please_negotiate = 1; + rec_do(conn, c); + tn->telrcv_state = TS_DATA; + continue; + + case TS_DONT: + printoption(data, "RCVD", DONT, c); + tn->please_negotiate = 1; + rec_dont(conn, c); + tn->telrcv_state = TS_DATA; + continue; + + case TS_SB: + if (c == IAC) + { + tn->telrcv_state = TS_SE; + } + else + { + SB_ACCUM(tn,c); + } + continue; + + case TS_SE: + if (c != SE) + { + if (c != IAC) + { + /* + * This is an error. We only expect to get + * "IAC IAC" or "IAC SE". Several things may + * have happend. An IAC was not doubled, the + * IAC SE was left off, or another option got + * inserted into the suboption are all possibilities. + * If we assume that the IAC was not doubled, + * and really the IAC SE was left off, we could + * get into an infinate loop here. So, instead, + * we terminate the suboption, and process the + * partial suboption if we can. + */ + SB_ACCUM(tn, (unsigned char)IAC); + SB_ACCUM(tn, c); + tn->subpointer -= 2; + SB_TERM(tn); + + printoption(data, "In SUBOPTION processing, RCVD", IAC, c); + suboption(conn); /* handle sub-option */ + tn->telrcv_state = TS_IAC; + goto process_iac; + } + SB_ACCUM(tn,c); + tn->telrcv_state = TS_SB; + } + else + { + SB_ACCUM(tn, (unsigned char)IAC); + SB_ACCUM(tn, (unsigned char)SE); + tn->subpointer -= 2; + SB_TERM(tn); + suboption(conn); /* handle sub-option */ + tn->telrcv_state = TS_DATA; + } + break; + } + } +} + +CURLcode Curl_telnet_done(struct connectdata *conn) +{ + struct TELNET *tn = (struct TELNET *)conn->proto.telnet; + curl_slist_free_all(tn->telnet_vars); + + free(conn->proto.telnet); + conn->proto.telnet = NULL; + + return CURLE_OK; +} + +CURLcode Curl_telnet(struct connectdata *conn) +{ + CURLcode code; + struct SessionHandle *data = conn->data; + int sockfd = conn->firstsocket; +#ifdef WIN32 + WSAEVENT event_handle; + WSANETWORKEVENTS events; + HANDLE stdin_handle; + HANDLE objs[2]; + DWORD waitret; +#else + fd_set readfd; + fd_set keepfd; +#endif + bool keepon = TRUE; + char *buf = data->state.buffer; + ssize_t nread; + struct TELNET *tn; + + code = init_telnet(conn); + if(code) + return code; + + tn = (struct TELNET *)conn->proto.telnet; + + code = check_telnet_options(conn); + if(code) + return code; + +#ifdef WIN32 + /* We want to wait for both stdin and the socket. Since + ** the select() function in winsock only works on sockets + ** we have to use the WaitForMultipleObjects() call. + */ + + /* First, create a sockets event object */ + event_handle = WSACreateEvent(); + + /* The get the Windows file handle for stdin */ + stdin_handle = GetStdHandle(STD_INPUT_HANDLE); + + /* Create the list of objects to wait for */ + objs[0] = stdin_handle; + objs[1] = event_handle; + + /* Tell winsock what events we want to listen to */ + if(WSAEventSelect(sockfd, event_handle, FD_READ|FD_CLOSE) == SOCKET_ERROR) { + return 0; + } + + /* Keep on listening and act on events */ + while(keepon) { + waitret = WaitForMultipleObjects(2, objs, FALSE, INFINITE); + switch(waitret - WAIT_OBJECT_0) + { + case 0: + { + unsigned char outbuf[2]; + int out_count = 0; + ssize_t bytes_written; + char *buffer = buf; + + if(!ReadFile(stdin_handle, buf, 255, &nread, NULL)) { + keepon = FALSE; + break; + } + + while(nread--) { + outbuf[0] = *buffer++; + out_count = 1; + if(outbuf[0] == IAC) + outbuf[out_count++] = IAC; + + Curl_write(conn, conn->firstsocket, outbuf, + out_count, &bytes_written); + } + } + break; + + case 1: + if(WSAEnumNetworkEvents(sockfd, event_handle, &events) + != SOCKET_ERROR) + { + if(events.lNetworkEvents & FD_READ) + { + /* This reallu OUGHT to check its return code. */ + Curl_read(conn, sockfd, buf, BUFSIZE - 1, &nread); + + telrcv(conn, (unsigned char *)buf, nread); + + fflush(stdout); + + /* Negotiate if the peer has started negotiating, + otherwise don't. We don't want to speak telnet with + non-telnet servers, like POP or SMTP. */ + if(tn->please_negotiate && !tn->already_negotiated) { + negotiate(conn); + tn->already_negotiated = 1; + } + } + + if(events.lNetworkEvents & FD_CLOSE) + { + keepon = FALSE; + } + } + break; + } + } +#else + FD_ZERO (&readfd); /* clear it */ + FD_SET (sockfd, &readfd); + FD_SET (0, &readfd); + + keepfd = readfd; + + while (keepon) { + readfd = keepfd; /* set this every lap in the loop */ + + switch (select (sockfd + 1, &readfd, NULL, NULL, NULL)) { + case -1: /* error, stop reading */ + keepon = FALSE; + continue; + case 0: /* timeout */ + break; + default: /* read! */ + if(FD_ISSET(0, &readfd)) { /* read from stdin */ + unsigned char outbuf[2]; + int out_count = 0; + ssize_t bytes_written; + char *buffer = buf; + + nread = read(0, buf, 255); + + while(nread--) { + outbuf[0] = *buffer++; + out_count = 1; + if(outbuf[0] == IAC) + outbuf[out_count++] = IAC; + + Curl_write(conn, conn->firstsocket, outbuf, + out_count, &bytes_written); + } + } + + if(FD_ISSET(sockfd, &readfd)) { + /* This OUGHT to check the return code... */ + Curl_read(conn, sockfd, buf, BUFSIZE - 1, &nread); + + /* if we receive 0 or less here, the server closed the connection and + we bail out from this! */ + if (nread <= 0) { + keepon = FALSE; + break; + } + + telrcv(conn, (unsigned char *)buf, nread); + + /* Negotiate if the peer has started negotiating, + otherwise don't. We don't want to speak telnet with + non-telnet servers, like POP or SMTP. */ + if(tn->please_negotiate && !tn->already_negotiated) { + negotiate(conn); + tn->already_negotiated = 1; + } + } + } + } +#endif + /* mark this as "no further transfer wanted" */ + return Curl_Transfer(conn, -1, -1, FALSE, NULL, -1, NULL); +} + +/* + * local variables: + * eval: (load-file "../curl-mode.el") + * end: + * vim600: fdm=marker + * vim: et sw=2 ts=2 sts=2 tw=78 + */ |