quickjs-tart

quickjs-based runtime for wallet-core logic
Log | Files | Refs | README | LICENSE

telnet.c (45756B)


      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 "url.h"
     51 #include <curl/curl.h>
     52 #include "transfer.h"
     53 #include "sendf.h"
     54 #include "telnet.h"
     55 #include "connect.h"
     56 #include "progress.h"
     57 #include "system_win32.h"
     58 #include "arpa_telnet.h"
     59 #include "select.h"
     60 #include "curlx/warnless.h"
     61 #include "curlx/strparse.h"
     62 
     63 /* The last 3 #include files should be in this order */
     64 #include "curl_printf.h"
     65 #include "curl_memory.h"
     66 #include "memdebug.h"
     67 
     68 #define SUBBUFSIZE 512
     69 
     70 #define CURL_SB_CLEAR(x)  x->subpointer = x->subbuffer
     71 #define CURL_SB_TERM(x)                                 \
     72   do {                                                  \
     73     x->subend = x->subpointer;                          \
     74     CURL_SB_CLEAR(x);                                   \
     75   } while(0)
     76 #define CURL_SB_ACCUM(x,c)                                      \
     77   do {                                                          \
     78     if(x->subpointer < (x->subbuffer + sizeof(x->subbuffer)))   \
     79       *x->subpointer++ = (c);                                   \
     80   } while(0)
     81 
     82 #define  CURL_SB_GET(x) ((*x->subpointer++)&0xff)
     83 #define  CURL_SB_LEN(x) (x->subend - x->subpointer)
     84 
     85 /* For posterity:
     86 #define  CURL_SB_PEEK(x) ((*x->subpointer)&0xff)
     87 #define  CURL_SB_EOF(x) (x->subpointer >= x->subend) */
     88 
     89 #ifdef CURL_DISABLE_VERBOSE_STRINGS
     90 #define printoption(a,b,c,d)  Curl_nop_stmt
     91 #endif
     92 
     93 /* For negotiation compliant to RFC 1143 */
     94 #define CURL_NO          0
     95 #define CURL_YES         1
     96 #define CURL_WANTYES     2
     97 #define CURL_WANTNO      3
     98 
     99 #define CURL_EMPTY       0
    100 #define CURL_OPPOSITE    1
    101 
    102 
    103 /* meta key for storing protocol meta at easy handle */
    104 #define CURL_META_TELNET_EASY   "meta:proto:telnet:easy"
    105 
    106 /*
    107  * Telnet receiver states for fsm
    108  */
    109 typedef enum
    110 {
    111    CURL_TS_DATA = 0,
    112    CURL_TS_IAC,
    113    CURL_TS_WILL,
    114    CURL_TS_WONT,
    115    CURL_TS_DO,
    116    CURL_TS_DONT,
    117    CURL_TS_CR,
    118    CURL_TS_SB,   /* sub-option collection */
    119    CURL_TS_SE   /* looking for sub-option end */
    120 } TelnetReceive;
    121 
    122 struct TELNET {
    123   int please_negotiate;
    124   int already_negotiated;
    125   int us[256];
    126   int usq[256];
    127   int us_preferred[256];
    128   int him[256];
    129   int himq[256];
    130   int him_preferred[256];
    131   int subnegotiation[256];
    132   char *subopt_ttype;                /* Set with suboption TTYPE */
    133   char *subopt_xdisploc;             /* Set with suboption XDISPLOC */
    134   unsigned short subopt_wsx;         /* Set with suboption NAWS */
    135   unsigned short subopt_wsy;         /* Set with suboption NAWS */
    136   TelnetReceive telrcv_state;
    137   struct curl_slist *telnet_vars;    /* Environment variables */
    138   struct dynbuf out;                 /* output buffer */
    139 
    140   /* suboptions */
    141   unsigned char subbuffer[SUBBUFSIZE];
    142   unsigned char *subpointer, *subend;      /* buffer for sub-options */
    143 };
    144 
    145 
    146 static
    147 CURLcode telrcv(struct Curl_easy *data,
    148                 struct TELNET *tn,
    149                 const unsigned char *inbuf, /* Data received from socket */
    150                 ssize_t count);             /* Number of bytes received */
    151 
    152 #ifndef CURL_DISABLE_VERBOSE_STRINGS
    153 static void printoption(struct Curl_easy *data,
    154                         const char *direction,
    155                         int cmd, int option);
    156 #endif
    157 
    158 static void send_negotiation(struct Curl_easy *data, int cmd, int option);
    159 static void set_local_option(struct Curl_easy *data, struct TELNET *tn,
    160                              int option, int newstate);
    161 static void set_remote_option(struct Curl_easy *data, struct TELNET *tn,
    162                               int option, int newstate);
    163 
    164 static void printsub(struct Curl_easy *data,
    165                      int direction, unsigned char *pointer,
    166                      size_t length);
    167 static void suboption(struct Curl_easy *data, struct TELNET *tn);
    168 static void sendsuboption(struct Curl_easy *data,
    169                           struct TELNET *tn, int option);
    170 
    171 static CURLcode telnet_do(struct Curl_easy *data, bool *done);
    172 static CURLcode telnet_done(struct Curl_easy *data,
    173                             CURLcode, bool premature);
    174 static CURLcode send_telnet_data(struct Curl_easy *data,
    175                                  struct TELNET *tn,
    176                                  char *buffer, ssize_t nread);
    177 
    178 /*
    179  * TELNET protocol handler.
    180  */
    181 
    182 const struct Curl_handler Curl_handler_telnet = {
    183   "telnet",                             /* scheme */
    184   ZERO_NULL,                            /* setup_connection */
    185   telnet_do,                            /* do_it */
    186   telnet_done,                          /* done */
    187   ZERO_NULL,                            /* do_more */
    188   ZERO_NULL,                            /* connect_it */
    189   ZERO_NULL,                            /* connecting */
    190   ZERO_NULL,                            /* doing */
    191   ZERO_NULL,                            /* proto_getsock */
    192   ZERO_NULL,                            /* doing_getsock */
    193   ZERO_NULL,                            /* domore_getsock */
    194   ZERO_NULL,                            /* perform_getsock */
    195   ZERO_NULL,                            /* disconnect */
    196   ZERO_NULL,                            /* write_resp */
    197   ZERO_NULL,                            /* write_resp_hd */
    198   ZERO_NULL,                            /* connection_check */
    199   ZERO_NULL,                            /* attach connection */
    200   ZERO_NULL,                            /* follow */
    201   PORT_TELNET,                          /* defport */
    202   CURLPROTO_TELNET,                     /* protocol */
    203   CURLPROTO_TELNET,                     /* family */
    204   PROTOPT_NONE | PROTOPT_NOURLQUERY     /* flags */
    205 };
    206 
    207 
    208 static void telnet_easy_dtor(void *key, size_t klen, void *entry)
    209 {
    210   struct TELNET *tn = entry;
    211   (void)key;
    212   (void)klen;
    213   curl_slist_free_all(tn->telnet_vars);
    214   curlx_dyn_free(&tn->out);
    215   free(tn);
    216 }
    217 
    218 static
    219 CURLcode init_telnet(struct Curl_easy *data)
    220 {
    221   struct TELNET *tn;
    222 
    223   tn = calloc(1, sizeof(struct TELNET));
    224   if(!tn)
    225     return CURLE_OUT_OF_MEMORY;
    226 
    227   curlx_dyn_init(&tn->out, 0xffff);
    228 
    229   tn->telrcv_state = CURL_TS_DATA;
    230 
    231   /* Init suboptions */
    232   CURL_SB_CLEAR(tn);
    233 
    234   /* Set the options we want by default */
    235   tn->us_preferred[CURL_TELOPT_SGA] = CURL_YES;
    236   tn->him_preferred[CURL_TELOPT_SGA] = CURL_YES;
    237 
    238   /* To be compliant with previous releases of libcurl
    239      we enable this option by default. This behavior
    240          can be changed thanks to the "BINARY" option in
    241          CURLOPT_TELNETOPTIONS
    242   */
    243   tn->us_preferred[CURL_TELOPT_BINARY] = CURL_YES;
    244   tn->him_preferred[CURL_TELOPT_BINARY] = CURL_YES;
    245 
    246   /* We must allow the server to echo what we sent
    247          but it is not necessary to request the server
    248          to do so (it might forces the server to close
    249          the connection). Hence, we ignore ECHO in the
    250          negotiate function
    251   */
    252   tn->him_preferred[CURL_TELOPT_ECHO] = CURL_YES;
    253 
    254   /* Set the subnegotiation fields to send information
    255     just after negotiation passed (do/will)
    256 
    257      Default values are (0,0) initialized by calloc.
    258      According to the RFC1013 it is valid:
    259      A value equal to zero is acceptable for the width (or height),
    260          and means that no character width (or height) is being sent.
    261          In this case, the width (or height) that will be assumed by the
    262          Telnet server is operating system specific (it will probably be
    263          based upon the terminal type information that may have been sent
    264          using the TERMINAL TYPE Telnet option). */
    265   tn->subnegotiation[CURL_TELOPT_NAWS] = CURL_YES;
    266 
    267   return Curl_meta_set(data, CURL_META_TELNET_EASY, tn, telnet_easy_dtor);
    268 }
    269 
    270 static void telnet_negotiate(struct Curl_easy *data, struct TELNET *tn)
    271 {
    272   int i;
    273 
    274   for(i = 0; i < CURL_NTELOPTS; i++) {
    275     if(i == CURL_TELOPT_ECHO)
    276       continue;
    277 
    278     if(tn->us_preferred[i] == CURL_YES)
    279       set_local_option(data, tn, i, CURL_YES);
    280 
    281     if(tn->him_preferred[i] == CURL_YES)
    282       set_remote_option(data, tn, i, CURL_YES);
    283   }
    284 }
    285 
    286 #ifndef CURL_DISABLE_VERBOSE_STRINGS
    287 static void printoption(struct Curl_easy *data,
    288                         const char *direction, int cmd, int option)
    289 {
    290   if(data->set.verbose) {
    291     if(cmd == CURL_IAC) {
    292       if(CURL_TELCMD_OK(option))
    293         infof(data, "%s IAC %s", direction, CURL_TELCMD(option));
    294       else
    295         infof(data, "%s IAC %d", direction, option);
    296     }
    297     else {
    298       const char *fmt = (cmd == CURL_WILL) ? "WILL" :
    299                         (cmd == CURL_WONT) ? "WONT" :
    300                         (cmd == CURL_DO) ? "DO" :
    301                         (cmd == CURL_DONT) ? "DONT" : 0;
    302       if(fmt) {
    303         const char *opt;
    304         if(CURL_TELOPT_OK(option))
    305           opt = CURL_TELOPT(option);
    306         else if(option == CURL_TELOPT_EXOPL)
    307           opt = "EXOPL";
    308         else
    309           opt = NULL;
    310 
    311         if(opt)
    312           infof(data, "%s %s %s", direction, fmt, opt);
    313         else
    314           infof(data, "%s %s %d", direction, fmt, option);
    315       }
    316       else
    317         infof(data, "%s %d %d", direction, cmd, option);
    318     }
    319   }
    320 }
    321 #endif
    322 
    323 static void send_negotiation(struct Curl_easy *data, int cmd, int option)
    324 {
    325   unsigned char buf[3];
    326   ssize_t bytes_written;
    327   struct connectdata *conn = data->conn;
    328 
    329   buf[0] = CURL_IAC;
    330   buf[1] = (unsigned char)cmd;
    331   buf[2] = (unsigned char)option;
    332 
    333   bytes_written = swrite(conn->sock[FIRSTSOCKET], buf, 3);
    334   if(bytes_written < 0) {
    335     int err = SOCKERRNO;
    336     failf(data,"Sending data failed (%d)",err);
    337   }
    338 
    339   printoption(data, "SENT", cmd, option);
    340 }
    341 
    342 static
    343 void set_remote_option(struct Curl_easy *data, struct TELNET *tn,
    344                        int option, int newstate)
    345 {
    346   if(newstate == CURL_YES) {
    347     switch(tn->him[option]) {
    348     case CURL_NO:
    349       tn->him[option] = CURL_WANTYES;
    350       send_negotiation(data, CURL_DO, option);
    351       break;
    352 
    353     case CURL_YES:
    354       /* Already enabled */
    355       break;
    356 
    357     case CURL_WANTNO:
    358       switch(tn->himq[option]) {
    359       case CURL_EMPTY:
    360         /* Already negotiating for CURL_YES, queue the request */
    361         tn->himq[option] = CURL_OPPOSITE;
    362         break;
    363       case CURL_OPPOSITE:
    364         /* Error: already queued an enable request */
    365         break;
    366       }
    367       break;
    368 
    369     case CURL_WANTYES:
    370       switch(tn->himq[option]) {
    371       case CURL_EMPTY:
    372         /* Error: already negotiating for enable */
    373         break;
    374       case CURL_OPPOSITE:
    375         tn->himq[option] = CURL_EMPTY;
    376         break;
    377       }
    378       break;
    379     }
    380   }
    381   else { /* NO */
    382     switch(tn->him[option]) {
    383     case CURL_NO:
    384       /* Already disabled */
    385       break;
    386 
    387     case CURL_YES:
    388       tn->him[option] = CURL_WANTNO;
    389       send_negotiation(data, CURL_DONT, option);
    390       break;
    391 
    392     case CURL_WANTNO:
    393       switch(tn->himq[option]) {
    394       case CURL_EMPTY:
    395         /* Already negotiating for NO */
    396         break;
    397       case CURL_OPPOSITE:
    398         tn->himq[option] = CURL_EMPTY;
    399         break;
    400       }
    401       break;
    402 
    403     case CURL_WANTYES:
    404       switch(tn->himq[option]) {
    405       case CURL_EMPTY:
    406         tn->himq[option] = CURL_OPPOSITE;
    407         break;
    408       case CURL_OPPOSITE:
    409         break;
    410       }
    411       break;
    412     }
    413   }
    414 }
    415 
    416 static
    417 void rec_will(struct Curl_easy *data, struct TELNET *tn, int option)
    418 {
    419   switch(tn->him[option]) {
    420   case CURL_NO:
    421     if(tn->him_preferred[option] == CURL_YES) {
    422       tn->him[option] = CURL_YES;
    423       send_negotiation(data, CURL_DO, option);
    424     }
    425     else
    426       send_negotiation(data, CURL_DONT, option);
    427 
    428     break;
    429 
    430   case CURL_YES:
    431     /* Already enabled */
    432     break;
    433 
    434   case CURL_WANTNO:
    435     switch(tn->himq[option]) {
    436     case CURL_EMPTY:
    437       /* Error: DONT answered by WILL */
    438       tn->him[option] = CURL_NO;
    439       break;
    440     case CURL_OPPOSITE:
    441       /* Error: DONT answered by WILL */
    442       tn->him[option] = CURL_YES;
    443       tn->himq[option] = CURL_EMPTY;
    444       break;
    445     }
    446     break;
    447 
    448   case CURL_WANTYES:
    449     switch(tn->himq[option]) {
    450     case CURL_EMPTY:
    451       tn->him[option] = CURL_YES;
    452       break;
    453     case CURL_OPPOSITE:
    454       tn->him[option] = CURL_WANTNO;
    455       tn->himq[option] = CURL_EMPTY;
    456       send_negotiation(data, CURL_DONT, option);
    457       break;
    458     }
    459     break;
    460   }
    461 }
    462 
    463 static
    464 void rec_wont(struct Curl_easy *data, struct TELNET *tn, int option)
    465 {
    466   switch(tn->him[option]) {
    467   case CURL_NO:
    468     /* Already disabled */
    469     break;
    470 
    471   case CURL_YES:
    472     tn->him[option] = CURL_NO;
    473     send_negotiation(data, CURL_DONT, option);
    474     break;
    475 
    476   case CURL_WANTNO:
    477     switch(tn->himq[option]) {
    478     case CURL_EMPTY:
    479       tn->him[option] = CURL_NO;
    480       break;
    481 
    482     case CURL_OPPOSITE:
    483       tn->him[option] = CURL_WANTYES;
    484       tn->himq[option] = CURL_EMPTY;
    485       send_negotiation(data, CURL_DO, option);
    486       break;
    487     }
    488     break;
    489 
    490   case CURL_WANTYES:
    491     switch(tn->himq[option]) {
    492     case CURL_EMPTY:
    493       tn->him[option] = CURL_NO;
    494       break;
    495     case CURL_OPPOSITE:
    496       tn->him[option] = CURL_NO;
    497       tn->himq[option] = CURL_EMPTY;
    498       break;
    499     }
    500     break;
    501   }
    502 }
    503 
    504 static void
    505 set_local_option(struct Curl_easy *data, struct TELNET *tn,
    506                  int option, int newstate)
    507 {
    508   if(newstate == CURL_YES) {
    509     switch(tn->us[option]) {
    510     case CURL_NO:
    511       tn->us[option] = CURL_WANTYES;
    512       send_negotiation(data, CURL_WILL, option);
    513       break;
    514 
    515     case CURL_YES:
    516       /* Already enabled */
    517       break;
    518 
    519     case CURL_WANTNO:
    520       switch(tn->usq[option]) {
    521       case CURL_EMPTY:
    522         /* Already negotiating for CURL_YES, queue the request */
    523         tn->usq[option] = CURL_OPPOSITE;
    524         break;
    525       case CURL_OPPOSITE:
    526         /* Error: already queued an enable request */
    527         break;
    528       }
    529       break;
    530 
    531     case CURL_WANTYES:
    532       switch(tn->usq[option]) {
    533       case CURL_EMPTY:
    534         /* Error: already negotiating for enable */
    535         break;
    536       case CURL_OPPOSITE:
    537         tn->usq[option] = CURL_EMPTY;
    538         break;
    539       }
    540       break;
    541     }
    542   }
    543   else { /* NO */
    544     switch(tn->us[option]) {
    545     case CURL_NO:
    546       /* Already disabled */
    547       break;
    548 
    549     case CURL_YES:
    550       tn->us[option] = CURL_WANTNO;
    551       send_negotiation(data, CURL_WONT, option);
    552       break;
    553 
    554     case CURL_WANTNO:
    555       switch(tn->usq[option]) {
    556       case CURL_EMPTY:
    557         /* Already negotiating for NO */
    558         break;
    559       case CURL_OPPOSITE:
    560         tn->usq[option] = CURL_EMPTY;
    561         break;
    562       }
    563       break;
    564 
    565     case CURL_WANTYES:
    566       switch(tn->usq[option]) {
    567       case CURL_EMPTY:
    568         tn->usq[option] = CURL_OPPOSITE;
    569         break;
    570       case CURL_OPPOSITE:
    571         break;
    572       }
    573       break;
    574     }
    575   }
    576 }
    577 
    578 static
    579 void rec_do(struct Curl_easy *data, struct TELNET *tn, int option)
    580 {
    581   switch(tn->us[option]) {
    582   case CURL_NO:
    583     if(tn->us_preferred[option] == CURL_YES) {
    584       tn->us[option] = CURL_YES;
    585       send_negotiation(data, CURL_WILL, option);
    586       if(tn->subnegotiation[option] == CURL_YES)
    587         /* transmission of data option */
    588         sendsuboption(data, tn, option);
    589     }
    590     else if(tn->subnegotiation[option] == CURL_YES) {
    591       /* send information to achieve this option */
    592       tn->us[option] = CURL_YES;
    593       send_negotiation(data, CURL_WILL, option);
    594       sendsuboption(data, tn, option);
    595     }
    596     else
    597       send_negotiation(data, CURL_WONT, option);
    598     break;
    599 
    600   case CURL_YES:
    601     /* Already enabled */
    602     break;
    603 
    604   case CURL_WANTNO:
    605     switch(tn->usq[option]) {
    606     case CURL_EMPTY:
    607       /* Error: DONT answered by WILL */
    608       tn->us[option] = CURL_NO;
    609       break;
    610     case CURL_OPPOSITE:
    611       /* Error: DONT answered by WILL */
    612       tn->us[option] = CURL_YES;
    613       tn->usq[option] = CURL_EMPTY;
    614       break;
    615     }
    616     break;
    617 
    618   case CURL_WANTYES:
    619     switch(tn->usq[option]) {
    620     case CURL_EMPTY:
    621       tn->us[option] = CURL_YES;
    622       if(tn->subnegotiation[option] == CURL_YES) {
    623         /* transmission of data option */
    624         sendsuboption(data, tn, option);
    625       }
    626       break;
    627     case CURL_OPPOSITE:
    628       tn->us[option] = CURL_WANTNO;
    629       tn->himq[option] = CURL_EMPTY;
    630       send_negotiation(data, CURL_WONT, option);
    631       break;
    632     }
    633     break;
    634   }
    635 }
    636 
    637 static
    638 void rec_dont(struct Curl_easy *data, struct TELNET *tn, int option)
    639 {
    640   switch(tn->us[option]) {
    641   case CURL_NO:
    642     /* Already disabled */
    643     break;
    644 
    645   case CURL_YES:
    646     tn->us[option] = CURL_NO;
    647     send_negotiation(data, CURL_WONT, option);
    648     break;
    649 
    650   case CURL_WANTNO:
    651     switch(tn->usq[option]) {
    652     case CURL_EMPTY:
    653       tn->us[option] = CURL_NO;
    654       break;
    655 
    656     case CURL_OPPOSITE:
    657       tn->us[option] = CURL_WANTYES;
    658       tn->usq[option] = CURL_EMPTY;
    659       send_negotiation(data, CURL_WILL, option);
    660       break;
    661     }
    662     break;
    663 
    664   case CURL_WANTYES:
    665     switch(tn->usq[option]) {
    666     case CURL_EMPTY:
    667       tn->us[option] = CURL_NO;
    668       break;
    669     case CURL_OPPOSITE:
    670       tn->us[option] = CURL_NO;
    671       tn->usq[option] = CURL_EMPTY;
    672       break;
    673     }
    674     break;
    675   }
    676 }
    677 
    678 
    679 static void printsub(struct Curl_easy *data,
    680                      int direction,             /* '<' or '>' */
    681                      unsigned char *pointer,    /* where suboption data is */
    682                      size_t length)             /* length of suboption data */
    683 {
    684   if(data->set.verbose) {
    685     unsigned int i = 0;
    686     if(direction) {
    687       infof(data, "%s IAC SB ", (direction == '<') ? "RCVD" : "SENT");
    688       if(length >= 3) {
    689         int j;
    690 
    691         i = pointer[length-2];
    692         j = pointer[length-1];
    693 
    694         if(i != CURL_IAC || j != CURL_SE) {
    695           infof(data, "(terminated by ");
    696           if(CURL_TELOPT_OK(i))
    697             infof(data, "%s ", CURL_TELOPT(i));
    698           else if(CURL_TELCMD_OK(i))
    699             infof(data, "%s ", CURL_TELCMD(i));
    700           else
    701             infof(data, "%u ", i);
    702           if(CURL_TELOPT_OK(j))
    703             infof(data, "%s", CURL_TELOPT(j));
    704           else if(CURL_TELCMD_OK(j))
    705             infof(data, "%s", CURL_TELCMD(j));
    706           else
    707             infof(data, "%d", j);
    708           infof(data, ", not IAC SE) ");
    709         }
    710       }
    711       if(length >= 2)
    712         length -= 2;
    713       else /* bad input */
    714         return;
    715     }
    716     if(length < 1) {
    717       infof(data, "(Empty suboption?)");
    718       return;
    719     }
    720 
    721     if(CURL_TELOPT_OK(pointer[0])) {
    722       switch(pointer[0]) {
    723       case CURL_TELOPT_TTYPE:
    724       case CURL_TELOPT_XDISPLOC:
    725       case CURL_TELOPT_NEW_ENVIRON:
    726       case CURL_TELOPT_NAWS:
    727         infof(data, "%s", CURL_TELOPT(pointer[0]));
    728         break;
    729       default:
    730         infof(data, "%s (unsupported)", CURL_TELOPT(pointer[0]));
    731         break;
    732       }
    733     }
    734     else
    735       infof(data, "%d (unknown)", pointer[i]);
    736 
    737     switch(pointer[0]) {
    738     case CURL_TELOPT_NAWS:
    739       if(length > 4)
    740         infof(data, "Width: %d ; Height: %d", (pointer[1] << 8) | pointer[2],
    741               (pointer[3] << 8) | pointer[4]);
    742       break;
    743     default:
    744       switch(pointer[1]) {
    745       case CURL_TELQUAL_IS:
    746         infof(data, " IS");
    747         break;
    748       case CURL_TELQUAL_SEND:
    749         infof(data, " SEND");
    750         break;
    751       case CURL_TELQUAL_INFO:
    752         infof(data, " INFO/REPLY");
    753         break;
    754       case CURL_TELQUAL_NAME:
    755         infof(data, " NAME");
    756         break;
    757       }
    758 
    759       switch(pointer[0]) {
    760       case CURL_TELOPT_TTYPE:
    761       case CURL_TELOPT_XDISPLOC:
    762         pointer[length] = 0;
    763         infof(data, " \"%s\"", &pointer[2]);
    764         break;
    765       case CURL_TELOPT_NEW_ENVIRON:
    766         if(pointer[1] == CURL_TELQUAL_IS) {
    767           infof(data, " ");
    768           for(i = 3; i < length; i++) {
    769             switch(pointer[i]) {
    770             case CURL_NEW_ENV_VAR:
    771               infof(data, ", ");
    772               break;
    773             case CURL_NEW_ENV_VALUE:
    774               infof(data, " = ");
    775               break;
    776             default:
    777               infof(data, "%c", pointer[i]);
    778               break;
    779             }
    780           }
    781         }
    782         break;
    783       default:
    784         for(i = 2; i < length; i++)
    785           infof(data, " %.2x", pointer[i]);
    786         break;
    787       }
    788     }
    789   }
    790 }
    791 
    792 static bool str_is_nonascii(const char *str)
    793 {
    794   char c;
    795   while((c = *str++) != 0)
    796     if(c & 0x80)
    797       return TRUE;
    798 
    799   return FALSE;
    800 }
    801 
    802 static CURLcode check_telnet_options(struct Curl_easy *data,
    803                                      struct TELNET *tn)
    804 {
    805   struct curl_slist *head;
    806   struct curl_slist *beg;
    807   CURLcode result = CURLE_OK;
    808 
    809   /* Add the username as an environment variable if it
    810      was given on the command line */
    811   if(data->state.aptr.user) {
    812     char buffer[256];
    813     if(str_is_nonascii(data->conn->user)) {
    814       DEBUGF(infof(data, "set a non ASCII username in telnet"));
    815       return CURLE_BAD_FUNCTION_ARGUMENT;
    816     }
    817     msnprintf(buffer, sizeof(buffer), "USER,%s", data->conn->user);
    818     beg = curl_slist_append(tn->telnet_vars, buffer);
    819     if(!beg) {
    820       curl_slist_free_all(tn->telnet_vars);
    821       tn->telnet_vars = NULL;
    822       return CURLE_OUT_OF_MEMORY;
    823     }
    824     tn->telnet_vars = beg;
    825     tn->us_preferred[CURL_TELOPT_NEW_ENVIRON] = CURL_YES;
    826   }
    827 
    828   for(head = data->set.telnet_options; head && !result; head = head->next) {
    829     size_t olen;
    830     char *option = head->data;
    831     char *arg;
    832     char *sep = strchr(option, '=');
    833     if(sep) {
    834       olen = sep - option;
    835       arg = ++sep;
    836       if(str_is_nonascii(arg))
    837         continue;
    838       switch(olen) {
    839       case 5:
    840         /* Terminal type */
    841         if(curl_strnequal(option, "TTYPE", 5)) {
    842           tn->subopt_ttype = arg;
    843           tn->us_preferred[CURL_TELOPT_TTYPE] = CURL_YES;
    844           break;
    845         }
    846         result = CURLE_UNKNOWN_OPTION;
    847         break;
    848 
    849       case 8:
    850         /* Display variable */
    851         if(curl_strnequal(option, "XDISPLOC", 8)) {
    852           tn->subopt_xdisploc = arg;
    853           tn->us_preferred[CURL_TELOPT_XDISPLOC] = CURL_YES;
    854           break;
    855         }
    856         result = CURLE_UNKNOWN_OPTION;
    857         break;
    858 
    859       case 7:
    860         /* Environment variable */
    861         if(curl_strnequal(option, "NEW_ENV", 7)) {
    862           beg = curl_slist_append(tn->telnet_vars, arg);
    863           if(!beg) {
    864             result = CURLE_OUT_OF_MEMORY;
    865             break;
    866           }
    867           tn->telnet_vars = beg;
    868           tn->us_preferred[CURL_TELOPT_NEW_ENVIRON] = CURL_YES;
    869         }
    870         else
    871           result = CURLE_UNKNOWN_OPTION;
    872         break;
    873 
    874       case 2:
    875         /* Window Size */
    876         if(curl_strnequal(option, "WS", 2)) {
    877           const char *p = arg;
    878           curl_off_t x = 0;
    879           curl_off_t y = 0;
    880           if(curlx_str_number(&p, &x, 0xffff) ||
    881              curlx_str_single(&p, 'x') ||
    882              curlx_str_number(&p, &y, 0xffff)) {
    883             failf(data, "Syntax error in telnet option: %s", head->data);
    884             result = CURLE_SETOPT_OPTION_SYNTAX;
    885           }
    886           else {
    887             tn->subopt_wsx = (unsigned short)x;
    888             tn->subopt_wsy = (unsigned short)y;
    889             tn->us_preferred[CURL_TELOPT_NAWS] = CURL_YES;
    890           }
    891         }
    892         else
    893           result = CURLE_UNKNOWN_OPTION;
    894         break;
    895 
    896       case 6:
    897         /* To take care or not of the 8th bit in data exchange */
    898         if(curl_strnequal(option, "BINARY", 6)) {
    899           int binary_option = atoi(arg);
    900           if(binary_option != 1) {
    901             tn->us_preferred[CURL_TELOPT_BINARY] = CURL_NO;
    902             tn->him_preferred[CURL_TELOPT_BINARY] = CURL_NO;
    903           }
    904         }
    905         else
    906           result = CURLE_UNKNOWN_OPTION;
    907         break;
    908       default:
    909         failf(data, "Unknown telnet option %s", head->data);
    910         result = CURLE_UNKNOWN_OPTION;
    911         break;
    912       }
    913     }
    914     else {
    915       failf(data, "Syntax error in telnet option: %s", head->data);
    916       result = CURLE_SETOPT_OPTION_SYNTAX;
    917     }
    918   }
    919 
    920   if(result) {
    921     curl_slist_free_all(tn->telnet_vars);
    922     tn->telnet_vars = NULL;
    923   }
    924 
    925   return result;
    926 }
    927 
    928 /*
    929  * suboption()
    930  *
    931  * Look at the sub-option buffer, and try to be helpful to the other
    932  * side.
    933  */
    934 
    935 static void suboption(struct Curl_easy *data, struct TELNET *tn)
    936 {
    937   struct curl_slist *v;
    938   unsigned char temp[2048];
    939   ssize_t bytes_written;
    940   size_t len;
    941   int err;
    942   struct 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 
   1013 static void sendsuboption(struct Curl_easy *data,
   1014                           struct TELNET *tn, int option)
   1015 {
   1016   ssize_t bytes_written;
   1017   int err;
   1018   unsigned short x, y;
   1019   unsigned char *uc1, *uc2;
   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, tn, (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 
   1068 static
   1069 CURLcode telrcv(struct Curl_easy *data,
   1070                 struct TELNET *tn,
   1071                 const unsigned char *inbuf, /* Data received from socket */
   1072                 ssize_t count)              /* Number of bytes received */
   1073 {
   1074   unsigned char c;
   1075   CURLcode result;
   1076   int in = 0;
   1077   int startwrite = -1;
   1078 
   1079 #define startskipping()                                          \
   1080   if(startwrite >= 0) {                                          \
   1081     result = Curl_client_write(data,                             \
   1082                                CLIENTWRITE_BODY,                 \
   1083                                (const 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:
   1121 process_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, tn, 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, tn, 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, tn, 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, tn, 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, tn);   /* 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, tn);   /* 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 */
   1231 static CURLcode send_telnet_data(struct Curl_easy *data,
   1232                                  struct TELNET *tn,
   1233                                  char *buffer, ssize_t nread)
   1234 {
   1235   size_t i, outlen;
   1236   unsigned char *outbuf;
   1237   CURLcode result = CURLE_OK;
   1238   size_t bytes_written;
   1239   size_t total_written = 0;
   1240   struct connectdata *conn = data->conn;
   1241 
   1242   DEBUGASSERT(tn);
   1243   DEBUGASSERT(nread > 0);
   1244   if(nread < 0)
   1245     return CURLE_TOO_LARGE;
   1246 
   1247   if(memchr(buffer, CURL_IAC, nread)) {
   1248     /* only use the escape buffer when necessary */
   1249     curlx_dyn_reset(&tn->out);
   1250 
   1251     for(i = 0; i < (size_t)nread && !result; i++) {
   1252       result = curlx_dyn_addn(&tn->out, &buffer[i], 1);
   1253       if(!result && ((unsigned char)buffer[i] == CURL_IAC))
   1254         /* IAC is FF in hex */
   1255         result = curlx_dyn_addn(&tn->out, "\xff", 1);
   1256     }
   1257 
   1258     outlen = curlx_dyn_len(&tn->out);
   1259     outbuf = curlx_dyn_uptr(&tn->out);
   1260   }
   1261   else {
   1262     outlen = (size_t)nread;
   1263     outbuf = (unsigned char *)buffer;
   1264   }
   1265   while(!result && total_written < outlen) {
   1266     /* Make sure socket is writable to avoid EWOULDBLOCK condition */
   1267     struct pollfd pfd[1];
   1268     pfd[0].fd = conn->sock[FIRSTSOCKET];
   1269     pfd[0].events = POLLOUT;
   1270     switch(Curl_poll(pfd, 1, -1)) {
   1271       case -1:                    /* error, abort writing */
   1272       case 0:                     /* timeout (will never happen) */
   1273         result = CURLE_SEND_ERROR;
   1274         break;
   1275       default:                    /* write! */
   1276         bytes_written = 0;
   1277         result = Curl_xfer_send(data, outbuf + total_written,
   1278                                 outlen - total_written, FALSE, &bytes_written);
   1279         total_written += bytes_written;
   1280         break;
   1281     }
   1282   }
   1283 
   1284   return result;
   1285 }
   1286 
   1287 static CURLcode telnet_done(struct Curl_easy *data,
   1288                             CURLcode status, bool premature)
   1289 {
   1290   (void)status; /* unused */
   1291   (void)premature; /* not used */
   1292   Curl_meta_remove(data, CURL_META_TELNET_EASY);
   1293   return CURLE_OK;
   1294 }
   1295 
   1296 static CURLcode telnet_do(struct Curl_easy *data, bool *done)
   1297 {
   1298   CURLcode result;
   1299   struct connectdata *conn = data->conn;
   1300   curl_socket_t sockfd = conn->sock[FIRSTSOCKET];
   1301 #ifdef USE_WINSOCK
   1302   WSAEVENT event_handle;
   1303   WSANETWORKEVENTS events;
   1304   HANDLE stdin_handle;
   1305   HANDLE objs[2];
   1306   DWORD  obj_count;
   1307   DWORD  wait_timeout;
   1308   DWORD readfile_read;
   1309   int err;
   1310 #else
   1311   timediff_t interval_ms;
   1312   struct pollfd pfd[2];
   1313   int poll_cnt;
   1314   curl_off_t total_dl = 0;
   1315   curl_off_t total_ul = 0;
   1316   ssize_t snread;
   1317 #endif
   1318   struct curltime now;
   1319   bool keepon = TRUE;
   1320   char buffer[4*1024];
   1321   struct TELNET *tn;
   1322 
   1323   *done = TRUE; /* unconditionally */
   1324 
   1325   result = init_telnet(data);
   1326   if(result)
   1327     return result;
   1328 
   1329   tn = Curl_meta_get(data, CURL_META_TELNET_EASY);
   1330   if(!tn)
   1331     return CURLE_FAILED_INIT;
   1332 
   1333   result = check_telnet_options(data, tn);
   1334   if(result)
   1335     return result;
   1336 
   1337 #ifdef USE_WINSOCK
   1338   /* We want to wait for both stdin and the socket. Since
   1339   ** the select() function in Winsock only works on sockets
   1340   ** we have to use the WaitForMultipleObjects() call.
   1341   */
   1342 
   1343   /* First, create a sockets event object */
   1344   event_handle = WSACreateEvent();
   1345   if(event_handle == WSA_INVALID_EVENT) {
   1346     failf(data, "WSACreateEvent failed (%d)", SOCKERRNO);
   1347     return CURLE_FAILED_INIT;
   1348   }
   1349 
   1350   /* Tell Winsock what events we want to listen to */
   1351   if(WSAEventSelect(sockfd, event_handle, FD_READ|FD_CLOSE) == SOCKET_ERROR) {
   1352     WSACloseEvent(event_handle);
   1353     return CURLE_OK;
   1354   }
   1355 
   1356   /* The get the Windows file handle for stdin */
   1357   stdin_handle = GetStdHandle(STD_INPUT_HANDLE);
   1358 
   1359   /* Create the list of objects to wait for */
   1360   objs[0] = event_handle;
   1361   objs[1] = stdin_handle;
   1362 
   1363   /* If stdin_handle is a pipe, use PeekNamedPipe() method to check it,
   1364      else use the old WaitForMultipleObjects() way */
   1365   if(GetFileType(stdin_handle) == FILE_TYPE_PIPE ||
   1366      data->set.is_fread_set) {
   1367     /* Do not wait for stdin_handle, just wait for event_handle */
   1368     obj_count = 1;
   1369     /* Check stdin_handle per 100 milliseconds */
   1370     wait_timeout = 100;
   1371   }
   1372   else {
   1373     obj_count = 2;
   1374     wait_timeout = 1000;
   1375   }
   1376 
   1377   /* Keep on listening and act on events */
   1378   while(keepon) {
   1379     const DWORD buf_size = (DWORD)sizeof(buffer);
   1380     DWORD waitret = WaitForMultipleObjects(obj_count, objs,
   1381                                            FALSE, wait_timeout);
   1382     switch(waitret) {
   1383 
   1384     case WAIT_TIMEOUT:
   1385     {
   1386       for(;;) {
   1387         if(data->set.is_fread_set) {
   1388           size_t n;
   1389           /* read from user-supplied method */
   1390           n = data->state.fread_func(buffer, 1, buf_size, data->state.in);
   1391           if(n == CURL_READFUNC_ABORT) {
   1392             keepon = FALSE;
   1393             result = CURLE_READ_ERROR;
   1394             break;
   1395           }
   1396 
   1397           if(n == CURL_READFUNC_PAUSE)
   1398             break;
   1399 
   1400           if(n == 0)                        /* no bytes */
   1401             break;
   1402 
   1403           /* fall through with number of bytes read */
   1404           readfile_read = (DWORD)n;
   1405         }
   1406         else {
   1407           /* read from stdin */
   1408           if(!PeekNamedPipe(stdin_handle, NULL, 0, NULL,
   1409                             &readfile_read, NULL)) {
   1410             keepon = FALSE;
   1411             result = CURLE_READ_ERROR;
   1412             break;
   1413           }
   1414 
   1415           if(!readfile_read)
   1416             break;
   1417 
   1418           if(!ReadFile(stdin_handle, buffer, buf_size,
   1419                        &readfile_read, NULL)) {
   1420             keepon = FALSE;
   1421             result = CURLE_READ_ERROR;
   1422             break;
   1423           }
   1424         }
   1425 
   1426         result = send_telnet_data(data, tn, buffer, readfile_read);
   1427         if(result) {
   1428           keepon = FALSE;
   1429           break;
   1430         }
   1431       }
   1432     }
   1433     break;
   1434 
   1435     case WAIT_OBJECT_0 + 1:
   1436     {
   1437       if(!ReadFile(stdin_handle, buffer, buf_size,
   1438                    &readfile_read, NULL)) {
   1439         keepon = FALSE;
   1440         result = CURLE_READ_ERROR;
   1441         break;
   1442       }
   1443 
   1444       result = send_telnet_data(data, tn, buffer, readfile_read);
   1445       if(result) {
   1446         keepon = FALSE;
   1447         break;
   1448       }
   1449     }
   1450     break;
   1451 
   1452     case WAIT_OBJECT_0:
   1453     {
   1454       events.lNetworkEvents = 0;
   1455       if(WSAEnumNetworkEvents(sockfd, event_handle, &events) == SOCKET_ERROR) {
   1456         err = SOCKERRNO;
   1457         if(err != SOCKEINPROGRESS) {
   1458           infof(data, "WSAEnumNetworkEvents failed (%d)", err);
   1459           keepon = FALSE;
   1460           result = CURLE_READ_ERROR;
   1461         }
   1462         break;
   1463       }
   1464       if(events.lNetworkEvents & FD_READ) {
   1465         /* read data from network */
   1466         size_t nread;
   1467         result = Curl_xfer_recv(data, buffer, sizeof(buffer), &nread);
   1468         /* read would have 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) {
   1479           keepon = FALSE;
   1480           break;
   1481         }
   1482 
   1483         result = telrcv(data, tn, (unsigned char *) buffer, nread);
   1484         if(result) {
   1485           keepon = FALSE;
   1486           break;
   1487         }
   1488 
   1489         /* Negotiate if the peer has started negotiating,
   1490            otherwise do not. We do not want to speak telnet with
   1491            non-telnet servers, like POP or SMTP. */
   1492         if(tn->please_negotiate && !tn->already_negotiated) {
   1493           telnet_negotiate(data, tn);
   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 = curlx_now();
   1507       if(curlx_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     if(pfd[1].fd < 0) {
   1534       failf(data, "cannot read input");
   1535       result = CURLE_RECV_ERROR;
   1536       keepon = FALSE;
   1537     }
   1538   }
   1539 
   1540   while(keepon) {
   1541     DEBUGF(infof(data, "telnet_do, poll %d fds", poll_cnt));
   1542     switch(Curl_poll(pfd, (unsigned int)poll_cnt, interval_ms)) {
   1543     case -1:                    /* error, stop reading */
   1544       keepon = FALSE;
   1545       continue;
   1546     case 0:                     /* timeout */
   1547       pfd[0].revents = 0;
   1548       pfd[1].revents = 0;
   1549       FALLTHROUGH();
   1550     default:                    /* read! */
   1551       if(pfd[0].revents & POLLIN) {
   1552         /* read data from network */
   1553         size_t nread;
   1554         result = Curl_xfer_recv(data, buffer, sizeof(buffer), &nread);
   1555         /* read would have blocked. Loop again */
   1556         if(result == CURLE_AGAIN)
   1557           break;
   1558         /* returned not-zero, this an error */
   1559         if(result) {
   1560           keepon = FALSE;
   1561           /* In test 1452, macOS sees a ECONNRESET sometimes? Is this the
   1562            * telnet test server not shutting down the socket in a clean way?
   1563            * Seems to be timing related, happens more on slow debug build */
   1564           if(data->state.os_errno == SOCKECONNRESET) {
   1565             DEBUGF(infof(data, "telnet_do, unexpected ECONNRESET on recv"));
   1566           }
   1567           break;
   1568         }
   1569         /* returned zero but actually received 0 or less here,
   1570            the server closed the connection and we bail out */
   1571         else if(!nread) {
   1572           keepon = FALSE;
   1573           break;
   1574         }
   1575 
   1576         total_dl += nread;
   1577         result = Curl_pgrsSetDownloadCounter(data, total_dl);
   1578         if(!result)
   1579           result = telrcv(data, tn, (unsigned char *)buffer, nread);
   1580         if(result) {
   1581           keepon = FALSE;
   1582           break;
   1583         }
   1584 
   1585         /* Negotiate if the peer has started negotiating,
   1586            otherwise do not. We do not want to speak telnet with
   1587            non-telnet servers, like POP or SMTP. */
   1588         if(tn->please_negotiate && !tn->already_negotiated) {
   1589           telnet_negotiate(data, tn);
   1590           tn->already_negotiated = 1;
   1591         }
   1592       }
   1593 
   1594       snread = 0;
   1595       if(poll_cnt == 2) {
   1596         if(pfd[1].revents & POLLIN) { /* read from in file */
   1597           snread = read(pfd[1].fd, buffer, sizeof(buffer));
   1598         }
   1599       }
   1600       else {
   1601         /* read from user-supplied method */
   1602         snread = (int)data->state.fread_func(buffer, 1, sizeof(buffer),
   1603                                             data->state.in);
   1604         if(snread == CURL_READFUNC_ABORT) {
   1605           keepon = FALSE;
   1606           break;
   1607         }
   1608         if(snread == CURL_READFUNC_PAUSE)
   1609           break;
   1610       }
   1611 
   1612       if(snread > 0) {
   1613         result = send_telnet_data(data, tn, buffer, snread);
   1614         if(result) {
   1615           keepon = FALSE;
   1616           break;
   1617         }
   1618         total_ul += snread;
   1619         Curl_pgrsSetUploadCounter(data, total_ul);
   1620       }
   1621       else if(snread < 0)
   1622         keepon = FALSE;
   1623 
   1624       break;
   1625     } /* poll switch statement */
   1626 
   1627     if(data->set.timeout) {
   1628       now = curlx_now();
   1629       if(curlx_timediff(now, conn->created) >= data->set.timeout) {
   1630         failf(data, "Time-out");
   1631         result = CURLE_OPERATION_TIMEDOUT;
   1632         keepon = FALSE;
   1633       }
   1634     }
   1635 
   1636     if(Curl_pgrsUpdate(data)) {
   1637       result = CURLE_ABORTED_BY_CALLBACK;
   1638       break;
   1639     }
   1640   }
   1641 #endif
   1642   /* mark this as "no further transfer wanted" */
   1643   Curl_xfer_setup_nop(data);
   1644 
   1645   return result;
   1646 }
   1647 #endif