From 88d0c2ca0f8102df3d18cc8cbf9076296a3952e6 Mon Sep 17 00:00:00 2001 From: Ico Doornekamp Date: Mon, 15 May 2023 12:15:36 +0200 Subject: [PATCH] add net/setsockopt --- src/core/net.c | 94 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 94 insertions(+) diff --git a/src/core/net.c b/src/core/net.c index 8ebbdb53..8c911f92 100644 --- a/src/core/net.c +++ b/src/core/net.c @@ -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);