diff options
-rw-r--r-- | modes/pt_socks5/pt_socks5.go | 99 | ||||
-rw-r--r-- | modes/stun_udp/stun_udp.go | 119 | ||||
-rw-r--r-- | modes/transparent_tcp/transparent_tcp.go | 31 | ||||
-rw-r--r-- | modes/transparent_udp/transparent_udp.go | 97 | ||||
-rw-r--r-- | shapeshifter-dispatcher/shapeshifter-dispatcher.go | 54 | ||||
-rw-r--r-- | state/dispatcher.log | 6 |
6 files changed, 257 insertions, 149 deletions
diff --git a/modes/pt_socks5/pt_socks5.go b/modes/pt_socks5/pt_socks5.go index e2aa546..cb7c974 100644 --- a/modes/pt_socks5/pt_socks5.go +++ b/modes/pt_socks5/pt_socks5.go @@ -33,6 +33,7 @@ import ( "fmt" options2 "github.com/OperatorFoundation/shapeshifter-dispatcher/common" "github.com/OperatorFoundation/shapeshifter-dispatcher/common/pt_extras" + "github.com/OperatorFoundation/shapeshifter-dispatcher/transports" "github.com/OperatorFoundation/shapeshifter-transports/transports/Dust" replicant "github.com/OperatorFoundation/shapeshifter-transports/transports/Replicant" "github.com/OperatorFoundation/shapeshifter-transports/transports/meeklite" @@ -41,7 +42,6 @@ import ( "io" "net" "net/url" - "strconv" "sync" "github.com/OperatorFoundation/shapeshifter-dispatcher/common/log" @@ -156,13 +156,13 @@ func clientHandler(name string, conn net.Conn, proxyURI *url.URL, options string return } -func ServerSetup(ptServerInfo pt.ServerInfo, options string) (launched bool, listeners []net.Listener) { +func ServerSetup(ptServerInfo pt.ServerInfo, statedir string, options string) (launched bool, listeners []net.Listener) { for _, bindaddr := range ptServerInfo.Bindaddrs { name := bindaddr.MethodName var listen func(address string) net.Listener - args, argsErr := pt.ParsePT2ClientParameters(options) + args, argsErr := options2.ParseServerOptions(options) if argsErr != nil { log.Errorf("Error parsing transport options: %s", options) return @@ -174,65 +174,92 @@ func ServerSetup(ptServerInfo pt.ServerInfo, options string) (launched bool, lis transport := obfs2.NewObfs2Transport() listen = transport.Listen case "obfs4": - var dialer proxy.Dialer - if cert, ok := args["cert"]; ok { - if iatModeStr, ok2 := args["iat-mode"]; ok2 { - iatMode, err := strconv.Atoi(iatModeStr[0]) - if err != nil { - transport := obfs4.NewObfs4Client(cert[0], iatMode, dialer) - listen = transport.Listen - } else { - log.Errorf("obfs4 transport bad iat-mode value: %s", iatModeStr) - return - } - } else { - log.Errorf("obfs4 transport missing cert argument: %s", args) - return - } - } else { - log.Errorf("obfs4 transport missing cert argument: %s", args) - return - } + transport := obfs4.NewObfs4Server(statedir) + listen = transport.Listen case "replicant": - config, ok := args.Get("config") - fmt.Println(config) - if !ok { + shargs, aok := args["Replicant"] + if !aok { return false, nil } - transport := replicant.New(replicant.Config{}) + + config, err := transports.ParseReplicantConfig(shargs) + if err != nil { + return false, nil + } + transport := replicant.New(*config) listen = transport.Listen case "Dust": - idPath, ok := args.Get("idPath") + shargs, aok := args["Dust"] + if !aok { + return false, nil + } + + untypedIdPath, ok := shargs["Url"] if !ok { return false, nil } + idPath, err := options2.CoerceToString(untypedIdPath) + if err != nil { + log.Errorf("could not coerce Dust Url to string") + return false, nil + } transport := Dust.NewDustServer(idPath) listen = transport.Listen case "meeklite": - Url, ok := args.Get("Url") + args, aok := args["meeklite"] + if !aok { + return false, nil + } + + untypedUrl, ok := args["Url"] if !ok { return false, nil } - Front, ok2 := args.Get("Front") - if !ok2 { + Url, err := options2.CoerceToString(untypedUrl) + if err != nil { + log.Errorf("could not coerce meeklite Url to string") + } + + untypedFront, ok := args["front"] + if !ok { return false, nil } - transport := meeklite.NewMeekTransportWithFront(Url, Front) - listen = transport.Listen + front, err2 := options2.CoerceToString(untypedFront) + if err2 != nil { + log.Errorf("could not coerce meeklite front to string") + } + + transport := meeklite.NewMeekTransportWithFront(Url, front) + listen = transport.Listen case "shadow": - password, ok := args.Get("password") + args, aok := args["shadow"] + if !aok { + return false, nil + } + + untypedPassword, ok := args["password"] if !ok { return false, nil } - cipherName, ok2 := args.Get("cipherName") - if !ok2 { + Password, err := options2.CoerceToString(untypedPassword) + if err != nil { + log.Errorf("could not coerce shadow password to string") + } + + untypedCertString, ok := args["Url"] + if !ok { return false, nil } - transport := shadow.NewShadowServer(password, cipherName) + certString, err2 := options2.CoerceToString(untypedCertString) + if err2 != nil { + log.Errorf("could not coerce meeklite Url to string") + } + + transport := shadow.NewShadowServer(Password, certString) listen = transport.Listen default: log.Errorf("Unknown transport: %s", name) diff --git a/modes/stun_udp/stun_udp.go b/modes/stun_udp/stun_udp.go index 3d72b69..e6d991c 100644 --- a/modes/stun_udp/stun_udp.go +++ b/modes/stun_udp/stun_udp.go @@ -33,6 +33,7 @@ import ( "fmt" options2 "github.com/OperatorFoundation/shapeshifter-dispatcher/common" "github.com/OperatorFoundation/shapeshifter-dispatcher/common/pt_extras" + "github.com/OperatorFoundation/shapeshifter-dispatcher/transports" "github.com/OperatorFoundation/shapeshifter-transports/transports/Dust" replicant "github.com/OperatorFoundation/shapeshifter-transports/transports/Replicant" "github.com/OperatorFoundation/shapeshifter-transports/transports/meeklite" @@ -189,7 +190,7 @@ func dialConn(tracker *ConnTracker, addr string, target string, name string, opt (*tracker)[addr] = ConnState{remote, false} } -func ServerSetup(ptServerInfo pt.ServerInfo, options string, stateDir string) (launched bool, listeners []net.Listener) { +func ServerSetup(ptServerInfo pt.ServerInfo, stateDir string, options string) (launched bool, listeners []net.Listener) { fmt.Println("ServerSetup") // Launch each of the server listeners. @@ -199,7 +200,7 @@ func ServerSetup(ptServerInfo pt.ServerInfo, options string, stateDir string) (l var listen func(address string) net.Listener - args, argsErr := pt.ParsePT2ClientParameters(options) + args, argsErr := options2.ParseServerOptions(options) if argsErr != nil { log.Errorf("Error parsing transport options: %s", options) return @@ -213,50 +214,92 @@ func ServerSetup(ptServerInfo pt.ServerInfo, options string, stateDir string) (l case "obfs4": transport := obfs4.NewObfs4Server(stateDir) listen = transport.Listen + case "Replicant": + shargs, aok := args["Replicant"] + if !aok { + return false, nil + } + + config, err := transports.ParseReplicantConfig(shargs) + if err != nil { + return false, nil + } + transport := replicant.New(*config) + listen = transport.Listen case "meeklite": - if Url, ok := args["Url"]; ok { - if Front, ok2 := args["Front"]; ok2 { - transport := meeklite.NewMeekTransportWithFront(Url[0], Front[0]) - listen = transport.Listen - } else { - log.Errorf("meeklite transport missing Url argument: %s", args) - return - } - } else { - log.Errorf("meeklite transport missing Front argument: %s", args) - return + args, aok := args["meeklite"] + if !aok { + return false, nil } - case "replicant": - if config, ok := args["config"]; ok { - fmt.Println(config) - transport := replicant.New(replicant.Config{}) - listen = transport.Listen - } else { - log.Errorf("replicant transport missing config argument: %s", args) - return + + untypedUrl, ok := args["Url"] + if !ok { + return false, nil } + + Url, err := options2.CoerceToString(untypedUrl) + if err != nil { + log.Errorf("could not coerce meeklite Url to string") + } + + untypedFront, ok := args["front"] + if !ok { + return false, nil + } + + front, err2 := options2.CoerceToString(untypedFront) + if err2 != nil { + log.Errorf("could not coerce meeklite front to string") + } + + transport := meeklite.NewMeekTransportWithFront(Url, front) + listen = transport.Listen case "Dust": - if idPath, ok := args["idPath"]; ok { - transport := Dust.NewDustServer(idPath[0]) - listen = transport.Listen - } else { - log.Errorf("Dust transport missing idPath argument: %s", args) - return + shargs, aok := args["Dust"] + if !aok { + return false, nil + } + + untypedIdPath, ok := shargs["Url"] + if !ok { + return false, nil } + idPath, err := options2.CoerceToString(untypedIdPath) + if err != nil { + log.Errorf("could not coerce Dust Url to string") + return false, nil + } + transport := Dust.NewDustServer(idPath) + listen = transport.Listen case "shadow": - if password, ok := args["password"]; ok { - if cipher, ok2 := args["cipherName"]; ok2 { - transport := shadow.NewShadowClient(password[0], cipher[0]) - listen = transport.Listen - } else { - log.Errorf("shadow transport missing cipher argument: %s", args) - return - } - } else { - log.Errorf("shadow transport missing password argument: %s", args) - return + args, aok := args["shadow"] + if !aok { + return false, nil + } + + untypedPassword, ok := args["password"] + if !ok { + return false, nil } + Password, err := options2.CoerceToString(untypedPassword) + if err != nil { + log.Errorf("could not coerce shadow password to string") + } + + untypedCertString, ok := args["Url"] + if !ok { + return false, nil + } + + certString, err2 := options2.CoerceToString(untypedCertString) + if err2 != nil { + log.Errorf("could not coerce meeklite Url to string") + } + + transport := shadow.NewShadowServer(Password, certString) + listen = transport.Listen + default: log.Errorf("Unknown transport: %s", name) return diff --git a/modes/transparent_tcp/transparent_tcp.go b/modes/transparent_tcp/transparent_tcp.go index c1482bb..06597e0 100644 --- a/modes/transparent_tcp/transparent_tcp.go +++ b/modes/transparent_tcp/transparent_tcp.go @@ -33,7 +33,9 @@ import ( "fmt" options2 "github.com/OperatorFoundation/shapeshifter-dispatcher/common" "github.com/OperatorFoundation/shapeshifter-dispatcher/common/pt_extras" + "github.com/OperatorFoundation/shapeshifter-dispatcher/transports" "github.com/OperatorFoundation/shapeshifter-transports/transports/Dust" + replicant "github.com/OperatorFoundation/shapeshifter-transports/transports/Replicant" "github.com/OperatorFoundation/shapeshifter-transports/transports/meeklite" "github.com/OperatorFoundation/shapeshifter-transports/transports/obfs2" "golang.org/x/net/proxy" @@ -140,23 +142,18 @@ func ServerSetup(ptServerInfo pt.ServerInfo, statedir string, options string) (l case "obfs4": transport := obfs4.NewObfs4Server(statedir) listen = transport.Listen - //FIXME make replicant case work for server side - //case "Replicant": - // shargs, aok := args["Replicant"] - // if !aok { - // return false, nil - // } - // - // configString, ok := shargs.Get("config") - // if !ok { - // return false, nil - // } - // config, err := transports.ParseReplicantConfig(configString) - // if err != nil { - // return false, nil - // } - // transport := replicant.New(config) - // listen = transport.Listen + case "Replicant": + shargs, aok := args["Replicant"] + if !aok { + return false, nil + } + + config, err := transports.ParseReplicantConfig(shargs) + if err != nil { + return false, nil + } + transport := replicant.New(*config) + listen = transport.Listen case "Dust": shargs, aok := args["Dust"] if !aok { diff --git a/modes/transparent_udp/transparent_udp.go b/modes/transparent_udp/transparent_udp.go index 506637e..ab0504e 100644 --- a/modes/transparent_udp/transparent_udp.go +++ b/modes/transparent_udp/transparent_udp.go @@ -36,6 +36,7 @@ import ( options2 "github.com/OperatorFoundation/shapeshifter-dispatcher/common" "github.com/OperatorFoundation/shapeshifter-dispatcher/common/log" "github.com/OperatorFoundation/shapeshifter-dispatcher/common/pt_extras" + "github.com/OperatorFoundation/shapeshifter-dispatcher/transports" "github.com/OperatorFoundation/shapeshifter-ipc" "github.com/OperatorFoundation/shapeshifter-transports/transports/Dust" replicant "github.com/OperatorFoundation/shapeshifter-transports/transports/Replicant" @@ -48,7 +49,6 @@ import ( golog "log" "net" "net/url" - "strconv" //"github.com/OperatorFoundation/shapeshifter-transports/transports/Optimizer" //"github.com/OperatorFoundation/shapeshifter-transports/transports/shadow" ) @@ -208,7 +208,7 @@ func dialConn(tracker *ConnTracker, addr string, target string, name string, opt (*tracker)[addr] = ConnState{remote, false} } -func ServerSetup(ptServerInfo pt.ServerInfo, options string) (launched bool, listeners []net.Listener) { +func ServerSetup(ptServerInfo pt.ServerInfo, stateDir string, options string) (launched bool, listeners []net.Listener) { fmt.Println("ServerSetup") // Launch each of the server listeners. @@ -218,7 +218,7 @@ func ServerSetup(ptServerInfo pt.ServerInfo, options string) (launched bool, lis var listen func(address string) net.Listener - args, argsErr := pt.ParsePT2ClientParameters(options) + args, argsErr := options2.ParseServerOptions(options) if argsErr != nil { log.Errorf("Error parsing transport options: %s", options) return @@ -230,67 +230,92 @@ func ServerSetup(ptServerInfo pt.ServerInfo, options string) (launched bool, lis transport := obfs2.NewObfs2Transport() listen = transport.Listen case "obfs4": - var dialer proxy.Dialer - if cert, ok := args["cert"]; ok { - if iatModeStr, ok2 := args["iat-mode"]; ok2 { - iatMode, err := strconv.Atoi(iatModeStr[0]) - if err != nil { - transport := obfs4.NewObfs4Client(cert[0], iatMode, dialer) - listen = transport.Listen - } else { - log.Errorf("obfs4 transport bad iat-mode value: %s", iatModeStr) - return - } - } else { - log.Errorf("obfs4 transport missing cert argument: %s", args) - return - } - } else { - log.Errorf("obfs4 transport missing cert argument: %s", args) - return - } + transport := obfs4.NewObfs4Server(stateDir) + listen = transport.Listen case "Replicant": - config, ok := args.Get("config") - fmt.Println(config) - if !ok { + shargs, aok := args["Replicant"] + if !aok { return false, nil } - transport := replicant.New(replicant.Config{}) + config, err := transports.ParseReplicantConfig(shargs) + if err != nil { + return false, nil + } + transport := replicant.New(*config) listen = transport.Listen case "Dust": - idPath, ok := args.Get("idPath") - if !ok { + shargs, aok := args["Dust"] + if !aok { return false, nil } + untypedIdPath, ok := shargs["Url"] + if !ok { + return false, nil + } + idPath, err := options2.CoerceToString(untypedIdPath) + if err != nil { + log.Errorf("could not coerce Dust Url to string") + return false, nil + } transport := Dust.NewDustServer(idPath) listen = transport.Listen case "meeklite": - Url, ok := args.Get("Url") + args, aok := args["meeklite"] + if !aok { + return false, nil + } + + untypedUrl, ok := args["Url"] if !ok { return false, nil } - Front, ok2 := args.Get("Front") - if !ok2 { + Url, err := options2.CoerceToString(untypedUrl) + if err != nil { + log.Errorf("could not coerce meeklite Url to string") + } + + untypedFront, ok := args["front"] + if !ok { return false, nil } - transport := meeklite.NewMeekTransportWithFront(Url, Front) + front, err2 := options2.CoerceToString(untypedFront) + if err2 != nil { + log.Errorf("could not coerce meeklite front to string") + } + + transport := meeklite.NewMeekTransportWithFront(Url, front) listen = transport.Listen case "shadow": - password, ok := args.Get("password") + args, aok := args["shadow"] + if !aok { + return false, nil + } + + untypedPassword, ok := args["password"] if !ok { return false, nil } - cipherName, ok2 := args.Get("cipherName") - if !ok2 { + Password, err := options2.CoerceToString(untypedPassword) + if err != nil { + log.Errorf("could not coerce shadow password to string") + } + + untypedCertString, ok := args["Url"] + if !ok { return false, nil } - transport := shadow.NewShadowServer(password, cipherName) + certString, err2 := options2.CoerceToString(untypedCertString) + if err2 != nil { + log.Errorf("could not coerce meeklite Url to string") + } + + transport := shadow.NewShadowServer(Password, certString) listen = transport.Listen default: log.Errorf("Unknown transport: %s", name) diff --git a/shapeshifter-dispatcher/shapeshifter-dispatcher.go b/shapeshifter-dispatcher/shapeshifter-dispatcher.go index 59263e2..83b4b24 100644 --- a/shapeshifter-dispatcher/shapeshifter-dispatcher.go +++ b/shapeshifter-dispatcher/shapeshifter-dispatcher.go @@ -79,8 +79,7 @@ func main() { } // PT 2.0 specification, 3.3.1.1. Common Configuration Parameters - // FIXME: in the spec, this is -version, which is already used for printing the version number - ptversion := flag.String("ptversion", "", "Specify the Pluggable Transport protocol version to use") + ptversion := flag.String("version", "", "Specify the Pluggable Transport protocol version to use") statePath := flag.String("state", "", "Specify the directory to use to store state information required by the transports") exitOnStdinClose := flag.Bool("exit-on-stdin-close", false, "Set to true to force the dispatcher to close when the stdin pipe is closed") @@ -102,7 +101,7 @@ func main() { optionsFile := flag.String("optionsFile", "", "store all the options in a single file") fmt.Println("checking for optionsFile") // Additional command line flags inherited from obfs4proxy - showVer := flag.Bool("version", false, "Print version and exit") + showVer := flag.Bool("showVersion", false, "Print version and exit") logLevelStr := flag.String("logLevel", "ERROR", "Log level (ERROR/WARN/INFO/DEBUG)") enableLogging := flag.Bool("enableLogging", false, "Log to TOR_PT_STATE_LOCATION/"+dispatcherLogFile) @@ -173,8 +172,11 @@ func main() { if *target == "" { log.Errorf("%s - transparent mode requires a target", execName) } else { - ptClientProxy, names := getClientNames(ptversion, transportsList, proxy) - + ptClientProxy, names, nameErr := getClientNames(ptversion, transportsList, proxy) + if nameErr != nil { + log.Errorf("must specify -version and -transports") + return + } launched = transparent_udp.ClientSetup(*socksAddr, *target, ptClientProxy, names, *options) } } else { @@ -185,7 +187,7 @@ func main() { // launched = transparent_udp.ServerSetup(termMon, *bindAddr, *target) ptServerInfo := getServerInfo(bindAddr, options, transportsList, orport, extorport, authcookie) - launched, _ = transparent_udp.ServerSetup(ptServerInfo, *options) + launched, _ = transparent_udp.ServerSetup(ptServerInfo, stateDir, *options) } } } else { @@ -195,8 +197,11 @@ func main() { if *target == "" { log.Errorf("%s - transparent mode requires a target", execName) } else { - ptClientProxy, names := getClientNames(ptversion, transportsList, proxy) - + ptClientProxy, names, nameErr := getClientNames(ptversion, transportsList, proxy) + if nameErr != nil { + log.Errorf("must specify -version and -transports") + return + } launched, _ = transparent_tcp.ClientSetup(*socksAddr, *target, ptClientProxy, names, *options) } } else { @@ -205,7 +210,7 @@ func main() { log.Errorf("%s - transparent mode requires a bindaddr", execName) } else { ptServerInfo := getServerInfo(bindAddr, options, transportsList, orport, extorport, authcookie) - launched, _ = transparent_tcp.ServerSetup(ptServerInfo, *statePath, *options) + launched, _ = transparent_tcp.ServerSetup(ptServerInfo, stateDir, *options) } } } @@ -217,8 +222,11 @@ func main() { if *target == "" { log.Errorf("%s - STUN mode requires a target", execName) } else { - ptClientProxy, names := getClientNames(ptversion, transportsList, proxy) - + ptClientProxy, names, nameErr := getClientNames(ptversion, transportsList, proxy) + if nameErr != nil { + log.Errorf("must specify -version and -transports") + return + } launched = stun_udp.ClientSetup(*socksAddr, *target, ptClientProxy, names, *options) } } else { @@ -227,7 +235,7 @@ func main() { log.Errorf("%s - STUN mode requires a bindaddr", execName) } else { ptServerInfo := getServerInfo(bindAddr, options, transportsList, orport, extorport, authcookie) - launched, _ = stun_udp.ServerSetup(ptServerInfo, *options, stateDir) + launched, _ = stun_udp.ServerSetup(ptServerInfo, stateDir, *options) } } } else { @@ -235,13 +243,16 @@ func main() { log.Infof("%s - initializing PT 2.0 proxy", execName) if isClient { log.Infof("%s - initializing client transport listeners", execName) - ptClientProxy, names := getClientNames(ptversion, transportsList, proxy) - + ptClientProxy, names, nameErr := getClientNames(ptversion, transportsList, proxy) + if nameErr != nil { + log.Errorf("must specify -version and -transports") + return + } launched, _ = pt_socks5.ClientSetup(*socksAddr, ptClientProxy, names, *options) } else { log.Infof("%s - initializing server transport listeners", execName) ptServerInfo := getServerInfo(bindAddr, options, transportsList, orport, extorport, authcookie) - launched, _ = pt_socks5.ServerSetup(ptServerInfo, *options) + launched, _ = pt_socks5.ServerSetup(ptServerInfo, stateDir, *options) } } } @@ -285,7 +296,7 @@ func makeStateDir(statePath string) (string, error) { } } -func getClientNames(ptversion *string, transportsList *string, proxy *string) (clientProxy *url.URL, names []string) { +func getClientNames(ptversion *string, transportsList *string, proxy *string) (clientProxy *url.URL, names []string, retErr error) { var ptClientInfo pt.ClientInfo var err error @@ -293,8 +304,7 @@ func getClientNames(ptversion *string, transportsList *string, proxy *string) (c log.Infof("Falling back to environment variables for ptversion/transports") ptClientInfo, err = pt.ClientSetup(transports.Transports()) if err != nil { - // FIXME - print a more useful error, specifying --ptversion and --transports flags - golog.Fatal(err) + return nil, nil, err } } else { if *transportsList == "*" { @@ -304,14 +314,14 @@ func getClientNames(ptversion *string, transportsList *string, proxy *string) (c } } - ptClientProxy, err := pt_extras.PtGetProxy(proxy) - if err != nil { - golog.Fatal(err) + ptClientProxy, proxyErr := pt_extras.PtGetProxy(proxy) + if proxyErr != nil { + return nil, nil, proxyErr } else if ptClientProxy != nil { pt_extras.PtProxyDone() } - return ptClientProxy, ptClientInfo.MethodNames + return ptClientProxy, ptClientInfo.MethodNames, nil } func getServerInfo(bindaddrList *string, options *string, transportList *string, orport *string, extorport *string, authcookie *string) pt.ServerInfo { diff --git a/state/dispatcher.log b/state/dispatcher.log index 3a53736..8609506 100644 --- a/state/dispatcher.log +++ b/state/dispatcher.log @@ -346,3 +346,9 @@ 2019/10/21 17:28:43 [INFO]: ___go_build_github_com_OperatorFoundation_shapeshifter_dispatcher_shapeshifter_dispatcher - initializing client transport listeners 2019/10/21 17:28:43 [INFO]: Optimizer - registered listener: 127.0.0.1:1444 2019/10/21 17:28:43 [INFO]: ___go_build_github_com_OperatorFoundation_shapeshifter_dispatcher_shapeshifter_dispatcher - accepting connections +2019/10/21 18:24:04 [NOTICE]: dispatcher-0.0.7-dev - launched +2019/10/21 18:24:04 [INFO]: ___go_build_github_com_OperatorFoundation_shapeshifter_dispatcher_shapeshifter_dispatcher - initializing transparent proxy +2019/10/21 18:24:04 [INFO]: ___go_build_github_com_OperatorFoundation_shapeshifter_dispatcher_shapeshifter_dispatcher - initializing TCP transparent proxy +2019/10/21 18:24:04 [INFO]: ___go_build_github_com_OperatorFoundation_shapeshifter_dispatcher_shapeshifter_dispatcher - initializing client transport listeners +2019/10/21 18:24:04 [INFO]: Optimizer - registered listener: 127.0.0.1:1444 +2019/10/21 18:24:04 [INFO]: ___go_build_github_com_OperatorFoundation_shapeshifter_dispatcher_shapeshifter_dispatcher - accepting connections |