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