Annotation of embedaddon/curl/lib/telnet.c, revision 1.1

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

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>