diodejs 0.3.0 → 0.4.1

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/bindPort.js CHANGED
@@ -5,6 +5,7 @@ const { Buffer } = require('buffer');
5
5
  const { toBufferView } = require('./utils');
6
6
  const { Duplex } = require('stream');
7
7
  const DiodeRPC = require('./rpc');
8
+ const nativeCrypto = require('./nativeCrypto');
8
9
  const logger = require('./logger');
9
10
 
10
11
  // Custom Duplex stream to handle the Diode connection
@@ -51,7 +52,8 @@ class BindPort {
51
52
  [localPortOrPortsConfig]: {
52
53
  targetPort,
53
54
  deviceIdHex: this._stripHexPrefix(deviceIdHex),
54
- protocol: 'tls' // Default protocol is tls
55
+ protocol: 'tls', // Default protocol is tls
56
+ transport: 'api'
55
57
  }
56
58
  };
57
59
  } else {
@@ -68,13 +70,21 @@ class BindPort {
68
70
  if (!this.portsConfig[port].protocol) {
69
71
  this.portsConfig[port].protocol = 'tls';
70
72
  }
71
- // Ensure protocol is uppercase
73
+ // Ensure protocol is lowercase
72
74
  this.portsConfig[port].protocol = this.portsConfig[port].protocol.toLowerCase();
75
+
76
+ // Normalize transport (api or native)
77
+ const transportValue = this.portsConfig[port].transport !== undefined
78
+ ? this.portsConfig[port].transport
79
+ : this.portsConfig[port].native;
80
+ this.portsConfig[port].transport = this._normalizeTransport(transportValue);
73
81
  }
74
82
  }
75
83
 
76
84
  this.servers = new Map(); // Track server instances by localPort
77
- this.rpc = new DiodeRPC(this.connection);
85
+ this._rpcByConnection = new Map();
86
+ this.rpc = this._isManager() ? null : this._getRpcFor(this.connection);
87
+ this.handshakeTimeoutMs = parseInt(process.env.DIODE_NATIVE_HANDSHAKE_TIMEOUT_MS, 10) || 10000;
78
88
 
79
89
  // Set up listener for unsolicited messages once
80
90
  this._setupMessageListener();
@@ -87,10 +97,146 @@ class BindPort {
87
97
  }
88
98
  return hexString;
89
99
  }
