feat: remote dns for sing

This commit is contained in:
cubemaze 2023-09-21 12:34:03 +08:00
parent b85a78209a
commit cecfffd081
10 changed files with 136 additions and 16 deletions

View File

@ -85,9 +85,10 @@ func serverHandle(_ *cobra.Command, _ []string) {
return return
} }
log.Info("Nodes started") log.Info("Nodes started")
dns := os.Getenv("XRAY_DNS_PATH") xdns := os.Getenv("XRAY_DNS_PATH")
sdns := os.Getenv("SING_DNS_PATH")
if watch { if watch {
err = c.Watch(config, dns, func() { err = c.Watch(config, xdns, sdns, func() {
nodes.Close() nodes.Close()
err = vc.Close() err = vc.Close()
if err != nil { if err != nil {

View File

@ -5,9 +5,10 @@ import (
) )
type SingConfig struct { type SingConfig struct {
LogConfig SingLogConfig `json:"Log"` LogConfig SingLogConfig `json:"Log"`
NtpConfig SingNtpConfig `json:"NTP"` NtpConfig SingNtpConfig `json:"NTP"`
OriginalPath string `json:"OriginalPath"` DnsConfigPath string `json:"DnsConfigPath"`
OriginalPath string `json:"OriginalPath"`
} }
type SingLogConfig struct { type SingLogConfig struct {
@ -35,6 +36,7 @@ type SingOptions struct {
EnableProxyProtocol bool `json:"EnableProxyProtocol"` EnableProxyProtocol bool `json:"EnableProxyProtocol"`
TCPFastOpen bool `json:"EnableTFO"` TCPFastOpen bool `json:"EnableTFO"`
SniffEnabled bool `json:"EnableSniff"` SniffEnabled bool `json:"EnableSniff"`
EnableDNS bool `json:"EnableDNS"`
DomainStrategy option.DomainStrategy `json:"DomainStrategy"` DomainStrategy option.DomainStrategy `json:"DomainStrategy"`
SniffOverrideDestination bool `json:"SniffOverrideDestination"` SniffOverrideDestination bool `json:"SniffOverrideDestination"`
FallBackConfigs *FallBackConfigForSing `json:"FallBackConfigs"` FallBackConfigs *FallBackConfigForSing `json:"FallBackConfigs"`
@ -59,6 +61,7 @@ type FallBack struct {
func NewSingOptions() *SingOptions { func NewSingOptions() *SingOptions {
return &SingOptions{ return &SingOptions{
EnableDNS: false,
EnableProxyProtocol: false, EnableProxyProtocol: false,
TCPFastOpen: false, TCPFastOpen: false,
SniffEnabled: true, SniffEnabled: true,

View File

@ -5,10 +5,12 @@ import (
"github.com/fsnotify/fsnotify" "github.com/fsnotify/fsnotify"
"log" "log"
"path" "path"
"path/filepath"
"strings"
"time" "time"
) )
func (p *Conf) Watch(filePath, dnsPath string, reload func()) error { func (p *Conf) Watch(filePath, xDnsPath string, sDnsPath string, reload func()) error {
watcher, err := fsnotify.NewWatcher() watcher, err := fsnotify.NewWatcher()
if err != nil { if err != nil {
return fmt.Errorf("new watcher error: %s", err) return fmt.Errorf("new watcher error: %s", err)
@ -28,9 +30,10 @@ func (p *Conf) Watch(filePath, dnsPath string, reload func()) error {
pre = time.Now() pre = time.Now()
go func() { go func() {
time.Sleep(5 * time.Second) time.Sleep(5 * time.Second)
if e.Name == dnsPath { switch filepath.Base(strings.TrimSuffix(e.Name, "~")) {
case filepath.Base(xDnsPath), filepath.Base(sDnsPath):
log.Println("DNS file changed, reloading...") log.Println("DNS file changed, reloading...")
} else { default:
log.Println("config dir changed, reloading...") log.Println("config dir changed, reloading...")
} }
*p = *New() *p = *New()
@ -52,8 +55,14 @@ func (p *Conf) Watch(filePath, dnsPath string, reload func()) error {
if err != nil { if err != nil {
return fmt.Errorf("watch file error: %s", err) return fmt.Errorf("watch file error: %s", err)
} }
if dnsPath != "" { if xDnsPath != "" {
err = watcher.Add(path.Dir(dnsPath)) err = watcher.Add(path.Dir(xDnsPath))
if err != nil {
return fmt.Errorf("watch dns file error: %s", err)
}
}
if sDnsPath != "" {
err = watcher.Add(path.Dir(sDnsPath))
if err != nil { if err != nil {
return fmt.Errorf("watch dns file error: %s", err) return fmt.Errorf("watch dns file error: %s", err)
} }

81
core/sing/dns.go Normal file
View File

@ -0,0 +1,81 @@
package sing
import (
"bytes"
"github.com/InazumaV/V2bX/api/panel"
"github.com/goccy/go-json"
log "github.com/sirupsen/logrus"
"os"
"strings"
)
func updateDNSConfig(node *panel.NodeInfo) (err error) {
dnsPath := os.Getenv("SING_DNS_PATH")
if len(node.RawDNS.DNSJson) != 0 {
err = saveDnsConfig(node.RawDNS.DNSJson, dnsPath)
} else if len(node.RawDNS.DNSMap) != 0 {
dnsConfig := DNSConfig{
Servers: []map[string]interface{}{
{
"tag": "default",
"address": "https://8.8.8.8/dns-query",
"detour": "direct",
},
},
}
for id, value := range node.RawDNS.DNSMap {
dnsConfig.Servers = append(dnsConfig.Servers,
map[string]interface{}{
"tag": id,
"address": value["address"],
"address_resolver": "default",
"detour": "direct",
},
)
rule := map[string]interface{}{
"server": id,
"disable_cache": true,
}
for _, ruleType := range []string{"domain_suffix", "domain_keyword", "domain_regex", "geosite"} {
var domains []string
for _, v := range value["domains"].([]string) {
split := strings.SplitN(v, ":", 2)
prefix := strings.ToLower(split[0])
if prefix == ruleType || (prefix == "domain" && ruleType == "domain_suffix") {
if len(split) > 1 {
domains = append(domains, split[1])
}
if len(domains) > 0 {
rule[ruleType] = domains
}
}
}
}
dnsConfig.Rules = append(dnsConfig.Rules, rule)
}
dnsConfigJSON, err := json.MarshalIndent(dnsConfig, "", " ")
if err != nil {
log.WithField("err", err).Error("Error marshaling dnsConfig to JSON")
return err
}
err = saveDnsConfig(dnsConfigJSON, dnsPath)
}
return err
}
func saveDnsConfig(dns []byte, dnsPath string) (err error) {
currentData, err := os.ReadFile(dnsPath)
if err != nil {
log.WithField("err", err).Error("Failed to read SING_DNS_PATH")
return err
}
if !bytes.Equal(currentData, dns) {
if err = os.Truncate(dnsPath, 0); err != nil {
log.WithField("err", err).Error("Failed to clear SING DNS PATH file")
}
if err = os.WriteFile(dnsPath, dns, 0644); err != nil {
log.WithField("err", err).Error("Failed to write DNS to SING DNS PATH file")
}
}
return err
}

View File

@ -30,6 +30,10 @@ func getInboundOptions(tag string, info *panel.NodeInfo, c *conf.Options) (optio
if err != nil { if err != nil {
return option.Inbound{}, fmt.Errorf("the listen ip not vail") return option.Inbound{}, fmt.Errorf("the listen ip not vail")
} }
var domainStrategy option.DomainStrategy
if c.SingOptions.EnableDNS {
domainStrategy = c.SingOptions.DomainStrategy
}
listen := option.ListenOptions{ listen := option.ListenOptions{
Listen: (*option.ListenAddress)(&addr), Listen: (*option.ListenAddress)(&addr),
ListenPort: uint16(info.Common.ServerPort), ListenPort: uint16(info.Common.ServerPort),
@ -38,7 +42,7 @@ func getInboundOptions(tag string, info *panel.NodeInfo, c *conf.Options) (optio
InboundOptions: option.InboundOptions{ InboundOptions: option.InboundOptions{
SniffEnabled: c.SingOptions.SniffEnabled, SniffEnabled: c.SingOptions.SniffEnabled,
SniffOverrideDestination: c.SingOptions.SniffOverrideDestination, SniffOverrideDestination: c.SingOptions.SniffOverrideDestination,
DomainStrategy: c.SingOptions.DomainStrategy, DomainStrategy: domainStrategy,
}, },
} }
var tls option.InboundTLSOptions var tls option.InboundTLSOptions
@ -203,6 +207,10 @@ func getInboundOptions(tag string, info *panel.NodeInfo, c *conf.Options) (optio
} }
func (b *Box) AddNode(tag string, info *panel.NodeInfo, config *conf.Options) error { func (b *Box) AddNode(tag string, info *panel.NodeInfo, config *conf.Options) error {
err := updateDNSConfig(info)
if err != nil {
return fmt.Errorf("build dns error: %s", err)
}
c, err := getInboundOptions(tag, info, config) c, err := getInboundOptions(tag, info, config)
if err != nil { if err != nil {
return err return err

View File

@ -26,6 +26,11 @@ import (
var _ adapter.Service = (*Box)(nil) var _ adapter.Service = (*Box)(nil)
type DNSConfig struct {
Servers []map[string]interface{} `json:"servers"`
Rules []map[string]interface{} `json:"rules"`
}
type Box struct { type Box struct {
createdAt time.Time createdAt time.Time
router adapter.Router router adapter.Router
@ -68,6 +73,17 @@ func New(c *conf.CoreConfig) (vCore.Core, error) {
ServerPort: c.SingConfig.NtpConfig.ServerPort, ServerPort: c.SingConfig.NtpConfig.ServerPort,
}, },
} }
os.Setenv("SING_DNS_PATH", "")
if c.SingConfig.DnsConfigPath != "" {
if f, err := os.Open(c.SingConfig.DnsConfigPath); err != nil {
log.Error("Failed to read DNS config file")
} else {
if err = json.NewDecoder(f).Decode(&option.DNSOptions{}); err != nil {
log.Error("Failed to unmarshal DNS config")
}
}
os.Setenv("SING_DNS_PATH", c.SingConfig.DnsConfigPath)
}
ctx := context.Background() ctx := context.Background()
ctx = service.ContextWithDefaultRegistry(ctx) ctx = service.ContextWithDefaultRegistry(ctx)
ctx = pause.ContextWithDefaultManager(ctx) ctx = pause.ContextWithDefaultManager(ctx)

View File

@ -7,7 +7,6 @@ import (
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
coreConf "github.com/xtls/xray-core/infra/conf" coreConf "github.com/xtls/xray-core/infra/conf"
"os" "os"
"time"
) )
func updateDNSConfig(node *panel.NodeInfo) (err error) { func updateDNSConfig(node *panel.NodeInfo) (err error) {
@ -57,7 +56,5 @@ func saveDnsConfig(dns []byte, dnsPath string) (err error) {
log.WithField("err", err).Error("Failed to write DNS to XRAY DNS PATH file") log.WithField("err", err).Error("Failed to write DNS to XRAY DNS PATH file")
} }
} }
log.Println("reloading config")
time.Sleep(5 * time.Second)
return err return err
} }

View File

@ -10,6 +10,7 @@
"Level": "error", "Level": "error",
"Timestamp": true "Timestamp": true
}, },
"DnsConfigPath": "/etc/V2bX/dns.json",
"NTP": { "NTP": {
"Enable": true, "Enable": true,
"Server": "time.apple.com", "Server": "time.apple.com",
@ -28,6 +29,7 @@
"ListenIP": "0.0.0.0", "ListenIP": "0.0.0.0",
"SendIP": "0.0.0.0", "SendIP": "0.0.0.0",
"EnableProxyProtocol": false, "EnableProxyProtocol": false,
"EnableDNS": true
"DomainStrategy": "ipv4_only", "DomainStrategy": "ipv4_only",
"LimitConfig": { "LimitConfig": {
"EnableRealtime": false, "EnableRealtime": false,

View File

@ -26,6 +26,7 @@
"Server": "time.apple.com", "Server": "time.apple.com",
"ServerPort": 0 "ServerPort": 0
}, },
"DnsConfigPath": "/etc/V2bX/dns.json",
// SingBoxSingBox // SingBoxSingBox
"OriginalPath": "/etc/V2bX/sing_origin.json" "OriginalPath": "/etc/V2bX/sing_origin.json"
}, },
@ -113,7 +114,9 @@
// TCP Fast Open // TCP Fast Open
"EnableTFO": true, "EnableTFO": true,
// Domain Strategy // DNS
"EnableDNS" : true,
// Domain Strategy DNS AsIS
// prefer_ipv4 / prefer_ipv6 / ipv4_only / ipv6_only // prefer_ipv4 / prefer_ipv6 / ipv4_only / ipv6_only
"DomainStrategy": "ipv4_only", "DomainStrategy": "ipv4_only",

View File

@ -9,5 +9,5 @@
"SendIP": "0.0.0.0", "SendIP": "0.0.0.0",
"EnableProxyProtocol": false, "EnableProxyProtocol": false,
"EnableTFO": true, "EnableTFO": true,
"DomainStrategy": "ipv4_only" "DNSType": "ipv4_only"
} }