2025-08-12 19:38:17 +03:00
|
|
|
package resolver
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"context"
|
2026-01-08 22:38:17 +02:00
|
|
|
"crypto/x509"
|
2025-08-12 19:38:17 +03:00
|
|
|
"fmt"
|
|
|
|
|
"net/netip"
|
|
|
|
|
|
2026-04-13 21:23:26 +03:00
|
|
|
"github.com/Alexey71/go-multierror"
|
2025-08-12 19:38:17 +03:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
type LookupNetIPer interface {
|
|
|
|
|
LookupNetIP(context.Context, string, string) ([]netip.Addr, error)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type FastResolver struct {
|
|
|
|
|
upstreams []LookupNetIPer
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-08 22:38:17 +02:00
|
|
|
func FastFromURLs(caPool *x509.CertPool, urls ...string) (LookupNetIPer, error) {
|
2025-08-12 19:38:17 +03:00
|
|
|
resolvers := make([]LookupNetIPer, 0, len(urls))
|
|
|
|
|
for i, u := range urls {
|
2026-01-08 22:38:17 +02:00
|
|
|
res, err := FromURL(u, caPool)
|
2025-08-12 19:38:17 +03:00
|
|
|
if err != nil {
|
|
|
|
|
return nil, fmt.Errorf("unable to construct resolver #%d (%q): %w", i, u, err)
|
|
|
|
|
}
|
|
|
|
|
resolvers = append(resolvers, res)
|
|
|
|
|
}
|
2025-09-23 23:59:41 +03:00
|
|
|
if len(resolvers) == 1 {
|
|
|
|
|
return resolvers[0], nil
|
|
|
|
|
}
|
2025-08-12 19:38:17 +03:00
|
|
|
return NewFastResolver(resolvers...), nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func NewFastResolver(resolvers ...LookupNetIPer) *FastResolver {
|
|
|
|
|
return &FastResolver{
|
|
|
|
|
upstreams: resolvers,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (r FastResolver) LookupNetIP(ctx context.Context, network, host string) ([]netip.Addr, error) {
|
2025-09-23 23:19:28 +03:00
|
|
|
ctx, cl := context.WithCancel(ctx)
|
|
|
|
|
defer cl()
|
2025-09-23 23:02:17 +03:00
|
|
|
errors := make(chan error)
|
|
|
|
|
success := make(chan []netip.Addr)
|
2025-08-12 19:38:17 +03:00
|
|
|
for _, res := range r.upstreams {
|
|
|
|
|
go func(res LookupNetIPer) {
|
|
|
|
|
addrs, err := res.LookupNetIP(ctx, network, host)
|
2025-09-23 23:02:17 +03:00
|
|
|
if err == nil {
|
|
|
|
|
select {
|
|
|
|
|
case success <- addrs:
|
2025-09-23 23:19:28 +03:00
|
|
|
case <-ctx.Done():
|
2025-09-23 23:02:17 +03:00
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
select {
|
2025-09-23 23:19:28 +03:00
|
|
|
case errors <- err:
|
|
|
|
|
case <-ctx.Done():
|
2025-09-23 23:02:17 +03:00
|
|
|
}
|
|
|
|
|
}
|
2025-08-12 19:38:17 +03:00
|
|
|
}(res)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var resErr error
|
2025-09-23 23:02:17 +03:00
|
|
|
for _ = range r.upstreams {
|
|
|
|
|
select {
|
|
|
|
|
case <-ctx.Done():
|
|
|
|
|
return nil, ctx.Err()
|
|
|
|
|
case resAddrs := <-success:
|
|
|
|
|
return resAddrs, nil
|
|
|
|
|
case err := <-errors:
|
|
|
|
|
resErr = multierror.Append(resErr, err)
|
2025-08-12 19:38:17 +03:00
|
|
|
}
|
|
|
|
|
}
|
2025-09-23 23:02:17 +03:00
|
|
|
return nil, resErr
|
2025-08-12 19:38:17 +03:00
|
|
|
}
|