unix,win: add uv_if_{indextoname,indextoiid}
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: https://github.com/nodejs/node/pull/14500 PR-URL: https://github.com/libuv/libuv/pull/1445 Reviewed-By: Santiago Gimeno <santiago.gimeno@gmail.com> Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
This commit is contained in:
parent
fd02ab681b
commit
695afe8322
@ -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.
|
||||
|
||||
@ -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>
|
||||
|
||||
15
include/uv.h
15
include/uv.h
@ -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);
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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) {
|
||||
@ -380,3 +382,69 @@ error:
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
@ -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));
|
||||
|
||||
@ -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",
|
||||
@ -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;
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user