Skip to content

نظرة عامة على نظام الإعدادات

يقوم نظام الإعدادات في Xray بتحويل ملفات JSON (أو YAML/TOML) القابلة للقراءة البشرية إلى رسائل protobuf التي يستهلكها المحرك الأساسي. يتميز خط المعالجة بفصل واضح: مجلد infra/conf/ يتعامل مع التحليل والتحقق، بينما مجلدات core/ و app/ تعمل حصريًا مع أنواع protobuf.

بنية خط المعالجة

mermaid
flowchart LR
    A[JSON/YAML/TOML File] --> B[Reader Decoder]
    B --> C["conf.Config (Go struct)"]
    C --> D["conf.Config.Build()"]
    D --> E["core.Config (protobuf)"]
    E --> F["core.New(config)"]
    F --> G[Xray Instance]

    subgraph "infra/conf/"
        B
        C
        D
    end

    subgraph "core/"
        E
        F
        G
    end

بنية الإعدادات الرئيسية

الملف: infra/conf/xray.go

go
type Config struct {
    Transport       map[string]json.RawMessage `json:"transport"`  // deprecated
    LogConfig       *LogConfig                 `json:"log"`
    RouterConfig    *RouterConfig              `json:"routing"`
    DNSConfig       *DNSConfig                 `json:"dns"`
    InboundConfigs  []InboundDetourConfig      `json:"inbounds"`
    OutboundConfigs []OutboundDetourConfig     `json:"outbounds"`
    Policy          *PolicyConfig              `json:"policy"`
    API             *APIConfig                 `json:"api"`
    Metrics         *MetricsConfig             `json:"metrics"`
    Stats           *StatsConfig               `json:"stats"`
    Reverse         *ReverseConfig             `json:"reverse"`
    FakeDNS         *FakeDNSConfig             `json:"fakeDns"`
    Observatory     *ObservatoryConfig         `json:"observatory"`
    BurstObservatory *BurstObservatoryConfig   `json:"burstObservatory"`
    Version         *VersionConfig             `json:"version"`
}

يتوافق كل حقل مع مفتاح JSON رئيسي. تتضمن البنية أنواع إعدادات فرعية تعرف كيفية بناء رسائل protobuf المقابلة لها.

واجهة Buildable

الملف: infra/conf/buildable.go

go
type Buildable interface {
    Build() (proto.Message, error)
}

كل بنية إعدادات تُنفّذ هذه الواجهة. تقوم دالة Build() بالتحقق من صحة الإعدادات وإنتاج رسالة protobuf المكافئة.

دعم الصيغ المتعددة

الملف: infra/conf/serial/loader.go

يدعم Xray إعدادات JSON و YAML و TOML. يتم تحويل جميع الصيغ إلى conf.Config عبر دوال فك التشفير:

go
var ReaderDecoderByFormat = map[string]readerDecoder{
    "json": DecodeJSONConfig,
    "yaml": DecodeYAMLConfig,
    "toml": DecodeTOMLConfig,
}

JSON: يتم تحليله مباشرة باستخدام قارئ يزيل التعليقات (json_reader.Reader)، مع تتبع موقع أخطاء الصياغة.

YAML: يتم تحويله إلى JSON عبر yaml.YAMLToJSON()، ثم يُحلل كـ JSON.

TOML: يتم فك تنظيمه إلى map[string]interface{}، ثم تنظيمه إلى JSON، ثم يُحلل كـ JSON.

دمج الإعدادات

الملف: infra/conf/serial/builder.go

يمكن دمج ملفات إعدادات متعددة:

go
func mergeConfigs(files []*core.ConfigSource) (*conf.Config, error) {
    cf := &conf.Config{}
    for i, file := range files {
        c, _ := ReaderDecoderByFormat[file.Format](r)
        if i == 0 {
            *cf = *c
            continue
        }
        cf.Override(c, file.Name)
    }
    return cf, nil
}

