Skip to content

Commit

Permalink
Make DNS hijack optional
Browse files Browse the repository at this point in the history
  • Loading branch information
nekohasekai committed Jun 1, 2024
1 parent 66e34ab commit 831469a
Show file tree
Hide file tree
Showing 7 changed files with 154 additions and 112 deletions.
63 changes: 35 additions & 28 deletions redirect_iptables.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"os/exec"
"strings"

"github.com/sagernet/sing/common"
E "github.com/sagernet/sing/common/exceptions"
F "github.com/sagernet/sing/common/format"

Expand Down Expand Up @@ -109,42 +110,48 @@ func (r *autoRedirect) setupIPTables(family int) error {
return err
}
}
var dnsServerAddress netip.Addr
if family == unix.AF_INET {
dnsServerAddress = r.tunOptions.Inet4Address[0].Addr().Next()
} else {
dnsServerAddress = r.tunOptions.Inet6Address[0].Addr().Next()
}
if len(routeAddress) > 0 {
for _, address := range routeAddress {
err = r.runShell(iptablesPath, "-t nat -A", tableNamePreRouteing,
"-d", address.String(), "-p udp --dport 53 -j DNAT --to", dnsServerAddress)
if err != nil {
return err
if !r.tunOptions.EXP_DisableDNSHijack {
dnsServer := common.Find(r.tunOptions.DNSServers, func(it netip.Addr) bool {
return it.Is4() == (family == unix.AF_INET)
})
if !dnsServer.IsValid() {
if family == unix.AF_INET {
dnsServer = r.tunOptions.Inet4Address[0].Addr().Next()
} else {
dnsServer = r.tunOptions.Inet6Address[0].Addr().Next()
}
}
} else if len(r.tunOptions.IncludeInterface) > 0 || len(r.tunOptions.IncludeUID) > 0 {
for _, name := range r.tunOptions.IncludeInterface {
err = r.runShell(iptablesPath, "-t nat -A", tableNamePreRouteing,
"-i", name, "-p udp --dport 53 -j DNAT --to", dnsServerAddress)
if err != nil {
return err
if len(routeAddress) > 0 {
for _, address := range routeAddress {
err = r.runShell(iptablesPath, "-t nat -A", tableNamePreRouteing,
"-d", address.String(), "-p udp --dport 53 -j DNAT --to", dnsServer)
if err != nil {
return err
}
}
}
for _, uidRange := range r.tunOptions.IncludeUID {
for uid := uidRange.Start; uid <= uidRange.End; uid++ {
} else if len(r.tunOptions.IncludeInterface) > 0 || len(r.tunOptions.IncludeUID) > 0 {
for _, name := range r.tunOptions.IncludeInterface {
err = r.runShell(iptablesPath, "-t nat -A", tableNamePreRouteing,
"-m owner --uid-owner", uid, "-p udp --dport 53 -j DNAT --to", dnsServerAddress)
"-i", name, "-p udp --dport 53 -j DNAT --to", dnsServer)
if err != nil {
return err
}
}
}
} else {
err = r.runShell(iptablesPath, "-t nat -A", tableNamePreRouteing,
"-p udp --dport 53 -j DNAT --to", dnsServerAddress)
if err != nil {
return err
for _, uidRange := range r.tunOptions.IncludeUID {
for uid := uidRange.Start; uid <= uidRange.End; uid++ {
err = r.runShell(iptablesPath, "-t nat -A", tableNamePreRouteing,
"-m owner --uid-owner", uid, "-p udp --dport 53 -j DNAT --to", dnsServer)
if err != nil {
return err
}
}
}
} else {
err = r.runShell(iptablesPath, "-t nat -A", tableNamePreRouteing,
"-p udp --dport 53 -j DNAT --to", dnsServer)
if err != nil {
return err
}
}
}

Expand Down
10 changes: 6 additions & 4 deletions redirect_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,16 @@ package tun

import (
"context"
"net/netip"
"os"
"os/exec"
"runtime"

"github.com/sagernet/nftables"
"github.com/sagernet/sing/common"
E "github.com/sagernet/sing/common/exceptions"
"github.com/sagernet/sing/common/logger"
M "github.com/sagernet/sing/common/metadata"
"net/netip"
"os"
"os/exec"
"runtime"

"golang.org/x/sys/unix"
)
Expand Down Expand Up @@ -39,6 +40,7 @@ func NewAutoRedirect(options AutoRedirectOptions) (AutoRedirect, error) {
ctx: options.Context,
handler: options.Handler,
logger: options.Logger,
tableName: options.TableName,
useNFTables: runtime.GOOS != "android" && !options.DisableNFTables,
customRedirectPortFunc: options.CustomRedirectPort,
}
Expand Down
50 changes: 28 additions & 22 deletions redirect_nftables.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"github.com/sagernet/nftables"
"github.com/sagernet/nftables/binaryutil"
"github.com/sagernet/nftables/expr"
"github.com/sagernet/sing/common"
F "github.com/sagernet/sing/common/format"

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

var dnsServerAddress netip.Addr
if table.Family == nftables.TableFamilyIPv4 {
dnsServerAddress = r.tunOptions.Inet4Address[0].Addr().Next()
} else {
dnsServerAddress = r.tunOptions.Inet6Address[0].Addr().Next()
}

if len(r.tunOptions.IncludeInterface) > 0 || len(r.tunOptions.IncludeUID) > 0 {
for _, name := range r.tunOptions.IncludeInterface {
nft.AddRule(&nftables.Rule{
Table: table,
Chain: chainPreRouting,
Exprs: nftablesRuleIfName(expr.MetaKeyIIFNAME, name, append(routeExprs, nftablesRuleHijackDNS(table.Family, dnsServerAddress)...)...),
})
if !r.tunOptions.EXP_DisableDNSHijack {
dnsServer := common.Find(r.tunOptions.DNSServers, func(it netip.Addr) bool {
return it.Is4() == (family == unix.AF_INET)
})
if !dnsServer.IsValid() {
if family == unix.AF_INET {
dnsServer = r.tunOptions.Inet4Address[0].Addr().Next()
} else {
dnsServer = r.tunOptions.Inet6Address[0].Addr().Next()
}
}
for _, uidRange := range r.tunOptions.IncludeUID {
if len(r.tunOptions.IncludeInterface) > 0 || len(r.tunOptions.IncludeUID) > 0 {
for _, name := range r.tunOptions.IncludeInterface {
nft.AddRule(&nftables.Rule{
Table: table,
Chain: chainPreRouting,
Exprs: nftablesRuleIfName(expr.MetaKeyIIFNAME, name, append(routeExprs, nftablesRuleHijackDNS(table.Family, dnsServer)...)...),
})
}
for _, uidRange := range r.tunOptions.IncludeUID {
nft.AddRule(&nftables.Rule{
Table: table,
Chain: chainPreRouting,
Exprs: nftablesRuleMetaUInt32Range(expr.MetaKeySKUID, uidRange, append(routeExprs, nftablesRuleHijackDNS(table.Family, dnsServer)...)...),
})
}
} else {
nft.AddRule(&nftables.Rule{
Table: table,
Chain: chainPreRouting,
Exprs: nftablesRuleMetaUInt32Range(expr.MetaKeySKUID, uidRange, append(routeExprs, nftablesRuleHijackDNS(table.Family, dnsServerAddress)...)...),
Exprs: append(routeExprs, nftablesRuleHijackDNS(table.Family, dnsServer)...),
})
}
} else {
nft.AddRule(&nftables.Rule{
Table: table,
Chain: chainPreRouting,
Exprs: append(routeExprs, nftablesRuleHijackDNS(table.Family, dnsServerAddress)...),
})
}

nft.AddRule(&nftables.Rule{
Expand Down
1 change: 0 additions & 1 deletion stack_gvisor_udp.go
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,6 @@ func (w *UDPBackWriter) WritePacket(packetBuffer *buf.Buffer, destination M.Sock
TTL: route.DefaultTTL(),
TOS: 0,
}, packet)

if err != nil {
route.Stats().UDP.PacketSendErrors.Increment()
return wrapStackError(err)
Expand Down
5 changes: 5 additions & 0 deletions tun.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ type Options struct {
MTU uint32
GSO bool
AutoRoute bool
DNSServers []netip.Addr
IPRoute2RuleIndex int
StrictRoute bool
Inet4RouteAddress []netip.Prefix
Inet6RouteAddress []netip.Prefix
Expand All @@ -67,6 +69,9 @@ type Options struct {

// No work for TCP, do not use.
_TXChecksumOffload bool

// For library usages.
EXP_DisableDNSHijack bool
}

func CalculateInterfaceName(name string) (tunName string) {
Expand Down
45 changes: 29 additions & 16 deletions tun_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -359,11 +359,6 @@ func (t *NativeTun) routes(tunLink netlink.Link) ([]netlink.Route, error) {
}), nil
}

const (
ruleStart = 9000
ruleEnd = ruleStart + 10
)

func (t *NativeTun) nextIndex6() int {
ruleList, err := netlink.RuleList(netlink.FAMILY_V6)
if err != nil {
Expand Down Expand Up @@ -411,9 +406,14 @@ func (t *NativeTun) rules() []*netlink.Rule {
var it *netlink.Rule

excludeRanges := t.options.ExcludedRanges()

ruleStart := t.options.IPRoute2RuleIndex
if ruleStart == 0 {
ruleStart = 9000
}
priority := ruleStart
priority6 := priority
nopPriority := ruleEnd
nopPriority := ruleStart + 10

for _, excludeRange := range excludeRanges {
if p4 {
Expand Down Expand Up @@ -788,6 +788,11 @@ func (t *NativeTun) unsetRules() error {
return err
}
for _, rule := range ruleList {
ruleStart := t.options.IPRoute2RuleIndex
if ruleStart == 0 {
ruleStart = 9000
}
ruleEnd := ruleStart + 10
if rule.Priority >= ruleStart && rule.Priority <= ruleEnd {
ruleToDel := netlink.NewRule()
ruleToDel.Family = rule.Family
Expand Down Expand Up @@ -820,20 +825,28 @@ func (t *NativeTun) routeUpdate(event int) {
}

func (t *NativeTun) setSearchDomainForSystemdResolved() {
if t.options.EXP_DisableDNSHijack {
return
}
ctlPath, err := exec.LookPath("resolvectl")
if err != nil {
return
}
var dnsServer []netip.Addr
if len(t.options.Inet4Address) > 0 {
dnsServer = append(dnsServer, t.options.Inet4Address[0].Addr().Next())
}
if len(t.options.Inet6Address) > 0 {
dnsServer = append(dnsServer, t.options.Inet6Address[0].Addr().Next())
dnsServer := t.options.DNSServers
if len(dnsServer) == 0 {
if len(t.options.Inet4Address) > 0 {
dnsServer = append(dnsServer, t.options.Inet4Address[0].Addr().Next())
}
if len(t.options.Inet6Address) > 0 {
dnsServer = append(dnsServer, t.options.Inet6Address[0].Addr().Next())
}
}
go shell.Exec(ctlPath, "domain", t.options.Name, "~.").Run()
if t.options.AutoRoute {
go shell.Exec(ctlPath, "default-route", t.options.Name, "true").Run()
go shell.Exec(ctlPath, append([]string{"dns", t.options.Name}, common.Map(dnsServer, netip.Addr.String)...)...).Run()
if len(dnsServer) == 0 {
return
}
go func() {
_ = shell.Exec(ctlPath, "domain", t.options.Name, "~.").Run()
_ = shell.Exec(ctlPath, "default-route", t.options.Name, "true").Run()
_ = shell.Exec(ctlPath, append([]string{"dns", t.options.Name}, common.Map(dnsServer, netip.Addr.String)...)...).Run()
}()
}
Loading

0 comments on commit 831469a

Please sign in to comment.