Updated Readme to reflect new branding

This commit is contained in:
Evan Hosinski
2025-05-17 12:58:37 -04:00
parent fe4d904c5f
commit d4db32c8b9
13 changed files with 290 additions and 131 deletions
Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 101 KiB

+6 -2
View File
@@ -30,14 +30,18 @@ clean:
# Build for current platform
build:
$(GO) build -o $(BUILD_DIR)/$(BINARY_NAME) -ldflags "-X main.version=$(VERSION)" crowsnest.go
CGO_ENABLED=1 $(GO) build -o $(BUILD_DIR)/$(BINARY_NAME) -ldflags "-X main.version=$(VERSION)" crowsnest.go
# Build for all platforms
build-all: clean
@for platform in $(PLATFORMS); do \
for arch in $(ARCHS); do \
echo "Building for $$platform/$$arch..."; \
GOOS=$$platform GOARCH=$$arch $(GO) build -o $(BUILD_DIR)/$(BINARY_NAME)-$$platform-$$arch -ldflags "-X main.version=$(VERSION)" crowsnest.go; \
if [ "$$platform" = "windows" ] || [ "$$platform" = "darwin" ]; then \
GOOS=$$platform GOARCH=$$arch CGO_ENABLED=0 $(GO) build -o $(BUILD_DIR)/$(BINARY_NAME)-$$platform-$$arch -ldflags "-X main.version=$(VERSION)" crowsnest.go; \
else \
GOOS=$$platform GOARCH=$$arch CGO_ENABLED=1 $(GO) build -o $(BUILD_DIR)/$(BINARY_NAME)-$$platform-$$arch -ldflags "-X main.version=$(VERSION)" crowsnest.go; \
fi; \
if [ "$$platform" = "windows" ]; then \
mv $(BUILD_DIR)/$(BINARY_NAME)-$$platform-$$arch $(BUILD_DIR)/$(BINARY_NAME)-$$platform-$$arch.exe; \
fi; \
+55 -44
View File
@@ -61,30 +61,25 @@ To configure the database location:
### Initial Setup
CrowsNest requires an API key from Dehashed. Set it up with:
![Alt text](.img/set-dehashed.png "Set Dehashed Key")
```bash
ar1ste1a@kali:~$ crowsnest set-dehashed <redacted>
```
### Simple Query
CrowsNest can be used simply for example to query for credentials matching a given email domain.
![Alt text](.img/simple_query.png "Simple Query")
``` go
# Provide credentials for domains matching target.com
crowsnest api -D target.com -C
crowsnest dehashed -D target.com
```
### Simple Credentials Query
CrowsNest can also be used to return only credentials for a given query.
![Alt text](.img/simple_creds_query.png "Creds Only Query")
``` go
# Provide credentials for emails matching @target.com
crowsnest api -E @target.com -C
```
### Multiple Match Query
CrowsNest is capable of handling multiple queries for the same field.
This is useful for when you want to search for multiple domains, or multiple usernames.
``` go
# Provide credentials for domains matching target.com and target2.com, retrieving only credentials
crowsnest api -D target.com,target2.com -C
crowsnest dehashed -D @target.com -C
```
### Wildcard Query
@@ -92,34 +87,46 @@ CrowsNest is capable of handling wildcard queries.
A wildcard query cannot begin with a wildcard.
This is a limitation of the Dehashed API.
An asterisk can be used to denote multiple characters, and a question mark can be used to denote a single character.
![Alt text](.img/wildcard_sample.png "Wildcard Query")
![Alt text](.img/wildcard_query.png "Wildcard Query")
``` go
# Provide credentials for emails matching @target.com and @target2.com
crowsnest api -E @target?.com -C -W
crowsnest dehashed -E @target?.com -C -W
```
### Email Query
Dehashed has dictated that emails should be searched in the following format:
`email:target.name&domain:target.com`.
As such, to query an email, please use the following format (note, wildcard is not required but can be useful):
<br>
*see photo above in Wildcard Query*
``` go
# Provide credentials for emails matching target.*@target.com
crowsnest api -W -E 'target*' -D target.com
crowsnest dehashed -W -E 'target*' -D target.com
```
You may also query the domain and find emails as well
``` go
# Provide credentials for emails matching target.com
crowsnest api -D target.com -C
crowsnest dehashed -D target.com -C
```
### Combining Queries
CrowsNest is capable of combining queries.
This is useful for when you want to query for credentials matching a given email or domain, but only for a specific username.
![Alt text](.img/combining_queries.png "Combined Query")
``` go
# Provide credentials for emails matching @target.com and username containing 'admin'
crowsnest dehashed -D target.com -U admin
```
### Regex Query
CrowsNest is capable of handling regex queries.
Simply denote regex queries with the `-R` flag.
Place all regex queries in quotes with the corresponding query flag in single quotes.
<br>
!!!! *Currently, the Regex Operators appear to be broken. I am waiting on a response from Dehashed* !!!!
``` go
# Return matches for emails matching this given regex query
crowsnest api -R -E '[a-zA-Z0-9]+(?:\.[a-zA-Z0-9]+)?@target.com'
crowsnest dehashed -R -E 'joh?n(ath[oa]n)' -D hotmail.com'
```
### Output Text (default JSON)
@@ -129,7 +136,7 @@ To change the output format, use the `-f` flag.
CrowsNest currently supports JSON, YAML, XML, and TEXT output formats.
``` go
# Return matches for usernames exactly matching "admin" and write to text file 'admins_file.txt'
crowsnest api -U admin -o admins_file -f txt
crowsnest dehashed -U admin -o admins_file -f txt
```
---
@@ -141,7 +148,7 @@ The WhoIs Lookups require a separate API Credit from the Dehashed API.
### Domain Lookup
CrowsNest can perform a domain lookup for a given domain.
This provides a tree view of the domain's WHOIS information.
![Alt text](.img/tree_whois_lookup.png "WhoIs Tree View")
![Alt text](.img/whois_domain.png "WhoIs Tree View")
```bash
# Perform a WHOIS lookup for example.com
crowsnest whois -d example.com
@@ -156,10 +163,20 @@ The history lookup is immediately written to file and not displayed in the termi
crowsnest whois -d example.com -H
```
### Subdomain Scan
CrowsNest can perform a subdomain scan for a given domain.
This provides a list of all subdomains that match the given query.
![Alt text](.img/whois_subdomain.png "WhoIs Tree View")
```bash
# Perform a WHOIS subdomain scan for google.com
crowsnest whois -d google.com -s
```
### Reverse WHOIS Lookup
CrowsNest can perform a reverse WHOIS lookup for given criteria.
This provides a list of all domains that match the given query.
The reverse WHOIS lookup is immediately written to file and not displayed in the terminal or stored in the database.
The reverse WHOIS lookup is immediately written to file and not stored in the database.
![Alt text](.img/whois_reverse.png "WhoIs Tree View")
```bash
# Perform a reverse WHOIS lookup for example.com
crowsnest whois -I example.com
@@ -168,7 +185,7 @@ crowsnest whois -I example.com
### IP Lookup
CrowsNest can perform a reverse IP lookup for a given IP address.
This provides a list of all domains that match the given query.
![Alt text](.img/reverse_ip_lookup.png "WhoIs Tree View")
![Alt text](.img/whois_ip.png "WhoIs View")
```bash
# Perform a reverse IP lookup for 8.8.8.8
crowsnest whois -i 8.8.8.8
@@ -177,28 +194,21 @@ crowsnest whois -i 8.8.8.8
### MX Lookup
CrowsNest can perform an MX lookup for a given MX hostname.
This provides a list of all domains that match the given query.
![Alt text](.img/mx_lookup.png "WhoIs Tree View")
![Alt text](.img/whois_mx.png "WhoIs Tree View")
```bash
# Perform a reverse MX lookup for google.com
crowsnest whois -m google.com
crowsnest whois -m stmp.google.com
```
### NS Lookup
CrowsNest can perform an NS lookup for a given NS hostname.
This provides a list of all domains that match the given query.
The picture below also includes the --debug global flag.
![Alt text](.img/debug_ns_search.png "WhoIs Tree View")
![Alt text](.img/whois_ns.png "WhoIs Tree View")
```bash
# Perform a reverse NS lookup for google.com
crowsnest whois -n google.com
```
### Subdomain Scan
CrowsNest can perform a subdomain scan for a given domain.
This provides a list of all subdomains that match the given query.
![Alt text](.img/subdomains_lookup.png "WhoIs Tree View")
```bash
# Perform a WHOIS subdomain scan for google.com
crowsnest whois -d google.com -s
```
---
@@ -206,6 +216,7 @@ crowsnest whois -d google.com -s
CrowsNest supports Hunter.io lookups.
Hunter.io lookups require a separate API Key from the Dehashed API.
This can be set using the `set-hunter` command.
![Alt text](.img/set-hunter.png "Set Dehashed Key")
```bash
# Set the Hunter.io API key
crowsnest set-hunter <redacted>
@@ -214,25 +225,16 @@ crowsnest set-hunter <redacted>
### Domain Search
CrowsNest can perform a domain search for a given domain.
This provides information about company including a description, social media information and any technologies in use.
![Alt text](.img/hunter_domain_search.png "Hunter.io Domain Search")
![Alt text](.img/hunter_domain.png "Hunter.io Domain Search")
```bash
# Perform a Hunter.io domain search for example.com
crowsnest hunter -d example.com -D
```
### Email Finder
CrowsNest can perform an email finder search for a given domain, first name, and last name.
This provides information about a user including a confidence score, and any social media accounts linked to a first name, last name and email.
![Alt text](.img/hunter_email_finder.png "Hunter.io Email Finder")
```bash
# Perform a Hunter.io email finder search for example.com
crowsnest hunter -d example.com -F John -L Doe -E
```
### Email Verification
CrowsNest can perform an email verification search for a given email.
This provides a verification and score of a given email address.
![Alt text](.img/email_verification.png "Hunter.io Email Verification")
![Alt text](.img/hunter_emailverification.png "Hunter.io Email Verification")
```bash
# Perform a Hunter.io email verification search for example@target.com
crowsnest hunter -e example@target.com -V
@@ -241,16 +243,25 @@ crowsnest hunter -e example@target.com -V
### Company Enrichment
CrowsNest can perform a company enrichment search for a given domain.
This provides information about a company given its domain.
![Alt text](.img/company_enrichment.png "Hunter.io Company Enrichment")
![Alt text](.img/hunter_company.png "Hunter.io Company Enrichment")
```bash
# Perform a Hunter.io company enrichment search for example.com
crowsnest hunter -d example.com -C
```
### Email Finder
CrowsNest can perform an email finder search for a given domain, first name, and last name.
This provides information about a user including a confidence score, and any social media accounts linked to a first name, last name and email.
![Alt text](.img/hunter_emailfind.png "Hunter.io Email Finder")
```bash
# Perform a Hunter.io email finder search for example.com
crowsnest hunter -d example.com -F John -L Doe -E
```
### Person Enrichment
CrowsNest can perform a person enrichment search for a given email.
This provides information about a user given an email address..
![Alt text](.img/person_enrichment.png "Hunter.io Person Enrichment")
![Alt text](.img/hunter_person.png "Hunter.io Person Enrichment")
```bash
# Perform a Hunter.io person enrichment search for example@target.com
crowsnest hunter -e example@target.com -P
@@ -376,7 +387,7 @@ crowsnest logs -s "05-01-2025" -v error,fatal
## 🎉 Sample Run
```bash
ar1ste1a@kali:~$ crowsnest api -D <redacted>.com -o <redacted> -f json
ar1ste1a@kali:~$ crowsnest dehashed -D <redacted>.com -o <redacted> -f json
Making 3 Requests for 10000 Records (30000 Total)
[*] Querying Dehashed API...
[*] Performing Request...
+1 -2
View File
@@ -166,7 +166,7 @@ var (
fmt.Println("Email Find Result:")
var (
headers = []string{"Email", "Score", "Domain", "Accept All", "Position", "Twitter", "Linkedin", "Phone Number", "Company", "Sources", "Verification"}
headers = []string{"Email", "Score", "Domain", "Accept All", "Position", "Twitter", "Linkedin", "Phone Number", "Company", "Verification"}
rows [][]string
)
@@ -180,7 +180,6 @@ var (
result.LinkedinURL,
result.PhoneNumber,
result.Company,
fmt.Sprintf("%v", result.Sources),
fmt.Sprintf("%v", result.Verification),
})
+45 -10
View File
@@ -26,7 +26,7 @@ func init() {
whoisCmd.Flags().StringVarP(&whoisNSAddress, "ns", "n", "", "NS hostname for reverse NS lookup")
whoisCmd.Flags().StringVarP(&whoisInclude, "include", "I", "", "Up to 4 Terms to include in reverse WHOIS search (comma-separated)")
whoisCmd.Flags().StringVarP(&whoisExclude, "exclude", "E", "", "Up to 4 Terms to exclude in reverse WHOIS search (comma-separated)")
whoisCmd.Flags().StringVarP(&whoisReverseType, "type", "t", "registrant", "Type of reverse WHOIS search ([default] current or historic)")
whoisCmd.Flags().StringVarP(&whoisReverseType, "type", "t", "current", "Type of reverse WHOIS search ([default] current or historic)")
whoisCmd.Flags().StringVarP(&whoisOutputFormat, "format", "f", "text", "Output format (text, json)")
whoisCmd.Flags().StringVarP(&whoisOutputFile, "output", "o", "whois", "File to output results to including extension")
whoisCmd.Flags().BoolVarP(&whoisShowCredits, "credits", "c", false, "Show remaining WHOIS credits")
@@ -118,6 +118,7 @@ var (
if whoisDomain != "" {
fmt.Println("[*] Performing WHOIS lookup...")
if !whoisHistory && !whoisSubdomainScan {
// Domain lookup
result, err := w.WhoisSearch(whoisDomain)
if err != nil {
@@ -170,6 +171,7 @@ var (
zap.String("message", "no whois record to write to file"),
)
}
}
if whoisHistory {
filename := whoisOutputFile + "_history"
@@ -253,7 +255,6 @@ var (
)
fmt.Printf("Error performing subdomain scan: %v\n", err)
} else {
fmt.Println("Subdomain Scan:")
err = sqlite.StoreWhoisSubdomainRecords(subdomains)
if err != nil {
if debugGlobal {
@@ -290,6 +291,7 @@ var (
}
// Store the subdomains
fmt.Println("Subdomain Scan:")
pretty.Table(headers, rows)
} else {
@@ -492,6 +494,12 @@ var (
}
if whoisInclude != "" || whoisExclude != "" {
if debugGlobal {
debug.PrintInfo("performing reverse whois")
debug.PrintInfo("include: " + whoisInclude)
debug.PrintInfo("exclude: " + whoisExclude)
debug.PrintInfo("reverse type: " + whoisReverseType)
}
// Reverse WHOIS
includeTerms := []string{}
if whoisInclude != "" {
@@ -511,18 +519,11 @@ var (
}
}
if whoisReverseType == "" {
if debugGlobal {
debug.PrintInfo("reverse type not specified, using default")
}
whoisReverseType = "current"
} else {
toLower := strings.ToLower(whoisReverseType)
if toLower != "current" && toLower != "historic" {
fmt.Println("[!] Error: Invalid reverse type. Must be 'current' or 'historic'.")
return
}
}
fmt.Println("[*] Performing reverse WHOIS lookup...")
result, err := w.ReverseWHOIS(includeTerms, excludeTerms, whoisReverseType)
@@ -538,8 +539,42 @@ var (
fmt.Printf("Error performing reverse WHOIS: %v\n", err)
return
}
// Write to file
if len(result.DomainsList) > 0 {
fmt.Printf("[*] Writing reverse WHOIS results to file: %s%s\n", whoisOutputFile, fType.Extension())
err = export.WriteIStringToFile(result, whoisOutputFile, fType)
if err != nil {
if debugGlobal {
debug.PrintInfo("failed to write reverse whois to file")
debug.PrintError(err)
}
zap.L().Error("write_reverse_whois",
zap.String("message", "failed to write reverse whois to file"),
zap.Error(err),
)
fmt.Printf("Error writing reverse WHOIS to file: %v\n", err)
}
fmt.Println("Reverse WHOIS Result:")
fmt.Println(result)
fmt.Printf("Total Domains: %d\n", result.DomainsCount)
var (
headers = []string{"Domain"}
rows [][]string
)
for _, r := range result.DomainsList {
rows = append(rows, []string{r})
}
pretty.Table(headers, rows)
} else {
fmt.Println("[!] No results found")
zap.L().Info("reverse_whois",
zap.String("message", "no results found"),
)
}
if whoisShowCredits {
checkBalance(w)
+5 -1
View File
@@ -166,7 +166,11 @@ func (dcv2 *DehashedClientV2) Search(searchRequest DehashedSearchRequest) (int,
zap.String("message", "preparing search request"),
)
}
reqBody, _ := json.Marshal(searchRequest)
// Create a copy of the search request to avoid modifying the original
requestCopy := searchRequest
reqBody, _ := json.Marshal(requestCopy)
if dcv2.debug {
j := string(reqBody)
+79 -15
View File
@@ -3,11 +3,12 @@ package dehashed
import (
"crowsnest/internal/debug"
"crowsnest/internal/export"
"crowsnest/internal/pretty"
"crowsnest/internal/sqlite"
"encoding/json"
"fmt"
"go.uber.org/zap"
"os"
"strings"
)
// Dehasher is a struct for querying the Dehashed API
@@ -208,12 +209,10 @@ func (dh *Dehasher) buildRequest() {
// parseResults parses the results and writes them to a file
func (dh *Dehasher) parseResults() {
var data []byte
zap.L().Info("extracting_credentials")
results := dh.client.GetResults()
creds := results.ExtractCredentials()
fmt.Printf("\n\t[+] Discovered %d Credentials", len(creds))
fmt.Printf(" [+] Discovered %d Credentials\n", len(creds))
err := sqlite.StoreDehashedCreds(creds)
if err != nil {
zap.L().Error("store_creds",
@@ -234,28 +233,93 @@ func (dh *Dehasher) parseResults() {
zap.L().Info("results_stored", zap.Int("count", len(results.Results)))
if len(results.Results) > 0 {
fmt.Printf("\n\t[*] Writing entries to file: %s.%s", dh.options.OutputFile, dh.options.OutputFormat.String())
var (
headers = []string{"Email", "Username", "Password"}
rows [][]string
)
fmt.Printf(" [*] Writing entries to file: %s.%s\n", dh.options.OutputFile, dh.options.OutputFormat.String())
if !dh.options.CredsOnly {
err := export.WriteToFile(results, dh.options.OutputFile, dh.options.OutputFormat)
if err != nil {
fmt.Printf("\n[!] Error Writing to file: %v\n\tOutputting to terminal.", err)
data, err = json.MarshalIndent(results, "", " ")
fmt.Println(string(data))
os.Exit(0)
fmt.Printf("[!] Error Writing to file: %v Outputting to terminal.\n", err)
zap.L().Error("write_results",
zap.String("message", "failed to write results to file"),
zap.Error(err),
)
} else {
fmt.Println("\n\t\t[*] Success\n")
fmt.Println(" [*] Success")
}
if dh.debug {
debug.PrintInfo("printing results table")
}
headers = []string{"Name", "Email", "Username", "Password", "Address", "Phone", "Social", "Crypto Address", "Company"}
if len(results.Results) > 50 {
fmt.Println(" [-] Large number of results recovered, displaying first 50...")
for i := 0; i < 50; i++ {
r := results.Results[i]
rows = append(rows, []string{
strings.Join(r.Name, ", "), strings.Join(r.Email, ", "),
strings.Join(r.Username, ", "), strings.Join(r.Password, ", "),
strings.Join(r.Address, ", "), strings.Join(r.Phone, ", "),
strings.Join(r.Social, ", "), strings.Join(r.CryptoCurrencyAddress, ", "),
strings.Join(r.Company, ", ")})
}
} else {
for _, r := range results.Results {
rows = append(rows, []string{
strings.Join(r.Name, ", "), strings.Join(r.Email, ", "),
strings.Join(r.Username, ", "), strings.Join(r.Password, ", "),
strings.Join(r.Address, ", "), strings.Join(r.Phone, ", "),
strings.Join(r.Social, ", "), strings.Join(r.CryptoCurrencyAddress, ", "),
strings.Join(r.Company, ", ")})
}
}
// Print Table
pretty.Table(headers, rows)
} else {
if dh.debug {
debug.PrintInfo("extracting credentials")
}
creds := results.ExtractCredentials()
if dh.debug {
debug.PrintInfo("writing credentials to file")
}
err := export.WriteCredsToFile(creds, dh.options.OutputFile, dh.options.OutputFormat)
if err != nil {
fmt.Printf("\n[!] Error Writing to file: %v\n\tOutputting to terminal.", err)
data, err = json.MarshalIndent(creds, "", " ")
fmt.Println(string(data))
os.Exit(0)
fmt.Printf("[!] Error Writing to file: %v\n Outputting to terminal.", err)
zap.L().Error("write_creds",
zap.String("message", "failed to write creds to file"),
zap.Error(err),
)
} else {
fmt.Println("\n\t\t[*] Success\n")
fmt.Println(" [*] Success")
}
if dh.debug {
debug.PrintInfo("printing credentials table")
}
headers = []string{"Email", "Username", "Password"}
if len(creds) > 50 {
fmt.Println(" [-] Large number of results recovered, displaying first 50...")
for i := 0; i < 50; i++ {
c := creds[i]
rows = append(rows, []string{c.Email, c.Username, c.Password})
}
} else {
for _, c := range creds {
rows = append(rows, []string{c.Email, c.Username, c.Password})
}
}
// Print Table
pretty.Table(headers, rows)
}
} else {
fmt.Println(" [-] No results found")
}
}
+17
View File
@@ -628,3 +628,20 @@ func StoreWhoisLookup(lookup []LookupResult) error {
return lastErr
}
// ReverseWhoisResponse represents the response from a reverse WHOIS lookup
type ReverseWhoisResponse struct {
RemainingCredits int `json:"remaining_credits"`
Data ReverseWhoisData `json:"data"`
}
// ReverseWhoisData contains the domain count and list from a reverse WHOIS lookup
type ReverseWhoisData struct {
DomainsCount int `json:"domainsCount"`
DomainsList []string `json:"domainsList"`
NextPageSearchAfter *string `json:"nextPageSearchAfter"`
}
func (rwd ReverseWhoisData) String() string {
return fmt.Sprintf("Domains Count: %d\nDomains List: %v\nNext Page Search After: %v\n", rwd.DomainsCount, rwd.DomainsList, rwd.NextPageSearchAfter)
}
+32 -7
View File
@@ -298,7 +298,9 @@ func (w *DehashedWhoIs) WhoisHistory(domain string) ([]sqlite.HistoryRecord, err
return whois.Data.Records, nil
}
func (w *DehashedWhoIs) ReverseWHOIS(include []string, exclude []string, reverseType string) (string, error) {
func (w *DehashedWhoIs) ReverseWHOIS(include []string, exclude []string, reverseType string) (sqlite.ReverseWhoisData, error) {
var whois sqlite.ReverseWhoisData
if w.debug {
debug.PrintInfo("performing reverse whois search")
zap.L().Info("reverse_whois_debug",
@@ -329,7 +331,7 @@ func (w *DehashedWhoIs) ReverseWHOIS(include []string, exclude []string, reverse
zap.String("message", "failed to create request"),
zap.Error(err),
)
return "", err
return whois, err
}
req.Header.Set("Content-Type", "application/json")
@@ -356,7 +358,7 @@ func (w *DehashedWhoIs) ReverseWHOIS(include []string, exclude []string, reverse
zap.String("message", "failed to perform request"),
zap.Error(err),
)
return "", err
return whois, err
}
if res == nil {
if w.debug {
@@ -365,7 +367,7 @@ func (w *DehashedWhoIs) ReverseWHOIS(include []string, exclude []string, reverse
zap.L().Error("reverse_whois",
zap.String("message", "response was nil"),
)
return "", errors.New("response was nil")
return whois, errors.New("response was nil")
}
b, err := io.ReadAll(res.Body)
@@ -378,7 +380,7 @@ func (w *DehashedWhoIs) ReverseWHOIS(include []string, exclude []string, reverse
zap.String("message", "failed to read response body"),
zap.Error(err),
)
return "", err
return whois, err
}
// Check for HTTP status code errors
@@ -396,7 +398,7 @@ func (w *DehashedWhoIs) ReverseWHOIS(include []string, exclude []string, reverse
zap.String("error", dhErr.Error()),
zap.String("body_error", string(b)),
)
return "", &dhErr
return whois, &dhErr
}
if w.debug {
@@ -404,7 +406,30 @@ func (w *DehashedWhoIs) ReverseWHOIS(include []string, exclude []string, reverse
debug.PrintJson(fmt.Sprintf("Body: %s\n", string(b[:])))
}
return string(b), nil
var whoisResponse sqlite.ReverseWhoisResponse
err = json.Unmarshal(b, &whoisResponse)
if err != nil {
if w.debug {
debug.PrintInfo("failed to unmarshal response body")
debug.PrintError(err)
}
zap.L().Error("reverse_whois",
zap.String("message", "failed to unmarshal response body"),
zap.Error(err),
)
return whois, err
}
if w.debug {
debug.PrintInfo("unmarshalled response body")
debug.PrintJson(fmt.Sprintf("Remaining Credits: %d\n", whoisResponse.RemainingCredits))
debug.PrintJson(fmt.Sprintf("Data: %v\n", whoisResponse.Data))
}
w.balance = whoisResponse.RemainingCredits
whois = whoisResponse.Data
return whois, nil
}
func (w *DehashedWhoIs) WhoisIP(ipAddress string) ([]sqlite.LookupResult, error) {