@lakutata/netif 0.0.0-stage → 3.1.4
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,313 @@
|
|
|
1
|
+
import { EventEmitter } from 'node:events';
|
|
2
|
+
import { type Server } from 'node:net';
|
|
3
|
+
import { InterfaceListener, type ListenOptions } from './InterfaceListener.js';
|
|
4
|
+
import { InterfaceUDPSocket, type UDPSocketOptions } from './InterfaceUDPSocket.js';
|
|
5
|
+
import { OBSERVE, type Observer } from './Internal.js';
|
|
6
|
+
import { type InterfaceAddress, type NetworkInterfaceInfo } from './Types.js';
|
|
7
|
+
/**
|
|
8
|
+
* How an interface is given: its name (`'eth1'`, `'en0'`, `'以太网 2'`), its hardware address (`{mac: '52:54:00:12:34:56'}`,
|
|
9
|
+
* the same on every system, the one to give a management port by) or its index (`{index: 3}`, which changes when the
|
|
10
|
+
* interface is unplugged and plugged again).
|
|
11
|
+
*/
|
|
12
|
+
export type InterfaceSelector = string | {
|
|
13
|
+
/**
|
|
14
|
+
* The hardware address: `'52:54:00:12:34:56'`, `'52-54-00-12-34-56'`, in either case.
|
|
15
|
+
*/
|
|
16
|
+
mac: string;
|
|
17
|
+
} | {
|
|
18
|
+
/**
|
|
19
|
+
* The index of the interface.
|
|
20
|
+
*/
|
|
21
|
+
index: number;
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* A change of the addresses of an interface: the `change` event.
|
|
25
|
+
*/
|
|
26
|
+
export interface AddressChange {
|
|
27
|
+
/**
|
|
28
|
+
* Every usable address of the interface now.
|
|
29
|
+
*/
|
|
30
|
+
addresses: InterfaceAddress[];
|
|
31
|
+
/**
|
|
32
|
+
* The addresses that came: added, or usable since (an IPv6 address once its duplicate address detection is over).
|
|
33
|
+
*/
|
|
34
|
+
added: InterfaceAddress[];
|
|
35
|
+
/**
|
|
36
|
+
* The addresses that went.
|
|
37
|
+
*/
|
|
38
|
+
removed: InterfaceAddress[];
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* The options of `waitForAddress()`.
|
|
42
|
+
*/
|
|
43
|
+
export interface WaitForAddressOptions {
|
|
44
|
+
/**
|
|
45
|
+
* The family of the address to wait for, any by default.
|
|
46
|
+
*/
|
|
47
|
+
family?: 'IPv4' | 'IPv6';
|
|
48
|
+
/**
|
|
49
|
+
* The milliseconds to wait at most, without limit by default: then a `NetIfTimeoutException`.
|
|
50
|
+
*/
|
|
51
|
+
timeout?: number;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* The options of `bindSocket()`.
|
|
55
|
+
*/
|
|
56
|
+
export interface BindSocketOptions {
|
|
57
|
+
/**
|
|
58
|
+
* The port, 0 by default: one the system chooses (`port` of the result).
|
|
59
|
+
*/
|
|
60
|
+
port?: number;
|
|
61
|
+
/**
|
|
62
|
+
* The family of the socket, `'IPv4'` by default. An IPv6 socket is IPv6 only.
|
|
63
|
+
*/
|
|
64
|
+
family?: 'IPv4' | 'IPv6';
|
|
65
|
+
/**
|
|
66
|
+
* `'tcp'` (a listening socket, by default) or `'udp'`.
|
|
67
|
+
*/
|
|
68
|
+
type?: 'tcp' | 'udp';
|
|
69
|
+
/**
|
|
70
|
+
* The length of the queue of the connections not accepted yet of a TCP socket, 511 by default.
|
|
71
|
+
*/
|
|
72
|
+
backlog?: number;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* A socket bound to an interface: `bindSocket()`.
|
|
76
|
+
*/
|
|
77
|
+
export interface BoundSocket {
|
|
78
|
+
/**
|
|
79
|
+
* Its file descriptor, to give to `server.listen({fd})` (a `net.Server`) or `socket.bind({fd})` (a `dgram` socket),
|
|
80
|
+
* which then own it; closed by `fs.closeSync(fd)` when it is not given.
|
|
81
|
+
*/
|
|
82
|
+
fd: number;
|
|
83
|
+
/**
|
|
84
|
+
* Its port.
|
|
85
|
+
*/
|
|
86
|
+
port: number;
|
|
87
|
+
/**
|
|
88
|
+
* Its family.
|
|
89
|
+
*/
|
|
90
|
+
family: 'IPv4' | 'IPv6';
|
|
91
|
+
/**
|
|
92
|
+
* Its type.
|
|
93
|
+
*/
|
|
94
|
+
type: 'tcp' | 'udp';
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* The events of a `NetworkInterface` and their arguments.
|
|
98
|
+
*/
|
|
99
|
+
export interface NetworkInterfaceEvents {
|
|
100
|
+
/**
|
|
101
|
+
* The addresses changed (some added, some removed).
|
|
102
|
+
*/
|
|
103
|
+
change: [change: AddressChange];
|
|
104
|
+
/**
|
|
105
|
+
* The link came up: a cable plugged in, an association made.
|
|
106
|
+
*/
|
|
107
|
+
up: [];
|
|
108
|
+
/**
|
|
109
|
+
* The link went down.
|
|
110
|
+
*/
|
|
111
|
+
down: [];
|
|
112
|
+
/**
|
|
113
|
+
* The interface appeared: plugged, created, enabled (on Windows).
|
|
114
|
+
*/
|
|
115
|
+
added: [];
|
|
116
|
+
/**
|
|
117
|
+
* The interface disappeared.
|
|
118
|
+
*/
|
|
119
|
+
removed: [];
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* What changed between two states of an interface: whether it appeared or disappeared, its link came up or went down,
|
|
123
|
+
* and its addresses added and removed
|
|
124
|
+
*/
|
|
125
|
+
export declare function changesOf(previous: NetworkInterfaceInfo | undefined, next: NetworkInterfaceInfo | undefined): {
|
|
126
|
+
appeared: boolean;
|
|
127
|
+
disappeared: boolean;
|
|
128
|
+
up: boolean;
|
|
129
|
+
down: boolean;
|
|
130
|
+
added: InterfaceAddress[];
|
|
131
|
+
removed: InterfaceAddress[];
|
|
132
|
+
};
|
|
133
|
+
/**
|
|
134
|
+
* A network interface of the machine, followed: its state and addresses now, events when they change, and the servers
|
|
135
|
+
* listening on it. The interface may not exist yet (a USB adapter not plugged in, a system starting): the object follows
|
|
136
|
+
* it once it appears, and again when it is unplugged and plugged back.
|
|
137
|
+
* @example
|
|
138
|
+
* ```typescript
|
|
139
|
+
* import {type InterfaceAddress, NetworkInterface} from '@lakutata/netif'
|
|
140
|
+
*
|
|
141
|
+
* const management: NetworkInterface = new NetworkInterface('eth1')
|
|
142
|
+
* console.log(management.present, management.running, management.addresses.map(address => address.address))
|
|
143
|
+
* management.on('change', ({added, removed}) => console.log('added', added, 'removed', removed))
|
|
144
|
+
* management.on('down', () => console.log('the cable of eth1 is unplugged'))
|
|
145
|
+
* const address: InterfaceAddress = await management.waitForAddress({family: 'IPv4', timeout: 30000})
|
|
146
|
+
* console.log(`http://${address.address}:8080`)
|
|
147
|
+
* management.close()
|
|
148
|
+
* ```
|
|
149
|
+
*/
|
|
150
|
+
export declare class NetworkInterface extends EventEmitter<NetworkInterfaceEvents> {
|
|
151
|
+
#private;
|
|
152
|
+
/**
|
|
153
|
+
* How the interface was given.
|
|
154
|
+
*/
|
|
155
|
+
readonly selector: InterfaceSelector;
|
|
156
|
+
/**
|
|
157
|
+
* Follows an interface.
|
|
158
|
+
* @param selector Its name, `{mac}` or `{index}`.
|
|
159
|
+
* @throws NetIfNotFoundException An index that no interface has (an index is never given again once its interface is
|
|
160
|
+
* gone), or a hardware address that several interfaces have now (a bridge and its ports: give the name then).
|
|
161
|
+
*/
|
|
162
|
+
constructor(selector: InterfaceSelector);
|
|
163
|
+
/**
|
|
164
|
+
* The network interfaces of the machine, as `listInterfaces()` gives them.
|
|
165
|
+
* @returns The interfaces, in the order of the system.
|
|
166
|
+
* @example
|
|
167
|
+
* ```typescript
|
|
168
|
+
* import {NetworkInterface} from '@lakutata/netif'
|
|
169
|
+
*
|
|
170
|
+
* const names: string[] = NetworkInterface.list().map(entry => entry.name)
|
|
171
|
+
* ```
|
|
172
|
+
*/
|
|
173
|
+
static list(): NetworkInterfaceInfo[];
|
|
174
|
+
/**
|
|
175
|
+
* Whether the interface exists now.
|
|
176
|
+
*/
|
|
177
|
+
get present(): boolean;
|
|
178
|
+
/**
|
|
179
|
+
* Its name, `undefined` while it does not exist.
|
|
180
|
+
*/
|
|
181
|
+
get name(): string | undefined;
|
|
182
|
+
/**
|
|
183
|
+
* Its index, `undefined` while it does not exist. It changes when the interface is unplugged and plugged again.
|
|
184
|
+
*/
|
|
185
|
+
get index(): number | undefined;
|
|
186
|
+
/**
|
|
187
|
+
* Its hardware address, `undefined` while it does not exist or when it has none (and on the macOS versions that hide
|
|
188
|
+
* it).
|
|
189
|
+
*/
|
|
190
|
+
get mac(): string | undefined;
|
|
191
|
+
/**
|
|
192
|
+
* Whether it is enabled (its administrative state); `false` while it does not exist.
|
|
193
|
+
*/
|
|
194
|
+
get up(): boolean;
|
|
195
|
+
/**
|
|
196
|
+
* Whether its link is up (a cable plugged in, an association made); `false` while it does not exist.
|
|
197
|
+
*/
|
|
198
|
+
get running(): boolean;
|
|
199
|
+
/**
|
|
200
|
+
* Whether it is the loopback interface.
|
|
201
|
+
*/
|
|
202
|
+
get loopback(): boolean;
|
|
203
|
+
/**
|
|
204
|
+
* Its usable addresses now, an empty array when it has none or does not exist.
|
|
205
|
+
*/
|
|
206
|
+
get addresses(): InterfaceAddress[];
|
|
207
|
+
/**
|
|
208
|
+
* Its state now as `listInterfaces()` gives it, `undefined` while it does not exist.
|
|
209
|
+
*/
|
|
210
|
+
get info(): NetworkInterfaceInfo | undefined;
|
|
211
|
+
/**
|
|
212
|
+
* Whether the object was closed.
|
|
213
|
+
*/
|
|
214
|
+
get closed(): boolean;
|
|
215
|
+
/**
|
|
216
|
+
* An address of the interface: one it has now, or the first one it gets.
|
|
217
|
+
* @param options The family of the address and the milliseconds to wait at most.
|
|
218
|
+
* @returns The address.
|
|
219
|
+
* @throws NetIfTimeoutException No address came in time.
|
|
220
|
+
* @throws NetIfException The object was closed while waiting.
|
|
221
|
+
* @example
|
|
222
|
+
* ```typescript
|
|
223
|
+
* import {type InterfaceAddress, NetworkInterface} from '@lakutata/netif'
|
|
224
|
+
*
|
|
225
|
+
* const wan: NetworkInterface = new NetworkInterface('eth0')
|
|
226
|
+
* const address: InterfaceAddress = await wan.waitForAddress({family: 'IPv4', timeout: 60000})
|
|
227
|
+
* wan.close()
|
|
228
|
+
* ```
|
|
229
|
+
*/
|
|
230
|
+
waitForAddress(options?: WaitForAddressOptions): Promise<InterfaceAddress>;
|
|
231
|
+
/**
|
|
232
|
+
* Listens on the interface for the server: its connections are accepted by the listener returned and given to the
|
|
233
|
+
* server, which handles them as its own. On Linux the sockets are bound to the interface (whatever its addresses, the
|
|
234
|
+
* system refuses the connections coming through another interface); on Windows and macOS each address of the
|
|
235
|
+
* interface is listened on, and listened on again when they change (`enforced` tells whether the system refuses the
|
|
236
|
+
* connections coming through another interface). The interface may not exist yet, nor have an address: it is listened
|
|
237
|
+
* on once it can be, and again when it is unplugged and plugged back.
|
|
238
|
+
* @param server A `net.Server` or a server built on one: `http`, `https`, `http2`, `tls`; for Express
|
|
239
|
+
* `createServer(app)`, for Fastify `fastify.server`. The server itself must not listen.
|
|
240
|
+
* @param options The port, the families, `strict` and the backlog.
|
|
241
|
+
* @returns The listener, once the interface is listened on (or waited for).
|
|
242
|
+
* @throws NetIfNotEnforcedException With `strict`, where the system cannot refuse the connections coming through
|
|
243
|
+
* another interface.
|
|
244
|
+
* @throws NetIfException The system refused the first listening, its code as `errno` (`'EADDRINUSE'`, `'EACCES'`…).
|
|
245
|
+
* Later refusals are `error` events of the listener, and retried.
|
|
246
|
+
* @example
|
|
247
|
+
* ```typescript
|
|
248
|
+
* import {createServer} from 'node:http'
|
|
249
|
+
* import express from 'express'
|
|
250
|
+
* import {type InterfaceListener, NetworkInterface} from '@lakutata/netif'
|
|
251
|
+
*
|
|
252
|
+
* const app = express()
|
|
253
|
+
* app.get('/', (request, response) => response.send('ok'))
|
|
254
|
+
* const management: NetworkInterface = new NetworkInterface({mac: '52:54:00:12:34:56'})
|
|
255
|
+
* const listener: InterfaceListener = await management.listen(createServer(app), {port: 8080})
|
|
256
|
+
* listener.on('error', error => console.error(error))
|
|
257
|
+
* ```
|
|
258
|
+
*/
|
|
259
|
+
listen(server: Server, options: ListenOptions): Promise<InterfaceListener>;
|
|
260
|
+
/**
|
|
261
|
+
* A UDP socket of the interface, with the methods and the events of a `dgram` socket. On Linux it is bound to the
|
|
262
|
+
* interface: it receives what comes through it, broadcasts and the multicast of the groups joined there included,
|
|
263
|
+
* whatever its addresses. On Windows and macOS a socket is bound to each address of the interface (on macOS it
|
|
264
|
+
* receives no broadcast nor multicast then). It is bound again when the addresses change and when the interface is
|
|
265
|
+
* plugged back.
|
|
266
|
+
* @param options The port, the families and `strict`.
|
|
267
|
+
* @returns The socket, once it is bound (or the interface waited for).
|
|
268
|
+
* @throws NetIfNotEnforcedException With `strict`, where the system cannot refuse the datagrams coming through
|
|
269
|
+
* another interface.
|
|
270
|
+
* @throws NetIfException The system refused the first binding, its code as `errno`.
|
|
271
|
+
* @example
|
|
272
|
+
* ```typescript
|
|
273
|
+
* import {type InterfaceUDPSocket, NetworkInterface} from '@lakutata/netif'
|
|
274
|
+
*
|
|
275
|
+
* const socket: InterfaceUDPSocket = await new NetworkInterface('eth1').createUDPSocket({port: 9999, family: 'IPv4'})
|
|
276
|
+
* socket.setBroadcast(true)
|
|
277
|
+
* socket.on('message', (message, remote) => socket.send(message, remote.port, remote.address))
|
|
278
|
+
* ```
|
|
279
|
+
*/
|
|
280
|
+
createUDPSocket(options: UDPSocketOptions): Promise<InterfaceUDPSocket>;
|
|
281
|
+
/**
|
|
282
|
+
* A socket bound to the interface (Linux, `SO_BINDTODEVICE`), on the port of every address of its family: it gets the
|
|
283
|
+
* connections and the datagrams that come through the interface, whatever the address they were sent to, and the
|
|
284
|
+
* system refuses the other ones. For the users that need the server itself listening (its `address()` and `close()`
|
|
285
|
+
* as usual): the socket is not made again when the interface is unplugged and plugged back, which `listen()` does.
|
|
286
|
+
* In Bun, give the descriptor to a `net.Server` or a `dgram` socket (Bun's `http.Server` does not listen on one).
|
|
287
|
+
* @param options The port, the family, the type and the backlog.
|
|
288
|
+
* @returns The descriptor of the socket and its port.
|
|
289
|
+
* @throws NetIfNotSupportedException Outside Linux: macOS and Windows have no socket bound to an interface.
|
|
290
|
+
* @throws NetIfNotFoundException The interface does not exist now.
|
|
291
|
+
* @throws NetIfNotEnforcedException The process may not bind a socket to an interface (CAP_NET_RAW before Linux 5.7).
|
|
292
|
+
* @throws NetIfException Another refusal of the system, its code as `errno` (`'EADDRINUSE'`…).
|
|
293
|
+
* @example
|
|
294
|
+
* ```typescript
|
|
295
|
+
* import {createServer} from 'node:http'
|
|
296
|
+
* import {type BoundSocket, NetworkInterface} from '@lakutata/netif'
|
|
297
|
+
*
|
|
298
|
+
* const management: NetworkInterface = new NetworkInterface('eth1')
|
|
299
|
+
* const socket: BoundSocket = management.bindSocket({port: 8080, family: 'IPv4', type: 'tcp'})
|
|
300
|
+
* createServer((request, response) => response.end('ok')).listen({fd: socket.fd})
|
|
301
|
+
* ```
|
|
302
|
+
*/
|
|
303
|
+
bindSocket(options?: BindSocketOptions): BoundSocket;
|
|
304
|
+
/**
|
|
305
|
+
* Stops following the interface: no event any more; its listeners are closed, the waits of `waitForAddress()`
|
|
306
|
+
* rejected.
|
|
307
|
+
*/
|
|
308
|
+
close(): void;
|
|
309
|
+
/**
|
|
310
|
+
* Internal: follows the readings of the interfaces (the listeners of the interface)
|
|
311
|
+
*/
|
|
312
|
+
[OBSERVE](observer: Observer): () => void;
|
|
313
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { type NetworkInterfaceInfo } from './Types.js';
|
|
2
|
+
/**
|
|
3
|
+
* A subscriber of the changes: called with the interfaces read after each change
|
|
4
|
+
*/
|
|
5
|
+
export type Subscriber = (interfaces: NetworkInterfaceInfo[]) => void;
|
|
6
|
+
/**
|
|
7
|
+
* The interfaces of the machine as the package follows them: the notifications of the system shared by every
|
|
8
|
+
* subscriber of the thread, started with the first one and stopped with the last one. The timers do not keep the event
|
|
9
|
+
* loop alive.
|
|
10
|
+
*/
|
|
11
|
+
declare class Registry {
|
|
12
|
+
#private;
|
|
13
|
+
/**
|
|
14
|
+
* The interfaces read last while followed, read now otherwise
|
|
15
|
+
*/
|
|
16
|
+
current(): NetworkInterfaceInfo[];
|
|
17
|
+
/**
|
|
18
|
+
* Follows the interfaces: the subscriber is called after each change; the function returned unsubscribes it
|
|
19
|
+
*/
|
|
20
|
+
subscribe(subscriber: Subscriber): () => void;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* The registry of the thread
|
|
24
|
+
*/
|
|
25
|
+
export declare const registry: Registry;
|
|
26
|
+
export {};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { type NativeInterface } from './Native.js';
|
|
2
|
+
import { type NetworkInterfaceInfo } from './Types.js';
|
|
3
|
+
/**
|
|
4
|
+
* The mask of a prefix length: `'255.255.255.0'` for 24 in IPv4, `'ffff:ffff:ffff:ffff::'` for 64 in IPv6
|
|
5
|
+
*/
|
|
6
|
+
export declare function netmaskOf(family: 'IPv4' | 'IPv6', prefixLength: number): string;
|
|
7
|
+
/**
|
|
8
|
+
* An interface read by the addon as an interface of the package: its usable addresses only
|
|
9
|
+
*/
|
|
10
|
+
export declare function interfaceOf(entry: NativeInterface): NetworkInterfaceInfo;
|
|
11
|
+
/**
|
|
12
|
+
* The interfaces of the machine now, and whether some address is still tentative (to be read again soon)
|
|
13
|
+
*/
|
|
14
|
+
export declare function readInterfaces(): {
|
|
15
|
+
interfaces: NetworkInterfaceInfo[];
|
|
16
|
+
tentative: boolean;
|
|
17
|
+
};
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* An address of a network interface, written as `os.networkInterfaces()` writes it
|
|
3
|
+
*/
|
|
4
|
+
export interface InterfaceAddress {
|
|
5
|
+
/**
|
|
6
|
+
* The address: `'192.168.10.2'`, `'fd00::2'`, `'fe80::1c82:430f:35a3:800a'` (an IPv6 address without its scope).
|
|
7
|
+
*/
|
|
8
|
+
address: string;
|
|
9
|
+
/**
|
|
10
|
+
* `'IPv4'` or `'IPv6'`.
|
|
11
|
+
*/
|
|
12
|
+
family: 'IPv4' | 'IPv6';
|
|
13
|
+
/**
|
|
14
|
+
* The mask of the network of the address: `'255.255.255.0'`, `'ffff:ffff:ffff:ffff::'`.
|
|
15
|
+
*/
|
|
16
|
+
netmask: string;
|
|
17
|
+
/**
|
|
18
|
+
* The address and the length of its prefix: `'192.168.10.2/24'`.
|
|
19
|
+
*/
|
|
20
|
+
cidr: string;
|
|
21
|
+
/**
|
|
22
|
+
* IPv6 only: the scope of the address, as `os.networkInterfaces()` gives it: the index of the interface for a
|
|
23
|
+
* link-local address (`fe80::…`, written `fe80::…%<scopeid>` to be connected to), `0` for a global one (Windows gives
|
|
24
|
+
* a site-local address, `fec0::…`, the number of its site).
|
|
25
|
+
*/
|
|
26
|
+
scopeid?: number;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* A network interface of the machine and its state when it was read
|
|
30
|
+
*/
|
|
31
|
+
export interface NetworkInterfaceInfo {
|
|
32
|
+
/**
|
|
33
|
+
* The name of the interface: `'eth1'`, `'en0'`, on Windows the name of its network connection (`'以太网 2'`,
|
|
34
|
+
* `'Ethernet 2'`, in the language of the system).
|
|
35
|
+
*/
|
|
36
|
+
name: string;
|
|
37
|
+
/**
|
|
38
|
+
* The index the system gives the interface. It changes when the interface is unplugged and plugged again.
|
|
39
|
+
*/
|
|
40
|
+
index: number;
|
|
41
|
+
/**
|
|
42
|
+
* The hardware address (`'52:54:00:12:34:56'`), `undefined` for an interface without one (loopback, tunnels) and on
|
|
43
|
+
* the macOS versions that hide it from the processes (macOS 27 gives `02:00:00:00:00:00` for every interface).
|
|
44
|
+
*/
|
|
45
|
+
mac?: string;
|
|
46
|
+
/**
|
|
47
|
+
* Whether the interface is enabled (its administrative state). On Windows a disabled adapter is not listed at all.
|
|
48
|
+
*/
|
|
49
|
+
up: boolean;
|
|
50
|
+
/**
|
|
51
|
+
* Whether its link is up: a cable plugged in, an association made.
|
|
52
|
+
*/
|
|
53
|
+
running: boolean;
|
|
54
|
+
/**
|
|
55
|
+
* Whether it is the loopback interface.
|
|
56
|
+
*/
|
|
57
|
+
loopback: boolean;
|
|
58
|
+
/**
|
|
59
|
+
* Its usable addresses, an empty array when it has none. An IPv6 address still in its duplicate address detection
|
|
60
|
+
* (about one second after it is added) is not usable yet and not listed.
|
|
61
|
+
*/
|
|
62
|
+
addresses: InterfaceAddress[];
|
|
63
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
//The parts of the reading of the interfaces common to every system
|
|
2
|
+
#include "Interfaces.h"
|
|
3
|
+
|
|
4
|
+
#ifdef _WIN32
|
|
5
|
+
#include <winsock2.h>
|
|
6
|
+
#include <ws2tcpip.h>
|
|
7
|
+
#else
|
|
8
|
+
#include <arpa/inet.h>
|
|
9
|
+
#include <sys/socket.h>
|
|
10
|
+
#endif
|
|
11
|
+
|
|
12
|
+
#include <cstdio>
|
|
13
|
+
|
|
14
|
+
namespace netif {
|
|
15
|
+
|
|
16
|
+
std::string addressText(int family, const void *bytes)
|
|
17
|
+
{
|
|
18
|
+
char text[64] = {0};
|
|
19
|
+
if (!inet_ntop(family, const_cast<void *>(bytes), text, sizeof text)) return std::string();
|
|
20
|
+
return std::string(text);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
std::string macText(const unsigned char *bytes, size_t length)
|
|
24
|
+
{
|
|
25
|
+
if (length != 6) return std::string();
|
|
26
|
+
bool zero = true;
|
|
27
|
+
for (size_t index = 0; index < length; index++) zero = zero && bytes[index] == 0;
|
|
28
|
+
if (zero) return std::string();
|
|
29
|
+
char text[18];
|
|
30
|
+
std::snprintf(text, sizeof text, "%02x:%02x:%02x:%02x:%02x:%02x", bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5]);
|
|
31
|
+
return std::string(text);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
//The network interfaces of the machine, read from the system: every interface, the ones without an address too, with
|
|
2
|
+
//its state and its unicast addresses. Each system has its own source file (linux/, darwin/, windows/).
|
|
3
|
+
#pragma once
|
|
4
|
+
|
|
5
|
+
#include <cstdint>
|
|
6
|
+
#include <string>
|
|
7
|
+
#include <vector>
|
|
8
|
+
|
|
9
|
+
namespace netif {
|
|
10
|
+
|
|
11
|
+
//A unicast address of an interface
|
|
12
|
+
struct Address {
|
|
13
|
+
//The address as text (an IPv6 address without its scope)
|
|
14
|
+
std::string address;
|
|
15
|
+
//4 or 6
|
|
16
|
+
int family = 4;
|
|
17
|
+
unsigned prefixLength = 0;
|
|
18
|
+
//Not usable yet: an IPv6 address in its duplicate address detection, or one that failed it
|
|
19
|
+
bool tentative = false;
|
|
20
|
+
//IPv6: the scope of the address, the index of its interface for a link-local one, 0 for a global one (Windows also
|
|
21
|
+
//gives the site of a site-local one)
|
|
22
|
+
uint32_t scopeId = 0;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
struct Interface {
|
|
26
|
+
std::string name;
|
|
27
|
+
uint32_t index = 0;
|
|
28
|
+
//The hardware address (aa:bb:cc:dd:ee:ff), empty when the interface has none
|
|
29
|
+
std::string mac;
|
|
30
|
+
//Enabled (its administrative state)
|
|
31
|
+
bool up = false;
|
|
32
|
+
//Connected: its link is up
|
|
33
|
+
bool running = false;
|
|
34
|
+
bool loopback = false;
|
|
35
|
+
std::vector<Address> addresses;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
//Every interface of the machine; a std::system_error when the system cannot be read
|
|
39
|
+
std::vector<Interface> listInterfaces();
|
|
40
|
+
|
|
41
|
+
//The text of an address of the given family (AF_INET or AF_INET6) from its bytes
|
|
42
|
+
std::string addressText(int family, const void *bytes);
|
|
43
|
+
|
|
44
|
+
//The text of a hardware address of 6 bytes, empty for none (any other length, or all zeros)
|
|
45
|
+
std::string macText(const unsigned char *bytes, size_t length);
|
|
46
|
+
|
|
47
|
+
}
|
package/native/Sockets.h
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
//The sockets bound to an interface (Linux: SO_BINDTODEVICE) and their multicast groups, and whether the system refuses
|
|
2
|
+
//the packets reaching the addresses of an interface through another one (the strong host model: Windows, macOS).
|
|
3
|
+
#pragma once
|
|
4
|
+
|
|
5
|
+
#include <cstdint>
|
|
6
|
+
#include <optional>
|
|
7
|
+
#include <string>
|
|
8
|
+
|
|
9
|
+
namespace netif {
|
|
10
|
+
|
|
11
|
+
struct BoundSocket {
|
|
12
|
+
int fd = -1;
|
|
13
|
+
uint16_t port = 0;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
//A socket of the family (4, 6) bound to the interface of the name, on the port of every address of the family (0: one
|
|
17
|
+
//the system chooses); a TCP socket listening already. IPv6 only for 6 (IPV6_V6ONLY). A std::system_error when the system
|
|
18
|
+
//refuses (EPERM: binding to an interface needs CAP_NET_RAW before Linux 5.7; ENODEV: no such interface; EADDRINUSE…).
|
|
19
|
+
BoundSocket bindToInterface(const std::string &name, int family, bool stream, uint16_t port, int backlog);
|
|
20
|
+
|
|
21
|
+
//The socket joins (or leaves) the multicast group on the interface of the index (MCAST_JOIN_GROUP: by its index, the
|
|
22
|
+
//interface may have no address)
|
|
23
|
+
void membership(int fd, const std::string &group, uint32_t index, bool join);
|
|
24
|
+
|
|
25
|
+
//A setting that makes the system accept, through another interface, packets for the addresses of the interface of the
|
|
26
|
+
//index: the interface and the setting
|
|
27
|
+
struct WeakReception {
|
|
28
|
+
std::string interfaceName;
|
|
29
|
+
std::string setting;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
//The first setting of the family (4, 6) that makes the system accept, through another interface, the packets for the
|
|
33
|
+
//addresses of the interface of the index; none when it refuses them
|
|
34
|
+
std::optional<WeakReception> weakReception(uint32_t index, int family);
|
|
35
|
+
|
|
36
|
+
}
|
package/native/Watcher.h
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
//The notifications of the changes of the interfaces (links, addresses, their settings), from a thread of the addon or
|
|
2
|
+
//of the system: each one calls the function given, which only signals that something changed (JavaScript reads the
|
|
3
|
+
//interfaces again). Stopped when destroyed: no call once the destructor has returned. Each system has its own source
|
|
4
|
+
//file (linux/, darwin/, windows/).
|
|
5
|
+
#pragma once
|
|
6
|
+
|
|
7
|
+
#include <functional>
|
|
8
|
+
#include <memory>
|
|
9
|
+
|
|
10
|
+
namespace netif {
|
|
11
|
+
|
|
12
|
+
class Watcher {
|
|
13
|
+
public:
|
|
14
|
+
explicit Watcher(std::function<void()> changed);
|
|
15
|
+
|
|
16
|
+
~Watcher();
|
|
17
|
+
|
|
18
|
+
Watcher(const Watcher &) = delete;
|
|
19
|
+
|
|
20
|
+
Watcher &operator=(const Watcher &) = delete;
|
|
21
|
+
|
|
22
|
+
private:
|
|
23
|
+
struct State;
|
|
24
|
+
std::unique_ptr<State> state;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
}
|