The branch, master has been updated via 9e91b00 tests: add new test test_bind_ipv4_addr_in_use() via 0c3efeb tests: extend the ipv6 bind test via a14a1af tests: greatly extend the ipv4 bind test via f38c0b4 swrap: check whether an address:port is already in use in swrap_bind() via da04f94 swrap: implement check_addr_port_in_use() via f04ea32 swrap: fix AF_UNSPEC special case in swrap_bind() via 97a65a1 swrap: extend input checks in swrap_bind() via af40930 Update the todo list. via b357525 tests: Avoid using getenv() to retrieve the path. via 3029067 swrap: add check for rpc/rpc.h - needed on freebsd for bindresvport via 0025857 tests: Add tests for bindresvport(). via ccf4e64 swrap: Add support for bindresvport(). via b124fcb tests: Add tests for bind(). via 45eb7c9 swrap: Add missing family check in bind(). via 37b472d tests: Add test that getsockname is correct after socket(). via bdbbf38 swrap: Setup myname in swrap_socket() for getsockname(). via 205d5c2 torture: Wait for the pidfile and then start the tests. via 323feb7 echo_srv: Write pidfile after we setup the listeners. from 1dbbd72 README.install: improvements and fixes.
http://gitweb.samba.org/?p=socket_wrapper.git;a=shortlog;h=master - Log ----------------------------------------------------------------- commit 9e91b00ee329578987c579b2c9d6db967055a59a Author: Michael Adam <ob...@samba.org> Date: Sat May 31 03:35:22 2014 +0200 tests: add new test test_bind_ipv4_addr_in_use() This tests binding an address that is already in use. Signed-off-by: Michael Adam <ob...@samba.org> Reviewed-by: Andreas Schneider <a...@samba.org> commit 0c3efeb3b07083e36068e354b20208316d02c79a Author: Michael Adam <ob...@samba.org> Date: Sat May 31 03:31:56 2014 +0200 tests: extend the ipv6 bind test Signed-off-by: Michael Adam <ob...@samba.org> Reviewed-by: Andreas Schneider <a...@samba.org> commit a14a1afe966a45000fa768df43a25d84a68772dd Author: Michael Adam <ob...@samba.org> Date: Sat May 31 03:31:29 2014 +0200 tests: greatly extend the ipv4 bind test Signed-off-by: Michael Adam <ob...@samba.org> Reviewed-by: Andreas Schneider <a...@samba.org> commit f38c0b43d5620ab6457bf8a4fbdaf3a7bf90627c Author: Michael Adam <ob...@samba.org> Date: Fri May 30 14:20:40 2014 +0200 swrap: check whether an address:port is already in use in swrap_bind() Signed-off-by: Michael Adam <ob...@samba.org> Reviewed-by: Andreas Schneider <a...@samba.org> commit da04f94dd0575245424cf7853d0a9fc5b7caacee Author: Michael Adam <ob...@samba.org> Date: Fri May 30 14:20:17 2014 +0200 swrap: implement check_addr_port_in_use() Signed-off-by: Michael Adam <ob...@samba.org> Reviewed-by: Andreas Schneider <a...@samba.org> commit f04ea324f6079e74532ee1ae085945097073a305 Author: Michael Adam <ob...@samba.org> Date: Fri May 30 13:21:00 2014 +0200 swrap: fix AF_UNSPEC special case in swrap_bind() Signed-off-by: Michael Adam <ob...@samba.org> Reviewed-by: Andreas Schneider <a...@samba.org> commit 97a65a132d1b14cbfd539f327f260237c1afff3d Author: Michael Adam <ob...@samba.org> Date: Fri May 30 13:06:02 2014 +0200 swrap: extend input checks in swrap_bind() Not only check family, but depending on family, also check the length. Signed-off-by: Michael Adam <ob...@samba.org> Reviewed-by: Andreas Schneider <a...@samba.org> commit af40930bebe0322d5876bda2aa36598e1bae2056 Author: Andreas Schneider <a...@samba.org> Date: Wed May 28 10:04:04 2014 +0200 Update the todo list. Signed-off-by: Andreas Schneider <a...@samba.org> Reviewed-by: Michael Adam <ob...@samba.org> commit b357525a2958e394fceb8c7b6165b572e944e8f2 Author: Andreas Schneider <a...@samba.org> Date: Wed May 28 09:46:57 2014 +0200 tests: Avoid using getenv() to retrieve the path. CID 17221 Signed-off-by: Andreas Schneider <a...@samba.org> Reviewed-by: Michael Adam <ob...@samba.org> commit 3029067b21775c1f6fec43e0b03712defa69e9a6 Author: Michael Adam <ob...@samba.org> Date: Sun Jun 1 01:48:15 2014 +0200 swrap: add check for rpc/rpc.h - needed on freebsd for bindresvport Signed-off-by: Michael Adam <ob...@samba.org> Reviewed-by: Andreas Schneider <a...@samba.org> commit 00258577e86f4b6c15d504954ee6233086638a3e Author: Andreas Schneider <a...@samba.org> Date: Wed May 28 09:46:40 2014 +0200 tests: Add tests for bindresvport(). Signed-off-by: Andreas Schneider <a...@samba.org> Reviewed-by: Michael Adam <ob...@samba.org> commit ccf4e64fe7bf788c17ca1a41e456e3b9ecdcb85b Author: Andreas Schneider <a...@samba.org> Date: Tue May 20 20:04:02 2014 +0200 swrap: Add support for bindresvport(). Signed-off-by: Andreas Schneider <a...@samba.org> Reviewed-by: Michael Adam <ob...@samba.org> commit b124fcb2dfb6193f198bd47891c3286e527bd530 Author: Andreas Schneider <a...@samba.org> Date: Wed May 21 09:15:10 2014 +0200 tests: Add tests for bind(). Signed-off-by: Andreas Schneider <a...@samba.org> Reviewed-by: Michael Adam <ob...@samba.org> commit 45eb7c9070fdd99b63fe01d31e26616d8f047f71 Author: Andreas Schneider <a...@samba.org> Date: Wed May 21 08:30:53 2014 +0200 swrap: Add missing family check in bind(). Signed-off-by: Andreas Schneider <a...@samba.org> Reviewed-by: Michael Adam <ob...@samba.org> commit 37b472d7972f0d18bd343137d4991f5e091a2ed7 Author: Andreas Schneider <a...@samba.org> Date: Tue May 27 20:53:51 2014 +0200 tests: Add test that getsockname is correct after socket(). Signed-off-by: Andreas Schneider <a...@samba.org> Reviewed-by: Michael Adam <ob...@samba.org> commit bdbbf3853c644da05e1fd74c523380a846394cec Author: Andreas Schneider <a...@samba.org> Date: Tue May 27 20:43:51 2014 +0200 swrap: Setup myname in swrap_socket() for getsockname(). Signed-off-by: Andreas Schneider <a...@samba.org> Reviewed-by: Michael Adam <ob...@samba.org> commit 205d5c2348a5ae4858c2fe07a8b3080b19c900be Author: Andreas Schneider <a...@samba.org> Date: Fri May 30 13:59:43 2014 +0200 torture: Wait for the pidfile and then start the tests. Signed-off-by: Andreas Schneider <a...@samba.org> Reviewed-by: Michael Adam <ob...@samba.org> commit 323feb7398527294d1102e24470c0761b1661d99 Author: Andreas Schneider <a...@samba.org> Date: Fri May 30 13:57:25 2014 +0200 echo_srv: Write pidfile after we setup the listeners. Signed-off-by: Andreas Schneider <a...@samba.org> Reviewed-by: Michael Adam <ob...@samba.org> ----------------------------------------------------------------------- Summary of changes: ConfigureChecks.cmake | 5 + TODO | 7 +- config.h.cmake | 2 + src/socket_wrapper.c | 287 ++++++++++++++++++++++++ tests/CMakeLists.txt | 2 + tests/echo_srv.c | 20 +- tests/test_echo_tcp_bind.c | 494 ++++++++++++++++++++++++++++++++++++++++++ tests/test_echo_tcp_socket.c | 69 ++++++ tests/test_ioctl.c | 14 +- tests/torture.c | 14 +- 10 files changed, 896 insertions(+), 18 deletions(-) create mode 100644 tests/test_echo_tcp_bind.c create mode 100644 tests/test_echo_tcp_socket.c Changeset truncated at 500 lines: diff --git a/ConfigureChecks.cmake b/ConfigureChecks.cmake index 3a31f50..df65411 100644 --- a/ConfigureChecks.cmake +++ b/ConfigureChecks.cmake @@ -52,6 +52,7 @@ check_include_file(sys/signalfd.h HAVE_SYS_SIGNALFD_H) check_include_file(sys/eventfd.h HAVE_SYS_EVENTFD_H) check_include_file(sys/timerfd.h HAVE_SYS_TIMERFD_H) check_include_file(gnu/lib-names.h HAVE_GNU_LIB_NAMES_H) +check_include_file(rpc/rpc.h HAVE_RPC_RPC_H) # FUNCTIONS check_function_exists(strncpy HAVE_STRNCPY) @@ -60,6 +61,10 @@ check_function_exists(snprintf HAVE_SNPRINTF) check_function_exists(signalfd HAVE_SIGNALFD) check_function_exists(eventfd HAVE_EVENTFD) check_function_exists(timerfd_create HAVE_TIMERFD_CREATE) +set(CMAKE_REQUIRED_FLAGS -D_GNU_SOURCE) +check_function_exists(bindresvport HAVE_BINDRESVPORT) +set(CMAKE_REQUIRED_FLAGS) + if (UNIX) if (NOT LINUX) diff --git a/TODO b/TODO index 205cfaf..fe05c75 100644 --- a/TODO +++ b/TODO @@ -1,10 +1,14 @@ TODO ===== +This is the TODO list of this project. It should give you some hints of things +which need to be implemented. If you can spend some time on this project, then +look at the list below. + + Library: --------- -* Add IP_PKTINFO support for sendmsg/recvmsg. * Add support for fd passing in sendmsg/recvmsg. Scenario: We accept a connection from a client and need to pass the fd to another @@ -12,6 +16,7 @@ Library: to the child first and set it up there. Or do it like swrap_accept() and call getpeername() and getsockname(). * Add support for threading. +* Use realpath() in socket_wrapper_dir(). Testing: --------- diff --git a/config.h.cmake b/config.h.cmake index 02c016e..466b951 100644 --- a/config.h.cmake +++ b/config.h.cmake @@ -19,6 +19,7 @@ #cmakedefine HAVE_SYS_EVENTFD_H 1 #cmakedefine HAVE_SYS_TIMERFD_H 1 #cmakedefine HAVE_GNU_LIB_NAMES_H 1 +#cmakedefine HAVE_RPC_RPC_H 1 /**************************** STRUCTS ****************************/ @@ -37,6 +38,7 @@ #cmakedefine HAVE_SIGNALFD 1 #cmakedefine HAVE_EVENTFD 1 #cmakedefine HAVE_TIMERFD_CREATE 1 +#cmakedefine HAVE_BINDRESVPORT 1 #cmakedefine HAVE_ACCEPT_PSOCKLEN_T 1 #cmakedefine HAVE_IOCTL_INT 1 diff --git a/src/socket_wrapper.c b/src/socket_wrapper.c index b8b1ca3..f6adc95 100644 --- a/src/socket_wrapper.c +++ b/src/socket_wrapper.c @@ -76,6 +76,9 @@ #ifdef HAVE_GNU_LIB_NAMES_H #include <gnu/lib-names.h> #endif +#ifdef HAVE_RPC_RPC_H +#include <rpc/rpc.h> +#endif enum swrap_dbglvl_e { SWRAP_LOG_ERROR = 0, @@ -111,6 +114,13 @@ enum swrap_dbglvl_e { #define ZERO_STRUCT(x) memset((char *)&(x), 0, sizeof(x)) #endif +#ifndef ZERO_STRUCTP +#define ZERO_STRUCTP(x) do { \ + if ((x) != NULL) \ + memset((char *)(x), 0, sizeof(*(x))); \ + } while(0) +#endif + #ifndef discard_const #define discard_const(ptr) ((void *)((uintptr_t)(ptr))) #endif @@ -843,6 +853,7 @@ static const char *socket_wrapper_dir(void) if (s == NULL) { return NULL; } + /* TODO use realpath(3) here, when we add support for threads */ if (strncmp(s, "./", 2) == 0) { s += 2; } @@ -1256,6 +1267,89 @@ static struct socket_info *find_socket_info(int fd) return NULL; } +static bool check_addr_port_in_use(const struct sockaddr *sa, socklen_t len) +{ + struct socket_info *s; + + /* first catch invalid input */ + switch (sa->sa_family) { + case AF_INET: + if (len < sizeof(struct sockaddr_in)) { + return false; + } + break; +#if HAVE_IPV6 + case AF_INET6: + if (len < sizeof(struct sockaddr_in6)) { + return false; + } + break; +#endif + default: + return false; + break; + } + + for (s = sockets; s != NULL; s = s->next) { + if (s->myname == NULL) { + continue; + } + if (s->myname->sa_family != sa->sa_family) { + continue; + } + switch (s->myname->sa_family) { + case AF_INET: { + struct sockaddr_in *sin1, *sin2; + + sin1 = (struct sockaddr_in *)s->myname; + sin2 = (struct sockaddr_in *)sa; + + if (sin1->sin_addr.s_addr == htonl(INADDR_ANY)) { + continue; + } + if (sin1->sin_port != sin2->sin_port) { + continue; + } + if (sin1->sin_addr.s_addr != sin2->sin_addr.s_addr) { + continue; + } + + /* found */ + return true; + break; + } +#if HAVE_IPV6 + case AF_INET6: { + struct sockaddr_in6 *sin1, *sin2; + + sin1 = (struct sockaddr_in6 *)s->myname; + sin2 = (struct sockaddr_in6 *)sa; + + if (sin1->sin6_port != sin2->sin6_port) { + continue; + } + if (!IN6_ARE_ADDR_EQUAL(&sin1->sin6_addr, + &sin2->sin6_addr)) + { + continue; + } + + /* found */ + return true; + break; + } +#endif + default: + continue; + break; + + } + } + + return false; +} + + static void swrap_remove_stale(int fd) { struct socket_info *si = find_socket_info(fd); @@ -1298,6 +1392,26 @@ static int sockaddr_convert_to_un(struct socket_info *si, #endif switch (in_addr->sa_family) { + case AF_UNSPEC: { + struct sockaddr_in *sin; + if (si->family != AF_INET) { + break; + } + if (in_len < sizeof(struct sockaddr_in)) { + break; + } + sin = (struct sockaddr_in *)in_addr; + if(sin->sin_addr.s_addr != htonl(INADDR_ANY)) { + break; + } + + /* + * Note: in the special case of AF_UNSPEC and INADDR_ANY, + * AF_UNSPEC is mapped to AF_INET and must be treated here. + */ + + /* FALL THROUGH */ + } case AF_INET: #ifdef HAVE_IPV6 case AF_INET6: @@ -2223,8 +2337,40 @@ static int swrap_socket(int family, int type, int protocol) si->type = real_type; si->protocol = protocol; + /* + * Setup myname so getsockname() can succeed to find out the socket + * type. + */ + switch(si->family) { + case AF_INET: { + struct sockaddr_in sin = { + .sin_family = AF_INET, + }; + + si->myname_len = sizeof(struct sockaddr_in); + si->myname = sockaddr_dup(&sin, si->myname_len); + break; + } + case AF_INET6: { + struct sockaddr_in6 sin6 = { + .sin6_family = AF_INET6, + }; + + si->myname_len = sizeof(struct sockaddr_in6); + si->myname = sockaddr_dup(&sin6, si->myname_len); + break; + } + default: + free(si); + errno = EINVAL; + return -1; + } + fi = (struct socket_info_fd *)calloc(1, sizeof(struct socket_info_fd)); if (fi == NULL) { + if (si->myname != NULL) { + free (si->myname); + } free(si); errno = ENOMEM; return -1; @@ -2503,6 +2649,7 @@ static int swrap_auto_bind(int fd, struct socket_info *si, int family) in.sin_addr.s_addr = htonl(127<<24 | socket_wrapper_default_iface()); + free(si->myname); si->myname_len = sizeof(in); si->myname = sockaddr_dup(&in, si->myname_len); break; @@ -2532,6 +2679,7 @@ static int swrap_auto_bind(int fd, struct socket_info *si, int family) in6.sin6_family = AF_INET6; in6.sin6_addr = *swrap_ipv6(); in6.sin6_addr.s6_addr[15] = socket_wrapper_default_iface(); + free(si->myname); si->myname_len = sizeof(in6); si->myname = sockaddr_dup(&in6, si->myname_len); break; @@ -2680,11 +2828,70 @@ static int swrap_bind(int s, const struct sockaddr *myaddr, socklen_t addrlen) int ret; struct sockaddr_un un_addr; struct socket_info *si = find_socket_info(s); + int bind_error = 0; + bool in_use; if (!si) { return libc_bind(s, myaddr, addrlen); } + switch (si->family) { + case AF_INET: { + const struct sockaddr_in *sin; + if (addrlen < sizeof(struct sockaddr_in)) { + bind_error = EINVAL; + break; + } + + sin = (struct sockaddr_in *)myaddr; + + if (sin->sin_family != AF_INET) { + bind_error = EAFNOSUPPORT; + } + + /* special case for AF_UNSPEC */ + if (sin->sin_family == AF_UNSPEC && + (sin->sin_addr.s_addr == htonl(INADDR_ANY))) + { + bind_error = 0; + } + + break; + } +#ifdef HAVE_IPV6 + case AF_INET6: { + const struct sockaddr_in6 *sin6; + if (addrlen < sizeof(struct sockaddr_in6)) { + bind_error = EINVAL; + break; + } + + sin6 = (struct sockaddr_in6 *)myaddr; + + if (sin6->sin6_family != AF_INET6) { + bind_error = EAFNOSUPPORT; + } + + break; + } +#endif + default: + bind_error = EINVAL; + break; + } + + if (bind_error != 0) { + errno = bind_error; + return -1; + } + + in_use = check_addr_port_in_use(myaddr, addrlen); + if (in_use) { + errno = EADDRINUSE; + return -1; + } + + free(si->myname); si->myname_len = addrlen; si->myname = sockaddr_dup(myaddr, addrlen); @@ -2713,6 +2920,86 @@ int bind(int s, const struct sockaddr *myaddr, socklen_t addrlen) } /**************************************************************************** + * BINDRESVPORT + ***************************************************************************/ + +#ifdef HAVE_BINDRESVPORT +static int swrap_getsockname(int s, struct sockaddr *name, socklen_t *addrlen); + +static int swrap_bindresvport_sa(int sd, struct sockaddr *sa) +{ + struct sockaddr_storage myaddr; + socklen_t salen; + static uint16_t port; + uint16_t i; + int rc = -1; + int af; + +#define SWRAP_STARTPORT 600 +#define SWRAP_ENDPORT (IPPORT_RESERVED - 1) +#define SWRAP_NPORTS (SWRAP_ENDPORT - SWRAP_STARTPORT + 1) + + if (port == 0) { + port = (getpid() % SWRAP_NPORTS) + SWRAP_STARTPORT; + } + + if (sa == NULL) { + salen = sizeof(struct sockaddr); + sa = (struct sockaddr *)&myaddr; + + rc = swrap_getsockname(sd, (struct sockaddr *)&myaddr, &salen); + if (rc < 0) { + return -1; + } + + af = sa->sa_family; + memset(&myaddr, 0, salen); + } else { + af = sa->sa_family; + } + + for (i = 0; i < SWRAP_NPORTS; i++, port++) { + switch(af) { + case AF_INET: { + struct sockaddr_in *sinp = (struct sockaddr_in *)sa; + + salen = sizeof(struct sockaddr_in); + sinp->sin_port = htons(port); + break; + } + case AF_INET6: { + struct sockaddr_in6 *sin6p = (struct sockaddr_in6 *)sa; + + salen = sizeof(struct sockaddr_in6); + sin6p->sin6_port = htons(port); + break; + } + default: + errno = EAFNOSUPPORT; + return -1; + } + sa->sa_family = af; + + if (port > SWRAP_ENDPORT) { + port = SWRAP_STARTPORT; + } + + rc = swrap_bind(sd, (struct sockaddr *)sa, salen); + if (rc == 0 || errno != EADDRINUSE) { + break; + } + } + + return rc; +} + +int bindresvport(int sockfd, struct sockaddr_in *sinp) +{ + return swrap_bindresvport_sa(sockfd, (struct sockaddr *)sinp); +} +#endif + +/**************************************************************************** * LISTEN ***************************************************************************/ diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 287539b..d3a4156 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -20,7 +20,9 @@ target_link_libraries(${TORTURE_LIBRARY} set(SWRAP_TESTS test_ioctl + test_echo_tcp_socket test_echo_tcp_connect + test_echo_tcp_bind test_echo_tcp_socket_options test_echo_tcp_write_read test_echo_tcp_writev_readv diff --git a/tests/echo_srv.c b/tests/echo_srv.c index 88d8170..e3973f5 100644 --- a/tests/echo_srv.c +++ b/tests/echo_srv.c @@ -153,7 +153,7 @@ static int pidfile(const char *path) return 0; } -static int become_daemon(struct echo_srv_opts *opts) +static int become_daemon(void) { int ret; pid_t child_pid; @@ -178,13 +178,6 @@ static int become_daemon(struct echo_srv_opts *opts) chdir(WORKING_DIR); #endif - ret = pidfile(opts->pidfile); - if (ret != 0) { - fprintf(stderr, "Cannot create pidfile %s: %s\n", - opts->pidfile, strerror(ret)); - return ret; - } - ret = setsid(); if (ret == -1) { ret = errno; @@ -895,7 +888,7 @@ int main(int argc, char **argv) } if (opts.daemon) { - ret = become_daemon(&opts); + ret = become_daemon(); if (ret != 0) { fprintf(stderr, "Cannot become daemon: %s\n", strerror(ret)); goto done; @@ -908,6 +901,15 @@ int main(int argc, char **argv) goto done; } + if (opts.daemon && opts.pidfile != NULL) { + ret = pidfile(opts.pidfile); + if (ret != 0) { + fprintf(stderr, "Cannot create pidfile %s: %s\n", + opts.pidfile, strerror(ret)); + goto done; + } -- Socket Wrapper Repository