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