package operations import ( "fmt" "net" "strings" "time" "service-operation/types" ) type DNSOperation struct { timeout time.Duration } func NewDNSOperation(timeout time.Duration) *DNSOperation { return &DNSOperation{timeout: timeout} } func (d *DNSOperation) Execute(host, query string) (*types.OperationResult, error) { // Validate inputs if host == "" { return nil, fmt.Errorf("host cannot be empty") } if query == "" { query = "A" // Default to A record } result := &types.OperationResult{ Type: types.OperationDNS, Host: host, DNSType: query, StartTime: time.Now(), } start := time.Now() // Resolve the host first to get detailed info var resolvedIPs []string var err error switch strings.ToUpper(query) { case "A": resolvedIPs, err = d.performARecordLookup(host) case "AAAA": resolvedIPs, err = d.performAAAARecordLookup(host) case "MX": resolvedIPs, err = d.performMXRecordLookup(host) case "TXT": resolvedIPs, err = d.performTXTRecordLookup(host) case "CNAME": resolvedIPs, err = d.performCNAMERecordLookup(host) case "NS": resolvedIPs, err = d.performNSRecordLookup(host) default: // Default to A record lookup for unknown types resolvedIPs, err = d.performARecordLookup(host) } result.ResponseTime = time.Since(start) result.EndTime = time.Now() if err != nil { result.Error = err.Error() result.Success = false result.Details = d.createDetailedErrorMessage(err.Error(), host, query) } else { result.DNSRecords = resolvedIPs result.Success = len(resolvedIPs) > 0 if result.Success { result.Details = d.createDetailedSuccessMessage(result, host, query, resolvedIPs) } else { result.Error = "No DNS records found" result.Details = d.createDetailedErrorMessage("no records found", host, query) } } return result, nil } func (d *DNSOperation) performARecordLookup(host string) ([]string, error) { ips, err := net.LookupIP(host) if err != nil { return nil, err } var ipv4Records []string for _, ip := range ips { if ipv4 := ip.To4(); ipv4 != nil { ipv4Records = append(ipv4Records, ipv4.String()) } } return ipv4Records, nil } func (d *DNSOperation) performAAAARecordLookup(host string) ([]string, error) { ips, err := net.LookupIP(host) if err != nil { return nil, err } var ipv6Records []string for _, ip := range ips { if ipv6 := ip.To16(); ipv6 != nil && ip.To4() == nil { ipv6Records = append(ipv6Records, ipv6.String()) } } return ipv6Records, nil } func (d *DNSOperation) performMXRecordLookup(host string) ([]string, error) { mxRecords, err := net.LookupMX(host) if err != nil { return nil, err } var records []string for _, mx := range mxRecords { records = append(records, fmt.Sprintf("%s (priority: %d)", mx.Host, mx.Pref)) } return records, nil } func (d *DNSOperation) performTXTRecordLookup(host string) ([]string, error) { txtRecords, err := net.LookupTXT(host) if err != nil { return nil, err } return txtRecords, nil } func (d *DNSOperation) performCNAMERecordLookup(host string) ([]string, error) { cname, err := net.LookupCNAME(host) if err != nil { return nil, err } return []string{cname}, nil } func (d *DNSOperation) performNSRecordLookup(host string) ([]string, error) { nsRecords, err := net.LookupNS(host) if err != nil { return nil, err } var records []string for _, ns := range nsRecords { records = append(records, ns.Host) } return records, nil } func (d *DNSOperation) createDetailedSuccessMessage(result *types.OperationResult, host, queryType string, records []string) string { var details strings.Builder // Success indicator with basic info details.WriteString(fmt.Sprintf("🟢 DNS SUCCESS - %s query for %s", strings.ToUpper(queryType), host)) // Response time details.WriteString(fmt.Sprintf(" | Response time: %.2fms", float64(result.ResponseTime.Nanoseconds())/1000000)) // Record count details.WriteString(fmt.Sprintf(" | Records found: %d", len(records))) // Show first few records for context if len(records) > 0 { details.WriteString(" | ") if len(records) <= 3 { details.WriteString(fmt.Sprintf("Results: %s", strings.Join(records, ", "))) } else { details.WriteString(fmt.Sprintf("Results: %s... (+%d more)", strings.Join(records[:3], ", "), len(records)-3)) } } return details.String() } func (d *DNSOperation) createDetailedErrorMessage(errorMsg, host, queryType string) string { var details strings.Builder errorLower := strings.ToLower(errorMsg) if strings.Contains(errorLower, "timeout") { details.WriteString("⏱️ DNS TIMEOUT - Query timed out") } else if strings.Contains(errorLower, "no such host") || strings.Contains(errorLower, "host not found") { details.WriteString("🔍 HOST NOT FOUND - DNS resolution failed") } else if strings.Contains(errorLower, "no answer") || strings.Contains(errorLower, "no records found") { details.WriteString("📝 NO RECORDS - No DNS records of requested type") } else if strings.Contains(errorLower, "server failure") { details.WriteString("🔧 SERVER FAILURE - DNS server error") } else if strings.Contains(errorLower, "refused") { details.WriteString("🚫 QUERY REFUSED - DNS server refused query") } else { details.WriteString("❌ DNS FAILED - Query error") } // Add query details details.WriteString(fmt.Sprintf(" | Query: %s %s", strings.ToUpper(queryType), host)) // Add specific error details if errorMsg != "" { details.WriteString(fmt.Sprintf(" | Error: %s", d.getShortErrorMessage(errorMsg))) } return details.String() } func (d *DNSOperation) getShortErrorMessage(errorMessage string) string { if errorMessage == "" { return "Unknown error" } errorLower := strings.ToLower(errorMessage) if strings.Contains(errorLower, "timeout") { return "Query timeout" } else if strings.Contains(errorLower, "no such host") { return "Host not found" } else if strings.Contains(errorLower, "no answer") { return "No records found" } else if strings.Contains(errorLower, "server failure") { return "DNS server failure" } else if strings.Contains(errorLower, "refused") { return "Query refused" } else if strings.Contains(errorLower, "network unreachable") { return "Network unreachable" } // For other errors, take first 50 characters and clean it up shortMsg := errorMessage if len(shortMsg) > 50 { shortMsg = shortMsg[:50] + "..." } return shortMsg }