matterbridge 3.5.0-dev-20260119-f9ea00e → 3.5.1-dev-20260121-22e98b4

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.
Files changed (71) hide show
  1. package/CHANGELOG.md +133 -117
  2. package/bin/mb_coap.js +1 -1
  3. package/bin/mb_mdns.js +1 -1
  4. package/dist/broadcastServer.js +1 -2
  5. package/dist/cli.d.ts +1 -2
  6. package/dist/cli.js +1 -5
  7. package/dist/cliHistory.js +1 -1
  8. package/dist/deviceManager.js +1 -1
  9. package/dist/frontend.js +2 -7
  10. package/dist/helpers.js +1 -1
  11. package/dist/matterNode.js +1 -5
  12. package/dist/matterbridge.js +14 -18
  13. package/dist/matterbridgeEndpoint.d.ts +2 -0
  14. package/dist/matterbridgeEndpoint.js +41 -2
  15. package/dist/matterbridgeEndpointHelpers.js +1 -3
  16. package/dist/matterbridgePlatform.js +1 -2
  17. package/dist/{dgram/mb_mdns.js → mb_mdns.js} +13 -1
  18. package/dist/pluginManager.js +3 -4
  19. package/dist/{utils/spawn.js → spawn.js} +2 -2
  20. package/dist/update.js +6 -7
  21. package/dist/utils/export.d.ts +1 -12
  22. package/dist/utils/export.js +1 -12
  23. package/dist/workerGlobalPrefix.js +1 -3
  24. package/frontend/build/assets/index.js +4 -4
  25. package/frontend/package.json +1 -1
  26. package/npm-shrinkwrap.json +2 -2
  27. package/package.json +5 -5
  28. package/dist/dgram/coap.d.ts +0 -34
  29. package/dist/dgram/coap.js +0 -252
  30. package/dist/dgram/dgram.d.ts +0 -45
  31. package/dist/dgram/dgram.js +0 -251
  32. package/dist/dgram/mdns.d.ts +0 -188
  33. package/dist/dgram/mdns.js +0 -702
  34. package/dist/dgram/multicast.d.ts +0 -18
  35. package/dist/dgram/multicast.js +0 -118
  36. package/dist/dgram/unicast.d.ts +0 -11
  37. package/dist/dgram/unicast.js +0 -40
  38. package/dist/utils/colorUtils.d.ts +0 -24
  39. package/dist/utils/colorUtils.js +0 -187
  40. package/dist/utils/commandLine.d.ts +0 -6
  41. package/dist/utils/commandLine.js +0 -63
  42. package/dist/utils/copyDirectory.d.ts +0 -2
  43. package/dist/utils/copyDirectory.js +0 -39
  44. package/dist/utils/createDirectory.d.ts +0 -2
  45. package/dist/utils/createDirectory.js +0 -21
  46. package/dist/utils/createZip.d.ts +0 -1
  47. package/dist/utils/createZip.js +0 -69
  48. package/dist/utils/deepCopy.d.ts +0 -1
  49. package/dist/utils/deepCopy.js +0 -40
  50. package/dist/utils/deepEqual.d.ts +0 -1
  51. package/dist/utils/deepEqual.js +0 -58
  52. package/dist/utils/error.d.ts +0 -3
  53. package/dist/utils/error.js +0 -12
  54. package/dist/utils/format.d.ts +0 -4
  55. package/dist/utils/format.js +0 -29
  56. package/dist/utils/hex.d.ts +0 -4
  57. package/dist/utils/hex.js +0 -118
  58. package/dist/utils/inspector.d.ts +0 -24
  59. package/dist/utils/inspector.js +0 -200
  60. package/dist/utils/isValid.d.ts +0 -10
  61. package/dist/utils/isValid.js +0 -69
  62. package/dist/utils/network.d.ts +0 -25
  63. package/dist/utils/network.js +0 -193
  64. package/dist/utils/tracker.d.ts +0 -52
  65. package/dist/utils/tracker.js +0 -201
  66. package/dist/utils/wait.d.ts +0 -3
  67. package/dist/utils/wait.js +0 -73
  68. /package/dist/{dgram/mb_coap.d.ts → mb_coap.d.ts} +0 -0
  69. /package/dist/{dgram/mb_coap.js → mb_coap.js} +0 -0
  70. /package/dist/{dgram/mb_mdns.d.ts → mb_mdns.d.ts} +0 -0
  71. /package/dist/{utils/spawn.d.ts → spawn.d.ts} +0 -0
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "frontend",
3
- "version": "3.4.1",
3
+ "version": "3.4.2",
4
4
  "private": true,
