{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... */
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)
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)
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;
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
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))
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)
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))
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);
}
* 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),
* 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)
{
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;
{
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)
{
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);
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. */
}
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);
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;