تكامل Fake-IP
Fake-IP هي تقنية تُعيد فيها استعلامات DNS عناوين IP مزيفة مؤقتة من مجمع محجوز. عندما تصل حركة مرور إلى IP مزيف، يبحث النظام عن النطاق الأصلي ويوجّه بناءً على اسم النطاق بدلاً من عنوان IP (الذي لا معنى له).
لماذا Fake-IP؟
بدون Fake-IP، يواجه التوكيل الشفاف المبني على TUN معضلة:
App → DNS Query "google.com" → Real DNS → 142.250.80.14
App → Connect to 142.250.80.14 → TUN → Xray
→ Xray sees IP 142.250.80.14, but doesn't know domain "google.com"
→ Cannot do domain-based routing!مع Fake-IP:
App → DNS Query "google.com" → Fake DNS → 198.18.0.42 (fake)
App → Connect to 198.18.0.42 → TUN → Xray
→ Xray detects 198.18.0.42 is in fake pool
→ Looks up: 198.18.0.42 → "google.com"
→ Routes based on domain "google.com"
→ Outbound resolves real IP and connectsالبنية المعمارية
flowchart TB
App([Application]) -->|"DNS: google.com?"| DNS["Xray DNS Module"]
DNS -->|"Return 198.18.0.42<br/>(fake IP)"| App
App -->|"Connect to<br/>198.18.0.42"| TUN["TUN Interface"]
TUN -->|"dest=198.18.0.42"| Handler["TUN Handler"]
Handler -->|"DispatchLink(ctx,<br/>198.18.0.42:443)"| Dispatcher["Dispatcher"]
Dispatcher --> Sniff["Sniffer"]
Sniff --> FakeDNS["FakeDNS Sniffer"]
FakeDNS -->|"198.18.0.42 in pool?<br/>→ google.com"| Override["Override dest:<br/>google.com:443"]
Override --> Router["Router"]
Router -->|"domain-based<br/>rule match"| Outbound["Outbound"]
Outbound -->|"Resolve google.com<br/>→ real IP"| Server([Real Server])FakeDNS Holder (app/dns/fakedns/fake.go)
type Holder struct {
domainToIP cache.Lru // LRU cache: domain → fake IP
ipRange *net.IPNet // CIDR pool (e.g., 198.18.0.0/15)
mu *sync.Mutex
config *FakeDnsPool
}تخصيص عناوين IP
func (fkdns *Holder) GetFakeIPForDomain(domain string) []net.Address {
fkdns.mu.Lock()
defer fkdns.mu.Unlock()
// Check cache first
if v, ok := fkdns.domainToIP.Get(domain); ok {
return []net.Address{v.(net.Address)}
}
// Generate new fake IP based on current time
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.Add(bigIntIP, new(big.Int).SetUint64(currentTimeMillis))
// Handle collision: increment until unused IP found
for {
ip := net.IPAddress(bigIntIP.Bytes())
if _, ok := fkdns.domainToIP.PeekKeyFromValue(ip); !ok {
break // IP not in use
}
bigIntIP.Add(bigIntIP, big.NewInt(1))
if !fkdns.ipRange.Contains(bigIntIP.Bytes()) {
bigIntIP = big.NewInt(0).SetBytes(fkdns.ipRange.IP) // wrap around
}
}
fkdns.domainToIP.Put(domain, ip)
return []net.Address{ip}
}البحث العكسي
func (fkdns *Holder) GetDomainFromFakeDNS(ip net.Address) string {
if !fkdns.ipRange.Contains(ip.IP()) {
return "" // not a fake IP
}
if k, ok := fkdns.domainToIP.GetKeyFromValue(ip); ok {
return k.(string)
}
return "" // fake IP but domain expired from LRU cache
}فحص المجمع
func (fkdns *Holder) IsIPInIPPool(ip net.Address) bool {
if ip.Family().IsDomain() {
return false
}
return fkdns.ipRange.Contains(ip.IP())
}دعم المجمعات المتعددة
يدعم HolderMulti مجمعات Fake منفصلة لـ IPv4 وIPv6:
type HolderMulti struct {
holders []*Holder // e.g., [0]=198.18.0.0/15, [1]=fc00::/18
}
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 // may return both IPv4 and IPv6 fake IPs
}التكامل مع فحص الموزع
يتكون تكامل Fake-IP في الموزع من ثلاثة مكونات:
1. فاحص بيانات FakeDNS الوصفية
// app/dispatcher/fakednssniffer.go
func newFakeDNSSniffer(ctx) (protocolSnifferWithMetadata, error) {
return protocolSnifferWithMetadata{
protocolSniffer: func(ctx, _ []byte) (SniffResult, error) {
ob := session.OutboundsFromContext(ctx)
dest := ob[len(ob)-1].Target
if fkr0.IsIPInIPPool(dest.Address) {
domain := fkr0.GetDomainFromFakeDNS(dest.Address)
if domain != "" {
return &fakeDNSSniffResult{domain: domain}, nil
}
}
return nil, common.ErrNoClue
},
metadataSniffer: true, // No payload needed
network: net.Network_TCP,
}
}2. مُركّب FakeDNS+Others
عندما يُحلّل Fake-IP النطاق، ولكننا نريد أيضًا الكشف عن البروتوكول بناءً على المحتوى:
// "fakedns+others" sniffer
// Returns Fake-IP domain as the domain result
// But uses content sniffing (TLS/HTTP) for protocol detection3. قرار التجاوز
func (d *DefaultDispatcher) shouldOverride(ctx, result, request, destination) bool {
for _, p := range request.OverrideDestinationForProtocol {
// Always override fake IPs, regardless of RouteOnly
if fkr0.IsIPInIPPool(destination.Address) && p == "fakedns" {
return true
}
}
}في مسار التوزيع:
// After sniffing:
isFakeIP := fkr0.IsIPInIPPool(ob.Target.Address)
if sniffingRequest.RouteOnly && !isFakeIP {
ob.RouteTarget = destination // route-only: don't change target
} else {
ob.Target = destination // full override (always for fake IPs)
}الإعداد
{
"dns": {
"servers": [
{
"address": "fakedns",
"domains": ["geosite:cn"]
},
"8.8.8.8"
]
},
"fakedns": {
"ipPool": "198.18.0.0/15",
"poolSize": 65535
},
"inbounds": [{
"tag": "tun-in",
"protocol": "tun",
"sniffing": {
"enabled": true,
"destOverride": ["fakedns+others"]
}
}]
}نقاط الإعداد الرئيسية:
- كتلة
fakednsتُهيّئ مجمع IP وحجم LRU - خادم DNS بعنوان
"fakedns"يُفعّل استجابات Fake-IP - فحص
destOverrideيتضمن"fakedns"أو"fakedns+others"
دورة حياة اتصال Fake-IP
sequenceDiagram
participant App
participant DNS as Xray DNS
participant FDH as FakeDNS Holder
participant TUN
participant D as Dispatcher
participant S as Sniffer
participant R as Router
participant O as Outbound
App->>DNS: Query A google.com
DNS->>FDH: GetFakeIPForDomain("google.com")
FDH-->>DNS: 198.18.1.42
DNS-->>App: A 198.18.1.42
App->>TUN: TCP SYN to 198.18.1.42:443
TUN->>D: DispatchLink(ctx, 198.18.1.42:443)
D->>S: SniffMetadata(ctx)
S->>FDH: IsIPInIPPool(198.18.1.42)?
FDH-->>S: Yes
S->>FDH: GetDomainFromFakeDNS(198.18.1.42)
FDH-->>S: "google.com"
S-->>D: FakeDNS result: google.com
D->>D: Override target: google.com:443
D->>R: PickRoute(google.com:443)
R-->>D: outbound-proxy
D->>O: Dispatch(google.com:443)
O->>O: Resolve google.com → 142.250.80.14
O->>O: Connect to 142.250.80.14:443ملاحظات التنفيذ
إخلاء LRU: ذاكرة التخزين المؤقت لعناوين IP المزيفة مبنية على LRU. عندما تمتلئ الذاكرة، يتم إخلاء أقدم إدخال. إذا كان لدى التطبيق اتصال مفتوح لعنوان IP المزيف المُخلى، سيفشل البحث العكسي. حدد حجم LRU بشكل مناسب (65535 هو الافتراضي).
توليد IP المبني على الوقت: يُشتق عنوان IP المزيف من
currentTimeMillis % poolSize. هذا يوفر توزيعًا عبر المجمع لكن قد يتعارض. حلقة حل التعارضات تتعامل مع هذا.فحص البيانات الوصفية فقط: لا يحتاج فاحص Fake-IP إلى قراءة أي بايتات من الحمولة. يفحص عنوان IP الوجهة مقابل المجمع قبل وصول أي بيانات. هذا يجعله فوريًا.
التجاوز الكامل دائمًا لعناوين IP المزيفة: حتى مع
routeOnly: true، يجب تجاوز عناوين IP المزيفة بالكامل (وليس فقط للتوجيه). عنوان IP المزيف ليس له معنى حقيقي — الاتصال به سيفشل.مجمعات IPv4 + IPv6: استخدم
HolderMultiمع مجمع IPv4 واحد ومجمع IPv6 واحد. ستُعيد وحدة DNS النوع المناسب بناءً على الاستعلام (A مقابل AAAA).يجب اعتراض DNS: لكي تعمل Fake-IP، يجب أن تصل استعلامات DNS الخاصة بالتطبيق إلى وحدة DNS في Xray. يتطلب هذا عادةً:
- TUN مع قواعد توجيه
dns.hijack - أو توجيه DNS النظام إلى مستمع DNS في Xray
- TUN مع قواعد توجيه