mirror of
https://github.com/Alexey71/opera-proxy.git
synced 2026-05-13 14:11:00 +00:00
Merge pull request #120 from Snawoot/ca_improvements
CA roots improvements
This commit is contained in:
+1
-7
@@ -4,15 +4,9 @@ WORKDIR /go/src/github.com/Snawoot/opera-proxy
|
|||||||
COPY . .
|
COPY . .
|
||||||
ARG TARGETOS TARGETARCH
|
ARG TARGETOS TARGETARCH
|
||||||
RUN GOOS=$TARGETOS GOARCH=$TARGETARCH CGO_ENABLED=0 go build -a -tags netgo -ldflags '-s -w -extldflags "-static"'
|
RUN GOOS=$TARGETOS GOARCH=$TARGETARCH CGO_ENABLED=0 go build -a -tags netgo -ldflags '-s -w -extldflags "-static"'
|
||||||
ADD https://curl.haxx.se/ca/cacert.pem /certs.crt
|
|
||||||
RUN chmod 0644 /certs.crt
|
|
||||||
|
|
||||||
FROM scratch AS arrange
|
|
||||||
COPY --from=build /go/src/github.com/Snawoot/opera-proxy/opera-proxy /
|
|
||||||
COPY --from=build /certs.crt /etc/ssl/certs/ca-certificates.crt
|
|
||||||
|
|
||||||
FROM scratch
|
FROM scratch
|
||||||
COPY --from=arrange / /
|
COPY --from=build /go/src/github.com/Snawoot/opera-proxy/opera-proxy /
|
||||||
USER 9999:9999
|
USER 9999:9999
|
||||||
EXPOSE 18080/tcp
|
EXPOSE 18080/tcp
|
||||||
ENTRYPOINT ["/opera-proxy", "-bind-address", "0.0.0.0:18080"]
|
ENTRYPOINT ["/opera-proxy", "-bind-address", "0.0.0.0:18080"]
|
||||||
|
|||||||
@@ -99,7 +99,6 @@ eu3.sec-tunnel.com,77.111.244.22,443
|
|||||||
| bind-address | String | proxy listen address (default "127.0.0.1:18080") |
|
| bind-address | String | proxy listen address (default "127.0.0.1:18080") |
|
||||||
| bootstrap-dns | String | Comma-separated list of DNS/DoH/DoT resolvers for initial discovery of SurfEasy API address. Supported schemes are: `dns://`, `https://`, `tls://`, `tcp://`. Examples: `https://1.1.1.1/dns-query`, `tls://9.9.9.9:853` (default `https://1.1.1.3/dns-query,https://8.8.8.8/dns-query,https://dns.google/dns-query,https://security.cloudflare-dns.com/dns-query,https://fidelity.vm-0.com/q,https://wikimedia-dns.org/dns-query,https://dns.adguard-dns.com/dns-query,https://dns.quad9.net/dns-query,https://doh.cleanbrowsing.org/doh/adult-filter/`) |
|
| bootstrap-dns | String | Comma-separated list of DNS/DoH/DoT resolvers for initial discovery of SurfEasy API address. Supported schemes are: `dns://`, `https://`, `tls://`, `tcp://`. Examples: `https://1.1.1.1/dns-query`, `tls://9.9.9.9:853` (default `https://1.1.1.3/dns-query,https://8.8.8.8/dns-query,https://dns.google/dns-query,https://security.cloudflare-dns.com/dns-query,https://fidelity.vm-0.com/q,https://wikimedia-dns.org/dns-query,https://dns.adguard-dns.com/dns-query,https://dns.quad9.net/dns-query,https://doh.cleanbrowsing.org/doh/adult-filter/`) |
|
||||||
| cafile | String | use custom CA certificate bundle file |
|
| cafile | String | use custom CA certificate bundle file |
|
||||||
| certchain-workaround | Boolean | add bundled cross-signed intermediate cert to certchain to make it check out on old systems (default true) |
|
|
||||||
| config | String | read configuration from file with space-separated keys and values |
|
| config | String | read configuration from file with space-separated keys and values |
|
||||||
| country | String | desired proxy location (default "EU") |
|
| country | String | desired proxy location (default "EU") |
|
||||||
| dp-export | - | export configuration for dumbproxy |
|
| dp-export | - | export configuration for dumbproxy |
|
||||||
|
|||||||
+1
-40
@@ -7,7 +7,6 @@ import (
|
|||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"encoding/pem"
|
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
@@ -22,35 +21,8 @@ const (
|
|||||||
PROXY_CONNECT_METHOD = "CONNECT"
|
PROXY_CONNECT_METHOD = "CONNECT"
|
||||||
PROXY_HOST_HEADER = "Host"
|
PROXY_HOST_HEADER = "Host"
|
||||||
PROXY_AUTHORIZATION_HEADER = "Proxy-Authorization"
|
PROXY_AUTHORIZATION_HEADER = "Proxy-Authorization"
|
||||||
MISSING_CHAIN_CERT = `-----BEGIN CERTIFICATE-----
|
|
||||||
MIID0zCCArugAwIBAgIQVmcdBOpPmUxvEIFHWdJ1lDANBgkqhkiG9w0BAQwFADB7
|
|
||||||
MQswCQYDVQQGEwJHQjEbMBkGA1UECAwSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYD
|
|
||||||
VQQHDAdTYWxmb3JkMRowGAYDVQQKDBFDb21vZG8gQ0EgTGltaXRlZDEhMB8GA1UE
|
|
||||||
AwwYQUFBIENlcnRpZmljYXRlIFNlcnZpY2VzMB4XDTE5MDMxMjAwMDAwMFoXDTI4
|
|
||||||
MTIzMTIzNTk1OVowgYgxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpOZXcgSmVyc2V5
|
|
||||||
MRQwEgYDVQQHEwtKZXJzZXkgQ2l0eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBO
|
|
||||||
ZXR3b3JrMS4wLAYDVQQDEyVVU0VSVHJ1c3QgRUNDIENlcnRpZmljYXRpb24gQXV0
|
|
||||||
aG9yaXR5MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEGqxUWqn5aCPnetUkb1PGWthL
|
|
||||||
q8bVttHmc3Gu3ZzWDGH926CJA7gFFOxXzu5dP+Ihs8731Ip54KODfi2X0GHE8Znc
|
|
||||||
JZFjq38wo7Rw4sehM5zzvy5cU7Ffs30yf4o043l5o4HyMIHvMB8GA1UdIwQYMBaA
|
|
||||||
FKARCiM+lvEH7OKvKe+CpX/QMKS0MB0GA1UdDgQWBBQ64QmG1M8ZwpZ2dEl23OA1
|
|
||||||
xmNjmjAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zARBgNVHSAECjAI
|
|
||||||
MAYGBFUdIAAwQwYDVR0fBDwwOjA4oDagNIYyaHR0cDovL2NybC5jb21vZG9jYS5j
|
|
||||||
b20vQUFBQ2VydGlmaWNhdGVTZXJ2aWNlcy5jcmwwNAYIKwYBBQUHAQEEKDAmMCQG
|
|
||||||
CCsGAQUFBzABhhhodHRwOi8vb2NzcC5jb21vZG9jYS5jb20wDQYJKoZIhvcNAQEM
|
|
||||||
BQADggEBABns652JLCALBIAdGN5CmXKZFjK9Dpx1WywV4ilAbe7/ctvbq5AfjJXy
|
|
||||||
ij0IckKJUAfiORVsAYfZFhr1wHUrxeZWEQff2Ji8fJ8ZOd+LygBkc7xGEJuTI42+
|
|
||||||
FsMuCIKchjN0djsoTI0DQoWz4rIjQtUfenVqGtF8qmchxDM6OW1TyaLtYiKou+JV
|
|
||||||
bJlsQ2uRl9EMC5MCHdK8aXdJ5htN978UeAOwproLtOGFfy/cQjutdAFI3tZs4RmY
|
|
||||||
CV4Ks2dH/hzg1cEo70qLRDEmBDeNiXQ2Lu+lIg+DdEmSx/cQwgwp+7e9un/jX9Wf
|
|
||||||
8qn0dNW44bOwgeThpWOjzOoEeJBuv/c=
|
|
||||||
-----END CERTIFICATE-----
|
|
||||||
`
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var missingLinkDER, _ = pem.Decode([]byte(MISSING_CHAIN_CERT))
|
|
||||||
var missingLink, _ = x509.ParseCertificate(missingLinkDER.Bytes)
|
|
||||||
|
|
||||||
type stringCb = func() (string, error)
|
type stringCb = func() (string, error)
|
||||||
|
|
||||||
type Dialer interface {
|
type Dialer interface {
|
||||||
@@ -68,18 +40,16 @@ type ProxyDialer struct {
|
|||||||
fakeSNI stringCb
|
fakeSNI stringCb
|
||||||
auth stringCb
|
auth stringCb
|
||||||
next ContextDialer
|
next ContextDialer
|
||||||
intermediateWorkaround bool
|
|
||||||
caPool *x509.CertPool
|
caPool *x509.CertPool
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewProxyDialer(address, tlsServerName, fakeSNI, auth stringCb, intermediateWorkaround bool, caPool *x509.CertPool, nextDialer ContextDialer) *ProxyDialer {
|
func NewProxyDialer(address, tlsServerName, fakeSNI, auth stringCb, caPool *x509.CertPool, nextDialer ContextDialer) *ProxyDialer {
|
||||||
return &ProxyDialer{
|
return &ProxyDialer{
|
||||||
address: address,
|
address: address,
|
||||||
tlsServerName: tlsServerName,
|
tlsServerName: tlsServerName,
|
||||||
fakeSNI: fakeSNI,
|
fakeSNI: fakeSNI,
|
||||||
auth: auth,
|
auth: auth,
|
||||||
next: nextDialer,
|
next: nextDialer,
|
||||||
intermediateWorkaround: intermediateWorkaround,
|
|
||||||
caPool: caPool,
|
caPool: caPool,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -116,7 +86,6 @@ func ProxyDialerFromURL(u *url.URL, next ContextDialer) (*ProxyDialer, error) {
|
|||||||
WrapStringToCb(tlsServerName),
|
WrapStringToCb(tlsServerName),
|
||||||
WrapStringToCb(tlsServerName),
|
WrapStringToCb(tlsServerName),
|
||||||
auth,
|
auth,
|
||||||
false,
|
|
||||||
nil,
|
nil,
|
||||||
next), nil
|
next), nil
|
||||||
}
|
}
|
||||||
@@ -158,16 +127,8 @@ func (d *ProxyDialer) DialContext(ctx context.Context, network, address string)
|
|||||||
Intermediates: x509.NewCertPool(),
|
Intermediates: x509.NewCertPool(),
|
||||||
Roots: d.caPool,
|
Roots: d.caPool,
|
||||||
}
|
}
|
||||||
waRequired := false
|
|
||||||
for _, cert := range cs.PeerCertificates[1:] {
|
for _, cert := range cs.PeerCertificates[1:] {
|
||||||
opts.Intermediates.AddCert(cert)
|
opts.Intermediates.AddCert(cert)
|
||||||
if d.intermediateWorkaround && !waRequired &&
|
|
||||||
bytes.Compare(cert.AuthorityKeyId, missingLink.SubjectKeyId) == 0 {
|
|
||||||
waRequired = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if waRequired {
|
|
||||||
opts.Intermediates.AddCert(missingLink)
|
|
||||||
}
|
}
|
||||||
_, err := cs.PeerCertificates[0].Verify(opts)
|
_, err := cs.PeerCertificates[0].Verify(opts)
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -12,4 +12,7 @@ require (
|
|||||||
golang.org/x/net v0.48.0
|
golang.org/x/net v0.48.0
|
||||||
)
|
)
|
||||||
|
|
||||||
require github.com/hashicorp/errwrap v1.1.0 // indirect
|
require (
|
||||||
|
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||||
|
golang.org/x/crypto/x509roots/fallback v0.0.0-20251210140736-7dacc380ba00 // indirect
|
||||||
|
)
|
||||||
|
|||||||
@@ -15,6 +15,9 @@ github.com/stretchr/testify v1.11.0 h1:ib4sjIrwZKxE5u/Japgo/7SJV3PvgjGiRNAvTVGqQ
|
|||||||
github.com/stretchr/testify v1.11.0/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
github.com/stretchr/testify v1.11.0/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||||
github.com/things-go/go-socks5 v0.1.0 h1:4f5dz0iMQ6cA4wseFmyLmCHmg3SWJTW92ndrKS6oERg=
|
github.com/things-go/go-socks5 v0.1.0 h1:4f5dz0iMQ6cA4wseFmyLmCHmg3SWJTW92ndrKS6oERg=
|
||||||
github.com/things-go/go-socks5 v0.1.0/go.mod h1:Riabiyu52kLsla0YmJqunt1c1JEl6iXSr4bRd7swFEA=
|
github.com/things-go/go-socks5 v0.1.0/go.mod h1:Riabiyu52kLsla0YmJqunt1c1JEl6iXSr4bRd7swFEA=
|
||||||
|
golang.org/x/crypto v0.46.0 h1:cKRW/pmt1pKAfetfu+RCEvjvZkA9RimPbh7bhFjGVBU=
|
||||||
|
golang.org/x/crypto/x509roots/fallback v0.0.0-20251210140736-7dacc380ba00 h1:qObov2/X4yIpr98j5t6samg3mMF12Rl4taUJd1rWj+c=
|
||||||
|
golang.org/x/crypto/x509roots/fallback v0.0.0-20251210140736-7dacc380ba00/go.mod h1:MEIPiCnxvQEjA4astfaKItNwEVZA5Ki+3+nyGbJ5N18=
|
||||||
golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU=
|
golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU=
|
||||||
golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY=
|
golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY=
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
|
|||||||
@@ -29,6 +29,9 @@ import (
|
|||||||
clog "github.com/Snawoot/opera-proxy/log"
|
clog "github.com/Snawoot/opera-proxy/log"
|
||||||
"github.com/Snawoot/opera-proxy/resolver"
|
"github.com/Snawoot/opera-proxy/resolver"
|
||||||
se "github.com/Snawoot/opera-proxy/seclient"
|
se "github.com/Snawoot/opera-proxy/seclient"
|
||||||
|
|
||||||
|
_ "golang.org/x/crypto/x509roots/fallback"
|
||||||
|
"golang.org/x/crypto/x509roots/fallback/bundle"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -119,7 +122,6 @@ type CLIArgs struct {
|
|||||||
refreshRetry time.Duration
|
refreshRetry time.Duration
|
||||||
initRetries int
|
initRetries int
|
||||||
initRetryInterval time.Duration
|
initRetryInterval time.Duration
|
||||||
certChainWorkaround bool
|
|
||||||
caFile string
|
caFile string
|
||||||
fakeSNI string
|
fakeSNI string
|
||||||
overrideProxyAddress string
|
overrideProxyAddress string
|
||||||
@@ -174,8 +176,6 @@ func parse_args() *CLIArgs {
|
|||||||
flag.DurationVar(&args.refreshRetry, "refresh-retry", 5*time.Second, "login refresh retry interval")
|
flag.DurationVar(&args.refreshRetry, "refresh-retry", 5*time.Second, "login refresh retry interval")
|
||||||
flag.IntVar(&args.initRetries, "init-retries", 0, "number of attempts for initialization steps, zero for unlimited retry")
|
flag.IntVar(&args.initRetries, "init-retries", 0, "number of attempts for initialization steps, zero for unlimited retry")
|
||||||
flag.DurationVar(&args.initRetryInterval, "init-retry-interval", 5*time.Second, "delay between initialization retries")
|
flag.DurationVar(&args.initRetryInterval, "init-retry-interval", 5*time.Second, "delay between initialization retries")
|
||||||
flag.BoolVar(&args.certChainWorkaround, "certchain-workaround", true,
|
|
||||||
"add bundled cross-signed intermediate cert to certchain to make it check out on old systems")
|
|
||||||
flag.StringVar(&args.caFile, "cafile", "", "use custom CA certificate bundle file")
|
flag.StringVar(&args.caFile, "cafile", "", "use custom CA certificate bundle file")
|
||||||
flag.StringVar(&args.fakeSNI, "fake-SNI", "", "domain name to use as SNI in communications with servers")
|
flag.StringVar(&args.fakeSNI, "fake-SNI", "", "domain name to use as SNI in communications with servers")
|
||||||
flag.StringVar(&args.overrideProxyAddress, "override-proxy-address", "", "use fixed proxy address instead of server address returned by SurfEasy API")
|
flag.StringVar(&args.overrideProxyAddress, "override-proxy-address", "", "use fixed proxy address instead of server address returned by SurfEasy API")
|
||||||
@@ -230,9 +230,8 @@ func run() int {
|
|||||||
KeepAlive: 30 * time.Second,
|
KeepAlive: 30 * time.Second,
|
||||||
}
|
}
|
||||||
|
|
||||||
var caPool *x509.CertPool
|
caPool := x509.NewCertPool()
|
||||||
if args.caFile != "" {
|
if args.caFile != "" {
|
||||||
caPool = x509.NewCertPool()
|
|
||||||
certs, err := ioutil.ReadFile(args.caFile)
|
certs, err := ioutil.ReadFile(args.caFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
mainLogger.Error("Can't load CA file: %v", err)
|
mainLogger.Error("Can't load CA file: %v", err)
|
||||||
@@ -242,6 +241,19 @@ func run() int {
|
|||||||
mainLogger.Error("Can't load certificates from CA file")
|
mainLogger.Error("Can't load certificates from CA file")
|
||||||
return 15
|
return 15
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
for c := range bundle.Roots() {
|
||||||
|
cert, err := x509.ParseCertificate(c.Certificate)
|
||||||
|
if err != nil {
|
||||||
|
mainLogger.Error("Unable to parse bundled certificate: %v", err)
|
||||||
|
return 15
|
||||||
|
}
|
||||||
|
if c.Constraint == nil {
|
||||||
|
caPool.AddCert(cert)
|
||||||
|
} else {
|
||||||
|
caPool.AddCertWithConstraint(cert, c.Constraint)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
xproxy.RegisterDialerType("http", proxyFromURLWrapper)
|
xproxy.RegisterDialerType("http", proxyFromURLWrapper)
|
||||||
@@ -278,7 +290,7 @@ func run() int {
|
|||||||
mainLogger.Info("Using fixed API host address = %s", args.apiAddress)
|
mainLogger.Info("Using fixed API host address = %s", args.apiAddress)
|
||||||
seclientDialer = dialer.NewFixedDialer(args.apiAddress, seclientDialer)
|
seclientDialer = dialer.NewFixedDialer(args.apiAddress, seclientDialer)
|
||||||
} else if len(args.bootstrapDNS.values) > 0 {
|
} else if len(args.bootstrapDNS.values) > 0 {
|
||||||
resolver, err := resolver.FastFromURLs(args.bootstrapDNS.values...)
|
resolver, err := resolver.FastFromURLs(caPool, args.bootstrapDNS.values...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
mainLogger.Critical("Unable to instantiate DNS resolver: %v", err)
|
mainLogger.Critical("Unable to instantiate DNS resolver: %v", err)
|
||||||
return 4
|
return 4
|
||||||
@@ -372,7 +384,6 @@ func run() int {
|
|||||||
func() (string, error) {
|
func() (string, error) {
|
||||||
return dialer.BasicAuthHeader(seclient.GetProxyCredentials()), nil
|
return dialer.BasicAuthHeader(seclient.GetProxyCredentials()), nil
|
||||||
},
|
},
|
||||||
args.certChainWorkaround,
|
|
||||||
caPool,
|
caPool,
|
||||||
d)
|
d)
|
||||||
}
|
}
|
||||||
|
|||||||
+23
-3
@@ -1,15 +1,19 @@
|
|||||||
package resolver
|
package resolver
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto/tls"
|
||||||
|
"crypto/x509"
|
||||||
"errors"
|
"errors"
|
||||||
"net"
|
"net"
|
||||||
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/ncruces/go-dns"
|
"github.com/ncruces/go-dns"
|
||||||
)
|
)
|
||||||
|
|
||||||
func FromURL(u string) (*net.Resolver, error) {
|
func FromURL(u string, caPool *x509.CertPool) (*net.Resolver, error) {
|
||||||
begin:
|
begin:
|
||||||
parsed, err := url.Parse(u)
|
parsed, err := url.Parse(u)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -48,13 +52,29 @@ begin:
|
|||||||
parsed.Scheme = "https"
|
parsed.Scheme = "https"
|
||||||
u = parsed.String()
|
u = parsed.String()
|
||||||
}
|
}
|
||||||
return dns.NewDoHResolver(u, dns.DoHAddresses(net.JoinHostPort(host, port)))
|
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,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
)
|
||||||
case "tls", "dot":
|
case "tls", "dot":
|
||||||
if port == "" {
|
if port == "" {
|
||||||
port = "853"
|
port = "853"
|
||||||
}
|
}
|
||||||
hp := net.JoinHostPort(host, port)
|
hp := net.JoinHostPort(host, port)
|
||||||
return dns.NewDoTResolver(hp, dns.DoTAddresses(hp))
|
return dns.NewDoTResolver(hp,
|
||||||
|
dns.DoTAddresses(hp),
|
||||||
|
dns.DoTConfig(&tls.Config{
|
||||||
|
RootCAs: caPool,
|
||||||
|
}),
|
||||||
|
)
|
||||||
default:
|
default:
|
||||||
return nil, errors.New("not implemented")
|
return nil, errors.New("not implemented")
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-2
@@ -2,6 +2,7 @@ package resolver
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"crypto/x509"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
|
|
||||||
@@ -16,10 +17,10 @@ type FastResolver struct {
|
|||||||
upstreams []LookupNetIPer
|
upstreams []LookupNetIPer
|
||||||
}
|
}
|
||||||
|
|
||||||
func FastFromURLs(urls ...string) (LookupNetIPer, error) {
|
func FastFromURLs(caPool *x509.CertPool, urls ...string) (LookupNetIPer, error) {
|
||||||
resolvers := make([]LookupNetIPer, 0, len(urls))
|
resolvers := make([]LookupNetIPer, 0, len(urls))
|
||||||
for i, u := range urls {
|
for i, u := range urls {
|
||||||
res, err := FromURL(u)
|
res, err := FromURL(u, caPool)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("unable to construct resolver #%d (%q): %w", i, u, err)
|
return nil, fmt.Errorf("unable to construct resolver #%d (%q): %w", i, u, err)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user