[Pkg-privacy-commits] [obfs4proxy] 04/151: Move to a unified client/server binary, and fix bugs.

Ximin Luo infinity0 at moszumanska.debian.org
Sat Aug 22 12:59:33 UTC 2015


This is an automated email from the git hooks/post-receive script.

infinity0 pushed a commit to branch master
in repository obfs4proxy.

commit 8e14ed44ad6ac76fd22e5d454824663f4a5f175f
Author: Yawning Angel <yawning at schwanenlied.me>
Date:   Mon May 12 00:02:24 2014 +0000

    Move to a unified client/server binary, and fix bugs.
    
     * The old and the busted: obfs4-[client,server].
     * The new hotness: obfs4client.
     * Add obfs4.ServerHandshake() that servers need to call after a
       successful return from Accept().  This allows implementations to
       move the handshake into a goroutine or whatever.
---
 .gitignore                   |   3 +-
 obfs4-client/obfs4-client.go | 183 --------------------
 obfs4-server/obfs4-server.go | 225 ------------------------
 obfs4.go                     |  79 ++++++---
 obfs4proxy/obfs4proxy.go     | 398 +++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 453 insertions(+), 435 deletions(-)

diff --git a/.gitignore b/.gitignore
index 5eac799..8251683 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1,4 @@
 *.swp
 *~
 
