From 677f5e2c4f02a9b187668d74b7dc8f015db0b083 Mon Sep 17 00:00:00 2001 From: Roman Nikitin Date: Fri, 15 Jul 2022 02:00:49 +0300 Subject: [PATCH] Multiple changes: - Add example exploits for testing - Running client in network mode host - Major refactoring in tests & proto - Fix distribution tests - Better wording in readme - Pubsub rewritten in generics - More tests --- README.md | 26 +- client_env/start.sh | 1 + cmd/client/cli/add.go | 9 +- cmd/client/cli/dry_run.go | 12 +- cmd/client/cli/info.go | 8 +- cmd/client/cli/set_disabled.go | 6 +- cmd/client/cli/single.go | 4 +- examples/simple.py | 9 + examples/spammy.py | 8 + examples/timeout.py | 9 + examples/unkillable.py | 12 + internal/client/client.go | 11 +- internal/config/config.go | 22 +- internal/exploit/cache.go | 4 +- internal/exploit/cache_test.go | 5 +- internal/exploit/runner.go | 10 +- internal/exploit/storage.go | 38 +-- internal/exploit/storage_test.go | 14 +- internal/exploit/submit_loop_test.go | 13 +- internal/exploit/utils.go | 4 +- internal/exploit/utils_test.go | 154 +++------ internal/queue/endless_test.go | 29 +- internal/queue/simple_test.go | 47 +-- internal/server/server.go | 149 ++++---- internal/server/server_test.go | 69 ++-- internal/server/storage_test.go | 62 ++-- lib/genproto/neo/neo.pb.go | 485 +++++++++++++-------------- lib/genproto/neo/neo_grpc.pb.go | 73 ++-- lib/proto/neo.proto | 121 +++---- pkg/archive/targz.go | 16 +- pkg/filestream/filestream.go | 2 +- pkg/filestream/filestream_test.go | 16 +- pkg/gstream/dynsize_test.go | 70 ++-- pkg/hostbucket/hostbucket_test.go | 115 +++---- pkg/pubsub/pubsub.go | 37 +- pkg/pubsub/pubsub_test.go | 86 ++--- pkg/pubsub/subscription.go | 45 +-- pkg/pubsub/subscription_test.go | 54 ++- pkg/rendezvous/rendezvous_test.go | 13 +- pkg/tasklogger/logger.go | 12 +- pkg/testutils/testutils.go | 3 +- 41 files changed, 824 insertions(+), 1059 deletions(-) create mode 100755 examples/simple.py create mode 100755 examples/spammy.py create mode 100755 examples/timeout.py create mode 100755 examples/unkillable.py diff --git a/README.md b/README.md index fcf3a00..d2a3fda 100644 --- a/README.md +++ b/README.md @@ -8,15 +8,18 @@ Client + server for exploit distribution during Attack & Defence CTF competition ## Why -Usually during large CTFs, where the regular laptop can't run the exploits on all participants, teams start some cloud -servers to run these exploits on, but the administration and access management can be a pain and wastes time. **Neo** -can solve this problem by doing 2 things: +Usually during large CTFs, where the regular laptop can't run the exploits on all participants, teams rent cloud +servers to run the exploits. However, uploading, managing and monitoring the exploits on a remote machine +can be a pain and wastes time. **Neo** helps in two primary ways: -1. Every player can start an instance of Neo client and attack a proportional part of the whole team pool automatically. +1. Every player can start an instance of **Neo client** and attack a proportional part of the whole team pool + automatically. -2. Exploit writers don't copy the newly-created exploits to the exploit server, or manage the distribution by hand, but - rather submit them to the **Neo server** using the same client, and the server does all the work, running the exploit - on available clients. +2. Exploit writers don't upload the newly-created exploits to the exploit server, neither do they manage the + distribution + by hand, but rather submit them to the **Neo server** using the same client, and the server does all the work, + distributing the exploit + among the available clients. ## Usage @@ -33,13 +36,14 @@ Neo uses the exploit farm to acquire the team list and submit the flags. The pro - `POST /api/post_flags` will receive an array of mappings with keys `flag`, `sploit` and `team`. `sploit` is the exploit name for statistics, and `team` is the team name. -Farm password will be passed in `Authorization` and `X-Token` headers, so the protocol is compatible with ** -DestructiveFarm**. +Farm password will be passed in `Authorization` and `X-Token` headers, so the protocol is compatible with +**DestructiveFarm**. ### Server -Server coordinates the clients and distributes teams to attack among them. It must have access to the farm, and all -clients must have access to the server, so you might want to start it somewhere with the public IP address. +Server coordinates the clients and distributes targets among them. It must have access to the farm, and all +clients must have access both to the server and the farm, so you might want to start it somewhere with the public IP +address. To start the server: diff --git a/client_env/start.sh b/client_env/start.sh index 5c9f12b..4277493 100755 --- a/client_env/start.sh +++ b/client_env/start.sh @@ -31,6 +31,7 @@ else --security-opt apparmor=unconfined \ --cap-add=NET_ADMIN \ --privileged \ + --network host \ --name "${CONTAINER_NAME}" \ --hostname "${CONTAINER_NAME}" \ "${IMAGE}" \ diff --git a/cmd/client/cli/add.go b/cmd/client/cli/add.go index 1948080..c3145a8 100644 --- a/cmd/client/cli/add.go +++ b/cmd/client/cli/add.go @@ -18,6 +18,7 @@ import ( "github.com/sirupsen/logrus" "github.com/spf13/cobra" + "google.golang.org/protobuf/types/known/durationpb" neopb "neo/lib/genproto/neo" ) @@ -93,8 +94,8 @@ func (ac *addCLI) Run(ctx context.Context) error { return fmt.Errorf("failed to get config from server: %w", err) } exists := false - for _, v := range state.GetExploits() { - if v.GetExploitId() == ac.exploitID { + for _, v := range state.Exploits { + if v.ExploitId == ac.exploitID { exists = true break } @@ -150,8 +151,8 @@ func (ac *addCLI) Run(ctx context.Context) error { Config: &neopb.ExploitConfiguration{ Entrypoint: file, IsArchive: ac.isArchive, - RunEvery: ac.runEvery.String(), - Timeout: ac.timeout.String(), + RunEvery: durationpb.New(ac.runEvery), + Timeout: durationpb.New(ac.timeout), }, Endless: ac.endless, Disabled: ac.disabled, diff --git a/cmd/client/cli/dry_run.go b/cmd/client/cli/dry_run.go index e08541c..82ce3ff 100644 --- a/cmd/client/cli/dry_run.go +++ b/cmd/client/cli/dry_run.go @@ -51,14 +51,14 @@ func (rc *dryRunCLI) Run(ctx context.Context) error { if err != nil { return fmt.Errorf("failed to get config from server: %w", err) } - cfg, err := config.FromProto(state.GetConfig()) + cfg, err := config.FromProto(state.Config) if err != nil { return fmt.Errorf("failed to parse config: %w", err) } exists := false - for _, v := range state.GetExploits() { - if v.GetExploitId() == rc.exploitID { + for _, v := range state.Exploits { + if v.ExploitId == rc.exploitID { exists = true break } @@ -68,15 +68,15 @@ func (rc *dryRunCLI) Run(ctx context.Context) error { } storage := exploit.NewStorage(exploit.NewCache(), rc.baseCLI.c.ExploitDir, c) - storage.UpdateExploits(ctx, state.GetExploits()) + storage.UpdateExploits(ctx, state.Exploits) ex, ok := storage.Exploit(rc.exploitID) if !ok { return fmt.Errorf("failed to find exploit '%s' in storage", rc.exploitID) } allTeams := make(map[string]string) - for _, tbuck := range state.GetClientTeamMap() { - for k, v := range tbuck.GetTeams() { + for _, tbuck := range state.ClientTeamMap { + for k, v := range tbuck.Teams { allTeams[k] = v } } diff --git a/cmd/client/cli/info.go b/cmd/client/cli/info.go index fe85297..a85cae3 100644 --- a/cmd/client/cli/info.go +++ b/cmd/client/cli/info.go @@ -29,19 +29,19 @@ func (ic *infoCLI) Run(ctx context.Context) error { if err != nil { return fmt.Errorf("making ping config request: %w", err) } - cfg, err := config.FromProto(state.GetConfig()) + cfg, err := config.FromProto(state.Config) if err != nil { return fmt.Errorf("unmarshalling config: %w", err) } fmt.Printf("config: %+v\n", cfg) fmt.Println("IPs buckets: ") - for k, v := range state.GetClientTeamMap() { + for k, v := range state.ClientTeamMap { fmt.Print(k, ": [") - fmt.Printf("%+v", v.GetTeams()) + fmt.Printf("%+v", v.Teams) fmt.Println("]") } fmt.Println("Exploits: ") - for _, e := range state.GetExploits() { + for _, e := range state.Exploits { fmt.Printf("%+v\n", e) } return nil diff --git a/cmd/client/cli/set_disabled.go b/cmd/client/cli/set_disabled.go index e9254cd..c53a63b 100644 --- a/cmd/client/cli/set_disabled.go +++ b/cmd/client/cli/set_disabled.go @@ -36,8 +36,8 @@ func (sc *setDisabledCli) Run(ctx context.Context) error { } var spl *neopb.ExploitState - for _, v := range state.GetExploits() { - if v.GetExploitId() == sc.exploitID { + for _, v := range state.Exploits { + if v.ExploitId == sc.exploitID { spl = v break } @@ -46,7 +46,7 @@ func (sc *setDisabledCli) Run(ctx context.Context) error { if spl == nil { return fmt.Errorf("exploit %s does not exist", sc.exploitID) } - if err := c.SetExploitDisabled(ctx, spl.GetExploitId(), sc.disabled); err != nil { + if err := c.SetExploitDisabled(ctx, spl.ExploitId, sc.disabled); err != nil { return fmt.Errorf("set disabled failed: %w", err) } diff --git a/cmd/client/cli/single.go b/cmd/client/cli/single.go index 22dbb8b..40f165d 100644 --- a/cmd/client/cli/single.go +++ b/cmd/client/cli/single.go @@ -34,8 +34,8 @@ func (sc *singleRunCLI) Run(ctx context.Context) error { return fmt.Errorf("failed to get config from server: %w", err) } exists := false - for _, v := range state.GetExploits() { - if v.GetExploitId() == sc.exploitID { + for _, v := range state.Exploits { + if v.ExploitId == sc.exploitID { exists = true break } diff --git a/examples/simple.py b/examples/simple.py new file mode 100755 index 0000000..489cfb9 --- /dev/null +++ b/examples/simple.py @@ -0,0 +1,9 @@ +#!/usr/bin/env python + +import base64 +import os +import time + +for i in range(5): + print(base64.b64encode(os.urandom(23), altchars=b'AB').decode().upper()) + time.sleep(1) diff --git a/examples/spammy.py b/examples/spammy.py new file mode 100755 index 0000000..ce44990 --- /dev/null +++ b/examples/spammy.py @@ -0,0 +1,8 @@ +#!/usr/bin/env python + +import base64 +import os + +for i in range(1000): + print('A' * 2 * 1024 * 1024) + print(base64.b64encode(os.urandom(23), altchars=b'AB').decode().upper()) diff --git a/examples/timeout.py b/examples/timeout.py new file mode 100755 index 0000000..8bf3716 --- /dev/null +++ b/examples/timeout.py @@ -0,0 +1,9 @@ +#!/usr/bin/env python + +import base64 +import os +import time + +while True: + print(base64.b64encode(os.urandom(23), altchars=b'AB').decode().upper()) + time.sleep(1) diff --git a/examples/unkillable.py b/examples/unkillable.py new file mode 100755 index 0000000..2236c8a --- /dev/null +++ b/examples/unkillable.py @@ -0,0 +1,12 @@ +#!/usr/bin/env python + +import base64 +import os +import time + +while True: + try: + print(base64.b64encode(os.urandom(23), altchars=b'AB').decode().upper()) + time.sleep(1) + except BaseException: # noqa + pass diff --git a/internal/client/client.go b/internal/client/client.go index 90e5a25..f59b6c7 100644 --- a/internal/client/client.go +++ b/internal/client/client.go @@ -7,6 +7,7 @@ import ( "io" "github.com/sirupsen/logrus" + "google.golang.org/protobuf/types/known/emptypb" "neo/pkg/filestream" @@ -38,7 +39,7 @@ func (nc *Client) Ping(ctx context.Context, t neopb.PingRequest_PingType) (*neop if err != nil { return nil, fmt.Errorf("making ping request: %w", err) } - return resp.GetState(), nil + return resp.State, nil } func (nc *Client) Exploit(ctx context.Context, id string) (*neopb.ExploitResponse, error) { @@ -58,7 +59,7 @@ func (nc *Client) UpdateExploit(ctx context.Context, state *neopb.ExploitState) if err != nil { return nil, fmt.Errorf("aking update exploit request: %w", err) } - return resp.GetState(), nil + return resp.State, nil } func (nc *Client) DownloadFile(ctx context.Context, info *neopb.FileInfo, out io.Writer) error { @@ -112,7 +113,7 @@ func (nc *Client) SetExploitDisabled(ctx context.Context, id string, disabled bo return fmt.Errorf("fetching current exploit config: %w", err) } - req := &neopb.UpdateExploitRequest{State: resp.GetState()} + req := &neopb.UpdateExploitRequest{State: resp.State} req.State.Disabled = disabled if _, err := nc.c.UpdateExploit(ctx, req); err != nil { @@ -122,7 +123,7 @@ func (nc *Client) SetExploitDisabled(ctx context.Context, id string, disabled bo } func (nc *Client) ListenBroadcasts(ctx context.Context) (<-chan *neopb.Command, error) { - stream, err := nc.c.BroadcastRequests(ctx, &neopb.Empty{}) + stream, err := nc.c.BroadcastRequests(ctx, &emptypb.Empty{}) if err != nil { return nil, fmt.Errorf("creating broadcast requests stream: %w", err) } @@ -148,7 +149,7 @@ func (nc *Client) ListenBroadcasts(ctx context.Context) (<-chan *neopb.Command, } func (nc *Client) ListenSingleRuns(ctx context.Context) (<-chan *neopb.SingleRunRequest, error) { - stream, err := nc.c.SingleRunRequests(ctx, &neopb.Empty{}) + stream, err := nc.c.SingleRunRequests(ctx, &emptypb.Empty{}) if err != nil { return nil, fmt.Errorf("creating single run requests stream: %w", err) } diff --git a/internal/config/config.go b/internal/config/config.go index 94cb836..0996db6 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -6,6 +6,8 @@ import ( "time" neopb "neo/lib/genproto/neo" + + "google.golang.org/protobuf/types/known/durationpb" ) type Config struct { @@ -22,8 +24,8 @@ func ToProto(c *Config) *neopb.Config { FarmUrl: c.FarmURL, FarmPassword: c.FarmPassword, FlagRegexp: c.FlagRegexp.String(), - PingEvery: c.PingEvery.String(), - SubmitEvery: c.SubmitEvery.String(), + PingEvery: durationpb.New(c.PingEvery), + SubmitEvery: durationpb.New(c.SubmitEvery), Environ: c.Environ, } } @@ -33,17 +35,13 @@ func FromProto(config *neopb.Config) (*Config, error) { cfg Config err error ) - if cfg.FlagRegexp, err = regexp.Compile(config.GetFlagRegexp()); err != nil { + if cfg.FlagRegexp, err = regexp.Compile(config.FlagRegexp); err != nil { return nil, fmt.Errorf("compiling regex: %w", err) } - if cfg.PingEvery, err = time.ParseDuration(config.GetPingEvery()); err != nil { - return nil, fmt.Errorf("parsing ping interval: %w", err) - } - if cfg.SubmitEvery, err = time.ParseDuration(config.GetSubmitEvery()); err != nil { - return nil, fmt.Errorf("parsing submit interval: %w", err) - } - cfg.FarmURL = config.GetFarmUrl() - cfg.FarmPassword = config.GetFarmPassword() - cfg.Environ = config.GetEnviron() + cfg.FarmURL = config.FarmUrl + cfg.FarmPassword = config.FarmPassword + cfg.PingEvery = config.PingEvery.AsDuration() + cfg.SubmitEvery = config.SubmitEvery.AsDuration() + cfg.Environ = config.Environ return &cfg, nil } diff --git a/internal/exploit/cache.go b/internal/exploit/cache.go index ae558d3..3b4bfe4 100644 --- a/internal/exploit/cache.go +++ b/internal/exploit/cache.go @@ -42,7 +42,7 @@ func (c *Cache) Diff(exs []*neopb.ExploitState) (diff []*neopb.ExploitState, res c.m.RLock() defer c.m.RUnlock() for _, ex := range exs { - state, exists := c.states[ex.GetExploitId()] + state, exists := c.states[ex.ExploitId] if !exists { if ex.Endless { restartEndless = true @@ -53,7 +53,7 @@ func (c *Cache) Diff(exs []*neopb.ExploitState) (diff []*neopb.ExploitState, res if state.Endless != ex.Endless { restartEndless = true } - if state.Version != ex.GetVersion() { + if state.Version != ex.Version { diff = append(diff, ex) } } diff --git a/internal/exploit/cache_test.go b/internal/exploit/cache_test.go index ac5d0d5..a3fc13c 100644 --- a/internal/exploit/cache_test.go +++ b/internal/exploit/cache_test.go @@ -5,6 +5,7 @@ import ( "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" + "github.com/stretchr/testify/require" "google.golang.org/protobuf/testing/protocmp" neopb "neo/lib/genproto/neo" @@ -195,9 +196,7 @@ func TestCache_Diff(t *testing.T) { if diff := cmp.Diff(tc.diff, got, protocmp.Transform(), cmpopts.EquateEmpty()); diff != "" { t.Errorf("Diff() mismatch (-want +got):\n%s", diff) } - if re != tc.re { - t.Errorf("Diff(): expected restartEndless = %t, got = %t", tc.re, re) - } + require.Equal(t, tc.re, re) } } diff --git a/internal/exploit/runner.go b/internal/exploit/runner.go index 1cef783..f1e6e71 100644 --- a/internal/exploit/runner.go +++ b/internal/exploit/runner.go @@ -62,7 +62,7 @@ func (r *Runner) Run(ctx context.Context) error { } r.printGreeting() - r.updateTeams(state.GetClientTeamMap()) + r.updateTeams(state.ClientTeamMap) wg := new(sync.WaitGroup) @@ -106,12 +106,12 @@ func (r *Runner) pingHeartbeat(ctx context.Context) (*neopb.ServerState, error) if err != nil { return nil, fmt.Errorf("sending ping request: %w", err) } - cfg, err := config.FromProto(state.GetConfig()) + cfg, err := config.FromProto(state.Config) if err != nil { return nil, fmt.Errorf("parsing config: %w", err) } r.updateConfig(cfg) - r.updateExploits(ctx, state.GetExploits()) + r.updateExploits(ctx, state.Exploits) return state, nil } @@ -144,7 +144,7 @@ func (r *Runner) eventLoop(ctx context.Context) { if err != nil { logrus.Errorf("Error sending recurrent heartbeat: %v", err) } - if r.updateTeams(state.GetClientTeamMap()) { + if r.updateTeams(state.ClientTeamMap) { r.restartSimple() r.restartEndless() } @@ -306,7 +306,7 @@ func (r *Runner) updateTeams(buckets map[string]*neopb.TeamBucket) bool { logrus.Errorf("Failed to find IPs in state for client: %s", cid) return false } - teams := ipbuck.GetTeams() + teams := ipbuck.Teams r.teamsLock.Lock() defer r.teamsLock.Unlock() diff --git a/internal/exploit/storage.go b/internal/exploit/storage.go index b23a9e0..5a80a4a 100644 --- a/internal/exploit/storage.go +++ b/internal/exploit/storage.go @@ -52,7 +52,7 @@ func (s *Storage) UpdateExploits(ctx context.Context, exs []*neopb.ExploitState) for _, ex := range diff { st, err := s.updateExploit(ctx, ex) if err != nil { - logrus.Errorf("Failed to update/download executable(%s:%d): %v", ex.GetExploitId(), ex.GetVersion(), err) + logrus.Errorf("Failed to update/download executable(%s:%d): %v", ex.ExploitId, ex.Version, err) continue } states = append(states, st) @@ -63,24 +63,11 @@ func (s *Storage) UpdateExploits(ctx context.Context, exs []*neopb.ExploitState) func (s *Storage) updateExploit(ctx context.Context, state *neopb.ExploitState) (*State, error) { // Download the current exploit state. - resp, err := s.client.Exploit(ctx, state.GetExploitId()) + resp, err := s.client.Exploit(ctx, state.ExploitId) if err != nil { return nil, fmt.Errorf("requesting exploit state: %w", err) } - state = resp.GetState() - cfg := state.GetConfig() - - runEvery, err := time.ParseDuration(cfg.GetRunEvery()) - if err != nil { - return nil, fmt.Errorf("could not parse run every: %w", err) - } - - timeout, err := time.ParseDuration(cfg.GetTimeout()) - if err != nil { - return nil, fmt.Errorf("could not parse timeout: %w", err) - } - if err := os.MkdirAll(s.exploitDir, os.ModePerm); err != nil { return nil, fmt.Errorf("creating base exploits directory: %w", err) } @@ -96,7 +83,7 @@ func (s *Storage) updateExploit(ctx context.Context, state *neopb.ExploitState) }() // Download the executable to temp file. - if err := s.client.DownloadFile(ctx, state.GetFile(), f); err != nil { + if err := s.client.DownloadFile(ctx, state.File, f); err != nil { return nil, fmt.Errorf("downloading file: %w", err) } // Seek to beginning to use file as an io.Reader. @@ -104,10 +91,10 @@ func (s *Storage) updateExploit(ctx context.Context, state *neopb.ExploitState) return nil, fmt.Errorf("seeking downloaded file: %w", err) } - oPath := filepath.Join(s.exploitDir, fmt.Sprintf("%s_%d", state.GetExploitId(), state.GetVersion())) + oPath := filepath.Join(s.exploitDir, fmt.Sprintf("%s_%d", state.ExploitId, state.Version)) var entryPath string - if entryPath, err = saveExploit(f, oPath, cfg); err != nil { + if entryPath, err = saveExploit(f, oPath, resp.State.Config); err != nil { return nil, fmt.Errorf("saving exploit: %w", err) } logrus.Infof("path = %s, entry = %s", oPath, entryPath) @@ -117,17 +104,16 @@ func (s *Storage) updateExploit(ctx context.Context, state *neopb.ExploitState) } res := &State{ - ID: state.GetExploitId(), - Version: state.GetVersion(), + ID: state.ExploitId, + Version: state.Version, Dir: "", Path: entryPath, - Disabled: state.GetDisabled(), - Endless: state.GetEndless(), - LastRun: time.Unix(0, 0), - RunEvery: runEvery, - Timeout: timeout, + Disabled: state.Disabled, + Endless: state.Endless, + RunEvery: resp.State.Config.RunEvery.AsDuration(), + Timeout: resp.State.Config.Timeout.AsDuration(), } - if cfg.GetIsArchive() { + if resp.State.Config.IsArchive { res.Dir = oPath } return res, nil diff --git a/internal/exploit/storage_test.go b/internal/exploit/storage_test.go index dcf69f5..151a100 100644 --- a/internal/exploit/storage_test.go +++ b/internal/exploit/storage_test.go @@ -4,6 +4,8 @@ import ( "fmt" "os" "testing" + + "github.com/stretchr/testify/require" ) func mockStorage() (*Storage, func() error) { @@ -23,18 +25,12 @@ func mockStorage() (*Storage, func() error) { func TestStorage_Exploit(t *testing.T) { st, cleanup := mockStorage() defer func() { - if err := cleanup(); err != nil { - t.Fatalf("Failed to cleanup mock storage: %v", err) - } + require.NoError(t, cleanup()) }() st.cache.Update([]*State{ {ID: "1", Version: 1}, }) res, ok := st.Exploit("1") - if !ok { - t.Errorf("storage.Exploit(): failed to find exploit in cache") - } - if res.Version != 1 { - t.Errorf("storage.Exploit(): invalid exploit returned") - } + require.True(t, ok) + require.EqualValues(t, 1, res.Version) } diff --git a/internal/exploit/submit_loop_test.go b/internal/exploit/submit_loop_test.go index 806463f..58fe9b1 100644 --- a/internal/exploit/submit_loop_test.go +++ b/internal/exploit/submit_loop_test.go @@ -14,6 +14,7 @@ import ( "neo/pkg/testutils" "github.com/google/go-cmp/cmp" + "github.com/stretchr/testify/assert" "go.uber.org/goleak" "github.com/stretchr/testify/require" @@ -83,15 +84,9 @@ func Test_newSubmitLoop(t *testing.T) { got := newSubmitLoop(5, &cfg, factory) - if got.maxJobs != want.maxJobs { - t.Errorf("newSubmitQueue(): invalid max jobs: want %v, got %v", want.maxJobs, got.maxJobs) - } - if got.cfg != want.cfg { - t.Errorf("newSubmitQueue(): invalid cfg: want %v, got %v", want.cfg, got.cfg) - } - if got.q != want.q { - t.Errorf("newSubmitQueue(): invalid queue: want %v, got %v", want.q, got.q) - } + assert.Equal(t, want.maxJobs, got.maxJobs) + assert.Equal(t, want.cfg, got.cfg) + assert.Equal(t, want.q, got.q) } func Test_submitLoop_Add(t *testing.T) { diff --git a/internal/exploit/utils.go b/internal/exploit/utils.go index adc613f..a21943d 100644 --- a/internal/exploit/utils.go +++ b/internal/exploit/utils.go @@ -18,11 +18,11 @@ import ( ) func saveExploit(f *os.File, outPath string, cfg *neopb.ExploitConfiguration) (string, error) { - if cfg.GetIsArchive() { + if cfg.IsArchive { if err := archive.Untar(outPath, f); err != nil { return "", fmt.Errorf("unarchiving: %w", err) } - return filepath.Join(outPath, cfg.GetEntrypoint()), nil + return filepath.Join(outPath, cfg.Entrypoint), nil } if err := f.Close(); err != nil { diff --git a/internal/exploit/utils_test.go b/internal/exploit/utils_test.go index ec688cb..a0a7738 100644 --- a/internal/exploit/utils_test.go +++ b/internal/exploit/utils_test.go @@ -3,7 +3,6 @@ package exploit import ( "context" "encoding/json" - "errors" "fmt" "io" "net/http" @@ -25,51 +24,32 @@ import ( func Test_saveExploit(t *testing.T) { f, err := os.CreateTemp("", "") - if err != nil { - t.Fatalf("os.CreateTemp() got unexpected error = %v", err) - } - defer func(name string) { - if err := os.Remove(name); err == nil { - t.Errorf("Exploit file %s was not removed: %v", name, err) - } - }(f.Name()) + require.NoError(t, err) defer func(f *os.File) { - if err := f.Close(); err == nil { - t.Errorf("Exploit file was not closed: %v", err) - } + require.Error(t, f.Close()) + require.True(t, os.IsNotExist(os.Remove(f.Name()))) }(f) testData := []byte("test") - if _, err := f.Write(testData); err != nil { - t.Fatalf("f.WriteString() got unexpected error = %v", err) - } + _, err = f.Write(testData) + require.NoError(t, err) outfile, err := os.CreateTemp("", "") - if err != nil { - t.Fatalf("os.CreateTemp() got unexpected error = %v", err) - } - if err := outfile.Close(); err != nil { - t.Fatalf("Close() got unexpected error = %v", err) - } - defer func(name string) { - if err := os.Remove(name); err != nil { - t.Errorf("Error removing %s: %v", name, err) - } - }(outfile.Name()) + require.NoError(t, err) + require.NoError(t, outfile.Close()) + defer func() { + require.NoError(t, os.Remove(outfile.Name())) + }() cfg := &neopb.ExploitConfiguration{ Entrypoint: "kek", IsArchive: false, } entry, err := saveExploit(f, outfile.Name(), cfg) - if err != nil { - t.Fatalf("Runner.saveExploit() got unexpected error = %v", err) - } - if entry != outfile.Name() { - t.Fatalf("Runner.saveExploit() invalid entry returned = %v ", entry) - } + require.NoError(t, err) + require.Equal(t, outfile.Name(), entry) + data, err := os.ReadFile(outfile.Name()) - if err != nil { - t.Fatalf("os.ReadFile() got unexpected error = %v", err) - } + require.NoError(t, err) + if diff := cmp.Diff(data, testData, cmpopts.EquateEmpty()); diff != "" { t.Errorf("Runner.saveExploit() returned data mismatch (-want +got):\n%s", diff) } @@ -77,14 +57,10 @@ func Test_saveExploit(t *testing.T) { func Test_saveExploitArchive(t *testing.T) { dir, err := os.MkdirTemp("", "TEMP_TAR") - if err != nil { - t.Fatalf("os.MkdirTemp() got unexpected error = %v", err) - } + require.NoError(t, err) remove := func(name string) { - if err := os.RemoveAll(name); err != nil { - t.Errorf("File/Directory %s wasn't removed: %v", name, err) - } + require.NoError(t, os.RemoveAll(name)) } defer remove(dir) @@ -105,34 +81,23 @@ func Test_saveExploitArchive(t *testing.T) { exploitFile := "exp.py" exploitData := []byte("print(1);") - if err := writeFile(path.Join(dir, exploitFile), exploitData); err != nil { - t.Fatalf("Failed to create exploit file: %v", err) - } + require.NoError(t, writeFile(path.Join(dir, exploitFile), exploitData)) secondFile := "t.txt" secondData := []byte("123") - if err := writeFile(path.Join(dir, secondFile), secondData); err != nil { - t.Fatalf("Failed to create additional file: %v", err) - } + require.NoError(t, writeFile(path.Join(dir, secondFile), secondData)) tarFile, err := os.CreateTemp("", "ARCHIVE") - if err != nil { - t.Fatalf("failed to create tmpfile: %v", err) - } + require.NoError(t, err) defer remove(tarFile.Name()) - if err := archive.Tar(dir, tarFile); err != nil { - t.Fatalf("failed to create TarGz archive: %v", err) - } + require.NoError(t, archive.Tar(dir, tarFile)) - if _, err := tarFile.Seek(0, io.SeekStart); err != nil { - t.Fatalf("failed to seek tar file: %v", err) - } + _, err = tarFile.Seek(0, io.SeekStart) + require.NoError(t, err) outDir, err := os.MkdirTemp("", "TEMP_UNTAR") - if err != nil { - t.Fatalf("os.MkdirTemp() got unexpected error = %v", err) - } + require.NoError(t, err) defer remove(outDir) @@ -142,34 +107,23 @@ func Test_saveExploitArchive(t *testing.T) { } entry, err := saveExploit(tarFile, outDir, cfg) - if err != nil { - t.Fatalf("Runner.saveExploit() got unexpected error = %v", err) - } + require.NoError(t, err) entryDir, entryPath := path.Split(entry) entryDir = path.Clean(entryDir) - if entryDir != outDir { - t.Fatalf("Runner.saveExploit() invalid entry (%v) dir returned = %v, want = %v ", entry, entryDir, outDir) - } - - if entryPath != cfg.Entrypoint { - t.Fatalf("Runner.saveExploit() invalid entry path returned = %v, want = %v ", entryDir, cfg.Entrypoint) - } + require.Equal(t, outDir, entryDir) + require.Equal(t, cfg.Entrypoint, entryPath) entryContent, err := os.ReadFile(entry) - if err != nil { - t.Fatalf("failed to read entrypoint untared file: %v", err) - } + require.NoError(t, err) if diff := cmp.Diff(exploitData, entryContent, cmpopts.EquateEmpty()); diff != "" { t.Errorf("Runner.saveExploit() returned entrypoint data mismatch (-want +got):\n%s", diff) } additionalContent, err := os.ReadFile(path.Join(entryDir, secondFile)) - if err != nil { - t.Fatalf("failed to read additional content untared file: %v", err) - } + require.NoError(t, err) if diff := cmp.Diff(secondData, additionalContent, cmpopts.EquateEmpty()); diff != "" { t.Errorf("Runner.saveExploit() returned additional data mismatch (-want +got):\n%s", diff) @@ -178,32 +132,20 @@ func Test_saveExploitArchive(t *testing.T) { func Test_prepareEntry(t *testing.T) { f, err := os.CreateTemp("", "exec") - if err != nil { - t.Fatalf("os.CreateTemp() got unexpected error = %v", err) - } - defer func(name string) { - if err := os.Remove(name); err != nil { - t.Errorf("Error removing file %s: %v", name, err) - } - }(f.Name()) - if err := f.Close(); err != nil { - t.Fatalf("Close() got unexpected error = %v", err) - } + require.NoError(t, err) + defer func() { + require.NoError(t, os.Remove(f.Name())) + }() + require.NoError(t, f.Close()) + entry, err := prepareEntry(f.Name()) - if err != nil { - t.Fatalf("Runner.prepareEntry() got unexpected error = %v", err) - } - if !strings.Contains(entry, f.Name()) { - t.Fatalf("Runner.prepareEntry() should return abs path, got = %v", entry) - } + require.NoError(t, err) + require.True(t, strings.Contains(entry, f.Name())) + fi, err := os.Stat(entry) - if err != nil { - t.Fatalf("os.Stat() got unexpected error = %v", err) - } + require.NoError(t, err) // Check that file is executable. - if fi.Mode()&0111 == 0 { - t.Errorf("Runner.prepareEntry() should make file executable, got = %v", fi.Mode()) - } + require.NotZero(t, fi.Mode()&0111) } func Test_submitResults(t *testing.T) { @@ -277,9 +219,7 @@ func Test_submitResults(t *testing.T) { }) defer s.Close() - if err := submitResults(tt.ctx, s.URL, "1234", tt.sendResults); !errors.Is(err, tt.wantErr) { - t.Errorf("submitResults() error = %v, wantErr %v", err, tt.wantErr) - } + require.ErrorIs(t, submitResults(tt.ctx, s.URL, "1234", tt.sendResults), tt.wantErr) }) } } @@ -338,15 +278,11 @@ func Test_retryLoop(t *testing.T) { } ctx, cancel := context.WithCancel(context.Background()) defer cancel() - if err := retryLoop(ctx, "test", tt.retries, time.Millisecond, f); !errors.Is(err, tt.wantErr) { - t.Errorf("retryLoop() error = %v, wantErr %v", err, tt.wantErr) - } + require.ErrorIs(t, retryLoop(ctx, "test", tt.retries, time.Millisecond, f), tt.wantErr) if tt.retries != -1 && tt.doRetries > tt.retries { - if cnt != tt.retries { - t.Errorf("retryLoop() invalid number of retries: got %d, want %d", cnt, tt.retries) - } - } else if cnt != tt.doRetries { - t.Errorf("retryLoop() invalid number of retries: got %d, want %d", cnt, tt.doRetries) + require.Equal(t, tt.retries, cnt) + } else { + require.Equal(t, tt.doRetries, cnt) } }) } diff --git a/internal/queue/endless_test.go b/internal/queue/endless_test.go index 35843f6..8b4fa1e 100644 --- a/internal/queue/endless_test.go +++ b/internal/queue/endless_test.go @@ -2,11 +2,13 @@ package queue import ( "context" - "errors" "testing" "time" "neo/pkg/testutils" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func Test_endlessQueue_Add(t *testing.T) { @@ -27,16 +29,11 @@ func Test_endlessQueue_Add(t *testing.T) { } { tc.t.logger = testutils.DummyTaskLogger("1", "127.0.0.1") err := tc.q.Add(*tc.t) - if !errors.Is(err, tc.wantErr) { - t.Errorf("endlessQueue.Add(): got error = %v, want = %v", err, tc.wantErr) - continue - } + require.ErrorIs(t, err, tc.wantErr) if err != nil { continue } - if tk := <-tc.q.c; tk.executable != tc.t.executable { - t.Errorf("endlessQueue.Add(): got unexpected data = %v, want = %v", tk, tc.t) - } + require.Equal(t, tc.t.executable, (<-tc.q.c).executable) } } @@ -51,9 +48,7 @@ func Test_endlessQueue_Start(t *testing.T) { timeout: time.Second * 2, logger: testutils.DummyTaskLogger("echo", "ip"), } - if err := q.Add(task); err != nil { - t.Errorf("endlessQueue.Add(): got unexpected error = %v", err) - } + require.NoError(t, q.Add(task)) var out *Output ctx, cancel := context.WithCancel(context.Background()) @@ -62,13 +57,7 @@ func Test_endlessQueue_Start(t *testing.T) { out = <-q.Results() cancel() - if out.Name != task.name { - t.Errorf("endlessQueue.Start(): got unexpected result name: got = %v, want = %v", out.Name, task.name) - } - if out.Team != task.teamID { - t.Errorf("endlessQueue.Start(): got unexpected result team: got = %v, want = %v", out.Team, task.teamID) - } - if string(out.Out) != task.teamIP { - t.Errorf("endlessQueue.Start(): got unexpected result: got = %v, want = %v", out.Out, task.teamIP) - } + assert.Equal(t, task.name, out.Name) + assert.Equal(t, task.teamID, out.Team) + assert.Equal(t, task.teamIP, string(out.Out)) } diff --git a/internal/queue/simple_test.go b/internal/queue/simple_test.go index 948a97b..3950f68 100644 --- a/internal/queue/simple_test.go +++ b/internal/queue/simple_test.go @@ -2,9 +2,7 @@ package queue import ( "context" - "errors" "os/exec" - "reflect" "testing" "time" @@ -12,6 +10,8 @@ import ( "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestSimpleQueue_Add(t *testing.T) { @@ -32,16 +32,11 @@ func TestSimpleQueue_Add(t *testing.T) { } { tc.t.logger = testutils.DummyTaskLogger(tc.t.name, tc.t.teamIP) err := tc.q.Add(*tc.t) - if !errors.Is(err, tc.wantErr) { - t.Errorf("simpleQueue.Add(): got error = %v, want = %v", err, tc.wantErr) - continue - } + require.ErrorIs(t, err, tc.wantErr) if err != nil { continue } - if tk := <-tc.q.c; tk.executable != tc.t.executable { - t.Errorf("simpleQueue.Add(): got unexpected data = %v, want = %v", tk, tc.t) - } + require.Equal(t, tc.t.executable, (<-tc.q.c).executable) } } @@ -68,7 +63,7 @@ func TestSimpleQueue_runExploit(t *testing.T) { { q: NewSimpleQueue(1).(*simpleQueue), t: Task{name: "bad executable", executable: "notfoundcli", teamID: "id", teamIP: "ip", timeout: time.Second * 5}, - wantErr: &exec.Error{}, + wantErr: exec.ErrNotFound, wantData: nil, ctx: context.Background(), }, @@ -103,15 +98,13 @@ func TestSimpleQueue_runExploit(t *testing.T) { } { tc.t.logger = testutils.DummyTaskLogger(tc.t.name, tc.t.teamIP) data, err := tc.q.runExploit(tc.ctx, tc.t) - cmpErr := func(e1, e2 error) bool { - if errors.Is(e1, e2) { - return true - } - return reflect.TypeOf(e1) == reflect.TypeOf(e2) - } - if !cmpErr(err, tc.wantErr) { - t.Errorf("simpleQueue.runExploit() [%s]: got unexpected err = %v, want = %v", tc.t.name, err, tc.wantErr) - } + // cmpErr := func(e1, e2 error) bool { + // if errors.Is(e1, e2) { + // return true + // } + // return reflect.TypeOf(e1) == reflect.TypeOf(e2) + // } + require.ErrorIs(t, err, tc.wantErr) if diff := cmp.Diff(data, tc.wantData, cmpopts.EquateEmpty()); diff != "" { t.Errorf("simpleQueue.runExploit() returned data mismatch (-want +got):\n%s", diff) @@ -130,9 +123,7 @@ func TestSimpleQueue_Start(t *testing.T) { timeout: time.Second * 2, logger: testutils.DummyTaskLogger("echo", "ip"), } - if err := q.Add(task); err != nil { - t.Errorf("simpleQueue.Add(): got unexpected error = %v", err) - } + require.NoError(t, q.Add(task)) var out *Output ctx, cancel := context.WithCancel(context.Background()) @@ -141,13 +132,7 @@ func TestSimpleQueue_Start(t *testing.T) { out = <-q.Results() cancel() - if out.Name != task.name { - t.Errorf("simpleQueue.Start(): got unexpected result name: got = %v, want = %v", out.Name, task.name) - } - if out.Team != task.teamID { - t.Errorf("simpleQueue.Start(): got unexpected result team: got = %v, want = %v", out.Team, task.teamID) - } - if string(out.Out) != task.teamIP+"\n" { - t.Errorf("simpleQueue.Start(): got unexpected result: got = %v, want = %v", out.Out, task.teamIP+"\n") - } + assert.Equal(t, task.name, out.Name) + assert.Equal(t, task.teamID, out.Team) + assert.Equal(t, task.teamIP+"\n", string(out.Out)) } diff --git a/internal/server/server.go b/internal/server/server.go index a31077d..f61ef20 100644 --- a/internal/server/server.go +++ b/internal/server/server.go @@ -2,7 +2,6 @@ package server import ( "context" - "errors" "fmt" "io" "os" @@ -22,15 +21,11 @@ import ( "github.com/sirupsen/logrus" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" + "google.golang.org/protobuf/types/known/emptypb" neopb "neo/lib/genproto/neo" ) -const ( - broadcastChannel = "broadcast" - singleRunChannel = "single_run" -) - const ( logLinesBatchSize = 100 ) @@ -40,14 +35,6 @@ const ( maxMsgSize = 4 * 1024 * 1024 ) -var ( - ErrInvalidMessageType = errors.New("invalid message type") -) - -var ( - noResponse = &neopb.Empty{} -) - type fileInterface interface { io.ReadWriteCloser Name() string @@ -91,12 +78,13 @@ func New(cfg *Config, storage *CachedStorage, logStore *LogStorage) (*ExploitMan return nil, fmt.Errorf("creating filesystem: %w", err) } ems := &ExploitManagerServer{ - storage: storage, - fs: fs, - buckets: hostbucket.New(cfg.FarmConfig.Teams), - visits: newVisitsMap(), - ps: pubsub.NewPubSub(), - logStorage: logStore, + storage: storage, + fs: fs, + buckets: hostbucket.New(cfg.FarmConfig.Teams), + visits: newVisitsMap(), + singleRunPubSub: pubsub.NewPubSub[*neopb.SingleRunRequest](), + broadcastPubSub: pubsub.NewPubSub[*neopb.Command](), + logStorage: logStore, } ems.UpdateConfig(cfg) return ems, nil @@ -112,18 +100,19 @@ type ExploitManagerServer struct { visits *visitsMap fs filesystem - ps pubsub.PubSub + singleRunPubSub *pubsub.PubSub[*neopb.SingleRunRequest] + broadcastPubSub *pubsub.PubSub[*neopb.Command] } -func (em *ExploitManagerServer) UpdateConfig(cfg *Config) { +func (s *ExploitManagerServer) UpdateConfig(cfg *Config) { env := make([]string, 0, len(cfg.Environ)) for k, v := range cfg.Environ { env = append(env, fmt.Sprintf("%s=%s", k, v)) } - em.cfgMutex.Lock() - defer em.cfgMutex.Unlock() - em.config = &config.Config{ + s.cfgMutex.Lock() + defer s.cfgMutex.Unlock() + s.config = &config.Config{ PingEvery: cfg.PingEvery, SubmitEvery: cfg.SubmitEvery, FarmURL: cfg.FarmConfig.URL, @@ -131,33 +120,33 @@ func (em *ExploitManagerServer) UpdateConfig(cfg *Config) { FlagRegexp: regexp.MustCompile(cfg.FarmConfig.FlagRegexp), Environ: env, } - em.buckets.UpdateTeams(cfg.FarmConfig.Teams) + s.buckets.UpdateTeams(cfg.FarmConfig.Teams) } -func (em *ExploitManagerServer) Ping(_ context.Context, r *neopb.PingRequest) (*neopb.PingResponse, error) { - logrus.Infof("Got %s from: %s", neopb.PingRequest_PingType_name[int32(r.GetType())], r.GetClientId()) +func (s *ExploitManagerServer) Ping(_ context.Context, r *neopb.PingRequest) (*neopb.PingResponse, error) { + logrus.Infof("Got %s from: %s", neopb.PingRequest_PingType_name[int32(r.Type)], r.ClientId) if r.Type == neopb.PingRequest_HEARTBEAT { - em.cfgMutex.RLock() - defer em.cfgMutex.RUnlock() - em.visits.Add(r.GetClientId()) - em.buckets.AddNode(r.GetClientId(), int(r.GetWeight())) + s.cfgMutex.RLock() + defer s.cfgMutex.RUnlock() + s.visits.Add(r.ClientId) + s.buckets.AddNode(r.ClientId, int(r.Weight)) } else if r.Type == neopb.PingRequest_LEAVE { - em.visits.MarkInvalid(r.GetClientId()) + s.visits.MarkInvalid(r.ClientId) } return &neopb.PingResponse{ State: &neopb.ServerState{ - ClientTeamMap: em.buckets.Buckets(), - Exploits: em.storage.States(), - Config: config.ToProto(em.config), + ClientTeamMap: s.buckets.Buckets(), + Exploits: s.storage.States(), + Config: config.ToProto(s.config), }, }, nil } -func (em *ExploitManagerServer) UploadFile(stream neopb.ExploitManager_UploadFileServer) error { - info := &neopb.FileInfo{Uuid: uuid.New().String()} - of, err := em.fs.Create(info.GetUuid()) +func (s *ExploitManagerServer) UploadFile(stream neopb.ExploitManager_UploadFileServer) error { + info := &neopb.FileInfo{Uuid: uuid.NewString()} + of, err := s.fs.Create(info.Uuid) if err != nil { return logErrorf(codes.Internal, "Failed to create file: %v", err) } @@ -181,10 +170,10 @@ func (em *ExploitManagerServer) UploadFile(stream neopb.ExploitManager_UploadFil return nil } -func (em *ExploitManagerServer) DownloadFile(fi *neopb.FileInfo, stream neopb.ExploitManager_DownloadFileServer) error { - f, err := em.fs.Open(fi.GetUuid()) +func (s *ExploitManagerServer) DownloadFile(fi *neopb.FileInfo, stream neopb.ExploitManager_DownloadFileServer) error { + f, err := s.fs.Open(fi.Uuid) if err != nil { - return logErrorf(codes.NotFound, "Failed to find file by uuid(%s): %v", fi.GetUuid(), err) + return logErrorf(codes.NotFound, "Failed to find file by uuid(%s): %v", fi.Uuid, err) } defer func() { if err := f.Close(); err != nil { @@ -192,13 +181,13 @@ func (em *ExploitManagerServer) DownloadFile(fi *neopb.FileInfo, stream neopb.Ex } }() if err := filestream.Load(f, stream); err != nil { - return logErrorf(codes.NotFound, "Failed to find file by uuid(%s): %v", fi.GetUuid(), err) + return logErrorf(codes.NotFound, "Failed to find file by uuid(%s): %v", fi.Uuid, err) } return nil } -func (em *ExploitManagerServer) Exploit(_ context.Context, r *neopb.ExploitRequest) (*neopb.ExploitResponse, error) { - state, ok := em.storage.GetState(r.GetExploitId()) +func (s *ExploitManagerServer) Exploit(_ context.Context, r *neopb.ExploitRequest) (*neopb.ExploitResponse, error) { + state, ok := s.storage.GetState(r.ExploitId) if !ok { return nil, logErrorf(codes.NotFound, "Failed to find an exploit state = %v", state.ExploitId) } @@ -207,79 +196,59 @@ func (em *ExploitManagerServer) Exploit(_ context.Context, r *neopb.ExploitReque }, nil } -func (em *ExploitManagerServer) UpdateExploit(_ context.Context, r *neopb.UpdateExploitRequest) (*neopb.UpdateExploitResponse, error) { - newState, err := em.storage.UpdateExploitVersion(r.GetState()) +func (s *ExploitManagerServer) UpdateExploit(_ context.Context, r *neopb.UpdateExploitRequest) (*neopb.UpdateExploitResponse, error) { + newState, err := s.storage.UpdateExploitVersion(r.State) if err != nil { return nil, logErrorf(codes.Internal, "Failed to update exploit version: %v", err) } return &neopb.UpdateExploitResponse{State: newState}, nil } -func (em *ExploitManagerServer) BroadcastCommand(_ context.Context, r *neopb.Command) (*neopb.Empty, error) { +func (s *ExploitManagerServer) BroadcastCommand(_ context.Context, r *neopb.Command) (*emptypb.Empty, error) { logrus.Infof("Received broadcast request to run %v", r) - em.ps.Publish(broadcastChannel, r) - return noResponse, nil + s.broadcastPubSub.Publish(r) + return &emptypb.Empty{}, nil } -func (em *ExploitManagerServer) BroadcastRequests(_ *neopb.Empty, stream neopb.ExploitManager_BroadcastRequestsServer) error { - handler := func(msg interface{}) error { - cmd, ok := msg.(*neopb.Command) - if !ok { - return ErrInvalidMessageType - } - if err := stream.Send(cmd); err != nil { - return fmt.Errorf("sending command: %w", err) - } - return nil - } - sub := em.ps.Subscribe(broadcastChannel, handler) - defer em.ps.Unsubscribe(sub) +func (s *ExploitManagerServer) BroadcastRequests(_ *emptypb.Empty, stream neopb.ExploitManager_BroadcastRequestsServer) error { + sub := s.broadcastPubSub.Subscribe(stream.Send) + defer s.broadcastPubSub.Unsubscribe(sub) sub.Run(stream.Context()) return nil } -func (em *ExploitManagerServer) SingleRun(_ context.Context, r *neopb.SingleRunRequest) (*neopb.Empty, error) { +func (s *ExploitManagerServer) SingleRun(_ context.Context, r *neopb.SingleRunRequest) (*emptypb.Empty, error) { logrus.Infof("Received single run request %v", r) - em.ps.Publish(singleRunChannel, r) - return noResponse, nil + s.singleRunPubSub.Publish(r) + return &emptypb.Empty{}, nil } -func (em *ExploitManagerServer) SingleRunRequests(_ *neopb.Empty, stream neopb.ExploitManager_SingleRunRequestsServer) error { - handler := func(msg interface{}) error { - req, ok := msg.(*neopb.SingleRunRequest) - if !ok { - return ErrInvalidMessageType - } - if err := stream.Send(req); err != nil { - return fmt.Errorf("sending single run request: %w", err) - } - return nil - } - sub := em.ps.Subscribe(singleRunChannel, handler) - defer em.ps.Unsubscribe(sub) +func (s *ExploitManagerServer) SingleRunRequests(_ *emptypb.Empty, stream neopb.ExploitManager_SingleRunRequestsServer) error { + sub := s.singleRunPubSub.Subscribe(stream.Send) + defer s.singleRunPubSub.Unsubscribe(sub) sub.Run(stream.Context()) return nil } -func (em *ExploitManagerServer) AddLogLines(ctx context.Context, lines *neopb.AddLogLinesRequest) (*neopb.Empty, error) { +func (s *ExploitManagerServer) AddLogLines(ctx context.Context, lines *neopb.AddLogLinesRequest) (*emptypb.Empty, error) { decoded := make([]LogLine, 0, len(lines.Lines)) for _, line := range lines.Lines { decoded = append(decoded, *NewLogLineFromProto(line)) } - if err := em.logStorage.Add(ctx, decoded); err != nil { + if err := s.logStorage.Add(ctx, decoded); err != nil { return nil, logErrorf(codes.Internal, "adding log lines: %v", err) } - return &neopb.Empty{}, nil + return &emptypb.Empty{}, nil } -func (em *ExploitManagerServer) SearchLogLines(req *neopb.SearchLogLinesRequest, stream neopb.ExploitManager_SearchLogLinesServer) error { +func (s *ExploitManagerServer) SearchLogLines(req *neopb.SearchLogLinesRequest, stream neopb.ExploitManager_SearchLogLinesServer) error { opts := GetOptions{ Exploit: req.Exploit, Version: req.Version, } - lines, err := em.logStorage.Get(stream.Context(), opts) + lines, err := s.logStorage.Get(stream.Context(), opts) if err != nil { return logErrorf(codes.Internal, "searching log lines: %v", err) } @@ -318,21 +287,21 @@ func (em *ExploitManagerServer) SearchLogLines(req *neopb.SearchLogLinesRequest, return nil } -func (em *ExploitManagerServer) checkClients() { - deadClients := em.visits.Invalidate(time.Now(), em.config.PingEvery) +func (s *ExploitManagerServer) checkClients() { + deadClients := s.visits.Invalidate(time.Now(), s.config.PingEvery) logrus.Infof("Heartbeat: got dead clients: %v", deadClients) for _, c := range deadClients { - em.buckets.DeleteNode(c) + s.buckets.DeleteNode(c) } } -func (em *ExploitManagerServer) HeartBeat(ctx context.Context) { - ticker := time.NewTicker(em.config.PingEvery) +func (s *ExploitManagerServer) HeartBeat(ctx context.Context) { + ticker := time.NewTicker(s.config.PingEvery) defer ticker.Stop() for { select { case <-ticker.C: - em.checkClients() + s.checkClients() case <-ctx.Done(): return } diff --git a/internal/server/server_test.go b/internal/server/server_test.go index 546ab3b..d7535f5 100644 --- a/internal/server/server_test.go +++ b/internal/server/server_test.go @@ -9,6 +9,7 @@ import ( "neo/pkg/hostbucket" "github.com/google/go-cmp/cmp" + "github.com/stretchr/testify/require" "google.golang.org/protobuf/testing/protocmp" neopb "neo/lib/genproto/neo" @@ -51,16 +52,14 @@ func TestExploitManagerServer_UpdateExploit(t *testing.T) { }, } resp, err := es.UpdateExploit(context.Background(), r) - if err != nil { - t.Fatalf("UpdateExploit() failed with unexpected error = %v", err) - } + require.NoError(t, err) want := &neopb.ExploitState{ ExploitId: "1", Version: 1, - File: r.GetState().GetFile(), + File: r.State.File, Config: cfg, } - if diff := cmp.Diff(want, resp.GetState(), protocmp.Transform()); diff != "" { + if diff := cmp.Diff(want, resp.State, protocmp.Transform()); diff != "" { t.Errorf("UpdateExploit() mismatch (-want +got):\n%s", diff) } } @@ -80,20 +79,16 @@ func TestExploitManagerServer_Exploit(t *testing.T) { req := &neopb.UpdateExploitRequest{State: state} ctx := context.Background() _, err := es.UpdateExploit(ctx, req) - if err != nil { - t.Fatalf("UpdateExploit() failed with unexpected error = %v", err) - } + require.NoError(t, err) resp, err := es.Exploit(ctx, &neopb.ExploitRequest{ExploitId: state.ExploitId}) - if err != nil { - t.Fatalf("Exploit() failed with unexpected error = %v", err) - } + require.NoError(t, err) wantState := &neopb.ExploitState{ ExploitId: "1", Version: 1, - File: state.GetFile(), + File: state.File, Config: cfg, } - if diff := cmp.Diff(wantState, resp.GetState(), protocmp.Transform()); diff != "" { + if diff := cmp.Diff(wantState, resp.State, protocmp.Transform()); diff != "" { t.Errorf("Exploit() state mismatch (-want +got):\n%s", diff) } } @@ -115,32 +110,21 @@ func TestExploitManagerServer_Ping(t *testing.T) { } r := &neopb.UpdateExploitRequest{State: state} updateResp, err := es.UpdateExploit(ctx, r) - if err != nil { - t.Fatalf("UpdateExploit(): unexpected error = %v", err) - } + require.NoError(t, err) req := &neopb.PingRequest{ClientId: "id1", Type: neopb.PingRequest_HEARTBEAT} resp, err := es.Ping(ctx, req) - if err != nil { - t.Fatalf("Ping(): unexpected error = %v", err) - } - want := []*neopb.ExploitState{updateResp.GetState()} - if diff := cmp.Diff(want, resp.GetState().GetExploits(), protocmp.Transform()); diff != "" { + require.NoError(t, err) + want := []*neopb.ExploitState{updateResp.State} + if diff := cmp.Diff(want, resp.State.Exploits, protocmp.Transform()); diff != "" { t.Errorf("Ping() states mismatch (-want +got):\n%s", diff) } - if diff := cmp.Diff(es.buckets.Buckets(), resp.GetState().GetClientTeamMap(), protocmp.Transform()); diff != "" { + if diff := cmp.Diff(es.buckets.Buckets(), resp.State.ClientTeamMap, protocmp.Transform()); diff != "" { t.Errorf("Ping() bucket mismatch (-want +got):\n%s", diff) } - if len(es.buckets.Buckets()[req.ClientId].GetTeams()) == 0 { - t.Errorf("Ping() ip bucket with zero len") - } - gotURL := resp.GetState().GetConfig().GetFarmUrl() - if es.config.FarmURL != gotURL { - t.Errorf("Ping() config mismatch want farmURL: %s, got: %s", es.config.FarmURL, gotURL) - } - if !es.visits.visits["id1"].Before(time.Now()) { - t.Errorf("Ping() visits missmatch") - } + require.NotEmpty(t, es.buckets.Buckets()[req.ClientId].Teams) + require.Equal(t, es.config.FarmURL, resp.State.Config.FarmUrl) + require.True(t, es.visits.visits["id1"].Before(time.Now())) } func TestExploitManagerServer_BroadcastCommand(t *testing.T) { @@ -152,24 +136,18 @@ func TestExploitManagerServer_BroadcastCommand(t *testing.T) { var received *neopb.Command signal := make(chan struct{}) - handler := func(msg interface{}) error { - cmd, ok := msg.(*neopb.Command) - if !ok { - t.Errorf("Invalid message passed to handler: %v", msg) - } - received = cmd + handler := func(msg *neopb.Command) error { + received = msg close(signal) return nil } - testSub := es.ps.Subscribe(broadcastChannel, handler) - defer es.ps.Unsubscribe(testSub) + testSub := es.broadcastPubSub.Subscribe(handler) + defer es.broadcastPubSub.Unsubscribe(testSub) go testSub.Run(ctx) r := &neopb.Command{Command: "echo 123"} _, err := es.BroadcastCommand(ctx, r) - if err != nil { - t.Errorf("Error received from broadcast command: %v", err) - } + require.NoError(t, err) select { case <-signal: @@ -177,8 +155,5 @@ func TestExploitManagerServer_BroadcastCommand(t *testing.T) { case <-time.After(time.Millisecond * 100): t.Errorf("Handler was not called in time") } - - if received != r { - t.Errorf("Received incorrect command: expected = %v, actual = %v", r, received) - } + require.Equal(t, r, received) } diff --git a/internal/server/storage_test.go b/internal/server/storage_test.go index a9d9df9..893d30d 100644 --- a/internal/server/storage_test.go +++ b/internal/server/storage_test.go @@ -63,9 +63,7 @@ func TestCachedStorage_UpdateStates(t *testing.T) { db, cleanup := testDB() defer cleanup() cs, err := NewStorage(db) - if err != nil { - t.Fatalf("NewStorage() failed with unexpected error = %v", err) - } + require.NoError(t, err) state := &neopb.ExploitState{ ExploitId: "1", File: &neopb.FileInfo{Uuid: "1"}, @@ -74,12 +72,9 @@ func TestCachedStorage_UpdateStates(t *testing.T) { IsArchive: false, }, } - if _, err := cs.UpdateExploitVersion(state); err != nil { - t.Fatalf("UpdateExploitVersion(): got unexpected error = %v", err) - } - if state.Version != 1 { - t.Errorf("UpdateExploitVersion(): wrong version returned: want: 1, got: %d", state.Version) - } + _, err = cs.UpdateExploitVersion(state) + require.NoError(t, err) + require.EqualValues(t, 1, state.Version) s, _ := cs.GetState(state.ExploitId) if diff := cmp.Diff(state, s, protocmp.Transform()); diff != "" { t.Errorf("UpdateExploitVersion(): unexpected state diff: (-want +got):\n%s", diff) @@ -92,28 +87,22 @@ func TestCachedStorage_UpdateStates(t *testing.T) { IsArchive: true, }, } - if _, err := cs.UpdateExploitVersion(state); err != nil { - t.Fatalf("UpdateExploitVersion(): got unexpected error = %v", err) - } - if state.Version != 2 { - t.Errorf("UpdateExploitVersion(): wrong version returned: want: 2, got: %d", state.Version) - } + _, err = cs.UpdateExploitVersion(state) + require.NoError(t, err) + require.EqualValues(t, 2, state.Version) s, _ = cs.GetState(state.ExploitId) if diff := cmp.Diff(state, s, protocmp.Transform()); diff != "" { t.Errorf("UpdateExploitVersion(): unexpected state diff: (-want +got):\n%s", diff) } - if len(cs.States()) != 1 { - t.Errorf("States(): want: %d, got: %d", 1, len(cs.States())) - } + require.Len(t, cs.States(), 1) } func TestCachedStorage_UpdateExploitVersionDB(t *testing.T) { db, cleanup := testDB() defer cleanup() cs, err := NewStorage(db) - if err != nil { - t.Fatalf("NewStorage() failed with unexpected error = %v", err) - } + require.NoError(t, err) + state := &neopb.ExploitState{ ExploitId: "1", File: &neopb.FileInfo{Uuid: "1"}, @@ -122,15 +111,10 @@ func TestCachedStorage_UpdateExploitVersionDB(t *testing.T) { IsArchive: false, }, } - if _, err := cs.UpdateExploitVersion(state); err != nil { - t.Fatalf("UpdateExploitVersion(): got unexpected error = %v", err) - } - if err := cs.readDB(); err != nil { - t.Fatalf("readDB(): got unexpected error: %v", err) - } - if len(cs.States()) != 1 { - t.Errorf("States(): want: %d, got: %d", 1, len(cs.States())) - } + _, err = cs.UpdateExploitVersion(state) + require.NoError(t, err) + require.NoError(t, cs.readDB()) + require.Len(t, cs.States(), 1) } // TestCachedStorage_readDB test the implementation of the readDB function. @@ -146,26 +130,18 @@ func TestCachedStorage_readDB(t *testing.T) { }, } cs, err := NewStorage(db) - if err != nil { - t.Fatalf("NewStorage() failed with unexpected error = %v", err) - } - if err := cs.bdb.Update(func(tx *bolt.Tx) error { + require.NoError(t, err) + require.NoError(t, cs.bdb.Update(func(tx *bolt.Tx) error { b := tx.Bucket([]byte(stateBucketKey)) stateBytes, err := proto.Marshal(state) - if err != nil { - t.Fatalf("proto.Marshall(): failed with error: %v", err) - } + require.NoError(t, err) stateKey := []byte(fmt.Sprintf("%s:%d", state.ExploitId, state.Version)) if err := b.Put(stateKey, stateBytes); err != nil { return fmt.Errorf("setting state in db: %w", err) } return nil - }); err != nil { - t.Fatalf("db.Update() failed with error = %v", err) - } - if err := cs.readDB(); err != nil { - t.Fatalf("readDB() failed with unexpected error = %v", err) - } + })) + require.NoError(t, cs.readDB()) if diff := cmp.Diff(state, cs.stateCache["1"], protocmp.Transform()); diff != "" { t.Errorf("readDB(): unexpected diff for exploit with id = 1") } diff --git a/lib/genproto/neo/neo.pb.go b/lib/genproto/neo/neo.pb.go index dcb4969..4d2f494 100644 --- a/lib/genproto/neo/neo.pb.go +++ b/lib/genproto/neo/neo.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.26.0 -// protoc v3.17.3 +// protoc-gen-go v1.28.0 +// protoc v3.19.4 // source: neo.proto package neo @@ -9,6 +9,8 @@ package neo import ( protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" + durationpb "google.golang.org/protobuf/types/known/durationpb" + emptypb "google.golang.org/protobuf/types/known/emptypb" reflect "reflect" sync "sync" ) @@ -168,10 +170,10 @@ type ExploitConfiguration struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Entrypoint string `protobuf:"bytes,1,opt,name=entrypoint,proto3" json:"entrypoint,omitempty"` - IsArchive bool `protobuf:"varint,2,opt,name=is_archive,json=isArchive,proto3" json:"is_archive,omitempty"` - RunEvery string `protobuf:"bytes,3,opt,name=run_every,json=runEvery,proto3" json:"run_every,omitempty"` - Timeout string `protobuf:"bytes,4,opt,name=timeout,proto3" json:"timeout,omitempty"` + Entrypoint string `protobuf:"bytes,1,opt,name=entrypoint,proto3" json:"entrypoint,omitempty"` + IsArchive bool `protobuf:"varint,2,opt,name=is_archive,json=isArchive,proto3" json:"is_archive,omitempty"` + RunEvery *durationpb.Duration `protobuf:"bytes,3,opt,name=run_every,json=runEvery,proto3" json:"run_every,omitempty"` + Timeout *durationpb.Duration `protobuf:"bytes,4,opt,name=timeout,proto3" json:"timeout,omitempty"` } func (x *ExploitConfiguration) Reset() { @@ -220,18 +222,18 @@ func (x *ExploitConfiguration) GetIsArchive() bool { return false } -func (x *ExploitConfiguration) GetRunEvery() string { +func (x *ExploitConfiguration) GetRunEvery() *durationpb.Duration { if x != nil { return x.RunEvery } - return "" + return nil } -func (x *ExploitConfiguration) GetTimeout() string { +func (x *ExploitConfiguration) GetTimeout() *durationpb.Duration { if x != nil { return x.Timeout } - return "" + return nil } type ExploitState struct { @@ -326,12 +328,12 @@ type Config struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - FarmUrl string `protobuf:"bytes,1,opt,name=farm_url,json=farmUrl,proto3" json:"farm_url,omitempty"` - FarmPassword string `protobuf:"bytes,2,opt,name=farm_password,json=farmPassword,proto3" json:"farm_password,omitempty"` - FlagRegexp string `protobuf:"bytes,3,opt,name=flag_regexp,json=flagRegexp,proto3" json:"flag_regexp,omitempty"` - PingEvery string `protobuf:"bytes,4,opt,name=ping_every,json=pingEvery,proto3" json:"ping_every,omitempty"` - SubmitEvery string `protobuf:"bytes,5,opt,name=submit_every,json=submitEvery,proto3" json:"submit_every,omitempty"` - Environ []string `protobuf:"bytes,6,rep,name=environ,proto3" json:"environ,omitempty"` + FarmUrl string `protobuf:"bytes,1,opt,name=farm_url,json=farmUrl,proto3" json:"farm_url,omitempty"` + FarmPassword string `protobuf:"bytes,2,opt,name=farm_password,json=farmPassword,proto3" json:"farm_password,omitempty"` + FlagRegexp string `protobuf:"bytes,3,opt,name=flag_regexp,json=flagRegexp,proto3" json:"flag_regexp,omitempty"` + PingEvery *durationpb.Duration `protobuf:"bytes,4,opt,name=ping_every,json=pingEvery,proto3" json:"ping_every,omitempty"` + SubmitEvery *durationpb.Duration `protobuf:"bytes,5,opt,name=submit_every,json=submitEvery,proto3" json:"submit_every,omitempty"` + Environ []string `protobuf:"bytes,6,rep,name=environ,proto3" json:"environ,omitempty"` } func (x *Config) Reset() { @@ -387,18 +389,18 @@ func (x *Config) GetFlagRegexp() string { return "" } -func (x *Config) GetPingEvery() string { +func (x *Config) GetPingEvery() *durationpb.Duration { if x != nil { return x.PingEvery } - return "" + return nil } -func (x *Config) GetSubmitEvery() string { +func (x *Config) GetSubmitEvery() *durationpb.Duration { if x != nil { return x.SubmitEvery } - return "" + return nil } func (x *Config) GetEnviron() []string { @@ -990,44 +992,6 @@ func (x *Command) GetCommand() string { return "" } -type Empty struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields -} - -func (x *Empty) Reset() { - *x = Empty{} - if protoimpl.UnsafeEnabled { - mi := &file_neo_proto_msgTypes[16] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *Empty) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*Empty) ProtoMessage() {} - -func (x *Empty) ProtoReflect() protoreflect.Message { - mi := &file_neo_proto_msgTypes[16] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use Empty.ProtoReflect.Descriptor instead. -func (*Empty) Descriptor() ([]byte, []int) { - return file_neo_proto_rawDescGZIP(), []int{16} -} - type AddLogLinesRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -1039,7 +1003,7 @@ type AddLogLinesRequest struct { func (x *AddLogLinesRequest) Reset() { *x = AddLogLinesRequest{} if protoimpl.UnsafeEnabled { - mi := &file_neo_proto_msgTypes[17] + mi := &file_neo_proto_msgTypes[16] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1052,7 +1016,7 @@ func (x *AddLogLinesRequest) String() string { func (*AddLogLinesRequest) ProtoMessage() {} func (x *AddLogLinesRequest) ProtoReflect() protoreflect.Message { - mi := &file_neo_proto_msgTypes[17] + mi := &file_neo_proto_msgTypes[16] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1065,7 +1029,7 @@ func (x *AddLogLinesRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use AddLogLinesRequest.ProtoReflect.Descriptor instead. func (*AddLogLinesRequest) Descriptor() ([]byte, []int) { - return file_neo_proto_rawDescGZIP(), []int{17} + return file_neo_proto_rawDescGZIP(), []int{16} } func (x *AddLogLinesRequest) GetLines() []*LogLine { @@ -1087,7 +1051,7 @@ type SearchLogLinesRequest struct { func (x *SearchLogLinesRequest) Reset() { *x = SearchLogLinesRequest{} if protoimpl.UnsafeEnabled { - mi := &file_neo_proto_msgTypes[18] + mi := &file_neo_proto_msgTypes[17] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1100,7 +1064,7 @@ func (x *SearchLogLinesRequest) String() string { func (*SearchLogLinesRequest) ProtoMessage() {} func (x *SearchLogLinesRequest) ProtoReflect() protoreflect.Message { - mi := &file_neo_proto_msgTypes[18] + mi := &file_neo_proto_msgTypes[17] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1113,7 +1077,7 @@ func (x *SearchLogLinesRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use SearchLogLinesRequest.ProtoReflect.Descriptor instead. func (*SearchLogLinesRequest) Descriptor() ([]byte, []int) { - return file_neo_proto_rawDescGZIP(), []int{18} + return file_neo_proto_rawDescGZIP(), []int{17} } func (x *SearchLogLinesRequest) GetExploit() string { @@ -1141,7 +1105,7 @@ type SearchLogLinesResponse struct { func (x *SearchLogLinesResponse) Reset() { *x = SearchLogLinesResponse{} if protoimpl.UnsafeEnabled { - mi := &file_neo_proto_msgTypes[19] + mi := &file_neo_proto_msgTypes[18] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1154,7 +1118,7 @@ func (x *SearchLogLinesResponse) String() string { func (*SearchLogLinesResponse) ProtoMessage() {} func (x *SearchLogLinesResponse) ProtoReflect() protoreflect.Message { - mi := &file_neo_proto_msgTypes[19] + mi := &file_neo_proto_msgTypes[18] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1167,7 +1131,7 @@ func (x *SearchLogLinesResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use SearchLogLinesResponse.ProtoReflect.Descriptor instead. func (*SearchLogLinesResponse) Descriptor() ([]byte, []int) { - return file_neo_proto_rawDescGZIP(), []int{19} + return file_neo_proto_rawDescGZIP(), []int{18} } func (x *SearchLogLinesResponse) GetLines() []*LogLine { @@ -1181,113 +1145,123 @@ var File_neo_proto protoreflect.FileDescriptor var file_neo_proto_rawDesc = []byte{ 0x0a, 0x09, 0x6e, 0x65, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x03, 0x6e, 0x65, 0x6f, - 0x22, 0x78, 0x0a, 0x0a, 0x54, 0x65, 0x61, 0x6d, 0x42, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x12, 0x30, - 0x0a, 0x05, 0x74, 0x65, 0x61, 0x6d, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, - 0x6e, 0x65, 0x6f, 0x2e, 0x54, 0x65, 0x61, 0x6d, 0x42, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x54, - 0x65, 0x61, 0x6d, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x05, 0x74, 0x65, 0x61, 0x6d, 0x73, - 0x1a, 0x38, 0x0a, 0x0a, 0x54, 0x65, 0x61, 0x6d, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, - 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, - 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x1e, 0x0a, 0x08, 0x46, 0x69, - 0x6c, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x12, 0x0a, 0x04, 0x75, 0x75, 0x69, 0x64, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x75, 0x75, 0x69, 0x64, 0x22, 0x8c, 0x01, 0x0a, 0x14, 0x45, - 0x78, 0x70, 0x6c, 0x6f, 0x69, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x12, 0x1e, 0x0a, 0x0a, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x70, 0x6f, 0x69, 0x6e, - 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x70, 0x6f, - 0x69, 0x6e, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x69, 0x73, 0x5f, 0x61, 0x72, 0x63, 0x68, 0x69, 0x76, - 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x69, 0x73, 0x41, 0x72, 0x63, 0x68, 0x69, - 0x76, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x72, 0x75, 0x6e, 0x5f, 0x65, 0x76, 0x65, 0x72, 0x79, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, 0x75, 0x6e, 0x45, 0x76, 0x65, 0x72, 0x79, 0x12, - 0x18, 0x0a, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x22, 0xd3, 0x01, 0x0a, 0x0c, 0x45, 0x78, - 0x70, 0x6c, 0x6f, 0x69, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x65, 0x78, - 0x70, 0x6c, 0x6f, 0x69, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, - 0x65, 0x78, 0x70, 0x6c, 0x6f, 0x69, 0x74, 0x49, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, - 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, - 0x69, 0x6f, 0x6e, 0x12, 0x21, 0x0a, 0x04, 0x66, 0x69, 0x6c, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x0d, 0x2e, 0x6e, 0x65, 0x6f, 0x2e, 0x46, 0x69, 0x6c, 0x65, 0x49, 0x6e, 0x66, 0x6f, - 0x52, 0x04, 0x66, 0x69, 0x6c, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, - 0x65, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, - 0x65, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x73, 0x18, 0x05, 0x20, - 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x73, 0x12, 0x31, 0x0a, 0x06, - 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x6e, - 0x65, 0x6f, 0x2e, 0x45, 0x78, 0x70, 0x6c, 0x6f, 0x69, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, - 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x22, - 0xc5, 0x01, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x19, 0x0a, 0x08, 0x66, 0x61, - 0x72, 0x6d, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x66, 0x61, - 0x72, 0x6d, 0x55, 0x72, 0x6c, 0x12, 0x23, 0x0a, 0x0d, 0x66, 0x61, 0x72, 0x6d, 0x5f, 0x70, 0x61, - 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x66, 0x61, - 0x72, 0x6d, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x66, 0x6c, - 0x61, 0x67, 0x5f, 0x72, 0x65, 0x67, 0x65, 0x78, 0x70, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x0a, 0x66, 0x6c, 0x61, 0x67, 0x52, 0x65, 0x67, 0x65, 0x78, 0x70, 0x12, 0x1d, 0x0a, 0x0a, 0x70, - 0x69, 0x6e, 0x67, 0x5f, 0x65, 0x76, 0x65, 0x72, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x09, 0x70, 0x69, 0x6e, 0x67, 0x45, 0x76, 0x65, 0x72, 0x79, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x75, - 0x62, 0x6d, 0x69, 0x74, 0x5f, 0x65, 0x76, 0x65, 0x72, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x0b, 0x73, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x45, 0x76, 0x65, 0x72, 0x79, 0x12, 0x18, 0x0a, - 0x07, 0x65, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, - 0x65, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x22, 0x81, 0x02, 0x0a, 0x0b, 0x53, 0x65, 0x72, 0x76, - 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x4b, 0x0a, 0x0f, 0x63, 0x6c, 0x69, 0x65, 0x6e, - 0x74, 0x5f, 0x74, 0x65, 0x61, 0x6d, 0x5f, 0x6d, 0x61, 0x70, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x23, 0x2e, 0x6e, 0x65, 0x6f, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x53, 0x74, 0x61, - 0x74, 0x65, 0x2e, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x54, 0x65, 0x61, 0x6d, 0x4d, 0x61, 0x70, - 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0d, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x54, 0x65, 0x61, - 0x6d, 0x4d, 0x61, 0x70, 0x12, 0x2d, 0x0a, 0x08, 0x65, 0x78, 0x70, 0x6c, 0x6f, 0x69, 0x74, 0x73, - 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x6e, 0x65, 0x6f, 0x2e, 0x45, 0x78, 0x70, - 0x6c, 0x6f, 0x69, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x08, 0x65, 0x78, 0x70, 0x6c, 0x6f, - 0x69, 0x74, 0x73, 0x12, 0x23, 0x0a, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x0b, 0x2e, 0x6e, 0x65, 0x6f, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, - 0x52, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x1a, 0x51, 0x0a, 0x12, 0x43, 0x6c, 0x69, 0x65, - 0x6e, 0x74, 0x54, 0x65, 0x61, 0x6d, 0x4d, 0x61, 0x70, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, - 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, - 0x12, 0x25, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x0f, 0x2e, 0x6e, 0x65, 0x6f, 0x2e, 0x54, 0x65, 0x61, 0x6d, 0x42, 0x75, 0x63, 0x6b, 0x65, 0x74, - 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x22, 0x0a, 0x0a, 0x46, - 0x69, 0x6c, 0x65, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x68, 0x75, - 0x6e, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x22, - 0x81, 0x01, 0x0a, 0x07, 0x4c, 0x6f, 0x67, 0x4c, 0x69, 0x6e, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x65, - 0x78, 0x70, 0x6c, 0x6f, 0x69, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x65, 0x78, - 0x70, 0x6c, 0x6f, 0x69, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, - 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x65, 0x76, - 0x65, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x12, - 0x12, 0x0a, 0x04, 0x74, 0x65, 0x61, 0x6d, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, - 0x65, 0x61, 0x6d, 0x22, 0xab, 0x01, 0x0a, 0x0b, 0x50, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x12, 0x2d, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x0e, 0x32, 0x19, 0x2e, 0x6e, 0x65, 0x6f, 0x2e, 0x50, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x2e, 0x50, 0x69, 0x6e, 0x67, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, - 0x70, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x12, - 0x16, 0x0a, 0x06, 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, - 0x06, 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x22, 0x38, 0x0a, 0x08, 0x50, 0x69, 0x6e, 0x67, 0x54, - 0x79, 0x70, 0x65, 0x12, 0x12, 0x0a, 0x0e, 0x43, 0x4f, 0x4e, 0x46, 0x49, 0x47, 0x5f, 0x52, 0x45, - 0x51, 0x55, 0x45, 0x53, 0x54, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x48, 0x45, 0x41, 0x52, 0x54, - 0x42, 0x45, 0x41, 0x54, 0x10, 0x01, 0x12, 0x09, 0x0a, 0x05, 0x4c, 0x45, 0x41, 0x56, 0x45, 0x10, - 0x02, 0x22, 0x36, 0x0a, 0x0c, 0x50, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x26, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x10, 0x2e, 0x6e, 0x65, 0x6f, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x53, 0x74, 0x61, - 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x22, 0x2f, 0x0a, 0x0e, 0x45, 0x78, 0x70, - 0x6c, 0x6f, 0x69, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x65, - 0x78, 0x70, 0x6c, 0x6f, 0x69, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x09, 0x65, 0x78, 0x70, 0x6c, 0x6f, 0x69, 0x74, 0x49, 0x64, 0x22, 0x3a, 0x0a, 0x0f, 0x45, 0x78, - 0x70, 0x6c, 0x6f, 0x69, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x27, 0x0a, - 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x6e, - 0x65, 0x6f, 0x2e, 0x45, 0x78, 0x70, 0x6c, 0x6f, 0x69, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, - 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x22, 0x3f, 0x0a, 0x14, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, - 0x45, 0x78, 0x70, 0x6c, 0x6f, 0x69, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x27, - 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, - 0x6e, 0x65, 0x6f, 0x2e, 0x45, 0x78, 0x70, 0x6c, 0x6f, 0x69, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, - 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x22, 0x40, 0x0a, 0x15, 0x55, 0x70, 0x64, 0x61, 0x74, - 0x65, 0x45, 0x78, 0x70, 0x6c, 0x6f, 0x69, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x27, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x11, 0x2e, 0x6e, 0x65, 0x6f, 0x2e, 0x45, 0x78, 0x70, 0x6c, 0x6f, 0x69, 0x74, 0x53, 0x74, 0x61, - 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x22, 0x31, 0x0a, 0x10, 0x53, 0x69, 0x6e, - 0x67, 0x6c, 0x65, 0x52, 0x75, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, + 0x1a, 0x1e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, + 0x66, 0x2f, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x1a, 0x1b, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, + 0x66, 0x2f, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x78, 0x0a, + 0x0a, 0x54, 0x65, 0x61, 0x6d, 0x42, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x12, 0x30, 0x0a, 0x05, 0x74, + 0x65, 0x61, 0x6d, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x6e, 0x65, 0x6f, + 0x2e, 0x54, 0x65, 0x61, 0x6d, 0x42, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x2e, 0x54, 0x65, 0x61, 0x6d, + 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x05, 0x74, 0x65, 0x61, 0x6d, 0x73, 0x1a, 0x38, 0x0a, + 0x0a, 0x54, 0x65, 0x61, 0x6d, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, + 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, + 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x1e, 0x0a, 0x08, 0x46, 0x69, 0x6c, 0x65, 0x49, + 0x6e, 0x66, 0x6f, 0x12, 0x12, 0x0a, 0x04, 0x75, 0x75, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x04, 0x75, 0x75, 0x69, 0x64, 0x22, 0xc2, 0x01, 0x0a, 0x14, 0x45, 0x78, 0x70, 0x6c, + 0x6f, 0x69, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x12, 0x1e, 0x0a, 0x0a, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x70, 0x6f, 0x69, 0x6e, 0x74, + 0x12, 0x1d, 0x0a, 0x0a, 0x69, 0x73, 0x5f, 0x61, 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x69, 0x73, 0x41, 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x12, + 0x36, 0x0a, 0x09, 0x72, 0x75, 0x6e, 0x5f, 0x65, 0x76, 0x65, 0x72, 0x79, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x08, 0x72, + 0x75, 0x6e, 0x45, 0x76, 0x65, 0x72, 0x79, 0x12, 0x33, 0x0a, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, + 0x75, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, + 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x52, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x22, 0xd3, 0x01, 0x0a, + 0x0c, 0x45, 0x78, 0x70, 0x6c, 0x6f, 0x69, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x65, 0x78, 0x70, 0x6c, 0x6f, 0x69, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x09, 0x65, 0x78, 0x70, 0x6c, 0x6f, 0x69, 0x74, 0x49, 0x64, 0x22, 0x23, 0x0a, 0x07, - 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x6d, 0x6d, 0x61, - 0x6e, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, - 0x64, 0x22, 0x07, 0x0a, 0x05, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x38, 0x0a, 0x12, 0x41, 0x64, + 0x09, 0x52, 0x09, 0x65, 0x78, 0x70, 0x6c, 0x6f, 0x69, 0x74, 0x49, 0x64, 0x12, 0x18, 0x0a, 0x07, + 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x76, + 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x21, 0x0a, 0x04, 0x66, 0x69, 0x6c, 0x65, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x6e, 0x65, 0x6f, 0x2e, 0x46, 0x69, 0x6c, 0x65, 0x49, + 0x6e, 0x66, 0x6f, 0x52, 0x04, 0x66, 0x69, 0x6c, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x64, 0x69, 0x73, + 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x64, 0x69, 0x73, + 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x73, + 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x73, 0x12, + 0x31, 0x0a, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x19, 0x2e, 0x6e, 0x65, 0x6f, 0x2e, 0x45, 0x78, 0x70, 0x6c, 0x6f, 0x69, 0x74, 0x43, 0x6f, 0x6e, + 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x06, 0x63, 0x6f, 0x6e, 0x66, + 0x69, 0x67, 0x22, 0xfb, 0x01, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x19, 0x0a, + 0x08, 0x66, 0x61, 0x72, 0x6d, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x07, 0x66, 0x61, 0x72, 0x6d, 0x55, 0x72, 0x6c, 0x12, 0x23, 0x0a, 0x0d, 0x66, 0x61, 0x72, 0x6d, + 0x5f, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0c, 0x66, 0x61, 0x72, 0x6d, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x12, 0x1f, 0x0a, + 0x0b, 0x66, 0x6c, 0x61, 0x67, 0x5f, 0x72, 0x65, 0x67, 0x65, 0x78, 0x70, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0a, 0x66, 0x6c, 0x61, 0x67, 0x52, 0x65, 0x67, 0x65, 0x78, 0x70, 0x12, 0x38, + 0x0a, 0x0a, 0x70, 0x69, 0x6e, 0x67, 0x5f, 0x65, 0x76, 0x65, 0x72, 0x79, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x09, 0x70, + 0x69, 0x6e, 0x67, 0x45, 0x76, 0x65, 0x72, 0x79, 0x12, 0x3c, 0x0a, 0x0c, 0x73, 0x75, 0x62, 0x6d, + 0x69, 0x74, 0x5f, 0x65, 0x76, 0x65, 0x72, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, + 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, + 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0b, 0x73, 0x75, 0x62, 0x6d, 0x69, + 0x74, 0x45, 0x76, 0x65, 0x72, 0x79, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x76, 0x69, 0x72, 0x6f, + 0x6e, 0x18, 0x06, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x65, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, + 0x22, 0x81, 0x02, 0x0a, 0x0b, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, + 0x12, 0x4b, 0x0a, 0x0f, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x74, 0x65, 0x61, 0x6d, 0x5f, + 0x6d, 0x61, 0x70, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x6e, 0x65, 0x6f, 0x2e, + 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x2e, 0x43, 0x6c, 0x69, 0x65, + 0x6e, 0x74, 0x54, 0x65, 0x61, 0x6d, 0x4d, 0x61, 0x70, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0d, + 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x54, 0x65, 0x61, 0x6d, 0x4d, 0x61, 0x70, 0x12, 0x2d, 0x0a, + 0x08, 0x65, 0x78, 0x70, 0x6c, 0x6f, 0x69, 0x74, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x11, 0x2e, 0x6e, 0x65, 0x6f, 0x2e, 0x45, 0x78, 0x70, 0x6c, 0x6f, 0x69, 0x74, 0x53, 0x74, 0x61, + 0x74, 0x65, 0x52, 0x08, 0x65, 0x78, 0x70, 0x6c, 0x6f, 0x69, 0x74, 0x73, 0x12, 0x23, 0x0a, 0x06, + 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0b, 0x2e, 0x6e, + 0x65, 0x6f, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, + 0x67, 0x1a, 0x51, 0x0a, 0x12, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x54, 0x65, 0x61, 0x6d, 0x4d, + 0x61, 0x70, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x25, 0x0a, 0x05, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x6e, 0x65, 0x6f, 0x2e, 0x54, + 0x65, 0x61, 0x6d, 0x42, 0x75, 0x63, 0x6b, 0x65, 0x74, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x3a, 0x02, 0x38, 0x01, 0x22, 0x22, 0x0a, 0x0a, 0x46, 0x69, 0x6c, 0x65, 0x53, 0x74, 0x72, 0x65, + 0x61, 0x6d, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0c, 0x52, 0x05, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x22, 0x81, 0x01, 0x0a, 0x07, 0x4c, 0x6f, 0x67, + 0x4c, 0x69, 0x6e, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x78, 0x70, 0x6c, 0x6f, 0x69, 0x74, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x65, 0x78, 0x70, 0x6c, 0x6f, 0x69, 0x74, 0x12, 0x18, + 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, + 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, + 0x61, 0x67, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, + 0x67, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x65, 0x61, 0x6d, + 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x65, 0x61, 0x6d, 0x22, 0xab, 0x01, 0x0a, + 0x0b, 0x50, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2d, 0x0a, 0x04, + 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x19, 0x2e, 0x6e, 0x65, 0x6f, + 0x2e, 0x50, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x50, 0x69, 0x6e, + 0x67, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x63, + 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, + 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x77, 0x65, 0x69, 0x67, + 0x68, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x06, 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, + 0x22, 0x38, 0x0a, 0x08, 0x50, 0x69, 0x6e, 0x67, 0x54, 0x79, 0x70, 0x65, 0x12, 0x12, 0x0a, 0x0e, + 0x43, 0x4f, 0x4e, 0x46, 0x49, 0x47, 0x5f, 0x52, 0x45, 0x51, 0x55, 0x45, 0x53, 0x54, 0x10, 0x00, + 0x12, 0x0d, 0x0a, 0x09, 0x48, 0x45, 0x41, 0x52, 0x54, 0x42, 0x45, 0x41, 0x54, 0x10, 0x01, 0x12, + 0x09, 0x0a, 0x05, 0x4c, 0x45, 0x41, 0x56, 0x45, 0x10, 0x02, 0x22, 0x36, 0x0a, 0x0c, 0x50, 0x69, + 0x6e, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x26, 0x0a, 0x05, 0x73, 0x74, + 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x6e, 0x65, 0x6f, 0x2e, + 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, + 0x74, 0x65, 0x22, 0x2f, 0x0a, 0x0e, 0x45, 0x78, 0x70, 0x6c, 0x6f, 0x69, 0x74, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x65, 0x78, 0x70, 0x6c, 0x6f, 0x69, 0x74, 0x5f, + 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x65, 0x78, 0x70, 0x6c, 0x6f, 0x69, + 0x74, 0x49, 0x64, 0x22, 0x3a, 0x0a, 0x0f, 0x45, 0x78, 0x70, 0x6c, 0x6f, 0x69, 0x74, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x27, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x6e, 0x65, 0x6f, 0x2e, 0x45, 0x78, 0x70, 0x6c, + 0x6f, 0x69, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x22, + 0x3f, 0x0a, 0x14, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x45, 0x78, 0x70, 0x6c, 0x6f, 0x69, 0x74, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x27, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x6e, 0x65, 0x6f, 0x2e, 0x45, 0x78, 0x70, + 0x6c, 0x6f, 0x69, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, + 0x22, 0x40, 0x0a, 0x15, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x45, 0x78, 0x70, 0x6c, 0x6f, 0x69, + 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x27, 0x0a, 0x05, 0x73, 0x74, 0x61, + 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x6e, 0x65, 0x6f, 0x2e, 0x45, + 0x78, 0x70, 0x6c, 0x6f, 0x69, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, + 0x74, 0x65, 0x22, 0x31, 0x0a, 0x10, 0x53, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x52, 0x75, 0x6e, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x65, 0x78, 0x70, 0x6c, 0x6f, 0x69, + 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x65, 0x78, 0x70, 0x6c, + 0x6f, 0x69, 0x74, 0x49, 0x64, 0x22, 0x23, 0x0a, 0x07, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, + 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x07, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x22, 0x38, 0x0a, 0x12, 0x41, 0x64, 0x64, 0x4c, 0x6f, 0x67, 0x4c, 0x69, 0x6e, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x22, 0x0a, 0x05, 0x6c, 0x69, 0x6e, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x6e, 0x65, 0x6f, 0x2e, 0x4c, 0x6f, 0x67, 0x4c, 0x69, 0x6e, 0x65, 0x52, 0x05, 0x6c, @@ -1300,7 +1274,7 @@ var file_neo_proto_rawDesc = []byte{ 0x6e, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x22, 0x0a, 0x05, 0x6c, 0x69, 0x6e, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x6e, 0x65, 0x6f, 0x2e, 0x4c, 0x6f, 0x67, 0x4c, 0x69, 0x6e, 0x65, 0x52, 0x05, 0x6c, 0x69, 0x6e, 0x65, 0x73, 0x32, - 0xfd, 0x04, 0x0a, 0x0e, 0x45, 0x78, 0x70, 0x6c, 0x6f, 0x69, 0x74, 0x4d, 0x61, 0x6e, 0x61, 0x67, + 0xb9, 0x05, 0x0a, 0x0e, 0x45, 0x78, 0x70, 0x6c, 0x6f, 0x69, 0x74, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x12, 0x2d, 0x0a, 0x04, 0x50, 0x69, 0x6e, 0x67, 0x12, 0x10, 0x2e, 0x6e, 0x65, 0x6f, 0x2e, 0x50, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x11, 0x2e, 0x6e, 0x65, 0x6f, 0x2e, 0x50, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, @@ -1318,31 +1292,35 @@ var file_neo_proto_rawDesc = []byte{ 0x12, 0x19, 0x2e, 0x6e, 0x65, 0x6f, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x45, 0x78, 0x70, 0x6c, 0x6f, 0x69, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x6e, 0x65, 0x6f, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x45, 0x78, 0x70, 0x6c, 0x6f, 0x69, 0x74, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x2e, 0x0a, 0x10, 0x42, 0x72, 0x6f, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x3a, 0x0a, 0x10, 0x42, 0x72, 0x6f, 0x61, 0x64, 0x63, 0x61, 0x73, 0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x12, 0x0c, 0x2e, - 0x6e, 0x65, 0x6f, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x1a, 0x0a, 0x2e, 0x6e, 0x65, - 0x6f, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x31, 0x0a, 0x11, 0x42, 0x72, 0x6f, - 0x61, 0x64, 0x63, 0x61, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x73, 0x12, 0x0a, - 0x2e, 0x6e, 0x65, 0x6f, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x0c, 0x2e, 0x6e, 0x65, 0x6f, - 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x22, 0x00, 0x30, 0x01, 0x12, 0x30, 0x0a, 0x09, - 0x53, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x52, 0x75, 0x6e, 0x12, 0x15, 0x2e, 0x6e, 0x65, 0x6f, 0x2e, - 0x53, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x52, 0x75, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x0a, 0x2e, 0x6e, 0x65, 0x6f, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x3a, - 0x0a, 0x11, 0x53, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x52, 0x75, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x73, 0x12, 0x0a, 0x2e, 0x6e, 0x65, 0x6f, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, + 0x6e, 0x65, 0x6f, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x1a, 0x16, 0x2e, 0x67, 0x6f, + 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, + 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x3d, 0x0a, 0x11, 0x42, 0x72, 0x6f, 0x61, 0x64, 0x63, 0x61, + 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x73, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, + 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, + 0x74, 0x79, 0x1a, 0x0c, 0x2e, 0x6e, 0x65, 0x6f, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, + 0x22, 0x00, 0x30, 0x01, 0x12, 0x3c, 0x0a, 0x09, 0x53, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x52, 0x75, + 0x6e, 0x12, 0x15, 0x2e, 0x6e, 0x65, 0x6f, 0x2e, 0x53, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x52, 0x75, + 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, + 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, + 0x22, 0x00, 0x12, 0x46, 0x0a, 0x11, 0x53, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x52, 0x75, 0x6e, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x73, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x15, 0x2e, 0x6e, 0x65, 0x6f, 0x2e, 0x53, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x52, 0x75, 0x6e, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x00, 0x30, 0x01, 0x12, 0x34, 0x0a, 0x0b, 0x41, 0x64, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x00, 0x30, 0x01, 0x12, 0x40, 0x0a, 0x0b, 0x41, 0x64, 0x64, 0x4c, 0x6f, 0x67, 0x4c, 0x69, 0x6e, 0x65, 0x73, 0x12, 0x17, 0x2e, 0x6e, 0x65, 0x6f, 0x2e, 0x41, 0x64, 0x64, 0x4c, 0x6f, 0x67, 0x4c, 0x69, 0x6e, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x0a, 0x2e, 0x6e, 0x65, 0x6f, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, - 0x12, 0x4d, 0x0a, 0x0e, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x4c, 0x6f, 0x67, 0x4c, 0x69, 0x6e, - 0x65, 0x73, 0x12, 0x1a, 0x2e, 0x6e, 0x65, 0x6f, 0x2e, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x4c, - 0x6f, 0x67, 0x4c, 0x69, 0x6e, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, + 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x4d, 0x0a, 0x0e, + 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x4c, 0x6f, 0x67, 0x4c, 0x69, 0x6e, 0x65, 0x73, 0x12, 0x1a, 0x2e, 0x6e, 0x65, 0x6f, 0x2e, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x4c, 0x6f, 0x67, 0x4c, 0x69, - 0x6e, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x30, 0x01, 0x42, - 0x21, 0x5a, 0x1f, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x6f, - 0x6d, 0x6f, 0x2d, 0x6d, 0x6f, 0x6e, 0x64, 0x72, 0x65, 0x67, 0x61, 0x6e, 0x74, 0x6f, 0x2f, 0x6e, - 0x65, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x6e, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x6e, 0x65, 0x6f, + 0x2e, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x4c, 0x6f, 0x67, 0x4c, 0x69, 0x6e, 0x65, 0x73, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x30, 0x01, 0x42, 0x21, 0x5a, 0x1f, 0x67, + 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x6f, 0x6d, 0x6f, 0x2d, 0x6d, + 0x6f, 0x6e, 0x64, 0x72, 0x65, 0x67, 0x61, 0x6e, 0x74, 0x6f, 0x2f, 0x6e, 0x65, 0x6f, 0x62, 0x06, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -1358,7 +1336,7 @@ func file_neo_proto_rawDescGZIP() []byte { } var file_neo_proto_enumTypes = make([]protoimpl.EnumInfo, 1) -var file_neo_proto_msgTypes = make([]protoimpl.MessageInfo, 22) +var file_neo_proto_msgTypes = make([]protoimpl.MessageInfo, 21) var file_neo_proto_goTypes = []interface{}{ (PingRequest_PingType)(0), // 0: neo.PingRequest.PingType (*TeamBucket)(nil), // 1: neo.TeamBucket @@ -1377,55 +1355,60 @@ var file_neo_proto_goTypes = []interface{}{ (*UpdateExploitResponse)(nil), // 14: neo.UpdateExploitResponse (*SingleRunRequest)(nil), // 15: neo.SingleRunRequest (*Command)(nil), // 16: neo.Command - (*Empty)(nil), // 17: neo.Empty - (*AddLogLinesRequest)(nil), // 18: neo.AddLogLinesRequest - (*SearchLogLinesRequest)(nil), // 19: neo.SearchLogLinesRequest - (*SearchLogLinesResponse)(nil), // 20: neo.SearchLogLinesResponse - nil, // 21: neo.TeamBucket.TeamsEntry - nil, // 22: neo.ServerState.ClientTeamMapEntry + (*AddLogLinesRequest)(nil), // 17: neo.AddLogLinesRequest + (*SearchLogLinesRequest)(nil), // 18: neo.SearchLogLinesRequest + (*SearchLogLinesResponse)(nil), // 19: neo.SearchLogLinesResponse + nil, // 20: neo.TeamBucket.TeamsEntry + nil, // 21: neo.ServerState.ClientTeamMapEntry + (*durationpb.Duration)(nil), // 22: google.protobuf.Duration + (*emptypb.Empty)(nil), // 23: google.protobuf.Empty } var file_neo_proto_depIdxs = []int32{ - 21, // 0: neo.TeamBucket.teams:type_name -> neo.TeamBucket.TeamsEntry - 2, // 1: neo.ExploitState.file:type_name -> neo.FileInfo - 3, // 2: neo.ExploitState.config:type_name -> neo.ExploitConfiguration - 22, // 3: neo.ServerState.client_team_map:type_name -> neo.ServerState.ClientTeamMapEntry - 4, // 4: neo.ServerState.exploits:type_name -> neo.ExploitState - 5, // 5: neo.ServerState.config:type_name -> neo.Config - 0, // 6: neo.PingRequest.type:type_name -> neo.PingRequest.PingType - 6, // 7: neo.PingResponse.state:type_name -> neo.ServerState - 4, // 8: neo.ExploitResponse.state:type_name -> neo.ExploitState - 4, // 9: neo.UpdateExploitRequest.state:type_name -> neo.ExploitState - 4, // 10: neo.UpdateExploitResponse.state:type_name -> neo.ExploitState - 8, // 11: neo.AddLogLinesRequest.lines:type_name -> neo.LogLine - 8, // 12: neo.SearchLogLinesResponse.lines:type_name -> neo.LogLine - 1, // 13: neo.ServerState.ClientTeamMapEntry.value:type_name -> neo.TeamBucket - 9, // 14: neo.ExploitManager.Ping:input_type -> neo.PingRequest - 7, // 15: neo.ExploitManager.UploadFile:input_type -> neo.FileStream - 2, // 16: neo.ExploitManager.DownloadFile:input_type -> neo.FileInfo - 11, // 17: neo.ExploitManager.Exploit:input_type -> neo.ExploitRequest - 13, // 18: neo.ExploitManager.UpdateExploit:input_type -> neo.UpdateExploitRequest - 16, // 19: neo.ExploitManager.BroadcastCommand:input_type -> neo.Command - 17, // 20: neo.ExploitManager.BroadcastRequests:input_type -> neo.Empty - 15, // 21: neo.ExploitManager.SingleRun:input_type -> neo.SingleRunRequest - 17, // 22: neo.ExploitManager.SingleRunRequests:input_type -> neo.Empty - 18, // 23: neo.ExploitManager.AddLogLines:input_type -> neo.AddLogLinesRequest - 19, // 24: neo.ExploitManager.SearchLogLines:input_type -> neo.SearchLogLinesRequest - 10, // 25: neo.ExploitManager.Ping:output_type -> neo.PingResponse - 2, // 26: neo.ExploitManager.UploadFile:output_type -> neo.FileInfo - 7, // 27: neo.ExploitManager.DownloadFile:output_type -> neo.FileStream - 12, // 28: neo.ExploitManager.Exploit:output_type -> neo.ExploitResponse - 14, // 29: neo.ExploitManager.UpdateExploit:output_type -> neo.UpdateExploitResponse - 17, // 30: neo.ExploitManager.BroadcastCommand:output_type -> neo.Empty - 16, // 31: neo.ExploitManager.BroadcastRequests:output_type -> neo.Command - 17, // 32: neo.ExploitManager.SingleRun:output_type -> neo.Empty - 15, // 33: neo.ExploitManager.SingleRunRequests:output_type -> neo.SingleRunRequest - 17, // 34: neo.ExploitManager.AddLogLines:output_type -> neo.Empty - 20, // 35: neo.ExploitManager.SearchLogLines:output_type -> neo.SearchLogLinesResponse - 25, // [25:36] is the sub-list for method output_type - 14, // [14:25] is the sub-list for method input_type - 14, // [14:14] is the sub-list for extension type_name - 14, // [14:14] is the sub-list for extension extendee - 0, // [0:14] is the sub-list for field type_name + 20, // 0: neo.TeamBucket.teams:type_name -> neo.TeamBucket.TeamsEntry + 22, // 1: neo.ExploitConfiguration.run_every:type_name -> google.protobuf.Duration + 22, // 2: neo.ExploitConfiguration.timeout:type_name -> google.protobuf.Duration + 2, // 3: neo.ExploitState.file:type_name -> neo.FileInfo + 3, // 4: neo.ExploitState.config:type_name -> neo.ExploitConfiguration + 22, // 5: neo.Config.ping_every:type_name -> google.protobuf.Duration + 22, // 6: neo.Config.submit_every:type_name -> google.protobuf.Duration + 21, // 7: neo.ServerState.client_team_map:type_name -> neo.ServerState.ClientTeamMapEntry + 4, // 8: neo.ServerState.exploits:type_name -> neo.ExploitState + 5, // 9: neo.ServerState.config:type_name -> neo.Config + 0, // 10: neo.PingRequest.type:type_name -> neo.PingRequest.PingType + 6, // 11: neo.PingResponse.state:type_name -> neo.ServerState + 4, // 12: neo.ExploitResponse.state:type_name -> neo.ExploitState + 4, // 13: neo.UpdateExploitRequest.state:type_name -> neo.ExploitState + 4, // 14: neo.UpdateExploitResponse.state:type_name -> neo.ExploitState + 8, // 15: neo.AddLogLinesRequest.lines:type_name -> neo.LogLine + 8, // 16: neo.SearchLogLinesResponse.lines:type_name -> neo.LogLine + 1, // 17: neo.ServerState.ClientTeamMapEntry.value:type_name -> neo.TeamBucket + 9, // 18: neo.ExploitManager.Ping:input_type -> neo.PingRequest + 7, // 19: neo.ExploitManager.UploadFile:input_type -> neo.FileStream + 2, // 20: neo.ExploitManager.DownloadFile:input_type -> neo.FileInfo + 11, // 21: neo.ExploitManager.Exploit:input_type -> neo.ExploitRequest + 13, // 22: neo.ExploitManager.UpdateExploit:input_type -> neo.UpdateExploitRequest + 16, // 23: neo.ExploitManager.BroadcastCommand:input_type -> neo.Command + 23, // 24: neo.ExploitManager.BroadcastRequests:input_type -> google.protobuf.Empty + 15, // 25: neo.ExploitManager.SingleRun:input_type -> neo.SingleRunRequest + 23, // 26: neo.ExploitManager.SingleRunRequests:input_type -> google.protobuf.Empty + 17, // 27: neo.ExploitManager.AddLogLines:input_type -> neo.AddLogLinesRequest + 18, // 28: neo.ExploitManager.SearchLogLines:input_type -> neo.SearchLogLinesRequest + 10, // 29: neo.ExploitManager.Ping:output_type -> neo.PingResponse + 2, // 30: neo.ExploitManager.UploadFile:output_type -> neo.FileInfo + 7, // 31: neo.ExploitManager.DownloadFile:output_type -> neo.FileStream + 12, // 32: neo.ExploitManager.Exploit:output_type -> neo.ExploitResponse + 14, // 33: neo.ExploitManager.UpdateExploit:output_type -> neo.UpdateExploitResponse + 23, // 34: neo.ExploitManager.BroadcastCommand:output_type -> google.protobuf.Empty + 16, // 35: neo.ExploitManager.BroadcastRequests:output_type -> neo.Command + 23, // 36: neo.ExploitManager.SingleRun:output_type -> google.protobuf.Empty + 15, // 37: neo.ExploitManager.SingleRunRequests:output_type -> neo.SingleRunRequest + 23, // 38: neo.ExploitManager.AddLogLines:output_type -> google.protobuf.Empty + 19, // 39: neo.ExploitManager.SearchLogLines:output_type -> neo.SearchLogLinesResponse + 29, // [29:40] is the sub-list for method output_type + 18, // [18:29] is the sub-list for method input_type + 18, // [18:18] is the sub-list for extension type_name + 18, // [18:18] is the sub-list for extension extendee + 0, // [0:18] is the sub-list for field type_name } func init() { file_neo_proto_init() } @@ -1627,18 +1610,6 @@ func file_neo_proto_init() { } } file_neo_proto_msgTypes[16].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Empty); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_neo_proto_msgTypes[17].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*AddLogLinesRequest); i { case 0: return &v.state @@ -1650,7 +1621,7 @@ func file_neo_proto_init() { return nil } } - file_neo_proto_msgTypes[18].Exporter = func(v interface{}, i int) interface{} { + file_neo_proto_msgTypes[17].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*SearchLogLinesRequest); i { case 0: return &v.state @@ -1662,7 +1633,7 @@ func file_neo_proto_init() { return nil } } - file_neo_proto_msgTypes[19].Exporter = func(v interface{}, i int) interface{} { + file_neo_proto_msgTypes[18].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*SearchLogLinesResponse); i { case 0: return &v.state @@ -1681,7 +1652,7 @@ func file_neo_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_neo_proto_rawDesc, NumEnums: 1, - NumMessages: 22, + NumMessages: 21, NumExtensions: 0, NumServices: 1, }, diff --git a/lib/genproto/neo/neo_grpc.pb.go b/lib/genproto/neo/neo_grpc.pb.go index f3a7581..db5c13b 100644 --- a/lib/genproto/neo/neo_grpc.pb.go +++ b/lib/genproto/neo/neo_grpc.pb.go @@ -1,4 +1,8 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. +// versions: +// - protoc-gen-go-grpc v1.2.0 +// - protoc v3.19.4 +// source: neo.proto package neo @@ -7,10 +11,12 @@ import ( grpc "google.golang.org/grpc" codes "google.golang.org/grpc/codes" status "google.golang.org/grpc/status" + emptypb "google.golang.org/protobuf/types/known/emptypb" ) // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. +// Requires gRPC-Go v1.32.0 or later. const _ = grpc.SupportPackageIsVersion7 // ExploitManagerClient is the client API for ExploitManager service. @@ -22,11 +28,11 @@ type ExploitManagerClient interface { DownloadFile(ctx context.Context, in *FileInfo, opts ...grpc.CallOption) (ExploitManager_DownloadFileClient, error) Exploit(ctx context.Context, in *ExploitRequest, opts ...grpc.CallOption) (*ExploitResponse, error) UpdateExploit(ctx context.Context, in *UpdateExploitRequest, opts ...grpc.CallOption) (*UpdateExploitResponse, error) - BroadcastCommand(ctx context.Context, in *Command, opts ...grpc.CallOption) (*Empty, error) - BroadcastRequests(ctx context.Context, in *Empty, opts ...grpc.CallOption) (ExploitManager_BroadcastRequestsClient, error) - SingleRun(ctx context.Context, in *SingleRunRequest, opts ...grpc.CallOption) (*Empty, error) - SingleRunRequests(ctx context.Context, in *Empty, opts ...grpc.CallOption) (ExploitManager_SingleRunRequestsClient, error) - AddLogLines(ctx context.Context, in *AddLogLinesRequest, opts ...grpc.CallOption) (*Empty, error) + BroadcastCommand(ctx context.Context, in *Command, opts ...grpc.CallOption) (*emptypb.Empty, error) + BroadcastRequests(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (ExploitManager_BroadcastRequestsClient, error) + SingleRun(ctx context.Context, in *SingleRunRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) + SingleRunRequests(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (ExploitManager_SingleRunRequestsClient, error) + AddLogLines(ctx context.Context, in *AddLogLinesRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) SearchLogLines(ctx context.Context, in *SearchLogLinesRequest, opts ...grpc.CallOption) (ExploitManager_SearchLogLinesClient, error) } @@ -48,7 +54,7 @@ func (c *exploitManagerClient) Ping(ctx context.Context, in *PingRequest, opts . } func (c *exploitManagerClient) UploadFile(ctx context.Context, opts ...grpc.CallOption) (ExploitManager_UploadFileClient, error) { - stream, err := c.cc.NewStream(ctx, &_ExploitManager_serviceDesc.Streams[0], "/neo.ExploitManager/UploadFile", opts...) + stream, err := c.cc.NewStream(ctx, &ExploitManager_ServiceDesc.Streams[0], "/neo.ExploitManager/UploadFile", opts...) if err != nil { return nil, err } @@ -82,7 +88,7 @@ func (x *exploitManagerUploadFileClient) CloseAndRecv() (*FileInfo, error) { } func (c *exploitManagerClient) DownloadFile(ctx context.Context, in *FileInfo, opts ...grpc.CallOption) (ExploitManager_DownloadFileClient, error) { - stream, err := c.cc.NewStream(ctx, &_ExploitManager_serviceDesc.Streams[1], "/neo.ExploitManager/DownloadFile", opts...) + stream, err := c.cc.NewStream(ctx, &ExploitManager_ServiceDesc.Streams[1], "/neo.ExploitManager/DownloadFile", opts...) if err != nil { return nil, err } @@ -131,8 +137,8 @@ func (c *exploitManagerClient) UpdateExploit(ctx context.Context, in *UpdateExpl return out, nil } -func (c *exploitManagerClient) BroadcastCommand(ctx context.Context, in *Command, opts ...grpc.CallOption) (*Empty, error) { - out := new(Empty) +func (c *exploitManagerClient) BroadcastCommand(ctx context.Context, in *Command, opts ...grpc.CallOption) (*emptypb.Empty, error) { + out := new(emptypb.Empty) err := c.cc.Invoke(ctx, "/neo.ExploitManager/BroadcastCommand", in, out, opts...) if err != nil { return nil, err @@ -140,8 +146,8 @@ func (c *exploitManagerClient) BroadcastCommand(ctx context.Context, in *Command return out, nil } -func (c *exploitManagerClient) BroadcastRequests(ctx context.Context, in *Empty, opts ...grpc.CallOption) (ExploitManager_BroadcastRequestsClient, error) { - stream, err := c.cc.NewStream(ctx, &_ExploitManager_serviceDesc.Streams[2], "/neo.ExploitManager/BroadcastRequests", opts...) +func (c *exploitManagerClient) BroadcastRequests(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (ExploitManager_BroadcastRequestsClient, error) { + stream, err := c.cc.NewStream(ctx, &ExploitManager_ServiceDesc.Streams[2], "/neo.ExploitManager/BroadcastRequests", opts...) if err != nil { return nil, err } @@ -172,8 +178,8 @@ func (x *exploitManagerBroadcastRequestsClient) Recv() (*Command, error) { return m, nil } -func (c *exploitManagerClient) SingleRun(ctx context.Context, in *SingleRunRequest, opts ...grpc.CallOption) (*Empty, error) { - out := new(Empty) +func (c *exploitManagerClient) SingleRun(ctx context.Context, in *SingleRunRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) { + out := new(emptypb.Empty) err := c.cc.Invoke(ctx, "/neo.ExploitManager/SingleRun", in, out, opts...) if err != nil { return nil, err @@ -181,8 +187,8 @@ func (c *exploitManagerClient) SingleRun(ctx context.Context, in *SingleRunReque return out, nil } -func (c *exploitManagerClient) SingleRunRequests(ctx context.Context, in *Empty, opts ...grpc.CallOption) (ExploitManager_SingleRunRequestsClient, error) { - stream, err := c.cc.NewStream(ctx, &_ExploitManager_serviceDesc.Streams[3], "/neo.ExploitManager/SingleRunRequests", opts...) +func (c *exploitManagerClient) SingleRunRequests(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (ExploitManager_SingleRunRequestsClient, error) { + stream, err := c.cc.NewStream(ctx, &ExploitManager_ServiceDesc.Streams[3], "/neo.ExploitManager/SingleRunRequests", opts...) if err != nil { return nil, err } @@ -213,8 +219,8 @@ func (x *exploitManagerSingleRunRequestsClient) Recv() (*SingleRunRequest, error return m, nil } -func (c *exploitManagerClient) AddLogLines(ctx context.Context, in *AddLogLinesRequest, opts ...grpc.CallOption) (*Empty, error) { - out := new(Empty) +func (c *exploitManagerClient) AddLogLines(ctx context.Context, in *AddLogLinesRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) { + out := new(emptypb.Empty) err := c.cc.Invoke(ctx, "/neo.ExploitManager/AddLogLines", in, out, opts...) if err != nil { return nil, err @@ -223,7 +229,7 @@ func (c *exploitManagerClient) AddLogLines(ctx context.Context, in *AddLogLinesR } func (c *exploitManagerClient) SearchLogLines(ctx context.Context, in *SearchLogLinesRequest, opts ...grpc.CallOption) (ExploitManager_SearchLogLinesClient, error) { - stream, err := c.cc.NewStream(ctx, &_ExploitManager_serviceDesc.Streams[4], "/neo.ExploitManager/SearchLogLines", opts...) + stream, err := c.cc.NewStream(ctx, &ExploitManager_ServiceDesc.Streams[4], "/neo.ExploitManager/SearchLogLines", opts...) if err != nil { return nil, err } @@ -263,11 +269,11 @@ type ExploitManagerServer interface { DownloadFile(*FileInfo, ExploitManager_DownloadFileServer) error Exploit(context.Context, *ExploitRequest) (*ExploitResponse, error) UpdateExploit(context.Context, *UpdateExploitRequest) (*UpdateExploitResponse, error) - BroadcastCommand(context.Context, *Command) (*Empty, error) - BroadcastRequests(*Empty, ExploitManager_BroadcastRequestsServer) error - SingleRun(context.Context, *SingleRunRequest) (*Empty, error) - SingleRunRequests(*Empty, ExploitManager_SingleRunRequestsServer) error - AddLogLines(context.Context, *AddLogLinesRequest) (*Empty, error) + BroadcastCommand(context.Context, *Command) (*emptypb.Empty, error) + BroadcastRequests(*emptypb.Empty, ExploitManager_BroadcastRequestsServer) error + SingleRun(context.Context, *SingleRunRequest) (*emptypb.Empty, error) + SingleRunRequests(*emptypb.Empty, ExploitManager_SingleRunRequestsServer) error + AddLogLines(context.Context, *AddLogLinesRequest) (*emptypb.Empty, error) SearchLogLines(*SearchLogLinesRequest, ExploitManager_SearchLogLinesServer) error mustEmbedUnimplementedExploitManagerServer() } @@ -291,19 +297,19 @@ func (UnimplementedExploitManagerServer) Exploit(context.Context, *ExploitReques func (UnimplementedExploitManagerServer) UpdateExploit(context.Context, *UpdateExploitRequest) (*UpdateExploitResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method UpdateExploit not implemented") } -func (UnimplementedExploitManagerServer) BroadcastCommand(context.Context, *Command) (*Empty, error) { +func (UnimplementedExploitManagerServer) BroadcastCommand(context.Context, *Command) (*emptypb.Empty, error) { return nil, status.Errorf(codes.Unimplemented, "method BroadcastCommand not implemented") } -func (UnimplementedExploitManagerServer) BroadcastRequests(*Empty, ExploitManager_BroadcastRequestsServer) error { +func (UnimplementedExploitManagerServer) BroadcastRequests(*emptypb.Empty, ExploitManager_BroadcastRequestsServer) error { return status.Errorf(codes.Unimplemented, "method BroadcastRequests not implemented") } -func (UnimplementedExploitManagerServer) SingleRun(context.Context, *SingleRunRequest) (*Empty, error) { +func (UnimplementedExploitManagerServer) SingleRun(context.Context, *SingleRunRequest) (*emptypb.Empty, error) { return nil, status.Errorf(codes.Unimplemented, "method SingleRun not implemented") } -func (UnimplementedExploitManagerServer) SingleRunRequests(*Empty, ExploitManager_SingleRunRequestsServer) error { +func (UnimplementedExploitManagerServer) SingleRunRequests(*emptypb.Empty, ExploitManager_SingleRunRequestsServer) error { return status.Errorf(codes.Unimplemented, "method SingleRunRequests not implemented") } -func (UnimplementedExploitManagerServer) AddLogLines(context.Context, *AddLogLinesRequest) (*Empty, error) { +func (UnimplementedExploitManagerServer) AddLogLines(context.Context, *AddLogLinesRequest) (*emptypb.Empty, error) { return nil, status.Errorf(codes.Unimplemented, "method AddLogLines not implemented") } func (UnimplementedExploitManagerServer) SearchLogLines(*SearchLogLinesRequest, ExploitManager_SearchLogLinesServer) error { @@ -319,7 +325,7 @@ type UnsafeExploitManagerServer interface { } func RegisterExploitManagerServer(s grpc.ServiceRegistrar, srv ExploitManagerServer) { - s.RegisterService(&_ExploitManager_serviceDesc, srv) + s.RegisterService(&ExploitManager_ServiceDesc, srv) } func _ExploitManager_Ping_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { @@ -442,7 +448,7 @@ func _ExploitManager_BroadcastCommand_Handler(srv interface{}, ctx context.Conte } func _ExploitManager_BroadcastRequests_Handler(srv interface{}, stream grpc.ServerStream) error { - m := new(Empty) + m := new(emptypb.Empty) if err := stream.RecvMsg(m); err != nil { return err } @@ -481,7 +487,7 @@ func _ExploitManager_SingleRun_Handler(srv interface{}, ctx context.Context, dec } func _ExploitManager_SingleRunRequests_Handler(srv interface{}, stream grpc.ServerStream) error { - m := new(Empty) + m := new(emptypb.Empty) if err := stream.RecvMsg(m); err != nil { return err } @@ -540,7 +546,10 @@ func (x *exploitManagerSearchLogLinesServer) Send(m *SearchLogLinesResponse) err return x.ServerStream.SendMsg(m) } -var _ExploitManager_serviceDesc = grpc.ServiceDesc{ +// ExploitManager_ServiceDesc is the grpc.ServiceDesc for ExploitManager service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var ExploitManager_ServiceDesc = grpc.ServiceDesc{ ServiceName: "neo.ExploitManager", HandlerType: (*ExploitManagerServer)(nil), Methods: []grpc.MethodDesc{ diff --git a/lib/proto/neo.proto b/lib/proto/neo.proto index 0b182d2..2d29626 100644 --- a/lib/proto/neo.proto +++ b/lib/proto/neo.proto @@ -3,128 +3,129 @@ package neo; option go_package = "github.com/pomo-mondreganto/neo"; +import "google/protobuf/duration.proto"; +import "google/protobuf/empty.proto"; + message TeamBucket { - map teams = 1; + map teams = 1; } message FileInfo { - string uuid = 1; + string uuid = 1; } message ExploitConfiguration { - string entrypoint = 1; - bool is_archive = 2; - string run_every = 3; - string timeout = 4; + string entrypoint = 1; + bool is_archive = 2; + google.protobuf.Duration run_every = 3; + google.protobuf.Duration timeout = 4; } message ExploitState { - string exploit_id = 1; - int64 version = 2; - FileInfo file = 3; - bool disabled = 4; - bool endless = 5; - ExploitConfiguration config = 6; + string exploit_id = 1; + int64 version = 2; + FileInfo file = 3; + bool disabled = 4; + bool endless = 5; + ExploitConfiguration config = 6; } message Config { - string farm_url = 1; - string farm_password = 2; - string flag_regexp = 3; - string ping_every = 4; - string submit_every = 5; - repeated string environ = 6; + string farm_url = 1; + string farm_password = 2; + string flag_regexp = 3; + google.protobuf.Duration ping_every = 4; + google.protobuf.Duration submit_every = 5; + repeated string environ = 6; } message ServerState { - // Ping distribution by client - map client_team_map = 1; - repeated ExploitState exploits = 2; - Config config = 3; + // Ping distribution by client + map client_team_map = 1; + repeated ExploitState exploits = 2; + Config config = 3; } message FileStream { - bytes chunk = 1; + bytes chunk = 1; } message LogLine { - string exploit = 1; - int64 version = 2; - string message = 3; - string level = 4; - string team = 5; + string exploit = 1; + int64 version = 2; + string message = 3; + string level = 4; + string team = 5; } message PingRequest { - enum PingType { - CONFIG_REQUEST = 0; - HEARTBEAT = 1; - LEAVE = 2; - }; + enum PingType { + CONFIG_REQUEST = 0; + HEARTBEAT = 1; + LEAVE = 2; + }; - PingType type = 1; - string client_id = 2; - int32 weight = 3; + PingType type = 1; + string client_id = 2; + int32 weight = 3; } message PingResponse { - ServerState state = 1; + ServerState state = 1; } message ExploitRequest { - string exploit_id = 1; + string exploit_id = 1; } message ExploitResponse { - ExploitState state = 1; + ExploitState state = 1; } message UpdateExploitRequest { - ExploitState state = 1; + ExploitState state = 1; } message UpdateExploitResponse { - ExploitState state = 1; + ExploitState state = 1; } message SingleRunRequest { - string exploit_id = 1; + string exploit_id = 1; } message Command { - string command = 1; + string command = 1; } -message Empty {} - message AddLogLinesRequest { - repeated LogLine lines = 1; + repeated LogLine lines = 1; } message SearchLogLinesRequest { - string exploit = 1; - int64 version = 2; + string exploit = 1; + int64 version = 2; } message SearchLogLinesResponse { - repeated LogLine lines = 1; + repeated LogLine lines = 1; } service ExploitManager { - rpc Ping (PingRequest) returns (PingResponse) {} + rpc Ping (PingRequest) returns (PingResponse) {} - rpc UploadFile(stream FileStream) returns (FileInfo) {} - rpc DownloadFile(FileInfo) returns (stream FileStream) {} + rpc UploadFile(stream FileStream) returns (FileInfo) {} + rpc DownloadFile(FileInfo) returns (stream FileStream) {} - rpc Exploit(ExploitRequest) returns (ExploitResponse) {} - rpc UpdateExploit(UpdateExploitRequest) returns (UpdateExploitResponse) {} + rpc Exploit(ExploitRequest) returns (ExploitResponse) {} + rpc UpdateExploit(UpdateExploitRequest) returns (UpdateExploitResponse) {} - rpc BroadcastCommand(Command) returns (Empty) {} - rpc BroadcastRequests(Empty) returns (stream Command) {} + rpc BroadcastCommand(Command) returns (google.protobuf.Empty) {} + rpc BroadcastRequests(google.protobuf.Empty) returns (stream Command) {} - rpc SingleRun(SingleRunRequest) returns (Empty) {} - rpc SingleRunRequests(Empty) returns (stream SingleRunRequest) {} + rpc SingleRun(SingleRunRequest) returns (google.protobuf.Empty) {} + rpc SingleRunRequests(google.protobuf.Empty) returns (stream SingleRunRequest) {} - rpc AddLogLines(AddLogLinesRequest) returns (Empty) {} - rpc SearchLogLines(SearchLogLinesRequest) returns (stream SearchLogLinesResponse) {} + rpc AddLogLines(AddLogLinesRequest) returns (google.protobuf.Empty) {} + rpc SearchLogLines(SearchLogLinesRequest) returns (stream SearchLogLinesResponse) {} } diff --git a/pkg/archive/targz.go b/pkg/archive/targz.go index bdd1a14..2f1ec90 100644 --- a/pkg/archive/targz.go +++ b/pkg/archive/targz.go @@ -52,18 +52,16 @@ func Untar(dst string, r io.Reader) error { // the target location where the dir/file should be created target := filepath.Join(dst, header.Name) - // the following switch could also be done using fi.Mode(), not sure if there + // the following switch could also be done using fi.Mode(), not sure if there's // a benefit of using one vs. the other. // fi := header.FileInfo() // check the file type switch header.Typeflag { - // if its a dir and it doesn't exist create it + // if it's a dir, and it doesn't exist create it case tar.TypeDir: - if _, err := os.Stat(target); err != nil { - if err := os.MkdirAll(target, 0755); err != nil { - return fmt.Errorf("creating directory %s: %w", target, err) - } + if err := os.MkdirAll(target, 0755); err != nil { + return fmt.Errorf("creating directory %s: %w", target, err) } // if it's a file create it @@ -75,10 +73,11 @@ func Untar(dst string, r io.Reader) error { // copy over contents if _, err := io.Copy(f, tr); err != nil { + _ = f.Close() return fmt.Errorf("copying file content: %w", err) } - // manually close here after each file operation; defering would cause each file close + // manually close here after each file operation; deferring would cause each file close // to wait until all operations have completed. if err := f.Close(); err != nil { return fmt.Errorf("closing file %s: %w", target, err) @@ -142,12 +141,13 @@ func Tar(src string, w io.Writer) error { // copy file data into tar writer if _, err := io.Copy(tw, f); err != nil { + _ = f.Close() return fmt.Errorf("copying file content: %w", err) } // manually close here after each file operation; defering would cause each file close // to wait until all operations have completed. - if err = f.Close(); err != nil { + if err := f.Close(); err != nil { return fmt.Errorf("closing file: %w", err) } diff --git a/pkg/filestream/filestream.go b/pkg/filestream/filestream.go index acbd215..1b5be0f 100644 --- a/pkg/filestream/filestream.go +++ b/pkg/filestream/filestream.go @@ -29,7 +29,7 @@ func Save(stream DownloadStream, out io.Writer) error { if err != nil { return fmt.Errorf("reading from stream: %w", err) } - if _, err := out.Write(in.GetChunk()); err != nil { + if _, err := out.Write(in.Chunk); err != nil { return fmt.Errorf("writing stream content chunk: %w", err) } } diff --git a/pkg/filestream/filestream_test.go b/pkg/filestream/filestream_test.go index d11c02d..70d493b 100644 --- a/pkg/filestream/filestream_test.go +++ b/pkg/filestream/filestream_test.go @@ -9,6 +9,7 @@ import ( "testing" "github.com/google/go-cmp/cmp" + "github.com/stretchr/testify/require" neopb "neo/lib/genproto/neo" ) @@ -32,7 +33,7 @@ type mockUploadStream struct { } func (ms *mockUploadStream) Send(s *neopb.FileStream) error { - ms.buf.Write(s.GetChunk()) + ms.buf.Write(s.Chunk) if ms.withError { return errTestWrite } @@ -86,10 +87,7 @@ func TestLoad(t *testing.T) { err: errTestWrite, }, } { - err := Load(tc.reader, tc.stream) - if !errors.Is(err, tc.err) { - t.Errorf("Load(): got unexpected error = %v", err) - } + require.ErrorIs(t, Load(tc.reader, tc.stream), tc.err) if diff := cmp.Diff(tc.want, tc.stream.buf.String()); diff != "" { t.Errorf("Load() result mismatch (-want +got):\n%s", diff) } @@ -122,13 +120,7 @@ func TestSave(t *testing.T) { err: errTestWrite, }, } { - err := Save(tc.stream, tc.writer) - if tc.err == nil && err != nil { - t.Errorf("Save(): error was not expected = %v", err) - } - if tc.err != nil && !errors.Is(err, tc.err) { - t.Errorf("Save(): expected error %v, got = %v", tc.err, err) - } + require.ErrorIs(t, Save(tc.stream, tc.writer), tc.err) if diff := cmp.Diff(tc.want, tc.writer.String()); diff != "" { t.Errorf("Save() result mismatch (-want +got):\n%s", diff) } diff --git a/pkg/gstream/dynsize_test.go b/pkg/gstream/dynsize_test.go index a730b5a..12c6541 100644 --- a/pkg/gstream/dynsize_test.go +++ b/pkg/gstream/dynsize_test.go @@ -2,35 +2,54 @@ package gstream import ( "context" + "errors" "testing" "github.com/stretchr/testify/require" ) func TestDynamicSizeCache(t *testing.T) { - s := &mockWStream{} - cache := NewDynamicSizeCache[*mockSizable, []*mockSizable]( - s, - 10, - func(a []*mockSizable) (*[]*mockSizable, error) { - return &a, nil - }, - ) - gen := func(a int) *mockSizable { - return &mockSizable{size: a} - } - require.NoError(t, cache.Queue(gen(5), gen(3))) - require.Empty(t, s.sent) - - require.NoError(t, cache.Flush()) - require.Equal(t, [][]*mockSizable{{gen(5), gen(3)}}, s.sent) - s.sent = nil - - require.NoError(t, cache.Queue(gen(5), gen(3))) - require.NoError(t, cache.Queue(gen(2), gen(3))) - require.Equal(t, [][]*mockSizable{{gen(5), gen(3), gen(2)}}, s.sent) - require.NoError(t, cache.Flush()) - require.Equal(t, [][]*mockSizable{{gen(5), gen(3), gen(2)}, {gen(3)}}, s.sent) + t.Run("simple", func(t *testing.T) { + s := &mockWStream{} + cache := NewDynamicSizeCache[*mockSizable, []*mockSizable]( + s, + 10, + func(a []*mockSizable) (*[]*mockSizable, error) { + return &a, nil + }, + ) + gen := func(a int) *mockSizable { + return &mockSizable{size: a} + } + require.NoError(t, cache.Queue(gen(5), gen(3))) + require.Empty(t, s.sent) + + require.NoError(t, cache.Flush()) + require.Equal(t, [][]*mockSizable{{gen(5), gen(3)}}, s.sent) + s.sent = nil + + require.NoError(t, cache.Queue(gen(5), gen(3))) + require.NoError(t, cache.Queue(gen(2), gen(3))) + require.Equal(t, [][]*mockSizable{{gen(5), gen(3), gen(2)}}, s.sent) + require.NoError(t, cache.Flush()) + require.Equal(t, [][]*mockSizable{{gen(5), gen(3), gen(2)}, {gen(3)}}, s.sent) + }) + + t.Run("error propagation", func(t *testing.T) { + mockErr := errors.New("mock error") + s := &mockWStream{returnErr: mockErr} + cache := NewDynamicSizeCache[*mockSizable, []*mockSizable]( + s, + 5, + func(a []*mockSizable) (*[]*mockSizable, error) { + return &a, nil + }, + ) + err := cache.Queue(&mockSizable{size: 5}) + require.ErrorIs(t, err, mockErr) + require.NotEqual(t, mockErr, err) + require.Equal(t, [][]*mockSizable{{&mockSizable{size: 5}}}, s.sent) + }) } type mockSizable struct { @@ -42,12 +61,13 @@ func (s *mockSizable) EstimateSize() int { } type mockWStream struct { - sent [][]*mockSizable + sent [][]*mockSizable + returnErr error } func (m *mockWStream) Send(t *[]*mockSizable) error { m.sent = append(m.sent, *t) - return nil + return m.returnErr } func (m *mockWStream) Context() context.Context { diff --git a/pkg/hostbucket/hostbucket_test.go b/pkg/hostbucket/hostbucket_test.go index f6050ab..883380b 100644 --- a/pkg/hostbucket/hostbucket_test.go +++ b/pkg/hostbucket/hostbucket_test.go @@ -1,6 +1,7 @@ package hostbucket import ( + "fmt" "math" "testing" @@ -9,6 +10,7 @@ import ( "github.com/denisbrodbeck/machineid" "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" + "github.com/stretchr/testify/require" ) func TestHostBucket_Add(t *testing.T) { @@ -35,37 +37,28 @@ func TestHostBucket_Add(t *testing.T) { gotTeams := make(map[string]string) for cid, wantn := range tc.want { ipb := b[cid] - teams := ipb.GetTeams() - if len(teams) != wantn { - t.Errorf("HostBucket.AddNode(): incorrent number of gotIps in user bucket want=%d, got=%d", - wantn, len(ipb.GetTeams()), - ) - } - for k, v := range teams { + require.Len(t, ipb.Teams, wantn) + for k, v := range ipb.Teams { gotTeams[k] = v } } - less := func(s1, s2 string) bool { - return s1 < s2 - } - if diff := cmp.Diff(tc.b.teams, gotTeams, cmpopts.SortSlices(less)); diff != "" && len(tc.want) > 0 { + if diff := cmp.Diff(tc.b.teams, gotTeams, cmpopts.SortSlices(testutils.LessString)); diff != "" && len(tc.want) > 0 { t.Errorf("HostBucket.AddNode() summary ips mismatch (-want +got):\n%s", diff) } } } func TestHostBucket_Add_Distribution(t *testing.T) { - populate := func(idCount int, weightMax int) *HostBucket { + populate := func(idCount, ipCount, weightMax int) *HostBucket { teams := make(map[string]string) - for id := range teams { + for i := 0; i < ipCount; i++ { + id := fmt.Sprintf("team-%d", i) teams[id] = testutils.RandomIP() } hb := New(teams) mid, err := machineid.ID() - if err != nil { - t.Fatalf("Could not get machine id: %v", err) - } + require.NoError(t, err) for i := 0; i < idCount; i++ { id := testutils.RandomString(len(mid)) w := testutils.RandomInt(1, weightMax+1) @@ -90,7 +83,7 @@ func TestHostBucket_Add_Distribution(t *testing.T) { 0.7, 0.3, 32, - 30, + 100, }, { 1000, @@ -99,66 +92,59 @@ func TestHostBucket_Add_Distribution(t *testing.T) { 0.5, 0.2, 32, - 30, + 100, }, } { meanStdDev := 0.0 for i := 0; i < tc.runs; i++ { - b := populate(tc.idCount, tc.weightMax) + b := populate(tc.idCount, tc.ipCount, tc.weightMax) sizes := make([]float64, tc.idCount) meanSize := 0.0 for i := range sizes { id := b.nodes[i].id - ips := b.buck[id].GetTeams() - sizes[i] = float64(len(ips)) / float64(b.nodes[i].weight) + sizes[i] = float64(len(b.buck[id].Teams)) / float64(b.nodes[i].weight) meanSize += sizes[i] } meanSize /= float64(len(sizes)) stdDev := 0.0 for i := range sizes { - id := b.nodes[i].id deviation := math.Abs((sizes[i] - meanSize) / meanSize) - if deviation > tc.maxDeviation { - t.Errorf( - "Deviation for bucket %s too large: %f > %f, target size: %f, weight %d, got size: %f", - id, - deviation, - tc.maxDeviation, - meanSize, - b.nodes[i].weight, - sizes[i], - ) - } + require.Truef( + t, + deviation <= tc.maxDeviation, + "Deviation for bucket %s too large: %f > %f, target size: %f, weight %d, got size: %f", + b.nodes[i].id, + deviation, + tc.maxDeviation, + meanSize, + b.nodes[i].weight, + sizes[i], + ) curDev := math.Abs(sizes[i] - meanSize) stdDev += curDev * curDev } stdDev = math.Sqrt(stdDev/float64(len(sizes))) / meanSize - if stdDev > tc.maxStdDev { - t.Errorf("Std too large: %f > %f", stdDev, tc.maxStdDev) - } + require.LessOrEqual(t, stdDev, tc.maxStdDev) meanStdDev += stdDev } meanStdDev /= float64(tc.runs) - if meanStdDev > tc.maxMeanStdDev { - t.Errorf("Mean std too large: %f > %f", meanStdDev, tc.maxMeanStdDev) - } + require.True(t, meanStdDev <= tc.maxMeanStdDev) t.Logf("Mean std dev: %f", meanStdDev) } } func TestHostBucket_Balancing(t *testing.T) { populate := func(ipCount, idCount int) *HostBucket { - teams := make(map[string]string, ipCount) - for id := range teams { + teams := make(map[string]string) + for i := 0; i < ipCount; i++ { + id := fmt.Sprintf("team-%d", i) teams[id] = testutils.RandomIP() } hb := New(teams) mid, err := machineid.ID() - if err != nil { - t.Fatalf("Could not get machine id: %v", err) - } + require.NoError(t, err) for i := 0; i < idCount; i++ { id := testutils.RandomString(len(mid)) hb.AddNode(id, 1) @@ -192,8 +178,7 @@ func TestHostBucket_Balancing(t *testing.T) { beforeByIP := make(map[string]string) for _, n := range b.nodes { - teams := b.buck[n.id].GetTeams() - for _, ip := range teams { + for _, ip := range b.buck[n.id].Teams { beforeByIP[ip] = n.id } } @@ -201,8 +186,7 @@ func TestHostBucket_Balancing(t *testing.T) { getCntMoved := func() int { cntMoved := 0 for _, n := range b.nodes { - teams := b.buck[n.id].GetTeams() - for _, ip := range teams { + for _, ip := range b.buck[n.id].Teams { if n.id != beforeByIP[ip] { cntMoved++ } @@ -218,23 +202,31 @@ func TestHostBucket_Balancing(t *testing.T) { } cntMoved := getCntMoved() movedFraction := float64(cntMoved) / float64(tc.ipCount) - if movedFraction > tc.maxMoved { - t.Errorf("Too many ips moved after delete: %f%%, %d of %d", movedFraction*100, cntMoved, tc.ipCount) - } + require.Truef( + t, + movedFraction <= tc.maxMoved, + "Too many ips moved after delete: %f%%, %d of %d", + movedFraction*100, + cntMoved, + tc.ipCount, + ) mid, err := machineid.ID() - if err != nil { - t.Fatalf("Could not get machine id: %v", err) - } + require.NoError(t, err) for i := 0; i < tc.cntAdd; i++ { id := testutils.RandomString(len(mid)) b.AddNode(id, 1) } cntMoved = getCntMoved() movedFraction = float64(cntMoved) / float64(tc.ipCount) - if movedFraction > tc.maxMoved { - t.Errorf("Too many ips moved after add: %f%%, %d of %d", movedFraction*100, cntMoved, tc.ipCount) - } + require.Truef( + t, + movedFraction <= tc.maxMoved, + "Too many ips moved after delete: %f%%, %d of %d", + movedFraction*100, + cntMoved, + tc.ipCount, + ) } } @@ -268,13 +260,8 @@ func TestHostBucket_Delete(t *testing.T) { gotTeams := make(map[string]string) for cid, wantn := range tc.want { ipb := tc.b.buck[cid] - teams := ipb.GetTeams() - if len(teams) != wantn { - t.Errorf("HostBucket.DeleteNode(): incorrent number of gotIps in user bucket want=%d, got=%d", - wantn, len(teams), - ) - } - for k, v := range teams { + require.Len(t, ipb.Teams, wantn) + for k, v := range ipb.Teams { gotTeams[k] = v } } diff --git a/pkg/pubsub/pubsub.go b/pkg/pubsub/pubsub.go index 3c5d1dc..4eee937 100644 --- a/pkg/pubsub/pubsub.go +++ b/pkg/pubsub/pubsub.go @@ -4,47 +4,36 @@ import ( "sync" ) -type PubSub interface { - Publish(string, interface{}) - Subscribe(string, MessageHandler) Subscription - Unsubscribe(Subscription) -} - -type pubsub struct { - subs map[string]map[string]Subscription +type PubSub[T any] struct { + subs map[string]*Subscription[T] mu sync.RWMutex } -func NewPubSub() PubSub { - return &pubsub{subs: make(map[string]map[string]Subscription)} +func NewPubSub[T any]() *PubSub[T] { + return &PubSub[T]{ + subs: make(map[string]*Subscription[T]), + } } -func (ps *pubsub) Publish(channel string, msg interface{}) { +func (ps *PubSub[T]) Publish(msg T) { ps.mu.RLock() defer ps.mu.RUnlock() - for _, sub := range ps.subs[channel] { + for _, sub := range ps.subs { sub.Push(msg) } } -func (ps *pubsub) Subscribe(channel string, onMsg MessageHandler) Subscription { +func (ps *PubSub[T]) Subscribe(onMsg MessageHandler[T]) *Subscription[T] { ps.mu.Lock() defer ps.mu.Unlock() - sub := NewSubscription(channel, onMsg) - if _, ok := ps.subs[channel]; !ok { - ps.subs[channel] = make(map[string]Subscription, 1) - } - ps.subs[channel][sub.GetID()] = sub + sub := NewSubscription(onMsg) + ps.subs[sub.GetID()] = sub return sub } -func (ps *pubsub) Unsubscribe(sub Subscription) { +func (ps *PubSub[T]) Unsubscribe(sub *Subscription[T]) { ps.mu.Lock() defer ps.mu.Unlock() - - id, channel := sub.GetID(), sub.GetChannel() - if chSubs, ok := ps.subs[channel]; ok { - delete(chSubs, id) - } + delete(ps.subs, sub.GetID()) } diff --git a/pkg/pubsub/pubsub_test.go b/pkg/pubsub/pubsub_test.go index ec25482..619f2f7 100644 --- a/pkg/pubsub/pubsub_test.go +++ b/pkg/pubsub/pubsub_test.go @@ -16,12 +16,12 @@ func TestMain(m *testing.M) { } func TestPubSub_single(t *testing.T) { - p := NewPubSub() + p := NewPubSub[string]() wg := sync.WaitGroup{} wg.Add(1) - sub := p.Subscribe("single", func(msg interface{}) error { + sub := p.Subscribe(func(msg string) error { require.Equal(t, "blah-blah", msg) wg.Done() return nil @@ -30,17 +30,17 @@ func TestPubSub_single(t *testing.T) { defer cancel() go sub.Run(ctx) - p.Publish("single", "blah-blah") + p.Publish("blah-blah") wg.Wait() } func TestPubSub_nonBlockPublish(t *testing.T) { - p := NewPubSub() + p := NewPubSub[string]() wg := sync.WaitGroup{} wg.Add(11) - sub := p.Subscribe("non-block", func(msg interface{}) error { + sub := p.Subscribe(func(msg string) error { require.Equal(t, "pew-pew", msg) wg.Done() return nil @@ -52,7 +52,7 @@ func TestPubSub_nonBlockPublish(t *testing.T) { done := make(chan struct{}) go func() { for i := 0; i < 11; i++ { - p.Publish("non-block", "pew-pew") + p.Publish("pew-pew") } close(done) }() @@ -66,45 +66,15 @@ func TestPubSub_nonBlockPublish(t *testing.T) { wg.Wait() } -func TestPubSub_multipleSubjects(t *testing.T) { - p := NewPubSub() - - wg := sync.WaitGroup{} - wg.Add(2) - - sub1 := p.Subscribe("sub1", func(msg interface{}) error { - require.Equal(t, "blah-blah-1", msg) - wg.Done() - return nil - }) - ctx1, cancel1 := context.WithCancel(context.Background()) - defer cancel1() - go sub1.Run(ctx1) - - sub2 := p.Subscribe("sub2", func(msg interface{}) error { - require.Equal(t, "blah-blah-2", msg) - wg.Done() - return nil - }) - ctx2, cancel2 := context.WithCancel(context.Background()) - defer cancel2() - go sub2.Run(ctx2) - - p.Publish("sub1", "blah-blah-1") - p.Publish("sub2", "blah-blah-2") - - wg.Wait() -} - func TestPubSub_multipleSubscribers(t *testing.T) { - p := NewPubSub() + p := NewPubSub[string]() wgFirst := sync.WaitGroup{} wgFirst.Add(1) wgSecond := sync.WaitGroup{} wgSecond.Add(1) - sub1 := p.Subscribe("multiple", func(msg interface{}) error { + sub1 := p.Subscribe(func(msg string) error { require.Equal(t, "blah", msg) wgFirst.Done() return nil @@ -113,7 +83,7 @@ func TestPubSub_multipleSubscribers(t *testing.T) { defer cancel1() go sub1.Run(ctx1) - sub2 := p.Subscribe("multiple", func(msg interface{}) error { + sub2 := p.Subscribe(func(msg string) error { require.Equal(t, "blah", msg) wgSecond.Done() return nil @@ -122,14 +92,14 @@ func TestPubSub_multipleSubscribers(t *testing.T) { defer cancel2() go sub2.Run(ctx2) - p.Publish("multiple", "blah") + p.Publish("blah") wgFirst.Wait() wgSecond.Wait() } func TestPubSub_slowpoke(t *testing.T) { - p := NewPubSub() + p := NewPubSub[string]() const samples = 100 @@ -141,7 +111,7 @@ func TestPubSub_slowpoke(t *testing.T) { wgSlow.Wait() }() - slowSub := p.Subscribe("slowpoke", func(msg interface{}) error { + slowSub := p.Subscribe(func(msg string) error { defer wgSlow.Done() select { @@ -157,7 +127,7 @@ func TestPubSub_slowpoke(t *testing.T) { fastWg := sync.WaitGroup{} fastWg.Add(samples) - fastSub := p.Subscribe("slowpoke", func(msg interface{}) error { + fastSub := p.Subscribe(func(msg string) error { require.Equal(t, "pew-pew", msg) fastWg.Done() return nil @@ -167,7 +137,7 @@ func TestPubSub_slowpoke(t *testing.T) { go fastSub.Run(fastCtx) for i := 0; i < samples; i++ { - p.Publish("slowpoke", "pew-pew") + p.Publish("pew-pew") } done := make(chan struct{}) @@ -185,9 +155,9 @@ func TestPubSub_slowpoke(t *testing.T) { } func TestPubSub_unsubscribe(t *testing.T) { - p := NewPubSub() + p := NewPubSub[string]() - sub1 := p.Subscribe("unsubscribe", func(msg interface{}) error { + sub1 := p.Subscribe(func(msg string) error { t.Error("first subscriber must not be called") return nil }) @@ -200,7 +170,7 @@ func TestPubSub_unsubscribe(t *testing.T) { wg := sync.WaitGroup{} wg.Add(1) - sub2 := p.Subscribe("unsubscribe", func(msg interface{}) error { + sub2 := p.Subscribe(func(msg string) error { require.Equal(t, "pew-pew", msg) wg.Done() return nil @@ -209,18 +179,18 @@ func TestPubSub_unsubscribe(t *testing.T) { defer cancel2() go sub2.Run(ctx2) - p.Publish("unsubscribe", "pew-pew") + p.Publish("pew-pew") wg.Wait() } func TestPubSub_sequencePublishers(t *testing.T) { - p := NewPubSub() + p := NewPubSub[string]() wg := sync.WaitGroup{} wg.Add(10) - sub := p.Subscribe("topic", func(msg interface{}) error { + sub := p.Subscribe(func(msg string) error { require.Equal(t, "pew-pew", msg) wg.Done() return nil @@ -230,19 +200,19 @@ func TestPubSub_sequencePublishers(t *testing.T) { go sub.Run(ctx) for i := 0; i < 10; i++ { - p.Publish("topic", "pew-pew") + p.Publish("pew-pew") } wg.Wait() } func TestPubSub_concurrentPublishers(t *testing.T) { - p := NewPubSub() + p := NewPubSub[string]() wg := sync.WaitGroup{} wg.Add(10) - sub := p.Subscribe("topic", func(msg interface{}) error { + sub := p.Subscribe(func(msg string) error { require.Equal(t, "pew-pew", msg) wg.Done() return nil @@ -252,14 +222,14 @@ func TestPubSub_concurrentPublishers(t *testing.T) { go sub.Run(ctx) for i := 0; i < 10; i++ { - go p.Publish("topic", "pew-pew") + go p.Publish("pew-pew") } wg.Wait() } func TestPubSub_msgOrder(t *testing.T) { - p := NewPubSub() + p := NewPubSub[uint64]() wg := sync.WaitGroup{} wg.Add(15) @@ -269,7 +239,7 @@ func TestPubSub_msgOrder(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() - sub := p.Subscribe("topic", func(msg interface{}) error { + sub := p.Subscribe(func(msg uint64) error { expected := atomic.AddUint64(&c, 1) require.Equal(t, expected, msg) wg.Done() @@ -280,7 +250,7 @@ func TestPubSub_msgOrder(t *testing.T) { for i := uint64(1); i < 11; i++ { if i == 6 { c := uint64(5) - sub := p.Subscribe("topic", func(msg interface{}) error { + sub := p.Subscribe(func(msg uint64) error { expected := atomic.AddUint64(&c, 1) require.Equal(t, expected, msg) wg.Done() @@ -289,7 +259,7 @@ func TestPubSub_msgOrder(t *testing.T) { go sub.Run(ctx) } - p.Publish("topic", i) + p.Publish(i) } wg.Wait() diff --git a/pkg/pubsub/subscription.go b/pkg/pubsub/subscription.go index 43cf2c4..e3d7056 100644 --- a/pkg/pubsub/subscription.go +++ b/pkg/pubsub/subscription.go @@ -9,36 +9,27 @@ import ( "github.com/sirupsen/logrus" ) -type MessageHandler func(interface{}) error +type MessageHandler[T any] func(T) error -type Subscription interface { - Run(ctx context.Context) - Push(interface{}) - GetID() string - GetChannel() string +type Subscription[T any] struct { + id string + queue []T + mu sync.Mutex + notify chan struct{} + h MessageHandler[T] } -type subscription struct { - id string - channel string - queue []interface{} - mu sync.Mutex - notify chan struct{} - h MessageHandler -} - -func NewSubscription(channel string, onMsg MessageHandler) Subscription { +func NewSubscription[T any](onMsg MessageHandler[T]) *Subscription[T] { id := uuid.NewString() - return &subscription{ - id: id, - channel: channel, - queue: nil, - notify: make(chan struct{}, 1), - h: onMsg, + return &Subscription[T]{ + id: id, + queue: nil, + notify: make(chan struct{}, 1), + h: onMsg, } } -func (s *subscription) Run(ctx context.Context) { +func (s *Subscription[T]) Run(ctx context.Context) { for { select { case _, ok := <-s.notify: @@ -67,7 +58,7 @@ func (s *subscription) Run(ctx context.Context) { } } -func (s *subscription) Push(msg interface{}) { +func (s *Subscription[T]) Push(msg T) { s.mu.Lock() s.queue = append(s.queue, msg) s.mu.Unlock() @@ -78,10 +69,6 @@ func (s *subscription) Push(msg interface{}) { } } -func (s *subscription) GetID() string { +func (s *Subscription[T]) GetID() string { return s.id } - -func (s *subscription) GetChannel() string { - return s.channel -} diff --git a/pkg/pubsub/subscription_test.go b/pkg/pubsub/subscription_test.go index 95b4bff..3000a05 100644 --- a/pkg/pubsub/subscription_test.go +++ b/pkg/pubsub/subscription_test.go @@ -2,56 +2,42 @@ package pubsub import ( "context" - "reflect" "testing" "time" + + "github.com/stretchr/testify/require" ) func TestNewSubscription(t *testing.T) { - const channel = "test" - sub := NewSubscription(channel, nil) - if sub.GetChannel() != channel { - t.Errorf("NewSubscription(): invalid channel, expected %s, got %s", channel, sub.GetChannel()) - } - - newSub := NewSubscription(channel, nil) - if sub.GetID() == newSub.GetID() { - t.Errorf("NewSubscription(): subscription ID is not random") - } + sub := NewSubscription[int](nil) + newSub := NewSubscription[int](nil) + require.NotEqual(t, sub.GetID(), newSub.GetID(), "id is not random") } func Test_subscription_Push(t *testing.T) { - sub := &subscription{ - id: "test", - channel: "test", - queue: nil, - notify: make(chan struct{}), - h: nil, + sub := &Subscription[string]{ + id: "test", + queue: nil, + notify: make(chan struct{}), + h: nil, } sub.Push("test") - if need := []interface{}{"test"}; !reflect.DeepEqual(sub.queue, need) { - t.Errorf("Push(): invalid queue state: expected %+v, got %+v", need, sub.queue) - } + require.Equal(t, []string{"test"}, sub.queue) } func Test_subscription_Run(t *testing.T) { var received string signal := make(chan struct{}) - handler := func(msg interface{}) error { - cmd, ok := msg.(string) - if !ok { - t.Errorf("Invalid message passed to handler: %v", msg) - } - received = cmd + handler := func(msg string) error { + received = msg close(signal) return nil } - sub := &subscription{ - id: "test", - channel: "test", - queue: nil, - notify: make(chan struct{}, 1), - h: handler, + sub := &Subscription[string]{ + id: "test", + queue: nil, + notify: make(chan struct{}, 1), + h: handler, } ctx, cancel := context.WithCancel(context.Background()) @@ -68,7 +54,5 @@ func Test_subscription_Run(t *testing.T) { t.Errorf("Handler was not called in time") } - if received != msg { - t.Errorf("Received incorrect command: expected = %v, actual = %v", msg, received) - } + require.Equal(t, msg, received) } diff --git a/pkg/rendezvous/rendezvous_test.go b/pkg/rendezvous/rendezvous_test.go index 54ff8b9..e35ffb0 100644 --- a/pkg/rendezvous/rendezvous_test.go +++ b/pkg/rendezvous/rendezvous_test.go @@ -2,6 +2,8 @@ package rendezvous import ( "testing" + + "github.com/stretchr/testify/require" ) func TestRendezvous_Calculate(t *testing.T) { @@ -28,14 +30,9 @@ func TestRendezvous_Calculate(t *testing.T) { k := combineKey(tt.nodeID, tt.value) rhash, ok := r.checkCache(k) - if !ok { - t.Error("Key is not cached after add") - } else { - needHash := weightHash(rhash, tt.nodeWeight) - if needHash != hash { - t.Errorf("Cached %f, but returned %f", needHash, hash) - } - } + require.True(t, ok) + needHash := weightHash(rhash, tt.nodeWeight) + require.Equal(t, needHash, hash) }) } } diff --git a/pkg/tasklogger/logger.go b/pkg/tasklogger/logger.go index ad60a1d..c51b836 100644 --- a/pkg/tasklogger/logger.go +++ b/pkg/tasklogger/logger.go @@ -8,6 +8,9 @@ import ( neopb "neo/lib/genproto/neo" ) +// 1 MB. +const maxMessageLength = 1024 * 1024 + func New(exploit string, version int64, team string, sender Sender) *TaskLogger { return &TaskLogger{ exploit: exploit, @@ -52,7 +55,7 @@ func (l *TaskLogger) newLine(msg, level string) *neopb.LogLine { return &neopb.LogLine{ Exploit: l.exploit, Version: l.version, - Message: msg, + Message: sanitizeMessage(msg), Level: level, Team: l.team, } @@ -65,3 +68,10 @@ func (l *TaskLogger) getLogger() *logrus.Entry { "team": l.team, }) } + +func sanitizeMessage(msg string) string { + if len(msg) > maxMessageLength { + msg = msg[:maxMessageLength] + } + return msg +} diff --git a/pkg/testutils/testutils.go b/pkg/testutils/testutils.go index 7995a84..722a224 100644 --- a/pkg/testutils/testutils.go +++ b/pkg/testutils/testutils.go @@ -14,8 +14,7 @@ import ( const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" -var seededRand = rand.New( - rand.NewSource(time.Now().UnixNano())) +var seededRand = rand.New(rand.NewSource(1337)) func LessString(v1, v2 string) bool { return v1 < v2