pax_global_header00006660000000000000000000000064123373530660014522gustar00rootroot0000000000000052 comment=e86c247a6c6c83d85b450a8c814548a94cc25d24 pt-websocket-0.2/000077500000000000000000000000001233735306600137725ustar00rootroot00000000000000pt-websocket-0.2/.gitignore000066400000000000000000000001221233735306600157550ustar00rootroot00000000000000/pt-websocket-client/pt-websocket-client /pt-websocket-server/pt-websocket-server pt-websocket-0.2/COPYING000066400000000000000000000156101233735306600150300ustar00rootroot00000000000000Creative Commons Legal Code CC0 1.0 Universal CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED HEREUNDER. Statement of Purpose The laws of most jurisdictions throughout the world automatically confer exclusive Copyright and Related Rights (defined below) upon the creator and subsequent owner(s) (each and all, an "owner") of an original work of authorship and/or a database (each, a "Work"). Certain owners wish to permanently relinquish those rights to a Work for the purpose of contributing to a commons of creative, cultural and scientific works ("Commons") that the public can reliably and without fear of later claims of infringement build upon, modify, incorporate in other works, reuse and redistribute as freely as possible in any form whatsoever and for any purposes, including without limitation commercial purposes. These owners may contribute to the Commons to promote the ideal of a free culture and the further production of creative, cultural and scientific works, or to gain reputation or greater distribution for their Work in part through the use and efforts of others. For these and/or other purposes and motivations, and without any expectation of additional consideration or compensation, the person associating CC0 with a Work (the "Affirmer"), to the extent that he or she is an owner of Copyright and Related Rights in the Work, voluntarily elects to apply CC0 to the Work and publicly distribute the Work under its terms, with knowledge of his or her Copyright and Related Rights in the Work and the meaning and intended legal effect of CC0 on those rights. 1. Copyright and Related Rights. A Work made available under CC0 may be protected by copyright and related or neighboring rights ("Copyright and Related Rights"). Copyright and Related Rights include, but are not limited to, the following: i. the right to reproduce, adapt, distribute, perform, display, communicate, and translate a Work; ii. moral rights retained by the original author(s) and/or performer(s); iii. publicity and privacy rights pertaining to a person's image or likeness depicted in a Work; iv. rights protecting against unfair competition in regards to a Work, subject to the limitations in paragraph 4(a), below; v. rights protecting the extraction, dissemination, use and reuse of data in a Work; vi. database rights (such as those arising under Directive 96/9/EC of the European Parliament and of the Council of 11 March 1996 on the legal protection of databases, and under any national implementation thereof, including any amended or successor version of such directive); and vii. other similar, equivalent or corresponding rights throughout the world based on applicable law or treaty, and any national implementations thereof. 2. Waiver. To the greatest extent permitted by, but not in contravention of, applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and unconditionally waives, abandons, and surrenders all of Affirmer's Copyright and Related Rights and associated claims and causes of action, whether now known or unknown (including existing as well as future claims and causes of action), in the Work (i) in all territories worldwide, (ii) for the maximum duration provided by applicable law or treaty (including future time extensions), (iii) in any current or future medium and for any number of copies, and (iv) for any purpose whatsoever, including without limitation commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each member of the public at large and to the detriment of Affirmer's heirs and successors, fully intending that such Waiver shall not be subject to revocation, rescission, cancellation, termination, or any other legal or equitable action to disrupt the quiet enjoyment of the Work by the public as contemplated by Affirmer's express Statement of Purpose. 3. Public License Fallback. Should any part of the Waiver for any reason be judged legally invalid or ineffective under applicable law, then the Waiver shall be preserved to the maximum extent permitted taking into account Affirmer's express Statement of Purpose. In addition, to the extent the Waiver is so judged Affirmer hereby grants to each affected person a royalty-free, non transferable, non sublicensable, non exclusive, irrevocable and unconditional license to exercise Affirmer's Copyright and Related Rights in the Work (i) in all territories worldwide, (ii) for the maximum duration provided by applicable law or treaty (including future time extensions), (iii) in any current or future medium and for any number of copies, and (iv) for any purpose whatsoever, including without limitation commercial, advertising or promotional purposes (the "License"). The License shall be deemed effective as of the date CC0 was applied by Affirmer to the Work. Should any part of the License for any reason be judged legally invalid or ineffective under applicable law, such partial invalidity or ineffectiveness shall not invalidate the remainder of the License, and in such case Affirmer hereby affirms that he or she will not (i) exercise any of his or her remaining Copyright and Related Rights in the Work or (ii) assert any associated claims and causes of action with respect to the Work, in either case contrary to Affirmer's express Statement of Purpose. 4. Limitations and Disclaimers. a. No trademark or patent rights held by Affirmer are waived, abandoned, surrendered, licensed or otherwise affected by this document. b. Affirmer offers the Work as-is and makes no representations or warranties of any kind concerning the Work, express, implied, statutory or otherwise, including without limitation warranties of title, merchantability, fitness for a particular purpose, non infringement, or the absence of latent or other defects, accuracy, or the present or absence of errors, whether or not discoverable, all to the greatest extent permissible under applicable law. c. Affirmer disclaims responsibility for clearing rights of other persons that may apply to the Work or any use thereof, including without limitation any person's Copyright and Related Rights in the Work. Further, Affirmer disclaims responsibility for obtaining any necessary consents, permissions or other rights required for any use of the Work. d. Affirmer understands and acknowledges that Creative Commons is not a party to this document and has no duty or obligation with respect to this CC0 or use of the Work. pt-websocket-0.2/Makefile000066400000000000000000000021731233735306600154350ustar00rootroot00000000000000DESTDIR = PREFIX = /usr/local BINDIR = $(PREFIX)/bin VERSION = 0.1 GOBUILDFLAGS = # Alternate flags to use gccgo, allowing cross-compiling for x86 from # x86_64, and presumably better optimization. Install this package: # apt-get install gccgo-multilib # GOBUILDFLAGS = -compiler gccgo -gccgoflags "-O3 -m32 -static-libgo" all: pt-websocket-server/pt-websocket-server pt-websocket-server/pt-websocket-server: pt-websocket-server/*.go websocket/*.go cd pt-websocket-server && go build $(GOBUILDFLAGS) pt-websocket-client/pt-websocket-client: pt-websocket-client/*.go cd pt-websocket-client && go build $(GOBUILDFLAGS) doc/pt-websocket-server.1: pt-websocket-server/pt-websocket-server help2man --no-info --name "WebSocket server pluggable transport" --version-string "$(VERSION)" -o "$@" "$<" install: pt-websocket-server/pt-websocket-server mkdir -p "$(DESTDIR)$(BINDIR)" cp -f "$<" "$(DESTDIR)$(BINDIR)" clean: rm -f pt-websocket-server/pt-websocket-server pt-websocket-client/pt-websocket-client rm -f doc/pt-websocket-server.1 fmt: go fmt ./pt-websocket-server ./pt-websocket-client ./websocket .PHONY: all install clean fmt pt-websocket-0.2/README000066400000000000000000000017551233735306600146620ustar00rootroot00000000000000This is a server transport plugin for the websocket pluggable transport for Tor. For information on the protocol, see doc/websocket-transport.txt. To the extent possible under law, the authors have dedicated all copyright and related and neighboring rights to this software to the public domain worldwide. This software is distributed without any warranty. See COPYING. == How to build and install In order to build, you need development tools for the Go programming language ("apt-get install golang" on Debian). $ make # make install == How to run a relay You will need a version of Tor supporting the extended OR port protocol. Git master as of 15 August, 2013 works. $ git clone https://git.torproject.org/tor.git 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 pt-websocket-0.2/doc/000077500000000000000000000000001233735306600145375ustar00rootroot00000000000000pt-websocket-0.2/doc/websocket-transport.txt000066400000000000000000000226151233735306600213260ustar00rootroot00000000000000Title: WebSocket pluggable transport Author: David Fifield Overview This proposal describes the "websocket" pluggable transport for Tor. It uses the WebSocket protocol now implemented by many web browsers. It is mostly a straightforward description of proxying WebSocket to plain TCP, with special consideration for a base64 encoding for agents that don't support binary WebSocket frames. Motivation The WebSocket protocol is used by the "flash proxy" system that uses web browsers as temporary proxies; browsers may connect to a relay that supports this pluggable transport. Additionally, if WebSocket has a lot of non-Tor use, it becomes a good target for tunneling, perhaps in conjunction with a lower layer of obfuscation. WebSocket commonly works over HTTP ports that are likely to get through a firewall. WebSocket overview WebSocket is a protocol (rather, several mostly compatible protocols) aimed at exposing socket-like functionality to JavaScript in web browsers. It is partially aimed at supplanting techniques such as HTTP long polling for client–server communication. WebSocket provides bidirectional communication between a client and server, sufficient to tunnel Tor traffic. A WebSocket session begins with an HTTP Upgrade handshake. The socket carries data broken into variable-length "messages" which are further broken into "frames." There are distinguished frame opcodes that serve to send either data or control information. Frames sent by the client (but not the server) are XORed with a repeating 32-bit mask that is randomly generated per-frame. Broadly speaking, there are two versions of WebSocket: the older "hixie" protocol, and the newer "hybi" protocol which is now RFC 6455. There are subprotocols within these two versions that differ only in small ways: "hixie-75" and "hixie-76"; and "hybi-7", "hybi-10", and "hybi-17". The older "hybi" sockets were supported by Firefox 4 and Opera 11, but were later disabled because of problems with interaction with reverse HTTP proxies. Current versions of Firefox and Chrome support "hybi" sockets, while Safari only supports "hixie". The "hybi" sockets support text frames and binary frames. Text frames may only include UTF-8–encoded text; it is an error if payload doesn't decode. Binary frames may contain any binary data. However, not all web browsers support binary frames; they were first added to Firefox in version 11. The "hixie" sockets have only text frames. Method name The method name of the transport is "websocket". For example, these are possible torrc configurations for a client and server, respectively: UseBridges 1 ClientTransportPlugin websocket exec /usr/libexec/tor-websocket-proxy --client Bridge websocket 198.51.100.1 ServerTransportPlugin websocket exec /usr/libexec/tor-websocket-proxy --server The base64 subprotocol The most convenient way to tunnel data over WebSocket is with binary frames, but not all web browsers support binary frames. To work around this, the "base64" subprotocol encodes binary data as base64 within text frames. A client that knows it does not support binary frames requests the base64 subprotocol by including "base64" in the value of the Sec-WebSocket-Protocol header field. A server that also supports this subprotocol by sending the value "base64" (and only "base64") in the Sec-WebSocket-Protocol header field of its response. See under "Examples" for examples of handshakes like this. The base64 encoding is applied at the message level, not the frame level. This means, in particular, that any '=' padding occurs only at the end of a message, not at the end of each of its constituent frames. So, for example, the 5-byte message "Hello", whose base64 encoding is "SGVsbG8=", may be sent as one text frame as follows: 0x81 0x08 "SGVsbG8=" or, for example, as two frames (one of 2 bytes and one of 6 bytes): 0x01 0x02 "SG" 0x81 0x06 "VsbG8=" When sent by a client, all frames including these must be masked. Here is an example of a masked base64-encoded message sent as a single frame (using the masking key 0x12345678): 0x81 0x18 0x12 0x34 0x56 0x78 0x41 0x73 0x00 0x0b 0x70 0x73 0x6e 0x45 Examples Here are examples of WebSocket handshakes and the beginning of data transfer. The data is the beginning of a Tor connection (i.e., it begins with a TLS handshake). Data are shown using C string syntax. "> " at the beginning of a line indicates client-to-server communication; "< " is server-to-client. "[...]" indicates contents omitted for brevity. Newlines in the presentation are not significant. This section is non-normative. Using "hybi"/RFC 6455 WebSocket with binary frames: > GET / HTTP/1.1\r\n > Host: 192.0.2.1:80\r\n > Origin: http://example.com\r\n > Sec-WebSocket-Version: 13\r\n > Sec-WebSocket-Key: mzo2xSF9N8VUxuefqO0RSw==\r\n > Connection: Upgrade\r\n > Upgrade: websocket\r\n > \r\n < HTTP/1.1 101 Switching Protocols\r\n < Upgrade: websocket\r\n < Connection: Upgrade\r\n < Sec-WebSocket-Accept: fM0KjD7ixoxkl4PEXU6tNaTveSg=\r\n < \r\n > \x82\xfe\x01\x04\xc9\xd6\xdd\x29\xdf\xd5\xde\x29\x36\xd7[...] < \x16\x03\x01\x00\x31\x02\x00\x00\x2d\x03[...] Using "hybi"/RFC 6455 WebSocket with the base64 subprotocol: > GET / HTTP/1.1\r\n > Host: 192.0.2.1:80\r\n > Origin: http://example.com\r\n > Sec-WebSocket-Version: 13\r\n > Sec-WebSocket-Protocol: base64\r\n > Sec-WebSocket-Key: k5Ybhw0XBDeBfmda1J9ooQ==\r\n > Connection: Upgrade\r\n > Upgrade: websocket\r\n > \r\n < HTTP/1.1 101 Switching Protocols\r\n < Upgrade: websocket\r\n < Connection: Upgrade\r\n < Sec-WebSocket-Accept: LYWpflPUHdal8U1BLPXWR3iqUrI=\r\n < Sec-WebSocket-Protocol: base64\r\n < \r\n > \x81\xfe\x01\x58\xbd\x94\x2a\x31\xfb\xf3\x67\x75\xfc\xc4[...] < \x81\x7e\x04\xd0FgMBADECAA[...] Considerations specific to pluggable transports Endpoints must implement WebSocket according to RFC 6455; for example, a server MUST close the connection if it receives an unmasked frame from a client, and a client MUST close the connection if it receives a masked frame from a server (RFC 6455 section 5.1). There are also additional requirements for WebSocket when used as a Tor pluggable transport. Clients MUST implement the RFC 6455 version of the protocol and use it for all connections. Servers MUST implement the RFC 6455 version of the protocol and MAY also implement earlier versions. That is, a server MAY check a client HTTP request to see if it matches an earlier version of the protocol, and MAY begin communicating using that protocol. Section 4.4 of RFC 6455 discusses supporting multiple versions of the protocol. Servers MUST support binary frames (opcode 2). Servers MAY also support text frames (opcode 1). Servers supporting text frames MUST implement the base64 subprotocol and accept it when requested by a client in the Sec-WebSocket-Protocol header field. Text frames MUST NOT be sent by either side if the base64 subprotocol has not been negotiated. Any endpoint receiving a text frame when base64 has not been negotiated, or a text message that cannot be decoded as base64, MUST close the connection. A client MUST NOT proceed after receiving any HTTP response status code other than 101. In particular, it MUST NOT follow redirections such as 301. Endpoints SHOULD respond to Ping frames with a single Pong frame, but nothing in this specification requires the sending of Ping frames. Message and frame boundaries are not meaningful. Received non-control messages are concatenated, in order, to reconstruct the original stream. Endpoints SHOULD limit the size of messages they send. All messages SHOULD be sent in a single frame. Endpoints MUST limit the size of messages and frames that they will buffer. When the sum of the length of already-buffered data and the length of the next frame exceeds the limit, the endpoint MUST close the connection and SHOULD do so with a status code of 1009 (see RFC 6455 section 7.4.1). Endpoints MUST be capable of receiving messages containing up to 16384 bytes of binary data; this may require buffering up to 21848 bytes of UTF-8–encoded base64 text. Questions/extensions WebSocket also has a TLS-wrapped version, identified by using the "wss" (as opposed to "ws") URL scheme. An advantage of this when tunneling through a browser is that the TLS handshake will be exactly that of a browser. However, this probably requires the certificates of relays' server transport plugins to be trusted by browsers. References "Pluggable transports for circumvention" https://gitweb.torproject.org/torspec.git/blob/HEAD:/proposals/180-pluggable-transport.txt RFC 6455, "The WebSocket Protocol" (a.k.a. hybi-17) https://tools.ietf.org/html/rfc6455 "The WebSocket protocol (draft-ietf-hybi-thewebsocketprotocol-10)" (a.k.a. hybi-10) https://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-10 "The WebSocket protocol (draft-ietf-hybi-thewebsocketprotocol-7)" (a.k.a. hybi-7) https://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-7 "The WebSocket protocol (draft-ietf-hybi-thewebsocketprotocol-00)" (a.k.a. hybi-00, hixie-76) https://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-00 "The Web Socket protocol (draft-hixie-thewebsocketprotocol-75)" (a.k.a. hixie-75) https://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-75 Browser support matrix http://autobahn.ws/testsuite/reports/clients/index.html pt-websocket-0.2/pt-websocket-client/000077500000000000000000000000001233735306600176555ustar00rootroot00000000000000pt-websocket-0.2/pt-websocket-client/pt-websocket-client.go000066400000000000000000000121261233735306600240710ustar00rootroot00000000000000// 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() { return err } continue } 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([]string{ptMethodName}) 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: } } } pt-websocket-0.2/pt-websocket-server/000077500000000000000000000000001233735306600177055ustar00rootroot00000000000000pt-websocket-0.2/pt-websocket-server/pt-websocket-server.go000066400000000000000000000153041233735306600241520ustar00rootroot00000000000000// 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([]string{ptMethodName}) 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) } } pt-websocket-0.2/websocket/000077500000000000000000000000001233735306600157605ustar00rootroot00000000000000pt-websocket-0.2/websocket/websocket.go000066400000000000000000000312241233735306600202770ustar00rootroot00000000000000// WebSocket library. Only the RFC 6455 variety of WebSocket is supported. // // Reading and writing is strictly per-frame (or per-message). There is no way // to partially read a frame. Config.MaxMessageSize affords control of the // maximum buffering of messages. // // The reason for using this custom implementation instead of // code.google.com/p/go.net/websocket is that the latter has problems with long // messages and does not support server subprotocols. // "Denial of Service Protection in Go HTTP Servers" // https://code.google.com/p/go/issues/detail?id=2093 // "go.websocket: Read/Copy fail with long frames" // https://code.google.com/p/go/issues/detail?id=2134 // http://golang.org/pkg/net/textproto/#pkg-bugs // "To let callers manage exposure to denial of service attacks, Reader should // allow them to set and reset a limit on the number of bytes read from the // connection." // "websocket.Dial doesn't limit response header length as http.Get does" // https://groups.google.com/forum/?fromgroups=#!topic/golang-nuts/2Tge6U8-QYI // // Example usage: // // func doSomething(ws *WebSocket) { // } // var config websocket.Config // config.Subprotocols = []string{"base64"} // config.MaxMessageSize = 2500 // http.Handle("/", config.Handler(doSomething)) // err = http.ListenAndServe(":8080", nil) package websocket import ( "bufio" "bytes" "crypto/rand" "crypto/sha1" "encoding/base64" "encoding/binary" "errors" "fmt" "io" "net" "net/http" "strings" ) // Settings for potential WebSocket connections. Subprotocols is a list of // supported subprotocols as in RFC 6455 section 1.9. When answering client // requests, the first of the client's requests subprotocols that is also in // this list (if any) will be used as the subprotocol for the connection. // MaxMessageSize is a limit on buffering messages. type Config struct { Subprotocols []string MaxMessageSize int } // Representation of a WebSocket frame. The Payload is always without masking. type Frame struct { Fin bool Opcode byte Payload []byte } // Return true iff the frame's opcode says it is a control frame. func (frame *Frame) IsControl() bool { return (frame.Opcode & 0x08) != 0 } // Representation of a WebSocket message. The Payload is always without masking. type Message struct { Opcode byte Payload []byte } // A WebSocket connection after hijacking from HTTP. type WebSocket struct { // Conn and ReadWriter from http.ResponseWriter.Hijack. Conn net.Conn Bufrw *bufio.ReadWriter // Whether we are a client or a server has implications for masking. IsClient bool // Set from a parent Config. MaxMessageSize int // The single selected subprotocol after negotiation, or "". Subprotocol string // Buffer for message payloads, which may be interrupted by control // messages. messageBuf bytes.Buffer } func applyMask(payload []byte, maskKey [4]byte) { for i := 0; i < len(payload); i++ { payload[i] = payload[i] ^ maskKey[i%4] } } func (ws *WebSocket) maxMessageSize() int { if ws.MaxMessageSize == 0 { return 64000 } return ws.MaxMessageSize } // Read a single frame from the WebSocket. func (ws *WebSocket) ReadFrame() (frame Frame, err error) { var b byte err = binary.Read(ws.Bufrw, binary.BigEndian, &b) if err != nil { return } frame.Fin = (b & 0x80) != 0 frame.Opcode = b & 0x0f err = binary.Read(ws.Bufrw, binary.BigEndian, &b) if err != nil { return } masked := (b & 0x80) != 0 payloadLen := uint64(b & 0x7f) if payloadLen == 126 { var short uint16 err = binary.Read(ws.Bufrw, binary.BigEndian, &short) if err != nil { return } payloadLen = uint64(short) } else if payloadLen == 127 { var long uint64 err = binary.Read(ws.Bufrw, binary.BigEndian, &long) if err != nil { return } payloadLen = long } if payloadLen > uint64(ws.maxMessageSize()) { err = errors.New(fmt.Sprintf("frame payload length of %d exceeds maximum of %d", payloadLen, ws.MaxMessageSize)) return } maskKey := [4]byte{} if masked { if ws.IsClient { err = errors.New("client got masked frame") return } err = binary.Read(ws.Bufrw, binary.BigEndian, &maskKey) if err != nil { return } } else { if !ws.IsClient { err = errors.New("server got unmasked frame") return } } frame.Payload = make([]byte, payloadLen) _, err = io.ReadFull(ws.Bufrw, frame.Payload) if err != nil { return } if masked { applyMask(frame.Payload, maskKey) } return frame, nil } // Read a single message from the WebSocket. Multiple fragmented frames are // combined into a single message before being returned. Non-control messages // may be interrupted by control frames. The control frames are returned as // individual messages before the message that they interrupt. func (ws *WebSocket) ReadMessage() (message Message, err error) { var opcode byte = 0 for { var frame Frame frame, err = ws.ReadFrame() if err != nil { return } if frame.IsControl() { if !frame.Fin { err = errors.New("control frame has fin bit unset") return } message.Opcode = frame.Opcode message.Payload = frame.Payload return message, nil } if opcode == 0 { if frame.Opcode == 0 { err = errors.New("first frame has opcode 0") return } opcode = frame.Opcode } else { if frame.Opcode != 0 { err = errors.New(fmt.Sprintf("non-first frame has nonzero opcode %d", frame.Opcode)) return } } if ws.messageBuf.Len()+len(frame.Payload) > ws.MaxMessageSize { err = errors.New(fmt.Sprintf("message payload length of %d exceeds maximum of %d", ws.messageBuf.Len()+len(frame.Payload), ws.MaxMessageSize)) return } ws.messageBuf.Write(frame.Payload) if frame.Fin { break } } message.Opcode = opcode message.Payload = ws.messageBuf.Bytes() ws.messageBuf.Reset() return message, nil } // Write a single frame to the WebSocket stream. Destructively masks payload in // place if ws.IsClient. Frames are always unfragmented. func (ws *WebSocket) WriteFrame(opcode byte, payload []byte) (err error) { if opcode >= 16 { err = errors.New(fmt.Sprintf("opcode %d is >= 16", opcode)) return } ws.Bufrw.WriteByte(0x80 | opcode) var maskBit byte var maskKey [4]byte if ws.IsClient { _, err = io.ReadFull(rand.Reader, maskKey[:]) if err != nil { return } applyMask(payload, maskKey) maskBit = 0x80 } else { maskBit = 0x00 } if len(payload) < 126 { ws.Bufrw.WriteByte(maskBit | byte(len(payload))) } else if len(payload) <= 0xffff { ws.Bufrw.WriteByte(maskBit | 126) binary.Write(ws.Bufrw, binary.BigEndian, uint16(len(payload))) } else { ws.Bufrw.WriteByte(maskBit | 127) binary.Write(ws.Bufrw, binary.BigEndian, uint64(len(payload))) } if ws.IsClient { _, err = ws.Bufrw.Write(maskKey[:]) if err != nil { return } } _, err = ws.Bufrw.Write(payload) if err != nil { return } ws.Bufrw.Flush() return } // Write a single message to the WebSocket stream. Destructively masks payload // in place if ws.IsClient. Messages are always sent as a single unfragmented // frame. func (ws *WebSocket) WriteMessage(opcode byte, payload []byte) (err error) { return ws.WriteFrame(opcode, payload) } // Split a string on commas and trim whitespace. func commaSplit(s string) []string { var result []string if strings.TrimSpace(s) == "" { return result } for _, e := range strings.Split(s, ",") { result = append(result, strings.TrimSpace(e)) } return result } // Returns true iff one of the strings in haystack is needle (case-insensitive). func containsCase(haystack []string, needle string) bool { for _, e := range haystack { if strings.ToLower(e) == strings.ToLower(needle) { return true } } return false } // One-step SHA-1 hash of a string. func sha1Hash(data string) []byte { h := sha1.New() h.Write([]byte(data)) return h.Sum(nil) } func httpError(w http.ResponseWriter, bufrw *bufio.ReadWriter, code int) { w.Header().Set("Connection", "close") bufrw.WriteString(fmt.Sprintf("HTTP/1.0 %d %s\r\n", code, http.StatusText(code))) w.Header().Write(bufrw) bufrw.WriteString("\r\n") bufrw.Flush() } // An implementation of http.Handler with a Config. The ServeHTTP function calls // Callback assuming WebSocket HTTP negotiation is successful. type HTTPHandler struct { Config *Config Callback func(*WebSocket) } // Implements the http.Handler interface. func (handler *HTTPHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { conn, bufrw, err := w.(http.Hijacker).Hijack() if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } defer conn.Close() // See RFC 6455 section 4.2.1 for this sequence of checks. // 1. An HTTP/1.1 or higher GET request, including a "Request-URI"... if req.Method != "GET" { httpError(w, bufrw, http.StatusMethodNotAllowed) return } if req.URL.Path != "/" { httpError(w, bufrw, http.StatusNotFound) return } // 2. A |Host| header field containing the server's authority. // We deliberately skip this test. // 3. An |Upgrade| header field containing the value "websocket", // treated as an ASCII case-insensitive value. if !containsCase(commaSplit(req.Header.Get("Upgrade")), "websocket") { httpError(w, bufrw, http.StatusBadRequest) return } // 4. A |Connection| header field that includes the token "Upgrade", // treated as an ASCII case-insensitive value. if !containsCase(commaSplit(req.Header.Get("Connection")), "Upgrade") { httpError(w, bufrw, http.StatusBadRequest) return } // 5. A |Sec-WebSocket-Key| header field with a base64-encoded value // that, when decoded, is 16 bytes in length. websocketKey := req.Header.Get("Sec-WebSocket-Key") key, err := base64.StdEncoding.DecodeString(websocketKey) if err != nil || len(key) != 16 { httpError(w, bufrw, http.StatusBadRequest) return } // 6. A |Sec-WebSocket-Version| header field, with a value of 13. // We also allow 8 from draft-ietf-hybi-thewebsocketprotocol-10. var knownVersions = []string{"8", "13"} websocketVersion := req.Header.Get("Sec-WebSocket-Version") if !containsCase(knownVersions, websocketVersion) { // "If this version does not match a version understood by the // server, the server MUST abort the WebSocket handshake // described in this section and instead send an appropriate // HTTP error code (such as 426 Upgrade Required) and a // |Sec-WebSocket-Version| header field indicating the // version(s) the server is capable of understanding." w.Header().Set("Sec-WebSocket-Version", strings.Join(knownVersions, ", ")) httpError(w, bufrw, 426) return } // 7. Optionally, an |Origin| header field. // 8. Optionally, a |Sec-WebSocket-Protocol| header field, with a list of // values indicating which protocols the client would like to speak, ordered // by preference. clientProtocols := commaSplit(req.Header.Get("Sec-WebSocket-Protocol")) // 9. Optionally, a |Sec-WebSocket-Extensions| header field... // 10. Optionally, other header fields... var ws WebSocket ws.Conn = conn ws.Bufrw = bufrw ws.IsClient = false ws.MaxMessageSize = handler.Config.MaxMessageSize // See RFC 6455 section 4.2.2, item 5 for these steps. // 1. A Status-Line with a 101 response code as per RFC 2616. bufrw.WriteString(fmt.Sprintf("HTTP/1.0 %d %s\r\n", http.StatusSwitchingProtocols, http.StatusText(http.StatusSwitchingProtocols))) // 2. An |Upgrade| header field with value "websocket" as per RFC 2616. w.Header().Set("Upgrade", "websocket") // 3. A |Connection| header field with value "Upgrade". w.Header().Set("Connection", "Upgrade") // 4. A |Sec-WebSocket-Accept| header field. The value of this header // field is constructed by concatenating /key/, defined above in step 4 // in Section 4.2.2, with the string // "258EAFA5-E914-47DA-95CA-C5AB0DC85B11", taking the SHA-1 hash of this // concatenated value to obtain a 20-byte value and base64-encoding (see // Section 4 of [RFC4648]) this 20-byte hash. const magicGUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" acceptKey := base64.StdEncoding.EncodeToString(sha1Hash(websocketKey + magicGUID)) w.Header().Set("Sec-WebSocket-Accept", acceptKey) // 5. Optionally, a |Sec-WebSocket-Protocol| header field, with a value // /subprotocol/ as defined in step 4 in Section 4.2.2. for _, clientProto := range clientProtocols { for _, serverProto := range handler.Config.Subprotocols { if clientProto == serverProto { ws.Subprotocol = clientProto w.Header().Set("Sec-WebSocket-Protocol", clientProto) break } } } // 6. Optionally, a |Sec-WebSocket-Extensions| header field... w.Header().Write(bufrw) bufrw.WriteString("\r\n") bufrw.Flush() // Call the WebSocket-specific handler. handler.Callback(&ws) } // Return an http.Handler with the given callback function. func (config *Config) Handler(callback func(*WebSocket)) http.Handler { return &HTTPHandler{config, callback} }