Allow the usage of the TOS API to set DSCP values on IPv4 sockets.
[doldaconnect.git] / daemon / net.c
index dd630cc..e7c63d1 100644 (file)
@@ -1,6 +1,6 @@
 /*
  *  Dolda Connect - Modular multiuser Direct Connect-style client
- *  Copyright (C) 2004 Fredrik Tolf (fredrik@dolda2000.com)
+ *  Copyright (C) 2004 Fredrik Tolf <fredrik@dolda2000.com>
  *  
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
 #include <sys/ioctl.h>
 #include <sys/socket.h>
 #include <sys/un.h>
-#include <sys/poll.h>
+#include <sys/select.h>
 #include <arpa/inet.h>
 #include <netinet/in.h>
 #include <netdb.h>
 #include <sys/signal.h>
+#include <sys/stat.h>       /* For rebindunix() */
 #ifdef HAVE_LINUX_SOCKIOS_H
 #include <linux/sockios.h>
 #endif
 
 static struct configvar myvars[] =
 {
-    /* 0 = Direct mode, 1 = Passive mode, 2 = SOCKS proxy */
+    /** The network mode to use. Currently supported values are 0 for
+     * active mode and 1 for passive mode. In the future, SOCKS5 proxy
+     * support may be added. */
     {CONF_VAR_INT, "mode", {.num = 0}},
+    /** Set the SO_REUSEADDR socket option on listening sockets, so
+     * that dead TCP connections waiting for timeout are ignored. */
     {CONF_VAR_BOOL, "reuseaddr", {.num = 0}},
-    /* Only for direct mode */
+    /** Overrides the IPv4 address reported to other clients in active
+     * mode. Useful for servers behind NAT routers. If both this and
+     * net.publicif are unspecified the address of the hub connection
+     * is used. */
     {CONF_VAR_IPV4, "visibleipv4", {.ipv4 = {0}}},
+    /** Specifies an interface name from which to fetch the IPv4
+     * address reported to other clients in active mode. If both this
+     * 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). */
     {CONF_VAR_INT, "diffserv-mincost", {.num = 0}},
+    /** The Diffserv value to use on IPv6 connections when the
+     * maximize reliability TOS value is used (see the TOS VALUES
+     * section). */
     {CONF_VAR_INT, "diffserv-maxrel", {.num = 0}},
+    /** The Diffserv value to use on IPv6 connections when the
+     * maximize throughput TOS value is used (see the TOS VALUES
+     * section). */
     {CONF_VAR_INT, "diffserv-maxtp", {.num = 0}},
+    /** The Diffserv value to use on IPv6 connections when the
+     * 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 to bits
+     * to the left (across the ECN bits). This may only work on
+     * Linux. */
+    {CONF_VAR_BOOL, "dscp-tos", {.num = 0}},
     {CONF_VAR_END}
 };
 
@@ -148,6 +175,7 @@ static struct socket *newsock(int type)
     struct socket *new;
     
     new = smalloc(sizeof(*new));
+    memset(new, 0, sizeof(*new));
     new->refcount = 2;
     new->fd = -1;
     new->isrealsocket = 1;
@@ -159,6 +187,8 @@ static struct socket *newsock(int type)
     new->close = 0;
     new->remote = NULL;
     new->remotelen = 0;
+    new->ucred.uid = -1;
+    new->ucred.gid = -1;
     switch(type)
     {
     case SOCK_STREAM:
@@ -253,18 +283,19 @@ void putsock(struct socket *sk)
            {
                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->fd >= 0)
-           close(sk->fd);
+       closesock(sk);
        if(sk->remote != NULL)
            free(sk->remote);
        free(sk);
@@ -321,23 +352,57 @@ void *sockgetinbuf(struct socket *sk, size_t *size)
     return(NULL);
 }
 
+static void recvcmsg(struct socket *sk, struct msghdr *msg)
+{
+    struct cmsghdr *cmsg;
+    
+    for(cmsg = CMSG_FIRSTHDR(msg); cmsg != NULL; cmsg = CMSG_NXTHDR(msg, cmsg))
+    {
+#if UNIX_AUTH_STYLE == 1
+       if((cmsg->cmsg_level == SOL_SOCKET) && (cmsg->cmsg_type == SCM_CREDENTIALS))
+       {
+           struct ucred *cred;
+           if(sk->ucred.uid == -1)
+           {
+               cred = (struct ucred *)CMSG_DATA(cmsg);
+               sk->ucred.uid = cred->uid;
+               sk->ucred.gid = cred->gid;
+           }
+       }
+#endif
+    }
+}
+
 static void sockrecv(struct socket *sk)
 {
     int ret, inq;
     struct dgrambuf *dbuf;
+    struct msghdr msg;
+    char cbuf[65536];
+    struct iovec bufvec;
     
+    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(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;
+       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;
@@ -345,7 +410,16 @@ static void sockrecv(struct socket *sk)
        if(inq > 65536)
            inq = 65536;
        sizebuf(&sk->inbuf.s.buf, &sk->inbuf.s.bufsize, sk->inbuf.s.datasize + inq, 1, 1);
-       ret = read(sk->fd, sk->inbuf.s.buf + sk->inbuf.s.datasize, inq);
+       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))
@@ -355,6 +429,10 @@ static void sockrecv(struct socket *sk)
            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)
