SO_PROTOCOL is platform-dependent
[sfrench/samba-autobuild/.git] / lib / socket_wrapper / socket_wrapper.c
index 7275e73afef1b50cbb0c658bdf0308e8ea4b33f5..903eec21537429fc69ea4979094888cf71c244a0 100644 (file)
@@ -76,6 +76,9 @@
 #ifdef HAVE_GNU_LIB_NAMES_H
 #include <gnu/lib-names.h>
 #endif
+#ifdef HAVE_RPC_RPC_H
+#include <rpc/rpc.h>
+#endif
 
 enum swrap_dbglvl_e {
        SWRAP_LOG_ERROR = 0,
@@ -111,6 +114,13 @@ enum swrap_dbglvl_e {
 #define ZERO_STRUCT(x) memset((char *)&(x), 0, sizeof(x))
 #endif
 
+#ifndef ZERO_STRUCTP
+#define ZERO_STRUCTP(x) do { \
+               if ((x) != NULL) \
+                       memset((char *)(x), 0, sizeof(*(x))); \
+       } while(0)
+#endif
+
 #ifndef discard_const
 #define discard_const(ptr) ((void *)((uintptr_t)(ptr)))
 #endif
@@ -843,6 +853,7 @@ static const char *socket_wrapper_dir(void)
        if (s == NULL) {
                return NULL;
        }
+       /* TODO use realpath(3) here, when we add support for threads */
        if (strncmp(s, "./", 2) == 0) {
                s += 2;
        }
@@ -1256,6 +1267,90 @@ static struct socket_info *find_socket_info(int fd)
        return NULL;
 }
 
