SOCKS5 و HTTP Proxy
يُنفّذ Xray-core بروتوكولات SOCKS4/4a/5 وHTTP/HTTPS كمعالجات واردة (خادم وكيل محلي) وصادرة (عميل وكيل علوي). يمكن للوارد SOCKS أيضاً الكشف التلقائي عن طلبات HTTP وتحويلها إلى معالج HTTP المُدمج.
بروتوكول SOCKS5
نظرة عامة
- الاتجاه: وارد + صادر
- النقل: TCP + UDP
- المصادقة: بدون أو اسم مستخدم/كلمة مرور (RFC 1929)
- الأوامر: CONNECT (TCP)، UDP ASSOCIATE
- المعايير: RFC 1928 (SOCKS5)، RFC 1929 (مصادقة اسم مستخدم/كلمة مرور)
صيغة السلك (Wire Format)
مصافحة SOCKS5
sequenceDiagram
participant C as Client
participant S as Server
C->>S: Version(1B) + NMethods(1B) + Methods(NB)
S->>C: Version(1B) + ChosenMethod(1B)
alt Username/Password Auth
C->>S: AuthVer(1B) + ULen(1B) + User + PLen(1B) + Pass
S->>C: AuthVer(1B) + Status(1B)
end
C->>S: Version(1B) + Cmd(1B) + Rsv(1B) + Address
S->>C: Version(1B) + Rep(1B) + Rsv(1B) + BndAddressتفاوض المصادقة
Client -> Server:
+-----+----------+----------+
| VER | NMETHODS | METHODS |
| 1B | 1B | 1-255B |
+-----+----------+----------+
0x05
Server -> Client:
+-----+--------+
| VER | METHOD |
| 1B | 1B |
+-----+--------+
0x05قيم الطرق:
| القيمة | الطريقة |
|---|---|
0x00 | بدون مصادقة |
0x02 | اسم مستخدم/كلمة مرور |
0xFF | لا توجد طرق مقبولة |
المصدر: proxy/socks/protocol.go:27-33
مصادقة اسم المستخدم/كلمة المرور (RFC 1929)
Client -> Server:
+-----+------+----------+------+----------+
| VER | ULEN | UNAME | PLEN | PASSWD |
| 1B | 1B | 1-255B | 1B | 1-255B |
+-----+------+----------+------+----------+
0x01
Server -> Client:
+-----+--------+
| VER | STATUS |
| 1B | 1B |
+-----+--------+
0x01 0x00=okالمصدر: proxy/socks/protocol.go:232-265
طلب SOCKS5
+-----+-----+------+----------+----------+------+
| VER | CMD | RSV | ATYP | DST.ADDR | PORT |
| 1B | 1B | 1B | 1B | variable | 2B |
+-----+-----+------+----------+----------+------+
0x05 0x00الأوامر:
| القيمة | الأمر | الدعم |
|---|---|---|
0x01 | TCP CONNECT | نعم |
0x02 | TCP BIND | لا (يُرجع 0x07) |
0x03 | UDP ASSOCIATE | نعم (إذا مُفعّل) |
0xF0 | Tor Resolve | يُعامل كـ CONNECT |
0xF1 | Tor Resolve PTR | يُعامل كـ CONNECT |
المصدر: proxy/socks/protocol.go:15-22، proxy/socks/protocol.go:164-180
استجابة SOCKS5
+-----+-----+------+----------+----------+------+
| VER | REP | RSV | ATYP | BND.ADDR | PORT |
| 1B | 1B | 1B | 1B | variable | 2B |
+-----+-----+------+----------+----------+------+
0x05 0x00لـ UDP ASSOCIATE، يشير BND.ADDR وBND.PORT إلى نقطة نهاية ترحيل UDP.
المصدر: proxy/socks/protocol.go:300-310
حزمة بيانات SOCKS5 UDP
+------+------+------+----------+----------+------+---------+
| RSV | RSV | FRAG | ATYP | DST.ADDR | PORT | DATA |
| 1B | 1B | 1B | 1B | variable | 2B | ... |
+------+------+------+----------+----------+------+---------+
0x00 0x00المصدر: proxy/socks/protocol.go:324-363
يجب أن يكون بايت التجزئة 0x00 — UDP المُجزّأ غير مدعوم:
if packet.Byte(2) != 0 /* fragments */ {
return nil, errors.New("discarding fragmented payload.")
}المصدر: proxy/socks/protocol.go:334-336
دعم SOCKS4/4a
يتعامل الوارد أيضاً مع SOCKS4/4a بناءً على بايت الإصدار:
switch version {
case socks4Version: // 0x04
return s.handshake4(cmd, reader, writer)
case socks5Version: // 0x05
return s.handshake5(cmd, reader, writer)
}المصدر: proxy/socks/protocol.go:222-230
يوسّع SOCKS4a من SOCKS4 بدعم أسماء النطاقات: إذا بدأ عنوان IP بـ 0x00، يتبعه اسم نطاق بعد فاصل null لمعرّف المستخدم.
المصدر: proxy/socks/protocol.go:72-78
الوارد (الخادم)
الملف: proxy/socks/server.go
الكشف التلقائي: يقرأ الخادم أول بايت للتمييز بين SOCKS وHTTP:
if firstbyte[0] != 5 && firstbyte[0] != 4 {
// Not SOCKS, try HTTP
return s.httpServer.ProcessWithFirstbyte(ctx, network, conn, dispatcher, firstbyte...)
}المصدر: proxy/socks/server.go:92-95
تدفق UDP Associate:
- يُرسل العميل CONNECT بالأمر 0x03
- يستجيب الخادم بعنوان/منفذ ترحيل UDP
- يُرسل العميل حزم بيانات UDP إلى ذلك العنوان
- يستقبل الخادم على شبكة UDP، يفك ترويسة SOCKS5 UDP، ويوزّع
- يبقى اتصال TCP مفتوحاً كإشارة بقاء (keepalive)
func (*Server) handleUDP(c io.Reader) error {
// Wait until client closes the TCP connection
return common.Error2(io.Copy(buf.DiscardBytes, c))
}المصدر: proxy/socks/server.go:182-186
مُرشّح UDP: عند تفعيل المصادقة، يُسمح فقط للعملاء الذين أنشأوا اتصال TCP UDP ASSOCIATE بإرسال حزم UDP:
if s.udpFilter != nil && !s.udpFilter.Check(conn.RemoteAddr()) {
return nil
}المصدر: proxy/socks/server.go:189-192
الصادر (العميل)
الملف: proxy/socks/client.go
يُجري العميل مصافحة SOCKS5 الكاملة مع الخادم العلوي:
func ClientHandshake(request *RequestHeader, reader io.Reader, writer io.Writer) (*RequestHeader, error) {
// 1. Send auth method selection
// 2. Read server's chosen method
// 3. If password auth, send credentials
// 4. Send CONNECT or UDP ASSOCIATE request
// 5. Read response
}المصدر: proxy/socks/protocol.go:421-515
لـ UDP، يفتح العميل اتصال UDP منفصل إلى العنوان المُرجع من استجابة SOCKS5 UDP ASSOCIATE.
المصدر: proxy/socks/client.go:146-163
بروتوكول HTTP Proxy
نظرة عامة
- الاتجاه: وارد + صادر
- النقل: TCP، مقبس UNIX
- المصادقة: HTTP Basic Auth (ترويسة Proxy-Authorization)
- الطرق: CONNECT (نفق)، HTTP عادي (وكيل)
- UDP: غير مدعوم
HTTP CONNECT (النفق)
sequenceDiagram
participant C as Client
participant P as Proxy
participant S as Server
C->>P: CONNECT host:port HTTP/1.1
P->>S: TCP connect to host:port
S->>P: Connected
P->>C: HTTP/1.1 200 Connection established
C->>P: [raw bytes]
P->>S: [raw bytes]
S->>P: [raw bytes]
P->>C: [raw bytes]المصدر: proxy/http/server.go:176-202
وكالة HTTP العادي
للطلبات غير CONNECT، يقوم الخادم بما يلي:
- تحليل طلب HTTP الكامل باستخدام
http.ReadRequest()في Go - استخراج الوجهة من ترويسة
Hostأو الرابط - توزيع الطلب إلى المعالج الصادر
- دعم keep-alive للطلبات المتتابعة على نفس الاتصال
keepAlive := strings.TrimSpace(strings.ToLower(
request.Header.Get("Proxy-Connection"))) == "keep-alive"المصدر: proxy/http/server.go:163
المصادقة
يتم التحقق من المصادقة الأساسية عبر ترويسة Proxy-Authorization:
func parseBasicAuth(auth string) (username, password string, ok bool) {
const prefix = "Basic "
c, err := base64.StdEncoding.DecodeString(auth[len(prefix):])
cs := string(c)
s := strings.IndexByte(cs, ':')
return cs[:s], cs[s+1:], true
}المصدر: proxy/http/server.go:63-78
عند فشل المصادقة، يُرجع 407 Proxy Authentication Required مع ترويسة Proxy-Authenticate: Basic realm="proxy".
المصدر: proxy/http/server.go:128
دعم HTTP/2 (الصادر)
يدعم عميل HTTP الصادر كلاً من HTTP/1.1 وHTTP/2 عند الاتصال عبر وكيل HTTP علوي:
switch nextProto {
case "", "http/1.1":
return connectHTTP1(rawConn)
case "h2":
t := http2.Transport{}
h2clientConn, err := t.NewClientConn(rawConn)
return connectHTTP2(rawConn, h2clientConn)
}المصدر: proxy/http/client.go:320-332
تُخزّن اتصالات H2 مؤقتاً لكل وجهة للتعدد:
var cachedH2Conns map[net.Destination]h2Connالمصدر: proxy/http/client.go:47-48
معالجة استجابات 1xx
يتعامل الخادم بشكل صحيح مع استجابات HTTP 1xx الوسيطة (مثل 100 Continue) بتمريرها إلى العميل قبل قراءة الاستجابة الفعلية:
if strings.HasPrefix(status, "1") {
// Read until \r\n\r\n, forward to client
writer.Write(ResponseHeader1xx)
}
return http.ReadResponse(r, req)المصدر: proxy/http/server.go:316-344
الوارد (الخادم)
الملف: proxy/http/server.go
السلوكيات الرئيسية:
- يدعم وضع
AllowTransparentللوكالة الشفافة (لا يحتاج رابط مطلق) - يزيل ترويسات hop-by-hop وفقاً لمواصفات HTTP
- يُعيّن
User-Agentكسلسلة فارغة إذا لم يُقدّم (يمنع القيمة الافتراضية لـ Go) - يتعامل مع اتصالات keep-alive بحلقة (
goto Start)
الصادر (العميل)
الملف: proxy/http/client.go
يقوم العميل بما يلي:
- قراءة الحمولة الأولى من قارئ الرابط
- إنشاء نفق HTTP CONNECT مع الوكيل العلوي
- إرسال الحمولة الأولى فوراً بعد إنشاء النفق
- دعم ترويسات مخصصة عبر الإعدادات (مع دعم قوالب Go لـ
Source/Target)
data := struct {
Source net.Destination
Target net.Destination
}{...}
tmpl.Execute(&buf, data)المصدر: proxy/http/client.go:180-203
ملاحظات التنفيذ
- الكشف التلقائي SOCKS/HTTP: يقرأ الوارد SOCKS أول بايت. إذا لم يكن
0x04أو0x05، يحوّل الاتصال إلى خادم HTTP مُضمّن. يسمح هذا لمنفذ واحد بخدمة كلاً من وكيل SOCKS5 وHTTP.
المصدر: proxy/socks/server.go:92-95
سلوك CanSpliceCopy: يُعيّن كلاً من SOCKS وHTTP القيمة
CanSpliceCopy = 2مبدئياً. بعد مصافحة البروتوكول (لـ TCP CONNECT)، يُرقّى إلى1، مما يُفعّل النسخ بدون نسخ على تدفق TCP الخام. إذا اكتُشف TLS على النقل، يبدأ بـ3.رفض مصادقة SOCKS4: لا يدعم SOCKS4 مصادقة كلمة المرور. إذا طلب الخادم مصادقة، تُرفض اتصالات SOCKS4 فوراً.
المصدر: proxy/socks/protocol.go:50-53
- UDP غير مدعوم في وكيل HTTP: يرفض المعالج الصادر HTTP أهداف UDP صراحةً.
المصدر: proxy/http/client.go:80-82
- DispatchLink مقابل Dispatch: لـ TCP CONNECT وSOCKS TCP، يستخدم الوارد
dispatcher.DispatchLink()الذي يحظر حتى اكتمال الاتصال، بدلاً منDispatch()الذي يُرجعLinkللنسخ اليدوي. يُبسّط هذا إدارة دورة الحياة.
المصدر: proxy/socks/server.go:163-169