Merge branch 'master' into socket
authorFredrik Tolf <fredrik@dolda2000.com>
Fri, 17 Oct 2008 23:11:09 +0000 (01:11 +0200)
committerFredrik Tolf <fredrik@dolda2000.com>
Fri, 17 Oct 2008 23:11:09 +0000 (01:11 +0200)
Conflicts:

daemon/net.c

1  2 
daemon/fnet-dc.c
daemon/net.c
daemon/ui.c

diff --combined daemon/fnet-dc.c
@@@ -141,7 -141,7 +141,7 @@@ struct dcpee
  static struct fnet dcnet;
  static struct transferiface dctransfer;
  static struct socket *udpsock = NULL;
 -static struct socket *tcpsock = NULL;
 +static struct lport *tcpsock = NULL;
  static struct dcpeer *peers = NULL;
  int numdcpeers = 0;
  static struct dcexppeer *expected = NULL;
@@@ -1145,7 -1145,7 +1145,7 @@@ static void cmd_search(struct socket *s
            goto out;
        prefix = sprintf2("$SR %s ", hub->nativenick);
        infix = sprintf2(" %i/%i\005", slotsleft(), confgetint("transfer", "slots"));
 -      postfix = sprintf2(" (%s)\005%s|", formataddress(hub->sk->remote, hub->sk->remotelen), args + 4);
 +      postfix = sprintf2(" (%s)\005%s|", formatsockpeer(hub->sk), args + 4);
        dsk = sk;
        getsock(dsk);
      } else {
        addr.sin_port = htons(atoi(p2));
        prefix = sprintf2("$SR %s ", hub->nativenick);
        infix = sprintf2(" %i/%i\005", slotsleft(), confgetint("transfer", "slots"));
 -      postfix = sprintf2(" (%s)|", formataddress(hub->sk->remote, hub->sk->remotelen));
 -      netdgramconn(dsk = netdupsock(udpsock), (struct sockaddr *)&addr, sizeof(addr));
 +      postfix = sprintf2(" (%s)|", formatsockpeer(hub->sk));
 +      dsk = netdgramconn(udpsock, (struct sockaddr *)&addr, sizeof(addr));
      }
      
      minsize = maxsize = -1;
