1
0

outbound.js 67 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103
  1. const Protocols = {
  2. Freedom: "freedom",
  3. Blackhole: "blackhole",
  4. DNS: "dns",
  5. VMess: "vmess",
  6. VLESS: "vless",
  7. Trojan: "trojan",
  8. Shadowsocks: "shadowsocks",
  9. Wireguard: "wireguard",
  10. Hysteria: "hysteria",
  11. Socks: "socks",
  12. HTTP: "http",
  13. };
  14. const SSMethods = {
  15. AES_256_GCM: 'aes-256-gcm',
  16. AES_128_GCM: 'aes-128-gcm',
  17. CHACHA20_POLY1305: 'chacha20-poly1305',
  18. CHACHA20_IETF_POLY1305: 'chacha20-ietf-poly1305',
  19. XCHACHA20_POLY1305: 'xchacha20-poly1305',
  20. XCHACHA20_IETF_POLY1305: 'xchacha20-ietf-poly1305',
  21. BLAKE3_AES_128_GCM: '2022-blake3-aes-128-gcm',
  22. BLAKE3_AES_256_GCM: '2022-blake3-aes-256-gcm',
  23. BLAKE3_CHACHA20_POLY1305: '2022-blake3-chacha20-poly1305',
  24. };
  25. const TLS_FLOW_CONTROL = {
  26. VISION: "xtls-rprx-vision",
  27. VISION_UDP443: "xtls-rprx-vision-udp443",
  28. };
  29. const UTLS_FINGERPRINT = {
  30. UTLS_CHROME: "chrome",
  31. UTLS_FIREFOX: "firefox",
  32. UTLS_SAFARI: "safari",
  33. UTLS_IOS: "ios",
  34. UTLS_android: "android",
  35. UTLS_EDGE: "edge",
  36. UTLS_360: "360",
  37. UTLS_QQ: "qq",
  38. UTLS_RANDOM: "random",
  39. UTLS_RANDOMIZED: "randomized",
  40. UTLS_RONDOMIZEDNOALPN: "randomizednoalpn",
  41. UTLS_UNSAFE: "unsafe",
  42. };
  43. const ALPN_OPTION = {
  44. H3: "h3",
  45. H2: "h2",
  46. HTTP1: "http/1.1",
  47. };
  48. const SNIFFING_OPTION = {
  49. HTTP: "http",
  50. TLS: "tls",
  51. QUIC: "quic",
  52. FAKEDNS: "fakedns"
  53. };
  54. const OutboundDomainStrategies = [
  55. "AsIs",
  56. "UseIP",
  57. "UseIPv4",
  58. "UseIPv6",
  59. "UseIPv6v4",
  60. "UseIPv4v6",
  61. "ForceIP",
  62. "ForceIPv6v4",
  63. "ForceIPv6",
  64. "ForceIPv4v6",
  65. "ForceIPv4"
  66. ];
  67. const WireguardDomainStrategy = [
  68. "ForceIP",
  69. "ForceIPv4",
  70. "ForceIPv4v6",
  71. "ForceIPv6",
  72. "ForceIPv6v4"
  73. ];
  74. const USERS_SECURITY = {
  75. AES_128_GCM: "aes-128-gcm",
  76. CHACHA20_POLY1305: "chacha20-poly1305",
  77. AUTO: "auto",
  78. NONE: "none",
  79. ZERO: "zero",
  80. };
  81. const MODE_OPTION = {
  82. AUTO: "auto",
  83. PACKET_UP: "packet-up",
  84. STREAM_UP: "stream-up",
  85. STREAM_ONE: "stream-one",
  86. };
  87. const Address_Port_Strategy = {
  88. NONE: "none",
  89. SrvPortOnly: "srvportonly",
  90. SrvAddressOnly: "srvaddressonly",
  91. SrvPortAndAddress: "srvportandaddress",
  92. TxtPortOnly: "txtportonly",
  93. TxtAddressOnly: "txtaddressonly",
  94. TxtPortAndAddress: "txtportandaddress"
  95. };
  96. const DNSRuleActions = ['direct', 'drop', 'reject', 'hijack'];
  97. function normalizeDNSRuleField(value) {
  98. if (value === null || value === undefined) {
  99. return '';
  100. }
  101. if (Array.isArray(value)) {
  102. return value.map(item => item.toString().trim()).filter(item => item.length > 0).join(',');
  103. }
  104. return value.toString().trim();
  105. }
  106. function normalizeDNSRuleAction(action) {
  107. action = ObjectUtil.isEmpty(action) ? 'direct' : action.toString().toLowerCase().trim();
  108. return DNSRuleActions.includes(action) ? action : 'direct';
  109. }
  110. function parseLegacyDNSBlockTypes(blockTypes) {
  111. if (blockTypes === null || blockTypes === undefined || blockTypes === '') {
  112. return [];
  113. }
  114. if (Array.isArray(blockTypes)) {
  115. return blockTypes
  116. .map(item => Number(item))
  117. .filter(item => Number.isInteger(item) && item >= 0 && item <= 65535);
  118. }
  119. if (typeof blockTypes === 'number') {
  120. return Number.isInteger(blockTypes) && blockTypes >= 0 && blockTypes <= 65535 ? [blockTypes] : [];
  121. }
  122. return blockTypes
  123. .toString()
  124. .split(',')
  125. .map(item => item.trim())
  126. .filter(item => /^\d+$/.test(item))
  127. .map(item => Number(item))
  128. .filter(item => item >= 0 && item <= 65535);
  129. }
  130. function buildLegacyDNSRules(nonIPQuery, blockTypes) {
  131. const mode = ['reject', 'drop', 'skip'].includes(nonIPQuery) ? nonIPQuery : 'reject';
  132. const rules = [];
  133. const parsedBlockTypes = parseLegacyDNSBlockTypes(blockTypes);
  134. if (parsedBlockTypes.length > 0) {
  135. rules.push(new Outbound.DNSRule(mode === 'reject' ? 'reject' : 'drop', parsedBlockTypes.join(',')));
  136. }
  137. rules.push(new Outbound.DNSRule('hijack', '1,28'));
  138. rules.push(new Outbound.DNSRule(mode === 'skip' ? 'direct' : mode));
  139. return rules;
  140. }
  141. function getDNSRulesFromJson(json = {}) {
  142. if (Array.isArray(json.rules) && json.rules.length > 0) {
  143. return json.rules.map(rule => Outbound.DNSRule.fromJson(rule));
  144. }
  145. if (json.nonIPQuery !== undefined || json.blockTypes !== undefined) {
  146. return buildLegacyDNSRules(json.nonIPQuery, json.blockTypes);
  147. }
  148. return [];
  149. }
  150. Object.freeze(Protocols);
  151. Object.freeze(SSMethods);
  152. Object.freeze(TLS_FLOW_CONTROL);
  153. Object.freeze(UTLS_FINGERPRINT);
  154. Object.freeze(ALPN_OPTION);
  155. Object.freeze(SNIFFING_OPTION);
  156. Object.freeze(OutboundDomainStrategies);
  157. Object.freeze(WireguardDomainStrategy);
  158. Object.freeze(USERS_SECURITY);
  159. Object.freeze(MODE_OPTION);
  160. Object.freeze(Address_Port_Strategy);
  161. Object.freeze(DNSRuleActions);
  162. class CommonClass {
  163. static toJsonArray(arr) {
  164. return arr.map(obj => obj.toJson());
  165. }
  166. static fromJson() {
  167. return new CommonClass();
  168. }
  169. toJson() {
  170. return this;
  171. }
  172. toString(format = true) {
  173. return format ? JSON.stringify(this.toJson(), null, 2) : JSON.stringify(this.toJson());
  174. }
  175. }
  176. class ReverseSniffing extends CommonClass {
  177. constructor(
  178. enabled = false,
  179. destOverride = ['http', 'tls', 'quic', 'fakedns'],
  180. metadataOnly = false,
  181. routeOnly = false,
  182. ipsExcluded = [],
  183. domainsExcluded = [],
  184. ) {
  185. super();
  186. this.enabled = enabled;
  187. this.destOverride = Array.isArray(destOverride) && destOverride.length > 0 ? destOverride : ['http', 'tls', 'quic', 'fakedns'];
  188. this.metadataOnly = metadataOnly;
  189. this.routeOnly = routeOnly;
  190. this.ipsExcluded = Array.isArray(ipsExcluded) ? ipsExcluded : [];
  191. this.domainsExcluded = Array.isArray(domainsExcluded) ? domainsExcluded : [];
  192. }
  193. static fromJson(json = {}) {
  194. if (!json || Object.keys(json).length === 0) {
  195. return new ReverseSniffing();
  196. }
  197. return new ReverseSniffing(
  198. !!json.enabled,
  199. json.destOverride,
  200. json.metadataOnly,
  201. json.routeOnly,
  202. json.ipsExcluded || [],
  203. json.domainsExcluded || [],
  204. );
  205. }
  206. toJson() {
  207. return {
  208. enabled: this.enabled,
  209. destOverride: this.destOverride,
  210. metadataOnly: this.metadataOnly,
  211. routeOnly: this.routeOnly,
  212. ipsExcluded: this.ipsExcluded.length > 0 ? this.ipsExcluded : undefined,
  213. domainsExcluded: this.domainsExcluded.length > 0 ? this.domainsExcluded : undefined,
  214. };
  215. }
  216. }
  217. class TcpStreamSettings extends CommonClass {
  218. constructor(type = 'none', host, path) {
  219. super();
  220. this.type = type;
  221. this.host = host;
  222. this.path = path;
  223. }
  224. static fromJson(json = {}) {
  225. let header = json.header;
  226. if (!header) return new TcpStreamSettings();
  227. if (header.type == 'http' && header.request) {
  228. return new TcpStreamSettings(
  229. header.type,
  230. header.request.headers.Host.join(','),
  231. header.request.path.join(','),
  232. );
  233. }
  234. return new TcpStreamSettings(header.type, '', '');
  235. }
  236. toJson() {
  237. return {
  238. header: {
  239. type: this.type,
  240. request: this.type === 'http' ? {
  241. headers: {
  242. Host: ObjectUtil.isEmpty(this.host) ? [] : this.host.split(',')
  243. },
  244. path: ObjectUtil.isEmpty(this.path) ? ["/"] : this.path.split(',')
  245. } : undefined,
  246. }
  247. };
  248. }
  249. }
  250. class KcpStreamSettings extends CommonClass {
  251. constructor(
  252. mtu = 1350,
  253. tti = 20,
  254. uplinkCapacity = 5,
  255. downlinkCapacity = 20,
  256. cwndMultiplier = 1,
  257. maxSendingWindow = 1350,
  258. ) {
  259. super();
  260. this.mtu = mtu;
  261. this.tti = tti;
  262. this.upCap = uplinkCapacity;
  263. this.downCap = downlinkCapacity;
  264. this.cwndMultiplier = cwndMultiplier;
  265. this.maxSendingWindow = maxSendingWindow;
  266. }
  267. static fromJson(json = {}) {
  268. return new KcpStreamSettings(
  269. json.mtu,
  270. json.tti,
  271. json.uplinkCapacity,
  272. json.downlinkCapacity,
  273. json.cwndMultiplier,
  274. json.maxSendingWindow,
  275. );
  276. }
  277. toJson() {
  278. return {
  279. mtu: this.mtu,
  280. tti: this.tti,
  281. uplinkCapacity: this.upCap,
  282. downlinkCapacity: this.downCap,
  283. cwndMultiplier: this.cwndMultiplier,
  284. maxSendingWindow: this.maxSendingWindow,
  285. };
  286. }
  287. }
  288. class WsStreamSettings extends CommonClass {
  289. constructor(
  290. path = '/',
  291. host = '',
  292. heartbeatPeriod = 0,
  293. ) {
  294. super();
  295. this.path = path;
  296. this.host = host;
  297. this.heartbeatPeriod = heartbeatPeriod;
  298. }
  299. static fromJson(json = {}) {
  300. return new WsStreamSettings(
  301. json.path,
  302. json.host,
  303. json.heartbeatPeriod,
  304. );
  305. }
  306. toJson() {
  307. return {
  308. path: this.path,
  309. host: this.host,
  310. heartbeatPeriod: this.heartbeatPeriod
  311. };
  312. }
  313. }
  314. class GrpcStreamSettings extends CommonClass {
  315. constructor(
  316. serviceName = "",
  317. authority = "",
  318. multiMode = false
  319. ) {
  320. super();
  321. this.serviceName = serviceName;
  322. this.authority = authority;
  323. this.multiMode = multiMode;
  324. }
  325. static fromJson(json = {}) {
  326. return new GrpcStreamSettings(json.serviceName, json.authority, json.multiMode);
  327. }
  328. toJson() {
  329. return {
  330. serviceName: this.serviceName,
  331. authority: this.authority,
  332. multiMode: this.multiMode
  333. }
  334. }
  335. }
  336. class HttpUpgradeStreamSettings extends CommonClass {
  337. constructor(path = '/', host = '') {
  338. super();
  339. this.path = path;
  340. this.host = host;
  341. }
  342. static fromJson(json = {}) {
  343. return new HttpUpgradeStreamSettings(
  344. json.path,
  345. json.host,
  346. );
  347. }
  348. toJson() {
  349. return {
  350. path: this.path,
  351. host: this.host,
  352. };
  353. }
  354. }
  355. class xHTTPStreamSettings extends CommonClass {
  356. constructor(
  357. path = '/',
  358. host = '',
  359. mode = '',
  360. noGRPCHeader = false,
  361. scMinPostsIntervalMs = "30",
  362. xmux = {
  363. maxConcurrency: "16-32",
  364. maxConnections: 0,
  365. cMaxReuseTimes: 0,
  366. hMaxRequestTimes: "600-900",
  367. hMaxReusableSecs: "1800-3000",
  368. hKeepAlivePeriod: 0,
  369. },
  370. ) {
  371. super();
  372. this.path = path;
  373. this.host = host;
  374. this.mode = mode;
  375. this.noGRPCHeader = noGRPCHeader;
  376. this.scMinPostsIntervalMs = scMinPostsIntervalMs;
  377. this.xmux = xmux;
  378. }
  379. static fromJson(json = {}) {
  380. return new xHTTPStreamSettings(
  381. json.path,
  382. json.host,
  383. json.mode,
  384. json.noGRPCHeader,
  385. json.scMinPostsIntervalMs,
  386. json.xmux
  387. );
  388. }
  389. toJson() {
  390. return {
  391. path: this.path,
  392. host: this.host,
  393. mode: this.mode,
  394. noGRPCHeader: this.noGRPCHeader,
  395. scMinPostsIntervalMs: this.scMinPostsIntervalMs,
  396. xmux: {
  397. maxConcurrency: this.xmux.maxConcurrency,
  398. maxConnections: this.xmux.maxConnections,
  399. cMaxReuseTimes: this.xmux.cMaxReuseTimes,
  400. hMaxRequestTimes: this.xmux.hMaxRequestTimes,
  401. hMaxReusableSecs: this.xmux.hMaxReusableSecs,
  402. hKeepAlivePeriod: this.xmux.hKeepAlivePeriod,
  403. },
  404. };
  405. }
  406. }
  407. class TlsStreamSettings extends CommonClass {
  408. constructor(
  409. serverName = '',
  410. alpn = [],
  411. fingerprint = '',
  412. echConfigList = '',
  413. verifyPeerCertByName = '',
  414. pinnedPeerCertSha256 = '',
  415. ) {
  416. super();
  417. this.serverName = serverName;
  418. this.alpn = alpn;
  419. this.fingerprint = fingerprint;
  420. this.echConfigList = echConfigList;
  421. this.verifyPeerCertByName = verifyPeerCertByName;
  422. this.pinnedPeerCertSha256 = pinnedPeerCertSha256;
  423. }
  424. static fromJson(json = {}) {
  425. return new TlsStreamSettings(
  426. json.serverName,
  427. json.alpn,
  428. json.fingerprint,
  429. json.echConfigList,
  430. json.verifyPeerCertByName,
  431. json.pinnedPeerCertSha256,
  432. );
  433. }
  434. toJson() {
  435. return {
  436. serverName: this.serverName,
  437. alpn: this.alpn,
  438. fingerprint: this.fingerprint,
  439. echConfigList: this.echConfigList,
  440. verifyPeerCertByName: this.verifyPeerCertByName,
  441. pinnedPeerCertSha256: this.pinnedPeerCertSha256
  442. };
  443. }
  444. }
  445. class RealityStreamSettings extends CommonClass {
  446. constructor(
  447. publicKey = '',
  448. fingerprint = '',
  449. serverName = '',
  450. shortId = '',
  451. spiderX = '',
  452. mldsa65Verify = ''
  453. ) {
  454. super();
  455. this.publicKey = publicKey;
  456. this.fingerprint = fingerprint;
  457. this.serverName = serverName;
  458. this.shortId = shortId
  459. this.spiderX = spiderX;
  460. this.mldsa65Verify = mldsa65Verify;
  461. }
  462. static fromJson(json = {}) {
  463. return new RealityStreamSettings(
  464. json.publicKey,
  465. json.fingerprint,
  466. json.serverName,
  467. json.shortId,
  468. json.spiderX,
  469. json.mldsa65Verify
  470. );
  471. }
  472. toJson() {
  473. return {
  474. publicKey: this.publicKey,
  475. fingerprint: this.fingerprint,
  476. serverName: this.serverName,
  477. shortId: this.shortId,
  478. spiderX: this.spiderX,
  479. mldsa65Verify: this.mldsa65Verify
  480. };
  481. }
  482. };
  483. class HysteriaStreamSettings extends CommonClass {
  484. constructor(
  485. version = 2,
  486. auth = '',
  487. congestion = '',
  488. up = '0',
  489. down = '0',
  490. udphopPort = '',
  491. udphopIntervalMin = 30,
  492. udphopIntervalMax = 30,
  493. initStreamReceiveWindow = 8388608,
  494. maxStreamReceiveWindow = 8388608,
  495. initConnectionReceiveWindow = 20971520,
  496. maxConnectionReceiveWindow = 20971520,
  497. maxIdleTimeout = 30,
  498. keepAlivePeriod = 2,
  499. disablePathMTUDiscovery = false
  500. ) {
  501. super();
  502. this.version = version;
  503. this.auth = auth;
  504. this.congestion = congestion;
  505. this.up = up;
  506. this.down = down;
  507. this.udphopPort = udphopPort;
  508. this.udphopIntervalMin = udphopIntervalMin;
  509. this.udphopIntervalMax = udphopIntervalMax;
  510. this.initStreamReceiveWindow = initStreamReceiveWindow;
  511. this.maxStreamReceiveWindow = maxStreamReceiveWindow;
  512. this.initConnectionReceiveWindow = initConnectionReceiveWindow;
  513. this.maxConnectionReceiveWindow = maxConnectionReceiveWindow;
  514. this.maxIdleTimeout = maxIdleTimeout;
  515. this.keepAlivePeriod = keepAlivePeriod;
  516. this.disablePathMTUDiscovery = disablePathMTUDiscovery;
  517. }
  518. static fromJson(json = {}) {
  519. let udphopPort = '';
  520. let udphopIntervalMin = 30;
  521. let udphopIntervalMax = 30;
  522. if (json.udphop) {
  523. udphopPort = json.udphop.port || '';
  524. // Backward compatibility: if old 'interval' exists, use it for both min/max
  525. if (json.udphop.interval !== undefined) {
  526. udphopIntervalMin = json.udphop.interval;
  527. udphopIntervalMax = json.udphop.interval;
  528. } else {
  529. udphopIntervalMin = json.udphop.intervalMin || 30;
  530. udphopIntervalMax = json.udphop.intervalMax || 30;
  531. }
  532. }
  533. return new HysteriaStreamSettings(
  534. json.version,
  535. json.auth,
  536. json.congestion,
  537. json.up,
  538. json.down,
  539. udphopPort,
  540. udphopIntervalMin,
  541. udphopIntervalMax,
  542. json.initStreamReceiveWindow,
  543. json.maxStreamReceiveWindow,
  544. json.initConnectionReceiveWindow,
  545. json.maxConnectionReceiveWindow,
  546. json.maxIdleTimeout,
  547. json.keepAlivePeriod,
  548. json.disablePathMTUDiscovery
  549. );
  550. }
  551. toJson() {
  552. const result = {
  553. version: this.version,
  554. auth: this.auth,
  555. congestion: this.congestion,
  556. up: this.up,
  557. down: this.down,
  558. initStreamReceiveWindow: this.initStreamReceiveWindow,
  559. maxStreamReceiveWindow: this.maxStreamReceiveWindow,
  560. initConnectionReceiveWindow: this.initConnectionReceiveWindow,
  561. maxConnectionReceiveWindow: this.maxConnectionReceiveWindow,
  562. maxIdleTimeout: this.maxIdleTimeout,
  563. keepAlivePeriod: this.keepAlivePeriod,
  564. disablePathMTUDiscovery: this.disablePathMTUDiscovery
  565. };
  566. if (this.udphopPort) {
  567. result.udphop = {
  568. port: this.udphopPort,
  569. intervalMin: this.udphopIntervalMin,
  570. intervalMax: this.udphopIntervalMax
  571. };
  572. }
  573. return result;
  574. }
  575. };
  576. class SockoptStreamSettings extends CommonClass {
  577. constructor(
  578. dialerProxy = "",
  579. tcpFastOpen = false,
  580. tcpKeepAliveInterval = 0,
  581. tcpMptcp = false,
  582. penetrate = false,
  583. addressPortStrategy = Address_Port_Strategy.NONE,
  584. trustedXForwardedFor = [],
  585. ) {
  586. super();
  587. this.dialerProxy = dialerProxy;
  588. this.tcpFastOpen = tcpFastOpen;
  589. this.tcpKeepAliveInterval = tcpKeepAliveInterval;
  590. this.tcpMptcp = tcpMptcp;
  591. this.penetrate = penetrate;
  592. this.addressPortStrategy = addressPortStrategy;
  593. this.trustedXForwardedFor = trustedXForwardedFor;
  594. }
  595. static fromJson(json = {}) {
  596. if (Object.keys(json).length === 0) return undefined;
  597. return new SockoptStreamSettings(
  598. json.dialerProxy,
  599. json.tcpFastOpen,
  600. json.tcpKeepAliveInterval,
  601. json.tcpMptcp,
  602. json.penetrate,
  603. json.addressPortStrategy,
  604. json.trustedXForwardedFor || []
  605. );
  606. }
  607. toJson() {
  608. const result = {
  609. dialerProxy: this.dialerProxy,
  610. tcpFastOpen: this.tcpFastOpen,
  611. tcpKeepAliveInterval: this.tcpKeepAliveInterval,
  612. tcpMptcp: this.tcpMptcp,
  613. penetrate: this.penetrate,
  614. addressPortStrategy: this.addressPortStrategy
  615. };
  616. if (this.trustedXForwardedFor && this.trustedXForwardedFor.length > 0) {
  617. result.trustedXForwardedFor = this.trustedXForwardedFor;
  618. }
  619. return result;
  620. }
  621. }
  622. class UdpMask extends CommonClass {
  623. constructor(type = 'salamander', settings = {}) {
  624. super();
  625. this.type = type;
  626. this.settings = this._getDefaultSettings(type, settings);
  627. }
  628. _getDefaultSettings(type, settings = {}) {
  629. switch (type) {
  630. case 'salamander':
  631. case 'mkcp-aes128gcm':
  632. return { password: settings.password || '' };
  633. case 'header-dns':
  634. return { domain: settings.domain || '' };
  635. case 'xdns':
  636. return { resolvers: Array.isArray(settings.resolvers) ? settings.resolvers : [] };
  637. case 'xicmp':
  638. return { ip: settings.ip || '', id: settings.id ?? 0 };
  639. case 'mkcp-original':
  640. case 'header-dtls':
  641. case 'header-srtp':
  642. case 'header-utp':
  643. case 'header-wechat':
  644. case 'header-wireguard':
  645. return {}; // No settings needed
  646. case 'header-custom':
  647. return {
  648. client: Array.isArray(settings.client) ? settings.client : [],
  649. server: Array.isArray(settings.server) ? settings.server : [],
  650. };
  651. case 'noise':
  652. return {
  653. reset: settings.reset ?? 0,
  654. noise: Array.isArray(settings.noise) ? settings.noise : [],
  655. };
  656. case 'sudoku':
  657. return {
  658. ascii: settings.ascii || '',
  659. customTable: settings.customTable || '',
  660. customTables: Array.isArray(settings.customTables) ? settings.customTables : [],
  661. paddingMin: settings.paddingMin ?? 0,
  662. paddingMax: settings.paddingMax ?? 0
  663. };
  664. default:
  665. return settings;
  666. }
  667. }
  668. static fromJson(json = {}) {
  669. return new UdpMask(
  670. json.type || 'salamander',
  671. json.settings || {}
  672. );
  673. }
  674. toJson() {
  675. const cleanItem = item => {
  676. const out = { ...item };
  677. if (out.type === 'array') {
  678. delete out.packet;
  679. } else {
  680. delete out.rand;
  681. delete out.randRange;
  682. }
  683. return out;
  684. };
  685. let settings = this.settings;
  686. if (this.type === 'noise' && settings && Array.isArray(settings.noise)) {
  687. settings = { ...settings, noise: settings.noise.map(cleanItem) };
  688. } else if (this.type === 'header-custom' && settings) {
  689. settings = {
  690. ...settings,
  691. client: Array.isArray(settings.client) ? settings.client.map(cleanItem) : settings.client,
  692. server: Array.isArray(settings.server) ? settings.server.map(cleanItem) : settings.server,
  693. };
  694. }
  695. return {
  696. type: this.type,
  697. settings: (settings && Object.keys(settings).length > 0) ? settings : undefined
  698. };
  699. }
  700. }
  701. class TcpMask extends CommonClass {
  702. constructor(type = 'fragment', settings = {}) {
  703. super();
  704. this.type = type;
  705. this.settings = this._getDefaultSettings(type, settings);
  706. }
  707. _getDefaultSettings(type, settings = {}) {
  708. switch (type) {
  709. case 'fragment':
  710. return {
  711. packets: settings.packets ?? 'tlshello',
  712. length: settings.length ?? '',
  713. delay: settings.delay ?? '',
  714. maxSplit: settings.maxSplit ?? '',
  715. };
  716. case 'sudoku':
  717. return {
  718. password: settings.password ?? '',
  719. ascii: settings.ascii ?? '',
  720. customTable: settings.customTable ?? '',
  721. customTables: Array.isArray(settings.customTables) ? settings.customTables : [],
  722. paddingMin: settings.paddingMin ?? 0,
  723. paddingMax: settings.paddingMax ?? 0,
  724. };
  725. case 'header-custom':
  726. return {
  727. clients: Array.isArray(settings.clients) ? settings.clients : [],
  728. servers: Array.isArray(settings.servers) ? settings.servers : [],
  729. };
  730. default:
  731. return settings;
  732. }
  733. }
  734. static fromJson(json = {}) {
  735. return new TcpMask(
  736. json.type || 'fragment',
  737. json.settings || {}
  738. );
  739. }
  740. toJson() {
  741. const cleanItem = item => {
  742. const out = { ...item };
  743. if (out.type === 'array') {
  744. delete out.packet;
  745. } else {
  746. delete out.rand;
  747. delete out.randRange;
  748. }
  749. return out;
  750. };
  751. let settings = this.settings;
  752. if (this.type === 'header-custom' && settings) {
  753. const cleanGroup = group => Array.isArray(group) ? group.map(cleanItem) : group;
  754. settings = {
  755. ...settings,
  756. clients: Array.isArray(settings.clients) ? settings.clients.map(cleanGroup) : settings.clients,
  757. servers: Array.isArray(settings.servers) ? settings.servers.map(cleanGroup) : settings.servers,
  758. };
  759. }
  760. return {
  761. type: this.type,
  762. settings: (settings && Object.keys(settings).length > 0) ? settings : undefined
  763. };
  764. }
  765. }
  766. class QuicParams extends CommonClass {
  767. constructor(
  768. congestion = 'bbr',
  769. debug = false,
  770. brutalUp = 65537,
  771. brutalDown = 65537,
  772. udpHop = undefined,
  773. initStreamReceiveWindow = 8388608,
  774. maxStreamReceiveWindow = 8388608,
  775. initConnectionReceiveWindow = 20971520,
  776. maxConnectionReceiveWindow = 20971520,
  777. maxIdleTimeout = 30,
  778. keepAlivePeriod = 5,
  779. disablePathMTUDiscovery = false,
  780. maxIncomingStreams = 1024,
  781. ) {
  782. super();
  783. this.congestion = congestion;
  784. this.debug = debug;
  785. this.brutalUp = brutalUp;
  786. this.brutalDown = brutalDown;
  787. this.udpHop = udpHop;
  788. this.initStreamReceiveWindow = initStreamReceiveWindow;
  789. this.maxStreamReceiveWindow = maxStreamReceiveWindow;
  790. this.initConnectionReceiveWindow = initConnectionReceiveWindow;
  791. this.maxConnectionReceiveWindow = maxConnectionReceiveWindow;
  792. this.maxIdleTimeout = maxIdleTimeout;
  793. this.keepAlivePeriod = keepAlivePeriod;
  794. this.disablePathMTUDiscovery = disablePathMTUDiscovery;
  795. this.maxIncomingStreams = maxIncomingStreams;
  796. }
  797. get hasUdpHop() {
  798. return this.udpHop != null;
  799. }
  800. set hasUdpHop(value) {
  801. this.udpHop = value ? (this.udpHop || { ports: '20000-50000', interval: '5-10' }) : undefined;
  802. }
  803. static fromJson(json = {}) {
  804. if (!json || Object.keys(json).length === 0) return undefined;
  805. return new QuicParams(
  806. json.congestion,
  807. json.debug,
  808. json.brutalUp,
  809. json.brutalDown,
  810. json.udpHop ? { ports: json.udpHop.ports, interval: json.udpHop.interval } : undefined,
  811. json.initStreamReceiveWindow,
  812. json.maxStreamReceiveWindow,
  813. json.initConnectionReceiveWindow,
  814. json.maxConnectionReceiveWindow,
  815. json.maxIdleTimeout,
  816. json.keepAlivePeriod,
  817. json.disablePathMTUDiscovery,
  818. json.maxIncomingStreams,
  819. );
  820. }
  821. toJson() {
  822. const result = { congestion: this.congestion };
  823. if (this.debug) result.debug = this.debug;
  824. if (['brutal', 'force-brutal'].includes(this.congestion)) {
  825. if (this.brutalUp) result.brutalUp = this.brutalUp;
  826. if (this.brutalDown) result.brutalDown = this.brutalDown;
  827. }
  828. if (this.udpHop) result.udpHop = { ports: this.udpHop.ports, interval: this.udpHop.interval };
  829. if (this.initStreamReceiveWindow > 0) result.initStreamReceiveWindow = this.initStreamReceiveWindow;
  830. if (this.maxStreamReceiveWindow > 0) result.maxStreamReceiveWindow = this.maxStreamReceiveWindow;
  831. if (this.initConnectionReceiveWindow > 0) result.initConnectionReceiveWindow = this.initConnectionReceiveWindow;
  832. if (this.maxConnectionReceiveWindow > 0) result.maxConnectionReceiveWindow = this.maxConnectionReceiveWindow;
  833. if (this.maxIdleTimeout !== 30 && this.maxIdleTimeout > 0) result.maxIdleTimeout = this.maxIdleTimeout;
  834. if (this.keepAlivePeriod > 0) result.keepAlivePeriod = this.keepAlivePeriod;
  835. if (this.disablePathMTUDiscovery) result.disablePathMTUDiscovery = this.disablePathMTUDiscovery;
  836. if (this.maxIncomingStreams > 0) result.maxIncomingStreams = this.maxIncomingStreams;
  837. return result;
  838. }
  839. }
  840. class FinalMaskStreamSettings extends CommonClass {
  841. constructor(tcp = [], udp = [], quicParams = undefined) {
  842. super();
  843. this.tcp = Array.isArray(tcp) ? tcp.map(t => t instanceof TcpMask ? t : new TcpMask(t.type, t.settings)) : [];
  844. this.udp = Array.isArray(udp) ? udp.map(u => new UdpMask(u.type, u.settings)) : [new UdpMask(udp.type, udp.settings)];
  845. this.quicParams = quicParams instanceof QuicParams ? quicParams : (quicParams ? QuicParams.fromJson(quicParams) : undefined);
  846. }
  847. get enableQuicParams() {
  848. return this.quicParams != null;
  849. }
  850. set enableQuicParams(value) {
  851. this.quicParams = value ? (this.quicParams || new QuicParams()) : undefined;
  852. }
  853. static fromJson(json = {}) {
  854. return new FinalMaskStreamSettings(
  855. json.tcp || [],
  856. json.udp || [],
  857. json.quicParams ? QuicParams.fromJson(json.quicParams) : undefined,
  858. );
  859. }
  860. toJson() {
  861. const result = {};
  862. if (this.tcp && this.tcp.length > 0) {
  863. result.tcp = this.tcp.map(t => t.toJson());
  864. }
  865. if (this.udp && this.udp.length > 0) {
  866. result.udp = this.udp.map(udp => udp.toJson());
  867. }
  868. if (this.quicParams) {
  869. result.quicParams = this.quicParams.toJson();
  870. }
  871. return result;
  872. }
  873. }
  874. class StreamSettings extends CommonClass {
  875. constructor(
  876. network = 'tcp',
  877. security = 'none',
  878. tlsSettings = new TlsStreamSettings(),
  879. realitySettings = new RealityStreamSettings(),
  880. tcpSettings = new TcpStreamSettings(),
  881. kcpSettings = new KcpStreamSettings(),
  882. wsSettings = new WsStreamSettings(),
  883. grpcSettings = new GrpcStreamSettings(),
  884. httpupgradeSettings = new HttpUpgradeStreamSettings(),
  885. xhttpSettings = new xHTTPStreamSettings(),
  886. hysteriaSettings = new HysteriaStreamSettings(),
  887. finalmask = new FinalMaskStreamSettings(),
  888. sockopt = undefined,
  889. ) {
  890. super();
  891. this.network = network;
  892. this.security = security;
  893. this.tls = tlsSettings;
  894. this.reality = realitySettings;
  895. this.tcp = tcpSettings;
  896. this.kcp = kcpSettings;
  897. this.ws = wsSettings;
  898. this.grpc = grpcSettings;
  899. this.httpupgrade = httpupgradeSettings;
  900. this.xhttp = xhttpSettings;
  901. this.hysteria = hysteriaSettings;
  902. this.finalmask = finalmask;
  903. this.sockopt = sockopt;
  904. }
  905. addTcpMask(type = 'fragment') {
  906. this.finalmask.tcp.push(new TcpMask(type));
  907. }
  908. delTcpMask(index) {
  909. if (this.finalmask.tcp) {
  910. this.finalmask.tcp.splice(index, 1);
  911. }
  912. }
  913. addUdpMask(type = 'salamander') {
  914. this.finalmask.udp.push(new UdpMask(type));
  915. }
  916. delUdpMask(index) {
  917. if (this.finalmask.udp) {
  918. this.finalmask.udp.splice(index, 1);
  919. }
  920. }
  921. get hasFinalMask() {
  922. const hasTcp = this.finalmask.tcp && this.finalmask.tcp.length > 0;
  923. const hasUdp = this.finalmask.udp && this.finalmask.udp.length > 0;
  924. const hasQuicParams = this.finalmask.quicParams != null;
  925. return hasTcp || hasUdp || hasQuicParams;
  926. }
  927. get isTls() {
  928. return this.security === 'tls';
  929. }
  930. get isReality() {
  931. return this.security === "reality";
  932. }
  933. get sockoptSwitch() {
  934. return this.sockopt != undefined;
  935. }
  936. set sockoptSwitch(value) {
  937. this.sockopt = value ? new SockoptStreamSettings() : undefined;
  938. }
  939. static fromJson(json = {}) {
  940. return new StreamSettings(
  941. json.network,
  942. json.security,
  943. TlsStreamSettings.fromJson(json.tlsSettings),
  944. RealityStreamSettings.fromJson(json.realitySettings),
  945. TcpStreamSettings.fromJson(json.tcpSettings),
  946. KcpStreamSettings.fromJson(json.kcpSettings),
  947. WsStreamSettings.fromJson(json.wsSettings),
  948. GrpcStreamSettings.fromJson(json.grpcSettings),
  949. HttpUpgradeStreamSettings.fromJson(json.httpupgradeSettings),
  950. xHTTPStreamSettings.fromJson(json.xhttpSettings),
  951. HysteriaStreamSettings.fromJson(json.hysteriaSettings),
  952. FinalMaskStreamSettings.fromJson(json.finalmask),
  953. SockoptStreamSettings.fromJson(json.sockopt),
  954. );
  955. }
  956. toJson() {
  957. const network = this.network;
  958. return {
  959. network: network,
  960. security: this.security,
  961. tlsSettings: this.security == 'tls' ? this.tls.toJson() : undefined,
  962. realitySettings: this.security == 'reality' ? this.reality.toJson() : undefined,
  963. tcpSettings: network === 'tcp' ? this.tcp.toJson() : undefined,
  964. kcpSettings: network === 'kcp' ? this.kcp.toJson() : undefined,
  965. wsSettings: network === 'ws' ? this.ws.toJson() : undefined,
  966. grpcSettings: network === 'grpc' ? this.grpc.toJson() : undefined,
  967. httpupgradeSettings: network === 'httpupgrade' ? this.httpupgrade.toJson() : undefined,
  968. xhttpSettings: network === 'xhttp' ? this.xhttp.toJson() : undefined,
  969. hysteriaSettings: network === 'hysteria' ? this.hysteria.toJson() : undefined,
  970. finalmask: this.hasFinalMask ? this.finalmask.toJson() : undefined,
  971. sockopt: this.sockopt != undefined ? this.sockopt.toJson() : undefined,
  972. };
  973. }
  974. }
  975. class Mux extends CommonClass {
  976. constructor(enabled = false, concurrency = 8, xudpConcurrency = 16, xudpProxyUDP443 = "reject") {
  977. super();
  978. this.enabled = enabled;
  979. this.concurrency = concurrency;
  980. this.xudpConcurrency = xudpConcurrency;
  981. this.xudpProxyUDP443 = xudpProxyUDP443;
  982. }
  983. static fromJson(json = {}) {
  984. if (Object.keys(json).length === 0) return undefined;
  985. return new Mux(
  986. json.enabled,
  987. json.concurrency,
  988. json.xudpConcurrency,
  989. json.xudpProxyUDP443,
  990. );
  991. }
  992. toJson() {
  993. return {
  994. enabled: this.enabled,
  995. concurrency: this.concurrency,
  996. xudpConcurrency: this.xudpConcurrency,
  997. xudpProxyUDP443: this.xudpProxyUDP443,
  998. };
  999. }
  1000. }
  1001. class Outbound extends CommonClass {
  1002. constructor(
  1003. tag = '',
  1004. protocol = Protocols.VLESS,
  1005. settings = null,
  1006. streamSettings = new StreamSettings(),
  1007. sendThrough,
  1008. mux = new Mux(),
  1009. ) {
  1010. super();
  1011. this.tag = tag;
  1012. this._protocol = protocol;
  1013. this.settings = settings == null ? Outbound.Settings.getSettings(protocol) : settings;
  1014. this.stream = streamSettings;
  1015. this.sendThrough = sendThrough;
  1016. this.mux = mux;
  1017. }
  1018. get protocol() {
  1019. return this._protocol;
  1020. }
  1021. set protocol(protocol) {
  1022. this._protocol = protocol;
  1023. this.settings = Outbound.Settings.getSettings(protocol);
  1024. this.stream = new StreamSettings();
  1025. }
  1026. canEnableTls() {
  1027. if (this.protocol === Protocols.Hysteria) return true;
  1028. if (![Protocols.VMess, Protocols.VLESS, Protocols.Trojan, Protocols.Shadowsocks].includes(this.protocol)) return false;
  1029. return ["tcp", "ws", "http", "grpc", "httpupgrade", "xhttp"].includes(this.stream.network);
  1030. }
  1031. //this is used for xtls-rprx-vision
  1032. canEnableTlsFlow() {
  1033. if ((this.stream.security != 'none') && (this.stream.network === "tcp")) {
  1034. return this.protocol === Protocols.VLESS;
  1035. }
  1036. return false;
  1037. }
  1038. // Vision seed applies only when vision flow is selected
  1039. canEnableVisionSeed() {
  1040. if (!this.canEnableTlsFlow()) return false;
  1041. const flow = this.settings?.flow;
  1042. return flow === TLS_FLOW_CONTROL.VISION || flow === TLS_FLOW_CONTROL.VISION_UDP443;
  1043. }
  1044. canEnableReality() {
  1045. if (![Protocols.VLESS, Protocols.Trojan].includes(this.protocol)) return false;
  1046. return ["tcp", "http", "grpc", "xhttp"].includes(this.stream.network);
  1047. }
  1048. canEnableStream() {
  1049. return [Protocols.VMess, Protocols.VLESS, Protocols.Trojan, Protocols.Shadowsocks, Protocols.Hysteria].includes(this.protocol);
  1050. }
  1051. canEnableMux() {
  1052. // Disable Mux if flow is set
  1053. if (this.settings.flow && this.settings.flow !== '') {
  1054. this.mux.enabled = false;
  1055. return false;
  1056. }
  1057. // Disable Mux if network is xhttp
  1058. if (this.stream.network === 'xhttp') {
  1059. this.mux.enabled = false;
  1060. return false;
  1061. }
  1062. // Allow Mux only for these protocols
  1063. return [
  1064. Protocols.VMess,
  1065. Protocols.VLESS,
  1066. Protocols.Trojan,
  1067. Protocols.Shadowsocks,
  1068. Protocols.HTTP,
  1069. Protocols.Socks
  1070. ].includes(this.protocol);
  1071. }
  1072. hasServers() {
  1073. return [Protocols.Trojan, Protocols.Shadowsocks, Protocols.Socks, Protocols.HTTP].includes(this.protocol);
  1074. }
  1075. hasAddressPort() {
  1076. return [
  1077. Protocols.DNS,
  1078. Protocols.VMess,
  1079. Protocols.VLESS,
  1080. Protocols.Trojan,
  1081. Protocols.Shadowsocks,
  1082. Protocols.Socks,
  1083. Protocols.HTTP,
  1084. Protocols.Hysteria
  1085. ].includes(this.protocol);
  1086. }
  1087. hasUsername() {
  1088. return [Protocols.Socks, Protocols.HTTP].includes(this.protocol);
  1089. }
  1090. static fromJson(json = {}) {
  1091. return new Outbound(
  1092. json.tag,
  1093. json.protocol,
  1094. Outbound.Settings.fromJson(json.protocol, json.settings),
  1095. StreamSettings.fromJson(json.streamSettings),
  1096. json.sendThrough,
  1097. Mux.fromJson(json.mux),
  1098. )
  1099. }
  1100. toJson() {
  1101. var stream;
  1102. if (this.canEnableStream()) {
  1103. stream = this.stream.toJson();
  1104. } else {
  1105. if (this.stream?.sockopt)
  1106. stream = { sockopt: this.stream.sockopt.toJson() };
  1107. }
  1108. let settingsOut = this.settings instanceof CommonClass ? this.settings.toJson() : this.settings;
  1109. return {
  1110. protocol: this.protocol,
  1111. settings: settingsOut,
  1112. // Only include tag, streamSettings, sendThrough, mux if present and not empty
  1113. ...(this.tag ? { tag: this.tag } : {}),
  1114. ...(stream ? { streamSettings: stream } : {}),
  1115. ...(this.sendThrough ? { sendThrough: this.sendThrough } : {}),
  1116. ...(this.mux?.enabled ? { mux: this.mux } : {}),
  1117. };
  1118. }
  1119. static fromLink(link) {
  1120. data = link.split('://');
  1121. if (data.length != 2) return null;
  1122. switch (data[0].toLowerCase()) {
  1123. case Protocols.VMess:
  1124. return this.fromVmessLink(JSON.parse(Base64.decode(data[1])));
  1125. case Protocols.VLESS:
  1126. case Protocols.Trojan:
  1127. case 'ss':
  1128. return this.fromParamLink(link);
  1129. case 'hysteria2':
  1130. case Protocols.Hysteria:
  1131. return this.fromHysteriaLink(link);
  1132. default:
  1133. return null;
  1134. }
  1135. }
  1136. static fromVmessLink(json = {}) {
  1137. let stream = new StreamSettings(json.net, json.tls);
  1138. let network = json.net;
  1139. if (network === 'tcp') {
  1140. stream.tcp = new TcpStreamSettings(
  1141. json.type,
  1142. json.host ?? '',
  1143. json.path ?? '');
  1144. } else if (network === 'kcp') {
  1145. stream.kcp = new KcpStreamSettings();
  1146. stream.type = json.type;
  1147. stream.seed = json.path;
  1148. const mtu = Number(json.mtu);
  1149. if (Number.isFinite(mtu) && mtu > 0) stream.kcp.mtu = mtu;
  1150. const tti = Number(json.tti);
  1151. if (Number.isFinite(tti) && tti > 0) stream.kcp.tti = tti;
  1152. } else if (network === 'ws') {
  1153. stream.ws = new WsStreamSettings(json.path, json.host);
  1154. } else if (network === 'grpc') {
  1155. stream.grpc = new GrpcStreamSettings(json.path, json.authority, json.type == 'multi');
  1156. } else if (network === 'httpupgrade') {
  1157. stream.httpupgrade = new HttpUpgradeStreamSettings(json.path, json.host);
  1158. } else if (network === 'xhttp') {
  1159. // xHTTPStreamSettings positional args are (path, host, headers, ..., mode);
  1160. // passing `json.mode` as the 3rd argument used to land in the `headers`
  1161. // slot, dropping the mode on the floor. Build the object and set mode
  1162. // explicitly to avoid that.
  1163. const xh = new xHTTPStreamSettings(json.path, json.host);
  1164. if (json.mode) xh.mode = json.mode;
  1165. stream.xhttp = xh;
  1166. }
  1167. if (json.tls && json.tls == 'tls') {
  1168. stream.tls = new TlsStreamSettings(
  1169. json.sni,
  1170. json.alpn ? json.alpn.split(',') : [],
  1171. json.fp);
  1172. }
  1173. const port = json.port * 1;
  1174. return new Outbound(json.ps, Protocols.VMess, new Outbound.VmessSettings(json.add, port, json.id, json.scy), stream);
  1175. }
  1176. static fromParamLink(link) {
  1177. const url = new URL(link);
  1178. let type = url.searchParams.get('type') ?? 'tcp';
  1179. let security = url.searchParams.get('security') ?? 'none';
  1180. let stream = new StreamSettings(type, security);
  1181. let headerType = url.searchParams.get('headerType') ?? undefined;
  1182. let host = url.searchParams.get('host') ?? undefined;
  1183. let path = url.searchParams.get('path') ?? undefined;
  1184. let seed = url.searchParams.get('seed') ?? path ?? undefined;
  1185. let mode = url.searchParams.get('mode') ?? undefined;
  1186. if (type === 'tcp' || type === 'none') {
  1187. stream.tcp = new TcpStreamSettings(headerType ?? 'none', host, path);
  1188. } else if (type === 'kcp') {
  1189. stream.kcp = new KcpStreamSettings();
  1190. stream.kcp.type = headerType ?? 'none';
  1191. stream.kcp.seed = seed;
  1192. const mtu = Number(url.searchParams.get('mtu'));
  1193. if (Number.isFinite(mtu) && mtu > 0) stream.kcp.mtu = mtu;
  1194. const tti = Number(url.searchParams.get('tti'));
  1195. if (Number.isFinite(tti) && tti > 0) stream.kcp.tti = tti;
  1196. } else if (type === 'ws') {
  1197. stream.ws = new WsStreamSettings(path, host);
  1198. } else if (type === 'grpc') {
  1199. stream.grpc = new GrpcStreamSettings(
  1200. url.searchParams.get('serviceName') ?? '',
  1201. url.searchParams.get('authority') ?? '',
  1202. url.searchParams.get('mode') == 'multi');
  1203. } else if (type === 'httpupgrade') {
  1204. stream.httpupgrade = new HttpUpgradeStreamSettings(path, host);
  1205. } else if (type === 'xhttp') {
  1206. // Same positional bug as in the VMess-JSON branch above:
  1207. // passing `mode` as the 3rd positional arg put it into the
  1208. // `headers` slot. Build explicitly instead.
  1209. const xh = new xHTTPStreamSettings(path, host);
  1210. if (mode) xh.mode = mode;
  1211. const xpb = url.searchParams.get('x_padding_bytes');
  1212. if (xpb) xh.xPaddingBytes = xpb;
  1213. const extraRaw = url.searchParams.get('extra');
  1214. if (extraRaw) {
  1215. try {
  1216. const extra = JSON.parse(extraRaw);
  1217. if (typeof extra.xPaddingBytes === 'string' && extra.xPaddingBytes) xh.xPaddingBytes = extra.xPaddingBytes;
  1218. if (extra.xPaddingObfsMode === true) xh.xPaddingObfsMode = true;
  1219. ["xPaddingKey", "xPaddingHeader", "xPaddingPlacement", "xPaddingMethod"].forEach(k => {
  1220. if (typeof extra[k] === 'string' && extra[k]) xh[k] = extra[k];
  1221. });
  1222. } catch (_) { /* ignore malformed extra */ }
  1223. }
  1224. stream.xhttp = xh;
  1225. }
  1226. if (security == 'tls') {
  1227. let fp = url.searchParams.get('fp') ?? 'none';
  1228. let alpn = url.searchParams.get('alpn');
  1229. let sni = url.searchParams.get('sni') ?? '';
  1230. let ech = url.searchParams.get('ech') ?? '';
  1231. stream.tls = new TlsStreamSettings(sni, alpn ? alpn.split(',') : [], fp, ech);
  1232. }
  1233. if (security == 'reality') {
  1234. let pbk = url.searchParams.get('pbk');
  1235. let fp = url.searchParams.get('fp');
  1236. let sni = url.searchParams.get('sni') ?? '';
  1237. let sid = url.searchParams.get('sid') ?? '';
  1238. let spx = url.searchParams.get('spx') ?? '';
  1239. let pqv = url.searchParams.get('pqv') ?? '';
  1240. stream.reality = new RealityStreamSettings(pbk, fp, sni, sid, spx, pqv);
  1241. }
  1242. const regex = /([^@]+):\/\/([^@]+)@(.+):(\d+)(.*)$/;
  1243. const match = link.match(regex);
  1244. if (!match) return null;
  1245. let [, protocol, userData, address, port,] = match;
  1246. port *= 1;
  1247. if (protocol == 'ss') {
  1248. protocol = 'shadowsocks';
  1249. userData = atob(userData).split(':');
  1250. }
  1251. var settings;
  1252. switch (protocol) {
  1253. case Protocols.VLESS:
  1254. settings = new Outbound.VLESSSettings(address, port, userData, url.searchParams.get('flow') ?? '', url.searchParams.get('encryption') ?? 'none');
  1255. break;
  1256. case Protocols.Trojan:
  1257. settings = new Outbound.TrojanSettings(address, port, userData);
  1258. break;
  1259. case Protocols.Shadowsocks:
  1260. let method = userData.splice(0, 1)[0];
  1261. settings = new Outbound.ShadowsocksSettings(address, port, userData.join(":"), method, true);
  1262. break;
  1263. default:
  1264. return null;
  1265. }
  1266. let remark = decodeURIComponent(url.hash);
  1267. // Remove '#' from url.hash
  1268. remark = remark.length > 0 ? remark.substring(1) : 'out-' + protocol + '-' + port;
  1269. return new Outbound(remark, protocol, settings, stream);
  1270. }
  1271. static fromHysteriaLink(link) {
  1272. // Parse hysteria2://password@address:port[?param1=value1&param2=value2...][#remarks]
  1273. const regex = /^hysteria2?:\/\/([^@]+)@([^:?#]+):(\d+)([^#]*)(#.*)?$/;
  1274. const match = link.match(regex);
  1275. if (!match) return null;
  1276. let [, password, address, port, params, hash] = match;
  1277. port = parseInt(port);
  1278. // Parse URL parameters if present
  1279. let urlParams = new URLSearchParams(params);
  1280. // Create stream settings with hysteria network
  1281. let stream = new StreamSettings('hysteria', 'none');
  1282. // Set hysteria stream settings
  1283. stream.hysteria.auth = password;
  1284. stream.hysteria.congestion = urlParams.get('congestion') ?? '';
  1285. stream.hysteria.up = urlParams.get('up') ?? '0';
  1286. stream.hysteria.down = urlParams.get('down') ?? '0';
  1287. stream.hysteria.udphopPort = urlParams.get('udphopPort') ?? '';
  1288. // Support both old single interval and new min/max range
  1289. if (urlParams.has('udphopInterval')) {
  1290. const interval = parseInt(urlParams.get('udphopInterval'));
  1291. stream.hysteria.udphopIntervalMin = interval;
  1292. stream.hysteria.udphopIntervalMax = interval;
  1293. } else {
  1294. stream.hysteria.udphopIntervalMin = parseInt(urlParams.get('udphopIntervalMin') ?? '30');
  1295. stream.hysteria.udphopIntervalMax = parseInt(urlParams.get('udphopIntervalMax') ?? '30');
  1296. }
  1297. // Optional QUIC parameters
  1298. if (urlParams.has('initStreamReceiveWindow')) {
  1299. stream.hysteria.initStreamReceiveWindow = parseInt(urlParams.get('initStreamReceiveWindow'));
  1300. }
  1301. if (urlParams.has('maxStreamReceiveWindow')) {
  1302. stream.hysteria.maxStreamReceiveWindow = parseInt(urlParams.get('maxStreamReceiveWindow'));
  1303. }
  1304. if (urlParams.has('initConnectionReceiveWindow')) {
  1305. stream.hysteria.initConnectionReceiveWindow = parseInt(urlParams.get('initConnectionReceiveWindow'));
  1306. }
  1307. if (urlParams.has('maxConnectionReceiveWindow')) {
  1308. stream.hysteria.maxConnectionReceiveWindow = parseInt(urlParams.get('maxConnectionReceiveWindow'));
  1309. }
  1310. if (urlParams.has('maxIdleTimeout')) {
  1311. stream.hysteria.maxIdleTimeout = parseInt(urlParams.get('maxIdleTimeout'));
  1312. }
  1313. if (urlParams.has('keepAlivePeriod')) {
  1314. stream.hysteria.keepAlivePeriod = parseInt(urlParams.get('keepAlivePeriod'));
  1315. }
  1316. if (urlParams.has('disablePathMTUDiscovery')) {
  1317. stream.hysteria.disablePathMTUDiscovery = urlParams.get('disablePathMTUDiscovery') === 'true';
  1318. }
  1319. // Create settings
  1320. let settings = new Outbound.HysteriaSettings(address, port, 2);
  1321. // Extract remark from hash
  1322. let remark = hash ? decodeURIComponent(hash.substring(1)) : `out-hysteria-${port}`;
  1323. return new Outbound(remark, Protocols.Hysteria, settings, stream);
  1324. }
  1325. }
  1326. Outbound.Settings = class extends CommonClass {
  1327. constructor(protocol) {
  1328. super();
  1329. this.protocol = protocol;
  1330. }
  1331. static getSettings(protocol) {
  1332. switch (protocol) {
  1333. case Protocols.Freedom: return new Outbound.FreedomSettings();
  1334. case Protocols.Blackhole: return new Outbound.BlackholeSettings();
  1335. case Protocols.DNS: return new Outbound.DNSSettings();
  1336. case Protocols.VMess: return new Outbound.VmessSettings();
  1337. case Protocols.VLESS: return new Outbound.VLESSSettings();
  1338. case Protocols.Trojan: return new Outbound.TrojanSettings();
  1339. case Protocols.Shadowsocks: return new Outbound.ShadowsocksSettings();
  1340. case Protocols.Socks: return new Outbound.SocksSettings();
  1341. case Protocols.HTTP: return new Outbound.HttpSettings();
  1342. case Protocols.Wireguard: return new Outbound.WireguardSettings();
  1343. case Protocols.Hysteria: return new Outbound.HysteriaSettings();
  1344. default: return null;
  1345. }
  1346. }
  1347. static fromJson(protocol, json) {
  1348. switch (protocol) {
  1349. case Protocols.Freedom: return Outbound.FreedomSettings.fromJson(json);
  1350. case Protocols.Blackhole: return Outbound.BlackholeSettings.fromJson(json);
  1351. case Protocols.DNS: return Outbound.DNSSettings.fromJson(json);
  1352. case Protocols.VMess: return Outbound.VmessSettings.fromJson(json);
  1353. case Protocols.VLESS: return Outbound.VLESSSettings.fromJson(json);
  1354. case Protocols.Trojan: return Outbound.TrojanSettings.fromJson(json);
  1355. case Protocols.Shadowsocks: return Outbound.ShadowsocksSettings.fromJson(json);
  1356. case Protocols.Socks: return Outbound.SocksSettings.fromJson(json);
  1357. case Protocols.HTTP: return Outbound.HttpSettings.fromJson(json);
  1358. case Protocols.Wireguard: return Outbound.WireguardSettings.fromJson(json);
  1359. case Protocols.Hysteria: return Outbound.HysteriaSettings.fromJson(json);
  1360. default: return null;
  1361. }
  1362. }
  1363. toJson() {
  1364. return {};
  1365. }
  1366. };
  1367. Outbound.FreedomSettings = class extends CommonClass {
  1368. constructor(
  1369. domainStrategy = '',
  1370. redirect = '',
  1371. fragment = {},
  1372. noises = [],
  1373. finalRules = [],
  1374. ) {
  1375. super();
  1376. this.domainStrategy = domainStrategy;
  1377. this.redirect = redirect;
  1378. this.fragment = fragment || {};
  1379. this.noises = Array.isArray(noises) ? noises : [];
  1380. this.finalRules = Array.isArray(finalRules)
  1381. ? finalRules.map(rule => rule instanceof Outbound.FreedomSettings.FinalRule ? rule : Outbound.FreedomSettings.FinalRule.fromJson(rule))
  1382. : [];
  1383. }
  1384. addNoise() {
  1385. this.noises.push(new Outbound.FreedomSettings.Noise());
  1386. }
  1387. delNoise(index) {
  1388. this.noises.splice(index, 1);
  1389. }
  1390. addFinalRule(action = 'block') {
  1391. this.finalRules.push(new Outbound.FreedomSettings.FinalRule(action));
  1392. }
  1393. delFinalRule(index) {
  1394. this.finalRules.splice(index, 1);
  1395. }
  1396. static fromJson(json = {}) {
  1397. const finalRules = Array.isArray(json.finalRules)
  1398. ? json.finalRules.map(rule => Outbound.FreedomSettings.FinalRule.fromJson(rule))
  1399. : [];
  1400. // Backward compatibility: map legacy ipsBlocked entries to blocking finalRules.
  1401. if (finalRules.length === 0 && Array.isArray(json.ipsBlocked) && json.ipsBlocked.length > 0) {
  1402. finalRules.push(new Outbound.FreedomSettings.FinalRule('block', '', '', json.ipsBlocked, ''));
  1403. }
  1404. return new Outbound.FreedomSettings(
  1405. json.domainStrategy,
  1406. json.redirect,
  1407. json.fragment ? Outbound.FreedomSettings.Fragment.fromJson(json.fragment) : {},
  1408. json.noises ? json.noises.map(noise => Outbound.FreedomSettings.Noise.fromJson(noise)) : [],
  1409. finalRules,
  1410. );
  1411. }
  1412. toJson() {
  1413. return {
  1414. domainStrategy: ObjectUtil.isEmpty(this.domainStrategy) ? undefined : this.domainStrategy,
  1415. redirect: ObjectUtil.isEmpty(this.redirect) ? undefined : this.redirect,
  1416. fragment: Object.keys(this.fragment).length === 0 ? undefined : this.fragment,
  1417. noises: this.noises.length === 0 ? undefined : Outbound.FreedomSettings.Noise.toJsonArray(this.noises),
  1418. finalRules: this.finalRules.length === 0 ? undefined : Outbound.FreedomSettings.FinalRule.toJsonArray(this.finalRules),
  1419. };
  1420. }
  1421. };
  1422. Outbound.FreedomSettings.Fragment = class extends CommonClass {
  1423. constructor(
  1424. packets = '1-3',
  1425. length = '',
  1426. interval = '',
  1427. maxSplit = ''
  1428. ) {
  1429. super();
  1430. this.packets = packets;
  1431. this.length = length;
  1432. this.interval = interval;
  1433. this.maxSplit = maxSplit;
  1434. }
  1435. static fromJson(json = {}) {
  1436. return new Outbound.FreedomSettings.Fragment(
  1437. json.packets,
  1438. json.length,
  1439. json.interval,
  1440. json.maxSplit
  1441. );
  1442. }
  1443. };
  1444. Outbound.FreedomSettings.Noise = class extends CommonClass {
  1445. constructor(
  1446. type = 'rand',
  1447. packet = '10-20',
  1448. delay = '10-16',
  1449. applyTo = 'ip'
  1450. ) {
  1451. super();
  1452. this.type = type;
  1453. this.packet = packet;
  1454. this.delay = delay;
  1455. this.applyTo = applyTo;
  1456. }
  1457. static fromJson(json = {}) {
  1458. return new Outbound.FreedomSettings.Noise(
  1459. json.type,
  1460. json.packet,
  1461. json.delay,
  1462. json.applyTo
  1463. );
  1464. }
  1465. toJson() {
  1466. return {
  1467. type: this.type,
  1468. packet: this.packet,
  1469. delay: this.delay,
  1470. applyTo: this.applyTo
  1471. };
  1472. }
  1473. };
  1474. Outbound.FreedomSettings.FinalRule = class extends CommonClass {
  1475. constructor(action = 'block', network = '', port = '', ip = [], blockDelay = '') {
  1476. super();
  1477. this.action = action;
  1478. this.network = network;
  1479. this.port = port;
  1480. this.ip = Array.isArray(ip) ? ip : [];
  1481. this.blockDelay = blockDelay;
  1482. }
  1483. static fromJson(json = {}) {
  1484. return new Outbound.FreedomSettings.FinalRule(
  1485. json.action,
  1486. Array.isArray(json.network) ? json.network.join(',') : json.network,
  1487. json.port,
  1488. json.ip || [],
  1489. json.blockDelay,
  1490. );
  1491. }
  1492. toJson() {
  1493. return {
  1494. action: ['allow', 'block'].includes(this.action) ? this.action : 'block',
  1495. network: ObjectUtil.isEmpty(this.network) ? undefined : this.network,
  1496. port: ObjectUtil.isEmpty(this.port) ? undefined : this.port,
  1497. ip: this.ip.length === 0 ? undefined : this.ip,
  1498. blockDelay: this.action === 'block' && !ObjectUtil.isEmpty(this.blockDelay) ? this.blockDelay : undefined,
  1499. };
  1500. }
  1501. };
  1502. Outbound.BlackholeSettings = class extends CommonClass {
  1503. constructor(type) {
  1504. super();
  1505. this.type = type;
  1506. }
  1507. static fromJson(json = {}) {
  1508. return new Outbound.BlackholeSettings(
  1509. json.response ? json.response.type : undefined,
  1510. );
  1511. }
  1512. toJson() {
  1513. return {
  1514. response: ObjectUtil.isEmpty(this.type) ? undefined : { type: this.type },
  1515. };
  1516. }
  1517. };
  1518. Outbound.DNSRule = class extends CommonClass {
  1519. constructor(action = 'direct', qtype = '', domain = '') {
  1520. super();
  1521. this.action = action;
  1522. this.qtype = qtype;
  1523. this.domain = domain;
  1524. }
  1525. static fromJson(json = {}) {
  1526. return new Outbound.DNSRule(
  1527. json.action,
  1528. normalizeDNSRuleField(json.qtype),
  1529. normalizeDNSRuleField(json.domain),
  1530. );
  1531. }
  1532. toJson() {
  1533. const rule = {
  1534. action: normalizeDNSRuleAction(this.action),
  1535. };
  1536. const qtype = normalizeDNSRuleField(this.qtype);
  1537. if (!ObjectUtil.isEmpty(qtype)) {
  1538. if (/^\d+$/.test(qtype)) {
  1539. rule.qtype = Number(qtype);
  1540. } else {
  1541. rule.qtype = qtype;
  1542. }
  1543. }
  1544. const domains = normalizeDNSRuleField(this.domain)
  1545. .split(',')
  1546. .map(d => d.trim())
  1547. .filter(d => d.length > 0);
  1548. if (domains.length > 0) {
  1549. rule.domain = domains;
  1550. }
  1551. return rule;
  1552. }
  1553. };
  1554. Outbound.DNSSettings = class extends CommonClass {
  1555. constructor(
  1556. network = 'udp',
  1557. address = '',
  1558. port = 53,
  1559. rules = []
  1560. ) {
  1561. super();
  1562. this.network = network;
  1563. this.address = address;
  1564. this.port = port;
  1565. this.rules = Array.isArray(rules) ? rules.map(rule => rule instanceof Outbound.DNSRule ? rule : Outbound.DNSRule.fromJson(rule)) : [];
  1566. }
  1567. addRule(action = 'direct') {
  1568. this.rules.push(new Outbound.DNSRule(action));
  1569. }
  1570. delRule(index) {
  1571. this.rules.splice(index, 1);
  1572. }
  1573. static fromJson(json = {}) {
  1574. return new Outbound.DNSSettings(
  1575. json.network,
  1576. json.address,
  1577. json.port,
  1578. getDNSRulesFromJson(json),
  1579. );
  1580. }
  1581. toJson() {
  1582. const json = {
  1583. network: this.network,
  1584. address: this.address,
  1585. port: this.port,
  1586. };
  1587. if (this.rules.length > 0) {
  1588. json.rules = Outbound.DNSRule.toJsonArray(this.rules);
  1589. }
  1590. return json;
  1591. }
  1592. };
  1593. Outbound.VmessSettings = class extends CommonClass {
  1594. constructor(address, port, id, security) {
  1595. super();
  1596. this.address = address;
  1597. this.port = port;
  1598. this.id = id;
  1599. this.security = security;
  1600. }
  1601. static fromJson(json = {}) {
  1602. if (!ObjectUtil.isArrEmpty(json.vnext)) {
  1603. const v = json.vnext[0] || {};
  1604. const u = ObjectUtil.isArrEmpty(v.users) ? {} : v.users[0];
  1605. return new Outbound.VmessSettings(
  1606. v.address,
  1607. v.port,
  1608. u.id,
  1609. u.security,
  1610. );
  1611. }
  1612. }
  1613. toJson() {
  1614. return {
  1615. vnext: [{
  1616. address: this.address,
  1617. port: this.port,
  1618. users: [{
  1619. id: this.id,
  1620. security: this.security
  1621. }]
  1622. }]
  1623. };
  1624. }
  1625. };
  1626. Outbound.VLESSSettings = class extends CommonClass {
  1627. constructor(address, port, id, flow, encryption, reverseTag = '', reverseSniffing = new ReverseSniffing(), testpre = 0, testseed = [900, 500, 900, 256]) {
  1628. super();
  1629. this.address = address;
  1630. this.port = port;
  1631. this.id = id;
  1632. this.flow = flow;
  1633. this.encryption = encryption;
  1634. this.reverseTag = reverseTag;
  1635. this.reverseSniffing = reverseSniffing;
  1636. this.testpre = testpre;
  1637. this.testseed = testseed;
  1638. }
  1639. static fromJson(json = {}) {
  1640. if (ObjectUtil.isEmpty(json.address) || ObjectUtil.isEmpty(json.port)) return new Outbound.VLESSSettings();
  1641. return new Outbound.VLESSSettings(
  1642. json.address,
  1643. json.port,
  1644. json.id,
  1645. json.flow,
  1646. json.encryption,
  1647. json.reverse?.tag || '',
  1648. ReverseSniffing.fromJson(json.reverse?.sniffing || {}),
  1649. json.testpre || 0,
  1650. json.testseed && json.testseed.length >= 4 ? json.testseed : [900, 500, 900, 256]
  1651. );
  1652. }
  1653. toJson() {
  1654. const result = {
  1655. address: this.address,
  1656. port: this.port,
  1657. id: this.id,
  1658. flow: this.flow,
  1659. encryption: this.encryption,
  1660. };
  1661. if (!ObjectUtil.isEmpty(this.reverseTag)) {
  1662. const reverseSniffing = this.reverseSniffing ? this.reverseSniffing.toJson() : {};
  1663. const defaultReverseSniffing = new ReverseSniffing().toJson();
  1664. result.reverse = {
  1665. tag: this.reverseTag,
  1666. sniffing: JSON.stringify(reverseSniffing) === JSON.stringify(defaultReverseSniffing) ? {} : reverseSniffing,
  1667. };
  1668. }
  1669. // Only include Vision settings when flow is set
  1670. if (this.flow && this.flow !== '') {
  1671. if (this.testpre > 0) {
  1672. result.testpre = this.testpre;
  1673. }
  1674. if (this.testseed && this.testseed.length >= 4) {
  1675. result.testseed = this.testseed;
  1676. }
  1677. }
  1678. return result;
  1679. }
  1680. };
  1681. Outbound.TrojanSettings = class extends CommonClass {
  1682. constructor(address, port, password) {
  1683. super();
  1684. this.address = address;
  1685. this.port = port;
  1686. this.password = password;
  1687. }
  1688. static fromJson(json = {}) {
  1689. if (ObjectUtil.isArrEmpty(json.servers)) return new Outbound.TrojanSettings();
  1690. return new Outbound.TrojanSettings(
  1691. json.servers[0].address,
  1692. json.servers[0].port,
  1693. json.servers[0].password,
  1694. );
  1695. }
  1696. toJson() {
  1697. return {
  1698. servers: [{
  1699. address: this.address,
  1700. port: this.port,
  1701. password: this.password,
  1702. }],
  1703. };
  1704. }
  1705. };
  1706. Outbound.ShadowsocksSettings = class extends CommonClass {
  1707. constructor(address, port, password, method, uot, UoTVersion) {
  1708. super();
  1709. this.address = address;
  1710. this.port = port;
  1711. this.password = password;
  1712. this.method = method;
  1713. this.uot = uot;
  1714. this.UoTVersion = UoTVersion;
  1715. }
  1716. static fromJson(json = {}) {
  1717. let servers = json.servers;
  1718. if (ObjectUtil.isArrEmpty(servers)) servers = [{}];
  1719. return new Outbound.ShadowsocksSettings(
  1720. servers[0].address,
  1721. servers[0].port,
  1722. servers[0].password,
  1723. servers[0].method,
  1724. servers[0].uot,
  1725. servers[0].UoTVersion,
  1726. );
  1727. }
  1728. toJson() {
  1729. return {
  1730. servers: [{
  1731. address: this.address,
  1732. port: this.port,
  1733. password: this.password,
  1734. method: this.method,
  1735. uot: this.uot,
  1736. UoTVersion: this.UoTVersion,
  1737. }],
  1738. };
  1739. }
  1740. };
  1741. Outbound.SocksSettings = class extends CommonClass {
  1742. constructor(address, port, user, pass) {
  1743. super();
  1744. this.address = address;
  1745. this.port = port;
  1746. this.user = user;
  1747. this.pass = pass;
  1748. }
  1749. static fromJson(json = {}) {
  1750. let servers = json.servers;
  1751. if (ObjectUtil.isArrEmpty(servers)) servers = [{ users: [{}] }];
  1752. return new Outbound.SocksSettings(
  1753. servers[0].address,
  1754. servers[0].port,
  1755. ObjectUtil.isArrEmpty(servers[0].users) ? '' : servers[0].users[0].user,
  1756. ObjectUtil.isArrEmpty(servers[0].users) ? '' : servers[0].users[0].pass,
  1757. );
  1758. }
  1759. toJson() {
  1760. return {
  1761. servers: [{
  1762. address: this.address,
  1763. port: this.port,
  1764. users: ObjectUtil.isEmpty(this.user) ? [] : [{ user: this.user, pass: this.pass }],
  1765. }],
  1766. };
  1767. }
  1768. };
  1769. Outbound.HttpSettings = class extends CommonClass {
  1770. constructor(address, port, user, pass) {
  1771. super();
  1772. this.address = address;
  1773. this.port = port;
  1774. this.user = user;
  1775. this.pass = pass;
  1776. }
  1777. static fromJson(json = {}) {
  1778. let servers = json.servers;
  1779. if (ObjectUtil.isArrEmpty(servers)) servers = [{ users: [{}] }];
  1780. return new Outbound.HttpSettings(
  1781. servers[0].address,
  1782. servers[0].port,
  1783. ObjectUtil.isArrEmpty(servers[0].users) ? '' : servers[0].users[0].user,
  1784. ObjectUtil.isArrEmpty(servers[0].users) ? '' : servers[0].users[0].pass,
  1785. );
  1786. }
  1787. toJson() {
  1788. return {
  1789. servers: [{
  1790. address: this.address,
  1791. port: this.port,
  1792. users: ObjectUtil.isEmpty(this.user) ? [] : [{ user: this.user, pass: this.pass }],
  1793. }],
  1794. };
  1795. }
  1796. };
  1797. Outbound.WireguardSettings = class extends CommonClass {
  1798. constructor(
  1799. mtu = 1420,
  1800. secretKey = '',
  1801. address = [''],
  1802. workers = 2,
  1803. domainStrategy = '',
  1804. reserved = '',
  1805. peers = [new Outbound.WireguardSettings.Peer()],
  1806. noKernelTun = false,
  1807. ) {
  1808. super();
  1809. this.mtu = mtu;
  1810. this.secretKey = secretKey;
  1811. this.pubKey = secretKey.length > 0 ? Wireguard.generateKeypair(secretKey).publicKey : '';
  1812. this.address = Array.isArray(address) ? address.join(',') : address;
  1813. this.workers = workers;
  1814. this.domainStrategy = domainStrategy;
  1815. this.reserved = Array.isArray(reserved) ? reserved.join(',') : reserved;
  1816. this.peers = peers;
  1817. this.noKernelTun = noKernelTun;
  1818. }
  1819. addPeer() {
  1820. this.peers.push(new Outbound.WireguardSettings.Peer());
  1821. }
  1822. delPeer(index) {
  1823. this.peers.splice(index, 1);
  1824. }
  1825. static fromJson(json = {}) {
  1826. return new Outbound.WireguardSettings(
  1827. json.mtu,
  1828. json.secretKey,
  1829. json.address,
  1830. json.workers,
  1831. json.domainStrategy,
  1832. json.reserved,
  1833. json.peers.map(peer => Outbound.WireguardSettings.Peer.fromJson(peer)),
  1834. json.noKernelTun,
  1835. );
  1836. }
  1837. toJson() {
  1838. return {
  1839. mtu: this.mtu ?? undefined,
  1840. secretKey: this.secretKey,
  1841. address: this.address ? this.address.split(",") : [],
  1842. workers: this.workers ?? undefined,
  1843. domainStrategy: WireguardDomainStrategy.includes(this.domainStrategy) ? this.domainStrategy : undefined,
  1844. reserved: this.reserved ? this.reserved.split(",").map(Number) : undefined,
  1845. peers: Outbound.WireguardSettings.Peer.toJsonArray(this.peers),
  1846. noKernelTun: this.noKernelTun,
  1847. };
  1848. }
  1849. };
  1850. Outbound.WireguardSettings.Peer = class extends CommonClass {
  1851. constructor(
  1852. publicKey = '',
  1853. psk = '',
  1854. allowedIPs = ['0.0.0.0/0', '::/0'],
  1855. endpoint = '',
  1856. keepAlive = 0
  1857. ) {
  1858. super();
  1859. this.publicKey = publicKey;
  1860. this.psk = psk;
  1861. this.allowedIPs = allowedIPs;
  1862. this.endpoint = endpoint;
  1863. this.keepAlive = keepAlive;
  1864. }
  1865. static fromJson(json = {}) {
  1866. return new Outbound.WireguardSettings.Peer(
  1867. json.publicKey,
  1868. json.preSharedKey,
  1869. json.allowedIPs,
  1870. json.endpoint,
  1871. json.keepAlive
  1872. );
  1873. }
  1874. toJson() {
  1875. return {
  1876. publicKey: this.publicKey,
  1877. preSharedKey: this.psk.length > 0 ? this.psk : undefined,
  1878. allowedIPs: this.allowedIPs ? this.allowedIPs : undefined,
  1879. endpoint: this.endpoint,
  1880. keepAlive: this.keepAlive ?? undefined,
  1881. };
  1882. }
  1883. };
  1884. Outbound.HysteriaSettings = class extends CommonClass {
  1885. constructor(address = '', port = 443, version = 2) {
  1886. super();
  1887. this.address = address;
  1888. this.port = port;
  1889. this.version = version;
  1890. }
  1891. static fromJson(json = {}) {
  1892. if (Object.keys(json).length === 0) return new Outbound.HysteriaSettings();
  1893. return new Outbound.HysteriaSettings(
  1894. json.address,
  1895. json.port,
  1896. json.version
  1897. );
  1898. }
  1899. toJson() {
  1900. return {
  1901. address: this.address,
  1902. port: this.port,
  1903. version: this.version
  1904. };
  1905. }
  1906. };