// The obfsproxy command creates a SOCKS5 obfuscating proxy. package main import ( "context" "encoding/json" "flag" "io" "log" "net" "os" "os/signal" "0xacab.org/leap/obfsvpn" "git.torproject.org/pluggable-transports/goptlib.git" ) const transportName = "obfs4" func main() { // Setup logging. logger := log.New(os.Stderr, "", log.LstdFlags) debug := log.New(io.Discard, "DEBUG ", log.LstdFlags) // Setup command line flags. var ( verbose bool addr string = "[::1]:0" vpnAddr string cfgFile string stateDir string ) flags := flag.NewFlagSet(os.Args[0], flag.ContinueOnError) flags.BoolVar(&verbose, "v", verbose, "Enable verbose logging") flags.StringVar(&addr, "addr", addr, "The address to listen on for client connections") flags.StringVar(&vpnAddr, "vpn", vpnAddr, "The address of the OpenVPN server to connect to") flags.StringVar(&cfgFile, "c", cfgFile, "The JSON config file to load") flags.StringVar(&stateDir, "state", stateDir, "A directory in which to store bridge state") err := flags.Parse(os.Args[1:]) if err != nil { logger.Fatalf("error parsing flags: %v", err) } if vpnAddr == "" { flags.PrintDefaults() logger.Fatal("must specify -vpn") } tcpVPNAddr, err := net.ResolveTCPAddr("tcp", vpnAddr) if err != nil { logger.Fatalf("error resolving VPN address: %v", err) } fd, err := os.Open(cfgFile) if err != nil { logger.Fatalf("error opening config file: %v", err) } var cfg Config err = json.NewDecoder(fd).Decode(&cfg) if err != nil { logger.Fatalf("error decoding config: %v", err) } // Configure logging. if verbose { debug.SetOutput(os.Stderr) } // Setup graceful shutdown. ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt) listenConfig := obfsvpn.ListenConfig{ NodeID: cfg.NodeID, PrivateKey: cfg.PrivateKey, Seed: cfg.DRBGSeed, StateDir: stateDir, } ln, err := listenConfig.Listen(ctx, "tcp", addr) if err != nil { logger.Fatalf("error binding to %s: %v", addr, err) } go func() { <-ctx.Done() // Stop releases the signal handling and falls back to the default behavior, // so sending another interrupt will immediately terminate. stop() logger.Printf("shutting down…") err := ln.Close() if err != nil { logger.Printf("error closing listener: %v", err) } }() info := &pt.ServerInfo{ OrAddr: tcpVPNAddr, } logger.Printf("listening on %s…", ln.Addr()) for { conn, err := ln.Accept() if err != nil { debug.Printf("error accepting connection: %v", err) return } debug.Printf("accepted connection %v…", conn) go proxyConn(ctx, info, conn, net.Dialer{}, logger, debug) } } func proxyConn(ctx context.Context, info *pt.ServerInfo, conn net.Conn, d net.Dialer, logger, debug *log.Logger) { defer func() { err := conn.Close() if err != nil { debug.Printf("error closing connection: %v", err) } }() // TODO: do we actually want to send the USERADDR/TRANSPORT ExtOrPort // commands? I don't understand how this works, so I'm unsure. remote, err := pt.DialOr(info, conn.RemoteAddr().String(), transportName) if err != nil { logger.Printf("error dialing remote: %v", err) return } defer func() { err := remote.Close() if err != nil { debug.Printf("error closing remote connection: %v", err) } }() go func() { _, err := io.Copy(remote, conn) if err != nil { logger.Printf("error proxying client data to remote: %v", err) return } }() go func() { _, err := io.Copy(conn, remote) if err != nil { logger.Printf("error proxying remote data to client: %v", err) return } }() }