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