From 8118227d80fc41efac23d30a5601fdfadb75931f Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Tue, 10 Jul 2018 17:41:40 +0200 Subject: [socket-nif] Add support for socket (level ip) option multicast_if Added support for the IP option MULTICAST_IF. OTP-14831 --- erts/emulator/nifs/common/socket_nif.c | 518 +++++++------------------------- erts/emulator/nifs/common/socket_util.c | 83 +++-- erts/emulator/nifs/common/socket_util.h | 18 +- erts/preloaded/ebin/socket.beam | Bin 43884 -> 44012 bytes erts/preloaded/src/socket.erl | 9 +- 5 files changed, 178 insertions(+), 450 deletions(-) (limited to 'erts') diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index f73364f28f..05ba9e55f1 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -362,6 +362,7 @@ typedef union { #define SOCKET_OPT_SOCK_SNDBUF 27 #define SOCKET_OPT_SOCK_TYPE 32 +#define SOCKET_OPT_IP_MULTICAST_IF 14 #define SOCKET_OPT_IP_MULTICAST_LOOP 15 #define SOCKET_OPT_IP_MULTICAST_TTL 16 #define SOCKET_OPT_IP_RECVTOS 25 @@ -845,6 +846,11 @@ static ERL_NIF_TERM nsetopt_lvl_ip(ErlNifEnv* env, SocketDescriptor* descP, int eOpt, ERL_NIF_TERM eVal); +#if defined(IP_MULTICAST_IF) +static ERL_NIF_TERM nsetopt_lvl_ip_multicast_if(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal); +#endif #if defined(IP_MULTICAST_LOOP) static ERL_NIF_TERM nsetopt_lvl_ip_multicast_loop(ErlNifEnv* env, SocketDescriptor* descP, @@ -1023,6 +1029,10 @@ static ERL_NIF_TERM ngetopt_lvl_sock_type(ErlNifEnv* env, static ERL_NIF_TERM ngetopt_lvl_ip(ErlNifEnv* env, SocketDescriptor* descP, int eOpt); +#if defined(IP_MULTICAST_IF) +static ERL_NIF_TERM ngetopt_lvl_ip_multicast_if(ErlNifEnv* env, + SocketDescriptor* descP); +#endif #if defined(IP_MULTICAST_LOOP) static ERL_NIF_TERM ngetopt_lvl_ip_multicast_loop(ErlNifEnv* env, SocketDescriptor* descP); @@ -1974,6 +1984,8 @@ ERL_NIF_TERM nbind(ErlNifEnv* env, return esock_make_error_errno(env, sock_errno()); } + SSDBG( descP, ("SOCKET", "nbind -> bound - get port\r\n") ); + port = which_address_port(sockAddrP); SSDBG( descP, ("SOCKET", "nbind -> port: %d\r\n", port) ); if (port == 0) { @@ -3646,7 +3658,7 @@ ERL_NIF_TERM nsetopt_native(ErlNifEnv* env, int res = socket_setopt(descP->sock, level, opt, val.data, val.size); if (res != 0) - result = esock_make_error_errno(env, res); + result = esock_make_error_errno(env, sock_errno()); else result = esock_atom_ok; } else { @@ -3866,7 +3878,7 @@ ERL_NIF_TERM nsetopt_lvl_sock_linger(ErlNifEnv* env, int res = socket_setopt(descP->sock, SOL_SOCKET, SO_LINGER, (void*) &val, optLen); if (res != 0) - result = esock_make_error_errno(env, res); + result = esock_make_error_errno(env, sock_errno()); else result = esock_atom_ok; } else { @@ -3956,6 +3968,12 @@ ERL_NIF_TERM nsetopt_lvl_ip(ErlNifEnv* env, ERL_NIF_TERM result; switch (eOpt) { +#if defined(IP_MULTICAST_IF) + case SOCKET_OPT_IP_MULTICAST_IF: + result = nsetopt_lvl_ip_multicast_if(env, descP, eVal); + break; +#endif + #if defined(IP_MULTICAST_LOOP) case SOCKET_OPT_IP_MULTICAST_LOOP: result = nsetopt_lvl_ip_multicast_loop(env, descP, eVal); @@ -4001,6 +4019,45 @@ ERL_NIF_TERM nsetopt_lvl_ip(ErlNifEnv* env, } +/* nsetopt_lvl_ip_multicast_if - Level IP MULTICAST_IF option + * + * The value is either the atom 'any' or a 4-tuple. + */ +#if defined(IP_MULTICAST_IF) +static +ERL_NIF_TERM nsetopt_lvl_ip_multicast_if(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal) +{ + ERL_NIF_TERM result; + struct in_addr ifAddr; + char* xres; + int res; +#if defined(SOL_IP) + int level = SOL_IP; +#else + int level = IPPROTO_IP; +#endif + + if ((xres = esock_decode_ip4_address(env, eVal, &ifAddr)) != NULL) { + result = esock_make_error_str(env, xres); + } else { + + res = socket_setopt(descP->sock, level, IP_MULTICAST_LOOP, + &ifAddr, sizeof(ifAddr)); + + if (res != 0) + result = esock_make_error_errno(env, sock_errno()); + else + result = esock_atom_ok; + + } + + return result; +} +#endif + + /* nsetopt_lvl_ip_multicast_loop - Level IP MULTICAST_LOOP option */ #if defined(IP_MULTICAST_LOOP) @@ -4097,7 +4154,7 @@ ERL_NIF_TERM nsetopt_lvl_ip_tos(ErlNifEnv* env, int res = socket_setopt(descP->sock, level, IP_TOS, &val, sizeof(val)); if (res != 0) - result = esock_make_error_errno(env, res); + result = esock_make_error_errno(env, sock_errno()); else result = esock_atom_ok; @@ -4376,7 +4433,7 @@ ERL_NIF_TERM nsetopt_str_opt(ErlNifEnv* env, int res = socket_setopt(descP->sock, level, opt, &val, optLen); if (res != 0) - result = esock_make_error_errno(env, res); + result = esock_make_error_errno(env, sock_errno()); else result = esock_atom_ok; @@ -4409,7 +4466,7 @@ ERL_NIF_TERM nsetopt_bool_opt(ErlNifEnv* env, res = socket_setopt(descP->sock, level, opt, &ival, sizeof(ival)); if (res != 0) - result = esock_make_error_errno(env, res); + result = esock_make_error_errno(env, sock_errno()); else result = esock_atom_ok; @@ -4789,7 +4846,7 @@ ERL_NIF_TERM ngetopt_native_unspec(ErlNifEnv* env, if (valueSz == 0) { res = sock_getopt(descP->sock, level, opt, NULL, NULL); if (res != 0) - result = esock_make_error_errno(env, res); + result = esock_make_error_errno(env, sock_errno()); else result = esock_atom_ok; } else { @@ -4798,7 +4855,7 @@ ERL_NIF_TERM ngetopt_native_unspec(ErlNifEnv* env, if (ALLOC_BIN(valueSz, &val)) { res = sock_getopt(descP->sock, level, opt, val.data, &valueSz); if (res != 0) { - result = esock_make_error_errno(env, res); + result = esock_make_error_errno(env, sock_errno()); } else { if (valueSz < val.size) { if (REALLOC_BIN(&val, valueSz)) { @@ -5023,7 +5080,7 @@ ERL_NIF_TERM ngetopt_lvl_sock_domain(ErlNifEnv* env, &val, &valSz); if (res != 0) { - result = esock_make_error_errno(env, res); + result = esock_make_error_errno(env, sock_errno()); } else { switch (val) { case AF_INET: @@ -5090,7 +5147,7 @@ ERL_NIF_TERM ngetopt_lvl_sock_linger(ErlNifEnv* env, &val, &valSz); if (res != 0) { - result = esock_make_error_errno(env, res); + result = esock_make_error_errno(env, sock_errno()); } else { ERL_NIF_TERM lOnOff = ((val.l_onoff) ? atom_true : atom_false); ERL_NIF_TERM lSecs = MKI(env, val.l_linger); @@ -5148,7 +5205,7 @@ ERL_NIF_TERM ngetopt_lvl_sock_protocol(ErlNifEnv* env, &val, &valSz); if (res != 0) { - result = esock_make_error_errno(env, res); + result = esock_make_error_errno(env, sock_errno()); } else { switch (val) { case IPPROTO_IP: @@ -5224,7 +5281,7 @@ ERL_NIF_TERM ngetopt_lvl_sock_type(ErlNifEnv* env, res = sock_getopt(descP->sock, SOL_SOCKET, SO_TYPE, &val, &valSz); if (res != 0) { - result = esock_make_error_errno(env, res); + result = esock_make_error_errno(env, sock_errno()); } else { switch (val) { case SOCK_STREAM: @@ -5266,6 +5323,12 @@ ERL_NIF_TERM ngetopt_lvl_ip(ErlNifEnv* env, ERL_NIF_TERM result; switch (eOpt) { +#if defined(IP_MULTICAST_IF) + case SOCKET_OPT_IP_MULTICAST_IF: + result = ngetopt_lvl_ip_multicast_if(env, descP); + break; +#endif + #if defined(IP_MULTICAST_LOOP) case SOCKET_OPT_IP_MULTICAST_LOOP: result = ngetopt_lvl_ip_multicast_loop(env, descP); @@ -5311,6 +5374,43 @@ ERL_NIF_TERM ngetopt_lvl_ip(ErlNifEnv* env, } +/* ngetopt_lvl_ip_multicast_if - Level IP MULTICAST_IF option + */ +#if defined(IP_MULTICAST_IF) +static +ERL_NIF_TERM ngetopt_lvl_ip_multicast_if(ErlNifEnv* env, + SocketDescriptor* descP) +{ + ERL_NIF_TERM result; + ERL_NIF_TERM eAddr; + struct in_addr ifAddr; + SOCKOPTLEN_T ifAddrSz = sizeof(ifAddr); + char* xres; + int res; +#if defined(SOL_IP) + int level = SOL_IP; +#else + int level = IPPROTO_IP; +#endif + + res = sock_getopt(descP->sock, level, IP_MULTICAST_IF, &ifAddr, &ifAddrSz); + + if (res != 0) { + result = esock_make_error_errno(env, sock_errno()); + } else { + if ((xres = esock_encode_ip4_address(env, &ifAddr, &eAddr)) != NULL) { + result = esock_make_error_str(env, xres); + } else { + result = esock_make_ok2(env, eAddr); + } + } + + return result; + +} +#endif + + /* ngetopt_lvl_ip_multicast_loop - Level IP MULTICAST_LOOP option */ #if defined(IP_MULTICAST_LOOP) @@ -5403,7 +5503,7 @@ ERL_NIF_TERM ngetopt_lvl_ip_tos(ErlNifEnv* env, res = sock_getopt(descP->sock, level, IP_TOS, &val, &valSz); if (res != 0) { - result = esock_make_error_errno(env, res); + result = esock_make_error_errno(env, sock_errno()); } else { result = encode_ip_tos(env, val); } @@ -5663,7 +5763,7 @@ ERL_NIF_TERM ngetopt_str_opt(ErlNifEnv* env, res = sock_getopt(descP->sock, level, opt, &val, &valSz); if (res != 0) { - result = esock_make_error_errno(env, res); + result = esock_make_error_errno(env, sock_errno()); } else { ERL_NIF_TERM sval = MKSL(env, val, valSz); @@ -5692,7 +5792,7 @@ ERL_NIF_TERM ngetopt_bool_opt(ErlNifEnv* env, res = sock_getopt(descP->sock, level, opt, &val, &valSz); if (res != 0) { - result = esock_make_error_errno(env, res); + result = esock_make_error_errno(env, sock_errno()); } else { ERL_NIF_TERM bval = ((val) ? atom_true : atom_false); @@ -5719,7 +5819,7 @@ ERL_NIF_TERM ngetopt_int_opt(ErlNifEnv* env, res = sock_getopt(descP->sock, level, opt, &val, &valSz); if (res != 0) { - result = esock_make_error_errno(env, res); + result = esock_make_error_errno(env, sock_errno()); } else { result = esock_make_ok2(env, MKI(env, val)); } @@ -6936,394 +7036,6 @@ BOOLEAN_T ehow2how(unsigned int ehow, int* how) -/* +++ decode_sockaddr +++ - * - * Decode a socket address - sockaddr. In erlang its represented by - * a map, which has a specific set of attributes, depending on one - * mandatory attribute; family. So depending on the value of the family - * attribute: - * - * local - sockaddr_un: path - * inet - sockaddr_in4: port, addr - * inet6 - sockaddr_in6: port, addr, flowinfo, scope_id - */ -/* -static -char* decode_sockaddr(ErlNifEnv* env, - ERL_NIF_TERM eSockAddr, - SocketAddress* sockAddrP, - unsigned int* addrLen) -{ - ERL_NIF_TERM efam; - int fam; - char* res; - - if (!IS_MAP(env, eSockAddr)) - return ESOCK_STR_EINVAL; - - if (!GET_MAP_VAL(env, eSockAddr, esock_atom_family, &efam)) - return ESOCK_STR_EINVAL; - - if (!decode_domain(env, efam, &fam)) - return ESOCK_STR_EINVAL; - - switch (fam) { - case AF_INET: - res = decode_sockaddr_in4(env, eSockAddr, &sockAddrP->in6, addrLen); - break; - -#if defined(HAVE_IN6) && defined(AF_INET6) - case AF_INET6: - res = decode_sockaddr_in6(env, eSockAddr, &sockAddrP->in6, addrLen); - break; -#endif - -#ifdef HAVE_SYS_UN_H - case AF_UNIX: - res = decode_sockaddr_un(env, eSockAddr, &sockAddrP->un, addrLen); - break; -#endif - - default: - result = ESOCK_STR_EAFNOSUPPORT; - break; - - } - - return res; -} -*/ - - - -/* +++ decode_sockaddr_in4 +++ - * - * Decode a IPv4 socket address - sockaddr_in4. In erlang its represented by - * a map, which has a specific set of attributes: - * - * port :: port_numbber() - * addr :: ip4_address() - * - * The erlang module ensures that both of these has values exist, so there - * is no need for any elaborate error handling. - */ -/* -static -char* decode_sockaddr_in4(ErlNifEnv* env, - ERL_NIF_TERM eSockAddr, - struct sockaddr_in* sockAddrP, - unsigned int* addrLen) -{ - ERL_NIF_TERM eport, eaddr; - short port; - - / * Basic init * / - sys_memzero((char*) sockAddrP, sizeof(struct sockaddr_in)); - -#ifndef NO_SA_LEN - sockAddrP->sin_len = sizeof(struct sockaddr_in); -#endif - - sockAddrP->sin_family = AF_INET; - - / * Extract (e) port number from map * / - if (!GET_MAP_VAL(env, eSockAddr, atom_port, &eport)) - return ESOCK_STR_EINVAL; - - / * Decode port number * / - if (!GET_INT(env, eport, &port)) - return ESOCK_STR_EINVAL; - sockAddrP->sin_port = sock_htons(port); - - / * Extract (e) address from map * / - if (!GET_MAP_VAL(env, eSockAddr, atom_addr, &eaddr)) - return ESOCK_STR_EINVAL; - - / * Decode address * / - if (!decode_ip4_address(env, eaddr, sockAddrP, addrLen)) - return ESOCK_STR_EINVAL; - - return NULL; -} -*/ - - - -/* +++ decode_sockaddr_in6 +++ - * - * Decode a IPv6 socket address - sockaddr_in6. In erlang its represented by - * a map, which has a specific set of attributes: - * - * port :: port_numbber() (integer) - * addr :: ip6_address() (tuple) - * flowinfo :: in6_flow_info() (integer) - * scope_id :: in6_scope_id() (integer) - * - * The erlang module ensures that all of these has values exist, so there - * is no need for any elaborate error handling. - */ -/* -#if defined(HAVE_IN6) && defined(AF_INET6) -static -char* decode_sockaddr_in6(ErlNifEnv* env, - ERL_NIF_TERM eSockAddr, - struct sockaddr_in6* sockAddrP, - unsigned int* addrLen) -{ - ERL_NIF_TERM eport, eaddr; - short port; - - / * Basic init * / - sys_memzero((char*) sockAddrP, sizeof(struct sockaddr_in6)); -#ifndef NO_SA_LEN - sockAddrP->sin6_len = sizeof(struct sockaddr_in); -#endif - - sockAddrP->sin6_family = AF_INET6; - - / * *** Extract (e) port number from map *** * / - if (!GET_MAP_VAL(env, eSockAddr, atom_port, &eport)) - return ESOCK_STR_EINVAL; - - / * Decode port number * / - if (!GET_INT(env, eport, &port)) - return ESOCK_STR_EINVAL; - - sockAddrP->sin6_port = sock_htons(port); - - / * *** Extract (e) flowinfo from map *** * / - if (!GET_MAP_VAL(env, eSockAddr, atom_flowinfo, &eflowInfo)) - return ESOCK_STR_EINVAL; - - / * 4: Get the flowinfo * / - if (!GET_UINT(env, eflowInfo, &flowInfo)) - return ESOCK_STR_EINVAL; - - sockAddrP->sin6_flowinfo = flowInfo; - - / * *** Extract (e) scope_id from map *** * / - if (!GET_MAP_VAL(env, eSockAddr, atom_scope_id, &escopeId)) - return ESOCK_STR_EINVAL; - - / * *** Get the scope_id *** * / - if (!GET_UINT(env, escopeId, &scopeId)) - return ESOCK_STR_EINVAL; - - sockAddrP->sin6_scope_id = scopeId; - - / * *** Extract (e) address from map *** * / - if (!GET_MAP_VAL(env, eSockAddr, atom_addr, &eaddr)) - return ESOCK_STR_EINVAL; - - / * Decode address * / - if (!decode_ip6_address(env, eaddr, sockAddrP, addrLen)) - return ESOCK_STR_EINVAL; - - return NULL; -} -#endif -*/ - - - - -/* +++ decode_sockaddr_un +++ - * - * Decode a Unix Domain socket address - sockaddr_un. In erlang its represented by - * a map, which has a specific set of attributes: - * - * path :: binary() - * - * The erlang module ensures that this value exist, so there - * is no need for any elaborate error handling. - */ -/* -#ifdef HAVE_SYS_UN_H -static -char* decode_sockaddr_un(ErlNifEnv* env, - ERL_NIF_TERM eSockAddr, - struct sockaddr_un* sockAddrP, - unsigned int* addrLen) -{ - ErlNifBinary bin; - ERL_NIF_TERM epath; - unsigned int len; - - / * *** Extract (e) path (a binary) from map *** * / - if (!GET_MAP_VAL(env, eSockAddr, atom_port, &epath)) - return ESOCK_STR_EINVAL; - - / * Get the path * / - if (!GET_BIN(env, epath, &bin)) - return ESOCK_STR_EINVAL; - - if ((bin.size + -#ifdef __linux__ - / * Make sure the address gets zero terminated - * except when the first byte is \0 because then it is - * sort of zero terminated although the zero termination - * comes before the address... - * This fix handles Linux's nonportable - * abstract socket address extension. - * / - (bin.data[0] == '\0' ? 0 : 1) -#else - 1 -#endif - ) > sizeof(sockaAddrP->sun_path)) - return ESOCK_STR_EINVAL; - - - sys_memzero((char*) sockAddrP, sizeof(struct sockaddr_un)); - sockAddrP->sun_family = AF_UNIX; - - sys_memcpy(sockAddrP->sun_path, bin.data, bin.size); - len = offsetof(struct sockaddr_un, sun_path) + bin.size; - -#ifndef NO_SA_LEN - sockAddrP->sun_len = len; -#endif - *addrLen = len: - - return NULL; -} -#endif -*/ - - - -/* +++ decode_ip4_address +++ - * - * Decode a IPv4 address. This can be three things: - * - * + Then atom 'any' - * + Then atom 'loopback' - * + An ip4_address() (4 tuple) - * - * Note that this *only* decodes the "address" part of a - * (IPv4) socket address. There are several other things (port). - */ -/* -static -char* decode_ip4_address(ErlNifEnv* env, - ERL_NIF_TERM eAddr, - struct sockaddr_in* sockAddrP, - unsigned int* addrLen) -{ - if (IS_ATOM(env, eAddr)) { - / * This is either 'any' or 'loopback' * / - struct in_addr addr; - - if (COMPARE(esock_atom_loopback, eAddr) == 0) { - addr.s_addr = sock_htonl(INADDR_LOOPBACK); - } else if (COMPARE(esock_atom_any, eAddr) == 0) { - addr.s_addr = sock_htonl(INADDR_ANY); - } else { - return ESOCK_STR_EINVAL; - } - - sockAddrP->sin_addr.s_addr = addr.s_addr; - *addrLen = sizeof(struct sockaddr_in); - - } else { - / * This is a 4-tuple * / - - const ERL_NIF_TERM* addrt; - int addrtSz; - int a, v; - char addr[4]; - - if (!GET_TUPLE(env, eAddr, &addrtSz, &addrt)) - return ESOCK_STR_EINVAL; - - if (addrtSz != 4) - return ESOCK_STR_EINVAL; - - for (a = 0; a < 4; a++) { - if (!GET_INT(env, addrt[a], &v)) - return ESOCK_STR_EINVAL; - addr[a] = v; - } - - sys_memcpy(&sockAddrP->sin_addr, &addr, sizeof(addr)); - *addrLenP = sizeof(struct sockaddr_in); - - } - - return NULL; -} -*/ - - - -/* +++ decode_ip6_address +++ - * - * Decode a IPv6 address. This can be three things: - * - * + Then atom 'any' - * + Then atom 'loopback' - * + An ip6_address() (8 tuple) - * - * Note that this *only* decodes the "address" part of a - * (IPv6) socket address. There are several other things - * (port, flowinfo and scope_id) that are handled elsewhere). - */ -/* -#if defined(HAVE_IN6) && defined(AF_INET6) -static -char* decode_ip6_address(ErlNifEnv* env, - ERL_NIF_TERM eAddr, - struct sockaddr_in6* sockAddrP, - unsigned int* addrLen) -{ - if (IS_ATOM(env, eAddr)) { - / * This is either 'any' or 'loopback' * / - const struct in6_addr* addr; - - if (COMPARE(esock_atom_loopback, eAddr) == 0) { - addr = &in6addr_loopback; - } else if (COMPARE(esock_atom_any, eAddr) == 0) { - addr = &in6addr_any; - } else { - return ESOCK_STR_EINVAL; - } - - sockAddrP->sin6_addr = *addr; - *addrLen = sizeof(struct sockaddr_in6); - - } else { - / * This is a 8-tuple * / - - const ERL_NIF_TERM* addrt; - int addrtSz; - int a, v; - char addr[16]; - - if (!GET_TUPLE(env, eAddr, &addrtSz, &addrt)) - return ESOCK_STR_EINVAL; - - if (addrtSz != 8) - return ESOCK_STR_EINVAL; - - for (a = 0; a < 8; a++) { - if (!GET_INT(env, addrt[a], &v)) - return ESOCK_STR_EINVAL; - addr[a*2 ] = ((v >> 8) & 0xFF); - addr[a*2+1] = (v & 0xFF); - } - - sys_memcpy(&sockAddrP->sin6_addr, &addr, sizeof(addr)); - *addrLen = sizeof(struct sockaddr_in6); - - } - - return NULL; -} -#endif -*/ - - - #if defined(HAVE_SYS_UN_H) || defined(SO_BINDTODEVICE) /* strnlen doesn't exist everywhere */ /* diff --git a/erts/emulator/nifs/common/socket_util.c b/erts/emulator/nifs/common/socket_util.c index 5014688852..512c1d38e0 100644 --- a/erts/emulator/nifs/common/socket_util.c +++ b/erts/emulator/nifs/common/socket_util.c @@ -237,9 +237,13 @@ char* esock_decode_sockaddr_in4(ErlNifEnv* env, /* Decode address */ UDBG( ("SUTIL", "esock_decode_sockaddr_in4 -> try decode (ip) address\r\n") ); - if ((xres = esock_decode_ip4_address(env, eaddr, sockAddrP, addrLen)) != NULL) + if ((xres = esock_decode_ip4_address(env, + eaddr, + &sockAddrP->sin_addr)) != NULL) return xres; + *addrLen = sizeof(struct sockaddr_in); + UDBG( ("SUTIL", "esock_decode_sockaddr_in4 -> done\r\n") ); return NULL; @@ -330,6 +334,8 @@ char* esock_decode_sockaddr_in6(ErlNifEnv* env, unsigned int flowInfo, scopeId; char* xres; + UDBG( ("SUTIL", "esock_decode_sockaddr_in6 -> entry\r\n") ); + /* Basic init */ sys_memzero((char*) sockAddrP, sizeof(struct sockaddr_in6)); #ifndef NO_SA_LEN @@ -346,6 +352,8 @@ char* esock_decode_sockaddr_in6(ErlNifEnv* env, if (!GET_INT(env, eport, &port)) return ESOCK_STR_EINVAL; + UDBG( ("SUTIL", "esock_decode_sockaddr_in6 -> port: %d\r\n", port) ); + sockAddrP->sin6_port = htons(port); /* *** Extract (e) flowinfo from map *** */ @@ -356,6 +364,8 @@ char* esock_decode_sockaddr_in6(ErlNifEnv* env, if (!GET_UINT(env, eflowInfo, &flowInfo)) return ESOCK_STR_EINVAL; + UDBG( ("SUTIL", "esock_decode_sockaddr_in6 -> flowinfo: %d\r\n", flowInfo) ); + sockAddrP->sin6_flowinfo = flowInfo; /* *** Extract (e) scope_id from map *** */ @@ -366,6 +376,8 @@ char* esock_decode_sockaddr_in6(ErlNifEnv* env, if (!GET_UINT(env, escopeId, &scopeId)) return ESOCK_STR_EINVAL; + UDBG( ("SUTIL", "esock_decode_sockaddr_in6 -> scopeId: %d\r\n", scopeId) ); + sockAddrP->sin6_scope_id = scopeId; /* *** Extract (e) address from map *** */ @@ -373,9 +385,15 @@ char* esock_decode_sockaddr_in6(ErlNifEnv* env, return ESOCK_STR_EINVAL; /* Decode address */ - if ((xres = esock_decode_ip6_address(env, eaddr, sockAddrP, addrLen)) != NULL) + if ((xres = esock_decode_ip6_address(env, + eaddr, + &sockAddrP->sin6_addr)) != NULL) return xres; + *addrLen = sizeof(struct sockaddr_in6); + + UDBG( ("SUTIL", "esock_decode_sockaddr_in6 -> done\r\n") ); + return NULL; } #endif @@ -570,22 +588,22 @@ char* esock_encode_sockaddr_un(ErlNifEnv* env, * + An ip4_address() (4 tuple) * * Note that this *only* decodes the "address" part of a - * (IPv4) socket address. There are several other things (port). + * (IPv4) socket address. */ extern -char* esock_decode_ip4_address(ErlNifEnv* env, - ERL_NIF_TERM eAddr, - struct sockaddr_in* sockAddrP, - unsigned int* addrLen) +char* esock_decode_ip4_address(ErlNifEnv* env, + ERL_NIF_TERM eAddr, + struct in_addr* inAddrP) { + struct in_addr addr; + UDBG( ("SUTIL", "esock_decode_ip4_address -> entry with" "\r\n eAddr: %T" "\r\n", eAddr) ); if (IS_ATOM(env, eAddr)) { /* This is either 'any' or 'loopback' */ - struct in_addr addr; if (COMPARE(esock_atom_loopback, eAddr) == 0) { UDBG( ("SUTIL", "esock_decode_ip4_address -> address: lookback\r\n") ); @@ -598,8 +616,7 @@ char* esock_decode_ip4_address(ErlNifEnv* env, return ESOCK_STR_EINVAL; } - sockAddrP->sin_addr.s_addr = addr.s_addr; - *addrLen = sizeof(struct sockaddr_in); + inAddrP->s_addr = addr.s_addr; } else { /* This is a 4-tuple */ @@ -621,8 +638,7 @@ char* esock_decode_ip4_address(ErlNifEnv* env, addr[a] = v; } - sys_memcpy(&sockAddrP->sin_addr, &addr, sizeof(addr)); - *addrLen = sizeof(struct sockaddr_in); + sys_memcpy(inAddrP, &addr, sizeof(addr)); } @@ -682,11 +698,14 @@ char* esock_encode_ip4_address(ErlNifEnv* env, #if defined(HAVE_IN6) && defined(AF_INET6) extern -char* esock_decode_ip6_address(ErlNifEnv* env, - ERL_NIF_TERM eAddr, - struct sockaddr_in6* sockAddrP, - unsigned int* addrLen) +char* esock_decode_ip6_address(ErlNifEnv* env, + ERL_NIF_TERM eAddr, + struct in6_addr* inAddrP) { + UDBG( ("SUTIL", "esock_decode_ip6_address -> entry with" + "\r\n eAddr: %T" + "\r\n", eAddr) ); + if (IS_ATOM(env, eAddr)) { /* This is either 'any' or 'loopback' */ const struct in6_addr* addr; @@ -698,34 +717,34 @@ char* esock_decode_ip6_address(ErlNifEnv* env, } else { return ESOCK_STR_EINVAL; } - - sockAddrP->sin6_addr = *addr; - *addrLen = sizeof(struct sockaddr_in6); - + + *inAddrP = *addr; + } else { /* This is a 8-tuple */ - + const ERL_NIF_TERM* addrt; int addrtSz; - int a, v; - char addr[16]; + int ai, v; + unsigned char addr[16]; + unsigned char* a = addr; + unsigned int addrLen = sizeof(addr) / sizeof(unsigned char); if (!GET_TUPLE(env, eAddr, &addrtSz, &addrt)) return ESOCK_STR_EINVAL; if (addrtSz != 8) return ESOCK_STR_EINVAL; - - for (a = 0; a < 8; a++) { - if (!GET_INT(env, addrt[a], &v)) + + for (ai = 0; ai < 8; ai++) { + if (!GET_INT(env, addrt[ai], &v)) return ESOCK_STR_EINVAL; - addr[a*2 ] = ((v >> 8) & 0xFF); - addr[a*2+1] = (v & 0xFF); + put_int16(v, a); + a += 2; } - - sys_memcpy(&sockAddrP->sin6_addr, &addr, sizeof(addr)); - *addrLen = sizeof(struct sockaddr_in6); + sys_memcpy(inAddrP, &addr, addrLen); + } return NULL; @@ -754,7 +773,7 @@ char* esock_encode_ip6_address(ErlNifEnv* env, unsigned int i; ERL_NIF_TERM at[8]; unsigned int atLen = sizeof(at) / sizeof(ERL_NIF_TERM); - unsigned char* a = (unsigned char*) &addrP; + unsigned char* a = (unsigned char*) addrP; /* The address */ for (i = 0; i < atLen; i++) { diff --git a/erts/emulator/nifs/common/socket_util.h b/erts/emulator/nifs/common/socket_util.h index cd8cc7e1fb..f1c122e281 100644 --- a/erts/emulator/nifs/common/socket_util.h +++ b/erts/emulator/nifs/common/socket_util.h @@ -32,10 +32,6 @@ #define ESOCK_ABORT(E) esock_abort(E, __func__, __FILE__, __LINE__) #define ESOCK_ASSERT(e) ((void) ((e) ? 1 : (ESOCK_ABORT(#e), 0))) -/* Two byte integer decoding */ -#define get_int16(s) ((((unsigned char*) (s))[0] << 8) | \ - (((unsigned char*) (s))[1])) - extern char* esock_decode_sockaddr(ErlNifEnv* env, ERL_NIF_TERM eSockAddr, @@ -85,10 +81,9 @@ char* esock_encode_sockaddr_un(ErlNifEnv* env, #endif extern -char* esock_decode_ip4_address(ErlNifEnv* env, - ERL_NIF_TERM eAddr, - struct sockaddr_in* sockAddrP, - unsigned int* addrLen); +char* esock_decode_ip4_address(ErlNifEnv* env, + ERL_NIF_TERM eAddr, + struct in_addr* inAddrP); extern char* esock_encode_ip4_address(ErlNifEnv* env, struct in_addr* addrP, @@ -96,10 +91,9 @@ char* esock_encode_ip4_address(ErlNifEnv* env, #if defined(HAVE_IN6) && defined(AF_INET6) extern -char* esock_decode_ip6_address(ErlNifEnv* env, - ERL_NIF_TERM eAddr, - struct sockaddr_in6* sockAddrP, - unsigned int* addrLen); +char* esock_decode_ip6_address(ErlNifEnv* env, + ERL_NIF_TERM eAddr, + struct in6_addr* inAddrP); extern char* esock_encode_ip6_address(ErlNifEnv* env, struct in6_addr* addrP, diff --git a/erts/preloaded/ebin/socket.beam b/erts/preloaded/ebin/socket.beam index 4ffa22f70d..01db75c170 100644 Binary files a/erts/preloaded/ebin/socket.beam and b/erts/preloaded/ebin/socket.beam differ diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl index f243559ceb..997a4ac225 100644 --- a/erts/preloaded/src/socket.erl +++ b/erts/preloaded/src/socket.erl @@ -510,7 +510,7 @@ %% -define(SOCKET_OPT_IP_MTU, 11). %% -define(SOCKET_OPT_IP_MTU_DISCOVER, 12). %% -define(SOCKET_OPT_IP_MULTICAST_ALL, 13). -%% -define(SOCKET_OPT_IP_MULTICAST_IF, 14). +-define(SOCKET_OPT_IP_MULTICAST_IF, 14). -define(SOCKET_OPT_IP_MULTICAST_LOOP, 15). -define(SOCKET_OPT_IP_MULTICAST_TTL, 16). %% -define(SOCKET_OPT_IP_NODEFRAG, 17). @@ -1955,6 +1955,9 @@ enc_setopt_value(socket, sndbuf, V, _D, _T, _P) when is_integer(V) -> enc_setopt_value(socket = L, Opt, V, _D, _T, _P) -> not_supported({L, Opt, V}); +enc_setopt_value(ip, multicast_if, V, _D, _T, _P) + when (V =:= any) orelse (is_tuple(V) andalso (size(V) =:= 4)) -> + V; enc_setopt_value(ip, multicast_loop, V, _D, _T, _P) when is_boolean(V) -> V; @@ -2251,8 +2254,8 @@ enc_sockopt_key(ip = L, mtu_discover = Opt, _Dir, _D, _T, _P) -> not_supported({L, Opt}); enc_sockopt_key(ip = L, multicast_all = Opt, _Dir, _D, _T, _P) -> not_supported({L, Opt}); -enc_sockopt_key(ip = L, multicast_if = Opt, _Dir, _D, _T, _P) -> - not_supported({L, Opt}); +enc_sockopt_key(ip = _L, multicast_if = _Opt, _Dir, _D, _T, _P) -> + ?SOCKET_OPT_IP_MULTICAST_IF; enc_sockopt_key(ip = _L, multicast_loop = _Opt, _Dir, _D, _T, _P) -> ?SOCKET_OPT_IP_MULTICAST_LOOP; enc_sockopt_key(ip = _L, multicast_ttl = _Opt, _Dir, _D, _T, _P) -> -- cgit v1.2.3