Docs and beginning of simple iface and examples

This commit is contained in:
Chad Retz 2018-05-12 23:58:15 -05:00
parent dc2d829ca3
commit 2c4f45c285
8 changed files with 149 additions and 14 deletions

View File

@ -4,6 +4,7 @@ import (
"strings"
)
// ExtendCircuit invokes EXTENDCIRCUIT and returns the circuit ID on success.
func (c *Conn) ExtendCircuit(circuitID string, path []string, purpose string) (string, error) {
if circuitID == "" {
circuitID = "0"
@ -22,10 +23,12 @@ func (c *Conn) ExtendCircuit(circuitID string, path []string, purpose string) (s
return resp.Reply[strings.LastIndexByte(resp.Reply, ' ')+1:], nil
}
// SetCircuitPurpose invokes SETCIRCUITPURPOSE.
func (c *Conn) SetCircuitPurpose(circuitID string, purpose string) error {
return c.sendRequestIgnoreResponse("SETCIRCUITPURPOSE %v purpose=%v", circuitID, purpose)
}
// CloseCircuit invokes CLOSECIRCUIT.
func (c *Conn) CloseCircuit(circuitID string, flags []string) error {
cmd := "CLOSECIRCUIT " + circuitID
for _, flag := range flags {

View File

@ -6,10 +6,12 @@ import (
"github.com/cretz/bine/util"
)
// SetConf invokes SETCONF.
func (c *Conn) SetConf(entries ...*KeyVal) error {
return c.sendSetConf("SETCONF", entries)
}
// ResetConf invokes RESETCONF.
func (c *Conn) ResetConf(entries ...*KeyVal) error {
return c.sendSetConf("RESETCONF", entries)
}
@ -24,6 +26,7 @@ func (c *Conn) sendSetConf(cmd string, entries []*KeyVal) error {
return c.sendRequestIgnoreResponse(cmd)
}
// GetConf invokes GETCONF and returns the values for the requested keys.
func (c *Conn) GetConf(keys ...string) ([]*KeyVal, error) {
resp, err := c.SendRequest("GETCONF %v", strings.Join(keys, " "))
if err != nil {
@ -47,6 +50,7 @@ func (c *Conn) GetConf(keys ...string) ([]*KeyVal, error) {
return ret, nil
}
// SaveConf invokes SAVECONF.
func (c *Conn) SaveConf(force bool) error {
cmd := "SAVECONF"
if force {
@ -55,6 +59,7 @@ func (c *Conn) SaveConf(force bool) error {
return c.sendRequestIgnoreResponse(cmd)
}
// LoadConf invokes LOADCONF.
func (c *Conn) LoadConf(conf string) error {
return c.sendRequestIgnoreResponse("+LOADCONF\r\n%v\r\n.", conf)
}

View File

@ -9,9 +9,11 @@ import (
"github.com/cretz/bine/util"
)
// EventCode represents an asynchronous event code (ref control spec 4.1)
type EventCode string
const (
// EventCodeAddrMap is ADDRMAP
EventCodeAddrMap EventCode = "ADDRMAP"
EventCodeBandwidth EventCode = "BW"
EventCodeBuildTimeoutSet EventCode = "BUILDTIMEOUT_SET"

View File

@ -1,5 +1,6 @@
package control
// GetHiddenServiceDescriptorAsync invokes HSFETCH.
func (c *Conn) GetHiddenServiceDescriptorAsync(address string, server string) error {
cmd := "HSFETCH " + address
if server != "" {
@ -8,6 +9,7 @@ func (c *Conn) GetHiddenServiceDescriptorAsync(address string, server string) er
return c.sendRequestIgnoreResponse(cmd)
}
// PostHiddenServiceDescriptorAsync invokes HSPOST.
func (c *Conn) PostHiddenServiceDescriptorAsync(desc string, servers []string, address string) error {
cmd := "+HSPOST"
for _, server := range servers {

View File

@ -6,14 +6,17 @@ import (
"github.com/cretz/bine/util"
)
// Signal invokes SIGNAL.
func (c *Conn) Signal(signal string) error {
return c.sendRequestIgnoreResponse("SIGNAL %v", signal)
}
// Quit invokes QUIT.
func (c *Conn) Quit() error {
return c.sendRequestIgnoreResponse("QUIT")
}
// MapAddresses invokes MAPADDRESS and returns mapped addresses.
func (c *Conn) MapAddresses(addresses ...*KeyVal) ([]*KeyVal, error) {
cmd := "MAPADDRESS"
for _, address := range addresses {
@ -33,6 +36,7 @@ func (c *Conn) MapAddresses(addresses ...*KeyVal) ([]*KeyVal, error) {
return ret, nil
}
// GetInfo invokes GETINTO and returns values for requested keys.
func (c *Conn) GetInfo(keys ...string) ([]*KeyVal, error) {
resp, err := c.SendRequest("GETINFO %v", strings.Join(keys, " "))
if err != nil {
@ -47,6 +51,7 @@ func (c *Conn) GetInfo(keys ...string) ([]*KeyVal, error) {
return ret, nil
}
// PostDescriptor invokes POSTDESCRIPTOR.
func (c *Conn) PostDescriptor(descriptor string, purpose string, cache string) error {
cmd := "+POSTDESCRIPTOR"
if purpose != "" {
@ -59,11 +64,12 @@ func (c *Conn) PostDescriptor(descriptor string, purpose string, cache string) e
return c.sendRequestIgnoreResponse(cmd)
}
// UseFeatures invokes USEFEATURE.
func (c *Conn) UseFeatures(features ...string) error {
return c.sendRequestIgnoreResponse("USEFEATURE " + strings.Join(features, " "))
}
// TODO: can this take multiple
// ResolveAsync invokes RESOLVE.
func (c *Conn) ResolveAsync(address string, reverse bool) error {
cmd := "RESOLVE "
if reverse {
@ -72,10 +78,12 @@ func (c *Conn) ResolveAsync(address string, reverse bool) error {
return c.sendRequestIgnoreResponse(cmd + address)
}
// TakeOwnership invokes TAKEOWNERSHIP.
func (c *Conn) TakeOwnership() error {
return c.sendRequestIgnoreResponse("TAKEOWNERSHIP")
}
// DropGuards invokes DROPGUARDS.
func (c *Conn) DropGuards() error {
return c.sendRequestIgnoreResponse("DROPGUARDS")
}

View File

@ -12,27 +12,39 @@ import (
"golang.org/x/crypto/ed25519"
)
// KeyType is a key type for Key in AddOnion.
type KeyType string
const (
KeyTypeNew KeyType = "NEW"
KeyTypeRSA1024 KeyType = "RSA1024"
// KeyTypeNew is NEW.
KeyTypeNew KeyType = "NEW"
// KeyTypeRSA1024 is RSA1024.
KeyTypeRSA1024 KeyType = "RSA1024"
// KeyTypeED25519V3 is ED25519-V3.
KeyTypeED25519V3 KeyType = "ED25519-V3"
)
// KeyAlgo is a key algorithm for GenKey on AddOnion.
type KeyAlgo string
const (
KeyAlgoBest KeyAlgo = "BEST"
KeyAlgoRSA1024 KeyAlgo = "RSA1024"
// KeyAlgoBest is BEST.
KeyAlgoBest KeyAlgo = "BEST"
// KeyAlgoRSA1024 is RSA1024.
KeyAlgoRSA1024 KeyAlgo = "RSA1024"
// KeyAlgoED25519V3 is ED25519-V3.
KeyAlgoED25519V3 KeyAlgo = "ED25519-V3"
)
// Key is a type of key to use for AddOnion. Implementations include GenKey, RSAKey, and ED25519Key.
type Key interface {
// Type is the KeyType for AddOnion.
Type() KeyType
// Blob is the serialized key for AddOnion.
Blob() string
}
// KeyFromString creates a Key for AddOnion based on a response string.
func KeyFromString(str string) (Key, error) {
typ, blob, _ := util.PartitionString(str, ':')
switch KeyType(typ) {
@ -47,14 +59,22 @@ func KeyFromString(str string) (Key, error) {
}
}
// GenKey is a Key for AddOnion that asks Tor to generate a key for the given algorithm.
type GenKey KeyAlgo
// GenKeyFromBlob creates a GenKey for the given response blob which is a KeyAlgo.
func GenKeyFromBlob(blob string) GenKey { return GenKey(KeyAlgo(blob)) }
func (GenKey) Type() KeyType { return KeyTypeNew }
func (g GenKey) Blob() string { return string(g) }
// Type implements Key.Type.
func (GenKey) Type() KeyType { return KeyTypeNew }
// Blob implements Key.Blob.
func (g GenKey) Blob() string { return string(g) }
// RSAKey is a Key for AddOnion that is a RSA-1024 key (i.e. v2).
type RSAKey struct{ *rsa.PrivateKey }
// RSA1024KeyFromBlob creates a RSAKey for the given response blob.
func RSA1024KeyFromBlob(blob string) (*RSAKey, error) {
byts, err := base64.StdEncoding.DecodeString(blob)
if err != nil {
@ -66,13 +86,19 @@ func RSA1024KeyFromBlob(blob string) (*RSAKey, error) {
}
return &RSAKey{rsaKey}, nil
}
// Type implements Key.Type.
func (*RSAKey) Type() KeyType { return KeyTypeRSA1024 }
// Blob implements Key.Blob.
func (r *RSAKey) Blob() string {
return base64.StdEncoding.EncodeToString(x509.MarshalPKCS1PrivateKey(r.PrivateKey))
}
// ED25519Key is a Key for AddOnion that is a ed25519 key (i.e. v3).
type ED25519Key ed25519.PrivateKey
// ED25519KeyFromBlob creates a ED25519Key for the given response blob.
func ED25519KeyFromBlob(blob string) (ED25519Key, error) {
byts, err := base64.StdEncoding.DecodeString(blob)
if err != nil {
@ -80,24 +106,40 @@ func ED25519KeyFromBlob(blob string) (ED25519Key, error) {
}
return ED25519Key(ed25519.PrivateKey(byts)), nil
}
func (ED25519Key) Type() KeyType { return KeyTypeED25519V3 }
// Type implements Key.Type.
func (ED25519Key) Type() KeyType { return KeyTypeED25519V3 }
// Blob implements Key.Blob.
func (e ED25519Key) Blob() string { return base64.StdEncoding.EncodeToString(e) }
// AddOnionRequest is a set of request params for AddOnion.
type AddOnionRequest struct {
Key Key
Flags []string
MaxStreams int
Ports map[string]string
// Key is the key to use or GenKey if Tor should generate it.
Key Key
// Flags are ADD_ONION flags.
Flags []string
// MaxStreams is ADD_ONION MaxStreams.
MaxStreams int
// Ports are ADD_ONION Port values. Key is virtual port, value is target port (or can be empty to use virtual port).
Ports map[string]string
// ClientAuths are ADD_ONION ClientAuth values. If value is empty string, Tor will generate the password.
ClientAuths map[string]string
}
// AddOnionResponse is the response for AddOnion.
type AddOnionResponse struct {
ServiceID string
Key Key
// ServiceID is the ADD_ONION response ServiceID value.
ServiceID string
// Key is the ADD_ONION response PrivateKey value.
Key Key
// ClientAuths are the ADD_ONION response ClientAuth values.
ClientAuths map[string]string
// RawResponse is the raw ADD_ONION response.
RawResponse *Response
}
// AddOnion invokes ADD_ONION and returns its response.
func (c *Conn) AddOnion(req *AddOnionRequest) (*AddOnionResponse, error) {
// Build command
if req.Key == nil {
@ -148,6 +190,7 @@ func (c *Conn) AddOnion(req *AddOnionRequest) (*AddOnionResponse, error) {
return ret, nil
}
// DelOnion invokes DELONION.
func (c *Conn) DelOnion(serviceID string) error {
return c.sendRequestIgnoreResponse("DELONION %v", serviceID)
}

View File

@ -0,0 +1,25 @@
package main
import (
"log"
"net/http"
)
func main() {
// Start tor with default config
t, err := tor.Start(nil)
if err != nil {
log.Fatal(err)
}
// Add a handler
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Hello, World!"))
})
// Create an onion service to listen on 8080 but show as 80
l, err := t.Listen(&tor.OnionConf{Port: 80, TargetPort: 8080})
if err != nil {
log.Fatal(err)
}
// Serve on HTTP
log.Fatal(http.Serve(l, nil))
}

47
tor/tor.go Normal file
View File

@ -0,0 +1,47 @@
package tor
import (
"context"
"io"
"net"
)
type Tor struct {
}
type StartConf struct {
// TODO: docs...Nil means contet.Background
Context context.Context
// TODO: docs...Empty string means just "tor" either locally or on PATH
ExePath string
// TODO: docs...If true, doesn't use exe path, uses statically compiled Tor
Embedded bool
// TODO: docs...If 0, Tor is asked to store the control port in a temporary file in the data directory that is
// deleted after read
ControlPort int
// TODO: docs...If not empty, this is the data directory used and *TempDataDir* fields are unused
DataDir string
// TODO: docs...If not empty, this is the parent directory that a child dir is created for data. If empty, the
// current dir is assumed. This has no effect if DataDir is set.
TempDataDirBase string
// TODO: docs...If true the temporary data dir is not deleted on close. This has no effect if DataDir is set.
RetainTempDataDir bool
// TODO: docs...Any extra CLI arguments to pass to Tor. This are applied after other CLI args.
ExtraArgs []string
// TODO: docs...
DebugWriter io.Writer
}
func (t *Tor) Start(conf *StartConf) error {
// actualConf := *conf
panic("TODO")
}
type OnionConf struct {
Port int
TargetPort int
}
func (t *Tor) Listen(conf *OnionConf) (net.Listener, error) {
panic("TODO")
}