@push.rocks/smartproxy
Version:
A powerful proxy package with unified route-based configuration for high traffic management. Features include SSL/TLS support, flexible routing patterns, WebSocket handling, advanced security options, and automatic ACME certificate management.
153 lines • 11.6 kB
JavaScript
/**
* PROXY Protocol Parser
* Implementation of HAProxy PROXY protocol v1 (text format)
* Spec: https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt
*/
/**
* PROXY protocol parser
*/
export class ProxyProtocolParser {
static { this.PROXY_V1_SIGNATURE = 'PROXY '; }
static { this.MAX_HEADER_LENGTH = 107; } // Max length for v1 header
static { this.HEADER_TERMINATOR = '\r\n'; }
/**
* Parse PROXY protocol v1 header from buffer
* Returns proxy info and remaining data after header
*/
static parse(data) {
// Check if buffer starts with PROXY signature
if (!data.toString('ascii', 0, 6).startsWith(this.PROXY_V1_SIGNATURE)) {
return {
proxyInfo: null,
remainingData: data
};
}
// Find header terminator
const headerEndIndex = data.indexOf(this.HEADER_TERMINATOR);
if (headerEndIndex === -1) {
// Header incomplete, need more data
if (data.length > this.MAX_HEADER_LENGTH) {
// Header too long, invalid
throw new Error('PROXY protocol header exceeds maximum length');
}
return {
proxyInfo: null,
remainingData: data
};
}
// Extract header line
const headerLine = data.toString('ascii', 0, headerEndIndex);
const remainingData = data.slice(headerEndIndex + 2); // Skip \r\n
// Parse header
const parts = headerLine.split(' ');
if (parts.length < 2) {
throw new Error(`Invalid PROXY protocol header format: ${headerLine}`);
}
const [signature, protocol] = parts;
// Validate protocol
if (!['TCP4', 'TCP6', 'UNKNOWN'].includes(protocol)) {
throw new Error(`Invalid PROXY protocol: ${protocol}`);
}
// For UNKNOWN protocol, ignore addresses
if (protocol === 'UNKNOWN') {
return {
proxyInfo: {
protocol: 'UNKNOWN',
sourceIP: '',
sourcePort: 0,
destinationIP: '',
destinationPort: 0
},
remainingData
};
}
// For TCP4/TCP6, we need all 6 parts
if (parts.length !== 6) {
throw new Error(`Invalid PROXY protocol header format: ${headerLine}`);
}
const [, , srcIP, dstIP, srcPort, dstPort] = parts;
// Validate and parse ports
const sourcePort = parseInt(srcPort, 10);
const destinationPort = parseInt(dstPort, 10);
if (isNaN(sourcePort) || sourcePort < 0 || sourcePort > 65535) {
throw new Error(`Invalid source port: ${srcPort}`);
}
if (isNaN(destinationPort) || destinationPort < 0 || destinationPort > 65535) {
throw new Error(`Invalid destination port: ${dstPort}`);
}
// Validate IP addresses
const protocolType = protocol;
if (!this.isValidIP(srcIP, protocolType)) {
throw new Error(`Invalid source IP for ${protocol}: ${srcIP}`);
}
if (!this.isValidIP(dstIP, protocolType)) {
throw new Error(`Invalid destination IP for ${protocol}: ${dstIP}`);
}
return {
proxyInfo: {
protocol: protocolType,
sourceIP: srcIP,
sourcePort,
destinationIP: dstIP,
destinationPort
},
remainingData
};
}
/**
* Generate PROXY protocol v1 header
*/
static generate(info) {
if (info.protocol === 'UNKNOWN') {
return Buffer.from(`PROXY UNKNOWN\r\n`, 'ascii');
}
const header = `PROXY ${info.protocol} ${info.sourceIP} ${info.destinationIP} ${info.sourcePort} ${info.destinationPort}\r\n`;
if (header.length > this.MAX_HEADER_LENGTH) {
throw new Error('Generated PROXY protocol header exceeds maximum length');
}
return Buffer.from(header, 'ascii');
}
/**
* Validate IP address format
*/
static isValidIP(ip, protocol) {
if (protocol === 'TCP4') {
return this.isIPv4(ip);
}
else if (protocol === 'TCP6') {
return this.isIPv6(ip);
}
return false;
}
/**
* Check if string is valid IPv4
*/
static isIPv4(ip) {
const parts = ip.split('.');
if (parts.length !== 4)
return false;
for (const part of parts) {
const num = parseInt(part, 10);
if (isNaN(num) || num < 0 || num > 255 || part !== num.toString()) {
return false;
}
}
return true;
}
/**
* Check if string is valid IPv6
*/
static isIPv6(ip) {
// Basic IPv6 validation
const ipv6Regex = /^(([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$/;
return ipv6Regex.test(ip);
}
/**
* Create a connection ID string for tracking
*/
static createConnectionId(connectionInfo) {
const { sourceIp, sourcePort, destIp, destPort } = connectionInfo;
return `${sourceIp}:${sourcePort}-${destIp}:${destPort}`;
}
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicGFyc2VyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vdHMvcHJvdG9jb2xzL3Byb3h5L3BhcnNlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7OztHQUlHO0FBSUg7O0dBRUc7QUFDSCxNQUFNLE9BQU8sbUJBQW1CO2FBQ2QsdUJBQWtCLEdBQUcsUUFBUSxDQUFDO2FBQzlCLHNCQUFpQixHQUFHLEdBQUcsQ0FBQyxHQUFDLDJCQUEyQjthQUNwRCxzQkFBaUIsR0FBRyxNQUFNLENBQUM7SUFFM0M7OztPQUdHO0lBQ0gsTUFBTSxDQUFDLEtBQUssQ0FBQyxJQUFZO1FBQ3ZCLDhDQUE4QztRQUM5QyxJQUFJLENBQUMsSUFBSSxDQUFDLFFBQVEsQ0FBQyxPQUFPLEVBQUUsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDLFVBQVUsQ0FBQyxJQUFJLENBQUMsa0JBQWtCLENBQUMsRUFBRSxDQUFDO1lBQ3RFLE9BQU87Z0JBQ0wsU0FBUyxFQUFFLElBQUk7Z0JBQ2YsYUFBYSxFQUFFLElBQUk7YUFDcEIsQ0FBQztRQUNKLENBQUM7UUFFRCx5QkFBeUI7UUFDekIsTUFBTSxjQUFjLEdBQUcsSUFBSSxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsaUJBQWlCLENBQUMsQ0FBQztRQUM1RCxJQUFJLGNBQWMsS0FBSyxDQUFDLENBQUMsRUFBRSxDQUFDO1lBQzFCLG9DQUFvQztZQUNwQyxJQUFJLElBQUksQ0FBQyxNQUFNLEdBQUcsSUFBSSxDQUFDLGlCQUFpQixFQUFFLENBQUM7Z0JBQ3pDLDJCQUEyQjtnQkFDM0IsTUFBTSxJQUFJLEtBQUssQ0FBQyw4Q0FBOEMsQ0FBQyxDQUFDO1lBQ2xFLENBQUM7WUFDRCxPQUFPO2dCQUNMLFNBQVMsRUFBRSxJQUFJO2dCQUNmLGFBQWEsRUFBRSxJQUFJO2FBQ3BCLENBQUM7UUFDSixDQUFDO1FBRUQsc0JBQXNCO1FBQ3RCLE1BQU0sVUFBVSxHQUFHLElBQUksQ0FBQyxRQUFRLENBQUMsT0FBTyxF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