Add unix domain socket support to redbean

This commit is contained in:
Justine Tunney 2022-06-22 03:04:25 -07:00
parent 4b23985b7f
commit fc097ac275
25 changed files with 594 additions and 172 deletions

View file

@ -71,8 +71,8 @@
* Like redbean, greenbean has superior performance too, with an
* advantage on benchmarks biased towards high connection counts
*
* $ wrk -c 300 -t 32 --latency http://10.10.10.124:8080/
* Running 10s test @ http://10.10.10.124:8080/
* $ wrk -c 300 -t 32 --latency http://127.0.0.1:8080/
* Running 10s test @ http://127.0.0.1:8080/
* 32 threads and 300 connections
* Thread Stats Avg Stdev Max +/- Stdev
* Latency 661.06us 5.11ms 96.22ms 98.85%

View file

@ -93,12 +93,21 @@ static int ioctl_siocgifconf_sysv(int fd, struct ifconf *ifc) {
return rc;
}
forceinline void Sockaddr2linux(void *saddr) {
char *p;
if (saddr) {
p = saddr;
p[0] = p[1];
p[1] = 0;
}
}
/* Used for all the ioctl that returns sockaddr structure that
* requires adjustment between Linux and XNU
*/
static int ioctl_siocgifaddr_sysv(int fd, uint64_t op, struct ifreq *ifr) {
if (sys_ioctl(fd, op, ifr) == -1) return -1;
if (IsBsd()) sockaddr2linux(&ifr->ifr_addr);
if (IsBsd()) Sockaddr2linux(&ifr->ifr_addr);
return 0;
}

View file

