/* This file is in the public domain. */ static const char *RND_DEV = "/dev/urandom"; #define REKEY_N_BASE 0x10000 /* XXX 0x3fff0000 */ #define QLIMIT 65536 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include extern const char *__progname; #include "oq.h" #include "protocol.h" #include "pollloop.h" #include "stdio-util.h" typedef enum { CR_ACTIVE = 1, CR_PASSIVE } CONNROLE; typedef struct ipaddr IPADDR; typedef struct peer PEER; typedef struct uplink UPLINK; typedef struct listen LISTEN; typedef struct isource ISOURCE; typedef struct apstate APSTATE; typedef struct cryptinit CRYPTINIT; typedef struct nap NAP; typedef struct pq PQ; typedef struct ipmap IPMAP; /* * State for "delay briefly" operations. s is an AF_TIMER socket. id * is the poll ID. done is a function to be called when the delay is * over, with arg its argument. */ struct nap { int s; int id; void (*done)(void *); void *arg; } ; /* * State for the initial crypto exchange. Ths is a separate struct so * it can be discarded easily when the exchange is over. * * Rc is actually only 16 bytes long. The array here is dimensioned * [49] to simplify the code in rd_crypto_passive_1 - this way we can * read the whole reply from the connecter into Rc, rather than * creating another buffer for it. */ struct cryptinit { CONNROLE myrole; PEER *p; unsigned char Rc[49]; unsigned char Rl[16]; unsigned char b; unsigned char s[32]; int n; } ; /* * 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; int id; } ; /* * An uplink point. Each of these is somewhere we want to maintain a * connection to. */ struct uplink { UPLINK *link; struct sockaddr *sa; int salen; char *txt; time_t stamp; PEER *peer; } ; /* * A connection to a peer. * * These include connections which we have initiated but which have not * yet completed at the socket layer and connections which have come * up at the socket layer but have not passed crypto verification; * they are just like other PEERs except that the callbacks for their * poll IDs are unusual. * * Kd is for the direction in which we write. Kd_ is what the protocol * document calls Kd', ie, Kd in the other direction. */ struct peer { struct sockaddr *lclsa; int lclsalen; struct sockaddr *remsa; int remsalen; char *txt; int fd; int id; OQ oq; unsigned char Kd[32]; unsigned char Kd_[32]; int bytes_to_rekey_out; int bytes_to_rekey_in; unsigned int n_rekeys_out; unsigned int n_rekeys_in; ARC4_STATE a4_out; ARC4_STATE a4_in; unsigned char *ibuf; int ifill; int iwant; void (*readdone)(PEER *); unsigned int want; #define WANT_REACH 0x00000001 #define WANT_PUBLIC 0x00000002 #define WANT_IPMAP 0x00000004 int peerid; PQ *pq; PQ **pqt; } ; /* * An IP address, without a port number. */ struct ipaddr { int af; union { struct in_addr v4; struct in6_addr v6; } ; } ; /* * An IP packet, queued for output. */ struct pq { PQ *link; IPADDR dst; void *body; int bodylen; } ; /* * An IP<->ID mapping entry. */ struct ipmap { IPMAP *link; IPADDR a; unsigned char id; } ; /* * 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; /* * Uplink points. Initially, these come from uplink declarations in * the configuration; if ignore_public is false, the list is mutated * as PUBLIC packets come in and timers expire. */ static UPLINK *uplinks; /* * The list of PEERs. */ static int npeers; static int apeers; static PEER **peers; /* * The fd and poll ID for the timer socket driving attempts to * (re)connect to uplinks. */ static int pcfd; static int pcid; /* * The block ID for cleaning up dead PEERs, and a boolean indicating * whether it might have work to do. */ static int peercleanup_id; static int peercleanup_needed; /* * Descriptor open onto RND_DEV. */ static int rndfd; /* * The queue of packets waiting to be sent. */ static PQ *pktq; static PQ **pktqt; /* * Pathname of the tun instance, descriptor open on it, and poll ID for * reading from it. */ static char *tunpath; static int tunfd; static int tunid; /* * Block ID for generating output traffic, and boolean indicating it * might have something to do. */ static int sendid; static int want_send; /* * Table of PEERs, indexed by cloud member ID. This allows us to * quickly find the PEER, if any, for a given ID. */ static PEER *peertbl[256]; /* * Cloud member ID reachability data. This is indexed by the member ID * we want to reach. reachtbl is the member ID we can reach it via, * or -1 if we think it's unreachable. reachtime is the time_t at * which reachtbl was last updated. reachdist is the hop count to the * target. (reachtime and reachdist are meaningless for unreachable * targets.) This is updated by REACH packets. */ static int reachtbl[256]; static int reachdist[256]; static time_t reachtime[256]; /* * IP <-> ID mapping. This is not 1-to-1; there may be any number of * IPs (zero, one, or more) for a given ID - but at most one ID for an * IP. This is updated by IPMAP packets. */ static IPMAP *ipmaps; #define Cisspace(c) isspace((unsigned char)(c)) 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(FILE *f, const char *name) { ISOURCE *i; i = malloc(sizeof(ISOURCE)); i->file = strdup(name); 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, int flags, 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 = flags; 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) { FILE *f; f = fopen(s,"r"); if (f == 0) config_err("can't open %s: %s",s,strerror(errno)); push_isrc(f,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 nonblocking_fd(int fd) { fcntl(fd,F_SETFL,fcntl(fd,F_GETFL,0)|O_NONBLOCK); } 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,AI_PASSIVE,&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)); } if (listen(fd,10) < 0) { config_err_cleanup(&errcleanup,"bind %s [for %s]: %s",txt,s,strerror(errno)); } nonblocking_fd(fd); 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 new_uplink(const struct sockaddr *sa, int salen, time_t stamp) { char hstr[NI_MAXHOST]; char sstr[NI_MAXSERV]; UPLINK *u; char *txt; int e; txt = 0; e = getnameinfo(sa,salen,&hstr[0],NI_MAXHOST,&sstr[0],NI_MAXSERV,NI_NUMERICHOST|NI_NUMERICSERV); if (e) { asprintf(&txt,"getnameinfo [af %d]: %s",sa->sa_family,gai_strerror(e)); } else { asprintf(&txt,"%s/%s",&hstr[0],&sstr[0]); } u = malloc(sizeof(UPLINK)); u->sa = malloc(salen); bcopy(sa,u->sa,salen); u->salen = salen; u->txt = txt; u->stamp = stamp; u->peer = 0; u->link = uplinks; uplinks = u; } static void confline_uplink(const char *s) { struct addrinfo *ai0; struct addrinfo *ai; ai0 = crack_addr_port(s,0,0); for (ai=ai0;ai;ai=ai->ai_next) new_uplink(ai->ai_addr,ai->ai_addrlen,0); freeaddrinfo(ai0); } static void confline_tun(const char *s) { long int n; char *ep; free(tunpath); n = strtol(s,&ep,0); if (ep == s) { tunpath = strdup(s); } else { while (*ep && Cisspace(*ep)) ep ++; if (* ep) { tunpath = strdup(s); } else { asprintf(&tunpath,"/dev/tun%ld",n); } } } 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 }, { "tun", &confline_tun }, { 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) { FILE *f; isrc = 0; f = fopen(fn,"r"); if (f == 0) config_err("can't open config file %s: %s",fn,strerror(errno)); push_isrc(f,fn); config_read(); } static void readconfline(const char *line) { FILE *f; isrc = 0; f = open_strings_r(line,OSR_STRLEN,"\n",1,OSR_END); push_isrc(f,""); 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; } if (!strcmp(*av,"-C")) { WANTARG(); readconfline(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; uplinks = 0; npeers = 0; apeers = 0; peers = 0; tunpath = 0; } static void set_defaults(void) { if (ignore_public < 0) { fprintf(stderr,"%s: no `type' line configured\n",__progname); exit(1); } if (uplinks == 0) { fprintf(stderr,"%s: no uplinks configured\n",__progname); exit(1); } if (tunpath == 0) { fprintf(stderr,"%s: no tun device configured\n",__progname); exit(1); } } static PEER *new_peer(void) { PEER *p; if (npeers >= apeers) peers = realloc(peers,(apeers=npeers+1)*sizeof(PEER *)); p = malloc(sizeof(PEER)); p->lclsa = 0; p->remsa = 0; p->txt = 0; p->fd = -1; p->id = PL_NOID; oq_init(&p->oq); arc4_init(&p->a4_out); arc4_init(&p->a4_in); p->ibuf = malloc(1+2+65535); p->ifill = 0; p->peerid = -1; p->pq = 0; p->pqt = &p->pq; peers[npeers++] = p; return(p); } static int peercleanup(void *arg __attribute__((__unused__))) { int i; PEER *p; if (! peercleanup_needed) return(BLOCK_NIL); peercleanup_needed = 0; for (i=npeers;i>=0;i--) { p = peers[i]; if (p->fd >= 0) continue; free(p->lclsa); free(p->remsa); free(p->txt); oq_flush(&p->oq); free(p->ibuf); while (p->pq) { PQ *q; q = p->pq; p->pq = q->link; free(q); } free(p); npeers --; if (i < npeers) peers[i] = peers[npeers]; } return(BLOCK_LOOP); } static void close_peer(PEER *p) { close(p->fd); p->fd = -1; peercleanup_needed = 1; } static void close_cryptinit(CRYPTINIT *ci) { remove_poll_id(ci->p->id); close_peer(ci->p); free(ci); } static int wr_oq(OQ *q, int fd, const char *txt) { int n; n = oq_writev(q,fd); if (n < 0) { switch (errno) { case EINTR: case EWOULDBLOCK: return(0); } fprintf(stderr,"%s: write to %s: %s\n",__progname,txt,strerror(errno)); return(-1); } oq_dropdata(q,n); return(0); } static void wr_cryptinit(void *civ) { CRYPTINIT *ci; ci = civ; if (wr_oq(&ci->p->oq,ci->p->fd,ci->p->txt) < 0) close_cryptinit(ci); } static int wtest_cryptinit(void *civ) { return(oq_nonempty(&((CRYPTINIT *)civ)->p->oq)); } static int rd_crypto_1(CRYPTINIT *ci, unsigned char *Rv, int len) { int n; n = read(ci->p->fd,&Rv[ci->n],len-ci->n); if (n < 0) { switch (errno) { case EINTR: case EWOULDBLOCK: return(1); break; } close_cryptinit(ci); return(1); } if (n == 0) { close_cryptinit(ci); return(1); } ci->n += n; if (ci->n < len) return(1); return(0); } static void random_data(unsigned char *buf, int len) { int n; while (len > 0) { n = read(rndfd,buf,len); if (n < 0) { fprintf(stderr,"%s: read from %s: %s\n",__progname,RND_DEV,strerror(errno)); exit(1); } if (n == 0) { fprintf(stderr,"%s: read EOF from %s\n",__progname,RND_DEV); exit(1); } buf += n; len -= n; } } static unsigned int crc32(const void *data, ...) #define CRC32TERM ((const void *)0) { unsigned int h; static const unsigned int crctbl[256] = { #include "crctable" }; va_list ap; int len; const unsigned char *dp; h = 0x31bb29b0; va_start(ap,data); while (data) { len = va_arg(ap,int); for (dp=data;len>0;len--) { h = (h >> 8) ^ crctbl[(h^*dp++)&0xff]; } data = va_arg(ap,const void *); } va_end(ap); return(h); } static void hash(unsigned char *rv, const void *data, ...) #define HASHTERM ((const void *)0) { void *hh; va_list ap; int len; hh = sha256_init(); va_start(ap,data); while (data) { len = va_arg(ap,int); sha256_process_bytes(hh,data,len); data = va_arg(ap,const void *); } va_end(ap); sha256_result(hh,rv); } static void nap_done(void *nv) { NAP *n; n = nv; remove_poll_id(n->id); (*n->done)(n->arg); close(n->s); free(n); } static void start_nap(unsigned int sec, unsigned int usec, void (*done)(void *), void *arg) { int s; NAP *n; struct itimerval itv; s = socket(AF_TIMER,SOCK_STREAM,0); n = malloc(sizeof(NAP)); n->s = s; n->done = done; n->arg = arg; n->id = add_poll_fd(s,&rwtest_always,&rwtest_never,&nap_done,0,n); itv.it_value.tv_sec = sec; itv.it_value.tv_usec = usec; itv.it_interval.tv_sec = 0; itv.it_interval.tv_usec = 0; write(s,&itv,sizeof(itv)); } static void cryptinit_gen_b_s(CRYPTINIT *ci) { unsigned int t; unsigned char b; unsigned char xs[32]; unsigned char s[36]; unsigned char hk[32]; int sl; int i; int j; void *rj1; void *rj2; unsigned char key1[32]; unsigned char B; static const unsigned char zero[32] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }; unsigned char data2[32]; t = crc32(ck,cklen,&ci->Rl[0],16,ck,cklen,&ci->Rc[0],16,ck,cklen,CRC32TERM); b = ((t >> 24) ^ (t >> 16) ^ (t >> 8) ^ t) & 0xff; for (i=0;i<32;i++) xs[i] = (b + i) & 0xff; sl = 36; bcopy(&ci->Rl[0],&s[0],16); s[16] = (t >> 24) & 0xff; s[17] = (t >> 16) & 0xff; s[18] = (t >> 8) & 0xff; s[19] = t & 0xff; bcopy(&ci->Rc[0],&s[20],16); hash(&hk[0],ck,cklen,HASHTERM); rj1 = rijndael_init(8,8); rj2 = rijndael_init(8,8); rijndael_set_key(rj2,&hk[0]); for (i=0;i<64;i++) { B = i; hash(&key1[0],ck,cklen,&B,1,&s[0],sl,&B,1,ck,cklen,HASHTERM); rijndael_set_key(rj1,&key1[0]); rijndael_encrypt(rj1,&zero[0],&data2[0]); for (j=32-1;j>=0;j--) data2[j] ^= xs[j]; rijndael_encrypt(rj2,&data2[0],&s[0]); sl = 32; } ci->b = b; bcopy(&s[0],&ci->s[0],32); rijndael_done(rj1); rijndael_done(rj2); } static void cryptinit_gen_Kd(CRYPTINIT *ci, unsigned char Kl2c[32], unsigned char Kc2l[32]) { hash(&Kl2c[0],"->",2,ck,cklen,&ci->Rl[0],16,"->",2,&ci->Rc[0],16,ck,cklen,"->",2,HASHTERM); hash(&Kc2l[0],"<-",2,ck,cklen,&ci->Rl[0],16,"<-",2,&ci->Rc[0],16,ck,cklen,"<-",2,HASHTERM); } static unsigned int min_bits(unsigned int v) { int n; if (v == 0) return(0); n = 1; if (v & 0xffff0000) { v >>= 16; n += 16; } if (v & 0xff00) { v >>= 8; n += 8; } if (v & 0xf0) { v >>= 4; n += 4; } if (v & 0xc) { v >>= 2; n += 2; } if (v & 0x2) { /* v >>= 1; unnecessary */ n += 1; } return(n); } static void gen_a4_key( ARC4_STATE *a4, const unsigned char *Kd, const unsigned char *Kd_, const unsigned char *Rk, unsigned int n_rekeys ) { unsigned char t0[96]; unsigned char *t; int tlen; unsigned char k[256]; int klen; void *rj; int i; int B; unsigned char SERbuf[8]; unsigned char *SER; int SERlen; unsigned char h[32]; SER = &SERbuf[8]; SERlen = 0; while (n_rekeys) { *--SER = n_rekeys & 0xff; n_rekeys >>= 8; SERlen ++; } rj = rijndael_init(8,8); rijndael_set_key(rj,Kd); bcopy(Kd,&t0[0],32); bcopy(Rk,&t0[32],32); bcopy(Kd_,&t0[64],32); t = &t0[0]; tlen = 96; klen = 0; for (i=0;i<8;i++) { B = i; hash(&h[0],t,tlen,Rk,32,&B,1,SER,SERlen,t,tlen,HASHTERM); rijndael_encrypt(rj,&h[0],&k[klen]); t = &k[klen]; tlen = 32; klen += 32; } rijndael_done(rj); arc4_setkey(a4,&k[0],klen,65536); } static void send_rekey(PEER *p) { unsigned char Rk[32]; int L; unsigned char *P1; unsigned char P2[32]; int N; unsigned char enc[32]; void *rj; unsigned int Lmask; random_data(&Rk[0],32); L = 8; // XXX select intelligently P1 = malloc(L); random_data(P1,L); random_data(&P2[0],32); N = min_bits(L); if ((N < 0) || (N > 15)) abort(); P2[0] = (P2[0] & 0x0f) | (N << 4); Lmask = (~0U) << N; P2[31] = ((P2[31] & Lmask) | (L & ~Lmask)) & 0xff; P2[30] = ((P2[30] & (Lmask >> 8)) | ((L & ~Lmask) >> 8)) & 0xff; rj = rijndael_init(8,8); rijndael_set_key(rj,&p->Kd[0]); rijndael_encrypt(rj,&Rk[0],&enc[0]); oq_queue_copy(&p->oq,&enc[0],32); rijndael_encrypt(rj,&P2[0],&enc[0]); oq_queue_copy(&p->oq,&enc[0],32); rijndael_done(rj); oq_queue_free(&p->oq,P1,L); gen_a4_key(&p->a4_out,&p->Kd[0],&p->Kd_[0],&Rk[0],p->n_rekeys_out); p->bytes_to_rekey_out = REKEY_N_BASE + (P2[1] << 8) + P2[2]; p->n_rekeys_out ++; } static int wtest_peer(void *pv) { return(oq_nonempty(&((PEER *)pv)->oq)); } static void rd_peer_rekey_2(PEER *p) { p->ifill = 0; } static void rd_peer_rekey_1(PEER *p) { void *rj; unsigned char Rk[32]; unsigned char P2[32]; int L; int N; rj = rijndael_init(8,8); rijndael_set_key(rj,&p->Kd_[0]); rijndael_decrypt(rj,p->ibuf,&Rk[0]); rijndael_decrypt(rj,p->ibuf+32,&P2[0]); N = P2[0] >> 4; L = ((P2[30] * 256U) | P2[31]) & ~((~0U) << N); gen_a4_key(&p->a4_in,&p->Kd_[0],&p->Kd[0],&Rk[0],p->n_rekeys_in); p->bytes_to_rekey_in = REKEY_N_BASE + (P2[1] << 8) + P2[2]; p->n_rekeys_in ++; if (L == 0) { rd_peer_rekey_2(p); } else { p->ifill = 1; p->iwant = L + 1; p->readdone = &rd_peer_rekey_2; } } static void set_quietdrop(int fd) { int on; on = 1; setsockopt(fd,IPPROTO_TCP,TCP_QUIETDROP,&on,sizeof(on)); } static void send_encrypted(PEER *p, const void *data, int len) { unsigned char *obuf; if (len < 1) return; obuf = malloc(len); arc4_crypt(&p->a4_out,data,len,obuf); oq_queue_free(&p->oq,obuf,len); p->bytes_to_rekey_out -= len; } static void send_postpacket(PEER *p) { if (p->bytes_to_rekey_out < 1) send_rekey(p); } static int addr_port_match(const struct sockaddr *a, const struct sockaddr *b) { if (a->sa_family != b->sa_family) return(0); switch (a->sa_family) { default: abort(); break; case AF_INET: return( ( ((const struct sockaddr_in *)a)->sin_addr.s_addr == ((const struct sockaddr_in *)b)->sin_addr.s_addr ) && ( ((const struct sockaddr_in *)a)->sin_port == ((const struct sockaddr_in *)b)->sin_port ) ); break; case AF_INET6: return( ( ((const struct sockaddr_in6 *)a)->sin6_port == ((const struct sockaddr_in6 *)b)->sin6_port ) && IN6_ARE_ADDR_EQUAL( &((const struct sockaddr_in6 *)a)->sin6_addr, &((const struct sockaddr_in6 *)b)->sin6_addr ) ); break; } } static int ipaddr_match(const IPADDR *a, const IPADDR *b) { if (a->af != b->af) return(0); switch (a->af) { default: abort(); break; case AF_INET: return(a->v4.s_addr==b->v4.s_addr); break; case AF_INET6: return(IN6_ARE_ADDR_EQUAL(&a->v6,&b->v6)); break; } } static void learn_public(const struct sockaddr *sa, int salen, time_t stamp) { UPLINK *u; for (u=uplinks;u;u=u->link) { if (addr_port_match(u->sa,sa)) { if (u->stamp) u->stamp = stamp; return; } } new_uplink(sa,salen,stamp); } static void peer_pkt_ip(PEER *p, const unsigned char *data, int len) { XXX;;; // contains IP packet } static void canreach( unsigned char tgt, unsigned char dist, unsigned char via, time_t stamp ) { if ( (reachtbl[tgt] < 0) || (dist < reachdist[tgt]) || (via == reachtbl[tgt]) || ( (dist == reachdist[tgt]) && (stamp-reachtime[tgt] > 180) ) ) { if (dist >= REACH_INFINITY) { reachtbl[tgt] = -1; } else { reachtbl[tgt] = via; reachdist[tgt] = dist; reachtime[tgt] = stamp; } } } static void peer_pkt_reach(PEER *p, const unsigned char *data, int len) { int o; time_t now; if (p->peerid < 0) return; time(&now); o = 0; while (1) { if (o > len) abort(); if (o == len) break; if (o+2 > len) { fprintf(stderr,"%s: peer %s sent REACH that overruns packet (%d > %d)\n",__progname,p->txt,o+2,len); close_peer(p); return; } canreach(data[o],data[o+1],p->peerid,now); } } static void peer_pkt_public(PEER *p, const unsigned char *data, int len) { __label__ throwto; int o; void overrun(int o) { if (o <= len) return; fprintf(stderr,"%s: peer %s sent PUBLIC that overruns packet (%d > %d)\n",__progname,p->txt,o,len); goto throwto; } if (0) { throwto:; close_peer(p); return; } if (ignore_public) return; o = 0; while (1) { if (o > len) abort(); if (o == len) break; switch (data[o]) { default: fprintf(stderr,"%s: unknown IP type %d in PUBLIC from %s\n",__progname,data[o],p->txt); close_peer(p); return; break; case IPTYPE_v4: { struct sockaddr_in sin; overrun(o+11); sin.sin_len = sizeof(sin); sin.sin_family = AF_INET; bcopy(data+o+1,&sin.sin_addr,4); sin.sin_port = htons((data[o+5]*256)+data[o+6]); learn_public( (void *)&sin, sizeof(sin), (data[o+7] * 0x01000000) + (data[o+8] * 0x00010000) + (data[o+9] * 0x00000100) + (data[o+10] * 0x00000001) ); o += 11; } break; case IPTYPE_v6: { struct sockaddr_in6 sin6; overrun(o+23); sin6.sin6_len = sizeof(sin6); sin6.sin6_family = AF_INET6; bcopy(data+o+1,&sin6.sin6_addr,16); sin6.sin6_port = htons((data[o+17]*256)+data[o+18]); learn_public( (void *)&sin6, sizeof(sin6), (data[o+19] * 0x01000000) + (data[o+20] * 0x00010000) + (data[o+21] * 0x00000100) + (data[o+22] * 0x00000001) ); o += 23; } break; } } } static void new_ipmap(unsigned char id, const IPADDR *a) { IPMAP *m; m = malloc(sizeof(IPMAP)); m->a = *a; m->id = id; m->link = ipmaps; ipmaps = m; } static void replace_ipmap(unsigned char id, const IPADDR *a) { IPMAP *m; for (m=ipmaps;m;m=m->link) { if (ipaddr_match(&m->a,a)) { m->id = id; return; } } new_ipmap(id,a); } static void clear_ipmaps(unsigned char id) { IPMAP *m; IPMAP **mp; mp = &ipmaps; while ((m = *mp)) { if (m->id == id) { *mp = m->link; free(m); } else { mp = &m->link; } } } static void peer_pkt_ipmap(PEER *p, const unsigned char *data, int len) { __label__ throwto; int o; IPADDR a; void overrun(int o) { if (o > len) { fprintf(stderr,"%s: packet overrun in IPMAP from %s (%d > %d)\n",__progname,p->txt,o,len); goto throwto; } } if (0) { throwto:; close_peer(p); return; } if (p->peerid < 0) return; clear_ipmaps(p->peerid); o = 0; while (1) { if (o > len) abort(); if (o == len) break; overrun(o+2); switch (data[o+1]) { default: fprintf(stderr,"%s: unknown IP type %d in IPMAP from %s\n",__progname,data[o+1],p->txt); close_peer(p); return; break; case IPTYPE_v4: overrun(o+6); a.af = AF_INET; bcopy(data+o+2,&a.v4,4); o += 6; break; case IPTYPE_v6: overrun(o+18); a.af = AF_INET6; bcopy(data+o+2,&a.v6,16); o += 18; break; } replace_ipmap(p->peerid,&a); } } static void peer_pkt_iam(PEER *p, const unsigned char *data, int len) { if (len != 1) { fprintf(stderr,"%s: peer %s sent IAM with length %d\n",__progname,p->txt,len); close_peer(p); } if (p->peerid >= 0) { fprintf(stderr,"%s: peer %s sent another IAM\n",__progname,p->txt); close_peer(p); } if (data[0] == cid) { fprintf(stderr,"%s: peer %s claims to be us\n",__progname,p->txt); set_quietdrop(p->fd); close_peer(p); } if (peertbl[data[0]] && (cid < data[0])) close_peer(peertbl[data[0]]); p->peerid = data[0]; peertbl[data[0]] = p; } static void peer_packet(PEER *p, unsigned char type, const unsigned char *data, int len) { switch (type) { case PKT_IGNORE: break; case PKT_IP: peer_pkt_ip(p,data,len); break; case PKT_REACH: peer_pkt_reach(p,data,len); break; case PKT_PUBLIC: peer_pkt_public(p,data,len); break; case PKT_IPMAP: peer_pkt_ipmap(p,data,len); break; case PKT_IAM: peer_pkt_iam(p,data,len); break; default: fprintf(stderr,"%s: received bad packet type 0x%02x from %s\n",__progname,type,p->txt); close_peer(p); break; } } static void rd_peer_packet_2(PEER *p) { arc4_crypt(&p->a4_in,&p->ibuf[3],p->iwant-3,&p->ibuf[3]); p->bytes_to_rekey_in -= p->iwant; peer_packet(p,p->ibuf[0],&p->ibuf[3],p->iwant-3); p->ifill = 0; } static void rd_peer_packet_1(PEER *p) { int len; arc4_crypt(&p->a4_in,&p->ibuf[0],3,&p->ibuf[0]); len = (p->ibuf[1] * 256) + p->ibuf[2]; if (len == 0) { peer_packet(p,p->ibuf[0],0,0); } else { p->iwant = len + 3; p->readdone = &rd_peer_packet_2; } } static void rd_peer(void *pv) { PEER *p; int n; p = pv; while (1) { if (p->fd < 0) return; if (p->ifill == 0) { if (p->bytes_to_rekey_in < 1) { p->iwant = 64; p->readdone = &rd_peer_rekey_1; } else { p->iwant = 3; p->readdone = &rd_peer_packet_1; } } if (p->ifill >= p->iwant) abort(); n = read(p->fd,p->ibuf+p->ifill,p->iwant-p->ifill); if (n < 0) { switch (errno) { case EINTR: case EWOULDBLOCK: return; } fprintf(stderr,"%s: read from %s: %s\n",__progname,p->txt,strerror(errno)); close_peer(p); return; } if (n == 0) { fprintf(stderr,"%s: read EOF from %s\n",__progname,p->txt); close_peer(p); return; } p->ifill += n; if (p->ifill >= p->iwant) (*p->readdone)(p); } } static void wr_peer(void *pv) { PEER *p; p = pv; if (p->fd < 0) return; if (wr_oq(&p->oq,p->fd,p->txt) < 0) close_peer(p); } static void send_iam(PEER *p) { unsigned char opkt[4]; opkt[0] = PKT_IAM; opkt[1] = 0; opkt[2] = 1; opkt[3] = cid; send_encrypted(p,&opkt[0],4); send_postpacket(p); } static void start_peer(PEER *p) { p->n_rekeys_out = 0; p->n_rekeys_in = 0; p->bytes_to_rekey_in = 0; p->want = WANT_REACH | WANT_PUBLIC | WANT_IPMAP; want_send = 1; send_rekey(p); send_iam(p); p->id = add_poll_fd(p->fd,&rwtest_always,&wtest_peer,&rd_peer,&wr_peer,p); } static void start_peer_v(void *pv) { start_peer(pv); } static void rd_crypto_active_1(void *civ) { CRYPTINIT *ci; PEER *p; ci = civ; if (rd_crypto_1(ci,&ci->Rl[0],16)) return; p = ci->p; random_data(&ci->Rc[0],16); cryptinit_gen_b_s(ci); oq_queue_copy(&p->oq,&ci->Rc[0],16); oq_queue_copy(&p->oq,&ci->b,1); oq_queue_copy(&p->oq,&ci->s[0],32); cryptinit_gen_Kd(ci,&p->Kd_[0],&p->Kd[0]); remove_poll_id(p->id); free(ci); start_peer(p); } static void rd_crypto_passive_1(void *civ) { CRYPTINIT *ci; PEER *p; ci = civ; if (rd_crypto_1(ci,&ci->Rc[0],49)) return; p = ci->p; cryptinit_gen_b_s(ci); if ( (ci->b != ci->Rc[16]) || bcmp(&ci->s[0],&ci->Rc[17],32) ) { set_quietdrop(p->fd); close_cryptinit(ci); return; } cryptinit_gen_Kd(ci,&p->Kd[0],&p->Kd_[0]); remove_poll_id(p->id); free(ci); start_nap(5,0,&start_peer_v,p); } static void peer_start_crypto(CRYPTINIT *ci) { switch (ci->myrole) { case CR_ACTIVE: ci->p->id = add_poll_fd(ci->p->fd,&rwtest_always,&wtest_cryptinit,&rd_crypto_active_1,&wr_cryptinit,ci); break; case CR_PASSIVE: ci->p->id = add_poll_fd(ci->p->fd,&rwtest_always,&wtest_cryptinit,&rd_crypto_passive_1,&wr_cryptinit,ci); random_data(&ci->Rl[0],16); oq_queue_copy(&ci->p->oq,&ci->Rl[0],16); break; default: abort(); break; } ci->n = 0; } static int peer_get_sockaddrs(PEER *p) { struct sockaddr_storage ss; socklen_t l; FILE *f; void build_txt(struct sockaddr *sa, int salen, FILE *f) { char hstr[NI_MAXHOST]; char sstr[NI_MAXSERV]; int e; e = getnameinfo(sa,salen,&hstr[0],NI_MAXHOST,&sstr[0],NI_MAXSERV,NI_NUMERICHOST|NI_NUMERICSERV); if (e) { if (e == EAI_SYSTEM) { fprintf(f,"[getnameinfo: %s]",strerror(errno)); } else { fprintf(f,"[getnameinfo: %s]",gai_strerror(e)); } } else { fprintf(f,"%s/%s",&hstr[0],&sstr[0]); } } if (! p->lclsa) { l = sizeof(ss); if (getsockname(p->fd,(struct sockaddr *)&ss,&l) < 0) { fprintf(stderr,"%s: getsockname for %s: %s\n",__progname,p->txt,strerror(errno)); return(-1); } p->lclsa = malloc(l); bcopy(&ss,p->lclsa,l); p->lclsalen = l; } if (! p->remsa) { l = sizeof(ss); if (getpeername(p->fd,(struct sockaddr *)&ss,&l) < 0) { fprintf(stderr,"%s: getpeername for %s: %s\n",__progname,p->txt,strerror(errno)); return(-1); } p->remsa = malloc(l); bcopy(&ss,p->remsa,l); p->remsalen = l; } free(p->txt); f = open_accum(&p->txt,0); build_txt(p->lclsa,p->lclsalen,f); fprintf(f," <-> "); build_txt(p->remsa,p->remsalen,f); fclose(f); return(0); } static void peer_connect_ok(PEER *p) { CRYPTINIT *ci; if (peer_get_sockaddrs(p) < 0) { close_peer(p); return; } ci = malloc(sizeof(CRYPTINIT)); ci->myrole = CR_ACTIVE; ci->p = p; peer_start_crypto(ci); } static void peer_connect_done(void *pv) { PEER *p; int e; socklen_t el; p = pv; remove_poll_id(p->id); p->id = PL_NOID; el = sizeof(e); if ( (getsockopt(p->fd,SOL_SOCKET,SO_ERROR,&e,&el) < 0) || (el != sizeof(e)) || e ) { close_peer(p); return; } peer_connect_ok(p); } static void pcfd_rd(void *arg __attribute__((__unused__))) { struct timersock_event ev; UPLINK *ul; PEER *p; int s; while (read(pcfd,&ev,sizeof(ev)) > 0) ; for (ul=uplinks;ul;ul=ul->link) { if (ul->peer) continue; s = socket(ul->sa->sa_family,SOCK_STREAM,0); if (s < 0) continue; nonblocking_fd(s); p = new_peer(); p->fd = s; p->id = PL_NOID; p->remsa = malloc(ul->salen); bcopy(ul->sa,p->remsa,ul->salen); p->remsalen = ul->salen; asprintf(&p->txt,"(connect to %s)",ul->txt); if (connect(s,p->remsa,p->remsalen) < 0) { if (errno == EINPROGRESS) { p->id = add_poll_fd(s,&rwtest_always,&rwtest_never,0,&peer_connect_done,p); } else { close_peer(p); continue; } } else { peer_connect_ok(p); } } } static void open_random(void) { rndfd = open(RND_DEV,O_RDONLY,0); if (rndfd < 0) { fprintf(stderr,"%s: %s: %s\n",__progname,RND_DEV,strerror(errno)); exit(1); } } static void open_tun(void) { int v; tunfd = open(tunpath,O_RDWR,0); if (tunfd < 0) { fprintf(stderr,"%s: %s: %s\n",__progname,tunpath,strerror(errno)); exit(1); } v = IFF_POINTOPOINT; if (ioctl(tunfd,TUNSIFMODE,&v) < 0) { fprintf(stderr,"%s: TUNSIFMODE %s: %s\n",__progname,tunpath,strerror(errno)); exit(1); } v = 1; if (ioctl(tunfd,TUNSLMODE,&v) < 0) { fprintf(stderr,"%s: TUNSIFMODE %s: %s\n",__progname,tunpath,strerror(errno)); exit(1); } nonblocking_fd(tunfd); } static void tun_rd(void *arg __attribute__((__unused__))) { unsigned char pkt[2048]; int len; int n; struct sockaddr_storage ss; PQ *pq; while (1) { len = read(tunfd,&pkt[0],sizeof(pkt)); if (len < 0) { switch (errno) { case EINTR: case EWOULDBLOCK: return; } fprintf(stderr,"%s: %s read: %s\n",__progname,tunpath,strerror(errno)); exit(1); } if (len == 0) { fprintf(stderr,"%s: %s read: %s\n",__progname,tunpath,strerror(errno)); exit(1); } n = offsetof(struct sockaddr_storage,ss_len) + sizeof(ss.ss_len); bcopy(&pkt[0],&ss,n); if ((ss.ss_len < n) || (ss.ss_len > sizeof(ss))) { fprintf(stderr,"%s: %s packet has impossible dst len %d\n",__progname,tunpath,ss.ss_len); continue; } if (ss.ss_len > len) { fprintf(stderr,"%s: %s: sockaddr overruns packet (%d > %d)\n",__progname,tunpath,ss.ss_len,len); continue; } bcopy(&pkt[0],&ss,ss.ss_len); pq = malloc(sizeof(PQ)+len-ss.ss_len); switch (ss.ss_family) { default: fprintf(stderr,"%s: %s packet has unknown AF %d\n",__progname,tunpath,ss.ss_family); continue; break; case AF_INET: pq->dst.af = AF_INET; pq->dst.v4 = ((const struct sockaddr_in *)&ss)->sin_addr; break; case AF_INET6: pq->dst.af = AF_INET6; pq->dst.v6 = ((const struct sockaddr_in6 *)&ss)->sin6_addr; break; } pq->body = (void *)(pq+1); pq->bodylen = len - ss.ss_len; bcopy(&pkt[ss.ss_len],pq->body,pq->bodylen); pq->link = 0; *pktqt = pq; pktqt = &pq->link; want_send = 1; } } static void send_reach(PEER *p) { XXX;;; // generate a REACH packet } static void send_public(PEER *p) { XXX;;; // generate a PUBLIC packet } static void send_ipmap(PEER *p) { XXX;;; // generate an IPMAP packet } static void send_packet(PEER *p) { XXX;;; // generate an IP packet } static PEER *peer_for_addr(const IPADDR *dst) { XXX;;; // look up the PEER to send packets for dst to } static int send_output(void *arg __attribute__((__unused__))) { PEER *p; int i; if (! want_send) return(BLOCK_NIL); want_send = 0; for (i=npeers-1;i>=0;i--) { p = peers[i]; if (oq_qlen(&p->oq) > QLIMIT) continue; if (p->want & WANT_REACH) send_reach(p); if (p->want & WANT_PUBLIC) send_public(p); if (p->want & WANT_IPMAP) send_ipmap(p); while ((oq_qlen(&p->oq) < QLIMIT) && p->pq) send_packet(p); p->want = 0; } while (pktq) { PQ *q; q = pktq; pktq = q->link; p = peer_for_addr(&q->dst); if (p) { q->link = 0; *p->pqt = q; p->pqt = &q->link; } else { free(q); } } return(BLOCK_LOOP); } static void listen_accept(void *lv) { LISTEN *l; int new; struct sockaddr_storage ss; socklen_t sslen; PEER *p; CRYPTINIT *ci; l = lv; sslen = sizeof(ss); new = accept(l->fd,(void *)&ss,&sslen); if (new < 0) { fprintf(stderr,"%s: accept on %s: %s\n",__progname,l->txt,strerror(errno)); return; } nonblocking_fd(new); p = new_peer(); p->fd = new; p->id = PL_NOID; p->remsa = malloc(sslen); bcopy(&ss,p->remsa,sslen); p->remsalen = sslen; asprintf(&p->txt,"(accept from %s)",l->txt); ci = malloc(sizeof(CRYPTINIT)); ci->myrole = CR_PASSIVE; ci->p = p; peer_start_crypto(ci); } static void start_accepts(void) { LISTEN *l; for (l=listens;l;l=l->link) { l->id = add_poll_fd(l->fd,&rwtest_always,&rwtest_never,&listen_accept,0,l); } } static void start_uplink_expire(void) { XXX;;; // start periodic check to see if UPLINKs have expired } static void init_ipmap(void) { int i; ipmaps = 0; for (i=ncipaddrs-1;i>=0;i--) new_ipmap(cid,cipaddrs[i]); } static void startup(void) { struct itimerval itv; int i; 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 = 60; itv.it_interval.tv_usec = 0; write(pcfd,&itv,sizeof(itv)); nonblocking_fd(pcfd); pcid = add_poll_fd(pcfd,&rwtest_always,&rwtest_never,&pcfd_rd,0,0); peercleanup_id = add_block_fn(&peercleanup,0); peercleanup_needed = 0; open_random(); open_tun(); start_accepts(); pktq = 0; pktqt = &pktq; tunid = add_poll_fd(tunfd,&rwtest_always,&rwtest_never,&tun_rd,0,0); want_send = 0; sendid = add_block_fn(&send_output,0); for (i=256-1;i>=0;i--) { peertbl[i] = 0; reachtbl[i] = -1; } if (! ignore_public) start_uplink_expire(); init_ipmap(); } int main(int, char **); int main(int ac, char **av) { init(); handleargs(ac,av); set_defaults(); startup(); while (1) { pre_poll(); if (do_poll() < 0) { if (errno == EINTR) continue; fprintf(stderr,"%s: poll: %s",__progname,strerror(errno)); exit(1); } post_poll(); } return(0); }