/* This file is in the public domain. */ #include #include #include #include #include #include #include #include #include #include #include #include extern const char *__progname; #include "protocol.h" #include "pollloop.h" typedef struct ipaddr IPADDR; typedef struct peer PEER; typedef struct uplink UPLINK; typedef struct listen LISTEN; typedef struct public PUBLIC; typedef struct isource ISOURCE; typedef struct apstate APSTATE; /* * State for parsing ADDR/PORT stuff. This exists so that specific * uses of the parser can tweak it in a failure callback. */ struct apstate { char *addr; char *port; struct addrinfo hints; } ; /* * An item on the stack of includes for config files. */ struct isource { ISOURCE *link; char *file; FILE *f; int lno; } ; /* * A listen-at point. */ struct listen { LISTEN *link; struct sockaddr *sa; int salen; char *txt; int fd; } ; /* * An uplink point. These may come from the config file or may come * from PUBLIC packets, depending on the configuration and in some * cases history. */ struct public { PUBLIC *link; struct sockaddr *sa; time_t stamp; } ; /* * A connection to a peer. */ struct peer { struct sockaddr *lclsa; struct sockaddr *remsa; char *txt; int fd; int cfd; int id; } ; /* * An IP address, without a port number. */ struct ipaddr { int af; union { struct in_addr v4; struct in6_addr v6; } ; } ; /* * The shared-cloudwide secret key, pointer and length. */ static unsigned char *ck; static int cklen; /* * This host's cloud ID. */ static unsigned char cid; /* * This host's list of cloud IP addresses. */ static int ncipaddrs; static IPADDR **cipaddrs; /* * Boolean saying whether this host ignores PUBLIC packets. */ static int ignore_public; /* * Stack of input sources. Used when reading the config. */ static ISOURCE *isrc; /* * Listening points. The difference between types 1 and 2, and between * types 4 and 3, is whether this list is empty or not. */ static LISTEN *listens; #define Cisspace(c) isspace((unsigned char)(c)) #if 0 static sockaddr *root; static int npeers; static int apeers; static PEER **peers; static int pcfd; static int pcid; static void peer_check_connect(void *pv) { PEER *p; int e; socklen_t el; p = pv; remove_poll_id(p->id); el = sizeof(e); if ( (getsockopt(p->cfd,SOL_SOCKET,SO_ERROR,&e,&el) < 0) || (el != sizeof(e)) || e ) { p->id = PL_NOID; close(p->cfd); p->cfd = -1; return; } peer_start_crypto(p); } static void pcfd_rd(void) { struct timersock_event ev; int i; PEER *p; while (read(pcfd,&ev,sizeof(ev)) > 0) ; for (i=npeers-1;i>=0;i--) { p = peers[i]; if ((p->fd >= 0) || (p->cfd >= 0)) continue; p->cfd = socket(p->sa->sa_family,SOCK_STREAM,0); if (p->cfd < 0) continue; fcntl(p->cfd,F_SETFL,fcntl(p->cfd,F_GETFL,0)|O_NONBLOCK); if (connect(p->cfd,p->sa,p->sa->sa_len) < 0) { if (errno == EINPROGRESS) { p->id = add_poll_fd(p->cfd,&rwtest_never,&rwtest_always,0,&peer_check_connect,p); } else { close(p->cfd); p->cfd = -1; } } else { peer_start_crypto(p); } } } static void startup(void) { struct itimerval itv; npeers = 1; apeers = 1; peers = malloc(sizeof(PEER *)); peers[0] = malloc(sizeof(PEER)); peers[0]->addr = sadup(root); peers[0]->fd = -1; peers[0]->cfd = -1; peers[0]->id = PL_NOID; init_polling(); pcfd = socket(AF_TIMER,SOCK_STREAM,0); itv.it_value.tv_sec = 1; itv.it_value.tv_usec = 0; itv.it_interval.tv_sec = 300; itv.it_interval.tv_usec = 0; write(pcfd,&itv,sizeof(itv)); pcid = add_poll_fd(pcfd,&rwtest_always,&rwtest_never,&pcfd_rd,0,0); } #endif static void config_err(const char *, ...) __attribute__((__format__(__printf__,1,2),__noreturn__)); static void config_err(const char *fmt, ...) { va_list ap; char *s; void dump_isrc_chain(ISOURCE *i) { if (i->link) dump_isrc_chain(i->link); fprintf(stderr," \"%s\", line %d:",i->file,i->lno); } va_start(ap,fmt); vasprintf(&s,fmt,ap); va_end(ap); fprintf(stderr,"%s:",__progname); dump_isrc_chain(isrc); fprintf(stderr," %s\n",s); free(s); exit(1); } static void config_err_cleanup(void (*)(void), const char *, ...) __attribute__((__format__(__printf__,2,3),__noreturn__)); static void config_err_cleanup(void (*cleanup)(void), const char *fmt, ...) { va_list ap; char *s; void dump_isrc_chain(ISOURCE *i) { if (i->link) dump_isrc_chain(i->link); fprintf(stderr," \"%s\", line %d:",i->file,i->lno); } va_start(ap,fmt); vasprintf(&s,fmt,ap); va_end(ap); fprintf(stderr,"%s:",__progname); dump_isrc_chain(isrc); fprintf(stderr," %s\n",s); free(s); (*cleanup)(); exit(1); } static void push_isrc(const char *s) { FILE *f; ISOURCE *i; f = fopen(s,"r"); if (f == 0) config_err("can't open config file %s: %s",s,strerror(errno)); i = malloc(sizeof(ISOURCE)); i->file = strdup(s); i->f = f; i->lno = 0; i->link = isrc; isrc = i; } static void pop_isrc(void) { ISOURCE *i; i = isrc; isrc = i->link; free(i->file); fclose(i->f); free(i); } static struct addrinfo *crack_addr_port(const char *s, void (*custom)(APSTATE *)) { APSTATE st; const char *slash; int i; struct addrinfo *rv; slash = index(s,'/'); if (! slash) config_err("no slash in ADDR/PORT"); i = slash - s; st.addr = malloc(i+1); bcopy(s,st.addr,i); st.addr[i] = '\0'; st.port = strdup(slash+1); st.hints.ai_flags = AI_PASSIVE; st.hints.ai_family = PF_UNSPEC; st.hints.ai_socktype = SOCK_STREAM; st.hints.ai_protocol = 0; st.hints.ai_addrlen = 0; st.hints.ai_addr = 0; st.hints.ai_canonname = 0; st.hints.ai_next = 0; if (custom) (*custom)(&st); i = getaddrinfo(st.addr,st.port,&st.hints,&rv); if (i == 0) return(rv); config_err("lookup %s: %s",s,gai_strerror(i)); } static void confline__at(const char *s) { push_isrc(s); } static void confline_key(const char *s) { free(ck); cklen = strlen(s); ck = malloc(cklen); bcopy(s,ck,cklen); } static void confline_keyfile(const char *s) { int fd; struct stat stb; int r; int l; unsigned char *b; fd = open(s,O_RDONLY,0); if (fd < 0) config_err("open %s: %s",s,strerror(errno)); if (fstat(fd,&stb) < 0) config_err("fstat %s: %s",s,strerror(errno)); if (stb.st_size < 1) config_err("%s: empty",s); else if (stb.st_size > 65536) { fprintf(stderr,"%s: %s: too large, using only first 64K\n",__progname,s); l = 65536; } else { l = stb.st_size; } b = malloc(l); r = read(fd,b,l); if (r < 0) config_err("read from %s: %s",s,strerror(errno)); if (r != l) config_err("read from %s: wanted %d, got %d",s,l,r); close(fd); free(ck); ck = b; cklen = l; } static void confline_id(const char *s) { long int id; char *ep; id = strtol(s,&ep,0); if (ep != s) while (*ep && Cisspace(*ep)) ep ++; if (ep == s) config_err("bad/missing ID number"); if (* ep) config_err("junk after ID number"); if ((id < 0) || (id > 255)) config_err("ID %ld is out of range",id); cid = id; } static void confline_ip(const char *s) { IPADDR *a; a = malloc(sizeof(IPADDR)); if (inet_pton(AF_INET,s,&a->v4) > 0) { a->af = AF_INET; } else if (inet_pton(AF_INET6,s,&a->v6) > 0) { a->af = AF_INET6; } else { config_err("unparseable IP `%s'",s); } cipaddrs = realloc(cipaddrs,(ncipaddrs+1)*sizeof(IPADDR *)); cipaddrs[ncipaddrs++] = a; } static void confline_type(const char *s) { if (! strcmp(s,"public")) { ignore_public = 0; } else if (! strcmp(s,"private")) { ignore_public = 1; } else { config_err("bad `type' value `%s'",s); } } static void capcustom_listen(APSTATE *s) { if (! strcmp(s->addr,"*")) { } else if (! strcmp(s->addr,"*4")) { s->hints.ai_family = AF_INET; } else if (! strcmp(s->addr,"*6")) { s->hints.ai_family = AF_INET6; } else { return; } free(s->addr); s->addr = 0; } static void confline_listen(const char *s) { struct addrinfo *ai0; struct addrinfo *ai; LISTEN *l; char *txt; int fd; int e; void errcleanup(void) { if (fd >= 0) close(fd); if (txt) free(txt); freeaddrinfo(ai0); } ai0 = crack_addr_port(s,&capcustom_listen); for (ai=ai0;ai;ai=ai->ai_next) { char hstr[NI_MAXHOST]; char sstr[NI_MAXSERV]; fd = -1; txt = 0; e = getnameinfo(ai->ai_addr,ai->ai_addrlen,&hstr[0],NI_MAXHOST,&sstr[0],NI_MAXSERV,NI_NUMERICHOST|NI_NUMERICSERV); if (e) config_err_cleanup(&errcleanup,"getnameinfo [for %s]: %s",s,gai_strerror(e)); asprintf(&txt,"%s/%s",&hstr[0],&sstr[0]); fd = socket(ai->ai_family,ai->ai_socktype,ai->ai_protocol); if (fd < 0) continue; if (bind(fd,ai->ai_addr,ai->ai_addrlen) < 0) { config_err_cleanup(&errcleanup,"bind %s [for %s]: %s",txt,s,strerror(errno)); } l = malloc(sizeof(LISTEN)); l->sa = malloc(ai->ai_addrlen); bcopy(ai->ai_addr,l->sa,ai->ai_addrlen); l->salen = ai->ai_addrlen; l->txt = txt; l->fd = fd; l->link = listens; listens = l; } } static void confline_uplink(const char *s) { s=s; config_err("`uplink' unimplemented"); } static void config_line(const char *s) { const char *k0; int kl; static struct { const char *key; void (*handler)(const char *); int keylen; } keys[] = { { "@", &confline__at }, { "key", &confline_key }, { "keyfile", &confline_keyfile }, { "id", &confline_id }, { "ip", &confline_ip }, { "type", &confline_type }, { "listen", &confline_listen }, { "uplink", &confline_uplink }, { 0 } }; int i; if (keys[0].keylen == 0) for (i=0;keys[i].key;i++) keys[i].keylen = strlen(keys[i].key); while (*s && Cisspace(*s)) s ++; if (! *s) return; if (*s == '#') return; k0 = s; while (*s && !Cisspace(*s)) s ++; kl = s - k0; while (*s && Cisspace(*s)) s ++; for (i=0;keys[i].key;i++) { if ((kl == keys[i].keylen) && !bcmp(k0,keys[i].key,kl)) { (*keys[i].handler)(s); return; } } config_err("unrecognized config key `%.*s'",kl,k0); } static void config_read(void) { char *lb; int la; int ln; int c; void savec(char ch) { if (ln >= la) lb = realloc(lb,la=ln+16); lb[ln++] = ch; } void cl(void) { savec('\0'); config_line(lb); } lb = 0; la = 0; ln = 0; while <"read"> (1) { c = getc(isrc->f); switch (c) { case EOF: if (ln > 0) cl(); ln = 0; pop_isrc(); if (! isrc) break <"read">; break; case '\n': cl(); ln = 0; break; default: if (ln == 0) isrc->lno ++; savec(c); break; } } free(lb); } static void readconf(const char *fn) { isrc = 0; push_isrc(fn); config_read(); } static void handleargs(int ac, char **av) { int skip; int errs; skip = 0; errs = 0; for (ac--,av++;ac;ac--,av++) { if (skip > 0) { skip --; continue; } if (**av != '-') { fprintf(stderr,"%s: stray argument `%s'\n",__progname,*av); errs ++; continue; } if (0) { needarg:; fprintf(stderr,"%s: %s needs a following argument\n",__progname,*av); errs ++; continue; } #define WANTARG() do { if (++skip >= ac) goto needarg; } while (0) if (!strcmp(*av,"-config")) { WANTARG(); readconf(av[skip]); continue; } #undef WANTARG fprintf(stderr,"%s: unrecognized option `%s'\n",__progname,*av); errs ++; } if (errs) exit(1); } static void init(void) { ck = 0; ncipaddrs = 0; cipaddrs = 0; ignore_public = -1; listens = 0; } static void set_defaults(void) { if (ignore_public < 0) { fprintf(stderr,"%s: no `type' line configured\n",__progname); exit(1); } } int main(int, char **); int main(int ac, char **av) { init(); handleargs(ac,av); set_defaults(); // startup(); return(0); }