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