@@ -367,6 +445,7 @@ static void sockrecv(struct socket *sk)
            sk->readcb(sk, sk->data);
        break;
     case SOCK_DGRAM:
+#if defined(HAVE_LINUX_SOCKIOS_H) && defined(SIOCINQ)
        if(ioctl(sk->fd, SIOCINQ, &inq))
        {
            /* I don't really know what could go wrong here, so let's
@@ -374,10 +453,21 @@ static void sockrecv(struct socket *sk)
            flog(LOG_WARNING, "SIOCINQ return %s on socket %i", strerror(errno), sk->fd);
            return;
        }
+#else
+       inq = 65536;
+#endif
        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);
+       dbuf->addrlen = msg.msg_namelen;
        if(ret < 0)
        {
            free(dbuf->addr);
@@ -390,6 +480,10 @@ static void sockrecv(struct socket *sk)
            closesock(sk);
            return;
        }
+       if(msg.msg_flags & MSG_CTRUNC)
+           flog(LOG_DEBUG, "ancillary data was truncated");
+       else
+           recvcmsg(sk, &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
@@ -435,8 +529,12 @@ static void sockflush(struct socket *sk)
            ret = write(sk->fd, sk->outbuf.s.buf, sk->outbuf.s.datasize);
        if(ret < 0)
        {
-           /* For now, assume transient error, since
-            * the socket is polled for errors */
+           if((errno != EINTR) && (errno != EAGAIN))
+           {
+               if(sk->errcb != NULL)
+                   sk->errcb(sk, errno, sk->data);
+               closesock(sk);
+           }
            break;
        }
        if(ret > 0)
@@ -462,6 +560,16 @@ static void sockflush(struct socket *sk)
 
 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;
@@ -538,6 +646,29 @@ size_t sockqueuesize(struct socket *sk)
 }
 
 /*
+ * 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)
+{
+    struct sockaddr_un *un;
+    struct stat sb;
+    
+    if((sk->family != AF_UNIX) || (name->sa_family != PF_UNIX))
+       return(-1);
+    un = (struct sockaddr_un *)name;
+    if(stat(un->sun_path, &sb))
+       return(-1);
+    if(!S_ISSOCK(sb.st_mode))
+       return(-1);
+    if(unlink(un->sun_path))
+       return(-1);
+    if(bind(sk->fd, name, namelen) < 0)
+       return(-1);
+    return(0);
+}
+
+/*
  * The difference between netcslisten() and netcslistenlocal() is that
  * netcslistenlocal() always listens on the local host, instead of
  * following proxy/passive mode directions. It is suitable for eg. the
@@ -564,7 +695,7 @@ struct socket *netcslistenlocal(int type, struct sockaddr *name, socklen_t namel
        intbuf = 1;
        setsockopt(sk->fd, SOL_SOCKET, SO_REUSEADDR, &intbuf, sizeof(intbuf));
     }
-    if(bind(sk->fd, name, namelen) < 0)
+    if((bind(sk->fd, name, namelen) < 0) && ((errno != EADDRINUSE) || (rebindunix(sk, name, namelen) < 0)))
     {
        putsock(sk);
        return(NULL);
@@ -700,63 +831,75 @@ struct socket *netcsconn(struct sockaddr *addr, socklen_t addrlen, void (*func)(
     return(NULL);
 }
 
+static void acceptunix(struct socket *sk)
+{
+    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));
+#elif UNIX_AUTH_STYLE == 2
+    if(getpeereid(sk->fd, &sk->ucred.uid, &sk->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;
+    }
+#endif
+}
+
 int pollsocks(int timeout)
 {
-    int i, num, ret;
+    int ret, fd;
     socklen_t retlen;
-    int newfd;
-    struct pollfd *pfds;
+    int newfd, maxfd;
+    fd_set rfds, wfds, efds;
     struct socket *sk, *next, *newsk;
     struct sockaddr_storage ss;
     socklen_t sslen;
+    struct timeval tv;
     
-    pfds = smalloc(sizeof(*pfds) * (num = numsocks));
-    for(i = 0, sk = sockets; i < num; sk = sk->next)
+    FD_ZERO(&rfds);
+    FD_ZERO(&wfds);
+    FD_ZERO(&efds);
+    for(maxfd = 0, sk = sockets; sk != NULL; sk = sk->next)
     {
-       if(sk->state == SOCK_STL)
-       {
-           num--;
+       if((sk->state == SOCK_STL) || (sk->fd < 0))
            continue;
-       }
-       pfds[i].fd = sk->fd;
-       pfds[i].events = 0;
        if(!sk->ignread)
-           pfds[i].events |= POLLIN;
+           FD_SET(sk->fd, &rfds);
        if((sk->state == SOCK_SYN) || (sockqueuesize(sk) > 0))
-           pfds[i].events |= POLLOUT;
-       pfds[i].revents = 0;
-       i++;
+           FD_SET(sk->fd, &wfds);
+       FD_SET(sk->fd, &efds);
+       if(sk->fd > maxfd)
+           maxfd = sk->fd;
     }
-    ret = poll(pfds, num, timeout);
+    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)
        {
-           flog(LOG_CRIT, "pollsocks: poll errored out: %s", strerror(errno));
+           flog(LOG_CRIT, "pollsocks: select errored out: %s", strerror(errno));
            /* To avoid CPU hogging in case it's bad, which it
             * probably is. */
            sleep(1);
        }
