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