xref: /curl/lib/telnet.c (revision 385c62aa)
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,                            /* 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 user name 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 user name 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           size_t l = strlen(arg);
835           if(l < sizeof(tn->subopt_ttype)) {
836             strcpy(tn->subopt_ttype, arg);
837             tn->us_preferred[CURL_TELOPT_TTYPE] = CURL_YES;
838             break;
839           }
840         }
841         result = CURLE_UNKNOWN_OPTION;
842         break;
843 
844       case 8:
845         /* Display variable */
846         if(strncasecompare(option, "XDISPLOC", 8)) {
847           size_t l = strlen(arg);
848           if(l < sizeof(tn->subopt_xdisploc)) {
849             strcpy(tn->subopt_xdisploc, arg);
850             tn->us_preferred[CURL_TELOPT_XDISPLOC] = CURL_YES;
851             break;
852           }
853         }
854         result = CURLE_UNKNOWN_OPTION;
855         break;
856 
857       case 7:
858         /* Environment variable */
859         if(strncasecompare(option, "NEW_ENV", 7)) {
860           beg = curl_slist_append(tn->telnet_vars, arg);
861           if(!beg) {
862             result = CURLE_OUT_OF_MEMORY;
863             break;
864           }
865           tn->telnet_vars = beg;
866           tn->us_preferred[CURL_TELOPT_NEW_ENVIRON] = CURL_YES;
867         }
868         else
869           result = CURLE_UNKNOWN_OPTION;
870         break;
871 
872       case 2:
873         /* Window Size */
874         if(strncasecompare(option, "WS", 2)) {
875           char *p;
876           unsigned long x = strtoul(arg, &p, 10);
877           unsigned long y = 0;
878           if(x && (x <= 0xffff) && Curl_raw_tolower(*p) == 'x') {
879             p++;
880             y = strtoul(p, NULL, 10);
881             if(y && (y <= 0xffff)) {
882               tn->subopt_wsx = (unsigned short)x;
883               tn->subopt_wsy = (unsigned short)y;
884               tn->us_preferred[CURL_TELOPT_NAWS] = CURL_YES;
885             }
886           }
887           if(!y) {
888             failf(data, "Syntax error in telnet option: %s", head->data);
889             result = CURLE_SETOPT_OPTION_SYNTAX;
890           }
891         }
892         else
893           result = CURLE_UNKNOWN_OPTION;
894         break;
895 
896       case 6:
897         /* To take care or not of the 8th bit in data exchange */
898         if(strncasecompare(option, "BINARY", 6)) {
899           int binary_option = atoi(arg);
900           if(binary_option != 1) {
901             tn->us_preferred[CURL_TELOPT_BINARY] = CURL_NO;
902             tn->him_preferred[CURL_TELOPT_BINARY] = CURL_NO;
903           }
904         }
905         else
906           result = CURLE_UNKNOWN_OPTION;
907         break;
908       default:
909         failf(data, "Unknown telnet option %s", head->data);
910         result = CURLE_UNKNOWN_OPTION;
911         break;
912       }
913     }
914     else {
915       failf(data, "Syntax error in telnet option: %s", head->data);
916       result = CURLE_SETOPT_OPTION_SYNTAX;
917     }
918   }
919 
920   if(result) {
921     curl_slist_free_all(tn->telnet_vars);
922     tn->telnet_vars = NULL;
923   }
924 
925   return result;
926 }
927 
928 /*
929  * suboption()
930  *
931  * Look at the sub-option buffer, and try to be helpful to the other
932  * side.
933  */
934 
suboption(struct Curl_easy * data)935 static void suboption(struct Curl_easy *data)
936 {
937   struct curl_slist *v;
938   unsigned char temp[2048];
939   ssize_t bytes_written;
940   size_t len;
941   int err;
942   struct TELNET *tn = data->req.p.telnet;
943   struct connectdata *conn = data->conn;
944 
945   printsub(data, '<', (unsigned char *)tn->subbuffer, CURL_SB_LEN(tn) + 2);
946   switch(CURL_SB_GET(tn)) {
947     case CURL_TELOPT_TTYPE:
948       len = strlen(tn->subopt_ttype) + 4 + 2;
949       msnprintf((char *)temp, sizeof(temp),
950                 "%c%c%c%c%s%c%c", CURL_IAC, CURL_SB, CURL_TELOPT_TTYPE,
951                 CURL_TELQUAL_IS, tn->subopt_ttype, CURL_IAC, CURL_SE);
952       bytes_written = swrite(conn->sock[FIRSTSOCKET], temp, len);
953       if(bytes_written < 0) {
954         err = SOCKERRNO;
955         failf(data,"Sending data failed (%d)",err);
956       }
957       printsub(data, '>', &temp[2], len-2);
958       break;
959     case CURL_TELOPT_XDISPLOC:
960       len = strlen(tn->subopt_xdisploc) + 4 + 2;
961       msnprintf((char *)temp, sizeof(temp),
962                 "%c%c%c%c%s%c%c", CURL_IAC, CURL_SB, CURL_TELOPT_XDISPLOC,
963                 CURL_TELQUAL_IS, tn->subopt_xdisploc, CURL_IAC, CURL_SE);
964       bytes_written = swrite(conn->sock[FIRSTSOCKET], temp, len);
965       if(bytes_written < 0) {
966         err = SOCKERRNO;
967         failf(data,"Sending data failed (%d)",err);
968       }
969       printsub(data, '>', &temp[2], len-2);
970       break;
971     case CURL_TELOPT_NEW_ENVIRON:
972       msnprintf((char *)temp, sizeof(temp),
973                 "%c%c%c%c", CURL_IAC, CURL_SB, CURL_TELOPT_NEW_ENVIRON,
974                 CURL_TELQUAL_IS);
975       len = 4;
976 
977       for(v = tn->telnet_vars; v; v = v->next) {
978         size_t tmplen = (strlen(v->data) + 1);
979         /* Add the variable if it fits */
980         if(len + tmplen < (int)sizeof(temp)-6) {
981           char *s = strchr(v->data, ',');
982           if(!s)
983             len += msnprintf((char *)&temp[len], sizeof(temp) - len,
984                              "%c%s", CURL_NEW_ENV_VAR, v->data);
985           else {
986             size_t vlen = s - v->data;
987             len += msnprintf((char *)&temp[len], sizeof(temp) - len,
988                              "%c%.*s%c%s", CURL_NEW_ENV_VAR,
989                              (int)vlen, v->data, CURL_NEW_ENV_VALUE, ++s);
990           }
991         }
992       }
993       msnprintf((char *)&temp[len], sizeof(temp) - len,
994                 "%c%c", CURL_IAC, CURL_SE);
995       len += 2;
996       bytes_written = swrite(conn->sock[FIRSTSOCKET], temp, len);
997       if(bytes_written < 0) {
998         err = SOCKERRNO;
999         failf(data,"Sending data failed (%d)",err);
1000       }
1001       printsub(data, '>', &temp[2], len-2);
1002       break;
1003   }
1004   return;
1005 }
1006 
1007 
1008 /*
1009  * sendsuboption()
1010  *
1011  * Send suboption information to the server side.
1012  */
1013 
sendsuboption(struct Curl_easy * data,int option)1014 static void sendsuboption(struct Curl_easy *data, int option)
1015 {
1016   ssize_t bytes_written;
1017   int err;
1018   unsigned short x, y;
1019   unsigned char *uc1, *uc2;
1020   struct TELNET *tn = data->req.p.telnet;
1021   struct connectdata *conn = data->conn;
1022 
1023   switch(option) {
1024   case CURL_TELOPT_NAWS:
1025     /* We prepare data to be sent */
1026     CURL_SB_CLEAR(tn);
1027     CURL_SB_ACCUM(tn, CURL_IAC);
1028     CURL_SB_ACCUM(tn, CURL_SB);
1029     CURL_SB_ACCUM(tn, CURL_TELOPT_NAWS);
1030     /* We must deal either with little or big endian processors */
1031     /* Window size must be sent according to the 'network order' */
1032     x = htons(tn->subopt_wsx);
1033     y = htons(tn->subopt_wsy);
1034     uc1 = (unsigned char *)&x;
1035     uc2 = (unsigned char *)&y;
1036     CURL_SB_ACCUM(tn, uc1[0]);
1037     CURL_SB_ACCUM(tn, uc1[1]);
1038     CURL_SB_ACCUM(tn, uc2[0]);
1039     CURL_SB_ACCUM(tn, uc2[1]);
1040 
1041     CURL_SB_ACCUM(tn, CURL_IAC);
1042     CURL_SB_ACCUM(tn, CURL_SE);
1043     CURL_SB_TERM(tn);
1044     /* data suboption is now ready */
1045 
1046     printsub(data, '>', (unsigned char *)tn->subbuffer + 2,
1047              CURL_SB_LEN(tn)-2);
1048 
1049     /* we send the header of the suboption... */
1050     bytes_written = swrite(conn->sock[FIRSTSOCKET], tn->subbuffer, 3);
1051     if(bytes_written < 0) {
1052       err = SOCKERRNO;
1053       failf(data, "Sending data failed (%d)", err);
1054     }
1055     /* ... then the window size with the send_telnet_data() function
1056        to deal with 0xFF cases ... */
1057     send_telnet_data(data, (char *)tn->subbuffer + 3, 4);
1058     /* ... and the footer */
1059     bytes_written = swrite(conn->sock[FIRSTSOCKET], tn->subbuffer + 7, 2);
1060     if(bytes_written < 0) {
1061       err = SOCKERRNO;
1062       failf(data, "Sending data failed (%d)", err);
1063     }
1064     break;
1065   }
1066 }
1067 
1068 
1069 static
telrcv(struct Curl_easy * data,const unsigned char * inbuf,ssize_t count)1070 CURLcode telrcv(struct Curl_easy *data,
1071                 const unsigned char *inbuf, /* Data received from socket */
1072                 ssize_t count)              /* Number of bytes received */
1073 {
1074   unsigned char c;
1075   CURLcode result;
1076   int in = 0;
1077   int startwrite = -1;
1078   struct TELNET *tn = data->req.p.telnet;
1079 
1080 #define startskipping()                                       \
1081   if(startwrite >= 0) {                                       \
1082     result = Curl_client_write(data,                          \
1083                                CLIENTWRITE_BODY,              \
1084                                (char *)&inbuf[startwrite],    \
1085                                in-startwrite);                \
1086     if(result)                                                \
1087       return result;                                          \
1088   }                                                           \
1089   startwrite = -1
1090 
1091 #define writebyte() \
1092     if(startwrite < 0) \
1093       startwrite = in
1094 
1095 #define bufferflush() startskipping()
1096 
1097   while(count--) {
1098     c = inbuf[in];
1099 
1100     switch(tn->telrcv_state) {
1101     case CURL_TS_CR:
1102       tn->telrcv_state = CURL_TS_DATA;
1103       if(c == '\0') {
1104         startskipping();
1105         break;   /* Ignore \0 after CR */
1106       }
1107       writebyte();
1108       break;
1109 
1110     case CURL_TS_DATA:
1111       if(c == CURL_IAC) {
1112         tn->telrcv_state = CURL_TS_IAC;
1113         startskipping();
1114         break;
1115       }
1116       else if(c == '\r')
1117         tn->telrcv_state = CURL_TS_CR;
1118       writebyte();
1119       break;
1120 
1121     case CURL_TS_IAC:
1122 process_iac:
1123       DEBUGASSERT(startwrite < 0);
1124       switch(c) {
1125       case CURL_WILL:
1126         tn->telrcv_state = CURL_TS_WILL;
1127         break;
1128       case CURL_WONT:
1129         tn->telrcv_state = CURL_TS_WONT;
1130         break;
1131       case CURL_DO:
1132         tn->telrcv_state = CURL_TS_DO;
1133         break;
1134       case CURL_DONT:
1135         tn->telrcv_state = CURL_TS_DONT;
1136         break;
1137       case CURL_SB:
1138         CURL_SB_CLEAR(tn);
1139         tn->telrcv_state = CURL_TS_SB;
1140         break;
1141       case CURL_IAC:
1142         tn->telrcv_state = CURL_TS_DATA;
1143         writebyte();
1144         break;
1145       case CURL_DM:
1146       case CURL_NOP:
1147       case CURL_GA:
1148       default:
1149         tn->telrcv_state = CURL_TS_DATA;
1150         printoption(data, "RCVD", CURL_IAC, c);
1151         break;
1152       }
1153       break;
1154 
1155       case CURL_TS_WILL:
1156         printoption(data, "RCVD", CURL_WILL, c);
1157         tn->please_negotiate = 1;
1158         rec_will(data, c);
1159         tn->telrcv_state = CURL_TS_DATA;
1160         break;
1161 
1162       case CURL_TS_WONT:
1163         printoption(data, "RCVD", CURL_WONT, c);
1164         tn->please_negotiate = 1;
1165         rec_wont(data, c);
1166         tn->telrcv_state = CURL_TS_DATA;
1167         break;
1168 
1169       case CURL_TS_DO:
1170         printoption(data, "RCVD", CURL_DO, c);
1171         tn->please_negotiate = 1;
1172         rec_do(data, c);
1173         tn->telrcv_state = CURL_TS_DATA;
1174         break;
1175 
1176       case CURL_TS_DONT:
1177         printoption(data, "RCVD", CURL_DONT, c);
1178         tn->please_negotiate = 1;
1179         rec_dont(data, c);
1180         tn->telrcv_state = CURL_TS_DATA;
1181         break;
1182 
1183       case CURL_TS_SB:
1184         if(c == CURL_IAC)
1185           tn->telrcv_state = CURL_TS_SE;
1186         else
1187           CURL_SB_ACCUM(tn, c);
1188         break;
1189 
1190       case CURL_TS_SE:
1191         if(c != CURL_SE) {
1192           if(c != CURL_IAC) {
1193             /*
1194              * This is an error.  We only expect to get "IAC IAC" or "IAC SE".
1195              * Several things may have happened.  An IAC was not doubled, the
1196              * IAC SE was left off, or another option got inserted into the
1197              * suboption are all possibilities.  If we assume that the IAC was
1198              * not doubled, and really the IAC SE was left off, we could get
1199              * into an infinite loop here.  So, instead, we terminate the
1200              * suboption, and process the partial suboption if we can.
1201              */
1202             CURL_SB_ACCUM(tn, CURL_IAC);
1203             CURL_SB_ACCUM(tn, c);
1204             tn->subpointer -= 2;
1205             CURL_SB_TERM(tn);
1206 
1207             printoption(data, "In SUBOPTION processing, RCVD", CURL_IAC, c);
1208             suboption(data);   /* handle sub-option */
1209             tn->telrcv_state = CURL_TS_IAC;
1210             goto process_iac;
1211           }
1212           CURL_SB_ACCUM(tn, c);
1213           tn->telrcv_state = CURL_TS_SB;
1214         }
1215         else {
1216           CURL_SB_ACCUM(tn, CURL_IAC);
1217           CURL_SB_ACCUM(tn, CURL_SE);
1218           tn->subpointer -= 2;
1219           CURL_SB_TERM(tn);
1220           suboption(data);   /* handle sub-option */
1221           tn->telrcv_state = CURL_TS_DATA;
1222         }
1223         break;
1224     }
1225     ++in;
1226   }
1227   bufferflush();
1228   return CURLE_OK;
1229 }
1230 
1231 /* Escape and send a telnet data block */
send_telnet_data(struct Curl_easy * data,char * buffer,ssize_t nread)1232 static CURLcode send_telnet_data(struct Curl_easy *data,
1233                                  char *buffer, ssize_t nread)
1234 {
1235   size_t i, outlen;
1236   unsigned char *outbuf;
1237   CURLcode result = CURLE_OK;
1238   size_t bytes_written;
1239   size_t total_written = 0;
1240   struct connectdata *conn = data->conn;
1241   struct TELNET *tn = data->req.p.telnet;
1242 
1243   DEBUGASSERT(tn);
1244   DEBUGASSERT(nread > 0);
1245   if(nread < 0)
1246     return CURLE_TOO_LARGE;
1247 
1248   if(memchr(buffer, CURL_IAC, nread)) {
1249     /* only use the escape buffer when necessary */
1250     Curl_dyn_reset(&tn->out);
1251 
1252     for(i = 0; i < (size_t)nread && !result; i++) {
1253       result = Curl_dyn_addn(&tn->out, &buffer[i], 1);
1254       if(!result && ((unsigned char)buffer[i] == CURL_IAC))
1255         /* IAC is FF in hex */
1256         result = Curl_dyn_addn(&tn->out, "\xff", 1);
1257     }
1258 
1259     outlen = Curl_dyn_len(&tn->out);
1260     outbuf = Curl_dyn_uptr(&tn->out);
1261   }
1262   else {
1263     outlen = (size_t)nread;
1264     outbuf = (unsigned char *)buffer;
1265   }
1266   while(!result && total_written < outlen) {
1267     /* Make sure socket is writable to avoid EWOULDBLOCK condition */
1268     struct pollfd pfd[1];
1269     pfd[0].fd = conn->sock[FIRSTSOCKET];
1270     pfd[0].events = POLLOUT;
1271     switch(Curl_poll(pfd, 1, -1)) {
1272       case -1:                    /* error, abort writing */
1273       case 0:                     /* timeout (will never happen) */
1274         result = CURLE_SEND_ERROR;
1275         break;
1276       default:                    /* write! */
1277         bytes_written = 0;
1278         result = Curl_xfer_send(data, outbuf + total_written,
1279                                 outlen - total_written, &bytes_written);
1280         total_written += bytes_written;
1281         break;
1282     }
1283   }
1284 
1285   return result;
1286 }
1287 
telnet_done(struct Curl_easy * data,CURLcode status,bool premature)1288 static CURLcode telnet_done(struct Curl_easy *data,
1289                             CURLcode status, bool premature)
1290 {
1291   struct TELNET *tn = data->req.p.telnet;
1292   (void)status; /* unused */
1293   (void)premature; /* not used */
1294 
1295   if(!tn)
1296     return CURLE_OK;
1297 
1298   curl_slist_free_all(tn->telnet_vars);
1299   tn->telnet_vars = NULL;
1300   Curl_dyn_free(&tn->out);
1301   return CURLE_OK;
1302 }
1303 
telnet_do(struct Curl_easy * data,bool * done)1304 static CURLcode telnet_do(struct Curl_easy *data, bool *done)
1305 {
1306   CURLcode result;
1307   struct connectdata *conn = data->conn;
1308   curl_socket_t sockfd = conn->sock[FIRSTSOCKET];
1309 #ifdef USE_WINSOCK
1310   WSAEVENT event_handle;
1311   WSANETWORKEVENTS events;
1312   HANDLE stdin_handle;
1313   HANDLE objs[2];
1314   DWORD  obj_count;
1315   DWORD  wait_timeout;
1316   DWORD readfile_read;
1317   int err;
1318 #else
1319   timediff_t interval_ms;
1320   struct pollfd pfd[2];
1321   int poll_cnt;
1322   curl_off_t total_dl = 0;
1323   curl_off_t total_ul = 0;
1324 #endif
1325   ssize_t nread;
1326   struct curltime now;
1327   bool keepon = TRUE;
1328   char buffer[4*1024];
1329   struct TELNET *tn;
1330 
1331   *done = TRUE; /* unconditionally */
1332 
1333   result = init_telnet(data);
1334   if(result)
1335     return result;
1336 
1337   tn = data->req.p.telnet;
1338 
1339   result = check_telnet_options(data);
1340   if(result)
1341     return result;
1342 
1343 #ifdef USE_WINSOCK
1344   /* We want to wait for both stdin and the socket. Since
1345   ** the select() function in winsock only works on sockets
1346   ** we have to use the WaitForMultipleObjects() call.
1347   */
1348 
1349   /* First, create a sockets event object */
1350   event_handle = WSACreateEvent();
1351   if(event_handle == WSA_INVALID_EVENT) {
1352     failf(data, "WSACreateEvent failed (%d)", SOCKERRNO);
1353     return CURLE_FAILED_INIT;
1354   }
1355 
1356   /* Tell winsock what events we want to listen to */
1357   if(WSAEventSelect(sockfd, event_handle, FD_READ|FD_CLOSE) == SOCKET_ERROR) {
1358     WSACloseEvent(event_handle);
1359     return CURLE_OK;
1360   }
1361 
1362   /* The get the Windows file handle for stdin */
1363   stdin_handle = GetStdHandle(STD_INPUT_HANDLE);
1364 
1365   /* Create the list of objects to wait for */
1366   objs[0] = event_handle;
1367   objs[1] = stdin_handle;
1368 
1369   /* If stdin_handle is a pipe, use PeekNamedPipe() method to check it,
1370      else use the old WaitForMultipleObjects() way */
1371   if(GetFileType(stdin_handle) == FILE_TYPE_PIPE ||
1372      data->set.is_fread_set) {
1373     /* Don't wait for stdin_handle, just wait for event_handle */
1374     obj_count = 1;
1375     /* Check stdin_handle per 100 milliseconds */
1376     wait_timeout = 100;
1377   }
1378   else {
1379     obj_count = 2;
1380     wait_timeout = 1000;
1381   }
1382 
1383   /* Keep on listening and act on events */
1384   while(keepon) {
1385     const DWORD buf_size = (DWORD)sizeof(buffer);
1386     DWORD waitret = WaitForMultipleObjects(obj_count, objs,
1387                                            FALSE, wait_timeout);
1388     switch(waitret) {
1389 
1390     case WAIT_TIMEOUT:
1391     {
1392       for(;;) {
1393         if(data->set.is_fread_set) {
1394           size_t n;
1395           /* read from user-supplied method */
1396           n = data->state.fread_func(buffer, 1, buf_size, data->state.in);
1397           if(n == CURL_READFUNC_ABORT) {
1398             keepon = FALSE;
1399             result = CURLE_READ_ERROR;
1400             break;
1401           }
1402 
1403           if(n == CURL_READFUNC_PAUSE)
1404             break;
1405 
1406           if(n == 0)                        /* no bytes */
1407             break;
1408 
1409           /* fall through with number of bytes read */
1410           readfile_read = (DWORD)n;
1411         }
1412         else {
1413           /* read from stdin */
1414           if(!PeekNamedPipe(stdin_handle, NULL, 0, NULL,
1415                             &readfile_read, NULL)) {
1416             keepon = FALSE;
1417             result = CURLE_READ_ERROR;
1418             break;
1419           }
1420 
1421           if(!readfile_read)
1422             break;
1423 
1424           if(!ReadFile(stdin_handle, buffer, buf_size,
1425                        &readfile_read, NULL)) {
1426             keepon = FALSE;
1427             result = CURLE_READ_ERROR;
1428             break;
1429           }
1430         }
1431 
1432         result = send_telnet_data(data, buffer, readfile_read);
1433         if(result) {
1434           keepon = FALSE;
1435           break;
1436         }
1437       }
1438     }
1439     break;
1440 
1441     case WAIT_OBJECT_0 + 1:
1442     {
1443       if(!ReadFile(stdin_handle, buffer, buf_size,
1444                    &readfile_read, NULL)) {
1445         keepon = FALSE;
1446         result = CURLE_READ_ERROR;
1447         break;
1448       }
1449 
1450       result = send_telnet_data(data, buffer, readfile_read);
1451       if(result) {
1452         keepon = FALSE;
1453         break;
1454       }
1455     }
1456     break;
1457 
1458     case WAIT_OBJECT_0:
1459     {
1460       events.lNetworkEvents = 0;
1461       if(WSAEnumNetworkEvents(sockfd, event_handle, &events) == SOCKET_ERROR) {
1462         err = SOCKERRNO;
1463         if(err != EINPROGRESS) {
1464           infof(data, "WSAEnumNetworkEvents failed (%d)", err);
1465           keepon = FALSE;
1466           result = CURLE_READ_ERROR;
1467         }
1468         break;
1469       }
1470       if(events.lNetworkEvents & FD_READ) {
1471         /* read data from network */
1472         result = Curl_xfer_recv(data, buffer, sizeof(buffer), &nread);
1473         /* read would've blocked. Loop again */
1474         if(result == CURLE_AGAIN)
1475           break;
1476         /* returned not-zero, this an error */
1477         else if(result) {
1478           keepon = FALSE;
1479           break;
1480         }
1481         /* returned zero but actually received 0 or less here,
1482            the server closed the connection and we bail out */
1483         else if(nread <= 0) {
1484           keepon = FALSE;
1485           break;
1486         }
1487 
1488         result = telrcv(data, (unsigned char *) buffer, nread);
1489         if(result) {
1490           keepon = FALSE;
1491           break;
1492         }
1493 
1494         /* Negotiate if the peer has started negotiating,
1495            otherwise don't. We don't want to speak telnet with
1496            non-telnet servers, like POP or SMTP. */
1497         if(tn->please_negotiate && !tn->already_negotiated) {
1498           negotiate(data);
1499           tn->already_negotiated = 1;
1500         }
1501       }
1502       if(events.lNetworkEvents & FD_CLOSE) {
1503         keepon = FALSE;
1504       }
1505     }
1506     break;
1507 
1508     }
1509 
1510     if(data->set.timeout) {
1511       now = Curl_now();
1512       if(Curl_timediff(now, conn->created) >= data->set.timeout) {
1513         failf(data, "Time-out");
1514         result = CURLE_OPERATION_TIMEDOUT;
1515         keepon = FALSE;
1516       }
1517     }
1518   }
1519 
1520   /* We called WSACreateEvent, so call WSACloseEvent */
1521   if(!WSACloseEvent(event_handle)) {
1522     infof(data, "WSACloseEvent failed (%d)", SOCKERRNO);
1523   }
1524 #else
1525   pfd[0].fd = sockfd;
1526   pfd[0].events = POLLIN;
1527 
1528   if(data->set.is_fread_set) {
1529     poll_cnt = 1;
1530     interval_ms = 100; /* poll user-supplied read function */
1531   }
1532   else {
1533     /* really using fread, so infile is a FILE* */
1534     pfd[1].fd = fileno((FILE *)data->state.in);
1535     pfd[1].events = POLLIN;
1536     poll_cnt = 2;
1537     interval_ms = 1 * 1000;
1538     if(pfd[1].fd < 0) {
1539       failf(data, "cannot read input");
1540       result = CURLE_RECV_ERROR;
1541       keepon = FALSE;
1542     }
1543   }
1544 
1545   while(keepon) {
1546     DEBUGF(infof(data, "telnet_do, poll %d fds", poll_cnt));
1547     switch(Curl_poll(pfd, (unsigned int)poll_cnt, interval_ms)) {
1548     case -1:                    /* error, stop reading */
1549       keepon = FALSE;
1550       continue;
1551     case 0:                     /* timeout */
1552       pfd[0].revents = 0;
1553       pfd[1].revents = 0;
1554       FALLTHROUGH();
1555     default:                    /* read! */
1556       if(pfd[0].revents & POLLIN) {
1557         /* read data from network */
1558         result = Curl_xfer_recv(data, buffer, sizeof(buffer), &nread);
1559         /* read would've blocked. Loop again */
1560         if(result == CURLE_AGAIN)
1561           break;
1562         /* returned not-zero, this an error */
1563         if(result) {
1564           keepon = FALSE;
1565           /* TODO: in test 1452, macOS sees a ECONNRESET sometimes?
1566            * Is this the telnet test server not shutting down the socket
1567            * in a clean way? Seems to be timing related, happens more
1568            * on slow debug build */
1569           if(data->state.os_errno == ECONNRESET) {
1570             DEBUGF(infof(data, "telnet_do, unexpected ECONNRESET on recv"));
1571           }
1572           break;
1573         }
1574         /* returned zero but actually received 0 or less here,
1575            the server closed the connection and we bail out */
1576         else if(nread <= 0) {
1577           keepon = FALSE;
1578           break;
1579         }
1580 
1581         total_dl += nread;
1582         result = Curl_pgrsSetDownloadCounter(data, total_dl);
1583         if(!result)
1584           result = telrcv(data, (unsigned char *)buffer, nread);
1585         if(result) {
1586           keepon = FALSE;
1587           break;
1588         }
1589 
1590         /* Negotiate if the peer has started negotiating,
1591            otherwise don't. We don't want to speak telnet with
1592            non-telnet servers, like POP or SMTP. */
1593         if(tn->please_negotiate && !tn->already_negotiated) {
1594           negotiate(data);
1595           tn->already_negotiated = 1;
1596         }
1597       }
1598 
1599       nread = 0;
1600       if(poll_cnt == 2) {
1601         if(pfd[1].revents & POLLIN) { /* read from in file */
1602           nread = read(pfd[1].fd, buffer, sizeof(buffer));
1603         }
1604       }
1605       else {
1606         /* read from user-supplied method */
1607         nread = (int)data->state.fread_func(buffer, 1, sizeof(buffer),
1608                                             data->state.in);
1609         if(nread == CURL_READFUNC_ABORT) {
1610           keepon = FALSE;
1611           break;
1612         }
1613         if(nread == CURL_READFUNC_PAUSE)
1614           break;
1615       }
1616 
1617       if(nread > 0) {
1618         result = send_telnet_data(data, buffer, nread);
1619         if(result) {
1620           keepon = FALSE;
1621           break;
1622         }
1623         total_ul += nread;
1624         Curl_pgrsSetUploadCounter(data, total_ul);
1625       }
1626       else if(nread < 0)
1627         keepon = FALSE;
1628 
1629       break;
1630     } /* poll switch statement */
1631 
1632     if(data->set.timeout) {
1633       now = Curl_now();
1634       if(Curl_timediff(now, conn->created) >= data->set.timeout) {
1635         failf(data, "Time-out");
1636         result = CURLE_OPERATION_TIMEDOUT;
1637         keepon = FALSE;
1638       }
1639     }
1640 
1641     if(Curl_pgrsUpdate(data)) {
1642       result = CURLE_ABORTED_BY_CALLBACK;
1643       break;
1644     }
1645   }
1646 #endif
1647   /* mark this as "no further transfer wanted" */
1648   Curl_xfer_setup_nop(data);
1649 
1650   return result;
1651 }
1652 #endif
1653