commit 9ca17c7ae0c24168d1aa09dd4ad01068914386f5 Author: David Fifield david@bamsoftware.com Date: Thu Dec 12 21:04:07 2013 -0800
Delete the local pt library.
Use goptlib instead. --- Makefile | 4 +- pt/.gitignore | 2 - pt/examples/dummy-client/dummy-client.go | 137 ------- pt/examples/dummy-server/dummy-server.go | 121 ------ pt/pt.go | 611 ------------------------------ pt/pt_test.go | 61 --- pt/socks/socks.go | 107 ------ websocket-client/websocket-client.go | 66 ++-- websocket-server/websocket-server.go | 22 +- 9 files changed, 49 insertions(+), 1082 deletions(-)
diff --git a/Makefile b/Makefile index 95aa243..28a94ad 100644 --- a/Makefile +++ b/Makefile @@ -10,10 +10,10 @@ GOBUILDFLAGS =
all: websocket-server/websocket-server
-websocket-server/websocket-server: websocket-server/*.go websocket/*.go pt/*.go +websocket-server/websocket-server: websocket-server/*.go websocket/*.go cd websocket-server && go build $(GOBUILDFLAGS)
-websocket-client/websocket-client: websocket-client/*.go websocket/*.go pt/*.go +websocket-client/websocket-client: websocket-client/*.go websocket/*.go cd websocket-client && go build $(GOBUILDFLAGS)
install: websocket-server/websocket-server diff --git a/pt/.gitignore b/pt/.gitignore deleted file mode 100644 index d4d5132..0000000 --- a/pt/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -/examples/dummy-client/dummy-client -/examples/dummy-server/dummy-server diff --git a/pt/examples/dummy-client/dummy-client.go b/pt/examples/dummy-client/dummy-client.go deleted file mode 100644 index 3cf7b45..0000000 --- a/pt/examples/dummy-client/dummy-client.go +++ /dev/null @@ -1,137 +0,0 @@ -// Usage (in torrc): -// UseBridges 1 -// Bridge dummy X.X.X.X:YYYY -// ClientTransportPlugin dummy exec dummy-client -// Because this transport doesn't do anything to the traffic, you can use any -// ordinary relay's ORPort in the Bridge line. - -package main - -import ( - "io" - "net" - "os" - "os/signal" - "sync" - "syscall" -) - -import "git.torproject.org/pluggable-transports/websocket.git/src/pt" -import "git.torproject.org/pluggable-transports/websocket.git/src/pt/socks" - -var ptInfo pt.ClientInfo - -// When a connection handler starts, +1 is written to this channel; when it -// ends, -1 is written. -var handlerChan = make(chan int) - -func copyLoop(a, b net.Conn) { - var wg sync.WaitGroup - wg.Add(2) - - go func() { - io.Copy(b, a) - wg.Done() - }() - go func() { - io.Copy(a, b) - wg.Done() - }() - - wg.Wait() -} - -func handleConnection(local net.Conn) error { - defer local.Close() - - handlerChan <- 1 - defer func() { - handlerChan <- -1 - }() - - var remote net.Conn - err := socks.AwaitSocks4aConnect(local.(*net.TCPConn), func(dest string) (*net.TCPAddr, error) { - var err error - // set remote in outer function environment - remote, err = net.Dial("tcp", dest) - if err != nil { - return nil, err - } - return remote.RemoteAddr().(*net.TCPAddr), nil - }) - if err != nil { - return err - } - defer remote.Close() - copyLoop(local, remote) - - return nil -} - -func acceptLoop(ln net.Listener) error { - for { - conn, err := ln.Accept() - if err != nil { - return err - } - go handleConnection(conn) - } - return nil -} - -func startListener(addr string) (net.Listener, error) { - ln, err := net.Listen("tcp", addr) - if err != nil { - return nil, err - } - go acceptLoop(ln) - return ln, nil -} - -func main() { - ptInfo = pt.ClientSetup([]string{"dummy"}) - - listeners := make([]net.Listener, 0) - for _, methodName := range ptInfo.MethodNames { - ln, err := startListener("127.0.0.1:0") - if err != nil { - pt.CmethodError(methodName, err.Error()) - continue - } - pt.Cmethod(methodName, "socks4", ln.Addr()) - listeners = append(listeners, ln) - } - 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/examples/dummy-server/dummy-server.go b/pt/examples/dummy-server/dummy-server.go deleted file mode 100644 index 26314d0..0000000 --- a/pt/examples/dummy-server/dummy-server.go +++ /dev/null @@ -1,121 +0,0 @@ -// Usage (in torrc): -// BridgeRelay 1 -// ORPort 9001 -// ExtORPort 6669 -// ServerTransportPlugin dummy exec dummy-server - -package main - -import ( - "io" - "net" - "os" - "os/signal" - "sync" - "syscall" -) - -import "git.torproject.org/pluggable-transports/websocket.git/src/pt" - -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 copyLoop(a, b net.Conn) { - var wg sync.WaitGroup - wg.Add(2) - - go func() { - io.Copy(b, a) - wg.Done() - }() - go func() { - io.Copy(a, b) - wg.Done() - }() - - wg.Wait() -} - -func handleConnection(conn net.Conn) { - handlerChan <- 1 - defer func() { - handlerChan <- -1 - }() - - or, err := pt.ConnectOr(&ptInfo, conn, "dummy") - if err != nil { - return - } - copyLoop(conn, or) -} - -func acceptLoop(ln net.Listener) error { - for { - conn, err := ln.Accept() - if err != nil { - return err - } - go handleConnection(conn) - } - return nil -} - -func startListener(addr *net.TCPAddr) (net.Listener, error) { - ln, err := net.ListenTCP("tcp", addr) - if err != nil { - return nil, err - } - go acceptLoop(ln) - return ln, nil -} - -func main() { - ptInfo = pt.ServerSetup([]string{"dummy"}) - - listeners := make([]net.Listener, 0) - for _, bindAddr := range ptInfo.BindAddrs { - ln, err := startListener(bindAddr.Addr) - if err != nil { - pt.SmethodError(bindAddr.MethodName, err.Error()) - continue - } - pt.Smethod(bindAddr.MethodName, ln.Addr()) - listeners = append(listeners, ln) - } - 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: - } - } - 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/pt.go b/pt/pt.go deleted file mode 100644 index 526f3b7..0000000 --- a/pt/pt.go +++ /dev/null @@ -1,611 +0,0 @@ -// Tor pluggable transports library. -// -// Sample client usage: -// -// import "git.torproject.org/pluggable-transports/websocket.git/src/pt" -// var ptInfo pt.ClientInfo -// ptInfo = pt.ClientSetup([]string{"foo"}) -// for _, methodName := range ptInfo.MethodNames { -// ln, err := startSocksListener() -// if err != nil { -// pt.CmethodError(methodName, err.Error()) -// continue -// } -// pt.Cmethod(methodName, "socks4", ln.Addr()) -// } -// pt.CmethodsDone() -// -// Sample server usage: -// -// import "git.torproject.org/pluggable-transports/websocket.git/src/pt" -// var ptInfo pt.ServerInfo -// ptInfo = pt.ServerSetup([]string{"foo", "bar"}) -// for _, bindAddr := range ptInfo.BindAddrs { -// ln, err := startListener(bindAddr.Addr, bindAddr.MethodName) -// if err != nil { -// pt.SmethodError(bindAddr.MethodName, err.Error()) -// continue -// } -// pt.Smethod(bindAddr.MethodName, ln.Addr()) -// } -// pt.SmethodsDone() -// func handler(conn net.Conn, methodName string) { -// or, err := pt.ConnectOr(&ptInfo, conn, methodName) -// if err != nil { -// return -// } -// // Do something with or and conn. -// } - -package pt - -import ( - "bufio" - "bytes" - "crypto/hmac" - "crypto/rand" - "crypto/sha256" - "crypto/subtle" - "encoding/binary" - "errors" - "fmt" - "io" - "net" - "os" - "strings" - "time" -) - -func getenv(key string) string { - return os.Getenv(key) -} - -// Abort with an ENV-ERROR if the environment variable isn't set. -func getenvRequired(key string) string { - value := os.Getenv(key) - if value == "" { - EnvError(fmt.Sprintf("no %s environment variable", key)) - } - return value -} - -// Escape a string so it contains no byte values over 127 and doesn't contain -// any of the characters '\x00' or '\n'. -func escape(s string) string { - var buf bytes.Buffer - for _, b := range []byte(s) { - if b == '\n' { - buf.WriteString("\n") - } else if b == '\' { - buf.WriteString("\\") - } else if 0 < b && b < 128 { - buf.WriteByte(b) - } else { - fmt.Fprintf(&buf, "\x%02x", b) - } - } - return buf.String() -} - -// Print a pluggable transports protocol line to stdout. The line consists of an -// unescaped keyword, followed by any number of escaped strings. -func Line(keyword string, v ...string) { - var buf bytes.Buffer - buf.WriteString(keyword) - for _, x := range v { - buf.WriteString(" " + escape(x)) - } - fmt.Println(buf.String()) - os.Stdout.Sync() -} - -// All of the *Error functions call os.Exit(1). - -// Emit an ENV-ERROR with explanation text. -func EnvError(msg string) { - Line("ENV-ERROR", msg) - os.Exit(1) -} - -// Emit a VERSION-ERROR with explanation text. -func VersionError(msg string) { - Line("VERSION-ERROR", msg) - os.Exit(1) -} - -// Emit a CMETHOD-ERROR with explanation text. -func CmethodError(methodName, msg string) { - Line("CMETHOD-ERROR", methodName, msg) - os.Exit(1) -} - -// Emit an SMETHOD-ERROR with explanation text. -func SmethodError(methodName, msg string) { - Line("SMETHOD-ERROR", methodName, msg) - os.Exit(1) -} - -// Emit a CMETHOD line. socks must be "socks4" or "socks5". Call this once for -// each listening client SOCKS port. -func Cmethod(name string, socks string, addr net.Addr) { - Line("CMETHOD", name, socks, addr.String()) -} - -// Emit a CMETHODS DONE line. Call this after opening all client listeners. -func CmethodsDone() { - Line("CMETHODS", "DONE") -} - -// Emit an SMETHOD line. Call this once for each listening server port. -func Smethod(name string, addr net.Addr) { - Line("SMETHOD", name, addr.String()) -} - -// Emit an SMETHODS DONE line. Call this after opening all server listeners. -func SmethodsDone() { - Line("SMETHODS", "DONE") -} - -// Get a pluggable transports version offered by Tor and understood by us, if -// any. The only version we understand is "1". This function reads the -// environment variable TOR_PT_MANAGED_TRANSPORT_VER. -func getManagedTransportVer() string { - const transportVersion = "1" - for _, offered := range strings.Split(getenvRequired("TOR_PT_MANAGED_TRANSPORT_VER"), ",") { - if offered == transportVersion { - return offered - } - } - return "" -} - -// Get the intersection of the method names offered by Tor and those in -// methodNames. This function reads the environment variable -// TOR_PT_CLIENT_TRANSPORTS. -func getClientTransports(methodNames []string) []string { - clientTransports := getenvRequired("TOR_PT_CLIENT_TRANSPORTS") - if clientTransports == "*" { - return methodNames - } - result := make([]string, 0) - for _, requested := range strings.Split(clientTransports, ",") { - for _, methodName := range methodNames { - if requested == methodName { - result = append(result, methodName) - break - } - } - } - return result -} - -// This structure is returned by ClientSetup. It consists of a list of method -// names. -type ClientInfo struct { - MethodNames []string -} - -// Check the client pluggable transports environments, emitting an error message -// and exiting the program if any error is encountered. Returns a subset of -// methodNames requested by Tor. -func ClientSetup(methodNames []string) ClientInfo { - var info ClientInfo - - ver := getManagedTransportVer() - if ver == "" { - VersionError("no-version") - } else { - Line("VERSION", ver) - } - - info.MethodNames = getClientTransports(methodNames) - if len(info.MethodNames) == 0 { - CmethodsDone() - os.Exit(1) - } - - return info -} - -// A combination of a method name and an address, as extracted from -// TOR_PT_SERVER_BINDADDR. -type BindAddr struct { - MethodName string - Addr *net.TCPAddr -} - -// Resolve an address string into a net.TCPAddr. -func resolveBindAddr(bindAddr string) (*net.TCPAddr, error) { - addr, err := net.ResolveTCPAddr("tcp", bindAddr) - if err == nil { - return addr, nil - } - // Before the fixing of bug #7011, tor doesn't put brackets around IPv6 - // addresses. Split after the last colon, assuming it is a port - // separator, and try adding the brackets. - parts := strings.Split(bindAddr, ":") - if len(parts) <= 2 { - return nil, err - } - bindAddr = "[" + strings.Join(parts[:len(parts)-1], ":") + "]:" + parts[len(parts)-1] - return net.ResolveTCPAddr("tcp", bindAddr) -} - -// Return a new slice, the members of which are those members of addrs having a -// MethodName in methodNames. -func filterBindAddrs(addrs []BindAddr, methodNames []string) []BindAddr { - var result []BindAddr - - for _, ba := range addrs { - for _, methodName := range methodNames { - if ba.MethodName == methodName { - result = append(result, ba) - break - } - } - } - - return result -} - -// Return a map from method names to bind addresses. The map is the contents of -// TOR_PT_SERVER_BINDADDR, with keys filtered by TOR_PT_SERVER_TRANSPORTS, and -// further filtered by the methods in methodNames. -func getServerBindAddrs(methodNames []string) []BindAddr { - var result []BindAddr - - // Get the list of all requested bindaddrs. - var serverBindAddr = getenvRequired("TOR_PT_SERVER_BINDADDR") - for _, spec := range strings.Split(serverBindAddr, ",") { - var bindAddr BindAddr - - parts := strings.SplitN(spec, "-", 2) - if len(parts) != 2 { - EnvError(fmt.Sprintf("TOR_PT_SERVER_BINDADDR: %q: doesn't contain "-"", spec)) - } - bindAddr.MethodName = parts[0] - addr, err := resolveBindAddr(parts[1]) - if err != nil { - EnvError(fmt.Sprintf("TOR_PT_SERVER_BINDADDR: %q: %s", spec, err.Error())) - } - bindAddr.Addr = addr - result = append(result, bindAddr) - } - - // Filter by TOR_PT_SERVER_TRANSPORTS. - serverTransports := getenvRequired("TOR_PT_SERVER_TRANSPORTS") - if serverTransports != "*" { - result = filterBindAddrs(result, strings.Split(serverTransports, ",")) - } - - // Finally filter by what we understand. - result = filterBindAddrs(result, methodNames) - - return result -} - -// Read and validate the contents of an auth cookie file. Returns the 32-byte -// cookie. See section 4.2.1.2 of pt-spec.txt. -func readAuthCookieFile(filename string) ([]byte, error) { - authCookieHeader := []byte("! Extended ORPort Auth Cookie !\x0a") - header := make([]byte, 32) - cookie := make([]byte, 32) - - f, err := os.Open(filename) - if err != nil { - return cookie, err - } - defer f.Close() - - n, err := io.ReadFull(f, header) - if err != nil { - return cookie, err - } - n, err = io.ReadFull(f, cookie) - if err != nil { - return cookie, err - } - // Check that the file ends here. - n, err = f.Read(make([]byte, 1)) - if n != 0 { - return cookie, errors.New(fmt.Sprintf("file is longer than 64 bytes")) - } else if err != io.EOF { - return cookie, errors.New(fmt.Sprintf("did not find EOF at end of file")) - } - - if !bytes.Equal(header, authCookieHeader) { - return cookie, errors.New(fmt.Sprintf("missing auth cookie header")) - } - - return cookie, nil -} - -// This structure is returned by ServerSetup. It consists of a list of -// BindAddrs, an address for the ORPort, an address for the extended ORPort (if -// any), and an authentication cookie (if any). -type ServerInfo struct { - BindAddrs []BindAddr - OrAddr *net.TCPAddr - ExtendedOrAddr *net.TCPAddr - AuthCookie []byte -} - -// Check the server pluggable transports environments, emitting an error message -// and exiting the program if any error is encountered. Resolves the various -// requested bind addresses, the server ORPort and extended ORPort, and reads -// the auth cookie file. Returns a ServerInfo struct. -func ServerSetup(methodNames []string) ServerInfo { - var info ServerInfo - var err error - - ver := getManagedTransportVer() - if ver == "" { - VersionError("no-version") - } else { - Line("VERSION", ver) - } - - var orPort = getenvRequired("TOR_PT_ORPORT") - info.OrAddr, err = net.ResolveTCPAddr("tcp", orPort) - if err != nil { - EnvError(fmt.Sprintf("cannot resolve TOR_PT_ORPORT %q: %s", orPort, err.Error())) - } - - info.BindAddrs = getServerBindAddrs(methodNames) - if len(info.BindAddrs) == 0 { - SmethodsDone() - os.Exit(1) - } - - var extendedOrPort = getenv("TOR_PT_EXTENDED_SERVER_PORT") - if extendedOrPort != "" { - info.ExtendedOrAddr, err = net.ResolveTCPAddr("tcp", extendedOrPort) - if err != nil { - EnvError(fmt.Sprintf("cannot resolve TOR_PT_EXTENDED_SERVER_PORT %q: %s", extendedOrPort, err.Error())) - } - } - - var authCookieFilename = getenv("TOR_PT_AUTH_COOKIE_FILE") - if authCookieFilename != "" { - info.AuthCookie, err = readAuthCookieFile(authCookieFilename) - if err != nil { - EnvError(fmt.Sprintf("error reading TOR_PT_AUTH_COOKIE_FILE %q: %s", authCookieFilename, err.Error())) - } - } - - return info -} - -// See 217-ext-orport-auth.txt section 4.2.1.3. -func computeServerHash(info *ServerInfo, clientNonce, serverNonce []byte) []byte { - h := hmac.New(sha256.New, info.AuthCookie) - io.WriteString(h, "ExtORPort authentication server-to-client hash") - h.Write(clientNonce) - h.Write(serverNonce) - return h.Sum([]byte{}) -} - -// See 217-ext-orport-auth.txt section 4.2.1.3. -func computeClientHash(info *ServerInfo, clientNonce, serverNonce []byte) []byte { - h := hmac.New(sha256.New, info.AuthCookie) - io.WriteString(h, "ExtORPort authentication client-to-server hash") - h.Write(clientNonce) - h.Write(serverNonce) - return h.Sum([]byte{}) -} - -func extOrPortAuthenticate(s *net.TCPConn, info *ServerInfo) error { - r := bufio.NewReader(s) - - // Read auth types. 217-ext-orport-auth.txt section 4.1. - var authTypes [256]bool - var count int - for count = 0; count < 256; count++ { - b, err := r.ReadByte() - if err != nil { - return err - } - if b == 0 { - break - } - authTypes[b] = true - } - if count >= 256 { - return errors.New(fmt.Sprintf("read 256 auth types without seeing \x00")) - } - - // We support only type 1, SAFE_COOKIE. - if !authTypes[1] { - return errors.New(fmt.Sprintf("server didn't offer auth type 1")) - } - _, err := s.Write([]byte{1}) - if err != nil { - return err - } - - clientNonce := make([]byte, 32) - clientHash := make([]byte, 32) - serverNonce := make([]byte, 32) - serverHash := make([]byte, 32) - - _, err = io.ReadFull(rand.Reader, clientNonce) - if err != nil { - return err - } - _, err = s.Write(clientNonce) - if err != nil { - return err - } - - _, err = io.ReadFull(r, serverHash) - if err != nil { - return err - } - _, err = io.ReadFull(r, serverNonce) - if err != nil { - return err - } - - expectedServerHash := computeServerHash(info, clientNonce, serverNonce) - if subtle.ConstantTimeCompare(serverHash, expectedServerHash) != 1 { - return errors.New(fmt.Sprintf("mismatch in server hash")) - } - - clientHash = computeClientHash(info, clientNonce, serverNonce) - _, err = s.Write(clientHash) - if err != nil { - return err - } - - status := make([]byte, 1) - _, err = io.ReadFull(r, status) - if err != nil { - return err - } - if status[0] != 1 { - return errors.New(fmt.Sprintf("server rejected authentication")) - } - - if r.Buffered() != 0 { - return errors.New(fmt.Sprintf("%d bytes left after extended OR port authentication", r.Buffered())) - } - - return nil -} - -// See section 3.1 of 196-transport-control-ports.txt. -const ( - extOrCmdDone = 0x0000 - extOrCmdUserAddr = 0x0001 - extOrCmdTransport = 0x0002 - extOrCmdOkay = 0x1000 - extOrCmdDeny = 0x1001 -) - -func extOrPortWriteCommand(s *net.TCPConn, cmd uint16, body []byte) error { - var buf bytes.Buffer - if len(body) > 65535 { - return errors.New("command exceeds maximum length of 65535") - } - err := binary.Write(&buf, binary.BigEndian, cmd) - if err != nil { - return err - } - err = binary.Write(&buf, binary.BigEndian, uint16(len(body))) - if err != nil { - return err - } - err = binary.Write(&buf, binary.BigEndian, body) - if err != nil { - return err - } - _, err = s.Write(buf.Bytes()) - if err != nil { - return err - } - - return nil -} - -// Send a USERADDR command on s. See section 3.1.2.1 of -// 196-transport-control-ports.txt. -func extOrPortSendUserAddr(s *net.TCPConn, conn net.Conn) error { - return extOrPortWriteCommand(s, extOrCmdUserAddr, []byte(conn.RemoteAddr().String())) -} - -// Send a TRANSPORT command on s. See section 3.1.2.2 of -// 196-transport-control-ports.txt. -func extOrPortSendTransport(s *net.TCPConn, methodName string) error { - return extOrPortWriteCommand(s, extOrCmdTransport, []byte(methodName)) -} - -// Send a DONE command on s. See section 3.1 of 196-transport-control-ports.txt. -func extOrPortSendDone(s *net.TCPConn) error { - return extOrPortWriteCommand(s, extOrCmdDone, []byte{}) -} - -func extOrPortRecvCommand(s *net.TCPConn) (cmd uint16, body []byte, err error) { - var bodyLen uint16 - data := make([]byte, 4) - - _, err = io.ReadFull(s, data) - if err != nil { - return - } - buf := bytes.NewBuffer(data) - err = binary.Read(buf, binary.BigEndian, &cmd) - if err != nil { - return - } - err = binary.Read(buf, binary.BigEndian, &bodyLen) - if err != nil { - return - } - body = make([]byte, bodyLen) - _, err = io.ReadFull(s, body) - if err != nil { - return - } - - return cmd, body, err -} - -// Send USERADDR and TRANSPORT commands followed by a DONE command. Wait for an -// OKAY or DENY response command from the server. Returns nil if and only if -// OKAY is received. -func extOrPortSetup(s *net.TCPConn, conn net.Conn, methodName string) error { - var err error - - err = extOrPortSendUserAddr(s, conn) - if err != nil { - return err - } - err = extOrPortSendTransport(s, methodName) - if err != nil { - return err - } - err = extOrPortSendDone(s) - if err != nil { - return err - } - cmd, _, err := extOrPortRecvCommand(s) - if err != nil { - return err - } - if cmd == extOrCmdDeny { - return errors.New("server returned DENY after our USERADDR and DONE") - } else if cmd != extOrCmdOkay { - return errors.New(fmt.Sprintf("server returned unknown command 0x%04x after our USERADDR and DONE", cmd)) - } - - return nil -} - -// Connect to info.ExtendedOrAddr if defined, or else info.OrAddr, and return an -// open *net.TCPConn. If connecting to the extended OR port, extended OR port -// authentication à la 217-ext-orport-auth.txt is done before returning; an -// error is returned if authentication fails. -func ConnectOr(info *ServerInfo, conn net.Conn, methodName string) (*net.TCPConn, error) { - if info.ExtendedOrAddr == nil { - return net.DialTCP("tcp", nil, info.OrAddr) - } - - s, err := net.DialTCP("tcp", nil, info.ExtendedOrAddr) - if err != nil { - return nil, err - } - s.SetDeadline(time.Now().Add(5 * time.Second)) - err = extOrPortAuthenticate(s, info) - if err != nil { - s.Close() - return nil, err - } - err = extOrPortSetup(s, conn, methodName) - if err != nil { - s.Close() - return nil, err - } - s.SetDeadline(time.Time{}) - - return s, nil -} diff --git a/pt/pt_test.go b/pt/pt_test.go deleted file mode 100644 index cc7924a..0000000 --- a/pt/pt_test.go +++ /dev/null @@ -1,61 +0,0 @@ -package pt - -import "os" -import "testing" - -func stringIsSafe(s string) bool { - for _, c := range []byte(s) { - if c == '\x00' || c == '\n' || c > 127 { - return false - } - } - return true -} - -func TestEscape(t *testing.T) { - tests := [...]string{ - "", - "abc", - "a\nb", - "a\b", - "ab\", - "ab\\n", - "ab\n\", - } - - check := func(input string) { - output := escape(input) - if !stringIsSafe(output) { - t.Errorf("escape(%q) → %q", input, output) - } - } - for _, input := range tests { - check(input) - } - for b := 0; b < 256; b++ { - // check one-byte string with each byte value 0–255 - check(string([]byte{byte(b)})) - // check UTF-8 encoding of each character 0–255 - check(string(b)) - } -} - -func TestGetManagedTransportVer(t *testing.T) { - tests := [...]struct { - input, expected string - }{ - {"1", "1"}, - {"1,1", "1"}, - {"1,2", "1"}, - {"2,1", "1"}, - {"2", ""}, - } - - for _, test := range tests { - os.Setenv("TOR_PT_MANAGED_TRANSPORT_VER", test.input) - output := getManagedTransportVer() - if output != test.expected { - t.Errorf("%q → %q (expected %q)", test.input, output, test.expected) - } - } -} diff --git a/pt/socks/socks.go b/pt/socks/socks.go deleted file mode 100644 index 788d53c..0000000 --- a/pt/socks/socks.go +++ /dev/null @@ -1,107 +0,0 @@ -// SOCKS4a server library. - -package socks - -import ( - "bufio" - "errors" - "fmt" - "io" - "net" -) - -const ( - socksVersion = 0x04 - socksCmdConnect = 0x01 - socksResponseVersion = 0x00 - socksRequestGranted = 0x5a - socksRequestFailed = 0x5b -) - -// Read a SOCKS4a connect request, and call the given connect callback with the -// requested destination string. If the callback returns an error, sends a SOCKS -// request failed message. Otherwise, sends a SOCKS request granted message for -// the destination address returned by the callback. -func AwaitSocks4aConnect(conn *net.TCPConn, connect func(string) (*net.TCPAddr, error)) error { - dest, err := readSocks4aConnect(conn) - if err != nil { - sendSocks4aResponseFailed(conn) - return err - } - destAddr, err := connect(dest) - if err != nil { - sendSocks4aResponseFailed(conn) - return err - } - sendSocks4aResponseGranted(conn, destAddr) - return nil -} - -// Read a SOCKS4a connect request. Returns a "host:port" string. -func readSocks4aConnect(s io.Reader) (string, error) { - r := bufio.NewReader(s) - - var h [8]byte - n, err := io.ReadFull(r, h[:]) - if err != nil { - return "", errors.New(fmt.Sprintf("after %d bytes of SOCKS header: %s", n, err)) - } - if h[0] != socksVersion { - return "", errors.New(fmt.Sprintf("SOCKS header had version 0x%02x, not 0x%02x", h[0], socksVersion)) - } - if h[1] != socksCmdConnect { - return "", errors.New(fmt.Sprintf("SOCKS header had command 0x%02x, not 0x%02x", h[1], socksCmdConnect)) - } - - _, err = r.ReadBytes('\x00') - if err != nil { - return "", errors.New(fmt.Sprintf("reading SOCKS userid: %s", err)) - } - - var port int - var host string - - port = int(h[2])<<8 | int(h[3])<<0 - if h[4] == 0 && h[5] == 0 && h[6] == 0 && h[7] != 0 { - hostBytes, err := r.ReadBytes('\x00') - if err != nil { - return "", errors.New(fmt.Sprintf("reading SOCKS4a destination: %s", err)) - } - host = string(hostBytes[:len(hostBytes)-1]) - } else { - host = net.IPv4(h[4], h[5], h[6], h[7]).String() - } - - if r.Buffered() != 0 { - return "", errors.New(fmt.Sprintf("%d bytes left after SOCKS header", r.Buffered())) - } - - return fmt.Sprintf("%s:%d", host, port), nil -} - -// Send a SOCKS4a response with the given code and address. -func sendSocks4aResponse(w io.Writer, code byte, addr *net.TCPAddr) error { - var resp [8]byte - resp[0] = socksResponseVersion - resp[1] = code - resp[2] = byte((addr.Port >> 8) & 0xff) - resp[3] = byte((addr.Port >> 0) & 0xff) - resp[4] = addr.IP[0] - resp[5] = addr.IP[1] - resp[6] = addr.IP[2] - resp[7] = addr.IP[3] - _, err := w.Write(resp[:]) - return err -} - -var emptyAddr = net.TCPAddr{IP: net.IPv4(0, 0, 0, 0), Port: 0} - -// Send a SOCKS4a response code 0x5a. -func sendSocks4aResponseGranted(w io.Writer, addr *net.TCPAddr) error { - return sendSocks4aResponse(w, socksRequestGranted, addr) -} - -// Send a SOCKS4a response code 0x5b (with an all-zero address). -func sendSocks4aResponseFailed(w io.Writer) error { - return sendSocks4aResponse(w, socksRequestFailed, &emptyAddr) -} diff --git a/websocket-client/websocket-client.go b/websocket-client/websocket-client.go index 7f838bb..547d217 100644 --- a/websocket-client/websocket-client.go +++ b/websocket-client/websocket-client.go @@ -18,11 +18,11 @@ import ( "time" )
-import "../pt" -import "../pt/socks" +import "git.torproject.org/pluggable-transports/goptlib.git" + +var ptInfo pt.ClientInfo
const ptMethodName = "websocket" -const socksTimeout = 2 * time.Second const bufSiz = 1500
var logFile = os.Stderr @@ -115,7 +115,7 @@ func proxy(local *net.TCPConn, ws *websocket.Conn) { wg.Wait() }
-func handleConnection(conn *net.TCPConn) error { +func handleConnection(conn *pt.SocksConn) error { defer conn.Close()
handlerChan <- 1 @@ -125,34 +125,32 @@ func handleConnection(conn *net.TCPConn) error {
var ws *websocket.Conn
- conn.SetDeadline(time.Now().Add(socksTimeout)) - err := socks.AwaitSocks4aConnect(conn, func(dest string) (*net.TCPAddr, error) { - // Disable deadline. - conn.SetDeadline(time.Time{}) - Log("SOCKS request for %s", dest) - destAddr, err := net.ResolveTCPAddr("tcp", dest) - if err != nil { - return nil, err - } - wsUrl := url.URL{Scheme: "ws", Host: dest} - ws, err = websocket.Dial(wsUrl.String(), "", wsUrl.String()) - if err != nil { - return nil, err - } - Log("WebSocket connection to %s", ws.Config().Location.String()) - return destAddr, nil - }) + 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() - proxy(conn, ws) + err = conn.Grant(destAddr) + if err != nil { + return err + } + + proxy(conn.Conn.(*net.TCPConn), ws) return nil }
-func socksAcceptLoop(ln *net.TCPListener) error { +func socksAcceptLoop(ln *pt.SocksListener) error { for { - socks, err := ln.AcceptTCP() + socks, err := ln.AcceptSocks() if err != nil { return err } @@ -166,12 +164,8 @@ func socksAcceptLoop(ln *net.TCPListener) error { return nil }
-func startListener(addrStr string) (*net.TCPListener, error) { - addr, err := net.ResolveTCPAddr("tcp", addrStr) - if err != nil { - return nil, err - } - ln, err := net.ListenTCP("tcp", addr) +func startListener(addrStr string) (*pt.SocksListener, error) { + ln, err := pt.ListenSocks("tcp", addrStr) if err != nil { return nil, err } @@ -188,6 +182,7 @@ func main() { var logFilename string var socksAddrStrs = []string{"127.0.0.1:0"} var socksArg string + var err error
flag.Usage = usage flag.StringVar(&logFilename, "log", "", "log file to write to") @@ -208,15 +203,20 @@ func main() { }
Log("starting") - pt.ClientSetup([]string{ptMethodName}) + ptInfo, err = pt.ClientSetup([]string{ptMethodName}) + if err != nil { + Log("error in setup: %s", err) + os.Exit(1) + }
- listeners := make([]*net.TCPListener, 0) + listeners := make([]net.Listener, 0) for _, socksAddrStr := range socksAddrStrs { ln, err := startListener(socksAddrStr) if err != nil { pt.CmethodError(ptMethodName, err.Error()) + continue } - pt.Cmethod(ptMethodName, "socks4", ln.Addr()) + pt.Cmethod(ptMethodName, ln.Version(), ln.Addr()) Log("listening on %s", ln.Addr().String()) listeners = append(listeners, ln) } diff --git a/websocket-server/websocket-server.go b/websocket-server/websocket-server.go index e5ed1c5..806dd51 100644 --- a/websocket-server/websocket-server.go +++ b/websocket-server/websocket-server.go @@ -20,9 +20,10 @@ import ( "time" )
-import "../pt" import "../websocket"
+import "git.torproject.org/pluggable-transports/goptlib.git" + const ptMethodName = "websocket" const requestTimeout = 10 * time.Second
@@ -176,7 +177,7 @@ func webSocketHandler(ws *websocket.WebSocket) { handlerChan <- -1 }()
- s, err := pt.ConnectOr(&ptInfo, ws.Conn, ptMethodName) + s, err := pt.DialOr(&ptInfo, ws.Conn.RemoteAddr(), ptMethodName) if err != nil { log("Failed to connect to ORPort: " + err.Error()) return @@ -225,23 +226,28 @@ func main() { }
log("starting") - ptInfo = pt.ServerSetup([]string{ptMethodName}) + 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 { + 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 + bindaddr.Addr.Port = port }
- ln, err := startListener(bindAddr.Addr) + ln, err := startListener(bindaddr.Addr) if err != nil { - pt.SmethodError(bindAddr.MethodName, err.Error()) + pt.SmethodError(bindaddr.MethodName, err.Error()) continue } - pt.Smethod(bindAddr.MethodName, ln.Addr()) + pt.Smethod(bindaddr.MethodName, ln.Addr()) log("listening on %s", ln.Addr().String()) listeners = append(listeners, ln) }