Files
god-eye/internal/dns/resolver.go
Vyntral 14c26dc726 feat: Add Multi-Agent AI Orchestration with 8 specialized agents
- Implement 8 specialized AI agents (XSS, SQLi, Auth, API, Crypto, Secrets, Headers, General)
- Add fast type-based routing for finding classification
- Include OWASP-aligned knowledge bases per agent
- Add agent handoff logic for cross-vulnerability detection
- Optimize timeouts and parallelism for local LLM
- Add new modules: cache, network, fingerprint, secrets, cloud, API, discovery
- Update documentation with multi-agent feature
2025-11-21 15:23:11 +01:00

283 lines
6.2 KiB
Go

package dns
import (
"context"
"fmt"
"strings"
"time"
"github.com/miekg/dns"
"god-eye/internal/cache"
"god-eye/internal/config"
"god-eye/internal/retry"
)
// ResolveSubdomain resolves a subdomain to IP addresses with retry logic
func ResolveSubdomain(subdomain string, resolvers []string, timeout int) []string {
return ResolveSubdomainWithRetry(subdomain, resolvers, timeout, true)
}
// ResolveSubdomainWithRetry resolves with optional retry
func ResolveSubdomainWithRetry(subdomain string, resolvers []string, timeout int, useRetry bool) []string {
c := dns.Client{
Timeout: time.Duration(timeout) * time.Second,
}
m := dns.Msg{}
m.SetQuestion(dns.Fqdn(subdomain), dns.TypeA)
// Try each resolver
for _, resolver := range resolvers {
var ips []string
if useRetry {
// Use retry logic
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(timeout*2)*time.Second)
result := retry.Do(ctx, retry.DNSConfig(), func() (interface{}, error) {
r, _, err := c.Exchange(&m, resolver)
if err != nil {
return nil, err
}
if r == nil {
return nil, fmt.Errorf("nil response")
}
var resolvedIPs []string
for _, ans := range r.Answer {
if a, ok := ans.(*dns.A); ok {
resolvedIPs = append(resolvedIPs, a.A.String())
}
}
if len(resolvedIPs) == 0 {
return nil, fmt.Errorf("no A records")
}
return resolvedIPs, nil
})
cancel()
if result.Error == nil && result.Value != nil {
ips = result.Value.([]string)
}
} else {
// Direct resolution without retry
r, _, err := c.Exchange(&m, resolver)
if err == nil && r != nil {
for _, ans := range r.Answer {
if a, ok := ans.(*dns.A); ok {
ips = append(ips, a.A.String())
}
}
}
}
if len(ips) > 0 {
return ips
}
}
return nil
}
func CheckWildcard(domain string, resolvers []string) []string {
// Test multiple random patterns for better wildcard detection
patterns := []string{
fmt.Sprintf("random%d.%s", time.Now().UnixNano(), domain),
fmt.Sprintf("xyz%d.%s", time.Now().UnixNano()%1000000, domain),
fmt.Sprintf("nonexistent-%s.%s", "abc123xyz", domain),
}
allIPs := make(map[string]int)
for _, pattern := range patterns {
ips := ResolveSubdomain(pattern, resolvers, 3)
for _, ip := range ips {
allIPs[ip]++
}
}
// If same IP(s) appear in multiple patterns, it's a wildcard
var wildcardIPs []string
for ip, count := range allIPs {
if count >= 2 {
wildcardIPs = append(wildcardIPs, ip)
}
}
return wildcardIPs
}
func ResolveCNAME(subdomain string, resolvers []string, timeout int) string {
c := dns.Client{
Timeout: time.Duration(timeout) * time.Second,
}
m := dns.Msg{}
m.SetQuestion(dns.Fqdn(subdomain), dns.TypeCNAME)
for _, resolver := range resolvers {
r, _, err := c.Exchange(&m, resolver)
if err != nil || r == nil {
continue
}
for _, ans := range r.Answer {
if cname, ok := ans.(*dns.CNAME); ok {
return strings.TrimSuffix(cname.Target, ".")
}
}
}
return ""
}
func ResolvePTR(ip string, resolvers []string, timeout int) string {
c := dns.Client{
Timeout: time.Duration(timeout) * time.Second,
}
// Convert IP to reverse DNS format
parts := strings.Split(ip, ".")
if len(parts) != 4 {
return ""
}
reverseIP := fmt.Sprintf("%s.%s.%s.%s.in-addr.arpa.", parts[3], parts[2], parts[1], parts[0])
m := dns.Msg{}
m.SetQuestion(reverseIP, dns.TypePTR)
for _, resolver := range resolvers {
r, _, err := c.Exchange(&m, resolver)
if err != nil || r == nil {
continue
}
for _, ans := range r.Answer {
if ptr, ok := ans.(*dns.PTR); ok {
return strings.TrimSuffix(ptr.Ptr, ".")
}
}
}
return ""
}
func ResolveMX(domain string, resolvers []string, timeout int) []string {
c := dns.Client{
Timeout: time.Duration(timeout) * time.Second,
}
m := dns.Msg{}
m.SetQuestion(dns.Fqdn(domain), dns.TypeMX)
for _, resolver := range resolvers {
r, _, err := c.Exchange(&m, resolver)
if err != nil || r == nil {
continue
}
var records []string
for _, ans := range r.Answer {
if mx, ok := ans.(*dns.MX); ok {
records = append(records, strings.TrimSuffix(mx.Mx, "."))
}
}
if len(records) > 0 {
return records
}
}
return nil
}
func ResolveTXT(domain string, resolvers []string, timeout int) []string {
c := dns.Client{
Timeout: time.Duration(timeout) * time.Second,
}
m := dns.Msg{}
m.SetQuestion(dns.Fqdn(domain), dns.TypeTXT)
for _, resolver := range resolvers {
r, _, err := c.Exchange(&m, resolver)
if err != nil || r == nil {
continue
}
var records []string
for _, ans := range r.Answer {
if txt, ok := ans.(*dns.TXT); ok {
for _, t := range txt.Txt {
// Limit length for display
if len(t) > 100 {
t = t[:97] + "..."
}
records = append(records, t)
}
}
}
if len(records) > 0 {
return records
}
}
return nil
}
func ResolveNS(domain string, resolvers []string, timeout int) []string {
c := dns.Client{
Timeout: time.Duration(timeout) * time.Second,
}
m := dns.Msg{}
m.SetQuestion(dns.Fqdn(domain), dns.TypeNS)
for _, resolver := range resolvers {
r, _, err := c.Exchange(&m, resolver)
if err != nil || r == nil {
continue
}
var records []string
for _, ans := range r.Answer {
if ns, ok := ans.(*dns.NS); ok {
records = append(records, strings.TrimSuffix(ns.Ns, "."))
}
}
if len(records) > 0 {
return records
}
}
return nil
}
// GetIPInfo retrieves IP geolocation info with caching (10x faster for repeated IPs)
func GetIPInfo(ip string) (*config.IPInfo, error) {
return cache.GetIPInfoCached(ip)
}
// GetIPInfoBatch retrieves IP info for multiple IPs efficiently
// Uses LRU cache and batches uncached lookups
func GetIPInfoBatch(ips []string) map[string]*config.IPInfo {
return cache.BatchIPLookup(ips)
}
// ResolveSubdomainCached resolves with DNS caching
func ResolveSubdomainCached(subdomain string, resolvers []string, timeout int) []string {
dnsCache := cache.GetDNSCache()
// Check cache first
if ips, found := dnsCache.Get(subdomain); found {
return ips
}
// Resolve and cache
ips := ResolveSubdomain(subdomain, resolvers, timeout)
if len(ips) > 0 {
dnsCache.Set(subdomain, ips)
}
return ips
}