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