mirror of
https://github.com/projectdiscovery/subfinder
synced 2026-06-21 14:05:24 +00:00
319 lines
7.5 KiB
Go
319 lines
7.5 KiB
Go
// Package onyphe logic
|
|
package onyphe
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"net/http"
|
|
"net/url"
|
|
"strconv"
|
|
"time"
|
|
|
|
"github.com/projectdiscovery/subfinder/v2/pkg/subscraping"
|
|
)
|
|
|
|
type OnypheResponse struct {
|
|
Error int `json:"error"`
|
|
Results []Result `json:"results"`
|
|
Page int `json:"page"`
|
|
PageSize int `json:"page_size"`
|
|
Total int `json:"total"`
|
|
MaxPage int `json:"max_page"`
|
|
}
|
|
|
|
type Result struct {
|
|
Subdomains []string `json:"subdomains"`
|
|
Hostname string `json:"hostname"`
|
|
Forward string `json:"forward"`
|
|
Reverse string `json:"reverse"`
|
|
Host string `json:"host"`
|
|
Domain string `json:"domain"`
|
|
}
|
|
|
|
type Source struct {
|
|
apiKeys []string
|
|
timeTaken time.Duration
|
|
errors int
|
|
results int
|
|
requests int
|
|
skipped bool
|
|
}
|
|
|
|
// Run function returns all subdomains found with the service
|
|
func (s *Source) Run(ctx context.Context, domain string, session *subscraping.Session) <-chan subscraping.Result {
|
|
results := make(chan subscraping.Result)
|
|
s.errors = 0
|
|
s.results = 0
|
|
s.requests = 0
|
|
|
|
go func() {
|
|
defer func(startTime time.Time) {
|
|
s.timeTaken = time.Since(startTime)
|
|
close(results)
|
|
}(time.Now())
|
|
|
|
randomApiKey := subscraping.PickRandom(s.apiKeys, s.Name())
|
|
if randomApiKey == "" {
|
|
s.skipped = true
|
|
return
|
|
}
|
|
|
|
headers := map[string]string{"Content-Type": "application/json", "Authorization": "bearer " + randomApiKey}
|
|
|
|
page := 1
|
|
pageSize := 1000
|
|
|
|
for {
|
|
select {
|
|
case <-ctx.Done():
|
|
return
|
|
default:
|
|
}
|
|
var resp *http.Response
|
|
var err error
|
|
|
|
urlWithQuery := fmt.Sprintf("https://www.onyphe.io/api/v2/search/?q=%s&page=%d&size=%d",
|
|
url.QueryEscape("category:resolver domain:"+domain), page, pageSize)
|
|
s.requests++
|
|
resp, err = session.Get(ctx, urlWithQuery, "", headers)
|
|
|
|
if err != nil {
|
|
results <- subscraping.Result{Source: s.Name(), Type: subscraping.Error, Error: err}
|
|
s.errors++
|
|
session.DiscardHTTPResponse(resp)
|
|
return
|
|
}
|
|
|
|
var respOnyphe OnypheResponse
|
|
err = json.NewDecoder(resp.Body).Decode(&respOnyphe)
|
|
if err != nil {
|
|
results <- subscraping.Result{Source: s.Name(), Type: subscraping.Error, Error: err}
|
|
s.errors++
|
|
session.DiscardHTTPResponse(resp)
|
|
return
|
|
}
|
|
|
|
session.DiscardHTTPResponse(resp)
|
|
|
|
for _, record := range respOnyphe.Results {
|
|
select {
|
|
case <-ctx.Done():
|
|
return
|
|
default:
|
|
}
|
|
for _, subdomain := range record.Subdomains {
|
|
if subdomain != "" {
|
|
results <- subscraping.Result{Source: s.Name(), Type: subscraping.Subdomain, Value: subdomain}
|
|
s.results++
|
|
}
|
|
}
|
|
|
|
if record.Hostname != "" {
|
|
results <- subscraping.Result{Source: s.Name(), Type: subscraping.Subdomain, Value: record.Hostname}
|
|
s.results++
|
|
}
|
|
|
|
if record.Forward != "" {
|
|
results <- subscraping.Result{Source: s.Name(), Type: subscraping.Subdomain, Value: record.Forward}
|
|
s.results++
|
|
}
|
|
|
|
if record.Reverse != "" {
|
|
results <- subscraping.Result{Source: s.Name(), Type: subscraping.Subdomain, Value: record.Reverse}
|
|
s.results++
|
|
}
|
|
}
|
|
|
|
if len(respOnyphe.Results) == 0 || page >= respOnyphe.MaxPage {
|
|
break
|
|
}
|
|
|
|
page++
|
|
|
|
}
|
|
}()
|
|
|
|
return results
|
|
}
|
|
|
|
// Name returns the name of the source
|
|
func (s *Source) Name() string {
|
|
return "onyphe"
|
|
}
|
|
|
|
func (s *Source) IsDefault() bool {
|
|
return true
|
|
}
|
|
|
|
func (s *Source) HasRecursiveSupport() bool {
|
|
return false
|
|
}
|
|
|
|
func (s *Source) KeyRequirement() subscraping.KeyRequirement {
|
|
return subscraping.RequiredKey
|
|
}
|
|
|
|
func (s *Source) NeedsKey() bool {
|
|
return s.KeyRequirement() == subscraping.RequiredKey
|
|
}
|
|
|
|
func (s *Source) AddApiKeys(keys []string) {
|
|
s.apiKeys = keys
|
|
}
|
|
|
|
func (s *Source) Statistics() subscraping.Statistics {
|
|
return subscraping.Statistics{
|
|
Errors: s.errors,
|
|
Results: s.results,
|
|
Requests: s.requests,
|
|
TimeTaken: s.timeTaken,
|
|
Skipped: s.skipped,
|
|
}
|
|
}
|
|
|
|
type OnypheResponseRaw struct {
|
|
Error int `json:"error"`
|
|
Results []Result `json:"results"`
|
|
Page json.RawMessage `json:"page"`
|
|
PageSize json.RawMessage `json:"page_size"`
|
|
Total json.RawMessage `json:"total"`
|
|
MaxPage json.RawMessage `json:"max_page"`
|
|
}
|
|
|
|
func (o *OnypheResponse) UnmarshalJSON(data []byte) error {
|
|
var raw OnypheResponseRaw
|
|
if err := json.Unmarshal(data, &raw); err != nil {
|
|
return err
|
|
}
|
|
|
|
o.Error = raw.Error
|
|
o.Results = raw.Results
|
|
|
|
if pageStr := string(raw.Page); pageStr != "" {
|
|
if page, err := strconv.Atoi(pageStr); err == nil {
|
|
o.Page = page
|
|
} else {
|
|
var pageStrQuoted string
|
|
if err := json.Unmarshal(raw.Page, &pageStrQuoted); err == nil {
|
|
if page, err := strconv.Atoi(pageStrQuoted); err == nil {
|
|
o.Page = page
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if pageSizeStr := string(raw.PageSize); pageSizeStr != "" {
|
|
if pageSize, err := strconv.Atoi(pageSizeStr); err == nil {
|
|
o.PageSize = pageSize
|
|
} else {
|
|
var pageSizeStrQuoted string
|
|
if err := json.Unmarshal(raw.PageSize, &pageSizeStrQuoted); err == nil {
|
|
if pageSize, err := strconv.Atoi(pageSizeStrQuoted); err == nil {
|
|
o.PageSize = pageSize
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if totalStr := string(raw.Total); totalStr != "" {
|
|
if total, err := strconv.Atoi(totalStr); err == nil {
|
|
o.Total = total
|
|
} else {
|
|
var totalStrQuoted string
|
|
if err := json.Unmarshal(raw.Total, &totalStrQuoted); err == nil {
|
|
if total, err := strconv.Atoi(totalStrQuoted); err == nil {
|
|
o.Total = total
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if maxPageStr := string(raw.MaxPage); maxPageStr != "" {
|
|
if maxPage, err := strconv.Atoi(maxPageStr); err == nil {
|
|
o.MaxPage = maxPage
|
|
} else {
|
|
var maxPageStrQuoted string
|
|
if err := json.Unmarshal(raw.MaxPage, &maxPageStrQuoted); err == nil {
|
|
if maxPage, err := strconv.Atoi(maxPageStrQuoted); err == nil {
|
|
o.MaxPage = maxPage
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
type ResultRaw struct {
|
|
Subdomains json.RawMessage `json:"subdomains"`
|
|
Hostname json.RawMessage `json:"hostname"`
|
|
Forward json.RawMessage `json:"forward"`
|
|
Reverse json.RawMessage `json:"reverse"`
|
|
Host json.RawMessage `json:"host"`
|
|
Domain json.RawMessage `json:"domain"`
|
|
}
|
|
|
|
func (r *Result) UnmarshalJSON(data []byte) error {
|
|
var raw ResultRaw
|
|
if err := json.Unmarshal(data, &raw); err != nil {
|
|
return err
|
|
}
|
|
|
|
var subdomains []string
|
|
if err := json.Unmarshal(raw.Subdomains, &subdomains); err == nil {
|
|
r.Subdomains = subdomains
|
|
} else {
|
|
var subdomainStr string
|
|
if err := json.Unmarshal(raw.Subdomains, &subdomainStr); err == nil {
|
|
r.Subdomains = []string{subdomainStr}
|
|
}
|
|
}
|
|
|
|
if len(raw.Hostname) > 0 {
|
|
var hostnameStr string
|
|
if err := json.Unmarshal(raw.Hostname, &hostnameStr); err == nil {
|
|
r.Hostname = hostnameStr
|
|
} else {
|
|
var hostnameArr []string
|
|
if err := json.Unmarshal(raw.Hostname, &hostnameArr); err == nil && len(hostnameArr) > 0 {
|
|
r.Hostname = hostnameArr[0]
|
|
}
|
|
}
|
|
}
|
|
|
|
if len(raw.Forward) > 0 {
|
|
_ = json.Unmarshal(raw.Forward, &r.Forward)
|
|
}
|
|
|
|
if len(raw.Reverse) > 0 {
|
|
_ = json.Unmarshal(raw.Reverse, &r.Reverse)
|
|
}
|
|
|
|
if len(raw.Host) > 0 {
|
|
var hostStr string
|
|
if err := json.Unmarshal(raw.Host, &hostStr); err == nil {
|
|
r.Host = hostStr
|
|
} else {
|
|
var hostArr []string
|
|
if err := json.Unmarshal(raw.Host, &hostArr); err == nil && len(hostArr) > 0 {
|
|
r.Host = hostArr[0]
|
|
}
|
|
}
|
|
}
|
|
|
|
if len(raw.Domain) > 0 {
|
|
var domainStr string
|
|
if err := json.Unmarshal(raw.Domain, &domainStr); err == nil {
|
|
r.Domain = domainStr
|
|
} else {
|
|
var domainArr []string
|
|
if err := json.Unmarshal(raw.Domain, &domainArr); err == nil && len(domainArr) > 0 {
|
|
r.Domain = domainArr[0]
|
|
}
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|