Dokodemo-door
Dokodemo-door ("الباب إلى أي مكان" باليابانية) هو معالج بروتوكول وارد فقط للوكالة الشفافة. يقبل الاتصالات على أي منفذ ويحوّلها إلى وجهة مُهيّأة أو محددة ديناميكياً. وهو الآلية الأساسية لـ TProxy وإعادة التوجيه الشفافة وتحويل المنافذ ذات الوجهة الثابتة.
نظرة عامة
- الاتجاه: وارد فقط
- النقل: TCP + UDP (قابل للتهيئة)
- التشفير: غير متاح (شفاف)
- المصادقة: غير متاح
- حالات الاستخدام: الوكيل الشفاف (iptables REDIRECT/TPROXY)، تحويل المنافذ، اعتراض DNS
البنية المعمارية
لا يملك Dokodemo-door بروتوكولاً خاصاً به — يأخذ ببساطة الاتصالات الواردة ويحوّلها. تُحدّد الوجهة بإحدى ثلاث آليات:
- وجهة ثابتة: العنوان والمنفذ محددان في الإعدادات
- متابعة إعادة التوجيه: استخدام الوجهة الأصلية من iptables REDIRECT/TPROXY (المُستخلصة من البيانات الوصفية للاتصال الصادر)
- تعيين المنافذ: ربط المنفذ الوارد بعنوان/منفذ وجهة مختلف
graph TD
A[Incoming Connection] --> B{FollowRedirect?}
B -->|Yes| C[Get original dest from OS/metadata]
B -->|No| D{Fixed address configured?}
D -->|Yes| E[Use config address:port]
D -->|No| F[Use connection local address]
C --> G[Dispatch to routing]
E --> G
F --> Gالإعدادات
type DokodemoDoor struct {
policyManager policy.Manager
config *Config
address net.Address // Fixed destination address
port net.Port // Fixed destination port
portMap map[string]string // Port remapping
sockopt *session.Sockopt // Socket options (for mark)
}المصدر: proxy/dokodemo/dokodemo.go:33-40
حقول الإعدادات الرئيسية:
| الحقل | الوصف |
|---|---|
Address | عنوان الوجهة المُحدد مسبقاً |
Port | منفذ الوجهة المُحدد مسبقاً |
Networks | أنواع الشبكات المسموحة (TCP أو UDP أو كلاهما) |
FollowRedirect | إذا كان true، استخدم الوجهة الأصلية من البيانات الوصفية للاتصال |
UserLevel | مستوى السياسة |
PortMap | خريطة "المنفذ_الوارد" -> "المضيف:المنفذ" للتوجيه لكل منفذ |
معالجة الاتصال
الملف: proxy/dokodemo/dokodemo.go:69-192
تحديد الوجهة
func (d *DokodemoDoor) Process(ctx context.Context, network net.Network,
conn stat.Connection, dispatcher routing.Dispatcher) error {
dest := net.Destination{
Network: network,
Address: d.address, // from config
Port: d.port, // from config
}
if d.config.FollowRedirect {
// Use the destination from outbound metadata
// (set by iptables REDIRECT/TPROXY or sniffing)
outbounds := session.OutboundsFromContext(ctx)
if len(outbounds) > 0 {
ob := outbounds[len(outbounds)-1]
if ob.Target.IsValid() {
dest = ob.Target
}
}
}
}المصدر: proxy/dokodemo/dokodemo.go:69-128
كشف اسم خادم TLS
عند تفعيل FollowRedirect وكان الاتصال الوارد عبر TLS، يمكن لـ dokodemo استخراج SNI (إشارة اسم الخادم) للتوجيه القائم على النطاق:
if tlsConn, ok := iConn.(tls.Interface); ok && !destinationOverridden {
if serverName := tlsConn.HandshakeContextServerName(ctx); serverName != "" {
dest.Address = net.DomainAddress(serverName)
destinationOverridden = true
ctx = session.ContextWithMitmServerName(ctx, serverName)
}
}المصدر: proxy/dokodemo/dokodemo.go:115-124
تعيين المنافذ
عند تهيئة PortMap، يُستخدم المنفذ الوارد للبحث عن وجهة مختلفة:
if d.portMap != nil && d.portMap[port] != "" {
h, p, _ := net.SplitHostPort(d.portMap[port])
if len(h) > 0 {
dest.Address = net.ParseAddress(h)
}
if len(p) > 0 {
dest.Port = net.Port(strconv.Atoi(p))
}
}المصدر: proxy/dokodemo/dokodemo.go:93-101
معالجة TCP مقابل UDP
TCP: buf.NewReader(conn) وbuf.NewWriter(conn) القياسية:
if dest.Network == net.Network_TCP {
reader = buf.NewReader(conn)
} else {
reader = buf.NewPacketReader(conn)
}المصدر: proxy/dokodemo/dokodemo.go:146-150
UDP مع TProxy: للوكالة الشفافة لـ UDP على Linux، يحتاج الخادم إلى "تزييف" عنوان المصدر عند إرسال حزم الرد. يُستخدم FakeUDP() لهذا:
if destinationOverridden {
back := conn.RemoteAddr().(*net.UDPAddr)
addr := &net.UDPAddr{
IP: dest.Address.IP(),
Port: int(dest.Port),
}
pConn, err := FakeUDP(addr, mark)
writer = NewPacketWriter(pConn, &dest, mark, back)
}المصدر: proxy/dokodemo/dokodemo.go:160-182
FakeUDP (Linux TProxy)
الملف: proxy/dokodemo/fakeudp_linux.go
على Linux، يُنشئ FakeUDP مقبس UDP مرتبط بعنوان الوجهة باستخدام خيار المقبس IP_TRANSPARENT، مما يسمح للنواة بقبول الحزم الموجهة لأي عنوان. تُرسل حزم الرد من عنوان المصدر "المُزيّف" هذا باستخدام WriteTo().
الملف: proxy/dokodemo/fakeudp_other.go
على الأنظمة غير Linux، يُرجع FakeUDP خطأ لأن TProxy خاص بـ Linux.
كاتب الحزم (PacketWriter)
يتعامل PacketWriter مع كتابة حزم الرد UDP إلى العميل، مع دعم عناوين وجهات متعددة (لاستجابات DNS من خوادم مختلفة):
type PacketWriter struct {
conn net.PacketConn
conns map[net.Destination]net.PacketConn // cached per-dest fake sockets
mark int // SO_MARK value
back *net.UDPAddr // client's address
}المصدر: proxy/dokodemo/dokodemo.go:205-210
كل وجهة فريدة تحصل على مقبس UDP مُزيّف خاص بها. يُنشئ الكاتب مقابس جديدة عند الحاجة:
func (w *PacketWriter) WriteMultiBuffer(mb buf.MultiBuffer) error {
if b.UDP != nil && b.UDP.Address.Family().IsIP() {
conn := w.conns[*b.UDP]
if conn == nil {
conn, _ = FakeUDP(&net.UDPAddr{IP: b.UDP.Address.IP(), Port: int(b.UDP.Port)}, w.mark)
w.conns[*b.UDP] = conn
}
conn.WriteTo(b.Bytes(), w.back)
}
}المصدر: proxy/dokodemo/dokodemo.go:212-253
DispatchLink
يستخدم Dokodemo دالة dispatcher.DispatchLink() بدلاً من dispatcher.Dispatch(). تمرّر القارئ/الكاتب مباشرة وتحظر حتى اكتمال الاتصال:
if err := dispatcher.DispatchLink(ctx, dest, &transport.Link{
Reader: reader,
Writer: writer,
}); err != nil {
return errors.New("failed to dispatch request").Base(err)
}
return nil // DispatchLink blocks until outbound finishesالمصدر: proxy/dokodemo/dokodemo.go:185-192
أنماط الاستخدام
iptables REDIRECT (TCP)
iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port 12345الإعدادات: FollowRedirect: true، الشبكات: TCP
تُعيد النواة كتابة الوجهة إلى 127.0.0.1:12345، لكن يمكن لـ Xray استعادة الوجهة الأصلية من SO_ORIGINAL_DST.
iptables TPROXY (TCP + UDP)
iptables -t mangle -A PREROUTING -p udp -j TPROXY --to-port 12345 --tproxy-mark 1الإعدادات: FollowRedirect: true، الشبكات: TCP+UDP، مع علامات المقبس المناسبة.
وجهة ثابتة (تحويل المنافذ)
الإعدادات: Address: "10.0.0.1"، Port: 8080، FollowRedirect: false
جميع الاتصالات الواردة على منفذ الاستماع تُحوّل إلى 10.0.0.1:8080.
ملاحظات التنفيذ
- CanSpliceCopy = 1: يُعيّن Dokodemo أدنى مستوى splice لأنه لا يوجد عبء بروتوكول — تمر البايتات الخام دون أي ترميز أو تأطير.
المصدر: proxy/dokodemo/dokodemo.go:132
- التحقق من الشبكة: تتطلب دالة
Init()تحديد شبكة واحدة على الأقل. تُسبب شريحةNetworksالفارغة خطأ.
المصدر: proxy/dokodemo/dokodemo.go:44-46
- العنوان الاحتياطي: إذا لم يُهيّأ عنوان وكان FollowRedirect معطّلاً، يستخدم dokodemo عنوان الاتصال المحلي كاحتياطي، مختاراً
127.0.0.1أو::1بناءً على ما إذا كان العنوان المحلي يحتوي على نقطة.
المصدر: proxy/dokodemo/dokodemo.go:79-90
نشر علامة المقبس: تُنشر قيمة
sockopt.Markمن إعدادات الوارد إلى مقابس FakeUDP، مما يضمن التفاعل الصحيح مع جدول التوجيه على Linux.دعم MITM: عند تفعيل كشف TLS SNI، يُعيّن dokodemo قيم
MitmServerNameوMitmAlpn11على السياق، مما يُمكّن المعالجات اللاحقة من إجراء اعتراض TLS.
المصدر: proxy/dokodemo/dokodemo.go:119-123