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