diff --git a/api/apimodel.go b/api/apimodel.go index 3db79b3..38f1005 100644 --- a/api/apimodel.go +++ b/api/apimodel.go @@ -3,6 +3,8 @@ package api import ( "encoding/json" "regexp" + + "github.com/xtls/xray-core/infra/conf" ) // Config API config @@ -45,6 +47,7 @@ type NodeInfo struct { ServerKey string ServiceName string Header json.RawMessage + NameServerConfig []*conf.NameServerConfig } type UserInfo struct { diff --git a/api/newV2board/v2board.go b/api/newV2board/v2board.go index ab9610f..9e5d9e8 100644 --- a/api/newV2board/v2board.go +++ b/api/newV2board/v2board.go @@ -15,6 +15,8 @@ import ( "github.com/bitly/go-simplejson" "github.com/go-resty/resty/v2" + "github.com/xtls/xray-core/common/net" + "github.com/xtls/xray-core/infra/conf" "github.com/XrayR-project/XrayR/api" ) @@ -261,11 +263,12 @@ func (c *APIClient) GetNodeRule() (*[]api.DetectRule, error) { for i, rule := range nodeInfoResponse.Get("routes").MustArray() { r := rule.(map[string]any) if r["action"] == "block" { - ruleListItem := api.DetectRule{ - ID: i, - Pattern: regexp.MustCompile(strings.TrimPrefix(r["match"].(string), "regexp:")), + for ii := range r["match"].([]any) { + ruleList = append(ruleList, api.DetectRule{ + ID: i, + Pattern: regexp.MustCompile(r["match"].([]any)[ii].(string)), + }) } - ruleList = append(ruleList, ruleListItem) } } @@ -304,12 +307,26 @@ func (c *APIClient) parseTrojanNodeResponse(nodeInfoResponse *simplejson.Json) ( TLSType: TLSType, Host: nodeInfoResponse.Get("host").MustString(), ServiceName: nodeInfoResponse.Get("server_name").MustString(), + NameServerConfig: parseDNSConfig(nodeInfoResponse), } return nodeInfo, nil } // parseSSNodeResponse parse the response for the given nodeInfo format func (c *APIClient) parseSSNodeResponse(nodeInfoResponse *simplejson.Json) (*api.NodeInfo, error) { + var header json.RawMessage + + if nodeInfoResponse.Get("obfs").MustString() == "http" { + path := "/" + if p := nodeInfoResponse.Get("obfs_settings").Get("path").MustString(); p != "" { + path = p + } + header, _ = json.Marshal(map[string]any{ + "type": "http", + "request": map[string]any{ + "path": path, + }}) + } // Create GeneralNodeInfo return &api.NodeInfo{ NodeType: c.NodeType, @@ -318,6 +335,8 @@ func (c *APIClient) parseSSNodeResponse(nodeInfoResponse *simplejson.Json) (*api TransportProtocol: "tcp", CypherMethod: nodeInfoResponse.Get("cipher").MustString(), ServerKey: nodeInfoResponse.Get("server_key").MustString(), // shadowsocks2022 share key + NameServerConfig: parseDNSConfig(nodeInfoResponse), + Header: header, }, nil } @@ -373,5 +392,20 @@ func (c *APIClient) parseV2rayNodeResponse(nodeInfoResponse *simplejson.Json) (* EnableVless: c.EnableVless, ServiceName: serviceName, Header: header, + NameServerConfig: parseDNSConfig(nodeInfoResponse), }, nil } + +func parseDNSConfig(nodeInfoResponse *simplejson.Json) (nameServerList []*conf.NameServerConfig) { + for _, rule := range nodeInfoResponse.Get("routes").MustArray() { + r := rule.(map[string]any) + if r["action"] == "dns" { + nameServerList = append(nameServerList, &conf.NameServerConfig{ + Address: &conf.Address{Address: net.ParseAddress(r["action_value"].(string))}, + Domains: strings.Split(r["match"].(string), ","), + }) + } + } + + return +} diff --git a/api/v2raysocks/v2raysocks.go b/api/v2raysocks/v2raysocks.go index 83b1d2f..e0427a8 100644 --- a/api/v2raysocks/v2raysocks.go +++ b/api/v2raysocks/v2raysocks.go @@ -140,7 +140,7 @@ func (c *APIClient) parseResponse(res *resty.Response, path string, err error) ( return rtn, nil } -// GetNodeInfo will pull NodeInfo Config from sspanel +// GetNodeInfo will pull NodeInfo Config from panel func (c *APIClient) GetNodeInfo() (nodeInfo *api.NodeInfo, err error) { var nodeType string switch c.NodeType { @@ -184,7 +184,7 @@ func (c *APIClient) GetNodeInfo() (nodeInfo *api.NodeInfo, err error) { return nodeInfo, nil } -// GetUserList will pull user form sspanel +// GetUserList will pull user form panel func (c *APIClient) GetUserList() (UserList *[]api.UserInfo, err error) { var nodeType string switch c.NodeType { @@ -215,16 +215,16 @@ func (c *APIClient) GetUserList() (UserList *[]api.UserInfo, err error) { user.Email = response.Get("data").GetIndex(i).Get("shadowsocks_user").Get("secret").MustString() user.Passwd = response.Get("data").GetIndex(i).Get("shadowsocks_user").Get("secret").MustString() user.Method = response.Get("data").GetIndex(i).Get("shadowsocks_user").Get("cipher").MustString() - user.SpeedLimit = uint64(response.Get("data").GetIndex(i).Get("shadowsocks_user").Get("speed_limit").MustUint64() * 1000000 / 8) + user.SpeedLimit = response.Get("data").GetIndex(i).Get("shadowsocks_user").Get("speed_limit").MustUint64() * 1000000 / 8 case "Trojan": user.UUID = response.Get("data").GetIndex(i).Get("trojan_user").Get("password").MustString() user.Email = response.Get("data").GetIndex(i).Get("trojan_user").Get("password").MustString() - user.SpeedLimit = uint64(response.Get("data").GetIndex(i).Get("trojan_user").Get("speed_limit").MustUint64() * 1000000 / 8) + user.SpeedLimit = response.Get("data").GetIndex(i).Get("trojan_user").Get("speed_limit").MustUint64() * 1000000 / 8 case "V2ray": user.UUID = response.Get("data").GetIndex(i).Get("v2ray_user").Get("uuid").MustString() user.Email = response.Get("data").GetIndex(i).Get("v2ray_user").Get("email").MustString() user.AlterID = uint16(response.Get("data").GetIndex(i).Get("v2ray_user").Get("alter_id").MustUint64()) - user.SpeedLimit = uint64(response.Get("data").GetIndex(i).Get("v2ray_user").Get("speed_limit").MustUint64() * 1000000 / 8) + user.SpeedLimit = response.Get("data").GetIndex(i).Get("v2ray_user").Get("speed_limit").MustUint64() * 1000000 / 8 } if c.SpeedLimit > 0 { user.SpeedLimit = uint64((c.SpeedLimit * 1000000) / 8) @@ -269,7 +269,7 @@ func (c *APIClient) GetNodeRule() (*[]api.DetectRule, error) { return &ruleList, nil } - // V2board only support the rule for v2ray + // Only support the rule for v2ray // fix: reuse config response c.access.Lock() defer c.access.Unlock() @@ -300,7 +300,7 @@ func (c *APIClient) ReportIllegal(detectResultList *[]api.DetectResult) error { return nil } -// ParseTrojanNodeResponse parse the response for the given nodeinfor format +// ParseTrojanNodeResponse parse the response for the given nodeInfo format func (c *APIClient) ParseTrojanNodeResponse(nodeInfoResponse *simplejson.Json) (*api.NodeInfo, error) { var TLSType = "tls" if c.EnableXTLS { @@ -315,7 +315,7 @@ func (c *APIClient) ParseTrojanNodeResponse(nodeInfoResponse *simplejson.Json) ( host := inboundInfo.Get("streamSettings").Get("tlsSettings").Get("serverName").MustString() // Create GeneralNodeInfo - nodeinfo := &api.NodeInfo{ + nodeInfo := &api.NodeInfo{ NodeType: c.NodeType, NodeID: c.NodeID, Port: port, @@ -324,10 +324,10 @@ func (c *APIClient) ParseTrojanNodeResponse(nodeInfoResponse *simplejson.Json) ( TLSType: TLSType, Host: host, } - return nodeinfo, nil + return nodeInfo, nil } -// ParseSSNodeResponse parse the response for the given nodeinfo format +// ParseSSNodeResponse parse the response for the given nodeInfo format func (c *APIClient) ParseSSNodeResponse(nodeInfoResponse *simplejson.Json) (*api.NodeInfo, error) { var method, serverPsk string tmpInboundInfo := nodeInfoResponse.Get("inbounds").MustArray() @@ -350,7 +350,7 @@ func (c *APIClient) ParseSSNodeResponse(nodeInfoResponse *simplejson.Json) (*api } // Create GeneralNodeInfo - nodeinfo := &api.NodeInfo{ + nodeInfo := &api.NodeInfo{ NodeType: c.NodeType, NodeID: c.NodeID, Port: port, @@ -359,12 +359,12 @@ func (c *APIClient) ParseSSNodeResponse(nodeInfoResponse *simplejson.Json) (*api ServerKey: serverPsk, } - return nodeinfo, nil + return nodeInfo, nil } -// ParseV2rayNodeResponse parse the response for the given nodeinfor format +// ParseV2rayNodeResponse parse the response for the given nodeInfo format func (c *APIClient) ParseV2rayNodeResponse(nodeInfoResponse *simplejson.Json) (*api.NodeInfo, error) { - var TLSType string = "tls" + var TLSType = "tls" var path, host, serviceName string var header json.RawMessage var enableTLS bool diff --git a/panel/panel.go b/panel/panel.go index 3d13671..c40f861 100644 --- a/panel/panel.go +++ b/panel/panel.go @@ -66,10 +66,12 @@ func (p *Panel) loadCore(panelConfig *Config) *core.Instance { } } } - dnsConfig, err := coreDnsConfig.Build() - if err != nil { - log.Panicf("Failed to understand DNS config, Please check: https://xtls.github.io/config/dns.html for help: %s", err) + + // init controller's DNS config + for _, config := range p.panelConfig.NodesConfig { + config.ControllerConfig.DNSConfig = coreDnsConfig } + // Routing config coreRouterConfig := &conf.RouterConfig{} if panelConfig.RouteConfigPath != "" { @@ -137,7 +139,6 @@ func (p *Panel) loadCore(panelConfig *Config) *core.Instance { serial.ToTypedMessage(&proxyman.InboundConfig{}), serial.ToTypedMessage(&proxyman.OutboundConfig{}), serial.ToTypedMessage(policyConfig), - serial.ToTypedMessage(dnsConfig), serial.ToTypedMessage(routeConfig), }, Inbound: inBoundConfig, diff --git a/service/controller/config.go b/service/controller/config.go index d1b7080..430f74b 100644 --- a/service/controller/config.go +++ b/service/controller/config.go @@ -1,6 +1,8 @@ package controller import ( + "github.com/xtls/xray-core/infra/conf" + "github.com/XrayR-project/XrayR/common/limiter" "github.com/XrayR-project/XrayR/common/mylego" ) @@ -21,6 +23,7 @@ type Config struct { AutoSpeedLimitConfig *AutoSpeedLimitConfig `mapstructure:"AutoSpeedLimitConfig"` GlobalDeviceLimitConfig *limiter.GlobalDeviceLimitConfig `mapstructure:"GlobalDeviceLimitConfig"` FallBackConfigs []*FallBackConfig `mapstructure:"FallBackConfigs"` + DNSConfig *conf.DNSConfig } type AutoSpeedLimitConfig struct { diff --git a/service/controller/controller.go b/service/controller/controller.go index b791ba4..047f8ae 100644 --- a/service/controller/controller.go +++ b/service/controller/controller.go @@ -7,12 +7,15 @@ import ( "time" "github.com/xtls/xray-core/common/protocol" + "github.com/xtls/xray-core/common/serial" "github.com/xtls/xray-core/common/task" "github.com/xtls/xray-core/core" + "github.com/xtls/xray-core/features" "github.com/xtls/xray-core/features/inbound" "github.com/xtls/xray-core/features/outbound" "github.com/xtls/xray-core/features/routing" "github.com/xtls/xray-core/features/stats" + "github.com/xtls/xray-core/infra/conf" "github.com/XrayR-project/XrayR/api" "github.com/XrayR-project/XrayR/app/mydispatcher" @@ -77,6 +80,13 @@ func (c *Controller) Start() error { } c.nodeInfo = newNodeInfo c.Tag = c.buildNodeTag() + + // append remote DNS config and init dns service + err = c.addNewDNS(newNodeInfo) + if err != nil { + return err + } + // Add new tag err = c.addNewTag(newNodeInfo) if err != nil { @@ -252,6 +262,13 @@ func (c *Controller) nodeInfoMonitor() (err error) { log.Print(err) return nil } + + // Add DNS + log.Printf("%s Reload DNS service", c.logPrefix()) + if err := c.addNewDNS(newNodeInfo); err != nil { + log.Print(err) + return nil + } } else { var deleted, added []api.UserInfo if usersChanged { @@ -613,3 +630,40 @@ func (c *Controller) certMonitor() error { } return nil } + +// append remote dns +func (c *Controller) addNewDNS(newNodeInfo *api.NodeInfo) error { + // reserve local DNS + servers := c.config.DNSConfig.Servers + servers = append(servers, newNodeInfo.NameServerConfig...) + dns := conf.DNSConfig{ + Servers: servers, + Hosts: c.config.DNSConfig.Hosts, + ClientIP: c.config.DNSConfig.ClientIP, + Tag: c.config.DNSConfig.Tag, + QueryStrategy: c.config.DNSConfig.QueryStrategy, + DisableCache: c.config.DNSConfig.DisableCache, + DisableFallback: c.config.DNSConfig.DisableFallback, + DisableFallbackIfMatch: c.config.DNSConfig.DisableFallbackIfMatch, + } + + dnsConfig, err := dns.Build() + if err != nil { + log.Panicf("Failed to understand DNS config, Please check: https://xtls.github.io/config/dns.html for help: %s", err) + } + dnsInstance, err := serial.ToTypedMessage(dnsConfig).GetInstance() + if err != nil { + return err + } + obj, err := core.CreateObject(c.server, dnsInstance) + if err != nil { + return err + } + if feature, ok := obj.(features.Feature); ok { + if err := c.server.AddFeature(feature); err != nil { + return err + } + } + + return nil +}