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
#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);
{
int fd;
- int domain;
+ int family;
int type;
int protocol;
int bound;
struct socket_info *prev, *next;
};
-static struct socket_info *sockets = NULL;
+static struct socket_info *sockets;
static const char *socket_wrapper_dir(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;
}
}
return -1;
}
- if (iface == 0 || iface > 0xFF) {
+ if (iface == 0 || iface > MAX_WRAPPED_INTERFACES) {
errno = EINVAL;
return -1;
}
}
-_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;
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);
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
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) {
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));
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;
}
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);
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)
return real_setsockopt(s, level, optname, optval, optlen);
}
- switch (si->domain) {
+ switch (si->family) {
case AF_INET:
return 0;
default:
return ret;
if (sockaddr_convert_from_un(si, &un_addr, un_addrlen,
- si->domain, from, fromlen) == -1) {
+ si->family, from, fromlen) == -1) {
return -1;
}
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;
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;
_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;
}