@@@ -1351,7 -1351,7 +1351,7 @@@ static void sendctm(struct socket *sk, 
      
      if(tcpsock == NULL)
        return;
 -    if(sockgetremotename2(tcpsock, sk, &addr, &addrlen) < 0)
 +    if(lstgetremotename2(tcpsock, sk, &addr, &addrlen) < 0)
        return;
      if(addr->sa_family == AF_INET)
        qstrf(sk, "$ConnectToMe %s %s|", nick, formataddress(addr, addrlen));
@@@ -2117,6 -2117,7 +2117,7 @@@ static void cmd_adcget(struct socket *s
      } else if(fd >= 0) {
        if((wbuf = adc2path(argv[1])) != NULL)
            transfersetpath(peer->transfer, wbuf);
+       free(wbuf);
        peer->transfer->flags.b.minislot = 1;
      }
      if(fd < 0)
@@@ -2294,6 -2295,7 +2295,6 @@@ static void cmd_adcsnd(struct socket *s
        {
            sockpushdata(sk, peer->inbuf, peer->inbufdata);
            peer->inbufdata = 0;
 -          transread(sk, peer);
        }
      } else {
        /* We certainly didn't request this...*/
@@@ -2326,6 -2328,7 +2327,6 @@@ static void cmd_sending(struct socket *
      {
        sockpushdata(sk, peer->inbuf, peer->inbufdata);
        peer->inbufdata = 0;
 -      transread(sk, peer);
      }
  }
  
@@@ -2691,7 -2694,7 +2692,7 @@@ static void dctransgotdata(struct trans
      
      if((peer->state == PEER_TRNS) || (peer->state == PEER_SYNC))
      {
 -      if(sockqueuesize(peer->sk) < 65536)
 +      if(sockqueueleft(peer->sk) > 0)
        {
            if((buf = transfergetdata(transfer, &bufsize)) != NULL)
            {
@@@ -2762,14 -2765,18 +2763,14 @@@ static void dctransendofdata(struct tra
      dctransgotdata(transfer, peer);
  }
  
 -static void dcwantdata(struct transfer *transfer, struct dcpeer *peer)
 -{
 -    if(transferdatasize(transfer) < 65536)
 -      peer->sk->ignread = 0;
 -}
 -
  static void transread(struct socket *sk, struct dcpeer *peer)
  {
      void *buf;
      size_t bufsize;
      struct transfer *transfer;
      
 +    if(transferdatasize(peer->transfer) < 0)
 +      return;
      if((buf = sockgetinbuf(sk, &bufsize)) == NULL)
        return;
      if(peer->transfer == NULL)
        transferendofdata(transfer);
        return;
      }
 -    if(transferdatasize(peer->transfer) > 65535)
 -      sk->ignread = 1;
 +}
 +
 +static void dcwantdata(struct transfer *transfer, struct dcpeer *peer)
 +{
 +    if(transferdatasize(transfer) > 0)
 +      transread(peer->sk, peer);
  }
  
  static void transerr(struct socket *sk, int err, struct dcpeer *peer)
@@@ -2826,7 -2829,6 +2827,7 @@@ static void udpread(struct socket *sk, 
      size_t buflen, hashlen;
      char *nick, *filename, *hubname;
      struct sockaddr_in hubaddr;
 +    struct sockaddr *addrbuf;
      off_t size;
      int slots;
      struct fnetnode *fn, *myfn;
        {
            for(fn = fnetnodes; fn != NULL; fn = fn->next)
            {
 -              if((fn->fnet == &dcnet) && ((hub = fn->data) != NULL))
 +              if((fn->fnet == &dcnet) && ((hub = fn->data) != NULL) && !sockpeeraddr(hub->sk, &addrbuf, NULL))
                {
 -                  if((hub->sk != NULL) && addreq(hub->sk->remote, (struct sockaddr *)&hubaddr))
 +                  if((hub->sk != NULL) && addreq(addrbuf, (struct sockaddr *)&hubaddr))
                    {
                        myfn = fn;
 +                      free(addrbuf);
                        break;
                    }
 +                  free(addrbuf);
                }
            }
        }
@@@ -2993,8 -2993,6 +2994,8 @@@ static void hubread(struct socket *sk, 
      char *p, *p2;
      
      hub = (struct dchub *)fn->data;
 +    if(hub->queue.size > 1000)
 +      return;
      if((newbuf = sockgetinbuf(sk, &datalen)) == NULL)
        return;
      if(hub->inbufdata > 500000) /* Discard possible malicious data */
        p = p2;
      }
      memmove(hub->inbuf, p, hub->inbufdata -= p - hub->inbuf);
 -    if(hub->queue.size > 1000)
 -      sk->ignread = 1;
  }
  
  static void huberr(struct socket *sk, int err, struct fnetnode *fn)
@@@ -3205,7 -3205,7 +3206,7 @@@ static void hubkill(struct fnetnode *fn
      struct dchub *hub;
      
      hub = (struct dchub *)fn->data;
 -    hub->sk->close = 1;
 +    closesock(hub->sk);
  }
  
  static struct transferiface dctransfer =
@@@ -3234,10 -3234,6 +3235,10 @@@ static void peerread(struct socket *sk
      size_t datalen, cnlen;
      struct command *cmd;
  
 +    if(peer->state == PEER_CMD) {
 +      if((peer->queue.size > 50) || (peer->inbufdata > 65536))
 +          return;
 +    }
      if((newbuf = sockgetinbuf(sk, &datalen)) == NULL)
        return;
      sizebuf2(peer->inbuf, peer->inbufdata + datalen, 1);
            {
                peer->state = PEER_STOP;
                break;
 -          } else {
 -              if(peer->queue.size > 50)
 -                  sk->ignread = 1;
            }
        }
      } else if(peer->state == PEER_TTHL) {
        handletthl(peer);
      }
 -    if(peer->inbufdata > 500000)
 -      sk->ignread = 1;
  }
  
  static void peererror(struct socket *sk, int err, struct dcpeer *peer)
@@@ -3301,7 -3302,7 +3302,7 @@@ static void peerconnect(struct socket *
      sendpeerlock(peer);
  }
  
 -static void peeraccept(struct socket *sk, struct socket *newsk, void *data)
 +static void peeraccept(struct lport *lp, struct socket *newsk, void *data)
  {
      struct dcpeer *peer;
      
@@@ -3771,7 -3772,7 +3772,7 @@@ static int run(void
            quota--;
        }
        if(hub->queue.size < 1000)
 -          hub->sk->ignread = 0;
 +          hubread(hub->sk, fn);
        if(quota < 1)
            break;
      }
            quota--;
        }
        if((peer->queue.size < 50) && (peer->inbufdata < 500000))
 -          peer->sk->ignread = 0;
 +          peerread(peer->sk, peer);
        if(quota < 1)
            break;
      }
@@@ -3833,7 -3834,7 +3834,7 @@@ static int updateudpport(struct configv
  static int updatetcpport(struct configvar *var, void *uudata)
  {
      struct sockaddr_in addr;
 -    struct socket *newsock;
 +    struct lport *newsock;
      
      memset(&addr, 0, sizeof(addr));
      addr.sin_family = AF_INET;
      if((newsock = netcslisten(SOCK_STREAM, (struct sockaddr *)&addr, sizeof(addr), peeraccept, NULL)) == NULL)
        flog(LOG_INFO, "could not listen to a remote address, going into passive mode");
      if(tcpsock != NULL)
 -      putsock(tcpsock);
 +      closelport(tcpsock);
      tcpsock = newsock;
      return(0);
  }
@@@ -3856,7 -3857,7 +3857,7 @@@ static int init(int hup
        if(udpsock != NULL)
            putsock(udpsock);
        if(tcpsock != NULL)
 -          putsock(tcpsock);
 +          closelport(tcpsock);
        addr.sin_family = AF_INET;
        memset(&addr.sin_addr, 0, sizeof(addr.sin_addr));
        addr.sin_port = htons(confgetint("dc", "udpport"));
diff --combined daemon/net.c
@@@ -66,8 -66,6 +66,6 @@@ static struct configvar myvars[] 
       * and net.visibleipv4 are unspecified the address of the hub
       * connection is used. */
      {CONF_VAR_STRING, "publicif", {.str = L""}},
-     /* Diffserv should be supported on IPv4, too, but I don't know the
-      * API to do that. */
      /** The Diffserv value to use on IPv6 connections when the
       * minimize cost TOS value is used (see the TOS VALUES
       * section). */
       * minimize delay TOS value is used (see the TOS VALUES
       * section). */
      {CONF_VAR_INT, "diffserv-mindelay", {.num = 0}},
+     /** If enabled, the IP TOS interface will be used to set Diffserv
+      * codepoints on IPv4 sockets, by shifting the DSCP value two bits
+      * to the left (remember, the DSCP field in the IPv4 header is
+      * defined as the 6 uppermost bits of the TOS field, the lower two
+      * being left for ECN). This may only work on Linux. */
+     {CONF_VAR_BOOL, "dscp-tos", {.num = 0}},
      {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 int getlocalname(int fd, struct sockaddr **namebuf, socklen_t *lenbuf);
 +
 +static struct ufd *ufds = NULL;
 +static struct scons *rbatch, *wbatch, *cbatch;
  int numsocks = 0;
  
  /* XXX: Get autoconf for all this... */
@@@ -203,145 -171,93 +207,145 @@@ int getpublicaddr(int af, struct sockad
      return(1);
  }
  
 -static struct socket *newsock(int type)
 +static struct socket *newsock1(int dgram)
  {
      struct socket *new;
      
 -    new = smalloc(sizeof(*new));
 -    memset(new, 0, 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;
 +    new->maxbuf = 65536;
      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);
 +    if(ufd->sk != NULL)
 +      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;
 +    ufd->type = type;
 +    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);
 +    }
 +    sksetstate(nsk, SOCK_EST);
 +    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)
 +{
 +    struct socket *back;
 +    
 +    if(--(sk->refcount) < 0) {
 +      flog(LOG_CRIT, "BUG: socket refcount < 0");
 +      abort();
 +    }
 +    if((sk->refcount == 0) && (sk->back->refcount == 0)) {
 +      back = sk->back;
 +      freesock(sk);
 +      freesock(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)
 +{
 +    if((sockgetdatalen(sk) == 0) && (sk->eos == 1))
 +      linksock(&rbatch, 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);
  }
  
 -static void recvcmsg(struct socket *sk, struct msghdr *msg)
 +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 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 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);
 +              closesock(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);
 +          closesock(ufd->sk);
 +          return;
 +      }
 +      sockqueue(ufd->sk, buf, ret);
 +      free(buf);
      }
  }
  
 -static void sockflush(struct socket *sk)
 +static int 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) {
 +      errno = EBADFD;
 +      return(-1);
 +    }
 +    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);
 +          ret = write(ufd->fd, ufd->sk->buf.s.buf, ufd->sk->buf.s.datasize);
        if(ret < 0)
 -      {
 -          if((errno != EINTR) && (errno != EAGAIN))
 -          {
 -              if(sk->errcb != NULL)
 -                  sk->errcb(sk, errno, sk->data);
 -              closesock(sk);
 -          }
 -          break;
 -      }
 -      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);
 +          return(-1);
 +      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;
      }
 +    return(0);
  }
  
  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))
 -    {
 -      if((sk->state == SOCK_LST) && strchr(un->sun_path, '/'))
 -      {
 -          if(unlink(un->sun_path))
 -              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);
 +    if(sk->back->eos == 0)
 +      sk->back->eos = 1;
 +    linksock(&rbatch, sk->back);
  }
  
  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)
 +/* size_t sockqueuesize(struct socket *sk) */
 +/* { */
 +/*     return(sockgetdatalen(sk->back)); */
 +/* } */
 +
 +ssize_t sockqueueleft(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(sk->back->maxbuf - 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);
  }
  
 +void closelport(struct lport *lp)
 +{
 +    struct ufd *ufd;
 +    struct sockaddr_un *un;
 +    
 +    ufd = lp->ufd;
 +    if((ufd->d.l.family == AF_UNIX) && !getlocalname(ufd->fd, (struct sockaddr **)(void *)&un, NULL) && (un->sun_family == PF_UNIX) && strchr(un->sun_path, '/')) {
 +      if(unlink(un->sun_path))
 +          flog(LOG_WARNING, "could not unlink Unix socket %s: %s", un->sun_path, strerror(errno));
 +    }
 +    freeufd(lp->ufd);
 +}
 +
  /*
   * The difference between netcslisten() and netcslistenlocal() is that
   * netcslistenlocal() always listens on the local host, instead of
   * 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.family = name->sa_family;
 +    lp = memset(smalloc(sizeof(*lp)), 0, sizeof(*lp));
 +    lp->ufd = ufd;
 +    ufd->d.l.lp = lp;
 +    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;
@@@ -901,27 -763,42 +905,27 @@@ struct socket *netcsdgram(struct sockad
      {
        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);
 +    getsock(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
  }
  
 +static void runbatches(void)
 +{
 +    struct scons *sc, *nsc;
 +
 +    for(sc = cbatch, cbatch = NULL; sc; sc = nsc) {
 +      nsc = sc->n;
 +      if(sc->s->conncb != NULL)
 +          sc->s->conncb(sc->s, 0, sc->s->data);
 +      putsock(sc->s);
 +      free(sc);
 +    }
 +    for(sc = rbatch, rbatch = NULL; sc; sc = nsc) {
 +      nsc = sc->n;
 +      if(sc->s->readcb != NULL)
 +          sc->s->readcb(sc->s, sc->s->data);
 +      if((sockgetdatalen(sc->s) == 0) && (sc->s->eos == 1)) {
 +          if(sc->s->errcb != NULL)
 +              sc->s->errcb(sc->s, 0, sc->s->data);
 +          sc->s->eos = 2;
 +      }
 +      putsock(sc->s);
 +      free(sc);
 +    }
 +    for(sc = wbatch, wbatch = NULL; sc; sc = nsc) {
 +      nsc = sc->n;
 +      if(sc->s->writecb != NULL)
 +          sc->s->writecb(sc->s, sc->s->data);
 +      putsock(sc->s);
 +      free(sc);
 +    }
 +}
 +
 +static void cleansocks(void)
 +{
 +    struct ufd *ufd, *next;
 +    
 +    for(ufd = ufds; ufd != NULL; ufd = next) {
 +      next = ufd->next;
 +      if(ufd->sk && ((ufd->fd < 0) || (sockgetdatalen(ufd->sk) == 0))) {
 +          if(ufd->sk->eos == 1) {
 +              ufd->sk->eos = 2;
 +              closeufd(ufd);
 +              closesock(ufd->sk);
 +          }
 +          if((ufd->sk->refcount == 1) && (ufd->sk->back->refcount == 0)) {
 +              freeufd(ufd);
 +              continue;
 +          }
 +      }
 +    }
 +}
 +
  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;
 +    struct socket *nsk;
      struct sockaddr_storage ss;
      socklen_t sslen;
      struct timeval tv;
      
 +    cleansocks();
      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 && ((ufd->sk == NULL) || (sockqueueleft(ufd->sk) > 0)))
 +          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;
      }
 +    if(rbatch || wbatch || cbatch)
 +      timeout = 0;
      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)
 -    {
 -      next = sk->next;
 -      fd = sk->fd;
 -      switch(sk->state)
 -      {
 -      case SOCK_LST:
 -          if(FD_ISSET(fd, &rfds))
 -          {
 +    for(ufd = ufds; ufd != NULL; ufd = ufd->next) {
 +      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->back, 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(ufd->fd == -1)
 +                  continue;
 +              if(FD_ISSET(ufd->fd, &wfds)) {
 +                  if(sockflush(ufd)) {
 +                      sockerror(ufd->sk, errno);
 +                      closeufd(ufd);
 +                      continue;
 +                  }
 +              }
            }
 -          break;
 -      }
 -    }
 -    for(sk = sockets; sk != NULL; sk = 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;
        }
      }
 +    runbatches();
 +    cleansocks();
      return(1);
  }
  
 +static struct ufd *getskufd(struct socket *sk)
 +{
 +    while(1) {
 +      if(sk->back->ufd != NULL)
 +          return(sk->back->ufd);
 +      if((sk = sk->back->pnext) == NULL)
 +          break;
 +    }
 +    return(NULL);
 +}
 +
  int socksettos(struct socket *sk, int tos)
  {
      int buf;
 +    struct ufd *ufd;
+     int dscp2tos;
      
 -    if(sk->family == AF_UNIX)
 +    ufd = getskufd(sk);
 +    if(ufd->type != UFD_SOCK) {
 +      errno = EOPNOTSUPP;
 +      return(-1);
 +    }
 +    if(ufd->d.s.family == AF_UNIX)
        return(0); /* Unix sockets are always perfect. :) */
 -    if(sk->family == AF_INET)
 +    if(ufd->d.s.family == AF_INET)
      {
+       dscp2tos = confgetint("net", "dscp-tos");
        switch(tos)
        {
        case 0:
            buf = 0;
            break;
        case SOCK_TOS_MINCOST:
-           buf = 0x02;
+           if(dscp2tos)
+               buf = confgetint("net", "diffserv-mincost") << 2;
+           else
+               buf = 0x02;
            break;
        case SOCK_TOS_MAXREL:
-           buf = 0x04;
+           if(dscp2tos)
+               buf = confgetint("net", "diffserv-maxrel") << 2;
+           else
+               buf = 0x04;
            break;
        case SOCK_TOS_MAXTP:
-           buf = 0x08;
+           if(dscp2tos)
+               buf = confgetint("net", "diffserv-maxtp") << 2;
+           else
+               buf = 0x08;
            break;
        case SOCK_TOS_MINDELAY:
-           buf = 0x10;
+           if(dscp2tos)
+               buf = confgetint("net", "diffserv-mindelay") << 2;
+           else
+               buf = 0x10;
            break;
        default:
            flog(LOG_WARNING, "attempted to set unknown TOS value %i to IPv4 sock", tos);
            return(-1);
        }
 -      if(setsockopt(sk->fd, IPPROTO_IP, IP_TOS, &buf, sizeof(buf)) < 0)
 +      if(setsockopt(ufd->fd, IPPROTO_IP, IP_TOS, &buf, sizeof(buf)) < 0)
        {
            flog(LOG_WARNING, "could not set sock TOS to %i: %s", tos, strerror(errno));
            return(-1);
        }
        return(0);
      }
 -    if(sk->family == AF_INET6)
 +    if(ufd->d.s.family == AF_INET6)
      {
        switch(tos)
        {
        */
        return(0);
      }
 -    flog(LOG_WARNING, "could not set TOS on sock of family %i", sk->family);
 +    flog(LOG_WARNING, "could not set TOS on sock of family %i", ufd->d.s.family);
      return(1);
  }
  
