r18947: overload listen() and ioctl() in socket_wrapper
[samba.git] / source4 / lib / socket_wrapper / socket_wrapper.c
index 230c40a72f9b9f67800b050301929e0d6efd6f48..c884b96c6ce1ec973e46f53434e76309dd319e5d 100644 (file)
@@ -3,6 +3,7 @@
    unix domain sockets if the environment variable SOCKET_WRAPPER_DIR 
    is set.
    Copyright (C) Jelmer Vernooij 2005
+   Copyright (C) Stefan Metzmacher 2006
    
    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
 */
 
 #ifdef _SAMBA_BUILD_
+
+#define SOCKET_WRAPPER_NOT_REPLACE
 #include "includes.h"
-#undef SOCKET_WRAPPER
 #include "system/network.h"
 #include "system/filesys.h"
-#else
+
+#ifndef _DLINKLIST_H
+#include "lib/util/dlinklist.h"
+#endif
+
+#ifdef malloc
+#undef malloc
+#endif
+#ifdef calloc
+#undef calloc
+#endif
+#ifdef strdup
+#undef strdup
+#endif
+
+#else /* _SAMBA_BUILD_ */
+
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <sys/socket.h>
 #include <unistd.h>
 #include <string.h>
 #include <stdio.h>
+
+#error "dlinklist.h missing"
+
 #endif
-#include "lib/util/dlinklist.h"
 
 /* LD_PRELOAD doesn't work yet, so REWRITE_CALLS is all we support
  * for now */
 #define real_accept accept
 #define real_connect connect
 #define real_bind bind
+#define real_listen listen
 #define real_getpeername getpeername
 #define real_getsockname getsockname
 #define real_getsockopt getsockopt
 #define real_setsockopt setsockopt
 #define real_recvfrom recvfrom
 #define real_sendto sendto
+#define real_ioctl ioctl
 #define real_recv recv
 #define real_send send
 #define real_socket socket
@@ -69,6 +91,8 @@
 #define SOCKET_TYPE_CHAR_TCP           'T'
 #define SOCKET_TYPE_CHAR_UDP           'U'
 
