@@ -10,6 +10,7 @@ import (
1010 "path/filepath"
1111 "regexp"
1212 "sort"
13+ "strings"
1314 "sync/atomic"
1415 "time"
1516
@@ -18,6 +19,7 @@ import (
1819 "github.com/getlantern/appdir"
1920 "github.com/getlantern/fronted"
2021 "github.com/getlantern/golog"
22+ "github.com/getlantern/jibber_jabber"
2123 "github.com/getlantern/launcher"
2224 "github.com/getlantern/proxiedsites"
2325 "github.com/getlantern/yaml"
4143 m * yamlconf.Manager
4244 lastCloudConfigETag = map [string ]string {}
4345 httpClient atomic.Value
44- r = regexp .MustCompile ("\\ d+" )
46+ r = regexp .MustCompile ("\\ d+\\ . \\ d+ " )
4547)
4648
4749type Config struct {
@@ -98,35 +100,51 @@ func majorVersion(version string) string {
98100// copyNewest is a one-time function for using older config files in the 2.x series.
99101// from 2.0.2 forward, Lantern will consider all major versions to be compatible and
100102// will name them accordingly, as in "lantern-2.yaml".
101- func copyNewest (file string ) {
103+ func copyNewest (file string , existsFunc func ( file string ) ( string , bool )) string {
102104 // If we already have a config file with the latest name, use that one.
103105 // Otherwise, copy the most recent config file available.
104- cur , exists := configExists (file )
105-
106+ cur , exists := existsFunc (file )
106107 if exists {
107- return
108+ log .Debugf ("Using existing config" )
109+ return cur
108110 }
109111 files := []string {"lantern-2.0.1.yaml" , "lantern-2.0.0+stable.yaml" , "lantern-2.0.0+manoto.yaml" , "lantern-2.0.0-beta8.yaml" }
110112
111113 for _ , file := range files {
112- if path , exists := configExists (file ); exists {
114+ if path , exists := existsFunc (file ); exists {
113115 if err := os .Rename (path , cur ); err != nil {
114116 log .Errorf ("Could not rename file from %v to %v: %v" , path , cur , err )
115117 } else {
116- return
118+ log .Debugf ("Copied old config at %v to %v" , path , cur )
119+ return path
117120 }
118121 }
119122 }
123+ return cur
120124}
121125
122126// Init initializes the configuration system.
123- func Init (version string ) (* Config , error ) {
127+ func Init (version string ) (* Config , error , string ) {
128+ path , settings , err := client .ReadSettings ()
129+ if err != nil {
130+ // Let packaged itself log errors as necessary.
131+ // This could happen if we're auto-updated from an older version that didn't
132+ // have packaged settings, for example.
133+ log .Debugf ("Could not read yaml from %v: %v" , path , err )
134+
135+ }
124136 file := "lantern-" + majorVersion (version ) + ".yaml"
125- copyNewest (file )
137+ // copyNewest(file, configExists )
126138 configPath , err := InConfigDir (file )
127139 if err != nil {
128140 log .Errorf ("Could not get config path? %v" , err )
129- return nil , err
141+ return nil , err , ""
142+ }
143+
144+ // If there's no configuration at the designated configuration path, download it
145+ // using the embedded servers.
146+ if _ , err := os .Stat (configPath ); os .IsNotExist (err ) {
147+ fetchInitialConfig (configPath , settings )
130148 }
131149 m = & yamlconf.Manager {
132150 FilePath : configPath ,
@@ -152,7 +170,7 @@ func Init(version string) (*Config, error) {
152170 }
153171
154172 var bytes []byte
155- if bytes , err = cfg .fetchCloudConfig (); err == nil {
173+ if bytes , err = cfg .fetchCloudConfig (httpClient . Load ().( * http. Client ) ); err == nil {
156174 // bytes will be nil if the config is unchanged (not modified)
157175 if bytes != nil {
158176 mutate = func (ycfg yamlconf.Config ) error {
@@ -173,10 +191,14 @@ func Init(version string) (*Config, error) {
173191 cfg = initial .(* Config )
174192 err = updateGlobals (cfg )
175193 if err != nil {
176- return nil , err
194+ return nil , err , ""
177195 }
178196 }
179- return cfg , err
197+ if settings != nil {
198+ return cfg , err , settings .StartupUrl
199+ } else {
200+ return cfg , err , ""
201+ }
180202}
181203
182204// Run runs the configuration system.
@@ -216,7 +238,7 @@ func InConfigDir(filename string) (string, error) {
216238 cdir = appdir .General ("Lantern" )
217239 }
218240
219- log .Debugf ("Placing configuration in %v" , cdir )
241+ log .Debugf ("Using config dir %v" , cdir )
220242 if _ , err := os .Stat (cdir ); err != nil {
221243 if os .IsNotExist (err ) {
222244 // Create config dir
@@ -309,6 +331,28 @@ func (cfg *Config) ApplyDefaults() {
309331 }
310332}
311333
334+ func defaultRoundRobin () string {
335+ localeTerritory , err := jibber_jabber .DetectTerritory ()
336+ if err != nil {
337+ localeTerritory = "us"
338+ }
339+ log .Debugf ("Locale territory: %v" , localeTerritory )
340+ return defaultRoundRobinForTerritory (localeTerritory )
341+ }
342+
343+ // defaultDataCenter customizes the default data center depending on the user's locale.
344+ func defaultRoundRobinForTerritory (localeTerritory string ) string {
345+ lt := strings .ToLower (localeTerritory )
346+ datacenter := ""
347+ if lt == "cn" {
348+ datacenter = "jp"
349+ } else {
350+ datacenter = "nl"
351+ }
352+ log .Debugf ("datacenter: %v" , datacenter )
353+ return datacenter + ".fallbacks.getiantem.org"
354+ }
355+
312356func (cfg * Config ) applyClientDefaults () {
313357 // Make sure we always have at least one masquerade set
314358 if cfg .Client .MasqueradeSets == nil {
@@ -325,7 +369,7 @@ func (cfg *Config) applyClientDefaults() {
325369 if len (cfg .Client .FrontedServers ) == 0 && len (cfg .Client .ChainedServers ) == 0 {
326370 cfg .Client .FrontedServers = []* client.FrontedServerInfo {
327371 & client.FrontedServerInfo {
328- Host : "jp.fallbacks.getiantem.org" ,
372+ Host : defaultRoundRobin () ,
329373 Port : 443 ,
330374 PoolSize : 0 ,
331375 MasqueradeSet : cloudflare ,
@@ -387,7 +431,37 @@ func (cfg Config) cloudPollSleepTime() time.Duration {
387431 return time .Duration ((CloudConfigPollInterval .Nanoseconds () / 2 ) + rand .Int63n (CloudConfigPollInterval .Nanoseconds ()))
388432}
389433
390- func (cfg Config ) fetchCloudConfig () ([]byte , error ) {
434+ func (cfg Config ) fetchInitialConfig (path string , ps * client.PackagedSettings ) error {
435+ var err error
436+ for _ , s := range ps .ChainedServers {
437+ log .Debugf ("Fetching config using chained server: %v" , s .Addr )
438+ dialer , er := s .Dialer ()
439+ if er != nil {
440+ log .Errorf ("Unable to configure chained server. Received error: %v" , er )
441+ continue
442+ }
443+ http := & http.Client {
444+ Transport : & http.Transport {
445+ DisableKeepAlives : true ,
446+ Dial : dialer .Dial ,
447+ },
448+ }
449+ var data []byte
450+ data , err = cfg .fetchCloudConfig (http )
451+ if err == nil {
452+ if er := ioutil .WriteFile (path , data , 0644 ); er != nil {
453+ log .Errorf ("Could not create file at %v, %v" , path , er )
454+ err = er
455+ } else {
456+ log .Debugf ("Wrote file at: %s" , path )
457+ return nil
458+ }
459+ }
460+ }
461+ return err
462+ }
463+
464+ func (cfg Config ) fetchCloudConfig (client * http.Client ) ([]byte , error ) {
391465 url := cfg .CloudConfig
392466 log .Debugf ("Checking for cloud configuration at: %s" , url )
393467 req , err := http .NewRequest ("GET" , url , nil )
@@ -407,7 +481,7 @@ func (cfg Config) fetchCloudConfig() ([]byte, error) {
407481 // successive requests
408482 req .Close = true
409483
410- resp , err := httpClient . Load ().( * http. Client ) .Do (req )
484+ resp , err := client .Do (req )
411485 if err != nil {
412486 return nil , fmt .Errorf ("Unable to fetch cloud config at %s: %s" , url , err )
413487 }
0 commit comments