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
|
and :man:`inet_pton(3)`. On success they return 0. In case of error
|
||||||
the target `dst` pointer is unmodified.
|
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)
|
.. c:function:: int uv_exepath(char* buffer, size_t* size)
|
||||||
|
|
||||||
Gets the executable path.
|
Gets the executable path.
|
||||||
|
|||||||
@ -32,6 +32,7 @@
|
|||||||
#include <netinet/tcp.h>
|
#include <netinet/tcp.h>
|
||||||
#include <arpa/inet.h>
|
#include <arpa/inet.h>
|
||||||
#include <netdb.h>
|
#include <netdb.h>
|
||||||
|
#include <net/if.h>
|
||||||
|
|
||||||
#include <termios.h>
|
#include <termios.h>
|
||||||
#include <pwd.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_ntop(int af, const void* src, char* dst, size_t size);
|
||||||
UV_EXTERN int uv_inet_pton(int af, const char* src, void* dst);
|
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_exepath(char* buffer, size_t* size);
|
||||||
|
|
||||||
UV_EXTERN int uv_cwd(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)
|
if (ai)
|
||||||
freeaddrinfo(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. */
|
/* EAI_* constants. */
|
||||||
#include <winsock2.h>
|
#include <winsock2.h>
|
||||||
|
|
||||||
|
/* Needed for ConvertInterfaceIndexToLuid and ConvertInterfaceLuidToNameA */
|
||||||
|
#include <iphlpapi.h>
|
||||||
|
|
||||||
int uv__getaddrinfo_translate_error(int sys_err) {
|
int uv__getaddrinfo_translate_error(int sys_err) {
|
||||||
switch (sys_err) {
|
switch (sys_err) {
|
||||||
@ -380,3 +382,69 @@ error:
|
|||||||
}
|
}
|
||||||
return uv_translate_sys_error(err);
|
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;
|
const char* device_name;
|
||||||
/* 40 bytes address, 16 bytes device name, plus reserve. */
|
/* 40 bytes address, 16 bytes device name, plus reserve. */
|
||||||
char scoped_addr[128];
|
char scoped_addr[128];
|
||||||
|
size_t scoped_addr_len;
|
||||||
|
char interface_id[UV_IF_NAMESIZE];
|
||||||
|
size_t interface_id_len;
|
||||||
int count;
|
int count;
|
||||||
int ix;
|
int ix;
|
||||||
|
int r;
|
||||||
|
|
||||||
ASSERT(0 == uv_interface_addresses(&addresses, &count));
|
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;
|
iface_index = address->address.address6.sin6_scope_id;
|
||||||
device_name = address->name;
|
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
|
#ifdef _WIN32
|
||||||
snprintf(scoped_addr,
|
/* On Windows, the interface identifier is the numeric string of the index. */
|
||||||
sizeof(scoped_addr),
|
ASSERT(strtol(interface_id, NULL, 10) == iface_index);
|
||||||
"%s%%%d",
|
|
||||||
string_address,
|
|
||||||
iface_index);
|
|
||||||
#else
|
#else
|
||||||
|
/* On Unix/Linux, the interface identifier is the interface device name. */
|
||||||
|
ASSERT(0 == strcmp(device_name, interface_id));
|
||||||
|
#endif
|
||||||
|
|
||||||
snprintf(scoped_addr,
|
snprintf(scoped_addr,
|
||||||
sizeof(scoped_addr),
|
sizeof(scoped_addr),
|
||||||
"%s%%%s",
|
"%s%%%s",
|
||||||
string_address,
|
string_address,
|
||||||
device_name);
|
interface_id);
|
||||||
#endif
|
|
||||||
|
|
||||||
fprintf(stderr, "Testing link-local address %s "
|
fprintf(stderr, "Testing link-local address %s "
|
||||||
"(iface_index: 0x%02x, device_name: %s)\n",
|
"(iface_index: 0x%02x, device_name: %s)\n",
|
||||||
@ -96,6 +110,9 @@ TEST_IMPL(ip6_addr_link_local) {
|
|||||||
|
|
||||||
uv_free_interface_addresses(addresses, count);
|
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();
|
MAKE_VALGRIND_HAPPY();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user