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.
- package/CHANGELOG.md +6 -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 +49 -0
- package/build/src/lib/apple-tv/constants.d.ts.map +1 -0
- package/build/src/lib/apple-tv/constants.js +71 -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/.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,365 @@
|
|
|
1
|
+
import { logger } from '@appium/support';
|
|
2
|
+
import net from 'node:net';
|
|
3
|
+
import Handshake from './handshake.js';
|
|
4
|
+
const log = logger.getLogger('RemoteXpcConnection');
|
|
5
|
+
// Timeout constants
|
|
6
|
+
const CONNECTION_TIMEOUT_MS = 30000; // 30 seconds
|
|
7
|
+
const SERVICE_EXTRACTION_TIMEOUT_MS = 5000; // 5 seconds
|
|
8
|
+
const HANDSHAKE_DELAY_MS = 100; // 100 milliseconds
|
|
9
|
+
const SERVICE_AFTER_HANDSHAKE_TIMEOUT_MS = 10000; // 10 seconds
|
|
10
|
+
const SOCKET_CLOSE_TIMEOUT_MS = 1000; // 1 second
|
|
11
|
+
const SOCKET_END_TIMEOUT_MS = 500; // 0.5 seconds
|
|
12
|
+
const SOCKET_WRITE_TIMEOUT_MS = 500; // 0.5 seconds
|
|
13
|
+
class RemoteXpcConnection {
|
|
14
|
+
_address;
|
|
15
|
+
_socket;
|
|
16
|
+
_handshake;
|
|
17
|
+
_isConnected;
|
|
18
|
+
_services;
|
|
19
|
+
constructor(address) {
|
|
20
|
+
this._address = address;
|
|
21
|
+
this._socket = undefined;
|
|
22
|
+
this._handshake = undefined;
|
|
23
|
+
this._isConnected = false;
|
|
24
|
+
this._services = undefined;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Connect to the remote device and perform handshake
|
|
28
|
+
* @returns Promise that resolves with the list of available services
|
|
29
|
+
*/
|
|
30
|
+
async connect() {
|
|
31
|
+
if (this._isConnected) {
|
|
32
|
+
throw new Error('Already connected');
|
|
33
|
+
}
|
|
34
|
+
return new Promise((resolve, reject) => {
|
|
35
|
+
// Set a timeout for the entire connection process
|
|
36
|
+
const connectionTimeout = setTimeout(() => {
|
|
37
|
+
if (this._socket) {
|
|
38
|
+
this._socket.destroy();
|
|
39
|
+
}
|
|
40
|
+
reject(new Error(`Connection timed out after ${CONNECTION_TIMEOUT_MS / 1000} seconds`));
|
|
41
|
+
}, CONNECTION_TIMEOUT_MS);
|
|
42
|
+
// Set a timeout for service extraction
|
|
43
|
+
let serviceExtractionTimeout;
|
|
44
|
+
const clearTimeouts = () => {
|
|
45
|
+
clearTimeout(connectionTimeout);
|
|
46
|
+
if (serviceExtractionTimeout) {
|
|
47
|
+
clearTimeout(serviceExtractionTimeout);
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
try {
|
|
51
|
+
this._socket = net.connect({
|
|
52
|
+
host: this._address[0],
|
|
53
|
+
port: this._address[1],
|
|
54
|
+
family: 6,
|
|
55
|
+
});
|
|
56
|
+
this._socket.setNoDelay(true);
|
|
57
|
+
this._socket.setKeepAlive(true);
|
|
58
|
+
// Buffer to accumulate data
|
|
59
|
+
let accumulatedData = Buffer.alloc(0);
|
|
60
|
+
this._socket.once('error', (error) => {
|
|
61
|
+
log.error(`Connection error: ${error}`);
|
|
62
|
+
this._isConnected = false;
|
|
63
|
+
clearTimeouts();
|
|
64
|
+
reject(error);
|
|
65
|
+
});
|
|
66
|
+
// Handle incoming data
|
|
67
|
+
this._socket.on('data', (data) => {
|
|
68
|
+
if (Buffer.isBuffer(data) || typeof data === 'string') {
|
|
69
|
+
const buffer = Buffer.isBuffer(data)
|
|
70
|
+
? data
|
|
71
|
+
: Buffer.from(data, 'hex');
|
|
72
|
+
// Accumulate data
|
|
73
|
+
accumulatedData = Buffer.concat([accumulatedData, buffer]);
|
|
74
|
+
// Check if we have enough data to extract services
|
|
75
|
+
// Don't rely solely on buffer length, also check for service patterns
|
|
76
|
+
const dataStr = accumulatedData.toString('utf8');
|
|
77
|
+
if (dataStr.includes('com.apple') && dataStr.includes('Port')) {
|
|
78
|
+
try {
|
|
79
|
+
const servicesResponse = extractServices(dataStr);
|
|
80
|
+
// Only resolve if we found at least one service
|
|
81
|
+
if (servicesResponse.services.length > 0) {
|
|
82
|
+
this._services = servicesResponse.services;
|
|
83
|
+
log.info(`Extracted ${servicesResponse.services.length} services`);
|
|
84
|
+
clearTimeouts();
|
|
85
|
+
resolve(servicesResponse);
|
|
86
|
+
}
|
|
87
|
+
else if (!serviceExtractionTimeout) {
|
|
88
|
+
// Set a timeout to resolve with whatever we have if no more data comes
|
|
89
|
+
serviceExtractionTimeout = setTimeout(() => {
|
|
90
|
+
log.warn('Service extraction timeout reached, resolving with current data');
|
|
91
|
+
const finalResponse = extractServices(accumulatedData.toString('utf8'));
|
|
92
|
+
this._services = finalResponse.services;
|
|
93
|
+
clearTimeouts();
|
|
94
|
+
resolve(finalResponse);
|
|
95
|
+
}, SERVICE_EXTRACTION_TIMEOUT_MS);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
catch (error) {
|
|
99
|
+
log.warn(`Error extracting services: ${error}, continuing to collect data`);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
this._socket.on('close', () => {
|
|
105
|
+
log.info('Socket closed');
|
|
106
|
+
this._isConnected = false;
|
|
107
|
+
clearTimeouts();
|
|
108
|
+
// If we haven't resolved yet, reject with an error
|
|
109
|
+
if (this._services === undefined) {
|
|
110
|
+
reject(new Error('Connection closed before services were extracted'));
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
this._socket.once('connect', async () => {
|
|
114
|
+
try {
|
|
115
|
+
this._isConnected = true;
|
|
116
|
+
if (this._socket) {
|
|
117
|
+
this._handshake = new Handshake(this._socket);
|
|
118
|
+
// Add a small delay before performing handshake to ensure socket is ready
|
|
119
|
+
await new Promise((resolve) => setTimeout(resolve, HANDSHAKE_DELAY_MS));
|
|
120
|
+
// Once handshake is successful we can get
|
|
121
|
+
// peer-info and get ports for lockdown in RSD
|
|
122
|
+
await this._handshake.perform();
|
|
123
|
+
// Set a timeout for service extraction
|
|
124
|
+
setTimeout(async () => {
|
|
125
|
+
if (this._services === undefined) {
|
|
126
|
+
log.warn('No services received after handshake, closing connection');
|
|
127
|
+
try {
|
|
128
|
+
await this.close();
|
|
129
|
+
}
|
|
130
|
+
catch (err) {
|
|
131
|
+
log.error(`Error closing connection: ${err}`);
|
|
132
|
+
}
|
|
133
|
+
reject(new Error('No services received after handshake'));
|
|
134
|
+
}
|
|
135
|
+
}, SERVICE_AFTER_HANDSHAKE_TIMEOUT_MS);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
catch (error) {
|
|
139
|
+
log.error(`Handshake failed: ${error}`);
|
|
140
|
+
clearTimeouts();
|
|
141
|
+
await this.close();
|
|
142
|
+
reject(error);
|
|
143
|
+
}
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
catch (error) {
|
|
147
|
+
log.error(`Failed to create connection: ${error}`);
|
|
148
|
+
clearTimeouts();
|
|
149
|
+
reject(error);
|
|
150
|
+
}
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Close the connection
|
|
155
|
+
*/
|
|
156
|
+
async close() {
|
|
157
|
+
if (!this._socket) {
|
|
158
|
+
return Promise.resolve();
|
|
159
|
+
}
|
|
160
|
+
// Immediately mark as disconnected to prevent further operations
|
|
161
|
+
this._isConnected = false;
|
|
162
|
+
return new Promise((resolve) => {
|
|
163
|
+
// Set a shorter timeout for socket closing
|
|
164
|
+
const closeTimeout = setTimeout(() => {
|
|
165
|
+
log.warn('Socket close timed out, destroying socket');
|
|
166
|
+
this.forceCleanup();
|
|
167
|
+
resolve();
|
|
168
|
+
}, SOCKET_CLOSE_TIMEOUT_MS);
|
|
169
|
+
// Listen for the close event
|
|
170
|
+
if (this._socket) {
|
|
171
|
+
this._socket.once('close', () => {
|
|
172
|
+
log.debug('Socket closed successfully');
|
|
173
|
+
clearTimeout(closeTimeout);
|
|
174
|
+
this.cleanupResources();
|
|
175
|
+
resolve();
|
|
176
|
+
});
|
|
177
|
+
// Add an error handler specifically for the close operation
|
|
178
|
+
this._socket.once('error', (err) => {
|
|
179
|
+
log.error(`Socket error during close: ${err.message}`);
|
|
180
|
+
// Don't wait for timeout, force cleanup immediately
|
|
181
|
+
clearTimeout(closeTimeout);
|
|
182
|
+
this.forceCleanup();
|
|
183
|
+
resolve();
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
try {
|
|
187
|
+
// First remove all data listeners to prevent parsing during close
|
|
188
|
+
this.cleanupSocket();
|
|
189
|
+
if (this._socket) {
|
|
190
|
+
// Set a small write timeout to prevent hanging
|
|
191
|
+
this._socket.setTimeout(SOCKET_WRITE_TIMEOUT_MS);
|
|
192
|
+
// End the socket with a small empty buffer to flush any pending data
|
|
193
|
+
this._socket.end(Buffer.alloc(0), () => {
|
|
194
|
+
// If end completes successfully, the 'close' event will handle cleanup
|
|
195
|
+
// But set a short timeout just in case 'close' doesn't fire
|
|
196
|
+
setTimeout(() => {
|
|
197
|
+
if (this._socket) {
|
|
198
|
+
log.debug('Socket end completed but close event not fired, forcing cleanup');
|
|
199
|
+
clearTimeout(closeTimeout);
|
|
200
|
+
this.forceCleanup();
|
|
201
|
+
resolve();
|
|
202
|
+
}
|
|
203
|
+
}, SOCKET_END_TIMEOUT_MS);
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
else {
|
|
207
|
+
clearTimeout(closeTimeout);
|
|
208
|
+
this.cleanupResources();
|
|
209
|
+
resolve();
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
catch (error) {
|
|
213
|
+
log.error(`Unexpected error during close: ${error instanceof Error ? error.message : String(error)}`);
|
|
214
|
+
clearTimeout(closeTimeout);
|
|
215
|
+
this.forceCleanup();
|
|
216
|
+
resolve();
|
|
217
|
+
}
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* Get the list of available services
|
|
222
|
+
* @returns Array of available services
|
|
223
|
+
*/
|
|
224
|
+
getServices() {
|
|
225
|
+
if (!this._services) {
|
|
226
|
+
throw new Error('Not connected or services not available');
|
|
227
|
+
}
|
|
228
|
+
return this._services;
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* List all available services
|
|
232
|
+
* @returns Array of all available services
|
|
233
|
+
*/
|
|
234
|
+
listAllServices() {
|
|
235
|
+
return this.getServices();
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* Find a service by name
|
|
239
|
+
* @param serviceName The name of the service to find
|
|
240
|
+
* @returns The service or throws an error if not found
|
|
241
|
+
*/
|
|
242
|
+
findService(serviceName) {
|
|
243
|
+
const services = this.getServices();
|
|
244
|
+
const service = services.find((service) => service.serviceName === serviceName);
|
|
245
|
+
if (!service) {
|
|
246
|
+
throw new Error(`Service ${serviceName} not found,
|
|
247
|
+
Check if the device is locked.`);
|
|
248
|
+
}
|
|
249
|
+
return service;
|
|
250
|
+
}
|
|
251
|
+
/**
|
|
252
|
+
* Remove all listeners from the socket to prevent memory leaks
|
|
253
|
+
*/
|
|
254
|
+
cleanupSocket() {
|
|
255
|
+
if (this._socket) {
|
|
256
|
+
try {
|
|
257
|
+
// Store references to the listeners we want to keep
|
|
258
|
+
const closeListeners = this._socket.listeners('close');
|
|
259
|
+
const errorListeners = this._socket.listeners('error');
|
|
260
|
+
// Remove all listeners
|
|
261
|
+
this._socket.removeAllListeners();
|
|
262
|
+
// Re-add only the close and error listeners we need for cleanup
|
|
263
|
+
for (const listener of closeListeners) {
|
|
264
|
+
this._socket.once('close', listener);
|
|
265
|
+
}
|
|
266
|
+
for (const listener of errorListeners) {
|
|
267
|
+
this._socket.once('error', listener);
|
|
268
|
+
}
|
|
269
|
+
log.debug('Successfully removed socket data listeners');
|
|
270
|
+
}
|
|
271
|
+
catch (error) {
|
|
272
|
+
log.error(`Error removing socket listeners: ${error instanceof Error ? error.message : String(error)}`);
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
/**
|
|
277
|
+
* Clean up all resources
|
|
278
|
+
*/
|
|
279
|
+
cleanupResources() {
|
|
280
|
+
this._socket = undefined;
|
|
281
|
+
this._isConnected = false;
|
|
282
|
+
this._handshake = undefined;
|
|
283
|
+
this._services = undefined;
|
|
284
|
+
}
|
|
285
|
+
/**
|
|
286
|
+
* Force cleanup by destroying the socket and cleaning up resources
|
|
287
|
+
*/
|
|
288
|
+
forceCleanup() {
|
|
289
|
+
try {
|
|
290
|
+
if (this._socket) {
|
|
291
|
+
// Destroy the socket forcefully
|
|
292
|
+
this._socket.destroy();
|
|
293
|
+
log.debug('Socket forcefully destroyed');
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
catch (error) {
|
|
297
|
+
log.error(`Error destroying socket: ${error instanceof Error ? error.message : String(error)}`);
|
|
298
|
+
}
|
|
299
|
+
finally {
|
|
300
|
+
this.cleanupResources();
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
/**
|
|
305
|
+
* Extract services from the response
|
|
306
|
+
* @param response The response string to parse
|
|
307
|
+
* @returns Object containing the extracted services
|
|
308
|
+
*/
|
|
309
|
+
function extractServices(response) {
|
|
310
|
+
// More robust regex that handles various formats of service names and port specifications
|
|
311
|
+
const serviceRegex = /com\.apple(?:\.[\w-]+)+/g;
|
|
312
|
+
const portRegex = /Port[^0-9]*(\d+)/g;
|
|
313
|
+
// First, collect all service names
|
|
314
|
+
const serviceMatches = [];
|
|
315
|
+
let match;
|
|
316
|
+
while ((match = serviceRegex.exec(response)) !== null) {
|
|
317
|
+
serviceMatches.push({ value: match[0], index: match.index });
|
|
318
|
+
}
|
|
319
|
+
// Then, collect all port numbers
|
|
320
|
+
const portMatches = [];
|
|
321
|
+
while ((match = portRegex.exec(response)) !== null) {
|
|
322
|
+
if (match[1]) {
|
|
323
|
+
// Ensure we have a captured port number
|
|
324
|
+
portMatches.push({ value: match[1], index: match.index });
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
// Sort both arrays by index to maintain order
|
|
328
|
+
serviceMatches.sort((a, b) => a.index - b.index);
|
|
329
|
+
portMatches.sort((a, b) => a.index - b.index);
|
|
330
|
+
// Log the extracted data for debugging
|
|
331
|
+
log.debug(`Found ${serviceMatches.length} services and ${portMatches.length} ports`);
|
|
332
|
+
// Create a mapping of services to ports
|
|
333
|
+
const services = [];
|
|
334
|
+
// Assign a port to each service based on proximity in the response
|
|
335
|
+
for (let i = 0; i < serviceMatches.length; i++) {
|
|
336
|
+
const serviceName = serviceMatches[i].value;
|
|
337
|
+
const serviceIndex = serviceMatches[i].index;
|
|
338
|
+
// Find the closest port after this service
|
|
339
|
+
let closestPort = '';
|
|
340
|
+
let closestDistance = Number.MAX_SAFE_INTEGER;
|
|
341
|
+
for (const portMatch of portMatches) {
|
|
342
|
+
// Only consider ports that come after the service in the response
|
|
343
|
+
if (portMatch.index > serviceIndex) {
|
|
344
|
+
const distance = portMatch.index - serviceIndex;
|
|
345
|
+
// If this port is closer than the current closest, update
|
|
346
|
+
if (distance < closestDistance) {
|
|
347
|
+
closestDistance = distance;
|
|
348
|
+
closestPort = portMatch.value;
|
|
349
|
+
// If the port is very close (within 200 chars), we can be confident it's the right one
|
|
350
|
+
if (distance < 200) {
|
|
351
|
+
break;
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
// Add the service with its port (or empty string if no port found)
|
|
357
|
+
services.push({
|
|
358
|
+
serviceName,
|
|
359
|
+
port: closestPort || '',
|
|
360
|
+
});
|
|
361
|
+
}
|
|
362
|
+
return { services };
|
|
363
|
+
}
|
|
364
|
+
export default RemoteXpcConnection;
|
|
365
|
+
export {};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { XPCDictionary } from '../types.js';
|
|
2
|
+
export declare const XPC_TYPES: {
|
|
3
|
+
[key: string]: number;
|
|
4
|
+
};
|
|
5
|
+
export interface XPCMessage {
|
|
6
|
+
flags: number;
|
|
7
|
+
id?: bigint | number;
|
|
8
|
+
body?: XPCDictionary | null;
|
|
9
|
+
size?: number;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Encodes a message object into an XPC Buffer.
|
|
13
|
+
* A message is an object like:
|
|
14
|
+
* { flags: Number, id: bigint, body: Object|null }
|
|
15
|
+
*/
|
|
16
|
+
export declare function encodeMessage(message: XPCMessage): Buffer;
|
|
17
|
+
/**
|
|
18
|
+
* Decodes an XPC Buffer into a message object.
|
|
19
|
+
* (Keep this function if you plan to handle incoming messages.)
|
|
20
|
+
*/
|
|
21
|
+
export declare function decodeMessage(buffer: Buffer): XPCMessage;
|
|
22
|
+
//# sourceMappingURL=xpc-protocol.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"xpc-protocol.d.ts","sourceRoot":"","sources":["../../../../src/lib/remote-xpc/xpc-protocol.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAY,aAAa,EAAY,MAAM,aAAa,CAAC;AAOrE,eAAO,MAAM,SAAS,EAAE;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAA;CAa9C,CAAC;AA4GF,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,aAAa,GAAG,IAAI,CAAC;IAC5B,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,UAAU,GAAG,MAAM,CAkCzD;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,UAAU,CAoCxD"}
|