first simple test
This commit is contained in:
parent
6b90c96a5d
commit
f4dc1b1f42
9
go.mod
9
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.
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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,
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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()
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -1,7 +0,0 @@
|
|||
package roomsrv
|
||||
|
||||
import "fmt"
|
||||
|
||||
func (s *Server) init() error {
|
||||
return fmt.Errorf("TODO:srv:init")
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
|
||||
}
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue