appium-ios-remotexpc 0.21.2 → 0.23.0
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/index.d.ts +1 -1
- package/build/src/index.d.ts.map +1 -1
- package/build/src/lib/apple-tv/constants.d.ts +4 -3
- package/build/src/lib/apple-tv/constants.d.ts.map +1 -1
- package/build/src/lib/apple-tv/constants.js +10 -3
- package/build/src/lib/apple-tv/discovery/device-discovery.d.ts.map +1 -1
- package/build/src/lib/apple-tv/discovery/device-discovery.js +2 -2
- package/build/src/lib/apple-tv/encryption/index.d.ts +1 -0
- package/build/src/lib/apple-tv/encryption/index.d.ts.map +1 -1
- package/build/src/lib/apple-tv/encryption/index.js +1 -0
- package/build/src/lib/apple-tv/encryption/x25519.d.ts +8 -0
- package/build/src/lib/apple-tv/encryption/x25519.d.ts.map +1 -0
- package/build/src/lib/apple-tv/encryption/x25519.js +52 -0
- package/build/src/lib/apple-tv/index.d.ts +1 -0
- package/build/src/lib/apple-tv/index.d.ts.map +1 -1
- package/build/src/lib/apple-tv/index.js +1 -0
- package/build/src/lib/apple-tv/network/network-client.js +2 -2
- package/build/src/lib/apple-tv/pairing/user-input-service.d.ts.map +1 -1
- package/build/src/lib/apple-tv/pairing/user-input-service.js +2 -2
- package/build/src/lib/apple-tv/pairing-protocol/constants.d.ts +17 -0
- package/build/src/lib/apple-tv/pairing-protocol/constants.d.ts.map +1 -1
- package/build/src/lib/apple-tv/pairing-protocol/constants.js +25 -0
- package/build/src/lib/apple-tv/pairing-protocol/index.d.ts +2 -1
- package/build/src/lib/apple-tv/pairing-protocol/index.d.ts.map +1 -1
- package/build/src/lib/apple-tv/pairing-protocol/index.js +2 -1
- package/build/src/lib/apple-tv/pairing-protocol/pair-verification-protocol.d.ts +66 -0
- package/build/src/lib/apple-tv/pairing-protocol/pair-verification-protocol.d.ts.map +1 -0
- package/build/src/lib/apple-tv/pairing-protocol/pair-verification-protocol.js +178 -0
- package/build/src/lib/apple-tv/pairing-protocol/pairing-protocol.d.ts +35 -2
- package/build/src/lib/apple-tv/pairing-protocol/pairing-protocol.d.ts.map +1 -1
- package/build/src/lib/apple-tv/pairing-protocol/pairing-protocol.js +48 -14
- package/build/src/lib/apple-tv/storage/index.d.ts +1 -1
- package/build/src/lib/apple-tv/storage/index.d.ts.map +1 -1
- package/build/src/lib/apple-tv/storage/pairing-storage.d.ts +4 -1
- package/build/src/lib/apple-tv/storage/pairing-storage.d.ts.map +1 -1
- package/build/src/lib/apple-tv/storage/pairing-storage.js +59 -4
- package/build/src/lib/apple-tv/storage/types.d.ts +7 -1
- package/build/src/lib/apple-tv/storage/types.d.ts.map +1 -1
- package/build/src/lib/apple-tv/tunnel/index.d.ts +3 -0
- package/build/src/lib/apple-tv/tunnel/index.d.ts.map +1 -0
- package/build/src/lib/apple-tv/tunnel/index.js +1 -0
- package/build/src/lib/apple-tv/tunnel/tunnel-service.d.ts +57 -0
- package/build/src/lib/apple-tv/tunnel/tunnel-service.d.ts.map +1 -0
- package/build/src/lib/apple-tv/tunnel/tunnel-service.js +357 -0
- package/build/src/lib/apple-tv/tunnel/types.d.ts +22 -0
- package/build/src/lib/apple-tv/tunnel/types.d.ts.map +1 -0
- package/build/src/lib/apple-tv/tunnel/types.js +1 -0
- package/build/src/lib/bonjour/bonjour-discovery.d.ts.map +1 -1
- package/build/src/lib/bonjour/bonjour-discovery.js +3 -3
- package/build/src/lib/lockdown/index.d.ts.map +1 -1
- package/build/src/lib/plist/length-based-splitter.d.ts.map +1 -1
- package/build/src/lib/plist/length-based-splitter.js +0 -7
- package/build/src/lib/tunnel/index.d.ts +1 -0
- package/build/src/lib/tunnel/index.d.ts.map +1 -1
- package/build/src/lib/tunnel/packet-stream-server.d.ts.map +1 -1
- package/build/src/lib/tunnel/tunnel-registry-server.d.ts +1 -0
- package/build/src/lib/tunnel/tunnel-registry-server.d.ts.map +1 -1
- package/build/src/lib/tunnel/tunnel-registry-server.js +1 -1
- package/build/src/lib/types.d.ts +59 -0
- package/build/src/lib/types.d.ts.map +1 -1
- package/build/src/services/index.d.ts +2 -1
- package/build/src/services/index.d.ts.map +1 -1
- package/build/src/services/index.js +2 -1
- package/build/src/services/ios/afc/codec.d.ts +12 -0
- package/build/src/services/ios/afc/codec.d.ts.map +1 -1
- package/build/src/services/ios/afc/codec.js +26 -0
- package/build/src/services/ios/afc/index.d.ts.map +1 -1
- package/build/src/services/ios/afc/index.js +3 -14
- package/build/src/services/ios/afc/stream-utils.d.ts.map +1 -1
- package/build/src/services/ios/afc/stream-utils.js +0 -2
- package/build/src/services/ios/crash-reports/index.d.ts +54 -0
- package/build/src/services/ios/crash-reports/index.d.ts.map +1 -0
- package/build/src/services/ios/crash-reports/index.js +136 -0
- package/build/src/services/ios/mobile-config/index.js +2 -2
- package/build/src/services.d.ts +6 -1
- package/build/src/services.d.ts.map +1 -1
- package/build/src/services.js +14 -0
- package/package.json +3 -1
- package/scripts/pair-appletv.ts +2 -2
- package/scripts/start-appletv-tunnel.ts +178 -0
- package/scripts/test-tunnel-creation.ts +32 -23
- package/src/index.ts +3 -0
- package/src/lib/apple-tv/constants.ts +11 -3
- package/src/lib/apple-tv/discovery/device-discovery.ts +2 -3
- package/src/lib/apple-tv/encryption/index.ts +6 -0
- package/src/lib/apple-tv/encryption/x25519.ts +79 -0
- package/src/lib/apple-tv/index.ts +1 -0
- package/src/lib/apple-tv/network/network-client.ts +2 -2
- package/src/lib/apple-tv/pairing/user-input-service.ts +2 -2
- package/src/lib/apple-tv/pairing-protocol/constants.ts +29 -0
- package/src/lib/apple-tv/pairing-protocol/index.ts +12 -1
- package/src/lib/apple-tv/pairing-protocol/pair-verification-protocol.ts +329 -0
- package/src/lib/apple-tv/pairing-protocol/pairing-protocol.ts +49 -19
- package/src/lib/apple-tv/storage/index.ts +1 -1
- package/src/lib/apple-tv/storage/pairing-storage.ts +73 -5
- package/src/lib/apple-tv/storage/types.ts +8 -1
- package/src/lib/apple-tv/tunnel/index.ts +2 -0
- package/src/lib/apple-tv/tunnel/tunnel-service.ts +543 -0
- package/src/lib/apple-tv/tunnel/types.ts +23 -0
- package/src/lib/bonjour/bonjour-discovery.ts +3 -5
- package/src/lib/lockdown/index.ts +0 -7
- package/src/lib/plist/length-based-splitter.ts +0 -22
- package/src/lib/tunnel/index.ts +2 -8
- package/src/lib/tunnel/packet-stream-server.ts +0 -8
- package/src/lib/tunnel/tunnel-registry-server.ts +1 -1
- package/src/lib/types.ts +70 -0
- package/src/services/index.ts +2 -0
- package/src/services/ios/afc/codec.ts +34 -0
- package/src/services/ios/afc/index.ts +4 -17
- package/src/services/ios/afc/stream-utils.ts +0 -2
- package/src/services/ios/crash-reports/index.ts +180 -0
- package/src/services/ios/mobile-config/index.ts +2 -2
- package/src/services.ts +27 -0
|
@@ -0,0 +1,357 @@
|
|
|
1
|
+
import { util } from '@appium/support';
|
|
2
|
+
import { lookup } from 'node:dns/promises';
|
|
3
|
+
import * as tls from 'node:tls';
|
|
4
|
+
import { BonjourDiscovery } from '../../bonjour/bonjour-discovery.js';
|
|
5
|
+
import { getLogger } from '../../logger.js';
|
|
6
|
+
import { DEFAULT_PAIRING_CONFIG } from '../constants.js';
|
|
7
|
+
import { decryptChaCha20Poly1305, encryptChaCha20Poly1305, } from '../encryption/index.js';
|
|
8
|
+
import { PairingError } from '../errors.js';
|
|
9
|
+
import { NetworkClient } from '../network/index.js';
|
|
10
|
+
import { PairVerificationProtocol } from '../pairing-protocol/index.js';
|
|
11
|
+
import { PairingStorage } from '../storage/pairing-storage.js';
|
|
12
|
+
const appleTVLog = getLogger('AppleTVTunnelService');
|
|
13
|
+
export class TunnelService {
|
|
14
|
+
networkClient;
|
|
15
|
+
keys;
|
|
16
|
+
sequenceNumber;
|
|
17
|
+
static log = getLogger('TunnelService');
|
|
18
|
+
encryptedSequenceNumber = 0;
|
|
19
|
+
constructor(networkClient, keys, sequenceNumber) {
|
|
20
|
+
this.networkClient = networkClient;
|
|
21
|
+
this.keys = keys;
|
|
22
|
+
this.sequenceNumber = sequenceNumber;
|
|
23
|
+
}
|
|
24
|
+
async createTcpListener() {
|
|
25
|
+
TunnelService.log.debug('Creating TCP listener (Encrypted Request)');
|
|
26
|
+
const request = {
|
|
27
|
+
request: {
|
|
28
|
+
_0: {
|
|
29
|
+
createListener: {
|
|
30
|
+
key: this.keys.encryptionKey.toString('base64'),
|
|
31
|
+
peerConnectionsInfo: [
|
|
32
|
+
{
|
|
33
|
+
owningPID: process.pid,
|
|
34
|
+
owningProcessName: 'CoreDeviceService',
|
|
35
|
+
},
|
|
36
|
+
],
|
|
37
|
+
transportProtocolType: 'tcp',
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
};
|
|
42
|
+
const nonce = Buffer.alloc(12);
|
|
43
|
+
nonce.writeBigUInt64LE(BigInt(this.encryptedSequenceNumber), 0);
|
|
44
|
+
const requestJson = JSON.stringify(request);
|
|
45
|
+
const encrypted = encryptChaCha20Poly1305({
|
|
46
|
+
plaintext: Buffer.from(requestJson, 'utf8'),
|
|
47
|
+
key: this.keys.clientEncryptionKey,
|
|
48
|
+
nonce,
|
|
49
|
+
});
|
|
50
|
+
const encryptedPayload = {
|
|
51
|
+
message: {
|
|
52
|
+
streamEncrypted: {
|
|
53
|
+
_0: encrypted.toString('base64'),
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
originatedBy: 'host',
|
|
57
|
+
sequenceNumber: this.sequenceNumber++,
|
|
58
|
+
};
|
|
59
|
+
await this.networkClient.sendPacket(encryptedPayload);
|
|
60
|
+
this.encryptedSequenceNumber++;
|
|
61
|
+
const response = await this.networkClient.receiveResponse();
|
|
62
|
+
const encryptedData = response.message?.streamEncrypted?._0;
|
|
63
|
+
if (!encryptedData) {
|
|
64
|
+
throw new PairingError('Failed to receive encrypted response from device', 'ENCRYPTED_RESPONSE_MISSING', { response });
|
|
65
|
+
}
|
|
66
|
+
const responseNonce = Buffer.alloc(12);
|
|
67
|
+
responseNonce.writeBigUInt64LE(BigInt(this.encryptedSequenceNumber - 1), 0);
|
|
68
|
+
const decrypted = decryptChaCha20Poly1305({
|
|
69
|
+
ciphertext: Buffer.from(encryptedData, 'base64'),
|
|
70
|
+
key: this.keys.serverEncryptionKey,
|
|
71
|
+
nonce: responseNonce,
|
|
72
|
+
});
|
|
73
|
+
const responseJson = JSON.parse(decrypted.toString('utf8'));
|
|
74
|
+
const createListenerResponse = responseJson?.response?._1?.createListener;
|
|
75
|
+
if (!createListenerResponse?.port) {
|
|
76
|
+
TunnelService.log.error('Invalid createListener response:', responseJson);
|
|
77
|
+
throw new PairingError('TCP listener creation failed: missing port in response', 'LISTENER_PORT_MISSING', { response: responseJson });
|
|
78
|
+
}
|
|
79
|
+
TunnelService.log.debug(`TCP Listener created on port: ${createListenerResponse.port}`);
|
|
80
|
+
return createListenerResponse;
|
|
81
|
+
}
|
|
82
|
+
async createTlsPskConnection(hostname, port) {
|
|
83
|
+
TunnelService.log.debug(`Creating TLS-PSK connection to ${hostname}:${port}`);
|
|
84
|
+
return new Promise((resolve, reject) => {
|
|
85
|
+
const options = {
|
|
86
|
+
host: hostname,
|
|
87
|
+
port,
|
|
88
|
+
pskCallback: (hint) => {
|
|
89
|
+
TunnelService.log.debug(`PSK callback invoked with hint: ${hint}`);
|
|
90
|
+
return {
|
|
91
|
+
psk: this.keys.encryptionKey,
|
|
92
|
+
identity: '',
|
|
93
|
+
};
|
|
94
|
+
},
|
|
95
|
+
ciphers: 'PSK-AES256-CBC-SHA:PSK-AES128-CBC-SHA:PSK-3DES-EDE-CBC-SHA:PSK-RC4-SHA:PSK',
|
|
96
|
+
secureProtocol: 'TLSv1_2_method',
|
|
97
|
+
// SECURITY NOTE: Disabling certificate validation is intentional and safe in this context.
|
|
98
|
+
// This connection uses TLS-PSK (Pre-Shared Key) authentication, where the pre-shared key
|
|
99
|
+
// itself provides mutual authentication between client and server. Traditional X.509
|
|
100
|
+
// certificate validation is not used in PSK-based TLS connections. The encryption key
|
|
101
|
+
// was securely established during the pairing process (which involves PIN verification),
|
|
102
|
+
// and this key authenticates both parties. This is the standard approach for Apple TV's
|
|
103
|
+
// RemoteXPC protocol and should NOT be changed to use certificate validation.
|
|
104
|
+
rejectUnauthorized: false,
|
|
105
|
+
checkServerIdentity: () => undefined,
|
|
106
|
+
};
|
|
107
|
+
const socket = tls.connect(options, () => {
|
|
108
|
+
TunnelService.log.debug('TLS-PSK connection established');
|
|
109
|
+
resolve(socket);
|
|
110
|
+
});
|
|
111
|
+
socket.on('error', (error) => {
|
|
112
|
+
TunnelService.log.error('TLS-PSK connection error:', error);
|
|
113
|
+
if (error.message?.includes('no shared cipher') ||
|
|
114
|
+
error.code === 'ECONNRESET') {
|
|
115
|
+
TunnelService.log.error('PSK ciphers may not be available in your Node.js build');
|
|
116
|
+
TunnelService.log.error('You may need to:');
|
|
117
|
+
TunnelService.log.error('1. Use Node.js compiled with PSK-enabled OpenSSL');
|
|
118
|
+
TunnelService.log.error('2. Use a Python subprocess for the TLS-PSK connection');
|
|
119
|
+
TunnelService.log.error('3. Use a native module like node-openssl');
|
|
120
|
+
}
|
|
121
|
+
reject(error);
|
|
122
|
+
});
|
|
123
|
+
socket.on('secureConnect', () => {
|
|
124
|
+
TunnelService.log.debug('Secure connection event fired');
|
|
125
|
+
});
|
|
126
|
+
socket.on('tlsClientError', (error) => {
|
|
127
|
+
TunnelService.log.error('TLS client error:', error);
|
|
128
|
+
});
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
getSequenceNumber() {
|
|
132
|
+
return this.sequenceNumber;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* High-level service for establishing Apple TV tunnels.
|
|
137
|
+
* Orchestrates device discovery, pairing verification, and tunnel creation.
|
|
138
|
+
*/
|
|
139
|
+
export class AppleTVTunnelService {
|
|
140
|
+
networkClient;
|
|
141
|
+
storage;
|
|
142
|
+
sequenceNumber = 0;
|
|
143
|
+
constructor() {
|
|
144
|
+
this.networkClient = new NetworkClient(DEFAULT_PAIRING_CONFIG);
|
|
145
|
+
this.storage = new PairingStorage(DEFAULT_PAIRING_CONFIG);
|
|
146
|
+
}
|
|
147
|
+
disconnect() {
|
|
148
|
+
this.networkClient.disconnect();
|
|
149
|
+
}
|
|
150
|
+
async startTunnel(deviceId, specificDeviceIdentifier) {
|
|
151
|
+
const devices = await this.discoverDevices();
|
|
152
|
+
this.logDiscoveredDevices(devices);
|
|
153
|
+
const devicesToProcess = this.selectDevicesToProcess(devices, specificDeviceIdentifier);
|
|
154
|
+
const identifiersToTry = await this.validateAndGetPairRecords(deviceId);
|
|
155
|
+
const failedAttempts = [];
|
|
156
|
+
for (const device of devicesToProcess) {
|
|
157
|
+
for (const identifier of identifiersToTry) {
|
|
158
|
+
appleTVLog.debug(`\n--- Attempting connection with pair record: ${identifier} ---`);
|
|
159
|
+
appleTVLog.debug(`Device: ${device.ip}:${device.port}`);
|
|
160
|
+
const pairRecord = await this.storage.load(identifier);
|
|
161
|
+
if (!pairRecord) {
|
|
162
|
+
appleTVLog.debug(`Failed to load pair record for ${identifier}`);
|
|
163
|
+
failedAttempts.push({
|
|
164
|
+
identifier,
|
|
165
|
+
error: 'Failed to load pair record',
|
|
166
|
+
});
|
|
167
|
+
continue;
|
|
168
|
+
}
|
|
169
|
+
const result = await this.attemptDeviceConnection(device, identifier, pairRecord, failedAttempts);
|
|
170
|
+
if (result) {
|
|
171
|
+
return result;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
this.logFailureSummary(failedAttempts);
|
|
176
|
+
throw new Error('Failed to establish tunnel with any pair record. All authentication attempts failed.');
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Logs information about discovered devices
|
|
180
|
+
*/
|
|
181
|
+
logDiscoveredDevices(devices) {
|
|
182
|
+
appleTVLog.debug('Step 1: Device Discovery success');
|
|
183
|
+
appleTVLog.debug(`Found ${util.pluralize('device', devices.length, true)} via Bonjour`);
|
|
184
|
+
if (devices.length > 0) {
|
|
185
|
+
appleTVLog.info('\nDiscovered Apple TV devices:');
|
|
186
|
+
devices.forEach((device, index) => {
|
|
187
|
+
appleTVLog.info(` ${index + 1}. Identifier: ${device.identifier}`);
|
|
188
|
+
appleTVLog.info(` Name: ${device.name}`);
|
|
189
|
+
appleTVLog.info(` IP: ${device.ip}:${device.port}`);
|
|
190
|
+
appleTVLog.info(` Model: ${device.model}`);
|
|
191
|
+
appleTVLog.info(` Version: ${device.version}`);
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Selects devices to process based on the specific device identifier
|
|
197
|
+
*/
|
|
198
|
+
selectDevicesToProcess(devices, specificDeviceIdentifier) {
|
|
199
|
+
if (!specificDeviceIdentifier) {
|
|
200
|
+
return devices;
|
|
201
|
+
}
|
|
202
|
+
const filteredDevices = devices.filter((device) => device.identifier === specificDeviceIdentifier);
|
|
203
|
+
if (filteredDevices.length === 0) {
|
|
204
|
+
appleTVLog.error(`\nDevice with identifier ${specificDeviceIdentifier} not found in discovered devices.`);
|
|
205
|
+
appleTVLog.error('Available devices:');
|
|
206
|
+
devices.forEach((device) => {
|
|
207
|
+
appleTVLog.error(` - ${device.identifier} (${device.name})`);
|
|
208
|
+
});
|
|
209
|
+
throw new Error(`Device with identifier ${specificDeviceIdentifier} not found. Please check available devices above.`);
|
|
210
|
+
}
|
|
211
|
+
appleTVLog.info(`\nFiltered to specific device: ${specificDeviceIdentifier}`);
|
|
212
|
+
return filteredDevices;
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* Validates pair records and returns the list of identifiers to try
|
|
216
|
+
*/
|
|
217
|
+
async validateAndGetPairRecords(deviceId) {
|
|
218
|
+
const availableDeviceIds = await this.storage.getAvailableDeviceIds();
|
|
219
|
+
if (availableDeviceIds.length === 0) {
|
|
220
|
+
throw new Error('No pair records found');
|
|
221
|
+
}
|
|
222
|
+
if (deviceId && !availableDeviceIds.includes(deviceId)) {
|
|
223
|
+
throw new Error(`No pair record found for specified device ${deviceId}`);
|
|
224
|
+
}
|
|
225
|
+
return deviceId ? [deviceId] : availableDeviceIds;
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* Attempts to establish a connection with a device using a specific pair record
|
|
229
|
+
*/
|
|
230
|
+
async attemptDeviceConnection(device, identifier, pairRecord, failedAttempts) {
|
|
231
|
+
try {
|
|
232
|
+
this.sequenceNumber = 0;
|
|
233
|
+
await this.networkClient.connect(device.ip, device.port);
|
|
234
|
+
try {
|
|
235
|
+
await this.performHandshake();
|
|
236
|
+
const keys = await this.performPairVerification(pairRecord, identifier);
|
|
237
|
+
appleTVLog.info(`✅ Successfully verified with pair record: ${identifier}`);
|
|
238
|
+
const listenerInfo = await this.createTcpListener(keys);
|
|
239
|
+
this.networkClient.disconnect();
|
|
240
|
+
const tlsSocket = await this.createTlsPskConnection(device.ip, listenerInfo.port, keys);
|
|
241
|
+
appleTVLog.debug('Step 6: Tunnel Establishment success');
|
|
242
|
+
appleTVLog.info(`🔑 Using pair record: ${identifier}`);
|
|
243
|
+
appleTVLog.info(`📱 Connected to device: ${device.identifier} (${device.name})`);
|
|
244
|
+
appleTVLog.info(` IP: ${device.ip}:${device.port}`);
|
|
245
|
+
return { socket: tlsSocket, device };
|
|
246
|
+
}
|
|
247
|
+
catch (error) {
|
|
248
|
+
const errorMessage = error.message || String(error);
|
|
249
|
+
appleTVLog.debug(`❌ Failed with pair record ${identifier}: ${errorMessage}`);
|
|
250
|
+
failedAttempts.push({ identifier, error: errorMessage });
|
|
251
|
+
this.networkClient.disconnect();
|
|
252
|
+
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
catch (connectionError) {
|
|
256
|
+
const errorMessage = connectionError.message || String(connectionError);
|
|
257
|
+
appleTVLog.debug(`Failed to connect to ${device.ip}:${device.port}: ${errorMessage}`);
|
|
258
|
+
failedAttempts.push({
|
|
259
|
+
identifier,
|
|
260
|
+
error: `Connection failed: ${errorMessage}`,
|
|
261
|
+
});
|
|
262
|
+
this.networkClient.disconnect();
|
|
263
|
+
}
|
|
264
|
+
return null;
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
* Logs a summary of all failed connection attempts
|
|
268
|
+
*/
|
|
269
|
+
logFailureSummary(failedAttempts) {
|
|
270
|
+
appleTVLog.error('\n=== Pair Record Verification Summary ===');
|
|
271
|
+
appleTVLog.error(`Total pair records tried: ${failedAttempts.length}`);
|
|
272
|
+
failedAttempts.forEach(({ identifier, error }) => {
|
|
273
|
+
appleTVLog.error(` - ${identifier}: ${error}`);
|
|
274
|
+
});
|
|
275
|
+
appleTVLog.error('=======================================\n');
|
|
276
|
+
}
|
|
277
|
+
async discoverDevices() {
|
|
278
|
+
const discovery = new BonjourDiscovery();
|
|
279
|
+
await discovery.startBrowsing('_remotepairing._tcp', 'local');
|
|
280
|
+
await new Promise((resolve) => setTimeout(resolve, DEFAULT_PAIRING_CONFIG.discoveryTimeout));
|
|
281
|
+
const services = discovery.getDiscoveredServices();
|
|
282
|
+
const devices = [];
|
|
283
|
+
for (const service of services) {
|
|
284
|
+
try {
|
|
285
|
+
const resolved = await discovery.resolveService(service.name, '_remotepairing._tcp', 'local');
|
|
286
|
+
if (resolved.hostname && resolved.port) {
|
|
287
|
+
const ipResult = await lookup(resolved.hostname.endsWith('.')
|
|
288
|
+
? resolved.hostname.slice(0, -1)
|
|
289
|
+
: resolved.hostname, { family: 4 });
|
|
290
|
+
devices.push({
|
|
291
|
+
name: resolved.name,
|
|
292
|
+
identifier: resolved.txtRecord?.identifier || resolved.name,
|
|
293
|
+
hostname: resolved.hostname,
|
|
294
|
+
ip: ipResult.address,
|
|
295
|
+
port: resolved.port,
|
|
296
|
+
model: resolved.txtRecord?.model || '',
|
|
297
|
+
version: resolved.txtRecord?.ver || '',
|
|
298
|
+
minVersion: resolved.txtRecord?.minVer || '17',
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
catch (err) {
|
|
303
|
+
appleTVLog.debug(`Failed to resolve service ${service.name}: ${err}`);
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
discovery.stopBrowsing();
|
|
307
|
+
if (devices.length === 0) {
|
|
308
|
+
throw new Error('No devices found via Bonjour discovery');
|
|
309
|
+
}
|
|
310
|
+
return devices;
|
|
311
|
+
}
|
|
312
|
+
async performHandshake() {
|
|
313
|
+
const handshakePayload = {
|
|
314
|
+
message: {
|
|
315
|
+
plain: {
|
|
316
|
+
_0: {
|
|
317
|
+
request: {
|
|
318
|
+
_0: {
|
|
319
|
+
handshake: {
|
|
320
|
+
_0: {
|
|
321
|
+
hostOptions: { attemptPairVerify: true },
|
|
322
|
+
wireProtocolVersion: 19,
|
|
323
|
+
},
|
|
324
|
+
},
|
|
325
|
+
},
|
|
326
|
+
},
|
|
327
|
+
},
|
|
328
|
+
},
|
|
329
|
+
},
|
|
330
|
+
originatedBy: 'host',
|
|
331
|
+
sequenceNumber: this.sequenceNumber++,
|
|
332
|
+
};
|
|
333
|
+
await this.networkClient.sendPacket(handshakePayload);
|
|
334
|
+
await this.networkClient.receiveResponse();
|
|
335
|
+
appleTVLog.debug('Step 2: Initial Connection & Handshake success');
|
|
336
|
+
}
|
|
337
|
+
async performPairVerification(pairRecord, deviceId) {
|
|
338
|
+
appleTVLog.debug('Step 3: Pair Verification (4-step process)');
|
|
339
|
+
const verificationProtocol = new PairVerificationProtocol(this.networkClient);
|
|
340
|
+
verificationProtocol.setSequenceNumber(this.sequenceNumber);
|
|
341
|
+
const keys = await verificationProtocol.verify(pairRecord, deviceId);
|
|
342
|
+
this.sequenceNumber = verificationProtocol.getSequenceNumber();
|
|
343
|
+
appleTVLog.debug('Step 4: Main Encryption Key Derivation success');
|
|
344
|
+
return keys;
|
|
345
|
+
}
|
|
346
|
+
async createTcpListener(keys) {
|
|
347
|
+
appleTVLog.debug('Step 5: TCP Listener Creation (Encrypted Request)');
|
|
348
|
+
const tunnelService = new TunnelService(this.networkClient, keys, this.sequenceNumber);
|
|
349
|
+
const listenerInfo = await tunnelService.createTcpListener();
|
|
350
|
+
this.sequenceNumber = tunnelService.getSequenceNumber();
|
|
351
|
+
return listenerInfo;
|
|
352
|
+
}
|
|
353
|
+
async createTlsPskConnection(hostname, port, keys) {
|
|
354
|
+
const tunnelService = new TunnelService(this.networkClient, keys, this.sequenceNumber);
|
|
355
|
+
return tunnelService.createTlsPskConnection(hostname, port);
|
|
356
|
+
}
|
|
357
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type * as tls from 'node:tls';
|
|
2
|
+
export interface TcpListenerInfo {
|
|
3
|
+
port: number;
|
|
4
|
+
serviceName: string;
|
|
5
|
+
devicePublicKey: string;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Extended TLS connection options that include PSK (Pre-Shared Key) callback support.
|
|
9
|
+
* This interface extends the standard Node.js TLS ConnectionOptions to add PSK authentication.
|
|
10
|
+
*/
|
|
11
|
+
export interface TlsPskConnectionOptions extends tls.ConnectionOptions {
|
|
12
|
+
/**
|
|
13
|
+
* Callback function invoked during TLS handshake to provide PSK credentials.
|
|
14
|
+
* @param hint - Optional hint from the server about which PSK identity to use
|
|
15
|
+
* @returns Object containing the pre-shared key and identity string
|
|
16
|
+
*/
|
|
17
|
+
pskCallback?: (hint: string | null) => {
|
|
18
|
+
psk: Buffer;
|
|
19
|
+
identity: string;
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../../src/lib/apple-tv/tunnel/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,GAAG,MAAM,UAAU,CAAC;AAErC,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,EAAE,MAAM,CAAC;CACzB;AAED;;;GAGG;AACH,MAAM,WAAW,uBAAwB,SAAQ,GAAG,CAAC,iBAAiB;IACpE;;;;OAIG;IACH,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,KAAK;QACrC,GAAG,EAAE,MAAM,CAAC;QACZ,QAAQ,EAAE,MAAM,CAAC;KAClB,CAAC;CACH"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bonjour-discovery.d.ts","sourceRoot":"","sources":["../../../../src/lib/bonjour/bonjour-discovery.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAkB3C;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACnC,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED;;GAEG;AACH,MAAM,MAAM,sBAAsB,GAAG,KAAK,CAAC;IACzC,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,cAAc,CAAC;CACzB,CAAC,CAAC;AAwXH,qBAAa,gBAAiB,SAAQ,YAAY;IAChD,OAAO,CAAC,cAAc,CAAC,CAAe;IACtC,OAAO,CAAC,cAAc,CAAS;IAC/B,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAA0C;IAE9E;;OAEG;IACG,aAAa,CACjB,WAAW,GAAE,MAA+C,EAC5D,MAAM,GAAE,MAA+B,GACtC,OAAO,CAAC,IAAI,CAAC;IAgBhB;;OAEG;IACH,YAAY,IAAI,IAAI;IAQpB;;OAEG;IACH,qBAAqB,IAAI,cAAc,EAAE;IAIzC;;OAEG;IACG,cAAc,CAClB,WAAW,EAAE,MAAM,EACnB,WAAW,GAAE,MAA+C,EAC5D,MAAM,GAAE,MAA+B,GACtC,OAAO,CAAC,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"bonjour-discovery.d.ts","sourceRoot":"","sources":["../../../../src/lib/bonjour/bonjour-discovery.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAkB3C;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACnC,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED;;GAEG;AACH,MAAM,MAAM,sBAAsB,GAAG,KAAK,CAAC;IACzC,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,cAAc,CAAC;CACzB,CAAC,CAAC;AAwXH,qBAAa,gBAAiB,SAAQ,YAAY;IAChD,OAAO,CAAC,cAAc,CAAC,CAAe;IACtC,OAAO,CAAC,cAAc,CAAS;IAC/B,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAA0C;IAE9E;;OAEG;IACG,aAAa,CACjB,WAAW,GAAE,MAA+C,EAC5D,MAAM,GAAE,MAA+B,GACtC,OAAO,CAAC,IAAI,CAAC;IAgBhB;;OAEG;IACH,YAAY,IAAI,IAAI;IAQpB;;OAEG;IACH,qBAAqB,IAAI,cAAc,EAAE;IAIzC;;OAEG;IACG,cAAc,CAClB,WAAW,EAAE,MAAM,EACnB,WAAW,GAAE,MAA+C,EAC5D,MAAM,GAAE,MAA+B,GACtC,OAAO,CAAC,cAAc,CAAC;IAkB1B;;OAEG;IACG,4BAA4B,CAChC,SAAS,GAAE,MAA2C,GACrD,OAAO,CAAC,aAAa,EAAE,CAAC;IAqB3B;;OAEG;IACH,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAsBzC;;OAEG;YACW,kBAAkB;IA4BhC;;OAEG;IACH,OAAO,CAAC,wBAAwB;IAuBhC;;OAEG;YACW,kBAAkB;IAuBhC;;OAEG;IACH,OAAO,CAAC,OAAO;CAMhB"}
|
|
@@ -310,7 +310,7 @@ export class BonjourDiscovery extends EventEmitter {
|
|
|
310
310
|
log.warn('Already discovering services');
|
|
311
311
|
return;
|
|
312
312
|
}
|
|
313
|
-
log.info(
|
|
313
|
+
log.info('Starting Bonjour discovery');
|
|
314
314
|
try {
|
|
315
315
|
await this.initializeBrowsing(serviceType, domain);
|
|
316
316
|
}
|
|
@@ -339,7 +339,7 @@ export class BonjourDiscovery extends EventEmitter {
|
|
|
339
339
|
* Resolve a specific service to get detailed information
|
|
340
340
|
*/
|
|
341
341
|
async resolveService(serviceName, serviceType = BONJOUR_SERVICE_TYPES.APPLE_TV_PAIRING, domain = BONJOUR_DEFAULT_DOMAIN) {
|
|
342
|
-
log.info(`[ServiceResolver] Resolving service: ${serviceName}
|
|
342
|
+
log.info(`[ServiceResolver] Resolving service: ${serviceName}`);
|
|
343
343
|
const service = await executeDnsSdCommand([DNS_SD_COMMANDS.RESOLVE, serviceName, serviceType, domain], BONJOUR_TIMEOUTS.SERVICE_RESOLUTION, (output) => parseResolveOutput(output, serviceName, serviceType, domain), `Service resolution timeout for ${serviceName}`);
|
|
344
344
|
if (!service) {
|
|
345
345
|
throw new Error(`Failed to resolve service ${serviceName}`);
|
|
@@ -374,7 +374,7 @@ export class BonjourDiscovery extends EventEmitter {
|
|
|
374
374
|
case DNS_SD_ACTIONS.ADD:
|
|
375
375
|
this._discoveredServices.set(service.name, service);
|
|
376
376
|
this.emit('serviceAdded', service);
|
|
377
|
-
log.
|
|
377
|
+
log.debug(`Discovered service: ${service.name}`);
|
|
378
378
|
break;
|
|
379
379
|
case DNS_SD_ACTIONS.REMOVE:
|
|
380
380
|
this._discoveredServices.delete(service.name);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/lib/lockdown/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAClC,OAAY,EAAE,KAAK,iBAAiB,EAAE,SAAS,EAAE,MAAM,KAAK,CAAC;AAE7D,OAAO,EAAE,gBAAgB,EAAE,MAAM,6BAA6B,CAAC;AAI/D,OAAO,KAAK,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAC5D,OAAO,EAAE,YAAY,EAAgB,MAAM,oBAAoB,CAAC;AAWhE,UAAU,gBAAgB;IACxB,eAAe,EAAE,MAAM,CAAC;IACxB,cAAc,EAAE,MAAM,CAAC;IACvB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,eAAe,EAAE,MAAM,CAAC;CACzB;AAED,UAAU,MAAM;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,gBAAgB,CAAC;CAC9B;AAED,UAAU,mBAAmB;IAC3B,eAAe,EAAE,eAAe,CAAC;IACjC,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,UAAU,WAAW;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,gBAAgB,EAAE,OAAO,CAAC;CAC3B;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/lib/lockdown/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"length-based-splitter.d.ts","sourceRoot":"","sources":["../../../../src/lib/plist/length-based-splitter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,KAAK,iBAAiB,EAAE,MAAM,QAAQ,CAAC;AA2B3D;;GAEG;AACH,MAAM,WAAW,0BAA0B;IACzC,cAAc,CAAC,EAAE,MAAM,CAAC,cAAc,CAAC;IACvC,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED;;GAEG;AACH,qBAAa,mBAAoB,SAAQ,SAAS;IAChD,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAU;IACvC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAS;IACxC,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAS;IAC3C,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAS;IAC3C,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAS;IAC1C,OAAO,CAAC,SAAS,CAAkB;IAEnC;;;OAGG;gBACS,OAAO,GAAE,0BAA+B;IAkBpD;;OAEG;IACH,QAAQ,IAAI,IAAI;IAUhB,UAAU,CACR,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,cAAc,EACxB,QAAQ,EAAE,iBAAiB,GAC1B,IAAI;
|
|
1
|
+
{"version":3,"file":"length-based-splitter.d.ts","sourceRoot":"","sources":["../../../../src/lib/plist/length-based-splitter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,KAAK,iBAAiB,EAAE,MAAM,QAAQ,CAAC;AA2B3D;;GAEG;AACH,MAAM,WAAW,0BAA0B;IACzC,cAAc,CAAC,EAAE,MAAM,CAAC,cAAc,CAAC;IACvC,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED;;GAEG;AACH,qBAAa,mBAAoB,SAAQ,SAAS;IAChD,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAU;IACvC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAS;IACxC,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAS;IAC3C,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAS;IAC3C,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAS;IAC1C,OAAO,CAAC,SAAS,CAAkB;IAEnC;;;OAGG;gBACS,OAAO,GAAE,0BAA+B;IAkBpD;;OAEG;IACH,QAAQ,IAAI,IAAI;IAUhB,UAAU,CACR,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,cAAc,EACxB,QAAQ,EAAE,iBAAiB,GAC1B,IAAI;IAsCP;;OAEG;IACH,OAAO,CAAC,cAAc;IA+CtB;;OAEG;IACH,OAAO,CAAC,iBAAiB;CAuF1B"}
|
|
@@ -55,8 +55,6 @@ export class LengthBasedSplitter extends Transform {
|
|
|
55
55
|
try {
|
|
56
56
|
// Add the new chunk to our buffer
|
|
57
57
|
this.buffer = Buffer.concat([this.buffer, chunk]);
|
|
58
|
-
// Check if this is XML data or binary plist before doing any other processing
|
|
59
|
-
const bufferString = this.buffer.toString(UTF8_ENCODING, 0, Math.min(MAX_PREVIEW_LENGTH, this.buffer.length));
|
|
60
58
|
// Check for binary plist format (bplist00 or Ibplist00)
|
|
61
59
|
if (this.buffer.length >= BINARY_PLIST_HEADER_LENGTH) {
|
|
62
60
|
const possibleBplistHeader = this.buffer.toString(UTF8_ENCODING, 0, BINARY_PLIST_HEADER_LENGTH);
|
|
@@ -157,17 +155,12 @@ export class LengthBasedSplitter extends Transform {
|
|
|
157
155
|
messageLength = alternateLength;
|
|
158
156
|
}
|
|
159
157
|
else {
|
|
160
|
-
// If length is still invalid, check if this might actually be XML
|
|
161
|
-
const suspiciousData = this.buffer.toString(UTF8_ENCODING, 0, Math.min(MAX_PREVIEW_LENGTH, this.buffer.length));
|
|
162
158
|
// Invalid length - skip one byte and try again
|
|
163
159
|
this.buffer = this.buffer.slice(1);
|
|
164
160
|
continue;
|
|
165
161
|
}
|
|
166
162
|
}
|
|
167
163
|
else {
|
|
168
|
-
// For non-4-byte length fields, just use the original approach
|
|
169
|
-
// If length is invalid, check if this might actually be XML
|
|
170
|
-
const suspiciousData = this.buffer.toString(UTF8_ENCODING, 0, Math.min(MAX_PREVIEW_LENGTH, this.buffer.length));
|
|
171
164
|
// Invalid length - skip one byte and try again
|
|
172
165
|
this.buffer = this.buffer.slice(1);
|
|
173
166
|
continue;
|
|
@@ -67,4 +67,5 @@ declare class TunnelManagerService {
|
|
|
67
67
|
export declare const TunnelManager: TunnelManagerService;
|
|
68
68
|
export { PacketStreamClient } from './packet-stream-client.js';
|
|
69
69
|
export { PacketStreamServer } from './packet-stream-server.js';
|
|
70
|
+
export type { TunnelConnection };
|
|
70
71
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/lib/tunnel/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,gBAAgB,EAEtB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,KAAK,CAAC;AAGrC,OAAO,EAAE,mBAAmB,EAAE,MAAM,wCAAwC,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/lib/tunnel/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,gBAAgB,EAEtB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,KAAK,CAAC;AAGrC,OAAO,EAAE,mBAAmB,EAAE,MAAM,wCAAwC,CAAC;AAc7E;;;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,mBAAmB,CAAC;IA2C/B;;;;;;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;AAE/D,YAAY,EAAE,gBAAgB,EAAE,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"packet-stream-server.d.ts","sourceRoot":"","sources":["../../../../src/lib/tunnel/packet-stream-server.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAc,MAAM,mBAAmB,CAAC;AACpE,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;
|
|
1
|
+
{"version":3,"file":"packet-stream-server.d.ts","sourceRoot":"","sources":["../../../../src/lib/tunnel/packet-stream-server.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAc,MAAM,mBAAmB,CAAC;AACpE,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AAOtC;;;GAGG;AACH,qBAAa,kBAAmB,SAAQ,YAAY;IAKtC,OAAO,CAAC,QAAQ,CAAC,IAAI;IAJjC,OAAO,CAAC,MAAM,CAAuB;IACrC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA0B;IAClD,OAAO,CAAC,cAAc,CAA+B;gBAExB,IAAI,EAAE,MAAM;IAIzC;;;OAGG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAqBtB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAgB3B,iBAAiB,IAAI,cAAc,GAAG,IAAI;IAI1C;;OAEG;IACH,OAAO,CAAC,sBAAsB;IAe9B;;OAEG;IACH,OAAO,CAAC,oBAAoB;IAQ5B;;OAEG;IACH,OAAO,CAAC,eAAe;IAoBvB;;OAEG;IACH,OAAO,CAAC,aAAa;CAItB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tunnel-registry-server.d.ts","sourceRoot":"","sources":["../../../../src/lib/tunnel/tunnel-registry-server.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,cAAc,EAAuB,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"tunnel-registry-server.d.ts","sourceRoot":"","sources":["../../../../src/lib/tunnel/tunnel-registry-server.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,cAAc,EAAuB,MAAM,aAAa,CAAC;AAGvE,eAAO,MAAM,4BAA4B,QAAQ,CAAC;AAoDlD;;GAEG;AACH,qBAAa,oBAAoB;IAC/B,OAAO,CAAC,MAAM,CAAC,CAAc;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,cAAc,CAAC;IACpC,OAAO,CAAC,QAAQ,CAOd;IAEF;;;;OAIG;gBACS,WAAW,EAAE,cAAc,GAAG,SAAS,EAAE,IAAI,EAAE,MAAM;IAKjE;;OAEG;IACH,OAAO,KAAK,OAAO,GAElB;IAED;;OAEG;IACH,OAAO,KAAK,QAAQ,GAOnB;IAED;;OAEG;IACH,OAAO,KAAK,YAAY,GAKvB;IAED;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAgC5B;;OAEG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAuB3B;;OAEG;YACW,aAAa;IA6D3B;;OAEG;YACW,aAAa;IAY3B;;OAEG;YACW,eAAe;IAwB7B;;OAEG;YACW,mBAAmB;IAgCjC;;OAEG;YACW,YAAY;IAqD1B;;OAEG;YACW,YAAY;CAmB3B;AAED;;;;;GAKG;AACH,wBAAsB,yBAAyB,CAC7C,WAAW,EAAE,cAAc,GAAG,SAAS,EACvC,IAAI,GAAE,MAAqC,GAC1C,OAAO,CAAC,oBAAoB,CAAC,CAI/B"}
|
|
@@ -2,7 +2,7 @@ import * as http from 'node:http';
|
|
|
2
2
|
import { URL } from 'node:url';
|
|
3
3
|
import { getLogger } from '../logger.js';
|
|
4
4
|
// Constants
|
|
5
|
-
const DEFAULT_TUNNEL_REGISTRY_PORT = 42314;
|
|
5
|
+
export const DEFAULT_TUNNEL_REGISTRY_PORT = 42314;
|
|
6
6
|
const API_BASE_PATH = '/remotexpc/tunnels';
|
|
7
7
|
// Logger instance
|
|
8
8
|
const log = getLogger('TunnelRegistryServer');
|
package/build/src/lib/types.d.ts
CHANGED
|
@@ -1322,4 +1322,63 @@ export interface MisagentServiceWithConnection {
|
|
|
1322
1322
|
misagentService: MisagentService;
|
|
1323
1323
|
remoteXPC: RemoteXpcConnection;
|
|
1324
1324
|
}
|
|
1325
|
+
/**
|
|
1326
|
+
* Options for pulling crash reports from device to local
|
|
1327
|
+
*/
|
|
1328
|
+
export interface CrashReportsPullOptions {
|
|
1329
|
+
/**
|
|
1330
|
+
* If true, deletes crash reports from the remote device after pulling to local.
|
|
1331
|
+
* @default false
|
|
1332
|
+
*/
|
|
1333
|
+
erase?: boolean;
|
|
1334
|
+
/**
|
|
1335
|
+
* Glob pattern to filter crash reports (e.g., '*.ips', 'Siri*', '**\/*.crash')
|
|
1336
|
+
*/
|
|
1337
|
+
match?: string;
|
|
1338
|
+
}
|
|
1339
|
+
/**
|
|
1340
|
+
* CrashReportsService provides an API to manage crash reports on iOS devices
|
|
1341
|
+
*/
|
|
1342
|
+
export interface CrashReportsService extends BaseService {
|
|
1343
|
+
/**
|
|
1344
|
+
* List files and folders in the crash report's directory
|
|
1345
|
+
*
|
|
1346
|
+
* Crash reports are primarily stored as .ips files, which contain detailed information about
|
|
1347
|
+
* app crashes, including stack traces, thread states, and device information.
|
|
1348
|
+
* In addition to .ips files, the crash reports directory may contain:
|
|
1349
|
+
* - Sysdiagnose tarballs (comprehensive system diagnostic archives)
|
|
1350
|
+
* - Special directories like /Retired, /Cloud, /Assistant, etc.
|
|
1351
|
+
*
|
|
1352
|
+
* For details on the .ips file format, see:
|
|
1353
|
+
* https://developer.apple.com/documentation/xcode/analyzing-a-crash-report
|
|
1354
|
+
*
|
|
1355
|
+
* @param dirPath Path to list, defaults to "/"
|
|
1356
|
+
* @param depth Listing depth: 1 for immediate children, -1 for infinite
|
|
1357
|
+
* @returns List of file paths (e.g., .ips files, sysdiagnose tarballs, directories like /Retired, /Cloud, etc.)
|
|
1358
|
+
*/
|
|
1359
|
+
ls(dirPath?: string, depth?: number): Promise<string[]>;
|
|
1360
|
+
/**
|
|
1361
|
+
* Pull crash reports from device to local machine
|
|
1362
|
+
* @param out Local directory path
|
|
1363
|
+
* @param entry Remote path on device, defaults to "/"
|
|
1364
|
+
* @param options Pull options (erase, match pattern)
|
|
1365
|
+
*/
|
|
1366
|
+
pull(out: string, entry?: string, options?: CrashReportsPullOptions): Promise<void>;
|
|
1367
|
+
/**
|
|
1368
|
+
* Clear all crash reports from the device
|
|
1369
|
+
*/
|
|
1370
|
+
clear(): Promise<void>;
|
|
1371
|
+
/**
|
|
1372
|
+
* Flush pending crash reports into CrashReports directory
|
|
1373
|
+
*/
|
|
1374
|
+
flush(): Promise<void>;
|
|
1375
|
+
/**
|
|
1376
|
+
* Close the service and release resources
|
|
1377
|
+
*/
|
|
1378
|
+
close(): void;
|
|
1379
|
+
}
|
|
1380
|
+
export interface CrashReportsServiceWithConnection {
|
|
1381
|
+
crashReportsService: CrashReportsService;
|
|
1382
|
+
remoteXPC: RemoteXpcConnection;
|
|
1383
|
+
}
|
|
1325
1384
|
//# sourceMappingURL=types.d.ts.map
|