Skip to content

Commit 2d29755

Browse files
committed
Add DNS mode
1 parent 812b89e commit 2d29755

5 files changed

Lines changed: 120 additions & 58 deletions

File tree

redirect_nftables_rules.go

Lines changed: 11 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -675,7 +675,7 @@ func (r *autoRedirect) nftablesCreateExcludeRules(nft *nftables.Conn, table *nft
675675
nftablesCreateExcludeDestinationIPSet(nft, table, chain, inet6RouteExcludeAddress.ID, inet6RouteExcludeAddress.Name, nftables.TableFamilyIPv6, false)
676676
}
677677

678-
if !r.tunOptions.EXP_DisableDNSHijack && ((chain.Hooknum == nftables.ChainHookPrerouting && chain.Type == nftables.ChainTypeNAT) ||
678+
if r.tunOptions.DNSMode == DNSModeHijack && ((chain.Hooknum == nftables.ChainHookPrerouting && chain.Type == nftables.ChainTypeNAT) ||
679679
(r.tunOptions.AutoRedirectMarkMode && chain.Hooknum == nftables.ChainHookOutput && chain.Type == nftables.ChainTypeNAT)) {
680680
if r.enableIPv4 {
681681
err := r.nftablesCreateDNSHijackRulesForFamily(nft, table, chain, nftables.TableFamilyIPv4, 5, "inet4_local_address_set")
@@ -997,23 +997,19 @@ func (r *autoRedirect) nftablesCreateDNSHijackRulesForFamily(
997997
if err != nil {
998998
return E.Cause(err, "add dns protocol set")
999999
}
1000-
dnsServer := common.Find(r.tunOptions.DNSServers, func(it netip.Addr) bool {
1001-
return it.Is4() == (family == nftables.TableFamilyIPv4)
1002-
})
1003-
if !dnsServer.IsValid() {
1004-
if family == nftables.TableFamilyIPv4 {
1005-
if HasNextAddress(r.tunOptions.Inet4Address[0], 1) {
1006-
dnsServer = r.tunOptions.Inet4Address[0].Addr().Next()
1007-
}
1008-
} else {
1009-
if HasNextAddress(r.tunOptions.Inet6Address[0], 1) {
1010-
dnsServer = r.tunOptions.Inet6Address[0].Addr().Next()
1011-
}
1012-
}
1000+
var dnsServers []netip.Addr
1001+
if family == nftables.TableFamilyIPv4 {
1002+
dnsServers, err = r.tunOptions.Inet4DNSAddress()
1003+
} else {
1004+
dnsServers, err = r.tunOptions.Inet6DNSAddress()
1005+
}
1006+
if err != nil {
1007+
return err
10131008
}
1014-
if !dnsServer.IsValid() {
1009+
if len(dnsServers) == 0 {
10151010
return nil
10161011
}
1012+
dnsServer := dnsServers[0]
10171013
exprs := []expr.Any{
10181014
&expr.Meta{
10191015
Key: expr.MetaKeyNFPROTO,

tun.go

Lines changed: 69 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,10 @@ import (
99
"strings"
1010
"time"
1111

12+
"github.com/sagernet/sing/common"
1213
"github.com/sagernet/sing/common/buf"
1314
"github.com/sagernet/sing/common/control"
15+
E "github.com/sagernet/sing/common/exceptions"
1416
F "github.com/sagernet/sing/common/format"
1517
"github.com/sagernet/sing/common/logger"
1618
M "github.com/sagernet/sing/common/metadata"
@@ -68,6 +70,12 @@ const (
6870
DefaultIPRoute2AutoRedirectFallbackRuleIndex = 32768
6971
)
7072

73+
const (
74+
DNSModeDisabled = "disabled"
75+
DNSModeNative = "native"
76+
DNSModeHijack = "hijack"
77+
)
78+
7179
type Options struct {
7280
Name string
7381
Inet4Address []netip.Prefix
@@ -78,7 +86,8 @@ type Options struct {
7886
InterfaceScope bool
7987
Inet4Gateway netip.Addr
8088
Inet6Gateway netip.Addr
81-
DNSServers []netip.Addr
89+
DNSMode string
90+
DNSAddress []netip.Addr
8291
IPRoute2TableIndex int
8392
IPRoute2RuleIndex int
8493
IPRoute2AutoRedirectFallbackRuleIndex int
@@ -124,6 +133,65 @@ type Options struct {
124133
EXP_SendMsgX bool
125134
}
126135

136+
func (o *Options) NormalizeDNSMode() error {
137+
switch o.DNSMode {
138+
case "":
139+
if o.EXP_DisableDNSHijack {
140+
o.DNSMode = DNSModeDisabled
141+
} else {
142+
o.DNSMode = DNSModeHijack
143+
}
144+
case DNSModeDisabled, DNSModeNative, DNSModeHijack:
145+
default:
146+
return E.New("unknown DNS mode: ", o.DNSMode)
147+
}
148+
return nil
149+
}
150+
151+
func (o *Options) DNSServerAddress() ([]netip.Addr, error) {
152+
inet4DNS, err := o.Inet4DNSAddress()
153+
if err != nil {
154+
return nil, err
155+
}
156+
inet6DNS, err := o.Inet6DNSAddress()
157+
if err != nil {
158+
return nil, err
159+
}
160+
return append(inet4DNS, inet6DNS...), nil
161+
}
162+
163+
func (o *Options) Inet4DNSAddress() ([]netip.Addr, error) {
164+
if len(o.Inet4Address) == 0 {
165+
return nil, nil
166+
}
167+
if len(o.DNSAddress) > 0 {
168+
return common.Filter(o.DNSAddress, netip.Addr.Is4), nil
169+
}
170+
if HasNextAddress(o.Inet4Address[0], 1) {
171+
return []netip.Addr{o.Inet4Address[0].Addr().Next()}, nil
172+
}
173+
if o.DNSMode == DNSModeNative {
174+
return nil, E.New("no IPv4 server configured and no usable next address in ", o.Inet4Address[0], " for native DNS")
175+
}
176+
return nil, nil
177+
}
178+
179+
func (o *Options) Inet6DNSAddress() ([]netip.Addr, error) {
180+
if len(o.Inet6Address) == 0 {
181+
return nil, nil
182+
}
183+
if len(o.DNSAddress) > 0 {
184+
return common.Filter(o.DNSAddress, netip.Addr.Is6), nil
185+
}
186+
if HasNextAddress(o.Inet6Address[0], 1) {
187+
return []netip.Addr{o.Inet6Address[0].Addr().Next()}, nil
188+
}
189+
if o.DNSMode == DNSModeNative {
190+
return nil, E.New("no IPv6 server configured and no usable next address in ", o.Inet6Address[0], " for native DNS")
191+
}
192+
return nil, nil
193+
}
194+
127195
func (o *Options) Inet4GatewayAddr() netip.Addr {
128196
if o.Inet4Gateway.IsValid() {
129197
return o.Inet4Gateway

tun_darwin.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,9 @@ func (t *NativeTun) Name() (string, error) {
8989
}
9090

9191
func New(options Options) (Tun, error) {
92+
if err := options.NormalizeDNSMode(); err != nil {
93+
return nil, err
94+
}
9295
var tunFd int
9396
batchSize := ((512 * 1024) / int(options.MTU)) + 1
9497
if options.FileDescriptor == 0 {

tun_linux.go

Lines changed: 19 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,9 @@ type NativeTun struct {
4949
}
5050

5151
func New(options Options) (Tun, error) {
52+
if err := options.NormalizeDNSMode(); err != nil {
53+
return nil, err
54+
}
5255
var nativeTun *NativeTun
5356
if options.FileDescriptor == 0 {
5457
tunFd, err := open(options.Name, options.GSO)
@@ -317,7 +320,12 @@ func (t *NativeTun) Start() error {
317320
return E.Cause(err, "set rules")
318321
}
319322

320-
t.setSearchDomainForSystemdResolved()
323+
if t.options.DNSMode != DNSModeDisabled {
324+
err = t.setSearchDomainForSystemdResolved()
325+
if err != nil {
326+
return E.Cause(err, "set search domain")
327+
}
328+
}
321329

322330
if t.options.AutoRoute && runtime.GOOS == "android" {
323331
t.interfaceCallback = t.options.InterfaceMonitor.RegisterCallback(t.routeUpdate)
@@ -332,7 +340,9 @@ func (t *NativeTun) Close() error {
332340
if t.options.EXP_ExternalConfiguration {
333341
return common.Close(common.PtrOrNil(t.tunFile))
334342
}
335-
t.unsetSearchDomainForSystemdResolved()
343+
if t.options.DNSMode != DNSModeDisabled {
344+
t.unsetSearchDomainForSystemdResolved()
345+
}
336346
t.unsetAddresses()
337347
return E.Errors(t.unsetRoute(), t.unsetRules(), common.Close(common.PtrOrNil(t.tunFile)))
338348
}
@@ -1073,37 +1083,24 @@ func (t *NativeTun) routeUpdate(_ *control.Interface, flags int) {
10731083
}
10741084
}
10751085

1076-
func (t *NativeTun) setSearchDomainForSystemdResolved() {
1077-
if t.options.EXP_DisableDNSHijack {
1078-
return
1079-
}
1086+
func (t *NativeTun) setSearchDomainForSystemdResolved() error {
10801087
ctlPath, err := exec.LookPath("resolvectl")
10811088
if err != nil {
1082-
return
1083-
}
1084-
dnsServer := t.options.DNSServers
1085-
if len(dnsServer) == 0 {
1086-
if len(t.options.Inet4Address) > 0 && HasNextAddress(t.options.Inet4Address[0], 1) {
1087-
dnsServer = append(dnsServer, t.options.Inet4Address[0].Addr().Next())
1088-
}
1089-
if len(t.options.Inet6Address) > 0 && HasNextAddress(t.options.Inet6Address[0], 1) {
1090-
dnsServer = append(dnsServer, t.options.Inet6Address[0].Addr().Next())
1091-
}
1089+
return nil
10921090
}
1093-
if len(dnsServer) == 0 {
1094-
return
1091+
dnsAddress, err := t.options.DNSServerAddress()
1092+
if err != nil {
1093+
return err
10951094
}
10961095
go func() {
10971096
_ = shell.Exec(ctlPath, "domain", t.options.Name, "~.").Run()
10981097
_ = shell.Exec(ctlPath, "default-route", t.options.Name, "true").Run()
1099-
_ = shell.Exec(ctlPath, append([]string{"dns", t.options.Name}, common.Map(dnsServer, netip.Addr.String)...)...).Run()
1098+
_ = shell.Exec(ctlPath, append([]string{"dns", t.options.Name}, common.Map(dnsAddress, netip.Addr.String)...)...).Run()
11001099
}()
1100+
return nil
11011101
}
11021102

11031103
func (t *NativeTun) unsetSearchDomainForSystemdResolved() {
1104-
if t.options.EXP_DisableDNSHijack {
1105-
return
1106-
}
11071104
ctlPath, err := exec.LookPath("resolvectl")
11081105
if err != nil {
11091106
return

tun_windows.go

Lines changed: 18 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ import (
1616
"github.com/sagernet/sing-tun/internal/winipcfg"
1717
"github.com/sagernet/sing-tun/internal/winsys"
1818
"github.com/sagernet/sing-tun/internal/wintun"
19-
"github.com/sagernet/sing/common"
2019
E "github.com/sagernet/sing/common/exceptions"
2120
"github.com/sagernet/sing/common/windnsapi"
2221

@@ -38,6 +37,9 @@ type NativeTun struct {
3837
}
3938

4039
func New(options Options) (WinTun, error) {
40+
if err := options.NormalizeDNSMode(); err != nil {
41+
return nil, err
42+
}
4143
if options.FileDescriptor != 0 {
4244
return nil, os.ErrInvalid
4345
}
@@ -74,16 +76,14 @@ func (t *NativeTun) configure() error {
7476
if err != nil {
7577
return E.Cause(err, "set ipv4 address")
7678
}
77-
if t.options.AutoRoute && !t.options.EXP_DisableDNSHijack {
78-
dnsServers := common.Filter(t.options.DNSServers, netip.Addr.Is4)
79-
if len(dnsServers) == 0 && HasNextAddress(t.options.Inet4Address[0], 1) {
80-
dnsServers = []netip.Addr{t.options.Inet4Address[0].Addr().Next()}
79+
if t.options.AutoRoute && t.options.DNSMode != DNSModeDisabled {
80+
dnsServers, err := t.options.Inet4DNSAddress()
81+
if err != nil {
82+
return err
8183
}
82-
if len(dnsServers) > 0 {
83-
err = luid.SetDNS(winipcfg.AddressFamily(windows.AF_INET), dnsServers, nil)
84-
if err != nil {
85-
return E.Cause(err, "set ipv4 dns")
86-
}
84+
err = luid.SetDNS(winipcfg.AddressFamily(windows.AF_INET), dnsServers, nil)
85+
if err != nil {
86+
return E.Cause(err, "set ipv4 dns")
8787
}
8888
} else {
8989
err = luid.SetDNS(winipcfg.AddressFamily(windows.AF_INET), nil, nil)
@@ -97,16 +97,14 @@ func (t *NativeTun) configure() error {
9797
if err != nil {
9898
return E.Cause(err, "set ipv6 address")
9999
}
100-
if t.options.AutoRoute && !t.options.EXP_DisableDNSHijack {
101-
dnsServers := common.Filter(t.options.DNSServers, netip.Addr.Is6)
102-
if len(dnsServers) == 0 && HasNextAddress(t.options.Inet6Address[0], 1) {
103-
dnsServers = []netip.Addr{t.options.Inet6Address[0].Addr().Next()}
100+
if t.options.AutoRoute && t.options.DNSMode != DNSModeDisabled {
101+
dnsServers, err := t.options.Inet6DNSAddress()
102+
if err != nil {
103+
return err
104104
}
105-
if len(dnsServers) > 0 {
106-
err = luid.SetDNS(winipcfg.AddressFamily(windows.AF_INET6), dnsServers, nil)
107-
if err != nil {
108-
return E.Cause(err, "set ipv6 dns")
109-
}
105+
err = luid.SetDNS(winipcfg.AddressFamily(windows.AF_INET6), dnsServers, nil)
106+
if err != nil {
107+
return E.Cause(err, "set ipv6 dns")
110108
}
111109
} else {
112110
err = luid.SetDNS(winipcfg.AddressFamily(windows.AF_INET6), nil, nil)
@@ -327,7 +325,7 @@ func (t *NativeTun) Start() error {
327325
}
328326
}
329327

330-
if !t.options.EXP_DisableDNSHijack {
328+
if t.options.DNSMode == DNSModeHijack {
331329
blockDNSCondition := make([]winsys.FWPM_FILTER_CONDITION0, 1)
332330
blockDNSCondition[0].FieldKey = winsys.FWPM_CONDITION_IP_REMOTE_PORT
333331
blockDNSCondition[0].MatchType = winsys.FWP_MATCH_EQUAL

0 commit comments

Comments
 (0)