feat: WS-fronted profiles to bypass TLS handshake filtering
- add _build_ws_link: rebuild loopback panel WS links into public vless://uuid@host:port?type=ws&security=none links - matcher supports per-inbound ws block (match on xray-side port) - serves SE Sweden-WS (via Caddy :80) and NL Netherlands-WS (:80)
This commit is contained in:
@@ -542,6 +542,39 @@ def _link_port(link: str) -> Optional[int]:
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
def _build_ws_link(panel_link: str, inbound: dict) -> Optional[str]:
|
||||
"""Rebuild a panel WS link (often with localhost host) into a public client link.
|
||||
|
||||
Panel generates e.g. vless://uuid@127.0.0.1:10085?... for loopback-bound
|
||||
WS inbounds; clients need the public front instead:
|
||||
vless://uuid@<ws.host>:<ws.port>?type=ws&security=none&host=<ws.host>&path=...
|
||||
Triggered by the optional "ws" block in servers.conf:
|
||||
{"host": "ws.example.com", "port": 80, "xray_port": 10085}.
|
||||
Returns None on parse error.
|
||||
"""
|
||||
import urllib.parse
|
||||
ws = inbound.get("ws") or {}
|
||||
if not ws.get("host") or not ws.get("port"):
|
||||
return panel_link
|
||||
try:
|
||||
parts = urllib.parse.urlparse(panel_link.split("#")[0])
|
||||
userinfo = parts.username or ""
|
||||
if not userinfo:
|
||||
return None
|
||||
query = dict(urllib.parse.parse_qsl(parts.query, keep_blank_values=True))
|
||||
query["type"] = "ws"
|
||||
query["security"] = "none"
|
||||
query["host"] = ws["host"]
|
||||
if "path" not in query:
|
||||
return None
|
||||
host = ws["host"]
|
||||
port = int(ws["port"])
|
||||
netloc = f"{userinfo}@{host}:{port}"
|
||||
# hysteria-style links carry passwords; vless carries uuid only
|
||||
return f"{parts.scheme}://{netloc}?{urllib.parse.urlencode(query)}"
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
def _build_hysteria_link(panel_link: str, sub_id: str, srv: dict) -> Optional[str]:
|
||||
"""Rebuild panel hysteria2://AUTH@host:port link into a working client link.
|
||||
|
||||
@@ -1016,6 +1049,11 @@ async def get_subscription(request: Request, subscription_id: str, format: str =
|
||||
|
||||
ib_protocol = _get_inbound_protocol(inbound)
|
||||
ib_port = inbound.get("port")
|
||||
# WS-fronted inbounds: panel links carry the loopback xray port;
|
||||
# match on it, the public link is rebuilt below.
|
||||
ws_cfg = inbound.get("ws") or {}
|
||||
if ws_cfg.get("xray_port"):
|
||||
ib_port = ws_cfg["xray_port"]
|
||||
matched_link = None
|
||||
fallback_link = None
|
||||
for candidate in links:
|
||||
@@ -1045,6 +1083,10 @@ async def get_subscription(request: Request, subscription_id: str, format: str =
|
||||
link = matched_link
|
||||
if ib_protocol == "hysteria":
|
||||
link = _build_hysteria_link(link, subscription_id, srv) or link
|
||||
if ws_cfg:
|
||||
link = _build_ws_link(link, inbound)
|
||||
if link is None:
|
||||
continue
|
||||
clean_link = link.split('#')[0]
|
||||
if clean_link in seen_links:
|
||||
continue
|
||||
|
||||
Reference in New Issue
Block a user