• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /***************************************************************************
2  *                                  _   _ ____  _
3  *  Project                     ___| | | |  _ \| |
4  *                             / __| | | | |_) | |
5  *                            | (__| |_| |  _ <| |___
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
9  *
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.
13  *
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.
17  *
18  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19  * KIND, either express or implied.
20  *
21  * SPDX-License-Identifier: curl
22  *
23  ***************************************************************************/
24 
25 #include "curl_setup.h"
26 
27 #ifndef CURL_DISABLE_TELNET
28 
29 #ifdef HAVE_NETINET_IN_H
30 #include <netinet/in.h>
31 #endif
32 #ifdef HAVE_NETDB_H
33 #include <netdb.h>
34 #endif
35 #ifdef HAVE_ARPA_INET_H
36 #include <arpa/inet.h>
37 #endif
38 #ifdef HAVE_NET_IF_H
39 #include <net/if.h>
40 #endif
41 #ifdef HAVE_SYS_IOCTL_H
42 #include <sys/ioctl.h>
43 #endif
44 
45 #ifdef HAVE_SYS_PARAM_H
46 #include <sys/param.h>
47 #endif
48 
49 #include "urldata.h"
50 #include <curl/curl.h>
51 #include "transfer.h"
52 #include "sendf.h"
53 #include "telnet.h"
54 #include "connect.h"
55 #include "progress.h"
56 #include "system_win32.h"
57 #include "arpa_telnet.h"
58 #include "select.h"
59 #include "strcase.h"
60 #include "warnless.h"
61 
62 /* The last 3 #include files should be in this order */
63 #include "curl_printf.h"
64 #include "curl_memory.h"
65 #include "memdebug.h"
66 
67 #define SUBBUFSIZE 512
68 
69 #define CURL_SB_CLEAR(x)  x->subpointer = x->subbuffer
70 #define CURL_SB_TERM(x)                                 \
71   do {                                                  \
72     x->subend = x->subpointer;                          \
73     CURL_SB_CLEAR(x);                                   \
74   } while(0)
75 #define CURL_SB_ACCUM(x,c)                                      \
76   do {                                                          \
77     if(x->subpointer < (x->subbuffer + sizeof(x->subbuffer)))   \
78       *x->subpointer++ = (c);                                   \
79   } while(0)
80 
81 #define  CURL_SB_GET(x) ((*x->subpointer++)&0xff)
82 #define  CURL_SB_LEN(x) (x->subend - x->subpointer)
83 
84 /* For posterity:
85 #define  CURL_SB_PEEK(x) ((*x->subpointer)&0xff)
86 #define  CURL_SB_EOF(x) (x->subpointer >= x->subend) */
87 
88 #ifdef CURL_DISABLE_VERBOSE_STRINGS
89 #define printoption(a,b,c,d)  Curl_nop_stmt
90 #endif
91 
92 static
93 CURLcode telrcv(struct Curl_easy *data,
94                 const unsigned char *inbuf, /* Data received from socket */
95                 ssize_t count);             /* Number of bytes received */
96 
97 #ifndef CURL_DISABLE_VERBOSE_STRINGS
98 static void printoption(struct Curl_easy *data,
99                         const char *direction,
100                         int cmd, int option);
101 #endif
102 
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);
109 
110 static void printsub(struct Curl_easy *data,
111                      int direction, unsigned char *pointer,
112                      size_t length);
113 static void suboption(struct Curl_easy *data);
114 static void sendsuboption(struct Curl_easy *data, int option);
115 
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);
121 
122 /* For negotiation compliant to RFC 1143 */
123 #define CURL_NO          0
124 #define CURL_YES         1
125 #define CURL_WANTYES     2
126 #define CURL_WANTNO      3
127 
128 #define CURL_EMPTY       0
129 #define CURL_OPPOSITE    1
130 
131 /*
132  * Telnet receiver states for fsm
133  */
134 typedef enum
135 {
136    CURL_TS_DATA = 0,
137    CURL_TS_IAC,
138    CURL_TS_WILL,
139    CURL_TS_WONT,
140    CURL_TS_DO,
141    CURL_TS_DONT,
142    CURL_TS_CR,
143    CURL_TS_SB,   /* sub-option collection */
144    CURL_TS_SE   /* looking for sub-option end */
145 } TelnetReceive;
146 
147 struct TELNET {
148   int please_negotiate;
149   int already_negotiated;
150   int us[256];
151   int usq[256];
152   int us_preferred[256];
153   int him[256];
154   int himq[256];
155   int him_preferred[256];
156   int subnegotiation[256];
157   char *subopt_ttype;                /* Set with suboption TTYPE */
158   char *subopt_xdisploc;             /* 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 */
163   struct dynbuf out;                 /* output buffer */
164 
165   /* suboptions */
166   unsigned char subbuffer[SUBBUFSIZE];
167   unsigned char *subpointer, *subend;      /* buffer for sub-options */
168 };
169 
170 
171 /*
172  * TELNET protocol handler.
173  */
174 
175 const struct Curl_handler Curl_handler_telnet = {
176   "telnet",                             /* scheme */
177   ZERO_NULL,                            /* setup_connection */
178   telnet_do,                            /* do_it */
179   telnet_done,                          /* done */
180   ZERO_NULL,                            /* do_more */
181   ZERO_NULL,                            /* connect_it */
182   ZERO_NULL,                            /* connecting */
183   ZERO_NULL,                            /* doing */
184   ZERO_NULL,                            /* proto_getsock */
185   ZERO_NULL,                            /* doing_getsock */
186   ZERO_NULL,                            /* domore_getsock */
187   ZERO_NULL,                            /* perform_getsock */
188   ZERO_NULL,                            /* disconnect */
189   ZERO_NULL,                            /* write_resp */
190   ZERO_NULL,                            /* write_resp_hd */
191   ZERO_NULL,                            /* connection_check */
192   ZERO_NULL,                            /* attach connection */
193   ZERO_NULL,                            /* follow */
194   PORT_TELNET,                          /* defport */
195   CURLPROTO_TELNET,                     /* protocol */
196   CURLPROTO_TELNET,                     /* family */
197   PROTOPT_NONE | PROTOPT_NOURLQUERY     /* flags */
198 };
199 
200 
201 static
init_telnet(struct Curl_easy * data)202 CURLcode init_telnet(struct Curl_easy *data)
203 {
204   struct TELNET *tn;
205 
206   tn = calloc(1, sizeof(struct TELNET));
207   if(!tn)
208     return CURLE_OUT_OF_MEMORY;
209 
210   Curl_dyn_init(&tn->out, 0xffff);
211   data->req.p.telnet = tn; /* make us known */
212 
213   tn->telrcv_state = CURL_TS_DATA;
214 
215   /* Init suboptions */
216   CURL_SB_CLEAR(tn);
217 
218   /* Set the options we want by default */
219   tn->us_preferred[CURL_TELOPT_SGA] = CURL_YES;
220   tn->him_preferred[CURL_TELOPT_SGA] = CURL_YES;
221 
222   /* To be compliant with previous releases of libcurl
223      we enable this option by default. This behavior
224          can be changed thanks to the "BINARY" option in
225          CURLOPT_TELNETOPTIONS
226   */
227   tn->us_preferred[CURL_TELOPT_BINARY] = CURL_YES;
228   tn->him_preferred[CURL_TELOPT_BINARY] = CURL_YES;
229 
230   /* We must allow the server to echo what we sent
231          but it is not necessary to request the server
232          to do so (it might forces the server to close
233          the connection). Hence, we ignore ECHO in the
234          negotiate function
235   */
236   tn->him_preferred[CURL_TELOPT_ECHO] = CURL_YES;
237 
238   /* Set the subnegotiation fields to send information
239     just after negotiation passed (do/will)
240 
241      Default values are (0,0) initialized by calloc.
242      According to the RFC1013 it is valid:
243      A value equal to zero is acceptable for the width (or height),
244          and means that no character width (or height) is being sent.
245          In this case, the width (or height) that will be assumed by the
246          Telnet server is operating system specific (it will probably be
247          based upon the terminal type information that may have been sent
248          using the TERMINAL TYPE Telnet option). */
249   tn->subnegotiation[CURL_TELOPT_NAWS] = CURL_YES;
250   return CURLE_OK;
251 }
252 
negotiate(struct Curl_easy * data)253 static void negotiate(struct Curl_easy *data)
254 {
255   int i;
256   struct TELNET *tn = data->req.p.telnet;
257 
258   for(i = 0; i < CURL_NTELOPTS; i++) {
259     if(i == CURL_TELOPT_ECHO)
260       continue;
261 
262     if(tn->us_preferred[i] == CURL_YES)
263       set_local_option(data, i, CURL_YES);
264 
265     if(tn->him_preferred[i] == CURL_YES)
266       set_remote_option(data, i, CURL_YES);
267   }
268 }
269 
270 #ifndef CURL_DISABLE_VERBOSE_STRINGS
printoption(struct Curl_easy * data,const char * direction,int cmd,int option)271 static void printoption(struct Curl_easy *data,
272                         const char *direction, int cmd, int option)
273 {
274   if(data->set.verbose) {
275     if(cmd == CURL_IAC) {
276       if(CURL_TELCMD_OK(option))
277         infof(data, "%s IAC %s", direction, CURL_TELCMD(option));
278       else
279         infof(data, "%s IAC %d", direction, option);
280     }
281     else {
282       const char *fmt = (cmd == CURL_WILL) ? "WILL" :
283                         (cmd == CURL_WONT) ? "WONT" :
284                         (cmd == CURL_DO) ? "DO" :
285                         (cmd == CURL_DONT) ? "DONT" : 0;
286       if(fmt) {
287         const char *opt;
288         if(CURL_TELOPT_OK(option))
289           opt = CURL_TELOPT(option);
290         else if(option == CURL_TELOPT_EXOPL)
291           opt = "EXOPL";
292         else
293           opt = NULL;
294 
295         if(opt)
296           infof(data, "%s %s %s", direction, fmt, opt);
297         else
298           infof(data, "%s %s %d", direction, fmt, option);
299       }
300       else
301         infof(data, "%s %d %d", direction, cmd, option);
302     }
303   }
304 }
305 #endif
306 
send_negotiation(struct Curl_easy * data,int cmd,int option)307 static void send_negotiation(struct Curl_easy *data, int cmd, int option)
308 {
309   unsigned char buf[3];
310   ssize_t bytes_written;
311   struct connectdata *conn = data->conn;
312 
313   buf[0] = CURL_IAC;
314   buf[1] = (unsigned char)cmd;
315   buf[2] = (unsigned char)option;
316 
317   bytes_written = swrite(conn->sock[FIRSTSOCKET], buf, 3);
318   if(bytes_written < 0) {
319     int err = SOCKERRNO;
320     failf(data,"Sending data failed (%d)",err);
321   }
322 
323   printoption(data, "SENT", cmd, option);
324 }
325 
326 static
set_remote_option(struct Curl_easy * data,int option,int newstate)327 void set_remote_option(struct Curl_easy *data, int option, int newstate)
328 {
329   struct TELNET *tn = data->req.p.telnet;
330   if(newstate == CURL_YES) {
331     switch(tn->him[option]) {
332     case CURL_NO:
333       tn->him[option] = CURL_WANTYES;
334       send_negotiation(data, CURL_DO, option);
335       break;
336 
337     case CURL_YES:
338       /* Already enabled */
339       break;
340 
341     case CURL_WANTNO:
342       switch(tn->himq[option]) {
343       case CURL_EMPTY:
344         /* Already negotiating for CURL_YES, queue the request */
345         tn->himq[option] = CURL_OPPOSITE;
346         break;
347       case CURL_OPPOSITE:
348         /* Error: already queued an enable request */
349         break;
350       }
351       break;
352 
353     case CURL_WANTYES:
354       switch(tn->himq[option]) {
355       case CURL_EMPTY:
356         /* Error: already negotiating for enable */
357         break;
358       case CURL_OPPOSITE:
359         tn->himq[option] = CURL_EMPTY;
360         break;
361       }
362       break;
363     }
364   }
365   else { /* NO */
366     switch(tn->him[option]) {
367     case CURL_NO:
368       /* Already disabled */
369       break;
370 
371     case CURL_YES:
372       tn->him[option] = CURL_WANTNO;
373       send_negotiation(data, CURL_DONT, option);
374       break;
375 
376     case CURL_WANTNO:
377       switch(tn->himq[option]) {
378       case CURL_EMPTY:
379         /* Already negotiating for NO */
380         break;
381       case CURL_OPPOSITE:
382         tn->himq[option] = CURL_EMPTY;
383         break;
384       }
385       break;
386 
387     case CURL_WANTYES:
388       switch(tn->himq[option]) {
389       case CURL_EMPTY:
390         tn->himq[option] = CURL_OPPOSITE;
391         break;
392       case CURL_OPPOSITE:
393         break;
394       }
395       break;
396     }
397   }
398 }
399 
400 static
rec_will(struct Curl_easy * data,int option)401 void rec_will(struct Curl_easy *data, int option)
402 {
403   struct TELNET *tn = data->req.p.telnet;
404   switch(tn->him[option]) {
405   case CURL_NO:
406     if(tn->him_preferred[option] == CURL_YES) {
407       tn->him[option] = CURL_YES;
408       send_negotiation(data, CURL_DO, option);
409     }
410     else
411       send_negotiation(data, CURL_DONT, option);
412 
413     break;
414 
415   case CURL_YES:
416     /* Already enabled */
417     break;
418 
419   case CURL_WANTNO:
420     switch(tn->himq[option]) {
421     case CURL_EMPTY:
422       /* Error: DONT answered by WILL */
423       tn->him[option] = CURL_NO;
424       break;
425     case CURL_OPPOSITE:
426       /* Error: DONT answered by WILL */
427       tn->him[option] = CURL_YES;
428       tn->himq[option] = CURL_EMPTY;
429       break;
430     }
431     break;
432 
433   case CURL_WANTYES:
434     switch(tn->himq[option]) {
435     case CURL_EMPTY:
436       tn->him[option] = CURL_YES;
437       break;
438     case CURL_OPPOSITE:
439       tn->him[option] = CURL_WANTNO;
440       tn->himq[option] = CURL_EMPTY;
441       send_negotiation(data, CURL_DONT, option);
442       break;
443     }
444     break;
445   }
446 }
447 
448 static
rec_wont(struct Curl_easy * data,int option)449 void rec_wont(struct Curl_easy *data, int option)
450 {
451   struct TELNET *tn = data->req.p.telnet;
452   switch(tn->him[option]) {
453   case CURL_NO:
454     /* Already disabled */
455     break;
456 
457   case CURL_YES:
458     tn->him[option] = CURL_NO;
459     send_negotiation(data, CURL_DONT, option);
460     break;
461 
462   case CURL_WANTNO:
463     switch(tn->himq[option]) {
464     case CURL_EMPTY:
465       tn->him[option] = CURL_NO;
466       break;
467 
468     case CURL_OPPOSITE:
469       tn->him[option] = CURL_WANTYES;
470       tn->himq[option] = CURL_EMPTY;
471       send_negotiation(data, CURL_DO, option);
472       break;
473     }
474     break;
475 
476   case CURL_WANTYES:
477     switch(tn->himq[option]) {
478     case CURL_EMPTY:
479       tn->him[option] = CURL_NO;
480       break;
481     case CURL_OPPOSITE:
482       tn->him[option] = CURL_NO;
483       tn->himq[option] = CURL_EMPTY;
484       break;
485     }
486     break;
487   }
488 }
489 
490 static void
set_local_option(struct Curl_easy * data,int option,int newstate)491 set_local_option(struct Curl_easy *data, int option, int newstate)
492 {
493   struct TELNET *tn = data->req.p.telnet;
494   if(newstate == CURL_YES) {
495     switch(tn->us[option]) {
496     case CURL_NO:
497       tn->us[option] = CURL_WANTYES;
498       send_negotiation(data, CURL_WILL, option);
499       break;
500 
501     case CURL_YES:
502       /* Already enabled */
503       break;
504 
505     case CURL_WANTNO:
506       switch(tn->usq[option]) {
507       case CURL_EMPTY:
508         /* Already negotiating for CURL_YES, queue the request */
509         tn->usq[option] = CURL_OPPOSITE;
510         break;
511       case CURL_OPPOSITE:
512         /* Error: already queued an enable request */
513         break;
514       }
515       break;
516 
517     case CURL_WANTYES:
518       switch(tn->usq[option]) {
519       case CURL_EMPTY:
520         /* Error: already negotiating for enable */
521         break;
522       case CURL_OPPOSITE:
523         tn->usq[option] = CURL_EMPTY;
524         break;
525       }
526       break;
527     }
528   }
529   else { /* NO */
530     switch(tn->us[option]) {
531     case CURL_NO:
532       /* Already disabled */
533       break;
534 
535     case CURL_YES:
536       tn->us[option] = CURL_WANTNO;
537       send_negotiation(data, CURL_WONT, option);
538       break;
539 
540     case CURL_WANTNO:
541       switch(tn->usq[option]) {
542       case CURL_EMPTY:
543         /* Already negotiating for NO */
544         break;
545       case CURL_OPPOSITE:
546         tn->usq[option] = CURL_EMPTY;
547         break;
548       }
549       break;
550 
551     case CURL_WANTYES:
552       switch(tn->usq[option]) {
553       case CURL_EMPTY:
554         tn->usq[option] = CURL_OPPOSITE;
555         break;
556       case CURL_OPPOSITE:
557         break;
558       }
559       break;
560     }
561   }
562 }
563 
564 static
rec_do(struct Curl_easy * data,int option)565 void rec_do(struct Curl_easy *data, int option)
566 {
567   struct TELNET *tn = data->req.p.telnet;
568   switch(tn->us[option]) {
569   case CURL_NO:
570     if(tn->us_preferred[option] == CURL_YES) {
571       tn->us[option] = CURL_YES;
572       send_negotiation(data, CURL_WILL, option);
573       if(tn->subnegotiation[option] == CURL_YES)
574         /* transmission of data option */
575         sendsuboption(data, option);
576     }
577     else if(tn->subnegotiation[option] == CURL_YES) {
578       /* send information to achieve this option */
579       tn->us[option] = CURL_YES;
580       send_negotiation(data, CURL_WILL, option);
581       sendsuboption(data, option);
582     }
583     else
584       send_negotiation(data, CURL_WONT, option);
585     break;
586 
587   case CURL_YES:
588     /* Already enabled */
589     break;
590 
591   case CURL_WANTNO:
592     switch(tn->usq[option]) {
593     case CURL_EMPTY:
594       /* Error: DONT answered by WILL */
595       tn->us[option] = CURL_NO;
596       break;
597     case CURL_OPPOSITE:
598       /* Error: DONT answered by WILL */
599       tn->us[option] = CURL_YES;
600       tn->usq[option] = CURL_EMPTY;
601       break;
602     }
603     break;
604 
605   case CURL_WANTYES:
606     switch(tn->usq[option]) {
607     case CURL_EMPTY:
608       tn->us[option] = CURL_YES;
609       if(tn->subnegotiation[option] == CURL_YES) {
610         /* transmission of data option */
611         sendsuboption(data, option);
612       }
613       break;
614     case CURL_OPPOSITE:
615       tn->us[option] = CURL_WANTNO;
616       tn->himq[option] = CURL_EMPTY;
617       send_negotiation(data, CURL_WONT, option);
618       break;
619     }
620     break;
621   }
622 }
623 
624 static
rec_dont(struct Curl_easy * data,int option)625 void rec_dont(struct Curl_easy *data, int option)
626 {
627   struct TELNET *tn = data->req.p.telnet;
628   switch(tn->us[option]) {
629   case CURL_NO:
630     /* Already disabled */
631     break;
632 
633   case CURL_YES:
634     tn->us[option] = CURL_NO;
635     send_negotiation(data, CURL_WONT, option);
636     break;
637 
638   case CURL_WANTNO:
639     switch(tn->usq[option]) {
640     case CURL_EMPTY:
641       tn->us[option] = CURL_NO;
642       break;
643 
644     case CURL_OPPOSITE:
645       tn->us[option] = CURL_WANTYES;
646       tn->usq[option] = CURL_EMPTY;
647       send_negotiation(data, CURL_WILL, option);
648       break;
649     }
650     break;
651 
652   case CURL_WANTYES:
653     switch(tn->usq[option]) {
654     case CURL_EMPTY:
655       tn->us[option] = CURL_NO;
656       break;
657     case CURL_OPPOSITE:
658       tn->us[option] = CURL_NO;
659       tn->usq[option] = CURL_EMPTY;
660       break;
661     }
662     break;
663   }
664 }
665 
666 
printsub(struct Curl_easy * data,int direction,unsigned char * pointer,size_t length)667 static void printsub(struct Curl_easy *data,
668                      int direction,             /* '<' or '>' */
669                      unsigned char *pointer,    /* where suboption data is */
670                      size_t length)             /* length of suboption data */
671 {
672   if(data->set.verbose) {
673     unsigned int i = 0;
674     if(direction) {
675       infof(data, "%s IAC SB ", (direction == '<') ? "RCVD" : "SENT");
676       if(length >= 3) {
677         int j;
678 
679         i = pointer[length-2];
680         j = pointer[length-1];
681 
682         if(i != CURL_IAC || j != CURL_SE) {
683           infof(data, "(terminated by ");
684           if(CURL_TELOPT_OK(i))
685             infof(data, "%s ", CURL_TELOPT(i));
686           else if(CURL_TELCMD_OK(i))
687             infof(data, "%s ", CURL_TELCMD(i));
688           else
689             infof(data, "%u ", i);
690           if(CURL_TELOPT_OK(j))
691             infof(data, "%s", CURL_TELOPT(j));
692           else if(CURL_TELCMD_OK(j))
693             infof(data, "%s", CURL_TELCMD(j));
694           else
695             infof(data, "%d", j);
696           infof(data, ", not IAC SE) ");
697         }
698       }
699       if(length >= 2)
700         length -= 2;
701       else /* bad input */
702         return;
703     }
704     if(length < 1) {
705       infof(data, "(Empty suboption?)");
706       return;
707     }
708 
709     if(CURL_TELOPT_OK(pointer[0])) {
710       switch(pointer[0]) {
711       case CURL_TELOPT_TTYPE:
712       case CURL_TELOPT_XDISPLOC:
713       case CURL_TELOPT_NEW_ENVIRON:
714       case CURL_TELOPT_NAWS:
715         infof(data, "%s", CURL_TELOPT(pointer[0]));
716         break;
717       default:
718         infof(data, "%s (unsupported)", CURL_TELOPT(pointer[0]));
719         break;
720       }
721     }
722     else
723       infof(data, "%d (unknown)", pointer[i]);
724 
725     switch(pointer[0]) {
726     case CURL_TELOPT_NAWS:
727       if(length > 4)
728         infof(data, "Width: %d ; Height: %d", (pointer[1] << 8) | pointer[2],
729               (pointer[3] << 8) | pointer[4]);
730       break;
731     default:
732       switch(pointer[1]) {
733       case CURL_TELQUAL_IS:
734         infof(data, " IS");
735         break;
736       case CURL_TELQUAL_SEND:
737         infof(data, " SEND");
738         break;
739       case CURL_TELQUAL_INFO:
740         infof(data, " INFO/REPLY");
741         break;
742       case CURL_TELQUAL_NAME:
743         infof(data, " NAME");
744         break;
745       }
746 
747       switch(pointer[0]) {
748       case CURL_TELOPT_TTYPE:
749       case CURL_TELOPT_XDISPLOC:
750         pointer[length] = 0;
751         infof(data, " \"%s\"", &pointer[2]);
752         break;
753       case CURL_TELOPT_NEW_ENVIRON:
754         if(pointer[1] == CURL_TELQUAL_IS) {
755           infof(data, " ");
756           for(i = 3; i < length; i++) {
757             switch(pointer[i]) {
758             case CURL_NEW_ENV_VAR:
759               infof(data, ", ");
760               break;
761             case CURL_NEW_ENV_VALUE:
762               infof(data, " = ");
763               break;
764             default:
765               infof(data, "%c", pointer[i]);
766               break;
767             }
768           }
769         }
770         break;
771       default:
772         for(i = 2; i < length; i++)
773           infof(data, " %.2x", pointer[i]);
774         break;
775       }
776     }
777   }
778 }
779 
780 #ifdef _MSC_VER
781 #pragma warning(push)
782 #pragma warning(disable:4706) /* assignment within conditional expression */
783 #endif
str_is_nonascii(const char * str)784 static bool str_is_nonascii(const char *str)
785 {
786   char c;
787   while((c = *str++))
788     if(c & 0x80)
789       return TRUE;
790 
791   return FALSE;
792 }
793 #ifdef _MSC_VER
794 #pragma warning(pop)
795 #endif
796 
check_telnet_options(struct Curl_easy * data)797 static CURLcode check_telnet_options(struct Curl_easy *data)
798 {
799   struct curl_slist *head;
800   struct curl_slist *beg;
801   struct TELNET *tn = data->req.p.telnet;
802   CURLcode result = CURLE_OK;
803 
804   /* Add the username as an environment variable if it
805      was given on the command line */
806   if(data->state.aptr.user) {
807     char buffer[256];
808     if(str_is_nonascii(data->conn->user)) {
809       DEBUGF(infof(data, "set a non ASCII username in telnet"));
810       return CURLE_BAD_FUNCTION_ARGUMENT;
811     }
812     msnprintf(buffer, sizeof(buffer), "USER,%s", data->conn->user);
813     beg = curl_slist_append(tn->telnet_vars, buffer);
814     if(!beg) {
815       curl_slist_free_all(tn->telnet_vars);
816       tn->telnet_vars = NULL;
817       return CURLE_OUT_OF_MEMORY;
818     }
819     tn->telnet_vars = beg;
820     tn->us_preferred[CURL_TELOPT_NEW_ENVIRON] = CURL_YES;
821   }
822 
823   for(head = data->set.telnet_options; head && !result; head = head->next) {
824     size_t olen;
825     char *option = head->data;
826     char *arg;
827     char *sep = strchr(option, '=');
828     if(sep) {
829       olen = sep - option;
830       arg = ++sep;
831       if(str_is_nonascii(arg))
832         continue;
833       switch(olen) {
834       case 5:
835         /* Terminal type */
836         if(strncasecompare(option, "TTYPE", 5)) {
837           tn->subopt_ttype = arg;
838           tn->us_preferred[CURL_TELOPT_TTYPE] = CURL_YES;
839           break;
840         }
841         result = CURLE_UNKNOWN_OPTION;
842         break;
843 
844       case 8:
845         /* Display variable */
846         if(strncasecompare(option, "XDISPLOC", 8)) {
847           tn->subopt_xdisploc = arg;
848           tn->us_preferred[CURL_TELOPT_XDISPLOC] = CURL_YES;
849           break;
850         }
851         result = CURLE_UNKNOWN_OPTION;
852         break;
853 
854       case 7:
855         /* Environment variable */
856         if(strncasecompare(option, "NEW_ENV", 7)) {
857           beg = curl_slist_append(tn->telnet_vars, arg);
858           if(!beg) {
859             result = CURLE_OUT_OF_MEMORY;
860             break;
861           }
862           tn->telnet_vars = beg;
863           tn->us_preferred[CURL_TELOPT_NEW_ENVIRON] = CURL_YES;
864         }
865         else
866           result = CURLE_UNKNOWN_OPTION;
867         break;
868 
869       case 2:
870         /* Window Size */
871         if(strncasecompare(option, "WS", 2)) {
872           char *p;
873           unsigned long x = strtoul(arg, &p, 10);
874           unsigned long y = 0;
875           if(x && (x <= 0xffff) && Curl_raw_tolower(*p) == 'x') {
876             p++;
877             y = strtoul(p, NULL, 10);
878             if(y && (y <= 0xffff)) {
879               tn->subopt_wsx = (unsigned short)x;
880               tn->subopt_wsy = (unsigned short)y;
881               tn->us_preferred[CURL_TELOPT_NAWS] = CURL_YES;
882             }
883           }
884           if(!y) {
885             failf(data, "Syntax error in telnet option: %s", head->data);
886             result = CURLE_SETOPT_OPTION_SYNTAX;
887           }
888         }
889         else
890           result = CURLE_UNKNOWN_OPTION;
891         break;
892 
893       case 6:
894         /* To take care or not of the 8th bit in data exchange */
895         if(strncasecompare(option, "BINARY", 6)) {
896           int binary_option = atoi(arg);
897           if(binary_option != 1) {
898             tn->us_preferred[CURL_TELOPT_BINARY] = CURL_NO;
899             tn->him_preferred[CURL_TELOPT_BINARY] = CURL_NO;
900           }
901         }
902         else
903           result = CURLE_UNKNOWN_OPTION;
904         break;
905       default:
906         failf(data, "Unknown telnet option %s", head->data);
907         result = CURLE_UNKNOWN_OPTION;
908         break;
909       }
910     }
911     else {
912       failf(data, "Syntax error in telnet option: %s", head->data);
913       result = CURLE_SETOPT_OPTION_SYNTAX;
914     }
915   }
916 
917   if(result) {
918     curl_slist_free_all(tn->telnet_vars);
919     tn->telnet_vars = NULL;
920   }
921 
922   return result;
923 }
924 
925 /*
926  * suboption()
927  *
928  * Look at the sub-option buffer, and try to be helpful to the other
929  * side.
930  */
931 
suboption(struct Curl_easy * data)932 static void suboption(struct Curl_easy *data)
933 {
934   struct curl_slist *v;
935   unsigned char temp[2048];
936   ssize_t bytes_written;
937   size_t len;
938   int err;
939   struct TELNET *tn = data->req.p.telnet;
940   struct connectdata *conn = data->conn;
941 
942   printsub(data, '<', (unsigned char *)tn->subbuffer, CURL_SB_LEN(tn) + 2);
943   switch(CURL_SB_GET(tn)) {
944     case CURL_TELOPT_TTYPE:
945       len = strlen(tn->subopt_ttype) + 4 + 2;
946       msnprintf((char *)temp, sizeof(temp),
947                 "%c%c%c%c%s%c%c", CURL_IAC, CURL_SB, CURL_TELOPT_TTYPE,
948                 CURL_TELQUAL_IS, tn->subopt_ttype, CURL_IAC, CURL_SE);
949       bytes_written = swrite(conn->sock[FIRSTSOCKET], temp, len);
950       if(bytes_written < 0) {
951         err = SOCKERRNO;
952         failf(data,"Sending data failed (%d)",err);
953       }
954       printsub(data, '>', &temp[2], len-2);
955       break;
956     case CURL_TELOPT_XDISPLOC:
957       len = strlen(tn->subopt_xdisploc) + 4 + 2;
958       msnprintf((char *)temp, sizeof(temp),
959                 "%c%c%c%c%s%c%c", CURL_IAC, CURL_SB, CURL_TELOPT_XDISPLOC,
960                 CURL_TELQUAL_IS, tn->subopt_xdisploc, CURL_IAC, CURL_SE);
961       bytes_written = swrite(conn->sock[FIRSTSOCKET], temp, len);
962       if(bytes_written < 0) {
963         err = SOCKERRNO;
964         failf(data,"Sending data failed (%d)",err);
965       }
966       printsub(data, '>', &temp[2], len-2);
967       break;
968     case CURL_TELOPT_NEW_ENVIRON:
969       msnprintf((char *)temp, sizeof(temp),
970                 "%c%c%c%c", CURL_IAC, CURL_SB, CURL_TELOPT_NEW_ENVIRON,
971                 CURL_TELQUAL_IS);
972       len = 4;
973 
974       for(v = tn->telnet_vars; v; v = v->next) {
975         size_t tmplen = (strlen(v->data) + 1);
976         /* Add the variable if it fits */
977         if(len + tmplen < (int)sizeof(temp)-6) {
978           char *s = strchr(v->data, ',');
979           if(!s)
980             len += msnprintf((char *)&temp[len], sizeof(temp) - len,
981                              "%c%s", CURL_NEW_ENV_VAR, v->data);
982           else {
983             size_t vlen = s - v->data;
984             len += msnprintf((char *)&temp[len], sizeof(temp) - len,
985                              "%c%.*s%c%s", CURL_NEW_ENV_VAR,
986                              (int)vlen, v->data, CURL_NEW_ENV_VALUE, ++s);
987           }
988         }
989       }
990       msnprintf((char *)&temp[len], sizeof(temp) - len,
991                 "%c%c", CURL_IAC, CURL_SE);
992       len += 2;
993       bytes_written = swrite(conn->sock[FIRSTSOCKET], temp, len);
994       if(bytes_written < 0) {
995         err = SOCKERRNO;
996         failf(data,"Sending data failed (%d)",err);
997       }
998       printsub(data, '>', &temp[2], len-2);
999       break;
1000   }
1001   return;
1002 }
1003 
1004 
1005 /*
1006  * sendsuboption()
1007  *
1008  * Send suboption information to the server side.
1009  */
1010 
sendsuboption(struct Curl_easy * data,int option)1011 static void sendsuboption(struct Curl_easy *data, int option)
1012 {
1013   ssize_t bytes_written;
1014   int err;
1015   unsigned short x, y;
1016   unsigned char *uc1, *uc2;
1017   struct TELNET *tn = data->req.p.telnet;
1018   struct connectdata *conn = data->conn;
1019 
1020   switch(option) {
1021   case CURL_TELOPT_NAWS:
1022     /* We prepare data to be sent */
1023     CURL_SB_CLEAR(tn);
1024     CURL_SB_ACCUM(tn, CURL_IAC);
1025     CURL_SB_ACCUM(tn, CURL_SB);
1026     CURL_SB_ACCUM(tn, CURL_TELOPT_NAWS);
1027     /* We must deal either with little or big endian processors */
1028     /* Window size must be sent according to the 'network order' */
1029     x = htons(tn->subopt_wsx);
1030     y = htons(tn->subopt_wsy);
1031     uc1 = (unsigned char *)&x;
1032     uc2 = (unsigned char *)&y;
1033     CURL_SB_ACCUM(tn, uc1[0]);
1034     CURL_SB_ACCUM(tn, uc1[1]);
1035     CURL_SB_ACCUM(tn, uc2[0]);
1036     CURL_SB_ACCUM(tn, uc2[1]);
1037 
1038     CURL_SB_ACCUM(tn, CURL_IAC);
1039     CURL_SB_ACCUM(tn, CURL_SE);
1040     CURL_SB_TERM(tn);
1041     /* data suboption is now ready */
1042 
1043     printsub(data, '>', (unsigned char *)tn->subbuffer + 2,
1044              CURL_SB_LEN(tn)-2);
1045 
1046     /* we send the header of the suboption... */
1047     bytes_written = swrite(conn->sock[FIRSTSOCKET], tn->subbuffer, 3);
1048     if(bytes_written < 0) {
1049       err = SOCKERRNO;
1050       failf(data, "Sending data failed (%d)", err);
1051     }
1052     /* ... then the window size with the send_telnet_data() function
1053        to deal with 0xFF cases ... */
1054     send_telnet_data(data, (char *)tn->subbuffer + 3, 4);
1055     /* ... and the footer */
1056     bytes_written = swrite(conn->sock[FIRSTSOCKET], tn->subbuffer + 7, 2);
1057     if(bytes_written < 0) {
1058       err = SOCKERRNO;
1059       failf(data, "Sending data failed (%d)", err);
1060     }
1061     break;
1062   }
1063 }
1064 
1065 
1066 static
telrcv(struct Curl_easy * data,const unsigned char * inbuf,ssize_t count)1067 CURLcode telrcv(struct Curl_easy *data,
1068                 const unsigned char *inbuf, /* Data received from socket */
1069                 ssize_t count)              /* Number of bytes received */
1070 {
1071   unsigned char c;
1072   CURLcode result;
1073   int in = 0;
1074   int startwrite = -1;
1075   struct TELNET *tn = data->req.p.telnet;
1076 
1077 #define startskipping()                                       \
1078   if(startwrite >= 0) {                                       \
1079     result = Curl_client_write(data,                          \
1080                                CLIENTWRITE_BODY,              \
1081                                (char *)&inbuf[startwrite],    \
1082                                in-startwrite);                \
1083     if(result)                                                \
1084       return result;                                          \
1085   }                                                           \
1086   startwrite = -1
1087 
1088 #define writebyte() \
1089     if(startwrite < 0) \
1090       startwrite = in
1091 
1092 #define bufferflush() startskipping()
1093 
1094   while(count--) {
1095     c = inbuf[in];
1096 
1097     switch(tn->telrcv_state) {
1098     case CURL_TS_CR:
1099       tn->telrcv_state = CURL_TS_DATA;
1100       if(c == '\0') {
1101         startskipping();
1102         break;   /* Ignore \0 after CR */
1103       }
1104       writebyte();
1105       break;
1106 
1107     case CURL_TS_DATA:
1108       if(c == CURL_IAC) {
1109         tn->telrcv_state = CURL_TS_IAC;
1110         startskipping();
1111         break;
1112       }
1113       else if(c == '\r')
1114         tn->telrcv_state = CURL_TS_CR;
1115       writebyte();
1116       break;
1117 
1118     case CURL_TS_IAC:
1119 process_iac:
1120       DEBUGASSERT(startwrite < 0);
1121       switch(c) {
1122       case CURL_WILL:
1123         tn->telrcv_state = CURL_TS_WILL;
1124         break;
1125       case CURL_WONT:
1126         tn->telrcv_state = CURL_TS_WONT;
1127         break;
1128       case CURL_DO:
1129         tn->telrcv_state = CURL_TS_DO;
1130         break;
1131       case CURL_DONT:
1132         tn->telrcv_state = CURL_TS_DONT;
1133         break;
1134       case CURL_SB:
1135         CURL_SB_CLEAR(tn);
1136         tn->telrcv_state = CURL_TS_SB;
1137         break;
1138       case CURL_IAC:
1139         tn->telrcv_state = CURL_TS_DATA;
1140         writebyte();
1141         break;
1142       case CURL_DM:
1143       case CURL_NOP:
1144       case CURL_GA:
1145       default:
1146         tn->telrcv_state = CURL_TS_DATA;
1147         printoption(data, "RCVD", CURL_IAC, c);
1148         break;
1149       }
1150       break;
1151 
1152       case CURL_TS_WILL:
1153         printoption(data, "RCVD", CURL_WILL, c);
1154         tn->please_negotiate = 1;
1155         rec_will(data, c);
1156         tn->telrcv_state = CURL_TS_DATA;
1157         break;
1158 
1159       case CURL_TS_WONT:
1160         printoption(data, "RCVD", CURL_WONT, c);
1161         tn->please_negotiate = 1;
1162         rec_wont(data, c);
1163         tn->telrcv_state = CURL_TS_DATA;
1164         break;
1165 
1166       case CURL_TS_DO:
1167         printoption(data, "RCVD", CURL_DO, c);
1168         tn->please_negotiate = 1;
1169         rec_do(data, c);
1170         tn->telrcv_state = CURL_TS_DATA;
1171         break;
1172 
1173       case CURL_TS_DONT:
1174         printoption(data, "RCVD", CURL_DONT, c);
1175         tn->please_negotiate = 1;
1176         rec_dont(data, c);
1177         tn->telrcv_state = CURL_TS_DATA;
1178         break;
1179 
1180       case CURL_TS_SB:
1181         if(c == CURL_IAC)
1182           tn->telrcv_state = CURL_TS_SE;
1183         else
1184           CURL_SB_ACCUM(tn, c);
1185         break;
1186 
1187       case CURL_TS_SE:
1188         if(c != CURL_SE) {
1189           if(c != CURL_IAC) {
1190             /*
1191              * This is an error. We only expect to get "IAC IAC" or "IAC SE".
1192              * Several things may have happened. An IAC was not doubled, the
1193              * IAC SE was left off, or another option got inserted into the
1194              * suboption are all possibilities. If we assume that the IAC was
1195              * not doubled, and really the IAC SE was left off, we could get
1196              * into an infinite loop here. So, instead, we terminate the
1197              * suboption, and process the partial suboption if we can.
1198              */
1199             CURL_SB_ACCUM(tn, CURL_IAC);
1200             CURL_SB_ACCUM(tn, c);
1201             tn->subpointer -= 2;
1202             CURL_SB_TERM(tn);
1203 
1204             printoption(data, "In SUBOPTION processing, RCVD", CURL_IAC, c);
1205             suboption(data);   /* handle sub-option */
1206             tn->telrcv_state = CURL_TS_IAC;
1207             goto process_iac;
1208           }
1209           CURL_SB_ACCUM(tn, c);
1210           tn->telrcv_state = CURL_TS_SB;
1211         }
1212         else {
1213           CURL_SB_ACCUM(tn, CURL_IAC);
1214           CURL_SB_ACCUM(tn, CURL_SE);
1215           tn->subpointer -= 2;
1216           CURL_SB_TERM(tn);
1217           suboption(data);   /* handle sub-option */
1218           tn->telrcv_state = CURL_TS_DATA;
1219         }
1220         break;
1221     }
1222     ++in;
1223   }
1224   bufferflush();
1225   return CURLE_OK;
1226 }
1227 
1228 /* Escape and send a telnet data block */
send_telnet_data(struct Curl_easy * data,char * buffer,ssize_t nread)1229 static CURLcode send_telnet_data(struct Curl_easy *data,
1230                                  char *buffer, ssize_t nread)
1231 {
1232   size_t i, outlen;
1233   unsigned char *outbuf;
1234   CURLcode result = CURLE_OK;
1235   size_t bytes_written;
1236   size_t total_written = 0;
1237   struct connectdata *conn = data->conn;
1238   struct TELNET *tn = data->req.p.telnet;
1239 
1240   DEBUGASSERT(tn);
1241   DEBUGASSERT(nread > 0);
1242   if(nread < 0)
1243     return CURLE_TOO_LARGE;
1244 
1245   if(memchr(buffer, CURL_IAC, nread)) {
1246     /* only use the escape buffer when necessary */
1247     Curl_dyn_reset(&tn->out);
1248 
1249     for(i = 0; i < (size_t)nread && !result; i++) {
1250       result = Curl_dyn_addn(&tn->out, &buffer[i], 1);
1251       if(!result && ((unsigned char)buffer[i] == CURL_IAC))
1252         /* IAC is FF in hex */
1253         result = Curl_dyn_addn(&tn->out, "\xff", 1);
1254     }
1255 
1256     outlen = Curl_dyn_len(&tn->out);
1257     outbuf = Curl_dyn_uptr(&tn->out);
1258   }
1259   else {
1260     outlen = (size_t)nread;
1261     outbuf = (unsigned char *)buffer;
1262   }
1263   while(!result && total_written < outlen) {
1264     /* Make sure socket is writable to avoid EWOULDBLOCK condition */
1265     struct pollfd pfd[1];
1266     pfd[0].fd = conn->sock[FIRSTSOCKET];
1267     pfd[0].events = POLLOUT;
1268     switch(Curl_poll(pfd, 1, -1)) {
1269       case -1:                    /* error, abort writing */
1270       case 0:                     /* timeout (will never happen) */
1271         result = CURLE_SEND_ERROR;
1272         break;
1273       default:                    /* write! */
1274         bytes_written = 0;
1275         result = Curl_xfer_send(data, outbuf + total_written,
1276                                 outlen - total_written, FALSE, &bytes_written);
1277         total_written += bytes_written;
1278         break;
1279     }
1280   }
1281 
1282   return result;
1283 }
1284 
telnet_done(struct Curl_easy * data,CURLcode status,bool premature)1285 static CURLcode telnet_done(struct Curl_easy *data,
1286                             CURLcode status, bool premature)
1287 {
1288   struct TELNET *tn = data->req.p.telnet;
1289   (void)status; /* unused */
1290   (void)premature; /* not used */
1291 
1292   if(!tn)
1293     return CURLE_OK;
1294 
1295   curl_slist_free_all(tn->telnet_vars);
1296   tn->telnet_vars = NULL;
1297   Curl_dyn_free(&tn->out);
1298   return CURLE_OK;
1299 }
1300 
telnet_do(struct Curl_easy * data,bool * done)1301 static CURLcode telnet_do(struct Curl_easy *data, bool *done)
1302 {
1303   CURLcode result;
1304   struct connectdata *conn = data->conn;
1305   curl_socket_t sockfd = conn->sock[FIRSTSOCKET];
1306 #ifdef USE_WINSOCK
1307   WSAEVENT event_handle;
1308   WSANETWORKEVENTS events;
1309   HANDLE stdin_handle;
1310   HANDLE objs[2];
1311   DWORD  obj_count;
1312   DWORD  wait_timeout;
1313   DWORD readfile_read;
1314   int err;
1315 #else
1316   timediff_t interval_ms;
1317   struct pollfd pfd[2];
1318   int poll_cnt;
1319   curl_off_t total_dl = 0;
1320   curl_off_t total_ul = 0;
1321 #endif
1322   ssize_t nread;
1323   struct curltime now;
1324   bool keepon = TRUE;
1325   char buffer[4*1024];
1326   struct TELNET *tn;
1327 
1328   *done = TRUE; /* unconditionally */
1329 
1330   result = init_telnet(data);
1331   if(result)
1332     return result;
1333 
1334   tn = data->req.p.telnet;
1335 
1336   result = check_telnet_options(data);
1337   if(result)
1338     return result;
1339 
1340 #ifdef USE_WINSOCK
1341   /* We want to wait for both stdin and the socket. Since
1342   ** the select() function in Winsock only works on sockets
1343   ** we have to use the WaitForMultipleObjects() call.
1344   */
1345 
1346   /* First, create a sockets event object */
1347   event_handle = WSACreateEvent();
1348   if(event_handle == WSA_INVALID_EVENT) {
1349     failf(data, "WSACreateEvent failed (%d)", SOCKERRNO);
1350     return CURLE_FAILED_INIT;
1351   }
1352 
1353   /* Tell Winsock what events we want to listen to */
1354   if(WSAEventSelect(sockfd, event_handle, FD_READ|FD_CLOSE) == SOCKET_ERROR) {
1355     WSACloseEvent(event_handle);
1356     return CURLE_OK;
1357   }
1358 
1359   /* The get the Windows file handle for stdin */
1360   stdin_handle = GetStdHandle(STD_INPUT_HANDLE);
1361 
1362   /* Create the list of objects to wait for */
1363   objs[0] = event_handle;
1364   objs[1] = stdin_handle;
1365 
1366   /* If stdin_handle is a pipe, use PeekNamedPipe() method to check it,
1367      else use the old WaitForMultipleObjects() way */
1368   if(GetFileType(stdin_handle) == FILE_TYPE_PIPE ||
1369      data->set.is_fread_set) {
1370     /* Do not wait for stdin_handle, just wait for event_handle */
1371     obj_count = 1;
1372     /* Check stdin_handle per 100 milliseconds */
1373     wait_timeout = 100;
1374   }
1375   else {
1376     obj_count = 2;
1377     wait_timeout = 1000;
1378   }
1379 
1380   /* Keep on listening and act on events */
1381   while(keepon) {
1382     const DWORD buf_size = (DWORD)sizeof(buffer);
1383     DWORD waitret = WaitForMultipleObjects(obj_count, objs,
1384                                            FALSE, wait_timeout);
1385     switch(waitret) {
1386 
1387     case WAIT_TIMEOUT:
1388     {
1389       for(;;) {
1390         if(data->set.is_fread_set) {
1391           size_t n;
1392           /* read from user-supplied method */
1393           n = data->state.fread_func(buffer, 1, buf_size, data->state.in);
1394           if(n == CURL_READFUNC_ABORT) {
1395             keepon = FALSE;
1396             result = CURLE_READ_ERROR;
1397             break;
1398           }
1399 
1400           if(n == CURL_READFUNC_PAUSE)
1401             break;
1402 
1403           if(n == 0)                        /* no bytes */
1404             break;
1405 
1406           /* fall through with number of bytes read */
1407           readfile_read = (DWORD)n;
1408         }
1409         else {
1410           /* read from stdin */
1411           if(!PeekNamedPipe(stdin_handle, NULL, 0, NULL,
1412                             &readfile_read, NULL)) {
1413             keepon = FALSE;
1414             result = CURLE_READ_ERROR;
1415             break;
1416           }
1417 
1418           if(!readfile_read)
1419             break;
1420 
1421           if(!ReadFile(stdin_handle, buffer, buf_size,
1422                        &readfile_read, NULL)) {
1423             keepon = FALSE;
1424             result = CURLE_READ_ERROR;
1425             break;
1426           }
1427         }
1428 
1429         result = send_telnet_data(data, buffer, readfile_read);
1430         if(result) {
1431           keepon = FALSE;
1432           break;
1433         }
1434       }
1435     }
1436     break;
1437 
1438     case WAIT_OBJECT_0 + 1:
1439     {
1440       if(!ReadFile(stdin_handle, buffer, buf_size,
1441                    &readfile_read, NULL)) {
1442         keepon = FALSE;
1443         result = CURLE_READ_ERROR;
1444         break;
1445       }
1446 
1447       result = send_telnet_data(data, buffer, readfile_read);
1448       if(result) {
1449         keepon = FALSE;
1450         break;
1451       }
1452     }
1453     break;
1454 
1455     case WAIT_OBJECT_0:
1456     {
1457       events.lNetworkEvents = 0;
1458       if(WSAEnumNetworkEvents(sockfd, event_handle, &events) == SOCKET_ERROR) {
1459         err = SOCKERRNO;
1460         if(err != EINPROGRESS) {
1461           infof(data, "WSAEnumNetworkEvents failed (%d)", err);
1462           keepon = FALSE;
1463           result = CURLE_READ_ERROR;
1464         }
1465         break;
1466       }
1467       if(events.lNetworkEvents & FD_READ) {
1468         /* read data from network */
1469         result = Curl_xfer_recv(data, buffer, sizeof(buffer), &nread);
1470         /* read would have blocked. Loop again */
1471         if(result == CURLE_AGAIN)
1472           break;
1473         /* returned not-zero, this an error */
1474         else if(result) {
1475           keepon = FALSE;
1476           break;
1477         }
1478         /* returned zero but actually received 0 or less here,
1479            the server closed the connection and we bail out */
1480         else if(nread <= 0) {
1481           keepon = FALSE;
1482           break;
1483         }
1484 
1485         result = telrcv(data, (unsigned char *) buffer, nread);
1486         if(result) {
1487           keepon = FALSE;
1488           break;
1489         }
1490 
1491         /* Negotiate if the peer has started negotiating,
1492            otherwise do not. We do not want to speak telnet with
1493            non-telnet servers, like POP or SMTP. */
1494         if(tn->please_negotiate && !tn->already_negotiated) {
1495           negotiate(data);
1496           tn->already_negotiated = 1;
1497         }
1498       }
1499       if(events.lNetworkEvents & FD_CLOSE) {
1500         keepon = FALSE;
1501       }
1502     }
1503     break;
1504 
1505     }
1506 
1507     if(data->set.timeout) {
1508       now = Curl_now();
1509       if(Curl_timediff(now, conn->created) >= data->set.timeout) {
1510         failf(data, "Time-out");
1511         result = CURLE_OPERATION_TIMEDOUT;
1512         keepon = FALSE;
1513       }
1514     }
1515   }
1516 
1517   /* We called WSACreateEvent, so call WSACloseEvent */
1518   if(!WSACloseEvent(event_handle)) {
1519     infof(data, "WSACloseEvent failed (%d)", SOCKERRNO);
1520   }
1521 #else
1522   pfd[0].fd = sockfd;
1523   pfd[0].events = POLLIN;
1524 
1525   if(data->set.is_fread_set) {
1526     poll_cnt = 1;
1527     interval_ms = 100; /* poll user-supplied read function */
1528   }
1529   else {
1530     /* really using fread, so infile is a FILE* */
1531     pfd[1].fd = fileno((FILE *)data->state.in);
1532     pfd[1].events = POLLIN;
1533     poll_cnt = 2;
1534     interval_ms = 1 * 1000;
1535     if(pfd[1].fd < 0) {
1536       failf(data, "cannot read input");
1537       result = CURLE_RECV_ERROR;
1538       keepon = FALSE;
1539     }
1540   }
1541 
1542   while(keepon) {
1543     DEBUGF(infof(data, "telnet_do, poll %d fds", poll_cnt));
1544     switch(Curl_poll(pfd, (unsigned int)poll_cnt, interval_ms)) {
1545     case -1:                    /* error, stop reading */
1546       keepon = FALSE;
1547       continue;
1548     case 0:                     /* timeout */
1549       pfd[0].revents = 0;
1550       pfd[1].revents = 0;
1551       FALLTHROUGH();
1552     default:                    /* read! */
1553       if(pfd[0].revents & POLLIN) {
1554         /* read data from network */
1555         result = Curl_xfer_recv(data, buffer, sizeof(buffer), &nread);
1556         /* read would have blocked. Loop again */
1557         if(result == CURLE_AGAIN)
1558           break;
1559         /* returned not-zero, this an error */
1560         if(result) {
1561           keepon = FALSE;
1562           /* TODO: in test 1452, macOS sees a ECONNRESET sometimes?
1563            * Is this the telnet test server not shutting down the socket
1564            * in a clean way? Seems to be timing related, happens more
1565            * on slow debug build */
1566           if(data->state.os_errno == ECONNRESET) {
1567             DEBUGF(infof(data, "telnet_do, unexpected ECONNRESET on recv"));
1568           }
1569           break;
1570         }
1571         /* returned zero but actually received 0 or less here,
1572            the server closed the connection and we bail out */
1573         else if(nread <= 0) {
1574           keepon = FALSE;
1575           break;
1576         }
1577 
1578         total_dl += nread;
1579         result = Curl_pgrsSetDownloadCounter(data, total_dl);
1580         if(!result)
1581           result = telrcv(data, (unsigned char *)buffer, nread);
1582         if(result) {
1583           keepon = FALSE;
1584           break;
1585         }
1586 
1587         /* Negotiate if the peer has started negotiating,
1588            otherwise do not. We do not want to speak telnet with
1589            non-telnet servers, like POP or SMTP. */
1590         if(tn->please_negotiate && !tn->already_negotiated) {
1591           negotiate(data);
1592           tn->already_negotiated = 1;
1593         }
1594       }
1595 
1596       nread = 0;
1597       if(poll_cnt == 2) {
1598         if(pfd[1].revents & POLLIN) { /* read from in file */
1599           nread = read(pfd[1].fd, buffer, sizeof(buffer));
1600         }
1601       }
1602       else {
1603         /* read from user-supplied method */
1604         nread = (int)data->state.fread_func(buffer, 1, sizeof(buffer),
1605                                             data->state.in);
1606         if(nread == CURL_READFUNC_ABORT) {
1607           keepon = FALSE;
1608           break;
1609         }
1610         if(nread == CURL_READFUNC_PAUSE)
1611           break;
1612       }
1613 
1614       if(nread > 0) {
1615         result = send_telnet_data(data, buffer, nread);
1616         if(result) {
1617           keepon = FALSE;
1618           break;
1619         }
1620         total_ul += nread;
1621         Curl_pgrsSetUploadCounter(data, total_ul);
1622       }
1623       else if(nread < 0)
1624         keepon = FALSE;
1625 
1626       break;
1627     } /* poll switch statement */
1628 
1629     if(data->set.timeout) {
1630       now = Curl_now();
1631       if(Curl_timediff(now, conn->created) >= data->set.timeout) {
1632         failf(data, "Time-out");
1633         result = CURLE_OPERATION_TIMEDOUT;
1634         keepon = FALSE;
1635       }
1636     }
1637 
1638     if(Curl_pgrsUpdate(data)) {
1639       result = CURLE_ABORTED_BY_CALLBACK;
1640       break;
1641     }
1642   }
1643 #endif
1644   /* mark this as "no further transfer wanted" */
1645   Curl_xfer_setup_nop(data);
1646 
1647   return result;
1648 }
1649 #endif
1650