mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-02-07 06:53:33 +00:00
Add unix domain socket support to redbean
This commit is contained in:
parent
4b23985b7f
commit
fc097ac275
25 changed files with 594 additions and 172 deletions
|
@ -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%
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
31
libc/sock/bind-sysv.c
Normal 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;
|
||||
}
|
||||
}
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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) */
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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)) {
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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
66
libc/sock/sockaddr2bsd.c
Normal 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();
|
||||
}
|
||||
}
|
60
libc/sock/sockaddr2linux.c
Normal file
60
libc/sock/sockaddr2linux.c
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
2
libc/sysv/calls/__sys_bind.s
Normal file
2
libc/sysv/calls/__sys_bind.s
Normal file
|
@ -0,0 +1,2 @@
|
|||
.include "o/libc/sysv/macros.internal.inc"
|
||||
.scall __sys_bind,0x0680680682068031,globl,hidden
|
|
@ -1,2 +0,0 @@
|
|||
.include "o/libc/sysv/macros.internal.inc"
|
||||
.scall sys_bind,0x0680680682068031,globl,hidden
|
|
@ -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
116
test/libc/sock/unix_test.c
Normal 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);
|
||||
}
|
160
third_party/lua/lunix.c
vendored
160
third_party/lua/lunix.c
vendored
|
@ -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)
|
||||
|
|
89
tool/net/demo/unix-unix.lua
Normal file
89
tool/net/demo/unix-unix.lua
Normal 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')
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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 \
|
||||
|
|
Loading…
Reference in a new issue