Initial work on the new implementation
authorFredrik Tolf <fredrik@dolda2000.com>
Sun, 16 Mar 2008 01:46:18 +0000 (02:46 +0100)
committerFredrik Tolf <fredrik@dolda2000.com>
Sun, 16 Mar 2008 01:46:18 +0000 (02:46 +0100)
daemon/net.c
daemon/net.h

index 44974f1..3265417 100644 (file)
@@ -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;
index 23275b3..b985ee9 100644 (file)
@@ -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);