From 7d26c026b245ce11312332665e6cb6407b7287a7 Mon Sep 17 00:00:00 2001 From: Yawning Angel Date: Sat, 10 Oct 2015 15:01:05 +0000 Subject: [PATCH] Added c.Listener() which returns a net.Listener. Likewise, different from David S' implementation. --- examples/listener/listener.go | 51 ++++++++++++++++++++ listener.go | 87 +++++++++++++++++++++++++++++++++++ 2 files changed, 138 insertions(+) create mode 100644 examples/listener/listener.go create mode 100644 listener.go diff --git a/examples/listener/listener.go b/examples/listener/listener.go new file mode 100644 index 0000000..d017f43 --- /dev/null +++ b/examples/listener/listener.go @@ -0,0 +1,51 @@ +// Listener example. +// +// 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 +// for full details. + +package main + +import ( + "io" + "log" + "net/http" + + "github.com/yawning/bulb" +) + +func onionServer(w http.ResponseWriter, req *http.Request) { + io.WriteString(w, "hello, onion world!\n") +} + +func main() { + // Connect to a running tor instance. + // * TCP: c, err := bulb.Dial("tcp4", "127.0.0.1:9051") + c, err := bulb.Dial("unix", "/var/run/tor/control") + if err != nil { + log.Fatalf("failed to connect to control port: %v", err) + } + defer c.Close() + + // See what's really going on under the hood. + // Do not enable in production. + c.Debug(true) + + // Authenticate with the control port. The password argument + // here can be "" if no password is set (CookieAuth, no auth). + if err := c.Authenticate("ExamplePassword"); err != nil { + log.Fatalf("Authentication failed: %v", err) + } + + // Create an ephemeral port 80 Onion Service. + l, err := c.Listener(80, nil) + if err != nil { + log.Fatalf("Failed to get Listener: %v", err) + } + defer l.Close() + + log.Printf("Listener: %s", l.Addr()) + http.HandleFunc("/", onionServer) + http.Serve(l, nil) +} diff --git a/listener.go b/listener.go new file mode 100644 index 0000000..b7ddfa8 --- /dev/null +++ b/listener.go @@ -0,0 +1,87 @@ +// 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 +// for full details. + +package bulb + +import ( + "crypto" + "fmt" + "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 +} + +// 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" + ) + + // 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") + } + + // Create the onion. + ports := []OnionPortSpec{{port, strconv.FormatUint((uint64)(tAddr.Port), 10)}} + oi, err := c.AddOnion(ports, key, key == nil) + if err != nil { + tcpListener.Close() + return nil, err + } + + oa := &onionAddr{info: oi, port: port} + ol := &onionListener{addr: oa, ctrlConn: c, listener: tcpListener} + + return ol, nil +}