/**
 * 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 type { ProtocolReaderCallback , ProtocolWriter } from './protocol' ; import { ProtocolClient , ProtocolReader , ProtocolReaderFactory } from './protocol' ; const debug = Debug ( 'expo:xdl:ios:lib:protocol:gdb' ) ; const ACK_SUCCESS = '+' . charCodeAt ( 0 ) ; export interface GDBMessage { cmd : string ; args : string [ ] ; } export class GDBProtocolClient extends ProtocolClient < GDBMessage > { constructor ( socket ) { super ( socket , new ProtocolReaderFactory ( GDBProtocolReader ) , new GDBProtocolWriter ( ) ) ; } } export class GDBProtocolReader extends ProtocolReader { constructor ( callback ) { super ( 1 , callback ) ; } onData ( data ? ) { try { this . buffer = data ? Buffer . concat ( [ this . buffer , data ] ) : this . buffer ; if ( this . buffer . length < this . headerSize ) { return ; } if ( this . parseHeader ( this . buffer ) === - 1 ) { const packetData = this . buffer . toString ( ) . match ( '\\$.*#[0-9a-f]{2}' ) ; if ( packetData == null ) { return ; } const body = Buffer . from ( packetData [ 0 ] ) ; this . buffer = this . buffer . slice ( this . headerSize + body . length ) ; this . callback ( this . parseBody ( body ) ) ; if ( this . buffer . length ) { this . onData ( ) ; } } } catch ( err ) { this . callback ( null , err ) ; } } parseHeader ( data ) { if ( data [ 0 ] !== ACK_SUCCESS ) { throw new Error ( 'Unsuccessful debugserver response' ) ; } return - 1 ; } parseBody ( buffer ) { debug ( `Response body: ${ buffer . toString ( ) }` ) ; const checksum = buffer . slice ( - 3 ) . toString ( ) ; if ( checksum . match ( /#[0-9a-f]{2}/ ) ) { const msg = buffer . slice ( 1 , - 3 ) . toString ( ) ; if ( validateChecksum ( checksum , msg ) ) { return msg ; } else { throw new Error ( 'Invalid checksum received from debugserver' ) ; } } else { throw new Error ( "Didn't receive checksum" ) ; } } } export class GDBProtocolWriter implements ProtocolWriter { write ( socket , msg ) { const { cmd , args } = msg ; debug ( `Socket write: ${ cmd }, args: ${ args }` ) ; const encodedArgs = args . map ( arg => Buffer . from ( arg ) . toString ( 'hex' ) ) . join ( ) . toUpperCase ( ) ; const checksumStr = calculateChecksum ( cmd + encodedArgs ) ; const formattedCmd = `$${ cmd }${ encodedArgs }#${ checksumStr }` ; socket . write ( formattedCmd ) ; } } function calculateChecksum ( cmdStr ) { let checksum = 0 ; for ( let i = 0 ; i < cmdStr . length ; i ++ ) { checksum += cmdStr . charCodeAt ( i ) ; } let result = ( checksum % 256 ) . toString ( 16 ) ; if ( result . length === 1 ) { result = `0${ result }` ; } return result ; } function validateChecksum ( checksum , msg ) { const checksumVal = checksum . slice ( 1 ) ; const computedChecksum = calculateChecksum ( msg ) ; debug ( `Checksum: ${ checksumVal }, computed checksum: ${ computedChecksum }` ) ; return checksumVal === computedChecksum ; }