5
5
  "homepage": "./",
6
6
  "type": "module",
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "matterbridge",
3
- "version": "3.5.0-dev-20260119-f9ea00e",
3
+ "version": "3.5.1-dev-20260121-22e98b4",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "matterbridge",
9
- "version": "3.5.0-dev-20260119-f9ea00e",
9
+ "version": "3.5.1-dev-20260121-22e98b4",
10
10
  "license": "Apache-2.0",
11
11
  "dependencies": {
12
12
  "@matter/main": "0.16.5",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "matterbridge",
3
- "version": "3.5.0-dev-20260119-f9ea00e",
3
+ "version": "3.5.1-dev-20260121-22e98b4",
4
4
  "description": "Matterbridge plugin manager for Matter",
5
5
  "author": "https://github.com/Luligu",
6
6
  "license": "Apache-2.0",
@@ -118,12 +118,12 @@
118
118
  "ws": "8.19.0"
119
119
  },
120
120
  "build": {
121
- "sha": "f9ea00e167b988574aeb124f9eb6894c12c0b4e5",
122
- "sha7": "f9ea00e",
123
- "event": "workflow_dispatch",
121
+ "sha": "e333c4a7e80ef23848f41deb21562ec89e3ab90b",
122
+ "sha7": "e333c4a",
123
+ "event": "schedule",
124
124
  "workflow": "Daily Dev Publish to npm",
125
125
  "type": "branch",
126
- "name": "dev",
126
+ "name": "main",
127
127
  "docker": "false"
128
128
  }
129
129
  }
