2018-05-15 21:49:57 +00:00
|
|
|
package tor
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"fmt"
|
2020-10-15 22:29:54 +00:00
|
|
|
"git.openprivacy.ca/openprivacy/bine/control"
|
2018-05-15 21:49:57 +00:00
|
|
|
"net"
|
|
|
|
"strings"
|
|
|
|
|
|
|
|
"golang.org/x/net/proxy"
|
|
|
|
)
|
|
|
|
|
|
|
|
// Dialer is a wrapper around a proxy.Dialer for dialing connections.
|
|
|
|
type Dialer struct {
|
|
|
|
proxy.Dialer
|
|
|
|
}
|
|
|
|
|
2020-10-15 22:29:54 +00:00
|
|
|
// Authenticator provides a facade over various Tor control port authentication methods.
|
|
|
|
type Authenticator interface {
|
|
|
|
Authenticate(controlport *control.Conn) error
|
|
|
|
}
|
|
|
|
|
2018-05-15 21:49:57 +00:00
|
|
|
// DialConf is the configuration used for Dialer.
|
|
|
|
type DialConf struct {
|
|
|
|
// ProxyAddress is the address for the SOCKS5 proxy. If empty, it is looked
|
|
|
|
// up.
|
|
|
|
ProxyAddress string
|
|
|
|
|
|
|
|
// ProxyNetwork is the network for the SOCKS5 proxy. If ProxyAddress is
|
|
|
|
// empty, this value is ignored and overridden by what is looked up. If this
|
|
|
|
// is empty and ProxyAddress is not empty, it defaults to "tcp".
|
|
|
|
ProxyNetwork string
|
|
|
|
|
2018-11-05 17:53:41 +00:00
|
|
|
// ProxyAuth is the auth for the proxy. Since Tor's SOCKS5 proxy is
|
|
|
|
// 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.
|
2018-05-15 21:49:57 +00:00
|
|
|
ProxyAuth *proxy.Auth
|
|
|
|
|
|
|
|
// SkipEnableNetwork, if true, will skip the enable network step in Dialer.
|
|
|
|
SkipEnableNetwork bool
|
|
|
|
|
|
|
|
// Forward is the dialer to forward to. If nil, just uses normal net dialer.
|
|
|
|
Forward proxy.Dialer
|
2020-10-15 22:29:54 +00:00
|
|
|
|
|
|
|
Authenticator Authenticator
|
2018-05-15 21:49:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Dialer creates a new Dialer for the given configuration. Context can be nil.
|
|
|
|
// If conf is nil, a default is used.
|
|
|
|
func (t *Tor) Dialer(ctx context.Context, conf *DialConf) (*Dialer, error) {
|
|
|
|
if ctx == nil {
|
|
|
|
ctx = context.Background()
|
|
|
|
}
|
|
|
|
if conf == nil {
|
|
|
|
conf = &DialConf{}
|
|
|
|
}
|
2020-10-15 22:29:54 +00:00
|
|
|
|
|
|
|
conf.Authenticator.Authenticate(t.Control)
|
|
|
|
|
2018-05-15 21:49:57 +00:00
|
|
|
// Enable the network if requested
|
|
|
|
if !conf.SkipEnableNetwork {
|
|
|
|
if err := t.EnableNetwork(ctx, true); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Lookup proxy address as needed
|
|
|
|
proxyNetwork := conf.ProxyNetwork
|
|
|
|
proxyAddress := conf.ProxyAddress
|
|
|
|
if proxyAddress == "" {
|
|
|
|
info, err := t.Control.GetInfo("net/listeners/socks")
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if len(info) != 1 || info[0].Key != "net/listeners/socks" {
|
|
|
|
return nil, fmt.Errorf("Unable to get socks proxy address")
|
|
|
|
}
|
2023-04-04 22:06:18 +00:00
|
|
|
|
|
|
|
if strings.HasPrefix(info[0].Val, "\"") {
|
|
|
|
options := strings.Split(info[0].Val, " ")
|
|
|
|
// we get this kind of response from Tails / OnionGrater, multiple potential listeners
|
|
|
|
// formatted. Simply choose the first one.
|
|
|
|
// NOTE: This logic is probably not the best...
|
|
|
|
proxyAddress = options[0][1 : len(options[0])-1]
|
2018-05-15 21:49:57 +00:00
|
|
|
proxyNetwork = "tcp"
|
2023-04-04 22:06:18 +00:00
|
|
|
} else {
|
|
|
|
|
|
|
|
proxyAddress = info[0].Val
|
|
|
|
if strings.HasPrefix(proxyAddress, "unix:") {
|
|
|
|
proxyAddress = proxyAddress[5:]
|
|
|
|
proxyNetwork = "unix"
|
|
|
|
} else {
|
|
|
|
proxyNetwork = "tcp"
|
|
|
|
}
|
2018-05-15 21:49:57 +00:00
|
|
|
}
|
|
|
|
} else if proxyNetwork == "" {
|
|
|
|
proxyNetwork = "tcp"
|
|
|
|
}
|
|
|
|
|
2018-11-05 17:53:41 +00:00
|
|
|
dialer, err := proxy.SOCKS5(proxyNetwork, proxyAddress, conf.ProxyAuth, conf.Forward)
|
2018-05-15 21:49:57 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return &Dialer{dialer}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// DialContext is the equivalent of net.DialContext.
|
|
|
|
//
|
|
|
|
// TODO: Remove when https://github.com/golang/go/issues/17759 is released.
|
|
|
|
func (d *Dialer) DialContext(ctx context.Context, network string, addr string) (net.Conn, error) {
|
|
|
|
errCh := make(chan error, 1)
|
|
|
|
connCh := make(chan net.Conn, 1)
|
|
|
|
go func() {
|
|
|
|
if conn, err := d.Dial(network, addr); err != nil {
|
|
|
|
errCh <- err
|
|
|
|
} else if ctx.Err() != nil {
|
|
|
|
conn.Close()
|
|
|
|
} else {
|
|
|
|
connCh <- conn
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
select {
|
|
|
|
case err := <-errCh:
|
|
|
|
return nil, err
|
|
|
|
case conn := <-connCh:
|
|
|
|
return conn, nil
|
|
|
|
case <-ctx.Done():
|
|
|
|
return nil, ctx.Err()
|
|
|
|
}
|
|
|
|
}
|