diff --git a/src/crypto.js b/src/crypto.js deleted file mode 100644 index cbd4089..0000000 --- a/src/crypto.js +++ /dev/null @@ -1,23 +0,0 @@ - -let CRC8 = [0, 94, -68, -30, 97, 63, -35, -125, -62, -100, 126, 32, -93, -3, 31, 65, -99, -61, 33, 127, -4, -94, 64, 30, 95, 1, -29, -67, 62, 96, -126, -36, 35, 125, -97, -63, 66, 28, -2, -96, -31, -65, 93, 3, -128, -34, 60, 98, -66, -32, 2, 92, -33, -127, 99, 61, 124, 34, -64, -98, 29, 67, -95, -1, 70, 24, -6, -92, 39, 121, -101, -59, -124, -38, 56, 102, -27, -69, 89, 7, -37, -123, 103, 57, -70, -28, 6, 88, 25, 71, -91, -5, 120, 38, -60, -102, 101, 59, -39, -121, 4, 90, -72, -26, -89, -7, 27, 69, -58, -104, 122, 36, -8, -90, 68, 26, -103, -57, 37, 123, 58, 100, -122, -40, 91, 5, -25, -71, -116, -46, 48, 110, -19, -77, 81, 15, 78, 16, -14, -84, 47, 113, -109, -51, 17, 79, -83, -13, 112, 46, -52, -110, -45, -115, 111, 49, -78, -20, 14, 80, -81, -15, 19, 77, -50, -112, 114, 44, 109, 51, -47, -113, 12, 82, -80, -18, 50, 108, -114, -48, 83, 13, -17, -79, -16, -82, 76, 18, -111, -49, 45, 115, -54, -108, 118, 40, -85, -11, 23, 73, 8, 86, -76, -22, 105, 55, -43, -117, 87, 9, -21, -75, 54, 104, -118, -44, -107, -53, 41, 119, -12, -86, 72, 22, -23, -73, 85, 11, -120, -42, 52, 106, 43, 117, -105, -55, 74, 20, -10, -88, 116, 42, -56, -106, 21, 75, -87, -9, -74, -24, 10, 84, -41, -119, 107, 53]; - -let CRC16 = [0, 4489, 8978, 12955, 17956, 22445, 25910, 29887, 35912, 40385, 44890, 48851, 51820, 56293, 59774, 63735, 4225, 264, 13203, 8730, 22181, 18220, 30135, 25662, 40137, 36160, 49115, 44626, 56045, 52068, 63999, 59510, 8450, 12427, 528, 5017, 26406, 30383, 17460, 21949, 44362, 48323, 36440, 40913, 60270, 64231, 51324, 55797, 12675, 8202, 4753, 792, 30631, 26158, 21685, 17724, 48587, 44098, 40665, 36688, 64495, 60006, 55549, 51572, 16900, 21389, 24854, 28831, 1056, 5545, 10034, 14011, 52812, 57285, 60766, 64727, 34920, 39393, 43898, 47859, 21125, 17164, 29079, 24606, 5281, 1320, 14259, 9786, 57037, 53060, 64991, 60502, 39145, 35168, 48123, 43634, 25350, 29327, 16404, 20893, 9506, 13483, 1584, 6073, 61262, 65223, 52316, 56789, 43370, 47331, 35448, 39921, 29575, 25102, 20629, 16668, 13731, 9258, 5809, 1848, 65487, 60998, 56541, 52564, 47595, 43106, 39673, 35696, 33800, 38273, 42778, 46739, 49708, 54181, 57662, 61623, 2112, 6601, 11090, 15067, 20068, 24557, 28022, 31999, 38025, 34048, 47003, 42514, 53933, 49956, 61887, 57398, 6337, 2376, 15315, 10842, 24293, 20332, 32247, 27774, 42250, 46211, 34328, 38801, 58158, 62119, 49212, 53685, 10562, 14539, 2640, 7129, 28518, 32495, 19572, 24061, 46475, 41986, 38553, 34576, 62383, 57894, 53437, 49460, 14787, 10314, 6865, 2904, 32743, 28270, 23797, 19836, 50700, 55173, 58654, 62615, 32808, 37281, 41786, 45747, 19012, 23501, 26966, 30943, 3168, 7657, 12146, 16123, 54925, 50948, 62879, 58390, 37033, 33056, 46011, 41522, 23237, 19276, 31191, 26718, 7393, 3432, 16371, 11898, 59150, 63111, 50204, 54677, 41258, 45219, 33336, 37809, 27462, 31439, 18516, 23005, 11618, 15595, 3696, 8185, 63375, 58886, 54429, 50452, 45483, 40994, 37561, 33584, 31687, 27214, 22741, 18780, 15843, 11370, 7921, 3960]; - - -function calcCrc16(Arr, i) { - let i2 = 13970; - for (let i3 = 0; i3 < i; i3++) { - i2 = CRC16[(i2 ^ Arr[i3]) & 255] ^ (i2 >> 8); - } - return i2; -} - -function calcCrc8(Arr, i) { - let b2 = 119; - for (let i2 = 0; i2 < i; i2++) { - b2 = CRC8[(b2 ^ Arr[i2]) & 255]; - } - return b2; -} - -module.exports = { calcCrc16, calcCrc8 } \ No newline at end of file diff --git a/src/duml/Packer.js b/src/duml/Packer.js new file mode 100644 index 0000000..6461203 --- /dev/null +++ b/src/duml/Packer.js @@ -0,0 +1,247 @@ +const CRC8 = new Int8Array([0, 94, 188, 226, 97, 63, 221, 131, 194, 156, 126, 32, 163, 253, 31, 65, 157, 195, 33, 127, 252, 162, 64, 30, 95, 1, 227, 189, 62, 96, 130, 220, 35, 125, 159, 193, 66, 28, 254, 160, 225, 191, 93, 3, 128, 222, 60, 98, 190, 224, 2, 92, 223, 129, 99, 61, 124, 34, 192, 158, 29, 67, 161, 255, 70, 24, 250, 164, 39, 121, 155, 197, 132, 218, 56, 102, 229, 187, 89, 7, 219, 133, 103, 57, 186, 228, 6, 88, 25, 71, 165, 251, 120, 38, 196, 154, 101, 59, 217, 135, 4, 90, 184, 230, 167, 249, 27, 69, 198, 152, 122, 36, 248, 166, 68, 26, 153, 199, 37, 123, 58, 100, 134, 216, 91, 5, 231, 185, 140, 210, 48, 110, 237, 179, 81, 15, 78, 16, 242, 172, 47, 113, 147, 205, 17, 79, 173, 243, 112, 46, 204, 146, 211, 141, 111, 49, 178, 236, 14, 80, 175, 241, 19, 77, 206, 144, 114, 44, 109, 51, 209, 143, 12, 82, 176, 238, 50, 108, 142, 208, 83, 13, 239, 177, 240, 174, 76, 18, 145, 207, 45, 115, 202, 148, 118, 40, 171, 245, 23, 73, 8, 86, 180, 234, 105, 55, 213, 139, 87, 9, 235, 181, 54, 104, 138, 212, 149, 203, 41, 119, 244, 170, 72, 22, 233, 183, 85, 11, 136, 214, 52, 106, 43, 117, 151, 201, 74, 20, 246, 168, 116, 42, 200, 150, 21, 75, 169, 247, 182, 232, 10, 84, 215, 137, 107, 53,]) +const CRC16 = new Int16Array([0, 4489, 8978, 12955, 17956, 22445, 25910, 29887, 35912, 40385, 44890, 48851, 51820, 56293, 59774, 63735, 4225, 264, 13203, 8730, 22181, 18220, 30135, 25662, 40137, 36160, 49115, 44626, 56045, 52068, 63999, 59510, 8450, 12427, 528, 5017, 26406, 30383, 17460, 21949, 44362, 48323, 36440, 40913, 60270, 64231, 51324, 55797, 12675, 8202, 4753, 792, 30631, 26158, 21685, 17724, 48587, 44098, 40665, 36688, 64495, 60006, 55549, 51572, 16900, 21389, 24854, 28831, 1056, 5545, 10034, 14011, 52812, 57285, 60766, 64727, 34920, 39393, 43898, 47859, 21125, 17164, 29079, 24606, 5281, 1320, 14259, 9786, 57037, 53060, 64991, 60502, 39145, 35168, 48123, 43634, 25350, 29327, 16404, 20893, 9506, 13483, 1584, 6073, 61262, 65223, 52316, 56789, 43370, 47331, 35448, 39921, 29575, 25102, 20629, 16668, 13731, 9258, 5809, 1848, 65487, 60998, 56541, 52564, 47595, 43106, 39673, 35696, 33800, 38273, 42778, 46739, 49708, 54181, 57662, 61623, 2112, 6601, 11090, 15067, 20068, 24557, 28022, 31999, 38025, 34048, 47003, 42514, 53933, 49956, 61887, 57398, 6337, 2376, 15315, 10842, 24293, 20332, 32247, 27774, 42250, 46211, 34328, 38801, 58158, 62119, 49212, 53685, 10562, 14539, 2640, 7129, 28518, 32495, 19572, 24061, 46475, 41986, 38553, 34576, 62383, 57894, 53437, 49460, 14787, 10314, 6865, 2904, 32743, 28270, 23797, 19836, 50700, 55173, 58654, 62615, 32808, 37281, 41786, 45747, 19012, 23501, 26966, 30943, 3168, 7657, 12146, 16123, 54925, 50948, 62879, 58390, 37033, 33056, 46011, 41522, 23237, 19276, 31191, 26718, 7393, 3432, 16371, 11898, 59150, 63111, 50204, 54677, 41258, 45219, 33336, 37809, 27462, 31439, 18516, 23005, 11618, 15595, 3696, 8185, 63375, 58886, 54429, 50452, 45483, 40994, 37561, 33584, 31687, 27214, 22741, 18780, 15843, 11370, 7921, 3960]) + + +const calcCrc16 = (array, length) => { + let seed = 0x3692; + + // seed = 0x1012 // Naza M + // seed = 0x1013 // Phantom 2 + // seed = 0x7000 // Naza M V2 + // seed = 0x3692 // P3/P4/Mavic/Later + + for (let pos = 0; pos < length; pos++) { + seed = (CRC16[((seed ^ array[pos]) & 0xff)] & 0xffff) ^ (seed >> 8); + } + return seed; +} + +const calcCrc8 = (array, length) => { + let crc = 0x77; + for (let pos = 0; pos < length; pos++) { + crc = CRC8[(crc ^ array[pos]) & 0xff]; + } + return crc; +} + +class pack { + constructor(inputArray = null) { + this.buffer; + this.sof = 85; + this.ccode; + this.cmdId; + this.cmdSet; + this.cmdType; + this.crc16; + this.crc8; + this.encryptType; + this.isNeedAck; + this.length; + this.receiverId; + this.receiverType; + this.senderId; + this.senderType; + this.seq; + this.data; + this.version = 1; + + this.packRepeatTimes = 2; + this.packTimeOut = 1000; + this.repeatTimes = 2; + this.timeOut = 1000; + + if (inputArray != null && typeof inputArray === "object") { + this.unpack(inputArray); + } + } + + + + getLength() { + return this.length; + } + + encodeSequenceNo() { + let sequence = new Uint8Array(2); + sequence[0] = (this.seq & 255); + sequence[1] = ((this.seq & 65280) >> 8); + return sequence; + } + + decodeBytes(offset, length) { + let value = 0; + for (let i = (offset + length) - 1; i >= offset; i--) { + value = (value << 8) | (this.buffer[i] & 255); + } + return value; + } + + reCrc() { + if (this.buffer != null) { + let endOfBuffer = this.buffer.length - 2; + let crcs = calcCrc16(this.buffer, endOfBuffer) + this.buffer[endOfBuffer - 0] = (crcs & 255); + this.buffer[endOfBuffer + 1] = ((65280 & crcs) >> 8); + } + } + + validate() { + // check the last two bytes of the buffer are equal to the last 2 after reCrc + if (this.buffer != null) { + let endOfBuffer = this.buffer.length - 2; + let crcs = calcCrc16(this.buffer, endOfBuffer) + return (this.buffer[endOfBuffer - 0] == (crcs & 255) && this.buffer[endOfBuffer + 1] == ((65280 & crcs) >> 8)) + } else return false; + } + + // isNeedCcode() { + // // NOT IMPLEMENTED + // // try { + // // this.cmdSetObj = CmdSet.find(this.cmdSet); + // // if (this.cmdSetObj == null || this.cmdSetObj.cmdIdClass() == null) { + + // // } else { + // // this.isNeedCcode = this.cmdSetObj.cmdIdClass().isNeedCcode(this.cmdId); + // // } + // // } catch (e) { + + // // } + // } + + pack() { + if (this.data == null) { + this.length = 13; + } else { + this.length = this.data.length + 13; + } + this.buffer = new Uint8Array(this.length); + let box_head = this.buffer + box_head[0] = this.sof; + box_head[1] = (this.length & 255); + box_head[2] = ((this.length >> 8) & 3); + box_head[2] = (box_head[2] | 4); + box_head[3] = calcCrc8(box_head, 3); + this.crc8 = box_head[3]; + box_head[4] = ((this.senderId << 5) | this.senderType); + box_head[5] = ((this.receiverId << 5) | this.receiverType); + box_head[6] = this.encodeSequenceNo()[0]; + box_head[7] = this.encodeSequenceNo()[1]; + box_head[8] = ((this.cmdType << 7) | (this.isNeedAck << 5) | this.encryptType); + box_head[9] = this.cmdSet; + box_head[10] = this.cmdId; + + if (this.data != null) { + this.data.forEach((e, i) => { + box_head[i + 11] = e + }); + } + + let endOfBuffer = this.length - 2; + let crcs = calcCrc16(box_head, endOfBuffer) + this.crc16 = crcs; + box_head[this.length - 2] = (crcs & 255); + box_head[this.length - 1] = ((65280 & crcs) >> 8); + + this.buffer = box_head; + } + + unpack(buffer) { + if (buffer != null && buffer.length >= 13) { + this.buffer = buffer; + this.sof = buffer[0]; + + let VL = this.decodeBytes(1, 2); + this.version = VL >> 10; + this.length = VL & 1023; + + this.crc8 = this.buffer[3]; + this.senderId = parseInt(this.buffer[4]) >> 5; + this.senderType = parseInt(this.buffer[4]) & 31; + this.receiverId = parseInt(this.buffer[5]) >> 5; + this.receiverType = parseInt(this.buffer[5]) & 31; + this.seq = this.decodeBytes(6, 2); + this.cmdType = parseInt(this.buffer[8]) >> 7; + this.isNeedAck = (parseInt(this.buffer[8]) >> 5) & 3; + this.encryptType = parseInt(this.buffer[8]) & 7; + this.cmdSet = parseInt(this.buffer[9]); + this.cmdId = parseInt(this.buffer[10]); + + if (this.cmdType == 1) { + this.ccode = parseInt(this.buffer[11]); + } + + let dataLen = (this.buffer.length - 11) - 2; + if (dataLen > 0) { + this.data = new Uint8Array(dataLen); + + this.data.forEach((e, i) => { + this.data[i] = this.buffer[i + 11] + }); + + } + this.crc16 = this.decodeBytes(this.buffer.length - 2, 2); //BytesUtil.getInt(buffer, buffer.length - 2, 2); + } + } + + resetData() { + this.isNeedCcode = true; + this.buffer = null; + this.sof = 0; + this.version = 1; + this.length = 0; + this.crc8 = 0; + this.senderId = 0; + this.senderType = 0; + this.receiverId = 0; + this.receiverType = 0; + this.seq = 0; + this.cmdType = 0; + this.isNeedAck = 0; + this.encryptType = 0; + this.cmdSet = 0; + this.cmdId = 0; + this.ccode = 0; + this.data = null; + this.crc16 = 0; + } + + toString() { + if (this.buffer != null) { + return Array.prototype.map.call(new Uint8Array(this.buffer), x => ('00' + x.toString(16)).slice(-2)).join(''); + } else return ""; + } + + toUint8Array() { + return new Uint8Array(this.buffer); + } + + toObject() { + return { + sof: this.sof, + version: this.version, + ccode: this.ccode, + cmdId: this.cmdId, + cmdSet: this.cmdSet, + cmdType: this.cmdType, + crc16: this.crc16, + crc8: this.crc8, + encryptType: this.encryptType, + isNeedAck: this.isNeedAck, + length: this.length, + receiverId: this.receiverId, + receiverType: this.receiverType, + senderId: this.senderId, + senderType: this.senderType, + seq: this.seq, + data: this.data, + buffer: this.buffer + + } + } + + static calcCrc16 = calcCrc16; + static calcCrc8 = calcCrc8; +} + + +module.exports = pack; \ No newline at end of file diff --git a/src/duml/Session.js b/src/duml/Session.js new file mode 100644 index 0000000..3a57a53 --- /dev/null +++ b/src/duml/Session.js @@ -0,0 +1,145 @@ +const Packer = require('./Packer') + +const hexToUint8Array = (hex) => { + if (!hex || hex.length === 0) return null; + return new Uint8Array(hex.match(/.{1,2}/g).map((i) => parseInt(i, 16))); +} + +const decodeBytes = (buffer, offset, length) => { + let value = 0; + for (let i = (offset + length) - 1; i >= offset; i--) { + value = (value << 8) | (buffer[i] & 255); + } + return value; +} + +class Session { + constructor() { + this.sentPackets = []; + this.receiveBuffer = new Uint8Array(0); + this.lastSeenSeq = 0; + this.unmatchedListener = () => { }; + this.filterListeners = []; + } + + setUnmatchedListener(callback) { + if (typeof callback === 'function') this.unmatchedListener = callback; + else throw new Error("Callback must be a function"); + } + + addFilterListener(filters, callback) { + if (typeof callback === 'function') this.filterListeners.push({ filters, callback }); + else throw new Error("Callback must be a function"); + } + + receive(data) { + // if the data is not a Uint8Array, convert it + if (!(data instanceof Uint8Array)) { + data = hexToUint8Array(data); + } + + // append data to the receive buffer + const removeCount = (this.receiveBuffer.length > 300) ? 50 : 0; + const bytesToCopy = this.receiveBuffer.length - removeCount; + + const newBuffer = new Uint8Array(bytesToCopy + data.length); + + newBuffer.set(this.receiveBuffer.subarray(removeCount, bytesToCopy)); + newBuffer.set(data, bytesToCopy); + + this.receiveBuffer = newBuffer; + + // process the buffer + while (this.receiveBuffer.length > 3) { + if (this.receiveBuffer[0] != 85) { + + this.receiveBuffer = this.receiveBuffer.subarray(1); + continue; + } + + const length = decodeBytes(this.receiveBuffer, 1, 2) & 0x3FF; + + if (length > 300) { // sanity limit + this.receiveBuffer = this.receiveBuffer.subarray(3); + continue; + } + + if (this.receiveBuffer.length < length) break; + + const packet = new Packer(); + packet.unpack(this.receiveBuffer.subarray(0, length)); + + // check if packet is valid + if (!packet.validate()) { + this.receiveBuffer = this.receiveBuffer.subarray(length - 1); + continue; + } + + + if (packet.buffer != null) { + // first send to global listeners + this._emitToFilterListeners(packet); + + // we can assume that the packet is valid + this.receiveBuffer = this.receiveBuffer.subarray(length); + + this.lastSeenSeq = packet.seq; + + // send packet to be matched up + const matched = this.sentPackets.find(lookup => (lookup.seq == packet.seq)); + if (matched != null) { + // we have a match + this.sentPackets.splice(this.sentPackets.indexOf(matched), 1); + + matched.timeout && clearTimeout(matched.timeout); + + // send the callback + matched.Promise.resolve(packet); + } else { + // we don't have a match, send to unmatched callback + this.unmatchedListener(packet); + } + } + } + } + + transmit(PackedPacket, ttl = 1000) { + return new Promise(function (resolve, reject) { + // check if PackedPacket requires a response + if (ttl === -1) { + return resolve(null); + } + + PackedPacket.Promise = { resolve, reject }; + + // set a timeout to reject the promise + PackedPacket.timeout = setTimeout(function () { + + // timeout the promise + PackedPacket.Promise.reject(new Error("Timeout")); + // remove the packet from the sent list + this.sentPackets.splice(this.sentPackets.indexOf(PackedPacket), 1); + }.bind(this), ttl); + + this.sentPackets.push(PackedPacket); + + }.bind(this)); + } + + _emitToFilterListeners(packet) { + const packetObj = packet.toObject(); + const matched = this.filterListeners.filter(listener => { + return Object.keys(listener.filter).some(key => { + // loose equality check so '0xFF' == 255 etc returns true + return filter[key] == packetObj[key]; + }); + }) + + for (const listener of matched) { + listener.callback(packet); + } + } +} + +module.exports = Session + diff --git a/src/exploit.js b/src/exploit.js index ef138ed..cf0267d 100644 --- a/src/exploit.js +++ b/src/exploit.js @@ -3,7 +3,7 @@ const https = require('https') const configs = require('./devices') -const {talk, sleep, confirm} = require('./utils') +const {talk, sleep, confirm, initSerialReceiver} = require('./utils') let portPath = "" const baudRate = 19200 @@ -17,12 +17,13 @@ const restartWait = 30000 async function identifyDevice(path) { portPath = path port = new SerialPort({path: portPath, baudRate}) + initSerialReceiver(port); return talk(port, { receiverType: 0, cmdSet: 0, cmdId: 1 }, true).then((result) => { - //console.info("identify response", Buffer.from(result.data).toString('hex')) + // console.info("identify response", Buffer.from(result.data).toString('hex')) const responseText = String.fromCharCode.apply(null, result.data) device = responseText.match(/(WM150|LT150|GL150|GP150)/g)[0] console.log("found", device) @@ -114,6 +115,7 @@ async function restart() { console.log("waiting for device to reboot") return sleep(restartWait).then(() => { port = new SerialPort({path: portPath, baudRate}) + initSerialReceiver(port) }) }) } @@ -179,6 +181,7 @@ async function unlockDebug() { console.log("waiting for device to reboot") return sleep(restartWait).then(() => { port = new SerialPort({path: portPath, baudRate}) + initSerialReceiver(port) }) }) } @@ -290,6 +293,7 @@ async function hardlock(path) { }) .then(() => { port = new SerialPort({path: portPath, baudRate}) + initSerialReceiver(port) }) .then(() => { console.log("sending flash command for second slot") @@ -303,7 +307,8 @@ async function hardlock(path) { return sleep(120000) }) .then(() => { - port = new SerialPort({path: portPath, baudRate}) + port = new SerialPort({path: portPath, baudRate}) + initSerialReceiver(port) }) .then(patch) .then(()=> { diff --git a/src/index.js b/src/index.js index 8461185..d436a47 100644 --- a/src/index.js +++ b/src/index.js @@ -1,7 +1,6 @@ const exploit = require("./exploit") const constants = require("./constants") -const crypto = require("./crypto") -const packer = require("./packer") +const packer = require("./duml/packer") const payload = require("./payload") const proxy = require("./proxy") const utils = require("./utils") @@ -9,7 +8,6 @@ const utils = require("./utils") module.exports = { exploit, constants, - crypto, packer, payload, proxy, diff --git a/src/packer.js b/src/packer.js deleted file mode 100644 index a1488d0..0000000 --- a/src/packer.js +++ /dev/null @@ -1,206 +0,0 @@ -const crypto = require('./crypto'); - -class pack { - constructor() { - this.sof = 85; - - this.version = 1; - - this.packRepeatTimes = 2; - this.packTimeOut = 1000; - this.repeatTimes = 2; - this.timeOut = 1000; - } - - getLength() { - return this.length; - } - - encodeSequenceNo() { - let sequence = new Uint8Array(2); - sequence[0] = (this.seq & 255); - sequence[1] = ((this.seq & 65280) >> 8); - return sequence; - } - - decodeBytes(offset, length) { - let value = 0; - for (let i = (offset + length) - 1; i >= offset; i--) { - value = (value << 8) | (this.buffer[i] & 255); - } - return value; - } - - reCrc() { - if (this.buffer != null) { - let endOfBuffer = this.buffer.length - 2; - let crcs = crypto.calcCrc16(this.buffer, endOfBuffer) - this.buffer[endOfBuffer - 0] = (crcs & 255); - this.buffer[endOfBuffer + 1] = ((65280 & crcs) >> 8); - } - } - - isNeedCcode() { - // NOT IMPLEMENTED - // try { - // this.cmdSetObj = CmdSet.find(this.cmdSet); - // if (this.cmdSetObj == null || this.cmdSetObj.cmdIdClass() == null) { - - // } else { - // this.isNeedCcode = this.cmdSetObj.cmdIdClass().isNeedCcode(this.cmdId); - // } - // } catch (e) { - - // } - } - - pack() { - if (this.data == null) { - this.length = 13; - } else { - this.length = this.data.length + 13; - } - this.buffer = new Uint8Array(this.length); - let box_head = this.buffer - box_head[0] = this.sof; - box_head[1] = (this.length & 255); - box_head[2] = ((this.length >> 8) & 3); - box_head[2] = (box_head[2] | 4); - box_head[3] = crypto.calcCrc8(box_head, 3); - this.crc8 = box_head[3]; - box_head[4] = ((this.senderId << 5) | this.senderType); - box_head[5] = ((this.receiverId << 5) | this.receiverType); - box_head[6] = this.encodeSequenceNo()[0]; - box_head[7] = this.encodeSequenceNo()[1]; - box_head[8] = ((this.cmdType << 7) | (this.isNeedAck << 5) | this.encryptType); - box_head[9] = this.cmdSet; - box_head[10] = this.cmdId; - - if (this.data != null) { - this.data.forEach((e, i) => { - box_head[i + 11] = e - }); - } - - let endOfBuffer = this.length - 2; - let crcs = crypto.calcCrc16(box_head, endOfBuffer) - this.crc16 = crcs; - box_head[this.length - 2] = (crcs & 255); - box_head[this.length - 1] = ((65280 & crcs) >> 8); - - this.buffer = box_head; - } - - unpack(buffer, request) { - if (buffer != null && buffer.length >= 13) { - this.buffer = buffer; - this.sof = buffer[0]; - - let VL = this.decodeBytes(1, 2); - this.version = VL >> 10; - this.length = VL & 1023; - //if more length then possibly dumb ack first - //try next packet instead - /*if(buffer.length > this.length && buffer[this.length] == 0x55) { - //console.info("multiple responses") - return this.unpack(buffer.subarray(this.length)); - }*/ - this.crc8 = this.buffer[3]; - this.senderId = parseInt(this.buffer[4]) >> 5; - this.senderType = parseInt(this.buffer[4]) & 31; - this.receiverId = parseInt(this.buffer[5]) >> 5; - this.receiverType = parseInt(this.buffer[5]) & 31; - this.seq = this.decodeBytes(6, 2); - this.cmdType = parseInt(this.buffer[8]) >> 7; - this.isNeedAck = (parseInt(this.buffer[8]) >> 5) & 3; - this.encryptType = parseInt(this.buffer[8]) & 7; - this.cmdSet = parseInt(this.buffer[9]); - this.cmdId = parseInt(this.buffer[10]); - - //this.seq matching _would_ be proper - //if the devices didn't sometimes respond with the wrong seq! - if(request && (this.cmdId !== request.cmdId || this.cmdSet !== request.cmdSet)) { - if(buffer.length > this.length && buffer[this.length] == 0x55) { - //console.log("multiple responses", this.cmdId) - return this.unpack(buffer.subarray(this.length), request) - } - else { - throw "unexpected response seq and no more packets to try" - } - } - - if (this.cmdType == 1) { - this.ccode = parseInt(this.buffer[11]); - } - - let dataLen = this.length - 11 - 2; - if (dataLen > 0) { - this.data = new Uint8Array(dataLen); - this.data.forEach((e, i) => { - this.data[i] = this.buffer[i + 11] - }); - } - this.crc16 = this.decodeBytes(this.length-2, 2); //BytesUtil.getInt(buffer, buffer.length - 2, 2); - } - } - - resetData() { - this.isNeedCcode = true; - this.buffer = null; - this.sof = 0; - this.version = 1; - this.length = 0; - this.crc8 = 0; - this.senderId = 0; - this.senderType = 0; - this.receiverId = 0; - this.receiverType = 0; - this.seq = 0; - this.cmdType = 0; - this.isNeedAck = 0; - this.encryptType = 0; - this.cmdSet = 0; - this.cmdId = 0; - this.ccode = 0; - this.data = null; - this.crc16 = 0; - } - - toString() { - if (this.buffer != null) { - return Array.prototype.map.call(new Uint8Array(this.buffer), x => ('00' + x.toString(16)).slice(-2)).join(''); - } else return ""; - } - - toBuffer() { - return this.buffer; - } - - toObject() { - return { - sof: this.sof, - version: this.version, - ccode: this.ccode, - cmdId: this.cmdId, - cmdSet: this.cmdSet, - cmdType: this.cmdType, - crc16: this.crc16, - crc8: this.crc8, - encryptType: this.encryptType, - isNeedAck: this.isNeedAck, - length: this.length, - receiverId: this.receiverId, - receiverType: this.receiverType, - senderId: this.senderId, - senderType: this.senderType, - seq: this.seq, - data: this.data, - buffer: this.buffer - - } - } - - -} - -module.exports = pack; diff --git a/src/utils.js b/src/utils.js index 05cdcac..e96f866 100644 --- a/src/utils.js +++ b/src/utils.js @@ -11,9 +11,14 @@ const rl = readline.createInterface({ output: process.stdout }); -const packer = require('./packer') +const Session = require('./duml/Session') +const Packer = require('./duml/Packer') -let seq = 1 +const dumlSession = new Session(); + +// dumlSession.setUnmatchedListener((pack) => { +// console.log("unmatched", pack.toString()) +// }) function sleep(ms) { return new Promise(resolve => { @@ -34,38 +39,21 @@ async function confirm() { }) } - -async function sendAndReceive(messageToSend, port, wait, timeout) { - if(!timeout) - timeout = 5 - port.write(messageToSend); - if(!wait) { - return; - } - let response = '' - var i = 0; - while(i < timeout) { - i++ - response = port.read() // BLOCKING, PERHAPS WITH TIMEOUT EXCEPTION; - if(response != null) { - //in case the other end is spammy and our actual response hasn't arrived yet - await sleep(1000) - const additional = port.read() - if(additional) { - response = Buffer.concat([response, additional]) - } - break - } - await sleep(1000) +function initSerialReceiver(port) { + try { + port.on('data', (data) => { + // console.log(data) + dumlSession.receive(data) + }) + } catch (error) { + console.log(error) } - return response } - const talk = function(port, cmd, wait, timeout) { return new Promise((resolve, reject) => { - const pack = new packer() + const pack = new Packer() pack.senderType = 10 pack.senderId = 1 pack.cmdType = 0 @@ -84,31 +72,34 @@ const talk = function(port, cmd, wait, timeout) { pack.timeOut = 3000 - pack.seq = seq - seq++ + pack.seq = dumlSession.lastSeenSeq + 1; + try { pack.pack(); - logInfo("request", Buffer.from(pack.toBuffer()).toString("hex")) + logInfo("request", Buffer.from(pack.toUint8Array()).toString("hex")) //console.info("request", pack.toObject()) //console.info("raw request", pack.toString()) - sendAndReceive(pack.toBuffer(), port, wait, timeout).then((res) => { - if(!wait) { - resolve() - return - } - if(res) { - //console.info("raw response", res.toString("hex")) - const unpacker = new packer(); - unpacker.unpack(res, pack) - //console.info("response", unpacker.toObject()) - logInfo("response", unpacker.buffer.slice(0, unpacker.length).toString("hex")) - resolve(unpacker.toObject()) + + // let the session know what we're sending + dumlSession.transmit(pack, (wait ? 1000 : -1)).then((response) => { + if(!response && wait) { + return reject("no response") + } + + if(!response && !wait) { + return resolve() } - else { - reject("no response") + + if(response) { + logInfo("response", response.toString()) + return resolve(response.toObject()) } - }) + }).catch(reject) + + // send the message + port.write(pack.toUint8Array()); + } catch (error) { reject(error) @@ -186,4 +177,4 @@ module.exports.talk = talk module.exports.logInfo = logInfo module.exports.wrapSentry = wrapSentry module.exports.downloadFile = downloadFile - +module.exports.initSerialReceiver = initSerialReceiver