X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=lib%2Ftelnet.c;h=78d3a853d071a4da22d9210a9ed46a7c63fcf9cf;hb=bda6f264bdcc6f0d337836cda66fe8251eb6c83f;hp=ebd47ebaee2beda1790fcd79b3d25e173d4e1cd8;hpb=44b44a751d07b42b4546e3d856a6e761f46dbf63;p=platform%2Fupstream%2Fcurl.git diff --git a/lib/telnet.c b/lib/telnet.c index ebd47eb..78d3a85 100644 --- a/lib/telnet.c +++ b/lib/telnet.c @@ -5,11 +5,11 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2011, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2017, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms - * are also available at http://curl.haxx.se/docs/copyright.html. + * are also available at https://curl.haxx.se/docs/copyright.html. * * 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 @@ -20,19 +20,13 @@ * ***************************************************************************/ -#include "setup.h" +#include "curl_setup.h" #ifndef CURL_DISABLE_TELNET -#ifdef HAVE_SYS_SOCKET_H -#include -#endif #ifdef HAVE_NETINET_IN_H #include #endif -#ifdef HAVE_UNISTD_H -#include -#endif #ifdef HAVE_NETDB_H #include #endif @@ -57,43 +51,49 @@ #include "telnet.h" #include "connect.h" #include "progress.h" - -#define _MPRINTF_REPLACE /* use our functions only */ -#include +#include "system_win32.h" #define TELOPTS #define TELCMDS #include "arpa_telnet.h" -#include "curl_memory.h" #include "select.h" -#include "strequal.h" -#include "rawstr.h" +#include "strcase.h" +#include "warnless.h" -/* The last #include file should be: */ +/* The last 3 #include files should be in this order */ +#include "curl_printf.h" +#include "curl_memory.h" #include "memdebug.h" #define SUBBUFSIZE 512 -#define CURL_SB_CLEAR(x) x->subpointer = x->subbuffer; -#define CURL_SB_TERM(x) { x->subend = x->subpointer; CURL_SB_CLEAR(x); } -#define CURL_SB_ACCUM(x,c) \ - if(x->subpointer < (x->subbuffer+sizeof x->subbuffer)) { \ - *x->subpointer++ = (c); \ - } +#define CURL_SB_CLEAR(x) x->subpointer = x->subbuffer +#define CURL_SB_TERM(x) \ + do { \ + x->subend = x->subpointer; \ + CURL_SB_CLEAR(x); \ + } WHILE_FALSE +#define CURL_SB_ACCUM(x,c) \ + do { \ + if(x->subpointer < (x->subbuffer + sizeof x->subbuffer)) \ + *x->subpointer++ = (c); \ + } WHILE_FALSE #define CURL_SB_GET(x) ((*x->subpointer++)&0xff) -#define CURL_SB_PEEK(x) ((*x->subpointer)&0xff) -#define CURL_SB_EOF(x) (x->subpointer >= x->subend) #define CURL_SB_LEN(x) (x->subend - x->subpointer) +/* For posterity: +#define CURL_SB_PEEK(x) ((*x->subpointer)&0xff) +#define CURL_SB_EOF(x) (x->subpointer >= x->subend) */ + #ifdef CURL_DISABLE_VERBOSE_STRINGS -#define printoption(a,b,c,d) do { } while(0) +#define printoption(a,b,c,d) Curl_nop_stmt #endif #ifdef USE_WINSOCK typedef FARPROC WSOCK2_FUNC; -static CURLcode check_wsock2 ( struct SessionHandle *data ); +static CURLcode check_wsock2(struct Curl_easy *data); #endif static @@ -102,7 +102,7 @@ CURLcode telrcv(struct connectdata *, ssize_t count); /* Number of bytes received */ #ifndef CURL_DISABLE_VERBOSE_STRINGS -static void printoption(struct SessionHandle *data, +static void printoption(struct Curl_easy *data, const char *direction, int cmd, int option); #endif @@ -112,14 +112,17 @@ 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, +static void printsub(struct Curl_easy *data, int direction, unsigned char *pointer, size_t length); static void suboption(struct connectdata *); +static void sendsuboption(struct connectdata *conn, int option); static CURLcode telnet_do(struct connectdata *conn, bool *done); static CURLcode telnet_done(struct connectdata *conn, CURLcode, bool premature); +static CURLcode send_telnet_data(struct connectdata *conn, + char *buffer, ssize_t nread); /* For negotiation compliant to RFC 1143 */ #define CURL_NO 0 @@ -155,9 +158,12 @@ struct TELNET { int him[256]; int himq[256]; int him_preferred[256]; + int subnegotiation[256]; char subopt_ttype[32]; /* Set with suboption TTYPE */ - char subopt_xdisploc[128]; /* Set with suboption XDISPLOC */ - struct curl_slist *telnet_vars; /* Environment variables */ + char subopt_xdisploc[128]; /* Set with suboption XDISPLOC */ + unsigned short subopt_wsx; /* Set with suboption NAWS */ + unsigned short subopt_wsy; /* Set with suboption NAWS */ + struct curl_slist *telnet_vars; /* Environment variables */ /* suboptions */ unsigned char subbuffer[SUBBUFSIZE]; @@ -182,18 +188,20 @@ const struct Curl_handler Curl_handler_telnet = { ZERO_NULL, /* doing */ ZERO_NULL, /* proto_getsock */ ZERO_NULL, /* doing_getsock */ + ZERO_NULL, /* domore_getsock */ ZERO_NULL, /* perform_getsock */ ZERO_NULL, /* disconnect */ ZERO_NULL, /* readwrite */ + ZERO_NULL, /* connection_check */ PORT_TELNET, /* defport */ CURLPROTO_TELNET, /* protocol */ - PROTOPT_NONE /* flags */ + PROTOPT_NONE | PROTOPT_NOURLQUERY /* flags */ }; #ifdef USE_WINSOCK static CURLcode -check_wsock2 ( struct SessionHandle *data ) +check_wsock2(struct Curl_easy *data) { int err; WORD wVersionRequested; @@ -221,9 +229,9 @@ check_wsock2 ( struct SessionHandle *data ) if(LOBYTE(wsaData.wVersion) != LOBYTE(wVersionRequested) || HIBYTE(wsaData.wVersion) != HIBYTE(wVersionRequested)) { /* Our version isn't supported */ - failf(data,"insufficient winsock version to support " - "telnet"); - return CURLE_FAILED_INIT; + failf(data, "insufficient winsock version to support " + "telnet"); + return CURLE_FAILED_INIT; } /* Our version is supported */ @@ -240,7 +248,7 @@ CURLcode init_telnet(struct connectdata *conn) if(!tn) return CURLE_OUT_OF_MEMORY; - conn->data->state.proto.telnet = (void *)tn; /* make us known */ + conn->data->req.protop = tn; /* make us known */ tn->telrcv_state = CURL_TS_DATA; @@ -248,20 +256,49 @@ CURLcode init_telnet(struct connectdata *conn) CURL_SB_CLEAR(tn); /* Set the options we want by default */ - tn->us_preferred[CURL_TELOPT_BINARY] = CURL_YES; tn->us_preferred[CURL_TELOPT_SGA] = CURL_YES; - tn->him_preferred[CURL_TELOPT_BINARY] = CURL_YES; tn->him_preferred[CURL_TELOPT_SGA] = CURL_YES; + /* To be compliant with previous releases of libcurl + we enable this option by default. This behaviour + can be changed thanks to the "BINARY" option in + CURLOPT_TELNETOPTIONS + */ + tn->us_preferred[CURL_TELOPT_BINARY] = CURL_YES; + tn->him_preferred[CURL_TELOPT_BINARY] = CURL_YES; + + /* We must allow the server to echo what we sent + but it is not necessary to request the server + to do so (it might forces the server to close + the connection). Hence, we ignore ECHO in the + negotiate function + */ + tn->him_preferred[CURL_TELOPT_ECHO] = CURL_YES; + + /* Set the subnegotiation fields to send information + just after negotiation passed (do/will) + + Default values are (0,0) initialized by calloc. + According to the RFC1013 it is valid: + A value equal to zero is acceptable for the width (or height), + and means that no character width (or height) is being sent. + In this case, the width (or height) that will be assumed by the + Telnet server is operating system specific (it will probably be + based upon the terminal type information that may have been sent + using the TERMINAL TYPE Telnet option). */ + tn->subnegotiation[CURL_TELOPT_NAWS] = CURL_YES; return CURLE_OK; } static void negotiate(struct connectdata *conn) { int i; - struct TELNET *tn = (struct TELNET *) conn->data->state.proto.telnet; + struct TELNET *tn = (struct TELNET *) conn->data->req.protop; + + for(i = 0; i < CURL_NTELOPTS; i++) { + if(i == CURL_TELOPT_ECHO) + continue; - for(i = 0;i < CURL_NTELOPTS;i++) { if(tn->us_preferred[i] == CURL_YES) set_local_option(conn, i, CURL_YES); @@ -271,7 +308,7 @@ static void negotiate(struct connectdata *conn) } #ifndef CURL_DISABLE_VERBOSE_STRINGS -static void printoption(struct SessionHandle *data, +static void printoption(struct Curl_easy *data, const char *direction, int cmd, int option) { const char *fmt; @@ -312,7 +349,7 @@ static void send_negotiation(struct connectdata *conn, int cmd, int option) unsigned char buf[3]; ssize_t bytes_written; int err; - struct SessionHandle *data = conn->data; + struct Curl_easy *data = conn->data; buf[0] = CURL_IAC; buf[1] = (unsigned char)cmd; @@ -330,7 +367,7 @@ static void send_negotiation(struct connectdata *conn, int cmd, int option) static void set_remote_option(struct connectdata *conn, int option, int newstate) { - struct TELNET *tn = (struct TELNET *)conn->data->state.proto.telnet; + struct TELNET *tn = (struct TELNET *)conn->data->req.protop; if(newstate == CURL_YES) { switch(tn->him[option]) { case CURL_NO: @@ -404,7 +441,7 @@ void set_remote_option(struct connectdata *conn, int option, int newstate) static void rec_will(struct connectdata *conn, int option) { - struct TELNET *tn = (struct TELNET *)conn->data->state.proto.telnet; + struct TELNET *tn = (struct TELNET *)conn->data->req.protop; switch(tn->him[option]) { case CURL_NO: if(tn->him_preferred[option] == CURL_YES) { @@ -452,7 +489,7 @@ void rec_will(struct connectdata *conn, int option) static void rec_wont(struct connectdata *conn, int option) { - struct TELNET *tn = (struct TELNET *)conn->data->state.proto.telnet; + struct TELNET *tn = (struct TELNET *)conn->data->req.protop; switch(tn->him[option]) { case CURL_NO: /* Already disabled */ @@ -494,7 +531,7 @@ void rec_wont(struct connectdata *conn, int option) static void set_local_option(struct connectdata *conn, int option, int newstate) { - struct TELNET *tn = (struct TELNET *)conn->data->state.proto.telnet; + struct TELNET *tn = (struct TELNET *)conn->data->req.protop; if(newstate == CURL_YES) { switch(tn->us[option]) { case CURL_NO: @@ -568,12 +605,21 @@ set_local_option(struct connectdata *conn, int option, int newstate) static void rec_do(struct connectdata *conn, int option) { - struct TELNET *tn = (struct TELNET *)conn->data->state.proto.telnet; + struct TELNET *tn = (struct TELNET *)conn->data->req.protop; switch(tn->us[option]) { case CURL_NO: if(tn->us_preferred[option] == CURL_YES) { tn->us[option] = CURL_YES; send_negotiation(conn, CURL_WILL, option); + if(tn->subnegotiation[option] == CURL_YES) + /* transmission of data option */ + sendsuboption(conn, option); + } + else if(tn->subnegotiation[option] == CURL_YES) { + /* send information to achieve this option*/ + tn->us[option] = CURL_YES; + send_negotiation(conn, CURL_WILL, option); + sendsuboption(conn, option); } else send_negotiation(conn, CURL_WONT, option); @@ -601,6 +647,10 @@ void rec_do(struct connectdata *conn, int option) switch(tn->usq[option]) { case CURL_EMPTY: tn->us[option] = CURL_YES; + if(tn->subnegotiation[option] == CURL_YES) { + /* transmission of data option */ + sendsuboption(conn, option); + } break; case CURL_OPPOSITE: tn->us[option] = CURL_WANTNO; @@ -615,7 +665,7 @@ void rec_do(struct connectdata *conn, int option) static void rec_dont(struct connectdata *conn, int option) { - struct TELNET *tn = (struct TELNET *)conn->data->state.proto.telnet; + struct TELNET *tn = (struct TELNET *)conn->data->req.protop; switch(tn->us[option]) { case CURL_NO: /* Already disabled */ @@ -655,7 +705,7 @@ void rec_dont(struct connectdata *conn, int option) } -static void printsub(struct SessionHandle *data, +static void printsub(struct Curl_easy *data, int direction, /* '<' or '>' */ unsigned char *pointer, /* where suboption data is */ size_t length) /* length of suboption data */ @@ -697,20 +747,28 @@ static void printsub(struct SessionHandle *data, if(CURL_TELOPT_OK(pointer[0])) { switch(pointer[0]) { - case CURL_TELOPT_TTYPE: - case CURL_TELOPT_XDISPLOC: - case CURL_TELOPT_NEW_ENVIRON: - infof(data, "%s", CURL_TELOPT(pointer[0])); - break; - default: - infof(data, "%s (unsupported)", CURL_TELOPT(pointer[0])); - break; + case CURL_TELOPT_TTYPE: + case CURL_TELOPT_XDISPLOC: + case CURL_TELOPT_NEW_ENVIRON: + case CURL_TELOPT_NAWS: + infof(data, "%s", CURL_TELOPT(pointer[0])); + break; + default: + infof(data, "%s (unsupported)", CURL_TELOPT(pointer[0])); + break; } } else infof(data, "%d (unknown)", pointer[i]); - switch(pointer[1]) { + switch(pointer[0]) { + case CURL_TELOPT_NAWS: + if(length > 4) + infof(data, "Width: %hu ; Height: %hu", (pointer[1]<<8) | pointer[2], + (pointer[3]<<8) | pointer[4]); + break; + default: + switch(pointer[1]) { case CURL_TELQUAL_IS: infof(data, " IS"); break; @@ -723,9 +781,9 @@ static void printsub(struct SessionHandle *data, case CURL_TELQUAL_NAME: infof(data, " NAME"); break; - } + } - switch(pointer[0]) { + switch(pointer[0]) { case CURL_TELOPT_TTYPE: case CURL_TELOPT_XDISPLOC: pointer[length] = 0; @@ -734,17 +792,17 @@ static void printsub(struct SessionHandle *data, case CURL_TELOPT_NEW_ENVIRON: if(pointer[1] == CURL_TELQUAL_IS) { infof(data, " "); - for(i = 3;i < length;i++) { + for(i = 3; i < length; i++) { switch(pointer[i]) { - case CURL_NEW_ENV_VAR: - infof(data, ", "); - break; - case CURL_NEW_ENV_VALUE: - infof(data, " = "); - break; - default: - infof(data, "%c", pointer[i]); - break; + case CURL_NEW_ENV_VAR: + infof(data, ", "); + break; + case CURL_NEW_ENV_VALUE: + infof(data, " = "); + break; + default: + infof(data, "%c", pointer[i]); + break; } } } @@ -753,8 +811,8 @@ static void printsub(struct SessionHandle *data, for(i = 2; i < length; i++) infof(data, " %.2x", pointer[i]); break; + } } - if(direction) infof(data, "\n"); } @@ -763,27 +821,34 @@ static void printsub(struct SessionHandle *data, static CURLcode 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->data->state.proto.telnet; + struct curl_slist *beg; + char option_keyword[128] = ""; + char option_arg[256] = ""; + struct Curl_easy *data = conn->data; + struct TELNET *tn = (struct TELNET *)conn->data->req.protop; + CURLcode result = CURLE_OK; + int binary_option; /* Add the user name as an environment variable if it was given on the command line */ if(conn->bits.user_passwd) { snprintf(option_arg, sizeof(option_arg), "USER,%s", conn->user); - tn->telnet_vars = curl_slist_append(tn->telnet_vars, option_arg); - + beg = curl_slist_append(tn->telnet_vars, option_arg); + if(!beg) { + curl_slist_free_all(tn->telnet_vars); + tn->telnet_vars = NULL; + return CURLE_OUT_OF_MEMORY; + } + tn->telnet_vars = beg; tn->us_preferred[CURL_TELOPT_NEW_ENVIRON] = CURL_YES; } - for(head = data->set.telnet_options; head; head=head->next) { + for(head = data->set.telnet_options; head; head = head->next) { if(sscanf(head->data, "%127[^= ]%*[ =]%255s", option_keyword, option_arg) == 2) { /* Terminal type */ - if(Curl_raw_equal(option_keyword, "TTYPE")) { + if(strcasecompare(option_keyword, "TTYPE")) { strncpy(tn->subopt_ttype, option_arg, 31); tn->subopt_ttype[31] = 0; /* String termination */ tn->us_preferred[CURL_TELOPT_TTYPE] = CURL_YES; @@ -791,7 +856,7 @@ static CURLcode check_telnet_options(struct connectdata *conn) } /* Display variable */ - if(Curl_raw_equal(option_keyword, "XDISPLOC")) { + if(strcasecompare(option_keyword, "XDISPLOC")) { strncpy(tn->subopt_xdisploc, option_arg, 127); tn->subopt_xdisploc[127] = 0; /* String termination */ tn->us_preferred[CURL_TELOPT_XDISPLOC] = CURL_YES; @@ -799,25 +864,55 @@ static CURLcode check_telnet_options(struct connectdata *conn) } /* Environment variable */ - if(Curl_raw_equal(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); + if(strcasecompare(option_keyword, "NEW_ENV")) { + beg = curl_slist_append(tn->telnet_vars, option_arg); + if(!beg) { + result = CURLE_OUT_OF_MEMORY; + break; + } + tn->telnet_vars = beg; tn->us_preferred[CURL_TELOPT_NEW_ENVIRON] = CURL_YES; continue; } + /* Window Size */ + if(strcasecompare(option_keyword, "WS")) { + if(sscanf(option_arg, "%hu%*[xX]%hu", + &tn->subopt_wsx, &tn->subopt_wsy) == 2) + tn->us_preferred[CURL_TELOPT_NAWS] = CURL_YES; + else { + failf(data, "Syntax error in telnet option: %s", head->data); + result = CURLE_TELNET_OPTION_SYNTAX; + break; + } + continue; + } + + /* To take care or not of the 8th bit in data exchange */ + if(strcasecompare(option_keyword, "BINARY")) { + binary_option = atoi(option_arg); + if(binary_option != 1) { + tn->us_preferred[CURL_TELOPT_BINARY] = CURL_NO; + tn->him_preferred[CURL_TELOPT_BINARY] = CURL_NO; + } + 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; + result = CURLE_UNKNOWN_OPTION; + break; } + failf(data, "Syntax error in telnet option: %s", head->data); + result = CURLE_TELNET_OPTION_SYNTAX; + break; } - return CURLE_OK; + if(result) { + curl_slist_free_all(tn->telnet_vars); + tn->telnet_vars = NULL; + } + + return result; } /* @@ -835,13 +930,13 @@ static void suboption(struct connectdata *conn) size_t len; size_t tmplen; int err; - char varname[128]; - char varval[128]; - struct SessionHandle *data = conn->data; - struct TELNET *tn = (struct TELNET *)data->state.proto.telnet; + char varname[128] = ""; + char varval[128] = ""; + struct Curl_easy *data = conn->data; + struct TELNET *tn = (struct TELNET *)data->req.protop; - printsub(data, '<', (unsigned char *)tn->subbuffer, CURL_SB_LEN(tn)+2); - switch (CURL_SB_GET(tn)) { + printsub(data, '<', (unsigned char *)tn->subbuffer, CURL_SB_LEN(tn) + 2); + switch(CURL_SB_GET(tn)) { case CURL_TELOPT_TTYPE: len = strlen(tn->subopt_ttype) + 4 + 2; snprintf((char *)temp, sizeof(temp), @@ -872,15 +967,16 @@ static void suboption(struct connectdata *conn) CURL_TELQUAL_IS); len = 4; - for(v = tn->telnet_vars;v;v = v->next) { + 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[^,],%127s", varname, varval); - snprintf((char *)&temp[len], sizeof(temp) - len, - "%c%s%c%s", CURL_NEW_ENV_VAR, varname, - CURL_NEW_ENV_VALUE, varval); - len += tmplen; + if(sscanf(v->data, "%127[^,],%127s", varname, varval)) { + snprintf((char *)&temp[len], sizeof(temp) - len, + "%c%s%c%s", CURL_NEW_ENV_VAR, varname, + CURL_NEW_ENV_VALUE, varval); + len += tmplen; + } } } snprintf((char *)&temp[len], sizeof(temp) - len, @@ -897,6 +993,69 @@ static void suboption(struct connectdata *conn) return; } + +/* + * sendsuboption() + * + * Send suboption information to the server side. + */ + +static void sendsuboption(struct connectdata *conn, int option) +{ + ssize_t bytes_written; + int err; + unsigned short x, y; + unsigned char *uc1, *uc2; + + struct Curl_easy *data = conn->data; + struct TELNET *tn = (struct TELNET *)data->req.protop; + + switch(option) { + case CURL_TELOPT_NAWS: + /* We prepare data to be sent */ + CURL_SB_CLEAR(tn); + CURL_SB_ACCUM(tn, CURL_IAC); + CURL_SB_ACCUM(tn, CURL_SB); + CURL_SB_ACCUM(tn, CURL_TELOPT_NAWS); + /* We must deal either with litte or big endian processors */ + /* Window size must be sent according to the 'network order' */ + x = htons(tn->subopt_wsx); + y = htons(tn->subopt_wsy); + uc1 = (unsigned char *)&x; + uc2 = (unsigned char *)&y; + CURL_SB_ACCUM(tn, uc1[0]); + CURL_SB_ACCUM(tn, uc1[1]); + CURL_SB_ACCUM(tn, uc2[0]); + CURL_SB_ACCUM(tn, uc2[1]); + + CURL_SB_ACCUM(tn, CURL_IAC); + CURL_SB_ACCUM(tn, CURL_SE); + CURL_SB_TERM(tn); + /* data suboption is now ready */ + + printsub(data, '>', (unsigned char *)tn->subbuffer + 2, + CURL_SB_LEN(tn)-2); + + /* we send the header of the suboption... */ + bytes_written = swrite(conn->sock[FIRSTSOCKET], tn->subbuffer, 3); + if(bytes_written < 0) { + err = SOCKERRNO; + failf(data, "Sending data failed (%d)", err); + } + /* ... then the window size with the send_telnet_data() function + to deal with 0xFF cases ... */ + send_telnet_data(conn, (char *)tn->subbuffer + 3, 4); + /* ... and the footer */ + bytes_written = swrite(conn->sock[FIRSTSOCKET], tn->subbuffer + 7, 2); + if(bytes_written < 0) { + err = SOCKERRNO; + failf(data, "Sending data failed (%d)", err); + } + break; + } +} + + static CURLcode telrcv(struct connectdata *conn, const unsigned char *inbuf, /* Data received from socket */ @@ -905,9 +1064,9 @@ CURLcode telrcv(struct connectdata *conn, unsigned char c; CURLcode result; int in = 0; - int startwrite=-1; - struct SessionHandle *data = conn->data; - struct TELNET *tn = (struct TELNET *)data->state.proto.telnet; + int startwrite = -1; + struct Curl_easy *data = conn->data; + struct TELNET *tn = (struct TELNET *)data->req.protop; #define startskipping() \ if(startwrite >= 0) { \ @@ -915,7 +1074,7 @@ CURLcode telrcv(struct connectdata *conn, CLIENTWRITE_BODY, \ (char *)&inbuf[startwrite], \ in-startwrite); \ - if(result != CURLE_OK) \ + if(result) \ return result; \ } \ startwrite = -1 @@ -929,7 +1088,7 @@ CURLcode telrcv(struct connectdata *conn, while(count--) { c = inbuf[in]; - switch (tn->telrcv_state) { + switch(tn->telrcv_state) { case CURL_TS_CR: tn->telrcv_state = CURL_TS_DATA; if(c == '\0') { @@ -953,7 +1112,7 @@ CURLcode telrcv(struct connectdata *conn, case CURL_TS_IAC: process_iac: DEBUGASSERT(startwrite < 0); - switch (c) { + switch(c) { case CURL_WILL: tn->telrcv_state = CURL_TS_WILL; break; @@ -1016,7 +1175,7 @@ CURLcode telrcv(struct connectdata *conn, if(c == CURL_IAC) tn->telrcv_state = CURL_TS_SE; else - CURL_SB_ACCUM(tn,c); + CURL_SB_ACCUM(tn, c); break; case CURL_TS_SE: @@ -1028,7 +1187,7 @@ CURLcode telrcv(struct connectdata *conn, * 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 + * into an infinite loop here. So, instead, we terminate the * suboption, and process the partial suboption if we can. */ CURL_SB_ACCUM(tn, CURL_IAC); @@ -1041,7 +1200,7 @@ CURLcode telrcv(struct connectdata *conn, tn->telrcv_state = CURL_TS_IAC; goto process_iac; } - CURL_SB_ACCUM(tn,c); + CURL_SB_ACCUM(tn, c); tn->telrcv_state = CURL_TS_SB; } else @@ -1062,64 +1221,88 @@ CURLcode telrcv(struct connectdata *conn, } /* Escape and send a telnet data block */ -/* TODO: write large chunks of data instead of one byte at a time */ static CURLcode send_telnet_data(struct connectdata *conn, char *buffer, ssize_t nread) { - unsigned char outbuf[2]; + ssize_t escapes, i, j, outlen; + unsigned char *outbuf = NULL; + CURLcode result = CURLE_OK; ssize_t bytes_written, total_written; - int out_count; - CURLcode rc = CURLE_OK; - - while(rc == CURLE_OK && nread--) { - outbuf[0] = *buffer++; - out_count = 1; - if(outbuf[0] == CURL_IAC) - outbuf[out_count++] = CURL_IAC; - - total_written = 0; - do { - /* Make sure socket is writable to avoid EWOULDBLOCK condition */ - struct pollfd pfd[1]; - pfd[0].fd = conn->sock[FIRSTSOCKET]; - pfd[0].events = POLLOUT; - switch (Curl_poll(pfd, 1, -1)) { - case -1: /* error, abort writing */ - case 0: /* timeout (will never happen) */ - rc = CURLE_SEND_ERROR; - break; - default: /* write! */ - bytes_written = 0; - rc = Curl_write(conn, conn->sock[FIRSTSOCKET], outbuf+total_written, - out_count-total_written, &bytes_written); - total_written += bytes_written; - break; - } - /* handle partial write */ - } while(rc == CURLE_OK && total_written < out_count); + + /* Determine size of new buffer after escaping */ + escapes = 0; + for(i = 0; i < nread; i++) + if((unsigned char)buffer[i] == CURL_IAC) + escapes++; + outlen = nread + escapes; + + if(outlen == nread) + outbuf = (unsigned char *)buffer; + else { + outbuf = malloc(nread + escapes + 1); + if(!outbuf) + return CURLE_OUT_OF_MEMORY; + + j = 0; + for(i = 0; i < nread; i++) { + outbuf[j++] = buffer[i]; + if((unsigned char)buffer[i] == CURL_IAC) + outbuf[j++] = CURL_IAC; + } + outbuf[j] = '\0'; } - return rc; + + total_written = 0; + while(!result && total_written < outlen) { + /* Make sure socket is writable to avoid EWOULDBLOCK condition */ + struct pollfd pfd[1]; + pfd[0].fd = conn->sock[FIRSTSOCKET]; + pfd[0].events = POLLOUT; + switch(Curl_poll(pfd, 1, -1)) { + case -1: /* error, abort writing */ + case 0: /* timeout (will never happen) */ + result = CURLE_SEND_ERROR; + break; + default: /* write! */ + bytes_written = 0; + result = Curl_write(conn, conn->sock[FIRSTSOCKET], + outbuf + total_written, + outlen - total_written, + &bytes_written); + total_written += bytes_written; + break; + } + } + + /* Free malloc copy if escaped */ + if(outbuf != (unsigned char *)buffer) + free(outbuf); + + return result; } static CURLcode telnet_done(struct connectdata *conn, CURLcode status, bool premature) { - struct TELNET *tn = (struct TELNET *)conn->data->state.proto.telnet; + struct TELNET *tn = (struct TELNET *)conn->data->req.protop; (void)status; /* unused */ (void)premature; /* not used */ + if(!tn) + return CURLE_OK; + curl_slist_free_all(tn->telnet_vars); + tn->telnet_vars = NULL; - free(conn->data->state.proto.telnet); - conn->data->state.proto.telnet = NULL; + Curl_safefree(conn->data->req.protop); return CURLE_OK; } static CURLcode telnet_do(struct connectdata *conn, bool *done) { - CURLcode code; - struct SessionHandle *data = conn->data; + CURLcode result; + struct Curl_easy *data = conn->data; curl_socket_t sockfd = conn->sock[FIRSTSOCKET]; #ifdef USE_WINSOCK HMODULE wsock2; @@ -1144,72 +1327,69 @@ static CURLcode telnet_do(struct connectdata *conn, bool *done) curl_off_t total_ul = 0; #endif ssize_t nread; - struct timeval now; + struct curltime now; bool keepon = TRUE; char *buf = data->state.buffer; struct TELNET *tn; *done = TRUE; /* unconditionally */ - code = init_telnet(conn); - if(code) - return code; + result = init_telnet(conn); + if(result) + return result; - tn = (struct TELNET *)data->state.proto.telnet; + tn = (struct TELNET *)data->req.protop; - code = check_telnet_options(conn); - if(code) - return code; + result = check_telnet_options(conn); + if(result) + return result; #ifdef USE_WINSOCK /* ** This functionality only works with WinSock >= 2.0. So, - ** make sure have it. + ** make sure we have it. */ - code = check_wsock2(data); - if(code) - return code; + result = check_wsock2(data); + if(result) + return result; /* OK, so we have WinSock 2.0. We need to dynamically */ /* load ws2_32.dll and get the function pointers we need. */ - wsock2 = LoadLibrary("WS2_32.DLL"); + wsock2 = Curl_load_library(TEXT("WS2_32.DLL")); if(wsock2 == NULL) { - failf(data,"failed to load WS2_32.DLL (%d)", ERRNO); + failf(data, "failed to load WS2_32.DLL (%u)", GetLastError()); return CURLE_FAILED_INIT; } /* Grab a pointer to WSACreateEvent */ - create_event_func = GetProcAddress(wsock2,"WSACreateEvent"); + create_event_func = GetProcAddress(wsock2, "WSACreateEvent"); if(create_event_func == NULL) { - failf(data,"failed to find WSACreateEvent function (%d)", - ERRNO); + failf(data, "failed to find WSACreateEvent function (%u)", GetLastError()); FreeLibrary(wsock2); return CURLE_FAILED_INIT; } /* And WSACloseEvent */ - close_event_func = GetProcAddress(wsock2,"WSACloseEvent"); + close_event_func = GetProcAddress(wsock2, "WSACloseEvent"); if(close_event_func == NULL) { - failf(data,"failed to find WSACloseEvent function (%d)", - ERRNO); + failf(data, "failed to find WSACloseEvent function (%u)", GetLastError()); FreeLibrary(wsock2); return CURLE_FAILED_INIT; } /* And WSAEventSelect */ - event_select_func = GetProcAddress(wsock2,"WSAEventSelect"); + event_select_func = GetProcAddress(wsock2, "WSAEventSelect"); if(event_select_func == NULL) { - failf(data,"failed to find WSAEventSelect function (%d)", - ERRNO); + failf(data, "failed to find WSAEventSelect function (%u)", GetLastError()); FreeLibrary(wsock2); return CURLE_FAILED_INIT; } /* And WSAEnumNetworkEvents */ - enum_netevents_func = GetProcAddress(wsock2,"WSAEnumNetworkEvents"); + enum_netevents_func = GetProcAddress(wsock2, "WSAEnumNetworkEvents"); if(enum_netevents_func == NULL) { - failf(data,"failed to find WSAEnumNetworkEvents function (%d)", - ERRNO); + failf(data, "failed to find WSAEnumNetworkEvents function (%u)", + GetLastError()); FreeLibrary(wsock2); return CURLE_FAILED_INIT; } @@ -1222,7 +1402,7 @@ static CURLcode telnet_do(struct connectdata *conn, bool *done) /* First, create a sockets event object */ event_handle = (WSAEVENT)create_event_func(); if(event_handle == WSA_INVALID_EVENT) { - failf(data,"WSACreateEvent failed (%d)", SOCKERRNO); + failf(data, "WSACreateEvent failed (%d)", SOCKERRNO); FreeLibrary(wsock2); return CURLE_FAILED_INIT; } @@ -1258,50 +1438,53 @@ static CURLcode telnet_do(struct connectdata *conn, bool *done) /* Keep on listening and act on events */ while(keepon) { + const DWORD buf_size = (DWORD)data->set.buffer_size; waitret = WaitForMultipleObjects(obj_count, objs, FALSE, wait_timeout); switch(waitret) { case WAIT_TIMEOUT: { for(;;) { - if(obj_count == 1) { + if(data->set.is_fread_set) { + size_t n; /* read from user-supplied method */ - code = (int)conn->fread_func(buf, 1, BUFSIZE - 1, conn->fread_in); - if(code == CURL_READFUNC_ABORT) { + n = data->state.fread_func(buf, 1, buf_size, data->state.in); + if(n == CURL_READFUNC_ABORT) { keepon = FALSE; - code = CURLE_READ_ERROR; + result = CURLE_READ_ERROR; break; } - if(code == CURL_READFUNC_PAUSE) + if(n == CURL_READFUNC_PAUSE) break; - if(code == 0) /* no bytes */ + if(n == 0) /* no bytes */ break; - readfile_read = code; /* fall thru with number of bytes read */ + /* fall through with number of bytes read */ + readfile_read = (DWORD)n; } else { /* read from stdin */ if(!PeekNamedPipe(stdin_handle, NULL, 0, NULL, &readfile_read, NULL)) { keepon = FALSE; - code = CURLE_READ_ERROR; + result = CURLE_READ_ERROR; break; } if(!readfile_read) break; - if(!ReadFile(stdin_handle, buf, sizeof(data->state.buffer), + if(!ReadFile(stdin_handle, buf, buf_size, &readfile_read, NULL)) { keepon = FALSE; - code = CURLE_READ_ERROR; + result = CURLE_READ_ERROR; break; } } - code = send_telnet_data(conn, buf, readfile_read); - if(code) { + result = send_telnet_data(conn, buf, readfile_read); + if(result) { keepon = FALSE; break; } @@ -1311,15 +1494,15 @@ static CURLcode telnet_do(struct connectdata *conn, bool *done) case WAIT_OBJECT_0 + 1: { - if(!ReadFile(stdin_handle, buf, sizeof(data->state.buffer), + if(!ReadFile(stdin_handle, buf, buf_size, &readfile_read, NULL)) { keepon = FALSE; - code = CURLE_READ_ERROR; + result = CURLE_READ_ERROR; break; } - code = send_telnet_data(conn, buf, readfile_read); - if(code) { + result = send_telnet_data(conn, buf, readfile_read); + if(result) { keepon = FALSE; break; } @@ -1328,22 +1511,24 @@ static CURLcode telnet_do(struct connectdata *conn, bool *done) case WAIT_OBJECT_0: + events.lNetworkEvents = 0; if(SOCKET_ERROR == enum_netevents_func(sockfd, event_handle, &events)) { - if((err = SOCKERRNO) != EINPROGRESS) { - infof(data,"WSAEnumNetworkEvents failed (%d)", err); + err = SOCKERRNO; + if(err != EINPROGRESS) { + infof(data, "WSAEnumNetworkEvents failed (%d)", err); keepon = FALSE; - code = CURLE_READ_ERROR; + result = CURLE_READ_ERROR; } break; } if(events.lNetworkEvents & FD_READ) { /* read data from network */ - code = Curl_read(conn, sockfd, buf, BUFSIZE - 1, &nread); + result = Curl_read(conn, sockfd, buf, data->set.buffer_size, &nread); /* read would've blocked. Loop again */ - if(code == CURLE_AGAIN) + if(result == CURLE_AGAIN) break; /* returned not-zero, this an error */ - else if(code) { + else if(result) { keepon = FALSE; break; } @@ -1354,8 +1539,8 @@ static CURLcode telnet_do(struct connectdata *conn, bool *done) break; } - code = telrcv(conn, (unsigned char *)buf, nread); - if(code) { + result = telrcv(conn, (unsigned char *) buf, nread); + if(result) { keepon = FALSE; break; } @@ -1376,18 +1561,18 @@ static CURLcode telnet_do(struct connectdata *conn, bool *done) } if(data->set.timeout) { - now = Curl_tvnow(); - if(Curl_tvdiff(now, conn->created) >= data->set.timeout) { + now = Curl_now(); + if(Curl_timediff(now, conn->created) >= data->set.timeout) { failf(data, "Time-out"); - code = CURLE_OPERATION_TIMEDOUT; + result = CURLE_OPERATION_TIMEDOUT; keepon = FALSE; } } } /* We called WSACreateEvent, so call WSACloseEvent */ - if(close_event_func(event_handle) == FALSE) { - infof(data,"WSACloseEvent failed (%d)", SOCKERRNO); + if(!close_event_func(event_handle)) { + infof(data, "WSACloseEvent failed (%d)", SOCKERRNO); } /* "Forget" pointers into the library we're about to free */ @@ -1398,7 +1583,7 @@ static CURLcode telnet_do(struct connectdata *conn, bool *done) /* We called LoadLibrary, so call FreeLibrary */ if(!FreeLibrary(wsock2)) - infof(data,"FreeLibrary(wsock2) failed (%d)", ERRNO); + infof(data, "FreeLibrary(wsock2) failed (%u)", GetLastError()); #else pfd[0].fd = sockfd; pfd[0].events = POLLIN; @@ -1408,14 +1593,15 @@ static CURLcode telnet_do(struct connectdata *conn, bool *done) interval_ms = 100; /* poll user-supplied read function */ } else { - pfd[1].fd = 0; + /* really using fread, so infile is a FILE* */ + pfd[1].fd = fileno((FILE *)data->state.in); pfd[1].events = POLLIN; poll_cnt = 2; interval_ms = 1 * 1000; } while(keepon) { - switch (Curl_poll(pfd, poll_cnt, interval_ms)) { + switch(Curl_poll(pfd, poll_cnt, interval_ms)) { case -1: /* error, stop reading */ keepon = FALSE; continue; @@ -1426,12 +1612,12 @@ static CURLcode telnet_do(struct connectdata *conn, bool *done) default: /* read! */ if(pfd[0].revents & POLLIN) { /* read data from network */ - code = Curl_read(conn, sockfd, buf, BUFSIZE - 1, &nread); + result = Curl_read(conn, sockfd, buf, data->set.buffer_size, &nread); /* read would've blocked. Loop again */ - if(code == CURLE_AGAIN) + if(result == CURLE_AGAIN) break; /* returned not-zero, this an error */ - else if(code) { + if(result) { keepon = FALSE; break; } @@ -1444,8 +1630,8 @@ static CURLcode telnet_do(struct connectdata *conn, bool *done) total_dl += nread; Curl_pgrsSetDownloadCounter(data, total_dl); - code = telrcv(conn, (unsigned char *)buf, nread); - if(code) { + result = telrcv(conn, (unsigned char *)buf, nread); + if(result) { keepon = FALSE; break; } @@ -1461,13 +1647,14 @@ static CURLcode telnet_do(struct connectdata *conn, bool *done) nread = 0; if(poll_cnt == 2) { - if(pfd[1].revents & POLLIN) { /* read from stdin */ - nread = read(0, buf, BUFSIZE - 1); + if(pfd[1].revents & POLLIN) { /* read from in file */ + nread = read(pfd[1].fd, buf, data->set.buffer_size); } } else { /* read from user-supplied method */ - nread = (int)conn->fread_func(buf, 1, BUFSIZE - 1, conn->fread_in); + nread = (int)data->state.fread_func(buf, 1, data->set.buffer_size, + data->state.in); if(nread == CURL_READFUNC_ABORT) { keepon = FALSE; break; @@ -1477,8 +1664,8 @@ static CURLcode telnet_do(struct connectdata *conn, bool *done) } if(nread > 0) { - code = send_telnet_data(conn, buf, nread); - if(code) { + result = send_telnet_data(conn, buf, nread); + if(result) { keepon = FALSE; break; } @@ -1492,16 +1679,16 @@ static CURLcode telnet_do(struct connectdata *conn, bool *done) } /* poll switch statement */ if(data->set.timeout) { - now = Curl_tvnow(); - if(Curl_tvdiff(now, conn->created) >= data->set.timeout) { + now = Curl_now(); + if(Curl_timediff(now, conn->created) >= data->set.timeout) { failf(data, "Time-out"); - code = CURLE_OPERATION_TIMEDOUT; + result = CURLE_OPERATION_TIMEDOUT; keepon = FALSE; } } if(Curl_pgrsUpdate(conn)) { - code = CURLE_ABORTED_BY_CALLBACK; + result = CURLE_ABORTED_BY_CALLBACK; break; } } @@ -1509,6 +1696,6 @@ static CURLcode telnet_do(struct connectdata *conn, bool *done) /* mark this as "no further transfer wanted" */ Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL); - return code; + return result; } #endif