-       free(pfds);
        return(1);
     }
     for(sk = sockets; sk != NULL; sk = next)
     {
        next = sk->next;
-       for(i = 0; i < num; i++)
-       {
-           if(pfds[i].fd == sk->fd)
-               break;
-       }
-       if(i == num)
-           continue;
+       fd = sk->fd;
        switch(sk->state)
        {
        case SOCK_LST:
-           if(pfds[i].revents & POLLIN)
+           if(FD_ISSET(fd, &rfds))
            {
                sslen = sizeof(ss);
-               if((newfd = accept(sk->fd, (struct sockaddr *)&ss, &sslen)) < 0)
+               if((newfd = accept(fd, (struct sockaddr *)&ss, &sslen)) < 0)
                {
                    if(sk->errcb != NULL)
                        sk->errcb(sk, errno, sk->data);
@@ -767,30 +910,32 @@ int pollsocks(int timeout)
                newsk->state = SOCK_EST;
                memcpy(newsk->remote = smalloc(sslen), &ss, sslen);
                newsk->remotelen = sslen;
-               putsock(newsk);
+               if(ss.ss_family == PF_UNIX)
+                   acceptunix(newsk);
                if(sk->acceptcb != NULL)
                    sk->acceptcb(sk, newsk, sk->data);
+               putsock(newsk);
            }
-           if(pfds[i].revents & POLLERR)
+           if(FD_ISSET(fd, &efds))
            {
                retlen = sizeof(ret);
-               getsockopt(sk->fd, SOL_SOCKET, SO_ERROR, &ret, &retlen);
+               getsockopt(fd, SOL_SOCKET, SO_ERROR, &ret, &retlen);
                if(sk->errcb != NULL)
                    sk->errcb(sk, ret, sk->data);
                continue;
            }
            break;
        case SOCK_SYN:
-           if(pfds[i].revents & POLLERR)
+           if(FD_ISSET(fd, &efds))
            {
                retlen = sizeof(ret);
-               getsockopt(sk->fd, SOL_SOCKET, SO_ERROR, &ret, &retlen);
+               getsockopt(fd, SOL_SOCKET, SO_ERROR, &ret, &retlen);
                if(sk->conncb != NULL)
                    sk->conncb(sk, ret, sk->data);
                closesock(sk);
                continue;
            }
-           if(pfds[i].revents & (POLLIN | POLLOUT))
+           if(FD_ISSET(fd, &rfds) || FD_ISSET(fd, &wfds))
            {
                sk->state = SOCK_EST;
                if(sk->conncb != NULL)
@@ -798,41 +943,25 @@ int pollsocks(int timeout)
            }
            break;
        case SOCK_EST:
-           if(pfds[i].revents & POLLERR)
+           if(FD_ISSET(fd, &efds))
            {
                retlen = sizeof(ret);
-               getsockopt(sk->fd, SOL_SOCKET, SO_ERROR, &ret, &retlen);
+               getsockopt(fd, SOL_SOCKET, SO_ERROR, &ret, &retlen);
                if(sk->errcb != NULL)
                    sk->errcb(sk, ret, sk->data);
                closesock(sk);
                continue;
            }
-           if(pfds[i].revents & POLLIN)
+           if(FD_ISSET(fd, &rfds))
                sockrecv(sk);
-           if(pfds[i].revents & POLLOUT)
+           if(FD_ISSET(fd, &wfds))
            {
                if(sockqueuesize(sk) > 0)
                    sockflush(sk);
            }
            break;
        }
-       if(pfds[i].revents & POLLNVAL)
-       {
-           flog(LOG_CRIT, "BUG: stale socket struct on fd %i", sk->fd);
-           sk->state = SOCK_STL;
-           unlinksock(sk);
-           continue;
-       }
-       if(pfds[i].revents & POLLHUP)
-       {
-           if(sk->errcb != NULL)
-               sk->errcb(sk, 0, sk->data);
-           closesock(sk);
-           unlinksock(sk);
-           continue;
-       }
     }
-    free(pfds);
     for(sk = sockets; sk != NULL; sk = next)
     {
        next = sk->next;
@@ -855,31 +984,47 @@ int pollsocks(int timeout)
 int socksettos(struct socket *sk, int tos)
 {
     int buf;
+    int dscp2tos;
     
+    if(sk->family == AF_UNIX)
+       return(0); /* Unix sockets are always perfect. :) */
     if(sk->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, SOL_IP, IP_TOS, &buf, sizeof(buf)) < 0)
+       if(setsockopt(sk->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);
@@ -943,7 +1088,7 @@ static void resolvecb(pid_t pid, int status, struct resolvedata *data)
     {
        if((ret = read(data->fd, buf, sizeof(buf))) != 4)
        {
-           errno = ENONET;
+           errno = ENOENT;
            data->callback(NULL, 0, data->data);
        } else {
            ipv4 = (struct sockaddr_in *)&data->addr;
@@ -951,7 +1096,7 @@ static void resolvecb(pid_t pid, int status, struct resolvedata *data)
            data->callback((struct sockaddr *)ipv4, sizeof(*ipv4), data->data);
        }
     } else {
-       errno = ENONET;
+       errno = ENOENT;
        data->callback(NULL, 0, data->data);
     }
     close(data->fd);
@@ -1036,7 +1181,8 @@ int sockgetlocalname(struct socket *sk, struct sockaddr **namebuf, socklen_t *le
        return(-1);
     }
     *namebuf = memcpy(smalloc(len), &name, len);
-    *lenbuf = len;
+    if(lenbuf != NULL)
+       *lenbuf = len;
     return(0);
 }
 
@@ -1112,6 +1258,29 @@ int sockgetremotename(struct socket *sk, struct sockaddr **namebuf, socklen_t *l
     }
 }
 
+int sockgetremotename2(struct socket *sk, struct socket *sk2, struct sockaddr **namebuf, socklen_t *lenbuf)
+{
+    struct sockaddr *name1, *name2;
+    socklen_t len1, len2;
+    
+    if(sk->family != sk2->family)
+    {
+       flog(LOG_ERR, "using sockgetremotename2 with sockets of differing family: %i %i", sk->family, sk2->family);
+       return(-1);
+    }
+    if(sockgetremotename(sk, &name1, &len1))
+       return(-1);
+    if(sockgetremotename(sk2, &name2, &len2)) {
+       free(name1);
+       return(-1);
+    }
+    sethostaddr(name1, name2);
+    free(name2);
+    *namebuf = name1;
+    *lenbuf = len1;
+    return(0);
+}
+
 int addreq(struct sockaddr *x, struct sockaddr *y)
 {
     struct sockaddr_un *u1, *u2;
@@ -1135,6 +1304,7 @@ int addreq(struct sockaddr *x, struct sockaddr *y)
        if(n1->sin_addr.s_addr != n2->sin_addr.s_addr)
            return(0);
        break;
+#ifdef HAVE_IPV6
     case AF_INET6:
        s1 = (struct sockaddr_in6 *)x; s2 = (struct sockaddr_in6 *)y;
        if(s1->sin6_port != s2->sin6_port)
@@ -1142,15 +1312,13 @@ int addreq(struct sockaddr *x, struct sockaddr *y)
        if(memcmp(s1->sin6_addr.s6_addr, s2->sin6_addr.s6_addr, sizeof(s1->sin6_addr.s6_addr)))
            return(0);
        break;
+#endif
     }
     return(1);
 }
 
 char *formataddress(struct sockaddr *arg, socklen_t arglen)
 {
-    struct sockaddr_un *UNIX; /* Some wise guy has #defined unix with
-                              * lowercase letters to 1, so I do this
-                              * instead. */
     struct sockaddr_in *ipv4;
 #ifdef HAVE_IPV6
     struct sockaddr_in6 *ipv6;
@@ -1164,8 +1332,7 @@ char *formataddress(struct sockaddr *arg, socklen_t arglen)
     switch(arg->sa_family)
     {
     case AF_UNIX:
-       UNIX = (struct sockaddr_un *)arg;
-       ret = sprintf2("%s", UNIX->sun_path);
+       ret = sstrdup("Unix socket");
        break;
     case AF_INET:
        ipv4 = (struct sockaddr_in *)arg;