From 8e9c4915b4af3c171ed6500464933c785230d238 Mon Sep 17 00:00:00 2001 From: qist <87984115@qq.com> Date: Mon, 16 Dec 2024 15:39:02 +0800 Subject: [PATCH] update XHTTP --- config/version | 2 +- web/assets/js/model/xray.js | 4440 ++++++++++---------- web/html/xui/common_sider.html | 8 +- web/html/xui/form/stream/stream_xhttp.html | 22 +- 4 files changed, 2240 insertions(+), 2232 deletions(-) diff --git a/config/version b/config/version index c15fb63b..480f021d 100644 --- a/config/version +++ b/config/version @@ -1 +1 @@ -24.12.16 \ No newline at end of file +24.12.161 \ No newline at end of file diff --git a/web/assets/js/model/xray.js b/web/assets/js/model/xray.js index 86f22ab1..a45893b9 100644 --- a/web/assets/js/model/xray.js +++ b/web/assets/js/model/xray.js @@ -1,2220 +1,2220 @@ -const Protocols = { - VMESS: 'vmess', - VLESS: 'vless', - TROJAN: 'trojan', - SHADOWSOCKS: 'shadowsocks', - DOKODEMO: 'dokodemo-door', - SOCKS: 'socks', - HTTP: 'http', -}; - -const VmessMethods = { - AES_128_GCM: 'aes-128-gcm', - CHACHA20_POLY1305: 'chacha20-poly1305', - AUTO: 'auto', - NONE: 'none', -}; - -const SSMethods = { - // AES_256_CFB: 'aes-256-cfb', - // AES_128_CFB: 'aes-128-cfb', - // CHACHA20: 'chacha20', - // CHACHA20_IETF: 'chacha20-ietf', - CHACHA20_POLY1305: 'chacha20-poly1305', - AES_256_GCM: 'aes-256-gcm', - AES_128_GCM: 'aes-128-gcm', - BLAKE3_AES_128_GCM: '2022-blake3-aes-128-gcm', - BLAKE3_AES_256_GCM: '2022-blake3-aes-256-gcm', - BLAKE3_CHACHA20_POLY1305: '2022-blake3-chacha20-poly1305', -}; - -const RULE_IP = { - PRIVATE: 'geoip:private', - CN: 'geoip:cn', -}; - -const RULE_DOMAIN = { - ADS: 'geosite:category-ads', - ADS_ALL: 'geosite:category-ads-all', - CN: 'geosite:cn', - GOOGLE: 'geosite:google', - FACEBOOK: 'geosite:facebook', - SPEEDTEST: 'geosite:speedtest', -}; - -const FLOW_VISION = { - FLOWVISION: "xtls-rprx-vision", -} - -const TLS_VERSION_OPTION = { - TLS10: "1.0", - TLS11: "1.1", - TLS12: "1.2", - TLS13: "1.3", -}; - -const TLS_CIPHER_OPTION = { - AES_128_GCM: "TLS_AES_128_GCM_SHA256", - AES_256_GCM: "TLS_AES_256_GCM_SHA384", - CHACHA20_POLY1305: "TLS_CHACHA20_POLY1305_SHA256", - ECDHE_ECDSA_AES_128_CBC: "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA", - ECDHE_ECDSA_AES_256_CBC: "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA", - ECDHE_ECDSA_AES_128_GCM: "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", - ECDHE_ECDSA_AES_256_GCM: "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", - ECDHE_RSA_AES_128_GCM: "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", - ECDHE_RSA_AES_256_GCM: "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", - ECDHE_ECDSA_CHACHA20_POLY1305: "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256", - ECDHE_RSA_CHACHA20_POLY1305: "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256", -}; - -const ALPN_OPTION = { - H3: "h3", - H2: "h2", - HTTP1: "http/1.1", -}; - -const TCP_CONGESTION = { - bbr: "bbr", - cubic: "cubic", - reno: "reno", -} - -const DOMAIN_STRATEGY = { - AsIs: "AsIs", - UseIP: "UseIP", - UseIPv4: "UseIPv4", - UseIPv6: "UseIPv6", - UseIPv6v4: "UseIPv6v4", - UseIPv4v6: "UseIPv4v6", - ForceIP: "ForceIP", - ForceIPv6v4: "ForceIPv6v4", - ForceIPv6: "ForceIPv6", - ForceIPv4v6: "ForceIPv4v6", - ForceIPv4: "ForceIPv4", -} - -const UTLS_FINGERPRINT = { - UTLS_CHROME: "chrome", - UTLS_FIREFOX: "firefox", - UTLS_SAFARI: "safari", - UTLS_IOS: "ios", - UTLS_android: "android", - UTLS_EDGE: "edge", - UTLS_360: "360", - UTLS_QQ: "qq", - UTLS_RANDOM: "random", - UTLS_RANDOMIZED: "randomized", -}; - -const SNIFFING_OPTION = { - HTTP: "http", - TLS: "tls", - QUIC: "quic", - FAKEDNS: "fakedns" -}; - -const MODE_OPTION = { - AUTO: "auto", - PACKET_UP: "packet-up", - STREAM_UP: "stream-up", - STREAM_ONE: "stream-one", -}; - -Object.freeze(Protocols); -Object.freeze(VmessMethods); -Object.freeze(SSMethods); -Object.freeze(RULE_IP); -Object.freeze(RULE_DOMAIN); -Object.freeze(FLOW_VISION); -Object.freeze(TLS_VERSION_OPTION); -Object.freeze(TLS_CIPHER_OPTION); -Object.freeze(ALPN_OPTION); -Object.freeze(TCP_CONGESTION); -Object.freeze(DOMAIN_STRATEGY); -Object.freeze(SNIFFING_OPTION); -Object.freeze(MODE_OPTION); - -class XrayCommonClass { - - static toJsonArray(arr) { - return arr.map(obj => obj.toJson()); - } - - static fromJson() { - return new XrayCommonClass(); - } - - toJson() { - return this; - } - - toString(format = true) { - return format ? JSON.stringify(this.toJson(), null, 2) : JSON.stringify(this.toJson()); - } - - static toHeaders(v2Headers) { - let newHeaders = []; - if (v2Headers) { - Object.keys(v2Headers).forEach(key => { - let values = v2Headers[key]; - if (typeof (values) === 'string') { - newHeaders.push({ name: key, value: values }); - } else { - for (let i = 0; i < values.length; ++i) { - newHeaders.push({ name: key, value: values[i] }); - } - } - }); - } - return newHeaders; - } - - static toV2Headers(headers, arr = true) { - let v2Headers = {}; - for (let i = 0; i < headers.length; ++i) { - let name = headers[i].name; - let value = headers[i].value; - if (ObjectUtil.isEmpty(name) || ObjectUtil.isEmpty(value)) { - continue; - } - if (!(name in v2Headers)) { - v2Headers[name] = arr ? [value] : value; - } else { - if (arr) { - v2Headers[name].push(value); - } else { - v2Headers[name] = value; - } - } - } - return v2Headers; - } -} - - -class TcpStreamSettings extends XrayCommonClass { - constructor(type = 'none', - request = new TcpStreamSettings.TcpRequest(), - response = new TcpStreamSettings.TcpResponse(), - ) { - super(); - this.type = type; - this.request = request; - this.response = response; - } - - static fromJson(json = {}) { - let header = json.header; - if (!header) { - header = {}; - } - return new TcpStreamSettings( - header.type, - TcpStreamSettings.TcpRequest.fromJson(header.request), - TcpStreamSettings.TcpResponse.fromJson(header.response), - ); - } - - toJson() { - return { - header: { - type: this.type, - request: this.type === 'http' ? this.request.toJson() : undefined, - response: this.type === 'http' ? this.response.toJson() : undefined, - }, - }; - - } -} - -TcpStreamSettings.TcpRequest = class extends XrayCommonClass { - constructor(version = '1.1', - method = 'GET', - path = ['/'], - headers = [], - ) { - super(); - this.version = version; - this.method = method; - this.path = path.length === 0 ? ['/'] : path; - this.headers = headers; - } - - addPath(path) { - this.path.push(path); - } - - removePath(index) { - this.path.splice(index, 1); - } - - addHeader(name, value) { - this.headers.push({ name: name, value: value }); - } - - getHeader(name) { - for (const header of this.headers) { - if (header.name.toLowerCase() === name.toLowerCase()) { - return header.value; - } - } - return null; - } - - removeHeader(index) { - this.headers.splice(index, 1); - } - - static fromJson(json = {}) { - return new TcpStreamSettings.TcpRequest( - json.version, - json.method, - json.path, - XrayCommonClass.toHeaders(json.headers), - ); - } - - toJson() { - return { - method: this.method, - path: ObjectUtil.clone(this.path), - headers: XrayCommonClass.toV2Headers(this.headers), - }; - } -}; - -TcpStreamSettings.TcpResponse = class extends XrayCommonClass { - constructor(version = '1.1', - status = '200', - reason = 'OK', - headers = [], - ) { - super(); - this.version = version; - this.status = status; - this.reason = reason; - this.headers = headers; - } - - addHeader(name, value) { - this.headers.push({ name: name, value: value }); - } - - removeHeader(index) { - this.headers.splice(index, 1); - } - - static fromJson(json = {}) { - return new TcpStreamSettings.TcpResponse( - json.version, - json.status, - json.reason, - XrayCommonClass.toHeaders(json.headers), - ); - } - - toJson() { - return { - version: this.version, - status: this.status, - reason: this.reason, - headers: XrayCommonClass.toV2Headers(this.headers), - }; - } -}; - -class KcpStreamSettings extends XrayCommonClass { - constructor(mtu = 1350, tti = 20, - uplinkCapacity = 5, - downlinkCapacity = 20, - congestion = false, - readBufferSize = 2, - writeBufferSize = 2, - type = 'none', - seed = RandomUtil.randomSeq(10), - ) { - super(); - this.mtu = mtu; - this.tti = tti; - this.upCap = uplinkCapacity; - this.downCap = downlinkCapacity; - this.congestion = congestion; - this.readBuffer = readBufferSize; - this.writeBuffer = writeBufferSize; - this.type = type; - this.seed = seed; - } - - static fromJson(json = {}) { - return new KcpStreamSettings( - json.mtu, - json.tti, - json.uplinkCapacity, - json.downlinkCapacity, - json.congestion, - json.readBufferSize, - json.writeBufferSize, - ObjectUtil.isEmpty(json.header) ? 'none' : json.header.type, - json.seed, - ); - } - - toJson() { - return { - mtu: this.mtu, - tti: this.tti, - uplinkCapacity: this.upCap, - downlinkCapacity: this.downCap, - congestion: this.congestion, - readBufferSize: this.readBuffer, - writeBufferSize: this.writeBuffer, - header: { - type: this.type, - }, - seed: this.seed, - }; - } -} - -class WsStreamSettings extends XrayCommonClass { - constructor(path = '/', headers = []) { - super(); - this.path = path; - this.headers = headers; - } - - addHeader(name, value) { - this.headers.push({ name: name, value: value }); - } - - getHeader(name) { - for (const header of this.headers) { - if (header.name.toLowerCase() === name.toLowerCase()) { - return header.value; - } - } - return null; - } - - removeHeader(index) { - this.headers.splice(index, 1); - } - - static fromJson(json = {}) { - return new WsStreamSettings( - json.path, - XrayCommonClass.toHeaders(json.headers), - ); - } - - toJson() { - return { - path: this.path, - headers: XrayCommonClass.toV2Headers(this.headers, false), - }; - } -} - -class GrpcStreamSettings extends XrayCommonClass { - constructor( - serviceName = "", - authority = "", - multiMode = false, - ) { - super(); - this.serviceName = serviceName; - this.authority = authority; - this.multiMode = multiMode; - } - - static fromJson(json = {}) { - return new GrpcStreamSettings( - json.serviceName, - json.authority, - json.multiMode); - } - - toJson() { - return { - serviceName: this.serviceName, - authority: this.authority, - multiMode: this.multiMode, - } - } -} - -class HttpUpgradeStreamSettings extends XrayCommonClass { - constructor(acceptProxyProtocol = false, path = '/', host = '', headers = []) { - super(); - this.acceptProxyProtocol = acceptProxyProtocol; - this.path = path; - this.host = host; - this.headers = headers; - } - - - addHeader(name, value) { - this.headers.push({ name: name, value: value }); - } - - getHeader(name) { - for (const header of this.headers) { - if (header.name.toLowerCase() === name.toLowerCase()) { - return header.value; - } - } - return null; - } - - removeHeader(index) { - this.headers.splice(index, 1); - } - - static fromJson(json = {}) { - return new HttpUpgradeStreamSettings( - json.acceptProxyProtocol, - json.path, - json.host, - XrayCommonClass.toHeaders(json.headers), - ); - } - - toJson() { - return { - acceptProxyProtocol: this.acceptProxyProtocol, - path: this.path, - host: this.host, - headers: XrayCommonClass.toV2Headers(this.headers, false), - }; - } -} - -class xHTTPStreamSettings extends XrayCommonClass { - constructor( - path = '/', - host = '', - headers = [], - scMaxEachPostBytes = 1000000, - scMaxConcurrentPosts = 100, - scMinPostsIntervalMs = 30, - noSSEHeader = false, - xPaddingBytes = "100-1000", - xmux = { - maxConnections: '16-32', - maxConcurrency: 0, - cMaxReuseTimes: '64-128', - cMaxLifetimeMs: 0 - }, - mode = MODE_OPTION.AUTO, - noGRPCHeader = false, - keepAlivePeriod = 45, - ) { - super(); - this.path = path; - this.host = host; - this.headers = headers; - this.scMaxEachPostBytes = scMaxEachPostBytes; - this.scMaxConcurrentPosts = scMaxConcurrentPosts; - this.scMinPostsIntervalMs = scMinPostsIntervalMs; - this.noSSEHeader = noSSEHeader; - this.xPaddingBytes = RandomUtil.convertXPaddingBytes(xPaddingBytes); - this.xmux = xmux; - this.mode = mode; - this.noGRPCHeader = noGRPCHeader; - this.keepAlivePeriod = keepAlivePeriod; - } - - addHeader(name, value) { - this.headers.push({ name: name, value: value }); - } - getHeader(name) { - for (const header of this.headers) { - if (header.name.toLowerCase() === name.toLowerCase()) { - return header.value; - } - } - return null; - } - removeHeader(index) { - this.headers.splice(index, 1); - } - - static fromJson(json = {}) { - return new xHTTPStreamSettings( - json.path, - json.host, - XrayCommonClass.toHeaders(json.headers), - json.scMaxEachPostBytes, - json.scMaxConcurrentPosts, - json.scMinPostsIntervalMs, - json.noSSEHeader, - json.xPaddingBytes, - json.xmux, - json.mode, - json.noGRPCHeader, - json.keepAlivePeriod, - ); - } - - toJson() { - const xmuxData = {}; - if (!ObjectUtil.isEmpty(this.xmux.maxConnections)) { - xmuxData.maxConnections = RandomUtil.convertXPaddingBytes(this.xmux.maxConnections); - } - if (!ObjectUtil.isEmpty(this.xmux.maxConcurrency)) { - xmuxData.maxConcurrency = RandomUtil.convertXPaddingBytes(this.xmux.maxConcurrency); - } - xmuxData.cMaxReuseTimes = RandomUtil.convertXPaddingBytes(this.xmux.cMaxReuseTimes); - xmuxData.cMaxLifetimeMs = RandomUtil.convertXPaddingBytes(this.xmux.cMaxLifetimeMs); - return { - path: this.path, - host: this.host, - headers: XrayCommonClass.toV2Headers(this.headers, false), - scMaxEachPostBytes: this.scMaxEachPostBytes, - scMaxConcurrentPosts: this.scMaxConcurrentPosts, - scMinPostsIntervalMs: this.scMinPostsIntervalMs, - noSSEHeader: this.noSSEHeader, - xPaddingBytes: RandomUtil.convertXPaddingBytes(this.xPaddingBytes), - xmux: xmuxData, - mode: this.mode, - noGRPCHeader: this.noGRPCHeader, - keepAlivePeriod: this.keepAlivePeriod, - }; - } -} - -class TlsStreamSettings extends XrayCommonClass { - constructor(serverName = '', - rejectUnknownSni = false, - minVersion = TLS_VERSION_OPTION.TLS10, - maxVersion = TLS_VERSION_OPTION.TLS12, - cipherSuites = '', - certificates = [new TlsStreamSettings.Cert()], alpn = [''], - settings = [new TlsStreamSettings.Settings()]) { - super(); - this.server = serverName; - this.rejectUnknownSni = rejectUnknownSni; - this.minVersion = minVersion; - this.maxVersion = maxVersion; - this.cipherSuites = cipherSuites instanceof Array ? cipherSuites.join(':') : cipherSuites.split(':'); - this.certs = certificates; - this.alpn = alpn; - this.settings = settings; - } - - addCert(cert) { - this.certs.push(cert); - } - - removeCert(index) { - this.certs.splice(index, 1); - } - - static fromJson(json = {}) { - let certs; - let settings; - if (!ObjectUtil.isEmpty(json.certificates)) { - certs = json.certificates.map(cert => TlsStreamSettings.Cert.fromJson(cert)); - } - - if (!ObjectUtil.isEmpty(json.settings)) { - let values = json.settings[0]; - settings = [new TlsStreamSettings.Settings(values.allowInsecure, values.fingerprint, values.serverName)]; - } - - return new TlsStreamSettings( - json.serverName, - json.rejectUnknownSni, - json.minVersion, - json.maxVersion, - json.cipherSuites, - certs, - json.alpn, - settings, - ); - } - - toJson() { - return { - serverName: this.server, - rejectUnknownSni: this.rejectUnknownSni, - minVersion: this.minVersion, - maxVersion: this.maxVersion, - cipherSuites: this.cipherSuites instanceof Array ? this.cipherSuites.join(':') : this.cipherSuites.split(':'), - certificates: TlsStreamSettings.toJsonArray(this.certs), - alpn: this.alpn, - settings: TlsStreamSettings.toJsonArray(this.settings), - }; - } -} - -TlsStreamSettings.Cert = class extends XrayCommonClass { - constructor(useFile = true, ocspStapling = 3600, certificateFile = '', keyFile = '', certificate = '', key = '') { - super(); - this.useFile = useFile; - this.ocspStapling = ocspStapling; - this.certFile = certificateFile; - this.keyFile = keyFile; - this.cert = certificate instanceof Array ? certificate.join('\n') : certificate; - this.key = key instanceof Array ? key.join('\n') : key; - } - - static fromJson(json = {}) { - if ('certificateFile' in json && 'keyFile' in json) { - return new TlsStreamSettings.Cert( - true, - json.ocspStapling, - json.certificateFile, - json.keyFile, - ); - } else { - return new TlsStreamSettings.Cert( - false, - json.ocspStapling, - '', '', - json.certificate.join('\n'), - json.key.join('\n'), - ); - } - } - - toJson() { - if (this.useFile) { - return { - ocspStapling: this.ocspStapling, - certificateFile: this.certFile, - keyFile: this.keyFile, - }; - } else { - return { - ocspStapling: this.ocspStapling, - certificate: this.cert.split('\n'), - key: this.key.split('\n'), - }; - } - } -}; - -TlsStreamSettings.Settings = class extends XrayCommonClass { - constructor(allowInsecure = false, fingerprint = '', serverName = '') { - super(); - this.allowInsecure = allowInsecure; - this.fingerprint = fingerprint; - this.serverName = serverName; - } - static fromJson(json = {}) { - return new TlsStreamSettings.Settings( - json.allowInsecure, - json.fingerprint, - json.servername, - ); - } - toJson() { - return { - allowInsecure: this.allowInsecure, - fingerprint: this.fingerprint, - serverName: this.serverName, - }; - } -}; - -class ReaLITyStreamSettings extends XrayCommonClass { - constructor(show = false, - fingerprint = UTLS_FINGERPRINT.UTLS_CHROME, - dest = 'www.lovelive-anime.jp:443', - xver = 0, - serverNames = 'lovelive-anime.jp\nwww.lovelive-anime.jp', - privateKey = '', - publicKey = '', - minClientVer = '', - maxClientVer = '', - maxTimeDiff = 0, - shortIds = RandomUtil.randowShortId(), - ) { - super(); - if (privateKey === '') { - RandomUtil.xraysecretkey().then((res) => { - this.privateKey = res.data.obj.key - this.publicKey = res.data.obj.value - }) - } else { - this.privateKey = privateKey - this.publicKey = publicKey - } - this.show = show; - this.fingerprint = fingerprint; - this.dest = dest; - this.xver = xver; - this.serverNames = serverNames instanceof Array ? serverNames.join('\n') : serverNames; - this.minClientVer = minClientVer; - this.maxClientVer = maxClientVer; - this.maxTimeDiff = maxTimeDiff; - this.shortIds = shortIds instanceof Array ? shortIds.join('\n') : shortIds; - - } - - static fromJson(json = {}) { - return new ReaLITyStreamSettings( - json.show, - json.fingerprint, - json.dest, - json.xver, - json.serverNames, - json.privateKey, - json.publicKey, - json.minClientVer, - json.maxClientVer, - json.maxTimeDiff, - json.shortIds, - ); - } - - toJson() { - return { - show: this.show, - fingerprint: this.fingerprint, - dest: this.dest, - xver: this.xver, - serverNames: this.serverNames.split('\n'), - privateKey: this.privateKey, - publicKey: this.publicKey, - minClientVer: this.minClientVer, - maxClientVer: this.maxClientVer, - maxTimeDiff: this.maxTimeDiff, - shortIds: this.shortIds.split('\n'), - }; - } -} - -class SockoptStreamSettings extends XrayCommonClass { - constructor(mark = 0, - tcpMaxSeg = 1440, - tcpFastOpen = false, - tproxy = "off", - domainStrategy = DOMAIN_STRATEGY.AsIs, - dialerProxy = "", - acceptProxyProtocol = false, - tcpKeepAliveInterval = 0, - tcpKeepAliveIdle = 0, - tcpUserTimeout = 10000, - tcpcongestion = "", - _interface = "", - V6Only = false, - tcpWindowClamp = 600, - TcpMptcp = true, - tcpNoDelay = false, - ) { - super(); - this.mark = mark; - this.tcpMaxSeg = tcpMaxSeg; - this.tcpFastOpen = tcpFastOpen; - this.tproxy = tproxy; - this.domainStrategy = domainStrategy; - this.dialerProxy = dialerProxy; - this.acceptProxyProtocol = acceptProxyProtocol; - this.tcpKeepAliveInterval = tcpKeepAliveInterval; - this.tcpKeepAliveIdle = tcpKeepAliveIdle; - this.tcpUserTimeout = tcpUserTimeout; - this.tcpcongestion = tcpcongestion; - this.interface = _interface instanceof Array ? this.interface : _interface; - this.V6Only = V6Only; - this.tcpWindowClamp = tcpWindowClamp; - this.TcpMptcp = TcpMptcp; - this.tcpNoDelay = tcpNoDelay; - } - - static fromJson(json = {}) { - if (Object.keys(json).length === 0) return undefined; - return new SockoptStreamSettings( - json.mark, - json.tcpMaxSeg, - json.tcpFastOpen, - json.tproxy, - json.domainStrategy, - json.dialerProxy, - json.acceptProxyProtocol, - json.tcpKeepAliveInterval, - json.tcpKeepAliveIdle, - json.tcpUserTimeout, - json.tcpcongestion, - json.interface, - json.V6Only, - json.tcpWindowClamp, - json.TcpMptcp, - json.tcpNoDelay, - ); - } - - toJson() { - return { - mark: this.mark, - tcpMaxSeg: this.tcpMaxSeg, - tcpFastOpen: this.tcpFastOpen, - tproxy: this.tproxy, - domainStrategy: this.domainStrategy, - dialerProxy: this.dialerProxy, - acceptProxyProtocol: this.acceptProxyProtocol, - tcpKeepAliveInterval: this.tcpKeepAliveInterval, - tcpKeepAliveIdle: this.tcpKeepAliveIdle, - tcpUserTimeout: this.tcpUserTimeout, - tcpcongestion: this.tcpcongestion, - interface: this.interface, - V6Only: this.V6Only, - tcpWindowClamp: this.tcpWindowClamp, - TcpMptcp: this.TcpMptcp, - tcpNoDelay: this.tcpNoDelay, - }; - } -} - - -class StreamSettings extends XrayCommonClass { - constructor(network = 'tcp', - security = 'none', - tlsSettings = new TlsStreamSettings(), - realitySettings = new ReaLITyStreamSettings(), - tcpSettings = new TcpStreamSettings(), - rawSettings = new TcpStreamSettings(), - kcpSettings = new KcpStreamSettings(), - wsSettings = new WsStreamSettings(), - grpcSettings = new GrpcStreamSettings(), - httpupgradeSettings = new HttpUpgradeStreamSettings(), - xhttpSettings = new xHTTPStreamSettings(), - sockopt = undefined, - ) { - super(); - this.network = network; - this.security = security; - this.tls = tlsSettings; - this.reality = realitySettings; - this.tcp = tcpSettings; - this.raw = rawSettings; - this.kcp = kcpSettings; - this.ws = wsSettings; - this.grpc = grpcSettings; - this.httpupgrade = httpupgradeSettings; - this.xhttp = xhttpSettings; - this.sockopt = sockopt; - } - - get isTls() { - return this.security === 'tls'; - } - - set isTls(isTls) { - if (isTls) { - this.security = 'tls'; - } else { - this.security = 'none'; - } - } - - get isReaLITy() { - return this.security === "reality"; - } - - set isReaLITy(isReaLITy) { - if (isReaLITy) { - this.security = 'reality'; - } else { - this.security = 'none'; - } - } - - get sockoptSwitch() { - return this.sockopt != undefined; - } - - set sockoptSwitch(value) { - this.sockopt = value ? new SockoptStreamSettings() : undefined; - } - - static fromJson(json = {}) { - return new StreamSettings( - json.network, - json.security, - TlsStreamSettings.fromJson(json.tlsSettings), - ReaLITyStreamSettings.fromJson(json.realitySettings), - TcpStreamSettings.fromJson(json.tcpSettings), - TcpStreamSettings.fromJson(json.rawSettings), - KcpStreamSettings.fromJson(json.kcpSettings), - WsStreamSettings.fromJson(json.wsSettings), - GrpcStreamSettings.fromJson(json.grpcSettings), - HttpUpgradeStreamSettings.fromJson(json.httpupgradeSettings), - xHTTPStreamSettings.fromJson(json.xhttpSettings), - SockoptStreamSettings.fromJson(json.sockopt), - ); - } - - toJson() { - const network = this.network; - return { - network: network, - security: this.security, - tlsSettings: this.isTls ? this.tls.toJson() : undefined, - realitySettings: this.isReaLITy ? this.reality.toJson() : undefined, - tcpSettings: network === 'tcp' ? this.tcp.toJson() : undefined, - rawSettings: network === 'raw' ? this.raw.toJson() : undefined, - kcpSettings: network === 'kcp' ? this.kcp.toJson() : undefined, - wsSettings: network === 'ws' ? this.ws.toJson() : undefined, - grpcSettings: network === 'grpc' ? this.grpc.toJson() : undefined, - httpupgradeSettings: network === 'httpupgrade' ? this.httpupgrade.toJson() : undefined, - xhttpSettings: network === 'xhttp' ? this.xhttp.toJson() : undefined, - sockopt: this.sockopt != undefined ? this.sockopt.toJson() : undefined, - }; - } -} - -class Sniffing extends XrayCommonClass { - constructor(enabled = true, destOverride = ['http', 'tls', 'quic']) { - super(); - this.enabled = enabled; - this.destOverride = destOverride; - } - - static fromJson(json = {}) { - let destOverride = ObjectUtil.clone(json.destOverride); - if (!ObjectUtil.isEmpty(destOverride) && !ObjectUtil.isArrEmpty(destOverride)) { - if (ObjectUtil.isEmpty(destOverride[0])) { - destOverride = ['http', 'tls', 'quic']; - } - } - return new Sniffing( - !!json.enabled, - destOverride, - ); - } -} - -class Inbound extends XrayCommonClass { - constructor(port = RandomUtil.randomIntRange(10000, 60000), - listen = '', - protocol = Protocols.VMESS, - settings = null, - streamSettings = new StreamSettings(), - tag = '', - sniffing = new Sniffing(), - ) { - super(); - this.port = port; - this.listen = listen; - this._protocol = protocol; - this.settings = ObjectUtil.isEmpty(settings) ? Inbound.Settings.getSettings(protocol) : settings; - this.stream = streamSettings; - this.tag = tag; - this.sniffing = sniffing; - } - - get protocol() { - return this._protocol; - } - - set protocol(protocol) { - this._protocol = protocol; - this.settings = Inbound.Settings.getSettings(protocol); - if (protocol === Protocols.TROJAN) { - if (this.network === "tcp" || this.network === "raw") { - this.tls = true; - } - } - } - - get tls() { - return this.stream.security === 'tls'; - } - - set tls(isTls) { - if (isTls) { - this.stream.security = 'tls'; - } else { - this.stream.security = 'none'; - } - - } - - get reality() { - return this.stream.security === 'reality'; - } - - set reality(isReaLITy) { - if (isReaLITy) { - this.stream.security = 'reality'; - } else { - this.stream.security = 'none'; - } - - } - - get network() { - return this.stream.network; - } - - set network(network) { - this.stream.network = network; - } - - get isTcp() { - return this.network === "tcp"; - } - get isRaw() { - return this.network === "raw"; - } - get isWs() { - return this.network === "ws"; - } - - get isKcp() { - return this.network === "kcp"; - } - - get isGrpc() { - return this.network === "grpc"; - } - - get isHttpupgrade() { - return this.network === "httpupgrade"; - } - get isXHTTP() { - return this.network === "xhttp"; - } - // VMess & VLess - get uuid() { - switch (this.protocol) { - case Protocols.VMESS: - return this.settings.vmesses[0].id; - case Protocols.VLESS: - return this.settings.vlesses[0].id; - default: - return ""; - } - } - - // VLess & Trojan - get flow() { - switch (this.protocol) { - case Protocols.VLESS: - return this.settings.vlesses[0].flow; - case Protocols.TROJAN: - return this.settings.clients[0].flow; - default: - return ""; - } - } - - // Socks & HTTP - get username() { - switch (this.protocol) { - case Protocols.SOCKS: - case Protocols.HTTP: - return this.settings.accounts[0].user; - default: - return ""; - } - } - - // Trojan & Shadowsocks & Socks & HTTP - get password() { - switch (this.protocol) { - case Protocols.TROJAN: - return this.settings.clients[0].password; - case Protocols.SHADOWSOCKS: - return this.settings.password; - case Protocols.SOCKS: - case Protocols.HTTP: - return this.settings.accounts[0].pass; - default: - return ""; - } - } - - // Shadowsocks - get method() { - switch (this.protocol) { - case Protocols.SHADOWSOCKS: - return this.settings.method; - default: - return ""; - } - } - - get serverName() { - if (this.stream.isTls) { - return this.stream.tls.server; - } - return ""; - } - - get host() { - if (this.isTcp) { - return this.stream.tcp.request.getHeader("Host"); - } else if (this.isRaw) { - return this.stream.raw.request.getHeader("Host"); - } else if (this.isWs) { - return this.stream.ws.getHeader("Host"); - } else if (this.isH2) { - return this.stream.http.host[0]; - } else if (this.isHttpupgrade) { - return this.stream.httpupgrade.host; - } else if (this.isXHTTP) { - return this.stream.xhttp.host; - } - return null; - } - - get path() { - if (this.isTcp) { - return this.stream.tcp.request.path[0]; - } else if (this.isRaw) { - return this.stream.raw.request.path[0]; - } else if (this.isWs) { - return this.stream.ws.path; - } else if (this.isH2) { - return this.stream.http.path[0]; - } else if (this.isHttpupgrade) { - return this.stream.httpupgrade.path; - } else if (this.isXHTTP) { - return this.stream.xhttp.path; - } - return null; - } - - get kcpType() { - return this.stream.kcp.type; - } - - get kcpSeed() { - return this.stream.kcp.seed; - } - - get serviceName() { - return this.stream.grpc.serviceName; - } - - canEnableTls() { - switch (this.protocol) { - case Protocols.VMESS: - case Protocols.VLESS: - case Protocols.TROJAN: - case Protocols.SHADOWSOCKS: - break; - default: - return false; - } - - switch (this.network) { - case "tcp": - case "raw": - case "ws": - case "grpc": - case "httpupgrade": - case "xhttp": - return true; - default: - return false; - } - } - - canSetTls() { - return this.canEnableTls(); - } - - canEnableReaLITy() { - switch (this.protocol) { - case Protocols.VLESS: - case Protocols.TROJAN: - break; - default: - return false; - } - return ['tcp', 'raw', 'grpc', 'httpupgrade', 'xhttp'].indexOf(this.network) !== -1; - //return this.network === "tcp"; - } - - // canSockopt() { - // switch (this.protocol) { - // case Protocols.VLESS: - // case Protocols.TROJAN: - // case Protocols.SHADOWSOCKS: - // case Protocols.VMESS: - // break; - // default: - // return false; - // } - // return ['tcp', 'http', 'grpc', 'ws'].indexOf(this.network) !== -1; - // //return this.network === "tcp"; - // } - - canEnableStream() { - switch (this.protocol) { - case Protocols.VMESS: - case Protocols.VLESS: - case Protocols.SHADOWSOCKS: - case Protocols.TROJAN: - return true; - default: - return false; - } - } - - canSniffing() { - switch (this.protocol) { - case Protocols.VMESS: - case Protocols.VLESS: - case Protocols.TROJAN: - case Protocols.SHADOWSOCKS: - return true; - default: - return false; - } - } - - reset() { - this.port = RandomUtil.randomIntRange(10000, 60000); - this.listen = ''; - this.protocol = Protocols.VMESS; - this.settings = Inbound.Settings.getSettings(Protocols.VMESS); - this.stream = new StreamSettings(); - this.tag = ''; - this.sniffing = new Sniffing(); - } - - genVmessLink(address = '', remark = '') { - if (this.protocol !== Protocols.VMESS) { - return ''; - } - let network = this.stream.network; - let type = 'none'; - let host = ''; - let path = ''; - let authority = ''; - let sni = ''; - let mode = ''; - if (network === 'tcp') { - let tcp = this.stream.tcp; - type = tcp.type; - if (type === 'http') { - let request = tcp.request; - path = request.path.join(','); - let index = request.headers.findIndex(header => header.name.toLowerCase() === 'host'); - if (index >= 0) { - host = request.headers[index].value; - } - } - } else if (network === 'raw') { - let raw = this.stream.raw; - type = raw.type; - if (type === 'http') { - let request = raw.request; - path = request.path.join(','); - let index = request.headers.findIndex(header => header.name.toLowerCase() === 'host'); - if (index >= 0) { - host = request.headers[index].value; - } - } - } else if (network === 'kcp') { - let kcp = this.stream.kcp; - type = kcp.type; - path = kcp.seed; - } else if (network === 'ws') { - let ws = this.stream.ws; - path = ws.path; - let index = ws.headers.findIndex(header => header.name.toLowerCase() === 'host'); - if (index >= 0) { - host = ws.headers[index].value; - } - } else if (network === 'grpc') { - path = this.stream.grpc.serviceName; - authority = this.stream.grpc.authority; - if (this.stream.grpc.multiMode) { - type = 'multi' - } - } else if (network === 'httpupgrade') { - let httpupgrade = this.stream.httpupgrade; - path = httpupgrade.path; - host = httpupgrade.host; - let index = httpupgrade.headers.findIndex(header => header.name.toLowerCase() === 'host'); - if (index >= 0) { - host = httpupgrade.headers[index].value; - } - } else if (network === 'xhttp') { - const xhttp = this.stream.xhttp; - path = xhttp.path; - host = xhttp.host; - let index = xhttp.headers.findIndex(header => header.name.toLowerCase() === 'host'); - if (index >= 0) { - host = xhttp.headers[index].value; - }; - mode = xhttp.mode; - } - - if (this.stream.security === 'tls') { - if (!ObjectUtil.isEmpty(this.stream.tls.server)) { - address = this.stream.tls.server; - } - if (!ObjectUtil.isEmpty(this.stream.tls.settings[0]['serverName'])) { - sni = this.stream.tls.settings[0]['serverName']; - } - } - - let obj = { - v: '2', - ps: remark, - add: address, - port: this.port, - id: this.settings.vmesses[0].id, - net: network, - type: type, - host: host, - path: path, - ...(network === 'xhttp' && { mode: mode }), - authority: authority, - tls: this.stream.security, - sni: sni, - fp: this.stream.tls.settings[0]['fingerprint'], - }; - return 'vmess://' + base64(JSON.stringify(obj, null, 2)); - } - - genVLESSLink(address = '', remark = '') { - const settings = this.settings; - const uuid = settings.vlesses[0].id; - const port = this.port; - const type = this.stream.network; - const params = new Map(); - params.set("type", this.stream.network); - if (this.reality) { - params.set("security", "reality"); - } else { - params.set("security", this.stream.security); - } - switch (type) { - case "tcp": - const tcp = this.stream.tcp; - if (tcp.type === 'http') { - const request = tcp.request; - params.set("path", request.path.join(',')); - const index = request.headers.findIndex(header => header.name.toLowerCase() === 'host'); - if (index >= 0) { - const host = request.headers[index].value; - params.set("host", host); - } - } - break; - case "raw": - const raw = this.stream.raw; - if (raw.type === 'http') { - const request = raw.request; - params.set("path", request.path.join(',')); - const index = request.headers.findIndex(header => header.name.toLowerCase() === 'host'); - if (index >= 0) { - const host = request.headers[index].value; - params.set("host", host); - } - } - break; - case "kcp": - const kcp = this.stream.kcp; - params.set("headerType", kcp.type); - params.set("seed", kcp.seed); - break; - case "ws": - const ws = this.stream.ws; - params.set("path", ws.path); - const index = ws.headers.findIndex(header => header.name.toLowerCase() === 'host'); - if (index >= 0) { - const host = ws.headers[index].value; - params.set("host", host); - } - break; - case "grpc": - const grpc = this.stream.grpc; - params.set("serviceName", grpc.serviceName); - params.set("authority", grpc.authority); - if (grpc.multiMode) { - params.set("mode", "multi"); - } - break; - case "httpupgrade": - const httpupgrade = this.stream.httpupgrade; - params.set("path", httpupgrade.path); - params.set("host", httpupgrade.host); - const httpupgradeIndex = httpupgrade.headers.findIndex(header => header.name.toLowerCase() === 'host'); - if (httpupgradeIndex >= 0) { - const host = httpupgrade.headers[httpupgradeIndex].value; - params.set("host", host); - } - break; - case "xhttp": - const xhttp = this.stream.xhttp; - params.set("path", xhttp.path); - params.set("host", xhttp.host); - const xhttpIndex = xhttp.headers.findIndex(header => header.name.toLowerCase() === 'host'); - if (xhttpIndex >= 0) { - const host = xhttp.headers[xhttpIndex].value; - params.set("host", host); - }; - params.set("mode", xhttp.mode); - break; - } - - if (this.tls) { - params.set("security", "tls"); - params.set("fp", this.stream.tls.settings[0]['fingerprint']); - params.set("alpn", this.stream.tls.alpn); - if (this.stream.tls.settings[0].allowInsecure) { - params.set("allowInsecure", "1"); - } - if (!ObjectUtil.isEmpty(this.stream.tls.server)) { - address = this.stream.tls.server; - } - if (this.stream.tls.settings[0]['serverName'] !== '') { - params.set("sni", this.stream.tls.settings[0]['serverName']); - } - if (type === "tcp" && this.settings.vlesses[0].flow.length > 0) { - params.set("flow", this.settings.vlesses[0].flow); - } - if (type === "raw" && this.settings.vlesses[0].flow.length > 0) { - params.set("flow", this.settings.vlesses[0].flow); - } - } - - if (this.stream.security === 'reality') { - if (!ObjectUtil.isArrEmpty(this.stream.reality.serverNames)) { - // params.set("sni", this.stream.reality.serverNames.split(/,|,|\s+/)[0]); - params.set("sni", ObjectUtil.generateSid(this.stream.reality.serverNames)); - } - if (this.stream.reality.publicKey != "") { - params.set("pbk", this.stream.reality.publicKey); - - } - if (this.stream.network === 'tcp') { - params.set("flow", this.settings.vlesses[0].flow); - } - if (this.stream.network === 'raw') { - params.set("flow", this.settings.vlesses[0].flow); - } - // var shortIds1 = this.stream.reality.shortIds.split(/,|,|\s+/); - // var index1 = Math.floor(Math.random() * shortIds1.length); - // var value1 = shortIds1[index1]; - params.set("sid", ObjectUtil.generateSid(this.stream.reality.shortIds)); - - if (this.stream.reality.fingerprint != "") { - params.set("fp", this.stream.reality.fingerprint); - } - } - - const link = `vless://${uuid}@${address}:${port}`; - const url = new URL(link); - for (const [key, value] of params) { - url.searchParams.set(key, value) - } - url.hash = encodeURIComponent(remark); - return url.toString(); - } - - genSSLink(address = '', forceTls, remark = '') { - let settings = this.settings; - const type = this.stream.network; - const security = forceTls == 'same' ? this.stream.security : forceTls; - const params = new Map(); - params.set("type", this.stream.network); - switch (type) { - case "tcp": - const tcp = this.stream.tcp; - if (tcp.type === 'http') { - const request = tcp.request; - params.set("path", request.path.join(',')); - const index = request.headers.findIndex(header => header.name.toLowerCase() === 'host'); - if (index >= 0) { - const host = request.headers[index].value; - params.set("host", host); - } - params.set("headerType", 'http'); - } - break; - case "raw": - const raw = this.stream.raw; - if (raw.type === 'http') { - const request = raw.request; - params.set("path", request.path.join(',')); - const index = request.headers.findIndex(header => header.name.toLowerCase() === 'host'); - if (index >= 0) { - const host = request.headers[index].value; - params.set("host", host); - } - params.set("headerType", 'http'); - } - break; - case "kcp": - const kcp = this.stream.kcp; - params.set("headerType", kcp.type); - params.set("seed", kcp.seed); - break; - case "ws": - const ws = this.stream.ws; - params.set("path", ws.path); - const index = ws.headers.findIndex(header => header.name.toLowerCase() === 'host'); - if (index >= 0) { - const host = ws.headers[index].value; - params.set("host", host); - } - break; - case "grpc": - const grpc = this.stream.grpc; - params.set("serviceName", grpc.serviceName); - params.set("authority", grpc.authority); - if (grpc.multiMode) { - params.set("mode", "multi"); - } - break; - case "httpupgrade": - const httpupgrade = this.stream.httpupgrade; - params.set("path", httpupgrade.path); - params.set("host", httpupgrade.host); - const httpupgradeIndex = httpupgrade.headers.findIndex(header => header.name.toLowerCase() === 'host'); - if (httpupgradeIndex >= 0) { - const host = httpupgrade.headers[httpupgradeIndex].value; - params.set("host", host); - } - break; - case "xhttp": - const xhttp = this.stream.xhttp; - params.set("path", xhttp.path); - params.set("host", xhttp.host); - const xhttpIndex = xhttp.headers.findIndex(header => header.name.toLowerCase() === 'host'); - if (xhttpIndex >= 0) { - const host = xhttp.headers[xhttpIndex].value; - params.set("host", host); - }; - params.set("mode", xhttp.mode); - break; - } - - if (security === 'tls') { - params.set("security", "tls"); - if (this.stream.isTls) { - params.set("fp", this.stream.tls.settings.fingerprint); - params.set("alpn", this.stream.tls.alpn); - if (this.stream.tls.settings.allowInsecure) { - params.set("allowInsecure", "1"); - } - if (!ObjectUtil.isEmpty(this.stream.tls.sni)) { - params.set("sni", this.stream.tls.sni); - } - } - } - - - if (settings.method == SSMethods.BLAKE3_AES_128_GCM || settings.method == SSMethods.BLAKE3_AES_256_GCM || settings.method == SSMethods.BLAKE3_CHACHA20_POLY1305) { - const link = `ss://${settings.method}:${settings.password}@${address}:${this.port}#${encodeURIComponent(remark)}`; - - const url = new URL(link); - for (const [key, value] of params) { - url.searchParams.set(key, value) - } - url.hash = encodeURIComponent(remark); - return url.toString(); - } else { - const link = 'ss://' + safeBase64(settings.method + ':' + settings.password + '@' + address + ':' + this.port) - + '#' + encodeURIComponent(remark); - - const url = new URL(link); - for (const [key, value] of params) { - url.searchParams.set(key, value) - } - url.hash = encodeURIComponent(remark); - return url.toString(); - } - - } - genTrojanLink(address = '', remark = '') { - let settings = this.settings; - const port = this.port; - const type = this.stream.network; - const params = new Map(); - params.set("type", this.stream.network); - if (this.reality) { - params.set("security", "reality"); - } else { - params.set("security", this.stream.security); - } - switch (type) { - case "tcp": - const tcp = this.stream.tcp; - if (tcp.type === 'http') { - const request = tcp.request; - params.set("path", request.path.join(',')); - const index = request.headers.findIndex(header => header.name.toLowerCase() === 'host'); - if (index >= 0) { - const host = request.headers[index].value; - params.set("host", host); - } - } - break; - case "raw": - const raw = this.stream.raw; - if (raw.type === 'http') { - const request = raw.request; - params.set("path", request.path.join(',')); - const index = request.headers.findIndex(header => header.name.toLowerCase() === 'host'); - if (index >= 0) { - const host = request.headers[index].value; - params.set("host", host); - } - } - break; - case "kcp": - const kcp = this.stream.kcp; - params.set("headerType", kcp.type); - params.set("seed", kcp.seed); - break; - case "ws": - const ws = this.stream.ws; - params.set("path", ws.path); - const index = ws.headers.findIndex(header => header.name.toLowerCase() === 'host'); - if (index >= 0) { - const host = ws.headers[index].value; - params.set("host", host); - } - break; - case "grpc": - const grpc = this.stream.grpc; - params.set("serviceName", grpc.serviceName); - params.set("authority", grpc.authority); - if (grpc.multiMode) { - params.set("mode", "multi"); - } - break; - case "httpupgrade": - const httpupgrade = this.stream.httpupgrade; - params.set("path", httpupgrade.path); - params.set("host", httpupgrade.host); - const httpUpgradeIndex = httpupgrade.headers.findIndex(header => header.name.toLowerCase() === 'host'); - if (httpUpgradeIndex >= 0) { - const host = httpupgrade.headers[httpUpgradeIndex].value; - params.set("host", host); - } - break; - case "xhttp": - const xhttp = this.stream.xhttp; - params.set("path", xhttp.path); - params.set("host", xhttp.host); - const xhttpIndex = xhttp.headers.findIndex(header => header.name.toLowerCase() === 'host'); - if (xhttpIndex >= 0) { - const host = xhttp.headers[xhttpIndex].value; - params.set("host", host); - }; - params.set("mode", xhttp.mode); - break; - } - - if (this.tls) { - params.set("security", "tls"); - params.set("fp", this.stream.tls.settings[0]['fingerprint']); - params.set("alpn", this.stream.tls.alpn); - if (this.stream.tls.settings[0].allowInsecure) { - params.set("allowInsecure", "1"); - } - if (!ObjectUtil.isEmpty(this.stream.tls.server)) { - address = this.stream.tls.server; - } - if (this.stream.tls.settings[0]['serverName'] !== '') { - params.set("sni", this.stream.tls.settings[0]['serverName']); - } - } - - if (this.stream.security === 'reality') { - if (!ObjectUtil.isArrEmpty(this.stream.reality.serverNames)) { - // params.set("sni", this.stream.reality.serverNames.split(/,|,|\s+/)[0]); - params.set("sni", ObjectUtil.generateSid(this.stream.reality.serverNames)); - } - if (this.stream.reality.publicKey != "") { - params.set("pbk", this.stream.reality.publicKey); - } - - // var shortIds1 = this.stream.reality.shortIds.split(/,|,|\s+/); - // var index1 = Math.floor(Math.random() * shortIds1.length); - // var value1 = shortIds1[index1]; - params.set("sid", ObjectUtil.generateSid(this.stream.reality.shortIds)); - - if (this.stream.reality.fingerprint != "") { - params.set("fp", this.stream.reality.fingerprint); - } - } - const link = `trojan://${settings.clients[0].password}@${address}:${port}`; - const url = new URL(link); - for (const [key, value] of params) { - url.searchParams.set(key, value) - } - url.hash = encodeURIComponent(remark); - return url.toString(); - } - - genLink(address = '', remark = '') { - switch (this.protocol) { - case Protocols.VMESS: return this.genVmessLink(address, remark); - case Protocols.VLESS: return this.genVLESSLink(address, remark); - case Protocols.SHADOWSOCKS: return this.genSSLink(address, remark); - case Protocols.TROJAN: return this.genTrojanLink(address, remark); - default: return ''; - } - } - - static fromJson(json = {}) { - return new Inbound( - json.port, - json.listen, - json.protocol, - Inbound.Settings.fromJson(json.protocol, json.settings), - StreamSettings.fromJson(json.streamSettings), - json.tag, - Sniffing.fromJson(json.sniffing), - ) - } - - toJson() { - let streamSettings; - if (this.canEnableStream() || this.protocol === Protocols.TROJAN) { - streamSettings = this.stream.toJson(); - } - return { - port: this.port, - listen: this.listen, - protocol: this.protocol, - settings: this.settings instanceof XrayCommonClass ? this.settings.toJson() : this.settings, - streamSettings: streamSettings, - tag: this.tag, - sniffing: this.sniffing.toJson(), - }; - } -} - -Inbound.Settings = class extends XrayCommonClass { - constructor(protocol) { - super(); - this.protocol = protocol; - } - - static getSettings(protocol) { - switch (protocol) { - case Protocols.VMESS: return new Inbound.VmessSettings(protocol); - case Protocols.VLESS: return new Inbound.VLESSSettings(protocol); - case Protocols.TROJAN: return new Inbound.TrojanSettings(protocol); - case Protocols.SHADOWSOCKS: return new Inbound.ShadowsocksSettings(protocol); - case Protocols.DOKODEMO: return new Inbound.DokodemoSettings(protocol); - case Protocols.SOCKS: return new Inbound.SocksSettings(protocol); - case Protocols.HTTP: return new Inbound.HttpSettings(protocol); - default: return null; - } - } - - static fromJson(protocol, json) { - switch (protocol) { - case Protocols.VMESS: return Inbound.VmessSettings.fromJson(json); - case Protocols.VLESS: return Inbound.VLESSSettings.fromJson(json); - case Protocols.TROJAN: return Inbound.TrojanSettings.fromJson(json); - case Protocols.SHADOWSOCKS: return Inbound.ShadowsocksSettings.fromJson(json); - case Protocols.DOKODEMO: return Inbound.DokodemoSettings.fromJson(json); - case Protocols.SOCKS: return Inbound.SocksSettings.fromJson(json); - case Protocols.HTTP: return Inbound.HttpSettings.fromJson(json); - default: return null; - } - } - - toJson() { - return {}; - } -}; - -Inbound.VmessSettings = class extends Inbound.Settings { - constructor(protocol, - vmesses = [new Inbound.VmessSettings.Vmess()], - disableInsecureEncryption = false) { - super(protocol); - this.vmesses = vmesses; - this.disableInsecure = disableInsecureEncryption; - } - - indexOfVmessById(id) { - return this.vmesses.findIndex(vmess => vmess.id === id); - } - - addVmess(vmess) { - if (this.indexOfVmessById(vmess.id) >= 0) { - return false; - } - this.vmesses.push(vmess); - } - - delVmess(vmess) { - const i = this.indexOfVmessById(vmess.id); - if (i >= 0) { - this.vmesses.splice(i, 1); - } - } - - static fromJson(json = {}) { - return new Inbound.VmessSettings( - Protocols.VMESS, - json.clients.map(client => Inbound.VmessSettings.Vmess.fromJson(client)), - ObjectUtil.isEmpty(json.disableInsecureEncryption) ? false : json.disableInsecureEncryption, - ); - } - - toJson() { - return { - clients: Inbound.VmessSettings.toJsonArray(this.vmesses), - disableInsecureEncryption: this.disableInsecure, - }; - } -}; -Inbound.VmessSettings.Vmess = class extends XrayCommonClass { - constructor(id = RandomUtil.randomUUID()) { - super(); - this.id = id; - } - - static fromJson(json = {}) { - return new Inbound.VmessSettings.Vmess( - json.id, - ); - } -}; - -Inbound.VLESSSettings = class extends Inbound.Settings { - constructor(protocol, - vlesses = [new Inbound.VLESSSettings.VLESS()], - decryption = 'none', - fallbacks = [],) { - super(protocol); - this.vlesses = vlesses; - this.decryption = decryption; - this.fallbacks = fallbacks; - } - - addFallback() { - this.fallbacks.push(new Inbound.VLESSSettings.Fallback()); - } - - delFallback(index) { - this.fallbacks.splice(index, 1); - } - - static fromJson(json = {}) { - return new Inbound.VLESSSettings( - Protocols.VLESS, - json.clients.map(client => Inbound.VLESSSettings.VLESS.fromJson(client)), - json.decryption, - Inbound.VLESSSettings.Fallback.fromJson(json.fallbacks), - ); - } - - toJson() { - return { - clients: Inbound.VLESSSettings.toJsonArray(this.vlesses), - decryption: this.decryption, - fallbacks: Inbound.VLESSSettings.toJsonArray(this.fallbacks), - }; - } -}; - -Inbound.VLESSSettings.VLESS = class extends XrayCommonClass { - - constructor(id = RandomUtil.randomUUID(), flow = "") { - super(); - this.id = id; - this.flow = flow; - } - - static fromJson(json = {}) { - return new Inbound.VLESSSettings.VLESS( - json.id, - json.flow, - ); - } -}; -Inbound.VLESSSettings.Fallback = class extends XrayCommonClass { - constructor(name = "", alpn = '', path = '', dest = '', xver = 0) { - super(); - this.name = name; - this.alpn = alpn; - this.path = path; - this.dest = dest; - this.xver = xver; - } - - toJson() { - let xver = this.xver; - if (!Number.isInteger(xver)) { - xver = 0; - } - return { - name: this.name, - alpn: this.alpn, - path: this.path, - dest: this.dest, - xver: xver, - } - } - - static fromJson(json = []) { - const fallbacks = []; - for (let fallback of json) { - fallbacks.push(new Inbound.VLESSSettings.Fallback( - fallback.name, - fallback.alpn, - fallback.path, - fallback.dest, - fallback.xver, - )) - } - return fallbacks; - } -}; - -Inbound.TrojanSettings = class extends Inbound.Settings { - constructor(protocol, - clients = [new Inbound.TrojanSettings.Client()], - fallbacks = [],) { - super(protocol); - this.clients = clients; - this.fallbacks = fallbacks; - } - - addTrojanFallback() { - this.fallbacks.push(new Inbound.TrojanSettings.Fallback()); - } - - delTrojanFallback(index) { - this.fallbacks.splice(index, 1); - } - - toJson() { - return { - clients: Inbound.TrojanSettings.toJsonArray(this.clients), - fallbacks: Inbound.TrojanSettings.toJsonArray(this.fallbacks), - }; - } - - static fromJson(json = {}) { - const clients = []; - for (const c of json.clients) { - clients.push(Inbound.TrojanSettings.Client.fromJson(c)); - } - return new Inbound.TrojanSettings( - Protocols.TROJAN, - clients, - Inbound.TrojanSettings.Fallback.fromJson(json.fallbacks)); - } -}; -Inbound.TrojanSettings.Client = class extends XrayCommonClass { - constructor(password = RandomUtil.randomSeq(10)) { - super(); - this.password = password; - //this.flow = flow; - } - - toJson() { - return { - password: this.password, - // flow: this.flow, - }; - } - - static fromJson(json = {}) { - return new Inbound.TrojanSettings.Client( - json.password, - // json.flow, - ); - } - -}; - -Inbound.TrojanSettings.Fallback = class extends XrayCommonClass { - constructor(name = "", alpn = '', path = '', dest = '', xver = 0) { - super(); - this.name = name; - this.alpn = alpn; - this.path = path; - this.dest = dest; - this.xver = xver; - } - - toJson() { - let xver = this.xver; - if (!Number.isInteger(xver)) { - xver = 0; - } - return { - name: this.name, - alpn: this.alpn, - path: this.path, - dest: this.dest, - xver: xver, - } - } - - static fromJson(json = []) { - const fallbacks = []; - for (let fallback of json) { - fallbacks.push(new Inbound.TrojanSettings.Fallback( - fallback.name, - fallback.alpn, - fallback.path, - fallback.dest, - fallback.xver, - )) - } - return fallbacks; - } -}; - -Inbound.ShadowsocksSettings = class extends Inbound.Settings { - constructor(protocol, - method = SSMethods.AES_256_GCM, - password = btoa(RandomUtil.randomSeq(32)), - network = 'tcp,udp' - ) { - super(protocol); - this.method = method; - this.password = password; - this.network = network; - } - - static fromJson(json = {}) { - return new Inbound.ShadowsocksSettings( - Protocols.SHADOWSOCKS, - json.method, - json.password, - json.network, - ); - } - - toJson() { - return { - method: this.method, - password: this.password, - network: this.network, - }; - } -}; - -Inbound.DokodemoSettings = class extends Inbound.Settings { - constructor(protocol, address, port, network = 'tcp,udp') { - super(protocol); - this.address = address; - this.port = port; - this.network = network; - } - - static fromJson(json = {}) { - return new Inbound.DokodemoSettings( - Protocols.DOKODEMO, - json.address, - json.port, - json.network, - ); - } - - toJson() { - return { - address: this.address, - port: this.port, - network: this.network, - }; - } -}; - -Inbound.SocksSettings = class extends Inbound.Settings { - constructor(protocol, auth = 'password', accounts = [new Inbound.SocksSettings.SocksAccount()], udp = false, ip = '127.0.0.1') { - super(protocol); - this.auth = auth; - this.accounts = accounts; - this.udp = udp; - this.ip = ip; - } - - addAccount(account) { - this.accounts.push(account); - } - - delAccount(index) { - this.accounts.splice(index, 1); - } - - static fromJson(json = {}) { - let accounts; - if (json.auth === 'password') { - accounts = json.accounts.map( - account => Inbound.SocksSettings.SocksAccount.fromJson(account) - ) - } - return new Inbound.SocksSettings( - Protocols.SOCKS, - json.auth, - accounts, - json.udp, - json.ip, - ); - } - - toJson() { - return { - auth: this.auth, - accounts: this.auth === 'password' ? this.accounts.map(account => account.toJson()) : undefined, - udp: this.udp, - ip: this.ip, - }; - } -}; -Inbound.SocksSettings.SocksAccount = class extends XrayCommonClass { - constructor(user = RandomUtil.randomSeq(10), pass = RandomUtil.randomSeq(10)) { - super(); - this.user = user; - this.pass = pass; - } - - static fromJson(json = {}) { - return new Inbound.SocksSettings.SocksAccount(json.user, json.pass); - } -}; - -Inbound.HttpSettings = class extends Inbound.Settings { - constructor(protocol, accounts = [new Inbound.HttpSettings.HttpAccount()]) { - super(protocol); - this.accounts = accounts; - } - - addAccount(account) { - this.accounts.push(account); - } - - delAccount(index) { - this.accounts.splice(index, 1); - } - - static fromJson(json = {}) { - return new Inbound.HttpSettings( - Protocols.HTTP, - json.accounts.map(account => Inbound.HttpSettings.HttpAccount.fromJson(account)), - ); - } - - toJson() { - return { - accounts: Inbound.HttpSettings.toJsonArray(this.accounts), - }; - } -}; - -Inbound.HttpSettings.HttpAccount = class extends XrayCommonClass { - constructor(user = RandomUtil.randomSeq(10), pass = RandomUtil.randomSeq(10)) { - super(); - this.user = user; - this.pass = pass; - } - - static fromJson(json = {}) { - return new Inbound.HttpSettings.HttpAccount(json.user, json.pass); - } -}; +const Protocols = { + VMESS: 'vmess', + VLESS: 'vless', + TROJAN: 'trojan', + SHADOWSOCKS: 'shadowsocks', + DOKODEMO: 'dokodemo-door', + SOCKS: 'socks', + HTTP: 'http', +}; + +const VmessMethods = { + AES_128_GCM: 'aes-128-gcm', + CHACHA20_POLY1305: 'chacha20-poly1305', + AUTO: 'auto', + NONE: 'none', +}; + +const SSMethods = { + // AES_256_CFB: 'aes-256-cfb', + // AES_128_CFB: 'aes-128-cfb', + // CHACHA20: 'chacha20', + // CHACHA20_IETF: 'chacha20-ietf', + CHACHA20_POLY1305: 'chacha20-poly1305', + AES_256_GCM: 'aes-256-gcm', + AES_128_GCM: 'aes-128-gcm', + BLAKE3_AES_128_GCM: '2022-blake3-aes-128-gcm', + BLAKE3_AES_256_GCM: '2022-blake3-aes-256-gcm', + BLAKE3_CHACHA20_POLY1305: '2022-blake3-chacha20-poly1305', +}; + +const RULE_IP = { + PRIVATE: 'geoip:private', + CN: 'geoip:cn', +}; + +const RULE_DOMAIN = { + ADS: 'geosite:category-ads', + ADS_ALL: 'geosite:category-ads-all', + CN: 'geosite:cn', + GOOGLE: 'geosite:google', + FACEBOOK: 'geosite:facebook', + SPEEDTEST: 'geosite:speedtest', +}; + +const FLOW_VISION = { + FLOWVISION: "xtls-rprx-vision", +} + +const TLS_VERSION_OPTION = { + TLS10: "1.0", + TLS11: "1.1", + TLS12: "1.2", + TLS13: "1.3", +}; + +const TLS_CIPHER_OPTION = { + AES_128_GCM: "TLS_AES_128_GCM_SHA256", + AES_256_GCM: "TLS_AES_256_GCM_SHA384", + CHACHA20_POLY1305: "TLS_CHACHA20_POLY1305_SHA256", + ECDHE_ECDSA_AES_128_CBC: "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA", + ECDHE_ECDSA_AES_256_CBC: "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA", + ECDHE_ECDSA_AES_128_GCM: "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", + ECDHE_ECDSA_AES_256_GCM: "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", + ECDHE_RSA_AES_128_GCM: "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", + ECDHE_RSA_AES_256_GCM: "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", + ECDHE_ECDSA_CHACHA20_POLY1305: "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256", + ECDHE_RSA_CHACHA20_POLY1305: "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256", +}; + +const ALPN_OPTION = { + H3: "h3", + H2: "h2", + HTTP1: "http/1.1", +}; + +const TCP_CONGESTION = { + bbr: "bbr", + cubic: "cubic", + reno: "reno", +} + +const DOMAIN_STRATEGY = { + AsIs: "AsIs", + UseIP: "UseIP", + UseIPv4: "UseIPv4", + UseIPv6: "UseIPv6", + UseIPv6v4: "UseIPv6v4", + UseIPv4v6: "UseIPv4v6", + ForceIP: "ForceIP", + ForceIPv6v4: "ForceIPv6v4", + ForceIPv6: "ForceIPv6", + ForceIPv4v6: "ForceIPv4v6", + ForceIPv4: "ForceIPv4", +} + +const UTLS_FINGERPRINT = { + UTLS_CHROME: "chrome", + UTLS_FIREFOX: "firefox", + UTLS_SAFARI: "safari", + UTLS_IOS: "ios", + UTLS_android: "android", + UTLS_EDGE: "edge", + UTLS_360: "360", + UTLS_QQ: "qq", + UTLS_RANDOM: "random", + UTLS_RANDOMIZED: "randomized", +}; + +const SNIFFING_OPTION = { + HTTP: "http", + TLS: "tls", + QUIC: "quic", + FAKEDNS: "fakedns" +}; + +const MODE_OPTION = { + AUTO: "auto", + PACKET_UP: "packet-up", + STREAM_UP: "stream-up", + STREAM_ONE: "stream-one", +}; + +Object.freeze(Protocols); +Object.freeze(VmessMethods); +Object.freeze(SSMethods); +Object.freeze(RULE_IP); +Object.freeze(RULE_DOMAIN); +Object.freeze(FLOW_VISION); +Object.freeze(TLS_VERSION_OPTION); +Object.freeze(TLS_CIPHER_OPTION); +Object.freeze(ALPN_OPTION); +Object.freeze(TCP_CONGESTION); +Object.freeze(DOMAIN_STRATEGY); +Object.freeze(SNIFFING_OPTION); +Object.freeze(MODE_OPTION); + +class XrayCommonClass { + + static toJsonArray(arr) { + return arr.map(obj => obj.toJson()); + } + + static fromJson() { + return new XrayCommonClass(); + } + + toJson() { + return this; + } + + toString(format = true) { + return format ? JSON.stringify(this.toJson(), null, 2) : JSON.stringify(this.toJson()); + } + + static toHeaders(v2Headers) { + let newHeaders = []; + if (v2Headers) { + Object.keys(v2Headers).forEach(key => { + let values = v2Headers[key]; + if (typeof (values) === 'string') { + newHeaders.push({ name: key, value: values }); + } else { + for (let i = 0; i < values.length; ++i) { + newHeaders.push({ name: key, value: values[i] }); + } + } + }); + } + return newHeaders; + } + + static toV2Headers(headers, arr = true) { + let v2Headers = {}; + for (let i = 0; i < headers.length; ++i) { + let name = headers[i].name; + let value = headers[i].value; + if (ObjectUtil.isEmpty(name) || ObjectUtil.isEmpty(value)) { + continue; + } + if (!(name in v2Headers)) { + v2Headers[name] = arr ? [value] : value; + } else { + if (arr) { + v2Headers[name].push(value); + } else { + v2Headers[name] = value; + } + } + } + return v2Headers; + } +} + + +class TcpStreamSettings extends XrayCommonClass { + constructor(type = 'none', + request = new TcpStreamSettings.TcpRequest(), + response = new TcpStreamSettings.TcpResponse(), + ) { + super(); + this.type = type; + this.request = request; + this.response = response; + } + + static fromJson(json = {}) { + let header = json.header; + if (!header) { + header = {}; + } + return new TcpStreamSettings( + header.type, + TcpStreamSettings.TcpRequest.fromJson(header.request), + TcpStreamSettings.TcpResponse.fromJson(header.response), + ); + } + + toJson() { + return { + header: { + type: this.type, + request: this.type === 'http' ? this.request.toJson() : undefined, + response: this.type === 'http' ? this.response.toJson() : undefined, + }, + }; + + } +} + +TcpStreamSettings.TcpRequest = class extends XrayCommonClass { + constructor(version = '1.1', + method = 'GET', + path = ['/'], + headers = [], + ) { + super(); + this.version = version; + this.method = method; + this.path = path.length === 0 ? ['/'] : path; + this.headers = headers; + } + + addPath(path) { + this.path.push(path); + } + + removePath(index) { + this.path.splice(index, 1); + } + + addHeader(name, value) { + this.headers.push({ name: name, value: value }); + } + + getHeader(name) { + for (const header of this.headers) { + if (header.name.toLowerCase() === name.toLowerCase()) { + return header.value; + } + } + return null; + } + + removeHeader(index) { + this.headers.splice(index, 1); + } + + static fromJson(json = {}) { + return new TcpStreamSettings.TcpRequest( + json.version, + json.method, + json.path, + XrayCommonClass.toHeaders(json.headers), + ); + } + + toJson() { + return { + method: this.method, + path: ObjectUtil.clone(this.path), + headers: XrayCommonClass.toV2Headers(this.headers), + }; + } +}; + +TcpStreamSettings.TcpResponse = class extends XrayCommonClass { + constructor(version = '1.1', + status = '200', + reason = 'OK', + headers = [], + ) { + super(); + this.version = version; + this.status = status; + this.reason = reason; + this.headers = headers; + } + + addHeader(name, value) { + this.headers.push({ name: name, value: value }); + } + + removeHeader(index) { + this.headers.splice(index, 1); + } + + static fromJson(json = {}) { + return new TcpStreamSettings.TcpResponse( + json.version, + json.status, + json.reason, + XrayCommonClass.toHeaders(json.headers), + ); + } + + toJson() { + return { + version: this.version, + status: this.status, + reason: this.reason, + headers: XrayCommonClass.toV2Headers(this.headers), + }; + } +}; + +class KcpStreamSettings extends XrayCommonClass { + constructor(mtu = 1350, tti = 20, + uplinkCapacity = 5, + downlinkCapacity = 20, + congestion = false, + readBufferSize = 2, + writeBufferSize = 2, + type = 'none', + seed = RandomUtil.randomSeq(10), + ) { + super(); + this.mtu = mtu; + this.tti = tti; + this.upCap = uplinkCapacity; + this.downCap = downlinkCapacity; + this.congestion = congestion; + this.readBuffer = readBufferSize; + this.writeBuffer = writeBufferSize; + this.type = type; + this.seed = seed; + } + + static fromJson(json = {}) { + return new KcpStreamSettings( + json.mtu, + json.tti, + json.uplinkCapacity, + json.downlinkCapacity, + json.congestion, + json.readBufferSize, + json.writeBufferSize, + ObjectUtil.isEmpty(json.header) ? 'none' : json.header.type, + json.seed, + ); + } + + toJson() { + return { + mtu: this.mtu, + tti: this.tti, + uplinkCapacity: this.upCap, + downlinkCapacity: this.downCap, + congestion: this.congestion, + readBufferSize: this.readBuffer, + writeBufferSize: this.writeBuffer, + header: { + type: this.type, + }, + seed: this.seed, + }; + } +} + +class WsStreamSettings extends XrayCommonClass { + constructor(path = '/', headers = []) { + super(); + this.path = path; + this.headers = headers; + } + + addHeader(name, value) { + this.headers.push({ name: name, value: value }); + } + + getHeader(name) { + for (const header of this.headers) { + if (header.name.toLowerCase() === name.toLowerCase()) { + return header.value; + } + } + return null; + } + + removeHeader(index) { + this.headers.splice(index, 1); + } + + static fromJson(json = {}) { + return new WsStreamSettings( + json.path, + XrayCommonClass.toHeaders(json.headers), + ); + } + + toJson() { + return { + path: this.path, + headers: XrayCommonClass.toV2Headers(this.headers, false), + }; + } +} + +class GrpcStreamSettings extends XrayCommonClass { + constructor( + serviceName = "", + authority = "", + multiMode = false, + ) { + super(); + this.serviceName = serviceName; + this.authority = authority; + this.multiMode = multiMode; + } + + static fromJson(json = {}) { + return new GrpcStreamSettings( + json.serviceName, + json.authority, + json.multiMode); + } + + toJson() { + return { + serviceName: this.serviceName, + authority: this.authority, + multiMode: this.multiMode, + } + } +} + +class HttpUpgradeStreamSettings extends XrayCommonClass { + constructor(acceptProxyProtocol = false, path = '/', host = '', headers = []) { + super(); + this.acceptProxyProtocol = acceptProxyProtocol; + this.path = path; + this.host = host; + this.headers = headers; + } + + + addHeader(name, value) { + this.headers.push({ name: name, value: value }); + } + + getHeader(name) { + for (const header of this.headers) { + if (header.name.toLowerCase() === name.toLowerCase()) { + return header.value; + } + } + return null; + } + + removeHeader(index) { + this.headers.splice(index, 1); + } + + static fromJson(json = {}) { + return new HttpUpgradeStreamSettings( + json.acceptProxyProtocol, + json.path, + json.host, + XrayCommonClass.toHeaders(json.headers), + ); + } + + toJson() { + return { + acceptProxyProtocol: this.acceptProxyProtocol, + path: this.path, + host: this.host, + headers: XrayCommonClass.toV2Headers(this.headers, false), + }; + } +} + +class xHTTPStreamSettings extends XrayCommonClass { + constructor( + path = '/', + host = '', + headers = [], + scMaxEachPostBytes = 1000000, + scMaxBufferedPosts = 100, + scMinPostsIntervalMs = 30, + noSSEHeader = false, + xPaddingBytes = "100-1000", + xmux = { + maxConnections: '16-32', + maxConcurrency: 0, + cMaxReuseTimes: '64-128', + cMaxLifetimeMs: 0, + hMaxRequestTimes: '800-900', + hKeepAlivePeriod: 45, + }, + mode = MODE_OPTION.AUTO, + noGRPCHeader = false, + ) { + super(); + this.path = path; + this.host = host; + this.headers = headers; + this.scMaxEachPostBytes = scMaxEachPostBytes; + this.scMaxBufferedPosts = scMaxBufferedPosts; + this.scMinPostsIntervalMs = scMinPostsIntervalMs; + this.noSSEHeader = noSSEHeader; + this.xPaddingBytes = RandomUtil.convertXPaddingBytes(xPaddingBytes); + this.xmux = xmux; + this.mode = mode; + this.noGRPCHeader = noGRPCHeader; + } + + addHeader(name, value) { + this.headers.push({ name: name, value: value }); + } + getHeader(name) { + for (const header of this.headers) { + if (header.name.toLowerCase() === name.toLowerCase()) { + return header.value; + } + } + return null; + } + removeHeader(index) { + this.headers.splice(index, 1); + } + + static fromJson(json = {}) { + return new xHTTPStreamSettings( + json.path, + json.host, + XrayCommonClass.toHeaders(json.headers), + json.scMaxEachPostBytes, + json.scMaxBufferedPosts, + json.scMinPostsIntervalMs, + json.noSSEHeader, + json.xPaddingBytes, + json.xmux, + json.mode, + json.noGRPCHeader, + ); + } + + toJson() { + const xmuxData = {}; + if (!ObjectUtil.isEmpty(this.xmux.maxConnections)) { + xmuxData.maxConnections = RandomUtil.convertXPaddingBytes(this.xmux.maxConnections); + } + if (!ObjectUtil.isEmpty(this.xmux.maxConcurrency)) { + xmuxData.maxConcurrency = RandomUtil.convertXPaddingBytes(this.xmux.maxConcurrency); + } + xmuxData.cMaxReuseTimes = RandomUtil.convertXPaddingBytes(this.xmux.cMaxReuseTimes); + xmuxData.cMaxLifetimeMs = RandomUtil.convertXPaddingBytes(this.xmux.cMaxLifetimeMs); + xmuxData.hMaxRequestTimes = RandomUtil.convertXPaddingBytes(this.xmux.hMaxRequestTimes); + xmuxData.hKeepAlivePeriod = RandomUtil.convertXPaddingBytes(this.xmux.hKeepAlivePeriod); + return { + path: this.path, + host: this.host, + headers: XrayCommonClass.toV2Headers(this.headers, false), + scMaxEachPostBytes: this.scMaxEachPostBytes, + scMaxBufferedPosts: this.scMaxBufferedPosts, + scMinPostsIntervalMs: this.scMinPostsIntervalMs, + noSSEHeader: this.noSSEHeader, + xPaddingBytes: RandomUtil.convertXPaddingBytes(this.xPaddingBytes), + xmux: xmuxData, + mode: this.mode, + noGRPCHeader: this.noGRPCHeader, + }; + } +} + +class TlsStreamSettings extends XrayCommonClass { + constructor(serverName = '', + rejectUnknownSni = false, + minVersion = TLS_VERSION_OPTION.TLS10, + maxVersion = TLS_VERSION_OPTION.TLS12, + cipherSuites = '', + certificates = [new TlsStreamSettings.Cert()], alpn = [''], + settings = [new TlsStreamSettings.Settings()]) { + super(); + this.server = serverName; + this.rejectUnknownSni = rejectUnknownSni; + this.minVersion = minVersion; + this.maxVersion = maxVersion; + this.cipherSuites = cipherSuites instanceof Array ? cipherSuites.join(':') : cipherSuites.split(':'); + this.certs = certificates; + this.alpn = alpn; + this.settings = settings; + } + + addCert(cert) { + this.certs.push(cert); + } + + removeCert(index) { + this.certs.splice(index, 1); + } + + static fromJson(json = {}) { + let certs; + let settings; + if (!ObjectUtil.isEmpty(json.certificates)) { + certs = json.certificates.map(cert => TlsStreamSettings.Cert.fromJson(cert)); + } + + if (!ObjectUtil.isEmpty(json.settings)) { + let values = json.settings[0]; + settings = [new TlsStreamSettings.Settings(values.allowInsecure, values.fingerprint, values.serverName)]; + } + + return new TlsStreamSettings( + json.serverName, + json.rejectUnknownSni, + json.minVersion, + json.maxVersion, + json.cipherSuites, + certs, + json.alpn, + settings, + ); + } + + toJson() { + return { + serverName: this.server, + rejectUnknownSni: this.rejectUnknownSni, + minVersion: this.minVersion, + maxVersion: this.maxVersion, + cipherSuites: this.cipherSuites instanceof Array ? this.cipherSuites.join(':') : this.cipherSuites.split(':'), + certificates: TlsStreamSettings.toJsonArray(this.certs), + alpn: this.alpn, + settings: TlsStreamSettings.toJsonArray(this.settings), + }; + } +} + +TlsStreamSettings.Cert = class extends XrayCommonClass { + constructor(useFile = true, ocspStapling = 3600, certificateFile = '', keyFile = '', certificate = '', key = '') { + super(); + this.useFile = useFile; + this.ocspStapling = ocspStapling; + this.certFile = certificateFile; + this.keyFile = keyFile; + this.cert = certificate instanceof Array ? certificate.join('\n') : certificate; + this.key = key instanceof Array ? key.join('\n') : key; + } + + static fromJson(json = {}) { + if ('certificateFile' in json && 'keyFile' in json) { + return new TlsStreamSettings.Cert( + true, + json.ocspStapling, + json.certificateFile, + json.keyFile, + ); + } else { + return new TlsStreamSettings.Cert( + false, + json.ocspStapling, + '', '', + json.certificate.join('\n'), + json.key.join('\n'), + ); + } + } + + toJson() { + if (this.useFile) { + return { + ocspStapling: this.ocspStapling, + certificateFile: this.certFile, + keyFile: this.keyFile, + }; + } else { + return { + ocspStapling: this.ocspStapling, + certificate: this.cert.split('\n'), + key: this.key.split('\n'), + }; + } + } +}; + +TlsStreamSettings.Settings = class extends XrayCommonClass { + constructor(allowInsecure = false, fingerprint = '', serverName = '') { + super(); + this.allowInsecure = allowInsecure; + this.fingerprint = fingerprint; + this.serverName = serverName; + } + static fromJson(json = {}) { + return new TlsStreamSettings.Settings( + json.allowInsecure, + json.fingerprint, + json.servername, + ); + } + toJson() { + return { + allowInsecure: this.allowInsecure, + fingerprint: this.fingerprint, + serverName: this.serverName, + }; + } +}; + +class ReaLITyStreamSettings extends XrayCommonClass { + constructor(show = false, + fingerprint = UTLS_FINGERPRINT.UTLS_CHROME, + dest = 'www.lovelive-anime.jp:443', + xver = 0, + serverNames = 'lovelive-anime.jp\nwww.lovelive-anime.jp', + privateKey = '', + publicKey = '', + minClientVer = '', + maxClientVer = '', + maxTimeDiff = 0, + shortIds = RandomUtil.randowShortId(), + ) { + super(); + if (privateKey === '') { + RandomUtil.xraysecretkey().then((res) => { + this.privateKey = res.data.obj.key + this.publicKey = res.data.obj.value + }) + } else { + this.privateKey = privateKey + this.publicKey = publicKey + } + this.show = show; + this.fingerprint = fingerprint; + this.dest = dest; + this.xver = xver; + this.serverNames = serverNames instanceof Array ? serverNames.join('\n') : serverNames; + this.minClientVer = minClientVer; + this.maxClientVer = maxClientVer; + this.maxTimeDiff = maxTimeDiff; + this.shortIds = shortIds instanceof Array ? shortIds.join('\n') : shortIds; + + } + + static fromJson(json = {}) { + return new ReaLITyStreamSettings( + json.show, + json.fingerprint, + json.dest, + json.xver, + json.serverNames, + json.privateKey, + json.publicKey, + json.minClientVer, + json.maxClientVer, + json.maxTimeDiff, + json.shortIds, + ); + } + + toJson() { + return { + show: this.show, + fingerprint: this.fingerprint, + dest: this.dest, + xver: this.xver, + serverNames: this.serverNames.split('\n'), + privateKey: this.privateKey, + publicKey: this.publicKey, + minClientVer: this.minClientVer, + maxClientVer: this.maxClientVer, + maxTimeDiff: this.maxTimeDiff, + shortIds: this.shortIds.split('\n'), + }; + } +} + +class SockoptStreamSettings extends XrayCommonClass { + constructor(mark = 0, + tcpMaxSeg = 1440, + tcpFastOpen = false, + tproxy = "off", + domainStrategy = DOMAIN_STRATEGY.AsIs, + dialerProxy = "", + acceptProxyProtocol = false, + tcpKeepAliveInterval = 0, + tcpKeepAliveIdle = 0, + tcpUserTimeout = 10000, + tcpcongestion = "", + _interface = "", + V6Only = false, + tcpWindowClamp = 600, + TcpMptcp = true, + tcpNoDelay = false, + ) { + super(); + this.mark = mark; + this.tcpMaxSeg = tcpMaxSeg; + this.tcpFastOpen = tcpFastOpen; + this.tproxy = tproxy; + this.domainStrategy = domainStrategy; + this.dialerProxy = dialerProxy; + this.acceptProxyProtocol = acceptProxyProtocol; + this.tcpKeepAliveInterval = tcpKeepAliveInterval; + this.tcpKeepAliveIdle = tcpKeepAliveIdle; + this.tcpUserTimeout = tcpUserTimeout; + this.tcpcongestion = tcpcongestion; + this.interface = _interface instanceof Array ? this.interface : _interface; + this.V6Only = V6Only; + this.tcpWindowClamp = tcpWindowClamp; + this.TcpMptcp = TcpMptcp; + this.tcpNoDelay = tcpNoDelay; + } + + static fromJson(json = {}) { + if (Object.keys(json).length === 0) return undefined; + return new SockoptStreamSettings( + json.mark, + json.tcpMaxSeg, + json.tcpFastOpen, + json.tproxy, + json.domainStrategy, + json.dialerProxy, + json.acceptProxyProtocol, + json.tcpKeepAliveInterval, + json.tcpKeepAliveIdle, + json.tcpUserTimeout, + json.tcpcongestion, + json.interface, + json.V6Only, + json.tcpWindowClamp, + json.TcpMptcp, + json.tcpNoDelay, + ); + } + + toJson() { + return { + mark: this.mark, + tcpMaxSeg: this.tcpMaxSeg, + tcpFastOpen: this.tcpFastOpen, + tproxy: this.tproxy, + domainStrategy: this.domainStrategy, + dialerProxy: this.dialerProxy, + acceptProxyProtocol: this.acceptProxyProtocol, + tcpKeepAliveInterval: this.tcpKeepAliveInterval, + tcpKeepAliveIdle: this.tcpKeepAliveIdle, + tcpUserTimeout: this.tcpUserTimeout, + tcpcongestion: this.tcpcongestion, + interface: this.interface, + V6Only: this.V6Only, + tcpWindowClamp: this.tcpWindowClamp, + TcpMptcp: this.TcpMptcp, + tcpNoDelay: this.tcpNoDelay, + }; + } +} + + +class StreamSettings extends XrayCommonClass { + constructor(network = 'tcp', + security = 'none', + tlsSettings = new TlsStreamSettings(), + realitySettings = new ReaLITyStreamSettings(), + tcpSettings = new TcpStreamSettings(), + rawSettings = new TcpStreamSettings(), + kcpSettings = new KcpStreamSettings(), + wsSettings = new WsStreamSettings(), + grpcSettings = new GrpcStreamSettings(), + httpupgradeSettings = new HttpUpgradeStreamSettings(), + xhttpSettings = new xHTTPStreamSettings(), + sockopt = undefined, + ) { + super(); + this.network = network; + this.security = security; + this.tls = tlsSettings; + this.reality = realitySettings; + this.tcp = tcpSettings; + this.raw = rawSettings; + this.kcp = kcpSettings; + this.ws = wsSettings; + this.grpc = grpcSettings; + this.httpupgrade = httpupgradeSettings; + this.xhttp = xhttpSettings; + this.sockopt = sockopt; + } + + get isTls() { + return this.security === 'tls'; + } + + set isTls(isTls) { + if (isTls) { + this.security = 'tls'; + } else { + this.security = 'none'; + } + } + + get isReaLITy() { + return this.security === "reality"; + } + + set isReaLITy(isReaLITy) { + if (isReaLITy) { + this.security = 'reality'; + } else { + this.security = 'none'; + } + } + + get sockoptSwitch() { + return this.sockopt != undefined; + } + + set sockoptSwitch(value) { + this.sockopt = value ? new SockoptStreamSettings() : undefined; + } + + static fromJson(json = {}) { + return new StreamSettings( + json.network, + json.security, + TlsStreamSettings.fromJson(json.tlsSettings), + ReaLITyStreamSettings.fromJson(json.realitySettings), + TcpStreamSettings.fromJson(json.tcpSettings), + TcpStreamSettings.fromJson(json.rawSettings), + KcpStreamSettings.fromJson(json.kcpSettings), + WsStreamSettings.fromJson(json.wsSettings), + GrpcStreamSettings.fromJson(json.grpcSettings), + HttpUpgradeStreamSettings.fromJson(json.httpupgradeSettings), + xHTTPStreamSettings.fromJson(json.xhttpSettings), + SockoptStreamSettings.fromJson(json.sockopt), + ); + } + + toJson() { + const network = this.network; + return { + network: network, + security: this.security, + tlsSettings: this.isTls ? this.tls.toJson() : undefined, + realitySettings: this.isReaLITy ? this.reality.toJson() : undefined, + tcpSettings: network === 'tcp' ? this.tcp.toJson() : undefined, + rawSettings: network === 'raw' ? this.raw.toJson() : undefined, + kcpSettings: network === 'kcp' ? this.kcp.toJson() : undefined, + wsSettings: network === 'ws' ? this.ws.toJson() : undefined, + grpcSettings: network === 'grpc' ? this.grpc.toJson() : undefined, + httpupgradeSettings: network === 'httpupgrade' ? this.httpupgrade.toJson() : undefined, + xhttpSettings: network === 'xhttp' ? this.xhttp.toJson() : undefined, + sockopt: this.sockopt != undefined ? this.sockopt.toJson() : undefined, + }; + } +} + +class Sniffing extends XrayCommonClass { + constructor(enabled = true, destOverride = ['http', 'tls', 'quic']) { + super(); + this.enabled = enabled; + this.destOverride = destOverride; + } + + static fromJson(json = {}) { + let destOverride = ObjectUtil.clone(json.destOverride); + if (!ObjectUtil.isEmpty(destOverride) && !ObjectUtil.isArrEmpty(destOverride)) { + if (ObjectUtil.isEmpty(destOverride[0])) { + destOverride = ['http', 'tls', 'quic']; + } + } + return new Sniffing( + !!json.enabled, + destOverride, + ); + } +} + +class Inbound extends XrayCommonClass { + constructor(port = RandomUtil.randomIntRange(10000, 60000), + listen = '', + protocol = Protocols.VMESS, + settings = null, + streamSettings = new StreamSettings(), + tag = '', + sniffing = new Sniffing(), + ) { + super(); + this.port = port; + this.listen = listen; + this._protocol = protocol; + this.settings = ObjectUtil.isEmpty(settings) ? Inbound.Settings.getSettings(protocol) : settings; + this.stream = streamSettings; + this.tag = tag; + this.sniffing = sniffing; + } + + get protocol() { + return this._protocol; + } + + set protocol(protocol) { + this._protocol = protocol; + this.settings = Inbound.Settings.getSettings(protocol); + if (protocol === Protocols.TROJAN) { + if (this.network === "tcp" || this.network === "raw") { + this.tls = true; + } + } + } + + get tls() { + return this.stream.security === 'tls'; + } + + set tls(isTls) { + if (isTls) { + this.stream.security = 'tls'; + } else { + this.stream.security = 'none'; + } + + } + + get reality() { + return this.stream.security === 'reality'; + } + + set reality(isReaLITy) { + if (isReaLITy) { + this.stream.security = 'reality'; + } else { + this.stream.security = 'none'; + } + + } + + get network() { + return this.stream.network; + } + + set network(network) { + this.stream.network = network; + } + + get isTcp() { + return this.network === "tcp"; + } + get isRaw() { + return this.network === "raw"; + } + get isWs() { + return this.network === "ws"; + } + + get isKcp() { + return this.network === "kcp"; + } + + get isGrpc() { + return this.network === "grpc"; + } + + get isHttpupgrade() { + return this.network === "httpupgrade"; + } + get isXHTTP() { + return this.network === "xhttp"; + } + // VMess & VLess + get uuid() { + switch (this.protocol) { + case Protocols.VMESS: + return this.settings.vmesses[0].id; + case Protocols.VLESS: + return this.settings.vlesses[0].id; + default: + return ""; + } + } + + // VLess & Trojan + get flow() { + switch (this.protocol) { + case Protocols.VLESS: + return this.settings.vlesses[0].flow; + case Protocols.TROJAN: + return this.settings.clients[0].flow; + default: + return ""; + } + } + + // Socks & HTTP + get username() { + switch (this.protocol) { + case Protocols.SOCKS: + case Protocols.HTTP: + return this.settings.accounts[0].user; + default: + return ""; + } + } + + // Trojan & Shadowsocks & Socks & HTTP + get password() { + switch (this.protocol) { + case Protocols.TROJAN: + return this.settings.clients[0].password; + case Protocols.SHADOWSOCKS: + return this.settings.password; + case Protocols.SOCKS: + case Protocols.HTTP: + return this.settings.accounts[0].pass; + default: + return ""; + } + } + + // Shadowsocks + get method() { + switch (this.protocol) { + case Protocols.SHADOWSOCKS: + return this.settings.method; + default: + return ""; + } + } + + get serverName() { + if (this.stream.isTls) { + return this.stream.tls.server; + } + return ""; + } + + get host() { + if (this.isTcp) { + return this.stream.tcp.request.getHeader("Host"); + } else if (this.isRaw) { + return this.stream.raw.request.getHeader("Host"); + } else if (this.isWs) { + return this.stream.ws.getHeader("Host"); + } else if (this.isH2) { + return this.stream.http.host[0]; + } else if (this.isHttpupgrade) { + return this.stream.httpupgrade.host; + } else if (this.isXHTTP) { + return this.stream.xhttp.host; + } + return null; + } + + get path() { + if (this.isTcp) { + return this.stream.tcp.request.path[0]; + } else if (this.isRaw) { + return this.stream.raw.request.path[0]; + } else if (this.isWs) { + return this.stream.ws.path; + } else if (this.isH2) { + return this.stream.http.path[0]; + } else if (this.isHttpupgrade) { + return this.stream.httpupgrade.path; + } else if (this.isXHTTP) { + return this.stream.xhttp.path; + } + return null; + } + + get kcpType() { + return this.stream.kcp.type; + } + + get kcpSeed() { + return this.stream.kcp.seed; + } + + get serviceName() { + return this.stream.grpc.serviceName; + } + + canEnableTls() { + switch (this.protocol) { + case Protocols.VMESS: + case Protocols.VLESS: + case Protocols.TROJAN: + case Protocols.SHADOWSOCKS: + break; + default: + return false; + } + + switch (this.network) { + case "tcp": + case "raw": + case "ws": + case "grpc": + case "httpupgrade": + case "xhttp": + return true; + default: + return false; + } + } + + canSetTls() { + return this.canEnableTls(); + } + + canEnableReaLITy() { + switch (this.protocol) { + case Protocols.VLESS: + case Protocols.TROJAN: + break; + default: + return false; + } + return ['tcp', 'raw', 'grpc', 'httpupgrade', 'xhttp'].indexOf(this.network) !== -1; + //return this.network === "tcp"; + } + + // canSockopt() { + // switch (this.protocol) { + // case Protocols.VLESS: + // case Protocols.TROJAN: + // case Protocols.SHADOWSOCKS: + // case Protocols.VMESS: + // break; + // default: + // return false; + // } + // return ['tcp', 'http', 'grpc', 'ws'].indexOf(this.network) !== -1; + // //return this.network === "tcp"; + // } + + canEnableStream() { + switch (this.protocol) { + case Protocols.VMESS: + case Protocols.VLESS: + case Protocols.SHADOWSOCKS: + case Protocols.TROJAN: + return true; + default: + return false; + } + } + + canSniffing() { + switch (this.protocol) { + case Protocols.VMESS: + case Protocols.VLESS: + case Protocols.TROJAN: + case Protocols.SHADOWSOCKS: + return true; + default: + return false; + } + } + + reset() { + this.port = RandomUtil.randomIntRange(10000, 60000); + this.listen = ''; + this.protocol = Protocols.VMESS; + this.settings = Inbound.Settings.getSettings(Protocols.VMESS); + this.stream = new StreamSettings(); + this.tag = ''; + this.sniffing = new Sniffing(); + } + + genVmessLink(address = '', remark = '') { + if (this.protocol !== Protocols.VMESS) { + return ''; + } + let network = this.stream.network; + let type = 'none'; + let host = ''; + let path = ''; + let authority = ''; + let sni = ''; + let mode = ''; + if (network === 'tcp') { + let tcp = this.stream.tcp; + type = tcp.type; + if (type === 'http') { + let request = tcp.request; + path = request.path.join(','); + let index = request.headers.findIndex(header => header.name.toLowerCase() === 'host'); + if (index >= 0) { + host = request.headers[index].value; + } + } + } else if (network === 'raw') { + let raw = this.stream.raw; + type = raw.type; + if (type === 'http') { + let request = raw.request; + path = request.path.join(','); + let index = request.headers.findIndex(header => header.name.toLowerCase() === 'host'); + if (index >= 0) { + host = request.headers[index].value; + } + } + } else if (network === 'kcp') { + let kcp = this.stream.kcp; + type = kcp.type; + path = kcp.seed; + } else if (network === 'ws') { + let ws = this.stream.ws; + path = ws.path; + let index = ws.headers.findIndex(header => header.name.toLowerCase() === 'host'); + if (index >= 0) { + host = ws.headers[index].value; + } + } else if (network === 'grpc') { + path = this.stream.grpc.serviceName; + authority = this.stream.grpc.authority; + if (this.stream.grpc.multiMode) { + type = 'multi' + } + } else if (network === 'httpupgrade') { + let httpupgrade = this.stream.httpupgrade; + path = httpupgrade.path; + host = httpupgrade.host; + let index = httpupgrade.headers.findIndex(header => header.name.toLowerCase() === 'host'); + if (index >= 0) { + host = httpupgrade.headers[index].value; + } + } else if (network === 'xhttp') { + const xhttp = this.stream.xhttp; + path = xhttp.path; + host = xhttp.host; + let index = xhttp.headers.findIndex(header => header.name.toLowerCase() === 'host'); + if (index >= 0) { + host = xhttp.headers[index].value; + }; + mode = xhttp.mode; + } + + if (this.stream.security === 'tls') { + if (!ObjectUtil.isEmpty(this.stream.tls.server)) { + address = this.stream.tls.server; + } + if (!ObjectUtil.isEmpty(this.stream.tls.settings[0]['serverName'])) { + sni = this.stream.tls.settings[0]['serverName']; + } + } + + let obj = { + v: '2', + ps: remark, + add: address, + port: this.port, + id: this.settings.vmesses[0].id, + net: network, + type: type, + host: host, + path: path, + ...(network === 'xhttp' && { mode: mode }), + authority: authority, + tls: this.stream.security, + sni: sni, + fp: this.stream.tls.settings[0]['fingerprint'], + }; + return 'vmess://' + base64(JSON.stringify(obj, null, 2)); + } + + genVLESSLink(address = '', remark = '') { + const settings = this.settings; + const uuid = settings.vlesses[0].id; + const port = this.port; + const type = this.stream.network; + const params = new Map(); + params.set("type", this.stream.network); + if (this.reality) { + params.set("security", "reality"); + } else { + params.set("security", this.stream.security); + } + switch (type) { + case "tcp": + const tcp = this.stream.tcp; + if (tcp.type === 'http') { + const request = tcp.request; + params.set("path", request.path.join(',')); + const index = request.headers.findIndex(header => header.name.toLowerCase() === 'host'); + if (index >= 0) { + const host = request.headers[index].value; + params.set("host", host); + } + } + break; + case "raw": + const raw = this.stream.raw; + if (raw.type === 'http') { + const request = raw.request; + params.set("path", request.path.join(',')); + const index = request.headers.findIndex(header => header.name.toLowerCase() === 'host'); + if (index >= 0) { + const host = request.headers[index].value; + params.set("host", host); + } + } + break; + case "kcp": + const kcp = this.stream.kcp; + params.set("headerType", kcp.type); + params.set("seed", kcp.seed); + break; + case "ws": + const ws = this.stream.ws; + params.set("path", ws.path); + const index = ws.headers.findIndex(header => header.name.toLowerCase() === 'host'); + if (index >= 0) { + const host = ws.headers[index].value; + params.set("host", host); + } + break; + case "grpc": + const grpc = this.stream.grpc; + params.set("serviceName", grpc.serviceName); + params.set("authority", grpc.authority); + if (grpc.multiMode) { + params.set("mode", "multi"); + } + break; + case "httpupgrade": + const httpupgrade = this.stream.httpupgrade; + params.set("path", httpupgrade.path); + params.set("host", httpupgrade.host); + const httpupgradeIndex = httpupgrade.headers.findIndex(header => header.name.toLowerCase() === 'host'); + if (httpupgradeIndex >= 0) { + const host = httpupgrade.headers[httpupgradeIndex].value; + params.set("host", host); + } + break; + case "xhttp": + const xhttp = this.stream.xhttp; + params.set("path", xhttp.path); + params.set("host", xhttp.host); + const xhttpIndex = xhttp.headers.findIndex(header => header.name.toLowerCase() === 'host'); + if (xhttpIndex >= 0) { + const host = xhttp.headers[xhttpIndex].value; + params.set("host", host); + }; + params.set("mode", xhttp.mode); + break; + } + + if (this.tls) { + params.set("security", "tls"); + params.set("fp", this.stream.tls.settings[0]['fingerprint']); + params.set("alpn", this.stream.tls.alpn); + if (this.stream.tls.settings[0].allowInsecure) { + params.set("allowInsecure", "1"); + } + if (!ObjectUtil.isEmpty(this.stream.tls.server)) { + address = this.stream.tls.server; + } + if (this.stream.tls.settings[0]['serverName'] !== '') { + params.set("sni", this.stream.tls.settings[0]['serverName']); + } + if (type === "tcp" && this.settings.vlesses[0].flow.length > 0) { + params.set("flow", this.settings.vlesses[0].flow); + } + if (type === "raw" && this.settings.vlesses[0].flow.length > 0) { + params.set("flow", this.settings.vlesses[0].flow); + } + } + + if (this.stream.security === 'reality') { + if (!ObjectUtil.isArrEmpty(this.stream.reality.serverNames)) { + // params.set("sni", this.stream.reality.serverNames.split(/,|,|\s+/)[0]); + params.set("sni", ObjectUtil.generateSid(this.stream.reality.serverNames)); + } + if (this.stream.reality.publicKey != "") { + params.set("pbk", this.stream.reality.publicKey); + + } + if (this.stream.network === 'tcp') { + params.set("flow", this.settings.vlesses[0].flow); + } + if (this.stream.network === 'raw') { + params.set("flow", this.settings.vlesses[0].flow); + } + // var shortIds1 = this.stream.reality.shortIds.split(/,|,|\s+/); + // var index1 = Math.floor(Math.random() * shortIds1.length); + // var value1 = shortIds1[index1]; + params.set("sid", ObjectUtil.generateSid(this.stream.reality.shortIds)); + + if (this.stream.reality.fingerprint != "") { + params.set("fp", this.stream.reality.fingerprint); + } + } + + const link = `vless://${uuid}@${address}:${port}`; + const url = new URL(link); + for (const [key, value] of params) { + url.searchParams.set(key, value) + } + url.hash = encodeURIComponent(remark); + return url.toString(); + } + + genSSLink(address = '', forceTls, remark = '') { + let settings = this.settings; + const type = this.stream.network; + const security = forceTls == 'same' ? this.stream.security : forceTls; + const params = new Map(); + params.set("type", this.stream.network); + switch (type) { + case "tcp": + const tcp = this.stream.tcp; + if (tcp.type === 'http') { + const request = tcp.request; + params.set("path", request.path.join(',')); + const index = request.headers.findIndex(header => header.name.toLowerCase() === 'host'); + if (index >= 0) { + const host = request.headers[index].value; + params.set("host", host); + } + params.set("headerType", 'http'); + } + break; + case "raw": + const raw = this.stream.raw; + if (raw.type === 'http') { + const request = raw.request; + params.set("path", request.path.join(',')); + const index = request.headers.findIndex(header => header.name.toLowerCase() === 'host'); + if (index >= 0) { + const host = request.headers[index].value; + params.set("host", host); + } + params.set("headerType", 'http'); + } + break; + case "kcp": + const kcp = this.stream.kcp; + params.set("headerType", kcp.type); + params.set("seed", kcp.seed); + break; + case "ws": + const ws = this.stream.ws; + params.set("path", ws.path); + const index = ws.headers.findIndex(header => header.name.toLowerCase() === 'host'); + if (index >= 0) { + const host = ws.headers[index].value; + params.set("host", host); + } + break; + case "grpc": + const grpc = this.stream.grpc; + params.set("serviceName", grpc.serviceName); + params.set("authority", grpc.authority); + if (grpc.multiMode) { + params.set("mode", "multi"); + } + break; + case "httpupgrade": + const httpupgrade = this.stream.httpupgrade; + params.set("path", httpupgrade.path); + params.set("host", httpupgrade.host); + const httpupgradeIndex = httpupgrade.headers.findIndex(header => header.name.toLowerCase() === 'host'); + if (httpupgradeIndex >= 0) { + const host = httpupgrade.headers[httpupgradeIndex].value; + params.set("host", host); + } + break; + case "xhttp": + const xhttp = this.stream.xhttp; + params.set("path", xhttp.path); + params.set("host", xhttp.host); + const xhttpIndex = xhttp.headers.findIndex(header => header.name.toLowerCase() === 'host'); + if (xhttpIndex >= 0) { + const host = xhttp.headers[xhttpIndex].value; + params.set("host", host); + }; + params.set("mode", xhttp.mode); + break; + } + + if (security === 'tls') { + params.set("security", "tls"); + if (this.stream.isTls) { + params.set("fp", this.stream.tls.settings.fingerprint); + params.set("alpn", this.stream.tls.alpn); + if (this.stream.tls.settings.allowInsecure) { + params.set("allowInsecure", "1"); + } + if (!ObjectUtil.isEmpty(this.stream.tls.sni)) { + params.set("sni", this.stream.tls.sni); + } + } + } + + + if (settings.method == SSMethods.BLAKE3_AES_128_GCM || settings.method == SSMethods.BLAKE3_AES_256_GCM || settings.method == SSMethods.BLAKE3_CHACHA20_POLY1305) { + const link = `ss://${settings.method}:${settings.password}@${address}:${this.port}#${encodeURIComponent(remark)}`; + + const url = new URL(link); + for (const [key, value] of params) { + url.searchParams.set(key, value) + } + url.hash = encodeURIComponent(remark); + return url.toString(); + } else { + const link = 'ss://' + safeBase64(settings.method + ':' + settings.password + '@' + address + ':' + this.port) + + '#' + encodeURIComponent(remark); + + const url = new URL(link); + for (const [key, value] of params) { + url.searchParams.set(key, value) + } + url.hash = encodeURIComponent(remark); + return url.toString(); + } + + } + genTrojanLink(address = '', remark = '') { + let settings = this.settings; + const port = this.port; + const type = this.stream.network; + const params = new Map(); + params.set("type", this.stream.network); + if (this.reality) { + params.set("security", "reality"); + } else { + params.set("security", this.stream.security); + } + switch (type) { + case "tcp": + const tcp = this.stream.tcp; + if (tcp.type === 'http') { + const request = tcp.request; + params.set("path", request.path.join(',')); + const index = request.headers.findIndex(header => header.name.toLowerCase() === 'host'); + if (index >= 0) { + const host = request.headers[index].value; + params.set("host", host); + } + } + break; + case "raw": + const raw = this.stream.raw; + if (raw.type === 'http') { + const request = raw.request; + params.set("path", request.path.join(',')); + const index = request.headers.findIndex(header => header.name.toLowerCase() === 'host'); + if (index >= 0) { + const host = request.headers[index].value; + params.set("host", host); + } + } + break; + case "kcp": + const kcp = this.stream.kcp; + params.set("headerType", kcp.type); + params.set("seed", kcp.seed); + break; + case "ws": + const ws = this.stream.ws; + params.set("path", ws.path); + const index = ws.headers.findIndex(header => header.name.toLowerCase() === 'host'); + if (index >= 0) { + const host = ws.headers[index].value; + params.set("host", host); + } + break; + case "grpc": + const grpc = this.stream.grpc; + params.set("serviceName", grpc.serviceName); + params.set("authority", grpc.authority); + if (grpc.multiMode) { + params.set("mode", "multi"); + } + break; + case "httpupgrade": + const httpupgrade = this.stream.httpupgrade; + params.set("path", httpupgrade.path); + params.set("host", httpupgrade.host); + const httpUpgradeIndex = httpupgrade.headers.findIndex(header => header.name.toLowerCase() === 'host'); + if (httpUpgradeIndex >= 0) { + const host = httpupgrade.headers[httpUpgradeIndex].value; + params.set("host", host); + } + break; + case "xhttp": + const xhttp = this.stream.xhttp; + params.set("path", xhttp.path); + params.set("host", xhttp.host); + const xhttpIndex = xhttp.headers.findIndex(header => header.name.toLowerCase() === 'host'); + if (xhttpIndex >= 0) { + const host = xhttp.headers[xhttpIndex].value; + params.set("host", host); + }; + params.set("mode", xhttp.mode); + break; + } + + if (this.tls) { + params.set("security", "tls"); + params.set("fp", this.stream.tls.settings[0]['fingerprint']); + params.set("alpn", this.stream.tls.alpn); + if (this.stream.tls.settings[0].allowInsecure) { + params.set("allowInsecure", "1"); + } + if (!ObjectUtil.isEmpty(this.stream.tls.server)) { + address = this.stream.tls.server; + } + if (this.stream.tls.settings[0]['serverName'] !== '') { + params.set("sni", this.stream.tls.settings[0]['serverName']); + } + } + + if (this.stream.security === 'reality') { + if (!ObjectUtil.isArrEmpty(this.stream.reality.serverNames)) { + // params.set("sni", this.stream.reality.serverNames.split(/,|,|\s+/)[0]); + params.set("sni", ObjectUtil.generateSid(this.stream.reality.serverNames)); + } + if (this.stream.reality.publicKey != "") { + params.set("pbk", this.stream.reality.publicKey); + } + + // var shortIds1 = this.stream.reality.shortIds.split(/,|,|\s+/); + // var index1 = Math.floor(Math.random() * shortIds1.length); + // var value1 = shortIds1[index1]; + params.set("sid", ObjectUtil.generateSid(this.stream.reality.shortIds)); + + if (this.stream.reality.fingerprint != "") { + params.set("fp", this.stream.reality.fingerprint); + } + } + const link = `trojan://${settings.clients[0].password}@${address}:${port}`; + const url = new URL(link); + for (const [key, value] of params) { + url.searchParams.set(key, value) + } + url.hash = encodeURIComponent(remark); + return url.toString(); + } + + genLink(address = '', remark = '') { + switch (this.protocol) { + case Protocols.VMESS: return this.genVmessLink(address, remark); + case Protocols.VLESS: return this.genVLESSLink(address, remark); + case Protocols.SHADOWSOCKS: return this.genSSLink(address, remark); + case Protocols.TROJAN: return this.genTrojanLink(address, remark); + default: return ''; + } + } + + static fromJson(json = {}) { + return new Inbound( + json.port, + json.listen, + json.protocol, + Inbound.Settings.fromJson(json.protocol, json.settings), + StreamSettings.fromJson(json.streamSettings), + json.tag, + Sniffing.fromJson(json.sniffing), + ) + } + + toJson() { + let streamSettings; + if (this.canEnableStream() || this.protocol === Protocols.TROJAN) { + streamSettings = this.stream.toJson(); + } + return { + port: this.port, + listen: this.listen, + protocol: this.protocol, + settings: this.settings instanceof XrayCommonClass ? this.settings.toJson() : this.settings, + streamSettings: streamSettings, + tag: this.tag, + sniffing: this.sniffing.toJson(), + }; + } +} + +Inbound.Settings = class extends XrayCommonClass { + constructor(protocol) { + super(); + this.protocol = protocol; + } + + static getSettings(protocol) { + switch (protocol) { + case Protocols.VMESS: return new Inbound.VmessSettings(protocol); + case Protocols.VLESS: return new Inbound.VLESSSettings(protocol); + case Protocols.TROJAN: return new Inbound.TrojanSettings(protocol); + case Protocols.SHADOWSOCKS: return new Inbound.ShadowsocksSettings(protocol); + case Protocols.DOKODEMO: return new Inbound.DokodemoSettings(protocol); + case Protocols.SOCKS: return new Inbound.SocksSettings(protocol); + case Protocols.HTTP: return new Inbound.HttpSettings(protocol); + default: return null; + } + } + + static fromJson(protocol, json) { + switch (protocol) { + case Protocols.VMESS: return Inbound.VmessSettings.fromJson(json); + case Protocols.VLESS: return Inbound.VLESSSettings.fromJson(json); + case Protocols.TROJAN: return Inbound.TrojanSettings.fromJson(json); + case Protocols.SHADOWSOCKS: return Inbound.ShadowsocksSettings.fromJson(json); + case Protocols.DOKODEMO: return Inbound.DokodemoSettings.fromJson(json); + case Protocols.SOCKS: return Inbound.SocksSettings.fromJson(json); + case Protocols.HTTP: return Inbound.HttpSettings.fromJson(json); + default: return null; + } + } + + toJson() { + return {}; + } +}; + +Inbound.VmessSettings = class extends Inbound.Settings { + constructor(protocol, + vmesses = [new Inbound.VmessSettings.Vmess()], + disableInsecureEncryption = false) { + super(protocol); + this.vmesses = vmesses; + this.disableInsecure = disableInsecureEncryption; + } + + indexOfVmessById(id) { + return this.vmesses.findIndex(vmess => vmess.id === id); + } + + addVmess(vmess) { + if (this.indexOfVmessById(vmess.id) >= 0) { + return false; + } + this.vmesses.push(vmess); + } + + delVmess(vmess) { + const i = this.indexOfVmessById(vmess.id); + if (i >= 0) { + this.vmesses.splice(i, 1); + } + } + + static fromJson(json = {}) { + return new Inbound.VmessSettings( + Protocols.VMESS, + json.clients.map(client => Inbound.VmessSettings.Vmess.fromJson(client)), + ObjectUtil.isEmpty(json.disableInsecureEncryption) ? false : json.disableInsecureEncryption, + ); + } + + toJson() { + return { + clients: Inbound.VmessSettings.toJsonArray(this.vmesses), + disableInsecureEncryption: this.disableInsecure, + }; + } +}; +Inbound.VmessSettings.Vmess = class extends XrayCommonClass { + constructor(id = RandomUtil.randomUUID()) { + super(); + this.id = id; + } + + static fromJson(json = {}) { + return new Inbound.VmessSettings.Vmess( + json.id, + ); + } +}; + +Inbound.VLESSSettings = class extends Inbound.Settings { + constructor(protocol, + vlesses = [new Inbound.VLESSSettings.VLESS()], + decryption = 'none', + fallbacks = [],) { + super(protocol); + this.vlesses = vlesses; + this.decryption = decryption; + this.fallbacks = fallbacks; + } + + addFallback() { + this.fallbacks.push(new Inbound.VLESSSettings.Fallback()); + } + + delFallback(index) { + this.fallbacks.splice(index, 1); + } + + static fromJson(json = {}) { + return new Inbound.VLESSSettings( + Protocols.VLESS, + json.clients.map(client => Inbound.VLESSSettings.VLESS.fromJson(client)), + json.decryption, + Inbound.VLESSSettings.Fallback.fromJson(json.fallbacks), + ); + } + + toJson() { + return { + clients: Inbound.VLESSSettings.toJsonArray(this.vlesses), + decryption: this.decryption, + fallbacks: Inbound.VLESSSettings.toJsonArray(this.fallbacks), + }; + } +}; + +Inbound.VLESSSettings.VLESS = class extends XrayCommonClass { + + constructor(id = RandomUtil.randomUUID(), flow = "") { + super(); + this.id = id; + this.flow = flow; + } + + static fromJson(json = {}) { + return new Inbound.VLESSSettings.VLESS( + json.id, + json.flow, + ); + } +}; +Inbound.VLESSSettings.Fallback = class extends XrayCommonClass { + constructor(name = "", alpn = '', path = '', dest = '', xver = 0) { + super(); + this.name = name; + this.alpn = alpn; + this.path = path; + this.dest = dest; + this.xver = xver; + } + + toJson() { + let xver = this.xver; + if (!Number.isInteger(xver)) { + xver = 0; + } + return { + name: this.name, + alpn: this.alpn, + path: this.path, + dest: this.dest, + xver: xver, + } + } + + static fromJson(json = []) { + const fallbacks = []; + for (let fallback of json) { + fallbacks.push(new Inbound.VLESSSettings.Fallback( + fallback.name, + fallback.alpn, + fallback.path, + fallback.dest, + fallback.xver, + )) + } + return fallbacks; + } +}; + +Inbound.TrojanSettings = class extends Inbound.Settings { + constructor(protocol, + clients = [new Inbound.TrojanSettings.Client()], + fallbacks = [],) { + super(protocol); + this.clients = clients; + this.fallbacks = fallbacks; + } + + addTrojanFallback() { + this.fallbacks.push(new Inbound.TrojanSettings.Fallback()); + } + + delTrojanFallback(index) { + this.fallbacks.splice(index, 1); + } + + toJson() { + return { + clients: Inbound.TrojanSettings.toJsonArray(this.clients), + fallbacks: Inbound.TrojanSettings.toJsonArray(this.fallbacks), + }; + } + + static fromJson(json = {}) { + const clients = []; + for (const c of json.clients) { + clients.push(Inbound.TrojanSettings.Client.fromJson(c)); + } + return new Inbound.TrojanSettings( + Protocols.TROJAN, + clients, + Inbound.TrojanSettings.Fallback.fromJson(json.fallbacks)); + } +}; +Inbound.TrojanSettings.Client = class extends XrayCommonClass { + constructor(password = RandomUtil.randomSeq(10)) { + super(); + this.password = password; + //this.flow = flow; + } + + toJson() { + return { + password: this.password, + // flow: this.flow, + }; + } + + static fromJson(json = {}) { + return new Inbound.TrojanSettings.Client( + json.password, + // json.flow, + ); + } + +}; + +Inbound.TrojanSettings.Fallback = class extends XrayCommonClass { + constructor(name = "", alpn = '', path = '', dest = '', xver = 0) { + super(); + this.name = name; + this.alpn = alpn; + this.path = path; + this.dest = dest; + this.xver = xver; + } + + toJson() { + let xver = this.xver; + if (!Number.isInteger(xver)) { + xver = 0; + } + return { + name: this.name, + alpn: this.alpn, + path: this.path, + dest: this.dest, + xver: xver, + } + } + + static fromJson(json = []) { + const fallbacks = []; + for (let fallback of json) { + fallbacks.push(new Inbound.TrojanSettings.Fallback( + fallback.name, + fallback.alpn, + fallback.path, + fallback.dest, + fallback.xver, + )) + } + return fallbacks; + } +}; + +Inbound.ShadowsocksSettings = class extends Inbound.Settings { + constructor(protocol, + method = SSMethods.AES_256_GCM, + password = btoa(RandomUtil.randomSeq(32)), + network = 'tcp,udp' + ) { + super(protocol); + this.method = method; + this.password = password; + this.network = network; + } + + static fromJson(json = {}) { + return new Inbound.ShadowsocksSettings( + Protocols.SHADOWSOCKS, + json.method, + json.password, + json.network, + ); + } + + toJson() { + return { + method: this.method, + password: this.password, + network: this.network, + }; + } +}; + +Inbound.DokodemoSettings = class extends Inbound.Settings { + constructor(protocol, address, port, network = 'tcp,udp') { + super(protocol); + this.address = address; + this.port = port; + this.network = network; + } + + static fromJson(json = {}) { + return new Inbound.DokodemoSettings( + Protocols.DOKODEMO, + json.address, + json.port, + json.network, + ); + } + + toJson() { + return { + address: this.address, + port: this.port, + network: this.network, + }; + } +}; + +Inbound.SocksSettings = class extends Inbound.Settings { + constructor(protocol, auth = 'password', accounts = [new Inbound.SocksSettings.SocksAccount()], udp = false, ip = '127.0.0.1') { + super(protocol); + this.auth = auth; + this.accounts = accounts; + this.udp = udp; + this.ip = ip; + } + + addAccount(account) { + this.accounts.push(account); + } + + delAccount(index) { + this.accounts.splice(index, 1); + } + + static fromJson(json = {}) { + let accounts; + if (json.auth === 'password') { + accounts = json.accounts.map( + account => Inbound.SocksSettings.SocksAccount.fromJson(account) + ) + } + return new Inbound.SocksSettings( + Protocols.SOCKS, + json.auth, + accounts, + json.udp, + json.ip, + ); + } + + toJson() { + return { + auth: this.auth, + accounts: this.auth === 'password' ? this.accounts.map(account => account.toJson()) : undefined, + udp: this.udp, + ip: this.ip, + }; + } +}; +Inbound.SocksSettings.SocksAccount = class extends XrayCommonClass { + constructor(user = RandomUtil.randomSeq(10), pass = RandomUtil.randomSeq(10)) { + super(); + this.user = user; + this.pass = pass; + } + + static fromJson(json = {}) { + return new Inbound.SocksSettings.SocksAccount(json.user, json.pass); + } +}; + +Inbound.HttpSettings = class extends Inbound.Settings { + constructor(protocol, accounts = [new Inbound.HttpSettings.HttpAccount()]) { + super(protocol); + this.accounts = accounts; + } + + addAccount(account) { + this.accounts.push(account); + } + + delAccount(index) { + this.accounts.splice(index, 1); + } + + static fromJson(json = {}) { + return new Inbound.HttpSettings( + Protocols.HTTP, + json.accounts.map(account => Inbound.HttpSettings.HttpAccount.fromJson(account)), + ); + } + + toJson() { + return { + accounts: Inbound.HttpSettings.toJsonArray(this.accounts), + }; + } +}; + +Inbound.HttpSettings.HttpAccount = class extends XrayCommonClass { + constructor(user = RandomUtil.randomSeq(10), pass = RandomUtil.randomSeq(10)) { + super(); + this.user = user; + this.pass = pass; + } + + static fromJson(json = {}) { + return new Inbound.HttpSettings.HttpAccount(json.user, json.pass); + } +}; diff --git a/web/html/xui/common_sider.html b/web/html/xui/common_sider.html index 2a837fdc..af17d499 100644 --- a/web/html/xui/common_sider.html +++ b/web/html/xui/common_sider.html @@ -11,10 +11,10 @@ 面板设置 - - - - +