1
0
mirror of https://github.com/janet-lang/janet synced 2024-12-01 04:19:55 +00:00

Merge pull request #1138 from zevv/setsockopt

add net/setsockopt
This commit is contained in:
Calvin Rose 2023-05-16 19:29:25 -05:00 committed by GitHub
commit 692b6ef8ac
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 98 additions and 0 deletions

View File

@ -36,6 +36,10 @@
# endif
#endif
#if defined(__APPLE__)
#define _DARWIN_C_SOURCE
#endif
/* Needed for sched.h for cpu count */
#ifdef __linux__
#define _GNU_SOURCE

View File

@ -872,6 +872,98 @@ JANET_CORE_FN(cfun_stream_flush,
return argv[0];
}
struct sockopt_type {
const char *name;
int level;
int optname;
enum JanetType type;
};
// List of supported socket options; The type JANET_POINTER is used
// for options that require special handling depending on the type.
static const struct sockopt_type sockopt_type_list[] = {
{ "so_broadcast", SOL_SOCKET, SO_BROADCAST, JANET_BOOLEAN },
{ "so_reuseaddr", SOL_SOCKET, SO_REUSEADDR, JANET_BOOLEAN },
{ "so_keepalive", SOL_SOCKET, SO_KEEPALIVE, JANET_BOOLEAN },
{ "ip_multicast_ttl", IPPROTO_IP, IP_MULTICAST_TTL, JANET_NUMBER },
{ "ip_add_membership", IPPROTO_IP, IP_ADD_MEMBERSHIP, JANET_POINTER },
{ "ip_drop_membership", IPPROTO_IP, IP_DROP_MEMBERSHIP, JANET_POINTER },
{ "ipv6_join_group", IPPROTO_IPV6, IPV6_JOIN_GROUP, JANET_POINTER },
{ "ipv6_leave_group", IPPROTO_IPV6, IPV6_LEAVE_GROUP, JANET_POINTER },
{ NULL }
};
JANET_CORE_FN(cfun_net_setsockopt,
"(net/setsockopt stream option value)",
"set socket options.\n"
"\n"
"supported options and associated value types:\n"
"- :so_broadcast boolean\n"
"- :so_reuseaddr boolean\n"
"- :so_keepalive boolean\n"
"- :ip_multicast_ttl number\n"
"- :ip_add_membership string\n"
"- :ip_drop_membership string\n"
"- :ipv6_join_group string\n"
"- :ipv6_leave_group string\n") {
janet_arity(argc, 3, 3);
JanetStream *stream = janet_getabstract(argv, 0, &janet_stream_type);
janet_stream_flags(stream, JANET_STREAM_SOCKET);
JanetKeyword optstr = janet_getkeyword(argv, 1);
const struct sockopt_type *st = sockopt_type_list;
while (st->name) {
if (janet_cstrcmp(optstr, st->name) == 0) {
break;
}
st++;
}
if (st->name == NULL) {
janet_panicf("unknown socket option %q", argv[1]);
}
union {
int v_int;
struct ip_mreq v_mreq;
struct ipv6_mreq v_mreq6;
} val;
void *optval = (void *)&val;
socklen_t optlen = 0;
if (st->type == JANET_BOOLEAN) {
val.v_int = janet_getboolean(argv, 2);
optlen = sizeof(val.v_int);
} else if (st->type == JANET_NUMBER) {
val.v_int = janet_getinteger(argv, 2);
optlen = sizeof(val.v_int);
} else if (st->optname == IP_ADD_MEMBERSHIP || st->optname == IP_DROP_MEMBERSHIP) {
const char *addr = janet_getcstring(argv, 2);
memset(&val.v_mreq, 0, sizeof val.v_mreq);
val.v_mreq.imr_interface.s_addr = htonl(INADDR_ANY);
val.v_mreq.imr_multiaddr.s_addr = inet_addr(addr);
optlen = sizeof(val.v_mreq);
} else if (st->optname == IPV6_JOIN_GROUP || st->optname == IPV6_LEAVE_GROUP) {
const char *addr = janet_getcstring(argv, 2);
memset(&val.v_mreq6, 0, sizeof val.v_mreq6);
val.v_mreq6.ipv6mr_interface = 0;
inet_pton(AF_INET6, addr, &val.v_mreq6.ipv6mr_multiaddr);
optlen = sizeof(val.v_mreq6);
} else {
janet_panicf("invalid socket option type");
}
janet_assert(optlen != 0, "invalid socket option value");
int r = setsockopt((JSock) stream->handle, st->level, st->optname, optval, optlen);
if (r == -1) {
janet_panicf("setsockopt(%q): %s", argv[1], strerror(errno));
}
return janet_wrap_nil();
}
static const JanetMethod net_stream_methods[] = {
{"chunk", cfun_stream_chunk},
{"close", janet_cfun_stream_close},
@ -886,6 +978,7 @@ static const JanetMethod net_stream_methods[] = {
{"evchunk", janet_cfun_stream_chunk},
{"evwrite", janet_cfun_stream_write},
{"shutdown", cfun_net_shutdown},
{"setsockopt", cfun_net_setsockopt},
{NULL, NULL}
};
@ -910,6 +1003,7 @@ void janet_lib_net(JanetTable *env) {
JANET_CORE_REG("net/peername", cfun_net_getpeername),
JANET_CORE_REG("net/localname", cfun_net_getsockname),
JANET_CORE_REG("net/address-unpack", cfun_net_address_unpack),
JANET_CORE_REG("net/setsockopt", cfun_net_setsockopt),
JANET_REG_END
};
janet_core_cfuns_ext(env, NULL, net_cfuns);