Skip to content

Commit

Permalink
unix,win: add uv_if_{indextoname,indextoiid}
Browse files Browse the repository at this point in the history
uv_if_indextoname() is used to convert an IPv6 scope_id
to an interface identifier string such as %eth0 or %lo.

uv_if_indextoiid() returns an IPv6 interface identifier.
On Unix it calls uv_if_indextoname(). On Windows it uses
snprintf() to return the numeric interface identifier as
a string.

Refs: nodejs/node#14500
PR-URL: #1445
Reviewed-By: Santiago Gimeno <santiago.gimeno@gmail.com>
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
  • Loading branch information
pekkanikander authored and cjihrig committed Nov 6, 2017
1 parent fd02ab6 commit 695afe8
Show file tree
Hide file tree
Showing 6 changed files with 191 additions and 7 deletions.
54 changes: 54 additions & 0 deletions docs/src/misc.rst
Expand Up @@ -283,6 +283,60 @@ API
and :man:`inet_pton(3)`. On success they return 0. In case of error
the target `dst` pointer is unmodified.
.. c:macro:: UV_IF_NAMESIZE
Maximum IPv6 interface identifier name length. Defined as
`IFNAMSIZ` on Unix and `IF_NAMESIZE` on Linux and Windows.
.. versionadded:: 1.16.0
.. c:function:: int uv_if_indextoname(unsigned int ifindex, char* buffer, size_t* size)
IPv6-capable implementation of :man:`if_indextoname(3)`. When called,
`*size` indicates the length of the `buffer`, which is used to store the
result.
On success, zero is returned, `buffer` contains the interface name, and
`*size` represents the string length of the `buffer`, excluding the NUL
terminator byte from `*size`. On error, a negative result is
returned. If `buffer` is not large enough to hold the result,
`UV_ENOBUFS` is returned, and `*size` represents the necessary size in
bytes, including the NUL terminator byte into the `*size`.
On Unix, the returned interface name can be used directly as an
interface identifier in scoped IPv6 addresses, e.g.
`fe80::abc:def1:2345%en0`.
On Windows, the returned interface cannot be used as an interface
identifier, as Windows uses numerical interface identifiers, e.g.
`fe80::abc:def1:2345%5`.
To get an interface identifier in a cross-platform compatible way,
use `uv_if_indextoiid()`.
Example:
::
char ifname[UV_IF_NAMESIZE];
size_t size = sizeof(ifname);
uv_if_indextoname(sin6->sin6_scope_id, ifname, &size);
.. versionadded:: 1.16.0

.. c:function:: int uv_if_indextoiid(unsigned int ifindex, char* buffer, size_t* size)
Retrieves a network interface identifier suitable for use in an IPv6 scoped
address. On Windows, returns the numeric `ifindex` as a string. On all other
platforms, `uv_if_indextoname()` is called. The result is written to
`buffer`, with `*size` indicating the length of `buffer`. If `buffer` is not
large enough to hold the result, then `UV_ENOBUFS` is returned, and `*size`
represents the size, including the NUL byte, required to hold the
result.
See `uv_if_indextoname` for further details.
.. versionadded:: 1.16.0
.. c:function:: int uv_exepath(char* buffer, size_t* size)
Gets the executable path.
Expand Down
1 change: 1 addition & 0 deletions include/uv-unix.h
Expand Up @@ -32,6 +32,7 @@
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <net/if.h>

