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