Add more resilient TCP listening function.
[doldaconnect.git] / daemon / net.c
index 866e136..0f7be54 100644 (file)
@@ -544,50 +544,6 @@ size_t sockqueuesize(struct socket *sk)
     return(ret);
 }
 
-struct socket *netcslisten(int type, struct sockaddr *name, socklen_t namelen, void (*func)(struct socket *, struct socket *, void *), void *data)
-{
-    struct socket *sk;
-    int intbuf;
-    
-    if(confgetint("net", "mode") == 1)
-    {
-       errno = EOPNOTSUPP;
-       return(NULL);
-    }
-    /* I don't know if this is actually correct (it probably isn't),
-     * but since, at on least Linux systems, PF_* are specifically
-     * #define'd to their AF_* counterparts, it allows for a severely
-     * smoother implementation. If it breaks something on your
-     * platform, please tell me so.
-     */
-    if(confgetint("net", "mode") == 0)
-    {
-       if((sk = mksock(name->sa_family, type)) == NULL)
-           return(NULL);
-       sk->state = SOCK_LST;
-       if(confgetint("net", "reuseaddr"))
-       {
-           intbuf = 1;
-           setsockopt(sk->fd, SOL_SOCKET, SO_REUSEADDR, &intbuf, sizeof(intbuf));
-       }
-       if(bind(sk->fd, name, namelen) < 0)
-       {
-           putsock(sk);
-           return(NULL);
-       }
-       if(listen(sk->fd, 16) < 0)
-       {
-           putsock(sk);
-           return(NULL);
-       }
-       sk->acceptcb = func;
-       sk->data = data;
-       return(sk);
-    }
-    errno = EOPNOTSUPP;
-    return(NULL);
-}
-
 /*
  * The difference between netcslisten() and netcslistenlocal() is that
  * netcslistenlocal() always listens on the local host, instead of
@@ -630,6 +586,54 @@ struct socket *netcslistenlocal(int type, struct sockaddr *name, socklen_t namel
     return(sk);
 }
 
+struct socket *netcslisten(int type, struct sockaddr *name, socklen_t namelen, void (*func)(struct socket *, struct socket *, void *), void *data)
+{
+    if(confgetint("net", "mode") == 1)
+    {
+       errno = EOPNOTSUPP;
+       return(NULL);
+    }
+    /* I don't know if this is actually correct (it probably isn't),
+     * but since, at on least Linux systems, PF_* are specifically
+     * #define'd to their AF_* counterparts, it allows for a severely
+     * smoother implementation. If it breaks something on your
+     * platform, please tell me so.
+     */
+    if(confgetint("net", "mode") == 0)
+       return(netcslistenlocal(type, name, namelen, func, data));
+    errno = EOPNOTSUPP;
+    return(NULL);
+}
+
+struct socket *netcstcplisten(int port, int local, void (*func)(struct socket *, 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;
+    
+    if(local)
+       csfunc = netcslistenlocal;
+    else
+       csfunc = netcslisten;
+#ifdef HAVE_IPV6
+    memset(&addr6, 0, sizeof(addr6));
+    addr6.sin6_family = AF_INET6;
+    addr6.sin6_port = htons(port);
+    addr6.sin6_addr = in6addr_any;
+    if((ret = csfunc(SOCK_STREAM, (struct sockaddr *)&addr6, sizeof(addr6), func, data)) != NULL)
+       return(ret);
+    if((ret == NULL) && (errno != EAFNOSUPPORT))
+       return(NULL);
+#endif
+    memset(&addr, 0, sizeof(addr));
+    addr.sin_family = AF_INET;
+    addr.sin_port = htons(port);
+    return(csfunc(SOCK_STREAM, (struct sockaddr *)&addr, sizeof(addr), func, data));
+}
+
 struct socket *netcsdgram(struct sockaddr *name, socklen_t namelen)
 {
     struct socket *sk;
@@ -1092,6 +1096,40 @@ int sockgetremotename(struct socket *sk, struct sockaddr **namebuf, socklen_t *l
     }
 }
 
+int addreq(struct sockaddr *x, struct sockaddr *y)
+{
+    struct sockaddr_un *u1, *u2;
+    struct sockaddr_in *n1, *n2;
+#ifdef HAVE_IPV6
+    struct sockaddr_in6 *s1, *s2;
+#endif
+    
+    if(x->sa_family != y->sa_family)
+       return(0);
+    switch(x->sa_family) {
+    case AF_UNIX:
+       u1 = (struct sockaddr_un *)x; u2 = (struct sockaddr_un *)y;
+       if(strncmp(u1->sun_path, u2->sun_path, sizeof(u1->sun_path)))
+           return(0);
+       break;
+    case AF_INET:
+       n1 = (struct sockaddr_in *)x; n2 = (struct sockaddr_in *)y;
+       if(n1->sin_port != n2->sin_port)
+           return(0);
+       if(n1->sin_addr.s_addr != n2->sin_addr.s_addr)
+           return(0);
+       break;
+    case AF_INET6:
+       s1 = (struct sockaddr_in6 *)x; s2 = (struct sockaddr_in6 *)y;
+       if(s1->sin6_port != s2->sin6_port)
+           return(0);
+       if(memcmp(s1->sin6_addr.s6_addr, s2->sin6_addr.s6_addr, sizeof(s1->sin6_addr.s6_addr)))
+           return(0);
+       break;
+    }
+    return(1);
+}
+
 char *formataddress(struct sockaddr *arg, socklen_t arglen)
 {
     struct sockaddr_un *UNIX; /* Some wise guy has #defined unix with