[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