From ef38b844f9989dff25f553b583aeafc411dd100e Mon Sep 17 00:00:00 2001 From: Yawning Angel Date: Fri, 9 May 2014 10:23:58 +0000 Subject: Initial import. --- obfs4-client/obfs4-client.go | 183 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 183 insertions(+) create mode 100644 obfs4-client/obfs4-client.go (limited to 'obfs4-client') 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 + * 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= 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: + } + } +} -- cgit v1.2.3