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
}
log.Info("Nodes started")
dns := os.Getenv("XRAY_DNS_PATH")
xdns := os.Getenv("XRAY_DNS_PATH")
sdns := os.Getenv("SING_DNS_PATH")
if watch {
err = c.Watch(config, dns, func() {
err = c.Watch(config, xdns, sdns, func() {
nodes.Close()
err = vc.Close()
if err != nil {

View File

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

View File

@ -5,10 +5,12 @@ import (
"github.com/fsnotify/fsnotify"
"log"
"path"
"path/filepath"
"strings"
"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()
if err != nil {
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()
go func() {
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...")
} else {
default:
log.Println("config dir changed, reloading...")
}
*p = *New()
@ -52,8 +55,14 @@ func (p *Conf) Watch(filePath, dnsPath string, reload func()) error {
if err != nil {
return fmt.Errorf("watch file error: %s", err)
}
if dnsPath != "" {
err = watcher.Add(path.Dir(dnsPath))
if xDnsPath != "" {
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 {
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 {
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.ListenAddress)(&addr),
ListenPort: uint16(info.Common.ServerPort),
@ -38,7 +42,7 @@ func getInboundOptions(tag string, info *panel.NodeInfo, c *conf.Options) (optio
InboundOptions: option.InboundOptions{
SniffEnabled: c.SingOptions.SniffEnabled,
SniffOverrideDestination: c.SingOptions.SniffOverrideDestination,
DomainStrategy: c.SingOptions.DomainStrategy,
DomainStrategy: domainStrategy,
},
}
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 {
err := updateDNSConfig(info)
if err != nil {
return fmt.Errorf("build dns error: %s", err)
}
c, err := getInboundOptions(tag, info, config)
if err != nil {
return err

View File

@ -26,6 +26,11 @@ import (
var _ adapter.Service = (*Box)(nil)
type DNSConfig struct {
Servers []map[string]interface{} `json:"servers"`
Rules []map[string]interface{} `json:"rules"`
}
type Box struct {
createdAt time.Time
router adapter.Router
@ -68,6 +73,17 @@ func New(c *conf.CoreConfig) (vCore.Core, error) {
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 = service.ContextWithDefaultRegistry(ctx)
ctx = pause.ContextWithDefaultManager(ctx)

View File

@ -7,7 +7,6 @@ import (
log "github.com/sirupsen/logrus"
coreConf "github.com/xtls/xray-core/infra/conf"
"os"
"time"
)
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.Println("reloading config")
time.Sleep(5 * time.Second)
return err
}

View File

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

View File

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

View File

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