2018-05-14 18:11:18 +00:00
|
|
|
package tor
|
|
|
|
|
2018-05-15 16:45:59 +00:00
|
|
|
import (
|
2018-05-15 19:37:18 +00:00
|
|
|
"context"
|
2018-05-15 16:45:59 +00:00
|
|
|
"crypto"
|
|
|
|
"crypto/rsa"
|
|
|
|
"fmt"
|
|
|
|
"net"
|
|
|
|
"strconv"
|
2018-05-14 18:11:18 +00:00
|
|
|
|
2018-05-15 16:45:59 +00:00
|
|
|
"github.com/cretz/bine/control"
|
2018-05-16 22:37:42 +00:00
|
|
|
"github.com/cretz/bine/torutil/ed25519"
|
|
|
|
othered25519 "golang.org/x/crypto/ed25519"
|
2018-05-15 16:45:59 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
// OnionService implements net.Listener and net.Addr for an onion service.
|
|
|
|
type OnionService struct {
|
|
|
|
// ID is the service ID for this onion service.
|
|
|
|
ID string
|
|
|
|
|
|
|
|
// Key is the private key for this service. It is either the set key, the
|
|
|
|
// generated key, or nil if asked to discard the key. If present, it is
|
|
|
|
// *crypto/rsa.PrivateKey (1024 bit) when Version3 is false or
|
2018-05-16 22:37:42 +00:00
|
|
|
// github.com/cretz/bine/torutil/ed25519.PrivateKey when Version3 is true.
|
2018-05-15 16:45:59 +00:00
|
|
|
Key crypto.PrivateKey
|
|
|
|
|
|
|
|
// Version3 says whether or not this service is a V3 service.
|
|
|
|
Version3 bool
|
|
|
|
|
|
|
|
// ClientAuths is the credential set for clients. The keys are username and
|
|
|
|
// the values are credentials. The credentials will always be present even
|
|
|
|
// if Tor had to generate them.
|
|
|
|
ClientAuths map[string]string
|
|
|
|
|
|
|
|
// LocalListener is the local TCP listener. This is always present.
|
|
|
|
LocalListener net.Listener
|
|
|
|
|
|
|
|
// RemotePorts is the set of remote ports that are forwarded to the local
|
|
|
|
// listener. This will always have at least one value.
|
|
|
|
RemotePorts []int
|
|
|
|
|
|
|
|
// CloseLocalListenerOnClose is true if the local listener should be closed
|
|
|
|
// on Close. This is set to true if a listener was created by Listen and set
|
|
|
|
// to false of an existing LocalListener was provided to Listen.
|
|
|
|
CloseLocalListenerOnClose bool
|
|
|
|
|
|
|
|
// The Tor object that created this. Needed for Close.
|
|
|
|
Tor *Tor
|
|
|
|
}
|
|
|
|
|
|
|
|
// ListenConf is the configuration for Listen calls.
|
|
|
|
type ListenConf struct {
|
|
|
|
// LocalPort is the local port to create a TCP listener on. If the port is
|
|
|
|
// 0, it is automatically chosen. This is ignored if LocalListener is set.
|
|
|
|
LocalPort int
|
|
|
|
|
|
|
|
// LocalListener is the specific local listener to back the onion service.
|
|
|
|
// If this is nil (the default), then a listener is created with LocalPort.
|
|
|
|
LocalListener net.Listener
|
|
|
|
|
|
|
|
// RemotePorts are the remote ports to serve the onion service on. If empty,
|
|
|
|
// it is the same as the local port or local listener. This must have at
|
|
|
|
// least one value if the local listener is not a *net.TCPListener.
|
|
|
|
RemotePorts []int
|
|
|
|
|
|
|
|
// Key is the private key to use. If not present, a key is generated based
|
|
|
|
// on whether Version3 is true or false. If present, it must be a
|
|
|
|
// *crypto/rsa.PrivateKey (1024 bit), a
|
2018-05-16 22:37:42 +00:00
|
|
|
// github.com/cretz/bine/torutil/ed25519.PrivateKey, a
|
2018-05-15 16:45:59 +00:00
|
|
|
// golang.org/x/crypto/ed25519.PrivateKey, or a
|
|
|
|
// github.com/cretz/bine/control.Key.
|
|
|
|
Key crypto.PrivateKey
|
|
|
|
|
|
|
|
// Version3 determines whether, when Key is nil, a version 2 or version 3
|
|
|
|
// service/key will be generated. If true it is version 3 (an ed25519 key
|
|
|
|
// and v3 onion service) and if false it is version 2 (a RSA-2014 key and v2
|
|
|
|
// onion service). If Key is not nil, this value is ignored.
|
|
|
|
Version3 bool
|
|
|
|
|
|
|
|
// ClientAuths is the set of usernames and credentials for client
|
|
|
|
// authentication. The keys are usernames and the values are credentials. If
|
|
|
|
// a username is present but the credential is empty, a credential is
|
|
|
|
// generated by Tor for that user. If this is empty there is no
|
|
|
|
// authentication.
|
|
|
|
ClientAuths map[string]string
|
|
|
|
|
|
|
|
// MaxStreams is the maximum number of streams the service will accept. 0
|
|
|
|
// means unlimited.
|
|
|
|
MaxStreams int
|
|
|
|
|
|
|
|
// DiscardKey, if true and Key is nil (meaning a private key is generated),
|
|
|
|
// tells Tor not to return the generated private key. This value is ignored
|
|
|
|
// if Key is not nil.
|
|
|
|
DiscardKey bool
|
|
|
|
|
|
|
|
// Detach, if true, prevents the default behavior of the onion service being
|
|
|
|
// deleted when this controller connection is closed.
|
|
|
|
Detach bool
|
|
|
|
|
|
|
|
// NonAnonymous must be true if Tor options HiddenServiceSingleHopMode and
|
|
|
|
// HiddenServiceNonAnonymousMode are set. Otherwise, it must be false.
|
|
|
|
NonAnonymous bool
|
|
|
|
|
|
|
|
// MaxStreamsCloseCircuit determines whether to close the circuit when the
|
|
|
|
// maximum number of streams is exceeded. If true, the circuit is closed. If
|
|
|
|
// false, the stream is simply not connected but the circuit stays open.
|
|
|
|
MaxStreamsCloseCircuit bool
|
2018-05-15 19:37:18 +00:00
|
|
|
|
|
|
|
// NoWait if true will not wait until the onion service is built. If false,
|
|
|
|
// the network will be enabled if it's not and then we will wait until
|
|
|
|
// the onion service is built.
|
|
|
|
NoWait bool
|
2018-05-15 16:45:59 +00:00
|
|
|
}
|
|
|
|
|
2018-05-15 19:37:18 +00:00
|
|
|
// Listen creates an onion service and local listener. The context can be nil.
|
|
|
|
// If conf is nil, the default struct value is used. Note, if this errors, any
|
|
|
|
// listeners created here are closed but if a LocalListener is provided it may remain open.
|
|
|
|
func (t *Tor) Listen(ctx context.Context, conf *ListenConf) (*OnionService, error) {
|
|
|
|
if ctx == nil {
|
|
|
|
ctx = context.Background()
|
|
|
|
}
|
2018-05-15 16:45:59 +00:00
|
|
|
// Create the service up here and make sure we close it no matter the error within
|
2018-05-16 22:37:42 +00:00
|
|
|
svc := &OnionService{Tor: t, CloseLocalListenerOnClose: conf.LocalListener == nil}
|
2018-05-15 16:45:59 +00:00
|
|
|
var err error
|
|
|
|
|
|
|
|
// Create the local listener if necessary
|
|
|
|
svc.LocalListener = conf.LocalListener
|
|
|
|
if svc.LocalListener == nil {
|
|
|
|
if svc.LocalListener, err = net.Listen("tcp", "127.0.0.1:"+strconv.Itoa(conf.LocalPort)); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Henceforth, any error requires we close the svc
|
|
|
|
|
|
|
|
// Build the onion request
|
|
|
|
req := &control.AddOnionRequest{MaxStreams: conf.MaxStreams, ClientAuths: conf.ClientAuths}
|
|
|
|
// Set flags
|
|
|
|
if conf.DiscardKey {
|
|
|
|
req.Flags = append(req.Flags, "DiscardPK")
|
|
|
|
}
|
|
|
|
if conf.Detach {
|
|
|
|
req.Flags = append(req.Flags, "Detach")
|
|
|
|
}
|
|
|
|
if len(conf.ClientAuths) > 0 {
|
|
|
|
req.Flags = append(req.Flags, "BasicAuth")
|
|
|
|
}
|
|
|
|
if conf.NonAnonymous {
|
|
|
|
req.Flags = append(req.Flags, "NonAnonymous")
|
|
|
|
}
|
|
|
|
if conf.MaxStreamsCloseCircuit {
|
|
|
|
req.Flags = append(req.Flags, "MaxStreamsCloseCircuit")
|
|
|
|
}
|
|
|
|
// Set the key
|
|
|
|
switch key := conf.Key.(type) {
|
|
|
|
case nil:
|
|
|
|
svc.Version3 = conf.Version3
|
|
|
|
if conf.Version3 {
|
|
|
|
req.Key = control.GenKey(control.KeyAlgoED25519V3)
|
|
|
|
} else {
|
|
|
|
req.Key = control.GenKey(control.KeyAlgoRSA1024)
|
|
|
|
}
|
2018-05-16 22:37:42 +00:00
|
|
|
case control.GenKey:
|
|
|
|
svc.Version3 = conf.Version3
|
|
|
|
req.Key = key
|
2018-05-15 16:45:59 +00:00
|
|
|
case *rsa.PrivateKey:
|
2018-05-16 22:37:42 +00:00
|
|
|
svc.Key = key
|
2018-05-15 16:45:59 +00:00
|
|
|
svc.Version3 = false
|
|
|
|
if key.N == nil || key.N.BitLen() != 1024 {
|
|
|
|
err = fmt.Errorf("RSA key must be 1024 bits")
|
|
|
|
} else {
|
|
|
|
req.Key = &control.RSAKey{PrivateKey: key}
|
|
|
|
}
|
2018-05-16 22:37:42 +00:00
|
|
|
case *control.RSAKey:
|
|
|
|
svc.Key = key.PrivateKey
|
|
|
|
svc.Version3 = false
|
|
|
|
if key.N == nil || key.N.BitLen() != 1024 {
|
|
|
|
err = fmt.Errorf("RSA key must be 1024 bits")
|
|
|
|
} else {
|
|
|
|
req.Key = key
|
|
|
|
}
|
2018-05-15 16:45:59 +00:00
|
|
|
case ed25519.PrivateKey:
|
2018-05-16 22:37:42 +00:00
|
|
|
svc.Key = key
|
2018-05-15 16:45:59 +00:00
|
|
|
svc.Version3 = true
|
|
|
|
req.Key = control.ED25519Key(key)
|
2018-05-16 22:37:42 +00:00
|
|
|
case othered25519.PrivateKey:
|
|
|
|
properKey := ed25519.FromCryptoPrivateKey(key)
|
|
|
|
svc.Key = properKey
|
|
|
|
svc.Version3 = true
|
|
|
|
req.Key = control.ED25519Key(properKey)
|
|
|
|
case control.ED25519Key:
|
|
|
|
svc.Key = ed25519.PrivateKey(key)
|
|
|
|
svc.Version3 = true
|
|
|
|
req.Key = key
|
2018-05-15 16:45:59 +00:00
|
|
|
default:
|
|
|
|
err = fmt.Errorf("Unrecognized key type: %T", key)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Apply the remote ports
|
|
|
|
if err == nil {
|
|
|
|
if len(conf.RemotePorts) == 0 {
|
|
|
|
tcpAddr, ok := svc.LocalListener.Addr().(*net.TCPAddr)
|
|
|
|
if !ok {
|
|
|
|
err = fmt.Errorf("Unable to derive local TCP port")
|
|
|
|
} else {
|
|
|
|
svc.RemotePorts = []int{tcpAddr.Port}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
svc.RemotePorts = make([]int, len(conf.RemotePorts))
|
|
|
|
copy(svc.RemotePorts, conf.RemotePorts)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Apply the local ports with the remote ports
|
|
|
|
if err == nil {
|
|
|
|
localAddr := svc.LocalListener.Addr().String()
|
|
|
|
if _, ok := svc.LocalListener.(*net.UnixListener); ok {
|
|
|
|
localAddr = "unix:" + localAddr
|
|
|
|
}
|
2018-05-15 19:37:18 +00:00
|
|
|
for _, remotePort := range svc.RemotePorts {
|
2018-05-15 16:45:59 +00:00
|
|
|
req.Ports = append(req.Ports, &control.KeyVal{Key: strconv.Itoa(remotePort), Val: localAddr})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create the onion service
|
|
|
|
var resp *control.AddOnionResponse
|
|
|
|
if err == nil {
|
|
|
|
resp, err = t.Control.AddOnion(req)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Apply the response to the service
|
|
|
|
if err == nil {
|
|
|
|
svc.ID = resp.ServiceID
|
|
|
|
switch key := resp.Key.(type) {
|
|
|
|
case nil:
|
|
|
|
// Do nothing
|
|
|
|
case *control.RSAKey:
|
|
|
|
svc.Key = key.PrivateKey
|
|
|
|
case control.ED25519Key:
|
|
|
|
svc.Key = ed25519.PrivateKey(key)
|
|
|
|
default:
|
|
|
|
err = fmt.Errorf("Unrecognized result key type: %T", key)
|
|
|
|
}
|
|
|
|
// Client auths are the conf and then overridden by results
|
|
|
|
if len(conf.ClientAuths) > 0 {
|
|
|
|
svc.ClientAuths = make(map[string]string, len(conf.ClientAuths))
|
|
|
|
for k, v := range conf.ClientAuths {
|
|
|
|
svc.ClientAuths[k] = v
|
|
|
|
}
|
|
|
|
for k, v := range resp.ClientAuths {
|
|
|
|
svc.ClientAuths[k] = v
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-05-15 19:37:18 +00:00
|
|
|
// Wait if necessary
|
|
|
|
if err == nil && !conf.NoWait {
|
|
|
|
t.Debugf("Enabling network before waiting for publication")
|
|
|
|
// First make sure network is enabled
|
|
|
|
if err = t.EnableNetwork(ctx, true); err == nil {
|
|
|
|
t.Debugf("Waiting for publication")
|
|
|
|
// Now we'll take a similar approach to Stem. Several UPLOADs are sent out, so we count em. If we see
|
|
|
|
// UPLOADED, we succeeded. If we see failed, we count those. If there are as many failures as uploads, they
|
|
|
|
// all failed and it's a failure. NOTE: unlike Stem's comments that say they don't, we are actually seeing
|
|
|
|
// the service IDs for UPLOADED so we don't keep a map.
|
|
|
|
uploadsAttempted := 0
|
|
|
|
failures := []string{}
|
|
|
|
_, err = t.Control.EventWait(ctx, []control.EventCode{control.EventCodeHSDesc},
|
|
|
|
func(evt control.Event) (bool, error) {
|
|
|
|
hs, _ := evt.(*control.HSDescEvent)
|
|
|
|
if hs != nil && hs.Address == svc.ID {
|
|
|
|
switch hs.Action {
|
|
|
|
case "UPLOAD":
|
|
|
|
uploadsAttempted++
|
|
|
|
case "FAILED":
|
|
|
|
failures = append(failures,
|
|
|
|
fmt.Sprintf("Failed uploading to dir %v - reason: %v", hs.HSDir, hs.Reason))
|
|
|
|
if len(failures) == uploadsAttempted {
|
|
|
|
return false, fmt.Errorf("Failed all uploads, reasons: %v", failures)
|
|
|
|
}
|
|
|
|
case "UPLOADED":
|
|
|
|
return true, nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false, nil
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Give back err and close if there is an err
|
2018-05-15 16:45:59 +00:00
|
|
|
if err != nil {
|
|
|
|
if closeErr := svc.Close(); closeErr != nil {
|
|
|
|
err = fmt.Errorf("Error on listen: %v (also got error trying to close: %v)", err, closeErr)
|
|
|
|
}
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return svc, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Accept implements net.Listener.Accept.
|
|
|
|
func (o *OnionService) Accept() (net.Conn, error) {
|
|
|
|
return o.LocalListener.Accept()
|
|
|
|
}
|
|
|
|
|
|
|
|
// Addr implements net.Listener.Addr just returning this object.
|
|
|
|
func (o *OnionService) Addr() net.Addr {
|
|
|
|
return o
|
|
|
|
}
|
|
|
|
|
|
|
|
// Network implements net.Addr.Network always returning "tcp".
|
|
|
|
func (o *OnionService) Network() string {
|
|
|
|
return "tcp"
|
|
|
|
}
|
|
|
|
|
|
|
|
// String implements net.Addr.String and returns "<serviceID>.onion:<virtport>".
|
|
|
|
func (o *OnionService) String() string {
|
|
|
|
return fmt.Sprintf("%v.onion:%v", o.ID, o.RemotePorts[0])
|
2018-05-14 18:11:18 +00:00
|
|
|
}
|
|
|
|
|
2018-05-15 16:45:59 +00:00
|
|
|
// Close implements net.Listener.Close and deletes the onion service and closes
|
|
|
|
// the LocalListener if CloseLocalListenerOnClose is true.
|
|
|
|
func (o *OnionService) Close() (err error) {
|
2018-05-15 19:37:18 +00:00
|
|
|
o.Tor.Debugf("Closing onion %v", o)
|
2018-05-15 16:45:59 +00:00
|
|
|
// Delete the onion first
|
|
|
|
if o.ID != "" {
|
2018-05-15 19:37:18 +00:00
|
|
|
err = o.Tor.Control.DelOnion(o.ID)
|
|
|
|
o.ID = ""
|
2018-05-15 16:45:59 +00:00
|
|
|
}
|
|
|
|
// Now if the local one needs to be closed, do it
|
|
|
|
if o.CloseLocalListenerOnClose && o.LocalListener != nil {
|
|
|
|
if closeErr := o.LocalListener.Close(); closeErr != nil {
|
|
|
|
if err != nil {
|
|
|
|
err = fmt.Errorf("Unable to close onion: %v (also unable to close local listener: %v)", err, closeErr)
|
|
|
|
} else {
|
|
|
|
err = closeErr
|
|
|
|
}
|
|
|
|
}
|
|
|
|
o.LocalListener = nil
|
|
|
|
}
|
2018-05-15 19:37:18 +00:00
|
|
|
if err != nil {
|
|
|
|
o.Tor.Debugf("Failed closing onion: %v", err)
|
|
|
|
}
|
2018-05-15 16:45:59 +00:00
|
|
|
return
|
2018-05-14 18:11:18 +00:00
|
|
|
}
|