diff --git a/app/dashboard/public/locales/en.json b/app/dashboard/public/locales/en.json
index 386eece26..a20a4dab9 100644
--- a/app/dashboard/public/locales/en.json
+++ b/app/dashboard/public/locales/en.json
@@ -50,6 +50,10 @@
"hostsDialog.fragment.info": "Correct pattern: length,interval,packets",
"hostsDialog.fragment.info.attention": "Attention: currently, this feature only supported in streisand >= 1.6.12 and v2rayNG >= 1.8.16 (custom config)",
"hostsDialog.fragment.info.examples": "Examples:",
+ "hostsDialog.noise": "Noise pattern",
+ "hostsDialog.noise.info": "Correct pattern: packets,delay",
+ "hostsDialog.noise.info.attention": "Attention: currently, this feature only supported in streisand >= 1.6.32 and v2rayNG >= 1.8.39 (custom config)",
+ "hostsDialog.noise.info.examples": "Examples:",
"hostsDialog.host": "Request Host",
"hostsDialog.host.info": "By default, if a request host is set in the Xray config, this host is used. However, you can set a custom request host here if needed.",
"hostsDialog.host.multiHost": "To set multiple addresses, separate them with , Each time an address is chosen randomly.",
@@ -184,4 +188,4 @@
"usersTable.noUserMatched": "It seems there is no user matched with what you are looking for",
"usersTable.status": "status",
"usersTable.total": "Total"
-}
\ No newline at end of file
+}
diff --git a/app/dashboard/public/locales/fa.json b/app/dashboard/public/locales/fa.json
index 22f361646..a85930811 100644
--- a/app/dashboard/public/locales/fa.json
+++ b/app/dashboard/public/locales/fa.json
@@ -55,6 +55,11 @@
"hostsDialog.fragment.info": "length,interval,packet (e.g. 10-100,100-200,tlshello)",
"hostsDialog.fragment.info.attention": "توجه: در حال حاضر، این ویژگی فقط در streisand >= 1.6.12 و v2rayNG >= 1.8.16 (پیکربندی سفارشی) پشتیبانی می شود",
"hostsDialog.fragment.info.examples": "نمونه ها:",
+ "hostsDialog.noise": "الگو نویز",
+ "hostsDialog.noise.info": "packet,delay (e.g. rand:10-20,100-200)",
+ "hostsDialog.noise.info.attention": "توجه: در حال حاضر، این ویژگی فقط در streisand >= 1.6.32 و v2rayNG >= 1.8.39 (پیکربندی سفارشی) پشتیبانی می شود",
+ "hostsDialog.noise.info.examples": "نمونه ها:",
+
"hostsDialog.host": "هاست درخواست",
"hostsDialog.host.info": "بهطور پیشفرض، اگر هاست درخواستی در پیکربندی *** تنظیم شده باشد، این هاست استفاده میشود. اما میتوانید یک هاست درخواستی متفاوت در اینجا قرار دهید.",
"hostsDialog.host.multiHost": "برای تنظیم چند آدرس، با , از هم جدا کنید. هر دفعه آدرسی به صورت تصادفی قرار داده میشود.",
@@ -189,4 +194,4 @@
"usersTable.noUserMatched": "بهنظر میرسه کاربری که جستجو کردید، وجود ندارد",
"usersTable.status": "وضعیت",
"usersTable.total": "مجموع"
-}
\ No newline at end of file
+}
diff --git a/app/dashboard/public/locales/ru.json b/app/dashboard/public/locales/ru.json
index 147458efd..4ad11a232 100644
--- a/app/dashboard/public/locales/ru.json
+++ b/app/dashboard/public/locales/ru.json
@@ -50,6 +50,10 @@
"hostsDialog.fragment.info": "length,interval,packet (e.g. 10-100,100-200,tlshello)",
"hostsDialog.fragment.info.attention": "Attention: currently, this feature only supported in streisand >= 1.6.12 and v2rayNG >= 1.8.16 (custom config)",
"hostsDialog.fragment.info.examples": "Examples:",
+ "hostsDialog.noise": "Шумовой паттерн",
+ "hostsDialog.noise.info": "packet,delay (e.g. rand:10-20,100-200)",
+ "hostsDialog.noise.info.attention": "Attention: currently, this feature only supported in streisand >= 1.6.32 and v2rayNG >= 1.8.39 (custom config)",
+ "hostsDialog.noise.info.examples": "Examples:",
"hostsDialog.host": "Host",
"hostsDialog.host.info": "По умолчанию, если в конфигурации XRAY задан запрашиваемый хост, то он и будет использоваться. Однако, если необходимо, вы можете установить здесь пользовательский запрашиваемый хост.",
"hostsDialog.host.multiHost": "Чтобы установить несколько адресов, разделяйте их с помощью ,. Каждый раз будет выбран случайный адрес.",
@@ -184,4 +188,4 @@
"usersTable.noUserMatched": "Похоже, нет пользователя, соответствующего вашему запросу",
"usersTable.status": "Статус",
"usersTable.total": "Всего"
-}
\ No newline at end of file
+}
diff --git a/app/dashboard/public/locales/zh.json b/app/dashboard/public/locales/zh.json
index c5ac2cff2..d43e52dbd 100644
--- a/app/dashboard/public/locales/zh.json
+++ b/app/dashboard/public/locales/zh.json
@@ -50,6 +50,10 @@
"hostsDialog.fragment.info": "length,interval,packet (e.g. 10-100,100-200,tlshello)",
"hostsDialog.fragment.info.attention": "请注意: 当前特性仅支持 streisand >= 1.6.12 and v2rayNG >= 1.8.16 (自定义配置)",
"hostsDialog.fragment.info.examples": "示例:",
+ "hostsDialog.noise": "噪声模式",
+ "hostsDialog.noise.info": "packet,delay (e.g. rand:10-20,100-200)",
+ "hostsDialog.noise.info.attention": "Attention: currently, this feature only supported in streisand >= 1.6.32 and v2rayNG >= 1.8.39 (custom config)",
+ "hostsDialog.noise.info.examples": "Examples:",
"hostsDialog.host": "请求主机",
"hostsDialog.host.info": "默认情况下,如果在 Xray 配置中设置了请求主机,则使用该主机。但是,如果需要,您可以在此处设置自定义请求主机。",
"hostsDialog.host.multiHost": "使用 , 作为分隔符设置多个地址,使用时,每次随机选择一个地址。",
diff --git a/app/dashboard/src/components/HostsDialog.tsx b/app/dashboard/src/components/HostsDialog.tsx
index 1f5f8ffc7..7c3760262 100644
--- a/app/dashboard/src/components/HostsDialog.tsx
+++ b/app/dashboard/src/components/HostsDialog.tsx
@@ -51,7 +51,8 @@ import {
proxyHostSecurity,
} from "constants/Proxies";
import { useHosts } from "contexts/HostsContext";
-import { FC, useEffect, useRef, useState } from "react";
+import { motion } from "framer-motion";
+import { FC, useEffect, useState } from "react";
import {
Controller,
FormProvider,
@@ -67,7 +68,6 @@ import { useDashboard } from "../contexts/DashboardContext";
import { DeleteIcon } from "./DeleteUserModal";
import { Icon } from "./Icon";
import { Input as CustomInput } from "./Input";
-import { motion } from "framer-motion";
export const DublicateIcon = chakra(DocumentDuplicateIcon, {
baseStyle: {
@@ -147,6 +147,7 @@ const hostsSchema = z.record(
allowinsecure: z.boolean().nullable().default(false),
is_disabled: z.boolean().default(true),
fragment_setting: z.string().nullable(),
+ noise_setting: z.string().nullable(),
random_user_agent: z.boolean().default(false),
security: z.string(),
alpn: z.string(),
@@ -206,6 +207,7 @@ const AccordionInbound: FC = ({
allowinsecure: false,
is_disabled: false,
fragment_setting: "",
+ noise_setting: "",
random_user_agent: false,
security: "inbound_default",
alpn: "",
@@ -999,6 +1001,71 @@ const AccordionInbound: FC = ({
)}
+
+
+ {t("hostsDialog.noise")}
+
+
+
+
+
+
+
+
+
+
+ {t("hostsDialog.noise.info")}
+
+
+ {t("hostsDialog.noise.info.examples")}
+
+
+ rand:10-20,10-20
+
+
+ rand:10-20,10-20&base64:7nQBAAABAAAAAAAABnQtcmluZwZtc2VkZ2UDbmV0AAABAAE=,10-25
+
+
+ {t("hostsDialog.noise.info.attention")}
+
+
+
+
+
+
+ {accordionErrors &&
+ accordionErrors[index]?.noise_setting && (
+
+ {
+ accordionErrors[index]?.noise_setting
+ ?.message
+ }
+
+ )}
+
+
+
None:
+ # ### commands auto generated by Alembic - please adjust! ###
+ op.add_column('hosts', sa.Column('noise_setting', sa.String(), nullable=True))
+ # ### end Alembic commands ###
+
+
+def downgrade() -> None:
+ # ### commands auto generated by Alembic - please adjust! ###
+ op.drop_column('hosts', 'noise_setting')
+ # ### end Alembic commands ###
diff --git a/app/db/models.py b/app/db/models.py
index 0ed4d6c82..806922625 100644
--- a/app/db/models.py
+++ b/app/db/models.py
@@ -214,6 +214,7 @@ class ProxyHost(Base):
is_disabled = Column(Boolean, nullable=True, default=False)
mux_enable = Column(Boolean, nullable=False, default=False, server_default='0')
fragment_setting = Column(String(100), nullable=True)
+ noise_setting = Column(String(), nullable=True)
random_user_agent = Column(Boolean, nullable=False, default=False, server_default='0')
diff --git a/app/models/proxy.py b/app/models/proxy.py
index 3e0d98321..162cc4e58 100644
--- a/app/models/proxy.py
+++ b/app/models/proxy.py
@@ -1,8 +1,8 @@
import json
+import re
from enum import Enum
from typing import Optional, Union
from uuid import UUID, uuid4
-import re
from pydantic import BaseModel, Field, validator
@@ -18,6 +18,9 @@
FRAGMENT_PATTERN = re.compile(r'^((\d{1,4}-\d{1,4})|(\d{1,4})),((\d{1,3}-\d{1,3})|(\d{1,3})),(tlshello|\d|\d\-\d)$')
+NOISE_PATTERN = re.compile(
+ r'^(rand:(\d{1,4}-\d{1,4}|\d{1,4})|str:.+|base64:.+)(,(\d{1,4}-\d{1,4}|\d{1,4}))?(&(rand:(\d{1,4}-\d{1,4}|\d{1,4})|str:.+|base64:.+)(,(\d{1,4}-\d{1,4}|\d{1,4}))?)*$')
+
class ProxyTypes(str, Enum):
# proxy_type = protocol
@@ -149,6 +152,7 @@ class ProxyHost(BaseModel):
is_disabled: Union[bool, None] = None
mux_enable: Union[bool, None] = None
fragment_setting: Optional[str] = Field(None, nullable=True)
+ noise_setting: Optional[str] = Field(None, nullable=True)
random_user_agent: Union[bool, None] = None
class Config:
@@ -180,6 +184,14 @@ def validate_fragment(cls, v):
)
return v
+ @validator("noise_setting", check_fields=False)
+ def validate_noise(cls, v):
+ if v and not NOISE_PATTERN.match(v):
+ raise ValueError(
+ "Noise setting must be like this: packet,delay (rand:10-20,100-200)."
+ )
+ return v
+
class ProxyInbound(BaseModel):
tag: str
diff --git a/app/subscription/clash.py b/app/subscription/clash.py
index 7c8e922b8..0579cbbe9 100644
--- a/app/subscription/clash.py
+++ b/app/subscription/clash.py
@@ -168,7 +168,7 @@ def make_node(self,
if type == 'shadowsocks':
type = 'ss'
- if network == 'tcp' and headers == 'http':
+ if network in ('tcp', 'raw') and headers == 'http':
network = 'http'
if network == 'httpupgrade':
network = 'ws'
@@ -233,7 +233,7 @@ def make_node(self,
elif network == 'h2':
net_opts = self.h2_config(path=path, host=host)
- elif network == 'tcp':
+ elif network in ('tcp', 'raw'):
net_opts = self.tcp_config(path=path, host=host)
else:
@@ -369,7 +369,7 @@ def add(self, remark: str, address: str, inbound: dict, settings: dict):
elif inbound['protocol'] == 'vless':
node['uuid'] = settings['id']
- if inbound['network'] in ('tcp', 'kcp') and inbound['header_type'] != 'http' and inbound['tls'] != 'none':
+ if inbound['network'] in ('tcp', 'raw', 'kcp') and inbound['header_type'] != 'http' and inbound['tls'] != 'none':
node['flow'] = settings.get('flow', '')
elif inbound['protocol'] == 'trojan':
diff --git a/app/subscription/share.py b/app/subscription/share.py
index 208ec639b..ecd879432 100644
--- a/app/subscription/share.py
+++ b/app/subscription/share.py
@@ -16,9 +16,13 @@
if TYPE_CHECKING:
from app.models.user import UserResponse
-from config import (ACTIVE_STATUS_TEXT, DISABLED_STATUS_TEXT,
- EXPIRED_STATUS_TEXT, LIMITED_STATUS_TEXT,
- ONHOLD_STATUS_TEXT)
+from config import (
+ ACTIVE_STATUS_TEXT,
+ DISABLED_STATUS_TEXT,
+ EXPIRED_STATUS_TEXT,
+ LIMITED_STATUS_TEXT,
+ ONHOLD_STATUS_TEXT
+)
SERVER_IP = get_public_ip()
SERVER_IPV6 = get_public_ipv6()
@@ -298,6 +302,7 @@ def process_inbounds_and_tags(
or inbound.get("allowinsecure", ""),
"mux_enable": host["mux_enable"],
"fragment_setting": host["fragment_setting"],
+ "noise_setting": host["noise_setting"],
"random_user_agent": host["random_user_agent"],
}
)
diff --git a/app/subscription/singbox.py b/app/subscription/singbox.py
index dfc1fa188..97aa4afc9 100644
--- a/app/subscription/singbox.py
+++ b/app/subscription/singbox.py
@@ -1,13 +1,17 @@
-import json
import copy
+import json
from random import choice
from jinja2.exceptions import TemplateNotFound
from app.subscription.funcs import get_grpc_gun
from app.templates import render_template
-from config import (MUX_TEMPLATE, SINGBOX_SETTINGS_TEMPLATE,
- SINGBOX_SUBSCRIPTION_TEMPLATE, USER_AGENT_TEMPLATE)
+from config import (
+ MUX_TEMPLATE,
+ SINGBOX_SETTINGS_TEMPLATE,
+ SINGBOX_SUBSCRIPTION_TEMPLATE,
+ USER_AGENT_TEMPLATE
+)
class SingBoxConfiguration(str):
@@ -232,14 +236,14 @@ def make_outbound(self,
"server_port": port,
}
- if net in ('tcp', 'kcp') and headers != 'http' and (tls or tls != 'none'):
+ if net in ('tcp', 'raw', 'kcp') and headers != 'http' and (tls or tls != 'none'):
if flow:
config["flow"] = flow
if net == 'h2':
net = 'http'
alpn = 'h2'
- elif net in ['tcp'] and headers == 'http':
+ elif net in ['tcp', 'raw'] and headers == 'http':
net = 'http'
if net in ['http', 'ws', 'quic', 'grpc', 'httpupgrade']:
diff --git a/app/subscription/v2ray.py b/app/subscription/v2ray.py
index 52024c881..1b99a56fb 100644
--- a/app/subscription/v2ray.py
+++ b/app/subscription/v2ray.py
@@ -1,6 +1,6 @@
import base64
-import json
import copy
+import json
import urllib.parse as urlparse
from random import choice
from typing import Union
@@ -11,9 +11,14 @@
from app.subscription.funcs import get_grpc_gun, get_grpc_multi
from app.templates import render_template
-from config import (EXTERNAL_CONFIG, GRPC_USER_AGENT_TEMPLATE, MUX_TEMPLATE,
- USER_AGENT_TEMPLATE, V2RAY_SETTINGS_TEMPLATE,
- V2RAY_SUBSCRIPTION_TEMPLATE)
+from config import (
+ EXTERNAL_CONFIG,
+ GRPC_USER_AGENT_TEMPLATE,
+ MUX_TEMPLATE,
+ USER_AGENT_TEMPLATE,
+ V2RAY_SETTINGS_TEMPLATE,
+ V2RAY_SUBSCRIPTION_TEMPLATE
+)
class V2rayShareLink(str):
@@ -69,6 +74,7 @@ def add(self, remark: str, address: str, inbound: dict, settings: dict):
sc_max_each_post_bytes=inbound.get('scMaxEachPostBytes', 1000000),
sc_max_concurrent_posts=inbound.get('scMaxConcurrentPosts', 100),
sc_min_posts_interval_ms=inbound.get('scMinPostsIntervalMs', 30),
+ x_padding_bytes=inbound.get("xPaddingBytes", "100-1000"),
)
elif inbound["protocol"] == "vless":
@@ -95,6 +101,7 @@ def add(self, remark: str, address: str, inbound: dict, settings: dict):
sc_max_each_post_bytes=inbound.get('scMaxEachPostBytes', 1000000),
sc_max_concurrent_posts=inbound.get('scMaxConcurrentPosts', 100),
sc_min_posts_interval_ms=inbound.get('scMinPostsIntervalMs', 30),
+ x_padding_bytes=inbound.get("xPaddingBytes", "100-1000"),
)
elif inbound["protocol"] == "trojan":
@@ -121,6 +128,7 @@ def add(self, remark: str, address: str, inbound: dict, settings: dict):
sc_max_each_post_bytes=inbound.get('scMaxEachPostBytes', 1000000),
sc_max_concurrent_posts=inbound.get('scMaxConcurrentPosts', 100),
sc_min_posts_interval_ms=inbound.get('scMinPostsIntervalMs', 30),
+ x_padding_bytes=inbound.get("xPaddingBytes", "100-1000"),
)
elif inbound["protocol"] == "shadowsocks":
@@ -160,6 +168,7 @@ def vmess(
sc_max_each_post_bytes: int = 1000000,
sc_max_concurrent_posts: int = 100,
sc_min_posts_interval_ms: int = 30,
+ x_padding_bytes: str = "100-1000",
):
payload = {
"add": address,
@@ -211,12 +220,13 @@ def vmess(
payload["scMaxEachPostBytes"] = sc_max_each_post_bytes
payload["scMaxConcurrentPosts"] = sc_max_concurrent_posts
payload["scMinPostsIntervalMs"] = sc_min_posts_interval_ms
+ payload["xPaddingBytes"] = x_padding_bytes
return (
- "vmess://"
- + base64.b64encode(
- json.dumps(payload, sort_keys=True).encode("utf-8")
- ).decode()
+ "vmess://"
+ + base64.b64encode(
+ json.dumps(payload, sort_keys=True).encode("utf-8")
+ ).decode()
)
@classmethod
@@ -243,6 +253,7 @@ def vless(cls,
sc_max_each_post_bytes: int = 1000000,
sc_max_concurrent_posts: int = 100,
sc_min_posts_interval_ms: int = 30,
+ x_padding_bytes: str = "100-1000",
):
payload = {
@@ -250,7 +261,7 @@ def vless(cls,
"type": net,
"headerType": type
}
- if flow and (tls in ('tls', 'reality') and net in ('tcp', 'kcp') and type != 'http'):
+ if flow and (tls in ('tls', 'reality') and net in ('tcp', 'raw', 'kcp') and type != 'http'):
payload['flow'] = flow
if net == 'grpc':
@@ -275,6 +286,7 @@ def vless(cls,
payload["scMaxEachPostBytes"] = sc_max_each_post_bytes
payload["scMaxConcurrentPosts"] = sc_max_concurrent_posts
payload["scMinPostsIntervalMs"] = sc_min_posts_interval_ms
+ payload["xPaddingBytes"] = x_padding_bytes
elif net == 'kcp':
payload['seed'] = path
@@ -303,10 +315,10 @@ def vless(cls,
payload["spx"] = spx
return (
- "vless://"
- + f"{id}@{address}:{port}?"
- + urlparse.urlencode(payload)
- + f"#{(urlparse.quote(remark))}"
+ "vless://"
+ + f"{id}@{address}:{port}?"
+ + urlparse.urlencode(payload)
+ + f"#{(urlparse.quote(remark))}"
)
@classmethod
@@ -333,6 +345,7 @@ def trojan(cls,
sc_max_each_post_bytes: int = 1000000,
sc_max_concurrent_posts: int = 100,
sc_min_posts_interval_ms: int = 30,
+ x_padding_bytes: str = "100-1000",
):
payload = {
@@ -340,7 +353,7 @@ def trojan(cls,
"type": net,
"headerType": type
}
- if flow and (tls in ('tls', 'reality') and net in ('tcp', 'kcp') and type != 'http'):
+ if flow and (tls in ('tls', 'reality') and net in ('tcp', 'raw', 'kcp') and type != 'http'):
payload['flow'] = flow
if net == 'grpc':
@@ -361,6 +374,7 @@ def trojan(cls,
payload["scMaxEachPostBytes"] = sc_max_each_post_bytes
payload["scMaxConcurrentPosts"] = sc_max_concurrent_posts
payload["scMinPostsIntervalMs"] = sc_min_posts_interval_ms
+ payload["xPaddingBytes"] = x_padding_bytes
elif net == 'quic':
payload['key'] = path
@@ -392,10 +406,10 @@ def trojan(cls,
payload["spx"] = spx
return (
- "trojan://"
- + f"{urlparse.quote(password, safe=':')}@{address}:{port}?"
- + urlparse.urlencode(payload)
- + f"#{urlparse.quote(remark)}"
+ "trojan://"
+ + f"{urlparse.quote(password, safe=':')}@{address}:{port}?"
+ + urlparse.urlencode(payload)
+ + f"#{urlparse.quote(remark)}"
)
@classmethod
@@ -403,9 +417,9 @@ def shadowsocks(
cls, remark: str, address: str, port: int, password: str, method: str
):
return (
- "ss://"
- + base64.b64encode(f"{method}:{password}".encode()).decode()
- + f"@{address}:{port}#{urlparse.quote(remark)}"
+ "ss://"
+ + base64.b64encode(f"{method}:{password}".encode()).decode()
+ + f"@{address}:{port}#{urlparse.quote(remark)}"
)
@@ -519,6 +533,13 @@ def splithttp_config(self, path=None, host=None, random_user_agent=None,
sc_max_each_post_bytes: int = 1000000,
sc_max_concurrent_posts: int = 100,
sc_min_posts_interval_ms: int = 30,
+ x_padding_bytes: str = "100-1000",
+ xmux: dict = {
+ "maxConcurrency": 0,
+ "maxConnections": 0,
+ "cMaxReuseTimes": 0,
+ "cMaxLifetimeMs": 0
+ },
):
config = copy.deepcopy(self.settings.get("splithttpSettings", {}))
@@ -536,6 +557,9 @@ def splithttp_config(self, path=None, host=None, random_user_agent=None,
config["scMaxEachPostBytes"] = sc_max_each_post_bytes
config["scMaxConcurrentPosts"] = sc_max_concurrent_posts
config["scMinPostsIntervalMs"] = sc_min_posts_interval_ms
+ config["xPaddingBytes"] = x_padding_bytes
+ if xmux:
+ config["xmux"] = xmux
# core will ignore unknown variables
@@ -581,11 +605,11 @@ def tcp_config(self, headers="none", path=None, host=None, random_user_agent=Non
}
}))
else:
- config = copy.deepcopy(self.settings.get("tcpSettings", {
+ config = copy.deepcopy(self.settings.get("tcpSettings", self.settings.get("rawSettings", {
"header": {
"type": "none"
}
- }))
+ })))
if "header" not in config:
config["header"] = {}
@@ -786,6 +810,53 @@ def make_fragment_outbound(packets="tlshello", length="100-200", interval="10-20
return outbound
+ @staticmethod
+ def make_fragment_and_noise_outbound(fragment="", noises=""):
+ outbound = {
+ "protocol": "freedom",
+ }
+ tag = ""
+ settings = {}
+
+ if fragment:
+ fragment_settings = {}
+ try:
+ length, interval, packets = fragment.split(',')
+ fragment_settings["fragment"] = {
+ "packets": packets,
+ "length": length,
+ "interval": interval
+ }
+ settings.update(fragment_settings)
+ tag = "fragment"
+ except ValueError:
+ pass
+ if noises:
+ sn = noises.split("&")
+ noises_settings = []
+ for n in sn:
+ try:
+ tp, delay = n.split(',')
+ _type, packet = tp.split(":")
+ noises_settings.append({
+ "type": _type,
+ "packet": packet,
+ "delay": delay
+ })
+ except ValueError:
+ pass
+ settings["noises"] = noises_settings
+ if not tag:
+ tag = "noises"
+ else:
+ tag += "_and_noises"
+ if not tag:
+ return
+ outbound["settings"] = settings
+ tag += "_out"
+ outbound["tag"] = tag
+ return outbound
+
def make_stream_setting(self,
net='',
path='',
@@ -805,6 +876,8 @@ def make_stream_setting(self,
sc_max_each_post_bytes: int = 1000000,
sc_max_concurrent_posts: int = 100,
sc_min_posts_interval_ms: int = 30,
+ x_padding_bytes: str = "100-1000",
+ xmux: dict = {},
):
if net == "ws":
@@ -819,7 +892,7 @@ def make_stream_setting(self,
elif net == "kcp":
network_setting = self.kcp_config(
seed=path, host=host, header=headers)
- elif net == "tcp" and tls != "reality":
+ elif net in ("tcp", "raw") and tls != "reality":
network_setting = self.tcp_config(
headers=headers, path=path, host=host, random_user_agent=random_user_agent)
elif net == "quic":
@@ -832,7 +905,9 @@ def make_stream_setting(self,
network_setting = self.splithttp_config(path=path, host=host, random_user_agent=random_user_agent,
sc_max_each_post_bytes=sc_max_each_post_bytes,
sc_max_concurrent_posts=sc_max_concurrent_posts,
- sc_min_posts_interval_ms=sc_min_posts_interval_ms
+ sc_min_posts_interval_ms=sc_min_posts_interval_ms,
+ x_padding_bytes=x_padding_bytes,
+ xmux=xmux,
)
else:
network_setting = {}
@@ -871,6 +946,7 @@ def add(self, remark: str, address: str, inbound: dict, settings: dict):
tls = (inbound['tls'])
headers = inbound['header_type']
fragment = inbound['fragment_setting']
+ noise = inbound['noise_setting']
path = inbound["path"]
multi_mode = inbound.get("multiMode", False)
@@ -891,7 +967,7 @@ def add(self, remark: str, address: str, inbound: dict, settings: dict):
id=settings['id'])
elif inbound['protocol'] == 'vless':
- if net in ('tcp', 'kcp') and headers != 'http' and tls in ('tls', 'reality'):
+ if net in ('tcp', 'raw', 'kcp') and headers != 'http' and tls in ('tls', 'reality'):
flow = settings.get('flow', '')
else:
flow = None
@@ -914,16 +990,10 @@ def add(self, remark: str, address: str, inbound: dict, settings: dict):
outbounds = [outbound]
dialer_proxy = ''
-
- if fragment:
- try:
- length, interval, packets = fragment.split(',')
- fragment_outbound = self.make_fragment_outbound(
- packets, length, interval)
- outbounds.append(fragment_outbound)
- dialer_proxy = fragment_outbound['tag']
- except ValueError:
- pass
+ extra_outbound = self.make_fragment_and_noise_outbound(fragment, noise)
+ if extra_outbound:
+ dialer_proxy = extra_outbound['tag']
+ outbounds.append(extra_outbound)
alpn = inbound.get('alpn', None)
outbound["streamSettings"] = self.make_stream_setting(
@@ -945,6 +1015,8 @@ def add(self, remark: str, address: str, inbound: dict, settings: dict):
sc_max_each_post_bytes=inbound.get('scMaxEachPostBytes', 1000000),
sc_max_concurrent_posts=inbound.get('scMaxConcurrentPosts', 100),
sc_min_posts_interval_ms=inbound.get('scMinPostsIntervalMs', 30),
+ x_padding_bytes=inbound.get("xPaddingBytes", "100-1000"),
+ xmux=inbound.get("xmux", {}),
)
mux_json = json.loads(self.mux_template)
diff --git a/app/xray/__init__.py b/app/xray/__init__.py
index 2f4875037..5b9e6c52c 100644
--- a/app/xray/__init__.py
+++ b/app/xray/__init__.py
@@ -62,6 +62,7 @@ def hosts(storage: dict):
"allowinsecure": host.allowinsecure,
"mux_enable": host.mux_enable,
"fragment_setting": host.fragment_setting,
+ "noise_setting": host.noise_setting,
"random_user_agent": host.random_user_agent,
} for host in inbound_hosts if not host.is_disabled
]
diff --git a/app/xray/config.py b/app/xray/config.py
index 55bd02efe..723e04e21 100644
--- a/app/xray/config.py
+++ b/app/xray/config.py
@@ -233,7 +233,7 @@ def _resolve_inbounds(self):
except:
settings['spx'] = ""
- if net == 'tcp':
+ if net in ('tcp', 'raw'):
header = net_settings.get('header', {})
request = header.get('request', {})
path = request.get('path')
@@ -293,6 +293,8 @@ def _resolve_inbounds(self):
settings['scMaxConcurrentPosts'] = net_settings.get('scMaxConcurrentPosts',
net_settings.get('maxConcurrentUploads', 100))
settings['scMinPostsIntervalMs'] = net_settings.get('scMinPostsIntervalMs', 30)
+ settings['xPaddingBytes'] = net_settings.get('xPaddingBytes', "100-1000")
+ settings['xmux'] = net_settings.get('xmux', {})
elif net == 'kcp':
header = net_settings.get('header', {})
@@ -391,12 +393,12 @@ def include_db_users(self) -> XRayConfig:
# XTLS currently only supports transmission methods of TCP and mKCP
if client.get('flow') and (
- inbound.get('network', 'tcp') not in ('tcp', 'kcp')
+ inbound.get('network', 'tcp') not in ('tcp', 'raw', 'kcp')
or
(
- inbound.get('network', 'tcp') in ('tcp', 'kcp')
- and
- inbound.get('tls') not in ('tls', 'reality')
+ inbound.get('network', 'tcp') in ('tcp', 'raw', 'kcp')
+ and
+ inbound.get('tls') not in ('tls', 'reality')
)
or
inbound.get('header_type') == 'http'