UNPKG

netkitty

Version:
935 lines (914 loc) 45.7 kB
import {ProtocolJSONSchema} from '../../schema/ProtocolJSONSchema' import {BaseHeader} from '../abstracts/BaseHeader' import {BufferToUInt16, BufferToUInt32, BufferToUInt64, BufferToUInt8} from '../../helper/BufferToNumber' import {UInt16ToBuffer, UInt32ToBuffer, UInt64ToBuffer, UInt8ToBuffer} from '../../helper/NumberToBuffer' import {IPv6ToBuffer} from '../../helper/IPToBuffer' import {CodecModule} from '../types/CodecModule' import {StringContentEncodingEnum} from '../lib/StringContentEncodingEnum' import {UInt64ToHex} from '../../helper/NumberToHex' import {HexToUInt64} from '../../helper/HexToNumber' enum TCPOption { End_of_Option_List = 'EOL', No_Operation = 'NOP', Maximum_Segment_Size = 'MSS', Window_Scale = 'Window-Scale', Selective_Acknowledgment_Permitted = 'SACK-Permitted', Selective_Acknowledgment = 'SACK', Timestamp = 'TS', User_Timeout = 'UTO', TCP_Authentication_Option = 'TCP-AO' } type OPTION_EOL = { option: TCPOption.End_of_Option_List } type OPTION_NOP = { option: TCPOption.No_Operation } type OPTION_MSS = { option: TCPOption.Maximum_Segment_Size mss: number } type OPTION_WINDOW_SCALE = { option: TCPOption.Window_Scale shift: number } type OPTION_SACK_PERMITTED = { option: TCPOption.Selective_Acknowledgment_Permitted } type OPTION_SACK = { option: TCPOption.Selective_Acknowledgment blocks: string[] } type OPTION_TS = { option: TCPOption.Timestamp tsval: number tsecr: number } type OPTION_UTO = { option: TCPOption.User_Timeout timeout: number granularity: number } type OPTION_TCP_AO = { option: TCPOption.TCP_Authentication_Option data: string } type OPTION_DEFAULT = { kind: number data: string } type OptionItem = OPTION_EOL | OPTION_NOP | OPTION_MSS | OPTION_WINDOW_SCALE | OPTION_SACK_PERMITTED | OPTION_SACK | OPTION_TS | OPTION_UTO | OPTION_TCP_AO | OPTION_DEFAULT export class TCP extends BaseHeader { /** * Calculate TCP Checksum * @param tcpHeaderBuffer * @protected */ protected calculateTCPChecksum(tcpHeaderBuffer: Buffer): number { const tcpHeaderLength: number = tcpHeaderBuffer.length const ipVersion: number = this.prevCodecModule.instance.version.getValue() let pseudoHeaderBuffer: Buffer = Buffer.from([]) const sourceIp: string = this.prevCodecModule.instance.sip.getValue() const destinationIp: string = this.prevCodecModule.instance.dip.getValue() if (ipVersion === 4) { //4 Bytes const sourceIPv4Buffer: Buffer = Buffer.from(sourceIp.split('.').map((value: string): number => parseInt(value))) const destinationIPv4Buffer: Buffer = Buffer.from(destinationIp.split('.').map((value: string): number => parseInt(value))) //IPv4 Pseudo header pseudoHeaderBuffer = Buffer.concat([ sourceIPv4Buffer, destinationIPv4Buffer, UInt8ToBuffer(0), //Reserved field UInt8ToBuffer(6), //Protocol type (TCP = 6) Buffer.from([(tcpHeaderLength >> 8) & 0xFF]), Buffer.from([tcpHeaderLength & 0xFF]) ]) } else if (ipVersion === 6) { //16 Bytes const sourceIPv6Buffer: Buffer = IPv6ToBuffer(sourceIp) const destinationIPv6Buffer: Buffer = IPv6ToBuffer(destinationIp) //IPv6 Pseudo header pseudoHeaderBuffer = Buffer.concat([ sourceIPv6Buffer, destinationIPv6Buffer, UInt8ToBuffer(0), //Reserved field UInt8ToBuffer(0), //Reserved field UInt8ToBuffer(0), //Reserved field UInt8ToBuffer(6), //Protocol type (TCP = 6) Buffer.from([(tcpHeaderLength >> 8) & 0xFF]), Buffer.from([tcpHeaderLength & 0xFF]) ]) } else { return 0 } const dataBuffer: Buffer = Buffer.concat([pseudoHeaderBuffer, tcpHeaderBuffer]) const data: Uint8Array = Uint8Array.from(dataBuffer) let sum: number = 0 for (let i: number = 0; i < data.length; i += 2) sum += (data[i] << 8) + (data[i + 1] || 0) while (sum >>> 16) sum = (sum & 0xFFFF) + (sum >>> 16) return (~sum) & 0xFFFF } public SCHEMA: ProtocolJSONSchema = { type: 'object', properties: { srcport: { type: 'integer', label: 'Source Port', minimum: 0, maximum: 65535, decode: (): void => { this.instance.srcport.setValue(BufferToUInt16(this.readBytes(0, 2))) }, encode: (): void => { let srcport: number = this.instance.srcport.getValue(0, (nodePath: string): void => this.recordError(nodePath, 'Not Found')) if (srcport > 65535) { this.recordError(this.instance.srcport.getPath(), 'Maximum value is 65535') srcport = 65535 } if (srcport < 0) { this.recordError(this.instance.srcport.getPath(), 'Minimum value is 0') srcport = 0 } this.instance.srcport.setValue(srcport) this.writeBytes(0, UInt16ToBuffer(srcport)) } }, dstport: { type: 'integer', label: 'Destination Port', minimum: 0, maximum: 65535, decode: (): void => { this.instance.dstport.setValue(BufferToUInt16(this.readBytes(2, 2))) }, encode: (): void => { let dstport: number = this.instance.dstport.getValue(0, (nodePath: string): void => this.recordError(nodePath, 'Not Found')) if (dstport > 65535) { this.recordError(this.instance.dstport.getPath(), 'Maximum value is 65535') dstport = 65535 } if (dstport < 0) { this.recordError(this.instance.dstport.getPath(), 'Minimum value is 0') dstport = 0 } this.instance.dstport.setValue(dstport) this.writeBytes(2, UInt16ToBuffer(dstport)) } }, seq: { type: 'integer', label: 'Sequence Number', minimum: 0, maximum: 4294967295, decode: (): void => { this.instance.seq.setValue(BufferToUInt32(this.readBytes(4, 4))) }, encode: (): void => { let seqNum: number = this.instance.seq.getValue(0) if (seqNum > 4294967295) { this.recordError(this.instance.seq.getPath(), 'Maximum value is 4294967295') seqNum = 4294967295 } if (seqNum < 0) { this.recordError(this.instance.seq.getPath(), 'Minimum value is 0') seqNum = 0 } this.instance.seq.setValue(seqNum) this.writeBytes(4, UInt32ToBuffer(seqNum)) } }, ack: { type: 'integer', label: 'Acknowledgment Number', minimum: 0, maximum: 4294967295, decode: (): void => { this.instance.ack.setValue(BufferToUInt32(this.readBytes(8, 4))) }, encode: (): void => { let ackNum: number = this.instance.ack.getValue(0) if (ackNum > 4294967295) { this.recordError(this.instance.ack.getPath(), 'Maximum value is 4294967295') ackNum = 4294967295 } if (ackNum < 0) { this.recordError(this.instance.ack.getPath(), 'Minimum value is 0') ackNum = 0 } this.instance.ack.setValue(ackNum) this.writeBytes(8, UInt32ToBuffer(ackNum)) } }, hdrLen: { type: 'integer', label: 'Header Length', minimum: 0, maximum: 60, decode: (): void => { this.instance.hdrLen.setValue(this.readBits(12, 1, 0, 4) * 4) }, encode: (): void => { let hdrLen: number = this.instance.hdrLen.getValue(0) if (hdrLen) { this.instance.hdrLen.setValue(hdrLen) this.writeBits(12, 1, 0, 4, hdrLen ? Math.floor(hdrLen / 4) : 0) } else { this.addPostSelfEncodeHandler((): void => { this.instance.hdrLen.setValue(Math.floor(this.length / 4) * 4) this.writeBits(12, 1, 0, 4, Math.floor(this.length / 4)) }, 1) } } }, flags: { type: 'object', label: 'Flags', properties: { res: { type: 'integer', label: 'Reserved', minimum: 0, maximum: 7, decode: (): void => { this.instance.flags.res.setValue(this.readBits(12, 1, 4, 3)) }, encode: (): void => { let reserved: number = this.instance.flags.res.getValue(0) if (reserved > 7) { this.recordError(this.instance.flags.res.getPath(), 'Maximum value is 7') reserved = 7 } if (reserved < 0) { this.recordError(this.instance.flags.res.getPath(), 'Minimum value is 0') reserved = 0 } this.instance.flags.res.setValue(reserved) this.writeBits(12, 1, 4, 3, reserved) } }, ae: { type: 'boolean', label: 'Accurate ECN', decode: (): void => { this.instance.flags.ae.setValue(!!this.readBits(12, 1, 7, 1)) }, encode: (): void => { const accurateECN: boolean = !!this.instance.flags.ae.getValue() this.instance.flags.ae.setValue(accurateECN) this.writeBits(12, 1, 7, 1, accurateECN ? 1 : 0) } }, cwr: { type: 'boolean', label: 'Congestion Window Reduced', decode: (): void => { this.instance.flags.cwr.setValue(!!this.readBits(13, 1, 0, 1)) }, encode: (): void => { const congestionWindowReduced: boolean = !!this.instance.flags.cwr.getValue() this.instance.flags.cwr.setValue(congestionWindowReduced) this.writeBits(13, 1, 0, 1, congestionWindowReduced ? 1 : 0) } }, ece: { type: 'boolean', label: 'ECN-Echo', decode: (): void => { this.instance.flags.ece.setValue(!!this.readBits(13, 1, 1, 1)) }, encode: (): void => { const ECNEcho: boolean = !!this.instance.flags.ece.getValue() this.instance.flags.ece.setValue(ECNEcho) this.writeBits(13, 1, 1, 1, ECNEcho ? 1 : 0) } }, urg: { type: 'boolean', label: 'Urgent', decode: (): void => { this.instance.flags.urg.setValue(!!this.readBits(13, 1, 2, 1)) }, encode: (): void => { const urgent: boolean = !!this.instance.flags.urg.getValue() this.instance.flags.urg.setValue(urgent) this.writeBits(13, 1, 2, 1, urgent ? 1 : 0) } }, ack: { type: 'boolean', label: 'Acknowledgment', decode: (): void => { this.instance.flags.ack.setValue(!!this.readBits(13, 1, 3, 1)) }, encode: (): void => { const acknowledgment: boolean = !!this.instance.flags.ack.getValue() this.instance.flags.ack.setValue(acknowledgment) this.writeBits(13, 1, 3, 1, acknowledgment ? 1 : 0) } }, push: { type: 'boolean', label: 'Push', decode: (): void => { this.instance.flags.push.setValue(!!this.readBits(13, 1, 4, 1)) }, encode: (): void => { const push: boolean = !!this.instance.flags.push.getValue() this.instance.flags.push.setValue(push) this.writeBits(13, 1, 4, 1, push ? 1 : 0) } }, rst: { type: 'boolean', label: 'Reset', decode: (): void => { this.instance.flags.rst.setValue(!!this.readBits(13, 1, 5, 1)) }, encode: (): void => { const reset: boolean = !!this.instance.flags.rst.getValue() this.instance.flags.rst.setValue(reset) this.writeBits(13, 1, 5, 1, reset ? 1 : 0) } }, syn: { type: 'boolean', label: 'Syn', decode: (): void => { this.instance.flags.syn.setValue(!!this.readBits(13, 1, 6, 1)) }, encode: (): void => { const syn: boolean = !!this.instance.flags.syn.getValue() this.instance.flags.syn.setValue(syn) this.writeBits(13, 1, 6, 1, syn ? 1 : 0) } }, fin: { type: 'boolean', label: 'Fin', decode: (): void => { this.instance.flags.fin.setValue(!!this.readBits(13, 1, 7, 1)) }, encode: (): void => { const fin: boolean = !!this.instance.flags.fin.getValue() this.instance.flags.fin.setValue(fin) this.writeBits(13, 1, 7, 1, fin ? 1 : 0) } } } }, window: { type: 'integer', label: 'Window Size', minimum: 0, maximum: 65535, decode: (): void => { this.instance.window.setValue(BufferToUInt16(this.readBytes(14, 2))) }, encode: (): void => { let windowSize: number = this.instance.window.getValue(0) if (windowSize > 65535) { this.recordError(this.instance.window.getPath(), 'Maximum value is 65535') windowSize = 65535 } if (windowSize < 0) { this.recordError(this.instance.window.getPath(), 'Minimum value is 0') windowSize = 0 } this.instance.window.setValue(windowSize) this.writeBytes(14, UInt16ToBuffer(windowSize)) } }, checksum: { type: 'integer', label: 'Checksum', minimum: 0, maximum: 65535, decode: (): void => { this.instance.checksum.setValue(BufferToUInt16(this.readBytes(16, 2))) }, encode: (): void => { let checksum: number = this.instance.checksum.getValue(0) if (checksum) { this.instance.checksum.setValue(checksum) this.writeBytes(16, UInt16ToBuffer(checksum)) } else { this.instance.checksum.setValue(checksum) this.writeBytes(16, UInt16ToBuffer(checksum)) this.addPostPacketEncodeHandler((): void => { //Calculate checksum after packet encoded let startPos: number = 0 let endPos: number = 0 this.codecModules.forEach((codecModule: CodecModule): void => { if (codecModule === this) startPos = codecModule.startPos endPos = codecModule.endPos > endPos ? codecModule.endPos : endPos }) const calcChecksum: number = this.calculateTCPChecksum(this.packet.subarray(startPos, endPos)) this.instance.checksum.setValue(calcChecksum) this.writeBytes(16, UInt16ToBuffer(calcChecksum)) }, 100) } } }, urgPtr: { type: 'integer', label: 'Urgent Pointer', minimum: 0, maximum: 65535, decode: (): void => { this.instance.urgPtr.setValue(BufferToUInt16(this.readBytes(18, 2))) }, encode: (): void => { let urgPtr: number = this.instance.urgPtr.getValue(0) if (urgPtr > 65535) { this.recordError(this.instance.urgPtr.getPath(), 'Maximum value is 65535') urgPtr = 65535 } if (urgPtr < 0) { this.recordError(this.instance.urgPtr.getPath(), 'Minimum value is 0') urgPtr = 0 } this.instance.urgPtr.setValue(urgPtr) this.writeBytes(18, UInt16ToBuffer(urgPtr)) } }, options: { type: 'array', label: 'Options', items: { anyOf: [ //Kind = 0 (End of Option List, EOL) { type: 'object', label: 'End of Option List', properties: { option: { type: 'string', label: 'Option', enum: [TCPOption.End_of_Option_List] } } }, //Kind = 1 (No-Operation, NOP) { type: 'object', label: 'No-Operation', properties: { option: { type: 'string', label: 'Option', enum: [TCPOption.No_Operation] } } }, //Kind = 2 (Maximum Segment Size, MSS) { type: 'object', label: 'Maximum Segment Size', properties: { option: { type: 'string', label: 'Option', enum: [TCPOption.Maximum_Segment_Size] }, mss: { type: 'integer', label: 'MSS', maximum: 65535, minimum: 0 } } }, //Kind = 3 (Window Scale) { type: 'object', label: 'Window Scale', properties: { option: { type: 'string', label: 'Option', enum: [TCPOption.Window_Scale] }, shift: { type: 'integer', label: 'Shift count', minimum: 0, maximum: 14 } } }, //Kind = 4 (Selective Acknowledgment Permitted, SACK-Permitted) { type: 'object', label: 'Selective Acknowledgment Permitted', properties: { option: { type: 'string', label: 'Option', enum: [TCPOption.Selective_Acknowledgment_Permitted] } } }, //Kind = 5 (Selective Acknowledgment, SACK) { type: 'object', label: 'Selective Acknowledgment', properties: { option: { type: 'string', label: 'Option', enum: [TCPOption.Selective_Acknowledgment] }, blocks: { type: 'array', label: 'SACK Blocks', items: { type: 'string', maxLength: 16, minLength: 16, contentEncoding: StringContentEncodingEnum.HEX } } } }, //Kind = 8 (Timestamp, TS) { type: 'object', label: 'Timestamp', properties: { option: { type: 'string', label: 'Option', enum: [TCPOption.Timestamp] }, tsval: { type: 'integer', label: 'TSval', minimum: 0, maximum: 4294967295 }, tsecr: { type: 'integer', label: 'TSecr', minimum: 0, maximum: 4294967295 } } }, //Kind = 28 (User Timeout, UTO) { type: 'object', label: 'User Timeout', properties: { option: { type: 'string', label: 'Option', enum: [TCPOption.User_Timeout] }, timeout: { type: 'integer', label: 'Timeout', minimum: 0, maximum: 65535 }, granularity: { type: 'integer', label: 'Granularity', enum: [0, 1] } } }, //Kind = 29 (TCP Authentication Option, TCP-AO) { type: 'object', label: 'TCP Authentication Option', properties: { option: { type: 'string', label: 'Option', enum: [TCPOption.TCP_Authentication_Option] }, data: { type: 'string', label: 'Data', contentEncoding: StringContentEncodingEnum.HEX } } }, //Default { type: 'object', label: 'Default', properties: { kind: { type: 'integer', label: 'Kind' }, data: { type: 'string', contentEncoding: StringContentEncodingEnum.HEX, label: 'Data' } } } ] }, decode: (): void => { const hdrLen: number = this.instance.hdrLen.getValue() const optionsLength: number = hdrLen - this.length if (!optionsLength) return let optionOffset: number = this.length const options: OptionItem[] = [] let index: number = 0 while (optionOffset < hdrLen) { const kind: number = BufferToUInt8(this.readBytes(optionOffset, 1)) optionOffset += 1 switch (kind) { //Kind = 0 (End of Option List, EOL) case 0: { options.push({ option: TCPOption.End_of_Option_List }) } break //Kind = 1 (No-Operation, NOP) case 1: { options.push({ option: TCPOption.No_Operation }) } break //Kind = 2 (Maximum Segment Size, MSS) case 2: { let length: number = BufferToUInt8(this.readBytes(optionOffset, 1)) if (length !== 4) this.recordError(this.instance.options.getPath(index), 'MSS option TLV length should be 4') optionOffset += 1 let value: number = BufferToUInt16(this.readBytes(optionOffset, 2)) optionOffset += 2 options.push({ option: TCPOption.Maximum_Segment_Size, mss: value }) } break //Kind = 3 (Window Scale) case 3: { let length: number = BufferToUInt8(this.readBytes(optionOffset, 1)) if (length !== 3) this.recordError(this.instance.options.getPath(index), 'Window Scale option TLV length should be 3') optionOffset += 1 let value: number = BufferToUInt8(this.readBytes(optionOffset, 1)) optionOffset += 1 if (value < 0 || value > 14) this.recordError(this.instance.options.getPath(index), 'Window Scale option TLV value should between 0 and 14') options.push({ option: TCPOption.Window_Scale, shift: value }) } break //Kind = 4 (Selective Acknowledgment Permitted, SACK-Permitted) case 4: { let length: number = BufferToUInt8(this.readBytes(optionOffset, 1)) optionOffset += 1 if (length !== 2) this.recordError(this.instance.options.getPath(index), 'SACK-Permitted option TLV length should be 2') options.push({ option: TCPOption.Selective_Acknowledgment_Permitted }) } break //Kind = 5 (Selective Acknowledgment, SACK) case 5: { let length: number = BufferToUInt8(this.readBytes(optionOffset, 1)) optionOffset += 1 let sackBlockTotalLength: number = length - 2 if (sackBlockTotalLength < 0) { this.recordError(this.instance.options.getPath(index), 'SACK option block count should not less than 0') sackBlockTotalLength = 0 } let sackBlockCount: number = sackBlockTotalLength ? Math.floor(sackBlockTotalLength / 8) : 0 const sackBlocks: string[] = [] while (sackBlockCount > 0) { sackBlocks.push(UInt64ToHex(BigInt(`0x${BufferToUInt64(this.readBytes(optionOffset, 8)).toString(16)}`))) optionOffset += 8 sackBlockCount -= 1 } options.push({ option: TCPOption.Selective_Acknowledgment, blocks: sackBlocks }) } break //Kind = 8 (Timestamp, TS) case 8: { let length: number = BufferToUInt8(this.readBytes(optionOffset, 1)) optionOffset += 1 if (length !== 10) this.recordError(this.instance.options.getPath(index), 'Timestamp option TLV length should be 10') let tsval: number = BufferToUInt32(this.readBytes(optionOffset, 4)) optionOffset += 4 let tsecr: number = BufferToUInt32(this.readBytes(optionOffset, 4)) optionOffset += 4 options.push({ option: TCPOption.Timestamp, tsval: tsval, tsecr: tsecr }) } break //Kind = 28 (User Timeout, UTO) case 28: { let length: number = BufferToUInt8(this.readBytes(optionOffset, 1)) optionOffset += 1 if (length !== 5) this.recordError(this.instance.options.getPath(index), 'UTO option TLV length should be 5') let timeout: number = BufferToUInt16(this.readBytes(optionOffset, 2)) optionOffset += 2 let granularity: number = BufferToUInt8(this.readBytes(optionOffset, 1)) optionOffset += 1 options.push({ option: TCPOption.User_Timeout, timeout: timeout, granularity: granularity }) } break //Kind = 29 (TCP Authentication Option, TCP-AO) case 29: { let length: number = BufferToUInt8(this.readBytes(optionOffset, 1)) optionOffset += 1 let dataLength: number = length - 2 if (dataLength < 0) { this.recordError(this.instance.options.getPath(index), 'TCP Authentication Option TLV length should not less than 2') dataLength = 0 } let data: Buffer if (dataLength) { data = this.readBytes(optionOffset, dataLength) optionOffset += dataLength } else { data = Buffer.from([]) } options.push({ option: TCPOption.TCP_Authentication_Option, data: data.toString('hex') }) } break default: { let length: number = BufferToUInt8(this.readBytes(optionOffset, 1)) let dataLength: number = length - 2 if (dataLength < 0) { this.recordError(this.instance.options.getPath(index), 'Option TLV length should not less than 2') dataLength = 0 } let data: Buffer if (dataLength) { data = this.readBytes(optionOffset, dataLength) optionOffset += dataLength } else { data = Buffer.from([]) } options.push({ kind: kind, data: data.toString('hex') }) } } index += 1 } this.instance.options.setValue(options) }, encode: (): void => { const options: (OptionItem[]) | undefined = this.instance.options.getValue() if (!options) return let optionOffset: number = this.length options.forEach((optionItem: OptionItem): void => { const namedOptionItem: OPTION_EOL | OPTION_NOP | OPTION_MSS | OPTION_WINDOW_SCALE | OPTION_SACK_PERMITTED | OPTION_SACK | OPTION_TS | OPTION_UTO | OPTION_TCP_AO = optionItem as any switch (namedOptionItem.option) { case TCPOption.End_of_Option_List: { const optionEOL: OPTION_EOL = optionItem as OPTION_EOL this.writeBytes(optionOffset, UInt8ToBuffer(0)) optionOffset += 1 return } case TCPOption.No_Operation: { const optionNOP: OPTION_NOP = optionItem as OPTION_NOP this.writeBytes(optionOffset, UInt8ToBuffer(1)) optionOffset += 1 return } case TCPOption.Maximum_Segment_Size: { const optionMSS: OPTION_MSS = optionItem as OPTION_MSS const mssBuffer: Buffer = Buffer.concat([ UInt8ToBuffer(2), UInt8ToBuffer(4), UInt16ToBuffer(optionMSS.mss) ]) this.writeBytes(optionOffset, mssBuffer) optionOffset += mssBuffer.length return } case TCPOption.Window_Scale: { const optionWS: OPTION_WINDOW_SCALE = optionItem as OPTION_WINDOW_SCALE const wsBuffer: Buffer = Buffer.concat([ UInt8ToBuffer(3), UInt8ToBuffer(3), UInt8ToBuffer(optionWS.shift) ]) this.writeBytes(optionOffset, wsBuffer) optionOffset += wsBuffer.length return } case TCPOption.Selective_Acknowledgment_Permitted: { const optionSackPermitted: OPTION_SACK_PERMITTED = optionItem as OPTION_SACK_PERMITTED const sackPermittedBuffer: Buffer = Buffer.concat([ UInt8ToBuffer(4), UInt8ToBuffer(2) ]) this.writeBytes(optionOffset, sackPermittedBuffer) optionOffset += sackPermittedBuffer.length return } case TCPOption.Selective_Acknowledgment: { const optionSack: OPTION_SACK = optionItem as OPTION_SACK const kindBuffer: Buffer = UInt8ToBuffer(5) let length: number = 2 let blocksBuffer: Buffer = Buffer.from([]) optionSack.blocks.forEach((block: string): void => { blocksBuffer = Buffer.concat([blocksBuffer, UInt64ToBuffer(HexToUInt64(block))]) }) const sackBuffer: Buffer = Buffer.concat([kindBuffer, UInt8ToBuffer(length + blocksBuffer.length), blocksBuffer]) this.writeBytes(optionOffset, sackBuffer) optionOffset += sackBuffer.length return } case TCPOption.Timestamp: { const optionTS: OPTION_TS = optionItem as OPTION_TS const tsBuffer: Buffer = Buffer.concat([ UInt8ToBuffer(8), UInt8ToBuffer(10), UInt32ToBuffer(optionTS.tsval ? optionTS.tsval : 0), UInt32ToBuffer(optionTS.tsecr ? optionTS.tsecr : 0) ]) this.writeBytes(optionOffset, tsBuffer) optionOffset += tsBuffer.length return } case TCPOption.User_Timeout: { const optionUTO: OPTION_UTO = optionItem as OPTION_UTO const utoBuffer: Buffer = Buffer.concat([ UInt8ToBuffer(28), UInt8ToBuffer(5), UInt16ToBuffer(optionUTO.timeout ? optionUTO.timeout : 0), UInt8ToBuffer(optionUTO.granularity ? optionUTO.granularity : 0) ]) this.writeBytes(optionOffset, utoBuffer) optionOffset += utoBuffer.length return } case TCPOption.TCP_Authentication_Option: { const optionTcpAo: OPTION_TCP_AO = optionItem as OPTION_TCP_AO const kindBuffer: Buffer = UInt8ToBuffer(29) const dataBuffer: Buffer = Buffer.from(optionTcpAo.data, 'hex') const lengthBuffer: Buffer = UInt8ToBuffer(dataBuffer.length + 2) const tcpAoBuffer: Buffer = Buffer.concat([kindBuffer, lengthBuffer, dataBuffer]) this.writeBytes(optionOffset, tcpAoBuffer) optionOffset += tcpAoBuffer.length return } default: { const defaultOptionItem: OPTION_DEFAULT = optionItem as OPTION_DEFAULT const kind: number = defaultOptionItem.kind const hexBuffer: Buffer = Buffer.from(defaultOptionItem.data.toString(), 'hex') const length: number = hexBuffer.length + 2 const defaultOptionBuffer: Buffer = Buffer.concat([UInt8ToBuffer(kind), UInt8ToBuffer(length), hexBuffer]) this.writeBytes(optionOffset, defaultOptionBuffer) optionOffset += defaultOptionBuffer.length } } }) if (this.length > 60) return this.recordError(this.instance.options.getPath(), 'Options too large') const headerBitLength: number = this.length * 8 if (headerBitLength % 4) { //Add Padding this.writeBytes(this.length, Buffer.from([0x00])) } } } } } public readonly id: string = 'tcp' public readonly name: string = 'Transmission Control Protocol' public readonly nickname: string = 'TCP' public match(): boolean { if (!this.prevCodecModule) return false if (this.prevCodecModule.instance.protocol.getValue() === 0x06) return true return false } }