|
|
|
@ -0,0 +1,615 @@ |
|
|
|
#include <dlfcn.h> |
|
|
|
#include <stdio.h> |
|
|
|
#include <sys/types.h> |
|
|
|
#include <sys/socket.h> |
|
|
|
#include <netinet/in.h> |
|
|
|
#include <netinet/ip.h> |
|
|
|
#include <sys/un.h> |
|
|
|
#include <stdlib.h> |
|
|
|
#include <arpa/inet.h> |
|
|
|
#include <assert.h> |
|
|
|
#include <unistd.h> |
|
|
|
#include <sys/stat.h> |
|
|
|
#include <errno.h> |
|
|
|
#include <pthread.h> |
|
|
|
|
|
|
|
typedef struct { |
|
|
|
int used_domain, |
|
|
|
wanted_domain, |
|
|
|
type, |
|
|
|
used_protocol, |
|
|
|
wanted_protocol; |
|
|
|
char unix_path[256]; |
|
|
|
struct sockaddr_storage sockname; |
|
|
|
int open:1, |
|
|
|
bound:1; |
|
|
|
} socket_t; |
|
|
|
|
|
|
|
typedef struct { |
|
|
|
struct sockaddr_un path; |
|
|
|
struct sockaddr_storage address; |
|
|
|
} peer_t; |
|
|
|
|
|
|
|
#define MAX_SOCKETS 1024 |
|
|
|
|
|
|
|
static socket_t real_sockets[MAX_SOCKETS]; |
|
|
|
static unsigned int anon_sock_inc; |
|
|
|
|
|
|
|
static peer_t remote_peers[MAX_SOCKETS]; |
|
|
|
static unsigned int anon_peer_inc; |
|
|
|
static pthread_mutex_t remote_peers_lock = PTHREAD_MUTEX_INITIALIZER; |
|
|
|
|
|
|
|
static void do_init(void) __attribute__((constructor)); |
|
|
|
static void do_exit(void) __attribute__((destructor)); |
|
|
|
|
|
|
|
static void do_init(void) { |
|
|
|
setenv("RTPE_PRELOAD_TEST_ACTIVE", "1", 1); |
|
|
|
} |
|
|
|
static void do_exit(void) { |
|
|
|
for (int i = 0; i < MAX_SOCKETS; i++) { |
|
|
|
socket_t *s = &real_sockets[i]; |
|
|
|
if (!s->open) |
|
|
|
continue; |
|
|
|
if (s->used_domain != AF_UNIX) |
|
|
|
continue; |
|
|
|
if (s->wanted_domain == AF_UNIX) |
|
|
|
continue; |
|
|
|
unlink(s->unix_path); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
static const char *path_prefix(void) { |
|
|
|
char *ret = getenv("TEST_SOCKET_PATH"); |
|
|
|
if (ret) |
|
|
|
return ret; |
|
|
|
return "/tmp"; |
|
|
|
} |
|
|
|
|
|
|
|
int socket(int domain, int type, int protocol) { |
|
|
|
int use_domain = domain; |
|
|
|
int use_protocol = protocol; |
|
|
|
|
|
|
|
if (domain == AF_INET || domain == AF_INET6) { |
|
|
|
use_domain = AF_UNIX; |
|
|
|
use_protocol = 0; |
|
|
|
} |
|
|
|
|
|
|
|
int (*real_socket)(int, int, int) = dlsym(RTLD_NEXT, "socket"); |
|
|
|
int fd = real_socket(use_domain, type, use_protocol); |
|
|
|
if (fd < 0 || fd >= MAX_SOCKETS) { |
|
|
|
fprintf(stderr, "preload socket(): fd out of bounds (fd %i)\n", fd); |
|
|
|
return fd; |
|
|
|
} |
|
|
|
real_sockets[fd] = (socket_t) { |
|
|
|
.used_domain = use_domain, |
|
|
|
.wanted_domain = domain, |
|
|
|
.type = type, |
|
|
|
.used_protocol = use_protocol, |
|
|
|
.wanted_protocol = protocol, |
|
|
|
.open = 1, |
|
|
|
}; |
|
|
|
|
|
|
|
return fd; |
|
|
|
} |
|
|
|
|
|
|
|
static const char *addr_translate(struct sockaddr_un *sun, const struct sockaddr *addr, |
|
|
|
socklen_t addrlen, |
|
|
|
int allow_anon) |
|
|
|
{ |
|
|
|
const char *err; |
|
|
|
char sockname[64]; |
|
|
|
const char *any_name; |
|
|
|
unsigned int port; |
|
|
|
|
|
|
|
switch (addr->sa_family) { |
|
|
|
case AF_INET:; |
|
|
|
struct sockaddr_in *sin = (void *) addr; |
|
|
|
err = "addrlen too short"; |
|
|
|
if (addrlen < sizeof(*sin)) |
|
|
|
goto err; |
|
|
|
err = "failed to print network address"; |
|
|
|
if (!inet_ntop(addr->sa_family, &sin->sin_addr, sockname, sizeof(sockname))) |
|
|
|
goto err; |
|
|
|
any_name = "0.0.0.0"; |
|
|
|
port = ntohs(sin->sin_port); |
|
|
|
break; |
|
|
|
case AF_INET6:; |
|
|
|
struct sockaddr_in6 *sin6 = (void *) addr; |
|
|
|
err = "addrlen too short"; |
|
|
|
if (addrlen < sizeof(*sin6)) |
|
|
|
goto err; |
|
|
|
err = "failed to print network address"; |
|
|
|
if (!inet_ntop(addr->sa_family, &sin6->sin6_addr, sockname, sizeof(sockname))) |
|
|
|
goto err; |
|
|
|
any_name = "::"; |
|
|
|
port = ntohs(sin6->sin6_port); |
|
|
|
break; |
|
|
|
default: |
|
|
|
goto skip; |
|
|
|
} |
|
|
|
|
|
|
|
int do_specific = 1; |
|
|
|
|
|
|
|
if (allow_anon) { |
|
|
|
err = "Unix socket path truncated"; |
|
|
|
if (snprintf(sun->sun_path, sizeof(sun->sun_path), "%s/[%s]:%u", path_prefix(), any_name, port) |
|
|
|
>= sizeof(sun->sun_path)) |
|
|
|
goto err; |
|
|
|
|
|
|
|
struct stat sb; |
|
|
|
int ret = stat(sun->sun_path, &sb); |
|
|
|
if (ret == 0 && sb.st_mode & S_IFSOCK) |
|
|
|
do_specific = 0; |
|
|
|
} |
|
|
|
|
|
|
|
if (do_specific) { |
|
|
|
err = "Unix socket path truncated"; |
|
|
|
if (snprintf(sun->sun_path, sizeof(sun->sun_path), "%s/[%s]:%u", path_prefix(), sockname, port) |
|
|
|
>= sizeof(sun->sun_path)) |
|
|
|
goto err; |
|
|
|
} |
|
|
|
|
|
|
|
sun->sun_family = AF_UNIX; |
|
|
|
return NULL; |
|
|
|
skip: |
|
|
|
return ""; // special return value |
|
|
|
err: |
|
|
|
return err; |
|
|
|
} |
|
|
|
|
|
|
|
int bind(int fd, const struct sockaddr *addr, socklen_t addrlen) { |
|
|
|
const char *err; |
|
|
|
int (*real_bind)(int, const struct sockaddr *, socklen_t) = dlsym(RTLD_NEXT, "bind"); |
|
|
|
err = "fd out of bounds"; |
|
|
|
if (fd < 0 || fd >= MAX_SOCKETS) |
|
|
|
goto do_bind_warn; |
|
|
|
socket_t *s = &real_sockets[fd]; |
|
|
|
err = "fd not open"; |
|
|
|
if (!s->open) |
|
|
|
goto do_bind_warn; |
|
|
|
|
|
|
|
assert(s->used_domain == AF_UNIX); |
|
|
|
assert(s->wanted_domain == addr->sa_family); |
|
|
|
|
|
|
|
struct sockaddr_un sun; |
|
|
|
err = addr_translate(&sun, addr, addrlen, 0); |
|
|
|
if (err) { |
|
|
|
if (!err[0]) |
|
|
|
goto do_bind; |
|
|
|
goto do_bind_warn; |
|
|
|
} |
|
|
|
|
|
|
|
struct sockaddr_storage sst = {0,}; |
|
|
|
if (addrlen > sizeof(sst)) |
|
|
|
goto do_bind_warn; |
|
|
|
memcpy(&sst, addr, addrlen); |
|
|
|
|
|
|
|
addr = (void *) &sun; |
|
|
|
addrlen = sizeof(sun); |
|
|
|
|
|
|
|
if (s->unix_path[0]) |
|
|
|
unlink(s->unix_path); |
|
|
|
|
|
|
|
assert(sizeof(s->unix_path) >= strlen(sun.sun_path)); |
|
|
|
strcpy(s->unix_path, sun.sun_path); |
|
|
|
s->sockname = sst; |
|
|
|
s->bound = 1; |
|
|
|
|
|
|
|
goto do_bind; |
|
|
|
|
|
|
|
do_bind_warn: |
|
|
|
fprintf(stderr, "preload bind(): %s (fd %i)\n", err, fd); |
|
|
|
do_bind: |
|
|
|
return real_bind(fd, addr, addrlen); |
|
|
|
} |
|
|
|
|
|
|
|
static void anon_addr(int domain, struct sockaddr_storage *sst, unsigned int id, unsigned int id2) { |
|
|
|
memset(sst, 0, sizeof(*sst)); |
|
|
|
switch (domain) { |
|
|
|
case AF_INET:; |
|
|
|
struct sockaddr_in sin; |
|
|
|
sin.sin_family = AF_INET; |
|
|
|
sin.sin_port = htons(id); |
|
|
|
sin.sin_addr.s_addr = id2; |
|
|
|
memcpy(sst, &sin, sizeof(sin)); |
|
|
|
break; |
|
|
|
case AF_INET6:; |
|
|
|
struct sockaddr_in6 sin6; |
|
|
|
sin6.sin6_family = AF_INET6; |
|
|
|
sin6.sin6_port = htons(id); |
|
|
|
memset(&sin6.sin6_addr, -1, sizeof(sin6.sin6_addr)); |
|
|
|
sin6.sin6_addr.s6_addr16[4] = id2; |
|
|
|
memcpy(sst, &sin6, sizeof(sin6)); |
|
|
|
break; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
static void check_bind(int fd) { |
|
|
|
// to make inspecting the peer address on the receiving end possible, we must bind |
|
|
|
// to some unix path name |
|
|
|
|
|
|
|
if (fd < 0 || fd >= MAX_SOCKETS) |
|
|
|
return; |
|
|
|
socket_t *s = &real_sockets[fd]; |
|
|
|
if (!s->open) |
|
|
|
return; |
|
|
|
if (s->bound) |
|
|
|
return; |
|
|
|
if (s->wanted_domain == AF_UNIX || s->used_domain != AF_UNIX) |
|
|
|
return; |
|
|
|
|
|
|
|
struct sockaddr_storage sst; |
|
|
|
unsigned int auto_inc = __sync_fetch_and_add(&anon_sock_inc, 1); |
|
|
|
anon_addr(s->wanted_domain, &sst, auto_inc, getpid()); |
|
|
|
|
|
|
|
struct sockaddr_un sun; |
|
|
|
sun.sun_family = AF_UNIX; |
|
|
|
if (snprintf(sun.sun_path, sizeof(sun.sun_path), "%s/ANON.%u.%u", path_prefix(), getpid(), |
|
|
|
auto_inc) |
|
|
|
>= sizeof(sun.sun_path)) |
|
|
|
fprintf(stderr, "preload socket(): failed to print anon (fd %i)\n", fd); |
|
|
|
|
|
|
|
assert(sizeof(real_sockets[fd].unix_path) >= strlen(sun.sun_path)); |
|
|
|
strcpy(real_sockets[fd].unix_path, sun.sun_path); |
|
|
|
|
|
|
|
int (*real_bind)(int, const struct sockaddr *, socklen_t) = dlsym(RTLD_NEXT, "bind"); |
|
|
|
if (real_bind(fd, (struct sockaddr *) &sun, sizeof(sun))) |
|
|
|
fprintf(stderr, "preload socket(): failed to bind to anon (fd %i): %s\n", |
|
|
|
fd, strerror(errno)); |
|
|
|
|
|
|
|
s->bound = 1; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
int close(int fd) { |
|
|
|
const char *err; |
|
|
|
int (*real_close)(int) = dlsym(RTLD_NEXT, "close"); |
|
|
|
err = "fd out of bounds"; |
|
|
|
if (fd < 0 || fd >= MAX_SOCKETS) |
|
|
|
goto do_close_warn; |
|
|
|
socket_t *s = &real_sockets[fd]; |
|
|
|
if (!s->open) |
|
|
|
goto do_close; |
|
|
|
|
|
|
|
s->open = 0; |
|
|
|
if (s->used_domain == AF_UNIX && s->wanted_domain != AF_UNIX && s->unix_path[0]) |
|
|
|
unlink(s->unix_path); |
|
|
|
goto do_close; |
|
|
|
|
|
|
|
do_close_warn: |
|
|
|
fprintf(stderr, "preload close(): %s (fd %i)\n", err, fd); |
|
|
|
do_close: |
|
|
|
return real_close(fd); |
|
|
|
} |
|
|
|
|
|
|
|
int getsockname(int fd, struct sockaddr *addr, socklen_t *addrlen) { |
|
|
|
check_bind(fd); |
|
|
|
|
|
|
|
const char *err; |
|
|
|
int (*real_getsockname)(int) = dlsym(RTLD_NEXT, "getsockname"); |
|
|
|
err = "fd out of bounds"; |
|
|
|
if (fd < 0 || fd >= MAX_SOCKETS) |
|
|
|
goto do_getsockname_warn; |
|
|
|
socket_t *s = &real_sockets[fd]; |
|
|
|
if (!s->open) |
|
|
|
goto do_getsockname; |
|
|
|
if (s->used_domain != AF_UNIX || s->wanted_domain == AF_UNIX || !s->bound) |
|
|
|
goto do_getsockname; |
|
|
|
|
|
|
|
switch (s->wanted_domain) { |
|
|
|
case AF_INET: |
|
|
|
if (*addrlen < sizeof(struct sockaddr_in)) |
|
|
|
memcpy(addr, &s->sockname, *addrlen); |
|
|
|
else |
|
|
|
memcpy(addr, &s->sockname, sizeof(struct sockaddr_in)); |
|
|
|
*addrlen = sizeof(struct sockaddr_in); |
|
|
|
break; |
|
|
|
case AF_INET6: |
|
|
|
if (*addrlen < sizeof(struct sockaddr_in6)) |
|
|
|
memcpy(addr, &s->sockname, *addrlen); |
|
|
|
else |
|
|
|
memcpy(addr, &s->sockname, sizeof(struct sockaddr_in6)); |
|
|
|
*addrlen = sizeof(struct sockaddr_in6); |
|
|
|
break; |
|
|
|
default: |
|
|
|
goto do_getsockname; |
|
|
|
} |
|
|
|
|
|
|
|
return 0; |
|
|
|
|
|
|
|
do_getsockname_warn: |
|
|
|
fprintf(stderr, "preload getsockname(): %s (fd %i)\n", err, fd); |
|
|
|
do_getsockname: |
|
|
|
return real_getsockname(fd); |
|
|
|
} |
|
|
|
|
|
|
|
int connect(int fd, const struct sockaddr *addr, socklen_t addrlen) { |
|
|
|
check_bind(fd); |
|
|
|
|
|
|
|
const char *err; |
|
|
|
int (*real_connect)(int, const struct sockaddr *, socklen_t) = dlsym(RTLD_NEXT, "connect"); |
|
|
|
err = "fd out of bounds"; |
|
|
|
if (fd < 0 || fd >= MAX_SOCKETS) |
|
|
|
goto do_connect_warn; |
|
|
|
socket_t *s = &real_sockets[fd]; |
|
|
|
err = "fd not open"; |
|
|
|
if (!s->open) |
|
|
|
goto do_connect_warn; |
|
|
|
|
|
|
|
assert(s->used_domain == AF_UNIX); |
|
|
|
assert(s->wanted_domain == addr->sa_family); |
|
|
|
|
|
|
|
struct sockaddr_un sun; |
|
|
|
err = addr_translate(&sun, addr, addrlen, 1); |
|
|
|
if (err) { |
|
|
|
if (!err[0]) |
|
|
|
goto do_connect; |
|
|
|
goto do_connect_warn; |
|
|
|
} |
|
|
|
|
|
|
|
addr = (void *) &sun; |
|
|
|
addrlen = sizeof(sun); |
|
|
|
|
|
|
|
goto do_connect; |
|
|
|
|
|
|
|
do_connect_warn: |
|
|
|
fprintf(stderr, "preload connect(): %s (fd %i)\n", err, fd); |
|
|
|
do_connect: |
|
|
|
return real_connect(fd, addr, addrlen); |
|
|
|
} |
|
|
|
|
|
|
|
int dup(int fd) { |
|
|
|
int (*real_dup)(int) = dlsym(RTLD_NEXT, "dup"); |
|
|
|
int ret = real_dup(fd); |
|
|
|
if (fd < 0 || fd >= MAX_SOCKETS || ret < 0 || ret >= MAX_SOCKETS) { |
|
|
|
fprintf(stderr, "preload dup(): fd out of bounds (%i/%i)\n", fd, ret); |
|
|
|
return ret; |
|
|
|
} |
|
|
|
real_sockets[ret] = real_sockets[fd]; |
|
|
|
return ret; |
|
|
|
} |
|
|
|
|
|
|
|
int dup2(int oldfd, int newfd) { |
|
|
|
int (*real_dup2)(int, int) = dlsym(RTLD_NEXT, "dup2"); |
|
|
|
int ret = real_dup2(oldfd, newfd); |
|
|
|
if (ret != newfd || oldfd < 0 || oldfd >= MAX_SOCKETS || newfd < 0 || newfd >= MAX_SOCKETS) { |
|
|
|
fprintf(stderr, "preload dup(): fd out of bounds (%i/%i/%i)\n", oldfd, newfd, ret); |
|
|
|
return ret; |
|
|
|
} |
|
|
|
if (real_sockets[newfd].open) { |
|
|
|
if (real_sockets[newfd].used_domain == AF_UNIX && real_sockets[newfd].unix_path[0]) |
|
|
|
unlink(real_sockets[newfd].unix_path); |
|
|
|
} |
|
|
|
real_sockets[newfd] = real_sockets[oldfd]; |
|
|
|
return ret; |
|
|
|
} |
|
|
|
|
|
|
|
ssize_t recvmsg(int fd, struct msghdr *msg, int flags) { |
|
|
|
const char *err; |
|
|
|
ssize_t (*real_recvmsg)(int, struct msghdr *, int) = dlsym(RTLD_NEXT, "recvmsg"); |
|
|
|
err = "fd out of bounds"; |
|
|
|
if (fd < 0 || fd >= MAX_SOCKETS) |
|
|
|
goto do_recvmsg_warn; |
|
|
|
socket_t *s = &real_sockets[fd]; |
|
|
|
err = "fd not open"; |
|
|
|
if (!s->open) |
|
|
|
goto do_recvmsg_warn; |
|
|
|
if (s->used_domain != AF_UNIX || s->wanted_domain == AF_UNIX) |
|
|
|
goto do_recvmsg; |
|
|
|
|
|
|
|
struct sockaddr_un sun; |
|
|
|
struct sockaddr *sa_orig = NULL; |
|
|
|
socklen_t sa_len; |
|
|
|
if (msg->msg_name) { |
|
|
|
sa_orig = msg->msg_name; |
|
|
|
sa_len = msg->msg_namelen; |
|
|
|
msg->msg_name = &sun; |
|
|
|
msg->msg_namelen = sizeof(sun); |
|
|
|
} |
|
|
|
|
|
|
|
ssize_t ret = real_recvmsg(fd, msg, flags); |
|
|
|
|
|
|
|
if (ret <= 0) |
|
|
|
goto out; |
|
|
|
|
|
|
|
if (sa_orig && msg->msg_name) { |
|
|
|
assert(sun.sun_family == AF_UNIX); |
|
|
|
char *path = sun.sun_path; |
|
|
|
assert(strlen(path) > 0); |
|
|
|
const char *pref = path_prefix(); |
|
|
|
err = "received from unknown peer"; |
|
|
|
if (strncmp(path, pref, strlen(pref))) |
|
|
|
goto out_warn; |
|
|
|
path += strlen(pref); |
|
|
|
if (path[0] != '/') |
|
|
|
goto out_warn; |
|
|
|
path++; |
|
|
|
if (!strncmp(path, "ANON.", 5)) { |
|
|
|
pthread_mutex_lock(&remote_peers_lock); |
|
|
|
peer_t *p = NULL; |
|
|
|
for (unsigned int i = 0; i < anon_peer_inc; i++) { |
|
|
|
p = &remote_peers[i]; |
|
|
|
if (!strcmp(p->path.sun_path, path)) |
|
|
|
goto got_peer; |
|
|
|
} |
|
|
|
assert(anon_peer_inc < MAX_SOCKETS); |
|
|
|
// generate new fake remote response address |
|
|
|
p = &remote_peers[anon_peer_inc++]; |
|
|
|
p->path = sun; |
|
|
|
anon_addr(s->wanted_domain, &p->address, anon_peer_inc, getpid()); |
|
|
|
got_peer: |
|
|
|
pthread_mutex_unlock(&remote_peers_lock); |
|
|
|
assert(sizeof(p->address) >= sa_len); |
|
|
|
memcpy(sa_orig, &p->address, sa_len); |
|
|
|
} |
|
|
|
else if (path[0] == '[') { |
|
|
|
path++; |
|
|
|
char *end = strchr(path, ']'); |
|
|
|
assert(end != NULL); |
|
|
|
char addr[64]; |
|
|
|
if (snprintf(addr, sizeof(addr), "%.*s", (int) (end - path), path) >= sizeof(addr)) |
|
|
|
abort(); |
|
|
|
end++; |
|
|
|
assert(*end == ':'); |
|
|
|
end++; |
|
|
|
int port = atoi(end); |
|
|
|
assert(port != 0); |
|
|
|
|
|
|
|
struct sockaddr_in sin = {0,}; |
|
|
|
struct sockaddr_in6 sin6 = {0,}; |
|
|
|
socklen_t addrlen; |
|
|
|
struct sockaddr *sa = NULL; |
|
|
|
|
|
|
|
if (inet_pton(AF_INET, addr, &sin.sin_addr)) { |
|
|
|
sin.sin_family = AF_INET; |
|
|
|
sin.sin_port = htons(port); |
|
|
|
sa = (struct sockaddr *) &sin; |
|
|
|
addrlen = sizeof(sin); |
|
|
|
} |
|
|
|
else if (inet_pton(AF_INET6, addr, &sin6.sin6_addr)) { |
|
|
|
sin6.sin6_family = AF_INET6; |
|
|
|
sin6.sin6_port = htons(port); |
|
|
|
sa = (struct sockaddr *) &sin6; |
|
|
|
addrlen = sizeof(sin6); |
|
|
|
} |
|
|
|
else |
|
|
|
abort(); |
|
|
|
|
|
|
|
assert(addrlen >= sa_len); |
|
|
|
memcpy(sa_orig, sa, sa_len); |
|
|
|
} |
|
|
|
else |
|
|
|
abort(); |
|
|
|
|
|
|
|
msg->msg_name = sa_orig; |
|
|
|
msg->msg_namelen = sa_len; |
|
|
|
} |
|
|
|
|
|
|
|
goto out; |
|
|
|
|
|
|
|
out_warn: |
|
|
|
fprintf(stderr, "preload recvmsg(): %s (fd %i)\n", err, fd); |
|
|
|
out: |
|
|
|
return ret; |
|
|
|
|
|
|
|
do_recvmsg_warn: |
|
|
|
fprintf(stderr, "preload recvmsg(): %s (fd %i)\n", err, fd); |
|
|
|
do_recvmsg: |
|
|
|
return real_recvmsg(fd, msg, flags); |
|
|
|
} |
|
|
|
|
|
|
|
ssize_t send(int fd, const void *buf, size_t len, int flags) { |
|
|
|
check_bind(fd); |
|
|
|
ssize_t (*real_send)(int, const void *, size_t, int) = dlsym(RTLD_NEXT, "send"); |
|
|
|
return real_send(fd, buf, len, flags); |
|
|
|
} |
|
|
|
|
|
|
|
static const struct sockaddr *addr_find(const struct sockaddr *addr, socklen_t *addrlen) { |
|
|
|
pthread_mutex_lock(&remote_peers_lock); |
|
|
|
for (unsigned int i = 0; i < anon_peer_inc; i++) { |
|
|
|
peer_t *p = &remote_peers[i]; |
|
|
|
if (p->address.ss_family != addr->sa_family) |
|
|
|
continue; |
|
|
|
switch (p->address.ss_family) { |
|
|
|
case AF_INET:{ |
|
|
|
struct sockaddr_in *a = (struct sockaddr_in *) addr, |
|
|
|
*b = (struct sockaddr_in *) &p->address; |
|
|
|
if (a->sin_port != b->sin_port) |
|
|
|
continue; |
|
|
|
if (a->sin_addr.s_addr != b->sin_addr.s_addr) |
|
|
|
continue; |
|
|
|
break; |
|
|
|
} |
|
|
|
|
|
|
|
case AF_INET6:{ |
|
|
|
struct sockaddr_in6 *a = (struct sockaddr_in6 *) addr, |
|
|
|
*b = (struct sockaddr_in6 *) &p->address; |
|
|
|
if (a->sin6_port != b->sin6_port) |
|
|
|
continue; |
|
|
|
if (memcmp(&a->sin6_addr, &b->sin6_addr, sizeof(a->sin6_addr))) |
|
|
|
continue; |
|
|
|
break; |
|
|
|
} |
|
|
|
|
|
|
|
default: |
|
|
|
continue; |
|
|
|
} |
|
|
|
|
|
|
|
// match |
|
|
|
*addrlen = sizeof(p->path); |
|
|
|
pthread_mutex_unlock(&remote_peers_lock); |
|
|
|
return (struct sockaddr *) &p->path; |
|
|
|
} |
|
|
|
pthread_mutex_unlock(&remote_peers_lock); |
|
|
|
return NULL; |
|
|
|
} |
|
|
|
|
|
|
|
static const struct sockaddr *addr_send_translate(const struct sockaddr *addr, socklen_t *addrlen) { |
|
|
|
const struct sockaddr *ret = addr_find(addr, addrlen); |
|
|
|
if (ret) |
|
|
|
return ret; |
|
|
|
|
|
|
|
static __thread struct sockaddr_un sun; |
|
|
|
const char *err = addr_translate(&sun, addr, *addrlen, 0); |
|
|
|
if (!err) { |
|
|
|
*addrlen = sizeof(sun); |
|
|
|
return (void *) &sun; |
|
|
|
} |
|
|
|
|
|
|
|
if (err[0]) |
|
|
|
fprintf(stderr, "preload addr_send_translate(): %s\n", err); |
|
|
|
|
|
|
|
return addr; |
|
|
|
} |
|
|
|
|
|
|
|
ssize_t sendto(int fd, const void *buf, size_t len, int flags, const struct sockaddr *addr, socklen_t addrlen) { |
|
|
|
check_bind(fd); |
|
|
|
ssize_t (*real_sendto)(int, const void *, size_t, int, const struct sockaddr *, socklen_t) |
|
|
|
= dlsym(RTLD_NEXT, "sendto"); |
|
|
|
addr = addr_send_translate(addr, &addrlen); |
|
|
|
return real_sendto(fd, buf, len, flags, addr, addrlen); |
|
|
|
} |
|
|
|
|
|
|
|
ssize_t sendmsg(int fd, const struct msghdr *msg, int flags) { |
|
|
|
check_bind(fd); |
|
|
|
ssize_t (*real_sendmsg)(int, const struct msghdr *, int) = dlsym(RTLD_NEXT, "sendmsg"); |
|
|
|
struct msghdr msg2 = *msg; |
|
|
|
if (msg2.msg_name) |
|
|
|
msg2.msg_name = (void *) addr_send_translate(msg2.msg_name, &msg2.msg_namelen); |
|
|
|
return real_sendmsg(fd, &msg2, flags); |
|
|
|
} |
|
|
|
|
|
|
|
int setsockopt(int fd, int level, int optname, const void *optval, socklen_t optlen) { |
|
|
|
const char *err; |
|
|
|
int (*real_setsockopt)(int, int, int, const void *, socklen_t) = dlsym(RTLD_NEXT, "setsockopt"); |
|
|
|
err = "fd out of bounds"; |
|
|
|
if (fd < 0 || fd >= MAX_SOCKETS) |
|
|
|
goto do_set_warn; |
|
|
|
socket_t *s = &real_sockets[fd]; |
|
|
|
err = "fd not open"; |
|
|
|
if (!s->open) |
|
|
|
goto do_set_warn; |
|
|
|
|
|
|
|
assert(s->used_domain == AF_UNIX); |
|
|
|
|
|
|
|
switch (s->wanted_domain) { |
|
|
|
case AF_INET: |
|
|
|
if (level == SOL_IP && optname == IP_TOS) |
|
|
|
return 0; |
|
|
|
break; |
|
|
|
|
|
|
|
case AF_INET6: |
|
|
|
if (level == SOL_IPV6 && optname == IPV6_V6ONLY) |
|
|
|
return 0; |
|
|
|
if (level == SOL_IPV6 && optname == IPV6_TCLASS) |
|
|
|
return 0; |
|
|
|
break; |
|
|
|
} |
|
|
|
|
|
|
|
goto do_set; |
|
|
|
|
|
|
|
do_set_warn: |
|
|
|
fprintf(stderr, "preload setsockopt(): %s (fd %i)\n", err, fd); |
|
|
|
do_set: |
|
|
|
return real_setsockopt(fd, level, optname, optval, optlen); |
|
|
|
} |