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