Docs and beginning of simple iface and examples
This commit is contained in:
parent
dc2d829ca3
commit
2c4f45c285
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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))
|
||||
}
|
|
@ -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")
|
||||
}
|
Loading…
Reference in New Issue