Scanning tool that takes a list of domains from a text file and verifies if they match a regular expression. It run in parallel with many works so is very performant.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

107 lines
2.9 KiB

package cmd
import (
"bufio"
"fmt"
"log"
"os"
"github.com/spf13/cobra"
"shimon/internal/shimon"
)
var (
domainsPath string
outFile string
prefix string
regexp string
suffix string
timeout int
ulimit int64
workers int
rootCmd = &cobra.Command{
Use: "shimon",
Short: "Load a file containing domains then return a list of domains matching a provided regular expression",
Run: rootRun,
}
)
// Initialize flags for cobra
func init() {
rootCmd.PersistentFlags().StringVarP(&domainsPath, "domains-file", "f", "", "Path to the input file containing domains (Example: './domains.txt')")
rootCmd.MarkPersistentFlagRequired("file")
rootCmd.PersistentFlags().StringVarP(&outFile, "output-file", "o", "results.txt", "File to output matching domains to")
rootCmd.PersistentFlags().StringVarP(&prefix, "prefix", "p", "https://", "Prefix for each domain")
rootCmd.PersistentFlags().StringVarP(&regexp, "regexp", "r", "", "Regular expression for fingerprinting (Example '[g|G]oogle'")
rootCmd.MarkPersistentFlagRequired("regexp")
rootCmd.PersistentFlags().StringVarP(&suffix, "suffix", "s", "/", "Suffix for each domain (Example: '/index/login.php'")
rootCmd.PersistentFlags().IntVarP(&timeout, "timeout", "t", 30, "Timeout (in seconds) for each GET request")
rootCmd.PersistentFlags().Int64VarP(&ulimit, "ulimit", "u", 1024, "Maximum number of open files allowed by 'ulimit -n'")
rootCmd.PersistentFlags().IntVarP(&workers, "workers", "w", 1000, "Number of workers to create")
}
// Load all domains (separated by newline) from a file into a slice of strings
func loadDomains(filePath string) ([]string, error) {
file, err := os.Open(filePath)
if err != nil {
return []string{}, err
}
defer file.Close() // Ensure files is closed at the end of the function
domains := []string{}
buf := bufio.NewScanner(file)
for buf.Scan() {
domains = append(domains, buf.Text())
}
if err := buf.Err(); err != nil {
return []string{}, err
}
return domains, nil
}
// Function for the root command (shimon)
func rootRun(cmd *cobra.Command, args []string) {
domains, err := loadDomains(domainsPath)
if err != nil {
log.Fatalf(err.Error())
}
shimon := shimon.ShiMon{
Domains: domains,
Prefix: prefix,
RegExp: regexp,
Suffix: suffix,
Timeout: timeout,
Ulimit: ulimit,
Workers: workers,
}
// Create results file for writing
resultsFile, err := os.Create(outFile)
if err != nil {
log.Fatal(err)
}
// Create a buffered writer to write to this file (allows for partial writes)
writer := bufio.NewWriter(resultsFile)
// Start scan and receive matches then perform a hard exit if it errors
matches, err := shimon.Scan(writer)
if err != nil {
log.Fatalf(err.Error())
}
fmt.Println("\nResults:")
for _, match := range matches {
fmt.Println(" ", match)
}
fmt.Println("\nSaving results to:", outFile)
}
// Execute executes the root command.
func Execute() error {
return rootCmd.Execute()
}