package infra import ( "fmt" "net" "strings" "sync" "time" "arcline-audit/internal/types" ) // CDN ranges for common CDN providers (simplified detection based on IP prefixes). var cdnRanges = map[string][]string{ "Cloudflare": { "104.16.", "104.17.", "104.18.", "104.19.", "104.20.", "104.21.", "104.22.", "104.23.", "104.24.", "104.25.", "104.26.", "104.27.", "104.28.", "104.29.", "104.30.", "104.31.", "172.64.", "172.65.", "172.66.", "172.67.", "172.68.", "172.69.", "172.70.", "172.71.", }, "Fastly": { "151.101.", "199.232.", "23.235.", "146.75.", }, "Amazon CloudFront": { "13.32.", "13.33.", "13.224.", "13.225.", "13.226.", "13.227.", "13.249.", "54.192.", "54.230.", "54.239.", }, } // commonPorts are the ports to probe. var commonPorts = map[int]string{ 80: "HTTP", 443: "HTTPS", 22: "SSH", 3306: "MySQL", 5432: "PostgreSQL", } // Run performs all infrastructure checks for the given domain. // It accepts pre-resolved IPs to avoid redundant lookups; if empty, it resolves the domain itself. func Run(domain string, resolvedIPs []string) types.InfraResult { var result types.InfraResult ips := resolvedIPs if len(ips) == 0 { var err error ips, err = net.LookupHost(domain) if err != nil || len(ips) == 0 { result.Checks = append(result.Checks, types.CheckResult{ Status: types.StatusFail, Message: fmt.Sprintf("cannot resolve domain: %v", err), }) return result } } // Select the first IPv4 address for port scanning, but check all IPs for CDN. targetIP := selectIPv4(ips) // CDN detection (check all IPs) cdn := detectCDNAny(ips) if cdn != "" { result.Checks = append(result.Checks, types.CheckResult{ Status: types.StatusOK, Message: fmt.Sprintf("CDN detected: %s", cdn), }) } else { result.Checks = append(result.Checks, types.CheckResult{ Status: types.StatusOK, Message: "not behind a CDN", }) } result.CDN = cdn // Common ports check result.Checks = append(result.Checks, checkPorts(targetIP)...) return result } func selectIPv4(ips []string) string { for _, ip := range ips { if parsed := net.ParseIP(ip); parsed != nil && parsed.To4() != nil { return ip } } return ips[0] } func detectCDNAny(ips []string) string { for _, ip := range ips { if cdn := detectCDN(ip); cdn != "" { return cdn } } return "" } func detectCDN(ip string) string { for provider, prefixes := range cdnRanges { for _, prefix := range prefixes { if strings.HasPrefix(ip, prefix) { return provider } } } return "" } func checkPorts(ip string) []types.CheckResult { var checks []types.CheckResult var mu sync.Mutex var wg sync.WaitGroup host := ip if strings.Contains(ip, ":") { host = "[" + ip + "]" } for port, name := range commonPorts { wg.Add(1) go func(port int, name string) { defer wg.Done() addr := net.JoinHostPort(host, fmt.Sprintf("%d", port)) conn, err := net.DialTimeout("tcp", addr, 2*time.Second) if err == nil { conn.Close() mu.Lock() checks = append(checks, types.CheckResult{ Status: types.StatusInfo, Message: fmt.Sprintf("port %d (%s) open", port, name), }) mu.Unlock() } }(port, name) } wg.Wait() return checks }