2025-08-12 19:38:17 +03:00
|
|
|
package resolver
|
|
|
|
|
|
|
|
|
|
import (
|
2026-01-08 22:38:17 +02:00
|
|
|
"crypto/tls"
|
|
|
|
|
"crypto/x509"
|
2025-08-12 19:38:17 +03:00
|
|
|
"errors"
|
|
|
|
|
"net"
|
2026-01-08 22:53:27 +02:00
|
|
|
"net/http"
|
2025-08-12 19:38:17 +03:00
|
|
|
"net/url"
|
|
|
|
|
"strings"
|
2026-01-08 22:53:27 +02:00
|
|
|
"time"
|
2025-08-12 19:38:17 +03:00
|
|
|
|
|
|
|
|
"github.com/ncruces/go-dns"
|
|
|
|
|
)
|
|
|
|
|
|
2026-01-08 22:38:17 +02:00
|
|
|
func FromURL(u string, caPool *x509.CertPool) (*net.Resolver, error) {
|
2025-09-24 00:32:48 +03:00
|
|
|
begin:
|
2025-08-12 19:38:17 +03:00
|
|
|
parsed, err := url.Parse(u)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
2025-08-16 02:52:52 +03:00
|
|
|
host := parsed.Hostname()
|
|
|
|
|
port := parsed.Port()
|
2025-09-23 23:37:29 +03:00
|
|
|
switch scheme := strings.ToLower(parsed.Scheme); scheme {
|
2025-09-24 00:32:48 +03:00
|
|
|
case "":
|
|
|
|
|
switch {
|
|
|
|
|
case strings.HasPrefix(u, "//"):
|
|
|
|
|
u = "dns:" + u
|
|
|
|
|
default:
|
|
|
|
|
u = "dns://" + u
|
|
|
|
|
}
|
|
|
|
|
goto begin
|
|
|
|
|
case "udp", "dns":
|
2025-08-12 19:38:17 +03:00
|
|
|
if port == "" {
|
|
|
|
|
port = "53"
|
|
|
|
|
}
|
|
|
|
|
return NewPlainResolver(net.JoinHostPort(host, port)), nil
|
|
|
|
|
case "tcp":
|
|
|
|
|
if port == "" {
|
|
|
|
|
port = "53"
|
|
|
|
|
}
|
|
|
|
|
return NewTCPResolver(net.JoinHostPort(host, port)), nil
|
2025-09-23 23:37:29 +03:00
|
|
|
case "http", "https", "doh":
|
2025-08-16 02:52:52 +03:00
|
|
|
if port == "" {
|
2025-09-23 23:37:29 +03:00
|
|
|
if scheme == "http" {
|
|
|
|
|
port = "80"
|
|
|
|
|
} else {
|
|
|
|
|
port = "443"
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if scheme == "doh" {
|
|
|
|
|
parsed.Scheme = "https"
|
|
|
|
|
u = parsed.String()
|
2025-08-16 02:52:52 +03:00
|
|
|
}
|
2026-01-08 22:53:27 +02:00
|
|
|
return dns.NewDoHResolver(u,
|
|
|
|
|
dns.DoHAddresses(net.JoinHostPort(host, port)),
|
|
|
|
|
dns.DoHTransport(&http.Transport{
|
|
|
|
|
MaxIdleConns: http.DefaultMaxIdleConnsPerHost,
|
|
|
|
|
IdleConnTimeout: 90 * time.Second,
|
|
|
|
|
TLSHandshakeTimeout: 10 * time.Second,
|
|
|
|
|
ForceAttemptHTTP2: true,
|
|
|
|
|
TLSClientConfig: &tls.Config{
|
|
|
|
|
RootCAs: caPool,
|
|
|
|
|
},
|
|
|
|
|
}),
|
|
|
|
|
)
|
2025-09-23 23:37:29 +03:00
|
|
|
case "tls", "dot":
|
2025-08-12 19:38:17 +03:00
|
|
|
if port == "" {
|
|
|
|
|
port = "853"
|
|
|
|
|
}
|
2025-08-16 02:52:52 +03:00
|
|
|
hp := net.JoinHostPort(host, port)
|
2026-01-08 22:38:17 +02:00
|
|
|
return dns.NewDoTResolver(hp,
|
|
|
|
|
dns.DoTAddresses(hp),
|
|
|
|
|
dns.DoTConfig(&tls.Config{
|
|
|
|
|
RootCAs: caPool,
|
|
|
|
|
}),
|
|
|
|
|
)
|
2025-08-12 19:38:17 +03:00
|
|
|
default:
|
|
|
|
|
return nil, errors.New("not implemented")
|
|
|
|
|
}
|
|
|
|
|
}
|