ccx-utils

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

ucspi-socksserver-access.c (6294B)


      1 #include <string.h>
      2 #include <stdint.h>
      3 #include <unistd.h>
      4 #include <errno.h>
      5 #include <stdlib.h>
      6 #include <assert.h>
      7 
      8 #include <skalibs/exec.h>
      9 #include <skalibs/sgetopt.h>
     10 #include <skalibs/strerr.h>
     11 #include <skalibs/cdb.h>
     12 #include <skalibs/ip46.h>
     13 
     14 #include <s6/accessrules.h>
     15 
     16 #include "socks.h"
     17 
     18 #define PROG "ucspi-socksserver-access"
     19 #define USAGE "ucspi-socksserve-access [ -i rulesdir | -x rulesfile ] prog..."
     20 #define dieusage() strerr_dieusage(100, USAGE)
     21 #define X() strerr_dief1x(101, "internal error")
     22 
     23 typedef struct app_options_s {
     24     unsigned int rulestype;
     25     char const * rules;
     26     char const * port;
     27     char const * addr_type;
     28     char const * addr;
     29     unsigned char socks_version;
     30 } app_options;
     31 
     32 const app_options const * options = NULL;
     33 
     34 typedef struct check_result_s {
     35     s6_accessrules_result_t accepted;
     36     s6_accessrules_params_t params;
     37 } check_result;
     38 
     39 
     40 char * required_getenv(const char *name){
     41     char *x = getenv(name);
     42     if (!x) {
     43         strerr_dienotset(100, name);
     44     }
     45     return x;
     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_fail(socks5_reply_t status)
     66 {
     67     char reply[] = {
     68         SOCKS_VERSION_5,
     69         status,
     70         SOCKS_RESERVED,
     71         SOCKS5_ADDR_TYPE_DOMAIN,
     72         0,  /* domain length */
     73         0,  /* port MSB */
     74         0   /* port LSB */
     75     };
     76     write1(reply, sizeof(reply));
     77     close(1);
     78 }
     79 
     80 void socks4_reply_fail()
     81 {
     82     const char resp[8] = {0x00, SOCKS4_STATUS_FAILED, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
     83     write1(resp, sizeof(resp));
     84     close(1);
     85 }
     86 
     87 void socks_reply_fail_generic(socks5_reply_t status) {
     88     if (options->socks_version == 5) {
     89         socks5_reply_fail(status);
     90     } else {
     91         socks4_reply_fail();
     92     }
     93 }
     94 
     95 void check_access_ip(check_result *result) {
     96     ip46 ip;
     97     if (!ip46_scan(options->addr, &ip)) {
     98         strerr_dieinvalid(100, "SOCKS_ADDR") ;
     99     }
    100     switch (options->rulestype)
    101     {
    102         case 1 :
    103             result->accepted = s6_accessrules_ip46_fs(&ip, (void *)options->rules, &result->params);
    104             break;
    105         case 2 :
    106             {
    107                 cdb c = CDB_ZERO ;
    108                 if (!cdb_init(&c, options->rules)) {
    109                     strerr_diefu2sys(111, "cdb_init ", options->rules);
    110                 }
    111                 result->accepted = s6_accessrules_ip46_cdb(&ip, &c, &result->params);
    112                 if (result->accepted == S6_ACCESSRULES_ALLOW) {
    113                     cdb_free(&c);
    114                 }
    115             }
    116             break;
    117         default : X();
    118     }
    119 }
    120 
    121 s6_accessrules_result_t check_access_dns(check_result *result) {
    122     switch (options->rulestype)
    123     {
    124         case 1 :
    125             result->accepted = s6_accessrules_reversedns_fs(options->addr, (void *)options->rules, &result->params);
    126             break;
    127         case 2 :
    128             {
    129                 cdb c = CDB_ZERO ;
    130                 if (!cdb_init(&c, options->rules)) {
    131                     strerr_diefu2sys(111, "cdb_init ", options->rules);
    132                 }
    133                 result->accepted = s6_accessrules_reversedns_cdb(options->addr, &c, &result->params);
    134                 if (result->accepted == S6_ACCESSRULES_ALLOW) {
    135                     cdb_free(&c);
    136                 }
    137             }
    138             break;
    139         default : X();
    140     }
    141 }
    142 
    143 
    144 
    145 int main (int argc, char const *const *argv)
    146 {
    147     char const *rulestypestr[3] = { "no", "fs", "cdb" } ;
    148     app_options opt;
    149     opt.rulestype = 0;
    150     opt.rules = 0;
    151 
    152     subgetopt l = SUBGETOPT_ZERO ;
    153     for (;;)
    154     {
    155         int o = subgetopt_r(argc, argv, "i:x:", &l) ;
    156         if (o == -1) break ;
    157         switch (o)
    158         {
    159             case 'i' : opt.rules = l.arg ; opt.rulestype = 1 ; break ;
    160             case 'x' : opt.rules = l.arg ; opt.rulestype = 2 ; break ;
    161             default : dieusage() ;
    162         }
    163     }
    164     argc -= l.ind ; argv += l.ind ;
    165     if (!argc) dieusage() ;
    166     if (!*argv[0]) dieusage() ;
    167 
    168     char const * env_socks_version = required_getenv("SOCKS_VERSION");
    169     if(strcmp(env_socks_version, "4") == 0) {
    170         opt.socks_version = 5;
    171     } else if (strcmp(env_socks_version, "5") == 0) {
    172         opt.socks_version = 5;
    173     } else {
    174         strerr_dieinvalid(100, "SOCKS_VERSION") ;
    175     }
    176 
    177     opt.port = required_getenv("SOCKS_PORT");
    178     opt.addr_type = required_getenv("SOCKS_ADDR_TYPE");
    179     opt.addr = required_getenv("SOCKS_ADDR");
    180 
    181     options = &opt;
    182 
    183     if(opt.rulestype == 0) {
    184         xexec(argv);
    185     }
    186 
    187     check_result result = { S6_ACCESSRULES_ALLOW, S6_ACCESSRULES_PARAMS_ZERO };
    188 
    189     if(strcmp(opt.addr_type, "dns") == 0) {
    190         check_access_dns(&result);
    191     } else if ( strcmp(opt.addr_type, "ip4") == 0 || strcmp(opt.addr_type, "ip6") == 0) {
    192         check_access_ip(&result);
    193     } else {
    194         strerr_dieinvalid(100, "SOCKS_ADDR_TYPE") ;
    195     }
    196 
    197     switch (result.accepted) {
    198         case S6_ACCESSRULES_ERROR:
    199             socks_reply_fail_generic(SOCKS5_REPLY_ERR_GENERAL);
    200             strerr_diefu6sys(111, "check ", rulestypestr[opt.rulestype], " ruleset for ", opt.addr_type, " in ", opt.rules);
    201         case S6_ACCESSRULES_ALLOW:
    202             break ;
    203         case S6_ACCESSRULES_DENY:
    204             socks_reply_fail_generic(SOCKS5_REPLY_ERR_FORBIDDEN);
    205             // if (verbosity >= 2) log_deny(getpid(), &remoteip) ;
    206             return 1;
    207         case S6_ACCESSRULES_NOTFOUND:
    208             socks_reply_fail_generic(SOCKS5_REPLY_ERR_FORBIDDEN);
    209             // if (flagdnslookup) {
    210             //     break;
    211             // }
    212             // if (verbosity >= 2) {
    213             //     log_deny(getpid(), opt.addr);
    214             // }
    215             return 1 ;
    216         default: X() ;
    217     }
    218 
    219     if (result.params.exec.len)
    220     {
    221         char *specialargv[4] = { "execlineb", "-c", result.params.exec.s, 0 } ;
    222         xmexec_m((char const *const *)specialargv, result.params.env.s, result.params.env.len) ;
    223     }
    224     xexec(argv);
    225 
    226 }
    227 
    228 /* vim: sw=4 sts=4 et
    229 */