1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
10 * This software is licensed as described in the file COPYING, which
11 * you should have received as part of this distribution. The terms
12 * are also available at https://curl.se/docs/copyright.html.
14 * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15 * copies of the Software, and permit persons to whom the Software is
16 * furnished to do so, under the terms of the COPYING file.
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
21 ***************************************************************************/
23 #include "curl_setup.h"
25 #ifndef CURL_DISABLE_TELNET
27 #ifdef HAVE_NETINET_IN_H
28 #include <netinet/in.h>
33 #ifdef HAVE_ARPA_INET_H
34 #include <arpa/inet.h>
39 #ifdef HAVE_SYS_IOCTL_H
40 #include <sys/ioctl.h>
43 #ifdef HAVE_SYS_PARAM_H
44 #include <sys/param.h>
48 #include <curl/curl.h>
54 #include "system_win32.h"
55 #include "arpa_telnet.h"
60 /* The last 3 #include files should be in this order */
61 #include "curl_printf.h"
62 #include "curl_memory.h"
65 #define SUBBUFSIZE 512
67 #define CURL_SB_CLEAR(x) x->subpointer = x->subbuffer
68 #define CURL_SB_TERM(x) \
70 x->subend = x->subpointer; \
73 #define CURL_SB_ACCUM(x,c) \
75 if(x->subpointer < (x->subbuffer + sizeof(x->subbuffer))) \
76 *x->subpointer++ = (c); \
79 #define CURL_SB_GET(x) ((*x->subpointer++)&0xff)
80 #define CURL_SB_LEN(x) (x->subend - x->subpointer)
83 #define CURL_SB_PEEK(x) ((*x->subpointer)&0xff)
84 #define CURL_SB_EOF(x) (x->subpointer >= x->subend) */
86 #ifdef CURL_DISABLE_VERBOSE_STRINGS
87 #define printoption(a,b,c,d) Curl_nop_stmt
91 CURLcode telrcv(struct Curl_easy *data,
92 const unsigned char *inbuf, /* Data received from socket */
93 ssize_t count); /* Number of bytes received */
95 #ifndef CURL_DISABLE_VERBOSE_STRINGS
96 static void printoption(struct Curl_easy *data,
97 const char *direction,
101 static void negotiate(struct Curl_easy *data);
102 static void send_negotiation(struct Curl_easy *data, int cmd, int option);
103 static void set_local_option(struct Curl_easy *data,
104 int option, int newstate);
105 static void set_remote_option(struct Curl_easy *data,
106 int option, int newstate);
108 static void printsub(struct Curl_easy *data,
109 int direction, unsigned char *pointer,
111 static void suboption(struct Curl_easy *data);
112 static void sendsuboption(struct Curl_easy *data, int option);
114 static CURLcode telnet_do(struct Curl_easy *data, bool *done);
115 static CURLcode telnet_done(struct Curl_easy *data,
116 CURLcode, bool premature);
117 static CURLcode send_telnet_data(struct Curl_easy *data,
118 char *buffer, ssize_t nread);
120 /* For negotiation compliant to RFC 1143 */
123 #define CURL_WANTYES 2
124 #define CURL_WANTNO 3
127 #define CURL_OPPOSITE 1
130 * Telnet receiver states for fsm
141 CURL_TS_SB, /* sub-option collection */
142 CURL_TS_SE /* looking for sub-option end */
146 int please_negotiate;
147 int already_negotiated;
150 int us_preferred[256];
153 int him_preferred[256];
154 int subnegotiation[256];
155 char subopt_ttype[32]; /* Set with suboption TTYPE */
156 char subopt_xdisploc[128]; /* Set with suboption XDISPLOC */
157 unsigned short subopt_wsx; /* Set with suboption NAWS */
158 unsigned short subopt_wsy; /* Set with suboption NAWS */
159 TelnetReceive telrcv_state;
160 struct curl_slist *telnet_vars; /* Environment variables */
163 unsigned char subbuffer[SUBBUFSIZE];
164 unsigned char *subpointer, *subend; /* buffer for sub-options */
169 * TELNET protocol handler.
172 const struct Curl_handler Curl_handler_telnet = {
173 "TELNET", /* scheme */
174 ZERO_NULL, /* setup_connection */
175 telnet_do, /* do_it */
176 telnet_done, /* done */
177 ZERO_NULL, /* do_more */
178 ZERO_NULL, /* connect_it */
179 ZERO_NULL, /* connecting */
180 ZERO_NULL, /* doing */
181 ZERO_NULL, /* proto_getsock */
182 ZERO_NULL, /* doing_getsock */
183 ZERO_NULL, /* domore_getsock */
184 ZERO_NULL, /* perform_getsock */
185 ZERO_NULL, /* disconnect */
186 ZERO_NULL, /* readwrite */
187 ZERO_NULL, /* connection_check */
188 ZERO_NULL, /* attach connection */
189 PORT_TELNET, /* defport */
190 CURLPROTO_TELNET, /* protocol */
191 CURLPROTO_TELNET, /* family */
192 PROTOPT_NONE | PROTOPT_NOURLQUERY /* flags */
197 CURLcode init_telnet(struct Curl_easy *data)
201 tn = calloc(1, sizeof(struct TELNET));
203 return CURLE_OUT_OF_MEMORY;
205 data->req.p.telnet = tn; /* make us known */
207 tn->telrcv_state = CURL_TS_DATA;
209 /* Init suboptions */
212 /* Set the options we want by default */
213 tn->us_preferred[CURL_TELOPT_SGA] = CURL_YES;
214 tn->him_preferred[CURL_TELOPT_SGA] = CURL_YES;
216 /* To be compliant with previous releases of libcurl
217 we enable this option by default. This behavior
218 can be changed thanks to the "BINARY" option in
219 CURLOPT_TELNETOPTIONS
221 tn->us_preferred[CURL_TELOPT_BINARY] = CURL_YES;
222 tn->him_preferred[CURL_TELOPT_BINARY] = CURL_YES;
224 /* We must allow the server to echo what we sent
225 but it is not necessary to request the server
226 to do so (it might forces the server to close
227 the connection). Hence, we ignore ECHO in the
230 tn->him_preferred[CURL_TELOPT_ECHO] = CURL_YES;
232 /* Set the subnegotiation fields to send information
233 just after negotiation passed (do/will)
235 Default values are (0,0) initialized by calloc.
236 According to the RFC1013 it is valid:
237 A value equal to zero is acceptable for the width (or height),
238 and means that no character width (or height) is being sent.
239 In this case, the width (or height) that will be assumed by the
240 Telnet server is operating system specific (it will probably be
241 based upon the terminal type information that may have been sent
242 using the TERMINAL TYPE Telnet option). */
243 tn->subnegotiation[CURL_TELOPT_NAWS] = CURL_YES;
247 static void negotiate(struct Curl_easy *data)
250 struct TELNET *tn = data->req.p.telnet;
252 for(i = 0; i < CURL_NTELOPTS; i++) {
253 if(i == CURL_TELOPT_ECHO)
256 if(tn->us_preferred[i] == CURL_YES)
257 set_local_option(data, i, CURL_YES);
259 if(tn->him_preferred[i] == CURL_YES)
260 set_remote_option(data, i, CURL_YES);
264 #ifndef CURL_DISABLE_VERBOSE_STRINGS
265 static void printoption(struct Curl_easy *data,
266 const char *direction, int cmd, int option)
268 if(data->set.verbose) {
269 if(cmd == CURL_IAC) {
270 if(CURL_TELCMD_OK(option))
271 infof(data, "%s IAC %s", direction, CURL_TELCMD(option));
273 infof(data, "%s IAC %d", direction, option);
276 const char *fmt = (cmd == CURL_WILL) ? "WILL" :
277 (cmd == CURL_WONT) ? "WONT" :
278 (cmd == CURL_DO) ? "DO" :
279 (cmd == CURL_DONT) ? "DONT" : 0;
282 if(CURL_TELOPT_OK(option))
283 opt = CURL_TELOPT(option);
284 else if(option == CURL_TELOPT_EXOPL)
290 infof(data, "%s %s %s", direction, fmt, opt);
292 infof(data, "%s %s %d", direction, fmt, option);
295 infof(data, "%s %d %d", direction, cmd, option);
301 static void send_negotiation(struct Curl_easy *data, int cmd, int option)
303 unsigned char buf[3];
304 ssize_t bytes_written;
305 struct connectdata *conn = data->conn;
308 buf[1] = (unsigned char)cmd;
309 buf[2] = (unsigned char)option;
311 bytes_written = swrite(conn->sock[FIRSTSOCKET], buf, 3);
312 if(bytes_written < 0) {
314 failf(data,"Sending data failed (%d)",err);
317 printoption(data, "SENT", cmd, option);
321 void set_remote_option(struct Curl_easy *data, int option, int newstate)
323 struct TELNET *tn = data->req.p.telnet;
324 if(newstate == CURL_YES) {
325 switch(tn->him[option]) {
327 tn->him[option] = CURL_WANTYES;
328 send_negotiation(data, CURL_DO, option);
332 /* Already enabled */
336 switch(tn->himq[option]) {
338 /* Already negotiating for CURL_YES, queue the request */
339 tn->himq[option] = CURL_OPPOSITE;
342 /* Error: already queued an enable request */
348 switch(tn->himq[option]) {
350 /* Error: already negotiating for enable */
353 tn->himq[option] = CURL_EMPTY;
360 switch(tn->him[option]) {
362 /* Already disabled */
366 tn->him[option] = CURL_WANTNO;
367 send_negotiation(data, CURL_DONT, option);
371 switch(tn->himq[option]) {
373 /* Already negotiating for NO */
376 tn->himq[option] = CURL_EMPTY;
382 switch(tn->himq[option]) {
384 tn->himq[option] = CURL_OPPOSITE;
395 void rec_will(struct Curl_easy *data, int option)
397 struct TELNET *tn = data->req.p.telnet;
398 switch(tn->him[option]) {
400 if(tn->him_preferred[option] == CURL_YES) {
401 tn->him[option] = CURL_YES;
402 send_negotiation(data, CURL_DO, option);
405 send_negotiation(data, CURL_DONT, option);
410 /* Already enabled */
414 switch(tn->himq[option]) {
416 /* Error: DONT answered by WILL */
417 tn->him[option] = CURL_NO;
420 /* Error: DONT answered by WILL */
421 tn->him[option] = CURL_YES;
422 tn->himq[option] = CURL_EMPTY;
428 switch(tn->himq[option]) {
430 tn->him[option] = CURL_YES;
433 tn->him[option] = CURL_WANTNO;
434 tn->himq[option] = CURL_EMPTY;
435 send_negotiation(data, CURL_DONT, option);
443 void rec_wont(struct Curl_easy *data, int option)
445 struct TELNET *tn = data->req.p.telnet;
446 switch(tn->him[option]) {
448 /* Already disabled */
452 tn->him[option] = CURL_NO;
453 send_negotiation(data, CURL_DONT, option);
457 switch(tn->himq[option]) {
459 tn->him[option] = CURL_NO;
463 tn->him[option] = CURL_WANTYES;
464 tn->himq[option] = CURL_EMPTY;
465 send_negotiation(data, CURL_DO, option);
471 switch(tn->himq[option]) {
473 tn->him[option] = CURL_NO;
476 tn->him[option] = CURL_NO;
477 tn->himq[option] = CURL_EMPTY;
485 set_local_option(struct Curl_easy *data, int option, int newstate)
487 struct TELNET *tn = data->req.p.telnet;
488 if(newstate == CURL_YES) {
489 switch(tn->us[option]) {
491 tn->us[option] = CURL_WANTYES;
492 send_negotiation(data, CURL_WILL, option);
496 /* Already enabled */
500 switch(tn->usq[option]) {
502 /* Already negotiating for CURL_YES, queue the request */
503 tn->usq[option] = CURL_OPPOSITE;
506 /* Error: already queued an enable request */
512 switch(tn->usq[option]) {
514 /* Error: already negotiating for enable */
517 tn->usq[option] = CURL_EMPTY;
524 switch(tn->us[option]) {
526 /* Already disabled */
530 tn->us[option] = CURL_WANTNO;
531 send_negotiation(data, CURL_WONT, option);
535 switch(tn->usq[option]) {
537 /* Already negotiating for NO */
540 tn->usq[option] = CURL_EMPTY;
546 switch(tn->usq[option]) {
548 tn->usq[option] = CURL_OPPOSITE;
559 void rec_do(struct Curl_easy *data, int option)
561 struct TELNET *tn = data->req.p.telnet;
562 switch(tn->us[option]) {
564 if(tn->us_preferred[option] == CURL_YES) {
565 tn->us[option] = CURL_YES;
566 send_negotiation(data, CURL_WILL, option);
567 if(tn->subnegotiation[option] == CURL_YES)
568 /* transmission of data option */
569 sendsuboption(data, option);
571 else if(tn->subnegotiation[option] == CURL_YES) {
572 /* send information to achieve this option*/
573 tn->us[option] = CURL_YES;
574 send_negotiation(data, CURL_WILL, option);
575 sendsuboption(data, option);
578 send_negotiation(data, CURL_WONT, option);
582 /* Already enabled */
586 switch(tn->usq[option]) {
588 /* Error: DONT answered by WILL */
589 tn->us[option] = CURL_NO;
592 /* Error: DONT answered by WILL */
593 tn->us[option] = CURL_YES;
594 tn->usq[option] = CURL_EMPTY;
600 switch(tn->usq[option]) {
602 tn->us[option] = CURL_YES;
603 if(tn->subnegotiation[option] == CURL_YES) {
604 /* transmission of data option */
605 sendsuboption(data, option);
609 tn->us[option] = CURL_WANTNO;
610 tn->himq[option] = CURL_EMPTY;
611 send_negotiation(data, CURL_WONT, option);
619 void rec_dont(struct Curl_easy *data, int option)
621 struct TELNET *tn = data->req.p.telnet;
622 switch(tn->us[option]) {
624 /* Already disabled */
628 tn->us[option] = CURL_NO;
629 send_negotiation(data, CURL_WONT, option);
633 switch(tn->usq[option]) {
635 tn->us[option] = CURL_NO;
639 tn->us[option] = CURL_WANTYES;
640 tn->usq[option] = CURL_EMPTY;
641 send_negotiation(data, CURL_WILL, option);
647 switch(tn->usq[option]) {
649 tn->us[option] = CURL_NO;
652 tn->us[option] = CURL_NO;
653 tn->usq[option] = CURL_EMPTY;
661 static void printsub(struct Curl_easy *data,
662 int direction, /* '<' or '>' */
663 unsigned char *pointer, /* where suboption data is */
664 size_t length) /* length of suboption data */
666 if(data->set.verbose) {
669 infof(data, "%s IAC SB ", (direction == '<')? "RCVD":"SENT");
673 i = pointer[length-2];
674 j = pointer[length-1];
676 if(i != CURL_IAC || j != CURL_SE) {
677 infof(data, "(terminated by ");
678 if(CURL_TELOPT_OK(i))
679 infof(data, "%s ", CURL_TELOPT(i));
680 else if(CURL_TELCMD_OK(i))
681 infof(data, "%s ", CURL_TELCMD(i));
683 infof(data, "%u ", i);
684 if(CURL_TELOPT_OK(j))
685 infof(data, "%s", CURL_TELOPT(j));
686 else if(CURL_TELCMD_OK(j))
687 infof(data, "%s", CURL_TELCMD(j));
689 infof(data, "%d", j);
690 infof(data, ", not IAC SE) ");
696 infof(data, "(Empty suboption?)");
700 if(CURL_TELOPT_OK(pointer[0])) {
702 case CURL_TELOPT_TTYPE:
703 case CURL_TELOPT_XDISPLOC:
704 case CURL_TELOPT_NEW_ENVIRON:
705 case CURL_TELOPT_NAWS:
706 infof(data, "%s", CURL_TELOPT(pointer[0]));
709 infof(data, "%s (unsupported)", CURL_TELOPT(pointer[0]));
714 infof(data, "%d (unknown)", pointer[i]);
717 case CURL_TELOPT_NAWS:
719 infof(data, "Width: %d ; Height: %d", (pointer[1]<<8) | pointer[2],
720 (pointer[3]<<8) | pointer[4]);
724 case CURL_TELQUAL_IS:
727 case CURL_TELQUAL_SEND:
728 infof(data, " SEND");
730 case CURL_TELQUAL_INFO:
731 infof(data, " INFO/REPLY");
733 case CURL_TELQUAL_NAME:
734 infof(data, " NAME");
739 case CURL_TELOPT_TTYPE:
740 case CURL_TELOPT_XDISPLOC:
742 infof(data, " \"%s\"", &pointer[2]);
744 case CURL_TELOPT_NEW_ENVIRON:
745 if(pointer[1] == CURL_TELQUAL_IS) {
747 for(i = 3; i < length; i++) {
749 case CURL_NEW_ENV_VAR:
752 case CURL_NEW_ENV_VALUE:
756 infof(data, "%c", pointer[i]);
763 for(i = 2; i < length; i++)
764 infof(data, " %.2x", pointer[i]);
771 static CURLcode check_telnet_options(struct Curl_easy *data)
773 struct curl_slist *head;
774 struct curl_slist *beg;
775 char option_keyword[128] = "";
776 char option_arg[256] = "";
777 struct TELNET *tn = data->req.p.telnet;
778 struct connectdata *conn = data->conn;
779 CURLcode result = CURLE_OK;
782 /* Add the user name as an environment variable if it
783 was given on the command line */
784 if(data->state.aptr.user) {
785 msnprintf(option_arg, sizeof(option_arg), "USER,%s", conn->user);
786 beg = curl_slist_append(tn->telnet_vars, option_arg);
788 curl_slist_free_all(tn->telnet_vars);
789 tn->telnet_vars = NULL;
790 return CURLE_OUT_OF_MEMORY;
792 tn->telnet_vars = beg;
793 tn->us_preferred[CURL_TELOPT_NEW_ENVIRON] = CURL_YES;
796 for(head = data->set.telnet_options; head; head = head->next) {
797 if(sscanf(head->data, "%127[^= ]%*[ =]%255s",
798 option_keyword, option_arg) == 2) {
801 if(strcasecompare(option_keyword, "TTYPE")) {
802 strncpy(tn->subopt_ttype, option_arg, 31);
803 tn->subopt_ttype[31] = 0; /* String termination */
804 tn->us_preferred[CURL_TELOPT_TTYPE] = CURL_YES;
808 /* Display variable */
809 if(strcasecompare(option_keyword, "XDISPLOC")) {
810 strncpy(tn->subopt_xdisploc, option_arg, 127);
811 tn->subopt_xdisploc[127] = 0; /* String termination */
812 tn->us_preferred[CURL_TELOPT_XDISPLOC] = CURL_YES;
816 /* Environment variable */
817 if(strcasecompare(option_keyword, "NEW_ENV")) {
818 beg = curl_slist_append(tn->telnet_vars, option_arg);
820 result = CURLE_OUT_OF_MEMORY;
823 tn->telnet_vars = beg;
824 tn->us_preferred[CURL_TELOPT_NEW_ENVIRON] = CURL_YES;
829 if(strcasecompare(option_keyword, "WS")) {
830 if(sscanf(option_arg, "%hu%*[xX]%hu",
831 &tn->subopt_wsx, &tn->subopt_wsy) == 2)
832 tn->us_preferred[CURL_TELOPT_NAWS] = CURL_YES;
834 failf(data, "Syntax error in telnet option: %s", head->data);
835 result = CURLE_SETOPT_OPTION_SYNTAX;
841 /* To take care or not of the 8th bit in data exchange */
842 if(strcasecompare(option_keyword, "BINARY")) {
843 binary_option = atoi(option_arg);
844 if(binary_option != 1) {
845 tn->us_preferred[CURL_TELOPT_BINARY] = CURL_NO;
846 tn->him_preferred[CURL_TELOPT_BINARY] = CURL_NO;
851 failf(data, "Unknown telnet option %s", head->data);
852 result = CURLE_UNKNOWN_OPTION;
855 failf(data, "Syntax error in telnet option: %s", head->data);
856 result = CURLE_SETOPT_OPTION_SYNTAX;
861 curl_slist_free_all(tn->telnet_vars);
862 tn->telnet_vars = NULL;
871 * Look at the sub-option buffer, and try to be helpful to the other
875 static void suboption(struct Curl_easy *data)
877 struct curl_slist *v;
878 unsigned char temp[2048];
879 ssize_t bytes_written;
882 char varname[128] = "";
883 char varval[128] = "";
884 struct TELNET *tn = data->req.p.telnet;
885 struct connectdata *conn = data->conn;
887 printsub(data, '<', (unsigned char *)tn->subbuffer, CURL_SB_LEN(tn) + 2);
888 switch(CURL_SB_GET(tn)) {
889 case CURL_TELOPT_TTYPE:
890 len = strlen(tn->subopt_ttype) + 4 + 2;
891 msnprintf((char *)temp, sizeof(temp),
892 "%c%c%c%c%s%c%c", CURL_IAC, CURL_SB, CURL_TELOPT_TTYPE,
893 CURL_TELQUAL_IS, tn->subopt_ttype, CURL_IAC, CURL_SE);
894 bytes_written = swrite(conn->sock[FIRSTSOCKET], temp, len);
895 if(bytes_written < 0) {
897 failf(data,"Sending data failed (%d)",err);
899 printsub(data, '>', &temp[2], len-2);
901 case CURL_TELOPT_XDISPLOC:
902 len = strlen(tn->subopt_xdisploc) + 4 + 2;
903 msnprintf((char *)temp, sizeof(temp),
904 "%c%c%c%c%s%c%c", CURL_IAC, CURL_SB, CURL_TELOPT_XDISPLOC,
905 CURL_TELQUAL_IS, tn->subopt_xdisploc, CURL_IAC, CURL_SE);
906 bytes_written = swrite(conn->sock[FIRSTSOCKET], temp, len);
907 if(bytes_written < 0) {
909 failf(data,"Sending data failed (%d)",err);
911 printsub(data, '>', &temp[2], len-2);
913 case CURL_TELOPT_NEW_ENVIRON:
914 msnprintf((char *)temp, sizeof(temp),
915 "%c%c%c%c", CURL_IAC, CURL_SB, CURL_TELOPT_NEW_ENVIRON,
919 for(v = tn->telnet_vars; v; v = v->next) {
920 size_t tmplen = (strlen(v->data) + 1);
921 /* Add the variable only if it fits */
922 if(len + tmplen < (int)sizeof(temp)-6) {
926 rv = sscanf(v->data, "%127[^,]%1[,]%127s", varname, sep, varval);
928 len += msnprintf((char *)&temp[len], sizeof(temp) - len,
929 "%c%s", CURL_NEW_ENV_VAR, varname);
931 len += msnprintf((char *)&temp[len], sizeof(temp) - len,
932 "%c%s%c%s", CURL_NEW_ENV_VAR, varname,
933 CURL_NEW_ENV_VALUE, varval);
936 msnprintf((char *)&temp[len], sizeof(temp) - len,
937 "%c%c", CURL_IAC, CURL_SE);
939 bytes_written = swrite(conn->sock[FIRSTSOCKET], temp, len);
940 if(bytes_written < 0) {
942 failf(data,"Sending data failed (%d)",err);
944 printsub(data, '>', &temp[2], len-2);
954 * Send suboption information to the server side.
957 static void sendsuboption(struct Curl_easy *data, int option)
959 ssize_t bytes_written;
962 unsigned char *uc1, *uc2;
963 struct TELNET *tn = data->req.p.telnet;
964 struct connectdata *conn = data->conn;
967 case CURL_TELOPT_NAWS:
968 /* We prepare data to be sent */
970 CURL_SB_ACCUM(tn, CURL_IAC);
971 CURL_SB_ACCUM(tn, CURL_SB);
972 CURL_SB_ACCUM(tn, CURL_TELOPT_NAWS);
973 /* We must deal either with little or big endian processors */
974 /* Window size must be sent according to the 'network order' */
975 x = htons(tn->subopt_wsx);
976 y = htons(tn->subopt_wsy);
977 uc1 = (unsigned char *)&x;
978 uc2 = (unsigned char *)&y;
979 CURL_SB_ACCUM(tn, uc1[0]);
980 CURL_SB_ACCUM(tn, uc1[1]);
981 CURL_SB_ACCUM(tn, uc2[0]);
982 CURL_SB_ACCUM(tn, uc2[1]);
984 CURL_SB_ACCUM(tn, CURL_IAC);
985 CURL_SB_ACCUM(tn, CURL_SE);
987 /* data suboption is now ready */
989 printsub(data, '>', (unsigned char *)tn->subbuffer + 2,
992 /* we send the header of the suboption... */
993 bytes_written = swrite(conn->sock[FIRSTSOCKET], tn->subbuffer, 3);
994 if(bytes_written < 0) {
996 failf(data, "Sending data failed (%d)", err);
998 /* ... then the window size with the send_telnet_data() function
999 to deal with 0xFF cases ... */
1000 send_telnet_data(data, (char *)tn->subbuffer + 3, 4);
1001 /* ... and the footer */
1002 bytes_written = swrite(conn->sock[FIRSTSOCKET], tn->subbuffer + 7, 2);
1003 if(bytes_written < 0) {
1005 failf(data, "Sending data failed (%d)", err);
1013 CURLcode telrcv(struct Curl_easy *data,
1014 const unsigned char *inbuf, /* Data received from socket */
1015 ssize_t count) /* Number of bytes received */
1020 int startwrite = -1;
1021 struct TELNET *tn = data->req.p.telnet;
1023 #define startskipping() \
1024 if(startwrite >= 0) { \
1025 result = Curl_client_write(data, \
1027 (char *)&inbuf[startwrite], \
1034 #define writebyte() \
1035 if(startwrite < 0) \
1038 #define bufferflush() startskipping()
1043 switch(tn->telrcv_state) {
1045 tn->telrcv_state = CURL_TS_DATA;
1048 break; /* Ignore \0 after CR */
1055 tn->telrcv_state = CURL_TS_IAC;
1060 tn->telrcv_state = CURL_TS_CR;
1066 DEBUGASSERT(startwrite < 0);
1069 tn->telrcv_state = CURL_TS_WILL;
1072 tn->telrcv_state = CURL_TS_WONT;
1075 tn->telrcv_state = CURL_TS_DO;
1078 tn->telrcv_state = CURL_TS_DONT;
1082 tn->telrcv_state = CURL_TS_SB;
1085 tn->telrcv_state = CURL_TS_DATA;
1092 tn->telrcv_state = CURL_TS_DATA;
1093 printoption(data, "RCVD", CURL_IAC, c);
1099 printoption(data, "RCVD", CURL_WILL, c);
1100 tn->please_negotiate = 1;
1102 tn->telrcv_state = CURL_TS_DATA;
1106 printoption(data, "RCVD", CURL_WONT, c);
1107 tn->please_negotiate = 1;
1109 tn->telrcv_state = CURL_TS_DATA;
1113 printoption(data, "RCVD", CURL_DO, c);
1114 tn->please_negotiate = 1;
1116 tn->telrcv_state = CURL_TS_DATA;
1120 printoption(data, "RCVD", CURL_DONT, c);
1121 tn->please_negotiate = 1;
1123 tn->telrcv_state = CURL_TS_DATA;
1128 tn->telrcv_state = CURL_TS_SE;
1130 CURL_SB_ACCUM(tn, c);
1137 * This is an error. We only expect to get "IAC IAC" or "IAC SE".
1138 * Several things may have happened. An IAC was not doubled, the
1139 * IAC SE was left off, or another option got inserted into the
1140 * suboption are all possibilities. If we assume that the IAC was
1141 * not doubled, and really the IAC SE was left off, we could get
1142 * into an infinite loop here. So, instead, we terminate the
1143 * suboption, and process the partial suboption if we can.
1145 CURL_SB_ACCUM(tn, CURL_IAC);
1146 CURL_SB_ACCUM(tn, c);
1147 tn->subpointer -= 2;
1150 printoption(data, "In SUBOPTION processing, RCVD", CURL_IAC, c);
1151 suboption(data); /* handle sub-option */
1152 tn->telrcv_state = CURL_TS_IAC;
1155 CURL_SB_ACCUM(tn, c);
1156 tn->telrcv_state = CURL_TS_SB;
1159 CURL_SB_ACCUM(tn, CURL_IAC);
1160 CURL_SB_ACCUM(tn, CURL_SE);
1161 tn->subpointer -= 2;
1163 suboption(data); /* handle sub-option */
1164 tn->telrcv_state = CURL_TS_DATA;
1174 /* Escape and send a telnet data block */
1175 static CURLcode send_telnet_data(struct Curl_easy *data,
1176 char *buffer, ssize_t nread)
1178 ssize_t escapes, i, outlen;
1179 unsigned char *outbuf = NULL;
1180 CURLcode result = CURLE_OK;
1181 ssize_t bytes_written, total_written;
1182 struct connectdata *conn = data->conn;
1184 /* Determine size of new buffer after escaping */
1186 for(i = 0; i < nread; i++)
1187 if((unsigned char)buffer[i] == CURL_IAC)
1189 outlen = nread + escapes;
1192 outbuf = (unsigned char *)buffer;
1195 outbuf = malloc(nread + escapes + 1);
1197 return CURLE_OUT_OF_MEMORY;
1200 for(i = 0; i < nread; i++) {
1201 outbuf[j++] = buffer[i];
1202 if((unsigned char)buffer[i] == CURL_IAC)
1203 outbuf[j++] = CURL_IAC;
1209 while(!result && total_written < outlen) {
1210 /* Make sure socket is writable to avoid EWOULDBLOCK condition */
1211 struct pollfd pfd[1];
1212 pfd[0].fd = conn->sock[FIRSTSOCKET];
1213 pfd[0].events = POLLOUT;
1214 switch(Curl_poll(pfd, 1, -1)) {
1215 case -1: /* error, abort writing */
1216 case 0: /* timeout (will never happen) */
1217 result = CURLE_SEND_ERROR;
1219 default: /* write! */
1221 result = Curl_write(data, conn->sock[FIRSTSOCKET],
1222 outbuf + total_written,
1223 outlen - total_written,
1225 total_written += bytes_written;
1230 /* Free malloc copy if escaped */
1231 if(outbuf != (unsigned char *)buffer)
1237 static CURLcode telnet_done(struct Curl_easy *data,
1238 CURLcode status, bool premature)
1240 struct TELNET *tn = data->req.p.telnet;
1241 (void)status; /* unused */
1242 (void)premature; /* not used */
1247 curl_slist_free_all(tn->telnet_vars);
1248 tn->telnet_vars = NULL;
1250 Curl_safefree(data->req.p.telnet);
1255 static CURLcode telnet_do(struct Curl_easy *data, bool *done)
1258 struct connectdata *conn = data->conn;
1259 curl_socket_t sockfd = conn->sock[FIRSTSOCKET];
1261 WSAEVENT event_handle;
1262 WSANETWORKEVENTS events;
1263 HANDLE stdin_handle;
1267 DWORD readfile_read;
1270 timediff_t interval_ms;
1271 struct pollfd pfd[2];
1273 curl_off_t total_dl = 0;
1274 curl_off_t total_ul = 0;
1277 struct curltime now;
1279 char *buf = data->state.buffer;
1282 *done = TRUE; /* unconditionally */
1284 result = init_telnet(data);
1288 tn = data->req.p.telnet;
1290 result = check_telnet_options(data);
1295 /* We want to wait for both stdin and the socket. Since
1296 ** the select() function in winsock only works on sockets
1297 ** we have to use the WaitForMultipleObjects() call.
1300 /* First, create a sockets event object */
1301 event_handle = WSACreateEvent();
1302 if(event_handle == WSA_INVALID_EVENT) {
1303 failf(data, "WSACreateEvent failed (%d)", SOCKERRNO);
1304 return CURLE_FAILED_INIT;
1307 /* Tell winsock what events we want to listen to */
1308 if(WSAEventSelect(sockfd, event_handle, FD_READ|FD_CLOSE) == SOCKET_ERROR) {
1309 WSACloseEvent(event_handle);
1313 /* The get the Windows file handle for stdin */
1314 stdin_handle = GetStdHandle(STD_INPUT_HANDLE);
1316 /* Create the list of objects to wait for */
1317 objs[0] = event_handle;
1318 objs[1] = stdin_handle;
1320 /* If stdin_handle is a pipe, use PeekNamedPipe() method to check it,
1321 else use the old WaitForMultipleObjects() way */
1322 if(GetFileType(stdin_handle) == FILE_TYPE_PIPE ||
1323 data->set.is_fread_set) {
1324 /* Don't wait for stdin_handle, just wait for event_handle */
1326 /* Check stdin_handle per 100 milliseconds */
1331 wait_timeout = 1000;
1334 /* Keep on listening and act on events */
1336 const DWORD buf_size = (DWORD)data->set.buffer_size;
1337 DWORD waitret = WaitForMultipleObjects(obj_count, objs,
1338 FALSE, wait_timeout);
1344 if(data->set.is_fread_set) {
1346 /* read from user-supplied method */
1347 n = data->state.fread_func(buf, 1, buf_size, data->state.in);
1348 if(n == CURL_READFUNC_ABORT) {
1350 result = CURLE_READ_ERROR;
1354 if(n == CURL_READFUNC_PAUSE)
1357 if(n == 0) /* no bytes */
1360 /* fall through with number of bytes read */
1361 readfile_read = (DWORD)n;
1364 /* read from stdin */
1365 if(!PeekNamedPipe(stdin_handle, NULL, 0, NULL,
1366 &readfile_read, NULL)) {
1368 result = CURLE_READ_ERROR;
1375 if(!ReadFile(stdin_handle, buf, buf_size,
1376 &readfile_read, NULL)) {
1378 result = CURLE_READ_ERROR;
1383 result = send_telnet_data(data, buf, readfile_read);
1392 case WAIT_OBJECT_0 + 1:
1394 if(!ReadFile(stdin_handle, buf, buf_size,
1395 &readfile_read, NULL)) {
1397 result = CURLE_READ_ERROR;
1401 result = send_telnet_data(data, buf, readfile_read);
1411 events.lNetworkEvents = 0;
1412 if(WSAEnumNetworkEvents(sockfd, event_handle, &events) == SOCKET_ERROR) {
1414 if(err != EINPROGRESS) {
1415 infof(data, "WSAEnumNetworkEvents failed (%d)", err);
1417 result = CURLE_READ_ERROR;
1421 if(events.lNetworkEvents & FD_READ) {
1422 /* read data from network */
1423 result = Curl_read(data, sockfd, buf, data->set.buffer_size, &nread);
1424 /* read would've blocked. Loop again */
1425 if(result == CURLE_AGAIN)
1427 /* returned not-zero, this an error */
1432 /* returned zero but actually received 0 or less here,
1433 the server closed the connection and we bail out */
1434 else if(nread <= 0) {
1439 result = telrcv(data, (unsigned char *) buf, nread);
1445 /* Negotiate if the peer has started negotiating,
1446 otherwise don't. We don't want to speak telnet with
1447 non-telnet servers, like POP or SMTP. */
1448 if(tn->please_negotiate && !tn->already_negotiated) {
1450 tn->already_negotiated = 1;
1453 if(events.lNetworkEvents & FD_CLOSE) {
1461 if(data->set.timeout) {
1463 if(Curl_timediff(now, conn->created) >= data->set.timeout) {
1464 failf(data, "Time-out");
1465 result = CURLE_OPERATION_TIMEDOUT;
1471 /* We called WSACreateEvent, so call WSACloseEvent */
1472 if(!WSACloseEvent(event_handle)) {
1473 infof(data, "WSACloseEvent failed (%d)", SOCKERRNO);
1477 pfd[0].events = POLLIN;
1479 if(data->set.is_fread_set) {
1481 interval_ms = 100; /* poll user-supplied read function */
1484 /* really using fread, so infile is a FILE* */
1485 pfd[1].fd = fileno((FILE *)data->state.in);
1486 pfd[1].events = POLLIN;
1488 interval_ms = 1 * 1000;
1492 switch(Curl_poll(pfd, poll_cnt, interval_ms)) {
1493 case -1: /* error, stop reading */
1496 case 0: /* timeout */
1500 default: /* read! */
1501 if(pfd[0].revents & POLLIN) {
1502 /* read data from network */
1503 result = Curl_read(data, sockfd, buf, data->set.buffer_size, &nread);
1504 /* read would've blocked. Loop again */
1505 if(result == CURLE_AGAIN)
1507 /* returned not-zero, this an error */
1512 /* returned zero but actually received 0 or less here,
1513 the server closed the connection and we bail out */
1514 else if(nread <= 0) {
1520 Curl_pgrsSetDownloadCounter(data, total_dl);
1521 result = telrcv(data, (unsigned char *)buf, nread);
1527 /* Negotiate if the peer has started negotiating,
1528 otherwise don't. We don't want to speak telnet with
1529 non-telnet servers, like POP or SMTP. */
1530 if(tn->please_negotiate && !tn->already_negotiated) {
1532 tn->already_negotiated = 1;
1538 if(pfd[1].revents & POLLIN) { /* read from in file */
1539 nread = read(pfd[1].fd, buf, data->set.buffer_size);
1543 /* read from user-supplied method */
1544 nread = (int)data->state.fread_func(buf, 1, data->set.buffer_size,
1546 if(nread == CURL_READFUNC_ABORT) {
1550 if(nread == CURL_READFUNC_PAUSE)
1555 result = send_telnet_data(data, buf, nread);
1561 Curl_pgrsSetUploadCounter(data, total_ul);
1567 } /* poll switch statement */
1569 if(data->set.timeout) {
1571 if(Curl_timediff(now, conn->created) >= data->set.timeout) {
1572 failf(data, "Time-out");
1573 result = CURLE_OPERATION_TIMEDOUT;
1578 if(Curl_pgrsUpdate(data)) {
1579 result = CURLE_ABORTED_BY_CALLBACK;
1584 /* mark this as "no further transfer wanted" */
1585 Curl_setup_transfer(data, -1, -1, FALSE, -1);