appium-ios-remotexpc 0.0.3 → 0.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (192) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/build/src/base-plist-service.d.ts +51 -0
  3. package/build/src/base-plist-service.d.ts.map +1 -0
  4. package/build/src/base-plist-service.js +61 -0
  5. package/build/src/base-socket-service.d.ts +15 -0
  6. package/build/src/base-socket-service.d.ts.map +1 -0
  7. package/build/src/base-socket-service.js +46 -0
  8. package/build/src/index.d.ts +9 -0
  9. package/build/src/index.d.ts.map +1 -0
  10. package/build/src/index.js +7 -0
  11. package/build/src/lib/apple-tv/constants.d.ts +49 -0
  12. package/build/src/lib/apple-tv/constants.d.ts.map +1 -0
  13. package/build/src/lib/apple-tv/constants.js +71 -0
  14. package/build/src/lib/apple-tv/errors.d.ts +17 -0
  15. package/build/src/lib/apple-tv/errors.d.ts.map +1 -0
  16. package/build/src/lib/apple-tv/errors.js +30 -0
  17. package/build/src/lib/apple-tv/tlv/decoder.d.ts +19 -0
  18. package/build/src/lib/apple-tv/tlv/decoder.d.ts.map +1 -0
  19. package/build/src/lib/apple-tv/tlv/decoder.js +49 -0
  20. package/build/src/lib/apple-tv/tlv/encoder.d.ts +10 -0
  21. package/build/src/lib/apple-tv/tlv/encoder.d.ts.map +1 -0
  22. package/build/src/lib/apple-tv/tlv/encoder.js +20 -0
  23. package/build/src/lib/apple-tv/tlv/index.d.ts +4 -0
  24. package/build/src/lib/apple-tv/tlv/index.d.ts.map +1 -0
  25. package/build/src/lib/apple-tv/tlv/index.js +3 -0
  26. package/build/src/lib/apple-tv/tlv/pairing-tlv.d.ts +14 -0
  27. package/build/src/lib/apple-tv/tlv/pairing-tlv.d.ts.map +1 -0
  28. package/build/src/lib/apple-tv/tlv/pairing-tlv.js +27 -0
  29. package/build/src/lib/apple-tv/types.d.ts +36 -0
  30. package/build/src/lib/apple-tv/types.d.ts.map +1 -0
  31. package/build/src/lib/apple-tv/types.js +1 -0
  32. package/build/src/lib/apple-tv/utils/buffer-utils.d.ts +40 -0
  33. package/build/src/lib/apple-tv/utils/buffer-utils.d.ts.map +1 -0
  34. package/build/src/lib/apple-tv/utils/buffer-utils.js +76 -0
  35. package/build/src/lib/apple-tv/utils/index.d.ts +3 -0
  36. package/build/src/lib/apple-tv/utils/index.d.ts.map +1 -0
  37. package/build/src/lib/apple-tv/utils/index.js +2 -0
  38. package/build/src/lib/apple-tv/utils/uuid-generator.d.ts +9 -0
  39. package/build/src/lib/apple-tv/utils/uuid-generator.d.ts.map +1 -0
  40. package/build/src/lib/apple-tv/utils/uuid-generator.js +36 -0
  41. package/build/src/lib/lockdown/index.d.ts +87 -0
  42. package/build/src/lib/lockdown/index.d.ts.map +1 -0
  43. package/build/src/lib/lockdown/index.js +324 -0
  44. package/build/src/lib/pair-record/index.d.ts +3 -0
  45. package/build/src/lib/pair-record/index.d.ts.map +1 -0
  46. package/build/src/lib/pair-record/index.js +2 -0
  47. package/build/src/lib/pair-record/pair-record.d.ts +48 -0
  48. package/build/src/lib/pair-record/pair-record.d.ts.map +1 -0
  49. package/build/src/lib/pair-record/pair-record.js +85 -0
  50. package/build/src/lib/plist/binary-plist-creator.d.ts +14 -0
  51. package/build/src/lib/plist/binary-plist-creator.d.ts.map +1 -0
  52. package/build/src/lib/plist/binary-plist-creator.js +475 -0
  53. package/build/src/lib/plist/binary-plist-parser.d.ts +14 -0
  54. package/build/src/lib/plist/binary-plist-parser.d.ts.map +1 -0
  55. package/build/src/lib/plist/binary-plist-parser.js +449 -0
  56. package/build/src/lib/plist/constants.d.ts +36 -0
  57. package/build/src/lib/plist/constants.d.ts.map +1 -0
  58. package/build/src/lib/plist/constants.js +43 -0
  59. package/build/src/lib/plist/index.d.ts +14 -0
  60. package/build/src/lib/plist/index.d.ts.map +1 -0
  61. package/build/src/lib/plist/index.js +16 -0
  62. package/build/src/lib/plist/length-based-splitter.d.ts +43 -0
  63. package/build/src/lib/plist/length-based-splitter.d.ts.map +1 -0
  64. package/build/src/lib/plist/length-based-splitter.js +228 -0
  65. package/build/src/lib/plist/plist-creator.d.ts +8 -0
  66. package/build/src/lib/plist/plist-creator.d.ts.map +1 -0
  67. package/build/src/lib/plist/plist-creator.js +33 -0
  68. package/build/src/lib/plist/plist-decoder.d.ts +25 -0
  69. package/build/src/lib/plist/plist-decoder.d.ts.map +1 -0
  70. package/build/src/lib/plist/plist-decoder.js +103 -0
  71. package/build/src/lib/plist/plist-encoder.d.ts +10 -0
  72. package/build/src/lib/plist/plist-encoder.d.ts.map +1 -0
  73. package/build/src/lib/plist/plist-encoder.js +27 -0
  74. package/build/src/lib/plist/plist-parser.d.ts +9 -0
  75. package/build/src/lib/plist/plist-parser.d.ts.map +1 -0
  76. package/build/src/lib/plist/plist-parser.js +109 -0
  77. package/build/src/lib/plist/plist-service.d.ts +86 -0
  78. package/build/src/lib/plist/plist-service.d.ts.map +1 -0
  79. package/build/src/lib/plist/plist-service.js +180 -0
  80. package/build/src/lib/plist/unified-plist-creator.d.ts +9 -0
  81. package/build/src/lib/plist/unified-plist-creator.d.ts.map +1 -0
  82. package/build/src/lib/plist/unified-plist-creator.js +14 -0
  83. package/build/src/lib/plist/unified-plist-parser.d.ts +8 -0
  84. package/build/src/lib/plist/unified-plist-parser.d.ts.map +1 -0
  85. package/build/src/lib/plist/unified-plist-parser.js +23 -0
  86. package/build/src/lib/plist/utils.d.ts +97 -0
  87. package/build/src/lib/plist/utils.d.ts.map +1 -0
  88. package/build/src/lib/plist/utils.js +287 -0
  89. package/build/src/lib/remote-xpc/constants.d.ts +20 -0
  90. package/build/src/lib/remote-xpc/constants.d.ts.map +1 -0
  91. package/build/src/lib/remote-xpc/constants.js +21 -0
  92. package/build/src/lib/remote-xpc/handshake-frames.d.ts +74 -0
  93. package/build/src/lib/remote-xpc/handshake-frames.d.ts.map +1 -0
  94. package/build/src/lib/remote-xpc/handshake-frames.js +285 -0
  95. package/build/src/lib/remote-xpc/handshake.d.ts +14 -0
  96. package/build/src/lib/remote-xpc/handshake.d.ts.map +1 -0
  97. package/build/src/lib/remote-xpc/handshake.js +95 -0
  98. package/build/src/lib/remote-xpc/remote-xpc-connection.d.ts +55 -0
  99. package/build/src/lib/remote-xpc/remote-xpc-connection.d.ts.map +1 -0
  100. package/build/src/lib/remote-xpc/remote-xpc-connection.js +365 -0
  101. package/build/src/lib/remote-xpc/xpc-protocol.d.ts +22 -0
  102. package/build/src/lib/remote-xpc/xpc-protocol.d.ts.map +1 -0
  103. package/build/src/lib/remote-xpc/xpc-protocol.js +368 -0
  104. package/build/src/lib/tunnel/index.d.ts +69 -0
  105. package/build/src/lib/tunnel/index.d.ts.map +1 -0
  106. package/build/src/lib/tunnel/index.js +205 -0
  107. package/build/src/lib/tunnel/packet-stream-client.d.ts +46 -0
  108. package/build/src/lib/tunnel/packet-stream-client.d.ts.map +1 -0
  109. package/build/src/lib/tunnel/packet-stream-client.js +152 -0
  110. package/build/src/lib/tunnel/packet-stream-server.d.ts +37 -0
  111. package/build/src/lib/tunnel/packet-stream-server.d.ts.map +1 -0
  112. package/build/src/lib/tunnel/packet-stream-server.js +109 -0
  113. package/build/src/lib/tunnel/tunnel-api-client.d.ts +85 -0
  114. package/build/src/lib/tunnel/tunnel-api-client.d.ts.map +1 -0
  115. package/build/src/lib/tunnel/tunnel-api-client.js +207 -0
  116. package/build/src/lib/tunnel/tunnel-registry-server.d.ts +68 -0
  117. package/build/src/lib/tunnel/tunnel-registry-server.d.ts.map +1 -0
  118. package/build/src/lib/tunnel/tunnel-registry-server.js +351 -0
  119. package/build/src/lib/types.d.ts +238 -0
  120. package/build/src/lib/types.d.ts.map +1 -0
  121. package/build/src/lib/types.js +4 -0
  122. package/build/src/lib/usbmux/index.d.ts +177 -0
  123. package/build/src/lib/usbmux/index.d.ts.map +1 -0
  124. package/build/src/lib/usbmux/index.js +490 -0
  125. package/build/src/lib/usbmux/usbmux-decoder.d.ts +19 -0
  126. package/build/src/lib/usbmux/usbmux-decoder.d.ts.map +1 -0
  127. package/build/src/lib/usbmux/usbmux-decoder.js +38 -0
  128. package/build/src/lib/usbmux/usbmux-encoder.d.ts +12 -0
  129. package/build/src/lib/usbmux/usbmux-encoder.d.ts.map +1 -0
  130. package/build/src/lib/usbmux/usbmux-encoder.js +32 -0
  131. package/build/src/service-connection.d.ts +34 -0
  132. package/build/src/service-connection.d.ts.map +1 -0
  133. package/build/src/service-connection.js +51 -0
  134. package/build/src/services/index.d.ts +6 -0
  135. package/build/src/services/index.d.ts.map +1 -0
  136. package/build/src/services/index.js +5 -0
  137. package/build/src/services/ios/base-service.d.ts +35 -0
  138. package/build/src/services/ios/base-service.d.ts.map +1 -0
  139. package/build/src/services/ios/base-service.js +55 -0
  140. package/build/src/services/ios/diagnostic-service/index.d.ts +46 -0
  141. package/build/src/services/ios/diagnostic-service/index.d.ts.map +1 -0
  142. package/build/src/services/ios/diagnostic-service/index.js +169 -0
  143. package/build/src/services/ios/diagnostic-service/keys.d.ts +5 -0
  144. package/build/src/services/ios/diagnostic-service/keys.d.ts.map +1 -0
  145. package/build/src/services/ios/diagnostic-service/keys.js +770 -0
  146. package/build/src/services/ios/syslog-service/index.d.ts +91 -0
  147. package/build/src/services/ios/syslog-service/index.d.ts.map +1 -0
  148. package/build/src/services/ios/syslog-service/index.js +323 -0
  149. package/build/src/services/ios/tunnel-service/index.d.ts +17 -0
  150. package/build/src/services/ios/tunnel-service/index.d.ts.map +1 -0
  151. package/build/src/services/ios/tunnel-service/index.js +57 -0
  152. package/build/src/services.d.ts +14 -0
  153. package/build/src/services.d.ts.map +1 -0
  154. package/build/src/services.js +48 -0
  155. package/package.json +12 -3
  156. package/.github/dependabot.yml +0 -38
  157. package/.github/workflows/format-check.yml +0 -43
  158. package/.github/workflows/lint-and-build.yml +0 -40
  159. package/.github/workflows/pr-title.yml +0 -16
  160. package/.github/workflows/publish.js.yml +0 -43
  161. package/.github/workflows/test-validation.yml +0 -40
  162. package/.mocharc.json +0 -8
  163. package/.prettierignore +0 -3
  164. package/.prettierrc +0 -17
  165. package/.releaserc +0 -48
  166. package/assets/images/ios-arch.png +0 -0
  167. package/eslint.config.js +0 -45
  168. package/npm-shrinkwrap.json +0 -2711
  169. package/test/integration/diagnostics-test.ts +0 -44
  170. package/test/integration/read-pair-record-test.ts +0 -39
  171. package/test/integration/tunnel-test.ts +0 -104
  172. package/test/unit/apple-tv/tlv/decoder.spec.ts +0 -144
  173. package/test/unit/apple-tv/tlv/encoder.spec.ts +0 -91
  174. package/test/unit/apple-tv/tlv/pairing-tlv.spec.ts +0 -101
  175. package/test/unit/apple-tv/tlv/tlv-integration.spec.ts +0 -146
  176. package/test/unit/apple-tv/utils/buffer-utils.spec.ts +0 -74
  177. package/test/unit/apple-tv/utils/uuid-generator.spec.ts +0 -39
  178. package/test/unit/fixtures/index.ts +0 -88
  179. package/test/unit/fixtures/usbmuxconnectmessage.bin +0 -0
  180. package/test/unit/fixtures/usbmuxlistdevicemessage.bin +0 -0
  181. package/test/unit/plist/error-handling.spec.ts +0 -101
  182. package/test/unit/plist/fixtures/sample.binary.plist +0 -0
  183. package/test/unit/plist/fixtures/sample.xml.plist +0 -38
  184. package/test/unit/plist/plist-parser.spec.ts +0 -283
  185. package/test/unit/plist/plist.spec.ts +0 -205
  186. package/test/unit/plist/tag-position-handling.spec.ts +0 -90
  187. package/test/unit/plist/unified-plist-parser.spec.ts +0 -227
  188. package/test/unit/plist/utils.spec.ts +0 -249
  189. package/test/unit/plist/xml-cleaning.spec.ts +0 -60
  190. package/test/unit/tunnel/tunnel-registry-server.spec.ts +0 -194
  191. package/test/unit/usbmux/usbmux-specs.ts +0 -71
  192. 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"}