UNPKG

@expo/cli

Version:
145 lines (144 loc) 5.76 kB
/** * 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. */ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); function _export(target, all) { for(var name in all)Object.defineProperty(target, name, { enumerable: true, get: all[name] }); } _export(exports, { GDBProtocolClient: ()=>GDBProtocolClient, GDBProtocolReader: ()=>GDBProtocolReader, GDBProtocolWriter: ()=>GDBProtocolWriter, validateChecksum: ()=>validateChecksum }); function _debug() { const data = /*#__PURE__*/ _interopRequireDefault(require("debug")); _debug = function() { return data; }; return data; } const _abstractProtocol = require("./AbstractProtocol"); const _errors = require("../../../../utils/errors"); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } const debug = (0, _debug().default)("expo:apple-device:protocol:gdb"); const ACK_SUCCESS = "+".charCodeAt(0); class GDBProtocolClient extends _abstractProtocol.ProtocolClient { constructor(socket){ super(socket, new _abstractProtocol.ProtocolReaderFactory(GDBProtocolReader), new GDBProtocolWriter()); } } class GDBProtocolReader extends _abstractProtocol.ProtocolReader { constructor(callback){ super(1 /* "Header" is '+' or '-' */ , callback); } onData(data) { // the GDB protocol does not support body length in its header so we cannot rely on // the parent implementation to determine when a payload is complete try { // if there's data, add it to the existing buffer this.buffer = data ? Buffer.concat([ this.buffer, data ]) : this.buffer; // do we have enough bytes to proceed if (this.buffer.length < this.headerSize) { return; // incomplete header, wait for more } // first, check the header if (this.parseHeader(this.buffer) === -1) { // we have a valid header so check the body. GDB packets will always be a leading '$', data bytes, // a trailing '#', and a two digit checksum. minimum valid body is the empty response '$#00' // https://developer.apple.com/library/archive/documentation/DeveloperTools/gdb/gdb/gdb_33.html const packetData = this.buffer.toString().match("\\$.*#[0-9a-f]{2}"); if (packetData == null) { return; // incomplete body, wait for more } // extract the body and update the buffer const body = Buffer.from(packetData[0]); this.buffer = this.buffer.slice(this.headerSize + body.length); // parse the payload and recurse if there is more data to process 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 _errors.CommandError("APPLE_DEVICE_GDB", "Unsuccessful debugserver response"); } // TODO: retry? return -1; } parseBody(buffer) { debug(`Response body: ${buffer.toString()}`); // check for checksum const checksum = buffer.slice(-3).toString(); if (checksum.match(/#[0-9a-f]{2}/)) { // remove '$' prefix and checksum const msg = buffer.slice(1, -3).toString(); if (validateChecksum(checksum, msg)) { return msg; } else if (msg.startsWith("E")) { if (msg.match(/the device was not, or could not be, unlocked/)) { throw new _errors.CommandError("APPLE_DEVICE_LOCKED", "Device is currently locked."); } // Error message from debugserver -- Drop the `E` return msg.slice(1); } else { throw new _errors.CommandError("APPLE_DEVICE_GDB", `Invalid checksum received from debugserver. (checksum: ${checksum}, msg: ${msg})`); } } else { throw new _errors.CommandError("APPLE_DEVICE_GDB", "Didn't receive checksum"); } } } class GDBProtocolWriter { write(socket, msg) { const { cmd , args } = msg; debug(`Socket write: ${cmd}, args: ${args}`); // hex encode and concat all 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); } } // hex value of (sum of cmd chars mod 256) function calculateChecksum(cmdStr) { let checksum = 0; for(let i = 0; i < cmdStr.length; i++){ checksum += cmdStr.charCodeAt(i); } let result = (checksum % 256).toString(16); // pad if necessary if (result.length === 1) { result = `0${result}`; } return result; } function validateChecksum(checksum, msg) { // remove '#' from checksum const checksumVal = checksum.startsWith("#") ? checksum.slice(1) : checksum; // remove '$' from msg and calculate its checksum const computedChecksum = calculateChecksum(msg); // debug(`Checksum: ${checksumVal}, computed checksum: ${computedChecksum}`); return checksumVal === computedChecksum; } //# sourceMappingURL=GDBProtocol.js.map