Proxy auth and isolate test
* Don't use Tor info for proxy auth and update docs which fixes #18 * Add new test to confirm functionality of IsolateSOCKSAuth
This commit is contained in:
parent
25e2ee8b21
commit
5c62b5e9f6
|
@ -8,12 +8,13 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/cretz/bine/tor"
|
||||||
"golang.org/x/net/context/ctxhttp"
|
"golang.org/x/net/context/ctxhttp"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestDialerSimpleHTTP(t *testing.T) {
|
func TestDialerSimpleHTTP(t *testing.T) {
|
||||||
ctx := GlobalEnabledNetworkContext(t)
|
ctx := GlobalEnabledNetworkContext(t)
|
||||||
httpClient := httpClient(ctx)
|
httpClient := httpClient(ctx, nil)
|
||||||
// IsTor check
|
// IsTor check
|
||||||
byts := httpGet(ctx, httpClient, "https://check.torproject.org/api/ip")
|
byts := httpGet(ctx, httpClient, "https://check.torproject.org/api/ip")
|
||||||
jsn := map[string]interface{}{}
|
jsn := map[string]interface{}{}
|
||||||
|
@ -21,12 +22,12 @@ func TestDialerSimpleHTTP(t *testing.T) {
|
||||||
ctx.Require.True(jsn["IsTor"].(bool))
|
ctx.Require.True(jsn["IsTor"].(bool))
|
||||||
}
|
}
|
||||||
|
|
||||||
func httpClient(ctx *TestContext) *http.Client {
|
func httpClient(ctx *TestContext, conf *tor.DialConf) *http.Client {
|
||||||
// 15 seconds max to dial
|
// 15 seconds max to dial
|
||||||
dialCtx, dialCancel := context.WithTimeout(ctx, 15*time.Second)
|
dialCtx, dialCancel := context.WithTimeout(ctx, 15*time.Second)
|
||||||
defer dialCancel()
|
defer dialCancel()
|
||||||
// Make connection
|
// Make connection
|
||||||
dialer, err := ctx.Dialer(dialCtx, nil)
|
dialer, err := ctx.Dialer(dialCtx, conf)
|
||||||
ctx.Require.NoError(err)
|
ctx.Require.NoError(err)
|
||||||
return &http.Client{Transport: &http.Transport{DialContext: dialer.DialContext}}
|
return &http.Client{Transport: &http.Transport{DialContext: dialer.DialContext}}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,72 @@
|
||||||
|
package tests
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/cretz/bine/tor"
|
||||||
|
"golang.org/x/net/proxy"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TestIsolateSocksAuth simply confirms the functionality of IsolateSOCKSAuth,
|
||||||
|
// namely that it uses a different circuit for different SOCKS credentials.
|
||||||
|
func TestIsolateSocksAuth(t *testing.T) {
|
||||||
|
// Create context w/ no isolate
|
||||||
|
ctx := NewTestContext(t, &tor.StartConf{
|
||||||
|
NoAutoSocksPort: true,
|
||||||
|
ExtraArgs: []string{"--SocksPort", "auto NoIsolateSOCKSAuth"},
|
||||||
|
})
|
||||||
|
// Make sure it reused the circuit (i.e. only has one) for both separate-auth calls
|
||||||
|
uniqueCircuitIDs := doSeparateAuthHttpCalls(ctx)
|
||||||
|
ctx.Debugf("Unique IDs without isolate: %v", uniqueCircuitIDs)
|
||||||
|
ctx.Require.Len(uniqueCircuitIDs, 1)
|
||||||
|
// Create context w/ isolate
|
||||||
|
ctx = NewTestContext(t, nil)
|
||||||
|
// Make sure it made a new circuit (i.e. has two) for each separate-auth call
|
||||||
|
uniqueCircuitIDs = doSeparateAuthHttpCalls(ctx)
|
||||||
|
ctx.Debugf("Unique IDs with isolate: %v", uniqueCircuitIDs)
|
||||||
|
ctx.Require.Len(uniqueCircuitIDs, 2)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the map keyed with unique circuit IDs after second separate-auth HTTP call
|
||||||
|
func doSeparateAuthHttpCalls(ctx *TestContext) map[int]struct{} {
|
||||||
|
defer ctx.Close()
|
||||||
|
enableCtx, enableCancel := context.WithTimeout(ctx, 100*time.Second)
|
||||||
|
defer enableCancel()
|
||||||
|
ctx.Require.NoError(ctx.EnableNetwork(enableCtx, true))
|
||||||
|
// Make HTTP call w/ an auth
|
||||||
|
client := httpClient(ctx, &tor.DialConf{ProxyAuth: &proxy.Auth{"foo", "bar"}})
|
||||||
|
byts := httpGet(ctx, client, "https://check.torproject.org/api/ip")
|
||||||
|
ctx.Debugf("Read bytes: %v", string(byts))
|
||||||
|
// Confirm just size 1
|
||||||
|
ids := uniqueStreamCircuitIDs(ctx)
|
||||||
|
ctx.Require.Len(ids, 1)
|
||||||
|
// Now make call with another auth and just return circuit IDs
|
||||||
|
client = httpClient(ctx, &tor.DialConf{ProxyAuth: &proxy.Auth{"baz", "qux"}})
|
||||||
|
byts = httpGet(ctx, client, "https://check.torproject.org/api/ip")
|
||||||
|
ctx.Debugf("Read bytes: %v", string(byts))
|
||||||
|
return uniqueStreamCircuitIDs(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return each stream circuit as a key of an empty-val map
|
||||||
|
func uniqueStreamCircuitIDs(ctx *TestContext) map[int]struct{} {
|
||||||
|
ret := map[int]struct{}{}
|
||||||
|
vals, err := ctx.Control.GetInfo("stream-status")
|
||||||
|
ctx.Require.NoError(err)
|
||||||
|
for _, val := range vals {
|
||||||
|
ctx.Require.Equal("stream-status", val.Key)
|
||||||
|
for _, line := range strings.Split(val.Val, "\n") {
|
||||||
|
pieces := strings.Split(strings.TrimSpace(line), " ")
|
||||||
|
if len(pieces) < 3 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
i, err := strconv.Atoi(pieces[2])
|
||||||
|
ctx.Require.NoError(err)
|
||||||
|
ret[i] = struct{}{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret
|
||||||
|
}
|
|
@ -50,7 +50,7 @@ func startHTTPServer(
|
||||||
handlePattern string,
|
handlePattern string,
|
||||||
handler func(http.ResponseWriter, *http.Request),
|
handler func(http.ResponseWriter, *http.Request),
|
||||||
) (*http.Client, *http.Server, *tor.OnionService) {
|
) (*http.Client, *http.Server, *tor.OnionService) {
|
||||||
httpClient := httpClient(ctx)
|
httpClient := httpClient(ctx, nil)
|
||||||
// Wait at most a few minutes for the entire test
|
// Wait at most a few minutes for the entire test
|
||||||
listenCtx, listenCancel := context.WithTimeout(context.Background(), 4*time.Minute)
|
listenCtx, listenCancel := context.WithTimeout(context.Background(), 4*time.Minute)
|
||||||
defer listenCancel()
|
defer listenCancel()
|
||||||
|
|
|
@ -25,8 +25,13 @@ type DialConf struct {
|
||||||
// is empty and ProxyAddress is not empty, it defaults to "tcp".
|
// is empty and ProxyAddress is not empty, it defaults to "tcp".
|
||||||
ProxyNetwork string
|
ProxyNetwork string
|
||||||
|
|
||||||
// ProxyAuth is the auth for the proxy. An empty auth means no auth, a nil
|
// ProxyAuth is the auth for the proxy. Since Tor's SOCKS5 proxy is
|
||||||
// auth means it is looked up.
|
// unauthenticated, this is rarely needed. It can be used when
|
||||||
|
// IsolateSOCKSAuth is set to ensure separate circuits.
|
||||||
|
//
|
||||||
|
// This should not be confused with downstream SOCKS proxy authentication
|
||||||
|
// which is set via Tor values for Socks5ProxyUsername and
|
||||||
|
// Socks5ProxyPassword when Socks5Proxy is set.
|
||||||
ProxyAuth *proxy.Auth
|
ProxyAuth *proxy.Auth
|
||||||
|
|
||||||
// SkipEnableNetwork, if true, will skip the enable network step in Dialer.
|
// SkipEnableNetwork, if true, will skip the enable network step in Dialer.
|
||||||
|
@ -72,23 +77,8 @@ func (t *Tor) Dialer(ctx context.Context, conf *DialConf) (*Dialer, error) {
|
||||||
} else if proxyNetwork == "" {
|
} else if proxyNetwork == "" {
|
||||||
proxyNetwork = "tcp"
|
proxyNetwork = "tcp"
|
||||||
}
|
}
|
||||||
// Lookup proxy auth as needed
|
|
||||||
proxyAuth := conf.ProxyAuth
|
|
||||||
if proxyAuth == nil {
|
|
||||||
info, err := t.Control.GetConf("Socks5ProxyUsername", "Socks5ProxyPassword")
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if len(info) != 2 || info[0].Key != "Socks5ProxyUsername" || info[1].Key != "Socks5ProxyPassword" {
|
|
||||||
return nil, fmt.Errorf("Unable to get proxy auth")
|
|
||||||
}
|
|
||||||
proxyAuth = &proxy.Auth{User: info[0].Val, Password: info[1].Val}
|
|
||||||
}
|
|
||||||
if proxyAuth.User == "" && proxyAuth.Password == "" {
|
|
||||||
proxyAuth = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
dialer, err := proxy.SOCKS5(proxyNetwork, proxyAddress, proxyAuth, conf.Forward)
|
dialer, err := proxy.SOCKS5(proxyNetwork, proxyAddress, conf.ProxyAuth, conf.Forward)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue