VirtualBox

source: vbox/trunk/src/libs/curl-8.7.1/lib/socks.c@ 105945

Last change on this file since 105945 was 104083, checked in by vboxsync, 8 months ago

curl-8.7.1: Applied and adjusted our curl changes to 8.4.0. bugref:10639

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

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette