1/***************************************************************************
2 * _ _ ____ _
3 * Project ___| | | | _ \| |
4 * / __| | | | |_) | |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
7 *
8 * Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
9 *
10 * This software is licensed as described in the file COPYING, which
11 * you should have received as part of this distribution. The terms
12 * are also available at https://curl.se/docs/copyright.html.
13 *
14 * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15 * copies of the Software, and permit persons to whom the Software is
16 * furnished to do so, under the terms of the COPYING file.
17 *
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
20 *
21 ***************************************************************************/
22
23#include "curl_setup.h"
24
25#if !defined(CURL_DISABLE_PROXY)
26
27#ifdef HAVE_NETINET_IN_H
28#include <netinet/in.h>
29#endif
30#ifdef HAVE_ARPA_INET_H
31#include <arpa/inet.h>
32#endif
33
34#include "urldata.h"
35#include "sendf.h"
36#include "select.h"
37#include "connect.h"
38#include "timeval.h"
39#include "socks.h"
40#include "multiif.h" /* for getsock macros */
41
42/* The last 3 #include files should be in this order */
43#include "curl_printf.h"
44#include "curl_memory.h"
45#include "memdebug.h"
46
47#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
48/*
49 * Helper read-from-socket functions. Does the same as Curl_read() but it
50 * blocks until all bytes amount of buffersize will be read. No more, no less.
51 *
52 * This is STUPID BLOCKING behavior. Only used by the SOCKS GSSAPI functions.
53 */
54int Curl_blockread_all(struct Curl_easy *data, /* transfer */
55 curl_socket_t sockfd, /* read from this socket */
56 char *buf, /* store read data here */
57 ssize_t buffersize, /* max amount to read */
58 ssize_t *n) /* amount bytes read */
59{
60 ssize_t nread = 0;
61 ssize_t allread = 0;
62 int result;
63 *n = 0;
64 for(;;) {
65 timediff_t timeout_ms = Curl_timeleft(data, NULL, TRUE);
66 if(timeout_ms < 0) {
67 /* we already got the timeout */
68 result = CURLE_OPERATION_TIMEDOUT;
69 break;
70 }
71 if(!timeout_ms)
72 timeout_ms = TIMEDIFF_T_MAX;
73 if(SOCKET_READABLE(sockfd, timeout_ms) <= 0) {
74 result = ~CURLE_OK;
75 break;
76 }
77 result = Curl_read_plain(sockfd, buf, buffersize, &nread);
78 if(CURLE_AGAIN == result)
79 continue;
80 if(result)
81 break;
82
83 if(buffersize == nread) {
84 allread += nread;
85 *n = allread;
86 result = CURLE_OK;
87 break;
88 }
89 if(!nread) {
90 result = ~CURLE_OK;
91 break;
92 }
93
94 buffersize -= nread;
95 buf += nread;
96 allread += nread;
97 }
98 return result;
99}
100#endif
101
102#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
103#define DEBUG_AND_VERBOSE
104#define sxstate(x,y) socksstate(x,y, __LINE__)
105#else
106#define sxstate(x,y) socksstate(x,y)
107#endif
108
109/* always use this function to change state, to make debugging easier */
110static void socksstate(struct Curl_easy *data,
111 enum connect_t state
112#ifdef DEBUG_AND_VERBOSE
113 , int lineno
114#endif
115)
116{
117 struct connectdata *conn = data->conn;
118 enum connect_t oldstate = conn->cnnct.state;
119#ifdef DEBUG_AND_VERBOSE
120 /* synced with the state list in urldata.h */
121 static const char * const statename[] = {
122 "INIT",
123 "SOCKS_INIT",
124 "SOCKS_SEND",
125 "SOCKS_READ_INIT",
126 "SOCKS_READ",
127 "GSSAPI_INIT",
128 "AUTH_INIT",
129 "AUTH_SEND",
130 "AUTH_READ",
131 "REQ_INIT",
132 "RESOLVING",
133 "RESOLVED",
134 "RESOLVE_REMOTE",
135 "REQ_SEND",
136 "REQ_SENDING",
137 "REQ_READ",
138 "REQ_READ_MORE",
139 "DONE"
140 };
141#endif
142
143 if(oldstate == state)
144 /* don't bother when the new state is the same as the old state */
145 return;
146
147 conn->cnnct.state = state;
148
149#ifdef DEBUG_AND_VERBOSE
150 infof(data,
151 "SXSTATE: %s => %s conn %p; line %d",
152 statename[oldstate], statename[conn->cnnct.state], conn,
153 lineno);
154#endif
155}
156
157int Curl_SOCKS_getsock(struct connectdata *conn, curl_socket_t *sock,
158 int sockindex)
159{
160 int rc = 0;
161 sock[0] = conn->sock[sockindex];
162 switch(conn->cnnct.state) {
163 case CONNECT_RESOLVING:
164 case CONNECT_SOCKS_READ:
165 case CONNECT_AUTH_READ:
166 case CONNECT_REQ_READ:
167 case CONNECT_REQ_READ_MORE:
168 rc = GETSOCK_READSOCK(0);
169 break;
170 default:
171 rc = GETSOCK_WRITESOCK(0);
172 break;
173 }
174 return rc;
175}
176
177/*
178* This function logs in to a SOCKS4 proxy and sends the specifics to the final
179* destination server.
180*
181* Reference :
182* https://www.openssh.com/txt/socks4.protocol
183*
184* Note :
185* Set protocol4a=true for "SOCKS 4A (Simple Extension to SOCKS 4 Protocol)"
186* Nonsupport "Identification Protocol (RFC1413)"
187*/
188CURLproxycode Curl_SOCKS4(const char *proxy_user,
189 const char *hostname,
190 int remote_port,
191 int sockindex,
192 struct Curl_easy *data,
193 bool *done)
194{
195 struct connectdata *conn = data->conn;
196 const bool protocol4a =
197 (conn->socks_proxy.proxytype == CURLPROXY_SOCKS4A) ? TRUE : FALSE;
198 unsigned char *socksreq = (unsigned char *)data->state.buffer;
199 CURLcode result;
200 curl_socket_t sockfd = conn->sock[sockindex];
201 struct connstate *sx = &conn->cnnct;
202 struct Curl_dns_entry *dns = NULL;
203 ssize_t actualread;
204 ssize_t written;
205
206 /* make sure that the buffer is at least 600 bytes */
207 DEBUGASSERT(READBUFFER_MIN >= 600);
208
209 if(!SOCKS_STATE(sx->state) && !*done)
210 sxstate(data, CONNECT_SOCKS_INIT);
211
212 switch(sx->state) {
213 case CONNECT_SOCKS_INIT:
214 /* SOCKS4 can only do IPv4, insist! */
215 conn->ip_version = CURL_IPRESOLVE_V4;
216 if(conn->bits.httpproxy)
217 infof(data, "SOCKS4%s: connecting to HTTP proxy %s port %d",
218 protocol4a ? "a" : "", hostname, remote_port);
219
220 infof(data, "SOCKS4 communication to %s:%d", hostname, remote_port);
221
222 /*
223 * Compose socks4 request
224 *
225 * Request format
226 *
227 * +----+----+----+----+----+----+----+----+----+----+....+----+
228 * | VN | CD | DSTPORT | DSTIP | USERID |NULL|
229 * +----+----+----+----+----+----+----+----+----+----+....+----+
230 * # of bytes: 1 1 2 4 variable 1
231 */
232
233 socksreq[0] = 4; /* version (SOCKS4) */
234 socksreq[1] = 1; /* connect */
235 socksreq[2] = (unsigned char)((remote_port >> 8) & 0xff); /* PORT MSB */
236 socksreq[3] = (unsigned char)(remote_port & 0xff); /* PORT LSB */
237
238 /* DNS resolve only for SOCKS4, not SOCKS4a */
239 if(!protocol4a) {
240 enum resolve_t rc =
241 Curl_resolv(data, hostname, remote_port, FALSE, &dns);
242
243 if(rc == CURLRESOLV_ERROR)
244 return CURLPX_RESOLVE_HOST;
245 else if(rc == CURLRESOLV_PENDING) {
246 sxstate(data, CONNECT_RESOLVING);
247 infof(data, "SOCKS4 non-blocking resolve of %s", hostname);
248 return CURLPX_OK;
249 }
250 sxstate(data, CONNECT_RESOLVED);
251 goto CONNECT_RESOLVED;
252 }
253
254 /* socks4a doesn't resolve anything locally */
255 sxstate(data, CONNECT_REQ_INIT);
256 goto CONNECT_REQ_INIT;
257
258 case CONNECT_RESOLVING:
259 /* check if we have the name resolved by now */
260 dns = Curl_fetch_addr(data, hostname, (int)conn->port);
261
262 if(dns) {
263#ifdef CURLRES_ASYNCH
264 data->state.async.dns = dns;
265 data->state.async.done = TRUE;
266#endif
267 infof(data, "Hostname '%s' was found", hostname);
268 sxstate(data, CONNECT_RESOLVED);
269 }
270 else {
271 result = Curl_resolv_check(data, &dns);
272 if(!dns) {
273 if(result)
274 return CURLPX_RESOLVE_HOST;
275 return CURLPX_OK;
276 }
277 }
278 /* FALLTHROUGH */
279 CONNECT_RESOLVED:
280 case CONNECT_RESOLVED: {
281 struct Curl_addrinfo *hp = NULL;
282 /*
283 * We cannot use 'hostent' as a struct that Curl_resolv() returns. It
284 * returns a Curl_addrinfo pointer that may not always look the same.
285 */
286 if(dns) {
287 hp = dns->addr;
288
289 /* scan for the first IPv4 address */
290 while(hp && (hp->ai_family != AF_INET))
291 hp = hp->ai_next;
292
293 if(hp) {
294 struct sockaddr_in *saddr_in;
295 char buf[64];
296 Curl_printable_address(hp, buf, sizeof(buf));
297
298 saddr_in = (struct sockaddr_in *)(void *)hp->ai_addr;
299 socksreq[4] = ((unsigned char *)&saddr_in->sin_addr.s_addr)[0];
300 socksreq[5] = ((unsigned char *)&saddr_in->sin_addr.s_addr)[1];
301 socksreq[6] = ((unsigned char *)&saddr_in->sin_addr.s_addr)[2];
302 socksreq[7] = ((unsigned char *)&saddr_in->sin_addr.s_addr)[3];
303
304 infof(data, "SOCKS4 connect to IPv4 %s (locally resolved)", buf);
305
306 Curl_resolv_unlock(data, dns); /* not used anymore from now on */
307 }
308 else
309 failf(data, "SOCKS4 connection to %s not supported", hostname);
310 }
311 else
312 failf(data, "Failed to resolve \"%s\" for SOCKS4 connect.",
313 hostname);
314
315 if(!hp)
316 return CURLPX_RESOLVE_HOST;
317 }
318 /* FALLTHROUGH */
319 CONNECT_REQ_INIT:
320 case CONNECT_REQ_INIT:
321 /*
322 * This is currently not supporting "Identification Protocol (RFC1413)".
323 */
324 socksreq[8] = 0; /* ensure empty userid is NUL-terminated */
325 if(proxy_user) {
326 size_t plen = strlen(proxy_user);
327 if(plen >= (size_t)data->set.buffer_size - 8) {
328 failf(data, "Too long SOCKS proxy user name, can't use!");
329 return CURLPX_LONG_USER;
330 }
331 /* copy the proxy name WITH trailing zero */
332 memcpy(socksreq + 8, proxy_user, plen + 1);
333 }
334
335 /*
336 * Make connection
337 */
338 {
339 size_t packetsize = 9 +
340 strlen((char *)socksreq + 8); /* size including NUL */
341
342 /* If SOCKS4a, set special invalid IP address 0.0.0.x */
343 if(protocol4a) {
344 size_t hostnamelen = 0;
345 socksreq[4] = 0;
346 socksreq[5] = 0;
347 socksreq[6] = 0;
348 socksreq[7] = 1;
349 /* append hostname */
350 hostnamelen = strlen(hostname) + 1; /* length including NUL */
351 if(hostnamelen <= 255)
352 strcpy((char *)socksreq + packetsize, hostname);
353 else {
354 failf(data, "SOCKS4: too long host name");
355 return CURLPX_LONG_HOSTNAME;
356 }
357 packetsize += hostnamelen;
358 }
359 sx->outp = socksreq;
360 sx->outstanding = packetsize;
361 sxstate(data, CONNECT_REQ_SENDING);
362 }
363 /* FALLTHROUGH */
364 case CONNECT_REQ_SENDING:
365 /* Send request */
366 result = Curl_write_plain(data, sockfd, (char *)sx->outp,
367 sx->outstanding, &written);
368 if(result && (CURLE_AGAIN != result)) {
369 failf(data, "Failed to send SOCKS4 connect request.");
370 return CURLPX_SEND_CONNECT;
371 }
372 if(written != sx->outstanding) {
373 /* not done, remain in state */
374 sx->outstanding -= written;
375 sx->outp += written;
376 return CURLPX_OK;
377 }
378
379 /* done sending! */
380 sx->outstanding = 8; /* receive data size */
381 sx->outp = socksreq;
382 sxstate(data, CONNECT_SOCKS_READ);
383
384 /* FALLTHROUGH */
385 case CONNECT_SOCKS_READ:
386 /* Receive response */
387 result = Curl_read_plain(sockfd, (char *)sx->outp,
388 sx->outstanding, &actualread);
389 if(result && (CURLE_AGAIN != result)) {
390 failf(data, "SOCKS4: Failed receiving connect request ack: %s",
391 curl_easy_strerror(result));
392 return CURLPX_RECV_CONNECT;
393 }
394 else if(!result && !actualread) {
395 /* connection closed */
396 failf(data, "connection to proxy closed");
397 return CURLPX_CLOSED;
398 }
399 else if(actualread != sx->outstanding) {
400 /* remain in reading state */
401 sx->outstanding -= actualread;
402 sx->outp += actualread;
403 return CURLPX_OK;
404 }
405 sxstate(data, CONNECT_DONE);
406 break;
407 default: /* lots of unused states in SOCKS4 */
408 break;
409 }
410
411 /*
412 * Response format
413 *
414 * +----+----+----+----+----+----+----+----+
415 * | VN | CD | DSTPORT | DSTIP |
416 * +----+----+----+----+----+----+----+----+
417 * # of bytes: 1 1 2 4
418 *
419 * VN is the version of the reply code and should be 0. CD is the result
420 * code with one of the following values:
421 *
422 * 90: request granted
423 * 91: request rejected or failed
424 * 92: request rejected because SOCKS server cannot connect to
425 * identd on the client
426 * 93: request rejected because the client program and identd
427 * report different user-ids
428 */
429
430 /* wrong version ? */
431 if(socksreq[0]) {
432 failf(data,
433 "SOCKS4 reply has wrong version, version should be 0.");
434 return CURLPX_BAD_VERSION;
435 }
436
437 /* Result */
438 switch(socksreq[1]) {
439 case 90:
440 infof(data, "SOCKS4%s request granted.", protocol4a?"a":"");
441 break;
442 case 91:
443 failf(data,
444 "Can't complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)"
445 ", request rejected or failed.",
446 socksreq[4], socksreq[5], socksreq[6], socksreq[7],
447 (((unsigned char)socksreq[2] << 8) | (unsigned char)socksreq[3]),
448 (unsigned char)socksreq[1]);
449 return CURLPX_REQUEST_FAILED;
450 case 92:
451 failf(data,
452 "Can't complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)"
453 ", request rejected because SOCKS server cannot connect to "
454 "identd on the client.",
455 socksreq[4], socksreq[5], socksreq[6], socksreq[7],
456 (((unsigned char)socksreq[2] << 8) | (unsigned char)socksreq[3]),
457 (unsigned char)socksreq[1]);
458 return CURLPX_IDENTD;
459 case 93:
460 failf(data,
461 "Can't complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)"
462 ", request rejected because the client program and identd "
463 "report different user-ids.",
464 socksreq[4], socksreq[5], socksreq[6], socksreq[7],
465 (((unsigned char)socksreq[2] << 8) | (unsigned char)socksreq[3]),
466 (unsigned char)socksreq[1]);
467 return CURLPX_IDENTD_DIFFER;
468 default:
469 failf(data,
470 "Can't complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)"
471 ", Unknown.",
472 socksreq[4], socksreq[5], socksreq[6], socksreq[7],
473 (((unsigned char)socksreq[2] << 8) | (unsigned char)socksreq[3]),
474 (unsigned char)socksreq[1]);
475 return CURLPX_UNKNOWN_FAIL;
476 }
477
478 *done = TRUE;
479 return CURLPX_OK; /* Proxy was successful! */
480}
481
482/*
483 * This function logs in to a SOCKS5 proxy and sends the specifics to the final
484 * destination server.
485 */
486CURLproxycode Curl_SOCKS5(const char *proxy_user,
487 const char *proxy_password,
488 const char *hostname,
489 int remote_port,
490 int sockindex,
491 struct Curl_easy *data,
492 bool *done)
493{
494 /*
495 According to the RFC1928, section "6. Replies". This is what a SOCK5
496 replies:
497
498 +----+-----+-------+------+----------+----------+
499 |VER | REP | RSV | ATYP | BND.ADDR | BND.PORT |
500 +----+-----+-------+------+----------+----------+
501 | 1 | 1 | X'00' | 1 | Variable | 2 |
502 +----+-----+-------+------+----------+----------+
503
504 Where:
505
506 o VER protocol version: X'05'
507 o REP Reply field:
508 o X'00' succeeded
509 */
510 struct connectdata *conn = data->conn;
511 unsigned char *socksreq = (unsigned char *)data->state.buffer;
512 char dest[256] = "unknown"; /* printable hostname:port */
513 int idx;
514 ssize_t actualread;
515 ssize_t written;
516 CURLcode result;
517 curl_socket_t sockfd = conn->sock[sockindex];
518 bool socks5_resolve_local =
519 (conn->socks_proxy.proxytype == CURLPROXY_SOCKS5) ? TRUE : FALSE;
520 const size_t hostname_len = strlen(hostname);
521 ssize_t len = 0;
522 const unsigned long auth = data->set.socks5auth;
523 bool allow_gssapi = FALSE;
524 struct connstate *sx = &conn->cnnct;
525 struct Curl_dns_entry *dns = NULL;
526
527 if(!SOCKS_STATE(sx->state) && !*done)
528 sxstate(data, CONNECT_SOCKS_INIT);
529
530 switch(sx->state) {
531 case CONNECT_SOCKS_INIT:
532 if(conn->bits.httpproxy)
533 infof(data, "SOCKS5: connecting to HTTP proxy %s port %d",
534 hostname, remote_port);
535
536 /* RFC1928 chapter 5 specifies max 255 chars for domain name in packet */
537 if(!socks5_resolve_local && hostname_len > 255) {
538 infof(data, "SOCKS5: server resolving disabled for hostnames of "
539 "length > 255 [actual len=%zu]", hostname_len);
540 socks5_resolve_local = TRUE;
541 }
542
543 if(auth & ~(CURLAUTH_BASIC | CURLAUTH_GSSAPI))
544 infof(data,
545 "warning: unsupported value passed to CURLOPT_SOCKS5_AUTH: %lu",
546 auth);
547 if(!(auth & CURLAUTH_BASIC))
548 /* disable username/password auth */
549 proxy_user = NULL;
550#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
551 if(auth & CURLAUTH_GSSAPI)
552 allow_gssapi = TRUE;
553#endif
554
555 idx = 0;
556 socksreq[idx++] = 5; /* version */
557 idx++; /* number of authentication methods */
558 socksreq[idx++] = 0; /* no authentication */
559 if(allow_gssapi)
560 socksreq[idx++] = 1; /* GSS-API */
561 if(proxy_user)
562 socksreq[idx++] = 2; /* username/password */
563 /* write the number of authentication methods */
564 socksreq[1] = (unsigned char) (idx - 2);
565
566 result = Curl_write_plain(data, sockfd, (char *)socksreq, idx, &written);
567 if(result && (CURLE_AGAIN != result)) {
568 failf(data, "Unable to send initial SOCKS5 request.");
569 return CURLPX_SEND_CONNECT;
570 }
571 if(written != idx) {
572 sxstate(data, CONNECT_SOCKS_SEND);
573 sx->outstanding = idx - written;
574 sx->outp = &socksreq[written];
575 return CURLPX_OK;
576 }
577 sxstate(data, CONNECT_SOCKS_READ);
578 goto CONNECT_SOCKS_READ_INIT;
579 case CONNECT_SOCKS_SEND:
580 result = Curl_write_plain(data, sockfd, (char *)sx->outp,
581 sx->outstanding, &written);
582 if(result && (CURLE_AGAIN != result)) {
583 failf(data, "Unable to send initial SOCKS5 request.");
584 return CURLPX_SEND_CONNECT;
585 }
586 if(written != sx->outstanding) {
587 /* not done, remain in state */
588 sx->outstanding -= written;
589 sx->outp += written;
590 return CURLPX_OK;
591 }
592 /* FALLTHROUGH */
593 CONNECT_SOCKS_READ_INIT:
594 case CONNECT_SOCKS_READ_INIT:
595 sx->outstanding = 2; /* expect two bytes */
596 sx->outp = socksreq; /* store it here */
597 /* FALLTHROUGH */
598 case CONNECT_SOCKS_READ:
599 result = Curl_read_plain(sockfd, (char *)sx->outp,
600 sx->outstanding, &actualread);
601 if(result && (CURLE_AGAIN != result)) {
602 failf(data, "Unable to receive initial SOCKS5 response.");
603 return CURLPX_RECV_CONNECT;
604 }
605 else if(!result && !actualread) {
606 /* connection closed */
607 failf(data, "Connection to proxy closed");
608 return CURLPX_CLOSED;
609 }
610 else if(actualread != sx->outstanding) {
611 /* remain in reading state */
612 sx->outstanding -= actualread;
613 sx->outp += actualread;
614 return CURLPX_OK;
615 }
616 else if(socksreq[0] != 5) {
617 failf(data, "Received invalid version in initial SOCKS5 response.");
618 return CURLPX_BAD_VERSION;
619 }
620 else if(socksreq[1] == 0) {
621 /* DONE! No authentication needed. Send request. */
622 sxstate(data, CONNECT_REQ_INIT);
623 goto CONNECT_REQ_INIT;
624 }
625 else if(socksreq[1] == 2) {
626 /* regular name + password authentication */
627 sxstate(data, CONNECT_AUTH_INIT);
628 goto CONNECT_AUTH_INIT;
629 }
630#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
631 else if(allow_gssapi && (socksreq[1] == 1)) {
632 sxstate(data, CONNECT_GSSAPI_INIT);
633 result = Curl_SOCKS5_gssapi_negotiate(sockindex, data);
634 if(result) {
635 failf(data, "Unable to negotiate SOCKS5 GSS-API context.");
636 return CURLPX_GSSAPI;
637 }
638 }
639#endif
640 else {
641 /* error */
642 if(!allow_gssapi && (socksreq[1] == 1)) {
643 failf(data,
644 "SOCKS5 GSSAPI per-message authentication is not supported.");
645 return CURLPX_GSSAPI_PERMSG;
646 }
647 else if(socksreq[1] == 255) {
648 failf(data, "No authentication method was acceptable.");
649 return CURLPX_NO_AUTH;
650 }
651 }
652 failf(data,
653 "Undocumented SOCKS5 mode attempted to be used by server.");
654 return CURLPX_UNKNOWN_MODE;
655#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
656 case CONNECT_GSSAPI_INIT:
657 /* GSSAPI stuff done non-blocking */
658 break;
659#endif
660
661 default: /* do nothing! */
662 break;
663
664 CONNECT_AUTH_INIT:
665 case CONNECT_AUTH_INIT: {
666 /* Needs user name and password */
667 size_t proxy_user_len, proxy_password_len;
668 if(proxy_user && proxy_password) {
669 proxy_user_len = strlen(proxy_user);
670 proxy_password_len = strlen(proxy_password);
671 }
672 else {
673 proxy_user_len = 0;
674 proxy_password_len = 0;
675 }
676
677 /* username/password request looks like
678 * +----+------+----------+------+----------+
679 * |VER | ULEN | UNAME | PLEN | PASSWD |
680 * +----+------+----------+------+----------+
681 * | 1 | 1 | 1 to 255 | 1 | 1 to 255 |
682 * +----+------+----------+------+----------+
683 */
684 len = 0;
685 socksreq[len++] = 1; /* username/pw subnegotiation version */
686 socksreq[len++] = (unsigned char) proxy_user_len;
687 if(proxy_user && proxy_user_len) {
688 /* the length must fit in a single byte */
689 if(proxy_user_len >= 255) {
690 failf(data, "Excessive user name length for proxy auth");
691 return CURLPX_LONG_USER;
692 }
693 memcpy(socksreq + len, proxy_user, proxy_user_len);
694 }
695 len += proxy_user_len;
696 socksreq[len++] = (unsigned char) proxy_password_len;
697 if(proxy_password && proxy_password_len) {
698 /* the length must fit in a single byte */
699 if(proxy_password_len > 255) {
700 failf(data, "Excessive password length for proxy auth");
701 return CURLPX_LONG_PASSWD;
702 }
703 memcpy(socksreq + len, proxy_password, proxy_password_len);
704 }
705 len += proxy_password_len;
706 sxstate(data, CONNECT_AUTH_SEND);
707 sx->outstanding = len;
708 sx->outp = socksreq;
709 }
710 /* FALLTHROUGH */
711 case CONNECT_AUTH_SEND:
712 result = Curl_write_plain(data, sockfd, (char *)sx->outp,
713 sx->outstanding, &written);
714 if(result && (CURLE_AGAIN != result)) {
715 failf(data, "Failed to send SOCKS5 sub-negotiation request.");
716 return CURLPX_SEND_AUTH;
717 }
718 if(sx->outstanding != written) {
719 /* remain in state */
720 sx->outstanding -= written;
721 sx->outp += written;
722 return CURLPX_OK;
723 }
724 sx->outp = socksreq;
725 sx->outstanding = 2;
726 sxstate(data, CONNECT_AUTH_READ);
727 /* FALLTHROUGH */
728 case CONNECT_AUTH_READ:
729 result = Curl_read_plain(sockfd, (char *)sx->outp,
730 sx->outstanding, &actualread);
731 if(result && (CURLE_AGAIN != result)) {
732 failf(data, "Unable to receive SOCKS5 sub-negotiation response.");
733 return CURLPX_RECV_AUTH;
734 }
735 else if(!result && !actualread) {
736 /* connection closed */
737 failf(data, "connection to proxy closed");
738 return CURLPX_CLOSED;
739 }
740 else if(actualread != sx->outstanding) {
741 /* remain in state */
742 sx->outstanding -= actualread;
743 sx->outp += actualread;
744 return CURLPX_OK;
745 }
746 /* ignore the first (VER) byte */
747 else if(socksreq[1]) { /* status */
748 failf(data, "User was rejected by the SOCKS5 server (%d %d).",
749 socksreq[0], socksreq[1]);
750 return CURLPX_USER_REJECTED;
751 }
752
753 /* Everything is good so far, user was authenticated! */
754 sxstate(data, CONNECT_REQ_INIT);
755 /* FALLTHROUGH */
756 CONNECT_REQ_INIT:
757 case CONNECT_REQ_INIT:
758 if(socks5_resolve_local) {
759 enum resolve_t rc = Curl_resolv(data, hostname, remote_port,
760 FALSE, &dns);
761
762 if(rc == CURLRESOLV_ERROR)
763 return CURLPX_RESOLVE_HOST;
764
765 if(rc == CURLRESOLV_PENDING) {
766 sxstate(data, CONNECT_RESOLVING);
767 return CURLPX_OK;
768 }
769 sxstate(data, CONNECT_RESOLVED);
770 goto CONNECT_RESOLVED;
771 }
772 goto CONNECT_RESOLVE_REMOTE;
773
774 case CONNECT_RESOLVING:
775 /* check if we have the name resolved by now */
776 dns = Curl_fetch_addr(data, hostname, remote_port);
777
778 if(dns) {
779#ifdef CURLRES_ASYNCH
780 data->state.async.dns = dns;
781 data->state.async.done = TRUE;
782#endif
783 infof(data, "SOCKS5: hostname '%s' found", hostname);
784 }
785
786 if(!dns) {
787 result = Curl_resolv_check(data, &dns);
788 if(!dns) {
789 if(result)
790 return CURLPX_RESOLVE_HOST;
791 return CURLPX_OK;
792 }
793 }
794 /* FALLTHROUGH */
795 CONNECT_RESOLVED:
796 case CONNECT_RESOLVED: {
797 struct Curl_addrinfo *hp = NULL;
798 size_t destlen;
799 if(dns)
800 hp = dns->addr;
801 if(!hp) {
802 failf(data, "Failed to resolve \"%s\" for SOCKS5 connect.",
803 hostname);
804 return CURLPX_RESOLVE_HOST;
805 }
806
807 Curl_printable_address(hp, dest, sizeof(dest));
808 destlen = strlen(dest);
809 msnprintf(dest + destlen, sizeof(dest) - destlen, ":%d", remote_port);
810
811 len = 0;
812 socksreq[len++] = 5; /* version (SOCKS5) */
813 socksreq[len++] = 1; /* connect */
814 socksreq[len++] = 0; /* must be zero */
815 if(hp->ai_family == AF_INET) {
816 int i;
817 struct sockaddr_in *saddr_in;
818 socksreq[len++] = 1; /* ATYP: IPv4 = 1 */
819
820 saddr_in = (struct sockaddr_in *)(void *)hp->ai_addr;
821 for(i = 0; i < 4; i++) {
822 socksreq[len++] = ((unsigned char *)&saddr_in->sin_addr.s_addr)[i];
823 }
824
825 infof(data, "SOCKS5 connect to IPv4 %s (locally resolved)", dest);
826 }
827#ifdef ENABLE_IPV6
828 else if(hp->ai_family == AF_INET6) {
829 int i;
830 struct sockaddr_in6 *saddr_in6;
831 socksreq[len++] = 4; /* ATYP: IPv6 = 4 */
832
833 saddr_in6 = (struct sockaddr_in6 *)(void *)hp->ai_addr;
834 for(i = 0; i < 16; i++) {
835 socksreq[len++] =
836 ((unsigned char *)&saddr_in6->sin6_addr.s6_addr)[i];
837 }
838
839 infof(data, "SOCKS5 connect to IPv6 %s (locally resolved)", dest);
840 }
841#endif
842 else {
843 hp = NULL; /* fail! */
844 failf(data, "SOCKS5 connection to %s not supported", dest);
845 }
846
847 Curl_resolv_unlock(data, dns); /* not used anymore from now on */
848 goto CONNECT_REQ_SEND;
849 }
850 CONNECT_RESOLVE_REMOTE:
851 case CONNECT_RESOLVE_REMOTE:
852 /* Authentication is complete, now specify destination to the proxy */
853 len = 0;
854 socksreq[len++] = 5; /* version (SOCKS5) */
855 socksreq[len++] = 1; /* connect */
856 socksreq[len++] = 0; /* must be zero */
857
858 if(!socks5_resolve_local) {
859 socksreq[len++] = 3; /* ATYP: domain name = 3 */
860 socksreq[len++] = (char) hostname_len; /* one byte address length */
861 memcpy(&socksreq[len], hostname, hostname_len); /* address w/o NULL */
862 len += hostname_len;
863 infof(data, "SOCKS5 connect to %s:%d (remotely resolved)",
864 hostname, remote_port);
865 }
866 /* FALLTHROUGH */
867
868 CONNECT_REQ_SEND:
869 case CONNECT_REQ_SEND:
870 /* PORT MSB */
871 socksreq[len++] = (unsigned char)((remote_port >> 8) & 0xff);
872 /* PORT LSB */
873 socksreq[len++] = (unsigned char)(remote_port & 0xff);
874
875#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
876 if(conn->socks5_gssapi_enctype) {
877 failf(data, "SOCKS5 GSS-API protection not yet implemented.");
878 return CURLPX_GSSAPI_PROTECTION;
879 }
880#endif
881 sx->outp = socksreq;
882 sx->outstanding = len;
883 sxstate(data, CONNECT_REQ_SENDING);
884 /* FALLTHROUGH */
885 case CONNECT_REQ_SENDING:
886 result = Curl_write_plain(data, sockfd, (char *)sx->outp,
887 sx->outstanding, &written);
888 if(result && (CURLE_AGAIN != result)) {
889 failf(data, "Failed to send SOCKS5 connect request.");
890 return CURLPX_SEND_REQUEST;
891 }
892 if(sx->outstanding != written) {
893 /* remain in state */
894 sx->outstanding -= written;
895 sx->outp += written;
896 return CURLPX_OK;
897 }
898#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
899 if(conn->socks5_gssapi_enctype) {
900 failf(data, "SOCKS5 GSS-API protection not yet implemented.");
901 return CURLPX_GSSAPI_PROTECTION;
902 }
903#endif
904 sx->outstanding = 10; /* minimum packet size is 10 */
905 sx->outp = socksreq;
906 sxstate(data, CONNECT_REQ_READ);
907 /* FALLTHROUGH */
908 case CONNECT_REQ_READ:
909 result = Curl_read_plain(sockfd, (char *)sx->outp,
910 sx->outstanding, &actualread);
911 if(result && (CURLE_AGAIN != result)) {
912 failf(data, "Failed to receive SOCKS5 connect request ack.");
913 return CURLPX_RECV_REQACK;
914 }
915 else if(!result && !actualread) {
916 /* connection closed */
917 failf(data, "connection to proxy closed");
918 return CURLPX_CLOSED;
919 }
920 else if(actualread != sx->outstanding) {
921 /* remain in state */
922 sx->outstanding -= actualread;
923 sx->outp += actualread;
924 return CURLPX_OK;
925 }
926
927 if(socksreq[0] != 5) { /* version */
928 failf(data,
929 "SOCKS5 reply has wrong version, version should be 5.");
930 return CURLPX_BAD_VERSION;
931 }
932 else if(socksreq[1]) { /* Anything besides 0 is an error */
933 CURLproxycode rc = CURLPX_REPLY_UNASSIGNED;
934 int code = socksreq[1];
935 failf(data, "Can't complete SOCKS5 connection to %s. (%d)",
936 hostname, (unsigned char)socksreq[1]);
937 if(code < 9) {
938 /* RFC 1928 section 6 lists: */
939 static const CURLproxycode lookup[] = {
940 CURLPX_OK,
941 CURLPX_REPLY_GENERAL_SERVER_FAILURE,
942 CURLPX_REPLY_NOT_ALLOWED,
943 CURLPX_REPLY_NETWORK_UNREACHABLE,
944 CURLPX_REPLY_HOST_UNREACHABLE,
945 CURLPX_REPLY_CONNECTION_REFUSED,
946 CURLPX_REPLY_TTL_EXPIRED,
947 CURLPX_REPLY_COMMAND_NOT_SUPPORTED,
948 CURLPX_REPLY_ADDRESS_TYPE_NOT_SUPPORTED,
949 };
950 rc = lookup[code];
951 }
952 return rc;
953 }
954
955 /* Fix: in general, returned BND.ADDR is variable length parameter by RFC
956 1928, so the reply packet should be read until the end to avoid errors
957 at subsequent protocol level.
958
959 +----+-----+-------+------+----------+----------+
960 |VER | REP | RSV | ATYP | BND.ADDR | BND.PORT |
961 +----+-----+-------+------+----------+----------+
962 | 1 | 1 | X'00' | 1 | Variable | 2 |
963 +----+-----+-------+------+----------+----------+
964
965 ATYP:
966 o IP v4 address: X'01', BND.ADDR = 4 byte
967 o domain name: X'03', BND.ADDR = [ 1 byte length, string ]
968 o IP v6 address: X'04', BND.ADDR = 16 byte
969 */
970
971 /* Calculate real packet size */
972 if(socksreq[3] == 3) {
973 /* domain name */
974 int addrlen = (int) socksreq[4];
975 len = 5 + addrlen + 2;
976 }
977 else if(socksreq[3] == 4) {
978 /* IPv6 */
979 len = 4 + 16 + 2;
980 }
981 else if(socksreq[3] == 1) {
982 len = 4 + 4 + 2;
983 }
984 else {
985 failf(data, "SOCKS5 reply has wrong address type.");
986 return CURLPX_BAD_ADDRESS_TYPE;
987 }
988
989 /* At this point we already read first 10 bytes */
990#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
991 if(!conn->socks5_gssapi_enctype) {
992 /* decrypt_gssapi_blockread already read the whole packet */
993#endif
994 if(len > 10) {
995 sx->outstanding = len - 10; /* get the rest */
996 sx->outp = &socksreq[10];
997 sxstate(data, CONNECT_REQ_READ_MORE);
998 }
999 else {
1000 sxstate(data, CONNECT_DONE);
1001 break;
1002 }
1003#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
1004 }
1005#endif
1006 /* FALLTHROUGH */
1007 case CONNECT_REQ_READ_MORE:
1008 result = Curl_read_plain(sockfd, (char *)sx->outp,
1009 sx->outstanding, &actualread);
1010 if(result && (CURLE_AGAIN != result)) {
1011 failf(data, "Failed to receive SOCKS5 connect request ack.");
1012 return CURLPX_RECV_ADDRESS;
1013 }
1014 else if(!result && !actualread) {
1015 /* connection closed */
1016 failf(data, "connection to proxy closed");
1017 return CURLPX_CLOSED;
1018 }
1019 else if(actualread != sx->outstanding) {
1020 /* remain in state */
1021 sx->outstanding -= actualread;
1022 sx->outp += actualread;
1023 return CURLPX_OK;
1024 }
1025 sxstate(data, CONNECT_DONE);
1026 }
1027 infof(data, "SOCKS5 request granted.");
1028
1029 *done = TRUE;
1030 return CURLPX_OK; /* Proxy was successful! */
1031}
1032
1033#endif /* CURL_DISABLE_PROXY */
1034