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#include "server_setup.h" 25#include <stdlib.h> 26 27/* Function 28 * 29 * Accepts a TCP connection on a custom port (IPv4 or IPv6). Connects to a 30 * given addr + port backend (that is NOT extracted form the client's 31 * request). The backend server default to connect to can be set with 32 * --backend and --backendport. 33 * 34 * Read commands from FILE (set with --config). The commands control how to 35 * act and is reset to defaults each client TCP connect. 36 * 37 * Config file keywords: 38 * 39 * "version [number: 5]" - requires the communication to use this version. 40 * "nmethods_min [number: 1]" - the minimum numberf NMETHODS the client must 41 * state 42 * "nmethods_max [number: 3]" - the minimum numberf NMETHODS the client must 43 * state 44 * "user [string]" - the user name that must match (if method is 2) 45 * "password [string]" - the password that must match (if method is 2) 46 * "backend [IPv4]" - numerical IPv4 address of backend to connect to 47 * "backendport [number:0]" - TCP port of backend to connect to. 0 means use 48 the client's specified port number. 49 * "method [number: 0]" - connect method to respond with: 50 * 0 - no auth 51 * 1 - GSSAPI (not supported) 52 * 2 - user + password 53 * "response [number]" - the decimal number to respond to a connect 54 * SOCKS5: 0 is OK, SOCKS4: 90 is ok 55 * 56 */ 57 58/* based on sockfilt.c */ 59 60#include <signal.h> 61#ifdef HAVE_NETINET_IN_H 62#include <netinet/in.h> 63#endif 64#ifdef HAVE_NETINET_IN6_H 65#include <netinet/in6.h> 66#endif 67#ifdef HAVE_ARPA_INET_H 68#include <arpa/inet.h> 69#endif 70#ifdef HAVE_NETDB_H 71#include <netdb.h> 72#endif 73 74#define ENABLE_CURLX_PRINTF 75/* make the curlx header define all printf() functions to use the curlx_* 76 versions instead */ 77#include "curlx.h" /* from the private lib dir */ 78#include "getpart.h" 79#include "inet_pton.h" 80#include "util.h" 81#include "server_sockaddr.h" 82#include "warnless.h" 83 84/* include memdebug.h last */ 85#include "memdebug.h" 86 87#ifdef USE_WINSOCK 88#undef EINTR 89#define EINTR 4 /* errno.h value */ 90#undef EAGAIN 91#define EAGAIN 11 /* errno.h value */ 92#undef ENOMEM 93#define ENOMEM 12 /* errno.h value */ 94#undef EINVAL 95#define EINVAL 22 /* errno.h value */ 96#endif 97 98#define DEFAULT_PORT 8905 99 100#ifndef DEFAULT_LOGFILE 101#define DEFAULT_LOGFILE "log/socksd.log" 102#endif 103 104#ifndef DEFAULT_REQFILE 105#define DEFAULT_REQFILE "log/socksd-request.log" 106#endif 107 108#ifndef DEFAULT_CONFIG 109#define DEFAULT_CONFIG "socksd.config" 110#endif 111 112static const char *backendaddr = "127.0.0.1"; 113static unsigned short backendport = 0; /* default is use client's */ 114 115struct configurable { 116 unsigned char version; /* initial version byte in the request must match 117 this */ 118 unsigned char nmethods_min; /* minimum number of nmethods to expect */ 119 unsigned char nmethods_max; /* maximum number of nmethods to expect */ 120 unsigned char responseversion; 121 unsigned char responsemethod; 122 unsigned char reqcmd; 123 unsigned char connectrep; 124 unsigned short port; /* backend port */ 125 char addr[32]; /* backend IPv4 numerical */ 126 char user[256]; 127 char password[256]; 128}; 129 130#define CONFIG_VERSION 5 131#define CONFIG_NMETHODS_MIN 1 /* unauth, gssapi, auth */ 132#define CONFIG_NMETHODS_MAX 3 133#define CONFIG_RESPONSEVERSION CONFIG_VERSION 134#define CONFIG_RESPONSEMETHOD 0 /* no auth */ 135#define CONFIG_REQCMD 1 /* CONNECT */ 136#define CONFIG_PORT backendport 137#define CONFIG_ADDR backendaddr 138#define CONFIG_CONNECTREP 0 139 140static struct configurable config; 141 142const char *serverlogfile = DEFAULT_LOGFILE; 143static const char *reqlogfile = DEFAULT_REQFILE; 144static const char *configfile = DEFAULT_CONFIG; 145 146static const char *socket_type = "IPv4"; 147static unsigned short port = DEFAULT_PORT; 148 149static void resetdefaults(void) 150{ 151 logmsg("Reset to defaults"); 152 config.version = CONFIG_VERSION; 153 config.nmethods_min = CONFIG_NMETHODS_MIN; 154 config.nmethods_max = CONFIG_NMETHODS_MAX; 155 config.responseversion = CONFIG_RESPONSEVERSION; 156 config.responsemethod = CONFIG_RESPONSEMETHOD; 157 config.reqcmd = CONFIG_REQCMD; 158 config.connectrep = CONFIG_CONNECTREP; 159 config.port = CONFIG_PORT; 160 strcpy(config.addr, CONFIG_ADDR); 161 strcpy(config.user, "user"); 162 strcpy(config.password, "password"); 163} 164 165static unsigned char byteval(char *value) 166{ 167 unsigned long num = strtoul(value, NULL, 10); 168 return num & 0xff; 169} 170 171static unsigned short shortval(char *value) 172{ 173 unsigned long num = strtoul(value, NULL, 10); 174 return num & 0xffff; 175} 176 177static enum { 178 socket_domain_inet = AF_INET 179#ifdef ENABLE_IPV6 180 , socket_domain_inet6 = AF_INET6 181#endif 182#ifdef USE_UNIX_SOCKETS 183 , socket_domain_unix = AF_UNIX 184#endif 185} socket_domain = AF_INET; 186 187static void getconfig(void) 188{ 189 FILE *fp = fopen(configfile, FOPEN_READTEXT); 190 resetdefaults(); 191 if(fp) { 192 char buffer[512]; 193 logmsg("parse config file"); 194 while(fgets(buffer, sizeof(buffer), fp)) { 195 char key[32]; 196 char value[260]; 197 if(2 == sscanf(buffer, "%31s %259s", key, value)) { 198 if(!strcmp(key, "version")) { 199 config.version = byteval(value); 200 logmsg("version [%d] set", config.version); 201 } 202 else if(!strcmp(key, "nmethods_min")) { 203 config.nmethods_min = byteval(value); 204 logmsg("nmethods_min [%d] set", config.nmethods_min); 205 } 206 else if(!strcmp(key, "nmethods_max")) { 207 config.nmethods_max = byteval(value); 208 logmsg("nmethods_max [%d] set", config.nmethods_max); 209 } 210 else if(!strcmp(key, "backend")) { 211 strcpy(config.addr, value); 212 logmsg("backend [%s] set", config.addr); 213 } 214 else if(!strcmp(key, "backendport")) { 215 config.port = shortval(value); 216 logmsg("backendport [%d] set", config.port); 217 } 218 else if(!strcmp(key, "user")) { 219 strcpy(config.user, value); 220 logmsg("user [%s] set", config.user); 221 } 222 else if(!strcmp(key, "password")) { 223 strcpy(config.password, value); 224 logmsg("password [%s] set", config.password); 225 } 226 /* Methods: 227 o X'00' NO AUTHENTICATION REQUIRED 228 o X'01' GSSAPI 229 o X'02' USERNAME/PASSWORD 230 */ 231 else if(!strcmp(key, "method")) { 232 config.responsemethod = byteval(value); 233 logmsg("method [%d] set", config.responsemethod); 234 } 235 else if(!strcmp(key, "response")) { 236 config.connectrep = byteval(value); 237 logmsg("response [%d] set", config.connectrep); 238 } 239 } 240 } 241 fclose(fp); 242 } 243} 244 245static void loghex(unsigned char *buffer, ssize_t len) 246{ 247 char data[1200]; 248 ssize_t i; 249 unsigned char *ptr = buffer; 250 char *optr = data; 251 ssize_t width = 0; 252 int left = sizeof(data); 253 254 for(i = 0; i<len && (left >= 0); i++) { 255 msnprintf(optr, left, "%02x", ptr[i]); 256 width += 2; 257 optr += 2; 258 left -= 2; 259 } 260 if(width) 261 logmsg("'%s'", data); 262} 263 264/* RFC 1928, SOCKS5 byte index */ 265#define SOCKS5_VERSION 0 266#define SOCKS5_NMETHODS 1 /* number of methods that is listed */ 267 268/* in the request: */ 269#define SOCKS5_REQCMD 1 270#define SOCKS5_RESERVED 2 271#define SOCKS5_ATYP 3 272#define SOCKS5_DSTADDR 4 273 274/* connect response */ 275#define SOCKS5_REP 1 276#define SOCKS5_BNDADDR 4 277 278/* auth request */ 279#define SOCKS5_ULEN 1 280#define SOCKS5_UNAME 2 281 282#define SOCKS4_CD 1 283#define SOCKS4_DSTPORT 2 284 285/* connect to a given IPv4 address, not the one asked for */ 286static curl_socket_t socksconnect(unsigned short connectport, 287 const char *connectaddr) 288{ 289 int rc; 290 srvr_sockaddr_union_t me; 291 curl_socket_t sock = socket(AF_INET, SOCK_STREAM, 0); 292 if(sock == CURL_SOCKET_BAD) 293 return CURL_SOCKET_BAD; 294 memset(&me.sa4, 0, sizeof(me.sa4)); 295 me.sa4.sin_family = AF_INET; 296 me.sa4.sin_port = htons(connectport); 297 me.sa4.sin_addr.s_addr = INADDR_ANY; 298 Curl_inet_pton(AF_INET, connectaddr, &me.sa4.sin_addr); 299 300 rc = connect(sock, &me.sa, sizeof(me.sa4)); 301 302 if(rc) { 303 int error = SOCKERRNO; 304 logmsg("Error connecting to %s:%hu: (%d) %s", 305 connectaddr, connectport, error, sstrerror(error)); 306 return CURL_SOCKET_BAD; 307 } 308 logmsg("Connected fine to %s:%d", connectaddr, connectport); 309 return sock; 310} 311 312static curl_socket_t socks4(curl_socket_t fd, 313 unsigned char *buffer, 314 ssize_t rc) 315{ 316 unsigned char response[256 + 16]; 317 curl_socket_t connfd; 318 unsigned char cd; 319 unsigned short s4port; 320 321 if(buffer[SOCKS4_CD] != 1) { 322 logmsg("SOCKS4 CD is not 1: %d", buffer[SOCKS4_CD]); 323 return CURL_SOCKET_BAD; 324 } 325 if(rc < 9) { 326 logmsg("SOCKS4 connect message too short: %zd", rc); 327 return CURL_SOCKET_BAD; 328 } 329 if(!config.port) 330 s4port = (unsigned short)((buffer[SOCKS4_DSTPORT]<<8) | 331 (buffer[SOCKS4_DSTPORT + 1])); 332 else 333 s4port = config.port; 334 335 connfd = socksconnect(s4port, config.addr); 336 if(connfd == CURL_SOCKET_BAD) { 337 /* failed */ 338 cd = 91; 339 } 340 else { 341 /* success */ 342 cd = 90; 343 } 344 response[0] = 0; /* reply version 0 */ 345 response[1] = cd; /* result */ 346 /* copy port and address from connect request */ 347 memcpy(&response[2], &buffer[SOCKS4_DSTPORT], 6); 348 rc = (send)(fd, (char *)response, 8, 0); 349 if(rc != 8) { 350 logmsg("Sending SOCKS4 response failed!"); 351 return CURL_SOCKET_BAD; 352 } 353 logmsg("Sent %zd bytes", rc); 354 loghex(response, rc); 355 356 if(cd == 90) 357 /* now do the transfer */ 358 return connfd; 359 360 if(connfd != CURL_SOCKET_BAD) 361 sclose(connfd); 362 363 return CURL_SOCKET_BAD; 364} 365 366static curl_socket_t sockit(curl_socket_t fd) 367{ 368 unsigned char buffer[2*256 + 16]; 369 unsigned char response[2*256 + 16]; 370 ssize_t rc; 371 unsigned char len; 372 unsigned char type; 373 unsigned char rep = 0; 374 unsigned char *address; 375 unsigned short socksport; 376 curl_socket_t connfd = CURL_SOCKET_BAD; 377 unsigned short s5port; 378 379 getconfig(); 380 381 rc = recv(fd, (char *)buffer, sizeof(buffer), 0); 382 if(rc <= 0) { 383 logmsg("SOCKS identifier message missing, recv returned %zd", rc); 384 return CURL_SOCKET_BAD; 385 } 386 387 logmsg("READ %zd bytes", rc); 388 loghex(buffer, rc); 389 390 if(buffer[SOCKS5_VERSION] == 4) 391 return socks4(fd, buffer, rc); 392 393 if(rc < 3) { 394 logmsg("SOCKS5 identifier message too short: %zd", rc); 395 return CURL_SOCKET_BAD; 396 } 397 398 if(buffer[SOCKS5_VERSION] != config.version) { 399 logmsg("VERSION byte not %d", config.version); 400 return CURL_SOCKET_BAD; 401 } 402 if((buffer[SOCKS5_NMETHODS] < config.nmethods_min) || 403 (buffer[SOCKS5_NMETHODS] > config.nmethods_max)) { 404 logmsg("NMETHODS byte not within %d - %d ", 405 config.nmethods_min, config.nmethods_max); 406 return CURL_SOCKET_BAD; 407 } 408 /* after NMETHODS follows that many bytes listing the methods the client 409 says it supports */ 410 if(rc != (buffer[SOCKS5_NMETHODS] + 2)) { 411 logmsg("Expected %d bytes, got %zd", buffer[SOCKS5_NMETHODS] + 2, rc); 412 return CURL_SOCKET_BAD; 413 } 414 logmsg("Incoming request deemed fine!"); 415 416 /* respond with two bytes: VERSION + METHOD */ 417 response[0] = config.responseversion; 418 response[1] = config.responsemethod; 419 rc = (send)(fd, (char *)response, 2, 0); 420 if(rc != 2) { 421 logmsg("Sending response failed!"); 422 return CURL_SOCKET_BAD; 423 } 424 logmsg("Sent %zd bytes", rc); 425 loghex(response, rc); 426 427 /* expect the request or auth */ 428 rc = recv(fd, (char *)buffer, sizeof(buffer), 0); 429 if(rc <= 0) { 430 logmsg("SOCKS5 request or auth message missing, recv returned %zd", rc); 431 return CURL_SOCKET_BAD; 432 } 433 434 logmsg("READ %zd bytes", rc); 435 loghex(buffer, rc); 436 437 if(config.responsemethod == 2) { 438 /* RFC 1929 authentication 439 +----+------+----------+------+----------+ 440 |VER | ULEN | UNAME | PLEN | PASSWD | 441 +----+------+----------+------+----------+ 442 | 1 | 1 | 1 to 255 | 1 | 1 to 255 | 443 +----+------+----------+------+----------+ 444 */ 445 unsigned char ulen; 446 unsigned char plen; 447 bool login = TRUE; 448 if(rc < 5) { 449 logmsg("Too short auth input: %zd", rc); 450 return CURL_SOCKET_BAD; 451 } 452 if(buffer[SOCKS5_VERSION] != 1) { 453 logmsg("Auth VERSION byte not 1, got %d", buffer[SOCKS5_VERSION]); 454 return CURL_SOCKET_BAD; 455 } 456 ulen = buffer[SOCKS5_ULEN]; 457 if(rc < 4 + ulen) { 458 logmsg("Too short packet for username: %zd", rc); 459 return CURL_SOCKET_BAD; 460 } 461 plen = buffer[SOCKS5_ULEN + ulen + 1]; 462 if(rc < 3 + ulen + plen) { 463 logmsg("Too short packet for ulen %d plen %d: %zd", ulen, plen, rc); 464 return CURL_SOCKET_BAD; 465 } 466 if((ulen != strlen(config.user)) || 467 (plen != strlen(config.password)) || 468 memcmp(&buffer[SOCKS5_UNAME], config.user, ulen) || 469 memcmp(&buffer[SOCKS5_UNAME + ulen + 1], config.password, plen)) { 470 /* no match! */ 471 logmsg("mismatched credentials!"); 472 login = FALSE; 473 } 474 response[0] = 1; 475 response[1] = login ? 0 : 1; 476 rc = (send)(fd, (char *)response, 2, 0); 477 if(rc != 2) { 478 logmsg("Sending auth response failed!"); 479 return CURL_SOCKET_BAD; 480 } 481 logmsg("Sent %zd bytes", rc); 482 loghex(response, rc); 483 if(!login) 484 return CURL_SOCKET_BAD; 485 486 /* expect the request */ 487 rc = recv(fd, (char *)buffer, sizeof(buffer), 0); 488 if(rc <= 0) { 489 logmsg("SOCKS5 request message missing, recv returned %zd", rc); 490 return CURL_SOCKET_BAD; 491 } 492 493 logmsg("READ %zd bytes", rc); 494 loghex(buffer, rc); 495 } 496 if(rc < 6) { 497 logmsg("Too short for request: %zd", rc); 498 return CURL_SOCKET_BAD; 499 } 500 501 if(buffer[SOCKS5_VERSION] != config.version) { 502 logmsg("Request VERSION byte not %d", config.version); 503 return CURL_SOCKET_BAD; 504 } 505 /* 1 == CONNECT */ 506 if(buffer[SOCKS5_REQCMD] != config.reqcmd) { 507 logmsg("Request COMMAND byte not %d", config.reqcmd); 508 return CURL_SOCKET_BAD; 509 } 510 /* reserved, should be zero */ 511 if(buffer[SOCKS5_RESERVED]) { 512 logmsg("Request COMMAND byte not %d", config.reqcmd); 513 return CURL_SOCKET_BAD; 514 } 515 /* ATYP: 516 o IP V4 address: X'01' 517 o DOMAINNAME: X'03' 518 o IP V6 address: X'04' 519 */ 520 type = buffer[SOCKS5_ATYP]; 521 address = &buffer[SOCKS5_DSTADDR]; 522 switch(type) { 523 case 1: 524 /* 4 bytes IPv4 address */ 525 len = 4; 526 break; 527 case 3: 528 /* The first octet of the address field contains the number of octets of 529 name that follow */ 530 len = buffer[SOCKS5_DSTADDR]; 531 len++; 532 break; 533 case 4: 534 /* 16 bytes IPv6 address */ 535 len = 16; 536 break; 537 default: 538 logmsg("Unknown ATYP %d", type); 539 return CURL_SOCKET_BAD; 540 } 541 if(rc < (4 + len + 2)) { 542 logmsg("Request too short: %zd, expected %d", rc, 4 + len + 2); 543 return CURL_SOCKET_BAD; 544 } 545 logmsg("Received ATYP %d", type); 546 547 { 548 FILE *dump; 549 dump = fopen(reqlogfile, "ab"); 550 if(dump) { 551 int i; 552 fprintf(dump, "atyp %u =>", type); 553 switch(type) { 554 case 1: 555 /* 4 bytes IPv4 address */ 556 fprintf(dump, " %u.%u.%u.%u\n", 557 address[0], address[1], address[2], address[3]); 558 break; 559 case 3: 560 /* The first octet of the address field contains the number of octets 561 of name that follow */ 562 fprintf(dump, " %.*s\n", len-1, &address[1]); 563 break; 564 case 4: 565 /* 16 bytes IPv6 address */ 566 for(i = 0; i < 16; i++) { 567 fprintf(dump, " %02x", address[i]); 568 } 569 fprintf(dump, "\n"); 570 break; 571 } 572 fclose(dump); 573 } 574 } 575 576 if(!config.port) { 577 unsigned char *portp = &buffer[SOCKS5_DSTADDR + len]; 578 s5port = (unsigned short)((portp[0]<<8) | (portp[1])); 579 } 580 else 581 s5port = config.port; 582 583 if(!config.connectrep) 584 connfd = socksconnect(s5port, config.addr); 585 586 if(connfd == CURL_SOCKET_BAD) { 587 /* failed */ 588 rep = 1; 589 } 590 else { 591 rep = config.connectrep; 592 } 593 594 /* */ 595 response[SOCKS5_VERSION] = config.responseversion; 596 597 /* 598 o REP Reply field: 599 o X'00' succeeded 600 o X'01' general SOCKS server failure 601 o X'02' connection not allowed by ruleset 602 o X'03' Network unreachable 603 o X'04' Host unreachable 604 o X'05' Connection refused 605 o X'06' TTL expired 606 o X'07' Command not supported 607 o X'08' Address type not supported 608 o X'09' to X'FF' unassigned 609 */ 610 response[SOCKS5_REP] = rep; 611 response[SOCKS5_RESERVED] = 0; /* must be zero */ 612 response[SOCKS5_ATYP] = type; /* address type */ 613 614 /* mirror back the original addr + port */ 615 616 /* address or hostname */ 617 memcpy(&response[SOCKS5_BNDADDR], address, len); 618 619 /* port number */ 620 memcpy(&response[SOCKS5_BNDADDR + len], 621 &buffer[SOCKS5_DSTADDR + len], sizeof(socksport)); 622 623 rc = (send)(fd, (char *)response, (size_t)(len + 6), 0); 624 if(rc != (len + 6)) { 625 logmsg("Sending connect response failed!"); 626 return CURL_SOCKET_BAD; 627 } 628 logmsg("Sent %zd bytes", rc); 629 loghex(response, rc); 630 631 if(!rep) 632 return connfd; 633 634 if(connfd != CURL_SOCKET_BAD) 635 sclose(connfd); 636 637 return CURL_SOCKET_BAD; 638} 639 640struct perclient { 641 size_t fromremote; 642 size_t fromclient; 643 curl_socket_t remotefd; 644 curl_socket_t clientfd; 645 bool used; 646}; 647 648/* return non-zero when transfer is done */ 649static int tunnel(struct perclient *cp, fd_set *fds) 650{ 651 ssize_t nread; 652 ssize_t nwrite; 653 char buffer[512]; 654 if(FD_ISSET(cp->clientfd, fds)) { 655 /* read from client, send to remote */ 656 nread = recv(cp->clientfd, buffer, sizeof(buffer), 0); 657 if(nread > 0) { 658 nwrite = send(cp->remotefd, (char *)buffer, 659 (SEND_TYPE_ARG3)nread, 0); 660 if(nwrite != nread) 661 return 1; 662 cp->fromclient += nwrite; 663 } 664 else 665 return 1; 666 } 667 if(FD_ISSET(cp->remotefd, fds)) { 668 /* read from remote, send to client */ 669 nread = recv(cp->remotefd, buffer, sizeof(buffer), 0); 670 if(nread > 0) { 671 nwrite = send(cp->clientfd, (char *)buffer, 672 (SEND_TYPE_ARG3)nread, 0); 673 if(nwrite != nread) 674 return 1; 675 cp->fromremote += nwrite; 676 } 677 else 678 return 1; 679 } 680 return 0; 681} 682 683/* 684 sockfdp is a pointer to an established stream or CURL_SOCKET_BAD 685 686 if sockfd is CURL_SOCKET_BAD, listendfd is a listening socket we must 687 accept() 688*/ 689static bool incoming(curl_socket_t listenfd) 690{ 691 fd_set fds_read; 692 fd_set fds_write; 693 fd_set fds_err; 694 int clients = 0; /* connected clients */ 695 struct perclient c[2]; 696 697 memset(c, 0, sizeof(c)); 698 if(got_exit_signal) { 699 logmsg("signalled to die, exiting..."); 700 return FALSE; 701 } 702 703#ifdef HAVE_GETPPID 704 /* As a last resort, quit if socks5 process becomes orphan. */ 705 if(getppid() <= 1) { 706 logmsg("process becomes orphan, exiting"); 707 return FALSE; 708 } 709#endif 710 711 do { 712 int i; 713 ssize_t rc; 714 int error = 0; 715 curl_socket_t sockfd = listenfd; 716 int maxfd = (int)sockfd; 717 718 FD_ZERO(&fds_read); 719 FD_ZERO(&fds_write); 720 FD_ZERO(&fds_err); 721 722 /* there's always a socket to wait for */ 723 FD_SET(sockfd, &fds_read); 724 725 for(i = 0; i < 2; i++) { 726 if(c[i].used) { 727 curl_socket_t fd = c[i].clientfd; 728 FD_SET(fd, &fds_read); 729 if((int)fd > maxfd) 730 maxfd = (int)fd; 731 fd = c[i].remotefd; 732 FD_SET(fd, &fds_read); 733 if((int)fd > maxfd) 734 maxfd = (int)fd; 735 } 736 } 737 738 do { 739 /* select() blocking behavior call on blocking descriptors please */ 740 rc = select(maxfd + 1, &fds_read, &fds_write, &fds_err, NULL); 741 if(got_exit_signal) { 742 logmsg("signalled to die, exiting..."); 743 return FALSE; 744 } 745 } while((rc == -1) && ((error = errno) == EINTR)); 746 747 if(rc < 0) { 748 logmsg("select() failed with error: (%d) %s", 749 error, strerror(error)); 750 return FALSE; 751 } 752 753 if((clients < 2) && FD_ISSET(sockfd, &fds_read)) { 754 curl_socket_t newfd = accept(sockfd, NULL, NULL); 755 if(CURL_SOCKET_BAD == newfd) { 756 error = SOCKERRNO; 757 logmsg("accept(%" CURL_FORMAT_SOCKET_T ", NULL, NULL) " 758 "failed with error: (%d) %s", 759 sockfd, error, sstrerror(error)); 760 } 761 else { 762 curl_socket_t remotefd; 763 logmsg("====> Client connect, fd %" CURL_FORMAT_SOCKET_T ". " 764 "Read config from %s", newfd, configfile); 765 remotefd = sockit(newfd); /* SOCKS until done */ 766 if(remotefd == CURL_SOCKET_BAD) { 767 logmsg("====> Client disconnect"); 768 sclose(newfd); 769 } 770 else { 771 struct perclient *cp = &c[0]; 772 logmsg("====> Tunnel transfer"); 773 774 if(c[0].used) 775 cp = &c[1]; 776 cp->fromremote = 0; 777 cp->fromclient = 0; 778 cp->clientfd = newfd; 779 cp->remotefd = remotefd; 780 cp->used = TRUE; 781 clients++; 782 } 783 784 } 785 } 786 for(i = 0; i < 2; i++) { 787 struct perclient *cp = &c[i]; 788 if(cp->used) { 789 if(tunnel(cp, &fds_read)) { 790 logmsg("SOCKS transfer completed. Bytes: < %zu > %zu", 791 cp->fromremote, cp->fromclient); 792 sclose(cp->clientfd); 793 sclose(cp->remotefd); 794 cp->used = FALSE; 795 clients--; 796 } 797 } 798 } 799 } while(clients); 800 801 return TRUE; 802} 803 804static curl_socket_t sockdaemon(curl_socket_t sock, 805 unsigned short *listenport 806#ifdef USE_UNIX_SOCKETS 807 , const char *unix_socket 808#endif 809 ) 810{ 811 /* passive daemon style */ 812 srvr_sockaddr_union_t listener; 813 int flag; 814 int rc; 815 int totdelay = 0; 816 int maxretr = 10; 817 int delay = 20; 818 int attempt = 0; 819 int error = 0; 820 821 do { 822 attempt++; 823 flag = 1; 824 rc = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, 825 (void *)&flag, sizeof(flag)); 826 if(rc) { 827 error = SOCKERRNO; 828 logmsg("setsockopt(SO_REUSEADDR) failed with error: (%d) %s", 829 error, sstrerror(error)); 830 if(maxretr) { 831 rc = wait_ms(delay); 832 if(rc) { 833 /* should not happen */ 834 error = errno; 835 logmsg("wait_ms() failed with error: (%d) %s", 836 error, strerror(error)); 837 sclose(sock); 838 return CURL_SOCKET_BAD; 839 } 840 if(got_exit_signal) { 841 logmsg("signalled to die, exiting..."); 842 sclose(sock); 843 return CURL_SOCKET_BAD; 844 } 845 totdelay += delay; 846 delay *= 2; /* double the sleep for next attempt */ 847 } 848 } 849 } while(rc && maxretr--); 850 851 if(rc) { 852 logmsg("setsockopt(SO_REUSEADDR) failed %d times in %d ms. Error: (%d) %s", 853 attempt, totdelay, error, strerror(error)); 854 logmsg("Continuing anyway..."); 855 } 856 857 /* When the specified listener port is zero, it is actually a 858 request to let the system choose a non-zero available port. */ 859 860 switch(socket_domain) { 861 case AF_INET: 862 memset(&listener.sa4, 0, sizeof(listener.sa4)); 863 listener.sa4.sin_family = AF_INET; 864 listener.sa4.sin_addr.s_addr = INADDR_ANY; 865 listener.sa4.sin_port = htons(*listenport); 866 rc = bind(sock, &listener.sa, sizeof(listener.sa4)); 867 break; 868#ifdef ENABLE_IPV6 869 case AF_INET6: 870 memset(&listener.sa6, 0, sizeof(listener.sa6)); 871 listener.sa6.sin6_family = AF_INET6; 872 listener.sa6.sin6_addr = in6addr_any; 873 listener.sa6.sin6_port = htons(*listenport); 874 rc = bind(sock, &listener.sa, sizeof(listener.sa6)); 875 break; 876#endif /* ENABLE_IPV6 */ 877#ifdef USE_UNIX_SOCKETS 878 case AF_UNIX: 879 rc = bind_unix_socket(sock, unix_socket, &listener.sau); 880#endif 881 } 882 883 if(rc) { 884 error = SOCKERRNO; 885#ifdef USE_UNIX_SOCKETS 886 if(socket_domain == AF_UNIX) 887 logmsg("Error binding socket on path %s: (%d) %s", 888 unix_socket, error, sstrerror(error)); 889 else 890#endif 891 logmsg("Error binding socket on port %hu: (%d) %s", 892 *listenport, error, sstrerror(error)); 893 sclose(sock); 894 return CURL_SOCKET_BAD; 895 } 896 897 if(!*listenport 898#ifdef USE_UNIX_SOCKETS 899 && !unix_socket 900#endif 901 ) { 902 /* The system was supposed to choose a port number, figure out which 903 port we actually got and update the listener port value with it. */ 904 curl_socklen_t la_size; 905 srvr_sockaddr_union_t localaddr; 906#ifdef ENABLE_IPV6 907 if(socket_domain == AF_INET6) 908 la_size = sizeof(localaddr.sa6); 909 else 910#endif 911 la_size = sizeof(localaddr.sa4); 912 memset(&localaddr.sa, 0, (size_t)la_size); 913 if(getsockname(sock, &localaddr.sa, &la_size) < 0) { 914 error = SOCKERRNO; 915 logmsg("getsockname() failed with error: (%d) %s", 916 error, sstrerror(error)); 917 sclose(sock); 918 return CURL_SOCKET_BAD; 919 } 920 switch(localaddr.sa.sa_family) { 921 case AF_INET: 922 *listenport = ntohs(localaddr.sa4.sin_port); 923 break; 924#ifdef ENABLE_IPV6 925 case AF_INET6: 926 *listenport = ntohs(localaddr.sa6.sin6_port); 927 break; 928#endif 929 default: 930 break; 931 } 932 if(!*listenport) { 933 /* Real failure, listener port shall not be zero beyond this point. */ 934 logmsg("Apparently getsockname() succeeded, with listener port zero."); 935 logmsg("A valid reason for this failure is a binary built without"); 936 logmsg("proper network library linkage. This might not be the only"); 937 logmsg("reason, but double check it before anything else."); 938 sclose(sock); 939 return CURL_SOCKET_BAD; 940 } 941 } 942 943 /* start accepting connections */ 944 rc = listen(sock, 5); 945 if(0 != rc) { 946 error = SOCKERRNO; 947 logmsg("listen(%" CURL_FORMAT_SOCKET_T ", 5) failed with error: (%d) %s", 948 sock, error, sstrerror(error)); 949 sclose(sock); 950 return CURL_SOCKET_BAD; 951 } 952 953 return sock; 954} 955 956 957int main(int argc, char *argv[]) 958{ 959 curl_socket_t sock = CURL_SOCKET_BAD; 960 curl_socket_t msgsock = CURL_SOCKET_BAD; 961 int wrotepidfile = 0; 962 int wroteportfile = 0; 963 const char *pidname = ".socksd.pid"; 964 const char *portname = NULL; /* none by default */ 965 bool juggle_again; 966 int error; 967 int arg = 1; 968 969#ifdef USE_UNIX_SOCKETS 970 const char *unix_socket = NULL; 971 bool unlink_socket = false; 972#endif 973 974 while(argc>arg) { 975 if(!strcmp("--version", argv[arg])) { 976 printf("socksd IPv4%s\n", 977#ifdef ENABLE_IPV6 978 "/IPv6" 979#else 980 "" 981#endif 982 ); 983 return 0; 984 } 985 else if(!strcmp("--pidfile", argv[arg])) { 986 arg++; 987 if(argc>arg) 988 pidname = argv[arg++]; 989 } 990 else if(!strcmp("--portfile", argv[arg])) { 991 arg++; 992 if(argc>arg) 993 portname = argv[arg++]; 994 } 995 else if(!strcmp("--config", argv[arg])) { 996 arg++; 997 if(argc>arg) 998 configfile = argv[arg++]; 999 } 1000 else if(!strcmp("--backend", argv[arg])) { 1001 arg++; 1002 if(argc>arg) 1003 backendaddr = argv[arg++]; 1004 } 1005 else if(!strcmp("--backendport", argv[arg])) { 1006 arg++; 1007 if(argc>arg) 1008 backendport = (unsigned short)atoi(argv[arg++]); 1009 } 1010 else if(!strcmp("--logfile", argv[arg])) { 1011 arg++; 1012 if(argc>arg) 1013 serverlogfile = argv[arg++]; 1014 } 1015 else if(!strcmp("--reqfile", argv[arg])) { 1016 arg++; 1017 if(argc>arg) 1018 reqlogfile = argv[arg++]; 1019 } 1020 else if(!strcmp("--ipv6", argv[arg])) { 1021#ifdef ENABLE_IPV6 1022 socket_domain = AF_INET6; 1023 socket_type = "IPv6"; 1024#endif 1025 arg++; 1026 } 1027 else if(!strcmp("--ipv4", argv[arg])) { 1028 /* for completeness, we support this option as well */ 1029#ifdef ENABLE_IPV6 1030 socket_type = "IPv4"; 1031#endif 1032 arg++; 1033 } 1034 else if(!strcmp("--unix-socket", argv[arg])) { 1035 arg++; 1036 if(argc>arg) { 1037#ifdef USE_UNIX_SOCKETS 1038 struct sockaddr_un sau; 1039 unix_socket = argv[arg]; 1040 if(strlen(unix_socket) >= sizeof(sau.sun_path)) { 1041 fprintf(stderr, 1042 "socksd: socket path must be shorter than %zu chars: %s\n", 1043 sizeof(sau.sun_path), unix_socket); 1044 return 0; 1045 } 1046 socket_domain = AF_UNIX; 1047 socket_type = "unix"; 1048#endif 1049 arg++; 1050 } 1051 } 1052 else if(!strcmp("--port", argv[arg])) { 1053 arg++; 1054 if(argc>arg) { 1055 char *endptr; 1056 unsigned long ulnum = strtoul(argv[arg], &endptr, 10); 1057 port = curlx_ultous(ulnum); 1058 arg++; 1059 } 1060 } 1061 else { 1062 puts("Usage: socksd [option]\n" 1063 " --backend [ipv4 addr]\n" 1064 " --backendport [TCP port]\n" 1065 " --config [file]\n" 1066 " --version\n" 1067 " --logfile [file]\n" 1068 " --pidfile [file]\n" 1069 " --portfile [file]\n" 1070 " --reqfile [file]\n" 1071 " --ipv4\n" 1072 " --ipv6\n" 1073 " --unix-socket [file]\n" 1074 " --bindonly\n" 1075 " --port [port]\n"); 1076 return 0; 1077 } 1078 } 1079 1080#ifdef _WIN32 1081 win32_init(); 1082 atexit(win32_cleanup); 1083 1084 setmode(fileno(stdin), O_BINARY); 1085 setmode(fileno(stdout), O_BINARY); 1086 setmode(fileno(stderr), O_BINARY); 1087#endif 1088 1089 install_signal_handlers(false); 1090 1091 sock = socket(socket_domain, SOCK_STREAM, 0); 1092 1093 if(CURL_SOCKET_BAD == sock) { 1094 error = SOCKERRNO; 1095 logmsg("Error creating socket: (%d) %s", 1096 error, sstrerror(error)); 1097 goto socks5_cleanup; 1098 } 1099 1100 { 1101 /* passive daemon style */ 1102 sock = sockdaemon(sock, &port 1103#ifdef USE_UNIX_SOCKETS 1104 , unix_socket 1105#endif 1106 ); 1107 if(CURL_SOCKET_BAD == sock) { 1108 goto socks5_cleanup; 1109 } 1110#ifdef USE_UNIX_SOCKETS 1111 unlink_socket = true; 1112#endif 1113 msgsock = CURL_SOCKET_BAD; /* no stream socket yet */ 1114 } 1115 1116 logmsg("Running %s version", socket_type); 1117 1118#ifdef USE_UNIX_SOCKETS 1119 if(socket_domain == AF_UNIX) 1120 logmsg("Listening on unix socket %s", unix_socket); 1121 else 1122#endif 1123 logmsg("Listening on port %hu", port); 1124 1125 wrotepidfile = write_pidfile(pidname); 1126 if(!wrotepidfile) { 1127 goto socks5_cleanup; 1128 } 1129 1130 if(portname) { 1131 wroteportfile = write_portfile(portname, port); 1132 if(!wroteportfile) { 1133 goto socks5_cleanup; 1134 } 1135 } 1136 1137 do { 1138 juggle_again = incoming(sock); 1139 } while(juggle_again); 1140 1141socks5_cleanup: 1142 1143 if((msgsock != sock) && (msgsock != CURL_SOCKET_BAD)) 1144 sclose(msgsock); 1145 1146 if(sock != CURL_SOCKET_BAD) 1147 sclose(sock); 1148 1149#ifdef USE_UNIX_SOCKETS 1150 if(unlink_socket && socket_domain == AF_UNIX) { 1151 error = unlink(unix_socket); 1152 logmsg("unlink(%s) = %d (%s)", unix_socket, error, strerror(error)); 1153 } 1154#endif 1155 1156 if(wrotepidfile) 1157 unlink(pidname); 1158 if(wroteportfile) 1159 unlink(portname); 1160 1161 restore_signal_handlers(false); 1162 1163 if(got_exit_signal) { 1164 logmsg("============> socksd exits with signal (%d)", exit_signal); 1165 /* 1166 * To properly set the return status of the process we 1167 * must raise the same signal SIGINT or SIGTERM that we 1168 * caught and let the old handler take care of it. 1169 */ 1170 raise(exit_signal); 1171 } 1172 1173 logmsg("============> socksd quits"); 1174 return 0; 1175} 1176