Compare commits

..
2 Commits
7 changed files with 70 additions and 19 deletions
+4 -4
View File
@@ -199,7 +199,7 @@ Também é possível editar diretamente o `config.json`:
```json ```json
"dnstt": { "dnstt": {
"domain": "t.example.com", "domain": "t.example.com",
"udp_listen": "[::]:5300", "udp_listen": "0.0.0.0:5300",
"privkey_file": "/opt/sshpanel/dnstt.key", "privkey_file": "/opt/sshpanel/dnstt.key",
"auto_restart_interval": "6h", "auto_restart_interval": "6h",
"auto_restart_grace": "2s" "auto_restart_grace": "2s"
@@ -219,7 +219,7 @@ Exemplo:
"t.example.com", "t.example.com",
"t.local.lan" "t.local.lan"
], ],
"udp_listen": "[::]:5300", "udp_listen": "0.0.0.0:5300",
"privkey_file": "/opt/sshpanel/dnstt.key" "privkey_file": "/opt/sshpanel/dnstt.key"
} }
``` ```
@@ -738,7 +738,7 @@ You can also edit `config.json` directly:
```json ```json
"dnstt": { "dnstt": {
"domain": "t.example.com", "domain": "t.example.com",
"udp_listen": "[::]:5300", "udp_listen": "0.0.0.0:5300",
"privkey_file": "/opt/sshpanel/dnstt.key", "privkey_file": "/opt/sshpanel/dnstt.key",
"auto_restart_interval": "6h", "auto_restart_interval": "6h",
"auto_restart_grace": "2s" "auto_restart_grace": "2s"
@@ -758,7 +758,7 @@ Example:
"t.example.com", "t.example.com",
"t.local.lan" "t.local.lan"
], ],
"udp_listen": "[::]:5300", "udp_listen": "0.0.0.0:5300",
"privkey_file": "/opt/sshpanel/dnstt.key" "privkey_file": "/opt/sshpanel/dnstt.key"
} }
``` ```
+2 -2
View File
@@ -426,7 +426,7 @@ async function loadManagedServerConfig(id) {
toggleManagedDnsttFields(hasDnstt); toggleManagedDnsttFields(hasDnstt);
const d = c.dnstt || {}; const d = c.dnstt || {};
document.getElementById("managedCfgDnsttDomains").value = dnsttDomainsText(d); document.getElementById("managedCfgDnsttDomains").value = dnsttDomainsText(d);
document.getElementById("managedCfgDnsttUDP").value = d.udp_listen || ""; document.getElementById("managedCfgDnsttUDP").value = d.udp_listen || "0.0.0.0:5300";
document.getElementById("managedCfgDnsttFakeEnabled").checked = !!d.fake_dns_enabled; document.getElementById("managedCfgDnsttFakeEnabled").checked = !!d.fake_dns_enabled;
document.getElementById("managedCfgDnsttFakeListen").value = d.fake_dns_listen || ""; document.getElementById("managedCfgDnsttFakeListen").value = d.fake_dns_listen || "";
document.getElementById("managedCfgDnsttFakeDomain").value = d.fake_dns_domain || "t.local.lan"; document.getElementById("managedCfgDnsttFakeDomain").value = d.fake_dns_domain || "t.local.lan";
@@ -494,7 +494,7 @@ function managedConfigFromForm() {
dnstt: document.getElementById("managedCfgDnsttEnabled").checked ? { dnstt: document.getElementById("managedCfgDnsttEnabled").checked ? {
domain: dnsttDomains[0] || "", domain: dnsttDomains[0] || "",
domains: dnsttDomains, domains: dnsttDomains,
udp_listen: document.getElementById("managedCfgDnsttUDP").value.trim(), udp_listen: document.getElementById("managedCfgDnsttUDP").value.trim() || "0.0.0.0:5300",
fake_dns_enabled: document.getElementById("managedCfgDnsttFakeEnabled").checked, fake_dns_enabled: document.getElementById("managedCfgDnsttFakeEnabled").checked,
fake_dns_listen: document.getElementById("managedCfgDnsttFakeListen").value.trim(), fake_dns_listen: document.getElementById("managedCfgDnsttFakeListen").value.trim(),
fake_dns_domain: document.getElementById("managedCfgDnsttFakeDomain").value.trim(), fake_dns_domain: document.getElementById("managedCfgDnsttFakeDomain").value.trim(),
+2 -2
View File
@@ -122,7 +122,7 @@ async function loadServerConfig() {
toggleDnsttFields(hasDnstt); toggleDnsttFields(hasDnstt);
const d = c.dnstt || {}; const d = c.dnstt || {};
document.getElementById("cfgDnsttDomains").value = dnsttDomainsText(d); document.getElementById("cfgDnsttDomains").value = dnsttDomainsText(d);
document.getElementById("cfgDnsttUDP").value = d.udp_listen || ""; document.getElementById("cfgDnsttUDP").value = d.udp_listen || "0.0.0.0:5300";
document.getElementById("cfgDnsttFakeEnabled").checked = !!d.fake_dns_enabled; document.getElementById("cfgDnsttFakeEnabled").checked = !!d.fake_dns_enabled;
document.getElementById("cfgDnsttFakeListen").value = d.fake_dns_listen || ""; document.getElementById("cfgDnsttFakeListen").value = d.fake_dns_listen || "";
document.getElementById("cfgDnsttFakeDomain").value = d.fake_dns_domain || "t.local.lan"; document.getElementById("cfgDnsttFakeDomain").value = d.fake_dns_domain || "t.local.lan";
@@ -201,7 +201,7 @@ async function saveServerConfig() {
dnstt: document.getElementById("cfgDnsttEnabled").checked ? { dnstt: document.getElementById("cfgDnsttEnabled").checked ? {
domain: dnsttDomains[0] || "", domain: dnsttDomains[0] || "",
domains: dnsttDomains, domains: dnsttDomains,
udp_listen: document.getElementById("cfgDnsttUDP").value.trim(), udp_listen: document.getElementById("cfgDnsttUDP").value.trim() || "0.0.0.0:5300",
fake_dns_enabled: document.getElementById("cfgDnsttFakeEnabled").checked, fake_dns_enabled: document.getElementById("cfgDnsttFakeEnabled").checked,
fake_dns_listen: document.getElementById("cfgDnsttFakeListen").value.trim(), fake_dns_listen: document.getElementById("cfgDnsttFakeListen").value.trim(),
fake_dns_domain: document.getElementById("cfgDnsttFakeDomain").value.trim(), fake_dns_domain: document.getElementById("cfgDnsttFakeDomain").value.trim(),
+2 -2
View File
@@ -867,7 +867,7 @@
</div> </div>
<div id="managedDnsttFields" class="form-grid" style="opacity:.4;pointer-events:none;"> <div id="managedDnsttFields" class="form-grid" style="opacity:.4;pointer-events:none;">
<div class="field" style="grid-column:1/-1"><label>NS / Root Domains <span class="hint">one per line</span></label><textarea id="managedCfgDnsttDomains" rows="3" placeholder="t.example.com&#10;t.local.lan"></textarea></div> <div class="field" style="grid-column:1/-1"><label>NS / Root Domains <span class="hint">one per line</span></label><textarea id="managedCfgDnsttDomains" rows="3" placeholder="t.example.com&#10;t.local.lan"></textarea></div>
<div class="field"><label>UDP Listen</label><input type="text" id="managedCfgDnsttUDP" placeholder="[::]:5300"/></div> <div class="field"><label>UDP Listen</label><input type="text" id="managedCfgDnsttUDP" placeholder="0.0.0.0:5300"/></div>
<label style="font-size:.73rem;display:flex;align-items:center;gap:5px;cursor:pointer;grid-column:1/-1"><input type="checkbox" id="managedCfgDnsttFakeEnabled"/> Built-in Local DNS / Fake DNS</label> <label style="font-size:.73rem;display:flex;align-items:center;gap:5px;cursor:pointer;grid-column:1/-1"><input type="checkbox" id="managedCfgDnsttFakeEnabled"/> Built-in Local DNS / Fake DNS</label>
<div class="field"><label>Local DNS Listen <span class="hint">IPv6 ok</span></label><input type="text" id="managedCfgDnsttFakeListen" placeholder="[2001:db8::1234]:53"/></div> <div class="field"><label>Local DNS Listen <span class="hint">IPv6 ok</span></label><input type="text" id="managedCfgDnsttFakeListen" placeholder="[2001:db8::1234]:53"/></div>
<div class="field"><label>Local DNS Domain</label><input type="text" id="managedCfgDnsttFakeDomain" placeholder="t.local.lan"/></div> <div class="field"><label>Local DNS Domain</label><input type="text" id="managedCfgDnsttFakeDomain" placeholder="t.local.lan"/></div>
@@ -1344,7 +1344,7 @@
</div> </div>
<div class="field"> <div class="field">
<label>UDP Listen</label> <label>UDP Listen</label>
<input type="text" id="cfgDnsttUDP" placeholder="[::]:5300"/> <input type="text" id="cfgDnsttUDP" placeholder="0.0.0.0:5300"/>
</div> </div>
<label style="font-size:.73rem;display:flex;align-items:center;gap:5px;cursor:pointer;grid-column:1/-1"> <label style="font-size:.73rem;display:flex;align-items:center;gap:5px;cursor:pointer;grid-column:1/-1">
<input type="checkbox" id="cfgDnsttFakeEnabled"/> Built-in Local DNS / Fake DNS <input type="checkbox" id="cfgDnsttFakeEnabled"/> Built-in Local DNS / Fake DNS
+28 -4
View File
@@ -11,7 +11,7 @@ import (
const ( const (
defaultMainListen = "0.0.0.0:80" defaultMainListen = "0.0.0.0:80"
defaultExtraListen = "0.0.0.0:8080" defaultExtraListen = "0.0.0.0:8080"
defaultDNSTTListen = "[::]:5300" defaultDNSTTListen = "0.0.0.0:5300"
defaultUDPGWListen = "0.0.0.0:7400" defaultUDPGWListen = "0.0.0.0:7400"
) )
@@ -109,9 +109,10 @@ func normalizeRuntimePorts(cfg *Config) []string {
} }
} }
cfg.DNSTT.UDPListen = strings.TrimSpace(cfg.DNSTT.UDPListen) var migratedLegacyDNSTTWildcard bool
if cfg.DNSTT.UDPListen == "" { cfg.DNSTT.UDPListen, migratedLegacyDNSTTWildcard = normalizeDNSTTListenDefault(cfg.DNSTT.UDPListen)
cfg.DNSTT.UDPListen = defaultDNSTTListen if migratedLegacyDNSTTWildcard {
warn("DNSTT legacy default [::]:5300 is IPv6-only; using IPv4 default %s", cfg.DNSTT.UDPListen)
} }
if err := udpAddrAvailableForDNSTT(cfg.DNSTT.UDPListen); err != nil { if err := udpAddrAvailableForDNSTT(cfg.DNSTT.UDPListen); err != nil {
old := cfg.DNSTT.UDPListen old := cfg.DNSTT.UDPListen
@@ -295,6 +296,29 @@ func normalizeDNSTTDomainList(primary string, domains []string) []string {
return out return out
} }
// normalizeDNSTTListenDefault keeps explicit IPv4 and concrete IPv6 listeners,
// but migrates the old wildcard IPv6 default. listenDNSTTPacket deliberately
// opens IPv6 addresses with udp6, so [::]:5300 never receives IPv4 queries.
// Existing installations commonly inherited that value from the old default;
// moving only that wildcard/default-port combination makes them work after an
// update without changing intentionally selected IPv6 interface addresses.
func normalizeDNSTTListenDefault(addr string) (string, bool) {
addr = strings.TrimSpace(addr)
if addr == "" {
return defaultDNSTTListen, false
}
host, port, err := net.SplitHostPort(addr)
if err != nil || port != "5300" {
return addr, false
}
ip := net.ParseIP(strings.Trim(host, "[]"))
if ip != nil && ip.To4() == nil && ip.IsUnspecified() {
return defaultDNSTTListen, true
}
return addr, false
}
func udpAddrAvailableForDNSTT(addr string) error { func udpAddrAvailableForDNSTT(addr string) error {
if addr == "" { if addr == "" {
return nil return nil
+30
View File
@@ -0,0 +1,30 @@
package main
import "testing"
func TestNormalizeDNSTTListenDefault(t *testing.T) {
tests := []struct {
name string
input string
want string
migrated bool
}{
{name: "empty uses IPv4 default", input: "", want: "0.0.0.0:5300"},
{name: "whitespace uses IPv4 default", input: " ", want: "0.0.0.0:5300"},
{name: "legacy IPv6 wildcard migrates", input: "[::]:5300", want: "0.0.0.0:5300", migrated: true},
{name: "expanded legacy wildcard migrates", input: "[0:0:0:0:0:0:0:0]:5300", want: "0.0.0.0:5300", migrated: true},
{name: "explicit IPv4 remains", input: "192.0.2.10:53", want: "192.0.2.10:53"},
{name: "IPv4 wildcard remains", input: "0.0.0.0:5300", want: "0.0.0.0:5300"},
{name: "concrete IPv6 remains", input: "[2001:db8::10]:53", want: "[2001:db8::10]:53"},
{name: "IPv6 wildcard on custom port remains", input: "[::]:5301", want: "[::]:5301"},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, migrated := normalizeDNSTTListenDefault(tt.input)
if got != tt.want || migrated != tt.migrated {
t.Fatalf("normalizeDNSTTListenDefault(%q) = (%q, %v), want (%q, %v)", tt.input, got, migrated, tt.want, tt.migrated)
}
})
}
}
+1 -4
View File
@@ -595,11 +595,8 @@ func startDNSTTInstance(cfg *DNSTTConfig, sshConf *ssh.ServerConfig) error {
return err return err
} }
udpListen := cfg.UDPListen udpListen, _ := normalizeDNSTTListenDefault(cfg.UDPListen)
if udpListen == "" {
udpListen = defaultDNSTTListen
cfg.UDPListen = udpListen cfg.UDPListen = udpListen
}
fakeDomains := domains fakeDomains := domains
if cfg.FakeDNSEnabled { if cfg.FakeDNSEnabled {