@@ -1,34 +0,0 @@
1
- import dgram from 'node:dgram';
2
- import { Multicast } from './multicast.js';
3
- export interface CoapMessage {
4
- version: number;
5
- type: number;
6
- tokenLength: number;
7
- code: number;
8
- messageId: number;
9
- token: Buffer;
10
- options: CoapOption[];
11
- payload?: Buffer;
12
- }
13
- interface CoapOption {
14
- number: number;
15
- value: Buffer;
16
- }
17
- export declare const COAP_OPTION_URI_PATH = 11;
18
- export declare const COIOT_OPTION_DEVID = 3332;
19
- export declare const COIOT_OPTION_VALIDITY = 3412;
20
- export declare const COIOT_OPTION_SERIAL = 3420;
21
- export declare const COIOT_REQUEST_STATUS_ID = 56831;
22
- export declare const COIOT_REQUEST_DESCRIPTION_ID = 56832;
23
- export declare class Coap extends Multicast {
24
- constructor(name: string, multicastAddress: string, multicastPort: number, socketType: 'udp4' | 'udp6', reuseAddr?: boolean | undefined, interfaceName?: string, interfaceAddress?: string);
25
- onCoapMessage(message: CoapMessage, rinfo: dgram.RemoteInfo): void;
26
- onMessage(msg: Buffer, rinfo: dgram.RemoteInfo): void;
27
- decodeCoapMessage(msg: Buffer): CoapMessage;
28
- encodeCoapMessage(msg: CoapMessage): Buffer;
29
- coapTypeToString(type: number): string;
30
- coapCodeToString(code: number): string;
31
- sendRequest(messageId: number, options: CoapOption[], payload: Record<string, any> | undefined, token: string | undefined, address: string | undefined, port: number | undefined): void;
32
- logCoapMessage(msg: CoapMessage): void;
33
- }
34
- export {};
@@ -1,252 +0,0 @@
1
- import { BLUE, db, GREEN, MAGENTA, nf } from 'node-ansi-logger';
2
- import { COAP_MULTICAST_IPV4_ADDRESS, COAP_MULTICAST_PORT, Multicast } from './multicast.js';
3
- export const COAP_OPTION_URI_PATH = 11;
4
- export const COIOT_OPTION_DEVID = 3332;
5
- export const COIOT_OPTION_VALIDITY = 3412;
6
- export const COIOT_OPTION_SERIAL = 3420;
7
- export const COIOT_REQUEST_STATUS_ID = 56831;
8
- export const COIOT_REQUEST_DESCRIPTION_ID = 56832;
9
- export class Coap extends Multicast {
10
- constructor(name, multicastAddress, multicastPort, socketType, reuseAddr = true, interfaceName, interfaceAddress) {
11
- super(name, multicastAddress, multicastPort, socketType, reuseAddr, interfaceName, interfaceAddress);
12
- }
13
- onCoapMessage(message, rinfo) {
14
- this.log.debug(`Coap message received from ${BLUE}${rinfo.family}${db} ${BLUE}${rinfo.address}${db}:${BLUE}${rinfo.port}${db}`);
15
- }
16
- onMessage(msg, rinfo) {
17
- this.log.info(`Dgram multicast socket received a CoAP message from ${BLUE}${rinfo.family}${nf} ${BLUE}${rinfo.address}${nf}:${BLUE}${rinfo.port}${nf}`);
18
- try {
19
- const result = this.decodeCoapMessage(msg);
20
- this.onCoapMessage(result, rinfo);
21
- this.logCoapMessage(result);
22
- }
23
- catch (error) {
24
- this.log.error(`Error decoding CoAP message: ${error instanceof Error ? error.message : error}`);
25
- }
26
- }
27
- decodeCoapMessage(msg) {
28
- if (msg.length < 4) {
29
- throw new Error('Message too short to be a valid CoAP message');
30
- }
31
- const version = (msg[0] & 0xc0) >> 6;
32
- const type = (msg[0] & 0x30) >> 4;
33
- const tokenLength = msg[0] & 0x0f;
34
- const code = msg[1];
35
- const messageId = msg.readUInt16BE(2);
36
- let offset = 4;
37
- let token = Buffer.alloc(0);
38
- if (tokenLength > 0) {
39
- if (msg.length < offset + tokenLength) {
40
- throw new Error('Message too short for the token length specified');
41
- }
42
- token = msg.slice(offset, offset + tokenLength);
43
- offset += tokenLength;
44
- }
45
- const options = [];
46
- let currentOptionNumber = 0;
47
- while (offset < msg.length) {
48
- if (msg[offset] === 0xff) {
49
- offset++;
50
- break;
51
- }
52
- const optionHeader = msg[offset++];
53
- let delta = (optionHeader & 0xf0) >> 4;
54
- let length = optionHeader & 0x0f;
55
- if (delta === 13) {
56
- if (offset >= msg.length) {
57
- throw new Error('Invalid extended option delta');
58
- }
59
- delta = msg[offset++] + 13;
60
- }
61
- else if (delta === 14) {
62
- if (offset + 1 >= msg.length) {
63
- throw new Error('Invalid extended option delta');
64
- }
65
- delta = msg.readUInt16BE(offset) + 269;
66
- offset += 2;
67
- }
68
- else if (delta === 15) {
69
- throw new Error('Reserved option delta value encountered');
70
- }
71
- if (length === 13) {
72
- if (offset >= msg.length) {
73
- throw new Error('Invalid extended option length');
74
- }
75
- length = msg[offset++] + 13;
76
- }
77
- else if (length === 14) {
78
- if (offset + 1 >= msg.length) {
79
- throw new Error('Invalid extended option length');
80
- }
81
- length = msg.readUInt16BE(offset) + 269;
82
- offset += 2;
83
- }
84
- else if (length === 15) {
85
- throw new Error('Reserved option length value encountered');
86
- }
87
- currentOptionNumber += delta;
88
- if (offset + length > msg.length) {
89
- throw new Error('Option length exceeds message length');
90
- }
91
- const optionValue = msg.slice(offset, offset + length);
92
- offset += length;
93
- options.push({
94
- number: currentOptionNumber,
95
- value: optionValue,
96
- });
97
- }
98
- const payload = offset < msg.length ? msg.slice(offset) : undefined;
99
- return {
100
- version,
101
- type,
102
- tokenLength,
103
- code,
104
- messageId,
105
- token,
106
- options,
107
- payload,
108
- };
109
- }
110
- encodeCoapMessage(msg) {
111
- const parts = [];
112
- const token = msg.token || Buffer.alloc(0);
113
- const tokenLength = token.length;
114
- if (tokenLength > 8) {
115
- throw new Error('Token length cannot exceed 8 bytes');
116
- }
117
- const header = Buffer.alloc(4);
118
- header[0] = ((msg.version & 0x03) << 6) | ((msg.type & 0x03) << 4) | (tokenLength & 0x0f);
119
- header[1] = msg.code;
120
- header.writeUInt16BE(msg.messageId, 2);
121
- parts.push(header);
122
- if (tokenLength > 0) {
123
- parts.push(token);
124
- }
125
- const sortedOptions = msg.options.slice().sort((a, b) => a.number - b.number);
126
- let previousOptionNumber = 0;
127
- for (const option of sortedOptions) {
128
- const optionDelta = option.number - previousOptionNumber;
129
- const optionValueLength = option.value.length;
130
- let deltaNibble;
131
- let deltaExtended = null;
132
- let lengthNibble;
133
- let lengthExtended = null;
134
- if (optionDelta < 13) {
135
- deltaNibble = optionDelta;
136
- }
137
- else if (optionDelta < 269) {
138
- deltaNibble = 13;
139
- deltaExtended = Buffer.from([optionDelta - 13]);
140
- }
141
- else {
142
- deltaNibble = 14;
143
- deltaExtended = Buffer.alloc(2);
144
- deltaExtended.writeUInt16BE(optionDelta - 269, 0);
145
- }
146
- if (optionValueLength < 13) {
147
- lengthNibble = optionValueLength;
148
- }
149
- else if (optionValueLength < 269) {
150
- lengthNibble = 13;
151
- lengthExtended = Buffer.from([optionValueLength - 13]);
152
- }
153
- else {
154
- lengthNibble = 14;
155
- lengthExtended = Buffer.alloc(2);
156
- lengthExtended.writeUInt16BE(optionValueLength - 269, 0);
157
- }
158
- const optionHeader = Buffer.alloc(1);
159
- optionHeader[0] = (deltaNibble << 4) | (lengthNibble & 0x0f);
160
- parts.push(optionHeader);
161
- if (deltaExtended) {
162
- parts.push(deltaExtended);
163
- }
164
- if (lengthExtended) {
165
- parts.push(lengthExtended);
166
- }
167
- parts.push(option.value);
168
- previousOptionNumber = option.number;
169
- }
170
- if (msg.payload && msg.payload.length > 0) {
171
- parts.push(Buffer.from([0xff]));
172
- parts.push(msg.payload);
173
- }
174
- return Buffer.concat(parts);
175
- }
176
- coapTypeToString(type) {
177
- switch (type) {
178
- case 0:
179
- return 'Confirmable (CON)';
180
- case 1:
181
- return 'Non-confirmable (NON)';
182
- case 2:
183
- return 'Acknowledgement (ACK)';
184
- case 3:
185
- return 'Reset (RST)';
186
- default:
187
- return `Unknown Type (${type})`;
188
- }
189
- }
190
- coapCodeToString(code) {
191
- const cls = code >> 5;
192
- const detail = code & 0x1f;
193
- const codeStr = `${cls}.${detail.toString().padStart(2, '0')}`;
194
- return codeStr;
195
- }
196
- sendRequest(messageId, options, payload, token, address, port) {
197
- const coapMessage = {
198
- version: 1,
199
- type: 1,
200
- tokenLength: token ? Buffer.from(token).length : 0,
201
- code: 1,
202
- messageId,
203
- token: token ? Buffer.from(token) : Buffer.alloc(0),
204
- options,
205
- payload: payload ? Buffer.from(JSON.stringify(payload)) : undefined,
206
- };
207
- const encodedBuffer = this.encodeCoapMessage(coapMessage);
208
- this.send(encodedBuffer, address ?? COAP_MULTICAST_IPV4_ADDRESS, port ?? COAP_MULTICAST_PORT);
209
- }
210
- logCoapMessage(msg) {
211
- this.log.info(`Decoded CoAP message: version ${MAGENTA}${msg.version}${nf} type ${MAGENTA}${this.coapTypeToString(msg.type)}${nf} ` +
212
- `code ${MAGENTA}${this.coapCodeToString(msg.code)}${nf} messageId ${MAGENTA}${msg.messageId}${nf} ` +
213
- `tokenLength ${MAGENTA}${msg.tokenLength}${nf} token ${MAGENTA}${msg.tokenLength == 0 ? 'no token' : msg.token.toString('hex')}${nf} ` +
214
- `options ${MAGENTA}${msg.options.length}${nf} payload ${MAGENTA}${msg.payload ? msg.payload.length + ' bytes' : undefined}${nf}`);
215
- msg.options.forEach((option) => {
216
- if (option.number === COAP_OPTION_URI_PATH) {
217
- this.log.info(`Option: COAP_OPTION_URI_PATH => ${GREEN}${option.value}${nf}`);
218
- }
219
- else if (option.number === COIOT_OPTION_DEVID) {
220
- const parts = option.value.toString().split('#');
221
- const deviceModel = parts[0];
222
- const deviceMac = parts[1];
223
- const protocolRevision = parts[2];
224
- this.log.info(`Option: COIOT_OPTION_DEVID => ${option.value} => Model: ${GREEN}${deviceModel}${nf}, MAC: ${GREEN}${deviceMac}${nf}, Protocol: ${GREEN}${protocolRevision}${nf}`);
225
- }
226
- else if (option.number === COIOT_OPTION_SERIAL) {
227
- const serial = option.value.readUInt16BE(0);
228
- this.log.info(`Option: COIOT_OPTION_SERIAL => 0x${option.value.toString('hex')} => serial: ${GREEN}${serial}${nf}`);
229
- }
230
- else if (option.number === COIOT_OPTION_VALIDITY) {
231
- const validity = option.value.readUInt16BE(0);
232
- let validFor = 0;
233
- if ((validity & 0x1) === 0) {
234
- validFor = Math.floor(validity / 10);
235
- }
236
- else {
237
- validFor = validity * 4;
238
- }
239
- this.log.info(`Option: COIOT_OPTION_VALIDITY => 0x${option.value.toString('hex')} => valid for: ${GREEN}${validFor}${nf} seconds`);
240
- }
241
- else
242
- this.log.info(`Option: ${option.number} - ${option.value
243
- .toString('hex')
244
- .match(/.{1,2}/g)
245
- ?.join(' ')}`);
246
- });
247
- if (msg.payload && msg.payload.length > 0)
248
- this.log.info(`Payload:`, JSON.parse(msg.payload.toString()), '\n');
249
- else
250
- this.log.info(`No payload`, '\n');
251
- }
252
- }
@@ -1,45 +0,0 @@
1
- import dgram from 'node:dgram';
2
- import EventEmitter from 'node:events';
3
- import { AddressInfo } from 'node:net';
4
- import { AnsiLogger } from 'node-ansi-logger';
5
- export interface DgramEvents {
6
- error: [error: Error];
7
- close: [];
8
- connect: [];
9
- message: [msg: Buffer, rinfo: dgram.RemoteInfo];
10
- listening: [address: AddressInfo];
11
- sent: [msg: Buffer, serverAddress: string, serverPort: number];
12
- ready: [address: AddressInfo];
13
- bound: [address: AddressInfo];
14
- }
15
- export declare class Dgram extends EventEmitter<DgramEvents> {
16
- verbose: boolean;
17
- debug: boolean;
18
- silent: boolean;
19
- log: AnsiLogger;
20
- socket: dgram.Socket;
21
- bound: boolean;
22
- socketType: 'udp4' | 'udp6';
23
- interfaceName?: string;
24
- interfaceAddress?: string;
25
- interfaceNetmask?: string;
26
- excludedInterfaceNamePattern: RegExp;
27
- constructor(name: string, socketType: 'udp4' | 'udp6', reuseAddr?: boolean | undefined, interfaceName?: string, interfaceAddress?: string);
28
- send(msg: Buffer, serverAddress: string, serverPort: number): void;
29
- onError(error: Error): void;
30
- onClose(): void;
31
- onConnect(): void;
32
- onSent(msg: Buffer, serverAddress: string, serverPort: number): void;
33
- onMessage(msg: Buffer, rinfo: dgram.RemoteInfo): void;
34
- onListening(address: AddressInfo): void;
35
- onReady(address: AddressInfo): void;
36
- getIpv4InterfaceAddress(networkInterface?: string): string | undefined;
37
- getIpv6InterfaceAddress(networkInterface?: string): string | undefined;
38
- getInterfacesNames(): string[];
39
- getIpv6ScopeId(interfaceName?: string): string;
40
- getInterfaceNameFromScopeId(scopeId: number): string | undefined;
41
- getNetmask(interfaceAddress: string): string | undefined;
42
- getIpv4BroadcastAddress(ipAddress: string | undefined, netmask: string | undefined): string | undefined;
43
- getIpv6BroadcastAddress(): string;
44
- listNetworkInterfaces(): void;
45
- }
@@ -1,251 +0,0 @@
1
- import dgram from 'node:dgram';
2
- import EventEmitter from 'node:events';
3
- import os from 'node:os';
4
- import { AnsiLogger, BLUE, db, idn, nf, rs } from 'node-ansi-logger';
5
- import { hasParameter } from '../utils/commandLine.js';
6
- export class Dgram extends EventEmitter {
7
- verbose = hasParameter('v') || hasParameter('verbose');
8
- debug = hasParameter('d') || hasParameter('debug') || hasParameter('v') || hasParameter('verbose');
9
- silent = hasParameter('s') || hasParameter('silent');
10
- log;
11
- socket;
12
- bound = false;
13
- socketType;
14
- interfaceName;
15
- interfaceAddress;
16
- interfaceNetmask;
17
- excludedInterfaceNamePattern = /(tailscale|wireguard|wintun|openvpn|\bwg\d*\b|\btun\d*\b|\btap\d*\b|\butun\d*\b|zerotier|hamachi|hyper-?v|v\s*ethernet|wsl|default switch|vmware|vmnet|vbox|virtualbox|virbr|docker|podman|\bveth\b|\bbr-\b|cni|kube|flannel|calico|teredo|isatap)/i;
18
- constructor(name, socketType, reuseAddr = true, interfaceName, interfaceAddress) {
19
- super();
20
- this.log = new AnsiLogger({ logName: name, logTimestampFormat: 4, logLevel: this.debug ? "debug" : this.silent ? "notice" : "info" });
21
- this.socket = dgram.createSocket({ type: socketType, reuseAddr });
22
- this.socketType = socketType;
23
- this.interfaceName = interfaceName;
24
- this.interfaceAddress = interfaceAddress;
25
- this.log.debug(`Created socket of type ${BLUE}${socketType}${db} on interface ${BLUE}${interfaceName || 'any'}${db} with address ${BLUE}${interfaceAddress || 'any'}${db} reuseAddr=${BLUE}${reuseAddr}${db}`);
26
- this.socket.on('error', (error) => {
27
- this.log.debug(`Socket error: ${error instanceof Error ? error.message : error}`);
28
- this.emit('error', error);
29
- this.onError(error);
30
- });
31
- this.socket.on('close', () => {
32
- this.log.debug('Socket closed');
33
- this.bound = false;
34
- this.emit('close');
35
- this.onClose();
36
- });
37
- this.socket.on('connect', () => {
38
- this.log.info('Socket connected');
39
- this.emit('connect');
40
- this.onConnect();
41
- });
42
- this.socket.on('message', (msg, rinfo) => {
43
- this.log.debug(`Socket received a message from ${BLUE}${rinfo.family}${db} ${BLUE}${rinfo.address}${db}:${BLUE}${rinfo.port}${db}`);
44
- this.emit('message', msg, rinfo);
45
- this.onMessage(msg, rinfo);
46
- });
47
- this.socket.on('listening', () => {
48
- this.bound = true;
49
- const address = this.socket.address();
50
- this.log.debug(`Socket listening on ${BLUE}${address.family}${db} ${BLUE}${address.address}${db}:${BLUE}${address.port}${db}`);
51
- this.emit('listening', address);
52
- this.onListening(address);
53
- });
54
- }
55
- send(msg, serverAddress, serverPort) {
56
- this.socket.send(msg, 0, msg.length, serverPort, serverAddress, (error) => {
57
- if (error) {
58
- this.log.error(`Socket failed to send a message: ${error instanceof Error ? error.message : error}`);
59
- this.emit('error', error);
60
- this.onError(error);
61
- }
62
- else {
63
- this.log.debug(`Socket sent a message to ${BLUE}${serverAddress}${db}:${BLUE}${serverPort}${db}`);
64
- this.emit('sent', msg, serverAddress, serverPort);
65
- this.onSent(msg, serverAddress, serverPort);
66
- }
67
- });
68
- }
69
- onError(error) {
70
- this.log.error(`Socket error: ${error instanceof Error ? error.message : error}`);
71
- }
72
- onClose() {
73
- this.log.info(`Socket closed`);
74
- }
75
- onConnect() {
76
- this.log.info(`Socket connected`);
77
- }
78
- onSent(msg, serverAddress, serverPort) {
79
- this.log.info(`Socket sent a message to ${BLUE}${serverAddress}${db}:${BLUE}${serverPort}${db}`);
80
- }
81
- onMessage(msg, rinfo) {
82
- this.log.info(`Socket received a message from ${BLUE}${rinfo.family}${nf} ${BLUE}${rinfo.address}${nf}:${BLUE}${rinfo.port}${nf}`);
83
- }
84
- onListening(address) {
85
- this.log.info(`Socket listening on ${BLUE}${address.family}${nf} ${BLUE}${address.address}${nf}:${BLUE}${address.port}${nf}`);
86
- this.onReady(address);
87
- }
88
- onReady(address) {
89
- this.log.info(`Socket ready on ${BLUE}${address.family}${nf} ${BLUE}${address.address}${nf}:${BLUE}${address.port}${nf}`);
90
- this.emit('ready', address);
91
- }
92
- getIpv4InterfaceAddress(networkInterface) {
93
- if (networkInterface === '')
94
- networkInterface = undefined;
95
- const interfaces = os.networkInterfaces();
96
- if (networkInterface && !interfaces[networkInterface]) {
97
- this.log.warn(`Interface "${networkInterface}" not found. Using first external IPv4 interface.`);
98
- networkInterface = undefined;
99
- }
100
- if (!networkInterface) {
101
- for (const [interfaceName, interfaceDetails] of Object.entries(interfaces)) {
102
- if (!networkInterface && this.excludedInterfaceNamePattern.test(interfaceName))
103
- continue;
104
- if (!interfaceDetails)
105
- continue;
106
- for (const detail of interfaceDetails) {
107
- if (detail.family === 'IPv4' && !detail.internal) {
108
- networkInterface = interfaceName;
109
- break;
110
- }
111
- }
112
- if (networkInterface)
113
- break;
114
- }
115
- }
116
- if (!networkInterface) {
117
- throw new Error(`Didn't find an external IPv4 network interface`);
118
- }
119
- const addresses = interfaces[networkInterface];
120
- const ipv4Address = addresses?.find((addr) => addr.family === 'IPv4' && !addr.internal);
121
- if (!ipv4Address) {
122
- throw new Error(`Interface ${networkInterface} does not have an external IPv4 address`);
123
- }
124
- return ipv4Address.address;
125
- }
126
- getIpv6InterfaceAddress(networkInterface) {
127
- if (networkInterface === '')
128
- networkInterface = undefined;
129
- const interfaces = os.networkInterfaces();
130
- if (networkInterface && !interfaces[networkInterface]) {
131
- this.log.warn(`Interface "${networkInterface}" not found. Using first external IPv6 interface.`);
132
- networkInterface = undefined;
133
- }
134
- if (!networkInterface) {
135
- for (const [interfaceName, interfaceDetails] of Object.entries(interfaces)) {
136
- if (!networkInterface && this.excludedInterfaceNamePattern.test(interfaceName))
137
- continue;
138
- if (!interfaceDetails)
139
- continue;
140
- for (const detail of interfaceDetails) {
141
- if (detail.family === 'IPv6' && !detail.internal) {
142
- networkInterface = interfaceName;
143
- break;
144
- }
145
- }
146
- if (networkInterface)
147
- break;
148
- }
149
- }
150
- if (!networkInterface) {
151
- throw new Error(`Didn't find an external IPv6 network interface`);
152
- }
153
- const addresses = interfaces[networkInterface];
154
- const linkLocalAddress = addresses?.find((addr) => addr.family === 'IPv6' && !addr.internal && addr.address.startsWith('fe80'));
155
- if (linkLocalAddress) {
156
- this.log.debug('Found IPv6 link-local address');
157
- return linkLocalAddress.scopeid ? `${linkLocalAddress.address}%${process.platform !== 'win32' ? networkInterface : linkLocalAddress.scopeid}` : linkLocalAddress.address;
158
- }
159
- this.log.debug('No IPv6 link-local address found');
160
- const ulaAddress = addresses?.find((addr) => addr.family === 'IPv6' && !addr.internal && addr.address.startsWith('fd') && addr.netmask === 'ffff:ffff:ffff:ffff::');
161
- if (ulaAddress) {
162
- this.log.debug('Found IPv6 Unique Local Addresses (ULA) unicast address');
163
- return ulaAddress.address;
164
- }
165
- this.log.debug('No IPv6 Unique Local Addresses (ULA) unicast address found');
166
- const uniqueLocalAddress = addresses?.find((addr) => addr.family === 'IPv6' && !addr.internal && addr.address.startsWith('fd'));
167
- if (uniqueLocalAddress) {
168
- this.log.debug('Found IPv6 Unique Local Addresses (ULA) address');
169
- return uniqueLocalAddress.address;
170
- }
171
- this.log.debug('No IPv6 Unique Local Addresses (ULA) address found');
172
- throw new Error(`Interface ${networkInterface} does not have a suitable external IPv6 address`);
173
- }
174
- getInterfacesNames() {
175
- const interfaces = os.networkInterfaces();
176
- const interfaceNames = [];
177
- for (const name in interfaces) {
178
- if (interfaces[name]) {
179
- interfaceNames.push(name);
180
- }
181
- }
182
- return interfaceNames;
183
- }
184
- getIpv6ScopeId(interfaceName) {
185
- const interfaces = os.networkInterfaces();
186
- for (const name in interfaces) {
187
- if (interfaceName && name !== interfaceName)
188
- continue;
189
- if (!interfaceName && this.excludedInterfaceNamePattern.test(name))
190
- continue;
191
- const iface = interfaces[name];
192
- if (iface) {
193
- const ipv6Address = iface.find((addr) => addr.family === 'IPv6' && !addr.internal && addr.scopeid);
194
- if (ipv6Address) {
195
- return process.platform === 'win32' ? '%' + String(ipv6Address.scopeid) : '%' + name;
196
- }
197
- }
198
- }
199
- return '';
200
- }
201
- getInterfaceNameFromScopeId(scopeId) {
202
- const nets = os.networkInterfaces();
203
- for (const ifaceName in nets) {
204
- const addresses = nets[ifaceName] || [];
205
- for (const addr of addresses) {
206
- if (addr.family === 'IPv6' && addr.scopeid === scopeId) {
207
- return ifaceName;
208
- }
209
- }
210
- }
211
- return undefined;
212
- }
213
- getNetmask(interfaceAddress) {
214
- const noZoneAddress = interfaceAddress.includes('%') ? interfaceAddress.split('%')[0] : interfaceAddress;
215
- const nets = os.networkInterfaces();
216
- for (const ifaceName in nets) {
217
- const ifaceAddresses = nets[ifaceName];
218
- if (!ifaceAddresses)
219
- continue;
220
- for (const addr of ifaceAddresses) {
221
- if (addr.address === noZoneAddress) {
222
- return addr.netmask;
223
- }
224
- }
225
- }
226
- return undefined;
227
- }
228
- getIpv4BroadcastAddress(ipAddress, netmask) {
229
- if (!ipAddress || !netmask) {
230
- return undefined;
231
- }
232
- const ipParts = ipAddress.split('.').map(Number);
233
- const maskParts = netmask.split('.').map(Number);
234
- const broadcastParts = ipParts.map((octet, i) => (octet & maskParts[i]) | (255 - maskParts[i]));
235
- return broadcastParts.join('.');
236
- }
237
- getIpv6BroadcastAddress() {
238
- return 'ff02::1';
239
- }
240
- listNetworkInterfaces() {
241
- const interfaces = os.networkInterfaces();
242
- for (const [name, addresses] of Object.entries(interfaces)) {
243
- if (!addresses)
244
- continue;
245
- this.log.debug(`Interface: ${idn}${name}${rs}${db}`);
246
- for (const address of addresses) {
247
- this.log.debug(`- address ${BLUE}${address.address}${db} netmask ${BLUE}${address.netmask}${db} ${address.mac ? 'MAC: ' + BLUE + address.mac + db : ''} type: ${BLUE}${address.family}${db} ${BLUE}${address.internal ? 'internal' : 'external'}${db} ${address.scopeid !== undefined ? 'scopeid: ' + BLUE + address.scopeid + db : ''} cidr: ${BLUE}${address.cidr}${db}`);
248
- }
249
- }
250
- }
251
- }