netkitty
Version:
Network tools kit
340 lines (339 loc) • 16.8 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.IPv4 = void 0;
const BaseHeader_1 = require("../abstracts/BaseHeader");
const NumberToHex_1 = require("../../helper/NumberToHex");
const StringContentEncodingEnum_1 = require("../lib/StringContentEncodingEnum");
const BufferToNumber_1 = require("../../helper/BufferToNumber");
const NumberToBuffer_1 = require("../../helper/NumberToBuffer");
const BufferToHex_1 = require("../../helper/BufferToHex");
const IPToBuffer_1 = require("../../helper/IPToBuffer");
const BufferToIP_1 = require("../../helper/BufferToIP");
const HexToBuffer_1 = require("../../helper/HexToBuffer");
class IPv4 extends BaseHeader_1.BaseHeader {
constructor() {
super(...arguments);
this.SCHEMA = {
type: 'object',
properties: {
version: {
type: 'integer',
label: 'Version',
minimum: 0,
maximum: 15,
decode: () => {
this.instance.version.setValue(this.readBits(0, 1, 0, 4));
if (this.instance.version.getValue() !== 4)
this.recordError(this.instance.version.getPath(), 'IPv4 version should be 4');
},
encode: () => {
let version = this.instance.version.getValue(4);
version = version > 15 ? 15 : version;
version = version < 0 ? 0 : version;
if (version !== 4)
this.recordError(this.instance.version.getPath(), 'IPv4 version should be 4');
this.instance.version.setValue(version);
this.writeBits(0, 1, 0, 4, version);
}
},
hdrLen: {
type: 'integer',
label: 'Header Length',
minimum: 20,
maximum: 60,
decode: () => {
this.instance.hdrLen.setValue(this.readBits(0, 1, 4, 4) * 4);
},
encode: () => {
let headerLength = this.instance.hdrLen.getValue(0);
if (headerLength)
headerLength = headerLength ? Math.floor(headerLength / 4) : 0;
this.writeBits(0, 1, 4, 4, headerLength);
if (!headerLength)
this.addPostSelfEncodeHandler(() => {
this.instance.hdrLen.setValue(this.length);
this.writeBits(0, 1, 4, 4, Math.floor(this.length / 4));
}, 10);
}
},
dsfield: {
type: 'object',
label: 'Differentiated Services Field',
properties: {
dscp: {
type: 'integer',
minimum: 0,
maximum: 63,
label: 'Differentiated Services Codepoint',
decode: () => {
this.instance.dsfield.dscp.setValue(this.readBits(1, 1, 0, 6));
},
encode: () => {
const dscp = this.instance.dsfield.dscp.getValue(0);
this.instance.dsfield.dscp.setValue(dscp);
this.writeBits(1, 1, 0, 6, dscp);
}
},
ecn: {
type: 'integer',
label: 'Explicit Congestion Notification',
decode: () => {
this.instance.dsfield.ecn.setValue(this.readBits(1, 1, 6, 2));
},
encode: () => {
const ecn = this.instance.dsfield.ecn.getValue(0);
this.instance.dsfield.ecn.setValue(ecn);
this.writeBits(1, 1, 6, 2, ecn);
}
}
}
},
length: {
type: 'integer',
label: 'Total Length',
minimum: 0,
maximum: 65535,
decode: () => {
this.instance.length.setValue((0, BufferToNumber_1.BufferToUInt16)(this.readBytes(2, 2)));
},
encode: () => {
//This field's real value needs down stream codec invoke recode to fill
const length = this.instance.length.getValue(0);
if (length) {
this.writeBytes(2, (0, NumberToBuffer_1.UInt16ToBuffer)(length));
}
else {
this.writeBytes(2, (0, NumberToBuffer_1.UInt16ToBuffer)(length));
this.addPostPacketEncodeHandler(() => {
let startCount = false;
let totalLength = 0;
this.codecModules.forEach((codecModule) => {
if (codecModule === this)
startCount = true;
if (startCount)
totalLength += codecModule.length;
});
this.instance.length.setValue(totalLength);
this.writeBytes(2, (0, NumberToBuffer_1.UInt16ToBuffer)(totalLength));
}, 1);
}
}
},
id: {
type: 'integer',
label: 'Identification',
minimum: 0,
maximum: 65535,
decode: () => {
this.instance.id.setValue((0, BufferToNumber_1.BufferToUInt16)(this.readBytes(4, 2)));
},
encode: () => {
const id = this.instance.id.getValue(0, (nodePath) => this.recordError(nodePath, 'Not Found'));
this.instance.id.setValue(id);
this.writeBytes(4, (0, NumberToBuffer_1.UInt16ToBuffer)(id));
}
},
flags: {
type: 'object',
label: 'Flags',
properties: {
rb: {
type: 'integer',
enum: [0, 1],
label: 'Reserved bit',
decode: () => {
this.instance.flags.rb.setValue(this.readBits(6, 1, 0, 1));
},
encode: () => {
const rb = this.instance.flags.rb.getValue(0, (nodePath) => this.recordError(nodePath, 'Not Found'));
this.instance.flags.rb.setValue(rb);
this.writeBits(6, 1, 0, 1, rb);
}
},
df: {
type: 'integer',
enum: [0, 1],
label: 'Don\'t fragment',
decode: () => {
this.instance.flags.df.setValue(this.readBits(6, 1, 1, 1));
},
encode: () => {
const df = this.instance.flags.df.getValue(1, (nodePath) => this.recordError(nodePath, 'Not Found'));
this.instance.flags.df.setValue(df);
this.writeBits(6, 1, 1, 1, df);
}
},
mf: {
type: 'integer',
enum: [0, 1],
label: 'More fragments',
decode: () => {
this.instance.flags.mf.setValue(this.readBits(6, 1, 2, 1));
},
encode: () => {
const mf = this.instance.flags.mf.getValue(0, (nodePath) => this.recordError(nodePath, 'Not Found'));
this.instance.flags.mf.setValue(mf);
this.writeBits(6, 1, 2, 1, mf);
}
}
}
},
fragOffset: {
type: 'integer',
minimum: 0,
maximum: 8191,
label: 'Fragment Offset',
decode: () => {
this.instance.fragOffset.setValue(this.readBits(6, 2, 3, 13));
},
encode: () => {
const fragOffset = this.instance.fragOffset.getValue(0, (nodePath) => this.recordError(nodePath, 'Not Found'));
this.instance.fragOffset.setValue(fragOffset);
this.writeBits(6, 2, 3, 13, fragOffset);
}
},
ttl: {
type: 'integer',
minimum: 0,
maximum: 255,
label: 'Time to Live',
decode: () => {
this.instance.ttl.setValue((0, BufferToNumber_1.BufferToUInt8)(this.readBytes(8, 1)));
},
encode: () => {
const ttl = this.instance.ttl.getValue(0, (nodePath) => this.recordError(nodePath, 'Not Found'));
this.instance.ttl.setValue(ttl);
this.writeBytes(8, (0, NumberToBuffer_1.UInt8ToBuffer)(ttl));
}
},
protocol: {
type: 'integer',
label: 'Protocol',
minimum: 0,
maximum: 255,
decode: () => {
this.instance.protocol.setValue((0, BufferToNumber_1.BufferToUInt8)(this.readBytes(9, 1)));
},
encode: () => {
const protocol = this.instance.protocol.getValue(0, (nodePath) => this.recordError(nodePath, 'Not Found'));
this.instance.protocol.setValue(protocol);
this.writeBytes(9, (0, NumberToBuffer_1.UInt8ToBuffer)(protocol));
}
},
checksum: {
type: 'integer',
label: 'Header Checksum',
minimum: 0,
maximum: 65535,
decode: () => {
this.instance.checksum.setValue((0, BufferToNumber_1.BufferToUInt16)(this.readBytes(10, 2)));
},
encode: () => {
let checksum = this.instance.checksum.getValue(0);
checksum = checksum > 65535 ? 65535 : checksum;
checksum = checksum < 0 ? 0 : checksum;
if (checksum) {
this.instance.checksum.setValue(checksum);
this.writeBytes(10, (0, NumberToBuffer_1.UInt16ToBuffer)(checksum));
}
else {
this.instance.checksum.setValue(checksum);
this.writeBytes(10, (0, NumberToBuffer_1.UInt16ToBuffer)(checksum));
this.addPostPacketEncodeHandler(() => {
const calcChecksum = this.calculateIPv4Checksum(this.packet.subarray(this.startPos, this.endPos));
this.instance.checksum.setValue(calcChecksum);
this.writeBytes(10, (0, NumberToBuffer_1.UInt16ToBuffer)(calcChecksum));
}, 2);
}
}
},
sip: {
type: 'string',
label: 'Source Address',
minLength: 7,
maxLength: 15,
contentEncoding: StringContentEncodingEnum_1.StringContentEncodingEnum.IPv4,
decode: () => {
const sipBuffer = this.readBytes(12, 4);
this.instance.sip.setValue((0, BufferToIP_1.BufferToIPv4)(sipBuffer));
},
encode: () => {
const sipStr = this.instance.sip.getValue('0.0.0.0', (nodePath) => this.recordError(nodePath, 'Not Found'));
this.instance.sip.setValue(sipStr);
this.writeBytes(12, (0, IPToBuffer_1.IPv4ToBuffer)(sipStr));
}
},
dip: {
type: 'string',
minLength: 7,
maxLength: 15,
label: 'Destination Address',
contentEncoding: StringContentEncodingEnum_1.StringContentEncodingEnum.IPv4,
decode: () => {
const dipBuffer = this.readBytes(16, 4);
this.instance.dip.setValue((0, BufferToIP_1.BufferToIPv4)(dipBuffer));
},
encode: () => {
const dipStr = this.instance.dip.getValue('0.0.0.0', (nodePath) => this.recordError(nodePath, 'Not Found'));
this.instance.dip.setValue(dipStr);
this.writeBytes(16, (0, IPToBuffer_1.IPv4ToBuffer)(dipStr));
}
},
options: {
type: 'string',
label: 'Options',
minLength: 0,
maxLength: 40 * 2,
contentEncoding: StringContentEncodingEnum_1.StringContentEncodingEnum.HEX,
decode: () => {
if (this.length < (this.instance.hdrLen.getValue())) {
this.instance.options.setValue((0, BufferToHex_1.BufferToHex)(this.readBytes(this.length, (this.instance.hdrLen.getValue()) - this.length)));
}
},
encode: () => {
if (!this.instance.options.isUndefined()) {
let optionsBuffer = (0, HexToBuffer_1.HexToBuffer)(this.instance.options.getValue(''));
if (optionsBuffer.length > 40)
optionsBuffer = optionsBuffer.subarray(0, 40);
const estimateHdrLen = this.length + optionsBuffer.length;
if (estimateHdrLen % 4) {
/**
* IPv4 Header should have a length that a multiple of 32 bits
* @see https://learningnetwork.cisco.com/s/question/0D53i00000Kt6hHCAR/padding-field-on-ipv4-header
*/
optionsBuffer = Buffer.concat([optionsBuffer, Buffer.from([0x00])]);
}
this.writeBytes(this.length, optionsBuffer);
}
}
}
}
};
this.id = 'ipv4';
this.name = 'Internet Protocol Version 4';
this.nickname = 'IPv4';
}
/**
* Calculate IPv4 header checksum
* @param headerBuffer
* @protected
*/
calculateIPv4Checksum(headerBuffer) {
const header = Uint8Array.from(headerBuffer);
let sum = 0;
for (let i = 0; i < header.length; i += 2) {
const word = (header[i] << 8) + (header[i + 1] || 0);
sum += word;
}
while (sum >>> 16) {
sum = (sum & 0xFFFF) + (sum >>> 16);
}
return (~sum) & 0xFFFF;
}
match() {
if (!this.prevCodecModule)
return false;
return this.prevCodecModule.instance.etherType.getValue() === (0, NumberToHex_1.UInt16ToHex)(0x0800);
}
}
exports.IPv4 = IPv4;