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