feat: update Go module dependencies and enhance DNS resolution in refresh pipeline. Upgrade Go version to 1.24.0, add support for DNS over HTTPS using RFC8484 format, and improve error handling for domain resolution. Update UI components for better log entry presentation in Svelte.
CI / changes (push) Successful in 6s
CI / openapi (push) Has been skipped
CI / go (push) Successful in 1m36s
CI / docker-web (deploy/docker/evobgp-web/Dockerfile, , evobgp-web) (push) Successful in 1m4s
CI / docker-web (deploy/docker/evobgp-web/Dockerfile, evobgp-all, evobgp-web-all) (push) Successful in 1m4s
CI / docker-bird (push) Has been skipped
CI / bird2 (push) Successful in 16s
CI / docker-go-prime (push) Failing after 35s
CI / docker-go (deploy/docker/evobgp-agent/Dockerfile, , evobgp-agent) (push) Has been skipped
CI / docker-go (evobgp-all, 1, deploy/docker/gobinary/Dockerfile, , evobgp-all) (push) Has been skipped
CI / docker-go (evobgp-api, 1, deploy/docker/gobinary/Dockerfile, , evobgp-api) (push) Has been skipped
CI / docker-go (evobgp-deploy, 0, deploy/docker/gobinary/Dockerfile, , evobgp-deploy) (push) Has been skipped
CI / docker-go (evobgp-ingest, 0, deploy/docker/gobinary/Dockerfile, , evobgp-ingest) (push) Has been skipped
CI / docker-go (evobgp-node, 0, deploy/docker/gobinary/Dockerfile, , evobgp-node) (push) Has been skipped
CI / docker-go (evobgp-render, 0, deploy/docker/gobinary/Dockerfile, , evobgp-render) (push) Has been skipped
CI / docker-go (evobgp-scheduler, 0, deploy/docker/gobinary/Dockerfile, , evobgp-scheduler) (push) Has been skipped
CI / changes (push) Successful in 6s
CI / openapi (push) Has been skipped
CI / go (push) Successful in 1m36s
CI / docker-web (deploy/docker/evobgp-web/Dockerfile, , evobgp-web) (push) Successful in 1m4s
CI / docker-web (deploy/docker/evobgp-web/Dockerfile, evobgp-all, evobgp-web-all) (push) Successful in 1m4s
CI / docker-bird (push) Has been skipped
CI / bird2 (push) Successful in 16s
CI / docker-go-prime (push) Failing after 35s
CI / docker-go (deploy/docker/evobgp-agent/Dockerfile, , evobgp-agent) (push) Has been skipped
CI / docker-go (evobgp-all, 1, deploy/docker/gobinary/Dockerfile, , evobgp-all) (push) Has been skipped
CI / docker-go (evobgp-api, 1, deploy/docker/gobinary/Dockerfile, , evobgp-api) (push) Has been skipped
CI / docker-go (evobgp-deploy, 0, deploy/docker/gobinary/Dockerfile, , evobgp-deploy) (push) Has been skipped
CI / docker-go (evobgp-ingest, 0, deploy/docker/gobinary/Dockerfile, , evobgp-ingest) (push) Has been skipped
CI / docker-go (evobgp-node, 0, deploy/docker/gobinary/Dockerfile, , evobgp-node) (push) Has been skipped
CI / docker-go (evobgp-render, 0, deploy/docker/gobinary/Dockerfile, , evobgp-render) (push) Has been skipped
CI / docker-go (evobgp-scheduler, 0, deploy/docker/gobinary/Dockerfile, , evobgp-scheduler) (push) Has been skipped
This commit is contained in:
@@ -1,13 +1,16 @@
|
||||
package pipeline
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/netip"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/netip"
|
||||
"net/url"
|
||||
"os"
|
||||
"sort"
|
||||
@@ -20,6 +23,7 @@ import (
|
||||
"evobgp/internal/store"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/miekg/dns"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -264,7 +268,7 @@ func resolveDomainIPs(ctx context.Context, hc *http.Client, profile *store.DohPr
|
||||
return nil, nil
|
||||
}
|
||||
if profile == nil || strings.TrimSpace(profile.URL) == "" {
|
||||
return nil, nil
|
||||
return resolveDomainWithSystemDNS(ctx, host)
|
||||
}
|
||||
|
||||
timeout := 10 * time.Second
|
||||
@@ -274,13 +278,113 @@ func resolveDomainIPs(ctx context.Context, hc *http.Client, profile *store.DohPr
|
||||
dctx, cancel := context.WithTimeout(ctx, timeout)
|
||||
defer cancel()
|
||||
|
||||
// RFC8484 endpoint with JSON mode: ?name=<fqdn>&type=A/AAAA
|
||||
v4, err4 := resolveDomainWithDOHJSON(dctx, hc, strings.TrimSpace(profile.URL), host, "A")
|
||||
v6, err6 := resolveDomainWithDOHJSON(dctx, hc, strings.TrimSpace(profile.URL), host, "AAAA")
|
||||
if err4 != nil && err6 != nil {
|
||||
return nil, fmt.Errorf("doh failed for A and AAAA: %v; %v", err4, err6)
|
||||
baseURL := strings.TrimSpace(profile.URL)
|
||||
// Prefer RFC8484 dns-message transport. Some providers don't support dns-json.
|
||||
v4, err4 := resolveDomainWithDOHMessage(dctx, hc, baseURL, host, dns.TypeA)
|
||||
v6, err6 := resolveDomainWithDOHMessage(dctx, hc, baseURL, host, dns.TypeAAAA)
|
||||
if err4 != nil {
|
||||
// Fallback to JSON mode for providers that only expose dns-json.
|
||||
v4, err4 = resolveDomainWithDOHJSON(dctx, hc, baseURL, host, "A")
|
||||
}
|
||||
return uniqAddrs(append(v4, v6...)), nil
|
||||
if err6 != nil {
|
||||
v6, err6 = resolveDomainWithDOHJSON(dctx, hc, baseURL, host, "AAAA")
|
||||
}
|
||||
if err4 != nil && err6 != nil {
|
||||
// Some DoH providers return non-JSON responses (RFC8484 dns-message, HTML error pages, etc.).
|
||||
// Fall back to system resolver to avoid failing the whole module refresh.
|
||||
ips, fallbackErr := resolveDomainWithSystemDNS(dctx, host)
|
||||
if fallbackErr != nil {
|
||||
return nil, fmt.Errorf("doh failed for A and AAAA: %v; %v; fallback dns failed: %w", err4, err6, fallbackErr)
|
||||
}
|
||||
return ips, nil
|
||||
}
|
||||
out := uniqAddrs(append(v4, v6...))
|
||||
if len(out) > 0 {
|
||||
return out, nil
|
||||
}
|
||||
// If DoH succeeds but returns no A/AAAA records, attempt system resolver as best-effort fallback.
|
||||
ips, err := resolveDomainWithSystemDNS(dctx, host)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ips, nil
|
||||
}
|
||||
|
||||
func resolveDomainWithDOHMessage(ctx context.Context, hc *http.Client, baseURL, host string, qtype uint16) ([]netip.Addr, error) {
|
||||
msg := new(dns.Msg)
|
||||
msg.SetQuestion(dns.Fqdn(host), qtype)
|
||||
wire, err := msg.Pack()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
u, err := url.Parse(baseURL)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
q := u.Query()
|
||||
q.Set("dns", base64.RawURLEncoding.EncodeToString(wire))
|
||||
u.RawQuery = q.Encode()
|
||||
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, u.String(), nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req.Header.Set("Accept", "application/dns-message")
|
||||
resp, err := hc.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
body, _ := io.ReadAll(io.LimitReader(resp.Body, 1024))
|
||||
return nil, fmt.Errorf("doh dns-message status %s: %s", resp.Status, strings.TrimSpace(string(body)))
|
||||
}
|
||||
raw, err := io.ReadAll(io.LimitReader(resp.Body, 2<<20))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
parsed := new(dns.Msg)
|
||||
if err := parsed.Unpack(raw); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if parsed.Rcode != dns.RcodeSuccess {
|
||||
return nil, fmt.Errorf("doh rcode=%s", dns.RcodeToString[parsed.Rcode])
|
||||
}
|
||||
var out []netip.Addr
|
||||
for _, rr := range parsed.Answer {
|
||||
switch x := rr.(type) {
|
||||
case *dns.A:
|
||||
if qtype == dns.TypeA {
|
||||
if ip, ok := netip.AddrFromSlice(x.A.To4()); ok {
|
||||
out = append(out, ip.Unmap())
|
||||
}
|
||||
}
|
||||
case *dns.AAAA:
|
||||
if qtype == dns.TypeAAAA {
|
||||
if ip, ok := netip.AddrFromSlice(x.AAAA.To16()); ok {
|
||||
out = append(out, ip.Unmap())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Some providers may return JSON even on dns-message request.
|
||||
if len(out) == 0 && bytes.Contains(bytes.ToLower(raw), []byte(`"answer"`)) {
|
||||
qs := "A"
|
||||
if qtype == dns.TypeAAAA {
|
||||
qs = "AAAA"
|
||||
}
|
||||
return resolveDomainWithDOHJSON(ctx, hc, baseURL, host, qs)
|
||||
}
|
||||
return uniqAddrs(out), nil
|
||||
}
|
||||
|
||||
func resolveDomainWithSystemDNS(ctx context.Context, host string) ([]netip.Addr, error) {
|
||||
ips, err := net.DefaultResolver.LookupNetIP(ctx, "ip", host)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return uniqAddrs(ips), nil
|
||||
}
|
||||
|
||||
func resolveDomainWithDOHJSON(ctx context.Context, hc *http.Client, baseURL, host, qtype string) ([]netip.Addr, error) {
|
||||
|
||||
Reference in New Issue
Block a user