import * as plugins from '../../../plugins.js'; import type { IAcmeOptions } from '../../../certificate/models/certificate-types.js'; import type { IRouteConfig } from './route-types.js'; import type { TForwardingType } from '../../../forwarding/config/forwarding-types.js'; /** * Provision object for static or HTTP-01 certificate */ export type TSmartProxyCertProvisionObject = plugins.tsclass.network.ICert | 'http01'; /** * Alias for backward compatibility with code that uses IRoutedSmartProxyOptions */ export type IRoutedSmartProxyOptions = ISmartProxyOptions; /** * Legacy domain configuration interface for backward compatibility */ export interface IDomainConfig { domains: string[]; forwarding: { type: TForwardingType; target: { host: string | string[]; port: number; }; acme?: { enabled?: boolean; maintenance?: boolean; production?: boolean; forwardChallenges?: { host: string; port: number; useTls?: boolean; }; }; http?: { enabled?: boolean; redirectToHttps?: boolean; headers?: Record; }; https?: { customCert?: { key: string; cert: string; }; forwardSni?: boolean; }; security?: { allowedIps?: string[]; blockedIps?: string[]; maxConnections?: number; }; advanced?: { portRanges?: Array<{ from: number; to: number }>; networkProxyPort?: number; keepAlive?: boolean; timeout?: number; headers?: Record; }; }; } /** * Helper functions for type checking configuration types */ export function isLegacyOptions(options: any): boolean { return !!(options.domainConfigs && options.domainConfigs.length > 0 && (!options.routes || options.routes.length === 0)); } export function isRoutedOptions(options: any): boolean { return !!(options.routes && options.routes.length > 0); } /** * SmartProxy configuration options */ export interface ISmartProxyOptions { // The unified configuration array (required) routes: IRouteConfig[]; // Legacy options for backward compatibility fromPort?: number; toPort?: number; sniEnabled?: boolean; domainConfigs?: IDomainConfig[]; targetIP?: string; defaultAllowedIPs?: string[]; defaultBlockedIPs?: string[]; globalPortRanges?: Array<{ from: number; to: number }>; forwardAllGlobalRanges?: boolean; preserveSourceIP?: boolean; // Global/default settings defaults?: { target?: { host: string; // Default host to use when not specified in routes port: number; // Default port to use when not specified in routes }; security?: { allowedIPs?: string[]; // Default allowed IPs blockedIPs?: string[]; // Default blocked IPs maxConnections?: number; // Default max connections }; preserveSourceIP?: boolean; // Default source IP preservation }; // TLS options pfx?: Buffer; key?: string | Buffer | Array; passphrase?: string; cert?: string | Buffer | Array; ca?: string | Buffer | Array; ciphers?: string; honorCipherOrder?: boolean; rejectUnauthorized?: boolean; secureProtocol?: string; servername?: string; minVersion?: string; maxVersion?: string; // Timeout settings initialDataTimeout?: number; // Timeout for initial data/SNI (ms), default: 60000 (60s) socketTimeout?: number; // Socket inactivity timeout (ms), default: 3600000 (1h) inactivityCheckInterval?: number; // How often to check for inactive connections (ms), default: 60000 (60s) maxConnectionLifetime?: number; // Default max connection lifetime (ms), default: 86400000 (24h) inactivityTimeout?: number; // Inactivity timeout (ms), default: 14400000 (4h) gracefulShutdownTimeout?: number; // (ms) maximum time to wait for connections to close during shutdown // Socket optimization settings noDelay?: boolean; // Disable Nagle's algorithm (default: true) keepAlive?: boolean; // Enable TCP keepalive (default: true) keepAliveInitialDelay?: number; // Initial delay before sending keepalive probes (ms) maxPendingDataSize?: number; // Maximum bytes to buffer during connection setup // Enhanced features disableInactivityCheck?: boolean; // Disable inactivity checking entirely enableKeepAliveProbes?: boolean; // Enable TCP keep-alive probes enableDetailedLogging?: boolean; // Enable detailed connection logging enableTlsDebugLogging?: boolean; // Enable TLS handshake debug logging enableRandomizedTimeouts?: boolean; // Randomize timeouts slightly to prevent thundering herd allowSessionTicket?: boolean; // Allow TLS session ticket for reconnection (default: true) // Rate limiting and security maxConnectionsPerIP?: number; // Maximum simultaneous connections from a single IP connectionRateLimitPerMinute?: number; // Max new connections per minute from a single IP // Enhanced keep-alive settings keepAliveTreatment?: 'standard' | 'extended' | 'immortal'; // How to treat keep-alive connections keepAliveInactivityMultiplier?: number; // Multiplier for inactivity timeout for keep-alive connections extendedKeepAliveLifetime?: number; // Extended lifetime for keep-alive connections (ms) // NetworkProxy integration useNetworkProxy?: number[]; // Array of ports to forward to NetworkProxy networkProxyPort?: number; // Port where NetworkProxy is listening (default: 8443) // ACME configuration options for SmartProxy acme?: IAcmeOptions; /** * Optional certificate provider callback. Return 'http01' to use HTTP-01 challenges, * or a static certificate object for immediate provisioning. */ certProvisionFunction?: (domain: string) => Promise; } /** * Enhanced connection record */ export interface IConnectionRecord { id: string; // Unique connection identifier incoming: plugins.net.Socket; outgoing: plugins.net.Socket | null; incomingStartTime: number; outgoingStartTime?: number; outgoingClosedTime?: number; lockedDomain?: string; // Used to lock this connection to the initial SNI connectionClosed: boolean; // Flag to prevent multiple cleanup attempts cleanupTimer?: NodeJS.Timeout; // Timer for max lifetime/inactivity alertFallbackTimeout?: NodeJS.Timeout; // Timer for fallback after alert lastActivity: number; // Last activity timestamp for inactivity detection pendingData: Buffer[]; // Buffer to hold data during connection setup pendingDataSize: number; // Track total size of pending data // Legacy property for backward compatibility domainConfig?: IDomainConfig; // Enhanced tracking fields bytesReceived: number; // Total bytes received bytesSent: number; // Total bytes sent remoteIP: string; // Remote IP (cached for logging after socket close) localPort: number; // Local port (cached for logging) isTLS: boolean; // Whether this connection is a TLS connection tlsHandshakeComplete: boolean; // Whether the TLS handshake is complete hasReceivedInitialData: boolean; // Whether initial data has been received routeConfig?: IRouteConfig; // Associated route config for this connection // Keep-alive tracking hasKeepAlive: boolean; // Whether keep-alive is enabled for this connection inactivityWarningIssued?: boolean; // Whether an inactivity warning has been issued incomingTerminationReason?: string | null; // Reason for incoming termination outgoingTerminationReason?: string | null; // Reason for outgoing termination // NetworkProxy tracking usingNetworkProxy?: boolean; // Whether this connection is using a NetworkProxy // Renegotiation handler renegotiationHandler?: (chunk: Buffer) => void; // Handler for renegotiation detection // Browser connection tracking isBrowserConnection?: boolean; // Whether this connection appears to be from a browser domainSwitches?: number; // Number of times the domain has been switched on this connection }