From bb2a2f263b569076031d5afc0ae29f0680c29463 Mon Sep 17 00:00:00 2001 From: Never M Date: Thu, 27 May 2021 12:27:05 +0300 Subject: [PATCH 01/55] Make dependecies explicit: move out of config --- pkg/router/router_test.go | 2 +- pkg/snet/network.go | 17 +++++++++-------- pkg/snet/snettest/env.go | 2 +- pkg/transport/manager.go | 21 +++++++++++---------- pkg/transport/manager_test.go | 5 +++-- pkg/visor/init.go | 7 +++---- 6 files changed, 28 insertions(+), 26 deletions(-) diff --git a/pkg/router/router_test.go b/pkg/router/router_test.go index df22a96c0..92656cbbf 100644 --- a/pkg/router/router_test.go +++ b/pkg/router/router_test.go @@ -795,7 +795,7 @@ func NewTestEnv(t *testing.T, nets []*snet.Network) *TestEnv { LogStore: transport.InMemoryTransportLogStore(), } - ms[i], err = transport.NewManager(nil, n, mConfs[i]) + ms[i], err = transport.NewManager(nil, n, nil, mConfs[i]) require.NoError(t, err) go ms[i].Serve(context.TODO()) diff --git a/pkg/snet/network.go b/pkg/snet/network.go index 33d5dd07b..0888e1c3a 100644 --- a/pkg/snet/network.go +++ b/pkg/snet/network.go @@ -71,7 +71,6 @@ func (c *STCPConfig) Type() string { type Config struct { PubKey cipher.PubKey SecKey cipher.SecKey - ARClient arclient.APIClient NetworkConfigs NetworkConfigs } @@ -94,12 +93,13 @@ type Network struct { nets map[string]struct{} // networks to be used with transports clients NetworkClients + arc arclient.APIClient onNewNetworkTypeMu sync.Mutex onNewNetworkType func(netType string) } // New creates a network from a config. -func New(conf Config, eb *appevent.Broadcaster) (*Network, error) { +func New(conf Config, eb *appevent.Broadcaster, arc arclient.APIClient) (*Network, error) { clients := NetworkClients{ Direct: make(map[string]directtp.Client), } @@ -143,12 +143,12 @@ func New(conf Config, eb *appevent.Broadcaster) (*Network, error) { clients.Direct[tptypes.STCP] = directtp.NewClient(conf) } - if conf.ARClient != nil { + if arc != nil { stcprConf := directtp.Config{ Type: tptypes.STCPR, PK: conf.PubKey, SK: conf.SecKey, - AddressResolver: conf.ARClient, + AddressResolver: arc, BeforeDialCallback: func(network, addr string) error { data := appevent.TCPDialData{RemoteNet: network, RemoteAddr: addr} event := appevent.NewEvent(appevent.TCPDial, data) @@ -163,21 +163,22 @@ func New(conf Config, eb *appevent.Broadcaster) (*Network, error) { Type: tptypes.SUDPH, PK: conf.PubKey, SK: conf.SecKey, - AddressResolver: conf.ARClient, + AddressResolver: arc, } clients.Direct[tptypes.SUDPH] = directtp.NewClient(sudphConf) } - return NewRaw(conf, clients), nil + return NewRaw(conf, clients, arc), nil } // NewRaw creates a network from a config and a dmsg client. -func NewRaw(conf Config, clients NetworkClients) *Network { +func NewRaw(conf Config, clients NetworkClients, arc arclient.APIClient) *Network { n := &Network{ conf: conf, nets: make(map[string]struct{}), clients: clients, + arc: arc, } if clients.DmsgC != nil { @@ -216,7 +217,7 @@ func (n *Network) Init() error { } } - if n.conf.ARClient != nil { + if n.arc != nil { if client, ok := n.clients.Direct[tptypes.STCPR]; ok && client != nil { if err := client.Serve(); err != nil { return fmt.Errorf("failed to initiate 'stcpr': %w", err) diff --git a/pkg/snet/snettest/env.go b/pkg/snet/snettest/env.go index 4540e971b..7f9f6659f 100644 --- a/pkg/snet/snettest/env.go +++ b/pkg/snet/snettest/env.go @@ -146,7 +146,7 @@ func NewEnv(t *testing.T, keys []KeyPair, networks []string) *Env { NetworkConfigs: networkConfigs, } - n := snet.NewRaw(snetConfig, clients) + n := snet.NewRaw(snetConfig, clients, addressResolver) require.NoError(t, n.Init()) ns[i] = n } diff --git a/pkg/transport/manager.go b/pkg/transport/manager.go index d664848ae..70ae74c24 100644 --- a/pkg/transport/manager.go +++ b/pkg/transport/manager.go @@ -34,10 +34,11 @@ type ManagerConfig struct { // Manager manages Transports. type Manager struct { - Logger *logging.Logger - Conf *ManagerConfig - tps map[uuid.UUID]*ManagedTransport - n *snet.Network + Logger *logging.Logger + Conf *ManagerConfig + tps map[uuid.UUID]*ManagedTransport + n *snet.Network + arClient arclient.APIClient listenersMu sync.Mutex listeners []*snet.Listener @@ -56,7 +57,7 @@ type Manager struct { // NewManager creates a Manager with the provided configuration and transport factories. // 'factories' should be ordered by preference. -func NewManager(log *logging.Logger, n *snet.Network, config *ManagerConfig) (*Manager, error) { +func NewManager(log *logging.Logger, n *snet.Network, arClient arclient.APIClient, config *ManagerConfig) (*Manager, error) { if log == nil { log = logging.MustGetLogger("tp_manager") } @@ -68,6 +69,7 @@ func NewManager(log *logging.Logger, n *snet.Network, config *ManagerConfig) (*M n: n, readCh: make(chan routing.Packet, 20), done: make(chan struct{}), + arClient: arClient, } return tm, nil } @@ -363,9 +365,8 @@ func (tm *Manager) saveTransport(remote cipher.PubKey, netName string, label Lab }) if mTp.netName == tptypes.STCPR { - ar := mTp.n.Conf().ARClient - if ar != nil { - visorData, err := ar.Resolve(context.Background(), mTp.netName, remote) + if tm.arClient != nil { + visorData, err := tm.arClient.Resolve(context.Background(), mTp.netName, remote) if err == nil { mTp.remoteAddr = visorData.RemoteAddr } else { @@ -531,7 +532,7 @@ func CreateTransportPair( // Prepare tp manager 0. pk0, sk0 := keys[0].PK, keys[0].SK ls0 := InMemoryTransportLogStore() - m0, err = NewManager(nil, nEnv.Nets[0], &ManagerConfig{ + m0, err = NewManager(nil, nEnv.Nets[0], new(arclient.MockAPIClient), &ManagerConfig{ PubKey: pk0, SecKey: sk0, DiscoveryClient: tpDisc, @@ -546,7 +547,7 @@ func CreateTransportPair( // Prepare tp manager 1. pk1, sk1 := keys[1].PK, keys[1].SK ls1 := InMemoryTransportLogStore() - m1, err = NewManager(nil, nEnv.Nets[1], &ManagerConfig{ + m1, err = NewManager(nil, nEnv.Nets[1], new(arclient.MockAPIClient), &ManagerConfig{ PubKey: pk1, SecKey: sk1, DiscoveryClient: tpDisc, diff --git a/pkg/transport/manager_test.go b/pkg/transport/manager_test.go index 1a37b2e44..f8f1150db 100644 --- a/pkg/transport/manager_test.go +++ b/pkg/transport/manager_test.go @@ -16,6 +16,7 @@ import ( "github.com/stretchr/testify/require" "github.com/skycoin/skywire/pkg/routing" + "github.com/skycoin/skywire/pkg/snet/arclient" "github.com/skycoin/skywire/pkg/snet/snettest" "github.com/skycoin/skywire/pkg/transport" ) @@ -49,7 +50,7 @@ func TestNewManager(t *testing.T) { // Prepare tp manager 0. pk0, sk0 := keys[0].PK, keys[0].SK ls0 := transport.InMemoryTransportLogStore() - m0, err := transport.NewManager(nil, nEnv.Nets[0], &transport.ManagerConfig{ + m0, err := transport.NewManager(nil, nEnv.Nets[0], new(arclient.MockAPIClient), &transport.ManagerConfig{ PubKey: pk0, SecKey: sk0, DiscoveryClient: tpDisc, @@ -62,7 +63,7 @@ func TestNewManager(t *testing.T) { // Prepare tp manager 1. pk1, sk1 := keys[1].PK, keys[1].SK ls1 := transport.InMemoryTransportLogStore() - m2, err := transport.NewManager(nil, nEnv.Nets[1], &transport.ManagerConfig{ + m2, err := transport.NewManager(nil, nEnv.Nets[1], new(arclient.MockAPIClient), &transport.ManagerConfig{ PubKey: pk1, SecKey: sk1, DiscoveryClient: tpDisc, diff --git a/pkg/visor/init.go b/pkg/visor/init.go index e1e325c5e..b78636adf 100644 --- a/pkg/visor/init.go +++ b/pkg/visor/init.go @@ -105,7 +105,7 @@ func registerModules(logger *logging.MasterLogger) { sn = maker("snet", initSNet, &ar, &disc, &ebc) dmsgCtrl = maker("dmsg_ctrl", initDmsgCtrl, &sn) pty = maker("dmsg_pty", initDmsgpty, &sn) - tr = maker("transport", initTransport, &sn, &ebc) + tr = maker("transport", initTransport, &ar, &sn, &ebc) rt = maker("router", initRouter, &tr, &sn) launch = maker("launcher", initLauncher, &ebc, &disc, &sn, &tr, &rt) cli = maker("cli", initCLI) @@ -186,11 +186,10 @@ func initSNet(ctx context.Context, v *Visor, log *logging.Logger) error { conf := snet.Config{ PubKey: v.conf.PK, SecKey: v.conf.SK, - ARClient: v.arClient, NetworkConfigs: nc, } - n, err := snet.New(conf, v.ebc) + n, err := snet.New(conf, v.ebc, v.arClient) if err != nil { return err } @@ -262,7 +261,7 @@ func initTransport(ctx context.Context, v *Visor, log *logging.Logger) error { LogStore: logS, } managerLogger := v.MasterLogger().PackageLogger("transport_manager") - tpM, err := transport.NewManager(managerLogger, v.net, &tpMConf) + tpM, err := transport.NewManager(managerLogger, v.net, v.arClient, &tpMConf) if err != nil { err := fmt.Errorf("failed to start transport manager: %w", err) return err From 24ec49d177417e5e57aae39b4ad42db68a3df694 Mon Sep 17 00:00:00 2001 From: Never M Date: Mon, 31 May 2021 13:38:33 +0300 Subject: [PATCH 02/55] Move dmsg client to its own module --- pkg/setup/config.go | 12 +++--- pkg/snet/dmsgc/dmsgc.go | 42 +++++++++++++++++++ pkg/snet/network.go | 73 +++++---------------------------- pkg/snet/snettest/env.go | 19 +++------ pkg/visor/init.go | 35 +++++++++++++--- pkg/visor/init_windows.go | 6 +++ pkg/visor/visor.go | 4 +- pkg/visor/visor_test.go | 3 +- pkg/visor/visorconfig/config.go | 3 +- pkg/visor/visorconfig/v0.go | 3 +- pkg/visor/visorconfig/v1.go | 15 +++---- 11 files changed, 116 insertions(+), 99 deletions(-) create mode 100644 pkg/snet/dmsgc/dmsgc.go diff --git a/pkg/setup/config.go b/pkg/setup/config.go index c99127240..9fa21d8eb 100644 --- a/pkg/setup/config.go +++ b/pkg/setup/config.go @@ -5,7 +5,7 @@ import ( "github.com/skycoin/dmsg/cipher" - "github.com/skycoin/skywire/pkg/snet" + "github.com/skycoin/skywire/pkg/snet/dmsgc" ) //go:generate readmegen -n Config -o ./README.md ./config.go @@ -18,9 +18,9 @@ const ( // Config defines configuration parameters for setup Node. type Config struct { - PK cipher.PubKey `json:"public_key"` - SK cipher.SecKey `json:"secret_key"` - Dmsg snet.DmsgConfig `json:"dmsg"` - TransportDiscovery string `json:"transport_discovery"` - LogLevel string `json:"log_level"` + PK cipher.PubKey `json:"public_key"` + SK cipher.SecKey `json:"secret_key"` + Dmsg dmsgc.DmsgConfig `json:"dmsg"` + TransportDiscovery string `json:"transport_discovery"` + LogLevel string `json:"log_level"` } diff --git a/pkg/snet/dmsgc/dmsgc.go b/pkg/snet/dmsgc/dmsgc.go new file mode 100644 index 000000000..bd6d36a36 --- /dev/null +++ b/pkg/snet/dmsgc/dmsgc.go @@ -0,0 +1,42 @@ +package dmsgc + +import ( + "context" + + "github.com/skycoin/dmsg" + "github.com/skycoin/dmsg/cipher" + "github.com/skycoin/dmsg/disc" + "github.com/skycoin/skycoin/src/util/logging" + + "github.com/skycoin/skywire/pkg/app/appevent" +) + +// DmsgConfig defines config for Dmsg network. +type DmsgConfig struct { + Discovery string `json:"discovery"` + SessionsCount int `json:"sessions_count"` +} + +// New makes new dmsg client from configuration +func New(pk cipher.PubKey, sk cipher.SecKey, eb *appevent.Broadcaster, conf *DmsgConfig) *dmsg.Client { + dmsgConf := &dmsg.Config{ + MinSessions: conf.SessionsCount, + Callbacks: &dmsg.ClientCallbacks{ + OnSessionDial: func(network, addr string) error { + data := appevent.TCPDialData{RemoteNet: network, RemoteAddr: addr} + event := appevent.NewEvent(appevent.TCPDial, data) + _ = eb.Broadcast(context.Background(), event) //nolint:errcheck + // @evanlinjin: An error is not returned here as this will cancel the session dial. + return nil + }, + OnSessionDisconnect: func(network, addr string, _ error) { + data := appevent.TCPCloseData{RemoteNet: network, RemoteAddr: addr} + event := appevent.NewEvent(appevent.TCPClose, data) + _ = eb.Broadcast(context.Background(), event) //nolint:errcheck + }, + }, + } + dmsgC := dmsg.NewClient(pk, sk, disc.NewHTTP(conf.Discovery), dmsgConf) + dmsgC.SetLogger(logging.MustGetLogger("dmsgC")) + return dmsgC +} diff --git a/pkg/snet/network.go b/pkg/snet/network.go index 0888e1c3a..eda000136 100644 --- a/pkg/snet/network.go +++ b/pkg/snet/network.go @@ -7,11 +7,9 @@ import ( "net" "strings" "sync" - "time" "github.com/skycoin/dmsg" "github.com/skycoin/dmsg/cipher" - "github.com/skycoin/dmsg/disc" "github.com/skycoin/skycoin/src/util/logging" "github.com/skycoin/skywire/pkg/app/appevent" @@ -45,17 +43,6 @@ type NetworkConfig interface { Type() string } -// DmsgConfig defines config for Dmsg network. -type DmsgConfig struct { - Discovery string `json:"discovery"` - SessionsCount int `json:"sessions_count"` -} - -// Type returns DmsgType. -func (c *DmsgConfig) Type() string { - return dmsg.Type -} - // STCPConfig defines config for STCP network. type STCPConfig struct { PKTable map[cipher.PubKey]string `json:"pk_table"` @@ -76,13 +63,11 @@ type Config struct { // NetworkConfigs represents all network configs. type NetworkConfigs struct { - Dmsg *DmsgConfig // The dmsg service will not be started if nil. STCP *STCPConfig // The stcp service will not be started if nil. } // NetworkClients represents all network clients. type NetworkClients struct { - DmsgC *dmsg.Client Direct map[string]directtp.Client } @@ -96,36 +81,15 @@ type Network struct { arc arclient.APIClient onNewNetworkTypeMu sync.Mutex onNewNetworkType func(netType string) + dmsgC *dmsg.Client } // New creates a network from a config. -func New(conf Config, eb *appevent.Broadcaster, arc arclient.APIClient) (*Network, error) { +func New(conf Config, dmsgC *dmsg.Client, eb *appevent.Broadcaster, arc arclient.APIClient) (*Network, error) { clients := NetworkClients{ Direct: make(map[string]directtp.Client), } - if conf.NetworkConfigs.Dmsg != nil { - dmsgConf := &dmsg.Config{ - MinSessions: conf.NetworkConfigs.Dmsg.SessionsCount, - Callbacks: &dmsg.ClientCallbacks{ - OnSessionDial: func(network, addr string) error { - data := appevent.TCPDialData{RemoteNet: network, RemoteAddr: addr} - event := appevent.NewEvent(appevent.TCPDial, data) - _ = eb.Broadcast(context.Background(), event) //nolint:errcheck - // @evanlinjin: An error is not returned here as this will cancel the session dial. - return nil - }, - OnSessionDisconnect: func(network, addr string, _ error) { - data := appevent.TCPCloseData{RemoteNet: network, RemoteAddr: addr} - event := appevent.NewEvent(appevent.TCPClose, data) - _ = eb.Broadcast(context.Background(), event) //nolint:errcheck - }, - }, - } - clients.DmsgC = dmsg.NewClient(conf.PubKey, conf.SecKey, disc.NewHTTP(conf.NetworkConfigs.Dmsg.Discovery), dmsgConf) - clients.DmsgC.SetLogger(logging.MustGetLogger("snet.dmsgC")) - } - if conf.NetworkConfigs.STCP != nil { conf := directtp.Config{ Type: tptypes.STCP, @@ -169,20 +133,21 @@ func New(conf Config, eb *appevent.Broadcaster, arc arclient.APIClient) (*Networ clients.Direct[tptypes.SUDPH] = directtp.NewClient(sudphConf) } - return NewRaw(conf, clients, arc), nil + return NewRaw(conf, clients, dmsgC, arc), nil } // NewRaw creates a network from a config and a dmsg client. -func NewRaw(conf Config, clients NetworkClients, arc arclient.APIClient) *Network { +func NewRaw(conf Config, clients NetworkClients, dmsgC *dmsg.Client, arc arclient.APIClient) *Network { n := &Network{ conf: conf, nets: make(map[string]struct{}), clients: clients, arc: arc, + dmsgC: dmsgC, } - if clients.DmsgC != nil { - n.addNetworkType(dmsg.Type) + if dmsgC != nil { + n.addNetworkType(dmsgC.Type()) } for k, v := range clients.Direct { @@ -201,11 +166,6 @@ func (n *Network) Conf() Config { // Init initiates server connections. func (n *Network) Init() error { - if n.clients.DmsgC != nil { - time.Sleep(200 * time.Millisecond) - go n.clients.DmsgC.Serve(context.Background()) - time.Sleep(200 * time.Millisecond) - } if n.conf.NetworkConfigs.STCP != nil { if client, ok := n.clients.Direct[tptypes.STCP]; ok && client != nil && n.conf.NetworkConfigs.STCP.LocalAddr != "" { @@ -260,15 +220,6 @@ func (n *Network) Close() error { wg := new(sync.WaitGroup) - var dmsgErr error - if n.clients.DmsgC != nil { - wg.Add(1) - go func() { - dmsgErr = n.clients.DmsgC.Close() - wg.Done() - }() - } - var directErrorsMu sync.Mutex directErrors := make(map[string]error) @@ -289,10 +240,6 @@ func (n *Network) Close() error { wg.Wait() - if dmsgErr != nil { - return dmsgErr - } - for _, err := range directErrors { if err != nil { return err @@ -321,7 +268,7 @@ func (n *Network) TransportNetworks() []string { } // Dmsg returns underlying dmsg client. -func (n *Network) Dmsg() *dmsg.Client { return n.clients.DmsgC } +func (n *Network) Dmsg() *dmsg.Client { return n.dmsgC } // STcp returns the underlying stcp.Client. func (n *Network) STcp() (directtp.Client, bool) { @@ -352,7 +299,7 @@ func (n *Network) Dial(ctx context.Context, network string, pk cipher.PubKey, po Port: port, } - conn, err := n.clients.DmsgC.Dial(ctx, addr) + conn, err := n.dmsgC.Dial(ctx, addr) if err != nil { return nil, fmt.Errorf("dmsg client dial %v: %w", addr, err) } @@ -378,7 +325,7 @@ func (n *Network) Dial(ctx context.Context, network string, pk cipher.PubKey, po func (n *Network) Listen(network string, port uint16) (*Listener, error) { switch network { case dmsg.Type: - lis, err := n.clients.DmsgC.Listen(port) + lis, err := n.dmsgC.Listen(port) if err != nil { return nil, err } diff --git a/pkg/snet/snettest/env.go b/pkg/snet/snettest/env.go index 7f9f6659f..729f922a0 100644 --- a/pkg/snet/snettest/env.go +++ b/pkg/snet/snettest/env.go @@ -1,7 +1,9 @@ package snettest +// todo: this seems like an integration test, we set up a real dmsg server here +// it doesn't test sending data, but it probably should +// for now (while refactoring snet) dmsg parts are cut out import ( - "context" "strconv" "testing" @@ -65,12 +67,10 @@ func NewEnv(t *testing.T, keys []KeyPair, networks []string) *Env { table := pktable.NewTable(tableEntries) - var hasDmsg, hasStcp, hasStcpr, hasSudph bool + var hasStcp, hasStcpr, hasSudph bool for _, network := range networks { switch network { - case dmsg.Type: - hasDmsg = true case tptypes.STCP: hasStcp = true case tptypes.STCPR: @@ -87,9 +87,6 @@ func NewEnv(t *testing.T, keys []KeyPair, networks []string) *Env { for i, pairs := range keys { networkConfigs := snet.NetworkConfigs{ - Dmsg: &snet.DmsgConfig{ - SessionsCount: 1, - }, STCP: &snet.STCPConfig{ LocalAddr: "127.0.0.1:" + strconv.Itoa(stcpBasePort+i), }, @@ -99,11 +96,6 @@ func NewEnv(t *testing.T, keys []KeyPair, networks []string) *Env { Direct: make(map[string]directtp.Client), } - if hasDmsg { - clients.DmsgC = dmsg.NewClient(pairs.PK, pairs.SK, dmsgD, nil) - go clients.DmsgC.Serve(context.Background()) - } - addressResolver := new(arclient.MockAPIClient) if hasStcp { @@ -146,7 +138,8 @@ func NewEnv(t *testing.T, keys []KeyPair, networks []string) *Env { NetworkConfigs: networkConfigs, } - n := snet.NewRaw(snetConfig, clients, addressResolver) + // todo: dmsg is not set up here + n := snet.NewRaw(snetConfig, clients, nil, addressResolver) require.NoError(t, n.Init()) ns[i] = n } diff --git a/pkg/visor/init.go b/pkg/visor/init.go index b78636adf..bc78d3fd6 100644 --- a/pkg/visor/init.go +++ b/pkg/visor/init.go @@ -10,12 +10,11 @@ import ( "sync" "time" - "github.com/skycoin/dmsg" - dmsgnetutil "github.com/skycoin/dmsg/netutil" - "github.com/sirupsen/logrus" + "github.com/skycoin/dmsg" "github.com/skycoin/dmsg/cipher" "github.com/skycoin/dmsg/dmsgctrl" + dmsgnetutil "github.com/skycoin/dmsg/netutil" "github.com/skycoin/skycoin/src/util/logging" "github.com/skycoin/skywire/internal/utclient" @@ -32,6 +31,7 @@ import ( "github.com/skycoin/skywire/pkg/snet" "github.com/skycoin/skywire/pkg/snet/arclient" "github.com/skycoin/skywire/pkg/snet/directtp/tptypes" + "github.com/skycoin/skywire/pkg/snet/dmsgc" "github.com/skycoin/skywire/pkg/transport" "github.com/skycoin/skywire/pkg/transport/tpdclient" "github.com/skycoin/skywire/pkg/util/netutil" @@ -66,6 +66,8 @@ var ( sn vinit.Module // dmsg pty: a remote terminal to the visor working over dmsg protocol pty vinit.Module + // Dmsg module + dmsgC vinit.Module // Transport setup tr vinit.Module // Routing system @@ -102,7 +104,8 @@ func registerModules(logger *logging.MasterLogger) { ebc = maker("event_broadcaster", initEventBroadcaster) ar = maker("address_resolver", initAddressResolver) disc = maker("discovery", initDiscovery) - sn = maker("snet", initSNet, &ar, &disc, &ebc) + dmsgC = maker("dmsg", initDmsg) + sn = maker("snet", initSNet, &dmsgC, &ar, &disc, &ebc) dmsgCtrl = maker("dmsg_ctrl", initDmsgCtrl, &sn) pty = maker("dmsg_pty", initDmsgpty, &sn) tr = maker("transport", initTransport, &ar, &sn, &ebc) @@ -177,9 +180,29 @@ func initDiscovery(ctx context.Context, v *Visor, log *logging.Logger) error { return nil } +func initDmsg(ctx context.Context, v *Visor, log *logging.Logger) error { + if v.conf.Dmsg == nil { + // todo: this is preserved behavior from snet. Do we need it? + return nil + } + dmsgC := dmsgc.New(v.conf.PK, v.conf.SK, v.ebc, v.conf.Dmsg) + + time.Sleep(200 * time.Millisecond) + go dmsgC.Serve(context.Background()) + time.Sleep(200 * time.Millisecond) + + v.initLock.Lock() + v.dmsgC = dmsgC + v.initLock.Unlock() + + v.pushCloseStack("dmsgC", func() error { + return dmsgC.Close() + }) + return nil +} + func initSNet(ctx context.Context, v *Visor, log *logging.Logger) error { nc := snet.NetworkConfigs{ - Dmsg: v.conf.Dmsg, STCP: v.conf.STCP, } @@ -189,7 +212,7 @@ func initSNet(ctx context.Context, v *Visor, log *logging.Logger) error { NetworkConfigs: nc, } - n, err := snet.New(conf, v.ebc, v.arClient) + n, err := snet.New(conf, v.dmsgC, v.ebc, v.arClient) if err != nil { return err } diff --git a/pkg/visor/init_windows.go b/pkg/visor/init_windows.go index 714ad14c9..7340510c4 100644 --- a/pkg/visor/init_windows.go +++ b/pkg/visor/init_windows.go @@ -2,6 +2,12 @@ package visor +import ( + "context" + + "github.com/skycoin/skycoin/src/util/logging" +) + func initDmsgpty(ctx context.Context, log *logging.Logger) error { log.Error("dmsgpty is not supported on windows.") return nil diff --git a/pkg/visor/visor.go b/pkg/visor/visor.go index 9a8277e07..b2a3cadff 100644 --- a/pkg/visor/visor.go +++ b/pkg/visor/visor.go @@ -8,6 +8,7 @@ import ( "sync" "time" + "github.com/skycoin/dmsg" "github.com/skycoin/skycoin/src/util/logging" "github.com/skycoin/skywire/internal/utclient" @@ -54,7 +55,8 @@ type Visor struct { updater *updater.Updater uptimeTracker utclient.APIClient - ebc *appevent.Broadcaster // event broadcaster + ebc *appevent.Broadcaster // event broadcaster + dmsgC *dmsg.Client net *snet.Network tpM *transport.Manager diff --git a/pkg/visor/visor_test.go b/pkg/visor/visor_test.go index 72efe2110..0272e36ed 100644 --- a/pkg/visor/visor_test.go +++ b/pkg/visor/visor_test.go @@ -18,6 +18,7 @@ import ( "github.com/skycoin/skywire/pkg/restart" "github.com/skycoin/skywire/pkg/skyenv" "github.com/skycoin/skywire/pkg/snet" + "github.com/skycoin/skywire/pkg/snet/dmsgc" "github.com/skycoin/skywire/pkg/visor/visorconfig" ) @@ -51,7 +52,7 @@ func TestNewVisor(t *testing.T) { PK: pk, SK: sk, }, - Dmsg: &snet.DmsgConfig{ + Dmsg: &dmsgc.DmsgConfig{ Discovery: skyenv.DefaultDmsgDiscAddr, SessionsCount: 10, }, diff --git a/pkg/visor/visorconfig/config.go b/pkg/visor/visorconfig/config.go index 18195ae90..a1636d7b1 100644 --- a/pkg/visor/visorconfig/config.go +++ b/pkg/visor/visorconfig/config.go @@ -9,6 +9,7 @@ import ( "github.com/skycoin/skywire/pkg/routing" "github.com/skycoin/skywire/pkg/skyenv" "github.com/skycoin/skywire/pkg/snet" + "github.com/skycoin/skywire/pkg/snet/dmsgc" "github.com/skycoin/skywire/pkg/visor/hypervisorconfig" ) @@ -18,7 +19,7 @@ import ( func MakeBaseConfig(common *Common) *V1 { conf := new(V1) conf.Common = common - conf.Dmsg = &snet.DmsgConfig{ + conf.Dmsg = &dmsgc.DmsgConfig{ Discovery: skyenv.DefaultDmsgDiscAddr, SessionsCount: 1, } diff --git a/pkg/visor/visorconfig/v0.go b/pkg/visor/visorconfig/v0.go index a80b0c72e..415a6d625 100644 --- a/pkg/visor/visorconfig/v0.go +++ b/pkg/visor/visorconfig/v0.go @@ -5,6 +5,7 @@ import ( "github.com/skycoin/skywire/pkg/routing" "github.com/skycoin/skywire/pkg/snet" + "github.com/skycoin/skywire/pkg/snet/dmsgc" ) // V0Name is the version string before proper versioning is implemented. @@ -20,7 +21,7 @@ type V0 struct { SecKey cipher.SecKey `json:"secret_key"` } `json:"key_pair"` - Dmsg *snet.DmsgConfig `json:"dmsg"` + Dmsg *dmsgc.DmsgConfig `json:"dmsg"` DmsgPty *V1Dmsgpty `json:"dmsg_pty,omitempty"` diff --git a/pkg/visor/visorconfig/v1.go b/pkg/visor/visorconfig/v1.go index 59ec1fdd9..34cf4a0d9 100644 --- a/pkg/visor/visorconfig/v1.go +++ b/pkg/visor/visorconfig/v1.go @@ -9,6 +9,7 @@ import ( "github.com/skycoin/skywire/pkg/app/launcher" "github.com/skycoin/skywire/pkg/snet" + "github.com/skycoin/skywire/pkg/snet/dmsgc" "github.com/skycoin/skywire/pkg/visor/hypervisorconfig" ) @@ -36,13 +37,13 @@ type V1 struct { *Common mu sync.RWMutex - Dmsg *snet.DmsgConfig `json:"dmsg"` - Dmsgpty *V1Dmsgpty `json:"dmsgpty,omitempty"` - STCP *snet.STCPConfig `json:"stcp,omitempty"` - Transport *V1Transport `json:"transport"` - Routing *V1Routing `json:"routing"` - UptimeTracker *V1UptimeTracker `json:"uptime_tracker,omitempty"` - Launcher *V1Launcher `json:"launcher"` + Dmsg *dmsgc.DmsgConfig `json:"dmsg"` + Dmsgpty *V1Dmsgpty `json:"dmsgpty,omitempty"` + STCP *snet.STCPConfig `json:"stcp,omitempty"` + Transport *V1Transport `json:"transport"` + Routing *V1Routing `json:"routing"` + UptimeTracker *V1UptimeTracker `json:"uptime_tracker,omitempty"` + Launcher *V1Launcher `json:"launcher"` Hypervisors []cipher.PubKey `json:"hypervisors"` CLIAddr string `json:"cli_addr"` From 240949bc9270371506d14d29c059672165296478 Mon Sep 17 00:00:00 2001 From: Never M Date: Mon, 31 May 2021 13:47:12 +0300 Subject: [PATCH 03/55] Change dependencies to use dmsgC module --- pkg/snet/network.go | 3 --- pkg/visor/api.go | 2 +- pkg/visor/init.go | 24 ++++++++++++------------ pkg/visor/init_unix.go | 2 +- 4 files changed, 14 insertions(+), 17 deletions(-) diff --git a/pkg/snet/network.go b/pkg/snet/network.go index eda000136..28fcf209d 100644 --- a/pkg/snet/network.go +++ b/pkg/snet/network.go @@ -267,9 +267,6 @@ func (n *Network) TransportNetworks() []string { return networks } -// Dmsg returns underlying dmsg client. -func (n *Network) Dmsg() *dmsg.Client { return n.dmsgC } - // STcp returns the underlying stcp.Client. func (n *Network) STcp() (directtp.Client, bool) { return n.getClient(tptypes.STCP) diff --git a/pkg/visor/api.go b/pkg/visor/api.go index 0b05465ea..f0f7229f0 100644 --- a/pkg/visor/api.go +++ b/pkg/visor/api.go @@ -276,7 +276,7 @@ func (v *Visor) StartApp(appName string) error { if appName == skyenv.VPNClientName { // todo: can we use some kind of app start hook that will be used for both autostart // and start? Reason: this is also called in init for autostart - maker := vpnEnvMaker(v.conf, v.net, v.tpM.STCPRRemoteAddrs()) + maker := vpnEnvMaker(v.conf, v.dmsgC, v.tpM.STCPRRemoteAddrs()) envs, err = maker() if err != nil { return err diff --git a/pkg/visor/init.go b/pkg/visor/init.go index bc78d3fd6..04fbd5edc 100644 --- a/pkg/visor/init.go +++ b/pkg/visor/init.go @@ -104,13 +104,13 @@ func registerModules(logger *logging.MasterLogger) { ebc = maker("event_broadcaster", initEventBroadcaster) ar = maker("address_resolver", initAddressResolver) disc = maker("discovery", initDiscovery) - dmsgC = maker("dmsg", initDmsg) + dmsgC = maker("dmsg", initDmsg, &ebc) sn = maker("snet", initSNet, &dmsgC, &ar, &disc, &ebc) - dmsgCtrl = maker("dmsg_ctrl", initDmsgCtrl, &sn) - pty = maker("dmsg_pty", initDmsgpty, &sn) + dmsgCtrl = maker("dmsg_ctrl", initDmsgCtrl, &dmsgC) + pty = maker("dmsg_pty", initDmsgpty, &dmsgC) tr = maker("transport", initTransport, &ar, &sn, &ebc) rt = maker("router", initRouter, &tr, &sn) - launch = maker("launcher", initLauncher, &ebc, &disc, &sn, &tr, &rt) + launch = maker("launcher", initLauncher, &ebc, &disc, &dmsgC, &tr, &rt) cli = maker("cli", initCLI) hvs = maker("hypervisors", initHypervisors, &sn) ut = maker("uptime_tracker", initUptimeTracker) @@ -229,7 +229,7 @@ func initSNet(ctx context.Context, v *Visor, log *logging.Logger) error { } func initDmsgCtrl(ctx context.Context, v *Visor, _ *logging.Logger) error { - dmsgC := v.net.Dmsg() + dmsgC := v.dmsgC if dmsgC == nil { return nil } @@ -239,7 +239,7 @@ func initDmsgCtrl(ctx context.Context, v *Visor, _ *logging.Logger) error { select { case <-time.After(dmsgTimeout): logger.Warn("Failed to connect to the dmsg network, will try again later.") - case <-v.net.Dmsg().Ready(): + case <-v.dmsgC.Ready(): logger.Info("Connected to the dmsg network.") } // dmsgctrl setup @@ -411,15 +411,15 @@ func initLauncher(ctx context.Context, v *Visor, log *logging.Logger) error { launchLog := v.MasterLogger().PackageLogger("launcher") - launch, err := launcher.NewLauncher(launchLog, launchConf, v.net.Dmsg(), v.router, procM) + launch, err := launcher.NewLauncher(launchLog, launchConf, v.dmsgC, v.router, procM) if err != nil { err := fmt.Errorf("failed to start launcher: %w", err) return err } err = launch.AutoStart(launcher.EnvMap{ - skyenv.VPNClientName: vpnEnvMaker(v.conf, v.net, v.tpM.STCPRRemoteAddrs()), - skyenv.VPNServerName: vpnEnvMaker(v.conf, v.net, nil), + skyenv.VPNClientName: vpnEnvMaker(v.conf, v.dmsgC, v.tpM.STCPRRemoteAddrs()), + skyenv.VPNServerName: vpnEnvMaker(v.conf, v.dmsgC, nil), }) if err != nil { @@ -436,7 +436,7 @@ func initLauncher(ctx context.Context, v *Visor, log *logging.Logger) error { } // Make an env maker function for vpn application -func vpnEnvMaker(conf *visorconfig.V1, n *snet.Network, tpRemoteAddrs []string) launcher.EnvMaker { +func vpnEnvMaker(conf *visorconfig.V1, dmsgC *dmsg.Client, tpRemoteAddrs []string) launcher.EnvMaker { return launcher.EnvMaker(func() ([]string, error) { var envCfg vpn.DirectRoutesEnvConfig @@ -445,7 +445,7 @@ func vpnEnvMaker(conf *visorconfig.V1, n *snet.Network, tpRemoteAddrs []string) r := dmsgnetutil.NewRetrier(logrus.New(), 1*time.Second, 10*time.Second, 0, 1) err := r.Do(context.Background(), func() error { - for _, ses := range n.Dmsg().AllSessions() { + for _, ses := range dmsgC.AllSessions() { envCfg.DmsgServers = append(envCfg.DmsgServers, ses.RemoteTCPAddr().String()) } @@ -681,7 +681,7 @@ func initHypervisor(_ context.Context, v *Visor, log *logging.Logger) error { conf.DmsgDiscovery = v.conf.Dmsg.Discovery // Prepare hypervisor. - hv, err := New(conf, v, v.net.Dmsg()) + hv, err := New(conf, v, v.dmsgC) if err != nil { v.log.Fatalln("Failed to start hypervisor:", err) } diff --git a/pkg/visor/init_unix.go b/pkg/visor/init_unix.go index 2a25d7196..b5a636931 100644 --- a/pkg/visor/init_unix.go +++ b/pkg/visor/init_unix.go @@ -53,7 +53,7 @@ func initDmsgpty(ctx context.Context, v *Visor, log *logging.Logger) error { v.log.Errorf("Cannot add itself to the pty whitelist: %s", err) } - dmsgC := v.net.Dmsg() + dmsgC := v.dmsgC if dmsgC == nil { err := errors.New("cannot create dmsgpty with nil dmsg client") return err From 1be07a108083844a6655b052d3339ff93eacb01b Mon Sep 17 00:00:00 2001 From: Never M Date: Tue, 1 Jun 2021 17:12:36 +0300 Subject: [PATCH 04/55] Move snet into transport manager --- pkg/router/router.go | 20 +++++----- pkg/setup/setupclient/client.go | 15 ++++---- pkg/setup/setupclient/wrappers.go | 8 ++-- pkg/transport/manager.go | 6 +++ pkg/visor/init.go | 64 +++++++++++++++---------------- pkg/visor/rpc_client_serve.go | 10 ++--- pkg/visor/visor.go | 2 - 7 files changed, 62 insertions(+), 63 deletions(-) diff --git a/pkg/router/router.go b/pkg/router/router.go index ecfdd4464..1b9f4ee88 100644 --- a/pkg/router/router.go +++ b/pkg/router/router.go @@ -21,7 +21,6 @@ import ( "github.com/skycoin/skywire/pkg/routing" "github.com/skycoin/skywire/pkg/setup/setupclient" "github.com/skycoin/skywire/pkg/skyenv" - "github.com/skycoin/skywire/pkg/snet" "github.com/skycoin/skywire/pkg/snet/directtp/noisewrapper" "github.com/skycoin/skywire/pkg/transport" ) @@ -145,8 +144,8 @@ type router struct { mx sync.Mutex conf *Config logger *logging.Logger - n *snet.Network - sl *snet.Listener + sl *dmsg.Listener + dmsgC *dmsg.Client trustedVisors map[cipher.PubKey]struct{} tm *transport.Manager rt routing.Table @@ -160,10 +159,10 @@ type router struct { } // New constructs a new Router. -func New(n *snet.Network, config *Config) (Router, error) { +func New(dmsgC *dmsg.Client, config *Config) (Router, error) { config.SetDefaults() - sl, err := n.Listen(dmsg.Type, skyenv.DmsgAwaitSetupPort) + sl, err := dmsgC.Listen(skyenv.DmsgAwaitSetupPort) if err != nil { return nil, err } @@ -176,10 +175,10 @@ func New(n *snet.Network, config *Config) (Router, error) { r := &router{ conf: config, logger: config.Logger, - n: n, tm: config.TransportManager, rt: routing.NewTable(), sl: sl, + dmsgC: dmsgC, rgsNs: make(map[routing.RouteDescriptor]*NoiseRouteGroup), rgsRaw: make(map[routing.RouteDescriptor]*RouteGroup), rpcSrv: rpc.NewServer(), @@ -233,7 +232,7 @@ func (r *router) DialRoutes( Reverse: reversePath, } - rules, err := r.conf.RouteGroupDialer.Dial(ctx, r.logger, r.n, r.conf.SetupNodes, req) + rules, err := r.conf.RouteGroupDialer.Dial(ctx, r.logger, r.dmsgC, r.conf.SetupNodes, req) if err != nil { r.logger.WithError(err).Error("Error dialing route group") return nil, err @@ -356,7 +355,7 @@ func (r *router) serveTransportManager(ctx context.Context) { func (r *router) serveSetup() { for { - conn, err := r.sl.AcceptConn() + conn, err := r.sl.AcceptStream() if err != nil { log := r.logger.WithError(err) if err == dmsg.ErrEntityClosed { @@ -367,12 +366,13 @@ func (r *router) serveSetup() { return } - if !r.SetupIsTrusted(conn.RemotePK()) { + remotePK := conn.RawRemoteAddr().PK + if !r.SetupIsTrusted(remotePK) { r.logger.Warnf("closing conn from untrusted setup node: %v", conn.Close()) continue } - r.logger.Infof("handling setup request: setupPK(%s)", conn.RemotePK()) + r.logger.Infof("handling setup request: setupPK(%s)", remotePK) go r.rpcSrv.ServeConn(conn) } diff --git a/pkg/setup/setupclient/client.go b/pkg/setup/setupclient/client.go index 5c3b13c84..0e3cde926 100644 --- a/pkg/setup/setupclient/client.go +++ b/pkg/setup/setupclient/client.go @@ -3,6 +3,7 @@ package setupclient import ( "context" "errors" + "net" "net/rpc" "github.com/skycoin/dmsg" @@ -11,7 +12,6 @@ import ( "github.com/skycoin/skywire/pkg/routing" "github.com/skycoin/skywire/pkg/skyenv" - "github.com/skycoin/skywire/pkg/snet" ) const rpcName = "RPCGateway" @@ -19,21 +19,19 @@ const rpcName = "RPCGateway" // Client is an RPC client for setup node. type Client struct { log *logging.Logger - n *snet.Network setupNodes []cipher.PubKey - conn *snet.Conn + conn net.Conn rpc *rpc.Client } // NewClient creates a new Client. -func NewClient(ctx context.Context, log *logging.Logger, n *snet.Network, setupNodes []cipher.PubKey) (*Client, error) { +func NewClient(ctx context.Context, log *logging.Logger, dmsgC *dmsg.Client, setupNodes []cipher.PubKey) (*Client, error) { client := &Client{ log: log, - n: n, setupNodes: setupNodes, } - conn, err := client.dial(ctx) + conn, err := client.dial(ctx, dmsgC) if err != nil { return nil, err } @@ -45,9 +43,10 @@ func NewClient(ctx context.Context, log *logging.Logger, n *snet.Network, setupN return client, nil } -func (c *Client) dial(ctx context.Context) (*snet.Conn, error) { +func (c *Client) dial(ctx context.Context, dmsgC *dmsg.Client) (net.Conn, error) { for _, sPK := range c.setupNodes { - conn, err := c.n.Dial(ctx, dmsg.Type, sPK, skyenv.DmsgSetupPort) + addr := dmsg.Addr{sPK, skyenv.DmsgSetupPort} + conn, err := dmsgC.Dial(ctx, addr) if err != nil { c.log.WithError(err).Warnf("failed to dial to setup node: setupPK(%s)", sPK) continue diff --git a/pkg/setup/setupclient/wrappers.go b/pkg/setup/setupclient/wrappers.go index 96bb4f5aa..030f91a81 100644 --- a/pkg/setup/setupclient/wrappers.go +++ b/pkg/setup/setupclient/wrappers.go @@ -4,11 +4,11 @@ import ( "context" "fmt" + "github.com/skycoin/dmsg" "github.com/skycoin/dmsg/cipher" "github.com/skycoin/skycoin/src/util/logging" "github.com/skycoin/skywire/pkg/routing" - "github.com/skycoin/skywire/pkg/snet" ) //go:generate mockery -name RouteGroupDialer -case underscore -inpkg @@ -18,7 +18,7 @@ type RouteGroupDialer interface { Dial( ctx context.Context, log *logging.Logger, - n *snet.Network, + dmsgC *dmsg.Client, setupNodes []cipher.PubKey, req routing.BidirectionalRoute, ) (routing.EdgeRules, error) @@ -35,11 +35,11 @@ func NewSetupNodeDialer() RouteGroupDialer { func (d *setupNodeDialer) Dial( ctx context.Context, log *logging.Logger, - n *snet.Network, + dmsgC *dmsg.Client, setupNodes []cipher.PubKey, req routing.BidirectionalRoute, ) (routing.EdgeRules, error) { - client, err := NewClient(ctx, log, n, setupNodes) + client, err := NewClient(ctx, log, dmsgC, setupNodes) if err != nil { return routing.EdgeRules{}, err } diff --git a/pkg/transport/manager.go b/pkg/transport/manager.go index 70ae74c24..c0ae29407 100644 --- a/pkg/transport/manager.go +++ b/pkg/transport/manager.go @@ -17,6 +17,7 @@ import ( "github.com/skycoin/skywire/pkg/skyenv" "github.com/skycoin/skywire/pkg/snet" "github.com/skycoin/skywire/pkg/snet/arclient" + "github.com/skycoin/skywire/pkg/snet/directtp" "github.com/skycoin/skywire/pkg/snet/directtp/tptypes" "github.com/skycoin/skywire/pkg/snet/snettest" ) @@ -74,6 +75,11 @@ func NewManager(log *logging.Logger, n *snet.Network, arClient arclient.APIClien return tm, nil } +// STcpr returns client for this transport and success status +func (tm *Manager) STcpr() (directtp.Client, bool) { + return tm.n.STcpr() +} + // OnAfterTPClosed sets callback which will fire after transport gets closed. func (tm *Manager) OnAfterTPClosed(f TPCloseCallback) { tm.mx.Lock() diff --git a/pkg/visor/init.go b/pkg/visor/init.go index 04fbd5edc..8303396fe 100644 --- a/pkg/visor/init.go +++ b/pkg/visor/init.go @@ -62,8 +62,6 @@ var ( ar vinit.Module // App discovery disc vinit.Module - // Snet (different network types) - sn vinit.Module // dmsg pty: a remote terminal to the visor working over dmsg protocol pty vinit.Module // Dmsg module @@ -105,18 +103,17 @@ func registerModules(logger *logging.MasterLogger) { ar = maker("address_resolver", initAddressResolver) disc = maker("discovery", initDiscovery) dmsgC = maker("dmsg", initDmsg, &ebc) - sn = maker("snet", initSNet, &dmsgC, &ar, &disc, &ebc) dmsgCtrl = maker("dmsg_ctrl", initDmsgCtrl, &dmsgC) pty = maker("dmsg_pty", initDmsgpty, &dmsgC) - tr = maker("transport", initTransport, &ar, &sn, &ebc) - rt = maker("router", initRouter, &tr, &sn) + tr = maker("transport", initTransport, &ar, &ebc) + rt = maker("router", initRouter, &tr, &dmsgC) launch = maker("launcher", initLauncher, &ebc, &disc, &dmsgC, &tr, &rt) cli = maker("cli", initCLI) - hvs = maker("hypervisors", initHypervisors, &sn) + hvs = maker("hypervisors", initHypervisors, &dmsgC) ut = maker("uptime_tracker", initUptimeTracker) pv = maker("public_visors", initPublicVisors, &tr) - pvs = maker("public_visor", initPublicVisor, &sn, &ar, &disc) - vis = vinit.MakeModule("visor", vinit.DoNothing, logger, &up, &ebc, &ar, &disc, &sn, &pty, + pvs = maker("public_visor", initPublicVisor, &tr, &ar, &disc) + vis = vinit.MakeModule("visor", vinit.DoNothing, logger, &up, &ebc, &ar, &disc, &pty, &tr, &rt, &launch, &cli, &hvs, &ut, &pv, &pvs, &dmsgCtrl) hv = maker("hypervisor", initHypervisor, &vis) @@ -202,29 +199,7 @@ func initDmsg(ctx context.Context, v *Visor, log *logging.Logger) error { } func initSNet(ctx context.Context, v *Visor, log *logging.Logger) error { - nc := snet.NetworkConfigs{ - STCP: v.conf.STCP, - } - - conf := snet.Config{ - PubKey: v.conf.PK, - SecKey: v.conf.SK, - NetworkConfigs: nc, - } - n, err := snet.New(conf, v.dmsgC, v.ebc, v.arClient) - if err != nil { - return err - } - - if err := n.Init(); err != nil { - return err - } - v.pushCloseStack("snet", n.Close) - - v.initLock.Lock() - v.net = n - v.initLock.Unlock() return nil } @@ -284,7 +259,28 @@ func initTransport(ctx context.Context, v *Visor, log *logging.Logger) error { LogStore: logS, } managerLogger := v.MasterLogger().PackageLogger("transport_manager") - tpM, err := transport.NewManager(managerLogger, v.net, v.arClient, &tpMConf) + + nc := snet.NetworkConfigs{ + STCP: v.conf.STCP, + } + + netconf := snet.Config{ + PubKey: v.conf.PK, + SecKey: v.conf.SK, + NetworkConfigs: nc, + } + + n, err := snet.New(netconf, v.dmsgC, v.ebc, v.arClient) + if err != nil { + return err + } + + if err := n.Init(); err != nil { + return err + } + v.pushCloseStack("snet", n.Close) + + tpM, err := transport.NewManager(managerLogger, n, v.arClient, &tpMConf) if err != nil { err := fmt.Errorf("failed to start transport manager: %w", err) return err @@ -338,7 +334,7 @@ func initRouter(ctx context.Context, v *Visor, log *logging.Logger) error { MinHops: v.conf.Routing.MinHops, } - r, err := router.New(v.net, &rConf) + r, err := router.New(v.dmsgC, &rConf) if err != nil { err := fmt.Errorf("failed to create router: %w", err) return err @@ -536,7 +532,7 @@ func initHypervisors(ctx context.Context, v *Visor, log *logging.Logger) error { go func(hvErrs chan error) { defer wg.Done() - ServeRPCClient(ctx, log, v.net, rpcS, addr, hvErrs) + ServeRPCClient(ctx, log, v.dmsgC, rpcS, addr, hvErrs) }(hvErrs) v.pushCloseStack("hypervisor."+hvPK.String()[:shortHashLen], func() error { @@ -613,7 +609,7 @@ func initPublicVisor(_ context.Context, v *Visor, log *logging.Logger) error { } // todo: consider moving this to transport into some helper function - stcpr, ok := v.net.STcpr() + stcpr, ok := v.tpM.STcpr() if !ok { return nil } diff --git a/pkg/visor/rpc_client_serve.go b/pkg/visor/rpc_client_serve.go index ec1760f1f..bbc7f082f 100644 --- a/pkg/visor/rpc_client_serve.go +++ b/pkg/visor/rpc_client_serve.go @@ -2,14 +2,13 @@ package visor import ( "context" + "net" "net/rpc" "time" "github.com/sirupsen/logrus" "github.com/skycoin/dmsg" "github.com/skycoin/dmsg/netutil" - - "github.com/skycoin/skywire/pkg/snet" ) func isDone(ctx context.Context) bool { @@ -22,15 +21,16 @@ func isDone(ctx context.Context) bool { } // ServeRPCClient repetitively dials to a remote dmsg address and serves a RPC server to that address. -func ServeRPCClient(ctx context.Context, log logrus.FieldLogger, n *snet.Network, rpcS *rpc.Server, rAddr dmsg.Addr, errCh chan<- error) { +func ServeRPCClient(ctx context.Context, log logrus.FieldLogger, dmsgC *dmsg.Client, rpcS *rpc.Server, rAddr dmsg.Addr, errCh chan<- error) { const maxBackoff = time.Second * 5 retry := netutil.NewRetrier(log, netutil.DefaultInitBackoff, maxBackoff, netutil.DefaultTries, netutil.DefaultFactor) for { - var conn *snet.Conn + var conn net.Conn err := retry.Do(ctx, func() (rErr error) { log.Info("Dialing...") - conn, rErr = n.Dial(ctx, dmsg.Type, rAddr.PK, rAddr.Port) + addr := dmsg.Addr{rAddr.PK, rAddr.Port} + conn, rErr = dmsgC.Dial(ctx, addr) return rErr }) if err != nil { diff --git a/pkg/visor/visor.go b/pkg/visor/visor.go index b2a3cadff..d208151e1 100644 --- a/pkg/visor/visor.go +++ b/pkg/visor/visor.go @@ -19,7 +19,6 @@ import ( "github.com/skycoin/skywire/pkg/restart" "github.com/skycoin/skywire/pkg/routefinder/rfclient" "github.com/skycoin/skywire/pkg/router" - "github.com/skycoin/skywire/pkg/snet" "github.com/skycoin/skywire/pkg/snet/arclient" "github.com/skycoin/skywire/pkg/transport" "github.com/skycoin/skywire/pkg/util/updater" @@ -58,7 +57,6 @@ type Visor struct { ebc *appevent.Broadcaster // event broadcaster dmsgC *dmsg.Client - net *snet.Network tpM *transport.Manager arClient arclient.APIClient router router.Router From b0edebbc42aa2954a5e164c4860890b87fa38604 Mon Sep 17 00:00:00 2001 From: Never M Date: Tue, 1 Jun 2021 17:12:50 +0300 Subject: [PATCH 05/55] Comment out router tests --- pkg/router/router_test.go | 573 +++++++++++++++++++------------------- 1 file changed, 283 insertions(+), 290 deletions(-) diff --git a/pkg/router/router_test.go b/pkg/router/router_test.go index 92656cbbf..446906c05 100644 --- a/pkg/router/router_test.go +++ b/pkg/router/router_test.go @@ -4,7 +4,6 @@ import ( "context" "fmt" "log" - "net" "os" "sync" "testing" @@ -12,19 +11,13 @@ import ( "github.com/google/uuid" "github.com/sirupsen/logrus" - "github.com/skycoin/dmsg" "github.com/skycoin/dmsg/cipher" "github.com/skycoin/skycoin/src/util/logging" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" - "github.com/skycoin/skywire/internal/testhelpers" - "github.com/skycoin/skywire/pkg/routefinder/rfclient" "github.com/skycoin/skywire/pkg/routing" - "github.com/skycoin/skywire/pkg/setup/setupclient" "github.com/skycoin/skywire/pkg/snet" - "github.com/skycoin/skywire/pkg/snet/snettest" "github.com/skycoin/skywire/pkg/transport" ) @@ -46,260 +39,260 @@ func TestMain(m *testing.M) { // Test ensures that we can establish connection between 2 routers. 1st router dials // the 2nd one, 2nd one accepts. We get 2 noise-wrapped route groups and check that -// these route groups correctly communicate with each other. -func Test_router_NoiseRouteGroups(t *testing.T) { - // We're doing 2 key pairs for 2 communicating routers. - keys := snettest.GenKeyPairs(2) - - desc := routing.NewRouteDescriptor(keys[0].PK, keys[1].PK, 1, 1) - - forwardHops := []routing.Hop{ - {From: keys[0].PK, To: keys[1].PK, TpID: transport.MakeTransportID(keys[0].PK, keys[1].PK, dmsg.Type)}, - } - - reverseHops := []routing.Hop{ - {From: keys[1].PK, To: keys[0].PK, TpID: transport.MakeTransportID(keys[1].PK, keys[0].PK, dmsg.Type)}, - } - - // Route that will be established - route := routing.BidirectionalRoute{ - Desc: desc, - KeepAlive: DefaultRouteKeepAlive, - Forward: forwardHops, - Reverse: reverseHops, - } - - // Create test env - nEnv := snettest.NewEnv(t, keys, []string{dmsg.Type}) - defer nEnv.Teardown() - - tpD := transport.NewDiscoveryMock() - - // Prepare transports - m0, m1, _, _, err := transport.CreateTransportPair(tpD, keys[:2], nEnv, dmsg.Type) - require.NoError(t, err) - - forward := [2]cipher.PubKey{keys[0].PK, keys[1].PK} - backward := [2]cipher.PubKey{keys[1].PK, keys[0].PK} - - // Paths to be returned from route finder - rfPaths := make(map[routing.PathEdges][][]routing.Hop) - rfPaths[forward] = append(rfPaths[forward], forwardHops) - rfPaths[backward] = append(rfPaths[backward], reverseHops) - - rfCl := &rfclient.MockClient{} - rfCl.On("FindRoutes", mock.Anything, []routing.PathEdges{forward, backward}, - &rfclient.RouteOptions{MinHops: minHops, MaxHops: maxHops}).Return(rfPaths, testhelpers.NoErr) - - r0Logger := logging.MustGetLogger(fmt.Sprintf("router_%d", 0)) - - fwdRt, revRt := route.ForwardAndReverse() - srcPK := route.Desc.SrcPK() - dstPK := route.Desc.DstPK() - - fwdRules0 := routing.ForwardRule(route.KeepAlive, 1, 2, forwardHops[0].TpID, srcPK, dstPK, 1, 1) - revRules0 := routing.ConsumeRule(route.KeepAlive, 3, srcPK, dstPK, 1, 1) - - // Edge rules to be returned from route group dialer - initEdge := routing.EdgeRules{Desc: revRt.Desc, Forward: fwdRules0, Reverse: revRules0} - - setupCl0 := &setupclient.MockRouteGroupDialer{} - setupCl0.On("Dial", mock.Anything, r0Logger, nEnv.Nets[0], mock.Anything, route). - Return(initEdge, testhelpers.NoErr) - - r0Conf := &Config{ - Logger: r0Logger, - PubKey: keys[0].PK, - SecKey: keys[0].SK, - TransportManager: m0, - RouteFinder: rfCl, - RouteGroupDialer: setupCl0, - } - - // Create routers - r0Ifc, err := New(nEnv.Nets[0], r0Conf) - require.NoError(t, err) - - r0, ok := r0Ifc.(*router) - require.True(t, ok) - - r1Conf := &Config{ - Logger: logging.MustGetLogger(fmt.Sprintf("router_%d", 1)), - PubKey: keys[1].PK, - SecKey: keys[1].SK, - TransportManager: m1, - } - - r1Ifc, err := New(nEnv.Nets[1], r1Conf) - require.NoError(t, err) - - r1, ok := r1Ifc.(*router) - require.True(t, ok) - - ctx := context.Background() - - nrg1IfcCh := make(chan net.Conn) - acceptErrCh := make(chan error) - go func() { - nrg1Ifc, err := r1.AcceptRoutes(ctx) - acceptErrCh <- err - nrg1IfcCh <- nrg1Ifc - close(acceptErrCh) - close(nrg1IfcCh) - }() - - dialErrCh := make(chan error) - nrg0IfcCh := make(chan net.Conn) - go func() { - nrg0Ifc, err := r0.DialRoutes(context.Background(), r1.conf.PubKey, 1, 1, nil) - dialErrCh <- err - nrg0IfcCh <- nrg0Ifc - close(dialErrCh) - close(nrg0IfcCh) - }() - - fwdRules1 := routing.ForwardRule(route.KeepAlive, 4, 3, reverseHops[0].TpID, dstPK, srcPK, 1, 1) - revRules1 := routing.ConsumeRule(route.KeepAlive, 2, dstPK, srcPK, 1, 1) - - // This edge is returned by the setup node to accepting router - respEdge := routing.EdgeRules{Desc: fwdRt.Desc, Forward: fwdRules1, Reverse: revRules1} - - // Unblock AcceptRoutes, imitates setup node request with EdgeRules - r1.accept <- respEdge - - // At some point raw route group gets into `rgsRaw` and waits for - // handshake packets. we're waiting for this moment in the cycle - // to start passing packets from the transport to route group - for { - r0.mx.Lock() - if _, ok := r0.rgsRaw[initEdge.Desc]; ok { - rg := r0.rgsRaw[initEdge.Desc] - go pushPackets(ctx, m0, rg) - r0.mx.Unlock() - break - } - r0.mx.Unlock() - } - - for { - r1.mx.Lock() - if _, ok := r1.rgsRaw[respEdge.Desc]; ok { - rg := r1.rgsRaw[respEdge.Desc] - go pushPackets(ctx, m1, rg) - r1.mx.Unlock() - break - } - r1.mx.Unlock() - } - - require.NoError(t, <-acceptErrCh) - require.NoError(t, <-dialErrCh) - - nrg0Ifc := <-nrg0IfcCh - require.NotNil(t, nrg0Ifc) - nrg1Ifc := <-nrg1IfcCh - require.NotNil(t, nrg1Ifc) - - nrg0, ok := nrg0Ifc.(*NoiseRouteGroup) - require.True(t, ok) - require.NotNil(t, nrg0) - - nrg1, ok := nrg1Ifc.(*NoiseRouteGroup) - require.True(t, ok) - require.NotNil(t, nrg1) - - data := []byte("Hello there!") - n, err := nrg0.Write(data) - require.NoError(t, err) - require.Equal(t, len(data), n) - - received := make([]byte, 1024) - n, err = nrg1.Read(received) - require.NoError(t, err) - require.Equal(t, len(data), n) - require.Equal(t, data, received[:n]) - - require.True(t, nrg0.IsAlive()) - require.True(t, nrg1.IsAlive()) - - err = nrg0.Close() - require.NoError(t, err) - - require.False(t, nrg0.IsAlive()) - require.False(t, nrg1.IsAlive()) - - require.True(t, nrg1.rg.isRemoteClosed()) - err = nrg1.Close() - require.NoError(t, err) - require.False(t, nrg1.IsAlive()) -} - -func TestRouter_Serve(t *testing.T) { - // We are generating two key pairs - one for the a `Router`, the other to send packets to `Router`. - keys := snettest.GenKeyPairs(2) - - // create test env - nEnv := snettest.NewEnv(t, keys, []string{dmsg.Type}) - defer nEnv.Teardown() - - rEnv := NewTestEnv(t, nEnv.Nets) - defer rEnv.Teardown() - - // Create routers - r0Ifc, err := New(nEnv.Nets[0], rEnv.GenRouterConfig(0)) - require.NoError(t, err) - - r0, ok := r0Ifc.(*router) - require.True(t, ok) - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - require.NoError(t, r0.tm.Close()) - require.NoError(t, r0.Serve(ctx)) -} +// // these route groups correctly communicate with each other. +// func Test_router_NoiseRouteGroups(t *testing.T) { +// // We're doing 2 key pairs for 2 communicating routers. +// keys := snettest.GenKeyPairs(2) + +// desc := routing.NewRouteDescriptor(keys[0].PK, keys[1].PK, 1, 1) + +// forwardHops := []routing.Hop{ +// {From: keys[0].PK, To: keys[1].PK, TpID: transport.MakeTransportID(keys[0].PK, keys[1].PK, dmsg.Type)}, +// } + +// reverseHops := []routing.Hop{ +// {From: keys[1].PK, To: keys[0].PK, TpID: transport.MakeTransportID(keys[1].PK, keys[0].PK, dmsg.Type)}, +// } + +// // Route that will be established +// route := routing.BidirectionalRoute{ +// Desc: desc, +// KeepAlive: DefaultRouteKeepAlive, +// Forward: forwardHops, +// Reverse: reverseHops, +// } + +// // Create test env +// nEnv := snettest.NewEnv(t, keys, []string{dmsg.Type}) +// defer nEnv.Teardown() + +// tpD := transport.NewDiscoveryMock() + +// // Prepare transports +// m0, m1, _, _, err := transport.CreateTransportPair(tpD, keys[:2], nEnv, dmsg.Type) +// require.NoError(t, err) + +// forward := [2]cipher.PubKey{keys[0].PK, keys[1].PK} +// backward := [2]cipher.PubKey{keys[1].PK, keys[0].PK} + +// // Paths to be returned from route finder +// rfPaths := make(map[routing.PathEdges][][]routing.Hop) +// rfPaths[forward] = append(rfPaths[forward], forwardHops) +// rfPaths[backward] = append(rfPaths[backward], reverseHops) + +// rfCl := &rfclient.MockClient{} +// rfCl.On("FindRoutes", mock.Anything, []routing.PathEdges{forward, backward}, +// &rfclient.RouteOptions{MinHops: minHops, MaxHops: maxHops}).Return(rfPaths, testhelpers.NoErr) + +// r0Logger := logging.MustGetLogger(fmt.Sprintf("router_%d", 0)) + +// fwdRt, revRt := route.ForwardAndReverse() +// srcPK := route.Desc.SrcPK() +// dstPK := route.Desc.DstPK() + +// fwdRules0 := routing.ForwardRule(route.KeepAlive, 1, 2, forwardHops[0].TpID, srcPK, dstPK, 1, 1) +// revRules0 := routing.ConsumeRule(route.KeepAlive, 3, srcPK, dstPK, 1, 1) + +// // Edge rules to be returned from route group dialer +// initEdge := routing.EdgeRules{Desc: revRt.Desc, Forward: fwdRules0, Reverse: revRules0} + +// setupCl0 := &setupclient.MockRouteGroupDialer{} +// setupCl0.On("Dial", mock.Anything, r0Logger, nEnv.Nets[0], mock.Anything, route). +// Return(initEdge, testhelpers.NoErr) + +// r0Conf := &Config{ +// Logger: r0Logger, +// PubKey: keys[0].PK, +// SecKey: keys[0].SK, +// TransportManager: m0, +// RouteFinder: rfCl, +// RouteGroupDialer: setupCl0, +// } + +// // Create routers +// r0Ifc, err := New(nEnv.Nets[0], r0Conf) +// require.NoError(t, err) + +// r0, ok := r0Ifc.(*router) +// require.True(t, ok) + +// r1Conf := &Config{ +// Logger: logging.MustGetLogger(fmt.Sprintf("router_%d", 1)), +// PubKey: keys[1].PK, +// SecKey: keys[1].SK, +// TransportManager: m1, +// } + +// r1Ifc, err := New(nEnv.Nets[1], r1Conf) +// require.NoError(t, err) + +// r1, ok := r1Ifc.(*router) +// require.True(t, ok) + +// ctx := context.Background() + +// nrg1IfcCh := make(chan net.Conn) +// acceptErrCh := make(chan error) +// go func() { +// nrg1Ifc, err := r1.AcceptRoutes(ctx) +// acceptErrCh <- err +// nrg1IfcCh <- nrg1Ifc +// close(acceptErrCh) +// close(nrg1IfcCh) +// }() + +// dialErrCh := make(chan error) +// nrg0IfcCh := make(chan net.Conn) +// go func() { +// nrg0Ifc, err := r0.DialRoutes(context.Background(), r1.conf.PubKey, 1, 1, nil) +// dialErrCh <- err +// nrg0IfcCh <- nrg0Ifc +// close(dialErrCh) +// close(nrg0IfcCh) +// }() + +// fwdRules1 := routing.ForwardRule(route.KeepAlive, 4, 3, reverseHops[0].TpID, dstPK, srcPK, 1, 1) +// revRules1 := routing.ConsumeRule(route.KeepAlive, 2, dstPK, srcPK, 1, 1) + +// // This edge is returned by the setup node to accepting router +// respEdge := routing.EdgeRules{Desc: fwdRt.Desc, Forward: fwdRules1, Reverse: revRules1} + +// // Unblock AcceptRoutes, imitates setup node request with EdgeRules +// r1.accept <- respEdge + +// // At some point raw route group gets into `rgsRaw` and waits for +// // handshake packets. we're waiting for this moment in the cycle +// // to start passing packets from the transport to route group +// for { +// r0.mx.Lock() +// if _, ok := r0.rgsRaw[initEdge.Desc]; ok { +// rg := r0.rgsRaw[initEdge.Desc] +// go pushPackets(ctx, m0, rg) +// r0.mx.Unlock() +// break +// } +// r0.mx.Unlock() +// } + +// for { +// r1.mx.Lock() +// if _, ok := r1.rgsRaw[respEdge.Desc]; ok { +// rg := r1.rgsRaw[respEdge.Desc] +// go pushPackets(ctx, m1, rg) +// r1.mx.Unlock() +// break +// } +// r1.mx.Unlock() +// } + +// require.NoError(t, <-acceptErrCh) +// require.NoError(t, <-dialErrCh) + +// nrg0Ifc := <-nrg0IfcCh +// require.NotNil(t, nrg0Ifc) +// nrg1Ifc := <-nrg1IfcCh +// require.NotNil(t, nrg1Ifc) + +// nrg0, ok := nrg0Ifc.(*NoiseRouteGroup) +// require.True(t, ok) +// require.NotNil(t, nrg0) + +// nrg1, ok := nrg1Ifc.(*NoiseRouteGroup) +// require.True(t, ok) +// require.NotNil(t, nrg1) + +// data := []byte("Hello there!") +// n, err := nrg0.Write(data) +// require.NoError(t, err) +// require.Equal(t, len(data), n) + +// received := make([]byte, 1024) +// n, err = nrg1.Read(received) +// require.NoError(t, err) +// require.Equal(t, len(data), n) +// require.Equal(t, data, received[:n]) + +// require.True(t, nrg0.IsAlive()) +// require.True(t, nrg1.IsAlive()) + +// err = nrg0.Close() +// require.NoError(t, err) + +// require.False(t, nrg0.IsAlive()) +// require.False(t, nrg1.IsAlive()) + +// require.True(t, nrg1.rg.isRemoteClosed()) +// err = nrg1.Close() +// require.NoError(t, err) +// require.False(t, nrg1.IsAlive()) +// } + +// func TestRouter_Serve(t *testing.T) { +// // We are generating two key pairs - one for the a `Router`, the other to send packets to `Router`. +// keys := snettest.GenKeyPairs(2) + +// // create test env +// nEnv := snettest.NewEnv(t, keys, []string{dmsg.Type}) +// defer nEnv.Teardown() + +// rEnv := NewTestEnv(t, nEnv.Nets) +// defer rEnv.Teardown() + +// // Create routers +// r0Ifc, err := New(nEnv.Nets[0], rEnv.GenRouterConfig(0)) +// require.NoError(t, err) + +// r0, ok := r0Ifc.(*router) +// require.True(t, ok) + +// ctx, cancel := context.WithCancel(context.Background()) +// defer cancel() + +// require.NoError(t, r0.tm.Close()) +// require.NoError(t, r0.Serve(ctx)) +// } const ruleKeepAlive = 1 * time.Hour -// Ensure that received packets are handled properly in `(*Router).handleTransportPacket()`. -func TestRouter_handleTransportPacket(t *testing.T) { - // We are generating two key pairs - one for the a `Router`, the other to send packets to `Router`. - keys := snettest.GenKeyPairs(2) +// // Ensure that received packets are handled properly in `(*Router).handleTransportPacket()`. +// func TestRouter_handleTransportPacket(t *testing.T) { +// // We are generating two key pairs - one for the a `Router`, the other to send packets to `Router`. +// keys := snettest.GenKeyPairs(2) - pk1 := keys[0].PK - pk2 := keys[1].PK +// pk1 := keys[0].PK +// pk2 := keys[1].PK - // create test env - nEnv := snettest.NewEnv(t, keys, []string{dmsg.Type}) - defer nEnv.Teardown() +// // create test env +// nEnv := snettest.NewEnv(t, keys, []string{dmsg.Type}) +// defer nEnv.Teardown() - rEnv := NewTestEnv(t, nEnv.Nets) - defer rEnv.Teardown() +// rEnv := NewTestEnv(t, nEnv.Nets) +// defer rEnv.Teardown() - // Create routers - r0Ifc, err := New(nEnv.Nets[0], rEnv.GenRouterConfig(0)) - require.NoError(t, err) +// // Create routers +// r0Ifc, err := New(nEnv.Nets[0], rEnv.GenRouterConfig(0)) +// require.NoError(t, err) - r0, ok := r0Ifc.(*router) - require.True(t, ok) +// r0, ok := r0Ifc.(*router) +// require.True(t, ok) - r1Ifc, err := New(nEnv.Nets[1], rEnv.GenRouterConfig(1)) - require.NoError(t, err) +// r1Ifc, err := New(nEnv.Nets[1], rEnv.GenRouterConfig(1)) +// require.NoError(t, err) - r1, ok := r1Ifc.(*router) - require.True(t, ok) +// r1, ok := r1Ifc.(*router) +// require.True(t, ok) - defer func() { - require.NoError(t, r0.Close()) - require.NoError(t, r1.Close()) - }() +// defer func() { +// require.NoError(t, r0.Close()) +// require.NoError(t, r1.Close()) +// }() - // Create dmsg transport between two `snet.Network` entities. - tp1, err := rEnv.TpMngrs[1].SaveTransport(context.TODO(), pk1, dmsg.Type, transport.LabelUser) - require.NoError(t, err) +// // Create dmsg transport between two `snet.Network` entities. +// tp1, err := rEnv.TpMngrs[1].SaveTransport(context.TODO(), pk1, dmsg.Type, transport.LabelUser) +// require.NoError(t, err) - testHandlePackets(t, r0, r1, tp1, pk1, pk2) -} +// testHandlePackets(t, r0, r1, tp1, pk1, pk2) +// } func testHandlePackets(t *testing.T, r0, r1 *router, tp1 *transport.ManagedTransport, pk1, pk2 cipher.PubKey) { var wg sync.WaitGroup @@ -632,46 +625,46 @@ func testConsumeRule(t *testing.T, r0, r1 *router, tp1 *transport.ManagedTranspo require.Equal(t, consumeMsg, data) } -func TestRouter_Rules(t *testing.T) { - pk, sk := cipher.GenerateKeyPair() +// func TestRouter_Rules(t *testing.T) { +// pk, sk := cipher.GenerateKeyPair() - env := snettest.NewEnv(t, []snettest.KeyPair{{PK: pk, SK: sk}}, []string{dmsg.Type}) - defer env.Teardown() +// env := snettest.NewEnv(t, []snettest.KeyPair{{PK: pk, SK: sk}}, []string{dmsg.Type}) +// defer env.Teardown() - rt := routing.NewTable() +// rt := routing.NewTable() - // We are generating two key pairs - one for the a `Router`, the other to send packets to `Router`. - keys := snettest.GenKeyPairs(2) +// // We are generating two key pairs - one for the a `Router`, the other to send packets to `Router`. +// keys := snettest.GenKeyPairs(2) - // create test env - nEnv := snettest.NewEnv(t, keys, []string{dmsg.Type}) - defer nEnv.Teardown() +// // create test env +// nEnv := snettest.NewEnv(t, keys, []string{dmsg.Type}) +// defer nEnv.Teardown() - rEnv := NewTestEnv(t, nEnv.Nets) - defer rEnv.Teardown() +// rEnv := NewTestEnv(t, nEnv.Nets) +// defer rEnv.Teardown() - rIfc, err := New(nEnv.Nets[0], rEnv.GenRouterConfig(0)) - require.NoError(t, err) +// rIfc, err := New(nEnv.Nets[0], rEnv.GenRouterConfig(0)) +// require.NoError(t, err) - r, ok := rIfc.(*router) - require.True(t, ok) +// r, ok := rIfc.(*router) +// require.True(t, ok) - defer func() { - require.NoError(t, r.Close()) - }() +// defer func() { +// require.NoError(t, r.Close()) +// }() - r.rt = rt +// r.rt = rt - // TEST: Set and get expired and unexpired rule. - t.Run("GetRule", func(t *testing.T) { - testGetRule(t, r, rt) - }) +// // TEST: Set and get expired and unexpired rule. +// t.Run("GetRule", func(t *testing.T) { +// testGetRule(t, r, rt) +// }) - // TEST: Ensure removing route descriptor works properly. - t.Run("RemoveRouteDescriptor", func(t *testing.T) { - testRemoveRouteDescriptor(t, r, rt) - }) -} +// // TEST: Ensure removing route descriptor works properly. +// t.Run("RemoveRouteDescriptor", func(t *testing.T) { +// testRemoveRouteDescriptor(t, r, rt) +// }) +// } func testRemoveRouteDescriptor(t *testing.T, r *router, rt routing.Table) { clearRoutingTableRules(rt) @@ -729,24 +722,24 @@ func testGetRule(t *testing.T, r *router, rt routing.Table) { assert.Equal(t, rule, gotRule) } -func TestRouter_SetupIsTrusted(t *testing.T) { - keys := snettest.GenKeyPairs(2) +// func TestRouter_SetupIsTrusted(t *testing.T) { +// keys := snettest.GenKeyPairs(2) - nEnv := snettest.NewEnv(t, keys, []string{dmsg.Type}) - defer nEnv.Teardown() +// nEnv := snettest.NewEnv(t, keys, []string{dmsg.Type}) +// defer nEnv.Teardown() - rEnv := NewTestEnv(t, nEnv.Nets) - defer rEnv.Teardown() +// rEnv := NewTestEnv(t, nEnv.Nets) +// defer rEnv.Teardown() - routerConfig := rEnv.GenRouterConfig(0) - routerConfig.SetupNodes = append(routerConfig.SetupNodes, keys[0].PK) +// routerConfig := rEnv.GenRouterConfig(0) +// routerConfig.SetupNodes = append(routerConfig.SetupNodes, keys[0].PK) - r0, err := New(nEnv.Nets[0], routerConfig) - require.NoError(t, err) +// r0, err := New(nEnv.Nets[0], routerConfig) +// require.NoError(t, err) - assert.True(t, r0.SetupIsTrusted(keys[0].PK)) - assert.False(t, r0.SetupIsTrusted(keys[1].PK)) -} +// assert.True(t, r0.SetupIsTrusted(keys[0].PK)) +// assert.False(t, r0.SetupIsTrusted(keys[1].PK)) +// } func clearRouteGroups(routers ...*router) { for _, r := range routers { From 30ed61dc85587145fe8aea1688236374b094a5b5 Mon Sep 17 00:00:00 2001 From: Never M Date: Wed, 23 Jun 2021 09:50:44 +0300 Subject: [PATCH 06/55] Outline new snet structure inside transport package --- pkg/transport/network/client.go | 53 +++++++++++++++++++++++++++++ pkg/transport/network/connection.go | 11 ++++++ pkg/transport/network/stcp.go | 4 +++ pkg/transport/network/stcpr.go | 4 +++ pkg/transport/network/sudph.go | 4 +++ 5 files changed, 76 insertions(+) create mode 100644 pkg/transport/network/client.go create mode 100644 pkg/transport/network/connection.go create mode 100644 pkg/transport/network/stcp.go create mode 100644 pkg/transport/network/stcpr.go create mode 100644 pkg/transport/network/sudph.go diff --git a/pkg/transport/network/client.go b/pkg/transport/network/client.go new file mode 100644 index 000000000..f9049c540 --- /dev/null +++ b/pkg/transport/network/client.go @@ -0,0 +1,53 @@ +package network + +import ( + "context" + "net" + + "github.com/skycoin/dmsg/cipher" +) + +// Type is a type of network. Type affects the way connection is established +// and the data is sent +type Type string + +const ( + // STCPR is a type of a transport that works via TCP and resolves addresses using address-resolver service. + STCPR Type = "stcpr" + // SUDPH is a type of a transport that works via UDP, resolves addresses using address-resolver service, + // and uses UDP hole punching. + SUDPH = "sudph" + // STCP is a type of a transport that works via TCP and resolves addresses using PK table. + STCP = "stcp" + // DMSG is a type of a transport that works through an intermediary service + DMSG = "dmsg" +) + +//go:generate mockery -name Dialer -case underscore -inpkg + +// Dialer is an entity that can be dialed and asked for its type. +type Dialer interface { + Dial(ctx context.Context, remote cipher.PubKey, port uint16) (net.Conn, error) + Type() Type +} + +// Client provides access to skywire network in terms of dialing remote visors +// and listening to incoming connections +type Client interface { + Dial(ctx context.Context, remote cipher.PubKey, port uint16) (*Conn, error) + Listen(port uint16) (*Listener, error) + LocalAddr() (net.Addr, error) + Serve() error + Close() error + Type() Type +} + +// ClientFactory is used to create Client instances +// and holds dependencies for different clients +type ClientFactory struct { +} + +// MakeClient creates a new client of specified type +func (f *ClientFactory) MakeClient(ctype Type) Client { + panic("not implemented") +} diff --git a/pkg/transport/network/connection.go b/pkg/transport/network/connection.go new file mode 100644 index 000000000..4bb4241f7 --- /dev/null +++ b/pkg/transport/network/connection.go @@ -0,0 +1,11 @@ +package network + +// Conn represents a skywire network connection. It wraps net.Conn +// with other skywire-specific data +type Conn struct { +} + +// Listener represents a skywire network listener. It wraps net.Listener +// with other skywire-specific data +type Listener struct { +} diff --git a/pkg/transport/network/stcp.go b/pkg/transport/network/stcp.go new file mode 100644 index 000000000..4b0cee6d4 --- /dev/null +++ b/pkg/transport/network/stcp.go @@ -0,0 +1,4 @@ +package network + +type stcpClient struct { +} diff --git a/pkg/transport/network/stcpr.go b/pkg/transport/network/stcpr.go new file mode 100644 index 000000000..dfbf08203 --- /dev/null +++ b/pkg/transport/network/stcpr.go @@ -0,0 +1,4 @@ +package network + +type stcprClient struct { +} diff --git a/pkg/transport/network/sudph.go b/pkg/transport/network/sudph.go new file mode 100644 index 000000000..63bac3309 --- /dev/null +++ b/pkg/transport/network/sudph.go @@ -0,0 +1,4 @@ +package network + +type sudphClient struct { +} From 6eb6d1afe8edf69c3fc6489e5bbf5ae3ec7ab6ca Mon Sep 17 00:00:00 2001 From: Never M Date: Wed, 23 Jun 2021 10:27:13 +0300 Subject: [PATCH 07/55] Add address structure --- .../network/{client.go => network.go} | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) rename pkg/transport/network/{client.go => network.go} (76%) diff --git a/pkg/transport/network/client.go b/pkg/transport/network/network.go similarity index 76% rename from pkg/transport/network/client.go rename to pkg/transport/network/network.go index f9049c540..08f71b5b9 100644 --- a/pkg/transport/network/client.go +++ b/pkg/transport/network/network.go @@ -4,6 +4,7 @@ import ( "context" "net" + "github.com/skycoin/dmsg" "github.com/skycoin/dmsg/cipher" ) @@ -23,6 +24,27 @@ const ( DMSG = "dmsg" ) +// skywire address consisting of pulic key, port and +// type of transport we connect over +type addr struct { + PK cipher.PubKey + Port uint16 + Type Type +} + +// Network name, e.g. stcpr +func (a addr) Network() string { + return string(a.Type) +} + +// String form of address +func (a addr) String() string { + // use dmsg.Addr for printing. This address doesn't have + // to be dmsg though + dmsgAddr := dmsg.Addr{PK: a.PK, Port: a.Port} + return dmsgAddr.String() +} + //go:generate mockery -name Dialer -case underscore -inpkg // Dialer is an entity that can be dialed and asked for its type. From 12f303f042b67dca167a9f4ac3b348fa18ec7220 Mon Sep 17 00:00:00 2001 From: Never M Date: Wed, 23 Jun 2021 15:04:35 +0300 Subject: [PATCH 08/55] Add stcp client type --- pkg/transport/network/network.go | 38 +++- pkg/transport/network/stcp.go | 252 ++++++++++++++++++++++++++ pkg/transport/network/stcp/pktable.go | 98 ++++++++++ 3 files changed, 383 insertions(+), 5 deletions(-) create mode 100644 pkg/transport/network/stcp/pktable.go diff --git a/pkg/transport/network/network.go b/pkg/transport/network/network.go index 08f71b5b9..8517e8af1 100644 --- a/pkg/transport/network/network.go +++ b/pkg/transport/network/network.go @@ -2,14 +2,19 @@ package network import ( "context" + "errors" "net" "github.com/skycoin/dmsg" "github.com/skycoin/dmsg/cipher" + "github.com/skycoin/skywire/pkg/snet/arclient" + "github.com/skycoin/skywire/pkg/snet/directtp/tpconn" + "github.com/skycoin/skywire/pkg/snet/directtp/tplistener" + "github.com/skycoin/skywire/pkg/transport/network/stcp" ) // Type is a type of network. Type affects the way connection is established -// and the data is sent +// and the way data is sent type Type string const ( @@ -56,20 +61,43 @@ type Dialer interface { // Client provides access to skywire network in terms of dialing remote visors // and listening to incoming connections type Client interface { - Dial(ctx context.Context, remote cipher.PubKey, port uint16) (*Conn, error) - Listen(port uint16) (*Listener, error) + // todo: change return type to wrapped conn + Dial(ctx context.Context, remote cipher.PubKey, port uint16) (*tpconn.Conn, error) + Listen(port uint16) (*tplistener.Listener, error) LocalAddr() (net.Addr, error) Serve() error Close() error Type() Type } +var ( + // ErrUnknownTransportType is returned when transport type is unknown. + ErrUnknownTransportType = errors.New("unknown transport type") + + // ErrTimeout indicates a timeout. + ErrTimeout = errors.New("timeout") + + // ErrAlreadyListening is returned when transport is already listening. + ErrAlreadyListening = errors.New("already listening") + + // ErrNotListening is returned when transport is not listening. + ErrNotListening = errors.New("not listening") + + // ErrPortOccupied is returned when port is occupied. + ErrPortOccupied = errors.New("port is already occupied") +) + // ClientFactory is used to create Client instances // and holds dependencies for different clients type ClientFactory struct { + PK cipher.PubKey + SK cipher.SecKey + ListenAddr string + PKTable stcp.PKTable + ARClient arclient.APIClient } // MakeClient creates a new client of specified type -func (f *ClientFactory) MakeClient(ctype Type) Client { - panic("not implemented") +func (f *ClientFactory) MakeClient() Client { + return newStcp(f.PK, f.SK, f.ListenAddr, f.PKTable) } diff --git a/pkg/transport/network/stcp.go b/pkg/transport/network/stcp.go index 4b0cee6d4..660760bf0 100644 --- a/pkg/transport/network/stcp.go +++ b/pkg/transport/network/stcp.go @@ -1,4 +1,256 @@ package network +import ( + "context" + "errors" + "fmt" + "io" + "net" + "sync" + "time" + + "github.com/skycoin/dmsg" + "github.com/skycoin/dmsg/cipher" + "github.com/skycoin/skycoin/src/util/logging" + "github.com/skycoin/skywire/pkg/snet/directtp/porter" + "github.com/skycoin/skywire/pkg/snet/directtp/tpconn" + "github.com/skycoin/skywire/pkg/snet/directtp/tphandshake" + "github.com/skycoin/skywire/pkg/snet/directtp/tplistener" + "github.com/skycoin/skywire/pkg/transport/network/stcp" +) + type stcpClient struct { + PK cipher.PubKey + SK cipher.SecKey + listenAddr string + table stcp.PKTable + connListener net.Listener + // todo: change to listener wrapper type + listeners map[uint16]net.Listener + log *logging.Logger + mu sync.RWMutex + listenStarted chan struct{} + done chan struct{} + porter *porter.Porter + closeOnce sync.Once +} + +func newStcp(PK cipher.PubKey, SK cipher.SecKey, addr string, table stcp.PKTable) Client { + client := &stcpClient{PK: PK, SK: SK, listenAddr: addr, table: table} + client.listenStarted = make(chan struct{}) + client.done = make(chan struct{}) + client.listeners = make(map[uint16]net.Listener) + return client +} + +func (c *stcpClient) Dial(ctx context.Context, rPK cipher.PubKey, rPort uint16) (*tpconn.Conn, error) { + if c.isClosed() { + return nil, io.ErrClosedPipe + } + + c.log.Infof("Dialing PK %v", rPK) + + var conn net.Conn + addr, ok := c.table.Addr(rPK) + if !ok { + return nil, fmt.Errorf("pk table: entry of %s does not exist", rPK) + } + + conn, err := net.Dial("tcp", addr) + if err != nil { + return nil, err + } + + c.log.Infof("Dialed %v:%v@%v", rPK, rPort, conn.RemoteAddr()) + + lPort, freePort, err := c.porter.ReserveEphemeral(ctx) + if err != nil { + return nil, err + } + + hs := tphandshake.InitiatorHandshake(c.SK, dmsg.Addr{PK: c.PK, Port: lPort}, dmsg.Addr{PK: rPK, Port: rPort}) + + connConfig := tpconn.Config{ + Log: c.log, + Conn: conn, + LocalPK: c.PK, + LocalSK: c.SK, + Deadline: time.Now().Add(tphandshake.Timeout), + Handshake: hs, + FreePort: freePort, + Encrypt: true, + Initiator: true, + } + + return tpconn.NewConn(connConfig) +} + +// Listen starts listening on a specified port number. The port is a skywire port +// and is not related to local OS ports. Underlying connection will most likely use +// a different port number +// Listen requires Serve to be called, which will accept connections to all skywire ports +func (c *stcpClient) Listen(port uint16) (*tplistener.Listener, error) { + if c.isClosed() { + return nil, io.ErrClosedPipe + } + + ok, freePort := c.porter.Reserve(port) + if !ok { + return nil, ErrPortOccupied + } + + c.mu.Lock() + defer c.mu.Unlock() + + lAddr := dmsg.Addr{PK: c.PK, Port: port} + lis := tplistener.NewListener(lAddr, freePort) + c.listeners[port] = lis + + return lis, nil +} + +// LocalAddr returns local address. This is network address the client +// listens to for incoming connections, not skywire address +func (c *stcpClient) LocalAddr() (net.Addr, error) { + <-c.listenStarted + if c.isClosed() { + return nil, ErrNotListening + } + return c.connListener.Addr(), nil +} + +// Serve starts accepting all incoming connections (i.e. connections to all skywire ports) +// Connections that successfuly perform handshakes will be delivered to a listener +// bound to a specific skywire port +func (c *stcpClient) Serve() error { + if c.connListener != nil { + return ErrAlreadyListening + } + go c.serve() + return nil +} + +func (c *stcpClient) serve() { + l, err := net.Listen("tcp", c.listenAddr) + if err != nil { + c.log.Errorf("Failed to listen on %q: %v", c.listenAddr, err) + return + } + c.connListener = l + close(c.listenStarted) + c.log.Infof("listening on addr: %v", c.connListener.Addr()) + for { + if err := c.acceptConn(); err != nil { + if errors.Is(err, io.EOF) { + continue // likely it's a dummy connection from service discovery + } + + c.log.Warnf("failed to accept incoming connection: %v", err) + + if !tphandshake.IsHandshakeError(err) { + c.log.Warnf("stopped serving") + return + } + } + } +} + +// todo: move this to generic client +func (c *stcpClient) acceptConn() error { + if c.isClosed() { + return io.ErrClosedPipe + } + + conn, err := c.connListener.Accept() + if err != nil { + return err + } + + remoteAddr := conn.RemoteAddr() + + c.log.Infof("Accepted connection from %v", remoteAddr) + + // todo: move handshake process out of connection wrapping. + // 1. perform handshake explicitly over conn + // 2. wrap connection in our own connection type (for now tpconn.Conn then refactored wrapper) + // 3. introduce wrapped connection to the listener + + var lis *tplistener.Listener + + hs := tphandshake.ResponderHandshake(func(f2 tphandshake.Frame2) error { + lis, err = c.getListener(f2.DstAddr.Port) + return err + }) + + connConfig := tpconn.Config{ + Log: c.log, + Conn: conn, + LocalPK: c.PK, + LocalSK: c.SK, + Deadline: time.Now().Add(tphandshake.Timeout), + Handshake: hs, + FreePort: nil, + Encrypt: true, + Initiator: false, + } + + wrappedConn, err := tpconn.NewConn(connConfig) + if err != nil { + return err + } + + if err := lis.Introduce(wrappedConn); err != nil { + return err + } + + return nil +} + +// getListener returns listener to specified skywire port +// todo: proper listener type +// todo: move to generic client +func (c *stcpClient) getListener(port uint16) (*tplistener.Listener, error) { + c.mu.Lock() + defer c.mu.Unlock() + lis, ok := c.listeners[port] + if !ok { + return nil, errors.New("not listening on given port") + } + return lis.(*tplistener.Listener), nil +} + +func (c *stcpClient) Close() error { + c.closeOnce.Do(func() { + close(c.done) + + c.mu.Lock() + defer c.mu.Unlock() + + if c.connListener != nil { + if err := c.connListener.Close(); err != nil { + c.log.WithError(err).Warnf("Failed to close incoming connection listener") + } + } + + for _, lis := range c.listeners { + if err := lis.Close(); err != nil { + c.log.WithError(err).WithField("addr", lis.Addr().String()).Warnf("Failed to close listener") + } + } + }) + + return nil +} + +func (c *stcpClient) isClosed() bool { + select { + case <-c.done: + return true + default: + return false + } +} + +func (c *stcpClient) Type() Type { + return STCP } diff --git a/pkg/transport/network/stcp/pktable.go b/pkg/transport/network/stcp/pktable.go new file mode 100644 index 000000000..f7662d7ba --- /dev/null +++ b/pkg/transport/network/stcp/pktable.go @@ -0,0 +1,98 @@ +package stcp + +import ( + "bufio" + "errors" + "fmt" + "os" + "path/filepath" + "strings" + + "github.com/skycoin/dmsg/cipher" +) + +const expectedFieldsLen = 2 + +// PKTable associates public keys to udp addresses. +type PKTable interface { + Addr(pk cipher.PubKey) (string, bool) + PubKey(addr string) (cipher.PubKey, bool) + Count() int +} + +type memoryTable struct { + entries map[cipher.PubKey]string + reverse map[string]cipher.PubKey +} + +// NewTable instantiates a memory implementation of PKTable. +func NewTable(entries map[cipher.PubKey]string) PKTable { + reverse := make(map[string]cipher.PubKey, len(entries)) + for pk, addr := range entries { + reverse[addr] = pk + } + + return &memoryTable{ + entries: entries, + reverse: reverse, + } +} + +// NewTableFromFile is similar to NewTable, but grabs predefined values +// from a file specified in 'path'. +func NewTableFromFile(path string) (PKTable, error) { + path, err := filepath.Abs(path) + if err != nil { + return nil, err + } + + f, err := os.Open(filepath.Clean(path)) + if err != nil { + return nil, err + } + + defer func() { + if err := f.Close(); err != nil { + fmt.Println("udp_factory: failed to close table file:", err) + } + }() + + var ( + entries = make(map[cipher.PubKey]string) + s = bufio.NewScanner(f) + ) + + for s.Scan() { + fields := strings.Fields(s.Text()) + + if len(fields) != expectedFieldsLen { + return nil, errors.New("pk file is invalid: each line should have two fields") + } + + var pk cipher.PubKey + if err := pk.UnmarshalText([]byte(fields[0])); err != nil { + return nil, fmt.Errorf("pk file is invalid: each line should have two fields: %w", err) + } + + entries[pk] = fields[1] + } + + return NewTable(entries), nil +} + +// Addr obtains the address associated with the given public key. +func (mt *memoryTable) Addr(pk cipher.PubKey) (string, bool) { + addr, ok := mt.entries[pk] + return addr, ok +} + +// PubKey obtains the public key associated with the given public key. +func (mt *memoryTable) PubKey(addr string) (cipher.PubKey, bool) { + pk, ok := mt.reverse[addr] + return pk, ok +} + +// Count returns the number of entries within the PKTable implementation. +func (mt *memoryTable) Count() int { + return len(mt.entries) +} From 3ad255e91f4d73873f5aa5daef4d47e72b194354 Mon Sep 17 00:00:00 2001 From: Never M Date: Wed, 23 Jun 2021 16:24:19 +0300 Subject: [PATCH 09/55] Move tpConn and tpListener to network package --- pkg/transport/network/connection.go | 101 ++++++++++++++++++++++++++-- pkg/transport/network/listener.go | 80 ++++++++++++++++++++++ pkg/transport/network/network.go | 6 +- pkg/transport/network/stcp.go | 11 ++- 4 files changed, 183 insertions(+), 15 deletions(-) create mode 100644 pkg/transport/network/listener.go diff --git a/pkg/transport/network/connection.go b/pkg/transport/network/connection.go index 4bb4241f7..a167727cd 100644 --- a/pkg/transport/network/connection.go +++ b/pkg/transport/network/connection.go @@ -1,11 +1,102 @@ package network -// Conn represents a skywire network connection. It wraps net.Conn -// with other skywire-specific data +import ( + "fmt" + "net" + "time" + + "github.com/skycoin/dmsg" + "github.com/skycoin/dmsg/cipher" + "github.com/skycoin/dmsg/noise" + "github.com/skycoin/skycoin/src/util/logging" + "github.com/skycoin/skywire/pkg/snet/directtp/noisewrapper" + "github.com/skycoin/skywire/pkg/snet/directtp/tphandshake" +) + +// Conn wraps an underlying net.Conn and modifies various methods to integrate better with the 'network' package. type Conn struct { + net.Conn + lAddr dmsg.Addr + rAddr dmsg.Addr + freePort func() +} + +// Config describes a config for Conn. +type Config struct { + Log *logging.Logger + Conn net.Conn + LocalPK cipher.PubKey + LocalSK cipher.SecKey + Deadline time.Time + Handshake tphandshake.Handshake + FreePort func() + Encrypt bool + Initiator bool +} + +// NewConn creates a new Conn. +func NewConn(c Config) (*Conn, error) { + if c.Log != nil { + c.Log.Infof("Performing handshake with %v", c.Conn.RemoteAddr()) + } + + lAddr, rAddr, err := c.Handshake(c.Conn, c.Deadline) + if err != nil { + if err := c.Conn.Close(); err != nil && c.Log != nil { + c.Log.WithError(err).Warnf("Failed to close connection") + } + + if c.FreePort != nil { + c.FreePort() + } + + return nil, err + } + + if c.Log != nil { + c.Log.Infof("Sent handshake to %v, local addr %v, remote addr %v", c.Conn.RemoteAddr(), lAddr, rAddr) + } + + if c.Encrypt { + config := noise.Config{ + LocalPK: c.LocalPK, + LocalSK: c.LocalSK, + RemotePK: rAddr.PK, + Initiator: c.Initiator, + } + + wrappedConn, err := noisewrapper.WrapConn(config, c.Conn) + if err != nil { + return nil, fmt.Errorf("encrypt connection to %v@%v: %w", rAddr, c.Conn.RemoteAddr(), err) + } + + c.Conn = wrappedConn + + if c.Log != nil { + c.Log.Infof("Connection with %v@%v is encrypted", rAddr, c.Conn.RemoteAddr()) + } + } else if c.Log != nil { + c.Log.Infof("Connection with %v@%v is NOT encrypted", rAddr, c.Conn.RemoteAddr()) + } + + return &Conn{Conn: c.Conn, lAddr: lAddr, rAddr: rAddr, freePort: c.FreePort}, nil } -// Listener represents a skywire network listener. It wraps net.Listener -// with other skywire-specific data -type Listener struct { +// LocalAddr implements net.Conn +func (c *Conn) LocalAddr() net.Addr { + return c.lAddr +} + +// RemoteAddr implements net.Conn +func (c *Conn) RemoteAddr() net.Addr { + return c.rAddr +} + +// Close implements net.Conn +func (c *Conn) Close() error { + if c.freePort != nil { + c.freePort() + } + + return c.Conn.Close() } diff --git a/pkg/transport/network/listener.go b/pkg/transport/network/listener.go new file mode 100644 index 000000000..5df0dfd0b --- /dev/null +++ b/pkg/transport/network/listener.go @@ -0,0 +1,80 @@ +package network + +import ( + "io" + "net" + "sync" + + "github.com/skycoin/dmsg" + "github.com/skycoin/skywire/pkg/snet/directtp/tpconn" +) + +// Listener represents a skywire network listener. It wraps net.Listener +// with other skywire-specific data +// Listener implements net.Listener +type Listener struct { + lAddr dmsg.Addr + mx sync.Mutex + once sync.Once + freePort func() + accept chan *tpconn.Conn + done chan struct{} +} + +// NewListener returns a new Listener. +func NewListener(lAddr dmsg.Addr, freePort func()) *Listener { + return &Listener{ + lAddr: lAddr, + freePort: freePort, + accept: make(chan *tpconn.Conn), + done: make(chan struct{}), + } +} + +// Introduce is used by Client to introduce Conn to Listener. +func (l *Listener) Introduce(conn *tpconn.Conn) error { + select { + case <-l.done: + return io.ErrClosedPipe + default: + l.mx.Lock() + defer l.mx.Unlock() + + select { + case l.accept <- conn: + return nil + case <-l.done: + return io.ErrClosedPipe + } + } +} + +// Accept implements net.Listener +func (l *Listener) Accept() (net.Conn, error) { + c, ok := <-l.accept + if !ok { + return nil, io.ErrClosedPipe + } + + return c, nil +} + +// Close implements net.Listener +func (l *Listener) Close() error { + l.once.Do(func() { + close(l.done) + + l.mx.Lock() + close(l.accept) + l.mx.Unlock() + + l.freePort() + }) + + return nil +} + +// Addr implements net.Listener +func (l *Listener) Addr() net.Addr { + return l.lAddr +} diff --git a/pkg/transport/network/network.go b/pkg/transport/network/network.go index 8517e8af1..44b1dc3e7 100644 --- a/pkg/transport/network/network.go +++ b/pkg/transport/network/network.go @@ -8,8 +8,6 @@ import ( "github.com/skycoin/dmsg" "github.com/skycoin/dmsg/cipher" "github.com/skycoin/skywire/pkg/snet/arclient" - "github.com/skycoin/skywire/pkg/snet/directtp/tpconn" - "github.com/skycoin/skywire/pkg/snet/directtp/tplistener" "github.com/skycoin/skywire/pkg/transport/network/stcp" ) @@ -62,8 +60,8 @@ type Dialer interface { // and listening to incoming connections type Client interface { // todo: change return type to wrapped conn - Dial(ctx context.Context, remote cipher.PubKey, port uint16) (*tpconn.Conn, error) - Listen(port uint16) (*tplistener.Listener, error) + Dial(ctx context.Context, remote cipher.PubKey, port uint16) (*Conn, error) + Listen(port uint16) (*Listener, error) LocalAddr() (net.Addr, error) Serve() error Close() error diff --git a/pkg/transport/network/stcp.go b/pkg/transport/network/stcp.go index 660760bf0..e6e008700 100644 --- a/pkg/transport/network/stcp.go +++ b/pkg/transport/network/stcp.go @@ -43,7 +43,7 @@ func newStcp(PK cipher.PubKey, SK cipher.SecKey, addr string, table stcp.PKTable return client } -func (c *stcpClient) Dial(ctx context.Context, rPK cipher.PubKey, rPort uint16) (*tpconn.Conn, error) { +func (c *stcpClient) Dial(ctx context.Context, rPK cipher.PubKey, rPort uint16) (*Conn, error) { if c.isClosed() { return nil, io.ErrClosedPipe } @@ -70,7 +70,7 @@ func (c *stcpClient) Dial(ctx context.Context, rPK cipher.PubKey, rPort uint16) hs := tphandshake.InitiatorHandshake(c.SK, dmsg.Addr{PK: c.PK, Port: lPort}, dmsg.Addr{PK: rPK, Port: rPort}) - connConfig := tpconn.Config{ + connConfig := Config{ Log: c.log, Conn: conn, LocalPK: c.PK, @@ -81,15 +81,14 @@ func (c *stcpClient) Dial(ctx context.Context, rPK cipher.PubKey, rPort uint16) Encrypt: true, Initiator: true, } - - return tpconn.NewConn(connConfig) + return NewConn(connConfig) } // Listen starts listening on a specified port number. The port is a skywire port // and is not related to local OS ports. Underlying connection will most likely use // a different port number // Listen requires Serve to be called, which will accept connections to all skywire ports -func (c *stcpClient) Listen(port uint16) (*tplistener.Listener, error) { +func (c *stcpClient) Listen(port uint16) (*Listener, error) { if c.isClosed() { return nil, io.ErrClosedPipe } @@ -103,7 +102,7 @@ func (c *stcpClient) Listen(port uint16) (*tplistener.Listener, error) { defer c.mu.Unlock() lAddr := dmsg.Addr{PK: c.PK, Port: port} - lis := tplistener.NewListener(lAddr, freePort) + lis := NewListener(lAddr, freePort) c.listeners[port] = lis return lis, nil From 6329db9c3584f4f1b12e38e48ce7f938b6ef28d9 Mon Sep 17 00:00:00 2001 From: Never M Date: Wed, 23 Jun 2021 17:24:17 +0300 Subject: [PATCH 10/55] Unify tpconn.Conn and snet.Conn in network.Conn --- pkg/transport/network/connection.go | 30 ++++++++++++++++++++++------- pkg/transport/network/listener.go | 14 +++++++++----- pkg/transport/network/network.go | 1 + pkg/transport/network/stcp.go | 25 +++++++++++------------- 4 files changed, 44 insertions(+), 26 deletions(-) diff --git a/pkg/transport/network/connection.go b/pkg/transport/network/connection.go index a167727cd..08401fea0 100644 --- a/pkg/transport/network/connection.go +++ b/pkg/transport/network/connection.go @@ -16,17 +16,17 @@ import ( // Conn wraps an underlying net.Conn and modifies various methods to integrate better with the 'network' package. type Conn struct { net.Conn - lAddr dmsg.Addr - rAddr dmsg.Addr - freePort func() + lAddr, rAddr dmsg.Addr + freePort func() + connType Type } -// Config describes a config for Conn. -type Config struct { +// ConnConfig describes a config for Conn. +type ConnConfig struct { Log *logging.Logger Conn net.Conn - LocalPK cipher.PubKey LocalSK cipher.SecKey + LocalPK cipher.PubKey Deadline time.Time Handshake tphandshake.Handshake FreePort func() @@ -35,7 +35,8 @@ type Config struct { } // NewConn creates a new Conn. -func NewConn(c Config) (*Conn, error) { +// todo: move out handshake +func NewConn(c ConnConfig, connType Type) (*Conn, error) { if c.Log != nil { c.Log.Infof("Performing handshake with %v", c.Conn.RemoteAddr()) } @@ -100,3 +101,18 @@ func (c *Conn) Close() error { return c.Conn.Close() } + +// LocalPK returns local public key of connection. +func (c *Conn) LocalPK() cipher.PubKey { return c.lAddr.PK } + +// RemotePK returns remote public key of connection. +func (c *Conn) RemotePK() cipher.PubKey { return c.rAddr.PK } + +// LocalPort returns local port of connection. +func (c *Conn) LocalPort() uint16 { return c.lAddr.Port } + +// RemotePort returns remote port of connection. +func (c *Conn) RemotePort() uint16 { return c.rAddr.Port } + +// Network returns network of connection. +func (c *Conn) Network() string { return string(c.connType) } diff --git a/pkg/transport/network/listener.go b/pkg/transport/network/listener.go index 5df0dfd0b..fe4173eea 100644 --- a/pkg/transport/network/listener.go +++ b/pkg/transport/network/listener.go @@ -6,7 +6,6 @@ import ( "sync" "github.com/skycoin/dmsg" - "github.com/skycoin/skywire/pkg/snet/directtp/tpconn" ) // Listener represents a skywire network listener. It wraps net.Listener @@ -17,7 +16,7 @@ type Listener struct { mx sync.Mutex once sync.Once freePort func() - accept chan *tpconn.Conn + accept chan *Conn done chan struct{} } @@ -26,13 +25,13 @@ func NewListener(lAddr dmsg.Addr, freePort func()) *Listener { return &Listener{ lAddr: lAddr, freePort: freePort, - accept: make(chan *tpconn.Conn), + accept: make(chan *Conn), done: make(chan struct{}), } } // Introduce is used by Client to introduce Conn to Listener. -func (l *Listener) Introduce(conn *tpconn.Conn) error { +func (l *Listener) Introduce(conn *Conn) error { select { case <-l.done: return io.ErrClosedPipe @@ -49,8 +48,13 @@ func (l *Listener) Introduce(conn *tpconn.Conn) error { } } -// Accept implements net.Listener +// Accept implements net.Listener, returns generic net.Conn func (l *Listener) Accept() (net.Conn, error) { + return l.AcceptConn() +} + +// AcceptConn accepts a skywire connection and returns network.Conn +func (l *Listener) AcceptConn() (*Conn, error) { c, ok := <-l.accept if !ok { return nil, io.ErrClosedPipe diff --git a/pkg/transport/network/network.go b/pkg/transport/network/network.go index 44b1dc3e7..eb902faec 100644 --- a/pkg/transport/network/network.go +++ b/pkg/transport/network/network.go @@ -29,6 +29,7 @@ const ( // skywire address consisting of pulic key, port and // type of transport we connect over +// todo: use instead of dmsg.Addr for readability type addr struct { PK cipher.PubKey Port uint16 diff --git a/pkg/transport/network/stcp.go b/pkg/transport/network/stcp.go index e6e008700..504b48e08 100644 --- a/pkg/transport/network/stcp.go +++ b/pkg/transport/network/stcp.go @@ -13,9 +13,7 @@ import ( "github.com/skycoin/dmsg/cipher" "github.com/skycoin/skycoin/src/util/logging" "github.com/skycoin/skywire/pkg/snet/directtp/porter" - "github.com/skycoin/skywire/pkg/snet/directtp/tpconn" "github.com/skycoin/skywire/pkg/snet/directtp/tphandshake" - "github.com/skycoin/skywire/pkg/snet/directtp/tplistener" "github.com/skycoin/skywire/pkg/transport/network/stcp" ) @@ -26,7 +24,7 @@ type stcpClient struct { table stcp.PKTable connListener net.Listener // todo: change to listener wrapper type - listeners map[uint16]net.Listener + listeners map[uint16]*Listener log *logging.Logger mu sync.RWMutex listenStarted chan struct{} @@ -39,7 +37,7 @@ func newStcp(PK cipher.PubKey, SK cipher.SecKey, addr string, table stcp.PKTable client := &stcpClient{PK: PK, SK: SK, listenAddr: addr, table: table} client.listenStarted = make(chan struct{}) client.done = make(chan struct{}) - client.listeners = make(map[uint16]net.Listener) + client.listeners = make(map[uint16]*Listener) return client } @@ -67,13 +65,12 @@ func (c *stcpClient) Dial(ctx context.Context, rPK cipher.PubKey, rPort uint16) if err != nil { return nil, err } + lAddr, rAddr := dmsg.Addr{PK: c.PK, Port: lPort}, dmsg.Addr{PK: rPK, Port: rPort} + hs := tphandshake.InitiatorHandshake(c.SK, lAddr, rAddr) - hs := tphandshake.InitiatorHandshake(c.SK, dmsg.Addr{PK: c.PK, Port: lPort}, dmsg.Addr{PK: rPK, Port: rPort}) - - connConfig := Config{ + connConfig := ConnConfig{ Log: c.log, Conn: conn, - LocalPK: c.PK, LocalSK: c.SK, Deadline: time.Now().Add(tphandshake.Timeout), Handshake: hs, @@ -81,7 +78,7 @@ func (c *stcpClient) Dial(ctx context.Context, rPK cipher.PubKey, rPort uint16) Encrypt: true, Initiator: true, } - return NewConn(connConfig) + return NewConn(connConfig, STCP) } // Listen starts listening on a specified port number. The port is a skywire port @@ -174,14 +171,14 @@ func (c *stcpClient) acceptConn() error { // 2. wrap connection in our own connection type (for now tpconn.Conn then refactored wrapper) // 3. introduce wrapped connection to the listener - var lis *tplistener.Listener + var lis *Listener hs := tphandshake.ResponderHandshake(func(f2 tphandshake.Frame2) error { lis, err = c.getListener(f2.DstAddr.Port) return err }) - connConfig := tpconn.Config{ + connConfig := ConnConfig{ Log: c.log, Conn: conn, LocalPK: c.PK, @@ -193,7 +190,7 @@ func (c *stcpClient) acceptConn() error { Initiator: false, } - wrappedConn, err := tpconn.NewConn(connConfig) + wrappedConn, err := NewConn(connConfig, STCP) if err != nil { return err } @@ -208,14 +205,14 @@ func (c *stcpClient) acceptConn() error { // getListener returns listener to specified skywire port // todo: proper listener type // todo: move to generic client -func (c *stcpClient) getListener(port uint16) (*tplistener.Listener, error) { +func (c *stcpClient) getListener(port uint16) (*Listener, error) { c.mu.Lock() defer c.mu.Unlock() lis, ok := c.listeners[port] if !ok { return nil, errors.New("not listening on given port") } - return lis.(*tplistener.Listener), nil + return lis, nil } func (c *stcpClient) Close() error { From fec2378dfaafe00c2ecf2f8036754a458dad8254 Mon Sep 17 00:00:00 2001 From: Never M Date: Wed, 23 Jun 2021 17:49:19 +0300 Subject: [PATCH 11/55] Disable snet and use stcp client directly --- pkg/transport/handshake.go | 12 +- pkg/transport/handshake_test.go | 62 ---------- pkg/transport/managed_transport.go | 30 ++--- pkg/transport/manager.go | 88 ++++++++------ pkg/transport/manager_test.go | 184 ----------------------------- pkg/transport/network/listener.go | 9 +- pkg/transport/network/network.go | 15 ++- pkg/transport/network/stcp.go | 50 +++++--- pkg/visor/init.go | 54 +++++---- 9 files changed, 158 insertions(+), 346 deletions(-) delete mode 100644 pkg/transport/handshake_test.go delete mode 100644 pkg/transport/manager_test.go diff --git a/pkg/transport/handshake.go b/pkg/transport/handshake.go index 819f9245d..f4ec8c32c 100644 --- a/pkg/transport/handshake.go +++ b/pkg/transport/handshake.go @@ -11,10 +11,10 @@ import ( "github.com/skycoin/dmsg/cipher" "github.com/skycoin/dmsg/httputil" - "github.com/skycoin/skywire/pkg/snet" + "github.com/skycoin/skywire/pkg/transport/network" ) -func makeEntryFromTpConn(conn *snet.Conn) Entry { +func makeEntryFromTpConn(conn *network.Conn) Entry { return MakeEntry(conn.LocalPK(), conn.RemotePK(), conn.Network(), true, LabelUser) } @@ -63,10 +63,10 @@ func receiveAndVerifyEntry(r io.Reader, expected *Entry, remotePK cipher.PubKey) // SettlementHS represents a settlement handshake. // This is the handshake responsible for registering a transport to transport discovery. -type SettlementHS func(ctx context.Context, dc DiscoveryClient, conn *snet.Conn, sk cipher.SecKey) error +type SettlementHS func(ctx context.Context, dc DiscoveryClient, conn *network.Conn, sk cipher.SecKey) error // Do performs the settlement handshake. -func (hs SettlementHS) Do(ctx context.Context, dc DiscoveryClient, conn *snet.Conn, sk cipher.SecKey) (err error) { +func (hs SettlementHS) Do(ctx context.Context, dc DiscoveryClient, conn *network.Conn, sk cipher.SecKey) (err error) { done := make(chan struct{}) go func() { err = hs(ctx, dc, conn, sk) @@ -85,7 +85,7 @@ func (hs SettlementHS) Do(ctx context.Context, dc DiscoveryClient, conn *snet.Co // The handshake logic only REGISTERS the transport, and does not update the status of the transport. func MakeSettlementHS(init bool) SettlementHS { // initiating logic. - initHS := func(ctx context.Context, dc DiscoveryClient, conn *snet.Conn, sk cipher.SecKey) (err error) { + initHS := func(ctx context.Context, dc DiscoveryClient, conn *network.Conn, sk cipher.SecKey) (err error) { entry := makeEntryFromTpConn(conn) // TODO(evanlinjin): Probably not needed as this is called in mTp already. Need to double check. @@ -117,7 +117,7 @@ func MakeSettlementHS(init bool) SettlementHS { } // responding logic. - respHS := func(ctx context.Context, dc DiscoveryClient, conn *snet.Conn, sk cipher.SecKey) error { + respHS := func(ctx context.Context, dc DiscoveryClient, conn *network.Conn, sk cipher.SecKey) error { entry := makeEntryFromTpConn(conn) // receive, verify and sign entry. diff --git a/pkg/transport/handshake_test.go b/pkg/transport/handshake_test.go deleted file mode 100644 index d0225a768..000000000 --- a/pkg/transport/handshake_test.go +++ /dev/null @@ -1,62 +0,0 @@ -package transport_test - -import ( - "context" - "testing" - "time" - - "github.com/skycoin/dmsg" - "github.com/stretchr/testify/require" - - "github.com/skycoin/skywire/pkg/skyenv" - "github.com/skycoin/skywire/pkg/snet/snettest" - "github.com/skycoin/skywire/pkg/transport" -) - -func TestSettlementHS(t *testing.T) { - tpDisc := transport.NewDiscoveryMock() - - keys := snettest.GenKeyPairs(2) - nEnv := snettest.NewEnv(t, keys, []string{dmsg.Type}) - defer nEnv.Teardown() - - // TEST: Perform a handshake between two snet.Network instances. - t.Run("Do", func(t *testing.T) { - lis1, err := nEnv.Nets[1].Listen(dmsg.Type, skyenv.DmsgTransportPort) - require.NoError(t, err) - - errCh1 := make(chan error, 1) - go func() { - defer close(errCh1) - conn1, err := lis1.AcceptConn() - if err != nil { - errCh1 <- err - return - } - errCh1 <- transport.MakeSettlementHS(false).Do(context.TODO(), tpDisc, conn1, keys[1].SK) - }() - - const entryTimeout = 5 * time.Second - start := time.Now() - - // Wait until entry is set. - // TODO: Implement more elegant solution. - for { - if time.Since(start) > entryTimeout { - t.Fatal("Entry in Dmsg Discovery is not set within expected time") - } - - if _, err := nEnv.DmsgD.Entry(context.TODO(), keys[1].PK); err == nil { - break - } - } - - conn0, err := nEnv.Nets[0].Dial(context.TODO(), dmsg.Type, keys[1].PK, skyenv.DmsgTransportPort) - require.NoError(t, err) - require.NoError(t, transport.MakeSettlementHS(true).Do(context.TODO(), tpDisc, conn0, keys[0].SK)) - - require.NoError(t, <-errCh1) - }) -} - -// TODO(evanlinjin): This will need further testing. diff --git a/pkg/transport/managed_transport.go b/pkg/transport/managed_transport.go index 63dc345e9..fa25707db 100644 --- a/pkg/transport/managed_transport.go +++ b/pkg/transport/managed_transport.go @@ -18,7 +18,7 @@ import ( "github.com/skycoin/skywire/pkg/routing" "github.com/skycoin/skywire/pkg/skyenv" - "github.com/skycoin/skywire/pkg/snet" + "github.com/skycoin/skywire/pkg/transport/network" ) const logWriteInterval = time.Second * 3 @@ -44,7 +44,7 @@ const ( // ManagedTransportConfig is a configuration for managed transport. type ManagedTransportConfig struct { - Net *snet.Network + client network.Client DC DiscoveryClient LS LogStore RemotePK cipher.PubKey @@ -76,8 +76,8 @@ type ManagedTransport struct { redialCancel context.CancelFunc // for canceling redialling logic redialMx sync.Mutex - n *snet.Network - conn *snet.Conn + client network.Client + conn *network.Conn connCh chan struct{} connMx sync.Mutex @@ -97,10 +97,10 @@ func NewManagedTransport(conf ManagedTransportConfig) *ManagedTransport { log: logging.MustGetLogger(fmt.Sprintf("tp:%s", conf.RemotePK.String()[:6])), rPK: conf.RemotePK, netName: conf.NetName, - n: conf.Net, dc: conf.DC, ls: conf.LS, - Entry: MakeEntry(conf.Net.LocalPK(), conf.RemotePK, conf.NetName, true, conf.TransportLabel), + client: conf.client, + Entry: MakeEntry(conf.client.PK(), conf.RemotePK, conf.NetName, true, conf.TransportLabel), LogEntry: new(LogEntry), connCh: make(chan struct{}, 1), done: make(chan struct{}), @@ -266,7 +266,7 @@ func (mt *ManagedTransport) disconnect() { } // Accept accepts a new underlying connection. -func (mt *ManagedTransport) Accept(ctx context.Context, conn *snet.Conn) error { +func (mt *ManagedTransport) Accept(ctx context.Context, conn *network.Conn) error { mt.connMx.Lock() defer mt.connMx.Unlock() @@ -287,7 +287,7 @@ func (mt *ManagedTransport) Accept(ctx context.Context, conn *snet.Conn) error { defer cancel() mt.log.Debug("Performing settlement handshake...") - if err := MakeSettlementHS(false).Do(ctx, mt.dc, conn, mt.n.LocalSK()); err != nil { + if err := MakeSettlementHS(false).Do(ctx, mt.dc, conn, mt.client.SK()); err != nil { return fmt.Errorf("settlement handshake failed: %w", err) } @@ -311,7 +311,7 @@ func (mt *ManagedTransport) Dial(ctx context.Context) error { } func (mt *ManagedTransport) dial(ctx context.Context) error { - tp, err := mt.n.Dial(ctx, mt.netName, mt.rPK, skyenv.DmsgTransportPort) + conn, err := mt.client.Dial(ctx, mt.rPK, skyenv.DmsgTransportPort) if err != nil { return fmt.Errorf("snet.Dial: %w", err) } @@ -319,11 +319,11 @@ func (mt *ManagedTransport) dial(ctx context.Context) error { ctx, cancel := context.WithTimeout(ctx, time.Second*20) defer cancel() - if err := MakeSettlementHS(true).Do(ctx, mt.dc, tp, mt.n.LocalSK()); err != nil { + if err := MakeSettlementHS(true).Do(ctx, mt.dc, conn, mt.client.SK()); err != nil { return fmt.Errorf("settlement handshake failed: %w", err) } - if err := mt.setConn(tp); err != nil { + if err := mt.setConn(conn); err != nil { return fmt.Errorf("setConn: %w", err) } @@ -381,14 +381,14 @@ func (mt *ManagedTransport) redialLoop(ctx context.Context) error { } func (mt *ManagedTransport) isLeastSignificantEdge() bool { - return mt.Entry.EdgeIndex(mt.n.LocalPK()) == 0 + return mt.Entry.EdgeIndex(mt.client.PK()) == 0 } /* <<< UNDERLYING CONNECTION >>> */ -func (mt *ManagedTransport) getConn() *snet.Conn { +func (mt *ManagedTransport) getConn() *network.Conn { if !mt.isServing() { return nil } @@ -401,7 +401,7 @@ func (mt *ManagedTransport) getConn() *snet.Conn { // setConn sets 'mt.conn' (the underlying connection). // If 'mt.conn' is already occupied, close the newly introduced connection. -func (mt *ManagedTransport) setConn(newConn *snet.Conn) error { +func (mt *ManagedTransport) setConn(newConn *network.Conn) error { if mt.conn != nil { if mt.isLeastSignificantEdge() { @@ -561,7 +561,7 @@ func (mt *ManagedTransport) WritePacket(ctx context.Context, packet routing.Pack func (mt *ManagedTransport) readPacket() (packet routing.Packet, err error) { log := mt.log.WithField("func", "readPacket") - var conn *snet.Conn + var conn *network.Conn for { if conn = mt.getConn(); conn != nil { break diff --git a/pkg/transport/manager.go b/pkg/transport/manager.go index c0ae29407..a70af882a 100644 --- a/pkg/transport/manager.go +++ b/pkg/transport/manager.go @@ -20,6 +20,7 @@ import ( "github.com/skycoin/skywire/pkg/snet/directtp" "github.com/skycoin/skywire/pkg/snet/directtp/tptypes" "github.com/skycoin/skywire/pkg/snet/snettest" + "github.com/skycoin/skywire/pkg/transport/network" ) // TPCloseCallback triggers after a session is closed. @@ -38,13 +39,12 @@ type Manager struct { Logger *logging.Logger Conf *ManagerConfig tps map[uuid.UUID]*ManagedTransport - n *snet.Network arClient arclient.APIClient listenersMu sync.Mutex - listeners []*snet.Listener + listeners []*network.Listener servingNetsMu sync.Mutex - servingNets map[string]struct{} + servingNets map[network.Type]struct{} readCh chan routing.Packet mx sync.RWMutex wgMu sync.Mutex @@ -54,32 +54,30 @@ type Manager struct { done chan struct{} afterTPClosed TPCloseCallback + factory network.ClientFactory + netClients map[network.Type]network.Client } // NewManager creates a Manager with the provided configuration and transport factories. // 'factories' should be ordered by preference. -func NewManager(log *logging.Logger, n *snet.Network, arClient arclient.APIClient, config *ManagerConfig) (*Manager, error) { +func NewManager(log *logging.Logger, arClient arclient.APIClient, config *ManagerConfig, factory network.ClientFactory) (*Manager, error) { if log == nil { log = logging.MustGetLogger("tp_manager") } tm := &Manager{ Logger: log, Conf: config, - servingNets: make(map[string]struct{}), + servingNets: make(map[network.Type]struct{}), tps: make(map[uuid.UUID]*ManagedTransport), - n: n, readCh: make(chan routing.Packet, 20), done: make(chan struct{}), + netClients: make(map[network.Type]network.Client), arClient: arClient, + factory: factory, } return tm, nil } -// STcpr returns client for this transport and success status -func (tm *Manager) STcpr() (directtp.Client, bool) { - return tm.n.STcpr() -} - // OnAfterTPClosed sets callback which will fire after transport gets closed. func (tm *Manager) OnAfterTPClosed(f TPCloseCallback) { tm.mx.Lock() @@ -96,11 +94,21 @@ func (tm *Manager) OnAfterTPClosed(f TPCloseCallback) { // Serve runs listening loop across all registered factories. func (tm *Manager) Serve(ctx context.Context) { tm.serveOnce.Do(func() { + tm.netClients[network.STCP] = tm.factory.MakeClient(network.STCP) tm.serve(ctx) }) } -func (tm *Manager) serveNetwork(ctx context.Context, netType string) { +// Networks returns all the network types contained within the TransportManager. +func (tm *Manager) Networks() []string { + var nets []string + for netType := range tm.netClients { + nets = append(nets, string(netType)) + } + return nets +} + +func (tm *Manager) serveNet(ctx context.Context, netType network.Type) { if tm.isClosing() { return } @@ -115,7 +123,12 @@ func (tm *Manager) serveNetwork(ctx context.Context, netType string) { tm.servingNets[netType] = struct{}{} tm.servingNetsMu.Unlock() - lis, err := tm.n.Listen(netType, skyenv.DmsgTransportPort) + client, ok := tm.netClients[netType] + if !ok { + return + } + + lis, err := client.Listen(skyenv.DmsgTransportPort) if err != nil { tm.Logger.WithError(err).Fatalf("failed to listen on network '%s' of port '%d'", netType, skyenv.DmsgTransportPort) @@ -155,15 +168,9 @@ func (tm *Manager) serveNetwork(ctx context.Context, netType string) { } func (tm *Manager) serve(ctx context.Context) { - // TODO: to get rid of this callback, we need to have method on future network interface like: `Ready() <-chan struct{}` - // some networks may not be ready yet, so we're setting a callback first - tm.n.OnNewNetworkType(func(netType string) { - tm.serveNetwork(ctx, netType) - }) - // here we may start serving all the networks which are ready at this point - for _, netType := range tm.n.TransportNetworks() { - tm.serveNetwork(ctx, netType) + for netType := range tm.netClients { + tm.serveNet(ctx, netType) } tm.initTransports(ctx) @@ -207,7 +214,11 @@ func (tm *Manager) initTransports(ctx context.Context) { } } -func (tm *Manager) acceptTransport(ctx context.Context, lis *snet.Listener) error { +func (tm *Manager) STcpr() (directtp.Client, bool) { + return nil, false +} + +func (tm *Manager) acceptTransport(ctx context.Context, lis *network.Listener) error { conn, err := lis.AcceptConn() // TODO: tcp panic. if err != nil { return err @@ -226,12 +237,17 @@ func (tm *Manager) acceptTransport(ctx context.Context, lis *snet.Listener) erro tpID := tm.tpIDFromPK(conn.RemotePK(), conn.Network()) + client, ok := tm.netClients[network.Type(conn.Network())] + if !ok { + return fmt.Errorf("client not found for the type %s", conn.Network()) + } + mTp, ok := tm.tps[tpID] if !ok { tm.Logger.Debugln("No TP found, creating new one") mTp = NewManagedTransport(ManagedTransportConfig{ - Net: tm.n, + client: client, DC: tm.Conf.DiscoveryClient, LS: tm.Conf.LogStore, RemotePK: conn.RemotePK(), @@ -358,10 +374,15 @@ func (tm *Manager) saveTransport(remote cipher.PubKey, netName string, label Lab return oldMTp, nil } + client, ok := tm.netClients[network.Type(netName)] + if !ok { + return nil, fmt.Errorf("client not found for the type %s", netName) + } + afterTPClosed := tm.afterTPClosed mTp := NewManagedTransport(ManagedTransportConfig{ - Net: tm.n, + client: client, DC: tm.Conf.DiscoveryClient, LS: tm.Conf.LogStore, RemotePK: remote, @@ -453,11 +474,6 @@ func (tm *Manager) ReadPacket() (routing.Packet, error) { STATE */ -// Networks returns all the network types contained within the TransportManager. -func (tm *Manager) Networks() []string { - return tm.n.TransportNetworks() -} - // Transport obtains a Transport via a given Transport ID. func (tm *Manager) Transport(id uuid.UUID) *ManagedTransport { tm.mx.RLock() @@ -533,17 +549,17 @@ func CreateTransportPair( tpDisc DiscoveryClient, keys []snettest.KeyPair, nEnv *snettest.Env, - network string, + net string, ) (m0 *Manager, m1 *Manager, tp0 *ManagedTransport, tp1 *ManagedTransport, err error) { // Prepare tp manager 0. pk0, sk0 := keys[0].PK, keys[0].SK ls0 := InMemoryTransportLogStore() - m0, err = NewManager(nil, nEnv.Nets[0], new(arclient.MockAPIClient), &ManagerConfig{ + m0, err = NewManager(nil, new(arclient.MockAPIClient), &ManagerConfig{ PubKey: pk0, SecKey: sk0, DiscoveryClient: tpDisc, LogStore: ls0, - }) + }, network.ClientFactory{}) if err != nil { return nil, nil, nil, nil, err } @@ -553,12 +569,12 @@ func CreateTransportPair( // Prepare tp manager 1. pk1, sk1 := keys[1].PK, keys[1].SK ls1 := InMemoryTransportLogStore() - m1, err = NewManager(nil, nEnv.Nets[1], new(arclient.MockAPIClient), &ManagerConfig{ + m1, err = NewManager(nil, new(arclient.MockAPIClient), &ManagerConfig{ PubKey: pk1, SecKey: sk1, DiscoveryClient: tpDisc, LogStore: ls1, - }) + }, network.ClientFactory{}) if err != nil { return nil, nil, nil, nil, err } @@ -566,12 +582,12 @@ func CreateTransportPair( go m1.Serve(context.TODO()) // Create data transport between manager 1 & manager 2. - tp1, err = m1.SaveTransport(context.TODO(), pk0, network, LabelUser) + tp1, err = m1.SaveTransport(context.TODO(), pk0, net, LabelUser) if err != nil { return nil, nil, nil, nil, err } - tp0 = m0.Transport(MakeTransportID(pk0, pk1, network)) + tp0 = m0.Transport(MakeTransportID(pk0, pk1, net)) return m0, m1, tp0, tp1, nil } diff --git a/pkg/transport/manager_test.go b/pkg/transport/manager_test.go deleted file mode 100644 index f8f1150db..000000000 --- a/pkg/transport/manager_test.go +++ /dev/null @@ -1,184 +0,0 @@ -package transport_test - -import ( - "context" - "fmt" - "io/ioutil" - "log" - "os" - "testing" - "time" - - "github.com/skycoin/dmsg" - "github.com/skycoin/dmsg/cipher" - "github.com/skycoin/skycoin/src/util/logging" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/skycoin/skywire/pkg/routing" - "github.com/skycoin/skywire/pkg/snet/arclient" - "github.com/skycoin/skywire/pkg/snet/snettest" - "github.com/skycoin/skywire/pkg/transport" -) - -var masterLogger *logging.MasterLogger - -func TestMain(m *testing.M) { - masterLogger = logging.NewMasterLogger() - loggingLevel, ok := os.LookupEnv("TEST_LOGGING_LEVEL") - if ok { - lvl, err := logging.LevelFromString(loggingLevel) - if err != nil { - log.Fatal(err) - } - masterLogger.SetLevel(lvl) - } else { - masterLogger.Out = ioutil.Discard - } - - os.Exit(m.Run()) -} - -// TODO: test hangs if Manager is closed to early, needs to receive an error though -func TestNewManager(t *testing.T) { - tpDisc := transport.NewDiscoveryMock() - - keys := snettest.GenKeyPairs(2) - nEnv := snettest.NewEnv(t, keys, []string{dmsg.Type}) - defer nEnv.Teardown() - - // Prepare tp manager 0. - pk0, sk0 := keys[0].PK, keys[0].SK - ls0 := transport.InMemoryTransportLogStore() - m0, err := transport.NewManager(nil, nEnv.Nets[0], new(arclient.MockAPIClient), &transport.ManagerConfig{ - PubKey: pk0, - SecKey: sk0, - DiscoveryClient: tpDisc, - LogStore: ls0, - }) - require.NoError(t, err) - go m0.Serve(context.TODO()) - defer func() { require.NoError(t, m0.Close()) }() - - // Prepare tp manager 1. - pk1, sk1 := keys[1].PK, keys[1].SK - ls1 := transport.InMemoryTransportLogStore() - m2, err := transport.NewManager(nil, nEnv.Nets[1], new(arclient.MockAPIClient), &transport.ManagerConfig{ - PubKey: pk1, - SecKey: sk1, - DiscoveryClient: tpDisc, - LogStore: ls1, - }) - require.NoError(t, err) - go m2.Serve(context.TODO()) - defer func() { require.NoError(t, m2.Close()) }() - - // Create data transport between manager 1 & manager 2. - tp2, err := m2.SaveTransport(context.TODO(), pk0, "dmsg", transport.LabelUser) - require.NoError(t, err) - tp1 := m0.Transport(transport.MakeTransportID(pk0, pk1, "dmsg")) - require.NotNil(t, tp1) - - fmt.Println("transports created") - - totalSent2 := 0 - totalSent1 := 0 - - // Check read/writes are of expected. - t.Run("check_read_write", func(t *testing.T) { - - for i := 0; i < 10; i++ { - totalSent2 += i - rID := routing.RouteID(i) - payload := cipher.RandByte(i) - - packet, err := routing.MakeDataPacket(rID, payload) - require.NoError(t, err) - - require.NoError(t, tp2.WritePacket(context.TODO(), packet)) - - recv, err := m0.ReadPacket() - require.NoError(t, err) - require.Equal(t, rID, recv.RouteID()) - require.Equal(t, uint16(i), recv.Size()) - require.Equal(t, payload, recv.Payload()) - } - - for i := 0; i < 20; i++ { - totalSent1 += i - rID := routing.RouteID(i) - payload := cipher.RandByte(i) - - packet, err := routing.MakeDataPacket(rID, payload) - require.NoError(t, err) - - require.NoError(t, tp1.WritePacket(context.TODO(), packet)) - - recv, err := m2.ReadPacket() - require.NoError(t, err) - require.Equal(t, rID, recv.RouteID()) - require.Equal(t, uint16(i), recv.Size()) - require.Equal(t, payload, recv.Payload()) - } - }) - - // Ensure tp log entries are of expected. - t.Run("check_tp_logs", func(t *testing.T) { - - // 1.5x log write interval just to be safe. - time.Sleep(time.Second * 9 / 2) - - entry1, err := ls0.Entry(tp1.Entry.ID) - require.NoError(t, err) - assert.Equal(t, uint64(totalSent1), entry1.SentBytes) - assert.Equal(t, uint64(totalSent2), entry1.RecvBytes) - - entry2, err := ls1.Entry(tp2.Entry.ID) - require.NoError(t, err) - assert.Equal(t, uint64(totalSent2), entry2.SentBytes) - assert.Equal(t, uint64(totalSent1), entry2.RecvBytes) - }) - - // Ensure deleting a transport works as expected. - t.Run("check_delete_tp", func(t *testing.T) { - - // Make transport ID. - tpID := transport.MakeTransportID(pk0, pk1, "dmsg") - - // Ensure transports are registered properly in tp discovery. - entry, err := tpDisc.GetTransportByID(context.TODO(), tpID) - require.NoError(t, err) - assert.Equal(t, transport.SortEdges(pk0, pk1), entry.Entry.Edges) - assert.True(t, entry.IsUp) - - m2.DeleteTransport(tp2.Entry.ID) - - _, err = tpDisc.GetTransportByID(context.TODO(), tpID) - require.NotNil(t, err) - require.Contains(t, err.Error(), "not found") - }) -} - -func TestSortEdges(t *testing.T) { - for i := 0; i < 100; i++ { - keyA, _ := cipher.GenerateKeyPair() - keyB, _ := cipher.GenerateKeyPair() - require.Equal(t, transport.SortEdges(keyA, keyB), transport.SortEdges(keyB, keyA)) - } -} - -func TestMakeTransportID(t *testing.T) { - t.Run("id_is_stable", func(t *testing.T) { - for i := 0; i < 100; i++ { - keyA, _ := cipher.GenerateKeyPair() - keyB, _ := cipher.GenerateKeyPair() - idAB := transport.MakeTransportID(keyA, keyB, "type") - idBA := transport.MakeTransportID(keyB, keyA, "type") - require.Equal(t, idAB, idBA) - } - }) - t.Run("tpType_changes_id", func(t *testing.T) { - keyA, _ := cipher.GenerateKeyPair() - require.NotEqual(t, transport.MakeTransportID(keyA, keyA, "a"), transport.MakeTransportID(keyA, keyA, "b")) - }) -} diff --git a/pkg/transport/network/listener.go b/pkg/transport/network/listener.go index fe4173eea..fbb190373 100644 --- a/pkg/transport/network/listener.go +++ b/pkg/transport/network/listener.go @@ -18,15 +18,17 @@ type Listener struct { freePort func() accept chan *Conn done chan struct{} + network Type } // NewListener returns a new Listener. -func NewListener(lAddr dmsg.Addr, freePort func()) *Listener { +func NewListener(lAddr dmsg.Addr, freePort func(), network Type) *Listener { return &Listener{ lAddr: lAddr, freePort: freePort, accept: make(chan *Conn), done: make(chan struct{}), + network: network, } } @@ -82,3 +84,8 @@ func (l *Listener) Close() error { func (l *Listener) Addr() net.Addr { return l.lAddr } + +// Network returns network type +func (l *Listener) Network() string { + return string(l.network) +} diff --git a/pkg/transport/network/network.go b/pkg/transport/network/network.go index eb902faec..31fd8d90b 100644 --- a/pkg/transport/network/network.go +++ b/pkg/transport/network/network.go @@ -7,7 +7,10 @@ import ( "github.com/skycoin/dmsg" "github.com/skycoin/dmsg/cipher" + "github.com/skycoin/skycoin/src/util/logging" + "github.com/skycoin/skywire/pkg/app/appevent" "github.com/skycoin/skywire/pkg/snet/arclient" + "github.com/skycoin/skywire/pkg/snet/directtp/porter" "github.com/skycoin/skywire/pkg/transport/network/stcp" ) @@ -64,6 +67,8 @@ type Client interface { Dial(ctx context.Context, remote cipher.PubKey, port uint16) (*Conn, error) Listen(port uint16) (*Listener, error) LocalAddr() (net.Addr, error) + PK() cipher.PubKey + SK() cipher.SecKey Serve() error Close() error Type() Type @@ -94,9 +99,15 @@ type ClientFactory struct { ListenAddr string PKTable stcp.PKTable ARClient arclient.APIClient + eb *appevent.Broadcaster } // MakeClient creates a new client of specified type -func (f *ClientFactory) MakeClient() Client { - return newStcp(f.PK, f.SK, f.ListenAddr, f.PKTable) +func (f *ClientFactory) MakeClient(netType Type) Client { + log := logging.MustGetLogger(string(netType)) + p := porter.New(porter.MinEphemeral) + if netType == STCP { + return newStcp(f.PK, f.SK, f.ListenAddr, f.eb, f.PKTable, p, log) + } + return nil } diff --git a/pkg/transport/network/stcp.go b/pkg/transport/network/stcp.go index 504b48e08..dd03a11b2 100644 --- a/pkg/transport/network/stcp.go +++ b/pkg/transport/network/stcp.go @@ -12,32 +12,38 @@ import ( "github.com/skycoin/dmsg" "github.com/skycoin/dmsg/cipher" "github.com/skycoin/skycoin/src/util/logging" + "github.com/skycoin/skywire/pkg/app/appevent" "github.com/skycoin/skywire/pkg/snet/directtp/porter" "github.com/skycoin/skywire/pkg/snet/directtp/tphandshake" "github.com/skycoin/skywire/pkg/transport/network/stcp" ) type stcpClient struct { - PK cipher.PubKey - SK cipher.SecKey - listenAddr string - table stcp.PKTable - connListener net.Listener - // todo: change to listener wrapper type + lPK cipher.PubKey + lSK cipher.SecKey + listenAddr string + + table stcp.PKTable + log *logging.Logger + porter *porter.Porter + eb *appevent.Broadcaster + + connListener net.Listener listeners map[uint16]*Listener - log *logging.Logger - mu sync.RWMutex listenStarted chan struct{} + mu sync.RWMutex done chan struct{} - porter *porter.Porter closeOnce sync.Once } -func newStcp(PK cipher.PubKey, SK cipher.SecKey, addr string, table stcp.PKTable) Client { - client := &stcpClient{PK: PK, SK: SK, listenAddr: addr, table: table} +func newStcp(PK cipher.PubKey, SK cipher.SecKey, addr string, eb *appevent.Broadcaster, table stcp.PKTable, porter *porter.Porter, log *logging.Logger) Client { + client := &stcpClient{lPK: PK, lSK: SK, listenAddr: addr, table: table} client.listenStarted = make(chan struct{}) client.done = make(chan struct{}) client.listeners = make(map[uint16]*Listener) + client.log = log + client.porter = porter + client.eb = eb return client } @@ -65,13 +71,13 @@ func (c *stcpClient) Dial(ctx context.Context, rPK cipher.PubKey, rPort uint16) if err != nil { return nil, err } - lAddr, rAddr := dmsg.Addr{PK: c.PK, Port: lPort}, dmsg.Addr{PK: rPK, Port: rPort} - hs := tphandshake.InitiatorHandshake(c.SK, lAddr, rAddr) + lAddr, rAddr := dmsg.Addr{PK: c.lPK, Port: lPort}, dmsg.Addr{PK: rPK, Port: rPort} + hs := tphandshake.InitiatorHandshake(c.lSK, lAddr, rAddr) connConfig := ConnConfig{ Log: c.log, Conn: conn, - LocalSK: c.SK, + LocalSK: c.lSK, Deadline: time.Now().Add(tphandshake.Timeout), Handshake: hs, FreePort: freePort, @@ -98,8 +104,8 @@ func (c *stcpClient) Listen(port uint16) (*Listener, error) { c.mu.Lock() defer c.mu.Unlock() - lAddr := dmsg.Addr{PK: c.PK, Port: port} - lis := NewListener(lAddr, freePort) + lAddr := dmsg.Addr{PK: c.lPK, Port: port} + lis := NewListener(lAddr, freePort, STCP) c.listeners[port] = lis return lis, nil @@ -181,8 +187,8 @@ func (c *stcpClient) acceptConn() error { connConfig := ConnConfig{ Log: c.log, Conn: conn, - LocalPK: c.PK, - LocalSK: c.SK, + LocalPK: c.lPK, + LocalSK: c.lSK, Deadline: time.Now().Add(tphandshake.Timeout), Handshake: hs, FreePort: nil, @@ -250,3 +256,11 @@ func (c *stcpClient) isClosed() bool { func (c *stcpClient) Type() Type { return STCP } + +func (c *stcpClient) PK() cipher.PubKey { + return c.lPK +} + +func (c *stcpClient) SK() cipher.SecKey { + return c.lSK +} diff --git a/pkg/visor/init.go b/pkg/visor/init.go index 8303396fe..15ac0bc26 100644 --- a/pkg/visor/init.go +++ b/pkg/visor/init.go @@ -28,11 +28,12 @@ import ( "github.com/skycoin/skywire/pkg/servicedisc" "github.com/skycoin/skywire/pkg/setup/setupclient" "github.com/skycoin/skywire/pkg/skyenv" - "github.com/skycoin/skywire/pkg/snet" "github.com/skycoin/skywire/pkg/snet/arclient" + "github.com/skycoin/skywire/pkg/snet/directtp/pktable" "github.com/skycoin/skywire/pkg/snet/directtp/tptypes" "github.com/skycoin/skywire/pkg/snet/dmsgc" "github.com/skycoin/skywire/pkg/transport" + "github.com/skycoin/skywire/pkg/transport/network" "github.com/skycoin/skywire/pkg/transport/tpdclient" "github.com/skycoin/skywire/pkg/util/netutil" "github.com/skycoin/skywire/pkg/util/updater" @@ -260,27 +261,36 @@ func initTransport(ctx context.Context, v *Visor, log *logging.Logger) error { } managerLogger := v.MasterLogger().PackageLogger("transport_manager") - nc := snet.NetworkConfigs{ - STCP: v.conf.STCP, - } - - netconf := snet.Config{ - PubKey: v.conf.PK, - SecKey: v.conf.SK, - NetworkConfigs: nc, - } - - n, err := snet.New(netconf, v.dmsgC, v.ebc, v.arClient) - if err != nil { - return err - } - - if err := n.Init(); err != nil { - return err - } - v.pushCloseStack("snet", n.Close) - - tpM, err := transport.NewManager(managerLogger, n, v.arClient, &tpMConf) + // nc := snet.NetworkConfigs{ + // STCP: v.conf.STCP, + // } + + // netconf := snet.Config{ + // PubKey: v.conf.PK, + // SecKey: v.conf.SK, + // NetworkConfigs: nc, + // } + + // n, err := snet.New(netconf, v.dmsgC, v.ebc, v.arClient) + // if err != nil { + // return err + // } + + // if err := n.Init(); err != nil { + // return err + // } + // v.pushCloseStack("snet", n.Close) + + // todo: pass down configuration? + table := pktable.NewTable(v.conf.STCP.PKTable) + factory := network.ClientFactory{ + PK: v.conf.PK, + SK: v.conf.SK, + ListenAddr: v.conf.STCP.LocalAddr, + PKTable: table, + ARClient: v.arClient, + } + tpM, err := transport.NewManager(managerLogger, v.arClient, &tpMConf, factory) if err != nil { err := fmt.Errorf("failed to start transport manager: %w", err) return err From 5f790b4652b610261662b92d0b73f42ae18253f5 Mon Sep 17 00:00:00 2001 From: Never M Date: Thu, 24 Jun 2021 12:10:43 +0300 Subject: [PATCH 12/55] Fix connection encryption --- pkg/transport/manager.go | 4 +++- pkg/transport/network/stcp.go | 4 ++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/pkg/transport/manager.go b/pkg/transport/manager.go index a70af882a..24f70834f 100644 --- a/pkg/transport/manager.go +++ b/pkg/transport/manager.go @@ -94,7 +94,9 @@ func (tm *Manager) OnAfterTPClosed(f TPCloseCallback) { // Serve runs listening loop across all registered factories. func (tm *Manager) Serve(ctx context.Context) { tm.serveOnce.Do(func() { - tm.netClients[network.STCP] = tm.factory.MakeClient(network.STCP) + client := tm.factory.MakeClient(network.STCP) + tm.netClients[network.STCP] = client + go client.Serve() tm.serve(ctx) }) } diff --git a/pkg/transport/network/stcp.go b/pkg/transport/network/stcp.go index dd03a11b2..8626e35e0 100644 --- a/pkg/transport/network/stcp.go +++ b/pkg/transport/network/stcp.go @@ -77,6 +77,7 @@ func (c *stcpClient) Dial(ctx context.Context, rPK cipher.PubKey, rPort uint16) connConfig := ConnConfig{ Log: c.log, Conn: conn, + LocalPK: c.lPK, LocalSK: c.lSK, Deadline: time.Now().Add(tphandshake.Timeout), Handshake: hs, @@ -181,6 +182,9 @@ func (c *stcpClient) acceptConn() error { hs := tphandshake.ResponderHandshake(func(f2 tphandshake.Frame2) error { lis, err = c.getListener(f2.DstAddr.Port) + if err != nil { + c.log.Errorf("cannot get listener for port %d", f2.DstAddr.Port) + } return err }) From f0909edb067b558e33338396a3ba56d8b91f30bb Mon Sep 17 00:00:00 2001 From: Never M Date: Thu, 24 Jun 2021 12:26:07 +0300 Subject: [PATCH 13/55] Fix connection type in network.Conn --- pkg/transport/network/connection.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/transport/network/connection.go b/pkg/transport/network/connection.go index 08401fea0..f871ff198 100644 --- a/pkg/transport/network/connection.go +++ b/pkg/transport/network/connection.go @@ -80,7 +80,7 @@ func NewConn(c ConnConfig, connType Type) (*Conn, error) { c.Log.Infof("Connection with %v@%v is NOT encrypted", rAddr, c.Conn.RemoteAddr()) } - return &Conn{Conn: c.Conn, lAddr: lAddr, rAddr: rAddr, freePort: c.FreePort}, nil + return &Conn{Conn: c.Conn, lAddr: lAddr, rAddr: rAddr, freePort: c.FreePort, connType: connType}, nil } // LocalAddr implements net.Conn From 2000e2b6d07a6bd45ca7d64769beaecd3bcd5203 Mon Sep 17 00:00:00 2001 From: Never M Date: Thu, 24 Jun 2021 12:59:30 +0300 Subject: [PATCH 14/55] Simplify transport manager startup --- pkg/transport/manager.go | 169 ++++++++++++++-------------------- pkg/transport/network/stcp.go | 2 + 2 files changed, 72 insertions(+), 99 deletions(-) diff --git a/pkg/transport/manager.go b/pkg/transport/manager.go index 24f70834f..7cdf56991 100644 --- a/pkg/transport/manager.go +++ b/pkg/transport/manager.go @@ -41,17 +41,13 @@ type Manager struct { tps map[uuid.UUID]*ManagedTransport arClient arclient.APIClient - listenersMu sync.Mutex - listeners []*network.Listener - servingNetsMu sync.Mutex - servingNets map[network.Type]struct{} - readCh chan routing.Packet - mx sync.RWMutex - wgMu sync.Mutex - wg sync.WaitGroup - serveOnce sync.Once // ensure we only serve once. - closeOnce sync.Once // ensure we only close once. - done chan struct{} + readCh chan routing.Packet + mx sync.RWMutex + wgMu sync.Mutex + wg sync.WaitGroup + serveOnce sync.Once // ensure we only serve once. + closeOnce sync.Once // ensure we only close once. + done chan struct{} afterTPClosed TPCloseCallback factory network.ClientFactory @@ -65,15 +61,14 @@ func NewManager(log *logging.Logger, arClient arclient.APIClient, config *Manage log = logging.MustGetLogger("tp_manager") } tm := &Manager{ - Logger: log, - Conf: config, - servingNets: make(map[network.Type]struct{}), - tps: make(map[uuid.UUID]*ManagedTransport), - readCh: make(chan routing.Packet, 20), - done: make(chan struct{}), - netClients: make(map[network.Type]network.Client), - arClient: arClient, - factory: factory, + Logger: log, + Conf: config, + tps: make(map[uuid.UUID]*ManagedTransport), + readCh: make(chan routing.Packet, 20), + done: make(chan struct{}), + netClients: make(map[network.Type]network.Client), + arClient: arClient, + factory: factory, } return tm, nil } @@ -94,104 +89,80 @@ func (tm *Manager) OnAfterTPClosed(f TPCloseCallback) { // Serve runs listening loop across all registered factories. func (tm *Manager) Serve(ctx context.Context) { tm.serveOnce.Do(func() { - client := tm.factory.MakeClient(network.STCP) - tm.netClients[network.STCP] = client - go client.Serve() tm.serve(ctx) }) } -// Networks returns all the network types contained within the TransportManager. -func (tm *Manager) Networks() []string { - var nets []string - for netType := range tm.netClients { - nets = append(nets, string(netType)) - } - return nets -} - -func (tm *Manager) serveNet(ctx context.Context, netType network.Type) { - if tm.isClosing() { - return - } - - // this func may be called by either initiating routing or a callback, - // so we should check whether this type of network is already being served - tm.servingNetsMu.Lock() - if _, ok := tm.servingNets[netType]; ok { - tm.servingNetsMu.Unlock() - return - } - tm.servingNets[netType] = struct{}{} - tm.servingNetsMu.Unlock() +func (tm *Manager) serve(ctx context.Context) { + tm.initClients() + tm.runClients(ctx) + tm.initTransports(ctx) + tm.Logger.Info("transport manager is serving.") - client, ok := tm.netClients[netType] - if !ok { - return - } + // closing logic + <-tm.done + tm.Logger.Info("transport manager is closing.") + tm.Logger.Info("transport manager closed.") +} - lis, err := client.Listen(skyenv.DmsgTransportPort) - if err != nil { - tm.Logger.WithError(err).Fatalf("failed to listen on network '%s' of port '%d'", - netType, skyenv.DmsgTransportPort) - return +func (tm *Manager) initClients() { + acceptedNetworks := []network.Type{network.STCP} + for _, netType := range acceptedNetworks { + tm.netClients[netType] = tm.factory.MakeClient(netType) } - tm.Logger.Infof("listening on network: %s", netType) - tm.listenersMu.Lock() - tm.listeners = append(tm.listeners, lis) - tm.listenersMu.Unlock() +} +func (tm *Manager) runClients(ctx context.Context) { if tm.isClosing() { return } + for _, client := range tm.netClients { + tm.Logger.Infof("Serving %s network", client.Type()) + err := client.Serve() + if err != nil { + tm.Logger.WithError(err).Errorf("Failed to listen on %s network", client.Type()) + continue + } + lis, err := client.Listen(skyenv.DmsgTransportPort) + if err != nil { + tm.Logger.WithError(err).Fatalf("failed to listen on network '%s' of port '%d'", + client.Type(), skyenv.DmsgTransportPort) + return + } + tm.Logger.Infof("listening on network: %s", client.Type()) + tm.wgMu.Lock() + tm.wg.Add(1) + tm.wgMu.Unlock() + go tm.acceptTransports(ctx, lis) + } +} - tm.wgMu.Lock() - tm.wg.Add(1) - tm.wgMu.Unlock() - - go func() { - defer tm.wg.Done() - for { - select { - case <-ctx.Done(): - return - case <-tm.done: - return - default: - if err := tm.acceptTransport(ctx, lis); err != nil { - tm.Logger.Warnf("Failed to accept connection: %v", err) - if strings.Contains(err.Error(), "closed") { - return - } +func (tm *Manager) acceptTransports(ctx context.Context, lis *network.Listener) { + defer tm.wg.Done() + for { + select { + case <-ctx.Done(): + return + case <-tm.done: + return + default: + if err := tm.acceptTransport(ctx, lis); err != nil { + tm.Logger.Warnf("Failed to accept connection: %v", err) + if strings.Contains(err.Error(), "closed") { + return } } } - }() + } } -func (tm *Manager) serve(ctx context.Context) { - +// Networks returns all the network types contained within the TransportManager. +func (tm *Manager) Networks() []string { + var nets []string for netType := range tm.netClients { - tm.serveNet(ctx, netType) - } - - tm.initTransports(ctx) - tm.Logger.Info("transport manager is serving.") - - // closing logic - <-tm.done - - tm.Logger.Info("transport manager is closing.") - defer tm.Logger.Info("transport manager closed.") - - // Close all listeners. - tm.listenersMu.Lock() - for i, lis := range tm.listeners { - if err := lis.Close(); err != nil { - tm.Logger.Warnf("listener %d of network '%s' closed with error: %v", i, lis.Network(), err) - } + nets = append(nets, string(netType)) } - tm.listenersMu.Unlock() + return nets } func (tm *Manager) initTransports(ctx context.Context) { diff --git a/pkg/transport/network/stcp.go b/pkg/transport/network/stcp.go index 8626e35e0..16c7753b7 100644 --- a/pkg/transport/network/stcp.go +++ b/pkg/transport/network/stcp.go @@ -139,8 +139,10 @@ func (c *stcpClient) serve() { c.log.Errorf("Failed to listen on %q: %v", c.listenAddr, err) return } + c.mu.Lock() c.connListener = l close(c.listenStarted) + c.mu.Unlock() c.log.Infof("listening on addr: %v", c.connListener.Addr()) for { if err := c.acceptConn(); err != nil { From cabf5edc3bdf9e61af129f4e352ddaeacb53272b Mon Sep 17 00:00:00 2001 From: Never M Date: Thu, 24 Jun 2021 13:01:36 +0300 Subject: [PATCH 15/55] Simplify transport manager close logic --- pkg/transport/manager.go | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/pkg/transport/manager.go b/pkg/transport/manager.go index 7cdf56991..29f89c642 100644 --- a/pkg/transport/manager.go +++ b/pkg/transport/manager.go @@ -98,11 +98,6 @@ func (tm *Manager) serve(ctx context.Context) { tm.runClients(ctx) tm.initTransports(ctx) tm.Logger.Info("transport manager is serving.") - - // closing logic - <-tm.done - tm.Logger.Info("transport manager is closing.") - tm.Logger.Info("transport manager closed.") } func (tm *Manager) initClients() { @@ -473,16 +468,13 @@ func (tm *Manager) Local() cipher.PubKey { // Close closes opened transports and registered factories. func (tm *Manager) Close() error { - tm.closeOnce.Do(func() { - tm.close() - }) + tm.closeOnce.Do(tm.close) return nil } func (tm *Manager) close() { - if tm == nil { - return - } + tm.Logger.Info("transport manager is closing.") + defer tm.Logger.Info("transport manager closed.") tm.mx.Lock() defer tm.mx.Unlock() From 0a301c5365938758b338e65e60a3eb5b6ad0cfe3 Mon Sep 17 00:00:00 2001 From: Never M Date: Fri, 25 Jun 2021 10:49:25 +0300 Subject: [PATCH 16/55] Refactor error handling in transport manager --- pkg/transport/manager.go | 12 ++---------- pkg/transport/network/stcp.go | 7 +++++-- 2 files changed, 7 insertions(+), 12 deletions(-) diff --git a/pkg/transport/manager.go b/pkg/transport/manager.go index 29f89c642..e81de74f2 100644 --- a/pkg/transport/manager.go +++ b/pkg/transport/manager.go @@ -5,7 +5,6 @@ import ( "errors" "fmt" "io" - "strings" "sync" "time" @@ -137,13 +136,12 @@ func (tm *Manager) acceptTransports(ctx context.Context, lis *network.Listener) for { select { case <-ctx.Done(): - return case <-tm.done: return default: if err := tm.acceptTransport(ctx, lis); err != nil { tm.Logger.Warnf("Failed to accept connection: %v", err) - if strings.Contains(err.Error(), "closed") { + if errors.Is(err, io.ErrClosedPipe) { return } } @@ -305,7 +303,7 @@ func (tm *Manager) SaveTransport(ctx context.Context, remote cipher.PubKey, tpTy // This occurs when the tp type is STCP and the requested remote PK is not associated with an IP address in // the STCP table. There is no point in retrying as a connection would be impossible, so we just return an // error. - if isSTCPTableError(remote, err) { + if errors.Is(err, network.ErrStcpEntryNotFound) { if closeErr := mTp.Close(); closeErr != nil { tm.Logger.WithError(err).Warn("Closing mTp returns non-nil error.") } @@ -320,12 +318,6 @@ func (tm *Manager) SaveTransport(ctx context.Context, remote cipher.PubKey, tpTy } } -// isSTCPPKError returns true if the error is a STCP table error. -// This occurs the requested remote public key does not exist in the STCP table. -func isSTCPTableError(remotePK cipher.PubKey, err error) bool { - return err.Error() == fmt.Sprintf("pk table: entry of %s does not exist", remotePK.String()) -} - func (tm *Manager) saveTransport(remote cipher.PubKey, netName string, label Label) (*ManagedTransport, error) { tm.mx.Lock() defer tm.mx.Unlock() diff --git a/pkg/transport/network/stcp.go b/pkg/transport/network/stcp.go index 16c7753b7..79161bb31 100644 --- a/pkg/transport/network/stcp.go +++ b/pkg/transport/network/stcp.go @@ -3,7 +3,6 @@ package network import ( "context" "errors" - "fmt" "io" "net" "sync" @@ -47,6 +46,10 @@ func newStcp(PK cipher.PubKey, SK cipher.SecKey, addr string, eb *appevent.Broad return client } +// ErrStcpEntryNotFound is returned when requested PK is not found in the local +// PK table +var ErrStcpEntryNotFound = errors.New("entry not found in PK table") + func (c *stcpClient) Dial(ctx context.Context, rPK cipher.PubKey, rPort uint16) (*Conn, error) { if c.isClosed() { return nil, io.ErrClosedPipe @@ -57,7 +60,7 @@ func (c *stcpClient) Dial(ctx context.Context, rPK cipher.PubKey, rPort uint16) var conn net.Conn addr, ok := c.table.Addr(rPK) if !ok { - return nil, fmt.Errorf("pk table: entry of %s does not exist", rPK) + return nil, ErrStcpEntryNotFound } conn, err := net.Dial("tcp", addr) From 434cdfc8c74cfd6066fd6f70742301b4efc6e9f7 Mon Sep 17 00:00:00 2001 From: Never M Date: Fri, 25 Jun 2021 11:26:03 +0300 Subject: [PATCH 17/55] Add generic network client --- pkg/transport/network/client.go | 284 +++++++++++++++++++++++++++++++ pkg/transport/network/network.go | 40 ----- pkg/transport/network/stcp.go | 218 +----------------------- 3 files changed, 291 insertions(+), 251 deletions(-) create mode 100644 pkg/transport/network/client.go diff --git a/pkg/transport/network/client.go b/pkg/transport/network/client.go new file mode 100644 index 000000000..243eee33f --- /dev/null +++ b/pkg/transport/network/client.go @@ -0,0 +1,284 @@ +package network + +import ( + "context" + "errors" + "io" + "net" + "sync" + "time" + + "github.com/skycoin/dmsg" + "github.com/skycoin/dmsg/cipher" + "github.com/skycoin/skycoin/src/util/logging" + "github.com/skycoin/skywire/pkg/app/appevent" + "github.com/skycoin/skywire/pkg/snet/arclient" + "github.com/skycoin/skywire/pkg/snet/directtp/porter" + "github.com/skycoin/skywire/pkg/snet/directtp/tphandshake" + "github.com/skycoin/skywire/pkg/transport/network/stcp" +) + +// Client provides access to skywire network in terms of dialing remote visors +// and listening to incoming connections +type Client interface { + // todo: change return type to wrapped conn + Dial(ctx context.Context, remote cipher.PubKey, port uint16) (*Conn, error) + Listen(port uint16) (*Listener, error) + LocalAddr() (net.Addr, error) + PK() cipher.PubKey + SK() cipher.SecKey + Serve() error + Close() error + Type() Type +} + +// ClientFactory is used to create Client instances +// and holds dependencies for different clients +type ClientFactory struct { + PK cipher.PubKey + SK cipher.SecKey + ListenAddr string + PKTable stcp.PKTable + ARClient arclient.APIClient + eb *appevent.Broadcaster +} + +// MakeClient creates a new client of specified type +func (f *ClientFactory) MakeClient(netType Type) Client { + log := logging.MustGetLogger(string(netType)) + p := porter.New(porter.MinEphemeral) + + generic := &genericClient{} + generic.listenStarted = make(chan struct{}) + generic.done = make(chan struct{}) + generic.listeners = make(map[uint16]*Listener) + generic.log = log + generic.porter = p + generic.eb = f.eb + generic.lPK = f.PK + generic.lSK = f.SK + generic.listenAddr = f.ListenAddr + + if netType == STCP { + return newStcp(generic, f.PKTable) + } + return nil +} + +// genericClient unites common logic for all clients +// The main responsibility is handshaking over incoming +// and outgoing raw network connections, obtaining remote information +// from the handshake and wrapping raw connections with skywire +// connection type. +// Incoming connections also directed to appropriate listener using +// skywire port, obtained from incoming connection handshake +type genericClient struct { + lPK cipher.PubKey + lSK cipher.SecKey + listenAddr string + + log *logging.Logger + porter *porter.Porter + eb *appevent.Broadcaster + + connListener net.Listener + listeners map[uint16]*Listener + listenStarted chan struct{} + mu sync.RWMutex + done chan struct{} + closeOnce sync.Once +} + +// initConnection will initialize skywire connection over opened raw connection to +// the remote client +// The process will perform handshake over raw connection +// todo: rename to handshake, initHandshake, skyConnect or smth? +func (c *genericClient) initConnection(ctx context.Context, conn net.Conn, lPK, rPK cipher.PubKey, rPort uint16) (*Conn, error) { + + lPort, freePort, err := c.porter.ReserveEphemeral(ctx) + if err != nil { + return nil, err + } + lAddr, rAddr := dmsg.Addr{PK: c.lPK, Port: lPort}, dmsg.Addr{PK: rPK, Port: rPort} + hs := tphandshake.InitiatorHandshake(c.lSK, lAddr, rAddr) + + connConfig := ConnConfig{ + Log: c.log, + Conn: conn, + LocalPK: c.lPK, + LocalSK: c.lSK, + Deadline: time.Now().Add(tphandshake.Timeout), + Handshake: hs, + FreePort: freePort, + Encrypt: true, + Initiator: true, + } + return NewConn(connConfig, STCP) +} + +// todo: context? +// acceptConnections continuously accepts incoming connections that come from given listener +// these connections will be properly handshaked and passed to an appropriate skywire listener +// using skywire port +func (c *genericClient) acceptConnections(lis net.Listener) { + c.mu.Lock() + c.connListener = lis + close(c.listenStarted) + c.mu.Unlock() + c.log.Infof("listening on addr: %v", c.connListener.Addr()) + for { + if err := c.acceptConn(); err != nil { + if errors.Is(err, io.EOF) { + continue // likely it's a dummy connection from service discovery + } + + c.log.Warnf("failed to accept incoming connection: %v", err) + + if !tphandshake.IsHandshakeError(err) { + c.log.Warnf("stopped serving") + return + } + } + } +} + +// todo: better comments +// acceptConn +// 1. accepts new raw network connection +// 2. performs accepting handshake over that connection +// 3. obtains skywire port from handshake +// 4. obtains skywire listener registered for that port +// 5. delivers connection to the listener +func (c *genericClient) acceptConn() error { + if c.isClosed() { + return io.ErrClosedPipe + } + + conn, err := c.connListener.Accept() + if err != nil { + return err + } + + remoteAddr := conn.RemoteAddr() + + c.log.Infof("Accepted connection from %v", remoteAddr) + + // todo: move handshake process out of connection wrapping. + var lis *Listener + + hs := tphandshake.ResponderHandshake(func(f2 tphandshake.Frame2) error { + lis, err = c.getListener(f2.DstAddr.Port) + if err != nil { + c.log.Errorf("cannot get listener for port %d", f2.DstAddr.Port) + } + return err + }) + + connConfig := ConnConfig{ + Log: c.log, + Conn: conn, + LocalPK: c.lPK, + LocalSK: c.lSK, + Deadline: time.Now().Add(tphandshake.Timeout), + Handshake: hs, + FreePort: nil, + Encrypt: true, + Initiator: false, + } + + wrappedConn, err := NewConn(connConfig, STCP) + if err != nil { + return err + } + + if err := lis.Introduce(wrappedConn); err != nil { + return err + } + + return nil +} + +// LocalAddr returns local address. This is network address the client +// listens to for incoming connections, not skywire address +func (c *genericClient) LocalAddr() (net.Addr, error) { + <-c.listenStarted + if c.isClosed() { + return nil, ErrNotListening + } + return c.connListener.Addr(), nil +} + +// getListener returns listener to specified skywire port +func (c *genericClient) getListener(port uint16) (*Listener, error) { + c.mu.Lock() + defer c.mu.Unlock() + lis, ok := c.listeners[port] + if !ok { + return nil, errors.New("not listening on given port") + } + return lis, nil +} + +// Listen starts listening on a specified port number. The port is a skywire port +// and is not related to local OS ports. Underlying connection will most likely use +// a different port number +// Listen requires Serve to be called, which will accept connections to all skywire ports +func (c *genericClient) Listen(port uint16) (*Listener, error) { + if c.isClosed() { + return nil, io.ErrClosedPipe + } + + ok, freePort := c.porter.Reserve(port) + if !ok { + return nil, ErrPortOccupied + } + + c.mu.Lock() + defer c.mu.Unlock() + + lAddr := dmsg.Addr{PK: c.lPK, Port: port} + lis := NewListener(lAddr, freePort, STCP) + c.listeners[port] = lis + + return lis, nil +} + +func (c *genericClient) isClosed() bool { + select { + case <-c.done: + return true + default: + return false + } +} + +func (c *stcpClient) PK() cipher.PubKey { + return c.lPK +} + +func (c *stcpClient) SK() cipher.SecKey { + return c.lSK +} + +func (c *stcpClient) Close() error { + c.closeOnce.Do(func() { + close(c.done) + + c.mu.Lock() + defer c.mu.Unlock() + + if c.connListener != nil { + if err := c.connListener.Close(); err != nil { + c.log.WithError(err).Warnf("Failed to close incoming connection listener") + } + } + + for _, lis := range c.listeners { + if err := lis.Close(); err != nil { + c.log.WithError(err).WithField("addr", lis.Addr().String()).Warnf("Failed to close listener") + } + } + }) + + return nil +} diff --git a/pkg/transport/network/network.go b/pkg/transport/network/network.go index 31fd8d90b..e5d2aae4c 100644 --- a/pkg/transport/network/network.go +++ b/pkg/transport/network/network.go @@ -7,11 +7,6 @@ import ( "github.com/skycoin/dmsg" "github.com/skycoin/dmsg/cipher" - "github.com/skycoin/skycoin/src/util/logging" - "github.com/skycoin/skywire/pkg/app/appevent" - "github.com/skycoin/skywire/pkg/snet/arclient" - "github.com/skycoin/skywire/pkg/snet/directtp/porter" - "github.com/skycoin/skywire/pkg/transport/network/stcp" ) // Type is a type of network. Type affects the way connection is established @@ -60,20 +55,6 @@ type Dialer interface { Type() Type } -// Client provides access to skywire network in terms of dialing remote visors -// and listening to incoming connections -type Client interface { - // todo: change return type to wrapped conn - Dial(ctx context.Context, remote cipher.PubKey, port uint16) (*Conn, error) - Listen(port uint16) (*Listener, error) - LocalAddr() (net.Addr, error) - PK() cipher.PubKey - SK() cipher.SecKey - Serve() error - Close() error - Type() Type -} - var ( // ErrUnknownTransportType is returned when transport type is unknown. ErrUnknownTransportType = errors.New("unknown transport type") @@ -90,24 +71,3 @@ var ( // ErrPortOccupied is returned when port is occupied. ErrPortOccupied = errors.New("port is already occupied") ) - -// ClientFactory is used to create Client instances -// and holds dependencies for different clients -type ClientFactory struct { - PK cipher.PubKey - SK cipher.SecKey - ListenAddr string - PKTable stcp.PKTable - ARClient arclient.APIClient - eb *appevent.Broadcaster -} - -// MakeClient creates a new client of specified type -func (f *ClientFactory) MakeClient(netType Type) Client { - log := logging.MustGetLogger(string(netType)) - p := porter.New(porter.MinEphemeral) - if netType == STCP { - return newStcp(f.PK, f.SK, f.ListenAddr, f.eb, f.PKTable, p, log) - } - return nil -} diff --git a/pkg/transport/network/stcp.go b/pkg/transport/network/stcp.go index 79161bb31..7e801faa4 100644 --- a/pkg/transport/network/stcp.go +++ b/pkg/transport/network/stcp.go @@ -5,44 +5,18 @@ import ( "errors" "io" "net" - "sync" - "time" - "github.com/skycoin/dmsg" "github.com/skycoin/dmsg/cipher" - "github.com/skycoin/skycoin/src/util/logging" - "github.com/skycoin/skywire/pkg/app/appevent" - "github.com/skycoin/skywire/pkg/snet/directtp/porter" - "github.com/skycoin/skywire/pkg/snet/directtp/tphandshake" "github.com/skycoin/skywire/pkg/transport/network/stcp" ) type stcpClient struct { - lPK cipher.PubKey - lSK cipher.SecKey - listenAddr string - - table stcp.PKTable - log *logging.Logger - porter *porter.Porter - eb *appevent.Broadcaster - - connListener net.Listener - listeners map[uint16]*Listener - listenStarted chan struct{} - mu sync.RWMutex - done chan struct{} - closeOnce sync.Once + *genericClient + table stcp.PKTable } -func newStcp(PK cipher.PubKey, SK cipher.SecKey, addr string, eb *appevent.Broadcaster, table stcp.PKTable, porter *porter.Porter, log *logging.Logger) Client { - client := &stcpClient{lPK: PK, lSK: SK, listenAddr: addr, table: table} - client.listenStarted = make(chan struct{}) - client.done = make(chan struct{}) - client.listeners = make(map[uint16]*Listener) - client.log = log - client.porter = porter - client.eb = eb +func newStcp(generic *genericClient, table stcp.PKTable) Client { + client := &stcpClient{genericClient: generic, table: table} return client } @@ -69,60 +43,7 @@ func (c *stcpClient) Dial(ctx context.Context, rPK cipher.PubKey, rPort uint16) } c.log.Infof("Dialed %v:%v@%v", rPK, rPort, conn.RemoteAddr()) - - lPort, freePort, err := c.porter.ReserveEphemeral(ctx) - if err != nil { - return nil, err - } - lAddr, rAddr := dmsg.Addr{PK: c.lPK, Port: lPort}, dmsg.Addr{PK: rPK, Port: rPort} - hs := tphandshake.InitiatorHandshake(c.lSK, lAddr, rAddr) - - connConfig := ConnConfig{ - Log: c.log, - Conn: conn, - LocalPK: c.lPK, - LocalSK: c.lSK, - Deadline: time.Now().Add(tphandshake.Timeout), - Handshake: hs, - FreePort: freePort, - Encrypt: true, - Initiator: true, - } - return NewConn(connConfig, STCP) -} - -// Listen starts listening on a specified port number. The port is a skywire port -// and is not related to local OS ports. Underlying connection will most likely use -// a different port number -// Listen requires Serve to be called, which will accept connections to all skywire ports -func (c *stcpClient) Listen(port uint16) (*Listener, error) { - if c.isClosed() { - return nil, io.ErrClosedPipe - } - - ok, freePort := c.porter.Reserve(port) - if !ok { - return nil, ErrPortOccupied - } - - c.mu.Lock() - defer c.mu.Unlock() - - lAddr := dmsg.Addr{PK: c.lPK, Port: port} - lis := NewListener(lAddr, freePort, STCP) - c.listeners[port] = lis - - return lis, nil -} - -// LocalAddr returns local address. This is network address the client -// listens to for incoming connections, not skywire address -func (c *stcpClient) LocalAddr() (net.Addr, error) { - <-c.listenStarted - if c.isClosed() { - return nil, ErrNotListening - } - return c.connListener.Addr(), nil + return c.initConnection(ctx, conn, c.lPK, rPK, rPort) } // Serve starts accepting all incoming connections (i.e. connections to all skywire ports) @@ -137,139 +58,14 @@ func (c *stcpClient) Serve() error { } func (c *stcpClient) serve() { - l, err := net.Listen("tcp", c.listenAddr) + lis, err := net.Listen("tcp", c.listenAddr) if err != nil { c.log.Errorf("Failed to listen on %q: %v", c.listenAddr, err) return } - c.mu.Lock() - c.connListener = l - close(c.listenStarted) - c.mu.Unlock() - c.log.Infof("listening on addr: %v", c.connListener.Addr()) - for { - if err := c.acceptConn(); err != nil { - if errors.Is(err, io.EOF) { - continue // likely it's a dummy connection from service discovery - } - - c.log.Warnf("failed to accept incoming connection: %v", err) - - if !tphandshake.IsHandshakeError(err) { - c.log.Warnf("stopped serving") - return - } - } - } -} - -// todo: move this to generic client -func (c *stcpClient) acceptConn() error { - if c.isClosed() { - return io.ErrClosedPipe - } - - conn, err := c.connListener.Accept() - if err != nil { - return err - } - - remoteAddr := conn.RemoteAddr() - - c.log.Infof("Accepted connection from %v", remoteAddr) - - // todo: move handshake process out of connection wrapping. - // 1. perform handshake explicitly over conn - // 2. wrap connection in our own connection type (for now tpconn.Conn then refactored wrapper) - // 3. introduce wrapped connection to the listener - - var lis *Listener - - hs := tphandshake.ResponderHandshake(func(f2 tphandshake.Frame2) error { - lis, err = c.getListener(f2.DstAddr.Port) - if err != nil { - c.log.Errorf("cannot get listener for port %d", f2.DstAddr.Port) - } - return err - }) - - connConfig := ConnConfig{ - Log: c.log, - Conn: conn, - LocalPK: c.lPK, - LocalSK: c.lSK, - Deadline: time.Now().Add(tphandshake.Timeout), - Handshake: hs, - FreePort: nil, - Encrypt: true, - Initiator: false, - } - - wrappedConn, err := NewConn(connConfig, STCP) - if err != nil { - return err - } - - if err := lis.Introduce(wrappedConn); err != nil { - return err - } - - return nil -} - -// getListener returns listener to specified skywire port -// todo: proper listener type -// todo: move to generic client -func (c *stcpClient) getListener(port uint16) (*Listener, error) { - c.mu.Lock() - defer c.mu.Unlock() - lis, ok := c.listeners[port] - if !ok { - return nil, errors.New("not listening on given port") - } - return lis, nil -} - -func (c *stcpClient) Close() error { - c.closeOnce.Do(func() { - close(c.done) - - c.mu.Lock() - defer c.mu.Unlock() - - if c.connListener != nil { - if err := c.connListener.Close(); err != nil { - c.log.WithError(err).Warnf("Failed to close incoming connection listener") - } - } - - for _, lis := range c.listeners { - if err := lis.Close(); err != nil { - c.log.WithError(err).WithField("addr", lis.Addr().String()).Warnf("Failed to close listener") - } - } - }) - - return nil -} - -func (c *stcpClient) isClosed() bool { - select { - case <-c.done: - return true - default: - return false - } + c.acceptConnections(lis) } func (c *stcpClient) Type() Type { return STCP } - -func (c *stcpClient) PK() cipher.PubKey { - return c.lPK -} - -func (c *stcpClient) SK() cipher.SecKey { - return c.lSK -} From c282b14bf8026e5c325be9acdd4f8972eb9a9524 Mon Sep 17 00:00:00 2001 From: Never M Date: Fri, 25 Jun 2021 12:19:44 +0300 Subject: [PATCH 18/55] Put handshake code to network client --- pkg/snet/directtp/tphandshake/handshake.go | 16 ++++- pkg/transport/network/client.go | 82 +++++++++++++--------- pkg/transport/network/connection.go | 52 +++----------- pkg/transport/network/stcp.go | 5 +- 4 files changed, 75 insertions(+), 80 deletions(-) diff --git a/pkg/snet/directtp/tphandshake/handshake.go b/pkg/snet/directtp/tphandshake/handshake.go index e037bbf29..f6b5e88cd 100644 --- a/pkg/snet/directtp/tphandshake/handshake.go +++ b/pkg/snet/directtp/tphandshake/handshake.go @@ -98,8 +98,22 @@ func InitiatorHandshake(lSK cipher.SecKey, localAddr, remoteAddr dmsg.Addr) Hand }) } +// CheckF2 checks second frame of handshake +type CheckF2 = func(f2 Frame2) error + +// MakeF2PortChecker returns new CheckF2 function that will use +// port checker to check port in Frame2 +func MakeF2PortChecker(portChecker func(port uint16) error) CheckF2 { + return func(f2 Frame2) error { + if err := portChecker(f2.DstAddr.Port); err != nil { + return err + } + return nil + } +} + // ResponderHandshake creates the handshake logic on the responder's side. -func ResponderHandshake(checkF2 func(f2 Frame2) error) Handshake { +func ResponderHandshake(checkF2 CheckF2) Handshake { return handshakeMiddleware(func(conn net.Conn, deadline time.Time) (lAddr, rAddr dmsg.Addr, err error) { if err = readFrame0(conn); err != nil { return dmsg.Addr{}, dmsg.Addr{}, err diff --git a/pkg/transport/network/client.go b/pkg/transport/network/client.go index 243eee33f..9b8d65f6c 100644 --- a/pkg/transport/network/client.go +++ b/pkg/transport/network/client.go @@ -76,6 +76,7 @@ type genericClient struct { lPK cipher.PubKey lSK cipher.SecKey listenAddr string + netType Type log *logging.Logger porter *porter.Porter @@ -100,20 +101,30 @@ func (c *genericClient) initConnection(ctx context.Context, conn net.Conn, lPK, return nil, err } lAddr, rAddr := dmsg.Addr{PK: c.lPK, Port: lPort}, dmsg.Addr{PK: rPK, Port: rPort} + + remoteAddr := conn.RemoteAddr() + // c.log.Infof("Accepted connection from %v", remoteAddr) + c.log.Infof("Performing handshake with %v", remoteAddr) + hs := tphandshake.InitiatorHandshake(c.lSK, lAddr, rAddr) + _, _, err = hs(conn, time.Now().Add(tphandshake.Timeout)) + if err != nil { + if err := conn.Close(); err != nil { + c.log.WithError(err).Warnf("Failed to close connection") + } + if freePort != nil { + freePort() + } + return nil, err + } + c.log.Infof("Sent handshake to %v, local addr %v, remote addr %v", remoteAddr, lAddr, rAddr) - connConfig := ConnConfig{ - Log: c.log, - Conn: conn, - LocalPK: c.lPK, - LocalSK: c.lSK, - Deadline: time.Now().Add(tphandshake.Timeout), - Handshake: hs, - FreePort: freePort, - Encrypt: true, - Initiator: true, + wrappedConn := &Conn{Conn: conn, lAddr: lAddr, rAddr: rAddr, freePort: freePort, connType: c.netType} + err = wrappedConn.encrypt(c.lPK, c.lSK, true) + if err != nil { + return nil, err } - return NewConn(connConfig, STCP) + return wrappedConn, nil } // todo: context? @@ -160,37 +171,33 @@ func (c *genericClient) acceptConn() error { } remoteAddr := conn.RemoteAddr() - c.log.Infof("Accepted connection from %v", remoteAddr) + c.log.Infof("Performing handshake with %v", conn.RemoteAddr()) - // todo: move handshake process out of connection wrapping. - var lis *Listener - - hs := tphandshake.ResponderHandshake(func(f2 tphandshake.Frame2) error { - lis, err = c.getListener(f2.DstAddr.Port) - if err != nil { - c.log.Errorf("cannot get listener for port %d", f2.DstAddr.Port) + var freePort func() + hs := tphandshake.ResponderHandshake(tphandshake.MakeF2PortChecker(c.checkListener)) + lAddr, rAddr, err := hs(conn, time.Now().Add(tphandshake.Timeout)) + if err != nil { + if err := conn.Close(); err != nil { + c.log.WithError(err).Warnf("Failed to close connection") + } + if freePort != nil { + freePort() } return err - }) - - connConfig := ConnConfig{ - Log: c.log, - Conn: conn, - LocalPK: c.lPK, - LocalSK: c.lSK, - Deadline: time.Now().Add(tphandshake.Timeout), - Handshake: hs, - FreePort: nil, - Encrypt: true, - Initiator: false, } + c.log.Infof("Sent handshake to %v, local addr %v, remote addr %v", conn.RemoteAddr(), lAddr, rAddr) - wrappedConn, err := NewConn(connConfig, STCP) + wrappedConn := &Conn{Conn: conn, lAddr: lAddr, rAddr: rAddr, freePort: freePort, connType: c.netType} + err = wrappedConn.encrypt(c.lPK, c.lSK, false) if err != nil { return err } + lis, err := c._getListener(lAddr.Port) + if err != nil { + return err + } if err := lis.Introduce(wrappedConn); err != nil { return err } @@ -209,7 +216,7 @@ func (c *genericClient) LocalAddr() (net.Addr, error) { } // getListener returns listener to specified skywire port -func (c *genericClient) getListener(port uint16) (*Listener, error) { +func (c *genericClient) _getListener(port uint16) (*Listener, error) { c.mu.Lock() defer c.mu.Unlock() lis, ok := c.listeners[port] @@ -219,6 +226,11 @@ func (c *genericClient) getListener(port uint16) (*Listener, error) { return lis, nil } +func (c *genericClient) checkListener(port uint16) error { + _, err := c._getListener(port) + return err +} + // Listen starts listening on a specified port number. The port is a skywire port // and is not related to local OS ports. Underlying connection will most likely use // a different port number @@ -282,3 +294,7 @@ func (c *stcpClient) Close() error { return nil } + +func (c *stcpClient) Type() Type { + return c.netType +} diff --git a/pkg/transport/network/connection.go b/pkg/transport/network/connection.go index f871ff198..8e303cc24 100644 --- a/pkg/transport/network/connection.go +++ b/pkg/transport/network/connection.go @@ -34,53 +34,21 @@ type ConnConfig struct { Initiator bool } -// NewConn creates a new Conn. -// todo: move out handshake -func NewConn(c ConnConfig, connType Type) (*Conn, error) { - if c.Log != nil { - c.Log.Infof("Performing handshake with %v", c.Conn.RemoteAddr()) +func (c *Conn) encrypt(lPK cipher.PubKey, lSK cipher.SecKey, initator bool) error { + config := noise.Config{ + LocalPK: lPK, + LocalSK: lSK, + RemotePK: c.rAddr.PK, + Initiator: initator, } - lAddr, rAddr, err := c.Handshake(c.Conn, c.Deadline) + wrappedConn, err := noisewrapper.WrapConn(config, c.Conn) if err != nil { - if err := c.Conn.Close(); err != nil && c.Log != nil { - c.Log.WithError(err).Warnf("Failed to close connection") - } - - if c.FreePort != nil { - c.FreePort() - } - - return nil, err - } - - if c.Log != nil { - c.Log.Infof("Sent handshake to %v, local addr %v, remote addr %v", c.Conn.RemoteAddr(), lAddr, rAddr) - } - - if c.Encrypt { - config := noise.Config{ - LocalPK: c.LocalPK, - LocalSK: c.LocalSK, - RemotePK: rAddr.PK, - Initiator: c.Initiator, - } - - wrappedConn, err := noisewrapper.WrapConn(config, c.Conn) - if err != nil { - return nil, fmt.Errorf("encrypt connection to %v@%v: %w", rAddr, c.Conn.RemoteAddr(), err) - } - - c.Conn = wrappedConn - - if c.Log != nil { - c.Log.Infof("Connection with %v@%v is encrypted", rAddr, c.Conn.RemoteAddr()) - } - } else if c.Log != nil { - c.Log.Infof("Connection with %v@%v is NOT encrypted", rAddr, c.Conn.RemoteAddr()) + return fmt.Errorf("encrypt connection to %v@%v: %w", c.rAddr, c.Conn.RemoteAddr(), err) } - return &Conn{Conn: c.Conn, lAddr: lAddr, rAddr: rAddr, freePort: c.FreePort, connType: connType}, nil + c.Conn = wrappedConn + return nil } // LocalAddr implements net.Conn diff --git a/pkg/transport/network/stcp.go b/pkg/transport/network/stcp.go index 7e801faa4..710314365 100644 --- a/pkg/transport/network/stcp.go +++ b/pkg/transport/network/stcp.go @@ -17,6 +17,7 @@ type stcpClient struct { func newStcp(generic *genericClient, table stcp.PKTable) Client { client := &stcpClient{genericClient: generic, table: table} + client.netType = STCP return client } @@ -65,7 +66,3 @@ func (c *stcpClient) serve() { } c.acceptConnections(lis) } - -func (c *stcpClient) Type() Type { - return STCP -} From 6ccadd729a9aa881644b62ac8a96af752dd11dd3 Mon Sep 17 00:00:00 2001 From: Never M Date: Fri, 25 Jun 2021 12:34:31 +0300 Subject: [PATCH 19/55] Refactor handshakes into wrapConn method --- pkg/transport/network/client.go | 72 ++++++++++++--------------------- 1 file changed, 25 insertions(+), 47 deletions(-) diff --git a/pkg/transport/network/client.go b/pkg/transport/network/client.go index 9b8d65f6c..d532ce643 100644 --- a/pkg/transport/network/client.go +++ b/pkg/transport/network/client.go @@ -95,36 +95,15 @@ type genericClient struct { // The process will perform handshake over raw connection // todo: rename to handshake, initHandshake, skyConnect or smth? func (c *genericClient) initConnection(ctx context.Context, conn net.Conn, lPK, rPK cipher.PubKey, rPort uint16) (*Conn, error) { - lPort, freePort, err := c.porter.ReserveEphemeral(ctx) if err != nil { return nil, err } lAddr, rAddr := dmsg.Addr{PK: c.lPK, Port: lPort}, dmsg.Addr{PK: rPK, Port: rPort} - remoteAddr := conn.RemoteAddr() - // c.log.Infof("Accepted connection from %v", remoteAddr) c.log.Infof("Performing handshake with %v", remoteAddr) - hs := tphandshake.InitiatorHandshake(c.lSK, lAddr, rAddr) - _, _, err = hs(conn, time.Now().Add(tphandshake.Timeout)) - if err != nil { - if err := conn.Close(); err != nil { - c.log.WithError(err).Warnf("Failed to close connection") - } - if freePort != nil { - freePort() - } - return nil, err - } - c.log.Infof("Sent handshake to %v, local addr %v, remote addr %v", remoteAddr, lAddr, rAddr) - - wrappedConn := &Conn{Conn: conn, lAddr: lAddr, rAddr: rAddr, freePort: freePort, connType: c.netType} - err = wrappedConn.encrypt(c.lPK, c.lSK, true) - if err != nil { - return nil, err - } - return wrappedConn, nil + return c.wrapConn(conn, hs, true, freePort) } // todo: context? @@ -142,9 +121,7 @@ func (c *genericClient) acceptConnections(lis net.Listener) { if errors.Is(err, io.EOF) { continue // likely it's a dummy connection from service discovery } - c.log.Warnf("failed to accept incoming connection: %v", err) - if !tphandshake.IsHandshakeError(err) { c.log.Warnf("stopped serving") return @@ -153,6 +130,25 @@ func (c *genericClient) acceptConnections(lis net.Listener) { } } +func (c *genericClient) wrapConn(conn net.Conn, hs tphandshake.Handshake, initiator bool, onClose func()) (*Conn, error) { + lAddr, rAddr, err := hs(conn, time.Now().Add(tphandshake.Timeout)) + if err != nil { + if err := conn.Close(); err != nil { + c.log.WithError(err).Warnf("Failed to close connection") + } + onClose() + return nil, err + } + c.log.Infof("Sent handshake to %v, local addr %v, remote addr %v", conn.RemoteAddr(), lAddr, rAddr) + + wrappedConn := &Conn{Conn: conn, lAddr: lAddr, rAddr: rAddr, freePort: onClose, connType: c.netType} + err = wrappedConn.encrypt(c.lPK, c.lSK, initiator) + if err != nil { + return nil, err + } + return wrappedConn, nil +} + // todo: better comments // acceptConn // 1. accepts new raw network connection @@ -164,44 +160,26 @@ func (c *genericClient) acceptConn() error { if c.isClosed() { return io.ErrClosedPipe } - conn, err := c.connListener.Accept() if err != nil { return err } - remoteAddr := conn.RemoteAddr() c.log.Infof("Accepted connection from %v", remoteAddr) - c.log.Infof("Performing handshake with %v", conn.RemoteAddr()) - var freePort func() + onClose := func() {} hs := tphandshake.ResponderHandshake(tphandshake.MakeF2PortChecker(c.checkListener)) - lAddr, rAddr, err := hs(conn, time.Now().Add(tphandshake.Timeout)) + wrappedConn, err := c.wrapConn(conn, hs, false, onClose) if err != nil { - if err := conn.Close(); err != nil { - c.log.WithError(err).Warnf("Failed to close connection") - } - if freePort != nil { - freePort() - } return err } - c.log.Infof("Sent handshake to %v, local addr %v, remote addr %v", conn.RemoteAddr(), lAddr, rAddr) - - wrappedConn := &Conn{Conn: conn, lAddr: lAddr, rAddr: rAddr, freePort: freePort, connType: c.netType} - err = wrappedConn.encrypt(c.lPK, c.lSK, false) - if err != nil { - return err - } - - lis, err := c._getListener(lAddr.Port) + lis, err := c.getListener(wrappedConn.lAddr.Port) if err != nil { return err } if err := lis.Introduce(wrappedConn); err != nil { return err } - return nil } @@ -216,7 +194,7 @@ func (c *genericClient) LocalAddr() (net.Addr, error) { } // getListener returns listener to specified skywire port -func (c *genericClient) _getListener(port uint16) (*Listener, error) { +func (c *genericClient) getListener(port uint16) (*Listener, error) { c.mu.Lock() defer c.mu.Unlock() lis, ok := c.listeners[port] @@ -227,7 +205,7 @@ func (c *genericClient) _getListener(port uint16) (*Listener, error) { } func (c *genericClient) checkListener(port uint16) error { - _, err := c._getListener(port) + _, err := c.getListener(port) return err } From 39587ccc13d1a2f79e47864f73a37235837b6514 Mon Sep 17 00:00:00 2001 From: Never M Date: Fri, 25 Jun 2021 17:13:06 +0300 Subject: [PATCH 20/55] Add stcpr client --- pkg/transport/network/client.go | 8 +-- pkg/transport/network/network.go | 6 +-- pkg/transport/network/stcpr.go | 84 ++++++++++++++++++++++++++++++++ 3 files changed, 91 insertions(+), 7 deletions(-) diff --git a/pkg/transport/network/client.go b/pkg/transport/network/client.go index d532ce643..a5537b933 100644 --- a/pkg/transport/network/client.go +++ b/pkg/transport/network/client.go @@ -242,15 +242,15 @@ func (c *genericClient) isClosed() bool { } } -func (c *stcpClient) PK() cipher.PubKey { +func (c *genericClient) PK() cipher.PubKey { return c.lPK } -func (c *stcpClient) SK() cipher.SecKey { +func (c *genericClient) SK() cipher.SecKey { return c.lSK } -func (c *stcpClient) Close() error { +func (c *genericClient) Close() error { c.closeOnce.Do(func() { close(c.done) @@ -273,6 +273,6 @@ func (c *stcpClient) Close() error { return nil } -func (c *stcpClient) Type() Type { +func (c *genericClient) Type() Type { return c.netType } diff --git a/pkg/transport/network/network.go b/pkg/transport/network/network.go index e5d2aae4c..6d8049321 100644 --- a/pkg/transport/network/network.go +++ b/pkg/transport/network/network.go @@ -18,11 +18,11 @@ const ( STCPR Type = "stcpr" // SUDPH is a type of a transport that works via UDP, resolves addresses using address-resolver service, // and uses UDP hole punching. - SUDPH = "sudph" + SUDPH Type = "sudph" // STCP is a type of a transport that works via TCP and resolves addresses using PK table. - STCP = "stcp" + STCP Type = "stcp" // DMSG is a type of a transport that works through an intermediary service - DMSG = "dmsg" + DMSG Type = "dmsg" ) // skywire address consisting of pulic key, port and diff --git a/pkg/transport/network/stcpr.go b/pkg/transport/network/stcpr.go index dfbf08203..2b2bcf663 100644 --- a/pkg/transport/network/stcpr.go +++ b/pkg/transport/network/stcpr.go @@ -1,4 +1,88 @@ package network +import ( + "context" + "fmt" + "io" + "net" + + "github.com/skycoin/dmsg/cipher" + "github.com/skycoin/skywire/pkg/app/appevent" + "github.com/skycoin/skywire/pkg/snet/arclient" +) + type stcprClient struct { + *genericClient + addressResolver arclient.APIClient +} + +func newStcpr(generic *genericClient, addressResolver arclient.APIClient) Client { + client := &stcprClient{genericClient: generic, addressResolver: addressResolver} + client.netType = STCPR + return client +} + +func (c *stcprClient) Dial(ctx context.Context, rPK cipher.PubKey, rPort uint16) (*Conn, error) { + if c.isClosed() { + return nil, io.ErrClosedPipe + } + + c.log.Infof("Dialing PK %v", rPK) + visorData, err := c.addressResolver.Resolve(ctx, string(STCPR), rPK) + if err != nil { + return nil, fmt.Errorf("resolve PK: %w", err) + } + + c.log.Infof("Resolved PK %v to visor data %v", rPK, visorData) + + conn, err := c.dialVisor(visorData) + if err != nil { + return nil, err + } + + return c.initConnection(ctx, conn, c.lPK, rPK, rPort) +} + +func (c *stcprClient) dialVisor(visorData arclient.VisorData) (net.Conn, error) { + if visorData.IsLocal { + for _, host := range visorData.Addresses { + addr := net.JoinHostPort(host, visorData.Port) + conn, err := c.dial(addr) + if err == nil { + return conn, nil + } + } + } + addr := visorData.RemoteAddr + if _, _, err := net.SplitHostPort(addr); err != nil { + addr = net.JoinHostPort(addr, visorData.Port) + } + return c.dial(addr) +} + +func (c *stcprClient) dial(addr string) (net.Conn, error) { + data := appevent.TCPDialData{RemoteNet: string(STCPR), RemoteAddr: addr} + event := appevent.NewEvent(appevent.TCPDial, data) + _ = c.eb.Broadcast(context.Background(), event) //nolint:errcheck + return net.Dial("tcp", addr) +} + +// Serve starts accepting all incoming connections (i.e. connections to all skywire ports) +// Connections that successfuly perform handshakes will be delivered to a listener +// bound to a specific skywire port +func (c *stcprClient) Serve() error { + if c.connListener != nil { + return ErrAlreadyListening + } + go c.serve() + return nil +} + +func (c *stcprClient) serve() { + lis, err := net.Listen("tcp", c.listenAddr) + if err != nil { + c.log.Errorf("Failed to listen on %q: %v", c.listenAddr, err) + return + } + c.acceptConnections(lis) } From f0cfaf03ab33e47a71f06d750ead359602d5f948 Mon Sep 17 00:00:00 2001 From: Never M Date: Fri, 25 Jun 2021 17:22:20 +0300 Subject: [PATCH 21/55] Refactor sending tcp events --- pkg/app/appevent/utils.go | 16 ++++++++++++++++ pkg/transport/network/stcp.go | 2 +- pkg/transport/network/stcpr.go | 5 +---- 3 files changed, 18 insertions(+), 5 deletions(-) create mode 100644 pkg/app/appevent/utils.go diff --git a/pkg/app/appevent/utils.go b/pkg/app/appevent/utils.go new file mode 100644 index 000000000..79b173b2f --- /dev/null +++ b/pkg/app/appevent/utils.go @@ -0,0 +1,16 @@ +package appevent + +import "context" + +func (eb *Broadcaster) SendTCPDial(ctx context.Context, remoteNet, remoteAddr string) { + data := TCPDialData{RemoteNet: remoteNet, RemoteAddr: remoteAddr} + event := NewEvent(TCPDial, data) + eb.sendEvent(ctx, event) +} + +func (eb *Broadcaster) sendEvent(ctx context.Context, event *Event) { + err := eb.Broadcast(context.Background(), event) //nolint:errcheck + if err != nil { + eb.log.Warn("Failed to broadcast event: %v", event) + } +} diff --git a/pkg/transport/network/stcp.go b/pkg/transport/network/stcp.go index 710314365..7e152d7be 100644 --- a/pkg/transport/network/stcp.go +++ b/pkg/transport/network/stcp.go @@ -37,7 +37,7 @@ func (c *stcpClient) Dial(ctx context.Context, rPK cipher.PubKey, rPort uint16) if !ok { return nil, ErrStcpEntryNotFound } - + c.eb.SendTCPDial(context.Background(), string(STCP), addr) conn, err := net.Dial("tcp", addr) if err != nil { return nil, err diff --git a/pkg/transport/network/stcpr.go b/pkg/transport/network/stcpr.go index 2b2bcf663..44dd256b6 100644 --- a/pkg/transport/network/stcpr.go +++ b/pkg/transport/network/stcpr.go @@ -7,7 +7,6 @@ import ( "net" "github.com/skycoin/dmsg/cipher" - "github.com/skycoin/skywire/pkg/app/appevent" "github.com/skycoin/skywire/pkg/snet/arclient" ) @@ -61,9 +60,7 @@ func (c *stcprClient) dialVisor(visorData arclient.VisorData) (net.Conn, error) } func (c *stcprClient) dial(addr string) (net.Conn, error) { - data := appevent.TCPDialData{RemoteNet: string(STCPR), RemoteAddr: addr} - event := appevent.NewEvent(appevent.TCPDial, data) - _ = c.eb.Broadcast(context.Background(), event) //nolint:errcheck + c.eb.SendTCPDial(context.Background(), string(STCPR), addr) return net.Dial("tcp", addr) } From 95561be36dcc16842c4a00ca4910dec5294375fc Mon Sep 17 00:00:00 2001 From: Never M Date: Mon, 28 Jun 2021 11:20:29 +0300 Subject: [PATCH 22/55] Fix event broadcaster --- pkg/transport/network/client.go | 4 ++-- pkg/visor/init.go | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/pkg/transport/network/client.go b/pkg/transport/network/client.go index a5537b933..3e5420d5f 100644 --- a/pkg/transport/network/client.go +++ b/pkg/transport/network/client.go @@ -40,7 +40,7 @@ type ClientFactory struct { ListenAddr string PKTable stcp.PKTable ARClient arclient.APIClient - eb *appevent.Broadcaster + EB *appevent.Broadcaster } // MakeClient creates a new client of specified type @@ -54,7 +54,7 @@ func (f *ClientFactory) MakeClient(netType Type) Client { generic.listeners = make(map[uint16]*Listener) generic.log = log generic.porter = p - generic.eb = f.eb + generic.eb = f.EB generic.lPK = f.PK generic.lSK = f.SK generic.listenAddr = f.ListenAddr diff --git a/pkg/visor/init.go b/pkg/visor/init.go index cc757d808..96578a696 100644 --- a/pkg/visor/init.go +++ b/pkg/visor/init.go @@ -279,6 +279,7 @@ func initTransport(ctx context.Context, v *Visor, log *logging.Logger) error { ListenAddr: v.conf.STCP.LocalAddr, PKTable: table, ARClient: v.arClient, + EB: v.ebc, } tpM, err := transport.NewManager(managerLogger, v.arClient, &tpMConf, factory) if err != nil { From 83ba317afa40d0cc661d8136e744de084c501364 Mon Sep 17 00:00:00 2001 From: Never M Date: Mon, 28 Jun 2021 11:41:16 +0300 Subject: [PATCH 23/55] Initialize stcpr client --- pkg/transport/network/client.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/pkg/transport/network/client.go b/pkg/transport/network/client.go index 3e5420d5f..d6549aad9 100644 --- a/pkg/transport/network/client.go +++ b/pkg/transport/network/client.go @@ -59,8 +59,11 @@ func (f *ClientFactory) MakeClient(netType Type) Client { generic.lSK = f.SK generic.listenAddr = f.ListenAddr - if netType == STCP { + switch netType { + case STCP: return newStcp(generic, f.PKTable) + case STCPR: + return newStcpr(generic, f.ARClient) } return nil } From b1bb4c1027ec8e3143d05bfd73ff5eadaa3e6dbe Mon Sep 17 00:00:00 2001 From: Never M Date: Mon, 28 Jun 2021 13:04:31 +0300 Subject: [PATCH 24/55] Fix stcpr bind and resolve --- pkg/transport/manager.go | 2 +- pkg/transport/network/client.go | 2 +- pkg/transport/network/stcpr.go | 26 ++++++++++++++++++++++++-- 3 files changed, 26 insertions(+), 4 deletions(-) diff --git a/pkg/transport/manager.go b/pkg/transport/manager.go index c8a5b6c7e..f3c15fb0d 100644 --- a/pkg/transport/manager.go +++ b/pkg/transport/manager.go @@ -100,7 +100,7 @@ func (tm *Manager) serve(ctx context.Context) { } func (tm *Manager) initClients() { - acceptedNetworks := []network.Type{network.STCP} + acceptedNetworks := []network.Type{network.STCP, network.STCPR} for _, netType := range acceptedNetworks { tm.netClients[netType] = tm.factory.MakeClient(netType) } diff --git a/pkg/transport/network/client.go b/pkg/transport/network/client.go index d6549aad9..15a60ed2e 100644 --- a/pkg/transport/network/client.go +++ b/pkg/transport/network/client.go @@ -230,7 +230,7 @@ func (c *genericClient) Listen(port uint16) (*Listener, error) { defer c.mu.Unlock() lAddr := dmsg.Addr{PK: c.lPK, Port: port} - lis := NewListener(lAddr, freePort, STCP) + lis := NewListener(lAddr, freePort, c.netType) c.listeners[port] = lis return lis, nil diff --git a/pkg/transport/network/stcpr.go b/pkg/transport/network/stcpr.go index 44dd256b6..b904d5ede 100644 --- a/pkg/transport/network/stcpr.go +++ b/pkg/transport/network/stcpr.go @@ -8,6 +8,7 @@ import ( "github.com/skycoin/dmsg/cipher" "github.com/skycoin/skywire/pkg/snet/arclient" + "github.com/skycoin/skywire/pkg/util/netutil" ) type stcprClient struct { @@ -76,10 +77,31 @@ func (c *stcprClient) Serve() error { } func (c *stcprClient) serve() { - lis, err := net.Listen("tcp", c.listenAddr) + lis, err := net.Listen("tcp", "") if err != nil { - c.log.Errorf("Failed to listen on %q: %v", c.listenAddr, err) + c.log.Errorf("Failed to listen on random port: %v", "", err) return } + + localAddr := lis.Addr().String() + _, port, err := net.SplitHostPort(localAddr) + if err != nil { + c.log.Errorf("Failed to extract port from addr %v: %v", err) + return + } + hasPublic, err := netutil.HasPublicIP() + if err != nil { + c.log.Errorf("Failed to check for public IP: %v", err) + } + if !hasPublic { + c.log.Infof("Not binding STCPR: no public IP address found") + return + } + c.log.Infof("Trying to bind stcpr") + if err := c.addressResolver.BindSTCPR(context.Background(), port); err != nil { + c.log.Errorf("Failed to bind STCPR: %v", err) + return + } + c.log.Infof("Successfuly bound stcpr to port %s", port) c.acceptConnections(lis) } From ad82e9631931ed8e53799eba701b1131de297e6a Mon Sep 17 00:00:00 2001 From: Never M Date: Mon, 28 Jun 2021 15:15:16 +0300 Subject: [PATCH 25/55] Switch to DialContext in stcpr client --- pkg/transport/network/stcpr.go | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/pkg/transport/network/stcpr.go b/pkg/transport/network/stcpr.go index b904d5ede..e729c12cb 100644 --- a/pkg/transport/network/stcpr.go +++ b/pkg/transport/network/stcpr.go @@ -35,7 +35,7 @@ func (c *stcprClient) Dial(ctx context.Context, rPK cipher.PubKey, rPort uint16) c.log.Infof("Resolved PK %v to visor data %v", rPK, visorData) - conn, err := c.dialVisor(visorData) + conn, err := c.dialVisor(ctx, visorData) if err != nil { return nil, err } @@ -43,11 +43,11 @@ func (c *stcprClient) Dial(ctx context.Context, rPK cipher.PubKey, rPort uint16) return c.initConnection(ctx, conn, c.lPK, rPK, rPort) } -func (c *stcprClient) dialVisor(visorData arclient.VisorData) (net.Conn, error) { +func (c *stcprClient) dialVisor(ctx context.Context, visorData arclient.VisorData) (net.Conn, error) { if visorData.IsLocal { for _, host := range visorData.Addresses { addr := net.JoinHostPort(host, visorData.Port) - conn, err := c.dial(addr) + conn, err := c.dial(ctx, addr) if err == nil { return conn, nil } @@ -57,12 +57,13 @@ func (c *stcprClient) dialVisor(visorData arclient.VisorData) (net.Conn, error) if _, _, err := net.SplitHostPort(addr); err != nil { addr = net.JoinHostPort(addr, visorData.Port) } - return c.dial(addr) + return c.dial(ctx, addr) } -func (c *stcprClient) dial(addr string) (net.Conn, error) { +func (c *stcprClient) dial(ctx context.Context, addr string) (net.Conn, error) { c.eb.SendTCPDial(context.Background(), string(STCPR), addr) - return net.Dial("tcp", addr) + dialer := net.Dialer{} + return dialer.DialContext(ctx, "tcp", addr) } // Serve starts accepting all incoming connections (i.e. connections to all skywire ports) @@ -97,7 +98,7 @@ func (c *stcprClient) serve() { c.log.Infof("Not binding STCPR: no public IP address found") return } - c.log.Infof("Trying to bind stcpr") + c.log.Infof("Binding") if err := c.addressResolver.BindSTCPR(context.Background(), port); err != nil { c.log.Errorf("Failed to bind STCPR: %v", err) return From f70f9030b1868a2409d81941571a2e90047b9914 Mon Sep 17 00:00:00 2001 From: Never M Date: Mon, 28 Jun 2021 15:37:06 +0300 Subject: [PATCH 26/55] Add sudph serve functionality --- pkg/transport/network/stcpr.go | 2 +- pkg/transport/network/sudph.go | 80 ++++++++++++++++++++++++++++++++++ 2 files changed, 81 insertions(+), 1 deletion(-) diff --git a/pkg/transport/network/stcpr.go b/pkg/transport/network/stcpr.go index e729c12cb..da93a52f5 100644 --- a/pkg/transport/network/stcpr.go +++ b/pkg/transport/network/stcpr.go @@ -80,7 +80,7 @@ func (c *stcprClient) Serve() error { func (c *stcprClient) serve() { lis, err := net.Listen("tcp", "") if err != nil { - c.log.Errorf("Failed to listen on random port: %v", "", err) + c.log.Errorf("Failed to listen on random port: %v", err) return } diff --git a/pkg/transport/network/sudph.go b/pkg/transport/network/sudph.go index 63bac3309..f7c21c3a7 100644 --- a/pkg/transport/network/sudph.go +++ b/pkg/transport/network/sudph.go @@ -1,4 +1,84 @@ package network +import ( + "net" + + "github.com/AudriusButkevicius/pfilter" + "github.com/skycoin/skywire/pkg/snet/arclient" + "github.com/xtaci/kcp-go" +) + +// holePunchMessage is sent in a dummy UDP packet that is sent by both parties to establish UDP hole punching. +const ( + holePunchMessage = "holepunch" + // dialConnPriority and visorsConnPriority are used to set an order how connection filters apply. + dialConnPriority = 2 + visorsConnPriority = 3 +) + type sudphClient struct { + *genericClient + addressResolver arclient.APIClient +} + +func newSudph(generic *genericClient, addressResolver arclient.APIClient) Client { + client := &stcprClient{genericClient: generic, addressResolver: addressResolver} + client.netType = SUDPH + return client +} + +// Serve starts accepting all incoming connections (i.e. connections to all skywire ports) +// Connections that successfuly perform handshakes will be delivered to a listener +// bound to a specific skywire port +func (c *sudphClient) Serve() error { + if c.connListener != nil { + return ErrAlreadyListening + } + go c.serve() + return nil +} + +func (c *sudphClient) serve() { + lis, err := c.listen() + if err != nil { + c.log.Errorf("Failed to listen on random port: %v", err) + return + } + c.acceptConnections(lis) +} + +func (c *sudphClient) listen() (net.Listener, error) { + packetListener, err := net.ListenPacket("udp", "") + if err != nil { + return nil, err + } + listenFilter := pfilter.NewPacketFilter(packetListener) + sudphVisorsConn := listenFilter.NewConn(visorsConnPriority, nil) + listenFilter.Start() + c.log.Infof("Binding") + addrCh, err := c.addressResolver.BindSUDPH(listenFilter) + if err != nil { + return nil, err + } + go c.PICKNAMEFORME(sudphVisorsConn, addrCh) + return kcp.ServeConn(nil, 0, 0, sudphVisorsConn) +} + +// todo: name +func (c *sudphClient) PICKNAMEFORME(conn net.PacketConn, addrCh <-chan arclient.RemoteVisor) { + for addr := range addrCh { + udpAddr, err := net.ResolveUDPAddr("udp", addr.Addr) + if err != nil { + c.log.WithError(err).Errorf("Failed to resolve UDP address %q", addr) + continue + } + + c.log.Infof("Sending hole punch packet to %v", addr) + + if _, err := conn.WriteTo([]byte(holePunchMessage), udpAddr); err != nil { + c.log.WithError(err).Errorf("Failed to send hole punch packet to %v", udpAddr) + continue + } + c.log.Infof("Sent hole punch packet to %v", addr) + } } From 0f18f4d23bacd62537373fdc97d57cc64bf2244a Mon Sep 17 00:00:00 2001 From: Never M Date: Mon, 28 Jun 2021 17:43:03 +0300 Subject: [PATCH 27/55] Add sudph dial functionality --- pkg/transport/manager.go | 2 +- pkg/transport/network/client.go | 2 + pkg/transport/network/sudph.go | 99 +++++++++++++++++++++++++++++++-- 3 files changed, 97 insertions(+), 6 deletions(-) diff --git a/pkg/transport/manager.go b/pkg/transport/manager.go index f3c15fb0d..7b17349d4 100644 --- a/pkg/transport/manager.go +++ b/pkg/transport/manager.go @@ -100,7 +100,7 @@ func (tm *Manager) serve(ctx context.Context) { } func (tm *Manager) initClients() { - acceptedNetworks := []network.Type{network.STCP, network.STCPR} + acceptedNetworks := []network.Type{network.STCP, network.STCPR, network.SUDPH} for _, netType := range acceptedNetworks { tm.netClients[netType] = tm.factory.MakeClient(netType) } diff --git a/pkg/transport/network/client.go b/pkg/transport/network/client.go index 15a60ed2e..efb5c215e 100644 --- a/pkg/transport/network/client.go +++ b/pkg/transport/network/client.go @@ -64,6 +64,8 @@ func (f *ClientFactory) MakeClient(netType Type) Client { return newStcp(generic, f.PKTable) case STCPR: return newStcpr(generic, f.ARClient) + case SUDPH: + return newSudph(generic, f.ARClient) } return nil } diff --git a/pkg/transport/network/sudph.go b/pkg/transport/network/sudph.go index f7c21c3a7..22b158f57 100644 --- a/pkg/transport/network/sudph.go +++ b/pkg/transport/network/sudph.go @@ -1,9 +1,15 @@ package network import ( + "context" + "fmt" + "io" "net" + "time" "github.com/AudriusButkevicius/pfilter" + "github.com/skycoin/dmsg/cipher" + "github.com/skycoin/skywire/internal/packetfilter" "github.com/skycoin/skywire/pkg/snet/arclient" "github.com/xtaci/kcp-go" ) @@ -14,15 +20,17 @@ const ( // dialConnPriority and visorsConnPriority are used to set an order how connection filters apply. dialConnPriority = 2 visorsConnPriority = 3 + dialTimeout = 30 * time.Second ) type sudphClient struct { *genericClient + filter *pfilter.PacketFilter addressResolver arclient.APIClient } func newSudph(generic *genericClient, addressResolver arclient.APIClient) Client { - client := &stcprClient{genericClient: generic, addressResolver: addressResolver} + client := &sudphClient{genericClient: generic, addressResolver: addressResolver} client.netType = SUDPH return client } @@ -52,11 +60,11 @@ func (c *sudphClient) listen() (net.Listener, error) { if err != nil { return nil, err } - listenFilter := pfilter.NewPacketFilter(packetListener) - sudphVisorsConn := listenFilter.NewConn(visorsConnPriority, nil) - listenFilter.Start() + c.filter = pfilter.NewPacketFilter(packetListener) + sudphVisorsConn := c.filter.NewConn(visorsConnPriority, nil) + c.filter.Start() c.log.Infof("Binding") - addrCh, err := c.addressResolver.BindSUDPH(listenFilter) + addrCh, err := c.addressResolver.BindSUDPH(c.filter) if err != nil { return nil, err } @@ -82,3 +90,84 @@ func (c *sudphClient) PICKNAMEFORME(conn net.PacketConn, addrCh <-chan arclient. c.log.Infof("Sent hole punch packet to %v", addr) } } + +func (c *sudphClient) Dial(ctx context.Context, rPK cipher.PubKey, rPort uint16) (*Conn, error) { + if c.isClosed() { + return nil, io.ErrClosedPipe + } + + c.log.Infof("Dialing PK %v", rPK) + visorData, err := c.addressResolver.Resolve(ctx, string(SUDPH), rPK) + if err != nil { + return nil, fmt.Errorf("resolve PK: %w", err) + } + + c.log.Infof("Resolved PK %v to visor data %v", rPK, visorData) + + conn, err := c.dialVisor(ctx, visorData) + if err != nil { + return nil, err + } + + return c.initConnection(ctx, conn, c.lPK, rPK, rPort) +} + +func (c *sudphClient) dialVisor(ctx context.Context, visorData arclient.VisorData) (net.Conn, error) { + if visorData.IsLocal { + for _, host := range visorData.Addresses { + addr := net.JoinHostPort(host, visorData.Port) + conn, err := c.dialWithTimeout(ctx, addr) + if err == nil { + return conn, nil + } + } + } + + addr := visorData.RemoteAddr + if _, _, err := net.SplitHostPort(addr); err != nil { + addr = net.JoinHostPort(addr, visorData.Port) + } + + return c.dialWithTimeout(ctx, addr) +} + +func (c *sudphClient) dialWithTimeout(ctx context.Context, addr string) (net.Conn, error) { + timedCtx, cancel := context.WithTimeout(ctx, dialTimeout) + defer cancel() + c.log.Infof("Dialing %v", addr) + + for { + select { + case <-timedCtx.Done(): + return nil, timedCtx.Err() + default: + conn, err := c.dial(addr) + if err == nil { + c.log.Infof("Dialed %v", addr) + return conn, nil + } + c.log.WithError(err). + Warnf("Failed to dial %v, trying again: %v", addr, err) + } + } +} + +func (c *sudphClient) dial(remoteAddr string) (net.Conn, error) { + rAddr, err := net.ResolveUDPAddr("udp", remoteAddr) + if err != nil { + return nil, fmt.Errorf("net.ResolveUDPAddr (remote): %w", err) + } + + dialConn := c.filter.NewConn(dialConnPriority, packetfilter.NewKCPConversationFilter()) + + if _, err := dialConn.WriteTo([]byte(holePunchMessage), rAddr); err != nil { + return nil, fmt.Errorf("dialConn.WriteTo: %w", err) + } + + kcpConn, err := kcp.NewConn(remoteAddr, nil, 0, 0, dialConn) + if err != nil { + return nil, err + } + + return kcpConn, nil +} From 962cbbd2c28f90dd9d98684549e0e421d2eeed86 Mon Sep 17 00:00:00 2001 From: Never M Date: Tue, 29 Jun 2021 11:54:14 +0300 Subject: [PATCH 28/55] Factor out common sudph and stcpr code --- pkg/transport/network/client.go | 37 +++++++++++++++++++++++++++++- pkg/transport/network/sudph.go | 40 +++++---------------------------- 2 files changed, 42 insertions(+), 35 deletions(-) diff --git a/pkg/transport/network/client.go b/pkg/transport/network/client.go index efb5c215e..a3a5c8aa1 100644 --- a/pkg/transport/network/client.go +++ b/pkg/transport/network/client.go @@ -3,6 +3,7 @@ package network import ( "context" "errors" + "fmt" "io" "net" "sync" @@ -59,13 +60,15 @@ func (f *ClientFactory) MakeClient(netType Type) Client { generic.lSK = f.SK generic.listenAddr = f.ListenAddr + resolved := &resolvedClient{genericClient: generic, ar: f.ARClient} + switch netType { case STCP: return newStcp(generic, f.PKTable) case STCPR: return newStcpr(generic, f.ARClient) case SUDPH: - return newSudph(generic, f.ARClient) + return newSudph(resolved, f.ARClient) } return nil } @@ -281,3 +284,35 @@ func (c *genericClient) Close() error { func (c *genericClient) Type() Type { return c.netType } + +type resolvedClient struct { + *genericClient + ar arclient.APIClient +} + +type dialFunc func(ctx context.Context, addr string) (net.Conn, error) + +func (c *resolvedClient) dialVisor(ctx context.Context, rPK cipher.PubKey, dial dialFunc) (net.Conn, error) { + c.log.Infof("Dialing PK %v", rPK) + visorData, err := c.ar.Resolve(ctx, string(c.netType), rPK) + if err != nil { + return nil, fmt.Errorf("resolve PK: %w", err) + } + c.log.Infof("Resolved PK %v to visor data %v", rPK, visorData) + + if visorData.IsLocal { + for _, host := range visorData.Addresses { + addr := net.JoinHostPort(host, visorData.Port) + conn, err := dial(ctx, addr) + if err == nil { + return conn, nil + } + } + } + + addr := visorData.RemoteAddr + if _, _, err := net.SplitHostPort(addr); err != nil { + addr = net.JoinHostPort(addr, visorData.Port) + } + return dial(ctx, addr) +} diff --git a/pkg/transport/network/sudph.go b/pkg/transport/network/sudph.go index 22b158f57..8da7c28dd 100644 --- a/pkg/transport/network/sudph.go +++ b/pkg/transport/network/sudph.go @@ -24,13 +24,12 @@ const ( ) type sudphClient struct { - *genericClient - filter *pfilter.PacketFilter - addressResolver arclient.APIClient + *resolvedClient + filter *pfilter.PacketFilter } -func newSudph(generic *genericClient, addressResolver arclient.APIClient) Client { - client := &sudphClient{genericClient: generic, addressResolver: addressResolver} +func newSudph(resolved *resolvedClient, addressResolver arclient.APIClient) Client { + client := &sudphClient{resolvedClient: resolved} client.netType = SUDPH return client } @@ -64,7 +63,7 @@ func (c *sudphClient) listen() (net.Listener, error) { sudphVisorsConn := c.filter.NewConn(visorsConnPriority, nil) c.filter.Start() c.log.Infof("Binding") - addrCh, err := c.addressResolver.BindSUDPH(c.filter) + addrCh, err := c.ar.BindSUDPH(c.filter) if err != nil { return nil, err } @@ -96,15 +95,7 @@ func (c *sudphClient) Dial(ctx context.Context, rPK cipher.PubKey, rPort uint16) return nil, io.ErrClosedPipe } - c.log.Infof("Dialing PK %v", rPK) - visorData, err := c.addressResolver.Resolve(ctx, string(SUDPH), rPK) - if err != nil { - return nil, fmt.Errorf("resolve PK: %w", err) - } - - c.log.Infof("Resolved PK %v to visor data %v", rPK, visorData) - - conn, err := c.dialVisor(ctx, visorData) + conn, err := c.dialVisor(ctx, rPK, c.dialWithTimeout) if err != nil { return nil, err } @@ -112,25 +103,6 @@ func (c *sudphClient) Dial(ctx context.Context, rPK cipher.PubKey, rPort uint16) return c.initConnection(ctx, conn, c.lPK, rPK, rPort) } -func (c *sudphClient) dialVisor(ctx context.Context, visorData arclient.VisorData) (net.Conn, error) { - if visorData.IsLocal { - for _, host := range visorData.Addresses { - addr := net.JoinHostPort(host, visorData.Port) - conn, err := c.dialWithTimeout(ctx, addr) - if err == nil { - return conn, nil - } - } - } - - addr := visorData.RemoteAddr - if _, _, err := net.SplitHostPort(addr); err != nil { - addr = net.JoinHostPort(addr, visorData.Port) - } - - return c.dialWithTimeout(ctx, addr) -} - func (c *sudphClient) dialWithTimeout(ctx context.Context, addr string) (net.Conn, error) { timedCtx, cancel := context.WithTimeout(ctx, dialTimeout) defer cancel() From d6aad7f857abf3389affe52a34de49a4a231fc52 Mon Sep 17 00:00:00 2001 From: Never M Date: Tue, 29 Jun 2021 12:39:51 +0300 Subject: [PATCH 29/55] Fix style, add comments and todos --- pkg/app/appevent/utils.go | 3 +- pkg/snet/directtp/tphandshake/handshake.go | 5 +- pkg/transport/manager.go | 2 +- pkg/transport/network/client.go | 56 +++++++++++++++------- pkg/transport/network/connection.go | 34 +++++-------- pkg/transport/network/listener.go | 41 ++++++++-------- pkg/transport/network/stcp.go | 10 ++-- pkg/transport/network/stcpr.go | 46 +++++------------- pkg/transport/network/sudph.go | 16 +++---- 9 files changed, 101 insertions(+), 112 deletions(-) diff --git a/pkg/app/appevent/utils.go b/pkg/app/appevent/utils.go index 79b173b2f..6f25ce978 100644 --- a/pkg/app/appevent/utils.go +++ b/pkg/app/appevent/utils.go @@ -2,13 +2,14 @@ package appevent import "context" +// SendTCPDial sends tcp dial event func (eb *Broadcaster) SendTCPDial(ctx context.Context, remoteNet, remoteAddr string) { data := TCPDialData{RemoteNet: remoteNet, RemoteAddr: remoteAddr} event := NewEvent(TCPDial, data) eb.sendEvent(ctx, event) } -func (eb *Broadcaster) sendEvent(ctx context.Context, event *Event) { +func (eb *Broadcaster) sendEvent(_ context.Context, event *Event) { err := eb.Broadcast(context.Background(), event) //nolint:errcheck if err != nil { eb.log.Warn("Failed to broadcast event: %v", event) diff --git a/pkg/snet/directtp/tphandshake/handshake.go b/pkg/snet/directtp/tphandshake/handshake.go index f6b5e88cd..eb0440e87 100644 --- a/pkg/snet/directtp/tphandshake/handshake.go +++ b/pkg/snet/directtp/tphandshake/handshake.go @@ -105,10 +105,7 @@ type CheckF2 = func(f2 Frame2) error // port checker to check port in Frame2 func MakeF2PortChecker(portChecker func(port uint16) error) CheckF2 { return func(f2 Frame2) error { - if err := portChecker(f2.DstAddr.Port); err != nil { - return err - } - return nil + return portChecker(f2.DstAddr.Port) } } diff --git a/pkg/transport/manager.go b/pkg/transport/manager.go index 7b17349d4..c1027036b 100644 --- a/pkg/transport/manager.go +++ b/pkg/transport/manager.go @@ -112,7 +112,7 @@ func (tm *Manager) runClients(ctx context.Context) { } for _, client := range tm.netClients { tm.Logger.Infof("Serving %s network", client.Type()) - err := client.Serve() + err := client.Start() if err != nil { tm.Logger.WithError(err).Errorf("Failed to listen on %s network", client.Type()) continue diff --git a/pkg/transport/network/client.go b/pkg/transport/network/client.go index a3a5c8aa1..bd4b7dbc9 100644 --- a/pkg/transport/network/client.go +++ b/pkg/transport/network/client.go @@ -12,6 +12,7 @@ import ( "github.com/skycoin/dmsg" "github.com/skycoin/dmsg/cipher" "github.com/skycoin/skycoin/src/util/logging" + "github.com/skycoin/skywire/pkg/app/appevent" "github.com/skycoin/skywire/pkg/snet/arclient" "github.com/skycoin/skywire/pkg/snet/directtp/porter" @@ -19,17 +20,29 @@ import ( "github.com/skycoin/skywire/pkg/transport/network/stcp" ) -// Client provides access to skywire network in terms of dialing remote visors -// and listening to incoming connections +// Client provides access to skywire network +// It allows dialing remote visors using their public keys, as +// well as listening to incoming connections from other visors type Client interface { - // todo: change return type to wrapped conn + // Dial remote visor, that is listening on the given skywire port Dial(ctx context.Context, remote cipher.PubKey, port uint16) (*Conn, error) + // Start initializes the client and prepares it for listening. It is required + // to be called to start accepting connections + Start() error + // Listen on the given skywire port. This can be called multiple times + // for different ports for the same client. It requires Start to be called + // to start accepting connections Listen(port uint16) (*Listener, error) + // todo: remove LocalAddr() (net.Addr, error) + // PK returns public key of the visor running this client PK() cipher.PubKey + // SK returns secret key of the visor running this client SK() cipher.SecKey - Serve() error + // Close the client, stop accepting connections. Connections returned by the + // client should be closed manually Close() error + // Type returns skywire network type in which this client operates Type() Type } @@ -66,9 +79,9 @@ func (f *ClientFactory) MakeClient(netType Type) Client { case STCP: return newStcp(generic, f.PKTable) case STCPR: - return newStcpr(generic, f.ARClient) + return newStcpr(resolved) case SUDPH: - return newSudph(resolved, f.ARClient) + return newSudph(resolved) } return nil } @@ -102,7 +115,7 @@ type genericClient struct { // the remote client // The process will perform handshake over raw connection // todo: rename to handshake, initHandshake, skyConnect or smth? -func (c *genericClient) initConnection(ctx context.Context, conn net.Conn, lPK, rPK cipher.PubKey, rPort uint16) (*Conn, error) { +func (c *genericClient) initConnection(ctx context.Context, conn net.Conn, rPK cipher.PubKey, rPort uint16) (*Conn, error) { lPort, freePort, err := c.porter.ReserveEphemeral(ctx) if err != nil { return nil, err @@ -138,6 +151,8 @@ func (c *genericClient) acceptConnections(lis net.Listener) { } } +// wrapConn performs handshake over provided raw connection and wraps it in +// network.Conn type using the data obtained from handshake process func (c *genericClient) wrapConn(conn net.Conn, hs tphandshake.Handshake, initiator bool, onClose func()) (*Conn, error) { lAddr, rAddr, err := hs(conn, time.Now().Add(tphandshake.Timeout)) if err != nil { @@ -157,13 +172,10 @@ func (c *genericClient) wrapConn(conn net.Conn, hs tphandshake.Handshake, initia return wrappedConn, nil } -// todo: better comments -// acceptConn -// 1. accepts new raw network connection -// 2. performs accepting handshake over that connection -// 3. obtains skywire port from handshake -// 4. obtains skywire listener registered for that port -// 5. delivers connection to the listener +// acceptConn accepts new connection in underlying raw network listener, +// performs handshake, and using the data from the handshake wraps +// connection and delivers it to the appropriate listener. +// The listener is chosen using skywire port from the incoming visor connection func (c *genericClient) acceptConn() error { if c.isClosed() { return io.ErrClosedPipe @@ -185,12 +197,10 @@ func (c *genericClient) acceptConn() error { if err != nil { return err } - if err := lis.Introduce(wrappedConn); err != nil { - return err - } - return nil + return lis.introduce(wrappedConn) } +// todo: remove // LocalAddr returns local address. This is network address the client // listens to for incoming connections, not skywire address func (c *genericClient) LocalAddr() (net.Addr, error) { @@ -250,14 +260,17 @@ func (c *genericClient) isClosed() bool { } } +// PK implements interface func (c *genericClient) PK() cipher.PubKey { return c.lPK } +// SK implements interface func (c *genericClient) SK() cipher.SecKey { return c.lSK } +// Close implements interface func (c *genericClient) Close() error { c.closeOnce.Do(func() { close(c.done) @@ -281,10 +294,14 @@ func (c *genericClient) Close() error { return nil } +// Type implements interface func (c *genericClient) Type() Type { return c.netType } +// resolvedClient is a wrapper around genericClient, +// for the types of transports that use address resolver service +// to resolve addresses of remote visors type resolvedClient struct { *genericClient ar arclient.APIClient @@ -292,6 +309,9 @@ type resolvedClient struct { type dialFunc func(ctx context.Context, addr string) (net.Conn, error) +// dialVisor uses address resovler to obtain network address of the target visor +// and dials that visor address(es) +// dial process is specific to transport type and is provided by the client func (c *resolvedClient) dialVisor(ctx context.Context, rPK cipher.PubKey, dial dialFunc) (net.Conn, error) { c.log.Infof("Dialing PK %v", rPK) visorData, err := c.ar.Resolve(ctx, string(c.netType), rPK) diff --git a/pkg/transport/network/connection.go b/pkg/transport/network/connection.go index 8e303cc24..9cb01e65f 100644 --- a/pkg/transport/network/connection.go +++ b/pkg/transport/network/connection.go @@ -3,17 +3,17 @@ package network import ( "fmt" "net" - "time" "github.com/skycoin/dmsg" "github.com/skycoin/dmsg/cipher" "github.com/skycoin/dmsg/noise" - "github.com/skycoin/skycoin/src/util/logging" + "github.com/skycoin/skywire/pkg/snet/directtp/noisewrapper" - "github.com/skycoin/skywire/pkg/snet/directtp/tphandshake" ) -// Conn wraps an underlying net.Conn and modifies various methods to integrate better with the 'network' package. +// Conn represents a network connection between two visors in skywire network +// This connection wraps raw network connection and is ready to use for sending data. +// It also provides skywire-specific methods on top of net.Conn type Conn struct { net.Conn lAddr, rAddr dmsg.Addr @@ -21,19 +21,6 @@ type Conn struct { connType Type } -// ConnConfig describes a config for Conn. -type ConnConfig struct { - Log *logging.Logger - Conn net.Conn - LocalSK cipher.SecKey - LocalPK cipher.PubKey - Deadline time.Time - Handshake tphandshake.Handshake - FreePort func() - Encrypt bool - Initiator bool -} - func (c *Conn) encrypt(lPK cipher.PubKey, lSK cipher.SecKey, initator bool) error { config := noise.Config{ LocalPK: lPK, @@ -70,17 +57,20 @@ func (c *Conn) Close() error { return c.Conn.Close() } -// LocalPK returns local public key of connection. +// LocalPK returns local public key of connection func (c *Conn) LocalPK() cipher.PubKey { return c.lAddr.PK } -// RemotePK returns remote public key of connection. +// RemotePK returns remote public key of connection func (c *Conn) RemotePK() cipher.PubKey { return c.rAddr.PK } -// LocalPort returns local port of connection. +// LocalPort returns local skywire port of connection +// This is not underlying OS port, but port within skywire network func (c *Conn) LocalPort() uint16 { return c.lAddr.Port } -// RemotePort returns remote port of connection. +// RemotePort returns remote skywire port of connection +// This is not underlying OS port, but port within skywire network func (c *Conn) RemotePort() uint16 { return c.rAddr.Port } -// Network returns network of connection. +// Network returns network of connection +// todo: consider switching to Type instead of string func (c *Conn) Network() string { return string(c.connType) } diff --git a/pkg/transport/network/listener.go b/pkg/transport/network/listener.go index fbb190373..5a5743d60 100644 --- a/pkg/transport/network/listener.go +++ b/pkg/transport/network/listener.go @@ -32,24 +32,6 @@ func NewListener(lAddr dmsg.Addr, freePort func(), network Type) *Listener { } } -// Introduce is used by Client to introduce Conn to Listener. -func (l *Listener) Introduce(conn *Conn) error { - select { - case <-l.done: - return io.ErrClosedPipe - default: - l.mx.Lock() - defer l.mx.Unlock() - - select { - case l.accept <- conn: - return nil - case <-l.done: - return io.ErrClosedPipe - } - } -} - // Accept implements net.Listener, returns generic net.Conn func (l *Listener) Accept() (net.Conn, error) { return l.AcceptConn() @@ -69,7 +51,8 @@ func (l *Listener) AcceptConn() (*Conn, error) { func (l *Listener) Close() error { l.once.Do(func() { close(l.done) - + // todo: consider if removing locks will change anything + // todo: close all pending connections in l.accept l.mx.Lock() close(l.accept) l.mx.Unlock() @@ -86,6 +69,26 @@ func (l *Listener) Addr() net.Addr { } // Network returns network type +// todo: consider switching to Type instead of string func (l *Listener) Network() string { return string(l.network) } + +// Introduce is used by Client to introduce a new connection to this Listener +func (l *Listener) introduce(conn *Conn) error { + // todo: think if this is needed + select { + case <-l.done: + return io.ErrClosedPipe + default: + l.mx.Lock() + defer l.mx.Unlock() + + select { + case l.accept <- conn: + return nil + case <-l.done: + return io.ErrClosedPipe + } + } +} diff --git a/pkg/transport/network/stcp.go b/pkg/transport/network/stcp.go index 7e152d7be..195897432 100644 --- a/pkg/transport/network/stcp.go +++ b/pkg/transport/network/stcp.go @@ -7,6 +7,7 @@ import ( "net" "github.com/skycoin/dmsg/cipher" + "github.com/skycoin/skywire/pkg/transport/network/stcp" ) @@ -25,6 +26,7 @@ func newStcp(generic *genericClient, table stcp.PKTable) Client { // PK table var ErrStcpEntryNotFound = errors.New("entry not found in PK table") +// Dial implements Client interface func (c *stcpClient) Dial(ctx context.Context, rPK cipher.PubKey, rPort uint16) (*Conn, error) { if c.isClosed() { return nil, io.ErrClosedPipe @@ -44,13 +46,11 @@ func (c *stcpClient) Dial(ctx context.Context, rPK cipher.PubKey, rPort uint16) } c.log.Infof("Dialed %v:%v@%v", rPK, rPort, conn.RemoteAddr()) - return c.initConnection(ctx, conn, c.lPK, rPK, rPort) + return c.initConnection(ctx, conn, rPK, rPort) } -// Serve starts accepting all incoming connections (i.e. connections to all skywire ports) -// Connections that successfuly perform handshakes will be delivered to a listener -// bound to a specific skywire port -func (c *stcpClient) Serve() error { +// Start implements Client interface +func (c *stcpClient) Start() error { if c.connListener != nil { return ErrAlreadyListening } diff --git a/pkg/transport/network/stcpr.go b/pkg/transport/network/stcpr.go index da93a52f5..c625fcecc 100644 --- a/pkg/transport/network/stcpr.go +++ b/pkg/transport/network/stcpr.go @@ -7,57 +7,37 @@ import ( "net" "github.com/skycoin/dmsg/cipher" - "github.com/skycoin/skywire/pkg/snet/arclient" + "github.com/skycoin/skywire/pkg/util/netutil" ) type stcprClient struct { - *genericClient - addressResolver arclient.APIClient + *resolvedClient } -func newStcpr(generic *genericClient, addressResolver arclient.APIClient) Client { - client := &stcprClient{genericClient: generic, addressResolver: addressResolver} +func newStcpr(resolved *resolvedClient) Client { + client := &stcprClient{resolvedClient: resolved} client.netType = STCPR return client } +// Dial implements interface func (c *stcprClient) Dial(ctx context.Context, rPK cipher.PubKey, rPort uint16) (*Conn, error) { if c.isClosed() { return nil, io.ErrClosedPipe } - c.log.Infof("Dialing PK %v", rPK) - visorData, err := c.addressResolver.Resolve(ctx, string(STCPR), rPK) + visorData, err := c.ar.Resolve(ctx, string(STCPR), rPK) if err != nil { return nil, fmt.Errorf("resolve PK: %w", err) } - c.log.Infof("Resolved PK %v to visor data %v", rPK, visorData) - - conn, err := c.dialVisor(ctx, visorData) + conn, err := c.dialVisor(ctx, rPK, c.dial) if err != nil { return nil, err } - return c.initConnection(ctx, conn, c.lPK, rPK, rPort) -} - -func (c *stcprClient) dialVisor(ctx context.Context, visorData arclient.VisorData) (net.Conn, error) { - if visorData.IsLocal { - for _, host := range visorData.Addresses { - addr := net.JoinHostPort(host, visorData.Port) - conn, err := c.dial(ctx, addr) - if err == nil { - return conn, nil - } - } - } - addr := visorData.RemoteAddr - if _, _, err := net.SplitHostPort(addr); err != nil { - addr = net.JoinHostPort(addr, visorData.Port) - } - return c.dial(ctx, addr) + return c.initConnection(ctx, conn, rPK, rPort) } func (c *stcprClient) dial(ctx context.Context, addr string) (net.Conn, error) { @@ -66,10 +46,8 @@ func (c *stcprClient) dial(ctx context.Context, addr string) (net.Conn, error) { return dialer.DialContext(ctx, "tcp", addr) } -// Serve starts accepting all incoming connections (i.e. connections to all skywire ports) -// Connections that successfuly perform handshakes will be delivered to a listener -// bound to a specific skywire port -func (c *stcprClient) Serve() error { +// Start implements Client interface +func (c *stcprClient) Start() error { if c.connListener != nil { return ErrAlreadyListening } @@ -99,10 +77,10 @@ func (c *stcprClient) serve() { return } c.log.Infof("Binding") - if err := c.addressResolver.BindSTCPR(context.Background(), port); err != nil { + if err := c.ar.BindSTCPR(context.Background(), port); err != nil { c.log.Errorf("Failed to bind STCPR: %v", err) return } - c.log.Infof("Successfuly bound stcpr to port %s", port) + c.log.Infof("Successfully bound stcpr to port %s", port) c.acceptConnections(lis) } diff --git a/pkg/transport/network/sudph.go b/pkg/transport/network/sudph.go index 8da7c28dd..c11b2e73a 100644 --- a/pkg/transport/network/sudph.go +++ b/pkg/transport/network/sudph.go @@ -9,13 +9,14 @@ import ( "github.com/AudriusButkevicius/pfilter" "github.com/skycoin/dmsg/cipher" + "github.com/xtaci/kcp-go" + "github.com/skycoin/skywire/internal/packetfilter" "github.com/skycoin/skywire/pkg/snet/arclient" - "github.com/xtaci/kcp-go" ) -// holePunchMessage is sent in a dummy UDP packet that is sent by both parties to establish UDP hole punching. const ( + // holePunchMessage is sent in a dummy UDP packet that is sent by both parties to establish UDP hole punching. holePunchMessage = "holepunch" // dialConnPriority and visorsConnPriority are used to set an order how connection filters apply. dialConnPriority = 2 @@ -28,16 +29,14 @@ type sudphClient struct { filter *pfilter.PacketFilter } -func newSudph(resolved *resolvedClient, addressResolver arclient.APIClient) Client { +func newSudph(resolved *resolvedClient) Client { client := &sudphClient{resolvedClient: resolved} client.netType = SUDPH return client } -// Serve starts accepting all incoming connections (i.e. connections to all skywire ports) -// Connections that successfuly perform handshakes will be delivered to a listener -// bound to a specific skywire port -func (c *sudphClient) Serve() error { +// Start implements Client interface +func (c *sudphClient) Start() error { if c.connListener != nil { return ErrAlreadyListening } @@ -90,6 +89,7 @@ func (c *sudphClient) PICKNAMEFORME(conn net.PacketConn, addrCh <-chan arclient. } } +// Dial implements interface func (c *sudphClient) Dial(ctx context.Context, rPK cipher.PubKey, rPort uint16) (*Conn, error) { if c.isClosed() { return nil, io.ErrClosedPipe @@ -100,7 +100,7 @@ func (c *sudphClient) Dial(ctx context.Context, rPK cipher.PubKey, rPort uint16) return nil, err } - return c.initConnection(ctx, conn, c.lPK, rPK, rPort) + return c.initConnection(ctx, conn, rPK, rPort) } func (c *sudphClient) dialWithTimeout(ctx context.Context, addr string) (net.Conn, error) { From 521bc59a7f3e9371b13092ca2b10776234ff3798 Mon Sep 17 00:00:00 2001 From: Never M Date: Tue, 29 Jun 2021 13:05:32 +0300 Subject: [PATCH 30/55] Move address resolver to transport --- pkg/transport/manager.go | 102 ++-- pkg/transport/network/addrresolver/client.go | 472 ++++++++++++++++++ .../network/addrresolver/client_test.go | 101 ++++ .../network/addrresolver/mock_api_client.go | 109 ++++ pkg/transport/network/client.go | 6 +- pkg/transport/network/sudph.go | 4 +- pkg/visor/init.go | 6 +- pkg/visor/rpc_test.go | 4 +- pkg/visor/visor.go | 6 +- 9 files changed, 726 insertions(+), 84 deletions(-) create mode 100644 pkg/transport/network/addrresolver/client.go create mode 100644 pkg/transport/network/addrresolver/client_test.go create mode 100644 pkg/transport/network/addrresolver/mock_api_client.go diff --git a/pkg/transport/manager.go b/pkg/transport/manager.go index c1027036b..1dafe8a2f 100644 --- a/pkg/transport/manager.go +++ b/pkg/transport/manager.go @@ -14,12 +14,8 @@ import ( "github.com/skycoin/skywire/pkg/routing" "github.com/skycoin/skywire/pkg/skyenv" - "github.com/skycoin/skywire/pkg/snet" - "github.com/skycoin/skywire/pkg/snet/arclient" - "github.com/skycoin/skywire/pkg/snet/directtp" - "github.com/skycoin/skywire/pkg/snet/directtp/tptypes" - "github.com/skycoin/skywire/pkg/snet/snettest" "github.com/skycoin/skywire/pkg/transport/network" + "github.com/skycoin/skywire/pkg/transport/network/addrresolver" ) // TPCloseCallback triggers after a session is closed. @@ -38,7 +34,7 @@ type Manager struct { Logger *logging.Logger Conf *ManagerConfig tps map[uuid.UUID]*ManagedTransport - arClient arclient.APIClient + arClient addrresolver.APIClient readCh chan routing.Packet mx sync.RWMutex @@ -55,7 +51,7 @@ type Manager struct { // NewManager creates a Manager with the provided configuration and transport factories. // 'factories' should be ordered by preference. -func NewManager(log *logging.Logger, arClient arclient.APIClient, config *ManagerConfig, factory network.ClientFactory) (*Manager, error) { +func NewManager(log *logging.Logger, arClient addrresolver.APIClient, config *ManagerConfig, factory network.ClientFactory) (*Manager, error) { if log == nil { log = logging.MustGetLogger("tp_manager") } @@ -181,8 +177,10 @@ func (tm *Manager) initTransports(ctx context.Context) { } } -func (tm *Manager) STcpr() (directtp.Client, bool) { - return nil, false +// Stcpr returns stcpr client +func (tm *Manager) Stcpr() (network.Client, bool) { + c, ok := tm.netClients[network.STCP] + return c, ok } func (tm *Manager) acceptTransport(ctx context.Context, lis *network.Listener) error { @@ -248,18 +246,28 @@ func (tm *Manager) acceptTransport(ctx context.Context, lis *network.Listener) e // ErrNotFound is returned when requested transport is not found var ErrNotFound = errors.New("transport not found") +// ErrUnknownNetwork occurs on attempt to use an unknown network type. +var ErrUnknownNetwork = errors.New("unknown network type") + +// IsKnownNetwork returns true when netName is a known +// network type that we are able to operate in +func (tm *Manager) IsKnownNetwork(netName string) bool { + _, ok := tm.netClients[network.Type(netName)] + return ok +} + // GetTransport gets transport entity to the given remote -func (tm *Manager) GetTransport(remote cipher.PubKey, tpType string) (*ManagedTransport, error) { +func (tm *Manager) GetTransport(remote cipher.PubKey, netName string) (*ManagedTransport, error) { tm.mx.RLock() defer tm.mx.RUnlock() - if !snet.IsKnownNetwork(tpType) { - return nil, snet.ErrUnknownNetwork + if !tm.IsKnownNetwork(netName) { + return nil, ErrUnknownNetwork } - tpID := tm.tpIDFromPK(remote, tpType) + tpID := tm.tpIDFromPK(remote, netName) tp, ok := tm.tps[tpID] if !ok { - return nil, fmt.Errorf("transport to %s of type %s error: %w", remote, tpType, ErrNotFound) + return nil, fmt.Errorf("transport to %s of type %s error: %w", remote, netName, ErrNotFound) } return tp, nil } @@ -287,14 +295,14 @@ func (tm *Manager) GetTransportsByLabel(label Label) []*ManagedTransport { } // SaveTransport begins to attempt to establish data transports to the given 'remote' visor. -func (tm *Manager) SaveTransport(ctx context.Context, remote cipher.PubKey, tpType string, label Label) (*ManagedTransport, error) { +func (tm *Manager) SaveTransport(ctx context.Context, remote cipher.PubKey, netName string, label Label) (*ManagedTransport, error) { if tm.isClosing() { return nil, io.ErrClosedPipe } for { - mTp, err := tm.saveTransport(remote, true, tpType, label) + mTp, err := tm.saveTransport(remote, true, netName, label) if err != nil { return nil, fmt.Errorf("save transport: %w", err) } @@ -334,8 +342,8 @@ func (tm *Manager) SaveTransport(ctx context.Context, remote cipher.PubKey, tpTy func (tm *Manager) saveTransport(remote cipher.PubKey, initiator bool, netName string, label Label) (*ManagedTransport, error) { tm.mx.Lock() defer tm.mx.Unlock() - if !snet.IsKnownNetwork(netName) { - return nil, snet.ErrUnknownNetwork + if !tm.IsKnownNetwork(netName) { + return nil, ErrUnknownNetwork } tpID := tm.tpIDFromPK(remote, netName) @@ -364,13 +372,13 @@ func (tm *Manager) saveTransport(remote cipher.PubKey, initiator bool, netName s TransportLabel: label, }, initiator) - if mTp.netName == tptypes.STCPR { + if mTp.netName == string(network.STCPR) { if tm.arClient != nil { visorData, err := tm.arClient.Resolve(context.Background(), mTp.netName, remote) if err == nil { mTp.remoteAddr = visorData.RemoteAddr } else { - if err != arclient.ErrNoEntry { + if err != addrresolver.ErrNoEntry { return nil, fmt.Errorf("failed to resolve %s: %w", remote, err) } } @@ -393,7 +401,7 @@ func (tm *Manager) STCPRRemoteAddrs() []string { defer tm.mx.RUnlock() for _, tp := range tm.tps { - if tp.Entry.Type == tptypes.STCPR && tp.remoteAddr != "" { + if tp.Entry.Type == string(network.STCPR) && tp.remoteAddr != "" { addrs = append(addrs, tp.remoteAddr) } } @@ -510,54 +518,6 @@ func (tm *Manager) isClosing() bool { } } -func (tm *Manager) tpIDFromPK(pk cipher.PubKey, tpType string) uuid.UUID { - return MakeTransportID(tm.Conf.PubKey, pk, tpType) -} - -// CreateTransportPair create a new transport pair for tests. -func CreateTransportPair( - tpDisc DiscoveryClient, - keys []snettest.KeyPair, - nEnv *snettest.Env, - net string, -) (m0 *Manager, m1 *Manager, tp0 *ManagedTransport, tp1 *ManagedTransport, err error) { - // Prepare tp manager 0. - pk0, sk0 := keys[0].PK, keys[0].SK - ls0 := InMemoryTransportLogStore() - m0, err = NewManager(nil, new(arclient.MockAPIClient), &ManagerConfig{ - PubKey: pk0, - SecKey: sk0, - DiscoveryClient: tpDisc, - LogStore: ls0, - }, network.ClientFactory{}) - if err != nil { - return nil, nil, nil, nil, err - } - - go m0.Serve(context.TODO()) - - // Prepare tp manager 1. - pk1, sk1 := keys[1].PK, keys[1].SK - ls1 := InMemoryTransportLogStore() - m1, err = NewManager(nil, new(arclient.MockAPIClient), &ManagerConfig{ - PubKey: pk1, - SecKey: sk1, - DiscoveryClient: tpDisc, - LogStore: ls1, - }, network.ClientFactory{}) - if err != nil { - return nil, nil, nil, nil, err - } - - go m1.Serve(context.TODO()) - - // Create data transport between manager 1 & manager 2. - tp1, err = m1.SaveTransport(context.TODO(), pk0, net, LabelUser) - if err != nil { - return nil, nil, nil, nil, err - } - - tp0 = m0.Transport(MakeTransportID(pk0, pk1, net)) - - return m0, m1, tp0, tp1, nil +func (tm *Manager) tpIDFromPK(pk cipher.PubKey, netName string) uuid.UUID { + return MakeTransportID(tm.Conf.PubKey, pk, netName) } diff --git a/pkg/transport/network/addrresolver/client.go b/pkg/transport/network/addrresolver/client.go new file mode 100644 index 000000000..9840aceb1 --- /dev/null +++ b/pkg/transport/network/addrresolver/client.go @@ -0,0 +1,472 @@ +// Package addrresolver implements address resolver client +package addrresolver + +import ( + "bytes" + "context" + "encoding/json" + "errors" + "fmt" + "io" + "io/ioutil" + "net" + "net/http" + "net/url" + "time" + + "github.com/AudriusButkevicius/pfilter" + "github.com/skycoin/dmsg" + "github.com/skycoin/dmsg/cipher" + dmsgnetutil "github.com/skycoin/dmsg/netutil" + "github.com/skycoin/skycoin/src/util/logging" + "github.com/xtaci/kcp-go" + + "github.com/skycoin/skywire/internal/httpauth" + "github.com/skycoin/skywire/internal/netutil" + "github.com/skycoin/skywire/internal/packetfilter" + "github.com/skycoin/skywire/pkg/snet/directtp/tpconn" + "github.com/skycoin/skywire/pkg/snet/directtp/tphandshake" +) + +const ( + // sudphPriority is used to set an order how connection filters apply. + sudphPriority = 1 + stcprBindPath = "/bind/stcpr" + addrChSize = 1024 + udpKeepAliveInterval = 10 * time.Second + udpKeepAliveMessage = "keepalive" + defaultUDPPort = "30178" +) + +var ( + // ErrNoEntry means that there exists no entry for this PK. + ErrNoEntry = errors.New("no entry for this PK") + // ErrNotReady is returned when address resolver is not ready. + ErrNotReady = errors.New("address resolver is not ready") +) + +// Error is the object returned to the client when there's an error. +type Error struct { + Error string `json:"error"` +} + +//go:generate mockery -name APIClient -case underscore -inpkg + +// APIClient implements address resolver API client. +type APIClient interface { + io.Closer + BindSTCPR(ctx context.Context, port string) error + BindSUDPH(filter *pfilter.PacketFilter) (<-chan RemoteVisor, error) + Resolve(ctx context.Context, tType string, pk cipher.PubKey) (VisorData, error) + Health(ctx context.Context) (int, error) +} + +// VisorData stores visor data. +type VisorData struct { + RemoteAddr string `json:"remote_addr"` + IsLocal bool `json:"is_local,omitempty"` + LocalAddresses +} + +// httpClient implements APIClient for address resolver API. +type httpClient struct { + log *logging.Logger + httpClient *httpauth.Client + pk cipher.PubKey + sk cipher.SecKey + remoteHTTPAddr string + remoteUDPAddr string + sudphConn net.PacketConn + ready chan struct{} + closed chan struct{} +} + +// NewHTTP creates a new client setting a public key to the client to be used for auth. +// When keys are set, the client will sign request before submitting. +// The signature information is transmitted in the header using: +// * SW-Public: The specified public key. +// * SW-Nonce: The nonce for that public key. +// * SW-Sig: The signature of the payload + the nonce. +func NewHTTP(remoteAddr string, pk cipher.PubKey, sk cipher.SecKey, log *logging.Logger) (APIClient, error) { + remoteURL, err := url.Parse(remoteAddr) + if err != nil { + return nil, fmt.Errorf("parse URL: %w", err) + } + + remoteUDP := remoteURL.Host + if _, _, err := net.SplitHostPort(remoteUDP); err != nil { + remoteUDP = net.JoinHostPort(remoteUDP, defaultUDPPort) + } + + client := &httpClient{ + log: log, + pk: pk, + sk: sk, + remoteHTTPAddr: remoteAddr, + remoteUDPAddr: remoteUDP, + ready: make(chan struct{}), + closed: make(chan struct{}), + } + + client.log.Infof("Remote UDP server: %q", remoteUDP) + + go client.initHTTPClient() + + return client, nil +} + +func (c *httpClient) initHTTPClient() { + httpAuthClient, err := httpauth.NewClient(context.Background(), c.remoteHTTPAddr, c.pk, c.sk) + if err != nil { + c.log.WithError(err). + Warnf("Failed to connect to address resolver. STCPR/SUDPH services are temporarily unavailable. Retrying...") + + retryLog := logging.MustGetLogger("snet.arclient.retrier") + retry := dmsgnetutil.NewRetrier(retryLog, 1*time.Second, 10*time.Second, 0, 1) + + err := retry.Do(context.Background(), func() error { + httpAuthClient, err = httpauth.NewClient(context.Background(), c.remoteHTTPAddr, c.pk, c.sk) + return err + }) + + if err != nil { + // This should not happen as retrier is set to try indefinitely. + // If address resolver cannot be contacted indefinitely, 'c.ready' will be blocked indefinitely. + c.log.WithError(err).Fatal("Permanently failed to connect to address resolver.") + } + } + + c.log.Infof("Connected to address resolver. STCPR/SUDPH services are available.") + + c.httpClient = httpAuthClient + close(c.ready) +} + +// Get performs a new GET request. +func (c *httpClient) Get(ctx context.Context, path string) (*http.Response, error) { + <-c.ready + + addr := c.httpClient.Addr() + path + + req, err := http.NewRequest(http.MethodGet, addr, new(bytes.Buffer)) + if err != nil { + return nil, err + } + + return c.httpClient.Do(req.WithContext(ctx)) +} + +// Post performs a POST request. +func (c *httpClient) Post(ctx context.Context, path string, payload interface{}) (*http.Response, error) { + <-c.ready + + body := bytes.NewBuffer(nil) + if err := json.NewEncoder(body).Encode(payload); err != nil { + return nil, err + } + + addr := c.httpClient.Addr() + path + + req, err := http.NewRequest(http.MethodPost, addr, body) + if err != nil { + return nil, err + } + + return c.httpClient.Do(req.WithContext(ctx)) +} + +// BindRequest stores bind request values. +type BindRequest struct { + Port string `json:"port"` +} + +// LocalAddresses contains outbound port and all network addresses of visor. +type LocalAddresses struct { + Port string `json:"port"` + Addresses []string `json:"addresses"` +} + +// BindSTCPR binds client PK to IP:port on address resolver. +func (c *httpClient) BindSTCPR(ctx context.Context, port string) error { + if !c.isReady() { + c.log.Infof("BindSTCPR: Address resolver is not ready yet, waiting...") + <-c.ready + c.log.Infof("BindSTCPR: Address resolver became ready, binding") + } + + addresses, err := netutil.LocalAddresses() + if err != nil { + return err + } + + localAddresses := LocalAddresses{ + Addresses: addresses, + Port: port, + } + + resp, err := c.Post(ctx, stcprBindPath, localAddresses) + if err != nil { + return err + } + + defer func() { + if err := resp.Body.Close(); err != nil { + c.log.WithError(err).Warn("Failed to close response body") + } + }() + + if resp.StatusCode != http.StatusOK { + return fmt.Errorf("status: %d, error: %w", resp.StatusCode, extractError(resp.Body)) + } + + return nil +} + +func (c *httpClient) BindSUDPH(filter *pfilter.PacketFilter) (<-chan RemoteVisor, error) { + if !c.isReady() { + c.log.Infof("BindSUDPR: Address resolver is not ready yet, waiting...") + <-c.ready + c.log.Infof("BindSUDPR: Address resolver became ready, binding") + } + + rAddr, err := net.ResolveUDPAddr("udp", c.remoteUDPAddr) + if err != nil { + return nil, err + } + + c.sudphConn = filter.NewConn(sudphPriority, packetfilter.NewAddressFilter(rAddr)) + + _, localPort, err := net.SplitHostPort(c.sudphConn.LocalAddr().String()) + if err != nil { + return nil, err + } + + c.log.Infof("SUDPH Local port: %v", localPort) + + arConn, err := c.wrapConn(c.sudphConn) + if err != nil { + return nil, err + } + + addresses, err := netutil.LocalAddresses() + if err != nil { + return nil, err + } + + localAddresses := LocalAddresses{ + Addresses: addresses, + Port: localPort, + } + + laData, err := json.Marshal(localAddresses) + if err != nil { + return nil, err + } + + if _, err := arConn.Write(laData); err != nil { + return nil, err + } + + addrCh := c.readSUDPHMessages(arConn) + + go func() { + if err := c.keepAliveLoop(arConn); err != nil { + c.log.WithError(err).Errorf("Failed to send keep alive UDP packet to address-resolver") + } + }() + + return addrCh, nil +} + +func (c *httpClient) Resolve(ctx context.Context, tType string, pk cipher.PubKey) (VisorData, error) { + if !c.isReady() { + return VisorData{}, ErrNotReady + } + + path := fmt.Sprintf("/resolve/%s/%s", tType, pk.String()) + + resp, err := c.Get(ctx, path) + if err != nil { + return VisorData{}, err + } + + defer func() { + if err := resp.Body.Close(); err != nil { + c.log.WithError(err).Warn("Failed to close response body") + } + }() + + if resp.StatusCode == http.StatusNotFound { + return VisorData{}, ErrNoEntry + } + + if resp.StatusCode != http.StatusOK { + return VisorData{}, fmt.Errorf("status: %d, error: %w", resp.StatusCode, extractError(resp.Body)) + } + + rawBody, err := ioutil.ReadAll(resp.Body) + if err != nil { + return VisorData{}, err + } + + var resolveResp VisorData + + if err := json.Unmarshal(rawBody, &resolveResp); err != nil { + return VisorData{}, err + } + + return resolveResp, nil +} + +func (c *httpClient) Health(ctx context.Context) (int, error) { + if !c.isReady() { + return http.StatusNotFound, nil + } + + resp, err := c.Get(ctx, "/health") + if err != nil { + return 0, err + } + + defer func() { + if err := resp.Body.Close(); err != nil { + c.log.WithError(err).Warn("Failed to close response body") + } + }() + + return resp.StatusCode, nil +} + +func (c *httpClient) isReady() bool { + select { + case <-c.ready: + return true + default: + return false + } +} + +// RemoteVisor contains public key and address of remote visor. +type RemoteVisor struct { + PK cipher.PubKey + Addr string +} + +func (c *httpClient) readSUDPHMessages(reader io.Reader) <-chan RemoteVisor { + addrCh := make(chan RemoteVisor, addrChSize) + + go func(addrCh chan<- RemoteVisor) { + defer func() { + close(addrCh) + }() + + buf := make([]byte, 4096) + + for { + select { + case <-c.closed: + return + default: + n, err := reader.Read(buf) + if err != nil { + c.log.Errorf("Failed to read SUDPH message: %v", err) + return + } + + c.log.Infof("New SUDPH message: %v", string(buf[:n])) + + var remote RemoteVisor + if err := json.Unmarshal(buf[:n], &remote); err != nil { + c.log.Errorf("Failed to read unmarshal message: %v", err) + continue + } + + addrCh <- remote + } + } + }(addrCh) + + return addrCh +} + +func (c *httpClient) wrapConn(conn net.PacketConn) (*tpconn.Conn, error) { + arKCPConn, err := kcp.NewConn(c.remoteUDPAddr, nil, 0, 0, conn) + if err != nil { + return nil, err + } + + emptyAddr := dmsg.Addr{PK: cipher.PubKey{}, Port: 0} + hs := tphandshake.InitiatorHandshake(c.sk, dmsg.Addr{PK: c.pk, Port: 0}, emptyAddr) + + connConfig := tpconn.Config{ + Log: c.log, + Conn: arKCPConn, + LocalPK: c.pk, + LocalSK: c.sk, + Deadline: time.Now().Add(tphandshake.Timeout), + Handshake: hs, + Encrypt: false, + Initiator: true, + } + + arConn, err := tpconn.NewConn(connConfig) + if err != nil { + return nil, fmt.Errorf("newConn: %w", err) + } + + return arConn, nil +} + +func (c *httpClient) Close() error { + select { + case <-c.closed: + return nil // already closed + default: // close + } + + defer func() { + c.sudphConn = nil + }() + + if c.sudphConn != nil { + if err := c.sudphConn.Close(); err != nil { + c.log.WithError(err).Errorf("Failed to close SUDPH") + } + } + + close(c.closed) + + return nil +} + +// Keep NAT mapping alive. +func (c *httpClient) keepAliveLoop(w io.Writer) error { + for { + select { + case <-c.closed: + return nil + default: + if _, err := w.Write([]byte(udpKeepAliveMessage)); err != nil { + return err + } + + time.Sleep(udpKeepAliveInterval) + } + } +} + +// extractError returns the decoded error message from Body. +func extractError(r io.Reader) error { + var apiError Error + + body, err := ioutil.ReadAll(r) + if err != nil { + return err + } + + if err := json.Unmarshal(body, &apiError); err != nil { + return errors.New(string(body)) + } + + return errors.New(apiError.Error) +} diff --git a/pkg/transport/network/addrresolver/client_test.go b/pkg/transport/network/addrresolver/client_test.go new file mode 100644 index 000000000..1746c0e7a --- /dev/null +++ b/pkg/transport/network/addrresolver/client_test.go @@ -0,0 +1,101 @@ +package addrresolver + +import ( + "context" + "encoding/json" + "fmt" + "net/http" + "net/http/httptest" + "sync" + "testing" + + "github.com/go-chi/chi" + "github.com/skycoin/dmsg/cipher" + "github.com/skycoin/skycoin/src/util/logging" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/skycoin/skywire/internal/httpauth" +) + +func TestClientAuth(t *testing.T) { + testPubKey, testSecKey := cipher.GenerateKeyPair() + + wg := sync.WaitGroup{} + + headerCh := make(chan http.Header, 1) + srv := httptest.NewServer(http.HandlerFunc( + func(w http.ResponseWriter, r *http.Request) { + switch url := r.URL.String(); url { + case "/": + defer wg.Done() + headerCh <- r.Header + + case fmt.Sprintf("/security/nonces/%s", testPubKey): + if _, err := fmt.Fprintf(w, `{"edge": "%s", "next_nonce": 1}`, testPubKey); err != nil { + t.Errorf("Failed to write nonce response: %w", err) + } + + default: + t.Errorf("Don't know how to handle URL = '%s'", url) + } + }, + )) + + defer srv.Close() + log := logging.MustGetLogger("test_client_auth") + apiClient, err := NewHTTP(srv.URL, testPubKey, testSecKey, log) + require.NoError(t, err) + + c := apiClient.(*httpClient) + + wg.Add(1) + + resp, err := c.Get(context.TODO(), "/") + require.NoError(t, err) + require.NoError(t, resp.Body.Close()) + + header := <-headerCh + assert.Equal(t, testPubKey.Hex(), header.Get("SW-Public")) + assert.Equal(t, "1", header.Get("SW-Nonce")) + assert.NotEmpty(t, header.Get("SW-Sig")) // TODO: check for the right key + + wg.Wait() +} + +func TestBind(t *testing.T) { + testPubKey, testSecKey := cipher.GenerateKeyPair() + + urlCh := make(chan string, 1) + srv := httptest.NewServer(authHandler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + urlCh <- r.URL.String() + }))) + + defer srv.Close() + log := logging.MustGetLogger("test_bind") + c, err := NewHTTP(srv.URL, testPubKey, testSecKey, log) + require.NoError(t, err) + + err = c.BindSTCPR(context.TODO(), "1234") + require.NoError(t, err) + + assert.Equal(t, "/bind/stcpr", <-urlCh) +} + +func authHandler(next http.Handler) http.Handler { + log := logging.MustGetLogger("arclient_test") + testPubKey, _ := cipher.GenerateKeyPair() + r := chi.NewRouter() + + r.Handle("/security/nonces/{pk}", http.HandlerFunc( + func(w http.ResponseWriter, r *http.Request) { + if err := json.NewEncoder(w).Encode(&httpauth.NextNonceResponse{Edge: testPubKey, NextNonce: 1}); err != nil { + log.WithError(err).Error("Failed to encode nonce response") + } + }, + )) + + r.Handle("/*", next) + + return r +} diff --git a/pkg/transport/network/addrresolver/mock_api_client.go b/pkg/transport/network/addrresolver/mock_api_client.go new file mode 100644 index 000000000..dcb5d3516 --- /dev/null +++ b/pkg/transport/network/addrresolver/mock_api_client.go @@ -0,0 +1,109 @@ +// Code generated by mockery v1.0.0. DO NOT EDIT. + +package addrresolver + +import ( + context "context" + + pfilter "github.com/AudriusButkevicius/pfilter" + cipher "github.com/skycoin/dmsg/cipher" + mock "github.com/stretchr/testify/mock" +) + +// MockAPIClient is an autogenerated mock type for the APIClient type +type MockAPIClient struct { + mock.Mock +} + +// BindSTCPR provides a mock function with given fields: ctx, port +func (_m *MockAPIClient) BindSTCPR(ctx context.Context, port string) error { + ret := _m.Called(ctx, port) + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, string) error); ok { + r0 = rf(ctx, port) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// BindSUDPH provides a mock function with given fields: filter +func (_m *MockAPIClient) BindSUDPH(filter *pfilter.PacketFilter) (<-chan RemoteVisor, error) { + ret := _m.Called(filter) + + var r0 <-chan RemoteVisor + if rf, ok := ret.Get(0).(func(*pfilter.PacketFilter) <-chan RemoteVisor); ok { + r0 = rf(filter) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(<-chan RemoteVisor) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(*pfilter.PacketFilter) error); ok { + r1 = rf(filter) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// Close provides a mock function with given fields: +func (_m *MockAPIClient) Close() error { + ret := _m.Called() + + var r0 error + if rf, ok := ret.Get(0).(func() error); ok { + r0 = rf() + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// Health provides a mock function with given fields: ctx +func (_m *MockAPIClient) Health(ctx context.Context) (int, error) { + ret := _m.Called(ctx) + + var r0 int + if rf, ok := ret.Get(0).(func(context.Context) int); ok { + r0 = rf(ctx) + } else { + r0 = ret.Get(0).(int) + } + + var r1 error + if rf, ok := ret.Get(1).(func(context.Context) error); ok { + r1 = rf(ctx) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// Resolve provides a mock function with given fields: ctx, tType, pk +func (_m *MockAPIClient) Resolve(ctx context.Context, tType string, pk cipher.PubKey) (VisorData, error) { + ret := _m.Called(ctx, tType, pk) + + var r0 VisorData + if rf, ok := ret.Get(0).(func(context.Context, string, cipher.PubKey) VisorData); ok { + r0 = rf(ctx, tType, pk) + } else { + r0 = ret.Get(0).(VisorData) + } + + var r1 error + if rf, ok := ret.Get(1).(func(context.Context, string, cipher.PubKey) error); ok { + r1 = rf(ctx, tType, pk) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} diff --git a/pkg/transport/network/client.go b/pkg/transport/network/client.go index bd4b7dbc9..ea9da846d 100644 --- a/pkg/transport/network/client.go +++ b/pkg/transport/network/client.go @@ -14,9 +14,9 @@ import ( "github.com/skycoin/skycoin/src/util/logging" "github.com/skycoin/skywire/pkg/app/appevent" - "github.com/skycoin/skywire/pkg/snet/arclient" "github.com/skycoin/skywire/pkg/snet/directtp/porter" "github.com/skycoin/skywire/pkg/snet/directtp/tphandshake" + "github.com/skycoin/skywire/pkg/transport/network/addrresolver" "github.com/skycoin/skywire/pkg/transport/network/stcp" ) @@ -53,7 +53,7 @@ type ClientFactory struct { SK cipher.SecKey ListenAddr string PKTable stcp.PKTable - ARClient arclient.APIClient + ARClient addrresolver.APIClient EB *appevent.Broadcaster } @@ -304,7 +304,7 @@ func (c *genericClient) Type() Type { // to resolve addresses of remote visors type resolvedClient struct { *genericClient - ar arclient.APIClient + ar addrresolver.APIClient } type dialFunc func(ctx context.Context, addr string) (net.Conn, error) diff --git a/pkg/transport/network/sudph.go b/pkg/transport/network/sudph.go index c11b2e73a..63a73e75e 100644 --- a/pkg/transport/network/sudph.go +++ b/pkg/transport/network/sudph.go @@ -12,7 +12,7 @@ import ( "github.com/xtaci/kcp-go" "github.com/skycoin/skywire/internal/packetfilter" - "github.com/skycoin/skywire/pkg/snet/arclient" + "github.com/skycoin/skywire/pkg/transport/network/addrresolver" ) const ( @@ -71,7 +71,7 @@ func (c *sudphClient) listen() (net.Listener, error) { } // todo: name -func (c *sudphClient) PICKNAMEFORME(conn net.PacketConn, addrCh <-chan arclient.RemoteVisor) { +func (c *sudphClient) PICKNAMEFORME(conn net.PacketConn, addrCh <-chan addrresolver.RemoteVisor) { for addr := range addrCh { udpAddr, err := net.ResolveUDPAddr("udp", addr.Addr) if err != nil { diff --git a/pkg/visor/init.go b/pkg/visor/init.go index 96578a696..f5cc6f418 100644 --- a/pkg/visor/init.go +++ b/pkg/visor/init.go @@ -28,12 +28,12 @@ import ( "github.com/skycoin/skywire/pkg/servicedisc" "github.com/skycoin/skywire/pkg/setup/setupclient" "github.com/skycoin/skywire/pkg/skyenv" - "github.com/skycoin/skywire/pkg/snet/arclient" "github.com/skycoin/skywire/pkg/snet/directtp/pktable" "github.com/skycoin/skywire/pkg/snet/directtp/tptypes" "github.com/skycoin/skywire/pkg/snet/dmsgc" "github.com/skycoin/skywire/pkg/transport" "github.com/skycoin/skywire/pkg/transport/network" + "github.com/skycoin/skywire/pkg/transport/network/addrresolver" ts "github.com/skycoin/skywire/pkg/transport/setup" "github.com/skycoin/skywire/pkg/transport/tpdclient" "github.com/skycoin/skywire/pkg/util/netutil" @@ -151,7 +151,7 @@ func initEventBroadcaster(ctx context.Context, v *Visor, log *logging.Logger) er func initAddressResolver(ctx context.Context, v *Visor, log *logging.Logger) error { conf := v.conf.Transport - arClient, err := arclient.NewHTTP(conf.AddressResolver, v.conf.PK, v.conf.SK, log) + arClient, err := addrresolver.NewHTTP(conf.AddressResolver, v.conf.PK, v.conf.SK, log) if err != nil { err := fmt.Errorf("failed to create address resolver client: %w", err) return err @@ -625,7 +625,7 @@ func initPublicVisor(_ context.Context, v *Visor, log *logging.Logger) error { } // todo: consider moving this to transport into some helper function - stcpr, ok := v.tpM.STcpr() + stcpr, ok := v.tpM.Stcpr() if !ok { return nil } diff --git a/pkg/visor/rpc_test.go b/pkg/visor/rpc_test.go index 178506d47..e064b4ed4 100644 --- a/pkg/visor/rpc_test.go +++ b/pkg/visor/rpc_test.go @@ -15,8 +15,8 @@ import ( "github.com/skycoin/skywire/internal/testhelpers" "github.com/skycoin/skywire/internal/utclient" "github.com/skycoin/skywire/pkg/routefinder/rfclient" - "github.com/skycoin/skywire/pkg/snet/arclient" "github.com/skycoin/skywire/pkg/transport" + "github.com/skycoin/skywire/pkg/transport/network/addrresolver" "github.com/skycoin/skywire/pkg/visor/visorconfig" ) @@ -40,7 +40,7 @@ func TestHealth(t *testing.T) { utClient := &utclient.MockAPIClient{} utClient.On("Health", mock.Anything).Return(http.StatusOK, nil) - arClient := &arclient.MockAPIClient{} + arClient := &addrresolver.MockAPIClient{} arClient.On("Health", mock.Anything).Return(http.StatusOK, nil) rfClient := &rfclient.MockClient{} diff --git a/pkg/visor/visor.go b/pkg/visor/visor.go index d208151e1..9486028bc 100644 --- a/pkg/visor/visor.go +++ b/pkg/visor/visor.go @@ -19,8 +19,8 @@ import ( "github.com/skycoin/skywire/pkg/restart" "github.com/skycoin/skywire/pkg/routefinder/rfclient" "github.com/skycoin/skywire/pkg/router" - "github.com/skycoin/skywire/pkg/snet/arclient" "github.com/skycoin/skywire/pkg/transport" + "github.com/skycoin/skywire/pkg/transport/network/addrresolver" "github.com/skycoin/skywire/pkg/util/updater" "github.com/skycoin/skywire/pkg/visor/logstore" "github.com/skycoin/skywire/pkg/visor/visorconfig" @@ -58,7 +58,7 @@ type Visor struct { dmsgC *dmsg.Client tpM *transport.Manager - arClient arclient.APIClient + arClient addrresolver.APIClient router router.Router rfClient rfclient.Client @@ -219,6 +219,6 @@ func (v *Visor) uptimeTrackerClient() utclient.APIClient { } // addressResolverClient is a convenience function to obtain uptime address resovler client. -func (v *Visor) addressResolverClient() arclient.APIClient { +func (v *Visor) addressResolverClient() addrresolver.APIClient { return v.arClient } From 677233416061ced8cb20080677f12e515c6619a8 Mon Sep 17 00:00:00 2001 From: Never M Date: Tue, 29 Jun 2021 13:05:48 +0300 Subject: [PATCH 31/55] Remove address resolver from snet --- pkg/snet/arclient/client.go | 472 ----------------------- pkg/snet/arclient/client_test.go | 101 ----- pkg/snet/arclient/mock_api_client.go | 109 ------ pkg/snet/directtp/client.go | 536 +-------------------------- pkg/snet/network.go | 210 +---------- pkg/snet/snettest/env.go | 142 +------ 6 files changed, 8 insertions(+), 1562 deletions(-) delete mode 100644 pkg/snet/arclient/client.go delete mode 100644 pkg/snet/arclient/client_test.go delete mode 100644 pkg/snet/arclient/mock_api_client.go diff --git a/pkg/snet/arclient/client.go b/pkg/snet/arclient/client.go deleted file mode 100644 index 360d4467d..000000000 --- a/pkg/snet/arclient/client.go +++ /dev/null @@ -1,472 +0,0 @@ -// Package arclient implements address resolver client -package arclient - -import ( - "bytes" - "context" - "encoding/json" - "errors" - "fmt" - "io" - "io/ioutil" - "net" - "net/http" - "net/url" - "time" - - "github.com/AudriusButkevicius/pfilter" - "github.com/skycoin/dmsg" - "github.com/skycoin/dmsg/cipher" - dmsgnetutil "github.com/skycoin/dmsg/netutil" - "github.com/skycoin/skycoin/src/util/logging" - "github.com/xtaci/kcp-go" - - "github.com/skycoin/skywire/internal/httpauth" - "github.com/skycoin/skywire/internal/netutil" - "github.com/skycoin/skywire/internal/packetfilter" - "github.com/skycoin/skywire/pkg/snet/directtp/tpconn" - "github.com/skycoin/skywire/pkg/snet/directtp/tphandshake" -) - -const ( - // sudphPriority is used to set an order how connection filters apply. - sudphPriority = 1 - stcprBindPath = "/bind/stcpr" - addrChSize = 1024 - udpKeepAliveInterval = 10 * time.Second - udpKeepAliveMessage = "keepalive" - defaultUDPPort = "30178" -) - -var ( - // ErrNoEntry means that there exists no entry for this PK. - ErrNoEntry = errors.New("no entry for this PK") - // ErrNotReady is returned when address resolver is not ready. - ErrNotReady = errors.New("address resolver is not ready") -) - -// Error is the object returned to the client when there's an error. -type Error struct { - Error string `json:"error"` -} - -//go:generate mockery -name APIClient -case underscore -inpkg - -// APIClient implements address resolver API client. -type APIClient interface { - io.Closer - BindSTCPR(ctx context.Context, port string) error - BindSUDPH(filter *pfilter.PacketFilter) (<-chan RemoteVisor, error) - Resolve(ctx context.Context, tType string, pk cipher.PubKey) (VisorData, error) - Health(ctx context.Context) (int, error) -} - -// VisorData stores visor data. -type VisorData struct { - RemoteAddr string `json:"remote_addr"` - IsLocal bool `json:"is_local,omitempty"` - LocalAddresses -} - -// httpClient implements APIClient for address resolver API. -type httpClient struct { - log *logging.Logger - httpClient *httpauth.Client - pk cipher.PubKey - sk cipher.SecKey - remoteHTTPAddr string - remoteUDPAddr string - sudphConn net.PacketConn - ready chan struct{} - closed chan struct{} -} - -// NewHTTP creates a new client setting a public key to the client to be used for auth. -// When keys are set, the client will sign request before submitting. -// The signature information is transmitted in the header using: -// * SW-Public: The specified public key. -// * SW-Nonce: The nonce for that public key. -// * SW-Sig: The signature of the payload + the nonce. -func NewHTTP(remoteAddr string, pk cipher.PubKey, sk cipher.SecKey, log *logging.Logger) (APIClient, error) { - remoteURL, err := url.Parse(remoteAddr) - if err != nil { - return nil, fmt.Errorf("parse URL: %w", err) - } - - remoteUDP := remoteURL.Host - if _, _, err := net.SplitHostPort(remoteUDP); err != nil { - remoteUDP = net.JoinHostPort(remoteUDP, defaultUDPPort) - } - - client := &httpClient{ - log: log, - pk: pk, - sk: sk, - remoteHTTPAddr: remoteAddr, - remoteUDPAddr: remoteUDP, - ready: make(chan struct{}), - closed: make(chan struct{}), - } - - client.log.Infof("Remote UDP server: %q", remoteUDP) - - go client.initHTTPClient() - - return client, nil -} - -func (c *httpClient) initHTTPClient() { - httpAuthClient, err := httpauth.NewClient(context.Background(), c.remoteHTTPAddr, c.pk, c.sk) - if err != nil { - c.log.WithError(err). - Warnf("Failed to connect to address resolver. STCPR/SUDPH services are temporarily unavailable. Retrying...") - - retryLog := logging.MustGetLogger("snet.arclient.retrier") - retry := dmsgnetutil.NewRetrier(retryLog, 1*time.Second, 10*time.Second, 0, 1) - - err := retry.Do(context.Background(), func() error { - httpAuthClient, err = httpauth.NewClient(context.Background(), c.remoteHTTPAddr, c.pk, c.sk) - return err - }) - - if err != nil { - // This should not happen as retrier is set to try indefinitely. - // If address resolver cannot be contacted indefinitely, 'c.ready' will be blocked indefinitely. - c.log.WithError(err).Fatal("Permanently failed to connect to address resolver.") - } - } - - c.log.Infof("Connected to address resolver. STCPR/SUDPH services are available.") - - c.httpClient = httpAuthClient - close(c.ready) -} - -// Get performs a new GET request. -func (c *httpClient) Get(ctx context.Context, path string) (*http.Response, error) { - <-c.ready - - addr := c.httpClient.Addr() + path - - req, err := http.NewRequest(http.MethodGet, addr, new(bytes.Buffer)) - if err != nil { - return nil, err - } - - return c.httpClient.Do(req.WithContext(ctx)) -} - -// Post performs a POST request. -func (c *httpClient) Post(ctx context.Context, path string, payload interface{}) (*http.Response, error) { - <-c.ready - - body := bytes.NewBuffer(nil) - if err := json.NewEncoder(body).Encode(payload); err != nil { - return nil, err - } - - addr := c.httpClient.Addr() + path - - req, err := http.NewRequest(http.MethodPost, addr, body) - if err != nil { - return nil, err - } - - return c.httpClient.Do(req.WithContext(ctx)) -} - -// BindRequest stores bind request values. -type BindRequest struct { - Port string `json:"port"` -} - -// LocalAddresses contains outbound port and all network addresses of visor. -type LocalAddresses struct { - Port string `json:"port"` - Addresses []string `json:"addresses"` -} - -// BindSTCPR binds client PK to IP:port on address resolver. -func (c *httpClient) BindSTCPR(ctx context.Context, port string) error { - if !c.isReady() { - c.log.Infof("BindSTCPR: Address resolver is not ready yet, waiting...") - <-c.ready - c.log.Infof("BindSTCPR: Address resolver became ready, binding") - } - - addresses, err := netutil.LocalAddresses() - if err != nil { - return err - } - - localAddresses := LocalAddresses{ - Addresses: addresses, - Port: port, - } - - resp, err := c.Post(ctx, stcprBindPath, localAddresses) - if err != nil { - return err - } - - defer func() { - if err := resp.Body.Close(); err != nil { - c.log.WithError(err).Warn("Failed to close response body") - } - }() - - if resp.StatusCode != http.StatusOK { - return fmt.Errorf("status: %d, error: %w", resp.StatusCode, extractError(resp.Body)) - } - - return nil -} - -func (c *httpClient) BindSUDPH(filter *pfilter.PacketFilter) (<-chan RemoteVisor, error) { - if !c.isReady() { - c.log.Infof("BindSUDPR: Address resolver is not ready yet, waiting...") - <-c.ready - c.log.Infof("BindSUDPR: Address resolver became ready, binding") - } - - rAddr, err := net.ResolveUDPAddr("udp", c.remoteUDPAddr) - if err != nil { - return nil, err - } - - c.sudphConn = filter.NewConn(sudphPriority, packetfilter.NewAddressFilter(rAddr)) - - _, localPort, err := net.SplitHostPort(c.sudphConn.LocalAddr().String()) - if err != nil { - return nil, err - } - - c.log.Infof("SUDPH Local port: %v", localPort) - - arConn, err := c.wrapConn(c.sudphConn) - if err != nil { - return nil, err - } - - addresses, err := netutil.LocalAddresses() - if err != nil { - return nil, err - } - - localAddresses := LocalAddresses{ - Addresses: addresses, - Port: localPort, - } - - laData, err := json.Marshal(localAddresses) - if err != nil { - return nil, err - } - - if _, err := arConn.Write(laData); err != nil { - return nil, err - } - - addrCh := c.readSUDPHMessages(arConn) - - go func() { - if err := c.keepAliveLoop(arConn); err != nil { - c.log.WithError(err).Errorf("Failed to send keep alive UDP packet to address-resolver") - } - }() - - return addrCh, nil -} - -func (c *httpClient) Resolve(ctx context.Context, tType string, pk cipher.PubKey) (VisorData, error) { - if !c.isReady() { - return VisorData{}, ErrNotReady - } - - path := fmt.Sprintf("/resolve/%s/%s", tType, pk.String()) - - resp, err := c.Get(ctx, path) - if err != nil { - return VisorData{}, err - } - - defer func() { - if err := resp.Body.Close(); err != nil { - c.log.WithError(err).Warn("Failed to close response body") - } - }() - - if resp.StatusCode == http.StatusNotFound { - return VisorData{}, ErrNoEntry - } - - if resp.StatusCode != http.StatusOK { - return VisorData{}, fmt.Errorf("status: %d, error: %w", resp.StatusCode, extractError(resp.Body)) - } - - rawBody, err := ioutil.ReadAll(resp.Body) - if err != nil { - return VisorData{}, err - } - - var resolveResp VisorData - - if err := json.Unmarshal(rawBody, &resolveResp); err != nil { - return VisorData{}, err - } - - return resolveResp, nil -} - -func (c *httpClient) Health(ctx context.Context) (int, error) { - if !c.isReady() { - return http.StatusNotFound, nil - } - - resp, err := c.Get(ctx, "/health") - if err != nil { - return 0, err - } - - defer func() { - if err := resp.Body.Close(); err != nil { - c.log.WithError(err).Warn("Failed to close response body") - } - }() - - return resp.StatusCode, nil -} - -func (c *httpClient) isReady() bool { - select { - case <-c.ready: - return true - default: - return false - } -} - -// RemoteVisor contains public key and address of remote visor. -type RemoteVisor struct { - PK cipher.PubKey - Addr string -} - -func (c *httpClient) readSUDPHMessages(reader io.Reader) <-chan RemoteVisor { - addrCh := make(chan RemoteVisor, addrChSize) - - go func(addrCh chan<- RemoteVisor) { - defer func() { - close(addrCh) - }() - - buf := make([]byte, 4096) - - for { - select { - case <-c.closed: - return - default: - n, err := reader.Read(buf) - if err != nil { - c.log.Errorf("Failed to read SUDPH message: %v", err) - return - } - - c.log.Infof("New SUDPH message: %v", string(buf[:n])) - - var remote RemoteVisor - if err := json.Unmarshal(buf[:n], &remote); err != nil { - c.log.Errorf("Failed to read unmarshal message: %v", err) - continue - } - - addrCh <- remote - } - } - }(addrCh) - - return addrCh -} - -func (c *httpClient) wrapConn(conn net.PacketConn) (*tpconn.Conn, error) { - arKCPConn, err := kcp.NewConn(c.remoteUDPAddr, nil, 0, 0, conn) - if err != nil { - return nil, err - } - - emptyAddr := dmsg.Addr{PK: cipher.PubKey{}, Port: 0} - hs := tphandshake.InitiatorHandshake(c.sk, dmsg.Addr{PK: c.pk, Port: 0}, emptyAddr) - - connConfig := tpconn.Config{ - Log: c.log, - Conn: arKCPConn, - LocalPK: c.pk, - LocalSK: c.sk, - Deadline: time.Now().Add(tphandshake.Timeout), - Handshake: hs, - Encrypt: false, - Initiator: true, - } - - arConn, err := tpconn.NewConn(connConfig) - if err != nil { - return nil, fmt.Errorf("newConn: %w", err) - } - - return arConn, nil -} - -func (c *httpClient) Close() error { - select { - case <-c.closed: - return nil // already closed - default: // close - } - - defer func() { - c.sudphConn = nil - }() - - if c.sudphConn != nil { - if err := c.sudphConn.Close(); err != nil { - c.log.WithError(err).Errorf("Failed to close SUDPH") - } - } - - close(c.closed) - - return nil -} - -// Keep NAT mapping alive. -func (c *httpClient) keepAliveLoop(w io.Writer) error { - for { - select { - case <-c.closed: - return nil - default: - if _, err := w.Write([]byte(udpKeepAliveMessage)); err != nil { - return err - } - - time.Sleep(udpKeepAliveInterval) - } - } -} - -// extractError returns the decoded error message from Body. -func extractError(r io.Reader) error { - var apiError Error - - body, err := ioutil.ReadAll(r) - if err != nil { - return err - } - - if err := json.Unmarshal(body, &apiError); err != nil { - return errors.New(string(body)) - } - - return errors.New(apiError.Error) -} diff --git a/pkg/snet/arclient/client_test.go b/pkg/snet/arclient/client_test.go deleted file mode 100644 index 304f33ddb..000000000 --- a/pkg/snet/arclient/client_test.go +++ /dev/null @@ -1,101 +0,0 @@ -package arclient - -import ( - "context" - "encoding/json" - "fmt" - "net/http" - "net/http/httptest" - "sync" - "testing" - - "github.com/go-chi/chi" - "github.com/skycoin/dmsg/cipher" - "github.com/skycoin/skycoin/src/util/logging" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/skycoin/skywire/internal/httpauth" -) - -func TestClientAuth(t *testing.T) { - testPubKey, testSecKey := cipher.GenerateKeyPair() - - wg := sync.WaitGroup{} - - headerCh := make(chan http.Header, 1) - srv := httptest.NewServer(http.HandlerFunc( - func(w http.ResponseWriter, r *http.Request) { - switch url := r.URL.String(); url { - case "/": - defer wg.Done() - headerCh <- r.Header - - case fmt.Sprintf("/security/nonces/%s", testPubKey): - if _, err := fmt.Fprintf(w, `{"edge": "%s", "next_nonce": 1}`, testPubKey); err != nil { - t.Errorf("Failed to write nonce response: %w", err) - } - - default: - t.Errorf("Don't know how to handle URL = '%s'", url) - } - }, - )) - - defer srv.Close() - log := logging.MustGetLogger("test_client_auth") - apiClient, err := NewHTTP(srv.URL, testPubKey, testSecKey, log) - require.NoError(t, err) - - c := apiClient.(*httpClient) - - wg.Add(1) - - resp, err := c.Get(context.TODO(), "/") - require.NoError(t, err) - require.NoError(t, resp.Body.Close()) - - header := <-headerCh - assert.Equal(t, testPubKey.Hex(), header.Get("SW-Public")) - assert.Equal(t, "1", header.Get("SW-Nonce")) - assert.NotEmpty(t, header.Get("SW-Sig")) // TODO: check for the right key - - wg.Wait() -} - -func TestBind(t *testing.T) { - testPubKey, testSecKey := cipher.GenerateKeyPair() - - urlCh := make(chan string, 1) - srv := httptest.NewServer(authHandler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - urlCh <- r.URL.String() - }))) - - defer srv.Close() - log := logging.MustGetLogger("test_bind") - c, err := NewHTTP(srv.URL, testPubKey, testSecKey, log) - require.NoError(t, err) - - err = c.BindSTCPR(context.TODO(), "1234") - require.NoError(t, err) - - assert.Equal(t, "/bind/stcpr", <-urlCh) -} - -func authHandler(next http.Handler) http.Handler { - log := logging.MustGetLogger("arclient_test") - testPubKey, _ := cipher.GenerateKeyPair() - r := chi.NewRouter() - - r.Handle("/security/nonces/{pk}", http.HandlerFunc( - func(w http.ResponseWriter, r *http.Request) { - if err := json.NewEncoder(w).Encode(&httpauth.NextNonceResponse{Edge: testPubKey, NextNonce: 1}); err != nil { - log.WithError(err).Error("Failed to encode nonce response") - } - }, - )) - - r.Handle("/*", next) - - return r -} diff --git a/pkg/snet/arclient/mock_api_client.go b/pkg/snet/arclient/mock_api_client.go deleted file mode 100644 index 5ee1f8dfc..000000000 --- a/pkg/snet/arclient/mock_api_client.go +++ /dev/null @@ -1,109 +0,0 @@ -// Code generated by mockery v1.0.0. DO NOT EDIT. - -package arclient - -import ( - context "context" - - pfilter "github.com/AudriusButkevicius/pfilter" - cipher "github.com/skycoin/dmsg/cipher" - mock "github.com/stretchr/testify/mock" -) - -// MockAPIClient is an autogenerated mock type for the APIClient type -type MockAPIClient struct { - mock.Mock -} - -// BindSTCPR provides a mock function with given fields: ctx, port -func (_m *MockAPIClient) BindSTCPR(ctx context.Context, port string) error { - ret := _m.Called(ctx, port) - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context, string) error); ok { - r0 = rf(ctx, port) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// BindSUDPH provides a mock function with given fields: filter -func (_m *MockAPIClient) BindSUDPH(filter *pfilter.PacketFilter) (<-chan RemoteVisor, error) { - ret := _m.Called(filter) - - var r0 <-chan RemoteVisor - if rf, ok := ret.Get(0).(func(*pfilter.PacketFilter) <-chan RemoteVisor); ok { - r0 = rf(filter) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(<-chan RemoteVisor) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(*pfilter.PacketFilter) error); ok { - r1 = rf(filter) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// Close provides a mock function with given fields: -func (_m *MockAPIClient) Close() error { - ret := _m.Called() - - var r0 error - if rf, ok := ret.Get(0).(func() error); ok { - r0 = rf() - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// Health provides a mock function with given fields: ctx -func (_m *MockAPIClient) Health(ctx context.Context) (int, error) { - ret := _m.Called(ctx) - - var r0 int - if rf, ok := ret.Get(0).(func(context.Context) int); ok { - r0 = rf(ctx) - } else { - r0 = ret.Get(0).(int) - } - - var r1 error - if rf, ok := ret.Get(1).(func(context.Context) error); ok { - r1 = rf(ctx) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// Resolve provides a mock function with given fields: ctx, tType, pk -func (_m *MockAPIClient) Resolve(ctx context.Context, tType string, pk cipher.PubKey) (VisorData, error) { - ret := _m.Called(ctx, tType, pk) - - var r0 VisorData - if rf, ok := ret.Get(0).(func(context.Context, string, cipher.PubKey) VisorData); ok { - r0 = rf(ctx, tType, pk) - } else { - r0 = ret.Get(0).(VisorData) - } - - var r1 error - if rf, ok := ret.Get(1).(func(context.Context, string, cipher.PubKey) error); ok { - r1 = rf(ctx, tType, pk) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} diff --git a/pkg/snet/directtp/client.go b/pkg/snet/directtp/client.go index be99aa86a..e249757fc 100644 --- a/pkg/snet/directtp/client.go +++ b/pkg/snet/directtp/client.go @@ -1,537 +1,3 @@ package directtp -import ( - "context" - "errors" - "fmt" - "io" - "net" - "strings" - "sync" - "time" - - "github.com/AudriusButkevicius/pfilter" - "github.com/skycoin/dmsg" - "github.com/skycoin/dmsg/cipher" - "github.com/skycoin/skycoin/src/util/logging" - "github.com/xtaci/kcp-go" - - "github.com/skycoin/skywire/internal/packetfilter" - "github.com/skycoin/skywire/pkg/snet/arclient" - "github.com/skycoin/skywire/pkg/snet/directtp/pktable" - "github.com/skycoin/skywire/pkg/snet/directtp/porter" - "github.com/skycoin/skywire/pkg/snet/directtp/tpconn" - "github.com/skycoin/skywire/pkg/snet/directtp/tphandshake" - "github.com/skycoin/skywire/pkg/snet/directtp/tplistener" - "github.com/skycoin/skywire/pkg/snet/directtp/tptypes" - "github.com/skycoin/skywire/pkg/util/netutil" -) - -const ( - // holePunchMessage is sent in a dummy UDP packet that is sent by both parties to establish UDP hole punching. - holePunchMessage = "holepunch" - dialTimeout = 30 * time.Second - // dialConnPriority and visorsConnPriority are used to set an order how connection filters apply. - dialConnPriority = 2 - visorsConnPriority = 3 -) - -var ( - // ErrUnknownTransportType is returned when transport type is unknown. - ErrUnknownTransportType = errors.New("unknown transport type") - - // ErrTimeout indicates a timeout. - ErrTimeout = errors.New("timeout") - - // ErrAlreadyListening is returned when transport is already listening. - ErrAlreadyListening = errors.New("already listening") - - // ErrNotListening is returned when transport is not listening. - ErrNotListening = errors.New("not listening") - - // ErrPortOccupied is returned when port is occupied. - ErrPortOccupied = errors.New("port is already occupied") -) - -// Client is the central control for incoming and outgoing 'Conn's. -type Client interface { - Dial(ctx context.Context, rPK cipher.PubKey, rPort uint16) (*tpconn.Conn, error) - Listen(lPort uint16) (*tplistener.Listener, error) - LocalAddr() (net.Addr, error) - Serve() error - Close() error - Type() string -} - -// Config configures Client. -type Config struct { - Type string - PK cipher.PubKey - SK cipher.SecKey - LocalAddr string - Table pktable.PKTable - AddressResolver arclient.APIClient - BeforeDialCallback BeforeDialCallback -} - -// BeforeDialCallback is triggered before client dials. -// If a non-nil error is returned, the dial is instantly terminated. -type BeforeDialCallback func(network, addr string) (err error) - -type client struct { - conf Config - mu sync.Mutex - done chan struct{} - once sync.Once - log *logging.Logger - porter *porter.Porter - listener net.Listener - listening chan struct{} - listeners map[uint16]*tplistener.Listener // key: lPort - sudphPacketFilter *pfilter.PacketFilter - sudphListener net.PacketConn - sudphVisorsConn net.PacketConn - beforeDialCallback BeforeDialCallback -} - -// NewClient creates a net Client. -func NewClient(conf Config, masterLogger *logging.MasterLogger) Client { - return &client{ - conf: conf, - log: masterLogger.PackageLogger(conf.Type), - porter: porter.New(porter.MinEphemeral), - listeners: make(map[uint16]*tplistener.Listener), - done: make(chan struct{}), - listening: make(chan struct{}), - beforeDialCallback: conf.BeforeDialCallback, - } -} - -// Serve serves the listening portion of the client. -func (c *client) Serve() error { - switch c.conf.Type { - case tptypes.STCP, tptypes.STCPR: - if c.listener != nil { - return ErrAlreadyListening - } - case tptypes.SUDPH: - if c.sudphListener != nil { - return ErrAlreadyListening - } - } - - go func() { - l, err := c.listen(c.conf.LocalAddr) - if err != nil { - c.log.Errorf("Failed to listen on %q: %v", c.conf.LocalAddr, err) - return - } - - c.listener = l - close(c.listening) - - if c.conf.Type == tptypes.STCPR { - localAddr := c.listener.Addr().String() - _, port, err := net.SplitHostPort(localAddr) - if err != nil { - c.log.Errorf("Failed to extract port from addr %v: %v", err) - return - } - hasPublic, err := netutil.HasPublicIP() - if err != nil { - c.log.Errorf("Failed to check for public IP: %v", err) - } - if !hasPublic { - c.log.Infof("Not binding STCPR: no public IP address found") - return - } - if err := c.conf.AddressResolver.BindSTCPR(context.Background(), port); err != nil { - c.log.Errorf("Failed to bind STCPR: %v", err) - return - } - } - - c.log.Infof("listening on addr: %v", c.listener.Addr()) - - for { - if err := c.acceptConn(); err != nil { - if strings.Contains(err.Error(), io.EOF.Error()) { - continue // likely it's a dummy connection from service discovery - } - - c.log.Warnf("failed to accept incoming connection: %v", err) - - if !tphandshake.IsHandshakeError(err) { - c.log.Warnf("stopped serving") - return - } - } - } - }() - - return nil -} - -func (c *client) LocalAddr() (net.Addr, error) { - <-c.listening - - switch c.conf.Type { - case tptypes.STCP, tptypes.STCPR: - if c.listener == nil { - return nil, ErrNotListening - } - - return c.listener.Addr(), nil - case tptypes.SUDPH: - if c.sudphListener == nil { - return nil, ErrNotListening - } - - return c.listener.Addr(), nil - } - - return nil, ErrUnknownTransportType -} - -func (c *client) acceptConn() error { - if c.isClosed() { - return io.ErrClosedPipe - } - - conn, err := c.listener.Accept() - if err != nil { - return err - } - - remoteAddr := conn.RemoteAddr() - - c.log.Infof("Accepted connection from %v", remoteAddr) - - var lis *tplistener.Listener - - hs := tphandshake.ResponderHandshake(func(f2 tphandshake.Frame2) error { - c.mu.Lock() - defer c.mu.Unlock() - - var ok bool - if lis, ok = c.listeners[f2.DstAddr.Port]; !ok { - return errors.New("not listening on given port") - } - - return nil - }) - - connConfig := tpconn.Config{ - Log: c.log, - Conn: conn, - LocalPK: c.conf.PK, - LocalSK: c.conf.SK, - Deadline: time.Now().Add(tphandshake.Timeout), - Handshake: hs, - FreePort: nil, - Encrypt: true, - Initiator: false, - } - - wrappedConn, err := tpconn.NewConn(connConfig) - if err != nil { - return err - } - - return lis.Introduce(wrappedConn) -} - -// Dial dials a new Conn to specified remote public key and port. -func (c *client) Dial(ctx context.Context, rPK cipher.PubKey, rPort uint16) (*tpconn.Conn, error) { - if c.isClosed() { - return nil, io.ErrClosedPipe - } - - c.log.Infof("Dialing PK %v", rPK) - - var visorConn net.Conn - - switch c.conf.Type { - case tptypes.STCP: - addr, ok := c.conf.Table.Addr(rPK) - if !ok { - return nil, fmt.Errorf("pk table: entry of %s does not exist", rPK) - } - - conn, err := c.dial(addr) - if err != nil { - return nil, err - } - - visorConn = conn - - case tptypes.STCPR, tptypes.SUDPH: - visorData, err := c.conf.AddressResolver.Resolve(ctx, c.Type(), rPK) - if err != nil { - return nil, fmt.Errorf("resolve PK: %w", err) - } - - c.log.Infof("Resolved PK %v to visor data %v", rPK, visorData) - - conn, err := c.dialVisor(ctx, visorData) - if err != nil { - return nil, err - } - - visorConn = conn - - default: - return nil, ErrUnknownTransportType - } - - c.log.Infof("Dialed %v:%v@%v", rPK, rPort, visorConn.RemoteAddr()) - - lPort, freePort, err := c.porter.ReserveEphemeral(ctx) - if err != nil { - return nil, err - } - - hs := tphandshake.InitiatorHandshake(c.conf.SK, dmsg.Addr{PK: c.conf.PK, Port: lPort}, dmsg.Addr{PK: rPK, Port: rPort}) - - connConfig := tpconn.Config{ - Log: c.log, - Conn: visorConn, - LocalPK: c.conf.PK, - LocalSK: c.conf.SK, - Deadline: time.Now().Add(tphandshake.Timeout), - Handshake: hs, - FreePort: freePort, - Encrypt: true, - Initiator: true, - } - - return tpconn.NewConn(connConfig) -} - -func (c *client) dial(addr string) (net.Conn, error) { - switch c.conf.Type { - case tptypes.STCP, tptypes.STCPR: - return net.Dial("tcp", addr) - - case tptypes.SUDPH: - return c.dialUDPWithTimeout(addr) - - default: - return nil, ErrUnknownTransportType - } -} - -func (c *client) dialContext(ctx context.Context, addr string) (net.Conn, error) { - dialer := net.Dialer{} - switch c.conf.Type { - case tptypes.STCP, tptypes.STCPR: - return dialer.DialContext(ctx, "tcp", addr) - - case tptypes.SUDPH: - return c.dialUDPWithTimeout(addr) - - default: - return nil, ErrUnknownTransportType - } -} - -func (c *client) listen(addr string) (net.Listener, error) { - switch c.conf.Type { - case tptypes.STCP, tptypes.STCPR: - return net.Listen("tcp", addr) - - case tptypes.SUDPH: - packetListener, err := net.ListenPacket("udp", "") - if err != nil { - return nil, err - } - - c.sudphListener = packetListener - - c.sudphPacketFilter = pfilter.NewPacketFilter(packetListener) - c.sudphVisorsConn = c.sudphPacketFilter.NewConn(visorsConnPriority, nil) - - c.sudphPacketFilter.Start() - - addrCh, err := c.conf.AddressResolver.BindSUDPH(c.sudphPacketFilter) - if err != nil { - return nil, err - } - - go func() { - for addr := range addrCh { - udpAddr, err := net.ResolveUDPAddr("udp", addr.Addr) - if err != nil { - c.log.WithError(err).Errorf("Failed to resolve UDP address %q", addr) - continue - } - - c.log.Infof("Sending hole punch packet to %v", addr) - - if _, err := c.sudphVisorsConn.WriteTo([]byte(holePunchMessage), udpAddr); err != nil { - c.log.WithError(err).Errorf("Failed to send hole punch packet to %v", udpAddr) - continue - } - - c.log.Infof("Sent hole punch packet to %v", addr) - } - }() - - return kcp.ServeConn(nil, 0, 0, c.sudphVisorsConn) - - default: - return nil, ErrUnknownTransportType - } -} - -func (c *client) dialUDP(remoteAddr string) (net.Conn, error) { - rAddr, err := net.ResolveUDPAddr("udp", remoteAddr) - if err != nil { - return nil, fmt.Errorf("net.ResolveUDPAddr (remote): %w", err) - } - - dialConn := c.sudphPacketFilter.NewConn(dialConnPriority, packetfilter.NewKCPConversationFilter()) - - if _, err := dialConn.WriteTo([]byte(holePunchMessage), rAddr); err != nil { - return nil, fmt.Errorf("dialConn.WriteTo: %w", err) - } - - kcpConn, err := kcp.NewConn(remoteAddr, nil, 0, 0, dialConn) - if err != nil { - return nil, err - } - - return kcpConn, nil -} - -func (c *client) dialUDPWithTimeout(addr string) (net.Conn, error) { - timer := time.NewTimer(dialTimeout) - defer timer.Stop() - - c.log.Infof("Dialing %v", addr) - - for { - select { - case <-timer.C: - return nil, ErrTimeout - default: - conn, err := c.dialUDP(addr) - if err == nil { - c.log.Infof("Dialed %v", addr) - return conn, nil - } - - c.log.WithError(err). - Warnf("Failed to dial %v, trying again: %v", addr, err) - } - } -} - -func (c *client) dialVisor(ctx context.Context, visorData arclient.VisorData) (net.Conn, error) { - if visorData.IsLocal { - for _, host := range visorData.Addresses { - addr := net.JoinHostPort(host, visorData.Port) - - if c.beforeDialCallback != nil { - if err := c.beforeDialCallback(c.conf.Type, addr); err != nil { - return nil, err - } - } - - conn, err := c.dialContext(ctx, addr) - if err == nil { - return conn, nil - } - } - } - - addr := visorData.RemoteAddr - if _, _, err := net.SplitHostPort(addr); err != nil { - addr = net.JoinHostPort(addr, visorData.Port) - } - - if c.beforeDialCallback != nil { - if err := c.beforeDialCallback(c.conf.Type, addr); err != nil { - return nil, err - } - } - - return c.dialContext(ctx, addr) -} - -// Listen creates a new listener for sudp. -// The created Listener cannot actually accept remote connections unless Serve is called beforehand. -func (c *client) Listen(lPort uint16) (*tplistener.Listener, error) { - if c.isClosed() { - return nil, io.ErrClosedPipe - } - - ok, freePort := c.porter.Reserve(lPort) - if !ok { - return nil, ErrPortOccupied - } - - c.mu.Lock() - defer c.mu.Unlock() - - lAddr := dmsg.Addr{PK: c.conf.PK, Port: lPort} - lis := tplistener.NewListener(lAddr, freePort) - c.listeners[lPort] = lis - - return lis, nil -} - -// Close closes the Client. -func (c *client) Close() error { - if c == nil { - return nil - } - - c.once.Do(func() { - close(c.done) - - c.mu.Lock() - defer c.mu.Unlock() - - if c.listener != nil { - if err := c.listener.Close(); err != nil { - c.log.WithError(err).Warnf("Failed to close listener") - } - } - - for _, lis := range c.listeners { - if err := lis.Close(); err != nil { - c.log.WithError(err).Warnf("Failed to close listener") - } - } - - switch c.Type() { - case tptypes.STCPR, tptypes.SUDPH: - if err := c.conf.AddressResolver.Close(); err != nil { - c.log.WithError(err).Warnf("Failed to close address-resolver") - } - } - - if c.sudphVisorsConn != nil { - if err := c.sudphVisorsConn.Close(); err != nil { - c.log.WithError(err).Warnf("Failed to close connection to visors") - } - } - }) - - return nil -} - -func (c *client) isClosed() bool { - select { - case <-c.done: - return true - default: - return false - } -} - -// Type returns the stream type. -func (c *client) Type() string { - return c.conf.Type -} +type Client struct{} diff --git a/pkg/snet/network.go b/pkg/snet/network.go index 1371472ed..37fdd507b 100644 --- a/pkg/snet/network.go +++ b/pkg/snet/network.go @@ -13,10 +13,9 @@ import ( "github.com/skycoin/skycoin/src/util/logging" "github.com/skycoin/skywire/pkg/app/appevent" - "github.com/skycoin/skywire/pkg/snet/arclient" "github.com/skycoin/skywire/pkg/snet/directtp" - "github.com/skycoin/skywire/pkg/snet/directtp/pktable" "github.com/skycoin/skywire/pkg/snet/directtp/tptypes" + "github.com/skycoin/skywire/pkg/transport/network/addrresolver" ) var log = logging.MustGetLogger("snet") @@ -78,85 +77,15 @@ type Network struct { nets map[string]struct{} // networks to be used with transports clients NetworkClients - arc arclient.APIClient + arc addrresolver.APIClient onNewNetworkTypeMu sync.Mutex onNewNetworkType func(netType string) dmsgC *dmsg.Client } // New creates a network from a config. -func New(conf Config, dmsgC *dmsg.Client, eb *appevent.Broadcaster, arc arclient.APIClient, masterLogger *logging.MasterLogger) (*Network, error) { - clients := NetworkClients{ - Direct: make(map[string]directtp.Client), - } - - if conf.NetworkConfigs.STCP != nil { - conf := directtp.Config{ - Type: tptypes.STCP, - PK: conf.PubKey, - SK: conf.SecKey, - Table: pktable.NewTable(conf.NetworkConfigs.STCP.PKTable), - LocalAddr: conf.NetworkConfigs.STCP.LocalAddr, - BeforeDialCallback: func(network, addr string) error { - data := appevent.TCPDialData{RemoteNet: network, RemoteAddr: addr} - event := appevent.NewEvent(appevent.TCPDial, data) - _ = eb.Broadcast(context.Background(), event) //nolint:errcheck - return nil - }, - } - clients.Direct[tptypes.STCP] = directtp.NewClient(conf, masterLogger) - } - - if arc != nil { - stcprConf := directtp.Config{ - Type: tptypes.STCPR, - PK: conf.PubKey, - SK: conf.SecKey, - AddressResolver: arc, - BeforeDialCallback: func(network, addr string) error { - data := appevent.TCPDialData{RemoteNet: network, RemoteAddr: addr} - event := appevent.NewEvent(appevent.TCPDial, data) - _ = eb.Broadcast(context.Background(), event) //nolint:errcheck - return nil - }, - } - - clients.Direct[tptypes.STCPR] = directtp.NewClient(stcprConf, masterLogger) - - sudphConf := directtp.Config{ - Type: tptypes.SUDPH, - PK: conf.PubKey, - SK: conf.SecKey, - AddressResolver: arc, - } - - clients.Direct[tptypes.SUDPH] = directtp.NewClient(sudphConf, masterLogger) - } - - return NewRaw(conf, clients, dmsgC, arc), nil -} - -// NewRaw creates a network from a config and a dmsg client. -func NewRaw(conf Config, clients NetworkClients, dmsgC *dmsg.Client, arc arclient.APIClient) *Network { - n := &Network{ - conf: conf, - nets: make(map[string]struct{}), - clients: clients, - arc: arc, - dmsgC: dmsgC, - } - - if dmsgC != nil { - n.addNetworkType(dmsgC.Type()) - } - - for k, v := range clients.Direct { - if v != nil { - n.addNetworkType(k) - } - } - - return n +func New(conf Config, dmsgC *dmsg.Client, eb *appevent.Broadcaster, arc addrresolver.APIClient, masterLogger *logging.MasterLogger) (*Network, error) { + panic("remove") } // Conf gets network configuration. @@ -164,87 +93,6 @@ func (n *Network) Conf() Config { return n.conf } -// Init initiates server connections. -func (n *Network) Init() error { - - if n.conf.NetworkConfigs.STCP != nil { - if client, ok := n.clients.Direct[tptypes.STCP]; ok && client != nil && n.conf.NetworkConfigs.STCP.LocalAddr != "" { - if err := client.Serve(); err != nil { - return fmt.Errorf("failed to initiate 'stcp': %w", err) - } - } else { - log.Infof("No config found for stcp") - } - } - - if n.arc != nil { - if client, ok := n.clients.Direct[tptypes.STCPR]; ok && client != nil { - if err := client.Serve(); err != nil { - return fmt.Errorf("failed to initiate 'stcpr': %w", err) - } - } else { - log.Infof("No config found for stcpr") - } - - if client, ok := n.clients.Direct[tptypes.SUDPH]; ok && client != nil { - if err := client.Serve(); err != nil { - return fmt.Errorf("failed to initiate 'sudph': %w", err) - } - } else { - log.Infof("No config found for sudph") - } - } - - return nil -} - -// OnNewNetworkType sets callback to be called when new network type is ready. -func (n *Network) OnNewNetworkType(callback func(netType string)) { - n.onNewNetworkTypeMu.Lock() - n.onNewNetworkType = callback - n.onNewNetworkTypeMu.Unlock() -} - -// IsNetworkReady checks whether network of type `netType` is ready. -func (n *Network) IsNetworkReady(netType string) bool { - n.netsMu.Lock() - _, ok := n.nets[netType] - n.netsMu.Unlock() - return ok -} - -// Close closes underlying connections. -func (n *Network) Close() error { - n.netsMu.Lock() - defer n.netsMu.Unlock() - - wg := new(sync.WaitGroup) - - directErrors := make(map[string]error) - - for _, directClient := range n.clients.Direct { - if directClient == nil { - continue - } - wg.Add(1) - go func(client directtp.Client) { - err := client.Close() - if err != nil { - } - wg.Done() - }(directClient) - } - wg.Wait() - - for _, err := range directErrors { - if err != nil { - return err - } - } - - return nil -} - // LocalPK returns local public key. func (n *Network) LocalPK() cipher.PubKey { return n.conf.PubKey } @@ -285,58 +133,12 @@ func (n *Network) getClient(tpType string) (directtp.Client, bool) { // Dial dials a visor by its public key and returns a connection. func (n *Network) Dial(ctx context.Context, network string, pk cipher.PubKey, port uint16) (*Conn, error) { - switch network { - case dmsg.Type: - addr := dmsg.Addr{ - PK: pk, - Port: port, - } - - conn, err := n.dmsgC.Dial(ctx, addr) - if err != nil { - return nil, fmt.Errorf("dmsg client dial %v: %w", addr, err) - } - - return makeConn(conn, network), nil - default: - client, ok := n.clients.Direct[network] - if !ok { - return nil, ErrUnknownNetwork - } - - conn, err := client.Dial(ctx, pk, port) - if err != nil { - return nil, fmt.Errorf("dial: %w", err) - } - - log.Infof("Dialed %v, conn local address %q, remote address %q", network, conn.LocalAddr(), conn.RemoteAddr()) - return makeConn(conn, network), nil - } + panic("remove") } // Listen listens on the specified port. func (n *Network) Listen(network string, port uint16) (*Listener, error) { - switch network { - case dmsg.Type: - lis, err := n.dmsgC.Listen(port) - if err != nil { - return nil, err - } - - return makeListener(lis, network), nil - default: - client, ok := n.clients.Direct[network] - if !ok { - return nil, ErrUnknownNetwork - } - - lis, err := client.Listen(port) - if err != nil { - return nil, fmt.Errorf("listen: %w", err) - } - - return makeListener(lis, network), nil - } + panic("remove") } func (n *Network) addNetworkType(netType string) { diff --git a/pkg/snet/snettest/env.go b/pkg/snet/snettest/env.go index 99cdec5c9..4f13b5d6c 100644 --- a/pkg/snet/snettest/env.go +++ b/pkg/snet/snettest/env.go @@ -4,22 +4,13 @@ package snettest // it doesn't test sending data, but it probably should // for now (while refactoring snet) dmsg parts are cut out import ( - "strconv" "testing" "github.com/skycoin/dmsg" "github.com/skycoin/dmsg/cipher" "github.com/skycoin/dmsg/disc" - "github.com/skycoin/skycoin/src/util/logging" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "golang.org/x/net/nettest" "github.com/skycoin/skywire/pkg/snet" - "github.com/skycoin/skywire/pkg/snet/arclient" - "github.com/skycoin/skywire/pkg/snet/directtp" - "github.com/skycoin/skywire/pkg/snet/directtp/pktable" - "github.com/skycoin/skywire/pkg/snet/directtp/tptypes" ) // KeyPair holds a public/private key pair. @@ -55,136 +46,5 @@ type Env struct { // NewEnv creates a `network.Network` test environment. // `nPairs` is the public/private key pairs of all the `network.Network`s to be created. func NewEnv(t *testing.T, keys []KeyPair, networks []string) *Env { - // Prepare `dmsg`. - dmsgD := disc.NewMock(0) - dmsgS, dmsgSErr := createDmsgSrv(t, dmsgD) - - const baseSTCPPort = 7033 - - tableEntries := make(map[cipher.PubKey]string) - for i, pair := range keys { - tableEntries[pair.PK] = "127.0.0.1:" + strconv.Itoa(baseSTCPPort+i) - } - - table := pktable.NewTable(tableEntries) - - var hasStcp, hasStcpr, hasSudph bool - - for _, network := range networks { - switch network { - case tptypes.STCP: - hasStcp = true - case tptypes.STCPR: - hasStcpr = true - case tptypes.SUDPH: - hasSudph = true - } - } - - // Prepare `snets`. - ns := make([]*snet.Network, len(keys)) - - const stcpBasePort = 7033 - - for i, pairs := range keys { - networkConfigs := snet.NetworkConfigs{ - STCP: &snet.STCPConfig{ - LocalAddr: "127.0.0.1:" + strconv.Itoa(stcpBasePort+i), - }, - } - - clients := snet.NetworkClients{ - Direct: make(map[string]directtp.Client), - } - - addressResolver := new(arclient.MockAPIClient) - - if hasStcp { - conf := directtp.Config{ - Type: tptypes.STCP, - PK: pairs.PK, - SK: pairs.SK, - Table: table, - LocalAddr: networkConfigs.STCP.LocalAddr, - } - - clients.Direct[tptypes.STCP] = directtp.NewClient(conf, logging.NewMasterLogger()) - } - - if hasStcpr { - conf := directtp.Config{ - Type: tptypes.STCPR, - PK: pairs.PK, - SK: pairs.SK, - AddressResolver: addressResolver, - } - - clients.Direct[tptypes.STCPR] = directtp.NewClient(conf, logging.NewMasterLogger()) - } - - if hasSudph { - conf := directtp.Config{ - Type: tptypes.SUDPH, - PK: pairs.PK, - SK: pairs.SK, - AddressResolver: addressResolver, - } - - clients.Direct[tptypes.SUDPH] = directtp.NewClient(conf, logging.NewMasterLogger()) - } - - snetConfig := snet.Config{ - PubKey: pairs.PK, - SecKey: pairs.SK, - NetworkConfigs: networkConfigs, - } - - // todo: dmsg is not set up here - n := snet.NewRaw(snetConfig, clients, nil, addressResolver) - require.NoError(t, n.Init()) - ns[i] = n - } - - // Prepare teardown closure. - teardown := func() { - for _, n := range ns { - assert.NoError(t, n.Close()) - } - assert.NoError(t, dmsgS.Close()) - for err := range dmsgSErr { - assert.NoError(t, err) - } - } - - return &Env{ - DmsgD: dmsgD, - DmsgS: dmsgS, - Keys: keys, - Nets: ns, - teardown: teardown, - } -} - -// Teardown shutdowns the Env. -func (e *Env) Teardown() { e.teardown() } - -func createDmsgSrv(t *testing.T, dc disc.APIClient) (srv *dmsg.Server, srvErr <-chan error) { - pk, sk, err := cipher.GenerateDeterministicKeyPair([]byte("s")) - require.NoError(t, err) - - l, err := nettest.NewLocalListener("tcp") - require.NoError(t, err) - - srv = dmsg.NewServer(pk, sk, dc, &dmsg.ServerConfig{MaxSessions: 100}, nil) - - errCh := make(chan error, 1) - - go func() { - errCh <- srv.Serve(l, "") - close(errCh) - }() - - <-srv.Ready() - - return srv, errCh + return nil } From 8f49205fe5a2db27eddd4cf7cdfb4d2a1c87db1a Mon Sep 17 00:00:00 2001 From: Never M Date: Tue, 29 Jun 2021 13:14:41 +0300 Subject: [PATCH 32/55] Remove unused snet code --- pkg/snet/conn.go | 37 ----- pkg/snet/directtp/client.go | 3 - pkg/snet/directtp/pktable/pktable.go | 98 ------------- pkg/snet/directtp/tplistener/listener.go | 79 ---------- pkg/snet/listener.go | 38 ----- pkg/snet/network.go | 174 +---------------------- pkg/snet/network_test.go | 20 --- pkg/transport/network/stcp.go | 6 + pkg/visor/init.go | 25 +--- pkg/visor/visorconfig/config.go | 4 +- pkg/visor/visorconfig/v0.go | 4 +- pkg/visor/visorconfig/v1.go | 16 +-- 12 files changed, 22 insertions(+), 482 deletions(-) delete mode 100644 pkg/snet/directtp/client.go delete mode 100644 pkg/snet/directtp/pktable/pktable.go delete mode 100644 pkg/snet/directtp/tplistener/listener.go diff --git a/pkg/snet/conn.go b/pkg/snet/conn.go index fdaf75cb7..941963d2f 100644 --- a/pkg/snet/conn.go +++ b/pkg/snet/conn.go @@ -1,38 +1 @@ package snet - -import ( - "net" - - "github.com/skycoin/dmsg/cipher" -) - -// Conn represent a connection between nodes in Skywire. -type Conn struct { - net.Conn - lPK cipher.PubKey - rPK cipher.PubKey - lPort uint16 - rPort uint16 - network string -} - -func makeConn(conn net.Conn, network string) *Conn { - lPK, lPort := disassembleAddr(conn.LocalAddr()) - rPK, rPort := disassembleAddr(conn.RemoteAddr()) - return &Conn{Conn: conn, lPK: lPK, rPK: rPK, lPort: lPort, rPort: rPort, network: network} -} - -// LocalPK returns local public key of connection. -func (c Conn) LocalPK() cipher.PubKey { return c.lPK } - -// RemotePK returns remote public key of connection. -func (c Conn) RemotePK() cipher.PubKey { return c.rPK } - -// LocalPort returns local port of connection. -func (c Conn) LocalPort() uint16 { return c.lPort } - -// RemotePort returns remote port of connection. -func (c Conn) RemotePort() uint16 { return c.rPort } - -// Network returns network of connection. -func (c Conn) Network() string { return c.network } diff --git a/pkg/snet/directtp/client.go b/pkg/snet/directtp/client.go deleted file mode 100644 index e249757fc..000000000 --- a/pkg/snet/directtp/client.go +++ /dev/null @@ -1,3 +0,0 @@ -package directtp - -type Client struct{} diff --git a/pkg/snet/directtp/pktable/pktable.go b/pkg/snet/directtp/pktable/pktable.go deleted file mode 100644 index bc40761f7..000000000 --- a/pkg/snet/directtp/pktable/pktable.go +++ /dev/null @@ -1,98 +0,0 @@ -package pktable - -import ( - "bufio" - "errors" - "fmt" - "os" - "path/filepath" - "strings" - - "github.com/skycoin/dmsg/cipher" -) - -const expectedFieldsLen = 2 - -// PKTable associates public keys to udp addresses. -type PKTable interface { - Addr(pk cipher.PubKey) (string, bool) - PubKey(addr string) (cipher.PubKey, bool) - Count() int -} - -type memoryTable struct { - entries map[cipher.PubKey]string - reverse map[string]cipher.PubKey -} - -// NewTable instantiates a memory implementation of PKTable. -func NewTable(entries map[cipher.PubKey]string) PKTable { - reverse := make(map[string]cipher.PubKey, len(entries)) - for pk, addr := range entries { - reverse[addr] = pk - } - - return &memoryTable{ - entries: entries, - reverse: reverse, - } -} - -// NewTableFromFile is similar to NewTable, but grabs predefined values -// from a file specified in 'path'. -func NewTableFromFile(path string) (PKTable, error) { - path, err := filepath.Abs(path) - if err != nil { - return nil, err - } - - f, err := os.Open(filepath.Clean(path)) - if err != nil { - return nil, err - } - - defer func() { - if err := f.Close(); err != nil { - fmt.Println("udp_factory: failed to close table file:", err) - } - }() - - var ( - entries = make(map[cipher.PubKey]string) - s = bufio.NewScanner(f) - ) - - for s.Scan() { - fields := strings.Fields(s.Text()) - - if len(fields) != expectedFieldsLen { - return nil, errors.New("pk file is invalid: each line should have two fields") - } - - var pk cipher.PubKey - if err := pk.UnmarshalText([]byte(fields[0])); err != nil { - return nil, fmt.Errorf("pk file is invalid: each line should have two fields: %w", err) - } - - entries[pk] = fields[1] - } - - return NewTable(entries), nil -} - -// Addr obtains the address associated with the given public key. -func (mt *memoryTable) Addr(pk cipher.PubKey) (string, bool) { - addr, ok := mt.entries[pk] - return addr, ok -} - -// PubKey obtains the public key associated with the given public key. -func (mt *memoryTable) PubKey(addr string) (cipher.PubKey, bool) { - pk, ok := mt.reverse[addr] - return pk, ok -} - -// Count returns the number of entries within the PKTable implementation. -func (mt *memoryTable) Count() int { - return len(mt.entries) -} diff --git a/pkg/snet/directtp/tplistener/listener.go b/pkg/snet/directtp/tplistener/listener.go deleted file mode 100644 index ccb2ccb80..000000000 --- a/pkg/snet/directtp/tplistener/listener.go +++ /dev/null @@ -1,79 +0,0 @@ -package tplistener - -import ( - "io" - "net" - "sync" - - "github.com/skycoin/dmsg" - - "github.com/skycoin/skywire/pkg/snet/directtp/tpconn" -) - -// Listener implements net.Listener -type Listener struct { - lAddr dmsg.Addr - mx sync.Mutex - once sync.Once - freePort func() - accept chan *tpconn.Conn - done chan struct{} -} - -// NewListener returns a new Listener. -func NewListener(lAddr dmsg.Addr, freePort func()) *Listener { - return &Listener{ - lAddr: lAddr, - freePort: freePort, - accept: make(chan *tpconn.Conn), - done: make(chan struct{}), - } -} - -// Introduce is used by Client to introduce Conn to Listener. -func (l *Listener) Introduce(conn *tpconn.Conn) error { - select { - case <-l.done: - return io.ErrClosedPipe - default: - l.mx.Lock() - defer l.mx.Unlock() - - select { - case l.accept <- conn: - return nil - case <-l.done: - return io.ErrClosedPipe - } - } -} - -// Accept implements net.Listener -func (l *Listener) Accept() (net.Conn, error) { - c, ok := <-l.accept - if !ok { - return nil, io.ErrClosedPipe - } - - return c, nil -} - -// Close implements net.Listener -func (l *Listener) Close() error { - l.once.Do(func() { - close(l.done) - - l.mx.Lock() - close(l.accept) - l.mx.Unlock() - - l.freePort() - }) - - return nil -} - -// Addr implements net.Listener -func (l *Listener) Addr() net.Addr { - return l.lAddr -} diff --git a/pkg/snet/listener.go b/pkg/snet/listener.go index d0809f327..941963d2f 100644 --- a/pkg/snet/listener.go +++ b/pkg/snet/listener.go @@ -1,39 +1 @@ package snet - -import ( - "net" - - "github.com/skycoin/dmsg/cipher" -) - -// Listener represents a listener. -type Listener struct { - net.Listener - lPK cipher.PubKey - lPort uint16 - network string -} - -func makeListener(l net.Listener, network string) *Listener { - lPK, lPort := disassembleAddr(l.Addr()) - return &Listener{Listener: l, lPK: lPK, lPort: lPort, network: network} -} - -// LocalPK returns a local public key of listener. -func (l Listener) LocalPK() cipher.PubKey { return l.lPK } - -// LocalPort returns a local port of listener. -func (l Listener) LocalPort() uint16 { return l.lPort } - -// Network returns a network of listener. -func (l Listener) Network() string { return l.network } - -// AcceptConn accepts a connection from listener. -func (l Listener) AcceptConn() (*Conn, error) { - conn, err := l.Listener.Accept() - if err != nil { - return nil, err - } - - return makeConn(conn, l.network), nil -} diff --git a/pkg/snet/network.go b/pkg/snet/network.go index 37fdd507b..08ee9327c 100644 --- a/pkg/snet/network.go +++ b/pkg/snet/network.go @@ -1,175 +1,3 @@ package snet -import ( - "context" - "errors" - "fmt" - "net" - "strings" - "sync" - - "github.com/skycoin/dmsg" - "github.com/skycoin/dmsg/cipher" - "github.com/skycoin/skycoin/src/util/logging" - - "github.com/skycoin/skywire/pkg/app/appevent" - "github.com/skycoin/skywire/pkg/snet/directtp" - "github.com/skycoin/skywire/pkg/snet/directtp/tptypes" - "github.com/skycoin/skywire/pkg/transport/network/addrresolver" -) - -var log = logging.MustGetLogger("snet") - -var ( - // ErrUnknownNetwork occurs on attempt to dial an unknown network type. - ErrUnknownNetwork = errors.New("unknown network type") - knownNetworks = map[string]struct{}{ - dmsg.Type: {}, - tptypes.STCP: {}, - tptypes.STCPR: {}, - tptypes.SUDPH: {}, - } -) - -// IsKnownNetwork tells whether network type `netType` is known. -func IsKnownNetwork(netType string) bool { - _, ok := knownNetworks[netType] - return ok -} - -// NetworkConfig is a common interface for network configs. -type NetworkConfig interface { - Type() string -} - -// STCPConfig defines config for STCP network. -type STCPConfig struct { - PKTable map[cipher.PubKey]string `json:"pk_table"` - LocalAddr string `json:"local_address"` -} - -// Type returns STCP type. -func (c *STCPConfig) Type() string { - return tptypes.STCP -} - -// Config represents a network configuration. -type Config struct { - PubKey cipher.PubKey - SecKey cipher.SecKey - NetworkConfigs NetworkConfigs -} - -// NetworkConfigs represents all network configs. -type NetworkConfigs struct { - STCP *STCPConfig // The stcp service will not be started if nil. -} - -// NetworkClients represents all network clients. -type NetworkClients struct { - Direct map[string]directtp.Client -} - -// Network represents a network between nodes in Skywire. -type Network struct { - conf Config - netsMu sync.RWMutex - nets map[string]struct{} // networks to be used with transports - clients NetworkClients - - arc addrresolver.APIClient - onNewNetworkTypeMu sync.Mutex - onNewNetworkType func(netType string) - dmsgC *dmsg.Client -} - -// New creates a network from a config. -func New(conf Config, dmsgC *dmsg.Client, eb *appevent.Broadcaster, arc addrresolver.APIClient, masterLogger *logging.MasterLogger) (*Network, error) { - panic("remove") -} - -// Conf gets network configuration. -func (n *Network) Conf() Config { - return n.conf -} - -// LocalPK returns local public key. -func (n *Network) LocalPK() cipher.PubKey { return n.conf.PubKey } - -// LocalSK returns local secure key. -func (n *Network) LocalSK() cipher.SecKey { return n.conf.SecKey } - -// TransportNetworks returns network types that are used for transports. -func (n *Network) TransportNetworks() []string { - n.netsMu.RLock() - networks := make([]string, 0, len(n.nets)) - for network := range n.nets { - networks = append(networks, network) - } - n.netsMu.RUnlock() - - return networks -} - -// STcp returns the underlying stcp.Client. -func (n *Network) STcp() (directtp.Client, bool) { - return n.getClient(tptypes.STCP) -} - -// STcpr returns the underlying stcpr.Client. -func (n *Network) STcpr() (directtp.Client, bool) { - return n.getClient(tptypes.STCPR) -} - -// SUdpH returns the underlying sudph.Client. -func (n *Network) SUdpH() (directtp.Client, bool) { - return n.getClient(tptypes.SUDPH) -} - -func (n *Network) getClient(tpType string) (directtp.Client, bool) { - c, ok := n.clients.Direct[tpType] - return c, ok -} - -// Dial dials a visor by its public key and returns a connection. -func (n *Network) Dial(ctx context.Context, network string, pk cipher.PubKey, port uint16) (*Conn, error) { - panic("remove") -} - -// Listen listens on the specified port. -func (n *Network) Listen(network string, port uint16) (*Listener, error) { - panic("remove") -} - -func (n *Network) addNetworkType(netType string) { - n.netsMu.Lock() - defer n.netsMu.Unlock() - - if _, ok := n.nets[netType]; !ok { - n.nets[netType] = struct{}{} - n.onNewNetworkTypeMu.Lock() - if n.onNewNetworkType != nil { - n.onNewNetworkType(netType) - } - n.onNewNetworkTypeMu.Unlock() - } -} - -func disassembleAddr(addr net.Addr) (pk cipher.PubKey, port uint16) { - strs := strings.Split(addr.String(), ":") - if len(strs) != 2 { - panic(fmt.Errorf("network.disassembleAddr: %v %s", "invalid addr", addr.String())) - } - - if err := pk.Set(strs[0]); err != nil { - panic(fmt.Errorf("network.disassembleAddr: %v %s", err, addr.String())) - } - - if strs[1] != "~" { - if _, err := fmt.Sscanf(strs[1], "%d", &port); err != nil { - panic(fmt.Errorf("network.disassembleAddr: %w", err)) - } - } - - return -} +type Network struct{} diff --git a/pkg/snet/network_test.go b/pkg/snet/network_test.go index 3dce54c36..941963d2f 100644 --- a/pkg/snet/network_test.go +++ b/pkg/snet/network_test.go @@ -1,21 +1 @@ package snet - -import ( - "testing" - - "github.com/skycoin/dmsg" - "github.com/skycoin/dmsg/cipher" - "github.com/stretchr/testify/require" -) - -func TestDisassembleAddr(t *testing.T) { - pk, _ := cipher.GenerateKeyPair() - port := uint16(2) - addr := dmsg.Addr{ - PK: pk, Port: port, - } - - gotPK, gotPort := disassembleAddr(addr) - require.Equal(t, pk, gotPK) - require.Equal(t, port, gotPort) -} diff --git a/pkg/transport/network/stcp.go b/pkg/transport/network/stcp.go index 195897432..4395b7852 100644 --- a/pkg/transport/network/stcp.go +++ b/pkg/transport/network/stcp.go @@ -11,6 +11,12 @@ import ( "github.com/skycoin/skywire/pkg/transport/network/stcp" ) +// STCPConfig defines config for STCP network. +type STCPConfig struct { + PKTable map[cipher.PubKey]string `json:"pk_table"` + LocalAddr string `json:"local_address"` +} + type stcpClient struct { *genericClient table stcp.PKTable diff --git a/pkg/visor/init.go b/pkg/visor/init.go index f5cc6f418..793a4da4e 100644 --- a/pkg/visor/init.go +++ b/pkg/visor/init.go @@ -28,12 +28,12 @@ import ( "github.com/skycoin/skywire/pkg/servicedisc" "github.com/skycoin/skywire/pkg/setup/setupclient" "github.com/skycoin/skywire/pkg/skyenv" - "github.com/skycoin/skywire/pkg/snet/directtp/pktable" "github.com/skycoin/skywire/pkg/snet/directtp/tptypes" "github.com/skycoin/skywire/pkg/snet/dmsgc" "github.com/skycoin/skywire/pkg/transport" "github.com/skycoin/skywire/pkg/transport/network" "github.com/skycoin/skywire/pkg/transport/network/addrresolver" + "github.com/skycoin/skywire/pkg/transport/network/stcp" ts "github.com/skycoin/skywire/pkg/transport/setup" "github.com/skycoin/skywire/pkg/transport/tpdclient" "github.com/skycoin/skywire/pkg/util/netutil" @@ -251,28 +251,8 @@ func initTransport(ctx context.Context, v *Visor, log *logging.Logger) error { } managerLogger := v.MasterLogger().PackageLogger("transport_manager") - // nc := snet.NetworkConfigs{ - // STCP: v.conf.STCP, - // } - - // netconf := snet.Config{ - // PubKey: v.conf.PK, - // SecKey: v.conf.SK, - // NetworkConfigs: nc, - // } - - // n, err := snet.New(netconf, v.dmsgC, v.ebc, v.arClient) - // if err != nil { - // return err - // } - - // if err := n.Init(); err != nil { - // return err - // } - // v.pushCloseStack("snet", n.Close) - // todo: pass down configuration? - table := pktable.NewTable(v.conf.STCP.PKTable) + table := stcp.NewTable(v.conf.STCP.PKTable) factory := network.ClientFactory{ PK: v.conf.PK, SK: v.conf.SK, @@ -287,6 +267,7 @@ func initTransport(ctx context.Context, v *Visor, log *logging.Logger) error { return err } + // todo: move to transport manager tpM.OnAfterTPClosed(func(network, addr string) { if network == tptypes.STCPR && addr != "" { data := appevent.TCPCloseData{RemoteNet: network, RemoteAddr: addr} diff --git a/pkg/visor/visorconfig/config.go b/pkg/visor/visorconfig/config.go index 4a0ec6924..313e639f4 100644 --- a/pkg/visor/visorconfig/config.go +++ b/pkg/visor/visorconfig/config.go @@ -8,8 +8,8 @@ import ( "github.com/skycoin/skywire/pkg/restart" "github.com/skycoin/skywire/pkg/routing" "github.com/skycoin/skywire/pkg/skyenv" - "github.com/skycoin/skywire/pkg/snet" "github.com/skycoin/skywire/pkg/snet/dmsgc" + "github.com/skycoin/skywire/pkg/transport/network" "github.com/skycoin/skywire/pkg/visor/hypervisorconfig" ) @@ -79,7 +79,7 @@ func defaultConfigFromCommon(cc *Common, hypervisor bool) (*V1, error) { CLIAddr: skyenv.DefaultDmsgPtyCLIAddr, } - conf.STCP = &snet.STCPConfig{ + conf.STCP = &network.STCPConfig{ LocalAddr: skyenv.DefaultSTCPAddr, PKTable: nil, } diff --git a/pkg/visor/visorconfig/v0.go b/pkg/visor/visorconfig/v0.go index 415a6d625..79ace6917 100644 --- a/pkg/visor/visorconfig/v0.go +++ b/pkg/visor/visorconfig/v0.go @@ -4,8 +4,8 @@ import ( "github.com/skycoin/dmsg/cipher" "github.com/skycoin/skywire/pkg/routing" - "github.com/skycoin/skywire/pkg/snet" "github.com/skycoin/skywire/pkg/snet/dmsgc" + "github.com/skycoin/skywire/pkg/transport/network" ) // V0Name is the version string before proper versioning is implemented. @@ -25,7 +25,7 @@ type V0 struct { DmsgPty *V1Dmsgpty `json:"dmsg_pty,omitempty"` - STCP *snet.STCPConfig `json:"stcp,omitempty"` + STCP *network.STCPConfig `json:"stcp,omitempty"` Transport *struct { Discovery string `json:"discovery"` diff --git a/pkg/visor/visorconfig/v1.go b/pkg/visor/visorconfig/v1.go index 094d52edc..c35214c5f 100644 --- a/pkg/visor/visorconfig/v1.go +++ b/pkg/visor/visorconfig/v1.go @@ -8,8 +8,8 @@ import ( "github.com/skycoin/dmsg/cipher" "github.com/skycoin/skywire/pkg/app/launcher" - "github.com/skycoin/skywire/pkg/snet" "github.com/skycoin/skywire/pkg/snet/dmsgc" + "github.com/skycoin/skywire/pkg/transport/network" "github.com/skycoin/skywire/pkg/visor/hypervisorconfig" ) @@ -40,13 +40,13 @@ type V1 struct { *Common mu sync.RWMutex - Dmsg *dmsgc.DmsgConfig `json:"dmsg"` - Dmsgpty *V1Dmsgpty `json:"dmsgpty,omitempty"` - STCP *snet.STCPConfig `json:"stcp,omitempty"` - Transport *V1Transport `json:"transport"` - Routing *V1Routing `json:"routing"` - UptimeTracker *V1UptimeTracker `json:"uptime_tracker,omitempty"` - Launcher *V1Launcher `json:"launcher"` + Dmsg *dmsgc.DmsgConfig `json:"dmsg"` + Dmsgpty *V1Dmsgpty `json:"dmsgpty,omitempty"` + STCP *network.STCPConfig `json:"stcp,omitempty"` + Transport *V1Transport `json:"transport"` + Routing *V1Routing `json:"routing"` + UptimeTracker *V1UptimeTracker `json:"uptime_tracker,omitempty"` + Launcher *V1Launcher `json:"launcher"` Hypervisors []cipher.PubKey `json:"hypervisors"` CLIAddr string `json:"cli_addr"` From fcad88779c199062395daa5acfa17ab752093cbc Mon Sep 17 00:00:00 2001 From: Never M Date: Tue, 29 Jun 2021 13:28:47 +0300 Subject: [PATCH 33/55] Move porter and handshake to network package --- pkg/router/router.go | 4 +-- .../directtp/noisewrapper/noisewrapper.go | 27 ------------------- pkg/snet/directtp/tpconn/conn.go | 23 ++++++++++++---- pkg/transport/network/addrresolver/client.go | 6 ++--- pkg/transport/network/client.go | 14 +++++----- pkg/transport/network/connection.go | 23 +++++++++++++--- .../network/handshake}/handshake.go | 2 +- .../network/handshake}/handshake_test.go | 2 +- .../network}/porter/porter.go | 0 9 files changed, 52 insertions(+), 49 deletions(-) delete mode 100644 pkg/snet/directtp/noisewrapper/noisewrapper.go rename pkg/{snet/directtp/tphandshake => transport/network/handshake}/handshake.go (99%) rename pkg/{snet/directtp/tphandshake => transport/network/handshake}/handshake_test.go (98%) rename pkg/{snet/directtp => transport/network}/porter/porter.go (100%) diff --git a/pkg/router/router.go b/pkg/router/router.go index a490e99fd..41017e44a 100644 --- a/pkg/router/router.go +++ b/pkg/router/router.go @@ -21,8 +21,8 @@ import ( "github.com/skycoin/skywire/pkg/routing" "github.com/skycoin/skywire/pkg/setup/setupclient" "github.com/skycoin/skywire/pkg/skyenv" - "github.com/skycoin/skywire/pkg/snet/directtp/noisewrapper" "github.com/skycoin/skywire/pkg/transport" + "github.com/skycoin/skywire/pkg/transport/network" ) //go:generate mockery -name Router -case underscore -inpkg @@ -451,7 +451,7 @@ func (r *router) saveRouteGroupRules(rules routing.EdgeRules, nsConf noise.Confi if rg.encrypt { // wrapping rg with noise - wrappedRG, err := noisewrapper.WrapConn(nsConf, rg) + wrappedRG, err := network.EncryptConn(nsConf, rg) if err != nil { r.logger.WithError(err).Errorf("Failed to wrap route group (%s): %v, closing...", &rules.Desc, err) if err := rg.Close(); err != nil { diff --git a/pkg/snet/directtp/noisewrapper/noisewrapper.go b/pkg/snet/directtp/noisewrapper/noisewrapper.go deleted file mode 100644 index a95ad88d5..000000000 --- a/pkg/snet/directtp/noisewrapper/noisewrapper.go +++ /dev/null @@ -1,27 +0,0 @@ -package noisewrapper - -import ( - "fmt" - "net" - "time" - - "github.com/skycoin/dmsg/noise" -) - -// HSTimeout sets handshake timeout. -const HSTimeout = 5 * time.Second - -// WrapConn wraps `conn` with noise. -func WrapConn(config noise.Config, conn net.Conn) (net.Conn, error) { - ns, err := noise.New(noise.HandshakeKK, config) - if err != nil { - return nil, fmt.Errorf("failed to prepare stream noise object: %w", err) - } - - wrappedConn, err := noise.WrapConn(conn, ns, HSTimeout) - if err != nil { - return nil, fmt.Errorf("error performing noise handshake: %w", err) - } - - return wrappedConn, nil -} diff --git a/pkg/snet/directtp/tpconn/conn.go b/pkg/snet/directtp/tpconn/conn.go index 6aae796dd..71e8afaf5 100644 --- a/pkg/snet/directtp/tpconn/conn.go +++ b/pkg/snet/directtp/tpconn/conn.go @@ -9,9 +9,7 @@ import ( "github.com/skycoin/dmsg/cipher" "github.com/skycoin/dmsg/noise" "github.com/skycoin/skycoin/src/util/logging" - - "github.com/skycoin/skywire/pkg/snet/directtp/noisewrapper" - "github.com/skycoin/skywire/pkg/snet/directtp/tphandshake" + "github.com/skycoin/skywire/pkg/transport/network/handshake" ) // Conn wraps an underlying net.Conn and modifies various methods to integrate better with the 'network' package. @@ -29,7 +27,7 @@ type Config struct { LocalPK cipher.PubKey LocalSK cipher.SecKey Deadline time.Time - Handshake tphandshake.Handshake + Handshake handshake.Handshake FreePort func() Encrypt bool Initiator bool @@ -66,7 +64,7 @@ func NewConn(c Config) (*Conn, error) { Initiator: c.Initiator, } - wrappedConn, err := noisewrapper.WrapConn(config, c.Conn) + wrappedConn, err := encryptConn(config, c.Conn) if err != nil { return nil, fmt.Errorf("encrypt connection to %v@%v: %w", rAddr, c.Conn.RemoteAddr(), err) } @@ -83,6 +81,21 @@ func NewConn(c Config) (*Conn, error) { return &Conn{Conn: c.Conn, lAddr: lAddr, rAddr: rAddr, freePort: c.FreePort}, nil } +// EncryptConn encrypts given connection using noise config +func encryptConn(config noise.Config, conn net.Conn) (net.Conn, error) { + ns, err := noise.New(noise.HandshakeKK, config) + if err != nil { + return nil, fmt.Errorf("failed to prepare stream noise object: %w", err) + } + + wrappedConn, err := noise.WrapConn(conn, ns, time.Second*3) + if err != nil { + return nil, fmt.Errorf("error performing noise handshake: %w", err) + } + + return wrappedConn, nil +} + // LocalAddr implements net.Conn func (c *Conn) LocalAddr() net.Addr { return c.lAddr diff --git a/pkg/transport/network/addrresolver/client.go b/pkg/transport/network/addrresolver/client.go index 9840aceb1..6aca51768 100644 --- a/pkg/transport/network/addrresolver/client.go +++ b/pkg/transport/network/addrresolver/client.go @@ -25,7 +25,7 @@ import ( "github.com/skycoin/skywire/internal/netutil" "github.com/skycoin/skywire/internal/packetfilter" "github.com/skycoin/skywire/pkg/snet/directtp/tpconn" - "github.com/skycoin/skywire/pkg/snet/directtp/tphandshake" + "github.com/skycoin/skywire/pkg/transport/network/handshake" ) const ( @@ -396,14 +396,14 @@ func (c *httpClient) wrapConn(conn net.PacketConn) (*tpconn.Conn, error) { } emptyAddr := dmsg.Addr{PK: cipher.PubKey{}, Port: 0} - hs := tphandshake.InitiatorHandshake(c.sk, dmsg.Addr{PK: c.pk, Port: 0}, emptyAddr) + hs := handshake.InitiatorHandshake(c.sk, dmsg.Addr{PK: c.pk, Port: 0}, emptyAddr) connConfig := tpconn.Config{ Log: c.log, Conn: arKCPConn, LocalPK: c.pk, LocalSK: c.sk, - Deadline: time.Now().Add(tphandshake.Timeout), + Deadline: time.Now().Add(handshake.Timeout), Handshake: hs, Encrypt: false, Initiator: true, diff --git a/pkg/transport/network/client.go b/pkg/transport/network/client.go index ea9da846d..a6e31f5f4 100644 --- a/pkg/transport/network/client.go +++ b/pkg/transport/network/client.go @@ -14,9 +14,9 @@ import ( "github.com/skycoin/skycoin/src/util/logging" "github.com/skycoin/skywire/pkg/app/appevent" - "github.com/skycoin/skywire/pkg/snet/directtp/porter" - "github.com/skycoin/skywire/pkg/snet/directtp/tphandshake" "github.com/skycoin/skywire/pkg/transport/network/addrresolver" + "github.com/skycoin/skywire/pkg/transport/network/handshake" + "github.com/skycoin/skywire/pkg/transport/network/porter" "github.com/skycoin/skywire/pkg/transport/network/stcp" ) @@ -123,7 +123,7 @@ func (c *genericClient) initConnection(ctx context.Context, conn net.Conn, rPK c lAddr, rAddr := dmsg.Addr{PK: c.lPK, Port: lPort}, dmsg.Addr{PK: rPK, Port: rPort} remoteAddr := conn.RemoteAddr() c.log.Infof("Performing handshake with %v", remoteAddr) - hs := tphandshake.InitiatorHandshake(c.lSK, lAddr, rAddr) + hs := handshake.InitiatorHandshake(c.lSK, lAddr, rAddr) return c.wrapConn(conn, hs, true, freePort) } @@ -143,7 +143,7 @@ func (c *genericClient) acceptConnections(lis net.Listener) { continue // likely it's a dummy connection from service discovery } c.log.Warnf("failed to accept incoming connection: %v", err) - if !tphandshake.IsHandshakeError(err) { + if !handshake.IsHandshakeError(err) { c.log.Warnf("stopped serving") return } @@ -153,8 +153,8 @@ func (c *genericClient) acceptConnections(lis net.Listener) { // wrapConn performs handshake over provided raw connection and wraps it in // network.Conn type using the data obtained from handshake process -func (c *genericClient) wrapConn(conn net.Conn, hs tphandshake.Handshake, initiator bool, onClose func()) (*Conn, error) { - lAddr, rAddr, err := hs(conn, time.Now().Add(tphandshake.Timeout)) +func (c *genericClient) wrapConn(conn net.Conn, hs handshake.Handshake, initiator bool, onClose func()) (*Conn, error) { + lAddr, rAddr, err := hs(conn, time.Now().Add(handshake.Timeout)) if err != nil { if err := conn.Close(); err != nil { c.log.WithError(err).Warnf("Failed to close connection") @@ -188,7 +188,7 @@ func (c *genericClient) acceptConn() error { c.log.Infof("Accepted connection from %v", remoteAddr) onClose := func() {} - hs := tphandshake.ResponderHandshake(tphandshake.MakeF2PortChecker(c.checkListener)) + hs := handshake.ResponderHandshake(handshake.MakeF2PortChecker(c.checkListener)) wrappedConn, err := c.wrapConn(conn, hs, false, onClose) if err != nil { return err diff --git a/pkg/transport/network/connection.go b/pkg/transport/network/connection.go index 9cb01e65f..6ae8a8b3e 100644 --- a/pkg/transport/network/connection.go +++ b/pkg/transport/network/connection.go @@ -3,14 +3,15 @@ package network import ( "fmt" "net" + "time" "github.com/skycoin/dmsg" "github.com/skycoin/dmsg/cipher" "github.com/skycoin/dmsg/noise" - - "github.com/skycoin/skywire/pkg/snet/directtp/noisewrapper" ) +const encryptHSTimout = 5 * time.Second + // Conn represents a network connection between two visors in skywire network // This connection wraps raw network connection and is ready to use for sending data. // It also provides skywire-specific methods on top of net.Conn @@ -29,7 +30,7 @@ func (c *Conn) encrypt(lPK cipher.PubKey, lSK cipher.SecKey, initator bool) erro Initiator: initator, } - wrappedConn, err := noisewrapper.WrapConn(config, c.Conn) + wrappedConn, err := EncryptConn(config, c.Conn) if err != nil { return fmt.Errorf("encrypt connection to %v@%v: %w", c.rAddr, c.Conn.RemoteAddr(), err) } @@ -38,6 +39,22 @@ func (c *Conn) encrypt(lPK cipher.PubKey, lSK cipher.SecKey, initator bool) erro return nil } +// EncryptConn encrypts given connection +// todo: make private when tpconn.Conn is gone +func EncryptConn(config noise.Config, conn net.Conn) (net.Conn, error) { + ns, err := noise.New(noise.HandshakeKK, config) + if err != nil { + return nil, fmt.Errorf("failed to prepare stream noise object: %w", err) + } + + wrappedConn, err := noise.WrapConn(conn, ns, encryptHSTimout) + if err != nil { + return nil, fmt.Errorf("error performing noise handshake: %w", err) + } + + return wrappedConn, nil +} + // LocalAddr implements net.Conn func (c *Conn) LocalAddr() net.Addr { return c.lAddr diff --git a/pkg/snet/directtp/tphandshake/handshake.go b/pkg/transport/network/handshake/handshake.go similarity index 99% rename from pkg/snet/directtp/tphandshake/handshake.go rename to pkg/transport/network/handshake/handshake.go index eb0440e87..10afde452 100644 --- a/pkg/snet/directtp/tphandshake/handshake.go +++ b/pkg/transport/network/handshake/handshake.go @@ -1,4 +1,4 @@ -package tphandshake +package handshake import ( "bytes" diff --git a/pkg/snet/directtp/tphandshake/handshake_test.go b/pkg/transport/network/handshake/handshake_test.go similarity index 98% rename from pkg/snet/directtp/tphandshake/handshake_test.go rename to pkg/transport/network/handshake/handshake_test.go index 8731ed569..474527f9d 100644 --- a/pkg/snet/directtp/tphandshake/handshake_test.go +++ b/pkg/transport/network/handshake/handshake_test.go @@ -1,4 +1,4 @@ -package tphandshake +package handshake import ( "errors" diff --git a/pkg/snet/directtp/porter/porter.go b/pkg/transport/network/porter/porter.go similarity index 100% rename from pkg/snet/directtp/porter/porter.go rename to pkg/transport/network/porter/porter.go From 63c834c33683c4654d9ce67216da5e0adcdf5b3a Mon Sep 17 00:00:00 2001 From: Never M Date: Tue, 29 Jun 2021 13:36:26 +0300 Subject: [PATCH 34/55] Move mock dialer to network package --- .../setupclient/mock_route_group_dialer.go | 40 ------------------- pkg/setup/testing_test.go | 6 +-- pkg/snet/conn.go | 1 - pkg/snet/dialer.go | 16 -------- pkg/snet/listener.go | 1 - pkg/snet/network.go | 3 -- pkg/snet/network_test.go | 1 - pkg/snet/snettest/env.go | 9 ----- .../network}/mock_dialer.go | 2 +- pkg/transport/network/network.go | 2 +- 10 files changed, 5 insertions(+), 76 deletions(-) delete mode 100644 pkg/setup/setupclient/mock_route_group_dialer.go delete mode 100644 pkg/snet/conn.go delete mode 100644 pkg/snet/dialer.go delete mode 100644 pkg/snet/listener.go delete mode 100644 pkg/snet/network.go delete mode 100644 pkg/snet/network_test.go rename pkg/{snet => transport/network}/mock_dialer.go (98%) diff --git a/pkg/setup/setupclient/mock_route_group_dialer.go b/pkg/setup/setupclient/mock_route_group_dialer.go deleted file mode 100644 index 6d098af92..000000000 --- a/pkg/setup/setupclient/mock_route_group_dialer.go +++ /dev/null @@ -1,40 +0,0 @@ -// Code generated by mockery v1.0.0. DO NOT EDIT. - -package setupclient - -import ( - context "context" - - cipher "github.com/skycoin/dmsg/cipher" - logging "github.com/skycoin/skycoin/src/util/logging" - mock "github.com/stretchr/testify/mock" - - routing "github.com/skycoin/skywire/pkg/routing" - snet "github.com/skycoin/skywire/pkg/snet" -) - -// MockRouteGroupDialer is an autogenerated mock type for the RouteGroupDialer type -type MockRouteGroupDialer struct { - mock.Mock -} - -// Dial provides a mock function with given fields: ctx, log, n, setupNodes, req -func (_m *MockRouteGroupDialer) Dial(ctx context.Context, log *logging.Logger, n *snet.Network, setupNodes []cipher.PubKey, req routing.BidirectionalRoute) (routing.EdgeRules, error) { - ret := _m.Called(ctx, log, n, setupNodes, req) - - var r0 routing.EdgeRules - if rf, ok := ret.Get(0).(func(context.Context, *logging.Logger, *snet.Network, []cipher.PubKey, routing.BidirectionalRoute) routing.EdgeRules); ok { - r0 = rf(ctx, log, n, setupNodes, req) - } else { - r0 = ret.Get(0).(routing.EdgeRules) - } - - var r1 error - if rf, ok := ret.Get(1).(func(context.Context, *logging.Logger, *snet.Network, []cipher.PubKey, routing.BidirectionalRoute) error); ok { - r1 = rf(ctx, log, n, setupNodes, req) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} diff --git a/pkg/setup/testing_test.go b/pkg/setup/testing_test.go index e9d263eb8..c539bb634 100644 --- a/pkg/setup/testing_test.go +++ b/pkg/setup/testing_test.go @@ -17,11 +17,11 @@ import ( "github.com/skycoin/skywire/pkg/router/routerclient" "github.com/skycoin/skywire/pkg/routing" - "github.com/skycoin/skywire/pkg/snet" + "github.com/skycoin/skywire/pkg/transport/network" ) // creates a mock dialer -func newMockDialer(t *testing.T, gateways map[cipher.PubKey]interface{}) snet.Dialer { +func newMockDialer(t *testing.T, gateways map[cipher.PubKey]interface{}) network.Dialer { newRPCConn := func(gw interface{}) net.Conn { connC, connS := net.Pipe() t.Cleanup(func() { @@ -38,7 +38,7 @@ func newMockDialer(t *testing.T, gateways map[cipher.PubKey]interface{}) snet.Di if gateways == nil { conn := newRPCConn(new(mockGatewayForDialer)) - dialer := new(snet.MockDialer) + dialer := new(network.MockDialer) dialer.On("Dial", mock.Anything, mock.Anything, mock.Anything).Return(conn, nil) return dialer } diff --git a/pkg/snet/conn.go b/pkg/snet/conn.go deleted file mode 100644 index 941963d2f..000000000 --- a/pkg/snet/conn.go +++ /dev/null @@ -1 +0,0 @@ -package snet diff --git a/pkg/snet/dialer.go b/pkg/snet/dialer.go deleted file mode 100644 index 728edece3..000000000 --- a/pkg/snet/dialer.go +++ /dev/null @@ -1,16 +0,0 @@ -package snet - -import ( - "context" - "net" - - "github.com/skycoin/dmsg/cipher" -) - -//go:generate mockery -name Dialer -case underscore -inpkg - -// Dialer is an entity that can be dialed and asked for its type. -type Dialer interface { - Dial(ctx context.Context, remote cipher.PubKey, port uint16) (net.Conn, error) - Type() string -} diff --git a/pkg/snet/listener.go b/pkg/snet/listener.go deleted file mode 100644 index 941963d2f..000000000 --- a/pkg/snet/listener.go +++ /dev/null @@ -1 +0,0 @@ -package snet diff --git a/pkg/snet/network.go b/pkg/snet/network.go deleted file mode 100644 index 08ee9327c..000000000 --- a/pkg/snet/network.go +++ /dev/null @@ -1,3 +0,0 @@ -package snet - -type Network struct{} diff --git a/pkg/snet/network_test.go b/pkg/snet/network_test.go deleted file mode 100644 index 941963d2f..000000000 --- a/pkg/snet/network_test.go +++ /dev/null @@ -1 +0,0 @@ -package snet diff --git a/pkg/snet/snettest/env.go b/pkg/snet/snettest/env.go index 4f13b5d6c..afd705e56 100644 --- a/pkg/snet/snettest/env.go +++ b/pkg/snet/snettest/env.go @@ -6,11 +6,7 @@ package snettest import ( "testing" - "github.com/skycoin/dmsg" "github.com/skycoin/dmsg/cipher" - "github.com/skycoin/dmsg/disc" - - "github.com/skycoin/skywire/pkg/snet" ) // KeyPair holds a public/private key pair. @@ -36,11 +32,6 @@ func GenKeyPairs(n int) []KeyPair { // Env contains a network test environment. type Env struct { - DmsgD disc.APIClient - DmsgS *dmsg.Server - Keys []KeyPair - Nets []*snet.Network - teardown func() } // NewEnv creates a `network.Network` test environment. diff --git a/pkg/snet/mock_dialer.go b/pkg/transport/network/mock_dialer.go similarity index 98% rename from pkg/snet/mock_dialer.go rename to pkg/transport/network/mock_dialer.go index 7cbb57833..e0cef6585 100644 --- a/pkg/snet/mock_dialer.go +++ b/pkg/transport/network/mock_dialer.go @@ -1,6 +1,6 @@ // Code generated by mockery v1.0.0. DO NOT EDIT. -package snet +package network import ( context "context" diff --git a/pkg/transport/network/network.go b/pkg/transport/network/network.go index 6d8049321..101fea169 100644 --- a/pkg/transport/network/network.go +++ b/pkg/transport/network/network.go @@ -52,7 +52,7 @@ func (a addr) String() string { // Dialer is an entity that can be dialed and asked for its type. type Dialer interface { Dial(ctx context.Context, remote cipher.PubKey, port uint16) (net.Conn, error) - Type() Type + Type() string } var ( From f5ebb9f58ad8bca595919e90082531bce1af167d Mon Sep 17 00:00:00 2001 From: Never M Date: Tue, 29 Jun 2021 15:31:30 +0300 Subject: [PATCH 35/55] Switch to network.Type --- pkg/router/routerclient/client.go | 4 +- pkg/router/routerclient/dmsg_wrapper.go | 5 +- pkg/router/routerclient/map.go | 5 +- pkg/servicedisc/autoconnect.go | 6 +- pkg/setup/id_reserver.go | 4 +- pkg/setup/node.go | 6 +- pkg/setup/rpc_gateway.go | 4 +- pkg/snet/directtp/tptypes/tptypes.go | 10 ---- pkg/transport/entry.go | 9 +-- pkg/transport/managed_transport.go | 11 ++-- pkg/transport/manager.go | 61 +++++++++----------- pkg/transport/network/addrresolver/client.go | 2 +- pkg/transport/network/connection.go | 2 +- pkg/transport/network/listener.go | 4 +- pkg/transport/setup/rpc.go | 5 +- pkg/transport/transport.go | 4 +- pkg/visor/api.go | 7 ++- pkg/visor/init.go | 7 +-- pkg/visor/rpc.go | 3 +- pkg/visor/rpc_client.go | 17 ++++-- 20 files changed, 83 insertions(+), 93 deletions(-) diff --git a/pkg/router/routerclient/client.go b/pkg/router/routerclient/client.go index ac920464b..f1976b27f 100644 --- a/pkg/router/routerclient/client.go +++ b/pkg/router/routerclient/client.go @@ -12,7 +12,7 @@ import ( "github.com/skycoin/skywire/pkg/routing" "github.com/skycoin/skywire/pkg/skyenv" - "github.com/skycoin/skywire/pkg/snet" + "github.com/skycoin/skywire/pkg/transport/network" ) // RPCName is the RPC gateway object name. @@ -26,7 +26,7 @@ type Client struct { } // NewClient creates a new Client. -func NewClient(ctx context.Context, dialer snet.Dialer, rPK cipher.PubKey) (*Client, error) { +func NewClient(ctx context.Context, dialer network.Dialer, rPK cipher.PubKey) (*Client, error) { s, err := dialer.Dial(ctx, rPK, skyenv.DmsgAwaitSetupPort) if err != nil { return nil, fmt.Errorf("dial %v@%v: %w", rPK, skyenv.DmsgAwaitSetupPort, err) diff --git a/pkg/router/routerclient/dmsg_wrapper.go b/pkg/router/routerclient/dmsg_wrapper.go index 245f58158..558692614 100644 --- a/pkg/router/routerclient/dmsg_wrapper.go +++ b/pkg/router/routerclient/dmsg_wrapper.go @@ -6,12 +6,11 @@ import ( "github.com/skycoin/dmsg" "github.com/skycoin/dmsg/cipher" - - "github.com/skycoin/skywire/pkg/snet" + "github.com/skycoin/skywire/pkg/transport/network" ) // WrapDmsgClient wraps a dmsg client to implement snet.Dialer -func WrapDmsgClient(dmsgC *dmsg.Client) snet.Dialer { +func WrapDmsgClient(dmsgC *dmsg.Client) network.Dialer { return &dmsgClientDialer{Client: dmsgC} } diff --git a/pkg/router/routerclient/map.go b/pkg/router/routerclient/map.go index 7745c5927..e20ea392c 100644 --- a/pkg/router/routerclient/map.go +++ b/pkg/router/routerclient/map.go @@ -4,8 +4,7 @@ import ( "context" "github.com/skycoin/dmsg/cipher" - - "github.com/skycoin/skywire/pkg/snet" + "github.com/skycoin/skywire/pkg/transport/network" ) // Map is a map of router RPC clients associated with the router's visor PK. @@ -18,7 +17,7 @@ type dialResult struct { // MakeMap makes a Map of the router clients, where the key is the router's visor public key. // It creates these router clients by dialing to them concurrently. -func MakeMap(ctx context.Context, dialer snet.Dialer, pks []cipher.PubKey) (Map, error) { +func MakeMap(ctx context.Context, dialer network.Dialer, pks []cipher.PubKey) (Map, error) { ctx, cancel := context.WithCancel(ctx) defer cancel() diff --git a/pkg/servicedisc/autoconnect.go b/pkg/servicedisc/autoconnect.go index 0012d9fc7..07aa5abc7 100644 --- a/pkg/servicedisc/autoconnect.go +++ b/pkg/servicedisc/autoconnect.go @@ -8,8 +8,8 @@ import ( "github.com/skycoin/skycoin/src/util/logging" "github.com/skycoin/skywire/internal/netutil" - "github.com/skycoin/skywire/pkg/snet/directtp/tptypes" "github.com/skycoin/skywire/pkg/transport" + "github.com/skycoin/skywire/pkg/transport/network" ) const ( @@ -59,8 +59,8 @@ func (a *autoconnector) Run(ctx context.Context) error { absent := a.filterDuplicates(addresses, tps) for _, pk := range absent { a.log.WithField("pk", pk).Infoln("Adding transport to public visor") - logger := a.log.WithField("pk", pk).WithField("type", tptypes.STCPR) - if _, err := a.tm.SaveTransport(ctx, pk, tptypes.STCPR, transport.LabelAutomatic); err != nil { + logger := a.log.WithField("pk", pk).WithField("type", string(network.STCPR)) + if _, err := a.tm.SaveTransport(ctx, pk, network.STCPR, transport.LabelAutomatic); err != nil { logger.WithError(err).Warnln("Failed to add transport to public visor") continue } diff --git a/pkg/setup/id_reserver.go b/pkg/setup/id_reserver.go index 03441c984..1df6a22ae 100644 --- a/pkg/setup/id_reserver.go +++ b/pkg/setup/id_reserver.go @@ -12,7 +12,7 @@ import ( "github.com/skycoin/skywire/pkg/router/routerclient" "github.com/skycoin/skywire/pkg/routing" - "github.com/skycoin/skywire/pkg/snet" + "github.com/skycoin/skywire/pkg/transport/network" ) // ErrNoKey is returned when key is not found. @@ -51,7 +51,7 @@ type idReserver struct { // NewIDReserver creates a new route ID reserver from a dialer and a slice of paths. // The exact number of route IDs to reserve from each router is determined from the slice of paths. -func NewIDReserver(ctx context.Context, dialer snet.Dialer, paths [][]routing.Hop) (IDReserver, error) { +func NewIDReserver(ctx context.Context, dialer network.Dialer, paths [][]routing.Hop) (IDReserver, error) { var total int // the total number of route IDs we reserve from the routers // Prepare 'rec': A map representing the number of expected rules per visor PK. diff --git a/pkg/setup/node.go b/pkg/setup/node.go index 7c6b5c419..2453a75d5 100644 --- a/pkg/setup/node.go +++ b/pkg/setup/node.go @@ -16,7 +16,7 @@ import ( "github.com/skycoin/skywire/pkg/routing" "github.com/skycoin/skywire/pkg/setup/setupmetrics" "github.com/skycoin/skywire/pkg/skyenv" - "github.com/skycoin/skywire/pkg/snet" + "github.com/skycoin/skywire/pkg/transport/network" ) var log = logging.MustGetLogger("setup_node") @@ -106,7 +106,7 @@ func (sn *Node) Serve(ctx context.Context, m setupmetrics.Metrics) error { // * Intermediary rules are broadcasted to the intermediary routers. // * Edge rules are broadcasted to the responding router. // * Edge rules is returned (to the initiating router). -func CreateRouteGroup(ctx context.Context, dialer snet.Dialer, biRt routing.BidirectionalRoute, metrics setupmetrics.Metrics) (resp routing.EdgeRules, err error) { +func CreateRouteGroup(ctx context.Context, dialer network.Dialer, biRt routing.BidirectionalRoute, metrics setupmetrics.Metrics) (resp routing.EdgeRules, err error) { log := logging.MustGetLogger(fmt.Sprintf("request:%s->%s", biRt.Desc.SrcPK(), biRt.Desc.DstPK())) log.Info("Processing request.") defer metrics.RecordRoute()(&err) @@ -158,7 +158,7 @@ func CreateRouteGroup(ctx context.Context, dialer snet.Dialer, biRt routing.Bidi // ReserveRouteIDs dials to all routers and reserves required route IDs from them. // The number of route IDs to be reserved per router, is extrapolated from the 'route' input. -func ReserveRouteIDs(ctx context.Context, log logrus.FieldLogger, dialer snet.Dialer, route routing.BidirectionalRoute) (idR IDReserver, err error) { +func ReserveRouteIDs(ctx context.Context, log logrus.FieldLogger, dialer network.Dialer, route routing.BidirectionalRoute) (idR IDReserver, err error) { log.Debug("Reserving route IDs...") defer func() { if err != nil { diff --git a/pkg/setup/rpc_gateway.go b/pkg/setup/rpc_gateway.go index dd8ebf750..28edfaf1f 100644 --- a/pkg/setup/rpc_gateway.go +++ b/pkg/setup/rpc_gateway.go @@ -10,7 +10,7 @@ import ( "github.com/skycoin/skywire/pkg/routing" "github.com/skycoin/skywire/pkg/setup/setupmetrics" - "github.com/skycoin/skywire/pkg/snet" + "github.com/skycoin/skywire/pkg/transport/network" ) // RPCGateway is a RPC interface for setup node. @@ -19,7 +19,7 @@ type RPCGateway struct { Ctx context.Context Conn net.Conn ReqPK cipher.PubKey - Dialer snet.Dialer + Dialer network.Dialer Timeout time.Duration } diff --git a/pkg/snet/directtp/tptypes/tptypes.go b/pkg/snet/directtp/tptypes/tptypes.go index bf0f385ef..841d4ca05 100644 --- a/pkg/snet/directtp/tptypes/tptypes.go +++ b/pkg/snet/directtp/tptypes/tptypes.go @@ -1,11 +1 @@ package tptypes - -const ( - // STCP is a type of a transport that works via TCP and resolves addresses using PK table. - STCP = "stcp" - // STCPR is a type of a transport that works via TCP and resolves addresses using address-resolver service. - STCPR = "stcpr" - // SUDPH is a type of a transport that works via UDP, resolves addresses using address-resolver service, - // and uses UDP hole punching. - SUDPH = "sudph" -) diff --git a/pkg/transport/entry.go b/pkg/transport/entry.go index f474e6fb1..39444cae5 100644 --- a/pkg/transport/entry.go +++ b/pkg/transport/entry.go @@ -7,6 +7,7 @@ import ( "github.com/google/uuid" "github.com/skycoin/dmsg/cipher" + "github.com/skycoin/skywire/pkg/transport/network" ) var ( @@ -38,7 +39,7 @@ type Entry struct { Edges [2]cipher.PubKey `json:"edges"` // Type represents the transport type. - Type string `json:"type"` + Type network.Type `json:"type"` // Public determines whether the transport is to be exposed to other nodes or not. // Public transports are to be registered in the Transport Discovery. @@ -48,10 +49,10 @@ type Entry struct { } // MakeEntry creates a new transport entry -func MakeEntry(initiator, target cipher.PubKey, tpType string, public bool, label Label) Entry { +func MakeEntry(initiator, target cipher.PubKey, netType network.Type, public bool, label Label) Entry { entry := Entry{ - ID: MakeTransportID(initiator, target, tpType), - Type: tpType, + ID: MakeTransportID(initiator, target, netType), + Type: netType, Public: public, Label: label, } diff --git a/pkg/transport/managed_transport.go b/pkg/transport/managed_transport.go index b9c226856..31f6c3b4f 100644 --- a/pkg/transport/managed_transport.go +++ b/pkg/transport/managed_transport.go @@ -49,7 +49,6 @@ type ManagedTransportConfig struct { DC DiscoveryClient LS LogStore RemotePK cipher.PubKey - NetName string AfterClosed TPCloseCallback TransportLabel Label } @@ -62,7 +61,6 @@ type ManagedTransport struct { log *logging.Logger rPK cipher.PubKey - netName string Entry Entry LogEntry *LogEntry logUpdates uint32 @@ -101,11 +99,10 @@ func NewManagedTransport(conf ManagedTransportConfig, isInitiator bool) *Managed mt := &ManagedTransport{ log: logging.MustGetLogger(fmt.Sprintf("tp:%s", conf.RemotePK.String()[:6])), rPK: conf.RemotePK, - netName: conf.NetName, dc: conf.DC, ls: conf.LS, client: conf.client, - Entry: MakeEntry(initiator, target, conf.NetName, true, conf.TransportLabel), + Entry: MakeEntry(initiator, target, conf.client.Type(), true, conf.TransportLabel), LogEntry: new(LogEntry), connCh: make(chan struct{}, 1), done: make(chan struct{}), @@ -259,7 +256,7 @@ func (mt *ManagedTransport) close() { mt.afterClosedMu.RUnlock() if afterClosed != nil { - afterClosed(mt.netName, mt.remoteAddr) + afterClosed(mt.Type(), mt.remoteAddr) } } @@ -275,7 +272,7 @@ func (mt *ManagedTransport) Accept(ctx context.Context, conn *network.Conn) erro mt.connMx.Lock() defer mt.connMx.Unlock() - if conn.Network() != mt.netName { + if conn.Network() != mt.Type() { return ErrWrongNetwork } @@ -639,4 +636,4 @@ func (mt *ManagedTransport) logMod() bool { func (mt *ManagedTransport) Remote() cipher.PubKey { return mt.rPK } // Type returns the transport type. -func (mt *ManagedTransport) Type() string { return mt.netName } +func (mt *ManagedTransport) Type() network.Type { return mt.client.Type() } diff --git a/pkg/transport/manager.go b/pkg/transport/manager.go index 1dafe8a2f..9b4dad76e 100644 --- a/pkg/transport/manager.go +++ b/pkg/transport/manager.go @@ -19,7 +19,7 @@ import ( ) // TPCloseCallback triggers after a session is closed. -type TPCloseCallback func(network, addr string) +type TPCloseCallback func(netType network.Type, addr string) // ManagerConfig configures a Manager. type ManagerConfig struct { @@ -216,7 +216,6 @@ func (tm *Manager) acceptTransport(ctx context.Context, lis *network.Listener) e DC: tm.Conf.DiscoveryClient, LS: tm.Conf.LogStore, RemotePK: conn.RemotePK(), - NetName: lis.Network(), AfterClosed: tm.afterTPClosed, TransportLabel: LabelUser, }, false) @@ -251,23 +250,23 @@ var ErrUnknownNetwork = errors.New("unknown network type") // IsKnownNetwork returns true when netName is a known // network type that we are able to operate in -func (tm *Manager) IsKnownNetwork(netName string) bool { - _, ok := tm.netClients[network.Type(netName)] +func (tm *Manager) IsKnownNetwork(netName network.Type) bool { + _, ok := tm.netClients[netName] return ok } // GetTransport gets transport entity to the given remote -func (tm *Manager) GetTransport(remote cipher.PubKey, netName string) (*ManagedTransport, error) { +func (tm *Manager) GetTransport(remote cipher.PubKey, netType network.Type) (*ManagedTransport, error) { tm.mx.RLock() defer tm.mx.RUnlock() - if !tm.IsKnownNetwork(netName) { + if !tm.IsKnownNetwork(netType) { return nil, ErrUnknownNetwork } - tpID := tm.tpIDFromPK(remote, netName) + tpID := tm.tpIDFromPK(remote, netType) tp, ok := tm.tps[tpID] if !ok { - return nil, fmt.Errorf("transport to %s of type %s error: %w", remote, netName, ErrNotFound) + return nil, fmt.Errorf("transport to %s of type %s error: %w", remote, netType, ErrNotFound) } return tp, nil } @@ -295,22 +294,20 @@ func (tm *Manager) GetTransportsByLabel(label Label) []*ManagedTransport { } // SaveTransport begins to attempt to establish data transports to the given 'remote' visor. -func (tm *Manager) SaveTransport(ctx context.Context, remote cipher.PubKey, netName string, label Label) (*ManagedTransport, error) { - +func (tm *Manager) SaveTransport(ctx context.Context, remote cipher.PubKey, netType network.Type, label Label) (*ManagedTransport, error) { if tm.isClosing() { return nil, io.ErrClosedPipe } - for { - mTp, err := tm.saveTransport(remote, true, netName, label) + mTp, err := tm.saveTransport(remote, true, netType, label) if err != nil { return nil, fmt.Errorf("save transport: %w", err) } - tm.Logger.Debugf("Dialing transport to %v via %v", mTp.Remote(), mTp.netName) + tm.Logger.Debugf("Dialing transport to %v via %v", mTp.Remote(), mTp.Type()) if err = mTp.Dial(ctx); err != nil { - tm.Logger.Debugf("Error dialing transport to %v via %v: %v", mTp.Remote(), mTp.netName, err) + tm.Logger.Debugf("Error dialing transport to %v via %v: %v", mTp.Remote(), mTp.Type(), err) // This occurs when an old tp is returned by 'tm.saveTransport', meaning a tp of the same transport ID was // just deleted (and has not yet fully closed). Hence, we should close and delete the old tp and try again. if err == ErrNotServing { @@ -339,14 +336,14 @@ func (tm *Manager) SaveTransport(ctx context.Context, remote cipher.PubKey, netN } } -func (tm *Manager) saveTransport(remote cipher.PubKey, initiator bool, netName string, label Label) (*ManagedTransport, error) { +func (tm *Manager) saveTransport(remote cipher.PubKey, initiator bool, netType network.Type, label Label) (*ManagedTransport, error) { tm.mx.Lock() defer tm.mx.Unlock() - if !tm.IsKnownNetwork(netName) { + if !tm.IsKnownNetwork(netType) { return nil, ErrUnknownNetwork } - tpID := tm.tpIDFromPK(remote, netName) + tpID := tm.tpIDFromPK(remote, netType) tm.Logger.Debugf("Initializing TP with ID %s", tpID) oldMTp, ok := tm.tps[tpID] @@ -355,9 +352,9 @@ func (tm *Manager) saveTransport(remote cipher.PubKey, initiator bool, netName s return oldMTp, nil } - client, ok := tm.netClients[network.Type(netName)] + client, ok := tm.netClients[network.Type(netType)] if !ok { - return nil, fmt.Errorf("client not found for the type %s", netName) + return nil, fmt.Errorf("client not found for the type %s", netType) } afterTPClosed := tm.afterTPClosed @@ -367,20 +364,18 @@ func (tm *Manager) saveTransport(remote cipher.PubKey, initiator bool, netName s DC: tm.Conf.DiscoveryClient, LS: tm.Conf.LogStore, RemotePK: remote, - NetName: netName, AfterClosed: afterTPClosed, TransportLabel: label, }, initiator) - if mTp.netName == string(network.STCPR) { - if tm.arClient != nil { - visorData, err := tm.arClient.Resolve(context.Background(), mTp.netName, remote) - if err == nil { - mTp.remoteAddr = visorData.RemoteAddr - } else { - if err != addrresolver.ErrNoEntry { - return nil, fmt.Errorf("failed to resolve %s: %w", remote, err) - } + // todo: do we need this here? Client dial will run resolve anyway + if mTp.Type() == network.STCPR && tm.arClient != nil { + visorData, err := tm.arClient.Resolve(context.Background(), string(mTp.Type()), remote) + if err == nil { + mTp.remoteAddr = visorData.RemoteAddr + } else { + if err != addrresolver.ErrNoEntry { + return nil, fmt.Errorf("failed to resolve %s: %w", remote, err) } } } @@ -389,7 +384,7 @@ func (tm *Manager) saveTransport(remote cipher.PubKey, initiator bool, netName s tm.deleteTransport(mTp.Entry.ID) }() tm.tps[tpID] = mTp - tm.Logger.Infof("saved transport: remote(%s) type(%s) tpID(%s)", remote, netName, tpID) + tm.Logger.Infof("saved transport: remote(%s) type(%s) tpID(%s)", remote, netType, tpID) return mTp, nil } @@ -401,7 +396,7 @@ func (tm *Manager) STCPRRemoteAddrs() []string { defer tm.mx.RUnlock() for _, tp := range tm.tps { - if tp.Entry.Type == string(network.STCPR) && tp.remoteAddr != "" { + if tp.Entry.Type == network.STCPR && tp.remoteAddr != "" { addrs = append(addrs, tp.remoteAddr) } } @@ -518,6 +513,6 @@ func (tm *Manager) isClosing() bool { } } -func (tm *Manager) tpIDFromPK(pk cipher.PubKey, netName string) uuid.UUID { - return MakeTransportID(tm.Conf.PubKey, pk, netName) +func (tm *Manager) tpIDFromPK(pk cipher.PubKey, netType network.Type) uuid.UUID { + return MakeTransportID(tm.Conf.PubKey, pk, netType) } diff --git a/pkg/transport/network/addrresolver/client.go b/pkg/transport/network/addrresolver/client.go index 6aca51768..3f04b18d8 100644 --- a/pkg/transport/network/addrresolver/client.go +++ b/pkg/transport/network/addrresolver/client.go @@ -57,7 +57,7 @@ type APIClient interface { io.Closer BindSTCPR(ctx context.Context, port string) error BindSUDPH(filter *pfilter.PacketFilter) (<-chan RemoteVisor, error) - Resolve(ctx context.Context, tType string, pk cipher.PubKey) (VisorData, error) + Resolve(ctx context.Context, netType string, pk cipher.PubKey) (VisorData, error) Health(ctx context.Context) (int, error) } diff --git a/pkg/transport/network/connection.go b/pkg/transport/network/connection.go index 6ae8a8b3e..f6daafabf 100644 --- a/pkg/transport/network/connection.go +++ b/pkg/transport/network/connection.go @@ -90,4 +90,4 @@ func (c *Conn) RemotePort() uint16 { return c.rAddr.Port } // Network returns network of connection // todo: consider switching to Type instead of string -func (c *Conn) Network() string { return string(c.connType) } +func (c *Conn) Network() Type { return c.connType } diff --git a/pkg/transport/network/listener.go b/pkg/transport/network/listener.go index 5a5743d60..c9ae06953 100644 --- a/pkg/transport/network/listener.go +++ b/pkg/transport/network/listener.go @@ -70,8 +70,8 @@ func (l *Listener) Addr() net.Addr { // Network returns network type // todo: consider switching to Type instead of string -func (l *Listener) Network() string { - return string(l.network) +func (l *Listener) Network() Type { + return l.network } // Introduce is used by Client to introduce a new connection to this Listener diff --git a/pkg/transport/setup/rpc.go b/pkg/transport/setup/rpc.go index d8989b440..625226157 100644 --- a/pkg/transport/setup/rpc.go +++ b/pkg/transport/setup/rpc.go @@ -9,6 +9,7 @@ import ( "github.com/skycoin/skycoin/src/util/logging" "github.com/skycoin/skywire/pkg/transport" + "github.com/skycoin/skywire/pkg/transport/network" ) // TransportGateway that exposes methods to be used via RPC @@ -20,7 +21,7 @@ type TransportGateway struct { // TransportRequest to perform an action over RPC type TransportRequest struct { RemotePK cipher.PubKey - Type string + Type network.Type } // UUIDRequest contains id in UUID format @@ -33,7 +34,7 @@ type TransportResponse struct { ID uuid.UUID Local cipher.PubKey Remote cipher.PubKey - Type string + Type network.Type IsUp bool } diff --git a/pkg/transport/transport.go b/pkg/transport/transport.go index d4af4a0e4..8d058179a 100644 --- a/pkg/transport/transport.go +++ b/pkg/transport/transport.go @@ -9,6 +9,7 @@ import ( "github.com/google/uuid" "github.com/skycoin/dmsg/cipher" "github.com/skycoin/skycoin/src/util/logging" + "github.com/skycoin/skywire/pkg/transport/network" ) var log = logging.MustGetLogger("transport") @@ -17,7 +18,8 @@ var log = logging.MustGetLogger("transport") // Generated uuid is: // - always the same for a given pair // - GenTransportUUID(keyA,keyB) == GenTransportUUID(keyB, keyA) -func MakeTransportID(keyA, keyB cipher.PubKey, tpType string) uuid.UUID { +func MakeTransportID(keyA, keyB cipher.PubKey, netType network.Type) uuid.UUID { + tpType := string(netType) keys := SortEdges(keyA, keyB) b := make([]byte, 33*2+len(tpType)) i := 0 diff --git a/pkg/visor/api.go b/pkg/visor/api.go index ededd8d4a..c98da58af 100644 --- a/pkg/visor/api.go +++ b/pkg/visor/api.go @@ -20,6 +20,7 @@ import ( "github.com/skycoin/skywire/pkg/routing" "github.com/skycoin/skywire/pkg/skyenv" "github.com/skycoin/skywire/pkg/transport" + "github.com/skycoin/skywire/pkg/transport/network" "github.com/skycoin/skywire/pkg/util/netutil" "github.com/skycoin/skywire/pkg/util/updater" "github.com/skycoin/skywire/pkg/visor/dmsgtracker" @@ -478,10 +479,10 @@ func (v *Visor) TransportTypes() ([]string, error) { func (v *Visor) Transports(types []string, pks []cipher.PubKey, logs bool) ([]*TransportSummary, error) { var result []*TransportSummary - typeIncluded := func(tType string) bool { + typeIncluded := func(tType network.Type) bool { if types != nil { for _, ft := range types { - if tType == ft { + if string(tType) == ft { return true } } @@ -532,7 +533,7 @@ func (v *Visor) AddTransport(remote cipher.PubKey, tpType string, public bool, t v.log.Debugf("Saving transport to %v via %v", remote, tpType) - tp, err := v.tpM.SaveTransport(ctx, remote, tpType, transport.LabelUser) + tp, err := v.tpM.SaveTransport(ctx, remote, network.Type(tpType), transport.LabelUser) if err != nil { return nil, err } diff --git a/pkg/visor/init.go b/pkg/visor/init.go index 793a4da4e..888b35b53 100644 --- a/pkg/visor/init.go +++ b/pkg/visor/init.go @@ -28,7 +28,6 @@ import ( "github.com/skycoin/skywire/pkg/servicedisc" "github.com/skycoin/skywire/pkg/setup/setupclient" "github.com/skycoin/skywire/pkg/skyenv" - "github.com/skycoin/skywire/pkg/snet/directtp/tptypes" "github.com/skycoin/skywire/pkg/snet/dmsgc" "github.com/skycoin/skywire/pkg/transport" "github.com/skycoin/skywire/pkg/transport/network" @@ -268,9 +267,9 @@ func initTransport(ctx context.Context, v *Visor, log *logging.Logger) error { } // todo: move to transport manager - tpM.OnAfterTPClosed(func(network, addr string) { - if network == tptypes.STCPR && addr != "" { - data := appevent.TCPCloseData{RemoteNet: network, RemoteAddr: addr} + tpM.OnAfterTPClosed(func(netType network.Type, addr string) { + if netType == network.STCPR && addr != "" { + data := appevent.TCPCloseData{RemoteNet: string(netType), RemoteAddr: addr} event := appevent.NewEvent(appevent.TCPClose, data) if err := v.ebc.Broadcast(context.Background(), event); err != nil { v.log.WithError(err).Errorln("Failed to broadcast TCPClose event") diff --git a/pkg/visor/rpc.go b/pkg/visor/rpc.go index 43bb84765..34f0bcb9b 100644 --- a/pkg/visor/rpc.go +++ b/pkg/visor/rpc.go @@ -14,6 +14,7 @@ import ( "github.com/skycoin/skywire/pkg/app/launcher" "github.com/skycoin/skywire/pkg/routing" "github.com/skycoin/skywire/pkg/transport" + "github.com/skycoin/skywire/pkg/transport/network" "github.com/skycoin/skywire/pkg/util/rpcutil" "github.com/skycoin/skywire/pkg/util/updater" ) @@ -121,7 +122,7 @@ type TransportSummary struct { ID uuid.UUID `json:"id"` Local cipher.PubKey `json:"local_pk"` Remote cipher.PubKey `json:"remote_pk"` - Type string `json:"type"` + Type network.Type `json:"type"` Log *transport.LogEntry `json:"log,omitempty"` IsSetup bool `json:"is_setup"` IsUp bool `json:"is_up"` diff --git a/pkg/visor/rpc_client.go b/pkg/visor/rpc_client.go index 7545c3ef3..174d1bbed 100644 --- a/pkg/visor/rpc_client.go +++ b/pkg/visor/rpc_client.go @@ -27,6 +27,7 @@ import ( "github.com/skycoin/skywire/pkg/skyenv" "github.com/skycoin/skywire/pkg/snet/snettest" "github.com/skycoin/skywire/pkg/transport" + "github.com/skycoin/skywire/pkg/transport/network" "github.com/skycoin/skywire/pkg/util/updater" ) @@ -445,7 +446,7 @@ func (rc *rpcClient) UpdateStatus() (string, error) { type mockRPCClient struct { startedAt time.Time o *Overview - tpTypes []string + tpTypes []network.Type rt routing.Table logS appcommon.LogStore sync.RWMutex @@ -455,7 +456,7 @@ type mockRPCClient struct { func NewMockRPCClient(r *rand.Rand, maxTps int, maxRules int) (cipher.PubKey, API, error) { log := logging.MustGetLogger("mock-rpc-client") - types := []string{"messaging", "native"} + types := []network.Type{"messaging", "native"} localPK, _ := cipher.GenerateKeyPair() log.Infof("generating mock client with: localPK(%s) maxTps(%d) maxRules(%d)", localPK, maxTps, maxRules) @@ -759,7 +760,11 @@ func (mc *mockRPCClient) GetAppConnectionsSummary(_ string) ([]appserver.Connect // TransportTypes implements API. func (mc *mockRPCClient) TransportTypes() ([]string, error) { - return mc.tpTypes, nil + var res []string + for _, tptype := range mc.tpTypes { + res = append(res, string(tptype)) + } + return res, nil } // Transports implements API. @@ -770,7 +775,7 @@ func (mc *mockRPCClient) Transports(types []string, pks []cipher.PubKey, logs bo tp := tp if types != nil { for _, reqT := range types { - if tp.Type == reqT { + if string(tp.Type) == reqT { goto TypeOK } } @@ -817,10 +822,10 @@ func (mc *mockRPCClient) Transport(tid uuid.UUID) (*TransportSummary, error) { // AddTransport implements API. func (mc *mockRPCClient) AddTransport(remote cipher.PubKey, tpType string, _ bool, _ time.Duration) (*TransportSummary, error) { summary := &TransportSummary{ - ID: transport.MakeTransportID(mc.o.PubKey, remote, tpType), + ID: transport.MakeTransportID(mc.o.PubKey, remote, network.Type(tpType)), Local: mc.o.PubKey, Remote: remote, - Type: tpType, + Type: network.Type(tpType), Log: new(transport.LogEntry), } return summary, mc.do(true, func() error { From 14934ba3e6ba67178731e6469a20f5cf700467b1 Mon Sep 17 00:00:00 2001 From: Never M Date: Tue, 29 Jun 2021 15:48:34 +0300 Subject: [PATCH 36/55] Get rid of transport close callback --- pkg/app/appevent/utils.go | 9 ++++++ pkg/transport/managed_transport.go | 46 +++++++++++------------------- pkg/transport/manager.go | 32 ++++++--------------- pkg/visor/init.go | 13 +-------- 4 files changed, 35 insertions(+), 65 deletions(-) diff --git a/pkg/app/appevent/utils.go b/pkg/app/appevent/utils.go index 6f25ce978..064627400 100644 --- a/pkg/app/appevent/utils.go +++ b/pkg/app/appevent/utils.go @@ -9,6 +9,15 @@ func (eb *Broadcaster) SendTCPDial(ctx context.Context, remoteNet, remoteAddr st eb.sendEvent(ctx, event) } +// SendTPClose sends transport close event +func (eb *Broadcaster) SendTPClose(ctx context.Context, netType, addr string) { + data := TCPCloseData{RemoteNet: string(netType), RemoteAddr: addr} + event := NewEvent(TCPClose, data) + if err := eb.Broadcast(context.Background(), event); err != nil { + eb.log.WithError(err).Errorln("Failed to broadcast TCPClose event") + } +} + func (eb *Broadcaster) sendEvent(_ context.Context, event *Event) { err := eb.Broadcast(context.Background(), event) //nolint:errcheck if err != nil { diff --git a/pkg/transport/managed_transport.go b/pkg/transport/managed_transport.go index 31f6c3b4f..dda2d10ec 100644 --- a/pkg/transport/managed_transport.go +++ b/pkg/transport/managed_transport.go @@ -16,6 +16,7 @@ import ( "github.com/skycoin/dmsg/netutil" "github.com/skycoin/skycoin/src/util/logging" + "github.com/skycoin/skywire/pkg/app/appevent" "github.com/skycoin/skywire/pkg/routing" "github.com/skycoin/skywire/pkg/skyenv" "github.com/skycoin/skywire/pkg/transport/network" @@ -46,10 +47,10 @@ const ( // ManagedTransportConfig is a configuration for managed transport. type ManagedTransportConfig struct { client network.Client + ebc *appevent.Broadcaster DC DiscoveryClient LS LogStore RemotePK cipher.PubKey - AfterClosed TPCloseCallback TransportLabel Label } @@ -65,8 +66,9 @@ type ManagedTransport struct { LogEntry *LogEntry logUpdates uint32 - dc DiscoveryClient - ls LogStore + dc DiscoveryClient + ls LogStore + ebc *appevent.Broadcaster isUp bool // records last successful status update to discovery isUpErr error // records whether the last status update was successful or not @@ -85,9 +87,6 @@ type ManagedTransport struct { wg sync.WaitGroup remoteAddr string - - afterClosedMu sync.RWMutex - afterClosed TPCloseCallback } // NewManagedTransport creates a new ManagedTransport. @@ -97,16 +96,16 @@ func NewManagedTransport(conf ManagedTransportConfig, isInitiator bool) *Managed initiator, target = target, initiator } mt := &ManagedTransport{ - log: logging.MustGetLogger(fmt.Sprintf("tp:%s", conf.RemotePK.String()[:6])), - rPK: conf.RemotePK, - dc: conf.DC, - ls: conf.LS, - client: conf.client, - Entry: MakeEntry(initiator, target, conf.client.Type(), true, conf.TransportLabel), - LogEntry: new(LogEntry), - connCh: make(chan struct{}, 1), - done: make(chan struct{}), - afterClosed: conf.AfterClosed, + log: logging.MustGetLogger(fmt.Sprintf("tp:%s", conf.RemotePK.String()[:6])), + rPK: conf.RemotePK, + dc: conf.DC, + ls: conf.LS, + client: conf.client, + Entry: MakeEntry(initiator, target, conf.client.Type(), true, conf.TransportLabel), + LogEntry: new(LogEntry), + connCh: make(chan struct{}, 1), + done: make(chan struct{}), + ebc: conf.ebc, } mt.wg.Add(2) return mt @@ -219,12 +218,6 @@ func (mt *ManagedTransport) Serve(readCh chan<- routing.Packet) { } } -func (mt *ManagedTransport) onAfterClosed(f TPCloseCallback) { - mt.afterClosedMu.Lock() - mt.afterClosed = f - mt.afterClosedMu.Unlock() -} - func (mt *ManagedTransport) isServing() bool { select { case <-mt.done: @@ -250,13 +243,8 @@ func (mt *ManagedTransport) Close() (err error) { func (mt *ManagedTransport) close() { mt.disconnect() - - mt.afterClosedMu.RLock() - afterClosed := mt.afterClosed - mt.afterClosedMu.RUnlock() - - if afterClosed != nil { - afterClosed(mt.Type(), mt.remoteAddr) + if mt.Type() == network.STCPR && mt.remoteAddr != "" { + mt.ebc.SendTPClose(context.Background(), string(network.STCPR), mt.remoteAddr) } } diff --git a/pkg/transport/manager.go b/pkg/transport/manager.go index 9b4dad76e..d97c6bdd2 100644 --- a/pkg/transport/manager.go +++ b/pkg/transport/manager.go @@ -12,15 +12,13 @@ import ( "github.com/skycoin/dmsg/cipher" "github.com/skycoin/skycoin/src/util/logging" + "github.com/skycoin/skywire/pkg/app/appevent" "github.com/skycoin/skywire/pkg/routing" "github.com/skycoin/skywire/pkg/skyenv" "github.com/skycoin/skywire/pkg/transport/network" "github.com/skycoin/skywire/pkg/transport/network/addrresolver" ) -// TPCloseCallback triggers after a session is closed. -type TPCloseCallback func(netType network.Type, addr string) - // ManagerConfig configures a Manager. type ManagerConfig struct { PubKey cipher.PubKey @@ -35,6 +33,7 @@ type Manager struct { Conf *ManagerConfig tps map[uuid.UUID]*ManagedTransport arClient addrresolver.APIClient + ebc *appevent.Broadcaster readCh chan routing.Packet mx sync.RWMutex @@ -44,14 +43,13 @@ type Manager struct { closeOnce sync.Once // ensure we only close once. done chan struct{} - afterTPClosed TPCloseCallback - factory network.ClientFactory - netClients map[network.Type]network.Client + factory network.ClientFactory + netClients map[network.Type]network.Client } // NewManager creates a Manager with the provided configuration and transport factories. // 'factories' should be ordered by preference. -func NewManager(log *logging.Logger, arClient addrresolver.APIClient, config *ManagerConfig, factory network.ClientFactory) (*Manager, error) { +func NewManager(log *logging.Logger, arClient addrresolver.APIClient, ebc *appevent.Broadcaster, config *ManagerConfig, factory network.ClientFactory) (*Manager, error) { if log == nil { log = logging.MustGetLogger("tp_manager") } @@ -64,23 +62,11 @@ func NewManager(log *logging.Logger, arClient addrresolver.APIClient, config *Ma netClients: make(map[network.Type]network.Client), arClient: arClient, factory: factory, + ebc: ebc, } return tm, nil } -// OnAfterTPClosed sets callback which will fire after transport gets closed. -func (tm *Manager) OnAfterTPClosed(f TPCloseCallback) { - tm.mx.Lock() - defer tm.mx.Unlock() - - tm.afterTPClosed = f - - // set callback for all already known tps - for _, tp := range tm.tps { - tp.onAfterClosed(f) - } -} - // Serve runs listening loop across all registered factories. func (tm *Manager) Serve(ctx context.Context) { tm.serveOnce.Do(func() { @@ -216,8 +202,8 @@ func (tm *Manager) acceptTransport(ctx context.Context, lis *network.Listener) e DC: tm.Conf.DiscoveryClient, LS: tm.Conf.LogStore, RemotePK: conn.RemotePK(), - AfterClosed: tm.afterTPClosed, TransportLabel: LabelUser, + ebc: tm.ebc, }, false) go func() { @@ -357,14 +343,12 @@ func (tm *Manager) saveTransport(remote cipher.PubKey, initiator bool, netType n return nil, fmt.Errorf("client not found for the type %s", netType) } - afterTPClosed := tm.afterTPClosed - mTp := NewManagedTransport(ManagedTransportConfig{ client: client, + ebc: tm.ebc, DC: tm.Conf.DiscoveryClient, LS: tm.Conf.LogStore, RemotePK: remote, - AfterClosed: afterTPClosed, TransportLabel: label, }, initiator) diff --git a/pkg/visor/init.go b/pkg/visor/init.go index 888b35b53..b583a3fd4 100644 --- a/pkg/visor/init.go +++ b/pkg/visor/init.go @@ -260,23 +260,12 @@ func initTransport(ctx context.Context, v *Visor, log *logging.Logger) error { ARClient: v.arClient, EB: v.ebc, } - tpM, err := transport.NewManager(managerLogger, v.arClient, &tpMConf, factory) + tpM, err := transport.NewManager(managerLogger, v.arClient, v.ebc, &tpMConf, factory) if err != nil { err := fmt.Errorf("failed to start transport manager: %w", err) return err } - // todo: move to transport manager - tpM.OnAfterTPClosed(func(netType network.Type, addr string) { - if netType == network.STCPR && addr != "" { - data := appevent.TCPCloseData{RemoteNet: string(netType), RemoteAddr: addr} - event := appevent.NewEvent(appevent.TCPClose, data) - if err := v.ebc.Broadcast(context.Background(), event); err != nil { - v.log.WithError(err).Errorln("Failed to broadcast TCPClose event") - } - } - }) - ctx, cancel := context.WithCancel(context.Background()) wg := new(sync.WaitGroup) wg.Add(1) From a6c18b5554c6f9bdb69fce3df1334783d5f78af1 Mon Sep 17 00:00:00 2001 From: Never M Date: Tue, 29 Jun 2021 16:11:06 +0300 Subject: [PATCH 37/55] Add dmsg stub --- pkg/transport/network/dmsg.go | 59 +++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 pkg/transport/network/dmsg.go diff --git a/pkg/transport/network/dmsg.go b/pkg/transport/network/dmsg.go new file mode 100644 index 000000000..86fbbc1c5 --- /dev/null +++ b/pkg/transport/network/dmsg.go @@ -0,0 +1,59 @@ +package network + +import ( + "context" + "net" + + "github.com/skycoin/dmsg" + "github.com/skycoin/dmsg/cipher" +) + +type dmsgClient struct { + dmsgC dmsg.Client +} + +// func newDmsgClient(dmsgC dmsg.Client) Client { +// return &dmsgClient{dmsgC: dmsg.Client} +// } + +// Dial implements interface +func (c *dmsgClient) Dial(ctx context.Context, remote cipher.PubKey, port uint16) (*Conn, error) { + panic("not implemented") +} + +// Start implements interface +func (c *dmsgClient) Start() error { + panic("not implemented") +} + +// Listen implements interface +func (c *dmsgClient) Listen(port uint16) (*Listener, error) { + panic("not implemented") +} + +// todo: remove +func (c *dmsgClient) LocalAddr() (net.Addr, error) { + return nil, nil +} + +// PK implements interface +func (c *dmsgClient) PK() cipher.PubKey { + return c.dmsgC.LocalPK() +} + +// SK implements interface +func (c *dmsgClient) SK() cipher.SecKey { + return c.dmsgC.LocalSK() +} + +// Close implements interface +func (c *dmsgClient) Close() error { + // todo: maybe not, the dmsg instance we get is the global one that is used + // in plenty other places + return c.dmsgC.Close() +} + +// Type implements interface +func (c *dmsgClient) Type() Type { + return DMSG +} From 500f769ce9e027827bad4bcce99acc5cfd3d588d Mon Sep 17 00:00:00 2001 From: Never M Date: Wed, 30 Jun 2021 10:13:36 +0300 Subject: [PATCH 38/55] Add Connection and Listener interfaces --- pkg/transport/handshake.go | 10 +++---- pkg/transport/managed_transport.go | 10 +++---- pkg/transport/manager.go | 4 +-- pkg/transport/network/client.go | 26 ++++++++--------- pkg/transport/network/connection.go | 41 +++++++++++++++++++------- pkg/transport/network/listener.go | 45 ++++++++++++++++++++++------- pkg/transport/network/stcp.go | 2 +- pkg/transport/network/stcpr.go | 2 +- pkg/transport/network/sudph.go | 2 +- 9 files changed, 93 insertions(+), 49 deletions(-) diff --git a/pkg/transport/handshake.go b/pkg/transport/handshake.go index 4b121bccc..16e3fdf8e 100644 --- a/pkg/transport/handshake.go +++ b/pkg/transport/handshake.go @@ -23,7 +23,7 @@ const ( responseInvalidEntry ) -func makeEntryFromTpConn(conn *network.Conn, isInitiator bool) Entry { +func makeEntryFromTpConn(conn network.Conn, isInitiator bool) Entry { initiator, target := conn.LocalPK(), conn.RemotePK() if !isInitiator { initiator, target = target, initiator @@ -76,10 +76,10 @@ func receiveAndVerifyEntry(r io.Reader, expected *Entry, remotePK cipher.PubKey) // SettlementHS represents a settlement handshake. // This is the handshake responsible for registering a transport to transport discovery. -type SettlementHS func(ctx context.Context, dc DiscoveryClient, conn *network.Conn, sk cipher.SecKey) error +type SettlementHS func(ctx context.Context, dc DiscoveryClient, conn network.Conn, sk cipher.SecKey) error // Do performs the settlement handshake. -func (hs SettlementHS) Do(ctx context.Context, dc DiscoveryClient, conn *network.Conn, sk cipher.SecKey) (err error) { +func (hs SettlementHS) Do(ctx context.Context, dc DiscoveryClient, conn network.Conn, sk cipher.SecKey) (err error) { done := make(chan struct{}) go func() { err = hs(ctx, dc, conn, sk) @@ -98,7 +98,7 @@ func (hs SettlementHS) Do(ctx context.Context, dc DiscoveryClient, conn *network // The handshake logic only REGISTERS the transport, and does not update the status of the transport. func MakeSettlementHS(init bool) SettlementHS { // initiating logic. - initHS := func(ctx context.Context, dc DiscoveryClient, conn *network.Conn, sk cipher.SecKey) (err error) { + initHS := func(ctx context.Context, dc DiscoveryClient, conn network.Conn, sk cipher.SecKey) (err error) { entry := makeEntryFromTpConn(conn, true) // TODO(evanlinjin): Probably not needed as this is called in mTp already. Need to double check. @@ -138,7 +138,7 @@ func MakeSettlementHS(init bool) SettlementHS { } // responding logic. - respHS := func(ctx context.Context, dc DiscoveryClient, conn *network.Conn, sk cipher.SecKey) error { + respHS := func(ctx context.Context, dc DiscoveryClient, conn network.Conn, sk cipher.SecKey) error { entry := makeEntryFromTpConn(conn, false) // receive, verify and sign entry. diff --git a/pkg/transport/managed_transport.go b/pkg/transport/managed_transport.go index dda2d10ec..c1ed2e22f 100644 --- a/pkg/transport/managed_transport.go +++ b/pkg/transport/managed_transport.go @@ -78,7 +78,7 @@ type ManagedTransport struct { redialMx sync.Mutex client network.Client - conn *network.Conn + conn network.Conn connCh chan struct{} connMx sync.Mutex @@ -256,7 +256,7 @@ func (mt *ManagedTransport) disconnect() { } // Accept accepts a new underlying connection. -func (mt *ManagedTransport) Accept(ctx context.Context, conn *network.Conn) error { +func (mt *ManagedTransport) Accept(ctx context.Context, conn network.Conn) error { mt.connMx.Lock() defer mt.connMx.Unlock() @@ -385,7 +385,7 @@ func (mt *ManagedTransport) isInitiator() bool { <<< UNDERLYING CONNECTION >>> */ -func (mt *ManagedTransport) getConn() *network.Conn { +func (mt *ManagedTransport) getConn() network.Conn { if !mt.isServing() { return nil } @@ -398,7 +398,7 @@ func (mt *ManagedTransport) getConn() *network.Conn { // setConn sets 'mt.conn' (the underlying connection). // If 'mt.conn' is already occupied, close the newly introduced connection. -func (mt *ManagedTransport) setConn(newConn *network.Conn) error { +func (mt *ManagedTransport) setConn(newConn network.Conn) error { if mt.conn != nil { if mt.isLeastSignificantEdge() { @@ -558,7 +558,7 @@ func (mt *ManagedTransport) WritePacket(ctx context.Context, packet routing.Pack func (mt *ManagedTransport) readPacket() (packet routing.Packet, err error) { log := mt.log.WithField("func", "readPacket") - var conn *network.Conn + var conn network.Conn for { if conn = mt.getConn(); conn != nil { break diff --git a/pkg/transport/manager.go b/pkg/transport/manager.go index d97c6bdd2..cc964e4a1 100644 --- a/pkg/transport/manager.go +++ b/pkg/transport/manager.go @@ -113,7 +113,7 @@ func (tm *Manager) runClients(ctx context.Context) { } } -func (tm *Manager) acceptTransports(ctx context.Context, lis *network.Listener) { +func (tm *Manager) acceptTransports(ctx context.Context, lis network.Listener) { defer tm.wg.Done() for { select { @@ -169,7 +169,7 @@ func (tm *Manager) Stcpr() (network.Client, bool) { return c, ok } -func (tm *Manager) acceptTransport(ctx context.Context, lis *network.Listener) error { +func (tm *Manager) acceptTransport(ctx context.Context, lis network.Listener) error { conn, err := lis.AcceptConn() // TODO: tcp panic. if err != nil { return err diff --git a/pkg/transport/network/client.go b/pkg/transport/network/client.go index a6e31f5f4..101bc1500 100644 --- a/pkg/transport/network/client.go +++ b/pkg/transport/network/client.go @@ -25,14 +25,14 @@ import ( // well as listening to incoming connections from other visors type Client interface { // Dial remote visor, that is listening on the given skywire port - Dial(ctx context.Context, remote cipher.PubKey, port uint16) (*Conn, error) + Dial(ctx context.Context, remote cipher.PubKey, port uint16) (Conn, error) // Start initializes the client and prepares it for listening. It is required // to be called to start accepting connections Start() error // Listen on the given skywire port. This can be called multiple times // for different ports for the same client. It requires Start to be called // to start accepting connections - Listen(port uint16) (*Listener, error) + Listen(port uint16) (Listener, error) // todo: remove LocalAddr() (net.Addr, error) // PK returns public key of the visor running this client @@ -65,7 +65,7 @@ func (f *ClientFactory) MakeClient(netType Type) Client { generic := &genericClient{} generic.listenStarted = make(chan struct{}) generic.done = make(chan struct{}) - generic.listeners = make(map[uint16]*Listener) + generic.listeners = make(map[uint16]*listener) generic.log = log generic.porter = p generic.eb = f.EB @@ -104,7 +104,7 @@ type genericClient struct { eb *appevent.Broadcaster connListener net.Listener - listeners map[uint16]*Listener + listeners map[uint16]*listener listenStarted chan struct{} mu sync.RWMutex done chan struct{} @@ -115,7 +115,7 @@ type genericClient struct { // the remote client // The process will perform handshake over raw connection // todo: rename to handshake, initHandshake, skyConnect or smth? -func (c *genericClient) initConnection(ctx context.Context, conn net.Conn, rPK cipher.PubKey, rPort uint16) (*Conn, error) { +func (c *genericClient) initConnection(ctx context.Context, conn net.Conn, rPK cipher.PubKey, rPort uint16) (*conn, error) { lPort, freePort, err := c.porter.ReserveEphemeral(ctx) if err != nil { return nil, err @@ -153,18 +153,18 @@ func (c *genericClient) acceptConnections(lis net.Listener) { // wrapConn performs handshake over provided raw connection and wraps it in // network.Conn type using the data obtained from handshake process -func (c *genericClient) wrapConn(conn net.Conn, hs handshake.Handshake, initiator bool, onClose func()) (*Conn, error) { - lAddr, rAddr, err := hs(conn, time.Now().Add(handshake.Timeout)) +func (c *genericClient) wrapConn(rawConn net.Conn, hs handshake.Handshake, initiator bool, onClose func()) (*conn, error) { + lAddr, rAddr, err := hs(rawConn, time.Now().Add(handshake.Timeout)) if err != nil { - if err := conn.Close(); err != nil { + if err := rawConn.Close(); err != nil { c.log.WithError(err).Warnf("Failed to close connection") } onClose() return nil, err } - c.log.Infof("Sent handshake to %v, local addr %v, remote addr %v", conn.RemoteAddr(), lAddr, rAddr) + c.log.Infof("Sent handshake to %v, local addr %v, remote addr %v", rawConn.RemoteAddr(), lAddr, rAddr) - wrappedConn := &Conn{Conn: conn, lAddr: lAddr, rAddr: rAddr, freePort: onClose, connType: c.netType} + wrappedConn := &conn{Conn: rawConn, lAddr: lAddr, rAddr: rAddr, freePort: onClose, connType: c.netType} err = wrappedConn.encrypt(c.lPK, c.lSK, initiator) if err != nil { return nil, err @@ -212,7 +212,7 @@ func (c *genericClient) LocalAddr() (net.Addr, error) { } // getListener returns listener to specified skywire port -func (c *genericClient) getListener(port uint16) (*Listener, error) { +func (c *genericClient) getListener(port uint16) (*listener, error) { c.mu.Lock() defer c.mu.Unlock() lis, ok := c.listeners[port] @@ -231,7 +231,7 @@ func (c *genericClient) checkListener(port uint16) error { // and is not related to local OS ports. Underlying connection will most likely use // a different port number // Listen requires Serve to be called, which will accept connections to all skywire ports -func (c *genericClient) Listen(port uint16) (*Listener, error) { +func (c *genericClient) Listen(port uint16) (Listener, error) { if c.isClosed() { return nil, io.ErrClosedPipe } @@ -245,7 +245,7 @@ func (c *genericClient) Listen(port uint16) (*Listener, error) { defer c.mu.Unlock() lAddr := dmsg.Addr{PK: c.lPK, Port: port} - lis := NewListener(lAddr, freePort, c.netType) + lis := newListener(lAddr, freePort, c.netType) c.listeners[port] = lis return lis, nil diff --git a/pkg/transport/network/connection.go b/pkg/transport/network/connection.go index f6daafabf..f4d95d069 100644 --- a/pkg/transport/network/connection.go +++ b/pkg/transport/network/connection.go @@ -15,14 +15,35 @@ const encryptHSTimout = 5 * time.Second // Conn represents a network connection between two visors in skywire network // This connection wraps raw network connection and is ready to use for sending data. // It also provides skywire-specific methods on top of net.Conn -type Conn struct { +type Conn interface { + net.Conn + // LocalPK returns local public key of connection + LocalPK() cipher.PubKey + + // RemotePK returns remote public key of connection + RemotePK() cipher.PubKey + + // LocalPort returns local skywire port of connection + // This is not underlying OS port, but port within skywire network + LocalPort() uint16 + + // RemotePort returns remote skywire port of connection + // This is not underlying OS port, but port within skywire network + RemotePort() uint16 + + // Network returns network of connection + // todo: consider switching to Type instead of string + Network() Type +} + +type conn struct { net.Conn lAddr, rAddr dmsg.Addr freePort func() connType Type } -func (c *Conn) encrypt(lPK cipher.PubKey, lSK cipher.SecKey, initator bool) error { +func (c *conn) encrypt(lPK cipher.PubKey, lSK cipher.SecKey, initator bool) error { config := noise.Config{ LocalPK: lPK, LocalSK: lSK, @@ -56,17 +77,17 @@ func EncryptConn(config noise.Config, conn net.Conn) (net.Conn, error) { } // LocalAddr implements net.Conn -func (c *Conn) LocalAddr() net.Addr { +func (c *conn) LocalAddr() net.Addr { return c.lAddr } // RemoteAddr implements net.Conn -func (c *Conn) RemoteAddr() net.Addr { +func (c *conn) RemoteAddr() net.Addr { return c.rAddr } // Close implements net.Conn -func (c *Conn) Close() error { +func (c *conn) Close() error { if c.freePort != nil { c.freePort() } @@ -75,19 +96,19 @@ func (c *Conn) Close() error { } // LocalPK returns local public key of connection -func (c *Conn) LocalPK() cipher.PubKey { return c.lAddr.PK } +func (c *conn) LocalPK() cipher.PubKey { return c.lAddr.PK } // RemotePK returns remote public key of connection -func (c *Conn) RemotePK() cipher.PubKey { return c.rAddr.PK } +func (c *conn) RemotePK() cipher.PubKey { return c.rAddr.PK } // LocalPort returns local skywire port of connection // This is not underlying OS port, but port within skywire network -func (c *Conn) LocalPort() uint16 { return c.lAddr.Port } +func (c *conn) LocalPort() uint16 { return c.lAddr.Port } // RemotePort returns remote skywire port of connection // This is not underlying OS port, but port within skywire network -func (c *Conn) RemotePort() uint16 { return c.rAddr.Port } +func (c *conn) RemotePort() uint16 { return c.rAddr.Port } // Network returns network of connection // todo: consider switching to Type instead of string -func (c *Conn) Network() Type { return c.connType } +func (c *conn) Network() Type { return c.connType } diff --git a/pkg/transport/network/listener.go b/pkg/transport/network/listener.go index c9ae06953..5f2d0ecef 100644 --- a/pkg/transport/network/listener.go +++ b/pkg/transport/network/listener.go @@ -6,39 +6,52 @@ import ( "sync" "github.com/skycoin/dmsg" + "github.com/skycoin/dmsg/cipher" ) // Listener represents a skywire network listener. It wraps net.Listener // with other skywire-specific data // Listener implements net.Listener -type Listener struct { +type Listener interface { + net.Listener + PK() cipher.PubKey + Port() uint16 + Network() Type + AcceptConn() (Conn, error) +} + +type listener struct { lAddr dmsg.Addr mx sync.Mutex once sync.Once freePort func() - accept chan *Conn + accept chan *conn done chan struct{} network Type } // NewListener returns a new Listener. -func NewListener(lAddr dmsg.Addr, freePort func(), network Type) *Listener { - return &Listener{ +func NewListener(lAddr dmsg.Addr, freePort func(), network Type) Listener { + return newListener(lAddr, freePort, network) +} + +func newListener(lAddr dmsg.Addr, freePort func(), network Type) *listener { + return &listener{ lAddr: lAddr, freePort: freePort, - accept: make(chan *Conn), + accept: make(chan *conn), done: make(chan struct{}), network: network, } } // Accept implements net.Listener, returns generic net.Conn -func (l *Listener) Accept() (net.Conn, error) { +func (l *listener) Accept() (net.Conn, error) { return l.AcceptConn() } // AcceptConn accepts a skywire connection and returns network.Conn -func (l *Listener) AcceptConn() (*Conn, error) { +func (l *listener) AcceptConn() (Conn, error) { c, ok := <-l.accept if !ok { return nil, io.ErrClosedPipe @@ -48,7 +61,7 @@ func (l *Listener) AcceptConn() (*Conn, error) { } // Close implements net.Listener -func (l *Listener) Close() error { +func (l *listener) Close() error { l.once.Do(func() { close(l.done) // todo: consider if removing locks will change anything @@ -64,18 +77,28 @@ func (l *Listener) Close() error { } // Addr implements net.Listener -func (l *Listener) Addr() net.Addr { +func (l *listener) Addr() net.Addr { return l.lAddr } +// Addr implements net.Listener +func (l *listener) PK() cipher.PubKey { + return l.lAddr.PK +} + +// Addr implements net.Listener +func (l *listener) Port() uint16 { + return l.lAddr.Port +} + // Network returns network type // todo: consider switching to Type instead of string -func (l *Listener) Network() Type { +func (l *listener) Network() Type { return l.network } // Introduce is used by Client to introduce a new connection to this Listener -func (l *Listener) introduce(conn *Conn) error { +func (l *listener) introduce(conn *conn) error { // todo: think if this is needed select { case <-l.done: diff --git a/pkg/transport/network/stcp.go b/pkg/transport/network/stcp.go index 4395b7852..0972e04c7 100644 --- a/pkg/transport/network/stcp.go +++ b/pkg/transport/network/stcp.go @@ -33,7 +33,7 @@ func newStcp(generic *genericClient, table stcp.PKTable) Client { var ErrStcpEntryNotFound = errors.New("entry not found in PK table") // Dial implements Client interface -func (c *stcpClient) Dial(ctx context.Context, rPK cipher.PubKey, rPort uint16) (*Conn, error) { +func (c *stcpClient) Dial(ctx context.Context, rPK cipher.PubKey, rPort uint16) (Conn, error) { if c.isClosed() { return nil, io.ErrClosedPipe } diff --git a/pkg/transport/network/stcpr.go b/pkg/transport/network/stcpr.go index c625fcecc..5444da12f 100644 --- a/pkg/transport/network/stcpr.go +++ b/pkg/transport/network/stcpr.go @@ -22,7 +22,7 @@ func newStcpr(resolved *resolvedClient) Client { } // Dial implements interface -func (c *stcprClient) Dial(ctx context.Context, rPK cipher.PubKey, rPort uint16) (*Conn, error) { +func (c *stcprClient) Dial(ctx context.Context, rPK cipher.PubKey, rPort uint16) (Conn, error) { if c.isClosed() { return nil, io.ErrClosedPipe } diff --git a/pkg/transport/network/sudph.go b/pkg/transport/network/sudph.go index 63a73e75e..642c615f7 100644 --- a/pkg/transport/network/sudph.go +++ b/pkg/transport/network/sudph.go @@ -90,7 +90,7 @@ func (c *sudphClient) PICKNAMEFORME(conn net.PacketConn, addrCh <-chan addrresol } // Dial implements interface -func (c *sudphClient) Dial(ctx context.Context, rPK cipher.PubKey, rPort uint16) (*Conn, error) { +func (c *sudphClient) Dial(ctx context.Context, rPK cipher.PubKey, rPort uint16) (Conn, error) { if c.isClosed() { return nil, io.ErrClosedPipe } From 07dac3abadaae421b208af2a7020220011e4ed93 Mon Sep 17 00:00:00 2001 From: Never M Date: Wed, 30 Jun 2021 11:06:37 +0300 Subject: [PATCH 39/55] Fix transport types --- cmd/skywire-cli/commands/visor/transports.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/cmd/skywire-cli/commands/visor/transports.go b/cmd/skywire-cli/commands/visor/transports.go index c95c48207..1c53d6298 100644 --- a/cmd/skywire-cli/commands/visor/transports.go +++ b/cmd/skywire-cli/commands/visor/transports.go @@ -12,7 +12,7 @@ import ( "github.com/spf13/cobra" "github.com/skycoin/skywire/cmd/skywire-cli/internal" - "github.com/skycoin/skywire/pkg/snet/directtp/tptypes" + "github.com/skycoin/skywire/pkg/transport/network" "github.com/skycoin/skywire/pkg/visor" ) @@ -113,15 +113,15 @@ var addTpCmd = &cobra.Command{ logger.Infof("Established %v transport to %v", transportType, pk) } else { - transportTypes := []string{ - tptypes.STCP, - tptypes.STCPR, - tptypes.SUDPH, + transportTypes := []network.Type{ + network.STCP, + network.STCPR, + network.SUDPH, dmsg.Type, } for _, transportType := range transportTypes { - tp, err = rpcClient().AddTransport(pk, transportType, public, timeout) + tp, err = rpcClient().AddTransport(pk, string(transportType), public, timeout) if err == nil { logger.Infof("Established %v transport to %v", transportType, pk) break From b040b137e02c6b0daa2bac4671aa94fd11e26000 Mon Sep 17 00:00:00 2001 From: Never M Date: Wed, 30 Jun 2021 12:12:04 +0300 Subject: [PATCH 40/55] Implement dmsg client adapter --- pkg/transport/network/connection.go | 1 - pkg/transport/network/dmsg.go | 123 ++++++++++++++++++++++------ 2 files changed, 98 insertions(+), 26 deletions(-) diff --git a/pkg/transport/network/connection.go b/pkg/transport/network/connection.go index f4d95d069..e5ab6b747 100644 --- a/pkg/transport/network/connection.go +++ b/pkg/transport/network/connection.go @@ -32,7 +32,6 @@ type Conn interface { RemotePort() uint16 // Network returns network of connection - // todo: consider switching to Type instead of string Network() Type } diff --git a/pkg/transport/network/dmsg.go b/pkg/transport/network/dmsg.go index 86fbbc1c5..dfc3cca48 100644 --- a/pkg/transport/network/dmsg.go +++ b/pkg/transport/network/dmsg.go @@ -2,58 +2,131 @@ package network import ( "context" + "fmt" "net" "github.com/skycoin/dmsg" "github.com/skycoin/dmsg/cipher" ) -type dmsgClient struct { - dmsgC dmsg.Client +// dmsgClientAdapter is a wrapper around dmsg.Client to conform to Client +// interface +type dmsgClientAdapter struct { + dmsgC *dmsg.Client } -// func newDmsgClient(dmsgC dmsg.Client) Client { -// return &dmsgClient{dmsgC: dmsg.Client} -// } +func newDmsgClient(dmsgC *dmsg.Client) Client { + return &dmsgClientAdapter{dmsgC: dmsgC} +} -// Dial implements interface -func (c *dmsgClient) Dial(ctx context.Context, remote cipher.PubKey, port uint16) (*Conn, error) { - panic("not implemented") +// LocalAddr implements interface +func (c *dmsgClientAdapter) LocalAddr() (net.Addr, error) { + for _, ses := range c.dmsgC.AllSessions() { + return ses.SessionCommon.GetConn().LocalAddr(), nil + } + return nil, fmt.Errorf("not listening to dmsg") } -// Start implements interface -func (c *dmsgClient) Start() error { - panic("not implemented") +// Dial implements Client interface +func (c *dmsgClientAdapter) Dial(ctx context.Context, remote cipher.PubKey, port uint16) (Conn, error) { + conn, err := c.dmsgC.DialStream(ctx, dmsg.Addr{PK: remote, Port: port}) + if err != nil { + return nil, err + } + return &dmsgConnAdapter{conn}, nil } -// Listen implements interface -func (c *dmsgClient) Listen(port uint16) (*Listener, error) { - panic("not implemented") +// Start implements Client interface +func (c *dmsgClientAdapter) Start() error { + // todo: update interface to pass context properly? + go c.dmsgC.Serve(context.TODO()) + return nil } -// todo: remove -func (c *dmsgClient) LocalAddr() (net.Addr, error) { - return nil, nil +// Listen implements Client interface +func (c *dmsgClientAdapter) Listen(port uint16) (Listener, error) { + lis, err := c.dmsgC.Listen(port) + if err != nil { + return nil, err + } + return &dmsgListenerAdapter{lis}, nil } -// PK implements interface -func (c *dmsgClient) PK() cipher.PubKey { +// PK implements Client interface +func (c *dmsgClientAdapter) PK() cipher.PubKey { return c.dmsgC.LocalPK() } -// SK implements interface -func (c *dmsgClient) SK() cipher.SecKey { +// SK implements Client interface +func (c *dmsgClientAdapter) SK() cipher.SecKey { return c.dmsgC.LocalSK() } -// Close implements interface -func (c *dmsgClient) Close() error { +// Close implements Client interface +func (c *dmsgClientAdapter) Close() error { // todo: maybe not, the dmsg instance we get is the global one that is used // in plenty other places return c.dmsgC.Close() } -// Type implements interface -func (c *dmsgClient) Type() Type { +// Type implements Client interface +func (c *dmsgClientAdapter) Type() Type { + return DMSG +} + +// wrapper around listener returned by dmsg.Client +// that conforms to Listener interface +type dmsgListenerAdapter struct { + *dmsg.Listener +} + +// AcceptConn implements Listener interface +func (lis *dmsgListenerAdapter) AcceptConn() (Conn, error) { + return nil, nil +} + +// Network implements Listener interface +func (lis *dmsgListenerAdapter) Network() Type { + return DMSG +} + +// PK implements Listener interface +func (lis *dmsgListenerAdapter) PK() cipher.PubKey { + return lis.PK() +} + +// Port implements Listener interface +func (lis *dmsgListenerAdapter) Port() uint16 { + return lis.DmsgAddr().Port +} + +// wrapper around connection returned by dmsg.Client +// that conforms to Conn interface +type dmsgConnAdapter struct { + *dmsg.Stream +} + +// LocalPK implements Conn interface +func (c *dmsgConnAdapter) LocalPK() cipher.PubKey { + return c.RawLocalAddr().PK +} + +// RemotePK implements Conn interface +func (c *dmsgConnAdapter) RemotePK() cipher.PubKey { + return c.RawRemoteAddr().PK +} + +// LocalPort implements Conn interface +func (c *dmsgConnAdapter) LocalPort() uint16 { + return c.RawLocalAddr().Port +} + +// RemotePort implements Conn interface +func (c *dmsgConnAdapter) RemotePort() uint16 { + return c.RawRemoteAddr().Port +} + +// Network implements Conn interface +func (c *dmsgConnAdapter) Network() Type { return DMSG } From c057ead41f68eb943442ed91382ea05bf384aac5 Mon Sep 17 00:00:00 2001 From: Never M Date: Wed, 30 Jun 2021 12:53:45 +0300 Subject: [PATCH 41/55] Fix dmsg client init and accept --- pkg/transport/manager.go | 2 +- pkg/transport/network/client.go | 4 ++++ pkg/transport/network/dmsg.go | 6 +++++- pkg/visor/init.go | 3 ++- 4 files changed, 12 insertions(+), 3 deletions(-) diff --git a/pkg/transport/manager.go b/pkg/transport/manager.go index cc964e4a1..03b63fe18 100644 --- a/pkg/transport/manager.go +++ b/pkg/transport/manager.go @@ -82,7 +82,7 @@ func (tm *Manager) serve(ctx context.Context) { } func (tm *Manager) initClients() { - acceptedNetworks := []network.Type{network.STCP, network.STCPR, network.SUDPH} + acceptedNetworks := []network.Type{network.STCP, network.STCPR, network.SUDPH, network.DMSG} for _, netType := range acceptedNetworks { tm.netClients[netType] = tm.factory.MakeClient(netType) } diff --git a/pkg/transport/network/client.go b/pkg/transport/network/client.go index 101bc1500..1daf21e4d 100644 --- a/pkg/transport/network/client.go +++ b/pkg/transport/network/client.go @@ -55,6 +55,7 @@ type ClientFactory struct { PKTable stcp.PKTable ARClient addrresolver.APIClient EB *appevent.Broadcaster + DmsgC *dmsg.Client } // MakeClient creates a new client of specified type @@ -82,7 +83,10 @@ func (f *ClientFactory) MakeClient(netType Type) Client { return newStcpr(resolved) case SUDPH: return newSudph(resolved) + case DMSG: + return newDmsgClient(f.DmsgC) } + // todo: maybe return an error that type is not found return nil } diff --git a/pkg/transport/network/dmsg.go b/pkg/transport/network/dmsg.go index dfc3cca48..02a0a19e6 100644 --- a/pkg/transport/network/dmsg.go +++ b/pkg/transport/network/dmsg.go @@ -82,7 +82,11 @@ type dmsgListenerAdapter struct { // AcceptConn implements Listener interface func (lis *dmsgListenerAdapter) AcceptConn() (Conn, error) { - return nil, nil + stream, err := lis.Listener.AcceptStream() + if err != nil { + return nil, err + } + return &dmsgConnAdapter{stream}, nil } // Network implements Listener interface diff --git a/pkg/visor/init.go b/pkg/visor/init.go index b583a3fd4..8e8b07b58 100644 --- a/pkg/visor/init.go +++ b/pkg/visor/init.go @@ -108,7 +108,7 @@ func registerModules(logger *logging.MasterLogger) { dmsgC = maker("dmsg", initDmsg, &ebc) dmsgCtrl = maker("dmsg_ctrl", initDmsgCtrl, &dmsgC) pty = maker("dmsg_pty", initDmsgpty, &dmsgC) - tr = maker("transport", initTransport, &ar, &ebc) + tr = maker("transport", initTransport, &ar, &ebc, &dmsgC) rt = maker("router", initRouter, &tr, &dmsgC) launch = maker("launcher", initLauncher, &ebc, &disc, &dmsgC, &tr, &rt) cli = maker("cli", initCLI) @@ -259,6 +259,7 @@ func initTransport(ctx context.Context, v *Visor, log *logging.Logger) error { PKTable: table, ARClient: v.arClient, EB: v.ebc, + DmsgC: v.dmsgC, } tpM, err := transport.NewManager(managerLogger, v.arClient, v.ebc, &tpMConf, factory) if err != nil { From 766230a5e4764859b036c3c32b69de69c922e2f6 Mon Sep 17 00:00:00 2001 From: Never M Date: Thu, 1 Jul 2021 11:22:02 +0300 Subject: [PATCH 42/55] Fix rpc test --- pkg/visor/rpc_test.go | 1 - 1 file changed, 1 deletion(-) diff --git a/pkg/visor/rpc_test.go b/pkg/visor/rpc_test.go index e064b4ed4..7bd9b71f1 100644 --- a/pkg/visor/rpc_test.go +++ b/pkg/visor/rpc_test.go @@ -54,7 +54,6 @@ func TestHealth(t *testing.T) { }, }, uptimeTracker: utClient, - arClient: arClient, rfClient: rfClient, } From 682a59c69e44790b03a0f826dc727ff935612162 Mon Sep 17 00:00:00 2001 From: Never M Date: Thu, 1 Jul 2021 12:20:11 +0300 Subject: [PATCH 43/55] Decouple address resolver from tpconn --- pkg/snet/directtp/tpconn/conn.go | 116 ------------------- pkg/snet/directtp/tptypes/tptypes.go | 1 - pkg/transport/network/addrresolver/client.go | 44 ++----- pkg/transport/network/client.go | 17 +-- pkg/transport/network/connection.go | 24 ++++ pkg/transport/network/sudph.go | 13 ++- 6 files changed, 50 insertions(+), 165 deletions(-) delete mode 100644 pkg/snet/directtp/tpconn/conn.go delete mode 100644 pkg/snet/directtp/tptypes/tptypes.go diff --git a/pkg/snet/directtp/tpconn/conn.go b/pkg/snet/directtp/tpconn/conn.go deleted file mode 100644 index 71e8afaf5..000000000 --- a/pkg/snet/directtp/tpconn/conn.go +++ /dev/null @@ -1,116 +0,0 @@ -package tpconn - -import ( - "fmt" - "net" - "time" - - "github.com/skycoin/dmsg" - "github.com/skycoin/dmsg/cipher" - "github.com/skycoin/dmsg/noise" - "github.com/skycoin/skycoin/src/util/logging" - "github.com/skycoin/skywire/pkg/transport/network/handshake" -) - -// Conn wraps an underlying net.Conn and modifies various methods to integrate better with the 'network' package. -type Conn struct { - net.Conn - lAddr dmsg.Addr - rAddr dmsg.Addr - freePort func() -} - -// Config describes a config for Conn. -type Config struct { - Log *logging.Logger - Conn net.Conn - LocalPK cipher.PubKey - LocalSK cipher.SecKey - Deadline time.Time - Handshake handshake.Handshake - FreePort func() - Encrypt bool - Initiator bool -} - -// NewConn creates a new Conn. -func NewConn(c Config) (*Conn, error) { - if c.Log != nil { - c.Log.Infof("Performing handshake with %v", c.Conn.RemoteAddr()) - } - - lAddr, rAddr, err := c.Handshake(c.Conn, c.Deadline) - if err != nil { - if err := c.Conn.Close(); err != nil && c.Log != nil { - c.Log.WithError(err).Warnf("Failed to close connection") - } - - if c.FreePort != nil { - c.FreePort() - } - - return nil, err - } - - if c.Log != nil { - c.Log.Infof("Sent handshake to %v, local addr %v, remote addr %v", c.Conn.RemoteAddr(), lAddr, rAddr) - } - - if c.Encrypt { - config := noise.Config{ - LocalPK: c.LocalPK, - LocalSK: c.LocalSK, - RemotePK: rAddr.PK, - Initiator: c.Initiator, - } - - wrappedConn, err := encryptConn(config, c.Conn) - if err != nil { - return nil, fmt.Errorf("encrypt connection to %v@%v: %w", rAddr, c.Conn.RemoteAddr(), err) - } - - c.Conn = wrappedConn - - if c.Log != nil { - c.Log.Infof("Connection with %v@%v is encrypted", rAddr, c.Conn.RemoteAddr()) - } - } else if c.Log != nil { - c.Log.Infof("Connection with %v@%v is NOT encrypted", rAddr, c.Conn.RemoteAddr()) - } - - return &Conn{Conn: c.Conn, lAddr: lAddr, rAddr: rAddr, freePort: c.FreePort}, nil -} - -// EncryptConn encrypts given connection using noise config -func encryptConn(config noise.Config, conn net.Conn) (net.Conn, error) { - ns, err := noise.New(noise.HandshakeKK, config) - if err != nil { - return nil, fmt.Errorf("failed to prepare stream noise object: %w", err) - } - - wrappedConn, err := noise.WrapConn(conn, ns, time.Second*3) - if err != nil { - return nil, fmt.Errorf("error performing noise handshake: %w", err) - } - - return wrappedConn, nil -} - -// LocalAddr implements net.Conn -func (c *Conn) LocalAddr() net.Addr { - return c.lAddr -} - -// RemoteAddr implements net.Conn -func (c *Conn) RemoteAddr() net.Addr { - return c.rAddr -} - -// Close implements net.Conn -func (c *Conn) Close() error { - if c.freePort != nil { - c.freePort() - } - - return c.Conn.Close() -} diff --git a/pkg/snet/directtp/tptypes/tptypes.go b/pkg/snet/directtp/tptypes/tptypes.go deleted file mode 100644 index 841d4ca05..000000000 --- a/pkg/snet/directtp/tptypes/tptypes.go +++ /dev/null @@ -1 +0,0 @@ -package tptypes diff --git a/pkg/transport/network/addrresolver/client.go b/pkg/transport/network/addrresolver/client.go index 3f04b18d8..a18a9fc92 100644 --- a/pkg/transport/network/addrresolver/client.go +++ b/pkg/transport/network/addrresolver/client.go @@ -15,7 +15,6 @@ import ( "time" "github.com/AudriusButkevicius/pfilter" - "github.com/skycoin/dmsg" "github.com/skycoin/dmsg/cipher" dmsgnetutil "github.com/skycoin/dmsg/netutil" "github.com/skycoin/skycoin/src/util/logging" @@ -24,8 +23,6 @@ import ( "github.com/skycoin/skywire/internal/httpauth" "github.com/skycoin/skywire/internal/netutil" "github.com/skycoin/skywire/internal/packetfilter" - "github.com/skycoin/skywire/pkg/snet/directtp/tpconn" - "github.com/skycoin/skywire/pkg/transport/network/handshake" ) const ( @@ -56,7 +53,7 @@ type Error struct { type APIClient interface { io.Closer BindSTCPR(ctx context.Context, port string) error - BindSUDPH(filter *pfilter.PacketFilter) (<-chan RemoteVisor, error) + BindSUDPH(filter *pfilter.PacketFilter, handshake Handshake) (<-chan RemoteVisor, error) Resolve(ctx context.Context, netType string, pk cipher.PubKey) (VisorData, error) Health(ctx context.Context) (int, error) } @@ -222,7 +219,9 @@ func (c *httpClient) BindSTCPR(ctx context.Context, port string) error { return nil } -func (c *httpClient) BindSUDPH(filter *pfilter.PacketFilter) (<-chan RemoteVisor, error) { +type Handshake func(net.Conn) (net.Conn, error) + +func (c *httpClient) BindSUDPH(filter *pfilter.PacketFilter, hs Handshake) (<-chan RemoteVisor, error) { if !c.isReady() { c.log.Infof("BindSUDPR: Address resolver is not ready yet, waiting...") <-c.ready @@ -242,8 +241,11 @@ func (c *httpClient) BindSUDPH(filter *pfilter.PacketFilter) (<-chan RemoteVisor } c.log.Infof("SUDPH Local port: %v", localPort) - - arConn, err := c.wrapConn(c.sudphConn) + kcpConn, err := kcp.NewConn(c.remoteUDPAddr, nil, 0, 0, c.sudphConn) + if err != nil { + return nil, err + } + arConn, err := hs(kcpConn) if err != nil { return nil, err } @@ -389,34 +391,6 @@ func (c *httpClient) readSUDPHMessages(reader io.Reader) <-chan RemoteVisor { return addrCh } -func (c *httpClient) wrapConn(conn net.PacketConn) (*tpconn.Conn, error) { - arKCPConn, err := kcp.NewConn(c.remoteUDPAddr, nil, 0, 0, conn) - if err != nil { - return nil, err - } - - emptyAddr := dmsg.Addr{PK: cipher.PubKey{}, Port: 0} - hs := handshake.InitiatorHandshake(c.sk, dmsg.Addr{PK: c.pk, Port: 0}, emptyAddr) - - connConfig := tpconn.Config{ - Log: c.log, - Conn: arKCPConn, - LocalPK: c.pk, - LocalSK: c.sk, - Deadline: time.Now().Add(handshake.Timeout), - Handshake: hs, - Encrypt: false, - Initiator: true, - } - - arConn, err := tpconn.NewConn(connConfig) - if err != nil { - return nil, fmt.Errorf("newConn: %w", err) - } - - return arConn, nil -} - func (c *httpClient) Close() error { select { case <-c.closed: diff --git a/pkg/transport/network/client.go b/pkg/transport/network/client.go index 1daf21e4d..f32d15338 100644 --- a/pkg/transport/network/client.go +++ b/pkg/transport/network/client.go @@ -7,7 +7,6 @@ import ( "io" "net" "sync" - "time" "github.com/skycoin/dmsg" "github.com/skycoin/dmsg/cipher" @@ -158,22 +157,16 @@ func (c *genericClient) acceptConnections(lis net.Listener) { // wrapConn performs handshake over provided raw connection and wraps it in // network.Conn type using the data obtained from handshake process func (c *genericClient) wrapConn(rawConn net.Conn, hs handshake.Handshake, initiator bool, onClose func()) (*conn, error) { - lAddr, rAddr, err := hs(rawConn, time.Now().Add(handshake.Timeout)) + conn, err := doHandshake(rawConn, hs, c.netType, c.log) if err != nil { - if err := rawConn.Close(); err != nil { - c.log.WithError(err).Warnf("Failed to close connection") - } onClose() - return nil, err } - c.log.Infof("Sent handshake to %v, local addr %v, remote addr %v", rawConn.RemoteAddr(), lAddr, rAddr) - - wrappedConn := &conn{Conn: rawConn, lAddr: lAddr, rAddr: rAddr, freePort: onClose, connType: c.netType} - err = wrappedConn.encrypt(c.lPK, c.lSK, initiator) - if err != nil { + conn.freePort = onClose + c.log.Infof("Sent handshake to %v, local addr %v, remote addr %v", rawConn.RemoteAddr(), conn.lAddr, conn.rAddr) + if err := conn.encrypt(c.lPK, c.lSK, initiator); err != nil { return nil, err } - return wrappedConn, nil + return conn, nil } // acceptConn accepts new connection in underlying raw network listener, diff --git a/pkg/transport/network/connection.go b/pkg/transport/network/connection.go index e5ab6b747..c64e65c90 100644 --- a/pkg/transport/network/connection.go +++ b/pkg/transport/network/connection.go @@ -8,6 +8,8 @@ import ( "github.com/skycoin/dmsg" "github.com/skycoin/dmsg/cipher" "github.com/skycoin/dmsg/noise" + "github.com/skycoin/skycoin/src/util/logging" + "github.com/skycoin/skywire/pkg/transport/network/handshake" ) const encryptHSTimout = 5 * time.Second @@ -42,6 +44,28 @@ type conn struct { connType Type } +// DoHandshake performs given handshake over given raw connection and wraps +// connection in network.Conn +func DoHandshake(rawConn net.Conn, hs handshake.Handshake, netType Type, log *logging.Logger) (Conn, error) { + return doHandshake(rawConn, hs, netType, log) +} + +// handshake performs given handshake over given raw connection and wraps +// connection in network.conn +// todo: ugly but necessary for AR compatibility +// consider moving "doing handshake" part to handshake package +func doHandshake(rawConn net.Conn, hs handshake.Handshake, netType Type, log *logging.Logger) (*conn, error) { + lAddr, rAddr, err := hs(rawConn, time.Now().Add(handshake.Timeout)) + if err != nil { + if err := rawConn.Close(); err != nil { + log.WithError(err).Warnf("Failed to close connection") + } + return nil, err + } + handshakedConn := &conn{Conn: rawConn, lAddr: lAddr, rAddr: rAddr, connType: netType} + return handshakedConn, nil +} + func (c *conn) encrypt(lPK cipher.PubKey, lSK cipher.SecKey, initator bool) error { config := noise.Config{ LocalPK: lPK, diff --git a/pkg/transport/network/sudph.go b/pkg/transport/network/sudph.go index 642c615f7..af99df5df 100644 --- a/pkg/transport/network/sudph.go +++ b/pkg/transport/network/sudph.go @@ -8,11 +8,13 @@ import ( "time" "github.com/AudriusButkevicius/pfilter" + "github.com/skycoin/dmsg" "github.com/skycoin/dmsg/cipher" "github.com/xtaci/kcp-go" "github.com/skycoin/skywire/internal/packetfilter" "github.com/skycoin/skywire/pkg/transport/network/addrresolver" + "github.com/skycoin/skywire/pkg/transport/network/handshake" ) const ( @@ -62,7 +64,7 @@ func (c *sudphClient) listen() (net.Listener, error) { sudphVisorsConn := c.filter.NewConn(visorsConnPriority, nil) c.filter.Start() c.log.Infof("Binding") - addrCh, err := c.ar.BindSUDPH(c.filter) + addrCh, err := c.ar.BindSUDPH(c.filter, c.makeBindHandshake()) if err != nil { return nil, err } @@ -70,6 +72,15 @@ func (c *sudphClient) listen() (net.Listener, error) { return kcp.ServeConn(nil, 0, 0, sudphVisorsConn) } +// make a handshake function that is compatible with address resolver interface +func (c *sudphClient) makeBindHandshake() func(in net.Conn) (net.Conn, error) { + emptyAddr := dmsg.Addr{PK: cipher.PubKey{}, Port: 0} + hs := handshake.InitiatorHandshake(c.SK(), dmsg.Addr{PK: c.PK(), Port: 0}, emptyAddr) + return func(in net.Conn) (net.Conn, error) { + return doHandshake(in, hs, SUDPH, c.log) + } +} + // todo: name func (c *sudphClient) PICKNAMEFORME(conn net.PacketConn, addrCh <-chan addrresolver.RemoteVisor) { for addr := range addrCh { From 3efb9c7a9d65576b0a8b80438a9384ea30331e91 Mon Sep 17 00:00:00 2001 From: Never M Date: Thu, 1 Jul 2021 14:11:28 +0300 Subject: [PATCH 44/55] WIP add tests back --- pkg/router/router_test.go | 575 ++++++++++++++++---------------- pkg/setup/testing_test.go | 6 +- pkg/snet/network_test.go | 21 ++ pkg/snet/snettest/env.go | 164 ++++++++- pkg/transport/handshake_test.go | 62 ++++ pkg/transport/manager_test.go | 183 ++++++++++ pkg/visor/rpc_test.go | 5 +- 7 files changed, 723 insertions(+), 293 deletions(-) create mode 100644 pkg/snet/network_test.go create mode 100644 pkg/transport/handshake_test.go create mode 100644 pkg/transport/manager_test.go diff --git a/pkg/router/router_test.go b/pkg/router/router_test.go index 446906c05..df22a96c0 100644 --- a/pkg/router/router_test.go +++ b/pkg/router/router_test.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "log" + "net" "os" "sync" "testing" @@ -11,13 +12,19 @@ import ( "github.com/google/uuid" "github.com/sirupsen/logrus" + "github.com/skycoin/dmsg" "github.com/skycoin/dmsg/cipher" "github.com/skycoin/skycoin/src/util/logging" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" + "github.com/skycoin/skywire/internal/testhelpers" + "github.com/skycoin/skywire/pkg/routefinder/rfclient" "github.com/skycoin/skywire/pkg/routing" + "github.com/skycoin/skywire/pkg/setup/setupclient" "github.com/skycoin/skywire/pkg/snet" + "github.com/skycoin/skywire/pkg/snet/snettest" "github.com/skycoin/skywire/pkg/transport" ) @@ -39,260 +46,260 @@ func TestMain(m *testing.M) { // Test ensures that we can establish connection between 2 routers. 1st router dials // the 2nd one, 2nd one accepts. We get 2 noise-wrapped route groups and check that -// // these route groups correctly communicate with each other. -// func Test_router_NoiseRouteGroups(t *testing.T) { -// // We're doing 2 key pairs for 2 communicating routers. -// keys := snettest.GenKeyPairs(2) - -// desc := routing.NewRouteDescriptor(keys[0].PK, keys[1].PK, 1, 1) - -// forwardHops := []routing.Hop{ -// {From: keys[0].PK, To: keys[1].PK, TpID: transport.MakeTransportID(keys[0].PK, keys[1].PK, dmsg.Type)}, -// } - -// reverseHops := []routing.Hop{ -// {From: keys[1].PK, To: keys[0].PK, TpID: transport.MakeTransportID(keys[1].PK, keys[0].PK, dmsg.Type)}, -// } - -// // Route that will be established -// route := routing.BidirectionalRoute{ -// Desc: desc, -// KeepAlive: DefaultRouteKeepAlive, -// Forward: forwardHops, -// Reverse: reverseHops, -// } - -// // Create test env -// nEnv := snettest.NewEnv(t, keys, []string{dmsg.Type}) -// defer nEnv.Teardown() - -// tpD := transport.NewDiscoveryMock() - -// // Prepare transports -// m0, m1, _, _, err := transport.CreateTransportPair(tpD, keys[:2], nEnv, dmsg.Type) -// require.NoError(t, err) - -// forward := [2]cipher.PubKey{keys[0].PK, keys[1].PK} -// backward := [2]cipher.PubKey{keys[1].PK, keys[0].PK} - -// // Paths to be returned from route finder -// rfPaths := make(map[routing.PathEdges][][]routing.Hop) -// rfPaths[forward] = append(rfPaths[forward], forwardHops) -// rfPaths[backward] = append(rfPaths[backward], reverseHops) - -// rfCl := &rfclient.MockClient{} -// rfCl.On("FindRoutes", mock.Anything, []routing.PathEdges{forward, backward}, -// &rfclient.RouteOptions{MinHops: minHops, MaxHops: maxHops}).Return(rfPaths, testhelpers.NoErr) - -// r0Logger := logging.MustGetLogger(fmt.Sprintf("router_%d", 0)) - -// fwdRt, revRt := route.ForwardAndReverse() -// srcPK := route.Desc.SrcPK() -// dstPK := route.Desc.DstPK() - -// fwdRules0 := routing.ForwardRule(route.KeepAlive, 1, 2, forwardHops[0].TpID, srcPK, dstPK, 1, 1) -// revRules0 := routing.ConsumeRule(route.KeepAlive, 3, srcPK, dstPK, 1, 1) - -// // Edge rules to be returned from route group dialer -// initEdge := routing.EdgeRules{Desc: revRt.Desc, Forward: fwdRules0, Reverse: revRules0} - -// setupCl0 := &setupclient.MockRouteGroupDialer{} -// setupCl0.On("Dial", mock.Anything, r0Logger, nEnv.Nets[0], mock.Anything, route). -// Return(initEdge, testhelpers.NoErr) - -// r0Conf := &Config{ -// Logger: r0Logger, -// PubKey: keys[0].PK, -// SecKey: keys[0].SK, -// TransportManager: m0, -// RouteFinder: rfCl, -// RouteGroupDialer: setupCl0, -// } - -// // Create routers -// r0Ifc, err := New(nEnv.Nets[0], r0Conf) -// require.NoError(t, err) - -// r0, ok := r0Ifc.(*router) -// require.True(t, ok) - -// r1Conf := &Config{ -// Logger: logging.MustGetLogger(fmt.Sprintf("router_%d", 1)), -// PubKey: keys[1].PK, -// SecKey: keys[1].SK, -// TransportManager: m1, -// } - -// r1Ifc, err := New(nEnv.Nets[1], r1Conf) -// require.NoError(t, err) - -// r1, ok := r1Ifc.(*router) -// require.True(t, ok) - -// ctx := context.Background() - -// nrg1IfcCh := make(chan net.Conn) -// acceptErrCh := make(chan error) -// go func() { -// nrg1Ifc, err := r1.AcceptRoutes(ctx) -// acceptErrCh <- err -// nrg1IfcCh <- nrg1Ifc -// close(acceptErrCh) -// close(nrg1IfcCh) -// }() - -// dialErrCh := make(chan error) -// nrg0IfcCh := make(chan net.Conn) -// go func() { -// nrg0Ifc, err := r0.DialRoutes(context.Background(), r1.conf.PubKey, 1, 1, nil) -// dialErrCh <- err -// nrg0IfcCh <- nrg0Ifc -// close(dialErrCh) -// close(nrg0IfcCh) -// }() - -// fwdRules1 := routing.ForwardRule(route.KeepAlive, 4, 3, reverseHops[0].TpID, dstPK, srcPK, 1, 1) -// revRules1 := routing.ConsumeRule(route.KeepAlive, 2, dstPK, srcPK, 1, 1) - -// // This edge is returned by the setup node to accepting router -// respEdge := routing.EdgeRules{Desc: fwdRt.Desc, Forward: fwdRules1, Reverse: revRules1} - -// // Unblock AcceptRoutes, imitates setup node request with EdgeRules -// r1.accept <- respEdge - -// // At some point raw route group gets into `rgsRaw` and waits for -// // handshake packets. we're waiting for this moment in the cycle -// // to start passing packets from the transport to route group -// for { -// r0.mx.Lock() -// if _, ok := r0.rgsRaw[initEdge.Desc]; ok { -// rg := r0.rgsRaw[initEdge.Desc] -// go pushPackets(ctx, m0, rg) -// r0.mx.Unlock() -// break -// } -// r0.mx.Unlock() -// } - -// for { -// r1.mx.Lock() -// if _, ok := r1.rgsRaw[respEdge.Desc]; ok { -// rg := r1.rgsRaw[respEdge.Desc] -// go pushPackets(ctx, m1, rg) -// r1.mx.Unlock() -// break -// } -// r1.mx.Unlock() -// } - -// require.NoError(t, <-acceptErrCh) -// require.NoError(t, <-dialErrCh) - -// nrg0Ifc := <-nrg0IfcCh -// require.NotNil(t, nrg0Ifc) -// nrg1Ifc := <-nrg1IfcCh -// require.NotNil(t, nrg1Ifc) - -// nrg0, ok := nrg0Ifc.(*NoiseRouteGroup) -// require.True(t, ok) -// require.NotNil(t, nrg0) - -// nrg1, ok := nrg1Ifc.(*NoiseRouteGroup) -// require.True(t, ok) -// require.NotNil(t, nrg1) - -// data := []byte("Hello there!") -// n, err := nrg0.Write(data) -// require.NoError(t, err) -// require.Equal(t, len(data), n) - -// received := make([]byte, 1024) -// n, err = nrg1.Read(received) -// require.NoError(t, err) -// require.Equal(t, len(data), n) -// require.Equal(t, data, received[:n]) - -// require.True(t, nrg0.IsAlive()) -// require.True(t, nrg1.IsAlive()) - -// err = nrg0.Close() -// require.NoError(t, err) - -// require.False(t, nrg0.IsAlive()) -// require.False(t, nrg1.IsAlive()) - -// require.True(t, nrg1.rg.isRemoteClosed()) -// err = nrg1.Close() -// require.NoError(t, err) -// require.False(t, nrg1.IsAlive()) -// } - -// func TestRouter_Serve(t *testing.T) { -// // We are generating two key pairs - one for the a `Router`, the other to send packets to `Router`. -// keys := snettest.GenKeyPairs(2) - -// // create test env -// nEnv := snettest.NewEnv(t, keys, []string{dmsg.Type}) -// defer nEnv.Teardown() - -// rEnv := NewTestEnv(t, nEnv.Nets) -// defer rEnv.Teardown() - -// // Create routers -// r0Ifc, err := New(nEnv.Nets[0], rEnv.GenRouterConfig(0)) -// require.NoError(t, err) - -// r0, ok := r0Ifc.(*router) -// require.True(t, ok) - -// ctx, cancel := context.WithCancel(context.Background()) -// defer cancel() - -// require.NoError(t, r0.tm.Close()) -// require.NoError(t, r0.Serve(ctx)) -// } +// these route groups correctly communicate with each other. +func Test_router_NoiseRouteGroups(t *testing.T) { + // We're doing 2 key pairs for 2 communicating routers. + keys := snettest.GenKeyPairs(2) + + desc := routing.NewRouteDescriptor(keys[0].PK, keys[1].PK, 1, 1) + + forwardHops := []routing.Hop{ + {From: keys[0].PK, To: keys[1].PK, TpID: transport.MakeTransportID(keys[0].PK, keys[1].PK, dmsg.Type)}, + } + + reverseHops := []routing.Hop{ + {From: keys[1].PK, To: keys[0].PK, TpID: transport.MakeTransportID(keys[1].PK, keys[0].PK, dmsg.Type)}, + } + + // Route that will be established + route := routing.BidirectionalRoute{ + Desc: desc, + KeepAlive: DefaultRouteKeepAlive, + Forward: forwardHops, + Reverse: reverseHops, + } + + // Create test env + nEnv := snettest.NewEnv(t, keys, []string{dmsg.Type}) + defer nEnv.Teardown() + + tpD := transport.NewDiscoveryMock() + + // Prepare transports + m0, m1, _, _, err := transport.CreateTransportPair(tpD, keys[:2], nEnv, dmsg.Type) + require.NoError(t, err) + + forward := [2]cipher.PubKey{keys[0].PK, keys[1].PK} + backward := [2]cipher.PubKey{keys[1].PK, keys[0].PK} + + // Paths to be returned from route finder + rfPaths := make(map[routing.PathEdges][][]routing.Hop) + rfPaths[forward] = append(rfPaths[forward], forwardHops) + rfPaths[backward] = append(rfPaths[backward], reverseHops) + + rfCl := &rfclient.MockClient{} + rfCl.On("FindRoutes", mock.Anything, []routing.PathEdges{forward, backward}, + &rfclient.RouteOptions{MinHops: minHops, MaxHops: maxHops}).Return(rfPaths, testhelpers.NoErr) + + r0Logger := logging.MustGetLogger(fmt.Sprintf("router_%d", 0)) + + fwdRt, revRt := route.ForwardAndReverse() + srcPK := route.Desc.SrcPK() + dstPK := route.Desc.DstPK() + + fwdRules0 := routing.ForwardRule(route.KeepAlive, 1, 2, forwardHops[0].TpID, srcPK, dstPK, 1, 1) + revRules0 := routing.ConsumeRule(route.KeepAlive, 3, srcPK, dstPK, 1, 1) + + // Edge rules to be returned from route group dialer + initEdge := routing.EdgeRules{Desc: revRt.Desc, Forward: fwdRules0, Reverse: revRules0} + + setupCl0 := &setupclient.MockRouteGroupDialer{} + setupCl0.On("Dial", mock.Anything, r0Logger, nEnv.Nets[0], mock.Anything, route). + Return(initEdge, testhelpers.NoErr) + + r0Conf := &Config{ + Logger: r0Logger, + PubKey: keys[0].PK, + SecKey: keys[0].SK, + TransportManager: m0, + RouteFinder: rfCl, + RouteGroupDialer: setupCl0, + } + + // Create routers + r0Ifc, err := New(nEnv.Nets[0], r0Conf) + require.NoError(t, err) + + r0, ok := r0Ifc.(*router) + require.True(t, ok) + + r1Conf := &Config{ + Logger: logging.MustGetLogger(fmt.Sprintf("router_%d", 1)), + PubKey: keys[1].PK, + SecKey: keys[1].SK, + TransportManager: m1, + } + + r1Ifc, err := New(nEnv.Nets[1], r1Conf) + require.NoError(t, err) + + r1, ok := r1Ifc.(*router) + require.True(t, ok) + + ctx := context.Background() + + nrg1IfcCh := make(chan net.Conn) + acceptErrCh := make(chan error) + go func() { + nrg1Ifc, err := r1.AcceptRoutes(ctx) + acceptErrCh <- err + nrg1IfcCh <- nrg1Ifc + close(acceptErrCh) + close(nrg1IfcCh) + }() + + dialErrCh := make(chan error) + nrg0IfcCh := make(chan net.Conn) + go func() { + nrg0Ifc, err := r0.DialRoutes(context.Background(), r1.conf.PubKey, 1, 1, nil) + dialErrCh <- err + nrg0IfcCh <- nrg0Ifc + close(dialErrCh) + close(nrg0IfcCh) + }() + + fwdRules1 := routing.ForwardRule(route.KeepAlive, 4, 3, reverseHops[0].TpID, dstPK, srcPK, 1, 1) + revRules1 := routing.ConsumeRule(route.KeepAlive, 2, dstPK, srcPK, 1, 1) + + // This edge is returned by the setup node to accepting router + respEdge := routing.EdgeRules{Desc: fwdRt.Desc, Forward: fwdRules1, Reverse: revRules1} + + // Unblock AcceptRoutes, imitates setup node request with EdgeRules + r1.accept <- respEdge + + // At some point raw route group gets into `rgsRaw` and waits for + // handshake packets. we're waiting for this moment in the cycle + // to start passing packets from the transport to route group + for { + r0.mx.Lock() + if _, ok := r0.rgsRaw[initEdge.Desc]; ok { + rg := r0.rgsRaw[initEdge.Desc] + go pushPackets(ctx, m0, rg) + r0.mx.Unlock() + break + } + r0.mx.Unlock() + } + + for { + r1.mx.Lock() + if _, ok := r1.rgsRaw[respEdge.Desc]; ok { + rg := r1.rgsRaw[respEdge.Desc] + go pushPackets(ctx, m1, rg) + r1.mx.Unlock() + break + } + r1.mx.Unlock() + } + + require.NoError(t, <-acceptErrCh) + require.NoError(t, <-dialErrCh) + + nrg0Ifc := <-nrg0IfcCh + require.NotNil(t, nrg0Ifc) + nrg1Ifc := <-nrg1IfcCh + require.NotNil(t, nrg1Ifc) + + nrg0, ok := nrg0Ifc.(*NoiseRouteGroup) + require.True(t, ok) + require.NotNil(t, nrg0) + + nrg1, ok := nrg1Ifc.(*NoiseRouteGroup) + require.True(t, ok) + require.NotNil(t, nrg1) + + data := []byte("Hello there!") + n, err := nrg0.Write(data) + require.NoError(t, err) + require.Equal(t, len(data), n) + + received := make([]byte, 1024) + n, err = nrg1.Read(received) + require.NoError(t, err) + require.Equal(t, len(data), n) + require.Equal(t, data, received[:n]) + + require.True(t, nrg0.IsAlive()) + require.True(t, nrg1.IsAlive()) + + err = nrg0.Close() + require.NoError(t, err) + + require.False(t, nrg0.IsAlive()) + require.False(t, nrg1.IsAlive()) + + require.True(t, nrg1.rg.isRemoteClosed()) + err = nrg1.Close() + require.NoError(t, err) + require.False(t, nrg1.IsAlive()) +} + +func TestRouter_Serve(t *testing.T) { + // We are generating two key pairs - one for the a `Router`, the other to send packets to `Router`. + keys := snettest.GenKeyPairs(2) + + // create test env + nEnv := snettest.NewEnv(t, keys, []string{dmsg.Type}) + defer nEnv.Teardown() + + rEnv := NewTestEnv(t, nEnv.Nets) + defer rEnv.Teardown() + + // Create routers + r0Ifc, err := New(nEnv.Nets[0], rEnv.GenRouterConfig(0)) + require.NoError(t, err) + + r0, ok := r0Ifc.(*router) + require.True(t, ok) + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + require.NoError(t, r0.tm.Close()) + require.NoError(t, r0.Serve(ctx)) +} const ruleKeepAlive = 1 * time.Hour -// // Ensure that received packets are handled properly in `(*Router).handleTransportPacket()`. -// func TestRouter_handleTransportPacket(t *testing.T) { -// // We are generating two key pairs - one for the a `Router`, the other to send packets to `Router`. -// keys := snettest.GenKeyPairs(2) +// Ensure that received packets are handled properly in `(*Router).handleTransportPacket()`. +func TestRouter_handleTransportPacket(t *testing.T) { + // We are generating two key pairs - one for the a `Router`, the other to send packets to `Router`. + keys := snettest.GenKeyPairs(2) -// pk1 := keys[0].PK -// pk2 := keys[1].PK + pk1 := keys[0].PK + pk2 := keys[1].PK -// // create test env -// nEnv := snettest.NewEnv(t, keys, []string{dmsg.Type}) -// defer nEnv.Teardown() + // create test env + nEnv := snettest.NewEnv(t, keys, []string{dmsg.Type}) + defer nEnv.Teardown() -// rEnv := NewTestEnv(t, nEnv.Nets) -// defer rEnv.Teardown() + rEnv := NewTestEnv(t, nEnv.Nets) + defer rEnv.Teardown() -// // Create routers -// r0Ifc, err := New(nEnv.Nets[0], rEnv.GenRouterConfig(0)) -// require.NoError(t, err) + // Create routers + r0Ifc, err := New(nEnv.Nets[0], rEnv.GenRouterConfig(0)) + require.NoError(t, err) -// r0, ok := r0Ifc.(*router) -// require.True(t, ok) + r0, ok := r0Ifc.(*router) + require.True(t, ok) -// r1Ifc, err := New(nEnv.Nets[1], rEnv.GenRouterConfig(1)) -// require.NoError(t, err) + r1Ifc, err := New(nEnv.Nets[1], rEnv.GenRouterConfig(1)) + require.NoError(t, err) -// r1, ok := r1Ifc.(*router) -// require.True(t, ok) + r1, ok := r1Ifc.(*router) + require.True(t, ok) -// defer func() { -// require.NoError(t, r0.Close()) -// require.NoError(t, r1.Close()) -// }() + defer func() { + require.NoError(t, r0.Close()) + require.NoError(t, r1.Close()) + }() -// // Create dmsg transport between two `snet.Network` entities. -// tp1, err := rEnv.TpMngrs[1].SaveTransport(context.TODO(), pk1, dmsg.Type, transport.LabelUser) -// require.NoError(t, err) + // Create dmsg transport between two `snet.Network` entities. + tp1, err := rEnv.TpMngrs[1].SaveTransport(context.TODO(), pk1, dmsg.Type, transport.LabelUser) + require.NoError(t, err) -// testHandlePackets(t, r0, r1, tp1, pk1, pk2) -// } + testHandlePackets(t, r0, r1, tp1, pk1, pk2) +} func testHandlePackets(t *testing.T, r0, r1 *router, tp1 *transport.ManagedTransport, pk1, pk2 cipher.PubKey) { var wg sync.WaitGroup @@ -625,46 +632,46 @@ func testConsumeRule(t *testing.T, r0, r1 *router, tp1 *transport.ManagedTranspo require.Equal(t, consumeMsg, data) } -// func TestRouter_Rules(t *testing.T) { -// pk, sk := cipher.GenerateKeyPair() +func TestRouter_Rules(t *testing.T) { + pk, sk := cipher.GenerateKeyPair() -// env := snettest.NewEnv(t, []snettest.KeyPair{{PK: pk, SK: sk}}, []string{dmsg.Type}) -// defer env.Teardown() + env := snettest.NewEnv(t, []snettest.KeyPair{{PK: pk, SK: sk}}, []string{dmsg.Type}) + defer env.Teardown() -// rt := routing.NewTable() + rt := routing.NewTable() -// // We are generating two key pairs - one for the a `Router`, the other to send packets to `Router`. -// keys := snettest.GenKeyPairs(2) + // We are generating two key pairs - one for the a `Router`, the other to send packets to `Router`. + keys := snettest.GenKeyPairs(2) -// // create test env -// nEnv := snettest.NewEnv(t, keys, []string{dmsg.Type}) -// defer nEnv.Teardown() + // create test env + nEnv := snettest.NewEnv(t, keys, []string{dmsg.Type}) + defer nEnv.Teardown() -// rEnv := NewTestEnv(t, nEnv.Nets) -// defer rEnv.Teardown() + rEnv := NewTestEnv(t, nEnv.Nets) + defer rEnv.Teardown() -// rIfc, err := New(nEnv.Nets[0], rEnv.GenRouterConfig(0)) -// require.NoError(t, err) + rIfc, err := New(nEnv.Nets[0], rEnv.GenRouterConfig(0)) + require.NoError(t, err) -// r, ok := rIfc.(*router) -// require.True(t, ok) + r, ok := rIfc.(*router) + require.True(t, ok) -// defer func() { -// require.NoError(t, r.Close()) -// }() + defer func() { + require.NoError(t, r.Close()) + }() -// r.rt = rt + r.rt = rt -// // TEST: Set and get expired and unexpired rule. -// t.Run("GetRule", func(t *testing.T) { -// testGetRule(t, r, rt) -// }) + // TEST: Set and get expired and unexpired rule. + t.Run("GetRule", func(t *testing.T) { + testGetRule(t, r, rt) + }) -// // TEST: Ensure removing route descriptor works properly. -// t.Run("RemoveRouteDescriptor", func(t *testing.T) { -// testRemoveRouteDescriptor(t, r, rt) -// }) -// } + // TEST: Ensure removing route descriptor works properly. + t.Run("RemoveRouteDescriptor", func(t *testing.T) { + testRemoveRouteDescriptor(t, r, rt) + }) +} func testRemoveRouteDescriptor(t *testing.T, r *router, rt routing.Table) { clearRoutingTableRules(rt) @@ -722,24 +729,24 @@ func testGetRule(t *testing.T, r *router, rt routing.Table) { assert.Equal(t, rule, gotRule) } -// func TestRouter_SetupIsTrusted(t *testing.T) { -// keys := snettest.GenKeyPairs(2) +func TestRouter_SetupIsTrusted(t *testing.T) { + keys := snettest.GenKeyPairs(2) -// nEnv := snettest.NewEnv(t, keys, []string{dmsg.Type}) -// defer nEnv.Teardown() + nEnv := snettest.NewEnv(t, keys, []string{dmsg.Type}) + defer nEnv.Teardown() -// rEnv := NewTestEnv(t, nEnv.Nets) -// defer rEnv.Teardown() + rEnv := NewTestEnv(t, nEnv.Nets) + defer rEnv.Teardown() -// routerConfig := rEnv.GenRouterConfig(0) -// routerConfig.SetupNodes = append(routerConfig.SetupNodes, keys[0].PK) + routerConfig := rEnv.GenRouterConfig(0) + routerConfig.SetupNodes = append(routerConfig.SetupNodes, keys[0].PK) -// r0, err := New(nEnv.Nets[0], routerConfig) -// require.NoError(t, err) + r0, err := New(nEnv.Nets[0], routerConfig) + require.NoError(t, err) -// assert.True(t, r0.SetupIsTrusted(keys[0].PK)) -// assert.False(t, r0.SetupIsTrusted(keys[1].PK)) -// } + assert.True(t, r0.SetupIsTrusted(keys[0].PK)) + assert.False(t, r0.SetupIsTrusted(keys[1].PK)) +} func clearRouteGroups(routers ...*router) { for _, r := range routers { @@ -788,7 +795,7 @@ func NewTestEnv(t *testing.T, nets []*snet.Network) *TestEnv { LogStore: transport.InMemoryTransportLogStore(), } - ms[i], err = transport.NewManager(nil, n, nil, mConfs[i]) + ms[i], err = transport.NewManager(nil, n, mConfs[i]) require.NoError(t, err) go ms[i].Serve(context.TODO()) diff --git a/pkg/setup/testing_test.go b/pkg/setup/testing_test.go index c539bb634..e9d263eb8 100644 --- a/pkg/setup/testing_test.go +++ b/pkg/setup/testing_test.go @@ -17,11 +17,11 @@ import ( "github.com/skycoin/skywire/pkg/router/routerclient" "github.com/skycoin/skywire/pkg/routing" - "github.com/skycoin/skywire/pkg/transport/network" + "github.com/skycoin/skywire/pkg/snet" ) // creates a mock dialer -func newMockDialer(t *testing.T, gateways map[cipher.PubKey]interface{}) network.Dialer { +func newMockDialer(t *testing.T, gateways map[cipher.PubKey]interface{}) snet.Dialer { newRPCConn := func(gw interface{}) net.Conn { connC, connS := net.Pipe() t.Cleanup(func() { @@ -38,7 +38,7 @@ func newMockDialer(t *testing.T, gateways map[cipher.PubKey]interface{}) network if gateways == nil { conn := newRPCConn(new(mockGatewayForDialer)) - dialer := new(network.MockDialer) + dialer := new(snet.MockDialer) dialer.On("Dial", mock.Anything, mock.Anything, mock.Anything).Return(conn, nil) return dialer } diff --git a/pkg/snet/network_test.go b/pkg/snet/network_test.go new file mode 100644 index 000000000..3dce54c36 --- /dev/null +++ b/pkg/snet/network_test.go @@ -0,0 +1,21 @@ +package snet + +import ( + "testing" + + "github.com/skycoin/dmsg" + "github.com/skycoin/dmsg/cipher" + "github.com/stretchr/testify/require" +) + +func TestDisassembleAddr(t *testing.T) { + pk, _ := cipher.GenerateKeyPair() + port := uint16(2) + addr := dmsg.Addr{ + PK: pk, Port: port, + } + + gotPK, gotPort := disassembleAddr(addr) + require.Equal(t, pk, gotPK) + require.Equal(t, port, gotPort) +} diff --git a/pkg/snet/snettest/env.go b/pkg/snet/snettest/env.go index afd705e56..e883a5bdb 100644 --- a/pkg/snet/snettest/env.go +++ b/pkg/snet/snettest/env.go @@ -1,12 +1,23 @@ package snettest -// todo: this seems like an integration test, we set up a real dmsg server here -// it doesn't test sending data, but it probably should -// for now (while refactoring snet) dmsg parts are cut out import ( + "context" + "strconv" "testing" + "github.com/skycoin/dmsg" "github.com/skycoin/dmsg/cipher" + "github.com/skycoin/dmsg/disc" + "github.com/skycoin/skycoin/src/util/logging" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "golang.org/x/net/nettest" + + "github.com/skycoin/skywire/pkg/snet" + "github.com/skycoin/skywire/pkg/snet/arclient" + "github.com/skycoin/skywire/pkg/snet/directtp" + "github.com/skycoin/skywire/pkg/snet/directtp/pktable" + "github.com/skycoin/skywire/pkg/snet/directtp/tptypes" ) // KeyPair holds a public/private key pair. @@ -32,10 +43,155 @@ func GenKeyPairs(n int) []KeyPair { // Env contains a network test environment. type Env struct { + DmsgD disc.APIClient + DmsgS *dmsg.Server + Keys []KeyPair + Nets []*snet.Network + teardown func() } // NewEnv creates a `network.Network` test environment. // `nPairs` is the public/private key pairs of all the `network.Network`s to be created. func NewEnv(t *testing.T, keys []KeyPair, networks []string) *Env { - return nil + // Prepare `dmsg`. + dmsgD := disc.NewMock(0) + dmsgS, dmsgSErr := createDmsgSrv(t, dmsgD) + + const baseSTCPPort = 7033 + + tableEntries := make(map[cipher.PubKey]string) + for i, pair := range keys { + tableEntries[pair.PK] = "127.0.0.1:" + strconv.Itoa(baseSTCPPort+i) + } + + table := pktable.NewTable(tableEntries) + + var hasDmsg, hasStcp, hasStcpr, hasSudph bool + + for _, network := range networks { + switch network { + case dmsg.Type: + hasDmsg = true + case tptypes.STCP: + hasStcp = true + case tptypes.STCPR: + hasStcpr = true + case tptypes.SUDPH: + hasSudph = true + } + } + + // Prepare `snets`. + ns := make([]*snet.Network, len(keys)) + + const stcpBasePort = 7033 + + for i, pairs := range keys { + networkConfigs := snet.NetworkConfigs{ + Dmsg: &snet.DmsgConfig{ + SessionsCount: 1, + }, + STCP: &snet.STCPConfig{ + LocalAddr: "127.0.0.1:" + strconv.Itoa(stcpBasePort+i), + }, + } + + clients := snet.NetworkClients{ + Direct: make(map[string]directtp.Client), + } + + if hasDmsg { + clients.DmsgC = dmsg.NewClient(pairs.PK, pairs.SK, dmsgD, nil) + go clients.DmsgC.Serve(context.Background()) + } + + addressResolver := new(arclient.MockAPIClient) + + if hasStcp { + conf := directtp.Config{ + Type: tptypes.STCP, + PK: pairs.PK, + SK: pairs.SK, + Table: table, + LocalAddr: networkConfigs.STCP.LocalAddr, + } + + clients.Direct[tptypes.STCP] = directtp.NewClient(conf, logging.NewMasterLogger()) + } + + if hasStcpr { + conf := directtp.Config{ + Type: tptypes.STCPR, + PK: pairs.PK, + SK: pairs.SK, + AddressResolver: addressResolver, + } + + clients.Direct[tptypes.STCPR] = directtp.NewClient(conf, logging.NewMasterLogger()) + } + + if hasSudph { + conf := directtp.Config{ + Type: tptypes.SUDPH, + PK: pairs.PK, + SK: pairs.SK, + AddressResolver: addressResolver, + } + + clients.Direct[tptypes.SUDPH] = directtp.NewClient(conf, logging.NewMasterLogger()) + } + + snetConfig := snet.Config{ + PubKey: pairs.PK, + SecKey: pairs.SK, + NetworkConfigs: networkConfigs, + } + + n := snet.NewRaw(snetConfig, clients) + require.NoError(t, n.Init()) + ns[i] = n + } + + // Prepare teardown closure. + teardown := func() { + for _, n := range ns { + assert.NoError(t, n.Close()) + } + assert.NoError(t, dmsgS.Close()) + for err := range dmsgSErr { + assert.NoError(t, err) + } + } + + return &Env{ + DmsgD: dmsgD, + DmsgS: dmsgS, + Keys: keys, + Nets: ns, + teardown: teardown, + } +} + +// Teardown shutdowns the Env. +func (e *Env) Teardown() { e.teardown() } + +func createDmsgSrv(t *testing.T, dc disc.APIClient) (srv *dmsg.Server, srvErr <-chan error) { + pk, sk, err := cipher.GenerateDeterministicKeyPair([]byte("s")) + require.NoError(t, err) + + l, err := nettest.NewLocalListener("tcp") + require.NoError(t, err) + + srv = dmsg.NewServer(pk, sk, dc, &dmsg.ServerConfig{MaxSessions: 100}, nil) + + errCh := make(chan error, 1) + + go func() { + errCh <- srv.Serve(l, "") + close(errCh) + }() + + <-srv.Ready() + + return srv, errCh } diff --git a/pkg/transport/handshake_test.go b/pkg/transport/handshake_test.go new file mode 100644 index 000000000..d0225a768 --- /dev/null +++ b/pkg/transport/handshake_test.go @@ -0,0 +1,62 @@ +package transport_test + +import ( + "context" + "testing" + "time" + + "github.com/skycoin/dmsg" + "github.com/stretchr/testify/require" + + "github.com/skycoin/skywire/pkg/skyenv" + "github.com/skycoin/skywire/pkg/snet/snettest" + "github.com/skycoin/skywire/pkg/transport" +) + +func TestSettlementHS(t *testing.T) { + tpDisc := transport.NewDiscoveryMock() + + keys := snettest.GenKeyPairs(2) + nEnv := snettest.NewEnv(t, keys, []string{dmsg.Type}) + defer nEnv.Teardown() + + // TEST: Perform a handshake between two snet.Network instances. + t.Run("Do", func(t *testing.T) { + lis1, err := nEnv.Nets[1].Listen(dmsg.Type, skyenv.DmsgTransportPort) + require.NoError(t, err) + + errCh1 := make(chan error, 1) + go func() { + defer close(errCh1) + conn1, err := lis1.AcceptConn() + if err != nil { + errCh1 <- err + return + } + errCh1 <- transport.MakeSettlementHS(false).Do(context.TODO(), tpDisc, conn1, keys[1].SK) + }() + + const entryTimeout = 5 * time.Second + start := time.Now() + + // Wait until entry is set. + // TODO: Implement more elegant solution. + for { + if time.Since(start) > entryTimeout { + t.Fatal("Entry in Dmsg Discovery is not set within expected time") + } + + if _, err := nEnv.DmsgD.Entry(context.TODO(), keys[1].PK); err == nil { + break + } + } + + conn0, err := nEnv.Nets[0].Dial(context.TODO(), dmsg.Type, keys[1].PK, skyenv.DmsgTransportPort) + require.NoError(t, err) + require.NoError(t, transport.MakeSettlementHS(true).Do(context.TODO(), tpDisc, conn0, keys[0].SK)) + + require.NoError(t, <-errCh1) + }) +} + +// TODO(evanlinjin): This will need further testing. diff --git a/pkg/transport/manager_test.go b/pkg/transport/manager_test.go new file mode 100644 index 000000000..1a37b2e44 --- /dev/null +++ b/pkg/transport/manager_test.go @@ -0,0 +1,183 @@ +package transport_test + +import ( + "context" + "fmt" + "io/ioutil" + "log" + "os" + "testing" + "time" + + "github.com/skycoin/dmsg" + "github.com/skycoin/dmsg/cipher" + "github.com/skycoin/skycoin/src/util/logging" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/skycoin/skywire/pkg/routing" + "github.com/skycoin/skywire/pkg/snet/snettest" + "github.com/skycoin/skywire/pkg/transport" +) + +var masterLogger *logging.MasterLogger + +func TestMain(m *testing.M) { + masterLogger = logging.NewMasterLogger() + loggingLevel, ok := os.LookupEnv("TEST_LOGGING_LEVEL") + if ok { + lvl, err := logging.LevelFromString(loggingLevel) + if err != nil { + log.Fatal(err) + } + masterLogger.SetLevel(lvl) + } else { + masterLogger.Out = ioutil.Discard + } + + os.Exit(m.Run()) +} + +// TODO: test hangs if Manager is closed to early, needs to receive an error though +func TestNewManager(t *testing.T) { + tpDisc := transport.NewDiscoveryMock() + + keys := snettest.GenKeyPairs(2) + nEnv := snettest.NewEnv(t, keys, []string{dmsg.Type}) + defer nEnv.Teardown() + + // Prepare tp manager 0. + pk0, sk0 := keys[0].PK, keys[0].SK + ls0 := transport.InMemoryTransportLogStore() + m0, err := transport.NewManager(nil, nEnv.Nets[0], &transport.ManagerConfig{ + PubKey: pk0, + SecKey: sk0, + DiscoveryClient: tpDisc, + LogStore: ls0, + }) + require.NoError(t, err) + go m0.Serve(context.TODO()) + defer func() { require.NoError(t, m0.Close()) }() + + // Prepare tp manager 1. + pk1, sk1 := keys[1].PK, keys[1].SK + ls1 := transport.InMemoryTransportLogStore() + m2, err := transport.NewManager(nil, nEnv.Nets[1], &transport.ManagerConfig{ + PubKey: pk1, + SecKey: sk1, + DiscoveryClient: tpDisc, + LogStore: ls1, + }) + require.NoError(t, err) + go m2.Serve(context.TODO()) + defer func() { require.NoError(t, m2.Close()) }() + + // Create data transport between manager 1 & manager 2. + tp2, err := m2.SaveTransport(context.TODO(), pk0, "dmsg", transport.LabelUser) + require.NoError(t, err) + tp1 := m0.Transport(transport.MakeTransportID(pk0, pk1, "dmsg")) + require.NotNil(t, tp1) + + fmt.Println("transports created") + + totalSent2 := 0 + totalSent1 := 0 + + // Check read/writes are of expected. + t.Run("check_read_write", func(t *testing.T) { + + for i := 0; i < 10; i++ { + totalSent2 += i + rID := routing.RouteID(i) + payload := cipher.RandByte(i) + + packet, err := routing.MakeDataPacket(rID, payload) + require.NoError(t, err) + + require.NoError(t, tp2.WritePacket(context.TODO(), packet)) + + recv, err := m0.ReadPacket() + require.NoError(t, err) + require.Equal(t, rID, recv.RouteID()) + require.Equal(t, uint16(i), recv.Size()) + require.Equal(t, payload, recv.Payload()) + } + + for i := 0; i < 20; i++ { + totalSent1 += i + rID := routing.RouteID(i) + payload := cipher.RandByte(i) + + packet, err := routing.MakeDataPacket(rID, payload) + require.NoError(t, err) + + require.NoError(t, tp1.WritePacket(context.TODO(), packet)) + + recv, err := m2.ReadPacket() + require.NoError(t, err) + require.Equal(t, rID, recv.RouteID()) + require.Equal(t, uint16(i), recv.Size()) + require.Equal(t, payload, recv.Payload()) + } + }) + + // Ensure tp log entries are of expected. + t.Run("check_tp_logs", func(t *testing.T) { + + // 1.5x log write interval just to be safe. + time.Sleep(time.Second * 9 / 2) + + entry1, err := ls0.Entry(tp1.Entry.ID) + require.NoError(t, err) + assert.Equal(t, uint64(totalSent1), entry1.SentBytes) + assert.Equal(t, uint64(totalSent2), entry1.RecvBytes) + + entry2, err := ls1.Entry(tp2.Entry.ID) + require.NoError(t, err) + assert.Equal(t, uint64(totalSent2), entry2.SentBytes) + assert.Equal(t, uint64(totalSent1), entry2.RecvBytes) + }) + + // Ensure deleting a transport works as expected. + t.Run("check_delete_tp", func(t *testing.T) { + + // Make transport ID. + tpID := transport.MakeTransportID(pk0, pk1, "dmsg") + + // Ensure transports are registered properly in tp discovery. + entry, err := tpDisc.GetTransportByID(context.TODO(), tpID) + require.NoError(t, err) + assert.Equal(t, transport.SortEdges(pk0, pk1), entry.Entry.Edges) + assert.True(t, entry.IsUp) + + m2.DeleteTransport(tp2.Entry.ID) + + _, err = tpDisc.GetTransportByID(context.TODO(), tpID) + require.NotNil(t, err) + require.Contains(t, err.Error(), "not found") + }) +} + +func TestSortEdges(t *testing.T) { + for i := 0; i < 100; i++ { + keyA, _ := cipher.GenerateKeyPair() + keyB, _ := cipher.GenerateKeyPair() + require.Equal(t, transport.SortEdges(keyA, keyB), transport.SortEdges(keyB, keyA)) + } +} + +func TestMakeTransportID(t *testing.T) { + t.Run("id_is_stable", func(t *testing.T) { + for i := 0; i < 100; i++ { + keyA, _ := cipher.GenerateKeyPair() + keyB, _ := cipher.GenerateKeyPair() + idAB := transport.MakeTransportID(keyA, keyB, "type") + idBA := transport.MakeTransportID(keyB, keyA, "type") + require.Equal(t, idAB, idBA) + } + }) + t.Run("tpType_changes_id", func(t *testing.T) { + keyA, _ := cipher.GenerateKeyPair() + require.NotEqual(t, transport.MakeTransportID(keyA, keyA, "a"), transport.MakeTransportID(keyA, keyA, "b")) + }) +} diff --git a/pkg/visor/rpc_test.go b/pkg/visor/rpc_test.go index 7bd9b71f1..178506d47 100644 --- a/pkg/visor/rpc_test.go +++ b/pkg/visor/rpc_test.go @@ -15,8 +15,8 @@ import ( "github.com/skycoin/skywire/internal/testhelpers" "github.com/skycoin/skywire/internal/utclient" "github.com/skycoin/skywire/pkg/routefinder/rfclient" + "github.com/skycoin/skywire/pkg/snet/arclient" "github.com/skycoin/skywire/pkg/transport" - "github.com/skycoin/skywire/pkg/transport/network/addrresolver" "github.com/skycoin/skywire/pkg/visor/visorconfig" ) @@ -40,7 +40,7 @@ func TestHealth(t *testing.T) { utClient := &utclient.MockAPIClient{} utClient.On("Health", mock.Anything).Return(http.StatusOK, nil) - arClient := &addrresolver.MockAPIClient{} + arClient := &arclient.MockAPIClient{} arClient.On("Health", mock.Anything).Return(http.StatusOK, nil) rfClient := &rfclient.MockClient{} @@ -54,6 +54,7 @@ func TestHealth(t *testing.T) { }, }, uptimeTracker: utClient, + arClient: arClient, rfClient: rfClient, } From 498d1690a7bf7525c99d4d45cc46e10a71b9a2b8 Mon Sep 17 00:00:00 2001 From: Never M Date: Fri, 2 Jul 2021 11:33:10 +0300 Subject: [PATCH 45/55] Move GenKeyPairs to utils --- pkg/snet/snettest/env.go | 197 ------------------------------ pkg/util/cipherutil/cipherutil.go | 24 ++++ pkg/visor/rpc_client.go | 4 +- 3 files changed, 26 insertions(+), 199 deletions(-) delete mode 100644 pkg/snet/snettest/env.go create mode 100644 pkg/util/cipherutil/cipherutil.go diff --git a/pkg/snet/snettest/env.go b/pkg/snet/snettest/env.go deleted file mode 100644 index e883a5bdb..000000000 --- a/pkg/snet/snettest/env.go +++ /dev/null @@ -1,197 +0,0 @@ -package snettest - -import ( - "context" - "strconv" - "testing" - - "github.com/skycoin/dmsg" - "github.com/skycoin/dmsg/cipher" - "github.com/skycoin/dmsg/disc" - "github.com/skycoin/skycoin/src/util/logging" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "golang.org/x/net/nettest" - - "github.com/skycoin/skywire/pkg/snet" - "github.com/skycoin/skywire/pkg/snet/arclient" - "github.com/skycoin/skywire/pkg/snet/directtp" - "github.com/skycoin/skywire/pkg/snet/directtp/pktable" - "github.com/skycoin/skywire/pkg/snet/directtp/tptypes" -) - -// KeyPair holds a public/private key pair. -type KeyPair struct { - PK cipher.PubKey - SK cipher.SecKey -} - -// GenKeyPairs generates 'n' number of key pairs. -func GenKeyPairs(n int) []KeyPair { - pairs := make([]KeyPair, n) - for i := range pairs { - pk, sk, err := cipher.GenerateDeterministicKeyPair([]byte{byte(i)}) - if err != nil { - panic(err) - } - - pairs[i] = KeyPair{PK: pk, SK: sk} - } - - return pairs -} - -// Env contains a network test environment. -type Env struct { - DmsgD disc.APIClient - DmsgS *dmsg.Server - Keys []KeyPair - Nets []*snet.Network - teardown func() -} - -// NewEnv creates a `network.Network` test environment. -// `nPairs` is the public/private key pairs of all the `network.Network`s to be created. -func NewEnv(t *testing.T, keys []KeyPair, networks []string) *Env { - // Prepare `dmsg`. - dmsgD := disc.NewMock(0) - dmsgS, dmsgSErr := createDmsgSrv(t, dmsgD) - - const baseSTCPPort = 7033 - - tableEntries := make(map[cipher.PubKey]string) - for i, pair := range keys { - tableEntries[pair.PK] = "127.0.0.1:" + strconv.Itoa(baseSTCPPort+i) - } - - table := pktable.NewTable(tableEntries) - - var hasDmsg, hasStcp, hasStcpr, hasSudph bool - - for _, network := range networks { - switch network { - case dmsg.Type: - hasDmsg = true - case tptypes.STCP: - hasStcp = true - case tptypes.STCPR: - hasStcpr = true - case tptypes.SUDPH: - hasSudph = true - } - } - - // Prepare `snets`. - ns := make([]*snet.Network, len(keys)) - - const stcpBasePort = 7033 - - for i, pairs := range keys { - networkConfigs := snet.NetworkConfigs{ - Dmsg: &snet.DmsgConfig{ - SessionsCount: 1, - }, - STCP: &snet.STCPConfig{ - LocalAddr: "127.0.0.1:" + strconv.Itoa(stcpBasePort+i), - }, - } - - clients := snet.NetworkClients{ - Direct: make(map[string]directtp.Client), - } - - if hasDmsg { - clients.DmsgC = dmsg.NewClient(pairs.PK, pairs.SK, dmsgD, nil) - go clients.DmsgC.Serve(context.Background()) - } - - addressResolver := new(arclient.MockAPIClient) - - if hasStcp { - conf := directtp.Config{ - Type: tptypes.STCP, - PK: pairs.PK, - SK: pairs.SK, - Table: table, - LocalAddr: networkConfigs.STCP.LocalAddr, - } - - clients.Direct[tptypes.STCP] = directtp.NewClient(conf, logging.NewMasterLogger()) - } - - if hasStcpr { - conf := directtp.Config{ - Type: tptypes.STCPR, - PK: pairs.PK, - SK: pairs.SK, - AddressResolver: addressResolver, - } - - clients.Direct[tptypes.STCPR] = directtp.NewClient(conf, logging.NewMasterLogger()) - } - - if hasSudph { - conf := directtp.Config{ - Type: tptypes.SUDPH, - PK: pairs.PK, - SK: pairs.SK, - AddressResolver: addressResolver, - } - - clients.Direct[tptypes.SUDPH] = directtp.NewClient(conf, logging.NewMasterLogger()) - } - - snetConfig := snet.Config{ - PubKey: pairs.PK, - SecKey: pairs.SK, - NetworkConfigs: networkConfigs, - } - - n := snet.NewRaw(snetConfig, clients) - require.NoError(t, n.Init()) - ns[i] = n - } - - // Prepare teardown closure. - teardown := func() { - for _, n := range ns { - assert.NoError(t, n.Close()) - } - assert.NoError(t, dmsgS.Close()) - for err := range dmsgSErr { - assert.NoError(t, err) - } - } - - return &Env{ - DmsgD: dmsgD, - DmsgS: dmsgS, - Keys: keys, - Nets: ns, - teardown: teardown, - } -} - -// Teardown shutdowns the Env. -func (e *Env) Teardown() { e.teardown() } - -func createDmsgSrv(t *testing.T, dc disc.APIClient) (srv *dmsg.Server, srvErr <-chan error) { - pk, sk, err := cipher.GenerateDeterministicKeyPair([]byte("s")) - require.NoError(t, err) - - l, err := nettest.NewLocalListener("tcp") - require.NoError(t, err) - - srv = dmsg.NewServer(pk, sk, dc, &dmsg.ServerConfig{MaxSessions: 100}, nil) - - errCh := make(chan error, 1) - - go func() { - errCh <- srv.Serve(l, "") - close(errCh) - }() - - <-srv.Ready() - - return srv, errCh -} diff --git a/pkg/util/cipherutil/cipherutil.go b/pkg/util/cipherutil/cipherutil.go new file mode 100644 index 000000000..d537e8368 --- /dev/null +++ b/pkg/util/cipherutil/cipherutil.go @@ -0,0 +1,24 @@ +package cipherutil + +import "github.com/skycoin/dmsg/cipher" + +// KeyPair is a pair of public and secret keys +type KeyPair struct { + PK cipher.PubKey + SK cipher.SecKey +} + +// GenKeyPairs generates n random key pairs +func GenKeyPairs(n int) []KeyPair { + pairs := make([]KeyPair, n) + for i := range pairs { + pk, sk, err := cipher.GenerateDeterministicKeyPair([]byte{byte(i)}) + if err != nil { + panic(err) + } + + pairs[i] = KeyPair{PK: pk, SK: sk} + } + + return pairs +} diff --git a/pkg/visor/rpc_client.go b/pkg/visor/rpc_client.go index 174d1bbed..b573273b9 100644 --- a/pkg/visor/rpc_client.go +++ b/pkg/visor/rpc_client.go @@ -25,9 +25,9 @@ import ( "github.com/skycoin/skywire/pkg/router" "github.com/skycoin/skywire/pkg/routing" "github.com/skycoin/skywire/pkg/skyenv" - "github.com/skycoin/skywire/pkg/snet/snettest" "github.com/skycoin/skywire/pkg/transport" "github.com/skycoin/skywire/pkg/transport/network" + "github.com/skycoin/skywire/pkg/util/cipherutil" "github.com/skycoin/skywire/pkg/util/updater" ) @@ -497,7 +497,7 @@ func NewMockRPCClient(r *rand.Rand, maxTps int, maxRules int) (cipher.PubKey, AP panic(err) } - keys := snettest.GenKeyPairs(2) + keys := cipherutil.GenKeyPairs(2) fwdRule := routing.ForwardRule(ruleKeepAlive, fwdRID[0], routing.RouteID(r.Uint32()), uuid.New(), keys[0].PK, keys[1].PK, 0, 0) if err := rt.SaveRule(fwdRule); err != nil { From 04235055e11c5a5fe541812b774c5af77cc41903 Mon Sep 17 00:00:00 2001 From: Never M Date: Fri, 2 Jul 2021 12:14:35 +0300 Subject: [PATCH 46/55] Remove tests that depend on snet.Network --- pkg/app/conn_test.go | 6 +- pkg/router/route_group_test.go | 379 ------------------ pkg/router/router_test.go | 359 ----------------- pkg/setup/testing_test.go | 6 +- pkg/snet/network_test.go | 21 - pkg/transport/handshake_test.go | 62 --- pkg/transport/manager_test.go | 128 ------ .../network/addrresolver/mock_api_client.go | 2 +- pkg/visor/rpc_test.go | 4 +- 9 files changed, 9 insertions(+), 958 deletions(-) delete mode 100644 pkg/snet/network_test.go delete mode 100644 pkg/transport/handshake_test.go diff --git a/pkg/app/conn_test.go b/pkg/app/conn_test.go index 7f337dd29..5b25497d7 100644 --- a/pkg/app/conn_test.go +++ b/pkg/app/conn_test.go @@ -18,7 +18,7 @@ import ( "github.com/skycoin/skywire/pkg/app/appserver" "github.com/skycoin/skywire/pkg/app/idmanager" "github.com/skycoin/skywire/pkg/routing" - "github.com/skycoin/skywire/pkg/snet/snettest" + "github.com/skycoin/skywire/pkg/util/cipherutil" ) func TestConn_Read(t *testing.T) { @@ -175,7 +175,7 @@ func (p *wrappedConn) RemoteAddr() net.Addr { func TestConn_TestConn(t *testing.T) { mp := func() (net.Conn, net.Conn, func(), error) { netType := appnet.TypeSkynet - keys := snettest.GenKeyPairs(2) + keys := cipherutil.GenKeyPairs(2) fmt.Printf("C1 Local: %s\n", keys[0].PK) fmt.Printf("C2 Local: %s\n", keys[1].PK) p1, p2 := net.Pipe() @@ -219,7 +219,7 @@ func TestConn_TestConn(t *testing.T) { rpcS := rpc.NewServer() - appKeys := snettest.GenKeyPairs(2) + appKeys := cipherutil.GenKeyPairs(2) var ( procKey1 appcommon.ProcKey diff --git a/pkg/router/route_group_test.go b/pkg/router/route_group_test.go index cab574704..48d67ee89 100644 --- a/pkg/router/route_group_test.go +++ b/pkg/router/route_group_test.go @@ -5,18 +5,14 @@ import ( "fmt" "io" "strconv" - "strings" "sync" "testing" "time" "github.com/skycoin/dmsg/cipher" - "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/skycoin/skywire/pkg/routing" - "github.com/skycoin/skywire/pkg/snet/directtp/tptypes" - "github.com/skycoin/skywire/pkg/snet/snettest" "github.com/skycoin/skywire/pkg/transport" ) @@ -26,106 +22,6 @@ func TestNewRouteGroup(t *testing.T) { require.Equal(t, DefaultRouteGroupConfig(), rg.cfg) } -// Uncomment for debugging -/* -func TestRouteGroupAlignment(t *testing.T) { - alignment.PrintStruct(RouteGroup{}) -} -*/ - -func TestRouteGroup_Close(t *testing.T) { - rg1, rg2, m1, m2, teardown := setupEnv(t) - - ctx, cancel := context.WithCancel(context.Background()) - - // push close packet from transport to route group - go pushPackets(ctx, m2, rg2) - go pushPackets(ctx, m1, rg1) - - err := rg1.Close() - require.NoError(t, err) - require.True(t, rg1.isClosed()) - require.True(t, rg2.isRemoteClosed()) - // rg1 should be done (not getting any new data, returning `io.EOF` on further reads) - // but not closed - require.False(t, rg2.isClosed()) - - err = rg1.Close() - require.Equal(t, io.ErrClosedPipe, err) - - err = rg2.Close() - require.NoError(t, err) - require.True(t, rg2.isClosed()) - - err = rg2.Close() - require.Equal(t, io.ErrClosedPipe, err) - - cancel() - teardown() -} - -func TestRouteGroup_Read(t *testing.T) { - rg1, rg2, m1, m2, teardown := setupEnv(t) - - ctx, cancel := context.WithCancel(context.Background()) - - // push close packet from transport to route group - go pushPackets(ctx, m2, rg2) - go pushPackets(ctx, m1, rg1) - - msg1 := []byte("hello1") - msg2 := []byte("hello2") - msg3 := []byte("hello3") - buf1 := make([]byte, len(msg1)) - buf2 := make([]byte, len(msg2)) - buf3 := make([]byte, len(msg2)/2) - buf4 := make([]byte, len(msg2)/2) - - rg1.readCh <- msg1 - rg2.readCh <- msg2 - rg2.readCh <- msg3 - - n, err := rg1.Read([]byte{}) - require.Equal(t, 0, n) - require.NoError(t, err) - - n, err = rg1.Read(buf1) - require.NoError(t, err) - require.Equal(t, msg1, buf1) - require.Equal(t, len(msg1), n) - - n, err = rg2.Read(buf2) - require.NoError(t, err) - require.Equal(t, msg2, buf2) - require.Equal(t, len(msg2), n) - - // Test short reads. - n, err = rg2.Read(buf3) - require.NoError(t, err) - require.Equal(t, msg3[0:len(msg3)/2], buf3) - require.Equal(t, len(msg3)/2, n) - - n, err = rg2.Read(buf4) - require.NoError(t, err) - require.Equal(t, msg3[len(msg3)/2:], buf4) - require.Equal(t, len(msg3)/2, n) - - require.NoError(t, rg1.Close()) - require.NoError(t, rg2.Close()) - cancel() - teardown() -} - -func TestRouteGroup_Write(t *testing.T) { - rg1, rg2, m1, m2, teardown := setupEnv(t) - - testWrite(t, rg1, rg2, m1, m2) - - require.NoError(t, rg1.Close()) - require.NoError(t, rg2.Close()) - teardown() -} - func testWrite(t *testing.T, rg1, rg2 *RouteGroup, m1, m2 *transport.Manager) { msg1 := []byte("hello1") msg2 := []byte("hello2") @@ -182,31 +78,6 @@ func testWrite(t *testing.T, rg1, rg2 *RouteGroup, m1, m2 *transport.Manager) { rg1.mu.Unlock() } -func TestRouteGroup_ReadWrite(t *testing.T) { - const iterations = 3 - - for i := 0; i < iterations; i++ { - testReadWrite(t, iterations) - } -} - -func testReadWrite(t *testing.T, iterations int) { - rg1, rg2, m1, m2, teardown := setupEnv(t) - - ctx, cancel := context.WithCancel(context.Background()) - - // push close packet from transport to route group - go pushPackets(ctx, m2, rg2) - go pushPackets(ctx, m1, rg1) - - testRouteGroupReadWrite(t, iterations, rg1, rg2) - - assert.NoError(t, rg1.Close()) - assert.NoError(t, rg2.Close()) - cancel() - teardown() -} - func testRouteGroupReadWrite(t *testing.T, iterations int, rg1, rg2 io.ReadWriter) { msg1 := []byte("hello1_") msg2 := []byte("hello2_") @@ -344,166 +215,6 @@ func testMultipleWR(t *testing.T, iterations int, rg1, rg2 io.ReadWriter, msg1, } } -func TestArbitrarySizeOneMessage(t *testing.T) { - // Test fails if message size is above 4059 - const ( - value1 = 4058 // dmsg/noise.maxFrameSize - 38 - value2 = 4059 // dmsg/noise.maxFrameSize - 37 - ) - - var wg sync.WaitGroup - - wg.Add(1) - - t.Run("Value1", func(t *testing.T) { - defer wg.Done() - testArbitrarySizeOneMessage(t, value1) - }) - - wg.Wait() - - t.Run("Value2", func(t *testing.T) { - testArbitrarySizeOneMessage(t, value2) - }) -} - -func TestArbitrarySizeMultipleMessagesByChunks(t *testing.T) { - // Test fails if message size is above 64810 - const ( - value1 = 64810 // 2^16 - 726 - value2 = 64811 // 2^16 - 725 - ) - - var wg sync.WaitGroup - - wg.Add(1) - - t.Run("Value1", func(t *testing.T) { - defer wg.Done() - testArbitrarySizeMultipleMessagesByChunks(t, value1) - }) - - wg.Wait() - - t.Run("Value2", func(t *testing.T) { - testArbitrarySizeMultipleMessagesByChunks(t, value2) - }) -} - -func testArbitrarySizeMultipleMessagesByChunks(t *testing.T, size int) { - rg1, rg2, m1, m2, teardown := setupEnv(t) - - ctx, cancel := context.WithCancel(context.Background()) - - // push close packet from transport to route group - go pushPackets(ctx, m2, rg2) - go pushPackets(ctx, m1, rg1) - - defer func() { - cancel() - teardown() - }() - - chunkSize := 1024 - - msg := []byte(strings.Repeat("A", size)) - - for offset := 0; offset < size; offset += chunkSize { - _, err := rg1.Write(msg[offset : offset+chunkSize]) - require.NoError(t, err) - } - - for offset := 0; offset < size; offset += chunkSize { - buf := make([]byte, chunkSize) - n, err := rg2.Read(buf) - require.NoError(t, err) - require.Equal(t, chunkSize, n) - require.Equal(t, msg[offset:offset+chunkSize], buf) - } - - var ( - errCh = make(chan error) - nCh = make(chan int) - bufCh = make(chan []byte) - ) - go func() { - buf := make([]byte, size) - n, err := rg2.Read(buf) - errCh <- err - nCh <- n - bufCh <- buf - }() - - // close remote to simulate `io.EOF` on local connection - require.NoError(t, rg1.Close()) - - err := <-errCh - n := <-nCh - readBuf := <-bufCh - close(nCh) - close(errCh) - close(bufCh) - require.Equal(t, io.EOF, err) - require.Equal(t, 0, n) - require.Equal(t, make([]byte, size), readBuf) - - require.NoError(t, rg2.Close()) -} - -func testArbitrarySizeOneMessage(t *testing.T, size int) { - rg1, rg2, m1, m2, teardown := setupEnv(t) - - ctx, cancel := context.WithCancel(context.Background()) - - // push close packet from transport to route group - go pushPackets(ctx, m2, rg2) - go pushPackets(ctx, m1, rg1) - - defer func() { - cancel() - teardown() - }() - - msg := []byte(strings.Repeat("A", size)) - - _, err := rg1.Write(msg) - require.NoError(t, err) - - buf := make([]byte, size) - n, err := rg2.Read(buf) - require.NoError(t, err) - require.Equal(t, size, n) - require.Equal(t, msg, buf) - - var ( - errCh = make(chan error) - nCh = make(chan int) - bufCh = make(chan []byte) - ) - go func() { - buf := make([]byte, size) - n, err := rg2.Read(buf) - errCh <- err - nCh <- n - bufCh <- buf - }() - - // close remote to simulate `io.EOF` on local connection - require.NoError(t, rg1.Close()) - - err = <-errCh - n = <-nCh - readBuf := <-bufCh - close(nCh) - close(errCh) - close(bufCh) - require.Equal(t, io.EOF, err) - require.Equal(t, 0, n) - require.Equal(t, make([]byte, size), readBuf) - - require.NoError(t, rg2.Close()) -} - func TestRouteGroup_LocalAddr(t *testing.T) { rg := createRouteGroup(DefaultRouteGroupConfig()) require.Equal(t, rg.desc.Dst(), rg.LocalAddr()) @@ -518,32 +229,6 @@ func TestRouteGroup_RemoteAddr(t *testing.T) { require.NoError(t, rg.Close()) } -// TODO(darkrengarius): Uncomment and fix. -/* -func TestRouteGroup_TestConn(t *testing.T) { - mp := func() (c1, c2 net.Conn, stop func(), err error) { - rg1, rg2, m1, m2, teardown := setupEnv(t) - - ctx, cancel := context.WithCancel(context.Background()) - - // push close packet from transport to route group - go pushPackets(ctx, m2, rg2) - go pushPackets(ctx, m1, rg1) - - stop = func() { - _ = rg1.Close() // nolint:errcheck - _ = rg2.Close() // nolint:errcheck - cancel() - teardown() - } - - return rg1, rg2, stop, nil - } - - nettest.TestConn(t, mp) -} -*/ - func pushPackets(ctx context.Context, from *transport.Manager, to *RouteGroup) { for { select { @@ -616,67 +301,3 @@ func createRouteGroup(cfg *RouteGroupConfig) *RouteGroup { return rg } - -func setupEnv(t *testing.T) (rg1, rg2 *RouteGroup, m1, m2 *transport.Manager, teardown func()) { - keys := snettest.GenKeyPairs(2) - - pk1 := keys[0].PK - pk2 := keys[1].PK - - // create test env - nEnv := snettest.NewEnv(t, keys, []string{tptypes.STCP}) - - tpDisc := transport.NewDiscoveryMock() - tpKeys := snettest.GenKeyPairs(2) - - m1, m2, tp1, tp2, err := transport.CreateTransportPair(tpDisc, tpKeys, nEnv, tptypes.STCP) - require.NoError(t, err) - require.NotNil(t, tp1) - require.NotNil(t, tp2) - require.NotNil(t, tp1.Entry) - require.NotNil(t, tp2.Entry) - - // because some subtests of `TestConn` are highly specific in their behavior, - // it's best to exceed the `readCh` size - rgCfg := &RouteGroupConfig{ - ReadChBufSize: defaultReadChBufSize * 3, - KeepAliveInterval: defaultRouteGroupKeepAliveInterval, - } - - rg1 = createRouteGroup(rgCfg) - rg2 = createRouteGroup(rgCfg) - - r1RtIDs, err := rg1.rt.ReserveKeys(1) - require.NoError(t, err) - - r2RtIDs, err := rg2.rt.ReserveKeys(1) - require.NoError(t, err) - - r1FwdRule := routing.ForwardRule(ruleKeepAlive, r1RtIDs[0], r2RtIDs[0], tp1.Entry.ID, pk2, pk1, 0, 0) - err = rg1.rt.SaveRule(r1FwdRule) - require.NoError(t, err) - - r2FwdRule := routing.ForwardRule(ruleKeepAlive, r2RtIDs[0], r1RtIDs[0], tp2.Entry.ID, pk1, pk2, 0, 0) - err = rg2.rt.SaveRule(r2FwdRule) - require.NoError(t, err) - - r1FwdRtDesc := r1FwdRule.RouteDescriptor() - rg1.mu.Lock() - rg1.desc = r1FwdRtDesc.Invert() - rg1.tps = append(rg1.tps, tp1) - rg1.fwd = append(rg1.fwd, r1FwdRule) - rg1.mu.Unlock() - - r2FwdRtDesc := r2FwdRule.RouteDescriptor() - rg2.mu.Lock() - rg2.desc = r2FwdRtDesc.Invert() - rg2.tps = append(rg2.tps, tp2) - rg2.fwd = append(rg2.fwd, r2FwdRule) - rg2.mu.Unlock() - - teardown = func() { - nEnv.Teardown() - } - - return rg1, rg2, m1, m2, teardown -} diff --git a/pkg/router/router_test.go b/pkg/router/router_test.go index df22a96c0..57952142e 100644 --- a/pkg/router/router_test.go +++ b/pkg/router/router_test.go @@ -4,7 +4,6 @@ import ( "context" "fmt" "log" - "net" "os" "sync" "testing" @@ -12,19 +11,12 @@ import ( "github.com/google/uuid" "github.com/sirupsen/logrus" - "github.com/skycoin/dmsg" "github.com/skycoin/dmsg/cipher" "github.com/skycoin/skycoin/src/util/logging" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" - "github.com/skycoin/skywire/internal/testhelpers" - "github.com/skycoin/skywire/pkg/routefinder/rfclient" "github.com/skycoin/skywire/pkg/routing" - "github.com/skycoin/skywire/pkg/setup/setupclient" - "github.com/skycoin/skywire/pkg/snet" - "github.com/skycoin/skywire/pkg/snet/snettest" "github.com/skycoin/skywire/pkg/transport" ) @@ -44,263 +36,8 @@ func TestMain(m *testing.M) { os.Exit(m.Run()) } -// Test ensures that we can establish connection between 2 routers. 1st router dials -// the 2nd one, 2nd one accepts. We get 2 noise-wrapped route groups and check that -// these route groups correctly communicate with each other. -func Test_router_NoiseRouteGroups(t *testing.T) { - // We're doing 2 key pairs for 2 communicating routers. - keys := snettest.GenKeyPairs(2) - - desc := routing.NewRouteDescriptor(keys[0].PK, keys[1].PK, 1, 1) - - forwardHops := []routing.Hop{ - {From: keys[0].PK, To: keys[1].PK, TpID: transport.MakeTransportID(keys[0].PK, keys[1].PK, dmsg.Type)}, - } - - reverseHops := []routing.Hop{ - {From: keys[1].PK, To: keys[0].PK, TpID: transport.MakeTransportID(keys[1].PK, keys[0].PK, dmsg.Type)}, - } - - // Route that will be established - route := routing.BidirectionalRoute{ - Desc: desc, - KeepAlive: DefaultRouteKeepAlive, - Forward: forwardHops, - Reverse: reverseHops, - } - - // Create test env - nEnv := snettest.NewEnv(t, keys, []string{dmsg.Type}) - defer nEnv.Teardown() - - tpD := transport.NewDiscoveryMock() - - // Prepare transports - m0, m1, _, _, err := transport.CreateTransportPair(tpD, keys[:2], nEnv, dmsg.Type) - require.NoError(t, err) - - forward := [2]cipher.PubKey{keys[0].PK, keys[1].PK} - backward := [2]cipher.PubKey{keys[1].PK, keys[0].PK} - - // Paths to be returned from route finder - rfPaths := make(map[routing.PathEdges][][]routing.Hop) - rfPaths[forward] = append(rfPaths[forward], forwardHops) - rfPaths[backward] = append(rfPaths[backward], reverseHops) - - rfCl := &rfclient.MockClient{} - rfCl.On("FindRoutes", mock.Anything, []routing.PathEdges{forward, backward}, - &rfclient.RouteOptions{MinHops: minHops, MaxHops: maxHops}).Return(rfPaths, testhelpers.NoErr) - - r0Logger := logging.MustGetLogger(fmt.Sprintf("router_%d", 0)) - - fwdRt, revRt := route.ForwardAndReverse() - srcPK := route.Desc.SrcPK() - dstPK := route.Desc.DstPK() - - fwdRules0 := routing.ForwardRule(route.KeepAlive, 1, 2, forwardHops[0].TpID, srcPK, dstPK, 1, 1) - revRules0 := routing.ConsumeRule(route.KeepAlive, 3, srcPK, dstPK, 1, 1) - - // Edge rules to be returned from route group dialer - initEdge := routing.EdgeRules{Desc: revRt.Desc, Forward: fwdRules0, Reverse: revRules0} - - setupCl0 := &setupclient.MockRouteGroupDialer{} - setupCl0.On("Dial", mock.Anything, r0Logger, nEnv.Nets[0], mock.Anything, route). - Return(initEdge, testhelpers.NoErr) - - r0Conf := &Config{ - Logger: r0Logger, - PubKey: keys[0].PK, - SecKey: keys[0].SK, - TransportManager: m0, - RouteFinder: rfCl, - RouteGroupDialer: setupCl0, - } - - // Create routers - r0Ifc, err := New(nEnv.Nets[0], r0Conf) - require.NoError(t, err) - - r0, ok := r0Ifc.(*router) - require.True(t, ok) - - r1Conf := &Config{ - Logger: logging.MustGetLogger(fmt.Sprintf("router_%d", 1)), - PubKey: keys[1].PK, - SecKey: keys[1].SK, - TransportManager: m1, - } - - r1Ifc, err := New(nEnv.Nets[1], r1Conf) - require.NoError(t, err) - - r1, ok := r1Ifc.(*router) - require.True(t, ok) - - ctx := context.Background() - - nrg1IfcCh := make(chan net.Conn) - acceptErrCh := make(chan error) - go func() { - nrg1Ifc, err := r1.AcceptRoutes(ctx) - acceptErrCh <- err - nrg1IfcCh <- nrg1Ifc - close(acceptErrCh) - close(nrg1IfcCh) - }() - - dialErrCh := make(chan error) - nrg0IfcCh := make(chan net.Conn) - go func() { - nrg0Ifc, err := r0.DialRoutes(context.Background(), r1.conf.PubKey, 1, 1, nil) - dialErrCh <- err - nrg0IfcCh <- nrg0Ifc - close(dialErrCh) - close(nrg0IfcCh) - }() - - fwdRules1 := routing.ForwardRule(route.KeepAlive, 4, 3, reverseHops[0].TpID, dstPK, srcPK, 1, 1) - revRules1 := routing.ConsumeRule(route.KeepAlive, 2, dstPK, srcPK, 1, 1) - - // This edge is returned by the setup node to accepting router - respEdge := routing.EdgeRules{Desc: fwdRt.Desc, Forward: fwdRules1, Reverse: revRules1} - - // Unblock AcceptRoutes, imitates setup node request with EdgeRules - r1.accept <- respEdge - - // At some point raw route group gets into `rgsRaw` and waits for - // handshake packets. we're waiting for this moment in the cycle - // to start passing packets from the transport to route group - for { - r0.mx.Lock() - if _, ok := r0.rgsRaw[initEdge.Desc]; ok { - rg := r0.rgsRaw[initEdge.Desc] - go pushPackets(ctx, m0, rg) - r0.mx.Unlock() - break - } - r0.mx.Unlock() - } - - for { - r1.mx.Lock() - if _, ok := r1.rgsRaw[respEdge.Desc]; ok { - rg := r1.rgsRaw[respEdge.Desc] - go pushPackets(ctx, m1, rg) - r1.mx.Unlock() - break - } - r1.mx.Unlock() - } - - require.NoError(t, <-acceptErrCh) - require.NoError(t, <-dialErrCh) - - nrg0Ifc := <-nrg0IfcCh - require.NotNil(t, nrg0Ifc) - nrg1Ifc := <-nrg1IfcCh - require.NotNil(t, nrg1Ifc) - - nrg0, ok := nrg0Ifc.(*NoiseRouteGroup) - require.True(t, ok) - require.NotNil(t, nrg0) - - nrg1, ok := nrg1Ifc.(*NoiseRouteGroup) - require.True(t, ok) - require.NotNil(t, nrg1) - - data := []byte("Hello there!") - n, err := nrg0.Write(data) - require.NoError(t, err) - require.Equal(t, len(data), n) - - received := make([]byte, 1024) - n, err = nrg1.Read(received) - require.NoError(t, err) - require.Equal(t, len(data), n) - require.Equal(t, data, received[:n]) - - require.True(t, nrg0.IsAlive()) - require.True(t, nrg1.IsAlive()) - - err = nrg0.Close() - require.NoError(t, err) - - require.False(t, nrg0.IsAlive()) - require.False(t, nrg1.IsAlive()) - - require.True(t, nrg1.rg.isRemoteClosed()) - err = nrg1.Close() - require.NoError(t, err) - require.False(t, nrg1.IsAlive()) -} - -func TestRouter_Serve(t *testing.T) { - // We are generating two key pairs - one for the a `Router`, the other to send packets to `Router`. - keys := snettest.GenKeyPairs(2) - - // create test env - nEnv := snettest.NewEnv(t, keys, []string{dmsg.Type}) - defer nEnv.Teardown() - - rEnv := NewTestEnv(t, nEnv.Nets) - defer rEnv.Teardown() - - // Create routers - r0Ifc, err := New(nEnv.Nets[0], rEnv.GenRouterConfig(0)) - require.NoError(t, err) - - r0, ok := r0Ifc.(*router) - require.True(t, ok) - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - require.NoError(t, r0.tm.Close()) - require.NoError(t, r0.Serve(ctx)) -} - const ruleKeepAlive = 1 * time.Hour -// Ensure that received packets are handled properly in `(*Router).handleTransportPacket()`. -func TestRouter_handleTransportPacket(t *testing.T) { - // We are generating two key pairs - one for the a `Router`, the other to send packets to `Router`. - keys := snettest.GenKeyPairs(2) - - pk1 := keys[0].PK - pk2 := keys[1].PK - - // create test env - nEnv := snettest.NewEnv(t, keys, []string{dmsg.Type}) - defer nEnv.Teardown() - - rEnv := NewTestEnv(t, nEnv.Nets) - defer rEnv.Teardown() - - // Create routers - r0Ifc, err := New(nEnv.Nets[0], rEnv.GenRouterConfig(0)) - require.NoError(t, err) - - r0, ok := r0Ifc.(*router) - require.True(t, ok) - - r1Ifc, err := New(nEnv.Nets[1], rEnv.GenRouterConfig(1)) - require.NoError(t, err) - - r1, ok := r1Ifc.(*router) - require.True(t, ok) - - defer func() { - require.NoError(t, r0.Close()) - require.NoError(t, r1.Close()) - }() - - // Create dmsg transport between two `snet.Network` entities. - tp1, err := rEnv.TpMngrs[1].SaveTransport(context.TODO(), pk1, dmsg.Type, transport.LabelUser) - require.NoError(t, err) - - testHandlePackets(t, r0, r1, tp1, pk1, pk2) -} - func testHandlePackets(t *testing.T, r0, r1 *router, tp1 *transport.ManagedTransport, pk1, pk2 cipher.PubKey) { var wg sync.WaitGroup @@ -632,47 +369,6 @@ func testConsumeRule(t *testing.T, r0, r1 *router, tp1 *transport.ManagedTranspo require.Equal(t, consumeMsg, data) } -func TestRouter_Rules(t *testing.T) { - pk, sk := cipher.GenerateKeyPair() - - env := snettest.NewEnv(t, []snettest.KeyPair{{PK: pk, SK: sk}}, []string{dmsg.Type}) - defer env.Teardown() - - rt := routing.NewTable() - - // We are generating two key pairs - one for the a `Router`, the other to send packets to `Router`. - keys := snettest.GenKeyPairs(2) - - // create test env - nEnv := snettest.NewEnv(t, keys, []string{dmsg.Type}) - defer nEnv.Teardown() - - rEnv := NewTestEnv(t, nEnv.Nets) - defer rEnv.Teardown() - - rIfc, err := New(nEnv.Nets[0], rEnv.GenRouterConfig(0)) - require.NoError(t, err) - - r, ok := rIfc.(*router) - require.True(t, ok) - - defer func() { - require.NoError(t, r.Close()) - }() - - r.rt = rt - - // TEST: Set and get expired and unexpired rule. - t.Run("GetRule", func(t *testing.T) { - testGetRule(t, r, rt) - }) - - // TEST: Ensure removing route descriptor works properly. - t.Run("RemoveRouteDescriptor", func(t *testing.T) { - testRemoveRouteDescriptor(t, r, rt) - }) -} - func testRemoveRouteDescriptor(t *testing.T, r *router, rt routing.Table) { clearRoutingTableRules(rt) @@ -729,25 +425,6 @@ func testGetRule(t *testing.T, r *router, rt routing.Table) { assert.Equal(t, rule, gotRule) } -func TestRouter_SetupIsTrusted(t *testing.T) { - keys := snettest.GenKeyPairs(2) - - nEnv := snettest.NewEnv(t, keys, []string{dmsg.Type}) - defer nEnv.Teardown() - - rEnv := NewTestEnv(t, nEnv.Nets) - defer rEnv.Teardown() - - routerConfig := rEnv.GenRouterConfig(0) - routerConfig.SetupNodes = append(routerConfig.SetupNodes, keys[0].PK) - - r0, err := New(nEnv.Nets[0], routerConfig) - require.NoError(t, err) - - assert.True(t, r0.SetupIsTrusted(keys[0].PK)) - assert.False(t, r0.SetupIsTrusted(keys[1].PK)) -} - func clearRouteGroups(routers ...*router) { for _, r := range routers { r.rgsNs = make(map[routing.RouteDescriptor]*NoiseRouteGroup) @@ -779,42 +456,6 @@ type TestEnv struct { teardown func() } -func NewTestEnv(t *testing.T, nets []*snet.Network) *TestEnv { - tpD := transport.NewDiscoveryMock() - - mConfs := make([]*transport.ManagerConfig, len(nets)) - ms := make([]*transport.Manager, len(nets)) - - for i, n := range nets { - var err error - - mConfs[i] = &transport.ManagerConfig{ - PubKey: n.LocalPK(), - SecKey: n.LocalSK(), - DiscoveryClient: tpD, - LogStore: transport.InMemoryTransportLogStore(), - } - - ms[i], err = transport.NewManager(nil, n, mConfs[i]) - require.NoError(t, err) - - go ms[i].Serve(context.TODO()) - } - - teardown := func() { - for _, m := range ms { - assert.NoError(t, m.Close()) - } - } - - return &TestEnv{ - TpD: tpD, - TpMngrConfs: mConfs, - TpMngrs: ms, - teardown: teardown, - } -} - func (e *TestEnv) GenRouterConfig(i int) *Config { return &Config{ Logger: logging.MustGetLogger(fmt.Sprintf("router_%d", i)), diff --git a/pkg/setup/testing_test.go b/pkg/setup/testing_test.go index e9d263eb8..c539bb634 100644 --- a/pkg/setup/testing_test.go +++ b/pkg/setup/testing_test.go @@ -17,11 +17,11 @@ import ( "github.com/skycoin/skywire/pkg/router/routerclient" "github.com/skycoin/skywire/pkg/routing" - "github.com/skycoin/skywire/pkg/snet" + "github.com/skycoin/skywire/pkg/transport/network" ) // creates a mock dialer -func newMockDialer(t *testing.T, gateways map[cipher.PubKey]interface{}) snet.Dialer { +func newMockDialer(t *testing.T, gateways map[cipher.PubKey]interface{}) network.Dialer { newRPCConn := func(gw interface{}) net.Conn { connC, connS := net.Pipe() t.Cleanup(func() { @@ -38,7 +38,7 @@ func newMockDialer(t *testing.T, gateways map[cipher.PubKey]interface{}) snet.Di if gateways == nil { conn := newRPCConn(new(mockGatewayForDialer)) - dialer := new(snet.MockDialer) + dialer := new(network.MockDialer) dialer.On("Dial", mock.Anything, mock.Anything, mock.Anything).Return(conn, nil) return dialer } diff --git a/pkg/snet/network_test.go b/pkg/snet/network_test.go deleted file mode 100644 index 3dce54c36..000000000 --- a/pkg/snet/network_test.go +++ /dev/null @@ -1,21 +0,0 @@ -package snet - -import ( - "testing" - - "github.com/skycoin/dmsg" - "github.com/skycoin/dmsg/cipher" - "github.com/stretchr/testify/require" -) - -func TestDisassembleAddr(t *testing.T) { - pk, _ := cipher.GenerateKeyPair() - port := uint16(2) - addr := dmsg.Addr{ - PK: pk, Port: port, - } - - gotPK, gotPort := disassembleAddr(addr) - require.Equal(t, pk, gotPK) - require.Equal(t, port, gotPort) -} diff --git a/pkg/transport/handshake_test.go b/pkg/transport/handshake_test.go deleted file mode 100644 index d0225a768..000000000 --- a/pkg/transport/handshake_test.go +++ /dev/null @@ -1,62 +0,0 @@ -package transport_test - -import ( - "context" - "testing" - "time" - - "github.com/skycoin/dmsg" - "github.com/stretchr/testify/require" - - "github.com/skycoin/skywire/pkg/skyenv" - "github.com/skycoin/skywire/pkg/snet/snettest" - "github.com/skycoin/skywire/pkg/transport" -) - -func TestSettlementHS(t *testing.T) { - tpDisc := transport.NewDiscoveryMock() - - keys := snettest.GenKeyPairs(2) - nEnv := snettest.NewEnv(t, keys, []string{dmsg.Type}) - defer nEnv.Teardown() - - // TEST: Perform a handshake between two snet.Network instances. - t.Run("Do", func(t *testing.T) { - lis1, err := nEnv.Nets[1].Listen(dmsg.Type, skyenv.DmsgTransportPort) - require.NoError(t, err) - - errCh1 := make(chan error, 1) - go func() { - defer close(errCh1) - conn1, err := lis1.AcceptConn() - if err != nil { - errCh1 <- err - return - } - errCh1 <- transport.MakeSettlementHS(false).Do(context.TODO(), tpDisc, conn1, keys[1].SK) - }() - - const entryTimeout = 5 * time.Second - start := time.Now() - - // Wait until entry is set. - // TODO: Implement more elegant solution. - for { - if time.Since(start) > entryTimeout { - t.Fatal("Entry in Dmsg Discovery is not set within expected time") - } - - if _, err := nEnv.DmsgD.Entry(context.TODO(), keys[1].PK); err == nil { - break - } - } - - conn0, err := nEnv.Nets[0].Dial(context.TODO(), dmsg.Type, keys[1].PK, skyenv.DmsgTransportPort) - require.NoError(t, err) - require.NoError(t, transport.MakeSettlementHS(true).Do(context.TODO(), tpDisc, conn0, keys[0].SK)) - - require.NoError(t, <-errCh1) - }) -} - -// TODO(evanlinjin): This will need further testing. diff --git a/pkg/transport/manager_test.go b/pkg/transport/manager_test.go index 1a37b2e44..33d229a4a 100644 --- a/pkg/transport/manager_test.go +++ b/pkg/transport/manager_test.go @@ -1,22 +1,15 @@ package transport_test import ( - "context" - "fmt" "io/ioutil" "log" "os" "testing" - "time" - "github.com/skycoin/dmsg" "github.com/skycoin/dmsg/cipher" "github.com/skycoin/skycoin/src/util/logging" - "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/skycoin/skywire/pkg/routing" - "github.com/skycoin/skywire/pkg/snet/snettest" "github.com/skycoin/skywire/pkg/transport" ) @@ -37,127 +30,6 @@ func TestMain(m *testing.M) { os.Exit(m.Run()) } - -// TODO: test hangs if Manager is closed to early, needs to receive an error though -func TestNewManager(t *testing.T) { - tpDisc := transport.NewDiscoveryMock() - - keys := snettest.GenKeyPairs(2) - nEnv := snettest.NewEnv(t, keys, []string{dmsg.Type}) - defer nEnv.Teardown() - - // Prepare tp manager 0. - pk0, sk0 := keys[0].PK, keys[0].SK - ls0 := transport.InMemoryTransportLogStore() - m0, err := transport.NewManager(nil, nEnv.Nets[0], &transport.ManagerConfig{ - PubKey: pk0, - SecKey: sk0, - DiscoveryClient: tpDisc, - LogStore: ls0, - }) - require.NoError(t, err) - go m0.Serve(context.TODO()) - defer func() { require.NoError(t, m0.Close()) }() - - // Prepare tp manager 1. - pk1, sk1 := keys[1].PK, keys[1].SK - ls1 := transport.InMemoryTransportLogStore() - m2, err := transport.NewManager(nil, nEnv.Nets[1], &transport.ManagerConfig{ - PubKey: pk1, - SecKey: sk1, - DiscoveryClient: tpDisc, - LogStore: ls1, - }) - require.NoError(t, err) - go m2.Serve(context.TODO()) - defer func() { require.NoError(t, m2.Close()) }() - - // Create data transport between manager 1 & manager 2. - tp2, err := m2.SaveTransport(context.TODO(), pk0, "dmsg", transport.LabelUser) - require.NoError(t, err) - tp1 := m0.Transport(transport.MakeTransportID(pk0, pk1, "dmsg")) - require.NotNil(t, tp1) - - fmt.Println("transports created") - - totalSent2 := 0 - totalSent1 := 0 - - // Check read/writes are of expected. - t.Run("check_read_write", func(t *testing.T) { - - for i := 0; i < 10; i++ { - totalSent2 += i - rID := routing.RouteID(i) - payload := cipher.RandByte(i) - - packet, err := routing.MakeDataPacket(rID, payload) - require.NoError(t, err) - - require.NoError(t, tp2.WritePacket(context.TODO(), packet)) - - recv, err := m0.ReadPacket() - require.NoError(t, err) - require.Equal(t, rID, recv.RouteID()) - require.Equal(t, uint16(i), recv.Size()) - require.Equal(t, payload, recv.Payload()) - } - - for i := 0; i < 20; i++ { - totalSent1 += i - rID := routing.RouteID(i) - payload := cipher.RandByte(i) - - packet, err := routing.MakeDataPacket(rID, payload) - require.NoError(t, err) - - require.NoError(t, tp1.WritePacket(context.TODO(), packet)) - - recv, err := m2.ReadPacket() - require.NoError(t, err) - require.Equal(t, rID, recv.RouteID()) - require.Equal(t, uint16(i), recv.Size()) - require.Equal(t, payload, recv.Payload()) - } - }) - - // Ensure tp log entries are of expected. - t.Run("check_tp_logs", func(t *testing.T) { - - // 1.5x log write interval just to be safe. - time.Sleep(time.Second * 9 / 2) - - entry1, err := ls0.Entry(tp1.Entry.ID) - require.NoError(t, err) - assert.Equal(t, uint64(totalSent1), entry1.SentBytes) - assert.Equal(t, uint64(totalSent2), entry1.RecvBytes) - - entry2, err := ls1.Entry(tp2.Entry.ID) - require.NoError(t, err) - assert.Equal(t, uint64(totalSent2), entry2.SentBytes) - assert.Equal(t, uint64(totalSent1), entry2.RecvBytes) - }) - - // Ensure deleting a transport works as expected. - t.Run("check_delete_tp", func(t *testing.T) { - - // Make transport ID. - tpID := transport.MakeTransportID(pk0, pk1, "dmsg") - - // Ensure transports are registered properly in tp discovery. - entry, err := tpDisc.GetTransportByID(context.TODO(), tpID) - require.NoError(t, err) - assert.Equal(t, transport.SortEdges(pk0, pk1), entry.Entry.Edges) - assert.True(t, entry.IsUp) - - m2.DeleteTransport(tp2.Entry.ID) - - _, err = tpDisc.GetTransportByID(context.TODO(), tpID) - require.NotNil(t, err) - require.Contains(t, err.Error(), "not found") - }) -} - func TestSortEdges(t *testing.T) { for i := 0; i < 100; i++ { keyA, _ := cipher.GenerateKeyPair() diff --git a/pkg/transport/network/addrresolver/mock_api_client.go b/pkg/transport/network/addrresolver/mock_api_client.go index dcb5d3516..a6cd7281d 100644 --- a/pkg/transport/network/addrresolver/mock_api_client.go +++ b/pkg/transport/network/addrresolver/mock_api_client.go @@ -30,7 +30,7 @@ func (_m *MockAPIClient) BindSTCPR(ctx context.Context, port string) error { } // BindSUDPH provides a mock function with given fields: filter -func (_m *MockAPIClient) BindSUDPH(filter *pfilter.PacketFilter) (<-chan RemoteVisor, error) { +func (_m *MockAPIClient) BindSUDPH(filter *pfilter.PacketFilter, handshake Handshake) (<-chan RemoteVisor, error) { ret := _m.Called(filter) var r0 <-chan RemoteVisor diff --git a/pkg/visor/rpc_test.go b/pkg/visor/rpc_test.go index 178506d47..e064b4ed4 100644 --- a/pkg/visor/rpc_test.go +++ b/pkg/visor/rpc_test.go @@ -15,8 +15,8 @@ import ( "github.com/skycoin/skywire/internal/testhelpers" "github.com/skycoin/skywire/internal/utclient" "github.com/skycoin/skywire/pkg/routefinder/rfclient" - "github.com/skycoin/skywire/pkg/snet/arclient" "github.com/skycoin/skywire/pkg/transport" + "github.com/skycoin/skywire/pkg/transport/network/addrresolver" "github.com/skycoin/skywire/pkg/visor/visorconfig" ) @@ -40,7 +40,7 @@ func TestHealth(t *testing.T) { utClient := &utclient.MockAPIClient{} utClient.On("Health", mock.Anything).Return(http.StatusOK, nil) - arClient := &arclient.MockAPIClient{} + arClient := &addrresolver.MockAPIClient{} arClient.On("Health", mock.Anything).Return(http.StatusOK, nil) rfClient := &rfclient.MockClient{} From 4d8b803bc8ef22ce204390e6b3e826656cbd26f1 Mon Sep 17 00:00:00 2001 From: Never M Date: Fri, 2 Jul 2021 12:53:09 +0300 Subject: [PATCH 47/55] Switch from dmsg.Type to network.DMSG --- cmd/skywire-cli/commands/visor/transports.go | 3 +-- pkg/router/routerclient/dmsg_wrapper.go | 2 +- pkg/router/routerclient/map_test.go | 4 ++-- pkg/setup/testing_test.go | 3 +-- 4 files changed, 5 insertions(+), 7 deletions(-) diff --git a/cmd/skywire-cli/commands/visor/transports.go b/cmd/skywire-cli/commands/visor/transports.go index 1c53d6298..99cf89cd1 100644 --- a/cmd/skywire-cli/commands/visor/transports.go +++ b/cmd/skywire-cli/commands/visor/transports.go @@ -7,7 +7,6 @@ import ( "text/tabwriter" "time" - "github.com/skycoin/dmsg" "github.com/skycoin/dmsg/cipher" "github.com/spf13/cobra" @@ -117,7 +116,7 @@ var addTpCmd = &cobra.Command{ network.STCP, network.STCPR, network.SUDPH, - dmsg.Type, + network.DMSG, } for _, transportType := range transportTypes { diff --git a/pkg/router/routerclient/dmsg_wrapper.go b/pkg/router/routerclient/dmsg_wrapper.go index 558692614..c3f05ad09 100644 --- a/pkg/router/routerclient/dmsg_wrapper.go +++ b/pkg/router/routerclient/dmsg_wrapper.go @@ -23,5 +23,5 @@ func (w *dmsgClientDialer) Dial(ctx context.Context, remote cipher.PubKey, port } func (w *dmsgClientDialer) Type() string { - return dmsg.Type + return string(network.DMSG) } diff --git a/pkg/router/routerclient/map_test.go b/pkg/router/routerclient/map_test.go index f8d7b1a04..d066f1781 100644 --- a/pkg/router/routerclient/map_test.go +++ b/pkg/router/routerclient/map_test.go @@ -10,7 +10,6 @@ import ( "time" "github.com/sirupsen/logrus" - "github.com/skycoin/dmsg" "github.com/skycoin/dmsg/cipher" "github.com/skycoin/skycoin/src/util/logging" "github.com/stretchr/testify/assert" @@ -20,6 +19,7 @@ import ( "github.com/skycoin/skywire/pkg/router" "github.com/skycoin/skywire/pkg/routing" + "github.com/skycoin/skywire/pkg/transport/network" ) func TestMakeMap(t *testing.T) { @@ -160,5 +160,5 @@ func (d *testDialer) Dial(_ context.Context, remote cipher.PubKey, _ uint16) (ne } func (testDialer) Type() string { - return dmsg.Type + return string(network.DMSG) } diff --git a/pkg/setup/testing_test.go b/pkg/setup/testing_test.go index c539bb634..b07dd14a7 100644 --- a/pkg/setup/testing_test.go +++ b/pkg/setup/testing_test.go @@ -9,7 +9,6 @@ import ( "testing" "time" - "github.com/skycoin/dmsg" "github.com/skycoin/dmsg/cipher" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" @@ -52,7 +51,7 @@ func newMockDialer(t *testing.T, gateways map[cipher.PubKey]interface{}) network type mockDialer map[cipher.PubKey]net.Conn -func (d mockDialer) Type() string { return dmsg.Type } +func (d mockDialer) Type() string { return string(network.DMSG) } func (d mockDialer) Dial(_ context.Context, remote cipher.PubKey, _ uint16) (net.Conn, error) { conn, ok := d[remote] From 1936d2bfc8565a8bbed286bd1aadcc4f44ea11ed Mon Sep 17 00:00:00 2001 From: Never M Date: Fri, 2 Jul 2021 12:54:15 +0300 Subject: [PATCH 48/55] Stop initialization when no dmsg config --- pkg/visor/init.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pkg/visor/init.go b/pkg/visor/init.go index 93004fd96..0bd6c4f0e 100644 --- a/pkg/visor/init.go +++ b/pkg/visor/init.go @@ -183,8 +183,7 @@ func initDiscovery(ctx context.Context, v *Visor, log *logging.Logger) error { func initDmsg(ctx context.Context, v *Visor, log *logging.Logger) error { if v.conf.Dmsg == nil { - // todo: this is preserved behavior from snet. Do we need it? - return nil + return fmt.Errorf("cannot initialize dmsg: empty configuration") } dmsgC := dmsgc.New(v.conf.PK, v.conf.SK, v.ebc, v.conf.Dmsg) From 6998fdebc8b63b34be86b36faaae3db803b68dc2 Mon Sep 17 00:00:00 2001 From: Never M Date: Fri, 2 Jul 2021 13:03:18 +0300 Subject: [PATCH 49/55] Move dmsgc out of snet --- pkg/{snet => }/dmsgc/dmsgc.go | 0 pkg/setup/config.go | 2 +- pkg/visor/init.go | 2 +- pkg/visor/visorconfig/config.go | 2 +- pkg/visor/visorconfig/v0.go | 2 +- pkg/visor/visorconfig/v1.go | 2 +- 6 files changed, 5 insertions(+), 5 deletions(-) rename pkg/{snet => }/dmsgc/dmsgc.go (100%) diff --git a/pkg/snet/dmsgc/dmsgc.go b/pkg/dmsgc/dmsgc.go similarity index 100% rename from pkg/snet/dmsgc/dmsgc.go rename to pkg/dmsgc/dmsgc.go diff --git a/pkg/setup/config.go b/pkg/setup/config.go index 9fa21d8eb..295a49b3e 100644 --- a/pkg/setup/config.go +++ b/pkg/setup/config.go @@ -5,7 +5,7 @@ import ( "github.com/skycoin/dmsg/cipher" - "github.com/skycoin/skywire/pkg/snet/dmsgc" + "github.com/skycoin/skywire/pkg/dmsgc" ) //go:generate readmegen -n Config -o ./README.md ./config.go diff --git a/pkg/visor/init.go b/pkg/visor/init.go index 0bd6c4f0e..c45437e18 100644 --- a/pkg/visor/init.go +++ b/pkg/visor/init.go @@ -28,7 +28,7 @@ import ( "github.com/skycoin/skywire/pkg/servicedisc" "github.com/skycoin/skywire/pkg/setup/setupclient" "github.com/skycoin/skywire/pkg/skyenv" - "github.com/skycoin/skywire/pkg/snet/dmsgc" + "github.com/skycoin/skywire/pkg/dmsgc" "github.com/skycoin/skywire/pkg/transport" "github.com/skycoin/skywire/pkg/transport/network" "github.com/skycoin/skywire/pkg/transport/network/addrresolver" diff --git a/pkg/visor/visorconfig/config.go b/pkg/visor/visorconfig/config.go index 313e639f4..f6f8d3551 100644 --- a/pkg/visor/visorconfig/config.go +++ b/pkg/visor/visorconfig/config.go @@ -8,7 +8,7 @@ import ( "github.com/skycoin/skywire/pkg/restart" "github.com/skycoin/skywire/pkg/routing" "github.com/skycoin/skywire/pkg/skyenv" - "github.com/skycoin/skywire/pkg/snet/dmsgc" + "github.com/skycoin/skywire/pkg/dmsgc" "github.com/skycoin/skywire/pkg/transport/network" "github.com/skycoin/skywire/pkg/visor/hypervisorconfig" ) diff --git a/pkg/visor/visorconfig/v0.go b/pkg/visor/visorconfig/v0.go index 79ace6917..00f3ccee5 100644 --- a/pkg/visor/visorconfig/v0.go +++ b/pkg/visor/visorconfig/v0.go @@ -4,7 +4,7 @@ import ( "github.com/skycoin/dmsg/cipher" "github.com/skycoin/skywire/pkg/routing" - "github.com/skycoin/skywire/pkg/snet/dmsgc" + "github.com/skycoin/skywire/pkg/dmsgc" "github.com/skycoin/skywire/pkg/transport/network" ) diff --git a/pkg/visor/visorconfig/v1.go b/pkg/visor/visorconfig/v1.go index c35214c5f..42815688d 100644 --- a/pkg/visor/visorconfig/v1.go +++ b/pkg/visor/visorconfig/v1.go @@ -8,7 +8,7 @@ import ( "github.com/skycoin/dmsg/cipher" "github.com/skycoin/skywire/pkg/app/launcher" - "github.com/skycoin/skywire/pkg/snet/dmsgc" + "github.com/skycoin/skywire/pkg/dmsgc" "github.com/skycoin/skywire/pkg/transport/network" "github.com/skycoin/skywire/pkg/visor/hypervisorconfig" ) From e325669263f7a2325d16488927e4408c156cd0dd Mon Sep 17 00:00:00 2001 From: Never M Date: Fri, 2 Jul 2021 17:50:45 +0300 Subject: [PATCH 50/55] Cleanup ready todos --- pkg/transport/network/client.go | 18 ++++++++---------- pkg/transport/network/connection.go | 2 -- pkg/transport/network/dmsg.go | 9 ++++----- pkg/transport/network/listener.go | 1 - pkg/transport/network/sudph.go | 15 +++++++++++---- pkg/visor/init.go | 7 +------ 6 files changed, 24 insertions(+), 28 deletions(-) diff --git a/pkg/transport/network/client.go b/pkg/transport/network/client.go index f32d15338..6ce2f3dce 100644 --- a/pkg/transport/network/client.go +++ b/pkg/transport/network/client.go @@ -32,7 +32,8 @@ type Client interface { // for different ports for the same client. It requires Start to be called // to start accepting connections Listen(port uint16) (Listener, error) - // todo: remove + // LocalAddr returns the actual network address under which this client listens to + // new connections LocalAddr() (net.Addr, error) // PK returns public key of the visor running this client PK() cipher.PubKey @@ -58,7 +59,7 @@ type ClientFactory struct { } // MakeClient creates a new client of specified type -func (f *ClientFactory) MakeClient(netType Type) Client { +func (f *ClientFactory) MakeClient(netType Type) (Client, error) { log := logging.MustGetLogger(string(netType)) p := porter.New(porter.MinEphemeral) @@ -77,16 +78,15 @@ func (f *ClientFactory) MakeClient(netType Type) Client { switch netType { case STCP: - return newStcp(generic, f.PKTable) + return newStcp(generic, f.PKTable), nil case STCPR: - return newStcpr(resolved) + return newStcpr(resolved), nil case SUDPH: - return newSudph(resolved) + return newSudph(resolved), nil case DMSG: - return newDmsgClient(f.DmsgC) + return newDmsgClient(f.DmsgC), nil } - // todo: maybe return an error that type is not found - return nil + return nil, fmt.Errorf("cannot initiate client, type %s not supported", netType) } // genericClient unites common logic for all clients @@ -130,7 +130,6 @@ func (c *genericClient) initConnection(ctx context.Context, conn net.Conn, rPK c return c.wrapConn(conn, hs, true, freePort) } -// todo: context? // acceptConnections continuously accepts incoming connections that come from given listener // these connections will be properly handshaked and passed to an appropriate skywire listener // using skywire port @@ -197,7 +196,6 @@ func (c *genericClient) acceptConn() error { return lis.introduce(wrappedConn) } -// todo: remove // LocalAddr returns local address. This is network address the client // listens to for incoming connections, not skywire address func (c *genericClient) LocalAddr() (net.Addr, error) { diff --git a/pkg/transport/network/connection.go b/pkg/transport/network/connection.go index c64e65c90..ec872cccd 100644 --- a/pkg/transport/network/connection.go +++ b/pkg/transport/network/connection.go @@ -84,7 +84,6 @@ func (c *conn) encrypt(lPK cipher.PubKey, lSK cipher.SecKey, initator bool) erro } // EncryptConn encrypts given connection -// todo: make private when tpconn.Conn is gone func EncryptConn(config noise.Config, conn net.Conn) (net.Conn, error) { ns, err := noise.New(noise.HandshakeKK, config) if err != nil { @@ -133,5 +132,4 @@ func (c *conn) LocalPort() uint16 { return c.lAddr.Port } func (c *conn) RemotePort() uint16 { return c.rAddr.Port } // Network returns network of connection -// todo: consider switching to Type instead of string func (c *conn) Network() Type { return c.connType } diff --git a/pkg/transport/network/dmsg.go b/pkg/transport/network/dmsg.go index 02a0a19e6..17ea6f73f 100644 --- a/pkg/transport/network/dmsg.go +++ b/pkg/transport/network/dmsg.go @@ -38,8 +38,7 @@ func (c *dmsgClientAdapter) Dial(ctx context.Context, remote cipher.PubKey, port // Start implements Client interface func (c *dmsgClientAdapter) Start() error { - // todo: update interface to pass context properly? - go c.dmsgC.Serve(context.TODO()) + // no need to serve, the wrapped dmsgC is already serving return nil } @@ -64,9 +63,9 @@ func (c *dmsgClientAdapter) SK() cipher.SecKey { // Close implements Client interface func (c *dmsgClientAdapter) Close() error { - // todo: maybe not, the dmsg instance we get is the global one that is used - // in plenty other places - return c.dmsgC.Close() + // this client is for transport usage, but dmsgC it wraps may be used in + // other places. It should be closed by whoever initialized it, not here + return nil } // Type implements Client interface diff --git a/pkg/transport/network/listener.go b/pkg/transport/network/listener.go index 5f2d0ecef..d71380185 100644 --- a/pkg/transport/network/listener.go +++ b/pkg/transport/network/listener.go @@ -92,7 +92,6 @@ func (l *listener) Port() uint16 { } // Network returns network type -// todo: consider switching to Type instead of string func (l *listener) Network() Type { return l.network } diff --git a/pkg/transport/network/sudph.go b/pkg/transport/network/sudph.go index af99df5df..fc4bef3e3 100644 --- a/pkg/transport/network/sudph.go +++ b/pkg/transport/network/sudph.go @@ -55,6 +55,7 @@ func (c *sudphClient) serve() { c.acceptConnections(lis) } +// listen func (c *sudphClient) listen() (net.Listener, error) { packetListener, err := net.ListenPacket("udp", "") if err != nil { @@ -68,7 +69,7 @@ func (c *sudphClient) listen() (net.Listener, error) { if err != nil { return nil, err } - go c.PICKNAMEFORME(sudphVisorsConn, addrCh) + go c.acceptAddresses(sudphVisorsConn, addrCh) return kcp.ServeConn(nil, 0, 0, sudphVisorsConn) } @@ -81,8 +82,12 @@ func (c *sudphClient) makeBindHandshake() func(in net.Conn) (net.Conn, error) { } } -// todo: name -func (c *sudphClient) PICKNAMEFORME(conn net.PacketConn, addrCh <-chan addrresolver.RemoteVisor) { +// acceptAddresses will read visor addresses from addrCh and send holepunch +// packets to them +// Basically each address coming from addrCh is a dial request from some remote +// visor to us. Dialing visor contacts address resolver and gives the address to +// it, address resolver in turn sends us this address. +func (c *sudphClient) acceptAddresses(conn net.PacketConn, addrCh <-chan addrresolver.RemoteVisor) { for addr := range addrCh { udpAddr, err := net.ResolveUDPAddr("udp", addr.Addr) if err != nil { @@ -105,7 +110,7 @@ func (c *sudphClient) Dial(ctx context.Context, rPK cipher.PubKey, rPort uint16) if c.isClosed() { return nil, io.ErrClosedPipe } - + // this will lookup visor address in address resolver and then dial that address conn, err := c.dialVisor(ctx, rPK, c.dialWithTimeout) if err != nil { return nil, err @@ -135,6 +140,8 @@ func (c *sudphClient) dialWithTimeout(ctx context.Context, addr string) (net.Con } } +// dial will send holepunch packet to the remote addr over UDP, and +// return the connection func (c *sudphClient) dial(remoteAddr string) (net.Conn, error) { rAddr, err := net.ResolveUDPAddr("udp", remoteAddr) if err != nil { diff --git a/pkg/visor/init.go b/pkg/visor/init.go index c45437e18..f6f4ca5a8 100644 --- a/pkg/visor/init.go +++ b/pkg/visor/init.go @@ -23,12 +23,12 @@ import ( "github.com/skycoin/skywire/pkg/app/appevent" "github.com/skycoin/skywire/pkg/app/appserver" "github.com/skycoin/skywire/pkg/app/launcher" + "github.com/skycoin/skywire/pkg/dmsgc" "github.com/skycoin/skywire/pkg/routefinder/rfclient" "github.com/skycoin/skywire/pkg/router" "github.com/skycoin/skywire/pkg/servicedisc" "github.com/skycoin/skywire/pkg/setup/setupclient" "github.com/skycoin/skywire/pkg/skyenv" - "github.com/skycoin/skywire/pkg/dmsgc" "github.com/skycoin/skywire/pkg/transport" "github.com/skycoin/skywire/pkg/transport/network" "github.com/skycoin/skywire/pkg/transport/network/addrresolver" @@ -201,11 +201,6 @@ func initDmsg(ctx context.Context, v *Visor, log *logging.Logger) error { return nil } -func initSNet(ctx context.Context, v *Visor, log *logging.Logger) error { - - return nil -} - func initDmsgCtrl(ctx context.Context, v *Visor, _ *logging.Logger) error { dmsgC := v.dmsgC if dmsgC == nil { From f9577c0d6127e77a76b8a8857d6c3696e6685a0f Mon Sep 17 00:00:00 2001 From: Never M Date: Fri, 2 Jul 2021 18:01:58 +0300 Subject: [PATCH 51/55] Add handshake todo --- pkg/transport/network/client.go | 1 + 1 file changed, 1 insertion(+) diff --git a/pkg/transport/network/client.go b/pkg/transport/network/client.go index 6ce2f3dce..df617ac56 100644 --- a/pkg/transport/network/client.go +++ b/pkg/transport/network/client.go @@ -156,6 +156,7 @@ func (c *genericClient) acceptConnections(lis net.Listener) { // wrapConn performs handshake over provided raw connection and wraps it in // network.Conn type using the data obtained from handshake process func (c *genericClient) wrapConn(rawConn net.Conn, hs handshake.Handshake, initiator bool, onClose func()) (*conn, error) { + // todo: move handshake logic to handshake package, pass there net.Conn conn, err := doHandshake(rawConn, hs, c.netType, c.log) if err != nil { onClose() From 52a87f49b0221af1dd57160a97bf94a7c9e6687c Mon Sep 17 00:00:00 2001 From: Never M Date: Sat, 3 Jul 2021 14:03:00 +0300 Subject: [PATCH 52/55] Fix todos in network package --- pkg/transport/network/client.go | 2 -- pkg/transport/network/connection.go | 2 -- pkg/transport/network/listener.go | 7 +++---- pkg/transport/network/network.go | 23 ----------------------- 4 files changed, 3 insertions(+), 31 deletions(-) diff --git a/pkg/transport/network/client.go b/pkg/transport/network/client.go index df617ac56..a5edc85b6 100644 --- a/pkg/transport/network/client.go +++ b/pkg/transport/network/client.go @@ -117,7 +117,6 @@ type genericClient struct { // initConnection will initialize skywire connection over opened raw connection to // the remote client // The process will perform handshake over raw connection -// todo: rename to handshake, initHandshake, skyConnect or smth? func (c *genericClient) initConnection(ctx context.Context, conn net.Conn, rPK cipher.PubKey, rPort uint16) (*conn, error) { lPort, freePort, err := c.porter.ReserveEphemeral(ctx) if err != nil { @@ -156,7 +155,6 @@ func (c *genericClient) acceptConnections(lis net.Listener) { // wrapConn performs handshake over provided raw connection and wraps it in // network.Conn type using the data obtained from handshake process func (c *genericClient) wrapConn(rawConn net.Conn, hs handshake.Handshake, initiator bool, onClose func()) (*conn, error) { - // todo: move handshake logic to handshake package, pass there net.Conn conn, err := doHandshake(rawConn, hs, c.netType, c.log) if err != nil { onClose() diff --git a/pkg/transport/network/connection.go b/pkg/transport/network/connection.go index ec872cccd..f01315efe 100644 --- a/pkg/transport/network/connection.go +++ b/pkg/transport/network/connection.go @@ -52,8 +52,6 @@ func DoHandshake(rawConn net.Conn, hs handshake.Handshake, netType Type, log *lo // handshake performs given handshake over given raw connection and wraps // connection in network.conn -// todo: ugly but necessary for AR compatibility -// consider moving "doing handshake" part to handshake package func doHandshake(rawConn net.Conn, hs handshake.Handshake, netType Type, log *logging.Logger) (*conn, error) { lAddr, rAddr, err := hs(rawConn, time.Now().Add(handshake.Timeout)) if err != nil { diff --git a/pkg/transport/network/listener.go b/pkg/transport/network/listener.go index d71380185..cd1db4d84 100644 --- a/pkg/transport/network/listener.go +++ b/pkg/transport/network/listener.go @@ -64,12 +64,12 @@ func (l *listener) AcceptConn() (Conn, error) { func (l *listener) Close() error { l.once.Do(func() { close(l.done) - // todo: consider if removing locks will change anything - // todo: close all pending connections in l.accept l.mx.Lock() close(l.accept) l.mx.Unlock() - + for conn := range l.accept { + conn.Close() + } l.freePort() }) @@ -98,7 +98,6 @@ func (l *listener) Network() Type { // Introduce is used by Client to introduce a new connection to this Listener func (l *listener) introduce(conn *conn) error { - // todo: think if this is needed select { case <-l.done: return io.ErrClosedPipe diff --git a/pkg/transport/network/network.go b/pkg/transport/network/network.go index 101fea169..354eeea07 100644 --- a/pkg/transport/network/network.go +++ b/pkg/transport/network/network.go @@ -5,7 +5,6 @@ import ( "errors" "net" - "github.com/skycoin/dmsg" "github.com/skycoin/dmsg/cipher" ) @@ -25,28 +24,6 @@ const ( DMSG Type = "dmsg" ) -// skywire address consisting of pulic key, port and -// type of transport we connect over -// todo: use instead of dmsg.Addr for readability -type addr struct { - PK cipher.PubKey - Port uint16 - Type Type -} - -// Network name, e.g. stcpr -func (a addr) Network() string { - return string(a.Type) -} - -// String form of address -func (a addr) String() string { - // use dmsg.Addr for printing. This address doesn't have - // to be dmsg though - dmsgAddr := dmsg.Addr{PK: a.PK, Port: a.Port} - return dmsgAddr.String() -} - //go:generate mockery -name Dialer -case underscore -inpkg // Dialer is an entity that can be dialed and asked for its type. From ea39be0386b16730c817b768b12cd75a937893cf Mon Sep 17 00:00:00 2001 From: Never M Date: Sat, 3 Jul 2021 14:07:58 +0300 Subject: [PATCH 53/55] Fix linter errors --- pkg/router/routerclient/dmsg_wrapper.go | 1 + pkg/router/routerclient/map.go | 1 + pkg/transport/entry.go | 1 + pkg/transport/manager.go | 7 ++++++- pkg/transport/network/connection.go | 1 + pkg/transport/network/dmsg.go | 2 +- pkg/transport/network/listener.go | 3 +-- pkg/transport/transport.go | 1 + pkg/visor/visorconfig/config.go | 2 +- pkg/visor/visorconfig/v0.go | 2 +- 10 files changed, 15 insertions(+), 6 deletions(-) diff --git a/pkg/router/routerclient/dmsg_wrapper.go b/pkg/router/routerclient/dmsg_wrapper.go index c3f05ad09..1f03d240e 100644 --- a/pkg/router/routerclient/dmsg_wrapper.go +++ b/pkg/router/routerclient/dmsg_wrapper.go @@ -6,6 +6,7 @@ import ( "github.com/skycoin/dmsg" "github.com/skycoin/dmsg/cipher" + "github.com/skycoin/skywire/pkg/transport/network" ) diff --git a/pkg/router/routerclient/map.go b/pkg/router/routerclient/map.go index e20ea392c..c7397d665 100644 --- a/pkg/router/routerclient/map.go +++ b/pkg/router/routerclient/map.go @@ -4,6 +4,7 @@ import ( "context" "github.com/skycoin/dmsg/cipher" + "github.com/skycoin/skywire/pkg/transport/network" ) diff --git a/pkg/transport/entry.go b/pkg/transport/entry.go index 39444cae5..24f0a9ad2 100644 --- a/pkg/transport/entry.go +++ b/pkg/transport/entry.go @@ -7,6 +7,7 @@ import ( "github.com/google/uuid" "github.com/skycoin/dmsg/cipher" + "github.com/skycoin/skywire/pkg/transport/network" ) diff --git a/pkg/transport/manager.go b/pkg/transport/manager.go index cb703d08e..e5150a8df 100644 --- a/pkg/transport/manager.go +++ b/pkg/transport/manager.go @@ -84,7 +84,12 @@ func (tm *Manager) serve(ctx context.Context) { func (tm *Manager) initClients() { acceptedNetworks := []network.Type{network.STCP, network.STCPR, network.SUDPH, network.DMSG} for _, netType := range acceptedNetworks { - tm.netClients[netType] = tm.factory.MakeClient(netType) + client, err := tm.factory.MakeClient(netType) + if err != nil { + tm.Logger.Warnf("Cannot initialize %s transport client", netType) + continue + } + tm.netClients[netType] = client } } diff --git a/pkg/transport/network/connection.go b/pkg/transport/network/connection.go index f01315efe..2bd5b5d48 100644 --- a/pkg/transport/network/connection.go +++ b/pkg/transport/network/connection.go @@ -9,6 +9,7 @@ import ( "github.com/skycoin/dmsg/cipher" "github.com/skycoin/dmsg/noise" "github.com/skycoin/skycoin/src/util/logging" + "github.com/skycoin/skywire/pkg/transport/network/handshake" ) diff --git a/pkg/transport/network/dmsg.go b/pkg/transport/network/dmsg.go index 17ea6f73f..f9f97121e 100644 --- a/pkg/transport/network/dmsg.go +++ b/pkg/transport/network/dmsg.go @@ -95,7 +95,7 @@ func (lis *dmsgListenerAdapter) Network() Type { // PK implements Listener interface func (lis *dmsgListenerAdapter) PK() cipher.PubKey { - return lis.PK() + return lis.Listener.DmsgAddr().PK } // Port implements Listener interface diff --git a/pkg/transport/network/listener.go b/pkg/transport/network/listener.go index cd1db4d84..96ebb33bd 100644 --- a/pkg/transport/network/listener.go +++ b/pkg/transport/network/listener.go @@ -68,7 +68,7 @@ func (l *listener) Close() error { close(l.accept) l.mx.Unlock() for conn := range l.accept { - conn.Close() + conn.Close() //nolint: errcheck, gosec } l.freePort() }) @@ -104,7 +104,6 @@ func (l *listener) introduce(conn *conn) error { default: l.mx.Lock() defer l.mx.Unlock() - select { case l.accept <- conn: return nil diff --git a/pkg/transport/transport.go b/pkg/transport/transport.go index 8d058179a..679d017fd 100644 --- a/pkg/transport/transport.go +++ b/pkg/transport/transport.go @@ -9,6 +9,7 @@ import ( "github.com/google/uuid" "github.com/skycoin/dmsg/cipher" "github.com/skycoin/skycoin/src/util/logging" + "github.com/skycoin/skywire/pkg/transport/network" ) diff --git a/pkg/visor/visorconfig/config.go b/pkg/visor/visorconfig/config.go index f6f8d3551..dbdb8ebe6 100644 --- a/pkg/visor/visorconfig/config.go +++ b/pkg/visor/visorconfig/config.go @@ -5,10 +5,10 @@ import ( "github.com/skycoin/skycoin/src/util/logging" "github.com/skycoin/skywire/pkg/app/launcher" + "github.com/skycoin/skywire/pkg/dmsgc" "github.com/skycoin/skywire/pkg/restart" "github.com/skycoin/skywire/pkg/routing" "github.com/skycoin/skywire/pkg/skyenv" - "github.com/skycoin/skywire/pkg/dmsgc" "github.com/skycoin/skywire/pkg/transport/network" "github.com/skycoin/skywire/pkg/visor/hypervisorconfig" ) diff --git a/pkg/visor/visorconfig/v0.go b/pkg/visor/visorconfig/v0.go index 00f3ccee5..b4d03e042 100644 --- a/pkg/visor/visorconfig/v0.go +++ b/pkg/visor/visorconfig/v0.go @@ -3,8 +3,8 @@ package visorconfig import ( "github.com/skycoin/dmsg/cipher" - "github.com/skycoin/skywire/pkg/routing" "github.com/skycoin/skywire/pkg/dmsgc" + "github.com/skycoin/skywire/pkg/routing" "github.com/skycoin/skywire/pkg/transport/network" ) From d6e1db06c11f7d3879598406e4027b85c2780a9d Mon Sep 17 00:00:00 2001 From: Never M Date: Wed, 7 Jul 2021 12:42:27 +0300 Subject: [PATCH 54/55] Remove unused code --- pkg/router/route_group_test.go | 259 -------------------- pkg/router/router.go | 1 - pkg/router/router_test.go | 419 --------------------------------- 3 files changed, 679 deletions(-) diff --git a/pkg/router/route_group_test.go b/pkg/router/route_group_test.go index 48d67ee89..3ad02ca34 100644 --- a/pkg/router/route_group_test.go +++ b/pkg/router/route_group_test.go @@ -1,19 +1,12 @@ package router import ( - "context" - "fmt" - "io" - "strconv" - "sync" "testing" - "time" "github.com/skycoin/dmsg/cipher" "github.com/stretchr/testify/require" "github.com/skycoin/skywire/pkg/routing" - "github.com/skycoin/skywire/pkg/transport" ) func TestNewRouteGroup(t *testing.T) { @@ -22,199 +15,6 @@ func TestNewRouteGroup(t *testing.T) { require.Equal(t, DefaultRouteGroupConfig(), rg.cfg) } -func testWrite(t *testing.T, rg1, rg2 *RouteGroup, m1, m2 *transport.Manager) { - msg1 := []byte("hello1") - msg2 := []byte("hello2") - - n, err := rg1.Write([]byte{}) - require.Equal(t, 0, n) - require.NoError(t, err) - - n, err = rg2.Write([]byte{}) - require.Equal(t, 0, n) - require.NoError(t, err) - - _, err = rg1.Write(msg1) - require.NoError(t, err) - - _, err = rg2.Write(msg2) - require.NoError(t, err) - - recv, err := m1.ReadPacket() - require.NoError(t, err) - require.Equal(t, msg2, recv.Payload()) - - recv, err = m2.ReadPacket() - require.NoError(t, err) - require.Equal(t, msg1, recv.Payload()) - - rg1.mu.Lock() - tpBackup := rg1.tps[0] - rg1.tps[0] = nil - rg1.mu.Unlock() - _, err = rg1.Write(msg1) - require.Equal(t, ErrBadTransport, err) - - rg1.mu.Lock() - rg1.tps[0] = tpBackup - - tpsBackup := rg1.tps - rg1.tps = nil - rg1.mu.Unlock() - _, err = rg1.Write(msg1) - require.Equal(t, ErrNoTransports, err) - - rg1.mu.Lock() - rg1.tps = tpsBackup - - fwdBackup := rg1.fwd - rg1.fwd = nil - rg1.mu.Unlock() - _, err = rg1.Write(msg1) - require.Equal(t, ErrNoRules, err) - - rg1.mu.Lock() - rg1.fwd = fwdBackup - rg1.mu.Unlock() -} - -func testRouteGroupReadWrite(t *testing.T, iterations int, rg1, rg2 io.ReadWriter) { - msg1 := []byte("hello1_") - msg2 := []byte("hello2_") - - t.Run("Group", func(t *testing.T) { - t.Run("MultipleWriteRead", func(t *testing.T) { - testMultipleWR(t, iterations, rg1, rg2, msg1, msg2) - }) - - t.Run("SingleReadWrite", func(t *testing.T) { - testSingleRW(t, rg1, rg2, msg1, msg2) - }) - - t.Run("MultipleReadWrite", func(t *testing.T) { - testMultipleRW(t, iterations, rg1, rg2, msg1, msg2) - }) - - t.Run("SingleWriteRead", func(t *testing.T) { - testSingleWR(t, rg1, rg2, msg1, msg2) - }) - }) -} - -func testSingleWR(t *testing.T, rg1, rg2 io.ReadWriter, msg1, msg2 []byte) { - _, err := rg1.Write(msg1) - require.NoError(t, err) - - _, err = rg2.Write(msg2) - require.NoError(t, err) - - buf1 := make([]byte, len(msg2)) - _, err = rg1.Read(buf1) - require.NoError(t, err) - require.Equal(t, msg2, buf1) - - buf2 := make([]byte, len(msg1)) - _, err = rg2.Read(buf2) - require.NoError(t, err) - require.Equal(t, msg1, buf2) -} - -func testMultipleRW(t *testing.T, iterations int, rg1, rg2 io.ReadWriter, msg1, msg2 []byte) { - var err1, err2 error - - for i := 0; i < iterations; i++ { - var wg sync.WaitGroup - - wg.Add(1) - - go func() { - defer wg.Done() - - time.Sleep(100 * time.Millisecond) - - for j := 0; j < iterations; j++ { - _, err := rg1.Write(append(msg1, []byte(strconv.Itoa(j))...)) - require.NoError(t, err) - - _, err = rg2.Write(append(msg2, []byte(strconv.Itoa(j))...)) - require.NoError(t, err) - } - }() - - require.NoError(t, err1) - require.NoError(t, err2) - - for j := 0; j < iterations; j++ { - msg := append(msg2, []byte(strconv.Itoa(j))...) - buf1 := make([]byte, len(msg)) - _, err := rg1.Read(buf1) - require.NoError(t, err) - require.Equal(t, msg, buf1) - } - - for j := 0; j < iterations; j++ { - msg := append(msg1, []byte(strconv.Itoa(j))...) - buf2 := make([]byte, len(msg)) - _, err := rg2.Read(buf2) - require.NoError(t, err) - require.Equal(t, msg, buf2) - } - - wg.Wait() - } -} - -func testSingleRW(t *testing.T, rg1, rg2 io.ReadWriter, msg1, msg2 []byte) { - var err1, err2 error - - go func() { - time.Sleep(1 * time.Second) - _, err1 = rg1.Write(msg1) - _, err2 = rg2.Write(msg2) - }() - - require.NoError(t, err1) - require.NoError(t, err2) - - buf1 := make([]byte, len(msg2)) - _, err := rg1.Read(buf1) - require.NoError(t, err) - require.Equal(t, msg2, buf1) - - buf2 := make([]byte, len(msg1)) - _, err = rg2.Read(buf2) - require.NoError(t, err) - require.Equal(t, msg1, buf2) -} - -func testMultipleWR(t *testing.T, iterations int, rg1, rg2 io.ReadWriter, msg1, msg2 []byte) { - for i := 0; i < iterations; i++ { - for j := 0; j < iterations; j++ { - _, err := rg1.Write(append(msg1, []byte(strconv.Itoa(j))...)) - require.NoError(t, err) - - _, err = rg2.Write(append(msg2, []byte(strconv.Itoa(j))...)) - require.NoError(t, err) - } - - for j := 0; j < iterations; j++ { - msg := append(msg2, []byte(strconv.Itoa(j))...) - buf1 := make([]byte, len(msg)) - _, err := rg1.Read(buf1) - require.NoError(t, err) - require.Equal(t, msg, buf1) - } - - for j := 0; j < iterations; j++ { - msg := append(msg1, []byte(strconv.Itoa(j))...) - buf2 := make([]byte, len(msg)) - _, err := rg2.Read(buf2) - require.NoError(t, err) - require.Equal(t, msg, buf2) - } - } -} - func TestRouteGroup_LocalAddr(t *testing.T) { rg := createRouteGroup(DefaultRouteGroupConfig()) require.Equal(t, rg.desc.Dst(), rg.LocalAddr()) @@ -229,65 +29,6 @@ func TestRouteGroup_RemoteAddr(t *testing.T) { require.NoError(t, rg.Close()) } -func pushPackets(ctx context.Context, from *transport.Manager, to *RouteGroup) { - for { - select { - case <-ctx.Done(): - return - default: - } - - packet, err := from.ReadPacket() - if err != nil { - panic(err) - } - - payload := packet.Payload() - if len(payload) != int(packet.Size()) { - panic("malformed packet") - } - - switch packet.Type() { - case routing.ClosePacket: - if to.isClosed() { - panic(io.ErrClosedPipe) - } - - if err := to.handleClosePacket(routing.CloseCode(packet.Payload()[0])); err != nil { - panic(err) - } - - return - case routing.DataPacket: - if !safeSend(ctx, to, payload) { - return - } - case routing.HandshakePacket: - // error won't happen with the handshake packet - _ = to.handlePacket(packet) //nolint:errcheck - default: - panic(fmt.Sprintf("wrong packet type %v", packet.Type())) - } - } -} - -func safeSend(ctx context.Context, to *RouteGroup, payload []byte) (keepSending bool) { - defer func() { - if r := recover(); r != nil { - keepSending = r == "send on closed channel" - } - }() - - select { - case <-ctx.Done(): - return false - case <-to.closed: - return false - case to.readCh <- payload: - return true - } -} - func createRouteGroup(cfg *RouteGroupConfig) *RouteGroup { rt := routing.NewTable() diff --git a/pkg/router/router.go b/pkg/router/router.go index 41017e44a..4836d77d0 100644 --- a/pkg/router/router.go +++ b/pkg/router/router.go @@ -36,7 +36,6 @@ const ( handshakeAwaitTimeout = 2 * time.Second - minHops = 0 maxHops = 50 retryDuration = 2 * time.Second retryInterval = 500 * time.Millisecond diff --git a/pkg/router/router_test.go b/pkg/router/router_test.go index 57952142e..5140908b8 100644 --- a/pkg/router/router_test.go +++ b/pkg/router/router_test.go @@ -1,22 +1,14 @@ package router import ( - "context" "fmt" "log" "os" - "sync" "testing" - "time" - "github.com/google/uuid" "github.com/sirupsen/logrus" - "github.com/skycoin/dmsg/cipher" "github.com/skycoin/skycoin/src/util/logging" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "github.com/skycoin/skywire/pkg/routing" "github.com/skycoin/skywire/pkg/transport" ) @@ -36,417 +28,6 @@ func TestMain(m *testing.M) { os.Exit(m.Run()) } -const ruleKeepAlive = 1 * time.Hour - -func testHandlePackets(t *testing.T, r0, r1 *router, tp1 *transport.ManagedTransport, pk1, pk2 cipher.PubKey) { - var wg sync.WaitGroup - - wg.Add(1) - t.Run("handlePacket_fwdRule", func(t *testing.T) { - defer wg.Done() - - testForwardRule(t, r0, r1, tp1, pk1, pk2) - }) - wg.Wait() - - wg.Add(1) - t.Run("handlePacket_intFwdRule", func(t *testing.T) { - defer wg.Done() - - testIntermediaryForwardRule(t, r0, r1, tp1) - }) - wg.Wait() - - wg.Add(1) - t.Run("handlePacket_cnsmRule", func(t *testing.T) { - defer wg.Done() - - testConsumeRule(t, r0, r1, tp1, pk1, pk2) - }) - wg.Wait() - - wg.Add(1) - t.Run("handlePacket_close_initiator", func(t *testing.T) { - defer wg.Done() - - testClosePacketInitiator(t, r0, r1, pk1, pk2, tp1) - }) - wg.Wait() - - wg.Add(1) - t.Run("handlePacket_close_remote", func(t *testing.T) { - defer wg.Done() - - testClosePacketRemote(t, r0, r1, pk1, pk2, tp1) - }) - wg.Wait() - - wg.Add(1) - t.Run("handlePacket_keepalive", func(t *testing.T) { - defer wg.Done() - - testKeepAlivePacket(t, r0, r1, pk1, pk2) - }) - wg.Wait() -} - -func testKeepAlivePacket(t *testing.T, r0, r1 *router, pk1, pk2 cipher.PubKey) { - defer clearRouterRules(r0, r1) - defer clearRouteGroups(r0, r1) - - rtIDs, err := r0.ReserveKeys(1) - require.NoError(t, err) - - rtID := rtIDs[0] - - cnsmRule := routing.ConsumeRule(100*time.Millisecond, rtID, pk2, pk1, 0, 0) - err = r0.rt.SaveRule(cnsmRule) - require.NoError(t, err) - require.Len(t, r0.rt.AllRules(), 1) - - time.Sleep(10 * time.Millisecond) - - packet := routing.MakeKeepAlivePacket(rtIDs[0]) - require.NoError(t, r0.handleTransportPacket(context.TODO(), packet)) - - require.Len(t, r0.rt.AllRules(), 1) - time.Sleep(10 * time.Millisecond) - require.Len(t, r0.rt.AllRules(), 1) - - time.Sleep(200 * time.Millisecond) - require.Len(t, r0.rt.AllRules(), 0) -} - -func testClosePacketRemote(t *testing.T, r0, r1 *router, pk1, pk2 cipher.PubKey, tp1 *transport.ManagedTransport) { - defer clearRouterRules(r0, r1) - defer clearRouteGroups(r0, r1) - - // reserve FWD IDs for r0. - intFwdID, err := r0.ReserveKeys(1) - require.NoError(t, err) - - // reserve FWD and CNSM IDs for r1. - r1RtIDs, err := r1.ReserveKeys(2) - require.NoError(t, err) - - intFwdRule := routing.IntermediaryForwardRule(1*time.Hour, intFwdID[0], r1RtIDs[1], tp1.Entry.ID) - err = r0.rt.SaveRule(intFwdRule) - require.NoError(t, err) - - routeID := routing.RouteID(7) - fwdRule := routing.ForwardRule(ruleKeepAlive, r1RtIDs[0], routeID, tp1.Entry.ID, pk1, pk2, 0, 0) - cnsmRule := routing.ConsumeRule(ruleKeepAlive, r1RtIDs[1], pk2, pk1, 0, 0) - - err = r1.rt.SaveRule(fwdRule) - require.NoError(t, err) - - err = r1.rt.SaveRule(cnsmRule) - require.NoError(t, err) - - fwdRtDesc := fwdRule.RouteDescriptor() - - rules := routing.EdgeRules{ - Desc: fwdRtDesc.Invert(), - Forward: fwdRule, - Reverse: cnsmRule, - } - - rg1 := NewRouteGroup(DefaultRouteGroupConfig(), r1.rt, rules.Desc) - rg1.appendRules(rules.Forward, rules.Reverse, r1.tm.Transport(rules.Forward.NextTransportID())) - - nrg1 := &NoiseRouteGroup{rg: rg1} - r1.rgsNs[rg1.desc] = nrg1 - - packet := routing.MakeClosePacket(intFwdID[0], routing.CloseRequested) - err = r0.handleTransportPacket(context.TODO(), packet) - require.NoError(t, err) - - recvPacket, err := r1.tm.ReadPacket() - require.NoError(t, err) - require.Equal(t, packet.Size(), recvPacket.Size()) - require.Equal(t, packet.Payload(), recvPacket.Payload()) - require.Equal(t, packet.Type(), recvPacket.Type()) - require.Equal(t, r1RtIDs[1], recvPacket.RouteID()) - - err = r1.handleTransportPacket(context.TODO(), recvPacket) - require.NoError(t, err) - - require.True(t, nrg1.rg.isRemoteClosed()) - require.False(t, nrg1.isClosed()) - require.Len(t, r1.rgsNs, 0) - require.Len(t, r0.rt.AllRules(), 0) - require.Len(t, r1.rt.AllRules(), 0) -} - -func testClosePacketInitiator(t *testing.T, r0, r1 *router, pk1, pk2 cipher.PubKey, tp1 *transport.ManagedTransport) { - defer clearRouterRules(r0, r1) - defer clearRouteGroups(r0, r1) - - // reserve FWD IDs for r0. - intFwdID, err := r0.ReserveKeys(1) - require.NoError(t, err) - - // reserve FWD and CNSM IDs for r1. - r1RtIDs, err := r1.ReserveKeys(2) - require.NoError(t, err) - - intFwdRule := routing.IntermediaryForwardRule(1*time.Hour, intFwdID[0], r1RtIDs[1], tp1.Entry.ID) - err = r0.rt.SaveRule(intFwdRule) - require.NoError(t, err) - - routeID := routing.RouteID(7) - fwdRule := routing.ForwardRule(ruleKeepAlive, r1RtIDs[0], routeID, tp1.Entry.ID, pk1, pk2, 0, 0) - cnsmRule := routing.ConsumeRule(ruleKeepAlive, r1RtIDs[1], pk2, pk1, 0, 0) - - err = r1.rt.SaveRule(fwdRule) - require.NoError(t, err) - - err = r1.rt.SaveRule(cnsmRule) - require.NoError(t, err) - - fwdRtDesc := fwdRule.RouteDescriptor() - - rules := routing.EdgeRules{ - Desc: fwdRtDesc.Invert(), - Forward: fwdRule, - Reverse: cnsmRule, - } - - rg1 := NewRouteGroup(DefaultRouteGroupConfig(), r1.rt, rules.Desc) - rg1.appendRules(rules.Forward, rules.Reverse, r1.tm.Transport(rules.Forward.NextTransportID())) - - nrg1 := &NoiseRouteGroup{rg: rg1} - r1.rgsNs[rg1.desc] = nrg1 - - packet := routing.MakeClosePacket(intFwdID[0], routing.CloseRequested) - err = r0.handleTransportPacket(context.TODO(), packet) - require.NoError(t, err) - - recvPacket, err := r1.tm.ReadPacket() - require.NoError(t, err) - require.Equal(t, packet.Size(), recvPacket.Size()) - require.Equal(t, packet.Payload(), recvPacket.Payload()) - require.Equal(t, packet.Type(), recvPacket.Type()) - require.Equal(t, r1RtIDs[1], recvPacket.RouteID()) - - rg1.closeDone.Add(1) - rg1.closeInitiated = 1 - - err = r1.handleTransportPacket(context.TODO(), recvPacket) - require.NoError(t, err) - - require.Len(t, r1.rgsNs, 0) - require.Len(t, r0.rt.AllRules(), 0) - // since this is the close initiator but the close routine wasn't called, - // forward rule is left - require.Len(t, r1.rt.AllRules(), 1) -} - -// TEST: Ensure handleTransportPacket does as expected. -// After setting a rule in r0, r0 should forward a packet to r1 (as specified in the given rule) -// when r0.handleTransportPacket() is called. -func testForwardRule(t *testing.T, r0, r1 *router, tp1 *transport.ManagedTransport, pk1, pk2 cipher.PubKey) { - defer clearRouterRules(r0, r1) - defer clearRouteGroups(r0, r1) - - // Add a FWD rule for r0. - fwdRtID, err := r0.ReserveKeys(1) - require.NoError(t, err) - - routeID := routing.RouteID(1) - fwdRule := routing.ForwardRule(ruleKeepAlive, fwdRtID[0], routeID, tp1.Entry.ID, pk1, pk2, 0, 0) - err = r0.rt.SaveRule(fwdRule) - require.NoError(t, err) - - rules := routing.EdgeRules{Desc: fwdRule.RouteDescriptor(), Forward: fwdRule, Reverse: nil} - rg0 := NewRouteGroup(DefaultRouteGroupConfig(), r0.rt, rules.Desc) - rg0.appendRules(rules.Forward, rules.Reverse, r0.tm.Transport(rules.Forward.NextTransportID())) - - nrg0 := &NoiseRouteGroup{rg: rg0} - r0.rgsNs[rg0.desc] = nrg0 - - // Call handleTransportPacket for r0 (this should in turn, use the rule we added). - packet, err := routing.MakeDataPacket(fwdRtID[0], []byte("This is a test!")) - require.NoError(t, err) - - require.NoError(t, r0.handleTransportPacket(context.TODO(), packet)) - - // r1 should receive the packet handled by r0. - recvPacket, err := r1.tm.ReadPacket() - assert.NoError(t, err) - assert.Equal(t, packet.Size(), recvPacket.Size()) - assert.Equal(t, packet.Payload(), recvPacket.Payload()) - assert.Equal(t, routeID, recvPacket.RouteID()) -} - -func testIntermediaryForwardRule(t *testing.T, r0, r1 *router, tp1 *transport.ManagedTransport) { - defer clearRouterRules(r0, r1) - defer clearRouteGroups(r0, r1) - - // Add a FWD rule for r0. - fwdRtID, err := r0.ReserveKeys(1) - require.NoError(t, err) - - fwdRule := routing.IntermediaryForwardRule(ruleKeepAlive, fwdRtID[0], routing.RouteID(5), tp1.Entry.ID) - err = r0.rt.SaveRule(fwdRule) - require.NoError(t, err) - - // Call handleTransportPacket for r0 (this should in turn, use the rule we added). - packet, err := routing.MakeDataPacket(fwdRtID[0], []byte("This is a test!")) - require.NoError(t, err) - - require.NoError(t, r0.handleTransportPacket(context.TODO(), packet)) - - // r1 should receive the packet handled by r0. - recvPacket, err := r1.tm.ReadPacket() - assert.NoError(t, err) - assert.Equal(t, packet.Size(), recvPacket.Size()) - assert.Equal(t, packet.Payload(), recvPacket.Payload()) - assert.Equal(t, routing.RouteID(5), recvPacket.RouteID()) -} - -func testConsumeRule(t *testing.T, r0, r1 *router, tp1 *transport.ManagedTransport, pk1, pk2 cipher.PubKey) { - defer clearRouterRules(r0, r1) - defer clearRouteGroups(r0, r1) - - // one for consume rule and one for reverse forward rule - dstRtIDs, err := r1.ReserveKeys(2) - require.NoError(t, err) - - intFwdRtID, err := r0.ReserveKeys(1) - require.NoError(t, err) - - intFwdRule := routing.IntermediaryForwardRule(ruleKeepAlive, intFwdRtID[0], dstRtIDs[1], tp1.Entry.ID) - err = r0.rt.SaveRule(intFwdRule) - require.NoError(t, err) - - routeID := routing.RouteID(7) - fwdRule := routing.ForwardRule(ruleKeepAlive, dstRtIDs[0], routeID, tp1.Entry.ID, pk1, pk2, 0, 0) - cnsmRule := routing.ConsumeRule(ruleKeepAlive, dstRtIDs[1], pk2, pk1, 0, 0) - - err = r1.rt.SaveRule(fwdRule) - require.NoError(t, err) - - err = r1.rt.SaveRule(cnsmRule) - require.NoError(t, err) - - fwdRtDesc := fwdRule.RouteDescriptor() - - rules := routing.EdgeRules{ - Desc: fwdRtDesc.Invert(), - Forward: fwdRule, - Reverse: cnsmRule, - } - - rg1 := NewRouteGroup(DefaultRouteGroupConfig(), r1.rt, rules.Desc) - rg1.appendRules(rules.Forward, rules.Reverse, r1.tm.Transport(rules.Forward.NextTransportID())) - - nrg1 := &NoiseRouteGroup{rg: rg1} - r1.rgsNs[rg1.desc] = nrg1 - - packet, err := routing.MakeDataPacket(intFwdRtID[0], []byte("test intermediary forward")) - require.NoError(t, err) - - require.NoError(t, r0.handleTransportPacket(context.TODO(), packet)) - - recvPacket, err := r1.tm.ReadPacket() - assert.NoError(t, err) - assert.Equal(t, packet.Size(), recvPacket.Size()) - assert.Equal(t, packet.Payload(), recvPacket.Payload()) - assert.Equal(t, dstRtIDs[1], recvPacket.RouteID()) - - consumeMsg := []byte("test_consume") - packet, err = routing.MakeDataPacket(dstRtIDs[1], consumeMsg) - require.NoError(t, err) - - require.NoError(t, r1.handleTransportPacket(context.TODO(), packet)) - - nrg, ok := r1.noiseRouteGroup(fwdRtDesc.Invert()) - require.True(t, ok) - require.NotNil(t, nrg) - - data := <-nrg.rg.readCh - require.Equal(t, consumeMsg, data) -} - -func testRemoveRouteDescriptor(t *testing.T, r *router, rt routing.Table) { - clearRoutingTableRules(rt) - - localPK, _ := cipher.GenerateKeyPair() - remotePK, _ := cipher.GenerateKeyPair() - - id, err := r.rt.ReserveKeys(1) - require.NoError(t, err) - - rule := routing.ConsumeRule(10*time.Minute, id[0], localPK, remotePK, 2, 3) - err = r.rt.SaveRule(rule) - require.NoError(t, err) - - desc := routing.NewRouteDescriptor(localPK, remotePK, 3, 2) - r.RemoveRouteDescriptor(desc) - assert.Equal(t, 1, rt.Count()) - - desc = routing.NewRouteDescriptor(localPK, remotePK, 2, 3) - r.RemoveRouteDescriptor(desc) - assert.Equal(t, 0, rt.Count()) -} - -func testGetRule(t *testing.T, r *router, rt routing.Table) { - clearRoutingTableRules(rt) - - expiredID, err := r.rt.ReserveKeys(1) - require.NoError(t, err) - - expiredRule := routing.IntermediaryForwardRule(-10*time.Minute, expiredID[0], 3, uuid.New()) - err = r.rt.SaveRule(expiredRule) - require.NoError(t, err) - - id, err := r.rt.ReserveKeys(1) - require.NoError(t, err) - - rule := routing.IntermediaryForwardRule(10*time.Minute, id[0], 3, uuid.New()) - err = r.rt.SaveRule(rule) - require.NoError(t, err) - - defer r.rt.DelRules([]routing.RouteID{id[0], expiredID[0]}) - - // rule should already be expired at this point due to the execution time. - // However, we'll just a bit to be sure - time.Sleep(1 * time.Millisecond) - - _, err = r.GetRule(expiredID[0]) - require.Error(t, err) - - _, err = r.GetRule(123) - require.Error(t, err) - - gotRule, err := r.GetRule(id[0]) - require.NoError(t, err) - assert.Equal(t, rule, gotRule) -} - -func clearRouteGroups(routers ...*router) { - for _, r := range routers { - r.rgsNs = make(map[routing.RouteDescriptor]*NoiseRouteGroup) - } -} - -func clearRouterRules(routers ...*router) { - for _, r := range routers { - rules := r.rt.AllRules() - for _, rule := range rules { - r.rt.DelRules([]routing.RouteID{rule.KeyRouteID()}) - } - } -} - -func clearRoutingTableRules(rt routing.Table) { - rules := rt.AllRules() - for _, rule := range rules { - rt.DelRules([]routing.RouteID{rule.KeyRouteID()}) - } -} - type TestEnv struct { TpD transport.DiscoveryClient From 3e7ee0c4376817d6b66bbd5b63ffddb2cc1ee347 Mon Sep 17 00:00:00 2001 From: Never M Date: Wed, 7 Jul 2021 13:32:32 +0300 Subject: [PATCH 55/55] Fix lint errors --- pkg/setup/setupclient/client.go | 2 +- pkg/transport/network/addrresolver/client.go | 1 + pkg/visor/rpc_client_serve.go | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/pkg/setup/setupclient/client.go b/pkg/setup/setupclient/client.go index 79d64cedc..a017d57b7 100644 --- a/pkg/setup/setupclient/client.go +++ b/pkg/setup/setupclient/client.go @@ -45,7 +45,7 @@ func NewClient(ctx context.Context, log *logging.Logger, dmsgC *dmsg.Client, set func (c *Client) dial(ctx context.Context, dmsgC *dmsg.Client) (net.Conn, error) { for _, sPK := range c.setupNodes { - addr := dmsg.Addr{sPK, skyenv.DmsgSetupPort} + addr := dmsg.Addr{PK: sPK, Port: skyenv.DmsgSetupPort} conn, err := dmsgC.Dial(ctx, addr) if err != nil { c.log.WithError(err).Warnf("failed to dial to setup node: setupPK(%s)", sPK) diff --git a/pkg/transport/network/addrresolver/client.go b/pkg/transport/network/addrresolver/client.go index a18a9fc92..f40ce9dbc 100644 --- a/pkg/transport/network/addrresolver/client.go +++ b/pkg/transport/network/addrresolver/client.go @@ -219,6 +219,7 @@ func (c *httpClient) BindSTCPR(ctx context.Context, port string) error { return nil } +// Handshake type is used to decouple client from handshake and network packages type Handshake func(net.Conn) (net.Conn, error) func (c *httpClient) BindSUDPH(filter *pfilter.PacketFilter, hs Handshake) (<-chan RemoteVisor, error) { diff --git a/pkg/visor/rpc_client_serve.go b/pkg/visor/rpc_client_serve.go index bbc7f082f..c9e449225 100644 --- a/pkg/visor/rpc_client_serve.go +++ b/pkg/visor/rpc_client_serve.go @@ -29,7 +29,7 @@ func ServeRPCClient(ctx context.Context, log logrus.FieldLogger, dmsgC *dmsg.Cli var conn net.Conn err := retry.Do(ctx, func() (rErr error) { log.Info("Dialing...") - addr := dmsg.Addr{rAddr.PK, rAddr.Port} + addr := dmsg.Addr{PK: rAddr.PK, Port: rAddr.Port} conn, rErr = dmsgC.Dial(ctx, addr) return rErr })