• 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 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         statename[oldstate], 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   char dest[256] = "unknown";  /* printable hostname:port */
571   int idx;
572   CURLcode result;
573   CURLproxycode presult;
574   bool socks5_resolve_local =
575     (conn->socks_proxy.proxytype == CURLPROXY_SOCKS5) ? TRUE : FALSE;
576   const size_t hostname_len = strlen(sx->hostname);
577   ssize_t len = 0;
578   const unsigned char auth = data->set.socks5auth;
579   bool allow_gssapi = FALSE;
580   struct Curl_dns_entry *dns = NULL;
581 
582   DEBUGASSERT(auth & (CURLAUTH_BASIC | CURLAUTH_GSSAPI));
583   switch(sx->state) {
584   case CONNECT_SOCKS_INIT:
585     if(conn->bits.httpproxy)
586       infof(data, "SOCKS5: connecting to HTTP proxy %s port %d",
587             sx->hostname, sx->remote_port);
588 
589     /* RFC1928 chapter 5 specifies max 255 chars for domain name in packet */
590     if(!socks5_resolve_local && hostname_len > 255) {
591       infof(data, "SOCKS5: server resolving disabled for hostnames of "
592             "length > 255 [actual len=%zu]", hostname_len);
593       socks5_resolve_local = TRUE;
594     }
595 
596     if(auth & ~(CURLAUTH_BASIC | CURLAUTH_GSSAPI))
597       infof(data,
598             "warning: unsupported value passed to CURLOPT_SOCKS5_AUTH: %u",
599             auth);
600     if(!(auth & CURLAUTH_BASIC))
601       /* disable username/password auth */
602       sx->proxy_user = NULL;
603 #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
604     if(auth & CURLAUTH_GSSAPI)
605       allow_gssapi = TRUE;
606 #endif
607 
608     idx = 0;
609     socksreq[idx++] = 5;   /* version */
610     idx++;                 /* number of authentication methods */
611     socksreq[idx++] = 0;   /* no authentication */
612     if(allow_gssapi)
613       socksreq[idx++] = 1; /* GSS-API */
614     if(sx->proxy_user)
615       socksreq[idx++] = 2; /* username/password */
616     /* write the number of authentication methods */
617     socksreq[1] = (unsigned char) (idx - 2);
618 
619     sx->outp = socksreq;
620     sx->outstanding = idx;
621     presult = socks_state_send(cf, sx, data, CURLPX_SEND_CONNECT,
622                                "initial SOCKS5 request");
623     if(CURLPX_OK != presult)
624       return presult;
625     else if(sx->outstanding) {
626       /* remain in sending state */
627       return CURLPX_OK;
628     }
629     sxstate(sx, data, CONNECT_SOCKS_READ);
630     goto CONNECT_SOCKS_READ_INIT;
631   case CONNECT_SOCKS_SEND:
632     presult = socks_state_send(cf, sx, data, CURLPX_SEND_CONNECT,
633                                "initial SOCKS5 request");
634     if(CURLPX_OK != presult)
635       return presult;
636     else if(sx->outstanding) {
637       /* remain in sending state */
638       return CURLPX_OK;
639     }
640     /* FALLTHROUGH */
641   CONNECT_SOCKS_READ_INIT:
642   case CONNECT_SOCKS_READ_INIT:
643     sx->outstanding = 2; /* expect two bytes */
644     sx->outp = socksreq; /* store it here */
645     /* FALLTHROUGH */
646   case CONNECT_SOCKS_READ:
647     presult = socks_state_recv(cf, sx, data, CURLPX_RECV_CONNECT,
648                                "initial SOCKS5 response");
649     if(CURLPX_OK != presult)
650       return presult;
651     else if(sx->outstanding) {
652       /* remain in reading state */
653       return CURLPX_OK;
654     }
655     else if(socksreq[0] != 5) {
656       failf(data, "Received invalid version in initial SOCKS5 response.");
657       return CURLPX_BAD_VERSION;
658     }
659     else if(socksreq[1] == 0) {
660       /* DONE! No authentication needed. Send request. */
661       sxstate(sx, data, CONNECT_REQ_INIT);
662       goto CONNECT_REQ_INIT;
663     }
664     else if(socksreq[1] == 2) {
665       /* regular name + password authentication */
666       sxstate(sx, data, CONNECT_AUTH_INIT);
667       goto CONNECT_AUTH_INIT;
668     }
669 #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
670     else if(allow_gssapi && (socksreq[1] == 1)) {
671       sxstate(sx, data, CONNECT_GSSAPI_INIT);
672       result = Curl_SOCKS5_gssapi_negotiate(cf, data);
673       if(result) {
674         failf(data, "Unable to negotiate SOCKS5 GSS-API context.");
675         return CURLPX_GSSAPI;
676       }
677     }
678 #endif
679     else {
680       /* error */
681       if(!allow_gssapi && (socksreq[1] == 1)) {
682         failf(data,
683               "SOCKS5 GSSAPI per-message authentication is not supported.");
684         return CURLPX_GSSAPI_PERMSG;
685       }
686       else if(socksreq[1] == 255) {
687         failf(data, "No authentication method was acceptable.");
688         return CURLPX_NO_AUTH;
689       }
690     }
691     failf(data,
692           "Undocumented SOCKS5 mode attempted to be used by server.");
693     return CURLPX_UNKNOWN_MODE;
694 #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
695   case CONNECT_GSSAPI_INIT:
696     /* GSSAPI stuff done non-blocking */
697     break;
698 #endif
699 
700   default: /* do nothing! */
701     break;
702 
703   CONNECT_AUTH_INIT:
704   case CONNECT_AUTH_INIT: {
705     /* Needs user name and password */
706     size_t proxy_user_len, proxy_password_len;
707     if(sx->proxy_user && sx->proxy_password) {
708       proxy_user_len = strlen(sx->proxy_user);
709       proxy_password_len = strlen(sx->proxy_password);
710     }
711     else {
712       proxy_user_len = 0;
713       proxy_password_len = 0;
714     }
715 
716     /*   username/password request looks like
717      * +----+------+----------+------+----------+
718      * |VER | ULEN |  UNAME   | PLEN |  PASSWD  |
719      * +----+------+----------+------+----------+
720      * | 1  |  1   | 1 to 255 |  1   | 1 to 255 |
721      * +----+------+----------+------+----------+
722      */
723     len = 0;
724     socksreq[len++] = 1;    /* username/pw subnegotiation version */
725     socksreq[len++] = (unsigned char) proxy_user_len;
726     if(sx->proxy_user && proxy_user_len) {
727       /* the length must fit in a single byte */
728       if(proxy_user_len > 255) {
729         failf(data, "Excessive user name length for proxy auth");
730         return CURLPX_LONG_USER;
731       }
732       memcpy(socksreq + len, sx->proxy_user, proxy_user_len);
733     }
734     len += proxy_user_len;
735     socksreq[len++] = (unsigned char) proxy_password_len;
736     if(sx->proxy_password && proxy_password_len) {
737       /* the length must fit in a single byte */
738       if(proxy_password_len > 255) {
739         failf(data, "Excessive password length for proxy auth");
740         return CURLPX_LONG_PASSWD;
741       }
742       memcpy(socksreq + len, sx->proxy_password, proxy_password_len);
743     }
744     len += proxy_password_len;
745     sxstate(sx, data, CONNECT_AUTH_SEND);
746     sx->outstanding = len;
747     sx->outp = socksreq;
748   }
749     /* FALLTHROUGH */
750   case CONNECT_AUTH_SEND:
751     presult = socks_state_send(cf, sx, data, CURLPX_SEND_AUTH,
752                                "SOCKS5 sub-negotiation request");
753     if(CURLPX_OK != presult)
754       return presult;
755     else if(sx->outstanding) {
756       /* remain in sending state */
757       return CURLPX_OK;
758     }
759     sx->outp = socksreq;
760     sx->outstanding = 2;
761     sxstate(sx, data, CONNECT_AUTH_READ);
762     /* FALLTHROUGH */
763   case CONNECT_AUTH_READ:
764     presult = socks_state_recv(cf, sx, data, CURLPX_RECV_AUTH,
765                                "SOCKS5 sub-negotiation response");
766     if(CURLPX_OK != presult)
767       return presult;
768     else if(sx->outstanding) {
769       /* remain in reading state */
770       return CURLPX_OK;
771     }
772     /* ignore the first (VER) byte */
773     else if(socksreq[1]) { /* status */
774       failf(data, "User was rejected by the SOCKS5 server (%d %d).",
775             socksreq[0], socksreq[1]);
776       return CURLPX_USER_REJECTED;
777     }
778 
779     /* Everything is good so far, user was authenticated! */
780     sxstate(sx, data, CONNECT_REQ_INIT);
781     /* FALLTHROUGH */
782   CONNECT_REQ_INIT:
783   case CONNECT_REQ_INIT:
784     if(socks5_resolve_local) {
785       enum resolve_t rc = Curl_resolv(data, sx->hostname, sx->remote_port,
786                                       TRUE, &dns);
787 
788       if(rc == CURLRESOLV_ERROR)
789         return CURLPX_RESOLVE_HOST;
790 
791       if(rc == CURLRESOLV_PENDING) {
792         sxstate(sx, data, CONNECT_RESOLVING);
793         return CURLPX_OK;
794       }
795       sxstate(sx, data, CONNECT_RESOLVED);
796       goto CONNECT_RESOLVED;
797     }
798     goto CONNECT_RESOLVE_REMOTE;
799 
800   case CONNECT_RESOLVING:
801     /* check if we have the name resolved by now */
802     dns = Curl_fetch_addr(data, sx->hostname, sx->remote_port);
803 
804     if(dns) {
805 #ifdef CURLRES_ASYNCH
806       data->state.async.dns = dns;
807       data->state.async.done = TRUE;
808 #endif
809       infof(data, "SOCKS5: hostname '%s' found", sx->hostname);
810     }
811 
812     if(!dns) {
813       result = Curl_resolv_check(data, &dns);
814       if(!dns) {
815         if(result)
816           return CURLPX_RESOLVE_HOST;
817         return CURLPX_OK;
818       }
819     }
820     /* FALLTHROUGH */
821   CONNECT_RESOLVED:
822   case CONNECT_RESOLVED: {
823     struct Curl_addrinfo *hp = NULL;
824     size_t destlen;
825     if(dns)
826       hp = dns->addr;
827     if(!hp) {
828       failf(data, "Failed to resolve \"%s\" for SOCKS5 connect.",
829             sx->hostname);
830       return CURLPX_RESOLVE_HOST;
831     }
832 
833     Curl_printable_address(hp, dest, sizeof(dest));
834     destlen = strlen(dest);
835     msnprintf(dest + destlen, sizeof(dest) - destlen, ":%d", sx->remote_port);
836 
837     len = 0;
838     socksreq[len++] = 5; /* version (SOCKS5) */
839     socksreq[len++] = 1; /* connect */
840     socksreq[len++] = 0; /* must be zero */
841     if(hp->ai_family == AF_INET) {
842       int i;
843       struct sockaddr_in *saddr_in;
844       socksreq[len++] = 1; /* ATYP: IPv4 = 1 */
845 
846       saddr_in = (struct sockaddr_in *)(void *)hp->ai_addr;
847       for(i = 0; i < 4; i++) {
848         socksreq[len++] = ((unsigned char *)&saddr_in->sin_addr.s_addr)[i];
849       }
850 
851       infof(data, "SOCKS5 connect to IPv4 %s (locally resolved)", dest);
852     }
853 #ifdef ENABLE_IPV6
854     else if(hp->ai_family == AF_INET6) {
855       int i;
856       struct sockaddr_in6 *saddr_in6;
857       socksreq[len++] = 4; /* ATYP: IPv6 = 4 */
858 
859       saddr_in6 = (struct sockaddr_in6 *)(void *)hp->ai_addr;
860       for(i = 0; i < 16; i++) {
861         socksreq[len++] =
862           ((unsigned char *)&saddr_in6->sin6_addr.s6_addr)[i];
863       }
864 
865       infof(data, "SOCKS5 connect to IPv6 %s (locally resolved)", dest);
866     }
867 #endif
868     else {
869       hp = NULL; /* fail! */
870       failf(data, "SOCKS5 connection to %s not supported", dest);
871     }
872 
873     Curl_resolv_unlock(data, dns); /* not used anymore from now on */
874     goto CONNECT_REQ_SEND;
875   }
876   CONNECT_RESOLVE_REMOTE:
877   case CONNECT_RESOLVE_REMOTE:
878     /* Authentication is complete, now specify destination to the proxy */
879     len = 0;
880     socksreq[len++] = 5; /* version (SOCKS5) */
881     socksreq[len++] = 1; /* connect */
882     socksreq[len++] = 0; /* must be zero */
883 
884     if(!socks5_resolve_local) {
885       /* ATYP: domain name = 3,
886          IPv6 == 4,
887          IPv4 == 1 */
888       unsigned char ip4[4];
889 #ifdef ENABLE_IPV6
890       if(conn->bits.ipv6_ip) {
891         char ip6[16];
892         if(1 != Curl_inet_pton(AF_INET6, sx->hostname, ip6))
893           return CURLPX_BAD_ADDRESS_TYPE;
894         socksreq[len++] = 4;
895         memcpy(&socksreq[len], ip6, sizeof(ip6));
896         len += sizeof(ip6);
897       }
898       else
899 #endif
900       if(1 == Curl_inet_pton(AF_INET, sx->hostname, ip4)) {
901         socksreq[len++] = 1;
902         memcpy(&socksreq[len], ip4, sizeof(ip4));
903         len += sizeof(ip4);
904       }
905       else {
906         socksreq[len++] = 3;
907         socksreq[len++] = (char) hostname_len; /* one byte address length */
908         memcpy(&socksreq[len], sx->hostname, hostname_len); /* w/o NULL */
909         len += hostname_len;
910       }
911       infof(data, "SOCKS5 connect to %s:%d (remotely resolved)",
912             sx->hostname, sx->remote_port);
913     }
914     /* FALLTHROUGH */
915 
916   CONNECT_REQ_SEND:
917   case CONNECT_REQ_SEND:
918     /* PORT MSB */
919     socksreq[len++] = (unsigned char)((sx->remote_port >> 8) & 0xff);
920     /* PORT LSB */
921     socksreq[len++] = (unsigned char)(sx->remote_port & 0xff);
922 
923 #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
924     if(conn->socks5_gssapi_enctype) {
925       failf(data, "SOCKS5 GSS-API protection not yet implemented.");
926       return CURLPX_GSSAPI_PROTECTION;
927     }
928 #endif
929     sx->outp = socksreq;
930     sx->outstanding = len;
931     sxstate(sx, data, CONNECT_REQ_SENDING);
932     /* FALLTHROUGH */
933   case CONNECT_REQ_SENDING:
934     presult = socks_state_send(cf, sx, data, CURLPX_SEND_REQUEST,
935                                "SOCKS5 connect request");
936     if(CURLPX_OK != presult)
937       return presult;
938     else if(sx->outstanding) {
939       /* remain in send state */
940       return CURLPX_OK;
941     }
942 #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
943     if(conn->socks5_gssapi_enctype) {
944       failf(data, "SOCKS5 GSS-API protection not yet implemented.");
945       return CURLPX_GSSAPI_PROTECTION;
946     }
947 #endif
948     sx->outstanding = 10; /* minimum packet size is 10 */
949     sx->outp = socksreq;
950     sxstate(sx, data, CONNECT_REQ_READ);
951     /* FALLTHROUGH */
952   case CONNECT_REQ_READ:
953     presult = socks_state_recv(cf, sx, data, CURLPX_RECV_REQACK,
954                                "SOCKS5 connect request ack");
955     if(CURLPX_OK != presult)
956       return presult;
957     else if(sx->outstanding) {
958       /* remain in reading state */
959       return CURLPX_OK;
960     }
961     else if(socksreq[0] != 5) { /* version */
962       failf(data,
963             "SOCKS5 reply has wrong version, version should be 5.");
964       return CURLPX_BAD_VERSION;
965     }
966     else if(socksreq[1]) { /* Anything besides 0 is an error */
967       CURLproxycode rc = CURLPX_REPLY_UNASSIGNED;
968       int code = socksreq[1];
969       failf(data, "Can't complete SOCKS5 connection to %s. (%d)",
970             sx->hostname, (unsigned char)socksreq[1]);
971       if(code < 9) {
972         /* RFC 1928 section 6 lists: */
973         static const CURLproxycode lookup[] = {
974           CURLPX_OK,
975           CURLPX_REPLY_GENERAL_SERVER_FAILURE,
976           CURLPX_REPLY_NOT_ALLOWED,
977           CURLPX_REPLY_NETWORK_UNREACHABLE,
978           CURLPX_REPLY_HOST_UNREACHABLE,
979           CURLPX_REPLY_CONNECTION_REFUSED,
980           CURLPX_REPLY_TTL_EXPIRED,
981           CURLPX_REPLY_COMMAND_NOT_SUPPORTED,
982           CURLPX_REPLY_ADDRESS_TYPE_NOT_SUPPORTED,
983         };
984         rc = lookup[code];
985       }
986       return rc;
987     }
988 
989     /* Fix: in general, returned BND.ADDR is variable length parameter by RFC
990        1928, so the reply packet should be read until the end to avoid errors
991        at subsequent protocol level.
992 
993        +----+-----+-------+------+----------+----------+
994        |VER | REP |  RSV  | ATYP | BND.ADDR | BND.PORT |
995        +----+-----+-------+------+----------+----------+
996        | 1  |  1  | X'00' |  1   | Variable |    2     |
997        +----+-----+-------+------+----------+----------+
998 
999        ATYP:
1000        o  IP v4 address: X'01', BND.ADDR = 4 byte
1001        o  domain name:  X'03', BND.ADDR = [ 1 byte length, string ]
1002        o  IP v6 address: X'04', BND.ADDR = 16 byte
1003     */
1004 
1005     /* Calculate real packet size */
1006     if(socksreq[3] == 3) {
1007       /* domain name */
1008       int addrlen = (int) socksreq[4];
1009       len = 5 + addrlen + 2;
1010     }
1011     else if(socksreq[3] == 4) {
1012       /* IPv6 */
1013       len = 4 + 16 + 2;
1014     }
1015     else if(socksreq[3] == 1) {
1016       len = 4 + 4 + 2;
1017     }
1018     else {
1019       failf(data, "SOCKS5 reply has wrong address type.");
1020       return CURLPX_BAD_ADDRESS_TYPE;
1021     }
1022 
1023     /* At this point we already read first 10 bytes */
1024 #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
1025     if(!conn->socks5_gssapi_enctype) {
1026       /* decrypt_gssapi_blockread already read the whole packet */
1027 #endif
1028       if(len > 10) {
1029         sx->outstanding = len - 10; /* get the rest */
1030         sx->outp = &socksreq[10];
1031         sxstate(sx, data, CONNECT_REQ_READ_MORE);
1032       }
1033       else {
1034         sxstate(sx, data, CONNECT_DONE);
1035         break;
1036       }
1037 #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
1038     }
1039 #endif
1040     /* FALLTHROUGH */
1041   case CONNECT_REQ_READ_MORE:
1042     presult = socks_state_recv(cf, sx, data, CURLPX_RECV_ADDRESS,
1043                                "SOCKS5 connect request address");
1044     if(CURLPX_OK != presult)
1045       return presult;
1046     else if(sx->outstanding) {
1047       /* remain in reading state */
1048       return CURLPX_OK;
1049     }
1050     sxstate(sx, data, CONNECT_DONE);
1051   }
1052   infof(data, "SOCKS5 request granted.");
1053 
1054   return CURLPX_OK; /* Proxy was successful! */
1055 }
1056 
connect_SOCKS(struct Curl_cfilter * cf,struct socks_state * sxstate,struct Curl_easy * data)1057 static CURLcode connect_SOCKS(struct Curl_cfilter *cf,
1058                               struct socks_state *sxstate,
1059                               struct Curl_easy *data)
1060 {
1061   CURLcode result = CURLE_OK;
1062   CURLproxycode pxresult = CURLPX_OK;
1063   struct connectdata *conn = cf->conn;
1064 
1065   switch(conn->socks_proxy.proxytype) {
1066   case CURLPROXY_SOCKS5:
1067   case CURLPROXY_SOCKS5_HOSTNAME:
1068     pxresult = do_SOCKS5(cf, sxstate, data);
1069     break;
1070 
1071   case CURLPROXY_SOCKS4:
1072   case CURLPROXY_SOCKS4A:
1073     pxresult = do_SOCKS4(cf, sxstate, data);
1074     break;
1075 
1076   default:
1077     failf(data, "unknown proxytype option given");
1078     result = CURLE_COULDNT_CONNECT;
1079   } /* switch proxytype */
1080   if(pxresult) {
1081     result = CURLE_PROXY;
1082     data->info.pxcode = pxresult;
1083   }
1084 
1085   return result;
1086 }
1087 
socks_proxy_cf_free(struct Curl_cfilter * cf)1088 static void socks_proxy_cf_free(struct Curl_cfilter *cf)
1089 {
1090   struct socks_state *sxstate = cf->ctx;
1091   if(sxstate) {
1092     free(sxstate);
1093     cf->ctx = NULL;
1094   }
1095 }
1096 
1097 /* After a TCP connection to the proxy has been verified, this function does
1098    the next magic steps. If 'done' isn't set TRUE, it is not done yet and
1099    must be called again.
1100 
1101    Note: this function's sub-functions call failf()
1102 
1103 */
socks_proxy_cf_connect(struct Curl_cfilter * cf,struct Curl_easy * data,bool blocking,bool * done)1104 static CURLcode socks_proxy_cf_connect(struct Curl_cfilter *cf,
1105                                        struct Curl_easy *data,
1106                                        bool blocking, bool *done)
1107 {
1108   CURLcode result;
1109   struct connectdata *conn = cf->conn;
1110   int sockindex = cf->sockindex;
1111   struct socks_state *sx = cf->ctx;
1112 
1113   if(cf->connected) {
1114     *done = TRUE;
1115     return CURLE_OK;
1116   }
1117 
1118   result = cf->next->cft->connect(cf->next, data, blocking, done);
1119   if(result || !*done)
1120     return result;
1121 
1122   if(!sx) {
1123     sx = calloc(sizeof(*sx), 1);
1124     if(!sx)
1125       return CURLE_OUT_OF_MEMORY;
1126     cf->ctx = sx;
1127   }
1128 
1129   if(sx->state == CONNECT_INIT) {
1130     /* for the secondary socket (FTP), use the "connect to host"
1131      * but ignore the "connect to port" (use the secondary port)
1132      */
1133     sxstate(sx, data, CONNECT_SOCKS_INIT);
1134     sx->hostname =
1135       conn->bits.httpproxy ?
1136       conn->http_proxy.host.name :
1137       conn->bits.conn_to_host ?
1138       conn->conn_to_host.name :
1139       sockindex == SECONDARYSOCKET ?
1140       conn->secondaryhostname : conn->host.name;
1141     sx->remote_port =
1142       conn->bits.httpproxy ? (int)conn->http_proxy.port :
1143       sockindex == SECONDARYSOCKET ? conn->secondary_port :
1144       conn->bits.conn_to_port ? conn->conn_to_port :
1145       conn->remote_port;
1146     sx->proxy_user = conn->socks_proxy.user;
1147     sx->proxy_password = conn->socks_proxy.passwd;
1148   }
1149 
1150   result = connect_SOCKS(cf, sx, data);
1151   if(!result && sx->state == CONNECT_DONE) {
1152     cf->connected = TRUE;
1153     Curl_verboseconnect(data, conn);
1154     socks_proxy_cf_free(cf);
1155   }
1156 
1157   *done = cf->connected;
1158   return result;
1159 }
1160 
socks_cf_get_select_socks(struct Curl_cfilter * cf,struct Curl_easy * data,curl_socket_t * socks)1161 static int socks_cf_get_select_socks(struct Curl_cfilter *cf,
1162                                      struct Curl_easy *data,
1163                                      curl_socket_t *socks)
1164 {
1165   struct socks_state *sx = cf->ctx;
1166   int fds;
1167 
1168   fds = cf->next->cft->get_select_socks(cf->next, data, socks);
1169   if(!fds && cf->next->connected && !cf->connected && sx) {
1170     /* If we are not connected, the filter below is and has nothing
1171      * to wait on, we determine what to wait for. */
1172     socks[0] = Curl_conn_cf_get_socket(cf, data);
1173     switch(sx->state) {
1174     case CONNECT_RESOLVING:
1175     case CONNECT_SOCKS_READ:
1176     case CONNECT_AUTH_READ:
1177     case CONNECT_REQ_READ:
1178     case CONNECT_REQ_READ_MORE:
1179       fds = GETSOCK_READSOCK(0);
1180       break;
1181     default:
1182       fds = GETSOCK_WRITESOCK(0);
1183       break;
1184     }
1185   }
1186   return fds;
1187 }
1188 
socks_proxy_cf_close(struct Curl_cfilter * cf,struct Curl_easy * data)1189 static void socks_proxy_cf_close(struct Curl_cfilter *cf,
1190                                  struct Curl_easy *data)
1191 {
1192 
1193   DEBUGASSERT(cf->next);
1194   cf->connected = FALSE;
1195   socks_proxy_cf_free(cf);
1196   cf->next->cft->close(cf->next, data);
1197 }
1198 
socks_proxy_cf_destroy(struct Curl_cfilter * cf,struct Curl_easy * data)1199 static void socks_proxy_cf_destroy(struct Curl_cfilter *cf,
1200                                    struct Curl_easy *data)
1201 {
1202   (void)data;
1203   socks_proxy_cf_free(cf);
1204 }
1205 
socks_cf_get_host(struct Curl_cfilter * cf,struct Curl_easy * data,const char ** phost,const char ** pdisplay_host,int * pport)1206 static void socks_cf_get_host(struct Curl_cfilter *cf,
1207                               struct Curl_easy *data,
1208                               const char **phost,
1209                               const char **pdisplay_host,
1210                               int *pport)
1211 {
1212   (void)data;
1213   if(!cf->connected) {
1214     *phost = cf->conn->socks_proxy.host.name;
1215     *pdisplay_host = cf->conn->http_proxy.host.dispname;
1216     *pport = (int)cf->conn->socks_proxy.port;
1217   }
1218   else {
1219     cf->next->cft->get_host(cf->next, data, phost, pdisplay_host, pport);
1220   }
1221 }
1222 
1223 struct Curl_cftype Curl_cft_socks_proxy = {
1224   "SOCKS-PROXYY",
1225   CF_TYPE_IP_CONNECT,
1226   0,
1227   socks_proxy_cf_destroy,
1228   socks_proxy_cf_connect,
1229   socks_proxy_cf_close,
1230   socks_cf_get_host,
1231   socks_cf_get_select_socks,
1232   Curl_cf_def_data_pending,
1233   Curl_cf_def_send,
1234   Curl_cf_def_recv,
1235   Curl_cf_def_cntrl,
1236   Curl_cf_def_conn_is_alive,
1237   Curl_cf_def_conn_keep_alive,
1238   Curl_cf_def_query,
1239 };
1240 
Curl_conn_socks_proxy_add(struct Curl_easy * data,struct connectdata * conn,int sockindex)1241 CURLcode Curl_conn_socks_proxy_add(struct Curl_easy *data,
1242                                    struct connectdata *conn,
1243                                    int sockindex)
1244 {
1245   struct Curl_cfilter *cf;
1246   CURLcode result;
1247 
1248   result = Curl_cf_create(&cf, &Curl_cft_socks_proxy, NULL);
1249   if(!result)
1250     Curl_conn_cf_add(data, conn, sockindex, cf);
1251   return result;
1252 }
1253 
Curl_cf_socks_proxy_insert_after(struct Curl_cfilter * cf_at,struct Curl_easy * data)1254 CURLcode Curl_cf_socks_proxy_insert_after(struct Curl_cfilter *cf_at,
1255                                           struct Curl_easy *data)
1256 {
1257   struct Curl_cfilter *cf;
1258   CURLcode result;
1259 
1260   (void)data;
1261   result = Curl_cf_create(&cf, &Curl_cft_socks_proxy, NULL);
1262   if(!result)
1263     Curl_conn_cf_insert_after(cf_at, cf);
1264   return result;
1265 }
1266 
1267 #endif /* CURL_DISABLE_PROXY */
1268