/* // @(#) proxtcfg.c - parse proxy config file // // License: Creative Commons CC0 // http://creativecommons.org/publicdomain/zero/1.0/legalcode // Author: James Sainsbury // toves@sdf.lonestar.org // */ # include # include # include # include # include # include # include # include # include # include "constants.h" # include "new.h" # include "cfile.h" # include "portlist.h" # include "proxycfg.h" /* if we are building a shared library */ # if !defined( SOLIB) # define SOLIB # endif /* // Convert a string usually ip address or fqdn to in_addr_t (NBO) */ static int str_to_inaddr (char ip[], in_addr_t* adrp) { int result = ok; if (inet_pton (AF_INET, ip, adrp)==0) { struct addrinfo hints; struct addrinfo* pres = 0; memset(&hints, 0, sizeof(hints)); result = err; hints.ai_family = PF_INET; if (getaddrinfo (ip, 0, &hints, &pres) == ok) { struct sockaddr_in* s = (struct sockaddr_in*)pres->ai_addr; if (s) { *adrp = s->sin_addr.s_addr; result = ok; } } freeaddrinfo (pres); } return result; } /* // Convert string "host" "port" to sockaddr_in structure */ static int sock_from_hostport (struct sockaddr_in* s, socklen_t slen, const char* ip, const char* port) { int result = err; struct addrinfo hints; struct addrinfo* pres; memset (&hints, 0, sizeof(hints)); hints.ai_socktype = SOCK_STREAM; hints.ai_family = PF_INET; hints.ai_protocol = IPPROTO_TCP; result = getaddrinfo (ip, port, &hints, &pres); if (result == ok) { result = err; if (pres->ai_family == PF_INET && pres->ai_socktype == SOCK_STREAM) { if (pres->ai_addr) { if (pres->ai_addrlen <= slen) { memcpy (s, pres->ai_addr, pres->ai_addrlen); result = ok; } } } freeaddrinfo (pres); } return result; } /* // ----------------------------------------------------------------- */ static int parse_portlist (PORTLIST* pl, char ports[]) { int result = ok; char* t = ports; char* p = 0; while ((p = strsep (&t, ","))!= 0 && result==ok) { h_port_t low = 0; h_port_t high = 0; char* s = strchr (p, '-'); if (s) { *s++ = '\0'; low = atoi (p); high = atoi (s); if (low > high) { h_port_t tmp = low; low = high; high = tmp; } result = portlist_append_range (pl, low, high); } else { low = atoi (p); result = portlist_append_port (pl, low); } } return result; } static int make_portlist (PORTLIST** plp, char ports[]) { PORTLIST* pl = 0; int result = portlist_Create(&pl); if (result == ok) { int ch = ports[0]; char* p = ports; switch (ch) { case '*': case '-': portlist_negate (pl); result = ok; break; case '!': portlist_negate (pl); default: result = parse_portlist (pl, p); } } if (result == ok) { *plp = pl; } return result; } /* // [!]10.117.192.0/255.255.240.0 */ typedef struct proxydest { int negated; in_addr_t destination; in_addr_t mask; } PROXYDEST; typedef struct destination { PROXYDEST d; PORTLIST* p; } DESTINATION; /* // [!]192.168.243.0/19 [!]80,443 DIRECT // * * proxy.dom.ain.org:8080 */ typedef struct proxydef { SOCKADDR* proxysock; char* auth; } PROXYDEF; typedef struct proxyentry { DESTINATION dest; PROXYDEF proxy; } PROXYENTRY; enum { PROXYENTRY_SIZEMIN = 32 }; struct cfg_proxy { size_t size; size_t used; PROXYENTRY* cfg; }; SOLIB int cfgproxy_Create (CFG_PROXY** cfp, size_t desired) { int result = err; size_t n = desired < PROXYENTRY_SIZEMIN ? PROXYENTRY_SIZEMIN : desired; CFG_PROXY* cf = 0; if (New(cf)==ok) { PROXYENTRY* cfg = 0; if (NewArr(cfg, n)==ok) { cf->cfg = cfg; cf->size = n; cf->used = 0; *cfp = cf; result = ok; } else { Dispose (cf); } } return result; } static int cfgproxy_append (CFG_PROXY* cf, char* destip, char* mask, char* ports, char* proxy, char* proxyport, char* auth) { int result = err; in_addr_t dst; in_addr_t msk; int negated = false; char* dports = "*"; PORTLIST* pl = 0; SOCKADDR* proxysock = 0; size_t used = cf->used; size_t size = cf->size; PROXYENTRY* cfg = &(cf->cfg[used]); if (used+1 > size) { size_t newsize = 2*size; if (ReSizeArr(cf->cfg, newsize)==ok) { cf->size = newsize; return cfgproxy_append (cf, destip, mask, ports, proxy, proxyport, auth); } else return err; } if (destip[0] == '!') { negated = true; ++destip; } if (*destip == '*') { destip = "0.0.0.0"; inet_pton (AF_INET, "0.0.0.0", &msk); } else if (strchr (mask, '.')==0) { int cidr = atoi(mask); if (0 <= cidr && cidr <= 32) { int shift = 32 - cidr; msk = htonl((~0u)<dest.d.negated = negated; cfg->dest.d.destination = dst; cfg->dest.d.mask = msk; cfg->dest.p = pl; cfg->proxy.proxysock = proxysock; cfg->proxy.auth = auth; cf->used = used+1; return result; } const char SEPS[] = " \t"; static char MASK[] = "255.255.255.255"; static char PROXY[] = "proxy"; static char PROXYPORT[] = "8080"; static int cfg_parseline (CFG_PROXY* pcf, char* line) { int result = ok; char* l = line; char* t = strchr (l, '#'); char* destip = 0; char* mask = MASK; char* portlist = 0; char* proxy = PROXY; char* proxyport = PROXYPORT; char* auth = 0; if (t) { *t = '\0'; } while (*l != '\0' && isspace(*l)) ++l; if (*l == '\0') return result; while ((t = strsep(&l, SEPS))!=0 && *t == '\0') ; if (t) { destip = t; char* m = strchr (t, '/'); if (m) { *m++ = '\0'; mask = m; } } while ((t = strsep(&l, SEPS))!=0 && *t == '\0') ; if (t) { portlist = t; } while ((t = strsep(&l, SEPS))!=0 && *t == '\0') ; if (t) { proxy = t; char* p = strchr (t, ':'); if (p) { *p++ = '\0'; proxyport = p; } } while ((t = strsep(&l, SEPS))!=0 && *t == '\0') ; if (t) { auth = t; } return cfgproxy_append (pcf, destip, mask, portlist, proxy, proxyport, auth); } SOLIB int cfgproxy_init (CFG_PROXY** cfp, char* cfgfile) { CFG_PROXY* cf = 0; int result = cfgproxy_Create (&cf, 0); if (result == ok) { CFILE* cfd = 0; cfgproxy_append (cf, "127.0.0.0", "255.0.0.0", "*", "-", 0, 0); cfgproxy_append (cf, "0.0.0.0", "255.255.255.255", "*", "-", 0, 0); result = cfile_open (&cfd, cfgfile); if (result == ok) { char line[BUFSIZ]; while (cfile_getline (cfd, line, sizeof(line)-1)!=EOF) { cfg_parseline (cf, line); } cfile_close (cfd); *cfp = cf; } cfgproxy_append (cf, "0.0.0.0", "0.0.0.0", "*", "-", 0, 0); } return result; } # if defined(DEBUG) int cfgproxy_printcfg (CFG_PROXY* cf, FILE* output) { size_t i = 0; for (; i < cf->used; ++i) { PROXYENTRY pe = cf->cfg[i]; PROXYDEST pd = pe.dest.d; PORTLIST* pl = pe.dest.p; char ps [64]; char pq [64]; inet_ntop (AF_INET, &pe.dest.d.destination, ps, sizeof(ps)); inet_ntop (AF_INET, &pe.dest.d.mask, pq, sizeof(pq)); fprintf (output, "%s/%s\t", ps, pq); portlist_print (pl, output); } return ok; } # endif /* // Lookup dest:port return true if proxy found and not direct // return false otherwise */ SOLIB int cfgproxy_proxyfor (CFG_PROXY* cf, in_addr_t dest, in_port_t inport, SOCKADDR** spp, char** authp) { int result = false; size_t i = 0; size_t j = cf->used; PROXYENTRY* cfg = cf->cfg; h_port_t port = ntohs (inport); # if defined(DEBUG) cfgproxy_printcfg (cf, stderr); # endif while (i != j) { PROXYENTRY pe = cfg[i]; PROXYDEST pd = pe.dest.d; PORTLIST* pl = pe.dest.p; int match = (pd.destination & pd.mask)==(dest & pd.mask); if (pd.negated) match = !match; if (match && portlist_contains (pl, port)) { *spp = pe.proxy.proxysock; *authp = pe.proxy.auth; j = i; result = ((*spp) != 0); /* !DIRECT */ } else ++i; } return result; }