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