mirror of
https://github.com/XTLS/Xray-core.git
synced 2025-05-01 01:44:15 +00:00
Add Fake DNS support (#309)
Co-authored-by: Xiaokang Wang <xiaokangwang@outlook.com> Co-authored-by: loyalsoldier <10487845+Loyalsoldier@users.noreply.github.com> Co-authored-by: kslr <kslrwang@gmail.com>
This commit is contained in:
parent
db32ce6fd9
commit
f50eff5ebb
43 changed files with 1351 additions and 301 deletions
136
app/dns/fakedns/fake.go
Normal file
136
app/dns/fakedns/fake.go
Normal file
|
@ -0,0 +1,136 @@
|
|||
// +build !confonly
|
||||
|
||||
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("240.0.0.0/8", 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
|
||||
}))
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue