[Pkg-privacy-commits] [pt-websocket] 01/10: Imported Upstream version 0.1
Ximin Luo
infinity0 at moszumanska.debian.org
Sat Aug 22 11:09:14 UTC 2015
This is an automated email from the git hooks/post-receive script.
infinity0 pushed a commit to branch master
in repository pt-websocket.
commit 1edb6a905a6f1bc0e274330596df2fda497ae5fa
Author: Ximin Luo <infinity0 at pwned.gg>
Date: Sun Mar 2 13:33:46 2014 +0000
Imported Upstream version 0.1
---
.gitignore | 2 +
COPYING | 121 ++++++++++
Makefile | 34 +++
README | 30 +++
doc/websocket-transport.txt | 219 ++++++++++++++++++
websocket-client/websocket-client.go | 263 +++++++++++++++++++++
websocket-server/websocket-server.go | 299 ++++++++++++++++++++++++
websocket/websocket.go | 431 +++++++++++++++++++++++++++++++++++
8 files changed, 1399 insertions(+)
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..1fee725
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+/websocket-client
+/websocket-server
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..0e259d4
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,121 @@
+Creative 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.
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..b723c86
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,34 @@
+DESTDIR =
+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: websocket-server/websocket-server
+
+websocket-server/websocket-server: websocket-server/*.go websocket/*.go
+ cd websocket-server && go build $(GOBUILDFLAGS)
+
+websocket-client/websocket-client: websocket-client/*.go
+ cd websocket-client && go build $(GOBUILDFLAGS)
+
+doc/websocket-server.1: websocket-server/websocket-server
+ help2man --no-info --name "WebSocket server pluggable transport" --version-string "$(VERSION)" -o "$@" "$<"
+
+install: websocket-server/websocket-server
+ mkdir -p "$(DESTDIR)$(BINDIR)"
+ cp -f websocket-server/websocket-server "$(DESTDIR)$(BINDIR)"
+
+clean:
+ rm -f websocket-server/websocket-server websocket-client/websocket-client
+ rm -f doc/websocket-server.1
+
+fmt:
+ go fmt ./websocket-server ./websocket-client ./websocket
+
+.PHONY: all install clean fmt
diff --git a/README b/README
new file mode 100644
index 0000000..718dd67
--- /dev/null
+++ b/README
@@ -0,0 +1,30 @@
+This 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/websocket-server --port 9901
diff --git a/doc/websocket-transport.txt b/doc/websocket-transport.txt
new file mode 100644
index 0000000..a9d839f
--- /dev/null
+++ b/doc/websocket-transport.txt
@@ -0,0 +1,219 @@
+Title: 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
diff --git a/websocket-client/websocket-client.go b/websocket-client/websocket-client.go
new file mode 100644
index 0000000..fe38c9d
--- /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() {
+ 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:
+ }
+ }
+}
diff --git a/websocket-server/websocket-server.go b/websocket-server/websocket-server.go
new file mode 100644
index 0000000..4120df8
--- /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([]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)
+ }
+}
diff --git a/websocket/websocket.go b/websocket/websocket.go
new file mode 100644
index 0000000..5786fdf
--- /dev/null
+++ b/websocket/websocket.go
@@ -0,0 +1,431 @@
+// 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}
+}
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-privacy/packages/pt-websocket.git
More information about the Pkg-privacy-commits
mailing list