summaryrefslogtreecommitdiff
path: root/obfs4-client
diff options
context:
space:
mode:
authorYawning Angel <yawning@schwanenlied.me>2014-05-09 10:23:58 +0000
committerYawning Angel <yawning@schwanenlied.me>2014-05-09 10:23:58 +0000
commitef38b844f9989dff25f553b583aeafc411dd100e (patch)
tree4162a60fd98f25c8f378523acd13e9adc319df52 /obfs4-client
Initial import.
Diffstat (limited to 'obfs4-client')
-rw-r--r--obfs4-client/obfs4-client.go183
1 files changed, 183 insertions, 0 deletions
diff --git a/obfs4-client/obfs4-client.go b/obfs4-client/obfs4-client.go
new file mode 100644
index 0000000..077fb85
--- /dev/null
+++ b/obfs4-client/obfs4-client.go
@@ -0,0 +1,183 @@
+/*
+ * 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:
+ }
+ }
+}