نظرة عامة على نظام الإعدادات
يقوم نظام الإعدادات في Xray بتحويل ملفات JSON (أو YAML/TOML) القابلة للقراءة البشرية إلى رسائل protobuf التي يستهلكها المحرك الأساسي. يتميز خط المعالجة بفصل واضح: مجلد infra/conf/ يتعامل مع التحليل والتحقق، بينما مجلدات core/ و app/ تعمل حصريًا مع أنواع protobuf.
بنية خط المعالجة
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
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
type Buildable interface {
Build() (proto.Message, error)
}كل بنية إعدادات تُنفّذ هذه الواجهة. تقوم دالة Build() بالتحقق من صحة الإعدادات وإنتاج رسالة protobuf المكافئة.
دعم الصيغ المتعددة
الملف: infra/conf/serial/loader.go
يدعم Xray إعدادات JSON و YAML و TOML. يتم تحويل جميع الصيغ إلى conf.Config عبر دوال فك التشفير:
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
يمكن دمج ملفات إعدادات متعددة:
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 النهائي:
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)
}
}الترتيب مهم:
- إعداد السجل يكون دائمًا أولاً (حتى تتمكن الوحدات الأخرى من التسجيل أثناء التهيئة)
- FakeDNS يُضاف قبل DNS (يجب تهيئته أولاً لـ
RequireFeatures) - المُوزّع وإعداد الوارد وإعداد الصادر موجودون دائمًا كتطبيقات أساسية
بناء إعدادات الوارد
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():
- تبني
ReceiverConfig(عنوان الاستماع، المنفذ، إعدادات التدفق، الاستنشاق) - تحمّل الإعدادات الخاصة بالبروتوكول عبر
inboundConfigLoader.LoadWithID() - تستدعي
Build()للإعداد المُحمَّل للحصول على إعدادات protobuf للوكيل - تغلّف كليهما في
core.InboundHandlerConfigمعserial.ToTypedMessage()
بناء إعدادات الصادر
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():
- تبني
SenderConfig(عنوان الإرسال، إعدادات التدفق، mux، سلسلة الوكيل) - تعالج
targetStrategy(استراتيجية النطاق لتحليل DNS الصادر) - تحمّل الإعدادات الخاصة بالبروتوكول عبر
outboundConfigLoader.LoadWithID() - تغلّف في
core.OutboundHandlerConfig
محمّلات إعدادات البروتوكولات
الملف: infra/conf/xray.go
سجلّان JSONConfigLoader يربطان أسماء البروتوكولات ببنّاءات الإعدادات:
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.go | Config الرئيسي، Build()، محمّلات البروتوكولات |
infra/conf/buildable.go | واجهة Buildable |
infra/conf/serial/builder.go | BuildConfig()، mergeConfigs() |
infra/conf/serial/loader.go | مفكّكات تشفير JSON/YAML/TOML |
infra/conf/dns.go | DNSConfig، NameServerConfig |
infra/conf/router.go | RouterConfig |
infra/conf/api.go | APIConfig |
infra/conf/common.go | أنواع مشتركة: Address، PortList، StringList |
core/config.go | protobuf الخاص بـ 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.