From 77110bc9e8a09ebefaa42eb4fd3a7449373fec9a Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Mon, 16 Jan 2023 22:13:35 +0100 Subject: [PATCH] third_party: Update socket_wrapper to version 1.4.0 The key feature is support for sendmmsg and recvmmsg, which is required by modern libuv versions, e.g. nsupdate -g makes use of libuv, so we need this for samba. Signed-off-by: Stefan Metzmacher Reviewed-by: Andreas Schneider --- third_party/socket_wrapper/socket_wrapper.c | 887 +++++++++++++++++++- third_party/socket_wrapper/wscript | 14 + 2 files changed, 884 insertions(+), 17 deletions(-) diff --git a/third_party/socket_wrapper/socket_wrapper.c b/third_party/socket_wrapper/socket_wrapper.c index bedda07a72b..bf4a976eaee 100644 --- a/third_party/socket_wrapper/socket_wrapper.c +++ b/third_party/socket_wrapper/socket_wrapper.c @@ -47,6 +47,12 @@ #include #include #include +#ifdef HAVE_SYS_SYSCALL_H +#include +#endif +#ifdef HAVE_SYSCALL_H +#include +#endif #include #include #ifdef HAVE_SYS_FILIO_H @@ -537,8 +543,29 @@ typedef int (*__libc_recvfrom)(int sockfd, struct sockaddr *src_addr, socklen_t *addrlen); typedef int (*__libc_recvmsg)(int sockfd, const struct msghdr *msg, int flags); +#ifdef HAVE_RECVMMSG +#if defined(HAVE_RECVMMSG_SSIZE_T_CONST_TIMEOUT) +/* FreeBSD */ +typedef ssize_t (*__libc_recvmmsg)(int sockfd, struct mmsghdr *msgvec, size_t vlen, int flags, const struct timespec *timeout); +#elif defined(HAVE_RECVMMSG_CONST_TIMEOUT) +/* Linux legacy glibc < 2.21 */ +typedef int (*__libc_recvmmsg)(int sockfd, struct mmsghdr *msgvec, unsigned int vlen, int flags, const struct timespec *timeout); +#else +/* Linux glibc >= 2.21 */ +typedef int (*__libc_recvmmsg)(int sockfd, struct mmsghdr *msgvec, unsigned int vlen, int flags, struct timespec *timeout); +#endif +#endif /* HAVE_RECVMMSG */ typedef int (*__libc_send)(int sockfd, const void *buf, size_t len, int flags); typedef int (*__libc_sendmsg)(int sockfd, const struct msghdr *msg, int flags); +#ifdef HAVE_SENDMMSG +#if defined(HAVE_SENDMMSG_SSIZE_T) +/* FreeBSD */ +typedef ssize_t (*__libc_sendmmsg)(int sockfd, struct mmsghdr *msgvec, size_t vlen, int flags); +#else +/* Linux */ +typedef int (*__libc_sendmmsg)(int sockfd, struct mmsghdr *msgvec, unsigned int vlen, int flags); +#endif +#endif /* HAVE_SENDMMSG */ typedef int (*__libc_sendto)(int sockfd, const void *buf, size_t len, @@ -560,6 +587,9 @@ typedef int (*__libc_timerfd_create)(int clockid, int flags); #endif typedef ssize_t (*__libc_write)(int fd, const void *buf, size_t count); typedef ssize_t (*__libc_writev)(int fd, const struct iovec *iov, int iovcnt); +#ifdef HAVE_SYSCALL +typedef long int (*__libc_syscall)(long int sysno, ...); +#endif #define SWRAP_SYMBOL_ENTRY(i) \ union { \ @@ -605,8 +635,14 @@ struct swrap_libc_symbols { SWRAP_SYMBOL_ENTRY(recv); SWRAP_SYMBOL_ENTRY(recvfrom); SWRAP_SYMBOL_ENTRY(recvmsg); +#ifdef HAVE_RECVMMSG + SWRAP_SYMBOL_ENTRY(recvmmsg); +#endif SWRAP_SYMBOL_ENTRY(send); SWRAP_SYMBOL_ENTRY(sendmsg); +#ifdef HAVE_SENDMMSG + SWRAP_SYMBOL_ENTRY(sendmmsg); +#endif SWRAP_SYMBOL_ENTRY(sendto); SWRAP_SYMBOL_ENTRY(setsockopt); #ifdef HAVE_SIGNALFD @@ -619,7 +655,32 @@ struct swrap_libc_symbols { #endif SWRAP_SYMBOL_ENTRY(write); SWRAP_SYMBOL_ENTRY(writev); +#ifdef HAVE_SYSCALL + SWRAP_SYMBOL_ENTRY(syscall); +#endif }; +#undef SWRAP_SYMBOL_ENTRY + +#define SWRAP_SYMBOL_ENTRY(i) \ + union { \ + __rtld_default_##i f; \ + void *obj; \ + } _rtld_default_##i + +#ifdef HAVE_SYSCALL +typedef bool (*__rtld_default_uid_wrapper_syscall_valid)(long int sysno); +typedef long int (*__rtld_default_uid_wrapper_syscall_va)(long int sysno, va_list va); +#endif + +struct swrap_rtld_default_symbols { +#ifdef HAVE_SYSCALL + SWRAP_SYMBOL_ENTRY(uid_wrapper_syscall_valid); + SWRAP_SYMBOL_ENTRY(uid_wrapper_syscall_va); +#else + uint8_t dummy; +#endif +}; +#undef SWRAP_SYMBOL_ENTRY struct swrap { struct { @@ -627,6 +688,10 @@ struct swrap { void *socket_handle; struct swrap_libc_symbols symbols; } libc; + + struct { + struct swrap_rtld_default_symbols symbols; + } rtld_default; }; static struct swrap swrap; @@ -807,6 +872,11 @@ static void _swrap_mutex_unlock(pthread_mutex_t *mutex, const char *name, const #define swrap_bind_symbol_libsocket(sym_name) \ _swrap_bind_symbol_generic(SWRAP_LIBSOCKET, sym_name) +#define swrap_bind_symbol_rtld_default_optional(sym_name) do { \ + swrap.rtld_default.symbols._rtld_default_##sym_name.obj = \ + dlsym(RTLD_DEFAULT, #sym_name); \ +} while(0); + static void swrap_bind_symbol_all(void); /**************************************************************************** @@ -1131,6 +1201,24 @@ static int libc_recvmsg(int sockfd, struct msghdr *msg, int flags) return swrap.libc.symbols._libc_recvmsg.f(sockfd, msg, flags); } +#ifdef HAVE_RECVMMSG +#if defined(HAVE_RECVMMSG_SSIZE_T_CONST_TIMEOUT) +/* FreeBSD */ +static ssize_t libc_recvmmsg(int sockfd, struct mmsghdr *msgvec, size_t vlen, int flags, const struct timespec *timeout) +#elif defined(HAVE_RECVMMSG_CONST_TIMEOUT) +/* Linux legacy glibc < 2.21 */ +static int libc_recvmmsg(int sockfd, struct mmsghdr *msgvec, unsigned int vlen, int flags, const struct timespec *timeout) +#else +/* Linux glibc >= 2.21 */ +static int libc_recvmmsg(int sockfd, struct mmsghdr *msgvec, unsigned int vlen, int flags, struct timespec *timeout) +#endif +{ + swrap_bind_symbol_all(); + + return swrap.libc.symbols._libc_recvmmsg.f(sockfd, msgvec, vlen, flags, timeout); +} +#endif + static int libc_send(int sockfd, const void *buf, size_t len, int flags) { swrap_bind_symbol_all(); @@ -1145,6 +1233,21 @@ static int libc_sendmsg(int sockfd, const struct msghdr *msg, int flags) return swrap.libc.symbols._libc_sendmsg.f(sockfd, msg, flags); } +#ifdef HAVE_SENDMMSG +#if defined(HAVE_SENDMMSG_SSIZE_T) +/* FreeBSD */ +static ssize_t libc_sendmmsg(int sockfd, struct mmsghdr *msgvec, size_t vlen, int flags) +#else +/* Linux */ +static int libc_sendmmsg(int sockfd, struct mmsghdr *msgvec, unsigned int vlen, int flags) +#endif +{ + swrap_bind_symbol_all(); + + return swrap.libc.symbols._libc_sendmmsg.f(sockfd, msgvec, vlen, flags); +} +#endif + static int libc_sendto(int sockfd, const void *buf, size_t len, @@ -1223,6 +1326,64 @@ static ssize_t libc_writev(int fd, const struct iovec *iov, int iovcnt) return swrap.libc.symbols._libc_writev.f(fd, iov, iovcnt); } +#ifdef HAVE_SYSCALL +DO_NOT_SANITIZE_ADDRESS_ATTRIBUTE +static long int libc_vsyscall(long int sysno, va_list va) +{ + long int args[8]; + long int rc; + int i; + + swrap_bind_symbol_all(); + + for (i = 0; i < 8; i++) { + args[i] = va_arg(va, long int); + } + + rc = swrap.libc.symbols._libc_syscall.f(sysno, + args[0], + args[1], + args[2], + args[3], + args[4], + args[5], + args[6], + args[7]); + + return rc; +} + +static bool swrap_uwrap_syscall_valid(long int sysno) +{ + swrap_bind_symbol_all(); + + if (swrap.rtld_default.symbols._rtld_default_uid_wrapper_syscall_valid.f == NULL) { + return false; + } + + return swrap.rtld_default.symbols._rtld_default_uid_wrapper_syscall_valid.f( + sysno); +} + +DO_NOT_SANITIZE_ADDRESS_ATTRIBUTE +static long int swrap_uwrap_syscall_va(long int sysno, va_list va) +{ + swrap_bind_symbol_all(); + + if (swrap.rtld_default.symbols._rtld_default_uid_wrapper_syscall_va.f == NULL) { + /* + * Fallback to libc, if uid_wrapper_syscall_va is not + * available. + */ + return libc_vsyscall(sysno, va); + } + + return swrap.rtld_default.symbols._rtld_default_uid_wrapper_syscall_va.f( + sysno, + va); +} +#endif /* HAVE_SYSCALL */ + /* DO NOT call this function during library initialization! */ static void __swrap_bind_symbol_all_once(void) { @@ -1263,8 +1424,14 @@ static void __swrap_bind_symbol_all_once(void) swrap_bind_symbol_libsocket(recv); swrap_bind_symbol_libsocket(recvfrom); swrap_bind_symbol_libsocket(recvmsg); +#ifdef HAVE_RECVMMSG + swrap_bind_symbol_libsocket(recvmmsg); +#endif swrap_bind_symbol_libsocket(send); swrap_bind_symbol_libsocket(sendmsg); +#ifdef HAVE_SENDMMSG + swrap_bind_symbol_libsocket(sendmmsg); +#endif swrap_bind_symbol_libsocket(sendto); swrap_bind_symbol_libsocket(setsockopt); #ifdef HAVE_SIGNALFD @@ -1277,6 +1444,11 @@ static void __swrap_bind_symbol_all_once(void) #endif swrap_bind_symbol_libc(write); swrap_bind_symbol_libsocket(writev); +#ifdef HAVE_SYSCALL + swrap_bind_symbol_libc(syscall); + swrap_bind_symbol_rtld_default_optional(uid_wrapper_syscall_valid); + swrap_bind_symbol_rtld_default_optional(uid_wrapper_syscall_va); +#endif } static void swrap_bind_symbol_all(void) @@ -1429,6 +1601,55 @@ static size_t socket_length(int family) return 0; } +struct swrap_sockaddr_buf { + char str[128]; +}; + +static const char *swrap_sockaddr_string(struct swrap_sockaddr_buf *buf, + const struct sockaddr *saddr) +{ + unsigned int port = 0; + char addr[64] = {0,}; + + switch (saddr->sa_family) { + case AF_INET: { + const struct sockaddr_in *in = + (const struct sockaddr_in *)(const void *)saddr; + + port = ntohs(in->sin_port); + + inet_ntop(saddr->sa_family, + &in->sin_addr, + addr, sizeof(addr)); + break; + } +#ifdef HAVE_IPV6 + case AF_INET6: { + const struct sockaddr_in6 *in6 = + (const struct sockaddr_in6 *)(const void *)saddr; + + port = ntohs(in6->sin6_port); + + inet_ntop(saddr->sa_family, + &in6->sin6_addr, + addr, sizeof(addr)); + break; + } +#endif + default: + snprintf(addr, sizeof(addr), + "", + saddr->sa_family); + break; + } + + snprintf(buf->str, sizeof(buf->str), + "addr[%s]/port[%u]", + addr, port); + + return buf->str; +} + static struct socket_info *swrap_get_socket_info(int si_index) { return (struct socket_info *)(&(sockets[si_index].info)); @@ -2064,13 +2285,10 @@ static int convert_in_un_remote(struct socket_info *si, const struct sockaddr *i type = u_type; iface = (addr & 0x000000FF); } else { - char str[256] = {0,}; - inet_ntop(inaddr->sa_family, - &in->sin_addr, - str, sizeof(str)); + struct swrap_sockaddr_buf buf = {}; SWRAP_LOG(SWRAP_LOG_WARN, - "str[%s] prt[%u]", - str, (unsigned)prt); + "%s", + swrap_sockaddr_string(&buf, inaddr)); errno = ENETUNREACH; return -1; } @@ -2106,13 +2324,10 @@ static int convert_in_un_remote(struct socket_info *si, const struct sockaddr *i if (IN6_ARE_ADDR_EQUAL(&cmp1, &cmp2)) { iface = in->sin6_addr.s6_addr[15]; } else { - char str[256] = {0,}; - inet_ntop(inaddr->sa_family, - &in->sin6_addr, - str, sizeof(str)); + struct swrap_sockaddr_buf buf = {}; SWRAP_LOG(SWRAP_LOG_WARN, - "str[%s] prt[%u]", - str, (unsigned)prt); + "%s", + swrap_sockaddr_string(&buf, inaddr)); errno = ENETUNREACH; return -1; } @@ -3984,6 +4199,7 @@ static int swrap_connect(int s, const struct sockaddr *serv_addr, .sa_socklen = sizeof(struct sockaddr_un), }; struct socket_info *si = find_socket_info(s); + struct swrap_sockaddr_buf buf = {}; int bcast = 0; if (!si) { @@ -4032,7 +4248,8 @@ static int swrap_connect(int s, const struct sockaddr *serv_addr, } SWRAP_LOG(SWRAP_LOG_TRACE, - "connect() path=%s, fd=%d", + "connect(%s) path=%s, fd=%d", + swrap_sockaddr_string(&buf, serv_addr), un_addr.sa.un.sun_path, s); @@ -4098,6 +4315,8 @@ static int swrap_bind(int s, const struct sockaddr *myaddr, socklen_t addrlen) .sa_socklen = sizeof(struct sockaddr_un), }; struct socket_info *si = find_socket_info(s); + struct swrap_sockaddr_buf buf = {}; + int ret_errno = errno; int bind_error = 0; #if 0 /* FIXME */ bool in_use; @@ -4155,7 +4374,7 @@ static int swrap_bind(int s, const struct sockaddr *myaddr, socklen_t addrlen) } if (bind_error != 0) { - errno = bind_error; + ret_errno = bind_error; ret = -1; goto out; } @@ -4179,16 +4398,21 @@ static int swrap_bind(int s, const struct sockaddr *myaddr, socklen_t addrlen) 1, &si->bcast); if (ret == -1) { + ret_errno = errno; goto out; } unlink(un_addr.sa.un.sun_path); ret = libc_bind(s, &un_addr.sa.s, un_addr.sa_socklen); + if (ret == -1) { + ret_errno = errno; + } SWRAP_LOG(SWRAP_LOG_TRACE, - "bind() path=%s, fd=%d", - un_addr.sa.un.sun_path, s); + "bind(%s) path=%s, fd=%d ret=%d ret_errno=%d", + swrap_sockaddr_string(&buf, myaddr), + un_addr.sa.un.sun_path, s, ret, ret_errno); if (ret == 0) { si->bound = 1; @@ -4196,7 +4420,7 @@ static int swrap_bind(int s, const struct sockaddr *myaddr, socklen_t addrlen) out: SWRAP_UNLOCK_SI(si); - + errno = ret_errno; return ret; } @@ -6120,6 +6344,7 @@ static ssize_t swrap_sendmsg_before(int fd, { size_t i, len = 0; ssize_t ret = -1; + struct swrap_sockaddr_buf buf = {}; if (to_un) { *to_un = NULL; @@ -6181,6 +6406,10 @@ static ssize_t swrap_sendmsg_before(int fd, msg->msg_name = NULL; msg->msg_namelen = 0; } + SWRAP_LOG(SWRAP_LOG_TRACE, + "connected(%s) fd=%d", + swrap_sockaddr_string(&buf, &si->peername.sa.s), + fd); } else { const struct sockaddr *msg_name; msg_name = (const struct sockaddr *)msg->msg_name; @@ -6235,6 +6464,11 @@ static ssize_t swrap_sendmsg_before(int fd, goto out; } + SWRAP_LOG(SWRAP_LOG_TRACE, + "deferred connect(%s) path=%s, fd=%d", + swrap_sockaddr_string(&buf, &si->peername.sa.s), + tmp_un->sun_path, fd); + ret = libc_connect(fd, (struct sockaddr *)(void *)tmp_un, sizeof(*tmp_un)); @@ -7135,6 +7369,219 @@ ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags) return swrap_recvmsg(sockfd, msg, flags); } +/**************************************************************************** + * RECVMMSG + ***************************************************************************/ + +#ifdef HAVE_RECVMMSG +#if defined(HAVE_RECVMMSG_SSIZE_T_CONST_TIMEOUT) +/* FreeBSD */ +static ssize_t swrap_recvmmsg(int s, struct mmsghdr *omsgvec, size_t _vlen, int flags, const struct timespec *timeout) +#elif defined(HAVE_RECVMMSG_CONST_TIMEOUT) +/* Linux legacy glibc < 2.21 */ +static int swrap_recvmmsg(int s, struct mmsghdr *omsgvec, unsigned int _vlen, int flags, const struct timespec *timeout) +#else +/* Linux glibc >= 2.21 */ +static int swrap_recvmmsg(int s, struct mmsghdr *omsgvec, unsigned int _vlen, int flags, struct timespec *timeout) +#endif +{ + struct socket_info *si = find_socket_info(s); +#define __SWRAP_RECVMMSG_MAX_VLEN 16 + struct mmsghdr msgvec[__SWRAP_RECVMMSG_MAX_VLEN] = {}; + struct { + struct iovec iov; + struct swrap_address from_addr; + struct swrap_address convert_addr; +#ifdef HAVE_STRUCT_MSGHDR_MSG_CONTROL + size_t msg_ctrllen_filled; + size_t msg_ctrllen_left; +#endif + } tmp[__SWRAP_RECVMMSG_MAX_VLEN] = {}; + int vlen; + int i; + int ret; + int rc; + int saved_errno; + + if (_vlen > __SWRAP_RECVMMSG_MAX_VLEN) { + vlen = __SWRAP_RECVMMSG_MAX_VLEN; + } else { + vlen = _vlen; + } + + if (si == NULL) { + uint8_t *tmp_control[__SWRAP_RECVMMSG_MAX_VLEN] = { NULL, }; + + for (i = 0; i < vlen; i++) { + struct msghdr *omsg = &omsgvec[i].msg_hdr; + struct msghdr *msg = &msgvec[i].msg_hdr; + + rc = swrap_recvmsg_before_unix(omsg, msg, + &tmp_control[i]); + if (rc < 0) { + ret = rc; + goto fail_libc; + } + } + + ret = libc_recvmmsg(s, msgvec, vlen, flags, timeout); + if (ret < 0) { + goto fail_libc; + } + + for (i = 0; i < ret; i++) { + omsgvec[i].msg_len = msgvec[i].msg_len; + } + +fail_libc: + saved_errno = errno; + for (i = 0; i < vlen; i++) { + struct msghdr *omsg = &omsgvec[i].msg_hdr; + struct msghdr *msg = &msgvec[i].msg_hdr; + + if (i == 0 || i < ret) { + swrap_recvmsg_after_unix(msg, &tmp_control[i], omsg, ret); + } + SAFE_FREE(tmp_control[i]); + } + errno = saved_errno; + + return ret; + } + + for (i = 0; i < vlen; i++) { + struct msghdr *omsg = &omsgvec[i].msg_hdr; + struct msghdr *msg = &msgvec[i].msg_hdr; + + tmp[i].from_addr.sa_socklen = sizeof(struct sockaddr_un); + tmp[i].convert_addr.sa_socklen = sizeof(struct sockaddr_storage); + + msg->msg_name = &tmp[i].from_addr.sa; /* optional address */ + msg->msg_namelen = tmp[i].from_addr.sa_socklen; /* size of address */ + msg->msg_iov = omsg->msg_iov; /* scatter/gather array */ + msg->msg_iovlen = omsg->msg_iovlen; /* # elements in msg_iov */ +#ifdef HAVE_STRUCT_MSGHDR_MSG_CONTROL + tmp[i].msg_ctrllen_filled = 0; + tmp[i].msg_ctrllen_left = omsg->msg_controllen; + + msg->msg_control = omsg->msg_control; /* ancillary data, see below */ + msg->msg_controllen = omsg->msg_controllen; /* ancillary data buffer len */ + msg->msg_flags = omsg->msg_flags; /* flags on received message */ +#endif + + rc = swrap_recvmsg_before(s, si, msg, &tmp[i].iov); + if (rc < 0) { + ret = rc; + goto fail_swrap; + } + } + + ret = libc_recvmmsg(s, msgvec, vlen, flags, timeout); + if (ret < 0) { + goto fail_swrap; + } + + for (i = 0; i < ret; i++) { + omsgvec[i].msg_len = msgvec[i].msg_len; + } + +fail_swrap: + + saved_errno = errno; + for (i = 0; i < vlen; i++) { + struct msghdr *omsg = &omsgvec[i].msg_hdr; + struct msghdr *msg = &msgvec[i].msg_hdr; + + if (!(i == 0 || i < ret)) { + break; + } + +#ifdef HAVE_STRUCT_MSGHDR_MSG_CONTROL + tmp[i].msg_ctrllen_filled += msg->msg_controllen; + tmp[i].msg_ctrllen_left -= msg->msg_controllen; + + if (omsg->msg_control != NULL) { + uint8_t *p; + + p = omsg->msg_control; + p += tmp[i].msg_ctrllen_filled; + + msg->msg_control = p; + msg->msg_controllen = tmp[i].msg_ctrllen_left; + } else { + msg->msg_control = NULL; + msg->msg_controllen = 0; + } +#endif + + /* + * We convert the unix address to a IP address so we need a buffer + * which can store the address in case of SOCK_DGRAM, see below. + */ + msg->msg_name = &tmp[i].convert_addr.sa; + msg->msg_namelen = tmp[i].convert_addr.sa_socklen; + + swrap_recvmsg_after(s, si, msg, + &tmp[i].from_addr.sa.un, + tmp[i].from_addr.sa_socklen, + ret); + +#ifdef HAVE_STRUCT_MSGHDR_MSG_CONTROL + if (omsg->msg_control != NULL) { + /* msg->msg_controllen = space left */ + tmp[i].msg_ctrllen_left = msg->msg_controllen; + tmp[i].msg_ctrllen_filled = omsg->msg_controllen - tmp[i].msg_ctrllen_left; + } + + /* Update the original message length */ + omsg->msg_controllen = tmp[i].msg_ctrllen_filled; + omsg->msg_flags = msg->msg_flags; +#endif + omsg->msg_iovlen = msg->msg_iovlen; + + SWRAP_LOCK_SI(si); + + /* + * From the manpage: + * + * The msg_name field points to a caller-allocated buffer that is + * used to return the source address if the socket is unconnected. The + * caller should set msg_namelen to the size of this buffer before this + * call; upon return from a successful call, msg_name will contain the + * length of the returned address. If the application does not need + * to know the source address, msg_name can be specified as NULL. + */ + if (si->type == SOCK_STREAM) { + omsg->msg_namelen = 0; + } else if (omsg->msg_name != NULL && + omsg->msg_namelen != 0 && + omsg->msg_namelen >= msg->msg_namelen) { + memcpy(omsg->msg_name, msg->msg_name, msg->msg_namelen); + omsg->msg_namelen = msg->msg_namelen; + } + + SWRAP_UNLOCK_SI(si); + } + errno = saved_errno; + + return ret; +} + +#if defined(HAVE_RECVMMSG_SSIZE_T_CONST_TIMEOUT) +/* FreeBSD */ +ssize_t recvmmsg(int sockfd, struct mmsghdr *msgvec, size_t vlen, int flags, const struct timespec *timeout) +#elif defined(HAVE_RECVMMSG_CONST_TIMEOUT) +/* Linux legacy glibc < 2.21 */ +int recvmmsg(int sockfd, struct mmsghdr *msgvec, unsigned int vlen, int flags, const struct timespec *timeout) +#else +/* Linux glibc >= 2.21 */ +int recvmmsg(int sockfd, struct mmsghdr *msgvec, unsigned int vlen, int flags, struct timespec *timeout) +#endif +{ + return swrap_recvmmsg(sockfd, msgvec, vlen, flags, timeout); +} +#endif /* HAVE_RECVMMSG */ + /**************************************************************************** * SENDMSG ***************************************************************************/ @@ -7306,6 +7753,249 @@ ssize_t sendmsg(int s, const struct msghdr *omsg, int flags) return swrap_sendmsg(s, omsg, flags); } +/**************************************************************************** + * SENDMMSG + ***************************************************************************/ + +#ifdef HAVE_SENDMMSG +#if defined(HAVE_SENDMMSG_SSIZE_T) +/* FreeBSD */ +static ssize_t swrap_sendmmsg(int s, struct mmsghdr *omsgvec, size_t _vlen, int flags) +#else +/* Linux */ +static int swrap_sendmmsg(int s, struct mmsghdr *omsgvec, unsigned int _vlen, int flags) +#endif +{ + struct socket_info *si = find_socket_info(s); +#define __SWRAP_SENDMMSG_MAX_VLEN 16 + struct mmsghdr msgvec[__SWRAP_SENDMMSG_MAX_VLEN] = {}; + struct { + struct iovec iov; + struct sockaddr_un un_addr; + const struct sockaddr_un *to_un; + const struct sockaddr *to; + int bcast; + } tmp[__SWRAP_SENDMMSG_MAX_VLEN] = {}; + int vlen; + int i; + char *swrap_dir = NULL; + int connected = 0; + int found_bcast = 0; + int ret; + int rc; + int saved_errno; + + if (_vlen > __SWRAP_SENDMMSG_MAX_VLEN) { + vlen = __SWRAP_SENDMMSG_MAX_VLEN; + } else { + vlen = _vlen; + } + + if (!si) { + int scm_rights_pipe_fd[__SWRAP_SENDMMSG_MAX_VLEN]; + + for (i = 0; i < __SWRAP_SENDMMSG_MAX_VLEN; i++) { + scm_rights_pipe_fd[i] = -1; + } + + for (i = 0; i < vlen; i++) { + struct msghdr *omsg = &omsgvec[i].msg_hdr; + struct msghdr *msg = &msgvec[i].msg_hdr; + + rc = swrap_sendmsg_before_unix(omsg, msg, + &scm_rights_pipe_fd[i]); + if (rc < 0) { + ret = rc; + goto fail_libc; + } + } + + ret = libc_sendmmsg(s, msgvec, vlen, flags); + if (ret < 0) { + goto fail_libc; + } + + for (i = 0; i < ret; i++) { + omsgvec[i].msg_len = msgvec[i].msg_len; + } + +fail_libc: + saved_errno = errno; + for (i = 0; i < vlen; i++) { + struct msghdr *msg = &msgvec[i].msg_hdr; + + swrap_sendmsg_after_unix(msg, ret, + scm_rights_pipe_fd[i]); + } + errno = saved_errno; + + return ret; + } + + SWRAP_LOCK_SI(si); + connected = si->connected; + SWRAP_UNLOCK_SI(si); + + for (i = 0; i < vlen; i++) { + struct msghdr *omsg = &omsgvec[i].msg_hdr; + struct msghdr *msg = &msgvec[i].msg_hdr; + + if (connected == 0) { + msg->msg_name = omsg->msg_name; /* optional address */ + msg->msg_namelen = omsg->msg_namelen; /* size of address */ + } + msg->msg_iov = omsg->msg_iov; /* scatter/gather array */ + msg->msg_iovlen = omsg->msg_iovlen; /* # elements in msg_iov */ + +#ifdef HAVE_STRUCT_MSGHDR_MSG_CONTROL + if (omsg->msg_controllen > 0 && omsg->msg_control != NULL) { + uint8_t *cmbuf = NULL; + size_t cmlen = 0; + + rc = swrap_sendmsg_filter_cmsghdr(omsg, &cmbuf, &cmlen); + if (rc < 0) { + ret = rc; + goto fail_swrap; + } + + if (cmlen != 0) { + msg->msg_control = cmbuf; + msg->msg_controllen = cmlen; + } + } + msg->msg_flags = omsg->msg_flags; /* flags on received message */ +#endif + + rc = swrap_sendmsg_before(s, si, msg, + &tmp[i].iov, + &tmp[i].un_addr, + &tmp[i].to_un, + &tmp[i].to, + &tmp[i].bcast); + if (rc < 0) { + ret = rc; + goto fail_swrap; + } + + if (tmp[i].bcast) { + found_bcast = 1; + } + } + + if (found_bcast) { + + swrap_dir = socket_wrapper_dir(); + if (swrap_dir == NULL) { + ret = -1; + goto fail_swrap; + } + + for (i = 0; i < vlen; i++) { + struct msghdr *msg = &msgvec[i].msg_hdr; + struct sockaddr_un *un_addr = &tmp[i].un_addr; + const struct sockaddr *to = tmp[i].to; + struct stat st; + unsigned int iface; + unsigned int prt = ntohs(((const struct sockaddr_in *)(const void *)to)->sin_port); + char type; + size_t l, len = 0; + uint8_t *buf; + off_t ofs = 0; + size_t avail = 0; + size_t remain; + + for (l = 0; l < (size_t)msg->msg_iovlen; l++) { + avail += msg->msg_iov[l].iov_len; + } + + len = avail; + remain = avail; + + /* we capture it as one single packet */ + buf = (uint8_t *)malloc(remain); + if (!buf) { + ret = -1; + goto fail_swrap; + } + + for (l = 0; l < (size_t)msg->msg_iovlen; l++) { + size_t this_time = MIN(remain, (size_t)msg->msg_iov[l].iov_len); + memcpy(buf + ofs, + msg->msg_iov[l].iov_base, + this_time); + ofs += this_time; + remain -= this_time; + } + + type = SOCKET_TYPE_CHAR_UDP; + + for(iface=0; iface <= MAX_WRAPPED_INTERFACES; iface++) { + swrap_un_path(un_addr, swrap_dir, type, iface, prt); + if (stat(un_addr->sun_path, &st) != 0) continue; + + msg->msg_name = un_addr; /* optional address */ + msg->msg_namelen = sizeof(*un_addr); /* size of address */ + + /* + * ignore the any errors in broadcast sends and + * do a single sendmsg instead of sendmmsg + */ + libc_sendmsg(s, msg, flags); + } + + SWRAP_LOCK_SI(si); + swrap_pcap_dump_packet(si, to, SWRAP_SENDTO, buf, len); + SWRAP_UNLOCK_SI(si); + + SAFE_FREE(buf); + + msgvec[i].msg_len = len; + } + + ret = vlen; + goto bcast_done; + } + + ret = libc_sendmmsg(s, msgvec, vlen, flags); + if (ret < 0) { + goto fail_swrap; + } + +bcast_done: + for (i = 0; i < ret; i++) { + omsgvec[i].msg_len = msgvec[i].msg_len; + } + +fail_swrap: + saved_errno = errno; + for (i = 0; i < vlen; i++) { + struct msghdr *msg = &msgvec[i].msg_hdr; + + if (i == 0 || i < ret) { + swrap_sendmsg_after(s, si, msg, tmp[i].to, ret); + } +#ifdef HAVE_STRUCT_MSGHDR_MSG_CONTROL + SAFE_FREE(msg->msg_control); +#endif + } + SAFE_FREE(swrap_dir); + errno = saved_errno; + + return ret; +} + +#if defined(HAVE_SENDMMSG_SSIZE_T) +/* FreeBSD */ +ssize_t sendmmsg(int s, struct mmsghdr *msgvec, size_t vlen, int flags) +#else +/* Linux */ +int sendmmsg(int s, struct mmsghdr *msgvec, unsigned int vlen, int flags) +#endif +{ + return swrap_sendmmsg(s, msgvec, vlen, flags); +} +#endif /* HAVE_SENDMMSG */ + /**************************************************************************** * READV ***************************************************************************/ @@ -7773,6 +8463,169 @@ int pledge(const char *promises, const char *paths[]) } #endif /* HAVE_PLEDGE */ +#ifdef HAVE_SYSCALL +static bool swrap_is_swrap_related_syscall(long int sysno) +{ + switch (sysno) { +#ifdef SYS_close + case SYS_close: + return true; +#endif /* SYS_close */ + +#ifdef SYS_recvmmsg + case SYS_recvmmsg: + return true; +#endif /* SYS_recvmmsg */ + +#ifdef SYS_sendmmsg + case SYS_sendmmsg: + return true; +#endif /* SYS_sendmmsg */ + + default: + return false; + } +} + +static long int swrap_syscall(long int sysno, va_list vp) +{ + long int rc; + + switch (sysno) { +#ifdef SYS_close + case SYS_close: + { + int fd = (int)va_arg(vp, int); + + SWRAP_LOG(SWRAP_LOG_TRACE, + "calling swrap_close syscall %lu", + sysno); + rc = swrap_close(fd); + } + break; +#endif /* SYS_close */ + +#ifdef SYS_recvmmsg + case SYS_recvmmsg: + { + int fd = (int)va_arg(vp, int); + struct mmsghdr *msgvec = va_arg(vp, struct mmsghdr *); + unsigned int vlen = va_arg(vp, unsigned int); + int flags = va_arg(vp, int); + struct timespec *timeout = va_arg(vp, struct timespec *); + + SWRAP_LOG(SWRAP_LOG_TRACE, + "calling swrap_recvmmsg syscall %lu", + sysno); + rc = swrap_recvmmsg(fd, msgvec, vlen, flags, timeout); + } + break; +#endif /* SYS_recvmmsg */ + +#ifdef SYS_sendmmsg + case SYS_sendmmsg: + { + int fd = (int)va_arg(vp, int); + struct mmsghdr *msgvec = va_arg(vp, struct mmsghdr *); + unsigned int vlen = va_arg(vp, unsigned int); + int flags = va_arg(vp, int); + + SWRAP_LOG(SWRAP_LOG_TRACE, + "calling swrap_sendmmsg syscall %lu", + sysno); + rc = swrap_sendmmsg(fd, msgvec, vlen, flags); + } + break; +#endif /* SYS_sendmmsg */ + + default: + rc = -1; + errno = ENOSYS; + break; + } + + return rc; +} + +#ifdef HAVE_SYSCALL_INT +int syscall(int sysno, ...) +#else +long int syscall(long int sysno, ...) +#endif +{ +#ifdef HAVE_SYSCALL_INT + int rc; +#else + long int rc; +#endif + va_list va; + + va_start(va, sysno); + + /* + * We should only handle the syscall numbers + * we care about... + */ + if (!swrap_is_swrap_related_syscall(sysno)) { + /* + * We need to give socket_wrapper a + * chance to take over... + */ + if (swrap_uwrap_syscall_valid(sysno)) { + rc = swrap_uwrap_syscall_va(sysno, va); + va_end(va); + return rc; + } + + rc = libc_vsyscall(sysno, va); + va_end(va); + return rc; + } + + if (!socket_wrapper_enabled()) { + rc = libc_vsyscall(sysno, va); + va_end(va); + return rc; + } + + rc = swrap_syscall(sysno, va); + va_end(va); + + return rc; +} + +/* used by uid_wrapper */ +bool socket_wrapper_syscall_valid(long int sysno); +bool socket_wrapper_syscall_valid(long int sysno) +{ + if (!swrap_is_swrap_related_syscall(sysno)) { + return false; + } + + if (!socket_wrapper_enabled()) { + return false; + } + + return true; +} + +/* used by uid_wrapper */ +long int socket_wrapper_syscall_va(long int sysno, va_list va); +long int socket_wrapper_syscall_va(long int sysno, va_list va) +{ + if (!swrap_is_swrap_related_syscall(sysno)) { + errno = ENOSYS; + return -1; + } + + if (!socket_wrapper_enabled()) { + return libc_vsyscall(sysno, va); + } + + return swrap_syscall(sysno, va); +} +#endif /* HAVE_SYSCALL */ + static void swrap_thread_prepare(void) { /* diff --git a/third_party/socket_wrapper/wscript b/third_party/socket_wrapper/wscript index b87072038f8..af6eb303e0a 100644 --- a/third_party/socket_wrapper/wscript +++ b/third_party/socket_wrapper/wscript @@ -53,6 +53,7 @@ def configure(conf): conf.CHECK_FUNCS('bindresvport') conf.CHECK_FUNCS('pledge') conf.CHECK_FUNCS('accept4') + conf.CHECK_FUNCS('sendmmsg recvmmsg') conf.CHECK_FUNCS('__close_nocancel') conf.CHECK_FUNCS_IN('bind', @@ -64,6 +65,19 @@ def configure(conf): 'int accept(int s, struct sockaddr *addr, Psocklen_t addrlen)', define='HAVE_ACCEPT_PSOCKLEN_T', headers='sys/types.h sys/socket.h') + # Linux legacy glibc < 2.21 + conf.CHECK_C_PROTOTYPE('recvmmsg', + 'int recvmmsg(int __fd, struct mmsghdr *__vmessages, unsigned int __vlen, int __flags, const struct timespec *__tmo)', + define='HAVE_RECVMMSG_CONST_TIMEOUT', headers='sys/types.h sys/socket.h') + # FreeBSD + conf.CHECK_C_PROTOTYPE('recvmmsg', + 'ssize_t recvmmsg(int __fd, struct mmsghdr * __restrict __vmessages, size_t __vlen, int __flags, const struct timespec * __restrict __tmo)', + define='HAVE_RECVMMSG_SSIZE_T_CONST_TIMEOUT', headers='sys/types.h sys/socket.h') + # FreeBSD + conf.CHECK_C_PROTOTYPE('sendmmsg', + 'ssize_t sendmmsg(int __fd, struct mmsghdr * __restrict __vmessages, size_t __vlen, int __flags)', + define='HAVE_SENDMMSG_SSIZE_T', headers='sys/types.h sys/socket.h') + conf.CHECK_C_PROTOTYPE('ioctl', 'int ioctl(int s, int r, ...)', define='HAVE_IOCTL_INT', headers='unistd.h sys/ioctl.h')