Skip to content

Commit 831469a

Browse files
committed
Make DNS hijack optional
1 parent 66e34ab commit 831469a

File tree

7 files changed

+154
-112
lines changed

7 files changed

+154
-112
lines changed

redirect_iptables.go

Lines changed: 35 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"os/exec"
88
"strings"
99

10+
"github.com/sagernet/sing/common"
1011
E "github.com/sagernet/sing/common/exceptions"
1112
F "github.com/sagernet/sing/common/format"
1213

@@ -109,42 +110,48 @@ func (r *autoRedirect) setupIPTables(family int) error {
109110
return err
110111
}
111112
}
112-
var dnsServerAddress netip.Addr
113-
if family == unix.AF_INET {
114-
dnsServerAddress = r.tunOptions.Inet4Address[0].Addr().Next()
115-
} else {
116-
dnsServerAddress = r.tunOptions.Inet6Address[0].Addr().Next()
117-
}
118-
if len(routeAddress) > 0 {
119-
for _, address := range routeAddress {
120-
err = r.runShell(iptablesPath, "-t nat -A", tableNamePreRouteing,
121-
"-d", address.String(), "-p udp --dport 53 -j DNAT --to", dnsServerAddress)
122-
if err != nil {
123-
return err
113+
if !r.tunOptions.EXP_DisableDNSHijack {
114+
dnsServer := common.Find(r.tunOptions.DNSServers, func(it netip.Addr) bool {
115+
return it.Is4() == (family == unix.AF_INET)
116+
})
117+
if !dnsServer.IsValid() {
118+
if family == unix.AF_INET {
119+
dnsServer = r.tunOptions.Inet4Address[0].Addr().Next()
120+
} else {
121+
dnsServer = r.tunOptions.Inet6Address[0].Addr().Next()
124122
}
125123
}
126-
} else if len(r.tunOptions.IncludeInterface) > 0 || len(r.tunOptions.IncludeUID) > 0 {
127-
for _, name := range r.tunOptions.IncludeInterface {
128-
err = r.runShell(iptablesPath, "-t nat -A", tableNamePreRouteing,
129-
"-i", name, "-p udp --dport 53 -j DNAT --to", dnsServerAddress)
130-
if err != nil {
131-
return err
124+
if len(routeAddress) > 0 {
125+
for _, address := range routeAddress {
126+
err = r.runShell(iptablesPath, "-t nat -A", tableNamePreRouteing,
127+
"-d", address.String(), "-p udp --dport 53 -j DNAT --to", dnsServer)
128+
if err != nil {
129+
return err
130+
}
132131
}
133-
}
134-
for _, uidRange := range r.tunOptions.IncludeUID {
135-
for uid := uidRange.Start; uid <= uidRange.End; uid++ {
132+
} else if len(r.tunOptions.IncludeInterface) > 0 || len(r.tunOptions.IncludeUID) > 0 {
133+
for _, name := range r.tunOptions.IncludeInterface {
136134
err = r.runShell(iptablesPath, "-t nat -A", tableNamePreRouteing,
137-
"-m owner --uid-owner", uid, "-p udp --dport 53 -j DNAT --to", dnsServerAddress)
135+
"-i", name, "-p udp --dport 53 -j DNAT --to", dnsServer)
138136
if err != nil {
139137
return err
140138
}
141139
}
142-
}
143-
} else {
144-
err = r.runShell(iptablesPath, "-t nat -A", tableNamePreRouteing,
145-
"-p udp --dport 53 -j DNAT --to", dnsServerAddress)
146-
if err != nil {
147-
return err
140+
for _, uidRange := range r.tunOptions.IncludeUID {
141+
for uid := uidRange.Start; uid <= uidRange.End; uid++ {
142+
err = r.runShell(iptablesPath, "-t nat -A", tableNamePreRouteing,
143+
"-m owner --uid-owner", uid, "-p udp --dport 53 -j DNAT --to", dnsServer)
144+
if err != nil {
145+
return err
146+
}
147+
}
148+
}
149+
} else {
150+
err = r.runShell(iptablesPath, "-t nat -A", tableNamePreRouteing,
151+
"-p udp --dport 53 -j DNAT --to", dnsServer)
152+
if err != nil {
153+
return err
154+
}
148155
}
149156
}
150157

redirect_linux.go

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,16 @@ package tun
22

33
import (
44
"context"
5+
"net/netip"
6+
"os"
7+
"os/exec"
8+
"runtime"
9+
510
"github.com/sagernet/nftables"
611
"github.com/sagernet/sing/common"
712
E "github.com/sagernet/sing/common/exceptions"
813
"github.com/sagernet/sing/common/logger"
914
M "github.com/sagernet/sing/common/metadata"
10-
"net/netip"
11-
"os"
12-
"os/exec"
13-
"runtime"
1415

1516
"golang.org/x/sys/unix"
1617
)
@@ -39,6 +40,7 @@ func NewAutoRedirect(options AutoRedirectOptions) (AutoRedirect, error) {
3940
ctx: options.Context,
4041
handler: options.Handler,
4142
logger: options.Logger,
43+
tableName: options.TableName,
4244
useNFTables: runtime.GOOS != "android" && !options.DisableNFTables,
4345
customRedirectPortFunc: options.CustomRedirectPort,
4446
}

redirect_nftables.go

Lines changed: 28 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"github.com/sagernet/nftables"
99
"github.com/sagernet/nftables/binaryutil"
1010
"github.com/sagernet/nftables/expr"
11+
"github.com/sagernet/sing/common"
1112
F "github.com/sagernet/sing/common/format"
1213

1314
"golang.org/x/sys/unix"
@@ -138,34 +139,39 @@ func (r *autoRedirect) setupNFTables(family int) error {
138139
}
139140
}
140141

141-
var dnsServerAddress netip.Addr
142-
if table.Family == nftables.TableFamilyIPv4 {
143-
dnsServerAddress = r.tunOptions.Inet4Address[0].Addr().Next()
144-
} else {
145-
dnsServerAddress = r.tunOptions.Inet6Address[0].Addr().Next()
146-
}
147-
148-
if len(r.tunOptions.IncludeInterface) > 0 || len(r.tunOptions.IncludeUID) > 0 {
149-
for _, name := range r.tunOptions.IncludeInterface {
150-
nft.AddRule(&nftables.Rule{
151-
Table: table,
152-
Chain: chainPreRouting,
153-
Exprs: nftablesRuleIfName(expr.MetaKeyIIFNAME, name, append(routeExprs, nftablesRuleHijackDNS(table.Family, dnsServerAddress)...)...),
154-
})
142+
if !r.tunOptions.EXP_DisableDNSHijack {
143+
dnsServer := common.Find(r.tunOptions.DNSServers, func(it netip.Addr) bool {
144+
return it.Is4() == (family == unix.AF_INET)
145+
})
146+
if !dnsServer.IsValid() {
147+
if family == unix.AF_INET {
148+
dnsServer = r.tunOptions.Inet4Address[0].Addr().Next()
149+
} else {
150+
dnsServer = r.tunOptions.Inet6Address[0].Addr().Next()
151+
}
155152
}
156-
for _, uidRange := range r.tunOptions.IncludeUID {
153+
if len(r.tunOptions.IncludeInterface) > 0 || len(r.tunOptions.IncludeUID) > 0 {
154+
for _, name := range r.tunOptions.IncludeInterface {
155+
nft.AddRule(&nftables.Rule{
156+
Table: table,
157+
Chain: chainPreRouting,
158+
Exprs: nftablesRuleIfName(expr.MetaKeyIIFNAME, name, append(routeExprs, nftablesRuleHijackDNS(table.Family, dnsServer)...)...),
159+
})
160+
}
161+
for _, uidRange := range r.tunOptions.IncludeUID {
162+
nft.AddRule(&nftables.Rule{
163+
Table: table,
164+
Chain: chainPreRouting,
165+
Exprs: nftablesRuleMetaUInt32Range(expr.MetaKeySKUID, uidRange, append(routeExprs, nftablesRuleHijackDNS(table.Family, dnsServer)...)...),
166+
})
167+
}
168+
} else {
157169
nft.AddRule(&nftables.Rule{
158170
Table: table,
159171
Chain: chainPreRouting,
160-
Exprs: nftablesRuleMetaUInt32Range(expr.MetaKeySKUID, uidRange, append(routeExprs, nftablesRuleHijackDNS(table.Family, dnsServerAddress)...)...),
172+
Exprs: append(routeExprs, nftablesRuleHijackDNS(table.Family, dnsServer)...),
161173
})
162174
}
163-
} else {
164-
nft.AddRule(&nftables.Rule{
165-
Table: table,
166-
Chain: chainPreRouting,
167-
Exprs: append(routeExprs, nftablesRuleHijackDNS(table.Family, dnsServerAddress)...),
168-
})
169175
}
170176

171177
nft.AddRule(&nftables.Rule{

stack_gvisor_udp.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,6 @@ func (w *UDPBackWriter) WritePacket(packetBuffer *buf.Buffer, destination M.Sock
138138
TTL: route.DefaultTTL(),
139139
TOS: 0,
140140
}, packet)
141-
142141
if err != nil {
143142
route.Stats().UDP.PacketSendErrors.Increment()
144143
return wrapStackError(err)

tun.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ type Options struct {
4848
MTU uint32
4949
GSO bool
5050
AutoRoute bool
51+
DNSServers []netip.Addr
52+
IPRoute2RuleIndex int
5153
StrictRoute bool
5254
Inet4RouteAddress []netip.Prefix
5355
Inet6RouteAddress []netip.Prefix
@@ -67,6 +69,9 @@ type Options struct {
6769

6870
// No work for TCP, do not use.
6971
_TXChecksumOffload bool
72+
73+
// For library usages.
74+
EXP_DisableDNSHijack bool
7075
}
7176

7277
func CalculateInterfaceName(name string) (tunName string) {

tun_linux.go

Lines changed: 29 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -359,11 +359,6 @@ func (t *NativeTun) routes(tunLink netlink.Link) ([]netlink.Route, error) {
359359
}), nil
360360
}
361361

362-
const (
363-
ruleStart = 9000
364-
ruleEnd = ruleStart + 10
365-
)
366-
367362
func (t *NativeTun) nextIndex6() int {
368363
ruleList, err := netlink.RuleList(netlink.FAMILY_V6)
369364
if err != nil {
@@ -411,9 +406,14 @@ func (t *NativeTun) rules() []*netlink.Rule {
411406
var it *netlink.Rule
412407

413408
excludeRanges := t.options.ExcludedRanges()
409+
410+
ruleStart := t.options.IPRoute2RuleIndex
411+
if ruleStart == 0 {
412+
ruleStart = 9000
413+
}
414414
priority := ruleStart
415415
priority6 := priority
416-
nopPriority := ruleEnd
416+
nopPriority := ruleStart + 10
417417

418418
for _, excludeRange := range excludeRanges {
419419
if p4 {
@@ -788,6 +788,11 @@ func (t *NativeTun) unsetRules() error {
788788
return err
789789
}
790790
for _, rule := range ruleList {
791+
ruleStart := t.options.IPRoute2RuleIndex
792+
if ruleStart == 0 {
793+
ruleStart = 9000
794+
}
795+
ruleEnd := ruleStart + 10
791796
if rule.Priority >= ruleStart && rule.Priority <= ruleEnd {
792797
ruleToDel := netlink.NewRule()
793798
ruleToDel.Family = rule.Family
@@ -820,20 +825,28 @@ func (t *NativeTun) routeUpdate(event int) {
820825
}
821826

822827
func (t *NativeTun) setSearchDomainForSystemdResolved() {
828+
if t.options.EXP_DisableDNSHijack {
829+
return
830+
}
823831
ctlPath, err := exec.LookPath("resolvectl")
824832
if err != nil {
825833
return
826834
}
827-
var dnsServer []netip.Addr
828-
if len(t.options.Inet4Address) > 0 {
829-
dnsServer = append(dnsServer, t.options.Inet4Address[0].Addr().Next())
830-
}
831-
if len(t.options.Inet6Address) > 0 {
832-
dnsServer = append(dnsServer, t.options.Inet6Address[0].Addr().Next())
835+
dnsServer := t.options.DNSServers
836+
if len(dnsServer) == 0 {
837+
if len(t.options.Inet4Address) > 0 {
838+
dnsServer = append(dnsServer, t.options.Inet4Address[0].Addr().Next())
839+
}
840+
if len(t.options.Inet6Address) > 0 {
841+
dnsServer = append(dnsServer, t.options.Inet6Address[0].Addr().Next())
842+
}
833843
}
834-
go shell.Exec(ctlPath, "domain", t.options.Name, "~.").Run()
835-
if t.options.AutoRoute {
836-
go shell.Exec(ctlPath, "default-route", t.options.Name, "true").Run()
837-
go shell.Exec(ctlPath, append([]string{"dns", t.options.Name}, common.Map(dnsServer, netip.Addr.String)...)...).Run()
844+
if len(dnsServer) == 0 {
845+
return
838846
}
847+
go func() {
848+
_ = shell.Exec(ctlPath, "domain", t.options.Name, "~.").Run()
849+
_ = shell.Exec(ctlPath, "default-route", t.options.Name, "true").Run()
850+
_ = shell.Exec(ctlPath, append([]string{"dns", t.options.Name}, common.Map(dnsServer, netip.Addr.String)...)...).Run()
851+
}()
839852
}

0 commit comments

Comments
 (0)