diodejs 0.4.0 → 0.4.2
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/README.md +185 -68
- package/clientManager.js +1679 -78
- package/connection.js +24 -3
- package/examples/RPCTest.js +11 -1
- package/examples/fleetContractExample.js +45 -0
- package/examples/publishPortTest.js +3 -2
- package/networkDiscoveryClient.js +89 -0
- package/package.json +4 -3
- package/publishPort.js +50 -27
- package/scripts/benchmark-relay-selection.js +476 -0
- package/test/clientManager.relaySelection.test.js +1454 -0
- package/test/fixtures/dio-network-snapshot.json +82 -0
- package/test/fleetContract.test.js +71 -0
- package/test/publishPort.test.js +399 -0
- package/utils.js +26 -0
package/connection.js
CHANGED
|
@@ -3,7 +3,19 @@ const tls = require('tls');
|
|
|
3
3
|
const fs = require('fs');
|
|
4
4
|
const { RLP } = require('@ethereumjs/rlp');
|
|
5
5
|
const EventEmitter = require('events');
|
|
6
|
-
const {
|
|
6
|
+
const {
|
|
7
|
+
makeReadable,
|
|
8
|
+
parseRequestId,
|
|
9
|
+
parseResponseType,
|
|
10
|
+
parseReason,
|
|
11
|
+
parseUInt,
|
|
12
|
+
generateCert,
|
|
13
|
+
ensureDirectoryExistence,
|
|
14
|
+
loadOrGenerateKeyPair,
|
|
15
|
+
toBufferView,
|
|
16
|
+
DEFAULT_FLEET_CONTRACT,
|
|
17
|
+
normalizeFleetContractAddress,
|
|
18
|
+
} = require('./utils');
|
|
7
19
|
const { Buffer } = require('buffer'); // Import Buffer
|
|
8
20
|
const asn1 = require('asn1.js');
|
|
9
21
|
const secp256k1 = require('secp256k1');
|
|
@@ -48,6 +60,8 @@ class DiodeConnection extends EventEmitter {
|
|
|
48
60
|
this.certPem = null;
|
|
49
61
|
this._serverEthAddress = null; // cache after first read
|
|
50
62
|
this.localAddressProvider = null;
|
|
63
|
+
this.fleetContractHex = DEFAULT_FLEET_CONTRACT;
|
|
64
|
+
this.fleetContract = Buffer.from(DEFAULT_FLEET_CONTRACT.slice(2), 'hex');
|
|
51
65
|
// Load or generate keypair
|
|
52
66
|
this.keyPair = loadOrGenerateKeyPair(this.keyLocation);
|
|
53
67
|
|
|
@@ -283,6 +297,13 @@ class DiodeConnection extends EventEmitter {
|
|
|
283
297
|
return this;
|
|
284
298
|
}
|
|
285
299
|
|
|
300
|
+
setFleetContract(fleetContract) {
|
|
301
|
+
const normalizedFleetContract = normalizeFleetContractAddress(fleetContract);
|
|
302
|
+
this.fleetContractHex = normalizedFleetContract;
|
|
303
|
+
this.fleetContract = Buffer.from(normalizedFleetContract.slice(2), 'hex');
|
|
304
|
+
return this;
|
|
305
|
+
}
|
|
306
|
+
|
|
286
307
|
// Update close method to prevent reconnection when intentionally closing
|
|
287
308
|
close() {
|
|
288
309
|
if (this.ticketUpdateTimer) {
|
|
@@ -592,7 +613,7 @@ class DiodeConnection extends EventEmitter {
|
|
|
592
613
|
|
|
593
614
|
async createTicketSignature(serverIdBuffer, totalConnections, totalBytes, localAddress, epoch) {
|
|
594
615
|
const chainId = 1284;
|
|
595
|
-
const fleetContractBuffer =
|
|
616
|
+
const fleetContractBuffer = this.fleetContract;
|
|
596
617
|
|
|
597
618
|
const localAddressBytes = Buffer.isBuffer(localAddress) || localAddress instanceof Uint8Array
|
|
598
619
|
? toBufferView(localAddress)
|
|
@@ -637,7 +658,7 @@ class DiodeConnection extends EventEmitter {
|
|
|
637
658
|
|
|
638
659
|
async createTicketCommand() {
|
|
639
660
|
const chainId = 1284;
|
|
640
|
-
const fleetContract =
|
|
661
|
+
const fleetContract = this.fleetContract;
|
|
641
662
|
let localAddress = '';
|
|
642
663
|
if (typeof this.localAddressProvider === 'function') {
|
|
643
664
|
try {
|
package/examples/RPCTest.js
CHANGED
|
@@ -3,8 +3,13 @@ const { makeReadable } = require('../utils');
|
|
|
3
3
|
|
|
4
4
|
async function main() {
|
|
5
5
|
const keyLocation = './db/keys.json';
|
|
6
|
+
const fleetContract = process.env.DIODE_FLEET_CONTRACT;
|
|
7
|
+
const nextFleetContract = process.env.DIODE_NEXT_FLEET_CONTRACT;
|
|
6
8
|
|
|
7
|
-
const client = new DiodeClientManager({
|
|
9
|
+
const client = new DiodeClientManager({
|
|
10
|
+
keyLocation,
|
|
11
|
+
...(fleetContract ? { fleetContract } : {}),
|
|
12
|
+
});
|
|
8
13
|
await client.connect();
|
|
9
14
|
const [connection] = client.getConnections();
|
|
10
15
|
if (!connection) {
|
|
@@ -15,6 +20,11 @@ async function main() {
|
|
|
15
20
|
try {
|
|
16
21
|
const address = connection.getEthereumAddress();
|
|
17
22
|
console.log('Address:', address);
|
|
23
|
+
console.log('Current fleet contract:', connection.fleetContractHex);
|
|
24
|
+
if (nextFleetContract) {
|
|
25
|
+
client.setFleetContract(nextFleetContract);
|
|
26
|
+
console.log('Updated fleet contract for future tickets:', connection.fleetContractHex);
|
|
27
|
+
}
|
|
18
28
|
const ping = await rpc.ping();
|
|
19
29
|
console.log('Ping:', ping);
|
|
20
30
|
const blockPeak = await rpc.getBlockPeak();
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
const { DiodeClientManager } = require('../index');
|
|
2
|
+
|
|
3
|
+
const keyLocation = './db/keys.json';
|
|
4
|
+
const initialFleetContract = process.env.DIODE_FLEET_CONTRACT || '0x1111111111111111111111111111111111111111';
|
|
5
|
+
const nextFleetContract = process.env.DIODE_NEXT_FLEET_CONTRACT || '0x2222222222222222222222222222222222222222';
|
|
6
|
+
const shouldConnect = process.env.DIODE_CONNECT === 'true';
|
|
7
|
+
|
|
8
|
+
async function main() {
|
|
9
|
+
const client = new DiodeClientManager({
|
|
10
|
+
keyLocation,
|
|
11
|
+
fleetContract: initialFleetContract,
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
console.log('Initial manager fleet contract:', client.fleetContract);
|
|
15
|
+
|
|
16
|
+
if (!shouldConnect) {
|
|
17
|
+
client.setFleetContract(nextFleetContract);
|
|
18
|
+
console.log('Updated manager fleet contract:', client.fleetContract);
|
|
19
|
+
console.log('Set DIODE_CONNECT=true to connect and observe the updated contract on active relay connections.');
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
try {
|
|
24
|
+
await client.connect();
|
|
25
|
+
console.log(`Connected relay count: ${client.getConnections().length}`);
|
|
26
|
+
for (const connection of client.getConnections()) {
|
|
27
|
+
console.log(`Before update ${connection.host}:${connection.port} fleet contract: ${connection.fleetContractHex}`);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
client.setFleetContract(nextFleetContract);
|
|
31
|
+
console.log('Updated manager fleet contract:', client.fleetContract);
|
|
32
|
+
for (const connection of client.getConnections()) {
|
|
33
|
+
console.log(`After update ${connection.host}:${connection.port} fleet contract: ${connection.fleetContractHex}`);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
console.log('The new fleet contract will be used on the next ticket generated by each connection.');
|
|
37
|
+
} finally {
|
|
38
|
+
client.close();
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
main().catch((error) => {
|
|
43
|
+
console.error('Fleet contract example failed:', error);
|
|
44
|
+
process.exitCode = 1;
|
|
45
|
+
});
|
|
@@ -10,9 +10,10 @@ async function startPublishing() {
|
|
|
10
10
|
|
|
11
11
|
// Create a PublishPort instance with initial ports
|
|
12
12
|
const publishPort = new PublishPort(client, {
|
|
13
|
-
3000: { mode: 'public' },
|
|
13
|
+
3000: { mode: 'public', host: '192.168.1.10' },
|
|
14
14
|
8080: {
|
|
15
15
|
mode: 'private',
|
|
16
|
+
host: 'backend.internal',
|
|
16
17
|
whitelist: ['0xca1e71d8105a598810578fb6042fa8cbc1e7f039'] // Replace with actual addresses
|
|
17
18
|
}
|
|
18
19
|
}, keyLocation);
|
|
@@ -29,7 +30,7 @@ async function startPublishing() {
|
|
|
29
30
|
// After 15 seconds, add multiple ports
|
|
30
31
|
setTimeout(() => {
|
|
31
32
|
console.log("Adding multiple ports");
|
|
32
|
-
publishPort.addPort(3000,{ mode: 'public' });
|
|
33
|
+
publishPort.addPort(3000,{ mode: 'public', host: '127.0.0.1' });
|
|
33
34
|
console.log('Updated published ports:', publishPort.getPublishedPorts());
|
|
34
35
|
}, 15000);
|
|
35
36
|
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
const WebSocket = require('ws');
|
|
2
|
+
|
|
3
|
+
function fetchNetworkDirectory(options = {}) {
|
|
4
|
+
const endpoint = typeof options.endpoint === 'string' && options.endpoint.trim()
|
|
5
|
+
? options.endpoint
|
|
6
|
+
: 'wss://prenet.diode.io:8443/ws';
|
|
7
|
+
const method = typeof options.method === 'string' && options.method.trim()
|
|
8
|
+
? options.method
|
|
9
|
+
: 'dio_network';
|
|
10
|
+
const timeoutMs = Number.isFinite(options.timeoutMs) && options.timeoutMs > 0
|
|
11
|
+
? Math.floor(options.timeoutMs)
|
|
12
|
+
: 1500;
|
|
13
|
+
|
|
14
|
+
return new Promise((resolve, reject) => {
|
|
15
|
+
let settled = false;
|
|
16
|
+
let socket = null;
|
|
17
|
+
let timer = null;
|
|
18
|
+
|
|
19
|
+
const cleanup = () => {
|
|
20
|
+
if (timer) {
|
|
21
|
+
clearTimeout(timer);
|
|
22
|
+
timer = null;
|
|
23
|
+
}
|
|
24
|
+
if (socket) {
|
|
25
|
+
socket.removeAllListeners();
|
|
26
|
+
try {
|
|
27
|
+
socket.close();
|
|
28
|
+
} catch (_) {}
|
|
29
|
+
socket = null;
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const finish = (fn, value) => {
|
|
34
|
+
if (settled) {
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
settled = true;
|
|
38
|
+
cleanup();
|
|
39
|
+
fn(value);
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
socket = new WebSocket(endpoint, {
|
|
43
|
+
handshakeTimeout: timeoutMs,
|
|
44
|
+
origin: 'https://diode.io',
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
socket.on('open', () => {
|
|
48
|
+
socket.send(JSON.stringify({
|
|
49
|
+
jsonrpc: '2.0',
|
|
50
|
+
id: 1,
|
|
51
|
+
method,
|
|
52
|
+
params: [],
|
|
53
|
+
}));
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
socket.on('message', (raw) => {
|
|
57
|
+
try {
|
|
58
|
+
const parsed = JSON.parse(typeof raw === 'string' ? raw : raw.toString('utf8'));
|
|
59
|
+
if (parsed && parsed.id === 1) {
|
|
60
|
+
if (parsed.error) {
|
|
61
|
+
finish(reject, new Error(parsed.error.message || 'Network discovery request failed'));
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
finish(resolve, Array.isArray(parsed.result) ? parsed.result : []);
|
|
65
|
+
}
|
|
66
|
+
} catch (error) {
|
|
67
|
+
finish(reject, error);
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
socket.on('error', (error) => {
|
|
72
|
+
finish(reject, error);
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
socket.on('close', () => {
|
|
76
|
+
if (!settled) {
|
|
77
|
+
finish(reject, new Error('Network discovery socket closed before a response was received'));
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
timer = setTimeout(() => {
|
|
82
|
+
finish(reject, new Error(`Network discovery timed out after ${timeoutMs}ms`));
|
|
83
|
+
}, timeoutMs);
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
module.exports = {
|
|
88
|
+
fetchNetworkDirectory,
|
|
89
|
+
};
|
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "diodejs",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.2",
|
|
4
4
|
"description": "A JavaScript client for interacting with the Diode network. It provides functionalities to bind and publish ports, send RPC commands, and handle responses.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
7
|
-
"test": "
|
|
7
|
+
"test": "node --test"
|
|
8
8
|
},
|
|
9
9
|
"author": "",
|
|
10
10
|
"license": "ISC",
|
|
@@ -20,7 +20,8 @@
|
|
|
20
20
|
"keccak": "^3.0.4",
|
|
21
21
|
"node-fetch": "^2.7.0",
|
|
22
22
|
"rlp": "^3.0.0",
|
|
23
|
-
"secp256k1": "^5.0.1"
|
|
23
|
+
"secp256k1": "^5.0.1",
|
|
24
|
+
"ws": "^8.19.0"
|
|
24
25
|
},
|
|
25
26
|
"engines": {
|
|
26
27
|
"node": ">=18"
|
package/publishPort.js
CHANGED
|
@@ -38,6 +38,25 @@ function normalizeDeviceId(raw) {
|
|
|
38
38
|
return '';
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
+
function normalizePublishedPortConfig(config = {}) {
|
|
42
|
+
const hasHost = Object.prototype.hasOwnProperty.call(config, 'host');
|
|
43
|
+
const rawHost = hasHost ? config.host : '127.0.0.1';
|
|
44
|
+
if (typeof rawHost !== 'string') {
|
|
45
|
+
throw new TypeError('PublishPort config.host must be a non-empty string');
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const host = rawHost.trim();
|
|
49
|
+
if (!host) {
|
|
50
|
+
throw new TypeError('PublishPort config.host must be a non-empty string');
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return {
|
|
54
|
+
mode: config.mode || 'public',
|
|
55
|
+
whitelist: Array.isArray(config.whitelist) ? config.whitelist : [],
|
|
56
|
+
host,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
|
|
41
60
|
class DiodeSocket extends Duplex {
|
|
42
61
|
constructor(ref, rpc) {
|
|
43
62
|
super({ readableHighWaterMark: 256 * 1024, writableHighWaterMark: 256 * 1024, allowHalfOpen: false });
|
|
@@ -93,14 +112,11 @@ class PublishPort extends EventEmitter {
|
|
|
93
112
|
const portNum = parseInt(port, 10);
|
|
94
113
|
|
|
95
114
|
// Normalize the configuration
|
|
96
|
-
const portConfig =
|
|
97
|
-
mode: config.mode || 'public',
|
|
98
|
-
whitelist: Array.isArray(config.whitelist) ? config.whitelist : []
|
|
99
|
-
};
|
|
115
|
+
const portConfig = normalizePublishedPortConfig(config);
|
|
100
116
|
|
|
101
117
|
// Add to map
|
|
102
118
|
this.publishedPorts.set(portNum, portConfig);
|
|
103
|
-
logger.info(() => `Added published port ${portNum} with mode: ${portConfig.mode}`);
|
|
119
|
+
logger.info(() => `Added published port ${portNum} with mode: ${portConfig.mode}, host: ${portConfig.host}`);
|
|
104
120
|
|
|
105
121
|
return true;
|
|
106
122
|
}
|
|
@@ -186,6 +202,10 @@ class PublishPort extends EventEmitter {
|
|
|
186
202
|
return rpc;
|
|
187
203
|
}
|
|
188
204
|
|
|
205
|
+
_getPublishedPortConfig(port) {
|
|
206
|
+
return this.publishedPorts.get(port);
|
|
207
|
+
}
|
|
208
|
+
|
|
189
209
|
startListening() {
|
|
190
210
|
if (this._listening) return this; // idempotent
|
|
191
211
|
// Listen for unsolicited messages from the connection
|
|
@@ -269,7 +289,7 @@ class PublishPort extends EventEmitter {
|
|
|
269
289
|
}
|
|
270
290
|
|
|
271
291
|
// Get port configuration and check whitelist if in private mode
|
|
272
|
-
const portConfig = this.
|
|
292
|
+
const portConfig = this._getPublishedPortConfig(port);
|
|
273
293
|
if (portConfig.mode === 'private' && Array.isArray(portConfig.whitelist)) {
|
|
274
294
|
if (!portConfig.whitelist.includes(deviceId)) {
|
|
275
295
|
logger.warn(() => `Device ${deviceId} is not whitelisted for port ${port}. Rejecting request.`);
|
|
@@ -281,15 +301,15 @@ class PublishPort extends EventEmitter {
|
|
|
281
301
|
|
|
282
302
|
// Handle based on protocol
|
|
283
303
|
if (protocol === 'tcp') {
|
|
284
|
-
this.handleTCPConnection(sessionId, ref, port, deviceId, connection);
|
|
304
|
+
this.handleTCPConnection(sessionId, ref, port, deviceId, portConfig, connection);
|
|
285
305
|
} else if (protocol === 'tls') {
|
|
286
306
|
if (isHandshake) {
|
|
287
307
|
this.handleTLSHandshake(sessionId, ref, port, deviceId, connection);
|
|
288
308
|
} else {
|
|
289
|
-
this.handleTLSConnection(sessionId, ref, port, deviceId, connection);
|
|
309
|
+
this.handleTLSConnection(sessionId, ref, port, deviceId, portConfig, connection);
|
|
290
310
|
}
|
|
291
311
|
} else if (protocol === 'udp') {
|
|
292
|
-
this.handleUDPConnection(sessionId, ref, port, deviceId, connection);
|
|
312
|
+
this.handleUDPConnection(sessionId, ref, port, deviceId, portConfig, connection);
|
|
293
313
|
} else {
|
|
294
314
|
logger.warn(() => `Unsupported protocol: ${protocol}`);
|
|
295
315
|
rpc.sendError(sessionId, ref, `Unsupported protocol: ${protocol}`);
|
|
@@ -449,7 +469,7 @@ class PublishPort extends EventEmitter {
|
|
|
449
469
|
}
|
|
450
470
|
|
|
451
471
|
// Get port configuration and check whitelist if in private mode
|
|
452
|
-
const portConfig = this.
|
|
472
|
+
const portConfig = this._getPublishedPortConfig(port);
|
|
453
473
|
if (portConfig.mode === 'private' && Array.isArray(portConfig.whitelist)) {
|
|
454
474
|
if (!portConfig.whitelist.includes(deviceId)) {
|
|
455
475
|
logger.warn(() => `Device ${deviceId} is not whitelisted for port ${port}. Rejecting request.`);
|
|
@@ -473,6 +493,7 @@ class PublishPort extends EventEmitter {
|
|
|
473
493
|
const session = {
|
|
474
494
|
physicalPort,
|
|
475
495
|
port,
|
|
496
|
+
host: portConfig.host,
|
|
476
497
|
protocol,
|
|
477
498
|
deviceId: deviceId.toLowerCase(),
|
|
478
499
|
connection,
|
|
@@ -527,7 +548,7 @@ class PublishPort extends EventEmitter {
|
|
|
527
548
|
|
|
528
549
|
handleNativeTCPRelay(sessionId, physicalPortRef, session, connection) {
|
|
529
550
|
const rpc = this._getRpcFor(connection);
|
|
530
|
-
const { physicalPort, port, deviceId } = session;
|
|
551
|
+
const { physicalPort, port, host, deviceId } = session;
|
|
531
552
|
let responded = false;
|
|
532
553
|
const sendOk = () => {
|
|
533
554
|
if (responded) return;
|
|
@@ -544,7 +565,7 @@ class PublishPort extends EventEmitter {
|
|
|
544
565
|
const relaySocket = net.connect({ host: relayHost, port: physicalPort }, () => {
|
|
545
566
|
relaySocket.setNoDelay(true);
|
|
546
567
|
});
|
|
547
|
-
const localSocket = net.connect({ port }, () => {
|
|
568
|
+
const localSocket = net.connect({ port, host }, () => {
|
|
548
569
|
localSocket.setNoDelay(true);
|
|
549
570
|
});
|
|
550
571
|
localSocket.pause();
|
|
@@ -616,7 +637,7 @@ class PublishPort extends EventEmitter {
|
|
|
616
637
|
|
|
617
638
|
handleNativeUDPRelay(sessionId, physicalPortRef, session, connection) {
|
|
618
639
|
const rpc = this._getRpcFor(connection);
|
|
619
|
-
const { physicalPort, port, deviceId } = session;
|
|
640
|
+
const { physicalPort, port, host, deviceId } = session;
|
|
620
641
|
let responded = false;
|
|
621
642
|
const sendOk = () => {
|
|
622
643
|
if (responded) return;
|
|
@@ -672,7 +693,7 @@ class PublishPort extends EventEmitter {
|
|
|
672
693
|
relayReady = true;
|
|
673
694
|
maybeReady();
|
|
674
695
|
});
|
|
675
|
-
localSocket.connect(port,
|
|
696
|
+
localSocket.connect(port, host, () => {
|
|
676
697
|
localReady = true;
|
|
677
698
|
maybeReady();
|
|
678
699
|
});
|
|
@@ -706,12 +727,12 @@ class PublishPort extends EventEmitter {
|
|
|
706
727
|
}
|
|
707
728
|
}
|
|
708
729
|
|
|
709
|
-
handleTCPConnection(sessionId, ref, port, deviceId, connection) {
|
|
730
|
+
handleTCPConnection(sessionId, ref, port, deviceId, portConfig, connection) {
|
|
710
731
|
const rpc = this._getRpcFor(connection);
|
|
711
732
|
// Create a TCP connection to the local service on the specified port
|
|
712
|
-
const localSocket = net.connect({ port:
|
|
733
|
+
const localSocket = net.connect({ port, host: portConfig.host }, () => {
|
|
713
734
|
localSocket.setNoDelay(true);
|
|
714
|
-
logger.info(() => `Connected to local TCP service on
|
|
735
|
+
logger.info(() => `Connected to local TCP service on ${portConfig.host}:${port}`);
|
|
715
736
|
// Send success response
|
|
716
737
|
rpc.sendResponse(sessionId, ref, 'ok');
|
|
717
738
|
});
|
|
@@ -720,10 +741,10 @@ class PublishPort extends EventEmitter {
|
|
|
720
741
|
this.setupLocalSocketHandlers(localSocket, ref, 'tcp', rpc, connection);
|
|
721
742
|
|
|
722
743
|
// Store the local socket with the ref using connection's method
|
|
723
|
-
connection.addConnection(ref, { socket: localSocket, protocol: 'tcp', port, deviceId });
|
|
744
|
+
connection.addConnection(ref, { socket: localSocket, protocol: 'tcp', port, host: portConfig.host, deviceId });
|
|
724
745
|
}
|
|
725
746
|
|
|
726
|
-
handleTLSConnection(sessionId, ref, port, deviceId, connection) {
|
|
747
|
+
handleTLSConnection(sessionId, ref, port, deviceId, portConfig, connection) {
|
|
727
748
|
const rpc = this._getRpcFor(connection);
|
|
728
749
|
// Create a DiodeSocket instance
|
|
729
750
|
const diodeSocket = new DiodeSocket(ref, rpc);
|
|
@@ -748,8 +769,8 @@ class PublishPort extends EventEmitter {
|
|
|
748
769
|
});
|
|
749
770
|
tlsSocket.setNoDelay(true);
|
|
750
771
|
// Connect to the local service (TCP or TLS as needed)
|
|
751
|
-
const localSocket = net.connect({ port:
|
|
752
|
-
logger.info(() => `Connected to local TCP service on
|
|
772
|
+
const localSocket = net.connect({ port, host: portConfig.host }, () => {
|
|
773
|
+
logger.info(() => `Connected to local TCP service on ${portConfig.host}:${port}`);
|
|
753
774
|
// Send success response
|
|
754
775
|
rpc.sendResponse(sessionId, ref, 'ok');
|
|
755
776
|
});
|
|
@@ -775,11 +796,12 @@ class PublishPort extends EventEmitter {
|
|
|
775
796
|
localSocket,
|
|
776
797
|
protocol: 'tls',
|
|
777
798
|
port,
|
|
799
|
+
host: portConfig.host,
|
|
778
800
|
deviceId,
|
|
779
801
|
});
|
|
780
802
|
}
|
|
781
803
|
|
|
782
|
-
handleUDPConnection(sessionId, ref, port, deviceId, connection) {
|
|
804
|
+
handleUDPConnection(sessionId, ref, port, deviceId, portConfig, connection) {
|
|
783
805
|
const rpc = this._getRpcFor(connection);
|
|
784
806
|
// Create a UDP socket
|
|
785
807
|
const localSocket = dgram.createSocket('udp4');
|
|
@@ -795,7 +817,7 @@ class PublishPort extends EventEmitter {
|
|
|
795
817
|
});
|
|
796
818
|
|
|
797
819
|
// Store the remote address and port from the Diode client
|
|
798
|
-
const remoteInfo = {port, address:
|
|
820
|
+
const remoteInfo = { port, address: portConfig.host };
|
|
799
821
|
|
|
800
822
|
// Send success response
|
|
801
823
|
rpc.sendResponse(sessionId, ref, 'ok');
|
|
@@ -806,10 +828,11 @@ class PublishPort extends EventEmitter {
|
|
|
806
828
|
protocol: 'udp',
|
|
807
829
|
remoteInfo,
|
|
808
830
|
port,
|
|
831
|
+
host: portConfig.host,
|
|
809
832
|
deviceId
|
|
810
833
|
});
|
|
811
834
|
|
|
812
|
-
logger.info(() => `UDP connection set up
|
|
835
|
+
logger.info(() => `UDP connection set up for ${portConfig.host}:${port}`);
|
|
813
836
|
|
|
814
837
|
// Handle messages from the local UDP service
|
|
815
838
|
localSocket.on('message', (msg, rinfo) => {
|
|
@@ -835,7 +858,7 @@ class PublishPort extends EventEmitter {
|
|
|
835
858
|
const connectionInfo = connection.getConnection(ref);
|
|
836
859
|
// Check if the port is still open and address is still in whitelist
|
|
837
860
|
if (connectionInfo) {
|
|
838
|
-
const { socket: localSocket, protocol, remoteInfo, port, deviceId } = connectionInfo;
|
|
861
|
+
const { socket: localSocket, protocol, remoteInfo, port, host, deviceId } = connectionInfo;
|
|
839
862
|
|
|
840
863
|
if (!this.publishedPorts.has(port)) {
|
|
841
864
|
logger.warn(() => `Port ${port} is not published. Sending portclose.`);
|
|
@@ -844,7 +867,7 @@ class PublishPort extends EventEmitter {
|
|
|
844
867
|
return;
|
|
845
868
|
}
|
|
846
869
|
|
|
847
|
-
const portConfig = this.
|
|
870
|
+
const portConfig = this._getPublishedPortConfig(port);
|
|
848
871
|
if (portConfig.mode === 'private' && Array.isArray(portConfig.whitelist)) {
|
|
849
872
|
if (!portConfig.whitelist.includes(deviceId)) {
|
|
850
873
|
logger.warn(() => `Device ${deviceId} is not whitelisted for port ${port}. Sending portclose.`);
|
|
@@ -865,7 +888,7 @@ class PublishPort extends EventEmitter {
|
|
|
865
888
|
|
|
866
889
|
// Update remoteInfo if not set
|
|
867
890
|
if (!localSocket.remoteAddress) {
|
|
868
|
-
localSocket.remoteAddress =
|
|
891
|
+
localSocket.remoteAddress = host || (remoteInfo && remoteInfo.address) || portConfig.host;
|
|
869
892
|
localSocket.remotePort = port;
|
|
870
893
|
}
|
|
871
894
|
} else if (protocol === 'tcp') {
|