fork of bulb which is not stem (for tor control port interaction in go) DEPRECATED in favor of bine
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

139 lines
3.7 KiB

// listener.go - Tor backed net.Listener.
//
// 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 asaur
import (
"crypto"
goodrand "crypto/rand"
"encoding/binary"
"fmt"
"golang.org/x/crypto/sha3"
"log"
"net"
"strconv"
)
type onionAddr struct {
info *OnionInfo
port uint16
}
func (a *onionAddr) Network() string {
return "tcp"
}
func (a *onionAddr) String() string {
return fmt.Sprintf("%s.onion:%d", a.info.OnionID, a.port)
}
type onionListener struct {
addr *onionAddr
ctrlConn *Conn
listener net.Listener
}
func (l *onionListener) Accept() (net.Conn, error) {
return l.listener.Accept()
}
func (l *onionListener) Close() (err error) {
if err = l.listener.Close(); err == nil {
// Only delete the onion once.
err = l.ctrlConn.DeleteOnion(l.addr.info.OnionID)
}
return err
}
func (l *onionListener) Addr() net.Addr {
return l.addr
}
// 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.
// [DEPRECATED] generate a random port to listen on (this is for backwards compatibility)
func (c *Conn) NewListener(config *NewOnionConfig, vports ...uint16) (net.Listener, error) {
return c.RecoverListener(config, "", vports...)
}
func (c *Conn) RecoverListener(config *NewOnionConfig, onion string, vports ...uint16) (net.Listener, error) {
var cfg NewOnionConfig
if config == nil {
cfg = NewOnionConfig{
DiscardPK: true,
}
} else {
cfg = *config
}
var port int
if onion == "" { // generate a random port for ephemeral programs
seedbytes := make([]byte, 2)
goodrand.Read(seedbytes)
port = int(binary.LittleEndian.Uint16(seedbytes))
} else { // generate a deterministic port for resumptive programs
seedbytes := sha3.New224().Sum([]byte(onion))
port = int(seedbytes[0]) + (int(seedbytes[1]) << 8)
}
if port < 1024 { // this is not uniformly random, but we don't need it to be
port += 1024
}
log.Printf("using local port: %d\n", port)
var loopbackAddr = "127.0.0.1:" + strconv.Itoa(port)
// Listen on the loopback interface.
tcpListener, err := net.Listen("tcp4", loopbackAddr)
if err != nil {
return nil, err
}
tAddr, ok := tcpListener.Addr().(*net.TCPAddr)
if !ok {
tcpListener.Close()
return nil, newProtocolError("failed to extract local port")
}
if len(vports) < 1 {
return nil, newProtocolError("no virtual 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.
oi, err := c.NewOnion(&cfg, false)
if err != nil {
tcpListener.Close()
return nil, err
}
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)
}