From ae552cec823c3c2053457c93eab8f8f92136c487 Mon Sep 17 00:00:00 2001 From: Mohammed <79150699+mrpalide@users.noreply.github.com> Date: Thu, 8 Dec 2022 17:30:34 +0330 Subject: [PATCH] [WIP] App/Services showing ports subcommand `skywire-cli visor ports` (#1412) * initial commit * fetch all ports from skyenv/config/running service * add hypervisor port * commit WIP - switching machines * use rpc to get the hypervisor port and replace hardcoded references to the default port * a little change on skysocks * define method GetAppPort and SetAppPort for apps initialization * add SetAppPort to apps * improve command output by adding Type column/field * Improve rpcClient.Ports response * add type field to some of ports * improve Ports() data * remove useless services that read from skyenv * improve output by add some notes * add port type to dmsgpty * add cli and proc port to list, read from config Co-authored-by: Moses Narrow <36607567+0pcom@users.noreply.github.com> --- cmd/apps/skychat/skychat.go | 8 +++ cmd/apps/skysocks-client/skysocks-client.go | 7 +++ cmd/apps/skysocks/skysocks.go | 12 +++- cmd/apps/vpn-client/vpn-client.go | 8 +++ cmd/apps/vpn-server/vpn-server.go | 8 ++- cmd/skywire-cli/commands/visor/hv.go | 29 ++++++--- cmd/skywire-cli/commands/visor/info.go | 43 ++++++++++++- cmd/skywire-cli/commands/vpn/vvpn.go | 15 ++--- cmd/skywire-cli/internal/internal.go | 5 ++ pkg/app/appserver/mock_rpc_ingress_client.go | 15 +++++ pkg/app/appserver/proc.go | 19 ++++++ pkg/app/appserver/proc_manager.go | 12 ++++ pkg/app/appserver/rpc_ingress_client.go | 6 ++ pkg/app/appserver/rpc_ingress_gateway.go | 7 +++ pkg/app/client.go | 5 ++ pkg/transport/network/addrresolver/client.go | 9 +++ .../network/addrresolver/mock_api_client.go | 4 ++ pkg/visor/api.go | 62 +++++++++++++++++++ pkg/visor/hypervisorconfig/config.go | 5 ++ pkg/visor/rpc.go | 10 +++ pkg/visor/rpc_client.go | 12 ++++ 21 files changed, 279 insertions(+), 22 deletions(-) diff --git a/cmd/apps/skychat/skychat.go b/cmd/apps/skychat/skychat.go index acd8af19a9..99ab56842a 100644 --- a/cmd/apps/skychat/skychat.go +++ b/cmd/apps/skychat/skychat.go @@ -118,6 +118,8 @@ func listenLoop() { return } + setAppPort(appCl, port) + for { fmt.Println("Accepting skychat conn...") conn, err := l.Accept() @@ -280,3 +282,9 @@ func setAppError(appCl *app.Client, appErr error) { print(fmt.Sprintf("Failed to set error %v: %v\n", appErr, err)) } } + +func setAppPort(appCl *app.Client, port routing.Port) { + if err := appCl.SetAppPort(port); err != nil { + print(fmt.Sprintf("Failed to set port %v: %v\n", port, err)) + } +} diff --git a/cmd/apps/skysocks-client/skysocks-client.go b/cmd/apps/skysocks-client/skysocks-client.go index 0e4b038e2d..10f9660df7 100644 --- a/cmd/apps/skysocks-client/skysocks-client.go +++ b/cmd/apps/skysocks-client/skysocks-client.go @@ -79,6 +79,7 @@ func main() { os.Exit(1) } defer setAppStatus(appCl, appserver.AppDetailedStatusStopped) + setAppPort(appCl, socksPort) for { conn, err := dialServer(ctx, appCl, pk) if err != nil { @@ -123,3 +124,9 @@ func setAppStatus(appCl *app.Client, status appserver.AppDetailedStatus) { print(fmt.Sprintf("Failed to set status %v: %v\n", status, err)) } } + +func setAppPort(appCl *app.Client, port routing.Port) { + if err := appCl.SetAppPort(port); err != nil { + print(fmt.Sprintf("Failed to set port %v: %v\n", port, err)) + } +} diff --git a/cmd/apps/skysocks/skysocks.go b/cmd/apps/skysocks/skysocks.go index 9feea76ac5..9c844c8bec 100644 --- a/cmd/apps/skysocks/skysocks.go +++ b/cmd/apps/skysocks/skysocks.go @@ -23,8 +23,8 @@ import ( ) const ( - netType = appnet.TypeSkynet - port routing.Port = 3 + netType = appnet.TypeSkynet + port = routing.Port(3) ) func main() { @@ -52,6 +52,8 @@ func main() { os.Exit(1) } + setAppPort(appCl, port) + fmt.Println("Starting serving proxy server") if runtime.GOOS == "windows" { @@ -94,3 +96,9 @@ func setAppError(appCl *app.Client, appErr error) { print(fmt.Sprintf("Failed to set error %v: %v\n", appErr, err)) } } + +func setAppPort(appCl *app.Client, port routing.Port) { + if err := appCl.SetAppPort(port); err != nil { + print(fmt.Sprintf("Failed to set port %v: %v\n", port, err)) + } +} diff --git a/cmd/apps/vpn-client/vpn-client.go b/cmd/apps/vpn-client/vpn-client.go index 7e51c570a9..a7549e50c4 100644 --- a/cmd/apps/vpn-client/vpn-client.go +++ b/cmd/apps/vpn-client/vpn-client.go @@ -19,6 +19,7 @@ import ( "github.com/skycoin/skywire/pkg/app" "github.com/skycoin/skywire/pkg/app/appevent" "github.com/skycoin/skywire/pkg/app/appserver" + "github.com/skycoin/skywire/pkg/routing" "github.com/skycoin/skywire/pkg/skyenv" ) @@ -106,6 +107,7 @@ func main() { } } + setAppPort(appCl, appCl.Config().RoutingPort) fmt.Printf("Connecting to VPN server %s\n", serverPK.String()) vpnClientCfg := vpn.ClientConfig{ @@ -190,3 +192,9 @@ func setAppStatus(appCl *app.Client, status appserver.AppDetailedStatus) { print(fmt.Sprintf("Failed to set status %v: %v\n", status, err)) } } + +func setAppPort(appCl *app.Client, port routing.Port) { + if err := appCl.SetAppPort(port); err != nil { + print(fmt.Sprintf("Failed to set port %v: %v\n", port, err)) + } +} diff --git a/cmd/apps/vpn-server/vpn-server.go b/cmd/apps/vpn-server/vpn-server.go index a9e6da3916..8b56224b84 100644 --- a/cmd/apps/vpn-server/vpn-server.go +++ b/cmd/apps/vpn-server/vpn-server.go @@ -82,7 +82,7 @@ func main() { setAppErr(appCl, err) os.Exit(1) } - + setAppPort(appCl, vpnPort) fmt.Printf("Got app listener, bound to %d\n", vpnPort) srvCfg := vpn.ServerConfig{ @@ -131,3 +131,9 @@ func setAppStatus(appCl *app.Client, status appserver.AppDetailedStatus) { print(fmt.Sprintf("Failed to set status %v: %v\n", status, err)) } } + +func setAppPort(appCl *app.Client, port routing.Port) { + if err := appCl.SetAppPort(port); err != nil { + print(fmt.Sprintf("Failed to set port %v: %v\n", port, err)) + } +} diff --git a/cmd/skywire-cli/commands/visor/hv.go b/cmd/skywire-cli/commands/visor/hv.go index 2511654e09..f7d24400ce 100644 --- a/cmd/skywire-cli/commands/visor/hv.go +++ b/cmd/skywire-cli/commands/visor/hv.go @@ -6,11 +6,13 @@ import ( "os" "github.com/spf13/cobra" + "github.com/spf13/pflag" "github.com/toqueteos/webbrowser" "github.com/skycoin/skywire-utilities/pkg/cipher" clirpc "github.com/skycoin/skywire/cmd/skywire-cli/commands/rpc" "github.com/skycoin/skywire/cmd/skywire-cli/internal" + "github.com/skycoin/skywire/pkg/visor/hypervisorconfig" "github.com/skycoin/skywire/pkg/visor/visorconfig" ) @@ -22,7 +24,6 @@ func init() { hvpkCmd.Flags().BoolVarP(&pkg, "pkg", "p", false, "read from /opt/skywire/skywire.json") hvpkCmd.Flags().BoolVarP(&web, "http", "w", false, "serve public key via http") hvCmd.AddCommand(chvpkCmd) - } var hvCmd = &cobra.Command{ @@ -35,9 +36,8 @@ var hvuiCmd = &cobra.Command{ Use: "ui", Short: "open Hypervisor UI in default browser", Long: "\n open Hypervisor UI in default browser", - Run: func(_ *cobra.Command, _ []string) { - //TODO: get the actual port from config instead of using default value here - if err := webbrowser.Open("http://127.0.0.1:8000/"); err != nil { + Run: func(cmd *cobra.Command, _ []string) { + if err := webbrowser.Open(fmt.Sprintf("http://127.0.0.1%s/", HypervisorPort(cmd.Flags()))); err != nil { logger.Fatal("Failed to open hypervisor UI in browser:", err) } }, @@ -49,11 +49,9 @@ var hvpkCmd = &cobra.Command{ Long: "\n Public key of remote hypervisor(s) set in config", Run: func(cmd *cobra.Command, _ []string) { var hypervisors []cipher.PubKey - if pkg { path = visorconfig.Pkgpath } - if path != "" { conf, err := visorconfig.ReadFile(path) if err != nil { @@ -67,7 +65,7 @@ var hvpkCmd = &cobra.Command{ } overview, err := rpcClient.Overview() if err != nil { - internal.PrintFatalError(cmd.Flags(), fmt.Errorf("Failed to connect: %v", err)) + internal.PrintFatalRPCError(cmd.Flags(), err) } hypervisors = overview.Hypervisors } @@ -82,12 +80,25 @@ var chvpkCmd = &cobra.Command{ Run: func(cmd *cobra.Command, _ []string) { rpcClient, err := clirpc.Client(cmd.Flags()) if err != nil { - os.Exit(1) + internal.PrintFatalRPCError(cmd.Flags(), err) } overview, err := rpcClient.Overview() if err != nil { - internal.PrintFatalError(cmd.Flags(), fmt.Errorf("Failed to connect: %v", err)) + internal.PrintFatalRPCError(cmd.Flags(), err) } internal.PrintOutput(cmd.Flags(), overview.ConnectedHypervisor, fmt.Sprintf("%v\n", overview.ConnectedHypervisor)) }, } + +// HypervisorPort returns the port of the hypervisor; either from the running visor or the default value +func HypervisorPort(cmdFlags *pflag.FlagSet) string { + rpcClient, err := clirpc.Client(cmdFlags) + if err != nil { + return hypervisorconfig.HTTPAddr() + } + ports, err := rpcClient.Ports() + if err != nil { + return hypervisorconfig.HTTPAddr() + } + return fmt.Sprintf(":%s", ports["hypervisor"]) +} diff --git a/cmd/skywire-cli/commands/visor/info.go b/cmd/skywire-cli/commands/visor/info.go index 091075cd58..cc9b8471be 100644 --- a/cmd/skywire-cli/commands/visor/info.go +++ b/cmd/skywire-cli/commands/visor/info.go @@ -5,6 +5,7 @@ import ( "fmt" "net/http" "os" + "sort" "strings" "github.com/spf13/cobra" @@ -28,6 +29,7 @@ func init() { pkCmd.Flags().StringVarP(&webPort, "prt", "x", "7998", "serve public key via http") RootCmd.AddCommand(summaryCmd) RootCmd.AddCommand(buildInfoCmd) + RootCmd.AddCommand(portsCmd) } var pkCmd = &cobra.Command{ @@ -52,7 +54,7 @@ var pkCmd = &cobra.Command{ } overview, err := rpcClient.Overview() if err != nil { - internal.PrintFatalError(cmd.Flags(), fmt.Errorf("Failed to connect: %v", err)) + internal.PrintFatalRPCError(cmd.Flags(), err) } pk = overview.PubKey.String() + "\n" if web { @@ -77,7 +79,7 @@ var summaryCmd = &cobra.Command{ } summary, err := rpcClient.Summary() if err != nil { - internal.PrintFatalError(cmd.Flags(), fmt.Errorf("Failed to connect: %v", err)) + internal.PrintFatalRPCError(cmd.Flags(), err) } msg := fmt.Sprintf(".:: Visor Summary ::.\nPublic key: %q\nSymmetric NAT: %t\nIP: %s\nDMSG Server: %q\nPing: %q\nVisor Version: %s\nSkybian Version: %s\nUptime Tracker: %s\nTime Online: %f seconds\nBuild Tag: %s\n", summary.Overview.PubKey, summary.Overview.IsSymmetricNAT, summary.Overview.LocalIP, summary.DmsgStats.ServerPK, summary.DmsgStats.RoundTrip, summary.Overview.BuildInfo.Version, summary.SkybianBuildVersion, @@ -121,7 +123,7 @@ var buildInfoCmd = &cobra.Command{ } overview, err := rpcClient.Overview() if err != nil { - internal.PrintFatalError(cmd.Flags(), fmt.Errorf("Failed to connect: %v", err)) + internal.PrintFatalRPCError(cmd.Flags(), err) } buildInfo := overview.BuildInfo msg := fmt.Sprintf("Version %q built on %q against commit %q\n", buildInfo.Version, buildInfo.Date, buildInfo.Commit) @@ -129,6 +131,41 @@ var buildInfoCmd = &cobra.Command{ }, } +var portsCmd = &cobra.Command{ + Use: "ports", + Short: "List of Ports", + Long: "\n List of all ports used by visor services and apps", + Run: func(cmd *cobra.Command, _ []string) { + rpcClient, err := clirpc.Client(cmd.Flags()) + if err != nil { + os.Exit(1) + } + ports, err := rpcClient.Ports() + if err != nil { + internal.PrintFatalRPCError(cmd.Flags(), err) + } + msg := "+-------------------------------------------+\n" + msg += fmt.Sprintf("| %-21s | %-7s | %-7s |\n", "App/Service", "Type", "Port") + msg += "|-------------------------------------------|\n" + + portsName := make([]string, 0, len(ports)) + for portName := range ports { + portsName = append(portsName, portName) + } + sort.Strings(portsName) + + for _, portName := range portsName { + msg += fmt.Sprintf("| %-21s | %-7s | %7s |\n", portName, ports[portName].Type, ports[portName].Port) + } + + msg += "|===========================================|\n" + msg += "| SKYNET: connection between apps and visor |\n" + msg += "| DMSG: connection by dmsg service |\n" + msg += "+-------------------------------------------+\n" + internal.PrintOutput(cmd.Flags(), ports, msg) + }, +} + func srvpk(w http.ResponseWriter, _ *http.Request) { fmt.Fprintf(w, pk) //nolint } diff --git a/cmd/skywire-cli/commands/vpn/vvpn.go b/cmd/skywire-cli/commands/vpn/vvpn.go index bccf4fdfd8..4e6f372c24 100644 --- a/cmd/skywire-cli/commands/vpn/vvpn.go +++ b/cmd/skywire-cli/commands/vpn/vvpn.go @@ -15,6 +15,7 @@ import ( "github.com/skycoin/skywire-utilities/pkg/buildinfo" "github.com/skycoin/skywire-utilities/pkg/cipher" clirpc "github.com/skycoin/skywire/cmd/skywire-cli/commands/rpc" + clivisor "github.com/skycoin/skywire/cmd/skywire-cli/commands/visor" "github.com/skycoin/skywire/cmd/skywire-cli/internal" "github.com/skycoin/skywire/pkg/app/appserver" "github.com/skycoin/skywire/pkg/visor" @@ -58,7 +59,7 @@ var vpnUICmd = &cobra.Command{ if err != nil { internal.PrintFatalError(cmd.Flags(), fmt.Errorf("Failed to read in config: %v", err)) } - url = fmt.Sprintf("http://127.0.0.1:8000/#/vpn/%s/", conf.PK.Hex()) + url = fmt.Sprintf("http://127.0.0.1%s/#/vpn/%s/", clivisor.HypervisorPort(cmd.Flags()), conf.PK.Hex()) } else { rpcClient, err := clirpc.Client(cmd.Flags()) if err != nil { @@ -68,7 +69,7 @@ var vpnUICmd = &cobra.Command{ if err != nil { internal.PrintFatalError(cmd.Flags(), fmt.Errorf("Failed to connect; is skywire running?: %v", err)) } - url = fmt.Sprintf("http://127.0.0.1:8000/#/vpn/%s/", overview.PubKey.Hex()) + url = fmt.Sprintf("http://127.0.0.1%s/#/vpn/%s/", clivisor.HypervisorPort(cmd.Flags()), overview.PubKey.Hex()) } if err := webbrowser.Open(url); err != nil { internal.PrintFatalError(cmd.Flags(), fmt.Errorf("Failed to open VPN UI in browser:: %v", err)) @@ -89,7 +90,7 @@ var vpnURLCmd = &cobra.Command{ if err != nil { internal.PrintFatalError(cmd.Flags(), fmt.Errorf("Failed to read in config: %v", err)) } - url = fmt.Sprintf("http://127.0.0.1:8000/#/vpn/%s/", conf.PK.Hex()) + url = fmt.Sprintf("http://127.0.0.1%s/#/vpn/%s/", clivisor.HypervisorPort(cmd.Flags()), conf.PK.Hex()) } else { rpcClient, err := clirpc.Client(cmd.Flags()) if err != nil { @@ -97,9 +98,9 @@ var vpnURLCmd = &cobra.Command{ } overview, err := rpcClient.Overview() if err != nil { - internal.PrintFatalError(cmd.Flags(), fmt.Errorf("Failed to connect; is skywire running?: %v", err)) + internal.PrintFatalRPCError(cmd.Flags(), err) } - url = fmt.Sprintf("http://127.0.0.1:8000/#/vpn/%s/", overview.PubKey.Hex()) + url = fmt.Sprintf("http://127.0.0.1%s/#/vpn/%s/", clivisor.HypervisorPort(cmd.Flags()), overview.PubKey.Hex()) } output := struct { @@ -118,7 +119,7 @@ var vpnListCmd = &cobra.Command{ Run: func(cmd *cobra.Command, _ []string) { rpcClient, err := clirpc.Client(cmd.Flags()) if err != nil { - os.Exit(1) + internal.PrintFatalRPCError(cmd.Flags(), err) } if isUnFiltered { ver = "" @@ -126,7 +127,7 @@ var vpnListCmd = &cobra.Command{ } servers, err := rpcClient.VPNServers(ver, country) if err != nil { - internal.PrintFatalError(cmd.Flags(), err) + internal.PrintFatalRPCError(cmd.Flags(), err) } if len(servers) == 0 { internal.PrintFatalError(cmd.Flags(), fmt.Errorf("No VPN Servers found")) diff --git a/cmd/skywire-cli/internal/internal.go b/cmd/skywire-cli/internal/internal.go index ffa14b9c88..fc535d5b28 100644 --- a/cmd/skywire-cli/internal/internal.go +++ b/cmd/skywire-cli/internal/internal.go @@ -42,6 +42,11 @@ func PrintFatalError(cmdFlags *pflag.FlagSet, err error) { log.Fatal(err) } +// PrintFatalRPCError prints errors for skywire-cli commands packages +func PrintFatalRPCError(cmdFlags *pflag.FlagSet, err error) { + PrintFatalError(cmdFlags, fmt.Errorf("Failed to connect; is skywire running?: %v", err)) +} + // PrintError prints errors for skywire-cli commands packages func PrintError(cmdFlags *pflag.FlagSet, err error) { isJSON, _ := cmdFlags.GetBool(JSONString) //nolint:errcheck diff --git a/pkg/app/appserver/mock_rpc_ingress_client.go b/pkg/app/appserver/mock_rpc_ingress_client.go index 5d42d2aa58..6a7292da1e 100644 --- a/pkg/app/appserver/mock_rpc_ingress_client.go +++ b/pkg/app/appserver/mock_rpc_ingress_client.go @@ -198,6 +198,21 @@ func (_m *MockRPCIngressClient) SetError(appErr string) error { return r0 } + +// SetDetailedStatus provides a mock function with given fields: status +func (_m *MockRPCIngressClient) SetAppPort(port routing.Port) error { + ret := _m.Called(port) + + var r0 error + if rf, ok := ret.Get(0).(func(routing.Port) error); ok { + r0 = rf(port) + } else { + r0 = ret.Error(0) + } + + return r0 +} + // SetReadDeadline provides a mock function with given fields: connID, d func (_m *MockRPCIngressClient) SetReadDeadline(connID uint16, d time.Time) error { ret := _m.Called(connID, d) diff --git a/pkg/app/appserver/proc.go b/pkg/app/appserver/proc.go index 3f10d673d0..c8a20e77cf 100644 --- a/pkg/app/appserver/proc.go +++ b/pkg/app/appserver/proc.go @@ -22,6 +22,7 @@ import ( "github.com/skycoin/skywire/pkg/app/appcommon" "github.com/skycoin/skywire/pkg/app/appdisc" "github.com/skycoin/skywire/pkg/app/appnet" + "github.com/skycoin/skywire/pkg/routing" "github.com/skycoin/skywire/pkg/skyenv" ) @@ -68,6 +69,9 @@ type Proc struct { errMx sync.RWMutex err string + portMx sync.RWMutex + port routing.Port + cmdStderr io.ReadCloser readyCh chan struct{} // push here when ready to start app disc - protected by 'readyOnce' @@ -375,6 +379,21 @@ func (p *Proc) SetError(appErr string) { p.err = appErr } +// SetAppPort sets the proc's connection port +func (p *Proc) SetAppPort(port routing.Port) { + p.portMx.Lock() + defer p.portMx.Unlock() + p.port = port +} + +// GetAppPort gets the proc's connection port +func (p *Proc) GetAppPort() routing.Port { + p.portMx.Lock() + defer p.portMx.Unlock() + + return p.port +} + // Error gets proc's error. func (p *Proc) Error() string { p.errMx.RLock() diff --git a/pkg/app/appserver/proc_manager.go b/pkg/app/appserver/proc_manager.go index 3b3257e42e..820281b326 100644 --- a/pkg/app/appserver/proc_manager.go +++ b/pkg/app/appserver/proc_manager.go @@ -15,6 +15,7 @@ import ( "github.com/skycoin/skywire/pkg/app/appcommon" "github.com/skycoin/skywire/pkg/app/appdisc" "github.com/skycoin/skywire/pkg/app/appevent" + "github.com/skycoin/skywire/pkg/routing" ) //go:generate mockery -name ProcManager -case underscore -inpkg @@ -46,6 +47,7 @@ type ProcManager interface { Stats(appName string) (AppStats, error) SetDetailedStatus(appName, status string) error DetailedStatus(appName string) (string, error) + GetAppPort(appName string) (routing.Port, error) ConnectionsSummary(appName string) ([]ConnectionSummary, error) Addr() net.Addr } @@ -332,6 +334,16 @@ func (m *procManager) SetError(appName, appErr string) error { return nil } +// GetAppPort gets port of the app `appName`. +func (m *procManager) GetAppPort(appName string) (routing.Port, error) { + p, err := m.get(appName) + if err != nil { + return routing.Port(0), err + } + + return p.GetAppPort(), nil +} + // ConnectionsSummary gets connections info for the app `appName`. func (m *procManager) ConnectionsSummary(appName string) ([]ConnectionSummary, error) { p, err := m.get(appName) diff --git a/pkg/app/appserver/rpc_ingress_client.go b/pkg/app/appserver/rpc_ingress_client.go index 0589efb714..11131e2f83 100644 --- a/pkg/app/appserver/rpc_ingress_client.go +++ b/pkg/app/appserver/rpc_ingress_client.go @@ -18,6 +18,7 @@ type RPCIngressClient interface { SetDetailedStatus(status string) error SetConnectionDuration(dur int64) error SetError(appErr string) error + SetAppPort(appPort routing.Port) error Dial(remote appnet.Addr) (connID uint16, localPort routing.Port, err error) Listen(local appnet.Addr) (uint16, error) Accept(lisID uint16) (connID uint16, remote appnet.Addr, err error) @@ -59,6 +60,11 @@ func (c *rpcIngressClient) SetError(appErr string) error { return c.rpc.Call(c.formatMethod("SetError"), &appErr, nil) } +// SetAppPort sets port of an app. +func (c *rpcIngressClient) SetAppPort(port routing.Port) error { + return c.rpc.Call(c.formatMethod("SetAppPort"), &port, nil) +} + // RPCErr is used to preserve the type of the errors we return via RPC type RPCErr struct { Err string diff --git a/pkg/app/appserver/rpc_ingress_gateway.go b/pkg/app/appserver/rpc_ingress_gateway.go index 0c5d355173..cb9c18c5de 100644 --- a/pkg/app/appserver/rpc_ingress_gateway.go +++ b/pkg/app/appserver/rpc_ingress_gateway.go @@ -99,6 +99,13 @@ func (r *RPCIngressGateway) SetError(appErr *string, _ *struct{}) (err error) { return nil } +// SetAppPort sets the connection port of an app (vpn-client in this instance) +func (r *RPCIngressGateway) SetAppPort(port routing.Port, _ *struct{}) (err error) { + defer rpcutil.LogCall(r.log, "SetAppPort", port)(nil, &err) + r.proc.SetAppPort(port) + return nil +} + // DialResp contains response parameters for `Dial`. type DialResp struct { ConnID uint16 diff --git a/pkg/app/client.go b/pkg/app/client.go index a6a04a2796..056019934e 100644 --- a/pkg/app/client.go +++ b/pkg/app/client.go @@ -79,6 +79,11 @@ func (c *Client) SetError(appErr string) error { return c.rpcC.SetError(appErr) } +// SetAppPort sets app port within the visor. +func (c *Client) SetAppPort(appPort routing.Port) error { + return c.rpcC.SetAppPort(appPort) +} + // Dial dials the remote visor using `remote`. func (c *Client) Dial(remote appnet.Addr) (net.Conn, error) { connID, localPort, err := c.rpcC.Dial(remote) diff --git a/pkg/transport/network/addrresolver/client.go b/pkg/transport/network/addrresolver/client.go index 2ffc7d92b5..6a9f03c3fb 100644 --- a/pkg/transport/network/addrresolver/client.go +++ b/pkg/transport/network/addrresolver/client.go @@ -11,6 +11,7 @@ import ( "net" "net/http" "net/url" + "strings" "sync" "time" @@ -58,6 +59,7 @@ type APIClient interface { BindSUDPH(filter *pfilter.PacketFilter, handshake Handshake) (<-chan RemoteVisor, error) Resolve(ctx context.Context, netType string, pk cipher.PubKey) (VisorData, error) Transports(ctx context.Context) (map[cipher.PubKey][]string, error) + Addresses(ctx context.Context) string Close() error } @@ -209,6 +211,13 @@ type LocalAddresses struct { Addresses []string `json:"addresses"` } +func (c *httpClient) Addresses(ctx context.Context) string { + if c.sudphConn != nil { + return strings.Split(c.sudphConn.LocalAddr().String(), ":")[3] + } + return "" +} + // BindSTCPR binds client PK to IP:port on address resolver. func (c *httpClient) BindSTCPR(ctx context.Context, port string) error { log := c.log.WithField("func", "httpClient.BindSTCPR") diff --git a/pkg/transport/network/addrresolver/mock_api_client.go b/pkg/transport/network/addrresolver/mock_api_client.go index 0f4910c7fc..5ed5136826 100644 --- a/pkg/transport/network/addrresolver/mock_api_client.go +++ b/pkg/transport/network/addrresolver/mock_api_client.go @@ -108,3 +108,7 @@ func (_m *MockAPIClient) Resolve(ctx context.Context, netType string, pk cipher. return r0, r1 } + +func (_m *MockAPIClient) Addresses(ctx context.Context) (string, error) { + return "", nil +} diff --git a/pkg/visor/api.go b/pkg/visor/api.go index 340f97e1e1..67e6675bda 100644 --- a/pkg/visor/api.go +++ b/pkg/visor/api.go @@ -62,6 +62,7 @@ type API interface { GetAppConnectionsSummary(appName string) ([]appserver.ConnectionSummary, error) VPNServers(version, country string) ([]servicedisc.Service, error) RemoteVisors() ([]string, error) + Ports() (map[string]PortDetail, error) TransportTypes() ([]string, error) Transports(types []string, pks []cipher.PubKey, logs bool) ([]*TransportSummary, error) @@ -683,6 +684,67 @@ func (v *Visor) RemoteVisors() ([]string, error) { return visors, nil } +// PortDetail type of port details +type PortDetail struct { + Port string + Type string +} + +// Ports return list of all ports used by visor services and apps +func (v *Visor) Ports() (map[string]PortDetail, error) { + ctx := context.Background() + var ports = make(map[string]PortDetail) + + if v.conf.Hypervisor != nil { + ports["hypervisor"] = PortDetail{Port: fmt.Sprint(strings.Split(v.conf.Hypervisor.HTTPAddr, ":")[1]), Type: "TCP"} + } + + ports["dmsg_pty"] = PortDetail{Port: fmt.Sprint(v.conf.Dmsgpty.DmsgPort), Type: "DMSG"} + ports["cli_addr"] = PortDetail{Port: fmt.Sprint(strings.Split(v.conf.CLIAddr, ":")[1]), Type: "TCP"} + ports["proc_addr"] = PortDetail{Port: fmt.Sprint(strings.Split(v.conf.Launcher.ServerAddr, ":")[1]), Type: "TCP"} + ports["stcp_addr"] = PortDetail{Port: fmt.Sprint(strings.Split(v.conf.STCP.ListeningAddress, ":")[1]), Type: "TCP"} + + if v.arClient != nil { + sudphPort := v.arClient.Addresses(ctx) + if sudphPort != "" { + ports["sudph"] = PortDetail{Port: sudphPort, Type: "UDP"} + } + } + if v.stunClient != nil { + if v.stunClient.PublicIP != nil { + ports["public_visor"] = PortDetail{Port: fmt.Sprint(v.stunClient.PublicIP.Port()), Type: "TCP"} + } + } + if v.dmsgC != nil { + dmsgSessions := v.dmsgC.AllSessions() + for i, session := range dmsgSessions { + ports[fmt.Sprintf("dmsg_session_%d", i)] = PortDetail{Port: strings.Split(session.LocalTCPAddr().String(), ":")[1], Type: "TCP"} + } + + dmsgStreams := v.dmsgC.AllStreams() + for i, stream := range dmsgStreams { + ports[fmt.Sprintf("dmsg_stream_%d", i)] = PortDetail{Port: strings.Split(stream.LocalAddr().String(), ":")[1], Type: "DMSG"} + } + } + if v.procM != nil { + apps, _ := v.Apps() //nolint + for _, app := range apps { + port, err := v.procM.GetAppPort(app.Name) + if err == nil { + ports[app.Name] = PortDetail{Port: fmt.Sprint(port), Type: "SKYNET"} + + switch app.Name { + case "skysocks_client": + ports["skysocks_client_addr"] = PortDetail{Port: fmt.Sprint(strings.Split(skyenv.SkysocksClientAddr, ":")[1]), Type: "TCP"} + case "skychat": + ports["skychat_addr"] = PortDetail{Port: fmt.Sprint(strings.Split(skyenv.SkychatAddr, ":")[1]), Type: "TCP"} + } + } + } + } + return ports, nil +} + // TransportTypes implements API. func (v *Visor) TransportTypes() ([]string, error) { var types []string diff --git a/pkg/visor/hypervisorconfig/config.go b/pkg/visor/hypervisorconfig/config.go index 17d6aaa08f..99f7cc482f 100644 --- a/pkg/visor/hypervisorconfig/config.go +++ b/pkg/visor/hypervisorconfig/config.go @@ -24,6 +24,11 @@ const ( blockKeyLen = 32 ) +// HTTPAddr returns the httpAddr +func HTTPAddr() string { + return httpAddr +} + // Key allows a byte slice to be marshaled or unmarshaled from a hex string. type Key []byte diff --git a/pkg/visor/rpc.go b/pkg/visor/rpc.go index bbee00288a..499eb0be64 100644 --- a/pkg/visor/rpc.go +++ b/pkg/visor/rpc.go @@ -611,6 +611,16 @@ func (r *RPC) RemoteVisors(_ *struct{}, out *[]string) (err error) { return err } +// Ports return list of all ports used by visor services and apps +func (r *RPC) Ports(_ *struct{}, out *map[string]PortDetail) (err error) { + defer rpcutil.LogCall(r.log, "Ports", nil)(out, &err) + ports, err := r.visor.Ports() + if ports != nil { + *out = ports + } + return err +} + // IsDMSGClientReady return status of dmsg client func (r *RPC) IsDMSGClientReady(_ *struct{}, out *bool) (err error) { defer rpcutil.LogCall(r.log, "IsDMSGClientReady", nil)(out, &err) diff --git a/pkg/visor/rpc_client.go b/pkg/visor/rpc_client.go index a12a011506..be3ac2e8b7 100644 --- a/pkg/visor/rpc_client.go +++ b/pkg/visor/rpc_client.go @@ -455,6 +455,13 @@ func (rc *rpcClient) RemoteVisors() ([]string, error) { return output, nil } +// Ports calls Ports. +func (rc *rpcClient) Ports() (map[string]PortDetail, error) { + output := map[string]PortDetail{} + rc.Call("Ports", &struct{}{}, &output) // nolint + return output, nil +} + // IsDMSGClientReady return availability of dsmg client func (rc *rpcClient) IsDMSGClientReady() (bool, error) { var out bool @@ -1045,6 +1052,11 @@ func (mc *mockRPCClient) RemoteVisors() ([]string, error) { return []string{}, nil } +// Ports implements API +func (mc *mockRPCClient) Ports() (map[string]PortDetail, error) { + return map[string]PortDetail{}, nil +} + // IsDMSGClientReady implements API. func (mc *mockRPCClient) IsDMSGClientReady() (bool, error) { return false, nil