From 9f05d3cbb331887446fea64b351e2181e6665b5d Mon Sep 17 00:00:00 2001 From: Frank Li <51806469+frankhli843@users.noreply.github.com> Date: Sun, 19 Mar 2023 11:37:30 -0400 Subject: [PATCH 1/3] Adding initial files with test case --- Shifud/devices.yml | 3 ++ Shifud/go.mod | 12 +++++ Shifud/go.sum | 20 ++++++++ Shifud/src/main.go | 98 ++++++++++++++++++++++++++++++++++++++ Shifud/src/scanner_test.go | 74 ++++++++++++++++++++++++++++ devices.yml | 3 ++ 6 files changed, 210 insertions(+) create mode 100644 Shifud/devices.yml create mode 100644 Shifud/go.mod create mode 100644 Shifud/go.sum create mode 100644 Shifud/src/main.go create mode 100644 Shifud/src/scanner_test.go create mode 100644 devices.yml diff --git a/Shifud/devices.yml b/Shifud/devices.yml new file mode 100644 index 000000000..047976a14 --- /dev/null +++ b/Shifud/devices.yml @@ -0,0 +1,3 @@ +- IP: 192.168.2.24 + Option: default + Port: 8080 diff --git a/Shifud/go.mod b/Shifud/go.mod new file mode 100644 index 000000000..ad3c6315d --- /dev/null +++ b/Shifud/go.mod @@ -0,0 +1,12 @@ +module example.com/Shifud + +go 1.17 + +require ( + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/stretchr/testify v1.8.2 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect + sigs.k8s.io/yaml v1.3.0 // indirect +) diff --git a/Shifud/go.sum b/Shifud/go.sum new file mode 100644 index 000000000..b09ce3d27 --- /dev/null +++ b/Shifud/go.sum @@ -0,0 +1,20 @@ +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= +github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= +sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= diff --git a/Shifud/src/main.go b/Shifud/src/main.go new file mode 100644 index 000000000..f4d836ba3 --- /dev/null +++ b/Shifud/src/main.go @@ -0,0 +1,98 @@ +package main + +import ( + "fmt" + "net" + "os" + + "sigs.k8s.io/yaml" +) + +type DeviceConfig struct { + IP string `yaml:"ip"` + Port int `yaml:"port"` + Option string `yaml:"option"` +} + +type ScannerPlugin interface { + Scan() ([]DeviceConfig, error) +} + +type LocalNetworkScanner struct{} + +func (s *LocalNetworkScanner) Scan() ([]DeviceConfig, error) { + var devices []DeviceConfig + + addrs, err := net.InterfaceAddrs() + if err != nil { + return nil, err + } + + for _, addr := range addrs { + ip, ok := addr.(*net.IPNet) + if ok && !ip.IP.IsLoopback() && ip.IP.To4() != nil { + config := DeviceConfig{ + IP: ip.IP.String(), + Port: 8080, + Option: "default", + } + devices = append(devices, config) + } + } + + return devices, nil +} + +type Scanner struct { + plugins []ScannerPlugin +} + +func (s *Scanner) AddPlugin(p ScannerPlugin) { + s.plugins = append(s.plugins, p) +} + +func (s *Scanner) Scan() ([]DeviceConfig, error) { + var devices []DeviceConfig + + // Scan with each plugin + for _, p := range s.plugins { + ds, err := p.Scan() + if err != nil { + return nil, err + } + devices = append(devices, ds...) + } + + return devices, nil +} + +func main() { + fmt.Println("Shifud online...") + // Create a scanner with the default plugin + scanner := &Scanner{} + scanner.AddPlugin(&LocalNetworkScanner{}) + + // Add plugins for other scanning locations, e.g.: + // scanner.AddPlugin(&HttpNetworkScanner{}) + // scanner.AddPlugin(&UsbScanner{}) + + // Scan devices with all plugins + devices, err := scanner.Scan() + if err != nil { + fmt.Println("Error:", err) + os.Exit(1) + } + + // Export the configuration settings to a YAML file + output, err := yaml.Marshal(devices) + if err != nil { + fmt.Println("Error:", err) + os.Exit(1) + } + + err = os.WriteFile("devices.yml", output, 0644) + if err != nil { + fmt.Println("Error:", err) + os.Exit(1) + } +} diff --git a/Shifud/src/scanner_test.go b/Shifud/src/scanner_test.go new file mode 100644 index 000000000..4aafd39ac --- /dev/null +++ b/Shifud/src/scanner_test.go @@ -0,0 +1,74 @@ +package main + +import ( + "github.com/stretchr/testify/assert" + "testing" +) + +func TestScanner(t *testing.T) { + scanner := &Scanner{} + scanner.AddPlugin(&LocalNetworkScannerMock{}) + + devices, err := scanner.Scan() + assert.NoError(t, err) + assert.NotNil(t, devices) + assert.Len(t, devices, 2) + + expectedDevices := []DeviceConfig{ + { + IP: "192.168.1.2", + Port: 8080, + Option: "default", + }, + { + IP: "192.168.1.3", + Port: 8080, + Option: "default", + }, + } + + assert.ElementsMatch(t, expectedDevices, devices) +} + +type LocalNetworkScannerMock struct{} + +func (s *LocalNetworkScannerMock) Scan() ([]DeviceConfig, error) { + devices := []DeviceConfig{ + { + IP: "192.168.1.2", + Port: 8080, + Option: "default", + }, + { + IP: "192.168.1.3", + Port: 8080, + Option: "default", + }, + } + + return devices, nil +} + +func TestLocalNetworkScanner(t *testing.T) { + scanner := &LocalNetworkScanner{} + devices, err := scanner.Scan() + + assert.NoError(t, err) + assert.NotNil(t, devices) + assert.Len(t, devices, 2) + + expectedDevices := []DeviceConfig{ + { + IP: "192.168.1.2", + Port: 8080, + Option: "default", + }, + { + IP: "192.168.1.3", + Port: 8080, + Option: "default", + }, + } + + assert.ElementsMatch(t, expectedDevices, devices) +} diff --git a/devices.yml b/devices.yml new file mode 100644 index 000000000..047976a14 --- /dev/null +++ b/devices.yml @@ -0,0 +1,3 @@ +- IP: 192.168.2.24 + Option: default + Port: 8080 From add6e6da62cb27f4df383ade7f36cf5c3dfd499a Mon Sep 17 00:00:00 2001 From: Frank Li <51806469+frankhli843@users.noreply.github.com> Date: Sun, 19 Mar 2023 15:43:16 -0400 Subject: [PATCH 2/3] Adding plugin examples --- Shifud/ReadMe.md | 65 ++++++++++++++++++ Shifud/main.go | 43 ++++++++++++ Shifud/plugins/basewebscanner.go | 36 ++++++++++ Shifud/plugins/httpscanner.go | 37 ++++++++++ Shifud/plugins/httpsscanner.go | 33 +++++++++ Shifud/shifud/shifud.go | 70 +++++++++++++++++++ Shifud/src/main.go | 98 --------------------------- Shifud/{src => tests}/scanner_test.go | 35 ++-------- 8 files changed, 290 insertions(+), 127 deletions(-) create mode 100644 Shifud/ReadMe.md create mode 100644 Shifud/main.go create mode 100644 Shifud/plugins/basewebscanner.go create mode 100644 Shifud/plugins/httpscanner.go create mode 100644 Shifud/plugins/httpsscanner.go create mode 100644 Shifud/shifud/shifud.go delete mode 100644 Shifud/src/main.go rename Shifud/{src => tests}/scanner_test.go (52%) diff --git a/Shifud/ReadMe.md b/Shifud/ReadMe.md new file mode 100644 index 000000000..82d8063c9 --- /dev/null +++ b/Shifud/ReadMe.md @@ -0,0 +1,65 @@ +# Shifud Plugin Development Guide + +Shifud is a flexible network scanning tool with an extensible plugin system. This guide will help you create and integrate a new plugin into the Shifud system. +## Understanding the Plugin System + +Shifud uses Go's `plugin` package to dynamically load plugins at runtime. Plugins are compiled as shared libraries (`.so` files) and loaded from the `plugins` folder. Each plugin must implement the `ScannerPlugin` interface, which is defined in the `shifud.go` file: + +```go +type ScannerPlugin interface { + Scan() ([]DeviceConfig, error) +} +``` + + +## Creating a New Plugin +1. ** folder.** Name the file based on your plugin's functionality, e.g., `http_scanner.go`. +2. ** interface.** For example: + +```go +package plugins + +import ( + "example.com/Shifud/shifud" + "fmt" +) + +type HttpScanner struct{} + +func (s *HttpScanner) Scan() ([]shifud.DeviceConfig, error) { + fmt.Println("Scanning with HTTP scanner...") + // Add your plugin logic here + return nil, nil +} + +var ScannerPlugin HttpScanner +``` + + +1. ** function.** This is where you'll add the core functionality of your plugin. The `Scan()` function should return a slice of `DeviceConfig` objects and an error if something goes wrong. +## Compiling and Loading the Plugin +1. **Compile the plugin as a shared library.** Use the following command to compile your plugin: + +```bash +go build -buildmode=plugin -o plugins/*.so plugins/*.go +``` + + + +Replace `your_plugin_name` with the appropriate name for your plugin. +1. **file.** The `LoadPlugins` function in the `shifud.go` file handles the loading of plugins. It is called in the `main.go` file as follows: + +```go +err := shifud.LoadPlugins(scanner, "plugins") +if err != nil { + fmt.Println("Error loading plugins:", err) + os.Exit(1) +} +``` + + + +With these steps, your new plugin will be loaded and used by Shifud at runtime. You can create additional plugins by following the same process, and Shifud will automatically load and use them. +## Testing Your Plugin + +After creating and loading your plugin, run the `main.go` file to test your plugin's functionality. If everything is set up correctly, you should see the output of your plugin's `Scan()` function when Shifud runs. \ No newline at end of file diff --git a/Shifud/main.go b/Shifud/main.go new file mode 100644 index 000000000..72dcba8c6 --- /dev/null +++ b/Shifud/main.go @@ -0,0 +1,43 @@ +package main + +import ( + "example.com/Shifud/shifud" + "fmt" + _ "net" + "os" + "sigs.k8s.io/yaml" +) + +func main() { + fmt.Println("Shifud online...") + + // Create a scanner + scanner := &shifud.Scanner{} + + // Load plugins from the plugins folder + err := shifud.LoadPlugins(scanner, "plugins") + if err != nil { + fmt.Println("Error loading plugins:", err) + os.Exit(1) + } + + // Scan devices with all plugins + devices, err := scanner.Scan() + if err != nil { + fmt.Println("Error:", err) + os.Exit(1) + } + + // Export the configuration settings to a YAML file + output, err := yaml.Marshal(devices) + if err != nil { + fmt.Println("Error:", err) + os.Exit(1) + } + + err = os.WriteFile("devices.yml", output, 0644) + if err != nil { + fmt.Println("Error:", err) + os.Exit(1) + } +} diff --git a/Shifud/plugins/basewebscanner.go b/Shifud/plugins/basewebscanner.go new file mode 100644 index 000000000..d8c6c6260 --- /dev/null +++ b/Shifud/plugins/basewebscanner.go @@ -0,0 +1,36 @@ +package plugins + +import ( + "example.com/Shifud/shifud" + "fmt" + "sync" + "time" +) + +type ProtocolHandler func(ip string, port int, timeout time.Duration) bool + +func WebScanner(startPort, endPort int, timeout time.Duration, handler ProtocolHandler) []shifud.DeviceConfig { + var devices []shifud.DeviceConfig + var wg sync.WaitGroup + + for i := 1; i <= 254; i++ { + ip := fmt.Sprintf("192.168.1.%d", i) + wg.Add(1) + + go func(ip string) { + defer wg.Done() + + for port := startPort; port <= endPort; port++ { + if handler(ip, port, timeout) { + devices = append(devices, shifud.DeviceConfig{ + IP: ip, + Port: port, + }) + } + } + }(ip) + } + + wg.Wait() + return devices +} diff --git a/Shifud/plugins/httpscanner.go b/Shifud/plugins/httpscanner.go new file mode 100644 index 000000000..e4801dab1 --- /dev/null +++ b/Shifud/plugins/httpscanner.go @@ -0,0 +1,37 @@ +package plugins + +import ( + "example.com/Shifud/shifud" + "net" + "net/http" + "strconv" + "time" +) + +type HttpScanner struct{} + +func (s *HttpScanner) Scan() ([]shifud.DeviceConfig, error) { + startPort := 80 + endPort := 10000 + timeout := 5 * time.Second + handler := httpHandler + devices := WebScanner(startPort, endPort, timeout, handler) + return devices, nil +} + +func httpHandler(ip string, port int, timeout time.Duration) bool { + address := ip + ":" + strconv.Itoa(port) + conn, err := net.DialTimeout("tcp", address, timeout) + if err == nil { + defer conn.Close() + + // Check if it's an HTTP server + _, err := http.Get("http://" + address) + if err == nil { + return true + } + } + return false +} + +var ScannerPlugin HttpScanner diff --git a/Shifud/plugins/httpsscanner.go b/Shifud/plugins/httpsscanner.go new file mode 100644 index 000000000..fdbabb762 --- /dev/null +++ b/Shifud/plugins/httpsscanner.go @@ -0,0 +1,33 @@ +package plugins + +import ( + "crypto/tls" + "example.com/Shifud/shifud" + "net" + "strconv" + "time" +) + +type HttpsScanner struct{} + +func (s *HttpsScanner) Scan() ([]shifud.DeviceConfig, error) { + startPort := 80 + endPort := 10000 + timeout := 5 * time.Second + handler := httpsHandler + devices := WebScanner(startPort, endPort, timeout, handler) + return devices, nil +} + +func httpsHandler(ip string, port int, timeout time.Duration) bool { + address := ip + ":" + strconv.Itoa(port) + dialer := &net.Dialer{Timeout: timeout} + conn, err := tls.DialWithDialer(dialer, "tcp", address, &tls.Config{InsecureSkipVerify: true}) + if err == nil { + defer conn.Close() + return true + } + return false +} + +var ScannerPlugin HttpsScanner diff --git a/Shifud/shifud/shifud.go b/Shifud/shifud/shifud.go new file mode 100644 index 000000000..2ace9b9f1 --- /dev/null +++ b/Shifud/shifud/shifud.go @@ -0,0 +1,70 @@ +package shifud + +import ( + "fmt" + "io/ioutil" + "plugin" +) + +type DeviceConfig struct { + IP string `yaml:"ip"` + Port int `yaml:"port"` + Option string `yaml:"option"` +} + +type ScannerPlugin interface { + Scan() ([]DeviceConfig, error) +} + +type Scanner struct { + plugins []ScannerPlugin +} + +func (s *Scanner) AddPlugin(p ScannerPlugin) { + s.plugins = append(s.plugins, p) +} + +func (s *Scanner) Scan() ([]DeviceConfig, error) { + var devices []DeviceConfig + + // Scan with each plugin + for _, p := range s.plugins { + ds, err := p.Scan() + if err != nil { + return nil, err + } + devices = append(devices, ds...) + } + + return devices, nil +} + +func LoadPlugins(scanner *Scanner, pluginDir string) error { + files, err := ioutil.ReadDir(pluginDir) + if err != nil { + return err + } + + for _, file := range files { + if !file.IsDir() && file.Name()[len(file.Name())-3:] == ".so" { + p, err := plugin.Open(pluginDir + "/" + file.Name()) + if err != nil { + return err + } + + symScannerPlugin, err := p.Lookup("ScannerPlugin") + if err != nil { + return err + } + + scannerPlugin, ok := symScannerPlugin.(ScannerPlugin) + if !ok { + return fmt.Errorf("unexpected type from module symbol") + } + + scanner.AddPlugin(scannerPlugin) + } + } + + return nil +} diff --git a/Shifud/src/main.go b/Shifud/src/main.go deleted file mode 100644 index f4d836ba3..000000000 --- a/Shifud/src/main.go +++ /dev/null @@ -1,98 +0,0 @@ -package main - -import ( - "fmt" - "net" - "os" - - "sigs.k8s.io/yaml" -) - -type DeviceConfig struct { - IP string `yaml:"ip"` - Port int `yaml:"port"` - Option string `yaml:"option"` -} - -type ScannerPlugin interface { - Scan() ([]DeviceConfig, error) -} - -type LocalNetworkScanner struct{} - -func (s *LocalNetworkScanner) Scan() ([]DeviceConfig, error) { - var devices []DeviceConfig - - addrs, err := net.InterfaceAddrs() - if err != nil { - return nil, err - } - - for _, addr := range addrs { - ip, ok := addr.(*net.IPNet) - if ok && !ip.IP.IsLoopback() && ip.IP.To4() != nil { - config := DeviceConfig{ - IP: ip.IP.String(), - Port: 8080, - Option: "default", - } - devices = append(devices, config) - } - } - - return devices, nil -} - -type Scanner struct { - plugins []ScannerPlugin -} - -func (s *Scanner) AddPlugin(p ScannerPlugin) { - s.plugins = append(s.plugins, p) -} - -func (s *Scanner) Scan() ([]DeviceConfig, error) { - var devices []DeviceConfig - - // Scan with each plugin - for _, p := range s.plugins { - ds, err := p.Scan() - if err != nil { - return nil, err - } - devices = append(devices, ds...) - } - - return devices, nil -} - -func main() { - fmt.Println("Shifud online...") - // Create a scanner with the default plugin - scanner := &Scanner{} - scanner.AddPlugin(&LocalNetworkScanner{}) - - // Add plugins for other scanning locations, e.g.: - // scanner.AddPlugin(&HttpNetworkScanner{}) - // scanner.AddPlugin(&UsbScanner{}) - - // Scan devices with all plugins - devices, err := scanner.Scan() - if err != nil { - fmt.Println("Error:", err) - os.Exit(1) - } - - // Export the configuration settings to a YAML file - output, err := yaml.Marshal(devices) - if err != nil { - fmt.Println("Error:", err) - os.Exit(1) - } - - err = os.WriteFile("devices.yml", output, 0644) - if err != nil { - fmt.Println("Error:", err) - os.Exit(1) - } -} diff --git a/Shifud/src/scanner_test.go b/Shifud/tests/scanner_test.go similarity index 52% rename from Shifud/src/scanner_test.go rename to Shifud/tests/scanner_test.go index 4aafd39ac..694ca6f5f 100644 --- a/Shifud/src/scanner_test.go +++ b/Shifud/tests/scanner_test.go @@ -1,12 +1,13 @@ -package main +package tests import ( + "example.com/Shifud/shifud" "github.com/stretchr/testify/assert" "testing" ) func TestScanner(t *testing.T) { - scanner := &Scanner{} + scanner := &shifud.Scanner{} scanner.AddPlugin(&LocalNetworkScannerMock{}) devices, err := scanner.Scan() @@ -14,7 +15,7 @@ func TestScanner(t *testing.T) { assert.NotNil(t, devices) assert.Len(t, devices, 2) - expectedDevices := []DeviceConfig{ + expectedDevices := []shifud.DeviceConfig{ { IP: "192.168.1.2", Port: 8080, @@ -32,8 +33,8 @@ func TestScanner(t *testing.T) { type LocalNetworkScannerMock struct{} -func (s *LocalNetworkScannerMock) Scan() ([]DeviceConfig, error) { - devices := []DeviceConfig{ +func (s *LocalNetworkScannerMock) Scan() ([]shifud.DeviceConfig, error) { + devices := []shifud.DeviceConfig{ { IP: "192.168.1.2", Port: 8080, @@ -48,27 +49,3 @@ func (s *LocalNetworkScannerMock) Scan() ([]DeviceConfig, error) { return devices, nil } - -func TestLocalNetworkScanner(t *testing.T) { - scanner := &LocalNetworkScanner{} - devices, err := scanner.Scan() - - assert.NoError(t, err) - assert.NotNil(t, devices) - assert.Len(t, devices, 2) - - expectedDevices := []DeviceConfig{ - { - IP: "192.168.1.2", - Port: 8080, - Option: "default", - }, - { - IP: "192.168.1.3", - Port: 8080, - Option: "default", - }, - } - - assert.ElementsMatch(t, expectedDevices, devices) -} From 07ac03a6e5db33cf91fbe9f2b74232eaa84ac2f7 Mon Sep 17 00:00:00 2001 From: Frank Li <51806469+frankhli843@users.noreply.github.com> Date: Sun, 19 Mar 2023 17:59:46 -0400 Subject: [PATCH 3/3] Adding dockerfile and some fun log messages --- Shifud/Dockerfile | 20 +++++++ Shifud/ReadMe.md | 27 +++++++-- Shifud/devices.yml | 4 +- .../basewebscanner.go | 6 +- Shifud/plugins/httpscanner.go | 9 ++- Shifud/plugins/httpsscanner.go | 5 +- Shifud/shifud/shifud.go | 59 ++++++++++++++++--- 7 files changed, 110 insertions(+), 20 deletions(-) create mode 100644 Shifud/Dockerfile rename Shifud/{plugins => plugincommon}/basewebscanner.go (73%) diff --git a/Shifud/Dockerfile b/Shifud/Dockerfile new file mode 100644 index 000000000..7bb6f8a41 --- /dev/null +++ b/Shifud/Dockerfile @@ -0,0 +1,20 @@ +# syntax=docker/dockerfile:1 + +FROM golang:1.19.2-alpine + +WORKDIR /app + +COPY go.mod go.sum ./ + +RUN go mod download + +# Install required packages +RUN apk add --no-cache gcc musl-dev libc-dev + +COPY . . + +RUN for f in plugins/*.go; do go build -buildmode=plugin -o "${f%.go}.so" "$f"; done + +RUN go build -o main main.go + +CMD ["./main"] diff --git a/Shifud/ReadMe.md b/Shifud/ReadMe.md index 82d8063c9..f7eeba4d3 100644 --- a/Shifud/ReadMe.md +++ b/Shifud/ReadMe.md @@ -1,6 +1,19 @@ -# Shifud Plugin Development Guide +# Shifud +## How do I run it locally +```bash +for f in plugins/*.go; do go build -buildmode=plugin -o "${f%.go}.so" "$f"; done +go run main.go +``` + +## Running in docker +```bash +docker build -t shifud . +docker run -p 8080:8080 shifud +``` +## Shifud Plugin Development Guide Shifud is a flexible network scanning tool with an extensible plugin system. This guide will help you create and integrate a new plugin into the Shifud system. + ## Understanding the Plugin System Shifud uses Go's `plugin` package to dynamically load plugins at runtime. Plugins are compiled as shared libraries (`.so` files) and loaded from the `plugins` folder. Each plugin must implement the `ScannerPlugin` interface, which is defined in the `shifud.go` file: @@ -11,7 +24,6 @@ type ScannerPlugin interface { } ``` - ## Creating a New Plugin 1. ** folder.** Name the file based on your plugin's functionality, e.g., `http_scanner.go`. 2. ** interface.** For example: @@ -36,18 +48,23 @@ var ScannerPlugin HttpScanner ``` -1. ** function.** This is where you'll add the core functionality of your plugin. The `Scan()` function should return a slice of `DeviceConfig` objects and an error if something goes wrong. +** function.** This is where you'll add the core functionality of your plugin. The `Scan()` function should return a slice of `DeviceConfig` objects and an error if something goes wrong. ## Compiling and Loading the Plugin -1. **Compile the plugin as a shared library.** Use the following command to compile your plugin: +**Compile the plugin as a shared library.** Use the following command to compile your plugin: ```bash go build -buildmode=plugin -o plugins/*.so plugins/*.go ``` +You can also build all plugins yourself with the following command: +```bash +for f in plugins/*.go; do go build -buildmode=plugin -o "${f%.go}.so" "$f"; done +``` + Replace `your_plugin_name` with the appropriate name for your plugin. -1. **file.** The `LoadPlugins` function in the `shifud.go` file handles the loading of plugins. It is called in the `main.go` file as follows: +**file.** The `LoadPlugins` function in the `shifud.go` file handles the loading of plugins. It is called in the `main.go` file as follows: ```go err := shifud.LoadPlugins(scanner, "plugins") diff --git a/Shifud/devices.yml b/Shifud/devices.yml index 047976a14..19765bd50 100644 --- a/Shifud/devices.yml +++ b/Shifud/devices.yml @@ -1,3 +1 @@ -- IP: 192.168.2.24 - Option: default - Port: 8080 +null diff --git a/Shifud/plugins/basewebscanner.go b/Shifud/plugincommon/basewebscanner.go similarity index 73% rename from Shifud/plugins/basewebscanner.go rename to Shifud/plugincommon/basewebscanner.go index d8c6c6260..a2f3ee593 100644 --- a/Shifud/plugins/basewebscanner.go +++ b/Shifud/plugincommon/basewebscanner.go @@ -1,8 +1,9 @@ -package plugins +package plugincommon import ( "example.com/Shifud/shifud" "fmt" + "log" "sync" "time" ) @@ -13,6 +14,7 @@ func WebScanner(startPort, endPort int, timeout time.Duration, handler ProtocolH var devices []shifud.DeviceConfig var wg sync.WaitGroup + log.Println("Launching our little scanner minions! 🚀") for i := 1; i <= 254; i++ { ip := fmt.Sprintf("192.168.1.%d", i) wg.Add(1) @@ -26,11 +28,13 @@ func WebScanner(startPort, endPort int, timeout time.Duration, handler ProtocolH IP: ip, Port: port, }) + log.Printf("Minion found a device at %s:%d! High five! 🙌\n", ip, port) } } }(ip) } wg.Wait() + log.Println("All minions have returned! Time to count our treasures! 💎") return devices } diff --git a/Shifud/plugins/httpscanner.go b/Shifud/plugins/httpscanner.go index e4801dab1..2645f9b80 100644 --- a/Shifud/plugins/httpscanner.go +++ b/Shifud/plugins/httpscanner.go @@ -1,7 +1,9 @@ -package plugins +package main import ( + "example.com/Shifud/plugincommon" "example.com/Shifud/shifud" + "log" "net" "net/http" "strconv" @@ -15,7 +17,9 @@ func (s *HttpScanner) Scan() ([]shifud.DeviceConfig, error) { endPort := 10000 timeout := 5 * time.Second handler := httpHandler - devices := WebScanner(startPort, endPort, timeout, handler) + log.Println("Scanning for HTTP devices in the wild... Let's see what we find!") + devices := plugincommon.WebScanner(startPort, endPort, timeout, handler) + log.Printf("Finished scanning HTTP devices! Found %d devices. Time to party! 🥳\n", len(devices)) return devices, nil } @@ -28,6 +32,7 @@ func httpHandler(ip string, port int, timeout time.Duration) bool { // Check if it's an HTTP server _, err := http.Get("http://" + address) if err == nil { + log.Printf("HTTP server spotted at %s! 🎯\n", address) return true } } diff --git a/Shifud/plugins/httpsscanner.go b/Shifud/plugins/httpsscanner.go index fdbabb762..29db7c2b4 100644 --- a/Shifud/plugins/httpsscanner.go +++ b/Shifud/plugins/httpsscanner.go @@ -1,7 +1,8 @@ -package plugins +package main import ( "crypto/tls" + "example.com/Shifud/plugincommon" "example.com/Shifud/shifud" "net" "strconv" @@ -15,7 +16,7 @@ func (s *HttpsScanner) Scan() ([]shifud.DeviceConfig, error) { endPort := 10000 timeout := 5 * time.Second handler := httpsHandler - devices := WebScanner(startPort, endPort, timeout, handler) + devices := plugincommon.WebScanner(startPort, endPort, timeout, handler) return devices, nil } diff --git a/Shifud/shifud/shifud.go b/Shifud/shifud/shifud.go index 2ace9b9f1..1196e0d79 100644 --- a/Shifud/shifud/shifud.go +++ b/Shifud/shifud/shifud.go @@ -3,7 +3,12 @@ package shifud import ( "fmt" "io/ioutil" + "os" "plugin" + "sync" + "time" + + "sigs.k8s.io/yaml" ) type DeviceConfig struct { @@ -26,16 +31,50 @@ func (s *Scanner) AddPlugin(p ScannerPlugin) { func (s *Scanner) Scan() ([]DeviceConfig, error) { var devices []DeviceConfig + var mu sync.Mutex + var wg sync.WaitGroup - // Scan with each plugin - for _, p := range s.plugins { - ds, err := p.Scan() - if err != nil { - return nil, err - } - devices = append(devices, ds...) + fmt.Println("Hold on to your hat, we're starting the scanning party! 🎉") + + // Scan with each plugin in parallel + for i, p := range s.plugins { + wg.Add(1) + go func(plugin ScannerPlugin, index int) { + defer wg.Done() + + start := time.Now() + ds, err := plugin.Scan() + duration := time.Since(start) + + if err != nil { + fmt.Printf("Oops! Plugin %d stumbled a bit: %v\n", index, err) + } else { + fmt.Printf("Plugin %d finished scanning in %v! 🚀\n", index, duration) + } + + output, err := yaml.Marshal(ds) + if err != nil { + fmt.Printf("Plugin %d had trouble packing its bags: %v\n", index, err) + } else { + filename := fmt.Sprintf("devices_plugin_%d.yml", index) + err = os.WriteFile(filename, output, 0644) + if err != nil { + fmt.Printf("Plugin %d's suitcase got lost: %v\n", index, err) + } else { + fmt.Printf("Plugin %d's results are ready for you in %s! 📦\n", index, filename) + } + } + + mu.Lock() + devices = append(devices, ds...) + mu.Unlock() + }(p, i) } + wg.Wait() + + fmt.Println("The scanning party is over. Thanks for joining! 🥳") + return devices, nil } @@ -45,6 +84,8 @@ func LoadPlugins(scanner *Scanner, pluginDir string) error { return err } + fmt.Println("Knocking on the plugins' doors... 🚪") + for _, file := range files { if !file.IsDir() && file.Name()[len(file.Name())-3:] == ".so" { p, err := plugin.Open(pluginDir + "/" + file.Name()) @@ -63,8 +104,12 @@ func LoadPlugins(scanner *Scanner, pluginDir string) error { } scanner.AddPlugin(scannerPlugin) + + fmt.Printf("Plugin %s is ready to join the fun! 🎊\n", file.Name()) } } + fmt.Println("All the plugins are on board! Let's get this party started! 🕺") + return nil }