mirror of https://github.com/XTLS/Xray-core
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
134 lines
3.5 KiB
134 lines
3.5 KiB
package fakedns |
|
|
|
import ( |
|
"context" |
|
"math" |
|
"math/big" |
|
gonet "net" |
|
"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 |
|
|
|
config *FakeDnsPool |
|
} |
|
|
|
func (*Holder) Type() interface{} { |
|
return (*dns.FakeDNSEngine)(nil) |
|
} |
|
|
|
func (fkdns *Holder) Start() error { |
|
return fkdns.initializeFromConfig() |
|
} |
|
|
|
func (fkdns *Holder) Close() error { |
|
fkdns.domainToIP = nil |
|
fkdns.ipRange = 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.FakeIPPool, 65535) |
|
if err != nil { |
|
return nil, err |
|
} |
|
return fkdns, nil |
|
} |
|
|
|
func NewFakeDNSHolderConfigOnly(conf *FakeDnsPool) (*Holder, error) { |
|
return &Holder{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 |
|
return nil |
|
} |
|
|
|
// GetFakeIPForDomain check and generate a fake IP for a domain name |
|
func (fkdns *Holder) GetFakeIPForDomain(domain string) []net.Address { |
|
if v, ok := fkdns.domainToIP.Get(domain); ok { |
|
return []net.Address{v.(net.Address)} |
|
} |
|
var currentTimeMillis = uint64(time.Now().UnixNano() / 1e6) |
|
ones, bits := fkdns.ipRange.Mask.Size() |
|
rooms := bits - ones |
|
if rooms < 64 { |
|
currentTimeMillis %= (uint64(1) << rooms) |
|
} |
|
var 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 check 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 "" |
|
} |
|
|
|
// GetFakeIPRange return fake IP range from configuration |
|
func (fkdns *Holder) GetFakeIPRange() *gonet.IPNet { |
|
return fkdns.ipRange |
|
} |
|
|
|
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 |
|
})) |
|
}
|
|
|