@@ -10,40 +10,71 @@ import (
1010 "sync"
1111 "sync/atomic"
1212
13- "github.com/getlantern/flashlight/v7/sentry "
13+ "github.com/getlantern/dnstt "
1414 "github.com/getlantern/fronted"
1515 "github.com/getlantern/kindling"
16+
17+ "github.com/getlantern/flashlight/v7/sentry"
1618)
1719
18- var httpClient * http.Client
19- var mutex = & sync.Mutex {}
20-
21- // These are the domains we will access via kindling.
22- var domains = []string {
23- "api.iantem.io" ,
24- "api.getiantem.org" , // Still used on iOS
25- "geo.getiantem.org" , // Still used on iOS
26- "config.getiantem.org" , // Still used on iOS
27- "df.iantem.io" ,
28- "raw.githubusercontent.com" ,
29- "media.githubusercontent.com" ,
30- "objects.githubusercontent.com" ,
31- "replica-r2.lantern.io" ,
32- "replica-search.lantern.io" ,
33- "update.getlantern.org" ,
34- "globalconfig.flashlightproxy.com" ,
35- "dogsdogs.xyz" , // Used in replica
36- "service.dogsdogs.xyz" , // Used in replica
20+ var (
21+ httpClient * http.Client
22+ mutex = & sync.Mutex {}
23+
24+ // These are the domains we will access via kindling.
25+ domains = []string {
26+ "api.iantem.io" ,
27+ "api.getiantem.org" , // Still used on iOS
28+ "geo.getiantem.org" , // Still used on iOS
29+ "config.getiantem.org" , // Still used on iOS
30+ "df.iantem.io" ,
31+ "raw.githubusercontent.com" ,
32+ "media.githubusercontent.com" ,
33+ "objects.githubusercontent.com" ,
34+ "replica-r2.lantern.io" ,
35+ "replica-search.lantern.io" ,
36+ "update.getlantern.org" ,
37+ "globalconfig.flashlightproxy.com" ,
38+ "dogsdogs.xyz" , // Used in replica
39+ "service.dogsdogs.xyz" , // Used in replica
40+ }
41+
42+ configDir atomic.Value
43+ dnsttConfig atomic.Value // Holds the DNSTTConfig
44+
45+ defaultDNSTTConfig = & DNSTTConfig {
46+ Domain : "t.iantem.io" ,
47+ PublicKey : "405eb9e22d806e3a0a8e667c6665a321c8a6a35fa680ed814716a66d7ad84977" ,
48+ DoHResolver : "https://cloudflare-dns.com/dns-query" ,
49+ DoTResolver : "" ,
50+ UTLSDistribution : "" ,
51+ }
52+ )
53+
54+ type DNSTTConfig struct {
55+ Domain string `yaml:"domain"` // DNS tunnel domain, e.g., "t.iantem.io"
56+ PublicKey string `yaml:"publicKey"` // DNSTT server public key
57+ DoHResolver string `yaml:"dohResolver,omitempty"`
58+ DoTResolver string `yaml:"dotResolver,omitempty"`
59+ UTLSDistribution string `yaml:"utlsDistribution,omitempty"`
3760}
3861
39- var configDir atomic.Value
62+ func init () {
63+ dnsttConfig .Store (defaultDNSTTConfig )
64+ }
4065
4166// The config directory on some platforms, such as Android, can only be determined in native code, so we
4267// need to set it externally.
4368func SetConfigDir (dir string ) {
4469 configDir .Store (dir )
4570}
4671
72+ func SetDNSTTConfig (cfg * DNSTTConfig ) {
73+ if cfg != nil {
74+ dnsttConfig .Store (cfg )
75+ }
76+ }
77+
4778func GetHTTPClient () * http.Client {
4879 mutex .Lock ()
4980 defer mutex .Unlock ()
@@ -53,26 +84,25 @@ func GetHTTPClient() *http.Client {
5384
5485 var k kindling.Kindling
5586 ioWriter := log .AsDebugLogger ().Writer ()
87+ kOptions := []kindling.Option {
88+ kindling .WithPanicListener (sentry .PanicListener ),
89+ kindling .WithLogWriter (ioWriter ),
90+ kindling .WithProxyless (domains ... ),
91+ }
92+
5693 // Create new fronted instance.
5794 f , err := newFronted (ioWriter , sentry .PanicListener )
5895 if err != nil {
5996 log .Errorf ("Failed to create fronted instance: %v" , err )
60- k = kindling .NewKindling (
61- "flashlight" ,
62- kindling .WithPanicListener (sentry .PanicListener ),
63- kindling .WithLogWriter (ioWriter ),
64- kindling .WithProxyless (domains ... ),
65- )
6697 } else {
67- // Set the client to the kindling client.
68- k = kindling .NewKindling (
69- "flashlight" ,
70- kindling .WithPanicListener (sentry .PanicListener ),
71- kindling .WithLogWriter (ioWriter ),
72- kindling .WithDomainFronting (f ),
73- kindling .WithProxyless (domains ... ),
74- )
98+ kOptions = append (kOptions , kindling .WithDomainFronting (f ))
99+ }
100+ if d , err := newDNSTT (); err != nil {
101+ log .Errorf ("Failed to create DNSTT: %v" , err )
102+ } else {
103+ kOptions = append (kOptions , kindling .WithDNSTunnel (d ))
75104 }
105+ k = kindling .NewKindling ("flashlight" , kOptions ... )
76106 httpClient = k .NewHTTPClient ()
77107 return httpClient
78108}
@@ -110,3 +140,38 @@ func newFronted(logWriter io.Writer, panicListener func(string)) (fronted.Fronte
110140 fronted .WithConfigURL (configURL ),
111141 ), nil
112142}
143+
144+ func (c * DNSTTConfig ) Validate () error {
145+ if c .PublicKey == "" {
146+ return fmt .Errorf ("publicKey is required" )
147+ }
148+ if c .Domain == "" {
149+ return fmt .Errorf ("domain is required" )
150+ }
151+ if c .DoHResolver == "" && c .DoTResolver == "" {
152+ return fmt .Errorf ("at least one of DoHResolver or DoTResolver must be specified" )
153+ }
154+ return nil
155+ }
156+
157+ func newDNSTT () (dnstt.DNSTT , error ) {
158+ cfg := dnsttConfig .Load ().(* DNSTTConfig )
159+ if err := cfg .Validate (); err != nil {
160+ return nil , fmt .Errorf ("invalid DNSTT configuration: %w" , err )
161+ }
162+
163+ options := []dnstt.Option {
164+ dnstt .WithPublicKey (cfg .PublicKey ),
165+ dnstt .WithTunnelDomain (cfg .Domain ),
166+ }
167+ switch {
168+ case cfg .DoHResolver != "" :
169+ options = append (options , dnstt .WithDoH (cfg .DoHResolver ))
170+ case cfg .DoTResolver != "" :
171+ options = append (options , dnstt .WithDoT (cfg .DoTResolver ))
172+ }
173+ if cfg .UTLSDistribution != "" {
174+ options = append (options , dnstt .WithUTLSDistribution (cfg .UTLSDistribution ))
175+ }
176+ return dnstt .NewDNSTT (options ... )
177+ }
0 commit comments