[tor-commits] [websocket/master] Change back to "websocket" names from "pt-websocket" names.
dcf at torproject.org
dcf at torproject.org
Sat Dec 19 08:34:30 UTC 2015
commit 138301de19e0ec4726d98ae1eeed3b939da950a6
Author: David Fifield <david at bamsoftware.com>
Date: Fri Dec 18 21:25:44 2015 -0800
Change back to "websocket" names from "pt-websocket" names.
Distributors can rename the binaries if they want to.
This reverts commit c4ef06f097354214460d45207a0297eec79d3bbe.
This reverts commit f75ce00346237321622a3959997c707a1eaaf5ad.
---
.gitignore | 4 +-
Makefile | 22 +-
README | 2 +-
pt-websocket-client/pt-websocket-client.go | 263 ------------------------
pt-websocket-server/pt-websocket-server.go | 299 ----------------------------
websocket-client/websocket-client.go | 263 ++++++++++++++++++++++++
websocket-server/websocket-server.go | 299 ++++++++++++++++++++++++++++
7 files changed, 576 insertions(+), 576 deletions(-)
diff --git a/.gitignore b/.gitignore
index 1e915e0..1fee725 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,2 @@
-/pt-websocket-client/pt-websocket-client
-/pt-websocket-server/pt-websocket-server
+/websocket-client
+/websocket-server
diff --git a/Makefile b/Makefile
index 4882798..b723c86 100644
--- a/Makefile
+++ b/Makefile
@@ -9,26 +9,26 @@ GOBUILDFLAGS =
# apt-get install gccgo-multilib
# GOBUILDFLAGS = -compiler gccgo -gccgoflags "-O3 -m32 -static-libgo"
-all: pt-websocket-server/pt-websocket-server
+all: websocket-server/websocket-server
-pt-websocket-server/pt-websocket-server: pt-websocket-server/*.go websocket/*.go
- cd pt-websocket-server && go build $(GOBUILDFLAGS)
+websocket-server/websocket-server: websocket-server/*.go websocket/*.go
+ cd websocket-server && go build $(GOBUILDFLAGS)
-pt-websocket-client/pt-websocket-client: pt-websocket-client/*.go
- cd pt-websocket-client && go build $(GOBUILDFLAGS)
+websocket-client/websocket-client: websocket-client/*.go
+ cd websocket-client && go build $(GOBUILDFLAGS)
-doc/pt-websocket-server.1: pt-websocket-server/pt-websocket-server
+doc/websocket-server.1: websocket-server/websocket-server
help2man --no-info --name "WebSocket server pluggable transport" --version-string "$(VERSION)" -o "$@" "$<"
-install: pt-websocket-server/pt-websocket-server
+install: websocket-server/websocket-server
mkdir -p "$(DESTDIR)$(BINDIR)"
- cp -f "$<" "$(DESTDIR)$(BINDIR)"
+ cp -f websocket-server/websocket-server "$(DESTDIR)$(BINDIR)"
clean:
- rm -f pt-websocket-server/pt-websocket-server pt-websocket-client/pt-websocket-client
- rm -f doc/pt-websocket-server.1
+ rm -f websocket-server/websocket-server websocket-client/websocket-client
+ rm -f doc/websocket-server.1
fmt:
- go fmt ./pt-websocket-server ./pt-websocket-client ./websocket
+ go fmt ./websocket-server ./websocket-client ./websocket
.PHONY: all install clean fmt
diff --git a/README b/README
index f2844eb..718dd67 100644
--- a/README
+++ b/README
@@ -27,4 +27,4 @@ Add configuration like the following to the relay's torrc. You can
change the --port option; make sure that port is open in the firewall.
ExtORPort 6669
- ServerTransportPlugin websocket exec /usr/local/bin/pt-websocket-server --port 9901
+ ServerTransportPlugin websocket exec /usr/local/bin/websocket-server --port 9901
diff --git a/pt-websocket-client/pt-websocket-client.go b/pt-websocket-client/pt-websocket-client.go
deleted file mode 100644
index f65a9c9..0000000
--- a/pt-websocket-client/pt-websocket-client.go
+++ /dev/null
@@ -1,263 +0,0 @@
-// Tor websocket client transport plugin.
-//
-// Usage in torrc:
-// UseBridges 1
-// Bridge websocket X.X.X.X:YYYY
-// ClientTransportPlugin websocket exec ./websocket-client
-package main
-
-import (
- "code.google.com/p/go.net/websocket"
- "flag"
- "fmt"
- "io"
- "net"
- "net/url"
- "os"
- "os/signal"
- "sync"
- "syscall"
- "time"
-)
-
-import "git.torproject.org/pluggable-transports/goptlib.git"
-
-var ptInfo pt.ClientInfo
-
-const ptMethodName = "websocket"
-const bufSiz = 1500
-
-var logFile = os.Stderr
-
-// When a connection handler starts, +1 is written to this channel; when it
-// ends, -1 is written.
-var handlerChan = make(chan int)
-
-var logMutex sync.Mutex
-
-func usage() {
- fmt.Printf("Usage: %s [OPTIONS]\n", os.Args[0])
- fmt.Printf("WebSocket client pluggable transport for Tor.\n")
- fmt.Printf("Works only as a managed proxy.\n")
- fmt.Printf("\n")
- fmt.Printf(" -h, --help show this help.\n")
- fmt.Printf(" --log FILE log messages to FILE (default stderr).\n")
- fmt.Printf(" --socks ADDR listen for SOCKS on ADDR.\n")
-}
-
-func log(format string, v ...interface{}) {
- dateStr := time.Now().Format("2006-01-02 15:04:05")
- logMutex.Lock()
- defer logMutex.Unlock()
- msg := fmt.Sprintf(format, v...)
- fmt.Fprintf(logFile, "%s %s\n", dateStr, msg)
-}
-
-func proxy(local *net.TCPConn, ws *websocket.Conn) {
- var wg sync.WaitGroup
-
- wg.Add(2)
-
- // Local-to-WebSocket read loop.
- go func() {
- buf := make([]byte, bufSiz)
- var err error
- for {
- n, er := local.Read(buf[:])
- if n > 0 {
- ew := websocket.Message.Send(ws, buf[:n])
- if ew != nil {
- err = ew
- break
- }
- }
- if er != nil {
- err = er
- break
- }
- }
- if err != nil && err != io.EOF {
- log("%s", err)
- }
- local.CloseRead()
- ws.Close()
-
- wg.Done()
- }()
-
- // WebSocket-to-local read loop.
- go func() {
- var buf []byte
- var err error
- for {
- er := websocket.Message.Receive(ws, &buf)
- if er != nil {
- err = er
- break
- }
- n, ew := local.Write(buf)
- if ew != nil {
- err = ew
- break
- }
- if n != len(buf) {
- err = io.ErrShortWrite
- break
- }
- }
- if err != nil && err != io.EOF {
- log("%s", err)
- }
- local.CloseWrite()
- ws.Close()
-
- wg.Done()
- }()
-
- wg.Wait()
-}
-
-func handleConnection(conn *pt.SocksConn) error {
- defer conn.Close()
-
- handlerChan <- 1
- defer func() {
- handlerChan <- -1
- }()
-
- var ws *websocket.Conn
-
- log("SOCKS request for %s", conn.Req.Target)
- destAddr, err := net.ResolveTCPAddr("tcp", conn.Req.Target)
- if err != nil {
- conn.Reject()
- return err
- }
- wsUrl := url.URL{Scheme: "ws", Host: conn.Req.Target}
- ws, err = websocket.Dial(wsUrl.String(), "", wsUrl.String())
- if err != nil {
- err = conn.Reject()
- return err
- }
- log("WebSocket connection to %s", ws.Config().Location.String())
- defer ws.Close()
- err = conn.Grant(destAddr)
- if err != nil {
- return err
- }
-
- proxy(conn.Conn.(*net.TCPConn), ws)
-
- return nil
-}
-
-func socksAcceptLoop(ln *pt.SocksListener) error {
- defer ln.Close()
- for {
- socks, err := ln.AcceptSocks()
- if err != nil {
- if e, ok := err.(*net.OpError); ok && e.Temporary() {
- continue
- }
- return err
- }
- go func() {
- err := handleConnection(socks)
- if err != nil {
- log("SOCKS from %s: %s", socks.RemoteAddr(), err)
- }
- }()
- }
- return nil
-}
-
-func startListener(addrStr string) (*pt.SocksListener, error) {
- ln, err := pt.ListenSocks("tcp", addrStr)
- if err != nil {
- return nil, err
- }
- go func() {
- err := socksAcceptLoop(ln)
- if err != nil {
- log("accept: %s", err)
- }
- }()
- return ln, nil
-}
-
-func main() {
- var logFilename string
- var socksAddrStr string
- var err error
-
- flag.Usage = usage
- flag.StringVar(&logFilename, "log", "", "log file to write to")
- flag.StringVar(&socksAddrStr, "socks", "127.0.0.1:0", "address on which to listen for SOCKS connections")
- flag.Parse()
-
- if logFilename != "" {
- f, err := os.OpenFile(logFilename, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0600)
- if err != nil {
- fmt.Fprintf(os.Stderr, "Can't open log file %q: %s.\n", logFilename, err.Error())
- os.Exit(1)
- }
- logFile = f
- }
-
- log("starting")
- ptInfo, err = pt.ClientSetup(nil)
- if err != nil {
- log("error in setup: %s", err)
- os.Exit(1)
- }
-
- listeners := make([]net.Listener, 0)
- for _, methodName := range ptInfo.MethodNames {
- switch methodName {
- case ptMethodName:
- ln, err := startListener(socksAddrStr)
- if err != nil {
- pt.CmethodError(ptMethodName, err.Error())
- break
- }
- pt.Cmethod(ptMethodName, ln.Version(), ln.Addr())
- log("listening on %s", ln.Addr().String())
- listeners = append(listeners, ln)
- default:
- pt.CmethodError(methodName, "no such method")
- }
- }
- pt.CmethodsDone()
-
- var numHandlers int = 0
- var sig os.Signal
- sigChan := make(chan os.Signal, 1)
- signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
-
- // wait for first signal
- sig = nil
- for sig == nil {
- select {
- case n := <-handlerChan:
- numHandlers += n
- case sig = <-sigChan:
- }
- }
- for _, ln := range listeners {
- ln.Close()
- }
-
- if sig == syscall.SIGTERM {
- return
- }
-
- // wait for second signal or no more handlers
- sig = nil
- for sig == nil && numHandlers != 0 {
- select {
- case n := <-handlerChan:
- numHandlers += n
- case sig = <-sigChan:
- }
- }
-}
diff --git a/pt-websocket-server/pt-websocket-server.go b/pt-websocket-server/pt-websocket-server.go
deleted file mode 100644
index b14c754..0000000
--- a/pt-websocket-server/pt-websocket-server.go
+++ /dev/null
@@ -1,299 +0,0 @@
-// Tor websocket server transport plugin.
-//
-// Usage in torrc:
-// ExtORPort 6669
-// ServerTransportPlugin websocket exec ./pt-websocket-server --port 9901
-package main
-
-import (
- "encoding/base64"
- "errors"
- "flag"
- "fmt"
- "io"
- "net"
- "net/http"
- "os"
- "os/signal"
- "sync"
- "syscall"
- "time"
-)
-
-import "../websocket"
-
-import "git.torproject.org/pluggable-transports/goptlib.git"
-
-const ptMethodName = "websocket"
-const requestTimeout = 10 * time.Second
-
-// "4/3+1" accounts for possible base64 encoding.
-const maxMessageSize = 64*1024*4/3 + 1
-
-var logFile = os.Stderr
-
-var ptInfo pt.ServerInfo
-
-// When a connection handler starts, +1 is written to this channel; when it
-// ends, -1 is written.
-var handlerChan = make(chan int)
-
-func usage() {
- fmt.Printf("Usage: %s [OPTIONS]\n", os.Args[0])
- fmt.Printf("WebSocket server pluggable transport for Tor.\n")
- fmt.Printf("Works only as a managed proxy.\n")
- fmt.Printf("\n")
- fmt.Printf(" -h, --help show this help.\n")
- fmt.Printf(" --log FILE log messages to FILE (default stderr).\n")
- fmt.Printf(" --port PORT listen on PORT (overrides Tor's requested port).\n")
-}
-
-var logMutex sync.Mutex
-
-func log(format string, v ...interface{}) {
- dateStr := time.Now().Format("2006-01-02 15:04:05")
- logMutex.Lock()
- defer logMutex.Unlock()
- msg := fmt.Sprintf(format, v...)
- fmt.Fprintf(logFile, "%s %s\n", dateStr, msg)
-}
-
-// An abstraction that makes an underlying WebSocket connection look like an
-// io.ReadWriteCloser. It internally takes care of things like base64 encoding
-// and decoding.
-type webSocketConn struct {
- Ws *websocket.WebSocket
- Base64 bool
- messageBuf []byte
-}
-
-// Implements io.Reader.
-func (conn *webSocketConn) Read(b []byte) (n int, err error) {
- for len(conn.messageBuf) == 0 {
- var m websocket.Message
- m, err = conn.Ws.ReadMessage()
- if err != nil {
- return
- }
- if m.Opcode == 8 {
- err = io.EOF
- return
- }
- if conn.Base64 {
- if m.Opcode != 1 {
- err = errors.New(fmt.Sprintf("got non-text opcode %d with the base64 subprotocol", m.Opcode))
- return
- }
- conn.messageBuf = make([]byte, base64.StdEncoding.DecodedLen(len(m.Payload)))
- var num int
- num, err = base64.StdEncoding.Decode(conn.messageBuf, m.Payload)
- if err != nil {
- return
- }
- conn.messageBuf = conn.messageBuf[:num]
- } else {
- if m.Opcode != 2 {
- err = errors.New(fmt.Sprintf("got non-binary opcode %d with no subprotocol", m.Opcode))
- return
- }
- conn.messageBuf = m.Payload
- }
- }
-
- n = copy(b, conn.messageBuf)
- conn.messageBuf = conn.messageBuf[n:]
-
- return
-}
-
-// Implements io.Writer.
-func (conn *webSocketConn) Write(b []byte) (n int, err error) {
- if conn.Base64 {
- buf := make([]byte, base64.StdEncoding.EncodedLen(len(b)))
- base64.StdEncoding.Encode(buf, b)
- err = conn.Ws.WriteMessage(1, buf)
- if err != nil {
- return
- }
- n = len(b)
- } else {
- err = conn.Ws.WriteMessage(2, b)
- n = len(b)
- }
- return
-}
-
-// Implements io.Closer.
-func (conn *webSocketConn) Close() error {
- // Ignore any error in trying to write a Close frame.
- _ = conn.Ws.WriteFrame(8, nil)
- return conn.Ws.Conn.Close()
-}
-
-// Create a new webSocketConn.
-func newWebSocketConn(ws *websocket.WebSocket) webSocketConn {
- var conn webSocketConn
- conn.Ws = ws
- conn.Base64 = (ws.Subprotocol == "base64")
- return conn
-}
-
-// Copy from WebSocket to socket and vice versa.
-func proxy(local *net.TCPConn, conn *webSocketConn) {
- var wg sync.WaitGroup
- wg.Add(2)
-
- go func() {
- _, err := io.Copy(conn, local)
- if err != nil {
- log("error copying ORPort to WebSocket")
- }
- local.CloseRead()
- conn.Close()
- wg.Done()
- }()
- go func() {
- _, err := io.Copy(local, conn)
- if err != nil {
- log("error copying WebSocket to ORPort")
- }
- local.CloseWrite()
- conn.Close()
- wg.Done()
- }()
-
- wg.Wait()
-}
-
-func webSocketHandler(ws *websocket.WebSocket) {
- // Undo timeouts on HTTP request handling.
- ws.Conn.SetDeadline(time.Time{})
- conn := newWebSocketConn(ws)
- defer conn.Close()
-
- handlerChan <- 1
- defer func() {
- handlerChan <- -1
- }()
-
- or, err := pt.DialOr(&ptInfo, ws.Conn.RemoteAddr().String(), ptMethodName)
- if err != nil {
- log("Failed to connect to ORPort: " + err.Error())
- return
- }
- defer or.Close()
-
- proxy(or, &conn)
-}
-
-func startListener(addr *net.TCPAddr) (*net.TCPListener, error) {
- ln, err := net.ListenTCP("tcp", addr)
- if err != nil {
- return nil, err
- }
- go func() {
- defer ln.Close()
- var config websocket.Config
- config.Subprotocols = []string{"base64"}
- config.MaxMessageSize = maxMessageSize
- s := &http.Server{
- Handler: config.Handler(webSocketHandler),
- ReadTimeout: requestTimeout,
- }
- err = s.Serve(ln)
- if err != nil {
- log("http.Serve: " + err.Error())
- }
- }()
- return ln, nil
-}
-
-func main() {
- var logFilename string
- var port int
-
- flag.Usage = usage
- flag.StringVar(&logFilename, "log", "", "log file to write to")
- flag.IntVar(&port, "port", 0, "port to listen on if unspecified by Tor")
- flag.Parse()
-
- if logFilename != "" {
- f, err := os.OpenFile(logFilename, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0600)
- if err != nil {
- fmt.Fprintf(os.Stderr, "Can't open log file %q: %s.\n", logFilename, err.Error())
- os.Exit(1)
- }
- logFile = f
- }
-
- log("starting")
- var err error
- ptInfo, err = pt.ServerSetup(nil)
- if err != nil {
- log("error in setup: %s", err)
- os.Exit(1)
- }
-
- listeners := make([]*net.TCPListener, 0)
- for _, bindaddr := range ptInfo.Bindaddrs {
- // Override tor's requested port (which is 0 if this transport
- // has not been run before) with the one requested by the --port
- // option.
- if port != 0 {
- bindaddr.Addr.Port = port
- }
-
- switch bindaddr.MethodName {
- case ptMethodName:
- ln, err := startListener(bindaddr.Addr)
- if err != nil {
- pt.SmethodError(bindaddr.MethodName, err.Error())
- break
- }
- pt.Smethod(bindaddr.MethodName, ln.Addr())
- log("listening on %s", ln.Addr().String())
- listeners = append(listeners, ln)
- default:
- pt.SmethodError(bindaddr.MethodName, "no such method")
- }
- }
- pt.SmethodsDone()
-
- var numHandlers int = 0
- var sig os.Signal
- sigChan := make(chan os.Signal, 1)
- signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
-
- // wait for first signal
- sig = nil
- for sig == nil {
- select {
- case n := <-handlerChan:
- numHandlers += n
- case sig = <-sigChan:
- }
- }
- log("Got first signal %q with %d running handlers.", sig, numHandlers)
- for _, ln := range listeners {
- ln.Close()
- }
-
- if sig == syscall.SIGTERM {
- log("Caught signal %q, exiting.", sig)
- return
- }
-
- // wait for second signal or no more handlers
- sig = nil
- for sig == nil && numHandlers != 0 {
- select {
- case n := <-handlerChan:
- numHandlers += n
- log("%d remaining handlers.", numHandlers)
- case sig = <-sigChan:
- }
- }
- if sig != nil {
- log("Got second signal %q with %d running handlers.", sig, numHandlers)
- }
-}
diff --git a/websocket-client/websocket-client.go b/websocket-client/websocket-client.go
new file mode 100644
index 0000000..f65a9c9
--- /dev/null
+++ b/websocket-client/websocket-client.go
@@ -0,0 +1,263 @@
+// Tor websocket client transport plugin.
+//
+// Usage in torrc:
+// UseBridges 1
+// Bridge websocket X.X.X.X:YYYY
+// ClientTransportPlugin websocket exec ./websocket-client
+package main
+
+import (
+ "code.google.com/p/go.net/websocket"
+ "flag"
+ "fmt"
+ "io"
+ "net"
+ "net/url"
+ "os"
+ "os/signal"
+ "sync"
+ "syscall"
+ "time"
+)
+
+import "git.torproject.org/pluggable-transports/goptlib.git"
+
+var ptInfo pt.ClientInfo
+
+const ptMethodName = "websocket"
+const bufSiz = 1500
+
+var logFile = os.Stderr
+
+// When a connection handler starts, +1 is written to this channel; when it
+// ends, -1 is written.
+var handlerChan = make(chan int)
+
+var logMutex sync.Mutex
+
+func usage() {
+ fmt.Printf("Usage: %s [OPTIONS]\n", os.Args[0])
+ fmt.Printf("WebSocket client pluggable transport for Tor.\n")
+ fmt.Printf("Works only as a managed proxy.\n")
+ fmt.Printf("\n")
+ fmt.Printf(" -h, --help show this help.\n")
+ fmt.Printf(" --log FILE log messages to FILE (default stderr).\n")
+ fmt.Printf(" --socks ADDR listen for SOCKS on ADDR.\n")
+}
+
+func log(format string, v ...interface{}) {
+ dateStr := time.Now().Format("2006-01-02 15:04:05")
+ logMutex.Lock()
+ defer logMutex.Unlock()
+ msg := fmt.Sprintf(format, v...)
+ fmt.Fprintf(logFile, "%s %s\n", dateStr, msg)
+}
+
+func proxy(local *net.TCPConn, ws *websocket.Conn) {
+ var wg sync.WaitGroup
+
+ wg.Add(2)
+
+ // Local-to-WebSocket read loop.
+ go func() {
+ buf := make([]byte, bufSiz)
+ var err error
+ for {
+ n, er := local.Read(buf[:])
+ if n > 0 {
+ ew := websocket.Message.Send(ws, buf[:n])
+ if ew != nil {
+ err = ew
+ break
+ }
+ }
+ if er != nil {
+ err = er
+ break
+ }
+ }
+ if err != nil && err != io.EOF {
+ log("%s", err)
+ }
+ local.CloseRead()
+ ws.Close()
+
+ wg.Done()
+ }()
+
+ // WebSocket-to-local read loop.
+ go func() {
+ var buf []byte
+ var err error
+ for {
+ er := websocket.Message.Receive(ws, &buf)
+ if er != nil {
+ err = er
+ break
+ }
+ n, ew := local.Write(buf)
+ if ew != nil {
+ err = ew
+ break
+ }
+ if n != len(buf) {
+ err = io.ErrShortWrite
+ break
+ }
+ }
+ if err != nil && err != io.EOF {
+ log("%s", err)
+ }
+ local.CloseWrite()
+ ws.Close()
+
+ wg.Done()
+ }()
+
+ wg.Wait()
+}
+
+func handleConnection(conn *pt.SocksConn) error {
+ defer conn.Close()
+
+ handlerChan <- 1
+ defer func() {
+ handlerChan <- -1
+ }()
+
+ var ws *websocket.Conn
+
+ log("SOCKS request for %s", conn.Req.Target)
+ destAddr, err := net.ResolveTCPAddr("tcp", conn.Req.Target)
+ if err != nil {
+ conn.Reject()
+ return err
+ }
+ wsUrl := url.URL{Scheme: "ws", Host: conn.Req.Target}
+ ws, err = websocket.Dial(wsUrl.String(), "", wsUrl.String())
+ if err != nil {
+ err = conn.Reject()
+ return err
+ }
+ log("WebSocket connection to %s", ws.Config().Location.String())
+ defer ws.Close()
+ err = conn.Grant(destAddr)
+ if err != nil {
+ return err
+ }
+
+ proxy(conn.Conn.(*net.TCPConn), ws)
+
+ return nil
+}
+
+func socksAcceptLoop(ln *pt.SocksListener) error {
+ defer ln.Close()
+ for {
+ socks, err := ln.AcceptSocks()
+ if err != nil {
+ if e, ok := err.(*net.OpError); ok && e.Temporary() {
+ continue
+ }
+ return err
+ }
+ go func() {
+ err := handleConnection(socks)
+ if err != nil {
+ log("SOCKS from %s: %s", socks.RemoteAddr(), err)
+ }
+ }()
+ }
+ return nil
+}
+
+func startListener(addrStr string) (*pt.SocksListener, error) {
+ ln, err := pt.ListenSocks("tcp", addrStr)
+ if err != nil {
+ return nil, err
+ }
+ go func() {
+ err := socksAcceptLoop(ln)
+ if err != nil {
+ log("accept: %s", err)
+ }
+ }()
+ return ln, nil
+}
+
+func main() {
+ var logFilename string
+ var socksAddrStr string
+ var err error
+
+ flag.Usage = usage
+ flag.StringVar(&logFilename, "log", "", "log file to write to")
+ flag.StringVar(&socksAddrStr, "socks", "127.0.0.1:0", "address on which to listen for SOCKS connections")
+ flag.Parse()
+
+ if logFilename != "" {
+ f, err := os.OpenFile(logFilename, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0600)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "Can't open log file %q: %s.\n", logFilename, err.Error())
+ os.Exit(1)
+ }
+ logFile = f
+ }
+
+ log("starting")
+ ptInfo, err = pt.ClientSetup(nil)
+ if err != nil {
+ log("error in setup: %s", err)
+ os.Exit(1)
+ }
+
+ listeners := make([]net.Listener, 0)
+ for _, methodName := range ptInfo.MethodNames {
+ switch methodName {
+ case ptMethodName:
+ ln, err := startListener(socksAddrStr)
+ if err != nil {
+ pt.CmethodError(ptMethodName, err.Error())
+ break
+ }
+ pt.Cmethod(ptMethodName, ln.Version(), ln.Addr())
+ log("listening on %s", ln.Addr().String())
+ listeners = append(listeners, ln)
+ default:
+ pt.CmethodError(methodName, "no such method")
+ }
+ }
+ pt.CmethodsDone()
+
+ var numHandlers int = 0
+ var sig os.Signal
+ sigChan := make(chan os.Signal, 1)
+ signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
+
+ // wait for first signal
+ sig = nil
+ for sig == nil {
+ select {
+ case n := <-handlerChan:
+ numHandlers += n
+ case sig = <-sigChan:
+ }
+ }
+ for _, ln := range listeners {
+ ln.Close()
+ }
+
+ if sig == syscall.SIGTERM {
+ return
+ }
+
+ // wait for second signal or no more handlers
+ sig = nil
+ for sig == nil && numHandlers != 0 {
+ select {
+ case n := <-handlerChan:
+ numHandlers += n
+ case sig = <-sigChan:
+ }
+ }
+}
diff --git a/websocket-server/websocket-server.go b/websocket-server/websocket-server.go
new file mode 100644
index 0000000..fbfffd8
--- /dev/null
+++ b/websocket-server/websocket-server.go
@@ -0,0 +1,299 @@
+// Tor websocket server transport plugin.
+//
+// Usage in torrc:
+// ExtORPort 6669
+// ServerTransportPlugin websocket exec ./websocket-server --port 9901
+package main
+
+import (
+ "encoding/base64"
+ "errors"
+ "flag"
+ "fmt"
+ "io"
+ "net"
+ "net/http"
+ "os"
+ "os/signal"
+ "sync"
+ "syscall"
+ "time"
+)
+
+import "../websocket"
+
+import "git.torproject.org/pluggable-transports/goptlib.git"
+
+const ptMethodName = "websocket"
+const requestTimeout = 10 * time.Second
+
+// "4/3+1" accounts for possible base64 encoding.
+const maxMessageSize = 64*1024*4/3 + 1
+
+var logFile = os.Stderr
+
+var ptInfo pt.ServerInfo
+
+// When a connection handler starts, +1 is written to this channel; when it
+// ends, -1 is written.
+var handlerChan = make(chan int)
+
+func usage() {
+ fmt.Printf("Usage: %s [OPTIONS]\n", os.Args[0])
+ fmt.Printf("WebSocket server pluggable transport for Tor.\n")
+ fmt.Printf("Works only as a managed proxy.\n")
+ fmt.Printf("\n")
+ fmt.Printf(" -h, --help show this help.\n")
+ fmt.Printf(" --log FILE log messages to FILE (default stderr).\n")
+ fmt.Printf(" --port PORT listen on PORT (overrides Tor's requested port).\n")
+}
+
+var logMutex sync.Mutex
+
+func log(format string, v ...interface{}) {
+ dateStr := time.Now().Format("2006-01-02 15:04:05")
+ logMutex.Lock()
+ defer logMutex.Unlock()
+ msg := fmt.Sprintf(format, v...)
+ fmt.Fprintf(logFile, "%s %s\n", dateStr, msg)
+}
+
+// An abstraction that makes an underlying WebSocket connection look like an
+// io.ReadWriteCloser. It internally takes care of things like base64 encoding
+// and decoding.
+type webSocketConn struct {
+ Ws *websocket.WebSocket
+ Base64 bool
+ messageBuf []byte
+}
+
+// Implements io.Reader.
+func (conn *webSocketConn) Read(b []byte) (n int, err error) {
+ for len(conn.messageBuf) == 0 {
+ var m websocket.Message
+ m, err = conn.Ws.ReadMessage()
+ if err != nil {
+ return
+ }
+ if m.Opcode == 8 {
+ err = io.EOF
+ return
+ }
+ if conn.Base64 {
+ if m.Opcode != 1 {
+ err = errors.New(fmt.Sprintf("got non-text opcode %d with the base64 subprotocol", m.Opcode))
+ return
+ }
+ conn.messageBuf = make([]byte, base64.StdEncoding.DecodedLen(len(m.Payload)))
+ var num int
+ num, err = base64.StdEncoding.Decode(conn.messageBuf, m.Payload)
+ if err != nil {
+ return
+ }
+ conn.messageBuf = conn.messageBuf[:num]
+ } else {
+ if m.Opcode != 2 {
+ err = errors.New(fmt.Sprintf("got non-binary opcode %d with no subprotocol", m.Opcode))
+ return
+ }
+ conn.messageBuf = m.Payload
+ }
+ }
+
+ n = copy(b, conn.messageBuf)
+ conn.messageBuf = conn.messageBuf[n:]
+
+ return
+}
+
+// Implements io.Writer.
+func (conn *webSocketConn) Write(b []byte) (n int, err error) {
+ if conn.Base64 {
+ buf := make([]byte, base64.StdEncoding.EncodedLen(len(b)))
+ base64.StdEncoding.Encode(buf, b)
+ err = conn.Ws.WriteMessage(1, buf)
+ if err != nil {
+ return
+ }
+ n = len(b)
+ } else {
+ err = conn.Ws.WriteMessage(2, b)
+ n = len(b)
+ }
+ return
+}
+
+// Implements io.Closer.
+func (conn *webSocketConn) Close() error {
+ // Ignore any error in trying to write a Close frame.
+ _ = conn.Ws.WriteFrame(8, nil)
+ return conn.Ws.Conn.Close()
+}
+
+// Create a new webSocketConn.
+func newWebSocketConn(ws *websocket.WebSocket) webSocketConn {
+ var conn webSocketConn
+ conn.Ws = ws
+ conn.Base64 = (ws.Subprotocol == "base64")
+ return conn
+}
+
+// Copy from WebSocket to socket and vice versa.
+func proxy(local *net.TCPConn, conn *webSocketConn) {
+ var wg sync.WaitGroup
+ wg.Add(2)
+
+ go func() {
+ _, err := io.Copy(conn, local)
+ if err != nil {
+ log("error copying ORPort to WebSocket")
+ }
+ local.CloseRead()
+ conn.Close()
+ wg.Done()
+ }()
+ go func() {
+ _, err := io.Copy(local, conn)
+ if err != nil {
+ log("error copying WebSocket to ORPort")
+ }
+ local.CloseWrite()
+ conn.Close()
+ wg.Done()
+ }()
+
+ wg.Wait()
+}
+
+func webSocketHandler(ws *websocket.WebSocket) {
+ // Undo timeouts on HTTP request handling.
+ ws.Conn.SetDeadline(time.Time{})
+ conn := newWebSocketConn(ws)
+ defer conn.Close()
+
+ handlerChan <- 1
+ defer func() {
+ handlerChan <- -1
+ }()
+
+ or, err := pt.DialOr(&ptInfo, ws.Conn.RemoteAddr().String(), ptMethodName)
+ if err != nil {
+ log("Failed to connect to ORPort: " + err.Error())
+ return
+ }
+ defer or.Close()
+
+ proxy(or, &conn)
+}
+
+func startListener(addr *net.TCPAddr) (*net.TCPListener, error) {
+ ln, err := net.ListenTCP("tcp", addr)
+ if err != nil {
+ return nil, err
+ }
+ go func() {
+ defer ln.Close()
+ var config websocket.Config
+ config.Subprotocols = []string{"base64"}
+ config.MaxMessageSize = maxMessageSize
+ s := &http.Server{
+ Handler: config.Handler(webSocketHandler),
+ ReadTimeout: requestTimeout,
+ }
+ err = s.Serve(ln)
+ if err != nil {
+ log("http.Serve: " + err.Error())
+ }
+ }()
+ return ln, nil
+}
+
+func main() {
+ var logFilename string
+ var port int
+
+ flag.Usage = usage
+ flag.StringVar(&logFilename, "log", "", "log file to write to")
+ flag.IntVar(&port, "port", 0, "port to listen on if unspecified by Tor")
+ flag.Parse()
+
+ if logFilename != "" {
+ f, err := os.OpenFile(logFilename, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0600)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "Can't open log file %q: %s.\n", logFilename, err.Error())
+ os.Exit(1)
+ }
+ logFile = f
+ }
+
+ log("starting")
+ var err error
+ ptInfo, err = pt.ServerSetup(nil)
+ if err != nil {
+ log("error in setup: %s", err)
+ os.Exit(1)
+ }
+
+ listeners := make([]*net.TCPListener, 0)
+ for _, bindaddr := range ptInfo.Bindaddrs {
+ // Override tor's requested port (which is 0 if this transport
+ // has not been run before) with the one requested by the --port
+ // option.
+ if port != 0 {
+ bindaddr.Addr.Port = port
+ }
+
+ switch bindaddr.MethodName {
+ case ptMethodName:
+ ln, err := startListener(bindaddr.Addr)
+ if err != nil {
+ pt.SmethodError(bindaddr.MethodName, err.Error())
+ break
+ }
+ pt.Smethod(bindaddr.MethodName, ln.Addr())
+ log("listening on %s", ln.Addr().String())
+ listeners = append(listeners, ln)
+ default:
+ pt.SmethodError(bindaddr.MethodName, "no such method")
+ }
+ }
+ pt.SmethodsDone()
+
+ var numHandlers int = 0
+ var sig os.Signal
+ sigChan := make(chan os.Signal, 1)
+ signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
+
+ // wait for first signal
+ sig = nil
+ for sig == nil {
+ select {
+ case n := <-handlerChan:
+ numHandlers += n
+ case sig = <-sigChan:
+ }
+ }
+ log("Got first signal %q with %d running handlers.", sig, numHandlers)
+ for _, ln := range listeners {
+ ln.Close()
+ }
+
+ if sig == syscall.SIGTERM {
+ log("Caught signal %q, exiting.", sig)
+ return
+ }
+
+ // wait for second signal or no more handlers
+ sig = nil
+ for sig == nil && numHandlers != 0 {
+ select {
+ case n := <-handlerChan:
+ numHandlers += n
+ log("%d remaining handlers.", numHandlers)
+ case sig = <-sigChan:
+ }
+ }
+ if sig != nil {
+ log("Got second signal %q with %d running handlers.", sig, numHandlers)
+ }
+}
More information about the tor-commits
mailing list