Skip to content

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

mermaid
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

الأوامر:

القيمةالأمرالدعم
0x01TCP CONNECTنعم
0x02TCP BINDلا (يُرجع 0x07)
0x03UDP ASSOCIATEنعم (إذا مُفعّل)
0xF0Tor Resolveيُعامل كـ CONNECT
0xF1Tor 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 المُجزّأ غير مدعوم:

go
if packet.Byte(2) != 0 /* fragments */ {
    return nil, errors.New("discarding fragmented payload.")
}

المصدر: proxy/socks/protocol.go:334-336

دعم SOCKS4/4a

يتعامل الوارد أيضاً مع SOCKS4/4a بناءً على بايت الإصدار:

go
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:

go
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:

  1. يُرسل العميل CONNECT بالأمر 0x03
  2. يستجيب الخادم بعنوان/منفذ ترحيل UDP
  3. يُرسل العميل حزم بيانات UDP إلى ذلك العنوان
  4. يستقبل الخادم على شبكة UDP، يفك ترويسة SOCKS5 UDP، ويوزّع
  5. يبقى اتصال TCP مفتوحاً كإشارة بقاء (keepalive)
go
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:

go
if s.udpFilter != nil && !s.udpFilter.Check(conn.RemoteAddr()) {
    return nil
}

المصدر: proxy/socks/server.go:189-192

الصادر (العميل)

الملف: proxy/socks/client.go

يُجري العميل مصافحة SOCKS5 الكاملة مع الخادم العلوي:

go
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 (النفق)

mermaid
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، يقوم الخادم بما يلي:

  1. تحليل طلب HTTP الكامل باستخدام http.ReadRequest() في Go
  2. استخراج الوجهة من ترويسة Host أو الرابط
  3. توزيع الطلب إلى المعالج الصادر
  4. دعم keep-alive للطلبات المتتابعة على نفس الاتصال
go
keepAlive := strings.TrimSpace(strings.ToLower(
    request.Header.Get("Proxy-Connection"))) == "keep-alive"

المصدر: proxy/http/server.go:163

المصادقة

يتم التحقق من المصادقة الأساسية عبر ترويسة Proxy-Authorization:

go
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 علوي:

go
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 مؤقتاً لكل وجهة للتعدد:

go
var cachedH2Conns map[net.Destination]h2Conn

المصدر: proxy/http/client.go:47-48

معالجة استجابات 1xx

يتعامل الخادم بشكل صحيح مع استجابات HTTP 1xx الوسيطة (مثل 100 Continue) بتمريرها إلى العميل قبل قراءة الاستجابة الفعلية:

go
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

يقوم العميل بما يلي:

  1. قراءة الحمولة الأولى من قارئ الرابط
  2. إنشاء نفق HTTP CONNECT مع الوكيل العلوي
  3. إرسال الحمولة الأولى فوراً بعد إنشاء النفق
  4. دعم ترويسات مخصصة عبر الإعدادات (مع دعم قوالب Go لـ Source/Target)
go
data := struct {
    Source net.Destination
    Target net.Destination
}{...}
tmpl.Execute(&buf, data)

المصدر: proxy/http/client.go:180-203

ملاحظات التنفيذ

  1. الكشف التلقائي SOCKS/HTTP: يقرأ الوارد SOCKS أول بايت. إذا لم يكن 0x04 أو 0x05، يحوّل الاتصال إلى خادم HTTP مُضمّن. يسمح هذا لمنفذ واحد بخدمة كلاً من وكيل SOCKS5 وHTTP.

المصدر: proxy/socks/server.go:92-95

  1. سلوك CanSpliceCopy: يُعيّن كلاً من SOCKS وHTTP القيمة CanSpliceCopy = 2 مبدئياً. بعد مصافحة البروتوكول (لـ TCP CONNECT)، يُرقّى إلى 1، مما يُفعّل النسخ بدون نسخ على تدفق TCP الخام. إذا اكتُشف TLS على النقل، يبدأ بـ 3.

  2. رفض مصادقة SOCKS4: لا يدعم SOCKS4 مصادقة كلمة المرور. إذا طلب الخادم مصادقة، تُرفض اتصالات SOCKS4 فوراً.

المصدر: proxy/socks/protocol.go:50-53

  1. UDP غير مدعوم في وكيل HTTP: يرفض المعالج الصادر HTTP أهداف UDP صراحةً.

المصدر: proxy/http/client.go:80-82

  1. DispatchLink مقابل Dispatch: لـ TCP CONNECT وSOCKS TCP، يستخدم الوارد dispatcher.DispatchLink() الذي يحظر حتى اكتمال الاتصال، بدلاً من Dispatch() الذي يُرجع Link للنسخ اليدوي. يُبسّط هذا إدارة دورة الحياة.

المصدر: proxy/socks/server.go:163-169

تحليل تقني لأغراض إعادة التنفيذ.