socksd.c (25958B)
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 "first.h" 25 26 #include <stdlib.h> 27 28 /* Function 29 * 30 * Accepts a TCP connection on a custom port (IPv4 or IPv6). Connects to a 31 * given addr + port backend (that is NOT extracted form the client's 32 * request). The backend server default to connect to can be set with 33 * --backend and --backendport. 34 * 35 * Read commands from FILE (set with --config). The commands control how to 36 * act and is reset to defaults each client TCP connect. 37 * 38 * Config file keywords: 39 * 40 * "version [number: 5]" - requires the communication to use this version. 41 * "nmethods_min [number: 1]" - the minimum numberf NMETHODS the client must 42 * state 43 * "nmethods_max [number: 3]" - the minimum numberf NMETHODS the client must 44 * state 45 * "user [string]" - the user name that must match (if method is 2) 46 * "password [string]" - the password that must match (if method is 2) 47 * "backend [IPv4]" - numerical IPv4 address of backend to connect to 48 * "backendport [number:0]" - TCP port of backend to connect to. 0 means use 49 the client's specified port number. 50 * "method [number: 0]" - connect method to respond with: 51 * 0 - no auth 52 * 1 - GSSAPI (not supported) 53 * 2 - user + password 54 * "response [number]" - the decimal number to respond to a connect 55 * SOCKS5: 0 is OK, SOCKS4: 90 is ok 56 * 57 */ 58 59 /* based on sockfilt.c */ 60 61 static const char *backendaddr = "127.0.0.1"; 62 static unsigned short backendport = 0; /* default is use client's */ 63 64 struct socksd_configurable { 65 unsigned char version; /* initial version byte in the request must match 66 this */ 67 unsigned char nmethods_min; /* minimum number of nmethods to expect */ 68 unsigned char nmethods_max; /* maximum number of nmethods to expect */ 69 unsigned char responseversion; 70 unsigned char responsemethod; 71 unsigned char reqcmd; 72 unsigned char connectrep; 73 unsigned short port; /* backend port */ 74 char addr[32]; /* backend IPv4 numerical */ 75 char user[256]; 76 char password[256]; 77 }; 78 79 #define CONFIG_VERSION 5 80 #define CONFIG_NMETHODS_MIN 1 /* unauth, gssapi, auth */ 81 #define CONFIG_NMETHODS_MAX 3 82 #define CONFIG_RESPONSEVERSION CONFIG_VERSION 83 #define CONFIG_RESPONSEMETHOD 0 /* no auth */ 84 #define CONFIG_REQCMD 1 /* CONNECT */ 85 #define CONFIG_PORT backendport 86 #define CONFIG_ADDR backendaddr 87 #define CONFIG_CONNECTREP 0 88 89 static struct socksd_configurable s_config; 90 91 static const char *reqlogfile = "log/socksd-request.log"; 92 93 static void socksd_resetdefaults(void) 94 { 95 logmsg("Reset to defaults"); 96 s_config.version = CONFIG_VERSION; 97 s_config.nmethods_min = CONFIG_NMETHODS_MIN; 98 s_config.nmethods_max = CONFIG_NMETHODS_MAX; 99 s_config.responseversion = CONFIG_RESPONSEVERSION; 100 s_config.responsemethod = CONFIG_RESPONSEMETHOD; 101 s_config.reqcmd = CONFIG_REQCMD; 102 s_config.connectrep = CONFIG_CONNECTREP; 103 s_config.port = CONFIG_PORT; 104 strcpy(s_config.addr, CONFIG_ADDR); 105 strcpy(s_config.user, "user"); 106 strcpy(s_config.password, "password"); 107 } 108 109 static unsigned short shortval(char *value) 110 { 111 unsigned long num = strtoul(value, NULL, 10); 112 return num & 0xffff; 113 } 114 115 static void socksd_getconfig(void) 116 { 117 FILE *fp = fopen(configfile, FOPEN_READTEXT); 118 socksd_resetdefaults(); 119 if(fp) { 120 char buffer[512]; 121 logmsg("parse config file"); 122 while(fgets(buffer, sizeof(buffer), fp)) { 123 char key[32]; 124 char value[260]; 125 if(2 == sscanf(buffer, "%31s %259s", key, value)) { 126 if(!strcmp(key, "version")) { 127 s_config.version = byteval(value); 128 logmsg("version [%d] set", s_config.version); 129 } 130 else if(!strcmp(key, "nmethods_min")) { 131 s_config.nmethods_min = byteval(value); 132 logmsg("nmethods_min [%d] set", s_config.nmethods_min); 133 } 134 else if(!strcmp(key, "nmethods_max")) { 135 s_config.nmethods_max = byteval(value); 136 logmsg("nmethods_max [%d] set", s_config.nmethods_max); 137 } 138 else if(!strcmp(key, "backend")) { 139 strcpy(s_config.addr, value); 140 logmsg("backend [%s] set", s_config.addr); 141 } 142 else if(!strcmp(key, "backendport")) { 143 s_config.port = shortval(value); 144 logmsg("backendport [%d] set", s_config.port); 145 } 146 else if(!strcmp(key, "user")) { 147 strcpy(s_config.user, value); 148 logmsg("user [%s] set", s_config.user); 149 } 150 else if(!strcmp(key, "password")) { 151 strcpy(s_config.password, value); 152 logmsg("password [%s] set", s_config.password); 153 } 154 /* Methods: 155 o X'00' NO AUTHENTICATION REQUIRED 156 o X'01' GSSAPI 157 o X'02' USERNAME/PASSWORD 158 */ 159 else if(!strcmp(key, "method")) { 160 s_config.responsemethod = byteval(value); 161 logmsg("method [%d] set", s_config.responsemethod); 162 } 163 else if(!strcmp(key, "response")) { 164 s_config.connectrep = byteval(value); 165 logmsg("response [%d] set", s_config.connectrep); 166 } 167 } 168 } 169 fclose(fp); 170 } 171 } 172 173 /* RFC 1928, SOCKS5 byte index */ 174 #define SOCKS5_VERSION 0 175 #define SOCKS5_NMETHODS 1 /* number of methods that is listed */ 176 177 /* in the request: */ 178 #define SOCKS5_REQCMD 1 179 #define SOCKS5_RESERVED 2 180 #define SOCKS5_ATYP 3 181 #define SOCKS5_DSTADDR 4 182 183 /* connect response */ 184 #define SOCKS5_REP 1 185 #define SOCKS5_BNDADDR 4 186 187 /* auth request */ 188 #define SOCKS5_ULEN 1 189 #define SOCKS5_UNAME 2 190 191 #define SOCKS4_CD 1 192 #define SOCKS4_DSTPORT 2 193 194 /* connect to a given IPv4 address, not the one asked for */ 195 static curl_socket_t socksconnect(unsigned short connectport, 196 const char *connectaddr) 197 { 198 int rc; 199 srvr_sockaddr_union_t me; 200 curl_socket_t sock = socket(AF_INET, SOCK_STREAM, 0); 201 if(sock == CURL_SOCKET_BAD) 202 return CURL_SOCKET_BAD; 203 memset(&me.sa4, 0, sizeof(me.sa4)); 204 me.sa4.sin_family = AF_INET; 205 me.sa4.sin_port = htons(connectport); 206 me.sa4.sin_addr.s_addr = INADDR_ANY; 207 curlx_inet_pton(AF_INET, connectaddr, &me.sa4.sin_addr); 208 209 rc = connect(sock, &me.sa, sizeof(me.sa4)); 210 211 if(rc) { 212 int error = SOCKERRNO; 213 logmsg("Failed connecting to %s:%hu (%d) %s", 214 connectaddr, connectport, error, sstrerror(error)); 215 return CURL_SOCKET_BAD; 216 } 217 logmsg("Connected fine to %s:%d", connectaddr, connectport); 218 return sock; 219 } 220 221 static curl_socket_t socks4(curl_socket_t fd, 222 unsigned char *buffer, 223 ssize_t rc) 224 { 225 unsigned char response[256 + 16]; 226 curl_socket_t connfd; 227 unsigned char cd; 228 unsigned short s4port; 229 230 if(buffer[SOCKS4_CD] != 1) { 231 logmsg("SOCKS4 CD is not 1: %d", buffer[SOCKS4_CD]); 232 return CURL_SOCKET_BAD; 233 } 234 if(rc < 9) { 235 logmsg("SOCKS4 connect message too short: %zd", rc); 236 return CURL_SOCKET_BAD; 237 } 238 if(!s_config.port) 239 s4port = (unsigned short)((buffer[SOCKS4_DSTPORT] << 8) | 240 (buffer[SOCKS4_DSTPORT + 1])); 241 else 242 s4port = s_config.port; 243 244 connfd = socksconnect(s4port, s_config.addr); 245 if(connfd == CURL_SOCKET_BAD) { 246 /* failed */ 247 cd = 91; 248 } 249 else { 250 /* success */ 251 cd = 90; 252 } 253 response[0] = 0; /* reply version 0 */ 254 response[1] = cd; /* result */ 255 /* copy port and address from connect request */ 256 memcpy(&response[2], &buffer[SOCKS4_DSTPORT], 6); 257 rc = (send)(fd, (char *)response, 8, 0); 258 if(rc != 8) { 259 logmsg("Sending SOCKS4 response failed!"); 260 return CURL_SOCKET_BAD; 261 } 262 logmsg("Sent %zd bytes", rc); 263 loghex(response, rc); 264 265 if(cd == 90) 266 /* now do the transfer */ 267 return connfd; 268 269 if(connfd != CURL_SOCKET_BAD) 270 sclose(connfd); 271 272 return CURL_SOCKET_BAD; 273 } 274 275 static curl_socket_t sockit(curl_socket_t fd) 276 { 277 unsigned char buffer[2*256 + 16]; 278 unsigned char response[2*256 + 16]; 279 ssize_t rc; 280 unsigned char len; 281 unsigned char type; 282 unsigned char rep = 0; 283 unsigned char *address; 284 unsigned short socksport; 285 curl_socket_t connfd = CURL_SOCKET_BAD; 286 unsigned short s5port; 287 288 socksd_getconfig(); 289 290 rc = recv(fd, (char *)buffer, sizeof(buffer), 0); 291 if(rc <= 0) { 292 logmsg("SOCKS identifier message missing, recv returned %zd", rc); 293 return CURL_SOCKET_BAD; 294 } 295 296 logmsg("READ %zd bytes", rc); 297 loghex(buffer, rc); 298 299 if(buffer[SOCKS5_VERSION] == 4) 300 return socks4(fd, buffer, rc); 301 302 if(rc < 3) { 303 logmsg("SOCKS5 identifier message too short: %zd", rc); 304 return CURL_SOCKET_BAD; 305 } 306 307 if(buffer[SOCKS5_VERSION] != s_config.version) { 308 logmsg("VERSION byte not %d", s_config.version); 309 return CURL_SOCKET_BAD; 310 } 311 if((buffer[SOCKS5_NMETHODS] < s_config.nmethods_min) || 312 (buffer[SOCKS5_NMETHODS] > s_config.nmethods_max)) { 313 logmsg("NMETHODS byte not within %d - %d ", 314 s_config.nmethods_min, s_config.nmethods_max); 315 return CURL_SOCKET_BAD; 316 } 317 /* after NMETHODS follows that many bytes listing the methods the client 318 says it supports */ 319 if(rc != (buffer[SOCKS5_NMETHODS] + 2)) { 320 logmsg("Expected %d bytes, got %zd", buffer[SOCKS5_NMETHODS] + 2, rc); 321 return CURL_SOCKET_BAD; 322 } 323 logmsg("Incoming request deemed fine!"); 324 325 /* respond with two bytes: VERSION + METHOD */ 326 response[0] = s_config.responseversion; 327 response[1] = s_config.responsemethod; 328 rc = (send)(fd, (char *)response, 2, 0); 329 if(rc != 2) { 330 logmsg("Sending response failed!"); 331 return CURL_SOCKET_BAD; 332 } 333 logmsg("Sent %zd bytes", rc); 334 loghex(response, rc); 335 336 /* expect the request or auth */ 337 rc = recv(fd, (char *)buffer, sizeof(buffer), 0); 338 if(rc <= 0) { 339 logmsg("SOCKS5 request or auth message missing, recv returned %zd", rc); 340 return CURL_SOCKET_BAD; 341 } 342 343 logmsg("READ %zd bytes", rc); 344 loghex(buffer, rc); 345 346 if(s_config.responsemethod == 2) { 347 /* RFC 1929 authentication 348 +----+------+----------+------+----------+ 349 |VER | ULEN | UNAME | PLEN | PASSWD | 350 +----+------+----------+------+----------+ 351 | 1 | 1 | 1 to 255 | 1 | 1 to 255 | 352 +----+------+----------+------+----------+ 353 */ 354 unsigned char ulen; 355 unsigned char plen; 356 bool login = TRUE; 357 if(rc < 5) { 358 logmsg("Too short auth input: %zd", rc); 359 return CURL_SOCKET_BAD; 360 } 361 if(buffer[SOCKS5_VERSION] != 1) { 362 logmsg("Auth VERSION byte not 1, got %d", buffer[SOCKS5_VERSION]); 363 return CURL_SOCKET_BAD; 364 } 365 ulen = buffer[SOCKS5_ULEN]; 366 if(rc < 4 + ulen) { 367 logmsg("Too short packet for username: %zd", rc); 368 return CURL_SOCKET_BAD; 369 } 370 plen = buffer[SOCKS5_ULEN + ulen + 1]; 371 if(rc < 3 + ulen + plen) { 372 logmsg("Too short packet for ulen %d plen %d: %zd", ulen, plen, rc); 373 return CURL_SOCKET_BAD; 374 } 375 if((ulen != strlen(s_config.user)) || 376 (plen != strlen(s_config.password)) || 377 memcmp(&buffer[SOCKS5_UNAME], s_config.user, ulen) || 378 memcmp(&buffer[SOCKS5_UNAME + ulen + 1], s_config.password, plen)) { 379 /* no match! */ 380 logmsg("mismatched credentials!"); 381 login = FALSE; 382 } 383 response[0] = 1; 384 response[1] = login ? 0 : 1; 385 rc = (send)(fd, (char *)response, 2, 0); 386 if(rc != 2) { 387 logmsg("Sending auth response failed!"); 388 return CURL_SOCKET_BAD; 389 } 390 logmsg("Sent %zd bytes", rc); 391 loghex(response, rc); 392 if(!login) 393 return CURL_SOCKET_BAD; 394 395 /* expect the request */ 396 rc = recv(fd, (char *)buffer, sizeof(buffer), 0); 397 if(rc <= 0) { 398 logmsg("SOCKS5 request message missing, recv returned %zd", rc); 399 return CURL_SOCKET_BAD; 400 } 401 402 logmsg("READ %zd bytes", rc); 403 loghex(buffer, rc); 404 } 405 if(rc < 6) { 406 logmsg("Too short for request: %zd", rc); 407 return CURL_SOCKET_BAD; 408 } 409 410 if(buffer[SOCKS5_VERSION] != s_config.version) { 411 logmsg("Request VERSION byte not %d", s_config.version); 412 return CURL_SOCKET_BAD; 413 } 414 /* 1 == CONNECT */ 415 if(buffer[SOCKS5_REQCMD] != s_config.reqcmd) { 416 logmsg("Request COMMAND byte not %d", s_config.reqcmd); 417 return CURL_SOCKET_BAD; 418 } 419 /* reserved, should be zero */ 420 if(buffer[SOCKS5_RESERVED]) { 421 logmsg("Request COMMAND byte not %d", s_config.reqcmd); 422 return CURL_SOCKET_BAD; 423 } 424 /* ATYP: 425 o IP V4 address: X'01' 426 o DOMAINNAME: X'03' 427 o IP V6 address: X'04' 428 */ 429 type = buffer[SOCKS5_ATYP]; 430 address = &buffer[SOCKS5_DSTADDR]; 431 switch(type) { 432 case 1: 433 /* 4 bytes IPv4 address */ 434 len = 4; 435 break; 436 case 3: 437 /* The first octet of the address field contains the number of octets of 438 name that follow */ 439 len = buffer[SOCKS5_DSTADDR]; 440 len++; 441 break; 442 case 4: 443 /* 16 bytes IPv6 address */ 444 len = 16; 445 break; 446 default: 447 logmsg("Unknown ATYP %d", type); 448 return CURL_SOCKET_BAD; 449 } 450 if(rc < (4 + len + 2)) { 451 logmsg("Request too short: %zd, expected %d", rc, 4 + len + 2); 452 return CURL_SOCKET_BAD; 453 } 454 logmsg("Received ATYP %d", type); 455 456 { 457 FILE *dump; 458 dump = fopen(reqlogfile, "ab"); 459 if(dump) { 460 int i; 461 fprintf(dump, "atyp %u =>", type); 462 switch(type) { 463 case 1: 464 /* 4 bytes IPv4 address */ 465 fprintf(dump, " %u.%u.%u.%u\n", 466 address[0], address[1], address[2], address[3]); 467 break; 468 case 3: 469 /* The first octet of the address field contains the number of octets 470 of name that follow */ 471 fprintf(dump, " %.*s\n", len-1, &address[1]); 472 break; 473 case 4: 474 /* 16 bytes IPv6 address */ 475 for(i = 0; i < 16; i++) { 476 fprintf(dump, " %02x", address[i]); 477 } 478 fprintf(dump, "\n"); 479 break; 480 } 481 fclose(dump); 482 } 483 } 484 485 if(!s_config.port) { 486 unsigned char *portp = &buffer[SOCKS5_DSTADDR + len]; 487 s5port = (unsigned short)((portp[0] << 8) | (portp[1])); 488 } 489 else 490 s5port = s_config.port; 491 492 if(!s_config.connectrep) 493 connfd = socksconnect(s5port, s_config.addr); 494 495 if(connfd == CURL_SOCKET_BAD) { 496 /* failed */ 497 rep = 1; 498 } 499 else { 500 rep = s_config.connectrep; 501 } 502 503 /* */ 504 response[SOCKS5_VERSION] = s_config.responseversion; 505 506 /* 507 o REP Reply field: 508 o X'00' succeeded 509 o X'01' general SOCKS server failure 510 o X'02' connection not allowed by ruleset 511 o X'03' Network unreachable 512 o X'04' Host unreachable 513 o X'05' Connection refused 514 o X'06' TTL expired 515 o X'07' Command not supported 516 o X'08' Address type not supported 517 o X'09' to X'FF' unassigned 518 */ 519 response[SOCKS5_REP] = rep; 520 response[SOCKS5_RESERVED] = 0; /* must be zero */ 521 response[SOCKS5_ATYP] = type; /* address type */ 522 523 /* mirror back the original addr + port */ 524 525 /* address or hostname */ 526 memcpy(&response[SOCKS5_BNDADDR], address, len); 527 528 /* port number */ 529 memcpy(&response[SOCKS5_BNDADDR + len], 530 &buffer[SOCKS5_DSTADDR + len], sizeof(socksport)); 531 532 rc = (send)(fd, (char *)response, (SEND_TYPE_ARG3)(len + 6), 0); 533 if(rc != (len + 6)) { 534 logmsg("Sending connect response failed!"); 535 return CURL_SOCKET_BAD; 536 } 537 logmsg("Sent %zd bytes", rc); 538 loghex(response, rc); 539 540 if(!rep) 541 return connfd; 542 543 if(connfd != CURL_SOCKET_BAD) 544 sclose(connfd); 545 546 return CURL_SOCKET_BAD; 547 } 548 549 struct perclient { 550 size_t fromremote; 551 size_t fromclient; 552 curl_socket_t remotefd; 553 curl_socket_t clientfd; 554 bool used; 555 }; 556 557 /* return non-zero when transfer is done */ 558 static int tunnel(struct perclient *cp, fd_set *fds) 559 { 560 ssize_t nread; 561 ssize_t nwrite; 562 char buffer[512]; 563 if(FD_ISSET(cp->clientfd, fds)) { 564 /* read from client, send to remote */ 565 nread = recv(cp->clientfd, buffer, sizeof(buffer), 0); 566 if(nread > 0) { 567 nwrite = send(cp->remotefd, (char *)buffer, 568 (SEND_TYPE_ARG3)nread, 0); 569 if(nwrite != nread) 570 return 1; 571 cp->fromclient += nwrite; 572 } 573 else 574 return 1; 575 } 576 if(FD_ISSET(cp->remotefd, fds)) { 577 /* read from remote, send to client */ 578 nread = recv(cp->remotefd, buffer, sizeof(buffer), 0); 579 if(nread > 0) { 580 nwrite = send(cp->clientfd, (char *)buffer, 581 (SEND_TYPE_ARG3)nread, 0); 582 if(nwrite != nread) 583 return 1; 584 cp->fromremote += nwrite; 585 } 586 else 587 return 1; 588 } 589 return 0; 590 } 591 592 /* 593 sockfdp is a pointer to an established stream or CURL_SOCKET_BAD 594 595 if sockfd is CURL_SOCKET_BAD, listendfd is a listening socket we must 596 accept() 597 */ 598 static bool socksd_incoming(curl_socket_t listenfd) 599 { 600 fd_set fds_read; 601 fd_set fds_write; 602 fd_set fds_err; 603 int clients = 0; /* connected clients */ 604 struct perclient c[2]; 605 606 memset(c, 0, sizeof(c)); 607 if(got_exit_signal) { 608 logmsg("signalled to die, exiting..."); 609 return FALSE; 610 } 611 612 #ifdef HAVE_GETPPID 613 /* As a last resort, quit if socks5 process becomes orphan. */ 614 if(getppid() <= 1) { 615 logmsg("process becomes orphan, exiting"); 616 return FALSE; 617 } 618 #endif 619 620 do { 621 int i; 622 ssize_t rc; 623 int error = 0; 624 curl_socket_t sockfd = listenfd; 625 int maxfd = (int)sockfd; 626 627 FD_ZERO(&fds_read); 628 FD_ZERO(&fds_write); 629 FD_ZERO(&fds_err); 630 631 /* there's always a socket to wait for */ 632 #if defined(__DJGPP__) 633 #pragma GCC diagnostic push 634 #pragma GCC diagnostic ignored "-Warith-conversion" 635 #endif 636 FD_SET(sockfd, &fds_read); 637 #if defined(__DJGPP__) 638 #pragma GCC diagnostic pop 639 #endif 640 641 for(i = 0; i < 2; i++) { 642 if(c[i].used) { 643 curl_socket_t fd = c[i].clientfd; 644 #if defined(__DJGPP__) 645 #pragma GCC diagnostic push 646 #pragma GCC diagnostic ignored "-Warith-conversion" 647 #endif 648 FD_SET(fd, &fds_read); 649 #if defined(__DJGPP__) 650 #pragma GCC diagnostic pop 651 #endif 652 if((int)fd > maxfd) 653 maxfd = (int)fd; 654 fd = c[i].remotefd; 655 #if defined(__DJGPP__) 656 #pragma GCC diagnostic push 657 #pragma GCC diagnostic ignored "-Warith-conversion" 658 #endif 659 FD_SET(fd, &fds_read); 660 #if defined(__DJGPP__) 661 #pragma GCC diagnostic pop 662 #endif 663 if((int)fd > maxfd) 664 maxfd = (int)fd; 665 } 666 } 667 668 do { 669 /* select() blocking behavior call on blocking descriptors please */ 670 rc = select(maxfd + 1, &fds_read, &fds_write, &fds_err, NULL); 671 if(got_exit_signal) { 672 logmsg("signalled to die, exiting..."); 673 return FALSE; 674 } 675 } while((rc == -1) && ((error = SOCKERRNO) == SOCKEINTR)); 676 677 if(rc < 0) { 678 logmsg("select() failed with error (%d) %s", 679 error, sstrerror(error)); 680 return FALSE; 681 } 682 683 if((clients < 2) && FD_ISSET(sockfd, &fds_read)) { 684 curl_socket_t newfd = accept(sockfd, NULL, NULL); 685 if(CURL_SOCKET_BAD == newfd) { 686 error = SOCKERRNO; 687 logmsg("accept() failed with error (%d) %s", 688 error, sstrerror(error)); 689 } 690 else { 691 curl_socket_t remotefd; 692 logmsg("====> Client connect, " 693 "Read config from %s", configfile); 694 remotefd = sockit(newfd); /* SOCKS until done */ 695 if(remotefd == CURL_SOCKET_BAD) { 696 logmsg("====> Client disconnect"); 697 sclose(newfd); 698 } 699 else { 700 struct perclient *cp = &c[0]; 701 logmsg("====> Tunnel transfer"); 702 703 if(c[0].used) 704 cp = &c[1]; 705 cp->fromremote = 0; 706 cp->fromclient = 0; 707 cp->clientfd = newfd; 708 cp->remotefd = remotefd; 709 cp->used = TRUE; 710 clients++; 711 } 712 713 } 714 } 715 for(i = 0; i < 2; i++) { 716 struct perclient *cp = &c[i]; 717 if(cp->used) { 718 if(tunnel(cp, &fds_read)) { 719 logmsg("SOCKS transfer completed. Bytes: < %zu > %zu", 720 cp->fromremote, cp->fromclient); 721 sclose(cp->clientfd); 722 sclose(cp->remotefd); 723 cp->used = FALSE; 724 clients--; 725 } 726 } 727 } 728 } while(clients); 729 730 return TRUE; 731 } 732 733 static int test_socksd(int argc, char *argv[]) 734 { 735 curl_socket_t sock = CURL_SOCKET_BAD; 736 curl_socket_t msgsock = CURL_SOCKET_BAD; 737 int wrotepidfile = 0; 738 int wroteportfile = 0; 739 bool juggle_again; 740 int error; 741 int arg = 1; 742 743 const char *unix_socket = NULL; 744 #ifdef USE_UNIX_SOCKETS 745 bool unlink_socket = false; 746 #endif 747 748 pidname = ".socksd.pid"; 749 serverlogfile = "log/socksd.log"; 750 configfile = "socksd.config"; 751 server_port = 8905; 752 753 while(argc > arg) { 754 if(!strcmp("--version", argv[arg])) { 755 printf("socksd IPv4%s\n", 756 #ifdef USE_IPV6 757 "/IPv6" 758 #else 759 "" 760 #endif 761 ); 762 return 0; 763 } 764 else if(!strcmp("--pidfile", argv[arg])) { 765 arg++; 766 if(argc > arg) 767 pidname = argv[arg++]; 768 } 769 else if(!strcmp("--portfile", argv[arg])) { 770 arg++; 771 if(argc > arg) 772 portname = argv[arg++]; 773 } 774 else if(!strcmp("--config", argv[arg])) { 775 arg++; 776 if(argc > arg) 777 configfile = argv[arg++]; 778 } 779 else if(!strcmp("--backend", argv[arg])) { 780 arg++; 781 if(argc > arg) 782 backendaddr = argv[arg++]; 783 } 784 else if(!strcmp("--backendport", argv[arg])) { 785 arg++; 786 if(argc > arg) 787 backendport = (unsigned short)atoi(argv[arg++]); 788 } 789 else if(!strcmp("--logfile", argv[arg])) { 790 arg++; 791 if(argc > arg) 792 serverlogfile = argv[arg++]; 793 } 794 else if(!strcmp("--reqfile", argv[arg])) { 795 arg++; 796 if(argc > arg) 797 reqlogfile = argv[arg++]; 798 } 799 else if(!strcmp("--ipv6", argv[arg])) { 800 #ifdef USE_IPV6 801 socket_domain = AF_INET6; 802 socket_type = "IPv6"; 803 #endif 804 arg++; 805 } 806 else if(!strcmp("--ipv4", argv[arg])) { 807 /* for completeness, we support this option as well */ 808 #ifdef USE_IPV6 809 socket_type = "IPv4"; 810 #endif 811 arg++; 812 } 813 else if(!strcmp("--unix-socket", argv[arg])) { 814 arg++; 815 if(argc > arg) { 816 #ifdef USE_UNIX_SOCKETS 817 struct sockaddr_un sau; 818 unix_socket = argv[arg]; 819 if(strlen(unix_socket) >= sizeof(sau.sun_path)) { 820 fprintf(stderr, 821 "socksd: socket path must be shorter than %u chars: %s\n", 822 (unsigned int)sizeof(sau.sun_path), unix_socket); 823 return 0; 824 } 825 socket_domain = AF_UNIX; 826 socket_type = "unix"; 827 #endif 828 arg++; 829 } 830 } 831 else if(!strcmp("--port", argv[arg])) { 832 arg++; 833 if(argc > arg) { 834 char *endptr; 835 unsigned long ulnum = strtoul(argv[arg], &endptr, 10); 836 server_port = util_ultous(ulnum); 837 arg++; 838 } 839 } 840 else { 841 puts("Usage: socksd [option]\n" 842 " --backend [ipv4 addr]\n" 843 " --backendport [TCP port]\n" 844 " --config [file]\n" 845 " --version\n" 846 " --logfile [file]\n" 847 " --pidfile [file]\n" 848 " --portfile [file]\n" 849 " --reqfile [file]\n" 850 " --ipv4\n" 851 " --ipv6\n" 852 " --unix-socket [file]\n" 853 " --bindonly\n" 854 " --port [port]\n"); 855 return 0; 856 } 857 } 858 859 #ifdef _WIN32 860 if(win32_init()) 861 return 2; 862 #endif 863 864 CURLX_SET_BINMODE(stdin); 865 CURLX_SET_BINMODE(stdout); 866 CURLX_SET_BINMODE(stderr); 867 868 install_signal_handlers(false); 869 870 sock = socket(socket_domain, SOCK_STREAM, 0); 871 872 if(CURL_SOCKET_BAD == sock) { 873 error = SOCKERRNO; 874 logmsg("Error creating socket (%d) %s", 875 error, sstrerror(error)); 876 goto socks5_cleanup; 877 } 878 879 { 880 /* passive daemon style */ 881 sock = sockdaemon(sock, &server_port, unix_socket, FALSE); 882 if(CURL_SOCKET_BAD == sock) { 883 goto socks5_cleanup; 884 } 885 #ifdef USE_UNIX_SOCKETS 886 unlink_socket = true; 887 #endif 888 msgsock = CURL_SOCKET_BAD; /* no stream socket yet */ 889 } 890 891 logmsg("Running %s version", socket_type); 892 893 #ifdef USE_UNIX_SOCKETS 894 if(socket_domain == AF_UNIX) 895 logmsg("Listening on Unix socket %s", unix_socket); 896 else 897 #endif 898 logmsg("Listening on port %hu", server_port); 899 900 wrotepidfile = write_pidfile(pidname); 901 if(!wrotepidfile) { 902 goto socks5_cleanup; 903 } 904 905 if(portname) { 906 wroteportfile = write_portfile(portname, server_port); 907 if(!wroteportfile) { 908 goto socks5_cleanup; 909 } 910 } 911 912 do { 913 juggle_again = socksd_incoming(sock); 914 } while(juggle_again); 915 916 socks5_cleanup: 917 918 if((msgsock != sock) && (msgsock != CURL_SOCKET_BAD)) 919 sclose(msgsock); 920 921 if(sock != CURL_SOCKET_BAD) 922 sclose(sock); 923 924 #ifdef USE_UNIX_SOCKETS 925 if(unlink_socket && socket_domain == AF_UNIX && unix_socket) { 926 error = unlink(unix_socket); 927 logmsg("unlink(%s) = %d (%s)", unix_socket, error, strerror(error)); 928 } 929 #endif 930 931 if(wrotepidfile) 932 unlink(pidname); 933 if(wroteportfile) 934 unlink(portname); 935 936 restore_signal_handlers(false); 937 938 if(got_exit_signal) { 939 logmsg("============> socksd exits with signal (%d)", exit_signal); 940 /* 941 * To properly set the return status of the process we 942 * must raise the same signal SIGINT or SIGTERM that we 943 * caught and let the old handler take care of it. 944 */ 945 raise(exit_signal); 946 } 947 948 logmsg("============> socksd quits"); 949 return 0; 950 }