summaryrefslogtreecommitdiff
path: root/source4/lib/socket_wrapper
diff options
context:
space:
mode:
authorStefan Metzmacher <metze@samba.org>2005-11-07 15:36:51 +0000
committerGerald (Jerry) Carter <jerry@samba.org>2007-10-10 13:45:53 -0500
commit14b59bcbec1a288810efee5c9442dff30f1a474a (patch)
treee3415521f54225a33c912ddf4deaed2b4457100c /source4/lib/socket_wrapper
parenta8d741b4f7f83485178aedf7ca8068b3420ee93c (diff)
downloadsamba-14b59bcbec1a288810efee5c9442dff30f1a474a.tar.gz
samba-14b59bcbec1a288810efee5c9442dff30f1a474a.tar.bz2
samba-14b59bcbec1a288810efee5c9442dff30f1a474a.zip
r11555: - change socket_wrapper to support multiple IP's
- SOCKET_WRAPPER_DEFAULT_IFACE=X specifies the default interface for 127.0.0.X - we now use multiple interfaces for smbtorture in make test 127.0.0.26-127.0.0.31 - and 127.0.0.1 only for smbd the are more work needed for better support for broacast messages... but this is enough for the winsrepl tests metze (This used to be commit dbd01110d1a3e0f5914ae8d156723d6d6edf160c)
Diffstat (limited to 'source4/lib/socket_wrapper')
-rw-r--r--source4/lib/socket_wrapper/socket_wrapper.c304
1 files changed, 260 insertions, 44 deletions
diff --git a/source4/lib/socket_wrapper/socket_wrapper.c b/source4/lib/socket_wrapper/socket_wrapper.c
index d95806783d..d9f61f7e3c 100644
--- a/source4/lib/socket_wrapper/socket_wrapper.c
+++ b/source4/lib/socket_wrapper/socket_wrapper.c
@@ -63,7 +63,9 @@
with this format we have 8 chars left for the directory name
*/
-#define SOCKET_FORMAT "%u_%05u"
+#define SOCKET_FORMAT "%c%02X%04X"
+#define SOCKET_TYPE_CHAR_TCP 'T'
+#define SOCKET_TYPE_CHAR_UDP 'U'
static struct sockaddr *sockaddr_dup(const void *data, socklen_t len)
{
@@ -80,6 +82,7 @@ struct socket_info
int type;
int protocol;
int bound;
+ int bcast;
char *path;
char *tmp_path;
@@ -108,45 +111,199 @@ static const char *socket_wrapper_dir(void)
return s;
}
+static unsigned int socket_wrapper_default_iface(void)
+{
+ const char *s = getenv("SOCKET_WRAPPER_DEFAULT_IFACE");
+ if (s) {
+ unsigned int iface;
+ if (sscanf(s, "%u", &iface) == 1) {
+ if (iface >= 1 && iface <= 0xFF) {
+ return iface;
+ }
+ }
+ }
+
+ return 1;/* 127.0.0.1 */
+}
+
static int convert_un_in(const struct sockaddr_un *un, struct sockaddr_in *in, socklen_t *len)
{
+ unsigned int iface;
unsigned int prt;
const char *p;
- int type;
+ char type;
if ((*len) < sizeof(struct sockaddr_in)) {
return 0;
}
- in->sin_family = AF_INET;
- in->sin_port = htons(1025); /* Default to 1025 */
p = strrchr(un->sun_path, '/');
if (p) p++; else p = un->sun_path;
- if (sscanf(p, SOCKET_FORMAT, &type, &prt) == 2) {
- in->sin_port = htons(prt);
+ if (sscanf(p, SOCKET_FORMAT, &type, &iface, &prt) != 3) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (type != SOCKET_TYPE_CHAR_TCP && type != SOCKET_TYPE_CHAR_UDP) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (iface == 0 || iface > 0xFF) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (prt > 0xFFFF) {
+ errno = EINVAL;
+ return -1;
}
- in->sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+
+ in->sin_addr.s_addr = htonl((127<<24) | iface);
+ in->sin_port = htons(prt);
+
*len = sizeof(struct sockaddr_in);
return 0;
}
-static int convert_in_un(struct socket_info *si, const struct sockaddr_in *in, struct sockaddr_un *un)
+static int convert_in_un_remote(struct socket_info *si, const struct sockaddr_in *in, struct sockaddr_un *un,
+ int *bcast)
{
- int type = si->type;
- uint16_t prt = ntohs(in->sin_port);
+ char u_type = '\0';
+ char b_type = '\0';
+ char a_type = '\0';
+ char type = '\0';
+ unsigned int addr= ntohl(in->sin_addr.s_addr);
+ unsigned int prt = ntohs(in->sin_port);
+ unsigned int iface;
+ int is_bcast = 0;
+
+ if (bcast) *bcast = 0;
+
+ if (prt == 0) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ switch (si->type) {
+ case SOCK_STREAM:
+ u_type = SOCKET_TYPE_CHAR_TCP;
+ break;
+ case SOCK_DGRAM:
+ u_type = SOCKET_TYPE_CHAR_UDP;
+ a_type = SOCKET_TYPE_CHAR_UDP;
+ b_type = SOCKET_TYPE_CHAR_UDP;
+ break;
+ }
+
+ if (a_type && addr == 0xFFFFFFFF) {
+ /* 255.255.255.255 only udp */
+ is_bcast = 2;
+ type = a_type;
+ iface = socket_wrapper_default_iface();
+ } else if (b_type && addr == 0x7FFFFFFF) {
+ /* 127.255.255.255 only udp */
+ is_bcast = 1;
+ type = b_type;
+ iface = socket_wrapper_default_iface();
+ } else if ((addr & 0xFFFFFF00) == 0x7F000000) {
+ /* 127.0.0.X */
+ is_bcast = 0;
+ type = u_type;
+ iface = (addr & 0x000000FF);
+ } else {
+ errno = ENETUNREACH;
+ return -1;
+ }
+
+ if (bcast) *bcast = is_bcast;
+
+ if (is_bcast) {
+ snprintf(un->sun_path, sizeof(un->sun_path), "%s/EINVAL",
+ socket_wrapper_dir());
+ /* the caller need to do more processing */
+ return 0;
+ }
+
+ snprintf(un->sun_path, sizeof(un->sun_path), "%s/"SOCKET_FORMAT,
+ socket_wrapper_dir(), type, iface, prt);
+
+ return 0;
+}
+
+static int convert_in_un_alloc(struct socket_info *si, const struct sockaddr_in *in, struct sockaddr_un *un,
+ int *bcast)
+{
+ char u_type = '\0';
+ char d_type = '\0';
+ char b_type = '\0';
+ char a_type = '\0';
+ char type = '\0';
+ unsigned int addr= ntohl(in->sin_addr.s_addr);
+ unsigned int prt = ntohs(in->sin_port);
+ unsigned int iface;
+ struct stat st;
+ int is_bcast = 0;
+
+ if (bcast) *bcast = 0;
+
+ switch (si->type) {
+ case SOCK_STREAM:
+ u_type = SOCKET_TYPE_CHAR_TCP;
+ d_type = SOCKET_TYPE_CHAR_TCP;
+ break;
+ case SOCK_DGRAM:
+ u_type = SOCKET_TYPE_CHAR_UDP;
+ d_type = SOCKET_TYPE_CHAR_UDP;
+ a_type = SOCKET_TYPE_CHAR_UDP;
+ b_type = SOCKET_TYPE_CHAR_UDP;
+ break;
+ }
+
+ if (addr == 0) {
+ /* 0.0.0.0 */
+ is_bcast = 0;
+ type = d_type;
+ iface = socket_wrapper_default_iface();
+ } else if (a_type && addr == 0xFFFFFFFF) {
+ /* 255.255.255.255 only udp */
+ is_bcast = 2;
+ type = a_type;
+ iface = socket_wrapper_default_iface();
+ } else if (b_type && addr == 0x7FFFFFFF) {
+ /* 127.255.255.255 only udp */
+ is_bcast = 1;
+ type = b_type;
+ iface = socket_wrapper_default_iface();
+ } else if ((addr & 0xFFFFFF00) == 0x7F000000) {
+ /* 127.0.0.X */
+ is_bcast = 0;
+ type = u_type;
+ iface = (addr & 0x000000FF);
+ } else {
+ errno = EADDRNOTAVAIL;
+ return -1;
+ }
+
+ if (bcast) *bcast = is_bcast;
+
if (prt == 0) {
- struct stat st;
/* handle auto-allocation of ephemeral ports */
- prt = 5000;
- do {
+ for (prt = 5001; prt < 10000; prt++) {
snprintf(un->sun_path, sizeof(un->sun_path), "%s/"SOCKET_FORMAT,
- socket_wrapper_dir(), type, ++prt);
- } while (stat(un->sun_path, &st) == 0 && prt < 10000);
- ((struct sockaddr_in *)si->myname)->sin_port = htons(prt);
- }
+ socket_wrapper_dir(), type, iface, prt);
+ if (stat(un->sun_path, &st) == 0) continue;
+
+ ((struct sockaddr_in *)si->myname)->sin_port = htons(prt);
+ return 0;
+ }
+ errno = ENFILE;
+ return -1;
+ }
+
snprintf(un->sun_path, sizeof(un->sun_path), "%s/"SOCKET_FORMAT,
- socket_wrapper_dir(), type, prt);
+ socket_wrapper_dir(), type, iface, prt);
return 0;
}
@@ -162,7 +319,7 @@ static struct socket_info *find_socket_info(int fd)
}
static int sockaddr_convert_to_un(struct socket_info *si, const struct sockaddr *in_addr, socklen_t in_len,
- struct sockaddr_un *out_addr)
+ struct sockaddr_un *out_addr, int alloc_sock, int *bcast)
{
if (!out_addr)
return 0;
@@ -171,7 +328,19 @@ static int sockaddr_convert_to_un(struct socket_info *si, const struct sockaddr
switch (in_addr->sa_family) {
case AF_INET:
- return convert_in_un(si, (const struct sockaddr_in *)in_addr, out_addr);
+ switch (si->type) {
+ case SOCK_STREAM:
+ case SOCK_DGRAM:
+ break;
+ default:
+ errno = ESOCKTNOSUPPORT;
+ return -1;
+ }
+ if (alloc_sock) {
+ return convert_in_un_alloc(si, (const struct sockaddr_in *)in_addr, out_addr, bcast);
+ } else {
+ return convert_in_un_remote(si, (const struct sockaddr_in *)in_addr, out_addr, bcast);
+ }
case AF_UNIX:
memcpy(out_addr, in_addr, sizeof(*out_addr));
return 0;
@@ -200,6 +369,14 @@ static int sockaddr_convert_from_un(const struct socket_info *si,
switch (family) {
case AF_INET:
+ switch (si->type) {
+ case SOCK_STREAM:
+ case SOCK_DGRAM:
+ break;
+ default:
+ errno = ESOCKTNOSUPPORT;
+ return -1;
+ }
return convert_un_in(in_addr, (struct sockaddr_in *)out_addr, out_len);
case AF_UNIX:
memcpy(out_addr, in_addr, sizeof(*in_addr));
@@ -288,28 +465,46 @@ static int swrap_auto_bind(struct socket_info *si)
struct sockaddr_un un_addr;
struct sockaddr_in in;
int i;
+ char type;
+ int ret;
+ struct stat st;
un_addr.sun_family = AF_UNIX;
+
+ switch (si->type) {
+ case SOCK_STREAM:
+ type = SOCKET_TYPE_CHAR_TCP;
+ break;
+ case SOCK_DGRAM:
+ type = SOCKET_TYPE_CHAR_UDP;
+ break;
+ default:
+ errno = ESOCKTNOSUPPORT;
+ return -1;
+ }
for (i=0;i<1000;i++) {
snprintf(un_addr.sun_path, sizeof(un_addr.sun_path),
"%s/"SOCKET_FORMAT, socket_wrapper_dir(),
- SOCK_DGRAM, i + 10000);
- if (bind(si->fd, (struct sockaddr *)&un_addr,
- sizeof(un_addr)) == 0) {
- si->tmp_path = strdup(un_addr.sun_path);
- si->bound = 1;
- break;
- }
+ type, socket_wrapper_default_iface(), i + 10000);
+ if (stat(un_addr.sun_path, &st) == 0) continue;
+
+ ret = real_bind(si->fd, (struct sockaddr *)&un_addr, sizeof(un_addr));
+ if (ret == -1) return ret;
+
+ si->tmp_path = strdup(un_addr.sun_path);
+ si->bound = 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_addr.s_addr = htonl(INADDR_LOOPBACK);
+ 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);
@@ -328,25 +523,24 @@ int swrap_connect(int s, const struct sockaddr *serv_addr, socklen_t addrlen)
return real_connect(s, serv_addr, addrlen);
}
- /* only allow pseudo loopback connections */
- if (serv_addr->sa_family == AF_INET &&
- ((const struct sockaddr_in *)serv_addr)->sin_addr.s_addr !=
- htonl(INADDR_LOOPBACK)) {
- errno = ENETUNREACH;
- return -1;
- }
-
if (si->bound == 0 && si->domain != AF_UNIX) {
ret = swrap_auto_bind(si);
if (ret == -1) return -1;
}
- ret = sockaddr_convert_to_un(si, (const struct sockaddr *)serv_addr, addrlen, &un_addr);
+ ret = sockaddr_convert_to_un(si, (const struct sockaddr *)serv_addr, addrlen, &un_addr, 0, NULL);
if (ret == -1) return -1;
ret = real_connect(s, (struct sockaddr *)&un_addr,
sizeof(struct sockaddr_un));
+ /* to give better errors */
+ if (serv_addr->sa_family == AF_INET) {
+ if (ret == -1 && errno == ENOENT) {
+ errno = EHOSTUNREACH;
+ }
+ }
+
if (ret == 0) {
si->peername_len = addrlen;
si->peername = sockaddr_dup(serv_addr, addrlen);
@@ -368,12 +562,7 @@ int swrap_bind(int s, const struct sockaddr *myaddr, socklen_t addrlen)
si->myname_len = addrlen;
si->myname = sockaddr_dup(myaddr, addrlen);
- if (myaddr->sa_family == AF_INET &&
- ((const struct sockaddr_in *)myaddr)->sin_addr.s_addr == 0) {
- ((struct sockaddr_in *)si->myname)->sin_addr.s_addr =
- htonl(INADDR_LOOPBACK);
- }
- ret = sockaddr_convert_to_un(si, (const struct sockaddr *)myaddr, addrlen, &un_addr);
+ ret = sockaddr_convert_to_un(si, (const struct sockaddr *)myaddr, addrlen, &un_addr, 1, &si->bcast);
if (ret == -1) return -1;
unlink(un_addr.sun_path);
@@ -501,6 +690,7 @@ ssize_t swrap_sendto(int s, const void *buf, size_t len, int flags, const str
struct sockaddr_un un_addr;
int ret;
struct socket_info *si = find_socket_info(s);
+ int bcast = 0;
if (!si) {
return real_sendto(s, buf, len, flags, to, tolen);
@@ -511,11 +701,37 @@ ssize_t swrap_sendto(int s, const void *buf, size_t len, int flags, const str
if (ret == -1) return -1;
}
- ret = sockaddr_convert_to_un(si, to, tolen, &un_addr);
+ ret = sockaddr_convert_to_un(si, to, tolen, &un_addr, 0, &bcast);
if (ret == -1) return -1;
+ if (bcast) {
+ struct stat st;
+ unsigned int iface;
+ unsigned int prt = ntohs(((const struct sockaddr_in *)to)->sin_port);
+ char type;
+
+ type = SOCKET_TYPE_CHAR_UDP;
+
+ for(iface=0; iface <= 0xFF; 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;
+
+ /* ignore the any errors in broadcast sends */
+ real_sendto(s, buf, len, flags, (struct sockaddr *)&un_addr, sizeof(un_addr));
+ }
+ return len;
+ }
+
ret = real_sendto(s, buf, len, flags, (struct sockaddr *)&un_addr, sizeof(un_addr));
+ /* to give better errors */
+ if (to->sa_family == AF_INET) {
+ if (ret == -1 && errno == ENOENT) {
+ errno = EHOSTUNREACH;
+ }
+ }
+
return ret;
}