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