@lakutata/netif 0.0.0-stage → 3.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +23 -0
- package/README.md +299 -2
- package/README.zh-CN.md +229 -0
- package/binding.gyp +43 -0
- package/dist/cjs/exceptions/NetIfException.d.ts +17 -0
- package/dist/cjs/exceptions/NetIfException.js +24 -0
- package/dist/cjs/exceptions/NetIfNotEnforcedException.d.ts +12 -0
- package/dist/cjs/exceptions/NetIfNotEnforcedException.js +19 -0
- package/dist/cjs/exceptions/NetIfNotFoundException.d.ts +12 -0
- package/dist/cjs/exceptions/NetIfNotFoundException.js +19 -0
- package/dist/cjs/exceptions/NetIfNotSupportedException.d.ts +12 -0
- package/dist/cjs/exceptions/NetIfNotSupportedException.js +19 -0
- package/dist/cjs/exceptions/NetIfTimeoutException.d.ts +11 -0
- package/dist/cjs/exceptions/NetIfTimeoutException.js +18 -0
- package/dist/cjs/exports/Netif.d.ts +11 -0
- package/dist/cjs/exports/Netif.js +26 -0
- package/dist/cjs/lib/Addon.d.ts +5 -0
- package/dist/cjs/lib/Addon.js +9 -0
- package/dist/cjs/lib/Attachment.d.ts +152 -0
- package/dist/cjs/lib/Attachment.js +415 -0
- package/dist/cjs/lib/Enforcement.d.ts +22 -0
- package/dist/cjs/lib/Enforcement.js +45 -0
- package/dist/cjs/lib/Functions.d.ts +66 -0
- package/dist/cjs/lib/Functions.js +97 -0
- package/dist/cjs/lib/InterfaceListener.d.ts +92 -0
- package/dist/cjs/lib/InterfaceListener.js +99 -0
- package/dist/cjs/lib/InterfaceUDPSocket.d.ts +119 -0
- package/dist/cjs/lib/InterfaceUDPSocket.js +245 -0
- package/dist/cjs/lib/Internal.d.ts +10 -0
- package/dist/cjs/lib/Internal.js +9 -0
- package/dist/cjs/lib/Native.d.ts +54 -0
- package/dist/cjs/lib/Native.js +83 -0
- package/dist/cjs/lib/NetworkInterface.d.ts +313 -0
- package/dist/cjs/lib/NetworkInterface.js +391 -0
- package/dist/cjs/lib/Registry.d.ts +26 -0
- package/dist/cjs/lib/Registry.js +103 -0
- package/dist/cjs/lib/Snapshot.d.ts +17 -0
- package/dist/cjs/lib/Snapshot.js +84 -0
- package/dist/cjs/lib/Types.d.ts +63 -0
- package/dist/cjs/lib/Types.js +2 -0
- package/dist/cjs/package.json +1 -0
- package/dist/esm/exceptions/NetIfException.js +20 -0
- package/dist/esm/exceptions/NetIfNotEnforcedException.js +15 -0
- package/dist/esm/exceptions/NetIfNotFoundException.js +15 -0
- package/dist/esm/exceptions/NetIfNotSupportedException.js +15 -0
- package/dist/esm/exceptions/NetIfTimeoutException.js +14 -0
- package/dist/esm/exports/Netif.js +9 -0
- package/dist/esm/lib/Addon.js +10 -0
- package/dist/esm/lib/Attachment.js +410 -0
- package/dist/esm/lib/Enforcement.js +41 -0
- package/dist/esm/lib/Functions.js +92 -0
- package/dist/esm/lib/InterfaceListener.js +95 -0
- package/dist/esm/lib/InterfaceUDPSocket.js +241 -0
- package/dist/esm/lib/Internal.js +6 -0
- package/dist/esm/lib/Native.js +75 -0
- package/dist/esm/lib/NetworkInterface.js +386 -0
- package/dist/esm/lib/Registry.js +100 -0
- package/dist/esm/lib/Snapshot.js +79 -0
- package/dist/esm/lib/Types.js +1 -0
- package/dist/types/exceptions/NetIfException.d.ts +17 -0
- package/dist/types/exceptions/NetIfNotEnforcedException.d.ts +12 -0
- package/dist/types/exceptions/NetIfNotFoundException.d.ts +12 -0
- package/dist/types/exceptions/NetIfNotSupportedException.d.ts +12 -0
- package/dist/types/exceptions/NetIfTimeoutException.d.ts +11 -0
- package/dist/types/exports/Netif.d.ts +11 -0
- package/dist/types/lib/Addon.d.ts +5 -0
- package/dist/types/lib/Attachment.d.ts +152 -0
- package/dist/types/lib/Enforcement.d.ts +22 -0
- package/dist/types/lib/Functions.d.ts +66 -0
- package/dist/types/lib/InterfaceListener.d.ts +92 -0
- package/dist/types/lib/InterfaceUDPSocket.d.ts +119 -0
- package/dist/types/lib/Internal.d.ts +10 -0
- package/dist/types/lib/Native.d.ts +54 -0
- package/dist/types/lib/NetworkInterface.d.ts +313 -0
- package/dist/types/lib/Registry.d.ts +26 -0
- package/dist/types/lib/Snapshot.d.ts +17 -0
- package/dist/types/lib/Types.d.ts +63 -0
- package/native/Interfaces.cpp +34 -0
- package/native/Interfaces.h +47 -0
- package/native/Sockets.h +36 -0
- package/native/Watcher.h +27 -0
- package/native/addon.cpp +195 -0
- package/native/darwin/Interfaces.cpp +150 -0
- package/native/darwin/Sockets.cpp +44 -0
- package/native/darwin/Watcher.cpp +113 -0
- package/native/linux/Interfaces.cpp +133 -0
- package/native/linux/RouteNetlink.h +38 -0
- package/native/linux/Sockets.cpp +89 -0
- package/native/linux/Watcher.cpp +70 -0
- package/native/windows/Interfaces.cpp +78 -0
- package/native/windows/Sockets.cpp +56 -0
- package/native/windows/Watcher.cpp +53 -0
- package/package.json +81 -4
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
//The sockets of Linux bound to an interface (SO_BINDTODEVICE, by its name: SO_BINDTOIFINDEX needs Linux 5.0) and their
|
|
2
|
+
//multicast groups. Linux receives the packets of an address through any interface: there is no strong host model to
|
|
3
|
+
//rely on, the binding is what refuses the other interfaces.
|
|
4
|
+
#include "../Sockets.h"
|
|
5
|
+
|
|
6
|
+
#include <arpa/inet.h>
|
|
7
|
+
#include <net/if.h>
|
|
8
|
+
#include <netinet/in.h>
|
|
9
|
+
#include <sys/socket.h>
|
|
10
|
+
#include <unistd.h>
|
|
11
|
+
|
|
12
|
+
#include <cerrno>
|
|
13
|
+
#include <cstring>
|
|
14
|
+
#include <system_error>
|
|
15
|
+
|
|
16
|
+
namespace netif {
|
|
17
|
+
|
|
18
|
+
namespace {
|
|
19
|
+
|
|
20
|
+
[[noreturn]] void fail(int fd, const char *call)
|
|
21
|
+
{
|
|
22
|
+
int error = errno;
|
|
23
|
+
if (fd >= 0) close(fd);
|
|
24
|
+
throw std::system_error(error, std::generic_category(), call);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
BoundSocket bindToInterface(const std::string &name, int family, bool stream, uint16_t port, int backlog)
|
|
30
|
+
{
|
|
31
|
+
int domain = family == 6 ? AF_INET6 : AF_INET;
|
|
32
|
+
int fd = socket(domain, (stream ? SOCK_STREAM : SOCK_DGRAM) | SOCK_CLOEXEC, 0);
|
|
33
|
+
if (fd < 0) fail(-1, "socket");
|
|
34
|
+
int on = 1;
|
|
35
|
+
//As Node.js does for its TCP servers: a port in TIME_WAIT can be listened on again
|
|
36
|
+
if (stream && setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof on) != 0) fail(fd, "setsockopt(SO_REUSEADDR)");
|
|
37
|
+
if (family == 6 && setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof on) != 0) fail(fd, "setsockopt(IPV6_V6ONLY)");
|
|
38
|
+
if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, name.c_str(), static_cast<socklen_t>(name.size())) != 0) fail(fd, "setsockopt(SO_BINDTODEVICE)");
|
|
39
|
+
sockaddr_storage address{};
|
|
40
|
+
socklen_t length;
|
|
41
|
+
if (family == 6) {
|
|
42
|
+
auto *in6 = reinterpret_cast<sockaddr_in6 *>(&address);
|
|
43
|
+
in6->sin6_family = AF_INET6;
|
|
44
|
+
in6->sin6_addr = in6addr_any;
|
|
45
|
+
in6->sin6_port = htons(port);
|
|
46
|
+
length = sizeof(sockaddr_in6);
|
|
47
|
+
} else {
|
|
48
|
+
auto *in4 = reinterpret_cast<sockaddr_in *>(&address);
|
|
49
|
+
in4->sin_family = AF_INET;
|
|
50
|
+
in4->sin_addr.s_addr = htonl(INADDR_ANY);
|
|
51
|
+
in4->sin_port = htons(port);
|
|
52
|
+
length = sizeof(sockaddr_in);
|
|
53
|
+
}
|
|
54
|
+
if (bind(fd, reinterpret_cast<sockaddr *>(&address), length) != 0) fail(fd, "bind");
|
|
55
|
+
if (stream && listen(fd, backlog) != 0) fail(fd, "listen");
|
|
56
|
+
length = sizeof address;
|
|
57
|
+
if (getsockname(fd, reinterpret_cast<sockaddr *>(&address), &length) != 0) fail(fd, "getsockname");
|
|
58
|
+
BoundSocket bound;
|
|
59
|
+
bound.fd = fd;
|
|
60
|
+
bound.port = ntohs(family == 6 ? reinterpret_cast<sockaddr_in6 *>(&address)->sin6_port : reinterpret_cast<sockaddr_in *>(&address)->sin_port);
|
|
61
|
+
return bound;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
void membership(int fd, const std::string &group, uint32_t index, bool join)
|
|
65
|
+
{
|
|
66
|
+
group_req request{};
|
|
67
|
+
request.gr_interface = index;
|
|
68
|
+
int level;
|
|
69
|
+
if (inet_pton(AF_INET, group.c_str(), &reinterpret_cast<sockaddr_in *>(&request.gr_group)->sin_addr) == 1) {
|
|
70
|
+
reinterpret_cast<sockaddr_in *>(&request.gr_group)->sin_family = AF_INET;
|
|
71
|
+
level = IPPROTO_IP;
|
|
72
|
+
} else if (inet_pton(AF_INET6, group.c_str(), &reinterpret_cast<sockaddr_in6 *>(&request.gr_group)->sin6_addr) == 1) {
|
|
73
|
+
reinterpret_cast<sockaddr_in6 *>(&request.gr_group)->sin6_family = AF_INET6;
|
|
74
|
+
level = IPPROTO_IPV6;
|
|
75
|
+
} else {
|
|
76
|
+
throw std::system_error(EINVAL, std::generic_category(), "inet_pton");
|
|
77
|
+
}
|
|
78
|
+
if (setsockopt(fd, level, join ? MCAST_JOIN_GROUP : MCAST_LEAVE_GROUP, &request, sizeof request) != 0) {
|
|
79
|
+
throw std::system_error(errno, std::generic_category(), join ? "setsockopt(MCAST_JOIN_GROUP)" : "setsockopt(MCAST_LEAVE_GROUP)");
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
//Linux has no strong host model: the binding decides
|
|
84
|
+
std::optional<WeakReception> weakReception(uint32_t, int)
|
|
85
|
+
{
|
|
86
|
+
return WeakReception{std::string(), std::string("the weak host model of Linux")};
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
//The notifications of Linux: a netlink socket joined to the groups of the links and of the IPv4 and IPv6 addresses,
|
|
2
|
+
//read by a thread of the addon, which a pipe wakes to stop. An overrun of the socket (ENOBUFS) is a change too.
|
|
3
|
+
#include "../Watcher.h"
|
|
4
|
+
#include "RouteNetlink.h"
|
|
5
|
+
|
|
6
|
+
#include <fcntl.h>
|
|
7
|
+
#include <poll.h>
|
|
8
|
+
#include <sys/socket.h>
|
|
9
|
+
#include <unistd.h>
|
|
10
|
+
|
|
11
|
+
#include <cerrno>
|
|
12
|
+
#include <system_error>
|
|
13
|
+
#include <thread>
|
|
14
|
+
#include <vector>
|
|
15
|
+
|
|
16
|
+
namespace netif {
|
|
17
|
+
|
|
18
|
+
struct Watcher::State {
|
|
19
|
+
Netlink socket{RTMGRP_LINK | RTMGRP_IPV4_IFADDR | RTMGRP_IPV6_IFADDR};
|
|
20
|
+
int wake[2] = {-1, -1};
|
|
21
|
+
std::thread thread;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
Watcher::Watcher(std::function<void()> changed) : state(std::make_unique<State>())
|
|
25
|
+
{
|
|
26
|
+
if (pipe2(state->wake, O_CLOEXEC | O_NONBLOCK) != 0) throw std::system_error(errno, std::generic_category(), "pipe2");
|
|
27
|
+
State *watched = state.get();
|
|
28
|
+
state->thread = std::thread([watched, changed]() {
|
|
29
|
+
std::vector<char> buffer(65536);
|
|
30
|
+
pollfd descriptors[2] = {{watched->socket.fd(), POLLIN, 0}, {watched->wake[0], POLLIN, 0}};
|
|
31
|
+
for (;;) {
|
|
32
|
+
if (poll(descriptors, 2, -1) < 0) {
|
|
33
|
+
if (errno == EINTR) continue;
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
if (descriptors[1].revents) return;
|
|
37
|
+
if (!descriptors[0].revents) continue;
|
|
38
|
+
bool any = false;
|
|
39
|
+
for (;;) {
|
|
40
|
+
ssize_t received = recv(watched->socket.fd(), buffer.data(), buffer.size(), MSG_DONTWAIT);
|
|
41
|
+
if (received < 0) {
|
|
42
|
+
if (errno == EINTR) continue;
|
|
43
|
+
if (errno == ENOBUFS) any = true;
|
|
44
|
+
break;
|
|
45
|
+
}
|
|
46
|
+
int remaining = static_cast<int>(received);
|
|
47
|
+
for (auto *header = reinterpret_cast<nlmsghdr *>(buffer.data()); NLMSG_OK(header, remaining); header = NLMSG_NEXT(header, remaining)) {
|
|
48
|
+
uint16_t type = header->nlmsg_type;
|
|
49
|
+
if (type == RTM_NEWLINK || type == RTM_DELLINK || type == RTM_NEWADDR || type == RTM_DELADDR) any = true;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
if (any) changed();
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
Watcher::~Watcher()
|
|
58
|
+
{
|
|
59
|
+
if (state->wake[1] >= 0) {
|
|
60
|
+
char byte = 1;
|
|
61
|
+
while (write(state->wake[1], &byte, 1) < 0 && errno == EINTR) {
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
if (state->thread.joinable()) state->thread.join();
|
|
65
|
+
for (int descriptor : state->wake) {
|
|
66
|
+
if (descriptor >= 0) close(descriptor);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
//The interfaces of Windows, read by GetAdaptersAddresses(): the name of an interface is the name of its network
|
|
2
|
+
//connection (以太网, Ethernet 2…, in the language of the system). A disabled adapter is not listed: listed is enabled. The
|
|
3
|
+
//duplicate address detection of an address is its DadState.
|
|
4
|
+
#include "../Interfaces.h"
|
|
5
|
+
|
|
6
|
+
#include <winsock2.h>
|
|
7
|
+
#include <ws2tcpip.h>
|
|
8
|
+
#include <iphlpapi.h>
|
|
9
|
+
|
|
10
|
+
#include <system_error>
|
|
11
|
+
#include <utility>
|
|
12
|
+
#include <vector>
|
|
13
|
+
|
|
14
|
+
namespace netif {
|
|
15
|
+
|
|
16
|
+
namespace {
|
|
17
|
+
|
|
18
|
+
std::string utf8(const wchar_t *text)
|
|
19
|
+
{
|
|
20
|
+
if (!text || !*text) return std::string();
|
|
21
|
+
int length = WideCharToMultiByte(CP_UTF8, 0, text, -1, nullptr, 0, nullptr, nullptr);
|
|
22
|
+
if (length <= 1) return std::string();
|
|
23
|
+
std::string converted(static_cast<size_t>(length - 1), '\0');
|
|
24
|
+
WideCharToMultiByte(CP_UTF8, 0, text, -1, converted.data(), length, nullptr, nullptr);
|
|
25
|
+
return converted;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
std::vector<Interface> listInterfaces()
|
|
31
|
+
{
|
|
32
|
+
const ULONG flags = GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER;
|
|
33
|
+
std::vector<unsigned char> buffer(16384);
|
|
34
|
+
ULONG size = static_cast<ULONG>(buffer.size());
|
|
35
|
+
ULONG result = ERROR_BUFFER_OVERFLOW;
|
|
36
|
+
//The size asked can grow between two calls (an adapter added meanwhile)
|
|
37
|
+
for (int attempt = 0; attempt < 4 && result == ERROR_BUFFER_OVERFLOW; attempt++) {
|
|
38
|
+
buffer.resize(size);
|
|
39
|
+
result = GetAdaptersAddresses(AF_UNSPEC, flags, nullptr, reinterpret_cast<IP_ADAPTER_ADDRESSES *>(buffer.data()), &size);
|
|
40
|
+
}
|
|
41
|
+
std::vector<Interface> interfaces;
|
|
42
|
+
if (result == ERROR_NO_DATA) return interfaces;
|
|
43
|
+
if (result != NO_ERROR) throw std::system_error(static_cast<int>(result), std::system_category(), "GetAdaptersAddresses");
|
|
44
|
+
for (auto *adapter = reinterpret_cast<IP_ADAPTER_ADDRESSES *>(buffer.data()); adapter; adapter = adapter->Next) {
|
|
45
|
+
Interface entry;
|
|
46
|
+
entry.name = utf8(adapter->FriendlyName);
|
|
47
|
+
entry.index = adapter->IfIndex ? adapter->IfIndex : adapter->Ipv6IfIndex;
|
|
48
|
+
entry.mac = macText(adapter->PhysicalAddress, adapter->PhysicalAddressLength);
|
|
49
|
+
entry.up = true;
|
|
50
|
+
entry.running = adapter->OperStatus == IfOperStatusUp;
|
|
51
|
+
entry.loopback = adapter->IfType == IF_TYPE_SOFTWARE_LOOPBACK;
|
|
52
|
+
for (auto *unicast = adapter->FirstUnicastAddress; unicast; unicast = unicast->Next) {
|
|
53
|
+
const sockaddr *socketAddress = unicast->Address.lpSockaddr;
|
|
54
|
+
if (!socketAddress) continue;
|
|
55
|
+
Address address;
|
|
56
|
+
if (socketAddress->sa_family == AF_INET) {
|
|
57
|
+
address.family = 4;
|
|
58
|
+
address.address = addressText(AF_INET, &reinterpret_cast<const sockaddr_in *>(socketAddress)->sin_addr);
|
|
59
|
+
} else if (socketAddress->sa_family == AF_INET6) {
|
|
60
|
+
auto *in6 = reinterpret_cast<const sockaddr_in6 *>(socketAddress);
|
|
61
|
+
address.family = 6;
|
|
62
|
+
address.address = addressText(AF_INET6, &in6->sin6_addr);
|
|
63
|
+
//The scope the system gives (as os.networkInterfaces() does): the interface of a link-local address, the
|
|
64
|
+
//site of a site-local one (fec0::/10)
|
|
65
|
+
address.scopeId = in6->sin6_scope_id;
|
|
66
|
+
} else {
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
address.prefixLength = unicast->OnLinkPrefixLength;
|
|
70
|
+
address.tentative = unicast->DadState != IpDadStatePreferred && unicast->DadState != IpDadStateDeprecated;
|
|
71
|
+
entry.addresses.push_back(std::move(address));
|
|
72
|
+
}
|
|
73
|
+
interfaces.push_back(std::move(entry));
|
|
74
|
+
}
|
|
75
|
+
return interfaces;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
//Windows: no socket of an interface (IP_UNICAST_IF only chooses the interface the packets leave by); the strong host
|
|
2
|
+
//model of the system refuses the packets of an address that come through another interface. Whether a packet is taken
|
|
3
|
+
//depends on the settings of the interface it came through: every other interface of the family has to receive with the
|
|
4
|
+
//strong host model and not forward (whether forwarding suspends the model is not documented: counted as weak).
|
|
5
|
+
#include "../Sockets.h"
|
|
6
|
+
|
|
7
|
+
#include <winsock2.h>
|
|
8
|
+
#include <ws2tcpip.h>
|
|
9
|
+
#include <iphlpapi.h>
|
|
10
|
+
|
|
11
|
+
#include <system_error>
|
|
12
|
+
|
|
13
|
+
namespace netif {
|
|
14
|
+
|
|
15
|
+
namespace {
|
|
16
|
+
|
|
17
|
+
std::string aliasOf(const NET_LUID &luid)
|
|
18
|
+
{
|
|
19
|
+
wchar_t alias[NDIS_IF_MAX_STRING_SIZE + 1] = {0};
|
|
20
|
+
if (ConvertInterfaceLuidToAlias(&luid, alias, NDIS_IF_MAX_STRING_SIZE + 1) != NO_ERROR) return std::string();
|
|
21
|
+
int length = WideCharToMultiByte(CP_UTF8, 0, alias, -1, nullptr, 0, nullptr, nullptr);
|
|
22
|
+
if (length <= 1) return std::string();
|
|
23
|
+
std::string converted(static_cast<size_t>(length - 1), '\0');
|
|
24
|
+
WideCharToMultiByte(CP_UTF8, 0, alias, -1, converted.data(), length, nullptr, nullptr);
|
|
25
|
+
return converted;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
BoundSocket bindToInterface(const std::string &, int, bool, uint16_t, int)
|
|
31
|
+
{
|
|
32
|
+
throw std::system_error(ERROR_NOT_SUPPORTED, std::system_category(), "bind to an interface");
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
void membership(int, const std::string &, uint32_t, bool)
|
|
36
|
+
{
|
|
37
|
+
throw std::system_error(ERROR_NOT_SUPPORTED, std::system_category(), "membership");
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
std::optional<WeakReception> weakReception(uint32_t index, int family)
|
|
41
|
+
{
|
|
42
|
+
PMIB_IPINTERFACE_TABLE table = nullptr;
|
|
43
|
+
DWORD result = GetIpInterfaceTable(family == 6 ? AF_INET6 : AF_INET, &table);
|
|
44
|
+
if (result != NO_ERROR) throw std::system_error(static_cast<int>(result), std::system_category(), "GetIpInterfaceTable");
|
|
45
|
+
std::optional<WeakReception> found;
|
|
46
|
+
for (ULONG position = 0; position < table->NumEntries && !found; position++) {
|
|
47
|
+
const MIB_IPINTERFACE_ROW &row = table->Table[position];
|
|
48
|
+
if (row.InterfaceIndex == index) continue;
|
|
49
|
+
if (row.WeakHostReceive) found = WeakReception{aliasOf(row.InterfaceLuid), "WeakHostReceive"};
|
|
50
|
+
else if (row.ForwardingEnabled) found = WeakReception{aliasOf(row.InterfaceLuid), "Forwarding"};
|
|
51
|
+
}
|
|
52
|
+
FreeMibTable(table);
|
|
53
|
+
return found;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
//The notifications of Windows: the changes of the IP interfaces (their arrival, removal and settings: the link state,
|
|
2
|
+
//the weak host model, the forwarding) and of the unicast addresses, whose callbacks run in threads of the system.
|
|
3
|
+
//CancelMibChangeNotify2 waits for the callbacks running: called by the destructor only, never from a callback.
|
|
4
|
+
#include "../Watcher.h"
|
|
5
|
+
|
|
6
|
+
#include <winsock2.h>
|
|
7
|
+
#include <ws2tcpip.h>
|
|
8
|
+
#include <iphlpapi.h>
|
|
9
|
+
|
|
10
|
+
#include <system_error>
|
|
11
|
+
|
|
12
|
+
namespace netif {
|
|
13
|
+
|
|
14
|
+
struct Watcher::State {
|
|
15
|
+
std::function<void()> changed;
|
|
16
|
+
HANDLE interfaces = nullptr;
|
|
17
|
+
HANDLE addresses = nullptr;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
namespace {
|
|
21
|
+
|
|
22
|
+
VOID NETIOAPI_API_ onInterface(PVOID context, PMIB_IPINTERFACE_ROW, MIB_NOTIFICATION_TYPE)
|
|
23
|
+
{
|
|
24
|
+
(*static_cast<std::function<void()> *>(context))();
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
VOID NETIOAPI_API_ onAddress(PVOID context, PMIB_UNICASTIPADDRESS_ROW, MIB_NOTIFICATION_TYPE)
|
|
28
|
+
{
|
|
29
|
+
(*static_cast<std::function<void()> *>(context))();
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
Watcher::Watcher(std::function<void()> changed) : state(std::make_unique<State>())
|
|
35
|
+
{
|
|
36
|
+
state->changed = std::move(changed);
|
|
37
|
+
DWORD result = NotifyIpInterfaceChange(AF_UNSPEC, onInterface, &state->changed, FALSE, &state->interfaces);
|
|
38
|
+
if (result != NO_ERROR) throw std::system_error(static_cast<int>(result), std::system_category(), "NotifyIpInterfaceChange");
|
|
39
|
+
result = NotifyUnicastIpAddressChange(AF_UNSPEC, onAddress, &state->changed, FALSE, &state->addresses);
|
|
40
|
+
if (result != NO_ERROR) {
|
|
41
|
+
CancelMibChangeNotify2(state->interfaces);
|
|
42
|
+
state->interfaces = nullptr;
|
|
43
|
+
throw std::system_error(static_cast<int>(result), std::system_category(), "NotifyUnicastIpAddressChange");
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
Watcher::~Watcher()
|
|
48
|
+
{
|
|
49
|
+
if (state->addresses) CancelMibChangeNotify2(state->addresses);
|
|
50
|
+
if (state->interfaces) CancelMibChangeNotify2(state->interfaces);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,83 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lakutata/netif",
|
|
3
|
-
"version": "
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "3.1.5",
|
|
4
|
+
"description": "Listen on a network interface instead of an address: servers of Node.js (http, https, Express, Fastify) and UDP sockets follow an interface whatever its addresses, through their changes and the unplugging of the interface; the interfaces of the machine with their state, and events when they change. A native addon for Linux, macOS and Windows.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"lakutata",
|
|
7
|
+
"network-interface",
|
|
8
|
+
"netif",
|
|
9
|
+
"bind-to-device",
|
|
10
|
+
"so_bindtodevice",
|
|
11
|
+
"listen",
|
|
12
|
+
"interface",
|
|
13
|
+
"nic",
|
|
14
|
+
"ip-address",
|
|
15
|
+
"ipv6",
|
|
16
|
+
"udp",
|
|
17
|
+
"multicast",
|
|
18
|
+
"native",
|
|
19
|
+
"typescript"
|
|
20
|
+
],
|
|
21
|
+
"homepage": "https://lakutata.com/packages/netif",
|
|
22
|
+
"bugs": {
|
|
23
|
+
"url": "https://github.com/lakutata/lakutata/issues",
|
|
24
|
+
"email": "admin@lakutata.com"
|
|
25
|
+
},
|
|
26
|
+
"repository": {
|
|
27
|
+
"type": "git",
|
|
28
|
+
"url": "https://github.com/lakutata/lakutata.git",
|
|
29
|
+
"directory": "packages/netif"
|
|
30
|
+
},
|
|
31
|
+
"author": "alex.ma",
|
|
32
|
+
"license": "MIT",
|
|
33
|
+
"type": "module",
|
|
34
|
+
"main": "./dist/cjs/exports/Netif.js",
|
|
35
|
+
"types": "./dist/cjs/exports/Netif.d.ts",
|
|
36
|
+
"exports": {
|
|
37
|
+
".": {
|
|
38
|
+
"import": {
|
|
39
|
+
"types": "./dist/types/exports/Netif.d.ts",
|
|
40
|
+
"default": "./dist/esm/exports/Netif.js"
|
|
41
|
+
},
|
|
42
|
+
"require": {
|
|
43
|
+
"types": "./dist/cjs/exports/Netif.d.ts",
|
|
44
|
+
"default": "./dist/cjs/exports/Netif.js"
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
"./package.json": "./package.json"
|
|
48
|
+
},
|
|
49
|
+
"gypfile": true,
|
|
50
|
+
"scripts": {
|
|
51
|
+
"install": "node-gyp rebuild -j max",
|
|
52
|
+
"build:native": "node-gyp rebuild -j max",
|
|
53
|
+
"clean": "node -e \"require('node:fs').rmSync('dist', {recursive: true, force: true})\"",
|
|
54
|
+
"build:cjs": "tsc -p tsconfig.cjs.json",
|
|
55
|
+
"build:esm": "tsc -p tsconfig.esm.json",
|
|
56
|
+
"build:cjs-pkg": "node ../../scripts/package-build/cjs-package.mjs",
|
|
57
|
+
"build:addon": "node scripts/addon.mjs",
|
|
58
|
+
"build:js": "npm run build:cjs && npm run build:esm && npm run build:cjs-pkg && npm run build:addon",
|
|
59
|
+
"rebuild": "npm run clean && npm run build:js",
|
|
60
|
+
"compile": "npm run rebuild",
|
|
61
|
+
"test:unit": "npm run compile && node --test --test-timeout=60000 \"dist/esm/tests/**/*.spec.js\"",
|
|
62
|
+
"test:bun": "sh test-env/bun.sh",
|
|
63
|
+
"test:electron": "electron test-env/electron/main.mjs"
|
|
64
|
+
},
|
|
65
|
+
"engines": {
|
|
66
|
+
"node": ">=18.10.0"
|
|
67
|
+
},
|
|
68
|
+
"files": [
|
|
69
|
+
"dist",
|
|
70
|
+
"!dist/*/tests",
|
|
71
|
+
"binding.gyp",
|
|
72
|
+
"native",
|
|
73
|
+
"*.md",
|
|
74
|
+
"LICENSE",
|
|
75
|
+
"package.json"
|
|
76
|
+
],
|
|
77
|
+
"dependencies": {
|
|
78
|
+
"node-addon-api": "^8.9.2"
|
|
79
|
+
},
|
|
80
|
+
"peerDependencies": {
|
|
81
|
+
"@lakutata/core": "~3.1.5"
|
|
82
|
+
}
|
|
83
|
+
}
|