From 363f7c937d0f2360b80cb733081385f0b6b43b82 Mon Sep 17 00:00:00 2001 From: Richard Fuchs Date: Fri, 16 Jul 2021 13:08:40 -0400 Subject: [PATCH] TT#89352 add TCP support to preload testing lib Change-Id: Id19450bde26a09494acbeb44206c2aa7ecda03b4 --- t/tests-preload.c | 267 ++++++++++++++++++++++++++++++++++------------ 1 file changed, 196 insertions(+), 71 deletions(-) diff --git a/t/tests-preload.c b/t/tests-preload.c index 7c6d011a3..bff6a2bd7 100644 --- a/t/tests-preload.c +++ b/t/tests-preload.c @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -21,7 +22,8 @@ typedef struct { used_protocol, wanted_protocol; char unix_path[256]; - struct sockaddr_storage sockname; + struct sockaddr_storage sockname, + peername; unsigned int open:1, bound:1; } socket_t; @@ -29,6 +31,7 @@ typedef struct { typedef struct { struct sockaddr_un path; struct sockaddr_storage address; + socklen_t addrlen; } peer_t; #define MAX_SOCKETS 4096 @@ -43,6 +46,8 @@ 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 socklen_t anon_addr(int domain, struct sockaddr_storage *sst, unsigned int id, unsigned int id2); + static void do_init(void) { setenv("RTPE_PRELOAD_TEST_ACTIVE", "1", 1); } @@ -158,6 +163,84 @@ err: return err; } +void addr_translate_reverse(struct sockaddr_storage *sst, socklen_t *socklen, int wanted_domain, + const struct sockaddr_un *sun) +{ + assert(sun->sun_family == AF_UNIX); + const char *path = sun->sun_path; + assert(strlen(path) > 0); + const char *pref = path_prefix(); + if (strncmp(path, pref, strlen(pref))) { + fprintf(stderr, "preload addr_translate_reverse(): received from unknown peer '%s'\n", path); + return; + } + path += strlen(pref); + if (path[0] != '/') { + fprintf(stderr, "preload addr_translate_reverse(): received from unknown peer '%s'\n", path); + return; + } + path++; + + struct sockaddr_in sin = {0,}; + struct sockaddr_in6 sin6 = {0,}; + socklen_t addrlen; + struct sockaddr *sa = NULL; + + 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; + p->addrlen = anon_addr(wanted_domain, &p->address, anon_peer_inc, getpid()); +got_peer: + pthread_mutex_unlock(&remote_peers_lock); + addrlen = p->addrlen; + sa = (struct sockaddr *) &p->address; + } + 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); + + 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(); + } + else + abort(); + + assert(addrlen <= sizeof(*sst)); + memset(sst, 0, sizeof(*sst)); + memcpy(sst, sa, addrlen); + *socklen = addrlen; +} + 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"); @@ -204,8 +287,9 @@ do_bind: return real_bind(fd, addr, addrlen); } -static void anon_addr(int domain, struct sockaddr_storage *sst, unsigned int id, unsigned int id2) { +static socklen_t anon_addr(int domain, struct sockaddr_storage *sst, unsigned int id, unsigned int id2) { memset(sst, 0, sizeof(*sst)); + socklen_t ret = -1; switch (domain) { case AF_INET:; struct sockaddr_in sin; @@ -213,6 +297,7 @@ static void anon_addr(int domain, struct sockaddr_storage *sst, unsigned int id, sin.sin_port = htons(id); sin.sin_addr.s_addr = id2; memcpy(sst, &sin, sizeof(sin)); + ret = sizeof(sin); break; case AF_INET6:; struct sockaddr_in6 sin6; @@ -221,8 +306,10 @@ static void anon_addr(int domain, struct sockaddr_storage *sst, unsigned int id, memset(&sin6.sin6_addr, -1, sizeof(sin6.sin6_addr)); sin6.sin6_addr.s6_addr16[4] = id2; memcpy(sst, &sin6, sizeof(sin6)); + ret = sizeof(sin6); break; } + return ret; } static void check_bind(int fd) { @@ -324,6 +411,47 @@ do_getsockname: return real_getsockname(fd); } +int getpeername(int fd, struct sockaddr *addr, socklen_t *addrlen) { + check_bind(fd); + + const char *err; + int (*real_getpeername)(int) = dlsym(RTLD_NEXT, "getpeername"); + err = "fd out of bounds"; + if (fd < 0 || fd >= MAX_SOCKETS) + goto do_getpeername_warn; + socket_t *s = &real_sockets[fd]; + if (!s->open) + goto do_getpeername; + if (s->used_domain != AF_UNIX || s->wanted_domain == AF_UNIX || !s->bound) + goto do_getpeername; + + switch (s->wanted_domain) { + case AF_INET: + if (*addrlen < sizeof(struct sockaddr_in)) + memcpy(addr, &s->peername, *addrlen); + else + memcpy(addr, &s->peername, sizeof(struct sockaddr_in)); + *addrlen = sizeof(struct sockaddr_in); + break; + case AF_INET6: + if (*addrlen < sizeof(struct sockaddr_in6)) + memcpy(addr, &s->peername, *addrlen); + else + memcpy(addr, &s->peername, sizeof(struct sockaddr_in6)); + *addrlen = sizeof(struct sockaddr_in6); + break; + default: + goto do_getpeername; + } + + return 0; + +do_getpeername_warn: + fprintf(stderr, "preload getpeername(): %s (fd %i)\n", err, fd); +do_getpeername: + return real_getpeername(fd); +} + int connect(int fd, const struct sockaddr *addr, socklen_t addrlen) { check_bind(fd); @@ -348,6 +476,12 @@ int connect(int fd, const struct sockaddr *addr, socklen_t addrlen) { goto do_connect_warn; } + struct sockaddr_storage sst = {0,}; + if (addrlen > sizeof(sst)) + goto do_connect_warn; + memcpy(&sst, addr, addrlen); + s->peername = sst; + addr = (void *) &sun; addrlen = sizeof(sun); @@ -359,6 +493,57 @@ do_connect: return real_connect(fd, addr, addrlen); } +int accept(int fd, struct sockaddr *addr, socklen_t *addrlen) { + const char *err; + int (*real_accept)(int, struct sockaddr *, socklen_t *) = dlsym(RTLD_NEXT, "accept"); + + err = "fd out of bounds"; + if (fd < 0 || fd >= MAX_SOCKETS) + goto do_accept_warn; + socket_t *s = &real_sockets[fd]; + err = "fd not open"; + if (!s->open) + goto do_accept_warn; + + assert(s->used_domain == AF_UNIX); + + goto do_accept; + +do_accept_warn: + fprintf(stderr, "preload accept(): %s (fd %i)\n", err, fd); +do_accept:; + struct sockaddr_un sun; + socklen_t sun_len = sizeof(sun); + int new_fd = real_accept(fd, (struct sockaddr *) &sun, &sun_len); + if (new_fd == -1) + return -1; + if (new_fd < 0 || new_fd >= MAX_SOCKETS || real_sockets[new_fd].open) { + fprintf(stderr, "preload accept(): new_fd out of bounds (%i/%i)\n", fd, new_fd); + return -1; + } + + assert(sun.sun_family == AF_UNIX); + socket_t *new_s = &real_sockets[new_fd]; + *new_s = *s; + assert(sun_len < sizeof(new_s->sockname)); + assert(sizeof(new_s->unix_path) >= strlen(sun.sun_path)); + strcpy(new_s->unix_path, sun.sun_path); + memset(&new_s->sockname, 0, sizeof(new_s->sockname)); + new_s->open = 1; + + struct sockaddr_storage sst; + socklen_t socklen; + addr_translate_reverse(&sst, &socklen, new_s->wanted_domain, &sun); + assert(socklen <= *addrlen); + memset(addr, 0, *addrlen); + memcpy(addr, &sst, socklen); + *addrlen = socklen; + assert(s->wanted_domain == addr->sa_family); + new_s->peername = sst; + + return new_fd; +} + int dup(int fd) { int (*real_dup)(int) = dlsym(RTLD_NEXT, "dup"); int ret = real_dup(fd); @@ -414,73 +599,11 @@ ssize_t recvmsg(int fd, struct msghdr *msg, int flags) { 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(); + struct sockaddr_storage sst; + socklen_t addrlen; + addr_translate_reverse(&sst, &addrlen, s->wanted_domain, &sun); + assert(addrlen <= sa_len); + memcpy(sa_orig, &sst, addrlen); msg->msg_name = sa_orig; msg->msg_namelen = sa_len; @@ -488,8 +611,6 @@ got_peer: goto out; -out_warn: - fprintf(stderr, "preload recvmsg(): %s (fd %i)\n", err, fd); out: return ret; @@ -597,6 +718,8 @@ int setsockopt(int fd, int level, int optname, const void *optval, socklen_t opt case AF_INET: if (level == SOL_IP && optname == IP_TOS) return 0; + if (level == IPPROTO_TCP && optname == TCP_NODELAY) + return 0; break; case AF_INET6: @@ -604,6 +727,8 @@ int setsockopt(int fd, int level, int optname, const void *optval, socklen_t opt return 0; if (level == SOL_IPV6 && optname == IPV6_TCLASS) return 0; + if (level == IPPROTO_TCP && optname == TCP_NODELAY) + return 0; break; }