Skip to content

بروتوكول VMess ​

VMess هو بروتوكول الوكيل الأصلي لـ V2Ray ويتميز بمصادقة قائمة على UUID مع تشفير AEAD. يفوض sing-box تنسيق بيانات VMess بالكامل إلى مكتبة sing-vmess.

المصدر: protocol/vmess/inbound.go، protocol/vmess/outbound.go، sing-vmess

تكامل sing-vmess ​

لا ينفذ sing-box تنسيق بيانات VMess بنفسه. بدلاً من ذلك، يستخدم مكتبة github.com/sagernet/sing-vmess التي توفر:

  • vmess.Service[int] -- معالج بروتوكول VMess من جانب الخادم، عام على نوع مفتاح المستخدم
  • vmess.Client -- معالج بروتوكول VMess من جانب العميل
  • vmess.ServiceOption / vmess.ClientOption -- خيارات وظيفية للتكوين
  • packetaddr -- ترميز عنوان الحزمة لـ UDP-over-TCP

هذا اختلاف جوهري عن Xray-core الذي ينفذ VMess مباشرة في قاعدة الكود الخاصة به. يوفر نهج sing-box فصلاً أنظف للمسؤوليات.

بنية الوارد (Inbound) ​

go
type Inbound struct {
    inbound.Adapter
    ctx       context.Context
    router    adapter.ConnectionRouterEx
    logger    logger.ContextLogger
    listener  *listener.Listener
    service   *vmess.Service[int]       // خدمة sing-vmess، مفهرسة بفهرس المستخدم
    users     []option.VMessUser
    tlsConfig tls.ServerConfig
    transport adapter.V2RayServerTransport
}

تدفق البناء ​

go
func NewInbound(ctx, router, logger, tag, options) (adapter.Inbound, error) {
    // 1. تغليف الموجه بدعم UoT (UDP-over-TCP)
    inbound.router = uot.NewRouter(router, logger)

    // 2. تغليف الموجه بدعم mux (تعدد الإرسال)
    inbound.router = mux.NewRouterWithOptions(inbound.router, logger, options.Multiplex)

    // 3. تكوين خيارات خدمة VMess
    //    - دالة وقت NTP (VMess حساس للوقت)
    //    - تعطيل حماية الرأس عند استخدام نقل V2Ray
    serviceOptions = append(serviceOptions, vmess.ServiceWithTimeFunc(timeFunc))
    if options.Transport != nil {
        serviceOptions = append(serviceOptions, vmess.ServiceWithDisableHeaderProtection())
    }

    // 4. إنشاء الخدمة وتسجيل المستخدمين (فهرس -> UUID + alterId)
    service := vmess.NewService[int](handler, serviceOptions...)
    service.UpdateUsers(indices, uuids, alterIds)

    // 5. TLS اختياري
    // 6. نقل V2Ray اختياري (WebSocket، gRPC، HTTP، QUIC)
    // 7. مستمع TCP
}

تصميم رئيسي: تعطيل حماية الرأس مع النقل ​

عند تكوين نقل V2Ray (WebSocket، gRPC، إلخ)، يتم تمرير vmess.ServiceWithDisableHeaderProtection(). هذا لأن طبقة النقل توفر بالفعل تأطيرها الخاص، مما يجعل حماية رأس VMess زائدة وقد تكون إشكالية.

معالجة الاتصال ​

go
func (h *Inbound) NewConnectionEx(ctx, conn, metadata, onClose) {
    // 1. مصافحة TLS (فقط إذا تم تكوين TLS ولا يوجد نقل)
    //    عند استخدام النقل، يتم معالجة TLS بواسطة طبقة النقل
    if h.tlsConfig != nil && h.transport == nil {
        conn = tls.ServerHandshake(ctx, conn, h.tlsConfig)
    }

    // 2. التفويض إلى خدمة sing-vmess
    //    فك تشفير VMess والمصادقة وتحليل الأوامر تحدث هنا
    h.service.NewConnection(ctx, conn, metadata.Source, onClose)
}

بعد أن تفك الخدمة تشفير طلب VMess، تستدعي معالجات الوارد:

go
func (h *Inbound) newConnectionEx(ctx, conn, metadata, onClose) {
    // استخراج فهرس المستخدم من السياق (معين بواسطة sing-vmess)
    userIndex, _ := auth.UserFromContext[int](ctx)
    user := h.users[userIndex].Name
    metadata.User = user
    h.router.RouteConnectionEx(ctx, conn, metadata, onClose)
}

معالجة عنوان الحزمة (packetaddr) ​

لاتصالات حزم UDP، يستخدم VMess عنوان FQDN سحري packetaddr.SeqPacketMagicAddress للإشارة إلى أن الاتصال يحمل حزم UDP متعددة الإرسال:

go
func (h *Inbound) newPacketConnectionEx(ctx, conn, metadata, onClose) {
    if metadata.Destination.Fqdn == packetaddr.SeqPacketMagicAddress {
        metadata.Destination = M.Socksaddr{}
        conn = packetaddr.NewConn(bufio.NewNetPacketConn(conn), metadata.Destination)
    }
    h.router.RoutePacketConnectionEx(ctx, conn, metadata, onClose)
}

بنية الصادر (Outbound) ​

go
type Outbound struct {
    outbound.Adapter
    logger          logger.ContextLogger
    dialer          N.Dialer
    client          *vmess.Client        // عميل sing-vmess
    serverAddr      M.Socksaddr
    multiplexDialer *mux.Client
    tlsConfig       tls.Config
    tlsDialer       tls.Dialer
    transport       adapter.V2RayClientTransport
    packetAddr      bool                 // ترميز packetaddr
    xudp            bool                 // ترميز XUDP
}

