diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000000..e589eea63c --- /dev/null +++ b/.dockerignore @@ -0,0 +1,6 @@ +.git +bin +skywire +ci_scripts +apps +integration diff --git a/.gitignore b/.gitignore index d02184724a..5c83e5a3b1 100644 --- a/.gitignore +++ b/.gitignore @@ -27,3 +27,6 @@ pkg/visor/foo/ /*.json /*.sh /*.log + +# Ignore backup go.mod after running '/ci_scripts/go_mod_replace.sh'. +go.mod-e diff --git a/Makefile b/Makefile index 19018126a4..2f0cb0496b 100644 --- a/Makefile +++ b/Makefile @@ -62,21 +62,7 @@ vendorcheck: ## Run vendorcheck test: ## Run tests -go clean -testcache &>/dev/null ${OPTS} go test ${TEST_OPTS} ./internal/... - #${OPTS} go test -race -tags no_ci -cover -timeout=5m ./pkg/... - ${OPTS} go test ${TEST_OPTS} ./pkg/app/... - ${OPTS} go test ${TEST_OPTS} ./pkg/cipher/... - ${OPTS} go test ${TEST_OPTS} ./pkg/dmsg/... - ${OPTS} go test ${TEST_OPTS} ./pkg/hypervisor/... - ${OPTS} go test ${TEST_OPTS} ./pkg/messaging-discovery/... - ${OPTS} go test ${TEST_OPTS} ./pkg/route-finder/... - ${OPTS} go test ${TEST_OPTS} ./pkg/router/... - ${OPTS} go test ${TEST_OPTS} ./pkg/routing/... - ${OPTS} go test ${TEST_OPTS} ./pkg/setup/... - ${OPTS} go test ${TEST_OPTS} ./pkg/transport/... - ${OPTS} go test ${TEST_OPTS} ./pkg/transport-discovery/... - ${OPTS} go test ${TEST_OPTS} ./pkg/visor/... - ${OPTS} go test -tags no_ci -cover -timeout=5m ./pkg/messaging/... - + ${OPTS} go test ${TEST_OPTS} ./pkg/... install-linters: ## Install linters - VERSION=1.17.1 ./ci_scripts/install-golangci-lint.sh @@ -119,7 +105,7 @@ release: ## Build `hypervisor`, `skywire-cli`, `SSH-cli`, `visor` and apps witho ${OPTS} go build -o ./setup-node ./cmd/setup-node ${OPTS} go build -o ./SSH-cli ./cmd/therealssh-cli ${OPTS} go build -o ./visor ./cmd/visor - ${OPTS} go build -o ./apps/skychat.v1.0 ./cmd/apps/skychat + ${OPTS} go build -o ./apps/skychat.v1.0 ./cmd/apps/skychat ${OPTS} go build -o ./apps/helloworld.v1.0 ./cmd/apps/helloworld ${OPTS} go build -o ./apps/socksproxy.v1.0 ./cmd/apps/therealproxy ${OPTS} go build -o ./apps/socksproxy-client.v1.0 ./cmd/apps/therealproxy-client @@ -198,6 +184,12 @@ integration-run-proxy: ## Runs the proxy interactive testing environment integration-run-ssh: ## Runs the ssh interactive testing environment ./integration/run-ssh-env.sh +mod-comm: ## Comments the 'replace' rule in go.mod + ./ci_scripts/go_mod_replace.sh comment go.mod + +mod-uncomm: ## Uncomments the 'replace' rule in go.mod + ./ci_scripts/go_mod_replace.sh uncomment go.mod + help: @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' diff --git a/ci_scripts/go_mod_replace.sh b/ci_scripts/go_mod_replace.sh new file mode 100755 index 0000000000..b7890cc86b --- /dev/null +++ b/ci_scripts/go_mod_replace.sh @@ -0,0 +1,34 @@ +#!/usr/bin/env bash + +if [[ $# -ne 2 ]]; then + exit 0 +fi + +# Inputs. +action=$1 +filename=$2 + +# Line to comment/uncomment in go.mod +line="replace github.com\/skycoin\/dmsg => ..\/dmsg" + +function print_usage() { + echo $"Usage: $0 (comment|uncomment) " +} + +case "$action" in + comment) + echo "commenting ${filename}..." + sed -i -e "/$line/s/^\/*/\/\//" ${filename} + ;; + uncomment) + echo "uncommenting ${filename}..." + sed -i -e "/$line/s/^\/\/*//" ${filename} + ;; + help) + print_usage + ;; + *) + print_usage + exit 1 + ;; +esac diff --git a/ci_scripts/run-pkg-tests.sh b/ci_scripts/run-pkg-tests.sh index 977f900530..fd6eba8f2d 100644 --- a/ci_scripts/run-pkg-tests.sh +++ b/ci_scripts/run-pkg-tests.sh @@ -22,38 +22,6 @@ go clean -testcache &> /dev/null || go test -race -tags no_ci -cover -timeout=5m go clean -testcache &> /dev/null || go test -race -tags no_ci -cover -timeout=5m github.com/skycoin/skywire/pkg/hypervisor -run TestNew >> ./logs/pkg/TestNewHypervisor.log -go clean -testcache &> /dev/null || go test -race -tags no_ci -cover -timeout=5m github.com/skycoin/skywire/pkg/messaging -run TestChannelRead >> ./logs/pkg/TestChannelRead.log -go clean -testcache &> /dev/null || go test -race -tags no_ci -cover -timeout=5m github.com/skycoin/skywire/pkg/messaging -run TestChannelWrite >> ./logs/pkg/TestChannelWrite.log -go clean -testcache &> /dev/null || go test -race -tags no_ci -cover -timeout=5m github.com/skycoin/skywire/pkg/messaging -run TestChannelClose >> ./logs/pkg/TestChannelClose.log -go clean -testcache &> /dev/null || go test -race -tags no_ci -cover -timeout=5m github.com/skycoin/skywire/pkg/messaging -run TestClientConnectInitialServers >> ./logs/pkg/TestClientConnectInitialServers.log -go clean -testcache &> /dev/null || go test -race -tags no_ci -cover -timeout=5m github.com/skycoin/skywire/pkg/messaging -run TestClientDial >> ./logs/pkg/TestClientDial.log -go clean -testcache &> /dev/null || go test -race -tags no_ci -cover -timeout=5m github.com/skycoin/skywire/pkg/messaging -run TestHandshakeFrame >> ./logs/pkg/TestHandshakeFrame.log -go clean -testcache &> /dev/null || go test -race -tags no_ci -cover -timeout=5m github.com/skycoin/skywire/pkg/messaging -run TestHandshake >> ./logs/pkg/TestHandshake.log -go clean -testcache &> /dev/null || go test -race -tags no_ci -cover -timeout=5m github.com/skycoin/skywire/pkg/messaging -run TestHandshakeInvalidResponder >> ./logs/pkg/TestHandshakeInvalidResponder.log -go clean -testcache &> /dev/null || go test -race -tags no_ci -cover -timeout=5m github.com/skycoin/skywire/pkg/messaging -run TestHandshakeInvalidInitiator >> ./logs/pkg/TestHandshakeInvalidInitiator.log -go clean -testcache &> /dev/null || go test -race -tags no_ci -cover -timeout=5m github.com/skycoin/skywire/pkg/messaging -run TestNewConn >> ./logs/pkg/TestNewConn.log -go clean -testcache &> /dev/null || go test -race -tags no_ci -cover -timeout=5m github.com/skycoin/skywire/pkg/messaging -run TestNewPool >> ./logs/pkg/TestNewPool.log -go clean -testcache &> /dev/null || go test -race -tags no_ci -cover -timeout=5m github.com/skycoin/skywire/pkg/messaging -run TestPool_Range >> ./logs/pkg/TestPool_Range.log -go clean -testcache &> /dev/null || go test -race -tags no_ci -cover -timeout=5m github.com/skycoin/skywire/pkg/messaging -run TestPool_All >> ./logs/pkg/TestPool_All.log - -go clean -testcache &> /dev/null || go test -race -tags no_ci -cover -timeout=5m github.com/skycoin/skywire/pkg/messaging-discovery/client -run TestNewMockGetAvailableServers>> ./logs/pkg/TestNewMockGetAvailableServers.log -go clean -testcache &> /dev/null || go test -race -tags no_ci -cover -timeout=5m github.com/skycoin/skywire/pkg/messaging-discovery/client -run TestNewMockEntriesEndpoint>> ./logs/pkg/TestNewMockEntriesEndpoint.log -go clean -testcache &> /dev/null || go test -race -tags no_ci -cover -timeout=5m github.com/skycoin/skywire/pkg/messaging-discovery/client -run TestNewMockSetEntriesEndpoint>> ./logs/pkg/TestNewMockSetEntriesEndpoint.log -go clean -testcache &> /dev/null || go test -race -tags no_ci -cover -timeout=5m github.com/skycoin/skywire/pkg/messaging-discovery/client -run TestNewMockUpdateEntriesEndpoint>> ./logs/pkg/TestNewMockUpdateEntriesEndpoint.log -go clean -testcache &> /dev/null || go test -race -tags no_ci -cover -timeout=5m github.com/skycoin/skywire/pkg/messaging-discovery/client -run TestNewMockUpdateEntrySequence>> ./logs/pkg/TestNewMockUpdateEntrySequence.log -go clean -testcache &> /dev/null || go test -race -tags no_ci -cover -timeout=5m github.com/skycoin/skywire/pkg/messaging-discovery/client -run TestNewClientEntryIsValid>> ./logs/pkg/TestNewClientEntryIsValid.log -go clean -testcache &> /dev/null || go test -race -tags no_ci -cover -timeout=5m github.com/skycoin/skywire/pkg/messaging-discovery/client -run TestVerifySignature>> ./logs/pkg/TestVerifySignature.log -go clean -testcache &> /dev/null || go test -race -tags no_ci -cover -timeout=5m github.com/skycoin/skywire/pkg/messaging-discovery/client -run TestValidateRightEntry>> ./logs/pkg/TestValidateRightEntry.log -go clean -testcache &> /dev/null || go test -race -tags no_ci -cover -timeout=5m github.com/skycoin/skywire/pkg/messaging-discovery/client -run TestValidateNonKeysEntry>> ./logs/pkg/TestValidateNonKeysEntry.log -go clean -testcache &> /dev/null || go test -race -tags no_ci -cover -timeout=5m github.com/skycoin/skywire/pkg/messaging-discovery/client -run TestValidateNonClientNonServerEntry>> ./logs/pkg/TestValidateNonClientNonServerEntry.log -go clean -testcache &> /dev/null || go test -race -tags no_ci -cover -timeout=5m github.com/skycoin/skywire/pkg/messaging-discovery/client -run TestValidateNonSignedEntry>> ./logs/pkg/TestValidateNonSignedEntry.log -go clean -testcache &> /dev/null || go test -race -tags no_ci -cover -timeout=5m github.com/skycoin/skywire/pkg/messaging-discovery/client -run TestValidateIteration>> ./logs/pkg/TestValidateIteration.log -go clean -testcache &> /dev/null || go test -race -tags no_ci -cover -timeout=5m github.com/skycoin/skywire/pkg/messaging-discovery/client -run TestValidateIterationEmptyClient>> ./logs/pkg/TestValidateIterationEmptyClient.log -go clean -testcache &> /dev/null || go test -race -tags no_ci -cover -timeout=5m github.com/skycoin/skywire/pkg/messaging-discovery/client -run TestValidateIterationWrongSequence>> ./logs/pkg/TestValidateIterationWrongSequence.log -go clean -testcache &> /dev/null || go test -race -tags no_ci -cover -timeout=5m github.com/skycoin/skywire/pkg/messaging-discovery/client -run TestValidateIterationWrongTime>> ./logs/pkg/TestValidateIterationWrongTime.log -go clean -testcache &> /dev/null || go test -race -tags no_ci -cover -timeout=5m github.com/skycoin/skywire/pkg/messaging-discovery/client -run TestCopy>> ./logs/pkg/TestCopy.log - - go clean -testcache &> /dev/null || go test -race -tags no_ci -cover -timeout=5m github.com/skycoin/skywire/pkg/visor -run TestMessagingDiscovery >> ./logs/pkg/TestMessagingDiscovery.log go clean -testcache &> /dev/null || go test -race -tags no_ci -cover -timeout=5m github.com/skycoin/skywire/pkg/visor -run TestTransportDiscovery >> ./logs/pkg/TestTransportDiscovery.log go clean -testcache &> /dev/null || go test -race -tags no_ci -cover -timeout=5m github.com/skycoin/skywire/pkg/visor -run TestTransportLogStore >> ./logs/pkg/TestTransportLogStore.log diff --git a/cmd/apps/helloworld/helloworld.go b/cmd/apps/helloworld/helloworld.go index b6b5801e5a..0d7a36c75c 100644 --- a/cmd/apps/helloworld/helloworld.go +++ b/cmd/apps/helloworld/helloworld.go @@ -7,8 +7,9 @@ import ( "log" "os" + "github.com/skycoin/dmsg/cipher" + "github.com/skycoin/skywire/pkg/app" - "github.com/skycoin/skywire/pkg/cipher" ) func main() { diff --git a/cmd/apps/skychat/chat.go b/cmd/apps/skychat/chat.go index 80224c12c9..8b76bf3423 100644 --- a/cmd/apps/skychat/chat.go +++ b/cmd/apps/skychat/chat.go @@ -15,9 +15,10 @@ import ( "sync" "time" + "github.com/skycoin/dmsg/cipher" + "github.com/skycoin/skywire/internal/netutil" "github.com/skycoin/skywire/pkg/app" - "github.com/skycoin/skywire/pkg/cipher" ) var addr = flag.String("addr", ":8000", "address to bind") @@ -107,10 +108,10 @@ func messageHandler(w http.ResponseWriter, req *http.Request) { addr := &app.Addr{PubKey: pk, Port: 1} connsMu.Lock() - conn := chatConns[pk] + conn, ok := chatConns[pk] connsMu.Unlock() - if conn == nil { + if !ok { var err error err = r.Do(func() error { conn, err = chatApp.Dial(addr) diff --git a/cmd/apps/therealproxy-client/therealproxy-client.go b/cmd/apps/therealproxy-client/therealproxy-client.go index eb1ab3c0f2..f1e7c01bd6 100644 --- a/cmd/apps/therealproxy-client/therealproxy-client.go +++ b/cmd/apps/therealproxy-client/therealproxy-client.go @@ -9,11 +9,11 @@ import ( "net" "time" - "github.com/skycoin/skywire/internal/netutil" + "github.com/skycoin/dmsg/cipher" + "github.com/skycoin/skywire/internal/netutil" "github.com/skycoin/skywire/internal/therealproxy" "github.com/skycoin/skywire/pkg/app" - "github.com/skycoin/skywire/pkg/cipher" ) const socksPort = 3 diff --git a/cmd/messaging-server/commands/root.go b/cmd/messaging-server/commands/root.go index 0fabdb2e87..6878a2f3a9 100644 --- a/cmd/messaging-server/commands/root.go +++ b/cmd/messaging-server/commands/root.go @@ -12,12 +12,11 @@ import ( "github.com/prometheus/client_golang/prometheus/promhttp" logrus_syslog "github.com/sirupsen/logrus/hooks/syslog" + "github.com/skycoin/dmsg" + "github.com/skycoin/dmsg/cipher" + "github.com/skycoin/dmsg/disc" "github.com/skycoin/skycoin/src/util/logging" "github.com/spf13/cobra" - - "github.com/skycoin/skywire/pkg/cipher" - "github.com/skycoin/skywire/pkg/dmsg" - "github.com/skycoin/skywire/pkg/messaging-discovery/client" ) var ( @@ -78,7 +77,7 @@ var rootCmd = &cobra.Command{ } // Start - srv, err := dmsg.NewServer(conf.PubKey, conf.SecKey, conf.PublicAddress, l, client.NewHTTP(conf.Discovery)) + srv, err := dmsg.NewServer(conf.PubKey, conf.SecKey, conf.PublicAddress, l, disc.NewHTTP(conf.Discovery)) if err != nil { logger.Fatalf("Error creating DMSG server instance: %v", err) } diff --git a/cmd/skywire-cli/commands/mdisc/root.go b/cmd/skywire-cli/commands/mdisc/root.go index f6fbc1416b..18a27e0c1c 100644 --- a/cmd/skywire-cli/commands/mdisc/root.go +++ b/cmd/skywire-cli/commands/mdisc/root.go @@ -7,10 +7,10 @@ import ( "text/tabwriter" "time" + "github.com/skycoin/dmsg/disc" "github.com/spf13/cobra" "github.com/skycoin/skywire/cmd/skywire-cli/internal" - "github.com/skycoin/skywire/pkg/messaging-discovery/client" ) var mdAddr string @@ -40,7 +40,7 @@ var entryCmd = &cobra.Command{ ctx, cancel := context.WithTimeout(context.Background(), time.Second*10) defer cancel() pk := internal.ParsePK("visor-public-key", args[0]) - entry, err := client.NewHTTP(mdAddr).Entry(ctx, pk) + entry, err := disc.NewHTTP(mdAddr).Entry(ctx, pk) internal.Catch(err) fmt.Println(entry) }, @@ -52,13 +52,13 @@ var availableServersCmd = &cobra.Command{ Run: func(_ *cobra.Command, _ []string) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*10) defer cancel() - entries, err := client.NewHTTP(mdAddr).AvailableServers(ctx) + entries, err := disc.NewHTTP(mdAddr).AvailableServers(ctx) internal.Catch(err) printAvailableServers(entries) }, } -func printAvailableServers(entries []*client.Entry) { +func printAvailableServers(entries []*disc.Entry) { w := tabwriter.NewWriter(os.Stdout, 0, 0, 5, ' ', tabwriter.TabIndent) _, err := fmt.Fprintln(w, "version\tregistered\tpublic-key\taddress\tport\tconns") internal.Catch(err) diff --git a/cmd/skywire-cli/commands/rtfind/root.go b/cmd/skywire-cli/commands/rtfind/root.go index 3b820e869a..3694c564fb 100644 --- a/cmd/skywire-cli/commands/rtfind/root.go +++ b/cmd/skywire-cli/commands/rtfind/root.go @@ -4,10 +4,10 @@ import ( "fmt" "time" + "github.com/skycoin/dmsg/cipher" "github.com/spf13/cobra" "github.com/skycoin/skywire/cmd/skywire-cli/internal" - "github.com/skycoin/skywire/pkg/cipher" "github.com/skycoin/skywire/pkg/route-finder/client" ) diff --git a/cmd/skywire-cli/commands/tpdisc/root.go b/cmd/skywire-cli/commands/tpdisc/root.go index d72036e914..323610e4d3 100644 --- a/cmd/skywire-cli/commands/tpdisc/root.go +++ b/cmd/skywire-cli/commands/tpdisc/root.go @@ -9,10 +9,10 @@ import ( "time" "github.com/google/uuid" + "github.com/skycoin/dmsg/cipher" "github.com/spf13/cobra" "github.com/skycoin/skywire/cmd/skywire-cli/internal" - "github.com/skycoin/skywire/pkg/cipher" "github.com/skycoin/skywire/pkg/transport" "github.com/skycoin/skywire/pkg/transport-discovery/client" ) diff --git a/cmd/skywire-cli/commands/visor/gen-config.go b/cmd/skywire-cli/commands/visor/gen-config.go index 27376a16a2..f2d2af6c20 100644 --- a/cmd/skywire-cli/commands/visor/gen-config.go +++ b/cmd/skywire-cli/commands/visor/gen-config.go @@ -6,9 +6,9 @@ import ( "path/filepath" "time" + "github.com/skycoin/dmsg/cipher" "github.com/spf13/cobra" - "github.com/skycoin/skywire/pkg/cipher" "github.com/skycoin/skywire/pkg/util/pathutil" "github.com/skycoin/skywire/pkg/visor" ) diff --git a/cmd/skywire-cli/commands/visor/transports.go b/cmd/skywire-cli/commands/visor/transports.go index 502322efb4..98609fda6e 100644 --- a/cmd/skywire-cli/commands/visor/transports.go +++ b/cmd/skywire-cli/commands/visor/transports.go @@ -7,12 +7,11 @@ import ( "text/tabwriter" "time" - "github.com/skycoin/skywire/pkg/dmsg" - + "github.com/skycoin/dmsg" + "github.com/skycoin/dmsg/cipher" "github.com/spf13/cobra" "github.com/skycoin/skywire/cmd/skywire-cli/internal" - "github.com/skycoin/skywire/pkg/cipher" "github.com/skycoin/skywire/pkg/visor" ) diff --git a/cmd/skywire-cli/internal/internal.go b/cmd/skywire-cli/internal/internal.go index 0557eae116..839bd31293 100644 --- a/cmd/skywire-cli/internal/internal.go +++ b/cmd/skywire-cli/internal/internal.go @@ -4,9 +4,8 @@ import ( "fmt" "github.com/google/uuid" + "github.com/skycoin/dmsg/cipher" "github.com/skycoin/skycoin/src/util/logging" - - "github.com/skycoin/skywire/pkg/cipher" ) var log = logging.MustGetLogger("skywire-cli") diff --git a/cmd/therealssh-cli/commands/root.go b/cmd/therealssh-cli/commands/root.go index 6937d587b5..646ef6ba6a 100644 --- a/cmd/therealssh-cli/commands/root.go +++ b/cmd/therealssh-cli/commands/root.go @@ -14,11 +14,10 @@ import ( "time" "github.com/kr/pty" + "github.com/skycoin/dmsg/cipher" "github.com/spf13/cobra" "golang.org/x/crypto/ssh/terminal" - "github.com/skycoin/skywire/pkg/cipher" - ssh "github.com/skycoin/skywire/internal/therealssh" ) diff --git a/docker/images/node/Dockerfile b/docker/images/node/Dockerfile new file mode 100644 index 0000000000..efa78efa5a --- /dev/null +++ b/docker/images/node/Dockerfile @@ -0,0 +1,38 @@ +# Builder +# ARG builder_base=golang:alpine +ARG base=alpine +FROM golang:alpine as builder + +ARG CGO_ENABLED=0 +ENV CGO_ENABLED=${CGO_ENABLED} \ + GOOS=linux \ + GOARCH=amd64 \ + GO111MODULE=on + +COPY . skywire + +WORKDIR skywire + +RUN go build -mod=vendor -tags netgo -ldflags="-w -s" \ + -o skywire-node cmd/skywire-node/skywire-node.go &&\ + go build -mod=vendor -ldflags="-w -s" -o ./apps/skychat.v1.0 ./cmd/apps/skychat &&\ + go build -mod=vendor -ldflags="-w -s" -o ./apps/helloworld.v1.0 ./cmd/apps/helloworld &&\ + go build -mod=vendor -ldflags="-w -s" -o ./apps/socksproxy.v1.0 ./cmd/apps/therealproxy &&\ + go build -mod=vendor -ldflags="-w -s" -o ./apps/socksproxy-client.v1.0 ./cmd/apps/therealproxy-client &&\ + go build -mod=vendor -ldflags="-w -s" -o ./apps/SSH.v1.0 ./cmd/apps/therealssh &&\ + go build -mod=vendor -ldflags="-w -s" -o ./apps/SSH-client.v1.0 ./cmd/apps/therealssh-client + + +## Resulting image +FROM ${base} as node-runner + +COPY --from=builder /go/skywire/skywire-node skywire-node +COPY --from=builder /go/skywire/apps bin/apps +COPY --from=builder /go/skywire/docker/images/node/update.sh update.sh + +RUN ./update.sh + +ENTRYPOINT [ "./skywire-node" ] + +# default target +FROM node-runner diff --git a/docker/images/node/update.sh b/docker/images/node/update.sh new file mode 100755 index 0000000000..8b78e26aee --- /dev/null +++ b/docker/images/node/update.sh @@ -0,0 +1,16 @@ +#!/bin/sh + +if type apt > /dev/null; then + apt-get update && apt-get install -y --no-install-recommends \ + ca-certificates \ + && rm -rf /var/lib/apt/lists/* +fi + +if type apk > /dev/null; then + + apk update + apk upgrade + apk add --no-cache ca-certificates openssl iproute2 + update-ca-certificates --fresh + apk add iproute2 +fi diff --git a/go.mod b/go.mod index f848fab2e2..8372dd0a9d 100644 --- a/go.mod +++ b/go.mod @@ -4,28 +4,26 @@ go 1.12 require ( github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 - github.com/flynn/noise v0.0.0-20180327030543-2492fe189ae6 github.com/go-chi/chi v4.0.2+incompatible github.com/google/uuid v1.1.1 github.com/gorilla/handlers v1.4.0 github.com/gorilla/securecookie v1.1.1 - github.com/hashicorp/go-retryablehttp v0.5.4 // indirect github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d github.com/kr/pty v1.1.5 - github.com/mattn/go-colorable v0.1.2 // indirect - github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b // indirect github.com/mitchellh/go-homedir v1.1.0 github.com/pkg/profile v1.3.0 github.com/prometheus/client_golang v1.0.0 github.com/prometheus/common v0.4.1 github.com/sirupsen/logrus v1.4.2 + github.com/skycoin/dmsg v0.0.0-20190628092537-e69f75132be9 github.com/skycoin/skycoin v0.26.0 github.com/spf13/cobra v0.0.5 github.com/stretchr/testify v1.3.0 go.etcd.io/bbolt v1.3.3 golang.org/x/crypto v0.0.0-20190621222207-cc06ce4a13d4 golang.org/x/net v0.0.0-20190620200207-3b0461eec859 - golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb // indirect golang.org/x/text v0.3.2 // indirect - golang.org/x/tools v0.0.0-20190627033414-4874f863e654 // indirect ) + +// Uncomment for tests with alternate branches of 'dmsg' +//replace github.com/skycoin/dmsg => ../dmsg diff --git a/go.sum b/go.sum index 756babf8f9..37cdc32e9b 100644 --- a/go.sum +++ b/go.sum @@ -34,8 +34,6 @@ github.com/gorilla/handlers v1.4.0 h1:XulKRWSQK5uChr4pEgSE4Tc/OcmnU9GJuSwdog/tZs github.com/gorilla/handlers v1.4.0/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ= github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ= github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4= -github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= -github.com/hashicorp/go-retryablehttp v0.5.4/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d h1:kJCB4vdITiW1eC1vq2e6IsrXKrZit1bv/TDYFGMp4BQ= github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= @@ -45,9 +43,16 @@ github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCV github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s= +github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= +github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.5 h1:hyz3dwM5QLc1Rfoz4FuWJQG5BN7tc6K1MndAUnGpQr4= github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/mattn/go-colorable v0.1.2 h1:/bC9yWikZXAL9uJdulbSfyVNIR3n3trXl+v8+1sx8mU= github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= @@ -84,6 +89,8 @@ github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/skycoin/dmsg v0.0.0-20190628092537-e69f75132be9 h1:Y7CZdMtd5HxkgIaAILImIlq8t32kqyRF9sx4CnTm00E= +github.com/skycoin/dmsg v0.0.0-20190628092537-e69f75132be9/go.mod h1:AwwGhHjvXpbYVz8oYFaXKWHwscc5nHyHjssQBtQgmZg= github.com/skycoin/skycoin v0.26.0 h1:xDxe2r8AclMntZ550Y/vUQgwgLtwrf9Wu5UYiYcN5/o= github.com/skycoin/skycoin v0.26.0/go.mod h1:78nHjQzd8KG0jJJVL/j0xMmrihXi70ti63fh8vXScJw= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= @@ -109,13 +116,11 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk golang.org/x/crypto v0.0.0-20190621222207-cc06ce4a13d4 h1:ydJNl0ENAG67pFbB+9tfhiL2pYqLhfoaZFw/cjLhY4A= golang.org/x/crypto v0.0.0-20190621222207-cc06ce4a13d4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859 h1:R/3boaszxrf1GEUWTVDzSKVwLmSJpwZ1yqXm8j0v2QI= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -123,20 +128,18 @@ golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0 h1:HyfiK1WMnHj5FXFXatD+Qs1A/xC2Run6RzeW1SyHxpc= -golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb h1:fgwFCsaw9buMuxNd6+DQfAuSFqbNiQZpcgJQAgJsK6k= golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190612231717-10539ce30318/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190627033414-4874f863e654 h1:a5iTclD5417yiTAwxzAQY6HG6HeJS4MqmPCoTlbd+0I= -golang.org/x/tools v0.0.0-20190627033414-4874f863e654/go.mod h1:F+l5rz3/Uc0BJWNSxc0r6FcPZ3lxfduWytgpR5peIOQ= -golang.org/x/tools/gopls v0.1.0/go.mod h1:p8Q0IUu6EEeGxqmoN/g6Et3gReLCGA7PtNRdyOxcWJE= gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/integration/test-messaging.sh b/integration/test-messaging.sh index 3a8ed4cf49..6a5add8b75 100755 --- a/integration/test-messaging.sh +++ b/integration/test-messaging.sh @@ -1,4 +1,3 @@ #!/usr/bin/env bash -source ./integration/generic/env-vars.sh -# curl --data {'"recipient":"'$PK_A'", "message":"Hello Joe!"}' -X POST $CHAT_C +curl --data {'"recipient":"'$PK_A'", "message":"Hello Joe!"}' -X POST $CHAT_C curl --data {'"recipient":"'$PK_C'", "message":"Hello Mike!"}' -X POST $CHAT_A diff --git a/internal/httpauth/auth.go b/internal/httpauth/auth.go index a004e90362..03a3a8cb03 100644 --- a/internal/httpauth/auth.go +++ b/internal/httpauth/auth.go @@ -6,7 +6,7 @@ import ( "net/http" "strconv" - "github.com/skycoin/skywire/pkg/cipher" + "github.com/skycoin/dmsg/cipher" ) // Nonce is used to sign requests in order to avoid replay attack diff --git a/internal/httpauth/client.go b/internal/httpauth/client.go index 0acc851232..5c1823c955 100644 --- a/internal/httpauth/client.go +++ b/internal/httpauth/client.go @@ -14,7 +14,7 @@ import ( "strings" "sync/atomic" - "github.com/skycoin/skywire/pkg/cipher" + "github.com/skycoin/dmsg/cipher" ) const ( diff --git a/internal/httpauth/client_test.go b/internal/httpauth/client_test.go index 8ca1d6f9ff..4697d88c68 100644 --- a/internal/httpauth/client_test.go +++ b/internal/httpauth/client_test.go @@ -13,12 +13,10 @@ import ( "strconv" "testing" + "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/skycoin/src/util/logging" - - "github.com/skycoin/skywire/pkg/cipher" ) const ( diff --git a/internal/ioutil/ack_waiter_test.go b/internal/ioutil/ack_waiter_test.go deleted file mode 100644 index 3ba22c5910..0000000000 --- a/internal/ioutil/ack_waiter_test.go +++ /dev/null @@ -1,47 +0,0 @@ -package ioutil_test - -import ( - "context" - "sync" - "testing" - - "github.com/skycoin/skywire/internal/ioutil" -) - -func TestUint16AckWaiter_Wait(t *testing.T) { - - // Ensure that no race conditions occurs when - // each concurrent call to 'Uint16AckWaiter.Wait()' is met with - // multiple concurrent calls to 'Uint16AckWaiter.Done()' with the same seq. - t.Run("ensure_no_race_conditions", func(*testing.T) { - w := new(ioutil.Uint16AckWaiter) - defer w.StopAll() - - seqChan := make(chan ioutil.Uint16Seq) - defer close(seqChan) - - wg := new(sync.WaitGroup) - - for i := 0; i < 64; i++ { - wg.Add(1) - go func() { - defer wg.Done() - _ = w.Wait(context.TODO(), func(seq ioutil.Uint16Seq) error { //nolint:errcheck,unparam - seqChan <- seq - return nil - }) - }() - - seq := <-seqChan - for j := 0; j <= i; j++ { - wg.Add(1) - go func() { - defer wg.Done() - w.Done(seq) - }() - } - } - - wg.Wait() - }) -} diff --git a/internal/ioutil/len_read_writer.go b/internal/ioutil/len_read_writer.go deleted file mode 100644 index dffaf48b72..0000000000 --- a/internal/ioutil/len_read_writer.go +++ /dev/null @@ -1,57 +0,0 @@ -package ioutil - -import ( - "bytes" - "encoding/binary" - "io" - "sync" -) - -// LenReadWriter writes len prepended packets and always reads whole -// packet. If read buffer is smaller than packet, LenReadWriter will -// buffer unread part and will return it first in subsequent reads. -type LenReadWriter struct { - io.ReadWriter - buf bytes.Buffer - mx sync.Mutex -} - -// NewLenReadWriter constructs a new LenReadWriter. -func NewLenReadWriter(rw io.ReadWriter) *LenReadWriter { - return &LenReadWriter{ReadWriter: rw} -} - -// ReadPacket returns single received len prepended packet. -func (rw *LenReadWriter) ReadPacket() ([]byte, error) { - h := make([]byte, 2) - if _, err := io.ReadFull(rw.ReadWriter, h); err != nil { - return nil, err - } - data := make([]byte, binary.BigEndian.Uint16(h)) - _, err := io.ReadFull(rw.ReadWriter, data) - return data, err -} - -func (rw *LenReadWriter) Read(p []byte) (n int, err error) { - rw.mx.Lock() - defer rw.mx.Unlock() - - if rw.buf.Len() != 0 { - return rw.buf.Read(p) - } - - var data []byte - data, err = rw.ReadPacket() - if err != nil { - return - } - - return BufRead(&rw.buf, data, p) -} - -func (rw *LenReadWriter) Write(p []byte) (n int, err error) { - buf := make([]byte, 2) - binary.BigEndian.PutUint16(buf, uint16(len(p))) - n, err = rw.ReadWriter.Write(append(buf, p...)) - return n - 2, err -} diff --git a/internal/ioutil/len_read_writer_test.go b/internal/ioutil/len_read_writer_test.go deleted file mode 100644 index 0b55b1b7aa..0000000000 --- a/internal/ioutil/len_read_writer_test.go +++ /dev/null @@ -1,72 +0,0 @@ -package ioutil - -import ( - "log" - "net" - "os" - "testing" - - "github.com/skycoin/skycoin/src/util/logging" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestMain(m *testing.M) { - loggingLevel, ok := os.LookupEnv("TEST_LOGGING_LEVEL") - if ok { - lvl, err := logging.LevelFromString(loggingLevel) - if err != nil { - log.Fatal(err) - } - logging.SetLevel(lvl) - } else { - logging.Disable() - } - - os.Exit(m.Run()) -} - -func TestLenReadWriter(t *testing.T) { - in, out := net.Pipe() - rwIn := NewLenReadWriter(in) - rwOut := NewLenReadWriter(out) - - errCh := make(chan error) - go func() { - _, err := rwIn.Write([]byte("foo")) - errCh <- err - }() - - buf := make([]byte, 2) - n, err := rwOut.Read(buf) - require.NoError(t, err) - require.NoError(t, <-errCh) - assert.Equal(t, 2, n) - assert.Equal(t, []byte("fo"), buf) - - buf = make([]byte, 2) - n, err = rwOut.Read(buf) - require.NoError(t, err) - assert.Equal(t, 1, n) - assert.Equal(t, []byte("o"), buf[:n]) - - go func() { - _, err := rwIn.Write([]byte("foo")) - errCh <- err - }() - - packet, err := rwOut.ReadPacket() - require.NoError(t, err) - require.NoError(t, <-errCh) - assert.Equal(t, []byte("foo"), packet) - - go func() { - _, err := rwOut.ReadPacket() - errCh <- err - }() - - n, err = rwIn.Write([]byte("bar")) - require.NoError(t, err) - require.NoError(t, <-errCh) - assert.Equal(t, 3, n) -} diff --git a/internal/noise/net_test.go b/internal/noise/net_test.go deleted file mode 100644 index 1462f86b36..0000000000 --- a/internal/noise/net_test.go +++ /dev/null @@ -1,342 +0,0 @@ -package noise - -import ( - "fmt" - "io" - "log" - "net" - "net/rpc" - "sync" - "testing" - "time" - - "github.com/flynn/noise" - "github.com/stretchr/testify/require" - - "github.com/skycoin/skywire/pkg/cipher" -) - -type TestRPC struct{} - -type AddIn struct{ A, B int } - -func (r *TestRPC) Add(in *AddIn, out *int) error { - *out = in.A + in.B - return nil -} - -func TestRPCClientDialer(t *testing.T) { - var ( - pattern = HandshakeXK - ) - - svr := rpc.NewServer() - require.NoError(t, svr.Register(new(TestRPC))) - - lPK, lSK := cipher.GenerateKeyPair() - var l net.Listener - var lAddr string - - setup := func() { - if len(lAddr) == 0 { - lAddr = ":0" - } - var err error - - l, err = net.Listen("tcp", lAddr) - require.NoError(t, err) - - l = WrapListener(l, lPK, lSK, false, pattern) - lAddr = l.Addr().(*Addr).Addr.String() - t.Logf("Listening on %s", lAddr) - } - - teardown := func() { - if l != nil { - require.NoError(t, l.Close()) - l = nil - } - } - - t.Run("RunRetry", func(t *testing.T) { - setup() - defer teardown() // Just in case of failure. - - const reconCount = 5 - const retry = time.Second / 4 - - dPK, dSK := cipher.GenerateKeyPair() - d := NewRPCClientDialer(lAddr, pattern, Config{ - LocalPK: dPK, - LocalSK: dSK, - RemotePK: lPK, - Initiator: true, - }) - dDone := make(chan error, 1) - - go func() { - dDone <- d.Run(svr, retry) - close(dDone) - }() - - for i := 0; i < reconCount; i++ { - teardown() - time.Sleep(retry * 2) // Dialer shouldn't quit retrying in this time. - setup() - - conn, err := l.Accept() - require.NoError(t, err) - - in, out := &AddIn{A: i, B: i}, new(int) - require.NoError(t, rpc.NewClient(conn).Call("TestRPC.Add", in, out)) - require.Equal(t, in.A+in.B, *out) - require.NoError(t, conn.Close()) - } - - _ = d.Close() - require.NoError(t, <-dDone) - }) -} - -func TestConn(t *testing.T) { - type Result struct { - N int - Err error - } - - const timeout = time.Second - - aPK, aSK := cipher.GenerateKeyPair() - bPK, bSK := cipher.GenerateKeyPair() - - aNs, err := XKAndSecp256k1(Config{LocalPK: aPK, LocalSK: aSK, RemotePK: bPK, Initiator: true}) - require.NoError(t, err) - bNs, err := XKAndSecp256k1(Config{LocalPK: bPK, LocalSK: bSK, Initiator: false}) - require.NoError(t, err) - - aConn, bConn := net.Pipe() - defer func() { _, _ = aConn.Close(), bConn.Close() }() - - aRW := NewReadWriter(aConn, aNs) - bRW := NewReadWriter(bConn, bNs) - - errChan := make(chan error, 2) - go func() { errChan <- aRW.Handshake(timeout) }() - go func() { errChan <- bRW.Handshake(timeout) }() - require.NoError(t, <-errChan) - require.NoError(t, <-errChan) - close(errChan) - - a := &Conn{Conn: aConn, ns: aRW} - b := &Conn{Conn: bConn, ns: bRW} - - t.Run("ReadWrite", func(t *testing.T) { - aResults := make(chan Result) - bResults := make(chan Result) - - for i := 0; i < 10; i++ { - msgAtoB := []byte(fmt.Sprintf("this is message %d from A for B", i)) - - go func() { - n, err := a.Write(msgAtoB) - aResults <- Result{N: n, Err: err} - }() - - receivedMsgAtoB := make([]byte, len(msgAtoB)) - n, err := io.ReadFull(b, receivedMsgAtoB) - require.Equal(t, len(msgAtoB), n) - require.NoError(t, err) - - aResult := <-aResults - require.Equal(t, len(msgAtoB), aResult.N) - require.NoError(t, aResult.Err) - - msgBtoA := []byte(fmt.Sprintf("this is message %d from B for A", i)) - - go func() { - n, err := b.Write(msgAtoB) - bResults <- Result{N: n, Err: err} - }() - - receivedMsgBtoA := make([]byte, len(msgBtoA)) - n, err = io.ReadFull(a, receivedMsgBtoA) - require.Equal(t, len(msgBtoA), n) - require.NoError(t, err) - - bResult := <-bResults - require.Equal(t, len(msgBtoA), bResult.N) - require.NoError(t, bResult.Err) - } - }) - - t.Run("ReadWriteConcurrent", func(t *testing.T) { - type ReadResult struct { - Msg string - N int - Err error - } - const ( - MsgCount = 100 - MsgLen = 4 - ) - var ( - aMap = make(map[string]struct{}) - bMap = make(map[string]struct{}) - aWrites = make(chan Result, MsgCount) - bWrites = make(chan Result, MsgCount) - aReads = make(chan ReadResult, MsgCount) - bReads = make(chan ReadResult, MsgCount) - ) - randSleep := func() { time.Sleep(time.Duration(cipher.RandByte(1)[0]) / 255 * time.Second) } - - for i := 0; i < MsgCount; i++ { - msg := fmt.Sprintf("%4d", i) - go func() { - randSleep() - n, err := a.Write([]byte(msg)) - aWrites <- Result{N: n, Err: err} - }() - go func() { - randSleep() - n, err := b.Write([]byte(msg)) - bWrites <- Result{N: n, Err: err} - }() - go func() { - randSleep() - msg := make([]byte, MsgLen) - n, err := io.ReadFull(a, msg) - aReads <- ReadResult{Msg: string(msg), N: n, Err: err} - }() - go func() { - randSleep() - msg := make([]byte, MsgLen) - n, err := io.ReadFull(b, msg) - bReads <- ReadResult{Msg: string(msg), N: n, Err: err} - }() - } - - for i := 0; i < MsgCount; i++ { - aWrite := <-aWrites - require.NoError(t, aWrite.Err) - require.Equal(t, MsgLen, aWrite.N) - - bWrite := <-bWrites - require.NoError(t, bWrite.Err) - require.Equal(t, MsgLen, bWrite.N) - - aRead := <-aReads - require.NoError(t, aRead.Err) - require.Equal(t, MsgLen, aRead.N) - _, aHas := aMap[aRead.Msg] - require.False(t, aHas) - aMap[aRead.Msg] = struct{}{} - - bRead := <-bReads - require.NoError(t, bRead.Err) - require.Equal(t, MsgLen, bRead.N) - _, bHas := bMap[bRead.Msg] - require.False(t, bHas) - bMap[bRead.Msg] = struct{}{} - } - - require.Len(t, aMap, MsgCount) - require.Len(t, bMap, MsgCount) - }) - - t.Run("ReadWriteIrregular", func(t *testing.T) { - const segLen = 100 - const segCount = 1000 - - aResults := make([]Result, segCount) - - msg := cipher.RandByte(segLen * segCount) - - wg := new(sync.WaitGroup) - wg.Add(1) - go func() { - for i := 0; i < segCount; i++ { - n, err := a.Write(msg[i*segLen : (i+1)*segLen]) - aResults[i] = Result{N: n, Err: err} - } - wg.Done() - }() - - msgResult := make([]byte, len(msg)) - _, err := io.ReadFull(b, msgResult) - require.NoError(t, err) - require.Equal(t, msg, msgResult) - - wg.Wait() - - for i, r := range aResults { - require.NoError(t, r.Err, i) - require.Equal(t, segLen, r.N, i) - } - }) -} - -func TestListener(t *testing.T) { - const ( - connCount = 10 - msg = "Hello, world!" - timeout = time.Second - ) - var ( - pattern = noise.HandshakeXK - ) - - dialAndWrite := func(remote cipher.PubKey, addr string) error { - pk, sk := cipher.GenerateKeyPair() - conn, err := net.Dial("tcp", addr) - if err != nil { - return err - } - ns, err := New(pattern, Config{LocalPK: pk, LocalSK: sk, RemotePK: remote, Initiator: true}) - if err != nil { - return err - } - conn, err = WrapConn(conn, ns, timeout) - if err != nil { - return err - } - _, err = conn.Write([]byte(msg)) - if err != nil { - return err - } - return conn.Close() - } - - lPK, lSK := cipher.GenerateKeyPair() - l, err := net.Listen("tcp", "") - require.NoError(t, err) - defer l.Close() - - l = WrapListener(l, lPK, lSK, false, pattern) - addr := l.Addr().(*Addr) - - t.Run("Accept", func(t *testing.T) { - hResults := make([]error, connCount) - wg := new(sync.WaitGroup) - wg.Add(1) - go func() { - for i := 0; i < connCount; i++ { - hResults[i] = dialAndWrite(lPK, addr.Addr.String()) - } - wg.Done() - }() - for i := 0; i < connCount; i++ { - lConn, err := l.Accept() - require.NoError(t, err) - rec := make([]byte, len(msg)) - n, err := io.ReadFull(lConn, rec) - log.Printf("Accept('%s'): received: '%s'", lConn.RemoteAddr(), string(rec)) - require.Equal(t, len(msg), n) - require.NoError(t, err) - require.NoError(t, lConn.Close()) - } - wg.Wait() - for i := 0; i < connCount; i++ { - require.NoError(t, hResults[i]) - } - }) -} diff --git a/internal/noise/noise_test.go b/internal/noise/noise_test.go deleted file mode 100644 index 143045e7f1..0000000000 --- a/internal/noise/noise_test.go +++ /dev/null @@ -1,141 +0,0 @@ -package noise - -import ( - "log" - "os" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/skycoin/skycoin/src/util/logging" - - "github.com/skycoin/skywire/pkg/cipher" -) - -func TestMain(m *testing.M) { - loggingLevel, ok := os.LookupEnv("TEST_LOGGING_LEVEL") - if ok { - lvl, err := logging.LevelFromString(loggingLevel) - if err != nil { - log.Fatal(err) - } - logging.SetLevel(lvl) - } else { - logging.Disable() - } - - os.Exit(m.Run()) -} - -func TestKKAndSecp256k1(t *testing.T) { - pkI, skI := cipher.GenerateKeyPair() - pkR, skR := cipher.GenerateKeyPair() - - confI := Config{ - LocalPK: pkI, - LocalSK: skI, - RemotePK: pkR, - Initiator: true, - } - - confR := Config{ - LocalPK: pkR, - LocalSK: skR, - RemotePK: pkI, - Initiator: false, - } - - nI, err := KKAndSecp256k1(confI) - require.NoError(t, err) - - nR, err := KKAndSecp256k1(confR) - require.NoError(t, err) - - // -> e, es - msg, err := nI.HandshakeMessage() - require.NoError(t, err) - require.Error(t, nR.ProcessMessage(append(msg, 1))) - require.NoError(t, nR.ProcessMessage(msg)) - - // <- e, ee - msg, err = nR.HandshakeMessage() - require.NoError(t, err) - require.Error(t, nI.ProcessMessage(append(msg, 1))) - require.NoError(t, nI.ProcessMessage(msg)) - - require.True(t, nI.HandshakeFinished()) - require.True(t, nR.HandshakeFinished()) - - encrypted := nI.EncryptUnsafe([]byte("foo")) - decrypted, err := nR.DecryptUnsafe(encrypted) - require.NoError(t, err) - assert.Equal(t, []byte("foo"), decrypted) - - encrypted = nR.EncryptUnsafe([]byte("bar")) - decrypted, err = nI.DecryptUnsafe(encrypted) - require.NoError(t, err) - assert.Equal(t, []byte("bar"), decrypted) - - encrypted = nI.EncryptUnsafe([]byte("baz")) - decrypted, err = nR.DecryptUnsafe(encrypted) - require.NoError(t, err) - assert.Equal(t, []byte("baz"), decrypted) -} - -func TestXKAndSecp256k1(t *testing.T) { - pkI, skI := cipher.GenerateKeyPair() - pkR, skR := cipher.GenerateKeyPair() - - confI := Config{ - LocalPK: pkI, - LocalSK: skI, - RemotePK: pkR, - Initiator: true, - } - - confR := Config{ - LocalPK: pkR, - LocalSK: skR, - Initiator: false, - } - - nI, err := XKAndSecp256k1(confI) - require.NoError(t, err) - - nR, err := XKAndSecp256k1(confR) - require.NoError(t, err) - - // -> e, es - msg, err := nI.HandshakeMessage() - require.NoError(t, err) - require.NoError(t, nR.ProcessMessage(msg)) - - // <- e, ee - msg, err = nR.HandshakeMessage() - require.NoError(t, err) - require.NoError(t, nI.ProcessMessage(msg)) - - // -> s, se - msg, err = nI.HandshakeMessage() - require.NoError(t, err) - require.NoError(t, nR.ProcessMessage(msg)) - - require.True(t, nI.HandshakeFinished()) - require.True(t, nR.HandshakeFinished()) - - encrypted := nI.EncryptUnsafe([]byte("foo")) - decrypted, err := nR.DecryptUnsafe(encrypted) - require.NoError(t, err) - assert.Equal(t, []byte("foo"), decrypted) - - encrypted = nR.EncryptUnsafe([]byte("bar")) - decrypted, err = nI.DecryptUnsafe(encrypted) - require.NoError(t, err) - assert.Equal(t, []byte("bar"), decrypted) - - encrypted = nI.EncryptUnsafe([]byte("baz")) - decrypted, err = nR.DecryptUnsafe(encrypted) - require.NoError(t, err) - assert.Equal(t, []byte("baz"), decrypted) -} diff --git a/internal/noise/read_writer_test.go b/internal/noise/read_writer_test.go deleted file mode 100644 index 18a7a5622f..0000000000 --- a/internal/noise/read_writer_test.go +++ /dev/null @@ -1,216 +0,0 @@ -package noise - -import ( - "fmt" - "net" - "testing" - "time" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/skycoin/skywire/pkg/cipher" -) - -func TestNewReadWriter(t *testing.T) { - - type Result struct { - n int - err error - b []byte - } - - t.Run("concurrent", func(t *testing.T) { - aPK, aSK := cipher.GenerateKeyPair() - bPK, bSK := cipher.GenerateKeyPair() - - aNs, err := KKAndSecp256k1(Config{ - LocalPK: aPK, - LocalSK: aSK, - RemotePK: bPK, - Initiator: true, - }) - require.NoError(t, err) - - bNs, err := KKAndSecp256k1(Config{ - LocalPK: bPK, - LocalSK: bSK, - RemotePK: aPK, - Initiator: false, - }) - require.NoError(t, err) - - aConn, bConn := net.Pipe() - defer func() { - _ = aConn.Close() //nolint:errcheck - _ = bConn.Close() //nolint:errcheck - }() - - aRW := NewReadWriter(aConn, aNs) - bRW := NewReadWriter(bConn, bNs) - - hsCh := make(chan error, 2) - defer close(hsCh) - go func() { hsCh <- aRW.Handshake(time.Second) }() - go func() { hsCh <- bRW.Handshake(time.Second) }() - require.NoError(t, <-hsCh) - require.NoError(t, <-hsCh) - - const groupSize = 10 - const totalGroups = 5 - const msgCount = totalGroups * groupSize - - writes := make([][]byte, msgCount) - - wCh := make(chan Result, msgCount) - defer close(wCh) - rCh := make(chan Result, msgCount) - defer close(rCh) - - for i := 0; i < msgCount; i++ { - writes[i] = []byte(fmt.Sprintf("this is message: %d", i)) - } - - for i := 0; i < totalGroups; i++ { - go func(i int) { - for j := 0; j < groupSize; j++ { - go func(i, j int) { - b := writes[i*j] - n, err := aRW.Write(b) - wCh <- Result{n: n, err: err, b: b} - }(i, j) - go func() { - buf := make([]byte, 100) - n, err := bRW.Read(buf) - rCh <- Result{n: n, err: err, b: buf[:n]} - }() - } - }(i) - } - - for i := 0; i < msgCount; i++ { - w := <-wCh - fmt.Printf("write_result[%d]: b(%s) err(%v)\n", i, string(w.b), w.err) - assert.NoError(t, w.err) - assert.True(t, w.n > 0) - - r := <-rCh - fmt.Printf(" read_result[%d]: b(%s) err(%v)\n", i, string(r.b), r.err) - assert.NoError(t, r.err) - assert.True(t, r.n > 0) - } - }) -} - -func TestReadWriterKKPattern(t *testing.T) { - pkI, skI := cipher.GenerateKeyPair() - pkR, skR := cipher.GenerateKeyPair() - - confI := Config{ - LocalPK: pkI, - LocalSK: skI, - RemotePK: pkR, - Initiator: true, - } - - confR := Config{ - LocalPK: pkR, - LocalSK: skR, - RemotePK: pkI, - Initiator: false, - } - - nI, err := KKAndSecp256k1(confI) - require.NoError(t, err) - - nR, err := KKAndSecp256k1(confR) - require.NoError(t, err) - - connI, connR := net.Pipe() - rwI := NewReadWriter(connI, nI) - rwR := NewReadWriter(connR, nR) - - errCh := make(chan error) - go func() { errCh <- rwR.Handshake(time.Second) }() - require.NoError(t, rwI.Handshake(time.Second)) - require.NoError(t, <-errCh) - - go func() { - _, err := rwI.Write([]byte("foo")) - errCh <- err - }() - - buf := make([]byte, 3) - n, err := rwR.Read(buf) - require.NoError(t, err) - require.NoError(t, <-errCh) - assert.Equal(t, 3, n) - assert.Equal(t, []byte("foo"), buf) - - go func() { - _, err := rwI.Read(buf) - errCh <- err - }() - - n, err = rwR.Write([]byte("bar")) - require.NoError(t, err) - require.NoError(t, <-errCh) - assert.Equal(t, 3, n) - assert.Equal(t, []byte("bar"), buf) -} - -func TestReadWriterXKPattern(t *testing.T) { - pkI, skI := cipher.GenerateKeyPair() - pkR, skR := cipher.GenerateKeyPair() - - confI := Config{ - LocalPK: pkI, - LocalSK: skI, - RemotePK: pkR, - Initiator: true, - } - - confR := Config{ - LocalPK: pkR, - LocalSK: skR, - Initiator: false, - } - - nI, err := XKAndSecp256k1(confI) - require.NoError(t, err) - - nR, err := XKAndSecp256k1(confR) - require.NoError(t, err) - - connI, connR := net.Pipe() - rwI := NewReadWriter(connI, nI) - rwR := NewReadWriter(connR, nR) - - errCh := make(chan error) - go func() { errCh <- rwR.Handshake(time.Second) }() - require.NoError(t, rwI.Handshake(time.Second)) - require.NoError(t, <-errCh) - - go func() { - _, err := rwI.Write([]byte("foo")) - errCh <- err - }() - - buf := make([]byte, 3) - n, err := rwR.Read(buf) - require.NoError(t, err) - require.NoError(t, <-errCh) - assert.Equal(t, 3, n) - assert.Equal(t, []byte("foo"), buf) - - go func() { - _, err := rwI.Read(buf) - errCh <- err - }() - - n, err = rwR.Write([]byte("bar")) - require.NoError(t, err) - require.NoError(t, <-errCh) - assert.Equal(t, 3, n) - assert.Equal(t, []byte("bar"), buf) -} diff --git a/internal/therealproxy/client.go b/internal/therealproxy/client.go index 5bbd5b25b0..397cc4408f 100644 --- a/internal/therealproxy/client.go +++ b/internal/therealproxy/client.go @@ -74,5 +74,8 @@ func (c *Client) ListenAndServe(addr string) error { // Close implement io.Closer. func (c *Client) Close() error { + if c == nil { + return nil + } return c.listener.Close() } diff --git a/internal/therealproxy/server.go b/internal/therealproxy/server.go index d117e8ff5b..4f0c3a7f39 100644 --- a/internal/therealproxy/server.go +++ b/internal/therealproxy/server.go @@ -55,6 +55,9 @@ func (s *Server) Serve(l net.Listener) error { // Close implement io.Closer. func (s *Server) Close() error { + if s == nil { + return nil + } return s.listener.Close() } diff --git a/internal/therealssh/auth.go b/internal/therealssh/auth.go index a8e25f50ec..c4ed1923c2 100644 --- a/internal/therealssh/auth.go +++ b/internal/therealssh/auth.go @@ -7,7 +7,7 @@ import ( "os" "path/filepath" - "github.com/skycoin/skywire/pkg/cipher" + "github.com/skycoin/dmsg/cipher" ) // Authorizer defines interface for authorization providers. @@ -62,6 +62,9 @@ func NewFileAuthorizer(authFile string) (*FileAuthorizer, error) { // Close releases underlying file pointer. func (auth *FileAuthorizer) Close() error { + if auth == nil { + return nil + } return auth.authFile.Close() } diff --git a/internal/therealssh/auth_test.go b/internal/therealssh/auth_test.go index 221c217a3e..22c327853c 100644 --- a/internal/therealssh/auth_test.go +++ b/internal/therealssh/auth_test.go @@ -5,9 +5,8 @@ import ( "os" "testing" + "github.com/skycoin/dmsg/cipher" "github.com/stretchr/testify/require" - - "github.com/skycoin/skywire/pkg/cipher" ) func TestListAuthoriser(t *testing.T) { diff --git a/internal/therealssh/channel.go b/internal/therealssh/channel.go index c3fd9a1b6e..eafa235bf3 100644 --- a/internal/therealssh/channel.go +++ b/internal/therealssh/channel.go @@ -11,10 +11,10 @@ import ( "os/user" "path/filepath" "strings" + "sync" "github.com/kr/pty" - - "github.com/skycoin/skywire/pkg/cipher" + "github.com/skycoin/dmsg/cipher" "github.com/skycoin/skywire/pkg/app" ) @@ -33,14 +33,21 @@ type SSHChannel struct { conn net.Conn msgCh chan []byte - session *Session - listener *net.UnixListener + session *Session + listenerMx sync.Mutex + listener *net.UnixListener + + dataChMx sync.Mutex dataCh chan []byte + + doneOnce sync.Once + done chan struct{} } // OpenChannel constructs new SSHChannel with empty Session. func OpenChannel(remoteID uint32, remoteAddr *app.Addr, conn net.Conn) *SSHChannel { - return &SSHChannel{RemoteID: remoteID, conn: conn, RemoteAddr: remoteAddr, msgCh: make(chan []byte), dataCh: make(chan []byte)} + return &SSHChannel{RemoteID: remoteID, conn: conn, RemoteAddr: remoteAddr, msgCh: make(chan []byte), + dataCh: make(chan []byte), done: make(chan struct{})} } // OpenClientChannel constructs new client SSHChannel with empty Session. @@ -149,7 +156,9 @@ func (sshCh *SSHChannel) ServeSocket() error { return fmt.Errorf("failed to open unix socket: %s", err) } + sshCh.listenerMx.Lock() sshCh.listener = l + sshCh.listenerMx.Unlock() conn, err := l.AcceptUnix() if err != nil { return fmt.Errorf("failed to accept connection: %s", err) @@ -158,8 +167,7 @@ func (sshCh *SSHChannel) ServeSocket() error { debug("got new socket connection") defer func() { conn.Close() - sshCh.listener.Close() - sshCh.listener = nil + sshCh.closeListener() //nolint:errcheck os.Remove(sshCh.SocketPath()) }() @@ -244,33 +252,73 @@ func (sshCh *SSHChannel) WindowChange(sz *pty.Winsize) error { return sshCh.session.WindowChange(sz) } +func (sshCh *SSHChannel) close() (closed bool, err error) { + sshCh.doneOnce.Do(func() { + closed = true + + close(sshCh.done) + + select { + case <-sshCh.dataCh: + default: + sshCh.dataChMx.Lock() + close(sshCh.dataCh) + sshCh.dataChMx.Unlock() + } + close(sshCh.msgCh) + + var sErr, lErr error + if sshCh.session != nil { + sErr = sshCh.session.Close() + } + + lErr = sshCh.closeListener() + + if sErr != nil { + err = sErr + return + } + + if lErr != nil { + err = lErr + } + }) + + return closed, err +} + // Close safely closes Channel resources. func (sshCh *SSHChannel) Close() error { - select { - case <-sshCh.dataCh: - default: - close(sshCh.dataCh) + if sshCh == nil { + return nil } - close(sshCh.msgCh) - var sErr, lErr error - if sshCh.session != nil { - sErr = sshCh.session.Close() + closed, err := sshCh.close() + if err != nil { + return err } - - if sshCh.listener != nil { - lErr = sshCh.listener.Close() + if !closed { + return errors.New("channel is already closed") } - if sErr != nil { - return sErr - } + return nil +} - if lErr != nil { - return lErr +// IsClosed returns whether the Channel is closed. +func (sshCh *SSHChannel) IsClosed() bool { + select { + case <-sshCh.done: + return true + default: + return false } +} - return nil +func (sshCh *SSHChannel) closeListener() error { + sshCh.listenerMx.Lock() + defer sshCh.listenerMx.Unlock() + + return sshCh.listener.Close() } func debug(format string, v ...interface{}) { diff --git a/internal/therealssh/channel_pty_test.go b/internal/therealssh/channel_pty_test.go index ed00c55f09..31a8d8f741 100644 --- a/internal/therealssh/channel_pty_test.go +++ b/internal/therealssh/channel_pty_test.go @@ -8,11 +8,10 @@ import ( "testing" "time" + "github.com/skycoin/dmsg/cipher" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/skycoin/skywire/pkg/cipher" - "github.com/skycoin/skywire/pkg/app" ) diff --git a/internal/therealssh/channel_test.go b/internal/therealssh/channel_test.go index fbb657a50f..8cca04e2eb 100644 --- a/internal/therealssh/channel_test.go +++ b/internal/therealssh/channel_test.go @@ -9,11 +9,10 @@ import ( "testing" "time" + "github.com/skycoin/dmsg/cipher" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/skycoin/skywire/pkg/cipher" - "github.com/skycoin/skywire/pkg/app" ) diff --git a/internal/therealssh/client.go b/internal/therealssh/client.go index f74b1dc733..2b9c02b98f 100644 --- a/internal/therealssh/client.go +++ b/internal/therealssh/client.go @@ -11,12 +11,10 @@ import ( "strings" "time" - "github.com/skycoin/skywire/internal/netutil" - "github.com/kr/pty" + "github.com/skycoin/dmsg/cipher" - "github.com/skycoin/skywire/pkg/cipher" - + "github.com/skycoin/skywire/internal/netutil" "github.com/skycoin/skywire/pkg/app" ) @@ -134,7 +132,11 @@ func (c *Client) serveConn(conn net.Conn) error { case CmdChannelOpenResponse, CmdChannelResponse: sshCh.msgCh <- data case CmdChannelData: - sshCh.dataCh <- data + sshCh.dataChMx.Lock() + if !sshCh.IsClosed() { + sshCh.dataCh <- data + } + sshCh.dataChMx.Unlock() case CmdChannelServerClose: err = sshCh.Close() default: @@ -149,6 +151,10 @@ func (c *Client) serveConn(conn net.Conn) error { // Close closes all opened channels. func (c *Client) Close() error { + if c == nil { + return nil + } + for _, sshCh := range c.chans.dropAll() { sshCh.Close() } @@ -226,6 +232,10 @@ func (rpc *RPCClient) WindowChange(args *WindowChangeArgs, _ *int) error { // Close defines close client RPC request. func (rpc *RPCClient) Close(channelID *uint32, _ *struct{}) error { + if rpc == nil { + return nil + } + sshCh := rpc.c.chans.getChannel(*channelID) if sshCh == nil { return errors.New("unknown ssh channel") diff --git a/internal/therealssh/client_test.go b/internal/therealssh/client_test.go index 999abce619..9599311851 100644 --- a/internal/therealssh/client_test.go +++ b/internal/therealssh/client_test.go @@ -5,11 +5,10 @@ import ( "net" "testing" + "github.com/skycoin/dmsg/cipher" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/skycoin/skywire/pkg/cipher" - "github.com/skycoin/skywire/pkg/app" ) diff --git a/internal/therealssh/server.go b/internal/therealssh/server.go index c8c987fded..550b5cb22a 100644 --- a/internal/therealssh/server.go +++ b/internal/therealssh/server.go @@ -8,8 +8,9 @@ import ( "log" "net" + "github.com/skycoin/dmsg/cipher" + "github.com/skycoin/skywire/pkg/app" - "github.com/skycoin/skywire/pkg/cipher" ) // CommandType represents global protocol messages. @@ -125,7 +126,11 @@ func (s *Server) HandleData(remotePK cipher.PubKey, localID uint32, data []byte) return errors.New("session is not started") } - channel.dataCh <- data + channel.dataChMx.Lock() + if !channel.IsClosed() { + channel.dataCh <- data + } + channel.dataChMx.Unlock() return nil } @@ -172,6 +177,10 @@ func (s *Server) Serve(conn net.Conn) error { // Close closes all opened channels. func (s *Server) Close() error { + if s == nil { + return nil + } + for _, channel := range s.chans.dropAll() { channel.Close() } diff --git a/internal/therealssh/server_test.go b/internal/therealssh/server_test.go index 84955fb372..96ed971691 100644 --- a/internal/therealssh/server_test.go +++ b/internal/therealssh/server_test.go @@ -7,13 +7,11 @@ import ( "os" "testing" + "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/skycoin/src/util/logging" - - "github.com/skycoin/skywire/pkg/cipher" - "github.com/skycoin/skywire/pkg/app" ) diff --git a/internal/therealssh/session.go b/internal/therealssh/session.go index 555beeb0d7..eefc72649e 100644 --- a/internal/therealssh/session.go +++ b/internal/therealssh/session.go @@ -123,5 +123,8 @@ func (s *Session) Read(p []byte) (int, error) { // Close releases PTY resources. func (s *Session) Close() error { + if s == nil { + return nil + } return s.pty.Close() } diff --git a/pkg/app/app.go b/pkg/app/app.go index fcf98130a1..7a3e131528 100644 --- a/pkg/app/app.go +++ b/pkg/app/app.go @@ -91,6 +91,10 @@ func Setup(config *Config) (*App, error) { // Close implements io.Closer for an App. func (app *App) Close() error { + if app == nil { + return nil + } + select { case <-app.doneChan: // already closed default: @@ -166,6 +170,8 @@ func (app *App) handleProto() { } func (app *App) serveConn(addr *LoopAddr, conn io.ReadWriteCloser) { + defer conn.Close() + for { buf := make([]byte, 32*1024) n, err := conn.Read(buf) @@ -179,11 +185,10 @@ func (app *App) serveConn(addr *LoopAddr, conn io.ReadWriteCloser) { } } - if app.conns[*addr] != nil { + app.mu.Lock() + if _, ok := app.conns[*addr]; ok { app.proto.Send(FrameClose, &addr, nil) // nolint: errcheck } - - app.mu.Lock() delete(app.conns, *addr) app.mu.Unlock() } @@ -217,10 +222,6 @@ func (app *App) closeConn(data []byte) error { delete(app.conns, *addr) app.mu.Unlock() - if conn == nil { - return nil - } - return conn.Close() } @@ -251,13 +252,12 @@ func (app *App) confirmLoop(data []byte) error { type appConn struct { net.Conn - rw io.ReadWriteCloser laddr *Addr raddr *Addr } func newAppConn(conn net.Conn, laddr, raddr *Addr) *appConn { - return &appConn{conn, conn, laddr, raddr} + return &appConn{conn, laddr, raddr} } func (conn *appConn) LocalAddr() net.Addr { @@ -267,15 +267,3 @@ func (conn *appConn) LocalAddr() net.Addr { func (conn *appConn) RemoteAddr() net.Addr { return conn.raddr } - -func (conn *appConn) Write(p []byte) (n int, err error) { - return conn.rw.Write(p) -} - -func (conn *appConn) Read(p []byte) (n int, err error) { - return conn.rw.Read(p) -} - -func (conn *appConn) Close() error { - return conn.rw.Close() -} diff --git a/pkg/app/app_test.go b/pkg/app/app_test.go index 56533b8cf8..b22181aeb5 100644 --- a/pkg/app/app_test.go +++ b/pkg/app/app_test.go @@ -10,12 +10,10 @@ import ( "testing" "time" + "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/skycoin/src/util/logging" - - "github.com/skycoin/skywire/pkg/cipher" ) func TestMain(m *testing.M) { diff --git a/pkg/app/conn.go b/pkg/app/conn.go index 4bc7927100..43eb4e5dfa 100644 --- a/pkg/app/conn.go +++ b/pkg/app/conn.go @@ -72,6 +72,10 @@ func (conn *PipeConn) Write(b []byte) (n int, err error) { // Close closes the connection. func (conn *PipeConn) Close() error { + if conn == nil { + return nil + } + inErr := conn.inFile.Close() outErr := conn.outFile.Close() if inErr != nil { diff --git a/pkg/app/packet.go b/pkg/app/packet.go index 573b1f535f..e4d964d9cf 100644 --- a/pkg/app/packet.go +++ b/pkg/app/packet.go @@ -3,7 +3,7 @@ package app import ( "fmt" - "github.com/skycoin/skywire/pkg/cipher" + "github.com/skycoin/dmsg/cipher" ) // Addr implements net.Addr for App connections. diff --git a/pkg/app/packet_test.go b/pkg/app/packet_test.go index 7e7b6f1f8e..0020ae0eea 100644 --- a/pkg/app/packet_test.go +++ b/pkg/app/packet_test.go @@ -3,7 +3,7 @@ package app import ( "fmt" - "github.com/skycoin/skywire/pkg/cipher" + "github.com/skycoin/dmsg/cipher" ) func ExamplePacket() { diff --git a/pkg/app/protocol.go b/pkg/app/protocol.go index 4bba6fa12a..384645b3c8 100644 --- a/pkg/app/protocol.go +++ b/pkg/app/protocol.go @@ -126,6 +126,9 @@ func (p *Protocol) Serve(handleFunc func(Frame, []byte) (interface{}, error)) er // Close closes underlying ReadWriter. func (p *Protocol) Close() error { + if p == nil { + return nil + } p.chans.closeAll() return p.rw.Close() } diff --git a/pkg/cipher/cipher_test.go b/pkg/cipher/cipher_test.go deleted file mode 100644 index a9a97ec740..0000000000 --- a/pkg/cipher/cipher_test.go +++ /dev/null @@ -1,100 +0,0 @@ -package cipher - -import ( - "log" - "os" - "testing" - - "github.com/skycoin/skycoin/src/util/logging" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestMain(m *testing.M) { - loggingLevel, ok := os.LookupEnv("TEST_LOGGING_LEVEL") - if ok { - lvl, err := logging.LevelFromString(loggingLevel) - if err != nil { - log.Fatal(err) - } - logging.SetLevel(lvl) - } else { - logging.Disable() - } - - os.Exit(m.Run()) -} - -func TestPubKeyString(t *testing.T) { - p, _ := GenerateKeyPair() - require.Equal(t, p.Hex(), p.String()) -} - -func TestPubKeyTextMarshaller(t *testing.T) { - p, _ := GenerateKeyPair() - h, err := p.MarshalText() - require.NoError(t, err) - - var p2 PubKey - err = p2.UnmarshalText(h) - require.NoError(t, err) - require.Equal(t, p, p2) -} - -func TestPubKeyBinaryMarshaller(t *testing.T) { - p, _ := GenerateKeyPair() - b, err := p.MarshalBinary() - require.NoError(t, err) - - var p2 PubKey - err = p2.UnmarshalBinary(b) - require.NoError(t, err) - require.Equal(t, p, p2) -} - -func TestSecKeyString(t *testing.T) { - _, s := GenerateKeyPair() - require.Equal(t, s.Hex(), s.String()) -} - -func TestSecKeyTextMarshaller(t *testing.T) { - _, s := GenerateKeyPair() - h, err := s.MarshalText() - require.NoError(t, err) - - var s2 SecKey - err = s2.UnmarshalText(h) - require.NoError(t, err) - require.Equal(t, s, s2) -} - -func TestSecKeyBinaryMarshaller(t *testing.T) { - _, s := GenerateKeyPair() - b, err := s.MarshalBinary() - require.NoError(t, err) - - var s2 SecKey - err = s2.UnmarshalBinary(b) - require.NoError(t, err) - require.Equal(t, s, s2) -} - -func TestSigString(t *testing.T) { - _, sk := GenerateKeyPair() - sig, err := SignPayload([]byte("foo"), sk) - require.NoError(t, err) - assert.Equal(t, sig.Hex(), sig.String()) -} - -func TestSigTextMarshaller(t *testing.T) { - _, sk := GenerateKeyPair() - sig, err := SignPayload([]byte("foo"), sk) - require.NoError(t, err) - h, err := sig.MarshalText() - require.NoError(t, err) - - var sig2 Sig - err = sig2.UnmarshalText(h) - require.NoError(t, err) - assert.Equal(t, sig, sig2) -} diff --git a/pkg/dmsg/client_test.go b/pkg/dmsg/client_test.go deleted file mode 100644 index 46fe121c23..0000000000 --- a/pkg/dmsg/client_test.go +++ /dev/null @@ -1,288 +0,0 @@ -package dmsg - -import ( - "context" - "net" - "testing" - "time" - - "github.com/skycoin/skycoin/src/util/logging" - "github.com/stretchr/testify/assert" - - "github.com/skycoin/skywire/pkg/cipher" -) - -const ( - chanReadThreshold = time.Second * 5 -) - -type transportWithError struct { - tr *Transport - err error -} - -func TestClient(t *testing.T) { - logger := logging.MustGetLogger("dms_client") - - // Runs two ClientConn's and dials a transport from one to another. - // Checks if states change properly and if closing of transport and connections works. - t.Run("Two connections", func(t *testing.T) { - p1, p2 := net.Pipe() - p1, p2 = invertedIDConn{p1}, invertedIDConn{p2} - - var pk1, pk2 cipher.PubKey - err := pk1.Set("024ec47420176680816e0406250e7156465e4531f5b26057c9f6297bb0303558c7") - assert.NoError(t, err) - err = pk2.Set("031b80cd5773143a39d940dc0710b93dcccc262a85108018a7a95ab9af734f8055") - assert.NoError(t, err) - - conn1 := NewClientConn(logger, p1, pk1, pk2) - conn2 := NewClientConn(logger, p2, pk2, pk1) - - ch1 := make(chan *Transport, AcceptBufferSize) - ch2 := make(chan *Transport, AcceptBufferSize) - - ctx := context.TODO() - - go func() { - _ = conn1.Serve(ctx, ch1) // nolint:errcheck - }() - - go func() { - _ = conn2.Serve(ctx, ch2) // nolint:errcheck - }() - - conn1.mx.RLock() - initID := conn1.nextInitID - conn1.mx.RUnlock() - _, ok := conn1.getTp(initID) - assert.False(t, ok) - - tr1, err := conn1.DialTransport(ctx, pk2) - assert.NoError(t, err) - - _, ok = conn1.getTp(initID) - assert.True(t, ok) - conn1.mx.RLock() - newInitID := conn1.nextInitID - conn1.mx.RUnlock() - assert.Equal(t, initID+2, newInitID) - - err = tr1.Close() - assert.NoError(t, err) - - err = conn1.Close() - assert.NoError(t, err) - - err = conn2.Close() - assert.NoError(t, err) - - assert.False(t, isDoneChannelOpen(conn1.done)) - assert.False(t, isDoneChannelOpen(conn2.done)) - assert.False(t, isDoneChannelOpen(tr1.done)) - assert.False(t, isReadChannelOpen(tr1.inCh)) - }) - - // Runs four ClientConn's and dials two transports between them. - // Checks if states change properly and if closing of transports and connections works. - t.Run("Four connections", func(t *testing.T) { - p1, p2 := net.Pipe() - p1, p2 = invertedIDConn{p1}, invertedIDConn{p2} - - p3, p4 := net.Pipe() - p3, p4 = invertedIDConn{p3}, invertedIDConn{p4} - - var pk1, pk2, pk3 cipher.PubKey - err := pk1.Set("024ec47420176680816e0406250e7156465e4531f5b26057c9f6297bb0303558c7") - assert.NoError(t, err) - err = pk2.Set("031b80cd5773143a39d940dc0710b93dcccc262a85108018a7a95ab9af734f8055") - assert.NoError(t, err) - err = pk3.Set("035b57eef30b9a6be1effc2c3337a3a1ffedcd04ffbac6667cd822892cf56be24a") - assert.NoError(t, err) - - conn1 := NewClientConn(logger, p1, pk1, pk2) - conn2 := NewClientConn(logger, p2, pk2, pk1) - conn3 := NewClientConn(logger, p3, pk2, pk3) - conn4 := NewClientConn(logger, p4, pk3, pk2) - - conn2.setNextInitID(randID(false)) - conn4.setNextInitID(randID(false)) - - ch1 := make(chan *Transport, AcceptBufferSize) - ch2 := make(chan *Transport, AcceptBufferSize) - ch3 := make(chan *Transport, AcceptBufferSize) - ch4 := make(chan *Transport, AcceptBufferSize) - - ctx := context.TODO() - - go func() { - _ = conn1.Serve(ctx, ch1) // nolint:errcheck - }() - - go func() { - _ = conn2.Serve(ctx, ch2) // nolint:errcheck - }() - - go func() { - _ = conn3.Serve(ctx, ch3) // nolint:errcheck - }() - - go func() { - _ = conn4.Serve(ctx, ch4) // nolint:errcheck - }() - - conn1.mx.RLock() - initID1 := conn1.nextInitID - conn1.mx.RUnlock() - - _, ok := conn1.getTp(initID1) - assert.False(t, ok) - - conn2.mx.RLock() - initID2 := conn2.nextInitID - conn2.mx.RUnlock() - - _, ok = conn2.getTp(initID2) - assert.False(t, ok) - - conn3.mx.RLock() - initID3 := conn3.nextInitID - conn3.mx.RUnlock() - - _, ok = conn3.getTp(initID3) - assert.False(t, ok) - - conn4.mx.RLock() - initID4 := conn4.nextInitID - conn4.mx.RUnlock() - - _, ok = conn4.getTp(initID4) - assert.False(t, ok) - - trCh1 := make(chan transportWithError) - trCh2 := make(chan transportWithError) - - go func() { - tr, err := conn1.DialTransport(ctx, pk2) - trCh1 <- transportWithError{ - tr: tr, - err: err, - } - }() - - go func() { - tr, err := conn3.DialTransport(ctx, pk3) - trCh2 <- transportWithError{ - tr: tr, - err: err, - } - }() - - twe1 := <-trCh1 - twe2 := <-trCh2 - - tr1, err := twe1.tr, twe1.err - assert.NoError(t, err) - - _, ok = conn1.getTp(initID1) - assert.True(t, ok) - conn1.mx.RLock() - newInitID1 := conn1.nextInitID - conn1.mx.RUnlock() - assert.Equal(t, initID1+2, newInitID1) - - tr2, err := twe2.tr, twe2.err - assert.NoError(t, err) - - _, ok = conn3.getTp(initID3) - assert.True(t, ok) - conn3.mx.RLock() - newInitID3 := conn3.nextInitID - conn3.mx.RUnlock() - assert.Equal(t, initID3+2, newInitID3) - - errCh1 := make(chan error) - errCh2 := make(chan error) - errCh3 := make(chan error) - errCh4 := make(chan error) - - go func() { - errCh1 <- tr1.Close() - }() - - go func() { - errCh2 <- tr2.Close() - }() - - err = <-errCh1 - assert.NoError(t, err) - - err = <-errCh2 - assert.NoError(t, err) - - go func() { - errCh1 <- conn1.Close() - }() - - go func() { - errCh2 <- conn2.Close() - }() - - go func() { - errCh3 <- conn3.Close() - }() - - go func() { - errCh4 <- conn4.Close() - }() - - err = <-errCh1 - assert.NoError(t, err) - - err = <-errCh2 - assert.NoError(t, err) - - err = <-errCh3 - assert.NoError(t, err) - - err = <-errCh4 - assert.NoError(t, err) - - assert.False(t, isDoneChannelOpen(conn1.done)) - assert.False(t, isDoneChannelOpen(conn3.done)) - assert.False(t, isDoneChannelOpen(tr1.done)) - assert.False(t, isReadChannelOpen(tr1.inCh)) - assert.False(t, isDoneChannelOpen(tr2.done)) - assert.False(t, isReadChannelOpen(tr2.inCh)) - }) -} - -func isDoneChannelOpen(ch chan struct{}) bool { - select { - case _, ok := <-ch: - return ok - case <-time.After(chanReadThreshold): - return false - } -} - -func isReadChannelOpen(ch chan Frame) bool { - select { - case _, ok := <-ch: - return ok - case <-time.After(chanReadThreshold): - return false - } -} - -// used so that we can get two 'ClientConn's directly communicating with one another. -type invertedIDConn struct { - net.Conn -} - -// Write ensures odd IDs turn even, and even IDs turn odd on write. -func (c invertedIDConn) Write(b []byte) (n int, err error) { - frame := Frame(b) - newFrame := MakeFrame(frame.Type(), frame.TpID()^1, frame.Pay()) - return c.Conn.Write(newFrame) -} diff --git a/pkg/dmsg/frame_test.go b/pkg/dmsg/frame_test.go deleted file mode 100644 index 953abed576..0000000000 --- a/pkg/dmsg/frame_test.go +++ /dev/null @@ -1,559 +0,0 @@ -package dmsg - -import ( - "bytes" - "encoding/hex" - "io" - "testing" - - "github.com/stretchr/testify/assert" - - "github.com/skycoin/skywire/internal/ioutil" - "github.com/skycoin/skywire/pkg/cipher" -) - -func Test_isInitiatorID(t *testing.T) { - type args struct { - tpID uint16 - } - - cases := []struct { - name string - args args - want bool - }{ - { - name: "Initiator ID", - args: args{tpID: 2}, - want: true, - }, - { - name: "Not initiator ID", - args: args{tpID: 1}, - want: false, - }, - } - - for _, tc := range cases { - t.Run(tc.name, func(t *testing.T) { - got := isInitiatorID(tc.args.tpID) - assert.Equal(t, tc.want, got) - }) - } -} - -func Test_randID(t *testing.T) { - type args struct { - initiator bool - } - - cases := []struct { - name string - args args - isEven bool - }{ - { - name: "Even number", - args: args{initiator: true}, - isEven: true, - }, - { - name: "Odd number", - args: args{initiator: false}, - isEven: false, - }, - } - - for _, tc := range cases { - t.Run(tc.name, func(t *testing.T) { - got := randID(tc.args.initiator) - isEven := got%2 == 0 - - assert.Equal(t, tc.isEven, isEven) - }) - } -} - -func TestFrameType_String(t *testing.T) { - cases := []struct { - name string - ft FrameType - want string - }{ - { - name: "Request type", - ft: RequestType, - want: "REQUEST", - }, - { - name: "Accept type", - ft: AcceptType, - want: "ACCEPT", - }, - { - name: "Close type", - ft: CloseType, - want: "CLOSE", - }, - { - name: "Fwd type", - ft: FwdType, - want: "FWD", - }, - { - name: "Ack type", - ft: AckType, - want: "ACK", - }, - { - name: "Ok type", - ft: OkType, - want: "OK", - }, - { - name: "Unknown type", - ft: 255, - want: "UNKNOWN:255", - }, - } - - for _, tc := range cases { - t.Run(tc.name, func(t *testing.T) { - got := tc.ft.String() - assert.Equal(t, tc.want, got) - }) - } -} - -func TestMakeFrame(t *testing.T) { - type args struct { - ft FrameType - chID uint16 - pay []byte - } - - cases := []struct { - name string - args args - want Frame - }{ - { - name: "Example 1", - args: args{ - ft: RequestType, - chID: 2, - pay: []byte{0x03, 0x04, 0x05}, - }, - want: Frame{0x01, 0x00, 0x02, 0x00, 0x03, 0x03, 0x04, 0x05}, - }, - { - name: "Example 2", - args: args{ - ft: 0xFF, - chID: 0xABCD, - pay: []byte{0x10, 0x20, 0x30}, - }, - want: Frame{0xFF, 0xAB, 0xCD, 0x00, 0x03, 0x10, 0x20, 0x30}, - }, - { - name: "Payload length > 65535 is not supported", - args: args{ - ft: 0xAB, - chID: 0xCDEF, - pay: make([]byte, 65536), - }, - want: append([]byte{0xAB, 0xCD, 0xEF, 0, 0x00}, make([]byte, 65536)...), - }, - } - - for _, tc := range cases { - t.Run(tc.name, func(t *testing.T) { - got := MakeFrame(tc.args.ft, tc.args.chID, tc.args.pay) - assert.Equal(t, tc.want, got) - }) - } -} - -func TestFrame_Type(t *testing.T) { - cases := []struct { - name string - f Frame - want FrameType - }{ - { - name: "Request type", - f: Frame{1}, - want: RequestType, - }, - { - name: "Accept type", - f: Frame{2}, - want: AcceptType, - }, - { - name: "Close type", - f: Frame{3}, - want: CloseType, - }, - { - name: "Fwd type", - f: Frame{10}, - want: FwdType, - }, - { - name: "Ack type", - f: Frame{11}, - want: AckType, - }, - { - name: "Unknown type", - f: Frame{255}, - want: FrameType(255), - }, - } - - for _, tc := range cases { - t.Run(tc.name, func(t *testing.T) { - got := tc.f.Type() - assert.Equal(t, tc.want, got) - }) - } -} - -func TestFrame_TpID(t *testing.T) { - cases := []struct { - name string - f Frame - want uint16 - }{ - { - name: "Example 1", - f: Frame{0, 0x00, 0x01}, - want: 0x01, - }, - { - name: "Example 2", - f: Frame{0, 0xAB, 0xCD}, - want: 0xABCD, - }, - } - - for _, tc := range cases { - t.Run(tc.name, func(t *testing.T) { - got := tc.f.TpID() - assert.Equal(t, tc.want, got) - }) - } -} - -func TestFrame_PayLen(t *testing.T) { - cases := []struct { - name string - f Frame - want int - }{ - { - name: "Example 1", - f: Frame{0, 0x00, 0x00, 0x00, 0x01}, - want: 0x01, - }, - { - name: "Example 2", - f: Frame{0, 0x00, 0x00, 0xAB, 0xCD}, - want: 0xABCD, - }, - } - - for _, tc := range cases { - t.Run(tc.name, func(t *testing.T) { - got := tc.f.PayLen() - assert.Equal(t, tc.want, got) - }) - } -} - -func TestFrame_Pay(t *testing.T) { - cases := []struct { - name string - f Frame - want []byte - }{ - { - name: "Empty payload", - f: Frame{0, 0x00, 0x00, 0x00, 0x00}, - want: []byte{}, - }, - { - name: "Two-byte payload", - f: Frame{0, 0x00, 0x00, 0x00, 0x01, 0xAB, 0xCD}, - want: []byte{0xAB, 0xCD}, - }, - } - - for _, tc := range cases { - t.Run(tc.name, func(t *testing.T) { - got := tc.f.Pay() - assert.Equal(t, tc.want, got) - }) - } -} - -func TestFrame_Disassemble(t *testing.T) { - cases := []struct { - name string - f Frame - wantFT FrameType - wantID uint16 - wantP []byte - }{ - { - name: "Example 1", - f: Frame{0x01, 0x00, 0x02, 0x00, 0x03, 0x03, 0x04, 0x05}, - wantFT: RequestType, - wantID: 2, - wantP: []byte{0x03, 0x04, 0x05}, - }, - { - name: "Example 2", - f: Frame{0xFF, 0xAB, 0xCD, 0x00, 0x03, 0x10, 0x20, 0x30}, - wantFT: 0xFF, - wantID: 0xABCD, - wantP: []byte{0x10, 0x20, 0x30}, - }, - } - - for _, tc := range cases { - t.Run(tc.name, func(t *testing.T) { - gotFT, gotID, gotP := tc.f.Disassemble() - - assert.Equal(t, tc.wantFT, gotFT) - assert.Equal(t, tc.wantID, gotID) - assert.Equal(t, tc.wantP, gotP) - }) - } -} - -func Test_readFrame(t *testing.T) { - type args struct { - r io.Reader - } - - cases := []struct { - name string - args args - want Frame - wantErr error - }{ - { - name: "Payload length equals to required", - args: args{r: bytes.NewReader([]byte{0x01, 0x00, 0x02, 0x00, 0x03, 0x03, 0x04, 0x05})}, - want: Frame{0x01, 0x00, 0x02, 0x00, 0x03, 0x03, 0x04, 0x05}, - wantErr: nil, - }, - { - name: "Payload longer than required", - args: args{r: bytes.NewReader(append([]byte{0x01, 0x00, 0x02, 0x00, 0x03, 0x03, 0x04, 0x05}, make([]byte, 10)...))}, - want: Frame{0x01, 0x00, 0x02, 0x00, 0x03, 0x03, 0x04, 0x05}, - wantErr: nil, - }, - { - name: "Payload shorter than required", - args: args{r: bytes.NewReader(append([]byte{0x01, 0x00, 0x02, 0x00, 0x03}))}, - want: Frame{0x01, 0x00, 0x02, 0x00, 0x03, 0x00, 0x00, 0x00}, - wantErr: io.EOF, - }, - } - - for _, tc := range cases { - t.Run(tc.name, func(t *testing.T) { - got, err := readFrame(tc.args.r) - - assert.Equal(t, tc.wantErr, err) - assert.Equal(t, tc.want, got) - }) - } -} - -func Test_writeFrame(t *testing.T) { - type args struct { - f Frame - } - - cases := []struct { - name string - args args - want []byte - wantErr error - }{ - { - name: "Example 1", - args: args{f: Frame{0xFF, 0xAB, 0xCD, 0x00, 0x03, 0x10, 0x20, 0x30}}, - want: []byte{0xFF, 0xAB, 0xCD, 0x00, 0x03, 0x10, 0x20, 0x30}, - wantErr: nil, - }, - } - - for _, tc := range cases { - t.Run(tc.name, func(t *testing.T) { - w := &bytes.Buffer{} - - err := writeFrame(w, tc.args.f) - assert.Equal(t, tc.wantErr, err) - - got := w.Bytes() - assert.Equal(t, tc.want, got) - }) - } -} - -func Test_writeCloseFrame(t *testing.T) { - type args struct { - id uint16 - reason byte - } - - cases := []struct { - name string - args args - want []byte - wantErr error - }{ - { - name: "Example 1", - args: args{ - id: 0xABCD, - reason: 0xEF, - }, - want: []byte{0x03, 0xAB, 0xCD, 0x00, 0x01, 0xEF}, - wantErr: nil, - }, - } - - for _, tc := range cases { - t.Run(tc.name, func(t *testing.T) { - w := &bytes.Buffer{} - - err := writeCloseFrame(w, tc.args.id, tc.args.reason) - assert.Equal(t, tc.wantErr, err) - - got := w.Bytes() - assert.Equal(t, tc.want, got) - }) - } -} - -func Test_writeFwdFrame(t *testing.T) { - type args struct { - id uint16 - seq ioutil.Uint16Seq - p []byte - } - - cases := []struct { - name string - args args - want []byte - wantErr error - }{ - { - name: "Example 1", - args: args{ - id: 0xABCD, - seq: 0xEF01, - p: []byte{0x23, 0x45, 0x67}, - }, - want: []byte{0x0A, 0xAB, 0xCD, 0x00, 0x05, 0xEF, 0x01, 0x23, 0x45, 0x67}, - wantErr: nil, - }, - } - - for _, tc := range cases { - t.Run(tc.name, func(t *testing.T) { - w := &bytes.Buffer{} - - err := writeFwdFrame(w, tc.args.id, tc.args.seq, tc.args.p) - assert.Equal(t, tc.wantErr, err) - - got := w.Bytes() - assert.Equal(t, tc.want, got) - }) - } -} - -func Test_combinePKs(t *testing.T) { - type args struct { - initPK string - respPK string - } - - cases := []struct { - name string - args args - want string - }{ - { - name: "Example 1", - args: args{ - initPK: "024ec47420176680816e0406250e7156465e4531f5b26057c9f6297bb0303558c7", - respPK: "031b80cd5773143a39d940dc0710b93dcccc262a85108018a7a95ab9af734f8055", - }, - want: "024ec47420176680816e0406250e7156465e4531f5b26057c9f6297bb0303558c7031b80cd5773143a39d940dc0710b93dcccc262a85108018a7a95ab9af734f8055", - }, - } - - for _, tc := range cases { - t.Run(tc.name, func(t *testing.T) { - var initPK, respPK cipher.PubKey - - err := initPK.Set(tc.args.initPK) - assert.NoError(t, err) - - err = respPK.Set(tc.args.respPK) - assert.NoError(t, err) - - got := combinePKs(initPK, respPK) - assert.Equal(t, tc.want, hex.EncodeToString(got)) - }) - } -} - -func Test_splitPKs(t *testing.T) { - type args struct { - s string - } - - cases := []struct { - name string - args args - wantInitPK string - wantRespPK string - wantOk bool - }{ - { - name: "OK", - args: args{s: "024ec47420176680816e0406250e7156465e4531f5b26057c9f6297bb0303558c7031b80cd5773143a39d940dc0710b93dcccc262a85108018a7a95ab9af734f8055"}, - wantInitPK: "024ec47420176680816e0406250e7156465e4531f5b26057c9f6297bb0303558c7", - wantRespPK: "031b80cd5773143a39d940dc0710b93dcccc262a85108018a7a95ab9af734f8055", - wantOk: true, - }, - { - name: "Not OK", - args: args{s: "024ec47420176680816e0406250e7156465e4531f5b26057c9f6297bb0303558c7"}, - wantInitPK: "000000000000000000000000000000000000000000000000000000000000000000", - wantRespPK: "000000000000000000000000000000000000000000000000000000000000000000", - wantOk: false, - }, - } - - for _, tc := range cases { - t.Run(tc.name, func(t *testing.T) { - pks, err := hex.DecodeString(tc.args.s) - assert.NoError(t, err) - - gotInitPK, gotRespPK, gotOk := splitPKs(pks) - assert.Equal(t, tc.wantOk, gotOk) - assert.Equal(t, tc.wantInitPK, gotInitPK.Hex()) - assert.Equal(t, tc.wantRespPK, gotRespPK.Hex()) - }) - } -} diff --git a/pkg/dmsg/server_test.go b/pkg/dmsg/server_test.go deleted file mode 100644 index cf270d1060..0000000000 --- a/pkg/dmsg/server_test.go +++ /dev/null @@ -1,1137 +0,0 @@ -package dmsg - -import ( - "context" - "errors" - "fmt" - "io" - "log" - "math" - "math/rand" - "net" - "os" - "sync" - "testing" - "time" - - "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/internal/noise" - "github.com/skycoin/skywire/pkg/cipher" - "github.com/skycoin/skywire/pkg/messaging-discovery/client" - "github.com/skycoin/skywire/pkg/transport" -) - -func TestMain(m *testing.M) { - loggingLevel, ok := os.LookupEnv("TEST_LOGGING_LEVEL") - if ok { - lvl, err := logging.LevelFromString(loggingLevel) - if err != nil { - log.Fatal(err) - } - logging.SetLevel(lvl) - } else { - logging.Disable() - } - - os.Exit(m.Run()) -} - -// TestServerConn_AddNext ensures that `nextConns` for the remote client is being filled correctly. -func TestServerConn_AddNext(t *testing.T) { - type want struct { - id uint16 - wantErr bool - } - - pk, _ := cipher.GenerateKeyPair() - - var fullNextConns [math.MaxUint16 + 1]*NextConn - fullNextConns[1] = &NextConn{} - for i := uint16(3); i != 1; i += 2 { - fullNextConns[i] = &NextConn{} - } - - timeoutCtx, cancel := context.WithTimeout(context.Background(), 10*time.Millisecond) - defer cancel() - - cases := []struct { - name string - conn *ServerConn - ctx context.Context - want want - }{ - { - name: "ok", - conn: &ServerConn{ - remoteClient: pk, - log: logging.MustGetLogger("ServerConn"), - nextRespID: 1, - }, - ctx: context.Background(), - want: want{ - id: 1, - }, - }, - { - name: "ok, skip 1", - conn: &ServerConn{ - remoteClient: pk, - log: logging.MustGetLogger("ServerConn"), - nextRespID: 1, - nextConns: [math.MaxUint16 + 1]*NextConn{ - 1: {}, - }, - }, - ctx: context.Background(), - want: want{ - id: 3, - }, - }, - { - name: "fail - timed out", - conn: &ServerConn{ - remoteClient: pk, - log: logging.MustGetLogger("ServerConn"), - nextRespID: 1, - nextConns: fullNextConns, - }, - ctx: timeoutCtx, - want: want{ - wantErr: true, - }, - }, - } - - for _, tc := range cases { - t.Run(tc.name, func(t *testing.T) { - id, err := tc.conn.addNext(tc.ctx, &NextConn{}) - - if tc.want.wantErr { - require.Error(t, err) - } else { - require.NoError(t, err) - } - if err != nil { - return - } - - require.Equal(t, tc.want.id, id) - }) - } - - // concurrent - connsCount := 50 - - serverConn := &ServerConn{ - log: logging.MustGetLogger("ServerConn"), - remoteClient: pk, - nextRespID: 1, - } - - var wg sync.WaitGroup - wg.Add(connsCount) - for i := 0; i < connsCount; i++ { - go func() { - _, err := serverConn.addNext(context.Background(), &NextConn{}) - require.NoError(t, err) - - wg.Done() - }() - } - - wg.Wait() - - for i := uint16(1); i < uint16(connsCount*2); i += 2 { - _, ok := serverConn.getNext(i) - require.Equal(t, true, ok) - } - - for i := uint16(connsCount*2 + 1); i != 1; i += 2 { - _, ok := serverConn.getNext(i) - require.Equal(t, false, ok) - } -} - -// TestNewServer ensures Server starts and quits with no error. -func TestNewServer(t *testing.T) { - sPK, sSK := cipher.GenerateKeyPair() - dc := client.NewMock() - - l, err := net.Listen("tcp", "") - require.NoError(t, err) - - // When calling 'NewServer', if the provided net.Listener is already a noise.Listener, - // An error should be returned. - t.Run("fail_on_wrapped_listener", func(t *testing.T) { - wrappedL := noise.WrapListener(l, sPK, sSK, false, noise.HandshakeXK) - s, err := NewServer(sPK, sSK, "", wrappedL, dc) - assert.Equal(t, ErrListenerAlreadyWrappedToNoise, err) - assert.Nil(t, s) - }) - - s, err := NewServer(sPK, sSK, "", l, dc) - require.NoError(t, err) - - go s.Serve() //nolint:errcheck - - time.Sleep(time.Second) - - assert.NoError(t, s.Close()) -} - -// TestServer_Serve ensures that Server processes request frames and -// instantiates transports properly. -func TestServer_Serve(t *testing.T) { - sPK, sSK := cipher.GenerateKeyPair() - dc := client.NewMock() - - l, err := nettest.NewLocalListener("tcp") - require.NoError(t, err) - - s, err := NewServer(sPK, sSK, "", l, dc) - require.NoError(t, err) - - go s.Serve() //nolint:errcheck - - // connect two clients, establish transport, check if there are - // two ServerConn's and that both conn's `nextConn` is filled correctly - t.Run("test transport establishment", func(t *testing.T) { - aPK, aSK := cipher.GenerateKeyPair() - bPK, bSK := cipher.GenerateKeyPair() - - a := NewClient(aPK, aSK, dc, SetLogger(logging.MustGetLogger("A"))) - err := a.InitiateServerConnections(context.Background(), 1) - require.NoError(t, err) - - b := NewClient(bPK, bSK, dc, SetLogger(logging.MustGetLogger("B"))) - err = b.InitiateServerConnections(context.Background(), 1) - require.NoError(t, err) - - bTransport, err := b.Dial(context.Background(), aPK) - require.NoError(t, err) - - aTransport, err := a.Accept(context.Background()) - require.NoError(t, err) - - // must be 2 ServerConn's - require.Equal(t, 2, s.connCount()) - - // must have ServerConn for A - aServerConn, ok := s.getConn(aPK) - require.Equal(t, true, ok) - require.Equal(t, aPK, aServerConn.PK()) - - // must have ServerConn for B - bServerConn, ok := s.getConn(bPK) - require.Equal(t, true, ok) - require.Equal(t, bPK, bServerConn.PK()) - - // must have a ClientConn - aClientConn, ok := a.getConn(sPK) - require.Equal(t, true, ok) - require.Equal(t, sPK, aClientConn.RemotePK()) - - // must have a ClientConn - bClientConn, ok := b.getConn(sPK) - require.Equal(t, true, ok) - require.Equal(t, sPK, bClientConn.RemotePK()) - - // check whether nextConn's contents are as must be - bClientConn.mx.RLock() - nextInitID := bClientConn.nextInitID - bClientConn.mx.RUnlock() - bNextConn, ok := bServerConn.getNext(nextInitID - 2) - require.Equal(t, true, ok) - aServerConn.mx.RLock() - nextRespID := aServerConn.nextRespID - aServerConn.mx.RUnlock() - require.Equal(t, bNextConn.id, nextRespID-2) - - // check whether nextConn's contents are as must be - aServerConn.mx.RLock() - nextRespID = aServerConn.nextRespID - aServerConn.mx.RUnlock() - aNextConn, ok := aServerConn.getNext(nextRespID - 2) - require.Equal(t, true, ok) - bClientConn.mx.RLock() - nextInitID = bClientConn.nextInitID - bClientConn.mx.RUnlock() - require.Equal(t, aNextConn.id, nextInitID-2) - - err = aTransport.Close() - require.NoError(t, err) - - err = bTransport.Close() - require.NoError(t, err) - - err = a.Close() - require.NoError(t, err) - - err = b.Close() - require.NoError(t, err) - - require.NoError(t, testWithTimeout(5*time.Second, func() error { - if s.connCount() != 0 { - return errors.New("s.conns is not empty") - } - - return nil - })) - - require.NoError(t, testWithTimeout(5*time.Second, func() error { - if a.connCount() != 0 { - return errors.New("a.conns is not empty") - } - - return nil - })) - - require.NoError(t, testWithTimeout(5*time.Second, func() error { - if b.connCount() != 0 { - return errors.New("b.conns is not empty") - } - - return nil - })) - }) - - t.Run("test transport establishment concurrently", func(t *testing.T) { - // this way we can control the tests' difficulty - initiatorsCount := 50 - remotesCount := 50 - - rand := rand.New(rand.NewSource(time.Now().UnixNano())) - - // store the number of transports each remote should handle - remotesTpCount := make(map[int]int) - // mapping initiators to remotes. one initiator performs a single connection, - // while remotes may handle from 0 to `initiatorsCount` connections - pickedRemotes := make([]int, 0, initiatorsCount) - for i := 0; i < initiatorsCount; i++ { - // pick random remote, which the initiator will connect to - remote := rand.Intn(remotesCount) - // increment the number of connections picked remote will handle - remotesTpCount[remote] = remotesTpCount[remote] + 1 - // map initiator to picked remote - pickedRemotes = append(pickedRemotes, remote) - } - - initiators := make([]*Client, 0, initiatorsCount) - remotes := make([]*Client, 0, remotesCount) - - // create initiators - for i := 0; i < initiatorsCount; i++ { - pk, sk := cipher.GenerateKeyPair() - - c := NewClient(pk, sk, dc, SetLogger(logging.MustGetLogger(fmt.Sprintf("initiator_%d", i)))) - err := c.InitiateServerConnections(context.Background(), 1) - require.NoError(t, err) - - initiators = append(initiators, c) - } - - // create remotes - for i := 0; i < remotesCount; i++ { - pk, sk := cipher.GenerateKeyPair() - - c := NewClient(pk, sk, dc, SetLogger(logging.MustGetLogger(fmt.Sprintf("remote_%d", i)))) - if _, ok := remotesTpCount[i]; ok { - err := c.InitiateServerConnections(context.Background(), 1) - require.NoError(t, err) - } - remotes = append(remotes, c) - } - - totalRemoteTpsCount := 0 - for _, connectionsCount := range remotesTpCount { - totalRemoteTpsCount += connectionsCount - } - - // channel to listen for `Accept` errors. Any single error must - // fail the test - acceptErrs := make(chan error, totalRemoteTpsCount) - var remotesTpsMX sync.Mutex - remotesTps := make(map[int][]transport.Transport, len(remotesTpCount)) - var remotesWG sync.WaitGroup - remotesWG.Add(totalRemoteTpsCount) - for i := range remotes { - // only run `Accept` in case the remote was picked before - if _, ok := remotesTpCount[i]; ok { - for connect := 0; connect < remotesTpCount[i]; connect++ { - // run remote - go func(remoteInd int) { - var ( - transport transport.Transport - err error - ) - - transport, err = remotes[remoteInd].Accept(context.Background()) - if err != nil { - acceptErrs <- err - } - - // store transport - remotesTpsMX.Lock() - remotesTps[remoteInd] = append(remotesTps[remoteInd], transport) - remotesTpsMX.Unlock() - - remotesWG.Done() - }(i) - } - } - } - - // channel to listen for `Dial` errors. Any single error must - // fail the test - dialErrs := make(chan error, initiatorsCount) - var initiatorsTpsMx sync.Mutex - initiatorsTps := make([]transport.Transport, 0, initiatorsCount) - var initiatorsWG sync.WaitGroup - initiatorsWG.Add(initiatorsCount) - for i := range initiators { - // run initiator - go func(initiatorInd int) { - var ( - transport transport.Transport - err error - ) - - remote := remotes[pickedRemotes[initiatorInd]] - transport, err = initiators[initiatorInd].Dial(context.Background(), remote.pk) - if err != nil { - dialErrs <- err - } - - // store transport - initiatorsTpsMx.Lock() - initiatorsTps = append(initiatorsTps, transport) - initiatorsTpsMx.Unlock() - - initiatorsWG.Done() - }(i) - } - - // wait for initiators - initiatorsWG.Wait() - close(dialErrs) - err = <-dialErrs - // single error should fail test - require.NoError(t, err) - - // wait for remotes - remotesWG.Wait() - close(acceptErrs) - err = <-acceptErrs - // single error should fail test - require.NoError(t, err) - - // check ServerConn's count - require.Equal(t, len(remotesTpCount)+initiatorsCount, s.connCount()) - - for i, initiator := range initiators { - // get and check initiator's ServerConn - initiatorServConn, ok := s.getConn(initiator.pk) - require.Equal(t, true, ok) - require.Equal(t, initiator.pk, initiatorServConn.PK()) - - // get and check initiator's ClientConn - initiatorClientConn, ok := initiator.getConn(sPK) - require.Equal(t, true, ok) - require.Equal(t, sPK, initiatorClientConn.RemotePK()) - - remote := remotes[pickedRemotes[i]] - - // get and check remote's ServerConn - remoteServConn, ok := s.getConn(remote.pk) - require.Equal(t, true, ok) - require.Equal(t, remote.pk, remoteServConn.PK()) - - // get and check remote's ClientConn - remoteClientConn, ok := remote.getConn(sPK) - require.Equal(t, true, ok) - require.Equal(t, sPK, remoteClientConn.RemotePK()) - - // get initiator's nextConn - initiatorClientConn.mx.RLock() - nextInitID := initiatorClientConn.nextInitID - initiatorClientConn.mx.RUnlock() - initiatorNextConn, ok := initiatorServConn.getNext(nextInitID - 2) - require.Equal(t, true, ok) - require.NotNil(t, initiatorNextConn) - } - - // close transports for remotes - for _, tps := range remotesTps { - for _, tp := range tps { - err := tp.Close() - require.NoError(t, err) - } - } - - // close transports for initiators - for _, tp := range initiatorsTps { - err := tp.Close() - require.NoError(t, err) - } - - // close remotes - for _, remote := range remotes { - err := remote.Close() - require.NoError(t, err) - } - - // close initiators - for _, initiator := range initiators { - err := initiator.Close() - require.NoError(t, err) - } - - require.NoError(t, testWithTimeout(10*time.Second, func() error { - if s.connCount() != 0 { - return errors.New("s.conns is not empty") - } - - return nil - })) - - for i, remote := range remotes { - require.NoError(t, testWithTimeout(10*time.Second, func() error { - if remote.connCount() != 0 { - return fmt.Errorf("remotes[%v].conns is not empty", i) - } - - return nil - })) - } - - for i, initiator := range initiators { - require.NoError(t, testWithTimeout(10*time.Second, func() error { - if initiator.connCount() != 0 { - return fmt.Errorf("initiators[%v].conns is not empty", i) - } - - return nil - })) - } - }) - - t.Run("failed_accepts_should_not_result_in_hang", func(t *testing.T) { - // generate keys for both clients - aPK, aSK := cipher.GenerateKeyPair() - bPK, bSK := cipher.GenerateKeyPair() - - // create remote - a := NewClient(aPK, aSK, dc, SetLogger(logging.MustGetLogger("A"))) - err = a.InitiateServerConnections(context.Background(), 1) - require.NoError(t, err) - - // create initiator - b := NewClient(bPK, bSK, dc, SetLogger(logging.MustGetLogger("B"))) - err = b.InitiateServerConnections(context.Background(), 1) - require.NoError(t, err) - - bTransport, err := b.Dial(context.Background(), aPK) - require.NoError(t, err) - - aTransport, err := a.Accept(context.Background()) - require.NoError(t, err) - - readWriteStop := make(chan struct{}) - readWriteDone := make(chan struct{}) - - var readErr, writeErr error - go func() { - // read/write to/from transport until the stop signal arrives - for { - select { - case <-readWriteStop: - close(readWriteDone) - return - default: - msg := []byte("Hello there!") - if _, writeErr = bTransport.Write(msg); writeErr != nil { - close(readWriteDone) - return - } - if _, readErr = aTransport.Read(msg); readErr != nil { - close(readWriteDone) - return - } - } - } - }() - - // continue creating transports until the error occurs - for { - ctx := context.Background() - if _, err = a.Dial(ctx, bPK); err != nil { - break - } - } - // must be error - require.Error(t, err) - - // the same as above, transport is created by another client - for { - ctx := context.Background() - if _, err = b.Dial(ctx, aPK); err != nil { - break - } - } - // must be error - require.Error(t, err) - - // wait more time to ensure that the initially created transport works - time.Sleep(2 * time.Second) - - err = aTransport.Close() - require.NoError(t, err) - - err = bTransport.Close() - require.NoError(t, err) - - // stop reading/writing goroutines - close(readWriteStop) - <-readWriteDone - - // check that the initial transport had been working properly all the time - // if any error, it must be `io.EOF` for reader - if readErr != io.EOF { - require.NoError(t, readErr) - } - // if any error, it must be `io.ErrClosedPipe` for writer - if writeErr != io.ErrClosedPipe { - require.NoError(t, writeErr) - } - - err = a.Close() - require.NoError(t, err) - - err = b.Close() - require.NoError(t, err) - }) - - t.Run("test sent/received message consistency", func(t *testing.T) { - // generate keys for both clients - aPK, aSK := cipher.GenerateKeyPair() - bPK, bSK := cipher.GenerateKeyPair() - - // create remote - a := NewClient(aPK, aSK, dc, SetLogger(logging.MustGetLogger("A"))) - err = a.InitiateServerConnections(context.Background(), 1) - require.NoError(t, err) - - // create initiator - b := NewClient(bPK, bSK, dc, SetLogger(logging.MustGetLogger("B"))) - err = b.InitiateServerConnections(context.Background(), 1) - require.NoError(t, err) - - // create transports - bTransport, err := b.Dial(context.Background(), aPK) - require.NoError(t, err) - - aTransport, err := a.Accept(context.Background()) - require.NoError(t, err) - - msgCount := 100 - for i := 0; i < msgCount; i++ { - msg := "Hello there!" - - // write message of 12 bytes - _, err := bTransport.Write([]byte(msg)) - require.NoError(t, err) - - // create a receiving buffer of 5 bytes - recBuff := make([]byte, 5) - - // read 5 bytes, 7 left - n, err := aTransport.Read(recBuff) - require.NoError(t, err) - require.Equal(t, n, len(recBuff)) - - received := string(recBuff[:n]) - - // read 5 more, 2 left - n, err = aTransport.Read(recBuff) - require.NoError(t, err) - require.Equal(t, n, len(recBuff)) - - received += string(recBuff[:n]) - - // read 2 bytes left - n, err = aTransport.Read(recBuff) - require.NoError(t, err) - require.Equal(t, n, len(msg)-len(recBuff)*2) - - received += string(recBuff[:n]) - - // received string must be equal to the sent one - require.Equal(t, received, msg) - } - - err = bTransport.Close() - require.NoError(t, err) - - err = aTransport.Close() - require.NoError(t, err) - - err = a.Close() - require.NoError(t, err) - - err = b.Close() - require.NoError(t, err) - }) - - t.Run("capped_transport_buffer_should_not_result_in_hang", func(t *testing.T) { - // generate keys for both clients - aPK, aSK := cipher.GenerateKeyPair() - bPK, bSK := cipher.GenerateKeyPair() - - // create remote - a := NewClient(aPK, aSK, dc, SetLogger(logging.MustGetLogger("A"))) - err = a.InitiateServerConnections(context.Background(), 1) - require.NoError(t, err) - - // create initiator - b := NewClient(bPK, bSK, dc, SetLogger(logging.MustGetLogger("B"))) - err = b.InitiateServerConnections(context.Background(), 1) - require.NoError(t, err) - - // create transports - aWrTransport, err := a.Dial(context.Background(), bPK) - require.NoError(t, err) - - _, err = b.Accept(context.Background()) - require.NoError(t, err) - - msg := []byte("Hello there!") - // exact iterations to fill the receiving buffer and hang `Write` - iterationsToDo := tpBufCap/len(msg) + 1 - - // fill the buffer, but no block yet - for i := 0; i < iterationsToDo-1; i++ { - _, err = aWrTransport.Write(msg) - require.NoError(t, err) - } - - // block on `Write` - var blockedWriteErr error - blockedWriteDone := make(chan struct{}) - go func() { - _, blockedWriteErr = aWrTransport.Write(msg) - close(blockedWriteDone) - }() - - // wait till it's definitely blocked - time.Sleep(1 * time.Second) - - // create new transport from `B` to `A` - bWrTransport, err := b.Dial(context.Background(), aPK) - require.NoError(t, err) - - aRdTransport, err := a.Accept(context.Background()) - require.NoError(t, err) - - // try to write/read message via the new transports - for i := 0; i < 100; i++ { - _, err := bWrTransport.Write(msg) - require.NoError(t, err) - - recBuff := make([]byte, len(msg)) - _, err = aRdTransport.Read(recBuff) - require.NoError(t, err) - - require.Equal(t, recBuff, msg) - } - - err = aWrTransport.Close() - require.NoError(t, err) - - <-blockedWriteDone - require.Error(t, blockedWriteErr) - - err = bWrTransport.Close() - require.NoError(t, err) - - err = aRdTransport.Close() - require.NoError(t, err) - - err = a.Close() - require.NoError(t, err) - - err = b.Close() - require.NoError(t, err) - }) - - t.Run("self_dial_should_work", func(t *testing.T) { - // generate keys for the client - aPK, aSK := cipher.GenerateKeyPair() - - // create client - a := NewClient(aPK, aSK, dc, SetLogger(logging.MustGetLogger("A"))) - err = a.InitiateServerConnections(context.Background(), 1) - require.NoError(t, err) - - // self-dial - selfWrTp, err := a.Dial(context.Background(), aPK) - require.NoError(t, err) - - // self-accept - selfRdTp, err := a.Accept(context.Background()) - require.NoError(t, err) - - // try to write/read message to/from self - msgCount := 100 - for i := 0; i < msgCount; i++ { - msg := []byte("Hello there!") - - _, err := selfWrTp.Write(msg) - require.NoError(t, err) - - recBuf := make([]byte, 5) - - _, err = selfRdTp.Read(recBuf) - require.NoError(t, err) - - _, err = selfRdTp.Read(recBuf) - require.NoError(t, err) - - _, err = selfRdTp.Read(recBuf) - require.NoError(t, err) - } - - err = selfRdTp.Close() - require.NoError(t, err) - - err = selfWrTp.Close() - require.NoError(t, err) - - err = a.Close() - require.NoError(t, err) - }) - - t.Run("server_disconnect_should_close_transports", func(t *testing.T) { - // generate keys for server - sPK, sSK := cipher.GenerateKeyPair() - - dc := client.NewMock() - - l, err := nettest.NewLocalListener("tcp") - require.NoError(t, err) - - // create a server separately from other tests, since this one should be closed - s, err := NewServer(sPK, sSK, "", l, dc) - require.NoError(t, err) - - var sStartErr error - sDone := make(chan struct{}) - go func() { - if err := s.Serve(); err != nil { - sStartErr = err - } - - sDone <- struct{}{} - }() - - // generate keys for both clients - aPK, aSK := cipher.GenerateKeyPair() - bPK, bSK := cipher.GenerateKeyPair() - - // create remote - a := NewClient(aPK, aSK, dc, SetLogger(logging.MustGetLogger("A"))) - err = a.InitiateServerConnections(context.Background(), 1) - require.NoError(t, err) - - // create initiator - b := NewClient(bPK, bSK, dc, SetLogger(logging.MustGetLogger("B"))) - err = b.InitiateServerConnections(context.Background(), 1) - require.NoError(t, err) - - bTransport, err := b.Dial(context.Background(), aPK) - require.NoError(t, err) - - aTransport, err := a.Accept(context.Background()) - require.NoError(t, err) - - msgCount := 100 - for i := 0; i < msgCount; i++ { - msg := []byte("Hello there!") - - _, err := bTransport.Write(msg) - require.NoError(t, err) - - recBuff := make([]byte, 5) - - _, err = aTransport.Read(recBuff) - require.NoError(t, err) - - _, err = aTransport.Read(recBuff) - require.NoError(t, err) - - _, err = aTransport.Read(msg) - require.NoError(t, err) - } - - err = s.Close() - require.NoError(t, err) - - <-sDone - // TODO: remove log, uncomment when bug is fixed - log.Printf("SERVE ERR: %v", sStartErr) - //require.NoError(t, sStartErr) - - /*time.Sleep(10 * time.Second) - - tp, ok := bTransport.(*Transport) - require.Equal(t, true, ok) - require.Equal(t, true, tp.IsClosed()) - - tp, ok = aTransport.(*Transport) - require.Equal(t, true, ok) - require.Equal(t, true, tp.IsClosed())*/ - }) - - t.Run("Reconnect to server should succeed", func(t *testing.T) { - t.Run("Same address", func(t *testing.T) { - t.Parallel() - testReconnect(t, false) - }) - - t.Run("Random address", func(t *testing.T) { - t.Parallel() - testReconnect(t, true) - }) - }) -} - -func testReconnect(t *testing.T, randomAddr bool) { - const smallDelay = time.Second * 5 - ctx := context.TODO() - - serverPK, serverSK := cipher.GenerateKeyPair() - dc := client.NewMock() - - l, err := nettest.NewLocalListener("tcp") - require.NoError(t, err) - - s, err := NewServer(serverPK, serverSK, "", l, dc) - require.NoError(t, err) - - serverAddr := s.Addr() - - go s.Serve() // nolint:errcheck - - remotePK, remoteSK := cipher.GenerateKeyPair() - initiatorPK, initiatorSK := cipher.GenerateKeyPair() - - assert.Equal(t, 0, s.connCount()) - - remote := NewClient(remotePK, remoteSK, dc, SetLogger(logging.MustGetLogger("remote"))) - err = remote.InitiateServerConnections(ctx, 1) - require.NoError(t, err) - - require.NoError(t, testWithTimeout(smallDelay, func() error { - if s.connCount() != 1 { - return errors.New("s.conns is not equal to 1") - } - return nil - })) - - initiator := NewClient(initiatorPK, initiatorSK, dc, SetLogger(logging.MustGetLogger("initiator"))) - err = initiator.InitiateServerConnections(ctx, 1) - require.NoError(t, err) - - initiatorTransport, err := initiator.Dial(ctx, remotePK) - require.NoError(t, err) - - remoteTransport, err := remote.Accept(context.Background()) - require.NoError(t, err) - - require.NoError(t, testWithTimeout(smallDelay, func() error { - if s.connCount() != 2 { - return errors.New("s.conns is not equal to 2") - } - return nil - })) - - err = s.Close() - assert.NoError(t, err) - - initTr := initiatorTransport.(*Transport) - assert.False(t, isDoneChannelOpen(initTr.done)) - assert.False(t, isReadChannelOpen(initTr.inCh)) - - remoteTr := remoteTransport.(*Transport) - assert.False(t, isDoneChannelOpen(remoteTr.done)) - assert.False(t, isReadChannelOpen(remoteTr.inCh)) - - assert.Equal(t, 0, s.connCount()) - - addr := "" - if !randomAddr { - addr = serverAddr - } - - l, err = net.Listen("tcp", serverAddr) - require.NoError(t, err) - - s, err = NewServer(serverPK, serverSK, addr, l, dc) - require.NoError(t, err) - - go s.Serve() // nolint:errcheck - - require.NoError(t, testWithTimeout(clientReconnectInterval+smallDelay, func() error { - if s.connCount() != 2 { - return errors.New("s.conns is not equal to 2") - } - return nil - })) - - require.NoError(t, testWithTimeout(smallDelay, func() error { - _, err = initiator.Dial(ctx, remotePK) - if err != nil { - return err - } - - _, err = remote.Accept(context.Background()) - return err - })) - - err = s.Close() - assert.NoError(t, err) -} - -// Given two client instances (a & b) and a server instance (s), -// Client b should be able to dial a transport with client b -// Data should be sent and delivered successfully via the transport. -// TODO: fix this. -func TestNewClient(t *testing.T) { - aPK, aSK := cipher.GenerateKeyPair() - bPK, bSK := cipher.GenerateKeyPair() - sPK, sSK := cipher.GenerateKeyPair() - sAddr := "127.0.0.1:8081" - - const tpCount = 10 - const msgCount = 100 - - dc := client.NewMock() - - l, err := net.Listen("tcp", sAddr) - require.NoError(t, err) - - log.Println(l.Addr().String()) - - s, err := NewServer(sPK, sSK, "", l, dc) - require.NoError(t, err) - - go s.Serve() //nolint:errcheck - - a := NewClient(aPK, aSK, dc, SetLogger(logging.MustGetLogger("A"))) - require.NoError(t, a.InitiateServerConnections(context.Background(), 1)) - - b := NewClient(bPK, bSK, dc, SetLogger(logging.MustGetLogger("B"))) - require.NoError(t, b.InitiateServerConnections(context.Background(), 1)) - - wg := new(sync.WaitGroup) - wg.Add(1) - go func() { - defer wg.Done() - for i := 0; i < tpCount; i++ { - aDone := make(chan struct{}) - var aTp transport.Transport - go func() { - var err error - aTp, err = a.Accept(context.Background()) - catch(err) - close(aDone) - }() - - bTp, err := b.Dial(context.Background(), aPK) - catch(err) - - <-aDone - catch(err) - - for j := 0; j < msgCount; j++ { - pay := []byte(fmt.Sprintf("This is message %d!", j)) - _, err := aTp.Write(pay) - catch(err) - _, err = bTp.Read(pay) - catch(err) - } - - // Close TPs - catch(aTp.Close()) - catch(bTp.Close()) - } - }() - - for i := 0; i < tpCount; i++ { - bDone := make(chan struct{}) - var bErr error - var bTp transport.Transport - go func() { - bTp, bErr = b.Accept(context.Background()) - close(bDone) - }() - - aTp, err := a.Dial(context.Background(), bPK) - require.NoError(t, err) - - <-bDone - require.NoError(t, bErr) - - for j := 0; j < msgCount; j++ { - pay := []byte(fmt.Sprintf("This is message %d!", j)) - - n, err := aTp.Write(pay) - require.NoError(t, err) - require.Equal(t, len(pay), n) - - got := make([]byte, len(pay)) - n, err = bTp.Read(got) - require.Equal(t, len(pay), n) - require.NoError(t, err) - require.Equal(t, pay, got) - } - - // Close TPs - require.NoError(t, aTp.Close()) - require.NoError(t, bTp.Close()) - } - wg.Wait() - - // Close server. - assert.NoError(t, s.Close()) -} - -func catch(err error) { - if err != nil { - panic(err) - } -} - -// intended to test some func of `func() error` signature with a given timeout. -// Exeeding timeout results in error. -func testWithTimeout(timeout time.Duration, run func() error) error { - timer := time.NewTimer(timeout) - defer timer.Stop() - - for { - if err := run(); err != nil { - select { - case <-timer.C: - return err - default: - time.Sleep(time.Millisecond * 5) - continue - } - } - - return nil - } -} diff --git a/pkg/dmsg/transport_test.go b/pkg/dmsg/transport_test.go deleted file mode 100644 index bb32fb23a7..0000000000 --- a/pkg/dmsg/transport_test.go +++ /dev/null @@ -1,43 +0,0 @@ -package dmsg - -import ( - "testing" - - "github.com/skycoin/skycoin/src/util/logging" - "github.com/stretchr/testify/assert" - - "github.com/skycoin/skywire/pkg/cipher" -) - -func TestNewTransport(t *testing.T) { - log := logging.MustGetLogger("dmsg_test") - tr := NewTransport(nil, log, cipher.PubKey{}, cipher.PubKey{}, 0, func(id uint16) {}) - assert.NotNil(t, tr) -} - -func TestTransport_close(t *testing.T) { - log := logging.MustGetLogger("dmsg_test") - tr := NewTransport(nil, log, cipher.PubKey{}, cipher.PubKey{}, 0, func(id uint16) {}) - - closed := tr.close() - - t.Run("Valid close() result (1st attempt)", func(t *testing.T) { - assert.True(t, closed) - }) - - t.Run("Channel closed (1st attempt)", func(t *testing.T) { - _, ok := <-tr.done - assert.False(t, ok) - }) - - closed = tr.close() - - t.Run("Valid close() result (2nd attempt)", func(t *testing.T) { - assert.False(t, closed) - }) - - t.Run("Channel closed (2nd attempt)", func(t *testing.T) { - _, ok := <-tr.done - assert.False(t, ok) - }) -} diff --git a/pkg/hypervisor/config.go b/pkg/hypervisor/config.go index 985e9ea253..f35e6f6a71 100644 --- a/pkg/hypervisor/config.go +++ b/pkg/hypervisor/config.go @@ -8,9 +8,9 @@ import ( "path/filepath" "time" - "github.com/skycoin/skywire/pkg/util/pathutil" + "github.com/skycoin/dmsg/cipher" - "github.com/skycoin/skywire/pkg/cipher" + "github.com/skycoin/skywire/pkg/util/pathutil" ) // Key allows a byte slice to be marshaled or unmarshaled from a hex string. diff --git a/pkg/hypervisor/hypervisor.go b/pkg/hypervisor/hypervisor.go index 2c21e2142f..d79e02f468 100644 --- a/pkg/hypervisor/hypervisor.go +++ b/pkg/hypervisor/hypervisor.go @@ -16,10 +16,10 @@ import ( "github.com/go-chi/chi" "github.com/go-chi/chi/middleware" "github.com/google/uuid" + "github.com/skycoin/dmsg/cipher" + "github.com/skycoin/dmsg/noise" "github.com/skycoin/skycoin/src/util/logging" - "github.com/skycoin/skywire/internal/noise" - "github.com/skycoin/skywire/pkg/cipher" "github.com/skycoin/skywire/pkg/httputil" "github.com/skycoin/skywire/pkg/routing" "github.com/skycoin/skywire/pkg/visor" diff --git a/pkg/hypervisor/user.go b/pkg/hypervisor/user.go index 394349d657..7dd0bfe765 100644 --- a/pkg/hypervisor/user.go +++ b/pkg/hypervisor/user.go @@ -8,9 +8,8 @@ import ( "regexp" "time" + "github.com/skycoin/dmsg/cipher" "go.etcd.io/bbolt" - - "github.com/skycoin/skywire/pkg/cipher" ) const ( diff --git a/pkg/messaging-discovery/client/client_available_servers_integration_test.go b/pkg/messaging-discovery/client/client_available_servers_integration_test.go deleted file mode 100644 index e75e4545d5..0000000000 --- a/pkg/messaging-discovery/client/client_available_servers_integration_test.go +++ /dev/null @@ -1,100 +0,0 @@ -// +build integration - -package client_test - -import ( - "context" - "fmt" - "net" - "sort" - "testing" - - "github.com/stretchr/testify/assert" - - "github.com/skycoin/skywire/pkg/cipher" - "github.com/skycoin/skywire/pkg/messaging-discovery/api" - "github.com/skycoin/skywire/pkg/messaging-discovery/client" - "github.com/skycoin/skywire/pkg/messaging-discovery/store" -) - -func TestGetAvailableServers(t *testing.T) { - var apiServerAddress string - var mockStore store.Storer - - mockStore = store.NewStore("mock", "") - apiServer := api.New(mockStore, 5) - - // get a free port - listener, err := net.Listen("tcp", ":0") - if err != nil { - panic(err) - } - apiServerAddress = fmt.Sprintf("http://localhost:%d", listener.Addr().(*net.TCPAddr).Port) - - go func() { - apiServer.Start(listener) - }() - - pk, sk := cipher.GenerateKeyPair() - serverStaticPk, _ := cipher.GenerateKeyPair() - ephemeralPk1, ephemeralSk1 := cipher.GenerateKeyPair() - ephemeralPk2, ephemeralSk2 := cipher.GenerateKeyPair() - baseEntry := newTestEntry(pk, serverStaticPk, ephemeralPk1, ephemeralPk2) - - cases := []struct { - name string - databaseAndEntriesPrehook func(store.Storer, *[]*client.Entry) - responseIsError bool - errorMessage client.HTTPMessage - }{ - { - name: "get 3 server entries", - responseIsError: false, - databaseAndEntriesPrehook: func(db store.Storer, entries *[]*client.Entry) { - var entry1, entry2, entry3 client.Entry - client.Copy(&entry1, &baseEntry) - client.Copy(&entry2, &baseEntry) - client.Copy(&entry3, &baseEntry) - - entry1.Sign(sk) - entry2.Keys.Static = ephemeralPk1 - entry2.Sign(ephemeralSk1) - entry3.Keys.Static = ephemeralPk2 - entry3.Sign(ephemeralSk2) - - db.SetEntry(context.Background(), &entry1) - db.SetEntry(context.Background(), &entry2) - db.SetEntry(context.Background(), &entry3) - - *entries = append(*entries, &entry1, &entry2, &entry3) - }, - }, - { - name: "get no entries", - responseIsError: false, - }, - } - - for _, tc := range cases { - t.Run(tc.name, func(t *testing.T) { - clientServer := client.New(apiServerAddress) - mockStore.(*store.MockStore).Clear() - expectedEntries := []*client.Entry{} - - if tc.databaseAndEntriesPrehook != nil { - tc.databaseAndEntriesPrehook(mockStore, &expectedEntries) - } - - entries, err := clientServer.AvailableServers(context.TODO()) - - if !tc.responseIsError { - assert.NoError(t, err) - sort.Slice(expectedEntries, func(i, j int) bool { return expectedEntries[i].Keys.Static > expectedEntries[j].Keys.Static }) - sort.Slice(entries, func(i, j int) bool { return entries[i].Keys.Static > entries[j].Keys.Static }) - assert.EqualValues(t, expectedEntries, entries) - } else { - assert.Equal(t, tc.errorMessage.String(), err.Error()) - } - }) - } -} diff --git a/pkg/messaging-discovery/client/client_clear_entry_integration_test.go b/pkg/messaging-discovery/client/client_clear_entry_integration_test.go deleted file mode 100644 index b45652fac9..0000000000 --- a/pkg/messaging-discovery/client/client_clear_entry_integration_test.go +++ /dev/null @@ -1,109 +0,0 @@ -// +build integration - -package client_test - -import ( - "context" - "fmt" - "net" - "net/http" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/skycoin/skywire/pkg/cipher" - - "github.com/skycoin/skywire/pkg/messaging-discovery/api" - "github.com/skycoin/skywire/pkg/messaging-discovery/client" - "github.com/skycoin/skywire/pkg/messaging-discovery/store" -) - -func TestClearEntriesEndpoint(t *testing.T) { - var apiServerAddress string - var mockStore store.Storer - - mockStore = store.NewStore("mock", "") - apiServer := api.New(mockStore, 5) - - // get a free port - listener, err := net.Listen("tcp", ":0") - if err != nil { - panic(err) - } - apiServerAddress = fmt.Sprintf("http://localhost:%d", listener.Addr().(*net.TCPAddr).Port) - - go func() { - apiServer.Start(listener) - }() - - pk, sk := cipher.GenerateKeyPair() - serverStaticPk, _ := cipher.GenerateKeyPair() - ephemeralPk1, ephemeralSk1 := cipher.GenerateKeyPair() - ephemeralPk2, _ := cipher.GenerateKeyPair() - baseEntry := newTestEntry(pk, serverStaticPk, ephemeralPk1, ephemeralPk2) - baseEntry.Sign(sk) - - mockStore.SetEntry(context.TODO(), &baseEntry) - - cases := []struct { - name string - httpResponse client.HTTPMessage - publicKey cipher.PubKey - secretKey cipher.SecKey - responseShouldError bool - entryPreHook func(entry *client.Entry) - storerPreHook func(*testing.T, store.Storer, *client.Entry) - }{ - - { - name: "clear client a client entry should succeed", - responseShouldError: false, - publicKey: pk, - secretKey: sk, - storerPreHook: func(t *testing.T, s store.Storer, e *client.Entry) { - err := s.SetEntry(context.TODO(), e) - require.NoError(t, err) - }, - }, - { - name: "clear an entry of key we don't own should error", - responseShouldError: true, - httpResponse: client.HTTPMessage{Code: http.StatusUnauthorized, Message: client.ErrUnauthorized.Error()}, - publicKey: pk, - secretKey: ephemeralSk1, - storerPreHook: func(t *testing.T, s store.Storer, e *client.Entry) { - err := s.SetEntry(context.TODO(), e) - require.NoError(t, err) - e.Client = nil - e.Keys.Ephemerals = nil - }, - }, - } - - for _, tc := range cases { - t.Run(tc.name, func(t *testing.T) { - clientServer := client.New(apiServerAddress) - entry := baseEntry - - if tc.entryPreHook != nil { - tc.entryPreHook(&entry) - } - - mockStore.(*store.MockStore).Clear() - - if tc.storerPreHook != nil { - tc.storerPreHook(t, mockStore, &entry) - } - - err := clientServer.ClearEntry(context.TODO(), tc.secretKey, tc.publicKey) - - if tc.responseShouldError { - assert.Error(t, err) - assert.Equal(t, tc.httpResponse.String(), err.Error()) - } else { - assert.NoError(t, err) - } - }) - } -} diff --git a/pkg/messaging-discovery/client/client_entry_integration_test.go b/pkg/messaging-discovery/client/client_entry_integration_test.go deleted file mode 100644 index 5bf9134461..0000000000 --- a/pkg/messaging-discovery/client/client_entry_integration_test.go +++ /dev/null @@ -1,99 +0,0 @@ -// +build integration - -package client_test - -import ( - "context" - "fmt" - "net" - "net/http" - "testing" - - "github.com/stretchr/testify/assert" - - "github.com/skycoin/skywire/pkg/cipher" - - "github.com/skycoin/skywire/pkg/messaging-discovery/api" - "github.com/skycoin/skywire/pkg/messaging-discovery/client" - "github.com/skycoin/skywire/pkg/messaging-discovery/store" -) - -func TestEntriesEndpoint(t *testing.T) { - var apiServerAddress string - var mockStore store.Storer - - mockStore = store.NewStore("mock", "") - apiServer := api.New(mockStore, 5) - - // get a free port - listener, err := net.Listen("tcp", ":0") - if err != nil { - panic(err) - } - apiServerAddress = fmt.Sprintf("http://localhost:%d", listener.Addr().(*net.TCPAddr).Port) - - go func() { - apiServer.Start(listener) - }() - - pk, sk := cipher.GenerateKeyPair() - serverStaticPk, _ := cipher.GenerateKeyPair() - ephemeralPk1, _ := cipher.GenerateKeyPair() - ephemeralPk2, _ := cipher.GenerateKeyPair() - baseEntry := newTestEntry(pk, serverStaticPk, ephemeralPk1, ephemeralPk2) - - cases := []struct { - name string - httpResponse client.HTTPMessage - publicKey cipher.PubKey - responseIsEntry bool - entry client.Entry - entryPreHook func(*client.Entry) - storerPreHook func(store.Storer, *client.Entry) - }{ - { - name: "get entry", - publicKey: pk, - responseIsEntry: true, - entry: baseEntry, - entryPreHook: func(e *client.Entry) { - e.Sign(sk) - }, - storerPreHook: func(s store.Storer, e *client.Entry) { - s.SetEntry(context.Background(), e) - }, - }, - { - name: "get not valid entry", - publicKey: pk, - responseIsEntry: false, - httpResponse: client.HTTPMessage{Code: http.StatusNotFound, Message: client.ErrKeyNotFound.Error()}, - entry: baseEntry, - }, - } - - for _, tc := range cases { - t.Run(tc.name, func(t *testing.T) { - clientServer := client.New(apiServerAddress) - - if tc.entryPreHook != nil { - tc.entryPreHook(&tc.entry) - } - - mockStore.(*store.MockStore).Clear() - - if tc.storerPreHook != nil { - tc.storerPreHook(mockStore, &tc.entry) - } - - entry, err := clientServer.Entry(context.TODO(), tc.publicKey) - if tc.responseIsEntry { - assert.NoError(t, err) - assert.Equal(t, &tc.entry, entry) - } else { - assert.Equal(t, tc.httpResponse.String(), err.Error()) - } - - }) - } -} diff --git a/pkg/messaging-discovery/client/client_mock_test.go b/pkg/messaging-discovery/client/client_mock_test.go deleted file mode 100644 index 23a0ee4284..0000000000 --- a/pkg/messaging-discovery/client/client_mock_test.go +++ /dev/null @@ -1,363 +0,0 @@ -package client_test - -import ( - "context" - "fmt" - "net/http" - "testing" - "time" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/skycoin/skywire/pkg/cipher" - - "github.com/skycoin/skywire/pkg/messaging-discovery/client" -) - -func TestNewMockGetAvailableServers(t *testing.T) { - pk, sk := cipher.GenerateKeyPair() - baseEntry := client.Entry{ - Static: pk, - Timestamp: time.Now().Unix(), - Client: &client.Client{}, - Server: &client.Server{ - Address: "localhost:8080", - AvailableConnections: 3, - }, - Version: "0", - Sequence: 1, - } - - cases := []struct { - name string - databaseAndEntriesPrehook func(*testing.T, client.APIClient, *[]*client.Entry) - responseIsError bool - errorMessage client.HTTPMessage - }{ - { - name: "get 3 server entries", - responseIsError: false, - databaseAndEntriesPrehook: func(t *testing.T, mockClient client.APIClient, entries *[]*client.Entry) { - entry1 := baseEntry - entry2 := baseEntry - entry3 := baseEntry - - err := entry1.Sign(sk) - require.NoError(t, err) - err = mockClient.SetEntry(context.TODO(), &entry1) - require.NoError(t, err) - - pk1, sk1 := cipher.GenerateKeyPair() - entry2.Static = pk1 - err = entry2.Sign(sk1) - require.NoError(t, err) - err = mockClient.SetEntry(context.TODO(), &entry2) - require.NoError(t, err) - - pk2, sk2 := cipher.GenerateKeyPair() - entry3.Static = pk2 - err = entry3.Sign(sk2) - require.NoError(t, err) - err = mockClient.SetEntry(context.TODO(), &entry3) - require.NoError(t, err) - - *entries = append(*entries, &entry1, &entry2, &entry3) - }, - }, - { - name: "get no entries", - responseIsError: false, - }, - } - - for _, tc := range cases { - t.Run(tc.name, func(t *testing.T) { - clientServer := client.NewMock() - expectedEntries := []*client.Entry{} - - if tc.databaseAndEntriesPrehook != nil { - tc.databaseAndEntriesPrehook(t, clientServer, &expectedEntries) - } - - entries, err := clientServer.AvailableServers(context.TODO()) - - if !tc.responseIsError { - assert.NoError(t, err) - assert.Equal(t, expectedEntries, entries) - } else { - assert.Equal(t, tc.errorMessage.String(), err.Error()) - } - }) - } -} - -func TestNewMockEntriesEndpoint(t *testing.T) { - pk, sk := cipher.GenerateKeyPair() - baseEntry := newTestEntry(pk) - - cases := []struct { - name string - httpResponse client.HTTPMessage - publicKey cipher.PubKey - responseIsEntry bool - entry client.Entry - entryPreHook func(*testing.T, *client.Entry) - storerPreHook func(*testing.T, client.APIClient, *client.Entry) - }{ - { - name: "get entry", - publicKey: pk, - responseIsEntry: true, - entry: baseEntry, - entryPreHook: func(t *testing.T, e *client.Entry) { - err := e.Sign(sk) - require.NoError(t, err) - }, - storerPreHook: func(t *testing.T, apiClient client.APIClient, e *client.Entry) { - err := apiClient.SetEntry(context.TODO(), e) - require.NoError(t, err) - }, - }, - { - name: "get not valid entry", - publicKey: pk, - responseIsEntry: false, - httpResponse: client.HTTPMessage{Code: http.StatusNotFound, Message: client.ErrKeyNotFound.Error()}, - entry: baseEntry, - }, - } - - for _, tc := range cases { - t.Run(tc.name, func(t *testing.T) { - clientServer := client.NewMock() - - if tc.entryPreHook != nil { - tc.entryPreHook(t, &tc.entry) - } - - if tc.storerPreHook != nil { - tc.storerPreHook(t, clientServer, &tc.entry) - } - - entry, err := clientServer.Entry(context.TODO(), tc.publicKey) - if tc.responseIsEntry { - assert.NoError(t, err) - assert.Equal(t, &tc.entry, entry) - } else { - assert.Equal(t, tc.httpResponse.String(), err.Error()) - } - - }) - } -} - -func TestNewMockSetEntriesEndpoint(t *testing.T) { - pk, sk := cipher.GenerateKeyPair() - _, ephemeralSk1 := cipher.GenerateKeyPair() - baseEntry := newTestEntry(pk) - - cases := []struct { - name string - httpResponse client.HTTPMessage - responseShouldError bool - entryPreHook func(t *testing.T, entry *client.Entry) - storerPreHook func(*testing.T, client.APIClient, *client.Entry) - }{ - - { - name: "set entry right", - responseShouldError: false, - entryPreHook: func(t *testing.T, e *client.Entry) { - err := e.Sign(sk) - require.NoError(t, err) - }, - }, - { - name: "set entry iteration", - responseShouldError: false, - entryPreHook: func(t *testing.T, e *client.Entry) { - err := e.Sign(sk) - require.NoError(t, err) - }, - storerPreHook: func(t *testing.T, s client.APIClient, e *client.Entry) { - var oldEntry client.Entry - client.Copy(&oldEntry, e) - fmt.Println(oldEntry.Static) - oldEntry.Sequence = 0 - err := oldEntry.Sign(sk) - require.NoError(t, err) - err = s.SetEntry(context.TODO(), &oldEntry) - require.NoError(t, err) - e.Sequence = 1 - e.Timestamp += 3 - err = e.Sign(sk) - require.NoError(t, err) - }, - }, - { - name: "set entry iteration wrong sequence", - responseShouldError: true, - httpResponse: client.HTTPMessage{Code: http.StatusUnprocessableEntity, Message: client.ErrValidationWrongSequence.Error()}, - entryPreHook: func(t *testing.T, e *client.Entry) { - err := e.Sign(sk) - require.NoError(t, err) - }, - storerPreHook: func(t *testing.T, s client.APIClient, e *client.Entry) { - e.Sequence = 2 - err := s.SetEntry(context.TODO(), e) - require.NoError(t, err) - }, - }, - { - name: "set entry iteration unauthorized", - responseShouldError: true, - httpResponse: client.HTTPMessage{Code: http.StatusUnauthorized, Message: client.ErrUnauthorized.Error()}, - entryPreHook: func(t *testing.T, e *client.Entry) { - err := e.Sign(sk) - require.NoError(t, err) - }, - storerPreHook: func(t *testing.T, s client.APIClient, e *client.Entry) { - e.Sequence = 0 - err := s.SetEntry(context.TODO(), e) - require.NoError(t, err) - e.Signature = "" - e.Sequence = 1 - e.Timestamp += 3 - err = e.Sign(ephemeralSk1) - require.NoError(t, err) - }, - }, - } - for _, tc := range cases { - t.Run(tc.name, func(t *testing.T) { - clientServer := client.NewMock() - var entry client.Entry - client.Copy(&entry, &baseEntry) - - if tc.entryPreHook != nil { - tc.entryPreHook(t, &entry) - } - - if tc.storerPreHook != nil { - tc.storerPreHook(t, clientServer, &entry) - } - - fmt.Println("key in: ", entry.Static) - err := clientServer.SetEntry(context.TODO(), &entry) - - if tc.responseShouldError { - assert.Error(t, err) - } else { - assert.NoError(t, err) - } - - }) - } -} - -func TestNewMockUpdateEntriesEndpoint(t *testing.T) { - pk, sk := cipher.GenerateKeyPair() - _, ephemeralSk1 := cipher.GenerateKeyPair() - baseEntry := newTestEntry(pk) - err := baseEntry.Sign(sk) - require.NoError(t, err) - - cases := []struct { - name string - secretKey cipher.SecKey - responseShouldError bool - entryPreHook func(entry *client.Entry) - storerPreHook func(client.APIClient, *client.Entry) - }{ - - { - name: "update entry iteration", - responseShouldError: false, - secretKey: sk, - storerPreHook: func(apiClient client.APIClient, e *client.Entry) { - e.Server.Address = "different one" - }, - }, - { - name: "update entry unauthorized", - responseShouldError: true, - secretKey: ephemeralSk1, - storerPreHook: func(apiClient client.APIClient, e *client.Entry) { - e.Server.Address = "different one" - }, - }, - { - name: "udpate retries on wrong sequence", - responseShouldError: false, - secretKey: sk, - entryPreHook: func(entry *client.Entry) { - entry.Sequence = 3 - }, - }, - } - for _, tc := range cases { - t.Run(tc.name, func(t *testing.T) { - clientServer := client.NewMock() - err := clientServer.SetEntry(context.TODO(), &baseEntry) - require.NoError(t, err) - - entry := baseEntry - - if tc.entryPreHook != nil { - tc.entryPreHook(&entry) - } - - if tc.storerPreHook != nil { - tc.storerPreHook(clientServer, &entry) - } - - err = clientServer.UpdateEntry(context.TODO(), tc.secretKey, &entry) - - if tc.responseShouldError { - assert.Error(t, err) - } else { - assert.NoError(t, err) - } - - }) - } -} - -func TestNewMockUpdateEntrySequence(t *testing.T) { - clientServer := client.NewMock() - pk, sk := cipher.GenerateKeyPair() - entry := &client.Entry{ - Sequence: 0, - Static: pk, - } - - err := clientServer.UpdateEntry(context.TODO(), sk, entry) - require.NoError(t, err) - - v1Entry, err := clientServer.Entry(context.TODO(), pk) - require.NoError(t, err) - - err = clientServer.UpdateEntry(context.TODO(), sk, entry) - require.NoError(t, err) - - v2Entry, err := clientServer.Entry(context.TODO(), pk) - require.NoError(t, err) - - assert.NotEqual(t, v1Entry.Sequence, v2Entry.Sequence) -} - -func newTestEntry(pk cipher.PubKey) client.Entry { - baseEntry := client.Entry{ - Static: pk, - Timestamp: time.Now().UnixNano(), - Client: &client.Client{}, - Server: &client.Server{ - Address: "localhost:8080", - AvailableConnections: 3, - }, - Version: "0", - Sequence: 0, - } - return baseEntry -} diff --git a/pkg/messaging-discovery/client/client_set_entry_integration_test.go b/pkg/messaging-discovery/client/client_set_entry_integration_test.go deleted file mode 100644 index 2c3cbbd67d..0000000000 --- a/pkg/messaging-discovery/client/client_set_entry_integration_test.go +++ /dev/null @@ -1,127 +0,0 @@ -// +build integration - -package client_test - -import ( - "context" - "fmt" - "net" - "net/http" - "testing" - - "github.com/stretchr/testify/assert" - - "github.com/skycoin/skywire/pkg/cipher" - "github.com/skycoin/skywire/pkg/messaging-discovery/api" - "github.com/skycoin/skywire/pkg/messaging-discovery/client" - "github.com/skycoin/skywire/pkg/messaging-discovery/store" -) - -func TestSetEntriesEndpoint(t *testing.T) { - var apiServerAddress string - var mockStore store.Storer - - mockStore = store.NewStore("mock", "") - apiServer := api.New(mockStore, 5) - - // get a free port - listener, err := net.Listen("tcp", ":0") - if err != nil { - panic(err) - } - apiServerAddress = fmt.Sprintf("http://localhost:%d", listener.Addr().(*net.TCPAddr).Port) - - go func() { - apiServer.Start(listener) - }() - - pk, sk := cipher.GenerateKeyPair() - serverStaticPk, _ := cipher.GenerateKeyPair() - ephemeralPk1, ephemeralSk1 := cipher.GenerateKeyPair() - ephemeralPk2, _ := cipher.GenerateKeyPair() - baseEntry := newTestEntry(pk, serverStaticPk, ephemeralPk1, ephemeralPk2) - - cases := []struct { - name string - httpResponse client.HTTPMessage - responseShouldError bool - entryPreHook func(entry *client.Entry) - storerPreHook func(store.Storer, *client.Entry) - }{ - - { - name: "set entry right", - responseShouldError: false, - entryPreHook: func(e *client.Entry) { - e.Sign(sk) - }, - }, - { - name: "set entry iteration", - responseShouldError: false, - entryPreHook: func(e *client.Entry) { - e.Sign(sk) - }, - storerPreHook: func(s store.Storer, e *client.Entry) { - e.Sequence = 0 - s.SetEntry(context.Background(), e) - e.Sequence = 1 - e.Timestamp += 3 - }, - }, - { - name: "set entry iteration wrong sequence", - responseShouldError: true, - httpResponse: client.HTTPMessage{Code: http.StatusUnprocessableEntity, Message: client.ErrValidationWrongSequence.Error()}, - entryPreHook: func(e *client.Entry) { - e.Sign(sk) - }, - storerPreHook: func(s store.Storer, e *client.Entry) { - e.Sequence = 2 - s.SetEntry(context.Background(), e) - }, - }, - { - name: "set entry iteration unauthorized", - responseShouldError: true, - httpResponse: client.HTTPMessage{Code: http.StatusUnauthorized, Message: client.ErrUnauthorized.Error()}, - entryPreHook: func(e *client.Entry) { - e.Sign(sk) - }, - storerPreHook: func(s store.Storer, e *client.Entry) { - e.Sequence = 0 - s.SetEntry(context.Background(), e) - e.Signature = "" - e.Sequence = 1 - e.Timestamp += 3 - e.Sign(ephemeralSk1) - }, - }, - } - for _, tc := range cases { - t.Run(tc.name, func(t *testing.T) { - clientServer := client.New(apiServerAddress) - entry := baseEntry - - if tc.entryPreHook != nil { - tc.entryPreHook(&entry) - } - - mockStore.(*store.MockStore).Clear() - - if tc.storerPreHook != nil { - tc.storerPreHook(mockStore, &entry) - } - - err := clientServer.SetEntry(context.TODO(), &entry) - - if tc.responseShouldError { - assert.Error(t, err) - assert.Equal(t, tc.httpResponse.String(), err.Error()) - } else { - assert.NoError(t, err) - } - - }) - } -} diff --git a/pkg/messaging-discovery/client/client_update_entry_integration_test.go b/pkg/messaging-discovery/client/client_update_entry_integration_test.go deleted file mode 100644 index 4cbe29cb12..0000000000 --- a/pkg/messaging-discovery/client/client_update_entry_integration_test.go +++ /dev/null @@ -1,114 +0,0 @@ -// +build integration - -package client_test - -import ( - "context" - "fmt" - "net" - "net/http" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/skycoin/skywire/pkg/cipher" - "github.com/skycoin/skywire/pkg/messaging-discovery/api" - "github.com/skycoin/skywire/pkg/messaging-discovery/client" - "github.com/skycoin/skywire/pkg/messaging-discovery/store" -) - -func TestUpdateEntriesEndpoint(t *testing.T) { - var apiServerAddress string - var mockStore store.Storer - - mockStore = store.NewStore("mock", "") - apiServer := api.New(mockStore, 5) - - // get a free port - listener, err := net.Listen("tcp", ":0") - if err != nil { - panic(err) - } - apiServerAddress = fmt.Sprintf("http://localhost:%d", listener.Addr().(*net.TCPAddr).Port) - - go func() { - apiServer.Start(listener) - }() - - pk, sk := cipher.GenerateKeyPair() - serverStaticPk, _ := cipher.GenerateKeyPair() - ephemeralPk1, ephemeralSk1 := cipher.GenerateKeyPair() - ephemeralPk2, _ := cipher.GenerateKeyPair() - baseEntry := newTestEntry(pk, serverStaticPk, ephemeralPk1, ephemeralPk2) - baseEntry.Sign(sk) - - mockStore.SetEntry(context.TODO(), &baseEntry) - - cases := []struct { - name string - httpResponse client.HTTPMessage - secretKey cipher.SecKey - responseShouldError bool - entryPreHook func(entry *client.Entry) - storerPreHook func(*testing.T, store.Storer, *client.Entry) - }{ - - { - name: "update entry iteration", - responseShouldError: false, - secretKey: sk, - storerPreHook: func(t *testing.T, s store.Storer, e *client.Entry) { - err := s.SetEntry(context.TODO(), e) - require.NoError(t, err) - e.Server.Address = "different one" - }, - }, - { - name: "update a client entry with no ephemerals", - responseShouldError: false, - secretKey: sk, - storerPreHook: func(t *testing.T, s store.Storer, e *client.Entry) { - err := s.SetEntry(context.TODO(), e) - require.NoError(t, err) - e.Server = nil - e.Keys.Ephemerals = nil - }, - }, - { - name: "update entry unauthorized", - responseShouldError: true, - secretKey: ephemeralSk1, - httpResponse: client.HTTPMessage{Code: http.StatusUnauthorized, Message: client.ErrUnauthorized.Error()}, - storerPreHook: func(t *testing.T, s store.Storer, e *client.Entry) { - e.Server.Address = "different one" - }, - }, - } - for _, tc := range cases { - t.Run(tc.name, func(t *testing.T) { - clientServer := client.New(apiServerAddress) - entry := baseEntry - - if tc.entryPreHook != nil { - tc.entryPreHook(&entry) - } - - mockStore.(*store.MockStore).Clear() - - if tc.storerPreHook != nil { - tc.storerPreHook(t, mockStore, &entry) - } - - err := clientServer.UpdateEntry(context.TODO(), tc.secretKey, &entry) - - if tc.responseShouldError { - assert.Error(t, err) - assert.Equal(t, tc.httpResponse.String(), err.Error()) - } else { - assert.NoError(t, err) - } - - }) - } -} diff --git a/pkg/messaging-discovery/client/entry_test.go b/pkg/messaging-discovery/client/entry_test.go deleted file mode 100644 index 8c2c7d6961..0000000000 --- a/pkg/messaging-discovery/client/entry_test.go +++ /dev/null @@ -1,298 +0,0 @@ -package client_test - -import ( - "fmt" - "log" - "os" - "testing" - "time" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/skycoin/skycoin/src/util/logging" - - "github.com/skycoin/skywire/pkg/cipher" - - "github.com/skycoin/skywire/pkg/messaging-discovery/client" -) - -func TestMain(m *testing.M) { - loggingLevel, ok := os.LookupEnv("TEST_LOGGING_LEVEL") - if ok { - lvl, err := logging.LevelFromString(loggingLevel) - if err != nil { - log.Fatal(err) - } - logging.SetLevel(lvl) - } else { - logging.Disable() - } - - os.Exit(m.Run()) -} - -func TestNewClientEntryIsValid(t *testing.T) { - pk, sk := cipher.GenerateKeyPair() - - cases := []struct { - name string - entry func() *client.Entry - }{ - { - name: "NewClientEntry is valid", - entry: func() *client.Entry { - return client.NewClientEntry(pk, 0, nil) - }, - }, - { - name: "NewServerEntry is valid", - entry: func() *client.Entry { - return client.NewServerEntry(pk, 0, "localhost:8080", 5) - }, - }, - } - - for _, tc := range cases { - t.Run(tc.name, func(t *testing.T) { - entry := tc.entry() - err := entry.Sign(sk) - require.NoError(t, err) - - err = entry.Validate() - - assert.NoError(t, err) - }) - } -} - -func TestVerifySignature(t *testing.T) { - // Arrange - // Create keys and signed entry - pk, sk := cipher.GenerateKeyPair() - wrongPk, _ := cipher.GenerateKeyPair() - - entry := newTestEntry(pk) - - err := entry.Sign(sk) - require.NoError(t, err) - - // Action - err = entry.VerifySignature() - - // Assert - assert.Nil(t, err) - - // Action - entry.Static = wrongPk - err = entry.VerifySignature() - - // Assert - assert.NotNilf(t, err, "this signature must not be valid") -} - -func TestValidateRightEntry(t *testing.T) { - // Arrange - // Create keys and signed entry - pk, sk := cipher.GenerateKeyPair() - - validEntry := newTestEntry(pk) - err := validEntry.Sign(sk) - require.NoError(t, err) - - // Action - err = validEntry.Validate() - assert.Nil(t, err) -} - -func TestValidateNonKeysEntry(t *testing.T) { - // Arrange - // Create keys and signed entry - _, sk := cipher.GenerateKeyPair() - nonKeysEntry := client.Entry{ - Timestamp: time.Now().Unix(), - Client: &client.Client{}, - Server: &client.Server{ - Address: "localhost:8080", - AvailableConnections: 3, - }, - Version: "0", - Sequence: 0, - } - err := nonKeysEntry.Sign(sk) - require.NoError(t, err) - - // Action - err = nonKeysEntry.Validate() - assert.NotNil(t, err) -} - -func TestValidateNonClientNonServerEntry(t *testing.T) { - // Arrange - // Create keys and signed entry - _, sk := cipher.GenerateKeyPair() - nonClientNonServerEntry := client.Entry{ - Timestamp: time.Now().Unix(), - Version: "0", - Sequence: 0, - } - err := nonClientNonServerEntry.Sign(sk) - require.NoError(t, err) - - // Action - err = nonClientNonServerEntry.Validate() - assert.NotNil(t, err) -} - -func TestValidateNonSignedEntry(t *testing.T) { - // Arrange - // Create keys and signed entry - nonClientNonServerEntry := client.Entry{ - Timestamp: time.Now().Unix(), - Version: "0", - Sequence: 0, - } - - // Action - err := nonClientNonServerEntry.Validate() - assert.NotNil(t, err) -} - -func TestValidateIteration(t *testing.T) { - // Arrange - // Create keys and two entries - pk, sk := cipher.GenerateKeyPair() - - entryPrevious := newTestEntry(pk) - entryNext := newTestEntry(pk) - entryNext.Sequence = 1 - err := entryPrevious.Sign(sk) - require.NoError(t, err) - - // Action - err = entryPrevious.ValidateIteration(&entryNext) - - // Assert - assert.NoError(t, err) -} - -func TestValidateIterationEmptyClient(t *testing.T) { - // Arrange - // Create keys and two entries - pk, sk := cipher.GenerateKeyPair() - - entryPrevious := newTestEntry(pk) - err := entryPrevious.Sign(sk) - require.NoError(t, err) - entryNext := newTestEntry(pk) - entryNext.Sequence = 1 - err = entryNext.Sign(sk) - require.NoError(t, err) - - // Action - errValidation := entryNext.Validate() - errIteration := entryPrevious.ValidateIteration(&entryNext) - - // Assert - assert.NoError(t, errValidation) - assert.NoError(t, errIteration) -} - -func TestValidateIterationWrongSequence(t *testing.T) { - // Arrange - // Create keys and two entries - pk, sk := cipher.GenerateKeyPair() - - entryPrevious := newTestEntry(pk) - entryPrevious.Sequence = 2 - err := entryPrevious.Sign(sk) - require.NoError(t, err) - entryNext := newTestEntry(pk) - err = entryNext.Sign(sk) - require.NoError(t, err) - - // Action - err = entryPrevious.ValidateIteration(&entryNext) - - // Assert - assert.NotNil(t, err) -} - -func TestValidateIterationWrongTime(t *testing.T) { - // Arrange - // Create keys and two entries - pk, sk := cipher.GenerateKeyPair() - - entryPrevious := newTestEntry(pk) - err := entryPrevious.Sign(sk) - require.NoError(t, err) - entryNext := newTestEntry(pk) - entryNext.Timestamp -= 3 - err = entryNext.Sign(sk) - require.NoError(t, err) - - // Action - err = entryPrevious.ValidateIteration(&entryNext) - - // Assert - assert.NotNil(t, err) -} - -func TestCopy(t *testing.T) { - pk, sk := cipher.GenerateKeyPair() - entry := newTestEntry(pk) - err := entry.Sign(sk) - require.NoError(t, err) - - cases := []struct { - name string - src *client.Entry - dst *client.Entry - }{ - { - name: "must copy values for client, server and keys", - src: &entry, - dst: &client.Entry{ - Client: &client.Client{}, - Server: &client.Server{Address: "s", AvailableConnections: 0}, - Static: cipher.PubKey{}, - Timestamp: 3, - Sequence: 0, - Version: "0", - Signature: "s", - }, - }, - { - name: "must accept dst empty entry", - src: &entry, - dst: &client.Entry{}, - }, - { - name: "must accept src empty entry", - src: &client.Entry{}, - dst: &client.Entry{ - Client: &client.Client{}, - Server: &client.Server{Address: "s", AvailableConnections: 0}, - Static: cipher.PubKey{}, - Timestamp: 3, - Sequence: 0, - Version: "0", - Signature: "s", - }, - }, - } - - for _, tc := range cases { - t.Run(tc.name, func(t *testing.T) { - client.Copy(tc.dst, tc.src) - - assert.EqualValues(t, tc.src, tc.dst) - if tc.dst.Server != nil { - assert.NotEqual(t, fmt.Sprintf("%p", tc.dst.Server), fmt.Sprintf("%p", tc.src.Server)) - } - if tc.dst.Client != nil { - assert.NotEqual(t, fmt.Sprintf("%p", tc.dst.Client), fmt.Sprintf("%p", tc.src.Client)) - } - }) - } -} diff --git a/pkg/messaging/callbacks.go b/pkg/messaging/callbacks.go deleted file mode 100644 index b4235d6bf8..0000000000 --- a/pkg/messaging/callbacks.go +++ /dev/null @@ -1,21 +0,0 @@ -package messaging - -type ( - // HandshakeCompleteAction triggers when a handshake is completed successfully. - HandshakeCompleteAction func(conn *Link) - - // FrameAction triggers when a connection receives a non-predefined frame. - // If an error is returned, the connection is closed and the 'Close' callback is triggered. - FrameAction func(conn *Link, dt FrameType, body []byte) error - - // TCPCloseAction triggers when we receive a connection closure. - // 'remote' determines whether the closure is requested remotely or locally. - TCPCloseAction func(conn *Link, remote bool) - - // Callbacks contains callbacks. - Callbacks struct { - HandshakeComplete HandshakeCompleteAction - Data FrameAction - Close TCPCloseAction - } -) diff --git a/pkg/messaging/chan_list.go b/pkg/messaging/chan_list.go deleted file mode 100644 index d9fe895462..0000000000 --- a/pkg/messaging/chan_list.go +++ /dev/null @@ -1,58 +0,0 @@ -package messaging - -import ( - "sync" -) - -type chanList struct { - sync.Mutex - - chans map[byte]*msgChannel -} - -func newChanList() *chanList { - return &chanList{chans: map[byte]*msgChannel{}} -} - -func (c *chanList) add(mCh *msgChannel) byte { - c.Lock() - defer c.Unlock() - - for i := byte(0); i < 255; i++ { - if c.chans[i] == nil { - c.chans[i] = mCh - return i - } - } - - panic("no free channels") -} - -func (c *chanList) get(id byte) *msgChannel { - c.Lock() - ch := c.chans[id] - c.Unlock() - - return ch -} - -func (c *chanList) remove(id byte) { - c.Lock() - delete(c.chans, id) - c.Unlock() -} - -func (c *chanList) dropAll() []*msgChannel { - c.Lock() - defer c.Unlock() - var r []*msgChannel - - for _, ch := range c.chans { - if ch == nil { - continue - } - r = append(r, ch) - } - c.chans = nil - return r -} diff --git a/pkg/messaging/channel.go b/pkg/messaging/channel.go deleted file mode 100644 index 02c298abec..0000000000 --- a/pkg/messaging/channel.go +++ /dev/null @@ -1,251 +0,0 @@ -package messaging - -import ( - "bytes" - "context" - "encoding/binary" - "io" - "sync" - "time" - - "github.com/skycoin/skywire/internal/noise" - "github.com/skycoin/skywire/pkg/cipher" - "github.com/skycoin/skywire/pkg/transport" -) - -type msgChannel struct { - id byte // This is to be changed. - idMx sync.RWMutex - - remotePK cipher.PubKey - link *Link - buf *bytes.Buffer - - deadline time.Time - - waitChan chan bool // waits for remote response (whether msgChannel is accepted or not). - readChan chan []byte - - doneChan chan struct{} - doneOnce sync.Once - - noise *noise.Noise - rMx sync.Mutex // lock for decrypt cipher state - wMx sync.Mutex // lock for encrypt cipher state -} - -func newChannel(initiator bool, secKey cipher.SecKey, remote cipher.PubKey, link *Link) (*msgChannel, error) { - noiseConf := noise.Config{ - LocalSK: secKey, - LocalPK: link.Local(), - RemotePK: remote, - Initiator: initiator, - } - noiseInstance, err := noise.KKAndSecp256k1(noiseConf) - if err != nil { - return nil, err - } - - return &msgChannel{ - remotePK: remote, - link: link, - buf: new(bytes.Buffer), - waitChan: make(chan bool, 1), // should allows receive one reply. - readChan: make(chan []byte), - doneChan: make(chan struct{}), - noise: noiseInstance, - deadline: time.Time{}, - }, nil -} - -// ID obtains the msgChannel's id. -func (mCh *msgChannel) ID() byte { - mCh.idMx.RLock() - id := mCh.id - mCh.idMx.RUnlock() - return id -} - -// SetID set's the msgChannel's id. -func (mCh *msgChannel) SetID(id byte) { - mCh.idMx.Lock() - mCh.id = id - mCh.idMx.Unlock() -} - -// Edges returns the public keys of the msgChannel's edge nodes -func (mCh *msgChannel) Edges() [2]cipher.PubKey { - return transport.SortPubKeys(mCh.link.Local(), mCh.remotePK) -} - -// HandshakeMessage prepares a handshake message safely. -func (mCh *msgChannel) HandshakeMessage() ([]byte, error) { - mCh.rMx.Lock() - mCh.wMx.Lock() - res, err := mCh.noise.HandshakeMessage() - mCh.rMx.Unlock() - mCh.wMx.Unlock() - return res, err -} - -// ProcessMessage reads a handshake message safely. -func (mCh *msgChannel) ProcessMessage(msg []byte) error { - mCh.rMx.Lock() - mCh.wMx.Lock() - err := mCh.noise.ProcessMessage(msg) - mCh.rMx.Unlock() - mCh.wMx.Unlock() - return err -} - -func (mCh *msgChannel) Read(p []byte) (n int, err error) { - return mCh.read(p) -} - -func (mCh *msgChannel) read(p []byte) (n int, err error) { - if mCh.buf.Len() != 0 { - return mCh.buf.Read(p) - } - - ctx := context.Background() - var cancel context.CancelFunc - if time.Until(mCh.deadline) > 0 { - ctx, cancel = context.WithDeadline(ctx, mCh.deadline) - defer cancel() - } - - return mCh.readEncrypted(ctx, p) -} - -func (mCh *msgChannel) Write(p []byte) (n int, err error) { - return mCh.write(p) -} - -func (mCh *msgChannel) write(p []byte) (n int, err error) { - select { - case <-mCh.doneChan: - return 0, ErrChannelClosed - default: - } - - ctx := context.Background() - var cancel context.CancelFunc - if time.Until(mCh.deadline) > 0 { - ctx, cancel = context.WithDeadline(ctx, mCh.deadline) - defer cancel() - } - - mCh.wMx.Lock() - defer mCh.wMx.Unlock() - data := mCh.noise.EncryptUnsafe(p) - - buf := make([]byte, 2+len(data)) - binary.BigEndian.PutUint16(buf[:2], uint16(len(data))) - copy(buf[2:], data) - - done := make(chan struct{}) - go func() { - n, err = mCh.link.Send(mCh.ID(), buf) - n = n - (len(data) - len(p) + 2) - close(done) - }() - - select { - case <-done: - return n, err - case <-ctx.Done(): - return 0, ErrDeadlineExceeded - } -} - -func (mCh *msgChannel) Close() error { - select { - case <-mCh.doneChan: - return ErrChannelClosed - default: - } - - if mCh.close() { - if _, err := mCh.link.SendCloseChannel(mCh.ID()); err != nil { - return err - } - } - - return nil -} - -func (mCh *msgChannel) SetDeadline(t time.Time) error { - mCh.deadline = t - return nil -} - -func (mCh *msgChannel) Type() string { - return "messaging" -} - -func (mCh *msgChannel) OnChannelClosed() bool { - return mCh.close() -} - -func (mCh *msgChannel) close() bool { - closed := false - mCh.doneOnce.Do(func() { - close(mCh.doneChan) - closed = true - }) - return closed -} - -func (mCh *msgChannel) readEncrypted(ctx context.Context, p []byte) (n int, err error) { - mCh.rMx.Lock() - defer mCh.rMx.Unlock() - - buf := new(bytes.Buffer) - readAtLeast := func(d []byte) (int, error) { - for { - if buf.Len() >= len(d) { - return buf.Read(d) - } - - select { - case <-mCh.doneChan: - return 0, io.EOF - case in, more := <-mCh.readChan: - if !more { - return 0, io.EOF - } - - if _, err := buf.Write(in); err != nil { - return 0, err - } - case <-ctx.Done(): - return 0, ErrDeadlineExceeded - } - } - } - - size := make([]byte, 2) - if _, err := readAtLeast(size); err != nil { - return 0, err - } - - encrypted := make([]byte, binary.BigEndian.Uint16(size)) - if _, err := readAtLeast(encrypted); err != nil { - return 0, err - } - - data, err := mCh.noise.DecryptUnsafe(encrypted) - if err != nil { - return 0, err - } - - if len(data) > len(p) { - if _, err := mCh.buf.Write(data[len(p):]); err != nil { // TODO: data race. - return 0, io.ErrShortBuffer - } - - return copy(p, data[:len(p)]), nil - } - - return copy(p, data), nil -} diff --git a/pkg/messaging/channel_test.go b/pkg/messaging/channel_test.go deleted file mode 100644 index d7aebe3153..0000000000 --- a/pkg/messaging/channel_test.go +++ /dev/null @@ -1,170 +0,0 @@ -package messaging - -import ( - "encoding/binary" - "io" - "net" - "testing" - "time" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/skycoin/skywire/pkg/cipher" - - "github.com/skycoin/skywire/internal/noise" -) - -func TestChannelRead(t *testing.T) { - remotePK, remoteSK := cipher.GenerateKeyPair() - pk, sk := cipher.GenerateKeyPair() - - in, _ := net.Pipe() - l, err := NewLink(in, &LinkConfig{Public: pk}, nil) - require.NoError(t, err) - - c, err := newChannel(true, sk, remotePK, l) - require.NoError(t, err) - - rn := handshakeChannel(t, c, remotePK, remoteSK) - - buf := make([]byte, 2) - c.SetDeadline(time.Now().Add(100 * time.Millisecond)) // nolint - _, err = c.Read(buf) - require.Equal(t, ErrDeadlineExceeded, err) - - go func() { - data := rn.EncryptUnsafe([]byte("foo")) - buf := make([]byte, 2) - binary.BigEndian.PutUint16(buf, uint16(len(data))) - buf = append(buf, data...) - c.readChan <- buf[0:3] - c.readChan <- buf[3:] - - data = rn.EncryptUnsafe([]byte("foo")) - buf = make([]byte, 2) - binary.BigEndian.PutUint16(buf, uint16(len(data))) - buf = append(buf, data...) - c.readChan <- buf - c.close() - }() - - buf = make([]byte, 3) - n, err := c.Read(buf) - require.NoError(t, err) - assert.Equal(t, 3, n) - assert.Equal(t, []byte("foo"), buf) - - buf = make([]byte, 2) - n, err = c.Read(buf) - require.NoError(t, err) - assert.Equal(t, 2, n) - assert.Equal(t, []byte("fo"), buf) - - buf = make([]byte, 2) - n, err = c.Read(buf) - require.NoError(t, err) - assert.Equal(t, 1, n) - assert.Equal(t, []byte("o"), buf[:n]) - - _, err = c.Read(buf) - require.Equal(t, io.EOF, err) -} - -func TestChannelWrite(t *testing.T) { - remotePK, remoteSK := cipher.GenerateKeyPair() - pk, sk := cipher.GenerateKeyPair() - - in, out := net.Pipe() - - l, err := NewLink(in, &LinkConfig{Public: pk}, nil) - require.NoError(t, err) - c, err := newChannel(true, sk, remotePK, l) - require.NoError(t, err) - c.SetID(10) - - rn := handshakeChannel(t, c, remotePK, remoteSK) - - var ( - readBuf = make([]byte, 29) - readErr error - readDone = make(chan struct{}) - ) - go func() { - _, readErr = out.Read(readBuf) - close(readDone) - }() - - n, err := c.Write([]byte("foo")) - require.NoError(t, err) - assert.Equal(t, 3, n) - - <-readDone - assert.NoError(t, readErr) - assert.Equal(t, FrameTypeSend, FrameType(readBuf[2])) - assert.Equal(t, byte(10), readBuf[3]) - - // Encoded length should be length of encrypted payload "foo". - require.Equal(t, uint16(23), binary.BigEndian.Uint16(readBuf[4:])) - - data, err := rn.DecryptUnsafe(readBuf[6:]) - require.NoError(t, err) - assert.Equal(t, []byte("foo"), data) - - c.SetDeadline(time.Now().Add(100 * time.Millisecond)) // nolint - _, err = c.Write([]byte("foo")) - require.Equal(t, ErrDeadlineExceeded, err) - - closed := c.close() - require.True(t, closed) - - _, err = c.Write([]byte("foo")) - require.Equal(t, ErrChannelClosed, err) -} - -func TestChannelClose(t *testing.T) { - remotePK, remoteSK := cipher.GenerateKeyPair() - pk, sk := cipher.GenerateKeyPair() - - in, out := net.Pipe() - l, err := NewLink(in, &LinkConfig{Public: pk}, nil) - require.NoError(t, err) - - c, err := newChannel(true, sk, remotePK, l) - require.NoError(t, err) - c.SetID(10) - - handshakeChannel(t, c, remotePK, remoteSK) - - require.NoError(t, c.SetDeadline(time.Now().Add(100*time.Millisecond))) - - buf := make([]byte, 4) - go out.Read(buf) // nolint - require.NoError(t, c.Close()) - assert.Equal(t, FrameTypeCloseChannel, FrameType(buf[2])) - assert.Equal(t, byte(10), buf[3]) -} - -func handshakeChannel(t *testing.T, mCh *msgChannel, pk cipher.PubKey, sk cipher.SecKey) *noise.Noise { - t.Helper() - - noiseConf := noise.Config{ - LocalSK: sk, - LocalPK: pk, - RemotePK: mCh.link.Local(), - Initiator: false, - } - - n, err := noise.KKAndSecp256k1(noiseConf) - require.NoError(t, err) - - msg, err := mCh.noise.HandshakeMessage() - require.NoError(t, err) - - require.NoError(t, n.ProcessMessage(msg)) - msg, err = n.HandshakeMessage() - require.NoError(t, err) - - require.NoError(t, mCh.noise.ProcessMessage(msg)) - return n -} diff --git a/pkg/messaging/factory.go b/pkg/messaging/factory.go deleted file mode 100644 index 84558613f5..0000000000 --- a/pkg/messaging/factory.go +++ /dev/null @@ -1,433 +0,0 @@ -// Package messaging implements messaging communication. Messaging -// communication is performed between 2 nodes using intermediate relay -// server, visor discovery is performed using messaging discovery. -package messaging - -import ( - "context" - "encoding/hex" - "errors" - "fmt" - "net" - "sync" - "time" - - "github.com/skycoin/skycoin/src/util/logging" - - "github.com/skycoin/skywire/pkg/cipher" - - "github.com/skycoin/skywire/pkg/messaging-discovery/client" - "github.com/skycoin/skywire/pkg/transport" -) - -var ( - // ErrNoSrv indicate that remote client does not have DelegatedServers in entry. - ErrNoSrv = errors.New("remote has no DelegatedServers") - // ErrRejected indicates that ChannelOpen frame was rejected by remote server. - ErrRejected = errors.New("rejected") - // ErrChannelClosed indicates that underlying channel is being closed and writes are prohibited. - ErrChannelClosed = errors.New("messaging channel closed") - // ErrDeadlineExceeded indicates that read/write operation failed due to timeout. - ErrDeadlineExceeded = errors.New("deadline exceeded in messaging") - // ErrClientClosed indicates that client is closed and not accepting new connections. - ErrClientClosed = errors.New("client closed") -) - -type clientLink struct { - link *Link - addr string - chans *chanList -} - -// Config configures MsgFactory -type Config struct { - PubKey cipher.PubKey - SecKey cipher.SecKey - Discovery client.APIClient - Retries int - RetryDelay time.Duration -} - -// MsgFactory sends messages to remote client nodes via relay Server -// Implements Factory -type MsgFactory struct { - Logger *logging.Logger - - pubKey cipher.PubKey - secKey cipher.SecKey - dc client.APIClient - pool *Pool - - retries int - retryDelay time.Duration - - links map[cipher.PubKey]*clientLink - mu sync.RWMutex - - newCh chan *msgChannel // chan for newly opened channels - newWG sync.WaitGroup // waits for goroutines writing to newCh to end. - - doneCh chan struct{} -} - -// NewMsgFactory constructs a new MsgFactory -func NewMsgFactory(conf *Config) *MsgFactory { - msgFactory := &MsgFactory{ - Logger: logging.MustGetLogger("messenger"), - pubKey: conf.PubKey, - secKey: conf.SecKey, - dc: conf.Discovery, - retries: conf.Retries, - retryDelay: conf.RetryDelay, - links: make(map[cipher.PubKey]*clientLink), - newCh: make(chan *msgChannel), - doneCh: make(chan struct{}), - } - config := &LinkConfig{ - Public: msgFactory.pubKey, - Secret: msgFactory.secKey, - HandshakeTimeout: DefaultHandshakeTimeout, - } - msgFactory.pool = NewPool(config, &Callbacks{ - Data: msgFactory.onData, - Close: msgFactory.onClose, - }) - - return msgFactory -} - -// ConnectToInitialServers tries to connect to at most serverCount servers. -func (msgFactory *MsgFactory) ConnectToInitialServers(ctx context.Context, serverCount int) error { - if serverCount == 0 { - return nil - } - - entries, err := msgFactory.dc.AvailableServers(ctx) - if err != nil { - return fmt.Errorf("servers are not available: %s", err) - } - - for _, entry := range entries { - if len(msgFactory.links) > serverCount { - break - } - - if _, err := msgFactory.link(entry.Static, entry.Server.Address); err != nil { - msgFactory.Logger.Warnf("Failed to connect to the server %s: %s", entry.Static, err) - } - } - - if len(msgFactory.links) == 0 { - return fmt.Errorf("servers are not available: all servers failed") - } - - if err := msgFactory.setEntry(ctx); err != nil { - return fmt.Errorf("entry update failure: %s", err) - } - - return nil -} - -// Accept accepts a remotely-initiated Transport. -func (msgFactory *MsgFactory) Accept(ctx context.Context) (transport.Transport, error) { - select { - case ch, more := <-msgFactory.newCh: - if !more { - return nil, ErrClientClosed - } - return ch, nil - case <-ctx.Done(): - return nil, ctx.Err() - } -} - -// Dial initiates a Transport with a remote visor. -func (msgFactory *MsgFactory) Dial(ctx context.Context, remote cipher.PubKey) (transport.Transport, error) { - entry, err := msgFactory.dc.Entry(ctx, remote) - if err != nil { - return nil, fmt.Errorf("get entry failure: %s", err) - } - - if entry.Client.DelegatedServers == nil || len(entry.Client.DelegatedServers) == 0 { - return nil, ErrNoSrv - } - - clientLink, err := msgFactory.ensureLink(ctx, entry.Client.DelegatedServers) - if err != nil { - return nil, fmt.Errorf("link failure: %s", err) - } - - channel, err := newChannel(true, msgFactory.secKey, remote, clientLink.link) - if err != nil { - return nil, fmt.Errorf("noise setup: %s", err) - } - localID := clientLink.chans.add(channel) - - msg, err := channel.HandshakeMessage() - if err != nil { - return nil, fmt.Errorf("noise handshake: %s", err) - } - - if _, err := clientLink.link.SendOpenChannel(localID, remote, msg); err != nil { - return nil, fmt.Errorf("failed to open channel: %s", err) - } - - select { - case result := <-channel.waitChan: - if !result { - return nil, ErrRejected - } - case <-ctx.Done(): - return nil, ctx.Err() - } - - msgFactory.Logger.Infof("Opened new channel local ID %d, remote ID %d with %s", localID, channel.ID(), remote) - return channel, nil -} - -// Local returns the local public key. -func (msgFactory *MsgFactory) Local() cipher.PubKey { - return msgFactory.pubKey -} - -// Type returns the Transport type. -func (msgFactory *MsgFactory) Type() string { - return "messaging" -} - -// Close closes underlying link pool. -func (msgFactory *MsgFactory) Close() error { - msgFactory.Logger.Info("Closing link pool") - select { - case <-msgFactory.doneCh: - default: - close(msgFactory.doneCh) - msgFactory.newWG.Wait() // Ensure that 'c.newCh' is not being written to before closing. - close(msgFactory.newCh) - } - return msgFactory.pool.Close() -} - -func (msgFactory *MsgFactory) setEntry(ctx context.Context) error { - msgFactory.Logger.Info("Updating discovery entry") - serverPKs := []cipher.PubKey{} - msgFactory.mu.RLock() - for pk := range msgFactory.links { - serverPKs = append(serverPKs, pk) - } - msgFactory.mu.RUnlock() - - entry, err := msgFactory.dc.Entry(ctx, msgFactory.pubKey) - if err != nil { - entry = client.NewClientEntry(msgFactory.pubKey, 0, serverPKs) - if err := entry.Sign(msgFactory.secKey); err != nil { - return err - } - - return msgFactory.dc.SetEntry(ctx, entry) - } - - entry.Client.DelegatedServers = serverPKs - return msgFactory.dc.UpdateEntry(ctx, msgFactory.secKey, entry) -} - -func (msgFactory *MsgFactory) link(remotePK cipher.PubKey, addr string) (*clientLink, error) { - conn, err := net.Dial("tcp", addr) - if err != nil { - return nil, err - } - l, err := msgFactory.pool.Initiate(conn, remotePK) - if err != nil && err != ErrConnExists { - return nil, err - } - - msgFactory.Logger.Infof("Opened new link with the server %s", remotePK) - clientLink := &clientLink{l, addr, newChanList()} - msgFactory.mu.Lock() - msgFactory.links[remotePK] = clientLink - msgFactory.mu.Unlock() - return clientLink, nil -} - -func (msgFactory *MsgFactory) ensureLink(ctx context.Context, serverList []cipher.PubKey) (*clientLink, error) { - for _, serverPK := range serverList { - if l := msgFactory.getLink(serverPK); l != nil { - return l, nil - } - } - - serverPK := serverList[0] - serverEntry, err := msgFactory.dc.Entry(ctx, serverPK) - if err != nil { - return nil, fmt.Errorf("get server failure: %s", err) - } - - l, err := msgFactory.link(serverPK, serverEntry.Server.Address) - if err != nil { - return nil, err - } - - if err := msgFactory.setEntry(ctx); err != nil { - return nil, fmt.Errorf("entry update failure: %s", err) - } - - return l, nil -} - -func (msgFactory *MsgFactory) getLink(remotePK cipher.PubKey) *clientLink { - msgFactory.mu.RLock() - l := msgFactory.links[remotePK] - msgFactory.mu.RUnlock() - return l -} - -func (msgFactory *MsgFactory) onData(l *Link, frameType FrameType, body []byte) error { - remotePK := l.Remote() - if len(body) == 0 { - msgFactory.Logger.Warnf("Invalid packet from %s: empty body", remotePK) - return nil - } - - clientLink := msgFactory.getLink(l.Remote()) - channelID := body[0] - var sendErr error - - msgFactory.Logger.Debugf("New frame %s from %s@%d", frameType, remotePK, channelID) - if frameType == FrameTypeOpenChannel { - if lID, msg, err := msgFactory.openChannel(channelID, body[1:34], body[34:], clientLink); err != nil { - msgFactory.Logger.Warnf("Failed to open new channel for %s: %s", remotePK, err) - _, sendErr = l.SendChannelClosed(channelID) - } else { - msgFactory.Logger.Infof("Opened new channel local ID %d, remote ID %d with %s", lID, channelID, - hex.EncodeToString(body[1:34])) - _, sendErr = l.SendChannelOpened(channelID, lID, msg) - } - - return msgFactory.warnSendError(remotePK, sendErr) - } - - channel := clientLink.chans.get(channelID) - if channel == nil { - if frameType != FrameTypeChannelClosed && frameType != FrameTypeCloseChannel { - msgFactory.Logger.Warnf("Frame for unknown channel %d from %s", channelID, remotePK) - } - return nil - } - - switch frameType { - case FrameTypeCloseChannel: - clientLink.chans.remove(channelID) - _, sendErr = l.SendChannelClosed(channel.ID()) - msgFactory.Logger.Debugf("Closed channel ID %d", channelID) - case FrameTypeChannelOpened: - channel.SetID(body[1]) - if err := channel.ProcessMessage(body[2:]); err != nil { - sendErr = fmt.Errorf("noise handshake: %s", err) - } - - select { - case channel.waitChan <- true: - default: - } - case FrameTypeChannelClosed: - channel.SetID(body[0]) - select { - case channel.waitChan <- false: - default: - } - channel.OnChannelClosed() - clientLink.chans.remove(channelID) - case FrameTypeSend: - go func() { - select { - case <-msgFactory.doneCh: - case <-channel.doneChan: - case channel.readChan <- body[1:]: - } - }() - } - - return msgFactory.warnSendError(remotePK, sendErr) -} - -func (msgFactory *MsgFactory) onClose(l *Link, remote bool) { - remotePK := l.Remote() - - msgFactory.mu.RLock() - chanLink := msgFactory.links[remotePK] - msgFactory.mu.RUnlock() - - for _, channel := range chanLink.chans.dropAll() { - channel.close() - } - - select { - case <-msgFactory.doneCh: - default: - msgFactory.Logger.Infof("Disconnected from the server %s. Trying to re-connect...", remotePK) - for attempt := 0; attempt < msgFactory.retries; attempt++ { - if _, err := msgFactory.link(remotePK, chanLink.addr); err == nil { - msgFactory.Logger.Infof("Re-connected to the server %s", remotePK) - return - } - time.Sleep(msgFactory.retryDelay) - } - } - - msgFactory.Logger.Infof("Closing link with the server %s", remotePK) - - msgFactory.mu.Lock() - delete(msgFactory.links, remotePK) - msgFactory.mu.Unlock() - - if err := msgFactory.setEntry(context.Background()); err != nil { - msgFactory.Logger.Warnf("Failed to update entry: %s", err) - } -} - -func (msgFactory *MsgFactory) openChannel(rID byte, remotePK []byte, noiseMsg []byte, chanLink *clientLink) (lID byte, noiseRes []byte, err error) { - var pubKey cipher.PubKey - pubKey, err = cipher.NewPubKey(remotePK) - if err != nil { - return - } - - channel, err := newChannel(false, msgFactory.secKey, pubKey, chanLink.link) - channel.SetID(rID) - if err != nil { - err = fmt.Errorf("noise setup: %s", err) - return - } - - if err = channel.ProcessMessage(noiseMsg); err != nil { - err = fmt.Errorf("noise handshake: %s", err) - return - } - - lID = chanLink.chans.add(channel) - - msgFactory.newWG.Add(1) // Ensure that 'c.newCh' is not being written to before closing. - go func() { - select { - case <-msgFactory.doneCh: - case msgFactory.newCh <- channel: - } - msgFactory.newWG.Done() - }() - - noiseRes, err = channel.HandshakeMessage() - if err != nil { - err = fmt.Errorf("noise handshake: %s", err) - return - } - - return lID, noiseRes, err -} - -func (msgFactory *MsgFactory) warnSendError(remote cipher.PubKey, err error) error { - if err != nil { - msgFactory.Logger.Warnf("Failed to send frame to %s: %s", remote, err) - } - - return nil -} diff --git a/pkg/messaging/factory_noci_test.go b/pkg/messaging/factory_noci_test.go deleted file mode 100644 index 9ca7dd4948..0000000000 --- a/pkg/messaging/factory_noci_test.go +++ /dev/null @@ -1,60 +0,0 @@ -// +build !no_ci - -package messaging - -import ( - "context" - "testing" - "time" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/skycoin/skywire/pkg/cipher" - "github.com/skycoin/skywire/pkg/messaging-discovery/client" -) - -func TestClientConnectInitialServers(t *testing.T) { - pk, sk := cipher.GenerateKeyPair() - discovery := client.NewMock() - c := NewMsgFactory(&Config{pk, sk, discovery, 1, 100 * time.Millisecond}) - - srv, err := newMockServer(discovery) - require.NoError(t, err) - - time.Sleep(100 * time.Millisecond) - - require.NoError(t, c.ConnectToInitialServers(context.TODO(), 1)) - c.mu.RLock() - require.Len(t, c.links, 1) - c.mu.RUnlock() - - entry, err := discovery.Entry(context.TODO(), pk) - require.NoError(t, err) - assert.Len(t, entry.Client.DelegatedServers, 1) - assert.Equal(t, srv.config.Public, entry.Client.DelegatedServers[0]) - - c.mu.RLock() - l := c.links[srv.config.Public] - c.mu.RUnlock() - require.NotNil(t, l) - require.NoError(t, l.link.Close()) - - time.Sleep(200 * time.Millisecond) - - c.mu.RLock() - require.Len(t, c.links, 1) - c.mu.RUnlock() - - require.NoError(t, c.Close()) - - time.Sleep(100 * time.Millisecond) - - c.mu.RLock() - require.Len(t, c.links, 0) - c.mu.RUnlock() - - entry, err = discovery.Entry(context.TODO(), pk) - require.NoError(t, err) - require.Len(t, entry.Client.DelegatedServers, 0) -} diff --git a/pkg/messaging/factory_test.go b/pkg/messaging/factory_test.go deleted file mode 100644 index ff55e325e7..0000000000 --- a/pkg/messaging/factory_test.go +++ /dev/null @@ -1,218 +0,0 @@ -package messaging - -import ( - "context" - "errors" - "log" - "net" - "os" - "sync" - "testing" - "time" - - "github.com/skycoin/skycoin/src/util/logging" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/skycoin/skywire/pkg/cipher" - "github.com/skycoin/skywire/pkg/messaging-discovery/client" - "github.com/skycoin/skywire/pkg/transport" -) - -func TestMain(m *testing.M) { - - loggingLevel, ok := os.LookupEnv("TEST_LOGGING_LEVEL") - if ok { - lvl, err := logging.LevelFromString(loggingLevel) - if err != nil { - log.Fatal(err) - } - logging.SetLevel(lvl) - } else { - logging.Disable() - } - - os.Exit(m.Run()) -} - -func TestClientDial(t *testing.T) { - pk, sk := cipher.GenerateKeyPair() - discovery := client.NewMock() - c1 := NewMsgFactory(&Config{pk, sk, discovery, 0, 0}) - c1.retries = 0 - - srv, err := newMockServer(discovery) - require.NoError(t, err) - srvPK := srv.config.Public - - anotherPK, anotherSK := cipher.GenerateKeyPair() - c2 := NewMsgFactory(&Config{anotherPK, anotherSK, discovery, 0, 0}) - require.NoError(t, c2.ConnectToInitialServers(context.TODO(), 1)) - - var ( - tp2 transport.Transport - tp2Err error - tp2Done = make(chan struct{}) - ) - go func() { - tp2, tp2Err = c2.Accept(context.TODO()) - close(tp2Done) - }() - - var ( - tp1 transport.Transport - tp1Err error - tp1Done = make(chan struct{}) - ) - go func() { - tp1, tp1Err = c1.Dial(context.TODO(), anotherPK) - close(tp1Done) - }() - - <-tp1Done - require.NoError(t, tp1Err) - require.NotNil(t, c1.getLink(srvPK).chans.get(0)) - - entry, err := discovery.Entry(context.TODO(), pk) - require.NoError(t, err) - require.Len(t, entry.Client.DelegatedServers, 1) - - <-tp2Done - require.NoError(t, tp2Err) - require.NotNil(t, c2.getLink(srvPK).chans.get(0)) - - go tp1.Write([]byte("foo")) // nolint: errcheck - - buf := make([]byte, 3) - n, err := tp2.Read(buf) - require.NoError(t, err) - assert.Equal(t, 3, n) - assert.Equal(t, []byte("foo"), buf) - - go tp2.Write([]byte("bar")) // nolint: errcheck - - buf = make([]byte, 3) - n, err = tp1.Read(buf) - require.NoError(t, err) - assert.Equal(t, 3, n) - assert.Equal(t, []byte("bar"), buf) - - require.NoError(t, tp1.Close()) - require.NoError(t, tp2.Close()) - - // It is expected for the messaging client to delete the channel for chanList eventually. - require.True(t, retry(time.Second*10, time.Second, func() bool { - return c2.getLink(srvPK).chans.get(0) == nil - })) -} - -// retries until successful under a given deadline. -// 'tick' specifies the break duration before retry. -func retry(deadline, tick time.Duration, do func() bool) bool { - timer := time.NewTimer(deadline) - defer timer.Stop() - - done := make(chan struct{}) - doneOnce := new(sync.Once) - defer doneOnce.Do(func() { close(done) }) - - go func() { - for { - select { - case <-done: - return - case <-time.Tick(tick): - if ok := do(); ok { - doneOnce.Do(func() { close(done) }) - return - } - } - } - }() - - for { - select { - case <-timer.C: - return false - case <-done: - return true - } - } -} - -type mockServer struct { - *Pool - - links []*Link - mu sync.Mutex -} - -func newMockServer(discovery client.APIClient) (*mockServer, error) { - pk, sk := cipher.GenerateKeyPair() - srv := &mockServer{} - pool := NewPool(DefaultLinkConfig(), &Callbacks{ - HandshakeComplete: srv.onHandshake, - Data: srv.onData, - }) - pool.config.Public = pk - pool.config.Secret = sk - - l, err := net.Listen("tcp", "127.0.0.1:0") - if err != nil { - return nil, err - } - go pool.Respond(l) // nolint: errcheck - - entry := client.NewServerEntry(pk, 0, l.Addr().String(), 10) - if err := entry.Sign(sk); err != nil { - return nil, err - } - if err := discovery.SetEntry(context.TODO(), entry); err != nil { - return nil, err - } - - srv.Pool = pool - return srv, nil -} - -func (s *mockServer) onHandshake(l *Link) { - s.mu.Lock() - s.links = append(s.links, l) - s.mu.Unlock() -} - -func (s *mockServer) onData(l *Link, frameType FrameType, body []byte) error { - ol, err := s.oppositeLink(l) - if err != nil { - return err - } - - channelID := body[0] - switch frameType { - case FrameTypeOpenChannel: - _, err = ol.SendOpenChannel(channelID, l.Remote(), body[34:]) - case FrameTypeChannelOpened: - _, err = ol.SendChannelOpened(channelID, channelID, body[2:]) - case FrameTypeCloseChannel: - l.SendChannelClosed(channelID) // nolint - _, err = ol.SendCloseChannel(channelID) - case FrameTypeChannelClosed: - _, err = ol.SendChannelClosed(channelID) - case FrameTypeSend: - _, err = ol.Send(channelID, body[1:]) - } - - return err -} - -func (s *mockServer) oppositeLink(l *Link) (*Link, error) { - s.mu.Lock() - defer s.mu.Unlock() - for _, link := range s.links { - if link != l { - return link, nil - } - } - - return nil, errors.New("unknown link") -} diff --git a/pkg/messaging/frame.go b/pkg/messaging/frame.go deleted file mode 100644 index b702201834..0000000000 --- a/pkg/messaging/frame.go +++ /dev/null @@ -1,151 +0,0 @@ -package messaging - -import ( - "encoding/binary" - "errors" - "fmt" - "io" -) - -const ( - // MaxDataBodySize defines the maximum payload body size. - MaxDataBodySize = 65518 -) - -var ( - // ErrDataTypeUnknown occurs when the data type is unknown. - ErrDataTypeUnknown = errors.New("data type unknown and unregistered") - - // ErrDataTooLarge occurs when the data is too large. - ErrDataTooLarge = errors.New("data too large") - - // ErrDataTooSmall occurs when the data is too small. - ErrDataTooSmall = errors.New("data too small") - - // ErrHandshakeFailed occurs when a handshake fails. - ErrHandshakeFailed = errors.New("handshake failed") // TODO(evanlinjin): Make this a struct. -) - -// FrameType determines the type of payload. -type FrameType byte - -func (f FrameType) String() string { - switch f { - case FrameTypeOpenChannel: - return "OpenChannel" - case FrameTypeChannelOpened: - return "ChannelOpened" - case FrameTypeCloseChannel: - return "CloseChannel" - case FrameTypeChannelClosed: - return "ChannelClosed" - case FrameTypeSend: - return "Send" - } - - return fmt.Sprintf("Unknown(%d)", f) -} - -const ( - // FrameTypeOpenChannel defines frame for OpenChannel packet. - FrameTypeOpenChannel FrameType = iota - // FrameTypeChannelOpened defines frame for ChannelOpened packet. - FrameTypeChannelOpened - // FrameTypeCloseChannel defines frame for CloseChannel packet. - FrameTypeCloseChannel - // FrameTypeChannelClosed defines frame for ChannelClosed packet. - FrameTypeChannelClosed - // FrameTypeSend defines frame for Send packet. - FrameTypeSend -) - -/* - <<< DATA >>> -*/ - -// Frame is structures as follows: -// - FrameType (1 byte) -// - FrameBody ([0,65518] bytes) -type Frame []byte - -// MakeFrame makes a data with given type and body. -func MakeFrame(t FrameType, body []byte) Frame { - p := make(Frame, len(body)+1) - p[0] = byte(t) - copy(p[1:], body) - return p -} - -// CheckSize checks the size of the data. -func (p Frame) CheckSize() error { - if len(p) < 1 { - return ErrDataTooSmall - } - if len(p) > MaxDataBodySize+1 { - return ErrDataTooLarge - } - return nil -} - -// Type returns the data type. -func (p Frame) Type() FrameType { - return FrameType(p[0]) -} - -// Body returns the data body. -func (p Frame) Body() []byte { - return p[1:] -} - -/* - <<< FRAME >>> -*/ - -// WriteFrame writes a frame to a given Writer. -// A frame is structured as follows: -// - FrameSize (2 bytes) -// - FrameType (1 byte) -// - ChannelID (1 byte) -// - Data ([3,65535] bytes) -func WriteFrame(w io.Writer, data Frame) (int, error) { - if err := data.CheckSize(); err != nil { - return 0, err - } - var ( - size = uint16(len(data)) - packet = make([]byte, len(data)+2) - ) - binary.BigEndian.PutUint16(packet[:2], size) - copy(packet[2:], data) - n, err := w.Write(packet) - return n - 4, err -} - -// ReadFrame read and decrypts data from a reader. -func ReadFrame(r io.Reader) (Frame, int, error) { - // determine encrypted data size - size, err := readUint16(r) - if err != nil { - return Frame{}, 0, err - } - if size > MaxDataBodySize { - return Frame{}, 0, ErrDataTooLarge - } - // decrypt data - plainText := make([]byte, size) - n, err := io.ReadFull(r, plainText) - if err != nil { - return Frame{}, 0, err - } - - // return results - return Frame(plainText), n + 2, nil -} - -func readUint16(r io.Reader) (uint16, error) { - v := make([]byte, 2) - if _, err := io.ReadFull(r, v); err != nil { - return 0, err - } - return binary.BigEndian.Uint16(v), nil -} diff --git a/pkg/messaging/handshake.go b/pkg/messaging/handshake.go deleted file mode 100644 index 6107b155e4..0000000000 --- a/pkg/messaging/handshake.go +++ /dev/null @@ -1,189 +0,0 @@ -package messaging - -import ( - "encoding/hex" - "encoding/json" - "errors" - "fmt" - "time" - - "github.com/skycoin/skywire/pkg/cipher" -) - -// HandshakeNonceSize defines size of the nonce used in handshake. -const HandshakeNonceSize = 16 - -type sigType = byte - -const ( - sig1 sigType = iota - sig2 -) - -type handshakeFrame struct { - Version string `json:"version"` - Initiator cipher.PubKey `json:"initiator"` - Responder cipher.PubKey `json:"responder"` - Nonce string `json:"nonce"` - Sig1 cipher.Sig `json:"sig1,omitempty"` - Sig2 cipher.Sig `json:"sig2,omitempty"` - Accepted bool `json:"accepted,omitempty"` -} - -func newHandshakeFrame(initiator, responder cipher.PubKey) *handshakeFrame { - nonce := cipher.RandByte(HandshakeNonceSize) - return &handshakeFrame{ - Version: "0.1", - Initiator: initiator, - Responder: responder, - Nonce: hex.EncodeToString(nonce), - } -} - -func (p *handshakeFrame) toBinary() ([]byte, error) { - nonce, err := hex.DecodeString(p.Nonce) - if err != nil { - return nil, err - } - - buf := append([]byte(p.Version), p.Initiator[:]...) - buf = append(buf, p.Responder[:]...) - buf = append(buf, nonce...) - - if p.Sig1.Null() { - return buf, nil - } - - buf = append(buf, p.Sig1[:]...) - - if p.Sig2.Null() { - return buf, nil - } - - buf = append(buf, p.Sig2[:]...) - - return buf, nil -} - -func (p *handshakeFrame) signature(secKey cipher.SecKey) (sig cipher.Sig, err error) { - var bPayload []byte - bPayload, err = p.toBinary() - if err != nil { - return - } - - sig, err = cipher.SignPayload(bPayload, secKey) - if err != nil { - return - } - - return sig, err -} - -func (p *handshakeFrame) verifySignature(sig cipher.Sig, sigType sigType) error { - var pk cipher.PubKey - if sigType == sig1 { - pk = p.Responder - } else { - pk = p.Initiator - } - - bPayload, err := p.toBinary() - if err != nil { - return err - } - - return cipher.VerifyPubKeySignedPayload(pk, sig, bPayload) -} - -// Handshake represents a set of actions that an instance performs to complete a handshake. -type Handshake func(dec *json.Decoder, enc *json.Encoder) error - -// Do performs a handshake with a given timeout. -// Non-nil error is returned on failure. -func (handshake Handshake) Do(dec *json.Decoder, enc *json.Encoder, timeout time.Duration) (err error) { - done := make(chan struct{}) - go func() { - err = handshake(dec, enc) - close(done) - }() - select { - case <-done: - return err - case <-time.After(timeout): - return ErrHandshakeFailed - } -} - -func initiatorHandshake(c *LinkConfig) Handshake { - return func(dec *json.Decoder, enc *json.Encoder) error { - frame := newHandshakeFrame(c.Public, c.Remote) - if err := enc.Encode(frame); err != nil { - return err - } - - var resFrame *handshakeFrame - var err error - if err := dec.Decode(&resFrame); err != nil { - return err - } - - if err := frame.verifySignature(resFrame.Sig1, sig1); err != nil { - return fmt.Errorf("invalid sig1: %s", err) - } - - if resFrame.Sig2, err = resFrame.signature(c.Secret); err != nil { - return fmt.Errorf("failed to make sig2: %s", err) - } - - if err := enc.Encode(resFrame); err != nil { - return err - } - - if err := dec.Decode(&resFrame); err != nil { - return err - } - - if !resFrame.Accepted { - return errors.New("rejected") - } - - return nil - } -} - -func responderHandshake(c *LinkConfig) Handshake { - return func(dec *json.Decoder, enc *json.Encoder) error { - var frame *handshakeFrame - var err error - - if err := dec.Decode(&frame); err != nil { - return err - } - - if frame.Sig1, err = frame.signature(c.Secret); err != nil { - return fmt.Errorf("failed to make sig1: %s", err) - } - - if err := enc.Encode(frame); err != nil { - return err - } - - var resFrame *handshakeFrame - if err := dec.Decode(&resFrame); err != nil { - return err - } - - if err := frame.verifySignature(resFrame.Sig2, sig2); err != nil { - return fmt.Errorf("invalid sig2: %s", err) - } - - resFrame.Accepted = true - if err := enc.Encode(resFrame); err != nil { - return err - } - - c.Remote = frame.Initiator - return nil - } -} diff --git a/pkg/messaging/handshake_test.go b/pkg/messaging/handshake_test.go deleted file mode 100644 index e5c79a53b5..0000000000 --- a/pkg/messaging/handshake_test.go +++ /dev/null @@ -1,135 +0,0 @@ -package messaging - -import ( - "encoding/json" - "net" - "testing" - "time" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/skycoin/skywire/pkg/cipher" -) - -const handshakeTimeout = time.Second - -func TestHandshakeFrame(t *testing.T) { - initPK, initSK := cipher.GenerateKeyPair() - respPK, respSK := cipher.GenerateKeyPair() - frame := newHandshakeFrame(initPK, respPK) - - p, err := frame.toBinary() - require.NoError(t, err) - require.Len(t, p, 3+33+33+16) - - s1, err := frame.signature(respSK) - require.NoError(t, err) - require.NoError(t, frame.verifySignature(s1, sig1)) - - frame.Sig1 = s1 - p, err = frame.toBinary() - require.NoError(t, err) - require.Len(t, p, 3+33+33+16+65) - - s2, err := frame.signature(initSK) - require.NoError(t, err) - require.NoError(t, frame.verifySignature(s2, sig2)) - - frame.Sig2 = s2 - p, err = frame.toBinary() - require.NoError(t, err) - require.Len(t, p, 3+33+33+16+65+65) -} - -func TestHandshake(t *testing.T) { - initPK, initSK := cipher.GenerateKeyPair() - respPK, respSK := cipher.GenerateKeyPair() - initConf := &LinkConfig{ - Public: initPK, - Secret: initSK, - Remote: respPK, - } - respConf := &LinkConfig{ - Public: respPK, - Secret: respSK, - } - - initHandshake := initiatorHandshake(initConf) - respHandshake := responderHandshake(respConf) - - initNet, respNet := net.Pipe() - errCh := make(chan error) - go func() { - errCh <- respHandshake.Do(json.NewDecoder(respNet), json.NewEncoder(respNet), handshakeTimeout) - }() - - err := initHandshake.Do(json.NewDecoder(initNet), json.NewEncoder(initNet), handshakeTimeout) - require.NoError(t, err) - require.NoError(t, <-errCh) - assert.Equal(t, initPK, respConf.Remote) -} - -func TestHandshakeInvalidResponder(t *testing.T) { - initPK, initSK := cipher.GenerateKeyPair() - respPK, respSK := cipher.GenerateKeyPair() - anotherPK, _ := cipher.GenerateKeyPair() - initConf := &LinkConfig{ - Public: initPK, - Secret: initSK, - Remote: anotherPK, - } - respConf := &LinkConfig{ - Public: respPK, - Secret: respSK, - } - - initHandshake := initiatorHandshake(initConf) - respHandshake := responderHandshake(respConf) - - initNet, respNet := net.Pipe() - errCh := make(chan error) - go func() { - errCh <- respHandshake.Do(json.NewDecoder(respNet), json.NewEncoder(respNet), handshakeTimeout) - }() - - err := initHandshake.Do(json.NewDecoder(initNet), json.NewEncoder(initNet), handshakeTimeout) - require.Error(t, err) - assert.Equal(t, "invalid sig1: Recovered pubkey does not match pubkey", err.Error()) - - err = <-errCh - require.Error(t, err) - assert.Equal(t, "handshake failed", err.Error()) -} - -func TestHandshakeInvalidInitiator(t *testing.T) { - initPK, _ := cipher.GenerateKeyPair() - respPK, respSK := cipher.GenerateKeyPair() - _, anotherSK := cipher.GenerateKeyPair() - initConf := &LinkConfig{ - Public: initPK, - Secret: anotherSK, - Remote: respPK, - } - respConf := &LinkConfig{ - Public: respPK, - Secret: respSK, - } - - initHandshake := initiatorHandshake(initConf) - respHandshake := responderHandshake(respConf) - - initNet, respNet := net.Pipe() - errCh := make(chan error) - go func() { - errCh <- respHandshake.Do(json.NewDecoder(respNet), json.NewEncoder(respNet), handshakeTimeout) - }() - - err := initHandshake.Do(json.NewDecoder(initNet), json.NewEncoder(initNet), handshakeTimeout) - require.Error(t, err) - assert.Equal(t, "handshake failed", err.Error()) - - err = <-errCh - require.Error(t, err) - assert.Equal(t, "invalid sig2: Recovered pubkey does not match pubkey", err.Error()) -} diff --git a/pkg/messaging/link.go b/pkg/messaging/link.go deleted file mode 100644 index f1b724117b..0000000000 --- a/pkg/messaging/link.go +++ /dev/null @@ -1,185 +0,0 @@ -package messaging - -import ( - "encoding/json" - "fmt" - "io" - "sync" - "time" - - "github.com/skycoin/skycoin/src/util/logging" - - "github.com/skycoin/skywire/pkg/cipher" -) - -const ( - // DefaultHandshakeTimeout represents the default handshake timeout. - DefaultHandshakeTimeout = time.Second * 3 -) - -// LinkConfig represents the common config of a connection. -type LinkConfig struct { - Public cipher.PubKey - Secret cipher.SecKey - Remote cipher.PubKey // Public static key of the remote instance. - HandshakeTimeout time.Duration - Logger *logging.Logger - Initiator bool // Whether the local instance initiates the connection. -} - -// DefaultLinkConfig returns a connection configuration with default values. -func DefaultLinkConfig() *LinkConfig { - return &LinkConfig{ - HandshakeTimeout: DefaultHandshakeTimeout, - Logger: logging.MustGetLogger("link"), - } -} - -// Link represents a messaging connection in the perspective of an instance. -type Link struct { - rw io.ReadWriteCloser - config *LinkConfig - callbacks *Callbacks -} - -// NewLink creates a new Link. -func NewLink(rw io.ReadWriteCloser, config *LinkConfig, callbacks *Callbacks) (*Link, error) { - return &Link{ - rw: rw, - config: config, - callbacks: callbacks, - }, nil -} - -func (c *Link) logf(format string, a ...interface{}) { - if c.config.Logger != nil { - prefix := fmt.Sprintf("[%s::%s]", - c.config.Public, - c.config.Remote) - c.config.Logger.Info(prefix, fmt.Sprintf(format, a...)) - } -} - -// Open performs a handshake with the remote instance and attempts to establish a connection. -func (c *Link) Open(wg *sync.WaitGroup) error { - var handshake Handshake - if c.config.Initiator { - handshake = initiatorHandshake(c.config) - } else { - handshake = responderHandshake(c.config) - } - - // Perform handshake. - if err := handshake.Do(json.NewDecoder(c.rw), json.NewEncoder(c.rw), c.config.HandshakeTimeout); err != nil { - return err - } - - // Handshake complete callback. - c.callbacks.HandshakeComplete(c) - - // Event loops. - wg.Add(1) - go func() { - // Exits when connection is closed. - if err := c.readLoop(); err != nil { - c.logf("CLOSED: err(%v)", err) - } - // TODO(evanlinjin): Determine if the 'close' is initiated from remote instance. - c.callbacks.Close(c, false) - wg.Done() - }() - - return nil -} - -// Close closes the connection with the remote instance. -func (c *Link) Close() error { - return c.rw.Close() -} - -// SendOpenChannel sends OpenChannel request. -func (c *Link) SendOpenChannel(channelID byte, remotePK cipher.PubKey, noiseMsg []byte) (int, error) { - payload := append([]byte{channelID}, remotePK[:]...) - return c.writeFrame(FrameTypeOpenChannel, append(payload, noiseMsg...)) -} - -// SendChannelOpened sends ChannelOpened frame. -func (c *Link) SendChannelOpened(channelID byte, remoteID byte, noiseMsg []byte) (int, error) { - return c.writeFrame(FrameTypeChannelOpened, append([]byte{channelID, remoteID}, noiseMsg...)) -} - -// SendCloseChannel sends CloseChannel request. -func (c *Link) SendCloseChannel(remoteID byte) (int, error) { - return c.writeFrame(FrameTypeCloseChannel, []byte{remoteID}) -} - -// SendChannelClosed sends ChannelClosed frame. -func (c *Link) SendChannelClosed(remoteID byte) (int, error) { - return c.writeFrame(FrameTypeChannelClosed, []byte{remoteID}) -} - -// Send sends data frame. -func (c *Link) Send(channelID byte, body []byte) (int, error) { - return c.writeFrame(FrameTypeSend, append([]byte{channelID}, body...)) -} - -// Config returns the instance's read-only configuration. -func (c *Link) Config() *LinkConfig { - return c.config -} - -// Local returns the local PubKey. -func (c *Link) Local() cipher.PubKey { - return c.config.Public -} - -// Remote returns the remote PubKey. -func (c *Link) Remote() cipher.PubKey { - return c.config.Remote -} - -// Initiator returns whether the current instance is the initiator. -func (c *Link) Initiator() bool { - return c.config.Initiator -} - -// event loop that processing packets -// runs after handshake -func (c *Link) readLoop() error { - for { - switch err := c.handleData(ReadFrame(c.rw)); err { - case nil: - continue - case io.EOF: - return nil - default: - return err - } - } -} - -func (c *Link) handleData(payload Frame, n int, err error) error { - if err != nil { - return err - } - t, b := payload.Type(), payload.Body() - c.logf("RECEIVED: type(%d) bytes(%d)", t, n) - if err := c.readFrame(t, b); err != nil { - return err - } - return nil -} - -func (c *Link) writeFrame(t FrameType, body []byte) (int, error) { - payload := MakeFrame(t, body) - n, err := WriteFrame(c.rw, payload) - if err != nil { - return 0, err - } - c.logf(" SENT: type(%d) bytes(%d)", t, n) - return n, nil -} - -func (c *Link) readFrame(dt FrameType, body []byte) error { - return c.callbacks.Data(c, dt, body) -} diff --git a/pkg/messaging/link_test.go b/pkg/messaging/link_test.go deleted file mode 100644 index c7b5439fc8..0000000000 --- a/pkg/messaging/link_test.go +++ /dev/null @@ -1,159 +0,0 @@ -package messaging - -import ( - "fmt" - "io" - "sync" - "testing" - - "github.com/stretchr/testify/require" - - "github.com/skycoin/skywire/pkg/cipher" -) - -var ( - initPK, initSK = genKeyPair("initiator seed") - respPK, respSK = genKeyPair("responder seed") -) - -func genKeyPair(seed string) (cipher.PubKey, cipher.SecKey) { - pk, sk, err := cipher.GenerateDeterministicKeyPair([]byte(seed)) - if err != nil { - panic(err) - } - return pk, sk -} - -type callbacksModifier func(cb *Callbacks) - -type mockReadWriteCloser struct { - reader io.ReadCloser - writer io.WriteCloser -} - -func (rw mockReadWriteCloser) Read(p []byte) (n int, err error) { - return rw.reader.Read(p) -} - -func (rw mockReadWriteCloser) Write(p []byte) (n int, err error) { - return rw.writer.Write(p) -} - -func (rw mockReadWriteCloser) Close() error { - if err := rw.writer.Close(); err != nil { - return err - } - if err := rw.reader.Close(); err != nil { - return err - } - return nil -} - -func makeMockPipe() (a, b mockReadWriteCloser) { - a.reader, b.writer = io.Pipe() - b.reader, a.writer = io.Pipe() - return -} - -func makeConnPair(t *testing.T, - initPK, respPK cipher.PubKey, - initSK, respSK cipher.SecKey, - modifyInit, modifyResp callbacksModifier, -) (initConn *Link, respConn *Link) { - - initNet, respNet := makeMockPipe() - - initConfig := DefaultLinkConfig() - initConfig.Public = initPK - initConfig.Secret = initSK - initConfig.Remote = respPK - initConfig.Initiator = true - - respConfig := DefaultLinkConfig() - respConfig.Public = respPK - respConfig.Secret = respSK - - initCB := connCallbacks() - modifyInit(initCB) - - respCB := connCallbacks() - modifyResp(respCB) - - initConn, err := NewLink(&initNet, initConfig, initCB) - require.NoError(t, err) - - respConn, err = NewLink(&respNet, respConfig, respCB) - require.NoError(t, err) - - return initConn, respConn -} - -func TestNewConn(t *testing.T) { - // An initiator and responder can send messages to one another. - t.Run("send_messages_back_and_forth", func(t *testing.T) { - const expectedMsgCount = 10000 - - msgsWG := new(sync.WaitGroup) // records the send and receive event of each msg. - msgsWG.Add(expectedMsgCount * 2) // 10000 msgs x (send event + receive event) - - initConn, respConn := makeConnPair(t, - initPK, respPK, - initSK, respSK, - func(cb *Callbacks) { // Initiator callbacks. - cb.Data = func(conn *Link, dt FrameType, body []byte) error { - msgsWG.Done() - return nil - } - }, - func(cb *Callbacks) { // Responder callbacks. - cb.Data = func(conn *Link, dt FrameType, body []byte) error { - msgsWG.Done() - _, err := conn.Send(1, []byte(fmt.Sprintf("got msg: %s", string(body)))) - return err - } - }, - ) - - initConnOpenDone := make(chan error) - connsWG := new(sync.WaitGroup) - go func() { initConnOpenDone <- initConn.Open(connsWG) }() - require.NoError(t, respConn.Open(connsWG)) - require.NoError(t, <-initConnOpenDone) - - for i := 0; i < expectedMsgCount; i++ { - msg := fmt.Sprintf("Hello world %d!", i) - _, err := initConn.Send(1, []byte(msg)) - require.NoError(t, err) - } - - msgsWG.Wait() - - require.NoError(t, initConn.Close()) - connsWG.Wait() - }) -} - -func connHandshakeCompleteAction() HandshakeCompleteAction { - return func(conn *Link) {} -} - -func connMessageAction() FrameAction { - return func(conn *Link, dt FrameType, body []byte) error { - conn.logf("%s", string(body)) - return nil - } -} - -func connCloseAction() TCPCloseAction { - return func(conn *Link, remote bool) { - conn.logf("connection closed: Remote(%v)", remote) - } -} - -func connCallbacks() *Callbacks { - return &Callbacks{ - HandshakeComplete: connHandshakeCompleteAction(), - Data: connMessageAction(), - Close: connCloseAction(), - } -} diff --git a/pkg/messaging/pool.go b/pkg/messaging/pool.go deleted file mode 100644 index 122706768a..0000000000 --- a/pkg/messaging/pool.go +++ /dev/null @@ -1,227 +0,0 @@ -package messaging - -import ( - "errors" - "net" - "sync" - - "github.com/skycoin/skywire/pkg/cipher" -) - -var ( - // ErrConnExists occurs when a connection already exists. - ErrConnExists = errors.New("connection already exists") - - // ErrAlreadyListening occurs when a pool already has a listener. - ErrAlreadyListening = errors.New("pool is already listening") - - // ErrPoolClosed is returned by the Respond method after a call to Close. - ErrPoolClosed = errors.New("pool closed") -) - -// Pool represents a connection pool. -type Pool struct { - config *LinkConfig - conns map[cipher.PubKey]*Link - mux sync.RWMutex - wg sync.WaitGroup - - listenerMutex sync.RWMutex - listener net.Listener - - callbacks *Callbacks - doneChan chan struct{} -} - -// NewPool creates a new Pool. -func NewPool(config *LinkConfig, callbacks *Callbacks) *Pool { - var pool Pool - if config == nil { - config = DefaultLinkConfig() - } - pool.config = config - pool.conns = make(map[cipher.PubKey]*Link) - pool.callbacks = &Callbacks{ - HandshakeComplete: pool.handshakeCompleteAction(callbacks.HandshakeComplete), - Data: pool.messageAction(callbacks.Data), - Close: pool.closeAction(callbacks.Close), - } - pool.doneChan = make(chan struct{}) - return &pool -} - -// Initiate initiates a connection to a remote party. -func (p *Pool) Initiate(conn net.Conn, remoteID cipher.PubKey) (*Link, error) { - if link, ok := p.Link(remoteID); ok { - return link, ErrConnExists - } - link, err := NewLink(conn, - &LinkConfig{ - Public: p.config.Public, - Secret: p.config.Secret, - HandshakeTimeout: p.config.HandshakeTimeout, - Logger: p.config.Logger, - Remote: remoteID, - Initiator: true, - }, - p.callbacks) - if err != nil { - return nil, err - } - if err := link.Open(&p.wg); err != nil { - return nil, err - } - return link, nil -} - -// Listener returns the current listener used by the pool. -func (p *Pool) Listener() net.Listener { - p.listenerMutex.RLock() - defer p.listenerMutex.RUnlock() - return p.listener -} - -// Respond responds to remotely initiated connections accepting connections from the given net.Listener. -// This is a blocking call. -func (p *Pool) Respond(l net.Listener) error { - p.listenerMutex.Lock() - if p.listener != nil { - p.listenerMutex.Unlock() - return ErrAlreadyListening - } - p.listener = l - p.listenerMutex.Unlock() - - for { - c, err := p.listener.Accept() - if err != nil { - select { - case <-p.doneChan: - return ErrPoolClosed - default: - } - - return err - } - var conn *Link - conn, err = NewLink(c, - &LinkConfig{ - Public: p.config.Public, - Secret: p.config.Secret, - HandshakeTimeout: p.config.HandshakeTimeout, - Logger: p.config.Logger, - }, - p.callbacks) - if err != nil { - return err - } - // TODO(evanlinjin): Deal with a connection's failure. - _ = conn.Open(&p.wg) // nolint - } -} - -// Close closes the Pool. -func (p *Pool) Close() error { - p.closeDoneChan() - p.listenerMutex.Lock() - if p.listener != nil { - p.listener.Close() - } - p.listenerMutex.Unlock() - - p.mux.Lock() - for _, conn := range p.conns { - conn.Close() - } - p.mux.Unlock() - p.wg.Wait() - return nil -} - -// Link returns a connection of a given remote static public key. -func (p *Pool) Link(id cipher.PubKey) (*Link, bool) { - p.mux.RLock() - conn, ok := p.conns[id] - p.mux.RUnlock() - return conn, ok -} - -// All obtains all connections within pool. -func (p *Pool) All() []*Link { - p.mux.RLock() - out := make([]*Link, len(p.conns)) - i := 0 - for _, conn := range p.conns { - out[i] = conn - i++ - } - p.mux.RUnlock() - return out -} - -// ConnAction performs an action on a connection. -// If ok is true, ConnAction is to be called on the next connection. -type ConnAction func(id cipher.PubKey, conn *Link) (ok bool) - -// Range ranges over all connections within pool and exits on caller's instruction. -func (p *Pool) Range(action ConnAction) error { - p.mux.RLock() - for id, conn := range p.conns { - if ok := action(id, conn); !ok { - break - } - } - p.mux.RUnlock() - return nil -} - -func (p *Pool) setConn(id cipher.PubKey, conn *Link) { - p.mux.Lock() - p.conns[id] = conn - p.mux.Unlock() -} - -func (p *Pool) delConn(id cipher.PubKey) { - p.mux.Lock() - delete(p.conns, id) - p.mux.Unlock() -} - -func (p *Pool) closeDoneChan() { - select { - case <-p.doneChan: // already closed - default: - close(p.doneChan) - } -} - -func (p *Pool) handshakeCompleteAction(action HandshakeCompleteAction) HandshakeCompleteAction { - if action == nil { - action = func(conn *Link) {} - } - return func(conn *Link) { - p.setConn(conn.Remote(), conn) - action(conn) - } -} - -func (p *Pool) messageAction(action FrameAction) FrameAction { - if action == nil { - action = func(conn *Link, dt FrameType, body []byte) error { - return nil - } - } - return func(conn *Link, dt FrameType, body []byte) error { - return action(conn, dt, body) - } -} - -func (p *Pool) closeAction(action TCPCloseAction) TCPCloseAction { - if action == nil { - action = func(conn *Link, remote bool) {} - } - return func(conn *Link, remote bool) { - p.delConn(conn.Remote()) - action(conn, remote) - } -} diff --git a/pkg/messaging/pool_test.go b/pkg/messaging/pool_test.go deleted file mode 100644 index 26292fdd3e..0000000000 --- a/pkg/messaging/pool_test.go +++ /dev/null @@ -1,158 +0,0 @@ -// +build !no_ci - -package messaging - -import ( - "encoding/binary" - "net" - "sync" - "testing" - "time" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/skycoin/skywire/pkg/cipher" -) - -func generateResponderPool(t *testing.T, msgAction FrameAction) *Pool { - responder := NewPool(DefaultLinkConfig(), &Callbacks{Data: msgAction}) - pk, sk := cipher.GenerateKeyPair() - responder.config.Public = pk - responder.config.Secret = sk - - l, err := net.Listen("tcp", "127.0.0.1:0") - require.NoError(t, err) - go func() { - err := responder.Respond(l) - if err != nil && err != ErrPoolClosed { - t.Log(err) - } - }() - return responder -} - -func generateInitiatorPool(msgAction FrameAction) *Pool { - initiator := NewPool(DefaultLinkConfig(), &Callbacks{Data: msgAction}) - pk, sk := cipher.GenerateKeyPair() - initiator.config.Public = pk - initiator.config.Secret = sk - return initiator -} - -func TestNewPool(t *testing.T) { - const ( - Initiators = 10 - MsgsPerInitiator = 20 - MsgLen = 200 - ) - - var results [Initiators * MsgsPerInitiator][2][]byte - - wg := new(sync.WaitGroup) - wg.Add(Initiators * MsgsPerInitiator) - - responder := generateResponderPool(t, func(_ *Link, _ FrameType, msg []byte) error { - results[binary.BigEndian.Uint32(msg[1:5])][1] = msg[1:] - wg.Done() - return nil - }) - - for i := 0; i < Initiators; i++ { - initiator := generateInitiatorPool(connMessageAction()) - conn, err := net.Dial("tcp", responder.Listener().Addr().String()) - require.NoError(t, err) - link, err := initiator.Initiate(conn, responder.config.Public) - require.NoError(t, err) - - go func(i int, initiator *Pool, conn *Link) { - for j := 0; j < MsgsPerInitiator; j++ { - - msg := cipher.RandByte(MsgLen) - - resultsIdx := i*MsgsPerInitiator + j - binary.BigEndian.PutUint32(msg[:4], uint32(resultsIdx)) - - results[i*MsgsPerInitiator+j][0] = msg - - _, err = conn.Send(1, msg) - if err != nil { - t.Error(err) - } - } - initiator.Close() - }(i, initiator, link) - } - - wg.Wait() - - for _, result := range results { - assert.Equal(t, result[0], result[1]) - } - - responder.Close() -} - -func TestPool_Range(t *testing.T) { - const ( - InitiatorCount = 5 - ) - - responder := generateResponderPool(t, func(_ *Link, _ FrameType, _ []byte) error { return nil }) - - var initiators []*Pool - for i := 0; i < InitiatorCount; i++ { - initiator := generateInitiatorPool(connMessageAction()) - conn, err := net.Dial("tcp", responder.Listener().Addr().String()) - require.NoError(t, err) - _, err = initiator.Initiate(conn, responder.config.Public) - require.NoError(t, err) - initiators = append(initiators, initiator) - } - - time.Sleep(100 * time.Millisecond) - - // Expect 10 connections. - count := int(0) - err := responder.Range(func(_ cipher.PubKey, _ *Link) bool { - count++ - return true - }) - - require.NoError(t, err) - require.Equal(t, InitiatorCount, count) - - for _, initiator := range initiators { - initiator.Close() - } - responder.Close() -} - -func TestPool_All(t *testing.T) { - const ( - InitiatorCount = 5 - ) - - responder := generateResponderPool(t, func(_ *Link, _ FrameType, _ []byte) error { return nil }) - - var initiators []*Pool - for i := 0; i < InitiatorCount; i++ { - initiator := generateInitiatorPool(connMessageAction()) - conn, err := net.Dial("tcp", responder.Listener().Addr().String()) - require.NoError(t, err) - _, err = initiator.Initiate(conn, responder.config.Public) - require.NoError(t, err) - initiators = append(initiators, initiator) - } - - time.Sleep(100 * time.Millisecond) - - // Expect 10 connections. - all := responder.All() - require.Equal(t, InitiatorCount, len(all)) - - for _, initiator := range initiators { - initiator.Close() - } - responder.Close() -} diff --git a/pkg/route-finder/client/client.go b/pkg/route-finder/client/client.go index 1a6cfd2c66..61c4993355 100644 --- a/pkg/route-finder/client/client.go +++ b/pkg/route-finder/client/client.go @@ -11,7 +11,8 @@ import ( "strings" "time" - "github.com/skycoin/skywire/pkg/cipher" + "github.com/skycoin/dmsg/cipher" + "github.com/skycoin/skywire/pkg/routing" ) diff --git a/pkg/route-finder/client/mock.go b/pkg/route-finder/client/mock.go index 57914181e3..260a2144db 100644 --- a/pkg/route-finder/client/mock.go +++ b/pkg/route-finder/client/mock.go @@ -1,7 +1,8 @@ package client import ( - "github.com/skycoin/skywire/pkg/cipher" + "github.com/skycoin/dmsg/cipher" + "github.com/skycoin/skywire/pkg/routing" "github.com/skycoin/skywire/pkg/transport" ) diff --git a/pkg/router/app_manager_test.go b/pkg/router/app_manager_test.go index 66f3ea7931..041866db2a 100644 --- a/pkg/router/app_manager_test.go +++ b/pkg/router/app_manager_test.go @@ -4,12 +4,12 @@ import ( "net" "testing" + "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/app" - "github.com/skycoin/skywire/pkg/cipher" ) func TestAppManagerInit(t *testing.T) { diff --git a/pkg/router/loop_list.go b/pkg/router/loop_list.go index 7641f93dfc..c35881bad5 100644 --- a/pkg/router/loop_list.go +++ b/pkg/router/loop_list.go @@ -5,7 +5,6 @@ import ( "github.com/google/uuid" - "github.com/skycoin/skywire/internal/noise" "github.com/skycoin/skywire/pkg/app" "github.com/skycoin/skywire/pkg/routing" ) @@ -13,7 +12,6 @@ import ( type loop struct { trID uuid.UUID routeID routing.RouteID - noise *noise.Noise } type loopList struct { diff --git a/pkg/router/port_manager.go b/pkg/router/port_manager.go index 718ba9c883..b5d2019687 100644 --- a/pkg/router/port_manager.go +++ b/pkg/router/port_manager.go @@ -62,6 +62,10 @@ func (pm *portManager) AppPorts(appConn *app.Protocol) []uint16 { } func (pm *portManager) Close(port uint16) []app.Addr { + if pm == nil { + return nil + } + b := pm.ports.remove(port) if b == nil { return nil diff --git a/pkg/router/port_manager_test.go b/pkg/router/port_manager_test.go index b1a634a5ce..7815abe027 100644 --- a/pkg/router/port_manager_test.go +++ b/pkg/router/port_manager_test.go @@ -5,11 +5,11 @@ import ( "sort" "testing" + "github.com/skycoin/dmsg/cipher" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/skycoin/skywire/pkg/app" - "github.com/skycoin/skywire/pkg/cipher" ) func TestPortManager(t *testing.T) { diff --git a/pkg/router/route_manager.go b/pkg/router/route_manager.go index 96928b4660..e489c4606d 100644 --- a/pkg/router/route_manager.go +++ b/pkg/router/route_manager.go @@ -15,7 +15,7 @@ import ( ) type setupCallbacks struct { - ConfirmLoop func(addr *app.LoopAddr, rule routing.Rule, noiseMsg []byte) (noiseRes []byte, err error) + ConfirmLoop func(addr *app.LoopAddr, rule routing.Rule) (err error) LoopClosed func(addr *app.LoopAddr) error } @@ -89,7 +89,7 @@ func (rm *routeManager) Serve(rw io.ReadWriter) error { case setup.PacketDeleteRules: respBody, err = rm.deleteRoutingRules(body) case setup.PacketConfirmLoop: - respBody, err = rm.confirmLoop(body) + err = rm.confirmLoop(body) case setup.PacketLoopClosed: err = rm.loopClosed(body) default: @@ -97,7 +97,6 @@ func (rm *routeManager) Serve(rw io.ReadWriter) error { } if err != nil { - rm.Logger.Infof("Setup request with type %s failed: %s", t, err) return proto.WritePacket(setup.RespFailure, err.Error()) } @@ -141,17 +140,17 @@ func (rm *routeManager) deleteRoutingRules(data []byte) ([]routing.RouteID, erro return ruleIDs, nil } -func (rm *routeManager) confirmLoop(data []byte) (noiseRes []byte, err error) { +func (rm *routeManager) confirmLoop(data []byte) error { ld := setup.LoopData{} - if err = json.Unmarshal(data, &ld); err != nil { - return + if err := json.Unmarshal(data, &ld); err != nil { + return err } raddr := &app.Addr{PubKey: ld.RemotePK, Port: ld.RemotePort} var appRouteID routing.RouteID var appRule routing.Rule - err = rm.rt.RangeRules(func(routeID routing.RouteID, rule routing.Rule) bool { + err := rm.rt.RangeRules(func(routeID routing.RouteID, rule routing.Rule) bool { if rule.Type() != routing.RuleApp || rule.RemotePK() != ld.RemotePK || rule.RemotePort() != ld.RemotePort || rule.LocalPort() != ld.LocalPort { return true @@ -163,41 +162,34 @@ func (rm *routeManager) confirmLoop(data []byte) (noiseRes []byte, err error) { return false }) if err != nil { - err = fmt.Errorf("routing table: %s", err) - return + return fmt.Errorf("routing table: %s", err) } if appRule == nil { - err = errors.New("unknown loop") - return + return errors.New("unknown loop") } rule, err := rm.rt.Rule(ld.RouteID) if err != nil { - err = fmt.Errorf("routing table: %s", err) - return + return fmt.Errorf("routing table: %s", err) } if rule.Type() != routing.RuleForward { - err = errors.New("reverse rule is not forward") - return + return errors.New("reverse rule is not forward") } - msg, err := rm.callbacks.ConfirmLoop(&app.LoopAddr{Port: ld.LocalPort, Remote: *raddr}, rule, ld.NoiseMessage) - if err != nil { - err = fmt.Errorf("confirm: %s", err) - return + if err = rm.callbacks.ConfirmLoop(&app.LoopAddr{Port: ld.LocalPort, Remote: *raddr}, rule); err != nil { + return fmt.Errorf("confirm: %s", err) } rm.Logger.Infof("Setting reverse route ID %d for rule with ID %d", ld.RouteID, appRouteID) appRule.SetRouteID(ld.RouteID) if rErr := rm.rt.SetRule(appRouteID, appRule); rErr != nil { - err = fmt.Errorf("routing table: %s", rErr) - return + return fmt.Errorf("routing table: %s", rErr) } rm.Logger.Infof("Confirmed loop with %s:%d", ld.RemotePK, ld.RemotePort) - return msg, nil + return nil } func (rm *routeManager) loopClosed(data []byte) error { diff --git a/pkg/router/route_manager_test.go b/pkg/router/route_manager_test.go index 4d96b98f7e..6edde1a356 100644 --- a/pkg/router/route_manager_test.go +++ b/pkg/router/route_manager_test.go @@ -6,12 +6,12 @@ import ( "time" "github.com/google/uuid" + "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/app" - "github.com/skycoin/skywire/pkg/cipher" "github.com/skycoin/skywire/pkg/routing" "github.com/skycoin/skywire/pkg/setup" ) @@ -123,13 +123,11 @@ func TestRouteManagerConfirmLoop(t *testing.T) { rt := manageRoutingTable(routing.InMemoryRoutingTable()) var inAddr *app.LoopAddr var inRule routing.Rule - var noiseMsg []byte callbacks := &setupCallbacks{ - ConfirmLoop: func(addr *app.LoopAddr, rule routing.Rule, nMsg []byte) (noiseRes []byte, err error) { + ConfirmLoop: func(addr *app.LoopAddr, rule routing.Rule) (err error) { inAddr = addr inRule = rule - noiseMsg = nMsg - return []byte("foo"), nil + return nil }, } rm := &routeManager{logging.MustGetLogger("routesetup"), rt, callbacks} @@ -149,16 +147,13 @@ func TestRouteManagerConfirmLoop(t *testing.T) { require.NoError(t, rt.SetRule(1, rule)) ld := &setup.LoopData{ - RemotePK: pk, - RemotePort: 3, - LocalPort: 2, - RouteID: 1, - NoiseMessage: []byte("bar"), + RemotePK: pk, + RemotePort: 3, + LocalPort: 2, + RouteID: 1, } - noiseRes, err := setup.ConfirmLoop(proto, ld) + err := setup.ConfirmLoop(proto, ld) require.NoError(t, err) - assert.Equal(t, []byte("foo"), noiseRes) - assert.Equal(t, []byte("bar"), noiseMsg) assert.Equal(t, rule, inRule) assert.Equal(t, uint16(2), inAddr.Port) assert.Equal(t, uint16(3), inAddr.Remote.Port) @@ -196,11 +191,10 @@ func TestRouteManagerLoopClosed(t *testing.T) { require.NoError(t, rt.SetRule(1, rule)) ld := &setup.LoopData{ - RemotePK: pk, - RemotePort: 3, - LocalPort: 2, - RouteID: 1, - NoiseMessage: []byte("bar"), + RemotePK: pk, + RemotePort: 3, + LocalPort: 2, + RouteID: 1, } require.NoError(t, setup.LoopClosed(proto, ld)) assert.Equal(t, uint16(2), inAddr.Port) diff --git a/pkg/router/router.go b/pkg/router/router.go index 9d0a2bf5c4..cafab3bb39 100644 --- a/pkg/router/router.go +++ b/pkg/router/router.go @@ -10,13 +10,10 @@ import ( "sync" "time" - "github.com/skycoin/skywire/pkg/dmsg" - + "github.com/skycoin/dmsg" + "github.com/skycoin/dmsg/cipher" "github.com/skycoin/skycoin/src/util/logging" - "github.com/skycoin/skywire/pkg/cipher" - - "github.com/skycoin/skywire/internal/noise" "github.com/skycoin/skywire/pkg/app" routeFinder "github.com/skycoin/skywire/pkg/route-finder/client" "github.com/skycoin/skywire/pkg/routing" @@ -100,6 +97,7 @@ func (r *Router) Serve(ctx context.Context) error { } go func(tp transport.Transport) { + defer tp.Close() for { if err := serve(tp); err != nil { if err != io.EOF { @@ -164,6 +162,10 @@ func (r *Router) ServeApp(conn net.Conn, port uint16, appConf *app.Config) error // Close safely stops Router. func (r *Router) Close() error { + if r == nil { + return nil + } + r.Logger.Info("Closing all App connections and Loops") r.expiryTicker.Stop() @@ -216,17 +218,8 @@ func (r *Router) forwardPacket(payload []byte, rule routing.Rule) error { func (r *Router) consumePacket(payload []byte, rule routing.Rule) error { raddr := &app.Addr{PubKey: rule.RemotePK(), Port: rule.RemotePort()} - l, err := r.pm.GetLoop(rule.LocalPort(), raddr) - if err != nil { - return errors.New("unknown loop") - } - - data, err := l.noise.DecryptUnsafe(payload) - if err != nil { - return fmt.Errorf("noise: %s", err) - } - p := &app.Packet{Addr: &app.LoopAddr{Port: rule.LocalPort(), Remote: *raddr}, Payload: data} + p := &app.Packet{Addr: &app.LoopAddr{Port: rule.LocalPort(), Remote: *raddr}, Payload: payload} b, _ := r.pm.Get(rule.LocalPort()) // nolint: errcheck if err := b.conn.Send(app.FrameSend, p, nil); err != nil { return err @@ -251,7 +244,7 @@ func (r *Router) forwardAppPacket(appConn *app.Protocol, packet *app.Packet) err return errors.New("unknown transport") } - p := routing.MakePacket(l.routeID, l.noise.EncryptUnsafe(packet.Payload)) + p := routing.MakePacket(l.routeID, packet.Payload) r.Logger.Infof("Forwarded App packet from LocalPort %d using route ID %d", packet.Addr.Port, l.routeID) _, err = tr.Write(p) return err @@ -274,25 +267,8 @@ func (r *Router) forwardLocalAppPacket(packet *app.Packet) error { } func (r *Router) requestLoop(appConn *app.Protocol, raddr *app.Addr) (*app.Addr, error) { - r.Logger.Infof("Requesting new loop to %s", raddr) - nConf := noise.Config{ - LocalSK: r.config.SecKey, - LocalPK: r.config.PubKey, - RemotePK: raddr.PubKey, - Initiator: true, - } - ni, err := noise.KKAndSecp256k1(nConf) - if err != nil { - return nil, fmt.Errorf("noise: %s", err) - } - - msg, err := ni.HandshakeMessage() - if err != nil { - return nil, fmt.Errorf("noise handshake: %s", err) - } - lport := r.pm.Alloc(appConn) - if err := r.pm.SetLoop(lport, raddr, &loop{noise: ni}); err != nil { + if err := r.pm.SetLoop(lport, raddr, &loop{}); err != nil { return nil, err } @@ -311,7 +287,7 @@ func (r *Router) requestLoop(appConn *app.Protocol, raddr *app.Addr) (*app.Addr, } l := &routing.Loop{LocalPort: laddr.Port, RemotePort: raddr.Port, - NoiseMessage: msg, Expiry: time.Now().Add(RouteTTL), + Expiry: time.Now().Add(RouteTTL), Forward: forwardRoute, Reverse: reverseRoute} proto, tr, err := r.setupProto(context.Background()) @@ -342,19 +318,14 @@ func (r *Router) confirmLocalLoop(laddr, raddr *app.Addr) error { return nil } -func (r *Router) confirmLoop(addr *app.LoopAddr, rule routing.Rule, noiseMsg []byte) ([]byte, error) { +func (r *Router) confirmLoop(addr *app.LoopAddr, rule routing.Rule) error { b, err := r.pm.Get(addr.Port) if err != nil { - return nil, err - } - - ni, msg, err := r.advanceNoiseHandshake(addr, noiseMsg) - if err != nil { - return nil, fmt.Errorf("noise handshake: %s", err) + return err } - if err := r.pm.SetLoop(addr.Port, &addr.Remote, &loop{rule.TransportID(), rule.RouteID(), ni}); err != nil { - return nil, err + if err := r.pm.SetLoop(addr.Port, &addr.Remote, &loop{rule.TransportID(), rule.RouteID()}); err != nil { + return err } addrs := [2]*app.Addr{&app.Addr{PubKey: r.config.PubKey, Port: addr.Port}, &addr.Remote} @@ -362,7 +333,7 @@ func (r *Router) confirmLoop(addr *app.LoopAddr, rule routing.Rule, noiseMsg []b r.Logger.Warnf("Failed to notify App about new loop: %s", err) } - return msg, nil + return nil } func (r *Router) closeLoop(appConn *app.Protocol, addr *app.LoopAddr) error { @@ -425,7 +396,7 @@ func (r *Router) setupProto(ctx context.Context) (*setup.Protocol, transport.Tra // TODO(evanlinjin): need string constant for tp type. tr, err := r.tm.CreateTransport(ctx, r.config.SetupNodes[0], dmsg.Type, false) if err != nil { - return nil, nil, fmt.Errorf("transport: %s", err) + return nil, nil, fmt.Errorf("setup transport: %s", err) } sProto := setup.NewSetupProtocol(tr) @@ -453,36 +424,6 @@ fetchRoutesAgain: return fwdRoutes[0], revRoutes[0], nil } -func (r *Router) advanceNoiseHandshake(addr *app.LoopAddr, noiseMsg []byte) (ni *noise.Noise, noiseRes []byte, err error) { - var l *loop - l, _ = r.pm.GetLoop(addr.Port, &addr.Remote) // nolint: errcheck - - if l != nil && l.routeID != 0 { - err = errors.New("loop already exist") - return - } - - if l != nil && l.noise != nil { - return l.noise, nil, l.noise.ProcessMessage(noiseMsg) - } - - nConf := noise.Config{ - LocalSK: r.config.SecKey, - LocalPK: r.config.PubKey, - RemotePK: addr.Remote.PubKey, - Initiator: false, - } - ni, err = noise.KKAndSecp256k1(nConf) - if err != nil { - return - } - if err = ni.ProcessMessage(noiseMsg); err != nil { - return - } - noiseRes, err = ni.HandshakeMessage() - return -} - // IsSetupTransport checks whether `tr` is running in the `setup` mode. func (r *Router) IsSetupTransport(tr *transport.ManagedTransport) bool { for _, pk := range r.config.SetupNodes { diff --git a/pkg/router/router_test.go b/pkg/router/router_test.go index 11c7ef34f6..0fa8baabc4 100644 --- a/pkg/router/router_test.go +++ b/pkg/router/router_test.go @@ -10,15 +10,12 @@ import ( "testing" "time" - "github.com/skycoin/skywire/pkg/dmsg" - + "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/cipher" - - "github.com/skycoin/skywire/internal/noise" "github.com/skycoin/skywire/pkg/app" routeFinder "github.com/skycoin/skywire/pkg/route-finder/client" "github.com/skycoin/skywire/pkg/routing" @@ -87,7 +84,7 @@ func TestRouterForwarding(t *testing.T) { tr3, err := m3.CreateTransport(context.TODO(), pk2, "mock2", true) require.NoError(t, err) - rule := routing.ForwardRule(time.Now().Add(time.Hour), 4, tr3.ID) + rule := routing.ForwardRule(time.Now().Add(time.Hour), 4, tr3.Entry.ID) routeID, err := rt.AddRule(rule) require.NoError(t, err) @@ -197,23 +194,20 @@ func TestRouterApp(t *testing.T) { routeID, err := rt.AddRule(rule) require.NoError(t, err) - ni1, ni2 := noiseInstances(t, pk1, pk2, sk1, sk2) raddr := &app.Addr{PubKey: pk2, Port: 5} - require.NoError(t, r.pm.SetLoop(6, raddr, &loop{tr.ID, 4, ni1})) + require.NoError(t, r.pm.SetLoop(6, raddr, &loop{tr.Entry.ID, 4})) - tr2 := m2.Transport(tr.ID) + tr2 := m2.Transport(tr.Entry.ID) go proto.Send(app.FrameSend, &app.Packet{Addr: &app.LoopAddr{Port: 6, Remote: *raddr}, Payload: []byte("bar")}, nil) // nolint: errcheck - packet := make(routing.Packet, 29) + packet := make(routing.Packet, 9) _, err = tr2.Read(packet) require.NoError(t, err) - assert.Equal(t, uint16(23), packet.Size()) + assert.Equal(t, uint16(3), packet.Size()) assert.Equal(t, routing.RouteID(4), packet.RouteID()) - decrypted, err := ni2.DecryptUnsafe(packet.Payload()) - require.NoError(t, err) - assert.Equal(t, []byte("bar"), decrypted) + assert.Equal(t, []byte("bar"), packet.Payload()) - _, err = tr2.Write(routing.MakePacket(routeID, ni2.EncryptUnsafe([]byte("foo")))) + _, err = tr2.Write(routing.MakePacket(routeID, []byte("foo"))) require.NoError(t, err) time.Sleep(100 * time.Millisecond) @@ -335,34 +329,20 @@ func TestRouterSetup(t *testing.T) { var routeID routing.RouteID t.Run("add route", func(t *testing.T) { - routeID, err = setup.AddRule(sProto, routing.ForwardRule(time.Now().Add(time.Hour), 2, tr.ID)) + routeID, err = setup.AddRule(sProto, routing.ForwardRule(time.Now().Add(time.Hour), 2, tr.Entry.ID)) require.NoError(t, err) rule, err := rt.Rule(routeID) require.NoError(t, err) assert.Equal(t, routing.RouteID(2), rule.RouteID()) - assert.Equal(t, tr.ID, rule.TransportID()) + assert.Equal(t, tr.Entry.ID, rule.TransportID()) }) t.Run("`confirm loop - responder", func(t *testing.T) { - confI := noise.Config{ - LocalSK: sk2, - LocalPK: pk2, - RemotePK: pk1, - Initiator: true, - } - - ni, err := noise.KKAndSecp256k1(confI) - require.NoError(t, err) - msg, err := ni.HandshakeMessage() - require.NoError(t, err) - - time.Sleep(100 * time.Millisecond) - appRouteID, err := setup.AddRule(sProto, routing.AppRule(time.Now().Add(time.Hour), 0, pk2, 1, 2)) require.NoError(t, err) - noiseRes, err := setup.ConfirmLoop(sProto, &setup.LoopData{RemotePK: pk2, RemotePort: 1, LocalPort: 2, RouteID: routeID, NoiseMessage: msg}) + err = setup.ConfirmLoop(sProto, &setup.LoopData{RemotePK: pk2, RemotePort: 1, LocalPort: 2, RouteID: routeID}) require.NoError(t, err) rule, err := rt.Rule(appRouteID) @@ -373,7 +353,7 @@ func TestRouterSetup(t *testing.T) { loop, err := r.pm.GetLoop(2, &app.Addr{PubKey: pk2, Port: 1}) require.NoError(t, err) require.NotNil(t, loop) - assert.Equal(t, tr.ID, loop.trID) + assert.Equal(t, tr.Entry.ID, loop.trID) assert.Equal(t, routing.RouteID(2), loop.routeID) addrs := [2]*app.Addr{} @@ -383,44 +363,17 @@ func TestRouterSetup(t *testing.T) { assert.Equal(t, uint16(2), addrs[0].Port) assert.Equal(t, pk2, addrs[1].PubKey) assert.Equal(t, uint16(1), addrs[1].Port) - - require.NoError(t, ni.ProcessMessage(noiseRes)) }) t.Run("confirm loop - initiator", func(t *testing.T) { - confI := noise.Config{ - LocalSK: sk1, - LocalPK: pk1, - RemotePK: pk2, - Initiator: true, - } - - ni, err := noise.KKAndSecp256k1(confI) - require.NoError(t, err) - msg, err := ni.HandshakeMessage() - require.NoError(t, err) - - confR := noise.Config{ - LocalSK: sk2, - LocalPK: pk2, - RemotePK: pk1, - Initiator: false, - } - - nr, err := noise.KKAndSecp256k1(confR) - require.NoError(t, err) - require.NoError(t, nr.ProcessMessage(msg)) - noiseRes, err := nr.HandshakeMessage() - require.NoError(t, err) - time.Sleep(100 * time.Millisecond) - require.NoError(t, r.pm.SetLoop(4, &app.Addr{PubKey: pk2, Port: 3}, &loop{noise: ni})) + require.NoError(t, r.pm.SetLoop(4, &app.Addr{PubKey: pk2, Port: 3}, &loop{})) appRouteID, err := setup.AddRule(sProto, routing.AppRule(time.Now().Add(time.Hour), 0, pk2, 3, 4)) require.NoError(t, err) - _, err = setup.ConfirmLoop(sProto, &setup.LoopData{RemotePK: pk2, RemotePort: 3, LocalPort: 4, RouteID: routeID, NoiseMessage: noiseRes}) + err = setup.ConfirmLoop(sProto, &setup.LoopData{RemotePK: pk2, RemotePort: 3, LocalPort: 4, RouteID: routeID}) require.NoError(t, err) rule, err := rt.Rule(appRouteID) @@ -429,7 +382,7 @@ func TestRouterSetup(t *testing.T) { l, err := r.pm.GetLoop(2, &app.Addr{PubKey: pk2, Port: 1}) require.NoError(t, err) require.NotNil(t, l) - assert.Equal(t, tr.ID, l.trID) + assert.Equal(t, tr.Entry.ID, l.trID) assert.Equal(t, routing.RouteID(2), l.routeID) addrs := [2]*app.Addr{} @@ -544,7 +497,6 @@ func TestRouterSetupLoop(t *testing.T) { ll, err := r.pm.GetLoop(10, &app.Addr{PubKey: pk2, Port: 6}) require.NoError(t, err) require.NotNil(t, ll) - require.NotNil(t, ll.noise) assert.Equal(t, pk1, addr.PubKey) assert.Equal(t, uint16(10), addr.Port) @@ -788,37 +740,3 @@ func TestRouterRouteExpiration(t *testing.T) { assert.Equal(t, 0, rt.Count()) require.NoError(t, r.Close()) } - -func noiseInstances(t *testing.T, pkI, pkR cipher.PubKey, skI, skR cipher.SecKey) (ni, nr *noise.Noise) { - t.Helper() - - var err error - confI := noise.Config{ - LocalSK: skI, - LocalPK: pkI, - RemotePK: pkR, - Initiator: true, - } - - confR := noise.Config{ - LocalSK: skR, - LocalPK: pkR, - RemotePK: pkI, - Initiator: false, - } - - ni, err = noise.KKAndSecp256k1(confI) - require.NoError(t, err) - - nr, err = noise.KKAndSecp256k1(confR) - require.NoError(t, err) - - msg, err := ni.HandshakeMessage() - require.NoError(t, err) - require.NoError(t, nr.ProcessMessage(msg)) - - res, err := nr.HandshakeMessage() - require.NoError(t, err) - require.NoError(t, ni.ProcessMessage(res)) - return ni, nr -} diff --git a/pkg/routing/boltdb_routing_table.go b/pkg/routing/boltdb_routing_table.go index e2394740bc..1e08a13bde 100644 --- a/pkg/routing/boltdb_routing_table.go +++ b/pkg/routing/boltdb_routing_table.go @@ -142,6 +142,9 @@ func (rt *boltDBRoutingTable) Count() (count int) { // Close closes underlying BoltDB instance. func (rt *boltDBRoutingTable) Close() error { + if rt == nil { + return nil + } return rt.db.Close() } diff --git a/pkg/routing/loop.go b/pkg/routing/loop.go index 669a0d11a6..7fe922d4bb 100644 --- a/pkg/routing/loop.go +++ b/pkg/routing/loop.go @@ -4,17 +4,16 @@ import ( "fmt" "time" - "github.com/skycoin/skywire/pkg/cipher" + "github.com/skycoin/dmsg/cipher" ) // Loop defines a loop over a pair of routes. type Loop struct { - LocalPort uint16 - RemotePort uint16 - Forward Route - Reverse Route - Expiry time.Time - NoiseMessage []byte + LocalPort uint16 + RemotePort uint16 + Forward Route + Reverse Route + Expiry time.Time } // Initiator returns initiator of the Loop. diff --git a/pkg/routing/route.go b/pkg/routing/route.go index 68f2663eed..87dca45e7d 100644 --- a/pkg/routing/route.go +++ b/pkg/routing/route.go @@ -6,8 +6,7 @@ import ( "fmt" "github.com/google/uuid" - - "github.com/skycoin/skywire/pkg/cipher" + "github.com/skycoin/dmsg/cipher" ) // Hop defines a route hop between 2 nodes. diff --git a/pkg/routing/rule.go b/pkg/routing/rule.go index 14b40a08f7..b2b3c0e8e1 100644 --- a/pkg/routing/rule.go +++ b/pkg/routing/rule.go @@ -7,8 +7,7 @@ import ( "time" "github.com/google/uuid" - - "github.com/skycoin/skywire/pkg/cipher" + "github.com/skycoin/dmsg/cipher" ) // RuleType defines type of a routing rule diff --git a/pkg/routing/rule_test.go b/pkg/routing/rule_test.go index 3372a172f2..b54199cd63 100644 --- a/pkg/routing/rule_test.go +++ b/pkg/routing/rule_test.go @@ -5,9 +5,9 @@ import ( "time" "github.com/google/uuid" - "github.com/stretchr/testify/assert" + "github.com/skycoin/dmsg/cipher" - "github.com/skycoin/skywire/pkg/cipher" + "github.com/stretchr/testify/assert" ) func TestAppRule(t *testing.T) { diff --git a/pkg/setup/config.go b/pkg/setup/config.go index 2cd7d15554..65f8e2aba3 100644 --- a/pkg/setup/config.go +++ b/pkg/setup/config.go @@ -1,7 +1,7 @@ package setup import ( - "github.com/skycoin/skywire/pkg/cipher" + "github.com/skycoin/dmsg/cipher" ) // Config defines configuration parameters for setup Node. diff --git a/pkg/setup/node.go b/pkg/setup/node.go index 56fe1ed2e4..dcf0017877 100644 --- a/pkg/setup/node.go +++ b/pkg/setup/node.go @@ -8,15 +8,15 @@ import ( "log" "time" + "github.com/skycoin/dmsg/cipher" + "github.com/skycoin/dmsg/disc" "github.com/skycoin/skycoin/src/util/logging" - "github.com/skycoin/skywire/pkg/cipher" - "github.com/skycoin/skywire/pkg/dmsg" - mClient "github.com/skycoin/skywire/pkg/messaging-discovery/client" "github.com/skycoin/skywire/pkg/metrics" "github.com/skycoin/skywire/pkg/routing" "github.com/skycoin/skywire/pkg/transport" trClient "github.com/skycoin/skywire/pkg/transport-discovery/client" + "github.com/skycoin/skywire/pkg/transport/dmsg" ) // Hop is a wrapper around transport hop to add functionality @@ -45,7 +45,7 @@ func NewNode(conf *Config, metrics metrics.Recorder) (*Node, error) { if lvl, err := logging.LevelFromString(conf.LogLevel); err == nil { logger.SetLevel(lvl) } - messenger := dmsg.NewClient(pk, sk, mClient.NewHTTP(conf.Messaging.Discovery), dmsg.SetLogger(logger.PackageLogger(dmsg.Type))) + messenger := dmsg.NewClient(pk, sk, disc.NewHTTP(conf.Messaging.Discovery), dmsg.SetLogger(logger.PackageLogger(dmsg.Type))) trDiscovery, err := trClient.NewHTTP(conf.TransportDiscovery, pk, sk) if err != nil { @@ -121,15 +121,14 @@ func (sn *Node) createLoop(l *routing.Loop) error { initiator := l.Initiator() responder := l.Responder() - ldR := &LoopData{RemotePK: initiator, RemotePort: l.LocalPort, LocalPort: l.RemotePort, RouteID: rRouteID, NoiseMessage: l.NoiseMessage} - noiseRes, err := sn.connectLoop(responder, ldR) - if err != nil { + ldR := &LoopData{RemotePK: initiator, RemotePort: l.LocalPort, LocalPort: l.RemotePort, RouteID: rRouteID} + if err := sn.connectLoop(responder, ldR); err != nil { sn.Logger.Warnf("Failed to confirm loop with responder: %s", err) return fmt.Errorf("loop connect: %s", err) } - ldI := &LoopData{RemotePK: responder, RemotePort: l.RemotePort, LocalPort: l.LocalPort, RouteID: fRouteID, NoiseMessage: noiseRes} - if _, err := sn.connectLoop(initiator, ldI); err != nil { + ldI := &LoopData{RemotePK: responder, RemotePort: l.RemotePort, LocalPort: l.LocalPort, RouteID: fRouteID} + if err := sn.connectLoop(initiator, ldI); err != nil { sn.Logger.Warnf("Failed to confirm loop with initiator: %s", err) if err := sn.closeLoop(responder, ldR); err != nil { sn.Logger.Warnf("Failed to close loop: %s", err) @@ -181,6 +180,9 @@ func (sn *Node) createRoute(expireAt time.Time, route routing.Route, rport, lpor // Close closes underlying transport manager. func (sn *Node) Close() error { + if sn == nil { + return nil + } return sn.tm.Close() } @@ -222,22 +224,20 @@ func (sn *Node) serveTransport(tr transport.Transport) error { return proto.WritePacket(RespSuccess, nil) } -func (sn *Node) connectLoop(on cipher.PubKey, ld *LoopData) (noiseRes []byte, err error) { +func (sn *Node) connectLoop(on cipher.PubKey, ld *LoopData) error { tr, err := sn.tm.CreateTransport(context.Background(), on, dmsg.Type, false) if err != nil { - err = fmt.Errorf("transport: %s", err) - return + return fmt.Errorf("transport: %s", err) } defer tr.Close() proto := NewSetupProtocol(tr) - res, err := ConfirmLoop(proto, ld) - if err != nil { - return nil, err + if err := ConfirmLoop(proto, ld); err != nil { + return err } sn.Logger.Infof("Confirmed loop on %s with %s. RemotePort: %d. LocalPort: %d", on, ld.RemotePK, ld.RemotePort, ld.LocalPort) - return res, nil + return nil } func (sn *Node) closeLoop(on cipher.PubKey, ld *LoopData) error { diff --git a/pkg/setup/node_test.go b/pkg/setup/node_test.go index 02ae11f489..0713fb7da1 100644 --- a/pkg/setup/node_test.go +++ b/pkg/setup/node_test.go @@ -10,15 +10,13 @@ import ( "testing" "time" - "github.com/skycoin/skywire/pkg/dmsg" - - "github.com/skycoin/skywire/pkg/metrics" - + "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/cipher" + "github.com/skycoin/skywire/pkg/metrics" "github.com/skycoin/skywire/pkg/routing" "github.com/skycoin/skywire/pkg/transport" ) @@ -101,12 +99,12 @@ func TestCreateLoop(t *testing.T) { l := &routing.Loop{LocalPort: 1, RemotePort: 2, Expiry: time.Now().Add(time.Hour), Forward: routing.Route{ - &routing.Hop{From: pk1, To: pk2, Transport: tr1.ID}, - &routing.Hop{From: pk2, To: pk3, Transport: tr3.ID}, + &routing.Hop{From: pk1, To: pk2, Transport: tr1.Entry.ID}, + &routing.Hop{From: pk2, To: pk3, Transport: tr3.Entry.ID}, }, Reverse: routing.Route{ - &routing.Hop{From: pk3, To: pk2, Transport: tr3.ID}, - &routing.Hop{From: pk2, To: pk1, Transport: tr1.ID}, + &routing.Hop{From: pk3, To: pk2, Transport: tr3.Entry.ID}, + &routing.Hop{From: pk2, To: pk1, Transport: tr1.Entry.ID}, }, } @@ -134,25 +132,25 @@ func TestCreateLoop(t *testing.T) { assert.Equal(t, uint16(1), rule.LocalPort()) rule = rules[2] assert.Equal(t, routing.RuleForward, rule.Type()) - assert.Equal(t, tr1.ID, rule.TransportID()) + assert.Equal(t, tr1.Entry.ID, rule.TransportID()) assert.Equal(t, routing.RouteID(2), rule.RouteID()) rules = n2.getRules() require.Len(t, rules, 2) rule = rules[1] assert.Equal(t, routing.RuleForward, rule.Type()) - assert.Equal(t, tr1.ID, rule.TransportID()) + assert.Equal(t, tr1.Entry.ID, rule.TransportID()) assert.Equal(t, routing.RouteID(1), rule.RouteID()) rule = rules[2] assert.Equal(t, routing.RuleForward, rule.Type()) - assert.Equal(t, tr3.ID, rule.TransportID()) + assert.Equal(t, tr3.Entry.ID, rule.TransportID()) assert.Equal(t, routing.RouteID(2), rule.RouteID()) rules = n3.getRules() require.Len(t, rules, 2) rule = rules[1] assert.Equal(t, routing.RuleForward, rule.Type()) - assert.Equal(t, tr3.ID, rule.TransportID()) + assert.Equal(t, tr3.Entry.ID, rule.TransportID()) assert.Equal(t, routing.RouteID(1), rule.RouteID()) rule = rules[2] assert.Equal(t, routing.RuleApp, rule.Type()) @@ -263,6 +261,10 @@ func (f *muxFactory) Dial(ctx context.Context, remote cipher.PubKey) (transport. } func (f *muxFactory) Close() error { + if f == nil { + return nil + } + var err error for _, factory := range f.factories { if fErr := factory.Close(); err == nil && fErr != nil { diff --git a/pkg/setup/protocol.go b/pkg/setup/protocol.go index 87d8270300..e7831fe102 100644 --- a/pkg/setup/protocol.go +++ b/pkg/setup/protocol.go @@ -9,7 +9,8 @@ import ( "fmt" "io" - "github.com/skycoin/skywire/pkg/cipher" + "github.com/skycoin/dmsg/cipher" + "github.com/skycoin/skywire/pkg/routing" ) @@ -56,11 +57,10 @@ const ( // LoopData stores loop confirmation request data. type LoopData struct { - RemotePK cipher.PubKey `json:"remote-pk"` - RemotePort uint16 `json:"remote-port"` - LocalPort uint16 `json:"local-port"` - RouteID routing.RouteID `json:"resp-rid,omitempty"` - NoiseMessage []byte `json:"noise-msg,omitempty"` + RemotePK cipher.PubKey `json:"remote-pk"` + RemotePort uint16 `json:"remote-port"` + LocalPort uint16 `json:"local-port"` + RouteID routing.RouteID `json:"resp-rid,omitempty"` } // Protocol defines routes setup protocol. @@ -146,22 +146,15 @@ func CreateLoop(p *Protocol, l *routing.Loop) error { if err := p.WritePacket(PacketCreateLoop, l); err != nil { return err } - if err := readAndDecodePacket(p, nil); err != nil { // TODO: data race. - return err - } - return nil + return readAndDecodePacket(p, nil) // TODO: data race. } // ConfirmLoop sends ConfirmLoop setup request. -func ConfirmLoop(p *Protocol, l *LoopData) (noiseRes []byte, err error) { - if err = p.WritePacket(PacketConfirmLoop, l); err != nil { - return - } - var res []byte - if err = readAndDecodePacket(p, &res); err != nil { - return +func ConfirmLoop(p *Protocol, l *LoopData) error { + if err := p.WritePacket(PacketConfirmLoop, l); err != nil { + return err } - return res, nil + return readAndDecodePacket(p, nil) } // CloseLoop sends CloseLoop setup request. @@ -169,10 +162,7 @@ func CloseLoop(p *Protocol, l *LoopData) error { if err := p.WritePacket(PacketCloseLoop, l); err != nil { return err } - if err := readAndDecodePacket(p, nil); err != nil { - return err - } - return nil + return readAndDecodePacket(p, nil) } // LoopClosed sends LoopClosed setup request. @@ -180,10 +170,7 @@ func LoopClosed(p *Protocol, l *LoopData) error { if err := p.WritePacket(PacketLoopClosed, l); err != nil { return err } - if err := readAndDecodePacket(p, nil); err != nil { - return err - } - return nil + return readAndDecodePacket(p, nil) } func readAndDecodePacket(p *Protocol, v interface{}) error { @@ -197,8 +184,5 @@ func readAndDecodePacket(p *Protocol, v interface{}) error { if v == nil { return nil } - if err = json.Unmarshal(raw, v); err != nil { - return err - } - return nil + return json.Unmarshal(raw, v) } diff --git a/pkg/transport-discovery/client/client.go b/pkg/transport-discovery/client/client.go index e4d7180822..2fe923a6d5 100644 --- a/pkg/transport-discovery/client/client.go +++ b/pkg/transport-discovery/client/client.go @@ -12,8 +12,7 @@ import ( "net/http" "github.com/google/uuid" - - "github.com/skycoin/skywire/pkg/cipher" + "github.com/skycoin/dmsg/cipher" "github.com/skycoin/skywire/internal/httpauth" "github.com/skycoin/skywire/pkg/transport" diff --git a/pkg/transport-discovery/client/client_test.go b/pkg/transport-discovery/client/client_test.go index 71dd9b95a4..9b7c0afabc 100644 --- a/pkg/transport-discovery/client/client_test.go +++ b/pkg/transport-discovery/client/client_test.go @@ -12,13 +12,11 @@ import ( "sync" "testing" + "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/skycoin/src/util/logging" - - "github.com/skycoin/skywire/pkg/cipher" - "github.com/skycoin/skywire/internal/httpauth" "github.com/skycoin/skywire/pkg/transport" ) diff --git a/pkg/transport/discovery.go b/pkg/transport/discovery.go index 2952306552..1597dc53f0 100644 --- a/pkg/transport/discovery.go +++ b/pkg/transport/discovery.go @@ -7,8 +7,7 @@ import ( "time" "github.com/google/uuid" - - "github.com/skycoin/skywire/pkg/cipher" + "github.com/skycoin/dmsg/cipher" ) // DiscoveryClient performs Transport discovery operations. diff --git a/pkg/transport/discovery_test.go b/pkg/transport/discovery_test.go index 190f86de42..e3595d10db 100644 --- a/pkg/transport/discovery_test.go +++ b/pkg/transport/discovery_test.go @@ -4,7 +4,8 @@ import ( "context" "fmt" - "github.com/skycoin/skywire/pkg/cipher" + "github.com/skycoin/dmsg/cipher" + "github.com/skycoin/skywire/pkg/transport" ) diff --git a/pkg/transport/dmsg/client.go b/pkg/transport/dmsg/client.go new file mode 100644 index 0000000000..c40bddb1d0 --- /dev/null +++ b/pkg/transport/dmsg/client.go @@ -0,0 +1,71 @@ +package dmsg + +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/transport" +) + +const ( + // Type is a wrapper type for "github.com/skycoin/dmsg".Type + Type = dmsg.Type +) + +// Client is a wrapper type for "github.com/skycoin/dmsg".Client +type Client struct { + *dmsg.Client +} + +// ClientOption is a wrapper type for "github.com/skycoin/dmsg".ClientOption +type ClientOption = dmsg.ClientOption + +// NewClient is a wrapper type for "github.com/skycoin/dmsg".NewClient +func NewClient(pk cipher.PubKey, sk cipher.SecKey, dc disc.APIClient, opts ...ClientOption) *Client { + return &Client{ + Client: dmsg.NewClient(pk, sk, dc, opts...), + } +} + +// Accept is a wrapper type for "github.com/skycoin/dmsg".Accept +func (c *Client) Accept(ctx context.Context) (transport.Transport, error) { + tp, err := c.Client.Accept(ctx) + if err != nil { + return nil, err + } + + return NewTransport(tp), nil +} + +// Dial is a wrapper type for "github.com/skycoin/dmsg".Dial +func (c *Client) Dial(ctx context.Context, remote cipher.PubKey) (transport.Transport, error) { + tp, err := c.Client.Dial(ctx, remote) + if err != nil { + return nil, err + } + return NewTransport(tp), nil +} + +// Close is a wrapper type for "github.com/skycoin/dmsg".Close +func (c *Client) Close() error { + return c.Client.Close() +} + +// Local is a wrapper type for "github.com/skycoin/dmsg".Local +func (c *Client) Local() cipher.PubKey { + return c.Client.Local() +} + +// Type is a wrapper type for "github.com/skycoin/dmsg".Type +func (c *Client) Type() string { + return c.Client.Type() +} + +// SetLogger is a wrapper type for "github.com/skycoin/dmsg".SetLogger +func SetLogger(log *logging.Logger) ClientOption { + return dmsg.SetLogger(log) +} diff --git a/pkg/transport/dmsg/config.go b/pkg/transport/dmsg/config.go new file mode 100644 index 0000000000..44dc2a4268 --- /dev/null +++ b/pkg/transport/dmsg/config.go @@ -0,0 +1,17 @@ +package dmsg + +import ( + "time" + + "github.com/skycoin/dmsg/cipher" + "github.com/skycoin/dmsg/disc" +) + +// Config configures dmsg +type Config struct { + PubKey cipher.PubKey + SecKey cipher.SecKey + Discovery disc.APIClient + Retries int + RetryDelay time.Duration +} diff --git a/pkg/transport/dmsg/transport.go b/pkg/transport/dmsg/transport.go new file mode 100644 index 0000000000..cbf90a9ba8 --- /dev/null +++ b/pkg/transport/dmsg/transport.go @@ -0,0 +1,50 @@ +package dmsg + +import ( + "time" + + "github.com/skycoin/dmsg" + "github.com/skycoin/dmsg/cipher" + + "github.com/skycoin/skywire/pkg/transport" +) + +// Transport is a wrapper type for "github.com/skycoin/dmsg".Transport +type Transport struct { + *dmsg.Transport +} + +// NewTransport creates a new Transport. +func NewTransport(tp *dmsg.Transport) *Transport { + return &Transport{Transport: tp} +} + +// Read is a wrapper for "github.com/skycoin/dmsg".(*Transport).Read +func (tp *Transport) Read(p []byte) (n int, err error) { + return tp.Transport.Read(p) +} + +// Write is a wrapper for "github.com/skycoin/dmsg".(*Transport).Write +func (tp *Transport) Write(p []byte) (n int, err error) { + return tp.Transport.Write(p) +} + +// Close is a wrapper for "github.com/skycoin/dmsg".(*Transport).Close +func (tp *Transport) Close() error { + return tp.Transport.Close() +} + +// Edges returns sorted edges of transport +func (tp *Transport) Edges() [2]cipher.PubKey { + return transport.SortPubKeys(tp.LocalPK(), tp.RemotePK()) +} + +// SetDeadline is a wrapper for "github.com/skycoin/dmsg".(*Transport).SetDeadline +func (tp *Transport) SetDeadline(t time.Time) error { + return tp.Transport.SetDeadline(t) +} + +// Type is a wrapper for "github.com/skycoin/dmsg".(*Transport).Type +func (tp *Transport) Type() string { + return tp.Transport.Type() +} diff --git a/pkg/transport/entry.go b/pkg/transport/entry.go index 35313609e6..3648971e3b 100644 --- a/pkg/transport/entry.go +++ b/pkg/transport/entry.go @@ -5,8 +5,7 @@ import ( "strings" "github.com/google/uuid" - - "github.com/skycoin/skywire/pkg/cipher" + "github.com/skycoin/dmsg/cipher" ) // Entry is the unsigned representation of a Transport. diff --git a/pkg/transport/entry_test.go b/pkg/transport/entry_test.go index 8e882ffc49..dc730c745b 100644 --- a/pkg/transport/entry_test.go +++ b/pkg/transport/entry_test.go @@ -4,8 +4,8 @@ import ( "fmt" "github.com/google/uuid" + "github.com/skycoin/dmsg/cipher" - "github.com/skycoin/skywire/pkg/cipher" "github.com/skycoin/skywire/pkg/transport" ) diff --git a/pkg/transport/handshake.go b/pkg/transport/handshake.go index 10dc1a1dd9..7140252d00 100644 --- a/pkg/transport/handshake.go +++ b/pkg/transport/handshake.go @@ -8,7 +8,7 @@ import ( "io" "time" - "github.com/skycoin/skywire/pkg/cipher" + "github.com/skycoin/dmsg/cipher" ) type settlementHandshake func(tm *Manager, tr Transport) (*Entry, error) diff --git a/pkg/transport/handshake_test.go b/pkg/transport/handshake_test.go index 86e639ecc1..2d2cf6f000 100644 --- a/pkg/transport/handshake_test.go +++ b/pkg/transport/handshake_test.go @@ -6,10 +6,9 @@ import ( "net" "testing" + "github.com/skycoin/dmsg/cipher" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - - "github.com/skycoin/skywire/pkg/cipher" ) type hsMockEnv struct { diff --git a/pkg/transport/log.go b/pkg/transport/log.go index 8ae0b1bc32..1983b0ab42 100644 --- a/pkg/transport/log.go +++ b/pkg/transport/log.go @@ -1,12 +1,16 @@ package transport import ( + "bytes" + "encoding/gob" "encoding/json" + "errors" "fmt" - "math/big" "os" "path/filepath" + "strconv" "sync" + "sync/atomic" "github.com/google/uuid" ) @@ -14,8 +18,55 @@ import ( // LogEntry represents a logging entry for a given Transport. // The entry is updated every time a packet is received or sent. type LogEntry struct { - ReceivedBytes *big.Int `json:"received"` // Total received bytes. - SentBytes *big.Int `json:"sent"` // Total sent bytes. + RecvBytes uint64 `json:"recv"` // Total received bytes. + SentBytes uint64 `json:"sent"` // Total sent bytes. +} + +// AddRecv records read. +func (le *LogEntry) AddRecv(n uint64) { + atomic.AddUint64(&le.RecvBytes, n) +} + +// AddSent records write. +func (le *LogEntry) AddSent(n uint64) { + atomic.AddUint64(&le.SentBytes, n) +} + +// MarshalJSON implements json.Marshaller +func (le *LogEntry) MarshalJSON() ([]byte, error) { + rb := strconv.FormatUint(atomic.LoadUint64(&le.RecvBytes), 10) + sb := strconv.FormatUint(atomic.LoadUint64(&le.SentBytes), 10) + return []byte(`{"recv":` + rb + `,"sent":` + sb + `}`), nil +} + +// GobEncode implements gob.GobEncoder +func (le *LogEntry) GobEncode() ([]byte, error) { + var b bytes.Buffer + enc := gob.NewEncoder(&b) + if err := enc.Encode(le.RecvBytes); err != nil { + return nil, err + } + if err := enc.Encode(le.SentBytes); err != nil { + return nil, err + } + return b.Bytes(), nil +} + +// GobDecode implements gob.GobDecoder +func (le *LogEntry) GobDecode(b []byte) error { + r := bytes.NewReader(b) + dec := gob.NewDecoder(r) + var rb uint64 + if err := dec.Decode(&rb); err != nil { + return err + } + var sb uint64 + if err := dec.Decode(&sb); err != nil { + return err + } + atomic.StoreUint64(&le.RecvBytes, rb) + atomic.StoreUint64(&le.SentBytes, sb) + return nil } // LogStore stores transport log entries. @@ -32,14 +83,17 @@ type inMemoryTransportLogStore struct { // InMemoryTransportLogStore implements in-memory TransportLogStore. func InMemoryTransportLogStore() LogStore { return &inMemoryTransportLogStore{ - entries: map[uuid.UUID]*LogEntry{}, + entries: make(map[uuid.UUID]*LogEntry), } } func (tls *inMemoryTransportLogStore) Entry(id uuid.UUID) (*LogEntry, error) { tls.mu.Lock() - entry := tls.entries[id] + entry, ok := tls.entries[id] tls.mu.Unlock() + if !ok { + return entry, errors.New("transport log entry not found") + } return entry, nil } diff --git a/pkg/transport/log_test.go b/pkg/transport/log_test.go index b118f57deb..1c3f577728 100644 --- a/pkg/transport/log_test.go +++ b/pkg/transport/log_test.go @@ -1,8 +1,9 @@ package transport_test import ( + "encoding/json" + "fmt" "io/ioutil" - "math/big" "os" "testing" @@ -17,17 +18,22 @@ func testTransportLogStore(t *testing.T, logStore transport.LogStore) { t.Helper() id1 := uuid.New() - entry1 := &transport.LogEntry{big.NewInt(100), big.NewInt(200)} + entry1 := new(transport.LogEntry) + entry1.AddRecv(100) + entry1.AddSent(200) + id2 := uuid.New() - entry2 := &transport.LogEntry{big.NewInt(300), big.NewInt(400)} + entry2 := new(transport.LogEntry) + entry2.AddRecv(300) + entry2.AddSent(400) require.NoError(t, logStore.Record(id1, entry1)) require.NoError(t, logStore.Record(id2, entry2)) entry, err := logStore.Entry(id2) require.NoError(t, err) - assert.Equal(t, int64(300), entry.ReceivedBytes.Int64()) - assert.Equal(t, int64(400), entry.SentBytes.Int64()) + assert.Equal(t, uint64(300), entry.RecvBytes) + assert.Equal(t, uint64(400), entry.SentBytes) } func TestInMemoryTransportLogStore(t *testing.T) { @@ -43,3 +49,24 @@ func TestFileTransportLogStore(t *testing.T) { require.NoError(t, err) testTransportLogStore(t, ls) } + +func TestLogEntry_MarshalJSON(t *testing.T) { + entry := new(transport.LogEntry) + entry.AddSent(10) + entry.AddRecv(100) + b, err := json.Marshal(entry) + require.NoError(t, err) + fmt.Println(string(b)) + b, err = json.MarshalIndent(entry, "", "\t") + require.NoError(t, err) + fmt.Println(string(b)) +} + +func TestLogEntry_GobEncode(t *testing.T) { + var entry transport.LogEntry + + enc, err := entry.GobEncode() + require.NoError(t, err) + + require.NoError(t, entry.GobDecode(enc)) +} diff --git a/pkg/transport/managed_transport.go b/pkg/transport/managed_transport.go index 0873f9b2d4..66a675fa5e 100644 --- a/pkg/transport/managed_transport.go +++ b/pkg/transport/managed_transport.go @@ -1,102 +1,108 @@ package transport import ( - "math/big" + "context" "sync" - - "github.com/google/uuid" + "time" ) +const logWriteInterval = time.Second * 3 + // ManagedTransport is a wrapper transport. It stores status and ID of // the Transport and can notify about network errors. type ManagedTransport struct { Transport - ID uuid.UUID - Public bool + Entry Entry Accepted bool + Setup bool LogEntry *LogEntry - doneChan chan struct{} - errChan chan error - mu sync.RWMutex - once sync.Once - - readLogChan chan int - writeLogChan chan int + done chan struct{} + update chan error + mu sync.RWMutex + once sync.Once } -func newManagedTransport(id uuid.UUID, tr Transport, public bool, accepted bool) *ManagedTransport { +func newManagedTransport(tr Transport, entry Entry, accepted bool) *ManagedTransport { return &ManagedTransport{ - ID: id, - Transport: tr, - Public: public, - Accepted: accepted, - doneChan: make(chan struct{}), - errChan: make(chan error), - readLogChan: make(chan int, 16), - writeLogChan: make(chan int, 16), - LogEntry: &LogEntry{new(big.Int), new(big.Int)}, + Transport: tr, + Entry: entry, + Accepted: accepted, + done: make(chan struct{}), + update: make(chan error, 16), + LogEntry: new(LogEntry), } } -// Read reads using underlying +// Read reads using underlying transport. func (tr *ManagedTransport) Read(p []byte) (n int, err error) { tr.mu.RLock() - n, err = tr.Transport.Read(p) // TODO: data race. - tr.mu.RUnlock() - - if err != nil { - tr.errChan <- err + n, err = tr.Transport.Read(p) + if n > 0 { + tr.LogEntry.AddRecv(uint64(n)) } - - tr.readLogChan <- n + if !tr.isClosing() { + select { + case tr.update <- err: + default: + } + } + tr.mu.RUnlock() return } -// Write writes to an underlying +// Write writes to an underlying transport. func (tr *ManagedTransport) Write(p []byte) (n int, err error) { tr.mu.RLock() n, err = tr.Transport.Write(p) - tr.mu.RUnlock() - - if err != nil { - tr.errChan <- err - return + if n > 0 { + tr.LogEntry.AddSent(uint64(n)) } - tr.writeLogChan <- n - + if !tr.isClosing() { + select { + case tr.update <- err: + default: + } + } + tr.mu.RUnlock() return } -// killWorker sends signal to Manager.manageTransport goroutine to exit -// it's safe to call it multiple times func (tr *ManagedTransport) killWorker() { tr.once.Do(func() { - close(tr.doneChan) + close(tr.done) }) } -// Close closes underlying -func (tr *ManagedTransport) Close() error { - tr.mu.RLock() - err := tr.Transport.Close() - tr.mu.RUnlock() +func (tr *ManagedTransport) killUpdate() { + tr.mu.Lock() + close(tr.update) + tr.update = nil + tr.mu.Unlock() +} +// Close closes underlying transport and kills worker. +func (tr *ManagedTransport) Close() error { + if tr == nil { + return nil + } tr.killWorker() - return err + return tr.Transport.Close() } func (tr *ManagedTransport) isClosing() bool { select { - case <-tr.doneChan: + case <-tr.done: return true default: return false } } -func (tr *ManagedTransport) updateTransport(newTr Transport) { +func (tr *ManagedTransport) updateTransport(ctx context.Context, newTr Transport, dc DiscoveryClient) error { tr.mu.Lock() tr.Transport = newTr + _, err := dc.UpdateStatuses(ctx, &Status{ID: tr.Entry.ID, IsUp: true, Updated: time.Now().UnixNano()}) tr.mu.Unlock() + return err } diff --git a/pkg/transport/manager.go b/pkg/transport/manager.go index f828f87206..65bd6b7617 100644 --- a/pkg/transport/manager.go +++ b/pkg/transport/manager.go @@ -4,16 +4,14 @@ import ( "context" "errors" "io" - "math/big" "strings" "sync" "sync/atomic" "time" "github.com/google/uuid" + "github.com/skycoin/dmsg/cipher" "github.com/skycoin/skycoin/src/util/logging" - - "github.com/skycoin/skywire/pkg/cipher" ) // ManagerConfig configures a Manager. @@ -211,8 +209,8 @@ func (tm *Manager) CreateTransport(ctx context.Context, remote cipher.PubKey, tp func (tm *Manager) DeleteTransport(id uuid.UUID) error { tm.mu.Lock() if tr, ok := tm.transports[id]; ok { - delete(tm.transports, id) _ = tr.Close() //nolint:errcheck + delete(tm.transports, id) } tm.mu.Unlock() @@ -226,16 +224,20 @@ func (tm *Manager) DeleteTransport(id uuid.UUID) error { // Close closes opened transports and registered factories. func (tm *Manager) Close() error { + if tm == nil { + return nil + } + close(tm.doneChan) tm.Logger.Info("Closing transport manager") tm.mu.Lock() statuses := make([]*Status, 0) for _, tr := range tm.transports { - if !tr.Public { + if !tr.Entry.Public { continue } - statuses = append(statuses, &Status{ID: tr.ID, IsUp: false}) + statuses = append(statuses, &Status{ID: tr.Entry.ID, IsUp: false}) tr.Close() } @@ -289,7 +291,7 @@ func (tm *Manager) createTransport(ctx context.Context, remote cipher.PubKey, tp } tm.Logger.Infof("Dialed to %s using %s factory. Transport ID: %s", remote, tpType, entry.ID) - mTr := newManagedTransport(entry.ID, tr, entry.Public, false) + mTr := newManagedTransport(tr, *entry, false) tm.mu.Lock() tm.transports[entry.ID] = mTr @@ -299,7 +301,7 @@ func (tm *Manager) createTransport(ctx context.Context, remote cipher.PubKey, tp case <-tm.doneChan: return nil, io.ErrClosedPipe case tm.TrChan <- mTr: - go tm.manageTransport(ctx, mTr, factory, remote, public, false) + go tm.manageTransport(ctx, mTr, factory, remote) return mTr, nil } } @@ -331,7 +333,8 @@ func (tm *Manager) acceptTransport(ctx context.Context, factory Factory) (*Manag if oldTr != nil { oldTr.killWorker() } - mTr := newManagedTransport(entry.ID, tr, entry.Public, true) + + mTr := newManagedTransport(tr, *entry, true) tm.mu.Lock() tm.transports[entry.ID] = mTr @@ -341,7 +344,7 @@ func (tm *Manager) acceptTransport(ctx context.Context, factory Factory) (*Manag case <-tm.doneChan: return nil, io.ErrClosedPipe case tm.TrChan <- mTr: - go tm.manageTransport(ctx, mTr, factory, remote, true, true) + go tm.manageTransport(ctx, mTr, factory, remote) return mTr, nil } } @@ -371,47 +374,67 @@ func (tm *Manager) isClosing() bool { } } -func (tm *Manager) manageTransport(ctx context.Context, mTr *ManagedTransport, factory Factory, remote cipher.PubKey, public bool, accepted bool) { +func (tm *Manager) manageTransport(ctx context.Context, mTr *ManagedTransport, factory Factory, remote cipher.PubKey) { + logTicker := time.NewTicker(logWriteInterval) + logUpdate := false + mgrQty := atomic.AddInt32(&tm.mgrQty, 1) - tm.Logger.Infof("Spawned manageTransport for mTr.ID: %v. mgrQty: %v", mTr.ID, mgrQty) + tm.Logger.Infof("Spawned manageTransport for mTr.ID: %v. mgrQty: %v PK: %s", mTr.Entry.ID, mgrQty, remote) + + defer func() { + logTicker.Stop() + if logUpdate { + if err := tm.config.LogStore.Record(mTr.Entry.ID, mTr.LogEntry); err != nil { + tm.Logger.Warnf("Failed to record log entry: %s", err) + } + } + mTr.killUpdate() + + mgrQty := atomic.AddInt32(&tm.mgrQty, -1) + tm.Logger.Infof("manageTransport exit for %v. mgrQty: %v", mTr.Entry.ID, mgrQty) + }() + for { select { - case <-mTr.doneChan: - mgrQty := atomic.AddInt32(&tm.mgrQty, -1) - tm.Logger.Infof("manageTransport exit for %v. mgrQty: %v", mTr.ID, mgrQty) + case <-mTr.done: return - case err := <-mTr.errChan: - if !mTr.isClosing() { - tm.Logger.Infof("Transport %s failed with error: %s. Re-dialing...", mTr.ID, err) - if accepted { - if err := tm.DeleteTransport(mTr.ID); err != nil { - tm.Logger.Warnf("Failed to delete accepted transport: %s", err) - } - } else { - tr, _, err := tm.dialTransport(ctx, factory, remote, public) - if err != nil { - tm.Logger.Infof("Failed to redial Transport %s: %s", mTr.ID, err) - if err := tm.DeleteTransport(mTr.ID); err != nil { - tm.Logger.Warnf("Failed to delete redialed transport: %s", err) - } - } else { - tm.Logger.Infof("Updating transport %s", mTr.ID) - mTr.updateTransport(tr) - } + + case <-logTicker.C: + if logUpdate { + if err := tm.config.LogStore.Record(mTr.Entry.ID, mTr.LogEntry); err != nil { + tm.Logger.Warnf("Failed to record log entry: %s", err) } - } else { - tm.Logger.Infof("Transport %s is already closing. Skipped error: %s", mTr.ID, err) } - case n := <-mTr.readLogChan: - mTr.LogEntry.ReceivedBytes.Add(mTr.LogEntry.ReceivedBytes, big.NewInt(int64(n))) - if err := tm.config.LogStore.Record(mTr.ID, mTr.LogEntry); err != nil { - tm.Logger.Warnf("Failed to record log entry: %s", err) + + case err, ok := <-mTr.update: + if !ok { + return } - case n := <-mTr.writeLogChan: - mTr.LogEntry.SentBytes.Add(mTr.LogEntry.SentBytes, big.NewInt(int64(n))) - if err := tm.config.LogStore.Record(mTr.ID, mTr.LogEntry); err != nil { - tm.Logger.Warnf("Failed to record log entry: %s", err) + + if err == nil { + logUpdate = true + continue } + + tm.Logger.Infof("Transport %s failed with error: %s. Re-dialing...", mTr.Entry.ID, err) + if _, err := tm.config.DiscoveryClient.UpdateStatuses(ctx, &Status{ID: mTr.Entry.ID, IsUp: false, Updated: time.Now().UnixNano()}); err != nil { + tm.Logger.Warnf("Failed to change transport status: %s", err) + } + + // If we are the acceptor, we are not responsible for restarting transport. + // If the transport is private, we don't need to restart. + if mTr.Accepted || !mTr.Entry.Public { + return + } + + tr, _, err := tm.dialTransport(ctx, factory, remote, mTr.Entry.Public) + if err != nil { + tm.Logger.Infof("Failed to redial Transport %s: %s", mTr.Entry.ID, err) + continue + } + + tm.Logger.Infof("Updating transport %s", mTr.Entry.ID) + _ = mTr.updateTransport(ctx, tr, tm.config.DiscoveryClient) //nolint:errcheck } } } diff --git a/pkg/transport/manager_test.go b/pkg/transport/manager_test.go index 352794e6c1..4f0b30f729 100644 --- a/pkg/transport/manager_test.go +++ b/pkg/transport/manager_test.go @@ -11,12 +11,10 @@ import ( "time" "github.com/google/uuid" + "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/skycoin/src/util/logging" - - "github.com/skycoin/skywire/pkg/cipher" ) func TestMain(m *testing.M) { @@ -89,16 +87,16 @@ func TestTransportManager(t *testing.T) { time.Sleep(time.Second) - tr1 := m1.Transport(tr2.ID) + tr1 := m1.Transport(tr2.Entry.ID) require.NotNil(t, tr1) - dEntry, err := client.GetTransportByID(context.TODO(), tr2.ID) + dEntry, err := client.GetTransportByID(context.TODO(), tr2.Entry.ID) require.NoError(t, err) assert.Equal(t, SortPubKeys(pk2, pk1), dEntry.Entry.Edges()) assert.True(t, dEntry.IsUp) - require.NoError(t, m1.DeleteTransport(tr1.ID)) - dEntry, err = client.GetTransportByID(context.TODO(), tr1.ID) + require.NoError(t, m1.DeleteTransport(tr1.Entry.ID)) + dEntry, err = client.GetTransportByID(context.TODO(), tr1.Entry.ID) require.NoError(t, err) assert.False(t, dEntry.IsUp) @@ -108,12 +106,12 @@ func TestTransportManager(t *testing.T) { time.Sleep(time.Second) - dEntry, err = client.GetTransportByID(context.TODO(), tr1.ID) + dEntry, err = client.GetTransportByID(context.TODO(), tr1.Entry.ID) require.NoError(t, err) assert.True(t, dEntry.IsUp) - require.NoError(t, m2.DeleteTransport(tr2.ID)) - dEntry, err = client.GetTransportByID(context.TODO(), tr2.ID) + require.NoError(t, m2.DeleteTransport(tr2.Entry.ID)) + dEntry, err = client.GetTransportByID(context.TODO(), tr2.Entry.ID) require.NoError(t, err) assert.False(t, dEntry.IsUp) @@ -155,17 +153,17 @@ func TestTransportManagerReEstablishTransports(t *testing.T) { tr2, err := m2.CreateTransport(context.TODO(), pk1, "mock", true) require.NoError(t, err) - tr1 := m1.Transport(tr2.ID) + tr1 := m1.Transport(tr2.Entry.ID) require.NotNil(t, tr1) - dEntry, err := client.GetTransportByID(context.TODO(), tr2.ID) + dEntry, err := client.GetTransportByID(context.TODO(), tr2.Entry.ID) require.NoError(t, err) assert.Equal(t, SortPubKeys(pk2, pk1), dEntry.Entry.Edges()) assert.True(t, dEntry.IsUp) require.NoError(t, m2.Close()) - dEntry2, err := client.GetTransportByID(context.TODO(), tr2.ID) + dEntry2, err := client.GetTransportByID(context.TODO(), tr2.Entry.ID) require.NoError(t, err) assert.False(t, dEntry2.IsUp) @@ -178,7 +176,7 @@ func TestTransportManagerReEstablishTransports(t *testing.T) { go func() { m2errCh <- m2.Serve(context.TODO()) }() //time.Sleep(time.Second * 1) // TODO: this time.Sleep looks fishy - figure out later - dEntry3, err := client.GetTransportByID(context.TODO(), tr2.ID) + dEntry3, err := client.GetTransportByID(context.TODO(), tr2.Entry.ID) require.NoError(t, err) assert.True(t, dEntry3.IsUp) @@ -220,7 +218,7 @@ func TestTransportManagerLogs(t *testing.T) { time.Sleep(100 * time.Millisecond) - tr1 := m1.Transport(tr2.ID) + tr1 := m1.Transport(tr2.Entry.ID) require.NotNil(t, tr1) go tr1.Write([]byte("foo")) // nolint @@ -228,17 +226,18 @@ func TestTransportManagerLogs(t *testing.T) { _, err = tr2.Read(buf) require.NoError(t, err) - time.Sleep(100 * time.Millisecond) + // 2x log write interval just to be safe. + time.Sleep(logWriteInterval * 2) - entry1, err := logStore1.Entry(tr1.ID) + entry1, err := logStore1.Entry(tr1.Entry.ID) require.NoError(t, err) - assert.Equal(t, uint64(3), entry1.SentBytes.Uint64()) - assert.Equal(t, uint64(0), entry1.ReceivedBytes.Uint64()) + assert.Equal(t, uint64(3), entry1.SentBytes) + assert.Equal(t, uint64(0), entry1.RecvBytes) - entry2, err := logStore2.Entry(tr1.ID) + entry2, err := logStore2.Entry(tr1.Entry.ID) require.NoError(t, err) - assert.Equal(t, uint64(0), entry2.SentBytes.Uint64()) - assert.Equal(t, uint64(3), entry2.ReceivedBytes.Uint64()) + assert.Equal(t, uint64(0), entry2.SentBytes) + assert.Equal(t, uint64(3), entry2.RecvBytes) require.NoError(t, m2.Close()) require.NoError(t, m1.Close()) @@ -316,7 +315,7 @@ func ExampleManager_CreateTransport() { return } - if (mtrAB.ID == uuid.UUID{}) { + if (mtrAB.Entry.ID == uuid.UUID{}) { fmt.Printf("Manager.CreateTransport failed on iteration %v", i) return } diff --git a/pkg/transport/mock.go b/pkg/transport/mock.go index a260aaaae6..f612a8c635 100644 --- a/pkg/transport/mock.go +++ b/pkg/transport/mock.go @@ -7,7 +7,7 @@ import ( "net" "time" - "github.com/skycoin/skywire/pkg/cipher" + "github.com/skycoin/dmsg/cipher" ) // ErrTransportCommunicationTimeout represent timeout error for a mock transport. @@ -71,6 +71,9 @@ func (f *MockFactory) Dial(ctx context.Context, remote cipher.PubKey) (Transport // Close closes notification channel between a pair of MockFactories. func (f *MockFactory) Close() error { + if f == nil { + return nil + } select { case <-f.inDone: default: @@ -125,6 +128,9 @@ func (m *MockTransport) Write(p []byte) (n int, err error) { // Close implements closer for mock transport func (m *MockTransport) Close() error { + if m == nil { + return nil + } return m.rw.Close() } diff --git a/pkg/transport/tcp_transport.go b/pkg/transport/tcp_transport.go index 63e4858f84..9ab00e441a 100644 --- a/pkg/transport/tcp_transport.go +++ b/pkg/transport/tcp_transport.go @@ -9,7 +9,7 @@ import ( "path/filepath" "strings" - "github.com/skycoin/skywire/pkg/cipher" + "github.com/skycoin/dmsg/cipher" ) // ErrUnknownRemote returned for connection attempts for remotes @@ -61,6 +61,9 @@ func (f *TCPFactory) Dial(ctx context.Context, remote cipher.PubKey) (Transport, // Close implements io.Closer func (f *TCPFactory) Close() error { + if f == nil { + return nil + } return f.l.Close() } diff --git a/pkg/transport/tcp_transport_test.go b/pkg/transport/tcp_transport_test.go index cd60504f87..ac2c5c08fe 100644 --- a/pkg/transport/tcp_transport_test.go +++ b/pkg/transport/tcp_transport_test.go @@ -8,10 +8,10 @@ import ( "os" "testing" + "github.com/skycoin/dmsg/cipher" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/skycoin/skywire/pkg/cipher" "github.com/skycoin/skywire/pkg/transport" ) diff --git a/pkg/transport/transport.go b/pkg/transport/transport.go index 693e6e7cd5..ed1f8ee335 100644 --- a/pkg/transport/transport.go +++ b/pkg/transport/transport.go @@ -7,8 +7,7 @@ import ( "time" "github.com/google/uuid" - - "github.com/skycoin/skywire/pkg/cipher" + "github.com/skycoin/dmsg/cipher" ) // Transport represents communication between two visors via a single hop. diff --git a/pkg/util/pathutil/homedir.go b/pkg/util/pathutil/homedir.go index 9e7500551f..bd6af341b7 100644 --- a/pkg/util/pathutil/homedir.go +++ b/pkg/util/pathutil/homedir.go @@ -7,7 +7,7 @@ import ( "path/filepath" "runtime" - "github.com/skycoin/skywire/pkg/cipher" + "github.com/skycoin/dmsg/cipher" ) // HomeDir obtains the path to the user's home directory via ENVs. diff --git a/pkg/visor/config.go b/pkg/visor/config.go index 9d90765e98..3124afa02c 100644 --- a/pkg/visor/config.go +++ b/pkg/visor/config.go @@ -8,13 +8,13 @@ import ( "path/filepath" "time" - "github.com/skycoin/skywire/pkg/messaging" + "github.com/skycoin/dmsg/cipher" + "github.com/skycoin/dmsg/disc" - "github.com/skycoin/skywire/pkg/cipher" - mClient "github.com/skycoin/skywire/pkg/messaging-discovery/client" "github.com/skycoin/skywire/pkg/routing" "github.com/skycoin/skywire/pkg/transport" trClient "github.com/skycoin/skywire/pkg/transport-discovery/client" + "github.com/skycoin/skywire/pkg/transport/dmsg" ) // Config defines configuration parameters for Visor. @@ -63,8 +63,8 @@ type Config struct { Interfaces InterfaceConfig `json:"interfaces"` } -// MessagingConfig returns config for messaging client. -func (c *Config) MessagingConfig() (*messaging.Config, error) { +// MessagingConfig returns config for dmsg client. +func (c *Config) MessagingConfig() (*dmsg.Config, error) { msgConfig := c.Messaging @@ -72,10 +72,10 @@ func (c *Config) MessagingConfig() (*messaging.Config, error) { return nil, errors.New("empty discovery") } - return &messaging.Config{ + return &dmsg.Config{ PubKey: c.Visor.StaticPubKey, SecKey: c.Visor.StaticSecKey, - Discovery: mClient.NewHTTP(msgConfig.Discovery), + Discovery: disc.NewHTTP(msgConfig.Discovery), Retries: 5, RetryDelay: time.Second, }, nil diff --git a/pkg/visor/config_test.go b/pkg/visor/config_test.go index 0c85d20b17..e0be3c6f8b 100644 --- a/pkg/visor/config_test.go +++ b/pkg/visor/config_test.go @@ -10,11 +10,11 @@ import ( "testing" "time" + "github.com/skycoin/dmsg/cipher" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/skycoin/skywire/internal/httpauth" - "github.com/skycoin/skywire/pkg/cipher" ) func TestMessagingDiscovery(t *testing.T) { diff --git a/pkg/visor/rpc.go b/pkg/visor/rpc.go index 0c55bc8ac2..71222b42e4 100644 --- a/pkg/visor/rpc.go +++ b/pkg/visor/rpc.go @@ -6,8 +6,7 @@ import ( "time" "github.com/google/uuid" - - "github.com/skycoin/skywire/pkg/cipher" + "github.com/skycoin/dmsg/cipher" "github.com/skycoin/skywire/pkg/routing" "github.com/skycoin/skywire/pkg/transport" @@ -56,7 +55,7 @@ func newTransportSummary(tm *transport.Manager, tp *transport.ManagedTransport, } summary := &TransportSummary{ - ID: tp.ID, + ID: tp.Entry.ID, Local: tm.Local(), Remote: remote, Type: tp.Type(), diff --git a/pkg/visor/rpc_client.go b/pkg/visor/rpc_client.go index 10a58faabf..ce68130852 100644 --- a/pkg/visor/rpc_client.go +++ b/pkg/visor/rpc_client.go @@ -8,11 +8,10 @@ import ( "sync" "time" - "github.com/skycoin/skycoin/src/util/logging" - "github.com/google/uuid" + "github.com/skycoin/dmsg/cipher" + "github.com/skycoin/skycoin/src/util/logging" - "github.com/skycoin/skywire/pkg/cipher" "github.com/skycoin/skywire/pkg/routing" "github.com/skycoin/skywire/pkg/transport" ) diff --git a/pkg/visor/rpc_test.go b/pkg/visor/rpc_test.go index 2a1e956ecc..bc1e084668 100644 --- a/pkg/visor/rpc_test.go +++ b/pkg/visor/rpc_test.go @@ -9,16 +9,15 @@ import ( "testing" "time" - "github.com/skycoin/skywire/pkg/cipher" - "github.com/skycoin/skywire/pkg/util/pathutil" - "github.com/google/uuid" + "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" + "github.com/skycoin/skywire/pkg/util/pathutil" ) func TestListApps(t *testing.T) { @@ -247,7 +246,7 @@ func TestRPC(t *testing.T) { t.Run("Transport", func(t *testing.T) { var ids []uuid.UUID node.tm.WalkTransports(func(tp *transport.ManagedTransport) bool { - ids = append(ids, tp.ID) + ids = append(ids, tp.Entry.ID) return true }) diff --git a/pkg/visor/visor.go b/pkg/visor/visor.go index b7bc83a85b..f07bbfecfe 100644 --- a/pkg/visor/visor.go +++ b/pkg/visor/visor.go @@ -19,15 +19,15 @@ import ( "syscall" "time" + "github.com/skycoin/dmsg/noise" "github.com/skycoin/skycoin/src/util/logging" - "github.com/skycoin/skywire/internal/noise" "github.com/skycoin/skywire/pkg/app" - "github.com/skycoin/skywire/pkg/dmsg" routeFinder "github.com/skycoin/skywire/pkg/route-finder/client" "github.com/skycoin/skywire/pkg/router" "github.com/skycoin/skywire/pkg/routing" "github.com/skycoin/skywire/pkg/transport" + "github.com/skycoin/skywire/pkg/transport/dmsg" "github.com/skycoin/skywire/pkg/util/pathutil" ) @@ -304,6 +304,9 @@ func (visor *Visor) stopUnhandledApp(name string, pid int) { // Close safely stops spawned Apps and messaging Visor. func (visor *Visor) Close() (err error) { + if visor == nil { + return nil + } if visor.rpcListener != nil { if err = visor.rpcListener.Close(); err != nil { visor.logger.WithError(err).Error("failed to stop RPC interface") diff --git a/pkg/visor/visor_test.go b/pkg/visor/visor_test.go index 3bfb7b86f3..f773fcd93d 100644 --- a/pkg/visor/visor_test.go +++ b/pkg/visor/visor_test.go @@ -7,29 +7,25 @@ import ( "io/ioutil" "log" "net" + "net/http" + "net/http/httptest" "os" "os/exec" "sync" "testing" "time" - "github.com/skycoin/skywire/pkg/dmsg" - "github.com/skycoin/skywire/pkg/util/pathutil" - - "net/http" - "net/http/httptest" - + "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" - "github.com/skycoin/skywire/pkg/cipher" - "github.com/skycoin/skywire/internal/httpauth" "github.com/skycoin/skywire/pkg/app" - "github.com/skycoin/skywire/pkg/messaging" - "github.com/skycoin/skywire/pkg/messaging-discovery/client" "github.com/skycoin/skywire/pkg/transport" + "github.com/skycoin/skywire/pkg/transport/dmsg" + "github.com/skycoin/skywire/pkg/util/pathutil" ) var masterLogger *logging.MasterLogger @@ -90,7 +86,7 @@ func TestVisorStartClose(t *testing.T) { defer os.RemoveAll("skychat") node := &Visor{config: &Config{}, router: r, executer: executer, appsConf: conf, startedApps: map[string]*appBind{}, logger: logging.MustGetLogger("test")} - mConf := &messaging.Config{PubKey: cipher.PubKey{}, SecKey: cipher.SecKey{}, Discovery: client.NewMock()} + mConf := &dmsg.Config{PubKey: cipher.PubKey{}, SecKey: cipher.SecKey{}, Discovery: disc.NewMock()} node.messenger = dmsg.NewClient(mConf.PubKey, mConf.SecKey, mConf.Discovery) var err error @@ -268,6 +264,9 @@ func (r *mockRouter) ServeApp(conn net.Conn, port uint16, appConf *app.Config) e } func (r *mockRouter) Close() error { + if r == nil { + return nil + } r.didClose = true r.Lock() if r.errChan != nil { diff --git a/vendor/github.com/konsorten/go-windows-terminal-sequences/README.md b/vendor/github.com/konsorten/go-windows-terminal-sequences/README.md index 949b77e304..195333e51d 100644 --- a/vendor/github.com/konsorten/go-windows-terminal-sequences/README.md +++ b/vendor/github.com/konsorten/go-windows-terminal-sequences/README.md @@ -26,6 +26,7 @@ The tool is sponsored by the [marvin + konsorten GmbH](http://www.konsorten.de). We thank all the authors who provided code to this library: * Felix Kollmann +* Nicolas Perraut ## License diff --git a/vendor/github.com/konsorten/go-windows-terminal-sequences/sequences_dummy.go b/vendor/github.com/konsorten/go-windows-terminal-sequences/sequences_dummy.go new file mode 100644 index 0000000000..df61a6f2f6 --- /dev/null +++ b/vendor/github.com/konsorten/go-windows-terminal-sequences/sequences_dummy.go @@ -0,0 +1,11 @@ +// +build linux darwin + +package sequences + +import ( + "fmt" +) + +func EnableVirtualTerminalProcessing(stream uintptr, enable bool) error { + return fmt.Errorf("windows only package") +} diff --git a/vendor/github.com/skycoin/dmsg/.gitignore b/vendor/github.com/skycoin/dmsg/.gitignore new file mode 100644 index 0000000000..c692e3af7d --- /dev/null +++ b/vendor/github.com/skycoin/dmsg/.gitignore @@ -0,0 +1,11 @@ +*.exe +*.exe~ +*.dll +*.so +*.dylib +*.test +*.out +.DS_Store + +.idea/ +/vendor \ No newline at end of file diff --git a/vendor/github.com/skycoin/dmsg/.golangci.yml b/vendor/github.com/skycoin/dmsg/.golangci.yml new file mode 100644 index 0000000000..8f87242efd --- /dev/null +++ b/vendor/github.com/skycoin/dmsg/.golangci.yml @@ -0,0 +1,195 @@ +# This file contains all available configuration options +# Modified for linting cmd/ and pkg/ + +# options for analysis running +run: + # default concurrency is a available CPU number + concurrency: 4 + + # timeout for analysis, e.g. 30s, 5m, default is 1m + deadline: 3m + + # exit code when at least one issue was found, default is 1 + issues-exit-code: 1 + + # include test files or not, default is true + tests: true + + # list of build tags, all linters use it. Default is empty list. + build-tags: + + # which dirs to skip: they won't be analyzed; + # can use regexp here: generated.*, regexp is applied on full path; + # default value is empty list, but next dirs are always skipped independently + # from this option's value: + # vendor$, third_party$, testdata$, examples$, Godeps$, builtin$ + skip-dirs: + + # which files to skip: they will be analyzed, but issues from them + # won't be reported. Default value is empty list, but there is + # no need to include all autogenerated files, we confidently recognize + # autogenerated files. If it's not please let us know. + skip-files: + + +# output configuration options +output: + # colored-line-number|line-number|json|tab|checkstyle, default is "colored-line-number" + format: colored-line-number + + # print lines of code with issue, default is true + print-issued-lines: true + + # print linter name in the end of issue text, default is true + print-linter-name: true + + +# all available settings of specific linters +linters-settings: + errcheck: + # report about not checking of errors in type assetions: `a := b.(MyStruct)`; + # default is false: such cases aren't reported by default. + check-type-assertions: false + + # report about assignment of errors to blank identifier: `num, _ := strconv.Atoi(numStr)`; + # default is false: such cases aren't reported by default. + check-blank: true + govet: + # report about shadowed variables + check-shadowing: true + + # Obtain type information from installed (to $GOPATH/pkg) package files: + # golangci-lint will execute `go install -i` and `go test -i` for analyzed packages + # before analyzing them. + # By default this option is disabled and govet gets type information by loader from source code. + # Loading from source code is slow, but it's done only once for all linters. + # Go-installing of packages first time is much slower than loading them from source code, + # therefore this option is disabled by default. + # But repeated installation is fast in go >= 1.10 because of build caching. + # Enable this option only if all conditions are met: + # 1. you use only "fast" linters (--fast e.g.): no program loading occurs + # 2. you use go >= 1.10 + # 3. you do repeated runs (false for CI) or cache $GOPATH/pkg or `go env GOCACHE` dir in CI. + use-installed-packages: false + golint: + # minimal confidence for issues, default is 0.8 + min-confidence: 0.8 + gofmt: + # simplify code: gofmt with `-s` option, true by default + simplify: true + gocyclo: + # minimal code complexity to report, 30 by default (but we recommend 10-20) + min-complexity: 10 + maligned: + # print struct with more effective memory layout or not, false by default + suggest-new: true + dupl: + # tokens count to trigger issue, 150 by default + threshold: 100 + goconst: + # minimal length of string constant, 3 by default + min-len: 3 + # minimal occurrences count to trigger, 3 by default + min-occurrences: 3 + depguard: + list-type: blacklist + include-go-root: false + packages: + - github.com/pkg/errors + misspell: + # Correct spellings using locale preferences for US or UK. + # Default is to use a neutral variety of English. + # Setting locale to US will correct the British spelling of 'colour' to 'color'. + locale: US + lll: + # max line length, lines longer will be reported. Default is 120. + # '\t' is counted as 1 character by default, and can be changed with the tab-width option + line-length: 120 + # tab width in spaces. Default to 1. + tab-width: 1 + unused: + # treat code as a program (not a library) and report unused exported identifiers; default is false. + # XXX: if you enable this setting, unused will report a lot of false-positives in text editors: + # if it's called for subdir of a project it can't find funcs usages. All text editor integrations + # with golangci-lint call it on a directory with the changed file. + check-exported: false + unparam: + # call graph construction algorithm (cha, rta). In general, use cha for libraries, + # and rta for programs with main packages. Default is cha. + algo: cha + + # Inspect exported functions, default is false. Set to true if no external program/library imports your code. + # XXX: if you enable this setting, unparam will report a lot of false-positives in text editors: + # if it's called for subdir of a project it can't find external interfaces. All text editor integrations + # with golangci-lint call it on a directory with the changed file. + check-exported: false + nakedret: + # make an issue if func has more lines of code than this setting and it has naked returns; default is 30 + max-func-lines: 30 + prealloc: + # XXX: we don't recommend using this linter before doing performance profiling. + # For most programs usage of prealloc will be a premature optimization. + + # Report preallocation suggestions only on simple loops that have no returns/breaks/continues/gotos in them. + # True by default. + simple: true + range-loops: true # Report preallocation suggestions on range loops, true by default + for-loops: false # Report preallocation suggestions on for loops, false by default + + +linters: + enable: + - golint + - goimports + - varcheck + - unparam + - deadcode + - structcheck + - errcheck + - gosimple + - staticcheck + - unused + - ineffassign + - typecheck + - gosec + - megacheck + - misspell + - nakedret + - depguard + enable-all: false + disable: + disable-all: true + presets: + fast: false + + +issues: + # List of regexps of issue texts to exclude, empty list by default. + # But independently from this option we use default exclude patterns, + # it can be disabled by `exclude-use-default: false`. To list all + # excluded by default patterns execute `golangci-lint run --help` + exclude: + - "G304: Potential file inclusion via variable" + - "G204: Subprocess launched with variable" + - "G104: Errors unhandled" + - Error return value of .((os\.)?std(out|err)\..*|.*Close|.*Flush|os\.Remove(All)?|.*printf?|os\.(Un)?Setenv). is not checked + + # Independently from option `exclude` we use default exclude patterns, + # it can be disabled by this option. To list all + # excluded by default patterns execute `golangci-lint run --help`. + # Default value for this option is true. + exclude-use-default: false + + # Maximum issues count per one linter. Set to 0 to disable. Default is 50. + max-per-linter: 0 + + # Maximum count of issues with the same text. Set to 0 to disable. Default is 3. + max-same: 0 + + # Show only new issues: if there are unstaged changes or untracked files, + # only those changes are analyzed, else only changes in HEAD~ are analyzed. + # It's a super-useful option for integration of golangci-lint into existing + # large codebase. It's not practical to fix all existing issues at the moment + # of integration: much better don't allow issues in new code. + # Default is false. + new: false \ No newline at end of file diff --git a/vendor/github.com/skycoin/dmsg/.travis.yml b/vendor/github.com/skycoin/dmsg/.travis.yml new file mode 100644 index 0000000000..6592737ce6 --- /dev/null +++ b/vendor/github.com/skycoin/dmsg/.travis.yml @@ -0,0 +1,21 @@ +language: go +go: + # - "1.11.x" At minimum the code should run make check on the latest two go versions in the default linux environment provided by Travis. + - "1.12.x" + +dist: xenial + +matrix: + include: + - os: linux + - os: osx + # Do not start osx build for PR + if: type != pull_request + osx_image: xcode8 + +install: + - go get -u github.com/FiloSottile/vendorcheck + - curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | sh -s -- -b $GOPATH/bin v1.17.1 + +script: + - make check diff --git a/vendor/github.com/skycoin/dmsg/Makefile b/vendor/github.com/skycoin/dmsg/Makefile new file mode 100644 index 0000000000..a51fdd0f27 --- /dev/null +++ b/vendor/github.com/skycoin/dmsg/Makefile @@ -0,0 +1,37 @@ +.DEFAULT_GOAL := help +.PHONY : check lint install-linters dep test + +OPTS?=GO111MODULE=on +TEST_OPTS?=-race -tags no_ci -cover -timeout=5m + +check: lint test ## Run linters and tests + +lint: ## Run linters. Use make install-linters first + ${OPTS} golangci-lint run -c .golangci.yml ./... + # The govet version in golangci-lint is out of date and has spurious warnings, run it separately + ${OPTS} go vet -all ./... + +vendorcheck: ## Run vendorcheck + GO111MODULE=off vendorcheck ./... + +test: ## Run tests + -go clean -testcache &>/dev/null + ${OPTS} go test ${TEST_OPTS} ./... + +install-linters: ## Install linters + - VERSION=1.17.1 ./ci_scripts/install-golangci-lint.sh + # GO111MODULE=off go get -u github.com/FiloSottile/vendorcheck + # For some reason this install method is not recommended, see https://github.com/golangci/golangci-lint#install + # However, they suggest `curl ... | bash` which we should not do + # ${OPTS} go get -u github.com/golangci/golangci-lint/cmd/golangci-lint + ${OPTS} go get -u golang.org/x/tools/cmd/goimports + +format: ## Formats the code. Must have goimports installed (use make install-linters). + ${OPTS} goimports -w -local github.com/skycoin/dmsg ./... + +dep: ## Sorts dependencies + ${OPTS} go mod download + ${OPTS} go mod tidy -v + +help: + @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' diff --git a/pkg/dmsg/README.md b/vendor/github.com/skycoin/dmsg/README.md similarity index 99% rename from pkg/dmsg/README.md rename to vendor/github.com/skycoin/dmsg/README.md index f9723c0320..f131334b8a 100644 --- a/pkg/dmsg/README.md +++ b/vendor/github.com/skycoin/dmsg/README.md @@ -1,4 +1,6 @@ -# `dmsg` +# dmsg + +Distributed messaging system. >**TODO:** > diff --git a/pkg/dmsg/TESTING.md b/vendor/github.com/skycoin/dmsg/TESTING.md similarity index 100% rename from pkg/dmsg/TESTING.md rename to vendor/github.com/skycoin/dmsg/TESTING.md diff --git a/pkg/cipher/cipher.go b/vendor/github.com/skycoin/dmsg/cipher/cipher.go similarity index 100% rename from pkg/cipher/cipher.go rename to vendor/github.com/skycoin/dmsg/cipher/cipher.go diff --git a/pkg/dmsg/client.go b/vendor/github.com/skycoin/dmsg/client.go similarity index 94% rename from pkg/dmsg/client.go rename to vendor/github.com/skycoin/dmsg/client.go index 0bceec2bb7..c933e1b892 100644 --- a/pkg/dmsg/client.go +++ b/vendor/github.com/skycoin/dmsg/client.go @@ -13,10 +13,9 @@ import ( "github.com/skycoin/skycoin/src/util/logging" - "github.com/skycoin/skywire/internal/noise" - "github.com/skycoin/skywire/pkg/cipher" - "github.com/skycoin/skywire/pkg/messaging-discovery/client" - "github.com/skycoin/skywire/pkg/transport" + "github.com/skycoin/dmsg/cipher" + "github.com/skycoin/dmsg/disc" + "github.com/skycoin/dmsg/noise" ) const ( @@ -257,14 +256,18 @@ func (c *ClientConn) DialTransport(ctx context.Context, clientPK cipher.PubKey) } func (c *ClientConn) close() (closed bool) { + if c == nil { + return false + } c.once.Do(func() { closed = true c.log.WithField("remoteServer", c.remoteSrv).Infoln("ClosingConnection") close(c.done) c.mx.Lock() for _, tp := range c.tps { + // Nil check is required here to keep 8192 running goroutines limit in tests with -race flag. if tp != nil { - go tp.Close() //nolint:errcheck + go tp.Close() // nolint:errcheck } } _ = c.Conn.Close() //nolint:errcheck @@ -275,6 +278,9 @@ func (c *ClientConn) close() (closed bool) { // Close closes the connection to dms_server. func (c *ClientConn) Close() error { + if c == nil { + return nil + } if c.close() { c.wg.Wait() } @@ -301,7 +307,7 @@ type Client struct { pk cipher.PubKey sk cipher.SecKey - dc client.APIClient + dc disc.APIClient conns map[cipher.PubKey]*ClientConn // conns with messaging servers. Key: pk of server mx sync.RWMutex @@ -312,7 +318,7 @@ type Client struct { } // NewClient creates a new Client. -func NewClient(pk cipher.PubKey, sk cipher.SecKey, dc client.APIClient, opts ...ClientOption) *Client { +func NewClient(pk cipher.PubKey, sk cipher.SecKey, dc disc.APIClient, opts ...ClientOption) *Client { c := &Client{ log: logging.MustGetLogger("dmsg_client"), pk: pk, @@ -331,13 +337,13 @@ func NewClient(pk cipher.PubKey, sk cipher.SecKey, dc client.APIClient, opts ... } func (c *Client) updateDiscEntry(ctx context.Context) error { - var srvPKs []cipher.PubKey + srvPKs := make([]cipher.PubKey, 0, len(c.conns)) for pk := range c.conns { srvPKs = append(srvPKs, pk) } entry, err := c.dc.Entry(ctx, c.pk) if err != nil { - entry = client.NewClientEntry(c.pk, 0, srvPKs) + entry = disc.NewClientEntry(c.pk, 0, srvPKs) if err := entry.Sign(c.sk); err != nil { return err } @@ -396,7 +402,7 @@ func (c *Client) InitiateServerConnections(ctx context.Context, min int) error { return nil } -func (c *Client) findServerEntries(ctx context.Context) ([]*client.Entry, error) { +func (c *Client) findServerEntries(ctx context.Context) ([]*disc.Entry, error) { for { entries, err := c.dc.AvailableServers(ctx) if err != nil || len(entries) == 0 { @@ -414,7 +420,7 @@ func (c *Client) findServerEntries(ctx context.Context) ([]*client.Entry, error) } } -func (c *Client) findOrConnectToServers(ctx context.Context, entries []*client.Entry, min int) error { +func (c *Client) findOrConnectToServers(ctx context.Context, entries []*disc.Entry, min int) error { for _, entry := range entries { _, err := c.findOrConnectToServer(ctx, entry.Static) if err != nil { @@ -490,7 +496,7 @@ func (c *Client) findOrConnectToServer(ctx context.Context, srvPK cipher.PubKey) } // Accept accepts remotely-initiated tps. -func (c *Client) Accept(ctx context.Context) (transport.Transport, error) { +func (c *Client) Accept(ctx context.Context) (*Transport, error) { select { case tp, ok := <-c.accept: if !ok { @@ -505,7 +511,7 @@ func (c *Client) Accept(ctx context.Context) (transport.Transport, error) { } // Dial dials a transport to remote dms_client. -func (c *Client) Dial(ctx context.Context, remote cipher.PubKey) (transport.Transport, error) { +func (c *Client) Dial(ctx context.Context, remote cipher.PubKey) (*Transport, error) { entry, err := c.dc.Entry(ctx, remote) if err != nil { return nil, fmt.Errorf("get entry failure: %s", err) @@ -540,6 +546,10 @@ func (c *Client) Type() string { // Close closes the dms_client and associated connections. // TODO(evaninjin): proper error handling. func (c *Client) Close() error { + if c == nil { + return nil + } + c.once.Do(func() { close(c.done) for { diff --git a/pkg/messaging-discovery/client/client.go b/vendor/github.com/skycoin/dmsg/disc/client.go similarity index 97% rename from pkg/messaging-discovery/client/client.go rename to vendor/github.com/skycoin/dmsg/disc/client.go index fd063c9974..0d6acc6bd3 100644 --- a/pkg/messaging-discovery/client/client.go +++ b/vendor/github.com/skycoin/dmsg/disc/client.go @@ -1,5 +1,5 @@ -// Package client implements client for messaging discovery. -package client +// Package disc implements client for dmsg discovery. +package disc import ( "bytes" @@ -11,7 +11,7 @@ import ( "sync" "time" - "github.com/skycoin/skywire/pkg/cipher" + "github.com/skycoin/dmsg/cipher" ) // APIClient implements messaging discovery API client. diff --git a/pkg/messaging-discovery/client/entry.go b/vendor/github.com/skycoin/dmsg/disc/entry.go similarity index 97% rename from pkg/messaging-discovery/client/entry.go rename to vendor/github.com/skycoin/dmsg/disc/entry.go index 08f5d5b85e..958fd899c5 100644 --- a/pkg/messaging-discovery/client/entry.go +++ b/vendor/github.com/skycoin/dmsg/disc/entry.go @@ -1,4 +1,4 @@ -package client +package disc import ( "encoding/json" @@ -7,7 +7,7 @@ import ( "strings" "time" - "github.com/skycoin/skywire/pkg/cipher" + "github.com/skycoin/dmsg/cipher" ) const currentVersion = "0.0.1" @@ -194,7 +194,7 @@ func (e *Entry) VerifySignature() error { return err } - return cipher.VerifyPubKeySignedPayload(cipher.PubKey(e.Static), signature, entryJSON) + return cipher.VerifyPubKeySignedPayload(e.Static, signature, entryJSON) } // Sign signs Entry with provided SecKey. @@ -207,7 +207,7 @@ func (e *Entry) Sign(sk cipher.SecKey) error { return err } - sig, err := cipher.SignPayload(entryJSON, cipher.SecKey(sk)) + sig, err := cipher.SignPayload(entryJSON, sk) if err != nil { return err } diff --git a/pkg/messaging-discovery/client/http_message.go b/vendor/github.com/skycoin/dmsg/disc/http_message.go similarity index 97% rename from pkg/messaging-discovery/client/http_message.go rename to vendor/github.com/skycoin/dmsg/disc/http_message.go index 6d50604a85..7336bf6973 100644 --- a/pkg/messaging-discovery/client/http_message.go +++ b/vendor/github.com/skycoin/dmsg/disc/http_message.go @@ -1,4 +1,4 @@ -package client +package disc import ( "fmt" diff --git a/pkg/messaging-discovery/client/testing.go b/vendor/github.com/skycoin/dmsg/disc/testing.go similarity index 97% rename from pkg/messaging-discovery/client/testing.go rename to vendor/github.com/skycoin/dmsg/disc/testing.go index e9327dee6c..dd2eeab9d1 100644 --- a/pkg/messaging-discovery/client/testing.go +++ b/vendor/github.com/skycoin/dmsg/disc/testing.go @@ -1,4 +1,4 @@ -package client +package disc import ( "context" @@ -7,7 +7,7 @@ import ( "sync" "time" - "github.com/skycoin/skywire/pkg/cipher" + "github.com/skycoin/dmsg/cipher" ) // MockClient is an APIClient mock. The mock doesn't reply with the same errors as the diff --git a/pkg/dmsg/frame.go b/vendor/github.com/skycoin/dmsg/frame.go similarity index 97% rename from pkg/dmsg/frame.go rename to vendor/github.com/skycoin/dmsg/frame.go index 07454947a7..78e10edf5f 100644 --- a/pkg/dmsg/frame.go +++ b/vendor/github.com/skycoin/dmsg/frame.go @@ -8,9 +8,9 @@ import ( "sync/atomic" "time" - "github.com/skycoin/skywire/internal/ioutil" + "github.com/skycoin/dmsg/ioutil" - "github.com/skycoin/skywire/pkg/cipher" + "github.com/skycoin/dmsg/cipher" ) const ( diff --git a/vendor/github.com/skycoin/dmsg/go.mod b/vendor/github.com/skycoin/dmsg/go.mod new file mode 100644 index 0000000000..bddbd7179d --- /dev/null +++ b/vendor/github.com/skycoin/dmsg/go.mod @@ -0,0 +1,18 @@ +module github.com/skycoin/dmsg + +go 1.12 + +require ( + github.com/flynn/noise v0.0.0-20180327030543-2492fe189ae6 + github.com/konsorten/go-windows-terminal-sequences v1.0.2 // indirect + github.com/kr/pretty v0.1.0 // indirect + github.com/mattn/go-colorable v0.1.2 // indirect + github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b // indirect + github.com/sirupsen/logrus v1.4.2 + github.com/skycoin/skycoin v0.26.0 + github.com/stretchr/testify v1.3.0 + golang.org/x/crypto v0.0.0-20190621222207-cc06ce4a13d4 // indirect + golang.org/x/net v0.0.0-20190620200207-3b0461eec859 + golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb // indirect + gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect +) diff --git a/vendor/github.com/skycoin/dmsg/go.sum b/vendor/github.com/skycoin/dmsg/go.sum new file mode 100644 index 0000000000..624818fed7 --- /dev/null +++ b/vendor/github.com/skycoin/dmsg/go.sum @@ -0,0 +1,47 @@ +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/flynn/noise v0.0.0-20180327030543-2492fe189ae6 h1:u/UEqS66A5ckRmS4yNpjmVH56sVtS/RfclBAYocb4as= +github.com/flynn/noise v0.0.0-20180327030543-2492fe189ae6/go.mod h1:1i71OnUq3iUe1ma7Lr6yG6/rjvM3emb6yoL7xLFzcVQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s= +github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/mattn/go-colorable v0.1.2 h1:/bC9yWikZXAL9uJdulbSfyVNIR3n3trXl+v8+1sx8mU= +github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-isatty v0.0.8 h1:HLtExJ+uU2HOZ+wI0Tt5DtUDrx8yhUqDcp7fYERX4CE= +github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b h1:j7+1HpAFS1zy5+Q4qx1fWh90gTKwiN4QCGoY9TWyyO4= +github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/skycoin/skycoin v0.26.0 h1:xDxe2r8AclMntZ550Y/vUQgwgLtwrf9Wu5UYiYcN5/o= +github.com/skycoin/skycoin v0.26.0/go.mod h1:78nHjQzd8KG0jJJVL/j0xMmrihXi70ti63fh8vXScJw= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190621222207-cc06ce4a13d4 h1:ydJNl0ENAG67pFbB+9tfhiL2pYqLhfoaZFw/cjLhY4A= +golang.org/x/crypto v0.0.0-20190621222207-cc06ce4a13d4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859 h1:R/3boaszxrf1GEUWTVDzSKVwLmSJpwZ1yqXm8j0v2QI= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb h1:fgwFCsaw9buMuxNd6+DQfAuSFqbNiQZpcgJQAgJsK6k= +golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/internal/ioutil/ack_waiter.go b/vendor/github.com/skycoin/dmsg/ioutil/ack_waiter.go similarity index 100% rename from internal/ioutil/ack_waiter.go rename to vendor/github.com/skycoin/dmsg/ioutil/ack_waiter.go diff --git a/internal/ioutil/atomic_bool.go b/vendor/github.com/skycoin/dmsg/ioutil/atomic_bool.go similarity index 100% rename from internal/ioutil/atomic_bool.go rename to vendor/github.com/skycoin/dmsg/ioutil/atomic_bool.go diff --git a/internal/ioutil/buf_read.go b/vendor/github.com/skycoin/dmsg/ioutil/buf_read.go similarity index 100% rename from internal/ioutil/buf_read.go rename to vendor/github.com/skycoin/dmsg/ioutil/buf_read.go diff --git a/internal/noise/dh.go b/vendor/github.com/skycoin/dmsg/noise/dh.go similarity index 100% rename from internal/noise/dh.go rename to vendor/github.com/skycoin/dmsg/noise/dh.go diff --git a/internal/noise/net.go b/vendor/github.com/skycoin/dmsg/noise/net.go similarity index 99% rename from internal/noise/net.go rename to vendor/github.com/skycoin/dmsg/noise/net.go index 1f835c7050..4904157197 100644 --- a/internal/noise/net.go +++ b/vendor/github.com/skycoin/dmsg/noise/net.go @@ -11,7 +11,7 @@ import ( "github.com/flynn/noise" - "github.com/skycoin/skywire/pkg/cipher" + "github.com/skycoin/dmsg/cipher" ) var ( diff --git a/internal/noise/noise.go b/vendor/github.com/skycoin/dmsg/noise/noise.go similarity index 98% rename from internal/noise/noise.go rename to vendor/github.com/skycoin/dmsg/noise/noise.go index d02e4a0727..e605c62ce5 100644 --- a/internal/noise/noise.go +++ b/vendor/github.com/skycoin/dmsg/noise/noise.go @@ -8,7 +8,7 @@ import ( "github.com/flynn/noise" - "github.com/skycoin/skywire/pkg/cipher" + "github.com/skycoin/dmsg/cipher" ) var noiseLogger = logging.MustGetLogger("noise") // TODO: initialize properly or remove @@ -118,7 +118,7 @@ func (ns *Noise) RemoteStatic() cipher.PubKey { if err != nil { panic(err) } - return cipher.PubKey(pk) + return pk } // EncryptUnsafe encrypts plaintext without interlocking, should only diff --git a/internal/noise/read_writer.go b/vendor/github.com/skycoin/dmsg/noise/read_writer.go similarity index 97% rename from internal/noise/read_writer.go rename to vendor/github.com/skycoin/dmsg/noise/read_writer.go index 15fc6662fd..0a17acf5ae 100644 --- a/internal/noise/read_writer.go +++ b/vendor/github.com/skycoin/dmsg/noise/read_writer.go @@ -8,8 +8,8 @@ import ( "sync" "time" - "github.com/skycoin/skywire/internal/ioutil" - "github.com/skycoin/skywire/pkg/cipher" + "github.com/skycoin/dmsg/cipher" + "github.com/skycoin/dmsg/ioutil" ) // ReadWriter implements noise encrypted read writer. diff --git a/pkg/dmsg/server.go b/vendor/github.com/skycoin/dmsg/server.go similarity index 95% rename from pkg/dmsg/server.go rename to vendor/github.com/skycoin/dmsg/server.go index 07aa172b25..f49612613b 100644 --- a/pkg/dmsg/server.go +++ b/vendor/github.com/skycoin/dmsg/server.go @@ -12,9 +12,9 @@ import ( "github.com/skycoin/skycoin/src/util/logging" - "github.com/skycoin/skywire/internal/noise" - "github.com/skycoin/skywire/pkg/cipher" - "github.com/skycoin/skywire/pkg/messaging-discovery/client" + "github.com/skycoin/dmsg/cipher" + "github.com/skycoin/dmsg/disc" + "github.com/skycoin/dmsg/noise" ) // ErrListenerAlreadyWrappedToNoise occurs when the provided net.Listener is already wrapped with noise.Listener @@ -224,7 +224,7 @@ type Server struct { pk cipher.PubKey sk cipher.SecKey - dc client.APIClient + dc disc.APIClient addr string lis net.Listener @@ -235,7 +235,7 @@ type Server struct { } // NewServer creates a new dms_server. -func NewServer(pk cipher.PubKey, sk cipher.SecKey, addr string, l net.Listener, dc client.APIClient) (*Server, error) { +func NewServer(pk cipher.PubKey, sk cipher.SecKey, addr string, l net.Listener, dc disc.APIClient) (*Server, error) { if addr == "" { addr = l.Addr().String() } @@ -293,6 +293,10 @@ func (s *Server) connCount() int { // Close closes the dms_server. func (s *Server) Close() (err error) { + if s == nil { + return nil + } + if err = s.lis.Close(); err != nil { return err } @@ -311,7 +315,7 @@ func (s *Server) Serve() error { defer cancel() if err := s.retryUpdateEntry(ctx, TransportHandshakeTimeout); err != nil { - return fmt.Errorf("updating server's discovery entry failed with: %s", err) + return fmt.Errorf("updating server's client entry failed with: %s", err) } s.log.Infof("serving: pk(%s) addr(%s)", s.pk, s.addr) @@ -341,7 +345,7 @@ func (s *Server) Serve() error { func (s *Server) updateDiscEntry(ctx context.Context) error { entry, err := s.dc.Entry(ctx, s.pk) if err != nil { - entry = client.NewServerEntry(s.pk, 0, s.addr, 10) + entry = disc.NewServerEntry(s.pk, 0, s.addr, 10) if err := entry.Sign(s.sk); err != nil { return err } diff --git a/pkg/dmsg/transport.go b/vendor/github.com/skycoin/dmsg/transport.go similarity index 89% rename from pkg/dmsg/transport.go rename to vendor/github.com/skycoin/dmsg/transport.go index fdd99faabb..e40dc88358 100644 --- a/pkg/dmsg/transport.go +++ b/vendor/github.com/skycoin/dmsg/transport.go @@ -10,9 +10,8 @@ import ( "github.com/skycoin/skycoin/src/util/logging" - "github.com/skycoin/skywire/internal/ioutil" - "github.com/skycoin/skywire/pkg/cipher" - "github.com/skycoin/skywire/pkg/transport" + "github.com/skycoin/dmsg/cipher" + "github.com/skycoin/dmsg/ioutil" ) // Errors related to REQUEST frames. @@ -23,7 +22,6 @@ var ( ) // Transport represents a connection from dmsg.Client to remote dmsg.Client (via dmsg.Server intermediary). -// It implements transport.Transport type Transport struct { net.Conn // underlying connection to dmsg.Server log *logging.Logger @@ -79,7 +77,16 @@ func (tp *Transport) serve() (started bool) { return started } +// Regarding the use of mutexes: +// 1. `done` is always closed before `inCh`/`bufCh` is closed. +// 2. mutexes protect `inCh`/`bufCh` to ensure that closing and writing to these chans does not happen concurrently. +// 3. Our worry now, is writing to `inCh`/`bufCh` AFTER they have been closed. +// 4. But as, under the mutexes protecting `inCh`/`bufCh`, checking `done` comes first, and we know that `done` is closed before `inCh`/`bufCh`, we can guarantee that it avoids writing to closed chan. func (tp *Transport) close() (closed bool) { + if tp == nil { + return false + } + tp.doneOnce.Do(func() { closed = true @@ -102,6 +109,9 @@ func (tp *Transport) close() (closed bool) { // Close closes the dmsg_tp. func (tp *Transport) Close() error { + if tp == nil { + return nil + } if tp.close() { _ = writeFrame(tp.Conn, MakeFrame(CloseType, tp.id, []byte{0})) //nolint:errcheck } @@ -118,9 +128,14 @@ func (tp *Transport) IsClosed() bool { } } -// Edges returns the local/remote edges of the transport (dms_client to dms_client). -func (tp *Transport) Edges() [2]cipher.PubKey { - return transport.SortPubKeys(tp.local, tp.remote) +// LocalPK returns the local public key of the transport. +func (tp *Transport) LocalPK() cipher.PubKey { + return tp.local +} + +// RemotePK returns the remote public key of the transport. +func (tp *Transport) RemotePK() cipher.PubKey { + return tp.remote } // Type returns the transport type. @@ -132,16 +147,15 @@ func (tp *Transport) Type() string { func (tp *Transport) HandleFrame(f Frame) error { tp.inMx.Lock() defer tp.inMx.Unlock() - -handleFrame: - if tp.IsClosed() { - return io.ErrClosedPipe - } - select { - case tp.inCh <- f: - return nil - default: - goto handleFrame + for { + if tp.IsClosed() { + return io.ErrClosedPipe + } + select { + case tp.inCh <- f: + return nil + default: + } } } diff --git a/vendor/golang.org/x/net/nettest/conntest.go b/vendor/golang.org/x/net/nettest/conntest.go deleted file mode 100644 index 39cc6a631e..0000000000 --- a/vendor/golang.org/x/net/nettest/conntest.go +++ /dev/null @@ -1,464 +0,0 @@ -// Copyright 2016 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package nettest - -import ( - "bytes" - "encoding/binary" - "io" - "io/ioutil" - "math/rand" - "net" - "runtime" - "sync" - "testing" - "time" -) - -// MakePipe creates a connection between two endpoints and returns the pair -// as c1 and c2, such that anything written to c1 is read by c2 and vice-versa. -// The stop function closes all resources, including c1, c2, and the underlying -// net.Listener (if there is one), and should not be nil. -type MakePipe func() (c1, c2 net.Conn, stop func(), err error) - -// TestConn tests that a net.Conn implementation properly satisfies the interface. -// The tests should not produce any false positives, but may experience -// false negatives. Thus, some issues may only be detected when the test is -// run multiple times. For maximal effectiveness, run the tests under the -// race detector. -func TestConn(t *testing.T, mp MakePipe) { - t.Run("BasicIO", func(t *testing.T) { timeoutWrapper(t, mp, testBasicIO) }) - t.Run("PingPong", func(t *testing.T) { timeoutWrapper(t, mp, testPingPong) }) - t.Run("RacyRead", func(t *testing.T) { timeoutWrapper(t, mp, testRacyRead) }) - t.Run("RacyWrite", func(t *testing.T) { timeoutWrapper(t, mp, testRacyWrite) }) - t.Run("ReadTimeout", func(t *testing.T) { timeoutWrapper(t, mp, testReadTimeout) }) - t.Run("WriteTimeout", func(t *testing.T) { timeoutWrapper(t, mp, testWriteTimeout) }) - t.Run("PastTimeout", func(t *testing.T) { timeoutWrapper(t, mp, testPastTimeout) }) - t.Run("PresentTimeout", func(t *testing.T) { timeoutWrapper(t, mp, testPresentTimeout) }) - t.Run("FutureTimeout", func(t *testing.T) { timeoutWrapper(t, mp, testFutureTimeout) }) - t.Run("CloseTimeout", func(t *testing.T) { timeoutWrapper(t, mp, testCloseTimeout) }) - t.Run("ConcurrentMethods", func(t *testing.T) { timeoutWrapper(t, mp, testConcurrentMethods) }) -} - -type connTester func(t *testing.T, c1, c2 net.Conn) - -func timeoutWrapper(t *testing.T, mp MakePipe, f connTester) { - t.Helper() - c1, c2, stop, err := mp() - if err != nil { - t.Fatalf("unable to make pipe: %v", err) - } - var once sync.Once - defer once.Do(func() { stop() }) - timer := time.AfterFunc(time.Minute, func() { - once.Do(func() { - t.Error("test timed out; terminating pipe") - stop() - }) - }) - defer timer.Stop() - f(t, c1, c2) -} - -// testBasicIO tests that the data sent on c1 is properly received on c2. -func testBasicIO(t *testing.T, c1, c2 net.Conn) { - want := make([]byte, 1<<20) - rand.New(rand.NewSource(0)).Read(want) - - dataCh := make(chan []byte) - go func() { - rd := bytes.NewReader(want) - if err := chunkedCopy(c1, rd); err != nil { - t.Errorf("unexpected c1.Write error: %v", err) - } - if err := c1.Close(); err != nil { - t.Errorf("unexpected c1.Close error: %v", err) - } - }() - - go func() { - wr := new(bytes.Buffer) - if err := chunkedCopy(wr, c2); err != nil { - t.Errorf("unexpected c2.Read error: %v", err) - } - if err := c2.Close(); err != nil { - t.Errorf("unexpected c2.Close error: %v", err) - } - dataCh <- wr.Bytes() - }() - - if got := <-dataCh; !bytes.Equal(got, want) { - t.Error("transmitted data differs") - } -} - -// testPingPong tests that the two endpoints can synchronously send data to -// each other in a typical request-response pattern. -func testPingPong(t *testing.T, c1, c2 net.Conn) { - var wg sync.WaitGroup - defer wg.Wait() - - pingPonger := func(c net.Conn) { - defer wg.Done() - buf := make([]byte, 8) - var prev uint64 - for { - if _, err := io.ReadFull(c, buf); err != nil { - if err == io.EOF { - break - } - t.Errorf("unexpected Read error: %v", err) - } - - v := binary.LittleEndian.Uint64(buf) - binary.LittleEndian.PutUint64(buf, v+1) - if prev != 0 && prev+2 != v { - t.Errorf("mismatching value: got %d, want %d", v, prev+2) - } - prev = v - if v == 1000 { - break - } - - if _, err := c.Write(buf); err != nil { - t.Errorf("unexpected Write error: %v", err) - break - } - } - if err := c.Close(); err != nil { - t.Errorf("unexpected Close error: %v", err) - } - } - - wg.Add(2) - go pingPonger(c1) - go pingPonger(c2) - - // Start off the chain reaction. - if _, err := c1.Write(make([]byte, 8)); err != nil { - t.Errorf("unexpected c1.Write error: %v", err) - } -} - -// testRacyRead tests that it is safe to mutate the input Read buffer -// immediately after cancelation has occurred. -func testRacyRead(t *testing.T, c1, c2 net.Conn) { - go chunkedCopy(c2, rand.New(rand.NewSource(0))) - - var wg sync.WaitGroup - defer wg.Wait() - - c1.SetReadDeadline(time.Now().Add(time.Millisecond)) - for i := 0; i < 10; i++ { - wg.Add(1) - go func() { - defer wg.Done() - - b1 := make([]byte, 1024) - b2 := make([]byte, 1024) - for j := 0; j < 100; j++ { - _, err := c1.Read(b1) - copy(b1, b2) // Mutate b1 to trigger potential race - if err != nil { - checkForTimeoutError(t, err) - c1.SetReadDeadline(time.Now().Add(time.Millisecond)) - } - } - }() - } -} - -// testRacyWrite tests that it is safe to mutate the input Write buffer -// immediately after cancelation has occurred. -func testRacyWrite(t *testing.T, c1, c2 net.Conn) { - go chunkedCopy(ioutil.Discard, c2) - - var wg sync.WaitGroup - defer wg.Wait() - - c1.SetWriteDeadline(time.Now().Add(time.Millisecond)) - for i := 0; i < 10; i++ { - wg.Add(1) - go func() { - defer wg.Done() - - b1 := make([]byte, 1024) - b2 := make([]byte, 1024) - for j := 0; j < 100; j++ { - _, err := c1.Write(b1) - copy(b1, b2) // Mutate b1 to trigger potential race - if err != nil { - checkForTimeoutError(t, err) - c1.SetWriteDeadline(time.Now().Add(time.Millisecond)) - } - } - }() - } -} - -// testReadTimeout tests that Read timeouts do not affect Write. -func testReadTimeout(t *testing.T, c1, c2 net.Conn) { - go chunkedCopy(ioutil.Discard, c2) - - c1.SetReadDeadline(aLongTimeAgo) - _, err := c1.Read(make([]byte, 1024)) - checkForTimeoutError(t, err) - if _, err := c1.Write(make([]byte, 1024)); err != nil { - t.Errorf("unexpected Write error: %v", err) - } -} - -// testWriteTimeout tests that Write timeouts do not affect Read. -func testWriteTimeout(t *testing.T, c1, c2 net.Conn) { - go chunkedCopy(c2, rand.New(rand.NewSource(0))) - - c1.SetWriteDeadline(aLongTimeAgo) - _, err := c1.Write(make([]byte, 1024)) - checkForTimeoutError(t, err) - if _, err := c1.Read(make([]byte, 1024)); err != nil { - t.Errorf("unexpected Read error: %v", err) - } -} - -// testPastTimeout tests that a deadline set in the past immediately times out -// Read and Write requests. -func testPastTimeout(t *testing.T, c1, c2 net.Conn) { - go chunkedCopy(c2, c2) - - testRoundtrip(t, c1) - - c1.SetDeadline(aLongTimeAgo) - n, err := c1.Write(make([]byte, 1024)) - if n != 0 { - t.Errorf("unexpected Write count: got %d, want 0", n) - } - checkForTimeoutError(t, err) - n, err = c1.Read(make([]byte, 1024)) - if n != 0 { - t.Errorf("unexpected Read count: got %d, want 0", n) - } - checkForTimeoutError(t, err) - - testRoundtrip(t, c1) -} - -// testPresentTimeout tests that a past deadline set while there are pending -// Read and Write operations immediately times out those operations. -func testPresentTimeout(t *testing.T, c1, c2 net.Conn) { - var wg sync.WaitGroup - defer wg.Wait() - wg.Add(3) - - deadlineSet := make(chan bool, 1) - go func() { - defer wg.Done() - time.Sleep(100 * time.Millisecond) - deadlineSet <- true - c1.SetReadDeadline(aLongTimeAgo) - c1.SetWriteDeadline(aLongTimeAgo) - }() - go func() { - defer wg.Done() - n, err := c1.Read(make([]byte, 1024)) - if n != 0 { - t.Errorf("unexpected Read count: got %d, want 0", n) - } - checkForTimeoutError(t, err) - if len(deadlineSet) == 0 { - t.Error("Read timed out before deadline is set") - } - }() - go func() { - defer wg.Done() - var err error - for err == nil { - _, err = c1.Write(make([]byte, 1024)) - } - checkForTimeoutError(t, err) - if len(deadlineSet) == 0 { - t.Error("Write timed out before deadline is set") - } - }() -} - -// testFutureTimeout tests that a future deadline will eventually time out -// Read and Write operations. -func testFutureTimeout(t *testing.T, c1, c2 net.Conn) { - var wg sync.WaitGroup - wg.Add(2) - - c1.SetDeadline(time.Now().Add(100 * time.Millisecond)) - go func() { - defer wg.Done() - _, err := c1.Read(make([]byte, 1024)) - checkForTimeoutError(t, err) - }() - go func() { - defer wg.Done() - var err error - for err == nil { - _, err = c1.Write(make([]byte, 1024)) - } - checkForTimeoutError(t, err) - }() - wg.Wait() - - go chunkedCopy(c2, c2) - resyncConn(t, c1) - testRoundtrip(t, c1) -} - -// testCloseTimeout tests that calling Close immediately times out pending -// Read and Write operations. -func testCloseTimeout(t *testing.T, c1, c2 net.Conn) { - go chunkedCopy(c2, c2) - - var wg sync.WaitGroup - defer wg.Wait() - wg.Add(3) - - // Test for cancelation upon connection closure. - c1.SetDeadline(neverTimeout) - go func() { - defer wg.Done() - time.Sleep(100 * time.Millisecond) - c1.Close() - }() - go func() { - defer wg.Done() - var err error - buf := make([]byte, 1024) - for err == nil { - _, err = c1.Read(buf) - } - }() - go func() { - defer wg.Done() - var err error - buf := make([]byte, 1024) - for err == nil { - _, err = c1.Write(buf) - } - }() -} - -// testConcurrentMethods tests that the methods of net.Conn can safely -// be called concurrently. -func testConcurrentMethods(t *testing.T, c1, c2 net.Conn) { - if runtime.GOOS == "plan9" { - t.Skip("skipping on plan9; see https://golang.org/issue/20489") - } - go chunkedCopy(c2, c2) - - // The results of the calls may be nonsensical, but this should - // not trigger a race detector warning. - var wg sync.WaitGroup - for i := 0; i < 100; i++ { - wg.Add(7) - go func() { - defer wg.Done() - c1.Read(make([]byte, 1024)) - }() - go func() { - defer wg.Done() - c1.Write(make([]byte, 1024)) - }() - go func() { - defer wg.Done() - c1.SetDeadline(time.Now().Add(10 * time.Millisecond)) - }() - go func() { - defer wg.Done() - c1.SetReadDeadline(aLongTimeAgo) - }() - go func() { - defer wg.Done() - c1.SetWriteDeadline(aLongTimeAgo) - }() - go func() { - defer wg.Done() - c1.LocalAddr() - }() - go func() { - defer wg.Done() - c1.RemoteAddr() - }() - } - wg.Wait() // At worst, the deadline is set 10ms into the future - - resyncConn(t, c1) - testRoundtrip(t, c1) -} - -// checkForTimeoutError checks that the error satisfies the Error interface -// and that Timeout returns true. -func checkForTimeoutError(t *testing.T, err error) { - t.Helper() - if nerr, ok := err.(net.Error); ok { - if !nerr.Timeout() { - t.Errorf("err.Timeout() = false, want true") - } - } else { - t.Errorf("got %T, want net.Error", err) - } -} - -// testRoundtrip writes something into c and reads it back. -// It assumes that everything written into c is echoed back to itself. -func testRoundtrip(t *testing.T, c net.Conn) { - t.Helper() - if err := c.SetDeadline(neverTimeout); err != nil { - t.Errorf("roundtrip SetDeadline error: %v", err) - } - - const s = "Hello, world!" - buf := []byte(s) - if _, err := c.Write(buf); err != nil { - t.Errorf("roundtrip Write error: %v", err) - } - if _, err := io.ReadFull(c, buf); err != nil { - t.Errorf("roundtrip Read error: %v", err) - } - if string(buf) != s { - t.Errorf("roundtrip data mismatch: got %q, want %q", buf, s) - } -} - -// resyncConn resynchronizes the connection into a sane state. -// It assumes that everything written into c is echoed back to itself. -// It assumes that 0xff is not currently on the wire or in the read buffer. -func resyncConn(t *testing.T, c net.Conn) { - t.Helper() - c.SetDeadline(neverTimeout) - errCh := make(chan error) - go func() { - _, err := c.Write([]byte{0xff}) - errCh <- err - }() - buf := make([]byte, 1024) - for { - n, err := c.Read(buf) - if n > 0 && bytes.IndexByte(buf[:n], 0xff) == n-1 { - break - } - if err != nil { - t.Errorf("unexpected Read error: %v", err) - break - } - } - if err := <-errCh; err != nil { - t.Errorf("unexpected Write error: %v", err) - } -} - -// chunkedCopy copies from r to w in fixed-width chunks to avoid -// causing a Write that exceeds the maximum packet size for packet-based -// connections like "unixpacket". -// We assume that the maximum packet size is at least 1024. -func chunkedCopy(w io.Writer, r io.Reader) error { - b := make([]byte, 1024) - _, err := io.CopyBuffer(struct{ io.Writer }{w}, struct{ io.Reader }{r}, b) - return err -} diff --git a/vendor/golang.org/x/net/nettest/nettest.go b/vendor/golang.org/x/net/nettest/nettest.go deleted file mode 100644 index 717bbb0640..0000000000 --- a/vendor/golang.org/x/net/nettest/nettest.go +++ /dev/null @@ -1,345 +0,0 @@ -// Copyright 2019 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Package nettest provides utilities for network testing. -package nettest - -import ( - "errors" - "fmt" - "io/ioutil" - "net" - "os" - "os/exec" - "runtime" - "strconv" - "strings" - "sync" - "time" -) - -var ( - stackOnce sync.Once - ipv4Enabled bool - ipv6Enabled bool - rawSocketSess bool - aixTechLvl int - - aLongTimeAgo = time.Unix(233431200, 0) - neverTimeout = time.Time{} - - errNoAvailableInterface = errors.New("no available interface") - errNoAvailableAddress = errors.New("no available address") -) - -func probeStack() { - if ln, err := net.Listen("tcp4", "127.0.0.1:0"); err == nil { - ln.Close() - ipv4Enabled = true - } - if ln, err := net.Listen("tcp6", "[::1]:0"); err == nil { - ln.Close() - ipv6Enabled = true - } - rawSocketSess = supportsRawSocket() - if runtime.GOOS == "aix" { - out, err := exec.Command("oslevel", "-s").Output() - if err == nil { - aixTechLvl, _ = strconv.Atoi(string(out[5:7])) - } - } -} - -func aixTechLevel() int { - stackOnce.Do(probeStack) - return aixTechLvl -} - -// SupportsIPv4 reports whether the platform supports IPv4 networking -// functionality. -func SupportsIPv4() bool { - stackOnce.Do(probeStack) - return ipv4Enabled -} - -// SupportsIPv6 reports whether the platform supports IPv6 networking -// functionality. -func SupportsIPv6() bool { - stackOnce.Do(probeStack) - return ipv6Enabled -} - -// SupportsRawSocket reports whether the current session is available -// to use raw sockets. -func SupportsRawSocket() bool { - stackOnce.Do(probeStack) - return rawSocketSess -} - -// TestableNetwork reports whether network is testable on the current -// platform configuration. -// -// See func Dial of the standard library for the supported networks. -func TestableNetwork(network string) bool { - ss := strings.Split(network, ":") - switch ss[0] { - case "ip+nopriv": - // This is an internal network name for testing on the - // package net of the standard library. - switch runtime.GOOS { - case "android", "fuchsia", "hurd", "js", "nacl", "plan9", "windows": - return false - case "darwin": - // iOS doesn't support it. - if runtime.GOARCH == "arm" || runtime.GOARCH == "arm64" { - return false - } - } - case "ip", "ip4", "ip6": - switch runtime.GOOS { - case "fuchsia", "hurd", "js", "nacl", "plan9": - return false - default: - if os.Getuid() != 0 { - return false - } - } - case "unix", "unixgram": - switch runtime.GOOS { - case "android", "fuchsia", "hurd", "js", "nacl", "plan9", "windows": - return false - case "aix": - // Unix network isn't properly working on AIX - // 7.2 with Technical Level < 2. - if aixTechLevel() < 2 { - return false - } - return true - case "darwin": - // iOS does not support unix, unixgram. - if runtime.GOARCH == "arm" || runtime.GOARCH == "arm64" { - return false - } - } - case "unixpacket": - switch runtime.GOOS { - case "aix", "android", "fuchsia", "hurd", "darwin", "js", "nacl", "plan9", "windows": - return false - case "netbsd": - // It passes on amd64 at least. 386 fails - // (Issue 22927). arm is unknown. - if runtime.GOARCH == "386" { - return false - } - } - } - switch ss[0] { - case "tcp4", "udp4", "ip4": - return SupportsIPv4() - case "tcp6", "udp6", "ip6": - return SupportsIPv6() - } - return true -} - -// TestableAddress reports whether address of network is testable on -// the current platform configuration. -func TestableAddress(network, address string) bool { - switch ss := strings.Split(network, ":"); ss[0] { - case "unix", "unixgram", "unixpacket": - // Abstract unix domain sockets, a Linux-ism. - if address[0] == '@' && runtime.GOOS != "linux" { - return false - } - } - return true -} - -// NewLocalListener returns a listener which listens to a loopback IP -// address or local file system path. -// -// The provided network must be "tcp", "tcp4", "tcp6", "unix" or -// "unixpacket". -func NewLocalListener(network string) (net.Listener, error) { - switch network { - case "tcp": - if SupportsIPv4() { - if ln, err := net.Listen("tcp4", "127.0.0.1:0"); err == nil { - return ln, nil - } - } - if SupportsIPv6() { - return net.Listen("tcp6", "[::1]:0") - } - case "tcp4": - if SupportsIPv4() { - return net.Listen("tcp4", "127.0.0.1:0") - } - case "tcp6": - if SupportsIPv6() { - return net.Listen("tcp6", "[::1]:0") - } - case "unix", "unixpacket": - path, err := LocalPath() - if err != nil { - return nil, err - } - return net.Listen(network, path) - } - return nil, fmt.Errorf("%s is not supported on %s/%s", network, runtime.GOOS, runtime.GOARCH) -} - -// NewLocalPacketListener returns a packet listener which listens to a -// loopback IP address or local file system path. -// -// The provided network must be "udp", "udp4", "udp6" or "unixgram". -func NewLocalPacketListener(network string) (net.PacketConn, error) { - switch network { - case "udp": - if SupportsIPv4() { - if c, err := net.ListenPacket("udp4", "127.0.0.1:0"); err == nil { - return c, nil - } - } - if SupportsIPv6() { - return net.ListenPacket("udp6", "[::1]:0") - } - case "udp4": - if SupportsIPv4() { - return net.ListenPacket("udp4", "127.0.0.1:0") - } - case "udp6": - if SupportsIPv6() { - return net.ListenPacket("udp6", "[::1]:0") - } - case "unixgram": - path, err := LocalPath() - if err != nil { - return nil, err - } - return net.ListenPacket(network, path) - } - return nil, fmt.Errorf("%s is not supported on %s/%s", network, runtime.GOOS, runtime.GOARCH) -} - -// LocalPath returns a local path that can be used for Unix-domain -// protocol testing. -func LocalPath() (string, error) { - f, err := ioutil.TempFile("", "go-nettest") - if err != nil { - return "", err - } - path := f.Name() - f.Close() - os.Remove(path) - return path, nil -} - -// MulticastSource returns a unicast IP address on ifi when ifi is an -// IP multicast-capable network interface. -// -// The provided network must be "ip", "ip4" or "ip6". -func MulticastSource(network string, ifi *net.Interface) (net.IP, error) { - switch network { - case "ip", "ip4", "ip6": - default: - return nil, errNoAvailableAddress - } - if ifi == nil || ifi.Flags&net.FlagUp == 0 || ifi.Flags&net.FlagMulticast == 0 { - return nil, errNoAvailableAddress - } - ip, ok := hasRoutableIP(network, ifi) - if !ok { - return nil, errNoAvailableAddress - } - return ip, nil -} - -// LoopbackInterface returns an available logical network interface -// for loopback test. -func LoopbackInterface() (*net.Interface, error) { - ift, err := net.Interfaces() - if err != nil { - return nil, errNoAvailableInterface - } - for _, ifi := range ift { - if ifi.Flags&net.FlagLoopback != 0 && ifi.Flags&net.FlagUp != 0 { - return &ifi, nil - } - } - return nil, errNoAvailableInterface -} - -// RoutedInterface returns a network interface that can route IP -// traffic and satisfies flags. -// -// The provided network must be "ip", "ip4" or "ip6". -func RoutedInterface(network string, flags net.Flags) (*net.Interface, error) { - switch network { - case "ip", "ip4", "ip6": - default: - return nil, errNoAvailableInterface - } - ift, err := net.Interfaces() - if err != nil { - return nil, errNoAvailableInterface - } - for _, ifi := range ift { - if ifi.Flags&flags != flags { - continue - } - if _, ok := hasRoutableIP(network, &ifi); !ok { - continue - } - return &ifi, nil - } - return nil, errNoAvailableInterface -} - -func hasRoutableIP(network string, ifi *net.Interface) (net.IP, bool) { - ifat, err := ifi.Addrs() - if err != nil { - return nil, false - } - for _, ifa := range ifat { - switch ifa := ifa.(type) { - case *net.IPAddr: - if ip, ok := routableIP(network, ifa.IP); ok { - return ip, true - } - case *net.IPNet: - if ip, ok := routableIP(network, ifa.IP); ok { - return ip, true - } - } - } - return nil, false -} - -func routableIP(network string, ip net.IP) (net.IP, bool) { - if !ip.IsLoopback() && !ip.IsLinkLocalUnicast() && !ip.IsGlobalUnicast() { - return nil, false - } - switch network { - case "ip4": - if ip := ip.To4(); ip != nil { - return ip, true - } - case "ip6": - if ip.IsLoopback() { // addressing scope of the loopback address depends on each implementation - return nil, false - } - if ip := ip.To16(); ip != nil && ip.To4() == nil { - return ip, true - } - default: - if ip := ip.To4(); ip != nil { - return ip, true - } - if ip := ip.To16(); ip != nil { - return ip, true - } - } - return nil, false -} diff --git a/vendor/golang.org/x/net/nettest/nettest_stub.go b/vendor/golang.org/x/net/nettest/nettest_stub.go deleted file mode 100644 index 2bb8c05762..0000000000 --- a/vendor/golang.org/x/net/nettest/nettest_stub.go +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright 2019 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build !aix,!darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris,!windows - -package nettest - -func supportsRawSocket() bool { - return false -} diff --git a/vendor/golang.org/x/net/nettest/nettest_unix.go b/vendor/golang.org/x/net/nettest/nettest_unix.go deleted file mode 100644 index afff744e8f..0000000000 --- a/vendor/golang.org/x/net/nettest/nettest_unix.go +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright 2019 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris - -package nettest - -import "syscall" - -func supportsRawSocket() bool { - for _, af := range []int{syscall.AF_INET, syscall.AF_INET6} { - s, err := syscall.Socket(af, syscall.SOCK_RAW, 0) - if err != nil { - continue - } - syscall.Close(s) - return true - } - return false -} diff --git a/vendor/golang.org/x/net/nettest/nettest_windows.go b/vendor/golang.org/x/net/nettest/nettest_windows.go deleted file mode 100644 index 4939964db5..0000000000 --- a/vendor/golang.org/x/net/nettest/nettest_windows.go +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright 2019 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package nettest - -import "syscall" - -func supportsRawSocket() bool { - // From http://msdn.microsoft.com/en-us/library/windows/desktop/ms740548.aspx: - // Note: To use a socket of type SOCK_RAW requires administrative privileges. - // Users running Winsock applications that use raw sockets must be a member of - // the Administrators group on the local computer, otherwise raw socket calls - // will fail with an error code of WSAEACCES. On Windows Vista and later, access - // for raw sockets is enforced at socket creation. In earlier versions of Windows, - // access for raw sockets is enforced during other socket operations. - for _, af := range []int{syscall.AF_INET, syscall.AF_INET6} { - s, err := syscall.Socket(af, syscall.SOCK_RAW, 0) - if err != nil { - continue - } - syscall.Closesocket(s) - return true - } - return false -} diff --git a/vendor/modules.txt b/vendor/modules.txt index 0268d73aff..ece3fd9d3b 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -26,7 +26,7 @@ github.com/gorilla/securecookie github.com/hashicorp/yamux # github.com/inconshreveable/mousetrap v1.0.0 github.com/inconshreveable/mousetrap -# github.com/konsorten/go-windows-terminal-sequences v1.0.1 +# github.com/konsorten/go-windows-terminal-sequences v1.0.2 github.com/konsorten/go-windows-terminal-sequences # github.com/kr/pty v1.1.5 github.com/kr/pty @@ -62,6 +62,12 @@ github.com/prometheus/procfs/internal/fs # github.com/sirupsen/logrus v1.4.2 github.com/sirupsen/logrus/hooks/syslog github.com/sirupsen/logrus +# github.com/skycoin/dmsg v0.0.0-20190628092537-e69f75132be9 +github.com/skycoin/dmsg/cipher +github.com/skycoin/dmsg +github.com/skycoin/dmsg/disc +github.com/skycoin/dmsg/noise +github.com/skycoin/dmsg/ioutil # github.com/skycoin/skycoin v0.26.0 github.com/skycoin/skycoin/src/util/logging github.com/skycoin/skycoin/src/cipher @@ -90,7 +96,6 @@ golang.org/x/crypto/poly1305 # golang.org/x/net v0.0.0-20190620200207-3b0461eec859 golang.org/x/net/context golang.org/x/net/proxy -golang.org/x/net/nettest golang.org/x/net/internal/socks # golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb golang.org/x/sys/unix