-obfs4-client/obfs4-client
-obfs4-server/obfs4-server
+obfs4proxy/obfs4proxy
diff --git a/obfs4-client/obfs4-client.go b/obfs4-client/obfs4-client.go
deleted file mode 100644
index 077fb85..0000000
--- a/obfs4-client/obfs4-client.go
+++ /dev/null
@@ -1,183 +0,0 @@
-/*
- * Copyright (c) 2014, Yawning Angel <yawning at torproject dot org>
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- *  * Redistributions of source code must retain the above copyright notice,
- *    this list of conditions and the following disclaimer.
- *
- *  * Redistributions in binary form must reproduce the above copyright notice,
- *    this list of conditions and the following disclaimer in the documentation
- *    and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- *
- * This file is based off goptlib's dummy-client.go file.
- */
-
-// obfs4 pluggable transport client. Works only as a managed proxy.
-//
-// Usage (in torrc):
-// 	UseBridges 1
-// 	Bridge obfs4 X.X.X.X:YYYY public-key=<Base64 Bridge public key> node-id=<Base64 Node ID>
-// 	ClientTransportPlugin obfs4 exec obfs4-client
-//
-// Becuase the pluggable transport requires arguments, using obfs4-client 
-// requires tor 0.2.5.x.
-package main
-
-import (
-	"io"
-	"net"
-	"os"
-	"os/signal"
-	"sync"
-	"syscall"
-
-	"github.com/yawning/obfs4"
-)
-
-import "git.torproject.org/pluggable-transports/goptlib.git"
-
-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)
-
-	// TODO: Log errors.
-	go func() {
-		io.Copy(b, a)
-		wg.Done()
-	}()
-	go func() {
-		io.Copy(a, b)
-		wg.Done()
-	}()
-
-	wg.Wait()
-}
-
-func handler(conn *pt.SocksConn) error {
-	// Extract the peer's node ID and public key.
-	nodeID, ok := conn.Req.Args.Get("node-id")
-	if !ok {
-		// TODO: Log something here.
-		conn.Reject()
-	}
-	publicKey, ok := conn.Req.Args.Get("public-key")
-	if !ok {
-		// TODO: Log something here.
-		conn.Reject()
-	}
-
-	handlerChan <- 1
-	defer func() {
-		handlerChan <- -1
-	}()
-
-	defer conn.Close()
-	remote, err := obfs4.Dial("tcp", conn.Req.Target, nodeID, publicKey)
-	if err != nil {
-		conn.Reject()
-		return err
-	}
-	defer remote.Close()
-	err = conn.Grant(remote.RemoteAddr().(*net.TCPAddr))
-	if err != nil {
-		return err
-	}
-
-	copyLoop(conn, remote)
-
-	return nil
-}
-
-func acceptLoop(ln *pt.SocksListener) error {
-	defer ln.Close()
-	for {
-		conn, err := ln.AcceptSocks()
-		if err != nil {
-			if e, ok := err.(net.Error); ok && !e.Temporary() {
-				return err
-			}
-			continue
-		}
-		go handler(conn)
-	}
-}
-
-func main() {
-	var err error
-
-	ptInfo, err = pt.ClientSetup([]string{"obfs4"})
-	if err != nil {
-		os.Exit(1)
-	}
-
-	listeners := make([]net.Listener, 0)
-	for _, methodName := range ptInfo.MethodNames {
-		switch methodName {
-		case "obfs4":
-			ln, err := pt.ListenSocks("tcp", "127.0.0.1:0")
-			if err != nil {
-				pt.CmethodError(methodName, err.Error())
-				break
-			}
-			go acceptLoop(ln)
-			pt.Cmethod(methodName, ln.Version(), ln.Addr())
-			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/obfs4-server/obfs4-server.go b/obfs4-server/obfs4-server.go
deleted file mode 100644
index aac2351..0000000
--- a/obfs4-server/obfs4-server.go
+++ /dev/null
@@ -1,225 +0,0 @@
-/*
- * Copyright (c) 2014, Yawning Angel <yawning at torproject dot org>
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- *  * Redistributions of source code must retain the above copyright notice,
- *    this list of conditions and the following disclaimer.
- *
- *  * Redistributions in binary form must reproduce the above copyright notice,
- *    this list of conditions and the following disclaimer in the documentation
- *    and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- *
- * This file is based off goptlib's dummy-server.go file.
- */
-
-// obfs4 pluggable transport server. Works only as a managed proxy.
-//
-// Usage (in torrc):
-// 	BridgeRelay 1
-// 	ORPort 9001
-// 	ExtORPort 6669
-// 	ServerTransportPlugin obfs4 exec obfs4-server
-//  ServerTransportOptions obfs4 private-key=<Base64 Bridge private key> node-id=<Base64 Node ID>
-//
-// Becuase the pluggable transport requires arguments, using obfs4-server
-// requires tor 0.2.5.x.
-package main
-
-import (
-	"encoding/hex"
-	"flag"
-	"fmt"
-	"io"
-	"net"
-	"os"
-	"os/signal"
-	"sync"
-	"syscall"
-
-	"github.com/yawning/obfs4"
-	"github.com/yawning/obfs4/ntor"
-)
-
-import "git.torproject.org/pluggable-transports/goptlib.git"
-
-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 handler(conn net.Conn) error {
-	defer conn.Close()
-
-	handlerChan <- 1
-	defer func() {
-		handlerChan <- -1
-	}()
-
-	or, err := pt.DialOr(&ptInfo, conn.RemoteAddr().String(), "obfs4")
-	if err != nil {
-		return err
-	}
-	defer or.Close()
-
-	copyLoop(conn, or)
-
-	return nil
-}
-
-func acceptLoop(ln net.Listener) error {
-	defer ln.Close()
-	for {
-		conn, err := ln.Accept()
-		if err != nil {
-			if e, ok := err.(net.Error); ok && !e.Temporary() {
-				return err
-			}
-			continue
-		}
-		go handler(conn)
-	}
-}
-
-func generateParams(id string) {
-	rawID, err := hex.DecodeString(id)
-	if err != nil {
-		fmt.Println("Failed to hex decode id:", err)
-		return
-	}
-
-	parsedID, err := ntor.NewNodeID(rawID)
-	if err != nil {
-		fmt.Println("Failed to parse id:", err)
-		return
-	}
-
-	fmt.Println("Generated node_id:", parsedID.Base64())
-
-	keypair, err := ntor.NewKeypair(false)
-	if err != nil {
-		fmt.Println("Failed to generate keypair:", err)
-		return
-	}
-
-	fmt.Println("Generated private-key:", keypair.Private().Base64())
-	fmt.Println("Generated public-key:", keypair.Public().Base64())
-}
-
-func main() {
-	var err error
-
-	// Some command line args.
-	genParams := flag.String("gen", "", "Generate params given a Node ID.")
-	flag.Parse()
-	if *genParams != "" {
-		generateParams(*genParams)
-		os.Exit(0)
-	}
-
-	// Ok, guess we're in PT land.
-	ptInfo, err = pt.ServerSetup([]string{"obfs4"})
-	if err != nil {
-		os.Exit(1)
-	}
-
-	listeners := make([]net.Listener, 0)
-	for _, bindaddr := range ptInfo.Bindaddrs {
-		switch bindaddr.MethodName {
-		case "obfs4":
-			// Handle the mandetory arguments.
-			privateKey, ok := bindaddr.Options.Get("private-key")
-			if !ok {
-				pt.SmethodError(bindaddr.MethodName, "need a private-key option")
-				break
-			}
-			nodeID, ok := bindaddr.Options.Get("node-id")
-			if !ok {
-				pt.SmethodError(bindaddr.MethodName, "need a node-id option")
-				break
-			}
-
-			ln, err := obfs4.Listen("tcp", bindaddr.Addr.String(), nodeID,
-									privateKey)
-			if err != nil {
-				pt.SmethodError(bindaddr.MethodName, err.Error())
-				break
-			}
-
-			oLn, _ := ln.(*obfs4.Obfs4Listener)
-			args := pt.Args{}
-			args.Add("node-id", nodeID)
-			args.Add("public-key", oLn.PublicKey())
-			go acceptLoop(ln)
-			pt.SmethodArgs(bindaddr.MethodName, ln.Addr(), args)
-			// TODO: Maybe log the args?
-			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:
-		}
-	}
-	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/obfs4.go b/obfs4.go
index afb0716..31575db 100644
--- a/obfs4.go
+++ b/obfs4.go
@@ -54,9 +54,26 @@ type Obfs4Conn struct {
 	receiveDecodedBuffer bytes.Buffer
 
 	isOk bool
+	isServer bool
+
+	// Server side state.
+	listener *Obfs4Listener
+}
+
+func (c *Obfs4Conn) closeAfterDelay() {
+	// I-it's not like I w-wanna handshake with or anything.  B-b-baka!
+
+	// XXX: Consume and immediately discard data of the network for a random
+	// period of time.
+
+	c.conn.Close();
 }
 
 func (c *Obfs4Conn) clientHandshake(nodeID *ntor.NodeID, publicKey *ntor.PublicKey) error {
+	if c.isServer {
+		panic("clientHandshake() called for server connection")
+	}
+
 	// Generate/send the client handshake.
 	hs, err := newClientHandshake(nodeID, publicKey)
 	if err != nil {
@@ -103,6 +120,10 @@ func (c *Obfs4Conn) clientHandshake(nodeID *ntor.NodeID, publicKey *ntor.PublicK
 }
 
 func (c *Obfs4Conn) serverHandshake(nodeID *ntor.NodeID, keypair *ntor.Keypair) error {
+	if !c.isServer {
+		panic("serverHandshake() called for client connection")
+	}
+
 	hs := newServerHandshake(nodeID, keypair)
 
 	// XXX: Set the request timer.
@@ -144,11 +165,39 @@ func (c *Obfs4Conn) serverHandshake(nodeID *ntor.NodeID, keypair *ntor.Keypair)
 		return err
 	}
 
+	// XXX: Generate/send the PRNG seed.
+
 	c.isOk = true
 
 	return nil
 }
 
+func (c *Obfs4Conn) ServerHandshake() error {
+	// Handshakes when already established are a no-op.
+	if c.isOk {
+		return nil;
+	}
+
+	// Clients handshake as part of Dial.
+	if !c.isServer {
+		panic("ServerHandshake() called for client connection")
+	}
+
+	// Regardless of what happens, don't need the listener past returning from
+	// this routine.
+	defer func() {
+		c.listener = nil
+	}()
+
+	// Complete the handshake.
+	err := c.serverHandshake(c.listener.nodeID, c.listener.keyPair)
+	if err != nil {
+		c.closeAfterDelay()
+	}
+
+	return err
+}
+
 func (c *Obfs4Conn) Read(b []byte) (int, error) {
 	if !c.isOk {
 		return 0, syscall.EINVAL
@@ -242,6 +291,8 @@ func (c *Obfs4Conn) Close() error {
 		return syscall.EINVAL
 	}
 
+	c.isOk = false;
+
 	return c.conn.Close()
 }
 
@@ -313,7 +364,7 @@ func Dial(network, address, nodeID, publicKey string) (net.Conn, error) {
 	return c, nil
 }
 
-// Obfs4Listener a obfs4 network listener.  Clients should use variables of
+// Obfs4Listener a obfs4 network listener.  Servers should use variables of
 // type Listener instead of assuming obfs4.
 type Obfs4Listener struct {
 	listener net.Listener
@@ -322,22 +373,6 @@ type Obfs4Listener struct {
 	nodeID  *ntor.NodeID
 }
 
-type ListenerError struct {
-	err error
-}
-
-func (e *ListenerError) Error() string {
-	return e.err.Error()
-}
-
-func (e *ListenerError) Temporary() bool {
-	return true
-}
-
-func (e *ListenerError) Timeout() bool {
-	return false
-}
-
 func (l *Obfs4Listener) Accept() (net.Conn, error) {
 	// Accept a connection.
 	c, err := l.listener.Accept()
@@ -348,14 +383,8 @@ func (l *Obfs4Listener) Accept() (net.Conn, error) {
 	// Allocate the obfs4 connection state.
 	cObfs := new(Obfs4Conn)
 	cObfs.conn = c
-
-	// Complete the handshake.
-	err = cObfs.serverHandshake(l.nodeID, l.keyPair)
-	if err != nil {
-		// XXX: Close after a delay.
-		c.Close()
-		return nil, &ListenerError{err}
-	}
+	cObfs.isServer = true
+	cObfs.listener = l
 
 	return cObfs, nil
 }
diff --git a/obfs4proxy/obfs4proxy.go b/obfs4proxy/obfs4proxy.go
new file mode 100644
index 0000000..f0d9595
--- /dev/null
+++ b/obfs4proxy/obfs4proxy.go
@@ -0,0 +1,398 @@
+/*
+ * Copyright (c) 2014, Yawning Angel <yawning at torproject dot org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *  * Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ *
+ *  * Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * This file is based off goptlib's dummy-[client,server].go files.
+ */
+
+// obfs4 pluggable transport.  Works only as a managed proxy.
+//
+// Client usage (in torrc):
+//   UseBridges 1
+//   Bridge obfs4 X.X.X.X:YYYY public-key=<Base64 Bridge public key> node-id=<Base64 Bridge Node ID>
+//   ClientTransportPlugin obfs4 exec obfs4proxy
+//
+// Server usage (in torrc):
+//   BridgeRelay 1
+//   ORPort 9001
+//   ExtORPort 6669
+//   ServerTransportPlugin obfs4 exec obfs4proxy
+//   ServerTransportOptions obfs4 private-key=<Base64 Bridge private key> node-id=<Base64 Node ID>
+//
+// Because the pluggable transport requires arguments, obfs4proxy requires
+// tor-0.2.5.x to be useful.
+
+package main
+
+import (
+	"encoding/hex"
+	"flag"
+	"fmt"
+	"io"
+	"log"
+	"net"
+	"os"
+	"os/signal"
+	"path"
+	"sync"
+	"syscall"
+
+	"github.com/yawning/obfs4"
+	"github.com/yawning/obfs4/ntor"
+	"git.torproject.org/pluggable-transports/goptlib.git"
+)
+
+const (
+	obfs4Method = "obfs4"
+	obfs4LogFile = "obfs4proxy.log"
+)
+
+var ptListeners []net.Listener
+
+// 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)
+
+	// XXX: Log/propagate errors.
+	go func() {
+		io.Copy(b, a)
+		wg.Done()
+	}()
+	go func() {
+		io.Copy(a, b)
+		wg.Done()
+	}()
+
+	wg.Wait()
+}
+
+func serverHandler(conn net.Conn, info *pt.ServerInfo) error {
+	defer conn.Close()
+
+	handlerChan <- 1
+	defer func() {
+		handlerChan <- -1
+	}()
+
+	// Handshake with the client.
+	oConn, _ := conn.(*obfs4.Obfs4Conn)
+	err := oConn.ServerHandshake()
+	if err != nil {
+		log.Printf("server: Handshake failed: %s", err)
+		return err
+	}
+
+	or, err := pt.DialOr(info, conn.RemoteAddr().String(), obfs4Method)
+	if err != nil {
+		log.Printf("server: DialOr failed: %s", err)
+		return err
+	}
+	defer or.Close()
+
+	copyLoop(conn, or)
+
+	return nil
+}
+
+func serverAcceptLoop(ln net.Listener, info *pt.ServerInfo) error {
+	defer ln.Close()
+	for {
+		conn, err := ln.Accept()
+		if err != nil {
+			if e, ok := err.(net.Error); ok && !e.Temporary() {
+				return err
+			}
+			continue
+		}
+		go serverHandler(conn, info)
+	}
+}
+
+func serverSetup() bool {
+	launch := false
+	var err error
+
+	ptServerInfo, err := pt.ServerSetup([]string{obfs4Method})
+	if err != nil {
+		return launch
+	}
+
+	for _, bindaddr := range ptServerInfo.Bindaddrs {
+		switch bindaddr.MethodName {
+		case obfs4Method:
+			// Handle the mandetory arguments.
+			privateKey, ok := bindaddr.Options.Get("private-key")
+			if !ok {
+				pt.SmethodError(bindaddr.MethodName, "need a private-key option")
+				break
+			}
+			nodeID, ok := bindaddr.Options.Get("node-id")
+			if !ok {
+				pt.SmethodError(bindaddr.MethodName, "need a node-id option")
+				break
+			}
+
+			// Initialize the listener.
+			ln, err := obfs4.Listen("tcp", bindaddr.Addr.String(), nodeID,
+									privateKey)
+			if err != nil {
+				pt.SmethodError(bindaddr.MethodName, err.Error())
+				break
+			}
+
+			// Report the SMETHOD including the parameters.
+			oLn, _ := ln.(*obfs4.Obfs4Listener)
+			args := pt.Args{}
+			args.Add("node-id", nodeID)
+			args.Add("public-key", oLn.PublicKey())
+			go serverAcceptLoop(ln, &ptServerInfo)
+			pt.SmethodArgs(bindaddr.MethodName, ln.Addr(), args)
+			ptListeners = append(ptListeners, ln)
+			launch = true
+		default:
+			pt.SmethodError(bindaddr.MethodName, "no such method")
+		}
+	}
+	pt.SmethodsDone()
+
+	return launch
+}
+
+func clientHandler(conn *pt.SocksConn) error {
+	defer conn.Close()
+
+	// Extract the peer's node ID and public key.
+	nodeID, ok := conn.Req.Args.Get("node-id")
+	if !ok {
+		log.Printf("client: missing node-id argument")
+		conn.Reject()
+		return nil
+	}
+	publicKey, ok := conn.Req.Args.Get("public-key")
+	if !ok {
+		log.Printf("client: missing public-key argument")
+		conn.Reject()
+		return nil
+	}
+
+	handlerChan <- 1
+	defer func() {
+		handlerChan <- -1
+	}()
+
+	remote, err := obfs4.Dial("tcp", conn.Req.Target, nodeID, publicKey)
+	if err != nil {
+		log.Printf("client: Handshake failed: %s", err)
+		conn.Reject()
+		return err
+	}
+	defer remote.Close()
+	err = conn.Grant(remote.RemoteAddr().(*net.TCPAddr))
+	if err != nil {
+		return err
+	}
+
+	copyLoop(conn, remote)
+
+	return nil
+}
+
+func clientAcceptLoop(ln *pt.SocksListener) error {
+	defer ln.Close()
+	for {
+		conn, err := ln.AcceptSocks()
+		if err != nil {
+			log.Println("AcceptSocks() failed:", err)
+			if e, ok := err.(net.Error); ok && !e.Temporary() {
+				return err
+			}
+			continue
+		}
+		go clientHandler(conn)
+	}
+}
+
+func clientSetup() bool {
+	launch := false
+
+	ptClientInfo, err := pt.ClientSetup([]string{obfs4Method})
+	if err != nil {
+		log.Fatal(err)
+		return launch
+	}
+
+	for _, methodName := range ptClientInfo.MethodNames {
+		switch methodName {
+		case obfs4Method:
+			ln, err := pt.ListenSocks("tcp", "127.0.0.1:0")
+			if err != nil {
+				pt.CmethodError(methodName, err.Error())
+				break
+			}
+			go clientAcceptLoop(ln)
+			pt.Cmethod(methodName, ln.Version(), ln.Addr())
+			ptListeners = append(ptListeners, ln)
+			launch = true
+		default:
+			pt.CmethodError(methodName, "no such method")
+		}
+	}
+	pt.CmethodsDone()
+
+	return launch
+}
+
+func ptIsClient() bool {
+	env := os.Getenv("TOR_PT_CLIENT_TRANSPORTS")
+	return env != ""
+}
+
+func ptIsServer() bool {
+	env := os.Getenv("TOR_PT_SERVER_TRANSPORTS")
+	return env != ""
+}
+
+func ptGetStateDir() string {
+	dir := os.Getenv("TOR_PT_STATE_LOCATION")
+	if dir == "" {
+		return dir
+	}
+
+	stat, err := os.Stat(dir)
+	if err != nil {
+		if !os.IsNotExist(err) {
+			log.Fatalf("Failed to stat log path: %s", err)
+		}
+		err = os.Mkdir(dir, 0755)
+		if err != nil {
+			log.Fatalf("Failed to create path: %s", err)
+		}
+	} else if !stat.IsDir() {
+		log.Fatalf("Pluggable Transport state location is not a directory")
+	}
+
+	return dir
+}
+
+func ptInitializeLogging() {
+	dir := ptGetStateDir()
+	if dir == "" {
+		return
+	}
+
+	f, err := os.OpenFile(path.Join(dir, obfs4LogFile), os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0600)
+	if err != nil {
+		log.Fatalf("Failed to open log file: %s", err)
+	}
+	log.SetOutput(f)
+}
+
+func generateServerParams(id string) {
+	rawID, err := hex.DecodeString(id)
+	if err != nil {
+		fmt.Println("Failed to hex decode id:", err)
+		return
+	}
+
+	parsedID, err := ntor.NewNodeID(rawID)
+	if err != nil {
+		fmt.Println("Failed to parse id:", err)
+		return
+	}
+
+	fmt.Println("Generated node_id:", parsedID.Base64())
+
+	keypair, err := ntor.NewKeypair(false)
+	if err != nil {
+		fmt.Println("Failed to generate keypair:", err)
+		return
+	}
+
+	fmt.Println("Generated private-key:", keypair.Private().Base64())
+	fmt.Println("Generated public-key:", keypair.Public().Base64())
+}
+
+func main() {
+	// Some command line args.
+	genParams := flag.String("gen", "", "Generate params given a Node ID.")
+	flag.Parse()
+	if *genParams != "" {
+		generateServerParams(*genParams)
+		os.Exit(0)
+	}
+
+	// Initialize pt logging.
+	ptInitializeLogging()
+
+	// Go through the pt protocol and initialize client or server mode.
+	launched := false
+	if ptIsClient() {
+		launched = clientSetup()
+	} else if ptIsServer() {
+		launched = serverSetup()
+	}
+	if !launched {
+		log.Fatal("obfs4proxy must be run as a managed transport or server.")
+	}
+
+	log.Println("obfs4proxy - Launched and listening")
+
+	// Handle termination notification.
+	numHandlers := 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 ptListeners {
+		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:
+		}
+	}
+}

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-privacy/packages/obfs4proxy.git



More information about the Pkg-privacy-commits mailing list