// 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 // 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) }