تتعامل دالة Override() في Config مع الدمج:

  • الحقول البسيطة (السجل، التوجيه، DNS، السياسة، إلخ) يتم استبدالها بالكامل
  • الواردات/الصادرات يتم دمجها حسب الوسم: الوسوم المتطابقة تُحدَّث، والوسوم الجديدة تُضاف
  • ترتيب الصادرات يعتمد على اسم الملف: الملفات التي تحتوي على "tail" تُلحق، والأخرى تُضاف في البداية

خط معالجة البناء

الملف: infra/conf/xray.go -- Config.Build()

تقوم دالة Build() ببناء core.Config النهائي:

go
func (c *Config) Build() (*core.Config, error) {
    PostProcessConfigureFile(c)  // lint and validation

    config := &core.Config{
        App: []*serial.TypedMessage{
            serial.ToTypedMessage(&dispatcher.Config{}),     // always present
            serial.ToTypedMessage(&proxyman.InboundConfig{}), // always present
            serial.ToTypedMessage(&proxyman.OutboundConfig{}),// always present
        },
    }

    // Build and append each section
    if c.API != nil       { config.App = append(config.App, serial.ToTypedMessage(apiConf)) }
    if c.Stats != nil     { config.App = append(config.App, serial.ToTypedMessage(statsConf)) }
    // Log is prepended (first to initialize)
    config.App = append([]*serial.TypedMessage{logConfMsg}, config.App...)
    if c.RouterConfig != nil { config.App = append(config.App, serial.ToTypedMessage(routerConfig)) }
    if c.DNSConfig != nil    { config.App = append(config.App, serial.ToTypedMessage(dnsApp)) }
    if c.FakeDNS != nil      { config.App = append([]*serial.TypedMessage{...}, config.App...) } // prepended!
    // ... observatory, reverse, policy, version

    // Build inbounds and outbounds
    for _, rawInboundConfig := range inbounds {
        ic, _ := rawInboundConfig.Build()
        config.Inbound = append(config.Inbound, ic)
    }
    for _, rawOutboundConfig := range outbounds {
        oc, _ := rawOutboundConfig.Build()
        config.Outbound = append(config.Outbound, oc)
    }
}

الترتيب مهم:

  1. إعداد السجل يكون دائمًا أولاً (حتى تتمكن الوحدات الأخرى من التسجيل أثناء التهيئة)
  2. FakeDNS يُضاف قبل DNS (يجب تهيئته أولاً لـ RequireFeatures)
  3. المُوزّع وإعداد الوارد وإعداد الصادر موجودون دائمًا كتطبيقات أساسية

بناء إعدادات الوارد

go
type InboundDetourConfig struct {
    Protocol       string           `json:"protocol"`
    PortList       *PortList        `json:"port"`
    ListenOn       *Address         `json:"listen"`
    Settings       *json.RawMessage `json:"settings"`
    Tag            string           `json:"tag"`
    StreamSetting  *StreamConfig    `json:"streamSettings"`
    SniffingConfig *SniffingConfig  `json:"sniffing"`
}

دالة Build():

  1. تبني ReceiverConfig (عنوان الاستماع، المنفذ، إعدادات التدفق، الاستنشاق)
  2. تحمّل الإعدادات الخاصة بالبروتوكول عبر inboundConfigLoader.LoadWithID()
  3. تستدعي Build() للإعداد المُحمَّل للحصول على إعدادات protobuf للوكيل
  4. تغلّف كليهما في core.InboundHandlerConfig مع serial.ToTypedMessage()

بناء إعدادات الصادر

go
type OutboundDetourConfig struct {
    Protocol       string           `json:"protocol"`
    SendThrough    *string          `json:"sendThrough"`
    Tag            string           `json:"tag"`
    Settings       *json.RawMessage `json:"settings"`
    StreamSetting  *StreamConfig    `json:"streamSettings"`
    ProxySettings  *ProxyConfig     `json:"proxySettings"`
    MuxSettings    *MuxConfig       `json:"mux"`
    TargetStrategy string           `json:"targetStrategy"`
}

