UNPKG

over-the-wire

Version:

Network inspection library for Node

215 lines (191 loc) 5.95 kB
const { compile } = require('struct-compile'); const { OsiModelLayers } = require('./osi'); const { IPProtocolTypes } = require('./enums'); const { checksums } = require('#lib/bindings'); const child = require('./child'); const mixins = require('./mixins'); const { omit } = require('#lib/pick'); const { addField } = require('#lib/struct'); const { TCPHeader } = compile(` //@NE struct TCPHeader { // Source TCP port uint16_t src; // Destination TCP port uint16_t dst; // Sequence number uint32_t seq; // Acknowledgment number uint32_t ack; // @LE uint16_t reservedFlag:4, dataOffset:4, finFlag:1, synFlag:1, rstFlag:1, pshFlag:1, ackFlag:1, urgFlag:1, eceFlag:1, cwrFlag:1; // The size of the receive window, which specifies the number of window size units (by default, bytes) uint16_t windowSize; // The 16-bit checksum field is used for error-checking of the header and data uint16_t checksum; // If the URG flag is set, then this 16-bit field is an offset from the sequence number indicating the last urgent data byte uint16_t urgentPointer; } __attribute__((packed)); `); const { length: baseLength } = TCPHeader.prototype.config; const flagNames = ['reserved', 'cwr', 'ece', 'urg', 'ack', 'psh', 'rst', 'syn', 'fin']; const flagKeys = flagNames.map(e => e + 'Flag'); /** * @typedef {Object} TCPFlags * @property {number} reserved - Reserved flag (0 or 1). * @property {number} cwr - Congestion Window Reduced (0 or 1). * @property {number} ece - ECN-Echo (0 или 1). * @property {number} urg - Urgent pointer field significant (0 или 1). * @property {number} ack - Acknowledgment field significant (0 или 1). * @property {number} psh - Push Function (0 или 1). * @property {number} rst - Reset the connection (0 или 1). * @property {number} syn - Synchronize sequence numbers (0 или 1). * @property {number} fin - No more data from sender (0 или 1). */ /** * TCP protocol layer * @class * @property {number} src - Source TCP port. * @property {number} dst - Destination TCP port. * @property {number} seq - Sequence number. * @property {number} ack - Acknowledgment number. * @property {number} windowSize - The size of the receive window, which specifies the number of window size units (by default, bytes). * @property {number} checksum - The 16-bit checksum field is used for error-checking of the header and data. * @property {number} urgentPointer - If the URG flag is set, then this 16-bit field is an offset from the sequence number indicating the last urgent data byte. * @property {TCPFlags} flags - TCP flags. * @implements {Layer} */ class TCP extends TCPHeader { name = 'TCP'; constructor(data = {}, opts = {}) { super(data); mixins.ctor(this, data, opts); this.length = opts.allocated ?? this.dataOffset * 4; /** * TLV options; * @type {Iterable.<TLVOption>} */ this.options; } static toAlloc = (data) => baseLength + TCP.prototype.optionsLength(data.options); static osi = OsiModelLayers.Transport; osi = OsiModelLayers.Transport; _prepareFlags() { const res = {}; for (const flag of flagNames) { res[flag] = this[flag + 'Flag']; } return res; } /** * Get the flags object. Changes to this object will update the associated buffer. * @type {TCPFlags} */ get flags() { const self = this; return new Proxy(Object.seal(this._prepareFlags()), { get(target, prop, receiver) { return target[prop]; }, set(target, prop, value) { target[prop] = value; const internalKey = prop + 'Flag'; if (self[internalKey] !== undefined) { self[internalKey] = value; } }, }); } set flags(obj) { for (const flag of flagNames) { if (obj[flag] !== undefined) { this[flag + 'Flag'] = obj[flag]; } } } /** * Retrieves all fields of the TCP layer. * @example * tcp.toObject(); * // { * // src: 52622, * // dst: 24043, * // seq: 3994458414, * // ack: 3198720281, * // dataOffset: 8, * // windowSize: 2048, * // checksum: 3346, * // urgentPointer: 0, * // flags: { * // reserved: 0, * // cwr: 0, * // ece: 0, * // urg: 0, * // ack: 1, * // psh: 0, * // rst: 0, * // syn: 0, * // fin: 0 * // }, * // options: [ * // { type: 1 }, * // { type: 1 }, * // { type: 8, recLength: 10, value: Buffer.from([0x52, 0xd3, 0xc6, 0x50, 0xdd, 0x04, 0xcd, 0xd6]) } * // ], * // } * @returns {Object} The TCP layer fields as an object. */ toObject() { return { ...omit(super.toObject(), ...flagKeys), flags: this.flags, options: [...this.options], }; } /** * Calculates and updates the checksum for the TCP layer. * This method mutates the object by setting the `checksum` property * based on the current state of the `buffer` and `prev` field. */ calculateChecksum() { this.checksum = checksums.pseudo({ data: this.buffer, addrType: this.prev?.name ?? 'IPv4', src: this.prev?.src, dst: this.prev?.dst, protocolType: IPProtocolTypes.TCP, }); } defaults(obj = {}, layers) { if (!obj.windowSize) { this.windowSize = 2048; } if (!obj.urgentPointer) { this.urgentPointer = 0; } if (!obj.dataOffset) { this.dataOffset = this.length / 4; } } checksums(obj) { if (!obj.checksum) { this.calculateChecksum(); } } nextProto(layers) { return new layers.Payload(this._buf.subarray(this.length)); } }; addField(TCP.prototype, 'flags'); mixins.withOptions(TCP.prototype, { baseLength, skipTypes: [0x1, 0x0], lengthIsTotal: true }); module.exports = { TCP };