1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
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.
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.
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
21 * SPDX-License-Identifier: curl
23 ***************************************************************************/
25 #include "curl_setup.h"
27 #if !defined(CURL_DISABLE_PROXY)
29 #ifdef HAVE_NETINET_IN_H
30 #include <netinet/in.h>
32 #ifdef HAVE_ARPA_INET_H
33 #include <arpa/inet.h>
42 #include "multiif.h" /* for getsock macros */
43 #include "inet_pton.h"
45 /* The last 3 #include files should be in this order */
46 #include "curl_printf.h"
47 #include "curl_memory.h"
50 #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
52 * Helper read-from-socket functions. Does the same as Curl_read() but it
53 * blocks until all bytes amount of buffersize will be read. No more, no less.
55 * This is STUPID BLOCKING behavior. Only used by the SOCKS GSSAPI functions.
57 int Curl_blockread_all(struct Curl_easy *data, /* transfer */
58 curl_socket_t sockfd, /* read from this socket */
59 char *buf, /* store read data here */
60 ssize_t buffersize, /* max amount to read */
61 ssize_t *n) /* amount bytes read */
68 timediff_t timeout_ms = Curl_timeleft(data, NULL, TRUE);
70 /* we already got the timeout */
71 result = CURLE_OPERATION_TIMEDOUT;
75 timeout_ms = TIMEDIFF_T_MAX;
76 if(SOCKET_READABLE(sockfd, timeout_ms) <= 0) {
80 result = Curl_read_plain(sockfd, buf, buffersize, &nread);
81 if(CURLE_AGAIN == result)
86 if(buffersize == nread) {
105 #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
106 #define DEBUG_AND_VERBOSE
107 #define sxstate(x,y) socksstate(x,y, __LINE__)
109 #define sxstate(x,y) socksstate(x,y)
112 /* always use this function to change state, to make debugging easier */
113 static void socksstate(struct Curl_easy *data,
115 #ifdef DEBUG_AND_VERBOSE
120 struct connectdata *conn = data->conn;
121 enum connect_t oldstate = conn->cnnct.state;
122 #ifdef DEBUG_AND_VERBOSE
123 /* synced with the state list in urldata.h */
124 static const char * const statename[] = {
146 if(oldstate == state)
147 /* don't bother when the new state is the same as the old state */
150 conn->cnnct.state = state;
152 #ifdef DEBUG_AND_VERBOSE
154 "SXSTATE: %s => %s conn %p; line %d",
155 statename[oldstate], statename[conn->cnnct.state], conn,
160 int Curl_SOCKS_getsock(struct connectdata *conn, curl_socket_t *sock,
164 sock[0] = conn->sock[sockindex];
165 switch(conn->cnnct.state) {
166 case CONNECT_RESOLVING:
167 case CONNECT_SOCKS_READ:
168 case CONNECT_AUTH_READ:
169 case CONNECT_REQ_READ:
170 case CONNECT_REQ_READ_MORE:
171 rc = GETSOCK_READSOCK(0);
174 rc = GETSOCK_WRITESOCK(0);
181 * This function logs in to a SOCKS4 proxy and sends the specifics to the final
182 * destination server.
185 * https://www.openssh.com/txt/socks4.protocol
188 * Set protocol4a=true for "SOCKS 4A (Simple Extension to SOCKS 4 Protocol)"
189 * Nonsupport "Identification Protocol (RFC1413)"
191 CURLproxycode Curl_SOCKS4(const char *proxy_user,
192 const char *hostname,
195 struct Curl_easy *data,
198 struct connectdata *conn = data->conn;
199 const bool protocol4a =
200 (conn->socks_proxy.proxytype == CURLPROXY_SOCKS4A) ? TRUE : FALSE;
201 unsigned char *socksreq = (unsigned char *)data->state.buffer;
203 curl_socket_t sockfd = conn->sock[sockindex];
204 struct connstate *sx = &conn->cnnct;
205 struct Curl_dns_entry *dns = NULL;
209 /* make sure that the buffer is at least 600 bytes */
210 DEBUGASSERT(READBUFFER_MIN >= 600);
212 if(!SOCKS_STATE(sx->state) && !*done)
213 sxstate(data, CONNECT_SOCKS_INIT);
216 case CONNECT_SOCKS_INIT:
217 /* SOCKS4 can only do IPv4, insist! */
218 conn->ip_version = CURL_IPRESOLVE_V4;
219 if(conn->bits.httpproxy)
220 infof(data, "SOCKS4%s: connecting to HTTP proxy %s port %d",
221 protocol4a ? "a" : "", hostname, remote_port);
223 infof(data, "SOCKS4 communication to %s:%d", hostname, remote_port);
226 * Compose socks4 request
230 * +----+----+----+----+----+----+----+----+----+----+....+----+
231 * | VN | CD | DSTPORT | DSTIP | USERID |NULL|
232 * +----+----+----+----+----+----+----+----+----+----+....+----+
233 * # of bytes: 1 1 2 4 variable 1
236 socksreq[0] = 4; /* version (SOCKS4) */
237 socksreq[1] = 1; /* connect */
238 socksreq[2] = (unsigned char)((remote_port >> 8) & 0xff); /* PORT MSB */
239 socksreq[3] = (unsigned char)(remote_port & 0xff); /* PORT LSB */
241 /* DNS resolve only for SOCKS4, not SOCKS4a */
244 Curl_resolv(data, hostname, remote_port, FALSE, &dns);
246 if(rc == CURLRESOLV_ERROR)
247 return CURLPX_RESOLVE_HOST;
248 else if(rc == CURLRESOLV_PENDING) {
249 sxstate(data, CONNECT_RESOLVING);
250 infof(data, "SOCKS4 non-blocking resolve of %s", hostname);
253 sxstate(data, CONNECT_RESOLVED);
254 goto CONNECT_RESOLVED;
257 /* socks4a doesn't resolve anything locally */
258 sxstate(data, CONNECT_REQ_INIT);
259 goto CONNECT_REQ_INIT;
261 case CONNECT_RESOLVING:
262 /* check if we have the name resolved by now */
263 dns = Curl_fetch_addr(data, hostname, (int)conn->port);
266 #ifdef CURLRES_ASYNCH
267 data->state.async.dns = dns;
268 data->state.async.done = TRUE;
270 infof(data, "Hostname '%s' was found", hostname);
271 sxstate(data, CONNECT_RESOLVED);
274 result = Curl_resolv_check(data, &dns);
277 return CURLPX_RESOLVE_HOST;
283 case CONNECT_RESOLVED: {
284 struct Curl_addrinfo *hp = NULL;
286 * We cannot use 'hostent' as a struct that Curl_resolv() returns. It
287 * returns a Curl_addrinfo pointer that may not always look the same.
292 /* scan for the first IPv4 address */
293 while(hp && (hp->ai_family != AF_INET))
297 struct sockaddr_in *saddr_in;
299 Curl_printable_address(hp, buf, sizeof(buf));
301 saddr_in = (struct sockaddr_in *)(void *)hp->ai_addr;
302 socksreq[4] = ((unsigned char *)&saddr_in->sin_addr.s_addr)[0];
303 socksreq[5] = ((unsigned char *)&saddr_in->sin_addr.s_addr)[1];
304 socksreq[6] = ((unsigned char *)&saddr_in->sin_addr.s_addr)[2];
305 socksreq[7] = ((unsigned char *)&saddr_in->sin_addr.s_addr)[3];
307 infof(data, "SOCKS4 connect to IPv4 %s (locally resolved)", buf);
309 Curl_resolv_unlock(data, dns); /* not used anymore from now on */
312 failf(data, "SOCKS4 connection to %s not supported", hostname);
315 failf(data, "Failed to resolve \"%s\" for SOCKS4 connect.",
319 return CURLPX_RESOLVE_HOST;
323 case CONNECT_REQ_INIT:
325 * This is currently not supporting "Identification Protocol (RFC1413)".
327 socksreq[8] = 0; /* ensure empty userid is NUL-terminated */
329 size_t plen = strlen(proxy_user);
330 if(plen >= (size_t)data->set.buffer_size - 8) {
331 failf(data, "Too long SOCKS proxy user name, can't use");
332 return CURLPX_LONG_USER;
334 /* copy the proxy name WITH trailing zero */
335 memcpy(socksreq + 8, proxy_user, plen + 1);
342 size_t packetsize = 9 +
343 strlen((char *)socksreq + 8); /* size including NUL */
345 /* If SOCKS4a, set special invalid IP address 0.0.0.x */
347 size_t hostnamelen = 0;
352 /* append hostname */
353 hostnamelen = strlen(hostname) + 1; /* length including NUL */
354 if(hostnamelen <= 255)
355 strcpy((char *)socksreq + packetsize, hostname);
357 failf(data, "SOCKS4: too long host name");
358 return CURLPX_LONG_HOSTNAME;
360 packetsize += hostnamelen;
363 sx->outstanding = packetsize;
364 sxstate(data, CONNECT_REQ_SENDING);
367 case CONNECT_REQ_SENDING:
369 result = Curl_write_plain(data, sockfd, (char *)sx->outp,
370 sx->outstanding, &written);
371 if(result && (CURLE_AGAIN != result)) {
372 failf(data, "Failed to send SOCKS4 connect request.");
373 return CURLPX_SEND_CONNECT;
375 if(written != sx->outstanding) {
376 /* not done, remain in state */
377 sx->outstanding -= written;
383 sx->outstanding = 8; /* receive data size */
385 sxstate(data, CONNECT_SOCKS_READ);
388 case CONNECT_SOCKS_READ:
389 /* Receive response */
390 result = Curl_read_plain(sockfd, (char *)sx->outp,
391 sx->outstanding, &actualread);
392 if(result && (CURLE_AGAIN != result)) {
393 failf(data, "SOCKS4: Failed receiving connect request ack: %s",
394 curl_easy_strerror(result));
395 return CURLPX_RECV_CONNECT;
397 else if(!result && !actualread) {
398 /* connection closed */
399 failf(data, "connection to proxy closed");
400 return CURLPX_CLOSED;
402 else if(actualread != sx->outstanding) {
403 /* remain in reading state */
404 sx->outstanding -= actualread;
405 sx->outp += actualread;
408 sxstate(data, CONNECT_DONE);
410 default: /* lots of unused states in SOCKS4 */
417 * +----+----+----+----+----+----+----+----+
418 * | VN | CD | DSTPORT | DSTIP |
419 * +----+----+----+----+----+----+----+----+
420 * # of bytes: 1 1 2 4
422 * VN is the version of the reply code and should be 0. CD is the result
423 * code with one of the following values:
425 * 90: request granted
426 * 91: request rejected or failed
427 * 92: request rejected because SOCKS server cannot connect to
428 * identd on the client
429 * 93: request rejected because the client program and identd
430 * report different user-ids
433 /* wrong version ? */
436 "SOCKS4 reply has wrong version, version should be 0.");
437 return CURLPX_BAD_VERSION;
441 switch(socksreq[1]) {
443 infof(data, "SOCKS4%s request granted.", protocol4a?"a":"");
447 "Can't complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)"
448 ", request rejected or failed.",
449 socksreq[4], socksreq[5], socksreq[6], socksreq[7],
450 (((unsigned char)socksreq[2] << 8) | (unsigned char)socksreq[3]),
451 (unsigned char)socksreq[1]);
452 return CURLPX_REQUEST_FAILED;
455 "Can't complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)"
456 ", request rejected because SOCKS server cannot connect to "
457 "identd on the client.",
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_IDENTD;
464 "Can't complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)"
465 ", request rejected because the client program and identd "
466 "report different user-ids.",
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_DIFFER;
473 "Can't complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)"
475 socksreq[4], socksreq[5], socksreq[6], socksreq[7],
476 (((unsigned char)socksreq[2] << 8) | (unsigned char)socksreq[3]),
477 (unsigned char)socksreq[1]);
478 return CURLPX_UNKNOWN_FAIL;
482 return CURLPX_OK; /* Proxy was successful! */
486 * This function logs in to a SOCKS5 proxy and sends the specifics to the final
487 * destination server.
489 CURLproxycode Curl_SOCKS5(const char *proxy_user,
490 const char *proxy_password,
491 const char *hostname,
494 struct Curl_easy *data,
498 According to the RFC1928, section "6. Replies". This is what a SOCK5
501 +----+-----+-------+------+----------+----------+
502 |VER | REP | RSV | ATYP | BND.ADDR | BND.PORT |
503 +----+-----+-------+------+----------+----------+
504 | 1 | 1 | X'00' | 1 | Variable | 2 |
505 +----+-----+-------+------+----------+----------+
509 o VER protocol version: X'05'
513 struct connectdata *conn = data->conn;
514 unsigned char *socksreq = (unsigned char *)data->state.buffer;
515 char dest[256] = "unknown"; /* printable hostname:port */
520 curl_socket_t sockfd = conn->sock[sockindex];
521 bool socks5_resolve_local =
522 (conn->socks_proxy.proxytype == CURLPROXY_SOCKS5) ? TRUE : FALSE;
523 const size_t hostname_len = strlen(hostname);
525 const unsigned long auth = data->set.socks5auth;
526 bool allow_gssapi = FALSE;
527 struct connstate *sx = &conn->cnnct;
528 struct Curl_dns_entry *dns = NULL;
530 if(!SOCKS_STATE(sx->state) && !*done)
531 sxstate(data, CONNECT_SOCKS_INIT);
534 case CONNECT_SOCKS_INIT:
535 if(conn->bits.httpproxy)
536 infof(data, "SOCKS5: connecting to HTTP proxy %s port %d",
537 hostname, remote_port);
539 /* RFC1928 chapter 5 specifies max 255 chars for domain name in packet */
540 if(!socks5_resolve_local && hostname_len > 255) {
541 infof(data, "SOCKS5: server resolving disabled for hostnames of "
542 "length > 255 [actual len=%zu]", hostname_len);
543 socks5_resolve_local = TRUE;
546 if(auth & ~(CURLAUTH_BASIC | CURLAUTH_GSSAPI))
548 "warning: unsupported value passed to CURLOPT_SOCKS5_AUTH: %lu",
550 if(!(auth & CURLAUTH_BASIC))
551 /* disable username/password auth */
553 #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
554 if(auth & CURLAUTH_GSSAPI)
559 socksreq[idx++] = 5; /* version */
560 idx++; /* number of authentication methods */
561 socksreq[idx++] = 0; /* no authentication */
563 socksreq[idx++] = 1; /* GSS-API */
565 socksreq[idx++] = 2; /* username/password */
566 /* write the number of authentication methods */
567 socksreq[1] = (unsigned char) (idx - 2);
569 result = Curl_write_plain(data, sockfd, (char *)socksreq, idx, &written);
570 if(result && (CURLE_AGAIN != result)) {
571 failf(data, "Unable to send initial SOCKS5 request.");
572 return CURLPX_SEND_CONNECT;
575 sxstate(data, CONNECT_SOCKS_SEND);
576 sx->outstanding = idx - written;
577 sx->outp = &socksreq[written];
580 sxstate(data, CONNECT_SOCKS_READ);
581 goto CONNECT_SOCKS_READ_INIT;
582 case CONNECT_SOCKS_SEND:
583 result = Curl_write_plain(data, sockfd, (char *)sx->outp,
584 sx->outstanding, &written);
585 if(result && (CURLE_AGAIN != result)) {
586 failf(data, "Unable to send initial SOCKS5 request.");
587 return CURLPX_SEND_CONNECT;
589 if(written != sx->outstanding) {
590 /* not done, remain in state */
591 sx->outstanding -= written;
596 CONNECT_SOCKS_READ_INIT:
597 case CONNECT_SOCKS_READ_INIT:
598 sx->outstanding = 2; /* expect two bytes */
599 sx->outp = socksreq; /* store it here */
601 case CONNECT_SOCKS_READ:
602 result = Curl_read_plain(sockfd, (char *)sx->outp,
603 sx->outstanding, &actualread);
604 if(result && (CURLE_AGAIN != result)) {
605 failf(data, "Unable to receive initial SOCKS5 response.");
606 return CURLPX_RECV_CONNECT;
608 else if(!result && !actualread) {
609 /* connection closed */
610 failf(data, "Connection to proxy closed");
611 return CURLPX_CLOSED;
613 else if(actualread != sx->outstanding) {
614 /* remain in reading state */
615 sx->outstanding -= actualread;
616 sx->outp += actualread;
619 else if(socksreq[0] != 5) {
620 failf(data, "Received invalid version in initial SOCKS5 response.");
621 return CURLPX_BAD_VERSION;
623 else if(socksreq[1] == 0) {
624 /* DONE! No authentication needed. Send request. */
625 sxstate(data, CONNECT_REQ_INIT);
626 goto CONNECT_REQ_INIT;
628 else if(socksreq[1] == 2) {
629 /* regular name + password authentication */
630 sxstate(data, CONNECT_AUTH_INIT);
631 goto CONNECT_AUTH_INIT;
633 #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
634 else if(allow_gssapi && (socksreq[1] == 1)) {
635 sxstate(data, CONNECT_GSSAPI_INIT);
636 result = Curl_SOCKS5_gssapi_negotiate(sockindex, data);
638 failf(data, "Unable to negotiate SOCKS5 GSS-API context.");
639 return CURLPX_GSSAPI;
645 if(!allow_gssapi && (socksreq[1] == 1)) {
647 "SOCKS5 GSSAPI per-message authentication is not supported.");
648 return CURLPX_GSSAPI_PERMSG;
650 else if(socksreq[1] == 255) {
651 failf(data, "No authentication method was acceptable.");
652 return CURLPX_NO_AUTH;
656 "Undocumented SOCKS5 mode attempted to be used by server.");
657 return CURLPX_UNKNOWN_MODE;
658 #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
659 case CONNECT_GSSAPI_INIT:
660 /* GSSAPI stuff done non-blocking */
664 default: /* do nothing! */
668 case CONNECT_AUTH_INIT: {
669 /* Needs user name and password */
670 size_t proxy_user_len, proxy_password_len;
671 if(proxy_user && proxy_password) {
672 proxy_user_len = strlen(proxy_user);
673 proxy_password_len = strlen(proxy_password);
677 proxy_password_len = 0;
680 /* username/password request looks like
681 * +----+------+----------+------+----------+
682 * |VER | ULEN | UNAME | PLEN | PASSWD |
683 * +----+------+----------+------+----------+
684 * | 1 | 1 | 1 to 255 | 1 | 1 to 255 |
685 * +----+------+----------+------+----------+
688 socksreq[len++] = 1; /* username/pw subnegotiation version */
689 socksreq[len++] = (unsigned char) proxy_user_len;
690 if(proxy_user && proxy_user_len) {
691 /* the length must fit in a single byte */
692 if(proxy_user_len >= 255) {
693 failf(data, "Excessive user name length for proxy auth");
694 return CURLPX_LONG_USER;
696 memcpy(socksreq + len, proxy_user, proxy_user_len);
698 len += proxy_user_len;
699 socksreq[len++] = (unsigned char) proxy_password_len;
700 if(proxy_password && proxy_password_len) {
701 /* the length must fit in a single byte */
702 if(proxy_password_len > 255) {
703 failf(data, "Excessive password length for proxy auth");
704 return CURLPX_LONG_PASSWD;
706 memcpy(socksreq + len, proxy_password, proxy_password_len);
708 len += proxy_password_len;
709 sxstate(data, CONNECT_AUTH_SEND);
710 sx->outstanding = len;
714 case CONNECT_AUTH_SEND:
715 result = Curl_write_plain(data, sockfd, (char *)sx->outp,
716 sx->outstanding, &written);
717 if(result && (CURLE_AGAIN != result)) {
718 failf(data, "Failed to send SOCKS5 sub-negotiation request.");
719 return CURLPX_SEND_AUTH;
721 if(sx->outstanding != written) {
722 /* remain in state */
723 sx->outstanding -= written;
729 sxstate(data, CONNECT_AUTH_READ);
731 case CONNECT_AUTH_READ:
732 result = Curl_read_plain(sockfd, (char *)sx->outp,
733 sx->outstanding, &actualread);
734 if(result && (CURLE_AGAIN != result)) {
735 failf(data, "Unable to receive SOCKS5 sub-negotiation response.");
736 return CURLPX_RECV_AUTH;
738 else if(!result && !actualread) {
739 /* connection closed */
740 failf(data, "connection to proxy closed");
741 return CURLPX_CLOSED;
743 else if(actualread != sx->outstanding) {
744 /* remain in state */
745 sx->outstanding -= actualread;
746 sx->outp += actualread;
749 /* ignore the first (VER) byte */
750 else if(socksreq[1]) { /* status */
751 failf(data, "User was rejected by the SOCKS5 server (%d %d).",
752 socksreq[0], socksreq[1]);
753 return CURLPX_USER_REJECTED;
756 /* Everything is good so far, user was authenticated! */
757 sxstate(data, CONNECT_REQ_INIT);
760 case CONNECT_REQ_INIT:
761 if(socks5_resolve_local) {
762 enum resolve_t rc = Curl_resolv(data, hostname, remote_port,
765 if(rc == CURLRESOLV_ERROR)
766 return CURLPX_RESOLVE_HOST;
768 if(rc == CURLRESOLV_PENDING) {
769 sxstate(data, CONNECT_RESOLVING);
772 sxstate(data, CONNECT_RESOLVED);
773 goto CONNECT_RESOLVED;
775 goto CONNECT_RESOLVE_REMOTE;
777 case CONNECT_RESOLVING:
778 /* check if we have the name resolved by now */
779 dns = Curl_fetch_addr(data, hostname, remote_port);
782 #ifdef CURLRES_ASYNCH
783 data->state.async.dns = dns;
784 data->state.async.done = TRUE;
786 infof(data, "SOCKS5: hostname '%s' found", hostname);
790 result = Curl_resolv_check(data, &dns);
793 return CURLPX_RESOLVE_HOST;
799 case CONNECT_RESOLVED: {
800 struct Curl_addrinfo *hp = NULL;
805 failf(data, "Failed to resolve \"%s\" for SOCKS5 connect.",
807 return CURLPX_RESOLVE_HOST;
810 Curl_printable_address(hp, dest, sizeof(dest));
811 destlen = strlen(dest);
812 msnprintf(dest + destlen, sizeof(dest) - destlen, ":%d", remote_port);
815 socksreq[len++] = 5; /* version (SOCKS5) */
816 socksreq[len++] = 1; /* connect */
817 socksreq[len++] = 0; /* must be zero */
818 if(hp->ai_family == AF_INET) {
820 struct sockaddr_in *saddr_in;
821 socksreq[len++] = 1; /* ATYP: IPv4 = 1 */
823 saddr_in = (struct sockaddr_in *)(void *)hp->ai_addr;
824 for(i = 0; i < 4; i++) {
825 socksreq[len++] = ((unsigned char *)&saddr_in->sin_addr.s_addr)[i];
828 infof(data, "SOCKS5 connect to IPv4 %s (locally resolved)", dest);
831 else if(hp->ai_family == AF_INET6) {
833 struct sockaddr_in6 *saddr_in6;
834 socksreq[len++] = 4; /* ATYP: IPv6 = 4 */
836 saddr_in6 = (struct sockaddr_in6 *)(void *)hp->ai_addr;
837 for(i = 0; i < 16; i++) {
839 ((unsigned char *)&saddr_in6->sin6_addr.s6_addr)[i];
842 infof(data, "SOCKS5 connect to IPv6 %s (locally resolved)", dest);
846 hp = NULL; /* fail! */
847 failf(data, "SOCKS5 connection to %s not supported", dest);
850 Curl_resolv_unlock(data, dns); /* not used anymore from now on */
851 goto CONNECT_REQ_SEND;
853 CONNECT_RESOLVE_REMOTE:
854 case CONNECT_RESOLVE_REMOTE:
855 /* Authentication is complete, now specify destination to the proxy */
857 socksreq[len++] = 5; /* version (SOCKS5) */
858 socksreq[len++] = 1; /* connect */
859 socksreq[len++] = 0; /* must be zero */
861 if(!socks5_resolve_local) {
862 /* ATYP: domain name = 3,
865 unsigned char ip4[4];
867 if(conn->bits.ipv6_ip) {
869 if(1 != Curl_inet_pton(AF_INET6, hostname, ip6))
870 return CURLPX_BAD_ADDRESS_TYPE;
872 memcpy(&socksreq[len], ip6, sizeof(ip6));
877 if(1 == Curl_inet_pton(AF_INET, hostname, ip4)) {
879 memcpy(&socksreq[len], ip4, sizeof(ip4));
884 socksreq[len++] = (char) hostname_len; /* one byte address length */
885 memcpy(&socksreq[len], hostname, hostname_len); /* address w/o NULL */
888 infof(data, "SOCKS5 connect to %s:%d (remotely resolved)",
889 hostname, remote_port);
894 case CONNECT_REQ_SEND:
896 socksreq[len++] = (unsigned char)((remote_port >> 8) & 0xff);
898 socksreq[len++] = (unsigned char)(remote_port & 0xff);
900 #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
901 if(conn->socks5_gssapi_enctype) {
902 failf(data, "SOCKS5 GSS-API protection not yet implemented.");
903 return CURLPX_GSSAPI_PROTECTION;
907 sx->outstanding = len;
908 sxstate(data, CONNECT_REQ_SENDING);
910 case CONNECT_REQ_SENDING:
911 result = Curl_write_plain(data, sockfd, (char *)sx->outp,
912 sx->outstanding, &written);
913 if(result && (CURLE_AGAIN != result)) {
914 failf(data, "Failed to send SOCKS5 connect request.");
915 return CURLPX_SEND_REQUEST;
917 if(sx->outstanding != written) {
918 /* remain in state */
919 sx->outstanding -= written;
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;
929 sx->outstanding = 10; /* minimum packet size is 10 */
931 sxstate(data, CONNECT_REQ_READ);
933 case CONNECT_REQ_READ:
934 result = Curl_read_plain(sockfd, (char *)sx->outp,
935 sx->outstanding, &actualread);
936 if(result && (CURLE_AGAIN != result)) {
937 failf(data, "Failed to receive SOCKS5 connect request ack.");
938 return CURLPX_RECV_REQACK;
940 else if(!result && !actualread) {
941 /* connection closed */
942 failf(data, "connection to proxy closed");
943 return CURLPX_CLOSED;
945 else if(actualread != sx->outstanding) {
946 /* remain in state */
947 sx->outstanding -= actualread;
948 sx->outp += actualread;
952 if(socksreq[0] != 5) { /* version */
954 "SOCKS5 reply has wrong version, version should be 5.");
955 return CURLPX_BAD_VERSION;
957 else if(socksreq[1]) { /* Anything besides 0 is an error */
958 CURLproxycode rc = CURLPX_REPLY_UNASSIGNED;
959 int code = socksreq[1];
960 failf(data, "Can't complete SOCKS5 connection to %s. (%d)",
961 hostname, (unsigned char)socksreq[1]);
963 /* RFC 1928 section 6 lists: */
964 static const CURLproxycode lookup[] = {
966 CURLPX_REPLY_GENERAL_SERVER_FAILURE,
967 CURLPX_REPLY_NOT_ALLOWED,
968 CURLPX_REPLY_NETWORK_UNREACHABLE,
969 CURLPX_REPLY_HOST_UNREACHABLE,
970 CURLPX_REPLY_CONNECTION_REFUSED,
971 CURLPX_REPLY_TTL_EXPIRED,
972 CURLPX_REPLY_COMMAND_NOT_SUPPORTED,
973 CURLPX_REPLY_ADDRESS_TYPE_NOT_SUPPORTED,
980 /* Fix: in general, returned BND.ADDR is variable length parameter by RFC
981 1928, so the reply packet should be read until the end to avoid errors
982 at subsequent protocol level.
984 +----+-----+-------+------+----------+----------+
985 |VER | REP | RSV | ATYP | BND.ADDR | BND.PORT |
986 +----+-----+-------+------+----------+----------+
987 | 1 | 1 | X'00' | 1 | Variable | 2 |
988 +----+-----+-------+------+----------+----------+
991 o IP v4 address: X'01', BND.ADDR = 4 byte
992 o domain name: X'03', BND.ADDR = [ 1 byte length, string ]
993 o IP v6 address: X'04', BND.ADDR = 16 byte
996 /* Calculate real packet size */
997 if(socksreq[3] == 3) {
999 int addrlen = (int) socksreq[4];
1000 len = 5 + addrlen + 2;
1002 else if(socksreq[3] == 4) {
1006 else if(socksreq[3] == 1) {
1010 failf(data, "SOCKS5 reply has wrong address type.");
1011 return CURLPX_BAD_ADDRESS_TYPE;
1014 /* At this point we already read first 10 bytes */
1015 #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
1016 if(!conn->socks5_gssapi_enctype) {
1017 /* decrypt_gssapi_blockread already read the whole packet */
1020 sx->outstanding = len - 10; /* get the rest */
1021 sx->outp = &socksreq[10];
1022 sxstate(data, CONNECT_REQ_READ_MORE);
1025 sxstate(data, CONNECT_DONE);
1028 #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
1032 case CONNECT_REQ_READ_MORE:
1033 result = Curl_read_plain(sockfd, (char *)sx->outp,
1034 sx->outstanding, &actualread);
1035 if(result && (CURLE_AGAIN != result)) {
1036 failf(data, "Failed to receive SOCKS5 connect request ack.");
1037 return CURLPX_RECV_ADDRESS;
1039 else if(!result && !actualread) {
1040 /* connection closed */
1041 failf(data, "connection to proxy closed");
1042 return CURLPX_CLOSED;
1044 else if(actualread != sx->outstanding) {
1045 /* remain in state */
1046 sx->outstanding -= actualread;
1047 sx->outp += actualread;
1050 sxstate(data, CONNECT_DONE);
1052 infof(data, "SOCKS5 request granted.");
1055 return CURLPX_OK; /* Proxy was successful! */
1058 #endif /* CURL_DISABLE_PROXY */