دالة Build():

  1. تبني SenderConfig (عنوان الإرسال، إعدادات التدفق، mux، سلسلة الوكيل)
  2. تعالج targetStrategy (استراتيجية النطاق لتحليل DNS الصادر)
  3. تحمّل الإعدادات الخاصة بالبروتوكول عبر outboundConfigLoader.LoadWithID()
  4. تغلّف في core.OutboundHandlerConfig

محمّلات إعدادات البروتوكولات

الملف: infra/conf/xray.go

سجلّان JSONConfigLoader يربطان أسماء البروتوكولات ببنّاءات الإعدادات:

go
var inboundConfigLoader = NewJSONConfigLoader(ConfigCreatorCache{
    "dokodemo-door": func() interface{} { return new(DokodemoConfig) },
    "http":          func() interface{} { return new(HTTPServerConfig) },
    "shadowsocks":   func() interface{} { return new(ShadowsocksServerConfig) },
    "socks":         func() interface{} { return new(SocksServerConfig) },
    "vless":         func() interface{} { return new(VLessInboundConfig) },
    "vmess":         func() interface{} { return new(VMessInboundConfig) },
    "trojan":        func() interface{} { return new(TrojanServerConfig) },
    // ...
}, "protocol", "settings")

var outboundConfigLoader = NewJSONConfigLoader(ConfigCreatorCache{
    "freedom":       func() interface{} { return new(FreedomConfig) },
    "blackhole":     func() interface{} { return new(BlackholeConfig) },
    "vless":         func() interface{} { return new(VLessOutboundConfig) },
    "vmess":         func() interface{} { return new(VMessOutboundConfig) },
    "dns":           func() interface{} { return new(DNSOutboundConfig) },
    // ...
}, "protocol", "settings")

تقوم LoadWithID() بفك تنظيم JSON الخام لحقل settings إلى بنية الإعداد المناسبة.

الملفات المصدرية الرئيسية

الملفالغرض
infra/conf/xray.goConfig الرئيسي، Build()، محمّلات البروتوكولات
infra/conf/buildable.goواجهة Buildable
infra/conf/serial/builder.goBuildConfig()، mergeConfigs()
infra/conf/serial/loader.goمفكّكات تشفير JSON/YAML/TOML
infra/conf/dns.goDNSConfig، NameServerConfig
infra/conf/router.goRouterConfig
infra/conf/api.goAPIConfig
infra/conf/common.goأنواع مشتركة: Address، PortList، StringList
core/config.goprotobuf الخاص بـ core.Config، تسجيل الصيغ

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

  • قارئ JSON (infra/conf/json/reader.go) يزيل التعليقات بأسلوب C (// و /* */) قبل التحليل. هذا يسمح بملفات "JSONC".

  • يتم استدعاء PostProcessConfigureFile() قبل Build() لمعالجة أي ما قبل المعالجة أو التحقق. هنا تعمل فحوصات التنسيق.

  • حقل Transport (إعدادات النقل العامة) مُهمل ويُرجع خطأ عند استخدامه، موجّهًا المستخدمين إلى streamSettings الخاصة بكل وارد/صادر.

  • توجد أسماء بديلة للبروتوكولات: "block" يُربط بـ BlackholeConfig، و "direct" يُربط بـ FreedomConfig، و "tunnel" يُربط بـ DokodemoConfig، و "mixed" يُربط بـ SocksServerConfig.

  • تتعامل دالة Override() مع دمج الإعدادات لإعدادات الملفات المتعددة. عندما تحمل إعدادات الصادرات نفس الوسم، يفوز الملف الأحدث. الصادرات الجديدة من الملفات غير "tail" تُضاف في البداية للحفاظ على أولوية التوجيه.

  • تُنشئ دالة BuildMPHCache() في Config ملف ذاكرة تخزين مؤقت لتجزئة مثالية بسيطة لمطابقات النطاقات، يمكن تحميله عند بدء التشغيل لتخطي تحليل ملفات بيانات geosite.

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