1
0
mirror of https://github.com/janet-lang/janet synced 2024-11-25 09:47:17 +00:00

Add unix domain socket support to net.

Code is a bit messy, as getaddrinfo does not support
unix domain sockets directly. We require a keyword :unix
instead of the usual hostname string, and the port is the
path to unix domain socket. The UDS should support both stream
and datagram sockets.
This commit is contained in:
Calvin Rose 2020-06-03 00:53:17 -05:00
parent ec0d0ba368
commit 123710078d

View File

@ -41,6 +41,7 @@
#include <sys/ioctl.h> #include <sys/ioctl.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <sys/un.h>
#include <netinet/tcp.h> #include <netinet/tcp.h>
#include <netdb.h> #include <netdb.h>
#include <fcntl.h> #include <fcntl.h>
@ -175,7 +176,7 @@ JanetAsyncStatus net_machine_read(JanetListenerState *s, JanetAsyncEvent event)
int32_t bytes_left = state->bytes_left; int32_t bytes_left = state->bytes_left;
janet_buffer_extra(buffer, bytes_left); janet_buffer_extra(buffer, bytes_left);
JReadInt nread; JReadInt nread;
struct sockaddr_in saddr; char saddr[256];
socklen_t socklen = sizeof(saddr); socklen_t socklen = sizeof(saddr);
do { do {
if (state->is_recv_from) { if (state->is_recv_from) {
@ -427,14 +428,27 @@ static int janet_get_sockettype(Janet *argv, int32_t argc, int32_t n) {
} }
/* Needs argc >= offset + 2 */ /* Needs argc >= offset + 2 */
static struct addrinfo *janet_get_addrinfo(Janet *argv, int32_t offset, int socktype, int passive) { /* For unix paths, just rertuns a single sockaddr and sets *is_unix to 1, otherwise 0 */
static struct addrinfo *janet_get_addrinfo(Janet *argv, int32_t offset, int socktype, int passive, int *is_unix) {
/* Unix socket support */
if (janet_keyeq(argv[offset], "unix")) {
const char *path = janet_getcstring(argv, offset + 1);
struct sockaddr_un *saddr = malloc(sizeof(struct sockaddr_un));
if (saddr == NULL) {
JANET_OUT_OF_MEMORY;
}
saddr->sun_family = AF_UNIX;
snprintf(saddr->sun_path, 108, "%s", path);
*is_unix = 1;
return (struct addrinfo *) saddr;
}
/* Get host and port */ /* Get host and port */
const char *host = janet_getcstring(argv, offset); const char *host = janet_getcstring(argv, offset);
const char *port; const char *port;
if (janet_checkint(argv[offset + 1])) { if (janet_checkint(argv[offset + 1])) {
port = (const char *)janet_to_string(argv[offset + 1]); port = (const char *)janet_to_string(argv[offset + 1]);
} else { } else {
port = janet_getcstring(argv, offset + 1); port = janet_optcstring(argv, offset + 2, offset + 1, NULL);
} }
/* getaddrinfo */ /* getaddrinfo */
struct addrinfo *ai = NULL; struct addrinfo *ai = NULL;
@ -447,6 +461,7 @@ static struct addrinfo *janet_get_addrinfo(Janet *argv, int32_t offset, int sock
if (status) { if (status) {
janet_panicf("could not get address info: %s", gai_strerror(status)); janet_panicf("could not get address info: %s", gai_strerror(status));
} }
*is_unix = 0;
return ai; return ai;
} }
@ -457,8 +472,16 @@ static struct addrinfo *janet_get_addrinfo(Janet *argv, int32_t offset, int sock
static Janet cfun_net_sockaddr(int32_t argc, Janet *argv) { static Janet cfun_net_sockaddr(int32_t argc, Janet *argv) {
janet_arity(argc, 2, 4); janet_arity(argc, 2, 4);
int socktype = janet_get_sockettype(argv, argc, 2); int socktype = janet_get_sockettype(argv, argc, 2);
struct addrinfo *ai = janet_get_addrinfo(argv, 0, socktype, 0); int is_unix = 0;
if (argc >= 3 && janet_truthy(argv[3])) { int make_arr = (argc >= 3 && janet_truthy(argv[3]));
struct addrinfo *ai = janet_get_addrinfo(argv, 0, socktype, 0, &is_unix);
if (is_unix) {
void *abst = janet_abstract(&AddressAT, sizeof(struct sockaddr_un));
memcpy(abst, ai, sizeof(struct sockaddr_un));
Janet ret = janet_wrap_abstract(abst);
return make_arr ? janet_wrap_array(janet_array_n(&ret, 1)) : ret;
}
if (make_arr) {
/* Select all */ /* Select all */
JanetArray *arr = janet_array(10); JanetArray *arr = janet_array(10);
struct addrinfo *iter = ai; struct addrinfo *iter = ai;
@ -486,23 +509,44 @@ static Janet cfun_net_connect(int32_t argc, Janet *argv) {
janet_arity(argc, 2, 3); janet_arity(argc, 2, 3);
int socktype = janet_get_sockettype(argv, argc, 2); int socktype = janet_get_sockettype(argv, argc, 2);
struct addrinfo *ai = janet_get_addrinfo(argv, 0, socktype, 0); int is_unix = 0;
struct addrinfo *ai = janet_get_addrinfo(argv, 0, socktype, 0, &is_unix);
/* Create socket */ /* Create socket */
JSock sock = JSOCKDEFAULT; JSock sock = JSOCKDEFAULT;
struct addrinfo *rp = NULL; void *addr = NULL;
for (rp = ai; rp != NULL; rp = rp->ai_next) { socklen_t addrlen;
sock = socket(rp->ai_family, rp->ai_socktype | JSOCKFLAGS, rp->ai_protocol); if (is_unix) {
if (JSOCKVALID(sock)) break; sock = socket(AF_UNIX, socktype | JSOCKFLAGS, 0);
} if (!JSOCKVALID(sock)) {
if (!JSOCKVALID(sock)) { janet_panic("could not create socket");
freeaddrinfo(ai); }
janet_panic("could not create socket"); addr = (void *) ai;
addrlen = sizeof(struct sockaddr_un);
} else {
struct addrinfo *rp = NULL;
for (rp = ai; rp != NULL; rp = rp->ai_next) {
sock = socket(rp->ai_family, rp->ai_socktype | JSOCKFLAGS, rp->ai_protocol);
if (JSOCKVALID(sock)) {
addr = rp->ai_addr;
addrlen = rp->ai_addrlen;
break;
}
}
if (NULL == addr) {
freeaddrinfo(ai);
janet_panic("could not create socket");
}
} }
/* Connect to socket */ /* Connect to socket */
int status = connect(sock, ai->ai_addr, (int) ai->ai_addrlen); int status = connect(sock, addr, addrlen);
freeaddrinfo(ai); if (is_unix) {
free(ai);
} else {
freeaddrinfo(ai);
}
if (status == -1) { if (status == -1) {
JSOCKCLOSE(sock); JSOCKCLOSE(sock);
janet_panic("could not connect to socket"); janet_panic("could not connect to socket");
@ -513,6 +557,25 @@ static Janet cfun_net_connect(int32_t argc, Janet *argv) {
return janet_wrap_abstract(stream); return janet_wrap_abstract(stream);
} }
static const char *serverify_socket(JSock sfd) {
/* Set various socket options */
int enable = 1;
if (setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, (char *) &enable, sizeof(int)) < 0) {
return "setsockopt(SO_REUSEADDR) failed";
}
#ifdef SO_NOSIGPIPE
if (setsockopt(sfd, SOL_SOCKET, SO_NOSIGPIPE, &enable, sizeof(int)) < 0) {
return "setsockopt(SO_NOSIGPIPE) failed";
}
#endif
#ifdef SO_REUSEPORT
if (setsockopt(sfd, SOL_SOCKET, SO_REUSEPORT, &enable, sizeof(int)) < 0) {
return "setsockopt(SO_REUSEPORT) failed";
}
#endif
return NULL;
}
static Janet cfun_net_server(int32_t argc, Janet *argv) { static Janet cfun_net_server(int32_t argc, Janet *argv) {
janet_arity(argc, 2, 4); janet_arity(argc, 2, 4);
@ -520,40 +583,42 @@ static Janet cfun_net_server(int32_t argc, Janet *argv) {
JanetFunction *fun = janet_optfunction(argv, argc, 2, NULL); JanetFunction *fun = janet_optfunction(argv, argc, 2, NULL);
int socktype = janet_get_sockettype(argv, argc, 3); int socktype = janet_get_sockettype(argv, argc, 3);
struct addrinfo *ai = janet_get_addrinfo(argv, 0, socktype, 1); int is_unix = 0;
struct addrinfo *ai = janet_get_addrinfo(argv, 0, socktype, 1, &is_unix);
/* Check all addrinfos in a loop for the first that we can bind to. */
JSock sfd = JSOCKDEFAULT; JSock sfd = JSOCKDEFAULT;
struct addrinfo *rp = NULL; if (is_unix) {
for (rp = ai; rp != NULL; rp = rp->ai_next) { sfd = socket(AF_UNIX, socktype | JSOCKFLAGS, 0);
sfd = socket(rp->ai_family, rp->ai_socktype | JSOCKFLAGS, rp->ai_protocol); if (!JSOCKVALID(sfd)) {
if (!JSOCKVALID(sfd)) continue; free(ai);
/* Set various socket options */ janet_panic("could not create socket");
int enable = 1;
if (setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, (char *) &enable, sizeof(int)) < 0) {
JSOCKCLOSE(sfd);
janet_panic("setsockopt(SO_REUSEADDR) failed");
} }
#ifdef SO_NOSIGPIPE const char *err = serverify_socket(sfd);
if (setsockopt(sfd, SOL_SOCKET, SO_NOSIGPIPE, &enable, sizeof(int)) < 0) { if (NULL != err || bind(sfd, (struct sockaddr *)ai, sizeof(struct sockaddr_un))) {
JSOCKCLOSE(sfd); JSOCKCLOSE(sfd);
janet_panic("setsockopt(SO_NOSIGPIPE) failed"); free(ai);
janet_panic(err ? err : "could not bind socket");
} }
#endif free(ai);
#ifdef SO_REUSEPORT } else {
if (setsockopt(sfd, SOL_SOCKET, SO_REUSEPORT, &enable, sizeof(int)) < 0) { /* Check all addrinfos in a loop for the first that we can bind to. */
struct addrinfo *rp = NULL;
for (rp = ai; rp != NULL; rp = rp->ai_next) {
sfd = socket(rp->ai_family, rp->ai_socktype | JSOCKFLAGS, rp->ai_protocol);
if (!JSOCKVALID(sfd)) continue;
const char *err = serverify_socket(sfd);
if (NULL != err) {
JSOCKCLOSE(sfd);
continue;
}
/* Bind */
if (bind(sfd, rp->ai_addr, (int) rp->ai_addrlen) == 0) break;
JSOCKCLOSE(sfd); JSOCKCLOSE(sfd);
janet_panic("setsockopt(SO_REUSEPORT) failed");
} }
#endif freeaddrinfo(ai);
/* Bind */ if (NULL == rp) {
if (bind(sfd, rp->ai_addr, (int) rp->ai_addrlen) == 0) break; janet_panic("could not bind to any sockets");
JSOCKCLOSE(sfd); }
}
freeaddrinfo(ai);
if (NULL == rp) {
janet_panic("could not bind to any sockets");
} }
if (socktype == SOCK_DGRAM) { if (socktype == SOCK_DGRAM) {