/* This file is in the public domain. */ #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; /* * 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; 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 listens to PUBLIC packets. */ static int listen_public; /* * Stack of input sources. Used when reading the config. */ static ISOURCE *isrc; #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 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 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")) { listen_public = 1; } else if (! strcmp(s,"private")) { listen_public = 0; } else { config_err("bad `type' value `%s'",s); } } static void confline_listen(const char *s) { s=s; config_err("`listen' unimplemented"); } 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[] = { { "@", &push_isrc }, { "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; listen_public = -1; } int main(int, char **); int main(int ac, char **av) { init(); handleargs(ac,av); // startup(); return(0); }