Skip to content

Commit

Permalink
Merge pull request getlantern#2965 from getlantern/issue2943
Browse files Browse the repository at this point in the history
use new ip and country headers closes #2943
  • Loading branch information
myleshorton committed Aug 19, 2015
2 parents d4c4f99 + b5dfb8e commit 0750012
Show file tree
Hide file tree
Showing 3 changed files with 136 additions and 27 deletions.
100 changes: 73 additions & 27 deletions src/github.com/getlantern/flashlight/geolookup/geolookup.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@ package geolookup

import (
"fmt"
"io/ioutil"
"math"
"math/rand"
"net/http"
"reflect"
"sync"
"sync/atomic"
"time"

"github.com/getlantern/geolookup"
"github.com/getlantern/enproxy"
"github.com/getlantern/golog"

"github.com/getlantern/flashlight/pubsub"
Expand All @@ -31,23 +31,22 @@ var (
service *ui.Service
client atomic.Value
cfgMutex sync.Mutex
location atomic.Value
country = atomicString()
ip = atomicString()
)

func GetLocation() *geolookup.City {
l := location.Load()
if l == nil {
return nil
}
return l.(*geolookup.City)
func atomicString() atomic.Value {
var val atomic.Value
val.Store("")
return val
}

func GetIp() string {
return ip.Load().(string)
}

func GetCountry() string {
loc := GetLocation()
if loc == nil {
return ""
}
return loc.Country.IsoCode
return country.Load().(string)
}

// Configure configures geolookup to use the given http.Client to perform
Expand All @@ -58,6 +57,10 @@ func Configure(newClient *http.Client) {
cfgMutex.Lock()
defer cfgMutex.Unlock()

// Avoid annoying checks for nil later.
ip.Store("")
country.Store("")

client.Store(newClient)

if service == nil {
Expand All @@ -74,20 +77,61 @@ func Configure(newClient *http.Client) {

func registerService() error {
helloFn := func(write func(interface{}) error) error {
location := GetLocation()
if location == nil {
log.Trace("No lastKnownLocation, not sending anything to client")
country := GetCountry()
if country == "" {
log.Trace("No lastKnownCountry, not sending anything to client")
return nil
}
log.Trace("Sending last known location to new client")
return write(location)
return write(country)
}

var err error
service, err = ui.Register(messageType, nil, helloFn)
return err
}

func lookupIp(httpClient *http.Client) (string, string, error) {
httpClient.Timeout = 60 * time.Second

var err error
var req *http.Request
var resp *http.Response

// Note this will typically be an HTTP client that uses direct domain fronting to
// hit our server pool in the Netherlands.
if req, err = http.NewRequest("HEAD", "http://nl.fallbacks.getiantem.org", nil); err != nil {
return "", "", fmt.Errorf("Could not create request: %q", err)
}

// Enproxy returns an error if this isn't there.
req.Header.Set(enproxy.X_ENPROXY_ID, "1")

if resp, err = httpClient.Do(req); err != nil {
return "", "", fmt.Errorf("Could not get response from server: %q", err)
}
defer func() {
if err := resp.Body.Close(); err != nil {
log.Debugf("Unable to close reponse body: %v", err)
}
}()

if resp.StatusCode != http.StatusOK {
body := "body unreadable"
b, err := ioutil.ReadAll(resp.Body)
if err == nil {
body = string(b)
}
return "", "", fmt.Errorf("Unexpected response status %d: %v", resp.StatusCode, body)
}

ip := resp.Header.Get("Lantern-Ip")
country := resp.Header.Get("Lantern-Country")

log.Debugf("Got IP and country: %v, %v", ip, country)
return country, ip, nil
}

func write() {
consecutiveFailures := 0

Expand All @@ -97,17 +141,19 @@ func write() {
n := rand.Intn(publishSecondsVariance)
wait := time.Duration(basePublishSeconds-publishSecondsVariance/2+n) * time.Second

oldLocation := GetLocation()
newLocation, ip, err := geolookup.LookupIPWithClient("", client.Load().(*http.Client))
oldIp := GetIp()
oldCountry := GetCountry()

newCountry, newIp, err := lookupIp(client.Load().(*http.Client))
if err == nil {
consecutiveFailures = 0
if !reflect.DeepEqual(newLocation, oldLocation) {
log.Debugf("Location changed")
location.Store(newLocation)
if newIp != oldIp {
log.Debugf("IP changed")
ip.Store(newIp)
}
// Always publish location, even if unchanged
pubsub.Pub(pubsub.IP, ip)
service.Out <- newLocation
pubsub.Pub(pubsub.IP, newIp)
service.Out <- newCountry
} else {
msg := fmt.Sprintf("Unable to get current location: %s", err)
// When retrying after a failure, wait a different amount of time
Expand All @@ -121,8 +167,8 @@ func write() {
log.Debugf("Waiting %v before retrying", wait)
consecutiveFailures += 1
// If available, publish last known location
if oldLocation != nil {
service.Out <- oldLocation
if oldCountry != "" {
service.Out <- oldCountry
}
}

Expand Down
58 changes: 58 additions & 0 deletions src/github.com/getlantern/flashlight/geolookup/geolookup_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package geolookup

import (
"testing"
"time"

"github.com/getlantern/fronted"
"github.com/getlantern/keyman"
)

func TestNonDefaultClient(t *testing.T) {
d := integrationDialer(t, nil)
defer func() {
if err := d.Close(); err != nil {
t.Fatalf("Unable to close dialer: %v", err)
}
}()

client := d.NewDirectDomainFronter()
country, ip, err := lookupIp(client)
if len(country) != 2 {
t.Fatalf("Bad country %s", country)
}

if len(ip) < 7 {
t.Fatalf("Bad IP %s", ip)
}

if err != nil {
t.Fatalf("Should not have gotten an error looking up country and IP %v", err)
}
}

func integrationDialer(t *testing.T, statsFunc func(success bool, domain, addr string, resolutionTime, connectTime, handshakeTime time.Duration)) fronted.Dialer {
rootCAs, err := keyman.PoolContainingCerts("-----BEGIN CERTIFICATE-----\nMIIDdTCCAl2gAwIBAgILBAAAAAABFUtaw5QwDQYJKoZIhvcNAQEFBQAwVzELMAkG\nA1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jv\nb3QgQ0ExGzAZBgNVBAMTEkdsb2JhbFNpZ24gUm9vdCBDQTAeFw05ODA5MDExMjAw\nMDBaFw0yODAxMjgxMjAwMDBaMFcxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9i\nYWxTaWduIG52LXNhMRAwDgYDVQQLEwdSb290IENBMRswGQYDVQQDExJHbG9iYWxT\naWduIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDaDuaZ\njc6j40+Kfvvxi4Mla+pIH/EqsLmVEQS98GPR4mdmzxzdzxtIK+6NiY6arymAZavp\nxy0Sy6scTHAHoT0KMM0VjU/43dSMUBUc71DuxC73/OlS8pF94G3VNTCOXkNz8kHp\n1Wrjsok6Vjk4bwY8iGlbKk3Fp1S4bInMm/k8yuX9ifUSPJJ4ltbcdG6TRGHRjcdG\nsnUOhugZitVtbNV4FpWi6cgKOOvyJBNPc1STE4U6G7weNLWLBYy5d4ux2x8gkasJ\nU26Qzns3dLlwR5EiUWMWea6xrkEmCMgZK9FGqkjWZCrXgzT/LCrBbBlDSgeF59N8\n9iFo7+ryUp9/k5DPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8E\nBTADAQH/MB0GA1UdDgQWBBRge2YaRQ2XyolQL30EzTSo//z9SzANBgkqhkiG9w0B\nAQUFAAOCAQEA1nPnfE920I2/7LqivjTFKDK1fPxsnCwrvQmeU79rXqoRSLblCKOz\nyj1hTdNGCbM+w6DjY1Ub8rrvrTnhQ7k4o+YviiY776BQVvnGCv04zcQLcFGUl5gE\n38NflNUVyRRBnMRddWQVDf9VMOyGj/8N7yy5Y0b2qvzfvGn9LhJIZJrglfCm7ymP\nAbEVtQwdpf5pLGkkeB6zpxxxYu7KyJesF12KwvhHhm4qxFYxldBniYUr+WymXUad\nDKqC5JlR3XC321Y9YeRq4VzW9v493kHMB65jUr9TU/Qr6cf9tveCX4XSQRjbgbME\nHMUfpIBvFSDJ3gyICh3WZlXi/EjJKSZp4A==\n-----END CERTIFICATE-----\n")
if err != nil {
t.Fatalf("Unable to set up cert pool")
}

maxMasquerades := 2
masquerades := make([]*fronted.Masquerade, maxMasquerades)
for i := 0; i < len(masquerades); i++ {
// Good masquerade with IP
masquerades[i] = &fronted.Masquerade{
Domain: "10minutemail.com",
IpAddress: "162.159.250.16",
}
}

return fronted.NewDialer(fronted.Config{
Host: "fallbacks.getiantem.org",
Port: 443,
Masquerades: masquerades,
MaxMasquerades: maxMasquerades,
RootCAs: rootCAs,
OnDialStats: statsFunc,
})
}
5 changes: 5 additions & 0 deletions src/github.com/getlantern/flashlight/server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,11 @@ func (server *Server) checkForBannedCountry(req *http.Request) error {
}

func (server *Server) lookupCountry(req *http.Request) (string, error) {
// Use the country CloudFlare gives us if it's available.
cf := req.Header.Get("Cf-Ipcountry")
if cf != "" {
return cf, nil
}
clientIp := getClientIp(req)
if clientIp == "" {
log.Debug("Unable to determine client ip for geolookup")
Expand Down

0 comments on commit 0750012

Please sign in to comment.