1 Commits

Author SHA1 Message Date
Evan Hosinski 770403419d Attempting to stay under the threshold but still hitting it somehow?
I cannot tell if its me or the dehashed devs at this point.
2025-05-16 17:17:52 -04:00
11 changed files with 305 additions and 449 deletions
-1
View File
@@ -1,3 +1,2 @@
.idea/* .idea/*
build/* build/*
.DS_Store
Binary file not shown.

Before

Width:  |  Height:  |  Size: 211 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 148 KiB

+2 -20
View File
@@ -271,29 +271,11 @@ The logs can be easily queried from the Dehasher CLI.
dehasher logs -l 10 dehasher logs -l 10
# Show logs from the last 24 hours # Show logs from the last 24 hours
dehasher logs -s "last 24 hours" dehasher logs -s "24 hours ago"
# Show logs from the last 24 hours with a severity of error or fatal # Show logs from the last 24 hours with a severity of error or fatal
dehasher logs -s "05-01-2025" -v error,fatal dehasher logs -s "24 hours ago" -v error,fatal
``` ```
### Logs Dates
#### Dehasher utilized 'easy time' to determine the appropriate time for a given query.
![Alt text](.img/easy_time_parsing.png "Easy Time")
#### You may also used dates mixed with easy time to perform queries.
![Alt text](.img/easy_time_query_2.png "Mixed Time")
#### The following formats are supported:
- `last 24 hours`
- `last 2 days`
- `30 minutes ago`
- `45 seconds ago`
- `1 week ago`
- `05-01-2025`
- `05/01/2025`
- `05/01/25`
- `05-01-25`
- `May 01, 2025`
## 🎉 Sample Run ## 🎉 Sample Run
```bash ```bash
ar1ste1a@kali:~$ dehasher api -D <redacted>.com -o <redacted> -f json ar1ste1a@kali:~$ dehasher api -D <redacted>.com -o <redacted> -f json
+1 -14
View File
@@ -2,12 +2,10 @@ package cmd
import ( import (
"dehasher/internal/badger" "dehasher/internal/badger"
"dehasher/internal/debug"
"dehasher/internal/dehashed" "dehasher/internal/dehashed"
"dehasher/internal/sqlite" "dehasher/internal/sqlite"
"fmt" "fmt"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"go.uber.org/zap"
) )
func init() { func init() {
@@ -118,18 +116,7 @@ var (
dehasher.Start() dehasher.Start()
fmt.Println("\n[*] Completing Process") fmt.Println("\n[*] Completing Process")
err := sqlite.StoreQueryOptions(queryOptions) sqlite.StoreQueryOptions(queryOptions)
if err != nil {
if debugGlobal {
debug.PrintInfo("failed to store query options")
debug.PrintError(err)
}
zap.L().Error("store_query_options",
zap.String("message", "failed to store query options"),
zap.Error(err),
)
fmt.Printf("Error storing query options: %v\n", err)
}
}, },
} }
) )
+32 -11
View File
@@ -1,7 +1,6 @@
package cmd package cmd
import ( import (
"dehasher/internal/easyTime"
"dehasher/internal/pretty" "dehasher/internal/pretty"
"encoding/json" "encoding/json"
"fmt" "fmt"
@@ -74,11 +73,6 @@ var (
allLogs = append(allLogs, filepath.Join(logsPath, "info.log"), filepath.Join(logsPath, "error.log")) allLogs = append(allLogs, filepath.Join(logsPath, "info.log"), filepath.Join(logsPath, "error.log"))
} }
var timeChunk easyTime.TimeChunk
if logStartDate != "" {
timeChunk = easyTime.NewTimeChunk(logStartDate, logEndDate, debugGlobal)
}
var parsedLogs []LogEntry var parsedLogs []LogEntry
for _, logFile := range allLogs { for _, logFile := range allLogs {
// Read the log file // Read the log file
@@ -103,7 +97,7 @@ var (
continue continue
} }
// Unmarshal to get additional fields // Also unmarshal to get additional fields
if err := json.Unmarshal([]byte(line), &rawEntry); err != nil { if err := json.Unmarshal([]byte(line), &rawEntry); err != nil {
fmt.Printf("Error parsing raw log entry: %v\n", err) fmt.Printf("Error parsing raw log entry: %v\n", err)
continue continue
@@ -112,10 +106,10 @@ var (
// Parse the timestamp // Parse the timestamp
parsedTime, err := time.Parse("2006-01-02T15:04:05.999-0700", entry.Timestamp) parsedTime, err := time.Parse("2006-01-02T15:04:05.999-0700", entry.Timestamp)
if err != nil { if err != nil {
// Try RFC3339 // Try alternative formats
parsedTime, err = time.Parse(time.RFC3339, entry.Timestamp) parsedTime, err = time.Parse(time.RFC3339, entry.Timestamp)
if err != nil { if err != nil {
// Try RFC3339Nano // Try another format
parsedTime, err = time.Parse(time.RFC3339Nano, entry.Timestamp) parsedTime, err = time.Parse(time.RFC3339Nano, entry.Timestamp)
if err != nil { if err != nil {
fmt.Printf("Error parsing timestamp '%s': %v\n", entry.Timestamp, err) fmt.Printf("Error parsing timestamp '%s': %v\n", entry.Timestamp, err)
@@ -139,8 +133,22 @@ var (
(logFatal && strings.EqualFold(entry.Level, "FATAL")) { (logFatal && strings.EqualFold(entry.Level, "FATAL")) {
// Filter by date range if specified // Filter by date range if specified
if timeChunk.IsSet() { if logStartDate != "" {
if entry.ParsedTime.Before(timeChunk.StartTime) || entry.ParsedTime.After(timeChunk.EndTime) { startDate, err := time.Parse("2006-01-02", logStartDate)
if err != nil {
fmt.Printf("Error parsing start date: %v\n", err)
} else if entry.ParsedTime.Before(startDate) {
continue
}
}
if logEndDate != "" {
endDate, err := time.Parse("2006-01-02", logEndDate)
// Add one day to include the end date
endDate = endDate.Add(24 * time.Hour)
if err != nil {
fmt.Printf("Error parsing end date: %v\n", err)
} else if entry.ParsedTime.After(endDate) {
continue continue
} }
} }
@@ -203,3 +211,16 @@ const (
FATAL FATAL
UNKNOWN Severity = -1 UNKNOWN Severity = -1
) )
func getSeverity(logLevel string) Severity {
switch logLevel {
case "INFO":
return INFO
case "ERROR":
return ERROR
case "FATAL":
return FATAL
default:
return UNKNOWN
}
}
+3 -3
View File
@@ -16,7 +16,7 @@ var (
// rootCmd is the base command for the CLI. // rootCmd is the base command for the CLI.
rootCmd = &cobra.Command{ rootCmd = &cobra.Command{
Use: "dehasher", Use: "dehasher",
Short: `Dehasher is a cli tool for querying the dehashed api.`, Short: `Dehasher is a cli tool for querying query.`,
Long: fmt.Sprintf( Long: fmt.Sprintf(
"%s\n%s", "%s\n%s",
` `
@@ -42,7 +42,7 @@ var (
––•–√\/––√\/––•––––•–√\/––√\/––•––––•–√\/––√\/––•––√\/––•––––•–√\/––√\/––•–– ––•–√\/––√\/––•––––•–√\/––√\/––•––––•–√\/––√\/––•––√\/––•––––•–√\/––√\/––•––
`, `,
), ),
Version: "v1.2.1", Version: "v1.0",
} }
) )
@@ -63,7 +63,7 @@ func init() {
rootCmd.CompletionOptions.HiddenDefaultCmd = true rootCmd.CompletionOptions.HiddenDefaultCmd = true
// Add global flags // Add global flags
rootCmd.PersistentFlags().BoolVar(&debugGlobal, "debug", false, "Show debug information") rootCmd.PersistentFlags().BoolVar(&debugGlobal, "debug", false, "Show debugGlobal information")
// Add subcommands // Add subcommands
rootCmd.AddCommand(setKeyCmd) rootCmd.AddCommand(setKeyCmd)
+103 -26
View File
@@ -10,7 +10,6 @@ import (
"fmt" "fmt"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"go.uber.org/zap" "go.uber.org/zap"
"os"
"strings" "strings"
"time" "time"
) )
@@ -98,9 +97,15 @@ var (
// Show credits if requested // Show credits if requested
if whoisShowCredits { if whoisShowCredits {
fmt.Println("[*] Getting WHOIS balance...") fmt.Println("[*] Getting WHOIS balance...")
if whoisShowCredits { balance, err := w.Balance()
checkBalance(w) if err != nil {
zap.L().Error("get_whois_credits",
zap.String("message", "failed to get whois balance"),
zap.Error(err),
)
fmt.Printf("Error getting WHOIS balance: %v\n", err)
} }
fmt.Printf("WHOIS Credits: %d\n", balance)
} }
// Check if domain is provided for history and subdomain scan // Check if domain is provided for history and subdomain scan
@@ -110,7 +115,15 @@ var (
return return
} }
if whoisShowCredits { if whoisShowCredits {
checkBalance(w) balance, err := w.Balance()
if err != nil {
zap.L().Error("get_whois_credits",
zap.String("message", "failed to get whois balance"),
zap.Error(err),
)
fmt.Printf("Error getting WHOIS balance: %v\n", err)
}
fmt.Println("WHOIS Credits: ", balance)
} }
} }
@@ -134,7 +147,19 @@ var (
} }
if whoisShowCredits { if whoisShowCredits {
checkBalance(w) balance, err := w.Balance()
if err != nil {
if debugGlobal {
debug.PrintInfo("failed to get whois balance")
debug.PrintError(err)
}
zap.L().Error("get_whois_credits",
zap.String("message", "failed to get whois balance"),
zap.Error(err),
)
fmt.Printf("Error getting WHOIS balance: %v\n", err)
}
fmt.Println("WHOIS Credits: ", balance)
} }
// Fix the output format to use proper formatting // Fix the output format to use proper formatting
@@ -187,7 +212,19 @@ var (
fmt.Printf("[!] Error performing WHOIS history lookup: %v\n", err) fmt.Printf("[!] Error performing WHOIS history lookup: %v\n", err)
} else { } else {
if whoisShowCredits { if whoisShowCredits {
checkBalance(w) balance, err := w.Balance()
if err != nil {
if debugGlobal {
debug.PrintInfo("failed to get whois balance")
debug.PrintError(err)
}
zap.L().Error("get_whois_credits",
zap.String("message", "failed to get whois balance"),
zap.Error(err),
)
fmt.Printf("Error getting WHOIS balance: %v\n", err)
}
fmt.Println("WHOIS Credits: ", balance)
} }
// Write history records to file if any // Write history records to file if any
@@ -237,7 +274,19 @@ var (
subdomains, err := w.WhoisSubdomainScan(whoisDomain) subdomains, err := w.WhoisSubdomainScan(whoisDomain)
if whoisShowCredits { if whoisShowCredits {
checkBalance(w) balance, err := w.Balance()
if err != nil {
if debugGlobal {
debug.PrintInfo("failed to get whois balance")
debug.PrintError(err)
}
zap.L().Error("get_whois_credits",
zap.String("message", "failed to get whois balance"),
zap.Error(err),
)
fmt.Printf("Error getting WHOIS balance: %v\n", err)
}
fmt.Println("WHOIS Credits: ", balance)
} }
if err != nil { if err != nil {
@@ -322,7 +371,19 @@ var (
// Get credits // Get credits
if whoisShowCredits { if whoisShowCredits {
checkBalance(w) balance, err := w.Balance()
if err != nil {
if debugGlobal {
debug.PrintInfo("failed to get whois balance")
debug.PrintError(err)
}
zap.L().Error("get_whois_credits",
zap.String("message", "failed to get whois balance"),
zap.Error(err),
)
fmt.Printf("Error getting WHOIS balance: %v\n", err)
}
fmt.Println("WHOIS Credits: ", balance)
} }
if len(result) == 0 { if len(result) == 0 {
@@ -384,7 +445,19 @@ var (
// Get credits // Get credits
if whoisShowCredits { if whoisShowCredits {
checkBalance(w) balance, err := w.Balance()
if err != nil {
if debugGlobal {
debug.PrintInfo("failed to get whois balance")
debug.PrintError(err)
}
zap.L().Error("get_whois_credits",
zap.String("message", "failed to get whois balance"),
zap.Error(err),
)
fmt.Printf("Error getting WHOIS balance: %v\n", err)
}
fmt.Println("WHOIS Credits: ", balance)
} }
if len(result) == 0 { if len(result) == 0 {
@@ -446,7 +519,19 @@ var (
// Get credits // Get credits
if whoisShowCredits { if whoisShowCredits {
checkBalance(w) balance, err := w.Balance()
if err != nil {
if debugGlobal {
debug.PrintInfo("failed to get whois balance")
debug.PrintError(err)
}
zap.L().Error("get_whois_credits",
zap.String("message", "failed to get whois balance"),
zap.Error(err),
)
fmt.Printf("Error getting WHOIS balance: %v\n", err)
}
fmt.Println("WHOIS Credits: ", balance)
} }
if len(result) == 0 { if len(result) == 0 {
@@ -540,18 +625,6 @@ var (
fmt.Println(result) fmt.Println(result)
if whoisShowCredits { if whoisShowCredits {
checkBalance(w)
}
return
}
// If no specific operation was requested
cmd.Help()
},
}
)
func checkBalance(w *whois.DehashedWhoIs) {
balance, err := w.Balance() balance, err := w.Balance()
if err != nil { if err != nil {
if debugGlobal { if debugGlobal {
@@ -565,8 +638,12 @@ func checkBalance(w *whois.DehashedWhoIs) {
fmt.Printf("Error getting WHOIS balance: %v\n", err) fmt.Printf("Error getting WHOIS balance: %v\n", err)
} }
fmt.Println("WHOIS Credits: ", balance) fmt.Println("WHOIS Credits: ", balance)
if balance == 0 {
fmt.Println("[!] No WHOIS credits remaining.")
os.Exit(0)
} }
} return
}
// If no specific operation was requested
cmd.Help()
},
}
)
+132 -71
View File
@@ -18,6 +18,7 @@ type Dehasher struct {
balance int balance int
request *DehashedSearchRequest request *DehashedSearchRequest
client *DehashedClientV2 client *DehashedClientV2
queryPlan []struct{ Page, Size int }
} }
// NewDehasher creates a new Dehasher // NewDehasher creates a new Dehasher
@@ -27,9 +28,18 @@ func NewDehasher(options *sqlite.QueryOptions) *Dehasher {
nextPage: options.StartingPage + 1, nextPage: options.StartingPage + 1,
debug: options.Debug, debug: options.Debug,
balance: 0, balance: 0,
queryPlan: make([]struct{ Page, Size int }, 0),
} }
dh.setQueries() dh.setQueries()
dh.request = NewDehashedSearchRequest(dh.options.StartingPage, dh.options.MaxRecords, dh.options.WildcardMatch, dh.options.RegexMatch, false, options.Debug) dh.request = NewDehashedSearchRequest(
dh.queryPlan[0].Page,
dh.queryPlan[0].Size,
dh.options.WildcardMatch,
dh.options.RegexMatch,
false,
options.Debug,
)
dh.buildRequest() dh.buildRequest()
return dh return dh
} }
@@ -48,66 +58,139 @@ func (dh *Dehasher) getNextPage() int {
return nextPage return nextPage
} }
// generatePagination creates a list of (page, size) tuples such that page * size <= 10000
func generatePagination(maxRecords int) []struct{ Page, Size int } {
const maxPageProduct = 9500
var queries []struct{ Page, Size int }
remaining := maxRecords
page := 1
for remaining > 0 {
size := (maxPageProduct - 1) / page // guarantees page * size < 10000
if size > remaining {
size = remaining
}
queries = append(queries, struct{ Page, Size int }{page, size})
remaining -= size
page++
}
return queries
}
// setQueries sets the number of queries to make based on the number of records and requests // setQueries sets the number of queries to make based on the number of records and requests
func (dh *Dehasher) setQueries() { func (dh *Dehasher) setQueries() {
var numQueries int if dh.options.MaxRecords <= 0 {
dh.options.MaxRecords = 10000
}
dh.queryPlan = generatePagination(dh.options.MaxRecords)
fmt.Printf("Making %d requests to retrieve %d records\n", len(dh.queryPlan), dh.options.MaxRecords)
if dh.debug { if dh.debug {
debug.PrintInfo("setting queries") for i, q := range dh.queryPlan {
debug.PrintInfo(fmt.Sprintf("query %d: page=%d, size=%d", i+1, q.Page, q.Size))
} }
switch {
case dh.options.MaxRequests == 0:
zap.L().Error("max requests cannot be zero")
fmt.Println("[!] Max Requests cannot be zero")
os.Exit(1)
case dh.options.MaxRecords <= 10000 || dh.options.MaxRequests == 1:
numQueries = 1
if dh.options.MaxRecords > 10000 {
dh.options.MaxRecords = 10000
} }
zap.L().Info("max requests set to 1", zap.Int("max_records", dh.options.MaxRecords))
case dh.options.MaxRequests < 0 && dh.options.MaxRecords > 20000:
numQueries = 3
dh.options.MaxRecords = 10000
zap.L().Info("max requests set to 3", zap.Int("max_records", dh.options.MaxRecords))
case dh.options.MaxRequests < 0 && dh.options.MaxRecords > 10000:
numQueries = 2
dh.options.MaxRecords = 10000
zap.L().Info("max requests set to 2", zap.Int("max_records", dh.options.MaxRecords))
case dh.options.MaxRecords < 0 && dh.options.MaxRecords < 10000:
numQueries = 1
zap.L().Info("max requests set to 1", zap.Int("max_records", dh.options.MaxRecords))
case dh.options.MaxRequests == 2 && dh.options.MaxRecords > 20000:
numQueries = 2
dh.options.MaxRecords = 10000
zap.L().Info("max requests set to 2", zap.Int("max_records", dh.options.MaxRecords))
case dh.options.MaxRequests == 2 && dh.options.MaxRecords <= 10000:
numQueries = 1
zap.L().Info("max requests set to 1", zap.Int("max_records", dh.options.MaxRecords))
default:
numQueries = 3
dh.options.MaxRecords = 10000
zap.L().Info("max requests set to 3", zap.Int("max_records", dh.options.MaxRecords))
}
dh.options.MaxRequests = numQueries
if dh.debug {
debug.PrintInfo(fmt.Sprintf("setting max requests: %d", numQueries))
debug.PrintInfo(fmt.Sprintf("setting max records: %d", dh.options.MaxRecords))
}
fmt.Printf("Making %d Requests for %d Records (%d Total)\n", dh.options.MaxRequests, dh.options.MaxRecords, dh.options.MaxRequests*dh.options.MaxRecords)
} }
// Start starts the querying process // Start starts the querying process
func (dh *Dehasher) Start() { func (dh *Dehasher) Start() {
fmt.Printf("[*] Querying Dehashed API...\n") fmt.Printf("[*] Querying Dehashed API...\n")
for i := 0; i < dh.options.MaxRequests; i++ {
fmt.Printf(" [*] Performing Request...\n") // Make initial request to get total count
count, balance, err := dh.client.Search(*dh.request) fmt.Printf(" [*] Performing initial request to determine total records...\n")
totalRecords, balance, err := dh.client.Search(*dh.request)
if err != nil { if err != nil {
handleSearchError(dh, err)
return
}
dh.balance = balance
recordsRetrieved := len(dh.client.results)
fmt.Printf(" [+] Retrieved %d records\n", recordsRetrieved)
fmt.Printf(" [*] Total available records: %d\n", totalRecords)
if dh.options.PrintBalance {
fmt.Printf(" [*] Balance: %d\n", balance)
}
// If we've already got all records or reached our limit, we're done
if recordsRetrieved >= totalRecords || recordsRetrieved >= dh.options.MaxRecords {
fmt.Printf(" [*] All requested records retrieved\n")
dh.parseResults()
return
}
// Calculate remaining records to fetch
remainingRecords := totalRecords - recordsRetrieved
if dh.options.MaxRecords > 0 && dh.options.MaxRecords < totalRecords {
remainingRecords = dh.options.MaxRecords - recordsRetrieved
}
// Check if we need user confirmation for large datasets
if remainingRecords > 30000 {
tokensRequired := (remainingRecords + 9999) / 10000 // Ceiling division
fmt.Printf("\n[!] Large dataset detected: %d additional records\n", remainingRecords)
fmt.Printf("[!] This will require approximately %d API tokens\n", tokensRequired)
fmt.Printf("[!] Your current balance: %d\n", balance)
if balance < tokensRequired {
fmt.Printf("[!] WARNING: Your balance (%d) is less than required tokens (%d)\n", balance, tokensRequired)
}
fmt.Printf("[?] Do you want to continue? (y/n): ")
var response string
fmt.Scanln(&response)
if response != "y" && response != "Y" {
fmt.Println("[*] Operation cancelled by user")
dh.parseResults()
return
}
}
// Make additional requests
for i, q := range dh.queryPlan {
if i == 0 {
// We already made the first request before this loop
continue
}
dh.request.Page = q.Page
dh.request.Size = q.Size
fmt.Printf(" [*] Performing Request %d of %d (page=%d, size=%d)...\n", i+1, len(dh.queryPlan), q.Page, q.Size)
_, balance, err := dh.client.Search(*dh.request)
if err != nil {
handleSearchError(dh, err)
break
}
dh.balance = balance
recordsRetrieved += len(dh.client.results)
fmt.Printf(" [+] Retrieved %d total records so far\n", recordsRetrieved)
if dh.options.PrintBalance {
fmt.Printf(" [*] Balance: %d\n", balance)
}
if recordsRetrieved >= totalRecords || recordsRetrieved >= dh.options.MaxRecords {
fmt.Printf(" [*] All requested records retrieved\n")
break
}
}
dh.parseResults()
}
// Helper function to handle search errors
func handleSearchError(dh *Dehasher, err error) {
if dh.debug { if dh.debug {
debug.PrintInfo("error performing request") debug.PrintInfo("error performing request")
debug.PrintError(err) debug.PrintError(err)
@@ -139,28 +222,6 @@ func (dh *Dehasher) Start() {
fmt.Printf(" [!] Error storing results: %v\n", err) fmt.Printf(" [!] Error storing results: %v\n", err)
} }
} }
dh.parseResults()
os.Exit(-1)
}
dh.balance = balance
if count < dh.options.MaxRecords {
fmt.Printf(" [+] Retrieved %d records\n", count)
fmt.Printf(" [-] Not enough entries, ending queries\n")
break
} else {
fmt.Printf(" [+] Retrieved %d records\n", dh.options.MaxRecords)
}
if dh.options.PrintBalance {
fmt.Printf(" [*] Balance: %d\n", balance)
}
dh.request.Page = dh.getNextPage()
}
dh.parseResults()
} }
// buildRequest constructs the query map // buildRequest constructs the query map
-266
View File
@@ -1,266 +0,0 @@
package easyTime
import (
"dehasher/internal/debug"
"fmt"
"go.uber.org/zap"
"os"
"strconv"
"strings"
"time"
)
type TimeChunk struct {
StartTime time.Time
EndTime time.Time
set bool
}
func (tc *TimeChunk) isValid() bool {
if !tc.StartTime.IsZero() && !tc.EndTime.IsZero() && tc.StartTime.Before(tc.EndTime) {
tc.set = true
return true
}
tc.set = false
return false
}
func (tc *TimeChunk) IsSet() bool {
return tc.set
}
func NewTimeChunk(start, end string, debugOn bool) TimeChunk {
if debugOn {
debug.PrintInfo("parsing time chunk")
debug.PrintInfo(fmt.Sprintf("Start: %s, End: %s", start, end))
zap.L().Info("parsing time chunk",
zap.String("start", start),
zap.String("end", end),
)
}
if end == "" {
if debugOn {
debug.PrintInfo("no end time provided, using now")
}
end = "now"
}
tc := TimeChunk{
StartTime: parseUserTime(start),
EndTime: parseUserTime(end),
}
if debugOn {
debug.PrintInfo("checking if time chunk is valid")
debug.PrintInfo(fmt.Sprintf("Start: %s, End: %s", tc.StartTime, tc.EndTime))
}
if !tc.isValid() {
fmt.Println("[!] Invalid time chunk")
zap.L().Fatal("invalid_time_chunk",
zap.String("message", "invalid time chunk"),
)
os.Exit(1)
}
return tc
}
func parseUserTime(args string) time.Time {
args = strings.TrimSpace(args)
if strings.EqualFold(args, "now") {
return time.Now()
}
// Check if time contains a space, if so, it's in 'last 24 hours' format
if strings.Contains(args, " ") && !containsMonth(strings.Split(args, " ")) {
splitArgs := strings.Split(args, " ")
if len(splitArgs) == 0 {
fmt.Println("[!] No time provided")
zap.L().Fatal("no_time_provided",
zap.String("message", "no time provided"),
)
os.Exit(1)
} else if len(splitArgs) < 3 {
fmt.Println("[!] Invalid time format")
zap.L().Fatal("invalid_time_format",
zap.String("message", "invalid time format"),
)
os.Exit(1)
}
// Handle 'last 24 hours' format
var (
tense string
amount int
duration time.Duration
)
for _, arg := range splitArgs {
if isPasteTense(arg) {
tense = arg
} else if isNumber(arg) {
amount, _ = strconv.Atoi(arg)
} else if isDuration(arg) {
duration = getDuration(arg)
}
}
if tense == "" {
fmt.Println("[!] Invalid time format: tense not found")
zap.L().Fatal("invalid_time_format",
zap.String("message", "invalid time format"),
)
os.Exit(1)
} else if amount == 0 {
fmt.Println("[!] Invalid time format: amount not found")
zap.L().Fatal("invalid_time_format",
zap.String("message", "invalid time format"),
)
os.Exit(1)
} else if duration == 0 {
fmt.Println("[!] Invalid time format: duration not found")
zap.L().Fatal("invalid_time_format",
zap.String("message", "invalid time format"),
)
os.Exit(1)
}
// Return the appropriate time
if tense == "last" {
return time.Now().Add(-time.Duration(amount) * duration)
} else if tense == "ago" {
return time.Now().Add(-time.Duration(amount) * duration)
}
}
// Handle possible formats 'May 01, 2025', '05-01-2025', '05/01/2025', '05/01/25', '05-01-25'
var (
t time.Time
err error
found bool
)
possible := []string{"01-02-2006", "01/02/2006", "01/02/06", "01-02-06", "Jan 02, 2006", "Jan 2, 2006"}
for _, format := range possible {
t, err = time.Parse(format, args)
if err == nil {
found = true
break
}
}
if !found {
fmt.Println("[!] Invalid time format")
zap.L().Fatal("invalid_time_format",
zap.String("message", "invalid time format"),
)
os.Exit(1)
}
// Convert UTC time to local time
local, err := time.LoadLocation("Local")
if err != nil {
fmt.Println("[!] Error loading local timezone")
zap.L().Error("load_timezone",
zap.String("message", "failed to load local timezone"),
zap.Error(err),
)
return t
}
// Convert the parsed time to local time
return time.Date(
t.Year(),
t.Month(),
t.Day(),
t.Hour(),
t.Minute(),
t.Second(),
t.Nanosecond(),
local,
)
}
func isPasteTense(value string) bool {
for _, v := range []string{"last", "ago"} {
if strings.EqualFold(value, v) {
return true
}
}
return false
}
func isDuration(value string) bool {
for _, v := range []string{"hour", "hours", "minute", "minutes", "second", "seconds", "day", "days", "week", "weeks", "month", "months", "year", "years"} {
if strings.EqualFold(value, v) {
return true
}
}
return false
}
func isNumber(value string) bool {
_, err := strconv.Atoi(value)
return err == nil
}
func getDuration(timeBlock string) time.Duration {
timeBlock = strings.TrimSpace(strings.ToLower(timeBlock))
switch timeBlock {
case "hour":
return time.Hour
case "hours":
return time.Hour
case "minute":
return time.Minute
case "minutes":
return time.Minute
case "second":
return time.Second
case "seconds":
return time.Second
case "day":
return 24 * time.Hour
case "days":
return 24 * time.Hour
case "week":
return 7 * 24 * time.Hour
case "weeks":
return 7 * 24 * time.Hour
case "month":
return 30 * 24 * time.Hour
case "months":
return 30 * 24 * time.Hour
case "year":
return 365 * 24 * time.Hour
case "years":
return 365 * 24 * time.Hour
default:
fmt.Printf("[!] Unknown duration: %s", timeBlock)
zap.L().Fatal("unknown_duration",
zap.String("message", "unknown duration"),
zap.String("duration", timeBlock),
)
os.Exit(1)
}
return 0
}
func containsMonth(arr []string) bool {
for _, v := range arr {
if isMonth(v) {
return true
}
}
return false
}
func isMonth(value string) bool {
for _, v := range []string{"jan", "feb", "mar", "apr", "may", "jun", "jul", "aug", "sep", "oct", "nov", "dec"} {
if strings.EqualFold(value, v) {
return true
}
}
return false
}
-5
View File
@@ -11,7 +11,6 @@ const (
WhoIsTable WhoIsTable
SubdomainsTable SubdomainsTable
HistoryTable HistoryTable
LookupTable
UnknownTable UnknownTable
) )
@@ -29,8 +28,6 @@ func GetTable(userInput string) Table {
return SubdomainsTable return SubdomainsTable
case "history": case "history":
return HistoryTable return HistoryTable
case "lookup":
return LookupTable
default: default:
return UnknownTable return UnknownTable
} }
@@ -50,8 +47,6 @@ func (t Table) Object() interface{} {
return SubdomainRecord{} return SubdomainRecord{}
case HistoryTable: case HistoryTable:
return HistoryRecord{} return HistoryRecord{}
case LookupTable:
return LookupResult{}
default: default:
return nil return nil
} }