mirror of
https://github.com/daeuniverse/dae.git
synced 2025-07-21 13:19:10 +07:00
refactor: rename check_url to tcp_check_url and restrict dns_upstream as udp://ip:port
This commit is contained in:
@ -75,11 +75,7 @@ func Run(log *logrus.Logger, param *config.Params) (err error) {
|
||||
nodeList,
|
||||
param.Group,
|
||||
¶m.Routing,
|
||||
param.Global.DnsUpstream,
|
||||
param.Global.CheckUrl,
|
||||
param.Global.CheckInterval,
|
||||
param.Global.LanInterface,
|
||||
param.Global.WanInterface,
|
||||
¶m.Global,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -10,6 +10,7 @@ import (
|
||||
"encoding/binary"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"github.com/v2rayA/dae/config"
|
||||
"net/url"
|
||||
"reflect"
|
||||
"strconv"
|
||||
@ -276,6 +277,27 @@ func FuzzyDecode(to interface{}, val string) bool {
|
||||
}
|
||||
case reflect.String:
|
||||
v.SetString(val)
|
||||
case reflect.Struct:
|
||||
switch v.Interface().(type) {
|
||||
case config.UrlOrEmpty:
|
||||
if val == "" {
|
||||
v.Set(reflect.ValueOf(config.UrlOrEmpty{
|
||||
Url: nil,
|
||||
Empty: true,
|
||||
}))
|
||||
} else {
|
||||
u, err := url.Parse(val)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
v.Set(reflect.ValueOf(config.UrlOrEmpty{
|
||||
Url: u,
|
||||
Empty: false,
|
||||
}))
|
||||
}
|
||||
default:
|
||||
return false
|
||||
}
|
||||
default:
|
||||
return false
|
||||
}
|
||||
|
@ -8,16 +8,23 @@ package config
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/v2rayA/dae/pkg/config_parser"
|
||||
"net/url"
|
||||
"reflect"
|
||||
"time"
|
||||
)
|
||||
|
||||
type UrlOrEmpty struct {
|
||||
Url *url.URL
|
||||
Empty bool
|
||||
}
|
||||
|
||||
type Global struct {
|
||||
TproxyPort uint16 `mapstructure:"tproxy_port" default:"12345"`
|
||||
LogLevel string `mapstructure:"log_level" default:"info"`
|
||||
CheckUrl string `mapstructure:"check_url" default:"https://connectivitycheck.gstatic.com/generate_204"`
|
||||
TcpCheckUrl string `mapstructure:"tcp_check_url" default:"https://connectivitycheck.gstatic.com/generate_204"`
|
||||
UdpCheckDns string `mapstructure:"udp_check_dns" default:"8.8.8.8:53"`
|
||||
CheckInterval time.Duration `mapstructure:"check_interval" default:"15s"`
|
||||
DnsUpstream string `mapstructure:"dns_upstream" require:""`
|
||||
DnsUpstream UrlOrEmpty `mapstructure:"dns_upstream" require:""`
|
||||
LanInterface []string `mapstructure:"lan_interface"`
|
||||
WanInterface []string `mapstructure:"wan_interface"`
|
||||
}
|
||||
@ -81,13 +88,13 @@ func New(sections []*config_parser.Section) (params *Params, err error) {
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("no parser is specified in field %v", structField.Name)
|
||||
}
|
||||
parserFunc, ok := ParserMap[parserName]
|
||||
parser, ok := ParserMap[parserName]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unknown parser %v in field %v", parserName, structField.Name)
|
||||
}
|
||||
|
||||
// Parse section and unmarshal to field.
|
||||
if err := parserFunc(field.Addr(), section.Val); err != nil {
|
||||
if err := parser(field.Addr(), section.Val); err != nil {
|
||||
return nil, fmt.Errorf("failed to parse \"%v\": %w", sectionName, err)
|
||||
}
|
||||
section.Parsed = true
|
||||
|
@ -29,7 +29,6 @@ import (
|
||||
"strings"
|
||||
"sync"
|
||||
"syscall"
|
||||
"time"
|
||||
)
|
||||
|
||||
type ControlPlane struct {
|
||||
@ -56,11 +55,7 @@ func NewControlPlane(
|
||||
nodes []string,
|
||||
groups []config.Group,
|
||||
routingA *config.Routing,
|
||||
dnsUpstream string,
|
||||
checkUrl string,
|
||||
checkInterval time.Duration,
|
||||
lanInterface []string,
|
||||
wanInterface []string,
|
||||
global *config.Global,
|
||||
) (c *ControlPlane, err error) {
|
||||
kernelVersion, e := internal.KernelVersion()
|
||||
if e != nil {
|
||||
@ -73,12 +68,12 @@ func NewControlPlane(
|
||||
kernelVersion.String(),
|
||||
consts.ChecksumFeatureVersion.String())
|
||||
}
|
||||
if len(wanInterface) > 0 && kernelVersion.Less(consts.CgSocketCookieFeatureVersion) {
|
||||
if len(global.WanInterface) > 0 && kernelVersion.Less(consts.CgSocketCookieFeatureVersion) {
|
||||
return nil, fmt.Errorf("your kernel version %v does not support bind to WAN; expect >=%v; remove wan_interface in config file and try again",
|
||||
kernelVersion.String(),
|
||||
consts.CgSocketCookieFeatureVersion.String())
|
||||
}
|
||||
if len(lanInterface) > 0 && kernelVersion.Less(consts.SkAssignFeatureVersion) {
|
||||
if len(global.LanInterface) > 0 && kernelVersion.Less(consts.SkAssignFeatureVersion) {
|
||||
return nil, fmt.Errorf("your kernel version %v does not support bind to LAN; expect >=%v; remove lan_interface in config file and try again",
|
||||
kernelVersion.String(),
|
||||
consts.SkAssignFeatureVersion.String())
|
||||
@ -117,8 +112,8 @@ func NewControlPlane(
|
||||
if err = selectivelyLoadBpfObjects(log, &bpf, &loadBpfOptions{
|
||||
PinPath: pinPath,
|
||||
CollectionOptions: collectionOpts,
|
||||
BindLan: len(lanInterface) > 0,
|
||||
BindWan: len(wanInterface) > 0,
|
||||
BindLan: len(global.LanInterface) > 0,
|
||||
BindWan: len(global.WanInterface) > 0,
|
||||
}); err != nil {
|
||||
return nil, fmt.Errorf("load eBPF objects: %w", err)
|
||||
}
|
||||
@ -164,26 +159,26 @@ func NewControlPlane(
|
||||
}()
|
||||
/// Bind to links. Binding should be advance of dialerGroups to avoid un-routable old connection.
|
||||
// Add clsact qdisc
|
||||
for _, ifname := range common.Deduplicate(append(append([]string{}, lanInterface...), wanInterface...)) {
|
||||
for _, ifname := range common.Deduplicate(append(append([]string{}, global.LanInterface...), global.WanInterface...)) {
|
||||
_ = core.addQdisc(ifname)
|
||||
}
|
||||
// Bind to LAN
|
||||
if len(lanInterface) > 0 {
|
||||
if len(global.LanInterface) > 0 {
|
||||
if err = core.setupRoutingPolicy(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, ifname := range lanInterface {
|
||||
for _, ifname := range global.LanInterface {
|
||||
if err = core.bindLan(ifname); err != nil {
|
||||
return nil, fmt.Errorf("bindLan: %v: %w", ifname, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
// Bind to WAN
|
||||
if len(wanInterface) > 0 {
|
||||
if len(global.WanInterface) > 0 {
|
||||
if err = core.setupSkPidMonitor(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, ifname := range wanInterface {
|
||||
for _, ifname := range global.WanInterface {
|
||||
if err = core.bindWan(ifname); err != nil {
|
||||
return nil, fmt.Errorf("bindWan: %v: %w", ifname, err)
|
||||
}
|
||||
@ -193,8 +188,8 @@ func NewControlPlane(
|
||||
/// DialerGroups (outbounds).
|
||||
option := &dialer.GlobalOption{
|
||||
Log: log,
|
||||
CheckUrl: checkUrl,
|
||||
CheckInterval: checkInterval,
|
||||
CheckUrl: global.TcpCheckUrl,
|
||||
CheckInterval: global.CheckInterval,
|
||||
}
|
||||
outbounds := []*outbound.DialerGroup{
|
||||
outbound.NewDialerGroup(option, consts.OutboundDirect.String(),
|
||||
@ -273,10 +268,9 @@ func NewControlPlane(
|
||||
|
||||
/// DNS upstream.
|
||||
var dnsAddrPort netip.AddrPort
|
||||
if dnsUpstream != "" {
|
||||
dnsAddrPort, err = netip.ParseAddrPort(dnsUpstream)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse DNS upstream: \"%v\": %w", dnsUpstream, err)
|
||||
if !global.DnsUpstream.Empty {
|
||||
if dnsAddrPort, err = resolveDnsUpstream(global.DnsUpstream.Url); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dnsAddr16 := dnsAddrPort.Addr().As16()
|
||||
if err = bpf.DnsUpstreamMap.Update(consts.ZeroKey, bpfIpPort{
|
||||
@ -286,6 +280,7 @@ func NewControlPlane(
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
// Empty.
|
||||
if err = bpf.DnsUpstreamMap.Update(consts.ZeroKey, bpfIpPort{
|
||||
Ip: [4]uint32{},
|
||||
// Zero port indicates no element, because bpf_map_lookup_elem cannot return 0 for map_type_array.
|
||||
@ -297,7 +292,7 @@ func NewControlPlane(
|
||||
|
||||
/// Listen address.
|
||||
listenIp := "::1"
|
||||
if len(wanInterface) > 0 {
|
||||
if len(global.WanInterface) > 0 {
|
||||
listenIp = "0.0.0.0"
|
||||
}
|
||||
return &ControlPlane{
|
||||
|
34
control/control_utils.go
Normal file
34
control/control_utils.go
Normal file
@ -0,0 +1,34 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
* Copyright (c) since 2023, mzz2017 <mzz@tuta.io>
|
||||
*/
|
||||
|
||||
package control
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"net/netip"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
func resolveDnsUpstream(dnsUpstream *url.URL) (addrPort netip.AddrPort, err error) {
|
||||
if dnsUpstream.Scheme != "udp" {
|
||||
return netip.AddrPort{}, fmt.Errorf("dns_upstream now only supports udp://")
|
||||
}
|
||||
port := dnsUpstream.Port()
|
||||
if port == "" {
|
||||
port = "53"
|
||||
}
|
||||
hostname := dnsUpstream.Hostname()
|
||||
ips, _ := net.LookupIP(hostname)
|
||||
if len(ips) == 0 {
|
||||
return netip.AddrPort{}, fmt.Errorf("cannot resolve hostname of dns upstream: %v", hostname)
|
||||
}
|
||||
// resolve hostname
|
||||
dnsAddrPort, err := netip.ParseAddrPort(net.JoinHostPort(ips[0].String(), port))
|
||||
if err != nil {
|
||||
return netip.AddrPort{}, fmt.Errorf("failed to parse DNS upstream: \"%v\": %w", dnsUpstream.String(), err)
|
||||
}
|
||||
return dnsAddrPort, nil
|
||||
}
|
@ -6,14 +6,14 @@ global {
|
||||
log_level: info
|
||||
|
||||
# Node connectivity check.
|
||||
check_url: 'https://connectivitycheck.gstatic.com/generate_204'
|
||||
tcp_check_url: 'https://connectivitycheck.gstatic.com/generate_204'
|
||||
check_interval: 30s
|
||||
|
||||
# Now only support UDP and format IP:Port. Empty value '' indicates as-is.
|
||||
# Now only support udp://IP:Port. Empty value '' indicates as-is.
|
||||
# Please make sure DNS traffic will go through and be forwarded by dae.
|
||||
# The upstream DNS answer MUST NOT be polluted.
|
||||
# The request to dns upstream follows routing defined below.
|
||||
dns_upstream: '8.8.8.8:53'
|
||||
dns_upstream: 'udp://8.8.8.8:53'
|
||||
|
||||
# The LAN interface to bind. Use it if you only want to proxy LAN instead of localhost.
|
||||
# Multiple interfaces split by ",".
|
||||
|
Reference in New Issue
Block a user