/* This file is in the public domain. */ #include #include #include #include #include #include #include #include #include extern const char *__progname; #include "util.h" #include "netfd.h" #include "config.h" #include "structs.h" #include "pollloop.h" typedef struct listen_ip_priv LISTEN_IP_PRIV; typedef struct lipaddr LIPADDR; struct listen_ip_priv { LISTEN *l; char *text; LIPADDR *addrs; } ; struct lipaddr { LIPADDR *link; LISTEN_IP_PRIV *lip; struct sockaddr *sa; socklen_t salen; /* thank you linux...not! */ int af; int socktype; int proto; int fd; int ioid; char *text; LIPADDR *xfer; } ; extern const LEP_OPS lep_ops_ip; static int allow_ip(const struct sockaddr *sa) { switch (sa->sa_family) { case AF_INET: case AF_INET6: return(1); break; } return(0); } static int allow_ip4(const struct sockaddr *sa) { return(sa->sa_family==AF_INET); } static int allow_ip6(const struct sockaddr *sa) { return(sa->sa_family==AF_INET6); } static int lep_ip_add(CONFIG *cfg, LISTEN *l, const char *key, int keylen, const char *rest) { int (*test)(const struct sockaddr *); const char *adjective; const char *slash; char *addrstr; const char *portstr; struct addrinfo hints; struct addrinfo *ai0; struct addrinfo *ai; int e; LISTEN_IP_PRIV *lip; LIPADDR *la; LEP *lep; char ihost[NI_MAXHOST]; char iserv[NI_MAXSERV]; if ((keylen == 2) && !bcmp(key,"ip",2)) { test = &allow_ip; adjective = "IP"; } else if ((keylen == 3) && !bcmp(key,"ip4",3)) { test = &allow_ip4; adjective = "IPv4"; } else if ((keylen == 3) && !bcmp(key,"ip6",3)) { test = &allow_ip6; adjective = "IPv6"; } else { return(0); } slash = index(rest,'/'); if (slash) { addrstr = blk_to_nulterm(rest,slash-rest); portstr = slash + 1; } else { addrstr = 0; portstr = rest; } hints.ai_flags = AI_PASSIVE; hints.ai_family = PF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = 0; hints.ai_addrlen = 0; hints.ai_addr = 0; hints.ai_canonname = 0; hints.ai_next = 0; e = getaddrinfo(addrstr,portstr,&hints,&ai0); free(addrstr); switch (e) { case 0: if (ai0) break; e = EAI_NODATA; /* fall through */ default: config_err(cfg,"error looking up %s: %s",rest,gai_strerror(e)); break; } lip = malloc(sizeof(LISTEN_IP_PRIV)); lip->l = l; lip->text = strdup(rest); lip->addrs = 0; for (ai=ai0;ai;ai=ai->ai_next) { if (! (*test)(ai->ai_addr)) continue; if (getnameinfo(ai->ai_addr,ai->ai_addrlen,&ihost[0],sizeof(ihost),&iserv[0],sizeof(iserv),NI_NUMERICHOST|NI_NUMERICSERV)) { config_err(cfg,"can't get text form of AF %d address: %s",ai->ai_family,strerror(errno)); } la = malloc(sizeof(LIPADDR)); la->lip = lip; la->sa = malloc(ai->ai_addrlen); bcopy(ai->ai_addr,la->sa,ai->ai_addrlen); la->salen = ai->ai_addrlen; la->af = ai->ai_family; la->socktype = ai->ai_socktype; la->proto = ai->ai_protocol; la->fd = -1; la->ioid = PL_NOID; asprintf(&la->text,"%s/%s",&ihost[0],&iserv[0]); la->link = lip->addrs; lip->addrs = la; } freeaddrinfo(ai0); if (! lip->addrs) { free(lip->text); free(lip); config_err(cfg,"error looking up %s: no %s addresses",rest,adjective); } lep = malloc(sizeof(LEP)); lep->ops = &lep_ops_ip; lep->priv = lip; lep->link = l->endpoints; l->endpoints = lep; return(1); } static void lep_ip_free(void *pv) { LISTEN_IP_PRIV *lip; LIPADDR *a; lip = pv; while (lip->addrs) { a = lip->addrs; lip->addrs = a->link; free(a->sa); if (a->fd >= 0) close(a->fd); if (a->ioid != PL_NOID) remove_poll_id(a->ioid); free(a->text); free(a); } free(lip); } static void lep_ip_pre_transfer(void *pv) { LISTEN_IP_PRIV *p; LIPADDR *a; p = pv; for (a=p->addrs;a;a=a->link) a->xfer = 0; } static void lep_ip_setup_transfer(void *fv, void *tv) { LISTEN_IP_PRIV *flip; LIPADDR *fa; LISTEN_IP_PRIV *tlip; LIPADDR *ta; int match; flip = fv; tlip = tv; for <"f"> (fa=flip->addrs;fa;fa=fa->link) { if (fa->xfer) continue; for (ta=tlip->addrs;ta;ta=ta->link) { if (ta->xfer) continue; if ( (fa->af != ta->af) || (fa->socktype != ta->socktype) || (fa->proto != ta->proto) || (fa->sa->sa_family != ta->sa->sa_family) ) continue; switch (fa->sa->sa_family) { case AF_INET: #define fsin ((struct sockaddr_in *)fa->sa) #define tsin ((struct sockaddr_in *)ta->sa) match = (fsin->sin_addr.s_addr == tsin->sin_addr.s_addr) && (fsin->sin_port == tsin->sin_port); #undef fsin #undef tsin break; case AF_INET6: #define fsin6 ((struct sockaddr_in6 *)fa->sa) #define tsin6 ((struct sockaddr_in6 *)ta->sa) match = (fsin6->sin6_port == tsin6->sin6_port) && IN6_ARE_ADDR_EQUAL(&fsin6->sin6_addr,&tsin6->sin6_addr); #undef fsin6 #undef tsin6 break; default: abort(); break; } if (match) { fa->xfer = ta; ta->xfer = fa; continue <"f">; } } } } static void lep_ip_do_transfer(void *pv) { LIPADDR *a; for (a=((LISTEN_IP_PRIV *)pv)->addrs;a;a=a->link) { if (a->xfer) { if (a->xfer->xfer != a) abort(); a->fd = a->xfer->fd; a->xfer->fd = -1; a->xfer->xfer = 0; a->xfer = 0; } } } static void lep_ip_no_transfer(void *pv) { LIPADDR *a; for (a=((LISTEN_IP_PRIV *)pv)->addrs;a;a=a->link) a->xfer = 0; } static int lep_ip_openfds(void *pv) { LISTEN_IP_PRIV *p; LIPADDR *a; int err; int errs; p = pv; errs = 0; for <"addrs"> (a=p->addrs;a;a=a->link) { if (a->xfer) continue; if (a->fd >= 0) abort(); do <"err"> { a->fd = socket(a->af,a->socktype,a->proto); if (a->fd < 0) { fprintf(stderr,"%s: %s (%s): socket(%d,%d,%d): %s\n", __progname,p->text,a->text,a->af,a->socktype,a->proto,strerror(errno)); break <"err">; continue; } if (bind(a->fd,a->sa,a->salen) < 0) { fprintf(stderr,"%s: %s (%s): bind: %s\n",__progname,p->text,a->text,strerror(err)); break <"err">; } if (listen(a->fd,25) < 0) { fprintf(stderr,"%s: %s (%s): listen: %s\n",__progname,p->text,a->text,strerror(err)); break <"err">; } continue <"addrs">; } while (0); if (a->fd >= 0) { close(a->fd); a->fd = -1; } errs = 1; } return(errs); } static void accept_ip(void *av) { LIPADDR *a; struct sockaddr_storage ss; socklen_t sl; int new; char *txt; char ihost[NI_MAXHOST]; char iserv[NI_MAXSERV]; a = av; sl = sizeof(ss); new = accept(a->fd,(void *)&ss,&sl); if (new < 0) { switch (errno) { case EINTR: case NONBLOCKING: return; break; } fprintf(stderr,"%s: %s (%s): accept: %s\n",__progname,a->lip->text,a->text,strerror(errno)); return; } if (getnameinfo((void *)&ss,sl,&ihost[0],sizeof(ihost),&iserv[0],sizeof(iserv),NI_NUMERICHOST|NI_NUMERICSERV)) { fprintf(stderr,"%s: %s (%s): can't get text form of AF %d address: %s",__progname,a->lip->text,a->text,ss.ss_family,strerror(errno)); close(new); return; } asprintf(&txt,"%s (%s) <-> %s/%s",a->lip->text,a->text,&ihost[0],&iserv[0]); set_nonblock(new); netfd_new(new,a->lip->l,txt); } static void lep_ip_setup_poll(void *pv) { LISTEN_IP_PRIV *p; LIPADDR *a; p = pv; for (a=p->addrs;a;a=a->link) { set_nonblock(a->fd); a->ioid = add_poll_fd(a->fd,&rwtest_always,&rwtest_never,&accept_ip,0,a); } } const LEP_OPS lep_ops_ip = LEP_OPS_INIT(ip);