Skip to content

Commit 26d1810

Browse files
committed
dnsx: choose fixed transport internally
Preserve uids for all internally originated dns requests. Choose fixed transport depending on whether alg translations and split dns are both turned on and the uid is either invalid or of known android components that are known to proxy dns requests on app's behalf.
1 parent da6ed17 commit 26d1810

14 files changed

Lines changed: 105 additions & 55 deletions

File tree

intra/backend/dnsx.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -145,8 +145,8 @@ type RDNSResolver interface {
145145
SetRdnsRemote(filetag string) error
146146
// GetRdnsRemote returns the remote rdns resolver.
147147
GetRdnsRemote() (RDNS, error)
148-
// Translate enables or disables ALG/fixed responses
149-
Translate(bool)
148+
// Translate enables or disables ALG and fixed responses.
149+
Translate(alg, fix bool)
150150
}
151151

152152
type DNSTransportMultProvider interface {

intra/common.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ const (
3636
smmchSize = 256 // some comfortably high number
3737
UNKNOWN_UID = core.UNKNOWN_UID
3838
UNKNOWN_UID_STR = core.UNKNOWN_UID_STR
39+
ANDROID_UID_STR = core.ANDROID_UID_STR
3940
SELF_UID = protect.UidSelf
4041
UNSUPPORTED_NETWORK = core.UNSUPPORTED_NETWORK
4142
)

intra/core/proto.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,11 @@ import (
1515

1616
// from: github.com/eycorsican/go-tun2socks/blob/301549c435/core/conn.go#LL3C9-L3C9
1717

18+
// ref: cs.android.com/android/platform/superproject/+/android-latest-release:system/core/libcutils/include/private/android_filesystem_config.h;drc=e999f05f34e91a3a313ba7dd77bcf52b58a0841e
1819
const (
1920
UNKNOWN_UID = -1
2021
UNKNOWN_UID_STR = "-1"
22+
ANDROID_UID_STR = "0"
2123
DNS_UID_STR = "1051"
2224
UNSUPPORTED_NETWORK = -1
2325
)

intra/dialers/dns.go

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
"net/url"
1313

1414
"github.com/celzero/firestack/intra/core"
15+
"github.com/celzero/firestack/intra/protect"
1516
"github.com/celzero/firestack/intra/xdns"
1617
"github.com/miekg/dns"
1718
)
@@ -70,7 +71,7 @@ func ECH(hostname string) ([]byte, error) {
7071
if err != nil {
7172
return nil, err
7273
}
73-
res, err := ipm.Lookup(q)
74+
res, err := ipm.LocalLookup(q)
7475
if err != nil {
7576
return nil, err
7677
}
@@ -100,7 +101,7 @@ func Query(msg *dns.Msg, tids ...string) (*dns.Msg, error) {
100101
return nil, err
101102
}
102103

103-
r, err := ipm.Lookup(q, tids...)
104+
r, err := ipm.Lookup(q, protect.UidSelf, tids...)
104105
if err != nil {
105106
return nil, err
106107
}
@@ -120,13 +121,8 @@ func QueryFor(msg *dns.Msg, uid, tid string) (*dns.Msg, error) {
120121
return nil, qerr
121122
}
122123

123-
var r []byte
124-
var rerr error
125-
if uid == core.UNKNOWN_UID_STR {
126-
r, rerr = ipm.Lookup(q, tid)
127-
} else {
128-
r, rerr = ipm.LookupFor(q, uid)
129-
}
124+
// uid may be core.UNKNOWN_UID_STR
125+
r, rerr := ipm.Lookup(q, uid, tid)
130126

131127
if rerr != nil {
132128
return nil, rerr

intra/dns53/dot_test.go

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,11 @@ type fakeResolver struct {
3636
*net.Resolver
3737
}
3838

39-
func (r fakeResolver) Lookup(q []byte, _ ...string) ([]byte, error) {
39+
func (r fakeResolver) LocalLookup(q []byte) ([]byte, error) {
40+
return r.Lookup(q, protect.UidSelf)
41+
}
42+
43+
func (r fakeResolver) Lookup(q []byte, _ string, _ ...string) ([]byte, error) {
4044
// return nil, errors.New("lookup: not implemented")
4145
msg := xdns.AsMsg(q)
4246
if msg == nil {
@@ -75,7 +79,7 @@ func (r fakeResolver) Lookup(q []byte, _ ...string) ([]byte, error) {
7579
}
7680

7781
func (r fakeResolver) LookupFor(q []byte, _ string) ([]byte, error) {
78-
return r.Lookup(q)
82+
return r.LocalLookup(q)
7983
}
8084

8185
func (r fakeResolver) LookupNetIP(ctx context.Context, network, host string) ([]netip.Addr, error) {
@@ -119,7 +123,7 @@ var (
119123
autoNsOpts = &x.DNSOpts{PIDCSV: x.RpnWin, IPCSV: "", TIDCSV: x.CT + "test0"}
120124
)
121125

122-
func (*fakeBdg) OnQuery(_, _ *x.Gostr, _ int) *x.DNSOpts { return autoNsOpts }
126+
func (*fakeBdg) OnQuery(_, _, _ *x.Gostr, _ int) *x.DNSOpts { return autoNsOpts }
123127
func (*fakeBdg) OnUpstreamAnswer(_ *x.DNSSummary, _ *x.Gostr) *x.DNSOpts { return nil }
124128
func (*fakeBdg) OnResponse(*x.DNSSummary) {}
125129
func (*fakeBdg) OnDNSAdded(*x.Gostr) {}
@@ -168,12 +172,12 @@ func TestDot(t *testing.T) {
168172
natpt := x64.NewNatPt()
169173
resolv := dnsx.NewResolver(ctx, "10.111.222.3:53", dtr, bdg, natpt)
170174
resolv.Add(tr)
171-
r4, _, err := resolv.Lookup(b4)
172-
r6, _, err6 := resolv.Lookup(b6)
173-
_, _, _ = resolv.Lookup(b24)
174-
_, _, _ = resolv.Lookup(b26)
175+
r4, _, err := resolv.LocalLookup(b4)
176+
r6, _, err6 := resolv.LocalLookup(b6)
177+
_, _, _ = resolv.LocalLookup(b24)
178+
_, _, _ = resolv.LocalLookup(b26)
175179
time.Sleep(1 * time.Second)
176-
_, _, _ = resolv.Lookup(b6)
180+
_, _, _ = resolv.LocalLookup(b6)
177181
if err != nil {
178182
// log.Output(2, smm.Str())
179183
t.Fatal(err)
@@ -286,8 +290,8 @@ func TestSEProxy(t *testing.T) {
286290
b4, _ := q.Pack()
287291
b6, _ := q6.Pack()
288292

289-
r4, _, err := resolv.Lookup(b4)
290-
r6, _, err6 := resolv.Lookup(b6)
293+
r4, _, err := resolv.LocalLookup(b4)
294+
r6, _, err6 := resolv.LocalLookup(b6)
291295
if err != nil {
292296
// log.Output(2, smm.Str())
293297
t.Fatal(err)
@@ -385,7 +389,7 @@ func TestWgReaches(t *testing.T) {
385389
}*/
386390
ilog.VV("-----------------------DNSX--------------------------")
387391
b4, _ := aquery("skysports.com").Pack()
388-
r4, _, err := resolv.Lookup(b4) // must use "test0"
392+
r4, _, err := resolv.LocalLookup(b4) // must use "test0"
389393

390394
ilog.D("testwg: %v", win.Router().Stat())
391395
time.Sleep(2 * time.Second)
@@ -509,7 +513,7 @@ func TestWinReaches(t *testing.T) {
509513
}*/
510514
ilog.VV("-----------------------DNSX--------------------------")
511515
b4, _ := aquery("skysports.com").Pack()
512-
r4, _, err := resolv.Lookup(b4) // must use "test0"
516+
r4, _, err := resolv.LocalLookup(b4) // must use "test0"
513517

514518
ilog.D("%v", propx2.Router().Stat())
515519
time.Sleep(2 * time.Second)

intra/dns53/ipmapper.go

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -74,8 +74,13 @@ func str2ip(host string) (netip.Addr, error) {
7474
}
7575

7676
// Implements IPMapper.
77-
func (m *ipmapper) Lookup(q []byte, tids ...string) ([]byte, error) {
78-
return m.queryAny(q, tids...)
77+
func (m *ipmapper) LocalLookup(q []byte) ([]byte, error) {
78+
return m.Lookup(q, protect.UidSelf)
79+
}
80+
81+
// Implements IPMapper.
82+
func (m *ipmapper) Lookup(q []byte, uid string, tids ...string) ([]byte, error) {
83+
return m.queryAny2(q, uid, tids...)
7984
}
8085

8186
// Implements IPMapper.
@@ -150,8 +155,8 @@ func (m *ipmapper) queryIP2(_ context.Context, network, host, uid string, tid ..
150155

151156
var val4, val6 *core.V[answer, string]
152157
if len(tid) > 0 { // always choose one among these tids
153-
val4, _ = m.ba.Do(key4(host, tid...), m.lookupon(q4, tid...))
154-
val6, _ = m.ba.Do(key6(host, tid...), m.lookupon(q6, tid...))
158+
val4, _ = m.ba.Do(key4(host, tid...), m.lookupon(q4, uid, tid...))
159+
val6, _ = m.ba.Do(key6(host, tid...), m.lookupon(q6, uid, tid...))
155160
} else if uid != core.UNKNOWN_UID_STR { // client code chooses a tid depending on uid & "origin"
156161
val4, _ = m.ba.Do(key4(host, uid), m.lookupfor(q4, uid))
157162
val6, _ = m.ba.Do(key6(host, uid), m.lookupfor(q6, uid))
@@ -227,7 +232,7 @@ func (m *ipmapper) queryAny2(q []byte, uid string, tids ...string) ([]byte, erro
227232

228233
var v *core.V[answer, string]
229234
if len(tids) > 0 {
230-
v, _ = m.ba.Do(key(qname, qtypestr, tids...), m.lookupon(q, tids...))
235+
v, _ = m.ba.Do(key(qname, qtypestr, tids...), m.lookupon(q, uid, tids...))
231236
} else if uid != core.UNKNOWN_UID_STR {
232237
v, _ = m.ba.Do(key(qname, qtypestr, uid), m.lookupfor(q, uid))
233238
} else {
@@ -257,10 +262,10 @@ func (m *ipmapper) lookupfor(q []byte, uid string) func() (answer, error) {
257262
// lookupon always resolves on one of the chosen tids
258263
// (if empty, it may or may not use dnsx.Default;
259264
// see: dnsx.transport.go:determineTransport)
260-
func (m *ipmapper) lookupon(q []byte, tids ...string) func() (answer, error) {
265+
func (m *ipmapper) lookupon(q []byte, uid string, tids ...string) func() (answer, error) {
261266
return func() (answer, error) {
262-
a, tid, err := m.r.Lookup(q, tids...)
263-
return answer{a, tid, core.UNKNOWN_UID_STR}, err
267+
a, tid, err := m.r.LookupFor2(q, uid, tids...)
268+
return answer{a, tid, uid}, err
264269
}
265270
}
266271

intra/dnscrypt/servers_test.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,11 @@ type fakeResolver struct {
6767
*net.Resolver
6868
}
6969

70-
func (r fakeResolver) Lookup([]byte, ...string) ([]byte, error) {
70+
func (r fakeResolver) LocalLookup([]byte) ([]byte, error) {
71+
return nil, errors.New("not implemented")
72+
}
73+
74+
func (r fakeResolver) Lookup([]byte, string, ...string) ([]byte, error) {
7175
return nil, errors.New("not implemented")
7276
}
7377

intra/dnsx/alg.go

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -90,9 +90,11 @@ type Gateway interface {
9090
// given an alg or real ip, retrieve assoc blocklists as csv, if any
9191
RDNSBL(maybeAlg netip.Addr) (blocklistcsv string)
9292
// translate overwrites ip answers to alg ip & fixed ip answers
93-
translate(yes bool)
93+
translate(tr, fix bool)
9494
// splitTunnel sets per-app split tunneling on/off
9595
splitTunnel()
96+
// fixedTransport returns true if split tunneling is enforced via dnsx.Fixed
97+
fixedTransport() bool
9698
// Query using t1 as primary transport and t2 as secondary and preset as pre-determined ip answers
9799
q(t1, t2 Transport, preset []netip.Addr, network, uid string, q *dns.Msg, s *x.DNSSummary) (og *dns.Msg, algd *dns.Msg, err error)
98100
// onStopped is called when a transport tid is stopped. Gateway invalidates its local caches, if any.
@@ -870,6 +872,7 @@ type dnsgateway struct {
870872
// fields below are mutable
871873

872874
mod atomic.Bool // modify realip to algip
875+
fix atomic.Bool // enforce split tunneling via dnsx.Fixed
873876
split atomic.Bool // per-app split tunneling
874877
}
875878

@@ -898,9 +901,12 @@ func NewDNSGateway(pctx context.Context, fakeaddrs []netip.AddrPort, outer RdnsR
898901
return
899902
}
900903

901-
func (t *dnsgateway) translate(yes bool) {
902-
prev := t.mod.Swap(yes)
903-
log.I("alg: translate? prev(%t) > now(%t)", prev, yes)
904+
func (t *dnsgateway) translate(tr, fix bool) {
905+
// fixed transport can only be used if translation is on
906+
fix = tr && fix
907+
prevtr := t.mod.Swap(tr)
908+
prevfix := t.fix.Swap(fix)
909+
log.I("alg: translate? prevtr(%t) > nowtr(%t); prevfix(%t) > nowfix(%t)", prevtr, tr, prevfix, fix)
904910
}
905911

906912
func (t *dnsgateway) splitTunnel() {
@@ -911,6 +917,10 @@ func (t *dnsgateway) splitTunnel() {
911917
log.I("alg: splitTunnel turned on")
912918
}
913919

920+
func (t *dnsgateway) fixedTransport() bool {
921+
return t.mod.Load() && t.split.Load() && t.fix.Load()
922+
}
923+
914924
func (t *dnsgateway) onStopped(tid string) {
915925
if len(tid) <= 0 {
916926
return
@@ -1773,8 +1783,10 @@ func (t *dnsgateway) S() string {
17731783
sb.WriteString("dnsgateway state:\n")
17741784
sb.WriteString(" mod: ")
17751785
sb.WriteString(strconv.FormatBool(t.mod.Load()))
1776-
sb.WriteString(" / split: ")
1786+
sb.WriteString(" / cansplit: ")
17771787
sb.WriteString(strconv.FormatBool(t.split.Load()))
1788+
sb.WriteString(" / wantsplit: ")
1789+
sb.WriteString(strconv.FormatBool(t.fixedTransport()))
17781790
sb.WriteString(" / chash: ")
17791791
sb.WriteString(strconv.FormatBool(t.chash))
17801792
sb.WriteString(" / adv: ")

intra/dnsx/rethinkdns.go

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -73,11 +73,10 @@ type ResolverSelf interface {
7373
// LocalLookup performs resolution on Default and/or Goos DNSes.
7474
// To be only used by protect.UidSelf.
7575
LocalLookup(q []byte) (a []byte, tid string, err error)
76-
// Lookup performs resolution on chosen Transport.
77-
// To be only used by protect.UidSelf.
78-
Lookup(q []byte, chosen ...string) (a []byte, tid string, err error)
7976
// LookupFor performs resolution for uid.
8077
LookupFor(q []byte, uid string) (a []byte, tid string, err error)
78+
// Lookup performs resolution on chosen Transport.
79+
LookupFor2(q []byte, uid string, chosen ...string) (a []byte, tid string, err error)
8180
}
8281

8382
type RDNS interface {

intra/dnsx/transport.go

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -285,8 +285,8 @@ func (r *resolver) MDNS() (MDNSTransport, error) {
285285
return nil, errNoSuchTransport
286286
}
287287

288-
func (r *resolver) Translate(b bool) {
289-
r.gateway.translate(b)
288+
func (r *resolver) Translate(tr, fix bool) {
289+
r.gateway.translate(tr, fix)
290290
}
291291

292292
// stopIfExistsLocked stops the transport if it exists,
@@ -467,21 +467,29 @@ func (r *resolver) IsDnsAddr(ipport netip.AddrPort) bool {
467467
return r.isDns(ipport)
468468
}
469469

470-
// Lookup implements ResolverSelf.
471-
func (r *resolver) Lookup(q []byte, tids ...string) ([]byte, string, error) {
470+
// LookupFor2 implements ResolverSelf.
471+
func (r *resolver) LookupFor2(q []byte, uid string, tids ...string) ([]byte, string, error) {
472472
if len(q) <= 0 {
473473
return nil, NoDNS, errNoQuestion
474474
}
475475
// if len(tids) == 0, use transport from preferences
476-
return r.forward(q, OriginInternal, protect.UidSelf, tids...)
476+
return r.forward(q, OriginInternal, uid, tids...)
477477
}
478478

479-
// LookupForSelf implements ResolverSelf.
479+
// LookupFor implements ResolverSelf.
480480
func (r *resolver) LookupFor(q []byte, uid string) ([]byte, string, error) {
481481
if len(q) <= 0 {
482482
return nil, NoDNS, errNoQuestion
483483
}
484484

485+
// prechose tids preferred & fixed when uid is set to 0, -1, or 1051 (common
486+
// android system components that send DNS requests on behalf of actual apps/uids)
487+
// to use "fixed" transport to later uncover the actual requesting app/uid during
488+
// tcp/udp flows (specifically, with preflow)
489+
if (uid == core.UNKNOWN_UID_STR || uid == core.DNS_UID_STR || uid == core.ANDROID_UID_STR) && r.gateway.fixedTransport() {
490+
return r.forward(q, OriginInternal, uid, Preferred, Fixed)
491+
}
492+
485493
return r.forward(q, OriginInternal, uid)
486494
}
487495

@@ -495,7 +503,7 @@ func (r *resolver) LocalLookup(q []byte) ([]byte, string, error) {
495503
defaultIsSystemDNS := r.isDefaultSystemDNS()
496504

497505
// including dns64 and/or alg
498-
ans, tid, err := r.forward(q, protect.UidSelf, Default)
506+
ans, tid, err := r.forward(q, OriginInternal, protect.UidSelf, Default)
499507
if !defaultIsSystemDNS || loopingBack {
500508
return ans, tid, err
501509
} // else: retry with Goos/System, if needed
@@ -735,7 +743,7 @@ func (r *resolver) Serve(proto string, c protect.Conn, uid string) {
735743
// if Serve (which is called by common.go:dnsOverride) calls in with a uid
736744
// that is not UNKNOWN_UID_STR, then we know that the query is from an app
737745
// and we can presume per app split tunnel is working as expected.
738-
if len(uid) > 0 && uid != core.UNKNOWN_UID_STR && uid != core.DNS_UID_STR {
746+
if len(uid) > 0 && uid != core.ANDROID_UID_STR && uid != core.UNKNOWN_UID_STR && uid != core.DNS_UID_STR {
739747
r.gateway.splitTunnel()
740748
}
741749

0 commit comments

Comments
 (0)