core: Add functions for publishing onion services
This commit is contained in:
parent
bec3784f2f
commit
be5a8f1f4e
125
core/network.go
125
core/network.go
|
@ -1,12 +1,14 @@
|
||||||
package core
|
package core
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto"
|
||||||
"errors"
|
"errors"
|
||||||
"github.com/special/notricochet/core/utils"
|
"github.com/special/notricochet/core/utils"
|
||||||
"github.com/special/notricochet/rpc"
|
"github.com/special/notricochet/rpc"
|
||||||
"github.com/yawning/bulb"
|
"github.com/yawning/bulb"
|
||||||
bulbutils "github.com/yawning/bulb/utils"
|
bulbutils "github.com/yawning/bulb/utils"
|
||||||
"log"
|
"log"
|
||||||
|
"net"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
@ -36,6 +38,14 @@ type Network struct {
|
||||||
// pointers and may be shared. Instead, construct a new TorControlStatus
|
// pointers and may be shared. Instead, construct a new TorControlStatus
|
||||||
// et al for each change.
|
// et al for each change.
|
||||||
status ricochet.NetworkStatus
|
status ricochet.NetworkStatus
|
||||||
|
|
||||||
|
onions []*OnionService
|
||||||
|
}
|
||||||
|
|
||||||
|
type OnionService struct {
|
||||||
|
OnionID string
|
||||||
|
Ports []bulb.OnionPortSpec
|
||||||
|
PrivateKey crypto.PrivateKey
|
||||||
}
|
}
|
||||||
|
|
||||||
func CreateNetwork() *Network {
|
func CreateNetwork() *Network {
|
||||||
|
@ -106,6 +116,98 @@ func (n *Network) GetStatus() ricochet.NetworkStatus {
|
||||||
return status
|
return status
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Return the control connection, blocking until connected if necessary
|
||||||
|
// May return nil on failure, and the returned connection can be closed
|
||||||
|
// or otherwise fail at any time.
|
||||||
|
func (n *Network) getConnection() *bulb.Conn {
|
||||||
|
// Optimistically try to get a connection before subscribin to events
|
||||||
|
n.controlMutex.Lock()
|
||||||
|
conn := n.conn
|
||||||
|
n.controlMutex.Unlock()
|
||||||
|
if conn != nil {
|
||||||
|
return conn
|
||||||
|
}
|
||||||
|
|
||||||
|
// Subscribe to connectivity change events
|
||||||
|
monitor := n.EventMonitor().Subscribe(20)
|
||||||
|
defer n.EventMonitor().Unsubscribe(monitor)
|
||||||
|
|
||||||
|
for {
|
||||||
|
// Check for connectivity; do this before blocking to avoid a
|
||||||
|
// race with the subscription.
|
||||||
|
n.controlMutex.Lock()
|
||||||
|
conn := n.conn
|
||||||
|
n.controlMutex.Unlock()
|
||||||
|
if conn != nil {
|
||||||
|
return conn
|
||||||
|
}
|
||||||
|
|
||||||
|
_, ok := <-monitor
|
||||||
|
if !ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add an onion service with the provided port mappings and private key.
|
||||||
|
// If key is nil, a new RSA key is generated and returned in OnionService.
|
||||||
|
// This function will block until a control connection is available and
|
||||||
|
// the service is added or the command has failed. If the control connection
|
||||||
|
// is lost and reconnected, the service will be re-added automatically.
|
||||||
|
// BUG: Errors that occur after reconnecting cannot be detected.
|
||||||
|
func (n *Network) AddOnionPorts(ports []bulb.OnionPortSpec, key crypto.PrivateKey) (*OnionService, error) {
|
||||||
|
if key == nil {
|
||||||
|
// Ask for a new key, force RSA1024
|
||||||
|
key = &bulb.OnionPrivateKey{
|
||||||
|
KeyType: "NEW",
|
||||||
|
Key: "RSA1024",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
conn := n.getConnection()
|
||||||
|
info, err := conn.AddOnion(ports, key, false)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
service := &OnionService{
|
||||||
|
OnionID: info.OnionID,
|
||||||
|
Ports: ports,
|
||||||
|
PrivateKey: info.PrivateKey,
|
||||||
|
}
|
||||||
|
|
||||||
|
n.controlMutex.Lock()
|
||||||
|
n.onions = append(n.onions, service)
|
||||||
|
n.controlMutex.Unlock()
|
||||||
|
return service, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add an onion service listening on the virtual onion port onionPort,
|
||||||
|
// with the provided private key, and return a net.Listener for it. This
|
||||||
|
// function behaves identically to AddOnionPorts, other than creating a
|
||||||
|
// listener automatically.
|
||||||
|
func (n *Network) NewOnionListener(onionPort uint16, key crypto.PrivateKey) (*OnionService, net.Listener, error) {
|
||||||
|
listener, err := net.Listen("tcp", "127.0.0.1:0")
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
onionPorts := []bulb.OnionPortSpec{
|
||||||
|
bulb.OnionPortSpec{
|
||||||
|
VirtPort: onionPort,
|
||||||
|
Target: listener.Addr().String(),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
service, err := n.AddOnionPorts(onionPorts, key)
|
||||||
|
if err != nil {
|
||||||
|
listener.Close()
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return service, listener, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (n *Network) run(connectChannel chan<- error) {
|
func (n *Network) run(connectChannel chan<- error) {
|
||||||
n.controlMutex.Lock()
|
n.controlMutex.Lock()
|
||||||
stopSignal := n.stopSignal
|
stopSignal := n.stopSignal
|
||||||
|
@ -168,6 +270,9 @@ func (n *Network) run(connectChannel chan<- error) {
|
||||||
// signalled on connection failure. Block on retryChannel
|
// signalled on connection failure. Block on retryChannel
|
||||||
// below.
|
// below.
|
||||||
go n.handleControlEvents(conn, retryChannel)
|
go n.handleControlEvents(conn, retryChannel)
|
||||||
|
|
||||||
|
// Re-publish onion services
|
||||||
|
n.publishOnions()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -377,3 +482,23 @@ func (n *Network) handleControlEvents(conn *bulb.Conn, errorChannel chan<- error
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (n *Network) publishOnions() {
|
||||||
|
n.controlMutex.Lock()
|
||||||
|
conn := n.conn
|
||||||
|
onions := make([]*OnionService, len(n.onions))
|
||||||
|
copy(onions, n.onions)
|
||||||
|
n.controlMutex.Unlock()
|
||||||
|
|
||||||
|
if conn == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, service := range onions {
|
||||||
|
_, err := conn.AddOnion(service.Ports, service.PrivateKey, false)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Control error for onion republication: %v", err)
|
||||||
|
}
|
||||||
|
log.Printf("Re-published onion service %s", service.OnionID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue