Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 770403419d |
@@ -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 |
@@ -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.
|
|
||||||

|
|
||||||
#### You may also used dates mixed with easy time to perform queries.
|
|
||||||

|
|
||||||
#### 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
@@ -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
@@ -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
@@ -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)
|
||||||
|
|||||||
+102
-25
@@ -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
@@ -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
|
||||||
|
|||||||
@@ -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
|
|
||||||
}
|
|
||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user