func TestTCP(t *testing.T) { // Lower maxAssertAttempts to keep this test from running too long maxAssertAttempts = 2 l0, err := net.Listen("tcp", "localhost:0") if err != nil { t.Fatal(err) } defer func() { if err := l0.Close(); err != nil { t.Fatalf("Unable to close listener: %v", err) } }() start, fdc, err := Matching("TCP") if err != nil { t.Fatal(err) } assert.Equal(t, 1, start, "Starting count should have been 1") err = fdc.AssertDelta(0) if err != nil { t.Fatal(err) } assert.NoError(t, err, "Initial TCP count should be 0") l, err := net.Listen("tcp", "localhost:0") if err != nil { t.Fatal(err) } _, middle, err := Matching("TCP") if err != nil { t.Fatal(err) } err = fdc.AssertDelta(0) if assert.Error(t, err, "Asserting wrong count should fail") { assert.Contains(t, err.Error(), "Expected 0, have 1") assert.True(t, len(err.Error()) > 100) } err = fdc.AssertDelta(1) assert.NoError(t, err, "Ending TCP count should be 1") err = fdc.AssertDelta(0) if assert.Error(t, err, "Asserting wrong count should fail") { assert.Contains(t, err.Error(), "Expected 0, have 1") assert.Contains(t, err.Error(), "New") assert.True(t, len(err.Error()) > 100) } if err := l.Close(); err != nil { t.Fatalf("Unable to close listener: %v", err) } err = middle.AssertDelta(0) if assert.Error(t, err, "Asserting wrong count should fail") { assert.Contains(t, err.Error(), "Expected 0, have -1") assert.Contains(t, err.Error(), "Removed") assert.True(t, len(err.Error()) > 100) } }
func TestGET(t *testing.T) { log.Debugf("TestGET") reqTemplate := "GET %s HTTP/1.1\r\nHost: %s\r\n\r\n" resp := "HTTP/1.1 200 OK\r\n" content := "holla!\n" defer stopMockServers() site, _ := newMockServer(content) u, _ := url.Parse(site) log.Debugf("Started target site at %s", u) addr, _ := startServer(t) log.Debugf("Started proxy server at %s", addr) con, err := net.Dial("tcp", addr.String()) if !assert.NoError(t, err, "should dial proxy server") { return } defer func() { assert.NoError(t, con.Close(), "should close connection") }() req := fmt.Sprintf(reqTemplate, u, u.Host) _, err = con.Write([]byte(req)) if !assert.NoError(t, err, "should write request") { return } var buf [400]byte _, err = con.Read(buf[:]) if !assert.Contains(t, string(buf[:]), resp, "should get 200 OK for GET") { return } assert.Contains(t, string(buf[:]), content, "should read content") }
func TestIdleOriginDirect(t *testing.T) { okServer, err := impatientProxy(0, 30*time.Second) if err != nil { assert.Fail(t, "Error starting proxy server: %s", err) } impatientServer, err := impatientProxy(0, 50*time.Millisecond) if err != nil { assert.Fail(t, "Error starting proxy server: %s", err) } okForwardFn := func(conn net.Conn, proxy *server.Server, originURL *url.URL) { var buf [400]byte chunkedReq(t, &buf, conn, originURL) assert.Contains(t, string(buf[:]), "200 OK", "should succeed") } failForwardFn := func(conn net.Conn, proxy *server.Server, originURL *url.URL) { var buf [400]byte chunkedReq(t, &buf, conn, originURL) assert.Contains(t, string(buf[:]), "502 Bad Gateway", "should fail with 502") } testRoundTrip(t, okServer, httpOriginServer, okForwardFn) testRoundTrip(t, impatientServer, httpOriginServer, failForwardFn) }
func TestDumpWhiteList(t *testing.T) { AddToWl("a.com:80", true) AddToWl("b.com:80", false) dumped := DumpWhitelist() assert.Contains(t, dumped, "a.com:80", "dumped list should contain permanent items") assert.NotContains(t, dumped, "b.com:80", "dumped list should not contain temporary items") }
func TestTimeout(t *testing.T) { offer := Offer(1 * time.Millisecond) _, err := offer.FiveTuple() assert.Error(t, err, "There should be an error") if err != nil { assert.Contains(t, err.Error(), "Timed out", "Error should mention timing out") } }
func TestNotOKWithServerName(t *testing.T) { fdStart := countTCPFiles() conn, err := Dial("tcp", ADDR, true, nil) assert.Error(t, err, "There should have been a problem dialing") if err != nil { assert.Contains(t, err.Error(), CERTIFICATE_ERROR, "Wrong error on dial") } <-receivedServerNames closeAndCountFDs(t, conn, err, fdStart) }
// X-Lantern-Auth-Token + X-Lantern-Device-Id -> Forward func TestDirectOK(t *testing.T) { reqTempl := "GET /%s HTTP/1.1\r\nHost: %s\r\n\r\n" failResp := "HTTP/1.1 500 Internal Server Error\r\n" testOk := func(conn net.Conn, proxy *server.Server, originURL *url.URL) { req := fmt.Sprintf(reqTempl, originURL.Path, originURL.Host) t.Log("\n" + req) _, err := conn.Write([]byte(req)) if !assert.NoError(t, err, "should write GET request") { t.FailNow() } buf := [400]byte{} _, err = conn.Read(buf[:]) assert.Contains(t, string(buf[:]), originResponse, "should read tunneled response") } testFail := func(conn net.Conn, proxy *server.Server, originURL *url.URL) { req := fmt.Sprintf(reqTempl, originURL.Path, originURL.Host) t.Log("\n" + req) _, err := conn.Write([]byte(req)) if !assert.NoError(t, err, "should write GET request") { t.FailNow() } buf := [400]byte{} _, err = conn.Read(buf[:]) t.Log("\n" + string(buf[:])) assert.Contains(t, string(buf[:]), failResp, "should respond with 500 Internal Server Error") } testRoundTrip(t, httpProxy, httpOriginServer, testOk) testRoundTrip(t, tlsProxy, httpOriginServer, testOk) // HTTPS can't be tunneled using Direct Proxying, as redirections // require a TLS handshake between the proxy and the origin testRoundTrip(t, httpProxy, tlsOriginServer, testFail) testRoundTrip(t, tlsProxy, tlsOriginServer, testFail) }
func TestCONNECT(t *testing.T) { connectReq := "CONNECT %s HTTP/1.1\r\nHost: %s\r\n\r\n" tunneledReq := "GET / HTTP/1.1\r\n\r\n" connectResp := "HTTP/1.1 200 OK\r\n" tunneledResp := "holla!\n" defer stopMockServers() site, _ := newMockServer("holla!") u, _ := url.Parse(site) log.Debugf("Started target site at %s", u) addr, _ := startServer(t) log.Debugf("Started proxy server at %s", addr) con, err := net.Dial("tcp", addr.String()) if !assert.NoError(t, err, "should dial proxy server") { return } defer func() { assert.NoError(t, con.Close(), "should close connection") }() req := fmt.Sprintf(connectReq, u.Host, u.Host) _, err = con.Write([]byte(req)) if !assert.NoError(t, err, "should write CONNECT request") { return } var buf [400]byte _, err = con.Read(buf[:]) if !assert.Contains(t, string(buf[:]), connectResp, "should get 200 OK for CONNECT") { return } _, err = con.Write([]byte(tunneledReq)) if !assert.NoError(t, err, "should write tunneled data") { return } _, err = con.Read(buf[:]) assert.Contains(t, string(buf[:]), tunneledResp, "should read tunneled response") }
func TestNotOKWithoutServerName(t *testing.T) { fdStart := countTCPFiles() conn, err := Dial("tcp", ADDR, true, &tls.Config{ ServerName: "localhost", }) assert.Error(t, err, "There should have been a problem dialing") if err != nil { assert.Contains(t, err.Error(), CERTIFICATE_ERROR, "Wrong error on dial") } serverName := <-receivedServerNames assert.Empty(t, serverName, "Unexpected ServerName on server") closeAndCountFDs(t, conn, err, fdStart) }
func readResponse(conn net.Conn, req *http.Request, t *testing.T) { buffIn := bufio.NewReader(conn) resp, err := http.ReadResponse(buffIn, req) if err != nil { t.Fatalf("Unable to read response: %s", err) } buff := bytes.NewBuffer(nil) _, err = io.Copy(buff, resp.Body) if err != nil { t.Fatalf("Unable to read response body: %s", err) } text := string(buff.Bytes()) assert.Contains(t, text, TEXT, "Wrong text returned from server") }
func TestNotOKWithServerName(t *testing.T) { _, fdc, err := fdcount.Matching("TCP") if err != nil { t.Fatal(err) } conn, err := Dial("tcp", ADDR, true, nil) assert.Error(t, err, "There should have been a problem dialing") if err != nil { assert.Contains(t, err.Error(), CERTIFICATE_ERROR, "Wrong error on dial") } <-receivedServerNames closeAndCountFDs(t, conn, err, fdc) }
func TestInvalidRequest(t *testing.T) { connectResp := "HTTP/1.1 400 Bad Request\r\n" testFn := func(conn net.Conn, proxy *server.Server, originURL *url.URL) { _, err := conn.Write([]byte("GET HTTP/1.1\r\n\r\n")) if !assert.NoError(t, err, "should write GET request") { t.FailNow() } buf := [400]byte{} _, err = conn.Read(buf[:]) assert.Contains(t, string(buf[:]), connectResp, "should 400") } for i := 0; i < 10; i++ { testRoundTrip(t, httpProxy, tlsOriginServer, testFn) testRoundTrip(t, tlsProxy, tlsOriginServer, testFn) } }
func TestNotOKWithBadRootCert(t *testing.T) { badCert, err := keyman.LoadCertificateFromPEMBytes([]byte(GoogleInternetAuthority)) if err != nil { t.Fatalf("Unable to load GoogleInternetAuthority cert: %s", err) } _, fdc, err := fdcount.Matching("TCP") if err != nil { t.Fatal(err) } conn, err := Dial("tcp", ADDR, true, &tls.Config{ RootCAs: badCert.PoolContainingCert(), }) assert.Error(t, err, "There should have been a problem dialing") if err != nil { assert.Contains(t, err.Error(), CERTIFICATE_ERROR, "Wrong error on dial") } <-receivedServerNames closeAndCountFDs(t, conn, err, fdc) }
func TestAll(t *testing.T) { // Start an echo server l, err := net.Listen("tcp", "localhost:0") if err != nil { log.Fatalf("Unable to listen: %s", err) } defer func() { if err := l.Close(); err != nil { log.Fatalf("Unable to close listener: %v", err) } }() go func() { for { c, err := l.Accept() if err == nil { go func() { _, err = io.Copy(c, c) if err != nil { log.Fatalf("Unable to echo: %s", err) } }() } } }() addr := l.Addr().String() dialedBy := int32(0) dialer1Closed := int32(0) dialer1 := &Dialer{ Label: "Dialer 1", Weight: 1, QOS: 10, Dial: func(network, addr string) (net.Conn, error) { atomic.StoreInt32(&dialedBy, 1) return net.Dial(network, addr) }, OnClose: func() { atomic.StoreInt32(&dialer1Closed, 1) }, } dialer2 := &Dialer{ Label: "Dialer 2", Weight: 10000000, QOS: 1, Dial: func(network, addr string) (net.Conn, error) { atomic.StoreInt32(&dialedBy, 2) return net.Dial(network, addr) }, } d3Attempts := int32(0) dialer3 := newFailingDialer(3, &dialedBy, &d3Attempts) d4attempts := int32(0) dialer4 := &Dialer{ Label: "Dialer 4", Weight: 1, QOS: 15, Dial: func(network, addr string) (net.Conn, error) { atomic.StoreInt32(&dialedBy, 4) defer atomic.AddInt32(&d4attempts, 1) if atomic.LoadInt32(&d4attempts) < 1 { // Fail once return nil, fmt.Errorf("Failing intentionally") } else { // Eventually succeed return net.Dial(network, addr) } }, } // Test failure with no dialers b := New() _, err = b.Dial("tcp", addr) assert.Error(t, err, "Dialing with no dialers should have failed") // Test successful single dialer b = New(dialer1) defer func() { b.Close() time.Sleep(250 * time.Millisecond) assert.Equal(t, int32(1), atomic.LoadInt32(&dialer1Closed), "Dialer 1 should have been closed") _, err := b.Dial("tcp", addr) if assert.Error(t, err, "Dialing on closed balancer should fail") { assert.Contains(t, "No dialers left to try on pass 0", err.Error(), "Error should have mentioned that there were no dialers left to try") } }() conn, err := b.Dial("tcp", addr) assert.NoError(t, err, "Dialing should have succeeded") assert.Equal(t, 1, atomic.LoadInt32(&dialedBy), "Wrong dialedBy") if err == nil { doTestConn(t, conn) } // Test QOS dialedBy = 0 b = New(dialer1, dialer2) defer b.Close() conn, err = b.DialQOS("tcp", addr, 5) assert.NoError(t, err, "Dialing should have succeeded") assert.Equal(t, 1, atomic.LoadInt32(&dialedBy), "Wrong dialedBy") if err == nil { doTestConn(t, conn) } // Test random selection dialedBy = 0 conn, err = b.Dial("tcp", addr) assert.NoError(t, err, "Dialing should have succeeded") assert.Equal(t, 2, atomic.LoadInt32(&dialedBy), "Wrong dialedBy (note this has a 1/%d chance of failing)", (dialer1.Weight + dialer2.Weight)) if err == nil { doTestConn(t, conn) } // Test success with failing dialer dialedBy = 0 b = New(dialer1, dialer2, dialer3) defer b.Close() conn, err = b.DialQOS("tcp", addr, 20) assert.NoError(t, err, "Dialing should have succeeded") assert.Equal(t, 1, atomic.LoadInt32(&dialedBy), "Wrong dialedBy") if err == nil { doTestConn(t, conn) } d5Attempts := int32(0) dialer5 := newFailingDialer(5, &dialedBy, &d5Attempts) // Test that a dialer that initially fails will successfully get rechecked and ultimately succeed. b = New(dialer5) // Lower the maximum time between rechecks so the test can run in a reasonable amount of time. maxCheckTimeout = 100 * time.Millisecond // Dial a bunch of times on multiple goroutines to hit different failure branches. In the case // of the failing dialer, this ensures that we "use up" all the failures. var wg sync.WaitGroup wg.Add(2) for j := 0; j < 2; j++ { bn := b go func() { for i := 0; i < 50; i++ { _, err := bn.Dial("tcp", addr) assert.Error(t, err, "Dialing should have failed") time.Sleep(10 * time.Millisecond) } wg.Done() }() } wg.Wait() time.Sleep(1 * time.Second) assert.Equal(t, 6, atomic.LoadInt32(&d5Attempts), "Wrong number of check attempts on failed dialer") // Test success after successful recheck using custom check conn, err = b.DialQOS("tcp", addr, 20) assert.NoError(t, err, "Dialing should have succeeded on initially failing dialer") assert.Equal(t, 5, atomic.LoadInt32(&dialedBy), "Wrong dialedBy") if err == nil { doTestConn(t, conn) } // Test failure b = New(dialer4, dialer4) _, err = b.Dial("tcp", addr) assert.NoError(t, err, "Dialing should have succeeded as we have 2nd try") assert.Equal(t, 2, atomic.LoadInt32(&d4attempts), "Wrong number of dial attempts on failed dialer") // Test success after successful retest using default check conn, err = b.DialQOS("tcp", addr, 20) assert.NoError(t, err, "Dialing should have succeeded") assert.Equal(t, 4, atomic.LoadInt32(&dialedBy), "Wrong dialedBy") if err == nil { doTestConn(t, conn) } }
// X-Lantern-Auth-Token + X-Lantern-Device-Id -> 200 OK <- Tunneled request -> 200 OK func TestConnectOK(t *testing.T) { connectReq := "CONNECT %s HTTP/1.1\r\nHost: %s\r\n\r\n" connectResp := "HTTP/1.1 200 OK\r\n" testHTTP := func(conn net.Conn, proxy *server.Server, originURL *url.URL) { req := fmt.Sprintf(connectReq, originURL.Host, originURL.Host) t.Log("\n" + req) _, err := conn.Write([]byte(req)) if !assert.NoError(t, err, "should write CONNECT request") { t.FailNow() } var buf [400]byte _, err = conn.Read(buf[:]) if !assert.Contains(t, string(buf[:]), connectResp, "should get 200 OK") { t.FailNow() } _, err = conn.Write([]byte(tunneledReq)) if !assert.NoError(t, err, "should write tunneled data") { t.FailNow() } buf = [400]byte{} _, err = conn.Read(buf[:]) assert.Contains(t, string(buf[:]), originResponse, "should read tunneled response") } testTLS := func(conn net.Conn, proxy *server.Server, originURL *url.URL) { req := fmt.Sprintf(connectReq, originURL.Host, originURL.Host) t.Log("\n" + req) _, err := conn.Write([]byte(req)) if !assert.NoError(t, err, "should write CONNECT request") { t.FailNow() } var buf [400]byte _, err = conn.Read(buf[:]) if !assert.Contains(t, string(buf[:]), connectResp, "should get 200 OK") { t.FailNow() } // HTTPS-Tunneled HTTPS tunnConn := tls.Client(conn, &tls.Config{ InsecureSkipVerify: true, }) tunnConn.Handshake() _, err = tunnConn.Write([]byte(tunneledReq)) if !assert.NoError(t, err, "should write tunneled data") { t.FailNow() } buf = [400]byte{} _, err = tunnConn.Read(buf[:]) assert.Contains(t, string(buf[:]), originResponse, "should read tunneled response") } testRoundTrip(t, httpProxy, httpOriginServer, testHTTP) testRoundTrip(t, tlsProxy, httpOriginServer, testHTTP) testRoundTrip(t, httpProxy, tlsOriginServer, testTLS) testRoundTrip(t, tlsProxy, tlsOriginServer, testTLS) }