/* * Resolver auxiliary protocol: * * Request: * id, as a raw unsigned int * host length, as a raw unsigned int * port length, as a raw unsigned int * host string * port string * * Response: * id, as a raw unsigned int * err, the error return from getaddrinfo, as a raw int * If err is nonzero, it ends there. * If err is zero: * naddrs, the number of addresses, as a raw unsigned int * naddrs repetitions of * sizeof(int) bytes, ai_family * sizeof(int) bytes, ai_socktype * sizeof(int) bytes, ai_protocol * sizeof(size_t) bytes, ai_addrlen * ai_addrlen bytes, ai_addr * * When a value is documented as `raw $TYPE' this means it is * sizeof(TYPE) bytes giving a TYPE object as natively laid out in * memory. This is based on the assumption that both ends of the * protocol are running on the same machine. * * Note that responses may be generated out of order - this is why id * exists. But each individual response is atomic. */ #include #include #include #include #include #include #include #include #include #include extern const char *__progname; #include "es.h" #include "res.h" #include "common.h" typedef struct resreq RESREQ; typedef struct req REQ; struct req { int fd; unsigned int rid; int pid; } ; struct resreq { RESREQ *link; unsigned int id; void (*cb)(void *, int, struct addrinfo *); void *cbarg; } ; static int resfd; static AIO_OQ resoq; static int resid; static RESREQ *rqs; static unsigned int *rrids; static unsigned int arrids; static unsigned int nrrids; static ES ibuf; static int iphase; static int idone; static unsigned int naddrs; static struct addrinfo *ailist; static struct addrinfo **ait; static struct addrinfo *resai; static unsigned int id; static unsigned int hlen; static unsigned int plen; static void do_request(int fd, unsigned int id, const char *hstr, int hlen, const char *pstr, int plen) { char *host; char *port; struct addrinfo hints; struct addrinfo *ai0; struct addrinfo *ai; int e; host = malloc(hlen+1); bcopy(hstr,host,hlen); host[hlen] = '\0'; if (plen) { port = malloc(plen+1); bcopy(pstr,port,plen); port[plen] = '\0'; } else { port = 0; } hints.ai_flags = 0; hints.ai_family = PF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = 0; hints.ai_addrlen = 0; hints.ai_canonname = 0; hints.ai_addr = 0; hints.ai_next = 0; e = getaddrinfo(host,port,&hints,&ai0); if (e) { struct iovec iov[2]; iov[0].iov_base = &id; iov[0].iov_len = sizeof(unsigned int); iov[1].iov_base = &e; iov[1].iov_len = sizeof(e); writev(fd,&iov[0],2); } else { unsigned int n; struct iovec iov[5]; n = 0; for (ai=ai0;ai;ai=ai->ai_next) n ++; iov[0].iov_base = &id; iov[0].iov_len = sizeof(unsigned int); iov[1].iov_base = &e; iov[1].iov_len = sizeof(int); iov[2].iov_base = &n; iov[2].iov_len = sizeof(unsigned int); writev(fd,&iov[0],3); for (ai=ai0;ai;ai=ai->ai_next) { iov[0].iov_base = &ai->ai_family; iov[0].iov_len = sizeof(int); iov[1].iov_base = &ai->ai_socktype; iov[1].iov_len = sizeof(int); iov[2].iov_base = &ai->ai_protocol; iov[2].iov_len = sizeof(int); iov[3].iov_base = &ai->ai_addrlen; iov[3].iov_len = sizeof(size_t); iov[4].iov_base = ai->ai_addr; iov[4].iov_len = ai->ai_addrlen; writev(fd,&iov[0],5); } } } static void rd_req(void *rv) { REQ *r; unsigned char rbuf[512]; int nr; r = rv; while (1) { nr = read(r->fd,&rbuf[0],sizeof(rbuf)); if (nr < 0) { if (errno == EINTR) continue; fprintf(stderr,"%s: internal resolver response read: %s\n",__progname,strerror(errno)); exit(1); } if (nr == 0) break; aio_oq_queue_copy(&resoq,&rbuf[0],nr); } close(r->fd); aio_remove_poll(r->pid); free(r); } static void start_request(void) { const char *b; int s[2]; pid_t kid; REQ *r; b = es_buf(&ibuf) + (3 * sizeof(unsigned int)); if (socketpair(AF_LOCAL,SOCK_STREAM,0,&s[0]) < 0) { fprintf(stderr,"%s: AF_LOCAL/SOCK_STREAM socketpair: %s\n",__progname,strerror(errno)); exit(1); } fflush(0); kid = fork(); if (kid < 0) { fprintf(stderr,"%s: fork: %s\n",__progname,strerror(errno)); exit(1); } if (kid == 0) { close(s[1]); do_request(s[0],id,b,hlen,b+hlen,plen); _exit(0); } close(s[0]); r = malloc(sizeof(REQ)); r->fd = s[1]; r->rid = id; r->pid = aio_add_poll(r->fd,&aio_rwtest_always,&aio_rwtest_never,&rd_req,0,r); es_empty(&ibuf); iphase = 0; } static void input_res(const void *data, int len) { const char *dp; int n; int l; int o; const char *b; dp = data; o = 0; while (o < len) { switch (iphase) { case 0: // accumulating id/hlen/plen n = 3 * sizeof(unsigned int); l = n - es_len(&ibuf); if (l < 0) abort(); es_append_n(&ibuf,dp+o,l); o += l; if (es_len(&ibuf) >= n) { b = es_buf(&ibuf); bcopy(b,&id,sizeof(unsigned int)); bcopy(b+sizeof(unsigned int),&hlen,sizeof(unsigned int)); bcopy(b+(2*sizeof(unsigned int)),&plen,sizeof(unsigned int)); if (hlen || plen) { iphase = 1; idone = n; } else { start_request(); } } break; case 1: // accumulating strings n = idone + hlen + plen; l = n - es_len(&ibuf); if (l < 0) abort(); if (l > len-o) l = len - o; es_append_n(&ibuf,dp+o,l); o += l; if (es_len(&ibuf) >= n) { start_request(); } break; } } } static void parent_reset(void) { iphase = 0; es_empty(&ibuf); } static void req_done(unsigned int id, int err, struct addrinfo *ai) { RESREQ *rq; RESREQ **rqp; rqp = &rqs; while (1) { rq = *rqp; if (! rq) abort(); if (rq->id == id) { *rqp = rq->link; break; } rqp = &rq->link; } (*rq->cb)(rq->cbarg,err,ai); rrids[nrrids++] = rq->id; free(rq); } static void input_parent(const void *data, int len) { const char *dp; int n; int l; int o; int e; const char *b; dp = data; o = 0; while (o < len) { switch (iphase) { case 0: // getting id/err n = sizeof(unsigned int) + sizeof(int); l = n - es_len(&ibuf); if (l < 0) abort(); es_append_n(&ibuf,dp+o,l); o += l; if (es_len(&ibuf) >= n) { b = es_buf(&ibuf); bcopy(b,&id,sizeof(unsigned int)); bcopy(b+sizeof(unsigned int),&e,sizeof(int)); if (e) { req_done(id,e,0); parent_reset(); } else { iphase = 1; es_empty(&ibuf); } } break; case 1: // getting naddrs n = sizeof(int); l = n - es_len(&ibuf); if (l < 0) abort(); es_append_n(&ibuf,dp+o,l); o += l; if (es_len(&ibuf) >= n) { b = es_buf(&ibuf); ait = &ailist; bcopy(b,&naddrs,sizeof(unsigned int)); if (naddrs < 1) { *ait = 0; req_done(id,0,ailist); parent_reset(); } else { iphase = 2; es_empty(&ibuf); } } break; case 2: // getting family/socktype/protocol/addrlen n = (3 * sizeof(int)) + sizeof(size_t); l = n - es_len(&ibuf); if (l < 0) abort(); es_append_n(&ibuf,dp+o,l); o += l; if (es_len(&ibuf) >= n) { b = es_buf(&ibuf); resai = malloc(sizeof(*resai)); resai->ai_flags = 0; bcopy(b+idone,&resai->ai_family,sizeof(int)); bcopy(b+idone+sizeof(int),&resai->ai_socktype,sizeof(int)); bcopy(b+idone+(2*sizeof(int)),&resai->ai_protocol,sizeof(int)); bcopy(b+idone+(3*sizeof(int)),&resai->ai_addrlen,sizeof(size_t)); resai->ai_canonname = 0; resai->ai_addr = malloc(resai->ai_addrlen); resai->ai_next = 0; iphase = 3; es_empty(&ibuf); } break; case 3: n = resai->ai_addrlen; l = n - es_len(&ibuf); if (l < 0) abort(); es_append_n(&ibuf,dp+o,l); o += l; if (es_len(&ibuf) >= n) { b = es_buf(&ibuf); bcopy(b,resai->ai_addr,resai->ai_addrlen); *ait = resai; ait = &resai->ai_next; naddrs --; if (naddrs < 1) { *ait = 0; req_done(id,0,ailist); parent_reset(); } else { iphase = 2; es_empty(&ibuf); } } break; } } } static int wtest_res(void *arg __attribute__((__unused__))) { return(aio_oq_nonempty(&resoq)); } static void wr_res(void *arg __attribute__((__unused__))) { wr_common(&resoq,resfd,"resolver"); } static void rd_common(void (*input)(const void *, int), void (*eof)(void)) { char rb[512]; int nr; nr = read(resfd,&rb[0],sizeof(rb)); if (nr < 0) { switch (errno) { case EINTR: case EWOULDBLOCK: return; break; } fprintf(stderr,"%s: resolver read: %s\n",__progname,strerror(errno)); exit(1); } if (nr == 0) { (*eof)(); return; } (*input)(&rb[0],nr); } static void closed_res(void) { exit(0); } static void closed_parent(void) { fprintf(stderr,"\r\n%s: resolver auxiliary died unexpectedly\r\n",__progname); exit(1); } static void rd_res(void *arg __attribute__((__unused__))) { rd_common(&input_res,&closed_res); } static void rd_parent(void *arg __attribute__((__unused__))) { rd_common(&input_parent,&closed_parent); } static void resolver_main(void) { struct sigaction sa; setproctitle("resolver"); sa.sa_handler = SIG_IGN; sigemptyset(&sa.sa_mask); sa.sa_flags = SA_NOCLDWAIT; sigaction(SIGCHLD,&sa,0); aio_oq_init(&resoq); resid = aio_add_poll(resfd,&aio_rwtest_always,&wtest_res,&rd_res,&wr_res,0); es_init(&ibuf); iphase = 0; aio_event_loop(); } void resolver_init(void) { int s[2]; pid_t kid; rqs = 0; if (socketpair(AF_LOCAL,SOCK_STREAM,0,&s[0])< 0) { fprintf(stderr,"%s: AF_LOCAL/SOCK_STREAM socketpair: %s\n",__progname,strerror(errno)); exit(1); } fflush(0); kid = fork(); if (kid < 0) { fprintf(stderr,"%s: fork: %s\n",__progname,strerror(errno)); exit(1); } if (kid == 0) { close(s[1]); resfd = s[0]; resolver_main(); _exit(0); } close(s[0]); resfd = s[1]; aio_oq_init(&resoq); resid = aio_add_poll(resfd,&aio_rwtest_always,&wtest_res,&rd_parent,&wr_res,0); rrids = 0; arrids = 0; nrrids = 0; es_init(&ibuf); iphase = 0; } void resolver_request(const char *host, const char *port, void (*cb)(void *, int, struct addrinfo *), void *cbarg) { RESREQ *rq; unsigned int id; unsigned int hl; unsigned int pl; if (nrrids < 1) { id = arrids; rrids = realloc(rrids,arrids++*sizeof(rrids)); } else { id = rrids[--nrrids]; } hl = strlen(host); pl = port ? strlen(port) : 0; aio_oq_queue_copy(&resoq,&id,sizeof(unsigned int)); aio_oq_queue_copy(&resoq,&hl,sizeof(unsigned int)); aio_oq_queue_copy(&resoq,&pl,sizeof(unsigned int)); if (hl > 0) aio_oq_queue_copy(&resoq,host,hl); if (pl > 0) aio_oq_queue_copy(&resoq,port,pl); rq = malloc(sizeof(RESREQ)); rq->id = id; rq->cb = cb; rq->cbarg = cbarg; rq->link = rqs; rqs = rq; } void resolver_free_ailist(struct addrinfo *ai0) { struct addrinfo *ai; while ((ai = ai0)) { ai0 = ai->ai_next; free(ai->ai_addr); free(ai); } }