/**
 * Copyright (c) 2021 Expo, Inc.
 * Copyright (c) 2018 Drifty Co.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */ import Debug from 'debug' ; import type * as net from 'net' ; import * as tls from 'tls' ; import { LockdownProtocolClient } from '../protocol/lockdown' ; import { ResponseError , ServiceClient } from './client' ; import type { UsbmuxdPairRecord } from './usbmuxd' ; const debug = Debug ( 'expo:xdl:ios:lib:client:lockdownd' ) ; export interface DeviceValues { BasebandCertId : number ; BasebandKeyHashInformation : { AKeyStatus : number ; SKeyHash : Buffer ; SKeyStatus : number ; } ; BasebandSerialNumber : Buffer ; BasebandVersion : string ; BoardId : number ; BuildVersion : string ; ChipID : number ; DeviceClass : string ; DeviceColor : string ; DeviceName : string ; DieID : number ; HardwareModel : string ; HasSiDP : boolean ; PartitionType : string ; ProductName : string ; ProductType : string ; ProductVersion : string ; ProductionSOC : boolean ; ProtocolVersion : string ; TelephonyCapability : boolean ; UniqueChipID : number ; UniqueDeviceID : string ; WiFiAddress : string ; [ key ] : any ; } interface LockdowndServiceResponse { Request : 'StartService' ; Service : string ; Port : number ; EnableServiceSSL ? : boolean ; } interface LockdowndSessionResponse { Request : 'StartSession' ; EnableSessionSSL : boolean ; } interface LockdowndAllValuesResponse { Request : 'GetValue' ; Value : DeviceValues ; } interface LockdowndValueResponse { Request : 'GetValue' ; Key : string ; Value : string ; } interface LockdowndQueryTypeResponse { Request : 'QueryType' ; Type : string ; } function isLockdowndServiceResponse ( resp ) { return resp . Request === 'StartService' && resp . Service !== undefined && resp . Port !== undefined ; } function isLockdowndSessionResponse ( resp ) { return resp . Request === 'StartSession' ; } function isLockdowndAllValuesResponse ( resp ) { return resp . Request === 'GetValue' && resp . Value !== undefined ; } function isLockdowndValueResponse ( resp ) { return resp . Request === 'GetValue' && resp . Key !== undefined && typeof resp . Value === 'string' ; } function isLockdowndQueryTypeResponse ( resp ) { return resp . Request === 'QueryType' && resp . Type !== undefined ; } export class LockdowndClient extends ServiceClient < LockdownProtocolClient > { constructor ( public socket ) { super ( socket , new LockdownProtocolClient ( socket ) ) ; } async startService ( name ) { debug ( `startService: ${ name }` ) ; const resp = await this . protocolClient . sendMessage ( { Request : 'StartService' , Service : name } ) ; if ( isLockdowndServiceResponse ( resp ) ) { return { port : resp . Port , enableServiceSSL : ! ! resp . EnableServiceSSL } ; } else { throw new ResponseError ( `Error starting service ${ name }` , resp ) ; } } async startSession ( pairRecord ) { debug ( `startSession: ${ pairRecord }` ) ; const resp = await this . protocolClient . sendMessage ( { Request : 'StartSession' , HostID : pairRecord . HostID , SystemBUID : pairRecord . SystemBUID } ) ; if ( isLockdowndSessionResponse ( resp ) ) { if ( resp . EnableSessionSSL ) { this . protocolClient . socket = new tls . TLSSocket ( this . protocolClient . socket , { secureContext : tls . createSecureContext ( { secureProtocol : 'TLSv1_method' , cert : pairRecord . RootCertificate , key : pairRecord . RootPrivateKey } ) } ) ; debug ( `Socket upgraded to TLS connection` ) ; } } else { throw new ResponseError ( 'Error starting session' , resp ) ; } } async getAllValues ( ) { debug ( `getAllValues` ) ; const resp = await this . protocolClient . sendMessage ( { Request : 'GetValue' } ) ; if ( isLockdowndAllValuesResponse ( resp ) ) { return resp . Value ; } else { throw new ResponseError ( 'Error getting lockdown value' , resp ) ; } } async getValue ( val ) { debug ( `getValue: ${ val }` ) ; const resp = await this . protocolClient . sendMessage ( { Request : 'GetValue' , Key : val } ) ; if ( isLockdowndValueResponse ( resp ) ) { return resp . Value ; } else { throw new ResponseError ( 'Error getting lockdown value' , resp ) ; } } async queryType ( ) { debug ( 'queryType' ) ; const resp = await this . protocolClient . sendMessage ( { Request : 'QueryType' } ) ; if ( isLockdowndQueryTypeResponse ( resp ) ) { return resp . Type ; } else { throw new ResponseError ( 'Error getting lockdown query type' , resp ) ; } } async doHandshake ( pairRecord ) { debug ( 'doHandshake' ) ; await this . startSession ( pairRecord ) ; } }