quickjs-tart

quickjs-based runtime for wallet-core logic
Log | Files | Refs | README | LICENSE

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 }