ooni-probe-cli/internal/engine/tunnel/tor_test.go
Simone Basso a9b3a3b3a5
fix(tunnel): pass /absolute/path/to/tor to cretz/bine (#323)
* fix(tunnel): pass /absolute/path/to/tor to cretz/bine

It seems cretz/bine is not aware of https://blog.golang.org/path-security
for now. I am planning to send over a diff for that later today.

In the meanwhile, do the right thing here, and make sure that we obtain
the absolute path to the tor binary before we continue.

This work is part of https://github.com/ooni/probe-engine/issues/283.

* fix tests when tor is not installed
2021-05-04 08:14:25 +02:00

321 lines
8.5 KiB
Go

package tunnel
import (
"context"
"errors"
"fmt"
"io/ioutil"
"net/url"
"os"
"path/filepath"
"strings"
"syscall"
"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 TestTorBinaryNotFoundFailure(t *testing.T) {
ctx := context.Background()
tun, err := torStart(ctx, &Config{
Session: &mockable.Session{},
TorBinary: "/nonexistent/directory/tor",
TunnelDir: "testdata",
})
if !errors.Is(err, syscall.ENOENT) {
t.Fatal("not the error we expected", err)
}
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",
testExecabsLookPath: func(name string) (string, error) {
return "/usr/local/bin/tor", nil
},
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",
testExecabsLookPath: func(name string) (string, error) {
return "/usr/local/bin/tor", nil
},
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",
testExecabsLookPath: func(name string) (string, error) {
return "/usr/local/bin/tor", nil
},
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",
testExecabsLookPath: func(name string) (string, error) {
return "/usr/local/bin/tor", nil
},
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",
testExecabsLookPath: func(name string) (string, error) {
return "/usr/local/bin/tor", nil
},
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",
testExecabsLookPath: func(name string) (string, error) {
return "/usr/local/bin/tor", nil
},
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",
testExecabsLookPath: func(name string) (string, error) {
return "/usr/local/bin/tor", nil
},
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)
}
}
}