mirror of
https://github.com/XTLS/Xray-core.git
synced 2024-11-02 19:19:20 +02:00
c1a54ae58e
In rare cases different domains asking for dns will return the same IP. Add a mutex.
251 lines
6.2 KiB
Go
251 lines
6.2 KiB
Go
package fakedns
|
|
|
|
import (
|
|
"context"
|
|
"math"
|
|
"math/big"
|
|
gonet "net"
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/xtls/xray-core/common"
|
|
"github.com/xtls/xray-core/common/cache"
|
|
"github.com/xtls/xray-core/common/net"
|
|
"github.com/xtls/xray-core/features/dns"
|
|
)
|
|
|
|
type Holder struct {
|
|
domainToIP cache.Lru
|
|
ipRange *gonet.IPNet
|
|
mu *sync.Mutex
|
|
|
|
config *FakeDnsPool
|
|
}
|
|
|
|
func (fkdns *Holder) IsIPInIPPool(ip net.Address) bool {
|
|
if ip.Family().IsDomain() {
|
|
return false
|
|
}
|
|
return fkdns.ipRange.Contains(ip.IP())
|
|
}
|
|
|
|
func (fkdns *Holder) GetFakeIPForDomain3(domain string, ipv4, ipv6 bool) []net.Address {
|
|
isIPv6 := fkdns.ipRange.IP.To4() == nil
|
|
if (isIPv6 && ipv6) || (!isIPv6 && ipv4) {
|
|
return fkdns.GetFakeIPForDomain(domain)
|
|
}
|
|
return []net.Address{}
|
|
}
|
|
|
|
func (*Holder) Type() interface{} {
|
|
return (*dns.FakeDNSEngine)(nil)
|
|
}
|
|
|
|
func (fkdns *Holder) Start() error {
|
|
if fkdns.config != nil && fkdns.config.IpPool != "" && fkdns.config.LruSize != 0 {
|
|
return fkdns.initializeFromConfig()
|
|
}
|
|
return newError("invalid fakeDNS setting")
|
|
}
|
|
|
|
func (fkdns *Holder) Close() error {
|
|
fkdns.domainToIP = nil
|
|
fkdns.ipRange = nil
|
|
fkdns.mu = nil
|
|
return nil
|
|
}
|
|
|
|
func NewFakeDNSHolder() (*Holder, error) {
|
|
var fkdns *Holder
|
|
var err error
|
|
|
|
if fkdns, err = NewFakeDNSHolderConfigOnly(nil); err != nil {
|
|
return nil, newError("Unable to create Fake Dns Engine").Base(err).AtError()
|
|
}
|
|
err = fkdns.initialize(dns.FakeIPv4Pool, 65535)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return fkdns, nil
|
|
}
|
|
|
|
func NewFakeDNSHolderConfigOnly(conf *FakeDnsPool) (*Holder, error) {
|
|
return &Holder{nil, nil, nil, conf}, nil
|
|
}
|
|
|
|
func (fkdns *Holder) initializeFromConfig() error {
|
|
return fkdns.initialize(fkdns.config.IpPool, int(fkdns.config.LruSize))
|
|
}
|
|
|
|
func (fkdns *Holder) initialize(ipPoolCidr string, lruSize int) error {
|
|
var ipRange *gonet.IPNet
|
|
var err error
|
|
|
|
if _, ipRange, err = gonet.ParseCIDR(ipPoolCidr); err != nil {
|
|
return newError("Unable to parse CIDR for Fake DNS IP assignment").Base(err).AtError()
|
|
}
|
|
|
|
ones, bits := ipRange.Mask.Size()
|
|
rooms := bits - ones
|
|
if math.Log2(float64(lruSize)) >= float64(rooms) {
|
|
return newError("LRU size is bigger than subnet size").AtError()
|
|
}
|
|
fkdns.domainToIP = cache.NewLru(lruSize)
|
|
fkdns.ipRange = ipRange
|
|
fkdns.mu = new(sync.Mutex)
|
|
return nil
|
|
}
|
|
|
|
// GetFakeIPForDomain checks and generates a fake IP for a domain name
|
|
func (fkdns *Holder) GetFakeIPForDomain(domain string) []net.Address {
|
|
fkdns.mu.Lock()
|
|
defer fkdns.mu.Unlock()
|
|
if v, ok := fkdns.domainToIP.Get(domain); ok {
|
|
return []net.Address{v.(net.Address)}
|
|
}
|
|
currentTimeMillis := uint64(time.Now().UnixNano() / 1e6)
|
|
ones, bits := fkdns.ipRange.Mask.Size()
|
|
rooms := bits - ones
|
|
if rooms < 64 {
|
|
currentTimeMillis %= (uint64(1) << rooms)
|
|
}
|
|
bigIntIP := big.NewInt(0).SetBytes(fkdns.ipRange.IP)
|
|
bigIntIP = bigIntIP.Add(bigIntIP, new(big.Int).SetUint64(currentTimeMillis))
|
|
var ip net.Address
|
|
for {
|
|
ip = net.IPAddress(bigIntIP.Bytes())
|
|
|
|
// if we run for a long time, we may go back to beginning and start seeing the IP in use
|
|
if _, ok := fkdns.domainToIP.PeekKeyFromValue(ip); !ok {
|
|
break
|
|
}
|
|
|
|
bigIntIP = bigIntIP.Add(bigIntIP, big.NewInt(1))
|
|
if !fkdns.ipRange.Contains(bigIntIP.Bytes()) {
|
|
bigIntIP = big.NewInt(0).SetBytes(fkdns.ipRange.IP)
|
|
}
|
|
}
|
|
fkdns.domainToIP.Put(domain, ip)
|
|
return []net.Address{ip}
|
|
}
|
|
|
|
// GetDomainFromFakeDNS checks if an IP is a fake IP and have corresponding domain name
|
|
func (fkdns *Holder) GetDomainFromFakeDNS(ip net.Address) string {
|
|
if !ip.Family().IsIP() || !fkdns.ipRange.Contains(ip.IP()) {
|
|
return ""
|
|
}
|
|
if k, ok := fkdns.domainToIP.GetKeyFromValue(ip); ok {
|
|
return k.(string)
|
|
}
|
|
newError("A fake ip request to ", ip, ", however there is no matching domain name in fake DNS").AtInfo().WriteToLog()
|
|
return ""
|
|
}
|
|
|
|
type HolderMulti struct {
|
|
holders []*Holder
|
|
|
|
config *FakeDnsPoolMulti
|
|
}
|
|
|
|
func (h *HolderMulti) IsIPInIPPool(ip net.Address) bool {
|
|
if ip.Family().IsDomain() {
|
|
return false
|
|
}
|
|
for _, v := range h.holders {
|
|
if v.IsIPInIPPool(ip) {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func (h *HolderMulti) GetFakeIPForDomain3(domain string, ipv4, ipv6 bool) []net.Address {
|
|
var ret []net.Address
|
|
for _, v := range h.holders {
|
|
ret = append(ret, v.GetFakeIPForDomain3(domain, ipv4, ipv6)...)
|
|
}
|
|
return ret
|
|
}
|
|
|
|
func (h *HolderMulti) GetFakeIPForDomain(domain string) []net.Address {
|
|
var ret []net.Address
|
|
for _, v := range h.holders {
|
|
ret = append(ret, v.GetFakeIPForDomain(domain)...)
|
|
}
|
|
return ret
|
|
}
|
|
|
|
func (h *HolderMulti) GetDomainFromFakeDNS(ip net.Address) string {
|
|
for _, v := range h.holders {
|
|
if domain := v.GetDomainFromFakeDNS(ip); domain != "" {
|
|
return domain
|
|
}
|
|
}
|
|
return ""
|
|
}
|
|
|
|
func (h *HolderMulti) Type() interface{} {
|
|
return (*dns.FakeDNSEngine)(nil)
|
|
}
|
|
|
|
func (h *HolderMulti) Start() error {
|
|
for _, v := range h.holders {
|
|
if v.config != nil && v.config.IpPool != "" && v.config.LruSize != 0 {
|
|
if err := v.Start(); err != nil {
|
|
return newError("Cannot start all fake dns pools").Base(err)
|
|
}
|
|
} else {
|
|
return newError("invalid fakeDNS setting")
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (h *HolderMulti) Close() error {
|
|
for _, v := range h.holders {
|
|
if err := v.Close(); err != nil {
|
|
return newError("Cannot close all fake dns pools").Base(err)
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (h *HolderMulti) createHolderGroups() error {
|
|
for _, v := range h.config.Pools {
|
|
holder, err := NewFakeDNSHolderConfigOnly(v)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
h.holders = append(h.holders, holder)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func NewFakeDNSHolderMulti(conf *FakeDnsPoolMulti) (*HolderMulti, error) {
|
|
holderMulti := &HolderMulti{nil, conf}
|
|
if err := holderMulti.createHolderGroups(); err != nil {
|
|
return nil, err
|
|
}
|
|
return holderMulti, nil
|
|
}
|
|
|
|
func init() {
|
|
common.Must(common.RegisterConfig((*FakeDnsPool)(nil), func(ctx context.Context, config interface{}) (interface{}, error) {
|
|
var f *Holder
|
|
var err error
|
|
if f, err = NewFakeDNSHolderConfigOnly(config.(*FakeDnsPool)); err != nil {
|
|
return nil, err
|
|
}
|
|
return f, nil
|
|
}))
|
|
|
|
common.Must(common.RegisterConfig((*FakeDnsPoolMulti)(nil), func(ctx context.Context, config interface{}) (interface{}, error) {
|
|
var f *HolderMulti
|
|
var err error
|
|
if f, err = NewFakeDNSHolderMulti(config.(*FakeDnsPoolMulti)); err != nil {
|
|
return nil, err
|
|
}
|
|
return f, nil
|
|
}))
|
|
}
|