8b92037ae3
* fix(tunnel/tor): keep tunneldir clean This diff ensures that we don't keep the log file growing and we also remove the temporary files created by the library we are currently using for running tor from golang. Part of https://github.com/ooni/probe/issues/985 * fix(session.go): tell use we're using a tunnel
284 lines
7.5 KiB
Go
284 lines
7.5 KiB
Go
package tunnel
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"net/url"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/cretz/bine/control"
|
|
"github.com/cretz/bine/tor"
|
|
"github.com/ooni/probe-cli/v3/internal/engine/internal/mockable"
|
|
)
|
|
|
|
// torCloser is used to mock a running tor process, which
|
|
// we abstract as a io.Closer in tor.go.
|
|
type torCloser struct {
|
|
counter int
|
|
}
|
|
|
|
// Close implements io.Closer.Close.
|
|
func (c *torCloser) Close() error {
|
|
c.counter++
|
|
return errors.New("mocked mocked mocked")
|
|
}
|
|
|
|
func TestTorTunnelNonNil(t *testing.T) {
|
|
closer := new(torCloser)
|
|
proxy := &url.URL{Scheme: "x", Host: "10.0.0.1:443"}
|
|
tun := &torTunnel{
|
|
bootstrapTime: 128,
|
|
instance: closer,
|
|
proxy: proxy,
|
|
}
|
|
if tun.BootstrapTime() != 128 {
|
|
t.Fatal("not the bootstrap time we expected")
|
|
}
|
|
if tun.SOCKS5ProxyURL() != proxy {
|
|
t.Fatal("not the url we expected")
|
|
}
|
|
tun.Stop()
|
|
if closer.counter != 1 {
|
|
t.Fatal("something went wrong while stopping the tunnel")
|
|
}
|
|
}
|
|
|
|
func TestTorWithCancelledContext(t *testing.T) {
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
cancel() // fail immediately
|
|
tun, err := torStart(ctx, &Config{
|
|
Session: &mockable.Session{},
|
|
TunnelDir: "testdata",
|
|
})
|
|
if !errors.Is(err, context.Canceled) {
|
|
t.Fatal("not the error we expected")
|
|
}
|
|
if tun != nil {
|
|
t.Fatal("expected nil tunnel here")
|
|
}
|
|
}
|
|
|
|
func TestTorWithEmptyTunnelDir(t *testing.T) {
|
|
ctx := context.Background()
|
|
tun, err := torStart(ctx, &Config{
|
|
Session: &mockable.Session{},
|
|
TunnelDir: "",
|
|
})
|
|
if !errors.Is(err, ErrEmptyTunnelDir) {
|
|
t.Fatal("not the error we expected")
|
|
}
|
|
if tun != nil {
|
|
t.Fatal("expected nil tunnel here")
|
|
}
|
|
}
|
|
|
|
func TestTorStartFailure(t *testing.T) {
|
|
expected := errors.New("mocked error")
|
|
ctx := context.Background()
|
|
tun, err := torStart(ctx, &Config{
|
|
Session: &mockable.Session{},
|
|
TunnelDir: "testdata",
|
|
testTorStart: func(ctx context.Context, conf *tor.StartConf) (*tor.Tor, error) {
|
|
return nil, expected
|
|
},
|
|
})
|
|
if !errors.Is(err, expected) {
|
|
t.Fatal("not the error we expected")
|
|
}
|
|
if tun != nil {
|
|
t.Fatal("expected nil tunnel here")
|
|
}
|
|
}
|
|
|
|
func TestTorEnableNetworkFailure(t *testing.T) {
|
|
expected := errors.New("mocked error")
|
|
ctx := context.Background()
|
|
tun, err := torStart(ctx, &Config{
|
|
Session: &mockable.Session{},
|
|
TunnelDir: "testdata",
|
|
testTorStart: func(ctx context.Context, conf *tor.StartConf) (*tor.Tor, error) {
|
|
return &tor.Tor{}, nil
|
|
},
|
|
testTorEnableNetwork: func(ctx context.Context, tor *tor.Tor, wait bool) error {
|
|
return expected
|
|
},
|
|
})
|
|
if !errors.Is(err, expected) {
|
|
t.Fatal("not the error we expected")
|
|
}
|
|
if tun != nil {
|
|
t.Fatal("expected nil tunnel here")
|
|
}
|
|
}
|
|
|
|
func TestTorGetInfoFailure(t *testing.T) {
|
|
expected := errors.New("mocked error")
|
|
ctx := context.Background()
|
|
tun, err := torStart(ctx, &Config{
|
|
Session: &mockable.Session{},
|
|
TunnelDir: "testdata",
|
|
testTorStart: func(ctx context.Context, conf *tor.StartConf) (*tor.Tor, error) {
|
|
return &tor.Tor{}, nil
|
|
},
|
|
testTorEnableNetwork: func(ctx context.Context, tor *tor.Tor, wait bool) error {
|
|
return nil
|
|
},
|
|
testTorGetInfo: func(ctrl *control.Conn, keys ...string) ([]*control.KeyVal, error) {
|
|
return nil, expected
|
|
},
|
|
})
|
|
if !errors.Is(err, expected) {
|
|
t.Fatal("not the error we expected")
|
|
}
|
|
if tun != nil {
|
|
t.Fatal("expected nil tunnel here")
|
|
}
|
|
}
|
|
|
|
func TestTorGetInfoInvalidNumberOfKeys(t *testing.T) {
|
|
ctx := context.Background()
|
|
tun, err := torStart(ctx, &Config{
|
|
Session: &mockable.Session{},
|
|
TunnelDir: "testdata",
|
|
testTorStart: func(ctx context.Context, conf *tor.StartConf) (*tor.Tor, error) {
|
|
return &tor.Tor{}, nil
|
|
},
|
|
testTorEnableNetwork: func(ctx context.Context, tor *tor.Tor, wait bool) error {
|
|
return nil
|
|
},
|
|
testTorGetInfo: func(ctrl *control.Conn, keys ...string) ([]*control.KeyVal, error) {
|
|
return nil, nil
|
|
},
|
|
})
|
|
if !errors.Is(err, ErrTorUnableToGetSOCKSProxyAddress) {
|
|
t.Fatal("not the error we expected", err)
|
|
}
|
|
if tun != nil {
|
|
t.Fatal("expected nil tunnel here")
|
|
}
|
|
}
|
|
|
|
func TestTorGetInfoInvalidKey(t *testing.T) {
|
|
ctx := context.Background()
|
|
tun, err := torStart(ctx, &Config{
|
|
Session: &mockable.Session{},
|
|
TunnelDir: "testdata",
|
|
testTorStart: func(ctx context.Context, conf *tor.StartConf) (*tor.Tor, error) {
|
|
return &tor.Tor{}, nil
|
|
},
|
|
testTorEnableNetwork: func(ctx context.Context, tor *tor.Tor, wait bool) error {
|
|
return nil
|
|
},
|
|
testTorGetInfo: func(ctrl *control.Conn, keys ...string) ([]*control.KeyVal, error) {
|
|
return []*control.KeyVal{{}}, nil
|
|
},
|
|
})
|
|
if !errors.Is(err, ErrTorUnableToGetSOCKSProxyAddress) {
|
|
t.Fatal("not the error we expected")
|
|
}
|
|
if tun != nil {
|
|
t.Fatal("expected nil tunnel here")
|
|
}
|
|
}
|
|
|
|
func TestTorGetInfoInvalidProxyType(t *testing.T) {
|
|
ctx := context.Background()
|
|
tun, err := torStart(ctx, &Config{
|
|
Session: &mockable.Session{},
|
|
TunnelDir: "testdata",
|
|
testTorStart: func(ctx context.Context, conf *tor.StartConf) (*tor.Tor, error) {
|
|
return &tor.Tor{}, nil
|
|
},
|
|
testTorEnableNetwork: func(ctx context.Context, tor *tor.Tor, wait bool) error {
|
|
return nil
|
|
},
|
|
testTorGetInfo: func(ctrl *control.Conn, keys ...string) ([]*control.KeyVal, error) {
|
|
return []*control.KeyVal{{Key: "net/listeners/socks", Val: "127.0.0.1:9050"}}, nil
|
|
},
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if tun == nil {
|
|
t.Fatal("expected non-nil tunnel here")
|
|
}
|
|
}
|
|
|
|
func TestTorUnsupportedProxy(t *testing.T) {
|
|
ctx := context.Background()
|
|
tun, err := torStart(ctx, &Config{
|
|
Session: &mockable.Session{},
|
|
TunnelDir: "testdata",
|
|
testTorStart: func(ctx context.Context, conf *tor.StartConf) (*tor.Tor, error) {
|
|
return &tor.Tor{}, nil
|
|
},
|
|
testTorEnableNetwork: func(ctx context.Context, tor *tor.Tor, wait bool) error {
|
|
return nil
|
|
},
|
|
testTorGetInfo: func(ctrl *control.Conn, keys ...string) ([]*control.KeyVal, error) {
|
|
return []*control.KeyVal{{Key: "net/listeners/socks", Val: "unix:/foo/bar"}}, nil
|
|
},
|
|
})
|
|
if !errors.Is(err, ErrTorReturnedUnsupportedProxy) {
|
|
t.Fatal("not the error we expected")
|
|
}
|
|
if tun != nil {
|
|
t.Fatal("expected nil tunnel here")
|
|
}
|
|
}
|
|
|
|
func TestMaybeCleanupTunnelDir(t *testing.T) {
|
|
fakeTunDir := filepath.Join("testdata", "fake-tun-dir")
|
|
if err := os.RemoveAll(fakeTunDir); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := os.MkdirAll(fakeTunDir, 0700); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
fakeData := []byte("deadbeef\n")
|
|
logfile := filepath.Join(fakeTunDir, "tor.log")
|
|
if err := ioutil.WriteFile(logfile, fakeData, 0600); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
for idx := 0; idx < 3; idx++ {
|
|
filename := filepath.Join(fakeTunDir, fmt.Sprintf("torrc-%d", idx))
|
|
if err := ioutil.WriteFile(filename, fakeData, 0600); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
filename = filepath.Join(fakeTunDir, fmt.Sprintf("control-port-%d", idx))
|
|
if err := ioutil.WriteFile(filename, fakeData, 0600); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
filename = filepath.Join(fakeTunDir, fmt.Sprintf("antani-%d", idx))
|
|
if err := ioutil.WriteFile(filename, fakeData, 0600); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
files, err := filepath.Glob(filepath.Join(fakeTunDir, "*"))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(files) != 10 {
|
|
t.Fatal("unexpected number of files")
|
|
}
|
|
maybeCleanupTunnelDir(fakeTunDir, logfile)
|
|
files, err = filepath.Glob(filepath.Join(fakeTunDir, "*"))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(files) != 3 {
|
|
t.Fatal("unexpected number of files")
|
|
}
|
|
expectPrefix := filepath.Join(fakeTunDir, "antani-")
|
|
for _, file := range files {
|
|
if !strings.HasPrefix(file, expectPrefix) {
|
|
t.Fatal("unexpected file name: ", file)
|
|
}
|
|
}
|
|
}
|