From 24bcbd2040fad723648e25af87cd848da8aa27bc Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Thu, 17 May 2018 14:22:52 +0200 Subject: [socket-nif] getopt partially implemented There are still many options not implemented (just as for setopt), but this will have to do for now... --- erts/emulator/nifs/common/socket_nif.c | 1334 +++++++++++++++++++++++++++++--- erts/preloaded/src/socket.erl | 71 +- 2 files changed, 1261 insertions(+), 144 deletions(-) diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index 18375caf60..d87492dd54 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -376,8 +376,9 @@ typedef union { #define SOCKET_OPT_IPV6_HOPLIMIT 12 #define SOCKET_OPT_TCP_CONGESTION 0 -#define SOCKET_OPT_TCP_MAXSEG 1 -#define SOCKET_OPT_TCP_NODELAY 2 +#define SOCKET_OPT_TCP_CORK 1 +#define SOCKET_OPT_TCP_MAXSEG 2 +#define SOCKET_OPT_TCP_NODELAY 3 #define SOCKET_OPT_UDP_CORK 0 @@ -437,6 +438,7 @@ typedef union { #define GET_TUPLE(E, TE, TSZ, TA) enif_get_tuple((E), (TE), (TSZ), (TA)) #define ALLOC_BIN(SZ, BP) enif_alloc_binary((SZ), (BP)) +#define REALLOC_BIN(SZ, BP) enif_realloc_binary((SZ), (BP)) /* =================================================================== * @@ -520,6 +522,11 @@ static unsigned long one_value = 1; # define SOCKLEN_T size_t #endif +#ifdef __WIN32__ +#define SOCKOPTLEN_T int +#else +#define SOCKOPTLEN_T SOCKLEN_T +#endif /* The general purpose sockaddr */ typedef union { @@ -805,6 +812,7 @@ static ERL_NIF_TERM nclose(ErlNifEnv* env, static ERL_NIF_TERM nshutdown(ErlNifEnv* env, SocketDescriptor* descP, int how); + static ERL_NIF_TERM nsetopt(ErlNifEnv* env, SocketDescriptor* descP, BOOLEAN_T isEncoded, @@ -956,6 +964,126 @@ static ERL_NIF_TERM nsetopt_lvl_sctp_nodelay(ErlNifEnv* env, #endif #endif // defined(HAVE_SCTP) +static ERL_NIF_TERM ngetopt(ErlNifEnv* env, + SocketDescriptor* descP, + BOOLEAN_T isEncoded, + BOOLEAN_T isOTP, + int level, + int eOpt); +static ERL_NIF_TERM ngetopt_otp(ErlNifEnv* env, + SocketDescriptor* descP, + int eOpt); +static ERL_NIF_TERM ngetopt_otp_debug(ErlNifEnv* env, + SocketDescriptor* descP); +static ERL_NIF_TERM ngetopt_otp_iow(ErlNifEnv* env, + SocketDescriptor* descP); +static ERL_NIF_TERM ngetopt_native(ErlNifEnv* env, + SocketDescriptor* descP, + int level, + int eOpt); +static ERL_NIF_TERM ngetopt_level(ErlNifEnv* env, + SocketDescriptor* descP, + int level, + int eOpt); +static ERL_NIF_TERM ngetopt_lvl_socket(ErlNifEnv* env, + SocketDescriptor* descP, + int eOpt); +#if defined(SO_BROADCAST) +static ERL_NIF_TERM ngetopt_lvl_sock_broadcast(ErlNifEnv* env, + SocketDescriptor* descP); +#endif +#if defined(SO_DONTROUTE) +static ERL_NIF_TERM ngetopt_lvl_sock_dontroute(ErlNifEnv* env, + SocketDescriptor* descP); +#endif +#if defined(SO_KEEPALIVE) +static ERL_NIF_TERM ngetopt_lvl_sock_keepalive(ErlNifEnv* env, + SocketDescriptor* descP); +#endif +#if defined(SO_LINGER) +static ERL_NIF_TERM ngetopt_lvl_sock_linger(ErlNifEnv* env, + SocketDescriptor* descP); +#endif +#if defined(SO_PRIORITY) +static ERL_NIF_TERM ngetopt_lvl_sock_priority(ErlNifEnv* env, + SocketDescriptor* descP); +#endif +#if defined(SO_RCVBUF) +static ERL_NIF_TERM ngetopt_lvl_sock_rcvbuf(ErlNifEnv* env, + SocketDescriptor* descP); +#endif +#if defined(SO_REUSEADDR) +static ERL_NIF_TERM ngetopt_lvl_sock_reuseaddr(ErlNifEnv* env, + SocketDescriptor* descP); +#endif +#if defined(SO_SNDBUF) +static ERL_NIF_TERM ngetopt_lvl_sock_sndbuf(ErlNifEnv* env, + SocketDescriptor* descP); +#endif +static ERL_NIF_TERM ngetopt_lvl_ip(ErlNifEnv* env, + SocketDescriptor* descP, + int eOpt); +#if defined(IP_RECVTOS) +static ERL_NIF_TERM ngetopt_lvl_ip_recvtos(ErlNifEnv* env, + SocketDescriptor* descP); +#endif +#if defined(IP_ROUTER_ALERT) +static ERL_NIF_TERM ngetopt_lvl_ip_router_alert(ErlNifEnv* env, + SocketDescriptor* descP); +#endif +#if defined(IP_TOS) +static ERL_NIF_TERM ngetopt_lvl_ip_tos(ErlNifEnv* env, + SocketDescriptor* descP); +#endif +#if defined(IP_TTL) +static ERL_NIF_TERM ngetopt_lvl_ip_ttl(ErlNifEnv* env, + SocketDescriptor* descP); +#endif +#if defined(SOL_IPV6) +static ERL_NIF_TERM ngetopt_lvl_ipv6(ErlNifEnv* env, + SocketDescriptor* descP, + int eOpt); +#if defined(IPV6_HOPLIMIT) +static ERL_NIF_TERM ngetopt_lvl_ipv6_hoplimit(ErlNifEnv* env, + SocketDescriptor* descP); +#endif +#endif // defined(SOL_IPV6) +static ERL_NIF_TERM ngetopt_lvl_tcp(ErlNifEnv* env, + SocketDescriptor* descP, + int eOpt); +#if defined(TCP_CONGESTION) +static ERL_NIF_TERM ngetopt_lvl_tcp_congestion(ErlNifEnv* env, + SocketDescriptor* descP); +#endif +#if defined(TCP_MAXSEG) +static ERL_NIF_TERM ngetopt_lvl_tcp_maxseg(ErlNifEnv* env, + SocketDescriptor* descP); +#endif +#if defined(TCP_NODELAY) +static ERL_NIF_TERM ngetopt_lvl_tcp_nodelay(ErlNifEnv* env, + SocketDescriptor* descP); +#endif +static ERL_NIF_TERM ngetopt_lvl_udp(ErlNifEnv* env, + SocketDescriptor* descP, + int eOpt); +#if defined(UDP_CORK) +static ERL_NIF_TERM ngetopt_lvl_udp_cork(ErlNifEnv* env, + SocketDescriptor* descP); +#endif +#if defined(HAVE_SCTP) +static ERL_NIF_TERM ngetopt_lvl_sctp(ErlNifEnv* env, + SocketDescriptor* descP, + int eOpt); +#if defined(SCTP_AUTOCLOSE) +static ERL_NIF_TERM ngetopt_lvl_sctp_autoclose(ErlNifEnv* env, + SocketDescriptor* descP); +#endif +#if defined(SCTP_NODELAY) +static ERL_NIF_TERM ngetopt_lvl_sctp_nodelay(ErlNifEnv* env, + SocketDescriptor* descP); +#endif +#endif // defined(HAVE_SCTP) + static ERL_NIF_TERM nsetopt_str_opt(ErlNifEnv* env, SocketDescriptor* descP, int level, @@ -973,6 +1101,20 @@ static ERL_NIF_TERM nsetopt_int_opt(ErlNifEnv* env, int opt, ERL_NIF_TERM eVal); +static ERL_NIF_TERM ngetopt_str_opt(ErlNifEnv* env, + SocketDescriptor* descP, + int level, + int opt, + int max); +static ERL_NIF_TERM ngetopt_bool_opt(ErlNifEnv* env, + SocketDescriptor* descP, + int level, + int opt); +static ERL_NIF_TERM ngetopt_int_opt(ErlNifEnv* env, + SocketDescriptor* descP, + int level, + int opt); + static ERL_NIF_TERM nlink_if2idx(ErlNifEnv* env, char* ifn); static ERL_NIF_TERM nlink_idx2if(ErlNifEnv* env, @@ -1059,6 +1201,12 @@ static BOOLEAN_T decode_ip_tos(ErlNifEnv* env, static BOOLEAN_T decode_bool(ErlNifEnv* env, ERL_NIF_TERM eVal, BOOLEAN_T* val); +static BOOLEAN_T decode_native_get_opt(ErlNifEnv* env, + ERL_NIF_TERM eVal, + int* opt, + int* valueSz); +static void encode_bool(BOOLEAN_T val, ERL_NIF_TERM* eVal); +static ERL_NIF_TERM encode_ip_tos(ErlNifEnv* env, int val); static void inform_waiting_procs(ErlNifEnv* env, SocketDescriptor* descP, @@ -1189,6 +1337,11 @@ static char str_timeout[] = "timeout"; static char str_true[] = "true"; static char str_undefined[] = "undefined"; +static char str_lowdelay[] = "lowdelay"; +static char str_throughput[] = "throughput"; +static char str_reliability[] = "reliability"; +static char str_mincost[] = "mincost"; + /* (special) error string constants */ static char str_eagain[] = "eagain"; static char str_eafnosupport[] = "eafnosupport"; @@ -1217,6 +1370,11 @@ static ERL_NIF_TERM atom_timeout; static ERL_NIF_TERM atom_true; static ERL_NIF_TERM atom_undefined; +static ERL_NIF_TERM atom_lowdelay; +static ERL_NIF_TERM atom_throughput; +static ERL_NIF_TERM atom_reliability; +static ERL_NIF_TERM atom_mincost; + static ERL_NIF_TERM atom_eagain; static ERL_NIF_TERM atom_eafnosupport; static ERL_NIF_TERM atom_einval; @@ -3710,9 +3868,9 @@ ERL_NIF_TERM nsetopt_lvl_tcp(ErlNifEnv* env, #endif #if defined(TCP_MAXSEG) - case SOCKET_OPT_TCP_MAXSEG: - result = nsetopt_lvl_tcp_maxseg(env, descP, eVal); - break; + case SOCKET_OPT_TCP_MAXSEG: + result = nsetopt_lvl_tcp_maxseg(env, descP, eVal); + break; #endif #if defined(TCP_NODELAY) @@ -4074,12 +4232,12 @@ int socket_setopt(int sock, int level, int opt, int res; #if defined(IP_TOS) && defined(SOL_IP) && defined(SO_PRIORITY) - int tmpIValPRIO; - int tmpIValTOS; - int resPRIO; - int resTOS; - SOCKLEN_T tmpArgSzPRIO = sizeof(tmpIValPRIO); - SOCKLEN_T tmpArgSzTOS = sizeof(tmpIValTOS); + int tmpIValPRIO; + int tmpIValTOS; + int resPRIO; + int resTOS; + SOCKOPTLEN_T tmpArgSzPRIO = sizeof(tmpIValPRIO); + SOCKOPTLEN_T tmpArgSzTOS = sizeof(tmpIValTOS); resPRIO = sock_getopt(sock, SOL_SOCKET, SO_PRIORITY, &tmpIValPRIO, &tmpArgSzPRIO); @@ -4131,177 +4289,1009 @@ int socket_setopt(int sock, int level, int opt, /* ---------------------------------------------------------------------- - * nif_link_if2idx + * nif_getopt * * Description: - * Perform a Interface Name to Interface Index translation. + * Get socket option. + * Its possible to use a "raw" mode (not encoded). That is, we do not + * interpret level and opt. They are passed "as is" to the + * getsockopt function call. The value in this case will "copied" as + * is and provided to the user in the form of a binary. * * Arguments: - * Ifn - Interface name to be translated. + * Socket (ref) - Points to the socket descriptor. + * IsEncoded - Are the "arguments" encoded or not. + * Level - Level of the socket option. + * Opt - The socket option. */ static -ERL_NIF_TERM nif_link_if2idx(ErlNifEnv* env, - int argc, - const ERL_NIF_TERM argv[]) +ERL_NIF_TERM nif_getopt(ErlNifEnv* env, + int argc, + const ERL_NIF_TERM argv[]) { - ERL_NIF_TERM eifn; - char ifn[IF_NAMESIZE+1]; + SocketDescriptor* descP; + int eLevel, level = -1; + int eOpt; + ERL_NIF_TERM eIsEncoded; + BOOLEAN_T isEncoded, isOTP; - if (argc != 1) { - return enif_make_badarg(env); - } - eifn = argv[0]; + if ((argc != 4) || + !enif_get_resource(env, argv[0], sockets, (void**) &descP) || + !GET_INT(env, argv[2], &eLevel) || + !GET_INT(env, argv[3], &eOpt)) { + return enif_make_badarg(env); + } + eIsEncoded = argv[1]; - if (0 >= GET_STR(env, eifn, ifn, sizeof(ifn))) - return make_error2(env, atom_einval); + if (!decode_bool(env, eIsEncoded, &isEncoded)) + return make_error(env, atom_einval); - return nlink_if2idx(env, ifn); + if (!elevel2level(isEncoded, eLevel, &isOTP, &level)) + return make_error(env, atom_einval); + + return ngetopt(env, descP, isEncoded, isOTP, level, eOpt); } static -ERL_NIF_TERM nlink_if2idx(ErlNifEnv* env, - char* ifn) +ERL_NIF_TERM ngetopt(ErlNifEnv* env, + SocketDescriptor* descP, + BOOLEAN_T isEncoded, + BOOLEAN_T isOTP, + int level, + int eOpt) { - unsigned int idx = if_nametoindex(ifn); + ERL_NIF_TERM result; - if (idx == 0) - return make_error2(env, sock_errno()); - else - return make_ok2(env, idx); + if (isOTP) { + /* These are not actual socket options, + * but options for our implementation. + */ + result = ngetopt_otp(env, descP, eOpt); + } else if (!isEncoded) { + result = ngetopt_native(env, descP, level, eOpt); + } else { + result = ngetopt_level(env, descP, level, eOpt); + } + return result; } -/* ---------------------------------------------------------------------- - * nif_link_idx2if - * - * Description: - * Perform a Interface Index to Interface Name translation. - * - * Arguments: - * Idx - Interface index to be translated. +/* ngetopt_otp - Handle OTP (level) options */ - static -ERL_NIF_TERM nif_link_idx2if(ErlNifEnv* env, - int argc, - const ERL_NIF_TERM argv[]) +ERL_NIF_TERM ngetopt_otp(ErlNifEnv* env, + SocketDescriptor* descP, + int eOpt) { - unsigned int idx; + ERL_NIF_TERM result; - if ((argc != 1) || - !GET_UINT(env, argv[0], &idx)) { - return enif_make_badarg(env); + switch (eOpt) { + case SOCKET_OPT_OTP_DEBUG: + result = ngetopt_otp_debug(env, descP); + break; + + case SOCKET_OPT_OTP_IOW: + result = ngetopt_otp_iow(env, descP); + break; + + default: + result = make_error(env, atom_einval); + break; } - return nlink_idx2if(env, idx); + return result; } - +/* ngetopt_otp_debug - Handle the OTP (level) debug options + */ static -ERL_NIF_TERM nlink_idx2if(ErlNifEnv* env, - unsigned int idx) +ERL_NIF_TERM ngetopt_otp_debug(ErlNifEnv* env, + SocketDescriptor* descP) { - ERL_NIF_TERM result; - char* ifn = MALLOC(IF_NAMESIZE+1); - - if (ifn == NULL) - return enif_make_badarg(env); // PLACEHOLDER - - if (NULL == if_indextoname(idx, ifn)) { - result = make_ok2(env, MKS(env, ifn)); - } else { - result = make_error2(env, sock_errno()); - } + ERL_NIF_TERM eVal; - FREE(ifn); // OR IS THIS AUTOMATIC? + encode_bool(descP->dbg, &eVal); - return result; + return make_ok2(env, eVal); } - -/* ---------------------------------------------------------------------- - * nif_link_idx2if - * - * Description: - * Get network interface names and indexes. - * +/* ngetopt_otp_iow - Handle the OTP (level) iow options */ - static -ERL_NIF_TERM nif_link_ifs(ErlNifEnv* env, - int argc, - const ERL_NIF_TERM argv[]) +ERL_NIF_TERM ngetopt_otp_iow(ErlNifEnv* env, + SocketDescriptor* descP) { - if (argc != 0) { - return enif_make_badarg(env); - } + ERL_NIF_TERM eVal; - return nlink_ifs(env); + encode_bool(descP->iow, &eVal); + + return make_ok2(env, eVal); } +/* The option has *not* been encoded. Instead it has been provided + * in "native mode" (option is provided as is). In this case it will have the + * format: {NativeOpt :: integer(), ValueSize :: non_neg_integer()} + */ static -ERL_NIF_TERM nlink_ifs(ErlNifEnv* env) +ERL_NIF_TERM ngetopt_native(ErlNifEnv* env, + SocketDescriptor* descP, + int level, + int eOpt) { - ERL_NIF_TERM result; - struct if_nameindex* ifs = if_nameindex(); - - if (ifs == NULL) { - result = make_error2(env, sock_errno()); - } else { - /* - * We got some interfaces: - * 1) Calculate how many - the only way is to iterate through the list - * until its end (which is indicated by an entry with index = zero - * and if_name = NULL). - * 2) Allocate an ERL_NIF_TERM array of the calculated length. - * 3) Iterate through the array of interfaces and for each create - * a two tuple: {Idx, If} - * - * Or shall we instead build a list in reverse order and then when - * its done, reverse that? Check - */ - unsigned int len = nlink_ifs_length(ifs); + ERL_NIF_TERM result = enif_make_badarg(env); + int opt; + SOCKOPTLEN_T valueSz; - if (len > 0) { - ERL_NIF_TERM* array = MALLOC(len * sizeof(ERL_NIF_TERM)); - unsigned int i; + /* + * We should really make it possible to specify common specific types, + * such as integer or boolean (instead of the size)... + * + */ - for (i = 0; i < len; i++) { - array[i] = MKT2(env, - MKI(env, ifs[i].if_index), - MKS(env, ifs[i].if_name)); - } + if (decode_native_get_opt(env, eOpt, &opt, (int*) &valueSz)) { + int res; - result = make_ok2(env, MKLA(env, array, len)); - FREE(array); + if (valueSz == 0) { + res = sock_getopt(descP->sock, level, opt, NULL, NULL); + if (res != 0) + result = make_error2(env, res); + else + result = atom_ok; } else { - result = make_ok2(env, enif_make_list(env, 0)); + ErlNifBinary val; + + if (ALLOC_BIN(valueSz, &val)) { + res = sock_getopt(descP->sock, level, opt, val.data, &valueSz); + if (res != 0) { + result = make_error2(env, res); + } else { + if (valueSz < val.size) { + if (REALLOC_BIN(&val, valueSz)) { + result = make_ok2(env, MKBIN(env, &val)); + } else { + result = enif_make_badarg(env); + } + } + } + } else { + result = enif_make_badarg(env); + } } - } - if (ifs != NULL) - if_freenameindex(ifs); + } else { + result = make_error(env, atom_einval); + } return result; } +/* ngetopt_level - A "proper" level (option) has been specified + */ static -unsigned int nlink_ifs_length(struct if_nameindex* p) +ERL_NIF_TERM ngetopt_level(ErlNifEnv* env, + SocketDescriptor* descP, + int level, + int eOpt) { - unsigned int len = 0; + ERL_NIF_TERM result; - while ((p[len].if_index == 0) && (p[len].if_name == NULL)) { - len++; + switch (level) { + case SOL_SOCKET: + result = ngetopt_lvl_socket(env, descP, eOpt); + break; + +#if defined(SOL_IP) + case SOL_IP: +#else + case IPPROTO_IP: +#endif + result = ngetopt_lvl_ip(env, descP, eOpt); + break; + +#if defined(SOL_IPV6) + case SOL_IPV6: + result = ngetopt_lvl_ipv6(env, descP, eOpt); + break; +#endif + + case IPPROTO_TCP: + result = ngetopt_lvl_tcp(env, descP, eOpt); + break; + + case IPPROTO_UDP: + result = ngetopt_lvl_udp(env, descP, eOpt); + break; + +#if defined(HAVE_SCTP) + case IPPROTO_SCTP: + result = ngetopt_lvl_sctp(env, descP, eOpt); + break; +#endif + + default: + result = make_error(env, atom_einval); + break; + } + + return result; +} + + +/* ngetopt_lvl_socket - Level *SOCKET* option + */ +static +ERL_NIF_TERM ngetopt_lvl_socket(ErlNifEnv* env, + SocketDescriptor* descP, + int eOpt) +{ + ERL_NIF_TERM result; + + switch (eOpt) { +#if defined(SO_BROADCAST) + case SOCKET_OPT_SOCK_BROADCAST: + result = ngetopt_lvl_sock_broadcast(env, descP); + break; +#endif + +#if defined(SO_DONTROUTE) + case SOCKET_OPT_SOCK_DONTROUTE: + result = ngetopt_lvl_sock_dontroute(env, descP); + break; +#endif + +#if defined(SO_KEEPALIVE) + case SOCKET_OPT_SOCK_KEEPALIVE: + result = ngetopt_lvl_sock_keepalive(env, descP); + break; +#endif + +#if defined(SO_LINGER) + case SOCKET_OPT_SOCK_LINGER: + result = ngetopt_lvl_sock_linger(env, descP); + break; +#endif + +#if defined(SO_PRIORITY) + case SOCKET_OPT_SOCK_PRIORITY: + result = ngetopt_lvl_sock_priority(env, descP); + break; +#endif + +#if defined(SO_RCVBUF) + case SOCKET_OPT_SOCK_RCVBUF: + result = ngetopt_lvl_sock_rcvbuf(env, descP); + break; +#endif + +#if defined(SO_REUSEADDR) + case SOCKET_OPT_SOCK_REUSEADDR: + result = ngetopt_lvl_sock_reuseaddr(env, descP); + break; +#endif + +#if defined(SO_SNDBUF) + case SOCKET_OPT_SOCK_SNDBUF: + result = ngetopt_lvl_sock_sndbuf(env, descP); + break; +#endif + + default: + result = make_error(env, atom_einval); + break; + } + + return result; +} + + +#if defined(SO_BROADCAST) +static +ERL_NIF_TERM ngetopt_lvl_sock_broadcast(ErlNifEnv* env, + SocketDescriptor* descP) +{ + return ngetopt_bool_opt(env, descP, SOL_SOCKET, SO_BROADCAST); +} +#endif + + +#if defined(SO_DONTROUTE) +static +ERL_NIF_TERM ngetopt_lvl_sock_dontroute(ErlNifEnv* env, + SocketDescriptor* descP) +{ + return ngetopt_bool_opt(env, descP, SOL_SOCKET, SO_DONTROUTE); +} +#endif + + +#if defined(SO_KEEPALIVE) +static +ERL_NIF_TERM ngetopt_lvl_sock_keepalive(ErlNifEnv* env, + SocketDescriptor* descP) +{ + return ngetopt_bool_opt(env, descP, SOL_SOCKET, SO_KEEPALIVE); +} +#endif + + +#if defined(SO_LINGER) +static +ERL_NIF_TERM ngetopt_lvl_sock_linger(ErlNifEnv* env, + SocketDescriptor* descP) +{ + ERL_NIF_TERM result; + struct linger val; + SOCKOPTLEN_T valSz = sizeof(val); + int res; + + sys_memzero((void *) &val, sizeof(val)); + + res = sock_getopt(descP->sock, SOL_SOCKET, SO_LINGER, + &val, &valSz); + + if (res != 0) { + result = make_error2(env, res); + } else { + ERL_NIF_TERM lOnOff = MKI(env, val.l_onoff); + ERL_NIF_TERM lSecs = MKI(env, val.l_linger); + ERL_NIF_TERM linger = MKT2(env, lOnOff, lSecs); + + result = make_ok2(env, linger); + } + + return result; +} +#endif + + +#if defined(SO_PRIORITY) +static +ERL_NIF_TERM ngetopt_lvl_sock_priority(ErlNifEnv* env, + SocketDescriptor* descP) +{ + return ngetopt_int_opt(env, descP, SOL_SOCKET, SO_PRIORITY); +} +#endif + + +#if defined(SO_RCVBUF) +static +ERL_NIF_TERM ngetopt_lvl_sock_rcvbuf(ErlNifEnv* env, + SocketDescriptor* descP) +{ + return ngetopt_int_opt(env, descP, SOL_SOCKET, SO_RCVBUF); +} +#endif + + +#if defined(SO_REUSEADDR) +static +ERL_NIF_TERM ngetopt_lvl_sock_reuseaddr(ErlNifEnv* env, + SocketDescriptor* descP) +{ + return ngetopt_bool_opt(env, descP, SOL_SOCKET, SO_REUSEADDR); +} +#endif + + +#if defined(SO_SNDBUF) +static +ERL_NIF_TERM ngetopt_lvl_sock_sndbuf(ErlNifEnv* env, + SocketDescriptor* descP) +{ + return ngetopt_int_opt(env, descP, SOL_SOCKET, SO_SNDBUF); +} +#endif + + +/* ngetopt_lvl_ip - Level *IP* option(s) + */ +static +ERL_NIF_TERM ngetopt_lvl_ip(ErlNifEnv* env, + SocketDescriptor* descP, + int eOpt) +{ + ERL_NIF_TERM result; + + switch (eOpt) { +#if defined(IP_RECVTOS) + case SOCKET_OPT_IP_RECVTOS: + result = ngetopt_lvl_ip_recvtos(env, descP); + break; +#endif + +#if defined(IP_ROUTER_ALERT) + case SOCKET_OPT_IP_ROUTER_ALERT: + result = ngetopt_lvl_ip_router_alert(env, descP); + break; +#endif + +#if defined(IP_TOS) + case SOCKET_OPT_IP_TOS: + result = ngetopt_lvl_ip_tos(env, descP); + break; +#endif + +#if defined(IP_TTL) + case SOCKET_OPT_IP_TTL: + result = ngetopt_lvl_ip_ttl(env, descP); + break; +#endif + + default: + result = make_error(env, atom_einval); + break; + } + + return result; +} + + +/* ngetopt_lvl_ip_recvtos - Level IP RECVTOS option + */ +#if defined(IP_RECVTOS) +static +ERL_NIF_TERM ngetopt_lvl_ip_recvtos(ErlNifEnv* env, + SocketDescriptor* descP) +{ +#if defined(SOL_IP) + int level = SOL_IP; +#else + int level = IPPROTO_IP; +#endif + + return ngetopt_bool_opt(env, descP, level, IP_RECVTOS); +} +#endif + + +/* ngetopt_lvl_ip_router_alert - Level IP ROUTER_ALERT option + */ +#if defined(IP_ROUTER_ALERT) +static +ERL_NIF_TERM ngetopt_lvl_ip_router_alert(ErlNifEnv* env, + SocketDescriptor* descP) +{ +#if defined(SOL_IP) + int level = SOL_IP; +#else + int level = IPPROTO_IP; +#endif + + return ngetopt_int_opt(env, descP, level, IP_ROUTER_ALERT); +} +#endif + + +/* ngetopt_lvl_ip_tos - Level IP TOS option + */ +#if defined(IP_TOS) +static +ERL_NIF_TERM ngetopt_lvl_ip_tos(ErlNifEnv* env, + SocketDescriptor* descP) +{ +#if defined(SOL_IP) + int level = SOL_IP; +#else + int level = IPPROTO_IP; +#endif + ERL_NIF_TERM result; + int val; + SOCKOPTLEN_T valSz = sizeof(val); + int res; + + res = sock_getopt(descP->sock, level, IP_TOS, &val, &valSz); + + if (res != 0) { + result = make_error2(env, res); + } else { + result = encode_ip_tos(env, val); + } + + return result; +} +#endif + + +/* ngetopt_lvl_ip_ttl - Level IP TTL option + */ +#if defined(IP_TTL) +static +ERL_NIF_TERM ngetopt_lvl_ip_ttl(ErlNifEnv* env, + SocketDescriptor* descP) +{ +#if defined(SOL_IP) + int level = SOL_IP; +#else + int level = IPPROTO_IP; +#endif + + return ngetopt_int_opt(env, descP, level, IP_TTL); +} +#endif + + + +/* ngetopt_lvl_ipv6 - Level *IPv6* option(s) + */ +#if defined(SOL_IPV6) +static +ERL_NIF_TERM ngetopt_lvl_ipv6(ErlNifEnv* env, + SocketDescriptor* descP, + int eOpt) +{ + ERL_NIF_TERM result; + + switch (eOpt) { +#if defined(IPV6_HOPLIMIT) + case SOCKET_OPT_IPV6_HOPLIMIT: + result = ngetopt_lvl_ipv6_hoplimit(env, descP); + break; +#endif + + default: + result = make_error(env, atom_einval); + break; + } + + return result; +} + + +#if defined(IPV6_HOPLIMIT) +static +ERL_NIF_TERM ngetopt_lvl_ipv6_hoplimit(ErlNifEnv* env, + SocketDescriptor* descP) +{ + return ngetopt_bool_opt(env, descP, SOL_IPV6, IPV6_HOPLIMIT); +} +#endif + + +#endif // defined(SOL_IPV6) + + + +/* ngetopt_lvl_tcp - Level *TCP* option(s) + */ +static +ERL_NIF_TERM ngetopt_lvl_tcp(ErlNifEnv* env, + SocketDescriptor* descP, + int eOpt) +{ + ERL_NIF_TERM result; + + switch (eOpt) { +#if defined(TCP_CONGESTION) + case SOCKET_OPT_TCP_CONGESTION: + result = ngetopt_lvl_tcp_congestion(env, descP); + break; +#endif + +#if defined(TCP_MAXSEG) + case SOCKET_OPT_TCP_MAXSEG: + result = ngetopt_lvl_tcp_maxseg(env, descP); + break; +#endif + +#if defined(TCP_NODELAY) + case SOCKET_OPT_TCP_NODELAY: + result = ngetopt_lvl_tcp_nodelay(env, descP); + break; +#endif + + default: + result = make_error(env, atom_einval); + break; + } + + return result; +} + + +/* ngetopt_lvl_tcp_congestion - Level TCP CONGESTION option + */ +#if defined(TCP_CONGESTION) +static +ERL_NIF_TERM ngetopt_lvl_tcp_congestion(ErlNifEnv* env, + SocketDescriptor* descP) +{ + int max = SOCKET_OPT_TCP_CONGESTION_NAME_MAX+1; + + return ngetopt_str_opt(env, descP, IPPROTO_TCP, TCP_CONGESTION, max); +} +#endif + + +/* ngetopt_lvl_tcp_maxseg - Level TCP MAXSEG option + */ +#if defined(TCP_MAXSEG) +static +ERL_NIF_TERM ngetopt_lvl_tcp_maxseg(ErlNifEnv* env, + SocketDescriptor* descP) +{ + return ngetopt_int_opt(env, descP, IPPROTO_TCP, TCP_MAXSEG); +} +#endif + + +/* ngetopt_lvl_tcp_nodelay - Level TCP NODELAY option + */ +#if defined(TCP_NODELAY) +static +ERL_NIF_TERM ngetopt_lvl_tcp_nodelay(ErlNifEnv* env, + SocketDescriptor* descP) +{ + return ngetopt_bool_opt(env, descP, IPPROTO_TCP, TCP_NODELAY); +} +#endif + + + +/* ngetopt_lvl_udp - Level *UDP* option(s) + */ +static +ERL_NIF_TERM ngetopt_lvl_udp(ErlNifEnv* env, + SocketDescriptor* descP, + int eOpt) +{ + ERL_NIF_TERM result; + + switch (eOpt) { +#if defined(UDP_CORK) + case SOCKET_OPT_UDP_CORK: + result = ngetopt_lvl_udp_cork(env, descP); + break; +#endif + + default: + result = make_error(env, atom_einval); + break; + } + + return result; +} + + +/* ngetopt_lvl_udp_cork - Level UDP CORK option + */ +#if defined(UDP_CORK) +static +ERL_NIF_TERM ngetopt_lvl_udp_cork(ErlNifEnv* env, + SocketDescriptor* descP) +{ + return ngetopt_bool_opt(env, descP, IPPROTO_UDP, UDP_CORK); +} +#endif + + + +/* ngetopt_lvl_sctp - Level *SCTP* option(s) + */ +#if defined(HAVE_SCTP) +static +ERL_NIF_TERM ngetopt_lvl_sctp(ErlNifEnv* env, + SocketDescriptor* descP, + int eOpt) +{ + ERL_NIF_TERM result; + + switch (eOpt) { +#if defined(SCTP_AUTOCLOSE) + case SOCKET_OPT_SCTP_AUTOCLOSE: + result = ngetopt_lvl_sctp_autoclose(env, descP); + break; +#endif + +#if defined(SCTP_NODELAY) + case SOCKET_OPT_SCTP_NODELAY: + result = ngetopt_lvl_sctp_nodelay(env, descP); + break; +#endif + + default: + result = make_error(env, atom_einval); + break; + } + + return result; +} + + +/* ngetopt_lvl_sctp_autoclose - Level SCTP AUTOCLOSE option + */ +#if defined(SCTP_AUTOCLOSE) +static +ERL_NIF_TERM ngetopt_lvl_sctp_autoclose(ErlNifEnv* env, + SocketDescriptor* descP) +{ + return ngetopt_int_opt(env, descP, IPPROTO_SCTP, SCTP_AUTOCLOSE); +} +#endif + + +/* ngetopt_lvl_sctp_nodelay - Level SCTP NODELAY option + */ +#if defined(SCTP_NODELAY) +static +ERL_NIF_TERM ngetopt_lvl_sctp_nodelay(ErlNifEnv* env, + SocketDescriptor* descP) +{ + return ngetopt_bool_opt(env, descP, IPPROTO_SCTP, SCTP_NODELAY); +} +#endif + + +#endif // defined(HAVE_SCTP) + + + +/* ngetopt_str_opt - get an string option + */ +static +ERL_NIF_TERM ngetopt_str_opt(ErlNifEnv* env, + SocketDescriptor* descP, + int level, + int opt, + int max) +{ + ERL_NIF_TERM result; + char* val = MALLOC(max); + SOCKOPTLEN_T valSz = max; + int res; + + res = sock_getopt(descP->sock, level, opt, &val, &valSz); + + if (res != 0) { + result = make_error2(env, res); + } else { + ERL_NIF_TERM sval = MKSL(env, val, valSz); + + result = make_ok2(env, sval); + } + + FREE(val); + + return result; +} + + +/* nsetopt_bool_opt - get an (integer) bool option + */ +static +ERL_NIF_TERM ngetopt_bool_opt(ErlNifEnv* env, + SocketDescriptor* descP, + int level, + int opt) +{ + ERL_NIF_TERM result; + int val; + SOCKOPTLEN_T valSz = sizeof(val); + int res; + + res = sock_getopt(descP->sock, level, opt, &val, &valSz); + + if (res != 0) { + result = make_error2(env, res); + } else { + ERL_NIF_TERM bval = ((val) ? atom_true : atom_false); + + result = make_ok2(env, bval); + } + + return result; +} + + +/* nsetopt_int_opt - get an integer option + */ +static +ERL_NIF_TERM ngetopt_int_opt(ErlNifEnv* env, + SocketDescriptor* descP, + int level, + int opt) +{ + ERL_NIF_TERM result; + int val; + SOCKOPTLEN_T valSz = sizeof(val); + int res; + + res = sock_getopt(descP->sock, level, opt, &val, &valSz); + + if (res != 0) { + result = make_error2(env, res); + } else { + result = make_ok2(env, MKI(env, val)); + } + + return result; +} + + + + +/* ---------------------------------------------------------------------- + * nif_link_if2idx + * + * Description: + * Perform a Interface Name to Interface Index translation. + * + * Arguments: + * Ifn - Interface name to be translated. + */ + +static +ERL_NIF_TERM nif_link_if2idx(ErlNifEnv* env, + int argc, + const ERL_NIF_TERM argv[]) +{ + ERL_NIF_TERM eifn; + char ifn[IF_NAMESIZE+1]; + + if (argc != 1) { + return enif_make_badarg(env); + } + eifn = argv[0]; + + if (0 >= GET_STR(env, eifn, ifn, sizeof(ifn))) + return make_error2(env, atom_einval); + + return nlink_if2idx(env, ifn); +} + + + +static +ERL_NIF_TERM nlink_if2idx(ErlNifEnv* env, + char* ifn) +{ + unsigned int idx = if_nametoindex(ifn); + + if (idx == 0) + return make_error2(env, sock_errno()); + else + return make_ok2(env, idx); + +} + + + +/* ---------------------------------------------------------------------- + * nif_link_idx2if + * + * Description: + * Perform a Interface Index to Interface Name translation. + * + * Arguments: + * Idx - Interface index to be translated. + */ + +static +ERL_NIF_TERM nif_link_idx2if(ErlNifEnv* env, + int argc, + const ERL_NIF_TERM argv[]) +{ + unsigned int idx; + + if ((argc != 1) || + !GET_UINT(env, argv[0], &idx)) { + return enif_make_badarg(env); + } + + return nlink_idx2if(env, idx); +} + + + +static +ERL_NIF_TERM nlink_idx2if(ErlNifEnv* env, + unsigned int idx) +{ + ERL_NIF_TERM result; + char* ifn = MALLOC(IF_NAMESIZE+1); + + if (ifn == NULL) + return enif_make_badarg(env); // PLACEHOLDER + + if (NULL == if_indextoname(idx, ifn)) { + result = make_ok2(env, MKS(env, ifn)); + } else { + result = make_error2(env, sock_errno()); + } + + FREE(ifn); // OR IS THIS AUTOMATIC? + + return result; +} + + + +/* ---------------------------------------------------------------------- + * nif_link_idx2if + * + * Description: + * Get network interface names and indexes. + * + */ + +static +ERL_NIF_TERM nif_link_ifs(ErlNifEnv* env, + int argc, + const ERL_NIF_TERM argv[]) +{ + if (argc != 0) { + return enif_make_badarg(env); + } + + return nlink_ifs(env); +} + + + +static +ERL_NIF_TERM nlink_ifs(ErlNifEnv* env) +{ + ERL_NIF_TERM result; + struct if_nameindex* ifs = if_nameindex(); + + if (ifs == NULL) { + result = make_error2(env, sock_errno()); + } else { + /* + * We got some interfaces: + * 1) Calculate how many - the only way is to iterate through the list + * until its end (which is indicated by an entry with index = zero + * and if_name = NULL). + * 2) Allocate an ERL_NIF_TERM array of the calculated length. + * 3) Iterate through the array of interfaces and for each create + * a two tuple: {Idx, If} + * + * Or shall we instead build a list in reverse order and then when + * its done, reverse that? Check + */ + unsigned int len = nlink_ifs_length(ifs); + + if (len > 0) { + ERL_NIF_TERM* array = MALLOC(len * sizeof(ERL_NIF_TERM)); + unsigned int i; + + for (i = 0; i < len; i++) { + array[i] = MKT2(env, + MKI(env, ifs[i].if_index), + MKS(env, ifs[i].if_name)); + } + + result = make_ok2(env, MKLA(env, array, len)); + FREE(array); + } else { + result = make_ok2(env, enif_make_list(env, 0)); + } + } + + if (ifs != NULL) + if_freenameindex(ifs); + + return result; +} + + +static +unsigned int nlink_ifs_length(struct if_nameindex* p) +{ + unsigned int len = 0; + + while ((p[len].if_index == 0) && (p[len].if_name == NULL)) { + len++; } return len; @@ -4977,7 +5967,6 @@ BOOLEAN_T decode_bool(ErlNifEnv* env, ERL_NIF_TERM eVal, BOOLEAN_T* val) } - /* +++ decode the linger value +++ * The (socket) linger option is provided as a two tuple: * @@ -5015,7 +6004,7 @@ BOOLEAN_T decode_sock_linger(ErlNifEnv* env, ERL_NIF_TERM eVal, struct linger* v -/* +++ decocde the ip socket option tos +++ +/* +++ decode the ip socket option tos +++ * The (ip) option can be provide in two ways: * * atom() | integer() @@ -5032,11 +6021,11 @@ BOOLEAN_T decode_ip_tos(ErlNifEnv* env, ERL_NIF_TERM eVal, int* val) if (IS_ATOM(env, eVal)) { unsigned int len; - char b[sizeof("reliability")+1]; // Just in case... + char b[sizeof(str_reliability)+1]; // Just in case... if (!(GET_ATOM_LEN(env, eVal, &len) && (len > 0) && - (len <= (sizeof("reliability"))))) { + (len <= (sizeof(str_reliability))))) { *val = -1; return FALSE; } @@ -5046,16 +6035,16 @@ BOOLEAN_T decode_ip_tos(ErlNifEnv* env, ERL_NIF_TERM eVal, int* val) return FALSE; } - if (strncmp(b, "lowdelay", len) == 0) { + if (strncmp(b, str_lowdelay, len) == 0) { *val = IPTOS_LOWDELAY; result = TRUE; - } else if (strncmp(b, "throughput", len) == 0) { + } else if (strncmp(b, str_throughput, len) == 0) { *val = IPTOS_THROUGHPUT; result = TRUE; - } else if (strncmp(b, "reliability", len) == 0) { + } else if (strncmp(b, str_reliability, len) == 0) { *val = IPTOS_RELIABILITY; result = TRUE; - } else if (strncmp(b, "mincost", len) == 0) { + } else if (strncmp(b, str_mincost, len) == 0) { *val = IPTOS_MINCOST; result = TRUE; } else { @@ -5082,6 +6071,92 @@ BOOLEAN_T decode_ip_tos(ErlNifEnv* env, ERL_NIF_TERM eVal, int* val) +/* +++ decocde the native getopt option +++ + * The option is in this case provide in the form of a two tuple: + * + * {NativeOpt, ValueSize} + * + * NativeOpt :: integer() + * ValueSize :: non_neg_integer() + * + */ +static +BOOLEAN_T decode_native_get_opt(ErlNifEnv* env, ERL_NIF_TERM eVal, + int* opt, int* valueSz) +{ + const ERL_NIF_TERM* nativeOptT; + int nativeOptTSz; + + /* First, get the tuple and verify its size (2) */ + + if (!GET_TUPLE(env, eVal, &nativeOptTSz, &nativeOptT)) + return FALSE; + + if (nativeOptTSz != 2) + return FALSE; + + /* So far so good. Both elements should be integers */ + + if (!GET_INT(env, nativeOptT[0], opt)) + return FALSE; + + if (!GET_INT(env, nativeOptT[1], valueSz)) + return FALSE; + + return TRUE; +} + + +static +void encode_bool(BOOLEAN_T val, ERL_NIF_TERM* eVal) +{ + if (val) + *eVal = atom_true; + else + *eVal = atom_false; +} + + +/* +++ encode the ip socket option tos +++ + * The (ip) option can be provide as: + * + * lowdelay | throughput | reliability | mincost | integer() + * + */ +static +ERL_NIF_TERM encode_ip_tos(ErlNifEnv* env, int val) +{ + ERL_NIF_TERM result; + + switch (val) { + case IPTOS_LOWDELAY: + result = make_ok2(env, atom_lowdelay); + break; + + case IPTOS_THROUGHPUT: + result = make_ok2(env, atom_throughput); + break; + + case IPTOS_RELIABILITY: + result = make_ok2(env, atom_reliability); + break; + + case IPTOS_MINCOST: + result = make_ok2(env, atom_mincost); + break; + + default: + result = make_ok2(env, MKI(env, val)); + break; + } + + return result; +} + + + + + /* *** alloc_descriptor *** * Allocate and perform basic initialization of a socket descriptor. * @@ -5999,6 +7074,11 @@ int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) atom_undefined = MKA(env, str_undefined); // atom_version = MKA(env, str_version); + atom_lowdelay = MKA(env, str_lowdelay); + atom_throughput = MKA(env, str_throughput); + atom_reliability = MKA(env, str_reliability); + atom_mincost = MKA(env, str_mincost); + /* Error codes */ atom_eagain = MKA(env, str_eagain); atom_eafnosupport = MKA(env, str_eafnosupport); diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl index 7b53f271ef..f8aa75bf8f 100644 --- a/erts/preloaded/src/socket.erl +++ b/erts/preloaded/src/socket.erl @@ -272,6 +272,7 @@ v6only. -type tcp_socket_option() :: congestion | + cork | maxseg | nodelay. @@ -451,10 +452,11 @@ -define(SOCKET_OPT_IPV6_HOPLIMIT, 12). -define(SOCKET_OPT_TCP_CONGESTION, 0). --define(SOCKET_OPT_TCP_MAXSEG, 1). --define(SOCKET_OPT_TCP_NODELAY, 2). +-define(SOCKET_OPT_TCP_CORK, 2). +-define(SOCKET_OPT_TCP_MAXSEG, 3). +-define(SOCKET_OPT_TCP_NODELAY, 4). --define(SOCKET_OPT_UDP_CORK, 1). +-define(SOCKET_OPT_UDP_CORK, 0). -define(SOCKET_OPT_SCTP_AUTOCLOSE, 7). -define(SOCKET_OPT_SCTP_NODELAY, 22). @@ -1370,6 +1372,10 @@ setopt(#socket{info = Info, ref = SockRef}, Level, Key, Value) -> %% If its an "invalid" option, we should not crash but return some %% useful error... %% +%% When specifying level as an integer, and therefor using "native mode", +%% we should make it possible to specify common types instead of the +%% value size. Example: int | bool | {string, pos_integer()} | non_neg_integer() +%% -spec getopt(Socket, Level, Key) -> {ok, Value} | {error, Reason} when Socket :: socket(), @@ -1412,7 +1418,15 @@ setopt(#socket{info = Info, ref = SockRef}, Level, Key, Value) -> Level :: sctp, Key :: sctp_socket_option(), Value :: term(), - Reason :: term(). + Reason :: term() + ; (Socket, Level, Key) -> ok | {ok, Value} | {error, Reason} when + Socket :: socket(), + Level :: integer(), + Key :: {NativeOpt, ValueSize}, + NativeOpt :: integer(), + ValueSize :: non_neg_integer(), + Value :: term(), + Reason :: term(). getopt(#socket{info = Info, ref = SockRef}, Level, Key) -> try @@ -1425,6 +1439,8 @@ getopt(#socket{info = Info, ref = SockRef}, Level, Key) -> %% We may need to decode the value (for the same reason %% we needed to encode the value for setopt). case nif_getopt(SockRef, EIsEncoded, ELevel, EKey) of + ok -> + ok; {ok, EVal} -> Val = dec_getopt_value(Level, Key, EVal, Domain, Type, Protocol), @@ -1716,10 +1732,14 @@ enc_getopt_key(Level, Opt, Domain, Type, Protocol) -> %% +++ Decode getopt value +++ +%% +%% For the most part, we simply let the value pass through, but for some +%% values we do an actual decode. +%% -%% We should ...really... do something with the domain, type and protocol args... -dec_getopt_value(otp, debug, B, _, _, _) when is_boolean(B) -> - B. +%% Let the user deal with this... +dec_getopt_value(_L, _Opt, V, _D, _T, _P) -> + V. @@ -1731,64 +1751,74 @@ dec_getopt_value(otp, debug, B, _, _, _) when is_boolean(B) -> Direction, Domain, Type, Protocol) -> non_neg_integer() when Level :: otp, - Opt :: otp_socket_option(), Direction :: set | get, + Opt :: otp_socket_option(), Domain :: domain(), Type :: type(), Protocol :: protocol() ; (Level, Direction, Opt, Domain, Type, Protocol) -> non_neg_integer() when Level :: socket, - Opt :: socket_option(), Direction :: set | get, + Opt :: socket_option(), Domain :: domain(), Type :: type(), Protocol :: protocol() ; (Level, Direction, Opt, Domain, Type, Protocol) -> non_neg_integer() when Level :: ip, - Opt :: ip_socket_option(), Direction :: set | get, + Opt :: ip_socket_option(), Domain :: domain(), Type :: type(), Protocol :: protocol() ; (Level, Direction, Opt, Domain, Type, Protocol) -> non_neg_integer() when Level :: ipv6, - Opt :: ipv6_socket_option(), Direction :: set | get, + Opt :: ipv6_socket_option(), Domain :: domain(), Type :: type(), Protocol :: protocol() ; (Level, Direction, Opt, Domain, Type, Protocol) -> non_neg_integer() when Level :: tcp, - Opt :: tcp_socket_option(), Direction :: set | get, + Opt :: tcp_socket_option(), Domain :: domain(), Type :: type(), Protocol :: protocol() ; (Level, Direction, Opt, Domain, Type, Protocol) -> non_neg_integer() when Level :: udp, - Opt :: udp_socket_option(), Direction :: set | get, + Opt :: udp_socket_option(), Domain :: domain(), Type :: type(), Protocol :: protocol() ; (Level, Direction, Opt, Domain, Type, Protocol) -> non_neg_integer() when Level :: sctp, - Opt :: sctp_socket_option(), Direction :: set | get, + Opt :: sctp_socket_option(), Domain :: domain(), Type :: type(), Protocol :: protocol() ; (Level, Direction, Opt, Domain, Type, Protocol) -> non_neg_integer() when Level :: integer(), + Direction :: set, Opt :: integer(), - Direction :: set | get, + Domain :: domain(), + Type :: type(), + Protocol :: protocol() + ; (Level, Direction, Opt, + Domain, Type, Protocol) -> non_neg_integer() when + Level :: integer(), + Direction :: get, + Opt :: {NativeOpt, ValueSize}, + NativeOpt :: integer(), + ValueSize :: non_neg_integer(), Domain :: domain(), Type :: type(), Protocol :: protocol(). @@ -1960,6 +1990,8 @@ enc_sockopt_key(ipv6, UnknownOpt, _Dir, _D, _T, _P) -> %% but they are difficult to get portable... enc_sockopt_key(tcp, congestion = _Opt, _Dir, _D, _T, _P) -> ?SOCKET_OPT_TCP_CONGESTION; +enc_sockopt_key(tcp, cork = Opt, _Dir, _D, _T, _P) -> + not_supported(Opt); enc_sockopt_key(tcp, maxseg = _Opt, _Dir, _D, _T, _P) -> ?SOCKET_OPT_TCP_MAXSEG; enc_sockopt_key(tcp, nodelay = _Opt, _Dir, _D, _T, _P) -> @@ -1981,10 +2013,15 @@ enc_sockopt_key(sctp, nodelay = _Opt, _Dir, _D, _T, _P) -> enc_sockopt_key(sctp, UnknownOpt, _Dir, _D, _T, _P) -> unknown(UnknownOpt); -%% +++ Plain socket options +++ -enc_sockopt_key(Level, Opt, _Dir, _D, _T, _P) +%% +++ "Native" socket options +++ +enc_sockopt_key(Level, Opt, set = _Dir, _D, _T, _P) when is_integer(Level) andalso is_integer(Opt) -> Opt; +enc_sockopt_key(Level, {NativeOpt, ValueSize} = Opt, get = _Dir, _D, _T, _P) + when is_integer(Level) andalso + is_integer(NativeOpt) andalso + is_integer(ValueSize) andalso (ValueSize >= 0) -> + Opt; enc_sockopt_key(Level, Opt, _Dir, _Domain, _Type, _Protocol) -> unknown({Level, Opt}). -- cgit v1.2.3