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 */