UNPKG

quic

Version:

A QUIC server/client implementation in Node.js.

765 lines 28.2 kB
'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); // **Github:** https://github.com/fidm/quic // // **License:** MIT const util_1 = require("util"); const crypto_1 = require("crypto"); const error_1 = require("./error"); const symbol_1 = require("./symbol"); const common_1 = require("./common"); const QUIC_VERSIONS = ['Q039']; var SessionType; (function (SessionType) { SessionType[SessionType["SERVER"] = 0] = "SERVER"; SessionType[SessionType["CLIENT"] = 1] = "CLIENT"; })(SessionType = exports.SessionType || (exports.SessionType = {})); var FamilyType; (function (FamilyType) { FamilyType["IPv4"] = "IPv4"; FamilyType["IPv6"] = "IPv6"; })(FamilyType = exports.FamilyType || (exports.FamilyType = {})); /** * Returns supported version. */ function getVersion() { return QUIC_VERSIONS[0]; } exports.getVersion = getVersion; /** * Returns supported versions array. */ function getVersions() { return QUIC_VERSIONS.slice(); } exports.getVersions = getVersions; /** * Chooses the best version in the overlap of ours and theirs. */ function chooseVersion(theirs) { for (const v of theirs) { if (isSupportedVersion(v)) { return v; } } return ''; } exports.chooseVersion = chooseVersion; /** * Returns true if the server supports this version. */ function isSupportedVersion(version) { return QUIC_VERSIONS.includes(version); } exports.isSupportedVersion = isSupportedVersion; /** Protocol representing a base protocol. */ class Protocol { static fromBuffer(_bufv, _len) { throw new Error(`class method "fromBuffer" is not implemented`); } constructor(val) { this[symbol_1.kVal] = val; } [util_1.inspect.custom](_depth, _options) { return `<${this.constructor.name} ${this.toString()}>`; } } exports.Protocol = Protocol; const ConnectionIDReg = /^[0-9a-f]{16}$/; /** ConnectionID representing a connectionID. */ class ConnectionID extends Protocol { static fromBuffer(bufv) { bufv.walk(8); if (bufv.isOutside()) { throw new error_1.QuicError('QUIC_INTERNAL_ERROR'); } return new ConnectionID(bufv.buf.toString('hex', bufv.start, bufv.end)); } static random() { return new ConnectionID(crypto_1.randomBytes(8).toString('hex')); } constructor(id) { if (!ConnectionIDReg.test(id)) { throw new Error('invalid Connection ID'); } super(id); } /** * @return {string} - 16 length hex string */ valueOf() { return this[symbol_1.kVal]; } equals(other) { return (other instanceof ConnectionID) && this.valueOf() === other.valueOf(); } byteLen() { return 8; } writeTo(bufv) { bufv.walk(8); bufv.buf.write(this[symbol_1.kVal], bufv.start, 8, 'hex'); return bufv; } toString() { return this[symbol_1.kVal]; } } exports.ConnectionID = ConnectionID; /** PacketNumber representing a packetNumber. */ class PacketNumber extends Protocol { // The lower 8, 16, 32, or 48 bits of the packet number, based on which // FLAG_?BYTE_SEQUENCE_NUMBER flag is set in the public flags. // Each Regular Packet (as opposed to the Special public reset and version // negotiation packets) is assigned a packet number by the sender. // The first packet sent by an endpoint shall have a packet number of 1, and // each subsequent packet shall have a packet number one larger than that of the previous packet. static flagToByteLen(flagBits) { if ((flagBits & 0b11) !== flagBits) { throw new Error('invalid flagBits'); } return flagBits > 0 ? (flagBits * 2) : 1; } static fromBuffer(bufv, len) { bufv.walk(len); if (bufv.isOutside()) { throw new error_1.QuicError('QUIC_INTERNAL_ERROR'); } return new PacketNumber(bufv.buf.readUIntBE(bufv.start, len)); } constructor(val) { if (!Number.isInteger(val) || val < 1 || val > 0xffffffffffff) { throw new Error(`invalid PacketNumber val ${val}`); } super(val); } valueOf() { return this[symbol_1.kVal]; } nextNumber() { return new PacketNumber(this[symbol_1.kVal] + 1); } prevNumber() { return new PacketNumber(this[symbol_1.kVal] - 1); } isLimitReached() { // If a QUIC endpoint transmits a packet with a packet number of (2^64-1), // that packet must include a CONNECTION_CLOSE frame with an error code of QUIC_SEQUENCE_NUMBER_LIMIT_REACHED, // and the endpoint must not transmit any additional packets. return this[symbol_1.kVal] >= 0xffffffffffff; // but here 2^48 } delta(other) { return Math.abs(this.valueOf() - other.valueOf()); } closestTo(a, b) { return this.delta(a) < this.delta(b) ? a : b; } flagBits() { const byteLen = this.byteLen(); if (byteLen === 1) { return 0; } return byteLen / 2; } equals(other) { return (other instanceof PacketNumber) && this.valueOf() === other.valueOf(); } byteLen(isFull = false) { if (!isFull) { const value = this[symbol_1.kVal]; if (value <= 0xff) { return 1; } else if (value <= 0xffff) { return 2; } else if (value <= 0xffffffff) { return 4; } } return 6; } writeTo(bufv, isFull = false) { const len = isFull ? 6 : this.byteLen(); bufv.walk(len); bufv.buf.writeUIntBE(this[symbol_1.kVal], bufv.start, len); return bufv; } toString() { return String(this[symbol_1.kVal]); } } exports.PacketNumber = PacketNumber; /** StreamID representing a streamID. */ class StreamID extends Protocol { // the Stream-ID must be even if the server initiates the stream, and odd if the client initiates the stream. // 0 is not a valid Stream-ID. Stream 1 is reserved for the crypto handshake, // which should be the first client-initiated stream. /** * 2 bits -> 8/8, 16/8, 24/8, 32/8 */ static flagToByteLen(flagBits) { if ((flagBits & 0b11) !== flagBits) { throw new Error('invalid flagBits'); } return flagBits + 1; } static fromBuffer(bufv, len) { bufv.walk(len); if (bufv.isOutside()) { throw new error_1.QuicError('QUIC_INVALID_STREAM_DATA'); } return new StreamID(bufv.buf.readUIntBE(bufv.start, len)); } constructor(id) { // StreamID(0) is used by WINDOW_UPDATE if (!Number.isInteger(id) || id < 0 || id > 0xffffffff) { throw new Error(`invalid Stream ID ${id}`); } super(id); } valueOf() { return this[symbol_1.kVal]; } flagBits() { return this.byteLen() - 1; } nextID() { const value = this[symbol_1.kVal] + 2; return new StreamID(value <= 0xffffffff ? value : (value - 0xffffffff)); } prevID() { return new StreamID(this[symbol_1.kVal] - 2); } equals(other) { return (other instanceof StreamID) && this.valueOf() === other.valueOf(); } byteLen(isFull = false) { if (!isFull) { const value = this[symbol_1.kVal]; if (value <= 0xff) { return 1; } else if (value <= 0xffff) { return 2; } else if (value <= 0xffffff) { return 3; } } return 4; } writeTo(bufv, isFull = false) { const len = isFull ? 4 : this.byteLen(); bufv.walk(len); bufv.buf.writeUIntBE(this[symbol_1.kVal], bufv.start, len); return bufv; } toString() { return String(this[symbol_1.kVal]); } } exports.StreamID = StreamID; /** Offset representing a data offset. */ class Offset extends Protocol { /** * 3 bits -> 0, 16/8, 24/8, 32/8, 40/8, 48/8, 56/8, 64/8 */ static flagToByteLen(flagBits) { if ((flagBits & 0b111) !== flagBits) { throw new Error('invalid flagBits'); } return flagBits > 0 ? (flagBits + 1) : 0; } static fromBuffer(bufv, len) { bufv.walk(len); if (bufv.isOutside()) { throw new error_1.QuicError('QUIC_INTERNAL_ERROR'); } return new Offset(common_1.readUnsafeUInt(bufv.buf, bufv.start, len)); } constructor(offset) { if (!Number.isSafeInteger(offset) || offset < 0) { throw new Error(`invalid Offset ${offset}`); } super(offset); } valueOf() { return this[symbol_1.kVal]; } equals(other) { return this.valueOf() === other.valueOf(); } gt(other) { return this.valueOf() > other.valueOf(); } byteLen(isFull = false) { if (!isFull) { const value = this[symbol_1.kVal]; if (value === 0) { return 0; } else if (value <= 0xffff) { return 2; } else if (value <= 0xffffff) { return 3; } else if (value <= 0xffffffff) { return 4; } else if (value <= 0xffffffffff) { return 5; } else if (value <= 0xffffffffffff) { return 6; } return 7; // value should small than 0xffffffffffffff } return 8; } /** * 0, 16/8, 24/8, 32/8, 40/8, 48/8, 56/8, 64/8 -> 3 bits */ flagBits() { const byteLen = this.byteLen(); if (byteLen === 0) { return 0; } return byteLen > 1 ? (byteLen - 1) : 1; } writeTo(bufv, isFull = false) { const len = isFull ? 8 : this.byteLen(); bufv.walk(len); if (bufv.isOutside()) { throw new error_1.QuicError('QUIC_INTERNAL_ERROR'); } common_1.writeUnsafeUInt(bufv.buf, this[symbol_1.kVal], bufv.start, len); return bufv; } toString() { return String(this[symbol_1.kVal]); } } exports.Offset = Offset; /** SocketAddress representing a socket address. */ class SocketAddress extends Protocol { static fromBuffer(bufv) { const obj = { address: '', family: FamilyType.IPv4, port: 0, }; bufv.walk(2); if (bufv.isOutside()) { throw new error_1.QuicError('QUIC_INTERNAL_ERROR'); } const family = bufv.buf.readUInt16BE(bufv.start); if (family === 0x02) { obj.family = FamilyType.IPv4; bufv.walk(4); if (bufv.isOutside()) { throw new error_1.QuicError('QUIC_INTERNAL_ERROR'); } obj.address = [ bufv.buf.readUInt8(bufv.start), bufv.buf.readUInt8(bufv.start + 1), bufv.buf.readUInt8(bufv.start + 2), bufv.buf.readUInt8(bufv.start + 3), ].join('.'); bufv.walk(2); if (bufv.isOutside()) { throw new error_1.QuicError('QUIC_INTERNAL_ERROR'); } obj.port = bufv.buf.readUInt16BE(bufv.start); } else if (family === 0x0a) { obj.family = FamilyType.IPv6; bufv.walk(16); if (bufv.isOutside()) { throw new error_1.QuicError('QUIC_INTERNAL_ERROR'); } obj.address = [ bufv.buf.readUInt16BE(bufv.start).toString(16), bufv.buf.readUInt16BE(bufv.start + 2).toString(16), bufv.buf.readUInt16BE(bufv.start + 4).toString(16), bufv.buf.readUInt16BE(bufv.start + 6).toString(16), bufv.buf.readUInt16BE(bufv.start + 8).toString(16), bufv.buf.readUInt16BE(bufv.start + 10).toString(16), bufv.buf.readUInt16BE(bufv.start + 12).toString(16), bufv.buf.readUInt16BE(bufv.start + 14).toString(16), ].join(':'); bufv.walk(2); if (bufv.isOutside()) { throw new error_1.QuicError('QUIC_INTERNAL_ERROR'); } obj.port = bufv.buf.readUInt16BE(bufv.start); } else { throw new Error('invalid SocketAddress buffer'); } return new SocketAddress(obj); } constructor(obj) { if (!isAddress(obj)) { throw new Error(`invalid Socket Address ${JSON.stringify(obj)}`); } let address = obj.address; if (address.includes('::')) { const unfold = '0:'; if (address.startsWith('::')) { address = '0' + address; } else if (address.endsWith('::')) { address += '0'; } const _address = address.split(':'); _address[_address.indexOf('')] = unfold.repeat(9 - _address.length).slice(0, -1); address = _address.join(':'); } super(address); this.port = obj.port; this.family = obj.family; this.address = address; } valueOf() { return { address: this.address, family: this.family, port: this.port, }; } equals(other) { if (!(other instanceof SocketAddress)) { return false; } return this.family === other.family && this.port === other.port && this.address === other.address; } byteLen() { return this.family === FamilyType.IPv4 ? 8 : 20; } writeTo(bufv) { const address = this.address; if (this.family === FamilyType.IPv4) { bufv.walk(2); bufv.buf.writeUInt16BE(0x02, bufv.start); for (const val of address.split('.')) { bufv.walk(1); bufv.buf.writeUInt8(parseInt(val, 10), bufv.start); } bufv.walk(2); bufv.buf.writeUInt16BE(this.port, bufv.start); } else { bufv.walk(2); bufv.buf.writeUInt16BE(0x0a, bufv.start); for (const val of address.split(':')) { bufv.walk(2); bufv.buf.writeUInt16BE(parseInt(val, 16), bufv.start); } bufv.walk(2); bufv.buf.writeUInt16BE(this.port, bufv.start); } return bufv; } toString() { return JSON.stringify(this.valueOf()); } } exports.SocketAddress = SocketAddress; /** QuicTags representing a QUIC tag. */ class QuicTags extends Protocol { static fromBuffer(bufv) { bufv.walk(4); const tagName = bufv.buf.readUInt32BE(bufv.start); const quicTag = new QuicTags(tagName); bufv.walk(4); if (bufv.isOutside()) { throw new error_1.QuicError('QUIC_INTERNAL_ERROR'); } let count = bufv.buf.readInt16LE(bufv.start); const baseOffset = bufv.end + 8 * count; const v2 = new common_1.Visitor(baseOffset); while (count-- > 0) { bufv.walk(4); if (bufv.isOutside()) { throw new error_1.QuicError('QUIC_INTERNAL_ERROR'); } const key = bufv.buf.readInt32BE(bufv.start); bufv.walk(4); v2.walk(0); if (bufv.isOutside()) { throw new error_1.QuicError('QUIC_INTERNAL_ERROR'); } v2.end = baseOffset + bufv.buf.readInt32LE(bufv.start); if (bufv.length < v2.end) { throw new error_1.QuicError('QUIC_INTERNAL_ERROR'); } const val = bufv.buf.slice(v2.start, v2.end); quicTag.set(key, val); } bufv.reset(v2.end, v2.end); return quicTag; } constructor(name) { super(name); this.name = name; this.tags = new Map(); } valueOf() { const tags = {}; for (const [key, value] of this.tags) { tags[Tag[key]] = value; } return { name: Tag[this.name], tags, }; } get size() { return this.tags.size; } [Symbol.iterator]() { return this.tags[Symbol.iterator](); } set(key, val) { this.tags.set(key, val); } get(key) { return this.tags.get(key); } has(key) { return this.tags.has(key); } equals(other) { if (!(other instanceof QuicTags)) { return false; } if (this.name !== other.name || this.tags.size !== other.tags.size) { return false; } for (const key of this.tags.keys()) { const a = this.tags.get(key); const b = other.tags.get(key); if (a == null || b == null || !a.equals(b)) { return false; } } return true; } byteLen() { let byteLen = 8; for (const buf of this.tags.values()) { byteLen += 8 + buf.length; } return byteLen; } writeTo(bufv) { bufv.walk(4); bufv.buf.writeUInt32BE(this.name, bufv.start); bufv.walk(4); const size = this.tags.size; bufv.buf.writeUInt16LE(size, bufv.start); bufv.buf.writeUInt16LE(0, bufv.start + 2); let baseOffset = 0; const v = new common_1.Visitor(bufv.end + 8 * size); const keys = Array.from(this.tags.keys()); keys.sort((a, b) => a - b); for (const key of keys) { const val = this.tags.get(key); if (val == null) { throw new error_1.QuicError('QUIC_INTERNAL_ERROR'); } bufv.walk(4); bufv.buf.writeUInt32BE(key, bufv.start); bufv.walk(4); baseOffset += val.length; bufv.buf.writeUInt32LE(baseOffset, bufv.start); v.walk(val.length); val.copy(bufv.buf, v.start, 0, val.length); } bufv.reset(v.end, v.end); return bufv; } toString() { return JSON.stringify(this.valueOf()); } } exports.QuicTags = QuicTags; var Tag; (function (Tag) { Tag[Tag["CHLO"] = toTag('C', 'H', 'L', 'O')] = "CHLO"; Tag[Tag["SHLO"] = toTag('S', 'H', 'L', 'O')] = "SHLO"; Tag[Tag["SCFG"] = toTag('S', 'C', 'F', 'G')] = "SCFG"; Tag[Tag["REJ"] = toTag('R', 'E', 'J', '\u{0}')] = "REJ"; Tag[Tag["SREJ"] = toTag('S', 'R', 'E', 'J')] = "SREJ"; Tag[Tag["CETV"] = toTag('C', 'E', 'T', 'V')] = "CETV"; Tag[Tag["PRST"] = toTag('P', 'R', 'S', 'T')] = "PRST"; Tag[Tag["SCUP"] = toTag('S', 'C', 'U', 'P')] = "SCUP"; Tag[Tag["ALPN"] = toTag('A', 'L', 'P', 'N')] = "ALPN"; // Key exchange methods Tag[Tag["P256"] = toTag('P', '2', '5', '6')] = "P256"; Tag[Tag["C255"] = toTag('C', '2', '5', '5')] = "C255"; // AEAD algorithms Tag[Tag["AESG"] = toTag('A', 'E', 'S', 'G')] = "AESG"; Tag[Tag["CC20"] = toTag('C', 'C', '2', '0')] = "CC20"; // Socket receive buffer Tag[Tag["SRBF"] = toTag('S', 'R', 'B', 'F')] = "SRBF"; // Congestion control feedback types Tag[Tag["QBIC"] = toTag('Q', 'B', 'I', 'C')] = "QBIC"; // Connection options (COPT) values Tag[Tag["AFCW"] = toTag('A', 'F', 'C', 'W')] = "AFCW"; Tag[Tag["IFW5"] = toTag('I', 'F', 'W', '5')] = "IFW5"; Tag[Tag["IFW6"] = toTag('I', 'F', 'W', '6')] = "IFW6"; Tag[Tag["IFW7"] = toTag('I', 'F', 'W', '7')] = "IFW7"; Tag[Tag["IFW8"] = toTag('I', 'F', 'W', '8')] = "IFW8"; Tag[Tag["IFW9"] = toTag('I', 'F', 'W', '9')] = "IFW9"; Tag[Tag["IFWA"] = toTag('I', 'F', 'W', 'a')] = "IFWA"; Tag[Tag["TBBR"] = toTag('T', 'B', 'B', 'R')] = "TBBR"; Tag[Tag["1RTT"] = toTag('1', 'R', 'T', 'T')] = "1RTT"; Tag[Tag["2RTT"] = toTag('2', 'R', 'T', 'T')] = "2RTT"; Tag[Tag["LRTT"] = toTag('L', 'R', 'T', 'T')] = "LRTT"; Tag[Tag["BBRR"] = toTag('B', 'B', 'R', 'R')] = "BBRR"; Tag[Tag["BBR1"] = toTag('B', 'B', 'R', '1')] = "BBR1"; Tag[Tag["BBR2"] = toTag('B', 'B', 'R', '2')] = "BBR2"; Tag[Tag["RENO"] = toTag('R', 'E', 'N', 'O')] = "RENO"; Tag[Tag["TPCC"] = toTag('P', 'C', 'C', '\u{0}')] = "TPCC"; Tag[Tag["BYTE"] = toTag('B', 'Y', 'T', 'E')] = "BYTE"; Tag[Tag["IW03"] = toTag('I', 'W', '0', '3')] = "IW03"; Tag[Tag["IW10"] = toTag('I', 'W', '1', '0')] = "IW10"; Tag[Tag["IW20"] = toTag('I', 'W', '2', '0')] = "IW20"; Tag[Tag["IW50"] = toTag('I', 'W', '5', '0')] = "IW50"; Tag[Tag["1CON"] = toTag('1', 'C', 'O', 'N')] = "1CON"; Tag[Tag["NTLP"] = toTag('N', 'T', 'L', 'P')] = "NTLP"; Tag[Tag["NCON"] = toTag('N', 'C', 'O', 'N')] = "NCON"; Tag[Tag["NRTO"] = toTag('N', 'R', 'T', 'O')] = "NRTO"; Tag[Tag["UNDO"] = toTag('U', 'N', 'D', 'O')] = "UNDO"; Tag[Tag["TIME"] = toTag('T', 'I', 'M', 'E')] = "TIME"; Tag[Tag["ATIM"] = toTag('A', 'T', 'I', 'M')] = "ATIM"; Tag[Tag["MIN1"] = toTag('M', 'I', 'N', '1')] = "MIN1"; Tag[Tag["MIN4"] = toTag('M', 'I', 'N', '4')] = "MIN4"; Tag[Tag["TLPR"] = toTag('T', 'L', 'P', 'R')] = "TLPR"; Tag[Tag["ACKD"] = toTag('A', 'C', 'K', 'D')] = "ACKD"; Tag[Tag["AKD2"] = toTag('A', 'K', 'D', '2')] = "AKD2"; Tag[Tag["AKD3"] = toTag('A', 'K', 'D', '3')] = "AKD3"; Tag[Tag["AKD4"] = toTag('A', 'K', 'D', '4')] = "AKD4"; Tag[Tag["AKDU"] = toTag('A', 'K', 'D', 'U')] = "AKDU"; Tag[Tag["SSLR"] = toTag('S', 'S', 'L', 'R')] = "SSLR"; Tag[Tag["NPRR"] = toTag('N', 'P', 'R', 'R')] = "NPRR"; Tag[Tag["5RTO"] = toTag('5', 'R', 'T', 'O')] = "5RTO"; Tag[Tag["3RTO"] = toTag('3', 'R', 'T', 'O')] = "3RTO"; Tag[Tag["CTIM"] = toTag('C', 'T', 'I', 'M')] = "CTIM"; Tag[Tag["DHDT"] = toTag('D', 'H', 'D', 'T')] = "DHDT"; Tag[Tag["CONH"] = toTag('C', 'O', 'N', 'H')] = "CONH"; Tag[Tag["LFAK"] = toTag('L', 'F', 'A', 'K')] = "LFAK"; // TODO(fayang): Remove this connection option in QUIC_VERSION_37, in which // MAX_HEADER_LIST_SIZE settings frame should be supported. Tag[Tag["SMHL"] = toTag('S', 'M', 'H', 'L')] = "SMHL"; Tag[Tag["CCVX"] = toTag('C', 'C', 'V', 'X')] = "CCVX"; Tag[Tag["CBQT"] = toTag('C', 'B', 'Q', 'T')] = "CBQT"; Tag[Tag["BLMX"] = toTag('B', 'L', 'M', 'X')] = "BLMX"; Tag[Tag["CPAU"] = toTag('C', 'P', 'A', 'U')] = "CPAU"; Tag[Tag["NSTP"] = toTag('N', 'S', 'T', 'P')] = "NSTP"; // Optional support of truncated Connection IDs. If sent by a peer, the value // is the minimum number of bytes allowed for the connection ID sent to the // peer. Tag[Tag["TCID"] = toTag('T', 'C', 'I', 'D')] = "TCID"; // Multipath option. Tag[Tag["MPTH"] = toTag('M', 'P', 'T', 'H')] = "MPTH"; Tag[Tag["NCMR"] = toTag('N', 'C', 'M', 'R')] = "NCMR"; // Enable bandwidth resumption experiment. Tag[Tag["BWRE"] = toTag('B', 'W', 'R', 'E')] = "BWRE"; Tag[Tag["BWMX"] = toTag('B', 'W', 'M', 'X')] = "BWMX"; Tag[Tag["BWRS"] = toTag('B', 'W', 'R', 'S')] = "BWRS"; Tag[Tag["BWS2"] = toTag('B', 'W', 'S', '2')] = "BWS2"; // Enable path MTU discovery experiment. Tag[Tag["MTUH"] = toTag('M', 'T', 'U', 'H')] = "MTUH"; Tag[Tag["MTUL"] = toTag('M', 'T', 'U', 'L')] = "MTUL"; // Tags for async signing experiments Tag[Tag["ASYN"] = toTag('A', 'S', 'Y', 'N')] = "ASYN"; Tag[Tag["SYNC"] = toTag('S', 'Y', 'N', 'C')] = "SYNC"; Tag[Tag["FHL2"] = toTag('F', 'H', 'L', '2')] = "FHL2"; // Proof types (i.e. certificate types) // NOTE: although it would be silly to do so, specifying both kX509 and kX59R // is allowed and is equivalent to specifying only kX509. Tag[Tag["X509"] = toTag('X', '5', '0', '9')] = "X509"; Tag[Tag["X59R"] = toTag('X', '5', '9', 'R')] = "X59R"; Tag[Tag["CHID"] = toTag('C', 'H', 'I', 'D')] = "CHID"; // Client hello tags Tag[Tag["VER"] = toTag('V', 'E', 'R', '\u{0}')] = "VER"; Tag[Tag["NONC"] = toTag('N', 'O', 'N', 'C')] = "NONC"; Tag[Tag["NONP"] = toTag('N', 'O', 'N', 'P')] = "NONP"; Tag[Tag["KEXS"] = toTag('K', 'E', 'X', 'S')] = "KEXS"; Tag[Tag["AEAD"] = toTag('A', 'E', 'A', 'D')] = "AEAD"; Tag[Tag["COPT"] = toTag('C', 'O', 'P', 'T')] = "COPT"; Tag[Tag["CLOP"] = toTag('C', 'L', 'O', 'P')] = "CLOP"; Tag[Tag["ICSL"] = toTag('I', 'C', 'S', 'L')] = "ICSL"; Tag[Tag["SCLS"] = toTag('S', 'C', 'L', 'S')] = "SCLS"; Tag[Tag["MSPC"] = toTag('M', 'S', 'P', 'C')] = "MSPC"; Tag[Tag["MIDS"] = toTag('M', 'I', 'D', 'S')] = "MIDS"; Tag[Tag["IRTT"] = toTag('I', 'R', 'T', 'T')] = "IRTT"; Tag[Tag["SWND"] = toTag('S', 'W', 'N', 'D')] = "SWND"; Tag[Tag["SNI"] = toTag('S', 'N', 'I', '\u{0}')] = "SNI"; Tag[Tag["PUBS"] = toTag('P', 'U', 'B', 'S')] = "PUBS"; Tag[Tag["SCID"] = toTag('S', 'C', 'I', 'D')] = "SCID"; Tag[Tag["ORBT"] = toTag('O', 'B', 'I', 'T')] = "ORBT"; Tag[Tag["PDMD"] = toTag('P', 'D', 'M', 'D')] = "PDMD"; Tag[Tag["PROF"] = toTag('P', 'R', 'O', 'F')] = "PROF"; Tag[Tag["CCS"] = toTag('C', 'C', 'S', '\u{0}')] = "CCS"; Tag[Tag["CCRT"] = toTag('C', 'C', 'R', 'T')] = "CCRT"; Tag[Tag["EXPY"] = toTag('E', 'X', 'P', 'Y')] = "EXPY"; Tag[Tag["STTL"] = toTag('S', 'T', 'T', 'L')] = "STTL"; Tag[Tag["SFCW"] = toTag('S', 'F', 'C', 'W')] = "SFCW"; Tag[Tag["CFCW"] = toTag('C', 'F', 'C', 'W')] = "CFCW"; Tag[Tag["UAID"] = toTag('U', 'A', 'I', 'D')] = "UAID"; Tag[Tag["XLCT"] = toTag('X', 'L', 'C', 'T')] = "XLCT"; Tag[Tag["TBKP"] = toTag('T', 'B', 'K', 'P')] = "TBKP"; // Token Binding tags Tag[Tag["TB10"] = toTag('T', 'B', '1', '0')] = "TB10"; // Rejection tags Tag[Tag["RREJ"] = toTag('R', 'R', 'E', 'J')] = "RREJ"; // Stateless Reject tags Tag[Tag["RCID"] = toTag('R', 'C', 'I', 'D')] = "RCID"; // Server hello tags Tag[Tag["CADR"] = toTag('C', 'A', 'D', 'R')] = "CADR"; Tag[Tag["ASAD"] = toTag('A', 'S', 'A', 'D')] = "ASAD"; // CETV tags Tag[Tag["CIDK"] = toTag('C', 'I', 'D', 'K')] = "CIDK"; Tag[Tag["CIDS"] = toTag('C', 'I', 'D', 'S')] = "CIDS"; // Public reset tags Tag[Tag["RNON"] = toTag('R', 'N', 'O', 'N')] = "RNON"; Tag[Tag["RSEQ"] = toTag('R', 'S', 'E', 'Q')] = "RSEQ"; // Universal tags Tag[Tag["PAD"] = toTag('P', 'A', 'D', '\u{0}')] = "PAD"; // Server push tags Tag[Tag["SPSH"] = toTag('S', 'P', 'S', 'H')] = "SPSH"; // clang-format on // These tags have a special form so that they appear either at the beginning // or the end of a handshake message. Since handshake messages are sorted by // tag value, the tags with 0 at the end will sort first and those with 255 at // the end will sort last. // // The certificate chain should have a tag that will cause it to be sorted at // the end of any handshake messages because it's likely to be large and the // client might be able to get everything that it needs from the small values at // the beginning. // // Likewise tags with random values should be towards the beginning of the // message because the server mightn't hold state for a rejected client hello // and therefore the client may have issues reassembling the rejection message // in the event that it sent two client hellos. Tag[Tag["SNO"] = toTag('S', 'N', 'O', '\u{0}')] = "SNO"; Tag[Tag["STK"] = toTag('S', 'T', 'K', '\u{0}')] = "STK"; Tag[Tag["CRT"] = toTag('C', 'R', 'T', '\u{ff}')] = "CRT"; Tag[Tag["CSCT"] = toTag('C', 'S', 'C', 'T')] = "CSCT"; })(Tag = exports.Tag || (exports.Tag = {})); function toTag(a, b, c, d) { return a.charCodeAt(0) * (0xffffff + 1) + b.charCodeAt(0) * (0xffff + 1) + c.charCodeAt(0) * (0xff + 1) + d.charCodeAt(0); } function isAddress(address) { return address != null && address.port >= 0 && Number.isInteger(address.port) && typeof address.address === 'string' && (address.family === FamilyType.IPv4 || address.family === FamilyType.IPv6); } //# sourceMappingURL=protocol.js.map