VirtualBox

source: vbox/trunk/src/libs/curl-7.87.0/lib/socks.c@ 99005

Last change on this file since 99005 was 98326, checked in by vboxsync, 2 years ago

curl-7.87.0: Applied and adjusted our curl changes to 7.83.1. bugref:10356

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

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