diff --git a/go.mod b/go.mod index 6010c7d..9671d8d 100644 --- a/go.mod +++ b/go.mod @@ -3,8 +3,9 @@ module go.mindeco.de/ssb-rooms go 1.15 require ( + github.com/cryptix/go v1.5.0 github.com/go-kit/kit v0.10.0 - github.com/gorilla/websocket v1.4.1 + github.com/gorilla/websocket v1.4.2 github.com/keks/nocomment v0.0.0-20181007001506-30c6dcb4a472 github.com/pkg/errors v0.9.1 github.com/stretchr/testify v1.6.1 @@ -12,9 +13,9 @@ require ( go.cryptoscope.co/netwrap v0.1.1 go.cryptoscope.co/secretstream v1.2.2 go.mindeco.de/ssb-refs v0.1.1-0.20210108133850-cf1f44fea870 - golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad // indirect - golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e - golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d // indirect + golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad + golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9 + golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f // indirect ) // We need our internal/extra25519 since agl pulled his repo recently. diff --git a/handlers/whoami/whoami.go b/handlers/whoami/whoami.go new file mode 100644 index 0000000..c41c5b0 --- /dev/null +++ b/handlers/whoami/whoami.go @@ -0,0 +1,87 @@ +// SPDX-License-Identifier: MIT + +package whoami + +import ( + "context" + "fmt" + "net" + + "github.com/cryptix/go/logging" + "go.cryptoscope.co/muxrpc/v2" + refs "go.mindeco.de/ssb-refs" + + "go.mindeco.de/ssb-rooms/internal/maybemuxrpc" +) + +var ( + method = muxrpc.Method{"whoami"} +) + +func checkAndLog(log logging.Interface, err error) { + if err != nil { + if err := logging.LogPanicWithStack(log, "checkAndLog", err); err != nil { + log.Log("event", "warning", "msg", "faild to write panic file", "err", err) + } + } +} + +func New(log logging.Interface, id refs.FeedRef) maybemuxrpc.Plugin { + return plugin{handler{ + log: log, + id: id, + }} +} + +type plugin struct { + h handler +} + +func (plugin) Name() string { return "whoami" } + +func (plugin) Method() muxrpc.Method { return method } + +func (wami plugin) Handler() muxrpc.Handler { return wami.h } + +func (plugin) Authorize(net.Conn) bool { return true } + +type handler struct { + log logging.Interface + id refs.FeedRef +} + +func (handler) HandleConnect(ctx context.Context, edp muxrpc.Endpoint) {} + +func (h handler) HandleCall(ctx context.Context, req *muxrpc.Request, edp muxrpc.Endpoint) { + // TODO: push manifest check into muxrpc + if req.Type == "" { + req.Type = "async" + } + if req.Method.String() != "whoami" { + req.CloseWithError(fmt.Errorf("wrong method")) + return + } + type ret struct { + ID string `json:"id"` + } + + err := req.Return(ctx, ret{h.id.Ref()}) + checkAndLog(h.log, err) +} + +type endpoint struct { + edp muxrpc.Endpoint +} + +func (edp endpoint) WhoAmI(ctx context.Context) (refs.FeedRef, error) { + var resp struct { + ID refs.FeedRef `json:"id"` + } + + err := edp.edp.Async(ctx, &resp, muxrpc.TypeJSON, method) + if err != nil { + return refs.FeedRef{}, fmt.Errorf("error making async call: %w", err) + } + + return resp.ID, nil +} diff --git a/internal/maybemod/keys/keys.go b/internal/maybemod/keys/keys.go index e89043b..500bc72 100644 --- a/internal/maybemod/keys/keys.go +++ b/internal/maybemod/keys/keys.go @@ -21,21 +21,21 @@ var SecretPerms = os.FileMode(0600) // KeyPair contains a seret handshake keypair and the assosicated feed type KeyPair struct { - Feed *refs.FeedRef + Feed refs.FeedRef Pair secrethandshake.EdKeyPair } // the format of the .ssb/secret file as defined by the js implementations type ssbSecret struct { - Curve string `json:"curve"` - ID *refs.FeedRef `json:"id"` - Private string `json:"private"` - Public string `json:"public"` + Curve string `json:"curve"` + ID refs.FeedRef `json:"id"` + Private string `json:"private"` + Public string `json:"public"` } // IsValidFeedFormat checks if the passed FeedRef is for one of the two supported formats, // legacy/crapp or GabbyGrove. -func IsValidFeedFormat(r *refs.FeedRef) error { +func IsValidFeedFormat(r refs.FeedRef) error { if r.Algo != refs.RefAlgoFeedSSB1 && r.Algo != refs.RefAlgoFeedGabby { return fmt.Errorf("ssb: unsupported feed format:%s", r.Algo) } @@ -52,7 +52,10 @@ func NewKeyPair(r io.Reader) (*KeyPair, error) { } keyPair := KeyPair{ - Feed: &refs.FeedRef{ID: kp.Public[:], Algo: refs.RefAlgoFeedSSB1}, + Feed: refs.FeedRef{ + ID: kp.Public[:], + Algo: refs.RefAlgoFeedSSB1, + }, Pair: *kp, } @@ -61,7 +64,7 @@ func NewKeyPair(r io.Reader) (*KeyPair, error) { // SaveKeyPair serializes the passed KeyPair to path. // It errors if path already exists. -func SaveKeyPair(kp *KeyPair, path string) error { +func SaveKeyPair(kp KeyPair, path string) error { if err := IsValidFeedFormat(kp.Feed); err != nil { return err } @@ -89,7 +92,7 @@ func SaveKeyPair(kp *KeyPair, path string) error { } // EncodeKeyPairAsJSON serializes the passed Keypair into the writer w -func EncodeKeyPairAsJSON(kp *KeyPair, w io.Writer) error { +func EncodeKeyPairAsJSON(kp KeyPair, w io.Writer) error { var sec = ssbSecret{ Curve: "ed25519", ID: kp.Feed, diff --git a/internal/maybemod/multicloser/testutils/logging.go b/internal/maybemod/testutils/logging.go similarity index 97% rename from internal/maybemod/multicloser/testutils/logging.go rename to internal/maybemod/testutils/logging.go index 10c6dfe..acbb918 100644 --- a/internal/maybemod/multicloser/testutils/logging.go +++ b/internal/maybemod/testutils/logging.go @@ -14,7 +14,7 @@ import ( func NewRelativeTimeLogger(w io.Writer) log.Logger { if w == nil { - w = os.Stderr + w = log.NewSyncWriter(os.Stderr) } var rtl relTimeLogger diff --git a/internal/maybemod/multicloser/testutils/mergeErrChans.go b/internal/maybemod/testutils/mergeErrChans.go similarity index 100% rename from internal/maybemod/multicloser/testutils/mergeErrChans.go rename to internal/maybemod/testutils/mergeErrChans.go diff --git a/internal/network/interface.go b/internal/network/interface.go index ac32a0f..de5a0e7 100644 --- a/internal/network/interface.go +++ b/internal/network/interface.go @@ -28,7 +28,7 @@ type Network interface { GetListenAddr() net.Addr GetAllEndpoints() []EndpointStat - GetEndpointFor(*refs.FeedRef) (muxrpc.Endpoint, bool) + GetEndpointFor(refs.FeedRef) (muxrpc.Endpoint, bool) GetConnTracker() ConnTracker diff --git a/internal/network/new.go b/internal/network/new.go index f163f35..c29748c 100644 --- a/internal/network/new.go +++ b/internal/network/new.go @@ -151,10 +151,7 @@ func (n *node) GetConnTracker() ConnTracker { // GetEndpointFor returns a muxrpc endpoint to call the remote identified by the passed feed ref // retruns false if there is no such connection // TODO: merge with conntracker -func (n *node) GetEndpointFor(ref *refs.FeedRef) (muxrpc.Endpoint, bool) { - if ref == nil { - return nil, false - } +func (n *node) GetEndpointFor(ref refs.FeedRef) (muxrpc.Endpoint, bool) { n.remotesLock.Lock() defer n.remotesLock.Unlock() diff --git a/internal/repo/secret.go b/internal/repo/secret.go index 9a4fa95..a12fafa 100644 --- a/internal/repo/secret.go +++ b/internal/repo/secret.go @@ -24,7 +24,7 @@ func DefaultKeyPair(r Interface) (*keys.KeyPair, error) { if err != nil { return nil, fmt.Errorf("repo: no keypair but couldn't create one either: %w", err) } - if err := keys.SaveKeyPair(keyPair, secPath); err != nil { + if err := keys.SaveKeyPair(*keyPair, secPath); err != nil { return nil, fmt.Errorf("repo: error saving new identity file: %w", err) } log.Printf("saved identity %s to %s", keyPair.Feed.Ref(), secPath) @@ -62,7 +62,7 @@ func newKeyPair(r Interface, name, algo string, seed io.Reader) (*keys.KeyPair, return nil, fmt.Errorf("repo: no keypair but couldn't create one either: %w", err) } keyPair.Feed.Algo = algo - if err := keys.SaveKeyPair(keyPair, secPath); err != nil { + if err := keys.SaveKeyPair(*keyPair, secPath); err != nil { return nil, fmt.Errorf("repo: error saving new identity file: %w", err) } log.Printf("saved identity %s to %s", keyPair.Feed.Ref(), secPath) diff --git a/roomsrv/init.go b/roomsrv/init.go deleted file mode 100644 index edd3315..0000000 --- a/roomsrv/init.go +++ /dev/null @@ -1,7 +0,0 @@ -package roomsrv - -import "fmt" - -func (s *Server) init() error { - return fmt.Errorf("TODO:srv:init") -} diff --git a/roomsrv/init_network.go b/roomsrv/init_network.go new file mode 100644 index 0000000..04fef68 --- /dev/null +++ b/roomsrv/init_network.go @@ -0,0 +1,111 @@ +package roomsrv + +import ( + "fmt" + "net" + "sync" + + kitlog "github.com/go-kit/kit/log" + "go.cryptoscope.co/muxrpc/v2" + + refs "go.mindeco.de/ssb-refs" + "go.mindeco.de/ssb-rooms/handlers/whoami" + "go.mindeco.de/ssb-rooms/internal/maybemuxrpc" + "go.mindeco.de/ssb-rooms/internal/network" +) + +func (s *Server) initNetwork() error { + s.authorizer.lst = make(map[string]struct{}) + + // muxrpc handler creation and authoratization decider + mkHandler := func(conn net.Conn) (muxrpc.Handler, error) { + // bypassing badger-close bug to go through with an accept (or not) before closing the bot + s.closedMu.Lock() + defer s.closedMu.Unlock() + + // todo: master authorize()er + remote, err := network.GetFeedRefFromAddr(conn.RemoteAddr()) + if err != nil { + return nil, fmt.Errorf("sbot: expected an address containing an shs-bs addr: %w", err) + } + if s.keyPair.Feed.Equal(remote) { + return s.master.MakeHandler(conn) + } + + if s.authorizer.Authorize(conn) { + return s.public.MakeHandler(conn) + } + + return nil, fmt.Errorf("not authorized") + } + + // whoami + whoami := whoami.New(kitlog.With(s.logger, "unit", "whoami"), s.keyPair.Feed) + s.public.Register(whoami) + s.master.Register(whoami) + + // s.master.Register(replicate.NewPlug(s.Users)) + + // tcp+shs + opts := network.Options{ + Logger: s.logger, + Dialer: s.dialer, + ListenAddr: s.listenAddr, + KeyPair: s.keyPair, + AppKey: s.appKey[:], + MakeHandler: mkHandler, + ConnTracker: s.networkConnTracker, + BefreCryptoWrappers: s.preSecureWrappers, + AfterSecureWrappers: s.postSecureWrappers, + + WebsocketAddr: s.wsAddr, + } + + var err error + s.Network, err = network.New(opts) + if err != nil { + return fmt.Errorf("failed to create network node: %w", err) + } + + return nil +} + +func (srv *Server) Allow(r refs.FeedRef, yes bool) { + if yes { + srv.authorizer.Add(r) + } else { + srv.authorizer.Remove(r) + } +} + +type listAuthorizer struct { + mu sync.Mutex + lst map[string]struct{} +} + +var _ maybemuxrpc.Authorizer = (*listAuthorizer)(nil) + +func (a *listAuthorizer) Add(feed refs.FeedRef) { + a.mu.Lock() + defer a.mu.Unlock() + a.lst[feed.Ref()] = struct{}{} +} + +func (a *listAuthorizer) Remove(feed refs.FeedRef) { + a.mu.Lock() + defer a.mu.Unlock() + delete(a.lst, feed.Ref()) +} + +func (a *listAuthorizer) Authorize(remote net.Conn) bool { + remoteID, err := network.GetFeedRefFromAddr(remote.RemoteAddr()) + if err != nil { + return false + } + + a.mu.Lock() + defer a.mu.Unlock() + + _, has := a.lst[remoteID.Ref()] + return has +} diff --git a/roomsrv/init_unixsock.go b/roomsrv/init_unixsock.go new file mode 100644 index 0000000..553d64f --- /dev/null +++ b/roomsrv/init_unixsock.go @@ -0,0 +1,111 @@ +// SPDX-License-Identifier: MIT + +package roomsrv + +import ( + "fmt" + "net" + "os" + "path/filepath" + + kitlog "github.com/go-kit/kit/log" + "github.com/go-kit/kit/log/level" + "go.cryptoscope.co/muxrpc/v2" + + "go.mindeco.de/ssb-rooms/internal/netwraputil" + "go.mindeco.de/ssb-rooms/internal/repo" +) + +// WithUNIXSocket enables listening for muxrpc connections on a unix socket files ($repo/socket). +// This socket is not encrypted or authenticated since access to it is mediated by filesystem ownership. +func WithUNIXSocket(yes bool) Option { + return func(s *Server) error { + s.loadUnixSock = yes + return nil + } +} + +func (s *Server) initUnixSock() error { + // this races because sbot might not be done with init yet + // TODO: refactor network peer code and make unixsock implement that (those will be inited late anyway) + if s.keyPair == nil { + return fmt.Errorf("sbot/unixsock: keypair is nil. please use unixSocket with LateOption") + } + spoofWrapper := netwraputil.SpoofRemoteAddress(s.keyPair.Feed.ID) + + r := repo.New(s.repoPath) + sockPath := r.GetPath("socket") + + // local clients (not using network package because we don't want conn limiting or advertising) + c, err := net.Dial("unix", sockPath) + if err == nil { + c.Close() + return fmt.Errorf("sbot: repo already in use, socket accepted connection") + } + os.Remove(sockPath) + os.MkdirAll(filepath.Dir(sockPath), 0700) + + uxLis, err := net.Listen("unix", sockPath) + if err != nil { + return err + } + s.closers.Add(uxLis) + + go func() { + + for { + c, err := uxLis.Accept() + if err != nil { + if nerr, ok := err.(*net.OpError); ok { + if nerr.Err.Error() == "use of closed network connection" { + return + } + } + + level.Warn(s.logger).Log("event", "unix sock accept failed", "err", err) + continue + } + + wc, err := spoofWrapper(c) + if err != nil { + c.Close() + continue + } + for _, w := range s.postSecureWrappers { + var err error + wc, err = w(wc) + if err != nil { + level.Warn(s.logger).Log("err", err) + c.Close() + continue + } + } + + go func(conn net.Conn) { + defer conn.Close() + + pkr := muxrpc.NewPacker(conn) + + h, err := s.master.MakeHandler(conn) + if err != nil { + level.Warn(s.logger).Log("event", "unix sock make handler", "err", err) + return + } + + edp := muxrpc.Handle(pkr, h, + muxrpc.WithContext(s.rootCtx), + muxrpc.WithLogger(kitlog.NewNopLogger()), + ) + + srv := edp.(muxrpc.Server) + if err := srv.Serve(); err != nil { + level.Warn(s.logger).Log("conn", "serve exited", "err", err, "peer", conn.RemoteAddr()) + } + edp.Terminate() + + }(wc) + } + }() + return nil + +} diff --git a/roomsrv/options.go b/roomsrv/options.go index 3c6ccac..b3cc96e 100644 --- a/roomsrv/options.go +++ b/roomsrv/options.go @@ -11,6 +11,7 @@ import ( kitlog "github.com/go-kit/kit/log" "go.cryptoscope.co/netwrap" "go.mindeco.de/ssb-rooms/internal/maybemod/keys" + "go.mindeco.de/ssb-rooms/internal/network" "go.mindeco.de/ssb-rooms/internal/repo" ) @@ -41,7 +42,7 @@ func WithNamedKeyPair(name string) Option { return func(s *Server) error { r := repo.New(s.repoPath) var err error - s.KeyPair, err = repo.LoadKeyPair(r, name) + s.keyPair, err = repo.LoadKeyPair(r, name) if err != nil { return fmt.Errorf("loading named key-pair %q failed: %w", name, err) } @@ -54,7 +55,7 @@ func WithNamedKeyPair(name string) Option { func WithJSONKeyPair(blob string) Option { return func(s *Server) error { var err error - s.KeyPair, err = keys.ParseKeyPair(strings.NewReader(blob)) + s.keyPair, err = keys.ParseKeyPair(strings.NewReader(blob)) if err != nil { return fmt.Errorf("JSON KeyPair decode failed: %w", err) } @@ -65,7 +66,7 @@ func WithJSONKeyPair(blob string) Option { // WithKeyPair exepect a initialized ssb.KeyPair. Useful for testing. func WithKeyPair(kp *keys.KeyPair) Option { return func(s *Server) error { - s.KeyPair = kp + s.keyPair = kp return nil } } @@ -90,6 +91,23 @@ func WithContext(ctx context.Context) Option { // TODO: remove all this network stuff and make them options on network +// WithDialer changes the function that is used to dial remote peers. +// This could be a sock5 connection builder to support tor proxying to hidden services. +func WithDialer(dial netwrap.Dialer) Option { + return func(s *Server) error { + s.dialer = dial + return nil + } +} + +// WithNetworkConnTracker changes the connection tracker. See network.NewLastWinsTracker and network.NewAcceptAllTracker. +func WithNetworkConnTracker(ct network.ConnTracker) Option { + return func(s *Server) error { + s.networkConnTracker = ct + return nil + } +} + // WithListenAddr changes the muxrpc listener address. By default it listens to ':8008'. func WithListenAddr(addr string) Option { return func(s *Server) error { diff --git a/roomsrv/server.go b/roomsrv/server.go index 1004cbb..b3474ff 100644 --- a/roomsrv/server.go +++ b/roomsrv/server.go @@ -16,6 +16,7 @@ import ( "github.com/go-kit/kit/log/level" "go.cryptoscope.co/netwrap" + refs "go.mindeco.de/ssb-refs" "go.mindeco.de/ssb-rooms/internal/maybemod/keys" "go.mindeco.de/ssb-rooms/internal/maybemod/multicloser" "go.mindeco.de/ssb-rooms/internal/maybemuxrpc" @@ -37,10 +38,13 @@ type Server struct { Network network.Network appKey []byte listenAddr net.Addr + wsAddr string dialer netwrap.Dialer + loadUnixSock bool + repoPath string - KeyPair *keys.KeyPair + keyPair *keys.KeyPair networkConnTracker network.ConnTracker preSecureWrappers []netwrap.ConnWrapper @@ -49,7 +53,11 @@ type Server struct { public maybemuxrpc.PluginManager master maybemuxrpc.PluginManager - authorizer maybemuxrpc.Authorizer + authorizer listAuthorizer +} + +func (s Server) Whoami() refs.FeedRef { + return s.keyPair.Feed } func New(opts ...Option) (*Server, error) { @@ -102,18 +110,24 @@ func New(opts ...Option) (*Server, error) { r := repo.New(s.repoPath) - if s.KeyPair == nil { + if s.keyPair == nil { var err error - s.KeyPair, err = repo.DefaultKeyPair(r) + s.keyPair, err = repo.DefaultKeyPair(r) if err != nil { return nil, fmt.Errorf("sbot: failed to get keypair: %w", err) } } - if err := s.init(); err != nil { + if err := s.initNetwork(); err != nil { return nil, err } + if s.loadUnixSock { + if err := s.initUnixSock(); err != nil { + return nil, err + } + } + return &s, nil } diff --git a/roomsrv/unixsock.go b/roomsrv/unixsock.go deleted file mode 100644 index 5753ecc..0000000 --- a/roomsrv/unixsock.go +++ /dev/null @@ -1,105 +0,0 @@ -// SPDX-License-Identifier: MIT - -package roomsrv - -import ( - "fmt" - "net" - "os" - "path/filepath" - - kitlog "github.com/go-kit/kit/log" - "github.com/go-kit/kit/log/level" - "go.cryptoscope.co/muxrpc/v2" - - "go.mindeco.de/ssb-rooms/internal/netwraputil" - "go.mindeco.de/ssb-rooms/internal/repo" -) - -// WithUNIXSocket enables listening for muxrpc connections on a unix socket files ($repo/socket). -// This socket is not encrypted or authenticated since access to it is mediated by filesystem ownership. -func WithUNIXSocket() Option { - return func(s *Server) error { - // this races because sbot might not be done with init yet - // TODO: refactor network peer code and make unixsock implement that (those will be inited late anyway) - if s.KeyPair == nil { - return fmt.Errorf("sbot/unixsock: keypair is nil. please use unixSocket with LateOption") - } - spoofWrapper := netwraputil.SpoofRemoteAddress(s.KeyPair.Feed.ID) - - r := repo.New(s.repoPath) - sockPath := r.GetPath("socket") - - // local clients (not using network package because we don't want conn limiting or advertising) - c, err := net.Dial("unix", sockPath) - if err == nil { - c.Close() - return fmt.Errorf("sbot: repo already in use, socket accepted connection") - } - os.Remove(sockPath) - os.MkdirAll(filepath.Dir(sockPath), 0700) - - uxLis, err := net.Listen("unix", sockPath) - if err != nil { - return err - } - s.closers.Add(uxLis) - - go func() { - - for { - c, err := uxLis.Accept() - if err != nil { - if nerr, ok := err.(*net.OpError); ok { - if nerr.Err.Error() == "use of closed network connection" { - return - } - } - - level.Warn(s.logger).Log("event", "unix sock accept failed", "err", err) - continue - } - - wc, err := spoofWrapper(c) - if err != nil { - c.Close() - continue - } - for _, w := range s.postSecureWrappers { - var err error - wc, err = w(wc) - if err != nil { - level.Warn(s.logger).Log("err", err) - c.Close() - continue - } - } - - go func(conn net.Conn) { - defer conn.Close() - - pkr := muxrpc.NewPacker(conn) - - h, err := s.master.MakeHandler(conn) - if err != nil { - level.Warn(s.logger).Log("event", "unix sock make handler", "err", err) - return - } - - edp := muxrpc.Handle(pkr, h, - muxrpc.WithContext(s.rootCtx), - muxrpc.WithLogger(kitlog.NewNopLogger()), - ) - - srv := edp.(muxrpc.Server) - if err := srv.Serve(); err != nil { - level.Warn(s.logger).Log("conn", "serve exited", "err", err, "peer", conn.RemoteAddr()) - } - edp.Terminate() - - }(wc) - } - }() - return nil - } -} diff --git a/test/go/simple_test.go b/test/go/simple_test.go index 156c9aa..d9ba288 100644 --- a/test/go/simple_test.go +++ b/test/go/simple_test.go @@ -3,107 +3,93 @@ package go_test import ( "context" "crypto/rand" - "net" - "os" - "path/filepath" "testing" + "time" - "github.com/go-kit/kit/log" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "go.cryptoscope.co/muxrpc/v2/debug" + "go.cryptoscope.co/muxrpc/v2" "golang.org/x/sync/errgroup" - "go.mindeco.de/ssb-rooms/internal/maybemod/multicloser/testutils" + refs "go.mindeco.de/ssb-refs" "go.mindeco.de/ssb-rooms/roomsrv" ) func TestTunnelServerSimple(t *testing.T) { - // defer leakcheck.Check(t) r := require.New(t) - if testing.Short() { - return - } - ctx, cancel := context.WithCancel(context.TODO()) + a := assert.New(t) + + // defer leakcheck.Check(t) + testInit(t) + + ctx, cancel := context.WithCancel(context.Background()) botgroup, ctx := errgroup.WithContext(ctx) - info := testutils.NewRelativeTimeLogger(nil) - bs := newBotServer(ctx, info) - - os.RemoveAll("testrun") + bs := newBotServer(ctx, mainLog) appKey := make([]byte, 32) rand.Read(appKey) - hmacKey := make([]byte, 32) - rand.Read(hmacKey) - srvLog := log.With(info, "peer", "srv") - srv, err := roomsrv.New( + netOpts := []roomsrv.Option{ roomsrv.WithAppKey(appKey), roomsrv.WithContext(ctx), - roomsrv.WithLogger(srvLog), - roomsrv.WithPostSecureConnWrapper(func(conn net.Conn) (net.Conn, error) { - return debug.WrapDump(filepath.Join("testrun", t.Name(), "muxdump"), conn) - }), - roomsrv.WithRepoPath(filepath.Join("testrun", t.Name(), "srv")), - roomsrv.WithListenAddr(":0"), - ) - r.NoError(err) - botgroup.Go(bs.Serve(srv)) - - bobLog := log.With(info, "peer", "bob") - bob, err := roomsrv.New( - roomsrv.WithAppKey(appKey), - roomsrv.WithContext(ctx), - roomsrv.WithLogger(bobLog), - roomsrv.WithRepoPath(filepath.Join("testrun", t.Name(), "bob")), - roomsrv.WithListenAddr(":0"), - ) - r.NoError(err) - botgroup.Go(bs.Serve(bob)) - - // TODO - // srv.Replicate(bob.KeyPair.Id) - // bob.Replicate(srv.KeyPair.Id) - - sess := &simpleSession{ - ctx: ctx, - srv: srv, - bob: bob, - redial: func(t *testing.T) { - t.Log("noop") - }, } - tests := []struct { - name string - tf func(t *testing.T) - }{ - {"empty", sess.simple}, - // {"justMe", sess.wantFirst}, - // {"eachOne", sess.eachOne}, - // {"eachOneConnet", sess.eachOneConnet}, - // {"eachOneBothWant", sess.eachOnBothWant}, - } + serv := makeNamedTestBot(t, "srv", netOpts) + botgroup.Go(bs.Serve(serv)) - // all on a single connection + botA := makeNamedTestBot(t, "B", netOpts) + botgroup.Go(bs.Serve(botA)) + + botB := makeNamedTestBot(t, "C", netOpts) + botgroup.Go(bs.Serve(botB)) + + theBots := []*roomsrv.Server{serv, botA, botB} + + // only allow B to dial A + serv.Allow(botA.Whoami(), true) + + // allow bots to dial the remote + botA.Allow(serv.Whoami(), true) + botB.Allow(serv.Whoami(), true) + + // dial up B->A and C->A + + // should work (we allowed A) + err := botA.Network.Connect(ctx, serv.Network.GetListenAddr()) + r.NoError(err, "connect A to the Server") + + // shouldn't work (we did not allowed A) + err = botB.Network.Connect(ctx, serv.Network.GetListenAddr()) + r.NoError(err, "connect B to the Server") // we dont see an error because it just establishes the tcp connection + + edpOfA, has := botA.Network.GetEndpointFor(serv.Whoami()) + r.True(has, "botA has no endpoint for the server") + + var srvWho struct { + ID refs.FeedRef + } + err = edpOfA.Async(ctx, &srvWho, muxrpc.TypeJSON, muxrpc.Method{"whoami"}) r.NoError(err) - for _, tc := range tests { - t.Run("noop/"+tc.name, tc.tf) + + t.Log("server whoami:", srvWho.ID.Ref()) + a.True(serv.Whoami().Equal(&srvWho.ID)) + + edpOfB, has := botB.Network.GetEndpointFor(serv.Whoami()) + r.False(has, "botB has an endpoint for the server!") + if edpOfB != nil { + t.Error("should not have an endpoint on B") + err = edpOfB.Async(ctx, &srvWho, muxrpc.TypeJSON, muxrpc.Method{"whoami"}) + r.Error(err) + t.Log(srvWho.ID.Ref()) } - srv.Shutdown() - bob.Shutdown() + // cleanup cancel() - - r.NoError(srv.Close()) - r.NoError(bob.Close()) + time.Sleep(1 * time.Second) + for _, bot := range theBots { + bot.Shutdown() + r.NoError(bot.Close()) + } r.NoError(botgroup.Wait()) } - -type simpleSession struct { - ctx context.Context - - redial func(t *testing.T) - - srv, bob *roomsrv.Server -} diff --git a/test/go/utils_test.go b/test/go/utils_test.go index 0d33ca2..98cf299 100644 --- a/test/go/utils_test.go +++ b/test/go/utils_test.go @@ -4,12 +4,36 @@ import ( "context" "errors" "io" + "os" + "path/filepath" + "sync" + "testing" "github.com/go-kit/kit/log" "github.com/go-kit/kit/log/level" + "github.com/stretchr/testify/require" + + "go.mindeco.de/ssb-rooms/internal/maybemod/testutils" + "go.mindeco.de/ssb-rooms/internal/network" "go.mindeco.de/ssb-rooms/roomsrv" ) +var ( + initLogging sync.Once + mainLog = log.NewNopLogger() +) + +// cant call testing.pkg in init() +func testInit(t *testing.T) { + initLogging.Do(func() { + if testing.Verbose() { + mainLog = testutils.NewRelativeTimeLogger(nil) + } + }) + + os.RemoveAll(filepath.Join("testrun", t.Name())) +} + type botServer struct { ctx context.Context log log.Logger @@ -31,3 +55,19 @@ func (bs botServer) Serve(s *roomsrv.Server) func() error { return err } } + +func makeNamedTestBot(t testing.TB, name string, opts []roomsrv.Option) *roomsrv.Server { + r := require.New(t) + testPath := filepath.Join("testrun", t.Name(), "bot-"+name) + + botOptions := append(opts, + roomsrv.WithLogger(log.With(mainLog, "bot", name)), + roomsrv.WithRepoPath(testPath), + roomsrv.WithListenAddr(":0"), + roomsrv.WithNetworkConnTracker(network.NewLastWinsTracker()), + ) + + theBot, err := roomsrv.New(botOptions...) + r.NoError(err) + return theBot +}