أوضاع ترميز الحزم ​

يدعم صادر VMess ثلاثة أوضاع لترميز الحزم لـ UDP:

الوضعالحقلالوصف
(لا شيء)افتراضيUDP القياسي لـ VMess
packetaddrpacketAddr=trueيستخدم FQDN السحري لـ packetaddr لتعدد إرسال UDP
xudpxudp=trueبروتوكول XUDP لتعدد إرسال UDP
go
switch options.PacketEncoding {
case "packetaddr":
    outbound.packetAddr = true
case "xudp":
    outbound.xudp = true
}

الاختيار التلقائي للأمان ​

go
security := options.Security
if security == "" {
    security = "auto"
}
if security == "auto" && outbound.tlsConfig != nil {
    security = "zero"  // استخدام تشفير صفري عند وجود TLS
}

عند تكوين TLS مسبقاً، يستخدم VMess تلقائياً أمان "zero" لتجنب التشفير المزدوج -- وهو تحسين للأداء.

خيارات العميل ​

go
var clientOptions []vmess.ClientOption
if options.GlobalPadding {
    clientOptions = append(clientOptions, vmess.ClientWithGlobalPadding())
}
if options.AuthenticatedLength {
    clientOptions = append(clientOptions, vmess.ClientWithAuthenticatedLength())
}
client, _ := vmess.NewClient(options.UUID, security, options.AlterId, clientOptions...)
  • GlobalPadding: يضيف حشواً عشوائياً لجميع الحزم لمقاومة تحليل حركة المرور
  • AuthenticatedLength: يتضمن طول الحمولة المصادق عليها في الرأس (وضع AEAD)

إنشاء الاتصال ​

يعالج النوع vmessDialer الاتصال الفعلي:

go
func (h *vmessDialer) DialContext(ctx, network, destination) (net.Conn, error) {
    // 1. إنشاء الاتصال الأساسي
    //    الأولوية: النقل > TLS > TCP الخام
    if h.transport != nil {
        conn = h.transport.DialContext(ctx)
    } else if h.tlsDialer != nil {
        conn = h.tlsDialer.DialTLSContext(ctx, h.serverAddr)
    } else {
        conn = h.dialer.DialContext(ctx, "tcp", h.serverAddr)
    }

    // 2. التغليف ببروتوكول VMess (بيانات مبكرة / 0-RTT)
    switch network {
    case "tcp":
        return h.client.DialEarlyConn(conn, destination)
    case "udp":
        return h.client.DialEarlyPacketConn(conn, destination)
    }
}

بالنسبة لـ ListenPacket، يحدد وضع الترميز الغلاف:

go
func (h *vmessDialer) ListenPacket(ctx, destination) (net.PacketConn, error) {
    conn := /* إنشاء الاتصال */
    if h.packetAddr {
        return packetaddr.NewConn(
            h.client.DialEarlyPacketConn(conn, M.Socksaddr{Fqdn: packetaddr.SeqPacketMagicAddress}),
            destination,
        )
    } else if h.xudp {
        return h.client.DialEarlyXUDPPacketConn(conn, destination)
    } else {
        return h.client.DialEarlyPacketConn(conn, destination)
    }
}

دعم Mux ​

يتم دعم تعدد الإرسال عبر حزمة common/mux. من جانب الوارد، يتم تغليف الموجه بـ mux.NewRouterWithOptions(). من جانب الصادر، يغلف mux.Client متصل VMess:

go
outbound.multiplexDialer, _ = mux.NewClientWithOptions((*vmessDialer)(outbound), logger, options.Multiplex)

عندما يكون mux نشطاً، يفوض DialContext وListenPacket إلى عميل mux بدلاً من إنشاء اتصالات VMess فردية.

الاختلافات عن Xray-core ​

الجانبsing-boxXray-core
التنفيذيفوض إلى مكتبة sing-vmessتنفيذ مدمج
AlterIdمدعوم لكن AEAD مفضلدعم كامل للقديم
XUDPمدعوم عبر sing-vmessتنفيذ أصلي
حماية الرأسمعطلة عند وجود نقلنشطة دائماً
أمان تلقائي"zero" عند وجود TLS"auto" بناءً على AlterId
مزامنة الوقتتكامل سياق NTPوقت النظام فقط

مثال على التكوين ​

json
{
  "type": "vmess",
  "tag": "vmess-in",
  "listen": "::",
  "listen_port": 10086,
  "users": [
    {
      "name": "user1",
      "uuid": "b831381d-6324-4d53-ad4f-8cda48b30811",
      "alterId": 0
    }
  ],
  "tls": {
    "enabled": true,
    "server_name": "example.com",
    "certificate_path": "/path/to/cert.pem",
    "key_path": "/path/to/key.pem"
  },
  "transport": {
    "type": "ws",
    "path": "/vmess"
  },
  "multiplex": {
    "enabled": true
  }
}
json
{
  "type": "vmess",
  "tag": "vmess-out",
  "server": "example.com",
  "server_port": 10086,
  "uuid": "b831381d-6324-4d53-ad4f-8cda48b30811",
  "security": "auto",
  "alter_id": 0,
  "global_padding": true,
  "authenticated_length": true,
  "packet_encoding": "xudp",
  "tls": {
    "enabled": true,
    "server_name": "example.com"
  },
  "transport": {
    "type": "ws",
    "path": "/vmess"
  }
}