corrige detecção de endereços

This commit is contained in:
2026-09-16 00:31:37 -03:00
parent 1a5a446b5c
commit 6c16f0da7a
4 changed files with 78 additions and 23 deletions
+27 -1
View File
@@ -5,16 +5,30 @@ import (
"fmt"
"os"
"path/filepath"
"strings"
"github.com/BurntSushi/toml"
)
const DefaultIntervalMinutes = 5
// RecordType identifies which DNS record family an entry updates. Hurricane
// Electric's dynamic DNS API cannot infer this from the submitted address
// alone, so it must be configured explicitly per entry.
type RecordType string
const (
RecordTypeA RecordType = "A"
RecordTypeAAAA RecordType = "AAAA"
)
// Entry describes a single Hurricane Electric dynamic DNS hostname to keep updated.
type Entry struct {
Hostname string `toml:"hostname"`
Token string `toml:"token"`
// RecordType selects whether this entry updates the A (IPv4) or AAAA
// (IPv6) record. Defaults to A when omitted.
RecordType RecordType `toml:"record_type"`
}
// Config is the top-level he-dydns configuration.
@@ -46,13 +60,25 @@ func Load(path string) (*Config, error) {
if len(cfg.Entries) == 0 {
return nil, fmt.Errorf("config %s: no entries defined", path)
}
for i, e := range cfg.Entries {
for i := range cfg.Entries {
e := &cfg.Entries[i]
if e.Hostname == "" {
return nil, fmt.Errorf("config %s: entries[%d] missing hostname", path, i)
}
if e.Token == "" {
return nil, fmt.Errorf("config %s: entries[%d] missing token", path, i)
}
switch RecordType(strings.ToUpper(string(e.RecordType))) {
case "":
e.RecordType = RecordTypeA
case RecordTypeA:
e.RecordType = RecordTypeA
case RecordTypeAAAA:
e.RecordType = RecordTypeAAAA
default:
return nil, fmt.Errorf("config %s: entries[%d] invalid record_type %q (must be \"A\" or \"AAAA\")", path, i, e.RecordType)
}
}
if cfg.IntervalMinutes <= 0 {
+25 -6
View File
@@ -2,6 +2,7 @@
package network
import (
"context"
"fmt"
"io"
"net"
@@ -83,14 +84,21 @@ func isUsable(ip net.IP) bool {
func fromPublicServices() Addresses {
return Addresses{
IPv4: queryFirst(PublicIPv4Services, false),
IPv6: queryFirst(PublicIPv6Services, true),
IPv4: queryFirst(PublicIPv4Services, "tcp4", false),
IPv6: queryFirst(PublicIPv6Services, "tcp6", true),
}
}
func queryFirst(services []string, wantIPv6 bool) net.IP {
// queryFirst tries each service in order over a connection forced to the
// given network ("tcp4" or "tcp6"). Forcing the dial family avoids relying
// on the target hostname having only an A or only an AAAA record: some of
// these services answer on both families, and letting the OS pick (e.g. via
// Happy Eyeballs) could silently return the wrong address family. The
// resulting address family is still double-checked as a second safeguard.
func queryFirst(services []string, network string, wantIPv6 bool) net.IP {
client := httpClientForNetwork(network)
for _, url := range services {
ip, err := queryOne(url)
ip, err := queryOne(client, url)
if err != nil {
continue
}
@@ -103,8 +111,19 @@ func queryFirst(services []string, wantIPv6 bool) net.IP {
return nil
}
func queryOne(url string) (net.IP, error) {
client := http.Client{Timeout: httpTimeout}
func httpClientForNetwork(network string) *http.Client {
dialer := net.Dialer{Timeout: httpTimeout}
return &http.Client{
Timeout: httpTimeout,
Transport: &http.Transport{
DialContext: func(ctx context.Context, _, addr string) (net.Conn, error) {
return dialer.DialContext(ctx, network, addr)
},
},
}
}
func queryOne(client *http.Client, url string) (net.IP, error) {
resp, err := client.Get(url)
if err != nil {
return nil, err
+18 -14
View File
@@ -4,6 +4,7 @@ package updater
import (
"fmt"
"log"
"net"
"git.aehoo.net/alphard/he-dydns/internal/config"
"git.aehoo.net/alphard/he-dydns/internal/hedns"
@@ -34,21 +35,24 @@ func Run(cfg *config.Config) error {
var failures int
for _, entry := range cfg.Entries {
if addrs.IPv4 != nil {
if err := hedns.Update(entry.Hostname, entry.Token, addrs.IPv4); err != nil {
log.Printf("error: %v", err)
failures++
} else {
log.Printf("updated %s -> %s", entry.Hostname, addrs.IPv4)
}
var ip net.IP
switch entry.RecordType {
case config.RecordTypeAAAA:
ip = addrs.IPv6
default:
ip = addrs.IPv4
}
if addrs.IPv6 != nil {
if err := hedns.Update(entry.Hostname, entry.Token, addrs.IPv6); err != nil {
log.Printf("error: %v", err)
failures++
} else {
log.Printf("updated %s -> %s", entry.Hostname, addrs.IPv6)
}
if ip == nil {
log.Printf("no %s address available for %s; skipping", entry.RecordType, entry.Hostname)
continue
}
if err := hedns.Update(entry.Hostname, entry.Token, ip); err != nil {
log.Printf("error: %v", err)
failures++
} else {
log.Printf("updated %s (%s) -> %s", entry.Hostname, entry.RecordType, ip)
}
}