@ -21,8 +21,15 @@
int sys_accept(int server, void *addr, uint32_t *addrsize) {
int client;
if ((client = __sys_accept(server, addr, addrsize, 0)) != -1 && IsBsd()) {
sockaddr2linux(addr);
uint32_t size;
union sockaddr_storage_bsd bsd;
if (!IsBsd()) {
client = __sys_accept(server, addr, addrsize, 0);
} else {
size = sizeof(bsd);
if ((client = __sys_accept(server, &bsd, &size, 0)) != -1) {
sockaddr2linux(&bsd, size, addr, addrsize);
}
}
return client;
}

View file

@ -21,27 +21,26 @@
#include "libc/sock/internal.h"
#include "libc/sock/sock.h"
#define __NR_accept4_linux 0x0120 /* rhel5:enosysevil */
int sys_accept4(int server, void *addr, uint32_t *addrsize, int flags) {
static bool once, demodernize;
if (!flags) return sys_accept(server, addr, addrsize);
int olderr, client;
if (!flags || demodernize) goto TriedAndTrue;
union sockaddr_storage_bsd bsd;
uint32_t size = sizeof(bsd);
void *out_addr = !IsBsd() ? addr : &bsd;
uint32_t *out_addrsize = !IsBsd() ? addrsize : &size;
static bool demodernize;
if (demodernize) goto TriedAndTrue;
olderr = errno;
client = __sys_accept4(server, addr, addrsize, flags);
client = __sys_accept4(server, out_addr, out_addrsize, flags);
if (client == -1 && errno == ENOSYS) {
errno = olderr;
demodernize = true;
TriedAndTrue:
client = __fixupnewsockfd(__sys_accept(server, addr, addrsize, 0), flags);
} else if (SupportsLinux() && !once) {
once = true;
if (client == __NR_accept4_linux) {
demodernize = true;
goto TriedAndTrue;
}
client = __fixupnewsockfd(__sys_accept(server, out_addr, out_addrsize, 0),
flags);
}
if (client != -1 && IsBsd()) {
sockaddr2linux(addr);
sockaddr2linux(&bsd, size, addr, addrsize);
}
return client;
}

31
libc/sock/bind-sysv.c Normal file
View file

@ -0,0 +1,31 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2020 Justine Alexandra Roberts Tunney
Permission to use, copy, modify, and/or distribute this software for
any purpose with or without fee is hereby granted, provided that the
above copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/dce.h"
#include "libc/sock/internal.h"
int sys_bind(int fd, const void *addr, uint32_t addrsize) {
union sockaddr_storage_bsd bsd;
if (!IsBsd()) {
return __sys_bind(fd, addr, addrsize);
} else if (!sockaddr2bsd(addr, addrsize, &bsd, &addrsize)) {
return __sys_bind(fd, &bsd.sa, addrsize);
} else {
return -1;
}
}

View file

@ -16,7 +16,6 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/assert.h"
#include "libc/calls/internal.h"
#include "libc/calls/strace.internal.h"
#include "libc/dce.h"
@ -25,7 +24,6 @@
#include "libc/sock/sock.h"
#include "libc/sock/sockdebug.h"
#include "libc/sock/syscall_fd.internal.h"
#include "libc/str/str.h"
#include "libc/sysv/errfuns.h"
/**
@ -40,21 +38,11 @@
*/
int bind(int fd, const void *addr, uint32_t addrsize) {
int rc;
char addrbuf[72];
if (!addr || (IsAsan() && !__asan_is_valid(addr, addrsize))) {
rc = efault();
} else if (addrsize >= sizeof(struct sockaddr_in)) {
if (!IsWindows()) {
if (!IsBsd()) {
rc = sys_bind(fd, addr, addrsize);
} else {
char addr2[sizeof(
struct sockaddr_un_bsd)]; /* sockaddr_un_bsd is the largest */
assert(addrsize <= sizeof(addr2));
memcpy(&addr2, addr, addrsize);
sockaddr2bsd(&addr2[0]);
rc = sys_bind(fd, &addr2, addrsize);
}
rc = sys_bind(fd, addr, addrsize);
} else if (__isfdkind(fd, kFdSocket)) {
rc = sys_bind_nt(&g_fds.p[fd], addr, addrsize);
} else {

View file

@ -16,20 +16,16 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/assert.h"
#include "libc/dce.h"
#include "libc/sock/internal.h"
#include "libc/str/str.h"
#include "libc/sysv/errfuns.h"
int sys_connect(int fd, const void *addr, uint32_t addrsize) {
union sockaddr_storage_bsd bsd;
if (!IsBsd()) {
return __sys_connect(fd, addr, addrsize);
} else if (!sockaddr2bsd(addr, addrsize, &bsd, &addrsize)) {
return __sys_connect(fd, &bsd.sa, addrsize);
} else {
char addr2[sizeof(struct sockaddr_un_bsd)]; /* sockaddr_un_bsd is the largest */
assert(addrsize <= sizeof(addr2));
memcpy(&addr2, addr, addrsize);
sockaddr2bsd(&addr2[0]);
return __sys_connect(fd, &addr2, addrsize);
return -1;
}
}

View file

@ -20,9 +20,16 @@
#include "libc/sock/internal.h"
int sys_getpeername(int fd, void *out_addr, uint32_t *out_addrsize) {
int rc = __sys_getpeername(fd, out_addr, out_addrsize);
if (rc != -1 && IsBsd()) {
sockaddr2linux(out_addr);
int rc;
uint32_t size;
union sockaddr_storage_bsd bsd;
if (!IsBsd()) {
rc = __sys_getpeername(fd, out_addr, out_addrsize);
} else {
size = sizeof(bsd);
if ((rc = __sys_getpeername(fd, &bsd, &size)) != -1) {
sockaddr2linux(&bsd, size, out_addr, out_addrsize);
}
}
return rc;
}

View file

@ -20,9 +20,16 @@
#include "libc/sock/internal.h"
int sys_getsockname(int fd, void *out_addr, uint32_t *out_addrsize) {
int rc = __sys_getsockname(fd, out_addr, out_addrsize);
if (rc != -1 && IsBsd()) {
sockaddr2linux(out_addr);
int rc;
uint32_t size;
union sockaddr_storage_bsd bsd;
if (!IsBsd()) {
rc = __sys_getsockname(fd, out_addr, out_addrsize);
} else {
size = sizeof(bsd);
if ((rc = __sys_getsockname(fd, &bsd, &size)) != -1) {
sockaddr2linux(&bsd, size, out_addr, out_addrsize);
}
}
return rc;
}

View file

@ -52,6 +52,18 @@ struct sockaddr_un_bsd {
char sun_path[108];
};
union sockaddr_storage_bsd {
struct sockaddr_bsd sa;
struct sockaddr_in_bsd sin;
struct sockaddr_un_bsd sun;
};
union sockaddr_storage_linux {
struct sockaddr sa;
struct sockaddr_in sin;
struct sockaddr_un sun;
};
/* ------------------------------------------------------------------------------------*/
#define SOCKFD_OVERLAP_BUFSIZ 128
@ -79,10 +91,11 @@ void _firewall(const void *, uint32_t) hidden;
int32_t __sys_accept(int32_t, void *, uint32_t *, int) dontdiscard hidden;
int32_t __sys_accept4(int32_t, void *, uint32_t *, int) dontdiscard hidden;
int32_t __sys_bind(int32_t, const void *, uint32_t) hidden;
int32_t __sys_connect(int32_t, const void *, uint32_t) hidden;
int32_t __sys_socket(int32_t, int32_t, int32_t) hidden;
int32_t __sys_getsockname(int32_t, void *, uint32_t *) hidden;
int32_t __sys_getpeername(int32_t, void *, uint32_t *) hidden;
int32_t __sys_getsockname(int32_t, void *, uint32_t *) hidden;
int32_t __sys_socket(int32_t, int32_t, int32_t) hidden;
int32_t __sys_socketpair(int32_t, int32_t, int32_t, int32_t[2]) hidden;
int32_t sys_accept4(int32_t, void *, uint32_t *, int) dontdiscard hidden;
@ -137,29 +150,10 @@ struct SockFd *_dupsockfd(struct SockFd *) hidden;
int64_t GetNtBaseSocket(int64_t) hidden;
int sys_close_epoll(int) hidden;
/**
* Converts sockaddr (Linux/Windows) sockaddr_bsd (XNU/BSD).
*/
forceinline void sockaddr2bsd(void *saddr) {
char *p;
if (saddr) {
p = saddr;
p[1] = p[0];
p[0] = sizeof(struct sockaddr_in_bsd);
}
}
/**
* Converts sockaddr_in_bsd (XNU/BSD) sockaddr (Linux/Windows).
*/
forceinline void sockaddr2linux(void *saddr) {
char *p;
if (saddr) {
p = saddr;
p[0] = p[1];
p[1] = 0;
}
}
int sockaddr2bsd(const void *, uint32_t, union sockaddr_storage_bsd *,
uint32_t *);
void sockaddr2linux(const union sockaddr_storage_bsd *, uint32_t,
union sockaddr_storage_linux *, uint32_t *);
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */

View file

@ -45,19 +45,26 @@
*/
ssize_t recvfrom(int fd, void *buf, size_t size, uint32_t flags,
void *opt_out_srcaddr, uint32_t *opt_inout_srcaddrsize) {
ssize_t rc, got;
ssize_t rc;
uint32_t sz;
union sockaddr_storage_bsd bsd;
if (IsAsan() &&
(!__asan_is_valid(buf, size) ||
(opt_out_srcaddr &&
!__asan_is_valid(opt_out_srcaddr, *opt_inout_srcaddrsize)))) {
(!__asan_is_valid(opt_inout_srcaddrsize,
sizeof(*opt_inout_srcaddrsize)) ||
!__asan_is_valid(opt_out_srcaddr, *opt_inout_srcaddrsize))))) {
rc = efault();
} else if (!IsWindows()) {
got = sys_recvfrom(fd, buf, size, flags, opt_out_srcaddr,
opt_inout_srcaddrsize);
if (opt_out_srcaddr && IsBsd() && got != -1) {
sockaddr2linux(opt_out_srcaddr);
if (!IsBsd() || !opt_out_srcaddr) {
rc = sys_recvfrom(fd, buf, size, flags, opt_out_srcaddr,
opt_inout_srcaddrsize);
} else {
sz = sizeof(bsd);
if ((rc = sys_recvfrom(fd, buf, size, flags, &bsd, &sz)) != -1) {
sockaddr2linux(&bsd, sz, opt_out_srcaddr, opt_inout_srcaddrsize);
}
}
rc = got;
} else if (__isfdopen(fd)) {
if (__isfdkind(fd, kFdSocket)) {
rc = sys_recvfrom_nt(&g_fds.p[fd], (struct iovec[]){{buf, size}}, 1,

View file

@ -41,15 +41,24 @@
*/
ssize_t recvmsg(int fd, struct msghdr *msg, int flags) {
ssize_t rc, got;
struct msghdr msg2;
union sockaddr_storage_bsd bsd;
if (IsAsan() && !__asan_is_valid_msghdr(msg)) {
rc = efault();
} else if (!IsWindows()) {
got = sys_recvmsg(fd, msg, flags);
// An address was provided, convert from BSD form
if (msg->msg_name && IsBsd() && got != -1) {
sockaddr2linux(msg->msg_name);
if (IsBsd() && msg->msg_name) {
memcpy(&msg2, msg, sizeof(msg2));
if (!(rc = sockaddr2bsd(msg->msg_name, msg->msg_namelen, &bsd,
&msg2.msg_namelen))) {
msg2.msg_name = &bsd.sa;
if ((rc = sys_recvmsg(fd, &msg2, flags)) != -1) {
sockaddr2linux(msg2.msg_name, msg2.msg_namelen, msg->msg_name,
&msg->msg_namelen);
}
}
} else {
rc = sys_recvmsg(fd, msg, flags);
}
rc = got;
} else if (__isfdopen(fd)) {
if (!msg->msg_control) {
if (__isfdkind(fd, kFdSocket)) {

View file

@ -44,27 +44,22 @@
*/
ssize_t sendmsg(int fd, const struct msghdr *msg, int flags) {
int64_t rc;
char addr2[128];
struct msghdr msg2;
union sockaddr_storage_bsd bsd;
if (IsAsan() && !__asan_is_valid_msghdr(msg)) {
rc = efault();
} else if (!IsWindows()) {
if (IsBsd() && msg->msg_name) {
/* An optional address is provided, convert it to the BSD form */
if (msg->msg_namelen <= sizeof(addr2)) {
memcpy(&addr2[0], msg->msg_name, msg->msg_namelen);
sockaddr2bsd(&addr2[0]);
/* Copy all of msg (except for msg_name) into the new ephemeral local */
memcpy(&msg2, msg, sizeof(msg2));
msg2.msg_name = &addr2[0];
memcpy(&msg2, msg, sizeof(msg2));
if (!(rc = sockaddr2bsd(msg->msg_name, msg->msg_namelen, &bsd,
&msg2.msg_namelen))) {
msg2.msg_name = &bsd.sa;
rc = sys_sendmsg(fd, &msg2, flags);
} else {
rc = einval();
}
} else {
rc = sys_sendmsg(fd, msg, flags);
}
/* else do the syscall */
rc = sys_sendmsg(fd, msg, flags);
} else if (__isfdopen(fd)) {
if (msg->msg_control) {
rc = einval(); /* control msg not supported */

View file

@ -52,7 +52,8 @@
ssize_t sendto(int fd, const void *buf, size_t size, uint32_t flags,
const void *opt_addr, uint32_t addrsize) {
ssize_t rc;
char addr2[sizeof(struct sockaddr_un_bsd)];
uint32_t bsdaddrsize;
union sockaddr_storage_bsd bsd;
if (IsAsan() && (!__asan_is_valid(buf, size) ||
(opt_addr && !__asan_is_valid(opt_addr, addrsize)))) {
rc = efault();
@ -61,12 +62,8 @@ ssize_t sendto(int fd, const void *buf, size_t size, uint32_t flags,
if (!IsWindows()) {
if (!IsBsd() || !opt_addr) {
rc = sys_sendto(fd, buf, size, flags, opt_addr, addrsize);
} else if (addrsize > sizeof(addr2)) {
rc = einval();
} else {
memcpy(&addr2, opt_addr, addrsize);
sockaddr2bsd(&addr2[0]);
rc = sys_sendto(fd, buf, size, flags, &addr2[0], addrsize);
} else if (!(rc = sockaddr2bsd(opt_addr, addrsize, &bsd, &bsdaddrsize))) {
rc = sys_sendto(fd, buf, size, flags, &bsd, bsdaddrsize);
}
} else if (__isfdopen(fd)) {
if (__isfdkind(fd, kFdSocket)) {

66
libc/sock/sockaddr2bsd.c Normal file
View file

@ -0,0 +1,66 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2022 Justine Alexandra Roberts Tunney
Permission to use, copy, modify, and/or distribute this software for
any purpose with or without fee is hereby granted, provided that the
above copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/sock/internal.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/af.h"
#include "libc/sysv/errfuns.h"
/**
* Converts sockaddr (Linux/Windows) sockaddr_bsd (XNU/BSD).
*/
int sockaddr2bsd(const void *addr, uint32_t addrsize,
union sockaddr_storage_bsd *out_addr, uint32_t *out_addrsize) {
uint32_t len, famsize;
if (addrsize >= sizeof(((struct sockaddr *)addr)->sa_family)) {
if (((struct sockaddr *)addr)->sa_family == AF_INET) {
if (addrsize >= sizeof(struct sockaddr_in)) {
out_addr->sin.sin_len = 0;
out_addr->sin.sin_family = AF_INET;
out_addr->sin.sin_port = ((struct sockaddr_in *)addr)->sin_port;
out_addr->sin.sin_addr = ((struct sockaddr_in *)addr)->sin_addr;
bzero(&out_addr->sin.sin_zero, sizeof(out_addr->sin.sin_zero));
*out_addrsize = sizeof(struct sockaddr_in_bsd);
return 0;
} else {
return einval();
}
} else if (((struct sockaddr *)addr)->sa_family == AF_UNIX) {
famsize = sizeof(((struct sockaddr_un *)addr)->sun_family);
if (addrsize >= famsize &&
(len = strnlen(((struct sockaddr_un *)addr)->sun_path,
addrsize - famsize)) <
sizeof(out_addr->sun.sun_path)) {
out_addr->sun.sun_len = 0;
out_addr->sun.sun_family = AF_UNIX;
memcpy(out_addr->sun.sun_path, ((struct sockaddr_un *)addr)->sun_path,
len);
out_addr->sun.sun_path[len] = 0;
*out_addrsize = sizeof(out_addr->sun.sun_len) +
sizeof(out_addr->sun.sun_family) + len;
return 0;
} else {
return einval();
}
} else {
return epfnosupport();
}
} else {
return einval();
}
}

View file

@ -0,0 +1,60 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2022 Justine Alexandra Roberts Tunney
Permission to use, copy, modify, and/or distribute this software for
any purpose with or without fee is hereby granted, provided that the
above copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/intrin/kprintf.h"
#include "libc/macros.internal.h"
#include "libc/sock/internal.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/af.h"
#include "libc/sysv/errfuns.h"
/**
* Converts sockaddr_bsd (XNU/BSD) sockaddr (Linux/Windows).
*/
void sockaddr2linux(const union sockaddr_storage_bsd *addr, uint32_t addrsize,
union sockaddr_storage_linux *out_addr,
uint32_t *inout_addrsize) {
uint32_t len, size;
if (out_addr && inout_addrsize) {
size = *inout_addrsize;
bzero(out_addr, size);
if (addrsize >= sizeof(addr->sa.sa_family)) {
if (addr->sa.sa_family == AF_INET) {
if (addrsize >= sizeof(struct sockaddr_in_bsd) &&
size >= sizeof(struct sockaddr_in)) {
out_addr->sin.sin_family = AF_INET;
out_addr->sin.sin_port = addr->sin.sin_port;
out_addr->sin.sin_addr = addr->sin.sin_addr;
*inout_addrsize = sizeof(struct sockaddr_in);
}
} else if (addr->sa.sa_family == AF_UNIX) {
if (addrsize >=
sizeof(addr->sun.sun_len) + sizeof(addr->sun.sun_family) &&
size >= sizeof(out_addr->sun.sun_family)) {
len = strnlen(((struct sockaddr_un *)addr)->sun_path,
MIN(addrsize - (sizeof(addr->sun.sun_len) +
sizeof(addr->sun.sun_family)),
size - sizeof(out_addr->sun.sun_family)));
out_addr->sun.sun_family = AF_UNIX;
if (len) memcpy(out_addr->sun.sun_path, addr->sun.sun_path, len);
*inout_addrsize = sizeof(out_addr->sun.sun_family) + len + 1;
}
}
}
}
}

View file

@ -0,0 +1,2 @@
.include "o/libc/sysv/macros.internal.inc"
.scall __sys_bind,0x0680680682068031,globl,hidden

View file

@ -1,2 +0,0 @@
.include "o/libc/sysv/macros.internal.inc"
.scall sys_bind,0x0680680682068031,globl,hidden

View file

@ -84,7 +84,7 @@ scall sys_recvfrom 0x01d01d01d201d02d globl hidden
scall sys_sendmsg 0x01c01c01c201c02e globl hidden
scall sys_recvmsg 0x01b01b01b201b02f globl hidden
scall sys_shutdown 0x0860860862086030 globl hidden
scall sys_bind 0x0680680682068031 globl hidden
scall __sys_bind 0x0680680682068031 globl hidden
scall sys_listen 0x06a06a06a206a032 globl hidden
scall __sys_getsockname 0x0200200202020033 globl hidden
scall __sys_getpeername 0x01f01f08d201f034 globl hidden

116
test/libc/sock/unix_test.c Normal file
View file

@ -0,0 +1,116 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2022 Justine Alexandra Roberts Tunney
Permission to use, copy, modify, and/or distribute this software for
any purpose with or without fee is hereby granted, provided that the
above copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/calls.h"
#include "libc/calls/internal.h"
#include "libc/calls/struct/timeval.h"
#include "libc/dce.h"
#include "libc/intrin/kprintf.h"
#include "libc/nt/version.h"
#include "libc/runtime/runtime.h"
#include "libc/sock/sock.h"
#include "libc/sysv/consts/af.h"
#include "libc/sysv/consts/so.h"
#include "libc/sysv/consts/sock.h"
#include "libc/sysv/consts/sol.h"
#include "libc/testlib/testlib.h"
#include "libc/time/time.h"
char testlib_enable_tmp_setup_teardown;
void DatagramServer(void) {
char buf[256] = {0};
uint32_t len = sizeof(struct sockaddr_un);
struct sockaddr_un addr = {AF_UNIX, "foo.sock"};
alarm(3);
ASSERT_SYS(0, 3, socket(AF_UNIX, SOCK_DGRAM, 0));
ASSERT_SYS(0, 0, bind(3, (void *)&addr, len));
bzero(&addr, sizeof(addr));
ASSERT_SYS(0, 0, getsockname(3, (void *)&addr, &len));
ASSERT_EQ(11, len);
ASSERT_STREQ("foo.sock", addr.sun_path);
ASSERT_SYS(0, 5, read(3, buf, 256));
EXPECT_STREQ("hello", buf);
ASSERT_SYS(0, 0, close(3));
}
TEST(unix, datagram) {
if (IsWindows()) return; // no unix datagram on windows :'(
int ws;
uint32_t len = sizeof(struct sockaddr_un);
struct sockaddr_un addr = {AF_UNIX, "foo.sock"};
if (!fork()) {
DatagramServer();
_Exit(0);
}
alarm(3);
while (!fileexists(addr.sun_path)) usleep(10000);
ASSERT_SYS(0, 3, socket(AF_UNIX, SOCK_DGRAM, 0));
ASSERT_SYS(0, 5, sendto(3, "hello", 5, 0, (void *)&addr, len));
ASSERT_SYS(0, 0, close(3));
ASSERT_NE(-1, wait(&ws));
EXPECT_TRUE(WIFEXITED(ws));
EXPECT_EQ(0, WEXITSTATUS(ws));
alarm(0);
}
void StreamServer(void) {
char buf[256] = {0};
uint32_t len = sizeof(struct sockaddr_un);
struct sockaddr_un addr = {AF_UNIX, "foo.sock"};
alarm(3);
ASSERT_SYS(0, 3, socket(AF_UNIX, SOCK_STREAM, 0));
ASSERT_SYS(0, 0, bind(3, (void *)&addr, len));
bzero(&addr, sizeof(addr));
ASSERT_SYS(0, 0, getsockname(3, (void *)&addr, &len));
ASSERT_EQ(2 + 8 + 1, len);
ASSERT_EQ(AF_UNIX, addr.sun_family);
ASSERT_STREQ("foo.sock", addr.sun_path);
ASSERT_SYS(0, 0, listen(3, 10));
bzero(&addr, sizeof(addr));
len = sizeof(addr);
ASSERT_SYS(0, 4, accept(3, &addr, &len));
ASSERT_EQ(AF_UNIX, addr.sun_family);
EXPECT_STREQ("", addr.sun_path);
ASSERT_SYS(0, 5, read(4, buf, 256));
EXPECT_STREQ("hello", buf);
ASSERT_SYS(0, 0, close(3));
}
TEST(unix, stream) {
if (IsWindows() && !IsAtLeastWindows10()) return;
int ws;
uint32_t len = sizeof(struct sockaddr_un);
struct sockaddr_un addr = {AF_UNIX, "foo.sock"};
// TODO(jart): move this line down when kFdProcess is gone
ASSERT_SYS(0, 3, socket(AF_UNIX, SOCK_STREAM, 0));
if (!fork()) {
close(3);
StreamServer();
_Exit(0);
}
alarm(3);
while (!fileexists(addr.sun_path)) usleep(10000);
ASSERT_SYS(0, 0, connect(3, (void *)&addr, len));
ASSERT_SYS(0, 5, write(3, "hello", 5));
ASSERT_SYS(0, 0, close(3));
ASSERT_NE(-1, wait(&ws));
EXPECT_TRUE(WIFEXITED(ws));
EXPECT_EQ(0, WEXITSTATUS(ws));
alarm(0);
}

View file

@ -101,6 +101,12 @@ struct UnixErrno {
const char *call;
};
union SockAddr {
struct sockaddr s;
struct sockaddr_in i;
struct sockaddr_un u;
};
static lua_State *GL;
static void *LuaRealloc(lua_State *L, void *p, size_t n) {
@ -219,6 +225,42 @@ static int SysretInteger(lua_State *L, const char *call, int olderr,
}
}
static int MakeSockaddr(lua_State *L, int i, union SockAddr *sa,
uint32_t *salen) {
bzero(sa, sizeof(*sa));
if (lua_isstring(L, i)) {
sa->u.sun_family = AF_UNIX;
if (!memccpy(sa->u.sun_path, luaL_checkstring(L, i), 0,
sizeof(sa->u.sun_path))) {
luaL_error(L, "unix path too long");
unreachable;
}
*salen = sizeof(struct sockaddr_un);
return i + 1;
} else {
sa->i.sin_family = AF_INET;
sa->i.sin_addr.s_addr = htonl(luaL_optinteger(L, i, 0));
sa->i.sin_port = htons(luaL_optinteger(L, i + 1, 0));
*salen = sizeof(struct sockaddr_in);
return i + 2;
}
}
static int PushSockaddr(lua_State *L, const struct sockaddr_storage *ss) {
if (ss->ss_family == AF_INET) {
lua_pushinteger(L,
ntohl(((const struct sockaddr_in *)ss)->sin_addr.s_addr));
lua_pushinteger(L, ntohs(((const struct sockaddr_in *)ss)->sin_port));
return 2;
} else if (ss->ss_family == AF_UNIX) {
lua_pushstring(L, ((const struct sockaddr_un *)ss)->sun_path);
return 1;
} else {
luaL_error(L, "bad family");
unreachable;
}
}
static void CheckOptvalsize(lua_State *L, uint32_t want, uint32_t got) {
if (!IsTiny()) {
if (want == got) return;
@ -1095,6 +1137,7 @@ static int LuaUnixSetsockopt(lua_State *L) {
// ├─→ true
// └─→ nil, unix.Errno
tv.tv_sec = luaL_checkinteger(L, 4);
tv.tv_usec = luaL_optinteger(L, 5, 0) / 1000;
optval = &tv;
optsize = sizeof(tv);
} else if (level == SOL_SOCKET && optname == SO_LINGER) {
@ -1164,10 +1207,11 @@ static int LuaUnixGetsockopt(lua_State *L) {
// └─→ nil, unix.Errno
static int LuaUnixSocket(lua_State *L) {
int olderr = errno;
int family = luaL_optinteger(L, 1, AF_INET);
return SysretInteger(
L, "socket", olderr,
socket(luaL_optinteger(L, 1, AF_INET), luaL_optinteger(L, 2, SOCK_STREAM),
luaL_optinteger(L, 3, IPPROTO_TCP)));
socket(family, luaL_optinteger(L, 2, SOCK_STREAM),
luaL_optinteger(L, 3, family == AF_INET ? IPPROTO_TCP : 0)));
}
// unix.socketpair([family:int[, type:int[, protocol:int]]])
@ -1187,33 +1231,29 @@ static int LuaUnixSocketpair(lua_State *L) {
}
// unix.bind(fd:int[, ip:uint32, port:uint16])
// unix.bind(fd:int[, unixpath:str])
// ├─→ true
// └─→ nil, unix.Errno
static int LuaUnixBind(lua_State *L) {
uint32_t salen;
union SockAddr sa;
int olderr = errno;
MakeSockaddr(L, 2, &sa, &salen);
return SysretBool(L, "bind", olderr,
bind(luaL_checkinteger(L, 1),
&(struct sockaddr_in){
.sin_family = AF_INET,
.sin_addr.s_addr = htonl(luaL_optinteger(L, 2, 0)),
.sin_port = htons(luaL_optinteger(L, 3, 0)),
},
sizeof(struct sockaddr_in)));
bind(luaL_checkinteger(L, 1), &sa.s, salen));
}
// unix.connect(fd:int, ip:uint32, port:uint16)
// unix.connect(fd:int, unixpath:str)
// ├─→ true
// └─→ nil, unix.Errno
static int LuaUnixConnect(lua_State *L) {
uint32_t salen;
union SockAddr sa;
int olderr = errno;
return SysretBool(
L, "connect", olderr,
connect(luaL_checkinteger(L, 1),
&(struct sockaddr_in){
.sin_addr.s_addr = htonl(luaL_checkinteger(L, 2)),
.sin_port = htons(luaL_checkinteger(L, 3)),
},
sizeof(struct sockaddr_in)));
MakeSockaddr(L, 2, &sa, &salen);
return SysretBool(L, "connect", olderr,
connect(luaL_checkinteger(L, 1), &sa.s, salen));
}
// unix.listen(fd:int[, backlog:int])
@ -1225,42 +1265,34 @@ static int LuaUnixListen(lua_State *L) {
listen(luaL_checkinteger(L, 1), luaL_optinteger(L, 2, 10)));
}
static int LuaUnixGetname(lua_State *L, const char *name,
int func(int, void *, uint32_t *)) {
int olderr;
uint32_t addrsize;
struct sockaddr_storage ss = {0};
olderr = errno;
addrsize = sizeof(ss) - 1;
if (!func(luaL_checkinteger(L, 1), &ss, &addrsize)) {
return PushSockaddr(L, &ss);
} else {
return SysretErrno(L, name, olderr);
}
}
// unix.getsockname(fd:int)
// ├─→ ip:uint32, port:uint16
// ├─→ unixpath:str
// └─→ nil, unix.Errno
static int LuaUnixGetsockname(lua_State *L) {
int fd, olderr;
uint32_t addrsize;
struct sockaddr_in sa;
olderr = errno;
addrsize = sizeof(sa);
fd = luaL_checkinteger(L, 1);
if (!getsockname(fd, &sa, &addrsize)) {
lua_pushinteger(L, ntohl(sa.sin_addr.s_addr));
lua_pushinteger(L, ntohs(sa.sin_port));
return 2;
} else {
return SysretErrno(L, "getsockname", olderr);
}
return LuaUnixGetname(L, "getsockname", getsockname);
}
// unix.getpeername(fd:int)
// ├─→ ip:uint32, port:uint16
// ├─→ unixpath:str
// └─→ nil, unix.Errno
static int LuaUnixGetpeername(lua_State *L) {
int fd, olderr;
uint32_t addrsize;
struct sockaddr_in sa;
olderr = errno;
addrsize = sizeof(sa);
fd = luaL_checkinteger(L, 1);
if (!getpeername(fd, &sa, &addrsize)) {
lua_pushinteger(L, ntohl(sa.sin_addr.s_addr));
lua_pushinteger(L, ntohs(sa.sin_port));
return 2;
} else {
return SysretErrno(L, "getpeername", olderr);
}
return LuaUnixGetname(L, "getpeername", getpeername);
}
// unix.siocgifconf()
@ -1338,21 +1370,20 @@ static int LuaUnixGethostname(lua_State *L) {
// unix.accept(serverfd:int[, flags:int])
// ├─→ clientfd:int, ip:uint32, port:uint16
// ├─→ clientfd:int, unixpath:str
// └─→ nil, unix.Errno
static int LuaUnixAccept(lua_State *L) {
uint32_t addrsize;
struct sockaddr_in sa;
struct sockaddr_storage ss;
int clientfd, serverfd, olderr, flags;
olderr = errno;
addrsize = sizeof(sa);
addrsize = sizeof(ss);
serverfd = luaL_checkinteger(L, 1);
flags = luaL_optinteger(L, 2, 0);
clientfd = accept4(serverfd, &sa, &addrsize, flags);
clientfd = accept4(serverfd, &ss, &addrsize, flags);
if (clientfd != -1) {
lua_pushinteger(L, clientfd);
lua_pushinteger(L, ntohl(sa.sin_addr.s_addr));
lua_pushinteger(L, ntohs(sa.sin_port));
return 3;
return 1 + PushSockaddr(L, &ss);
} else {
return SysretErrno(L, "accept", olderr);
}
@ -1403,6 +1434,7 @@ static int LuaUnixPoll(lua_State *L) {
// unix.recvfrom(fd:int[, bufsiz:int[, flags:int]])
// ├─→ data:str, ip:uint32, port:uint16
// ├─→ data:str, unixpath:str
// └─→ nil, unix.Errno
static int LuaUnixRecvfrom(lua_State *L) {
char *buf;
@ -1410,22 +1442,20 @@ static int LuaUnixRecvfrom(lua_State *L) {
ssize_t rc;
uint32_t addrsize;
lua_Integer bufsiz;
struct sockaddr_in sa;
int fd, flags, olderr = errno;
addrsize = sizeof(sa);
struct sockaddr_storage ss;
int fd, flags, pushed, olderr = errno;
addrsize = sizeof(ss);
fd = luaL_checkinteger(L, 1);
bufsiz = luaL_optinteger(L, 2, 1500);
bufsiz = MIN(bufsiz, 0x7ffff000);
flags = luaL_optinteger(L, 3, 0);
buf = LuaAllocOrDie(L, bufsiz);
rc = recvfrom(fd, buf, bufsiz, flags, &sa, &addrsize);
if (rc != -1) {
if ((rc = recvfrom(fd, buf, bufsiz, flags, &ss, &addrsize)) != -1) {
got = rc;
lua_pushlstring(L, buf, got);
lua_pushinteger(L, ntohl(sa.sin_addr.s_addr));
lua_pushinteger(L, ntohs(sa.sin_port));
pushed = 1 + PushSockaddr(L, &ss);
free(buf);
return 3;
return pushed;
} else {
free(buf);
return SysretErrno(L, "recvfrom", olderr);
@ -1473,21 +1503,21 @@ static int LuaUnixSend(lua_State *L) {
}
// unix.sendto(fd:int, data:str, ip:uint32, port:uint16[, flags:int])
// unix.sendto(fd:int, data:str, unixpath:str[, flags:int])
// ├─→ sent:int
// └─→ nil, unix.Errno
static int LuaUnixSendto(lua_State *L) {
char *data;
size_t sent, size;
struct sockaddr_in sa;
int fd, flags, olderr = errno;
size_t size;
uint32_t salen;
union SockAddr sa;
int i, fd, flags, olderr = errno;
fd = luaL_checkinteger(L, 1);
data = luaL_checklstring(L, 2, &size);
bzero(&sa, sizeof(sa));
sa.sin_addr.s_addr = htonl(luaL_checkinteger(L, 3));
sa.sin_port = htons(luaL_checkinteger(L, 4));
flags = luaL_optinteger(L, 5, 0);
i = MakeSockaddr(L, 3, &sa, &salen);
flags = luaL_optinteger(L, i, 0);
return SysretInteger(L, "sendto", olderr,
sendto(fd, data, size, flags, &sa, sizeof(sa)));
sendto(fd, data, size, flags, &sa.s, salen));
}
// unix.shutdown(fd:int, how:int)

View file

@ -0,0 +1,89 @@
-- UNIX Domain Sockets Example
-- So we can detect that child died.
died = false
function OnSigchld(sig)
died = true
unix.wait() -- Prevent it from becoming a zombie
end
assert(unix.sigaction(unix.SIGCHLD, OnSigchld))
-- So keyboard interurpts only go to subprocess.
oldint = assert(unix.sigaction(unix.SIGINT, unix.SIG_IGN))
oldquit = assert(unix.sigaction(unix.SIGQUIT, unix.SIG_IGN))
-- UNIX domain sockets need a file
tmpdir = os.getenv('TMPDIR') or '/tmp'
unixpath = '%s/redbean-unix.sock.%d' % {tmpdir, unix.getpid()}
-- Create child process which is the server.
child = assert(unix.fork())
if child == 0 then
server = assert(unix.socket(unix.AF_UNIX, unix.SOCK_STREAM))
assert(unix.setsockopt(server, unix.SOL_SOCKET, unix.SO_RCVTIMEO, 2))
assert(unix.bind(server, unixpath))
assert(unix.listen(server))
client = assert(unix.accept(server))
data = assert(unix.read(client))
assert(data == 'ping!')
assert(assert(unix.write(client, 'pong!')) == 5)
assert(unix.close(client))
unix.exit(0)
end
-- Wait for the child to create the the socket file.
function FileExists(path)
st, err = unix.stat(path)
return not err
end
expobackoff = 1
while not died do
if FileExists(unixpath) then
break
else
expobackoff = expobackoff << 1
unix.nanosleep(expobackoff // 1000000000,
expobackoff % 1000000000)
end
end
-- Now connect to the socket.
if not died then
client = assert(unix.socket(unix.AF_UNIX, unix.SOCK_STREAM))
assert(unix.connect(client, unixpath))
assert(assert(unix.write(client, 'ping!')) == 5)
data = assert(unix.read(client))
assert(data == 'pong!')
itworked = true
else
itworked = false
end
-- Wait for client to terminate. We don't check error here because if
-- the child already died and the signal handler reaped it, then this
-- returns a ECHILD error which is fine.
unix.wait()
assert(itworked)
-- Now clean up the socket file.
unix.unlink(unixpath)
SetStatus(200)
SetHeader('Connection', 'close') -- be lazy and let _Exit() clean up signal handlers
SetHeader('Content-Type', 'text/html; charset=utf-8')
Write('<!doctype html>\r\n')
Write('<title>redbean unix domain sockets example</title>\r\n')
Write('<h1>\r\n')
Write('<img style="vertical-align:middle" src="data:image/png;base64,\r\n')
Write(EncodeBase64(LoadAsset('/redbean.png')))
Write('">\r\n')
Write('redbean unix domain sockets example\r\n')
Write('</h1>\r\n')
Write([[
<p>
<strong>It worked!</strong> We successfully sent a ping
pong via UNIX local sockets. Please check out the source
code to this example inside your redbean at unix-unix.lua.
</p>
]])
Write('</h1>\r\n')

View file

@ -2546,8 +2546,10 @@ UNIX MODULE
`family` defaults to `AF_INET` and can be:
- `AF_UNIX`
- `AF_INET`
- `AF_INET`: Creates Internet Protocol Version 4 (IPv4) socket.
- `AF_UNIX`: Creates local UNIX domain socket. On the New Technology
this requires Windows 10 and only works with `SOCK_STREAM`.
`type` defaults to `SOCK_STREAM` and can be:
@ -2562,7 +2564,8 @@ UNIX MODULE
- `SOCK_CLOEXEC`
- `SOCK_NONBLOCK`
`protocol` defaults to `IPPROTO_TCP` and can be:
`protocol` defaults to `IPPROTO_TCP` for AF_INET` and `0` for
`AF_UNIX`. It can also be:
- `IPPROTO_IP`
- `IPPROTO_ICMP`
@ -2592,6 +2595,7 @@ UNIX MODULE
`protocol` defaults to `0`.
unix.bind(fd:int[, ip:uint32, port:uint16])
unix.bind(fd:int[, unixpath:str])
├─→ true
└─→ nil, unix.Errno
@ -2743,6 +2747,7 @@ UNIX MODULE
unix.accept(serverfd:int[, flags:int])
├─→ clientfd:int, ip:uint32, port:uint16
├─→ clientfd:int, unixpath:str
└─→ nil, unix.Errno
Accepts new client socket descriptor for a listening tcp socket.
@ -2753,6 +2758,7 @@ UNIX MODULE
- `SOCK_NONBLOCK`
unix.connect(fd:int, ip:uint32, port:uint16)
unix.connect(fd:int, unixpath:str)
├─→ true
└─→ nil, unix.Errno
@ -2764,16 +2770,21 @@ UNIX MODULE
unix.getsockname(fd:int)
├─→ ip:uint32, port:uint16
├─→ unixpath:str
└─→ nil, unix.Errno
Retrieves the local address of a socket.
unix.getpeername(fd:int)
├─→ ip:uint32, port:uint16
├─→ unixpath:str
└─→ nil, unix.Errno
Retrieves the remote address of a socket.
This operation will either fail on `AF_UNIX` sockets or return an
empty string.
unix.recv(fd:int[, bufsiz:int[, flags:int]])
├─→ data:str
└─→ nil, unix.Errno
@ -2787,6 +2798,7 @@ UNIX MODULE
unix.recvfrom(fd:int[, bufsiz:int[, flags:int]])
├─→ data:str, ip:uint32, port:uint16
├─→ data:str, unixpath:str
└─→ nil, unix.Errno
`flags` may have any combination (using bitwise OR) of:
@ -2810,6 +2822,7 @@ UNIX MODULE
- `MSG_NOSIGNAL`
unix.sendto(fd:int, data:str, ip:uint32, port:uint16[, flags:int])
unix.sendto(fd:int, data:str, unixpath:str[, flags:int])
├─→ sent:int
└─→ nil, unix.Errno

View file

@ -167,6 +167,7 @@ o/tinylinux/tool/net/redbean.com: \
o/$(MODE)/tool/net/demo/.init.lua.zip.o \
o/$(MODE)/tool/net/demo/.reload.lua.zip.o \
o/$(MODE)/tool/net/demo/sql.lua.zip.o \
o/$(MODE)/tool/net/demo/unix-unix.lua.zip.o \
o/$(MODE)/tool/net/demo/unix-rawsocket.lua.zip.o \
o/$(MODE)/tool/net/demo/unix-subprocess.lua.zip.o \
o/$(MODE)/tool/net/demo/unix-webserver.lua.zip.o \
@ -215,6 +216,7 @@ o/$(MODE)/tool/net/redbean-demo.com.dbg: \
o/$(MODE)/tool/net/largon2.o \
o/$(MODE)/tool/net/net.pkg \
o/$(MODE)/tool/net/demo/sql.lua.zip.o \
o/$(MODE)/tool/net/demo/unix-unix.lua.zip.o \
o/$(MODE)/tool/net/demo/unix-rawsocket.lua.zip.o \
o/$(MODE)/tool/net/demo/unix-subprocess.lua.zip.o \
o/$(MODE)/tool/net/demo/unix-webserver.lua.zip.o \