100
+
101
+ _normalizeTransport(value) {
102
+ if (value === true) return 'native';
103
+ if (typeof value === 'string') {
104
+ const lower = value.toLowerCase();
105
+ if (lower === 'native') return 'native';
106
+ if (lower === 'api') return 'api';
107
+ }
108
+ return 'api';
109
+ }
110
+
111
+ _isManager() {
112
+ return this.connection && typeof this.connection.getConnectionForDevice === 'function';
113
+ }
114
+
115
+ _getRpcFor(connection) {
116
+ if (!connection) return null;
117
+ let rpc = this._rpcByConnection.get(connection);
118
+ if (!rpc) {
119
+ rpc = connection.RPC || new DiodeRPC(connection);
120
+ this._rpcByConnection.set(connection, rpc);
121
+ }
122
+ return rpc;
123
+ }
124
+
125
+ async _resolveConnectionForDevice(deviceId) {
126
+ if (this._isManager()) {
127
+ return this.connection.getConnectionForDevice(deviceId);
128
+ }
129
+ return this.connection;
130
+ }
131
+
132
+ async _openTlsHandshakeChannel(connection, rpc, ref) {
133
+ const diodeSocket = new DiodeSocket(ref, rpc);
134
+ const certPem = connection.getDeviceCertificate();
135
+ if (!certPem) {
136
+ throw new Error('No device certificate available');
137
+ }
138
+
139
+ const tlsOptions = {
140
+ cert: certPem,
141
+ key: certPem,
142
+ rejectUnauthorized: false,
143
+ ciphers: 'ECDHE-ECDSA-AES256-GCM-SHA384',
144
+ ecdhCurve: 'secp256k1',
145
+ minVersion: 'TLSv1.2',
146
+ maxVersion: 'TLSv1.2',
147
+ };
148
+
149
+ const tlsSocket = tls.connect({
150
+ socket: diodeSocket,
151
+ ...tlsOptions
152
+ });
153
+ tlsSocket.setNoDelay(true);
154
+
155
+ const socketWrapper = {
156
+ diodeSocket,
157
+ tlsSocket,
158
+ end: () => {
159
+ try { tlsSocket.end(); } catch {}
160
+ try { diodeSocket._destroy(null, () => {}); } catch {}
161
+ }
162
+ };
163
+
164
+ connection.addClientSocket(ref, socketWrapper);
165
+
166
+ await new Promise((resolve, reject) => {
167
+ const timer = setTimeout(() => reject(new Error('TLS handshake timeout')), this.handshakeTimeoutMs);
168
+ tlsSocket.once('secureConnect', () => {
169
+ clearTimeout(timer);
170
+ resolve();
171
+ });
172
+ tlsSocket.once('error', (err) => {
173
+ clearTimeout(timer);
174
+ reject(err);
175
+ });
176
+ });
177
+
178
+ return { tlsSocket, socketWrapper };
179
+ }
180
+
181
+ async _performNativeHandshake(connection, rpc, deviceId, targetPort, physicalPort) {
182
+ const handshakePort = `tls:${targetPort}#hs`;
183
+ const ref = await rpc.portOpen(deviceId, handshakePort, 'rw');
184
+ if (!ref) {
185
+ throw new Error('Handshake portopen failed');
186
+ }
187
+
188
+ let tlsSocket;
189
+ try {
190
+ ({ tlsSocket } = await this._openTlsHandshakeChannel(connection, rpc, ref));
191
+
192
+ const localDeviceId = connection.getEthereumAddress().toLowerCase();
193
+ const remoteDeviceId = `0x${Buffer.from(deviceId).toString('hex')}`.toLowerCase();
194
+ const { message, privKey, nonce } = nativeCrypto.createHandshakeMessage({
195
+ role: 'bind',
196
+ deviceId: localDeviceId,
197
+ physicalPort,
198
+ privateKey: connection.getPrivateKey()
199
+ });
200
+
201
+ nativeCrypto.writeHandshakeMessage(tlsSocket, message);
202
+
203
+ const peerMessage = await nativeCrypto.readHandshakeMessage(tlsSocket, this.handshakeTimeoutMs);
204
+ const verification = nativeCrypto.verifyHandshakeMessage(peerMessage, {
205
+ expectedRole: 'publish',
206
+ expectedDeviceId: remoteDeviceId,
207
+ expectedPhysicalPort: physicalPort
208
+ });
209
+ if (!verification.ok) {
210
+ throw new Error(`Handshake verification failed: ${verification.reason}`);
211
+ }
212
+
213
+ const session = nativeCrypto.deriveSessionKeys({
214
+ role: 'bind',
215
+ localDeviceId,
216
+ remoteDeviceId,
217
+ localEphPriv: privKey,
218
+ remoteEphPub: verification.ephPub,
219
+ localNonce: nonce,
220
+ remoteNonce: verification.nonce,
221
+ physicalPort,
222
+ });
223
+
224
+ return session;
225
+ } finally {
226
+ try { if (tlsSocket) tlsSocket.end(); } catch {}
227
+ try { await rpc.portClose(ref); } catch {}
228
+ try { connection.deleteClientSocket(ref); } catch {}
229
+ }
230
+ }
90
231
 