@@@ -1328,16 -1167,16 +1346,16 @@@ int netresolve(char *addr, void (*callb
      return(0);
  }
  
 -int sockgetlocalname(struct socket *sk, struct sockaddr **namebuf, socklen_t *lenbuf)
 +static int getlocalname(int fd, struct sockaddr **namebuf, socklen_t *lenbuf)
  {
      socklen_t len;
      struct sockaddr_storage name;
      
      *namebuf = NULL;
 -    if((sk->state == SOCK_STL) || (sk->fd < 0))
 +    if(fd < 0)
        return(-1);
      len = sizeof(name);
 -    if(getsockname(sk->fd, (struct sockaddr *)&name, &len) < 0)
 +    if(getsockname(fd, (struct sockaddr *)&name, &len) < 0)
      {
        flog(LOG_ERR, "BUG: alive socket with dead fd in sockgetlocalname (%s)", strerror(errno));
        return(-1);
      return(0);
  }
  
 +int lstgetlocalname(struct lport *lp, struct sockaddr **namebuf, socklen_t *lenbuf)
 +{
 +    struct ufd *ufd;
 +
 +    ufd = lp->ufd;
 +    return(getlocalname(ufd->fd, namebuf, lenbuf));
 +}
 +
 +int sockgetlocalname(struct socket *sk, struct sockaddr **namebuf, socklen_t *lenbuf)
 +{
 +    struct ufd *ufd;
 +
 +    ufd = getskufd(sk);
 +    if(ufd->type != UFD_SOCK) {
 +      errno = EOPNOTSUPP;
 +      return(-1);
 +    }
 +    return(getlocalname(ufd->fd, namebuf, lenbuf));
 +}
 +
  static void sethostaddr(struct sockaddr *dst, struct sockaddr *src)
  {
      if(dst->sa_family != src->sa_family)
@@@ -1407,15 -1226,22 +1425,15 @@@ static int makepublic(struct sockaddr *
      return(0);
  }
  
 -int sockgetremotename(struct socket *sk, struct sockaddr **namebuf, socklen_t *lenbuf)
 +static int getremotename(int fd, struct sockaddr **namebuf, socklen_t *lenbuf)
  {
      socklen_t len;
      struct sockaddr *name;
 -    
 -    switch(confgetint("net", "mode"))
 -    {
 +
 +    switch(confgetint("net", "mode")) {
      case 0:
        *namebuf = NULL;
 -      if((sk->state == SOCK_STL) || (sk->fd < 0))
 -      {
 -          errno = EBADF;
 -          return(-1);
 -      }
 -      if(!sockgetlocalname(sk, &name, &len))
 -      {
 +      if(!getlocalname(fd, &name, &len)) {
            *namebuf = name;
            *lenbuf = len;
            makepublic(name);
      }
  }
  
 +int sockgetremotename(struct socket *sk, struct sockaddr **namebuf, socklen_t *lenbuf)
 +{
 +    struct ufd *ufd;
 +    
 +    ufd = getskufd(sk);
 +    if(ufd->type != UFD_SOCK) {
 +      errno = EOPNOTSUPP;
 +      return(-1);
 +    }
 +    if(ufd->fd < 0) {
 +      errno = EBADF;
 +      return(-1);
 +    }
 +    return(getremotename(ufd->fd, namebuf, lenbuf));
 +}
 +
 +int lstgetremotename(struct lport *lp, struct sockaddr **namebuf, socklen_t *lenbuf)
 +{
 +    struct ufd *ufd;
 +    
 +    ufd = lp->ufd;
 +    return(getremotename(ufd->fd, namebuf, lenbuf));
 +}
 +
  int sockgetremotename2(struct socket *sk, struct socket *sk2, struct sockaddr **namebuf, socklen_t *lenbuf)
  {
      struct sockaddr *name1, *name2;
      socklen_t len1, len2;
 +    struct ufd *ufd1, *ufd2;
      
 -    if(sk->family != sk2->family)
 +    ufd1 = getskufd(sk);
 +    ufd2 = getskufd(sk2);
 +    if((ufd1->type != UFD_SOCK) || (ufd2->type != UFD_SOCK)) {
 +      errno = EOPNOTSUPP;
 +      return(-1);
 +    }
 +    if(ufd1->d.s.family != ufd2->d.s.family)
      {
 -      flog(LOG_ERR, "using sockgetremotename2 with sockets of differing family: %i %i", sk->family, sk2->family);
 +      flog(LOG_ERR, "using sockgetremotename2 with sockets of differing family: %i %i", ufd1->d.s.family, ufd2->d.s.family);
        return(-1);
      }
 -    if(sockgetremotename(sk, &name1, &len1))
 +    if(getremotename(ufd1->fd, &name1, &len1))
        return(-1);
 -    if(sockgetremotename(sk2, &name2, &len2)) {
 +    if(getremotename(ufd2->fd, &name2, &len2)) {
        free(name1);
        return(-1);
      }
      return(0);
  }
  
 +int lstgetremotename2(struct lport *lp, struct socket *sk2, struct sockaddr **namebuf, socklen_t *lenbuf)
 +{
 +    struct sockaddr *name1, *name2;
 +    socklen_t len1, len2;
 +    struct ufd *ufd1, *ufd2;
 +    
 +    ufd1 = lp->ufd;
 +    ufd2 = getskufd(sk2);
 +    if(ufd2->type != UFD_SOCK) {
 +      errno = EOPNOTSUPP;
 +      return(-1);
 +    }
 +    if(ufd1->d.l.family != ufd2->d.s.family)
 +    {
 +      flog(LOG_ERR, "using lstgetremotename2 with sockets of differing family: %i %i", ufd1->d.l.family, ufd2->d.s.family);
 +      return(-1);
 +    }
 +    if(getremotename(ufd1->fd, &name1, &len1))
 +      return(-1);
 +    if(getremotename(ufd2->fd, &name2, &len2)) {
 +      free(name1);
 +      return(-1);
 +    }
 +    sethostaddr(name1, name2);
 +    free(name2);
 +    *namebuf = name1;
 +    *lenbuf = len1;
 +    return(0);
 +}
 +
 +int getucred(struct socket *sk, uid_t *uid, gid_t *gid)
 +{
 +    struct ufd *ufd;
 +    
 +    ufd = getskufd(sk);
 +    if(ufd->type != UFD_SOCK) {
 +      errno = EOPNOTSUPP;
 +      return(-1);
 +    }
 +    if(ufd->d.s.family != AF_UNIX) {
 +      errno = EOPNOTSUPP;
 +      return(-1);
 +    }
 +    *uid = ufd->d.s.ucred.uid;
 +    *gid = ufd->d.s.ucred.gid;
 +    return(0);
 +}
 +
 +/* void sockblock(struct socket *sk, int block) */
 +/* { */
 +/*     struct ufd *ufd; */
 +    
 +/*     ufd = getskufd(sk); */
 +/*     ufd->ignread = block; */
 +/* } */
 +
 +int sockfamily(struct socket *sk)
 +{
 +    struct ufd *ufd;
 +    
 +    ufd = getskufd(sk);
 +    if(ufd->type != UFD_SOCK) {
 +      errno = EOPNOTSUPP;
 +      return(-1);
 +    }
 +    return(ufd->d.s.family);
 +}
 +
 +int sockpeeraddr(struct socket *sk, struct sockaddr **namebuf, socklen_t *lenbuf)
 +{
 +    struct ufd *ufd;
 +    
 +    ufd = getskufd(sk);
 +    if(ufd->type != UFD_SOCK) {
 +      errno = EOPNOTSUPP;
 +      return(-1);
 +    }
 +    if(ufd->d.s.remote == NULL)
 +      return(-1);
 +    *namebuf = memcpy(smalloc(ufd->d.s.remotelen), ufd->d.s.remote, ufd->d.s.remotelen);
 +    if(lenbuf != NULL)
 +      *lenbuf = ufd->d.s.remotelen;
 +    return(0);
 +}
 +
 +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;
@@@ -1725,10 -1422,8 +1743,10 @@@ static int init(int hup
  
  static void terminate(void)
  {
 -    while(sockets != NULL)
 -      unlinksock(sockets);
 +    /*
 +    while(ufds != NULL)
 +      freeufd(ufds);
 +    */
  }
  
  static struct module me =
diff --combined daemon/ui.c
@@@ -171,8 -171,8 +171,8 @@@ static void notifappend(struct notif *n
  
  struct uiuser *users = NULL;
  struct uidata *actives = NULL;
 -struct socket *tcpsocket = NULL;
 -struct socket *unixsocket = NULL;
 +struct lport *tcpsocket = NULL;
 +struct lport *unixsocket = NULL;
  static time_t starttime;
  
  static wchar_t *quoteword(wchar_t *word)
@@@ -340,35 -340,33 +340,35 @@@ static void cmd_connect(struct socket *
  {
      int valid;
      struct in6_addr mv4lo;
 +    struct sockaddr *remote;
      
      if(confgetint("ui", "onlylocal"))
      {
 -      switch(sk->remote->sa_family)
 -      {
 -      case AF_INET:
 -          valid = ((struct sockaddr_in *)sk->remote)->sin_addr.s_addr == INADDR_LOOPBACK;
 -          break;
 -      case AF_INET6:
 -          inet_pton(AF_INET6, "::ffff:127.0.0.1", &mv4lo);
 -          valid = 0;
 -          if(!memcmp(&((struct sockaddr_in6 *)sk->remote)->sin6_addr, &in6addr_loopback, sizeof(in6addr_loopback)))
 -              valid = 1;
 -          if(!memcmp(&((struct sockaddr_in6 *)sk->remote)->sin6_addr, &mv4lo, sizeof(in6addr_loopback)))
 +      valid = 0;
 +      if(!sockpeeraddr(sk, &remote, NULL)) {
 +          switch(remote->sa_family)
 +          {
 +          case AF_INET:
 +              valid = ((struct sockaddr_in *)remote)->sin_addr.s_addr == INADDR_LOOPBACK;
 +              break;
 +          case AF_INET6:
 +              inet_pton(AF_INET6, "::ffff:127.0.0.1", &mv4lo);
 +              valid = 0;
 +              if(!memcmp(&((struct sockaddr_in6 *)remote)->sin6_addr, &in6addr_loopback, sizeof(in6addr_loopback)))
 +                  valid = 1;
 +              if(!memcmp(&((struct sockaddr_in6 *)remote)->sin6_addr, &mv4lo, sizeof(in6addr_loopback)))
 +                  valid = 1;
 +              break;
 +          case AF_UNIX:
                valid = 1;
 -          break;
 -      case AF_UNIX:
 -          valid = 1;
 -          break;
 -      default:
 -          valid = 0;
 -          break;
 +              break;
 +          }
 +          free(remote);
        }
        if(!valid)
        {
            sq(sk, 0, L"502", L"Only localhost connections allowed to this host", NULL);
 -          sk->close = 1;
 +          closesock(sk);
            data->close = 1;
            return;
        }
@@@ -465,20 -463,20 +465,20 @@@ static void cmd_login(struct socket *sk
        if(data->uid == -1)
        {
            sq(sk, 0, L"506", L"Authentication error", NULL);
 -          flog(LOG_INFO, "user %ls authenticated successfully from %s, but no account existed", data->username, formataddress(sk->remote, sk->remotelen));
 +          flog(LOG_INFO, "user %ls authenticated successfully from %s, but no account existed", data->username, formatsockpeer(sk));
            logout(data);
        } else if((data->userinfo == NULL) || (data->userinfo->perms & PERM_DISALLOW)) {
            sq(sk, 0, L"506", L"Authentication error", NULL);
 -          flog(LOG_INFO, "user %ls authenticated successfully from %s, but was not authorized", data->username, formataddress(sk->remote, sk->remotelen));
 +          flog(LOG_INFO, "user %ls authenticated successfully from %s, but was not authorized", data->username, formatsockpeer(sk));
            logout(data);
        } else {
            sq(sk, 0, L"200", L"Welcome", NULL);
 -          flog(LOG_INFO, "%ls (UID %i) logged in from %s", data->username, data->uid, formataddress(sk->remote, sk->remotelen));
 +          flog(LOG_INFO, "%ls (UID %i) logged in from %s", data->username, data->uid, formatsockpeer(sk));
        }
        break;
      case AUTH_DENIED:
        sq(sk, 0, L"506", L"Authentication error", L"%ls", (data->auth->text == NULL)?L"":(data->auth->text), NULL);
 -      flog(LOG_INFO, "authentication failed for %ls from %s", data->username, formataddress(sk->remote, sk->remotelen));
 +      flog(LOG_INFO, "authentication failed for %ls from %s", data->username, formatsockpeer(sk));
        logout(data);
        break;
      case AUTH_PASS:
@@@ -539,20 -537,20 +539,20 @@@ static void cmd_pass(struct socket *sk
        if(data->uid == -1)
        {
            sq(sk, 0, L"506", L"Authentication error", NULL);
 -          flog(LOG_INFO, "user %ls authenticated successfully from %s, but no account existed", data->username, formataddress(sk->remote, sk->remotelen));
 +          flog(LOG_INFO, "user %ls authenticated successfully from %s, but no account existed", data->username, formatsockpeer(sk));
            logout(data);
        } else if((data->userinfo == NULL) || (data->userinfo->perms & PERM_DISALLOW)) {
            sq(sk, 0, L"506", L"Authentication error", NULL);
 -          flog(LOG_INFO, "user %ls authenticated successfully from %s, but was not authorized", data->username, formataddress(sk->remote, sk->remotelen));
 +          flog(LOG_INFO, "user %ls authenticated successfully from %s, but was not authorized", data->username, formatsockpeer(sk));
            logout(data);
        } else {
            sq(sk, 0, L"200", L"Welcome", NULL);
 -          flog(LOG_INFO, "%ls (UID %i) logged in from %s", data->username, data->uid, formataddress(sk->remote, sk->remotelen));
 +          flog(LOG_INFO, "%ls (UID %i) logged in from %s", data->username, data->uid, formatsockpeer(sk));
        }
        break;
      case AUTH_DENIED:
        sq(sk, 0, L"506", L"Authentication error", L"%ls", (data->auth->text == NULL)?L"":(data->auth->text), NULL);
 -      flog(LOG_INFO, "authentication failed for %ls from %s", data->username, formataddress(sk->remote, sk->remotelen));
 +      flog(LOG_INFO, "authentication failed for %ls from %s", data->username, formatsockpeer(sk));
        logout(data);
        break;
      case AUTH_PASS:
@@@ -1216,6 -1214,7 +1216,7 @@@ static void cmd_filtercmd(struct socke
      for(pp = cargv; *pp; pp++)
        free(*pp);
      free(cargv);
+     free(filtercmd);
      data->fcmdsk = wrapsock(pipe);
      data->fcmdpid = pid;
      if(data->fcmdbuf != NULL)
@@@ -1823,7 -1822,7 +1824,7 @@@ static void uierror(struct socket *sk, 
      freeuidata(data);
  }
  
 -static void uiaccept(struct socket *sk, struct socket *newsk, void *data)
 +static void uiaccept(struct lport *lp, struct socket *newsk, void *data)
  {
      struct uidata *uidata;
      
@@@ -2237,7 -2236,7 +2238,7 @@@ static struct sockaddr_un *makeunixname
  
  static int tcpportupdate(struct configvar *var, void *uudata)
  {
 -    struct socket *newsock;
 +    struct lport *newsock;
      
      newsock = NULL;
      if((var->val.num != -1) && ((newsock = netcstcplisten(var->val.num, 1, uiaccept, NULL)) == NULL))
      }
      if(tcpsocket != NULL)
      {
 -      putsock(tcpsocket);
 +      closelport(tcpsocket);
        tcpsocket = NULL;
      }
      tcpsocket = newsock;
  
  static int unixsockupdate(struct configvar *var, void *uudata)
  {
 -    struct socket *newsock;
 +    struct lport *newsock;
      struct sockaddr_un *un;
      mode_t ou;
      
      umask(ou);
      if(unixsocket != NULL)
      {
 -      putsock(unixsocket);
 +      closelport(unixsocket);
        unixsocket = NULL;
      }
      unixsocket = newsock;
@@@ -2429,9 -2428,9 +2430,9 @@@ static void terminate(void
      while(users != NULL)
        freeuser(users);
      if(tcpsocket != NULL)
 -      putsock(tcpsocket);
 +      closelport(tcpsocket);
      if(unixsocket != NULL)
 -      putsock(unixsocket);
 +      closelport(unixsocket);
  }
  
  static struct configvar myvars[] =