MHSanaei 1 year ago
parent
commit
abb79bd978

+ 131 - 5
web/assets/js/model/xray.js

@@ -91,6 +91,9 @@ const UTLS_FINGERPRINT = {
     UTLS_RANDOMIZED: "randomized",
 };
 
+const bytesToHex = e => Array.from(e).map(e => e.toString(16).padStart(2, 0)).join('');
+const hexToBytes = e => new Uint8Array(e.match(/[0-9a-f]{2}/gi).map(e => parseInt(e, 16)));
+
 const ALPN_OPTION = {
     H3: "h3",
     H2: "h2",
@@ -596,10 +599,62 @@ TlsStreamSettings.Settings = class extends XrayCommonClass {
     }
 };
 
+class RealityStreamSettings extends XrayCommonClass {
+    constructor(show = false,xver = 0, fingerprint = UTLS_FINGERPRINT.UTLS_FIREFOX, dest = 'github.io:443',  serverNames = 'github.io,www.github.io,', privateKey = RandomUtil.randomX25519PrivateKey(), publicKey = '', minClient = '',
+        maxClient = '', maxTimediff = 0, shortIds = RandomUtil.randowShortId()) {
+        super();
+        this.show = show;
+        this.xver = xver;
+        this.fingerprint = fingerprint;
+        this.dest = dest;
+        this.serverNames = serverNames instanceof Array ? serverNames.join(",") : serverNames;
+        this.privateKey = privateKey;
+        this.publicKey = RandomUtil.randomX25519PublicKey(this.privateKey);
+        this.minClient = minClient;
+        this.maxClient = maxClient;
+        this.maxTimediff = maxTimediff;
+        this.shortIds = shortIds instanceof Array ? shortIds.join(",") : shortIds;
+        
+    }
+
+    static fromJson(json = {}) {
+        return new RealityStreamSettings(
+            json.show,
+            json.xver,
+            json.fingerprint,
+            json.dest,
+            json.serverNames,
+            json.privateKey,
+            json.publicKey,
+            json.minClient,
+            json.maxClient,
+            json.maxTimediff,
+            json.shortIds  
+        );
+
+    }
+    toJson() {
+        return {
+            show: this.show,
+            xver: this.xver,
+            fingerprint: this.fingerprint,
+            dest: this.dest,
+            serverNames: this.serverNames.split(/,|,|\s+/),
+            privateKey: this.privateKey,
+            publicKey: this.publicKey,
+            minClient: this.minClient,
+            maxClient: this.maxClient,
+            maxTimediff: this.maxTimediff,
+            shortIds: this.shortIds.split(/,|,|\s+/)
+        };
+    }
+}
+
 class StreamSettings extends XrayCommonClass {
     constructor(network='tcp',
                 security='none',
                 tlsSettings=new TlsStreamSettings(),
+                realitySettings = new RealityStreamSettings(),
                 tcpSettings=new TcpStreamSettings(),
                 kcpSettings=new KcpStreamSettings(),
                 wsSettings=new WsStreamSettings(),
@@ -611,6 +666,7 @@ class StreamSettings extends XrayCommonClass {
         this.network = network;
         this.security = security;
         this.tls = tlsSettings;
+        this.reality = realitySettings;
         this.tcp = tcpSettings;
         this.kcp = kcpSettings;
         this.ws = wsSettings;
@@ -643,17 +699,34 @@ class StreamSettings extends XrayCommonClass {
         }
     }
 
-    static fromJson(json={}) {
-        let tls;
-        if (json.security === "xtls") {
-            tls = TlsStreamSettings.fromJson(json.XTLSSettings);
+    //for Reality
+    get isReality() {
+        return this.security === "reality";
+    }
+
+    set isReality(isReality) {
+        if (isReality) {
+            this.security = "reality";
         } else {
+            this.security = "none";
+        }
+    }
+    
+    static fromJson(json = {}) {
+        let tls, reality;
+        if (json.security === "xtls") {
+            tls = TlsStreamSettings.fromJson(json.xtlsSettings);
+        } else if (json.security === "tls") {
             tls = TlsStreamSettings.fromJson(json.tlsSettings);
         }
+        if (json.security === "reality") {
+            reality = RealityStreamSettings.fromJson(json.realitySettings)
+        }
         return new StreamSettings(
             json.network,
             json.security,
             tls,
+            reality,
             TcpStreamSettings.fromJson(json.tcpSettings),
             KcpStreamSettings.fromJson(json.kcpSettings),
             WsStreamSettings.fromJson(json.wsSettings),
@@ -671,6 +744,7 @@ class StreamSettings extends XrayCommonClass {
             tlsSettings: this.isTls ? this.tls.toJson() : undefined,
             XTLSSettings: this.isXTLS ? this.tls.toJson() : undefined,
             tcpSettings: network === 'tcp' ? this.tcp.toJson() : undefined,
+            realitySettings: this.isReality ? this.reality.toJson() : undefined,
             kcpSettings: network === 'kcp' ? this.kcp.toJson() : undefined,
             wsSettings: network === 'ws' ? this.ws.toJson() : undefined,
             httpSettings: network === 'http' ? this.http.toJson() : undefined,
@@ -743,6 +817,8 @@ class Inbound extends XrayCommonClass {
 
     set tls(isTls) {
         if (isTls) {
+            this.xtls = false;
+            this.reality = false;
             this.stream.security = 'tls';
         } else {
             this.stream.security = 'none';
@@ -755,12 +831,32 @@ class Inbound extends XrayCommonClass {
 
     set XTLS(isXTLS) {
         if (isXTLS) {
+            this.xtls = false;
+            this.reality = false;
             this.stream.security = 'xtls';
         } else {
             this.stream.security = 'none';
         }
     }
 
+    //for Reality
+    get reality() {
+        if (this.stream.security === "reality") {
+            return this.network === "tcp" || this.network === "grpc" || this.network === "http";
+        }
+        return false;
+    }
+
+    set reality(isReality) {
+        if (isReality) {
+            this.tls = false;
+            this.xtls = false;
+            this.stream.security = "reality";
+        } else {
+            this.stream.security = "none";
+        }
+    }
+
     get network() {
         return this.stream.network;
     }
@@ -957,9 +1053,19 @@ class Inbound extends XrayCommonClass {
         }
     }
 
+    canEnableReality() {
+        switch (this.protocol) {
+            case Protocols.VLESS:
+                break;
+            default:
+                return false;
+        }
+        return this.network === "tcp" || this.network === "grpc" || this.network === "http";
+    }
+
     //this is used for xtls-rprx-vision
     canEnableTlsFlow() {
-        if ((this.stream.security === 'tls') && (this.network === "tcp")) {
+        if (((this.stream.security === 'tls') || (this.stream.security === 'reality')) && (this.network === "tcp")) {
             switch (this.protocol) {
                 case Protocols.VLESS:
                     return true;
@@ -1169,6 +1275,26 @@ class Inbound extends XrayCommonClass {
             params.set("flow", this.settings.vlesses[clientIndex].flow);
         }
 
+        if (this.reality) {
+            params.set("security", "reality");
+            if (!ObjectUtil.isArrEmpty(this.stream.reality.serverNames)) {
+                params.set("sni", this.stream.reality.serverNames.split(/,|,|\s+/)[0]);
+            }
+            if (this.stream.reality.publicKey != "") {
+                //params.set("pbk", Ed25519.getPublicKey(this.stream.reality.privateKey));
+                params.set("pbk", this.stream.reality.publicKey);
+            }
+            if (this.stream.network === 'tcp') {
+                params.set("flow", this.settings.vlesses[clientIndex].flow);
+            }
+            if (this.stream.reality.shortIds != "") {
+                params.set("sid", this.stream.reality.shortIds);
+            }
+            if (this.stream.reality.fingerprint != "") {
+                params.set("fp", this.stream.reality.fingerprint);
+            }
+        }
+        
         const link = `vless://${uuid}@${address}:${port}`;
         const url = new URL(link);
         for (const [key, value] of params) {

+ 53 - 0
web/assets/js/util/utils.js

@@ -89,6 +89,31 @@ const seq = [
     'U', 'V', 'W', 'X', 'Y', 'Z'
 ];
 
+const shortIdSeq = [
+    'a', 'b', 'c', 'd', 'e', 'f',
+    '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+];
+
+const x25519Map = new Map(
+    [
+        ['EH2FWe-Ij_FFAa2u9__-aiErLvVIneP601GOCdlyPWw', "goY3OtfaA4UYbiz7Hn0NysI5QJrK0VT_Chg6RLgUPQU"],
+        ['cKI_6DoMSP1IepeWWXrG3G9nkehl94KYBhagU50g2U0', "VigpKFbSLnHLzBWobZaS1IBmw--giJ51w92y723ajnU"],
+        ['qM2SNyK3NyHB6deWpEP3ITyCGKQFRTna_mlKP0w1QH0', "HYyIGuyNFslmcnNT7mrDdmuXwn4cm7smE_FZbYguKHQ"],
+        ['qCWg5GMEDFd3n1nxDswlIpOHoPUXMLuMOIiLUVzubkI', "rJFC3dUjJxMnVZiUGzmf_LFsJUwFWY-CU5RQgFOHCWM"],
+        ['4NOBxDrEsOhNI3Y3EnVIy_TN-uyBoAjQw6QM0YsOi0s', "CbcY9qc4YuMDJDyyL0OITlU824TBg1O84ClPy27e2RM"],
+        ['eBvFb0M4HpSOwWjtXV8zliiEs_hg56zX4a2LpuuqpEI', "CjulQ2qVIky7ImIfysgQhNX7s_drGLheCGSkVHcLZhc"],
+        ['yEpOzQV04NNcycWVeWtRNTzv5TS-ynTuKRacZCH-6U8', "O9RSr5gSdok2K_tobQnf_scyKVqnCx6C4Jrl7_rCZEQ"],
+        ['CNt6TAUVCwqM6xIBHyni0K3Zqbn2htKQLvLb6XDgh0s', "d9cGLVBrDFS02L2OvkqyqwFZ1Ux3AHs28ehl4Rwiyl0"],
+        ['EInKw-6Wr0rAHXlxxDuZU5mByIzcD3Z-_iWPzXlUL1k', "LlYD2nNVAvyjNvjZGZh4R8PkMIwkc6EycPTvR2LE0nQ"],
+        ['GKIKo7rcXVyle-EUHtGIDtYnDsI6osQmOUl3DTJRAGc', "VcqHivYGGoBkcxOI6cSSjQmneltstkb2OhvO53dyhEM"],
+        ['-FVDzv68IC17fJVlNDlhrrgX44WeBfbhwjWpCQVXGHE', "PGG2EYOvsFt2lAQTD7lqHeRxz2KxvllEDKcUrtizPBU"],
+        ['0H3OJEYEu6XW7woqy7cKh2vzg6YHkbF_xSDTHKyrsn4', "mzevpYbS8kXengBY5p7tt56QE4tS3lwlwRemmkcQeyc"],
+        ['8F8XywN6ci44ES6em2Z0fYYxyptB9uaXY9Hc1WSSPE4', "qCZUdWQZ2H33vWXnOkG8NpxBeq3qn5QWXlfCOWBNkkc"],
+        ['IN0dqfkC10dj-ifRHrg2PmmOrzYs697ajGMwcLbu-1g', "2UW_EO3r7uczPGUUlpJBnMDpDmWUHE2yDzCmXS4sckE"],
+        ['uIcmks5rAhvBe4dRaJOdeSqgxLGGMZhsGk4J4PEKL2s', "F9WJV_74IZp0Ide4hWjiJXk9FRtBUBkUr3mzU-q1lzk"],
+    ]
+);
+
 class RandomUtil {
 
     static randomIntRange(min, max) {
@@ -107,6 +132,14 @@ class RandomUtil {
         return str;
     }
 
+    static randomShortIdSeq(count) {
+        let str = '';
+        for (let i = 0; i < count; ++i) {
+            str += shortIdSeq[this.randomInt(16)];
+        }
+        return str;
+    }
+
     static randomLowerAndNum(count) {
         let str = '';
         for (let i = 0; i < count; ++i) {
@@ -137,6 +170,26 @@ class RandomUtil {
         });
     }
 
+    static randowShortId() {
+        let str = '';
+        str += this.randomShortIdSeq(8)
+        return str;
+    }
+
+    static randomX25519PrivateKey() {
+        let num = x25519Map.size;
+        let index = this.randomInt(num);
+        let cntr = 0;
+        for (let key of x25519Map.keys()) {
+            if (cntr++ === index) {
+                return key;
+            }
+        }
+    }
+
+    static randomX25519PublicKey(key) {
+        return x25519Map.get(key)
+    }
     static randomText() {
         var chars = 'abcdefghijklmnopqrstuvwxyz1234567890';
         var string = '';

+ 52 - 2
web/html/xui/form/tls_settings.html

@@ -1,11 +1,32 @@
 {{define "form/tlsSettings"}}
 <!-- tls enable -->
 <a-form layout="inline" v-if="inbound.canSetTls()">
-    <a-form-item label="TLS">
+    <a-form-item v-if="inbound.canEnableTls()" label="TLS">
         <a-switch v-model="inbound.tls">
         </a-switch>
     </a-form-item>
-    <a-form-item v-if="inbound.canEnableXTLS()" label="XTLS">
+    <a-form-item v-if="inbound.canEnableReality()">
+        <span slot="label">
+            Reality
+            <a-tooltip>
+                <template slot="title">
+                  <span>{{ i18n "pages.inbounds.Realitydec" }}</span>
+                </template>
+                <a-icon type="question-circle" theme="filled"></a-icon>
+            </a-tooltip>
+        </span>
+        <a-switch v-model="inbound.reality"></a-switch>
+    </a-form-item>
+    <a-form-item v-if="inbound.canEnableXTLS()">
+        <span slot="label">
+            XTLS
+            <a-tooltip>
+                <template slot="title">
+                  <span>{{ i18n "pages.inbounds.XTLSdec" }}</span>
+                </template>
+                <a-icon type="question-circle" theme="filled"></a-icon>
+            </a-tooltip>
+        </span>
         <a-switch v-model="inbound.XTLS"></a-switch>
     </a-form-item>
 </a-form>
@@ -72,4 +93,33 @@
         </a-form-item>
     </template>
 </a-form>
+<a-form v-else-if="inbound.reality" layout="inline">
+    <a-form-item label="show">
+        <a-switch v-model="inbound.stream.reality.show">
+        </a-switch>
+    </a-form-item>
+    <a-form-item label="xver">
+        <a-input type="number" v-model.number="inbound.stream.reality.xver" :min="0" style="width: 60px"></a-input>
+    </a-form-item>
+    <a-form-item label="uTLS" >
+        <a-select v-model="inbound.stream.reality.fingerprint" style="width: 135px">
+            <a-select-option v-for="key in UTLS_FINGERPRINT" :value="key">[[ key ]]</a-select-option>
+        </a-select>
+    </a-form-item>
+	<a-form-item label="dest">
+        <a-input v-model.trim="inbound.stream.reality.dest" style="width: 360px"></a-input>
+    </a-form-item>
+    <a-form-item label="serverNames">
+        <a-input v-model.trim="inbound.stream.reality.serverNames" style="width: 360px"></a-input>
+    </a-form-item>
+    <a-form-item label="privateKey">
+        <a-input v-model.trim="inbound.stream.reality.privateKey" style="width: 360px"></a-input>
+    </a-form-item>
+    <a-form-item label="publicKey">
+        <a-input v-model.trim="inbound.stream.reality.publicKey" style="width: 360px"></a-input>
+    </a-form-item>
+    <a-form-item label="shortIds">
+        <a-input v-model.trim="inbound.stream.reality.shortIds"></a-input>
+    </a-form-item>
+</a-form>
 {{end}}

+ 2 - 1
web/html/xui/inbounds.html

@@ -134,6 +134,7 @@
                                     <a-tag style="margin:0;" color="green">[[ dbInbound.toInbound().stream.network ]]</a-tag>
                                     <a-tag style="margin:0;" v-if="dbInbound.toInbound().stream.isTls" color="cyan">TLS</a-tag>
                                     <a-tag style="margin:0;" v-if="dbInbound.toInbound().stream.isXTLS" color="cyan">XTLS</a-tag>
+                                    <a-tag style="margin:0;" v-if="dbInbound.toInbound().stream.isReality" color="cyan">Reality</a-tag>
                                 </template>
                             </template>
                             <template slot="clients" slot-scope="text, dbInbound">
@@ -239,7 +240,7 @@
     }, {
         title: '{{ i18n "pages.inbounds.protocol" }}',
         align: 'left',
-        width: 70,
+        width: 80,
         scopedSlots: { customRender: 'protocol' },
     }, {
         title: '{{ i18n "clients" }}',

+ 2 - 0
web/translation/translate.en_US.toml

@@ -152,6 +152,8 @@
 "IPLimitlogDesc" = "IPs history Log (before enabling inbound after it has been disabled by IP limit, you should clear the log)"
 "IPLimitlogclear" = "Clear The Log"
 "setDefaultCert" = "Set cert from panel"
+"XTLSdec" = "Xray core needs to be 1.7.5 and below"
+"Realitydec" = "Xray core needs to be 1.8.0 and above"
 
 [pages.client]
 "add" = "Add client"

+ 2 - 0
web/translation/translate.fa_IR.toml

@@ -150,6 +150,8 @@
 "IPLimitlogDesc" = "گزارش سابقه ای پی (قبل از فعال کردن ورودی پس از غیرفعال شدن توسط محدودیت ای پی، باید گزارش را پاک کنید)"
 "IPLimitlogclear" = "پاک کردن گزارش ها"
 "setDefaultCert" = "استفاده از گواهی پنل"
+"XTLSdec" = "هسته Xray باید 1.7.5 و کمتر باشد"
+"Realitydec" = "هسته Xray باید 1.8.0 و بالاتر باشد"
 
 [pages.client]
 "add" = "کاربر جدید"

+ 2 - 0
web/translation/translate.zh_Hans.toml

@@ -150,6 +150,8 @@
 "IPLimitlogDesc" = "IP 历史日志 (通过IP限制禁用inbound之前,需要清空日志)"
 "IPLimitlogclear" = "清除日志"
 "setDefaultCert" = "从面板设置证书"
+"XTLSdec" = "Xray核心需要1.7.5及以下版本"
+"Realitydec" = "Xray核心需要1.8.0及以上版本"
 
 [pages.client]
 "add" = "添加客户端"