diff --git a/app/cmd/server.go b/app/cmd/server.go index a684679..3ab6c4b 100644 --- a/app/cmd/server.go +++ b/app/cmd/server.go @@ -101,10 +101,18 @@ type serverConfigResolverUDP struct { Timeout time.Duration `mapstructure:"timeout"` } +type serverConfigResolverTLS struct { + Addr string `mapstructure:"addr"` + Timeout time.Duration `mapstructure:"timeout"` + SNI string `mapstructure:"sni"` + Insecure bool `mapstructure:"insecure"` +} + type serverConfigResolver struct { Type string `mapstructure:"type"` TCP serverConfigResolverTCP `mapstructure:"tcp"` UDP serverConfigResolverUDP `mapstructure:"udp"` + TLS serverConfigResolverTLS `mapstructure:"tls"` } type serverConfigMasqueradeFile struct { @@ -254,6 +262,11 @@ func (c *serverConfig) fillOutboundConfig(hyConfig *server.Config) error { return configError{Field: "resolver.udp.addr", Err: errors.New("empty resolver address")} } ob = outbounds.NewStandardResolverUDP(c.Resolver.UDP.Addr, c.Resolver.UDP.Timeout, ob) + case "tls", "tcp-tls": + if c.Resolver.TLS.Addr == "" { + return configError{Field: "resolver.tls.addr", Err: errors.New("empty resolver address")} + } + ob = outbounds.NewStandardResolverTLS(c.Resolver.TLS.Addr, c.Resolver.TLS.Timeout, c.Resolver.TLS.SNI, c.Resolver.TLS.Insecure, ob) default: return configError{Field: "resolver.type", Err: errors.New("unsupported resolver type")} } diff --git a/app/cmd/server_test.go b/app/cmd/server_test.go index 3c3c1c6..1ca27ff 100644 --- a/app/cmd/server_test.go +++ b/app/cmd/server_test.go @@ -71,6 +71,12 @@ func TestServerConfig(t *testing.T) { Addr: "4.6.8.0:53", Timeout: 2 * time.Second, }, + TLS: serverConfigResolverTLS{ + Addr: "dot.yolo.com:8853", + Timeout: 10 * time.Second, + SNI: "server1.yolo.net", + Insecure: true, + }, }, Masquerade: serverConfigMasquerade{ Type: "proxy", diff --git a/app/cmd/server_test.yaml b/app/cmd/server_test.yaml index c44f16c..218bb73 100644 --- a/app/cmd/server_test.yaml +++ b/app/cmd/server_test.yaml @@ -49,6 +49,11 @@ resolver: udp: addr: 4.6.8.0:53 timeout: 2s + tls: + addr: dot.yolo.com:8853 + timeout: 10s + sni: server1.yolo.net + insecure: true masquerade: type: proxy diff --git a/app/server.example.yaml b/app/server.example.yaml index e06b51d..a892966 100644 --- a/app/server.example.yaml +++ b/app/server.example.yaml @@ -41,10 +41,12 @@ auth: password: some_password # resolver: -# type: udp -# udp: -# addr: 8.8.4.4 -# timeout: 2s +# type: tls +# tls: +# addr: dot.yolo.com:8853 +# timeout: 10s +# sni: server1.yolo.net +# insecure: true masquerade: type: proxy diff --git a/extras/outbounds/dns_standard.go b/extras/outbounds/dns_standard.go index caad77d..44f0e56 100644 --- a/extras/outbounds/dns_standard.go +++ b/extras/outbounds/dns_standard.go @@ -1,6 +1,7 @@ package outbounds import ( + "crypto/tls" "net" "time" @@ -42,6 +43,21 @@ func NewStandardResolverTCP(addr string, timeout time.Duration, next PluggableOu } } +func NewStandardResolverTLS(addr string, timeout time.Duration, sni string, insecure bool, next PluggableOutbound) PluggableOutbound { + return &standardResolver{ + Addr: addDefaultPortTLS(addr), + Client: &dns.Client{ + Net: "tcp-tls", + Timeout: timeoutOrDefault(timeout), + TLSConfig: &tls.Config{ + ServerName: sni, + InsecureSkipVerify: insecure, + }, + }, + Next: next, + } +} + // addDefaultPort adds the default DNS port (53) to the address if not present. func addDefaultPort(addr string) string { if _, _, err := net.SplitHostPort(addr); err != nil { @@ -50,6 +66,14 @@ func addDefaultPort(addr string) string { return addr } +// addDefaultPortTLS adds the default DNS-over-TLS port (853) to the address if not present. +func addDefaultPortTLS(addr string) string { + if _, _, err := net.SplitHostPort(addr); err != nil { + return net.JoinHostPort(addr, "853") + } + return addr +} + func timeoutOrDefault(timeout time.Duration) time.Duration { if timeout == 0 { return standardResolverDefaultTimeout