@basmilius/apple-common 0.8.2 → 0.9.3
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/dist/index.d.mts +199 -78
- package/dist/index.mjs +670 -37
- package/package.json +9 -5
- package/dist/index.d.mts.map +0 -1
- package/dist/index.mjs.map +0 -1
package/dist/index.d.mts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
/// <reference types="node" />
|
|
2
|
-
import { DiscoveryResult as DiscoveryResult$1 } from "node-dns-sd";
|
|
3
2
|
import { EventEmitter } from "node:events";
|
|
4
3
|
|
|
5
4
|
//#region ../../node_modules/.bun/uuid@13.0.0/node_modules/uuid/dist/types.d.ts
|
|
@@ -12,20 +11,62 @@ type Version4Options = {
|
|
|
12
11
|
declare function v4(options?: Version4Options, buf?: undefined, offset?: number): string;
|
|
13
12
|
declare function v4<TBuf extends Uint8Array = Uint8Array>(options: Version4Options | undefined, buf: TBuf, offset?: number): TBuf;
|
|
14
13
|
//#endregion
|
|
15
|
-
//#region src/
|
|
16
|
-
declare
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
14
|
+
//#region src/airplayFeatures.d.ts
|
|
15
|
+
declare const AirPlayFeatureFlags: {
|
|
16
|
+
readonly SupportsAirPlayVideoV1: bigint;
|
|
17
|
+
readonly SupportsAirPlayPhoto: bigint;
|
|
18
|
+
readonly SupportsAirPlayVideoFairPlay: bigint;
|
|
19
|
+
readonly SupportsAirPlayVideoVolumeControl: bigint;
|
|
20
|
+
readonly SupportsAirPlayVideoHTTPLiveStreams: bigint;
|
|
21
|
+
readonly SupportsAirPlaySlideShow: bigint;
|
|
22
|
+
readonly SupportsAirPlayScreen: bigint;
|
|
23
|
+
readonly SupportsAirPlayAudio: bigint;
|
|
24
|
+
readonly AudioRedundant: bigint;
|
|
25
|
+
readonly Authentication_4: bigint;
|
|
26
|
+
readonly MetadataFeatures_0: bigint;
|
|
27
|
+
readonly MetadataFeatures_1: bigint;
|
|
28
|
+
readonly MetadataFeatures_2: bigint;
|
|
29
|
+
readonly AudioFormats_0: bigint;
|
|
30
|
+
readonly AudioFormats_1: bigint;
|
|
31
|
+
readonly AudioFormats_2: bigint;
|
|
32
|
+
readonly AudioFormats_3: bigint;
|
|
33
|
+
readonly Authentication_1: bigint;
|
|
34
|
+
readonly Authentication_8: bigint;
|
|
35
|
+
readonly SupportsLegacyPairing: bigint;
|
|
36
|
+
readonly HasUnifiedAdvertiserInfo: bigint;
|
|
37
|
+
readonly IsCarPlay: bigint;
|
|
38
|
+
readonly SupportsAirPlayVideoPlayQueue: bigint;
|
|
39
|
+
readonly SupportsAirPlayFromCloud: bigint;
|
|
40
|
+
readonly SupportsTLS_PSK: bigint;
|
|
41
|
+
readonly SupportsUnifiedMediaControl: bigint;
|
|
42
|
+
readonly SupportsBufferedAudio: bigint;
|
|
43
|
+
readonly SupportsPTP: bigint;
|
|
44
|
+
readonly SupportsScreenMultiCodec: bigint;
|
|
45
|
+
readonly SupportsSystemPairing: bigint;
|
|
46
|
+
readonly IsAPValeriaScreenSender: bigint;
|
|
47
|
+
readonly SupportsHKPairingAndAccessControl: bigint;
|
|
48
|
+
readonly SupportsCoreUtilsPairingAndEncryption: bigint;
|
|
49
|
+
readonly SupportsAirPlayVideoV2: bigint;
|
|
50
|
+
readonly MetadataFeatures_3: bigint;
|
|
51
|
+
readonly SupportsUnifiedPairSetupAndMFi: bigint;
|
|
52
|
+
readonly SupportsSetPeersExtendedMessage: bigint;
|
|
53
|
+
readonly SupportsAPSync: bigint;
|
|
54
|
+
readonly SupportsWoL: bigint;
|
|
55
|
+
readonly SupportsWoL2: bigint;
|
|
56
|
+
readonly SupportsHangdogRemoteControl: bigint;
|
|
57
|
+
readonly SupportsAudioStreamConnectionSetup: bigint;
|
|
58
|
+
readonly SupportsAudioMetadataControl: bigint;
|
|
59
|
+
readonly SupportsRFC2198Redundancy: bigint;
|
|
60
|
+
};
|
|
61
|
+
type AirPlayFeatureFlagName = keyof typeof AirPlayFeatureFlags;
|
|
62
|
+
type PairingRequirement = 'none' | 'pin' | 'transient' | 'homekit';
|
|
63
|
+
declare const parseFeatures: (features: string) => bigint;
|
|
64
|
+
declare const hasFeatureFlag: (features: bigint, flag: bigint) => boolean;
|
|
65
|
+
declare const describeFlags: (features: bigint) => AirPlayFeatureFlagName[];
|
|
66
|
+
declare const getProtocolVersion: (txt: Record<string, string>) => 1 | 2;
|
|
67
|
+
declare const getPairingRequirement: (txt: Record<string, string>) => PairingRequirement;
|
|
68
|
+
declare const isPasswordRequired: (txt: Record<string, string>) => boolean;
|
|
69
|
+
declare const isRemoteControlSupported: (txt: Record<string, string>) => boolean;
|
|
29
70
|
//#endregion
|
|
30
71
|
//#region src/reporter.d.ts
|
|
31
72
|
type DebugGroup = 'debug' | 'error' | 'info' | 'net' | 'raw' | 'warn';
|
|
@@ -58,8 +99,107 @@ declare class Context {
|
|
|
58
99
|
constructor(deviceId: string);
|
|
59
100
|
}
|
|
60
101
|
//#endregion
|
|
61
|
-
//#region src/
|
|
62
|
-
declare
|
|
102
|
+
//#region src/pairing.d.ts
|
|
103
|
+
declare abstract class BasePairing {
|
|
104
|
+
#private;
|
|
105
|
+
get context(): Context;
|
|
106
|
+
constructor(context: Context);
|
|
107
|
+
tlv(buffer: Buffer): Map<number, Buffer>;
|
|
108
|
+
}
|
|
109
|
+
declare class AccessoryPair extends BasePairing {
|
|
110
|
+
#private;
|
|
111
|
+
constructor(context: Context, requestHandler: RequestHandler);
|
|
112
|
+
start(): Promise<void>;
|
|
113
|
+
pin(askPin: () => Promise<string>): Promise<AccessoryCredentials>;
|
|
114
|
+
transient(): Promise<AccessoryKeys>;
|
|
115
|
+
m1(additionalTlv?: [number, number | Buffer][]): Promise<PairM1>;
|
|
116
|
+
m2(m1: PairM1, pin?: string): Promise<PairM2>;
|
|
117
|
+
m3(m2: PairM2): Promise<PairM3>;
|
|
118
|
+
m4(m3: PairM3): Promise<PairM4>;
|
|
119
|
+
m5(m4: PairM4): Promise<PairM5>;
|
|
120
|
+
m6(m4: PairM4, m5: PairM5): Promise<AccessoryCredentials>;
|
|
121
|
+
}
|
|
122
|
+
declare class AccessoryVerify extends BasePairing {
|
|
123
|
+
#private;
|
|
124
|
+
constructor(context: Context, requestHandler: RequestHandler);
|
|
125
|
+
start(credentials: AccessoryCredentials): Promise<AccessoryKeys>;
|
|
126
|
+
}
|
|
127
|
+
type RequestHandler = (step: 'm1' | 'm3' | 'm5', data: Buffer) => Promise<Buffer>;
|
|
128
|
+
type PairM1 = {
|
|
129
|
+
readonly publicKey: Buffer;
|
|
130
|
+
readonly salt: Buffer;
|
|
131
|
+
};
|
|
132
|
+
type PairM2 = {
|
|
133
|
+
readonly publicKey: Buffer;
|
|
134
|
+
readonly proof: Buffer;
|
|
135
|
+
};
|
|
136
|
+
type PairM3 = {
|
|
137
|
+
readonly serverProof: Buffer;
|
|
138
|
+
};
|
|
139
|
+
type PairM4 = {
|
|
140
|
+
readonly sharedSecret: Buffer;
|
|
141
|
+
};
|
|
142
|
+
type PairM5 = {
|
|
143
|
+
readonly authTag: Buffer;
|
|
144
|
+
readonly data: Buffer;
|
|
145
|
+
readonly sessionKey: Buffer;
|
|
146
|
+
};
|
|
147
|
+
type AccessoryCredentials = {
|
|
148
|
+
readonly accessoryIdentifier: string;
|
|
149
|
+
readonly accessoryLongTermPublicKey: Buffer;
|
|
150
|
+
readonly pairingId: Buffer;
|
|
151
|
+
readonly publicKey: Buffer;
|
|
152
|
+
readonly secretKey: Buffer;
|
|
153
|
+
};
|
|
154
|
+
type AccessoryKeys = {
|
|
155
|
+
readonly pairingId: Buffer;
|
|
156
|
+
readonly sharedSecret: Buffer;
|
|
157
|
+
readonly accessoryToControllerKey: Buffer;
|
|
158
|
+
readonly controllerToAccessoryKey: Buffer;
|
|
159
|
+
};
|
|
160
|
+
//#endregion
|
|
161
|
+
//#region src/storage.d.ts
|
|
162
|
+
type ProtocolType = 'airplay' | 'companionLink' | 'raop';
|
|
163
|
+
type StoredDevice = {
|
|
164
|
+
readonly identifier: string;
|
|
165
|
+
readonly name: string;
|
|
166
|
+
};
|
|
167
|
+
type StorageData = {
|
|
168
|
+
version: 1;
|
|
169
|
+
devices: Record<string, StoredDevice>;
|
|
170
|
+
credentials: Record<string, SerializedCredentials>;
|
|
171
|
+
};
|
|
172
|
+
type SerializedCredentials = {
|
|
173
|
+
readonly accessoryIdentifier: string;
|
|
174
|
+
readonly accessoryLongTermPublicKey: string;
|
|
175
|
+
readonly pairingId: string;
|
|
176
|
+
readonly publicKey: string;
|
|
177
|
+
readonly secretKey: string;
|
|
178
|
+
};
|
|
179
|
+
declare abstract class Storage {
|
|
180
|
+
#private;
|
|
181
|
+
get data(): StorageData;
|
|
182
|
+
abstract load(): Promise<void>;
|
|
183
|
+
abstract save(): Promise<void>;
|
|
184
|
+
protected setData(data: StorageData): void;
|
|
185
|
+
getDevice(identifier: string): StoredDevice | undefined;
|
|
186
|
+
setDevice(identifier: string, device: StoredDevice): void;
|
|
187
|
+
removeDevice(identifier: string): void;
|
|
188
|
+
listDevices(): StoredDevice[];
|
|
189
|
+
getCredentials(deviceId: string, protocol: ProtocolType): AccessoryCredentials | undefined;
|
|
190
|
+
setCredentials(deviceId: string, protocol: ProtocolType, credentials: AccessoryCredentials): void;
|
|
191
|
+
removeCredentials(deviceId: string, protocol: ProtocolType): void;
|
|
192
|
+
}
|
|
193
|
+
declare class JsonStorage extends Storage {
|
|
194
|
+
#private;
|
|
195
|
+
constructor(path?: string);
|
|
196
|
+
load(): Promise<void>;
|
|
197
|
+
save(): Promise<void>;
|
|
198
|
+
}
|
|
199
|
+
declare class MemoryStorage extends Storage {
|
|
200
|
+
load(): Promise<void>;
|
|
201
|
+
save(): Promise<void>;
|
|
202
|
+
}
|
|
63
203
|
//#endregion
|
|
64
204
|
//#region src/types.d.ts
|
|
65
205
|
type ConnectionState = 'closing' | 'connected' | 'connecting' | 'disconnected' | 'failed';
|
|
@@ -145,7 +285,48 @@ type Result = {
|
|
|
145
285
|
type DiscoveryResult = {
|
|
146
286
|
readonly id: string;
|
|
147
287
|
readonly txt: Record<string, string>;
|
|
288
|
+
readonly features?: bigint;
|
|
148
289
|
} & Result;
|
|
290
|
+
type CombinedDiscoveryResult = {
|
|
291
|
+
readonly id: string;
|
|
292
|
+
readonly name: string;
|
|
293
|
+
readonly address: string;
|
|
294
|
+
airplay?: DiscoveryResult;
|
|
295
|
+
companionLink?: DiscoveryResult;
|
|
296
|
+
raop?: DiscoveryResult;
|
|
297
|
+
};
|
|
298
|
+
//#endregion
|
|
299
|
+
//#region src/discovery.d.ts
|
|
300
|
+
declare class Discovery {
|
|
301
|
+
#private;
|
|
302
|
+
constructor(service: string);
|
|
303
|
+
find(useCache?: boolean): Promise<DiscoveryResult[]>;
|
|
304
|
+
findUntil(id: string, tries?: number, timeout?: number): Promise<DiscoveryResult>;
|
|
305
|
+
static clearCache(): void;
|
|
306
|
+
static wake(address: string): Promise<void>;
|
|
307
|
+
static discoverAll(): Promise<CombinedDiscoveryResult[]>;
|
|
308
|
+
static airplay(): Discovery;
|
|
309
|
+
static companionLink(): Discovery;
|
|
310
|
+
static raop(): Discovery;
|
|
311
|
+
}
|
|
312
|
+
//#endregion
|
|
313
|
+
//#region src/mdns.d.ts
|
|
314
|
+
type MdnsService = {
|
|
315
|
+
readonly name: string;
|
|
316
|
+
readonly type: string;
|
|
317
|
+
readonly address: string;
|
|
318
|
+
readonly port: number;
|
|
319
|
+
readonly properties: Record<string, string>;
|
|
320
|
+
};
|
|
321
|
+
declare const unicast: (hosts: string[], services: string[], timeout?: number) => Promise<MdnsService[]>;
|
|
322
|
+
declare const multicast: (services: string[], timeout?: number) => Promise<MdnsService[]>;
|
|
323
|
+
//#endregion
|
|
324
|
+
//#region src/cli.d.ts
|
|
325
|
+
declare function prompt(message: string): Promise<string>;
|
|
326
|
+
declare function waitFor(ms: number): Promise<void>;
|
|
327
|
+
//#endregion
|
|
328
|
+
//#region src/symbols.d.ts
|
|
329
|
+
declare const ENCRYPTION: unique symbol;
|
|
149
330
|
//#endregion
|
|
150
331
|
//#region src/connection.d.ts
|
|
151
332
|
type ConnectionEventMap = {
|
|
@@ -191,65 +372,6 @@ declare const AIRPLAY_SERVICE = "_airplay._tcp.local";
|
|
|
191
372
|
declare const COMPANION_LINK_SERVICE = "_companion-link._tcp.local";
|
|
192
373
|
declare const RAOP_SERVICE = "_raop._tcp.local";
|
|
193
374
|
//#endregion
|
|
194
|
-
//#region src/pairing.d.ts
|
|
195
|
-
declare abstract class BasePairing {
|
|
196
|
-
#private;
|
|
197
|
-
get context(): Context;
|
|
198
|
-
constructor(context: Context);
|
|
199
|
-
tlv(buffer: Buffer): Map<number, Buffer>;
|
|
200
|
-
}
|
|
201
|
-
declare class AccessoryPair extends BasePairing {
|
|
202
|
-
#private;
|
|
203
|
-
constructor(context: Context, requestHandler: RequestHandler);
|
|
204
|
-
start(): Promise<void>;
|
|
205
|
-
pin(askPin: () => Promise<string>): Promise<AccessoryCredentials>;
|
|
206
|
-
transient(): Promise<AccessoryKeys>;
|
|
207
|
-
m1(additionalTlv?: [number, number | Buffer][]): Promise<PairM1>;
|
|
208
|
-
m2(m1: PairM1, pin?: string): Promise<PairM2>;
|
|
209
|
-
m3(m2: PairM2): Promise<PairM3>;
|
|
210
|
-
m4(m3: PairM3): Promise<PairM4>;
|
|
211
|
-
m5(m4: PairM4): Promise<PairM5>;
|
|
212
|
-
m6(m4: PairM4, m5: PairM5): Promise<AccessoryCredentials>;
|
|
213
|
-
}
|
|
214
|
-
declare class AccessoryVerify extends BasePairing {
|
|
215
|
-
#private;
|
|
216
|
-
constructor(context: Context, requestHandler: RequestHandler);
|
|
217
|
-
start(credentials: AccessoryCredentials): Promise<AccessoryKeys>;
|
|
218
|
-
}
|
|
219
|
-
type RequestHandler = (step: 'm1' | 'm3' | 'm5', data: Buffer) => Promise<Buffer>;
|
|
220
|
-
type PairM1 = {
|
|
221
|
-
readonly publicKey: Buffer;
|
|
222
|
-
readonly salt: Buffer;
|
|
223
|
-
};
|
|
224
|
-
type PairM2 = {
|
|
225
|
-
readonly publicKey: Buffer;
|
|
226
|
-
readonly proof: Buffer;
|
|
227
|
-
};
|
|
228
|
-
type PairM3 = {
|
|
229
|
-
readonly serverProof: Buffer;
|
|
230
|
-
};
|
|
231
|
-
type PairM4 = {
|
|
232
|
-
readonly sharedSecret: Buffer;
|
|
233
|
-
};
|
|
234
|
-
type PairM5 = {
|
|
235
|
-
readonly authTag: Buffer;
|
|
236
|
-
readonly data: Buffer;
|
|
237
|
-
readonly sessionKey: Buffer;
|
|
238
|
-
};
|
|
239
|
-
type AccessoryCredentials = {
|
|
240
|
-
readonly accessoryIdentifier: string;
|
|
241
|
-
readonly accessoryLongTermPublicKey: Buffer;
|
|
242
|
-
readonly pairingId: Buffer;
|
|
243
|
-
readonly publicKey: Buffer;
|
|
244
|
-
readonly secretKey: Buffer;
|
|
245
|
-
};
|
|
246
|
-
type AccessoryKeys = {
|
|
247
|
-
readonly pairingId: Buffer;
|
|
248
|
-
readonly sharedSecret: Buffer;
|
|
249
|
-
readonly accessoryToControllerKey: Buffer;
|
|
250
|
-
readonly controllerToAccessoryKey: Buffer;
|
|
251
|
-
};
|
|
252
|
-
//#endregion
|
|
253
375
|
//#region src/timing.d.ts
|
|
254
376
|
declare class TimingServer {
|
|
255
377
|
#private;
|
|
@@ -279,5 +401,4 @@ interface AudioSource {
|
|
|
279
401
|
stop(): Promise<void>;
|
|
280
402
|
}
|
|
281
403
|
//#endregion
|
|
282
|
-
export { AIRPLAY_SERVICE, AIRPLAY_TRANSIENT_PIN, type AccessoryCredentials, type AccessoryKeys, AccessoryPair, AccessoryVerify, type AudioSource, COMPANION_LINK_SERVICE, Connection, type ConnectionState, Context, Discovery, type DiscoveryResult, ENCRYPTION, EncryptionAwareConnection, EncryptionState, type EventMap, HTTP_TIMEOUT, type Logger, RAOP_SERVICE, type Reporter, TimingServer, generateActiveRemoteId, generateDacpId, generateSessionId, getLocalIP, getMacAddress, prompt, randomInt32, randomInt64, reporter, uint16ToBE, uint53ToLE, v4 as uuid, waitFor };
|
|
283
|
-
//# sourceMappingURL=index.d.mts.map
|
|
404
|
+
export { AIRPLAY_SERVICE, AIRPLAY_TRANSIENT_PIN, type AccessoryCredentials, type AccessoryKeys, AccessoryPair, AccessoryVerify, type AirPlayFeatureFlagName, AirPlayFeatureFlags, type AudioSource, COMPANION_LINK_SERVICE, type CombinedDiscoveryResult, Connection, type ConnectionState, Context, Discovery, type DiscoveryResult, ENCRYPTION, EncryptionAwareConnection, EncryptionState, type EventMap, HTTP_TIMEOUT, JsonStorage, type Logger, type MdnsService, MemoryStorage, type PairingRequirement, type ProtocolType, RAOP_SERVICE, type Reporter, Storage, type StorageData, type StoredDevice, TimingServer, describeFlags, generateActiveRemoteId, generateDacpId, generateSessionId, getLocalIP, getMacAddress, getPairingRequirement, getProtocolVersion, hasFeatureFlag, isPasswordRequired, isRemoteControlSupported, multicast as mdnsMulticast, unicast as mdnsUnicast, parseFeatures, prompt, randomInt32, randomInt64, reporter, uint16ToBE, uint53ToLE, v4 as uuid, waitFor };
|
package/dist/index.mjs
CHANGED
|
@@ -1,14 +1,20 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
1
2
|
import { randomBytes, randomFillSync, randomUUID } from "node:crypto";
|
|
2
|
-
import
|
|
3
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
4
|
+
import { dirname, join } from "node:path";
|
|
5
|
+
import { Socket, createConnection } from "node:net";
|
|
3
6
|
import { createInterface } from "node:readline";
|
|
7
|
+
import { createSocket } from "node:dgram";
|
|
8
|
+
import { networkInterfaces } from "node:os";
|
|
4
9
|
import { EventEmitter } from "node:events";
|
|
5
|
-
import { Socket } from "node:net";
|
|
6
10
|
import { NTP, OPack, TLV8 } from "@basmilius/apple-encoding";
|
|
7
11
|
import { Chacha20, Curve25519, Ed25519, hkdf } from "@basmilius/apple-encryption";
|
|
8
12
|
import { SRP, SrpClient } from "fast-srp-hap";
|
|
9
|
-
import { createSocket } from "node:dgram";
|
|
10
|
-
import { networkInterfaces } from "node:os";
|
|
11
13
|
|
|
14
|
+
//#region \0rolldown/runtime.js
|
|
15
|
+
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
16
|
+
|
|
17
|
+
//#endregion
|
|
12
18
|
//#region ../../node_modules/.bun/uuid@13.0.0/node_modules/uuid/dist-node/stringify.js
|
|
13
19
|
const byteToHex = [];
|
|
14
20
|
for (let i = 0; i < 256; ++i) byteToHex.push((i + 256).toString(16).slice(1));
|
|
@@ -53,6 +59,178 @@ function v4(options, buf, offset) {
|
|
|
53
59
|
return _v4(options, buf, offset);
|
|
54
60
|
}
|
|
55
61
|
|
|
62
|
+
//#endregion
|
|
63
|
+
//#region src/airplayFeatures.ts
|
|
64
|
+
const AirPlayFeatureFlags = {
|
|
65
|
+
SupportsAirPlayVideoV1: 1n << 0n,
|
|
66
|
+
SupportsAirPlayPhoto: 1n << 1n,
|
|
67
|
+
SupportsAirPlayVideoFairPlay: 1n << 2n,
|
|
68
|
+
SupportsAirPlayVideoVolumeControl: 1n << 3n,
|
|
69
|
+
SupportsAirPlayVideoHTTPLiveStreams: 1n << 4n,
|
|
70
|
+
SupportsAirPlaySlideShow: 1n << 5n,
|
|
71
|
+
SupportsAirPlayScreen: 1n << 7n,
|
|
72
|
+
SupportsAirPlayAudio: 1n << 9n,
|
|
73
|
+
AudioRedundant: 1n << 11n,
|
|
74
|
+
Authentication_4: 1n << 14n,
|
|
75
|
+
MetadataFeatures_0: 1n << 15n,
|
|
76
|
+
MetadataFeatures_1: 1n << 16n,
|
|
77
|
+
MetadataFeatures_2: 1n << 17n,
|
|
78
|
+
AudioFormats_0: 1n << 18n,
|
|
79
|
+
AudioFormats_1: 1n << 19n,
|
|
80
|
+
AudioFormats_2: 1n << 20n,
|
|
81
|
+
AudioFormats_3: 1n << 21n,
|
|
82
|
+
Authentication_1: 1n << 23n,
|
|
83
|
+
Authentication_8: 1n << 26n,
|
|
84
|
+
SupportsLegacyPairing: 1n << 27n,
|
|
85
|
+
HasUnifiedAdvertiserInfo: 1n << 30n,
|
|
86
|
+
IsCarPlay: 1n << 32n,
|
|
87
|
+
SupportsAirPlayVideoPlayQueue: 1n << 33n,
|
|
88
|
+
SupportsAirPlayFromCloud: 1n << 34n,
|
|
89
|
+
SupportsTLS_PSK: 1n << 35n,
|
|
90
|
+
SupportsUnifiedMediaControl: 1n << 38n,
|
|
91
|
+
SupportsBufferedAudio: 1n << 40n,
|
|
92
|
+
SupportsPTP: 1n << 41n,
|
|
93
|
+
SupportsScreenMultiCodec: 1n << 42n,
|
|
94
|
+
SupportsSystemPairing: 1n << 43n,
|
|
95
|
+
IsAPValeriaScreenSender: 1n << 44n,
|
|
96
|
+
SupportsHKPairingAndAccessControl: 1n << 46n,
|
|
97
|
+
SupportsCoreUtilsPairingAndEncryption: 1n << 48n,
|
|
98
|
+
SupportsAirPlayVideoV2: 1n << 49n,
|
|
99
|
+
MetadataFeatures_3: 1n << 50n,
|
|
100
|
+
SupportsUnifiedPairSetupAndMFi: 1n << 51n,
|
|
101
|
+
SupportsSetPeersExtendedMessage: 1n << 52n,
|
|
102
|
+
SupportsAPSync: 1n << 54n,
|
|
103
|
+
SupportsWoL: 1n << 55n,
|
|
104
|
+
SupportsWoL2: 1n << 56n,
|
|
105
|
+
SupportsHangdogRemoteControl: 1n << 58n,
|
|
106
|
+
SupportsAudioStreamConnectionSetup: 1n << 59n,
|
|
107
|
+
SupportsAudioMetadataControl: 1n << 60n,
|
|
108
|
+
SupportsRFC2198Redundancy: 1n << 61n
|
|
109
|
+
};
|
|
110
|
+
const PASSWORD_BIT = 128n;
|
|
111
|
+
const LEGACY_PAIRING_BIT = 512n;
|
|
112
|
+
const PIN_REQUIRED_BIT = 8n;
|
|
113
|
+
const parseFeatures = (features) => {
|
|
114
|
+
const parts = features.split(",").map((part) => part.trim());
|
|
115
|
+
if (parts.length === 1) return BigInt(parts[0]);
|
|
116
|
+
if (parts.length === 2) {
|
|
117
|
+
const low = BigInt(parts[0]);
|
|
118
|
+
return BigInt(parts[1]) << 32n | low;
|
|
119
|
+
}
|
|
120
|
+
throw new Error(`Invalid features format: ${features}`);
|
|
121
|
+
};
|
|
122
|
+
const hasFeatureFlag = (features, flag) => (features & flag) !== 0n;
|
|
123
|
+
const describeFlags = (features) => {
|
|
124
|
+
const result = [];
|
|
125
|
+
for (const [name, flag] of Object.entries(AirPlayFeatureFlags)) if (hasFeatureFlag(features, flag)) result.push(name);
|
|
126
|
+
return result;
|
|
127
|
+
};
|
|
128
|
+
const getProtocolVersion = (txt) => {
|
|
129
|
+
const featuresStr = txt.features ?? txt.ft;
|
|
130
|
+
if (!featuresStr) return 1;
|
|
131
|
+
const features = parseFeatures(featuresStr);
|
|
132
|
+
if (hasFeatureFlag(features, AirPlayFeatureFlags.SupportsUnifiedMediaControl)) return 2;
|
|
133
|
+
if (hasFeatureFlag(features, AirPlayFeatureFlags.SupportsCoreUtilsPairingAndEncryption)) return 2;
|
|
134
|
+
return 1;
|
|
135
|
+
};
|
|
136
|
+
const getPairingRequirement = (txt) => {
|
|
137
|
+
const featuresStr = txt.features ?? txt.ft;
|
|
138
|
+
if (!featuresStr) return "none";
|
|
139
|
+
const features = parseFeatures(featuresStr);
|
|
140
|
+
const sf = txt.sf ? BigInt(txt.sf) : 0n;
|
|
141
|
+
if (hasFeatureFlag(features, AirPlayFeatureFlags.SupportsHKPairingAndAccessControl)) return "homekit";
|
|
142
|
+
if ((sf & PIN_REQUIRED_BIT) !== 0n) return "pin";
|
|
143
|
+
if (hasFeatureFlag(features, AirPlayFeatureFlags.SupportsSystemPairing)) return "transient";
|
|
144
|
+
if ((sf & LEGACY_PAIRING_BIT) !== 0n) return "pin";
|
|
145
|
+
return "none";
|
|
146
|
+
};
|
|
147
|
+
const isPasswordRequired = (txt) => {
|
|
148
|
+
if (txt.pw === "true") return true;
|
|
149
|
+
return ((txt.sf ? BigInt(txt.sf) : 0n) & PASSWORD_BIT) !== 0n;
|
|
150
|
+
};
|
|
151
|
+
const isRemoteControlSupported = (txt) => {
|
|
152
|
+
const featuresStr = txt.features ?? txt.ft;
|
|
153
|
+
if (!featuresStr) return false;
|
|
154
|
+
return hasFeatureFlag(parseFeatures(featuresStr), AirPlayFeatureFlags.SupportsHangdogRemoteControl);
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
//#endregion
|
|
158
|
+
//#region src/storage.ts
|
|
159
|
+
const credentialKey = (deviceId, protocol) => `${deviceId}:${protocol}`;
|
|
160
|
+
const serializeCredentials = (credentials) => ({
|
|
161
|
+
accessoryIdentifier: credentials.accessoryIdentifier,
|
|
162
|
+
accessoryLongTermPublicKey: credentials.accessoryLongTermPublicKey.toString("base64"),
|
|
163
|
+
pairingId: credentials.pairingId.toString("base64"),
|
|
164
|
+
publicKey: credentials.publicKey.toString("base64"),
|
|
165
|
+
secretKey: credentials.secretKey.toString("base64")
|
|
166
|
+
});
|
|
167
|
+
const deserializeCredentials = (stored) => ({
|
|
168
|
+
accessoryIdentifier: stored.accessoryIdentifier,
|
|
169
|
+
accessoryLongTermPublicKey: Buffer.from(stored.accessoryLongTermPublicKey, "base64"),
|
|
170
|
+
pairingId: Buffer.from(stored.pairingId, "base64"),
|
|
171
|
+
publicKey: Buffer.from(stored.publicKey, "base64"),
|
|
172
|
+
secretKey: Buffer.from(stored.secretKey, "base64")
|
|
173
|
+
});
|
|
174
|
+
const createEmptyData = () => ({
|
|
175
|
+
version: 1,
|
|
176
|
+
devices: {},
|
|
177
|
+
credentials: {}
|
|
178
|
+
});
|
|
179
|
+
var Storage = class {
|
|
180
|
+
#data = createEmptyData();
|
|
181
|
+
get data() {
|
|
182
|
+
return this.#data;
|
|
183
|
+
}
|
|
184
|
+
setData(data) {
|
|
185
|
+
this.#data = data;
|
|
186
|
+
}
|
|
187
|
+
getDevice(identifier) {
|
|
188
|
+
return this.#data.devices[identifier];
|
|
189
|
+
}
|
|
190
|
+
setDevice(identifier, device) {
|
|
191
|
+
this.#data.devices[identifier] = device;
|
|
192
|
+
}
|
|
193
|
+
removeDevice(identifier) {
|
|
194
|
+
delete this.#data.devices[identifier];
|
|
195
|
+
for (const key of Object.keys(this.#data.credentials)) if (key.startsWith(`${identifier}:`)) delete this.#data.credentials[key];
|
|
196
|
+
}
|
|
197
|
+
listDevices() {
|
|
198
|
+
return Object.values(this.#data.devices);
|
|
199
|
+
}
|
|
200
|
+
getCredentials(deviceId, protocol) {
|
|
201
|
+
const stored = this.#data.credentials[credentialKey(deviceId, protocol)];
|
|
202
|
+
if (!stored) return;
|
|
203
|
+
return deserializeCredentials(stored);
|
|
204
|
+
}
|
|
205
|
+
setCredentials(deviceId, protocol, credentials) {
|
|
206
|
+
this.#data.credentials[credentialKey(deviceId, protocol)] = serializeCredentials(credentials);
|
|
207
|
+
}
|
|
208
|
+
removeCredentials(deviceId, protocol) {
|
|
209
|
+
delete this.#data.credentials[credentialKey(deviceId, protocol)];
|
|
210
|
+
}
|
|
211
|
+
};
|
|
212
|
+
var JsonStorage = class extends Storage {
|
|
213
|
+
#path;
|
|
214
|
+
constructor(path) {
|
|
215
|
+
super();
|
|
216
|
+
this.#path = path ?? join(process.env.HOME ?? process.env.USERPROFILE ?? ".", ".config", "apple-protocols", "storage.json");
|
|
217
|
+
}
|
|
218
|
+
async load() {
|
|
219
|
+
if (!existsSync(this.#path)) return;
|
|
220
|
+
const raw = readFileSync(this.#path, "utf-8");
|
|
221
|
+
const json = JSON.parse(raw);
|
|
222
|
+
if (json.version === 1) this.setData(json);
|
|
223
|
+
}
|
|
224
|
+
async save() {
|
|
225
|
+
mkdirSync(dirname(this.#path), { recursive: true });
|
|
226
|
+
writeFileSync(this.#path, JSON.stringify(this.data, null, 2), "utf-8");
|
|
227
|
+
}
|
|
228
|
+
};
|
|
229
|
+
var MemoryStorage = class extends Storage {
|
|
230
|
+
async load() {}
|
|
231
|
+
async save() {}
|
|
232
|
+
};
|
|
233
|
+
|
|
56
234
|
//#endregion
|
|
57
235
|
//#region src/cli.ts
|
|
58
236
|
async function prompt(message) {
|
|
@@ -77,23 +255,452 @@ const AIRPLAY_SERVICE = "_airplay._tcp.local";
|
|
|
77
255
|
const COMPANION_LINK_SERVICE = "_companion-link._tcp.local";
|
|
78
256
|
const RAOP_SERVICE = "_raop._tcp.local";
|
|
79
257
|
|
|
258
|
+
//#endregion
|
|
259
|
+
//#region src/mdns.ts
|
|
260
|
+
const MDNS_ADDRESS = "224.0.0.251";
|
|
261
|
+
const MDNS_PORT = 5353;
|
|
262
|
+
const QUERY_ID = 13823;
|
|
263
|
+
const SERVICES_PER_MSG = 3;
|
|
264
|
+
const QueryType = {
|
|
265
|
+
A: 1,
|
|
266
|
+
PTR: 12,
|
|
267
|
+
TXT: 16,
|
|
268
|
+
AAAA: 28,
|
|
269
|
+
SRV: 33,
|
|
270
|
+
ANY: 255
|
|
271
|
+
};
|
|
272
|
+
const encodeQName = (name) => {
|
|
273
|
+
const parts = [];
|
|
274
|
+
const labels = splitServiceName(name);
|
|
275
|
+
for (const label of labels) {
|
|
276
|
+
const encoded = Buffer.from(label, "utf-8");
|
|
277
|
+
if (encoded.byteLength > 63) parts.push(Buffer.from([63]), encoded.subarray(0, 63));
|
|
278
|
+
else parts.push(Buffer.from([encoded.byteLength]), encoded);
|
|
279
|
+
}
|
|
280
|
+
parts.push(Buffer.from([0]));
|
|
281
|
+
return Buffer.concat(parts);
|
|
282
|
+
};
|
|
283
|
+
const splitServiceName = (name) => {
|
|
284
|
+
const match = name.match(/\._[a-z]+\._(?:tcp|udp)\.local$/);
|
|
285
|
+
if (match) return [name.substring(0, match.index), ...match[0].substring(1).split(".")];
|
|
286
|
+
return name.split(".");
|
|
287
|
+
};
|
|
288
|
+
const encodeDnsHeader = (header) => {
|
|
289
|
+
const buf = Buffer.allocUnsafe(12);
|
|
290
|
+
buf.writeUInt16BE(header.id, 0);
|
|
291
|
+
buf.writeUInt16BE(header.flags, 2);
|
|
292
|
+
buf.writeUInt16BE(header.qdcount, 4);
|
|
293
|
+
buf.writeUInt16BE(header.ancount, 6);
|
|
294
|
+
buf.writeUInt16BE(header.nscount, 8);
|
|
295
|
+
buf.writeUInt16BE(header.arcount, 10);
|
|
296
|
+
return buf;
|
|
297
|
+
};
|
|
298
|
+
const encodeDnsQuestion = (name, qtype, unicastResponse = false) => {
|
|
299
|
+
const qname = encodeQName(name);
|
|
300
|
+
const suffix = Buffer.allocUnsafe(4);
|
|
301
|
+
suffix.writeUInt16BE(qtype, 0);
|
|
302
|
+
suffix.writeUInt16BE(unicastResponse ? 32769 : 1, 2);
|
|
303
|
+
return Buffer.concat([qname, suffix]);
|
|
304
|
+
};
|
|
305
|
+
const createQueryPackets = (services, qtype = QueryType.PTR, unicastResponse = false) => {
|
|
306
|
+
const packets = [];
|
|
307
|
+
for (let i = 0; i < services.length; i += SERVICES_PER_MSG) {
|
|
308
|
+
const chunk = services.slice(i, i + SERVICES_PER_MSG);
|
|
309
|
+
const questions = chunk.map((s) => encodeDnsQuestion(s, qtype, unicastResponse));
|
|
310
|
+
const header = encodeDnsHeader({
|
|
311
|
+
id: QUERY_ID,
|
|
312
|
+
flags: 0,
|
|
313
|
+
qdcount: chunk.length,
|
|
314
|
+
ancount: 0,
|
|
315
|
+
nscount: 0,
|
|
316
|
+
arcount: 0
|
|
317
|
+
});
|
|
318
|
+
packets.push(Buffer.concat([header, ...questions]));
|
|
319
|
+
}
|
|
320
|
+
return packets;
|
|
321
|
+
};
|
|
322
|
+
const decodeQName = (buf, offset) => {
|
|
323
|
+
const labels = [];
|
|
324
|
+
let currentOffset = offset;
|
|
325
|
+
let jumped = false;
|
|
326
|
+
let returnOffset = offset;
|
|
327
|
+
while (currentOffset < buf.byteLength) {
|
|
328
|
+
const length = buf[currentOffset];
|
|
329
|
+
if (length === 0) {
|
|
330
|
+
if (!jumped) returnOffset = currentOffset + 1;
|
|
331
|
+
break;
|
|
332
|
+
}
|
|
333
|
+
if ((length & 192) === 192) {
|
|
334
|
+
const pointer = (length & 63) << 8 | buf[currentOffset + 1];
|
|
335
|
+
if (!jumped) returnOffset = currentOffset + 2;
|
|
336
|
+
currentOffset = pointer;
|
|
337
|
+
jumped = true;
|
|
338
|
+
continue;
|
|
339
|
+
}
|
|
340
|
+
currentOffset++;
|
|
341
|
+
labels.push(buf.toString("utf-8", currentOffset, currentOffset + length));
|
|
342
|
+
currentOffset += length;
|
|
343
|
+
if (!jumped) returnOffset = currentOffset;
|
|
344
|
+
}
|
|
345
|
+
return [labels.join("."), returnOffset];
|
|
346
|
+
};
|
|
347
|
+
const decodeDnsHeader = (buf) => ({
|
|
348
|
+
id: buf.readUInt16BE(0),
|
|
349
|
+
flags: buf.readUInt16BE(2),
|
|
350
|
+
qdcount: buf.readUInt16BE(4),
|
|
351
|
+
ancount: buf.readUInt16BE(6),
|
|
352
|
+
nscount: buf.readUInt16BE(8),
|
|
353
|
+
arcount: buf.readUInt16BE(10)
|
|
354
|
+
});
|
|
355
|
+
const decodeQuestion = (buf, offset) => {
|
|
356
|
+
const [qname, newOffset] = decodeQName(buf, offset);
|
|
357
|
+
return [{
|
|
358
|
+
qname,
|
|
359
|
+
qtype: buf.readUInt16BE(newOffset),
|
|
360
|
+
qclass: buf.readUInt16BE(newOffset + 2)
|
|
361
|
+
}, newOffset + 4];
|
|
362
|
+
};
|
|
363
|
+
const decodeTxtRecord = (buf, offset, length) => {
|
|
364
|
+
const properties = {};
|
|
365
|
+
let pos = offset;
|
|
366
|
+
const end = offset + length;
|
|
367
|
+
while (pos < end) {
|
|
368
|
+
const strLen = buf[pos];
|
|
369
|
+
pos++;
|
|
370
|
+
if (strLen === 0 || pos + strLen > end) break;
|
|
371
|
+
const str = buf.toString("utf-8", pos, pos + strLen);
|
|
372
|
+
pos += strLen;
|
|
373
|
+
const eqIndex = str.indexOf("=");
|
|
374
|
+
if (eqIndex >= 0) properties[str.substring(0, eqIndex)] = str.substring(eqIndex + 1);
|
|
375
|
+
else properties[str] = "";
|
|
376
|
+
}
|
|
377
|
+
return properties;
|
|
378
|
+
};
|
|
379
|
+
const decodeSrvRecord = (buf, offset) => {
|
|
380
|
+
const priority = buf.readUInt16BE(offset);
|
|
381
|
+
const weight = buf.readUInt16BE(offset + 2);
|
|
382
|
+
const port = buf.readUInt16BE(offset + 4);
|
|
383
|
+
const [target] = decodeQName(buf, offset + 6);
|
|
384
|
+
return {
|
|
385
|
+
priority,
|
|
386
|
+
weight,
|
|
387
|
+
port,
|
|
388
|
+
target
|
|
389
|
+
};
|
|
390
|
+
};
|
|
391
|
+
const decodeResource = (buf, offset) => {
|
|
392
|
+
const [qname, nameEnd] = decodeQName(buf, offset);
|
|
393
|
+
const qtype = buf.readUInt16BE(nameEnd);
|
|
394
|
+
const qclass = buf.readUInt16BE(nameEnd + 2);
|
|
395
|
+
const ttl = buf.readUInt32BE(nameEnd + 4);
|
|
396
|
+
const rdLength = buf.readUInt16BE(nameEnd + 8);
|
|
397
|
+
const rdOffset = nameEnd + 10;
|
|
398
|
+
let rdata;
|
|
399
|
+
switch (qtype) {
|
|
400
|
+
case QueryType.A:
|
|
401
|
+
rdata = `${buf[rdOffset]}.${buf[rdOffset + 1]}.${buf[rdOffset + 2]}.${buf[rdOffset + 3]}`;
|
|
402
|
+
break;
|
|
403
|
+
case QueryType.AAAA: {
|
|
404
|
+
const parts = [];
|
|
405
|
+
for (let i = 0; i < 8; i++) parts.push(buf.readUInt16BE(rdOffset + i * 2).toString(16));
|
|
406
|
+
rdata = parts.join(":");
|
|
407
|
+
break;
|
|
408
|
+
}
|
|
409
|
+
case QueryType.PTR: {
|
|
410
|
+
const [name] = decodeQName(buf, rdOffset);
|
|
411
|
+
rdata = name;
|
|
412
|
+
break;
|
|
413
|
+
}
|
|
414
|
+
case QueryType.SRV:
|
|
415
|
+
rdata = decodeSrvRecord(buf, rdOffset);
|
|
416
|
+
break;
|
|
417
|
+
case QueryType.TXT:
|
|
418
|
+
rdata = decodeTxtRecord(buf, rdOffset, rdLength);
|
|
419
|
+
break;
|
|
420
|
+
default: rdata = buf.subarray(rdOffset, rdOffset + rdLength);
|
|
421
|
+
}
|
|
422
|
+
return [{
|
|
423
|
+
qname,
|
|
424
|
+
qtype,
|
|
425
|
+
qclass,
|
|
426
|
+
ttl,
|
|
427
|
+
rdata
|
|
428
|
+
}, rdOffset + rdLength];
|
|
429
|
+
};
|
|
430
|
+
const decodeDnsResponse = (buf) => {
|
|
431
|
+
const header = decodeDnsHeader(buf);
|
|
432
|
+
let offset = 12;
|
|
433
|
+
for (let i = 0; i < header.qdcount; i++) {
|
|
434
|
+
const [, newOffset] = decodeQuestion(buf, offset);
|
|
435
|
+
offset = newOffset;
|
|
436
|
+
}
|
|
437
|
+
const answers = [];
|
|
438
|
+
for (let i = 0; i < header.ancount; i++) {
|
|
439
|
+
const [record, newOffset] = decodeResource(buf, offset);
|
|
440
|
+
answers.push(record);
|
|
441
|
+
offset = newOffset;
|
|
442
|
+
}
|
|
443
|
+
for (let i = 0; i < header.nscount; i++) {
|
|
444
|
+
const [, newOffset] = decodeResource(buf, offset);
|
|
445
|
+
offset = newOffset;
|
|
446
|
+
}
|
|
447
|
+
const resources = [];
|
|
448
|
+
for (let i = 0; i < header.arcount; i++) {
|
|
449
|
+
const [record, newOffset] = decodeResource(buf, offset);
|
|
450
|
+
resources.push(record);
|
|
451
|
+
offset = newOffset;
|
|
452
|
+
}
|
|
453
|
+
return {
|
|
454
|
+
header,
|
|
455
|
+
answers,
|
|
456
|
+
resources
|
|
457
|
+
};
|
|
458
|
+
};
|
|
459
|
+
var ServiceCollector = class {
|
|
460
|
+
#ptrMap = /* @__PURE__ */ new Map();
|
|
461
|
+
#srvMap = /* @__PURE__ */ new Map();
|
|
462
|
+
#txtMap = /* @__PURE__ */ new Map();
|
|
463
|
+
#addressMap = /* @__PURE__ */ new Map();
|
|
464
|
+
addRecords(answers, resources) {
|
|
465
|
+
for (const record of [...answers, ...resources]) switch (record.qtype) {
|
|
466
|
+
case QueryType.PTR: {
|
|
467
|
+
const existing = this.#ptrMap.get(record.qname);
|
|
468
|
+
if (existing) existing.add(record.rdata);
|
|
469
|
+
else this.#ptrMap.set(record.qname, new Set([record.rdata]));
|
|
470
|
+
break;
|
|
471
|
+
}
|
|
472
|
+
case QueryType.SRV:
|
|
473
|
+
this.#srvMap.set(record.qname, record.rdata);
|
|
474
|
+
break;
|
|
475
|
+
case QueryType.TXT:
|
|
476
|
+
this.#txtMap.set(record.qname, record.rdata);
|
|
477
|
+
break;
|
|
478
|
+
case QueryType.A:
|
|
479
|
+
this.#addressMap.set(record.qname, record.rdata);
|
|
480
|
+
break;
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
get services() {
|
|
484
|
+
const results = [];
|
|
485
|
+
for (const [serviceType, instanceNames] of this.#ptrMap) for (const instanceQName of instanceNames) {
|
|
486
|
+
const srv = this.#srvMap.get(instanceQName);
|
|
487
|
+
if (!srv || srv.port === 0) continue;
|
|
488
|
+
const address = this.#addressMap.get(srv.target);
|
|
489
|
+
if (!address) continue;
|
|
490
|
+
const txt = this.#txtMap.get(instanceQName) ?? {};
|
|
491
|
+
const typeIndex = instanceQName.indexOf("._");
|
|
492
|
+
const name = typeIndex >= 0 ? instanceQName.substring(0, typeIndex) : instanceQName;
|
|
493
|
+
if (!results.some((s) => s.name === name && s.type === serviceType)) results.push({
|
|
494
|
+
name,
|
|
495
|
+
type: serviceType,
|
|
496
|
+
address,
|
|
497
|
+
port: srv.port,
|
|
498
|
+
properties: txt
|
|
499
|
+
});
|
|
500
|
+
}
|
|
501
|
+
return results;
|
|
502
|
+
}
|
|
503
|
+
};
|
|
504
|
+
const WAKE_PORTS$1 = [
|
|
505
|
+
7e3,
|
|
506
|
+
3689,
|
|
507
|
+
49152,
|
|
508
|
+
32498
|
|
509
|
+
];
|
|
510
|
+
const knock = (address) => {
|
|
511
|
+
const promises = WAKE_PORTS$1.map((port) => new Promise((resolve) => {
|
|
512
|
+
const socket = createConnection({
|
|
513
|
+
host: address,
|
|
514
|
+
port,
|
|
515
|
+
timeout: 500
|
|
516
|
+
});
|
|
517
|
+
socket.on("connect", () => {
|
|
518
|
+
socket.destroy();
|
|
519
|
+
resolve();
|
|
520
|
+
});
|
|
521
|
+
socket.on("error", () => {
|
|
522
|
+
socket.destroy();
|
|
523
|
+
resolve();
|
|
524
|
+
});
|
|
525
|
+
socket.on("timeout", () => {
|
|
526
|
+
socket.destroy();
|
|
527
|
+
resolve();
|
|
528
|
+
});
|
|
529
|
+
}));
|
|
530
|
+
return Promise.all(promises).then(() => {});
|
|
531
|
+
};
|
|
532
|
+
const unicast = (hosts, services, timeout = 4) => {
|
|
533
|
+
return new Promise((resolve) => {
|
|
534
|
+
const queries = createQueryPackets(services);
|
|
535
|
+
const collector = new ServiceCollector();
|
|
536
|
+
const sockets = [];
|
|
537
|
+
let resolved = false;
|
|
538
|
+
const finish = () => {
|
|
539
|
+
if (resolved) return;
|
|
540
|
+
resolved = true;
|
|
541
|
+
clearInterval(interval);
|
|
542
|
+
for (const socket of sockets) try {
|
|
543
|
+
socket.close();
|
|
544
|
+
} catch {}
|
|
545
|
+
resolve(collector.services);
|
|
546
|
+
};
|
|
547
|
+
for (const host of hosts) {
|
|
548
|
+
const socket = createSocket("udp4");
|
|
549
|
+
sockets.push(socket);
|
|
550
|
+
socket.on("message", (data) => {
|
|
551
|
+
try {
|
|
552
|
+
const response = decodeDnsResponse(data);
|
|
553
|
+
collector.addRecords(response.answers, response.resources);
|
|
554
|
+
} catch {}
|
|
555
|
+
});
|
|
556
|
+
socket.on("error", () => {});
|
|
557
|
+
}
|
|
558
|
+
let interval;
|
|
559
|
+
Promise.all(hosts.map((h) => knock(h))).then(() => {
|
|
560
|
+
const sendQueries = () => {
|
|
561
|
+
for (let i = 0; i < hosts.length; i++) for (const query of queries) sockets[i]?.send(query, MDNS_PORT, hosts[i]);
|
|
562
|
+
};
|
|
563
|
+
sendQueries();
|
|
564
|
+
interval = setInterval(sendQueries, 1e3);
|
|
565
|
+
setTimeout(finish, timeout * 1e3);
|
|
566
|
+
});
|
|
567
|
+
});
|
|
568
|
+
};
|
|
569
|
+
const multicast = (services, timeout = 4) => {
|
|
570
|
+
return new Promise((resolve) => {
|
|
571
|
+
const collector = new ServiceCollector();
|
|
572
|
+
const queries = createQueryPackets(services);
|
|
573
|
+
const sockets = [];
|
|
574
|
+
let resolved = false;
|
|
575
|
+
let interval;
|
|
576
|
+
const finish = () => {
|
|
577
|
+
if (resolved) return;
|
|
578
|
+
resolved = true;
|
|
579
|
+
clearInterval(interval);
|
|
580
|
+
for (const socket of sockets) try {
|
|
581
|
+
socket.close();
|
|
582
|
+
} catch {}
|
|
583
|
+
resolve(collector.services);
|
|
584
|
+
};
|
|
585
|
+
const onMessage = (data) => {
|
|
586
|
+
try {
|
|
587
|
+
const response = decodeDnsResponse(data);
|
|
588
|
+
collector.addRecords(response.answers, response.resources);
|
|
589
|
+
} catch {}
|
|
590
|
+
};
|
|
591
|
+
const addSocket = (address, port) => {
|
|
592
|
+
return new Promise((resolveSocket) => {
|
|
593
|
+
const socket = createSocket({
|
|
594
|
+
type: "udp4",
|
|
595
|
+
reuseAddr: true
|
|
596
|
+
});
|
|
597
|
+
socket.on("message", onMessage);
|
|
598
|
+
socket.on("error", () => {
|
|
599
|
+
resolveSocket(null);
|
|
600
|
+
});
|
|
601
|
+
socket.bind(port, address ?? "", () => {
|
|
602
|
+
if (address) try {
|
|
603
|
+
socket.setMulticastInterface(address);
|
|
604
|
+
socket.addMembership(MDNS_ADDRESS, address);
|
|
605
|
+
} catch {}
|
|
606
|
+
else try {
|
|
607
|
+
socket.addMembership(MDNS_ADDRESS);
|
|
608
|
+
} catch {}
|
|
609
|
+
sockets.push(socket);
|
|
610
|
+
resolveSocket(socket);
|
|
611
|
+
});
|
|
612
|
+
});
|
|
613
|
+
};
|
|
614
|
+
const getPrivateAddresses = () => {
|
|
615
|
+
try {
|
|
616
|
+
const { networkInterfaces } = __require("node:os");
|
|
617
|
+
const interfaces = networkInterfaces();
|
|
618
|
+
const addresses = [];
|
|
619
|
+
for (const nets of Object.values(interfaces)) for (const net of nets) if (net.family === "IPv4" && net.internal === false) addresses.push(net.address);
|
|
620
|
+
return addresses;
|
|
621
|
+
} catch {
|
|
622
|
+
return [];
|
|
623
|
+
}
|
|
624
|
+
};
|
|
625
|
+
const setup = async () => {
|
|
626
|
+
await addSocket(null, MDNS_PORT);
|
|
627
|
+
for (const address of getPrivateAddresses()) await addSocket(address, 0);
|
|
628
|
+
if (sockets.length === 0) {
|
|
629
|
+
resolve([]);
|
|
630
|
+
return;
|
|
631
|
+
}
|
|
632
|
+
const sendQueries = () => {
|
|
633
|
+
for (const socket of sockets) for (const query of queries) try {
|
|
634
|
+
socket.send(query, MDNS_PORT, MDNS_ADDRESS);
|
|
635
|
+
} catch {}
|
|
636
|
+
};
|
|
637
|
+
sendQueries();
|
|
638
|
+
interval = setInterval(sendQueries, 1e3);
|
|
639
|
+
setTimeout(finish, timeout * 1e3);
|
|
640
|
+
};
|
|
641
|
+
setup();
|
|
642
|
+
});
|
|
643
|
+
};
|
|
644
|
+
|
|
80
645
|
//#endregion
|
|
81
646
|
//#region src/discovery.ts
|
|
647
|
+
const CACHE_TTL = 3e4;
|
|
648
|
+
const WAKE_PORTS = [
|
|
649
|
+
7e3,
|
|
650
|
+
3689,
|
|
651
|
+
49152,
|
|
652
|
+
32498
|
|
653
|
+
];
|
|
654
|
+
const toDiscoveryResult = (service) => {
|
|
655
|
+
const txt = service.properties;
|
|
656
|
+
const featuresStr = txt.features ?? txt.ft;
|
|
657
|
+
const model = txt.model ?? txt.am ?? "";
|
|
658
|
+
const protocol = service.type.includes("._tcp") ? "tcp" : "udp";
|
|
659
|
+
const hostname = service.name.replace(/\s+/g, "-");
|
|
660
|
+
return {
|
|
661
|
+
id: `${hostname}.local`,
|
|
662
|
+
fqdn: `${hostname}.local`,
|
|
663
|
+
address: service.address,
|
|
664
|
+
modelName: model,
|
|
665
|
+
familyName: null,
|
|
666
|
+
txt,
|
|
667
|
+
features: featuresStr ? tryParseFeatures(featuresStr) : void 0,
|
|
668
|
+
service: {
|
|
669
|
+
port: service.port,
|
|
670
|
+
protocol,
|
|
671
|
+
type: service.type
|
|
672
|
+
},
|
|
673
|
+
packet: null
|
|
674
|
+
};
|
|
675
|
+
};
|
|
676
|
+
const tryParseFeatures = (features) => {
|
|
677
|
+
try {
|
|
678
|
+
return parseFeatures(features);
|
|
679
|
+
} catch {
|
|
680
|
+
return;
|
|
681
|
+
}
|
|
682
|
+
};
|
|
82
683
|
var Discovery = class Discovery {
|
|
684
|
+
static #cache = /* @__PURE__ */ new Map();
|
|
83
685
|
#service;
|
|
84
686
|
constructor(service) {
|
|
85
687
|
this.#service = service;
|
|
86
688
|
}
|
|
87
|
-
async find() {
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
689
|
+
async find(useCache = true) {
|
|
690
|
+
if (useCache) {
|
|
691
|
+
const cached = Discovery.#cache.get(this.#service);
|
|
692
|
+
if (cached && cached.expiresAt > Date.now()) return cached.results;
|
|
693
|
+
}
|
|
694
|
+
const mapped = (await multicast([this.#service], 4)).map(toDiscoveryResult);
|
|
695
|
+
Discovery.#cache.set(this.#service, {
|
|
696
|
+
results: mapped,
|
|
697
|
+
expiresAt: Date.now() + CACHE_TTL
|
|
698
|
+
});
|
|
699
|
+
return mapped;
|
|
93
700
|
}
|
|
94
701
|
async findUntil(id, tries = 10, timeout = 1e3) {
|
|
95
702
|
while (tries > 0) {
|
|
96
|
-
const devices = await this.find();
|
|
703
|
+
const devices = await this.find(false);
|
|
97
704
|
const device = devices.find((device) => device.id === id);
|
|
98
705
|
if (device) return device;
|
|
99
706
|
console.log();
|
|
@@ -102,7 +709,57 @@ var Discovery = class Discovery {
|
|
|
102
709
|
tries--;
|
|
103
710
|
await waitFor(timeout);
|
|
104
711
|
}
|
|
105
|
-
throw new Error("Device not found after
|
|
712
|
+
throw new Error("Device not found after several tries, aborting.");
|
|
713
|
+
}
|
|
714
|
+
static clearCache() {
|
|
715
|
+
Discovery.#cache.clear();
|
|
716
|
+
}
|
|
717
|
+
static async wake(address) {
|
|
718
|
+
const promises = WAKE_PORTS.map((port) => new Promise((resolve) => {
|
|
719
|
+
const socket = createConnection({
|
|
720
|
+
host: address,
|
|
721
|
+
port,
|
|
722
|
+
timeout: 500
|
|
723
|
+
});
|
|
724
|
+
socket.on("connect", () => {
|
|
725
|
+
socket.destroy();
|
|
726
|
+
resolve();
|
|
727
|
+
});
|
|
728
|
+
socket.on("error", () => {
|
|
729
|
+
socket.destroy();
|
|
730
|
+
resolve();
|
|
731
|
+
});
|
|
732
|
+
socket.on("timeout", () => {
|
|
733
|
+
socket.destroy();
|
|
734
|
+
resolve();
|
|
735
|
+
});
|
|
736
|
+
}));
|
|
737
|
+
await Promise.all(promises);
|
|
738
|
+
}
|
|
739
|
+
static async discoverAll() {
|
|
740
|
+
const allServices = await multicast([
|
|
741
|
+
AIRPLAY_SERVICE,
|
|
742
|
+
COMPANION_LINK_SERVICE,
|
|
743
|
+
RAOP_SERVICE
|
|
744
|
+
], 4);
|
|
745
|
+
const devices = /* @__PURE__ */ new Map();
|
|
746
|
+
for (const service of allServices) {
|
|
747
|
+
const result = toDiscoveryResult(service);
|
|
748
|
+
const existing = devices.get(result.id);
|
|
749
|
+
if (existing) {
|
|
750
|
+
if (service.type === AIRPLAY_SERVICE) existing.airplay = result;
|
|
751
|
+
else if (service.type === COMPANION_LINK_SERVICE) existing.companionLink = result;
|
|
752
|
+
else if (service.type === RAOP_SERVICE) existing.raop = result;
|
|
753
|
+
} else devices.set(result.id, {
|
|
754
|
+
id: result.id,
|
|
755
|
+
name: result.fqdn,
|
|
756
|
+
address: result.address,
|
|
757
|
+
airplay: service.type === AIRPLAY_SERVICE ? result : void 0,
|
|
758
|
+
companionLink: service.type === COMPANION_LINK_SERVICE ? result : void 0,
|
|
759
|
+
raop: service.type === RAOP_SERVICE ? result : void 0
|
|
760
|
+
});
|
|
761
|
+
}
|
|
762
|
+
return [...devices.values()];
|
|
106
763
|
}
|
|
107
764
|
static airplay() {
|
|
108
765
|
return new Discovery(AIRPLAY_SERVICE);
|
|
@@ -114,29 +771,6 @@ var Discovery = class Discovery {
|
|
|
114
771
|
return new Discovery(RAOP_SERVICE);
|
|
115
772
|
}
|
|
116
773
|
};
|
|
117
|
-
function generateId(result) {
|
|
118
|
-
if (!result?.packet) return null;
|
|
119
|
-
const { answers = [], additionals = [] } = result.packet;
|
|
120
|
-
const allRecords = [...answers, ...additionals];
|
|
121
|
-
const srvRecord = allRecords.find((record) => record.type === "SRV");
|
|
122
|
-
if (srvRecord?.rdata?.target) return srvRecord.rdata.target;
|
|
123
|
-
if (result.address) {
|
|
124
|
-
const addressRecord = allRecords.find((record) => (record.type === "A" || record.type === "AAAA") && record.rdata === result.address);
|
|
125
|
-
if (addressRecord?.name) return addressRecord.name;
|
|
126
|
-
}
|
|
127
|
-
const aRecord = allRecords.find((record) => record.type === "A");
|
|
128
|
-
if (aRecord?.name) return aRecord.name;
|
|
129
|
-
if (result.modelName) return `${result.modelName.replace(/\s+/g, "-").replace(/[^a-zA-Z0-9-]/g, "")}.local`;
|
|
130
|
-
return null;
|
|
131
|
-
}
|
|
132
|
-
function getTxt(result) {
|
|
133
|
-
if (!result.packet) return {};
|
|
134
|
-
const { answers = [], additionals = [] } = result.packet;
|
|
135
|
-
const records = [...answers, ...additionals];
|
|
136
|
-
const txt = {};
|
|
137
|
-
for (const record of records) if (record.type === "TXT" && record.rdata) Object.assign(txt, record.rdata);
|
|
138
|
-
return txt;
|
|
139
|
-
}
|
|
140
774
|
|
|
141
775
|
//#endregion
|
|
142
776
|
//#region src/symbols.ts
|
|
@@ -841,5 +1475,4 @@ function splitUInt53(number) {
|
|
|
841
1475
|
}
|
|
842
1476
|
|
|
843
1477
|
//#endregion
|
|
844
|
-
export { AIRPLAY_SERVICE, AIRPLAY_TRANSIENT_PIN, AccessoryPair, AccessoryVerify, COMPANION_LINK_SERVICE, Connection, Context, Discovery, ENCRYPTION, EncryptionAwareConnection, EncryptionState, HTTP_TIMEOUT, RAOP_SERVICE, TimingServer, generateActiveRemoteId, generateDacpId, generateSessionId, getLocalIP, getMacAddress, prompt, randomInt32, randomInt64, reporter, uint16ToBE, uint53ToLE, v4 as uuid, waitFor };
|
|
845
|
-
//# sourceMappingURL=index.mjs.map
|
|
1478
|
+
export { AIRPLAY_SERVICE, AIRPLAY_TRANSIENT_PIN, AccessoryPair, AccessoryVerify, AirPlayFeatureFlags, COMPANION_LINK_SERVICE, Connection, Context, Discovery, ENCRYPTION, EncryptionAwareConnection, EncryptionState, HTTP_TIMEOUT, JsonStorage, MemoryStorage, RAOP_SERVICE, Storage, TimingServer, describeFlags, generateActiveRemoteId, generateDacpId, generateSessionId, getLocalIP, getMacAddress, getPairingRequirement, getProtocolVersion, hasFeatureFlag, isPasswordRequired, isRemoteControlSupported, multicast as mdnsMulticast, unicast as mdnsUnicast, parseFeatures, prompt, randomInt32, randomInt64, reporter, uint16ToBE, uint53ToLE, v4 as uuid, waitFor };
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@basmilius/apple-common",
|
|
3
3
|
"description": "Common features shared across various apple protocol packages.",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.9.3",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"author": {
|
|
@@ -9,6 +9,11 @@
|
|
|
9
9
|
"email": "bas@mili.us",
|
|
10
10
|
"url": "https://bas.dev"
|
|
11
11
|
},
|
|
12
|
+
"repository": {
|
|
13
|
+
"type": "git",
|
|
14
|
+
"url": "https://github.com/basmilius/apple-protocols",
|
|
15
|
+
"directory": "packages/common"
|
|
16
|
+
},
|
|
12
17
|
"keywords": [
|
|
13
18
|
"apple",
|
|
14
19
|
"airplay",
|
|
@@ -40,10 +45,9 @@
|
|
|
40
45
|
}
|
|
41
46
|
},
|
|
42
47
|
"dependencies": {
|
|
43
|
-
"@basmilius/apple-encoding": "0.
|
|
44
|
-
"@basmilius/apple-encryption": "0.
|
|
45
|
-
"fast-srp-hap": "^2.0.4"
|
|
46
|
-
"node-dns-sd": "^1.0.1"
|
|
48
|
+
"@basmilius/apple-encoding": "0.9.3",
|
|
49
|
+
"@basmilius/apple-encryption": "0.9.3",
|
|
50
|
+
"fast-srp-hap": "^2.0.4"
|
|
47
51
|
},
|
|
48
52
|
"devDependencies": {
|
|
49
53
|
"@types/bun": "^1.3.9",
|
package/dist/index.d.mts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.mts","names":["UUIDTypes","TBuf","Uint8Array","Version1Options","node","clockseq","random","rng","msecs","nsecs","_v6","Version4Options","Version6Options","Version7Options","seq","Version4Options","v4","options","buf","offset","TBuf","Uint8Array","default","DiscoveryResult","Discovery","Promise","private","constructor","service","find","findUntil","id","tries","timeout","airplay","companionLink","raop","prompt","Promise","message","waitFor","ms","DebugGroup","Logger","private","id","label","constructor","debug","data","error","info","net","raw","warn","Reporter","all","disable","group","enable","isEnabled","reporter","Logger","Context","private","deviceId","logger","constructor","ENCRYPTION","ConnectionState","EventMap","Record","DnsRecordClass","DnsRecordType","DnsRecordBase","name","type","class","flash","ttl","DnsRecordA","rdata","DnsRecordAAAA","DnsRecordPTR","DnsRecordTXT","Buffer","rdata_buffer","DnsRecordSRV","priority","weight","port","target","DnsRecordNSEC","DnsRecord","DnsPacketHeader","id","qr","op","aa","tc","rd","ra","z","ad","cd","rc","questions","answers","authorities","additionals","DnsPacket","address","header","Result","fqdn","modelName","familyName","service","protocol","packet","key","DiscoveryResult","txt","EventEmitter","Context","ENCRYPTION","ConnectionState","EventMap","ConnectionEventMap","Buffer","Error","close","hadError","connect","data","end","error","err","timeout","Connection","TEventMap","Promise","Uint8Array","private","address","context","port","isConnected","state","constructor","destroy","disconnect","debug","enabled","retry","attempts","interval","write","EncryptionAwareConnection","EncryptionState","isEncrypted","enableEncryption","readKey","writeKey","readCount","writeCount","AIRPLAY_TRANSIENT_PIN","HTTP_TIMEOUT","SOCKET_TIMEOUT","AIRPLAY_SERVICE","COMPANION_LINK_SERVICE","RAOP_SERVICE","Context","BasePairing","Buffer","Map","private","context","constructor","tlv","buffer","AccessoryPair","RequestHandler","Promise","AccessoryCredentials","AccessoryKeys","PairM1","PairM2","PairM3","PairM4","PairM5","requestHandler","start","pin","askPin","transient","m1","additionalTlv","m2","m3","m4","m5","m6","AccessoryVerify","credentials","step","data","publicKey","salt","proof","serverProof","sharedSecret","authTag","sessionKey","accessoryIdentifier","accessoryLongTermPublicKey","pairingId","secretKey","accessoryToControllerKey","controllerToAccessoryKey","TimingServer","Promise","private","port","constructor","close","listen","generateActiveRemoteId","generateDacpId","generateSessionId","getLocalIP","getMacAddress","randomInt32","randomInt64","uint16ToBE","Buffer","value","uint53ToLE","AudioSource","Buffer","Promise","duration","readFrames","count","reset","start","stop"],"sources":["../../../node_modules/.bun/uuid@13.0.0/node_modules/uuid/dist/types.d.ts","../../../node_modules/.bun/uuid@13.0.0/node_modules/uuid/dist/v4.d.ts","../src/discovery.d.ts","../src/cli.d.ts","../src/reporter.d.ts","../src/context.d.ts","../src/symbols.d.ts","../src/types.d.ts","../src/connection.d.ts","../src/const.d.ts","../src/pairing.d.ts","../src/timing.d.ts","../src/utils.d.ts","../src/audioSource.d.ts"],"x_google_ignoreList":[0,1],"mappings":"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"}
|
package/dist/index.mjs.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":["native","#service","#address","#context","#port","#state","#socket","#retryEnabled","#retryAttempt","#attemptConnect","#retryTimeout","#cleanup","#debug","#retryAttempts","#retryInterval","#connectPromise","#onClose","#onConnect","#onData","#onEnd","#onError","#onTimeout","#scheduleRetry","#id","#label","#enabled","#deviceId","#logger","#context","#name","#pairingId","#requestHandler","uuid","#publicKey","#secretKey","#srp","#ephemeralKeyPair","#m1","#m2","#m3","#m4","#port","#logger","#socket","#onConnect","#onError","#onMessage","#onListening"],"sources":["../../../node_modules/.bun/uuid@13.0.0/node_modules/uuid/dist-node/stringify.js","../../../node_modules/.bun/uuid@13.0.0/node_modules/uuid/dist-node/rng.js","../../../node_modules/.bun/uuid@13.0.0/node_modules/uuid/dist-node/native.js","../../../node_modules/.bun/uuid@13.0.0/node_modules/uuid/dist-node/v4.js","../src/cli.ts","../src/const.ts","../src/discovery.ts","../src/symbols.ts","../src/connection.ts","../src/reporter.ts","../src/context.ts","../src/pairing.ts","../src/timing.ts","../src/utils.ts"],"sourcesContent":["import validate from './validate.js';\nconst byteToHex = [];\nfor (let i = 0; i < 256; ++i) {\n byteToHex.push((i + 0x100).toString(16).slice(1));\n}\nexport function unsafeStringify(arr, offset = 0) {\n return (byteToHex[arr[offset + 0]] +\n byteToHex[arr[offset + 1]] +\n byteToHex[arr[offset + 2]] +\n byteToHex[arr[offset + 3]] +\n '-' +\n byteToHex[arr[offset + 4]] +\n byteToHex[arr[offset + 5]] +\n '-' +\n byteToHex[arr[offset + 6]] +\n byteToHex[arr[offset + 7]] +\n '-' +\n byteToHex[arr[offset + 8]] +\n byteToHex[arr[offset + 9]] +\n '-' +\n byteToHex[arr[offset + 10]] +\n byteToHex[arr[offset + 11]] +\n byteToHex[arr[offset + 12]] +\n byteToHex[arr[offset + 13]] +\n byteToHex[arr[offset + 14]] +\n byteToHex[arr[offset + 15]]).toLowerCase();\n}\nfunction stringify(arr, offset = 0) {\n const uuid = unsafeStringify(arr, offset);\n if (!validate(uuid)) {\n throw TypeError('Stringified UUID is invalid');\n }\n return uuid;\n}\nexport default stringify;\n","import { randomFillSync } from 'node:crypto';\nconst rnds8Pool = new Uint8Array(256);\nlet poolPtr = rnds8Pool.length;\nexport default function rng() {\n if (poolPtr > rnds8Pool.length - 16) {\n randomFillSync(rnds8Pool);\n poolPtr = 0;\n }\n return rnds8Pool.slice(poolPtr, (poolPtr += 16));\n}\n","import { randomUUID } from 'node:crypto';\nexport default { randomUUID };\n","import native from './native.js';\nimport rng from './rng.js';\nimport { unsafeStringify } from './stringify.js';\nfunction _v4(options, buf, offset) {\n options = options || {};\n const rnds = options.random ?? options.rng?.() ?? rng();\n if (rnds.length < 16) {\n throw new Error('Random bytes length must be >= 16');\n }\n rnds[6] = (rnds[6] & 0x0f) | 0x40;\n rnds[8] = (rnds[8] & 0x3f) | 0x80;\n if (buf) {\n offset = offset || 0;\n if (offset < 0 || offset + 16 > buf.length) {\n throw new RangeError(`UUID byte range ${offset}:${offset + 15} is out of buffer bounds`);\n }\n for (let i = 0; i < 16; ++i) {\n buf[offset + i] = rnds[i];\n }\n return buf;\n }\n return unsafeStringify(rnds);\n}\nfunction v4(options, buf, offset) {\n if (native.randomUUID && !buf && !options) {\n return native.randomUUID();\n }\n return _v4(options, buf, offset);\n}\nexport default v4;\n","import { createInterface } from 'node:readline';\n\nexport async function prompt(message: string): Promise<string> {\n const cli = createInterface({\n input: process.stdin,\n output: process.stdout\n });\n\n const answer = await new Promise<string>(resolve => cli.question(`${message}: `, resolve));\n\n cli.close();\n\n return answer;\n}\n\nexport async function waitFor(ms: number): Promise<void> {\n return new Promise(resolve => setTimeout(resolve, ms));\n}\n","export const AIRPLAY_TRANSIENT_PIN = '3939';\n\nexport const HTTP_TIMEOUT = 6000;\nexport const SOCKET_TIMEOUT = 10000;\n\nexport const AIRPLAY_SERVICE = '_airplay._tcp.local';\nexport const COMPANION_LINK_SERVICE = '_companion-link._tcp.local';\nexport const RAOP_SERVICE = '_raop._tcp.local';\n","import mdns, { DiscoveryResult, DnsRecord, Result } from 'node-dns-sd';\nimport { waitFor } from './cli';\nimport { AIRPLAY_SERVICE, COMPANION_LINK_SERVICE, RAOP_SERVICE } from './const';\n\nexport class Discovery {\n readonly #service: string;\n\n constructor(service: string) {\n this.#service = service;\n }\n\n async find(): Promise<DiscoveryResult[]> {\n const results = await mdns.discover({\n name: this.#service\n });\n\n return results.map(result => ({\n id: generateId(result) ?? result.fqdn,\n txt: getTxt(result),\n ...result\n }));\n }\n\n async findUntil(id: string, tries: number = 10, timeout: number = 1000): Promise<DiscoveryResult> {\n while (tries > 0) {\n const devices = await this.find();\n const device = devices.find(device => device.id === id);\n\n if (device) {\n return device;\n }\n\n console.log();\n console.log(`Device not found, retrying in ${timeout}ms...`);\n console.log(devices.map(d => ` ● ${d.id} (${d.fqdn})`).join('\\n'));\n\n tries--;\n\n await waitFor(timeout);\n }\n\n throw new Error('Device not found after serveral tries, aborting.');\n }\n\n static airplay(): Discovery {\n return new Discovery(AIRPLAY_SERVICE);\n }\n\n static companionLink(): Discovery {\n return new Discovery(COMPANION_LINK_SERVICE);\n }\n\n static raop(): Discovery {\n return new Discovery(RAOP_SERVICE);\n }\n}\n\nfunction generateId(result: Result): string | null {\n if (!result?.packet) {\n return null;\n }\n\n const {answers = [], additionals = []} = result.packet;\n const allRecords = [...answers, ...additionals];\n\n // Strategy 1: Find SRV record and get the target (most reliable)\n const srvRecord = allRecords.find((record) => record.type === 'SRV');\n if (srvRecord?.rdata?.target) {\n return srvRecord.rdata.target;\n }\n\n // Strategy 2: Find A or AAAA record name that matches the IP address\n // (the record name is the hostname)\n if (result.address) {\n const addressRecord = allRecords.find(record => (record.type === 'A' || record.type === 'AAAA') && record.rdata === result.address);\n\n if (addressRecord?.name) {\n return addressRecord.name;\n }\n }\n\n // Strategy 3: Find any A record and use its name as hostname\n const aRecord = allRecords.find((record) => record.type === 'A');\n if (aRecord?.name) {\n return aRecord.name;\n }\n\n // Strategy 4: Fallback - derive from fqdn/modelName (less reliable)\n if (result.modelName) {\n const hostname = result.modelName\n .replace(/\\s+/g, '-')\n .replace(/[^a-zA-Z0-9-]/g, '');\n\n return `${hostname}.local`;\n }\n\n return null;\n}\n\nfunction getTxt(result: Result): Record<string, string> {\n if (!result.packet) {\n return {};\n }\n\n const {answers = [], additionals = []} = result.packet;\n const records: DnsRecord[] = [\n ...answers,\n ...additionals\n ];\n\n const txt: Record<string, string> = {};\n\n for (const record of records) {\n if (record.type === 'TXT' && record.rdata) {\n Object.assign(txt, record.rdata);\n }\n }\n\n return txt;\n}\n","export const ENCRYPTION: unique symbol = Symbol();\n","import { EventEmitter } from 'node:events';\nimport { Socket } from 'node:net';\nimport { SOCKET_TIMEOUT } from './const';\nimport type { Context } from './context';\nimport { ENCRYPTION } from './symbols';\nimport type { ConnectionState, EventMap } from './types';\n\nconst NOOP_PROMISE_HANDLER = {\n resolve: () => {\n },\n reject: (_: Error) => {\n }\n} as const;\n\ntype ConnectionEventMap = {\n close: [hadError: boolean];\n connect: [];\n data: [data: Buffer];\n end: [];\n error: [err: Error];\n timeout: [];\n};\n\nexport class Connection<TEventMap extends EventMap> extends EventEmitter<ConnectionEventMap | TEventMap> {\n get address(): string {\n return this.#address;\n }\n\n get context(): Context {\n return this.#context;\n }\n\n get port(): number {\n return this.#port;\n }\n\n get isConnected(): boolean {\n return this.#state === 'connected';\n }\n\n get state(): ConnectionState {\n if (this.#state === 'closing' || this.#state === 'failed') {\n return this.#state;\n }\n\n if (!this.#socket) {\n return 'disconnected';\n }\n\n switch (this.#socket.readyState) {\n case 'opening':\n return 'connecting';\n\n case 'open':\n return 'connected';\n\n default:\n return this.#state;\n }\n }\n\n readonly #address: string;\n readonly #port: number;\n readonly #context: Context;\n #debug: boolean = false;\n #retryAttempt: number = 0;\n #retryAttempts: number = 3;\n #retryEnabled: boolean = true;\n #retryInterval: number = 3000;\n #retryTimeout?: NodeJS.Timeout;\n #socket?: Socket;\n #state: ConnectionState;\n\n #connectPromise?: {\n resolve: () => void;\n reject: (err: Error) => void;\n };\n\n constructor(context: Context, address: string, port: number) {\n super();\n\n this.#address = address;\n this.#port = port;\n this.#context = context;\n\n this.#state = 'disconnected';\n }\n\n async connect(): Promise<void> {\n if (this.#state === 'connected') {\n return;\n }\n\n if (this.#state === 'connecting') {\n throw new Error('A connection is already being established.');\n }\n\n this.#retryEnabled = true;\n this.#retryAttempt = 0;\n\n return this.#attemptConnect();\n }\n\n destroy(): void {\n this.#socket?.destroy();\n }\n\n async disconnect(): Promise<void> {\n if (this.#retryTimeout) {\n clearTimeout(this.#retryTimeout);\n this.#retryTimeout = undefined;\n }\n\n this.#retryEnabled = false;\n\n if (!this.#socket || this.#state === 'disconnected') {\n return;\n }\n\n return new Promise(resolve => {\n this.#state = 'closing';\n this.#socket.once('close', () => {\n this.#cleanup();\n resolve();\n });\n this.#socket.end();\n });\n }\n\n debug(enabled: boolean): this {\n this.#debug = enabled;\n\n return this;\n }\n\n retry(attempts: number, interval: number = 3000): this {\n this.#retryAttempts = attempts;\n this.#retryInterval = interval;\n\n return this;\n }\n\n write(data: Buffer | Uint8Array): void {\n if (!this.#socket || this.state !== 'connected' || !this.#socket.writable) {\n this.emit('error', new Error('Cannot write to a disconnected connection.'));\n return;\n }\n\n this.#socket.write(data, err => {\n if (!err) {\n return;\n }\n\n this.#context.logger.error('Failed to write data to socket.');\n this.emit('error', err);\n });\n }\n\n async #attemptConnect(): Promise<void> {\n return new Promise((resolve, reject) => {\n this.#state = 'connecting';\n this.#connectPromise = {resolve, reject};\n\n this.#socket?.removeAllListeners();\n this.#socket = undefined;\n\n this.#socket = new Socket();\n this.#socket.setNoDelay(true);\n this.#socket.setTimeout(SOCKET_TIMEOUT);\n\n this.#socket.on('close', this.#onClose.bind(this));\n this.#socket.on('connect', this.#onConnect.bind(this));\n this.#socket.on('data', this.#onData.bind(this));\n this.#socket.on('end', this.#onEnd.bind(this));\n this.#socket.on('error', this.#onError.bind(this));\n this.#socket.on('timeout', this.#onTimeout.bind(this));\n\n this.#context.logger.net(`Connecting to ${this.#address}:${this.#port}...`);\n\n this.#socket.connect({\n host: this.#address,\n port: this.#port,\n keepAlive: true\n });\n });\n }\n\n #cleanup(): void {\n if (this.#retryTimeout) {\n clearTimeout(this.#retryTimeout);\n this.#retryTimeout = undefined;\n }\n\n if (this.#socket) {\n this.#socket.removeAllListeners();\n this.#socket.destroy();\n this.#socket = undefined;\n }\n\n this.#state = 'disconnected';\n this.#connectPromise = undefined;\n }\n\n #scheduleRetry(err: Error): void {\n if (!this.#retryEnabled || this.#retryAttempt >= this.#retryAttempts) {\n this.#state = 'failed';\n this.#connectPromise?.reject(err);\n this.#connectPromise = undefined;\n return;\n }\n\n if (this.#retryTimeout) {\n clearTimeout(this.#retryTimeout);\n this.#retryTimeout = undefined;\n }\n\n this.#retryAttempt++;\n this.#context.logger.net(`Retry attempt ${this.#retryAttempt} / ${this.#retryAttempts} in ${this.#retryInterval}ms...`);\n\n const {resolve, reject} = this.#connectPromise ?? NOOP_PROMISE_HANDLER;\n this.#cleanup();\n\n this.#retryTimeout = setTimeout(async () => {\n this.#retryTimeout = undefined;\n\n try {\n // Re-assign the stored handlers\n this.#connectPromise = {resolve, reject};\n await this.#attemptConnect();\n resolve();\n } catch (retryErr) {\n // Error handling is done in #onError/#onTimeout\n }\n }, this.#retryInterval);\n }\n\n #onClose(hadError: boolean): void {\n const wasConnected = this.#state === 'connected';\n\n if (this.#state !== 'closing') {\n this.#state = 'disconnected';\n this.#context.logger.net(`Connection closed (${hadError ? 'with error' : 'normally'}).`);\n }\n\n this.emit('close', hadError);\n\n if (wasConnected && this.#retryEnabled && hadError) {\n this.#scheduleRetry(new Error('Connection closed unexpectedly.'));\n }\n }\n\n #onConnect(): void {\n this.#state = 'connected';\n this.#retryAttempt = 0;\n\n this.#socket.setKeepAlive(true, 10000);\n this.#socket.setTimeout(0);\n\n this.emit('connect');\n this.#connectPromise?.resolve();\n this.#connectPromise = undefined;\n }\n\n #onData(data: Buffer): void {\n if (this.#debug) {\n const cutoff = Math.min(data.byteLength, 64);\n this.#context.logger.debug(`Received ${data.byteLength} bytes of data.`);\n this.#context.logger.debug(`hex=${data.subarray(0, cutoff).toString('hex')}`);\n this.#context.logger.debug(`ascii=${data.toString('ascii').replace(/[^\\x20-\\x7E]/g, '.').substring(0, cutoff)}`);\n }\n\n this.emit('data', data);\n }\n\n #onEnd(): void {\n this.emit('end');\n }\n\n #onError(err: Error): void {\n this.#context.logger.error(`Connection error: ${err.message}`);\n\n if (this.listenerCount('error') > 0) {\n this.emit('error', err);\n } else {\n this.#context.logger.warn('No error handler registered. This is likely a bug.', this.constructor.name, '#onError');\n }\n\n if (this.#state === 'connecting') {\n this.#scheduleRetry(err);\n } else {\n this.#state = 'failed';\n }\n }\n\n #onTimeout(): void {\n this.#context.logger.error('Connection timed out.');\n\n const err = new Error('Connection timed out.');\n\n this.emit('timeout');\n\n if (this.#state === 'connecting') {\n this.#scheduleRetry(err);\n } else {\n this.#state = 'failed';\n this.#socket?.destroy();\n }\n }\n}\n\nexport class EncryptionAwareConnection<TEventMap extends EventMap> extends Connection<TEventMap> {\n get isEncrypted(): boolean {\n return !!this[ENCRYPTION];\n }\n\n [ENCRYPTION]?: EncryptionState;\n\n enableEncryption(readKey: Buffer, writeKey: Buffer): void {\n this[ENCRYPTION] = new EncryptionState(readKey, writeKey);\n }\n}\n\nexport class EncryptionState {\n readKey: Buffer;\n readCount: number;\n writeKey: Buffer;\n writeCount: number;\n\n constructor(readKey: Buffer, writeKey: Buffer) {\n this.readCount = 0;\n this.readKey = readKey;\n this.writeCount = 0;\n this.writeKey = writeKey;\n }\n}\n","type DebugGroup =\n | 'debug'\n | 'error'\n | 'info'\n | 'net'\n | 'raw'\n | 'warn';\n\nexport class Logger {\n get id(): string {\n return this.#id;\n }\n\n get label(): string {\n return this.#label;\n }\n\n readonly #id: string;\n readonly #label: string;\n\n constructor(id: string) {\n this.#id = id;\n this.#label = `\\u001b[36m[${id}]\\u001b[39m`;\n }\n\n debug(...data: any[]): void {\n debug(this.#label, ...data);\n }\n\n error(...data: any[]): void {\n error(this.#label, ...data);\n }\n\n info(...data: any[]): void {\n info(this.#label, ...data);\n }\n\n net(...data: any[]): void {\n net(this.#label, ...data);\n }\n\n raw(...data: any[]): void {\n raw(this.#label, ...data);\n }\n\n warn(...data: any[]): void {\n warn(this.#label, ...data);\n }\n}\n\nexport class Reporter {\n #enabled: DebugGroup[] = [];\n\n all(): void {\n this.#enabled = ['debug', 'error', 'info', 'net', 'raw', 'warn'] as DebugGroup[];\n }\n\n disable(group: DebugGroup): void {\n if (this.#enabled.includes(group)) {\n this.#enabled.splice(this.#enabled.indexOf(group), 1);\n }\n }\n\n enable(group: DebugGroup): void {\n if (!this.#enabled.includes(group)) {\n this.#enabled.push(group);\n }\n }\n\n isEnabled(group: DebugGroup): boolean {\n return this.#enabled.includes(group);\n }\n}\n\nfunction debug(...data: any[]): void {\n reporter.isEnabled('debug') && console.debug(`\\u001b[36m[debug]\\u001b[39m`, ...data);\n}\n\nfunction error(...data: any[]): void {\n reporter.isEnabled('error') && console.error(`\\u001b[31m[error]\\u001b[39m`, ...data);\n}\n\nfunction info(...data: any[]): void {\n reporter.isEnabled('info') && console.info(`\\u001b[32m[info]\\u001b[39m`, ...data);\n}\n\nfunction net(...data: any[]): void {\n reporter.isEnabled('net') && console.info(`\\u001b[33m[net]\\u001b[39m`, ...data);\n}\n\nfunction raw(...data: any[]): void {\n reporter.isEnabled('raw') && console.log(`\\u001b[34m[raw]\\u001b[39m`, ...data);\n}\n\nfunction warn(...data: any[]): void {\n reporter.isEnabled('warn') && console.warn(`\\u001b[33m[warn]\\u001b[39m`, ...data);\n}\n\nexport const reporter: Reporter = new Reporter();\n","import { Logger } from './reporter';\n\nexport class Context {\n get deviceId(): string {\n return this.#deviceId;\n }\n\n get logger(): Logger {\n return this.#logger;\n }\n\n readonly #deviceId: string;\n readonly #logger: Logger;\n\n constructor(deviceId: string) {\n this.#deviceId = deviceId;\n this.#logger = new Logger(deviceId);\n }\n}\n","import { OPack, TLV8 } from '@basmilius/apple-encoding';\nimport { Chacha20, Curve25519, Ed25519, hkdf, type KeyPair } from '@basmilius/apple-encryption';\nimport { SRP, SrpClient } from 'fast-srp-hap';\nimport { v4 as uuid } from 'uuid';\nimport { AIRPLAY_TRANSIENT_PIN } from './const';\nimport type { Context } from './context';\n\nabstract class BasePairing {\n get context(): Context {\n return this.#context;\n }\n\n readonly #context: Context;\n\n constructor(context: Context) {\n this.#context = context;\n }\n\n tlv(buffer: Buffer): Map<number, Buffer> {\n const data = TLV8.decode(buffer);\n\n if (data.has(TLV8.Value.Error)) {\n TLV8.bail(data);\n }\n\n this.#context.logger.raw('Decoded TLV', data);\n\n return data;\n }\n}\n\nexport class AccessoryPair extends BasePairing {\n readonly #name: string;\n readonly #pairingId: Buffer;\n readonly #requestHandler: RequestHandler;\n #publicKey: Buffer;\n #secretKey: Buffer;\n #srp: SrpClient;\n\n constructor(context: Context, requestHandler: RequestHandler) {\n super(context);\n\n this.#name = 'basmilius/apple-protocols';\n this.#pairingId = Buffer.from(uuid().toUpperCase());\n this.#requestHandler = requestHandler;\n }\n\n async start(): Promise<void> {\n const keyPair = Ed25519.generateKeyPair();\n this.#publicKey = Buffer.from(keyPair.publicKey);\n this.#secretKey = Buffer.from(keyPair.secretKey);\n }\n\n async pin(askPin: () => Promise<string>): Promise<AccessoryCredentials> {\n const m1 = await this.m1();\n const m2 = await this.m2(m1, await askPin());\n const m3 = await this.m3(m2);\n const m4 = await this.m4(m3);\n const m5 = await this.m5(m4);\n const m6 = await this.m6(m4, m5);\n\n if (!m6) {\n throw new Error('Pairing failed, could not get accessory keys.');\n }\n\n return m6;\n }\n\n async transient(): Promise<AccessoryKeys> {\n const m1 = await this.m1([[TLV8.Value.Flags, TLV8.Flags.TransientPairing]]);\n const m2 = await this.m2(m1);\n const m3 = await this.m3(m2);\n const m4 = await this.m4(m3);\n\n const accessoryToControllerKey = hkdf({\n hash: 'sha512',\n key: m4.sharedSecret,\n length: 32,\n salt: Buffer.from('Control-Salt'),\n info: Buffer.from('Control-Read-Encryption-Key')\n });\n\n const controllerToAccessoryKey = hkdf({\n hash: 'sha512',\n key: m4.sharedSecret,\n length: 32,\n salt: Buffer.from('Control-Salt'),\n info: Buffer.from('Control-Write-Encryption-Key')\n });\n\n return {\n pairingId: this.#pairingId,\n sharedSecret: m4.sharedSecret,\n accessoryToControllerKey,\n controllerToAccessoryKey\n };\n }\n\n async m1(additionalTlv: [number, number | Buffer][] = []): Promise<PairM1> {\n const response = await this.#requestHandler('m1', TLV8.encode([\n [TLV8.Value.Method, TLV8.Method.PairSetup],\n [TLV8.Value.State, TLV8.State.M1],\n ...additionalTlv\n ]));\n\n const data = this.tlv(response);\n const publicKey = data.get(TLV8.Value.PublicKey);\n const salt = data.get(TLV8.Value.Salt);\n\n return {publicKey, salt};\n }\n\n async m2(m1: PairM1, pin: string = AIRPLAY_TRANSIENT_PIN): Promise<PairM2> {\n const srpKey = await SRP.genKey(32);\n\n this.#srp = new SrpClient(SRP.params.hap, m1.salt, Buffer.from('Pair-Setup'), Buffer.from(pin), srpKey, true);\n this.#srp.setB(m1.publicKey);\n\n const publicKey = this.#srp.computeA();\n const proof = this.#srp.computeM1();\n\n return {publicKey, proof};\n }\n\n async m3(m2: PairM2): Promise<PairM3> {\n const response = await this.#requestHandler('m3', TLV8.encode([\n [TLV8.Value.State, TLV8.State.M3],\n [TLV8.Value.PublicKey, m2.publicKey],\n [TLV8.Value.Proof, m2.proof]\n ]));\n\n const data = this.tlv(response);\n const serverProof = data.get(TLV8.Value.Proof);\n\n return {serverProof};\n }\n\n async m4(m3: PairM3): Promise<PairM4> {\n this.#srp.checkM2(m3.serverProof);\n\n const sharedSecret = this.#srp.computeK();\n\n return {sharedSecret};\n }\n\n async m5(m4: PairM4): Promise<PairM5> {\n const iosDeviceX = hkdf({\n hash: 'sha512',\n key: m4.sharedSecret,\n length: 32,\n salt: Buffer.from('Pair-Setup-Controller-Sign-Salt', 'utf8'),\n info: Buffer.from('Pair-Setup-Controller-Sign-Info', 'utf8')\n });\n\n const sessionKey = hkdf({\n hash: 'sha512',\n key: m4.sharedSecret,\n length: 32,\n salt: Buffer.from('Pair-Setup-Encrypt-Salt', 'utf8'),\n info: Buffer.from('Pair-Setup-Encrypt-Info', 'utf8')\n });\n\n const deviceInfo = Buffer.concat([\n iosDeviceX,\n this.#pairingId,\n this.#publicKey\n ]);\n\n const signature = Ed25519.sign(deviceInfo, this.#secretKey);\n\n const innerTlv = TLV8.encode([\n [TLV8.Value.Identifier, this.#pairingId],\n [TLV8.Value.PublicKey, this.#publicKey],\n [TLV8.Value.Signature, Buffer.from(signature)],\n [TLV8.Value.Name, OPack.encode({\n name: this.#name\n })]\n ]);\n\n const {authTag, ciphertext} = Chacha20.encrypt(sessionKey, Buffer.from('PS-Msg05'), null, innerTlv);\n const encrypted = Buffer.concat([ciphertext, authTag]);\n\n const response = await this.#requestHandler('m5', TLV8.encode([\n [TLV8.Value.State, TLV8.State.M5],\n [TLV8.Value.EncryptedData, encrypted]\n ]));\n\n const data = this.tlv(response);\n const encryptedDataRaw = data.get(TLV8.Value.EncryptedData);\n const encryptedData = encryptedDataRaw.subarray(0, -16);\n const encryptedTag = encryptedDataRaw.subarray(-16);\n\n return {\n authTag: encryptedTag,\n data: encryptedData,\n sessionKey\n };\n }\n\n async m6(m4: PairM4, m5: PairM5): Promise<AccessoryCredentials> {\n const data = Chacha20.decrypt(m5.sessionKey, Buffer.from('PS-Msg06'), null, m5.data, m5.authTag);\n const tlv = TLV8.decode(data);\n\n const accessoryIdentifier = tlv.get(TLV8.Value.Identifier);\n const accessoryLongTermPublicKey = tlv.get(TLV8.Value.PublicKey);\n const accessorySignature = tlv.get(TLV8.Value.Signature);\n\n const accessoryX = hkdf({\n hash: 'sha512',\n key: m4.sharedSecret,\n length: 32,\n salt: Buffer.from('Pair-Setup-Accessory-Sign-Salt'),\n info: Buffer.from('Pair-Setup-Accessory-Sign-Info')\n });\n\n const accessoryInfo = Buffer.concat([\n accessoryX,\n accessoryIdentifier,\n accessoryLongTermPublicKey\n ]);\n\n if (!Ed25519.verify(accessoryInfo, accessorySignature, accessoryLongTermPublicKey)) {\n throw new Error('Invalid accessory signature.');\n }\n\n return {\n accessoryIdentifier: accessoryIdentifier.toString(),\n accessoryLongTermPublicKey: accessoryLongTermPublicKey,\n pairingId: this.#pairingId,\n publicKey: this.#publicKey,\n secretKey: this.#secretKey\n };\n }\n}\n\nexport class AccessoryVerify extends BasePairing {\n readonly #ephemeralKeyPair: KeyPair;\n readonly #requestHandler: RequestHandler;\n\n constructor(context: Context, requestHandler: RequestHandler) {\n super(context);\n\n this.#ephemeralKeyPair = Curve25519.generateKeyPair();\n this.#requestHandler = requestHandler;\n }\n\n async start(credentials: AccessoryCredentials): Promise<AccessoryKeys> {\n const m1 = await this.#m1();\n const m2 = await this.#m2(credentials.accessoryIdentifier, credentials.accessoryLongTermPublicKey, m1);\n\n await this.#m3(credentials.pairingId, credentials.secretKey, m2);\n\n return await this.#m4(m2, credentials.pairingId);\n }\n\n async #m1(): Promise<VerifyM1> {\n const response = await this.#requestHandler('m1', TLV8.encode([\n [TLV8.Value.State, TLV8.State.M1],\n [TLV8.Value.PublicKey, Buffer.from(this.#ephemeralKeyPair.publicKey)]\n ]));\n\n const data = this.tlv(response);\n const serverPublicKey = data.get(TLV8.Value.PublicKey);\n const encryptedData = data.get(TLV8.Value.EncryptedData);\n\n return {\n encryptedData,\n serverPublicKey\n };\n }\n\n async #m2(localAccessoryIdentifier: string, longTermPublicKey: Buffer, m1: VerifyM1): Promise<VerifyM2> {\n const sharedSecret = Buffer.from(Curve25519.generateSharedSecKey(\n this.#ephemeralKeyPair.secretKey,\n m1.serverPublicKey\n ));\n\n const sessionKey = hkdf({\n hash: 'sha512',\n key: sharedSecret,\n length: 32,\n salt: Buffer.from('Pair-Verify-Encrypt-Salt'),\n info: Buffer.from('Pair-Verify-Encrypt-Info')\n });\n\n const encryptedData = m1.encryptedData.subarray(0, -16);\n const encryptedTag = m1.encryptedData.subarray(-16);\n\n const data = Chacha20.decrypt(sessionKey, Buffer.from('PV-Msg02'), null, encryptedData, encryptedTag);\n const tlv = TLV8.decode(data);\n\n const accessoryIdentifier = tlv.get(TLV8.Value.Identifier);\n const accessorySignature = tlv.get(TLV8.Value.Signature);\n\n if (accessoryIdentifier.toString() !== localAccessoryIdentifier) {\n throw new Error(`Invalid accessory identifier. Expected ${accessoryIdentifier.toString()} to be ${localAccessoryIdentifier}.`);\n }\n\n const accessoryInfo = Buffer.concat([\n m1.serverPublicKey,\n accessoryIdentifier,\n this.#ephemeralKeyPair.publicKey\n ]);\n\n if (!Ed25519.verify(accessoryInfo, accessorySignature, longTermPublicKey)) {\n throw new Error('Invalid accessory signature.');\n }\n\n return {\n serverEphemeralPublicKey: m1.serverPublicKey,\n sessionKey,\n sharedSecret\n };\n }\n\n async #m3(pairingId: Buffer, secretKey: Buffer, m2: VerifyM2): Promise<VerifyM3> {\n const iosDeviceInfo = Buffer.concat([\n this.#ephemeralKeyPair.publicKey,\n pairingId,\n m2.serverEphemeralPublicKey\n ]);\n\n const iosDeviceSignature = Buffer.from(Ed25519.sign(iosDeviceInfo, secretKey));\n\n const innerTlv = TLV8.encode([\n [TLV8.Value.Identifier, pairingId],\n [TLV8.Value.Signature, iosDeviceSignature]\n ]);\n\n const {authTag, ciphertext} = Chacha20.encrypt(m2.sessionKey, Buffer.from('PV-Msg03'), null, innerTlv);\n const encrypted = Buffer.concat([ciphertext, authTag]);\n\n await this.#requestHandler('m3', TLV8.encode([\n [TLV8.Value.State, TLV8.State.M3],\n [TLV8.Value.EncryptedData, encrypted]\n ]));\n\n return {};\n }\n\n async #m4(m2: VerifyM2, pairingId: Buffer): Promise<AccessoryKeys> {\n return {\n accessoryToControllerKey: Buffer.alloc(0),\n controllerToAccessoryKey: Buffer.alloc(0),\n pairingId,\n sharedSecret: m2.sharedSecret\n };\n }\n}\n\ntype RequestHandler = (step: 'm1' | 'm3' | 'm5', data: Buffer) => Promise<Buffer>;\n\ntype PairM1 = {\n readonly publicKey: Buffer;\n readonly salt: Buffer;\n}\n\ntype PairM2 = {\n readonly publicKey: Buffer;\n readonly proof: Buffer;\n};\n\ntype PairM3 = {\n readonly serverProof: Buffer;\n};\n\ntype PairM4 = {\n readonly sharedSecret: Buffer;\n};\n\ntype PairM5 = {\n readonly authTag: Buffer;\n readonly data: Buffer;\n readonly sessionKey: Buffer;\n};\n\ntype VerifyM1 = {\n readonly encryptedData: Buffer;\n readonly serverPublicKey: Buffer;\n};\n\ntype VerifyM2 = {\n readonly serverEphemeralPublicKey: Buffer;\n readonly sessionKey: Buffer;\n readonly sharedSecret: Buffer;\n};\n\ntype VerifyM3 = {};\n\nexport type AccessoryCredentials = {\n readonly accessoryIdentifier: string;\n readonly accessoryLongTermPublicKey: Buffer;\n readonly pairingId: Buffer;\n readonly publicKey: Buffer;\n readonly secretKey: Buffer;\n};\n\nexport type AccessoryKeys = {\n readonly pairingId: Buffer;\n readonly sharedSecret: Buffer;\n readonly accessoryToControllerKey: Buffer;\n readonly controllerToAccessoryKey: Buffer;\n};\n","import { createSocket, RemoteInfo, Socket } from 'node:dgram';\nimport { NTP } from '@basmilius/apple-encoding';\nimport { Logger } from './reporter';\n\nexport class TimingServer {\n get port(): number {\n return this.#port;\n }\n\n readonly #logger: Logger;\n readonly #socket: Socket;\n #port: number = 0;\n\n constructor() {\n this.#logger = new Logger('timing-server');\n this.#socket = createSocket('udp4');\n\n this.#socket.on('connect', this.#onConnect.bind(this));\n this.#socket.on('error', this.#onError.bind(this));\n this.#socket.on('message', this.#onMessage.bind(this));\n }\n\n close(): void {\n this.#socket.close();\n this.#port = 0;\n }\n\n listen(): Promise<void> {\n return new Promise<void>((resolve, reject) => {\n this.#socket.once('error', reject);\n this.#socket.once('listening', () => {\n this.#socket.removeListener('error', reject);\n this.#onListening();\n resolve();\n });\n this.#socket.bind(0, resolve);\n });\n }\n\n #onConnect(): void {\n this.#socket.setRecvBufferSize(16384);\n this.#socket.setSendBufferSize(16384);\n }\n\n #onError(err: Error): void {\n this.#logger.error('Timing server error', err);\n }\n\n #onListening(): void {\n const {port} = this.#socket.address();\n this.#port = port;\n }\n\n #onMessage(data: Buffer, info: RemoteInfo): void {\n try {\n const request = NTP.decode(data);\n const ntp = NTP.now();\n const [receivedSeconds, receivedFraction] = NTP.parts(ntp);\n\n this.#logger.info(`Timing server ntp=${ntp} receivedSeconds=${receivedSeconds} receivedFraction=${receivedFraction}`);\n\n const response = NTP.encode({\n proto: request.proto,\n type: 0x53 | 0x80,\n seqno: request.seqno,\n padding: 0,\n reftime_sec: request.sendtime_sec,\n reftime_frac: request.sendtime_frac,\n recvtime_sec: receivedSeconds,\n recvtime_frac: receivedFraction,\n sendtime_sec: receivedSeconds,\n sendtime_frac: receivedFraction\n });\n\n this.#socket.send(response, info.port, info.address, err => {\n if (!err) {\n return;\n }\n\n this.#logger.warn(`Timing server failed to send response to ${info.address}:${info.port}`, err);\n });\n } catch (err) {\n this.#logger.warn(`Timing server received malformed packet (${data.length} bytes) from ${info.address}:${info.port}`, err);\n }\n }\n}\n","import { randomBytes } from 'node:crypto';\nimport { networkInterfaces } from 'node:os';\n\nexport function generateActiveRemoteId(): string {\n return Math.floor(Math.random() * 2 ** 32).toString(10);\n}\n\nexport function generateDacpId(): string {\n return Math.floor(Math.random() * 2 ** 64).toString(16).toUpperCase();\n}\n\nexport function generateSessionId(): string {\n return Math.floor(Math.random() * 2 ** 32).toString(10);\n}\n\nexport function getLocalIP(): string | null {\n const interfaces = networkInterfaces();\n\n for (const iface of Object.values(interfaces)) {\n if (!iface) {\n continue;\n }\n\n for (const net of iface) {\n if (net.internal || net.family !== 'IPv4') {\n continue;\n }\n\n if (net.address && net.address !== '127.0.0.1') {\n return net.address;\n }\n }\n }\n\n return null;\n}\n\nexport function getMacAddress(): string {\n const interfaces = networkInterfaces();\n\n for (const iface of Object.values(interfaces)) {\n if (!iface) {\n continue;\n }\n\n for (const net of iface) {\n if (net.internal || net.family !== 'IPv4') {\n continue;\n }\n\n if (net.mac && net.mac !== '00:00:00:00:00:00') {\n return net.mac.toUpperCase();\n }\n }\n }\n\n return '00:00:00:00:00:00';\n}\n\nexport function randomInt32(): number {\n return randomBytes(4).readUInt32BE(0);\n}\n\nexport function randomInt64(): bigint {\n return randomBytes(8).readBigUint64LE(0);\n}\n\nexport function uint16ToBE(value: number): Buffer {\n const buffer = Buffer.allocUnsafe(2);\n buffer.writeUInt16BE(value, 0);\n\n return buffer;\n}\n\nexport function uint53ToLE(value: number): Buffer {\n const [upper, lower] = splitUInt53(value);\n const buffer = Buffer.allocUnsafe(8);\n buffer.writeUInt32LE(lower, 0);\n buffer.writeUInt32LE(upper, 4);\n\n return buffer;\n}\n\nfunction splitUInt53(number: number): [number, number] {\n const MAX_UINT32 = 0x00000000FFFFFFFF;\n const MAX_INT53 = 0x001FFFFFFFFFFFFF;\n\n if (number <= -1 || number > MAX_INT53) {\n throw new Error('Number out of range.');\n }\n\n if (Math.floor(number) !== number) {\n throw new Error('Number is not an integer.');\n }\n\n let upper: number = 0;\n const signbit = number & 0xFFFFFFFF;\n const lower = signbit < 0 ? (number & 0x7FFFFFFF) + 0x80000000 : signbit;\n\n if (number > MAX_UINT32) {\n upper = (number - lower) / (MAX_UINT32 + 1);\n }\n\n return [upper, lower];\n}\n"],"x_google_ignoreList":[0,1,2,3],"mappings":"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"}
|