Annotation of embedaddon/curl/tests/server/socksd.c, revision 1.1.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>