Only unlink listening Unix sockets.
[doldaconnect.git] / daemon / net.c
index b8289d7..00bc465 100644 (file)
@@ -33,6 +33,7 @@
 #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}},
     {CONF_VAR_END}
 };
@@ -253,18 +277,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);
@@ -462,6 +487,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 **)&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 +573,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 +622,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);
@@ -856,6 +914,8 @@ int socksettos(struct socket *sk, int tos)
 {
     int buf;
     
+    if(sk->family == AF_UNIX)
+       return(0); /* Unix sockets are always perfect. :) */
     if(sk->family == AF_INET)
     {
        switch(tos)
@@ -1036,7 +1096,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);
 }