fix(singbox): prevent UDP routing to HTTP outbounds (#45) and improve dashboard metadata
- Ensure selector and urltest groups require all member outbounds to support UDP - Add explicit reject rule for UDP on non-UDP outbounds to avoid sing-box router log spam - Add http/s:// to connection URL description in LuCI settings and update localization - Display transport protocol badges on dashboard outbound cards and modal rows - Deduplicate and collapse UDP routing error lines in telegram logs and bug report
This commit is contained in:
@@ -1731,8 +1731,16 @@ function view_logs(token, chat_id, msg_id, level, count) {
|
||||
if (res && res.status == 0 && res.output) {
|
||||
let lines = split(res.output, "\n");
|
||||
let filtered = [];
|
||||
let udp_http_notice_emitted = false;
|
||||
for (let line in lines) {
|
||||
if (line == "") continue;
|
||||
if (index(as_string(line), "UDP is not supported by outbound:") >= 0) {
|
||||
if (!udp_http_notice_emitted) {
|
||||
push(filtered, "UDP traffic through HTTP outbounds is not supported by sing-box; repeated UDP warnings for HTTP outbounds are hidden by Tachyon.");
|
||||
udp_http_notice_emitted = true;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (level == "error" && !match(line, /\[err\]|\[error\]/i)) continue;
|
||||
if (level == "warn" && !match(line, /\[warn\]/i)) continue;
|
||||
if (level == "info" && !match(line, /\[info\]/i)) continue;
|
||||
|
||||
@@ -632,11 +632,12 @@ function add_mixed_proxy_for_section(config, section, service_address) {
|
||||
inbound.users = [{ username, password }];
|
||||
}
|
||||
push(config.inbounds, inbound);
|
||||
push(config.route.rules, {
|
||||
let section_outbound = runtime_constants.outbound_tag(section[".name"]);
|
||||
generator_routes.push_section_route_rule(config, {
|
||||
action: "route",
|
||||
inbound: inbound.tag,
|
||||
outbound: runtime_constants.outbound_tag(section[".name"])
|
||||
});
|
||||
outbound: section_outbound
|
||||
}, section_outbound);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -685,11 +686,11 @@ function add_service_mixed_proxy_inbound(config, tag_name, listen_port, outbound
|
||||
listen: runtime_constants.SERVICE_MIXED_INBOUND_ADDRESS,
|
||||
listen_port
|
||||
});
|
||||
push(config.route.rules, {
|
||||
generator_routes.push_section_route_rule(config, {
|
||||
action: "route",
|
||||
inbound: tag_name,
|
||||
outbound
|
||||
});
|
||||
}, outbound);
|
||||
}
|
||||
|
||||
function service_mixed_proxy_inbound_tag_for_purpose(purpose) {
|
||||
|
||||
@@ -1189,31 +1189,35 @@ function tproxy_inbound_matcher() {
|
||||
return [ runtime_constants.TPROXY_INBOUND_TAG, runtime_constants.TPROXY_INBOUND6_TAG ];
|
||||
}
|
||||
|
||||
function outbound_supports_udp(config, tag_name) {
|
||||
function outbound_supports_udp(config, tag_name, visited) {
|
||||
tag_name = as_string(tag_name);
|
||||
if (tag_name == "")
|
||||
return false;
|
||||
if (tag_name == runtime_constants.DIRECT_OUTBOUND_TAG || tag_name == runtime_constants.BYPASS_OUTBOUND_TAG)
|
||||
return true;
|
||||
visited = visited || {};
|
||||
if (visited[tag_name])
|
||||
return true;
|
||||
visited[tag_name] = true;
|
||||
for (let outb in (config.outbounds || [])) {
|
||||
if (as_string(outb.tag) == tag_name) {
|
||||
let t = as_string(outb.type);
|
||||
if (t == "http")
|
||||
if (t == "http" || (t == "socks" && as_string(outb.version) == "4") || t == "block")
|
||||
return false;
|
||||
if (t == "selector" || t == "urltest") {
|
||||
if (type(outb.outbounds) == "array" && length(outb.outbounds) > 0) {
|
||||
let has_udp = false;
|
||||
for (let nested_tag in outb.outbounds) {
|
||||
if (outbound_supports_udp(config, nested_tag)) {
|
||||
has_udp = true;
|
||||
break;
|
||||
}
|
||||
if (!outbound_supports_udp(config, nested_tag, visited))
|
||||
return false;
|
||||
}
|
||||
return has_udp;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
function push_section_route_rule(config, rule, target_outbound) {
|
||||
@@ -1860,5 +1864,7 @@ return {
|
||||
enabled_sections,
|
||||
enabled_servers,
|
||||
section_by_name,
|
||||
add_server_routes
|
||||
add_server_routes,
|
||||
outbound_supports_udp,
|
||||
push_section_route_rule
|
||||
};
|
||||
|
||||
@@ -204,11 +204,18 @@ function remember_outbound_metadata(state, tag_name, display_name, outbound) {
|
||||
state.outboundMetadata.protocols[tag_name] = protocol;
|
||||
|
||||
let transport = lc(as_string(object_or_empty(outbound.transport).type || ""));
|
||||
if (transport == "" || transport == "raw")
|
||||
transport = "tcp";
|
||||
else if (transport == "h2")
|
||||
if (transport == "h2")
|
||||
transport = "http";
|
||||
state.outboundMetadata.transports[tag_name] = transport;
|
||||
else if (transport == "raw")
|
||||
transport = "tcp";
|
||||
else if (transport == "" && protocol != "" &&
|
||||
protocol != "direct" && protocol != "wireguard" &&
|
||||
protocol != "hysteria" && protocol != "hysteria2" && protocol != "tuic" &&
|
||||
protocol != "dns" && protocol != "block" &&
|
||||
protocol != "selector" && protocol != "urltest")
|
||||
transport = "tcp";
|
||||
if (transport != "")
|
||||
state.outboundMetadata.transports[tag_name] = transport;
|
||||
|
||||
let tls = type(outbound.tls) == "object" ? outbound.tls : null;
|
||||
let security = "none";
|
||||
|
||||
@@ -1065,7 +1065,8 @@ function write_user_agent_candidates(path, configured_user_agent, preferred_user
|
||||
function write_empty_outbound_metadata() {
|
||||
write_stdout_json({
|
||||
names: {},
|
||||
countries: {}
|
||||
countries: {},
|
||||
transports: {}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1155,6 +1156,9 @@ function normalize_cache(cache, section, format_version) {
|
||||
cache.outboundMetadata = object_or_empty(cache.outboundMetadata);
|
||||
cache.outboundMetadata.names = object_or_empty(cache.outboundMetadata.names);
|
||||
cache.outboundMetadata.countries = object_or_empty(cache.outboundMetadata.countries);
|
||||
cache.outboundMetadata.transports = object_or_empty(cache.outboundMetadata.transports);
|
||||
cache.outboundMetadata.protocols = object_or_empty(cache.outboundMetadata.protocols);
|
||||
cache.outboundMetadata.securities = object_or_empty(cache.outboundMetadata.securities);
|
||||
cache.servers = object_or_empty(cache.servers);
|
||||
cache.urltestCandidateTags = array_or_empty(cache.urltestCandidateTags);
|
||||
cache.urltestGroups = object_or_empty(cache.urltestGroups);
|
||||
@@ -1179,9 +1183,13 @@ function save_cache(cache_dir, section, format_version, cache) {
|
||||
|
||||
function write_outbound_metadata(cache_dir, format_version, section, names_path, countries_path, servers_path) {
|
||||
let cache = load_cache(cache_dir, section);
|
||||
let previous_metadata = object_or_empty(cache.outboundMetadata);
|
||||
cache.outboundMetadata = {
|
||||
names: object_or_empty(read_json(names_path)),
|
||||
countries: object_or_empty(read_json(countries_path))
|
||||
countries: object_or_empty(read_json(countries_path)),
|
||||
transports: object_or_empty(previous_metadata.transports),
|
||||
protocols: object_or_empty(previous_metadata.protocols),
|
||||
securities: object_or_empty(previous_metadata.securities)
|
||||
};
|
||||
cache.servers = object_or_empty(read_json(servers_path));
|
||||
save_cache(cache_dir, section, format_version, cache);
|
||||
@@ -1354,12 +1362,14 @@ function get_outbound_metadata(cache_dir, section, legacy_path) {
|
||||
metadata = object_or_empty(metadata);
|
||||
let names = object_or_empty(metadata.names);
|
||||
let countries = object_or_empty(metadata.countries);
|
||||
let transports = object_or_empty(metadata.transports);
|
||||
let candidate_tags = array_or_empty(cache.urltestCandidateTags);
|
||||
let groups = object_or_empty(cache.urltestGroups);
|
||||
let priority_groups = object_or_empty(cache.priorityGroups);
|
||||
let result = {
|
||||
names: {},
|
||||
countries: {}
|
||||
countries: {},
|
||||
transports: {}
|
||||
};
|
||||
|
||||
if (length(candidate_tags) > 0) {
|
||||
@@ -1371,6 +1381,8 @@ function get_outbound_metadata(cache_dir, section, legacy_path) {
|
||||
result.names[tag] = names[tag];
|
||||
if (countries[tag] != null)
|
||||
result.countries[tag] = countries[tag];
|
||||
if (transports[tag] != null)
|
||||
result.transports[tag] = transports[tag];
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -1382,6 +1394,10 @@ function get_outbound_metadata(cache_dir, section, legacy_path) {
|
||||
if (!groups[tag] && !priority_groups[tag])
|
||||
result.countries[tag] = country;
|
||||
}
|
||||
for (let tag, transport in transports) {
|
||||
if (!groups[tag] && !priority_groups[tag])
|
||||
result.transports[tag] = transport;
|
||||
}
|
||||
}
|
||||
|
||||
write_stdout_json(result);
|
||||
|
||||
Reference in New Issue
Block a user