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>