Skip to content

Commit

Permalink
DNS https+local: Use Chrome's fingerprint, Add fakeSNI
Browse files Browse the repository at this point in the history
  • Loading branch information
RPRX authored Jan 22, 2025
1 parent ca9a902 commit 7f0dbf7
Show file tree
Hide file tree
Showing 6 changed files with 102 additions and 68 deletions.
126 changes: 68 additions & 58 deletions app/dns/config.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions app/dns/config.proto
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ message NameServer {
repeated xray.app.router.GeoIP geoip = 3;
repeated OriginalRule original_rules = 4;
QueryStrategy query_strategy = 7;
string fake_sni = 8;
}

enum DomainMatchingType {
Expand Down
6 changes: 3 additions & 3 deletions app/dns/nameserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ type Client struct {
var errExpectedIPNonMatch = errors.New("expectIPs not match")

// NewServer creates a name server object according to the network destination url.
func NewServer(ctx context.Context, dest net.Destination, dispatcher routing.Dispatcher, queryStrategy QueryStrategy) (Server, error) {
func NewServer(ctx context.Context, dest net.Destination, dispatcher routing.Dispatcher, queryStrategy QueryStrategy, fakeSNI string) (Server, error) {
if address := dest.Address; address.Family().IsDomain() {
u, err := url.Parse(address.Domain())
if err != nil {
Expand All @@ -47,7 +47,7 @@ func NewServer(ctx context.Context, dest net.Destination, dispatcher routing.Dis
case strings.EqualFold(u.Scheme, "https"): // DOH Remote mode
return NewDoHNameServer(u, dispatcher, queryStrategy)
case strings.EqualFold(u.Scheme, "https+local"): // DOH Local mode
return NewDoHLocalNameServer(u, queryStrategy), nil
return NewDoHLocalNameServer(u, queryStrategy, fakeSNI), nil
case strings.EqualFold(u.Scheme, "quic+local"): // DNS-over-QUIC Local mode
return NewQUICNameServer(u, queryStrategy)
case strings.EqualFold(u.Scheme, "tcp"): // DNS-over-TCP Remote mode
Expand Down Expand Up @@ -84,7 +84,7 @@ func NewClient(

err := core.RequireFeatures(ctx, func(dispatcher routing.Dispatcher) error {
// Create a new server for each client for now
server, err := NewServer(ctx, ns.Address.AsDestination(), dispatcher, ns.GetQueryStrategy())
server, err := NewServer(ctx, ns.Address.AsDestination(), dispatcher, ns.GetQueryStrategy(), ns.FakeSni)
if err != nil {
return errors.New("failed to create nameserver").Base(err).AtWarning()
}
Expand Down
21 changes: 18 additions & 3 deletions app/dns/nameserver_doh.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"sync"
"time"

utls "github.com/refraction-networking/utls"
"github.com/xtls/xray-core/common"
"github.com/xtls/xray-core/common/errors"
"github.com/xtls/xray-core/common/log"
Expand Down Expand Up @@ -90,13 +91,16 @@ func NewDoHNameServer(url *url.URL, dispatcher routing.Dispatcher, queryStrategy
}

// NewDoHLocalNameServer creates DOH client object for local resolving
func NewDoHLocalNameServer(url *url.URL, queryStrategy QueryStrategy) *DoHNameServer {
func NewDoHLocalNameServer(url *url.URL, queryStrategy QueryStrategy, fakeSNI string) *DoHNameServer {
url.Scheme = "https"
s := baseDOHNameServer(url, "DOHL", queryStrategy)
if fakeSNI == "" {
fakeSNI = "disabled"
}
tr := &http.Transport{
IdleConnTimeout: 90 * time.Second,
ForceAttemptHTTP2: true,
DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
DialTLSContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
dest, err := net.ParseDestination(network + ":" + addr)
if err != nil {
return nil, err
Expand All @@ -111,14 +115,25 @@ func NewDoHLocalNameServer(url *url.URL, queryStrategy QueryStrategy) *DoHNameSe
if err != nil {
return nil, err
}
utlsConfig := &utls.Config{
ServerName: fakeSNI,
InsecureServerNameToVerify: url.Host,
}
if fakeSNI == "disabled" {
utlsConfig = nil
}
conn = utls.UClient(conn, utlsConfig, utls.HelloChrome_Auto)
if err := conn.(*utls.UConn).HandshakeContext(ctx); err != nil {
return nil, err
}
return conn, nil
},
}
s.httpClient = &http.Client{
Timeout: time.Second * 180,
Transport: tr,
}
errors.LogInfo(context.Background(), "DNS: created Local DOH client for ", url.String())
errors.LogInfo(context.Background(), "DNS: created Local DOH client for ", url.String(), ", with fakeSNI ", fakeSNI)
return s
}

Expand Down
8 changes: 4 additions & 4 deletions app/dns/nameserver_doh_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ func TestDOHNameServer(t *testing.T) {
url, err := url.Parse("https+local://1.1.1.1/dns-query")
common.Must(err)

s := NewDoHLocalNameServer(url, QueryStrategy_USE_IP)
s := NewDoHLocalNameServer(url, QueryStrategy_USE_IP, "")
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
ips, err := s.QueryIP(ctx, "google.com", net.IP(nil), dns_feature.IPOption{
IPv4Enable: true,
Expand All @@ -34,7 +34,7 @@ func TestDOHNameServerWithCache(t *testing.T) {
url, err := url.Parse("https+local://1.1.1.1/dns-query")
common.Must(err)

s := NewDoHLocalNameServer(url, QueryStrategy_USE_IP)
s := NewDoHLocalNameServer(url, QueryStrategy_USE_IP, "")
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
ips, err := s.QueryIP(ctx, "google.com", net.IP(nil), dns_feature.IPOption{
IPv4Enable: true,
Expand Down Expand Up @@ -62,7 +62,7 @@ func TestDOHNameServerWithIPv4Override(t *testing.T) {
url, err := url.Parse("https+local://1.1.1.1/dns-query")
common.Must(err)

s := NewDoHLocalNameServer(url, QueryStrategy_USE_IP4)
s := NewDoHLocalNameServer(url, QueryStrategy_USE_IP4, "")
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
ips, err := s.QueryIP(ctx, "google.com", net.IP(nil), dns_feature.IPOption{
IPv4Enable: true,
Expand All @@ -85,7 +85,7 @@ func TestDOHNameServerWithIPv6Override(t *testing.T) {
url, err := url.Parse("https+local://1.1.1.1/dns-query")
common.Must(err)

s := NewDoHLocalNameServer(url, QueryStrategy_USE_IP6)
s := NewDoHLocalNameServer(url, QueryStrategy_USE_IP6, "")
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
ips, err := s.QueryIP(ctx, "google.com", net.IP(nil), dns_feature.IPOption{
IPv4Enable: true,
Expand Down
Loading

0 comments on commit 7f0dbf7

Please sign in to comment.