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