appium-ios-remotexpc 0.0.3 → 0.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -0
- package/build/src/base-plist-service.d.ts +51 -0
- package/build/src/base-plist-service.d.ts.map +1 -0
- package/build/src/base-plist-service.js +61 -0
- package/build/src/base-socket-service.d.ts +15 -0
- package/build/src/base-socket-service.d.ts.map +1 -0
- package/build/src/base-socket-service.js +46 -0
- package/build/src/index.d.ts +9 -0
- package/build/src/index.d.ts.map +1 -0
- package/build/src/index.js +7 -0
- package/build/src/lib/apple-tv/constants.d.ts +77 -0
- package/build/src/lib/apple-tv/constants.d.ts.map +1 -0
- package/build/src/lib/apple-tv/constants.js +106 -0
- package/build/src/lib/apple-tv/encryption/chacha20-poly1305.d.ts +22 -0
- package/build/src/lib/apple-tv/encryption/chacha20-poly1305.d.ts.map +1 -0
- package/build/src/lib/apple-tv/encryption/chacha20-poly1305.js +97 -0
- package/build/src/lib/apple-tv/encryption/ed25519.d.ts +16 -0
- package/build/src/lib/apple-tv/encryption/ed25519.d.ts.map +1 -0
- package/build/src/lib/apple-tv/encryption/ed25519.js +93 -0
- package/build/src/lib/apple-tv/encryption/hkdf.d.ts +18 -0
- package/build/src/lib/apple-tv/encryption/hkdf.d.ts.map +1 -0
- package/build/src/lib/apple-tv/encryption/hkdf.js +73 -0
- package/build/src/lib/apple-tv/encryption/index.d.ts +5 -0
- package/build/src/lib/apple-tv/encryption/index.d.ts.map +1 -0
- package/build/src/lib/apple-tv/encryption/index.js +4 -0
- package/build/src/lib/apple-tv/encryption/opack2.d.ts +57 -0
- package/build/src/lib/apple-tv/encryption/opack2.d.ts.map +1 -0
- package/build/src/lib/apple-tv/encryption/opack2.js +203 -0
- package/build/src/lib/apple-tv/errors.d.ts +17 -0
- package/build/src/lib/apple-tv/errors.d.ts.map +1 -0
- package/build/src/lib/apple-tv/errors.js +30 -0
- package/build/src/lib/apple-tv/tlv/decoder.d.ts +19 -0
- package/build/src/lib/apple-tv/tlv/decoder.d.ts.map +1 -0
- package/build/src/lib/apple-tv/tlv/decoder.js +49 -0
- package/build/src/lib/apple-tv/tlv/encoder.d.ts +10 -0
- package/build/src/lib/apple-tv/tlv/encoder.d.ts.map +1 -0
- package/build/src/lib/apple-tv/tlv/encoder.js +20 -0
- package/build/src/lib/apple-tv/tlv/index.d.ts +4 -0
- package/build/src/lib/apple-tv/tlv/index.d.ts.map +1 -0
- package/build/src/lib/apple-tv/tlv/index.js +3 -0
- package/build/src/lib/apple-tv/tlv/pairing-tlv.d.ts +14 -0
- package/build/src/lib/apple-tv/tlv/pairing-tlv.d.ts.map +1 -0
- package/build/src/lib/apple-tv/tlv/pairing-tlv.js +27 -0
- package/build/src/lib/apple-tv/types.d.ts +36 -0
- package/build/src/lib/apple-tv/types.d.ts.map +1 -0
- package/build/src/lib/apple-tv/types.js +1 -0
- package/build/src/lib/apple-tv/utils/buffer-utils.d.ts +40 -0
- package/build/src/lib/apple-tv/utils/buffer-utils.d.ts.map +1 -0
- package/build/src/lib/apple-tv/utils/buffer-utils.js +76 -0
- package/build/src/lib/apple-tv/utils/index.d.ts +3 -0
- package/build/src/lib/apple-tv/utils/index.d.ts.map +1 -0
- package/build/src/lib/apple-tv/utils/index.js +2 -0
- package/build/src/lib/apple-tv/utils/uuid-generator.d.ts +9 -0
- package/build/src/lib/apple-tv/utils/uuid-generator.d.ts.map +1 -0
- package/build/src/lib/apple-tv/utils/uuid-generator.js +36 -0
- package/build/src/lib/lockdown/index.d.ts +87 -0
- package/build/src/lib/lockdown/index.d.ts.map +1 -0
- package/build/src/lib/lockdown/index.js +324 -0
- package/build/src/lib/pair-record/index.d.ts +3 -0
- package/build/src/lib/pair-record/index.d.ts.map +1 -0
- package/build/src/lib/pair-record/index.js +2 -0
- package/build/src/lib/pair-record/pair-record.d.ts +48 -0
- package/build/src/lib/pair-record/pair-record.d.ts.map +1 -0
- package/build/src/lib/pair-record/pair-record.js +85 -0
- package/build/src/lib/plist/binary-plist-creator.d.ts +14 -0
- package/build/src/lib/plist/binary-plist-creator.d.ts.map +1 -0
- package/build/src/lib/plist/binary-plist-creator.js +475 -0
- package/build/src/lib/plist/binary-plist-parser.d.ts +14 -0
- package/build/src/lib/plist/binary-plist-parser.d.ts.map +1 -0
- package/build/src/lib/plist/binary-plist-parser.js +449 -0
- package/build/src/lib/plist/constants.d.ts +36 -0
- package/build/src/lib/plist/constants.d.ts.map +1 -0
- package/build/src/lib/plist/constants.js +43 -0
- package/build/src/lib/plist/index.d.ts +14 -0
- package/build/src/lib/plist/index.d.ts.map +1 -0
- package/build/src/lib/plist/index.js +16 -0
- package/build/src/lib/plist/length-based-splitter.d.ts +43 -0
- package/build/src/lib/plist/length-based-splitter.d.ts.map +1 -0
- package/build/src/lib/plist/length-based-splitter.js +228 -0
- package/build/src/lib/plist/plist-creator.d.ts +8 -0
- package/build/src/lib/plist/plist-creator.d.ts.map +1 -0
- package/build/src/lib/plist/plist-creator.js +33 -0
- package/build/src/lib/plist/plist-decoder.d.ts +25 -0
- package/build/src/lib/plist/plist-decoder.d.ts.map +1 -0
- package/build/src/lib/plist/plist-decoder.js +103 -0
- package/build/src/lib/plist/plist-encoder.d.ts +10 -0
- package/build/src/lib/plist/plist-encoder.d.ts.map +1 -0
- package/build/src/lib/plist/plist-encoder.js +27 -0
- package/build/src/lib/plist/plist-parser.d.ts +9 -0
- package/build/src/lib/plist/plist-parser.d.ts.map +1 -0
- package/build/src/lib/plist/plist-parser.js +109 -0
- package/build/src/lib/plist/plist-service.d.ts +86 -0
- package/build/src/lib/plist/plist-service.d.ts.map +1 -0
- package/build/src/lib/plist/plist-service.js +180 -0
- package/build/src/lib/plist/unified-plist-creator.d.ts +9 -0
- package/build/src/lib/plist/unified-plist-creator.d.ts.map +1 -0
- package/build/src/lib/plist/unified-plist-creator.js +14 -0
- package/build/src/lib/plist/unified-plist-parser.d.ts +8 -0
- package/build/src/lib/plist/unified-plist-parser.d.ts.map +1 -0
- package/build/src/lib/plist/unified-plist-parser.js +23 -0
- package/build/src/lib/plist/utils.d.ts +97 -0
- package/build/src/lib/plist/utils.d.ts.map +1 -0
- package/build/src/lib/plist/utils.js +287 -0
- package/build/src/lib/remote-xpc/constants.d.ts +20 -0
- package/build/src/lib/remote-xpc/constants.d.ts.map +1 -0
- package/build/src/lib/remote-xpc/constants.js +21 -0
- package/build/src/lib/remote-xpc/handshake-frames.d.ts +74 -0
- package/build/src/lib/remote-xpc/handshake-frames.d.ts.map +1 -0
- package/build/src/lib/remote-xpc/handshake-frames.js +285 -0
- package/build/src/lib/remote-xpc/handshake.d.ts +14 -0
- package/build/src/lib/remote-xpc/handshake.d.ts.map +1 -0
- package/build/src/lib/remote-xpc/handshake.js +95 -0
- package/build/src/lib/remote-xpc/remote-xpc-connection.d.ts +55 -0
- package/build/src/lib/remote-xpc/remote-xpc-connection.d.ts.map +1 -0
- package/build/src/lib/remote-xpc/remote-xpc-connection.js +365 -0
- package/build/src/lib/remote-xpc/xpc-protocol.d.ts +22 -0
- package/build/src/lib/remote-xpc/xpc-protocol.d.ts.map +1 -0
- package/build/src/lib/remote-xpc/xpc-protocol.js +368 -0
- package/build/src/lib/tunnel/index.d.ts +69 -0
- package/build/src/lib/tunnel/index.d.ts.map +1 -0
- package/build/src/lib/tunnel/index.js +205 -0
- package/build/src/lib/tunnel/packet-stream-client.d.ts +46 -0
- package/build/src/lib/tunnel/packet-stream-client.d.ts.map +1 -0
- package/build/src/lib/tunnel/packet-stream-client.js +152 -0
- package/build/src/lib/tunnel/packet-stream-server.d.ts +37 -0
- package/build/src/lib/tunnel/packet-stream-server.d.ts.map +1 -0
- package/build/src/lib/tunnel/packet-stream-server.js +109 -0
- package/build/src/lib/tunnel/tunnel-api-client.d.ts +85 -0
- package/build/src/lib/tunnel/tunnel-api-client.d.ts.map +1 -0
- package/build/src/lib/tunnel/tunnel-api-client.js +207 -0
- package/build/src/lib/tunnel/tunnel-registry-server.d.ts +68 -0
- package/build/src/lib/tunnel/tunnel-registry-server.d.ts.map +1 -0
- package/build/src/lib/tunnel/tunnel-registry-server.js +351 -0
- package/build/src/lib/types.d.ts +238 -0
- package/build/src/lib/types.d.ts.map +1 -0
- package/build/src/lib/types.js +4 -0
- package/build/src/lib/usbmux/index.d.ts +177 -0
- package/build/src/lib/usbmux/index.d.ts.map +1 -0
- package/build/src/lib/usbmux/index.js +490 -0
- package/build/src/lib/usbmux/usbmux-decoder.d.ts +19 -0
- package/build/src/lib/usbmux/usbmux-decoder.d.ts.map +1 -0
- package/build/src/lib/usbmux/usbmux-decoder.js +38 -0
- package/build/src/lib/usbmux/usbmux-encoder.d.ts +12 -0
- package/build/src/lib/usbmux/usbmux-encoder.d.ts.map +1 -0
- package/build/src/lib/usbmux/usbmux-encoder.js +32 -0
- package/build/src/service-connection.d.ts +34 -0
- package/build/src/service-connection.d.ts.map +1 -0
- package/build/src/service-connection.js +51 -0
- package/build/src/services/index.d.ts +6 -0
- package/build/src/services/index.d.ts.map +1 -0
- package/build/src/services/index.js +5 -0
- package/build/src/services/ios/base-service.d.ts +35 -0
- package/build/src/services/ios/base-service.d.ts.map +1 -0
- package/build/src/services/ios/base-service.js +55 -0
- package/build/src/services/ios/diagnostic-service/index.d.ts +46 -0
- package/build/src/services/ios/diagnostic-service/index.d.ts.map +1 -0
- package/build/src/services/ios/diagnostic-service/index.js +169 -0
- package/build/src/services/ios/diagnostic-service/keys.d.ts +5 -0
- package/build/src/services/ios/diagnostic-service/keys.d.ts.map +1 -0
- package/build/src/services/ios/diagnostic-service/keys.js +770 -0
- package/build/src/services/ios/syslog-service/index.d.ts +91 -0
- package/build/src/services/ios/syslog-service/index.d.ts.map +1 -0
- package/build/src/services/ios/syslog-service/index.js +323 -0
- package/build/src/services/ios/tunnel-service/index.d.ts +17 -0
- package/build/src/services/ios/tunnel-service/index.d.ts.map +1 -0
- package/build/src/services/ios/tunnel-service/index.js +57 -0
- package/build/src/services.d.ts +14 -0
- package/build/src/services.d.ts.map +1 -0
- package/build/src/services.js +48 -0
- package/package.json +12 -3
- package/src/lib/apple-tv/constants.ts +42 -0
- package/src/lib/apple-tv/encryption/chacha20-poly1305.ts +147 -0
- package/src/lib/apple-tv/encryption/ed25519.ts +126 -0
- package/src/lib/apple-tv/encryption/hkdf.ts +95 -0
- package/src/lib/apple-tv/encryption/index.ts +11 -0
- package/src/lib/apple-tv/encryption/opack2.ts +257 -0
- package/.github/dependabot.yml +0 -38
- package/.github/workflows/format-check.yml +0 -43
- package/.github/workflows/lint-and-build.yml +0 -40
- package/.github/workflows/pr-title.yml +0 -16
- package/.github/workflows/publish.js.yml +0 -43
- package/.github/workflows/test-validation.yml +0 -40
- package/.mocharc.json +0 -8
- package/.prettierignore +0 -3
- package/.prettierrc +0 -17
- package/.releaserc +0 -48
- package/assets/images/ios-arch.png +0 -0
- package/eslint.config.js +0 -45
- package/npm-shrinkwrap.json +0 -2711
- package/test/integration/diagnostics-test.ts +0 -44
- package/test/integration/read-pair-record-test.ts +0 -39
- package/test/integration/tunnel-test.ts +0 -104
- package/test/unit/apple-tv/tlv/decoder.spec.ts +0 -144
- package/test/unit/apple-tv/tlv/encoder.spec.ts +0 -91
- package/test/unit/apple-tv/tlv/pairing-tlv.spec.ts +0 -101
- package/test/unit/apple-tv/tlv/tlv-integration.spec.ts +0 -146
- package/test/unit/apple-tv/utils/buffer-utils.spec.ts +0 -74
- package/test/unit/apple-tv/utils/uuid-generator.spec.ts +0 -39
- package/test/unit/fixtures/index.ts +0 -88
- package/test/unit/fixtures/usbmuxconnectmessage.bin +0 -0
- package/test/unit/fixtures/usbmuxlistdevicemessage.bin +0 -0
- package/test/unit/plist/error-handling.spec.ts +0 -101
- package/test/unit/plist/fixtures/sample.binary.plist +0 -0
- package/test/unit/plist/fixtures/sample.xml.plist +0 -38
- package/test/unit/plist/plist-parser.spec.ts +0 -283
- package/test/unit/plist/plist.spec.ts +0 -205
- package/test/unit/plist/tag-position-handling.spec.ts +0 -90
- package/test/unit/plist/unified-plist-parser.spec.ts +0 -227
- package/test/unit/plist/utils.spec.ts +0 -249
- package/test/unit/plist/xml-cleaning.spec.ts +0 -60
- package/test/unit/tunnel/tunnel-registry-server.spec.ts +0 -194
- package/test/unit/usbmux/usbmux-specs.ts +0 -71
- package/tsconfig.json +0 -36
|
@@ -0,0 +1,368 @@
|
|
|
1
|
+
// Constants for XPC protocol.
|
|
2
|
+
const BODY_VERSION = 0x00000005;
|
|
3
|
+
const WRAPPER_MAGIC = 0x29b00b92;
|
|
4
|
+
const OBJECT_MAGIC = 0x42133742;
|
|
5
|
+
export const XPC_TYPES = {
|
|
6
|
+
null: 0x00001000,
|
|
7
|
+
bool: 0x00002000,
|
|
8
|
+
int64: 0x00003000,
|
|
9
|
+
uint64: 0x00004000,
|
|
10
|
+
double: 0x00005000,
|
|
11
|
+
date: 0x00007000,
|
|
12
|
+
data: 0x00008000,
|
|
13
|
+
string: 0x00009000,
|
|
14
|
+
uuid: 0x0000a000,
|
|
15
|
+
array: 0x0000e000,
|
|
16
|
+
dictionary: 0x0000f000,
|
|
17
|
+
fileTransfer: 0x0001a000,
|
|
18
|
+
};
|
|
19
|
+
// Helper: calculates padding bytes needed to align a length to a multiple of 4.
|
|
20
|
+
function calcPadding(len) {
|
|
21
|
+
return (4 - (len % 4)) % 4;
|
|
22
|
+
}
|
|
23
|
+
// A simple binary writer that collects Buffer chunks.
|
|
24
|
+
class Writer {
|
|
25
|
+
chunks = [];
|
|
26
|
+
writeBuffer(buf) {
|
|
27
|
+
this.chunks.push(buf);
|
|
28
|
+
}
|
|
29
|
+
writeUInt32LE(value) {
|
|
30
|
+
const buf = Buffer.alloc(4);
|
|
31
|
+
buf.writeUInt32LE(value, 0);
|
|
32
|
+
this.writeBuffer(buf);
|
|
33
|
+
}
|
|
34
|
+
writeBigUInt64LE(value) {
|
|
35
|
+
const buf = Buffer.alloc(8);
|
|
36
|
+
if (typeof value === 'bigint') {
|
|
37
|
+
buf.writeBigUInt64LE(value, 0);
|
|
38
|
+
}
|
|
39
|
+
else {
|
|
40
|
+
buf.writeBigUInt64LE(BigInt(value), 0);
|
|
41
|
+
}
|
|
42
|
+
this.writeBuffer(buf);
|
|
43
|
+
}
|
|
44
|
+
writeBigInt64LE(value) {
|
|
45
|
+
const buf = Buffer.alloc(8);
|
|
46
|
+
buf.writeBigInt64LE(value, 0);
|
|
47
|
+
this.writeBuffer(buf);
|
|
48
|
+
}
|
|
49
|
+
writeDoubleLE(value) {
|
|
50
|
+
const buf = Buffer.alloc(8);
|
|
51
|
+
buf.writeDoubleLE(value, 0);
|
|
52
|
+
this.writeBuffer(buf);
|
|
53
|
+
}
|
|
54
|
+
writeByte(value) {
|
|
55
|
+
const buf = Buffer.alloc(1);
|
|
56
|
+
buf.writeUInt8(value, 0);
|
|
57
|
+
this.writeBuffer(buf);
|
|
58
|
+
}
|
|
59
|
+
writePadding(len) {
|
|
60
|
+
if (len > 0) {
|
|
61
|
+
this.writeBuffer(Buffer.alloc(len));
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
concat() {
|
|
65
|
+
return Buffer.concat(this.chunks);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
// A simple binary reader that uses an offset to traverse a Buffer.
|
|
69
|
+
class Reader {
|
|
70
|
+
buffer;
|
|
71
|
+
offset = 0;
|
|
72
|
+
constructor(buffer) {
|
|
73
|
+
this.buffer = buffer;
|
|
74
|
+
}
|
|
75
|
+
readUInt32LE() {
|
|
76
|
+
const val = this.buffer.readUInt32LE(this.offset);
|
|
77
|
+
this.offset += 4;
|
|
78
|
+
return val;
|
|
79
|
+
}
|
|
80
|
+
readBigUInt64LE() {
|
|
81
|
+
const val = this.buffer.readBigUInt64LE(this.offset);
|
|
82
|
+
this.offset += 8;
|
|
83
|
+
return val;
|
|
84
|
+
}
|
|
85
|
+
readBigInt64LE() {
|
|
86
|
+
const val = this.buffer.readBigInt64LE(this.offset);
|
|
87
|
+
this.offset += 8;
|
|
88
|
+
return val;
|
|
89
|
+
}
|
|
90
|
+
readDoubleLE() {
|
|
91
|
+
const val = this.buffer.readDoubleLE(this.offset);
|
|
92
|
+
this.offset += 8;
|
|
93
|
+
return val;
|
|
94
|
+
}
|
|
95
|
+
readByte() {
|
|
96
|
+
const val = this.buffer.readUInt8(this.offset);
|
|
97
|
+
this.offset += 1;
|
|
98
|
+
return val;
|
|
99
|
+
}
|
|
100
|
+
readBytes(n) {
|
|
101
|
+
const buf = this.buffer.slice(this.offset, this.offset + n);
|
|
102
|
+
this.offset += n;
|
|
103
|
+
return buf;
|
|
104
|
+
}
|
|
105
|
+
skip(n) {
|
|
106
|
+
this.offset += n;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Encodes a message object into an XPC Buffer.
|
|
111
|
+
* A message is an object like:
|
|
112
|
+
* { flags: Number, id: bigint, body: Object|null }
|
|
113
|
+
*/
|
|
114
|
+
export function encodeMessage(message) {
|
|
115
|
+
const writer = new Writer();
|
|
116
|
+
// Provide defaults if id or body is not provided.
|
|
117
|
+
const messageId = message.id ?? BigInt(0);
|
|
118
|
+
const body = message.body === undefined ? null : message.body;
|
|
119
|
+
// Write the wrapper magic number.
|
|
120
|
+
writer.writeUInt32LE(WRAPPER_MAGIC);
|
|
121
|
+
if (body === null) {
|
|
122
|
+
writer.writeUInt32LE(message.flags);
|
|
123
|
+
writer.writeBigUInt64LE(BigInt(0));
|
|
124
|
+
writer.writeBigUInt64LE(messageId);
|
|
125
|
+
return writer.concat();
|
|
126
|
+
}
|
|
127
|
+
// Encode the message body (a dictionary) into a temporary buffer.
|
|
128
|
+
const bodyWriter = new Writer();
|
|
129
|
+
encodeDictionary(bodyWriter, body);
|
|
130
|
+
const bodyBuffer = bodyWriter.concat();
|
|
131
|
+
// Write header: flags, BodyLen (payload length + 8 for the body header), msg id.
|
|
132
|
+
writer.writeUInt32LE(message.flags);
|
|
133
|
+
writer.writeBigUInt64LE(BigInt(bodyBuffer.length + 8));
|
|
134
|
+
writer.writeBigUInt64LE(messageId);
|
|
135
|
+
// Write body header: object magic number and version.
|
|
136
|
+
writer.writeUInt32LE(OBJECT_MAGIC);
|
|
137
|
+
writer.writeUInt32LE(BODY_VERSION);
|
|
138
|
+
// Write the actual body payload.
|
|
139
|
+
writer.writeBuffer(bodyBuffer);
|
|
140
|
+
return writer.concat();
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Decodes an XPC Buffer into a message object.
|
|
144
|
+
* (Keep this function if you plan to handle incoming messages.)
|
|
145
|
+
*/
|
|
146
|
+
export function decodeMessage(buffer) {
|
|
147
|
+
const reader = new Reader(buffer);
|
|
148
|
+
const magic = reader.readUInt32LE();
|
|
149
|
+
if (magic !== WRAPPER_MAGIC) {
|
|
150
|
+
throw new Error(`Invalid wrapper magic: 0x${magic.toString(16)}`);
|
|
151
|
+
}
|
|
152
|
+
const flags = reader.readUInt32LE();
|
|
153
|
+
const bodyLen = reader.readBigUInt64LE();
|
|
154
|
+
const msgId = reader.readBigUInt64LE();
|
|
155
|
+
if (bodyLen === BigInt(0)) {
|
|
156
|
+
return { flags, id: msgId, body: null };
|
|
157
|
+
}
|
|
158
|
+
// Read body header.
|
|
159
|
+
const objMagic = reader.readUInt32LE();
|
|
160
|
+
const version = reader.readUInt32LE();
|
|
161
|
+
if (objMagic !== OBJECT_MAGIC) {
|
|
162
|
+
throw new Error(`Invalid object magic: 0x${objMagic.toString(16)}`);
|
|
163
|
+
}
|
|
164
|
+
if (version !== BODY_VERSION) {
|
|
165
|
+
throw new Error(`Unexpected body version: 0x${version.toString(16)}`);
|
|
166
|
+
}
|
|
167
|
+
// The remaining body is (bodyLen - 8) bytes.
|
|
168
|
+
const bodyPayloadLength = Number(bodyLen) - 8;
|
|
169
|
+
const bodyBuffer = reader.readBytes(bodyPayloadLength);
|
|
170
|
+
const decodedValue = decodeObject(new Reader(bodyBuffer));
|
|
171
|
+
// Ensure the decoded value is a dictionary
|
|
172
|
+
if (typeof decodedValue !== 'object' ||
|
|
173
|
+
decodedValue === null ||
|
|
174
|
+
Array.isArray(decodedValue)) {
|
|
175
|
+
throw new TypeError('Expected dictionary as message body');
|
|
176
|
+
}
|
|
177
|
+
return { flags, id: msgId, body: decodedValue };
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Encodes a JavaScript object (dictionary) into XPC format.
|
|
181
|
+
*/
|
|
182
|
+
function encodeDictionary(writer, dict) {
|
|
183
|
+
const inner = new Writer();
|
|
184
|
+
const keys = Object.keys(dict);
|
|
185
|
+
// Write the number of dictionary entries (uint32).
|
|
186
|
+
inner.writeUInt32LE(keys.length);
|
|
187
|
+
for (const key of keys) {
|
|
188
|
+
encodeDictionaryKey(inner, key);
|
|
189
|
+
encodeObject(inner, dict[key]);
|
|
190
|
+
}
|
|
191
|
+
const payload = inner.concat();
|
|
192
|
+
// Write dictionary type.
|
|
193
|
+
writer.writeUInt32LE(XPC_TYPES.dictionary);
|
|
194
|
+
// Write payload length (uint32).
|
|
195
|
+
writer.writeUInt32LE(payload.length);
|
|
196
|
+
writer.writeBuffer(payload);
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Encodes a dictionary key: writes the key string followed by a null terminator and padding.
|
|
200
|
+
*/
|
|
201
|
+
function encodeDictionaryKey(writer, key) {
|
|
202
|
+
const keyBuf = Buffer.from(key, 'utf8');
|
|
203
|
+
const len = keyBuf.length + 1; // +1 for null terminator.
|
|
204
|
+
writer.writeBuffer(keyBuf);
|
|
205
|
+
writer.writeByte(0); // null terminator.
|
|
206
|
+
const pad = calcPadding(len);
|
|
207
|
+
writer.writePadding(pad);
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Encodes a JavaScript value into XPC format.
|
|
211
|
+
* Supports: null, booleans, numbers (as int64 or double), strings, Date, Buffer/Uint8Array, arrays, and objects.
|
|
212
|
+
*/
|
|
213
|
+
function encodeObject(writer, value) {
|
|
214
|
+
if (value === null || value === undefined) {
|
|
215
|
+
writer.writeUInt32LE(XPC_TYPES.null);
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
if (typeof value === 'boolean') {
|
|
219
|
+
writer.writeUInt32LE(XPC_TYPES.bool);
|
|
220
|
+
writer.writeByte(value ? 1 : 0);
|
|
221
|
+
writer.writePadding(3); // 3 bytes padding.
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
if (typeof value === 'number') {
|
|
225
|
+
if (Number.isInteger(value)) {
|
|
226
|
+
writer.writeUInt32LE(XPC_TYPES.int64);
|
|
227
|
+
writer.writeBigInt64LE(BigInt(value));
|
|
228
|
+
}
|
|
229
|
+
else {
|
|
230
|
+
writer.writeUInt32LE(XPC_TYPES.double);
|
|
231
|
+
writer.writeDoubleLE(value);
|
|
232
|
+
}
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
if (typeof value === 'string') {
|
|
236
|
+
writer.writeUInt32LE(XPC_TYPES.string);
|
|
237
|
+
const strBuf = Buffer.from(value, 'utf8');
|
|
238
|
+
const len = strBuf.length + 1; // include null terminator.
|
|
239
|
+
writer.writeUInt32LE(len);
|
|
240
|
+
writer.writeBuffer(strBuf);
|
|
241
|
+
writer.writeByte(0);
|
|
242
|
+
const pad = calcPadding(len);
|
|
243
|
+
writer.writePadding(pad);
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
if (value instanceof Date) {
|
|
247
|
+
writer.writeUInt32LE(XPC_TYPES.date);
|
|
248
|
+
// Encode time in nanoseconds (Date.getTime() gives milliseconds).
|
|
249
|
+
writer.writeBigInt64LE(BigInt(value.getTime()) * BigInt(1000000));
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
if (Buffer.isBuffer(value) || value instanceof Uint8Array) {
|
|
253
|
+
const data = Buffer.isBuffer(value) ? value : Buffer.from(value);
|
|
254
|
+
writer.writeUInt32LE(XPC_TYPES.data);
|
|
255
|
+
writer.writeUInt32LE(data.length);
|
|
256
|
+
writer.writeBuffer(data);
|
|
257
|
+
const pad = calcPadding(data.length);
|
|
258
|
+
writer.writePadding(pad);
|
|
259
|
+
return;
|
|
260
|
+
}
|
|
261
|
+
if (Array.isArray(value)) {
|
|
262
|
+
const inner = new Writer();
|
|
263
|
+
for (let i = 0; i < value.length; i++) {
|
|
264
|
+
encodeObject(inner, value[i]);
|
|
265
|
+
}
|
|
266
|
+
const payload = inner.concat();
|
|
267
|
+
writer.writeUInt32LE(XPC_TYPES.array);
|
|
268
|
+
writer.writeUInt32LE(payload.length); // payload length.
|
|
269
|
+
writer.writeUInt32LE(value.length); // number of objects.
|
|
270
|
+
writer.writeBuffer(payload);
|
|
271
|
+
return;
|
|
272
|
+
}
|
|
273
|
+
if (typeof value === 'object') {
|
|
274
|
+
// Treat as a dictionary.
|
|
275
|
+
encodeDictionary(writer, value);
|
|
276
|
+
return;
|
|
277
|
+
}
|
|
278
|
+
throw new TypeError('Unsupported type: ' + typeof value);
|
|
279
|
+
}
|
|
280
|
+
/**
|
|
281
|
+
* Decodes an XPC object from the provided reader.
|
|
282
|
+
*/
|
|
283
|
+
function decodeObject(reader) {
|
|
284
|
+
const type = reader.readUInt32LE();
|
|
285
|
+
switch (type) {
|
|
286
|
+
case XPC_TYPES.null:
|
|
287
|
+
return null;
|
|
288
|
+
case XPC_TYPES.bool: {
|
|
289
|
+
const b = reader.readByte();
|
|
290
|
+
reader.skip(3);
|
|
291
|
+
return Boolean(b);
|
|
292
|
+
}
|
|
293
|
+
case XPC_TYPES.int64:
|
|
294
|
+
return Number(reader.readBigInt64LE());
|
|
295
|
+
case XPC_TYPES.uint64:
|
|
296
|
+
return Number(reader.readBigUInt64LE());
|
|
297
|
+
case XPC_TYPES.double:
|
|
298
|
+
return reader.readDoubleLE();
|
|
299
|
+
case XPC_TYPES.date: {
|
|
300
|
+
// Date is encoded as int64 nanoseconds.
|
|
301
|
+
const ns = reader.readBigInt64LE();
|
|
302
|
+
return new Date(Number(ns / BigInt(1000000)));
|
|
303
|
+
}
|
|
304
|
+
case XPC_TYPES.data: {
|
|
305
|
+
const dataLen = reader.readUInt32LE();
|
|
306
|
+
const data = reader.readBytes(dataLen);
|
|
307
|
+
const pad = calcPadding(dataLen);
|
|
308
|
+
reader.skip(pad);
|
|
309
|
+
return data;
|
|
310
|
+
}
|
|
311
|
+
case XPC_TYPES.string: {
|
|
312
|
+
const strLen = reader.readUInt32LE();
|
|
313
|
+
const strBuf = reader.readBytes(strLen);
|
|
314
|
+
// Remove the trailing null terminator.
|
|
315
|
+
const nullIndex = strBuf.indexOf(0);
|
|
316
|
+
const str = strBuf.slice(0, nullIndex).toString('utf8');
|
|
317
|
+
const pad = calcPadding(strLen);
|
|
318
|
+
reader.skip(pad);
|
|
319
|
+
return str;
|
|
320
|
+
}
|
|
321
|
+
case XPC_TYPES.uuid: {
|
|
322
|
+
const uuidBuf = reader.readBytes(16);
|
|
323
|
+
return uuidBuf.toString('hex'); // Return UUID as hex string.
|
|
324
|
+
}
|
|
325
|
+
case XPC_TYPES.array: {
|
|
326
|
+
const numElements = reader.readUInt32LE();
|
|
327
|
+
const arr = [];
|
|
328
|
+
for (let i = 0; i < numElements; i++) {
|
|
329
|
+
arr.push(decodeObject(reader));
|
|
330
|
+
}
|
|
331
|
+
return arr;
|
|
332
|
+
}
|
|
333
|
+
case XPC_TYPES.dictionary:
|
|
334
|
+
return decodeDictionary(reader);
|
|
335
|
+
// Note: fileTransfer type is not implemented here.
|
|
336
|
+
default:
|
|
337
|
+
throw new TypeError(`Unsupported xpc type: 0x${type.toString(16)}`);
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
/**
|
|
341
|
+
* Decodes a dictionary from the reader.
|
|
342
|
+
*/
|
|
343
|
+
function decodeDictionary(reader) {
|
|
344
|
+
const numEntries = reader.readUInt32LE();
|
|
345
|
+
const dict = {};
|
|
346
|
+
for (let i = 0; i < numEntries; i++) {
|
|
347
|
+
const key = readDictionaryKey(reader);
|
|
348
|
+
dict[key] = decodeObject(reader);
|
|
349
|
+
}
|
|
350
|
+
return dict;
|
|
351
|
+
}
|
|
352
|
+
/**
|
|
353
|
+
* Reads a dictionary key: reads bytes until a null terminator is encountered then skips padding.
|
|
354
|
+
*/
|
|
355
|
+
function readDictionaryKey(reader) {
|
|
356
|
+
const bytes = [];
|
|
357
|
+
while (true) {
|
|
358
|
+
const b = reader.readByte();
|
|
359
|
+
if (b === 0) {
|
|
360
|
+
break;
|
|
361
|
+
}
|
|
362
|
+
bytes.push(b);
|
|
363
|
+
}
|
|
364
|
+
const key = Buffer.from(bytes).toString('utf8');
|
|
365
|
+
const pad = calcPadding(key.length + 1);
|
|
366
|
+
reader.skip(pad);
|
|
367
|
+
return key;
|
|
368
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import type { TLSSocket } from 'tls';
|
|
2
|
+
import { type TunnelConnection } from 'tuntap-bridge';
|
|
3
|
+
/**
|
|
4
|
+
* A wrapper around the tunnel connection that
|
|
5
|
+
* maintains a registry of active tunnels that can be reused.
|
|
6
|
+
*/
|
|
7
|
+
declare class TunnelManagerService {
|
|
8
|
+
private tunnelRegistry;
|
|
9
|
+
/**
|
|
10
|
+
* Checks if a tunnel is already open for the given address
|
|
11
|
+
*
|
|
12
|
+
* @param address - The tunnel address to check
|
|
13
|
+
* @returns True if a tunnel is open for the address, false otherwise
|
|
14
|
+
*/
|
|
15
|
+
isTunnelOpen(address: string): boolean;
|
|
16
|
+
/**
|
|
17
|
+
* Gets all active tunnels
|
|
18
|
+
*
|
|
19
|
+
* @returns Array of active tunnel addresses
|
|
20
|
+
*/
|
|
21
|
+
getActiveTunnels(): string[];
|
|
22
|
+
/**
|
|
23
|
+
* Creates a RemoteXPC connection for the specified device.
|
|
24
|
+
*
|
|
25
|
+
* @param address - The address of the tunnel
|
|
26
|
+
* @param rsdPort - The RSD port of the tunnel
|
|
27
|
+
* @returns A promise that resolves to the RemoteXPC connection
|
|
28
|
+
*/
|
|
29
|
+
createRemoteXPCConnection(address: string, rsdPort: number): Promise<any>;
|
|
30
|
+
/**
|
|
31
|
+
* Establishes a tunnel connection if not already connected.
|
|
32
|
+
* If a tunnel is already open for the same address, it will be reused.
|
|
33
|
+
*
|
|
34
|
+
* @param secureServiceSocket - The secure service socket used to create the tunnel.
|
|
35
|
+
* @returns A promise that resolves to the tunnel connection instance.
|
|
36
|
+
*/
|
|
37
|
+
getTunnel(secureServiceSocket: TLSSocket): Promise<TunnelConnection>;
|
|
38
|
+
/**
|
|
39
|
+
* Gets an existing tunnel by address if available
|
|
40
|
+
*
|
|
41
|
+
* @param address - The tunnel address
|
|
42
|
+
* @returns The tunnel if found and active, null otherwise
|
|
43
|
+
*/
|
|
44
|
+
getTunnelByAddress(address: string): TunnelConnection | null;
|
|
45
|
+
/**
|
|
46
|
+
* Closes a specific tunnel connection by address.
|
|
47
|
+
*
|
|
48
|
+
* @param address - The address of the tunnel to close
|
|
49
|
+
* @returns A promise that resolves when the tunnel is closed.
|
|
50
|
+
*/
|
|
51
|
+
closeTunnelByAddress(address: string): Promise<void>;
|
|
52
|
+
/**
|
|
53
|
+
* Closes all tunnel connections and resets the registry.
|
|
54
|
+
*
|
|
55
|
+
* @returns A promise that resolves when all tunnels are closed.
|
|
56
|
+
*/
|
|
57
|
+
closeAllTunnels(): Promise<void>;
|
|
58
|
+
/**
|
|
59
|
+
* Closes the tunnel connection for backward compatibility.
|
|
60
|
+
* This method is kept for backward compatibility with existing code.
|
|
61
|
+
*
|
|
62
|
+
* @returns A promise that resolves when all tunnels are closed.
|
|
63
|
+
*/
|
|
64
|
+
closeTunnel(): Promise<void>;
|
|
65
|
+
}
|
|
66
|
+
export declare const TunnelManager: TunnelManagerService;
|
|
67
|
+
export { PacketStreamServer } from './packet-stream-server.js';
|
|
68
|
+
export { PacketStreamClient } from './packet-stream-client.js';
|
|
69
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/lib/tunnel/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,KAAK,CAAC;AACrC,OAAO,EAAE,KAAK,gBAAgB,EAA2B,MAAM,eAAe,CAAC;AAwB/E;;;GAGG;AACH,cAAM,oBAAoB;IAExB,OAAO,CAAC,cAAc,CAA+C;IAErE;;;;;OAKG;IACH,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO;IAKtC;;;;OAIG;IACH,gBAAgB,IAAI,MAAM,EAAE;IAM5B;;;;;;OAMG;IACG,yBAAyB,CAC7B,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,GAAG,CAAC;IAyCf;;;;;;OAMG;IACG,SAAS,CAAC,mBAAmB,EAAE,SAAS,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAmD1E;;;;;OAKG;IACH,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,gBAAgB,GAAG,IAAI;IAU5D;;;;;OAKG;IACG,oBAAoB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAgC1D;;;;OAIG;IACG,eAAe,IAAI,OAAO,CAAC,IAAI,CAAC;IAatC;;;;;OAKG;IACG,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC;CAGnC;AAGD,eAAO,MAAM,aAAa,sBAA6B,CAAC;AAExD,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAC/D,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC"}
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
import { logger } from '@appium/support';
|
|
2
|
+
import { connectToTunnelLockdown } from 'tuntap-bridge';
|
|
3
|
+
import RemoteXpcConnection from '../remote-xpc/remote-xpc-connection.js';
|
|
4
|
+
const log = logger.getLogger('TunnelManager');
|
|
5
|
+
/**
|
|
6
|
+
* A wrapper around the tunnel connection that
|
|
7
|
+
* maintains a registry of active tunnels that can be reused.
|
|
8
|
+
*/
|
|
9
|
+
class TunnelManagerService {
|
|
10
|
+
// Map of tunnel address to tunnel registry entry
|
|
11
|
+
tunnelRegistry = new Map();
|
|
12
|
+
/**
|
|
13
|
+
* Checks if a tunnel is already open for the given address
|
|
14
|
+
*
|
|
15
|
+
* @param address - The tunnel address to check
|
|
16
|
+
* @returns True if a tunnel is open for the address, false otherwise
|
|
17
|
+
*/
|
|
18
|
+
isTunnelOpen(address) {
|
|
19
|
+
const entry = this.tunnelRegistry.get(address);
|
|
20
|
+
return Boolean(entry?.isActive);
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Gets all active tunnels
|
|
24
|
+
*
|
|
25
|
+
* @returns Array of active tunnel addresses
|
|
26
|
+
*/
|
|
27
|
+
getActiveTunnels() {
|
|
28
|
+
return Array.from(this.tunnelRegistry.entries())
|
|
29
|
+
.filter(([, entry]) => entry.isActive)
|
|
30
|
+
.map(([address]) => address);
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Creates a RemoteXPC connection for the specified device.
|
|
34
|
+
*
|
|
35
|
+
* @param address - The address of the tunnel
|
|
36
|
+
* @param rsdPort - The RSD port of the tunnel
|
|
37
|
+
* @returns A promise that resolves to the RemoteXPC connection
|
|
38
|
+
*/
|
|
39
|
+
async createRemoteXPCConnection(address, rsdPort) {
|
|
40
|
+
try {
|
|
41
|
+
const remoteXPC = new RemoteXpcConnection([address, rsdPort]);
|
|
42
|
+
// Connect to RemoteXPC with delay between retries
|
|
43
|
+
let retries = 3;
|
|
44
|
+
let lastError;
|
|
45
|
+
while (retries > 0) {
|
|
46
|
+
try {
|
|
47
|
+
await remoteXPC.connect();
|
|
48
|
+
// Update the registry entry with the RemoteXPC connection
|
|
49
|
+
const entry = this.tunnelRegistry.get(address);
|
|
50
|
+
if (entry) {
|
|
51
|
+
entry.remoteXPC = remoteXPC;
|
|
52
|
+
}
|
|
53
|
+
return remoteXPC;
|
|
54
|
+
}
|
|
55
|
+
catch (error) {
|
|
56
|
+
lastError = error;
|
|
57
|
+
log.warn(`RemoteXPC connection attempt failed (${retries} retries left): ${error}`);
|
|
58
|
+
retries--;
|
|
59
|
+
// Wait before retrying
|
|
60
|
+
if (retries > 0) {
|
|
61
|
+
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
// All retries failed
|
|
66
|
+
throw lastError || new Error('Failed to connect to RemoteXPC');
|
|
67
|
+
}
|
|
68
|
+
catch (error) {
|
|
69
|
+
log.error(`Error for device ${address}: ${error}`);
|
|
70
|
+
throw error;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Establishes a tunnel connection if not already connected.
|
|
75
|
+
* If a tunnel is already open for the same address, it will be reused.
|
|
76
|
+
*
|
|
77
|
+
* @param secureServiceSocket - The secure service socket used to create the tunnel.
|
|
78
|
+
* @returns A promise that resolves to the tunnel connection instance.
|
|
79
|
+
*/
|
|
80
|
+
async getTunnel(secureServiceSocket) {
|
|
81
|
+
// Create a new tunnel
|
|
82
|
+
const tunnel = await connectToTunnelLockdown(secureServiceSocket);
|
|
83
|
+
// Check if we already have an active tunnel for this address
|
|
84
|
+
const existingTunnel = this.tunnelRegistry.get(tunnel.Address);
|
|
85
|
+
if (existingTunnel?.isActive) {
|
|
86
|
+
log.info(`Reusing existing tunnel for address: ${tunnel.Address}`);
|
|
87
|
+
// Verify the tunnel is still functional
|
|
88
|
+
try {
|
|
89
|
+
// A simple check to see if the tunnel is still functional
|
|
90
|
+
if (tunnel.tunnelManager?.emit instanceof Function) {
|
|
91
|
+
// Close the new tunnel since we're reusing an existing one
|
|
92
|
+
try {
|
|
93
|
+
await tunnel.closer();
|
|
94
|
+
}
|
|
95
|
+
catch (error) {
|
|
96
|
+
log.warn(`Error closing redundant tunnel: ${error}`);
|
|
97
|
+
}
|
|
98
|
+
// Update the last used timestamp
|
|
99
|
+
existingTunnel.lastUsed = Date.now();
|
|
100
|
+
return existingTunnel.tunnel;
|
|
101
|
+
}
|
|
102
|
+
else {
|
|
103
|
+
log.warn('Existing tunnel appears to be non-functional, creating a new one');
|
|
104
|
+
// Mark the existing tunnel as inactive
|
|
105
|
+
existingTunnel.isActive = false;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
catch (error) {
|
|
109
|
+
log.warn(`Error checking tunnel functionality: ${error}, creating a new one`);
|
|
110
|
+
// Mark the existing tunnel as inactive
|
|
111
|
+
existingTunnel.isActive = false;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
// Register the new tunnel
|
|
115
|
+
log.info(`Creating new tunnel for address: ${tunnel.Address}`);
|
|
116
|
+
this.tunnelRegistry.set(tunnel.Address, {
|
|
117
|
+
tunnel,
|
|
118
|
+
lastUsed: Date.now(),
|
|
119
|
+
isActive: true,
|
|
120
|
+
});
|
|
121
|
+
return tunnel;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Gets an existing tunnel by address if available
|
|
125
|
+
*
|
|
126
|
+
* @param address - The tunnel address
|
|
127
|
+
* @returns The tunnel if found and active, null otherwise
|
|
128
|
+
*/
|
|
129
|
+
getTunnelByAddress(address) {
|
|
130
|
+
const entry = this.tunnelRegistry.get(address);
|
|
131
|
+
if (entry?.isActive) {
|
|
132
|
+
// Update the last used timestamp
|
|
133
|
+
entry.lastUsed = Date.now();
|
|
134
|
+
return entry.tunnel;
|
|
135
|
+
}
|
|
136
|
+
return null;
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Closes a specific tunnel connection by address.
|
|
140
|
+
*
|
|
141
|
+
* @param address - The address of the tunnel to close
|
|
142
|
+
* @returns A promise that resolves when the tunnel is closed.
|
|
143
|
+
*/
|
|
144
|
+
async closeTunnelByAddress(address) {
|
|
145
|
+
const entry = this.tunnelRegistry.get(address);
|
|
146
|
+
if (entry?.isActive) {
|
|
147
|
+
try {
|
|
148
|
+
// Close RemoteXPC connection if it exists
|
|
149
|
+
if (entry.remoteXPC) {
|
|
150
|
+
try {
|
|
151
|
+
await entry.remoteXPC.close();
|
|
152
|
+
log.info(`Closed RemoteXPC connection for address: ${address}`);
|
|
153
|
+
}
|
|
154
|
+
catch (error) {
|
|
155
|
+
log.error(`Error closing RemoteXPC connection for address ${address}: ${error}`);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
// Close the tunnel
|
|
159
|
+
try {
|
|
160
|
+
await entry.tunnel.closer();
|
|
161
|
+
log.info(`Closed tunnel for address: ${address}`);
|
|
162
|
+
}
|
|
163
|
+
catch (error) {
|
|
164
|
+
log.error(`Error closing tunnel for address ${address}: ${error}`);
|
|
165
|
+
}
|
|
166
|
+
finally {
|
|
167
|
+
entry.isActive = false;
|
|
168
|
+
log.info(`Marked tunnel for address ${address} as inactive`);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
catch (error) {
|
|
172
|
+
log.error(`Error closing tunnel for address ${address}: ${error}`);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Closes all tunnel connections and resets the registry.
|
|
178
|
+
*
|
|
179
|
+
* @returns A promise that resolves when all tunnels are closed.
|
|
180
|
+
*/
|
|
181
|
+
async closeAllTunnels() {
|
|
182
|
+
const closePromises = Array.from(this.tunnelRegistry.entries())
|
|
183
|
+
.filter(([, entry]) => entry.isActive)
|
|
184
|
+
.map(([address]) => this.closeTunnelByAddress(address));
|
|
185
|
+
if (closePromises.length > 0) {
|
|
186
|
+
await Promise.all(closePromises);
|
|
187
|
+
}
|
|
188
|
+
this.tunnelRegistry.clear();
|
|
189
|
+
log.info('All tunnels closed');
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Closes the tunnel connection for backward compatibility.
|
|
193
|
+
* This method is kept for backward compatibility with existing code.
|
|
194
|
+
*
|
|
195
|
+
* @returns A promise that resolves when all tunnels are closed.
|
|
196
|
+
*/
|
|
197
|
+
async closeTunnel() {
|
|
198
|
+
return this.closeAllTunnels();
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
// Create and export the singleton instance
|
|
202
|
+
export const TunnelManager = new TunnelManagerService();
|
|
203
|
+
// Export packet streaming IPC functionality
|
|
204
|
+
export { PacketStreamServer } from './packet-stream-server.js';
|
|
205
|
+
export { PacketStreamClient } from './packet-stream-client.js';
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { EventEmitter } from 'events';
|
|
2
|
+
import type { PacketConsumer } from 'tuntap-bridge';
|
|
3
|
+
/**
|
|
4
|
+
* Client that connects to a PacketStreamServer to receive packet data
|
|
5
|
+
* Implements the PacketSource interface required by SyslogService
|
|
6
|
+
*/
|
|
7
|
+
export declare class PacketStreamClient extends EventEmitter {
|
|
8
|
+
private readonly host;
|
|
9
|
+
private readonly port;
|
|
10
|
+
private socket;
|
|
11
|
+
private readonly packetConsumers;
|
|
12
|
+
private buffer;
|
|
13
|
+
private connected;
|
|
14
|
+
constructor(host: string, port: number);
|
|
15
|
+
connect(): Promise<void>;
|
|
16
|
+
isConnected(): boolean;
|
|
17
|
+
disconnect(): Promise<void>;
|
|
18
|
+
addPacketConsumer(consumer: PacketConsumer): void;
|
|
19
|
+
removePacketConsumer(consumer: PacketConsumer): void;
|
|
20
|
+
/**
|
|
21
|
+
* Handle incoming data from the socket
|
|
22
|
+
*/
|
|
23
|
+
private handleData;
|
|
24
|
+
/**
|
|
25
|
+
* Process buffered data to extract complete messages
|
|
26
|
+
*/
|
|
27
|
+
private processBuffer;
|
|
28
|
+
/**
|
|
29
|
+
* Extract message length from buffer
|
|
30
|
+
* @returns Message length or null if invalid
|
|
31
|
+
*/
|
|
32
|
+
private extractMessageLength;
|
|
33
|
+
/**
|
|
34
|
+
* Process a single message
|
|
35
|
+
*/
|
|
36
|
+
private processMessage;
|
|
37
|
+
/**
|
|
38
|
+
* Parse packet data from message buffer
|
|
39
|
+
*/
|
|
40
|
+
private parsePacket;
|
|
41
|
+
/**
|
|
42
|
+
* Notify all packet consumers
|
|
43
|
+
*/
|
|
44
|
+
private notifyConsumers;
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=packet-stream-client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"packet-stream-client.d.ts","sourceRoot":"","sources":["../../../../src/lib/tunnel/packet-stream-client.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AAEtC,OAAO,KAAK,EAAE,cAAc,EAAc,MAAM,eAAe,CAAC;AAShE;;;GAGG;AACH,qBAAa,kBAAmB,SAAQ,YAAY;IAOhD,OAAO,CAAC,QAAQ,CAAC,IAAI;IACrB,OAAO,CAAC,QAAQ,CAAC,IAAI;IAPvB,OAAO,CAAC,MAAM,CAAuB;IACrC,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAkC;IAClE,OAAO,CAAC,MAAM,CAA2B;IACzC,OAAO,CAAC,SAAS,CAAS;gBAGP,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM;IAKzB,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAwC9B,WAAW,IAAI,OAAO;IAIhB,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IASjC,iBAAiB,CAAC,QAAQ,EAAE,cAAc,GAAG,IAAI;IAIjD,oBAAoB,CAAC,QAAQ,EAAE,cAAc,GAAG,IAAI;IAIpD;;OAEG;IACH,OAAO,CAAC,UAAU;IAKlB;;OAEG;IACH,OAAO,CAAC,aAAa;IA2BrB;;;OAGG;IACH,OAAO,CAAC,oBAAoB;IAc5B;;OAEG;IACH,OAAO,CAAC,cAAc;IAStB;;OAEG;IACH,OAAO,CAAC,WAAW;IAWnB;;OAEG;IACH,OAAO,CAAC,eAAe;CASxB"}
|