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 - * 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: - } - } -} 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 - * 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= 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 + * 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= node-id= +// ClientTransportPlugin obfs4 exec obfs4proxy +// +// Server usage (in torrc): +// BridgeRelay 1 +// ORPort 9001 +// ExtORPort 6669 +// ServerTransportPlugin obfs4 exec obfs4proxy +// ServerTransportOptions obfs4 private-key= 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: + } + } +}