all: support ipv6 for doh3 upstream bootstrap ip

We did it for doh, but the doh3 transport also needs to be changed.
This commit is contained in:
Cuong Manh Le
2022-12-20 01:25:52 +07:00
committed by Cuong Manh Le
parent 837d3195ca
commit b03aa39b83
2 changed files with 62 additions and 42 deletions
+53 -9
View File
@@ -2,6 +2,7 @@ package ctrld
import ( import (
"context" "context"
"crypto/tls"
"net" "net"
"net/http" "net/http"
"net/url" "net/url"
@@ -10,6 +11,8 @@ import (
"github.com/Control-D-Inc/ctrld/internal/dnsrcode" "github.com/Control-D-Inc/ctrld/internal/dnsrcode"
"github.com/go-playground/validator/v10" "github.com/go-playground/validator/v10"
"github.com/lucas-clemente/quic-go"
"github.com/lucas-clemente/quic-go/http3"
"github.com/miekg/dns" "github.com/miekg/dns"
"github.com/spf13/viper" "github.com/spf13/viper"
) )
@@ -79,13 +82,14 @@ type NetworkConfig struct {
// UpstreamConfig specifies configuration for upstreams that ctrld will forward requests to. // UpstreamConfig specifies configuration for upstreams that ctrld will forward requests to.
type UpstreamConfig struct { type UpstreamConfig struct {
Name string `mapstructure:"name" toml:"name"` Name string `mapstructure:"name" toml:"name"`
Type string `mapstructure:"type" toml:"type" validate:"oneof=doh doh3 dot doq os legacy"` Type string `mapstructure:"type" toml:"type" validate:"oneof=doh doh3 dot doq os legacy"`
Endpoint string `mapstructure:"endpoint" toml:"endpoint" validate:"required_unless=Type os"` Endpoint string `mapstructure:"endpoint" toml:"endpoint" validate:"required_unless=Type os"`
BootstrapIP string `mapstructure:"bootstrap_ip" toml:"bootstrap_ip"` BootstrapIP string `mapstructure:"bootstrap_ip" toml:"bootstrap_ip"`
Domain string `mapstructure:"-" toml:"-"` Domain string `mapstructure:"-" toml:"-"`
Timeout int `mapstructure:"timeout" toml:"timeout" validate:"gte=0"` Timeout int `mapstructure:"timeout" toml:"timeout" validate:"gte=0"`
transport *http.Transport `mapstructure:"-" toml:"-"` transport *http.Transport `mapstructure:"-" toml:"-"`
http3RoundTripper *http3.RoundTripper `mapstructure:"-" toml:"-"`
} }
// ListenerConfig specifies the networks configuration that ctrld will run on. // ListenerConfig specifies the networks configuration that ctrld will run on.
@@ -133,9 +137,15 @@ func (uc *UpstreamConfig) Init() {
// SetupTransport initializes the network transport used to connect to upstream server. // SetupTransport initializes the network transport used to connect to upstream server.
// For now, only DoH upstream is supported. // For now, only DoH upstream is supported.
func (uc *UpstreamConfig) SetupTransport() { func (uc *UpstreamConfig) SetupTransport() {
if uc.Type != resolverTypeDOH { switch uc.Type {
return case resolverTypeDOH:
uc.setupDOHTransport()
case resolverTypeDOH3:
uc.setupDOH3Transport()
} }
}
func (uc *UpstreamConfig) setupDOHTransport() {
uc.transport = http.DefaultTransport.(*http.Transport).Clone() uc.transport = http.DefaultTransport.(*http.Transport).Clone()
uc.transport.DialContext = func(ctx context.Context, network, addr string) (net.Conn, error) { uc.transport.DialContext = func(ctx context.Context, network, addr string) (net.Conn, error) {
dialer := &net.Dialer{ dialer := &net.Dialer{
@@ -153,6 +163,40 @@ func (uc *UpstreamConfig) SetupTransport() {
return dialer.DialContext(ctx, network, addr) return dialer.DialContext(ctx, network, addr)
} }
uc.pingUpstream()
}
func (uc *UpstreamConfig) setupDOH3Transport() {
uc.http3RoundTripper = &http3.RoundTripper{}
uc.http3RoundTripper.Dial = func(ctx context.Context, addr string, tlsCfg *tls.Config, cfg *quic.Config) (quic.EarlyConnection, error) {
host := addr
ProxyLog.Debug().Msgf("debug dial context D0H3 %s - %s", addr, bootstrapDNS)
// if we have a bootstrap ip set, use it to avoid DNS lookup
if uc.BootstrapIP != "" {
if _, port, _ := net.SplitHostPort(addr); port != "" {
addr = net.JoinHostPort(uc.BootstrapIP, port)
}
ProxyLog.Debug().Msgf("sending doh3 request to: %s", addr)
}
remoteAddr, err := net.ResolveUDPAddr("udp", addr)
if err != nil {
return nil, err
}
localAddr := &net.UDPAddr{IP: net.IPv4zero, Port: 0}
if strings.Index(uc.BootstrapIP, ":") > -1 {
localAddr = &net.UDPAddr{IP: net.IPv6zero, Port: 0}
}
udpConn, err := net.ListenUDP("udp", localAddr)
if err != nil {
return nil, err
}
return quic.DialEarlyContext(ctx, udpConn, remoteAddr, host, tlsCfg, cfg)
}
uc.pingUpstream()
}
func (uc *UpstreamConfig) pingUpstream() {
// Warming up the transport by querying a test packet. // Warming up the transport by querying a test packet.
dnsResolver, err := NewResolver(uc) dnsResolver, err := NewResolver(uc)
if err != nil { if err != nil {
+9 -33
View File
@@ -2,52 +2,30 @@ package ctrld
import ( import (
"context" "context"
"crypto/tls"
"encoding/base64" "encoding/base64"
"fmt" "fmt"
"io" "io"
"net"
"net/http" "net/http"
"github.com/lucas-clemente/quic-go"
"github.com/lucas-clemente/quic-go/http3" "github.com/lucas-clemente/quic-go/http3"
"github.com/miekg/dns" "github.com/miekg/dns"
) )
func newDohResolver(uc *UpstreamConfig) *dohResolver { func newDohResolver(uc *UpstreamConfig) *dohResolver {
r := &dohResolver{ r := &dohResolver{
endpoint: uc.Endpoint, endpoint: uc.Endpoint,
isDoH3: uc.Type == resolverTypeDOH3, isDoH3: uc.Type == resolverTypeDOH3,
transport: uc.transport, transport: uc.transport,
} http3RoundTripper: uc.http3RoundTripper,
if r.isDoH3 {
r.doh3DialFunc = func(ctx context.Context, addr string, tlsCfg *tls.Config, cfg *quic.Config) (quic.EarlyConnection, error) {
host := addr
Log(ctx, ProxyLog.Debug(), "debug dial context D0H3 %s - %s", addr, bootstrapDNS)
// if we have a bootstrap ip set, use it to avoid DNS lookup
if uc.BootstrapIP != "" && addr == fmt.Sprintf("%s:443", uc.Domain) {
addr = fmt.Sprintf("%s:443", uc.BootstrapIP)
Log(ctx, ProxyLog.Debug(), "sending doh3 request to: %s", addr)
}
remoteAddr, err := net.ResolveUDPAddr("udp", addr)
if err != nil {
return nil, err
}
udpConn, err := net.ListenUDP("udp", &net.UDPAddr{IP: net.IPv4zero, Port: 0})
if err != nil {
return nil, err
}
return quic.DialEarlyContext(ctx, udpConn, remoteAddr, host, tlsCfg, cfg)
}
} }
return r return r
} }
type dohResolver struct { type dohResolver struct {
endpoint string endpoint string
isDoH3 bool isDoH3 bool
doh3DialFunc func(ctx context.Context, addr string, tlsCfg *tls.Config, cfg *quic.Config) (quic.EarlyConnection, error) transport *http.Transport
transport *http.Transport http3RoundTripper *http3.RoundTripper
} }
func (r *dohResolver) Resolve(ctx context.Context, msg *dns.Msg) (*dns.Msg, error) { func (r *dohResolver) Resolve(ctx context.Context, msg *dns.Msg) (*dns.Msg, error) {
@@ -66,9 +44,7 @@ func (r *dohResolver) Resolve(ctx context.Context, msg *dns.Msg) (*dns.Msg, erro
c := http.Client{Transport: r.transport} c := http.Client{Transport: r.transport}
if r.isDoH3 { if r.isDoH3 {
c.Transport = &http3.RoundTripper{} c.Transport = r.http3RoundTripper
c.Transport.(*http3.RoundTripper).Dial = r.doh3DialFunc
defer c.Transport.(*http3.RoundTripper).Close()
} }
resp, err := c.Do(req) resp, err := c.Do(req)
if err != nil { if err != nil {