2021-04-03 19:57:21 +02:00
|
|
|
package tunnel
|
2021-02-02 12:05:47 +01:00
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"fmt"
|
|
|
|
"net"
|
|
|
|
"net/url"
|
|
|
|
"os"
|
|
|
|
"path/filepath"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/ooni/psiphon/oopsi/github.com/Psiphon-Labs/psiphon-tunnel-core/ClientLibrary/clientlib"
|
|
|
|
)
|
|
|
|
|
2021-04-03 19:57:21 +02:00
|
|
|
// psiphonDependencies contains dependencies for psiphonStart
|
|
|
|
type psiphonDependencies interface {
|
2021-02-02 12:05:47 +01:00
|
|
|
MkdirAll(path string, perm os.FileMode) error
|
|
|
|
RemoveAll(path string) error
|
|
|
|
Start(ctx context.Context, config []byte,
|
|
|
|
workdir string) (*clientlib.PsiphonTunnel, error)
|
|
|
|
}
|
|
|
|
|
|
|
|
type defaultDependencies struct{}
|
|
|
|
|
|
|
|
func (defaultDependencies) MkdirAll(path string, perm os.FileMode) error {
|
|
|
|
return os.MkdirAll(path, perm)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (defaultDependencies) RemoveAll(path string) error {
|
|
|
|
return os.RemoveAll(path)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (defaultDependencies) Start(
|
|
|
|
ctx context.Context, config []byte, workdir string) (*clientlib.PsiphonTunnel, error) {
|
|
|
|
return clientlib.StartTunnel(ctx, config, "", clientlib.Parameters{
|
|
|
|
DataRootDirectory: &workdir}, nil, nil)
|
|
|
|
}
|
|
|
|
|
2021-04-03 19:57:21 +02:00
|
|
|
// psiphonConfig contains the settings for psiphonStart. The empty config object implies
|
2021-02-02 12:05:47 +01:00
|
|
|
// that we will be using default settings for starting the tunnel.
|
2021-04-03 19:57:21 +02:00
|
|
|
type psiphonConfig struct {
|
2021-02-02 12:05:47 +01:00
|
|
|
// Dependencies contains dependencies for Start.
|
2021-04-03 19:57:21 +02:00
|
|
|
Dependencies psiphonDependencies
|
2021-02-02 12:05:47 +01:00
|
|
|
|
|
|
|
// WorkDir is the directory where Psiphon should store
|
|
|
|
// its configuration database.
|
|
|
|
WorkDir string
|
|
|
|
}
|
|
|
|
|
2021-04-03 19:57:21 +02:00
|
|
|
// psiphonTunnel is a psiphon tunnel
|
|
|
|
type psiphonTunnel struct {
|
2021-02-02 12:05:47 +01:00
|
|
|
tunnel *clientlib.PsiphonTunnel
|
|
|
|
duration time.Duration
|
|
|
|
}
|
|
|
|
|
2021-04-03 19:57:21 +02:00
|
|
|
func makeworkingdir(config psiphonConfig) (string, error) {
|
2021-02-02 12:05:47 +01:00
|
|
|
const testdirname = "oonipsiphon"
|
|
|
|
workdir := filepath.Join(config.WorkDir, testdirname)
|
|
|
|
if err := config.Dependencies.RemoveAll(workdir); err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
if err := config.Dependencies.MkdirAll(workdir, 0700); err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
return workdir, nil
|
|
|
|
}
|
|
|
|
|
2021-04-03 19:57:21 +02:00
|
|
|
// psiphonStart starts the psiphon tunnel.
|
|
|
|
func psiphonStart(
|
|
|
|
ctx context.Context, sess Session, config psiphonConfig) (Tunnel, error) {
|
2021-02-02 12:05:47 +01:00
|
|
|
select {
|
|
|
|
case <-ctx.Done():
|
|
|
|
return nil, ctx.Err() // simplifies unit testing this code
|
|
|
|
default:
|
|
|
|
}
|
|
|
|
if config.Dependencies == nil {
|
|
|
|
config.Dependencies = defaultDependencies{}
|
|
|
|
}
|
|
|
|
if config.WorkDir == "" {
|
|
|
|
config.WorkDir = sess.TempDir()
|
|
|
|
}
|
2021-04-02 12:03:18 +02:00
|
|
|
configJSON, err := sess.FetchPsiphonConfig(ctx)
|
2021-02-02 12:05:47 +01:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
workdir, err := makeworkingdir(config)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
start := time.Now()
|
|
|
|
tunnel, err := config.Dependencies.Start(ctx, configJSON, workdir)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
stop := time.Now()
|
2021-04-03 19:57:21 +02:00
|
|
|
return &psiphonTunnel{tunnel: tunnel, duration: stop.Sub(start)}, nil
|
2021-02-02 12:05:47 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Stop is an idempotent method that shuts down the tunnel
|
2021-04-03 19:57:21 +02:00
|
|
|
func (t *psiphonTunnel) Stop() {
|
2021-02-02 12:05:47 +01:00
|
|
|
if t != nil {
|
|
|
|
t.tunnel.Stop()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// SOCKS5ProxyURL returns the SOCKS5 proxy URL.
|
2021-04-03 19:57:21 +02:00
|
|
|
func (t *psiphonTunnel) SOCKS5ProxyURL() (proxyURL *url.URL) {
|
2021-02-02 12:05:47 +01:00
|
|
|
if t != nil {
|
|
|
|
proxyURL = &url.URL{
|
|
|
|
Scheme: "socks5",
|
|
|
|
Host: net.JoinHostPort(
|
|
|
|
"127.0.0.1", fmt.Sprintf("%d", t.tunnel.SOCKSProxyPort)),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// BootstrapTime returns the bootstrap time
|
2021-04-03 19:57:21 +02:00
|
|
|
func (t *psiphonTunnel) BootstrapTime() (duration time.Duration) {
|
2021-02-02 12:05:47 +01:00
|
|
|
if t != nil {
|
|
|
|
duration = t.duration
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|