Skip to content

Commit

Permalink
Added function to Fronted for direct fronting
Browse files Browse the repository at this point in the history
  • Loading branch information
oxtoacart committed Feb 18, 2015
1 parent 4c3f93a commit aa0fbc3
Show file tree
Hide file tree
Showing 2 changed files with 89 additions and 46 deletions.
24 changes: 23 additions & 1 deletion src/github.com/getlantern/fronted/dialer.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
// package fronted provides a client and server for domain-fronted proxying.
// package fronted provides a client and server for domain-fronted proxying
// using enproxy proxies.
package fronted

import (
Expand Down Expand Up @@ -42,6 +43,16 @@ type Dialer interface {
// HttpClientUsing creates a simple domain-fronted HTTP client using the
// specified Masquerade.
HttpClientUsing(masquerade *Masquerade) *http.Client

// DirectHttpClient creates an HttpClient that domain-fronts but instead of
// using enproxy proxies routes to the destination server directly from the
// CDN. This is useful for web properties registered on the CDN itself, for
// example geo.getiantem.org.
//
// Note - the connection is already encrypted by domain-fronting, so this
// client should only be used to make HTTP requests. Using it for HTTPS
// requests will result in an error.
DirectHttpClient() *http.Client
}

// Config captures the configuration of a domain-fronted dialer.
Expand Down Expand Up @@ -188,6 +199,17 @@ func (d *dialer) HttpClientUsing(masquerade *Masquerade) *http.Client {
}
}

func (d *dialer) DirectHttpClient() *http.Client {
masquerade := d.masquerades.nextVerified()
return &http.Client{
Transport: &http.Transport{
Dial: func(network, addr string) (net.Conn, error) {
return d.dialServerWith(masquerade)
},
},
}
}

func (d *dialer) enproxyConfigWith(dialProxy func(addr string) (net.Conn, error)) *enproxy.Config {
return &enproxy.Config{
DialProxy: dialProxy,
Expand Down
111 changes: 66 additions & 45 deletions src/github.com/getlantern/fronted/fronted_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,70 @@ func TestRoundTripUnpooled(t *testing.T) {
// TestIntegration tests against existing domain-fronted servers running on
// CloudFlare.
func TestIntegration(t *testing.T) {
dialedDomain := ""
dialedAddr := ""
actualResolutionTime := time.Duration(0)
actualConnectTime := time.Duration(0)
actualHandshakeTime := time.Duration(0)
var statsMutex sync.Mutex

statsFunc := func(success bool, domain, addr string, resolutionTime, connectTime, handshakeTime time.Duration) {
if success {
statsMutex.Lock()
defer statsMutex.Unlock()
dialedDomain = domain
dialedAddr = addr
actualResolutionTime = resolutionTime
actualConnectTime = connectTime
actualHandshakeTime = handshakeTime
}
}

d := integrationDialer(t, statsFunc)
defer d.Close()

hc := &http.Client{
Transport: &http.Transport{
Dial: d.Dial,
},
}

resp, err := hc.Get("https://www.google.com/humans.txt")
if err != nil {
t.Fatalf("Unable to fetch from Google: %s", err)
}
defer resp.Body.Close()
b, err := ioutil.ReadAll(resp.Body)
if err != nil {
t.Fatalf("Unable to read response from Google: %s", err)
}
assert.Equal(t, expectedGoogleResponse, string(b), "Didn't get expected response from Google")

statsMutex.Lock()
defer statsMutex.Unlock()
assert.True(t, dialedDomain == "100partnerprogramme.de" || dialedDomain == "10minutemail.com", "Dialed domain didn't match one of the masquerade domains", dialedDomain)
assert.NotEqual(t, "", dialedAddr, "Should have received an addr")
assert.NotEqual(t, time.Duration(0), actualResolutionTime, "Should have received a resolutionTime")
assert.NotEqual(t, time.Duration(0), actualConnectTime, "Should have received a connectTime")
assert.NotEqual(t, time.Duration(0), actualHandshakeTime, "Should have received a handshakeTime")
}

func TestDirect(t *testing.T) {
d := integrationDialer(t, nil)
defer d.Close()

client := d.DirectHttpClient()
resp, err := client.Get("http://geo.getiantem.org/lookup")
if assert.NoError(t, err, "Should be able to call geo.getiantem.org") {
defer resp.Body.Close()
if assert.Equal(t, 200, resp.StatusCode, "Response should be successful") {
reflectedIp := resp.Header.Get("X-Reflected-Ip")
assert.NotEmpty(t, reflectedIp, "Response from geo.getiantem.org should contains a reflected ip")
}
}
}

func integrationDialer(t *testing.T, statsFunc func(success bool, domain, addr string, resolutionTime, connectTime, handshakeTime time.Duration)) 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")
Expand Down Expand Up @@ -216,57 +280,14 @@ func TestIntegration(t *testing.T) {
}
}

dialedDomain := ""
dialedAddr := ""
actualResolutionTime := time.Duration(0)
actualConnectTime := time.Duration(0)
actualHandshakeTime := time.Duration(0)
var statsMutex sync.Mutex

d := NewDialer(Config{
return NewDialer(Config{
Host: "fallbacks.getiantem.org",
Port: 443,
Masquerades: masquerades,
MaxMasquerades: maxMasquerades,
RootCAs: rootCAs,
OnDialStats: func(success bool, domain, addr string, resolutionTime, connectTime, handshakeTime time.Duration) {
if success {
statsMutex.Lock()
defer statsMutex.Unlock()
dialedDomain = domain
dialedAddr = addr
actualResolutionTime = resolutionTime
actualConnectTime = connectTime
actualHandshakeTime = handshakeTime
}
},
OnDialStats: statsFunc,
})
defer d.Close()

hc := &http.Client{
Transport: &http.Transport{
Dial: d.Dial,
},
}

resp, err := hc.Get("https://www.google.com/humans.txt")
if err != nil {
t.Fatalf("Unable to fetch from Google: %s", err)
}
defer resp.Body.Close()
b, err := ioutil.ReadAll(resp.Body)
if err != nil {
t.Fatalf("Unable to read response from Google: %s", err)
}
assert.Equal(t, expectedGoogleResponse, string(b), "Didn't get expected response from Google")

statsMutex.Lock()
defer statsMutex.Unlock()
assert.True(t, dialedDomain == "100partnerprogramme.de" || dialedDomain == "10minutemail.com", "Dialed domain didn't match one of the masquerade domains", dialedDomain)
assert.NotEqual(t, "", dialedAddr, "Should have received an addr")
assert.NotEqual(t, time.Duration(0), actualResolutionTime, "Should have received a resolutionTime")
assert.NotEqual(t, time.Duration(0), actualConnectTime, "Should have received a connectTime")
assert.NotEqual(t, time.Duration(0), actualHandshakeTime, "Should have received a handshakeTime")
}

func startServer(t *testing.T, allowNonGlobal bool, allowedPorts []int) net.Listener {
Expand Down

0 comments on commit aa0fbc3

Please sign in to comment.