|
|
@@ -1,3359 +0,0 @@
|
|
|
-/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
|
-import dayjs from 'dayjs';
|
|
|
-import { ObjectUtil, RandomUtil, Base64, NumberFormatter, SizeFormatter, Wireguard } from '@/utils';
|
|
|
-import { getRandomRealityTarget } from '@/models/reality-targets';
|
|
|
-
|
|
|
-export const Protocols = {
|
|
|
- VMESS: 'vmess',
|
|
|
- VLESS: 'vless',
|
|
|
- TROJAN: 'trojan',
|
|
|
- SHADOWSOCKS: 'shadowsocks',
|
|
|
- WIREGUARD: 'wireguard',
|
|
|
- HYSTERIA: 'hysteria',
|
|
|
- MIXED: 'mixed',
|
|
|
- HTTP: 'http',
|
|
|
- TUNNEL: 'tunnel',
|
|
|
- TUN: 'tun',
|
|
|
-};
|
|
|
-
|
|
|
-export const SSMethods = {
|
|
|
- AES_256_GCM: 'aes-256-gcm',
|
|
|
- CHACHA20_POLY1305: 'chacha20-poly1305',
|
|
|
- CHACHA20_IETF_POLY1305: 'chacha20-ietf-poly1305',
|
|
|
- XCHACHA20_IETF_POLY1305: 'xchacha20-ietf-poly1305',
|
|
|
- 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',
|
|
|
-};
|
|
|
-
|
|
|
-export const TLS_FLOW_CONTROL = {
|
|
|
- VISION: "xtls-rprx-vision",
|
|
|
- VISION_UDP443: "xtls-rprx-vision-udp443",
|
|
|
-};
|
|
|
-
|
|
|
-export const TLS_VERSION_OPTION = {
|
|
|
- TLS10: "1.0",
|
|
|
- TLS11: "1.1",
|
|
|
- TLS12: "1.2",
|
|
|
- TLS13: "1.3",
|
|
|
-};
|
|
|
-
|
|
|
-export 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_RSA_AES_128_CBC: "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA",
|
|
|
- ECDHE_RSA_AES_256_CBC: "TLS_ECDHE_RSA_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",
|
|
|
-};
|
|
|
-
|
|
|
-export 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",
|
|
|
- UTLS_RONDOMIZEDNOALPN: "randomizednoalpn",
|
|
|
- UTLS_UNSAFE: "unsafe",
|
|
|
-};
|
|
|
-
|
|
|
-export const ALPN_OPTION = {
|
|
|
- H3: "h3",
|
|
|
- H2: "h2",
|
|
|
- HTTP1: "http/1.1",
|
|
|
-};
|
|
|
-
|
|
|
-export const SNIFFING_OPTION = {
|
|
|
- HTTP: "http",
|
|
|
- TLS: "tls",
|
|
|
- QUIC: "quic",
|
|
|
- FAKEDNS: "fakedns"
|
|
|
-};
|
|
|
-
|
|
|
-export const USAGE_OPTION = {
|
|
|
- ENCIPHERMENT: "encipherment",
|
|
|
- VERIFY: "verify",
|
|
|
- ISSUE: "issue",
|
|
|
-};
|
|
|
-
|
|
|
-export const DOMAIN_STRATEGY_OPTION = {
|
|
|
- AS_IS: "AsIs",
|
|
|
- USE_IP: "UseIP",
|
|
|
- USE_IPV6V4: "UseIPv6v4",
|
|
|
- USE_IPV6: "UseIPv6",
|
|
|
- USE_IPV4V6: "UseIPv4v6",
|
|
|
- USE_IPV4: "UseIPv4",
|
|
|
- FORCE_IP: "ForceIP",
|
|
|
- FORCE_IPV6V4: "ForceIPv6v4",
|
|
|
- FORCE_IPV6: "ForceIPv6",
|
|
|
- FORCE_IPV4V6: "ForceIPv4v6",
|
|
|
- FORCE_IPV4: "ForceIPv4",
|
|
|
-};
|
|
|
-
|
|
|
-export const TCP_CONGESTION_OPTION = {
|
|
|
- BBR: "bbr",
|
|
|
- CUBIC: "cubic",
|
|
|
- RENO: "reno",
|
|
|
-};
|
|
|
-
|
|
|
-export const USERS_SECURITY = {
|
|
|
- AES_128_GCM: "aes-128-gcm",
|
|
|
- CHACHA20_POLY1305: "chacha20-poly1305",
|
|
|
- AUTO: "auto",
|
|
|
- NONE: "none",
|
|
|
- ZERO: "zero",
|
|
|
-};
|
|
|
-
|
|
|
-export const MODE_OPTION = {
|
|
|
- AUTO: "auto",
|
|
|
- PACKET_UP: "packet-up",
|
|
|
- STREAM_UP: "stream-up",
|
|
|
- STREAM_ONE: "stream-one",
|
|
|
-};
|
|
|
-
|
|
|
-Object.freeze(Protocols);
|
|
|
-Object.freeze(SSMethods);
|
|
|
-Object.freeze(TLS_FLOW_CONTROL);
|
|
|
-Object.freeze(TLS_VERSION_OPTION);
|
|
|
-Object.freeze(TLS_CIPHER_OPTION);
|
|
|
-Object.freeze(UTLS_FINGERPRINT);
|
|
|
-Object.freeze(ALPN_OPTION);
|
|
|
-Object.freeze(SNIFFING_OPTION);
|
|
|
-Object.freeze(USAGE_OPTION);
|
|
|
-Object.freeze(DOMAIN_STRATEGY_OPTION);
|
|
|
-Object.freeze(TCP_CONGESTION_OPTION);
|
|
|
-Object.freeze(USERS_SECURITY);
|
|
|
-Object.freeze(MODE_OPTION);
|
|
|
-
|
|
|
-export type JsonObject = Record<string, unknown>;
|
|
|
-export interface HeaderEntry { name: string; value: string }
|
|
|
-export interface FallbackEntry {
|
|
|
- dest?: string | number;
|
|
|
- name?: string;
|
|
|
- alpn?: string;
|
|
|
- path?: string;
|
|
|
- xver?: number | string;
|
|
|
-}
|
|
|
-
|
|
|
-export class XrayCommonClass {
|
|
|
- [key: string]: any;
|
|
|
-
|
|
|
- static toJsonArray<T extends { toJson(): unknown }>(arr: T[]): unknown[] {
|
|
|
- return arr.map((obj) => obj.toJson());
|
|
|
- }
|
|
|
-
|
|
|
- static fromJson(..._args: unknown[]): XrayCommonClass | undefined {
|
|
|
- return new XrayCommonClass();
|
|
|
- }
|
|
|
-
|
|
|
- toJson(): unknown {
|
|
|
- return this;
|
|
|
- }
|
|
|
-
|
|
|
- static fallbackToJson(fb: FallbackEntry): JsonObject {
|
|
|
- const out: JsonObject = { dest: fb.dest };
|
|
|
- if (fb.name) out.name = fb.name;
|
|
|
- if (fb.alpn) out.alpn = fb.alpn;
|
|
|
- if (fb.path) out.path = fb.path;
|
|
|
- const xver = Number(fb.xver);
|
|
|
- if (Number.isInteger(xver) && xver > 0) out.xver = xver;
|
|
|
- return out;
|
|
|
- }
|
|
|
-
|
|
|
- toString(format: boolean = true): string {
|
|
|
- return format ? JSON.stringify(this.toJson(), null, 2) : JSON.stringify(this.toJson());
|
|
|
- }
|
|
|
-
|
|
|
- static toHeaders(v2Headers: unknown): HeaderEntry[] {
|
|
|
- const newHeaders: HeaderEntry[] = [];
|
|
|
- if (v2Headers && typeof v2Headers === 'object') {
|
|
|
- const map = v2Headers as Record<string, string | string[]>;
|
|
|
- Object.keys(map).forEach((key: string) => {
|
|
|
- const values = map[key];
|
|
|
- if (typeof values === 'string') {
|
|
|
- newHeaders.push({ name: key, value: values });
|
|
|
- } else if (Array.isArray(values)) {
|
|
|
- for (let i = 0; i < values.length; ++i) {
|
|
|
- newHeaders.push({ name: key, value: values[i] });
|
|
|
- }
|
|
|
- }
|
|
|
- });
|
|
|
- }
|
|
|
- return newHeaders;
|
|
|
- }
|
|
|
-
|
|
|
- static toV2Headers(headers: HeaderEntry[], arr: boolean = true): Record<string, string | string[]> {
|
|
|
- const v2Headers: Record<string, string | string[]> = {};
|
|
|
- for (let i = 0; i < headers.length; ++i) {
|
|
|
- const name = headers[i].name;
|
|
|
- const value = headers[i].value;
|
|
|
- if (ObjectUtil.isEmpty(name) || ObjectUtil.isEmpty(value)) {
|
|
|
- continue;
|
|
|
- }
|
|
|
- if (!(name in v2Headers)) {
|
|
|
- v2Headers[name] = arr ? [value] : value;
|
|
|
- } else {
|
|
|
- const existing = v2Headers[name];
|
|
|
- if (arr && Array.isArray(existing)) {
|
|
|
- existing.push(value);
|
|
|
- } else {
|
|
|
- v2Headers[name] = value;
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- return v2Headers;
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-export class TcpStreamSettings extends XrayCommonClass {
|
|
|
- static TcpRequest: any;
|
|
|
- static TcpResponse: any;
|
|
|
-
|
|
|
- constructor(
|
|
|
- acceptProxyProtocol: any = false,
|
|
|
- type: any = 'none',
|
|
|
- request: any = new TcpStreamSettings.TcpRequest(),
|
|
|
- response = new TcpStreamSettings.TcpResponse(),
|
|
|
- ) {
|
|
|
- super();
|
|
|
- this.acceptProxyProtocol = acceptProxyProtocol;
|
|
|
- this.type = type;
|
|
|
- this.request = request;
|
|
|
- this.response = response;
|
|
|
- }
|
|
|
-
|
|
|
- static fromJson(json: any = {}) {
|
|
|
- let header = json.header;
|
|
|
- if (!header) {
|
|
|
- header = {};
|
|
|
- }
|
|
|
- return new TcpStreamSettings(json.acceptProxyProtocol,
|
|
|
- header.type,
|
|
|
- TcpStreamSettings.TcpRequest.fromJson(header.request),
|
|
|
- TcpStreamSettings.TcpResponse.fromJson(header.response),
|
|
|
- );
|
|
|
- }
|
|
|
-
|
|
|
- toJson() {
|
|
|
- const json: any = {};
|
|
|
- if (this.acceptProxyProtocol) {
|
|
|
- json.acceptProxyProtocol = true;
|
|
|
- }
|
|
|
- if (this.type === 'http') {
|
|
|
- json.header = {
|
|
|
- type: 'http',
|
|
|
- request: this.request.toJson(),
|
|
|
- response: this.response.toJson(),
|
|
|
- };
|
|
|
- } else if (this.type && this.type !== 'none') {
|
|
|
- json.header = { type: this.type };
|
|
|
- }
|
|
|
- return json;
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-TcpStreamSettings.TcpRequest = class extends XrayCommonClass {
|
|
|
- constructor(
|
|
|
- version = '1.1',
|
|
|
- method = 'GET',
|
|
|
- path = ['/'],
|
|
|
- headers: any[] = [],
|
|
|
- ) {
|
|
|
- super();
|
|
|
- this.version = version;
|
|
|
- this.method = method;
|
|
|
- this.path = path.length === 0 ? ['/'] : path;
|
|
|
- this.headers = headers;
|
|
|
- }
|
|
|
-
|
|
|
- addPath(path: any) {
|
|
|
- this.path.push(path);
|
|
|
- }
|
|
|
-
|
|
|
- removePath(index: number) {
|
|
|
- this.path.splice(index, 1);
|
|
|
- }
|
|
|
-
|
|
|
- addHeader(name: any, value: any) {
|
|
|
- this.headers.push({ name: name, value: value });
|
|
|
- }
|
|
|
-
|
|
|
- removeHeader(index: number) {
|
|
|
- this.headers.splice(index, 1);
|
|
|
- }
|
|
|
-
|
|
|
- static fromJson(json: any = {}) {
|
|
|
- return new TcpStreamSettings.TcpRequest(
|
|
|
- json.version,
|
|
|
- json.method,
|
|
|
- json.path,
|
|
|
- XrayCommonClass.toHeaders(json.headers),
|
|
|
- );
|
|
|
- }
|
|
|
-
|
|
|
- toJson() {
|
|
|
- return {
|
|
|
- version: this.version,
|
|
|
- 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: any[] = [],
|
|
|
- ) {
|
|
|
- super();
|
|
|
- this.version = version;
|
|
|
- this.status = status;
|
|
|
- this.reason = reason;
|
|
|
- this.headers = headers;
|
|
|
- }
|
|
|
-
|
|
|
- addHeader(name: any, value: any) {
|
|
|
- this.headers.push({ name: name, value: value });
|
|
|
- }
|
|
|
-
|
|
|
- removeHeader(index: number) {
|
|
|
- this.headers.splice(index, 1);
|
|
|
- }
|
|
|
-
|
|
|
- static fromJson(json: any = {}) {
|
|
|
- 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),
|
|
|
- };
|
|
|
- }
|
|
|
-};
|
|
|
-
|
|
|
-export class KcpStreamSettings extends XrayCommonClass {
|
|
|
- constructor(
|
|
|
- mtu = 1350,
|
|
|
- tti = 20,
|
|
|
- uplinkCapacity = 5,
|
|
|
- downlinkCapacity = 20,
|
|
|
- cwndMultiplier = 1,
|
|
|
- maxSendingWindow = 2097152,
|
|
|
- ) {
|
|
|
- super();
|
|
|
- this.mtu = mtu;
|
|
|
- this.tti = tti;
|
|
|
- this.upCap = uplinkCapacity;
|
|
|
- this.downCap = downlinkCapacity;
|
|
|
- this.cwndMultiplier = cwndMultiplier;
|
|
|
- this.maxSendingWindow = maxSendingWindow;
|
|
|
- }
|
|
|
-
|
|
|
- static fromJson(json: any = {}) {
|
|
|
- return new KcpStreamSettings(
|
|
|
- json.mtu,
|
|
|
- json.tti,
|
|
|
- json.uplinkCapacity,
|
|
|
- json.downlinkCapacity,
|
|
|
- json.cwndMultiplier,
|
|
|
- json.maxSendingWindow,
|
|
|
- );
|
|
|
- }
|
|
|
-
|
|
|
- toJson() {
|
|
|
- return {
|
|
|
- mtu: this.mtu,
|
|
|
- tti: this.tti,
|
|
|
- uplinkCapacity: this.upCap,
|
|
|
- downlinkCapacity: this.downCap,
|
|
|
- cwndMultiplier: this.cwndMultiplier,
|
|
|
- maxSendingWindow: this.maxSendingWindow,
|
|
|
- };
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-export class WsStreamSettings extends XrayCommonClass {
|
|
|
- constructor(
|
|
|
- acceptProxyProtocol: any = false,
|
|
|
- path = '/',
|
|
|
- host = '',
|
|
|
- headers: any[] = [],
|
|
|
- heartbeatPeriod = 0,
|
|
|
- ) {
|
|
|
- super();
|
|
|
- this.acceptProxyProtocol = acceptProxyProtocol;
|
|
|
- this.path = path;
|
|
|
- this.host = host;
|
|
|
- this.headers = headers;
|
|
|
- this.heartbeatPeriod = heartbeatPeriod;
|
|
|
- }
|
|
|
-
|
|
|
- addHeader(name: any, value: any) {
|
|
|
- this.headers.push({ name: name, value: value });
|
|
|
- }
|
|
|
-
|
|
|
- removeHeader(index: number) {
|
|
|
- this.headers.splice(index, 1);
|
|
|
- }
|
|
|
-
|
|
|
- static fromJson(json: any = {}) {
|
|
|
- return new WsStreamSettings(
|
|
|
- json.acceptProxyProtocol,
|
|
|
- json.path,
|
|
|
- json.host,
|
|
|
- XrayCommonClass.toHeaders(json.headers),
|
|
|
- json.heartbeatPeriod,
|
|
|
- );
|
|
|
- }
|
|
|
-
|
|
|
- toJson() {
|
|
|
- return {
|
|
|
- acceptProxyProtocol: this.acceptProxyProtocol,
|
|
|
- path: this.path,
|
|
|
- host: this.host,
|
|
|
- headers: XrayCommonClass.toV2Headers(this.headers, false),
|
|
|
- heartbeatPeriod: this.heartbeatPeriod,
|
|
|
- };
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-export class GrpcStreamSettings extends XrayCommonClass {
|
|
|
- constructor(
|
|
|
- serviceName = "",
|
|
|
- authority = "",
|
|
|
- multiMode = false,
|
|
|
- ) {
|
|
|
- super();
|
|
|
- this.serviceName = serviceName;
|
|
|
- this.authority = authority;
|
|
|
- this.multiMode = multiMode;
|
|
|
- }
|
|
|
-
|
|
|
- static fromJson(json: any = {}) {
|
|
|
- return new GrpcStreamSettings(
|
|
|
- json.serviceName,
|
|
|
- json.authority,
|
|
|
- json.multiMode
|
|
|
- );
|
|
|
- }
|
|
|
-
|
|
|
- toJson() {
|
|
|
- return {
|
|
|
- serviceName: this.serviceName,
|
|
|
- authority: this.authority,
|
|
|
- multiMode: this.multiMode,
|
|
|
- }
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-export class HTTPUpgradeStreamSettings extends XrayCommonClass {
|
|
|
- constructor(
|
|
|
- acceptProxyProtocol: any = false,
|
|
|
- path = '/',
|
|
|
- host = '',
|
|
|
- headers: any[] = []
|
|
|
- ) {
|
|
|
- super();
|
|
|
- this.acceptProxyProtocol = acceptProxyProtocol;
|
|
|
- this.path = path;
|
|
|
- this.host = host;
|
|
|
- this.headers = headers;
|
|
|
- }
|
|
|
-
|
|
|
- addHeader(name: any, value: any) {
|
|
|
- this.headers.push({ name: name, value: value });
|
|
|
- }
|
|
|
-
|
|
|
- removeHeader(index: number) {
|
|
|
- this.headers.splice(index, 1);
|
|
|
- }
|
|
|
-
|
|
|
- static fromJson(json: any = {}) {
|
|
|
- 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),
|
|
|
- };
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-// Mirrors the inbound (server-side) view of Xray-core's SplitHTTPConfig
|
|
|
-// (infra/conf/transport_internet.go). Only fields the server actually
|
|
|
-// reads at runtime, plus the bidirectional fields the server enforces,
|
|
|
-// live here. Most client-only fields (uplinkChunkSize, noGRPCHeader,
|
|
|
-// scMinPostsIntervalMs, xmux, downloadSettings) belong on the outbound
|
|
|
-// class instead.
|
|
|
-//
|
|
|
-// `headers` and `uplinkHTTPMethod` are client-only at runtime (xray's
|
|
|
-// listener doesn't read them) but we keep them here so the admin can set
|
|
|
-// values that get embedded into the share link's `extra` blob.
|
|
|
-export class xHTTPStreamSettings extends XrayCommonClass {
|
|
|
- constructor(
|
|
|
- // Bidirectional — must match between client and server
|
|
|
- path = '/',
|
|
|
- host = '',
|
|
|
- mode = MODE_OPTION.AUTO,
|
|
|
- xPaddingBytes = "100-1000",
|
|
|
- xPaddingObfsMode = false,
|
|
|
- xPaddingKey = '',
|
|
|
- xPaddingHeader = '',
|
|
|
- xPaddingPlacement = '',
|
|
|
- xPaddingMethod = '',
|
|
|
- sessionPlacement = '',
|
|
|
- sessionKey = '',
|
|
|
- seqPlacement = '',
|
|
|
- seqKey = '',
|
|
|
- uplinkDataPlacement = '',
|
|
|
- uplinkDataKey = '',
|
|
|
- scMaxEachPostBytes = "1000000",
|
|
|
- // Server-side only
|
|
|
- noSSEHeader = false,
|
|
|
- scMaxBufferedPosts = 30,
|
|
|
- scStreamUpServerSecs = "20-80",
|
|
|
- serverMaxHeaderBytes = 0,
|
|
|
- // URL-share only — embedded in the link's `extra` blob so clients
|
|
|
- // pick them up; xray's listener ignores them at runtime.
|
|
|
- uplinkHTTPMethod = '',
|
|
|
- headers: any[] = [],
|
|
|
- ) {
|
|
|
- super();
|
|
|
- this.path = path;
|
|
|
- this.host = host;
|
|
|
- this.mode = mode;
|
|
|
- this.xPaddingBytes = xPaddingBytes;
|
|
|
- this.xPaddingObfsMode = xPaddingObfsMode;
|
|
|
- this.xPaddingKey = xPaddingKey;
|
|
|
- this.xPaddingHeader = xPaddingHeader;
|
|
|
- this.xPaddingPlacement = xPaddingPlacement;
|
|
|
- this.xPaddingMethod = xPaddingMethod;
|
|
|
- this.sessionPlacement = sessionPlacement;
|
|
|
- this.sessionKey = sessionKey;
|
|
|
- this.seqPlacement = seqPlacement;
|
|
|
- this.seqKey = seqKey;
|
|
|
- this.uplinkDataPlacement = uplinkDataPlacement;
|
|
|
- this.uplinkDataKey = uplinkDataKey;
|
|
|
- this.scMaxEachPostBytes = scMaxEachPostBytes;
|
|
|
- this.noSSEHeader = noSSEHeader;
|
|
|
- this.scMaxBufferedPosts = scMaxBufferedPosts;
|
|
|
- this.scStreamUpServerSecs = scStreamUpServerSecs;
|
|
|
- this.serverMaxHeaderBytes = serverMaxHeaderBytes;
|
|
|
- this.uplinkHTTPMethod = uplinkHTTPMethod;
|
|
|
- this.headers = headers;
|
|
|
- }
|
|
|
-
|
|
|
- addHeader(name: any, value: any) {
|
|
|
- this.headers.push({ name: name, value: value });
|
|
|
- }
|
|
|
-
|
|
|
- removeHeader(index: number) {
|
|
|
- this.headers.splice(index, 1);
|
|
|
- }
|
|
|
-
|
|
|
- static fromJson(json: any = {}) {
|
|
|
- return new xHTTPStreamSettings(
|
|
|
- json.path,
|
|
|
- json.host,
|
|
|
- json.mode,
|
|
|
- json.xPaddingBytes,
|
|
|
- json.xPaddingObfsMode,
|
|
|
- json.xPaddingKey,
|
|
|
- json.xPaddingHeader,
|
|
|
- json.xPaddingPlacement,
|
|
|
- json.xPaddingMethod,
|
|
|
- json.sessionPlacement,
|
|
|
- json.sessionKey,
|
|
|
- json.seqPlacement,
|
|
|
- json.seqKey,
|
|
|
- json.uplinkDataPlacement,
|
|
|
- json.uplinkDataKey,
|
|
|
- json.scMaxEachPostBytes,
|
|
|
- json.noSSEHeader,
|
|
|
- json.scMaxBufferedPosts,
|
|
|
- json.scStreamUpServerSecs,
|
|
|
- json.serverMaxHeaderBytes,
|
|
|
- json.uplinkHTTPMethod,
|
|
|
- XrayCommonClass.toHeaders(json.headers),
|
|
|
- );
|
|
|
- }
|
|
|
-
|
|
|
- toJson() {
|
|
|
- return {
|
|
|
- path: this.path,
|
|
|
- host: this.host,
|
|
|
- mode: this.mode,
|
|
|
- xPaddingBytes: this.xPaddingBytes,
|
|
|
- xPaddingObfsMode: this.xPaddingObfsMode,
|
|
|
- xPaddingKey: this.xPaddingKey,
|
|
|
- xPaddingHeader: this.xPaddingHeader,
|
|
|
- xPaddingPlacement: this.xPaddingPlacement,
|
|
|
- xPaddingMethod: this.xPaddingMethod,
|
|
|
- sessionPlacement: this.sessionPlacement,
|
|
|
- sessionKey: this.sessionKey,
|
|
|
- seqPlacement: this.seqPlacement,
|
|
|
- seqKey: this.seqKey,
|
|
|
- uplinkDataPlacement: this.uplinkDataPlacement,
|
|
|
- uplinkDataKey: this.uplinkDataKey,
|
|
|
- scMaxEachPostBytes: this.scMaxEachPostBytes,
|
|
|
- noSSEHeader: this.noSSEHeader,
|
|
|
- scMaxBufferedPosts: this.scMaxBufferedPosts,
|
|
|
- scStreamUpServerSecs: this.scStreamUpServerSecs,
|
|
|
- serverMaxHeaderBytes: this.serverMaxHeaderBytes,
|
|
|
- uplinkHTTPMethod: this.uplinkHTTPMethod,
|
|
|
- headers: XrayCommonClass.toV2Headers(this.headers, false),
|
|
|
- };
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-export class HysteriaStreamSettings extends XrayCommonClass {
|
|
|
- constructor(
|
|
|
- protocol?: any,
|
|
|
- version: any = 2,
|
|
|
- auth: any = '',
|
|
|
- udpIdleTimeout: any = 60,
|
|
|
- masquerade?: any,
|
|
|
- ) {
|
|
|
- super();
|
|
|
- this.protocol = protocol;
|
|
|
- this.version = version;
|
|
|
- this.auth = auth;
|
|
|
- this.udpIdleTimeout = udpIdleTimeout;
|
|
|
- this.masquerade = masquerade;
|
|
|
- }
|
|
|
-
|
|
|
- static fromJson(json: any = {}) {
|
|
|
- return new HysteriaStreamSettings(
|
|
|
- json.protocol,
|
|
|
- json.version ?? 2,
|
|
|
- json.auth ?? '',
|
|
|
- json.udpIdleTimeout ?? 60,
|
|
|
- json.masquerade ? HysteriaMasquerade.fromJson(json.masquerade) : undefined,
|
|
|
- );
|
|
|
- }
|
|
|
-
|
|
|
- toJson() {
|
|
|
- return {
|
|
|
- protocol: this.protocol,
|
|
|
- version: this.version,
|
|
|
- auth: this.auth,
|
|
|
- udpIdleTimeout: this.udpIdleTimeout,
|
|
|
- masquerade: this.masqueradeSwitch ? this.masquerade.toJson() : undefined,
|
|
|
- };
|
|
|
- }
|
|
|
-
|
|
|
- get masqueradeSwitch() {
|
|
|
- return this.masquerade != undefined;
|
|
|
- }
|
|
|
-
|
|
|
- set masqueradeSwitch(value) {
|
|
|
- this.masquerade = value ? new HysteriaMasquerade() : undefined;
|
|
|
- }
|
|
|
-};
|
|
|
-
|
|
|
-export class HysteriaMasquerade extends XrayCommonClass {
|
|
|
- constructor(
|
|
|
- type = 'proxy',
|
|
|
- dir = '',
|
|
|
- url = '',
|
|
|
- rewriteHost = false,
|
|
|
- insecure = false,
|
|
|
- content = '',
|
|
|
- headers: any[] = [],
|
|
|
- statusCode = 0,
|
|
|
- ) {
|
|
|
- super();
|
|
|
- this.type = type;
|
|
|
- this.dir = dir;
|
|
|
- this.url = url;
|
|
|
- this.rewriteHost = rewriteHost;
|
|
|
- this.insecure = insecure;
|
|
|
- this.content = content;
|
|
|
- this.headers = headers;
|
|
|
- this.statusCode = statusCode;
|
|
|
- }
|
|
|
-
|
|
|
- addHeader(name: any, value: any) {
|
|
|
- this.headers.push({ name: name, value: value });
|
|
|
- }
|
|
|
-
|
|
|
- removeHeader(index: number) {
|
|
|
- this.headers.splice(index, 1);
|
|
|
- }
|
|
|
-
|
|
|
- static fromJson(json: any = {}) {
|
|
|
- const type = ['proxy', 'file', 'string'].includes(json.type) ? json.type : 'proxy';
|
|
|
- return new HysteriaMasquerade(
|
|
|
- type,
|
|
|
- json.dir,
|
|
|
- json.url,
|
|
|
- json.rewriteHost,
|
|
|
- json.insecure,
|
|
|
- json.content,
|
|
|
- XrayCommonClass.toHeaders(json.headers),
|
|
|
- json.statusCode,
|
|
|
- );
|
|
|
- }
|
|
|
-
|
|
|
- toJson() {
|
|
|
- return {
|
|
|
- type: this.type,
|
|
|
- dir: this.dir,
|
|
|
- url: this.url,
|
|
|
- rewriteHost: this.rewriteHost,
|
|
|
- insecure: this.insecure,
|
|
|
- content: this.content,
|
|
|
- headers: XrayCommonClass.toV2Headers(this.headers, false),
|
|
|
- statusCode: this.statusCode,
|
|
|
- };
|
|
|
- }
|
|
|
-};
|
|
|
-export class TlsStreamSettings extends XrayCommonClass {
|
|
|
- static Cert: any;
|
|
|
- static Settings: any;
|
|
|
-
|
|
|
- constructor(
|
|
|
- serverName: any = '',
|
|
|
- minVersion = TLS_VERSION_OPTION.TLS12,
|
|
|
- maxVersion = TLS_VERSION_OPTION.TLS13,
|
|
|
- cipherSuites = '',
|
|
|
- rejectUnknownSni = false,
|
|
|
- disableSystemRoot = false,
|
|
|
- enableSessionResumption = false,
|
|
|
- certificates = [new TlsStreamSettings.Cert()],
|
|
|
- alpn = [ALPN_OPTION.H2, ALPN_OPTION.HTTP1],
|
|
|
- echServerKeys = '',
|
|
|
- settings = new TlsStreamSettings.Settings()
|
|
|
- ) {
|
|
|
- super();
|
|
|
- this.sni = serverName;
|
|
|
- this.minVersion = minVersion;
|
|
|
- this.maxVersion = maxVersion;
|
|
|
- this.cipherSuites = cipherSuites;
|
|
|
- this.rejectUnknownSni = rejectUnknownSni;
|
|
|
- this.disableSystemRoot = disableSystemRoot;
|
|
|
- this.enableSessionResumption = enableSessionResumption;
|
|
|
- this.certs = certificates;
|
|
|
- this.alpn = alpn;
|
|
|
- this.echServerKeys = echServerKeys;
|
|
|
- this.settings = settings;
|
|
|
- }
|
|
|
-
|
|
|
- addCert() {
|
|
|
- this.certs.push(new TlsStreamSettings.Cert());
|
|
|
- }
|
|
|
-
|
|
|
- removeCert(index: number) {
|
|
|
- this.certs.splice(index, 1);
|
|
|
- }
|
|
|
-
|
|
|
- static fromJson(json: any = {}) {
|
|
|
- let certs;
|
|
|
- let settings;
|
|
|
- if (!ObjectUtil.isEmpty(json.certificates)) {
|
|
|
- certs = json.certificates.map((cert: any) => TlsStreamSettings.Cert.fromJson(cert));
|
|
|
- }
|
|
|
-
|
|
|
- if (!ObjectUtil.isEmpty(json.settings)) {
|
|
|
- settings = new TlsStreamSettings.Settings(json.settings.fingerprint, json.settings.echConfigList);
|
|
|
- }
|
|
|
- return new TlsStreamSettings(
|
|
|
- json.serverName,
|
|
|
- json.minVersion,
|
|
|
- json.maxVersion,
|
|
|
- json.cipherSuites,
|
|
|
- json.rejectUnknownSni,
|
|
|
- json.disableSystemRoot,
|
|
|
- json.enableSessionResumption,
|
|
|
- certs,
|
|
|
- json.alpn,
|
|
|
- json.echServerKeys,
|
|
|
- settings,
|
|
|
- );
|
|
|
- }
|
|
|
-
|
|
|
- toJson() {
|
|
|
- return {
|
|
|
- serverName: this.sni,
|
|
|
- minVersion: this.minVersion,
|
|
|
- maxVersion: this.maxVersion,
|
|
|
- cipherSuites: this.cipherSuites,
|
|
|
- rejectUnknownSni: this.rejectUnknownSni,
|
|
|
- disableSystemRoot: this.disableSystemRoot,
|
|
|
- enableSessionResumption: this.enableSessionResumption,
|
|
|
- certificates: TlsStreamSettings.toJsonArray(this.certs),
|
|
|
- alpn: this.alpn,
|
|
|
- echServerKeys: this.echServerKeys,
|
|
|
- settings: this.settings,
|
|
|
- };
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-TlsStreamSettings.Cert = class extends XrayCommonClass {
|
|
|
- constructor(
|
|
|
- useFile = true,
|
|
|
- certificateFile = '',
|
|
|
- keyFile = '',
|
|
|
- certificate = '',
|
|
|
- key = '',
|
|
|
- oneTimeLoading = false,
|
|
|
- usage = USAGE_OPTION.ENCIPHERMENT,
|
|
|
- buildChain = false,
|
|
|
- ) {
|
|
|
- super();
|
|
|
- this.useFile = useFile;
|
|
|
- this.certFile = certificateFile;
|
|
|
- this.keyFile = keyFile;
|
|
|
- this.cert = Array.isArray(certificate) ? certificate.join('\n') : certificate;
|
|
|
- this.key = Array.isArray(key) ? key.join('\n') : key;
|
|
|
- this.oneTimeLoading = oneTimeLoading;
|
|
|
- this.usage = usage;
|
|
|
- this.buildChain = buildChain
|
|
|
- }
|
|
|
-
|
|
|
- static fromJson(json: any = {}) {
|
|
|
- if ('certificateFile' in json && 'keyFile' in json) {
|
|
|
- return new TlsStreamSettings.Cert(
|
|
|
- true,
|
|
|
- json.certificateFile,
|
|
|
- json.keyFile, '', '',
|
|
|
- json.oneTimeLoading,
|
|
|
- json.usage,
|
|
|
- json.buildChain,
|
|
|
- );
|
|
|
- } else {
|
|
|
- return new TlsStreamSettings.Cert(
|
|
|
- false, '', '',
|
|
|
- Array.isArray(json.certificate) ? json.certificate.join('\n') : (json.certificate ?? ''),
|
|
|
- Array.isArray(json.key) ? json.key.join('\n') : (json.key ?? ''),
|
|
|
- json.oneTimeLoading,
|
|
|
- json.usage,
|
|
|
- json.buildChain,
|
|
|
- );
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- toJson() {
|
|
|
- if (this.useFile) {
|
|
|
- return {
|
|
|
- certificateFile: this.certFile,
|
|
|
- keyFile: this.keyFile,
|
|
|
- oneTimeLoading: this.oneTimeLoading,
|
|
|
- usage: this.usage,
|
|
|
- buildChain: this.buildChain,
|
|
|
- };
|
|
|
- } else {
|
|
|
- return {
|
|
|
- certificate: this.cert.split('\n'),
|
|
|
- key: this.key.split('\n'),
|
|
|
- oneTimeLoading: this.oneTimeLoading,
|
|
|
- usage: this.usage,
|
|
|
- buildChain: this.buildChain,
|
|
|
- };
|
|
|
- }
|
|
|
- }
|
|
|
-};
|
|
|
-
|
|
|
-TlsStreamSettings.Settings = class extends XrayCommonClass {
|
|
|
- constructor(
|
|
|
- fingerprint = UTLS_FINGERPRINT.UTLS_CHROME,
|
|
|
- echConfigList = '',
|
|
|
- ) {
|
|
|
- super();
|
|
|
- this.fingerprint = fingerprint;
|
|
|
- this.echConfigList = echConfigList;
|
|
|
- }
|
|
|
- static fromJson(json: any = {}) {
|
|
|
- return new TlsStreamSettings.Settings(
|
|
|
- json.fingerprint,
|
|
|
- json.echConfigList,
|
|
|
- );
|
|
|
- }
|
|
|
- toJson() {
|
|
|
- return {
|
|
|
- fingerprint: this.fingerprint,
|
|
|
- echConfigList: this.echConfigList
|
|
|
- };
|
|
|
- }
|
|
|
-};
|
|
|
-
|
|
|
-
|
|
|
-export class RealityStreamSettings extends XrayCommonClass {
|
|
|
- static Settings: any;
|
|
|
-
|
|
|
- constructor(
|
|
|
- show: any = false,
|
|
|
- xver = 0,
|
|
|
- target = '',
|
|
|
- serverNames = '',
|
|
|
- privateKey = '',
|
|
|
- minClientVer = '',
|
|
|
- maxClientVer = '',
|
|
|
- maxTimediff = 0,
|
|
|
- shortIds = RandomUtil.randomShortIds(),
|
|
|
- mldsa65Seed = '',
|
|
|
- settings = new RealityStreamSettings.Settings()
|
|
|
- ) {
|
|
|
- super();
|
|
|
- // If target/serverNames are not provided, use random values
|
|
|
- if (!target && !serverNames) {
|
|
|
- const randomTarget = getRandomRealityTarget();
|
|
|
- target = randomTarget.target;
|
|
|
- serverNames = randomTarget.sni;
|
|
|
- }
|
|
|
- this.show = show;
|
|
|
- this.xver = xver;
|
|
|
- this.target = target;
|
|
|
- this.serverNames = Array.isArray(serverNames) ? serverNames.join(",") : serverNames;
|
|
|
- this.privateKey = privateKey;
|
|
|
- this.minClientVer = minClientVer;
|
|
|
- this.maxClientVer = maxClientVer;
|
|
|
- this.maxTimediff = maxTimediff;
|
|
|
- this.shortIds = Array.isArray(shortIds) ? shortIds.join(",") : shortIds;
|
|
|
- this.mldsa65Seed = mldsa65Seed;
|
|
|
- this.settings = settings;
|
|
|
- }
|
|
|
-
|
|
|
- static fromJson(json: any = {}) {
|
|
|
- let settings;
|
|
|
- if (!ObjectUtil.isEmpty(json.settings)) {
|
|
|
- settings = new RealityStreamSettings.Settings(
|
|
|
- json.settings.publicKey,
|
|
|
- json.settings.fingerprint,
|
|
|
- json.settings.serverName,
|
|
|
- json.settings.spiderX,
|
|
|
- json.settings.mldsa65Verify,
|
|
|
- );
|
|
|
- }
|
|
|
- return new RealityStreamSettings(
|
|
|
- json.show,
|
|
|
- json.xver,
|
|
|
- json.target,
|
|
|
- json.serverNames,
|
|
|
- json.privateKey,
|
|
|
- json.minClientVer,
|
|
|
- json.maxClientVer,
|
|
|
- json.maxTimediff,
|
|
|
- json.shortIds,
|
|
|
- json.mldsa65Seed,
|
|
|
- settings,
|
|
|
- );
|
|
|
- }
|
|
|
-
|
|
|
- toJson() {
|
|
|
- return {
|
|
|
- show: this.show,
|
|
|
- xver: this.xver,
|
|
|
- target: this.target,
|
|
|
- serverNames: this.serverNames.split(","),
|
|
|
- privateKey: this.privateKey,
|
|
|
- minClientVer: this.minClientVer,
|
|
|
- maxClientVer: this.maxClientVer,
|
|
|
- maxTimediff: this.maxTimediff,
|
|
|
- shortIds: this.shortIds.split(","),
|
|
|
- mldsa65Seed: this.mldsa65Seed,
|
|
|
- settings: this.settings,
|
|
|
- };
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-RealityStreamSettings.Settings = class extends XrayCommonClass {
|
|
|
- constructor(
|
|
|
- publicKey = '',
|
|
|
- fingerprint = UTLS_FINGERPRINT.UTLS_CHROME,
|
|
|
- serverName = '',
|
|
|
- spiderX = '/',
|
|
|
- mldsa65Verify = ''
|
|
|
- ) {
|
|
|
- super();
|
|
|
- this.publicKey = publicKey;
|
|
|
- this.fingerprint = fingerprint;
|
|
|
- this.serverName = serverName;
|
|
|
- this.spiderX = spiderX;
|
|
|
- this.mldsa65Verify = mldsa65Verify;
|
|
|
- }
|
|
|
- static fromJson(json: any = {}) {
|
|
|
- return new RealityStreamSettings.Settings(
|
|
|
- json.publicKey,
|
|
|
- json.fingerprint,
|
|
|
- json.serverName,
|
|
|
- json.spiderX,
|
|
|
- json.mldsa65Verify
|
|
|
- );
|
|
|
- }
|
|
|
- toJson() {
|
|
|
- return {
|
|
|
- publicKey: this.publicKey,
|
|
|
- fingerprint: this.fingerprint,
|
|
|
- serverName: this.serverName,
|
|
|
- spiderX: this.spiderX,
|
|
|
- mldsa65Verify: this.mldsa65Verify
|
|
|
- };
|
|
|
- }
|
|
|
-};
|
|
|
-
|
|
|
-export class SockoptStreamSettings extends XrayCommonClass {
|
|
|
- constructor(
|
|
|
- acceptProxyProtocol: any = false,
|
|
|
- tcpFastOpen = false,
|
|
|
- mark = 0,
|
|
|
- tproxy = "off",
|
|
|
- tcpMptcp = false,
|
|
|
- penetrate = false,
|
|
|
- domainStrategy = DOMAIN_STRATEGY_OPTION.USE_IP,
|
|
|
- tcpMaxSeg = 1440,
|
|
|
- dialerProxy = "",
|
|
|
- tcpKeepAliveInterval = 0,
|
|
|
- tcpKeepAliveIdle = 300,
|
|
|
- tcpUserTimeout = 10000,
|
|
|
- tcpcongestion = TCP_CONGESTION_OPTION.BBR,
|
|
|
- V6Only = false,
|
|
|
- tcpWindowClamp = 600,
|
|
|
- interfaceName = "",
|
|
|
- trustedXForwardedFor = [],
|
|
|
- ) {
|
|
|
- super();
|
|
|
- this.acceptProxyProtocol = acceptProxyProtocol;
|
|
|
- this.tcpFastOpen = tcpFastOpen;
|
|
|
- this.mark = mark;
|
|
|
- this.tproxy = tproxy;
|
|
|
- this.tcpMptcp = tcpMptcp;
|
|
|
- this.penetrate = penetrate;
|
|
|
- this.domainStrategy = domainStrategy;
|
|
|
- this.tcpMaxSeg = tcpMaxSeg;
|
|
|
- this.dialerProxy = dialerProxy;
|
|
|
- this.tcpKeepAliveInterval = tcpKeepAliveInterval;
|
|
|
- this.tcpKeepAliveIdle = tcpKeepAliveIdle;
|
|
|
- this.tcpUserTimeout = tcpUserTimeout;
|
|
|
- this.tcpcongestion = tcpcongestion;
|
|
|
- this.V6Only = V6Only;
|
|
|
- this.tcpWindowClamp = tcpWindowClamp;
|
|
|
- this.interfaceName = interfaceName;
|
|
|
- this.trustedXForwardedFor = trustedXForwardedFor;
|
|
|
- }
|
|
|
-
|
|
|
- static fromJson(json: any = {}) {
|
|
|
- if (Object.keys(json).length === 0) return undefined;
|
|
|
- return new SockoptStreamSettings(
|
|
|
- json.acceptProxyProtocol,
|
|
|
- json.tcpFastOpen,
|
|
|
- json.mark,
|
|
|
- json.tproxy,
|
|
|
- json.tcpMptcp,
|
|
|
- json.penetrate,
|
|
|
- json.domainStrategy,
|
|
|
- json.tcpMaxSeg,
|
|
|
- json.dialerProxy,
|
|
|
- json.tcpKeepAliveInterval,
|
|
|
- json.tcpKeepAliveIdle,
|
|
|
- json.tcpUserTimeout,
|
|
|
- json.tcpcongestion,
|
|
|
- json.V6Only,
|
|
|
- json.tcpWindowClamp,
|
|
|
- json.interface,
|
|
|
- json.trustedXForwardedFor || [],
|
|
|
- );
|
|
|
- }
|
|
|
-
|
|
|
- toJson() {
|
|
|
- const result: any = {
|
|
|
- acceptProxyProtocol: this.acceptProxyProtocol,
|
|
|
- tcpFastOpen: this.tcpFastOpen,
|
|
|
- mark: this.mark,
|
|
|
- tproxy: this.tproxy,
|
|
|
- tcpMptcp: this.tcpMptcp,
|
|
|
- penetrate: this.penetrate,
|
|
|
- domainStrategy: this.domainStrategy,
|
|
|
- tcpMaxSeg: this.tcpMaxSeg,
|
|
|
- dialerProxy: this.dialerProxy,
|
|
|
- tcpKeepAliveInterval: this.tcpKeepAliveInterval,
|
|
|
- tcpKeepAliveIdle: this.tcpKeepAliveIdle,
|
|
|
- tcpUserTimeout: this.tcpUserTimeout,
|
|
|
- tcpcongestion: this.tcpcongestion,
|
|
|
- V6Only: this.V6Only,
|
|
|
- tcpWindowClamp: this.tcpWindowClamp,
|
|
|
- interface: this.interfaceName,
|
|
|
- };
|
|
|
- if (this.trustedXForwardedFor && this.trustedXForwardedFor.length > 0) {
|
|
|
- result.trustedXForwardedFor = this.trustedXForwardedFor;
|
|
|
- }
|
|
|
- return result;
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-export class UdpMask extends XrayCommonClass {
|
|
|
- constructor(type: any = 'salamander', settings: any = {}) {
|
|
|
- super();
|
|
|
- this.type = type;
|
|
|
- this.settings = this._getDefaultSettings(type, settings);
|
|
|
- }
|
|
|
-
|
|
|
- _getDefaultSettings(type: any, settings: any = {}): any {
|
|
|
- switch (type) {
|
|
|
- case 'salamander':
|
|
|
- case 'mkcp-aes128gcm':
|
|
|
- return { password: settings.password || '' };
|
|
|
- case 'header-dns':
|
|
|
- return { domain: settings.domain || '' };
|
|
|
- case 'xdns':
|
|
|
- return { domains: Array.isArray(settings.domains) ? settings.domains : [] };
|
|
|
- case 'xicmp':
|
|
|
- return { ip: settings.ip || '', id: settings.id ?? 0 };
|
|
|
- case 'mkcp-original':
|
|
|
- case 'header-dtls':
|
|
|
- case 'header-srtp':
|
|
|
- case 'header-utp':
|
|
|
- case 'header-wechat':
|
|
|
- case 'header-wireguard':
|
|
|
- return {};
|
|
|
- case 'header-custom':
|
|
|
- return {
|
|
|
- client: Array.isArray(settings.client) ? settings.client : [],
|
|
|
- server: Array.isArray(settings.server) ? settings.server : [],
|
|
|
- };
|
|
|
- case 'noise':
|
|
|
- return {
|
|
|
- reset: settings.reset ?? 0,
|
|
|
- noise: Array.isArray(settings.noise) ? settings.noise : [],
|
|
|
- };
|
|
|
- default:
|
|
|
- return settings;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- static fromJson(json: any = {}) {
|
|
|
- return new UdpMask(
|
|
|
- json.type || 'salamander',
|
|
|
- json.settings || {}
|
|
|
- );
|
|
|
- }
|
|
|
-
|
|
|
- toJson() {
|
|
|
- const cleanItem = (item: any) => {
|
|
|
- const out = { ...item };
|
|
|
- if (out.type === 'array') {
|
|
|
- delete out.packet;
|
|
|
- } else {
|
|
|
- delete out.rand;
|
|
|
- delete out.randRange;
|
|
|
- }
|
|
|
- return out;
|
|
|
- };
|
|
|
-
|
|
|
- let settings = this.settings;
|
|
|
- if (this.type === 'noise' && settings && Array.isArray(settings.noise)) {
|
|
|
- settings = { ...settings, noise: settings.noise.map(cleanItem) };
|
|
|
- } else if (this.type === 'header-custom' && settings) {
|
|
|
- settings = {
|
|
|
- ...settings,
|
|
|
- client: Array.isArray(settings.client) ? settings.client.map(cleanItem) : settings.client,
|
|
|
- server: Array.isArray(settings.server) ? settings.server.map(cleanItem) : settings.server,
|
|
|
- };
|
|
|
- }
|
|
|
-
|
|
|
- return {
|
|
|
- type: this.type,
|
|
|
- settings: (settings && Object.keys(settings).length > 0) ? settings : undefined
|
|
|
- };
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-export class TcpMask extends XrayCommonClass {
|
|
|
- constructor(type: any = 'fragment', settings: any = {}) {
|
|
|
- super();
|
|
|
- this.type = type;
|
|
|
- this.settings = this._getDefaultSettings(type, settings);
|
|
|
- }
|
|
|
-
|
|
|
- _getDefaultSettings(type: any, settings: any = {}): any {
|
|
|
- switch (type) {
|
|
|
- case 'fragment':
|
|
|
- return {
|
|
|
- packets: settings.packets ?? 'tlshello',
|
|
|
- length: settings.length ?? '',
|
|
|
- delay: settings.delay ?? '',
|
|
|
- maxSplit: settings.maxSplit ?? '',
|
|
|
- };
|
|
|
- case 'sudoku':
|
|
|
- return {
|
|
|
- password: settings.password ?? '',
|
|
|
- ascii: settings.ascii ?? '',
|
|
|
- customTable: settings.customTable ?? '',
|
|
|
- customTables: Array.isArray(settings.customTables) ? settings.customTables : [],
|
|
|
- paddingMin: settings.paddingMin ?? 0,
|
|
|
- paddingMax: settings.paddingMax ?? 0,
|
|
|
- };
|
|
|
- case 'header-custom':
|
|
|
- return {
|
|
|
- clients: Array.isArray(settings.clients) ? settings.clients : [],
|
|
|
- servers: Array.isArray(settings.servers) ? settings.servers : [],
|
|
|
- };
|
|
|
- default:
|
|
|
- return settings;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- static fromJson(json: any = {}) {
|
|
|
- return new TcpMask(
|
|
|
- json.type || 'fragment',
|
|
|
- json.settings || {}
|
|
|
- );
|
|
|
- }
|
|
|
-
|
|
|
- toJson() {
|
|
|
- const cleanItem = (item: any) => {
|
|
|
- const out = { ...item };
|
|
|
- if (out.type === 'array') {
|
|
|
- delete out.packet;
|
|
|
- } else {
|
|
|
- delete out.rand;
|
|
|
- delete out.randRange;
|
|
|
- }
|
|
|
- return out;
|
|
|
- };
|
|
|
-
|
|
|
- let settings = this.settings;
|
|
|
- if (this.type === 'header-custom' && settings) {
|
|
|
- const cleanGroup = (group: any) => Array.isArray(group) ? group.map(cleanItem) : group;
|
|
|
- settings = {
|
|
|
- ...settings,
|
|
|
- clients: Array.isArray(settings.clients) ? settings.clients.map(cleanGroup) : settings.clients,
|
|
|
- servers: Array.isArray(settings.servers) ? settings.servers.map(cleanGroup) : settings.servers,
|
|
|
- };
|
|
|
- }
|
|
|
-
|
|
|
- return {
|
|
|
- type: this.type,
|
|
|
- settings: (settings && Object.keys(settings).length > 0) ? settings : undefined
|
|
|
- };
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-export class QuicParams extends XrayCommonClass {
|
|
|
- constructor(
|
|
|
- congestion: any = 'bbr',
|
|
|
- debug: any = false,
|
|
|
- brutalUp: any = 65537,
|
|
|
- brutalDown: any = 65537,
|
|
|
- udpHop: any = undefined,
|
|
|
- initStreamReceiveWindow: any = 8388608,
|
|
|
- maxStreamReceiveWindow: any = 8388608,
|
|
|
- initConnectionReceiveWindow: any = 20971520,
|
|
|
- maxConnectionReceiveWindow: any = 20971520,
|
|
|
- maxIdleTimeout: any = 30,
|
|
|
- keepAlivePeriod: any = 5,
|
|
|
- disablePathMTUDiscovery: any = false,
|
|
|
- maxIncomingStreams = 1024,
|
|
|
- ) {
|
|
|
- super();
|
|
|
- this.congestion = congestion;
|
|
|
- this.debug = debug;
|
|
|
- this.brutalUp = brutalUp;
|
|
|
- this.brutalDown = brutalDown;
|
|
|
- this.udpHop = udpHop;
|
|
|
- this.initStreamReceiveWindow = initStreamReceiveWindow;
|
|
|
- this.maxStreamReceiveWindow = maxStreamReceiveWindow;
|
|
|
- this.initConnectionReceiveWindow = initConnectionReceiveWindow;
|
|
|
- this.maxConnectionReceiveWindow = maxConnectionReceiveWindow;
|
|
|
- this.maxIdleTimeout = maxIdleTimeout;
|
|
|
- this.keepAlivePeriod = keepAlivePeriod;
|
|
|
- this.disablePathMTUDiscovery = disablePathMTUDiscovery;
|
|
|
- this.maxIncomingStreams = maxIncomingStreams;
|
|
|
- }
|
|
|
-
|
|
|
- get hasUdpHop() {
|
|
|
- return this.udpHop != null;
|
|
|
- }
|
|
|
-
|
|
|
- set hasUdpHop(value) {
|
|
|
- this.udpHop = value ? (this.udpHop || { ports: '20000-50000', interval: '5-10' }) : undefined;
|
|
|
- }
|
|
|
-
|
|
|
- static fromJson(json: any = {}) {
|
|
|
- if (!json || Object.keys(json).length === 0) return undefined;
|
|
|
- return new QuicParams(
|
|
|
- json.congestion,
|
|
|
- json.debug,
|
|
|
- json.brutalUp,
|
|
|
- json.brutalDown,
|
|
|
- json.udpHop ? { ports: json.udpHop.ports, interval: json.udpHop.interval } : undefined,
|
|
|
- json.initStreamReceiveWindow,
|
|
|
- json.maxStreamReceiveWindow,
|
|
|
- json.initConnectionReceiveWindow,
|
|
|
- json.maxConnectionReceiveWindow,
|
|
|
- json.maxIdleTimeout,
|
|
|
- json.keepAlivePeriod,
|
|
|
- json.disablePathMTUDiscovery,
|
|
|
- json.maxIncomingStreams,
|
|
|
- );
|
|
|
- }
|
|
|
-
|
|
|
- toJson() {
|
|
|
- const result: any = { congestion: this.congestion };
|
|
|
- if (this.debug) result.debug = this.debug;
|
|
|
- if (['brutal', 'force-brutal'].includes(this.congestion)) {
|
|
|
- if (this.brutalUp) result.brutalUp = this.brutalUp;
|
|
|
- if (this.brutalDown) result.brutalDown = this.brutalDown;
|
|
|
- }
|
|
|
- if (this.udpHop) result.udpHop = { ports: this.udpHop.ports, interval: this.udpHop.interval };
|
|
|
- if (this.initStreamReceiveWindow > 0) result.initStreamReceiveWindow = this.initStreamReceiveWindow;
|
|
|
- if (this.maxStreamReceiveWindow > 0) result.maxStreamReceiveWindow = this.maxStreamReceiveWindow;
|
|
|
- if (this.initConnectionReceiveWindow > 0) result.initConnectionReceiveWindow = this.initConnectionReceiveWindow;
|
|
|
- if (this.maxConnectionReceiveWindow > 0) result.maxConnectionReceiveWindow = this.maxConnectionReceiveWindow;
|
|
|
- if (this.maxIdleTimeout !== 30 && this.maxIdleTimeout > 0) result.maxIdleTimeout = this.maxIdleTimeout;
|
|
|
- if (this.keepAlivePeriod > 0) result.keepAlivePeriod = this.keepAlivePeriod;
|
|
|
- if (this.disablePathMTUDiscovery) result.disablePathMTUDiscovery = this.disablePathMTUDiscovery;
|
|
|
- if (this.maxIncomingStreams > 0) result.maxIncomingStreams = this.maxIncomingStreams;
|
|
|
- return result;
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-export class FinalMaskStreamSettings extends XrayCommonClass {
|
|
|
- constructor(tcp: any[] = [], udp: any[] = [], quicParams: any = undefined) {
|
|
|
- super();
|
|
|
- this.tcp = Array.isArray(tcp) ? tcp.map((t: any) => t instanceof TcpMask ? t : new TcpMask(t.type, t.settings)) : [];
|
|
|
- this.udp = Array.isArray(udp) ? udp.map((u: any) => new UdpMask(u.type, u.settings)) : [new UdpMask((udp as any).type, (udp as any).settings)];
|
|
|
- this.quicParams = quicParams instanceof QuicParams ? quicParams : (quicParams ? QuicParams.fromJson(quicParams) : undefined);
|
|
|
- }
|
|
|
-
|
|
|
- get enableQuicParams() {
|
|
|
- return this.quicParams != null;
|
|
|
- }
|
|
|
-
|
|
|
- set enableQuicParams(value) {
|
|
|
- this.quicParams = value ? (this.quicParams || new QuicParams()) : undefined;
|
|
|
- }
|
|
|
-
|
|
|
- static fromJson(json: any = {}) {
|
|
|
- return new FinalMaskStreamSettings(
|
|
|
- json.tcp || [],
|
|
|
- json.udp || [],
|
|
|
- json.quicParams ? QuicParams.fromJson(json.quicParams) : undefined,
|
|
|
- );
|
|
|
- }
|
|
|
-
|
|
|
- toJson() {
|
|
|
- const result: any = {} as any;
|
|
|
- if (this.tcp && this.tcp.length > 0) {
|
|
|
- result.tcp = this.tcp.map((t: any) => t.toJson());
|
|
|
- }
|
|
|
- if (this.udp && this.udp.length > 0) {
|
|
|
- result.udp = this.udp.map((udp: any) => udp.toJson());
|
|
|
- }
|
|
|
- if (this.quicParams) {
|
|
|
- result.quicParams = this.quicParams.toJson();
|
|
|
- }
|
|
|
- return result;
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-export class StreamSettings extends XrayCommonClass {
|
|
|
- constructor(network = 'tcp',
|
|
|
- security = 'none',
|
|
|
- externalProxy = [],
|
|
|
- tlsSettings = new TlsStreamSettings(),
|
|
|
- realitySettings = new RealityStreamSettings(),
|
|
|
- tcpSettings = new TcpStreamSettings(),
|
|
|
- kcpSettings = new KcpStreamSettings(),
|
|
|
- wsSettings = new WsStreamSettings(),
|
|
|
- grpcSettings = new GrpcStreamSettings(),
|
|
|
- httpupgradeSettings = new HTTPUpgradeStreamSettings(),
|
|
|
- xhttpSettings = new xHTTPStreamSettings(),
|
|
|
- hysteriaSettings = new HysteriaStreamSettings(),
|
|
|
- finalmask = new FinalMaskStreamSettings(),
|
|
|
- sockopt: any = undefined,
|
|
|
- ) {
|
|
|
- super();
|
|
|
- this.network = network;
|
|
|
- this.security = security;
|
|
|
- this.externalProxy = externalProxy;
|
|
|
- this.tls = tlsSettings;
|
|
|
- this.reality = realitySettings;
|
|
|
- this.tcp = tcpSettings;
|
|
|
- this.kcp = kcpSettings;
|
|
|
- this.ws = wsSettings;
|
|
|
- this.grpc = grpcSettings;
|
|
|
- this.httpupgrade = httpupgradeSettings;
|
|
|
- this.xhttp = xhttpSettings;
|
|
|
- this.hysteria = hysteriaSettings;
|
|
|
- this.finalmask = finalmask;
|
|
|
- this.sockopt = sockopt;
|
|
|
- }
|
|
|
-
|
|
|
- addTcpMask(type = 'fragment') {
|
|
|
- this.finalmask.tcp.push(new TcpMask(type));
|
|
|
- }
|
|
|
-
|
|
|
- delTcpMask(index: number) {
|
|
|
- if (this.finalmask.tcp) {
|
|
|
- this.finalmask.tcp.splice(index, 1);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- addUdpMask(type = 'salamander') {
|
|
|
- this.finalmask.udp.push(new UdpMask(type));
|
|
|
- }
|
|
|
-
|
|
|
- delUdpMask(index: number) {
|
|
|
- if (this.finalmask.udp) {
|
|
|
- this.finalmask.udp.splice(index, 1);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- get hasFinalMask() {
|
|
|
- const hasTcp = this.finalmask.tcp && this.finalmask.tcp.length > 0;
|
|
|
- const hasUdp = this.finalmask.udp && this.finalmask.udp.length > 0;
|
|
|
- const hasQuicParams = this.finalmask.quicParams != null;
|
|
|
- return hasTcp || hasUdp || hasQuicParams;
|
|
|
- }
|
|
|
-
|
|
|
- get isTls() {
|
|
|
- return this.security === "tls";
|
|
|
- }
|
|
|
-
|
|
|
- set isTls(isTls) {
|
|
|
- if (isTls) {
|
|
|
- this.security = 'tls';
|
|
|
- } else {
|
|
|
- this.security = 'none';
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- //for Reality
|
|
|
- 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: any = {}) {
|
|
|
- return new StreamSettings(
|
|
|
- json.network,
|
|
|
- json.security,
|
|
|
- json.externalProxy,
|
|
|
- TlsStreamSettings.fromJson(json.tlsSettings),
|
|
|
- RealityStreamSettings.fromJson(json.realitySettings),
|
|
|
- TcpStreamSettings.fromJson(json.tcpSettings),
|
|
|
- KcpStreamSettings.fromJson(json.kcpSettings),
|
|
|
- WsStreamSettings.fromJson(json.wsSettings),
|
|
|
- GrpcStreamSettings.fromJson(json.grpcSettings),
|
|
|
- HTTPUpgradeStreamSettings.fromJson(json.httpupgradeSettings),
|
|
|
- xHTTPStreamSettings.fromJson(json.xhttpSettings),
|
|
|
- HysteriaStreamSettings.fromJson(json.hysteriaSettings),
|
|
|
- FinalMaskStreamSettings.fromJson(json.finalmask),
|
|
|
- SockoptStreamSettings.fromJson(json.sockopt),
|
|
|
- );
|
|
|
- }
|
|
|
-
|
|
|
- toJson() {
|
|
|
- const network = this.network;
|
|
|
- return {
|
|
|
- network: network,
|
|
|
- security: this.security,
|
|
|
- externalProxy: Array.isArray(this.externalProxy) && this.externalProxy.length > 0
|
|
|
- ? this.externalProxy
|
|
|
- : undefined,
|
|
|
- tlsSettings: this.isTls ? this.tls.toJson() : undefined,
|
|
|
- realitySettings: this.isReality ? this.reality.toJson() : undefined,
|
|
|
- tcpSettings: network === 'tcp' ? this.tcp.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,
|
|
|
- hysteriaSettings: network === 'hysteria' ? this.hysteria.toJson() : undefined,
|
|
|
- finalmask: this.hasFinalMask ? this.finalmask.toJson() : undefined,
|
|
|
- sockopt: this.sockopt != undefined ? this.sockopt.toJson() : undefined,
|
|
|
- };
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-export class Sniffing extends XrayCommonClass {
|
|
|
- constructor(
|
|
|
- enabled = false,
|
|
|
- destOverride = ['http', 'tls', 'quic', 'fakedns'],
|
|
|
- metadataOnly = false,
|
|
|
- routeOnly = false,
|
|
|
- ipsExcluded = [],
|
|
|
- domainsExcluded = []) {
|
|
|
- super();
|
|
|
- this.enabled = enabled;
|
|
|
- this.destOverride = Array.isArray(destOverride) && destOverride.length > 0 ? destOverride : ['http', 'tls', 'quic', 'fakedns'];
|
|
|
- this.metadataOnly = metadataOnly;
|
|
|
- this.routeOnly = routeOnly;
|
|
|
- this.ipsExcluded = Array.isArray(ipsExcluded) ? ipsExcluded : [];
|
|
|
- this.domainsExcluded = Array.isArray(domainsExcluded) ? domainsExcluded : [];
|
|
|
- }
|
|
|
-
|
|
|
- static fromJson(json: any = {}) {
|
|
|
- let destOverride = ObjectUtil.clone(json.destOverride);
|
|
|
- if (ObjectUtil.isEmpty(destOverride) || ObjectUtil.isArrEmpty(destOverride) || ObjectUtil.isEmpty(destOverride[0])) {
|
|
|
- destOverride = ['http', 'tls', 'quic', 'fakedns'];
|
|
|
- }
|
|
|
- return new Sniffing(
|
|
|
- !!json.enabled,
|
|
|
- destOverride,
|
|
|
- json.metadataOnly,
|
|
|
- json.routeOnly,
|
|
|
- json.ipsExcluded || [],
|
|
|
- json.domainsExcluded || [],
|
|
|
- );
|
|
|
- }
|
|
|
-
|
|
|
- toJson() {
|
|
|
- if (!this.enabled) {
|
|
|
- return { enabled: false };
|
|
|
- }
|
|
|
- return {
|
|
|
- enabled: true,
|
|
|
- destOverride: this.destOverride,
|
|
|
- metadataOnly: this.metadataOnly || undefined,
|
|
|
- routeOnly: this.routeOnly || undefined,
|
|
|
- ipsExcluded: this.ipsExcluded.length > 0 ? this.ipsExcluded : undefined,
|
|
|
- domainsExcluded: this.domainsExcluded.length > 0 ? this.domainsExcluded : undefined,
|
|
|
- };
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-export class Inbound extends XrayCommonClass {
|
|
|
- static Settings: any;
|
|
|
- static ClientBase: any;
|
|
|
- static VmessSettings: any;
|
|
|
- static VLESSSettings: any;
|
|
|
- static TrojanSettings: any;
|
|
|
- static ShadowsocksSettings: any;
|
|
|
- static HysteriaSettings: any;
|
|
|
- static TunnelSettings: any;
|
|
|
- static MixedSettings: any;
|
|
|
- static HttpSettings: any;
|
|
|
- static WireguardSettings: any;
|
|
|
- static TunSettings: any;
|
|
|
-
|
|
|
- constructor(
|
|
|
- port: any = RandomUtil.randomInteger(10000, 60000),
|
|
|
- listen = '',
|
|
|
- protocol = Protocols.VLESS,
|
|
|
- settings = null,
|
|
|
- streamSettings = new StreamSettings(),
|
|
|
- tag = '',
|
|
|
- sniffing = new Sniffing(),
|
|
|
- clientStats = '',
|
|
|
- ) {
|
|
|
- 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;
|
|
|
- this.clientStats = clientStats;
|
|
|
- }
|
|
|
- getClientStats() {
|
|
|
- return this.clientStats;
|
|
|
- }
|
|
|
-
|
|
|
- // Looks for a "host"-named entry in xhttp.headers and returns its value,
|
|
|
- // or '' if not found. Used as a fallback when xhttp.host is empty so the
|
|
|
- // share URL still carries a usable Host hint.
|
|
|
- static xhttpHostFallback(xhttp: any): string {
|
|
|
- if (!xhttp || !Array.isArray(xhttp.headers)) return '';
|
|
|
- for (const h of xhttp.headers) {
|
|
|
- if (h && typeof h.name === 'string' && h.name.toLowerCase() === 'host') {
|
|
|
- return h.value || '';
|
|
|
- }
|
|
|
- }
|
|
|
- return '';
|
|
|
- }
|
|
|
-
|
|
|
- // Build the JSON blob that goes into the URL's `extra` param (or, for
|
|
|
- // VMess, into the base64-encoded link object). Carries ONLY the
|
|
|
- // bidirectional fields from xray-core's SplitHTTPConfig — i.e. the
|
|
|
- // ones the server enforces and the client must match. Strictly
|
|
|
- // one-sided fields are excluded:
|
|
|
- //
|
|
|
- // - server-only (noSSEHeader, scMaxBufferedPosts,
|
|
|
- // scStreamUpServerSecs, serverMaxHeaderBytes) — client wouldn't
|
|
|
- // read them, so emitting them just bloats the URL.
|
|
|
- // - client-only values are included only when present on the inbound
|
|
|
- // object. Imported/API-created configs can carry them there, and
|
|
|
- // the share link is the only place clients can receive them.
|
|
|
- //
|
|
|
- // Truthy-only guards keep default inbounds emitting the same compact
|
|
|
- // URL they did before this helper grew.
|
|
|
- static buildXhttpExtra(xhttp: any): any {
|
|
|
- if (!xhttp) return null;
|
|
|
- const extra: any = {};
|
|
|
-
|
|
|
- if (typeof xhttp.xPaddingBytes === 'string' && xhttp.xPaddingBytes.length > 0) {
|
|
|
- extra.xPaddingBytes = xhttp.xPaddingBytes;
|
|
|
- }
|
|
|
- if (xhttp.xPaddingObfsMode === true) {
|
|
|
- extra.xPaddingObfsMode = true;
|
|
|
- ["xPaddingKey", "xPaddingHeader", "xPaddingPlacement", "xPaddingMethod"].forEach((k: string) => {
|
|
|
- if (typeof xhttp[k] === 'string' && xhttp[k].length > 0) {
|
|
|
- extra[k] = xhttp[k];
|
|
|
- }
|
|
|
- });
|
|
|
- }
|
|
|
-
|
|
|
- const stringFields = [
|
|
|
- "uplinkHTTPMethod",
|
|
|
- "sessionPlacement", "sessionKey",
|
|
|
- "seqPlacement", "seqKey",
|
|
|
- "uplinkDataPlacement", "uplinkDataKey",
|
|
|
- "scMaxEachPostBytes", "scMinPostsIntervalMs",
|
|
|
- ];
|
|
|
- for (const k of stringFields) {
|
|
|
- const v = xhttp[k];
|
|
|
- if (typeof v === 'string' && v.length > 0) extra[k] = v;
|
|
|
- }
|
|
|
-
|
|
|
- const uplinkChunkSize = xhttp.uplinkChunkSize;
|
|
|
- if ((typeof uplinkChunkSize === 'number' && uplinkChunkSize !== 0) ||
|
|
|
- (typeof uplinkChunkSize === 'string' && uplinkChunkSize.length > 0)) {
|
|
|
- extra.uplinkChunkSize = uplinkChunkSize;
|
|
|
- }
|
|
|
-
|
|
|
- if (xhttp.noGRPCHeader === true) {
|
|
|
- extra.noGRPCHeader = true;
|
|
|
- }
|
|
|
-
|
|
|
- for (const k of ["xmux", "downloadSettings"]) {
|
|
|
- const v = xhttp[k];
|
|
|
- if (v && typeof v === 'object' && Object.keys(v).length > 0) {
|
|
|
- extra[k] = v;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- // Headers — emitted as the {name: value} map upstream's struct
|
|
|
- // expects. The server runtime ignores this field, but the client
|
|
|
- // (consuming the share link) honors it.
|
|
|
- if (Array.isArray(xhttp.headers) && xhttp.headers.length > 0) {
|
|
|
- const headersMap: any = {};
|
|
|
- for (const h of xhttp.headers) {
|
|
|
- if (h && h.name && h.name.toLowerCase() !== 'host') {
|
|
|
- headersMap[h.name] = h.value || '';
|
|
|
- }
|
|
|
- }
|
|
|
- if (Object.keys(headersMap).length > 0) extra.headers = headersMap;
|
|
|
- }
|
|
|
-
|
|
|
- return Object.keys(extra).length > 0 ? extra : null;
|
|
|
- }
|
|
|
-
|
|
|
- // Inject the inbound-side xhttp config into URL query params for
|
|
|
- // vless/trojan/ss links. Sets path/host/mode at top level (xray's
|
|
|
- // Build() always lets these win over `extra`) and packs the
|
|
|
- // bidirectional fields into a JSON `extra` param. Also writes the
|
|
|
- // flat `x_padding_bytes` param sing-box-family clients understand.
|
|
|
- //
|
|
|
- // Without this, the admin's custom xPaddingBytes / sessionKey / etc.
|
|
|
- // never reach the client and handshakes are silently rejected with
|
|
|
- // `invalid padding (...) length: 0`.
|
|
|
- static applyXhttpExtraToParams(xhttp: any, params: any): void {
|
|
|
- if (!xhttp) return;
|
|
|
- params.set("path", xhttp.path);
|
|
|
- const host = xhttp.host?.length > 0 ? xhttp.host : Inbound.xhttpHostFallback(xhttp);
|
|
|
- params.set("host", host);
|
|
|
- params.set("mode", xhttp.mode);
|
|
|
-
|
|
|
- // Flat fallback for sing-box-family clients that don't read `extra`.
|
|
|
- if (typeof xhttp.xPaddingBytes === 'string' && xhttp.xPaddingBytes.length > 0) {
|
|
|
- params.set("x_padding_bytes", xhttp.xPaddingBytes);
|
|
|
- }
|
|
|
-
|
|
|
- const extra = Inbound.buildXhttpExtra(xhttp);
|
|
|
- if (extra) params.set("extra", JSON.stringify(extra));
|
|
|
- }
|
|
|
-
|
|
|
- // VMess variant: VMess links are a base64-encoded JSON object, so we
|
|
|
- // copy the same bidirectional fields directly into the JSON instead
|
|
|
- // of building a query string. (The base VMess link generator already
|
|
|
- // sets net/type/path/host, so we only contribute the SplitHTTPConfig
|
|
|
- // extra side here.)
|
|
|
- static applyXhttpExtraToObj(xhttp: any, obj: any): void {
|
|
|
- if (!xhttp || !obj) return;
|
|
|
- if (typeof xhttp.xPaddingBytes === 'string' && xhttp.xPaddingBytes.length > 0) {
|
|
|
- obj.x_padding_bytes = xhttp.xPaddingBytes;
|
|
|
- }
|
|
|
- const extra = Inbound.buildXhttpExtra(xhttp);
|
|
|
- if (!extra) return;
|
|
|
- for (const [k, v] of Object.entries(extra)) {
|
|
|
- obj[k] = v;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- static externalProxyAlpn(value: any): any {
|
|
|
- if (Array.isArray(value)) return value.filter(Boolean).join(',');
|
|
|
- return typeof value === 'string' ? value : '';
|
|
|
- }
|
|
|
-
|
|
|
- static applyExternalProxyTLSParams(externalProxy: any, params: any, security: any): void {
|
|
|
- if (!externalProxy || security !== 'tls') return;
|
|
|
- const sni = externalProxy.sni?.length > 0 ? externalProxy.sni : externalProxy.dest;
|
|
|
- if (sni?.length > 0) params.set("sni", sni);
|
|
|
- if (externalProxy.fingerprint?.length > 0) params.set("fp", externalProxy.fingerprint);
|
|
|
- const alpn = Inbound.externalProxyAlpn(externalProxy.alpn);
|
|
|
- if (alpn.length > 0) params.set("alpn", alpn);
|
|
|
- }
|
|
|
-
|
|
|
- static applyExternalProxyTLSObj(externalProxy: any, obj: any, security: any): void {
|
|
|
- if (!externalProxy || !obj || security !== 'tls') return;
|
|
|
- const sni = externalProxy.sni?.length > 0 ? externalProxy.sni : externalProxy.dest;
|
|
|
- if (sni?.length > 0) obj.sni = sni;
|
|
|
- if (externalProxy.fingerprint?.length > 0) obj.fp = externalProxy.fingerprint;
|
|
|
- const alpn = Inbound.externalProxyAlpn(externalProxy.alpn);
|
|
|
- if (alpn.length > 0) obj.alpn = alpn;
|
|
|
- }
|
|
|
-
|
|
|
- static hasShareableFinalMaskValue(value: any): boolean {
|
|
|
- if (value == null) {
|
|
|
- return false;
|
|
|
- }
|
|
|
- if (Array.isArray(value)) {
|
|
|
- return value.some((item: any) => Inbound.hasShareableFinalMaskValue(item));
|
|
|
- }
|
|
|
- if (typeof value === 'object') {
|
|
|
- return Object.values(value).some((item: any) => Inbound.hasShareableFinalMaskValue(item));
|
|
|
- }
|
|
|
- if (typeof value === 'string') {
|
|
|
- return value.length > 0;
|
|
|
- }
|
|
|
- return true;
|
|
|
- }
|
|
|
-
|
|
|
- static serializeFinalMask(finalmask: any): any {
|
|
|
- if (!finalmask) {
|
|
|
- return '';
|
|
|
- }
|
|
|
- const value = typeof finalmask.toJson === 'function' ? finalmask.toJson() : finalmask;
|
|
|
- return Inbound.hasShareableFinalMaskValue(value) ? JSON.stringify(value) : '';
|
|
|
- }
|
|
|
-
|
|
|
- // Export finalmask with the same compact JSON payload shape that
|
|
|
- // v2rayN-compatible share links use: fm=<json>.
|
|
|
- static applyFinalMaskToParams(finalmask: any, params: any): void {
|
|
|
- if (!params) return;
|
|
|
- const payload = Inbound.serializeFinalMask(finalmask);
|
|
|
- if (payload.length > 0) {
|
|
|
- params.set("fm", payload);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- // VMess links are a base64 JSON object, so keep the same fm payload
|
|
|
- // under a flat property instead of a URL query string.
|
|
|
- static applyFinalMaskToObj(finalmask: any, obj: any): void {
|
|
|
- if (!obj) return;
|
|
|
- const payload = Inbound.serializeFinalMask(finalmask);
|
|
|
- if (payload.length > 0) {
|
|
|
- obj.fm = payload;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- get clients() {
|
|
|
- switch (this.protocol) {
|
|
|
- case Protocols.VMESS: return this.settings.vmesses;
|
|
|
- case Protocols.VLESS: return this.settings.vlesses;
|
|
|
- case Protocols.TROJAN: return this.settings.trojans;
|
|
|
- case Protocols.SHADOWSOCKS: return this.isSSMultiUser ? this.settings.shadowsockses : null;
|
|
|
- case Protocols.HYSTERIA: return this.settings.hysterias;
|
|
|
- default: return null;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- get protocol() {
|
|
|
- return this._protocol;
|
|
|
- }
|
|
|
-
|
|
|
- set protocol(protocol) {
|
|
|
- this._protocol = protocol;
|
|
|
- this.settings = Inbound.Settings.getSettings(protocol);
|
|
|
- this.stream = new StreamSettings();
|
|
|
- if (protocol === Protocols.TROJAN) {
|
|
|
- this.tls = false;
|
|
|
- }
|
|
|
- if (protocol === Protocols.HYSTERIA) {
|
|
|
- this.stream.network = 'hysteria';
|
|
|
- this.stream.security = 'tls';
|
|
|
- // Hysteria runs over QUIC and must not inherit TCP TLS ALPN defaults.
|
|
|
- this.stream.tls.alpn = [ALPN_OPTION.H3];
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- get network() {
|
|
|
- return this.stream.network;
|
|
|
- }
|
|
|
-
|
|
|
- set network(network) {
|
|
|
- this.stream.network = network;
|
|
|
- }
|
|
|
-
|
|
|
- get isTcp() {
|
|
|
- return this.network === "tcp";
|
|
|
- }
|
|
|
-
|
|
|
- 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";
|
|
|
- }
|
|
|
-
|
|
|
- // Shadowsocks
|
|
|
- get method() {
|
|
|
- switch (this.protocol) {
|
|
|
- case Protocols.SHADOWSOCKS:
|
|
|
- return this.settings.method;
|
|
|
- default:
|
|
|
- return "";
|
|
|
- }
|
|
|
- }
|
|
|
- get isSSMultiUser() {
|
|
|
- return this.method != SSMethods.BLAKE3_CHACHA20_POLY1305;
|
|
|
- }
|
|
|
- get isSS2022() {
|
|
|
- return this.method.substring(0, 4) === "2022";
|
|
|
- }
|
|
|
-
|
|
|
- get serverName() {
|
|
|
- if (this.stream.isTls) return this.stream.tls.sni;
|
|
|
- if (this.stream.isReality) return this.stream.reality.serverNames;
|
|
|
- return "";
|
|
|
- }
|
|
|
-
|
|
|
- getHeader(obj: any, name: any) {
|
|
|
- for (const header of obj.headers) {
|
|
|
- if (header.name.toLowerCase() === name.toLowerCase()) {
|
|
|
- return header.value;
|
|
|
- }
|
|
|
- }
|
|
|
- return "";
|
|
|
- }
|
|
|
-
|
|
|
- get host() {
|
|
|
- if (this.isTcp) {
|
|
|
- return this.getHeader(this.stream.tcp.request, 'host');
|
|
|
- } else if (this.isWs) {
|
|
|
- return this.stream.ws.host?.length > 0 ? this.stream.ws.host : this.getHeader(this.stream.ws, 'host');
|
|
|
- } else if (this.isHttpupgrade) {
|
|
|
- return this.stream.httpupgrade.host?.length > 0 ? this.stream.httpupgrade.host : this.getHeader(this.stream.httpupgrade, 'host');
|
|
|
- } else if (this.isXHTTP) {
|
|
|
- return this.stream.xhttp.host?.length > 0 ? this.stream.xhttp.host : this.getHeader(this.stream.xhttp, 'host');
|
|
|
- }
|
|
|
- return null;
|
|
|
- }
|
|
|
-
|
|
|
- get path() {
|
|
|
- if (this.isTcp) {
|
|
|
- return this.stream.tcp.request.path[0];
|
|
|
- } else if (this.isWs) {
|
|
|
- return this.stream.ws.path;
|
|
|
- } else if (this.isHttpupgrade) {
|
|
|
- return this.stream.httpupgrade.path;
|
|
|
- } else if (this.isXHTTP) {
|
|
|
- return this.stream.xhttp.path;
|
|
|
- }
|
|
|
- return null;
|
|
|
- }
|
|
|
-
|
|
|
- get serviceName() {
|
|
|
- return this.stream.grpc.serviceName;
|
|
|
- }
|
|
|
-
|
|
|
- isExpiry(index: number) {
|
|
|
- const exp = this.clients[index].expiryTime;
|
|
|
- return exp > 0 ? exp < new Date().getTime() : false;
|
|
|
- }
|
|
|
-
|
|
|
- canEnableTls() {
|
|
|
- if (this.protocol === Protocols.HYSTERIA) return true;
|
|
|
- if (![Protocols.VMESS, Protocols.VLESS, Protocols.TROJAN, Protocols.SHADOWSOCKS].includes(this.protocol)) return false;
|
|
|
- return ["tcp", "ws", "http", "grpc", "httpupgrade", "xhttp"].includes(this.network);
|
|
|
- }
|
|
|
-
|
|
|
- //this is used for xtls-rprx-vision
|
|
|
- canEnableTlsFlow() {
|
|
|
- if (((this.stream.security === 'tls') || (this.stream.security === 'reality')) && (this.network === "tcp")) {
|
|
|
- return this.protocol === Protocols.VLESS;
|
|
|
- }
|
|
|
- return false;
|
|
|
- }
|
|
|
-
|
|
|
- // Vision seed applies only when the XTLS Vision (TCP/TLS) flow is selected.
|
|
|
- // Excludes the UDP variant per spec.
|
|
|
- canEnableVisionSeed() {
|
|
|
- if (!this.canEnableTlsFlow()) return false;
|
|
|
- const clients = this.settings?.vlesses;
|
|
|
- if (!Array.isArray(clients)) return false;
|
|
|
- return clients.some((c: any) => c?.flow === TLS_FLOW_CONTROL.VISION);
|
|
|
- }
|
|
|
-
|
|
|
- canEnableReality() {
|
|
|
- if (![Protocols.VLESS, Protocols.TROJAN].includes(this.protocol)) return false;
|
|
|
- return ["tcp", "http", "grpc", "xhttp"].includes(this.network);
|
|
|
- }
|
|
|
-
|
|
|
- canEnableStream() {
|
|
|
- return [Protocols.VMESS, Protocols.VLESS, Protocols.TROJAN, Protocols.SHADOWSOCKS, Protocols.HYSTERIA].includes(this.protocol);
|
|
|
- }
|
|
|
-
|
|
|
- reset() {
|
|
|
- this.port = RandomUtil.randomInteger(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: any = '', port: any = this.port, forceTls?: any, remark: any = '', clientId?: any, security?: any, externalProxy: any = null) {
|
|
|
- if (this.protocol !== Protocols.VMESS) {
|
|
|
- return '';
|
|
|
- }
|
|
|
- const tls = forceTls == 'same' ? this.stream.security : forceTls;
|
|
|
- const obj: any = {
|
|
|
- v: '2',
|
|
|
- ps: remark,
|
|
|
- add: address,
|
|
|
- port: port,
|
|
|
- id: clientId,
|
|
|
- scy: security,
|
|
|
- net: this.stream.network,
|
|
|
- tls: tls,
|
|
|
- };
|
|
|
- const network = this.stream.network;
|
|
|
- if (network === 'tcp') {
|
|
|
- const tcp = this.stream.tcp;
|
|
|
- obj.type = tcp.type;
|
|
|
- if (tcp.type === 'http') {
|
|
|
- const request = tcp.request;
|
|
|
- obj.path = request.path.join(',');
|
|
|
- const host = this.getHeader(request, 'host');
|
|
|
- if (host) obj.host = host;
|
|
|
- }
|
|
|
- } else if (network === 'kcp') {
|
|
|
- const kcp = this.stream.kcp;
|
|
|
- obj.mtu = kcp.mtu;
|
|
|
- obj.tti = kcp.tti;
|
|
|
- } else if (network === 'ws') {
|
|
|
- const ws = this.stream.ws;
|
|
|
- obj.path = ws.path;
|
|
|
- obj.host = ws.host?.length > 0 ? ws.host : this.getHeader(ws, 'host');
|
|
|
- } else if (network === 'grpc') {
|
|
|
- obj.path = this.stream.grpc.serviceName;
|
|
|
- obj.authority = this.stream.grpc.authority;
|
|
|
- if (this.stream.grpc.multiMode) {
|
|
|
- obj.type = 'multi'
|
|
|
- }
|
|
|
- } else if (network === 'httpupgrade') {
|
|
|
- const httpupgrade = this.stream.httpupgrade;
|
|
|
- obj.path = httpupgrade.path;
|
|
|
- obj.host = httpupgrade.host?.length > 0 ? httpupgrade.host : this.getHeader(httpupgrade, 'host');
|
|
|
- } else if (network === 'xhttp') {
|
|
|
- const xhttp = this.stream.xhttp;
|
|
|
- obj.path = xhttp.path;
|
|
|
- obj.host = xhttp.host?.length > 0 ? xhttp.host : this.getHeader(xhttp, 'host');
|
|
|
- obj.type = xhttp.mode;
|
|
|
- Inbound.applyXhttpExtraToObj(xhttp, obj);
|
|
|
- }
|
|
|
-
|
|
|
- Inbound.applyFinalMaskToObj(this.stream.finalmask, obj);
|
|
|
-
|
|
|
- if (tls === 'tls') {
|
|
|
- if (!ObjectUtil.isEmpty(this.stream.tls.sni)) {
|
|
|
- obj.sni = this.stream.tls.sni;
|
|
|
- }
|
|
|
- if (!ObjectUtil.isEmpty(this.stream.tls.settings.fingerprint)) {
|
|
|
- obj.fp = this.stream.tls.settings.fingerprint;
|
|
|
- }
|
|
|
- if (this.stream.tls.alpn.length > 0) {
|
|
|
- obj.alpn = this.stream.tls.alpn.join(',');
|
|
|
- }
|
|
|
- }
|
|
|
- Inbound.applyExternalProxyTLSObj(externalProxy, obj, tls);
|
|
|
-
|
|
|
- return 'vmess://' + Base64.encode(JSON.stringify(obj, null, 2));
|
|
|
- }
|
|
|
-
|
|
|
- genVLESSLink(address: any = '', port: any = this.port, forceTls?: any, remark: any = '', clientId?: any, flow?: any, externalProxy: any = null) {
|
|
|
- const uuid = clientId;
|
|
|
- const type = this.stream.network;
|
|
|
- const security = forceTls == 'same' ? this.stream.security : forceTls;
|
|
|
- const params = new Map();
|
|
|
- params.set("type", this.stream.network);
|
|
|
- params.set("encryption", this.settings.encryption);
|
|
|
- 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: any) => 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("mtu", kcp.mtu);
|
|
|
- params.set("tti", kcp.tti);
|
|
|
- break;
|
|
|
- }
|
|
|
- case "ws": {
|
|
|
- const ws = this.stream.ws;
|
|
|
- params.set("path", ws.path);
|
|
|
- params.set("host", ws.host?.length > 0 ? ws.host : this.getHeader(ws, '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?.length > 0 ? httpupgrade.host : this.getHeader(httpupgrade, 'host'));
|
|
|
- break;
|
|
|
- }
|
|
|
- case "xhttp":
|
|
|
- Inbound.applyXhttpExtraToParams(this.stream.xhttp, params);
|
|
|
- break;
|
|
|
- }
|
|
|
-
|
|
|
- Inbound.applyFinalMaskToParams(this.stream.finalmask, params);
|
|
|
-
|
|
|
- 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 (!ObjectUtil.isEmpty(this.stream.tls.sni)) {
|
|
|
- params.set("sni", this.stream.tls.sni);
|
|
|
- }
|
|
|
- if (this.stream.tls.settings.echConfigList?.length > 0) {
|
|
|
- params.set("ech", this.stream.tls.settings.echConfigList);
|
|
|
- }
|
|
|
- if (type == "tcp" && !ObjectUtil.isEmpty(flow)) {
|
|
|
- params.set("flow", flow);
|
|
|
- }
|
|
|
- }
|
|
|
- Inbound.applyExternalProxyTLSParams(externalProxy, params, security);
|
|
|
- }
|
|
|
-
|
|
|
- else if (security === 'reality') {
|
|
|
- params.set("security", "reality");
|
|
|
- params.set("pbk", this.stream.reality.settings.publicKey);
|
|
|
- params.set("fp", this.stream.reality.settings.fingerprint);
|
|
|
- if (!ObjectUtil.isArrEmpty(this.stream.reality.serverNames)) {
|
|
|
- params.set("sni", this.stream.reality.serverNames.split(",")[0]);
|
|
|
- }
|
|
|
- if (this.stream.reality.shortIds.length > 0) {
|
|
|
- params.set("sid", this.stream.reality.shortIds.split(",")[0]);
|
|
|
- }
|
|
|
- if (!ObjectUtil.isEmpty(this.stream.reality.settings.spiderX)) {
|
|
|
- params.set("spx", this.stream.reality.settings.spiderX);
|
|
|
- }
|
|
|
- if (!ObjectUtil.isEmpty(this.stream.reality.settings.mldsa65Verify)) {
|
|
|
- params.set("pqv", this.stream.reality.settings.mldsa65Verify);
|
|
|
- }
|
|
|
- if (type == 'tcp' && !ObjectUtil.isEmpty(flow)) {
|
|
|
- params.set("flow", flow);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- else {
|
|
|
- params.set("security", "none");
|
|
|
- }
|
|
|
-
|
|
|
- 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: any = '', port: any = this.port, forceTls?: any, remark: any = '', clientPassword?: any, externalProxy: any = null) {
|
|
|
- const 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: any) => 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("mtu", kcp.mtu);
|
|
|
- params.set("tti", kcp.tti);
|
|
|
- break;
|
|
|
- }
|
|
|
- case "ws": {
|
|
|
- const ws = this.stream.ws;
|
|
|
- params.set("path", ws.path);
|
|
|
- params.set("host", ws.host?.length > 0 ? ws.host : this.getHeader(ws, '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?.length > 0 ? httpupgrade.host : this.getHeader(httpupgrade, 'host'));
|
|
|
- break;
|
|
|
- }
|
|
|
- case "xhttp":
|
|
|
- Inbound.applyXhttpExtraToParams(this.stream.xhttp, params);
|
|
|
- break;
|
|
|
- }
|
|
|
-
|
|
|
- Inbound.applyFinalMaskToParams(this.stream.finalmask, params);
|
|
|
-
|
|
|
- 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.echConfigList?.length > 0) {
|
|
|
- params.set("ech", this.stream.tls.settings.echConfigList);
|
|
|
- }
|
|
|
- if (!ObjectUtil.isEmpty(this.stream.tls.sni)) {
|
|
|
- params.set("sni", this.stream.tls.sni);
|
|
|
- }
|
|
|
- }
|
|
|
- Inbound.applyExternalProxyTLSParams(externalProxy, params, security);
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- const password: string[] = [];
|
|
|
- if (this.isSS2022) password.push(settings.password);
|
|
|
- if (this.isSSMultiUser) password.push(clientPassword);
|
|
|
-
|
|
|
- const link = `ss://${Base64.encode(`${settings.method}:${password.join(':')}`, true)}@${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();
|
|
|
- }
|
|
|
-
|
|
|
- genTrojanLink(address: any = '', port: any = this.port, forceTls?: any, remark: any = '', clientPassword?: any, externalProxy: any = null) {
|
|
|
- const security = forceTls == 'same' ? this.stream.security : forceTls;
|
|
|
- const type = this.stream.network;
|
|
|
- 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: any) => 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("mtu", kcp.mtu);
|
|
|
- params.set("tti", kcp.tti);
|
|
|
- break;
|
|
|
- }
|
|
|
- case "ws": {
|
|
|
- const ws = this.stream.ws;
|
|
|
- params.set("path", ws.path);
|
|
|
- params.set("host", ws.host?.length > 0 ? ws.host : this.getHeader(ws, '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?.length > 0 ? httpupgrade.host : this.getHeader(httpupgrade, 'host'));
|
|
|
- break;
|
|
|
- }
|
|
|
- case "xhttp":
|
|
|
- Inbound.applyXhttpExtraToParams(this.stream.xhttp, params);
|
|
|
- break;
|
|
|
- }
|
|
|
-
|
|
|
- Inbound.applyFinalMaskToParams(this.stream.finalmask, params);
|
|
|
-
|
|
|
- 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.echConfigList?.length > 0) {
|
|
|
- params.set("ech", this.stream.tls.settings.echConfigList);
|
|
|
- }
|
|
|
- if (!ObjectUtil.isEmpty(this.stream.tls.sni)) {
|
|
|
- params.set("sni", this.stream.tls.sni);
|
|
|
- }
|
|
|
- }
|
|
|
- Inbound.applyExternalProxyTLSParams(externalProxy, params, security);
|
|
|
- }
|
|
|
-
|
|
|
- else if (security === 'reality') {
|
|
|
- params.set("security", "reality");
|
|
|
- params.set("pbk", this.stream.reality.settings.publicKey);
|
|
|
- params.set("fp", this.stream.reality.settings.fingerprint);
|
|
|
- if (!ObjectUtil.isArrEmpty(this.stream.reality.serverNames)) {
|
|
|
- params.set("sni", this.stream.reality.serverNames.split(",")[0]);
|
|
|
- }
|
|
|
- if (this.stream.reality.shortIds.length > 0) {
|
|
|
- params.set("sid", this.stream.reality.shortIds.split(",")[0]);
|
|
|
- }
|
|
|
- if (!ObjectUtil.isEmpty(this.stream.reality.settings.spiderX)) {
|
|
|
- params.set("spx", this.stream.reality.settings.spiderX);
|
|
|
- }
|
|
|
- if (!ObjectUtil.isEmpty(this.stream.reality.settings.mldsa65Verify)) {
|
|
|
- params.set("pqv", this.stream.reality.settings.mldsa65Verify);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- else {
|
|
|
- params.set("security", "none");
|
|
|
- }
|
|
|
-
|
|
|
- const link = `trojan://${clientPassword}@${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();
|
|
|
- }
|
|
|
-
|
|
|
- genHysteriaLink(address: any = '', port: any = this.port, remark: any = '', clientAuth?: any) {
|
|
|
- const protocol = this.settings.version == 2 ? "hysteria2" : "hysteria";
|
|
|
- const link = `${protocol}://${clientAuth}@${address}:${port}`;
|
|
|
-
|
|
|
- const params = new Map();
|
|
|
- params.set("security", "tls");
|
|
|
- if (this.stream.tls.settings.fingerprint?.length > 0) params.set("fp", this.stream.tls.settings.fingerprint);
|
|
|
- if (this.stream.tls.alpn?.length > 0) params.set("alpn", this.stream.tls.alpn);
|
|
|
- if (this.stream.tls.settings.allowInsecure) params.set("insecure", "1");
|
|
|
- if (this.stream.tls.settings.echConfigList?.length > 0) params.set("ech", this.stream.tls.settings.echConfigList);
|
|
|
- if (this.stream.tls.sni?.length > 0) params.set("sni", this.stream.tls.sni);
|
|
|
-
|
|
|
- const udpMasks = this.stream?.finalmask?.udp;
|
|
|
- if (Array.isArray(udpMasks)) {
|
|
|
- const salamanderMask = udpMasks.find((mask: any) => mask?.type === 'salamander');
|
|
|
- const obfsPassword = salamanderMask?.settings?.password;
|
|
|
- if (typeof obfsPassword === 'string' && obfsPassword.length > 0) {
|
|
|
- params.set("obfs", "salamander");
|
|
|
- params.set("obfs-password", obfsPassword);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- Inbound.applyFinalMaskToParams(this.stream.finalmask, params);
|
|
|
-
|
|
|
- const url = new URL(link);
|
|
|
- for (const [key, value] of params) {
|
|
|
- url.searchParams.set(key, value);
|
|
|
- }
|
|
|
- url.hash = encodeURIComponent(remark);
|
|
|
- return url.toString();
|
|
|
- }
|
|
|
-
|
|
|
- getWireguardTxt(address: any, port: any, remark: any, peerId: any) {
|
|
|
- let txt = `[Interface]\n`
|
|
|
- txt += `PrivateKey = ${this.settings.peers[peerId].privateKey}\n`
|
|
|
- txt += `Address = ${this.settings.peers[peerId].allowedIPs[0]}\n`
|
|
|
- txt += `DNS = 1.1.1.1, 1.0.0.1\n`
|
|
|
- if (this.settings.mtu) {
|
|
|
- txt += `MTU = ${this.settings.mtu}\n`
|
|
|
- }
|
|
|
- txt += `\n# ${remark}\n`
|
|
|
- txt += `[Peer]\n`
|
|
|
- txt += `PublicKey = ${this.settings.pubKey}\n`
|
|
|
- txt += `AllowedIPs = 0.0.0.0/0, ::/0\n`
|
|
|
- txt += `Endpoint = ${address}:${port}`
|
|
|
- if (this.settings.peers[peerId].psk) {
|
|
|
- txt += `\nPresharedKey = ${this.settings.peers[peerId].psk}`
|
|
|
- }
|
|
|
- if (this.settings.peers[peerId].keepAlive) {
|
|
|
- txt += `\nPersistentKeepalive = ${this.settings.peers[peerId].keepAlive}\n`
|
|
|
- }
|
|
|
- return txt;
|
|
|
- }
|
|
|
-
|
|
|
- getWireguardLink(address: any, port: any, remark: any, peerId: any) {
|
|
|
- const peer = this.settings?.peers?.[peerId];
|
|
|
- if (!peer) return '';
|
|
|
-
|
|
|
- const link = `wireguard://${address}:${port}`;
|
|
|
- const url = new URL(link);
|
|
|
- url.username = peer.privateKey || '';
|
|
|
-
|
|
|
- if (this.settings?.pubKey) {
|
|
|
- url.searchParams.set("publickey", this.settings.pubKey);
|
|
|
- }
|
|
|
- if (Array.isArray(peer.allowedIPs) && peer.allowedIPs.length > 0 && peer.allowedIPs[0]) {
|
|
|
- url.searchParams.set("address", peer.allowedIPs[0]);
|
|
|
- }
|
|
|
- if (this.settings?.mtu) {
|
|
|
- url.searchParams.set("mtu", this.settings.mtu);
|
|
|
- }
|
|
|
-
|
|
|
- url.hash = encodeURIComponent(remark);
|
|
|
- return url.toString();
|
|
|
- }
|
|
|
-
|
|
|
- // resolveAddr picks the host that goes into share/sub links. Order:
|
|
|
- // 1. hostOverride (caller supplies node address for node-managed inbounds)
|
|
|
- // 2. inbound's bind listen (when explicit, not 0.0.0.0)
|
|
|
- // 3. browser's location.hostname (single-panel default)
|
|
|
- // Centralised so genAllLinks/genInboundLinks/genWireguard*
|
|
|
- // all share the same chain — pre-Phase 3 we had four duplicated lines.
|
|
|
- _resolveAddr(hostOverride = '') {
|
|
|
- if (hostOverride) return hostOverride;
|
|
|
- if (!ObjectUtil.isEmpty(this.listen) && this.listen !== "0.0.0.0") return this.listen;
|
|
|
- return location.hostname;
|
|
|
- }
|
|
|
-
|
|
|
- genWireguardLinks(remark = '', remarkModel = '-ieo', hostOverride = '') {
|
|
|
- const addr = this._resolveAddr(hostOverride);
|
|
|
- const separationChar = remarkModel.charAt(0);
|
|
|
- const links: any[] = [];
|
|
|
- this.settings.peers.forEach((_p: any, index: number) => {
|
|
|
- links.push(this.getWireguardLink(addr, this.port, remark + separationChar + (index + 1), index));
|
|
|
- });
|
|
|
- return links.join('\r\n');
|
|
|
- }
|
|
|
-
|
|
|
- genWireguardConfigs(remark = '', remarkModel = '-ieo', hostOverride = '') {
|
|
|
- const addr = this._resolveAddr(hostOverride);
|
|
|
- const separationChar = remarkModel.charAt(0);
|
|
|
- const links: any[] = [];
|
|
|
- this.settings.peers.forEach((_p: any, index: number) => {
|
|
|
- links.push(this.getWireguardTxt(addr, this.port, remark + separationChar + (index + 1), index));
|
|
|
- });
|
|
|
- return links.join('\r\n');
|
|
|
- }
|
|
|
-
|
|
|
- genLink(address: any = '', port: any = this.port, forceTls: any = 'same', remark: any = '', client?: any, externalProxy: any = null) {
|
|
|
- switch (this.protocol) {
|
|
|
- case Protocols.VMESS:
|
|
|
- return this.genVmessLink(address, port, forceTls, remark, client.id, client.security, externalProxy);
|
|
|
- case Protocols.VLESS:
|
|
|
- return this.genVLESSLink(address, port, forceTls, remark, client.id, client.flow, externalProxy);
|
|
|
- case Protocols.SHADOWSOCKS:
|
|
|
- return this.genSSLink(address, port, forceTls, remark, this.isSSMultiUser ? client.password : '', externalProxy);
|
|
|
- case Protocols.TROJAN:
|
|
|
- return this.genTrojanLink(address, port, forceTls, remark, client.password, externalProxy);
|
|
|
- case Protocols.HYSTERIA:
|
|
|
- return this.genHysteriaLink(address, port, remark, client.auth.length > 0 ? client.auth : this.stream.hysteria.auth);
|
|
|
- default: return '';
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- genAllLinks(remark: any = '', remarkModel: any = '-ieo', client?: any, hostOverride: any = '') {
|
|
|
- const result: any[] = [];
|
|
|
- const email = client ? client.email : '';
|
|
|
- const addr = this._resolveAddr(hostOverride);
|
|
|
- const port = this.port;
|
|
|
- const separationChar = remarkModel.charAt(0);
|
|
|
- const orderChars = remarkModel.slice(1);
|
|
|
- const orders: any = {
|
|
|
- 'i': remark,
|
|
|
- 'e': email,
|
|
|
- 'o': '',
|
|
|
- };
|
|
|
- if (ObjectUtil.isArrEmpty(this.stream.externalProxy)) {
|
|
|
- const r = orderChars.split('').map((char: string) => orders[char]).filter((x: any) => x.length > 0).join(separationChar);
|
|
|
- result.push({
|
|
|
- remark: r,
|
|
|
- link: this.genLink(addr, port, 'same', r, client)
|
|
|
- });
|
|
|
- } else {
|
|
|
- this.stream.externalProxy.forEach((ep: any) => {
|
|
|
- orders['o'] = ep.remark;
|
|
|
- const r = orderChars.split('').map((char: string) => orders[char]).filter((x: any) => x.length > 0).join(separationChar);
|
|
|
- result.push({
|
|
|
- remark: r,
|
|
|
- link: this.genLink(ep.dest, ep.port, ep.forceTls, r, client, ep)
|
|
|
- });
|
|
|
- });
|
|
|
- }
|
|
|
- return result;
|
|
|
- }
|
|
|
-
|
|
|
- genInboundLinks(remark = '', remarkModel = '-ieo', hostOverride = '') {
|
|
|
- const addr = this._resolveAddr(hostOverride);
|
|
|
- if (this.clients) {
|
|
|
- const links: any[] = [];
|
|
|
- this.clients.forEach((client: any) => {
|
|
|
- this.genAllLinks(remark, remarkModel, client, hostOverride).forEach((l: any) => {
|
|
|
- links.push(l.link);
|
|
|
- })
|
|
|
- });
|
|
|
- return links.join('\r\n');
|
|
|
- } else {
|
|
|
- if (this.protocol == Protocols.SHADOWSOCKS && !this.isSSMultiUser) return this.genSSLink(addr, this.port, 'same', remark);
|
|
|
- if (this.protocol == Protocols.WIREGUARD) {
|
|
|
- return this.genWireguardConfigs(remark, remarkModel, hostOverride);
|
|
|
- }
|
|
|
- return '';
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- static fromJson(json: any = {}) {
|
|
|
- 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),
|
|
|
- json.clientStats
|
|
|
- )
|
|
|
- }
|
|
|
-
|
|
|
- toJson() {
|
|
|
- // Only these protocols use streamSettings
|
|
|
- const streamProtocols = [Protocols.VLESS, Protocols.VMESS, Protocols.TROJAN, Protocols.SHADOWSOCKS, Protocols.HYSTERIA];
|
|
|
-
|
|
|
- const result: any = {
|
|
|
- port: this.port,
|
|
|
- listen: this.listen,
|
|
|
- protocol: this.protocol,
|
|
|
- settings: this.settings instanceof XrayCommonClass ? this.settings.toJson() : this.settings,
|
|
|
- tag: this.tag,
|
|
|
- sniffing: this.sniffing.toJson(),
|
|
|
- clientStats: this.clientStats
|
|
|
- };
|
|
|
-
|
|
|
- // Only add streamSettings if protocol supports it
|
|
|
- if (streamProtocols.includes(this.protocol)) {
|
|
|
- result.streamSettings = this.stream.toJson();
|
|
|
- }
|
|
|
-
|
|
|
- return result;
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-Inbound.Settings = class extends XrayCommonClass {
|
|
|
- constructor(protocol: any) {
|
|
|
- super();
|
|
|
- this.protocol = protocol;
|
|
|
- }
|
|
|
-
|
|
|
- static getSettings(protocol: any): any {
|
|
|
- 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.TUNNEL: return new Inbound.TunnelSettings(protocol);
|
|
|
- case Protocols.MIXED: return new Inbound.MixedSettings(protocol);
|
|
|
- case Protocols.HTTP: return new Inbound.HttpSettings(protocol);
|
|
|
- case Protocols.WIREGUARD: return new Inbound.WireguardSettings(protocol);
|
|
|
- case Protocols.TUN: return new Inbound.TunSettings(protocol);
|
|
|
- case Protocols.HYSTERIA: return new Inbound.HysteriaSettings(protocol);
|
|
|
- default: return null;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- static fromJson(protocol: any, json: any): any {
|
|
|
- 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.TUNNEL: return Inbound.TunnelSettings.fromJson(json);
|
|
|
- case Protocols.MIXED: return Inbound.MixedSettings.fromJson(json);
|
|
|
- case Protocols.HTTP: return Inbound.HttpSettings.fromJson(json);
|
|
|
- case Protocols.WIREGUARD: return Inbound.WireguardSettings.fromJson(json);
|
|
|
- case Protocols.TUN: return Inbound.TunSettings.fromJson(json);
|
|
|
- case Protocols.HYSTERIA: return Inbound.HysteriaSettings.fromJson(json);
|
|
|
- default: return null;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- toJson() {
|
|
|
- return {};
|
|
|
- }
|
|
|
-};
|
|
|
-
|
|
|
-/** Shared user-quota fields and UI helpers for multi-user protocol clients. */
|
|
|
-Inbound.ClientBase = class extends XrayCommonClass {
|
|
|
- constructor(
|
|
|
- email: any = RandomUtil.randomLowerAndNum(8),
|
|
|
- limitIp: any = 0,
|
|
|
- totalGB: any = 0,
|
|
|
- expiryTime: any = 0,
|
|
|
- enable: any = true,
|
|
|
- tgId: any = '',
|
|
|
- subId: any = RandomUtil.randomLowerAndNum(16),
|
|
|
- comment: any = '',
|
|
|
- reset: any = 0,
|
|
|
- created_at: any = undefined,
|
|
|
- updated_at: any = undefined,
|
|
|
- ) {
|
|
|
- super();
|
|
|
- this.email = email;
|
|
|
- this.limitIp = limitIp;
|
|
|
- this.totalGB = totalGB;
|
|
|
- this.expiryTime = expiryTime;
|
|
|
- this.enable = enable;
|
|
|
- this.tgId = tgId;
|
|
|
- this.subId = subId;
|
|
|
- this.comment = comment;
|
|
|
- this.reset = reset;
|
|
|
- this.created_at = created_at;
|
|
|
- this.updated_at = updated_at;
|
|
|
- }
|
|
|
-
|
|
|
- static commonArgsFromJson(json: any = {}) {
|
|
|
- return [
|
|
|
- json.email,
|
|
|
- json.limitIp,
|
|
|
- json.totalGB,
|
|
|
- json.expiryTime,
|
|
|
- json.enable,
|
|
|
- json.tgId,
|
|
|
- json.subId,
|
|
|
- json.comment,
|
|
|
- json.reset,
|
|
|
- json.created_at,
|
|
|
- json.updated_at,
|
|
|
- ];
|
|
|
- }
|
|
|
-
|
|
|
- _clientBaseToJson() {
|
|
|
- return {
|
|
|
- email: this.email,
|
|
|
- limitIp: this.limitIp,
|
|
|
- totalGB: this.totalGB,
|
|
|
- expiryTime: this.expiryTime,
|
|
|
- enable: this.enable,
|
|
|
- tgId: this.tgId,
|
|
|
- subId: this.subId,
|
|
|
- comment: this.comment,
|
|
|
- reset: this.reset,
|
|
|
- created_at: this.created_at,
|
|
|
- updated_at: this.updated_at,
|
|
|
- };
|
|
|
- }
|
|
|
-
|
|
|
- get _expiryTime() {
|
|
|
- if (this.expiryTime === 0 || this.expiryTime === '') {
|
|
|
- return null;
|
|
|
- }
|
|
|
- if (this.expiryTime < 0) {
|
|
|
- return this.expiryTime / -86400000;
|
|
|
- }
|
|
|
- return dayjs(this.expiryTime);
|
|
|
- }
|
|
|
-
|
|
|
- set _expiryTime(t: any) {
|
|
|
- if (t == null || t === '') {
|
|
|
- this.expiryTime = 0;
|
|
|
- } else {
|
|
|
- this.expiryTime = t.valueOf();
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- get _totalGB() {
|
|
|
- return NumberFormatter.toFixed(this.totalGB / SizeFormatter.ONE_GB, 2);
|
|
|
- }
|
|
|
-
|
|
|
- set _totalGB(gb) {
|
|
|
- this.totalGB = NumberFormatter.toFixed(gb * SizeFormatter.ONE_GB, 0);
|
|
|
- }
|
|
|
-};
|
|
|
-
|
|
|
-Inbound.VmessSettings = class extends Inbound.Settings {
|
|
|
- constructor(protocol: any,
|
|
|
- vmesses: any[] = []) {
|
|
|
- super(protocol);
|
|
|
- this.vmesses = vmesses;
|
|
|
- }
|
|
|
-
|
|
|
- indexOfVmessById(id: any) {
|
|
|
- return this.vmesses.findIndex((VMESS: any) => VMESS.id === id);
|
|
|
- }
|
|
|
-
|
|
|
- addVmess(VMESS: any) {
|
|
|
- if (this.indexOfVmessById(VMESS.id) >= 0) {
|
|
|
- return false;
|
|
|
- }
|
|
|
- this.vmesses.push(VMESS);
|
|
|
- }
|
|
|
-
|
|
|
- delVmess(VMESS: any) {
|
|
|
- const i = this.indexOfVmessById(VMESS.id);
|
|
|
- if (i >= 0) {
|
|
|
- this.vmesses.splice(i, 1);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- static fromJson(json: any = {}) {
|
|
|
- return new Inbound.VmessSettings(
|
|
|
- Protocols.VMESS,
|
|
|
- (json.clients || []).map((client: any) => Inbound.VmessSettings.VMESS.fromJson(client)),
|
|
|
- );
|
|
|
- }
|
|
|
-
|
|
|
- toJson() {
|
|
|
- return {
|
|
|
- clients: Inbound.VmessSettings.toJsonArray(this.vmesses),
|
|
|
- };
|
|
|
- }
|
|
|
-};
|
|
|
-
|
|
|
-Inbound.VmessSettings.VMESS = class extends Inbound.ClientBase {
|
|
|
- constructor(
|
|
|
- id: any = RandomUtil.randomUUID(),
|
|
|
- security: any = USERS_SECURITY.AUTO,
|
|
|
- email?: any, limitIp?: any, totalGB?: any, expiryTime?: any, enable?: any, tgId?: any, subId?: any, comment?: any, reset?: any, created_at?: any, updated_at?: any,
|
|
|
- ) {
|
|
|
- super(email, limitIp, totalGB, expiryTime, enable, tgId, subId, comment, reset, created_at, updated_at);
|
|
|
- this.id = id;
|
|
|
- this.security = security;
|
|
|
- }
|
|
|
-
|
|
|
- static fromJson(json: any = {}) {
|
|
|
- return new Inbound.VmessSettings.VMESS(
|
|
|
- json.id,
|
|
|
- json.security,
|
|
|
- ...Inbound.ClientBase.commonArgsFromJson(json),
|
|
|
- );
|
|
|
- }
|
|
|
-
|
|
|
- toJson() {
|
|
|
- return {
|
|
|
- id: this.id,
|
|
|
- security: this.security,
|
|
|
- ...this._clientBaseToJson(),
|
|
|
- };
|
|
|
- }
|
|
|
-};
|
|
|
-
|
|
|
-Inbound.VLESSSettings = class extends Inbound.Settings {
|
|
|
- constructor(
|
|
|
- protocol: any,
|
|
|
- vlesses: any[] = [],
|
|
|
- decryption: any = "none",
|
|
|
- encryption: any = "none",
|
|
|
- fallbacks: any[] = [],
|
|
|
- testseed: any[] = [],
|
|
|
- ) {
|
|
|
- super(protocol);
|
|
|
- this.vlesses = vlesses;
|
|
|
- this.decryption = decryption;
|
|
|
- this.encryption = encryption;
|
|
|
- this.fallbacks = fallbacks;
|
|
|
- this.testseed = testseed;
|
|
|
- }
|
|
|
-
|
|
|
- addFallback() {
|
|
|
- this.fallbacks.push(new Inbound.VLESSSettings.Fallback());
|
|
|
- }
|
|
|
-
|
|
|
- delFallback(index: number) {
|
|
|
- this.fallbacks.splice(index, 1);
|
|
|
- }
|
|
|
-
|
|
|
- // Empty array means "use server defaults" (won't be sent).
|
|
|
- // Anything else must be exactly 4 positive integers.
|
|
|
- static isValidTestseed(arr: any): boolean {
|
|
|
- if (!Array.isArray(arr) || arr.length === 0) return true;
|
|
|
- if (arr.length !== 4) return false;
|
|
|
- return arr.every((v: any) => Number.isInteger(v) && v > 0);
|
|
|
- }
|
|
|
-
|
|
|
- static fromJson(json: any = {}) {
|
|
|
- // Preserve a saved testseed only if it's a valid 4-positive-int array; otherwise leave empty
|
|
|
- // so toJson omits it and the form falls back to placeholder defaults.
|
|
|
- const saved = json.testseed;
|
|
|
- const testseed = (Array.isArray(saved)
|
|
|
- && saved.length === 4
|
|
|
- && saved.every((v: any) => Number.isInteger(v) && v > 0))
|
|
|
- ? saved
|
|
|
- : [];
|
|
|
-
|
|
|
- const obj = new Inbound.VLESSSettings(
|
|
|
- Protocols.VLESS,
|
|
|
- (json.clients || []).map((client: any) => Inbound.VLESSSettings.VLESS.fromJson(client)),
|
|
|
- json.decryption,
|
|
|
- json.encryption,
|
|
|
- Inbound.VLESSSettings.Fallback.fromJson(json.fallbacks || []),
|
|
|
- testseed,
|
|
|
- );
|
|
|
- return obj;
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- toJson() {
|
|
|
- const json: any = {
|
|
|
- clients: Inbound.VLESSSettings.toJsonArray(this.vlesses),
|
|
|
- };
|
|
|
-
|
|
|
- if (this.decryption) {
|
|
|
- json.decryption = this.decryption;
|
|
|
- }
|
|
|
-
|
|
|
- if (this.encryption) {
|
|
|
- json.encryption = this.encryption;
|
|
|
- }
|
|
|
-
|
|
|
- if (this.fallbacks && this.fallbacks.length > 0) {
|
|
|
- json.fallbacks = Inbound.VLESSSettings.toJsonArray(this.fallbacks);
|
|
|
- }
|
|
|
-
|
|
|
- // testseed is only meaningful for the exact xtls-rprx-vision flow, and only when
|
|
|
- // the user supplied a complete 4-positive-int array. Otherwise omit and let the
|
|
|
- // backend fall back to its safe defaults.
|
|
|
- const hasVisionFlow = this.vlesses && this.vlesses.some((v: any) => v.flow === TLS_FLOW_CONTROL.VISION);
|
|
|
- if (hasVisionFlow
|
|
|
- && Array.isArray(this.testseed)
|
|
|
- && this.testseed.length === 4
|
|
|
- && this.testseed.every((v: any) => Number.isInteger(v) && v > 0)) {
|
|
|
- json.testseed = this.testseed;
|
|
|
- }
|
|
|
-
|
|
|
- return json;
|
|
|
- }
|
|
|
-};
|
|
|
-
|
|
|
-Inbound.VLESSSettings.VLESS = class extends Inbound.ClientBase {
|
|
|
- constructor(
|
|
|
- id: any = RandomUtil.randomUUID(),
|
|
|
- flow: any = '',
|
|
|
- reverseTag: any = '',
|
|
|
- reverseSniffing: any = new Sniffing(),
|
|
|
- email?: any, limitIp?: any, totalGB?: any, expiryTime?: any, enable?: any, tgId?: any, subId?: any, comment?: any, reset?: any, created_at?: any, updated_at?: any,
|
|
|
- ) {
|
|
|
- super(email, limitIp, totalGB, expiryTime, enable, tgId, subId, comment, reset, created_at, updated_at);
|
|
|
- this.id = id;
|
|
|
- this.flow = flow;
|
|
|
- this.reverseTag = reverseTag;
|
|
|
- this.reverseSniffing = reverseSniffing;
|
|
|
- }
|
|
|
-
|
|
|
- static fromJson(json: any = {}) {
|
|
|
- return new Inbound.VLESSSettings.VLESS(
|
|
|
- json.id,
|
|
|
- json.flow,
|
|
|
- json.reverse?.tag ?? '',
|
|
|
- Sniffing.fromJson(json.reverse?.sniffing || {}),
|
|
|
- ...Inbound.ClientBase.commonArgsFromJson(json),
|
|
|
- );
|
|
|
- }
|
|
|
-
|
|
|
- toJson() {
|
|
|
- const json: any = {
|
|
|
- id: this.id,
|
|
|
- flow: this.flow,
|
|
|
- ...this._clientBaseToJson(),
|
|
|
- };
|
|
|
- if (this.reverseTag) {
|
|
|
- json.reverse = {
|
|
|
- tag: this.reverseTag,
|
|
|
- };
|
|
|
- }
|
|
|
- return json;
|
|
|
- }
|
|
|
-};
|
|
|
-
|
|
|
-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() {
|
|
|
- return XrayCommonClass.fallbackToJson(this as unknown as FallbackEntry);
|
|
|
- }
|
|
|
-
|
|
|
- static fromJson(json: any = []) {
|
|
|
- return (json || []).map((f: any) => new Inbound.VLESSSettings.Fallback(
|
|
|
- f.name, f.alpn, f.path, f.dest, f.xver,
|
|
|
- ));
|
|
|
- }
|
|
|
-};
|
|
|
-
|
|
|
-Inbound.TrojanSettings = class extends Inbound.Settings {
|
|
|
- constructor(protocol: any,
|
|
|
- trojans: any[] = [],
|
|
|
- fallbacks: any[] = [],) {
|
|
|
- super(protocol);
|
|
|
- this.trojans = trojans;
|
|
|
- this.fallbacks = fallbacks;
|
|
|
- }
|
|
|
-
|
|
|
- addFallback() {
|
|
|
- this.fallbacks.push(new Inbound.TrojanSettings.Fallback());
|
|
|
- }
|
|
|
-
|
|
|
- delFallback(index: number) {
|
|
|
- this.fallbacks.splice(index, 1);
|
|
|
- }
|
|
|
-
|
|
|
- static fromJson(json: any = {}) {
|
|
|
- return new Inbound.TrojanSettings(
|
|
|
- Protocols.TROJAN,
|
|
|
- (json.clients || []).map((client: any) => Inbound.TrojanSettings.Trojan.fromJson(client)),
|
|
|
- Inbound.TrojanSettings.Fallback.fromJson(json.fallbacks),);
|
|
|
- }
|
|
|
-
|
|
|
- toJson() {
|
|
|
- const json: any = {
|
|
|
- clients: Inbound.TrojanSettings.toJsonArray(this.trojans),
|
|
|
- };
|
|
|
- if (this.fallbacks && this.fallbacks.length > 0) {
|
|
|
- json.fallbacks = Inbound.TrojanSettings.toJsonArray(this.fallbacks);
|
|
|
- }
|
|
|
- return json;
|
|
|
- }
|
|
|
-};
|
|
|
-
|
|
|
-Inbound.TrojanSettings.Trojan = class extends Inbound.ClientBase {
|
|
|
- constructor(
|
|
|
- password = RandomUtil.randomSeq(10),
|
|
|
- email?: any, limitIp?: any, totalGB?: any, expiryTime?: any, enable?: any, tgId?: any, subId?: any, comment?: any, reset?: any, created_at?: any, updated_at?: any,
|
|
|
- ) {
|
|
|
- super(email, limitIp, totalGB, expiryTime, enable, tgId, subId, comment, reset, created_at, updated_at);
|
|
|
- this.password = password;
|
|
|
- }
|
|
|
-
|
|
|
- toJson() {
|
|
|
- return {
|
|
|
- password: this.password,
|
|
|
- ...this._clientBaseToJson(),
|
|
|
- };
|
|
|
- }
|
|
|
-
|
|
|
- static fromJson(json: any = {}) {
|
|
|
- return new Inbound.TrojanSettings.Trojan(
|
|
|
- json.password,
|
|
|
- ...Inbound.ClientBase.commonArgsFromJson(json),
|
|
|
- );
|
|
|
- }
|
|
|
-};
|
|
|
-
|
|
|
-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() {
|
|
|
- return XrayCommonClass.fallbackToJson(this as unknown as FallbackEntry);
|
|
|
- }
|
|
|
-
|
|
|
- static fromJson(json: any = []) {
|
|
|
- return (json || []).map((f: any) => new Inbound.TrojanSettings.Fallback(
|
|
|
- f.name, f.alpn, f.path, f.dest, f.xver,
|
|
|
- ));
|
|
|
- }
|
|
|
-};
|
|
|
-
|
|
|
-Inbound.ShadowsocksSettings = class extends Inbound.Settings {
|
|
|
- constructor(protocol: any,
|
|
|
- method: any = SSMethods.BLAKE3_AES_256_GCM,
|
|
|
- password: any = RandomUtil.randomShadowsocksPassword(),
|
|
|
- network: any = 'tcp',
|
|
|
- shadowsockses: any[] = [],
|
|
|
- ivCheck = false,
|
|
|
- ) {
|
|
|
- super(protocol);
|
|
|
- this.method = method;
|
|
|
- this.password = password;
|
|
|
- this.network = network;
|
|
|
- this.shadowsockses = shadowsockses;
|
|
|
- this.ivCheck = ivCheck;
|
|
|
- }
|
|
|
-
|
|
|
- static fromJson(json: any = {}) {
|
|
|
- return new Inbound.ShadowsocksSettings(
|
|
|
- Protocols.SHADOWSOCKS,
|
|
|
- json.method,
|
|
|
- json.password,
|
|
|
- json.network,
|
|
|
- (json.clients || []).map((client: any) => Inbound.ShadowsocksSettings.Shadowsocks.fromJson(client)),
|
|
|
- json.ivCheck,
|
|
|
- );
|
|
|
- }
|
|
|
-
|
|
|
- toJson() {
|
|
|
- return {
|
|
|
- method: this.method,
|
|
|
- password: this.password,
|
|
|
- network: this.network,
|
|
|
- clients: Inbound.ShadowsocksSettings.toJsonArray(this.shadowsockses),
|
|
|
- ivCheck: this.ivCheck,
|
|
|
- };
|
|
|
- }
|
|
|
-};
|
|
|
-
|
|
|
-Inbound.ShadowsocksSettings.Shadowsocks = class extends Inbound.ClientBase {
|
|
|
- constructor(
|
|
|
- method = '',
|
|
|
- password = RandomUtil.randomShadowsocksPassword(),
|
|
|
- email?: any, limitIp?: any, totalGB?: any, expiryTime?: any, enable?: any, tgId?: any, subId?: any, comment?: any, reset?: any, created_at?: any, updated_at?: any,
|
|
|
- ) {
|
|
|
- super(email, limitIp, totalGB, expiryTime, enable, tgId, subId, comment, reset, created_at, updated_at);
|
|
|
- this.method = method;
|
|
|
- this.password = password;
|
|
|
- }
|
|
|
-
|
|
|
- toJson() {
|
|
|
- return {
|
|
|
- method: this.method,
|
|
|
- password: this.password,
|
|
|
- ...this._clientBaseToJson(),
|
|
|
- };
|
|
|
- }
|
|
|
-
|
|
|
- static fromJson(json: any = {}) {
|
|
|
- return new Inbound.ShadowsocksSettings.Shadowsocks(
|
|
|
- json.method,
|
|
|
- json.password,
|
|
|
- ...Inbound.ClientBase.commonArgsFromJson(json),
|
|
|
- );
|
|
|
- }
|
|
|
-};
|
|
|
-
|
|
|
-Inbound.HysteriaSettings = class extends Inbound.Settings {
|
|
|
- constructor(protocol: any, version: any = 2, hysterias: any[] = []) {
|
|
|
- super(protocol);
|
|
|
- this.version = version;
|
|
|
- this.hysterias = hysterias;
|
|
|
- }
|
|
|
-
|
|
|
- static fromJson(json: any = {}) {
|
|
|
- return new Inbound.HysteriaSettings(
|
|
|
- Protocols.HYSTERIA,
|
|
|
- json.version ?? 2,
|
|
|
- (json.clients || []).map((client: any) => Inbound.HysteriaSettings.Hysteria.fromJson(client)),
|
|
|
- );
|
|
|
- }
|
|
|
-
|
|
|
- toJson() {
|
|
|
- return {
|
|
|
- version: this.version,
|
|
|
- clients: Inbound.HysteriaSettings.toJsonArray(this.hysterias),
|
|
|
- };
|
|
|
- }
|
|
|
-};
|
|
|
-
|
|
|
-Inbound.HysteriaSettings.Hysteria = class extends Inbound.ClientBase {
|
|
|
- constructor(
|
|
|
- auth = RandomUtil.randomSeq(10),
|
|
|
- email?: any, limitIp?: any, totalGB?: any, expiryTime?: any, enable?: any, tgId?: any, subId?: any, comment?: any, reset?: any, created_at?: any, updated_at?: any,
|
|
|
- ) {
|
|
|
- super(email, limitIp, totalGB, expiryTime, enable, tgId, subId, comment, reset, created_at, updated_at);
|
|
|
- this.auth = auth;
|
|
|
- }
|
|
|
-
|
|
|
- toJson() {
|
|
|
- return {
|
|
|
- auth: this.auth,
|
|
|
- ...this._clientBaseToJson(),
|
|
|
- };
|
|
|
- }
|
|
|
-
|
|
|
- static fromJson(json: any = {}) {
|
|
|
- return new Inbound.HysteriaSettings.Hysteria(
|
|
|
- json.auth,
|
|
|
- ...Inbound.ClientBase.commonArgsFromJson(json),
|
|
|
- );
|
|
|
- }
|
|
|
-};
|
|
|
-
|
|
|
-Inbound.TunnelSettings = class extends Inbound.Settings {
|
|
|
- constructor(
|
|
|
- protocol: any,
|
|
|
- rewriteAddress?: any,
|
|
|
- rewritePort?: any,
|
|
|
- portMap: any[] = [],
|
|
|
- allowedNetwork: any = 'tcp,udp',
|
|
|
- followRedirect: any = false
|
|
|
- ) {
|
|
|
- super(protocol);
|
|
|
- this.rewriteAddress = rewriteAddress;
|
|
|
- this.rewritePort = rewritePort;
|
|
|
- this.portMap = portMap;
|
|
|
- this.allowedNetwork = allowedNetwork;
|
|
|
- this.followRedirect = followRedirect;
|
|
|
- }
|
|
|
-
|
|
|
- addPortMap(port = '', target = '') {
|
|
|
- this.portMap.push({ name: port, value: target });
|
|
|
- }
|
|
|
-
|
|
|
- removePortMap(index: number) {
|
|
|
- this.portMap.splice(index, 1);
|
|
|
- }
|
|
|
-
|
|
|
- static fromJson(json: any = {}) {
|
|
|
- return new Inbound.TunnelSettings(
|
|
|
- Protocols.TUNNEL,
|
|
|
- json.rewriteAddress,
|
|
|
- json.rewritePort,
|
|
|
- XrayCommonClass.toHeaders(json.portMap),
|
|
|
- json.allowedNetwork,
|
|
|
- json.followRedirect,
|
|
|
- );
|
|
|
- }
|
|
|
-
|
|
|
- toJson() {
|
|
|
- return {
|
|
|
- rewriteAddress: this.rewriteAddress,
|
|
|
- rewritePort: this.rewritePort,
|
|
|
- portMap: XrayCommonClass.toV2Headers(this.portMap, false),
|
|
|
- allowedNetwork: this.allowedNetwork,
|
|
|
- followRedirect: this.followRedirect,
|
|
|
- };
|
|
|
- }
|
|
|
-};
|
|
|
-
|
|
|
-Inbound.MixedSettings = class extends Inbound.Settings {
|
|
|
- constructor(protocol: any, auth: any = 'password', accounts: any[] = [new Inbound.MixedSettings.SocksAccount()], udp: any = false, ip: any = '127.0.0.1') {
|
|
|
- super(protocol);
|
|
|
- this.auth = auth;
|
|
|
- this.accounts = accounts;
|
|
|
- this.udp = udp;
|
|
|
- this.ip = ip;
|
|
|
- }
|
|
|
-
|
|
|
- addAccount(account: any) {
|
|
|
- this.accounts.push(account);
|
|
|
- }
|
|
|
-
|
|
|
- delAccount(index: number) {
|
|
|
- this.accounts.splice(index, 1);
|
|
|
- }
|
|
|
-
|
|
|
- static fromJson(json: any = {}) {
|
|
|
- let accounts;
|
|
|
- if (json.auth === 'password') {
|
|
|
- accounts = json.accounts.map(
|
|
|
- (account: any) => Inbound.MixedSettings.SocksAccount.fromJson(account)
|
|
|
- )
|
|
|
- }
|
|
|
- return new Inbound.MixedSettings(
|
|
|
- Protocols.MIXED,
|
|
|
- json.auth,
|
|
|
- accounts,
|
|
|
- json.udp,
|
|
|
- json.ip,
|
|
|
- );
|
|
|
- }
|
|
|
-
|
|
|
- toJson() {
|
|
|
- return {
|
|
|
- auth: this.auth,
|
|
|
- accounts: this.auth === 'password' ? this.accounts.map((account: any) => account.toJson()) : undefined,
|
|
|
- udp: this.udp,
|
|
|
- ip: this.ip,
|
|
|
- };
|
|
|
- }
|
|
|
-};
|
|
|
-Inbound.MixedSettings.SocksAccount = class extends XrayCommonClass {
|
|
|
- constructor(user = RandomUtil.randomSeq(10), pass = RandomUtil.randomSeq(10)) {
|
|
|
- super();
|
|
|
- this.user = user;
|
|
|
- this.pass = pass;
|
|
|
- }
|
|
|
-
|
|
|
- static fromJson(json: any = {}) {
|
|
|
- return new Inbound.MixedSettings.SocksAccount(json.user, json.pass);
|
|
|
- }
|
|
|
-};
|
|
|
-
|
|
|
-Inbound.HttpSettings = class extends Inbound.Settings {
|
|
|
- constructor(
|
|
|
- protocol: any,
|
|
|
- accounts: any[] = [new Inbound.HttpSettings.HttpAccount()],
|
|
|
- allowTransparent: any = false,
|
|
|
- ) {
|
|
|
- super(protocol);
|
|
|
- this.accounts = accounts;
|
|
|
- this.allowTransparent = allowTransparent;
|
|
|
- }
|
|
|
-
|
|
|
- addAccount(account: any) {
|
|
|
- this.accounts.push(account);
|
|
|
- }
|
|
|
-
|
|
|
- delAccount(index: number) {
|
|
|
- this.accounts.splice(index, 1);
|
|
|
- }
|
|
|
-
|
|
|
- static fromJson(json: any = {}) {
|
|
|
- return new Inbound.HttpSettings(
|
|
|
- Protocols.HTTP,
|
|
|
- json.accounts.map((account: any) => Inbound.HttpSettings.HttpAccount.fromJson(account)),
|
|
|
- json.allowTransparent,
|
|
|
- );
|
|
|
- }
|
|
|
-
|
|
|
- toJson() {
|
|
|
- return {
|
|
|
- accounts: Inbound.HttpSettings.toJsonArray(this.accounts),
|
|
|
- allowTransparent: this.allowTransparent,
|
|
|
- };
|
|
|
- }
|
|
|
-};
|
|
|
-
|
|
|
-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: any = {}) {
|
|
|
- return new Inbound.HttpSettings.HttpAccount(json.user, json.pass);
|
|
|
- }
|
|
|
-};
|
|
|
-
|
|
|
-Inbound.WireguardSettings = class extends XrayCommonClass {
|
|
|
- constructor(
|
|
|
- protocol?: any,
|
|
|
- mtu: any = 1420,
|
|
|
- secretKey: any = Wireguard.generateKeypair().privateKey,
|
|
|
- peers: any[] = [new Inbound.WireguardSettings.Peer()],
|
|
|
- noKernelTun: any = false
|
|
|
- ) {
|
|
|
- super();
|
|
|
- this.protocol = protocol;
|
|
|
- this.mtu = mtu;
|
|
|
- this.secretKey = secretKey;
|
|
|
- this.pubKey = secretKey.length > 0 ? Wireguard.generateKeypair(secretKey).publicKey : '';
|
|
|
- this.peers = peers;
|
|
|
- this.noKernelTun = noKernelTun;
|
|
|
- }
|
|
|
-
|
|
|
- addPeer() {
|
|
|
- this.peers.push(new Inbound.WireguardSettings.Peer(null, null, '', ['10.0.0.' + (this.peers.length + 2)]));
|
|
|
- }
|
|
|
-
|
|
|
- delPeer(index: number) {
|
|
|
- this.peers.splice(index, 1);
|
|
|
- }
|
|
|
-
|
|
|
- static fromJson(json: any = {}) {
|
|
|
- return new Inbound.WireguardSettings(
|
|
|
- Protocols.WIREGUARD,
|
|
|
- json.mtu,
|
|
|
- json.secretKey,
|
|
|
- json.peers.map((peer: any) => Inbound.WireguardSettings.Peer.fromJson(peer)),
|
|
|
- json.noKernelTun,
|
|
|
- );
|
|
|
- }
|
|
|
-
|
|
|
- toJson() {
|
|
|
- return {
|
|
|
- mtu: this.mtu ?? undefined,
|
|
|
- secretKey: this.secretKey,
|
|
|
- peers: Inbound.WireguardSettings.Peer.toJsonArray(this.peers),
|
|
|
- noKernelTun: this.noKernelTun,
|
|
|
- };
|
|
|
- }
|
|
|
-};
|
|
|
-
|
|
|
-Inbound.WireguardSettings.Peer = class extends XrayCommonClass {
|
|
|
- constructor(privateKey?: any, publicKey?: any, psk: any = '', allowedIPs: any[] = ['10.0.0.2/32'], keepAlive: any = 0) {
|
|
|
- super();
|
|
|
- this.privateKey = privateKey
|
|
|
- this.publicKey = publicKey;
|
|
|
- if (!this.publicKey) {
|
|
|
- [this.publicKey, this.privateKey] = Object.values(Wireguard.generateKeypair())
|
|
|
- }
|
|
|
- this.psk = psk;
|
|
|
- allowedIPs.forEach((a: any, index: number) => {
|
|
|
- if (a.length > 0 && !a.includes('/')) allowedIPs[index] += '/32';
|
|
|
- })
|
|
|
- this.allowedIPs = allowedIPs;
|
|
|
- this.keepAlive = keepAlive;
|
|
|
- }
|
|
|
-
|
|
|
- static fromJson(json: any = {}) {
|
|
|
- return new Inbound.WireguardSettings.Peer(
|
|
|
- json.privateKey,
|
|
|
- json.publicKey,
|
|
|
- json.preSharedKey,
|
|
|
- json.allowedIPs,
|
|
|
- json.keepAlive
|
|
|
- );
|
|
|
- }
|
|
|
-
|
|
|
- toJson() {
|
|
|
- this.allowedIPs.forEach((a: any, index: number) => {
|
|
|
- if (a.length > 0 && !a.includes('/')) this.allowedIPs[index] += '/32';
|
|
|
- });
|
|
|
- return {
|
|
|
- privateKey: this.privateKey,
|
|
|
- publicKey: this.publicKey,
|
|
|
- preSharedKey: this.psk.length > 0 ? this.psk : undefined,
|
|
|
- allowedIPs: this.allowedIPs,
|
|
|
- keepAlive: this.keepAlive ?? undefined,
|
|
|
- };
|
|
|
- }
|
|
|
-};
|
|
|
-
|
|
|
-Inbound.TunSettings = class extends Inbound.Settings {
|
|
|
- constructor(
|
|
|
- protocol: any,
|
|
|
- name: any = 'xray0',
|
|
|
- mtu: any = 1500,
|
|
|
- gateway: any[] = [],
|
|
|
- dns: any[] = [],
|
|
|
- userLevel: any = 0,
|
|
|
- autoSystemRoutingTable: any[] = [],
|
|
|
- autoOutboundsInterface = 'auto'
|
|
|
- ) {
|
|
|
- super(protocol);
|
|
|
- this.name = name;
|
|
|
- this.mtu = Number(mtu) || 1500;
|
|
|
- this.gateway = Array.isArray(gateway) ? gateway : [];
|
|
|
- this.dns = Array.isArray(dns) ? dns : [];
|
|
|
- this.userLevel = userLevel;
|
|
|
- this.autoSystemRoutingTable = Array.isArray(autoSystemRoutingTable) ? autoSystemRoutingTable : [];
|
|
|
- this.autoOutboundsInterface = autoOutboundsInterface;
|
|
|
- }
|
|
|
-
|
|
|
- static fromJson(json: any = {}) {
|
|
|
- const rawMtu = json.mtu ?? json.MTU;
|
|
|
- const mtu = Array.isArray(rawMtu) ? rawMtu[0] : rawMtu;
|
|
|
- return new Inbound.TunSettings(
|
|
|
- Protocols.TUN,
|
|
|
- json.name ?? 'xray0',
|
|
|
- mtu ?? 1500,
|
|
|
- json.gateway ?? json.Gateway ?? [],
|
|
|
- json.dns ?? json.DNS ?? [],
|
|
|
- json.userLevel ?? 0,
|
|
|
- json.autoSystemRoutingTable ?? [],
|
|
|
- Object.prototype.hasOwnProperty.call(json, 'autoOutboundsInterface') ? json.autoOutboundsInterface : 'auto'
|
|
|
- );
|
|
|
- }
|
|
|
-
|
|
|
- toJson() {
|
|
|
- return {
|
|
|
- name: this.name || 'xray0',
|
|
|
- mtu: Number(this.mtu) || 1500,
|
|
|
- gateway: this.gateway,
|
|
|
- dns: this.dns,
|
|
|
- userLevel: this.userLevel || 0,
|
|
|
- autoSystemRoutingTable: this.autoSystemRoutingTable,
|
|
|
- autoOutboundsInterface: this.autoOutboundsInterface,
|
|
|
- };
|
|
|
- }
|
|
|
-};
|