91
232
  _setupMessageListener() {
92
233
  // Listen for data events from the device
93
- this.connection.on('unsolicited', (message) => {
234
+ this.connection.on('unsolicited', (message, sourceConnection) => {
235
+ const connection = sourceConnection || this.connection;
236
+ if (!connection || typeof connection.getClientSocket !== 'function') {
237
+ logger.warn(() => 'Received unsolicited message without a valid connection context');
238
+ return;
239
+ }
94
240
  const [messageIdRaw, messageContent] = message;
95
241
  const messageTypeRaw = messageContent[0];
96
242
  const messageType = toBufferView(messageTypeRaw).toString('utf8');
@@ -103,7 +249,7 @@ class BindPort {
103
249
  const data = toBufferView(dataRaw);
104
250
 
105
251
  // Find the associated client socket from connection
106
- const clientSocket = this.connection.getClientSocket(dataRef);
252
+ const clientSocket = connection.getClientSocket(dataRef);
107
253
  if (clientSocket) {
108
254
  if (clientSocket.diodeSocket) {
109
255
  // If it's a DiodeSocket, push data to it so tls can process
@@ -113,7 +259,7 @@ class BindPort {
113
259
  clientSocket.write(data);
114
260
  }
115
261
  } else {
116
- const connectionInfo = this.connection.getConnection(dataRef);
262
+ const connectionInfo = connection.getConnection(dataRef);
117
263
  if (connectionInfo) {
118
264
  logger.debug(() => `No client socket found for ref: ${dataRef.toString('hex')}, but connection exists for ${connectionInfo.host}:${connectionInfo.port}`);
119
265
  } else {
@@ -125,17 +271,17 @@ class BindPort {
125
271
  const dataRef = toBufferView(refRaw);
126
272
 
127
273
  // Close the associated client socket
128
- const clientSocket = this.connection.getClientSocket(dataRef);
274
+ const clientSocket = connection.getClientSocket(dataRef);
129
275
  if (clientSocket) {
130
276
  if (clientSocket.diodeSocket) {
131
277
  clientSocket.diodeSocket._destroy(null, () => {});
132
278
  }
133
279
  clientSocket.end();
134
- this.connection.deleteClientSocket(dataRef);
280
+ connection.deleteClientSocket(dataRef);
135
281
  logger.info(() => `Port closed for ref: ${dataRef.toString('hex')}`);
136
282
  }
137
283
  } else {
138
- if (messageType != 'portopen') {
284
+ if (messageType != 'portopen' && messageType != 'portopen2' && messageType != 'ticket_request') {
139
285
  logger.warn(() => `Unknown unsolicited message type: ${messageType}`);
140
286
  }
141
287
  }
@@ -154,7 +300,7 @@ class BindPort {
154
300
  });
155
301
  }
156
302
 
157
- addPort(localPort, targetPort, deviceIdHex, protocol = 'tls') {
303
+ addPort(localPort, targetPort, deviceIdHex, protocol = 'tls', transport = undefined) {
158
304
  if (this.servers.has(localPort)) {
159
305
  logger.warn(() => `Port ${localPort} is already bound`);
160
306
  return false;
@@ -163,7 +309,8 @@ class BindPort {
163
309
  this.portsConfig[localPort] = {
164
310
  targetPort,
165
311
  deviceIdHex: this._stripHexPrefix(deviceIdHex),
166
- protocol: protocol.toLowerCase()
312
+ protocol: protocol.toLowerCase(),
313
+ transport: this._normalizeTransport(transport)
167
314
  };
168
315
 
169
316
  this.bindSinglePort(localPort);
@@ -207,6 +354,11 @@ class BindPort {
207
354
  }
208
355
 
209
356
  const { targetPort, deviceIdHex, protocol = 'tls' } = config;
357
+ const transport = config.transport || 'api';
358
+ const useNative = transport === 'native' && (protocol === 'tcp' || protocol === 'udp');
359
+ if (transport === 'native' && protocol === 'tls') {
360
+ logger.warn(() => `Native transport does not support TLS for port ${localPort}. Falling back to API relay.`);
361
+ }
210
362
  const deviceId = Buffer.from(deviceIdHex, 'hex');
211
363
 
212
364
  // Format the target port with protocol prefix for the remote connection
@@ -222,23 +374,128 @@ class BindPort {
222
374
  });
223
375
 
224
376
  server.on('message', async (data, rinfo) => {
225
- // Open a new port on the device if this is a new client
226
377
  const clientKey = `${rinfo.address}:${rinfo.port}`;
227
- let ref = server.clientRefs && server.clientRefs[clientKey];
378
+ if (useNative) {
379
+ if (!server.nativeRelays) server.nativeRelays = {};
380
+ let relayInfo = server.nativeRelays[clientKey];
381
+ if (!relayInfo) {
382
+ let connection;
383
+ try {
384
+ connection = await this._resolveConnectionForDevice(deviceId);
385
+ } catch (error) {
386
+ logger.error(() => `Error resolving relay for device ${deviceIdHex}: ${error}`);
387
+ return;
388
+ }
389
+ if (!connection) {
390
+ logger.error(() => `No relay connection available for device ${deviceIdHex}`);
391
+ return;
392
+ }
393
+ const rpc = this._getRpcFor(connection);
394
+ try {
395
+ const flags = config.flags || 'rwu';
396
+ const physicalPort = await rpc.portOpen2(deviceId, formattedTargetPort, flags);
397
+ if (!physicalPort) {
398
+ logger.error(() => `Error opening portopen2 ${formattedTargetPort} on deviceId: ${deviceIdHex}`);
399
+ return;
400
+ }
401
+
402
+ const relaySocket = dgram.createSocket('udp4');
403
+ relaySocket.on('message', (msg) => {
404
+ if (!relayInfo || !relayInfo.session) return;
405
+ const plaintext = nativeCrypto.parseUdpPacket(relayInfo.session, msg);
406
+ if (!plaintext) return;
407
+ server.send(plaintext, rinfo.port, rinfo.address);
408
+ });
409
+ relaySocket.on('error', (err) => {
410
+ logger.error(() => `udp relay socket error: ${err}`);
411
+ try { relaySocket.close(); } catch {}
412
+ delete server.nativeRelays[clientKey];
413
+ });
414
+ relaySocket.bind(0);
415
+
416
+ relayInfo = {
417
+ socket: relaySocket,
418
+ physicalPort,
419
+ relayHost: connection.getServerRelayHost(),
420
+ connection,
421
+ session: null,
422
+ handshakePromise: null,
423
+ client: { address: rinfo.address, port: rinfo.port }
424
+ };
425
+ server.nativeRelays[clientKey] = relayInfo;
426
+ logger.info(() => `Portopen2 ${formattedTargetPort} opened with server port ${physicalPort} for udp client ${clientKey}`);
427
+ } catch (error) {
428
+ logger.error(() => `Error opening portopen2 ${formattedTargetPort} on device: ${error}`);
429
+ return;
430
+ }
431
+ }
432
+
433
+ if (!relayInfo.handshakePromise) {
434
+ const rpc = this._getRpcFor(relayInfo.connection);
435
+ relayInfo.handshakePromise = this._performNativeHandshake(
436
+ relayInfo.connection,
437
+ rpc,
438
+ deviceId,
439
+ targetPort,
440
+ relayInfo.physicalPort
441
+ ).then((session) => {
442
+ relayInfo.session = session;
443
+ return session;
444
+ }).catch((error) => {
445
+ logger.error(() => `Native UDP handshake failed: ${error}`);
446
+ try { relayInfo.socket.close(); } catch {}
447
+ delete server.nativeRelays[clientKey];
448
+ throw error;
449
+ });
450
+ }
451
+
452
+ try {
453
+ await relayInfo.handshakePromise;
454
+ } catch (_) {
455
+ return;
456
+ }
457
+
458
+ if (!relayInfo.session) return;
459
+
460
+ // Send encrypted data to the server relay port
461
+ try {
462
+ const packet = nativeCrypto.createUdpPacket(relayInfo.session, data);
463
+ relayInfo.socket.send(packet, relayInfo.physicalPort, relayInfo.relayHost);
464
+ } catch (error) {
465
+ logger.error(() => `Error sending udp data to relay: ${error}`);
466
+ }
467
+ return;
468
+ }
469
+
470
+ // Legacy API relay
471
+ let entry = server.clientRefs && server.clientRefs[clientKey];
228
472
 
229
- if (!ref) {
473
+ if (!entry) {
474
+ let connection;
230
475
  try {
231
- ref = await this.rpc.portOpen(deviceId, formattedTargetPort, 'rw');
476
+ connection = await this._resolveConnectionForDevice(deviceId);
477
+ } catch (error) {
478
+ logger.error(() => `Error resolving relay for device ${deviceIdHex}: ${error}`);
479
+ return;
480
+ }
481
+ if (!connection) {
482
+ logger.error(() => `No relay connection available for device ${deviceIdHex}`);
483
+ return;
484
+ }
485
+ const rpc = this._getRpcFor(connection);
486
+ try {
487
+ const ref = await rpc.portOpen(deviceId, formattedTargetPort, 'rw');
232
488
  if (!ref) {
233
489
  logger.error(() => `Error opening port ${formattedTargetPort} on deviceId: ${deviceIdHex}`);
234
490
  return;
235
491
  } else {
236
492
  logger.info(() => `Port ${formattedTargetPort} opened on device with ref: ${ref.toString('hex')} for udp client ${clientKey}`);
237
493
  if (!server.clientRefs) server.clientRefs = {};
238
- server.clientRefs[clientKey] = ref;
494
+ entry = { ref, connection };
495
+ server.clientRefs[clientKey] = entry;
239
496
 
240
497
  // Store the client info
241
- this.connection.addClientSocket(ref, {
498
+ connection.addClientSocket(ref, {
242
499
  address: rinfo.address,
243
500
  port: rinfo.port,
244
501
  protocol: 'udp',
@@ -255,7 +512,8 @@ class BindPort {
255
512
 
256
513
  // Send data to the device
257
514
  try {
258
- await this.rpc.portSend(ref, data);
515
+ const rpc = this._getRpcFor(entry.connection);
516
+ await rpc.portSend(entry.ref, data);
259
517
  } catch (error) {
260
518
  logger.error(() => `Error sending udp data to device: ${error}`);
261
519
  }
@@ -265,6 +523,15 @@ class BindPort {
265
523
  logger.error(() => `udp Server error: ${err}`);
266
524
  });
267
525
 
526
+ server.on('close', () => {
527
+ if (server.nativeRelays) {
528
+ for (const relayInfo of Object.values(server.nativeRelays)) {
529
+ try { relayInfo.socket.close(); } catch {}
530
+ }
531
+ server.nativeRelays = null;
532
+ }
533
+ });
534
+
268
535
  server.bind(localPort);
269
536
  this.servers.set(parseInt(localPort), server);
270
537
  } else {
@@ -273,10 +540,120 @@ class BindPort {
273
540
  logger.info(() => `Client connected to local server on port ${localPort}`);
274
541
  clientSocket.setNoDelay(true);
275
542
 
276
- // Open a new port on the device for this client
543
+ let connection;
544
+ try {
545
+ connection = await this._resolveConnectionForDevice(deviceId);
546
+ } catch (error) {
547
+ logger.error(() => `Error resolving relay for device ${deviceIdHex}: ${error}`);
548
+ clientSocket.destroy();
549
+ return;
550
+ }
551
+ if (!connection) {
552
+ logger.error(() => `No relay connection available for device ${deviceIdHex}`);
553
+ clientSocket.destroy();
554
+ return;
555
+ }
556
+ const rpc = this._getRpcFor(connection);
557
+
277
558
  let ref;
559
+ if (useNative) {
560
+ // Open a new native relay port on the device for this client
561
+ let physicalPort;
562
+ try {
563
+ const flags = config.flags || 'rw';
564
+ physicalPort = await rpc.portOpen2(deviceId, formattedTargetPort, flags);
565
+ if (!physicalPort) {
566
+ logger.error(() => `Error opening portopen2 ${formattedTargetPort} on deviceId: ${deviceIdHex}`);
567
+ clientSocket.destroy();
568
+ return;
569
+ }
570
+ } catch (error) {
571
+ logger.error(() => `Error opening portopen2 ${formattedTargetPort} on device: ${error}`);
572
+ clientSocket.destroy();
573
+ return;
574
+ }
575
+
576
+ let session;
577
+ try {
578
+ session = await this._performNativeHandshake(connection, rpc, deviceId, targetPort, physicalPort);
579
+ } catch (error) {
580
+ logger.error(() => `Native TCP handshake failed: ${error}`);
581
+ clientSocket.destroy();
582
+ return;
583
+ }
584
+
585
+ const relayHost = connection.getServerRelayHost();
586
+ const relaySocket = net.connect({ host: relayHost, port: physicalPort }, () => {
587
+ logger.info(() => `Connected to relay ${relayHost}:${physicalPort} for ${formattedTargetPort}`);
588
+ });
589
+ relaySocket.setNoDelay(true);
590
+
591
+ let relayReady = false;
592
+ const pendingChunks = [];
593
+
594
+ const cleanup = () => {
595
+ if (!clientSocket.destroyed) clientSocket.destroy();
596
+ if (!relaySocket.destroyed) relaySocket.destroy();
597
+ };
598
+
599
+ relaySocket.on('connect', () => {
600
+ relayReady = true;
601
+ while (pendingChunks.length > 0) {
602
+ const chunk = pendingChunks.shift();
603
+ try {
604
+ const frame = nativeCrypto.createTcpFrame(session, chunk);
605
+ relaySocket.write(frame);
606
+ } catch (error) {
607
+ logger.error(() => `Error sending TCP frame: ${error}`);
608
+ cleanup();
609
+ break;
610
+ }
611
+ }
612
+ });
613
+
614
+ relaySocket.on('data', (data) => {
615
+ try {
616
+ const messages = nativeCrypto.consumeTcpFrames(session, data);
617
+ for (const msg of messages) {
618
+ clientSocket.write(msg);
619
+ }
620
+ } catch (error) {
621
+ logger.error(() => `TCP decrypt error: ${error}`);
622
+ cleanup();
623
+ }
624
+ });
625
+
626
+ clientSocket.on('data', (data) => {
627
+ if (!relayReady) {
628
+ pendingChunks.push(data);
629
+ return;
630
+ }
631
+ try {
632
+ const frame = nativeCrypto.createTcpFrame(session, data);
633
+ relaySocket.write(frame);
634
+ } catch (error) {
635
+ logger.error(() => `Error sending TCP frame: ${error}`);
636
+ cleanup();
637
+ }
638
+ });
639
+
640
+ relaySocket.on('error', (err) => {
641
+ logger.error(() => `Relay socket error: ${err}`);
642
+ cleanup();
643
+ });
644
+ clientSocket.on('error', (err) => {
645
+ logger.error(() => `Client socket error: ${err}`);
646
+ cleanup();
647
+ });
648
+ clientSocket.on('end', cleanup);
649
+ relaySocket.on('end', cleanup);
650
+
651
+ return;
652
+ }
653
+
654
+ // Legacy API relay
278
655
  try {
279
- ref = await this.rpc.portOpen(deviceId, formattedTargetPort, 'rw');
656
+ ref = await rpc.portOpen(deviceId, formattedTargetPort, 'rw');
280
657
  if (!ref) {
281
658
  logger.error(() => `Error opening port ${formattedTargetPort} on deviceId: ${deviceIdHex}`);
282
659
  clientSocket.destroy();
@@ -294,10 +671,10 @@ class BindPort {
294
671
  // For tls protocol, create a proper tls connection
295
672
  try {
296
673
  // Create a DiodeSocket to handle communication with the device
297
- const diodeSocket = new DiodeSocket(ref, this.rpc);
674
+ const diodeSocket = new DiodeSocket(ref, rpc);
298
675
 
299
676
  // Get the device certificate for tls
300
- const certPem = this.connection.getDeviceCertificate();
677
+ const certPem = connection.getDeviceCertificate();
301
678
  if (!certPem) {
302
679
  throw new Error('No device certificate available');
303
680
  }
@@ -342,7 +719,7 @@ class BindPort {
342
719
  };
343
720
 
344
721
  // Store the socket wrapper
345
- this.connection.addClientSocket(ref, socketWrapper);
722
+ connection.addClientSocket(ref, socketWrapper);
346
723
 
347
724
  } catch (error) {
348
725
  logger.error(() => `Error setting up tls connection: ${error}`);
@@ -351,12 +728,12 @@ class BindPort {
351
728
  }
352
729
  } else {
353
730
  // For TCP protocol, just use the raw socket
354
- this.connection.addClientSocket(ref, clientSocket);
731
+ connection.addClientSocket(ref, clientSocket);
355
732
 
356
733
  // Handle data from client to device
357
734
  clientSocket.on('data', async (data) => {
358
735
  try {
359
- await this.rpc.portSend(ref, data);
736
+ await rpc.portSend(ref, data);
360
737
  } catch (error) {
361
738
  logger.error(() => `Error sending data to device: ${error}`);
362
739
  clientSocket.destroy();
@@ -367,11 +744,11 @@ class BindPort {
367
744
  // Handle client socket closure (common for all protocols)
368
745
  clientSocket.on('end', async () => {
369
746
  logger.info(() => 'Client disconnected');
370
- if (ref && this.connection.hasClientSocket(ref)) {
747
+ if (ref && connection.hasClientSocket(ref)) {
371
748
  try {
372
- await this.rpc.portClose(ref);
749
+ await rpc.portClose(ref);
373
750
  logger.info(() => `Port closed on device for ref: ${ref.toString('hex')}`);
374
- this.connection.deleteClientSocket(ref);
751
+ connection.deleteClientSocket(ref);
375
752
  } catch (error) {
376
753
  logger.error(() => `Error closing port on device: ${error}`);
377
754
  }