+#define MAX_WRAPPED_INTERFACES 16
+
 static struct sockaddr *sockaddr_dup(const void *data, socklen_t len)
 {
        struct sockaddr *ret = (struct sockaddr *)malloc(len);
@@ -80,7 +104,7 @@ struct socket_info
 {
        int fd;
 
-       int domain;
+       int family;
        int type;
        int protocol;
        int bound;
@@ -98,7 +122,7 @@ struct socket_info
        struct socket_info *prev, *next;
 };
 
-static struct socket_info *sockets = NULL;
+static struct socket_info *sockets;
 
 
 static const char *socket_wrapper_dir(void)
@@ -136,7 +160,7 @@ static unsigned int socket_wrapper_default_iface(void)
        if (s) {
                unsigned int iface;
                if (sscanf(s, "%u", &iface) == 1) {
-                       if (iface >= 1 && iface <= 0xFF) {
+                       if (iface >= 1 && iface <= MAX_WRAPPED_INTERFACES) {
                                return iface;
                        }
                }
@@ -169,7 +193,7 @@ static int convert_un_in(const struct sockaddr_un *un, struct sockaddr_in *in, s
                return -1;
        }
 
-       if (iface == 0 || iface > 0xFF) {
+       if (iface == 0 || iface > MAX_WRAPPED_INTERFACES) {
                errno = EINVAL;
                return -1;
        }
@@ -430,32 +454,50 @@ static void swrap_dump_packet(struct socket_info *si, const struct sockaddr *add
 
 }
 
-_PUBLIC_ int swrap_socket(int domain, int type, int protocol)
+_PUBLIC_ int swrap_socket(int family, int type, int protocol)
 {
        struct socket_info *si;
        int fd;
 
        if (!socket_wrapper_dir()) {
-               return real_socket(domain, type, protocol);
+               return real_socket(family, type, protocol);
        }
 
-       switch (domain) {
+       switch (family) {
        case AF_INET:
                break;
        case AF_UNIX:
-               return real_socket(domain, type, protocol);
+               return real_socket(family, type, protocol);
        default:
                errno = EAFNOSUPPORT;
                return -1;
        }
-       
+
+       switch (type) {
+       case SOCK_STREAM:
+               break;
+       case SOCK_DGRAM:
+               break;
+       default:
+               errno = EPROTONOSUPPORT;
+               return -1;
+       }
+
+       switch (protocol) {
+       case 0:
+               break;
+       default:
+               errno = EPROTONOSUPPORT;
+               return -1;
+       }
+
        fd = real_socket(AF_UNIX, type, 0);
 
        if (fd == -1) return -1;
 
-       si = calloc(1, sizeof(struct socket_info));
+       si = (struct socket_info *)calloc(1, sizeof(struct socket_info));
 
-       si->domain = domain;
+       si->family = family;
        si->type = type;
        si->protocol = protocol;
        si->fd = fd;
@@ -492,24 +534,35 @@ _PUBLIC_ int swrap_accept(int s, struct sockaddr *addr, socklen_t *addrlen)
        fd = ret;
 
        ret = sockaddr_convert_from_un(parent_si, &un_addr, un_addrlen,
-                                      parent_si->domain, addr, addrlen);
-       if (ret == -1) return ret;
+                                      parent_si->family, addr, addrlen);
+       if (ret == -1) {
+               close(fd);
+               return ret;
+       }
 
-       child_si = malloc(sizeof(struct socket_info));
+       child_si = (struct socket_info *)malloc(sizeof(struct socket_info));
        memset(child_si, 0, sizeof(*child_si));
 
        child_si->fd = fd;
-       child_si->domain = parent_si->domain;
+       child_si->family = parent_si->family;
        child_si->type = parent_si->type;
        child_si->protocol = parent_si->protocol;
        child_si->bound = 1;
 
        ret = real_getsockname(fd, (struct sockaddr *)&un_my_addr, &un_my_addrlen);
-       if (ret == -1) return ret;
+       if (ret == -1) {
+               free(child_si);
+               close(fd);
+               return ret;
+       }
 
        ret = sockaddr_convert_from_un(child_si, &un_my_addr, un_my_addrlen,
-                                      child_si->domain, &my_addr, &my_addrlen);
-       if (ret == -1) return ret;
+                                      child_si->family, &my_addr, &my_addrlen);
+       if (ret == -1) {
+               free(child_si);
+               close(fd);
+               return ret;
+       }
 
        child_si->myname_len = my_addrlen;
        child_si->myname = sockaddr_dup(&my_addr, my_addrlen);
@@ -524,6 +577,9 @@ _PUBLIC_ int swrap_accept(int s, struct sockaddr *addr, socklen_t *addrlen)
        return fd;
 }
 
+static int autobind_start_init;
+static int autobind_start;
+
 /* using sendto() or connect() on an unbound socket would give the
    recipient no way to reply, as unlike UDP and TCP, a unix domain
    socket can't auto-assign emphemeral port numbers, so we need to
@@ -535,8 +591,16 @@ static int swrap_auto_bind(struct socket_info *si)
        int i;
        char type;
        int ret;
+       int port;
        struct stat st;
-       
+
+       if (autobind_start_init != 1) {
+               autobind_start_init = 1;
+               autobind_start = getpid();
+               autobind_start %= 50000;
+               autobind_start += 10000;
+       }
+
        un_addr.sun_family = AF_UNIX;
 
        switch (si->type) {
@@ -550,11 +614,16 @@ static int swrap_auto_bind(struct socket_info *si)
                errno = ESOCKTNOSUPPORT;
                return -1;
        }
-       
+
+       if (autobind_start > 60000) {
+               autobind_start = 10000;
+       }
+
        for (i=0;i<1000;i++) {
+               port = autobind_start + i;
                snprintf(un_addr.sun_path, sizeof(un_addr.sun_path), 
                         "%s/"SOCKET_FORMAT, socket_wrapper_dir(),
-                        type, socket_wrapper_default_iface(), i + 10000);
+                        type, socket_wrapper_default_iface(), port);
                if (stat(un_addr.sun_path, &st) == 0) continue;
                
                ret = real_bind(si->fd, (struct sockaddr *)&un_addr, sizeof(un_addr));
@@ -562,21 +631,21 @@ static int swrap_auto_bind(struct socket_info *si)
 
                si->tmp_path = strdup(un_addr.sun_path);
                si->bound = 1;
+               autobind_start = port + 1;
                break;
        }
        if (i == 1000) {
                errno = ENFILE;
                return -1;
        }
-       
+
        memset(&in, 0, sizeof(in));
        in.sin_family = AF_INET;
-       in.sin_port   = htons(i);
+       in.sin_port   = htons(port);
        in.sin_addr.s_addr = htonl(127<<24 | socket_wrapper_default_iface());
        
        si->myname_len = sizeof(in);
        si->myname = sockaddr_dup(&in, si->myname_len);
-       si->bound = 1;
        return 0;
 }
 
@@ -645,6 +714,20 @@ _PUBLIC_ int swrap_bind(int s, const struct sockaddr *myaddr, socklen_t addrlen)
        return ret;
 }
 
+_PUBLIC_ int swrap_listen(int s, int backlog)
+{
+       int ret;
+       struct socket_info *si = find_socket_info(s);
+
+       if (!si) {
+               return real_listen(s, backlog);
+       }
+
+       ret = real_listen(s, backlog);
+
+       return ret;
+}
+
 _PUBLIC_ int swrap_getpeername(int s, struct sockaddr *name, socklen_t *addrlen)
 {
        struct socket_info *si = find_socket_info(s);
@@ -691,11 +774,8 @@ _PUBLIC_ int swrap_getsockopt(int s, int level, int optname, void *optval, sockl
                return real_getsockopt(s, level, optname, optval, optlen);
        } 
 
-       switch (si->domain) {
-       default:
-               errno = ENOPROTOOPT;
-               return -1;
-       }
+       errno = ENOPROTOOPT;
+       return -1;
 }
 
 _PUBLIC_ int swrap_setsockopt(int s, int  level,  int  optname,  const  void  *optval, socklen_t optlen)
@@ -710,7 +790,7 @@ _PUBLIC_ int swrap_setsockopt(int s, int  level,  int  optname,  const  void  *o
                return real_setsockopt(s, level, optname, optval, optlen);
        }
 
-       switch (si->domain) {
+       switch (si->family) {
        case AF_INET:
                return 0;
        default:
@@ -737,7 +817,7 @@ _PUBLIC_ ssize_t swrap_recvfrom(int s, void *buf, size_t len, int flags, struct
                return ret;
 
        if (sockaddr_convert_from_un(si, &un_addr, un_addrlen,
-                                    si->domain, from, fromlen) == -1) {
+                                    si->family, from, fromlen) == -1) {
                return -1;
        }
 
@@ -774,7 +854,7 @@ _PUBLIC_ ssize_t swrap_sendto(int s, const void *buf, size_t len, int flags, con
 
                type = SOCKET_TYPE_CHAR_UDP;
 
-               for(iface=0; iface <= 0xFF; iface++) {
+               for(iface=0; iface <= MAX_WRAPPED_INTERFACES; iface++) {
                        snprintf(un_addr.sun_path, sizeof(un_addr.sun_path), "%s/"SOCKET_FORMAT, 
                                 socket_wrapper_dir(), type, iface, prt);
                        if (stat(un_addr.sun_path, &st) != 0) continue;
@@ -800,6 +880,20 @@ _PUBLIC_ ssize_t swrap_sendto(int s, const void *buf, size_t len, int flags, con
        return ret;
 }
 
+_PUBLIC_ int swrap_ioctl(int s, int r, void *p)
+{
+       int ret;
+       struct socket_info *si = find_socket_info(s);   
+
+       if (!si) {
+               return real_ioctl(s, r, p);
+       }
+
+       ret = real_ioctl(s, r, p);
+
+       return ret;
+}
+
 _PUBLIC_ ssize_t swrap_recv(int s, void *buf, size_t len, int flags)
 {
        int ret;
@@ -840,21 +934,24 @@ _PUBLIC_ ssize_t swrap_send(int s, const void *buf, size_t len, int flags)
 _PUBLIC_ int swrap_close(int fd)
 {
        struct socket_info *si = find_socket_info(fd);
+       int ret;
 
-       if (si) {
-               DLIST_REMOVE(sockets, si);
+       if (!si) {
+               return real_close(fd);
+       }
 
-               swrap_dump_packet(si, NULL, SWRAP_CLOSE, NULL, 0, 0);
+       DLIST_REMOVE(sockets, si);
 
-               free(si->path);
-               free(si->myname);
-               free(si->peername);
-               if (si->tmp_path) {
-                       unlink(si->tmp_path);
-                       free(si->tmp_path);
-               }
-               free(si);
+       ret = real_close(fd);
+
+       free(si->path);
+       free(si->myname);
+       free(si->peername);
+       if (si->tmp_path) {
+               unlink(si->tmp_path);
+               free(si->tmp_path);
        }
+       free(si);
 
-       return real_close(fd);
+       return ret;
 }