corrige detecção de endereços
This commit is contained in:
+8
-2
@@ -8,10 +8,16 @@
|
|||||||
# Optional: how often to update in daemon mode, in minutes. Default: 5.
|
# Optional: how often to update in daemon mode, in minutes. Default: 5.
|
||||||
# interval_minutes = 5
|
# interval_minutes = 5
|
||||||
|
|
||||||
|
# record_type selects which DNS record this entry updates: "A" (IPv4) or
|
||||||
|
# "AAAA" (IPv6). HE's dynamic DNS API cannot infer this from the address
|
||||||
|
# submitted, so it must be set explicitly. Defaults to "A" when omitted.
|
||||||
|
|
||||||
[[entries]]
|
[[entries]]
|
||||||
hostname = "home.example.com"
|
hostname = "home.example.com"
|
||||||
token = "your-he-dyndns-token"
|
token = "your-he-dyndns-token"
|
||||||
|
record_type = "A"
|
||||||
|
|
||||||
[[entries]]
|
[[entries]]
|
||||||
hostname = "home-v6.example.com"
|
hostname = "home.example.com"
|
||||||
token = "another-he-dyndns-token"
|
token = "your-he-dyndns-token"
|
||||||
|
record_type = "AAAA"
|
||||||
|
|||||||
@@ -5,16 +5,30 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/BurntSushi/toml"
|
"github.com/BurntSushi/toml"
|
||||||
)
|
)
|
||||||
|
|
||||||
const DefaultIntervalMinutes = 5
|
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.
|
// Entry describes a single Hurricane Electric dynamic DNS hostname to keep updated.
|
||||||
type Entry struct {
|
type Entry struct {
|
||||||
Hostname string `toml:"hostname"`
|
Hostname string `toml:"hostname"`
|
||||||
Token string `toml:"token"`
|
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.
|
// Config is the top-level he-dydns configuration.
|
||||||
@@ -46,13 +60,25 @@ func Load(path string) (*Config, error) {
|
|||||||
if len(cfg.Entries) == 0 {
|
if len(cfg.Entries) == 0 {
|
||||||
return nil, fmt.Errorf("config %s: no entries defined", path)
|
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 == "" {
|
if e.Hostname == "" {
|
||||||
return nil, fmt.Errorf("config %s: entries[%d] missing hostname", path, i)
|
return nil, fmt.Errorf("config %s: entries[%d] missing hostname", path, i)
|
||||||
}
|
}
|
||||||
if e.Token == "" {
|
if e.Token == "" {
|
||||||
return nil, fmt.Errorf("config %s: entries[%d] missing token", path, i)
|
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 {
|
if cfg.IntervalMinutes <= 0 {
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
package network
|
package network
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
@@ -83,14 +84,21 @@ func isUsable(ip net.IP) bool {
|
|||||||
|
|
||||||
func fromPublicServices() Addresses {
|
func fromPublicServices() Addresses {
|
||||||
return Addresses{
|
return Addresses{
|
||||||
IPv4: queryFirst(PublicIPv4Services, false),
|
IPv4: queryFirst(PublicIPv4Services, "tcp4", false),
|
||||||
IPv6: queryFirst(PublicIPv6Services, true),
|
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 {
|
for _, url := range services {
|
||||||
ip, err := queryOne(url)
|
ip, err := queryOne(client, url)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@@ -103,8 +111,19 @@ func queryFirst(services []string, wantIPv6 bool) net.IP {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func queryOne(url string) (net.IP, error) {
|
func httpClientForNetwork(network string) *http.Client {
|
||||||
client := http.Client{Timeout: httpTimeout}
|
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)
|
resp, err := client.Get(url)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|||||||
+16
-12
@@ -4,6 +4,7 @@ package updater
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
|
"net"
|
||||||
|
|
||||||
"git.aehoo.net/alphard/he-dydns/internal/config"
|
"git.aehoo.net/alphard/he-dydns/internal/config"
|
||||||
"git.aehoo.net/alphard/he-dydns/internal/hedns"
|
"git.aehoo.net/alphard/he-dydns/internal/hedns"
|
||||||
@@ -34,21 +35,24 @@ func Run(cfg *config.Config) error {
|
|||||||
|
|
||||||
var failures int
|
var failures int
|
||||||
for _, entry := range cfg.Entries {
|
for _, entry := range cfg.Entries {
|
||||||
if addrs.IPv4 != nil {
|
var ip net.IP
|
||||||
if err := hedns.Update(entry.Hostname, entry.Token, addrs.IPv4); err != nil {
|
switch entry.RecordType {
|
||||||
|
case config.RecordTypeAAAA:
|
||||||
|
ip = addrs.IPv6
|
||||||
|
default:
|
||||||
|
ip = addrs.IPv4
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
log.Printf("error: %v", err)
|
||||||
failures++
|
failures++
|
||||||
} else {
|
} else {
|
||||||
log.Printf("updated %s -> %s", entry.Hostname, addrs.IPv4)
|
log.Printf("updated %s (%s) -> %s", entry.Hostname, entry.RecordType, ip)
|
||||||
}
|
|
||||||
}
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user