docs: fix a typo in history file
[bbaumbach/samba-autobuild/.git] / source4 / lib / socket / socket_ip.c
index cdb75fe0218d945714f520ebc8f505126db6b4bf..62dbf1d2213431fbbbe63b13d8bff033a214c718 100644 (file)
 #include "system/filesys.h"
 #include "lib/socket/socket.h"
 #include "system/network.h"
+#include "lib/util/util_net.h"
+
+#undef strcasecmp
+
+_PUBLIC_ const struct socket_ops *socket_ipv4_ops(enum socket_type type);
+_PUBLIC_ const struct socket_ops *socket_ipv6_ops(enum socket_type type);
 
 static NTSTATUS ipv4_init(struct socket_context *sock)
 {
@@ -43,9 +49,11 @@ static NTSTATUS ipv4_init(struct socket_context *sock)
 
        sock->fd = socket(PF_INET, type, 0);
        if (sock->fd == -1) {
-               return map_nt_error_from_unix(errno);
+               return map_nt_error_from_unix_common(errno);
        }
 
+       smb_set_close_on_exec(sock->fd);
+
        sock->backend_name = "ipv4";
        sock->family = AF_INET;
 
@@ -54,7 +62,10 @@ static NTSTATUS ipv4_init(struct socket_context *sock)
 
 static void ip_close(struct socket_context *sock)
 {
-       close(sock->fd);
+       if (sock->fd != -1) {
+               close(sock->fd);
+               sock->fd = -1;
+       }
 }
 
 static NTSTATUS ip_connect_complete(struct socket_context *sock, uint32_t flags)
@@ -66,17 +77,15 @@ static NTSTATUS ip_connect_complete(struct socket_context *sock, uint32_t flags)
           for non-blocking connect */
        ret = getsockopt(sock->fd, SOL_SOCKET, SO_ERROR, &error, &len);
        if (ret == -1) {
-               return map_nt_error_from_unix(errno);
+               return map_nt_error_from_unix_common(errno);
        }
        if (error != 0) {
-               return map_nt_error_from_unix(error);
+               return map_nt_error_from_unix_common(error);
        }
 
-       if (!(flags & SOCKET_FLAG_BLOCK)) {
-               ret = set_blocking(sock->fd, false);
-               if (ret == -1) {
-                       return map_nt_error_from_unix(errno);
-               }
+       ret = set_blocking(sock->fd, false);
+       if (ret == -1) {
+               return map_nt_error_from_unix_common(errno);
        }
 
        sock->state = SOCKET_STATE_CLIENT_CONNECTED;
@@ -98,7 +107,7 @@ static NTSTATUS ipv4_connect(struct socket_context *sock,
        if (my_address && my_address->sockaddr) {
                ret = bind(sock->fd, my_address->sockaddr, my_address->sockaddrlen);
                if (ret == -1) {
-                       return map_nt_error_from_unix(errno);
+                       return map_nt_error_from_unix_common(errno);
                }
        } else if (my_address) {
                my_ip = interpret_addr2(my_address->addr);
@@ -115,7 +124,7 @@ static NTSTATUS ipv4_connect(struct socket_context *sock,
                        
                        ret = bind(sock->fd, (struct sockaddr *)&my_addr, sizeof(my_addr));
                        if (ret == -1) {
-                               return map_nt_error_from_unix(errno);
+                               return map_nt_error_from_unix_common(errno);
                        }
                }
        }
@@ -123,7 +132,7 @@ static NTSTATUS ipv4_connect(struct socket_context *sock,
        if (srv_address->sockaddr) {
                ret = connect(sock->fd, srv_address->sockaddr, srv_address->sockaddrlen);
                if (ret == -1) {
-                       return map_nt_error_from_unix(errno);
+                       return map_nt_error_from_unix_common(errno);
                }
        } else {
                srv_ip = interpret_addr2(srv_address->addr);
@@ -143,7 +152,7 @@ static NTSTATUS ipv4_connect(struct socket_context *sock,
 
                ret = connect(sock->fd, (const struct sockaddr *)&srv_addr, sizeof(srv_addr));
                if (ret == -1) {
-                       return map_nt_error_from_unix(errno);
+                       return map_nt_error_from_unix_common(errno);
                }
        }
 
@@ -182,21 +191,19 @@ static NTSTATUS ipv4_listen(struct socket_context *sock,
        }
 
        if (ret == -1) {
-               return map_nt_error_from_unix(errno);
+               return map_nt_error_from_unix_common(errno);
        }
 
        if (sock->type == SOCKET_TYPE_STREAM) {
                ret = listen(sock->fd, queue_size);
                if (ret == -1) {
-                       return map_nt_error_from_unix(errno);
+                       return map_nt_error_from_unix_common(errno);
                }
        }
 
-       if (!(flags & SOCKET_FLAG_BLOCK)) {
-               ret = set_blocking(sock->fd, false);
-               if (ret == -1) {
-                       return map_nt_error_from_unix(errno);
-               }
+       ret = set_blocking(sock->fd, false);
+       if (ret == -1) {
+               return map_nt_error_from_unix_common(errno);
        }
 
        sock->state= SOCKET_STATE_SERVER_LISTEN;
@@ -208,7 +215,7 @@ static NTSTATUS ipv4_accept(struct socket_context *sock, struct socket_context *
 {
        struct sockaddr_in cli_addr;
        socklen_t cli_addr_len = sizeof(cli_addr);
-       int new_fd;
+       int new_fd, ret;
 
        if (sock->type != SOCKET_TYPE_STREAM) {
                return NT_STATUS_INVALID_PARAMETER;
@@ -216,17 +223,18 @@ static NTSTATUS ipv4_accept(struct socket_context *sock, struct socket_context *
 
        new_fd = accept(sock->fd, (struct sockaddr *)&cli_addr, &cli_addr_len);
        if (new_fd == -1) {
-               return map_nt_error_from_unix(errno);
+               return map_nt_error_from_unix_common(errno);
        }
 
-       if (!(sock->flags & SOCKET_FLAG_BLOCK)) {
-               int ret = set_blocking(new_fd, false);
-               if (ret == -1) {
-                       close(new_fd);
-                       return map_nt_error_from_unix(errno);
-               }
+       ret = set_blocking(new_fd, false);
+       if (ret == -1) {
+               close(new_fd);
+               return map_nt_error_from_unix_common(errno);
        }
 
+       smb_set_close_on_exec(new_fd);
+
+
        /* TODO: we could add a 'accept_check' hook here
         *       which get the black/white lists via socket_set_accept_filter()
         *       or something like that
@@ -264,7 +272,7 @@ static NTSTATUS ip_recv(struct socket_context *sock, void *buf,
        if (gotlen == 0) {
                return NT_STATUS_END_OF_FILE;
        } else if (gotlen == -1) {
-               return map_nt_error_from_unix(errno);
+               return map_nt_error_from_unix_common(errno);
        }
 
        *nread = gotlen;
@@ -305,9 +313,10 @@ static NTSTATUS ipv4_recvfrom(struct socket_context *sock, void *buf,
        if (gotlen == 0) {
                talloc_free(src);
                return NT_STATUS_END_OF_FILE;
-       } else if (gotlen == -1) {
+       }
+       if (gotlen == -1) {
                talloc_free(src);
-               return map_nt_error_from_unix(errno);
+               return map_nt_error_from_unix_common(errno);
        }
 
        src->sockaddrlen = from_len;
@@ -338,7 +347,7 @@ static NTSTATUS ip_send(struct socket_context *sock,
 
        len = send(sock->fd, blob->data, blob->length, 0);
        if (len == -1) {
-               return map_nt_error_from_unix(errno);
+               return map_nt_error_from_unix_common(errno);
        }       
 
        *sendlen = len;
@@ -379,7 +388,7 @@ static NTSTATUS ipv4_sendto(struct socket_context *sock,
                             (struct sockaddr *)&srv_addr, sizeof(srv_addr));
        }
        if (len == -1) {
-               return map_nt_error_from_unix(errno);
+               return map_nt_error_from_unix_common(errno);
        }       
 
        *sendlen = len;
@@ -514,7 +523,7 @@ static NTSTATUS ip_pending(struct socket_context *sock, size_t *npending)
                *npending = value;
                return NT_STATUS_OK;
        }
-       return map_nt_error_from_unix(errno);
+       return map_nt_error_from_unix_common(errno);
 }
 
 static const struct socket_ops ipv4_ops = {
@@ -545,18 +554,20 @@ _PUBLIC_ const struct socket_ops *socket_ipv4_ops(enum socket_type type)
        return &ipv4_ops;
 }
 
-#if HAVE_IPV6
+#ifdef HAVE_IPV6
 
 static struct in6_addr interpret_addr6(const char *name)
 {
        char addr[INET6_ADDRSTRLEN];
        struct in6_addr dest6;
        const char *sp = name;
-       char *p = strchr_m(sp, '%');
+       char *p;
        int ret;
 
        if (sp == NULL) return in6addr_any;
 
+       p = strchr_m(sp, '%');
+
        if (strcasecmp(sp, "localhost") == 0) {
                sp = "::1";
        }
@@ -598,9 +609,11 @@ static NTSTATUS ipv6_init(struct socket_context *sock)
 
        sock->fd = socket(PF_INET6, type, 0);
        if (sock->fd == -1) {
-               return map_nt_error_from_unix(errno);
+               return map_nt_error_from_unix_common(errno);
        }
 
+       smb_set_close_on_exec(sock->fd);
+
        sock->backend_name = "ipv6";
        sock->family = AF_INET6;
 
@@ -617,7 +630,7 @@ static NTSTATUS ipv6_tcp_connect(struct socket_context *sock,
        if (my_address && my_address->sockaddr) {
                ret = bind(sock->fd, my_address->sockaddr, my_address->sockaddrlen);
                if (ret == -1) {
-                       return map_nt_error_from_unix(errno);
+                       return map_nt_error_from_unix_common(errno);
                }
        } else if (my_address) {
                struct in6_addr my_ip;
@@ -632,7 +645,7 @@ static NTSTATUS ipv6_tcp_connect(struct socket_context *sock,
                        
                        ret = bind(sock->fd, (struct sockaddr *)&my_addr, sizeof(my_addr));
                        if (ret == -1) {
-                               return map_nt_error_from_unix(errno);
+                               return map_nt_error_from_unix_common(errno);
                        }
                }
        }
@@ -655,15 +668,28 @@ static NTSTATUS ipv6_tcp_connect(struct socket_context *sock,
                ret = connect(sock->fd, (const struct sockaddr *)&srv_addr, sizeof(srv_addr));
        }
        if (ret == -1) {
-               return map_nt_error_from_unix(errno);
+               return map_nt_error_from_unix_common(errno);
        }
 
        return ip_connect_complete(sock, flags);
 }
 
+/*
+  fix the sin6_scope_id based on the address interface
+ */
+static void fix_scope_id(struct sockaddr_in6 *in6,
+                        const char *address)
+{
+       const char *p = strchr(address, '%');
+       if (p != NULL) {
+               in6->sin6_scope_id = if_nametoindex(p+1);
+       }
+}
+
+
 static NTSTATUS ipv6_listen(struct socket_context *sock,
-                               const struct socket_address *my_address,
-                               int queue_size, uint32_t flags)
+                           const struct socket_address *my_address,
+                           int queue_size, uint32_t flags)
 {
        struct sockaddr_in6 my_addr;
        struct in6_addr ip_addr;
@@ -674,32 +700,37 @@ static NTSTATUS ipv6_listen(struct socket_context *sock,
        if (my_address->sockaddr) {
                ret = bind(sock->fd, my_address->sockaddr, my_address->sockaddrlen);
        } else {
+               int one = 1;
                ip_addr = interpret_addr6(my_address->addr);
                
                ZERO_STRUCT(my_addr);
                my_addr.sin6_addr       = ip_addr;
                my_addr.sin6_port       = htons(my_address->port);
                my_addr.sin6_family     = PF_INET6;
-               
-               ret = bind(sock->fd, (struct sockaddr *)&my_addr, sizeof(my_addr));
+               fix_scope_id(&my_addr, my_address->addr);
+
+               /* when binding on ipv6 we always want to only bind on v6 */
+               ret = setsockopt(sock->fd, IPPROTO_IPV6, IPV6_V6ONLY,
+                                (const void *)&one, sizeof(one));
+               if (ret != -1) {
+                       ret = bind(sock->fd, (struct sockaddr *)&my_addr, sizeof(my_addr));
+               }
        }
 
        if (ret == -1) {
-               return map_nt_error_from_unix(errno);
+               return map_nt_error_from_unix_common(errno);
        }
 
        if (sock->type == SOCKET_TYPE_STREAM) {
                ret = listen(sock->fd, queue_size);
                if (ret == -1) {
-                       return map_nt_error_from_unix(errno);
+                       return map_nt_error_from_unix_common(errno);
                }
        }
 
-       if (!(flags & SOCKET_FLAG_BLOCK)) {
-               ret = set_blocking(sock->fd, false);
-               if (ret == -1) {
-                       return map_nt_error_from_unix(errno);
-               }
+       ret = set_blocking(sock->fd, false);
+       if (ret == -1) {
+               return map_nt_error_from_unix_common(errno);
        }
 
        sock->state= SOCKET_STATE_SERVER_LISTEN;
@@ -709,9 +740,9 @@ static NTSTATUS ipv6_listen(struct socket_context *sock,
 
 static NTSTATUS ipv6_tcp_accept(struct socket_context *sock, struct socket_context **new_sock)
 {
-       struct sockaddr_in cli_addr;
+       struct sockaddr_in6 cli_addr;
        socklen_t cli_addr_len = sizeof(cli_addr);
-       int new_fd;
+       int new_fd, ret;
        
        if (sock->type != SOCKET_TYPE_STREAM) {
                return NT_STATUS_INVALID_PARAMETER;
@@ -719,16 +750,15 @@ static NTSTATUS ipv6_tcp_accept(struct socket_context *sock, struct socket_conte
 
        new_fd = accept(sock->fd, (struct sockaddr *)&cli_addr, &cli_addr_len);
        if (new_fd == -1) {
-               return map_nt_error_from_unix(errno);
+               return map_nt_error_from_unix_common(errno);
        }
 
-       if (!(sock->flags & SOCKET_FLAG_BLOCK)) {
-               int ret = set_blocking(new_fd, false);
-               if (ret == -1) {
-                       close(new_fd);
-                       return map_nt_error_from_unix(errno);
-               }
+       ret = set_blocking(new_fd, false);
+       if (ret == -1) {
+               close(new_fd);
+               return map_nt_error_from_unix_common(errno);
        }
+       smb_set_close_on_exec(new_fd);
 
        /* TODO: we could add a 'accept_check' hook here
         *       which get the black/white lists via socket_set_accept_filter()
@@ -790,7 +820,7 @@ static NTSTATUS ipv6_recvfrom(struct socket_context *sock, void *buf,
                return NT_STATUS_END_OF_FILE;
        } else if (gotlen == -1) {
                talloc_free(src);
-               return map_nt_error_from_unix(errno);
+               return map_nt_error_from_unix_common(errno);
        }
 
        src->sockaddrlen = from_len;
@@ -828,7 +858,8 @@ static NTSTATUS ipv6_sendto(struct socket_context *sock,
                
                ZERO_STRUCT(srv_addr);
                addr                     = interpret_addr6(dest_addr->addr);
-               if (addr.s6_addr == 0) {
+               if (memcmp(&addr.s6_addr, &in6addr_any,
+                          sizeof(addr.s6_addr)) == 0) {
                        return NT_STATUS_HOST_UNREACHABLE;
                }
                srv_addr.sin6_addr = addr;
@@ -841,7 +872,7 @@ static NTSTATUS ipv6_sendto(struct socket_context *sock,
                             (struct sockaddr *)&srv_addr, sizeof(srv_addr));
        }
        if (len == -1) {
-               return map_nt_error_from_unix(errno);
+               return map_nt_error_from_unix_common(errno);
        }       
 
        *sendlen = len;
@@ -961,7 +992,7 @@ static struct socket_address *ipv6_tcp_get_my_addr(struct socket_context *sock,
                return NULL;
        }
        
-       local->addr = talloc_strdup(mem_ctx, addrstring);
+       local->addr = talloc_strdup(local, addrstring);
        if (!local->addr) {
                talloc_free(local);
                return NULL;