#include <termios.h>
#include <pwd.h>
Expand Down
15 changes: 15 additions & 0 deletions include/uv.h
Expand Up @@ -1408,6 +1408,21 @@ UV_EXTERN int uv_ip6_name(const struct sockaddr_in6* src, char* dst, size_t size
UV_EXTERN int uv_inet_ntop(int af, const void* src, char* dst, size_t size);
UV_EXTERN int uv_inet_pton(int af, const char* src, void* dst);

#if defined(IF_NAMESIZE)
# define UV_IF_NAMESIZE (IF_NAMESIZE + 1)
#elif defined(IFNAMSIZ)
# define UV_IF_NAMESIZE (IFNAMSIZ + 1)
#else
# define UV_IF_NAMESIZE (16 + 1)
#endif

UV_EXTERN int uv_if_indextoname(unsigned int ifindex,
char* buffer,
size_t* size);
UV_EXTERN int uv_if_indextoiid(unsigned int ifindex,
char* buffer,
size_t* size);

UV_EXTERN int uv_exepath(char* buffer, size_t* size);

UV_EXTERN int uv_cwd(char* buffer, size_t* size);
Expand Down
29 changes: 29 additions & 0 deletions src/unix/getaddrinfo.c
Expand Up @@ -200,3 +200,32 @@ void uv_freeaddrinfo(struct addrinfo* ai) {
if (ai)
freeaddrinfo(ai);
}


int uv_if_indextoname(unsigned int ifindex, char* buffer, size_t* size) {
char ifname_buf[UV_IF_NAMESIZE];
size_t len;

if (buffer == NULL || size == NULL || *size == 0)
return UV_EINVAL;

if (if_indextoname(ifindex, ifname_buf) == NULL)
return -errno;

len = strnlen(ifname_buf, sizeof(ifname_buf));

if (*size <= len) {
*size = len + 1;
return UV_ENOBUFS;
}

memcpy(buffer, ifname_buf, len);
buffer[len] = '\0';
*size = len;

return 0;
}

int uv_if_indextoiid(unsigned int ifindex, char* buffer, size_t* size) {
return uv_if_indextoname(ifindex, buffer, size);
}
68 changes: 68 additions & 0 deletions src/win/getaddrinfo.c
Expand Up @@ -28,6 +28,8 @@
/* EAI_* constants. */
#include <winsock2.h>

/* Needed for ConvertInterfaceIndexToLuid and ConvertInterfaceLuidToNameA */
#include <iphlpapi.h>

int uv__getaddrinfo_translate_error(int sys_err) {
switch (sys_err) {
Expand Down Expand Up @@ -380,3 +382,69 @@ int uv_getaddrinfo(uv_loop_t* loop,
}
return uv_translate_sys_error(err);
}

int uv_if_indextoname(unsigned int ifindex, char* buffer, size_t* size) {
NET_LUID luid;
wchar_t wname[NDIS_IF_MAX_STRING_SIZE + 1]; /* Add one for the NUL. */
DWORD bufsize;
int r;

if (buffer == NULL || size == NULL || *size == 0)
return UV_EINVAL;

r = ConvertInterfaceIndexToLuid(ifindex, &luid);

if (r != 0)
return uv_translate_sys_error(r);

r = ConvertInterfaceLuidToNameW(&luid, wname, ARRAY_SIZE(wname));

if (r != 0)
return uv_translate_sys_error(r);

/* Check how much space we need */
bufsize = WideCharToMultiByte(CP_UTF8, 0, wname, -1, NULL, 0, NULL, NULL);

if (bufsize == 0) {
return uv_translate_sys_error(GetLastError());
} else if (bufsize > *size) {
*size = bufsize;
return UV_ENOBUFS;
}

/* Convert to UTF-8 */
bufsize = WideCharToMultiByte(CP_UTF8,
0,
wname,
-1,
buffer,
*size,
NULL,
NULL);

if (bufsize == 0)
return uv_translate_sys_error(GetLastError());

*size = bufsize - 1;
return 0;
}

int uv_if_indextoiid(unsigned int ifindex, char* buffer, size_t* size) {
int r;

if (buffer == NULL || size == NULL || *size == 0)
return UV_EINVAL;

r = snprintf(buffer, *size, "%d", ifindex);

if (r < 0)
return uv_translate_sys_error(r);

if (r >= (int) *size) {
*size = r + 1;
return UV_ENOBUFS;
}

*size = r;
return 0;
}
31 changes: 24 additions & 7 deletions test/test-ip6-addr.c
Expand Up @@ -44,8 +44,12 @@ TEST_IMPL(ip6_addr_link_local) {
const char* device_name;
/* 40 bytes address, 16 bytes device name, plus reserve. */
char scoped_addr[128];
size_t scoped_addr_len;
char interface_id[UV_IF_NAMESIZE];
size_t interface_id_len;
int count;
int ix;
int r;

ASSERT(0 == uv_interface_addresses(&addresses, &count));

Expand All @@ -67,19 +71,29 @@ TEST_IMPL(ip6_addr_link_local) {
iface_index = address->address.address6.sin6_scope_id;
device_name = address->name;

scoped_addr_len = sizeof(scoped_addr);
ASSERT(0 == uv_if_indextoname(iface_index, scoped_addr, &scoped_addr_len));
#ifndef _WIN32
/* This assert fails on Windows, as Windows semantics are different. */
ASSERT(0 == strcmp(device_name, scoped_addr));
#endif

interface_id_len = sizeof(interface_id);
r = uv_if_indextoiid(iface_index, interface_id, &interface_id_len);
ASSERT(0 == r);
#ifdef _WIN32
snprintf(scoped_addr,
sizeof(scoped_addr),
"%s%%%d",
string_address,
iface_index);
/* On Windows, the interface identifier is the numeric string of the index. */
ASSERT(strtol(interface_id, NULL, 10) == iface_index);
#else
/* On Unix/Linux, the interface identifier is the interface device name. */
ASSERT(0 == strcmp(device_name, interface_id));
#endif

snprintf(scoped_addr,
sizeof(scoped_addr),
"%s%%%s",
string_address,
device_name);
#endif
interface_id);

fprintf(stderr, "Testing link-local address %s "
"(iface_index: 0x%02x, device_name: %s)\n",
Expand All @@ -96,6 +110,9 @@ TEST_IMPL(ip6_addr_link_local) {

uv_free_interface_addresses(addresses, count);

scoped_addr_len = sizeof(scoped_addr);
ASSERT(0 != uv_if_indextoname((unsigned int)-1, scoped_addr, &scoped_addr_len));

MAKE_VALGRIND_HAPPY();
return 0;
}
Expand Down

0 comments on commit 695afe8

Please sign in to comment.