1/***************************************************************************
2 * _ _ ____ _
3 * Project ___| | | | _ \| |
4 * / __| | | | |_) | |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
7 *
8 * Copyright (C) 1998 - 2019, 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.haxx.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
41/* The last 3 #include files should be in this order */
42#include "curl_printf.h"
43#include "curl_memory.h"
44#include "memdebug.h"
45
46/*
47 * Helper read-from-socket functions. Does the same as Curl_read() but it
48 * blocks until all bytes amount of buffersize will be read. No more, no less.
49 *
50 * This is STUPID BLOCKING behaviour which we frown upon, but right now this
51 * is what we have...
52 */
53int Curl_blockread_all(struct connectdata *conn, /* connection data */
54 curl_socket_t sockfd, /* read from this socket */
55 char *buf, /* store read data here */
56 ssize_t buffersize, /* max amount to read */
57 ssize_t *n) /* amount bytes read */
58{
59 ssize_t nread = 0;
60 ssize_t allread = 0;
61 int result;
62 *n = 0;
63 for(;;) {
64 timediff_t timeleft = Curl_timeleft(conn->data, NULL, TRUE);
65 if(timeleft < 0) {
66 /* we already got the timeout */
67 result = CURLE_OPERATION_TIMEDOUT;
68 break;
69 }
70 if(SOCKET_READABLE(sockfd, timeleft) <= 0) {
71 result = ~CURLE_OK;
72 break;
73 }
74 result = Curl_read_plain(sockfd, buf, buffersize, &nread);
75 if(CURLE_AGAIN == result)
76 continue;
77 if(result)
78 break;
79
80 if(buffersize == nread) {
81 allread += nread;
82 *n = allread;
83 result = CURLE_OK;
84 break;
85 }
86 if(!nread) {
87 result = ~CURLE_OK;
88 break;
89 }
90
91 buffersize -= nread;
92 buf += nread;
93 allread += nread;
94 }
95 return result;
96}
97
98/*
99* This function logs in to a SOCKS4 proxy and sends the specifics to the final
100* destination server.
101*
102* Reference :
103* https://www.openssh.com/txt/socks4.protocol
104*
105* Note :
106* Set protocol4a=true for "SOCKS 4A (Simple Extension to SOCKS 4 Protocol)"
107* Nonsupport "Identification Protocol (RFC1413)"
108*/
109CURLcode Curl_SOCKS4(const char *proxy_user,
110 const char *hostname,
111 int remote_port,
112 int sockindex,
113 struct connectdata *conn)
114{
115 const bool protocol4a =
116 (conn->socks_proxy.proxytype == CURLPROXY_SOCKS4A) ? TRUE : FALSE;
117#define SOCKS4REQLEN 262
118 unsigned char socksreq[SOCKS4REQLEN]; /* room for SOCKS4 request incl. user
119 id */
120 CURLcode code;
121 curl_socket_t sock = conn->sock[sockindex];
122 struct Curl_easy *data = conn->data;
123
124 if(Curl_timeleft(data, NULL, TRUE) < 0) {
125 /* time-out, bail out, go home */
126 failf(data, "Connection time-out");
127 return CURLE_OPERATION_TIMEDOUT;
128 }
129
130 if(conn->bits.httpproxy)
131 infof(conn->data, "SOCKS4%s: connecting to HTTP proxy %s port %d\n",
132 protocol4a ? "a" : "", hostname, remote_port);
133
134 (void)curlx_nonblock(sock, FALSE);
135
136 infof(data, "SOCKS4 communication to %s:%d\n", hostname, remote_port);
137
138 /*
139 * Compose socks4 request
140 *
141 * Request format
142 *
143 * +----+----+----+----+----+----+----+----+----+----+....+----+
144 * | VN | CD | DSTPORT | DSTIP | USERID |NULL|
145 * +----+----+----+----+----+----+----+----+----+----+....+----+
146 * # of bytes: 1 1 2 4 variable 1
147 */
148
149 socksreq[0] = 4; /* version (SOCKS4) */
150 socksreq[1] = 1; /* connect */
151 socksreq[2] = (unsigned char)((remote_port >> 8) & 0xff); /* PORT MSB */
152 socksreq[3] = (unsigned char)(remote_port & 0xff); /* PORT LSB */
153
154 /* DNS resolve only for SOCKS4, not SOCKS4a */
155 if(!protocol4a) {
156 struct Curl_dns_entry *dns;
157 Curl_addrinfo *hp = NULL;
158 int rc;
159
160 rc = Curl_resolv(conn, hostname, remote_port, FALSE, &dns);
161
162 if(rc == CURLRESOLV_ERROR)
163 return CURLE_COULDNT_RESOLVE_PROXY;
164
165 if(rc == CURLRESOLV_PENDING)
166 /* ignores the return code, but 'dns' remains NULL on failure */
167 (void)Curl_resolver_wait_resolv(conn, &dns);
168
169 /*
170 * We cannot use 'hostent' as a struct that Curl_resolv() returns. It
171 * returns a Curl_addrinfo pointer that may not always look the same.
172 */
173 if(dns)
174 hp = dns->addr;
175 if(hp) {
176 char buf[64];
177 Curl_printable_address(hp, buf, sizeof(buf));
178
179 if(hp->ai_family == AF_INET) {
180 struct sockaddr_in *saddr_in;
181
182 saddr_in = (struct sockaddr_in *)(void *)hp->ai_addr;
183 socksreq[4] = ((unsigned char *)&saddr_in->sin_addr.s_addr)[0];
184 socksreq[5] = ((unsigned char *)&saddr_in->sin_addr.s_addr)[1];
185 socksreq[6] = ((unsigned char *)&saddr_in->sin_addr.s_addr)[2];
186 socksreq[7] = ((unsigned char *)&saddr_in->sin_addr.s_addr)[3];
187
188 infof(data, "SOCKS4 connect to IPv4 %s (locally resolved)\n", buf);
189 }
190 else {
191 hp = NULL; /* fail! */
192
193 failf(data, "SOCKS4 connection to %s not supported\n", buf);
194 }
195
196 Curl_resolv_unlock(data, dns); /* not used anymore from now on */
197 }
198 if(!hp) {
199 failf(data, "Failed to resolve \"%s\" for SOCKS4 connect.",
200 hostname);
201 return CURLE_COULDNT_RESOLVE_HOST;
202 }
203 }
204
205 /*
206 * This is currently not supporting "Identification Protocol (RFC1413)".
207 */
208 socksreq[8] = 0; /* ensure empty userid is NUL-terminated */
209 if(proxy_user) {
210 size_t plen = strlen(proxy_user);
211 if(plen >= sizeof(socksreq) - 8) {
212 failf(data, "Too long SOCKS proxy name, can't use!\n");
213 return CURLE_COULDNT_CONNECT;
214 }
215 /* copy the proxy name WITH trailing zero */
216 memcpy(socksreq + 8, proxy_user, plen + 1);
217 }
218
219 /*
220 * Make connection
221 */
222 {
223 int result;
224 ssize_t actualread;
225 ssize_t written;
226 ssize_t hostnamelen = 0;
227 ssize_t packetsize = 9 +
228 strlen((char *)socksreq + 8); /* size including NUL */
229
230 /* If SOCKS4a, set special invalid IP address 0.0.0.x */
231 if(protocol4a) {
232 socksreq[4] = 0;
233 socksreq[5] = 0;
234 socksreq[6] = 0;
235 socksreq[7] = 1;
236 /* If still enough room in buffer, also append hostname */
237 hostnamelen = (ssize_t)strlen(hostname) + 1; /* length including NUL */
238 if(packetsize + hostnamelen <= SOCKS4REQLEN)
239 strcpy((char *)socksreq + packetsize, hostname);
240 else
241 hostnamelen = 0; /* Flag: hostname did not fit in buffer */
242 }
243
244 /* Send request */
245 code = Curl_write_plain(conn, sock, (char *)socksreq,
246 packetsize + hostnamelen,
247 &written);
248 if(code || (written != packetsize + hostnamelen)) {
249 failf(data, "Failed to send SOCKS4 connect request.");
250 return CURLE_COULDNT_CONNECT;
251 }
252 if(protocol4a && hostnamelen == 0) {
253 /* SOCKS4a with very long hostname - send that name separately */
254 hostnamelen = (ssize_t)strlen(hostname) + 1;
255 code = Curl_write_plain(conn, sock, (char *)hostname, hostnamelen,
256 &written);
257 if(code || (written != hostnamelen)) {
258 failf(data, "Failed to send SOCKS4 connect request.");
259 return CURLE_COULDNT_CONNECT;
260 }
261 }
262
263 packetsize = 8; /* receive data size */
264
265 /* Receive response */
266 result = Curl_blockread_all(conn, sock, (char *)socksreq, packetsize,
267 &actualread);
268 if(result || (actualread != packetsize)) {
269 failf(data, "Failed to receive SOCKS4 connect request ack.");
270 return CURLE_COULDNT_CONNECT;
271 }
272
273 /*
274 * Response format
275 *
276 * +----+----+----+----+----+----+----+----+
277 * | VN | CD | DSTPORT | DSTIP |
278 * +----+----+----+----+----+----+----+----+
279 * # of bytes: 1 1 2 4
280 *
281 * VN is the version of the reply code and should be 0. CD is the result
282 * code with one of the following values:
283 *
284 * 90: request granted
285 * 91: request rejected or failed
286 * 92: request rejected because SOCKS server cannot connect to
287 * identd on the client
288 * 93: request rejected because the client program and identd
289 * report different user-ids
290 */
291
292 /* wrong version ? */
293 if(socksreq[0] != 0) {
294 failf(data,
295 "SOCKS4 reply has wrong version, version should be 0.");
296 return CURLE_COULDNT_CONNECT;
297 }
298
299 /* Result */
300 switch(socksreq[1]) {
301 case 90:
302 infof(data, "SOCKS4%s request granted.\n", protocol4a?"a":"");
303 break;
304 case 91:
305 failf(data,
306 "Can't complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)"
307 ", request rejected or failed.",
308 (unsigned char)socksreq[4], (unsigned char)socksreq[5],
309 (unsigned char)socksreq[6], (unsigned char)socksreq[7],
310 (((unsigned char)socksreq[2] << 8) | (unsigned char)socksreq[3]),
311 (unsigned char)socksreq[1]);
312 return CURLE_COULDNT_CONNECT;
313 case 92:
314 failf(data,
315 "Can't complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)"
316 ", request rejected because SOCKS server cannot connect to "
317 "identd on the client.",
318 (unsigned char)socksreq[4], (unsigned char)socksreq[5],
319 (unsigned char)socksreq[6], (unsigned char)socksreq[7],
320 (((unsigned char)socksreq[2] << 8) | (unsigned char)socksreq[3]),
321 (unsigned char)socksreq[1]);
322 return CURLE_COULDNT_CONNECT;
323 case 93:
324 failf(data,
325 "Can't complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)"
326 ", request rejected because the client program and identd "
327 "report different user-ids.",
328 (unsigned char)socksreq[4], (unsigned char)socksreq[5],
329 (unsigned char)socksreq[6], (unsigned char)socksreq[7],
330 (((unsigned char)socksreq[2] << 8) | (unsigned char)socksreq[3]),
331 (unsigned char)socksreq[1]);
332 return CURLE_COULDNT_CONNECT;
333 default:
334 failf(data,
335 "Can't complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)"
336 ", Unknown.",
337 (unsigned char)socksreq[4], (unsigned char)socksreq[5],
338 (unsigned char)socksreq[6], (unsigned char)socksreq[7],
339 (((unsigned char)socksreq[2] << 8) | (unsigned char)socksreq[3]),
340 (unsigned char)socksreq[1]);
341 return CURLE_COULDNT_CONNECT;
342 }
343 }
344
345 (void)curlx_nonblock(sock, TRUE);
346
347 return CURLE_OK; /* Proxy was successful! */
348}
349
350/*
351 * This function logs in to a SOCKS5 proxy and sends the specifics to the final
352 * destination server.
353 */
354CURLcode Curl_SOCKS5(const char *proxy_user,
355 const char *proxy_password,
356 const char *hostname,
357 int remote_port,
358 int sockindex,
359 struct connectdata *conn)
360{
361 /*
362 According to the RFC1928, section "6. Replies". This is what a SOCK5
363 replies:
364
365 +----+-----+-------+------+----------+----------+
366 |VER | REP | RSV | ATYP | BND.ADDR | BND.PORT |
367 +----+-----+-------+------+----------+----------+
368 | 1 | 1 | X'00' | 1 | Variable | 2 |
369 +----+-----+-------+------+----------+----------+
370
371 Where:
372
373 o VER protocol version: X'05'
374 o REP Reply field:
375 o X'00' succeeded
376 */
377#define REQUEST_BUFSIZE 600 /* room for large user/pw (255 max each) */
378 unsigned char socksreq[REQUEST_BUFSIZE];
379 char dest[REQUEST_BUFSIZE] = "unknown"; /* printable hostname:port */
380 int idx;
381 ssize_t actualread;
382 ssize_t written;
383 int result;
384 CURLcode code;
385 curl_socket_t sock = conn->sock[sockindex];
386 struct Curl_easy *data = conn->data;
387 timediff_t timeout;
388 bool socks5_resolve_local =
389 (conn->socks_proxy.proxytype == CURLPROXY_SOCKS5) ? TRUE : FALSE;
390 const size_t hostname_len = strlen(hostname);
391 ssize_t len = 0;
392 const unsigned long auth = data->set.socks5auth;
393 bool allow_gssapi = FALSE;
394
395 if(conn->bits.httpproxy)
396 infof(conn->data, "SOCKS5: connecting to HTTP proxy %s port %d\n",
397 hostname, remote_port);
398
399 /* RFC1928 chapter 5 specifies max 255 chars for domain name in packet */
400 if(!socks5_resolve_local && hostname_len > 255) {
401 infof(conn->data, "SOCKS5: server resolving disabled for hostnames of "
402 "length > 255 [actual len=%zu]\n", hostname_len);
403 socks5_resolve_local = TRUE;
404 }
405
406 /* get timeout */
407 timeout = Curl_timeleft(data, NULL, TRUE);
408
409 if(timeout < 0) {
410 /* time-out, bail out, go home */
411 failf(data, "Connection time-out");
412 return CURLE_OPERATION_TIMEDOUT;
413 }
414
415 (void)curlx_nonblock(sock, TRUE);
416
417 /* wait until socket gets connected */
418 result = SOCKET_WRITABLE(sock, timeout);
419
420 if(-1 == result) {
421 failf(conn->data, "SOCKS5: no connection here");
422 return CURLE_COULDNT_CONNECT;
423 }
424 if(0 == result) {
425 failf(conn->data, "SOCKS5: connection timeout");
426 return CURLE_OPERATION_TIMEDOUT;
427 }
428
429 if(result & CURL_CSELECT_ERR) {
430 failf(conn->data, "SOCKS5: error occurred during connection");
431 return CURLE_COULDNT_CONNECT;
432 }
433
434 if(auth & ~(CURLAUTH_BASIC | CURLAUTH_GSSAPI))
435 infof(conn->data,
436 "warning: unsupported value passed to CURLOPT_SOCKS5_AUTH: %lu\n",
437 auth);
438 if(!(auth & CURLAUTH_BASIC))
439 /* disable username/password auth */
440 proxy_user = NULL;
441#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
442 if(auth & CURLAUTH_GSSAPI)
443 allow_gssapi = TRUE;
444#endif
445
446 idx = 0;
447 socksreq[idx++] = 5; /* version */
448 idx++; /* reserve for the number of authentication methods */
449 socksreq[idx++] = 0; /* no authentication */
450 if(allow_gssapi)
451 socksreq[idx++] = 1; /* GSS-API */
452 if(proxy_user)
453 socksreq[idx++] = 2; /* username/password */
454 /* write the number of authentication methods */
455 socksreq[1] = (unsigned char) (idx - 2);
456
457 (void)curlx_nonblock(sock, FALSE);
458
459 infof(data, "SOCKS5 communication to %s:%d\n", hostname, remote_port);
460
461 code = Curl_write_plain(conn, sock, (char *)socksreq, (2 + (int)socksreq[1]),
462 &written);
463 if(code || (written != (2 + (int)socksreq[1]))) {
464 failf(data, "Unable to send initial SOCKS5 request.");
465 return CURLE_COULDNT_CONNECT;
466 }
467
468 (void)curlx_nonblock(sock, TRUE);
469
470 result = SOCKET_READABLE(sock, timeout);
471
472 if(-1 == result) {
473 failf(conn->data, "SOCKS5 nothing to read");
474 return CURLE_COULDNT_CONNECT;
475 }
476 if(0 == result) {
477 failf(conn->data, "SOCKS5 read timeout");
478 return CURLE_OPERATION_TIMEDOUT;
479 }
480
481 if(result & CURL_CSELECT_ERR) {
482 failf(conn->data, "SOCKS5 read error occurred");
483 return CURLE_RECV_ERROR;
484 }
485
486 (void)curlx_nonblock(sock, FALSE);
487
488 result = Curl_blockread_all(conn, sock, (char *)socksreq, 2, &actualread);
489 if(result || (actualread != 2)) {
490 failf(data, "Unable to receive initial SOCKS5 response.");
491 return CURLE_COULDNT_CONNECT;
492 }
493
494 if(socksreq[0] != 5) {
495 failf(data, "Received invalid version in initial SOCKS5 response.");
496 return CURLE_COULDNT_CONNECT;
497 }
498 if(socksreq[1] == 0) {
499 /* Nothing to do, no authentication needed */
500 ;
501 }
502#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
503 else if(allow_gssapi && (socksreq[1] == 1)) {
504 code = Curl_SOCKS5_gssapi_negotiate(sockindex, conn);
505 if(code) {
506 failf(data, "Unable to negotiate SOCKS5 GSS-API context.");
507 return CURLE_COULDNT_CONNECT;
508 }
509 }
510#endif
511 else if(socksreq[1] == 2) {
512 /* Needs user name and password */
513 size_t proxy_user_len, proxy_password_len;
514 if(proxy_user && proxy_password) {
515 proxy_user_len = strlen(proxy_user);
516 proxy_password_len = strlen(proxy_password);
517 }
518 else {
519 proxy_user_len = 0;
520 proxy_password_len = 0;
521 }
522
523 /* username/password request looks like
524 * +----+------+----------+------+----------+
525 * |VER | ULEN | UNAME | PLEN | PASSWD |
526 * +----+------+----------+------+----------+
527 * | 1 | 1 | 1 to 255 | 1 | 1 to 255 |
528 * +----+------+----------+------+----------+
529 */
530 len = 0;
531 socksreq[len++] = 1; /* username/pw subnegotiation version */
532 socksreq[len++] = (unsigned char) proxy_user_len;
533 if(proxy_user && proxy_user_len) {
534 /* the length must fit in a single byte */
535 if(proxy_user_len >= 255) {
536 failf(data, "Excessive user name length for proxy auth");
537 return CURLE_BAD_FUNCTION_ARGUMENT;
538 }
539 memcpy(socksreq + len, proxy_user, proxy_user_len);
540 }
541 len += proxy_user_len;
542 socksreq[len++] = (unsigned char) proxy_password_len;
543 if(proxy_password && proxy_password_len) {
544 /* the length must fit in a single byte */
545 if(proxy_password_len > 255) {
546 failf(data, "Excessive password length for proxy auth");
547 return CURLE_BAD_FUNCTION_ARGUMENT;
548 }
549 memcpy(socksreq + len, proxy_password, proxy_password_len);
550 }
551 len += proxy_password_len;
552
553 code = Curl_write_plain(conn, sock, (char *)socksreq, len, &written);
554 if(code || (len != written)) {
555 failf(data, "Failed to send SOCKS5 sub-negotiation request.");
556 return CURLE_COULDNT_CONNECT;
557 }
558
559 result = Curl_blockread_all(conn, sock, (char *)socksreq, 2, &actualread);
560 if(result || (actualread != 2)) {
561 failf(data, "Unable to receive SOCKS5 sub-negotiation response.");
562 return CURLE_COULDNT_CONNECT;
563 }
564
565 /* ignore the first (VER) byte */
566 if(socksreq[1] != 0) { /* status */
567 failf(data, "User was rejected by the SOCKS5 server (%d %d).",
568 socksreq[0], socksreq[1]);
569 return CURLE_COULDNT_CONNECT;
570 }
571
572 /* Everything is good so far, user was authenticated! */
573 }
574 else {
575 /* error */
576 if(!allow_gssapi && (socksreq[1] == 1)) {
577 failf(data,
578 "SOCKS5 GSSAPI per-message authentication is not supported.");
579 return CURLE_COULDNT_CONNECT;
580 }
581 if(socksreq[1] == 255) {
582 if(!proxy_user || !*proxy_user) {
583 failf(data,
584 "No authentication method was acceptable. (It is quite likely"
585 " that the SOCKS5 server wanted a username/password, since none"
586 " was supplied to the server on this connection.)");
587 }
588 else {
589 failf(data, "No authentication method was acceptable.");
590 }
591 return CURLE_COULDNT_CONNECT;
592 }
593 else {
594 failf(data,
595 "Undocumented SOCKS5 mode attempted to be used by server.");
596 return CURLE_COULDNT_CONNECT;
597 }
598 }
599
600 /* Authentication is complete, now specify destination to the proxy */
601 len = 0;
602 socksreq[len++] = 5; /* version (SOCKS5) */
603 socksreq[len++] = 1; /* connect */
604 socksreq[len++] = 0; /* must be zero */
605
606 if(!socks5_resolve_local) {
607 socksreq[len++] = 3; /* ATYP: domain name = 3 */
608 socksreq[len++] = (char) hostname_len; /* address length */
609 memcpy(&socksreq[len], hostname, hostname_len); /* address str w/o NULL */
610 len += hostname_len;
611 msnprintf(dest, sizeof(dest), "%s:%d", hostname, remote_port);
612 infof(data, "SOCKS5 connect to %s (remotely resolved)\n", dest);
613 }
614 else {
615 struct Curl_dns_entry *dns;
616 Curl_addrinfo *hp = NULL;
617 int rc = Curl_resolv(conn, hostname, remote_port, FALSE, &dns);
618
619 if(rc == CURLRESOLV_ERROR)
620 return CURLE_COULDNT_RESOLVE_HOST;
621
622 if(rc == CURLRESOLV_PENDING) {
623 /* this requires that we're in "wait for resolve" state */
624 code = Curl_resolver_wait_resolv(conn, &dns);
625 if(code)
626 return code;
627 }
628
629 /*
630 * We cannot use 'hostent' as a struct that Curl_resolv() returns. It
631 * returns a Curl_addrinfo pointer that may not always look the same.
632 */
633 if(dns)
634 hp = dns->addr;
635 if(hp) {
636 if(Curl_printable_address(hp, dest, sizeof(dest))) {
637 size_t destlen = strlen(dest);
638 msnprintf(dest + destlen, sizeof(dest) - destlen, ":%d", remote_port);
639 }
640 else {
641 strcpy(dest, "unknown");
642 }
643
644 if(hp->ai_family == AF_INET) {
645 int i;
646 struct sockaddr_in *saddr_in;
647 socksreq[len++] = 1; /* ATYP: IPv4 = 1 */
648
649 saddr_in = (struct sockaddr_in *)(void *)hp->ai_addr;
650 for(i = 0; i < 4; i++) {
651 socksreq[len++] = ((unsigned char *)&saddr_in->sin_addr.s_addr)[i];
652 }
653
654 infof(data, "SOCKS5 connect to IPv4 %s (locally resolved)\n", dest);
655 }
656#ifdef ENABLE_IPV6
657 else if(hp->ai_family == AF_INET6) {
658 int i;
659 struct sockaddr_in6 *saddr_in6;
660 socksreq[len++] = 4; /* ATYP: IPv6 = 4 */
661
662 saddr_in6 = (struct sockaddr_in6 *)(void *)hp->ai_addr;
663 for(i = 0; i < 16; i++) {
664 socksreq[len++] =
665 ((unsigned char *)&saddr_in6->sin6_addr.s6_addr)[i];
666 }
667
668 infof(data, "SOCKS5 connect to IPv6 %s (locally resolved)\n", dest);
669 }
670#endif
671 else {
672 hp = NULL; /* fail! */
673
674 failf(data, "SOCKS5 connection to %s not supported\n", dest);
675 }
676
677 Curl_resolv_unlock(data, dns); /* not used anymore from now on */
678 }
679 if(!hp) {
680 failf(data, "Failed to resolve \"%s\" for SOCKS5 connect.",
681 hostname);
682 return CURLE_COULDNT_RESOLVE_HOST;
683 }
684 }
685
686 socksreq[len++] = (unsigned char)((remote_port >> 8) & 0xff); /* PORT MSB */
687 socksreq[len++] = (unsigned char)(remote_port & 0xff); /* PORT LSB */
688
689#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
690 if(conn->socks5_gssapi_enctype) {
691 failf(data, "SOCKS5 GSS-API protection not yet implemented.");
692 }
693 else
694#endif
695 code = Curl_write_plain(conn, sock, (char *)socksreq, len, &written);
696
697 if(code || (len != written)) {
698 failf(data, "Failed to send SOCKS5 connect request.");
699 return CURLE_COULDNT_CONNECT;
700 }
701
702 len = 10; /* minimum packet size is 10 */
703
704#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
705 if(conn->socks5_gssapi_enctype) {
706 failf(data, "SOCKS5 GSS-API protection not yet implemented.");
707 }
708 else
709#endif
710 result = Curl_blockread_all(conn, sock, (char *)socksreq,
711 len, &actualread);
712
713 if(result || (len != actualread)) {
714 failf(data, "Failed to receive SOCKS5 connect request ack.");
715 return CURLE_COULDNT_CONNECT;
716 }
717
718 if(socksreq[0] != 5) { /* version */
719 failf(data,
720 "SOCKS5 reply has wrong version, version should be 5.");
721 return CURLE_COULDNT_CONNECT;
722 }
723
724 /* Fix: in general, returned BND.ADDR is variable length parameter by RFC
725 1928, so the reply packet should be read until the end to avoid errors at
726 subsequent protocol level.
727
728 +----+-----+-------+------+----------+----------+
729 |VER | REP | RSV | ATYP | BND.ADDR | BND.PORT |
730 +----+-----+-------+------+----------+----------+
731 | 1 | 1 | X'00' | 1 | Variable | 2 |
732 +----+-----+-------+------+----------+----------+
733
734 ATYP:
735 o IP v4 address: X'01', BND.ADDR = 4 byte
736 o domain name: X'03', BND.ADDR = [ 1 byte length, string ]
737 o IP v6 address: X'04', BND.ADDR = 16 byte
738 */
739
740 /* Calculate real packet size */
741 if(socksreq[3] == 3) {
742 /* domain name */
743 int addrlen = (int) socksreq[4];
744 len = 5 + addrlen + 2;
745 }
746 else if(socksreq[3] == 4) {
747 /* IPv6 */
748 len = 4 + 16 + 2;
749 }
750
751 /* At this point we already read first 10 bytes */
752#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
753 if(!conn->socks5_gssapi_enctype) {
754 /* decrypt_gssapi_blockread already read the whole packet */
755#endif
756 if(len > 10) {
757 result = Curl_blockread_all(conn, sock, (char *)&socksreq[10],
758 len - 10, &actualread);
759 if(result || ((len - 10) != actualread)) {
760 failf(data, "Failed to receive SOCKS5 connect request ack.");
761 return CURLE_COULDNT_CONNECT;
762 }
763 }
764#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
765 }
766#endif
767
768 if(socksreq[1] != 0) { /* Anything besides 0 is an error */
769 failf(data, "Can't complete SOCKS5 connection to %s. (%d)",
770 dest, (unsigned char)socksreq[1]);
771 return CURLE_COULDNT_CONNECT;
772 }
773 infof(data, "SOCKS5 request granted.\n");
774
775 (void)curlx_nonblock(sock, TRUE);
776 return CURLE_OK; /* Proxy was successful! */
777}
778
779#endif /* CURL_DISABLE_PROXY */
780