113 lines
2.7 KiB
Go
113 lines
2.7 KiB
Go
package app
|
|
|
|
import (
|
|
"net"
|
|
"net/netip"
|
|
"net/url"
|
|
"strings"
|
|
)
|
|
|
|
func convertLegacyDNSServer(address string) (map[string]any, string, bool) {
|
|
a := strings.TrimSpace(address)
|
|
if a == "" {
|
|
return nil, "empty address", false
|
|
}
|
|
low := strings.ToLower(a)
|
|
switch low {
|
|
case "local":
|
|
return map[string]any{"type": "local"}, "", true
|
|
case "fakeip":
|
|
return map[string]any{"type": "fakeip"}, "", true
|
|
}
|
|
if strings.HasPrefix(low, "rcode://") {
|
|
return nil, "rcode:// legacy server is not auto-migrated (use dns rule action)", false
|
|
}
|
|
if strings.HasPrefix(low, "dhcp://") {
|
|
iface := strings.TrimSpace(a[len("dhcp://"):])
|
|
out := map[string]any{"type": "dhcp"}
|
|
if iface != "" && !strings.EqualFold(iface, "auto") {
|
|
out["interface"] = iface
|
|
}
|
|
return out, "", true
|
|
}
|
|
|
|
if strings.Contains(a, "://") {
|
|
u, err := url.Parse(a)
|
|
if err != nil {
|
|
return nil, "invalid URL address", false
|
|
}
|
|
scheme := strings.ToLower(strings.TrimSpace(u.Scheme))
|
|
host := strings.TrimSpace(u.Hostname())
|
|
if host == "" {
|
|
return nil, "empty host in URL address", false
|
|
}
|
|
port := parsePort(u.Port())
|
|
out := map[string]any{}
|
|
switch scheme {
|
|
case "tcp", "udp":
|
|
out["type"] = scheme
|
|
case "tls":
|
|
out["type"] = "tls"
|
|
case "quic":
|
|
out["type"] = "quic"
|
|
case "https":
|
|
out["type"] = "https"
|
|
if p := strings.TrimSpace(u.EscapedPath()); p != "" && p != "/dns-query" {
|
|
out["path"] = p
|
|
}
|
|
case "h3":
|
|
out["type"] = "h3"
|
|
if p := strings.TrimSpace(u.EscapedPath()); p != "" && p != "/dns-query" {
|
|
out["path"] = p
|
|
}
|
|
default:
|
|
return nil, "unsupported address scheme: " + scheme, false
|
|
}
|
|
out["server"] = host
|
|
if port > 0 {
|
|
out["server_port"] = port
|
|
}
|
|
return out, "", true
|
|
}
|
|
|
|
// Plain host/ip, optional :port => UDP.
|
|
host, port := splitHostPortLoose(a)
|
|
if host == "" {
|
|
return nil, "invalid host", false
|
|
}
|
|
out := map[string]any{
|
|
"type": "udp",
|
|
"server": host,
|
|
}
|
|
if port > 0 {
|
|
out["server_port"] = port
|
|
}
|
|
return out, "", true
|
|
}
|
|
|
|
func splitHostPortLoose(raw string) (string, int) {
|
|
s := strings.TrimSpace(strings.Trim(raw, "[]"))
|
|
if s == "" {
|
|
return "", 0
|
|
}
|
|
// IPv6 literal without brackets can't carry optional port unambiguously here.
|
|
if strings.Count(s, ":") > 1 && !strings.Contains(raw, "]") {
|
|
addr, err := netip.ParseAddr(s)
|
|
if err != nil || !addr.Is6() {
|
|
return "", 0
|
|
}
|
|
return s, 0
|
|
}
|
|
if h, p, err := net.SplitHostPort(raw); err == nil {
|
|
return strings.TrimSpace(strings.Trim(h, "[]")), parsePort(p)
|
|
}
|
|
if i := strings.LastIndex(s, ":"); i > 0 && strings.Count(s, ":") == 1 {
|
|
host := strings.TrimSpace(s[:i])
|
|
port := parsePort(s[i+1:])
|
|
if host != "" && port > 0 {
|
|
return host, port
|
|
}
|
|
}
|
|
return s, 0
|
|
}
|