diff --git a/src/index.js b/src/index.js index 9d38b48..ffae389 100644 --- a/src/index.js +++ b/src/index.js @@ -19,32 +19,89 @@ function logDataStream(data){ return strs.map((str) => chunkString(str, 2).join(' ')).join('\n'); } -function TLV(buf) { - this.type = buf.slice(0, 2); - this.len = buf.slice(2, 4).readInt16BE(0) - this.payload = buf.slice(4, 4 + this.len); - this.toString = () => `TLV(${this.type.toString('hex')}, ${this.len}, ${this.payload.toString('ascii')})`; +class TLV { + static fromBuffer(buf) { + const type = buf.slice(0, 2).readInt16BE(0); + const len = buf.slice(2, 4).readInt16BE(0) + const payload = buf.slice(4, 4 + len); + + return new TLV(type, payload); + } + + constructor(type, payload) { + this.type = type; + this.len = payload.length; + this.payload = payload; + } + + toString() { + return `TLV(${this.type}, ${this.len}, ${this.payload.toString('ascii')})`; + } } -function SNAC(buf) { - this.family = buf.slice(0,2).readInt16BE(0); - this.service = buf.slice(2,4).readInt16BE(0); - this.flags = buf.slice(4, 6); - this.requestID = buf.slice(6, 10).readInt32BE(0); - this.payload = new TLV(buf.slice(10)); - this.toString = () => `SNAC(${this.family.toString(16)},${this.service.toString(16)}) #${this.requestID}\n ${this.payload}`; +class SNAC { + static fromBuffer(buf, payloadLength = 0) { + const family = buf.slice(0,2).readInt16BE(0); + const service = buf.slice(2,4).readInt16BE(0); + const flags = buf.slice(4, 6); + const requestID = buf.slice(6, 10).readInt32BE(0); + const payload = []; // SNACs can have multiple TLVs + + let payloadIdx = 10; + let cb = 0, cbLimit = 10; //circuit breaker + while (payloadIdx < payloadLength && cb < cbLimit) { + const tlv = TLV.fromBuffer(buf.slice(payloadIdx)); + payload.push(tlv); + payloadIdx += tlv.len + 4; // 4 bytes for TLV type + payload length + cb++; + } + if (cb === cbLimit) { + console.error('Application error, cb limit reached'); + process.exit(1); + } + + return new SNAC(family, service, flags, requestID, payload); + } + + constructor(family, service, flags, requestID, payload) { + this.family = family; + this.service = service; + this.flags = flags; + this.requestID = requestID; + this.payload = payload; + } + + toString() { + return `SNAC(${this.family.toString(16)},${this.service.toString(16)}) #${this.requestID}\n ${this.payload}`; + } } -function FLAP(buf) { - assert.equal(buf[0], 0x2a, 'Expected 0x2a FLAP header'); - this.channel = buf[1]; - this.datagramNumber = buf.slice(2,4).readInt16BE(0); - this.payloadLength = buf.slice(4, 6).readInt16BE(0); - this.payload = buf.slice(6, 6 + this.payloadLength); - this.toString = () => `ch:${this.channel}, dn: ${this.datagramNumber}, len: ${this.payloadLength}, payload:\n ${ this.payload instanceof SNAC ? this.payload.toString() : logDataStream(this.payload).split('\n').join('\n ')}`; +class FLAP { + static fromBuffer(buf) { + assert.equal(buf[0], 0x2a, 'Expected 0x2a FLAP header'); + const channel = parseInt(buf[1], 16); + const datagramNumber = buf.slice(2,4).readInt16BE(0); + const payloadLength = buf.slice(4, 6).readInt16BE(0); + const payload = buf.slice(6, 6 + payloadLength); - if (this.channel === 2) { - this.payload = new SNAC(this.payload); + return new FLAP(channel, datagramNumber, payload) + } + + constructor(channel, datagramNumber, payload) { + this.channel = channel; + this.datagramNumber = datagramNumber; + this.payload = payload; + this.payloadLength = this.payload.length; + + if (channel === 2) { + this.payload = SNAC.fromBuffer(this.payload, this.payloadLength); + } + } + + toString() { + const hasSnac = this.payload instanceof SNAC; + const payload = hasSnac ? this.payload.toString() : logDataStream(this.payload).split('\n').join('\n '); + return `ch:${this.channel}, dn: ${this.datagramNumber}, len: ${this.payloadLength}, payload:\n ${payload}` } } @@ -57,7 +114,7 @@ const server = net.createServer((socket) => { }); socket.on('data', (data) => { - const flap = new FLAP(Buffer.from(data, 'hex')); + const flap = FLAP.fromBuffer(Buffer.from(data, 'hex')); console.log('RECV', flap.toString()); });