+#if 0 /* FIXME */
+static bool check_addr_port_in_use(const struct sockaddr *sa, socklen_t len)
+{
+       struct socket_info *s;
+
+       /* first catch invalid input */
+       switch (sa->sa_family) {
+       case AF_INET:
+               if (len < sizeof(struct sockaddr_in)) {
+                       return false;
+               }
+               break;
+#if HAVE_IPV6
+       case AF_INET6:
+               if (len < sizeof(struct sockaddr_in6)) {
+                       return false;
+               }
+               break;
+#endif
+       default:
+               return false;
+               break;
+       }
+
+       for (s = sockets; s != NULL; s = s->next) {
+               if (s->myname == NULL) {
+                       continue;
+               }
+               if (s->myname->sa_family != sa->sa_family) {
+                       continue;
+               }
+               switch (s->myname->sa_family) {
+               case AF_INET: {
+                       struct sockaddr_in *sin1, *sin2;
+
+                       sin1 = (struct sockaddr_in *)s->myname;
+                       sin2 = (struct sockaddr_in *)sa;
+
+                       if (sin1->sin_addr.s_addr == htonl(INADDR_ANY)) {
+                               continue;
+                       }
+                       if (sin1->sin_port != sin2->sin_port) {
+                               continue;
+                       }
+                       if (sin1->sin_addr.s_addr != sin2->sin_addr.s_addr) {
+                               continue;
+                       }
+
+                       /* found */
+                       return true;
+                       break;
+               }
+#if HAVE_IPV6
+               case AF_INET6: {
+                       struct sockaddr_in6 *sin1, *sin2;
+
+                       sin1 = (struct sockaddr_in6 *)s->myname;
+                       sin2 = (struct sockaddr_in6 *)sa;
+
+                       if (sin1->sin6_port != sin2->sin6_port) {
+                               continue;
+                       }
+                       if (!IN6_ARE_ADDR_EQUAL(&sin1->sin6_addr,
+                                               &sin2->sin6_addr))
+                       {
+                               continue;
+                       }
+
+                       /* found */
+                       return true;
+                       break;
+               }
+#endif
+               default:
+                       continue;
+                       break;
+
+               }
+       }
+
+       return false;
+}
+#endif
+
 static void swrap_remove_stale(int fd)
 {
        struct socket_info *si = find_socket_info(fd);
@@ -1298,6 +1393,26 @@ static int sockaddr_convert_to_un(struct socket_info *si,
 #endif
 
        switch (in_addr->sa_family) {
+       case AF_UNSPEC: {
+               const struct sockaddr_in *sin;
+               if (si->family != AF_INET) {
+                       break;
+               }
+               if (in_len < sizeof(struct sockaddr_in)) {
+                       break;
+               }
+               sin = (const struct sockaddr_in *)in_addr;
+               if(sin->sin_addr.s_addr != htonl(INADDR_ANY)) {
+                       break;
+               }
+
+               /*
+                * Note: in the special case of AF_UNSPEC and INADDR_ANY,
+                * AF_UNSPEC is mapped to AF_INET and must be treated here.
+                */
+
+               /* FALL THROUGH */
+       }
        case AF_INET:
 #ifdef HAVE_IPV6
        case AF_INET6:
@@ -2223,8 +2338,40 @@ static int swrap_socket(int family, int type, int protocol)
        si->type = real_type;
        si->protocol = protocol;
 
+       /*
+        * Setup myname so getsockname() can succeed to find out the socket
+        * type.
+        */
+       switch(si->family) {
+       case AF_INET: {
+               struct sockaddr_in sin = {
+                       .sin_family = AF_INET,
+               };
+
+               si->myname_len = sizeof(struct sockaddr_in);
+               si->myname = sockaddr_dup(&sin, si->myname_len);
+               break;
+       }
+       case AF_INET6: {
+               struct sockaddr_in6 sin6 = {
+                       .sin6_family = AF_INET6,
+               };
+
+               si->myname_len = sizeof(struct sockaddr_in6);
+               si->myname = sockaddr_dup(&sin6, si->myname_len);
+               break;
+       }
+       default:
+               free(si);
+               errno = EINVAL;
+               return -1;
+       }
+
        fi = (struct socket_info_fd *)calloc(1, sizeof(struct socket_info_fd));
        if (fi == NULL) {
+               if (si->myname != NULL) {
+                       free (si->myname);
+               }
                free(si);
                errno = ENOMEM;
                return -1;
@@ -2503,6 +2650,7 @@ static int swrap_auto_bind(int fd, struct socket_info *si, int family)
                in.sin_addr.s_addr = htonl(127<<24 | 
                                           socket_wrapper_default_iface());
 
+               free(si->myname);
                si->myname_len = sizeof(in);
                si->myname = sockaddr_dup(&in, si->myname_len);
                break;
@@ -2532,6 +2680,7 @@ static int swrap_auto_bind(int fd, struct socket_info *si, int family)
                in6.sin6_family = AF_INET6;
                in6.sin6_addr = *swrap_ipv6();
                in6.sin6_addr.s6_addr[15] = socket_wrapper_default_iface();
+               free(si->myname);
                si->myname_len = sizeof(in6);
                si->myname = sockaddr_dup(&in6, si->myname_len);
                break;
@@ -2680,11 +2829,74 @@ static int swrap_bind(int s, const struct sockaddr *myaddr, socklen_t addrlen)
        int ret;
        struct sockaddr_un un_addr;
        struct socket_info *si = find_socket_info(s);
+       int bind_error = 0;
+#if 0 /* FIXME */
+       bool in_use;
+#endif
 
        if (!si) {
                return libc_bind(s, myaddr, addrlen);
        }
 
+       switch (si->family) {
+       case AF_INET: {
+               const struct sockaddr_in *sin;
+               if (addrlen < sizeof(struct sockaddr_in)) {
+                       bind_error = EINVAL;
+                       break;
+               }
+
+               sin = (const struct sockaddr_in *)myaddr;
+
+               if (sin->sin_family != AF_INET) {
+                       bind_error = EAFNOSUPPORT;
+               }
+
+               /* special case for AF_UNSPEC */
+               if (sin->sin_family == AF_UNSPEC &&
+                   (sin->sin_addr.s_addr == htonl(INADDR_ANY)))
+               {
+                       bind_error = 0;
+               }
+
+               break;
+       }
+#ifdef HAVE_IPV6
+       case AF_INET6: {
+               const struct sockaddr_in6 *sin6;
+               if (addrlen < sizeof(struct sockaddr_in6)) {
+                       bind_error = EINVAL;
+                       break;
+               }
+
+               sin6 = (const struct sockaddr_in6 *)myaddr;
+
+               if (sin6->sin6_family != AF_INET6) {
+                       bind_error = EAFNOSUPPORT;
+               }
+
+               break;
+       }
+#endif
+       default:
+               bind_error = EINVAL;
+               break;
+       }
+
+       if (bind_error != 0) {
+               errno = bind_error;
+               return -1;
+       }
+
+#if 0 /* FIXME */
+       in_use = check_addr_port_in_use(myaddr, addrlen);
+       if (in_use) {
+               errno = EADDRINUSE;
+               return -1;
+       }
+#endif
+
+       free(si->myname);
        si->myname_len = addrlen;
        si->myname = sockaddr_dup(myaddr, addrlen);
 
@@ -2712,6 +2924,86 @@ int bind(int s, const struct sockaddr *myaddr, socklen_t addrlen)
        return swrap_bind(s, myaddr, addrlen);
 }
 
+/****************************************************************************
+ *   BINDRESVPORT
+ ***************************************************************************/
+
+#ifdef HAVE_BINDRESVPORT
+static int swrap_getsockname(int s, struct sockaddr *name, socklen_t *addrlen);
+
+static int swrap_bindresvport_sa(int sd, struct sockaddr *sa)
+{
+       struct sockaddr_storage myaddr;
+       socklen_t salen;
+       static uint16_t port;
+       uint16_t i;
+       int rc = -1;
+       int af;
+
+#define SWRAP_STARTPORT 600
+#define SWRAP_ENDPORT (IPPORT_RESERVED - 1)
+#define SWRAP_NPORTS (SWRAP_ENDPORT - SWRAP_STARTPORT + 1)
+
+       if (port == 0) {
+               port = (getpid() % SWRAP_NPORTS) + SWRAP_STARTPORT;
+       }
+
+       if (sa == NULL) {
+               salen = sizeof(struct sockaddr);
+               sa = (struct sockaddr *)&myaddr;
+
+               rc = swrap_getsockname(sd, (struct sockaddr *)&myaddr, &salen);
+               if (rc < 0) {
+                       return -1;
+               }
+
+               af = sa->sa_family;
+               memset(&myaddr, 0, salen);
+       } else {
+               af = sa->sa_family;
+       }
+
+       for (i = 0; i < SWRAP_NPORTS; i++, port++) {
+               switch(af) {
+               case AF_INET: {
+                       struct sockaddr_in *sinp = (struct sockaddr_in *)sa;
+
+                       salen = sizeof(struct sockaddr_in);
+                       sinp->sin_port = htons(port);
+                       break;
+               }
+               case AF_INET6: {
+                       struct sockaddr_in6 *sin6p = (struct sockaddr_in6 *)sa;
+
+                       salen = sizeof(struct sockaddr_in6);
+                       sin6p->sin6_port = htons(port);
+                       break;
+               }
+               default:
+                       errno = EAFNOSUPPORT;
+                       return -1;
+               }
+               sa->sa_family = af;
+
+               if (port > SWRAP_ENDPORT) {
+                       port = SWRAP_STARTPORT;
+               }
+
+               rc = swrap_bind(sd, (struct sockaddr *)sa, salen);
+               if (rc == 0 || errno != EADDRINUSE) {
+                       break;
+               }
+       }
+
+       return rc;
+}
+
+int bindresvport(int sockfd, struct sockaddr_in *sinp)
+{
+       return swrap_bindresvport_sa(sockfd, (struct sockaddr *)sinp);
+}
+#endif
+
 /****************************************************************************
  *   LISTEN
  ***************************************************************************/
@@ -2877,6 +3169,8 @@ static int swrap_getsockopt(int s, int level, int optname,
                        *(int *)optval = si->family;
                        return 0;
 #endif /* SO_DOMAIN */
+
+#ifdef SO_PROTOCOL
                case SO_PROTOCOL:
                        if (optval == NULL || optlen == NULL ||
                            *optlen < (socklen_t)sizeof(int)) {
@@ -2887,6 +3181,7 @@ static int swrap_getsockopt(int s, int level, int optname,
                        *optlen = sizeof(int);
                        *(int *)optval = si->protocol;
                        return 0;
+#endif /* SO_PROTOCOL */
                case SO_TYPE:
                        if (optval == NULL || optlen == NULL ||
                            *optlen < (socklen_t)sizeof(int)) {
@@ -3115,8 +3410,7 @@ static int swrap_msghdr_add_pktinfo(struct socket_info *si,
 {
        /* Add packet info */
        switch (si->pktinfo) {
-#if defined(IP_PKTINFO)
-/* && (defined(HAVE_STRUCT_IN_PKTINFO) || defined(IP_RECVDSTADDR)) */
+#if defined(IP_PKTINFO) && (defined(HAVE_STRUCT_IN_PKTINFO) || defined(IP_RECVDSTADDR))
        case AF_INET: {
                struct sockaddr_in *sin;
 #if defined(HAVE_STRUCT_IN_PKTINFO)
@@ -3441,7 +3735,7 @@ static ssize_t swrap_sendmsg_before(int fd,
                if (cmlen == 0) {
                        msg->msg_controllen = 0;
                        msg->msg_control = NULL;
-               } else if (cmlen < msg->msg_controllen) {
+               } else if (cmlen < msg->msg_controllen && cmbuf != NULL) {
                        memcpy(msg->msg_control, cmbuf, cmlen);
                        msg->msg_controllen = cmlen;
                }