Add expanded interface for ADD_ONION (NewOnion) to use special configuration
structs that implements all ADD_ONION flags. Corresponding interface for creating onion Listener (NewListener) that also makes onion Listeners use multiple virtual ports. Some code simplifications.
This commit is contained in:
parent
38b4676028
commit
3cc4ebc379
79
cmd_onion.go
79
cmd_onion.go
|
@ -1,8 +1,8 @@
|
||||||
// cmd_onion.go - various onion service commands: ADD_ONION, DEL_ONION...
|
// cmd_onion.go - various onion service commands: ADD_ONION, DEL_ONION...
|
||||||
//
|
//
|
||||||
// To the extent possible under law, David Stainton waived all copyright
|
// To the extent possible under law, David Stainton and Ivan Markin waived
|
||||||
// and related or neighboring rights to this module of bulb, using the creative
|
// all copyright and related or neighboring rights to this module of bulb,
|
||||||
// commons "cc0" public domain dedication. See LICENSE or
|
// using the creative commons "cc0" public domain dedication. See LICENSE or
|
||||||
// <http://creativecommons.org/publicdomain/zero/1.0/> for full details.
|
// <http://creativecommons.org/publicdomain/zero/1.0/> for full details.
|
||||||
|
|
||||||
package bulb
|
package bulb
|
||||||
|
@ -37,16 +37,27 @@ type OnionPortSpec struct {
|
||||||
Target string
|
Target string
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddOnion issues an ADD_ONION command and returns the parsed response.
|
// NewOnionConfig is a configuration for NewOnion command.
|
||||||
func (c *Conn) AddOnion(ports []OnionPortSpec, key crypto.PrivateKey, oneshot bool) (*OnionInfo, error) {
|
type NewOnionConfig struct {
|
||||||
|
PortSpecs []OnionPortSpec
|
||||||
|
PrivateKey crypto.PrivateKey
|
||||||
|
DiscardPK bool
|
||||||
|
Detach bool
|
||||||
|
BasicAuth bool
|
||||||
|
NonAnonymous bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewOnion issues an ADD_ONION command using configuration config and
|
||||||
|
// returns the parsed response.
|
||||||
|
func (c *Conn) NewOnion(config *NewOnionConfig) (*OnionInfo, error) {
|
||||||
const keyTypeRSA = "RSA1024"
|
const keyTypeRSA = "RSA1024"
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
var portStr string
|
var portStr string
|
||||||
if ports == nil {
|
if config.PortSpecs == nil {
|
||||||
return nil, newProtocolError("invalid port specification")
|
return nil, newProtocolError("invalid port specification")
|
||||||
}
|
}
|
||||||
for _, v := range ports {
|
for _, v := range config.PortSpecs {
|
||||||
portStr += fmt.Sprintf(" Port=%d", v.VirtPort)
|
portStr += fmt.Sprintf(" Port=%d", v.VirtPort)
|
||||||
if v.Target != "" {
|
if v.Target != "" {
|
||||||
portStr += "," + v.Target
|
portStr += "," + v.Target
|
||||||
|
@ -54,10 +65,10 @@ func (c *Conn) AddOnion(ports []OnionPortSpec, key crypto.PrivateKey, oneshot bo
|
||||||
}
|
}
|
||||||
|
|
||||||
var hsKeyType, hsKeyStr string
|
var hsKeyType, hsKeyStr string
|
||||||
if key != nil {
|
if config.PrivateKey != nil {
|
||||||
switch t := key.(type) {
|
switch t := config.PrivateKey.(type) {
|
||||||
case *rsa.PrivateKey:
|
case *rsa.PrivateKey:
|
||||||
rsaPK, _ := key.(*rsa.PrivateKey)
|
rsaPK, _ := config.PrivateKey.(*rsa.PrivateKey)
|
||||||
if rsaPK.N.BitLen() != 1024 {
|
if rsaPK.N.BitLen() != 1024 {
|
||||||
return nil, newProtocolError("invalid RSA key size")
|
return nil, newProtocolError("invalid RSA key size")
|
||||||
}
|
}
|
||||||
|
@ -68,24 +79,38 @@ func (c *Conn) AddOnion(ports []OnionPortSpec, key crypto.PrivateKey, oneshot bo
|
||||||
hsKeyType = keyTypeRSA
|
hsKeyType = keyTypeRSA
|
||||||
hsKeyStr = base64.StdEncoding.EncodeToString(pkDER)
|
hsKeyStr = base64.StdEncoding.EncodeToString(pkDER)
|
||||||
case *OnionPrivateKey:
|
case *OnionPrivateKey:
|
||||||
genericPK, _ := key.(*OnionPrivateKey)
|
genericPK, _ := config.PrivateKey.(*OnionPrivateKey)
|
||||||
hsKeyType = genericPK.KeyType
|
hsKeyType = genericPK.KeyType
|
||||||
hsKeyStr = genericPK.Key
|
hsKeyStr = genericPK.Key
|
||||||
default:
|
default:
|
||||||
return nil, newProtocolError("unsupported private key type: %v", t)
|
return nil, newProtocolError("unsupported private key type: %v", t)
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
hsKeyStr = "BEST"
|
||||||
|
hsKeyType = "NEW"
|
||||||
}
|
}
|
||||||
|
|
||||||
var resp *Response
|
var flags []string
|
||||||
if hsKeyStr == "" {
|
var flagsStr string
|
||||||
flags := " Flags=DiscardPK"
|
|
||||||
if !oneshot {
|
if config.DiscardPK {
|
||||||
flags = ""
|
flags = append(flags, "DiscardPK")
|
||||||
}
|
|
||||||
resp, err = c.Request("ADD_ONION NEW:BEST%s%s", portStr, flags)
|
|
||||||
} else {
|
|
||||||
resp, err = c.Request("ADD_ONION %s:%s%s", hsKeyType, hsKeyStr, portStr)
|
|
||||||
}
|
}
|
||||||
|
if config.Detach {
|
||||||
|
flags = append(flags, "Detach")
|
||||||
|
}
|
||||||
|
if config.BasicAuth {
|
||||||
|
flags = append(flags, "BasicAuth")
|
||||||
|
}
|
||||||
|
if config.NonAnonymous {
|
||||||
|
flags = append(flags, "NonAnonymous")
|
||||||
|
}
|
||||||
|
if flags != nil {
|
||||||
|
flagsStr = " Flags="
|
||||||
|
flagsStr += strings.Join(flags, ",")
|
||||||
|
}
|
||||||
|
request := fmt.Sprintf("ADD_ONION %s:%s%s%s", hsKeyType, hsKeyStr, portStr, flagsStr)
|
||||||
|
resp, err := c.Request(request)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -102,7 +127,7 @@ func (c *Conn) AddOnion(ports []OnionPortSpec, key crypto.PrivateKey, oneshot bo
|
||||||
if strings.HasPrefix(l, serviceIDPrefix) {
|
if strings.HasPrefix(l, serviceIDPrefix) {
|
||||||
serviceID = strings.TrimPrefix(l, serviceIDPrefix)
|
serviceID = strings.TrimPrefix(l, serviceIDPrefix)
|
||||||
} else if strings.HasPrefix(l, privateKeyPrefix) {
|
} else if strings.HasPrefix(l, privateKeyPrefix) {
|
||||||
if oneshot || hsKeyStr != "" {
|
if config.DiscardPK || hsKeyStr != "" {
|
||||||
return nil, newProtocolError("received an unexpected private key")
|
return nil, newProtocolError("received an unexpected private key")
|
||||||
}
|
}
|
||||||
hsKeyStr = strings.TrimPrefix(l, privateKeyPrefix)
|
hsKeyStr = strings.TrimPrefix(l, privateKeyPrefix)
|
||||||
|
@ -142,6 +167,18 @@ func (c *Conn) AddOnion(ports []OnionPortSpec, key crypto.PrivateKey, oneshot bo
|
||||||
return oi, nil
|
return oi, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// [DEPRECATED] AddOnion issues an ADD_ONION command and
|
||||||
|
// returns the parsed response.
|
||||||
|
func (c *Conn) AddOnion(ports []OnionPortSpec, key crypto.PrivateKey, oneshot bool) (*OnionInfo, error) {
|
||||||
|
cfg := &NewOnionConfig{}
|
||||||
|
cfg.PortSpecs = ports
|
||||||
|
if key != nil {
|
||||||
|
cfg.PrivateKey = key
|
||||||
|
}
|
||||||
|
cfg.DiscardPK = oneshot
|
||||||
|
return c.NewOnion(cfg)
|
||||||
|
}
|
||||||
|
|
||||||
// DeleteOnion issues a DEL_ONION command and returns the parsed response.
|
// DeleteOnion issues a DEL_ONION command and returns the parsed response.
|
||||||
func (c *Conn) DeleteOnion(serviceID string) error {
|
func (c *Conn) DeleteOnion(serviceID string) error {
|
||||||
_, err := c.Request("DEL_ONION %s", serviceID)
|
_, err := c.Request("DEL_ONION %s", serviceID)
|
||||||
|
|
|
@ -55,7 +55,11 @@ func main() {
|
||||||
}
|
}
|
||||||
log.Printf("Expected ID: %v", id)
|
log.Printf("Expected ID: %v", id)
|
||||||
|
|
||||||
l, err := c.Listener(80, pk)
|
cfg := &bulb.NewOnionConfig{
|
||||||
|
DiscardPK: true,
|
||||||
|
PrivateKey: pk,
|
||||||
|
}
|
||||||
|
l, err := c.NewListener(cfg, 80)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Failed to get Listener: %v", err)
|
log.Fatalf("Failed to get Listener: %v", err)
|
||||||
}
|
}
|
||||||
|
|
63
listener.go
63
listener.go
|
@ -1,8 +1,8 @@
|
||||||
// listener.go - Tor backed net.Listener.
|
// listener.go - Tor backed net.Listener.
|
||||||
//
|
//
|
||||||
// To the extent possible under law, Yawning Angel waived all copyright
|
// To the extent possible under law, Yawning Angel and Ivan Markin
|
||||||
// and related or neighboring rights to bulb, using the creative
|
// waived all copyright and related or neighboring rights to bulb, using
|
||||||
// commons "cc0" public domain dedication. See LICENSE or
|
// the creative commons "cc0" public domain dedication. See LICENSE or
|
||||||
// <http://creativecommons.org/publicdomain/zero/1.0/> for full details.
|
// <http://creativecommons.org/publicdomain/zero/1.0/> for full details.
|
||||||
|
|
||||||
package bulb
|
package bulb
|
||||||
|
@ -49,17 +49,23 @@ func (l *onionListener) Addr() net.Addr {
|
||||||
return l.addr
|
return l.addr
|
||||||
}
|
}
|
||||||
|
|
||||||
// Listener returns a net.Listener backed by a Onion Service, optionally
|
// NewListener returns a net.Listener backed by an Onion Service using configuration
|
||||||
// having Tor generate an ephemeral private key. Regardless of the status of
|
// config, optionally having Tor generate an ephemeral private key (config is nil or
|
||||||
// the returned Listener, the Onion Service will be torn down when the control
|
// config.PrivateKey is nil).
|
||||||
// connection is closed.
|
// All of virtual ports specified in vports will be mapped to the port to which
|
||||||
//
|
// the underlying TCP listener binded. PortSpecs in config will be ignored since
|
||||||
// WARNING: Only one port can be listened to per PrivateKey if this interface
|
// there is only one mapping for a vports set is possible.
|
||||||
// is used. To bind to more ports, use the AddOnion call directly.
|
func (c *Conn) NewListener(config *NewOnionConfig, vports ...uint16) (net.Listener, error) {
|
||||||
func (c *Conn) Listener(port uint16, key crypto.PrivateKey) (net.Listener, error) {
|
var cfg NewOnionConfig
|
||||||
const (
|
if config == nil {
|
||||||
loopbackAddr = "127.0.0.1:0"
|
cfg = NewOnionConfig{
|
||||||
)
|
DiscardPK: true,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
cfg = *config
|
||||||
|
}
|
||||||
|
|
||||||
|
const loopbackAddr = "127.0.0.1:0"
|
||||||
|
|
||||||
// Listen on the loopback interface.
|
// Listen on the loopback interface.
|
||||||
tcpListener, err := net.Listen("tcp4", loopbackAddr)
|
tcpListener, err := net.Listen("tcp4", loopbackAddr)
|
||||||
|
@ -72,16 +78,39 @@ func (c *Conn) Listener(port uint16, key crypto.PrivateKey) (net.Listener, error
|
||||||
return nil, newProtocolError("failed to extract local port")
|
return nil, newProtocolError("failed to extract local port")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if len(vports) < 1 {
|
||||||
|
return nil, newProtocolError("no virual ports specified")
|
||||||
|
}
|
||||||
|
targetPortStr := strconv.FormatUint((uint64)(tAddr.Port), 10)
|
||||||
|
var portSpecs []OnionPortSpec
|
||||||
|
for _, vport := range vports {
|
||||||
|
portSpecs = append(portSpecs, OnionPortSpec{
|
||||||
|
VirtPort: vport,
|
||||||
|
Target: targetPortStr,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
cfg.PortSpecs = portSpecs
|
||||||
// Create the onion.
|
// Create the onion.
|
||||||
ports := []OnionPortSpec{{port, strconv.FormatUint((uint64)(tAddr.Port), 10)}}
|
oi, err := c.NewOnion(&cfg)
|
||||||
oi, err := c.AddOnion(ports, key, key == nil)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
tcpListener.Close()
|
tcpListener.Close()
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
oa := &onionAddr{info: oi, port: port}
|
oa := &onionAddr{info: oi, port: vports[0]}
|
||||||
ol := &onionListener{addr: oa, ctrlConn: c, listener: tcpListener}
|
ol := &onionListener{addr: oa, ctrlConn: c, listener: tcpListener}
|
||||||
|
|
||||||
return ol, nil
|
return ol, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// [DEPRECATED] Listener returns a net.Listener backed by an Onion Service.
|
||||||
|
func (c *Conn) Listener(port uint16, key crypto.PrivateKey) (net.Listener, error) {
|
||||||
|
cfg := &NewOnionConfig{}
|
||||||
|
if key != nil {
|
||||||
|
cfg.PrivateKey = key
|
||||||
|
cfg.DiscardPK = false
|
||||||
|
} else {
|
||||||
|
cfg.DiscardPK = true
|
||||||
|
}
|
||||||
|
return c.NewListener(cfg, port)
|
||||||
|
}
|
||||||
|
|
Reference in New Issue