netkitty
Version:
Network tools kit
935 lines (914 loc) • 45.7 kB
text/typescript
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
}
}