diff --git a/include/uv.h b/include/uv.h index a6688131..3b61e28f 100644 --- a/include/uv.h +++ b/include/uv.h @@ -1479,6 +1479,10 @@ struct uv_interface_address_s { struct sockaddr_in address4; struct sockaddr_in6 address6; } address; + union { + struct sockaddr_in netmask4; + struct sockaddr_in6 netmask6; + } netmask; }; UV_EXTERN char** uv_setup_args(int argc, char** argv); diff --git a/src/unix/aix.c b/src/unix/aix.c index 5ea33bbc..e1f247a7 100644 --- a/src/unix/aix.c +++ b/src/unix/aix.c @@ -369,6 +369,8 @@ uv_err_t uv_interface_addresses(uv_interface_address_t** addresses, address->address.address4 = *((struct sockaddr_in *)&p->ifr_addr); } + /* TODO: Retrieve netmask using SIOCGIFNETMASK ioctl */ + address->is_internal = flg.ifr_flags & IFF_LOOPBACK ? 1 : 0; address++; diff --git a/src/unix/darwin.c b/src/unix/darwin.c index 85a1d9ad..e641b500 100644 --- a/src/unix/darwin.c +++ b/src/unix/darwin.c @@ -408,6 +408,12 @@ uv_err_t uv_interface_addresses(uv_interface_address_t** addresses, address->address.address4 = *((struct sockaddr_in *)ent->ifa_addr); } + if (ent->ifa_netmask->sa_family == AF_INET6) { + address->netmask.netmask6 = *((struct sockaddr_in6 *)ent->ifa_netmask); + } else { + address->netmask.netmask4 = *((struct sockaddr_in *)ent->ifa_netmask); + } + address->is_internal = ent->ifa_flags & IFF_LOOPBACK ? 1 : 0; address++; diff --git a/src/unix/linux-core.c b/src/unix/linux-core.c index 23a59772..13ed0c5e 100644 --- a/src/unix/linux-core.c +++ b/src/unix/linux-core.c @@ -693,6 +693,12 @@ uv_err_t uv_interface_addresses(uv_interface_address_t** addresses, address->address.address4 = *((struct sockaddr_in *)ent->ifa_addr); } + if (ent->ifa_netmask->sa_family == AF_INET6) { + address->netmask.netmask6 = *((struct sockaddr_in6 *)ent->ifa_netmask); + } else { + address->netmask.netmask4 = *((struct sockaddr_in *)ent->ifa_netmask); + } + address->is_internal = ent->ifa_flags & IFF_LOOPBACK ? 1 : 0; address++; diff --git a/src/unix/netbsd.c b/src/unix/netbsd.c index 0fbcf108..9d3b2146 100644 --- a/src/unix/netbsd.c +++ b/src/unix/netbsd.c @@ -331,6 +331,12 @@ uv_err_t uv_interface_addresses(uv_interface_address_t** addresses, int* count) address->address.address4 = *((struct sockaddr_in *)ent->ifa_addr); } + if (ent->ifa_netmask->sa_family == AF_INET6) { + address->netmask.netmask6 = *((struct sockaddr_in6 *)ent->ifa_netmask); + } else { + address->netmask.netmask4 = *((struct sockaddr_in *)ent->ifa_netmask); + } + address->is_internal = !!(ent->ifa_flags & IFF_LOOPBACK) ? 1 : 0; address++; diff --git a/src/unix/sunos.c b/src/unix/sunos.c index ff5044cf..1bec39c0 100644 --- a/src/unix/sunos.c +++ b/src/unix/sunos.c @@ -622,6 +622,12 @@ uv_err_t uv_interface_addresses(uv_interface_address_t** addresses, address->address.address4 = *((struct sockaddr_in *)ent->ifa_addr); } + if (ent->ifa_netmask->sa_family == AF_INET6) { + address->netmask.netmask6 = *((struct sockaddr_in6 *)ent->ifa_netmask); + } else { + address->netmask.netmask4 = *((struct sockaddr_in *)ent->ifa_netmask); + } + address->is_internal = ent->ifa_flags & IFF_PRIVATE || ent->ifa_flags & IFF_LOOPBACK ? 1 : 0; diff --git a/src/win/util.c b/src/win/util.c index 96b1abe5..be076814 100644 --- a/src/win/util.c +++ b/src/win/util.c @@ -31,6 +31,7 @@ #include "uv.h" #include "internal.h" +#include #include #include #include @@ -765,7 +766,7 @@ uv_err_t uv_interface_addresses(uv_interface_address_t** addresses_ptr, /* ERROR_BUFFER_OVERFLOW, and the required buffer size will be stored in */ /* win_address_buf_size. */ r = GetAdaptersAddresses(AF_UNSPEC, - 0, + GAA_FLAG_INCLUDE_PREFIX, NULL, win_address_buf, &win_address_buf_size); @@ -882,6 +883,7 @@ uv_err_t uv_interface_addresses(uv_interface_address_t** addresses_ptr, win_address != NULL; win_address = win_address->Next) { IP_ADAPTER_UNICAST_ADDRESS_XP* unicast_address; + IP_ADAPTER_PREFIX* prefix; int name_size; size_t max_name_size; @@ -907,21 +909,55 @@ uv_err_t uv_interface_addresses(uv_interface_address_t** addresses_ptr, return uv__new_sys_error(GetLastError()); } + prefix = win_address->FirstPrefix; + /* Add an uv_interface_address_t element for every unicast address. */ for (unicast_address = (IP_ADAPTER_UNICAST_ADDRESS_XP*) win_address->FirstUnicastAddress; unicast_address != NULL; unicast_address = unicast_address->Next) { struct sockaddr* sa; + int prefixlen; uv_address->name = name_buf; + /* Walk the prefix list in sync with the address list and extract + the prefixlen for each address. On Vista and newer, we could + instead just use: unicast_address->OnLinkPrefixLength */ + if (prefix != NULL) { + prefixlen = prefix->PrefixLength; + prefix = prefix->Next; + } else { + prefixlen = 0; + } + sa = unicast_address->Address.lpSockaddr; - if (sa->sa_family == AF_INET6) + if (sa->sa_family == AF_INET6) { + int i; + uv_address->address.address6 = *((struct sockaddr_in6 *) sa); - else + + uv_address->netmask.netmask6.sin6_family = AF_INET6; + prefixlen = prefixlen > 0 ? prefixlen : 128; + for (i = 0; i < 16; ++i) { + int bits; + uint8_t byte_val; + + bits = prefixlen < 8 ? prefixlen : 8; + byte_val = ~(0xff >> bits); + prefixlen -= bits; + + uv_address->netmask.netmask6.sin6_addr.s6_addr[i] = byte_val; + } + } else { uv_address->address.address4 = *((struct sockaddr_in *) sa); + uv_address->netmask.netmask4.sin_family = AF_INET; + prefixlen = prefixlen > 0 ? prefixlen : 32; + uv_address->netmask.netmask4.sin_addr.s_addr = + htonl(0xffffffff << (32 - prefixlen)); + } + uv_address->is_internal = (win_address->IfType == IF_TYPE_SOFTWARE_LOOPBACK); diff --git a/test/test-platform-output.c b/test/test-platform-output.c index 008d14fb..80737a78 100644 --- a/test/test-platform-output.c +++ b/test/test-platform-output.c @@ -80,6 +80,14 @@ TEST_IMPL(platform_output) { } printf(" address: %s\n", buffer); + + if (interfaces[i].netmask.netmask4.sin_family == AF_INET) { + uv_ip4_name(&interfaces[i].netmask.netmask4, buffer, sizeof(buffer)); + } else if (interfaces[i].netmask.netmask4.sin_family == AF_INET6) { + uv_ip6_name(&interfaces[i].netmask.netmask6, buffer, sizeof(buffer)); + } + + printf(" netmask: %s\n", buffer); } uv_free_interface_addresses(interfaces, count);