From 5e932dbbb9b5aac03ec9dde73335d5011b51fbde Mon Sep 17 00:00:00 2001 From: Moses Narrow Date: Sun, 30 Jun 2024 10:53:40 -0500 Subject: [PATCH 01/21] improve dmsgweb --- cmd/dmsgweb/commands/dmsgweb.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/cmd/dmsgweb/commands/dmsgweb.go b/cmd/dmsgweb/commands/dmsgweb.go index 8f74616a..d85f2a50 100644 --- a/cmd/dmsgweb/commands/dmsgweb.go +++ b/cmd/dmsgweb/commands/dmsgweb.go @@ -180,7 +180,7 @@ dmsgweb conf file detected: ` + dmsgwebconffile dmsgWebLog.Info("dmsg client pk: ", pk.String()) if len(resolveDmsgAddr) > 0 { dialPK = make([]cipher.PubKey, len(resolveDmsgAddr)) - dmsgPorts = make([]uint, dmsgSessions) + dmsgPorts = make([]uint, len(resolveDmsgAddr)) for i, dmsgaddr := range resolveDmsgAddr { dmsgWebLog.Info("dmsg address to dial: ", dmsgaddr) dmsgAddr = strings.Split(dmsgaddr, ":") @@ -201,7 +201,6 @@ dmsgweb conf file detected: ` + dmsgwebconffile } } } - dmsgWebLog.Info("test") dmsgC, closeDmsg, err := startDmsg(ctx, pk, sk) if err != nil { dmsgWebLog.WithError(err).Fatal("failed to start dmsg") @@ -272,15 +271,19 @@ dmsgweb conf file detected: ` + dmsgwebconffile if len(resolveDmsgAddr) == 0 && len(webPort) == 1 { if rawTCP[0] { + dmsgWebLog.Debug("proxyTCPConn(-1)") proxyTCPConn(-1) } else { + dmsgWebLog.Debug("proxyHTTPConn(-1)") proxyHTTPConn(-1) } } else { for i := range resolveDmsgAddr { if rawTCP[i] { + dmsgWebLog.Debug("proxyTCPConn("+fmt.Sprintf("%v",i)+")") proxyTCPConn(i) } else { + dmsgWebLog.Debug("proxyHTTPConn("+fmt.Sprintf("%v",i)+")") proxyHTTPConn(i) } } From d9b55f338b30544bd08d0ad6fa8f3b0b2b188561 Mon Sep 17 00:00:00 2001 From: Moses Narrow Date: Sun, 30 Jun 2024 12:16:23 -0500 Subject: [PATCH 02/21] make format --- cmd/dmsgweb/commands/dmsgweb.go | 79 +++++++++++++++++++-------------- 1 file changed, 45 insertions(+), 34 deletions(-) diff --git a/cmd/dmsgweb/commands/dmsgweb.go b/cmd/dmsgweb/commands/dmsgweb.go index d85f2a50..22438a26 100644 --- a/cmd/dmsgweb/commands/dmsgweb.go +++ b/cmd/dmsgweb/commands/dmsgweb.go @@ -205,7 +205,7 @@ dmsgweb conf file detected: ` + dmsgwebconffile if err != nil { dmsgWebLog.WithError(err).Fatal("failed to start dmsg") } - defer closeDmsg() + // defer closeDmsg() go func() { <-ctx.Done() @@ -280,10 +280,10 @@ dmsgweb conf file detected: ` + dmsgwebconffile } else { for i := range resolveDmsgAddr { if rawTCP[i] { - dmsgWebLog.Debug("proxyTCPConn("+fmt.Sprintf("%v",i)+")") + dmsgWebLog.Debug("proxyTCPConn(" + fmt.Sprintf("%v", i) + ")") proxyTCPConn(i) } else { - dmsgWebLog.Debug("proxyHTTPConn("+fmt.Sprintf("%v",i)+")") + dmsgWebLog.Debug("proxyHTTPConn(" + fmt.Sprintf("%v", i) + ")") proxyHTTPConn(i) } } @@ -378,45 +378,56 @@ func proxyTCPConn(n int) { if err != nil { dmsgWebLog.Fatalf("Failed to start TCP listener on port %v: %v", thiswebport, err) } - defer listener.Close() //nolint log.Printf("Serving TCP on 127.0.0.1:%v", thiswebport) - for { - conn, err := listener.Accept() - if err != nil { - log.Printf("Failed to accept connection: %v", err) - continue - } - - wg.Add(1) - go func(conn net.Conn, n int) { - defer wg.Done() - defer conn.Close() //nolint - - dmsgConn, err := dmsgC.DialStream(context.Background(), dmsg.Addr{PK: dialPK[n], Port: uint16(dmsgPorts[n])}) + go func() { + for { + conn, err := listener.Accept() if err != nil { - log.Printf("Failed to dial dmsg address %v:%v %v", dialPK[n].String(), dmsgPorts[n], err) - return + if opErr, ok := err.(*net.OpError); ok && opErr.Err.Error() == "use of closed network connection" { + log.Printf("Listener on port %v closed", thiswebport) + return + } + log.Printf("Failed to accept connection: %v", err) + continue } - defer dmsgConn.Close() //nolint + wg.Add(1) + go func(conn net.Conn, n int) { + defer wg.Done() + defer conn.Close() //nolint - go func() { - _, err := io.Copy(dmsgConn, conn) + dmsgConn, err := dmsgC.DialStream(context.Background(), dmsg.Addr{PK: dialPK[n], Port: uint16(dmsgPorts[n])}) if err != nil { - log.Printf("Error copying data to dmsg server: %v", err) + log.Printf("Failed to dial dmsg address %v:%v %v", dialPK[n].String(), dmsgPorts[n], err) + return } - dmsgConn.Close() //nolint - }() + defer dmsgConn.Close() //nolint - go func() { - _, err := io.Copy(conn, dmsgConn) - if err != nil { - log.Printf("Error copying data from dmsg server: %v", err) - } - conn.Close() //nolint - }() - }(conn, n) - } + go func() { + _, err := io.Copy(dmsgConn, conn) + if err != nil { + log.Printf("Error copying data to dmsg server: %v", err) + } + dmsgConn.Close() //nolint + }() + + go func() { + _, err := io.Copy(conn, dmsgConn) + if err != nil { + log.Printf("Error copying data from dmsg server: %v", err) + } + conn.Close() //nolint + }() + }(conn, n) + } + }() + + wg.Add(1) + go func() { + defer wg.Done() + <-context.Background().Done() + listener.Close() + }() } const envfileLinux = ` From 7c1b82c8df0986323a73d1f8d5780871305f3ada Mon Sep 17 00:00:00 2001 From: Moses Narrow Date: Sun, 30 Jun 2024 13:06:05 -0500 Subject: [PATCH 03/21] fix nil pointer error --- cmd/dmsgweb/commands/dmsgweb.go | 77 ++++++++++++++------------------- cmd/dmsgweb/commands/root.go | 1 + 2 files changed, 34 insertions(+), 44 deletions(-) diff --git a/cmd/dmsgweb/commands/dmsgweb.go b/cmd/dmsgweb/commands/dmsgweb.go index 22438a26..5029e615 100644 --- a/cmd/dmsgweb/commands/dmsgweb.go +++ b/cmd/dmsgweb/commands/dmsgweb.go @@ -201,11 +201,11 @@ dmsgweb conf file detected: ` + dmsgwebconffile } } } - dmsgC, closeDmsg, err := startDmsg(ctx, pk, sk) + dmsgC, closeDmsg, err = startDmsg(ctx, pk, sk) if err != nil { dmsgWebLog.WithError(err).Fatal("failed to start dmsg") } - // defer closeDmsg() + defer closeDmsg() go func() { <-ctx.Done() @@ -378,56 +378,45 @@ func proxyTCPConn(n int) { if err != nil { dmsgWebLog.Fatalf("Failed to start TCP listener on port %v: %v", thiswebport, err) } + defer listener.Close() //nolint log.Printf("Serving TCP on 127.0.0.1:%v", thiswebport) - go func() { - for { - conn, err := listener.Accept() + for { + conn, err := listener.Accept() + if err != nil { + log.Printf("Failed to accept connection: %v", err) + continue + } + + wg.Add(1) + go func(conn net.Conn, n int, dmsgC *dmsg.Client) { + defer wg.Done() + defer conn.Close() //nolint + + dmsgConn, err := dmsgC.DialStream(context.Background(), dmsg.Addr{PK: dialPK[n], Port: uint16(dmsgPorts[n])}) if err != nil { - if opErr, ok := err.(*net.OpError); ok && opErr.Err.Error() == "use of closed network connection" { - log.Printf("Listener on port %v closed", thiswebport) - return - } - log.Printf("Failed to accept connection: %v", err) - continue + log.Printf("Failed to dial dmsg address %v:%v %v", dialPK[n].String(), dmsgPorts[n], err) + return } - wg.Add(1) - go func(conn net.Conn, n int) { - defer wg.Done() - defer conn.Close() //nolint + defer dmsgConn.Close() //nolint - dmsgConn, err := dmsgC.DialStream(context.Background(), dmsg.Addr{PK: dialPK[n], Port: uint16(dmsgPorts[n])}) + go func() { + _, err := io.Copy(dmsgConn, conn) if err != nil { - log.Printf("Failed to dial dmsg address %v:%v %v", dialPK[n].String(), dmsgPorts[n], err) - return + log.Printf("Error copying data to dmsg client: %v", err) } - defer dmsgConn.Close() //nolint - - go func() { - _, err := io.Copy(dmsgConn, conn) - if err != nil { - log.Printf("Error copying data to dmsg server: %v", err) - } - dmsgConn.Close() //nolint - }() - - go func() { - _, err := io.Copy(conn, dmsgConn) - if err != nil { - log.Printf("Error copying data from dmsg server: %v", err) - } - conn.Close() //nolint - }() - }(conn, n) - } - }() + dmsgConn.Close() //nolint + }() - wg.Add(1) - go func() { - defer wg.Done() - <-context.Background().Done() - listener.Close() - }() + go func() { + _, err := io.Copy(conn, dmsgConn) + if err != nil { + log.Printf("Error copying data from dmsg client: %v", err) + } + conn.Close() //nolint + }() + }(conn, n, dmsgC) + } } const envfileLinux = ` diff --git a/cmd/dmsgweb/commands/root.go b/cmd/dmsgweb/commands/root.go index 091a3123..00d5e7c5 100644 --- a/cmd/dmsgweb/commands/root.go +++ b/cmd/dmsgweb/commands/root.go @@ -26,6 +26,7 @@ import ( var ( httpC http.Client dmsgC *dmsg.Client + closeDmsg func() dmsgDisc string dmsgSessions int dmsgAddr []string From 5232456ec1ec7b7d2f18bd0c5f6031d955fb5d30 Mon Sep 17 00:00:00 2001 From: Moses Narrow Date: Sun, 30 Jun 2024 13:10:43 -0500 Subject: [PATCH 04/21] add example tcp server & proxy --- examples/tcp/tcp-proxy.go | 88 +++++++++++++++++++++++++++++++++++++++ examples/tcp/tcp.go | 40 ++++++++++++++++++ 2 files changed, 128 insertions(+) create mode 100644 examples/tcp/tcp-proxy.go create mode 100644 examples/tcp/tcp.go diff --git a/examples/tcp/tcp-proxy.go b/examples/tcp/tcp-proxy.go new file mode 100644 index 00000000..96271c1a --- /dev/null +++ b/examples/tcp/tcp-proxy.go @@ -0,0 +1,88 @@ +package main + +import ( + "fmt" + "io" + "log" + "net" + "os" + "strconv" + "sync" +) + +func main() { + if len(os.Args) < 3 { + log.Fatalf("requires two arguments; usage: tcp1 ") + } + sourcePort, err := strconv.Atoi(os.Args[2]) + if err != nil { + log.Fatalf("Failed to parse tcp source port string \"%v\" to int: %v", sourcePort, err) + } + targetPort, err := strconv.Atoi(os.Args[1]) + if err != nil { + log.Fatalf("Failed to parse tcp target port string \"%v\" to int: %v", targetPort, err) + } + listener, err := net.Listen("tcp", fmt.Sprintf(":%d", sourcePort)) + if err != nil { + log.Fatalf("Failed to start TCP listener on port %d: %v", sourcePort, err) + } + defer listener.Close() + log.Printf("TCP proxy started: Listening on port %d and forwarding to port %d", sourcePort, targetPort) + + for { + conn, err := listener.Accept() + if err != nil { + log.Printf("Failed to accept connection: %v", err) + continue + } + + go handleConnection(conn, targetPort) + } +} + +func handleConnection(conn net.Conn, targetPort int) { + defer conn.Close() + + targetAddr := fmt.Sprintf("localhost:%d", targetPort) + target, err := net.Dial("tcp", targetAddr) + if err != nil { + log.Printf("Failed to dial target server %s: %v", targetAddr, err) + return + } + defer target.Close() + + var wg sync.WaitGroup + wg.Add(2) + + // Copy from client to target + go func() { + _, err := io.Copy(target, conn) + if err != nil && !isClosedConnErr(err) { + log.Printf("Error copying from client to target: %v", err) + } + target.Close() // Close target side after copy + wg.Done() + }() + + // Copy from target to client + go func() { + _, err := io.Copy(conn, target) + if err != nil && !isClosedConnErr(err) { + log.Printf("Error copying from target to client: %v", err) + } + conn.Close() // Close client side after copy + wg.Done() + }() + + // Wait for both copies to finish + wg.Wait() +} + +// isClosedConnErr checks if the error indicates a closed connection. +func isClosedConnErr(err error) bool { + if err == io.EOF { + return true + } + netErr, ok := err.(net.Error) + return ok && netErr.Timeout() // Check for timeout error indicating closed connection +} diff --git a/examples/tcp/tcp.go b/examples/tcp/tcp.go new file mode 100644 index 00000000..03f4acb6 --- /dev/null +++ b/examples/tcp/tcp.go @@ -0,0 +1,40 @@ +package main + +import ( + "fmt" + "net" + "os" +) + +func main() { + // Start a TCP server listening on port 8000 + listener, err := net.Listen("tcp", os.Args[1]) //":8000") + if err != nil { + fmt.Printf("Failed to start server: %v\n", err) + return + } + defer listener.Close() + fmt.Println("TCP server started on port " + os.Args[1]) + + // Accept and handle incoming connections + for { + conn, err := listener.Accept() + if err != nil { + fmt.Printf("Failed to accept connection: %v\n", err) + continue + } + go handleConnection(conn) + } +} + +func handleConnection(conn net.Conn) { + defer conn.Close() + + // Send a greeting message to the client + message := "Hello, World!\n" + _, err := conn.Write([]byte(message)) + if err != nil { + fmt.Printf("Error writing response: %v\n", err) + return + } +} From 4bb2a0cbee5347361e9a15b20d01d224be508bda Mon Sep 17 00:00:00 2001 From: Moses Narrow Date: Sun, 30 Jun 2024 13:13:39 -0500 Subject: [PATCH 05/21] fix ci errors --- examples/{tcp => tcp-proxy}/tcp-proxy.go | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename examples/{tcp => tcp-proxy}/tcp-proxy.go (100%) diff --git a/examples/tcp/tcp-proxy.go b/examples/tcp-proxy/tcp-proxy.go similarity index 100% rename from examples/tcp/tcp-proxy.go rename to examples/tcp-proxy/tcp-proxy.go From 1b40ba1af4d0c6275019ada68e9ce846e4e9f8ec Mon Sep 17 00:00:00 2001 From: Moses Narrow Date: Sun, 30 Jun 2024 14:48:36 -0500 Subject: [PATCH 06/21] update examples --- examples/tcp-proxy-dmsg/tcp-proxy-dmsg.go | 219 ++++++++++++++++ .../tcp-reverse-proxy-dmsg.go | 236 ++++++++++++++++++ 2 files changed, 455 insertions(+) create mode 100644 examples/tcp-proxy-dmsg/tcp-proxy-dmsg.go create mode 100644 examples/tcp-reverse-proxy-dmsg/tcp-reverse-proxy-dmsg.go diff --git a/examples/tcp-proxy-dmsg/tcp-proxy-dmsg.go b/examples/tcp-proxy-dmsg/tcp-proxy-dmsg.go new file mode 100644 index 00000000..710569dc --- /dev/null +++ b/examples/tcp-proxy-dmsg/tcp-proxy-dmsg.go @@ -0,0 +1,219 @@ +package main + +import ( + "context" + "fmt" + "io" + "net" + "net/http" + "os" + "sync" + + "github.com/skycoin/skywire-utilities/pkg/cipher" + "github.com/skycoin/skywire-utilities/pkg/cmdutil" + "github.com/skycoin/skywire-utilities/pkg/logging" + "github.com/skycoin/skywire-utilities/pkg/skyenv" + cc "github.com/ivanpirog/coloredcobra" + "github.com/spf13/cobra" + + "github.com/skycoin/dmsg/pkg/disc" + dmsg "github.com/skycoin/dmsg/pkg/dmsg" + +) + + + +func main() { + cc.Init(&cc.Config{ + RootCmd: srvCmd, + Headings: cc.HiBlue + cc.Bold, + Commands: cc.HiBlue + cc.Bold, + CmdShortDescr: cc.HiBlue, + Example: cc.HiBlue + cc.Italic, + ExecName: cc.HiBlue + cc.Bold, + Flags: cc.HiBlue + cc.Bold, + FlagsDescr: cc.HiBlue, + NoExtraNewlines: true, + NoBottomNewline: true, + }) + srvCmd.Execute() +} + +const help = "Usage:\r\n" + + " {{.UseLine}}{{if .HasAvailableSubCommands}}{{end}} {{if gt (len .Aliases) 0}}\r\n\r\n" + + "{{.NameAndAliases}}{{end}}{{if .HasAvailableSubCommands}}\r\n\r\n" + + "Available Commands:{{range .Commands}}{{if (or .IsAvailableCommand)}}\r\n " + + "{{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}{{if .HasAvailableLocalFlags}}\r\n\r\n" + + "Flags:\r\n" + + "{{.LocalFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasAvailableInheritedFlags}}\r\n\r\n" + + "Global Flags:\r\n" + + "{{.InheritedFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}\r\n\r\n" + + + +var ( + httpC http.Client + dmsgC *dmsg.Client + closeDmsg func() + dmsgDisc string + dmsgSessions int + dmsgAddr []string + dialPK []cipher.PubKey + filterDomainSuffix string + sk cipher.SecKey + pk cipher.PubKey + dmsgWebLog *logging.Logger + logLvl string + webPort []uint + proxyPort uint + addProxy string + resolveDmsgAddr []string + wg sync.WaitGroup + isEnvs bool + dmsgPort uint + dmsgPorts []uint + dmsgSess int + wl []string + wlkeys []cipher.PubKey + localPort uint + err error + rawTCP []bool + RootCmd = srvCmd +) + + +func init() { + srvCmd.Flags().UintVarP(&localPort, "lport", "l", 8086, "local application http interface port(s)") + srvCmd.Flags().UintVarP(&dmsgPort, "dport", "d", 8086, "dmsg port(s) to serve") + srvCmd.Flags().StringVarP(&dmsgDisc, "dmsg-disc", "D", skyenv.DmsgDiscAddr, "dmsg discovery url") + srvCmd.Flags().IntVarP(&dmsgSess, "dsess", "e", 1, "dmsg sessions") + srvCmd.Flags().VarP(&sk, "sk", "s", "a random key is generated if unspecified\n\r") + + srvCmd.CompletionOptions.DisableDefaultCmd = true + var helpflag bool + srvCmd.SetUsageTemplate(help) + srvCmd.PersistentFlags().BoolVarP(&helpflag, "help", "h", false, "help for dmsgweb") + srvCmd.SetHelpCommand(&cobra.Command{Hidden: true}) + srvCmd.PersistentFlags().MarkHidden("help") //nolint +} +var srvCmd = &cobra.Command{ + Use: "srv", + Short: "serve raw TCP from local port over dmsg", + Long: `DMSG web server - serve http or raw TCP interface from local port over dmsg`, + Run: func(_ *cobra.Command, _ []string) { + server() + }, +} + +func server() { + log := logging.MustGetLogger("dmsgwebsrv") + + ctx, cancel := cmdutil.SignalContext(context.Background(), log) + + defer cancel() + pk, err = sk.PubKey() + if err != nil { + pk, sk = cipher.GenerateKeyPair() + } + log.Infof("dmsg client pk: %v", pk.String()) + + + dmsgC := dmsg.NewClient(pk, sk, disc.NewHTTP(dmsgDisc, &http.Client{}, log), dmsg.DefaultConfig()) + defer func() { + if err := dmsgC.Close(); err != nil { + log.WithError(err).Error() + } + }() + + go dmsgC.Serve(context.Background()) + + select { + case <-ctx.Done(): + log.WithError(ctx.Err()).Warn() + return + + case <-dmsgC.Ready(): + } + + lis, err := dmsgC.Listen(uint16(dmsgPort)) + if err != nil { + log.Fatalf("Error listening on port %d: %v", dmsgPort, err) + } + + + go func(l net.Listener, port uint) { + <-ctx.Done() + if err := l.Close(); err != nil { + log.Printf("Error closing listener on port %d: %v", port, err) + log.WithError(err).Error() + } + }(lis, dmsgPort) + + + wg := new(sync.WaitGroup) + + wg.Add(1) + go func(localPort uint, lis net.Listener) { + defer wg.Done() + proxyTCPConnections(localPort, lis, log) + }(localPort, lis) + + wg.Wait() +} + +func proxyTCPConnections(localPort uint, lis net.Listener, log *logging.Logger) { + for { + conn, err := lis.Accept() + if err != nil { + log.Printf("Error accepting connection: %v", err) + return + } + + go handleTCPConnection(conn, localPort, log) + } +} + +func handleTCPConnection(dmsgConn net.Conn, localPort uint, log *logging.Logger) { + defer dmsgConn.Close() //nolint + + localConn, err := net.Dial("tcp", fmt.Sprintf("127.0.0.1:%d", localPort)) + if err != nil { + log.Printf("Error connecting to local port %d: %v", localPort, err) + return + } + defer localConn.Close() //nolint + + copyConn := func(dst net.Conn, src net.Conn) { + _, err := io.Copy(dst, src) + if err != nil { + log.Printf("Error during copy: %v", err) + } + } + + go copyConn(dmsgConn, localConn) + go copyConn(localConn, dmsgConn) +} + +func startDmsg(ctx context.Context, pk cipher.PubKey, sk cipher.SecKey) (dmsgC *dmsg.Client, stop func(), err error) { + dmsgC = dmsg.NewClient(pk, sk, disc.NewHTTP(dmsgDisc, &http.Client{}, dmsgWebLog), &dmsg.Config{MinSessions: dmsgSessions}) + go dmsgC.Serve(context.Background()) + + stop = func() { + err := dmsgC.Close() + dmsgWebLog.WithError(err).Debug("Disconnected from dmsg network.") + fmt.Printf("\n") + } + dmsgWebLog.WithField("public_key", pk.String()).WithField("dmsg_disc", dmsgDisc). + Debug("Connecting to dmsg network...") + + select { + case <-ctx.Done(): + stop() + os.Exit(0) + return nil, nil, ctx.Err() + + case <-dmsgC.Ready(): + dmsgWebLog.Debug("Dmsg network ready.") + return dmsgC, stop, nil + } +} diff --git a/examples/tcp-reverse-proxy-dmsg/tcp-reverse-proxy-dmsg.go b/examples/tcp-reverse-proxy-dmsg/tcp-reverse-proxy-dmsg.go new file mode 100644 index 00000000..e9c74a97 --- /dev/null +++ b/examples/tcp-reverse-proxy-dmsg/tcp-reverse-proxy-dmsg.go @@ -0,0 +1,236 @@ +package main + +import ( + "context" + "fmt" + "io" + "log" + "net" + "net/http" + "os" + "os/signal" + "path/filepath" + "strconv" + "strings" + "syscall" + "sync" + + "github.com/skycoin/skywire-utilities/pkg/buildinfo" + "github.com/skycoin/skywire-utilities/pkg/cipher" + "github.com/skycoin/skywire-utilities/pkg/cmdutil" + "github.com/skycoin/skywire-utilities/pkg/logging" + "github.com/skycoin/skywire-utilities/pkg/skyenv" + "github.com/spf13/cobra" + cc "github.com/ivanpirog/coloredcobra" + + dmsg "github.com/skycoin/dmsg/pkg/dmsg" + "github.com/skycoin/dmsg/pkg/disc" + + +) + + + +func main() { + cc.Init(&cc.Config{ + RootCmd: RootCmd, + Headings: cc.HiBlue + cc.Bold, + Commands: cc.HiBlue + cc.Bold, + CmdShortDescr: cc.HiBlue, + Example: cc.HiBlue + cc.Italic, + ExecName: cc.HiBlue + cc.Bold, + Flags: cc.HiBlue + cc.Bold, + FlagsDescr: cc.HiBlue, + NoExtraNewlines: true, + NoBottomNewline: true, + }) + RootCmd.Execute() +} + +const help = "Usage:\r\n" + + " {{.UseLine}}{{if .HasAvailableSubCommands}}{{end}} {{if gt (len .Aliases) 0}}\r\n\r\n" + + "{{.NameAndAliases}}{{end}}{{if .HasAvailableSubCommands}}\r\n\r\n" + + "Available Commands:{{range .Commands}}{{if (or .IsAvailableCommand)}}\r\n " + + "{{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}{{if .HasAvailableLocalFlags}}\r\n\r\n" + + "Flags:\r\n" + + "{{.LocalFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasAvailableInheritedFlags}}\r\n\r\n" + + "Global Flags:\r\n" + + "{{.InheritedFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}\r\n\r\n" + + + var ( + httpC http.Client + dmsgC *dmsg.Client + closeDmsg func() + dmsgDisc string + dmsgSessions int + dmsgAddr []string + dialPK cipher.PubKey + sk cipher.SecKey + pk cipher.PubKey + dmsgWebLog *logging.Logger + logLvl string + webPort uint + resolveDmsgAddr string + wg sync.WaitGroup + dmsgPort uint + dmsgSess int + err error + ) + + func init() { + RootCmd.Flags().UintVarP(&webPort, "port", "p", 8080, "port to serve the web application") + RootCmd.Flags().StringVarP(&resolveDmsgAddr, "resolve", "t", "", "resolve the specified dmsg address:port on the local port & disable proxy") + RootCmd.Flags().StringVarP(&dmsgDisc, "dmsg-disc", "d", skyenv.DmsgDiscAddr, "dmsg discovery url") + RootCmd.Flags().IntVarP(&dmsgSessions, "sess", "e", 1, "number of dmsg servers to connect to") + RootCmd.Flags().StringVarP(&logLvl, "loglvl", "l", "", "[ debug | warn | error | fatal | panic | trace | info ]\033[0m") + RootCmd.Flags().VarP(&sk, "sk", "s", "a random key is generated if unspecified\n\r") + } + + // RootCmd contains the root command for dmsgweb + var RootCmd = &cobra.Command{ + Use: func() string { + return strings.Split(filepath.Base(strings.ReplaceAll(strings.ReplaceAll(fmt.Sprintf("%v", os.Args), "[", ""), "]", "")), " ")[0] + }(), + Short: "DMSG reverse tcp proxy", + Long: "DMSG reverse tcp proxy", + SilenceErrors: true, + SilenceUsage: true, + DisableSuggestions: true, + DisableFlagsInUseLine: true, + Version: buildinfo.Version(), + Run: func(cmd *cobra.Command, _ []string) { + + c := make(chan os.Signal, 1) + signal.Notify(c, os.Interrupt, syscall.SIGTERM) //nolint + go func() { + <-c + os.Exit(1) + }() + if dmsgWebLog == nil { + dmsgWebLog = logging.MustGetLogger("dmsgweb") + } + if logLvl != "" { + if lvl, err := logging.LevelFromString(logLvl); err == nil { + logging.SetLevel(lvl) + } + } + + if dmsgDisc == "" { + dmsgDisc = skyenv.DmsgDiscAddr + } + ctx, cancel := cmdutil.SignalContext(context.Background(), dmsgWebLog) + defer cancel() + + pk, err := sk.PubKey() + if err != nil { + pk, sk = cipher.GenerateKeyPair() + } + dmsgWebLog.Info("dmsg client pk: ", pk.String()) + + dmsgWebLog.Info("dmsg address to dial: ", resolveDmsgAddr) + dmsgAddr = strings.Split(resolveDmsgAddr, ":") + var setpk cipher.PubKey + err = setpk.Set(dmsgAddr[0]) + if err != nil { + log.Fatalf("failed to parse dmsg
: : %v", err) + } + dialPK = setpk + if len(dmsgAddr) > 1 { + dport, err := strconv.ParseUint(dmsgAddr[1], 10, 64) + if err != nil { + log.Fatalf("Failed to parse dmsg port: %v", err) + } + dmsgPort = uint(dport) + } else { + dmsgPort = uint(80) + } + + dmsgC, closeDmsg, err = startDmsg(ctx, pk, sk) + if err != nil { + dmsgWebLog.WithError(err).Fatal("failed to start dmsg") + } + defer closeDmsg() + + go func() { + <-ctx.Done() + cancel() + closeDmsg() + os.Exit(0) + }() + + proxyTCPConn() + wg.Wait() + }, + } + + func proxyTCPConn() { + listener, err := net.Listen("tcp", fmt.Sprintf(":%v", webPort)) + if err != nil { + dmsgWebLog.Fatalf("Failed to start TCP listener on port %v: %v", webPort, err) + } + defer listener.Close() //nolint + log.Printf("Serving TCP on 127.0.0.1:%v", webPort) + + for { + conn, err := listener.Accept() + if err != nil { + log.Printf("Failed to accept connection: %v", err) + continue + } + + wg.Add(1) + go func(conn net.Conn) { + defer wg.Done() + defer conn.Close() //nolint + + dmsgConn, err := dmsgC.DialStream(context.Background(), dmsg.Addr{PK: dialPK, Port: uint16(dmsgPort)}) + if err != nil { + log.Printf("Failed to dial dmsg address %v:%v %v", dialPK.String(), dmsgPort, err) + return + } + defer dmsgConn.Close() //nolint + + go func() { + _, err := io.Copy(dmsgConn, conn) + if err != nil { + log.Printf("Error copying data to dmsg client: %v", err) + } + dmsgConn.Close() //nolint + }() + + go func() { + _, err := io.Copy(conn, dmsgConn) + if err != nil { + log.Printf("Error copying data from dmsg client: %v", err) + } + conn.Close() //nolint + }() + }(conn) + } + } + + + func startDmsg(ctx context.Context, pk cipher.PubKey, sk cipher.SecKey) (dmsgC *dmsg.Client, stop func(), err error) { + dmsgC = dmsg.NewClient(pk, sk, disc.NewHTTP(dmsgDisc, &http.Client{}, dmsgWebLog), &dmsg.Config{MinSessions: dmsgSessions}) + go dmsgC.Serve(context.Background()) + + stop = func() { + err := dmsgC.Close() + dmsgWebLog.WithError(err).Debug("Disconnected from dmsg network.") + fmt.Printf("\n") + } + dmsgWebLog.WithField("public_key", pk.String()).WithField("dmsg_disc", dmsgDisc). + Debug("Connecting to dmsg network...") + + select { + case <-ctx.Done(): + stop() + os.Exit(0) + return nil, nil, ctx.Err() + + case <-dmsgC.Ready(): + dmsgWebLog.Debug("Dmsg network ready.") + return dmsgC, stop, nil + } + } From 8ed7718e78f666ee456ecc9435c9c9fb72b6fa51 Mon Sep 17 00:00:00 2001 From: Moses Narrow Date: Tue, 2 Jul 2024 11:03:00 -0500 Subject: [PATCH 07/21] update examples --- examples/tcp-proxy-dmsg/tcp-proxy-dmsg.go | 16 +- .../tcp-reverse-proxy-dmsg.go | 310 +++++++++--------- 2 files changed, 156 insertions(+), 170 deletions(-) diff --git a/examples/tcp-proxy-dmsg/tcp-proxy-dmsg.go b/examples/tcp-proxy-dmsg/tcp-proxy-dmsg.go index 710569dc..0b04782a 100644 --- a/examples/tcp-proxy-dmsg/tcp-proxy-dmsg.go +++ b/examples/tcp-proxy-dmsg/tcp-proxy-dmsg.go @@ -9,20 +9,17 @@ import ( "os" "sync" + cc "github.com/ivanpirog/coloredcobra" "github.com/skycoin/skywire-utilities/pkg/cipher" "github.com/skycoin/skywire-utilities/pkg/cmdutil" "github.com/skycoin/skywire-utilities/pkg/logging" "github.com/skycoin/skywire-utilities/pkg/skyenv" - cc "github.com/ivanpirog/coloredcobra" "github.com/spf13/cobra" "github.com/skycoin/dmsg/pkg/disc" dmsg "github.com/skycoin/dmsg/pkg/dmsg" - ) - - func main() { cc.Init(&cc.Config{ RootCmd: srvCmd, @@ -49,8 +46,6 @@ const help = "Usage:\r\n" + "Global Flags:\r\n" + "{{.InheritedFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}\r\n\r\n" - - var ( httpC http.Client dmsgC *dmsg.Client @@ -78,10 +73,9 @@ var ( localPort uint err error rawTCP []bool - RootCmd = srvCmd + RootCmd = srvCmd ) - func init() { srvCmd.Flags().UintVarP(&localPort, "lport", "l", 8086, "local application http interface port(s)") srvCmd.Flags().UintVarP(&dmsgPort, "dport", "d", 8086, "dmsg port(s) to serve") @@ -96,10 +90,11 @@ func init() { srvCmd.SetHelpCommand(&cobra.Command{Hidden: true}) srvCmd.PersistentFlags().MarkHidden("help") //nolint } + var srvCmd = &cobra.Command{ Use: "srv", Short: "serve raw TCP from local port over dmsg", - Long: `DMSG web server - serve http or raw TCP interface from local port over dmsg`, + Long: `DMSG web server - serve http or raw TCP interface from local port over dmsg`, Run: func(_ *cobra.Command, _ []string) { server() }, @@ -117,7 +112,6 @@ func server() { } log.Infof("dmsg client pk: %v", pk.String()) - dmsgC := dmsg.NewClient(pk, sk, disc.NewHTTP(dmsgDisc, &http.Client{}, log), dmsg.DefaultConfig()) defer func() { if err := dmsgC.Close(); err != nil { @@ -140,7 +134,6 @@ func server() { log.Fatalf("Error listening on port %d: %v", dmsgPort, err) } - go func(l net.Listener, port uint) { <-ctx.Done() if err := l.Close(); err != nil { @@ -149,7 +142,6 @@ func server() { } }(lis, dmsgPort) - wg := new(sync.WaitGroup) wg.Add(1) diff --git a/examples/tcp-reverse-proxy-dmsg/tcp-reverse-proxy-dmsg.go b/examples/tcp-reverse-proxy-dmsg/tcp-reverse-proxy-dmsg.go index e9c74a97..155829a0 100644 --- a/examples/tcp-reverse-proxy-dmsg/tcp-reverse-proxy-dmsg.go +++ b/examples/tcp-reverse-proxy-dmsg/tcp-reverse-proxy-dmsg.go @@ -12,25 +12,21 @@ import ( "path/filepath" "strconv" "strings" - "syscall" "sync" + "syscall" + cc "github.com/ivanpirog/coloredcobra" "github.com/skycoin/skywire-utilities/pkg/buildinfo" "github.com/skycoin/skywire-utilities/pkg/cipher" "github.com/skycoin/skywire-utilities/pkg/cmdutil" "github.com/skycoin/skywire-utilities/pkg/logging" "github.com/skycoin/skywire-utilities/pkg/skyenv" "github.com/spf13/cobra" - cc "github.com/ivanpirog/coloredcobra" - dmsg "github.com/skycoin/dmsg/pkg/dmsg" "github.com/skycoin/dmsg/pkg/disc" - - + dmsg "github.com/skycoin/dmsg/pkg/dmsg" ) - - func main() { cc.Init(&cc.Config{ RootCmd: RootCmd, @@ -57,180 +53,178 @@ const help = "Usage:\r\n" + "Global Flags:\r\n" + "{{.InheritedFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}\r\n\r\n" +var ( + httpC http.Client + dmsgC *dmsg.Client + closeDmsg func() + dmsgDisc string + dmsgSessions int + dmsgAddr []string + dialPK cipher.PubKey + sk cipher.SecKey + pk cipher.PubKey + dmsgWebLog *logging.Logger + logLvl string + webPort uint + resolveDmsgAddr string + wg sync.WaitGroup + dmsgPort uint + dmsgSess int + err error +) - var ( - httpC http.Client - dmsgC *dmsg.Client - closeDmsg func() - dmsgDisc string - dmsgSessions int - dmsgAddr []string - dialPK cipher.PubKey - sk cipher.SecKey - pk cipher.PubKey - dmsgWebLog *logging.Logger - logLvl string - webPort uint - resolveDmsgAddr string - wg sync.WaitGroup - dmsgPort uint - dmsgSess int - err error - ) - - func init() { - RootCmd.Flags().UintVarP(&webPort, "port", "p", 8080, "port to serve the web application") - RootCmd.Flags().StringVarP(&resolveDmsgAddr, "resolve", "t", "", "resolve the specified dmsg address:port on the local port & disable proxy") - RootCmd.Flags().StringVarP(&dmsgDisc, "dmsg-disc", "d", skyenv.DmsgDiscAddr, "dmsg discovery url") - RootCmd.Flags().IntVarP(&dmsgSessions, "sess", "e", 1, "number of dmsg servers to connect to") - RootCmd.Flags().StringVarP(&logLvl, "loglvl", "l", "", "[ debug | warn | error | fatal | panic | trace | info ]\033[0m") - RootCmd.Flags().VarP(&sk, "sk", "s", "a random key is generated if unspecified\n\r") - } +func init() { + RootCmd.Flags().UintVarP(&webPort, "port", "p", 8080, "port to serve the web application") + RootCmd.Flags().StringVarP(&resolveDmsgAddr, "resolve", "t", "", "resolve the specified dmsg address:port on the local port & disable proxy") + RootCmd.Flags().StringVarP(&dmsgDisc, "dmsg-disc", "d", skyenv.DmsgDiscAddr, "dmsg discovery url") + RootCmd.Flags().IntVarP(&dmsgSessions, "sess", "e", 1, "number of dmsg servers to connect to") + RootCmd.Flags().StringVarP(&logLvl, "loglvl", "l", "", "[ debug | warn | error | fatal | panic | trace | info ]\033[0m") + RootCmd.Flags().VarP(&sk, "sk", "s", "a random key is generated if unspecified\n\r") +} - // RootCmd contains the root command for dmsgweb - var RootCmd = &cobra.Command{ - Use: func() string { - return strings.Split(filepath.Base(strings.ReplaceAll(strings.ReplaceAll(fmt.Sprintf("%v", os.Args), "[", ""), "]", "")), " ")[0] - }(), - Short: "DMSG reverse tcp proxy", - Long: "DMSG reverse tcp proxy", - SilenceErrors: true, - SilenceUsage: true, - DisableSuggestions: true, - DisableFlagsInUseLine: true, - Version: buildinfo.Version(), - Run: func(cmd *cobra.Command, _ []string) { - - c := make(chan os.Signal, 1) - signal.Notify(c, os.Interrupt, syscall.SIGTERM) //nolint - go func() { - <-c - os.Exit(1) - }() - if dmsgWebLog == nil { - dmsgWebLog = logging.MustGetLogger("dmsgweb") - } - if logLvl != "" { - if lvl, err := logging.LevelFromString(logLvl); err == nil { - logging.SetLevel(lvl) - } +// RootCmd contains the root command for dmsgweb +var RootCmd = &cobra.Command{ + Use: func() string { + return strings.Split(filepath.Base(strings.ReplaceAll(strings.ReplaceAll(fmt.Sprintf("%v", os.Args), "[", ""), "]", "")), " ")[0] + }(), + Short: "DMSG reverse tcp proxy", + Long: "DMSG reverse tcp proxy", + SilenceErrors: true, + SilenceUsage: true, + DisableSuggestions: true, + DisableFlagsInUseLine: true, + Version: buildinfo.Version(), + Run: func(cmd *cobra.Command, _ []string) { + + c := make(chan os.Signal, 1) + signal.Notify(c, os.Interrupt, syscall.SIGTERM) //nolint + go func() { + <-c + os.Exit(1) + }() + if dmsgWebLog == nil { + dmsgWebLog = logging.MustGetLogger("dmsgweb") + } + if logLvl != "" { + if lvl, err := logging.LevelFromString(logLvl); err == nil { + logging.SetLevel(lvl) } + } - if dmsgDisc == "" { - dmsgDisc = skyenv.DmsgDiscAddr - } - ctx, cancel := cmdutil.SignalContext(context.Background(), dmsgWebLog) - defer cancel() + if dmsgDisc == "" { + dmsgDisc = skyenv.DmsgDiscAddr + } + ctx, cancel := cmdutil.SignalContext(context.Background(), dmsgWebLog) + defer cancel() - pk, err := sk.PubKey() - if err != nil { - pk, sk = cipher.GenerateKeyPair() - } - dmsgWebLog.Info("dmsg client pk: ", pk.String()) + pk, err := sk.PubKey() + if err != nil { + pk, sk = cipher.GenerateKeyPair() + } + dmsgWebLog.Info("dmsg client pk: ", pk.String()) - dmsgWebLog.Info("dmsg address to dial: ", resolveDmsgAddr) - dmsgAddr = strings.Split(resolveDmsgAddr, ":") - var setpk cipher.PubKey - err = setpk.Set(dmsgAddr[0]) + dmsgWebLog.Info("dmsg address to dial: ", resolveDmsgAddr) + dmsgAddr = strings.Split(resolveDmsgAddr, ":") + var setpk cipher.PubKey + err = setpk.Set(dmsgAddr[0]) + if err != nil { + log.Fatalf("failed to parse dmsg
: : %v", err) + } + dialPK = setpk + if len(dmsgAddr) > 1 { + dport, err := strconv.ParseUint(dmsgAddr[1], 10, 64) if err != nil { - log.Fatalf("failed to parse dmsg
: : %v", err) - } - dialPK = setpk - if len(dmsgAddr) > 1 { - dport, err := strconv.ParseUint(dmsgAddr[1], 10, 64) - if err != nil { - log.Fatalf("Failed to parse dmsg port: %v", err) - } - dmsgPort = uint(dport) - } else { - dmsgPort = uint(80) + log.Fatalf("Failed to parse dmsg port: %v", err) } + dmsgPort = uint(dport) + } else { + dmsgPort = uint(80) + } - dmsgC, closeDmsg, err = startDmsg(ctx, pk, sk) - if err != nil { - dmsgWebLog.WithError(err).Fatal("failed to start dmsg") - } - defer closeDmsg() + dmsgC, closeDmsg, err = startDmsg(ctx, pk, sk) + if err != nil { + dmsgWebLog.WithError(err).Fatal("failed to start dmsg") + } + defer closeDmsg() - go func() { - <-ctx.Done() - cancel() - closeDmsg() - os.Exit(0) - }() + go func() { + <-ctx.Done() + cancel() + closeDmsg() + os.Exit(0) + }() + + proxyTCPConn() + wg.Wait() + }, +} - proxyTCPConn() - wg.Wait() - }, +func proxyTCPConn() { + listener, err := net.Listen("tcp", fmt.Sprintf(":%v", webPort)) + if err != nil { + dmsgWebLog.Fatalf("Failed to start TCP listener on port %v: %v", webPort, err) } + defer listener.Close() //nolint + log.Printf("Serving TCP on 127.0.0.1:%v", webPort) - func proxyTCPConn() { - listener, err := net.Listen("tcp", fmt.Sprintf(":%v", webPort)) + for { + conn, err := listener.Accept() if err != nil { - dmsgWebLog.Fatalf("Failed to start TCP listener on port %v: %v", webPort, err) + log.Printf("Failed to accept connection: %v", err) + continue } - defer listener.Close() //nolint - log.Printf("Serving TCP on 127.0.0.1:%v", webPort) - for { - conn, err := listener.Accept() + wg.Add(1) + go func(conn net.Conn) { + defer wg.Done() + defer conn.Close() //nolint + + dmsgConn, err := dmsgC.DialStream(context.Background(), dmsg.Addr{PK: dialPK, Port: uint16(dmsgPort)}) if err != nil { - log.Printf("Failed to accept connection: %v", err) - continue + log.Printf("Failed to dial dmsg address %v:%v %v", dialPK.String(), dmsgPort, err) + return } + defer dmsgConn.Close() //nolint - wg.Add(1) - go func(conn net.Conn) { - defer wg.Done() - defer conn.Close() //nolint + go func() { + _, err := io.Copy(dmsgConn, conn) + if err != nil { + log.Printf("Error copying data to dmsg client: %v", err) + } + dmsgConn.Close() //nolint + }() - dmsgConn, err := dmsgC.DialStream(context.Background(), dmsg.Addr{PK: dialPK, Port: uint16(dmsgPort)}) + go func() { + _, err := io.Copy(conn, dmsgConn) if err != nil { - log.Printf("Failed to dial dmsg address %v:%v %v", dialPK.String(), dmsgPort, err) - return + log.Printf("Error copying data from dmsg client: %v", err) } - defer dmsgConn.Close() //nolint - - go func() { - _, err := io.Copy(dmsgConn, conn) - if err != nil { - log.Printf("Error copying data to dmsg client: %v", err) - } - dmsgConn.Close() //nolint - }() - - go func() { - _, err := io.Copy(conn, dmsgConn) - if err != nil { - log.Printf("Error copying data from dmsg client: %v", err) - } - conn.Close() //nolint - }() - }(conn) - } + conn.Close() //nolint + }() + }(conn) } +} +func startDmsg(ctx context.Context, pk cipher.PubKey, sk cipher.SecKey) (dmsgC *dmsg.Client, stop func(), err error) { + dmsgC = dmsg.NewClient(pk, sk, disc.NewHTTP(dmsgDisc, &http.Client{}, dmsgWebLog), &dmsg.Config{MinSessions: dmsgSessions}) + go dmsgC.Serve(context.Background()) - func startDmsg(ctx context.Context, pk cipher.PubKey, sk cipher.SecKey) (dmsgC *dmsg.Client, stop func(), err error) { - dmsgC = dmsg.NewClient(pk, sk, disc.NewHTTP(dmsgDisc, &http.Client{}, dmsgWebLog), &dmsg.Config{MinSessions: dmsgSessions}) - go dmsgC.Serve(context.Background()) - - stop = func() { - err := dmsgC.Close() - dmsgWebLog.WithError(err).Debug("Disconnected from dmsg network.") - fmt.Printf("\n") - } - dmsgWebLog.WithField("public_key", pk.String()).WithField("dmsg_disc", dmsgDisc). - Debug("Connecting to dmsg network...") - - select { - case <-ctx.Done(): - stop() - os.Exit(0) - return nil, nil, ctx.Err() - - case <-dmsgC.Ready(): - dmsgWebLog.Debug("Dmsg network ready.") - return dmsgC, stop, nil - } + stop = func() { + err := dmsgC.Close() + dmsgWebLog.WithError(err).Debug("Disconnected from dmsg network.") + fmt.Printf("\n") + } + dmsgWebLog.WithField("public_key", pk.String()).WithField("dmsg_disc", dmsgDisc). + Debug("Connecting to dmsg network...") + + select { + case <-ctx.Done(): + stop() + os.Exit(0) + return nil, nil, ctx.Err() + + case <-dmsgC.Ready(): + dmsgWebLog.Debug("Dmsg network ready.") + return dmsgC, stop, nil } +} From d115f763383f896e1ba7ed5b46cbc3cc28a6fd31 Mon Sep 17 00:00:00 2001 From: Moses Narrow Date: Tue, 21 Jan 2025 18:03:16 -0600 Subject: [PATCH 08/21] make logging more uniform --- cmd/dmsgweb/commands/dmsgweb.go | 50 ++++++++++++++++++--------------- 1 file changed, 28 insertions(+), 22 deletions(-) diff --git a/cmd/dmsgweb/commands/dmsgweb.go b/cmd/dmsgweb/commands/dmsgweb.go index a8066514..04de4df6 100644 --- a/cmd/dmsgweb/commands/dmsgweb.go +++ b/cmd/dmsgweb/commands/dmsgweb.go @@ -5,7 +5,6 @@ import ( "context" "fmt" "io" - "log" "net" "net/http" "os" @@ -188,13 +187,13 @@ dmsgweb conf file detected: ` + dmsgwebconffile var setpk cipher.PubKey err := setpk.Set(dmsgAddr[0]) if err != nil { - log.Fatalf("failed to parse dmsg
: : %v", err) + dmsgWebLog.WithError(err).Fatal("failed to parse dmsg
:") } dialPK[i] = setpk if len(dmsgAddr) > 1 { dport, err := strconv.ParseUint(dmsgAddr[1], 10, 64) if err != nil { - log.Fatalf("Failed to parse dmsg port: %v", err) + dmsgWebLog.WithError(err).Fatal("Failed to parse dmsg port") } dmsgPorts[i] = uint(dport) } else { @@ -207,7 +206,7 @@ dmsgweb conf file detected: ` + dmsgwebconffile // Use SOCKS5 proxy dialer if specified dialer, err = proxy.SOCKS5("tcp", proxyAddr, nil, proxy.Direct) if err != nil { - log.Fatalf("Error creating SOCKS5 dialer: %v", err) + dmsgWebLog.WithError(err).Fatal("Error creating SOCKS5 dialer") } transport := &http.Transport{ Dial: dialer.Dial, @@ -267,11 +266,11 @@ dmsgweb conf file detected: ` + dmsgwebconffile // Start the SOCKS5 server socksAddr := fmt.Sprintf("127.0.0.1:%v", proxyPort) - log.Printf("SOCKS5 proxy server started on %s", socksAddr) + dmsgWebLog.Debug("SOCKS5 proxy server started on", socksAddr) server, err := socks5.New(conf) if err != nil { - log.Fatalf("Failed to create SOCKS5 server: %v", err) + dmsgWebLog.WithError(err).Fatal("Failed to create SOCKS5 server") } wg.Add(1) @@ -279,7 +278,7 @@ dmsgweb conf file detected: ` + dmsgwebconffile dmsgWebLog.Debug("Serving SOCKS5 proxy on " + socksAddr) err := server.ListenAndServe("tcp", socksAddr) if err != nil { - log.Fatalf("Failed to start SOCKS5 server: %v", err) + dmsgWebLog.WithError(err).Fatal("Failed to start SOCKS5 server") } defer server.Close() dmsgWebLog.Debug("Stopped serving SOCKS5 proxy on " + socksAddr) @@ -289,19 +288,19 @@ dmsgweb conf file detected: ` + dmsgwebconffile if len(resolveDmsgAddr) == 0 && len(webPort) == 1 { if rawTCP[0] { dmsgWebLog.Debug("proxyTCPConn(-1)") - proxyTCPConn(-1) + proxyTCPConn(-1, dmsgC) } else { dmsgWebLog.Debug("proxyHTTPConn(-1)") - proxyHTTPConn(-1) + proxyHTTPConn(-1, dmsgC) } } else { for i := range resolveDmsgAddr { if rawTCP[i] { dmsgWebLog.Debug("proxyTCPConn(" + fmt.Sprintf("%v", i) + ")") - proxyTCPConn(i) + proxyTCPConn(i, dmsgC) } else { dmsgWebLog.Debug("proxyHTTPConn(" + fmt.Sprintf("%v", i) + ")") - proxyHTTPConn(i) + proxyHTTPConn(i, dmsgC) } } } @@ -309,7 +308,7 @@ dmsgweb conf file detected: ` + dmsgwebconffile }, } -func proxyHTTPConn(n int) { +func proxyHTTPConn(n int, dmsgC *dmsg.Client) { r := gin.New() r.Use(gin.Recovery()) @@ -337,10 +336,11 @@ func proxyHTTPConn(n int) { } } - fmt.Printf("Proxying request: %s %s\n", c.Request.Method, urlStr) + dmsgWebLog.Debug("Proxying request: %s %s\n", c.Request.Method, urlStr) req, err := http.NewRequest(c.Request.Method, urlStr, c.Request.Body) if err != nil { c.String(http.StatusInternalServerError, "Failed to create HTTP request") + dmsgWebLog.WithError(err).Warn("Failed to create HTTP request") return } @@ -353,7 +353,7 @@ func proxyHTTPConn(n int) { resp, err := httpC.Do(req) if err != nil { c.String(http.StatusInternalServerError, "Failed to connect to HTTP server") - fmt.Printf("Error: %v\n", err) + dmsgWebLog.WithError(err).Warn("Failed to connect to HTTP server") return } defer resp.Body.Close() //nolint @@ -367,7 +367,7 @@ func proxyHTTPConn(n int) { c.Status(resp.StatusCode) if _, err := io.Copy(c.Writer, resp.Body); err != nil { c.String(http.StatusInternalServerError, "Failed to copy response body") - fmt.Printf("Error copying response body: %v\n", err) + dmsgWebLog.WithError(err).Warn("Failed to copy response body") } }) wg.Add(1) @@ -384,7 +384,7 @@ func proxyHTTPConn(n int) { wg.Done() }() } -func proxyTCPConn(n int) { +func proxyTCPConn(n int, dmsgC *dmsg.Client) { var thiswebport uint if n == -1 { thiswebport = webPort[0] @@ -393,15 +393,18 @@ func proxyTCPConn(n int) { } listener, err := net.Listen("tcp", fmt.Sprintf(":%v", thiswebport)) if err != nil { - dmsgWebLog.Fatalf("Failed to start TCP listener on port %v: %v", thiswebport, err) + dmsgWebLog.WithError(err).Fatal(fmt.Sprintf("Failed to start TCP listener on port: %v", thiswebport)) } defer listener.Close() //nolint - log.Printf("Serving TCP on 127.0.0.1:%v", thiswebport) + dmsgWebLog.Debug("Serving TCP on 127.0.0.1:", thiswebport) + if dmsgC == nil { + dmsgWebLog.Fatal("dmsgC is nil") + } for { conn, err := listener.Accept() if err != nil { - log.Printf("Failed to accept connection: %v", err) + dmsgWebLog.WithError(err).Warn("Failed to accept connection") continue } @@ -413,9 +416,12 @@ func proxyTCPConn(n int) { if !ok { dmsgWebLog.Fatal("uint16 overflow when converting dmsg port") } + // dialPK[n].Set("020679271a434fd4a362000ccba80ce583df58b5a16cba091004f657406443e773") + // dp = uint16(8000) + // dmsgConn, err := dmsgC.DialStream(context.Background(), dmsg.Addr{PK: dialPK[n], Port: dp}) //nolint dmsgConn, err := dmsgC.DialStream(context.Background(), dmsg.Addr{PK: dialPK[n], Port: dp}) //nolint if err != nil { - log.Printf("Failed to dial dmsg address %v:%v %v", dialPK[n].String(), dmsgPorts[n], err) + dmsgWebLog.WithError(err).Warn(fmt.Sprintf("Failed to dial dmsg address %v:%v", dialPK[n].String(), dmsgPorts[n])) return } defer dmsgConn.Close() //nolint @@ -423,7 +429,7 @@ func proxyTCPConn(n int) { go func() { _, err := io.Copy(dmsgConn, conn) if err != nil { - log.Printf("Error copying data to dmsg client: %v", err) + dmsgWebLog.WithError(err).Warn("Error on io.Copy(dmsgConn, conn)") } dmsgConn.Close() //nolint }() @@ -431,7 +437,7 @@ func proxyTCPConn(n int) { go func() { _, err := io.Copy(conn, dmsgConn) if err != nil { - log.Printf("Error copying data from dmsg client: %v", err) + dmsgWebLog.WithError(err).Warn("Error on io.Copy(conn, dmsgConn)") } conn.Close() //nolint }() From d9fbe8b003781256a65f68469ce214dec8e6169c Mon Sep 17 00:00:00 2001 From: Moses Narrow Date: Thu, 23 Jan 2025 14:30:27 -0600 Subject: [PATCH 09/21] update examples --- cmd/dmsgweb/commands/dmsgweb.go | 3 ++- examples/tcp-proxy-dmsg/tcp-proxy-dmsg.go | 9 ++++----- .../tcp-reverse-proxy-dmsg.go | 14 ++++---------- 3 files changed, 10 insertions(+), 16 deletions(-) diff --git a/cmd/dmsgweb/commands/dmsgweb.go b/cmd/dmsgweb/commands/dmsgweb.go index 04de4df6..46f49639 100644 --- a/cmd/dmsgweb/commands/dmsgweb.go +++ b/cmd/dmsgweb/commands/dmsgweb.go @@ -419,9 +419,10 @@ func proxyTCPConn(n int, dmsgC *dmsg.Client) { // dialPK[n].Set("020679271a434fd4a362000ccba80ce583df58b5a16cba091004f657406443e773") // dp = uint16(8000) // dmsgConn, err := dmsgC.DialStream(context.Background(), dmsg.Addr{PK: dialPK[n], Port: dp}) //nolint + dmsgWebLog.Debug(fmt.Sprintf("Dialing dmsg address: %v ; port: %v", dialPK[n].String(), dmsgPorts[n])) dmsgConn, err := dmsgC.DialStream(context.Background(), dmsg.Addr{PK: dialPK[n], Port: dp}) //nolint if err != nil { - dmsgWebLog.WithError(err).Warn(fmt.Sprintf("Failed to dial dmsg address %v:%v", dialPK[n].String(), dmsgPorts[n])) + dmsgWebLog.WithError(err).Warn(fmt.Sprintf("Failed to dial dmsg address %v port %v", dialPK[n].String(), dmsgPorts[n])) return } defer dmsgConn.Close() //nolint diff --git a/examples/tcp-proxy-dmsg/tcp-proxy-dmsg.go b/examples/tcp-proxy-dmsg/tcp-proxy-dmsg.go index 0b04782a..d9553635 100644 --- a/examples/tcp-proxy-dmsg/tcp-proxy-dmsg.go +++ b/examples/tcp-proxy-dmsg/tcp-proxy-dmsg.go @@ -10,10 +10,9 @@ import ( "sync" cc "github.com/ivanpirog/coloredcobra" - "github.com/skycoin/skywire-utilities/pkg/cipher" - "github.com/skycoin/skywire-utilities/pkg/cmdutil" - "github.com/skycoin/skywire-utilities/pkg/logging" - "github.com/skycoin/skywire-utilities/pkg/skyenv" + "github.com/skycoin/skywire/pkg/skywire-utilities/pkg/cipher" + "github.com/skycoin/skywire/pkg/skywire-utilities/pkg/cmdutil" + "github.com/skycoin/skywire/pkg/skywire-utilities/pkg/logging" "github.com/spf13/cobra" "github.com/skycoin/dmsg/pkg/disc" @@ -79,7 +78,7 @@ var ( func init() { srvCmd.Flags().UintVarP(&localPort, "lport", "l", 8086, "local application http interface port(s)") srvCmd.Flags().UintVarP(&dmsgPort, "dport", "d", 8086, "dmsg port(s) to serve") - srvCmd.Flags().StringVarP(&dmsgDisc, "dmsg-disc", "D", skyenv.DmsgDiscAddr, "dmsg discovery url") + srvCmd.Flags().StringVarP(&dmsgDisc, "dmsg-disc", "D", dmsg.DiscAddr(false), "dmsg discovery url") srvCmd.Flags().IntVarP(&dmsgSess, "dsess", "e", 1, "dmsg sessions") srvCmd.Flags().VarP(&sk, "sk", "s", "a random key is generated if unspecified\n\r") diff --git a/examples/tcp-reverse-proxy-dmsg/tcp-reverse-proxy-dmsg.go b/examples/tcp-reverse-proxy-dmsg/tcp-reverse-proxy-dmsg.go index 155829a0..515b2cf9 100644 --- a/examples/tcp-reverse-proxy-dmsg/tcp-reverse-proxy-dmsg.go +++ b/examples/tcp-reverse-proxy-dmsg/tcp-reverse-proxy-dmsg.go @@ -16,11 +16,9 @@ import ( "syscall" cc "github.com/ivanpirog/coloredcobra" - "github.com/skycoin/skywire-utilities/pkg/buildinfo" - "github.com/skycoin/skywire-utilities/pkg/cipher" - "github.com/skycoin/skywire-utilities/pkg/cmdutil" - "github.com/skycoin/skywire-utilities/pkg/logging" - "github.com/skycoin/skywire-utilities/pkg/skyenv" + "github.com/skycoin/skywire/pkg/skywire-utilities/pkg/cipher" + "github.com/skycoin/skywire/pkg/skywire-utilities/pkg/cmdutil" + "github.com/skycoin/skywire/pkg/skywire-utilities/pkg/logging" "github.com/spf13/cobra" "github.com/skycoin/dmsg/pkg/disc" @@ -76,7 +74,7 @@ var ( func init() { RootCmd.Flags().UintVarP(&webPort, "port", "p", 8080, "port to serve the web application") RootCmd.Flags().StringVarP(&resolveDmsgAddr, "resolve", "t", "", "resolve the specified dmsg address:port on the local port & disable proxy") - RootCmd.Flags().StringVarP(&dmsgDisc, "dmsg-disc", "d", skyenv.DmsgDiscAddr, "dmsg discovery url") + RootCmd.Flags().StringVarP(&dmsgDisc, "dmsg-disc", "d", dmsg.DiscAddr(false), "dmsg discovery url") RootCmd.Flags().IntVarP(&dmsgSessions, "sess", "e", 1, "number of dmsg servers to connect to") RootCmd.Flags().StringVarP(&logLvl, "loglvl", "l", "", "[ debug | warn | error | fatal | panic | trace | info ]\033[0m") RootCmd.Flags().VarP(&sk, "sk", "s", "a random key is generated if unspecified\n\r") @@ -93,7 +91,6 @@ var RootCmd = &cobra.Command{ SilenceUsage: true, DisableSuggestions: true, DisableFlagsInUseLine: true, - Version: buildinfo.Version(), Run: func(cmd *cobra.Command, _ []string) { c := make(chan os.Signal, 1) @@ -111,9 +108,6 @@ var RootCmd = &cobra.Command{ } } - if dmsgDisc == "" { - dmsgDisc = skyenv.DmsgDiscAddr - } ctx, cancel := cmdutil.SignalContext(context.Background(), dmsgWebLog) defer cancel() From 1d04d3ec6d8fcd52ffd491df1a3abc97abb482b3 Mon Sep 17 00:00:00 2001 From: Moses Narrow Date: Thu, 23 Jan 2025 18:47:12 -0600 Subject: [PATCH 10/21] update examples --- examples/tcp-proxy-dmsg/tcp-proxy-dmsg.go | 37 ++++++++++++++++++----- examples/tcp-proxy/tcp-proxy.go | 10 ++---- examples/tcp/tcp.go | 12 ++++---- 3 files changed, 38 insertions(+), 21 deletions(-) diff --git a/examples/tcp-proxy-dmsg/tcp-proxy-dmsg.go b/examples/tcp-proxy-dmsg/tcp-proxy-dmsg.go index d9553635..bfc6b685 100644 --- a/examples/tcp-proxy-dmsg/tcp-proxy-dmsg.go +++ b/examples/tcp-proxy-dmsg/tcp-proxy-dmsg.go @@ -169,20 +169,41 @@ func handleTCPConnection(dmsgConn net.Conn, localPort uint, log *logging.Logger) localConn, err := net.Dial("tcp", fmt.Sprintf("127.0.0.1:%d", localPort)) if err != nil { - log.Printf("Error connecting to local port %d: %v", localPort, err) + log.Printf("Failed to dial server %s: %v", fmt.Sprintf("127.0.0.1:%d", localPort), err) return } defer localConn.Close() //nolint - copyConn := func(dst net.Conn, src net.Conn) { - _, err := io.Copy(dst, src) - if err != nil { - log.Printf("Error during copy: %v", err) + var wg sync.WaitGroup + wg.Add(2) + + go func() { + _, err := io.Copy(dmsgConn, localConn) + if err != nil && !isClosedConnErr(err) { + log.Printf("Error copying from local to dmsg: %v", err) } - } + dmsgConn.Close() + wg.Done() + }() - go copyConn(dmsgConn, localConn) - go copyConn(localConn, dmsgConn) + go func() { + _, err := io.Copy(localConn, dmsgConn) + if err != nil && !isClosedConnErr(err) { + log.Printf("Error copying from dmsg to local: %v", err) + } + localConn.Close() + wg.Done() + }() + + wg.Wait() +} + +func isClosedConnErr(err error) bool { + if err == io.EOF { + return true + } + netErr, ok := err.(net.Error) + return ok && netErr.Timeout() } func startDmsg(ctx context.Context, pk cipher.PubKey, sk cipher.SecKey) (dmsgC *dmsg.Client, stop func(), err error) { diff --git a/examples/tcp-proxy/tcp-proxy.go b/examples/tcp-proxy/tcp-proxy.go index 96271c1a..888dc2c1 100644 --- a/examples/tcp-proxy/tcp-proxy.go +++ b/examples/tcp-proxy/tcp-proxy.go @@ -54,35 +54,31 @@ func handleConnection(conn net.Conn, targetPort int) { var wg sync.WaitGroup wg.Add(2) - // Copy from client to target go func() { _, err := io.Copy(target, conn) if err != nil && !isClosedConnErr(err) { log.Printf("Error copying from client to target: %v", err) } - target.Close() // Close target side after copy + target.Close() wg.Done() }() - // Copy from target to client go func() { _, err := io.Copy(conn, target) if err != nil && !isClosedConnErr(err) { log.Printf("Error copying from target to client: %v", err) } - conn.Close() // Close client side after copy + conn.Close() wg.Done() }() - // Wait for both copies to finish wg.Wait() } -// isClosedConnErr checks if the error indicates a closed connection. func isClosedConnErr(err error) bool { if err == io.EOF { return true } netErr, ok := err.(net.Error) - return ok && netErr.Timeout() // Check for timeout error indicating closed connection + return ok && netErr.Timeout() } diff --git a/examples/tcp/tcp.go b/examples/tcp/tcp.go index 03f4acb6..a2d38f9a 100644 --- a/examples/tcp/tcp.go +++ b/examples/tcp/tcp.go @@ -1,7 +1,7 @@ package main import ( - "fmt" + "log" "net" "os" ) @@ -10,17 +10,17 @@ func main() { // Start a TCP server listening on port 8000 listener, err := net.Listen("tcp", os.Args[1]) //":8000") if err != nil { - fmt.Printf("Failed to start server: %v\n", err) + log.Fatal("Failed to start server:", err) return } defer listener.Close() - fmt.Println("TCP server started on port " + os.Args[1]) + log.Println("TCP server started on port", os.Args[1]) // Accept and handle incoming connections for { conn, err := listener.Accept() if err != nil { - fmt.Printf("Failed to accept connection: %v\n", err) + log.Println("Failed to accept connection:", err) continue } go handleConnection(conn) @@ -29,12 +29,12 @@ func main() { func handleConnection(conn net.Conn) { defer conn.Close() - + log.Println("Handling Connection") // Send a greeting message to the client message := "Hello, World!\n" _, err := conn.Write([]byte(message)) if err != nil { - fmt.Printf("Error writing response: %v\n", err) + log.Println("Error writing response:", err) return } } From caac3196307c13a795b1a69d4f68b1cdedabc8f3 Mon Sep 17 00:00:00 2001 From: Moses Narrow Date: Thu, 23 Jan 2025 19:24:52 -0600 Subject: [PATCH 11/21] update examples --- examples/tcp-proxy-dmsg/tcp-proxy-dmsg.go | 43 +++++++++++++++---- .../tcp-reverse-proxy-dmsg.go | 35 ++++++++++++--- 2 files changed, 62 insertions(+), 16 deletions(-) diff --git a/examples/tcp-proxy-dmsg/tcp-proxy-dmsg.go b/examples/tcp-proxy-dmsg/tcp-proxy-dmsg.go index bfc6b685..e707329a 100644 --- a/examples/tcp-proxy-dmsg/tcp-proxy-dmsg.go +++ b/examples/tcp-proxy-dmsg/tcp-proxy-dmsg.go @@ -165,45 +165,70 @@ func proxyTCPConnections(localPort uint, lis net.Listener, log *logging.Logger) } func handleTCPConnection(dmsgConn net.Conn, localPort uint, log *logging.Logger) { - defer dmsgConn.Close() //nolint + defer dmsgConn.Close() // Ensure the dmsg connection is closed. localConn, err := net.Dial("tcp", fmt.Sprintf("127.0.0.1:%d", localPort)) if err != nil { log.Printf("Failed to dial server %s: %v", fmt.Sprintf("127.0.0.1:%d", localPort), err) return } - defer localConn.Close() //nolint + defer localConn.Close() // Ensure the local connection is closed. var wg sync.WaitGroup wg.Add(2) + // Log data copied from localConn to dmsgConn go func() { - _, err := io.Copy(dmsgConn, localConn) + defer wg.Done() + reader := io.TeeReader(localConn, logWriter("local -> dmsg", log)) + _, err := io.Copy(dmsgConn, reader) if err != nil && !isClosedConnErr(err) { log.Printf("Error copying from local to dmsg: %v", err) } - dmsgConn.Close() - wg.Done() }() + // Log data copied from dmsgConn to localConn go func() { - _, err := io.Copy(localConn, dmsgConn) + defer wg.Done() + reader := io.TeeReader(dmsgConn, logWriter("dmsg -> local", log)) + _, err := io.Copy(localConn, reader) if err != nil && !isClosedConnErr(err) { log.Printf("Error copying from dmsg to local: %v", err) } - localConn.Close() - wg.Done() }() wg.Wait() + dmsgConn.Close() + localConn.Close() + log.Printf("Closed connection between DMSG and local port %d", localPort) +} + +// logWriter creates a writer that logs the copied data with a prefix. +func logWriter(direction string, log *logging.Logger) io.Writer { + return &logWriterImpl{ + prefix: direction, + log: log, + } +} + +// logWriterImpl is an implementation of io.Writer that logs data as it is written. +type logWriterImpl struct { + prefix string + log *logging.Logger +} + +func (lw *logWriterImpl) Write(p []byte) (int, error) { + lw.log.Printf("[%s] %s", lw.prefix, string(p)) // Log the data as a string. + return len(p), nil } +// isClosedConnErr checks if the error indicates a closed connection. func isClosedConnErr(err error) bool { if err == io.EOF { return true } netErr, ok := err.(net.Error) - return ok && netErr.Timeout() + return ok && netErr.Timeout() // Check for timeout error indicating closed connection } func startDmsg(ctx context.Context, pk cipher.PubKey, sk cipher.SecKey) (dmsgC *dmsg.Client, stop func(), err error) { diff --git a/examples/tcp-reverse-proxy-dmsg/tcp-reverse-proxy-dmsg.go b/examples/tcp-reverse-proxy-dmsg/tcp-reverse-proxy-dmsg.go index 515b2cf9..c5f5a9da 100644 --- a/examples/tcp-reverse-proxy-dmsg/tcp-reverse-proxy-dmsg.go +++ b/examples/tcp-reverse-proxy-dmsg/tcp-reverse-proxy-dmsg.go @@ -171,34 +171,55 @@ func proxyTCPConn() { wg.Add(1) go func(conn net.Conn) { defer wg.Done() - defer conn.Close() //nolint dmsgConn, err := dmsgC.DialStream(context.Background(), dmsg.Addr{PK: dialPK, Port: uint16(dmsgPort)}) if err != nil { log.Printf("Failed to dial dmsg address %v:%v %v", dialPK.String(), dmsgPort, err) return } - defer dmsgConn.Close() //nolint - + // Log data copied to the dmsg connection (from the client connection) go func() { - _, err := io.Copy(dmsgConn, conn) + defer dmsgConn.Close() + reader := io.TeeReader(conn, logWriter("client -> dmsg", dmsgWebLog)) + _, err := io.Copy(dmsgConn, reader) if err != nil { log.Printf("Error copying data to dmsg client: %v", err) } - dmsgConn.Close() //nolint }() + // Log data copied from the dmsg connection (to the client connection) go func() { - _, err := io.Copy(conn, dmsgConn) + defer conn.Close() //nolint + reader := io.TeeReader(dmsgConn, logWriter("dmsg -> client", dmsgWebLog)) + _, err := io.Copy(conn, reader) if err != nil { log.Printf("Error copying data from dmsg client: %v", err) } - conn.Close() //nolint }() }(conn) + wg.Wait() + } +} + +// logWriter creates a writer that logs the copied data with a prefix. +func logWriter(direction string, log *logging.Logger) io.Writer { + return &logWriterImpl{ + prefix: direction, + log: log, } } +// logWriterImpl is an implementation of io.Writer that logs data as it is written. +type logWriterImpl struct { + prefix string + log *logging.Logger +} + +func (lw *logWriterImpl) Write(p []byte) (int, error) { + lw.log.Printf("[%s] %s", lw.prefix, string(p)) // Log the data as a string. + return len(p), nil +} + func startDmsg(ctx context.Context, pk cipher.PubKey, sk cipher.SecKey) (dmsgC *dmsg.Client, stop func(), err error) { dmsgC = dmsg.NewClient(pk, sk, disc.NewHTTP(dmsgDisc, &http.Client{}, dmsgWebLog), &dmsg.Config{MinSessions: dmsgSessions}) go dmsgC.Serve(context.Background()) From 3b202dc2ee9c769234a50f88bcdf5de9fff57959 Mon Sep 17 00:00:00 2001 From: Mohammed Date: Sun, 26 Jan 2025 13:17:27 +0000 Subject: [PATCH 12/21] remove useless inputs --- cmd/dmsgweb/commands/dmsgweb.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/cmd/dmsgweb/commands/dmsgweb.go b/cmd/dmsgweb/commands/dmsgweb.go index 46f49639..cda765ad 100644 --- a/cmd/dmsgweb/commands/dmsgweb.go +++ b/cmd/dmsgweb/commands/dmsgweb.go @@ -288,19 +288,19 @@ dmsgweb conf file detected: ` + dmsgwebconffile if len(resolveDmsgAddr) == 0 && len(webPort) == 1 { if rawTCP[0] { dmsgWebLog.Debug("proxyTCPConn(-1)") - proxyTCPConn(-1, dmsgC) + proxyTCPConn(-1) } else { dmsgWebLog.Debug("proxyHTTPConn(-1)") - proxyHTTPConn(-1, dmsgC) + proxyHTTPConn(-1) } } else { for i := range resolveDmsgAddr { if rawTCP[i] { dmsgWebLog.Debug("proxyTCPConn(" + fmt.Sprintf("%v", i) + ")") - proxyTCPConn(i, dmsgC) + proxyTCPConn(i) } else { dmsgWebLog.Debug("proxyHTTPConn(" + fmt.Sprintf("%v", i) + ")") - proxyHTTPConn(i, dmsgC) + proxyHTTPConn(i) } } } @@ -308,7 +308,7 @@ dmsgweb conf file detected: ` + dmsgwebconffile }, } -func proxyHTTPConn(n int, dmsgC *dmsg.Client) { +func proxyHTTPConn(n int) { r := gin.New() r.Use(gin.Recovery()) @@ -384,7 +384,7 @@ func proxyHTTPConn(n int, dmsgC *dmsg.Client) { wg.Done() }() } -func proxyTCPConn(n int, dmsgC *dmsg.Client) { +func proxyTCPConn(n int) { var thiswebport uint if n == -1 { thiswebport = webPort[0] From d4fa599775d513dd3515230fca3ff672a7829bf7 Mon Sep 17 00:00:00 2001 From: Moses Narrow Date: Mon, 27 Jan 2025 13:16:02 -0600 Subject: [PATCH 13/21] update examples and revisions to dmsgweb --- cmd/dmsgweb/commands/dmsgweb.go | 14 +- cmd/dmsgweb/commands/dmsgwebsrv.go | 222 ++++++------------ examples/dmsgweb/commands/dmsgweb.go | 201 ++++++++++++++++ examples/dmsgweb/commands/root.go | 179 ++++++++++++++ examples/dmsgweb/dmsgweb.go | 44 ++++ examples/http/http.go | 33 +++ .../tcp-proxy-dmsg/tcp-multi-proxy-dmsg.go | 174 ++++++++++++++ examples/tcp-proxy/tcp-proxy.go | 2 +- .../tcp-reverse-proxy-dmsg.go | 4 + 9 files changed, 715 insertions(+), 158 deletions(-) create mode 100644 examples/dmsgweb/commands/dmsgweb.go create mode 100644 examples/dmsgweb/commands/root.go create mode 100644 examples/dmsgweb/dmsgweb.go create mode 100644 examples/http/http.go create mode 100644 examples/tcp-proxy-dmsg/tcp-multi-proxy-dmsg.go diff --git a/cmd/dmsgweb/commands/dmsgweb.go b/cmd/dmsgweb/commands/dmsgweb.go index 46f49639..e4b3ffca 100644 --- a/cmd/dmsgweb/commands/dmsgweb.go +++ b/cmd/dmsgweb/commands/dmsgweb.go @@ -61,7 +61,7 @@ func init() { RootCmd.Flags().StringVarP(&addProxy, "addproxy", "r", scriptExecString("${ADDPROXY}", dmsgwebconffile), "configure additional socks5 proxy for dmsgweb (i.e. 127.0.0.1:1080)") RootCmd.Flags().UintSliceVarP(&webPort, "port", "p", scriptExecUintSlice("${WEBPORT[@]:-8080}", dmsgwebconffile), "port(s) to serve the web application") RootCmd.Flags().StringSliceVarP(&resolveDmsgAddr, "resolve", "t", scriptExecStringSlice("${RESOLVEPK[@]}", dmsgwebconffile), "resolve the specified dmsg address:port on the local port & disable proxy") - RootCmd.Flags().StringVarP(&dmsgDisc, "dmsg-disc", "D", dmsgDisc, "dmsg discovery url(s)") + RootCmd.Flags().StringVarP(&dmsgDisc, "dmsg-disc", "D", dmsgDisc, "dmsg discovery url") RootCmd.Flags().StringVarP(&proxyAddr, "proxy", "x", "", "connect to dmsg via proxy (i.e. '127.0.0.1:1080')") RootCmd.Flags().IntVarP(&dmsgSessions, "sess", "e", scriptExecInt("${DMSGSESSIONS:-1}", dmsgwebconffile), "number of dmsg servers to connect to") RootCmd.Flags().BoolSliceVarP(&rawTCP, "rt", "c", scriptExecBoolSlice("${RAWTCP[@]:-false}", dmsgwebconffile), "proxy local port as raw TCP") @@ -411,15 +411,12 @@ func proxyTCPConn(n int, dmsgC *dmsg.Client) { wg.Add(1) go func(conn net.Conn, n int, dmsgC *dmsg.Client) { defer wg.Done() - defer conn.Close() //nolint + dp, ok := safecast.To[uint16](dmsgPorts[n]) if !ok { dmsgWebLog.Fatal("uint16 overflow when converting dmsg port") } - // dialPK[n].Set("020679271a434fd4a362000ccba80ce583df58b5a16cba091004f657406443e773") - // dp = uint16(8000) - // dmsgConn, err := dmsgC.DialStream(context.Background(), dmsg.Addr{PK: dialPK[n], Port: dp}) //nolint - dmsgWebLog.Debug(fmt.Sprintf("Dialing dmsg address: %v ; port: %v", dialPK[n].String(), dmsgPorts[n])) + dmsgWebLog.Debug(fmt.Sprintf("Dialing dmsg address: %v ; port: %v", dialPK[n].String(), dp)) dmsgConn, err := dmsgC.DialStream(context.Background(), dmsg.Addr{PK: dialPK[n], Port: dp}) //nolint if err != nil { dmsgWebLog.WithError(err).Warn(fmt.Sprintf("Failed to dial dmsg address %v port %v", dialPK[n].String(), dmsgPorts[n])) @@ -428,21 +425,22 @@ func proxyTCPConn(n int, dmsgC *dmsg.Client) { defer dmsgConn.Close() //nolint go func() { + defer dmsgConn.Close() _, err := io.Copy(dmsgConn, conn) if err != nil { dmsgWebLog.WithError(err).Warn("Error on io.Copy(dmsgConn, conn)") } - dmsgConn.Close() //nolint }() go func() { + defer conn.Close() //nolint _, err := io.Copy(conn, dmsgConn) if err != nil { dmsgWebLog.WithError(err).Warn("Error on io.Copy(conn, dmsgConn)") } - conn.Close() //nolint }() }(conn, n, dmsgC) + wg.Wait() } } diff --git a/cmd/dmsgweb/commands/dmsgwebsrv.go b/cmd/dmsgweb/commands/dmsgwebsrv.go index 314716c1..bed92748 100644 --- a/cmd/dmsgweb/commands/dmsgwebsrv.go +++ b/cmd/dmsgweb/commands/dmsgwebsrv.go @@ -13,10 +13,8 @@ import ( "runtime" "strings" "sync" - "time" "github.com/bitfield/script" - "github.com/chen3feng/safecast" "github.com/gin-gonic/gin" "github.com/skycoin/skywire/pkg/skywire-utilities/pkg/cipher" "github.com/skycoin/skywire/pkg/skywire-utilities/pkg/cmdutil" @@ -30,49 +28,45 @@ import ( const dmsgwebsrvenvname = "DMSGWEBSRV" -var dmsgwebsrvconffile = os.Getenv(dmsgwebsrvenvname) - func init() { RootCmd.AddCommand(srvCmd) - srvCmd.Flags().UintSliceVarP(&localPort, "lport", "l", scriptExecUintSlice("${LOCALPORT[@]:-8086}", dmsgwebsrvconffile), "local application http interface port(s)") - srvCmd.Flags().UintSliceVarP(&dmsgPort, "dport", "d", scriptExecUintSlice("${DMSGPORT[@]:-80}", dmsgwebsrvconffile), "dmsg port(s) to serve") - srvCmd.Flags().StringSliceVarP(&wl, "wl", "w", scriptExecStringSlice("${WHITELISTPKS[@]}", dmsgwebsrvconffile), "whitelisted keys for dmsg authenticated routes\r") - srvCmd.Flags().StringVarP(&dmsgDisc, "dmsg-disc", "D", dmsgDisc, "dmsg discovery url(s)") - srvCmd.Flags().StringVarP(&proxyAddr, "proxy", "x", proxyAddr, "connect to dmsg via proxy (i.e. '127.0.0.1:1080')") - srvCmd.Flags().IntVarP(&dmsgSess, "dsess", "e", scriptExecInt("${DMSGSESSIONS:-1}", dmsgwebsrvconffile), "dmsg sessions") - srvCmd.Flags().BoolSliceVarP(&rawTCP, "rt", "c", scriptExecBoolSlice("${RAWTCP[@]:-false}", dmsgwebsrvconffile), "proxy local port as raw TCP") + srvCmd.Flags().UintSliceVarP(&localPort, "lport", "l", scriptExecUintSlice("${LOCALPORT[@]:-8086}", srvenvfileLinux), "local application HTTP interface port(s)") + srvCmd.Flags().UintSliceVarP(&dmsgPort, "dport", "d", scriptExecUintSlice("${DMSGPORT[@]:-80}", srvenvfileLinux), "DMSG port(s) to serve") + srvCmd.Flags().StringSliceVarP(&wl, "wl", "w", scriptExecStringSlice("${WHITELISTPKS[@]}", srvenvfileLinux), "whitelisted keys for DMSG authenticated routes") + srvCmd.Flags().StringVarP(&dmsgDisc, "dmsg-disc", "D", dmsgDisc, "DMSG discovery URL(s)") + srvCmd.Flags().StringVarP(&proxyAddr, "proxy", "x", proxyAddr, "connect to DMSG via proxy (e.g., '127.0.0.1:1080')") + srvCmd.Flags().IntVarP(&dmsgSess, "dsess", "e", scriptExecInt("${DMSGSESSIONS:-1}", srvenvfileLinux), "DMSG sessions") + srvCmd.Flags().BoolSliceVarP(&rawTCP, "rt", "c", scriptExecBoolSlice("${RAWTCP[@]:-false}", srvenvfileLinux), "proxy local port as raw TCP") + srvCmd.Flags().BoolVarP(&isEnvs, "envs", "z", false, "show example .conf file") + if os.Getenv("DMSGWEBSRVSK") != "" { - sk.Set(os.Getenv("DMSGWEBSRVSK")) //nolint + sk.Set(os.Getenv("DMSGWEBSRVSK")) } - if scriptExecString("${DMSGWEBSRVSK}", dmsgwebsrvconffile) != "" { - sk.Set(scriptExecString("${DMSGWEBSRVSK}", dmsgwebsrvconffile)) //nolint + if scriptExecString("${DMSGWEBSRVSK}", srvenvfileLinux) != "" { + sk.Set(scriptExecString("${DMSGWEBSRVSK}", srvenvfileLinux)) } - pk, _ = sk.PubKey() //nolint - srvCmd.Flags().VarP(&sk, "sk", "s", "a random key is generated if unspecified\n\r") - srvCmd.Flags().BoolVarP(&isEnvs, "envs", "z", false, "show example .conf file") + pk, _ = sk.PubKey() + srvCmd.Flags().VarP(&sk, "sk", "s", "a random key is generated if unspecified") srvCmd.CompletionOptions.DisableDefaultCmd = true } var srvCmd = &cobra.Command{ Use: "srv", - Short: "serve http or raw TCP from local port over dmsg", - Long: `DMSG web server - serve http or raw TCP interface from local port over dmsg` + func() string { - if _, err := os.Stat(dmsgwebsrvconffile); err == nil { - return ` - dmsenv file detected: ` + dmsgwebsrvconffile + Short: "Serve HTTP or raw TCP from local port over DMSG", + Long: `DMSG web server - serve HTTP or raw TCP interface from local port over DMSG` + func() string { + if _, err := os.Stat(srvenvfileLinux); err == nil { + return "\n\t.dmsenv file detected: " + srvenvfileLinux } - return ` - .conf file may also be specified with - ` + dmsgwebsrvenvname + `=/path/to/dmsgwebsrv.conf skywire dmsg web srv` + return "\n\t.conf file may also be specified with " + dmsgwebsrvenvname + `=/path/to/dmsgwebsrv.conf skywire dmsg web srv` }(), Run: func(_ *cobra.Command, _ []string) { if isEnvs { envfile := srvenvfileLinux if runtime.GOOS == "windows" { - envfileslice, _ := script.Echo(envfile).Slice() //nolint + envfileslice, _ := script.Echo(envfile).Slice() for i := range envfileslice { - efs, _ := script.Echo(envfileslice[i]).Reject("##").Reject("#-").Reject("# ").Replace("#", "#$").String() //nolint + efs, _ := script.Echo(envfileslice[i]).Reject("##").Reject("#-").Reject("# ").Replace("#", "#$").String() if efs != "" && efs != "\n" { envfileslice[i] = strings.ReplaceAll(efs, "\n", "") } @@ -89,189 +83,119 @@ var srvCmd = &cobra.Command{ func server() { log := logging.MustGetLogger("dmsgwebsrv") - if len(localPort) != len(dmsgPort) { - log.Fatal(fmt.Sprintf("the same number of local ports as dmsg ports must be specified ; local ports: %v ; dmsg ports: %v", len(localPort), len(dmsgPort))) - } - seenLocalPort := make(map[uint]bool) - for _, item := range localPort { - if seenLocalPort[item] { - log.Fatal("-lport --l flag cannot contain duplicates") - } - seenLocalPort[item] = true - } - - seenDmsgPort := make(map[uint]bool) - for _, item := range dmsgPort { - if seenDmsgPort[item] { - log.Fatal("-dport --d flag cannot contain duplicates") - } - seenDmsgPort[item] = true + if len(localPort) != len(dmsgPort) || len(localPort) != len(rawTCP) { + log.Fatal("The number of local ports, DMSG ports, and raw TCP flags must be the same") } ctx, cancel := cmdutil.SignalContext(context.Background(), log) - defer cancel() - pk, err = sk.PubKey() + + pk, err := sk.PubKey() if err != nil { pk, sk = cipher.GenerateKeyPair() } - log.Infof("dmsg client pk: %v", pk.String()) + log.Infof("DMSG client public key: %v", pk.String()) if len(wl) > 0 { for _, key := range wl { - var pk0 cipher.PubKey - err := pk0.Set(key) - if err == nil { - wlkeys = append(wlkeys, pk0) + var pk cipher.PubKey + if err := pk.Set(key); err == nil { + wlkeys = append(wlkeys, pk) } } - } - if len(wlkeys) > 0 { - if len(wlkeys) == 1 { - log.Info(fmt.Sprintf("%d key whitelisted", len(wlkeys))) - } else { - log.Info(fmt.Sprintf("%d keys whitelisted", len(wlkeys))) - } + log.Infof("%d keys whitelisted", len(wlkeys)) } if proxyAddr != "" { - // Use SOCKS5 proxy dialer if specified + var err error dialer, err = proxy.SOCKS5("tcp", proxyAddr, nil, proxy.Direct) if err != nil { log.Fatalf("Error creating SOCKS5 dialer: %v", err) } - transport := &http.Transport{ - Dial: dialer.Dial, - } - httpClient = &http.Client{ - Transport: transport, - } - ctx = context.WithValue(context.Background(), "socks5_proxy", proxyAddr) //nolint + httpClient = &http.Client{Transport: &http.Transport{Dial: dialer.Dial}} } - dmsgC := dmsg.NewClient(pk, sk, disc.NewHTTP(dmsgDisc, &http.Client{}, log), dmsg.DefaultConfig()) + dmsgClient := dmsg.NewClient(pk, sk, disc.NewHTTP(dmsgDisc, &http.Client{}, log), dmsg.DefaultConfig()) defer func() { - if err := dmsgC.Close(); err != nil { + if err := dmsgClient.Close(); err != nil { log.WithError(err).Error() } }() - - go dmsgC.Serve(context.Background()) + go dmsgClient.Serve(context.Background()) select { case <-ctx.Done(): log.WithError(ctx.Err()).Warn() return - - case <-dmsgC.Ready(): + case <-dmsgClient.Ready(): } - var listN []net.Listener - - for _, dport := range dmsgPort { - dp, ok := safecast.To[uint16](dport) - if !ok { - log.Fatal("uint16 overflow when converting dmsg port") - } - lis, err := dmsgC.Listen(dp) + wg := sync.WaitGroup{} + for i := range localPort { + lis, err := dmsgClient.Listen(uint16(dmsgPort[i])) if err != nil { - log.Fatalf("Error listening on port %d: %v", dport, err) + log.Fatalf("Error listening on DMSG port %d: %v", dmsgPort[i], err) } - - listN = append(listN, lis) - - dport := dp - go func(l net.Listener, port uint16) { - <-ctx.Done() - if err := l.Close(); err != nil { - log.Printf("Error closing listener on port %d: %v", port, err) - log.WithError(err).Error() - } - }(lis, dport) - } - - wg := new(sync.WaitGroup) - - for i, lpt := range localPort { wg.Add(1) - go func(localPort uint, rtcp bool, lis net.Listener) { + go func(localPort uint, rawTCP bool, listener net.Listener) { defer wg.Done() - if rtcp { - proxyTCPConnections(localPort, lis, log) + if rawTCP { + proxyTCPConnections(localPort, listener, log) } else { - proxyHTTPConnections(localPort, lis, log) + proxyHTTPConnections(localPort, listener, log) } - }(lpt, rawTCP[i], listN[i]) + }(localPort[i], rawTCP[i], lis) } - wg.Wait() } -func proxyHTTPConnections(localPort uint, lis net.Listener, log *logging.Logger) { - r1 := gin.New() - r1.Use(gin.Recovery()) - r1.Use(loggingMiddleware()) +func proxyHTTPConnections(localPort uint, listener net.Listener, log *logging.Logger) { + router := gin.New() + router.Use(gin.Recovery()) + router.Use(loggingMiddleware()) - authRoute := r1.Group("/") + authRoute := router.Group("/") if len(wlkeys) > 0 { authRoute.Use(whitelistAuth(wlkeys)) } authRoute.Any("/*path", func(c *gin.Context) { - targetURL, _ := url.Parse(fmt.Sprintf("http://127.0.0.1:%v%s?%s", localPort, c.Request.URL.Path, c.Request.URL.RawQuery)) //nolint - proxy := httputil.ReverseProxy{ - Director: func(req *http.Request) { - req.URL = targetURL - req.Host = targetURL.Host - req.Method = c.Request.Method - }, - Transport: &http.Transport{}, - } + targetURL := fmt.Sprintf("http://127.0.0.1:%d%s?%s", localPort, c.Request.URL.Path, c.Request.URL.RawQuery) + proxy := httputil.ReverseProxy{Director: func(req *http.Request) { + req.URL, _ = url.Parse(targetURL) + req.Host = req.URL.Host + }} proxy.ServeHTTP(c.Writer, c.Request) }) - serve := &http.Server{ - Handler: &ginHandler{Router: r1}, - ReadHeaderTimeout: 5 * time.Second, - ReadTimeout: 10 * time.Second, - WriteTimeout: 10 * time.Second, - } - log.Printf("Serving HTTP on dmsg port %v with DMSG listener %s", localPort, lis.Addr().String()) - if err := serve.Serve(lis); err != nil && err != http.ErrServerClosed { - log.Fatalf("Serve: %v", err) + + server := &http.Server{Handler: router} + if err := server.Serve(listener); err != nil && err != http.ErrServerClosed { + log.Fatalf("HTTP server error: %v", err) } } -func proxyTCPConnections(localPort uint, lis net.Listener, log *logging.Logger) { +func proxyTCPConnections(localPort uint, listener net.Listener, log *logging.Logger) { for { - conn, err := lis.Accept() + conn, err := listener.Accept() if err != nil { - log.Printf("Error accepting connection: %v", err) + log.Errorf("Error accepting connection: %v", err) return } - go handleTCPConnection(conn, localPort, log) - } -} - -func handleTCPConnection(dmsgConn net.Conn, localPort uint, log *logging.Logger) { - defer dmsgConn.Close() //nolint + go func(dmsgConn net.Conn) { + defer dmsgConn.Close() - localConn, err := net.Dial("tcp", fmt.Sprintf("127.0.0.1:%d", localPort)) - if err != nil { - log.Printf("Error connecting to local port %d: %v", localPort, err) - return - } - defer localConn.Close() //nolint + localConn, err := net.Dial("tcp", fmt.Sprintf("127.0.0.1:%d", localPort)) + if err != nil { + log.Errorf("Error connecting to local port %d: %v", localPort, err) + return + } + defer localConn.Close() - copyConn := func(dst net.Conn, src net.Conn) { - _, err := io.Copy(dst, src) - if err != nil { - log.Printf("Error during copy: %v", err) - } + go io.Copy(dmsgConn, localConn) + io.Copy(localConn, dmsgConn) + }(conn) } - - go copyConn(dmsgConn, localConn) - go copyConn(localConn, dmsgConn) } const srvenvfileLinux = ` diff --git a/examples/dmsgweb/commands/dmsgweb.go b/examples/dmsgweb/commands/dmsgweb.go new file mode 100644 index 00000000..ef6b325c --- /dev/null +++ b/examples/dmsgweb/commands/dmsgweb.go @@ -0,0 +1,201 @@ +// Package commands cmd/dmsgweb/commands/dmsgweb.go +package commands + +import ( + "context" + "fmt" + "io" + "log" + "net" + "net/http" + "os" + "os/signal" + "regexp" + "strings" + "syscall" + + "github.com/confiant-inc/go-socks5" + "github.com/gin-gonic/gin" + "github.com/skycoin/skywire/pkg/skywire-utilities/pkg/buildinfo" + "github.com/skycoin/skywire/pkg/skywire-utilities/pkg/cipher" + "github.com/skycoin/skywire/pkg/skywire-utilities/pkg/cmdutil" + "github.com/skycoin/skywire/pkg/skywire-utilities/pkg/logging" + "github.com/spf13/cobra" + "golang.org/x/net/proxy" + + "github.com/skycoin/dmsg/pkg/dmsghttp" +) + + +func init() { + RootCmd.AddCommand(genKeysCmd) + RootCmd.Flags().StringVarP(&filterDomainSuffix, "filter", "f", ".dmsg", "domain suffix to filter") + RootCmd.Flags().StringVarP(&proxyPort, "socks", "q", "4445", "port to serve the socks5 proxy") + RootCmd.Flags().StringVarP(&addProxy, "proxy", "r", "", "configure additional socks5 proxy for dmsgweb (i.e. 127.0.0.1:1080)") + RootCmd.Flags().StringVarP(&webPort, "port", "p", "8080", "port to serve the web application") + RootCmd.Flags().StringVarP(&resolveDmsgAddr, "resolve", "t", "", "resolve the specified dmsg address:port on the local port & disable proxy") + RootCmd.Flags().StringVarP(&dmsgDisc, "dmsg-disc", "D", dmsgDisc, "dmsg discovery url") + RootCmd.Flags().IntVarP(&dmsgSessions, "sess", "e", 1, "number of dmsg servers to connect to") + RootCmd.Flags().StringVarP(&logLvl, "loglvl", "l", "", "[ debug | warn | error | fatal | panic | trace | info ]\033[0m") + if os.Getenv("DMSGGET_SK") != "" { + sk.Set(os.Getenv("DMSGGET_SK")) //nolint + } + RootCmd.Flags().VarP(&sk, "sk", "s", "a random key is generated if unspecified\n\r") +} + +// RootCmd contains the root command for dmsgweb +var RootCmd = &cobra.Command{ + Use: "web", + Short: "DMSG resolving proxy & browser client", + Long: ` + ┌┬┐┌┬┐┌─┐┌─┐┬ ┬┌─┐┌┐ + │││││└─┐│ ┬│││├┤ ├┴┐ + ─┴┘┴ ┴└─┘└─┘└┴┘└─┘└─┘ + ` + "DMSG resolving proxy & browser client - access websites over dmsg", + SilenceErrors: true, + SilenceUsage: true, + DisableSuggestions: true, + DisableFlagsInUseLine: true, + Version: buildinfo.Version(), + Run: func(cmd *cobra.Command, _ []string) { + c := make(chan os.Signal, 1) + signal.Notify(c, os.Interrupt, syscall.SIGTERM) //nolint + go func() { + <-c + os.Exit(1) + }() + if dmsgWebLog == nil { + dmsgWebLog = logging.MustGetLogger("dmsgweb") + } + if logLvl != "" { + if lvl, err := logging.LevelFromString(logLvl); err == nil { + logging.SetLevel(lvl) + } + } + + if filterDomainSuffix == "" { + dmsgWebLog.Fatal("domain suffix to filter cannot be an empty string") + } + ctx, cancel := cmdutil.SignalContext(context.Background(), dmsgWebLog) + defer cancel() + + pk, err := sk.PubKey() + if err != nil { + pk, sk = cipher.GenerateKeyPair() + } + + dmsgC, closeDmsg, err := startDmsg(ctx, pk, sk) + if err != nil { + dmsgWebLog.WithError(err).Fatal("failed to start dmsg") + } + defer closeDmsg() + + go func() { + <-ctx.Done() + cancel() + closeDmsg() + os.Exit(0) //this should not be necessary + }() + + httpC = http.Client{Transport: dmsghttp.MakeHTTPTransport(ctx, dmsgC)} + + if resolveDmsgAddr == "" { + // Create a SOCKS5 server with custom name resolution + conf := &socks5.Config{ + Resolver: &customResolver{}, + Dial: func(ctx context.Context, network, addr string) (net.Conn, error) { + host, _, err := net.SplitHostPort(addr) + if err != nil { + return nil, err + } + regexPattern := `\` + filterDomainSuffix + `(:[0-9]+)?$` + match, _ := regexp.MatchString(regexPattern, host) //nolint:errcheck + if match { + port, ok := ctx.Value("port").(string) + if !ok { + port = webPort + } + addr = "localhost:" + port + } else { + if addProxy != "" { + // Fallback to another SOCKS5 proxy + dialer, err := proxy.SOCKS5("tcp", addProxy, nil, proxy.Direct) + if err != nil { + return nil, err + } + return dialer.Dial(network, addr) + } + } + dmsgWebLog.Debug("Dialing address:", addr) + return net.Dial(network, addr) + }, + } + + // Start the SOCKS5 server + socksAddr := "127.0.0.1:" + proxyPort + log.Printf("SOCKS5 proxy server started on %s", socksAddr) + + server, err := socks5.New(conf) + if err != nil { + log.Fatalf("Failed to create SOCKS5 server: %v", err) + } + + wg.Add(1) + go func() { + dmsgWebLog.Debug("Serving SOCKS5 proxy on " + socksAddr) + err := server.ListenAndServe("tcp", socksAddr) + if err != nil { + log.Fatalf("Failed to start SOCKS5 server: %v", err) + } + defer server.Close() + dmsgWebLog.Debug("Stopped serving SOCKS5 proxy on " + socksAddr) + }() + } + r := gin.New() + + r.Use(gin.Recovery()) + + r.Use(loggingMiddleware()) + + r.Any("/*path", func(c *gin.Context) { + var urlStr string + if resolveDmsgAddr != "" { + urlStr = fmt.Sprintf("dmsg://%s%s", resolveDmsgAddr, c.Param("path")) + } else { + + hostParts := strings.Split(c.Request.Host, ":") + var dmsgp string + if len(hostParts) > 1 { + dmsgp = hostParts[1] + } else { + dmsgp = "80" + } + urlStr = fmt.Sprintf("dmsg://%s:%s%s", strings.TrimRight(hostParts[0], filterDomainSuffix), dmsgp, c.Param("path")) + } + + req, err := http.NewRequest(http.MethodGet, urlStr, nil) + if err != nil { + c.String(http.StatusInternalServerError, "Failed to create HTTP request") + return + } + + resp, err := httpC.Do(req) + if err != nil { + c.String(http.StatusInternalServerError, "Failed to connect to HTTP server") + return + } + defer resp.Body.Close() //nolint + + c.Status(http.StatusOK) + io.Copy(c.Writer, resp.Body) //nolint + }) + wg.Add(1) + go func() { + dmsgWebLog.Debug("Serving http on " + webPort) + r.Run(":" + webPort) //nolint + dmsgWebLog.Debug("Stopped serving http on " + webPort) + wg.Done() + }() + wg.Wait() + }, +} diff --git a/examples/dmsgweb/commands/root.go b/examples/dmsgweb/commands/root.go new file mode 100644 index 00000000..2420c088 --- /dev/null +++ b/examples/dmsgweb/commands/root.go @@ -0,0 +1,179 @@ +// Package commands cmd/dmsgweb/commands/dmsgweb.go +package commands + +import ( + "context" + "fmt" + "log" + "net" + "net/http" + "regexp" + "sync" + "time" + "os" + + "github.com/gin-gonic/gin" + "github.com/skycoin/skywire/pkg/skywire-utilities/pkg/cipher" + "github.com/skycoin/skywire/pkg/skywire-utilities/pkg/logging" + "github.com/spf13/cobra" + + "github.com/skycoin/dmsg/pkg/disc" + dmsg "github.com/skycoin/dmsg/pkg/dmsg" +) + +// RootCmd contains commands that interact with the config of local skywire-visor +var genKeysCmd = &cobra.Command{ + Use: "gen-keys", + Short: "generate public / secret keypair", + Run: func(cmd *cobra.Command, args []string) { + pk, sk := cipher.GenerateKeyPair() + fmt.Println(pk) + fmt.Println(sk) + }, +} + +type customResolver struct{} + +func (r *customResolver) Resolve(ctx context.Context, name string) (context.Context, net.IP, error) { + // Handle custom name resolution for .dmsg domains + regexPattern := `\.` + filterDomainSuffix + `(:[0-9]+)?$` + match, _ := regexp.MatchString(regexPattern, name) //nolint:errcheck + if match { + ip := net.ParseIP("127.0.0.1") + if ip == nil { + return ctx, nil, fmt.Errorf("failed to parse IP address") + } + // Modify the context to include the desired port + ctx = context.WithValue(ctx, "port", webPort) //nolint + return ctx, ip, nil + } + // Use default name resolution for other domains + return ctx, nil, nil +} + +var ( + httpC http.Client + dmsgDisc = dmsg.DiscAddr(false) + dmsgSessions int + filterDomainSuffix string + sk cipher.SecKey + dmsgWebLog *logging.Logger + logLvl string + webPort string + proxyPort string + addProxy string + resolveDmsgAddr string + wg sync.WaitGroup +) + +func startDmsg(ctx context.Context, pk cipher.PubKey, sk cipher.SecKey) (dmsgC *dmsg.Client, stop func(), err error) { + dmsgC = dmsg.NewClient(pk, sk, disc.NewHTTP(dmsgDisc, &http.Client{}, dmsgWebLog), &dmsg.Config{MinSessions: dmsgSessions}) + go dmsgC.Serve(context.Background()) + + stop = func() { + err := dmsgC.Close() + dmsgWebLog.WithError(err).Debug("Disconnected from dmsg network.") + fmt.Printf("\n") + } + dmsgWebLog.WithField("public_key", pk.String()).WithField("dmsg_disc", dmsgDisc). + Debug("Connecting to dmsg network...") + + select { + case <-ctx.Done(): + stop() + os.Exit(0) + return nil, nil, ctx.Err() + + case <-dmsgC.Ready(): + dmsgWebLog.Debug("Dmsg network ready.") + return dmsgC, stop, nil + } +} + +func loggingMiddleware() gin.HandlerFunc { + return func(c *gin.Context) { + start := time.Now() + c.Next() + latency := time.Since(start) + if latency > time.Minute { + latency = latency.Truncate(time.Second) + } + statusCode := c.Writer.Status() + method := c.Request.Method + path := c.Request.URL.Path + // Get the background color based on the status code + statusCodeBackgroundColor := getBackgroundColor(statusCode) + // Get the method color + methodColor := getMethodColor(method) + // Print the logging in a custom format which includes the publickeyfrom c.Request.RemoteAddr ex.: + // [DMSGHTTP] 2023/05/18 - 19:43:15 | 200 | 10.80885ms | | 02b5ee5333aa6b7f5fc623b7d5f35f505cb7f974e98a70751cf41962f84c8c4637:49153 | GET /node-info.json + fmt.Printf("[DMSGHTTP] %s |%s %3d %s| %13v | %15s | %72s |%s %-7s %s %s\n", + time.Now().Format("2006/01/02 - 15:04:05"), + statusCodeBackgroundColor, + statusCode, + resetColor(), + latency, + c.ClientIP(), + c.Request.RemoteAddr, + methodColor, + method, + resetColor(), + path, + ) + } +} +func getBackgroundColor(statusCode int) string { + switch { + case statusCode >= http.StatusOK && statusCode < http.StatusMultipleChoices: + return green + case statusCode >= http.StatusMultipleChoices && statusCode < http.StatusBadRequest: + return white + case statusCode >= http.StatusBadRequest && statusCode < http.StatusInternalServerError: + return yellow + default: + return red + } +} + +func getMethodColor(method string) string { + switch method { + case http.MethodGet: + return blue + case http.MethodPost: + return cyan + case http.MethodPut: + return yellow + case http.MethodDelete: + return red + case http.MethodPatch: + return green + case http.MethodHead: + return magenta + case http.MethodOptions: + return white + default: + return reset + } +} + +func resetColor() string { + return reset +} + +const ( + green = "\033[97;42m" + white = "\033[90;47m" + yellow = "\033[90;43m" + red = "\033[97;41m" + blue = "\033[97;44m" + magenta = "\033[97;45m" + cyan = "\033[97;46m" + reset = "\033[0m" +) + +// Execute executes root CLI command. +func Execute() { + if err := RootCmd.Execute(); err != nil { + log.Fatal("Failed to execute command: ", err) + } +} diff --git a/examples/dmsgweb/dmsgweb.go b/examples/dmsgweb/dmsgweb.go new file mode 100644 index 00000000..58198978 --- /dev/null +++ b/examples/dmsgweb/dmsgweb.go @@ -0,0 +1,44 @@ +// Package main cmd/dmsgweb/dmsgweb.go +package main + +import ( + cc "github.com/ivanpirog/coloredcobra" + "github.com/spf13/cobra" + + "github.com/skycoin/dmsg/examples/dmsgweb/commands" +) + +func init() { + var helpflag bool + commands.RootCmd.SetUsageTemplate(help) + commands.RootCmd.PersistentFlags().BoolVarP(&helpflag, "help", "h", false, "help for dmsgweb") + commands.RootCmd.SetHelpCommand(&cobra.Command{Hidden: true}) + commands.RootCmd.PersistentFlags().MarkHidden("help") //nolint +} + +func main() { + cc.Init(&cc.Config{ + RootCmd: commands.RootCmd, + Headings: cc.HiBlue + cc.Bold, + Commands: cc.HiBlue + cc.Bold, + CmdShortDescr: cc.HiBlue, + Example: cc.HiBlue + cc.Italic, + ExecName: cc.HiBlue + cc.Bold, + Flags: cc.HiBlue + cc.Bold, + //FlagsDataType: cc.HiBlue, + FlagsDescr: cc.HiBlue, + NoExtraNewlines: true, + NoBottomNewline: true, + }) + commands.Execute() +} + +const help = "Usage:\r\n" + + " {{.UseLine}}{{if .HasAvailableSubCommands}}{{end}} {{if gt (len .Aliases) 0}}\r\n\r\n" + + "{{.NameAndAliases}}{{end}}{{if .HasAvailableSubCommands}}\r\n\r\n" + + "Available Commands:{{range .Commands}}{{if (or .IsAvailableCommand)}}\r\n " + + "{{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}{{if .HasAvailableLocalFlags}}\r\n\r\n" + + "Flags:\r\n" + + "{{.LocalFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasAvailableInheritedFlags}}\r\n\r\n" + + "Global Flags:\r\n" + + "{{.InheritedFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}\r\n\r\n" diff --git a/examples/http/http.go b/examples/http/http.go new file mode 100644 index 00000000..2f9750f1 --- /dev/null +++ b/examples/http/http.go @@ -0,0 +1,33 @@ +package main + +import ( + "fmt" + "log" + "net" + "net/http" + "os" +) + +func main() { + // Define the HTTP handler + http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + log.Printf("Received request: %s %s", r.Method, r.URL.Path) + fmt.Fprintf(w, "Hello, World!\n") + }) + + // Use the specified port from command-line arguments + address := os.Args[1] + listener, err := net.Listen("tcp", address) + if err != nil { + log.Fatal("Failed to start HTTP server:", err) + return + } + defer listener.Close() + + log.Println("HTTP server started on", address) + // Start the HTTP server + err = http.Serve(listener, nil) + if err != nil { + log.Fatal("HTTP server stopped with error:", err) + } +} diff --git a/examples/tcp-proxy-dmsg/tcp-multi-proxy-dmsg.go b/examples/tcp-proxy-dmsg/tcp-multi-proxy-dmsg.go new file mode 100644 index 00000000..b7a60352 --- /dev/null +++ b/examples/tcp-proxy-dmsg/tcp-multi-proxy-dmsg.go @@ -0,0 +1,174 @@ +package main + +import ( + "context" + "fmt" + "io" + "net" + "net/http" + "sync" + + cc "github.com/ivanpirog/coloredcobra" + "github.com/skycoin/skywire/pkg/skywire-utilities/pkg/cipher" + "github.com/skycoin/skywire/pkg/skywire-utilities/pkg/cmdutil" + "github.com/skycoin/skywire/pkg/skywire-utilities/pkg/logging" + "github.com/spf13/cobra" + + "github.com/skycoin/dmsg/pkg/disc" + dmsg "github.com/skycoin/dmsg/pkg/dmsg" +) + +func main() { + cc.Init(&cc.Config{ + RootCmd: srvCmd, + Headings: cc.HiBlue + cc.Bold, + Commands: cc.HiBlue + cc.Bold, + CmdShortDescr: cc.HiBlue, + Example: cc.HiBlue + cc.Italic, + ExecName: cc.HiBlue + cc.Bold, + Flags: cc.HiBlue + cc.Bold, + FlagsDescr: cc.HiBlue, + NoExtraNewlines: true, + NoBottomNewline: true, + }) + srvCmd.Execute() +} + +const help = "Usage:\r\n" + + " {{.UseLine}}{{if .HasAvailableSubCommands}}{{end}} {{if gt (len .Aliases) 0}}\r\n\r\n" + + "{{.NameAndAliases}}{{end}}{{if .HasAvailableSubCommands}}\r\n\r\n" + + "Available Commands:{{range .Commands}}{{if (or .IsAvailableCommand)}}\r\n " + + "{{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}{{if .HasAvailableLocalFlags}}\r\n\r\n" + + "Flags:\r\n" + + "{{.LocalFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasAvailableInheritedFlags}}\r\n\r\n" + + "Global Flags:\r\n" + + "{{.InheritedFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}\r\n\r\n" + +var ( + localPorts []uint + dmsgPorts []uint + dmsgDisc string + dmsgSess int + sk cipher.SecKey +) + +func init() { + srvCmd.Flags().UintSliceVarP(&localPorts, "lport", "l", nil, "local application HTTP interface port(s) (comma-separated)") + srvCmd.Flags().UintSliceVarP(&dmsgPorts, "dport", "d", nil, "DMSG port(s) to serve (comma-separated)") + srvCmd.Flags().StringVarP(&dmsgDisc, "dmsg-disc", "D", dmsg.DiscAddr(false), "DMSG discovery URL") + srvCmd.Flags().IntVarP(&dmsgSess, "dsess", "e", 1, "DMSG sessions") + srvCmd.Flags().VarP(&sk, "sk", "s", "a random key is generated if unspecified\n\r") + + srvCmd.CompletionOptions.DisableDefaultCmd = true + var helpFlag bool + srvCmd.SetUsageTemplate(help) + srvCmd.PersistentFlags().BoolVarP(&helpFlag, "help", "h", false, "help for dmsgweb") + srvCmd.SetHelpCommand(&cobra.Command{Hidden: true}) + srvCmd.PersistentFlags().MarkHidden("help") //nolint +} + +var srvCmd = &cobra.Command{ + Use: "srv", + Short: "Serve raw TCP from local ports over DMSG", + Long: `DMSG web server - serve HTTP or raw TCP interface from local ports over DMSG`, + Run: func(_ *cobra.Command, _ []string) { + server() + }, +} + +func server() { + log := logging.MustGetLogger("dmsgwebsrv") + ctx, cancel := cmdutil.SignalContext(context.Background(), log) + defer cancel() + + if len(localPorts) != len(dmsgPorts) { + log.Fatalf("The number of local ports (%d) must match the number of DMSG ports (%d).", len(localPorts), len(dmsgPorts)) + } + + pk, err := sk.PubKey() + if err != nil { + pk, sk = cipher.GenerateKeyPair() + } + log.Infof("DMSG client public key: %v", pk.String()) + + dmsgC := dmsg.NewClient(pk, sk, disc.NewHTTP(dmsgDisc, &http.Client{}, log), dmsg.DefaultConfig()) + defer func() { + if err := dmsgC.Close(); err != nil { + log.WithError(err).Error("Error closing DMSG client") + } + }() + go dmsgC.Serve(ctx) + + select { + case <-ctx.Done(): + log.WithError(ctx.Err()).Warn() + return + case <-dmsgC.Ready(): + log.Info("DMSG client is ready.") + } + + wg := new(sync.WaitGroup) + for i, localPort := range localPorts { + dmsgPort := dmsgPorts[i] + wg.Add(1) + + go func(localPort, dmsgPort uint) { + defer wg.Done() + proxyPort(ctx, dmsgC, localPort, dmsgPort, log) + }(localPort, dmsgPort) + } + + wg.Wait() +} + +func proxyPort(ctx context.Context, dmsgC *dmsg.Client, localPort, dmsgPort uint, log *logging.Logger) { + listener, err := dmsgC.Listen(uint16(dmsgPort)) + if err != nil { + log.Fatalf("Error listening on DMSG port %d: %v", dmsgPort, err) + } + defer listener.Close() + + log.Infof("Started proxying local port %d to DMSG port %d", localPort, dmsgPort) + + go func() { + <-ctx.Done() + listener.Close() + }() + + for { + conn, err := listener.Accept() + if err != nil { + log.Printf("Error accepting connection on DMSG port %d: %v", dmsgPort, err) + return + } + + go handleTCPConnection(conn, localPort, log) + } +} + +func handleTCPConnection(dmsgConn net.Conn, localPort uint, log *logging.Logger) { + defer dmsgConn.Close() + + localConn, err := net.Dial("tcp", fmt.Sprintf("127.0.0.1:%d", localPort)) + if err != nil { + log.Printf("Failed to connect to local port %d: %v", localPort, err) + return + } + defer localConn.Close() + + var wg sync.WaitGroup + wg.Add(2) + + go func() { + defer wg.Done() + io.Copy(dmsgConn, localConn) + }() + + go func() { + defer wg.Done() + io.Copy(localConn, dmsgConn) + }() + + wg.Wait() + log.Printf("Closed connection between local port %d and DMSG connection", localPort) +} diff --git a/examples/tcp-proxy/tcp-proxy.go b/examples/tcp-proxy/tcp-proxy.go index 888dc2c1..d126ae45 100644 --- a/examples/tcp-proxy/tcp-proxy.go +++ b/examples/tcp-proxy/tcp-proxy.go @@ -12,7 +12,7 @@ import ( func main() { if len(os.Args) < 3 { - log.Fatalf("requires two arguments; usage: tcp1 ") + log.Fatalf("requires two arguments; usage: tcp-proxy ") } sourcePort, err := strconv.Atoi(os.Args[2]) if err != nil { diff --git a/examples/tcp-reverse-proxy-dmsg/tcp-reverse-proxy-dmsg.go b/examples/tcp-reverse-proxy-dmsg/tcp-reverse-proxy-dmsg.go index c5f5a9da..1bbd240f 100644 --- a/examples/tcp-reverse-proxy-dmsg/tcp-reverse-proxy-dmsg.go +++ b/examples/tcp-reverse-proxy-dmsg/tcp-reverse-proxy-dmsg.go @@ -160,6 +160,9 @@ func proxyTCPConn() { } defer listener.Close() //nolint log.Printf("Serving TCP on 127.0.0.1:%v", webPort) + if dmsgC == nil { + log.Fatal("dmsgC is nil") + } for { conn, err := listener.Accept() @@ -172,6 +175,7 @@ func proxyTCPConn() { go func(conn net.Conn) { defer wg.Done() + log.Println(fmt.Sprintf("Dialing dmsg address: %v ; port: %v", dialPK.String(), dmsgPort)) dmsgConn, err := dmsgC.DialStream(context.Background(), dmsg.Addr{PK: dialPK, Port: uint16(dmsgPort)}) if err != nil { log.Printf("Failed to dial dmsg address %v:%v %v", dialPK.String(), dmsgPort, err) From 4d68c98a005744848f7bcb48ae08b48321f7ef5f Mon Sep 17 00:00:00 2001 From: Moses Narrow Date: Wed, 29 Jan 2025 10:52:18 -0600 Subject: [PATCH 14/21] add examples --- cmd/dmsgweb/commands/dmsgweb.go | 210 +++++++++---------- cmd/dmsgweb/commands/dmsgwebsrv.go | 217 ++++++++++++-------- cmd/dmsgweb/commands/root.go | 18 ++ examples/dmsghttp-client/dmsghttp-client.go | 49 +++++ examples/dmsghttp/README.md | 72 +++++++ examples/dmsghttp/dmsghttp.go | 153 ++++++++++++++ examples/dmsgtcp/README.md | 1 + examples/dmsgtcp/dmsgtcp.go | 166 +++++++++++++++ examples/dmsgweb/commands/dmsgweb.go | 5 +- examples/dmsgweb/commands/root.go | 2 +- examples/gen-keys/README.md | 11 + examples/gen-keys/gen-keys.go | 12 ++ examples/http/README.md | 1 + examples/http/http.go | 1 + examples/tcp/README.md | 1 + examples/tcp/tcp.go | 1 + 16 files changed, 723 insertions(+), 197 deletions(-) create mode 100644 examples/dmsghttp-client/dmsghttp-client.go create mode 100644 examples/dmsghttp/README.md create mode 100644 examples/dmsghttp/dmsghttp.go create mode 100644 examples/dmsgtcp/README.md create mode 100644 examples/dmsgtcp/dmsgtcp.go create mode 100644 examples/gen-keys/README.md create mode 100644 examples/gen-keys/gen-keys.go create mode 100644 examples/http/README.md create mode 100644 examples/tcp/README.md diff --git a/cmd/dmsgweb/commands/dmsgweb.go b/cmd/dmsgweb/commands/dmsgweb.go index e54ab01a..5c4fde6b 100644 --- a/cmd/dmsgweb/commands/dmsgweb.go +++ b/cmd/dmsgweb/commands/dmsgweb.go @@ -11,12 +11,10 @@ import ( "os/signal" "path/filepath" "regexp" - "runtime" "strconv" "strings" "syscall" - "github.com/bitfield/script" "github.com/chen3feng/safecast" "github.com/confiant-inc/go-socks5" "github.com/gin-gonic/gin" @@ -35,7 +33,6 @@ import ( type customResolver struct{} func (r *customResolver) Resolve(ctx context.Context, name string) (context.Context, net.IP, error) { - // Handle custom name resolution for .dmsg domains regexPattern := `\.` + filterDomainSuffix + `(:[0-9]+)?$` match, _ := regexp.MatchString(regexPattern, name) //nolint:errcheck if match { @@ -43,35 +40,43 @@ func (r *customResolver) Resolve(ctx context.Context, name string) (context.Cont if ip == nil { return ctx, nil, fmt.Errorf("failed to parse IP address") } - // Modify the context to include the desired port ctx = context.WithValue(ctx, "port", fmt.Sprintf("%v", webPort)) //nolint return ctx, ip, nil } - // Use default name resolution for other domains return ctx, nil, nil } -const dmsgwebenvname = "DMSGWEB" +const dwenv = "DMSGWEB" -var dmsgwebconffile = os.Getenv(dmsgwebenvname) +var dwcfg = os.Getenv(dwenv) func init() { - RootCmd.Flags().StringVarP(&filterDomainSuffix, "filter", "f", ".dmsg", "domain suffix to filter") - RootCmd.Flags().UintVarP(&proxyPort, "socks", "q", scriptExecUint("${PROXYPORT:-4445}", dmsgwebconffile), "port to serve the socks5 proxy") - RootCmd.Flags().StringVarP(&addProxy, "addproxy", "r", scriptExecString("${ADDPROXY}", dmsgwebconffile), "configure additional socks5 proxy for dmsgweb (i.e. 127.0.0.1:1080)") - RootCmd.Flags().UintSliceVarP(&webPort, "port", "p", scriptExecUintSlice("${WEBPORT[@]:-8080}", dmsgwebconffile), "port(s) to serve the web application") - RootCmd.Flags().StringSliceVarP(&resolveDmsgAddr, "resolve", "t", scriptExecStringSlice("${RESOLVEPK[@]}", dmsgwebconffile), "resolve the specified dmsg address:port on the local port & disable proxy") - RootCmd.Flags().StringVarP(&dmsgDisc, "dmsg-disc", "D", dmsgDisc, "dmsg discovery url") - RootCmd.Flags().StringVarP(&proxyAddr, "proxy", "x", "", "connect to dmsg via proxy (i.e. '127.0.0.1:1080')") - RootCmd.Flags().IntVarP(&dmsgSessions, "sess", "e", scriptExecInt("${DMSGSESSIONS:-1}", dmsgwebconffile), "number of dmsg servers to connect to") - RootCmd.Flags().BoolSliceVarP(&rawTCP, "rt", "c", scriptExecBoolSlice("${RAWTCP[@]:-false}", dmsgwebconffile), "proxy local port as raw TCP") - RootCmd.Flags().StringVarP(&logLvl, "loglvl", "l", "", "[ debug | warn | error | fatal | panic | trace | info ]\033[0m") + dLog = logging.MustGetLogger("dmsgweb") + dmsgDisc = dmsg.DiscAddr(false) + webPort = scriptExecUintSlice("${WEBPORT[@]:-8080}", dwcfg) + proxyPort = scriptExecUint("${PROXYPORT:-4445}", dwcfg) + addProxy = scriptExecString("${ADDPROXY}", dwcfg) + resolveDmsgAddr = scriptExecStringSlice("${RESOLVEPK[@]}", dwcfg) + dmsgSess = scriptExecInt("${DMSGSESSIONS:-1}", dwcfg) + rawTCP = scriptExecBoolSlice("${RAWTCP[@]:-false}", dwcfg) if os.Getenv("DMSGWEBSK") != "" { sk.Set(os.Getenv("DMSGWEBSK")) //nolint } - if scriptExecString("${DMSGWEBSK}", dmsgwebconffile) != "" { - sk.Set(scriptExecString("${DMSGWEBSK}", dmsgwebconffile)) //nolint + if scriptExecString("${DMSGWEBSK}", dwcfg) != "" { + sk.Set(scriptExecString("${DMSGWEBSK}", dwcfg)) //nolint } + pk, _ = sk.PubKey() + + RootCmd.Flags().StringVarP(&filterDomainSuffix, "filter", "f", ".dmsg", "domain suffix to filter") + RootCmd.Flags().UintVarP(&proxyPort, "socks", "q", proxyPort, "port to serve the socks5 proxy") + RootCmd.Flags().StringVarP(&addProxy, "addproxy", "r", addProxy, "configure additional socks5 proxy for dmsgweb (i.e. 127.0.0.1:1080)") + RootCmd.Flags().UintSliceVarP(&webPort, "port", "p", webPort, "port(s) to serve the web application") + RootCmd.Flags().StringSliceVarP(&resolveDmsgAddr, "resolve", "t", resolveDmsgAddr, "resolve the specified dmsg address:port on the local port & disable proxy") + RootCmd.Flags().StringVarP(&dmsgDisc, "dmsg-disc", "D", dmsgDisc, "dmsg discovery url") + RootCmd.Flags().StringVarP(&proxyAddr, "proxy", "x", "", "connect to dmsg via proxy (i.e. '127.0.0.1:1080')") + RootCmd.Flags().IntVarP(&dmsgSessions, "sess", "e", dmsgSess, "number of dmsg servers to connect to") + RootCmd.Flags().BoolSliceVarP(&rawTCP, "rt", "c", rawTCP, "proxy local port as raw TCP") + RootCmd.Flags().StringVarP(&logLvl, "loglvl", "l", "", "[ debug | warn | error | fatal | panic | trace | info ]\033[0m") RootCmd.Flags().VarP(&sk, "sk", "s", "a random key is generated if unspecified\n\r") RootCmd.Flags().BoolVarP(&isEnvs, "envs", "z", false, "show example .conf file") @@ -88,44 +93,22 @@ var RootCmd = &cobra.Command{ │││││└─┐│ ┬│││├┤ ├┴┐ ─┴┘┴ ┴└─┘└─┘└┴┘└─┘└─┘ DMSG resolving proxy & browser client - access websites and http interfaces over dmsg` + func() string { - if _, err := os.Stat(dmsgwebconffile); err == nil { + if _, err := os.Stat(dwcfg); err == nil { return ` -dmsgweb conf file detected: ` + dmsgwebconffile +dmsgweb conf file detected: ` + dwcfg } return ` .conf file may also be specified with -` + dmsgwebenvname + `=/path/to/dmsgweb.conf skywire dmsg web` +` + dwenv + `=/path/to/dmsgweb.conf skywire dmsg web` }(), SilenceErrors: true, SilenceUsage: true, DisableSuggestions: true, DisableFlagsInUseLine: true, Version: buildinfo.Version(), - Run: func(_ *cobra.Command, _ []string) { + PreRun: func(_ *cobra.Command, _ []string) { if isEnvs { - envfile := envfileLinux - if runtime.GOOS == "windows" { - envfileslice, _ := script.Echo(envfile).Slice() //nolint - for i := range envfileslice { - efs, _ := script.Echo(envfileslice[i]).Reject("##").Reject("#-").Reject("# ").Replace("#", "#$").String() //nolint - if efs != "" && efs != "\n" { - envfileslice[i] = strings.ReplaceAll(efs, "\n", "") - } - } - envfile = strings.Join(envfileslice, "\n") - } - fmt.Println(envfile) - os.Exit(0) - } - - c := make(chan os.Signal, 1) - signal.Notify(c, os.Interrupt, syscall.SIGTERM) //nolint - go func() { - <-c - os.Exit(1) - }() - if dmsgWebLog == nil { - dmsgWebLog = logging.MustGetLogger("dmsgweb") + printEnvs(srvenvfileLinux) } if logLvl != "" { if lvl, err := logging.LevelFromString(logLvl); err == nil { @@ -133,20 +116,20 @@ dmsgweb conf file detected: ` + dmsgwebconffile } } if dmsgDisc == "" { - dmsgWebLog.Fatal("Dmsg Discovery URL not specified") + dLog.Fatal("Dmsg Discovery URL not specified") } if len(resolveDmsgAddr) > 0 && len(webPort) != len(resolveDmsgAddr) { - dmsgWebLog.Fatal("-resolve --t flag cannot contain a different number of elements than -port -p flag") + dLog.Fatal("-resolve --t flag cannot contain a different number of elements than -port -p flag") } if len(resolveDmsgAddr) == 0 && len(webPort) > 1 { - dmsgWebLog.Fatal("-port --p flag cannot specify multiple ports without specifying multiple dmsg address:port(s) to -resolve --t flag") + dLog.Fatal("-port --p flag cannot specify multiple ports without specifying multiple dmsg address:port(s) to -resolve --t flag") } seenResolveDmsgAddr := make(map[string]bool) for _, item := range resolveDmsgAddr { if seenResolveDmsgAddr[item] { - dmsgWebLog.Fatal("-resolve --t flag cannot contain duplicates") + dLog.Fatal("-resolve --t flag cannot contain duplicates") } seenResolveDmsgAddr[item] = true } @@ -154,7 +137,7 @@ dmsgweb conf file detected: ` + dmsgwebconffile seenWebPort := make(map[uint]bool) for _, item := range webPort { if seenWebPort[item] { - dmsgWebLog.Fatal("-port --p flag cannot contain duplicates") + dLog.Fatal("-port --p flag cannot contain duplicates") } seenWebPort[item] = true } @@ -166,47 +149,70 @@ dmsgweb conf file detected: ` + dmsgwebconffile } else if len(rawTCP) > len(resolveDmsgAddr) { rawTCP = rawTCP[:len(resolveDmsgAddr)] } - + if len(webPort) == 0 { + dLog.Fatal("webPort is empty. Ensure at least one port is specified.") + } if filterDomainSuffix == "" { - dmsgWebLog.Fatal("domain suffix to filter cannot be an empty string") + dLog.Fatal("domain suffix to filter cannot be an empty string") } - ctx, cancel := cmdutil.SignalContext(context.Background(), dmsgWebLog) + }, + Run: func(_ *cobra.Command, _ []string) { + c := make(chan os.Signal, 1) + signal.Notify(c, os.Interrupt, syscall.SIGTERM) //nolint + go func() { + <-c + os.Exit(1) + }() + + ctx, cancel := cmdutil.SignalContext(context.Background(), dLog) defer cancel() - pk, err := sk.PubKey() + pk, err = sk.PubKey() if err != nil { pk, sk = cipher.GenerateKeyPair() } - dmsgWebLog.Info("dmsg client pk: ", pk.String()) + dLog.Info("dmsg client pk: ", pk.String()) if len(resolveDmsgAddr) > 0 { - dialPK = make([]cipher.PubKey, len(resolveDmsgAddr)) - dmsgPorts = make([]uint, len(resolveDmsgAddr)) + dialPK := make([]cipher.PubKey, len(resolveDmsgAddr)) + dmsgPorts := make([]uint, len(resolveDmsgAddr)) + for i, dmsgaddr := range resolveDmsgAddr { - dmsgWebLog.Info("dmsg address to dial: ", dmsgaddr) - dmsgAddr = strings.Split(dmsgaddr, ":") - var setpk cipher.PubKey - err := setpk.Set(dmsgAddr[0]) - if err != nil { - dmsgWebLog.WithError(err).Fatal("failed to parse dmsg
:") + dLog.Info("dmsg address to dial: ", dmsgaddr) + + // Split the address into public key and port + parts := strings.Split(dmsgaddr, ":") + if len(parts) < 1 || parts[0] == "" { + dLog.Fatal("Invalid dmsg address format. Expected [:]") } - dialPK[i] = setpk - if len(dmsgAddr) > 1 { - dport, err := strconv.ParseUint(dmsgAddr[1], 10, 64) + + // Parse the public key + var pk cipher.PubKey + if err := pk.Set(parts[0]); err != nil { + dLog.WithError(err).Fatal("Failed to parse public key from dmsg address") + } + dialPK[i] = pk + dLog.Info("Parsed public key: ", pk) + + // Parse the port or use the default (80) + port := uint(80) // Default port + if len(parts) > 1 && parts[1] != "" { + parsedPort, err := strconv.ParseUint(parts[1], 10, 16) // Ports are 16-bit unsigned integers if err != nil { - dmsgWebLog.WithError(err).Fatal("Failed to parse dmsg port") + dLog.WithError(err).Fatal("Failed to parse dmsg port") } - dmsgPorts[i] = uint(dport) - } else { - dmsgPorts[i] = uint(80) + port = uint(parsedPort) } + dmsgPorts[i] = port + dLog.Info("Parsed port: ", port) } } +/* if proxyAddr != "" { // Use SOCKS5 proxy dialer if specified dialer, err = proxy.SOCKS5("tcp", proxyAddr, nil, proxy.Direct) if err != nil { - dmsgWebLog.WithError(err).Fatal("Error creating SOCKS5 dialer") + dLog.WithError(err).Fatal("Error creating SOCKS5 dialer") } transport := &http.Transport{ Dial: dialer.Dial, @@ -216,10 +222,11 @@ dmsgweb conf file detected: ` + dmsgwebconffile } ctx = context.WithValue(context.Background(), "socks5_proxy", proxyAddr) //nolint } - - dmsgC, closeDmsg, err := cli.StartDmsg(ctx, dmsgWebLog, pk, sk, &httpC, dmsgDisc, dmsgSessions) +*/ +// dmsgC, closeDmsg, err := cli.StartDmsg(ctx, dLog, pk, sk, &httpC, dmsgDisc, dmsgSessions) + dmsgC, closeDmsg, err = cli.StartDmsg(ctx, dLog, pk, sk, &http.Client{}, dmsgDisc, dmsgSessions) if err != nil { - dmsgWebLog.WithError(err).Fatal("failed to start dmsg") + dLog.WithError(err).Fatal("failed to start dmsg") } defer closeDmsg() @@ -233,7 +240,6 @@ dmsgweb conf file detected: ` + dmsgwebconffile httpC = http.Client{Transport: dmsghttp.MakeHTTPTransport(ctx, dmsgC)} if len(resolveDmsgAddr) == 0 { - // Create a SOCKS5 server with custom name resolution conf := &socks5.Config{ Resolver: &customResolver{}, Dial: func(ctx context.Context, network, addr string) (net.Conn, error) { @@ -251,7 +257,6 @@ dmsgweb conf file detected: ` + dmsgwebconffile addr = "localhost:" + port } else { if addProxy != "" { - // Fallback to another SOCKS5 proxy dialer, err := proxy.SOCKS5("tcp", addProxy, nil, proxy.Direct) if err != nil { return nil, err @@ -259,47 +264,46 @@ dmsgweb conf file detected: ` + dmsgwebconffile return dialer.Dial(network, addr) } } - dmsgWebLog.Debug("Dialing address:", addr) + dLog.Debug("Dialing address:", addr) return net.Dial(network, addr) }, } - // Start the SOCKS5 server socksAddr := fmt.Sprintf("127.0.0.1:%v", proxyPort) - dmsgWebLog.Debug("SOCKS5 proxy server started on", socksAddr) + dLog.Debug("SOCKS5 proxy server started on", socksAddr) server, err := socks5.New(conf) if err != nil { - dmsgWebLog.WithError(err).Fatal("Failed to create SOCKS5 server") + dLog.WithError(err).Fatal("Failed to create SOCKS5 server") } wg.Add(1) go func() { - dmsgWebLog.Debug("Serving SOCKS5 proxy on " + socksAddr) + dLog.Debug("Serving SOCKS5 proxy on " + socksAddr) err := server.ListenAndServe("tcp", socksAddr) if err != nil { - dmsgWebLog.WithError(err).Fatal("Failed to start SOCKS5 server") + dLog.WithError(err).Fatal("Failed to start SOCKS5 server") } defer server.Close() - dmsgWebLog.Debug("Stopped serving SOCKS5 proxy on " + socksAddr) + dLog.Debug("Stopped serving SOCKS5 proxy on " + socksAddr) }() } if len(resolveDmsgAddr) == 0 && len(webPort) == 1 { if rawTCP[0] { - dmsgWebLog.Debug("proxyTCPConn(-1)") + dLog.Debug("proxyTCPConn(-1)") proxyTCPConn(-1) } else { - dmsgWebLog.Debug("proxyHTTPConn(-1)") + dLog.Debug("proxyHTTPConn(-1)") proxyHTTPConn(-1) } } else { for i := range resolveDmsgAddr { if rawTCP[i] { - dmsgWebLog.Debug("proxyTCPConn(" + fmt.Sprintf("%v", i) + ")") + dLog.Debug("proxyTCPConn(" + fmt.Sprintf("%v", i) + ")") proxyTCPConn(i) } else { - dmsgWebLog.Debug("proxyHTTPConn(" + fmt.Sprintf("%v", i) + ")") + dLog.Debug("proxyHTTPConn(" + fmt.Sprintf("%v", i) + ")") proxyHTTPConn(i) } } @@ -336,11 +340,11 @@ func proxyHTTPConn(n int) { } } - dmsgWebLog.Debug("Proxying request: %s %s\n", c.Request.Method, urlStr) + dLog.Debug(fmt.Sprintf("Proxying request: %s %s", c.Request.Method, urlStr)) req, err := http.NewRequest(c.Request.Method, urlStr, c.Request.Body) if err != nil { c.String(http.StatusInternalServerError, "Failed to create HTTP request") - dmsgWebLog.WithError(err).Warn("Failed to create HTTP request") + dLog.WithError(err).Warn("Failed to create HTTP request") return } @@ -353,7 +357,7 @@ func proxyHTTPConn(n int) { resp, err := httpC.Do(req) if err != nil { c.String(http.StatusInternalServerError, "Failed to connect to HTTP server") - dmsgWebLog.WithError(err).Warn("Failed to connect to HTTP server") + dLog.WithError(err).Warn("Failed to connect to HTTP server") return } defer resp.Body.Close() //nolint @@ -367,7 +371,7 @@ func proxyHTTPConn(n int) { c.Status(resp.StatusCode) if _, err := io.Copy(c.Writer, resp.Body); err != nil { c.String(http.StatusInternalServerError, "Failed to copy response body") - dmsgWebLog.WithError(err).Warn("Failed to copy response body") + dLog.WithError(err).Warn("Failed to copy response body") } }) wg.Add(1) @@ -378,9 +382,9 @@ func proxyHTTPConn(n int) { } else { thiswebport = webPort[n] } - dmsgWebLog.Debug(fmt.Sprintf("Serving http on: http://127.0.0.1:%v", thiswebport)) + dLog.Debug(fmt.Sprintf("Serving http on: http://127.0.0.1:%v", thiswebport)) r.Run(":" + fmt.Sprintf("%v", thiswebport)) //nolint - dmsgWebLog.Debug(fmt.Sprintf("Stopped serving http on: http://127.0.0.1:%v", thiswebport)) + dLog.Debug(fmt.Sprintf("Stopped serving http on: http://127.0.0.1:%v", thiswebport)) wg.Done() }() } @@ -393,18 +397,18 @@ func proxyTCPConn(n int) { } listener, err := net.Listen("tcp", fmt.Sprintf(":%v", thiswebport)) if err != nil { - dmsgWebLog.WithError(err).Fatal(fmt.Sprintf("Failed to start TCP listener on port: %v", thiswebport)) + dLog.WithError(err).Fatal(fmt.Sprintf("Failed to start TCP listener on port: %v", thiswebport)) } defer listener.Close() //nolint - dmsgWebLog.Debug("Serving TCP on 127.0.0.1:", thiswebport) + dLog.Debug("Serving TCP on 127.0.0.1:", thiswebport) if dmsgC == nil { - dmsgWebLog.Fatal("dmsgC is nil") + dLog.Fatal("dmsgC is nil") } for { conn, err := listener.Accept() if err != nil { - dmsgWebLog.WithError(err).Warn("Failed to accept connection") + dLog.WithError(err).Warn("Failed to accept connection") continue } @@ -414,12 +418,12 @@ func proxyTCPConn(n int) { dp, ok := safecast.To[uint16](dmsgPorts[n]) if !ok { - dmsgWebLog.Fatal("uint16 overflow when converting dmsg port") + dLog.Fatal("uint16 overflow when converting dmsg port") } - dmsgWebLog.Debug(fmt.Sprintf("Dialing dmsg address: %v ; port: %v", dialPK[n].String(), dp)) + dLog.Debug(fmt.Sprintf("Dialing dmsg address: %v ; port: %v", dialPK[n].String(), dp)) dmsgConn, err := dmsgC.DialStream(context.Background(), dmsg.Addr{PK: dialPK[n], Port: dp}) //nolint if err != nil { - dmsgWebLog.WithError(err).Warn(fmt.Sprintf("Failed to dial dmsg address %v port %v", dialPK[n].String(), dmsgPorts[n])) + dLog.WithError(err).Warn(fmt.Sprintf("Failed to dial dmsg address %v port %v", dialPK[n].String(), dmsgPorts[n])) return } defer dmsgConn.Close() //nolint @@ -428,7 +432,7 @@ func proxyTCPConn(n int) { defer dmsgConn.Close() _, err := io.Copy(dmsgConn, conn) if err != nil { - dmsgWebLog.WithError(err).Warn("Error on io.Copy(dmsgConn, conn)") + dLog.WithError(err).Warn("Error on io.Copy(dmsgConn, conn)") } }() @@ -436,7 +440,7 @@ func proxyTCPConn(n int) { defer conn.Close() //nolint _, err := io.Copy(conn, dmsgConn) if err != nil { - dmsgWebLog.WithError(err).Warn("Error on io.Copy(conn, dmsgConn)") + dLog.WithError(err).Warn("Error on io.Copy(conn, dmsgConn)") } }() }(conn, n, dmsgC) diff --git a/cmd/dmsgweb/commands/dmsgwebsrv.go b/cmd/dmsgweb/commands/dmsgwebsrv.go index bed92748..7083ea76 100644 --- a/cmd/dmsgweb/commands/dmsgwebsrv.go +++ b/cmd/dmsgweb/commands/dmsgwebsrv.go @@ -10,11 +10,8 @@ import ( "net/http/httputil" "net/url" "os" - "runtime" - "strings" "sync" - "github.com/bitfield/script" "github.com/gin-gonic/gin" "github.com/skycoin/skywire/pkg/skywire-utilities/pkg/cipher" "github.com/skycoin/skywire/pkg/skywire-utilities/pkg/cmdutil" @@ -26,28 +23,35 @@ import ( dmsg "github.com/skycoin/dmsg/pkg/dmsg" ) -const dmsgwebsrvenvname = "DMSGWEBSRV" +const dwsenv = "DMSGWEBSRV" -func init() { - RootCmd.AddCommand(srvCmd) - srvCmd.Flags().UintSliceVarP(&localPort, "lport", "l", scriptExecUintSlice("${LOCALPORT[@]:-8086}", srvenvfileLinux), "local application HTTP interface port(s)") - srvCmd.Flags().UintSliceVarP(&dmsgPort, "dport", "d", scriptExecUintSlice("${DMSGPORT[@]:-80}", srvenvfileLinux), "DMSG port(s) to serve") - srvCmd.Flags().StringSliceVarP(&wl, "wl", "w", scriptExecStringSlice("${WHITELISTPKS[@]}", srvenvfileLinux), "whitelisted keys for DMSG authenticated routes") - srvCmd.Flags().StringVarP(&dmsgDisc, "dmsg-disc", "D", dmsgDisc, "DMSG discovery URL(s)") - srvCmd.Flags().StringVarP(&proxyAddr, "proxy", "x", proxyAddr, "connect to DMSG via proxy (e.g., '127.0.0.1:1080')") - srvCmd.Flags().IntVarP(&dmsgSess, "dsess", "e", scriptExecInt("${DMSGSESSIONS:-1}", srvenvfileLinux), "DMSG sessions") - srvCmd.Flags().BoolSliceVarP(&rawTCP, "rt", "c", scriptExecBoolSlice("${RAWTCP[@]:-false}", srvenvfileLinux), "proxy local port as raw TCP") - srvCmd.Flags().BoolVarP(&isEnvs, "envs", "z", false, "show example .conf file") +var dwscfg = os.Getenv(dwsenv) +func init() { + dLog = logging.MustGetLogger("dmsgwebsrv") + dmsgPort = scriptExecUintSlice("${DMSGPORT[@]:-80}", dwscfg) + dmsgSess = scriptExecInt("${DMSGSESSIONS:-1}", dwscfg) + wl = scriptExecStringSlice("${WHITELISTPKS[@]}", dwscfg) + localPort = scriptExecUintSlice("${LOCALPORT[@]:-8086}", dwscfg) + rawTCP = scriptExecBoolSlice("${RAWTCP[@]:-false}", dwscfg) if os.Getenv("DMSGWEBSRVSK") != "" { sk.Set(os.Getenv("DMSGWEBSRVSK")) } - if scriptExecString("${DMSGWEBSRVSK}", srvenvfileLinux) != "" { - sk.Set(scriptExecString("${DMSGWEBSRVSK}", srvenvfileLinux)) + if scriptExecString("${DMSGWEBSRVSK}", dwscfg) != "" { + sk.Set(scriptExecString("${DMSGWEBSRVSK}", dwscfg)) } pk, _ = sk.PubKey() - srvCmd.Flags().VarP(&sk, "sk", "s", "a random key is generated if unspecified") + RootCmd.AddCommand(srvCmd) + srvCmd.Flags().UintSliceVarP(&localPort, "lport", "l", localPort, "local application HTTP interface port(s)") + srvCmd.Flags().UintSliceVarP(&dmsgPort, "dport", "d", dmsgPort, "DMSG port(s) to serve") + srvCmd.Flags().StringSliceVarP(&wl, "wl", "w", wl, "whitelisted keys for DMSG authenticated routes") + srvCmd.Flags().StringVarP(&dmsgDisc, "dmsg-disc", "D", dmsgDisc, "DMSG discovery URL(s)") + srvCmd.Flags().StringVarP(&proxyAddr, "proxy", "x", proxyAddr, "connect to DMSG via proxy (e.g., '127.0.0.1:1080')") + srvCmd.Flags().IntVarP(&dmsgSess, "dsess", "e", dmsgSess, "DMSG sessions") + srvCmd.Flags().BoolSliceVarP(&rawTCP, "rt", "c", rawTCP, "proxy local port as raw TCP") + srvCmd.Flags().BoolVarP(&isEnvs, "envs", "z", false, "show example .conf file") + srvCmd.Flags().VarP(&sk, "sk", "s", "a random key is generated if unspecified") srvCmd.CompletionOptions.DisableDefaultCmd = true } @@ -55,78 +59,64 @@ var srvCmd = &cobra.Command{ Use: "srv", Short: "Serve HTTP or raw TCP from local port over DMSG", Long: `DMSG web server - serve HTTP or raw TCP interface from local port over DMSG` + func() string { - if _, err := os.Stat(srvenvfileLinux); err == nil { - return "\n\t.dmsenv file detected: " + srvenvfileLinux + if _, err := os.Stat(dwscfg); err == nil { + return "\n\t.dmsenv file detected: " + dwscfg } - return "\n\t.conf file may also be specified with " + dmsgwebsrvenvname + `=/path/to/dmsgwebsrv.conf skywire dmsg web srv` + return "\n\t.conf file may also be specified with " + dwsenv + `=/path/to/dmsgwebsrv.conf skywire dmsg web srv` }(), - Run: func(_ *cobra.Command, _ []string) { + PreRun: func(_ *cobra.Command, _ []string) { if isEnvs { - envfile := srvenvfileLinux - if runtime.GOOS == "windows" { - envfileslice, _ := script.Echo(envfile).Slice() - for i := range envfileslice { - efs, _ := script.Echo(envfileslice[i]).Reject("##").Reject("#-").Reject("# ").Replace("#", "#$").String() - if efs != "" && efs != "\n" { - envfileslice[i] = strings.ReplaceAll(efs, "\n", "") - } + printEnvs(srvenvfileLinux) + } + if len(localPort) != len(dmsgPort) || len(localPort) != len(rawTCP) { + dLog.Fatal("The number of local ports, DMSG ports, and raw TCP flags must be the same") + } + pk, err = sk.PubKey() + if err != nil { + pk, sk = cipher.GenerateKeyPair() + } + dLog.Infof("DMSG client public key: %v", pk.String()) + + if len(wl) > 0 { + for _, key := range wl { + var pk cipher.PubKey + if err := pk.Set(key); err == nil { + wlkeys = append(wlkeys, pk) } - envfile = strings.Join(envfileslice, "\n") } - fmt.Println(envfile) - os.Exit(0) + dLog.Infof("%d keys whitelisted", len(wlkeys)) } + if proxyAddr != "" { + var err error + dialer, err = proxy.SOCKS5("tcp", proxyAddr, nil, proxy.Direct) + if err != nil { + dLog.Fatalf("Error creating SOCKS5 dialer: %v", err) + } + httpClient = &http.Client{Transport: &http.Transport{Dial: dialer.Dial}} + } + }, + Run: func(_ *cobra.Command, _ []string) { server() }, } func server() { - log := logging.MustGetLogger("dmsgwebsrv") - - if len(localPort) != len(dmsgPort) || len(localPort) != len(rawTCP) { - log.Fatal("The number of local ports, DMSG ports, and raw TCP flags must be the same") - } - ctx, cancel := cmdutil.SignalContext(context.Background(), log) + ctx, cancel := cmdutil.SignalContext(context.Background(), dLog) defer cancel() - pk, err := sk.PubKey() - if err != nil { - pk, sk = cipher.GenerateKeyPair() - } - log.Infof("DMSG client public key: %v", pk.String()) - - if len(wl) > 0 { - for _, key := range wl { - var pk cipher.PubKey - if err := pk.Set(key); err == nil { - wlkeys = append(wlkeys, pk) - } - } - log.Infof("%d keys whitelisted", len(wlkeys)) - } - - if proxyAddr != "" { - var err error - dialer, err = proxy.SOCKS5("tcp", proxyAddr, nil, proxy.Direct) - if err != nil { - log.Fatalf("Error creating SOCKS5 dialer: %v", err) - } - httpClient = &http.Client{Transport: &http.Transport{Dial: dialer.Dial}} - } - - dmsgClient := dmsg.NewClient(pk, sk, disc.NewHTTP(dmsgDisc, &http.Client{}, log), dmsg.DefaultConfig()) + dmsgClient := dmsg.NewClient(pk, sk, disc.NewHTTP(dmsgDisc, &http.Client{}, dLog), dmsg.DefaultConfig()) defer func() { if err := dmsgClient.Close(); err != nil { - log.WithError(err).Error() + dLog.WithError(err).Error() } }() - go dmsgClient.Serve(context.Background()) + go dmsgClient.Serve(ctx) select { case <-ctx.Done(): - log.WithError(ctx.Err()).Warn() + dLog.WithError(ctx.Err()).Warn() return case <-dmsgClient.Ready(): } @@ -135,22 +125,24 @@ func server() { for i := range localPort { lis, err := dmsgClient.Listen(uint16(dmsgPort[i])) if err != nil { - log.Fatalf("Error listening on DMSG port %d: %v", dmsgPort[i], err) + dLog.Fatalf("Error listening on DMSG port %d: %v", dmsgPort[i], err) } wg.Add(1) - go func(localPort uint, rawTCP bool, listener net.Listener) { + go func(ctx context.Context, localPort uint, rawTCP bool, listener net.Listener) { defer wg.Done() + defer listener.Close() + if rawTCP { - proxyTCPConnections(localPort, listener, log) + proxyTCPConnections(ctx, localPort, listener) } else { - proxyHTTPConnections(localPort, listener, log) + proxyHTTPConnections(ctx, localPort, listener) } - }(localPort[i], rawTCP[i], lis) + }(ctx, localPort[i], rawTCP[i], lis) } wg.Wait() } -func proxyHTTPConnections(localPort uint, listener net.Listener, log *logging.Logger) { +func proxyHTTPConnections(ctx context.Context, localPort uint, listener net.Listener) { router := gin.New() router.Use(gin.Recovery()) router.Use(loggingMiddleware()) @@ -169,32 +161,77 @@ func proxyHTTPConnections(localPort uint, listener net.Listener, log *logging.Lo }) server := &http.Server{Handler: router} + + // Graceful shutdown on context cancellation + go func() { + <-ctx.Done() + if err := server.Shutdown(context.Background()); err != nil { + dLog.Errorf("HTTP server shutdown error: %v", err) + } + }() + if err := server.Serve(listener); err != nil && err != http.ErrServerClosed { - log.Fatalf("HTTP server error: %v", err) + dLog.Fatalf("HTTP server error: %v", err) } } -func proxyTCPConnections(localPort uint, listener net.Listener, log *logging.Logger) { - for { - conn, err := listener.Accept() - if err != nil { - log.Errorf("Error accepting connection: %v", err) - return - } - - go func(dmsgConn net.Conn) { - defer dmsgConn.Close() +func proxyTCPConnections(ctx context.Context, localPort uint, listener net.Listener) { + // To track active connections for cleanup + var connWg sync.WaitGroup + connChan := make(chan net.Conn) - localConn, err := net.Dial("tcp", fmt.Sprintf("127.0.0.1:%d", localPort)) + // Goroutine to accept new connections + go func() { + defer close(connChan) + for { + conn, err := listener.Accept() if err != nil { - log.Errorf("Error connecting to local port %d: %v", localPort, err) + select { + case <-ctx.Done(): + // Listener closed due to context cancellation + return + default: + dLog.Errorf("Error accepting connection: %v", err) + return + } + } + connChan <- conn + } + }() + + // Goroutine to handle active connections + for { + select { + case <-ctx.Done(): + // Context canceled: stop accepting new connections and clean up + dLog.Info("Shutting down TCP proxy connections...") + listener.Close() // Close the listener to stop new connections + connWg.Wait() // Wait for all active connections to finish + return + case conn, ok := <-connChan: + if !ok { + // connChan closed, exit the loop return } - defer localConn.Close() - go io.Copy(dmsgConn, localConn) - io.Copy(localConn, dmsgConn) - }(conn) + // Handle each connection + connWg.Add(1) + go func(dmsgConn net.Conn) { + defer connWg.Done() + defer dmsgConn.Close() + + localConn, err := net.Dial("tcp", fmt.Sprintf("127.0.0.1:%d", localPort)) + if err != nil { + dLog.Errorf("Error connecting to local port %d: %v", localPort, err) + return + } + defer localConn.Close() + + // Start bidirectional copy + go io.Copy(dmsgConn, localConn) + io.Copy(localConn, dmsgConn) + }(conn) + } } } diff --git a/cmd/dmsgweb/commands/root.go b/cmd/dmsgweb/commands/root.go index 9ab16eac..cc3b3039 100644 --- a/cmd/dmsgweb/commands/root.go +++ b/cmd/dmsgweb/commands/root.go @@ -6,6 +6,7 @@ import ( "log" "net" "net/http" + "os" "runtime" "strconv" "strings" @@ -23,6 +24,7 @@ import ( ) var ( + dLog *logging.Logger httpC http.Client dmsgC *dmsg.Client closeDmsg func() @@ -86,6 +88,22 @@ func startDmsg(ctx context.Context, pk cipher.PubKey, sk cipher.SecKey, dmsgDisc } } */ + +func printEnvs(envfile string) { + if runtime.GOOS == "windows" { + envfileslice, _ := script.Echo(envfile).Slice() + for i := range envfileslice { + efs, _ := script.Echo(envfileslice[i]).Reject("##").Reject("#-").Reject("# ").Replace("#", "#$").String() + if efs != "" && efs != "\n" { + envfileslice[i] = strings.ReplaceAll(efs, "\n", "") + } + } + envfile = strings.Join(envfileslice, "\n") + } + fmt.Println(envfile) + os.Exit(0) +} + //TODO: these functions are more or less duplicated in several places - need to standardize and put in it's own library import in "github.com/skycoin/skywire/pkg/skywire-utilities/pkg/..." func scriptExecString(s, envfile string) string { diff --git a/examples/dmsghttp-client/dmsghttp-client.go b/examples/dmsghttp-client/dmsghttp-client.go new file mode 100644 index 00000000..fe198a4b --- /dev/null +++ b/examples/dmsghttp-client/dmsghttp-client.go @@ -0,0 +1,49 @@ +package main + +import ( + "context" + "fmt" + "io" + "net/http" + "net/url" + "os" + + "github.com/skycoin/dmsg/internal/cli" + "github.com/skycoin/dmsg/pkg/dmsg" + "github.com/skycoin/dmsg/pkg/dmsghttp" + "github.com/skycoin/skywire/pkg/skywire-utilities/pkg/cipher" + "github.com/skycoin/skywire/pkg/skywire-utilities/pkg/logging" +) + +func main() { + dLog := logging.MustGetLogger("dmsghttp-client") + dmsgDisc := dmsg.DiscAddr(false) + parsedURL, err := url.Parse(os.Args[1]) + if err != nil { + dLog.Fatalf("Failed to parse URL: %v", err) + } + pk, sk := cipher.GenerateKeyPair() + ctx := context.Background() + dmsgClient, closeDmsg, err := cli.StartDmsg(ctx, dLog, pk, sk, &http.Client{}, dmsgDisc, 1) + if err != nil { + dLog.Fatalf("Failed to start DMSG client: %v", err) + } + dLog.Println("started dmsg client") + defer closeDmsg() + if dmsgClient == nil { + dLog.Fatal("DMSG client initialization failed. Exiting.") + } + httpClient := &http.Client{ + Transport: dmsghttp.MakeHTTPTransport(ctx, dmsgClient), + } + resp, err := httpClient.Get(parsedURL.String()) + if err != nil { + dLog.Fatalf("Failed to perform GET request: %v", err) + } + defer resp.Body.Close() + body, err := io.ReadAll(resp.Body) + if err != nil { + dLog.Fatalf("Failed to read response body: %v", err) + } + fmt.Println(string(body)) +} diff --git a/examples/dmsghttp/README.md b/examples/dmsghttp/README.md new file mode 100644 index 00000000..06089b03 --- /dev/null +++ b/examples/dmsghttp/README.md @@ -0,0 +1,72 @@ +## example hello world via HTTP over DMSG + +### Generate keys: + +``` +go run ../gen-keys/gen-keys.go | tee dmsgtest.keys +``` +OR +``` +go run ../gen-keys/gen-keys.go > dmsgtest.keys +``` + + +### Start application using secret key + + +``` +$ go run dmsghttp.go -s $(tail -n1 dmsgtest.key) +[2025-01-28T15:45:26.218738525-06:00] DEBUG disc.NewHTTP [dmsghttp]: Created HTTP client. addr="http://dmsgd.skywire.skycoin.com" +[2025-01-28T15:45:26.218798474-06:00] DEBUG [dmsg_client]: Discovering dmsg servers... +[2025-01-28T15:45:26.494061772-06:00] DEBUG [dmsg_client]: Dialing session... remote_pk=0281a102c82820e811368c8d028cf11b1a985043b726b1bcdb8fce89b27384b2cb +[2025-01-28T15:45:27.047394122-06:00] DEBUG [dmsg_client]: Serving session. remote_pk=0281a102c82820e811368c8d028cf11b1a985043b726b1bcdb8fce89b27384b2cb +[2025-01-28T15:45:27.047406948-06:00] INFO [dmsghttp]: Serving Hello World on DMSG address 03f45df9890955214bbfe2e06487741489266a60c487365989b9723680b45e0f6e:80 +[2025-01-28T15:46:02.992699297-06:00] INFO [dmsghttp]: Received request from 0352b141c5a423a3788ad7202745b083e2240dea5ce304742a272ec4a80ece6c1c:49153 + +``` + +### Get using dmsgcurl + + +``` +$ go run ../../cmd/dmsgcurl/dmsgcurl.go -l debug dmsg://$(head -n1 dmsgtest.key):80 +[2025-01-28T15:46:00.785331607-06:00] DEBUG disc.NewHTTP [dmsgcurl]: Created HTTP client. addr="http://dmsgd.skywire.skycoin.com" +[2025-01-28T15:46:00.785375317-06:00] DEBUG [dmsgcurl]: Connecting to dmsg network... dmsg_disc="http://dmsgd.skywire.skycoin.com" public_key="0352b141c5a423a3788ad7202745b083e2240dea5ce304742a272ec4a80ece6c1c" +[2025-01-28T15:46:00.785475179-06:00] DEBUG [dmsg_client]: Discovering dmsg servers... +[2025-01-28T15:46:01.053319739-06:00] DEBUG [dmsg_client]: Dialing session... remote_pk=02a2d4c346dabd165fd555dfdba4a7f4d18786fe7e055e562397cd5102bdd7f8dd +[2025-01-28T15:46:01.611696253-06:00] DEBUG [dmsg_client]: Serving session. remote_pk=02a2d4c346dabd165fd555dfdba4a7f4d18786fe7e055e562397cd5102bdd7f8dd +[2025-01-28T15:46:01.611738628-06:00] DEBUG [dmsgcurl]: Dmsg network ready. +[2025-01-28T15:46:02.018101877-06:00] DEBUG [dmsg_client]: Dialing session... remote_pk=0281a102c82820e811368c8d028cf11b1a985043b726b1bcdb8fce89b27384b2cb +[2025-01-28T15:46:02.431323641-06:00] DEBUG [dmsg_client]: Updating entry. entry= version: 0.0.1 + sequence: 0 + registered at: 1738100761468088283 + static public key: 0352b141c5a423a3788ad7202745b083e2240dea5ce304742a272ec4a80ece6c1c + signature: 4a761f8e84f021683957f272832a007890de622414d155db49bf0cdac944477d6160f846e07f68d6d78c93209ebe593b3dc4c48d1ef729f7ee7c52fb79d295a200 + entry is registered as client. Related info: + delegated servers: + 02a2d4c346dabd165fd555dfdba4a7f4d18786fe7e055e562397cd5102bdd7f8dd + 0281a102c82820e811368c8d028cf11b1a985043b726b1bcdb8fce89b27384b2cb + + +[2025-01-28T15:46:02.569096852-06:00] DEBUG [dmsg_client]: Serving session. remote_pk=0281a102c82820e811368c8d028cf11b1a985043b726b1bcdb8fce89b27384b2cb +Hello, World![2025-01-28T15:46:03.127242853-06:00] DEBUG [dmsg_client]: Stopped serving client! +[2025-01-28T15:46:03.127263963-06:00] DEBUG [dmsg_client]: Stopped accepting streams. error="session shutdown" session=02a2d4c346dabd165fd555dfdba4a7f4d18786fe7e055e562397cd5102bdd7f8dd +[2025-01-28T15:46:03.12746417-06:00] DEBUG [dmsg_client]: Session closed. error= +[2025-01-28T15:46:03.127561533-06:00] DEBUG [dmsg_client]: Stopped accepting streams. error="session shutdown" session=0281a102c82820e811368c8d028cf11b1a985043b726b1bcdb8fce89b27384b2cb +[2025-01-28T15:46:03.127602412-06:00] DEBUG [dmsg_client]: Session closed. error= +[2025-01-28T15:46:03.127623574-06:00] DEBUG [dmsg_client]: All sessions closed. +[2025-01-28T15:46:03.395174115-06:00] DEBUG [dmsg_client]: Deleting entry. entry= version: 0.0.1 + sequence: 1 + registered at: 1738100762431422654 + static public key: 0352b141c5a423a3788ad7202745b083e2240dea5ce304742a272ec4a80ece6c1c + signature: 40a80e385094c38a420d9229d517172a3859f28b79057b5b79cf2600d3021f9d7af57fa26d0f5c9c0643fa45d07bad8169860b842851543d866455a8f170cf6701 + entry is registered as client. Related info: + delegated servers: + 02a2d4c346dabd165fd555dfdba4a7f4d18786fe7e055e562397cd5102bdd7f8dd + 0281a102c82820e811368c8d028cf11b1a985043b726b1bcdb8fce89b27384b2cb + + +[2025-01-28T15:46:03.533465754-06:00] DEBUG [dmsg_client]: Entry Deleted successfully. +[2025-01-28T15:46:03.533508976-06:00] DEBUG [dmsgcurl]: Disconnected from dmsg network. error= + +``` diff --git a/examples/dmsghttp/dmsghttp.go b/examples/dmsghttp/dmsghttp.go new file mode 100644 index 00000000..7e766fda --- /dev/null +++ b/examples/dmsghttp/dmsghttp.go @@ -0,0 +1,153 @@ +// Example Hello World HTTP over DMSG +package main + +import ( + "context" + "log" + "net/http" + "os" + "strings" + "time" + + "github.com/skycoin/skywire/pkg/skywire-utilities/pkg/cipher" + "github.com/skycoin/skywire/pkg/skywire-utilities/pkg/cmdutil" + "github.com/skycoin/skywire/pkg/skywire-utilities/pkg/logging" + cc "github.com/ivanpirog/coloredcobra" + "github.com/spf13/cobra" + + "github.com/skycoin/dmsg/pkg/disc" + dmsg "github.com/skycoin/dmsg/pkg/dmsg" +) + +var ( + sk cipher.SecKey + dmsgDisc string + dmsgPort uint +) + +func init() { + RootCmd.Flags().UintVarP(&dmsgPort, "port", "p", 80, "DMSG port to serve from") + RootCmd.Flags().StringVarP(&dmsgDisc, "dmsg-disc", "D", dmsg.DiscAddr(false), "DMSG discovery URL") + if os.Getenv("DMSGHTTP_SK") != "" { + sk.Set(os.Getenv("DMSGHTTP_SK")) //nolint + } + RootCmd.Flags().VarP(&sk, "sk", "s", "A random key is generated if unspecified\n\r") +} + +// RootCmd contains the root DMSG HTTP command +var RootCmd = &cobra.Command{ + Use: func() string { + return strings.Split(os.Args[0], " ")[0] + }(), + Short: "DMSG HTTP Hello World server", + Long: "DMSG HTTP Hello World server", + SilenceErrors: true, + SilenceUsage: true, + DisableSuggestions: true, + DisableFlagsInUseLine: true, + Run: func(_ *cobra.Command, _ []string) { + log := logging.MustGetLogger("dmsghttp") + if dmsgDisc == "" { + log.Fatal("DMSG discovery URL not specified") + } + + ctx, cancel := cmdutil.SignalContext(context.Background(), log) + defer cancel() + + // Generate keys if not provided + pk, err := sk.PubKey() + if err != nil { + pk, sk = cipher.GenerateKeyPair() + } + + // Initialize the DMSG client + c := dmsg.NewClient(pk, sk, disc.NewHTTP(dmsgDisc, &http.Client{}, log), dmsg.DefaultConfig()) + defer func() { + if err := c.Close(); err != nil { + log.WithError(err).Error("Failed to close DMSG client") + } + }() + go c.Serve(context.Background()) + + // Wait for the DMSG client to be ready + select { + case <-ctx.Done(): + log.WithError(ctx.Err()).Warn() + return + case <-c.Ready(): + } + + // Listen on the specified DMSG port + lis, err := c.Listen(uint16(dmsgPort)) + if err != nil { + log.WithError(err).Fatal("Failed to listen on DMSG port") + } + defer lis.Close() + + log.Infof("Serving Hello World on DMSG address %s", lis.Addr()) + + // Set up HTTP server to respond with "Hello, World!" + http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + log.Infof("Received request from %s", r.RemoteAddr) + w.WriteHeader(http.StatusOK) + w.Write([]byte("Hello, World!")) + }) + + // Start the HTTP server + server := &http.Server{ + ReadHeaderTimeout: 3 * time.Second, + } + + // Graceful shutdown handler + go func() { + <-ctx.Done() + log.Info("Shutdown signal received, shutting down HTTP server...") + server.Shutdown(context.Background()) + log.Info("HTTP server successfully shut down") + }() + + // Start serving HTTP requests + log.Fatal(server.Serve(lis)) + }, +} + +// Execute executes the root CLI command +func Execute() { + if err := RootCmd.Execute(); err != nil { + log.Fatal("Failed to execute command: ", err) + } +} + +func init() { + var helpflag bool + RootCmd.SetUsageTemplate(help) + RootCmd.PersistentFlags().BoolVarP(&helpflag, "help", "h", false, "help for dmsghttp-cli") + RootCmd.SetHelpCommand(&cobra.Command{Hidden: true}) + RootCmd.PersistentFlags().MarkHidden("help") //nolint +} + +func main() { + cc.Init(&cc.Config{ + RootCmd: RootCmd, + Headings: cc.HiBlue + cc.Bold, + Commands: cc.HiBlue + cc.Bold, + CmdShortDescr: cc.HiBlue, + Example: cc.HiBlue + cc.Italic, + ExecName: cc.HiBlue + cc.Bold, + Flags: cc.HiBlue + cc.Bold, + FlagsDescr: cc.HiBlue, + NoExtraNewlines: true, + NoBottomNewline: true, + }) + Execute() +} + +const help = "Usage:\r\n" + + " {{.UseLine}}{{if .HasAvailableSubCommands}}{{end}} {{if gt (len .Aliases) 0}}\r\n\r\n" + + "{{.NameAndAliases}}{{end}}{{if .HasAvailableSubCommands}}\r\n\r\n" + + "Available Commands:{{range .Commands}}{{if (or .IsAvailableCommand)}}\r\n " + + "{{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}{{if .HasAvailableLocalFlags}}\r\n\r\n" + + "Flags:\r\n" + + "{{.LocalFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasAvailableInheritedFlags}}\r\n\r\n" + + "Global Flags:\r\n" + + "{{.InheritedFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}\r\n\r\n" diff --git a/examples/dmsgtcp/README.md b/examples/dmsgtcp/README.md new file mode 100644 index 00000000..44cf8d62 --- /dev/null +++ b/examples/dmsgtcp/README.md @@ -0,0 +1 @@ +example hello world via TCP over DMSG diff --git a/examples/dmsgtcp/dmsgtcp.go b/examples/dmsgtcp/dmsgtcp.go new file mode 100644 index 00000000..51f4b1b7 --- /dev/null +++ b/examples/dmsgtcp/dmsgtcp.go @@ -0,0 +1,166 @@ +// Example hello world TCP over DMSG +package main + +import ( + "context" + "log" + "net" + "net/http" + "os" + "os/signal" + "strings" + "syscall" + + "github.com/skycoin/skywire/pkg/skywire-utilities/pkg/cipher" + "github.com/skycoin/skywire/pkg/skywire-utilities/pkg/cmdutil" + "github.com/skycoin/skywire/pkg/skywire-utilities/pkg/logging" + + cc "github.com/ivanpirog/coloredcobra" + "github.com/spf13/cobra" + + "github.com/skycoin/dmsg/internal/cli" + dmsg "github.com/skycoin/dmsg/pkg/dmsg" +) + +var ( + sk cipher.SecKey + dmsgDisc string + dmsgPort uint +) + +func init() { + RootCmd.Flags().UintVarP(&dmsgPort, "port", "p", 80, "DMSG port to serve from") + RootCmd.Flags().StringVarP(&dmsgDisc, "dmsg-disc", "D", dmsg.DiscAddr(false), "DMSG discovery URL") + if os.Getenv("DMSGTCP_SK") != "" { + sk.Set(os.Getenv("DMSGTCP_SK")) //nolint + } + RootCmd.Flags().VarP(&sk, "sk", "s", "A random key is generated if unspecified\n\r") +} + +// RootCmd contains the root DMSG TCP command +var RootCmd = &cobra.Command{ + Use: func() string { + return strings.Split(os.Args[0], " ")[0] + }(), + Short: "DMSG TCP Hello World server", + Long: "DMSG TCP Hello World server", + Run: func(_ *cobra.Command, _ []string) { + log := logging.MustGetLogger("dmsgtcp") + if dmsgDisc == "" { + log.Fatal("DMSG discovery URL not specified") + } + + // Create the context and cancel function + ctx, cancel := cmdutil.SignalContext(context.Background(), log) + defer cancel() + + // Generate keys if not provided + pk, err := sk.PubKey() + if err != nil { + pk, sk = cipher.GenerateKeyPair() + } + + // Initialize the DMSG client + dmsgC, closeDmsg, err := cli.StartDmsg(ctx, log, pk, sk, &http.Client{}, dmsgDisc, 1) + if err != nil { + log.WithError(err).Fatal("failed to start dmsg") + } + defer closeDmsg() + + go func() { + <-ctx.Done() + cancel() + closeDmsg() + os.Exit(0) + }() + + // Listen on the specified DMSG port + lis, err := dmsgC.Listen(uint16(dmsgPort)) + if err != nil { + log.WithError(err).Fatal("Failed to listen on DMSG port") + } + defer lis.Close() + + log.Infof("Serving Hello World TCP on DMSG address %s", lis.Addr()) + + // Handle system interrupt (Ctrl + C) + signalChan := make(chan os.Signal, 1) + signal.Notify(signalChan, syscall.SIGINT, syscall.SIGTERM) + + // Start a goroutine to wait for shutdown signals + go func() { + <-signalChan + log.Info("Received shutdown signal.") + lis.Close() + closeDmsg() + cancel() // Cancel context to terminate DMSG client and server + }() + + // Accept TCP connections and respond with "Hello, World!" + for { + conn, err := lis.Accept() + if err != nil { + // If the server was closed or a signal was received, break out + if ctx.Err() != nil { + log.Info("Server shutting down...") + return + } + log.WithError(err).Error("Failed to accept connection") + continue + } + go handleConnection(conn, log) + } + }, +} + +func handleConnection(conn net.Conn, log *logging.Logger) { + defer conn.Close() + log.Infof("Received connection from %s", conn.RemoteAddr()) + + // Write "Hello, World!" message to the connection + _, err := conn.Write([]byte("Hello, World!\n")) + if err != nil { + log.WithError(err).Error("Failed to write response") + } +} + +// Execute executes the root CLI command +func Execute() { + if err := RootCmd.Execute(); err != nil { + log.Fatal("Failed to execute command: ", err) + } +} + +func init() { + var helpflag bool + RootCmd.SetUsageTemplate(help) + RootCmd.PersistentFlags().BoolVarP(&helpflag, "help", "h", false, "help for dmsgpty-cli") + RootCmd.SetHelpCommand(&cobra.Command{Hidden: true}) + RootCmd.PersistentFlags().MarkHidden("help") //nolint +} + +func main() { + cc.Init(&cc.Config{ + RootCmd: RootCmd, + Headings: cc.HiBlue + cc.Bold, + Commands: cc.HiBlue + cc.Bold, + CmdShortDescr: cc.HiBlue, + Example: cc.HiBlue + cc.Italic, + ExecName: cc.HiBlue + cc.Bold, + Flags: cc.HiBlue + cc.Bold, + FlagsDescr: cc.HiBlue, + NoExtraNewlines: true, + NoBottomNewline: true, + }) + Execute() +} + +const help = "Usage:\r\n" + + " {{.UseLine}}{{if .HasAvailableSubCommands}}{{end}} {{if gt (len .Aliases) 0}}\r\n\r\n" + + "{{.NameAndAliases}}{{end}}{{if .HasAvailableSubCommands}}\r\n\r\n" + + "Available Commands:{{range .Commands}}{{if (or .IsAvailableCommand)}}\r\n " + + "{{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}{{if .HasAvailableLocalFlags}}\r\n\r\n" + + "Flags:\r\n" + + "{{.LocalFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasAvailableInheritedFlags}}\r\n\r\n" + + "Global Flags:\r\n" + + "{{.InheritedFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}\r\n\r\n" diff --git a/examples/dmsgweb/commands/dmsgweb.go b/examples/dmsgweb/commands/dmsgweb.go index ef6b325c..03e2a2e9 100644 --- a/examples/dmsgweb/commands/dmsgweb.go +++ b/examples/dmsgweb/commands/dmsgweb.go @@ -16,7 +16,7 @@ import ( "github.com/confiant-inc/go-socks5" "github.com/gin-gonic/gin" - "github.com/skycoin/skywire/pkg/skywire-utilities/pkg/buildinfo" + "github.com/skycoin/skywire/pkg/skywire-utilities/pkg/buildinfo" "github.com/skycoin/skywire/pkg/skywire-utilities/pkg/cipher" "github.com/skycoin/skywire/pkg/skywire-utilities/pkg/cmdutil" "github.com/skycoin/skywire/pkg/skywire-utilities/pkg/logging" @@ -26,7 +26,6 @@ import ( "github.com/skycoin/dmsg/pkg/dmsghttp" ) - func init() { RootCmd.AddCommand(genKeysCmd) RootCmd.Flags().StringVarP(&filterDomainSuffix, "filter", "f", ".dmsg", "domain suffix to filter") @@ -34,7 +33,7 @@ func init() { RootCmd.Flags().StringVarP(&addProxy, "proxy", "r", "", "configure additional socks5 proxy for dmsgweb (i.e. 127.0.0.1:1080)") RootCmd.Flags().StringVarP(&webPort, "port", "p", "8080", "port to serve the web application") RootCmd.Flags().StringVarP(&resolveDmsgAddr, "resolve", "t", "", "resolve the specified dmsg address:port on the local port & disable proxy") - RootCmd.Flags().StringVarP(&dmsgDisc, "dmsg-disc", "D", dmsgDisc, "dmsg discovery url") + RootCmd.Flags().StringVarP(&dmsgDisc, "dmsg-disc", "D", dmsgDisc, "dmsg discovery url") RootCmd.Flags().IntVarP(&dmsgSessions, "sess", "e", 1, "number of dmsg servers to connect to") RootCmd.Flags().StringVarP(&logLvl, "loglvl", "l", "", "[ debug | warn | error | fatal | panic | trace | info ]\033[0m") if os.Getenv("DMSGGET_SK") != "" { diff --git a/examples/dmsgweb/commands/root.go b/examples/dmsgweb/commands/root.go index 2420c088..894fe977 100644 --- a/examples/dmsgweb/commands/root.go +++ b/examples/dmsgweb/commands/root.go @@ -7,10 +7,10 @@ import ( "log" "net" "net/http" + "os" "regexp" "sync" "time" - "os" "github.com/gin-gonic/gin" "github.com/skycoin/skywire/pkg/skywire-utilities/pkg/cipher" diff --git a/examples/gen-keys/README.md b/examples/gen-keys/README.md new file mode 100644 index 00000000..2c6c673e --- /dev/null +++ b/examples/gen-keys/README.md @@ -0,0 +1,11 @@ +## public / secret key pair generation + +Returns: +* Public Key +* Secret Key + +``` +$ go run gen-keys.go +03620454a0ba368051defa4baac2fd92a434449292d2dd2aff8b81041403a9d3d1 +54ce2ff7310f8ab33e01d9b0f5be7f79a62ba92d812a113b1a8a993c95f162ac +``` diff --git a/examples/gen-keys/gen-keys.go b/examples/gen-keys/gen-keys.go new file mode 100644 index 00000000..5ba6e920 --- /dev/null +++ b/examples/gen-keys/gen-keys.go @@ -0,0 +1,12 @@ +// example keypair generation +package main + +import ( + "fmt" + "github.com/skycoin/skywire/pkg/skywire-utilities/pkg/cipher" +) + +func main() { + pk, sk := cipher.GenerateKeyPair() + fmt.Printf("%s\n%s\n", pk, sk) +} diff --git a/examples/http/README.md b/examples/http/README.md new file mode 100644 index 00000000..7e79b8ee --- /dev/null +++ b/examples/http/README.md @@ -0,0 +1 @@ +example hello world via HTTP diff --git a/examples/http/http.go b/examples/http/http.go index 2f9750f1..90cc98d5 100644 --- a/examples/http/http.go +++ b/examples/http/http.go @@ -1,3 +1,4 @@ +// example hello world HTTP package main import ( diff --git a/examples/tcp/README.md b/examples/tcp/README.md new file mode 100644 index 00000000..e7f52381 --- /dev/null +++ b/examples/tcp/README.md @@ -0,0 +1 @@ +example hello world via TCP diff --git a/examples/tcp/tcp.go b/examples/tcp/tcp.go index a2d38f9a..c9569f1c 100644 --- a/examples/tcp/tcp.go +++ b/examples/tcp/tcp.go @@ -1,3 +1,4 @@ +// example hello world TCP package main import ( From 0e09fd7892024350cb4f806650abb65fa2332826 Mon Sep 17 00:00:00 2001 From: Moses Narrow Date: Thu, 30 Jan 2025 16:50:20 -0600 Subject: [PATCH 15/21] fix multiple concurrent tcp connections for dmsgweb --- cmd/dmsgweb/commands/dmsgweb.go | 168 ++++++++++---------- cmd/dmsgweb/commands/dmsgwebsrv.go | 45 +++++- examples/dmsghttp-client/dmsghttp-client.go | 5 +- examples/dmsghttp/dmsghttp.go | 6 +- examples/dmsgtcp/dmsgtcp.go | 5 +- examples/gen-keys/gen-keys.go | 1 + 6 files changed, 134 insertions(+), 96 deletions(-) diff --git a/cmd/dmsgweb/commands/dmsgweb.go b/cmd/dmsgweb/commands/dmsgweb.go index 5c4fde6b..60f456a7 100644 --- a/cmd/dmsgweb/commands/dmsgweb.go +++ b/cmd/dmsgweb/commands/dmsgweb.go @@ -13,6 +13,7 @@ import ( "regexp" "strconv" "strings" + "sync" "syscall" "github.com/chen3feng/safecast" @@ -51,7 +52,6 @@ const dwenv = "DMSGWEB" var dwcfg = os.Getenv(dwenv) func init() { - dLog = logging.MustGetLogger("dmsgweb") dmsgDisc = dmsg.DiscAddr(false) webPort = scriptExecUintSlice("${WEBPORT[@]:-8080}", dwcfg) proxyPort = scriptExecUint("${PROXYPORT:-4445}", dwcfg) @@ -115,6 +115,7 @@ dmsgweb conf file detected: ` + dwcfg logging.SetLevel(lvl) } } + dLog = logging.MustGetLogger("dmsgweb") if dmsgDisc == "" { dLog.Fatal("Dmsg Discovery URL not specified") } @@ -161,7 +162,7 @@ dmsgweb conf file detected: ` + dwcfg signal.Notify(c, os.Interrupt, syscall.SIGTERM) //nolint go func() { <-c - os.Exit(1) + os.Exit(0) }() ctx, cancel := cmdutil.SignalContext(context.Background(), dLog) @@ -173,8 +174,8 @@ dmsgweb conf file detected: ` + dwcfg } dLog.Info("dmsg client pk: ", pk.String()) if len(resolveDmsgAddr) > 0 { - dialPK := make([]cipher.PubKey, len(resolveDmsgAddr)) - dmsgPorts := make([]uint, len(resolveDmsgAddr)) + dialPK = make([]cipher.PubKey, len(resolveDmsgAddr)) + dmsgPorts = make([]uint, len(resolveDmsgAddr)) for i, dmsgaddr := range resolveDmsgAddr { dLog.Info("dmsg address to dial: ", dmsgaddr) @@ -207,23 +208,23 @@ dmsgweb conf file detected: ` + dwcfg } } -/* - if proxyAddr != "" { - // Use SOCKS5 proxy dialer if specified - dialer, err = proxy.SOCKS5("tcp", proxyAddr, nil, proxy.Direct) - if err != nil { - dLog.WithError(err).Fatal("Error creating SOCKS5 dialer") - } - transport := &http.Transport{ - Dial: dialer.Dial, - } - httpClient = &http.Client{ - Transport: transport, + /* + if proxyAddr != "" { + // Use SOCKS5 proxy dialer if specified + dialer, err = proxy.SOCKS5("tcp", proxyAddr, nil, proxy.Direct) + if err != nil { + dLog.WithError(err).Fatal("Error creating SOCKS5 dialer") + } + transport := &http.Transport{ + Dial: dialer.Dial, + } + httpClient = &http.Client{ + Transport: transport, + } + ctx = context.WithValue(context.Background(), "socks5_proxy", proxyAddr) //nolint } - ctx = context.WithValue(context.Background(), "socks5_proxy", proxyAddr) //nolint - } -*/ -// dmsgC, closeDmsg, err := cli.StartDmsg(ctx, dLog, pk, sk, &httpC, dmsgDisc, dmsgSessions) + */ + // dmsgC, closeDmsg, err := cli.StartDmsg(ctx, dLog, pk, sk, &httpC, dmsgDisc, dmsgSessions) dmsgC, closeDmsg, err = cli.StartDmsg(ctx, dLog, pk, sk, &http.Client{}, dmsgDisc, dmsgSessions) if err != nil { dLog.WithError(err).Fatal("failed to start dmsg") @@ -299,12 +300,13 @@ dmsgweb conf file detected: ` + dwcfg } } else { for i := range resolveDmsgAddr { + wg.Add(1) if rawTCP[i] { dLog.Debug("proxyTCPConn(" + fmt.Sprintf("%v", i) + ")") - proxyTCPConn(i) + go proxyTCPConn(i) } else { dLog.Debug("proxyHTTPConn(" + fmt.Sprintf("%v", i) + ")") - proxyHTTPConn(i) + go proxyHTTPConn(i) } } } @@ -312,6 +314,69 @@ dmsgweb conf file detected: ` + dwcfg }, } +func proxyTCPConn(n int) { + var thiswebport uint + if n == -1 { + thiswebport = webPort[0] + } else { + thiswebport = webPort[n] + } + listener, err := net.Listen("tcp", fmt.Sprintf(":%v", thiswebport)) + if err != nil { + dLog.WithError(err).Fatal(fmt.Sprintf("Failed to start TCP listener on port: %v", thiswebport)) + } + defer listener.Close() //nolint + dLog.Debug("Serving TCP on 127.0.0.1:", thiswebport) + if dmsgC == nil { + dLog.Fatal("dmsgC is nil") + } + + for { + conn, err := listener.Accept() + if err != nil { + dLog.WithError(err).Warn("Failed to accept connection") + continue + } + + go func(conn net.Conn, n int, dmsgC *dmsg.Client) { + defer conn.Close() + dp, ok := safecast.To[uint16](dmsgPorts[n]) + if !ok { + dLog.Fatal("uint16 overflow when converting dmsg port") + } + dLog.Debug(fmt.Sprintf("Dialing %v:%v", dialPK[n].String(), dp)) + dmsgConn, err := dmsgC.DialStream(context.Background(), dmsg.Addr{PK: dialPK[n], Port: dp}) //nolint + if err != nil { + dLog.WithError(err).Warn(fmt.Sprintf("Failed to dial dmsg address %v port %v", dialPK[n].String(), dmsgPorts[n])) + return + } + + defer dmsgConn.Close() + + var wg sync.WaitGroup + wg.Add(2) + + go func() { + defer wg.Done() + _, err := io.Copy(dmsgConn, conn) + if err != nil { + dLog.WithError(err).Warn("Error on io.Copy(dmsgConn, conn)") + } + }() + + go func() { + defer wg.Done() + _, err := io.Copy(conn, dmsgConn) + if err != nil { + dLog.WithError(err).Warn("Error on io.Copy(conn, dmsgConn)") + } + }() + + wg.Wait() + }(conn, n, dmsgC) + } +} + func proxyHTTPConn(n int) { r := gin.New() @@ -388,65 +453,6 @@ func proxyHTTPConn(n int) { wg.Done() }() } -func proxyTCPConn(n int) { - var thiswebport uint - if n == -1 { - thiswebport = webPort[0] - } else { - thiswebport = webPort[n] - } - listener, err := net.Listen("tcp", fmt.Sprintf(":%v", thiswebport)) - if err != nil { - dLog.WithError(err).Fatal(fmt.Sprintf("Failed to start TCP listener on port: %v", thiswebport)) - } - defer listener.Close() //nolint - dLog.Debug("Serving TCP on 127.0.0.1:", thiswebport) - if dmsgC == nil { - dLog.Fatal("dmsgC is nil") - } - - for { - conn, err := listener.Accept() - if err != nil { - dLog.WithError(err).Warn("Failed to accept connection") - continue - } - - wg.Add(1) - go func(conn net.Conn, n int, dmsgC *dmsg.Client) { - defer wg.Done() - - dp, ok := safecast.To[uint16](dmsgPorts[n]) - if !ok { - dLog.Fatal("uint16 overflow when converting dmsg port") - } - dLog.Debug(fmt.Sprintf("Dialing dmsg address: %v ; port: %v", dialPK[n].String(), dp)) - dmsgConn, err := dmsgC.DialStream(context.Background(), dmsg.Addr{PK: dialPK[n], Port: dp}) //nolint - if err != nil { - dLog.WithError(err).Warn(fmt.Sprintf("Failed to dial dmsg address %v port %v", dialPK[n].String(), dmsgPorts[n])) - return - } - defer dmsgConn.Close() //nolint - - go func() { - defer dmsgConn.Close() - _, err := io.Copy(dmsgConn, conn) - if err != nil { - dLog.WithError(err).Warn("Error on io.Copy(dmsgConn, conn)") - } - }() - - go func() { - defer conn.Close() //nolint - _, err := io.Copy(conn, dmsgConn) - if err != nil { - dLog.WithError(err).Warn("Error on io.Copy(conn, dmsgConn)") - } - }() - }(conn, n, dmsgC) - wg.Wait() - } -} const envfileLinux = ` ######################################################################### diff --git a/cmd/dmsgweb/commands/dmsgwebsrv.go b/cmd/dmsgweb/commands/dmsgwebsrv.go index 7083ea76..c3a4ca13 100644 --- a/cmd/dmsgweb/commands/dmsgwebsrv.go +++ b/cmd/dmsgweb/commands/dmsgwebsrv.go @@ -28,7 +28,6 @@ const dwsenv = "DMSGWEBSRV" var dwscfg = os.Getenv(dwsenv) func init() { - dLog = logging.MustGetLogger("dmsgwebsrv") dmsgPort = scriptExecUintSlice("${DMSGPORT[@]:-80}", dwscfg) dmsgSess = scriptExecInt("${DMSGSESSIONS:-1}", dwscfg) wl = scriptExecStringSlice("${WHITELISTPKS[@]}", dwscfg) @@ -43,13 +42,14 @@ func init() { pk, _ = sk.PubKey() RootCmd.AddCommand(srvCmd) - srvCmd.Flags().UintSliceVarP(&localPort, "lport", "l", localPort, "local application HTTP interface port(s)") + srvCmd.Flags().UintSliceVarP(&localPort, "lport", "p", localPort, "local application interface port(s)") srvCmd.Flags().UintSliceVarP(&dmsgPort, "dport", "d", dmsgPort, "DMSG port(s) to serve") srvCmd.Flags().StringSliceVarP(&wl, "wl", "w", wl, "whitelisted keys for DMSG authenticated routes") - srvCmd.Flags().StringVarP(&dmsgDisc, "dmsg-disc", "D", dmsgDisc, "DMSG discovery URL(s)") + srvCmd.Flags().StringVarP(&dmsgDisc, "dmsg-disc", "D", dmsgDisc, "DMSG discovery URL") srvCmd.Flags().StringVarP(&proxyAddr, "proxy", "x", proxyAddr, "connect to DMSG via proxy (e.g., '127.0.0.1:1080')") srvCmd.Flags().IntVarP(&dmsgSess, "dsess", "e", dmsgSess, "DMSG sessions") - srvCmd.Flags().BoolSliceVarP(&rawTCP, "rt", "c", rawTCP, "proxy local port as raw TCP") + srvCmd.Flags().BoolSliceVarP(&rawTCP, "rt", "c", rawTCP, "proxy local port as raw TCP, comma separated") + srvCmd.Flags().StringVarP(&logLvl, "loglvl", "l", "", "[ debug | warn | error | fatal | panic | trace | info ]\033[0m") srvCmd.Flags().BoolVarP(&isEnvs, "envs", "z", false, "show example .conf file") srvCmd.Flags().VarP(&sk, "sk", "s", "a random key is generated if unspecified") srvCmd.CompletionOptions.DisableDefaultCmd = true @@ -68,6 +68,12 @@ var srvCmd = &cobra.Command{ if isEnvs { printEnvs(srvenvfileLinux) } + if logLvl != "" { + if lvl, err := logging.LevelFromString(logLvl); err == nil { + logging.SetLevel(lvl) + } + } + dLog = logging.MustGetLogger("dmsgwebsrv") if len(localPort) != len(dmsgPort) || len(localPort) != len(rawTCP) { dLog.Fatal("The number of local ports, DMSG ports, and raw TCP flags must be the same") } @@ -75,7 +81,7 @@ var srvCmd = &cobra.Command{ if err != nil { pk, sk = cipher.GenerateKeyPair() } - dLog.Infof("DMSG client public key: %v", pk.String()) + dLog.Debugf("DMSG client public key: %v", pk.String()) if len(wl) > 0 { for _, key := range wl { @@ -179,6 +185,8 @@ func proxyTCPConnections(ctx context.Context, localPort uint, listener net.Liste // To track active connections for cleanup var connWg sync.WaitGroup connChan := make(chan net.Conn) + activeConns := make(map[net.Conn]struct{}) + connMutex := &sync.Mutex{} // Protect access to activeConns // Goroutine to accept new connections go func() { @@ -206,15 +214,28 @@ func proxyTCPConnections(ctx context.Context, localPort uint, listener net.Liste // Context canceled: stop accepting new connections and clean up dLog.Info("Shutting down TCP proxy connections...") listener.Close() // Close the listener to stop new connections - connWg.Wait() // Wait for all active connections to finish + + // Close all active connections + connMutex.Lock() + for conn := range activeConns { + conn.Close() // Forcefully close connections to unblock io.Copy() + } + connMutex.Unlock() + + connWg.Wait() // Now it should not hang because io.Copy() is unblocked return + case conn, ok := <-connChan: if !ok { // connChan closed, exit the loop return } - // Handle each connection + // Track the connection + connMutex.Lock() + activeConns[conn] = struct{}{} + connMutex.Unlock() + connWg.Add(1) go func(dmsgConn net.Conn) { defer connWg.Done() @@ -223,6 +244,11 @@ func proxyTCPConnections(ctx context.Context, localPort uint, listener net.Liste localConn, err := net.Dial("tcp", fmt.Sprintf("127.0.0.1:%d", localPort)) if err != nil { dLog.Errorf("Error connecting to local port %d: %v", localPort, err) + + connMutex.Lock() + delete(activeConns, dmsgConn) // Remove from tracking + connMutex.Unlock() + return } defer localConn.Close() @@ -230,6 +256,11 @@ func proxyTCPConnections(ctx context.Context, localPort uint, listener net.Liste // Start bidirectional copy go io.Copy(dmsgConn, localConn) io.Copy(localConn, dmsgConn) + + // Remove connection from tracking on completion + connMutex.Lock() + delete(activeConns, dmsgConn) + connMutex.Unlock() }(conn) } } diff --git a/examples/dmsghttp-client/dmsghttp-client.go b/examples/dmsghttp-client/dmsghttp-client.go index fe198a4b..05efc83b 100644 --- a/examples/dmsghttp-client/dmsghttp-client.go +++ b/examples/dmsghttp-client/dmsghttp-client.go @@ -8,11 +8,12 @@ import ( "net/url" "os" + "github.com/skycoin/skywire/pkg/skywire-utilities/pkg/cipher" + "github.com/skycoin/skywire/pkg/skywire-utilities/pkg/logging" + "github.com/skycoin/dmsg/internal/cli" "github.com/skycoin/dmsg/pkg/dmsg" "github.com/skycoin/dmsg/pkg/dmsghttp" - "github.com/skycoin/skywire/pkg/skywire-utilities/pkg/cipher" - "github.com/skycoin/skywire/pkg/skywire-utilities/pkg/logging" ) func main() { diff --git a/examples/dmsghttp/dmsghttp.go b/examples/dmsghttp/dmsghttp.go index 7e766fda..ec01e75c 100644 --- a/examples/dmsghttp/dmsghttp.go +++ b/examples/dmsghttp/dmsghttp.go @@ -9,10 +9,10 @@ import ( "strings" "time" + cc "github.com/ivanpirog/coloredcobra" "github.com/skycoin/skywire/pkg/skywire-utilities/pkg/cipher" "github.com/skycoin/skywire/pkg/skywire-utilities/pkg/cmdutil" "github.com/skycoin/skywire/pkg/skywire-utilities/pkg/logging" - cc "github.com/ivanpirog/coloredcobra" "github.com/spf13/cobra" "github.com/skycoin/dmsg/pkg/disc" @@ -39,8 +39,8 @@ var RootCmd = &cobra.Command{ Use: func() string { return strings.Split(os.Args[0], " ")[0] }(), - Short: "DMSG HTTP Hello World server", - Long: "DMSG HTTP Hello World server", + Short: "DMSG HTTP Hello World server", + Long: "DMSG HTTP Hello World server", SilenceErrors: true, SilenceUsage: true, DisableSuggestions: true, diff --git a/examples/dmsgtcp/dmsgtcp.go b/examples/dmsgtcp/dmsgtcp.go index 51f4b1b7..1a0592f2 100644 --- a/examples/dmsgtcp/dmsgtcp.go +++ b/examples/dmsgtcp/dmsgtcp.go @@ -11,11 +11,10 @@ import ( "strings" "syscall" + cc "github.com/ivanpirog/coloredcobra" "github.com/skycoin/skywire/pkg/skywire-utilities/pkg/cipher" "github.com/skycoin/skywire/pkg/skywire-utilities/pkg/cmdutil" "github.com/skycoin/skywire/pkg/skywire-utilities/pkg/logging" - - cc "github.com/ivanpirog/coloredcobra" "github.com/spf13/cobra" "github.com/skycoin/dmsg/internal/cli" @@ -93,7 +92,7 @@ var RootCmd = &cobra.Command{ log.Info("Received shutdown signal.") lis.Close() closeDmsg() - cancel() // Cancel context to terminate DMSG client and server + cancel() // Cancel context to terminate DMSG client and server }() // Accept TCP connections and respond with "Hello, World!" diff --git a/examples/gen-keys/gen-keys.go b/examples/gen-keys/gen-keys.go index 5ba6e920..cf7175af 100644 --- a/examples/gen-keys/gen-keys.go +++ b/examples/gen-keys/gen-keys.go @@ -3,6 +3,7 @@ package main import ( "fmt" + "github.com/skycoin/skywire/pkg/skywire-utilities/pkg/cipher" ) From b2a2459ecaf87f700c7b14ea0ef4e61680fa2c5e Mon Sep 17 00:00:00 2001 From: Moses Narrow Date: Thu, 30 Jan 2025 16:57:35 -0600 Subject: [PATCH 16/21] fix CI errors --- cmd/dmsgweb/commands/dmsgweb.go | 37 ++++++++++++++---------------- cmd/dmsgweb/commands/dmsgwebsrv.go | 25 ++++++++------------ cmd/dmsgweb/commands/root.go | 4 ++-- 3 files changed, 28 insertions(+), 38 deletions(-) diff --git a/cmd/dmsgweb/commands/dmsgweb.go b/cmd/dmsgweb/commands/dmsgweb.go index 60f456a7..f1c5c7c4 100644 --- a/cmd/dmsgweb/commands/dmsgweb.go +++ b/cmd/dmsgweb/commands/dmsgweb.go @@ -65,7 +65,7 @@ func init() { if scriptExecString("${DMSGWEBSK}", dwcfg) != "" { sk.Set(scriptExecString("${DMSGWEBSK}", dwcfg)) //nolint } - pk, _ = sk.PubKey() + pk, _ = sk.PubKey() //nolint RootCmd.Flags().StringVarP(&filterDomainSuffix, "filter", "f", ".dmsg", "domain suffix to filter") RootCmd.Flags().UintVarP(&proxyPort, "socks", "q", proxyPort, "port to serve the socks5 proxy") @@ -208,23 +208,20 @@ dmsgweb conf file detected: ` + dwcfg } } - /* - if proxyAddr != "" { - // Use SOCKS5 proxy dialer if specified - dialer, err = proxy.SOCKS5("tcp", proxyAddr, nil, proxy.Direct) - if err != nil { - dLog.WithError(err).Fatal("Error creating SOCKS5 dialer") - } - transport := &http.Transport{ - Dial: dialer.Dial, - } - httpClient = &http.Client{ - Transport: transport, - } - ctx = context.WithValue(context.Background(), "socks5_proxy", proxyAddr) //nolint + if proxyAddr != "" { + dialer, err = proxy.SOCKS5("tcp", proxyAddr, nil, proxy.Direct) + if err != nil { + dLog.WithError(err).Fatal("Error creating SOCKS5 dialer") } - */ - // dmsgC, closeDmsg, err := cli.StartDmsg(ctx, dLog, pk, sk, &httpC, dmsgDisc, dmsgSessions) + transport := &http.Transport{ + Dial: dialer.Dial, + } + httpClient = &http.Client{ + Transport: transport, + } + ctx = context.WithValue(context.Background(), "socks5_proxy", proxyAddr) //nolint + } + dmsgC, closeDmsg, err = cli.StartDmsg(ctx, dLog, pk, sk, &http.Client{}, dmsgDisc, dmsgSessions) if err != nil { dLog.WithError(err).Fatal("failed to start dmsg") @@ -285,7 +282,7 @@ dmsgweb conf file detected: ` + dwcfg if err != nil { dLog.WithError(err).Fatal("Failed to start SOCKS5 server") } - defer server.Close() + defer server.Close() //nolint dLog.Debug("Stopped serving SOCKS5 proxy on " + socksAddr) }() } @@ -339,7 +336,7 @@ func proxyTCPConn(n int) { } go func(conn net.Conn, n int, dmsgC *dmsg.Client) { - defer conn.Close() + defer conn.Close() //nolint dp, ok := safecast.To[uint16](dmsgPorts[n]) if !ok { dLog.Fatal("uint16 overflow when converting dmsg port") @@ -351,7 +348,7 @@ func proxyTCPConn(n int) { return } - defer dmsgConn.Close() + defer dmsgConn.Close() //nolint var wg sync.WaitGroup wg.Add(2) diff --git a/cmd/dmsgweb/commands/dmsgwebsrv.go b/cmd/dmsgweb/commands/dmsgwebsrv.go index c3a4ca13..c1ef01ae 100644 --- a/cmd/dmsgweb/commands/dmsgwebsrv.go +++ b/cmd/dmsgweb/commands/dmsgwebsrv.go @@ -34,10 +34,10 @@ func init() { localPort = scriptExecUintSlice("${LOCALPORT[@]:-8086}", dwscfg) rawTCP = scriptExecBoolSlice("${RAWTCP[@]:-false}", dwscfg) if os.Getenv("DMSGWEBSRVSK") != "" { - sk.Set(os.Getenv("DMSGWEBSRVSK")) + sk.Set(os.Getenv("DMSGWEBSRVSK")) //nolint } if scriptExecString("${DMSGWEBSRVSK}", dwscfg) != "" { - sk.Set(scriptExecString("${DMSGWEBSRVSK}", dwscfg)) + sk.Set(scriptExecString("${DMSGWEBSRVSK}", dwscfg)) //nolint } pk, _ = sk.PubKey() @@ -136,7 +136,7 @@ func server() { wg.Add(1) go func(ctx context.Context, localPort uint, rawTCP bool, listener net.Listener) { defer wg.Done() - defer listener.Close() + defer listener.Close() //nolint if rawTCP { proxyTCPConnections(ctx, localPort, listener) @@ -160,7 +160,7 @@ func proxyHTTPConnections(ctx context.Context, localPort uint, listener net.List authRoute.Any("/*path", func(c *gin.Context) { targetURL := fmt.Sprintf("http://127.0.0.1:%d%s?%s", localPort, c.Request.URL.Path, c.Request.URL.RawQuery) proxy := httputil.ReverseProxy{Director: func(req *http.Request) { - req.URL, _ = url.Parse(targetURL) + req.URL, _ = url.Parse(targetURL) //nolint req.Host = req.URL.Host }} proxy.ServeHTTP(c.Writer, c.Request) @@ -207,31 +207,26 @@ func proxyTCPConnections(ctx context.Context, localPort uint, listener net.Liste } }() - // Goroutine to handle active connections for { select { case <-ctx.Done(): - // Context canceled: stop accepting new connections and clean up dLog.Info("Shutting down TCP proxy connections...") - listener.Close() // Close the listener to stop new connections + listener.Close() //nolint - // Close all active connections connMutex.Lock() for conn := range activeConns { - conn.Close() // Forcefully close connections to unblock io.Copy() + conn.Close() //nolint } connMutex.Unlock() - connWg.Wait() // Now it should not hang because io.Copy() is unblocked + connWg.Wait() return case conn, ok := <-connChan: if !ok { - // connChan closed, exit the loop return } - // Track the connection connMutex.Lock() activeConns[conn] = struct{}{} connMutex.Unlock() @@ -246,18 +241,16 @@ func proxyTCPConnections(ctx context.Context, localPort uint, listener net.Liste dLog.Errorf("Error connecting to local port %d: %v", localPort, err) connMutex.Lock() - delete(activeConns, dmsgConn) // Remove from tracking + delete(activeConns, dmsgConn) connMutex.Unlock() return } - defer localConn.Close() + defer localConn.Close() //nolint - // Start bidirectional copy go io.Copy(dmsgConn, localConn) io.Copy(localConn, dmsgConn) - // Remove connection from tracking on completion connMutex.Lock() delete(activeConns, dmsgConn) connMutex.Unlock() diff --git a/cmd/dmsgweb/commands/root.go b/cmd/dmsgweb/commands/root.go index cc3b3039..0f8b6c32 100644 --- a/cmd/dmsgweb/commands/root.go +++ b/cmd/dmsgweb/commands/root.go @@ -91,9 +91,9 @@ func startDmsg(ctx context.Context, pk cipher.PubKey, sk cipher.SecKey, dmsgDisc func printEnvs(envfile string) { if runtime.GOOS == "windows" { - envfileslice, _ := script.Echo(envfile).Slice() + envfileslice, _ := script.Echo(envfile).Slice() //nolint for i := range envfileslice { - efs, _ := script.Echo(envfileslice[i]).Reject("##").Reject("#-").Reject("# ").Replace("#", "#$").String() + efs, _ := script.Echo(envfileslice[i]).Reject("##").Reject("#-").Reject("# ").Replace("#", "#$").String() //nolint if efs != "" && efs != "\n" { envfileslice[i] = strings.ReplaceAll(efs, "\n", "") } From 4af48921d33f5593cfa25b87b7d6e8223cbb217f Mon Sep 17 00:00:00 2001 From: Moses Narrow Date: Thu, 30 Jan 2025 17:04:55 -0600 Subject: [PATCH 17/21] fix CI errors --- cmd/dmsgweb/commands/dmsgwebsrv.go | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/cmd/dmsgweb/commands/dmsgwebsrv.go b/cmd/dmsgweb/commands/dmsgwebsrv.go index c1ef01ae..5d869d5e 100644 --- a/cmd/dmsgweb/commands/dmsgwebsrv.go +++ b/cmd/dmsgweb/commands/dmsgwebsrv.go @@ -11,6 +11,7 @@ import ( "net/url" "os" "sync" + "time" "github.com/gin-gonic/gin" "github.com/skycoin/skywire/pkg/skywire-utilities/pkg/cipher" @@ -39,7 +40,7 @@ func init() { if scriptExecString("${DMSGWEBSRVSK}", dwscfg) != "" { sk.Set(scriptExecString("${DMSGWEBSRVSK}", dwscfg)) //nolint } - pk, _ = sk.PubKey() + pk, _ = sk.PubKey() //nolint RootCmd.AddCommand(srvCmd) srvCmd.Flags().UintSliceVarP(&localPort, "lport", "p", localPort, "local application interface port(s)") @@ -129,7 +130,7 @@ func server() { wg := sync.WaitGroup{} for i := range localPort { - lis, err := dmsgClient.Listen(uint16(dmsgPort[i])) + lis, err := dmsgClient.Listen(uint16(dmsgPort[i])) //nolint if err != nil { dLog.Fatalf("Error listening on DMSG port %d: %v", dmsgPort[i], err) } @@ -166,7 +167,14 @@ func proxyHTTPConnections(ctx context.Context, localPort uint, listener net.List proxy.ServeHTTP(c.Writer, c.Request) }) - server := &http.Server{Handler: router} + server := &http.Server{ + Handler: router, + ReadHeaderTimeout: 5 * time.Second, + ReadTimeout: 10 * time.Second, + WriteTimeout: 10 * time.Second, + IdleTimeout: 60 * time.Second, + MaxHeaderBytes: 1 << 20, + } // Graceful shutdown on context cancellation go func() { @@ -234,7 +242,7 @@ func proxyTCPConnections(ctx context.Context, localPort uint, listener net.Liste connWg.Add(1) go func(dmsgConn net.Conn) { defer connWg.Done() - defer dmsgConn.Close() + defer dmsgConn.Close() //nolint localConn, err := net.Dial("tcp", fmt.Sprintf("127.0.0.1:%d", localPort)) if err != nil { @@ -248,8 +256,16 @@ func proxyTCPConnections(ctx context.Context, localPort uint, listener net.Liste } defer localConn.Close() //nolint - go io.Copy(dmsgConn, localConn) - io.Copy(localConn, dmsgConn) + go func() { + _, err := io.Copy(dmsgConn, localConn) + if err != nil { + dLog.WithError(err).Warn("Error on io.Copy(dmsgConn, localConn)") + } + }() + _, err := io.Copy(localConn, dmsgConn) + if err != nil { + dLog.WithError(err).Warn("Error on io.Copy(localConn, dmsgConn)") + } connMutex.Lock() delete(activeConns, dmsgConn) From eae5c78511b98f04dccc7a646f730be031f661c9 Mon Sep 17 00:00:00 2001 From: Moses Narrow Date: Thu, 30 Jan 2025 17:13:37 -0600 Subject: [PATCH 18/21] fix CI errors & improve herlp menu --- cmd/dmsgweb/README.md | 86 +++++++++++++++++++++++------- cmd/dmsgweb/commands/dmsgweb.go | 22 ++++---- cmd/dmsgweb/commands/dmsgwebsrv.go | 28 +++++----- 3 files changed, 92 insertions(+), 44 deletions(-) diff --git a/cmd/dmsgweb/README.md b/cmd/dmsgweb/README.md index ac418b38..c5fe77fa 100644 --- a/cmd/dmsgweb/README.md +++ b/cmd/dmsgweb/README.md @@ -2,31 +2,79 @@ ``` +$ go run cmd/dmsgweb/dmsgweb.go --help -┌┬┐┌┬┐┌─┐┌─┐┬ ┬┌─┐┌┐ - │││││└─┐│ ┬│││├┤ ├┴┐ -─┴┘┴ ┴└─┘└─┘└┴┘└─┘└─┘ -DMSG resolving proxy & browser client - access websites over dmsg + ┌┬┐┌┬┐┌─┐┌─┐┬ ┬┌─┐┌┐ + │││││└─┐│ ┬│││├┤ ├┴┐ + ─┴┘┴ ┴└─┘└─┘└┴┘└─┘└─┘ +DMSG resolving proxy & browser client - access websites and http interfaces over dmsg +.conf file may also be specified with +DMSGWEB=/path/to/dmsgweb.conf skywire dmsg web Usage: -web + dmsgweb Available Commands: -completion Generate the autocompletion script for the specified shell -gen-keys generate public / secret keypair + completion Generate the autocompletion script for the specified shell + srv Serve HTTP or raw TCP from local port over DMSG Flags: --d, --dmsg-disc string dmsg discovery url default: - http://dmsgd.skywire.skycoin.com --f, --filter string domain suffix to filter (default ".dmsg") --l, --loglvl string [ debug | warn | error | fatal | panic | trace | info ] --p, --port string port to serve the web application (default "8080") --r, --proxy string configure additional socks5 proxy for dmsgweb (i.e. 127.0.0.1:1080) --t, --resolve string resolve the specified dmsg address:port on the local port & disable proxy --e, --sess int number of dmsg servers to connect to (default 1) --s, --sk cipher.SecKey a random key is generated if unspecified -(default 0000000000000000000000000000000000000000000000000000000000000000) --q, --socks string port to serve the socks5 proxy (default "4445") --v, --version version for web + -r, --addproxy string configure additional socks5 proxy for dmsgweb (i.e. 127.0.0.1:1080) + + -D, --dmsg-disc string dmsg discovery url + (default "http://dmsgd.skywire.skycoin.com") + -z, --envs show example .conf file + + -f, --filter string domain suffix to filter + (default ".dmsg") + -l, --loglvl string [ debug | warn | error | fatal | panic | trace | info ] + + -p, --port uints port(s) to serve the web application + (default [8080]) + -x, --proxy string connect to dmsg via proxy (i.e. '127.0.0.1:1080') + + -t, --resolve strings resolve the specified dmsg address:port on the local port & disable proxy + + -c, --rt bools proxy local port as raw TCP + (default [false]) + -e, --sess int number of dmsg servers to connect to + (default 1) + -s, --sk cipher.SecKey a random key is generated if unspecified + (default 0000000000000000000000000000000000000000000000000000000000000000) + -q, --socks uint port to serve the socks5 proxy + (default 4445) + -v, --version version for dmsgweb + +``` + +``` +$ go run cmd/dmsgweb/dmsgweb.go srv --help +DMSG web server - serve HTTP or raw TCP interface from local port over DMSG + .conf file may also be specified with DMSGWEBSRV=/path/to/dmsgwebsrv.conf skywire dmsg web srv + +Usage: + dmsgweb srv [flags] + +Flags: + -D, --dmsg-disc string DMSG discovery URL + (default "http://dmsgd.skywire.skycoin.com") + -d, --dport uints DMSG port(s) to serve + (default [80]) + -e, --dsess int DMSG sessions + (default 1) + -z, --envs show example .conf file + + -l, --loglvl string [ debug | warn | error | fatal | panic | trace | info ] + + -p, --lport uints local application interface port(s) + (default [8086]) + -x, --proxy string connect to DMSG via proxy (e.g., '127.0.0.1:1080') + + -c, --rt bools proxy local port as raw TCP, comma separated + (default [false]) + -s, --sk cipher.SecKey a random key is generated if unspecified + (default 0000000000000000000000000000000000000000000000000000000000000000) + -w, --wl strings whitelisted keys for DMSG authenticated routes + ``` diff --git a/cmd/dmsgweb/commands/dmsgweb.go b/cmd/dmsgweb/commands/dmsgweb.go index f1c5c7c4..7db2a4c1 100644 --- a/cmd/dmsgweb/commands/dmsgweb.go +++ b/cmd/dmsgweb/commands/dmsgweb.go @@ -67,18 +67,18 @@ func init() { } pk, _ = sk.PubKey() //nolint - RootCmd.Flags().StringVarP(&filterDomainSuffix, "filter", "f", ".dmsg", "domain suffix to filter") - RootCmd.Flags().UintVarP(&proxyPort, "socks", "q", proxyPort, "port to serve the socks5 proxy") - RootCmd.Flags().StringVarP(&addProxy, "addproxy", "r", addProxy, "configure additional socks5 proxy for dmsgweb (i.e. 127.0.0.1:1080)") - RootCmd.Flags().UintSliceVarP(&webPort, "port", "p", webPort, "port(s) to serve the web application") - RootCmd.Flags().StringSliceVarP(&resolveDmsgAddr, "resolve", "t", resolveDmsgAddr, "resolve the specified dmsg address:port on the local port & disable proxy") - RootCmd.Flags().StringVarP(&dmsgDisc, "dmsg-disc", "D", dmsgDisc, "dmsg discovery url") - RootCmd.Flags().StringVarP(&proxyAddr, "proxy", "x", "", "connect to dmsg via proxy (i.e. '127.0.0.1:1080')") - RootCmd.Flags().IntVarP(&dmsgSessions, "sess", "e", dmsgSess, "number of dmsg servers to connect to") - RootCmd.Flags().BoolSliceVarP(&rawTCP, "rt", "c", rawTCP, "proxy local port as raw TCP") - RootCmd.Flags().StringVarP(&logLvl, "loglvl", "l", "", "[ debug | warn | error | fatal | panic | trace | info ]\033[0m") + RootCmd.Flags().StringVarP(&filterDomainSuffix, "filter", "f", ".dmsg", "domain suffix to filter\033[0m\n\r") + RootCmd.Flags().UintVarP(&proxyPort, "socks", "q", proxyPort, "port to serve the socks5 proxy\033[0m\n\r") + RootCmd.Flags().StringVarP(&addProxy, "addproxy", "r", addProxy, "configure additional socks5 proxy for dmsgweb (i.e. 127.0.0.1:1080)\033[0m\n\r") + RootCmd.Flags().UintSliceVarP(&webPort, "port", "p", webPort, "port(s) to serve the web application\033[0m\n\r") + RootCmd.Flags().StringSliceVarP(&resolveDmsgAddr, "resolve", "t", resolveDmsgAddr, "resolve the specified dmsg address:port on the local port & disable proxy\033[0m\n\r") + RootCmd.Flags().StringVarP(&dmsgDisc, "dmsg-disc", "D", dmsgDisc, "dmsg discovery url\033[0m\n\r") + RootCmd.Flags().StringVarP(&proxyAddr, "proxy", "x", "", "connect to dmsg via proxy (i.e. '127.0.0.1:1080')\033[0m\n\r") + RootCmd.Flags().IntVarP(&dmsgSessions, "sess", "e", dmsgSess, "number of dmsg servers to connect to\033[0m\n\r") + RootCmd.Flags().BoolSliceVarP(&rawTCP, "rt", "c", rawTCP, "proxy local port as raw TCP\033[0m\n\r") + RootCmd.Flags().StringVarP(&logLvl, "loglvl", "l", "debug", "[ debug | warn | error | fatal | panic | trace | info ]\033[0m\n\r") RootCmd.Flags().VarP(&sk, "sk", "s", "a random key is generated if unspecified\n\r") - RootCmd.Flags().BoolVarP(&isEnvs, "envs", "z", false, "show example .conf file") + RootCmd.Flags().BoolVarP(&isEnvs, "envs", "z", false, "show example .conf file\033[0m\n\r") } diff --git a/cmd/dmsgweb/commands/dmsgwebsrv.go b/cmd/dmsgweb/commands/dmsgwebsrv.go index 5d869d5e..a1253e17 100644 --- a/cmd/dmsgweb/commands/dmsgwebsrv.go +++ b/cmd/dmsgweb/commands/dmsgwebsrv.go @@ -43,16 +43,16 @@ func init() { pk, _ = sk.PubKey() //nolint RootCmd.AddCommand(srvCmd) - srvCmd.Flags().UintSliceVarP(&localPort, "lport", "p", localPort, "local application interface port(s)") - srvCmd.Flags().UintSliceVarP(&dmsgPort, "dport", "d", dmsgPort, "DMSG port(s) to serve") - srvCmd.Flags().StringSliceVarP(&wl, "wl", "w", wl, "whitelisted keys for DMSG authenticated routes") - srvCmd.Flags().StringVarP(&dmsgDisc, "dmsg-disc", "D", dmsgDisc, "DMSG discovery URL") - srvCmd.Flags().StringVarP(&proxyAddr, "proxy", "x", proxyAddr, "connect to DMSG via proxy (e.g., '127.0.0.1:1080')") - srvCmd.Flags().IntVarP(&dmsgSess, "dsess", "e", dmsgSess, "DMSG sessions") - srvCmd.Flags().BoolSliceVarP(&rawTCP, "rt", "c", rawTCP, "proxy local port as raw TCP, comma separated") - srvCmd.Flags().StringVarP(&logLvl, "loglvl", "l", "", "[ debug | warn | error | fatal | panic | trace | info ]\033[0m") - srvCmd.Flags().BoolVarP(&isEnvs, "envs", "z", false, "show example .conf file") - srvCmd.Flags().VarP(&sk, "sk", "s", "a random key is generated if unspecified") + srvCmd.Flags().UintSliceVarP(&localPort, "lport", "p", localPort, "local application interface port(s)\033[0m\n\r") + srvCmd.Flags().UintSliceVarP(&dmsgPort, "dport", "d", dmsgPort, "DMSG port(s) to serve\033[0m\n\r") + srvCmd.Flags().StringSliceVarP(&wl, "wl", "w", wl, "whitelisted keys for DMSG authenticated routes\033[0m\n\r") + srvCmd.Flags().StringVarP(&dmsgDisc, "dmsg-disc", "D", dmsgDisc, "DMSG discovery URL\033[0m\n\r") + srvCmd.Flags().StringVarP(&proxyAddr, "proxy", "x", proxyAddr, "connect to DMSG via proxy (e.g., '127.0.0.1:1080')\033[0m\n\r") + srvCmd.Flags().IntVarP(&dmsgSess, "dsess", "e", dmsgSess, "DMSG sessions\033[0m\n\r") + srvCmd.Flags().BoolSliceVarP(&rawTCP, "rt", "c", rawTCP, "proxy local port as raw TCP, comma separated\033[0m\n\r") + srvCmd.Flags().StringVarP(&logLvl, "loglvl", "l", "debug", "[ debug | warn | error | fatal | panic | trace | info ]\033[0m\n\r") + srvCmd.Flags().BoolVarP(&isEnvs, "envs", "z", false, "show example .conf file\033[0m\n\r") + srvCmd.Flags().VarP(&sk, "sk", "s", "a random key is generated if unspecified\033[0m\n\r") srvCmd.CompletionOptions.DisableDefaultCmd = true } @@ -257,13 +257,13 @@ func proxyTCPConnections(ctx context.Context, localPort uint, listener net.Liste defer localConn.Close() //nolint go func() { - _, err := io.Copy(dmsgConn, localConn) - if err != nil { + _, err1 := io.Copy(dmsgConn, localConn) + if err1 != nil { dLog.WithError(err).Warn("Error on io.Copy(dmsgConn, localConn)") } }() - _, err := io.Copy(localConn, dmsgConn) - if err != nil { + _, err2 := io.Copy(localConn, dmsgConn) + if err2 != nil { dLog.WithError(err).Warn("Error on io.Copy(localConn, dmsgConn)") } From ddd65dfbe498831c4479856fdffb803e59ede177 Mon Sep 17 00:00:00 2001 From: Moses Narrow Date: Thu, 30 Jan 2025 17:22:57 -0600 Subject: [PATCH 19/21] fix CI errors --- .../tcp-multi-proxy-dmsg.go | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename examples/{tcp-proxy-dmsg => tcp-multi-proxy-dmsg}/tcp-multi-proxy-dmsg.go (100%) diff --git a/examples/tcp-proxy-dmsg/tcp-multi-proxy-dmsg.go b/examples/tcp-multi-proxy-dmsg/tcp-multi-proxy-dmsg.go similarity index 100% rename from examples/tcp-proxy-dmsg/tcp-multi-proxy-dmsg.go rename to examples/tcp-multi-proxy-dmsg/tcp-multi-proxy-dmsg.go From e93839585754ec94fee9b4597429271060e0a2f1 Mon Sep 17 00:00:00 2001 From: Moses Narrow Date: Thu, 30 Jan 2025 17:27:50 -0600 Subject: [PATCH 20/21] fix error message --- cmd/dmsgweb/commands/dmsgwebsrv.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/dmsgweb/commands/dmsgwebsrv.go b/cmd/dmsgweb/commands/dmsgwebsrv.go index a1253e17..46f96484 100644 --- a/cmd/dmsgweb/commands/dmsgwebsrv.go +++ b/cmd/dmsgweb/commands/dmsgwebsrv.go @@ -259,12 +259,12 @@ func proxyTCPConnections(ctx context.Context, localPort uint, listener net.Liste go func() { _, err1 := io.Copy(dmsgConn, localConn) if err1 != nil { - dLog.WithError(err).Warn("Error on io.Copy(dmsgConn, localConn)") + dLog.WithError(err1).Warn("Error on io.Copy(dmsgConn, localConn)") } }() _, err2 := io.Copy(localConn, dmsgConn) if err2 != nil { - dLog.WithError(err).Warn("Error on io.Copy(localConn, dmsgConn)") + dLog.WithError(err1).Warn("Error on io.Copy(localConn, dmsgConn)") } connMutex.Lock() From f7835ce9745998303d33e7cf9bb38e40773090ac Mon Sep 17 00:00:00 2001 From: Moses Narrow Date: Thu, 30 Jan 2025 17:29:27 -0600 Subject: [PATCH 21/21] fix error message --- cmd/dmsgweb/commands/dmsgwebsrv.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/dmsgweb/commands/dmsgwebsrv.go b/cmd/dmsgweb/commands/dmsgwebsrv.go index 46f96484..84f0a916 100644 --- a/cmd/dmsgweb/commands/dmsgwebsrv.go +++ b/cmd/dmsgweb/commands/dmsgwebsrv.go @@ -264,7 +264,7 @@ func proxyTCPConnections(ctx context.Context, localPort uint, listener net.Liste }() _, err2 := io.Copy(localConn, dmsgConn) if err2 != nil { - dLog.WithError(err1).Warn("Error on io.Copy(localConn, dmsgConn)") + dLog.WithError(err2).Warn("Error on io.Copy(localConn, dmsgConn)") } connMutex.Lock()