ccx-utils

Miscellaneous utilities written in C
git clone https://ccx.te2000.cz/git/ccx-utils
Log | Files | Refs

ucspi-socksserver-connected.c (4321B)


      1 #include <assert.h>
      2 #include <errno.h>
      3 #include <stdlib.h>
      4 #include <fcntl.h>
      5 #include <unistd.h>
      6 #include <sys/socket.h>
      7 #include <netinet/in.h>
      8 
      9 #include <skalibs/exec.h>
     10 #include <skalibs/strerr.h>
     11 #include <skalibs/types.h>
     12 
     13 #include "socks.h"
     14 
     15 #define PROG "ucspi-socketserver-connect"
     16 
     17 #define SOCKS_RESERVED 0x00
     18 #define SOCKS_VERSION_4 0x04
     19 #define SOCKS_VERSION_5 0x05
     20 
     21 typedef unsigned char socks5_addr_type_t;
     22 #define SOCKS5_ADDR_TYPE_IP4 0x01
     23 #define SOCKS5_ADDR_TYPE_DOMAIN 0x03
     24 #define SOCKS5_ADDR_TYPE_IP6 0x04
     25 
     26 typedef unsigned char socks5_reply_t;
     27 #define SOCKS5_REPLY_OK 0x00 // succeeded
     28 #define SOCKS5_REPLY_ERR_GENERAL 0x01 // general SOCKS server failure
     29 #define SOCKS5_REPLY_ERR_FORBIDDEN 0x02 // connection not allowed by ruleset
     30 #define SOCKS5_REPLY_ERR_NET_UNREACH 0x03 // Network unreachable
     31 #define SOCKS5_REPLY_ERR_HOST_UNREACH 0x04 // Host unreachable
     32 #define SOCKS5_REPLY_ERR_CONN_REFUSED 0x05 // Connection refused
     33 #define SOCKS5_REPLY_ERR_TTL 0x06 // TTL expired
     34 #define SOCKS5_REPLY_ERR_CMD 0x07 // Command not supported
     35 #define SOCKS5_REPLY_ERR_ATYPE 0x08 // Address type not supported
     36 
     37 void fd_block(int fd)
     38 {
     39     int flags = fcntl(fd, F_GETFL);
     40     if(flags == -1) {
     41         strerr_dief1sys(111, "fcntl() getfd");
     42     }
     43     if(fcntl(fd, F_SETFL, flags | O_NONBLOCK) < 0) {
     44         strerr_dief1sys(111, "fcntl() setfd");
     45     }
     46 }
     47 
     48 void write1(const void *buf, ssize_t n)
     49 {
     50     ssize_t nwrite, left = n;
     51     while (left > 0) {
     52         if ((nwrite = write(STDOUT_FILENO, buf, left)) < 0) {
     53             if (errno == EINTR || errno == EAGAIN) {
     54                 continue;
     55             }
     56             strerr_diefu1sys(111, "write(stdout)");
     57         } else {
     58             assert(left >= nwrite);
     59             left -= nwrite;
     60             buf += nwrite;
     61         }
     62     }
     63 }
     64 
     65 void socks5_reply_ok(socks5_addr_type_t addr_type, void *addr, in_port_t port)
     66 {
     67     size_t size;
     68     switch(addr_type) {
     69         case SOCKS5_ADDR_TYPE_IP4:
     70             size = 4;
     71             break;
     72         case SOCKS5_ADDR_TYPE_IP6:
     73             size = 16;
     74             break;
     75         case SOCKS5_ADDR_TYPE_DOMAIN:
     76             size = strlen(addr);
     77             if(size > 0xff) {
     78                 strerr_dieinvalid(110, "local hostname too long") ;
     79             }
     80             break;
     81     }
     82 
     83     char reply[] = {
     84         SOCKS_VERSION_5,
     85         SOCKS5_REPLY_OK,
     86         SOCKS_RESERVED,
     87         addr_type,
     88     };
     89     write1(reply, sizeof(reply));
     90 
     91     if (addr_type == SOCKS5_ADDR_TYPE_DOMAIN) {
     92         /* prefix domain by length */
     93         unsigned char domain_len = size;
     94         write1(&domain_len, 1);
     95     }
     96     write1(addr, size);
     97 
     98     /* should be already in network byte order (from sin_port) */
     99     write1(&port, sizeof(port));
    100 }
    101 
    102 void socks4_send_reply(int status)
    103 {
    104     char resp[8] = {0x00, (char)status, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
    105     write1(resp, sizeof(resp));
    106 }
    107 
    108 int main (int argc, char const *const *argv)
    109 {
    110     char const *env_socks_version = getenv("SOCKS_VERSION");
    111     if (!env_socks_version) {
    112         strerr_dienotset(100, "SOCKS_VERSION");
    113     }
    114 
    115     /* fd_block(STDIN_FILENO); */
    116     fd_block(STDOUT_FILENO);
    117 
    118     if (strcmp(env_socks_version, "5") == 0) {
    119 
    120         struct sockaddr_storage local_addr;
    121         socklen_t addrlen = sizeof(local_addr);
    122         if(getsockname(6, (struct sockaddr *)&local_addr, &addrlen) != 0) {
    123             strerr_diefu1sys(111, "getsockname(6, ...)");
    124         }
    125         assert(addrlen <= sizeof(local_addr));
    126         switch(local_addr.ss_family) {
    127             case AF_INET:
    128                 struct sockaddr_in *in_addr = (struct sockaddr_in *)&local_addr;
    129                 socks5_reply_ok(SOCKS5_ADDR_TYPE_IP4, &in_addr->sin_addr, in_addr->sin_port);
    130                 break;
    131             case AF_INET6:
    132                 struct sockaddr_in6 *in6_addr = (struct sockaddr_in6 *)&local_addr;
    133                 socks5_reply_ok(SOCKS5_ADDR_TYPE_IP6, &in6_addr->sin6_addr, in6_addr->sin6_port);
    134                 break;
    135             default:
    136                 socks5_reply_ok(SOCKS5_ADDR_TYPE_DOMAIN, "localhost", 0);
    137                 break;
    138         }
    139 
    140     } else if (strcmp(env_socks_version, "4") == 0) {
    141         socks4_send_reply(SOCKS4_STATUS_OK);
    142 
    143     } else {
    144         strerr_dieinvalid(100, "SOCKS_VERSION") ;
    145 
    146     }
    147     xexec(argv+1);
    148     return 110;
    149 }
    150 
    151 /* vim: sw=4 sts=4 et
    152 */