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...
|
||||
//
|
||||
// To the extent possible under law, David Stainton waived all copyright
|
||||
// and related or neighboring rights to this module of bulb, using the creative
|
||||
// commons "cc0" public domain dedication. See LICENSE or
|
||||
// To the extent possible under law, David Stainton and Ivan Markin waived
|
||||
// all copyright and related or neighboring rights to this module of bulb,
|
||||
// using the creative commons "cc0" public domain dedication. See LICENSE or
|
||||
// <http://creativecommons.org/publicdomain/zero/1.0/> for full details.
|
||||
|
||||
package bulb
|
||||
|
@ -37,16 +37,27 @@ type OnionPortSpec struct {
|
|||
Target string
|
||||
}
|
||||
|
||||
// AddOnion issues an ADD_ONION command and returns the parsed response.
|
||||
func (c *Conn) AddOnion(ports []OnionPortSpec, key crypto.PrivateKey, oneshot bool) (*OnionInfo, error) {
|
||||
// NewOnionConfig is a configuration for NewOnion command.
|
||||
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"
|
||||
var err error
|
||||
|
||||
var portStr string
|
||||
if ports == nil {
|
||||
if config.PortSpecs == nil {
|
||||
return nil, newProtocolError("invalid port specification")
|
||||
}
|
||||
for _, v := range ports {
|
||||
for _, v := range config.PortSpecs {
|
||||
portStr += fmt.Sprintf(" Port=%d", v.VirtPort)
|
||||
if v.Target != "" {
|
||||
portStr += "," + v.Target
|
||||
|
@ -54,10 +65,10 @@ func (c *Conn) AddOnion(ports []OnionPortSpec, key crypto.PrivateKey, oneshot bo
|
|||
}
|
||||
|
||||
var hsKeyType, hsKeyStr string
|
||||
if key != nil {
|
||||
switch t := key.(type) {
|
||||
if config.PrivateKey != nil {
|
||||
switch t := config.PrivateKey.(type) {
|
||||
case *rsa.PrivateKey:
|
||||
rsaPK, _ := key.(*rsa.PrivateKey)
|
||||
rsaPK, _ := config.PrivateKey.(*rsa.PrivateKey)
|
||||
if rsaPK.N.BitLen() != 1024 {
|
||||
return nil, newProtocolError("invalid RSA key size")
|
||||
}
|
||||
|
@ -68,24 +79,38 @@ func (c *Conn) AddOnion(ports []OnionPortSpec, key crypto.PrivateKey, oneshot bo
|
|||
hsKeyType = keyTypeRSA
|
||||
hsKeyStr = base64.StdEncoding.EncodeToString(pkDER)
|
||||
case *OnionPrivateKey:
|
||||
genericPK, _ := key.(*OnionPrivateKey)
|
||||
genericPK, _ := config.PrivateKey.(*OnionPrivateKey)
|
||||
hsKeyType = genericPK.KeyType
|
||||
hsKeyStr = genericPK.Key
|
||||
default:
|
||||
return nil, newProtocolError("unsupported private key type: %v", t)
|
||||
}
|
||||
} else {
|
||||
hsKeyStr = "BEST"
|
||||
hsKeyType = "NEW"
|
||||
}
|
||||
|
||||
var resp *Response
|
||||
if hsKeyStr == "" {
|
||||
flags := " Flags=DiscardPK"
|
||||
if !oneshot {
|
||||
flags = ""
|
||||
}
|
||||
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)
|
||||
var flags []string
|
||||
var flagsStr string
|
||||
|
||||
if config.DiscardPK {
|
||||
flags = append(flags, "DiscardPK")
|
||||
}
|
||||
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 {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -102,7 +127,7 @@ func (c *Conn) AddOnion(ports []OnionPortSpec, key crypto.PrivateKey, oneshot bo
|
|||
if strings.HasPrefix(l, serviceIDPrefix) {
|
||||
serviceID = strings.TrimPrefix(l, serviceIDPrefix)
|
||||
} else if strings.HasPrefix(l, privateKeyPrefix) {
|
||||
if oneshot || hsKeyStr != "" {
|
||||
if config.DiscardPK || hsKeyStr != "" {
|
||||
return nil, newProtocolError("received an unexpected private key")
|
||||
}
|
||||
hsKeyStr = strings.TrimPrefix(l, privateKeyPrefix)
|
||||
|
@ -142,6 +167,18 @@ func (c *Conn) AddOnion(ports []OnionPortSpec, key crypto.PrivateKey, oneshot bo
|
|||
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.
|
||||
func (c *Conn) DeleteOnion(serviceID string) error {
|
||||
_, err := c.Request("DEL_ONION %s", serviceID)
|
||||
|
|
|
@ -55,7 +55,11 @@ func main() {
|
|||
}
|
||||
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 {
|
||||
log.Fatalf("Failed to get Listener: %v", err)
|
||||
}
|
||||
|
|
63
listener.go
63
listener.go
|
@ -1,8 +1,8 @@
|
|||
// listener.go - Tor backed net.Listener.
|
||||
//
|
||||
// To the extent possible under law, Yawning Angel waived all copyright
|
||||
// and related or neighboring rights to bulb, using the creative
|
||||
// commons "cc0" public domain dedication. See LICENSE or
|
||||
// To the extent possible under law, Yawning Angel and Ivan Markin
|
||||
// waived all copyright and related or neighboring rights to bulb, using
|
||||
// the creative commons "cc0" public domain dedication. See LICENSE or
|
||||
// <http://creativecommons.org/publicdomain/zero/1.0/> for full details.
|
||||
|
||||
package bulb
|
||||
|
@ -49,17 +49,23 @@ func (l *onionListener) Addr() net.Addr {
|
|||
return l.addr
|
||||
}
|
||||
|
||||
// Listener returns a net.Listener backed by a Onion Service, optionally
|
||||
// having Tor generate an ephemeral private key. Regardless of the status of
|
||||
// the returned Listener, the Onion Service will be torn down when the control
|
||||
// connection is closed.
|
||||
//
|
||||
// WARNING: Only one port can be listened to per PrivateKey if this interface
|
||||
// is used. To bind to more ports, use the AddOnion call directly.
|
||||
func (c *Conn) Listener(port uint16, key crypto.PrivateKey) (net.Listener, error) {
|
||||
const (
|
||||
loopbackAddr = "127.0.0.1:0"
|
||||
)
|
||||
// NewListener returns a net.Listener backed by an Onion Service using configuration
|
||||
// config, optionally having Tor generate an ephemeral private key (config is nil or
|
||||
// config.PrivateKey is nil).
|
||||
// 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
|
||||
// there is only one mapping for a vports set is possible.
|
||||
func (c *Conn) NewListener(config *NewOnionConfig, vports ...uint16) (net.Listener, error) {
|
||||
var cfg NewOnionConfig
|
||||
if config == nil {
|
||||
cfg = NewOnionConfig{
|
||||
DiscardPK: true,
|
||||
}
|
||||
} else {
|
||||
cfg = *config
|
||||
}
|
||||
|
||||
const loopbackAddr = "127.0.0.1:0"
|
||||
|
||||
// Listen on the loopback interface.
|
||||
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")
|
||||
}
|
||||
|
||||
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.
|
||||
ports := []OnionPortSpec{{port, strconv.FormatUint((uint64)(tAddr.Port), 10)}}
|
||||
oi, err := c.AddOnion(ports, key, key == nil)
|
||||
oi, err := c.NewOnion(&cfg)
|
||||
if err != nil {
|
||||
tcpListener.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
oa := &onionAddr{info: oi, port: port}
|
||||
oa := &onionAddr{info: oi, port: vports[0]}
|
||||
ol := &onionListener{addr: oa, ctrlConn: c, listener: tcpListener}
|
||||
|
||||
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