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 * SPDX-License-Identifier: curl
23 ***************************************************************************/
25 #include "curl_setup.h"
27 #ifndef CURL_DISABLE_TELNET
29 #ifdef HAVE_NETINET_IN_H
30 #include <netinet/in.h>
35 #ifdef HAVE_ARPA_INET_H
36 #include <arpa/inet.h>
41 #ifdef HAVE_SYS_IOCTL_H
42 #include <sys/ioctl.h>
45 #ifdef HAVE_SYS_PARAM_H
46 #include <sys/param.h>
50 #include <curl/curl.h>
56 #include "system_win32.h"
57 #include "arpa_telnet.h"
62 /* The last 3 #include files should be in this order */
63 #include "curl_printf.h"
64 #include "curl_memory.h"
67 #define SUBBUFSIZE 512
69 #define CURL_SB_CLEAR(x) x->subpointer = x->subbuffer
70 #define CURL_SB_TERM(x) \
72 x->subend = x->subpointer; \
75 #define CURL_SB_ACCUM(x,c) \
77 if(x->subpointer < (x->subbuffer + sizeof(x->subbuffer))) \
78 *x->subpointer++ = (c); \
81 #define CURL_SB_GET(x) ((*x->subpointer++)&0xff)
82 #define CURL_SB_LEN(x) (x->subend - x->subpointer)
85 #define CURL_SB_PEEK(x) ((*x->subpointer)&0xff)
86 #define CURL_SB_EOF(x) (x->subpointer >= x->subend) */
88 #ifdef CURL_DISABLE_VERBOSE_STRINGS
89 #define printoption(a,b,c,d) Curl_nop_stmt
93 CURLcode telrcv(struct Curl_easy *data,
94 const unsigned char *inbuf, /* Data received from socket */
95 ssize_t count); /* Number of bytes received */
97 #ifndef CURL_DISABLE_VERBOSE_STRINGS
98 static void printoption(struct Curl_easy *data,
99 const char *direction,
100 int cmd, int option);
103 static void negotiate(struct Curl_easy *data);
104 static void send_negotiation(struct Curl_easy *data, int cmd, int option);
105 static void set_local_option(struct Curl_easy *data,
106 int option, int newstate);
107 static void set_remote_option(struct Curl_easy *data,
108 int option, int newstate);
110 static void printsub(struct Curl_easy *data,
111 int direction, unsigned char *pointer,
113 static void suboption(struct Curl_easy *data);
114 static void sendsuboption(struct Curl_easy *data, int option);
116 static CURLcode telnet_do(struct Curl_easy *data, bool *done);
117 static CURLcode telnet_done(struct Curl_easy *data,
118 CURLcode, bool premature);
119 static CURLcode send_telnet_data(struct Curl_easy *data,
120 char *buffer, ssize_t nread);
122 /* For negotiation compliant to RFC 1143 */
125 #define CURL_WANTYES 2
126 #define CURL_WANTNO 3
129 #define CURL_OPPOSITE 1
132 * Telnet receiver states for fsm
143 CURL_TS_SB, /* sub-option collection */
144 CURL_TS_SE /* looking for sub-option end */
148 int please_negotiate;
149 int already_negotiated;
152 int us_preferred[256];
155 int him_preferred[256];
156 int subnegotiation[256];
157 char subopt_ttype[32]; /* Set with suboption TTYPE */
158 char subopt_xdisploc[128]; /* Set with suboption XDISPLOC */
159 unsigned short subopt_wsx; /* Set with suboption NAWS */
160 unsigned short subopt_wsy; /* Set with suboption NAWS */
161 TelnetReceive telrcv_state;
162 struct curl_slist *telnet_vars; /* Environment variables */
165 unsigned char subbuffer[SUBBUFSIZE];
166 unsigned char *subpointer, *subend; /* buffer for sub-options */
171 * TELNET protocol handler.
174 const struct Curl_handler Curl_handler_telnet = {
175 "TELNET", /* scheme */
176 ZERO_NULL, /* setup_connection */
177 telnet_do, /* do_it */
178 telnet_done, /* done */
179 ZERO_NULL, /* do_more */
180 ZERO_NULL, /* connect_it */
181 ZERO_NULL, /* connecting */
182 ZERO_NULL, /* doing */
183 ZERO_NULL, /* proto_getsock */
184 ZERO_NULL, /* doing_getsock */
185 ZERO_NULL, /* domore_getsock */
186 ZERO_NULL, /* perform_getsock */
187 ZERO_NULL, /* disconnect */
188 ZERO_NULL, /* readwrite */
189 ZERO_NULL, /* connection_check */
190 ZERO_NULL, /* attach connection */
191 PORT_TELNET, /* defport */
192 CURLPROTO_TELNET, /* protocol */
193 CURLPROTO_TELNET, /* family */
194 PROTOPT_NONE | PROTOPT_NOURLQUERY /* flags */
199 CURLcode init_telnet(struct Curl_easy *data)
203 tn = calloc(1, sizeof(struct TELNET));
205 return CURLE_OUT_OF_MEMORY;
207 data->req.p.telnet = tn; /* make us known */
209 tn->telrcv_state = CURL_TS_DATA;
211 /* Init suboptions */
214 /* Set the options we want by default */
215 tn->us_preferred[CURL_TELOPT_SGA] = CURL_YES;
216 tn->him_preferred[CURL_TELOPT_SGA] = CURL_YES;
218 /* To be compliant with previous releases of libcurl
219 we enable this option by default. This behavior
220 can be changed thanks to the "BINARY" option in
221 CURLOPT_TELNETOPTIONS
223 tn->us_preferred[CURL_TELOPT_BINARY] = CURL_YES;
224 tn->him_preferred[CURL_TELOPT_BINARY] = CURL_YES;
226 /* We must allow the server to echo what we sent
227 but it is not necessary to request the server
228 to do so (it might forces the server to close
229 the connection). Hence, we ignore ECHO in the
232 tn->him_preferred[CURL_TELOPT_ECHO] = CURL_YES;
234 /* Set the subnegotiation fields to send information
235 just after negotiation passed (do/will)
237 Default values are (0,0) initialized by calloc.
238 According to the RFC1013 it is valid:
239 A value equal to zero is acceptable for the width (or height),
240 and means that no character width (or height) is being sent.
241 In this case, the width (or height) that will be assumed by the
242 Telnet server is operating system specific (it will probably be
243 based upon the terminal type information that may have been sent
244 using the TERMINAL TYPE Telnet option). */
245 tn->subnegotiation[CURL_TELOPT_NAWS] = CURL_YES;
249 static void negotiate(struct Curl_easy *data)
252 struct TELNET *tn = data->req.p.telnet;
254 for(i = 0; i < CURL_NTELOPTS; i++) {
255 if(i == CURL_TELOPT_ECHO)
258 if(tn->us_preferred[i] == CURL_YES)
259 set_local_option(data, i, CURL_YES);
261 if(tn->him_preferred[i] == CURL_YES)
262 set_remote_option(data, i, CURL_YES);
266 #ifndef CURL_DISABLE_VERBOSE_STRINGS
267 static void printoption(struct Curl_easy *data,
268 const char *direction, int cmd, int option)
270 if(data->set.verbose) {
271 if(cmd == CURL_IAC) {
272 if(CURL_TELCMD_OK(option))
273 infof(data, "%s IAC %s", direction, CURL_TELCMD(option));
275 infof(data, "%s IAC %d", direction, option);
278 const char *fmt = (cmd == CURL_WILL) ? "WILL" :
279 (cmd == CURL_WONT) ? "WONT" :
280 (cmd == CURL_DO) ? "DO" :
281 (cmd == CURL_DONT) ? "DONT" : 0;
284 if(CURL_TELOPT_OK(option))
285 opt = CURL_TELOPT(option);
286 else if(option == CURL_TELOPT_EXOPL)
292 infof(data, "%s %s %s", direction, fmt, opt);
294 infof(data, "%s %s %d", direction, fmt, option);
297 infof(data, "%s %d %d", direction, cmd, option);
303 static void send_negotiation(struct Curl_easy *data, int cmd, int option)
305 unsigned char buf[3];
306 ssize_t bytes_written;
307 struct connectdata *conn = data->conn;
310 buf[1] = (unsigned char)cmd;
311 buf[2] = (unsigned char)option;
313 bytes_written = swrite(conn->sock[FIRSTSOCKET], buf, 3);
314 if(bytes_written < 0) {
316 failf(data,"Sending data failed (%d)",err);
319 printoption(data, "SENT", cmd, option);
323 void set_remote_option(struct Curl_easy *data, int option, int newstate)
325 struct TELNET *tn = data->req.p.telnet;
326 if(newstate == CURL_YES) {
327 switch(tn->him[option]) {
329 tn->him[option] = CURL_WANTYES;
330 send_negotiation(data, CURL_DO, option);
334 /* Already enabled */
338 switch(tn->himq[option]) {
340 /* Already negotiating for CURL_YES, queue the request */
341 tn->himq[option] = CURL_OPPOSITE;
344 /* Error: already queued an enable request */
350 switch(tn->himq[option]) {
352 /* Error: already negotiating for enable */
355 tn->himq[option] = CURL_EMPTY;
362 switch(tn->him[option]) {
364 /* Already disabled */
368 tn->him[option] = CURL_WANTNO;
369 send_negotiation(data, CURL_DONT, option);
373 switch(tn->himq[option]) {
375 /* Already negotiating for NO */
378 tn->himq[option] = CURL_EMPTY;
384 switch(tn->himq[option]) {
386 tn->himq[option] = CURL_OPPOSITE;
397 void rec_will(struct Curl_easy *data, int option)
399 struct TELNET *tn = data->req.p.telnet;
400 switch(tn->him[option]) {
402 if(tn->him_preferred[option] == CURL_YES) {
403 tn->him[option] = CURL_YES;
404 send_negotiation(data, CURL_DO, option);
407 send_negotiation(data, CURL_DONT, option);
412 /* Already enabled */
416 switch(tn->himq[option]) {
418 /* Error: DONT answered by WILL */
419 tn->him[option] = CURL_NO;
422 /* Error: DONT answered by WILL */
423 tn->him[option] = CURL_YES;
424 tn->himq[option] = CURL_EMPTY;
430 switch(tn->himq[option]) {
432 tn->him[option] = CURL_YES;
435 tn->him[option] = CURL_WANTNO;
436 tn->himq[option] = CURL_EMPTY;
437 send_negotiation(data, CURL_DONT, option);
445 void rec_wont(struct Curl_easy *data, int option)
447 struct TELNET *tn = data->req.p.telnet;
448 switch(tn->him[option]) {
450 /* Already disabled */
454 tn->him[option] = CURL_NO;
455 send_negotiation(data, CURL_DONT, option);
459 switch(tn->himq[option]) {
461 tn->him[option] = CURL_NO;
465 tn->him[option] = CURL_WANTYES;
466 tn->himq[option] = CURL_EMPTY;
467 send_negotiation(data, CURL_DO, option);
473 switch(tn->himq[option]) {
475 tn->him[option] = CURL_NO;
478 tn->him[option] = CURL_NO;
479 tn->himq[option] = CURL_EMPTY;
487 set_local_option(struct Curl_easy *data, int option, int newstate)
489 struct TELNET *tn = data->req.p.telnet;
490 if(newstate == CURL_YES) {
491 switch(tn->us[option]) {
493 tn->us[option] = CURL_WANTYES;
494 send_negotiation(data, CURL_WILL, option);
498 /* Already enabled */
502 switch(tn->usq[option]) {
504 /* Already negotiating for CURL_YES, queue the request */
505 tn->usq[option] = CURL_OPPOSITE;
508 /* Error: already queued an enable request */
514 switch(tn->usq[option]) {
516 /* Error: already negotiating for enable */
519 tn->usq[option] = CURL_EMPTY;
526 switch(tn->us[option]) {
528 /* Already disabled */
532 tn->us[option] = CURL_WANTNO;
533 send_negotiation(data, CURL_WONT, option);
537 switch(tn->usq[option]) {
539 /* Already negotiating for NO */
542 tn->usq[option] = CURL_EMPTY;
548 switch(tn->usq[option]) {
550 tn->usq[option] = CURL_OPPOSITE;
561 void rec_do(struct Curl_easy *data, int option)
563 struct TELNET *tn = data->req.p.telnet;
564 switch(tn->us[option]) {
566 if(tn->us_preferred[option] == CURL_YES) {
567 tn->us[option] = CURL_YES;
568 send_negotiation(data, CURL_WILL, option);
569 if(tn->subnegotiation[option] == CURL_YES)
570 /* transmission of data option */
571 sendsuboption(data, option);
573 else if(tn->subnegotiation[option] == CURL_YES) {
574 /* send information to achieve this option*/
575 tn->us[option] = CURL_YES;
576 send_negotiation(data, CURL_WILL, option);
577 sendsuboption(data, option);
580 send_negotiation(data, CURL_WONT, option);
584 /* Already enabled */
588 switch(tn->usq[option]) {
590 /* Error: DONT answered by WILL */
591 tn->us[option] = CURL_NO;
594 /* Error: DONT answered by WILL */
595 tn->us[option] = CURL_YES;
596 tn->usq[option] = CURL_EMPTY;
602 switch(tn->usq[option]) {
604 tn->us[option] = CURL_YES;
605 if(tn->subnegotiation[option] == CURL_YES) {
606 /* transmission of data option */
607 sendsuboption(data, option);
611 tn->us[option] = CURL_WANTNO;
612 tn->himq[option] = CURL_EMPTY;
613 send_negotiation(data, CURL_WONT, option);
621 void rec_dont(struct Curl_easy *data, int option)
623 struct TELNET *tn = data->req.p.telnet;
624 switch(tn->us[option]) {
626 /* Already disabled */
630 tn->us[option] = CURL_NO;
631 send_negotiation(data, CURL_WONT, option);
635 switch(tn->usq[option]) {
637 tn->us[option] = CURL_NO;
641 tn->us[option] = CURL_WANTYES;
642 tn->usq[option] = CURL_EMPTY;
643 send_negotiation(data, CURL_WILL, option);
649 switch(tn->usq[option]) {
651 tn->us[option] = CURL_NO;
654 tn->us[option] = CURL_NO;
655 tn->usq[option] = CURL_EMPTY;
663 static void printsub(struct Curl_easy *data,
664 int direction, /* '<' or '>' */
665 unsigned char *pointer, /* where suboption data is */
666 size_t length) /* length of suboption data */
668 if(data->set.verbose) {
671 infof(data, "%s IAC SB ", (direction == '<')? "RCVD":"SENT");
675 i = pointer[length-2];
676 j = pointer[length-1];
678 if(i != CURL_IAC || j != CURL_SE) {
679 infof(data, "(terminated by ");
680 if(CURL_TELOPT_OK(i))
681 infof(data, "%s ", CURL_TELOPT(i));
682 else if(CURL_TELCMD_OK(i))
683 infof(data, "%s ", CURL_TELCMD(i));
685 infof(data, "%u ", i);
686 if(CURL_TELOPT_OK(j))
687 infof(data, "%s", CURL_TELOPT(j));
688 else if(CURL_TELCMD_OK(j))
689 infof(data, "%s", CURL_TELCMD(j));
691 infof(data, "%d", j);
692 infof(data, ", not IAC SE) ");
698 infof(data, "(Empty suboption?)");
702 if(CURL_TELOPT_OK(pointer[0])) {
704 case CURL_TELOPT_TTYPE:
705 case CURL_TELOPT_XDISPLOC:
706 case CURL_TELOPT_NEW_ENVIRON:
707 case CURL_TELOPT_NAWS:
708 infof(data, "%s", CURL_TELOPT(pointer[0]));
711 infof(data, "%s (unsupported)", CURL_TELOPT(pointer[0]));
716 infof(data, "%d (unknown)", pointer[i]);
719 case CURL_TELOPT_NAWS:
721 infof(data, "Width: %d ; Height: %d", (pointer[1]<<8) | pointer[2],
722 (pointer[3]<<8) | pointer[4]);
726 case CURL_TELQUAL_IS:
729 case CURL_TELQUAL_SEND:
730 infof(data, " SEND");
732 case CURL_TELQUAL_INFO:
733 infof(data, " INFO/REPLY");
735 case CURL_TELQUAL_NAME:
736 infof(data, " NAME");
741 case CURL_TELOPT_TTYPE:
742 case CURL_TELOPT_XDISPLOC:
744 infof(data, " \"%s\"", &pointer[2]);
746 case CURL_TELOPT_NEW_ENVIRON:
747 if(pointer[1] == CURL_TELQUAL_IS) {
749 for(i = 3; i < length; i++) {
751 case CURL_NEW_ENV_VAR:
754 case CURL_NEW_ENV_VALUE:
758 infof(data, "%c", pointer[i]);
765 for(i = 2; i < length; i++)
766 infof(data, " %.2x", pointer[i]);
773 static CURLcode check_telnet_options(struct Curl_easy *data)
775 struct curl_slist *head;
776 struct curl_slist *beg;
777 char option_keyword[128] = "";
778 char option_arg[256] = "";
779 struct TELNET *tn = data->req.p.telnet;
780 struct connectdata *conn = data->conn;
781 CURLcode result = CURLE_OK;
784 /* Add the user name as an environment variable if it
785 was given on the command line */
786 if(data->state.aptr.user) {
787 msnprintf(option_arg, sizeof(option_arg), "USER,%s", conn->user);
788 beg = curl_slist_append(tn->telnet_vars, option_arg);
790 curl_slist_free_all(tn->telnet_vars);
791 tn->telnet_vars = NULL;
792 return CURLE_OUT_OF_MEMORY;
794 tn->telnet_vars = beg;
795 tn->us_preferred[CURL_TELOPT_NEW_ENVIRON] = CURL_YES;
798 for(head = data->set.telnet_options; head; head = head->next) {
799 if(sscanf(head->data, "%127[^= ]%*[ =]%255s",
800 option_keyword, option_arg) == 2) {
803 if(strcasecompare(option_keyword, "TTYPE")) {
804 strncpy(tn->subopt_ttype, option_arg, 31);
805 tn->subopt_ttype[31] = 0; /* String termination */
806 tn->us_preferred[CURL_TELOPT_TTYPE] = CURL_YES;
810 /* Display variable */
811 if(strcasecompare(option_keyword, "XDISPLOC")) {
812 strncpy(tn->subopt_xdisploc, option_arg, 127);
813 tn->subopt_xdisploc[127] = 0; /* String termination */
814 tn->us_preferred[CURL_TELOPT_XDISPLOC] = CURL_YES;
818 /* Environment variable */
819 if(strcasecompare(option_keyword, "NEW_ENV")) {
820 beg = curl_slist_append(tn->telnet_vars, option_arg);
822 result = CURLE_OUT_OF_MEMORY;
825 tn->telnet_vars = beg;
826 tn->us_preferred[CURL_TELOPT_NEW_ENVIRON] = CURL_YES;
831 if(strcasecompare(option_keyword, "WS")) {
832 if(sscanf(option_arg, "%hu%*[xX]%hu",
833 &tn->subopt_wsx, &tn->subopt_wsy) == 2)
834 tn->us_preferred[CURL_TELOPT_NAWS] = CURL_YES;
836 failf(data, "Syntax error in telnet option: %s", head->data);
837 result = CURLE_SETOPT_OPTION_SYNTAX;
843 /* To take care or not of the 8th bit in data exchange */
844 if(strcasecompare(option_keyword, "BINARY")) {
845 binary_option = atoi(option_arg);
846 if(binary_option != 1) {
847 tn->us_preferred[CURL_TELOPT_BINARY] = CURL_NO;
848 tn->him_preferred[CURL_TELOPT_BINARY] = CURL_NO;
853 failf(data, "Unknown telnet option %s", head->data);
854 result = CURLE_UNKNOWN_OPTION;
857 failf(data, "Syntax error in telnet option: %s", head->data);
858 result = CURLE_SETOPT_OPTION_SYNTAX;
863 curl_slist_free_all(tn->telnet_vars);
864 tn->telnet_vars = NULL;
873 * Look at the sub-option buffer, and try to be helpful to the other
877 static void suboption(struct Curl_easy *data)
879 struct curl_slist *v;
880 unsigned char temp[2048];
881 ssize_t bytes_written;
884 char varname[128] = "";
885 char varval[128] = "";
886 struct TELNET *tn = data->req.p.telnet;
887 struct connectdata *conn = data->conn;
889 printsub(data, '<', (unsigned char *)tn->subbuffer, CURL_SB_LEN(tn) + 2);
890 switch(CURL_SB_GET(tn)) {
891 case CURL_TELOPT_TTYPE:
892 len = strlen(tn->subopt_ttype) + 4 + 2;
893 msnprintf((char *)temp, sizeof(temp),
894 "%c%c%c%c%s%c%c", CURL_IAC, CURL_SB, CURL_TELOPT_TTYPE,
895 CURL_TELQUAL_IS, tn->subopt_ttype, CURL_IAC, CURL_SE);
896 bytes_written = swrite(conn->sock[FIRSTSOCKET], temp, len);
897 if(bytes_written < 0) {
899 failf(data,"Sending data failed (%d)",err);
901 printsub(data, '>', &temp[2], len-2);
903 case CURL_TELOPT_XDISPLOC:
904 len = strlen(tn->subopt_xdisploc) + 4 + 2;
905 msnprintf((char *)temp, sizeof(temp),
906 "%c%c%c%c%s%c%c", CURL_IAC, CURL_SB, CURL_TELOPT_XDISPLOC,
907 CURL_TELQUAL_IS, tn->subopt_xdisploc, CURL_IAC, CURL_SE);
908 bytes_written = swrite(conn->sock[FIRSTSOCKET], temp, len);
909 if(bytes_written < 0) {
911 failf(data,"Sending data failed (%d)",err);
913 printsub(data, '>', &temp[2], len-2);
915 case CURL_TELOPT_NEW_ENVIRON:
916 msnprintf((char *)temp, sizeof(temp),
917 "%c%c%c%c", CURL_IAC, CURL_SB, CURL_TELOPT_NEW_ENVIRON,
921 for(v = tn->telnet_vars; v; v = v->next) {
922 size_t tmplen = (strlen(v->data) + 1);
923 /* Add the variable only if it fits */
924 if(len + tmplen < (int)sizeof(temp)-6) {
928 rv = sscanf(v->data, "%127[^,]%1[,]%127s", varname, sep, varval);
930 len += msnprintf((char *)&temp[len], sizeof(temp) - len,
931 "%c%s", CURL_NEW_ENV_VAR, varname);
933 len += msnprintf((char *)&temp[len], sizeof(temp) - len,
934 "%c%s%c%s", CURL_NEW_ENV_VAR, varname,
935 CURL_NEW_ENV_VALUE, varval);
938 msnprintf((char *)&temp[len], sizeof(temp) - len,
939 "%c%c", CURL_IAC, CURL_SE);
941 bytes_written = swrite(conn->sock[FIRSTSOCKET], temp, len);
942 if(bytes_written < 0) {
944 failf(data,"Sending data failed (%d)",err);
946 printsub(data, '>', &temp[2], len-2);
956 * Send suboption information to the server side.
959 static void sendsuboption(struct Curl_easy *data, int option)
961 ssize_t bytes_written;
964 unsigned char *uc1, *uc2;
965 struct TELNET *tn = data->req.p.telnet;
966 struct connectdata *conn = data->conn;
969 case CURL_TELOPT_NAWS:
970 /* We prepare data to be sent */
972 CURL_SB_ACCUM(tn, CURL_IAC);
973 CURL_SB_ACCUM(tn, CURL_SB);
974 CURL_SB_ACCUM(tn, CURL_TELOPT_NAWS);
975 /* We must deal either with little or big endian processors */
976 /* Window size must be sent according to the 'network order' */
977 x = htons(tn->subopt_wsx);
978 y = htons(tn->subopt_wsy);
979 uc1 = (unsigned char *)&x;
980 uc2 = (unsigned char *)&y;
981 CURL_SB_ACCUM(tn, uc1[0]);
982 CURL_SB_ACCUM(tn, uc1[1]);
983 CURL_SB_ACCUM(tn, uc2[0]);
984 CURL_SB_ACCUM(tn, uc2[1]);
986 CURL_SB_ACCUM(tn, CURL_IAC);
987 CURL_SB_ACCUM(tn, CURL_SE);
989 /* data suboption is now ready */
991 printsub(data, '>', (unsigned char *)tn->subbuffer + 2,
994 /* we send the header of the suboption... */
995 bytes_written = swrite(conn->sock[FIRSTSOCKET], tn->subbuffer, 3);
996 if(bytes_written < 0) {
998 failf(data, "Sending data failed (%d)", err);
1000 /* ... then the window size with the send_telnet_data() function
1001 to deal with 0xFF cases ... */
1002 send_telnet_data(data, (char *)tn->subbuffer + 3, 4);
1003 /* ... and the footer */
1004 bytes_written = swrite(conn->sock[FIRSTSOCKET], tn->subbuffer + 7, 2);
1005 if(bytes_written < 0) {
1007 failf(data, "Sending data failed (%d)", err);
1015 CURLcode telrcv(struct Curl_easy *data,
1016 const unsigned char *inbuf, /* Data received from socket */
1017 ssize_t count) /* Number of bytes received */
1022 int startwrite = -1;
1023 struct TELNET *tn = data->req.p.telnet;
1025 #define startskipping() \
1026 if(startwrite >= 0) { \
1027 result = Curl_client_write(data, \
1029 (char *)&inbuf[startwrite], \
1036 #define writebyte() \
1037 if(startwrite < 0) \
1040 #define bufferflush() startskipping()
1045 switch(tn->telrcv_state) {
1047 tn->telrcv_state = CURL_TS_DATA;
1050 break; /* Ignore \0 after CR */
1057 tn->telrcv_state = CURL_TS_IAC;
1062 tn->telrcv_state = CURL_TS_CR;
1068 DEBUGASSERT(startwrite < 0);
1071 tn->telrcv_state = CURL_TS_WILL;
1074 tn->telrcv_state = CURL_TS_WONT;
1077 tn->telrcv_state = CURL_TS_DO;
1080 tn->telrcv_state = CURL_TS_DONT;
1084 tn->telrcv_state = CURL_TS_SB;
1087 tn->telrcv_state = CURL_TS_DATA;
1094 tn->telrcv_state = CURL_TS_DATA;
1095 printoption(data, "RCVD", CURL_IAC, c);
1101 printoption(data, "RCVD", CURL_WILL, c);
1102 tn->please_negotiate = 1;
1104 tn->telrcv_state = CURL_TS_DATA;
1108 printoption(data, "RCVD", CURL_WONT, c);
1109 tn->please_negotiate = 1;
1111 tn->telrcv_state = CURL_TS_DATA;
1115 printoption(data, "RCVD", CURL_DO, c);
1116 tn->please_negotiate = 1;
1118 tn->telrcv_state = CURL_TS_DATA;
1122 printoption(data, "RCVD", CURL_DONT, c);
1123 tn->please_negotiate = 1;
1125 tn->telrcv_state = CURL_TS_DATA;
1130 tn->telrcv_state = CURL_TS_SE;
1132 CURL_SB_ACCUM(tn, c);
1139 * This is an error. We only expect to get "IAC IAC" or "IAC SE".
1140 * Several things may have happened. An IAC was not doubled, the
1141 * IAC SE was left off, or another option got inserted into the
1142 * suboption are all possibilities. If we assume that the IAC was
1143 * not doubled, and really the IAC SE was left off, we could get
1144 * into an infinite loop here. So, instead, we terminate the
1145 * suboption, and process the partial suboption if we can.
1147 CURL_SB_ACCUM(tn, CURL_IAC);
1148 CURL_SB_ACCUM(tn, c);
1149 tn->subpointer -= 2;
1152 printoption(data, "In SUBOPTION processing, RCVD", CURL_IAC, c);
1153 suboption(data); /* handle sub-option */
1154 tn->telrcv_state = CURL_TS_IAC;
1157 CURL_SB_ACCUM(tn, c);
1158 tn->telrcv_state = CURL_TS_SB;
1161 CURL_SB_ACCUM(tn, CURL_IAC);
1162 CURL_SB_ACCUM(tn, CURL_SE);
1163 tn->subpointer -= 2;
1165 suboption(data); /* handle sub-option */
1166 tn->telrcv_state = CURL_TS_DATA;
1176 /* Escape and send a telnet data block */
1177 static CURLcode send_telnet_data(struct Curl_easy *data,
1178 char *buffer, ssize_t nread)
1180 ssize_t escapes, i, outlen;
1181 unsigned char *outbuf = NULL;
1182 CURLcode result = CURLE_OK;
1183 ssize_t bytes_written, total_written;
1184 struct connectdata *conn = data->conn;
1186 /* Determine size of new buffer after escaping */
1188 for(i = 0; i < nread; i++)
1189 if((unsigned char)buffer[i] == CURL_IAC)
1191 outlen = nread + escapes;
1194 outbuf = (unsigned char *)buffer;
1197 outbuf = malloc(nread + escapes + 1);
1199 return CURLE_OUT_OF_MEMORY;
1202 for(i = 0; i < nread; i++) {
1203 outbuf[j++] = buffer[i];
1204 if((unsigned char)buffer[i] == CURL_IAC)
1205 outbuf[j++] = CURL_IAC;
1211 while(!result && total_written < outlen) {
1212 /* Make sure socket is writable to avoid EWOULDBLOCK condition */
1213 struct pollfd pfd[1];
1214 pfd[0].fd = conn->sock[FIRSTSOCKET];
1215 pfd[0].events = POLLOUT;
1216 switch(Curl_poll(pfd, 1, -1)) {
1217 case -1: /* error, abort writing */
1218 case 0: /* timeout (will never happen) */
1219 result = CURLE_SEND_ERROR;
1221 default: /* write! */
1223 result = Curl_write(data, conn->sock[FIRSTSOCKET],
1224 outbuf + total_written,
1225 outlen - total_written,
1227 total_written += bytes_written;
1232 /* Free malloc copy if escaped */
1233 if(outbuf != (unsigned char *)buffer)
1239 static CURLcode telnet_done(struct Curl_easy *data,
1240 CURLcode status, bool premature)
1242 struct TELNET *tn = data->req.p.telnet;
1243 (void)status; /* unused */
1244 (void)premature; /* not used */
1249 curl_slist_free_all(tn->telnet_vars);
1250 tn->telnet_vars = NULL;
1252 Curl_safefree(data->req.p.telnet);
1257 static CURLcode telnet_do(struct Curl_easy *data, bool *done)
1260 struct connectdata *conn = data->conn;
1261 curl_socket_t sockfd = conn->sock[FIRSTSOCKET];
1263 WSAEVENT event_handle;
1264 WSANETWORKEVENTS events;
1265 HANDLE stdin_handle;
1269 DWORD readfile_read;
1272 timediff_t interval_ms;
1273 struct pollfd pfd[2];
1275 curl_off_t total_dl = 0;
1276 curl_off_t total_ul = 0;
1279 struct curltime now;
1281 char *buf = data->state.buffer;
1284 *done = TRUE; /* unconditionally */
1286 result = init_telnet(data);
1290 tn = data->req.p.telnet;
1292 result = check_telnet_options(data);
1297 /* We want to wait for both stdin and the socket. Since
1298 ** the select() function in winsock only works on sockets
1299 ** we have to use the WaitForMultipleObjects() call.
1302 /* First, create a sockets event object */
1303 event_handle = WSACreateEvent();
1304 if(event_handle == WSA_INVALID_EVENT) {
1305 failf(data, "WSACreateEvent failed (%d)", SOCKERRNO);
1306 return CURLE_FAILED_INIT;
1309 /* Tell winsock what events we want to listen to */
1310 if(WSAEventSelect(sockfd, event_handle, FD_READ|FD_CLOSE) == SOCKET_ERROR) {
1311 WSACloseEvent(event_handle);
1315 /* The get the Windows file handle for stdin */
1316 stdin_handle = GetStdHandle(STD_INPUT_HANDLE);
1318 /* Create the list of objects to wait for */
1319 objs[0] = event_handle;
1320 objs[1] = stdin_handle;
1322 /* If stdin_handle is a pipe, use PeekNamedPipe() method to check it,
1323 else use the old WaitForMultipleObjects() way */
1324 if(GetFileType(stdin_handle) == FILE_TYPE_PIPE ||
1325 data->set.is_fread_set) {
1326 /* Don't wait for stdin_handle, just wait for event_handle */
1328 /* Check stdin_handle per 100 milliseconds */
1333 wait_timeout = 1000;
1336 /* Keep on listening and act on events */
1338 const DWORD buf_size = (DWORD)data->set.buffer_size;
1339 DWORD waitret = WaitForMultipleObjects(obj_count, objs,
1340 FALSE, wait_timeout);
1346 if(data->set.is_fread_set) {
1348 /* read from user-supplied method */
1349 n = data->state.fread_func(buf, 1, buf_size, data->state.in);
1350 if(n == CURL_READFUNC_ABORT) {
1352 result = CURLE_READ_ERROR;
1356 if(n == CURL_READFUNC_PAUSE)
1359 if(n == 0) /* no bytes */
1362 /* fall through with number of bytes read */
1363 readfile_read = (DWORD)n;
1366 /* read from stdin */
1367 if(!PeekNamedPipe(stdin_handle, NULL, 0, NULL,
1368 &readfile_read, NULL)) {
1370 result = CURLE_READ_ERROR;
1377 if(!ReadFile(stdin_handle, buf, buf_size,
1378 &readfile_read, NULL)) {
1380 result = CURLE_READ_ERROR;
1385 result = send_telnet_data(data, buf, readfile_read);
1394 case WAIT_OBJECT_0 + 1:
1396 if(!ReadFile(stdin_handle, buf, buf_size,
1397 &readfile_read, NULL)) {
1399 result = CURLE_READ_ERROR;
1403 result = send_telnet_data(data, buf, readfile_read);
1413 events.lNetworkEvents = 0;
1414 if(WSAEnumNetworkEvents(sockfd, event_handle, &events) == SOCKET_ERROR) {
1416 if(err != EINPROGRESS) {
1417 infof(data, "WSAEnumNetworkEvents failed (%d)", err);
1419 result = CURLE_READ_ERROR;
1423 if(events.lNetworkEvents & FD_READ) {
1424 /* read data from network */
1425 result = Curl_read(data, sockfd, buf, data->set.buffer_size, &nread);
1426 /* read would've blocked. Loop again */
1427 if(result == CURLE_AGAIN)
1429 /* returned not-zero, this an error */
1434 /* returned zero but actually received 0 or less here,
1435 the server closed the connection and we bail out */
1436 else if(nread <= 0) {
1441 result = telrcv(data, (unsigned char *) buf, nread);
1447 /* Negotiate if the peer has started negotiating,
1448 otherwise don't. We don't want to speak telnet with
1449 non-telnet servers, like POP or SMTP. */
1450 if(tn->please_negotiate && !tn->already_negotiated) {
1452 tn->already_negotiated = 1;
1455 if(events.lNetworkEvents & FD_CLOSE) {
1463 if(data->set.timeout) {
1465 if(Curl_timediff(now, conn->created) >= data->set.timeout) {
1466 failf(data, "Time-out");
1467 result = CURLE_OPERATION_TIMEDOUT;
1473 /* We called WSACreateEvent, so call WSACloseEvent */
1474 if(!WSACloseEvent(event_handle)) {
1475 infof(data, "WSACloseEvent failed (%d)", SOCKERRNO);
1479 pfd[0].events = POLLIN;
1481 if(data->set.is_fread_set) {
1483 interval_ms = 100; /* poll user-supplied read function */
1486 /* really using fread, so infile is a FILE* */
1487 pfd[1].fd = fileno((FILE *)data->state.in);
1488 pfd[1].events = POLLIN;
1490 interval_ms = 1 * 1000;
1494 switch(Curl_poll(pfd, poll_cnt, interval_ms)) {
1495 case -1: /* error, stop reading */
1498 case 0: /* timeout */
1502 default: /* read! */
1503 if(pfd[0].revents & POLLIN) {
1504 /* read data from network */
1505 result = Curl_read(data, sockfd, buf, data->set.buffer_size, &nread);
1506 /* read would've blocked. Loop again */
1507 if(result == CURLE_AGAIN)
1509 /* returned not-zero, this an error */
1514 /* returned zero but actually received 0 or less here,
1515 the server closed the connection and we bail out */
1516 else if(nread <= 0) {
1522 Curl_pgrsSetDownloadCounter(data, total_dl);
1523 result = telrcv(data, (unsigned char *)buf, nread);
1529 /* Negotiate if the peer has started negotiating,
1530 otherwise don't. We don't want to speak telnet with
1531 non-telnet servers, like POP or SMTP. */
1532 if(tn->please_negotiate && !tn->already_negotiated) {
1534 tn->already_negotiated = 1;
1540 if(pfd[1].revents & POLLIN) { /* read from in file */
1541 nread = read(pfd[1].fd, buf, data->set.buffer_size);
1545 /* read from user-supplied method */
1546 nread = (int)data->state.fread_func(buf, 1, data->set.buffer_size,
1548 if(nread == CURL_READFUNC_ABORT) {
1552 if(nread == CURL_READFUNC_PAUSE)
1557 result = send_telnet_data(data, buf, nread);
1563 Curl_pgrsSetUploadCounter(data, total_ul);
1569 } /* poll switch statement */
1571 if(data->set.timeout) {
1573 if(Curl_timediff(now, conn->created) >= data->set.timeout) {
1574 failf(data, "Time-out");
1575 result = CURLE_OPERATION_TIMEDOUT;
1580 if(Curl_pgrsUpdate(data)) {
1581 result = CURLE_ABORTED_BY_CALLBACK;
1586 /* mark this as "no further transfer wanted" */
1587 Curl_setup_transfer(data, -1, -1, FALSE, -1);