quickjs-tart

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

socks.c (38048B)


      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 #if !defined(CURL_DISABLE_PROXY)
     28 
     29 #ifdef HAVE_NETINET_IN_H
     30 #include <netinet/in.h>
     31 #endif
     32 #ifdef HAVE_ARPA_INET_H
     33 #include <arpa/inet.h>
     34 #endif
     35 
     36 #include "urldata.h"
     37 #include "sendf.h"
     38 #include "select.h"
     39 #include "cfilters.h"
     40 #include "connect.h"
     41 #include "curlx/timeval.h"
     42 #include "socks.h"
     43 #include "multiif.h" /* for getsock macros */
     44 #include "curlx/inet_pton.h"
     45 #include "url.h"
     46 
     47 /* The last 3 #include files should be in this order */
     48 #include "curl_printf.h"
     49 #include "curl_memory.h"
     50 #include "memdebug.h"
     51 
     52 /* for the (SOCKS) connect state machine */
     53 enum connect_t {
     54   CONNECT_INIT,
     55   CONNECT_SOCKS_INIT, /* 1 */
     56   CONNECT_SOCKS_SEND, /* 2 waiting to send more first data */
     57   CONNECT_SOCKS_READ_INIT, /* 3 set up read */
     58   CONNECT_SOCKS_READ, /* 4 read server response */
     59   CONNECT_GSSAPI_INIT, /* 5 */
     60   CONNECT_AUTH_INIT, /* 6 setup outgoing auth buffer */
     61   CONNECT_AUTH_SEND, /* 7 send auth */
     62   CONNECT_AUTH_READ, /* 8 read auth response */
     63   CONNECT_REQ_INIT,  /* 9 init SOCKS "request" */
     64   CONNECT_RESOLVING, /* 10 */
     65   CONNECT_RESOLVED,  /* 11 */
     66   CONNECT_RESOLVE_REMOTE, /* 12 */
     67   CONNECT_REQ_SEND,  /* 13 */
     68   CONNECT_REQ_SENDING, /* 14 */
     69   CONNECT_REQ_READ,  /* 15 */
     70   CONNECT_REQ_READ_MORE, /* 16 */
     71   CONNECT_DONE /* 17 connected fine to the remote or the SOCKS proxy */
     72 };
     73 
     74 #define CURL_SOCKS_BUF_SIZE 600
     75 
     76 /* make sure we configure it not too low */
     77 #if CURL_SOCKS_BUF_SIZE < 600
     78 #error CURL_SOCKS_BUF_SIZE must be at least 600
     79 #endif
     80 
     81 
     82 struct socks_state {
     83   enum connect_t state;
     84   size_t outstanding;  /* send this many bytes more */
     85   unsigned char buffer[CURL_SOCKS_BUF_SIZE];
     86   unsigned char *outp; /* send from this pointer */
     87 
     88   const char *hostname;
     89   int remote_port;
     90   const char *proxy_user;
     91   const char *proxy_password;
     92 };
     93 
     94 #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
     95 /*
     96  * Helper read-from-socket functions. Does the same as Curl_read() but it
     97  * blocks until all bytes amount of buffersize will be read. No more, no less.
     98  *
     99  * This is STUPID BLOCKING behavior. Only used by the SOCKS GSSAPI functions.
    100  */
    101 int Curl_blockread_all(struct Curl_cfilter *cf,
    102                        struct Curl_easy *data,   /* transfer */
    103                        char *buf,                /* store read data here */
    104                        size_t blen,              /* space in buf */
    105                        size_t *pnread)           /* amount bytes read */
    106 {
    107   size_t nread = 0;
    108   CURLcode err;
    109 
    110   *pnread = 0;
    111   for(;;) {
    112     timediff_t timeout_ms = Curl_timeleft(data, NULL, TRUE);
    113     if(timeout_ms < 0) {
    114       /* we already got the timeout */
    115       return CURLE_OPERATION_TIMEDOUT;
    116     }
    117     if(!timeout_ms)
    118       timeout_ms = TIMEDIFF_T_MAX;
    119     if(SOCKET_READABLE(cf->conn->sock[cf->sockindex], timeout_ms) <= 0) {
    120       return ~CURLE_OK;
    121     }
    122     err = Curl_conn_cf_recv(cf->next, data, buf, blen, &nread);
    123     if(CURLE_AGAIN == err)
    124       continue;
    125     else if(err)
    126       return (int)err;
    127 
    128     if(blen == nread) {
    129       *pnread += nread;
    130       return CURLE_OK;
    131     }
    132     if(!nread) /* EOF */
    133       return ~CURLE_OK;
    134 
    135     buf += nread;
    136     blen -= nread;
    137     *pnread += nread;
    138   }
    139 }
    140 #endif
    141 
    142 #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
    143 #define DEBUG_AND_VERBOSE
    144 #define sxstate(x,d,y) socksstate(x,d,y, __LINE__)
    145 #else
    146 #define sxstate(x,d,y) socksstate(x,d,y)
    147 #endif
    148 
    149 /* always use this function to change state, to make debugging easier */
    150 static void socksstate(struct socks_state *sx, struct Curl_easy *data,
    151                        enum connect_t state
    152 #ifdef DEBUG_AND_VERBOSE
    153                        , int lineno
    154 #endif
    155 )
    156 {
    157   enum connect_t oldstate = sx->state;
    158 #ifdef DEBUG_AND_VERBOSE
    159   /* synced with the state list in urldata.h */
    160   static const char * const socks_statename[] = {
    161     "INIT",
    162     "SOCKS_INIT",
    163     "SOCKS_SEND",
    164     "SOCKS_READ_INIT",
    165     "SOCKS_READ",
    166     "GSSAPI_INIT",
    167     "AUTH_INIT",
    168     "AUTH_SEND",
    169     "AUTH_READ",
    170     "REQ_INIT",
    171     "RESOLVING",
    172     "RESOLVED",
    173     "RESOLVE_REMOTE",
    174     "REQ_SEND",
    175     "REQ_SENDING",
    176     "REQ_READ",
    177     "REQ_READ_MORE",
    178     "DONE"
    179   };
    180 #endif
    181 
    182   (void)data;
    183   if(oldstate == state)
    184     /* do not bother when the new state is the same as the old state */
    185     return;
    186 
    187   sx->state = state;
    188 
    189 #ifdef DEBUG_AND_VERBOSE
    190   infof(data,
    191         "SXSTATE: %s => %s; line %d",
    192         socks_statename[oldstate], socks_statename[sx->state],
    193         lineno);
    194 #endif
    195 }
    196 
    197 static CURLproxycode socks_state_send(struct Curl_cfilter *cf,
    198                                       struct socks_state *sx,
    199                                       struct Curl_easy *data,
    200                                       CURLproxycode failcode,
    201                                       const char *description)
    202 {
    203   size_t nwritten;
    204   CURLcode result;
    205 
    206   result = Curl_conn_cf_send(cf->next, data, (char *)sx->outp,
    207                              sx->outstanding, FALSE, &nwritten);
    208   if(result) {
    209     if(CURLE_AGAIN == result)
    210       return CURLPX_OK;
    211 
    212     failf(data, "Failed to send %s: %s", description,
    213           curl_easy_strerror(result));
    214     return failcode;
    215   }
    216   else if(!nwritten) {
    217     /* connection closed */
    218     failf(data, "connection to proxy closed");
    219     return CURLPX_CLOSED;
    220   }
    221 
    222   DEBUGASSERT(sx->outstanding >= nwritten);
    223   /* not done, remain in state */
    224   sx->outstanding -= nwritten;
    225   sx->outp += nwritten;
    226   return CURLPX_OK;
    227 }
    228 
    229 static CURLproxycode socks_state_recv(struct Curl_cfilter *cf,
    230                                       struct socks_state *sx,
    231                                       struct Curl_easy *data,
    232                                       CURLproxycode failcode,
    233                                       const char *description)
    234 {
    235   size_t nread;
    236   CURLcode result;
    237 
    238   result = Curl_conn_cf_recv(cf->next, data, (char *)sx->outp,
    239                             sx->outstanding, &nread);
    240   if(result) {
    241     if(CURLE_AGAIN == result)
    242       return CURLPX_OK;
    243 
    244     failf(data, "SOCKS: Failed receiving %s: %s", description,
    245           curl_easy_strerror(result));
    246     return failcode;
    247   }
    248   else if(!nread) {
    249     /* connection closed */
    250     failf(data, "connection to proxy closed");
    251     return CURLPX_CLOSED;
    252   }
    253   /* remain in reading state */
    254   DEBUGASSERT(sx->outstanding >= nread);
    255   sx->outstanding -= nread;
    256   sx->outp += nread;
    257   return CURLPX_OK;
    258 }
    259 
    260 /*
    261 * This function logs in to a SOCKS4 proxy and sends the specifics to the final
    262 * destination server.
    263 *
    264 * Reference :
    265 *   https://www.openssh.com/txt/socks4.protocol
    266 *
    267 * Note :
    268 *   Set protocol4a=true for  "SOCKS 4A (Simple Extension to SOCKS 4 Protocol)"
    269 *   Nonsupport "Identification Protocol (RFC1413)"
    270 */
    271 static CURLproxycode do_SOCKS4(struct Curl_cfilter *cf,
    272                                struct socks_state *sx,
    273                                struct Curl_easy *data)
    274 {
    275   struct connectdata *conn = cf->conn;
    276   const bool protocol4a =
    277     (conn->socks_proxy.proxytype == CURLPROXY_SOCKS4A);
    278   unsigned char *socksreq = sx->buffer;
    279   CURLcode result;
    280   CURLproxycode presult;
    281   struct Curl_dns_entry *dns = NULL;
    282 
    283   switch(sx->state) {
    284   case CONNECT_SOCKS_INIT:
    285     /* SOCKS4 can only do IPv4, insist! */
    286     conn->ip_version = CURL_IPRESOLVE_V4;
    287     if(conn->bits.httpproxy)
    288       infof(data, "SOCKS4%s: connecting to HTTP proxy %s port %d",
    289             protocol4a ? "a" : "", sx->hostname, sx->remote_port);
    290 
    291     infof(data, "SOCKS4 communication to %s:%d",
    292           sx->hostname, sx->remote_port);
    293 
    294     /*
    295      * Compose socks4 request
    296      *
    297      * Request format
    298      *
    299      *     +----+----+----+----+----+----+----+----+----+----+....+----+
    300      *     | VN | CD | DSTPORT |      DSTIP        | USERID       |NULL|
    301      *     +----+----+----+----+----+----+----+----+----+----+....+----+
    302      * # of bytes:  1    1      2              4           variable       1
    303      */
    304 
    305     socksreq[0] = 4; /* version (SOCKS4) */
    306     socksreq[1] = 1; /* connect */
    307     socksreq[2] = (unsigned char)((sx->remote_port >> 8) & 0xff); /* MSB */
    308     socksreq[3] = (unsigned char)(sx->remote_port & 0xff);        /* LSB */
    309 
    310     /* DNS resolve only for SOCKS4, not SOCKS4a */
    311     if(!protocol4a) {
    312       result = Curl_resolv(data, sx->hostname, sx->remote_port,
    313                            cf->conn->ip_version, TRUE, &dns);
    314 
    315       if(result == CURLE_AGAIN) {
    316         sxstate(sx, data, CONNECT_RESOLVING);
    317         infof(data, "SOCKS4 non-blocking resolve of %s", sx->hostname);
    318         return CURLPX_OK;
    319       }
    320       else if(result)
    321         return CURLPX_RESOLVE_HOST;
    322       sxstate(sx, data, CONNECT_RESOLVED);
    323       goto CONNECT_RESOLVED;
    324     }
    325 
    326     /* socks4a does not resolve anything locally */
    327     sxstate(sx, data, CONNECT_REQ_INIT);
    328     goto CONNECT_REQ_INIT;
    329 
    330   case CONNECT_RESOLVING:
    331     /* check if we have the name resolved by now */
    332     result = Curl_resolv_check(data, &dns);
    333     if(!dns) {
    334       if(result)
    335         return CURLPX_RESOLVE_HOST;
    336       return CURLPX_OK;
    337     }
    338     FALLTHROUGH();
    339   case CONNECT_RESOLVED:
    340 CONNECT_RESOLVED:
    341   {
    342     struct Curl_addrinfo *hp = NULL;
    343     /*
    344      * We cannot use 'hostent' as a struct that Curl_resolv() returns. It
    345      * returns a Curl_addrinfo pointer that may not always look the same.
    346      */
    347     if(dns) {
    348       hp = dns->addr;
    349 
    350       /* scan for the first IPv4 address */
    351       while(hp && (hp->ai_family != AF_INET))
    352         hp = hp->ai_next;
    353 
    354       if(hp) {
    355         struct sockaddr_in *saddr_in;
    356         char buf[64];
    357         Curl_printable_address(hp, buf, sizeof(buf));
    358 
    359         saddr_in = (struct sockaddr_in *)(void *)hp->ai_addr;
    360         socksreq[4] = ((unsigned char *)&saddr_in->sin_addr.s_addr)[0];
    361         socksreq[5] = ((unsigned char *)&saddr_in->sin_addr.s_addr)[1];
    362         socksreq[6] = ((unsigned char *)&saddr_in->sin_addr.s_addr)[2];
    363         socksreq[7] = ((unsigned char *)&saddr_in->sin_addr.s_addr)[3];
    364 
    365         infof(data, "SOCKS4 connect to IPv4 %s (locally resolved)", buf);
    366 
    367         Curl_resolv_unlink(data, &dns); /* not used anymore from now on */
    368       }
    369       else
    370         failf(data, "SOCKS4 connection to %s not supported", sx->hostname);
    371     }
    372     else
    373       failf(data, "Failed to resolve \"%s\" for SOCKS4 connect.",
    374             sx->hostname);
    375 
    376     if(!hp)
    377       return CURLPX_RESOLVE_HOST;
    378   }
    379     FALLTHROUGH();
    380   case CONNECT_REQ_INIT:
    381 CONNECT_REQ_INIT:
    382     /*
    383      * This is currently not supporting "Identification Protocol (RFC1413)".
    384      */
    385     socksreq[8] = 0; /* ensure empty userid is null-terminated */
    386     if(sx->proxy_user) {
    387       size_t plen = strlen(sx->proxy_user);
    388       if(plen > 255) {
    389         /* there is no real size limit to this field in the protocol, but
    390            SOCKS5 limits the proxy user field to 255 bytes and it seems likely
    391            that a longer field is either a mistake or malicious input */
    392         failf(data, "Too long SOCKS proxy username");
    393         return CURLPX_LONG_USER;
    394       }
    395       /* copy the proxy name WITH trailing zero */
    396       memcpy(socksreq + 8, sx->proxy_user, plen + 1);
    397     }
    398 
    399     /*
    400      * Make connection
    401      */
    402     {
    403       size_t packetsize = 9 +
    404         strlen((char *)socksreq + 8); /* size including NUL */
    405 
    406       /* If SOCKS4a, set special invalid IP address 0.0.0.x */
    407       if(protocol4a) {
    408         size_t hostnamelen = 0;
    409         socksreq[4] = 0;
    410         socksreq[5] = 0;
    411         socksreq[6] = 0;
    412         socksreq[7] = 1;
    413         /* append hostname */
    414         hostnamelen = strlen(sx->hostname) + 1; /* length including NUL */
    415         if((hostnamelen <= 255) &&
    416            (packetsize + hostnamelen < sizeof(sx->buffer)))
    417           strcpy((char *)socksreq + packetsize, sx->hostname);
    418         else {
    419           failf(data, "SOCKS4: too long hostname");
    420           return CURLPX_LONG_HOSTNAME;
    421         }
    422         packetsize += hostnamelen;
    423       }
    424       sx->outp = socksreq;
    425       DEBUGASSERT(packetsize <= sizeof(sx->buffer));
    426       sx->outstanding = packetsize;
    427       sxstate(sx, data, CONNECT_REQ_SENDING);
    428     }
    429     FALLTHROUGH();
    430   case CONNECT_REQ_SENDING:
    431     /* Send request */
    432     presult = socks_state_send(cf, sx, data, CURLPX_SEND_CONNECT,
    433                                "SOCKS4 connect request");
    434     if(CURLPX_OK != presult)
    435       return presult;
    436     else if(sx->outstanding) {
    437       /* remain in sending state */
    438       return CURLPX_OK;
    439     }
    440     /* done sending! */
    441     sx->outstanding = 8; /* receive data size */
    442     sx->outp = socksreq;
    443     sxstate(sx, data, CONNECT_SOCKS_READ);
    444 
    445     FALLTHROUGH();
    446   case CONNECT_SOCKS_READ:
    447     /* Receive response */
    448     presult = socks_state_recv(cf, sx, data, CURLPX_RECV_CONNECT,
    449                                "connect request ack");
    450     if(CURLPX_OK != presult)
    451       return presult;
    452     else if(sx->outstanding) {
    453       /* remain in reading state */
    454       return CURLPX_OK;
    455     }
    456     sxstate(sx, data, CONNECT_DONE);
    457     break;
    458   default: /* lots of unused states in SOCKS4 */
    459     break;
    460   }
    461 
    462   /*
    463    * Response format
    464    *
    465    *     +----+----+----+----+----+----+----+----+
    466    *     | VN | CD | DSTPORT |      DSTIP        |
    467    *     +----+----+----+----+----+----+----+----+
    468    * # of bytes:  1    1      2              4
    469    *
    470    * VN is the version of the reply code and should be 0. CD is the result
    471    * code with one of the following values:
    472    *
    473    * 90: request granted
    474    * 91: request rejected or failed
    475    * 92: request rejected because SOCKS server cannot connect to
    476    *     identd on the client
    477    * 93: request rejected because the client program and identd
    478    *     report different user-ids
    479    */
    480 
    481   /* wrong version ? */
    482   if(socksreq[0]) {
    483     failf(data,
    484           "SOCKS4 reply has wrong version, version should be 0.");
    485     return CURLPX_BAD_VERSION;
    486   }
    487 
    488   /* Result */
    489   switch(socksreq[1]) {
    490   case 90:
    491     infof(data, "SOCKS4%s request granted.", protocol4a ? "a" : "");
    492     break;
    493   case 91:
    494     failf(data,
    495           "cannot complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)"
    496           ", request rejected or failed.",
    497           socksreq[4], socksreq[5], socksreq[6], socksreq[7],
    498           (((unsigned char)socksreq[2] << 8) | (unsigned char)socksreq[3]),
    499           (unsigned char)socksreq[1]);
    500     return CURLPX_REQUEST_FAILED;
    501   case 92:
    502     failf(data,
    503           "cannot complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)"
    504           ", request rejected because SOCKS server cannot connect to "
    505           "identd on the client.",
    506           socksreq[4], socksreq[5], socksreq[6], socksreq[7],
    507           (((unsigned char)socksreq[2] << 8) | (unsigned char)socksreq[3]),
    508           (unsigned char)socksreq[1]);
    509     return CURLPX_IDENTD;
    510   case 93:
    511     failf(data,
    512           "cannot complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)"
    513           ", request rejected because the client program and identd "
    514           "report different user-ids.",
    515           socksreq[4], socksreq[5], socksreq[6], socksreq[7],
    516           (((unsigned char)socksreq[2] << 8) | (unsigned char)socksreq[3]),
    517           (unsigned char)socksreq[1]);
    518     return CURLPX_IDENTD_DIFFER;
    519   default:
    520     failf(data,
    521           "cannot complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)"
    522           ", Unknown.",
    523           socksreq[4], socksreq[5], socksreq[6], socksreq[7],
    524           (((unsigned char)socksreq[2] << 8) | (unsigned char)socksreq[3]),
    525           (unsigned char)socksreq[1]);
    526     return CURLPX_UNKNOWN_FAIL;
    527   }
    528 
    529   return CURLPX_OK; /* Proxy was successful! */
    530 }
    531 
    532 /*
    533  * This function logs in to a SOCKS5 proxy and sends the specifics to the final
    534  * destination server.
    535  */
    536 static CURLproxycode do_SOCKS5(struct Curl_cfilter *cf,
    537                                struct socks_state *sx,
    538                                struct Curl_easy *data)
    539 {
    540   /*
    541     According to the RFC1928, section "6. Replies". This is what a SOCK5
    542     replies:
    543 
    544         +----+-----+-------+------+----------+----------+
    545         |VER | REP |  RSV  | ATYP | BND.ADDR | BND.PORT |
    546         +----+-----+-------+------+----------+----------+
    547         | 1  |  1  | X'00' |  1   | Variable |    2     |
    548         +----+-----+-------+------+----------+----------+
    549 
    550     Where:
    551 
    552     o  VER    protocol version: X'05'
    553     o  REP    Reply field:
    554     o  X'00' succeeded
    555   */
    556   struct connectdata *conn = cf->conn;
    557   unsigned char *socksreq = sx->buffer;
    558   size_t idx;
    559   CURLcode result;
    560   CURLproxycode presult;
    561   bool socks5_resolve_local =
    562     (conn->socks_proxy.proxytype == CURLPROXY_SOCKS5);
    563   const size_t hostname_len = strlen(sx->hostname);
    564   size_t len = 0;
    565   const unsigned char auth = data->set.socks5auth;
    566   bool allow_gssapi = FALSE;
    567   struct Curl_dns_entry *dns = NULL;
    568 
    569   switch(sx->state) {
    570   case CONNECT_SOCKS_INIT:
    571     if(conn->bits.httpproxy)
    572       infof(data, "SOCKS5: connecting to HTTP proxy %s port %d",
    573             sx->hostname, sx->remote_port);
    574 
    575     /* RFC1928 chapter 5 specifies max 255 chars for domain name in packet */
    576     if(!socks5_resolve_local && hostname_len > 255) {
    577       failf(data, "SOCKS5: the destination hostname is too long to be "
    578             "resolved remotely by the proxy.");
    579       return CURLPX_LONG_HOSTNAME;
    580     }
    581 
    582     if(auth & ~(CURLAUTH_BASIC | CURLAUTH_GSSAPI))
    583       infof(data,
    584             "warning: unsupported value passed to CURLOPT_SOCKS5_AUTH: %u",
    585             auth);
    586     if(!(auth & CURLAUTH_BASIC))
    587       /* disable username/password auth */
    588       sx->proxy_user = NULL;
    589 #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
    590     if(auth & CURLAUTH_GSSAPI)
    591       allow_gssapi = TRUE;
    592 #endif
    593 
    594     idx = 0;
    595     socksreq[idx++] = 5;   /* version */
    596     idx++;                 /* number of authentication methods */
    597     socksreq[idx++] = 0;   /* no authentication */
    598     if(allow_gssapi)
    599       socksreq[idx++] = 1; /* GSS-API */
    600     if(sx->proxy_user)
    601       socksreq[idx++] = 2; /* username/password */
    602     /* write the number of authentication methods */
    603     socksreq[1] = (unsigned char) (idx - 2);
    604 
    605     sx->outp = socksreq;
    606     DEBUGASSERT(idx <= sizeof(sx->buffer));
    607     sx->outstanding = idx;
    608     presult = socks_state_send(cf, sx, data, CURLPX_SEND_CONNECT,
    609                                "initial SOCKS5 request");
    610     if(CURLPX_OK != presult)
    611       return presult;
    612     else if(sx->outstanding) {
    613       /* remain in sending state */
    614       return CURLPX_OK;
    615     }
    616     sxstate(sx, data, CONNECT_SOCKS_READ);
    617     goto CONNECT_SOCKS_READ_INIT;
    618   case CONNECT_SOCKS_SEND:
    619     presult = socks_state_send(cf, sx, data, CURLPX_SEND_CONNECT,
    620                                "initial SOCKS5 request");
    621     if(CURLPX_OK != presult)
    622       return presult;
    623     else if(sx->outstanding) {
    624       /* remain in sending state */
    625       return CURLPX_OK;
    626     }
    627     FALLTHROUGH();
    628   case CONNECT_SOCKS_READ_INIT:
    629 CONNECT_SOCKS_READ_INIT:
    630     sx->outstanding = 2; /* expect two bytes */
    631     sx->outp = socksreq; /* store it here */
    632     FALLTHROUGH();
    633   case CONNECT_SOCKS_READ:
    634     presult = socks_state_recv(cf, sx, data, CURLPX_RECV_CONNECT,
    635                                "initial SOCKS5 response");
    636     if(CURLPX_OK != presult)
    637       return presult;
    638     else if(sx->outstanding) {
    639       /* remain in reading state */
    640       return CURLPX_OK;
    641     }
    642     else if(socksreq[0] != 5) {
    643       failf(data, "Received invalid version in initial SOCKS5 response.");
    644       return CURLPX_BAD_VERSION;
    645     }
    646     else if(socksreq[1] == 0) {
    647       /* DONE! No authentication needed. Send request. */
    648       sxstate(sx, data, CONNECT_REQ_INIT);
    649       goto CONNECT_REQ_INIT;
    650     }
    651     else if(socksreq[1] == 2) {
    652       /* regular name + password authentication */
    653       sxstate(sx, data, CONNECT_AUTH_INIT);
    654       goto CONNECT_AUTH_INIT;
    655     }
    656 #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
    657     else if(allow_gssapi && (socksreq[1] == 1)) {
    658       sxstate(sx, data, CONNECT_GSSAPI_INIT);
    659       result = Curl_SOCKS5_gssapi_negotiate(cf, data);
    660       if(result) {
    661         failf(data, "Unable to negotiate SOCKS5 GSS-API context.");
    662         return CURLPX_GSSAPI;
    663       }
    664     }
    665 #endif
    666     else {
    667       /* error */
    668       if(!allow_gssapi && (socksreq[1] == 1)) {
    669         failf(data,
    670               "SOCKS5 GSSAPI per-message authentication is not supported.");
    671         return CURLPX_GSSAPI_PERMSG;
    672       }
    673       else if(socksreq[1] == 255) {
    674         failf(data, "No authentication method was acceptable.");
    675         return CURLPX_NO_AUTH;
    676       }
    677     }
    678     failf(data,
    679           "Undocumented SOCKS5 mode attempted to be used by server.");
    680     return CURLPX_UNKNOWN_MODE;
    681 #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
    682   case CONNECT_GSSAPI_INIT:
    683     /* GSSAPI stuff done non-blocking */
    684     break;
    685 #endif
    686 
    687   default: /* do nothing! */
    688     break;
    689 
    690 CONNECT_AUTH_INIT:
    691   case CONNECT_AUTH_INIT: {
    692     /* Needs username and password */
    693     size_t proxy_user_len, proxy_password_len;
    694     if(sx->proxy_user && sx->proxy_password) {
    695       proxy_user_len = strlen(sx->proxy_user);
    696       proxy_password_len = strlen(sx->proxy_password);
    697     }
    698     else {
    699       proxy_user_len = 0;
    700       proxy_password_len = 0;
    701     }
    702 
    703     /*   username/password request looks like
    704      * +----+------+----------+------+----------+
    705      * |VER | ULEN |  UNAME   | PLEN |  PASSWD  |
    706      * +----+------+----------+------+----------+
    707      * | 1  |  1   | 1 to 255 |  1   | 1 to 255 |
    708      * +----+------+----------+------+----------+
    709      */
    710     len = 0;
    711     socksreq[len++] = 1;    /* username/pw subnegotiation version */
    712     socksreq[len++] = (unsigned char) proxy_user_len;
    713     if(sx->proxy_user && proxy_user_len) {
    714       /* the length must fit in a single byte */
    715       if(proxy_user_len > 255) {
    716         failf(data, "Excessive username length for proxy auth");
    717         return CURLPX_LONG_USER;
    718       }
    719       memcpy(socksreq + len, sx->proxy_user, proxy_user_len);
    720     }
    721     len += proxy_user_len;
    722     socksreq[len++] = (unsigned char) proxy_password_len;
    723     if(sx->proxy_password && proxy_password_len) {
    724       /* the length must fit in a single byte */
    725       if(proxy_password_len > 255) {
    726         failf(data, "Excessive password length for proxy auth");
    727         return CURLPX_LONG_PASSWD;
    728       }
    729       memcpy(socksreq + len, sx->proxy_password, proxy_password_len);
    730     }
    731     len += proxy_password_len;
    732     sxstate(sx, data, CONNECT_AUTH_SEND);
    733     DEBUGASSERT(len <= sizeof(sx->buffer));
    734     sx->outstanding = len;
    735     sx->outp = socksreq;
    736   }
    737     FALLTHROUGH();
    738   case CONNECT_AUTH_SEND:
    739     presult = socks_state_send(cf, sx, data, CURLPX_SEND_AUTH,
    740                                "SOCKS5 sub-negotiation request");
    741     if(CURLPX_OK != presult)
    742       return presult;
    743     else if(sx->outstanding) {
    744       /* remain in sending state */
    745       return CURLPX_OK;
    746     }
    747     sx->outp = socksreq;
    748     sx->outstanding = 2;
    749     sxstate(sx, data, CONNECT_AUTH_READ);
    750     FALLTHROUGH();
    751   case CONNECT_AUTH_READ:
    752     presult = socks_state_recv(cf, sx, data, CURLPX_RECV_AUTH,
    753                                "SOCKS5 sub-negotiation response");
    754     if(CURLPX_OK != presult)
    755       return presult;
    756     else if(sx->outstanding) {
    757       /* remain in reading state */
    758       return CURLPX_OK;
    759     }
    760     /* ignore the first (VER) byte */
    761     else if(socksreq[1]) { /* status */
    762       failf(data, "User was rejected by the SOCKS5 server (%d %d).",
    763             socksreq[0], socksreq[1]);
    764       return CURLPX_USER_REJECTED;
    765     }
    766 
    767     /* Everything is good so far, user was authenticated! */
    768     sxstate(sx, data, CONNECT_REQ_INIT);
    769     FALLTHROUGH();
    770   case CONNECT_REQ_INIT:
    771 CONNECT_REQ_INIT:
    772     if(socks5_resolve_local) {
    773       result = Curl_resolv(data, sx->hostname, sx->remote_port,
    774                            cf->conn->ip_version, TRUE, &dns);
    775 
    776       if(result == CURLE_AGAIN) {
    777         sxstate(sx, data, CONNECT_RESOLVING);
    778         return CURLPX_OK;
    779       }
    780       else if(result)
    781         return CURLPX_RESOLVE_HOST;
    782       sxstate(sx, data, CONNECT_RESOLVED);
    783       goto CONNECT_RESOLVED;
    784     }
    785     goto CONNECT_RESOLVE_REMOTE;
    786 
    787   case CONNECT_RESOLVING:
    788     /* check if we have the name resolved by now */
    789     result = Curl_resolv_check(data, &dns);
    790     if(!dns) {
    791       if(result)
    792         return CURLPX_RESOLVE_HOST;
    793       return CURLPX_OK;
    794     }
    795     FALLTHROUGH();
    796   case CONNECT_RESOLVED:
    797 CONNECT_RESOLVED:
    798   {
    799     char dest[MAX_IPADR_LEN];  /* printable address */
    800     struct Curl_addrinfo *hp = NULL;
    801     if(dns)
    802       hp = dns->addr;
    803 #ifdef USE_IPV6
    804     if(data->set.ipver != CURL_IPRESOLVE_WHATEVER) {
    805       int wanted_family = data->set.ipver == CURL_IPRESOLVE_V4 ?
    806         AF_INET : AF_INET6;
    807       /* scan for the first proper address */
    808       while(hp && (hp->ai_family != wanted_family))
    809         hp = hp->ai_next;
    810     }
    811 #endif
    812     if(!hp) {
    813       failf(data, "Failed to resolve \"%s\" for SOCKS5 connect.",
    814             sx->hostname);
    815       return CURLPX_RESOLVE_HOST;
    816     }
    817 
    818     Curl_printable_address(hp, dest, sizeof(dest));
    819 
    820     len = 0;
    821     socksreq[len++] = 5; /* version (SOCKS5) */
    822     socksreq[len++] = 1; /* connect */
    823     socksreq[len++] = 0; /* must be zero */
    824     if(hp->ai_family == AF_INET) {
    825       int i;
    826       struct sockaddr_in *saddr_in;
    827       socksreq[len++] = 1; /* ATYP: IPv4 = 1 */
    828 
    829       saddr_in = (struct sockaddr_in *)(void *)hp->ai_addr;
    830       for(i = 0; i < 4; i++) {
    831         socksreq[len++] = ((unsigned char *)&saddr_in->sin_addr.s_addr)[i];
    832       }
    833 
    834       infof(data, "SOCKS5 connect to %s:%d (locally resolved)", dest,
    835             sx->remote_port);
    836     }
    837 #ifdef USE_IPV6
    838     else if(hp->ai_family == AF_INET6) {
    839       int i;
    840       struct sockaddr_in6 *saddr_in6;
    841       socksreq[len++] = 4; /* ATYP: IPv6 = 4 */
    842 
    843       saddr_in6 = (struct sockaddr_in6 *)(void *)hp->ai_addr;
    844       for(i = 0; i < 16; i++) {
    845         socksreq[len++] =
    846           ((unsigned char *)&saddr_in6->sin6_addr.s6_addr)[i];
    847       }
    848 
    849       infof(data, "SOCKS5 connect to [%s]:%d (locally resolved)", dest,
    850             sx->remote_port);
    851     }
    852 #endif
    853     else {
    854       hp = NULL; /* fail! */
    855       failf(data, "SOCKS5 connection to %s not supported", dest);
    856     }
    857 
    858     Curl_resolv_unlink(data, &dns); /* not used anymore from now on */
    859     goto CONNECT_REQ_SEND;
    860   }
    861 CONNECT_RESOLVE_REMOTE:
    862   case CONNECT_RESOLVE_REMOTE:
    863     /* Authentication is complete, now specify destination to the proxy */
    864     len = 0;
    865     socksreq[len++] = 5; /* version (SOCKS5) */
    866     socksreq[len++] = 1; /* connect */
    867     socksreq[len++] = 0; /* must be zero */
    868 
    869     if(!socks5_resolve_local) {
    870       /* ATYP: domain name = 3,
    871          IPv6 == 4,
    872          IPv4 == 1 */
    873       unsigned char ip4[4];
    874 #ifdef USE_IPV6
    875       if(conn->bits.ipv6_ip) {
    876         char ip6[16];
    877         if(1 != curlx_inet_pton(AF_INET6, sx->hostname, ip6))
    878           return CURLPX_BAD_ADDRESS_TYPE;
    879         socksreq[len++] = 4;
    880         memcpy(&socksreq[len], ip6, sizeof(ip6));
    881         len += sizeof(ip6);
    882       }
    883       else
    884 #endif
    885       if(1 == curlx_inet_pton(AF_INET, sx->hostname, ip4)) {
    886         socksreq[len++] = 1;
    887         memcpy(&socksreq[len], ip4, sizeof(ip4));
    888         len += sizeof(ip4);
    889       }
    890       else {
    891         socksreq[len++] = 3;
    892         socksreq[len++] = (unsigned char) hostname_len; /* one byte length */
    893         memcpy(&socksreq[len], sx->hostname, hostname_len); /* w/o NULL */
    894         len += hostname_len;
    895       }
    896       infof(data, "SOCKS5 connect to %s:%d (remotely resolved)",
    897             sx->hostname, sx->remote_port);
    898     }
    899     FALLTHROUGH();
    900 
    901   case CONNECT_REQ_SEND:
    902 CONNECT_REQ_SEND:
    903     /* PORT MSB */
    904     socksreq[len++] = (unsigned char)((sx->remote_port >> 8) & 0xff);
    905     /* PORT LSB */
    906     socksreq[len++] = (unsigned char)(sx->remote_port & 0xff);
    907 
    908 #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
    909     if(conn->socks5_gssapi_enctype) {
    910       failf(data, "SOCKS5 GSS-API protection not yet implemented.");
    911       return CURLPX_GSSAPI_PROTECTION;
    912     }
    913 #endif
    914     sx->outp = socksreq;
    915     DEBUGASSERT(len <= sizeof(sx->buffer));
    916     sx->outstanding = len;
    917     sxstate(sx, data, CONNECT_REQ_SENDING);
    918     FALLTHROUGH();
    919   case CONNECT_REQ_SENDING:
    920     presult = socks_state_send(cf, sx, data, CURLPX_SEND_REQUEST,
    921                                "SOCKS5 connect request");
    922     if(CURLPX_OK != presult)
    923       return presult;
    924     else if(sx->outstanding) {
    925       /* remain in send state */
    926       return CURLPX_OK;
    927     }
    928 #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
    929     if(conn->socks5_gssapi_enctype) {
    930       failf(data, "SOCKS5 GSS-API protection not yet implemented.");
    931       return CURLPX_GSSAPI_PROTECTION;
    932     }
    933 #endif
    934     sx->outstanding = 10; /* minimum packet size is 10 */
    935     sx->outp = socksreq;
    936     sxstate(sx, data, CONNECT_REQ_READ);
    937     FALLTHROUGH();
    938   case CONNECT_REQ_READ:
    939     presult = socks_state_recv(cf, sx, data, CURLPX_RECV_REQACK,
    940                                "SOCKS5 connect request ack");
    941     if(CURLPX_OK != presult)
    942       return presult;
    943     else if(sx->outstanding) {
    944       /* remain in reading state */
    945       return CURLPX_OK;
    946     }
    947     else if(socksreq[0] != 5) { /* version */
    948       failf(data,
    949             "SOCKS5 reply has wrong version, version should be 5.");
    950       return CURLPX_BAD_VERSION;
    951     }
    952     else if(socksreq[1]) { /* Anything besides 0 is an error */
    953       CURLproxycode rc = CURLPX_REPLY_UNASSIGNED;
    954       int code = socksreq[1];
    955       failf(data, "cannot complete SOCKS5 connection to %s. (%d)",
    956             sx->hostname, (unsigned char)socksreq[1]);
    957       if(code < 9) {
    958         /* RFC 1928 section 6 lists: */
    959         static const CURLproxycode lookup[] = {
    960           CURLPX_OK,
    961           CURLPX_REPLY_GENERAL_SERVER_FAILURE,
    962           CURLPX_REPLY_NOT_ALLOWED,
    963           CURLPX_REPLY_NETWORK_UNREACHABLE,
    964           CURLPX_REPLY_HOST_UNREACHABLE,
    965           CURLPX_REPLY_CONNECTION_REFUSED,
    966           CURLPX_REPLY_TTL_EXPIRED,
    967           CURLPX_REPLY_COMMAND_NOT_SUPPORTED,
    968           CURLPX_REPLY_ADDRESS_TYPE_NOT_SUPPORTED,
    969         };
    970         rc = lookup[code];
    971       }
    972       return rc;
    973     }
    974 
    975     /* Fix: in general, returned BND.ADDR is variable length parameter by RFC
    976        1928, so the reply packet should be read until the end to avoid errors
    977        at subsequent protocol level.
    978 
    979        +----+-----+-------+------+----------+----------+
    980        |VER | REP |  RSV  | ATYP | BND.ADDR | BND.PORT |
    981        +----+-----+-------+------+----------+----------+
    982        | 1  |  1  | X'00' |  1   | Variable |    2     |
    983        +----+-----+-------+------+----------+----------+
    984 
    985        ATYP:
    986        o  IP v4 address: X'01', BND.ADDR = 4 byte
    987        o  domain name:  X'03', BND.ADDR = [ 1 byte length, string ]
    988        o  IP v6 address: X'04', BND.ADDR = 16 byte
    989     */
    990 
    991     /* Calculate real packet size */
    992     if(socksreq[3] == 3) {
    993       /* domain name */
    994       int addrlen = (int) socksreq[4];
    995       len = 5 + addrlen + 2;
    996     }
    997     else if(socksreq[3] == 4) {
    998       /* IPv6 */
    999       len = 4 + 16 + 2;
   1000     }
   1001     else if(socksreq[3] == 1) {
   1002       len = 4 + 4 + 2;
   1003     }
   1004     else {
   1005       failf(data, "SOCKS5 reply has wrong address type.");
   1006       return CURLPX_BAD_ADDRESS_TYPE;
   1007     }
   1008 
   1009     /* At this point we already read first 10 bytes */
   1010 #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
   1011     if(!conn->socks5_gssapi_enctype) {
   1012       /* decrypt_gssapi_blockread already read the whole packet */
   1013 #endif
   1014       if(len > 10) {
   1015         DEBUGASSERT(len <= sizeof(sx->buffer));
   1016         sx->outstanding = len - 10; /* get the rest */
   1017         sx->outp = &socksreq[10];
   1018         sxstate(sx, data, CONNECT_REQ_READ_MORE);
   1019       }
   1020       else {
   1021         sxstate(sx, data, CONNECT_DONE);
   1022         break;
   1023       }
   1024 #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
   1025     }
   1026 #endif
   1027     FALLTHROUGH();
   1028   case CONNECT_REQ_READ_MORE:
   1029     presult = socks_state_recv(cf, sx, data, CURLPX_RECV_ADDRESS,
   1030                                "SOCKS5 connect request address");
   1031     if(CURLPX_OK != presult)
   1032       return presult;
   1033     else if(sx->outstanding) {
   1034       /* remain in reading state */
   1035       return CURLPX_OK;
   1036     }
   1037     sxstate(sx, data, CONNECT_DONE);
   1038   }
   1039   infof(data, "SOCKS5 request granted.");
   1040 
   1041   return CURLPX_OK; /* Proxy was successful! */
   1042 }
   1043 
   1044 static CURLcode connect_SOCKS(struct Curl_cfilter *cf,
   1045                               struct socks_state *sxstate,
   1046                               struct Curl_easy *data)
   1047 {
   1048   CURLcode result = CURLE_OK;
   1049   CURLproxycode pxresult = CURLPX_OK;
   1050   struct connectdata *conn = cf->conn;
   1051 
   1052   switch(conn->socks_proxy.proxytype) {
   1053   case CURLPROXY_SOCKS5:
   1054   case CURLPROXY_SOCKS5_HOSTNAME:
   1055     pxresult = do_SOCKS5(cf, sxstate, data);
   1056     break;
   1057 
   1058   case CURLPROXY_SOCKS4:
   1059   case CURLPROXY_SOCKS4A:
   1060     pxresult = do_SOCKS4(cf, sxstate, data);
   1061     break;
   1062 
   1063   default:
   1064     failf(data, "unknown proxytype option given");
   1065     result = CURLE_COULDNT_CONNECT;
   1066   } /* switch proxytype */
   1067   if(pxresult) {
   1068     result = CURLE_PROXY;
   1069     data->info.pxcode = pxresult;
   1070   }
   1071 
   1072   return result;
   1073 }
   1074 
   1075 static void socks_proxy_cf_free(struct Curl_cfilter *cf)
   1076 {
   1077   struct socks_state *sxstate = cf->ctx;
   1078   if(sxstate) {
   1079     free(sxstate);
   1080     cf->ctx = NULL;
   1081   }
   1082 }
   1083 
   1084 /* After a TCP connection to the proxy has been verified, this function does
   1085    the next magic steps. If 'done' is not set TRUE, it is not done yet and
   1086    must be called again.
   1087 
   1088    Note: this function's sub-functions call failf()
   1089 
   1090 */
   1091 static CURLcode socks_proxy_cf_connect(struct Curl_cfilter *cf,
   1092                                        struct Curl_easy *data,
   1093                                        bool *done)
   1094 {
   1095   CURLcode result;
   1096   struct connectdata *conn = cf->conn;
   1097   int sockindex = cf->sockindex;
   1098   struct socks_state *sx = cf->ctx;
   1099 
   1100   if(cf->connected) {
   1101     *done = TRUE;
   1102     return CURLE_OK;
   1103   }
   1104 
   1105   result = cf->next->cft->do_connect(cf->next, data, done);
   1106   if(result || !*done)
   1107     return result;
   1108 
   1109   if(!sx) {
   1110     sx = calloc(1, sizeof(*sx));
   1111     if(!sx)
   1112       return CURLE_OUT_OF_MEMORY;
   1113     cf->ctx = sx;
   1114   }
   1115 
   1116   if(sx->state == CONNECT_INIT) {
   1117     /* for the secondary socket (FTP), use the "connect to host"
   1118      * but ignore the "connect to port" (use the secondary port)
   1119      */
   1120     sxstate(sx, data, CONNECT_SOCKS_INIT);
   1121     sx->hostname =
   1122       conn->bits.httpproxy ?
   1123       conn->http_proxy.host.name :
   1124       conn->bits.conn_to_host ?
   1125       conn->conn_to_host.name :
   1126       sockindex == SECONDARYSOCKET ?
   1127       conn->secondaryhostname : conn->host.name;
   1128     sx->remote_port =
   1129       conn->bits.httpproxy ? (int)conn->http_proxy.port :
   1130       sockindex == SECONDARYSOCKET ? conn->secondary_port :
   1131       conn->bits.conn_to_port ? conn->conn_to_port :
   1132       conn->remote_port;
   1133     sx->proxy_user = conn->socks_proxy.user;
   1134     sx->proxy_password = conn->socks_proxy.passwd;
   1135   }
   1136 
   1137   result = connect_SOCKS(cf, sx, data);
   1138   if(!result && sx->state == CONNECT_DONE) {
   1139     cf->connected = TRUE;
   1140     Curl_verboseconnect(data, conn, cf->sockindex);
   1141     socks_proxy_cf_free(cf);
   1142   }
   1143 
   1144   *done = cf->connected;
   1145   return result;
   1146 }
   1147 
   1148 static void socks_cf_adjust_pollset(struct Curl_cfilter *cf,
   1149                                     struct Curl_easy *data,
   1150                                     struct easy_pollset *ps)
   1151 {
   1152   struct socks_state *sx = cf->ctx;
   1153 
   1154   if(!cf->connected && sx) {
   1155     /* If we are not connected, the filter below is and has nothing
   1156      * to wait on, we determine what to wait for. */
   1157     curl_socket_t sock = Curl_conn_cf_get_socket(cf, data);
   1158     switch(sx->state) {
   1159     case CONNECT_RESOLVING:
   1160     case CONNECT_SOCKS_READ:
   1161     case CONNECT_AUTH_READ:
   1162     case CONNECT_REQ_READ:
   1163     case CONNECT_REQ_READ_MORE:
   1164       Curl_pollset_set_in_only(data, ps, sock);
   1165       break;
   1166     default:
   1167       Curl_pollset_set_out_only(data, ps, sock);
   1168       break;
   1169     }
   1170   }
   1171 }
   1172 
   1173 static void socks_proxy_cf_close(struct Curl_cfilter *cf,
   1174                                  struct Curl_easy *data)
   1175 {
   1176 
   1177   DEBUGASSERT(cf->next);
   1178   cf->connected = FALSE;
   1179   socks_proxy_cf_free(cf);
   1180   cf->next->cft->do_close(cf->next, data);
   1181 }
   1182 
   1183 static void socks_proxy_cf_destroy(struct Curl_cfilter *cf,
   1184                                    struct Curl_easy *data)
   1185 {
   1186   (void)data;
   1187   socks_proxy_cf_free(cf);
   1188 }
   1189 
   1190 static CURLcode socks_cf_query(struct Curl_cfilter *cf,
   1191                                struct Curl_easy *data,
   1192                                int query, int *pres1, void *pres2)
   1193 {
   1194   struct socks_state *sx = cf->ctx;
   1195 
   1196   if(sx) {
   1197     switch(query) {
   1198     case CF_QUERY_HOST_PORT:
   1199       *pres1 = sx->remote_port;
   1200       *((const char **)pres2) = sx->hostname;
   1201       return CURLE_OK;
   1202     default:
   1203       break;
   1204     }
   1205   }
   1206   return cf->next ?
   1207     cf->next->cft->query(cf->next, data, query, pres1, pres2) :
   1208     CURLE_UNKNOWN_OPTION;
   1209 }
   1210 
   1211 struct Curl_cftype Curl_cft_socks_proxy = {
   1212   "SOCKS-PROXYY",
   1213   CF_TYPE_IP_CONNECT|CF_TYPE_PROXY,
   1214   0,
   1215   socks_proxy_cf_destroy,
   1216   socks_proxy_cf_connect,
   1217   socks_proxy_cf_close,
   1218   Curl_cf_def_shutdown,
   1219   socks_cf_adjust_pollset,
   1220   Curl_cf_def_data_pending,
   1221   Curl_cf_def_send,
   1222   Curl_cf_def_recv,
   1223   Curl_cf_def_cntrl,
   1224   Curl_cf_def_conn_is_alive,
   1225   Curl_cf_def_conn_keep_alive,
   1226   socks_cf_query,
   1227 };
   1228 
   1229 CURLcode Curl_cf_socks_proxy_insert_after(struct Curl_cfilter *cf_at,
   1230                                           struct Curl_easy *data)
   1231 {
   1232   struct Curl_cfilter *cf;
   1233   CURLcode result;
   1234 
   1235   (void)data;
   1236   result = Curl_cf_create(&cf, &Curl_cft_socks_proxy, NULL);
   1237   if(!result)
   1238     Curl_conn_cf_insert_after(cf_at, cf);
   1239   return result;
   1240 }
   1241 
   1242 #endif /* CURL_DISABLE_PROXY */