From 02a705adbd87e20f15a1a2e43cf9f9ed53a19f72 Mon Sep 17 00:00:00 2001 From: Fredrik Tolf Date: Sun, 16 Mar 2008 02:46:18 +0100 Subject: [PATCH] Initial work on the new implementation --- daemon/net.c | 1041 +++++++++++++++++++++++++++++++++------------------------- daemon/net.h | 10 +- 2 files changed, 592 insertions(+), 459 deletions(-) diff --git a/daemon/net.c b/daemon/net.c index 44974f1..3265417 100644 --- a/daemon/net.c +++ b/daemon/net.c @@ -87,7 +87,41 @@ static struct configvar myvars[] = {CONF_VAR_END} }; -static struct socket *sockets = NULL; +#define UFD_SOCK 0 +#define UFD_PIPE 1 +#define UFD_LISTEN 2 + +struct scons { + struct scons *n, *p; + struct socket *s; +}; + +struct ufd { + struct ufd *next, *prev; + int fd; + int type; + int ignread; + struct socket *sk; + union { + struct { + int family; + int type; + struct sockaddr *remote; + socklen_t remotelen; + struct { + uid_t uid; + gid_t gid; + } ucred; + } s; + struct { + struct lport *lp; + int family; + } l; + } d; +}; + +static struct ufd *ufds = NULL; +static struct scons *rbatch, *wbatch, *cbatch; int numsocks = 0; /* XXX: Get autoconf for all this... */ @@ -167,92 +201,141 @@ int getpublicaddr(int af, struct sockaddr **addr, socklen_t *lenbuf) return(1); } -static struct socket *newsock(int type) +static struct socket *newsock1(int dgram) { struct socket *new; - new = smalloc(sizeof(*new)); - new->refcount = 2; - new->fd = -1; - new->isrealsocket = 1; - new->family = -1; - new->tos = 0; - new->type = type; + new = memset(smalloc(sizeof(*new)), 0, sizeof(*new)); + new->refcount = 1; new->state = -1; - new->ignread = 0; - new->close = 0; - new->remote = NULL; - new->remotelen = 0; - new->ucred.uid = -1; - new->ucred.gid = -1; - switch(type) - { - case SOCK_STREAM: - new->outbuf.s.buf = NULL; - new->outbuf.s.bufsize = 0; - new->outbuf.s.datasize = 0; - new->inbuf.s.buf = NULL; - new->inbuf.s.bufsize = 0; - new->inbuf.s.datasize = 0; - break; - case SOCK_DGRAM: - new->outbuf.d.f = new->outbuf.d.l = NULL; - new->inbuf.d.f = new->inbuf.d.l = NULL; - break; - } - new->conncb = NULL; - new->errcb = NULL; - new->readcb = NULL; - new->writecb = NULL; - new->acceptcb = NULL; - new->next = sockets; - new->prev = NULL; - if(sockets != NULL) - sockets->prev = new; - sockets = new; + new->dgram = dgram; numsocks++; return(new); } +static struct socket *sockpair(int dgram) +{ + struct socket *s1, *s2; + + s1 = newsock1(dgram); + s2 = newsock1(dgram); + s1->back = s2; + s2->back = s1; + putsock(s2); + return(s1); +} + +static void sksetstate(struct socket *sk, int state) +{ + sk->state = state; + sk->back->state = state; +} + +static void closeufd(struct ufd *ufd) +{ + if(ufd->fd != -1) + close(ufd->fd); + ufd->fd = -1; +} + +static void freeufd(struct ufd *ufd) +{ + if(ufd->next != NULL) + ufd->next->prev = ufd->prev; + if(ufd->prev != NULL) + ufd->prev->next = ufd->next; + if(ufd == ufds) + ufds = ufd->next; + closeufd(ufd); + putsock(ufd->sk); + if(ufd->type == UFD_SOCK) { + if(ufd->d.s.remote != NULL) + free(ufd->d.s.remote); + } + free(ufd); +} + +static struct ufd *mkufd(int fd, int type, struct socket *sk) +{ + struct ufd *ufd; + + ufd = memset(smalloc(sizeof(*ufd)), 0, sizeof(*ufd)); + ufd->fd = fd; + if(sk != NULL) { + getsock(ufd->sk = sk); + sk->ufd = ufd; + } + if(type == UFD_SOCK) { + ufd->d.s.ucred.uid = -1; + ufd->d.s.ucred.gid = -1; + } + ufd->next = ufds; + if(ufds) + ufds->prev = ufd; + ufds = ufd; + return(ufd); +} + +static struct ufd *dupufd(struct ufd *ufd) +{ + struct ufd *nufd; + struct socket *nsk; + + if(ufd->sk != NULL) + nsk = sockpair(ufd->sk->dgram); + else + nsk = NULL; + nufd = mkufd(ufd->fd, ufd->type, nsk); + if(nsk != NULL) + putsock(nsk); + if((nufd->fd = dup(ufd->fd)) < 0) + { + flog(LOG_WARNING, "could not dup() fd: %s", strerror(errno)); + freeufd(nufd); + return(NULL); + } + if(ufd->type == UFD_SOCK) { + nufd->d.s.family = ufd->d.s.family; + nufd->d.s.type = ufd->d.s.type; + nufd->d.s.ucred.uid = ufd->d.s.ucred.uid; + nufd->d.s.ucred.gid = ufd->d.s.ucred.gid; + if(ufd->d.s.remote != NULL) + nufd->d.s.remote = memcpy(smalloc(ufd->d.s.remotelen), ufd->d.s.remote, nufd->d.s.remotelen = ufd->d.s.remotelen); + } else if(ufd->type == UFD_LISTEN) { + nufd->d.l.family = ufd->d.l.family; + } + return(nufd); +} + static struct socket *mksock(int domain, int type) { int fd; - struct socket *new; + struct socket *sk; + struct ufd *ufd; if((fd = socket(domain, type, 0)) < 0) { flog(LOG_CRIT, "could not create socket: %s", strerror(errno)); return(NULL); } - new = newsock(type); - new->fd = fd; - new->family = domain; + sk = sockpair(type == SOCK_DGRAM); + ufd = mkufd(fd, UFD_SOCK, sk); + ufd->d.s.family = domain; + ufd->d.s.type = type; fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK); - return(new); + return(sk); } struct socket *wrapsock(int fd) { - struct socket *new; + struct socket *sk; + struct ufd *ufd; - new = newsock(SOCK_STREAM); - new->fd = fd; - new->state = SOCK_EST; - new->isrealsocket = 0; + sk = sockpair(0); + ufd = mkufd(fd, UFD_PIPE, sk->back); + sksetstate(sk, SOCK_EST); fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK); - return(new); -} - -static void unlinksock(struct socket *sk) -{ - if(sk->prev != NULL) - sk->prev->next = sk->next; - if(sk->next != NULL) - sk->next->prev = sk->prev; - if(sk == sockets) - sockets = sk->next; - putsock(sk); - numsocks--; + return(sk); } void getsock(struct socket *sk) @@ -260,59 +343,94 @@ void getsock(struct socket *sk) sk->refcount++; } -void putsock(struct socket *sk) +static void freesock(struct socket *sk) { struct dgrambuf *buf; - if(--(sk->refcount) == 0) - { - switch(sk->type) - { - case SOCK_STREAM: - if(sk->outbuf.s.buf != NULL) - free(sk->outbuf.s.buf); - if(sk->inbuf.s.buf != NULL) - free(sk->inbuf.s.buf); - break; - case SOCK_DGRAM: - while((buf = sk->outbuf.d.f) != NULL) - { - sk->outbuf.d.f = buf->next; - free(buf->data); - free(buf->addr); - free(buf); - } - while((buf = sk->inbuf.d.f) != NULL) - { - sk->inbuf.d.f = buf->next; - free(buf->data); - free(buf->addr); - free(buf); - } - break; + if(sk->dgram) { + while((buf = sk->buf.d.f) != NULL) { + sk->buf.d.f = buf->next; + freedgbuf(buf); } - closesock(sk); - if(sk->remote != NULL) - free(sk->remote); - free(sk); + } else { + if(sk->buf.s.buf != NULL) + free(sk->buf.s.buf); + } + free(sk); + numsocks--; +} + +void putsock(struct socket *sk) +{ + if(--(sk->refcount) < 0) { + flog(LOG_CRIT, "BUG: socket refcount < 0"); + abort(); + } + if((sk->refcount == 0) && (sk->back->refcount == 0)) { + freesock(sk); + freesock(sk->back); + } +} + +static void linksock(struct scons **list, struct socket *sk) +{ + struct scons *sc; + + for(sc = *list; sc != NULL; sc = sc->n) { + if(sc->s == sk) + return; } + sc = smalloc(sizeof(*sc)); + getsock(sc->s = sk); + sc->n = *list; + sc->p = NULL; + if(*list) + (*list)->p = sc; + *list = sc; } void sockpushdata(struct socket *sk, void *buf, size_t size) { - switch(sk->type) - { - case SOCK_STREAM: - sizebuf(&sk->inbuf.s.buf, &sk->inbuf.s.bufsize, sk->inbuf.s.datasize + size, 1, 1); - memmove(sk->inbuf.s.buf + size, sk->inbuf.s.buf, sk->inbuf.s.datasize); - memcpy(sk->inbuf.s.buf, buf, size); - sk->inbuf.s.datasize += size; - break; - case SOCK_DGRAM: + if(size == 0) + return; + if(sk->dgram) { /* XXX */ - break; + } else { + sizebuf(&sk->buf.s.buf, &sk->buf.s.bufsize, sk->buf.s.datasize + size, 1, 1); + memmove(sk->buf.s.buf + size, sk->buf.s.buf, sk->buf.s.datasize); + memcpy(sk->buf.s.buf, buf, size); + sk->buf.s.datasize += size; + linksock(&rbatch, sk); } - return; +} + +/* Read as the preterite of `read' */ +void sockread(struct socket *sk) +{ + linksock(&wbatch, sk->back); +} + +void freedgbuf(struct dgrambuf *dg) +{ + if(dg->data != NULL) + free(dg->data); + if(dg->addr != NULL) + free(dg->addr); + free(dg); +} + +struct dgrambuf *sockgetdgbuf(struct socket *sk) +{ + struct dgrambuf *dbuf; + + if((dbuf = sk->buf.d.f) == NULL) + return(NULL); + sk->buf.d.f = dbuf->next; + if(dbuf->next == NULL) + sk->buf.d.l = NULL; + dbuf->next = NULL; + sockread(sk); + return(dbuf); } void *sockgetinbuf(struct socket *sk, size_t *size) @@ -320,35 +438,98 @@ void *sockgetinbuf(struct socket *sk, size_t *size) void *buf; struct dgrambuf *dbuf; - switch(sk->type) - { - case SOCK_STREAM: - if((sk->inbuf.s.buf == NULL) || (sk->inbuf.s.datasize == 0)) - { - *size = 0; - return(NULL); - } - buf = sk->inbuf.s.buf; - *size = sk->inbuf.s.datasize; - sk->inbuf.s.buf = NULL; - sk->inbuf.s.bufsize = sk->inbuf.s.datasize = 0; - return(buf); - case SOCK_DGRAM: - if((dbuf = sk->inbuf.d.f) == NULL) - return(NULL); - sk->inbuf.d.f = dbuf->next; - if(dbuf->next == NULL) - sk->inbuf.d.l = NULL; + if(sk->dgram) { + dbuf = sockgetdgbuf(sk); buf = dbuf->data; *size = dbuf->size; free(dbuf->addr); free(dbuf); - return(buf); + } else { + if((sk->buf.s.buf == NULL) || (sk->buf.s.datasize == 0)) + { + *size = 0; + return(NULL); + } + buf = sk->buf.s.buf; + *size = sk->buf.s.datasize; + sk->buf.s.buf = NULL; + sk->buf.s.bufsize = sk->buf.s.datasize = 0; + sockread(sk); } - return(NULL); + return(buf); +} + +void sockqueue(struct socket *sk, void *data, size_t size) +{ + struct dgrambuf *new; + struct sockaddr *remote; + socklen_t remotelen; + + if(size == 0) + return; + if(sk->state == SOCK_STL) + return; + if(sk->dgram) { + if(sockpeeraddr(sk, &remote, &remotelen)) + return; + new = smalloc(sizeof(*new)); + new->next = NULL; + memcpy(new->data = smalloc(size), data, new->size = size); + new->addr = remote; + new->addrlen = remotelen; + if(sk->back->buf.d.l == NULL) + { + sk->back->buf.d.l = sk->back->buf.d.f = new; + } else { + sk->back->buf.d.l->next = new; + sk->back->buf.d.l = new; + } + } else { + sizebuf(&(sk->back->buf.s.buf), &(sk->back->buf.s.bufsize), sk->back->buf.s.datasize + size, 1, 1); + memcpy(sk->back->buf.s.buf + sk->back->buf.s.datasize, data, size); + sk->back->buf.s.datasize += size; + } + linksock(&rbatch, sk->back); +} + +void sockqueuedg(struct socket *sk, struct dgrambuf *dg) +{ + if(sk->state == SOCK_STL) { + freedgbuf(dg); + return; + } + if(!sk->dgram) { + flog(LOG_ERR, "BUG: sockqueuedg called on non-dgram socket"); + freedgbuf(dg); + return; + } + dg->next = NULL; + if(sk->back->buf.d.l == NULL) + { + sk->back->buf.d.l = sk->back->buf.d.f = dg; + } else { + sk->back->buf.d.l->next = dg; + sk->back->buf.d.l = dg; + } + linksock(&rbatch, sk->back); +} + +void sockeos(struct socket *sk) +{ + sksetstate(sk, SOCK_STL); + if(sk->back->eos == 0) + sk->back->eos = 1; + linksock(&rbatch, sk->back); +} + +static void sockerror(struct socket *sk, int en) +{ + sksetstate(sk, SOCK_STL); + if(sk->back->errcb != NULL) + sk->back->errcb(sk->back, en, sk->back->data); } -static void recvcmsg(struct socket *sk, struct msghdr *msg) +static void recvcmsg(struct ufd *ufd, struct msghdr *msg) { struct cmsghdr *cmsg; @@ -358,95 +539,64 @@ static void recvcmsg(struct socket *sk, struct msghdr *msg) if((cmsg->cmsg_level == SOL_SOCKET) && (cmsg->cmsg_type == SCM_CREDENTIALS)) { struct ucred *cred; - if(sk->ucred.uid == -1) + if(ufd->d.s.ucred.uid == -1) { cred = (struct ucred *)CMSG_DATA(cmsg); - sk->ucred.uid = cred->uid; - sk->ucred.gid = cred->gid; + ufd->d.s.ucred.uid = cred->uid; + ufd->d.s.ucred.gid = cred->gid; } } #endif } } -static void sockrecv(struct socket *sk) +static int ufddgram(struct ufd *ufd) +{ + int dgram; + + if(ufd->type == UFD_SOCK) { + dgram = ufd->d.s.type == SOCK_DGRAM; + } else if(ufd->type == UFD_PIPE) { + dgram = 0; + } else { + flog(LOG_ERR, "BUG: calling ufddgram on ufd of bad type %i", ufd->type); + return(-1); + } + if(ufd->sk == NULL) { + flog(LOG_ERR, "BUG: calling ufddgram on socketless ufd (type %i)", ufd->type); + return(-1); + } + if(dgram != ufd->sk->dgram) { + flog(LOG_ERR, "BUG: ufd/socket dgram value mismatch"); + return(-1); + } + return(dgram); +} + +static void sockrecv(struct ufd *ufd) { int ret, inq; + int dgram; struct dgrambuf *dbuf; struct msghdr msg; char cbuf[65536]; struct iovec bufvec; + void *buf; memset(&msg, 0, sizeof(msg)); msg.msg_iov = &bufvec; msg.msg_iovlen = 1; msg.msg_control = cbuf; msg.msg_controllen = sizeof(cbuf); - switch(sk->type) - { - case SOCK_STREAM: -#if defined(HAVE_LINUX_SOCKIOS_H) && defined(SIOCINQ) - /* SIOCINQ is Linux-specific AFAIK, but I really have no idea - * how to read the inqueue size on other OSs */ - if(sk->isrealsocket) { - if(ioctl(sk->fd, SIOCINQ, &inq)) - { - /* I don't really know what could go wrong here, so let's - * assume it's transient. */ - flog(LOG_WARNING, "SIOCINQ return %s on socket %i, falling back to 2048 bytes", strerror(errno), sk->fd); - inq = 2048; - } - } else { - /* There are perils when trying to use SIOCINQ on files >2GiB... */ - inq = 65536; - } -#else - inq = 2048; -#endif - if(inq > 65536) - inq = 65536; - sizebuf(&sk->inbuf.s.buf, &sk->inbuf.s.bufsize, sk->inbuf.s.datasize + inq, 1, 1); - if(sk->isrealsocket) - { - bufvec.iov_base = sk->inbuf.s.buf + sk->inbuf.s.datasize; - bufvec.iov_len = inq; - ret = recvmsg(sk->fd, &msg, 0); - } else { - ret = read(sk->fd, sk->inbuf.s.buf + sk->inbuf.s.datasize, inq); - msg.msg_controllen = 0; - msg.msg_flags = 0; - } - if(ret < 0) - { - if((errno == EINTR) || (errno == EAGAIN)) - return; - if(sk->errcb != NULL) - sk->errcb(sk, errno, sk->data); - closesock(sk); - return; - } - if(msg.msg_flags & MSG_CTRUNC) - flog(LOG_DEBUG, "ancillary data was truncated"); - else - recvcmsg(sk, &msg); - if(ret == 0) - { - if(sk->errcb != NULL) - sk->errcb(sk, 0, sk->data); - closesock(sk); - return; - } - sk->inbuf.s.datasize += ret; - if(sk->readcb != NULL) - sk->readcb(sk, sk->data); - break; - case SOCK_DGRAM: + if((dgram = ufddgram(ufd)) < 0) + return; + if(dgram) { #if defined(HAVE_LINUX_SOCKIOS_H) && defined(SIOCINQ) - if(ioctl(sk->fd, SIOCINQ, &inq)) + if(ioctl(ufd->fd, SIOCINQ, &inq)) { /* I don't really know what could go wrong here, so let's * assume it's transient. */ - flog(LOG_WARNING, "SIOCINQ return %s on socket %i", strerror(errno), sk->fd); + flog(LOG_WARNING, "SIOCINQ return %s on socket %i", strerror(errno), ufd->fd); return; } #else @@ -455,103 +605,135 @@ static void sockrecv(struct socket *sk) dbuf = smalloc(sizeof(*dbuf)); dbuf->data = smalloc(inq); dbuf->addr = smalloc(dbuf->addrlen = sizeof(struct sockaddr_storage)); - /* - ret = recvfrom(sk->fd, dbuf->data, inq, 0, dbuf->addr, &dbuf->addrlen); - */ msg.msg_name = dbuf->addr; msg.msg_namelen = dbuf->addrlen; bufvec.iov_base = dbuf->data; bufvec.iov_len = inq; - ret = recvmsg(sk->fd, &msg, 0); + ret = recvmsg(ufd->fd, &msg, 0); dbuf->addrlen = msg.msg_namelen; if(ret < 0) { - free(dbuf->addr); - free(dbuf->data); - free(dbuf); + freedgbuf(dbuf); if((errno == EINTR) || (errno == EAGAIN)) return; - if(sk->errcb != NULL) - sk->errcb(sk, errno, sk->data); - closesock(sk); + closeufd(ufd); + sockerror(ufd->sk, errno); return; } if(msg.msg_flags & MSG_CTRUNC) flog(LOG_DEBUG, "ancillary data was truncated"); else - recvcmsg(sk, &msg); + recvcmsg(ufd, &msg); /* On UDP/IPv[46], ret == 0 doesn't mean EOF (since UDP can't * have EOF), but rather an empty packet. I don't know if any * other potential DGRAM protocols might have an EOF * condition, so let's play safe. */ if(ret == 0) { - free(dbuf->addr); - free(dbuf->data); - free(dbuf); - if(!((sk->family == AF_INET) || (sk->family == AF_INET6))) + freedgbuf(dbuf); + if((ufd->type != UFD_SOCK) || !((ufd->d.s.family == AF_INET) || (ufd->d.s.family == AF_INET6))) { - if(sk->errcb != NULL) - sk->errcb(sk, 0, sk->data); - closesock(sk); + sockeos(ufd->sk); + closeufd(ufd); } return; } dbuf->addr = srealloc(dbuf->addr, dbuf->addrlen); dbuf->data = srealloc(dbuf->data, dbuf->size = ret); dbuf->next = NULL; - if(sk->inbuf.d.l != NULL) - sk->inbuf.d.l->next = dbuf; + sockqueuedg(ufd->sk, dbuf); + } else { +#if defined(HAVE_LINUX_SOCKIOS_H) && defined(SIOCINQ) + /* SIOCINQ is Linux-specific AFAIK, but I really have no idea + * how to read the inqueue size on other OSs */ + if(ufd->type == UFD_SOCK) { + if(ioctl(ufd->fd, SIOCINQ, &inq)) + { + /* I don't really know what could go wrong here, so let's + * assume it's transient. */ + flog(LOG_WARNING, "SIOCINQ return %s on socket %i, falling back to 2048 bytes", strerror(errno), ufd->fd); + inq = 2048; + } + } else { + /* There are perils when trying to use SIOCINQ on files >2GiB... */ + inq = 65536; + } +#else + inq = 2048; +#endif + if(inq > 65536) + inq = 65536; + /* This part could be optimized by telling the kernel to read + * directly into ufd->sk->back->buf, but that would be uglier + * by not using the socket function interface. */ + buf = smalloc(inq); + if(ufd->type == UFD_SOCK) + { + bufvec.iov_base = buf; + bufvec.iov_len = inq; + ret = recvmsg(ufd->fd, &msg, 0); + } else { + ret = read(ufd->fd, buf, inq); + msg.msg_controllen = 0; + msg.msg_flags = 0; + } + if(ret < 0) + { + free(buf); + if((errno == EINTR) || (errno == EAGAIN)) + return; + closeufd(ufd); + sockerror(ufd->sk, errno); + return; + } + if(msg.msg_flags & MSG_CTRUNC) + flog(LOG_DEBUG, "ancillary data was truncated"); else - sk->inbuf.d.f = dbuf; - sk->inbuf.d.l = dbuf; - if(sk->readcb != NULL) - sk->readcb(sk, sk->data); - break; + recvcmsg(ufd, &msg); + if(ret == 0) + { + free(buf); + closeufd(ufd); + sockeos(ufd->sk); + return; + } + sockqueue(ufd->sk, buf, ret); + free(buf); } } -static void sockflush(struct socket *sk) +static void sockflush(struct ufd *ufd) { int ret; struct dgrambuf *dbuf; + int dgram; - switch(sk->type) - { - case SOCK_STREAM: - if(sk->isrealsocket) - ret = send(sk->fd, sk->outbuf.s.buf, sk->outbuf.s.datasize, MSG_DONTWAIT | MSG_NOSIGNAL); + if((dgram = ufddgram(ufd)) < 0) + return; + if(dgram) { + dbuf = sockgetdgbuf(ufd->sk); + sendto(ufd->fd, dbuf->data, dbuf->size, MSG_DONTWAIT | MSG_NOSIGNAL, dbuf->addr, dbuf->addrlen); + freedgbuf(dbuf); + } else { + if(ufd->type == UFD_SOCK) + ret = send(ufd->fd, ufd->sk->buf.s.buf, ufd->sk->buf.s.datasize, MSG_DONTWAIT | MSG_NOSIGNAL); else - ret = write(sk->fd, sk->outbuf.s.buf, sk->outbuf.s.datasize); - if(ret < 0) - { + ret = write(ufd->fd, ufd->sk->buf.s.buf, ufd->sk->buf.s.datasize); + if(ret < 0) { /* For now, assume transient error, since * the socket is polled for errors */ - break; + return; } - if(ret > 0) - { - memmove(sk->outbuf.s.buf, ((char *)sk->outbuf.s.buf) + ret, sk->outbuf.s.datasize -= ret); - if(sk->writecb != NULL) - sk->writecb(sk, sk->data); + if(ret > 0) { + memmove(ufd->sk->buf.s.buf, ((char *)ufd->sk->buf.s.buf) + ret, ufd->sk->buf.s.datasize -= ret); + sockread(ufd->sk); } - break; - case SOCK_DGRAM: - dbuf = sk->outbuf.d.f; - if((sk->outbuf.d.f = dbuf->next) == NULL) - sk->outbuf.d.l = NULL; - sendto(sk->fd, dbuf->data, dbuf->size, MSG_DONTWAIT | MSG_NOSIGNAL, dbuf->addr, dbuf->addrlen); - free(dbuf->data); - free(dbuf->addr); - free(dbuf); - if(sk->writecb != NULL) - sk->writecb(sk, sk->data); - break; } } void closesock(struct socket *sk) { +/* struct sockaddr_un *un; if((sk->family == AF_UNIX) && !sockgetlocalname(sk, (struct sockaddr **)(void *)&un, NULL) && (un->sun_family == PF_UNIX)) @@ -562,41 +744,9 @@ void closesock(struct socket *sk) flog(LOG_WARNING, "could not unlink Unix socket %s: %s", un->sun_path, strerror(errno)); } } - sk->state = SOCK_STL; - close(sk->fd); - sk->fd = -1; - sk->close = 0; -} - -void sockqueue(struct socket *sk, void *data, size_t size) -{ - struct dgrambuf *new; - - if(sk->state == SOCK_STL) - return; - switch(sk->type) - { - case SOCK_STREAM: - sizebuf(&(sk->outbuf.s.buf), &(sk->outbuf.s.bufsize), sk->outbuf.s.datasize + size, 1, 1); - memcpy(sk->outbuf.s.buf + sk->outbuf.s.datasize, data, size); - sk->outbuf.s.datasize += size; - break; - case SOCK_DGRAM: - if(sk->remote == NULL) - return; - new = smalloc(sizeof(*new)); - new->next = NULL; - memcpy(new->data = smalloc(size), data, new->size = size); - memcpy(new->addr = smalloc(sk->remotelen), sk->remote, new->addrlen = sk->remotelen); - if(sk->outbuf.d.l == NULL) - { - sk->outbuf.d.l = sk->outbuf.d.f = new; - } else { - sk->outbuf.d.l->next = new; - sk->outbuf.d.l = new; - } - break; - } +*/ + sksetstate(sk, SOCK_STL); + sockeos(sk); } size_t sockgetdatalen(struct socket *sk) @@ -604,49 +754,31 @@ size_t sockgetdatalen(struct socket *sk) struct dgrambuf *b; size_t ret; - switch(sk->type) - { - case SOCK_STREAM: - ret = sk->inbuf.s.datasize; - break; - case SOCK_DGRAM: + if(sk->dgram) { ret = 0; - for(b = sk->inbuf.d.f; b != NULL; b = b->next) + for(b = sk->buf.d.f; b != NULL; b = b->next) ret += b->size; - break; + } else { + ret = sk->buf.s.datasize; } return(ret); } size_t sockqueuesize(struct socket *sk) { - struct dgrambuf *b; - size_t ret; - - switch(sk->type) - { - case SOCK_STREAM: - ret = sk->outbuf.s.datasize; - break; - case SOCK_DGRAM: - ret = 0; - for(b = sk->outbuf.d.f; b != NULL; b = b->next) - ret += b->size; - break; - } - return(ret); + return(sockgetdatalen(sk->back)); } /* * Seriously, I don't know if it's naughty or not to remove * pre-existing Unix sockets. */ -static int rebindunix(struct socket *sk, struct sockaddr *name, socklen_t namelen) +static int rebindunix(struct ufd *ufd, struct sockaddr *name, socklen_t namelen) { struct sockaddr_un *un; struct stat sb; - if((sk->family != AF_UNIX) || (name->sa_family != PF_UNIX)) + if((ufd->d.l.family != AF_UNIX) || (name->sa_family != PF_UNIX)) return(-1); un = (struct sockaddr_un *)name; if(stat(un->sun_path, &sb)) @@ -655,7 +787,7 @@ static int rebindunix(struct socket *sk, struct sockaddr *name, socklen_t namele return(-1); if(unlink(un->sun_path)) return(-1); - if(bind(sk->fd, name, namelen) < 0) + if(bind(ufd->fd, name, namelen) < 0) return(-1); return(0); } @@ -668,9 +800,11 @@ static int rebindunix(struct socket *sk, struct sockaddr *name, socklen_t namele * netcslisten() instead. */ -struct socket *netcslistenlocal(int type, struct sockaddr *name, socklen_t namelen, void (*func)(struct socket *, struct socket *, void *), void *data) +struct lport *netcslistenlocal(int type, struct sockaddr *name, socklen_t namelen, void (*func)(struct lport *, struct socket *, void *), void *data) { - struct socket *sk; + struct lport *lp; + struct ufd *ufd; + int fd; int intbuf; /* I don't know if this is actually correct (it probably isn't), @@ -679,30 +813,30 @@ struct socket *netcslistenlocal(int type, struct sockaddr *name, socklen_t namel * smoother implementation. If it breaks something on your * platform, please tell me so. */ - if((sk = mksock(name->sa_family, type)) == NULL) + if((fd = socket(name->sa_family, type, 0)) < 0) return(NULL); - sk->state = SOCK_LST; - if(confgetint("net", "reuseaddr")) - { + if(confgetint("net", "reuseaddr")) { intbuf = 1; - setsockopt(sk->fd, SOL_SOCKET, SO_REUSEADDR, &intbuf, sizeof(intbuf)); + setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &intbuf, sizeof(intbuf)); } - if((bind(sk->fd, name, namelen) < 0) && ((errno != EADDRINUSE) || (rebindunix(sk, name, namelen) < 0))) - { - putsock(sk); + ufd = mkufd(fd, UFD_LISTEN, NULL); + ufd->d.l.lp = lp; + ufd->d.l.family = name->sa_family; + if((bind(fd, name, namelen) < 0) && ((errno != EADDRINUSE) || (rebindunix(ufd, name, namelen) < 0))) { + freeufd(ufd); return(NULL); } - if(listen(sk->fd, 16) < 0) + if(listen(fd, 16) < 0) { - putsock(sk); + freeufd(ufd); return(NULL); } - sk->acceptcb = func; - sk->data = data; - return(sk); + lp->acceptcb = func; + lp->data = data; + return(lp); } -struct socket *netcslisten(int type, struct sockaddr *name, socklen_t namelen, void (*func)(struct socket *, struct socket *, void *), void *data) +struct lport *netcslisten(int type, struct sockaddr *name, socklen_t namelen, void (*func)(struct lport *, struct socket *, void *), void *data) { if(confgetint("net", "mode") == 1) { @@ -715,14 +849,14 @@ struct socket *netcslisten(int type, struct sockaddr *name, socklen_t namelen, v return(NULL); } -struct socket *netcstcplisten(int port, int local, void (*func)(struct socket *, struct socket *, void *), void *data) +struct lport *netcstcplisten(int port, int local, void (*func)(struct lport *, struct socket *, void *), void *data) { struct sockaddr_in addr; #ifdef HAVE_IPV6 struct sockaddr_in6 addr6; #endif - struct socket *(*csfunc)(int, struct sockaddr *, socklen_t, void (*)(struct socket *, struct socket *, void *), void *); - struct socket *ret; + struct lport *(*csfunc)(int, struct sockaddr *, socklen_t, void (*)(struct lport *, struct socket *, void *), void *); + struct lport *ret; if(local) csfunc = netcslistenlocal; @@ -754,42 +888,27 @@ struct socket *netcsdgram(struct sockaddr *name, socklen_t namelen) { if((sk = mksock(name->sa_family, SOCK_DGRAM)) == NULL) return(NULL); - if(bind(sk->fd, name, namelen) < 0) + if(bind(sk->ufd->fd, name, namelen) < 0) { putsock(sk); return(NULL); } - sk->state = SOCK_EST; - return(sk); + sksetstate(sk, SOCK_EST); + return(sk->back); } errno = EOPNOTSUPP; return(NULL); } -struct socket *netdupsock(struct socket *sk) +struct socket *netdgramconn(struct socket *sk, struct sockaddr *addr, socklen_t addrlen) { - struct socket *newsk; + struct ufd *nufd; - newsk = newsock(sk->type); - if((newsk->fd = dup(sk->fd)) < 0) - { - flog(LOG_WARNING, "could not dup() socket: %s", strerror(errno)); - putsock(newsk); - return(NULL); - } - newsk->state = sk->state; - newsk->ignread = sk->ignread; - if(sk->remote != NULL) - memcpy(newsk->remote = smalloc(sk->remotelen), sk->remote, newsk->remotelen = sk->remotelen); - return(newsk); -} - -void netdgramconn(struct socket *sk, struct sockaddr *addr, socklen_t addrlen) -{ - if(sk->remote != NULL) - free(sk->remote); - memcpy(sk->remote = smalloc(addrlen), addr, sk->remotelen = addrlen); - sk->ignread = 1; + nufd = dupufd(sk->back->ufd); + sk = nufd->sk->back; + memcpy(nufd->d.s.remote = smalloc(addrlen), addr, nufd->d.s.remotelen = addrlen); + nufd->ignread = 1; + return(sk); } struct socket *netcsconn(struct sockaddr *addr, socklen_t addrlen, void (*func)(struct socket *, int, void *), void *data) @@ -802,19 +921,19 @@ struct socket *netcsconn(struct sockaddr *addr, socklen_t addrlen, void (*func)( { if((sk = mksock(addr->sa_family, SOCK_STREAM)) == NULL) return(NULL); - memcpy(sk->remote = smalloc(addrlen), addr, sk->remotelen = addrlen); - if(!connect(sk->fd, addr, addrlen)) + memcpy(sk->ufd->d.s.remote = smalloc(addrlen), addr, sk->ufd->d.s.remotelen = addrlen); + sk->back->conncb = func; + sk->back->data = data; + if(!connect(sk->ufd->fd, addr, addrlen)) { - sk->state = SOCK_EST; - func(sk, 0, data); - return(sk); + sksetstate(sk, SOCK_EST); + linksock(&cbatch, sk->back); + return(sk->back); } if(errno == EINPROGRESS) { - sk->state = SOCK_SYN; - sk->conncb = func; - sk->data = data; - return(sk); + sksetstate(sk, SOCK_SYN); + return(sk->back); } putsock(sk); return(NULL); @@ -823,57 +942,60 @@ struct socket *netcsconn(struct sockaddr *addr, socklen_t addrlen, void (*func)( return(NULL); } -static void acceptunix(struct socket *sk) +static void acceptunix(struct ufd *ufd) { int buf; buf = 1; #if UNIX_AUTH_STYLE == 1 - if(setsockopt(sk->fd, SOL_SOCKET, SO_PASSCRED, &buf, sizeof(buf)) < 0) - flog(LOG_WARNING, "could not enable SO_PASSCRED on Unix socket %i: %s", sk->fd, strerror(errno)); + if(setsockopt(ufd->fd, SOL_SOCKET, SO_PASSCRED, &buf, sizeof(buf)) < 0) + flog(LOG_WARNING, "could not enable SO_PASSCRED on Unix socket %i: %s", ufd->fd, strerror(errno)); #elif UNIX_AUTH_STYLE == 2 - if(getpeereid(sk->fd, &sk->ucred.uid, &sk->ucred.gid) < 0) + if(getpeereid(ufd->fd, &ufd->d.s.ucred.uid, &ufd->d.s.ucred.gid) < 0) { - flog(LOG_WARNING, "could not get peer creds on Unix socket %i: %s", sk->fd, strerror(errno)); - sk->ucred.uid = -1; - sk->ucred.gid = -1; + flog(LOG_WARNING, "could not get peer creds on Unix socket %i: %s", ufd->fd, strerror(errno)); + ufd->d.s.ucred.uid = -1; + ufd->d.s.ucred.gid = -1; } #endif } int pollsocks(int timeout) { - int ret, fd; + int ret; socklen_t retlen; int newfd, maxfd; fd_set rfds, wfds, efds; - struct socket *sk, *next, *newsk; + struct ufd *ufd, *nufd, *next; + struct socket *nsk; struct sockaddr_storage ss; socklen_t sslen; struct timeval tv; + struct scons *sc, *nsc; FD_ZERO(&rfds); FD_ZERO(&wfds); FD_ZERO(&efds); - for(maxfd = 0, sk = sockets; sk != NULL; sk = sk->next) - { - if((sk->state == SOCK_STL) || (sk->fd < 0)) + for(maxfd = 0, ufd = ufds; ufd != NULL; ufd = ufd->next) { + if(ufd->fd < 0) continue; - if(!sk->ignread) - FD_SET(sk->fd, &rfds); - if((sk->state == SOCK_SYN) || (sockqueuesize(sk) > 0)) - FD_SET(sk->fd, &wfds); - FD_SET(sk->fd, &efds); - if(sk->fd > maxfd) - maxfd = sk->fd; + if(!ufd->ignread) + FD_SET(ufd->fd, &rfds); + if(ufd->sk != NULL) { + if(sockgetdatalen(ufd->sk) > 0) + FD_SET(ufd->fd, &wfds); + else if(ufd->sk->state == SOCK_SYN) + FD_SET(ufd->fd, &wfds); + } + FD_SET(ufd->fd, &efds); + if(ufd->fd > maxfd) + maxfd = ufd->fd; } tv.tv_sec = timeout / 1000; tv.tv_usec = (timeout % 1000) * 1000; ret = select(maxfd + 1, &rfds, &wfds, &efds, (timeout < 0)?NULL:&tv); - if(ret < 0) - { - if(errno != EINTR) - { + if(ret < 0) { + if(errno != EINTR) { flog(LOG_CRIT, "pollsocks: select errored out: %s", strerror(errno)); /* To avoid CPU hogging in case it's bad, which it * probably is. */ @@ -881,93 +1003,77 @@ int pollsocks(int timeout) } return(1); } - for(sk = sockets; sk != NULL; sk = next) + for(ufd = ufds; ufd != NULL; ufd = ufd->next) { - next = sk->next; - fd = sk->fd; - switch(sk->state) - { - case SOCK_LST: - if(FD_ISSET(fd, &rfds)) - { + if(ufd->sk < 0) + continue; + if(ufd->type == UFD_LISTEN) { + if(FD_ISSET(ufd->fd, &rfds)) { sslen = sizeof(ss); - if((newfd = accept(fd, (struct sockaddr *)&ss, &sslen)) < 0) - { - if(sk->errcb != NULL) - sk->errcb(sk, errno, sk->data); + if((newfd = accept(ufd->fd, (struct sockaddr *)&ss, &sslen)) < 0) { + if(ufd->d.l.lp->errcb != NULL) + ufd->d.l.lp->errcb(ufd->d.l.lp, errno, ufd->d.l.lp->data); } - newsk = newsock(sk->type); - newsk->fd = newfd; - newsk->family = sk->family; - newsk->state = SOCK_EST; - memcpy(newsk->remote = smalloc(sslen), &ss, sslen); - newsk->remotelen = sslen; + nsk = sockpair(0); + nufd = mkufd(newfd, UFD_SOCK, nsk); + nufd->d.s.family = ufd->d.l.family; + sksetstate(nsk, SOCK_EST); + memcpy(nufd->d.s.remote = smalloc(sslen), &ss, sslen); + nufd->d.s.remotelen = sslen; if(ss.ss_family == PF_UNIX) - acceptunix(newsk); - if(sk->acceptcb != NULL) - sk->acceptcb(sk, newsk, sk->data); - putsock(newsk); + acceptunix(nufd); + if(ufd->d.l.lp->acceptcb != NULL) + ufd->d.l.lp->acceptcb(ufd->d.l.lp, nsk, ufd->d.l.lp->data); + putsock(nsk); } - if(FD_ISSET(fd, &efds)) - { + if(FD_ISSET(ufd->fd, &efds)) { retlen = sizeof(ret); - getsockopt(fd, SOL_SOCKET, SO_ERROR, &ret, &retlen); - if(sk->errcb != NULL) - sk->errcb(sk, ret, sk->data); + getsockopt(ufd->fd, SOL_SOCKET, SO_ERROR, &ret, &retlen); + if(ufd->d.l.lp->errcb != NULL) + ufd->d.l.lp->errcb(ufd->d.l.lp, ret, ufd->d.l.lp->data); continue; } - break; - case SOCK_SYN: - if(FD_ISSET(fd, &efds)) - { - retlen = sizeof(ret); - getsockopt(fd, SOL_SOCKET, SO_ERROR, &ret, &retlen); - if(sk->conncb != NULL) - sk->conncb(sk, ret, sk->data); - closesock(sk); - continue; - } - if(FD_ISSET(fd, &rfds) || FD_ISSET(fd, &wfds)) - { - sk->state = SOCK_EST; - if(sk->conncb != NULL) - sk->conncb(sk, 0, sk->data); - } - break; - case SOCK_EST: - if(FD_ISSET(fd, &efds)) - { - retlen = sizeof(ret); - getsockopt(fd, SOL_SOCKET, SO_ERROR, &ret, &retlen); - if(sk->errcb != NULL) - sk->errcb(sk, ret, sk->data); - closesock(sk); - continue; - } - if(FD_ISSET(fd, &rfds)) - sockrecv(sk); - if(FD_ISSET(fd, &wfds)) - { - if(sockqueuesize(sk) > 0) - sockflush(sk); + } else { + if(ufd->sk->state == SOCK_SYN) { + if(FD_ISSET(ufd->fd, &efds)) { + retlen = sizeof(ret); + getsockopt(ufd->fd, SOL_SOCKET, SO_ERROR, &ret, &retlen); + if(ufd->sk->back->conncb != NULL) + ufd->sk->back->conncb(ufd->sk->back, ret, ufd->sk->back->data); + closeufd(ufd); + continue; + } + if(FD_ISSET(ufd->fd, &rfds) || FD_ISSET(ufd->fd, &wfds)) { + sksetstate(ufd->sk, SOCK_EST); + linksock(&cbatch, ufd->sk->back); + } + } else if(ufd->sk->state == SOCK_EST) { + if(FD_ISSET(ufd->fd, &efds)) { + retlen = sizeof(ret); + getsockopt(ufd->fd, SOL_SOCKET, SO_ERROR, &ret, &retlen); + sockerror(ufd->sk, ret); + closeufd(ufd); + continue; + } + if(FD_ISSET(ufd->fd, &rfds)) + sockrecv(ufd); + if(FD_ISSET(ufd->fd, &wfds)) + sockflush(ufd); } - break; } } - for(sk = sockets; sk != NULL; sk = next) + for(ufd = ufds; ufd != NULL; ufd = next) { - next = sk->next; - if(sk->refcount == 1 && (sockqueuesize(sk) == 0)) - { - unlinksock(sk); - continue; - } - if(sk->close && (sockqueuesize(sk) == 0)) - closesock(sk); - if(sk->state == SOCK_STL) - { - unlinksock(sk); - continue; + next = ufd->next; + if(sockgetdatalen(ufd->sk) == 0) { + if(ufd->sk->eos) { + closeufd(ufd); + closesock(ufd->sk); + } + if((ufd->sk->refcount == 1) && (ufd->sk->back->refcount == 0)) { + freeufd(ufd); + continue; + } } } return(1); @@ -1259,6 +1365,29 @@ int sockgetremotename2(struct socket *sk, struct socket *sk2, struct sockaddr ** return(0); } +void sockblock(struct socket *sk, int block) +{ + sk->block = block; +} + +int sockpeeraddr(struct socket *sk, struct sockaddr **namebuf, socklen_t *lenbuf) +{ + return(-1); +} + +char *formatsockpeer(struct socket *sk) +{ + struct sockaddr *name; + socklen_t nlen; + char *ret; + + if(sockpeeraddr(sk, &name, &nlen)) + return(NULL); + ret = formataddress(name, nlen); + free(name); + return(ret); +} + int addreq(struct sockaddr *x, struct sockaddr *y) { struct sockaddr_un *u1, *u2; diff --git a/daemon/net.h b/daemon/net.h index 23275b3..b985ee9 100644 --- a/daemon/net.h +++ b/daemon/net.h @@ -45,6 +45,8 @@ struct socket int state; int block; int dgram; + int eos; + struct socket *back; union { struct @@ -76,12 +78,14 @@ struct lport { void putsock(struct socket *sk); void getsock(struct socket *sk); -struct lport *netcslisten(int type, struct sockaddr *name, socklen_t namelen, void (*func)(struct socket *, struct socket *, void *), void *data); -struct lport *netcslistenlocal(int type, struct sockaddr *name, socklen_t namelen, void (*func)(struct socket *, struct socket *, void *), void *data); -struct lport *netcstcplisten(int port, int local, void (*func)(struct socket *, struct socket *, void *), void *data); +struct lport *netcslisten(int type, struct sockaddr *name, socklen_t namelen, void (*func)(struct lport *, struct socket *, void *), void *data); +struct lport *netcslistenlocal(int type, struct sockaddr *name, socklen_t namelen, void (*func)(struct lport *, struct socket *, void *), void *data); +struct lport *netcstcplisten(int port, int local, void (*func)(struct lport *, struct socket *, void *), void *data); struct socket *netcsconn(struct sockaddr *addr, socklen_t addrlen, void (*func)(struct socket *, int, void *), void *data); int pollsocks(int timeout); +void freedgbuf(struct dgrambuf *dg); void sockqueue(struct socket *sk, void *data, size_t size); +void sockeos(struct socket *sk); size_t sockqueuesize(struct socket *sk); int netresolve(char *addr, void (*callback)(struct sockaddr *addr, int addrlen, void *data), void *data); struct socket *netcsdgram(struct sockaddr *name, socklen_t namelen); -- 2.11.0