/* * 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: } } }