2017-12-19 17:55:09 -05:00
package dns
2016-05-16 02:09:28 -04:00
import (
2017-01-26 14:46:44 -05:00
"context"
2020-12-18 04:24:33 -05:00
"net/url"
2021-02-22 21:17:20 -05:00
"strings"
2020-12-18 04:24:33 -05:00
"time"
2016-05-16 02:09:28 -04:00
2022-01-02 10:16:23 -05:00
core "github.com/v2fly/v2ray-core/v5"
2022-12-14 21:38:28 -05:00
"github.com/v2fly/v2ray-core/v5/app/dns/fakedns"
2022-01-02 10:16:23 -05:00
"github.com/v2fly/v2ray-core/v5/app/router"
"github.com/v2fly/v2ray-core/v5/common/errors"
"github.com/v2fly/v2ray-core/v5/common/net"
2022-11-29 19:43:39 -05:00
"github.com/v2fly/v2ray-core/v5/common/session"
"github.com/v2fly/v2ray-core/v5/features"
2022-01-02 10:16:23 -05:00
"github.com/v2fly/v2ray-core/v5/features/dns"
"github.com/v2fly/v2ray-core/v5/features/routing"
2016-05-16 02:09:28 -04:00
)
2020-12-18 04:24:33 -05:00
// Server is the interface for Name Server.
type Server interface {
2019-02-22 18:01:23 -05:00
// Name of the Client.
2018-11-22 10:29:09 -05:00
Name ( ) string
2019-02-22 18:01:23 -05:00
// QueryIP sends IP queries to its configured server.
2021-02-24 03:03:50 -05:00
QueryIP ( ctx context . Context , domain string , clientIP net . IP , option dns . IPOption , disableCache bool ) ( [ ] net . IP , error )
2016-05-16 02:09:28 -04:00
}
2016-05-16 12:05:01 -04:00
2020-12-18 04:24:33 -05:00
// Client is the interface for DNS client.
type Client struct {
2022-11-29 19:43:39 -05:00
server Server
clientIP net . IP
tag string
queryStrategy dns . IPOption
cacheStrategy CacheStrategy
fallbackStrategy FallbackStrategy
domains [ ] string
expectIPs [ ] * router . GeoIPMatcher
2022-12-14 21:38:28 -05:00
fakeDNS Server
2016-05-16 12:05:01 -04:00
}
2020-12-18 04:24:33 -05:00
var errExpectedIPNonMatch = errors . New ( "expectIPs not match" )
2018-11-19 14:42:02 -05:00
2020-12-18 04:24:33 -05:00
// NewServer creates a name server object according to the network destination url.
2022-12-14 21:38:28 -05:00
func NewServer ( ctx context . Context , dest net . Destination , onCreated func ( Server ) error ) error {
onCreatedWithError := func ( server Server , err error ) error {
if err != nil {
return err
}
return onCreated ( server )
}
2020-12-18 04:24:33 -05:00
if address := dest . Address ; address . Family ( ) . IsDomain ( ) {
u , err := url . Parse ( address . Domain ( ) )
if err != nil {
2022-12-14 21:38:28 -05:00
return err
2020-12-18 04:24:33 -05:00
}
switch {
2021-02-22 21:17:20 -05:00
case strings . EqualFold ( u . String ( ) , "localhost" ) :
2022-12-14 21:38:28 -05:00
return onCreated ( NewLocalNameServer ( ) )
case strings . EqualFold ( u . String ( ) , "fakedns" ) :
return core . RequireFeatures ( ctx , func ( fakedns dns . FakeDNSEngine ) error { return onCreated ( NewFakeDNSServer ( fakedns ) ) } )
2021-02-22 21:17:20 -05:00
case strings . EqualFold ( u . Scheme , "https" ) : // DOH Remote mode
2022-12-14 21:38:28 -05:00
return core . RequireFeatures ( ctx , func ( dispatcher routing . Dispatcher ) error { return onCreatedWithError ( NewDoHNameServer ( u , dispatcher ) ) } )
2021-02-22 21:17:20 -05:00
case strings . EqualFold ( u . Scheme , "https+local" ) : // DOH Local mode
2022-12-14 21:38:28 -05:00
return onCreated ( NewDoHLocalNameServer ( u ) )
2021-06-04 15:09:06 -04:00
case strings . EqualFold ( u . Scheme , "tcp" ) : // DNS-over-TCP Remote mode
2022-12-14 21:38:28 -05:00
return core . RequireFeatures ( ctx , func ( dispatcher routing . Dispatcher ) error { return onCreatedWithError ( NewTCPNameServer ( u , dispatcher ) ) } )
2021-06-04 15:09:06 -04:00
case strings . EqualFold ( u . Scheme , "tcp+local" ) : // DNS-over-TCP Local mode
2022-12-14 21:38:28 -05:00
return onCreatedWithError ( NewTCPLocalNameServer ( u ) )
case strings . EqualFold ( u . Scheme , "quic+local" ) : // DNS-over-QUIC Local mode
return onCreatedWithError ( NewQUICNameServer ( u ) )
2020-12-18 04:24:33 -05:00
}
}
if dest . Network == net . Network_Unknown {
dest . Network = net . Network_UDP
}
if dest . Network == net . Network_UDP { // UDP classic DNS mode
2022-12-14 21:38:28 -05:00
return core . RequireFeatures ( ctx , func ( dispatcher routing . Dispatcher ) error { return onCreated ( NewClassicNameServer ( dest , dispatcher ) ) } )
2018-06-26 09:16:45 -04:00
}
2022-12-14 21:38:28 -05:00
return newError ( "No available name server could be created from " , dest ) . AtWarning ( )
2020-12-18 04:24:33 -05:00
}
// NewClient creates a DNS client managing a name server with client IP, domain rules and expected IPs.
2022-11-29 19:43:39 -05:00
func NewClient ( ctx context . Context , ns * NameServer , dns * Config ) ( * Client , error ) {
2020-12-18 04:24:33 -05:00
client := & Client { }
2021-04-08 22:35:26 -04:00
2022-11-29 19:43:39 -05:00
// Create DNS server instance
2022-12-14 21:38:28 -05:00
err := NewServer ( ctx , ns . Address . AsDestination ( ) , func ( server Server ) error {
2020-12-18 04:24:33 -05:00
client . server = server
return nil
} )
2022-11-29 19:43:39 -05:00
if err != nil {
return nil , err
}
2020-12-18 04:24:33 -05:00
2022-11-29 19:43:39 -05:00
// Initialize fields with default values
if len ( ns . Tag ) == 0 {
ns . Tag = dns . Tag
if len ( ns . Tag ) == 0 {
ns . Tag = generateRandomTag ( )
2020-12-18 04:24:33 -05:00
}
2022-11-29 19:43:39 -05:00
}
if len ( ns . ClientIp ) == 0 {
ns . ClientIp = dns . ClientIp
}
if ns . QueryStrategy == nil {
ns . QueryStrategy = & dns . QueryStrategy
}
if ns . CacheStrategy == nil {
ns . CacheStrategy = new ( CacheStrategy )
switch {
case dns . CacheStrategy != CacheStrategy_CacheEnabled :
* ns . CacheStrategy = dns . CacheStrategy
case dns . DisableCache :
features . PrintDeprecatedFeatureWarning ( "DNS disableCache settings" )
* ns . CacheStrategy = CacheStrategy_CacheDisabled
2020-12-18 04:24:33 -05:00
}
2018-06-26 09:16:45 -04:00
}
2022-11-29 19:43:39 -05:00
if ns . FallbackStrategy == nil {
ns . FallbackStrategy = new ( FallbackStrategy )
switch {
case ns . SkipFallback :
features . PrintDeprecatedFeatureWarning ( "DNS server skipFallback settings" )
* ns . FallbackStrategy = FallbackStrategy_Disabled
case dns . FallbackStrategy != FallbackStrategy_Enabled :
* ns . FallbackStrategy = dns . FallbackStrategy
case dns . DisableFallback :
features . PrintDeprecatedFeatureWarning ( "DNS disableFallback settings" )
* ns . FallbackStrategy = FallbackStrategy_Disabled
case dns . DisableFallbackIfMatch :
features . PrintDeprecatedFeatureWarning ( "DNS disableFallbackIfMatch settings" )
* ns . FallbackStrategy = FallbackStrategy_DisabledIfAnyMatch
}
}
2022-12-14 21:38:28 -05:00
if ( ns . FakeDns != nil && len ( ns . FakeDns . Pools ) == 0 ) || // Use globally configured fake ip pool if: 1. `fakedns` config is set, but empty(represents { "fakedns": true } in JSON settings);
ns . FakeDns == nil && strings . EqualFold ( ns . Address . Address . GetDomain ( ) , "fakedns" ) { // 2. `fakedns` config not set, but server address is `fakedns`(represents { "address": "fakedns" } in JSON settings).
if dns . FakeDns != nil {
ns . FakeDns = dns . FakeDns
} else {
ns . FakeDns = & fakedns . FakeDnsPoolMulti { }
queryStrategy := toIPOption ( * ns . QueryStrategy )
if queryStrategy . IPv4Enable {
ns . FakeDns . Pools = append ( ns . FakeDns . Pools , & fakedns . FakeDnsPool {
IpPool : "198.18.0.0/15" ,
LruSize : 65535 ,
} )
}
if queryStrategy . IPv6Enable {
ns . FakeDns . Pools = append ( ns . FakeDns . Pools , & fakedns . FakeDnsPool {
IpPool : "fc00::/18" ,
LruSize : 65535 ,
} )
}
}
}
2022-11-29 19:43:39 -05:00
// Priotize local domains with specific TLDs or without any dot to local DNS
if strings . EqualFold ( ns . Address . Address . GetDomain ( ) , "localhost" ) {
ns . PrioritizedDomain = append ( ns . PrioritizedDomain , localTLDsAndDotlessDomains ... )
ns . OriginalRules = append ( ns . OriginalRules , localTLDsAndDotlessDomainsRule )
}
if len ( ns . ClientIp ) > 0 {
newError ( "DNS: client " , ns . Address . Address . AsAddress ( ) , " uses clientIP " , net . IP ( ns . ClientIp ) . String ( ) ) . AtInfo ( ) . WriteToLog ( )
}
2018-11-19 14:42:02 -05:00
2022-11-29 19:43:39 -05:00
client . clientIP = ns . ClientIp
client . tag = ns . Tag
client . queryStrategy = toIPOption ( * ns . QueryStrategy )
client . cacheStrategy = * ns . CacheStrategy
client . fallbackStrategy = * ns . FallbackStrategy
return client , nil
2020-12-18 04:24:33 -05:00
}
// Name returns the server name the client manages.
func ( c * Client ) Name ( ) string {
return c . server . Name ( )
2018-06-26 09:16:45 -04:00
}
2022-12-14 21:38:28 -05:00
// QueryIP send DNS query to the name server with the client's IP and IP options.
2022-11-29 19:43:39 -05:00
func ( c * Client ) QueryIP ( ctx context . Context , domain string , option dns . IPOption ) ( [ ] net . IP , error ) {
queryOption := option . With ( c . queryStrategy )
if ! queryOption . IsValid ( ) {
newError ( c . server . Name ( ) , " returns empty answer: " , domain , ". " , toReqTypes ( option ) ) . AtInfo ( ) . WriteToLog ( )
return nil , dns . ErrEmptyResponse
}
2022-12-14 21:38:28 -05:00
server := c . server
if queryOption . FakeEnable && c . fakeDNS != nil {
server = c . fakeDNS
}
2022-11-29 19:43:39 -05:00
disableCache := c . cacheStrategy == CacheStrategy_CacheDisabled
ctx = session . ContextWithInbound ( ctx , & session . Inbound { Tag : c . tag } )
2020-12-18 04:24:33 -05:00
ctx , cancel := context . WithTimeout ( ctx , 4 * time . Second )
2022-12-14 21:38:28 -05:00
ips , err := server . QueryIP ( ctx , domain , c . clientIP , queryOption , disableCache )
2020-12-18 04:24:33 -05:00
cancel ( )
2022-12-14 21:38:28 -05:00
if err != nil || queryOption . FakeEnable {
2020-12-18 04:24:33 -05:00
return ips , err
}
return c . MatchExpectedIPs ( domain , ips )
2018-11-22 10:29:09 -05:00
}
2020-12-18 04:24:33 -05:00
// MatchExpectedIPs matches queried domain IPs with expected IPs and returns matched ones.
func ( c * Client ) MatchExpectedIPs ( domain string , ips [ ] net . IP ) ( [ ] net . IP , error ) {
if len ( c . expectIPs ) == 0 {
return ips , nil
}
newIps := [ ] net . IP { }
for _ , ip := range ips {
for _ , matcher := range c . expectIPs {
if matcher . Match ( ip ) {
newIps = append ( newIps , ip )
break
}
}
}
if len ( newIps ) == 0 {
return nil , errExpectedIPNonMatch
2018-06-26 09:16:45 -04:00
}
2020-12-18 04:24:33 -05:00
newError ( "domain " , domain , " expectIPs " , newIps , " matched at server " , c . Name ( ) ) . AtDebug ( ) . WriteToLog ( )
return newIps , nil
2016-05-16 12:05:01 -04:00
}