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 "connect.h"
40#include "timeval.h"
41#include "socks.h"
42#include "multiif.h" /* for getsock macros */
43#include "inet_pton.h"
44
45/* The last 3 #include files should be in this order */
46#include "curl_printf.h"
47#include "curl_memory.h"
48#include "memdebug.h"
49
50#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
51/*
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.
54 *
55 * This is STUPID BLOCKING behavior. Only used by the SOCKS GSSAPI functions.
56 */
57int 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 */
62{
63 ssize_t nread = 0;
64 ssize_t allread = 0;
65 int result;
66 *n = 0;
67 for(;;) {
68 timediff_t timeout_ms = Curl_timeleft(data, NULL, TRUE);
69 if(timeout_ms < 0) {
70 /* we already got the timeout */
71 result = CURLE_OPERATION_TIMEDOUT;
72 break;
73 }
74 if(!timeout_ms)
75 timeout_ms = TIMEDIFF_T_MAX;
76 if(SOCKET_READABLE(sockfd, timeout_ms) <= 0) {
77 result = ~CURLE_OK;
78 break;
79 }
80 result = Curl_read_plain(sockfd, buf, buffersize, &nread);
81 if(CURLE_AGAIN == result)
82 continue;
83 if(result)
84 break;
85
86 if(buffersize == nread) {
87 allread += nread;
88 *n = allread;
89 result = CURLE_OK;
90 break;
91 }
92 if(!nread) {
93 result = ~CURLE_OK;
94 break;
95 }
96
97 buffersize -= nread;
98 buf += nread;
99 allread += nread;
100 }
101 return result;
102}
103#endif
104
105#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
106#define DEBUG_AND_VERBOSE
107#define sxstate(x,y) socksstate(x,y, __LINE__)
108#else
109#define sxstate(x,y) socksstate(x,y)
110#endif
111
112/* always use this function to change state, to make debugging easier */
113static void socksstate(struct Curl_easy *data,
114 enum connect_t state
115#ifdef DEBUG_AND_VERBOSE
116 , int lineno
117#endif
118)
119{
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[] = {
125 "INIT",
126 "SOCKS_INIT",
127 "SOCKS_SEND",
128 "SOCKS_READ_INIT",
129 "SOCKS_READ",
130 "GSSAPI_INIT",
131 "AUTH_INIT",
132 "AUTH_SEND",
133 "AUTH_READ",
134 "REQ_INIT",
135 "RESOLVING",
136 "RESOLVED",
137 "RESOLVE_REMOTE",
138 "REQ_SEND",
139 "REQ_SENDING",
140 "REQ_READ",
141 "REQ_READ_MORE",
142 "DONE"
143 };
144#endif
145
146 if(oldstate == state)
147 /* don't bother when the new state is the same as the old state */
148 return;
149
150 conn->cnnct.state = state;
151
152#ifdef DEBUG_AND_VERBOSE
153 infof(data,
154 "SXSTATE: %s => %s conn %p; line %d",
155 statename[oldstate], statename[conn->cnnct.state], conn,
156 lineno);
157#endif
158}
159
160int Curl_SOCKS_getsock(struct connectdata *conn, curl_socket_t *sock,
161 int sockindex)
162{
163 int rc = 0;
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);
172 break;
173 default:
174 rc = GETSOCK_WRITESOCK(0);
175 break;
176 }
177 return rc;
178}
179
180/*
181* This function logs in to a SOCKS4 proxy and sends the specifics to the final
182* destination server.
183*
184* Reference :
185* https://www.openssh.com/txt/socks4.protocol
186*
187* Note :
188* Set protocol4a=true for "SOCKS 4A (Simple Extension to SOCKS 4 Protocol)"
189* Nonsupport "Identification Protocol (RFC1413)"
190*/
191CURLproxycode Curl_SOCKS4(const char *proxy_user,
192 const char *hostname,
193 int remote_port,
194 int sockindex,
195 struct Curl_easy *data,
196 bool *done)
197{
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;
202 CURLcode result;
203 curl_socket_t sockfd = conn->sock[sockindex];
204 struct connstate *sx = &conn->cnnct;
205 struct Curl_dns_entry *dns = NULL;
206 ssize_t actualread;
207 ssize_t written;
208
209 /* make sure that the buffer is at least 600 bytes */
210 DEBUGASSERT(READBUFFER_MIN >= 600);
211
212 if(!SOCKS_STATE(sx->state) && !*done)
213 sxstate(data, CONNECT_SOCKS_INIT);
214
215 switch(sx->state) {
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);
222
223 infof(data, "SOCKS4 communication to %s:%d", hostname, remote_port);
224
225 /*
226 * Compose socks4 request
227 *
228 * Request format
229 *
230 * +----+----+----+----+----+----+----+----+----+----+....+----+
231 * | VN | CD | DSTPORT | DSTIP | USERID |NULL|
232 * +----+----+----+----+----+----+----+----+----+----+....+----+
233 * # of bytes: 1 1 2 4 variable 1
234 */
235
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 */
240
241 /* DNS resolve only for SOCKS4, not SOCKS4a */
242 if(!protocol4a) {
243 enum resolve_t rc =
244 Curl_resolv(data, hostname, remote_port, FALSE, &dns);
245
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);
251 return CURLPX_OK;
252 }
253 sxstate(data, CONNECT_RESOLVED);
254 goto CONNECT_RESOLVED;
255 }
256
257 /* socks4a doesn't resolve anything locally */
258 sxstate(data, CONNECT_REQ_INIT);
259 goto CONNECT_REQ_INIT;
260
261 case CONNECT_RESOLVING:
262 /* check if we have the name resolved by now */
263 dns = Curl_fetch_addr(data, hostname, (int)conn->port);
264
265 if(dns) {
266#ifdef CURLRES_ASYNCH
267 data->state.async.dns = dns;
268 data->state.async.done = TRUE;
269#endif
270 infof(data, "Hostname '%s' was found", hostname);
271 sxstate(data, CONNECT_RESOLVED);
272 }
273 else {
274 result = Curl_resolv_check(data, &dns);
275 if(!dns) {
276 if(result)
277 return CURLPX_RESOLVE_HOST;
278 return CURLPX_OK;
279 }
280 }
281 /* FALLTHROUGH */
282 CONNECT_RESOLVED:
283 case CONNECT_RESOLVED: {
284 struct Curl_addrinfo *hp = NULL;
285 /*
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.
288 */
289 if(dns) {
290 hp = dns->addr;
291
292 /* scan for the first IPv4 address */
293 while(hp && (hp->ai_family != AF_INET))
294 hp = hp->ai_next;
295
296 if(hp) {
297 struct sockaddr_in *saddr_in;
298 char buf[64];
299 Curl_printable_address(hp, buf, sizeof(buf));
300
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];
306
307 infof(data, "SOCKS4 connect to IPv4 %s (locally resolved)", buf);
308
309 Curl_resolv_unlock(data, dns); /* not used anymore from now on */
310 }
311 else
312 failf(data, "SOCKS4 connection to %s not supported", hostname);
313 }
314 else
315 failf(data, "Failed to resolve \"%s\" for SOCKS4 connect.",
316 hostname);
317
318 if(!hp)
319 return CURLPX_RESOLVE_HOST;
320 }
321 /* FALLTHROUGH */
322 CONNECT_REQ_INIT:
323 case CONNECT_REQ_INIT:
324 /*
325 * This is currently not supporting "Identification Protocol (RFC1413)".
326 */
327 socksreq[8] = 0; /* ensure empty userid is NUL-terminated */
328 if(proxy_user) {
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;
333 }
334 /* copy the proxy name WITH trailing zero */
335 memcpy(socksreq + 8, proxy_user, plen + 1);
336 }
337
338 /*
339 * Make connection
340 */
341 {
342 size_t packetsize = 9 +
343 strlen((char *)socksreq + 8); /* size including NUL */
344
345 /* If SOCKS4a, set special invalid IP address 0.0.0.x */
346 if(protocol4a) {
347 size_t hostnamelen = 0;
348 socksreq[4] = 0;
349 socksreq[5] = 0;
350 socksreq[6] = 0;
351 socksreq[7] = 1;
352 /* append hostname */
353 hostnamelen = strlen(hostname) + 1; /* length including NUL */
354 if(hostnamelen <= 255)
355 strcpy((char *)socksreq + packetsize, hostname);
356 else {
357 failf(data, "SOCKS4: too long host name");
358 return CURLPX_LONG_HOSTNAME;
359 }
360 packetsize += hostnamelen;
361 }
362 sx->outp = socksreq;
363 sx->outstanding = packetsize;
364 sxstate(data, CONNECT_REQ_SENDING);
365 }
366 /* FALLTHROUGH */
367 case CONNECT_REQ_SENDING:
368 /* Send request */
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;
374 }
375 if(written != sx->outstanding) {
376 /* not done, remain in state */
377 sx->outstanding -= written;
378 sx->outp += written;
379 return CURLPX_OK;
380 }
381
382 /* done sending! */
383 sx->outstanding = 8; /* receive data size */
384 sx->outp = socksreq;
385 sxstate(data, CONNECT_SOCKS_READ);
386
387 /* FALLTHROUGH */
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;
396 }
397 else if(!result && !actualread) {
398 /* connection closed */
399 failf(data, "connection to proxy closed");
400 return CURLPX_CLOSED;
401 }
402 else if(actualread != sx->outstanding) {
403 /* remain in reading state */
404 sx->outstanding -= actualread;
405 sx->outp += actualread;
406 return CURLPX_OK;
407 }
408 sxstate(data, CONNECT_DONE);
409 break;
410 default: /* lots of unused states in SOCKS4 */
411 break;
412 }
413
414 /*
415 * Response format
416 *
417 * +----+----+----+----+----+----+----+----+
418 * | VN | CD | DSTPORT | DSTIP |
419 * +----+----+----+----+----+----+----+----+
420 * # of bytes: 1 1 2 4
421 *
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:
424 *
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
431 */
432
433 /* wrong version ? */
434 if(socksreq[0]) {
435 failf(data,
436 "SOCKS4 reply has wrong version, version should be 0.");
437 return CURLPX_BAD_VERSION;
438 }
439
440 /* Result */
441 switch(socksreq[1]) {
442 case 90:
443 infof(data, "SOCKS4%s request granted.", protocol4a?"a":"");
444 break;
445 case 91:
446 failf(data,
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;
453 case 92:
454 failf(data,
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;
462 case 93:
463 failf(data,
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;
471 default:
472 failf(data,
473 "Can't complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)"
474 ", Unknown.",
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;
479 }
480
481 *done = TRUE;
482 return CURLPX_OK; /* Proxy was successful! */
483}
484
485/*
486 * This function logs in to a SOCKS5 proxy and sends the specifics to the final
487 * destination server.
488 */
489CURLproxycode Curl_SOCKS5(const char *proxy_user,
490 const char *proxy_password,
491 const char *hostname,
492 int remote_port,
493 int sockindex,
494 struct Curl_easy *data,
495 bool *done)
496{
497 /*
498 According to the RFC1928, section "6. Replies". This is what a SOCK5
499 replies:
500
501 +----+-----+-------+------+----------+----------+
502 |VER | REP | RSV | ATYP | BND.ADDR | BND.PORT |
503 +----+-----+-------+------+----------+----------+
504 | 1 | 1 | X'00' | 1 | Variable | 2 |
505 +----+-----+-------+------+----------+----------+
506
507 Where:
508
509 o VER protocol version: X'05'
510 o REP Reply field:
511 o X'00' succeeded
512 */
513 struct connectdata *conn = data->conn;
514 unsigned char *socksreq = (unsigned char *)data->state.buffer;
515 char dest[256] = "unknown"; /* printable hostname:port */
516 int idx;
517 ssize_t actualread;
518 ssize_t written;
519 CURLcode result;
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);
524 ssize_t len = 0;
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;
529
530 if(!SOCKS_STATE(sx->state) && !*done)
531 sxstate(data, CONNECT_SOCKS_INIT);
532
533 switch(sx->state) {
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);
538
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;
544 }
545
546 if(auth & ~(CURLAUTH_BASIC | CURLAUTH_GSSAPI))
547 infof(data,
548 "warning: unsupported value passed to CURLOPT_SOCKS5_AUTH: %lu",
549 auth);
550 if(!(auth & CURLAUTH_BASIC))
551 /* disable username/password auth */
552 proxy_user = NULL;
553#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
554 if(auth & CURLAUTH_GSSAPI)
555 allow_gssapi = TRUE;
556#endif
557
558 idx = 0;
559 socksreq[idx++] = 5; /* version */
560 idx++; /* number of authentication methods */
561 socksreq[idx++] = 0; /* no authentication */
562 if(allow_gssapi)
563 socksreq[idx++] = 1; /* GSS-API */
564 if(proxy_user)
565 socksreq[idx++] = 2; /* username/password */
566 /* write the number of authentication methods */
567 socksreq[1] = (unsigned char) (idx - 2);
568
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;
573 }
574 if(written != idx) {
575 sxstate(data, CONNECT_SOCKS_SEND);
576 sx->outstanding = idx - written;
577 sx->outp = &socksreq[written];
578 return CURLPX_OK;
579 }
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;
588 }
589 if(written != sx->outstanding) {
590 /* not done, remain in state */
591 sx->outstanding -= written;
592 sx->outp += written;
593 return CURLPX_OK;
594 }
595 /* FALLTHROUGH */
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 */
600 /* FALLTHROUGH */
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;
607 }
608 else if(!result && !actualread) {
609 /* connection closed */
610 failf(data, "Connection to proxy closed");
611 return CURLPX_CLOSED;
612 }
613 else if(actualread != sx->outstanding) {
614 /* remain in reading state */
615 sx->outstanding -= actualread;
616 sx->outp += actualread;
617 return CURLPX_OK;
618 }
619 else if(socksreq[0] != 5) {
620 failf(data, "Received invalid version in initial SOCKS5 response.");
621 return CURLPX_BAD_VERSION;
622 }
623 else if(socksreq[1] == 0) {
624 /* DONE! No authentication needed. Send request. */
625 sxstate(data, CONNECT_REQ_INIT);
626 goto CONNECT_REQ_INIT;
627 }
628 else if(socksreq[1] == 2) {
629 /* regular name + password authentication */
630 sxstate(data, CONNECT_AUTH_INIT);
631 goto CONNECT_AUTH_INIT;
632 }
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);
637 if(result) {
638 failf(data, "Unable to negotiate SOCKS5 GSS-API context.");
639 return CURLPX_GSSAPI;
640 }
641 }
642#endif
643 else {
644 /* error */
645 if(!allow_gssapi && (socksreq[1] == 1)) {
646 failf(data,
647 "SOCKS5 GSSAPI per-message authentication is not supported.");
648 return CURLPX_GSSAPI_PERMSG;
649 }
650 else if(socksreq[1] == 255) {
651 failf(data, "No authentication method was acceptable.");
652 return CURLPX_NO_AUTH;
653 }
654 }
655 failf(data,
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 */
661 break;
662#endif
663
664 default: /* do nothing! */
665 break;
666
667 CONNECT_AUTH_INIT:
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);
674 }
675 else {
676 proxy_user_len = 0;
677 proxy_password_len = 0;
678 }
679
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 * +----+------+----------+------+----------+
686 */
687 len = 0;
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;
695 }
696 memcpy(socksreq + len, proxy_user, proxy_user_len);
697 }
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;
705 }
706 memcpy(socksreq + len, proxy_password, proxy_password_len);
707 }
708 len += proxy_password_len;
709 sxstate(data, CONNECT_AUTH_SEND);
710 sx->outstanding = len;
711 sx->outp = socksreq;
712 }
713 /* FALLTHROUGH */
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;
720 }
721 if(sx->outstanding != written) {
722 /* remain in state */
723 sx->outstanding -= written;
724 sx->outp += written;
725 return CURLPX_OK;
726 }
727 sx->outp = socksreq;
728 sx->outstanding = 2;
729 sxstate(data, CONNECT_AUTH_READ);
730 /* FALLTHROUGH */
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;
737 }
738 else if(!result && !actualread) {
739 /* connection closed */
740 failf(data, "connection to proxy closed");
741 return CURLPX_CLOSED;
742 }
743 else if(actualread != sx->outstanding) {
744 /* remain in state */
745 sx->outstanding -= actualread;
746 sx->outp += actualread;
747 return CURLPX_OK;
748 }
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;
754 }
755
756 /* Everything is good so far, user was authenticated! */
757 sxstate(data, CONNECT_REQ_INIT);
758 /* FALLTHROUGH */
759 CONNECT_REQ_INIT:
760 case CONNECT_REQ_INIT:
761 if(socks5_resolve_local) {
762 enum resolve_t rc = Curl_resolv(data, hostname, remote_port,
763 FALSE, &dns);
764
765 if(rc == CURLRESOLV_ERROR)
766 return CURLPX_RESOLVE_HOST;
767
768 if(rc == CURLRESOLV_PENDING) {
769 sxstate(data, CONNECT_RESOLVING);
770 return CURLPX_OK;
771 }
772 sxstate(data, CONNECT_RESOLVED);
773 goto CONNECT_RESOLVED;
774 }
775 goto CONNECT_RESOLVE_REMOTE;
776
777 case CONNECT_RESOLVING:
778 /* check if we have the name resolved by now */
779 dns = Curl_fetch_addr(data, hostname, remote_port);
780
781 if(dns) {
782#ifdef CURLRES_ASYNCH
783 data->state.async.dns = dns;
784 data->state.async.done = TRUE;
785#endif
786 infof(data, "SOCKS5: hostname '%s' found", hostname);
787 }
788
789 if(!dns) {
790 result = Curl_resolv_check(data, &dns);
791 if(!dns) {
792 if(result)
793 return CURLPX_RESOLVE_HOST;
794 return CURLPX_OK;
795 }
796 }
797 /* FALLTHROUGH */
798 CONNECT_RESOLVED:
799 case CONNECT_RESOLVED: {
800 struct Curl_addrinfo *hp = NULL;
801 size_t destlen;
802 if(dns)
803 hp = dns->addr;
804 if(!hp) {
805 failf(data, "Failed to resolve \"%s\" for SOCKS5 connect.",
806 hostname);
807 return CURLPX_RESOLVE_HOST;
808 }
809
810 Curl_printable_address(hp, dest, sizeof(dest));
811 destlen = strlen(dest);
812 msnprintf(dest + destlen, sizeof(dest) - destlen, ":%d", remote_port);
813
814 len = 0;
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) {
819 int i;
820 struct sockaddr_in *saddr_in;
821 socksreq[len++] = 1; /* ATYP: IPv4 = 1 */
822
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];
826 }
827
828 infof(data, "SOCKS5 connect to IPv4 %s (locally resolved)", dest);
829 }
830#ifdef ENABLE_IPV6
831 else if(hp->ai_family == AF_INET6) {
832 int i;
833 struct sockaddr_in6 *saddr_in6;
834 socksreq[len++] = 4; /* ATYP: IPv6 = 4 */
835
836 saddr_in6 = (struct sockaddr_in6 *)(void *)hp->ai_addr;
837 for(i = 0; i < 16; i++) {
838 socksreq[len++] =
839 ((unsigned char *)&saddr_in6->sin6_addr.s6_addr)[i];
840 }
841
842 infof(data, "SOCKS5 connect to IPv6 %s (locally resolved)", dest);
843 }
844#endif
845 else {
846 hp = NULL; /* fail! */
847 failf(data, "SOCKS5 connection to %s not supported", dest);
848 }
849
850 Curl_resolv_unlock(data, dns); /* not used anymore from now on */
851 goto CONNECT_REQ_SEND;
852 }
853 CONNECT_RESOLVE_REMOTE:
854 case CONNECT_RESOLVE_REMOTE:
855 /* Authentication is complete, now specify destination to the proxy */
856 len = 0;
857 socksreq[len++] = 5; /* version (SOCKS5) */
858 socksreq[len++] = 1; /* connect */
859 socksreq[len++] = 0; /* must be zero */
860
861 if(!socks5_resolve_local) {
862 /* ATYP: domain name = 3,
863 IPv6 == 4,
864 IPv4 == 1 */
865 unsigned char ip4[4];
866#ifdef ENABLE_IPV6
867 if(conn->bits.ipv6_ip) {
868 char ip6[16];
869 if(1 != Curl_inet_pton(AF_INET6, hostname, ip6))
870 return CURLPX_BAD_ADDRESS_TYPE;
871 socksreq[len++] = 4;
872 memcpy(&socksreq[len], ip6, sizeof(ip6));
873 len += sizeof(ip6);
874 }
875 else
876#endif
877 if(1 == Curl_inet_pton(AF_INET, hostname, ip4)) {
878 socksreq[len++] = 1;
879 memcpy(&socksreq[len], ip4, sizeof(ip4));
880 len += sizeof(ip4);
881 }
882 else {
883 socksreq[len++] = 3;
884 socksreq[len++] = (char) hostname_len; /* one byte address length */
885 memcpy(&socksreq[len], hostname, hostname_len); /* address w/o NULL */
886 len += hostname_len;
887 }
888 infof(data, "SOCKS5 connect to %s:%d (remotely resolved)",
889 hostname, remote_port);
890 }
891 /* FALLTHROUGH */
892
893 CONNECT_REQ_SEND:
894 case CONNECT_REQ_SEND:
895 /* PORT MSB */
896 socksreq[len++] = (unsigned char)((remote_port >> 8) & 0xff);
897 /* PORT LSB */
898 socksreq[len++] = (unsigned char)(remote_port & 0xff);
899
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;
904 }
905#endif
906 sx->outp = socksreq;
907 sx->outstanding = len;
908 sxstate(data, CONNECT_REQ_SENDING);
909 /* FALLTHROUGH */
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;
916 }
917 if(sx->outstanding != written) {
918 /* remain in state */
919 sx->outstanding -= written;
920 sx->outp += written;
921 return CURLPX_OK;
922 }
923#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
924 if(conn->socks5_gssapi_enctype) {
925 failf(data, "SOCKS5 GSS-API protection not yet implemented.");
926 return CURLPX_GSSAPI_PROTECTION;
927 }
928#endif
929 sx->outstanding = 10; /* minimum packet size is 10 */
930 sx->outp = socksreq;
931 sxstate(data, CONNECT_REQ_READ);
932 /* FALLTHROUGH */
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;
939 }
940 else if(!result && !actualread) {
941 /* connection closed */
942 failf(data, "connection to proxy closed");
943 return CURLPX_CLOSED;
944 }
945 else if(actualread != sx->outstanding) {
946 /* remain in state */
947 sx->outstanding -= actualread;
948 sx->outp += actualread;
949 return CURLPX_OK;
950 }
951
952 if(socksreq[0] != 5) { /* version */
953 failf(data,
954 "SOCKS5 reply has wrong version, version should be 5.");
955 return CURLPX_BAD_VERSION;
956 }
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]);
962 if(code < 9) {
963 /* RFC 1928 section 6 lists: */
964 static const CURLproxycode lookup[] = {
965 CURLPX_OK,
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,
974 };
975 rc = lookup[code];
976 }
977 return rc;
978 }
979
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.
983
984 +----+-----+-------+------+----------+----------+
985 |VER | REP | RSV | ATYP | BND.ADDR | BND.PORT |
986 +----+-----+-------+------+----------+----------+
987 | 1 | 1 | X'00' | 1 | Variable | 2 |
988 +----+-----+-------+------+----------+----------+
989
990 ATYP:
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
994 */
995
996 /* Calculate real packet size */
997 if(socksreq[3] == 3) {
998 /* domain name */
999 int addrlen = (int) socksreq[4];
1000 len = 5 + addrlen + 2;
1001 }
1002 else if(socksreq[3] == 4) {
1003 /* IPv6 */
1004 len = 4 + 16 + 2;
1005 }
1006 else if(socksreq[3] == 1) {
1007 len = 4 + 4 + 2;
1008 }
1009 else {
1010 failf(data, "SOCKS5 reply has wrong address type.");
1011 return CURLPX_BAD_ADDRESS_TYPE;
1012 }
1013
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 */
1018#endif
1019 if(len > 10) {
1020 sx->outstanding = len - 10; /* get the rest */
1021 sx->outp = &socksreq[10];
1022 sxstate(data, CONNECT_REQ_READ_MORE);
1023 }
1024 else {
1025 sxstate(data, CONNECT_DONE);
1026 break;
1027 }
1028#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
1029 }
1030#endif
1031 /* FALLTHROUGH */
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;
1038 }
1039 else if(!result && !actualread) {
1040 /* connection closed */
1041 failf(data, "connection to proxy closed");
1042 return CURLPX_CLOSED;
1043 }
1044 else if(actualread != sx->outstanding) {
1045 /* remain in state */
1046 sx->outstanding -= actualread;
1047 sx->outp += actualread;
1048 return CURLPX_OK;
1049 }
1050 sxstate(data, CONNECT_DONE);
1051 }
1052 infof(data, "SOCKS5 request granted.");
1053
1054 *done = TRUE;
1055 return CURLPX_OK; /* Proxy was successful! */
1056}
1057
1058#endif /* CURL_DISABLE_PROXY */
1059