Files
Linux-Server-Management-Too…/modules/email/email-diagnostics.sh
T
cschantz 89ad050222 Fix critical logic errors in email diagnostics scripts
CRITICAL FIXES (5 issues):
1. email-diagnostics.sh: Fix inverted sender/recipient extraction logic
   - Lines 292-303: Corrected pattern matching to properly extract recipients and senders
   - Removed inverted grep patterns that were looking for wrong log entry types

2. mail-log-analyzer.sh: Fix string comparison with percent sign
   - Line 1184-1186: Properly extract numeric value before '%' character
   - Use sed to isolate leading digits for numeric comparison

3. email-diagnostics.sh: Fix malformed grep syntax
   - Line 525-527: Corrected grep command structure with -e options
   - Changed to -iE with pipe patterns and proper file argument placement

4. mail-log-analyzer.sh: Fix overly broad domain bounce pattern
   - Line 749: Changed from "^.*${domain}" to "\b${domain}$"
   - Prevents false positives from substring domain matches

5. mail-log-analyzer.sh: Fix undefined TEMP_LOG variable
   - Line 860: Changed TEMP_LOG to MAIL_LOG (the actual global variable)
   - Added error handling with 2>/dev/null

HIGH SEVERITY FIXES (2 issues):
6. mail-log-analyzer.sh: Fix AWK uninitialized variable
   - Lines 1447-1456: Added BEGIN block to initialize print_line = 0
   - Prevents first log entries from being incorrectly filtered

7. mail-log-analyzer.sh: Fix overly permissive bounce detection pattern
   - Line 247: Changed from "(==|defer)" to more specific pattern
   - Prevents false positives from non-bounce defer messages

MODERATE FIXES (3 issues):
8. mail-queue-inspector.sh: Fix queue message count mismatch
   - Line 41: Changed head -40 to head -20 to match label

9. deliverability-test.sh: Fix fragile SMTP connection test
   - Lines 102-106: Added nc availability check and fallback to bash TCP
   - Proper variable quoting and error handling

10. blacklist-check.sh: Replace deprecated host command with dig
    - Line 52: Changed from host to dig +short for consistency and timeout control

All scripts pass syntax validation.
Impact: Logic errors fixed, no security issues introduced, all existing functionality preserved.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-02-07 00:39:07 -05:00

1236 lines
50 KiB
Bash
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/bin/bash
################################################################################
# Email Account/Domain Diagnostics
################################################################################
# Purpose: Verify email is working for specific address or domain
# Shows proof of delivery or identifies why emails aren't working
################################################################################
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
source "$SCRIPT_DIR/lib/common-functions.sh"
source "$SCRIPT_DIR/lib/system-detect.sh"
source "$SCRIPT_DIR/lib/email-functions.sh"
show_banner "Email Diagnostics - Verify Email Delivery"
# Get mail log path
MAIL_LOG=$(get_mail_log_path)
if [ ! -f "$MAIL_LOG" ]; then
print_error "Mail log not found: $MAIL_LOG"
exit 1
fi
print_success "Using mail log: $MAIL_LOG"
echo ""
# Ask what to check
echo -e "${BOLD}What would you like to check?${NC}"
echo ""
echo " 1) Specific email address (e.g., user@example.com)"
echo " 2) Entire domain (e.g., example.com)"
echo ""
read -p "Enter choice [1]: " check_type
check_type=${check_type:-1}
# Get email/domain to check
echo ""
if [ "$check_type" = "2" ]; then
read -p "Enter domain to check (e.g., example.com): " target
search_pattern="@${target}"
check_label="domain $target"
else
read -p "Enter email address to check: " target
search_pattern="$target"
check_label="email $target"
fi
if [ -z "$target" ]; then
print_error "No email/domain provided"
exit 1
fi
# Time period to check
echo ""
echo "Check logs from:"
echo " 1) Last 1 hour"
echo " 2) Last 6 hours"
echo " 3) Last 24 hours (recommended)"
echo " 4) Last 48 hours"
echo " 5) Last week"
echo ""
read -p "Enter choice [3]: " time_choice
time_choice=${time_choice:-3}
case "$time_choice" in
1) hours=1 ;;
2) hours=6 ;;
3) hours=24 ;;
4) hours=48 ;;
5) hours=168 ;;
*) hours=24 ;;
esac
echo ""
print_info "Analyzing $check_label for last $hours hours..."
echo ""
# Calculate time cutoff
if date --version 2>&1 | grep -q "GNU"; then
# GNU date
cutoff_time=$(date -d "$hours hours ago" +"%Y-%m-%d %H:%M:%S")
else
# BSD date (macOS)
cutoff_time=$(date -v-${hours}H +"%Y-%m-%d %H:%M:%S")
fi
################################################################################
# Analysis
################################################################################
TEMP_MATCHES="/tmp/email_diag_$$.txt"
TEMP_AUTH="/tmp/email_auth_$$.txt"
TEMP_ALL="/tmp/email_all_$$.txt"
# Search multiple log files for comprehensive results
# Check Exim logs (email delivery)
if [ -f "/var/log/exim_mainlog" ]; then
grep -i "$search_pattern" /var/log/exim_mainlog >> "$TEMP_ALL" 2>/dev/null || true
fi
# Check maillog (Dovecot auth + some delivery)
if [ -f "/var/log/maillog" ]; then
grep -i "$search_pattern" /var/log/maillog >> "$TEMP_ALL" 2>/dev/null || true
fi
# Check messages log (fallback)
if [ -f "/var/log/messages" ]; then
grep -i "$search_pattern" /var/log/messages >> "$TEMP_ALL" 2>/dev/null || true
fi
# If we found nothing, fall back to the detected mail log
if [ ! -s "$TEMP_ALL" ]; then
grep -i "$search_pattern" "$MAIL_LOG" > "$TEMP_ALL" 2>/dev/null || true
fi
# Separate authentication events (IMAP/POP3 logins)
grep -E "imap-login|pop3-login|dovecot.*Login|Logged in|Disconnected" "$TEMP_ALL" > "$TEMP_AUTH" 2>/dev/null || true
# Get only email delivery events (exclude auth logs)
grep -v "imap-login\|pop3-login\|dovecot.*Login\|Logged in\|Disconnected" "$TEMP_ALL" > "$TEMP_MATCHES" 2>/dev/null || true
if [ ! -s "$TEMP_MATCHES" ]; then
print_error "NO EMAIL ACTIVITY FOUND for $check_label"
echo ""
echo "This means:"
echo " • No emails sent TO this $check_label"
echo " • No emails sent FROM this $check_label"
echo " • No delivery attempts logged"
echo ""
print_warning "Possible reasons:"
echo " 1. Email address/domain doesn't exist on this server"
echo " 2. No email activity in the last $hours hours"
echo " 3. Emails are going to a different mail server"
echo ""
# Check if domain exists
if [ "$check_type" = "2" ]; then
if grep -q "$target" /etc/localdomains 2>/dev/null || grep -q "$target" /etc/userdomains 2>/dev/null; then
print_info "Domain $target IS configured on this server"
else
print_warning "Domain $target NOT found in local domains"
fi
fi
rm -f "$TEMP_MATCHES" "$TEMP_AUTH" "$TEMP_ALL"
exit 0
fi
total_lines=$(wc -l < "$TEMP_MATCHES")
print_success "Found $total_lines log entries for $check_label"
echo ""
################################################################################
# Categorize activity
################################################################################
# Count different types first (sanitize to remove newlines)
delivered=$(grep -ci "=> .*$search_pattern\|delivered.*$search_pattern" "$TEMP_MATCHES" 2>/dev/null || echo 0)
delivered=$(echo "$delivered" | head -1 | tr -d '\n\r')
sent=$(grep -ci "<=.*$search_pattern" "$TEMP_MATCHES" 2>/dev/null || echo 0)
sent=$(echo "$sent" | head -1 | tr -d '\n\r')
# Only count actual email bounces, not auth failures or successful deliveries
bounced=$(grep -i "$search_pattern" "$TEMP_MATCHES" | grep -v "authenticator failed\|Authentication failed\|saved mail to\|=>" | grep -ci "550\|551\|552\|553\|554\|bounced\|Mail delivery failed\|** " 2>/dev/null || echo 0)
bounced=$(echo "$bounced" | head -1 | tr -d '\n\r')
deferred=$(grep -ci "deferred.*$search_pattern\|retry.*$search_pattern\|temporarily rejected" "$TEMP_MATCHES" 2>/dev/null || echo 0)
deferred=$(echo "$deferred" | head -1 | tr -d '\n\r')
rejected=$(grep -i "$search_pattern" "$TEMP_MATCHES" | grep -v "authenticator failed\|Authentication failed" | grep -ci "rejected RCPT\|rejected.*relay.*denied\|rejected.*spam" 2>/dev/null || echo 0)
rejected=$(echo "$rejected" | head -1 | tr -d '\n\r')
spf_fail=$(grep -ci "SPF.*fail.*$search_pattern" "$TEMP_MATCHES" 2>/dev/null || echo 0)
spf_fail=$(echo "$spf_fail" | head -1 | tr -d '\n\r')
dkim_fail=$(grep -ci "DKIM.*fail.*$search_pattern" "$TEMP_MATCHES" 2>/dev/null || echo 0)
dkim_fail=$(echo "$dkim_fail" | head -1 | tr -d '\n\r')
# Only count actually rejected spam, not spam delivered to spam folder
spam_rejected=$(grep -i "$search_pattern" "$TEMP_MATCHES" | grep -i "spam" | grep -ci "rejected\|blocked\|denied" 2>/dev/null || echo 0)
spam_rejected=$(echo "$spam_rejected" | head -1 | tr -d '\n\r')
greylist=$(grep -ci "greylist.*$search_pattern\|greylisted.*$search_pattern" "$TEMP_MATCHES" 2>/dev/null || echo 0)
greylist=$(echo "$greylist" | head -1 | tr -d '\n\r')
received=$(grep -ci "=> .*$search_pattern" "$TEMP_MATCHES" 2>/dev/null || echo 0)
received=$(echo "$received" | head -1 | tr -d '\n\r')
# Count authentication events
auth_failed=$(grep -ci "auth failed\|Login aborted\|authentication failed" "$TEMP_AUTH" 2>/dev/null || echo 0)
auth_failed=$(echo "$auth_failed" | head -1 | tr -d '\n\r')
auth_success=$(grep -ci "Logged in\|Login:.*user=.*$search_pattern" "$TEMP_AUTH" 2>/dev/null || echo 0)
auth_success=$(echo "$auth_success" | head -1 | tr -d '\n\r')
################################################################################
# Quick Summary
################################################################################
print_header "QUICK SUMMARY"
echo ""
# Determine quick status
total_problems=$((bounced + rejected + spam_rejected))
total_email_activity=$((sent + received + delivered))
if [ "$total_email_activity" -gt 0 ] && [ "$total_problems" -eq 0 ]; then
print_success "Email appears to be working (${total_email_activity} email events, no problems)"
elif [ "$total_email_activity" -gt 0 ] && [ "$total_problems" -gt 0 ]; then
print_warning "Partial email issues (${total_email_activity} delivered, ${total_problems} failed)"
elif [ "$total_problems" -gt 0 ]; then
print_error "Email delivery problems detected (${total_problems} failures)"
elif [ "$auth_success" -gt 0 ] && [ "$total_email_activity" -eq 0 ]; then
print_info "Account active (${auth_success} logins) but no email traffic in last ${hours}h"
elif [ "$auth_failed" -gt 0 ]; then
print_warning "Failed login attempts detected (${auth_failed} attempts) - possible attack"
else
print_info "No activity detected in last ${hours} hours"
fi
echo ""
################################################################################
# Detailed Activity Breakdown
################################################################################
print_header "EMAIL DELIVERY ACTIVITY (last $hours hours)"
echo ""
# Always show main metrics for clarity
if [ "$received" -gt 0 ]; then
print_success "Received: $received emails (incoming TO this $check_label)"
else
echo "Received: 0 emails (no incoming mail)"
fi
if [ "$sent" -gt 0 ]; then
print_success "Sent: $sent emails (outgoing FROM this $check_label)"
else
echo "Sent: 0 emails (no outgoing mail)"
fi
if [ "$delivered" -gt 0 ]; then
print_success "Delivered: $delivered successful deliveries"
else
echo "Delivered: 0 (no completed deliveries logged)"
fi
echo ""
# Only show problems if they exist
has_problems=0
if [ "$bounced" -gt 0 ]; then
print_error "Bounced: $bounced emails bounced/failed"
has_problems=1
fi
if [ "$deferred" -gt 0 ]; then
print_warning "Deferred: $deferred emails temporarily delayed"
has_problems=1
fi
if [ "$rejected" -gt 0 ]; then
print_error "Rejected: $rejected emails blocked/rejected"
has_problems=1
fi
if [ "$spam_rejected" -gt 0 ]; then
print_error "Spam Rejected: $spam_rejected emails marked as spam"
has_problems=1
fi
if [ "$spf_fail" -gt 0 ]; then
print_warning "SPF Failures: $spf_fail authentication failures"
has_problems=1
fi
if [ "$dkim_fail" -gt 0 ]; then
print_warning "DKIM Failures: $dkim_fail signature failures"
has_problems=1
fi
if [ "$greylist" -gt 0 ]; then
print_info "Greylisted: $greylist emails temporarily delayed (normal anti-spam)"
fi
if [ $has_problems -eq 0 ]; then
print_success "No delivery problems detected"
fi
# Show email traffic patterns
if [ "$sent" -gt 0 ] || [ "$received" -gt 0 ]; then
echo ""
print_header "EMAIL TRAFFIC PATTERNS"
echo ""
# Top recipients (delivery recipients from emails in TEMP_MATCHES)
if [ "$sent" -gt 0 ] || [ "$delivered" -gt 0 ]; then
print_info "Top 5 recipients (emails delivered TO):"
grep -oE "=> [^@]+@[^ ]+" "$TEMP_MATCHES" | sed 's/=> //' | sort | uniq -c | sort -rn | head -5 | while read count recipient; do
[ -n "$count" ] && echo " $recipient - $count emails"
done
echo ""
fi
# Top senders (who is sending emails in TEMP_MATCHES)
if [ "$sent" -gt 0 ]; then
print_info "Top 5 senders (emails sent FROM):"
grep -oE "<= [^@]+@[^ ]+" "$TEMP_MATCHES" | sed 's/<= //' | sort | uniq -c | sort -rn | head -5 | while read count sender; do
[ -n "$count" ] && echo " $sender - $count emails"
done
echo ""
fi
fi
# Show authentication summary
if [ -s "$TEMP_AUTH" ]; then
echo ""
print_header "MAILBOX ACCESS ACTIVITY (IMAP/POP3 Logins)"
echo ""
print_info "This shows if customer can access their mailbox (not email delivery)"
echo ""
if [ "$auth_success" -gt 0 ]; then
print_success "Successful Logins: $auth_success (password is correct, mailbox accessible)"
fi
if [ "$auth_failed" -gt 0 ]; then
print_error "Failed Logins: $auth_failed (WRONG PASSWORD or brute-force attack)"
# Extract IPs attempting failed logins
failed_ips=$(grep -i "auth failed\|Login aborted" "$TEMP_AUTH" | grep -oE "rip=[0-9.]+|from [0-9.]+" | sed 's/rip=//; s/from //' | sort | uniq -c | sort -rn)
if [ -n "$failed_ips" ]; then
echo ""
print_warning "IPs with failed login attempts:"
echo "$failed_ips" | while read count ip; do
echo " $ip - $count attempts"
done
fi
fi
fi
echo ""
################################################################################
# Account Status Check (for specific email addresses)
################################################################################
if [ "$check_type" != "2" ]; then
print_header "EMAIL ACCOUNT STATUS"
echo ""
# Extract username and domain from email
local_part=$(echo "$target" | cut -d'@' -f1)
domain_part=$(echo "$target" | cut -d'@' -f2)
# Check if email account exists in various locations
account_found=0
maildir=""
# Check cPanel mail directory
if [ -d "/home/*/mail/$domain_part/$local_part" ]; then
maildir=$(find /home/*/mail/$domain_part/$local_part -maxdepth 0 -type d 2>/dev/null | head -1)
if [ -n "$maildir" ]; then
account_found=1
fi
fi
# Check Plesk mail directory
if [ $account_found -eq 0 ] && [ -d "/var/qmail/mailnames/$domain_part/$local_part" ]; then
account_found=1
maildir="/var/qmail/mailnames/$domain_part/$local_part"
fi
# Check generic /var/mail
if [ $account_found -eq 0 ] && [ -f "/var/mail/$local_part" ]; then
account_found=1
maildir="/var/mail/$local_part"
fi
# If successful logins exist, account must exist (even if we can't find the directory)
if [ $account_found -eq 0 ] && [ "$auth_success" -gt 0 ]; then
account_found=1
print_success "Email account EXISTS (confirmed by successful logins)"
print_warning "Note: Mailbox directory not found in standard locations"
elif [ $account_found -eq 1 ]; then
print_success "Email account EXISTS on this server"
# Show mailbox details if we found the directory
if [ -n "$maildir" ] && [ -d "$maildir" ]; then
disk_usage=$(du -sh "$maildir" 2>/dev/null | awk '{print $1}')
if [ -n "$disk_usage" ]; then
print_info "Mailbox size: $disk_usage"
fi
# Count messages
msg_count=$(find "$maildir" -type f -name "*:2,*" 2>/dev/null | wc -l)
if [ "$msg_count" -gt 0 ]; then
print_info "Messages stored: $msg_count"
fi
# Check for quota file
quota_file=$(find "$maildir" -name "maildirsize" 2>/dev/null | head -1)
if [ -f "$quota_file" ]; then
quota_info=$(head -1 "$quota_file" 2>/dev/null)
print_info "Quota: $quota_info"
fi
fi
else
print_warning "Email account NOT FOUND on this server"
echo ""
echo "This could mean:"
echo " • Account doesn't exist (typo in email address?)"
echo " • Account is on a different mail server"
echo " • Mail is handled by external service (Google, Office365, etc.)"
fi
# Check for forwarders
if [ -f "/etc/valiases/$domain_part" ]; then
forwarder=$(grep "^$local_part:" "/etc/valiases/$domain_part" 2>/dev/null)
if [ -n "$forwarder" ]; then
echo ""
print_info "Forwarder configured:"
echo " $forwarder"
fi
fi
echo ""
fi
################################################################################
# Show verdict
################################################################################
print_header "DIAGNOSTIC RESULT"
echo ""
# Determine overall status
if [ "$delivered" -gt 0 ] && [ "$bounced" -eq 0 ] && [ "$rejected" -eq 0 ] && [ "$spam_rejected" -eq 0 ]; then
print_success "EMAIL IS WORKING PROPERLY"
echo ""
echo "Evidence: $delivered successful deliveries in the last $hours hours"
echo "No bounces, rejections, or spam filtering detected"
echo ""
elif [ "$delivered" -gt 0 ] && [ "$bounced" -gt 0 ]; then
print_warning "EMAIL PARTIALLY WORKING"
echo ""
echo "Some emails delivered ($delivered) but some failed ($bounced)"
echo "Check bounce reasons below for details"
echo ""
elif [ "$bounced" -gt 0 ] || [ "$rejected" -gt 0 ] || [ "$spam_rejected" -gt 0 ]; then
print_error "EMAIL HAS DELIVERY PROBLEMS"
echo ""
echo "Emails are being rejected, bouncing, or filtered as spam"
echo "See details below for why"
echo ""
elif [ "$deferred" -gt 0 ]; then
print_warning "EMAIL DELAYED"
echo ""
echo "Emails are being deferred (temporary failures)"
echo "This usually resolves itself within minutes"
echo ""
elif [ "$sent" -gt 0 ] || [ "$received" -gt 0 ]; then
print_info "EMAIL ACTIVITY DETECTED"
echo ""
echo "Email traffic logged but no delivery confirmations in last $hours hours"
echo "This may be normal depending on email volume"
echo ""
elif [ "$auth_success" -gt 0 ] || [ "$auth_failed" -gt 0 ]; then
print_info "MAILBOX ACCESS ONLY"
echo ""
echo "No email delivery activity, only mailbox access (IMAP/POP3) detected"
echo "This means the account exists and is being checked, but no emails sent/received"
echo ""
if [ "$auth_success" -gt 0 ]; then
echo "Authentication is working (customer can access mailbox)"
fi
if [ "$auth_failed" -gt 0 ]; then
echo "Failed login attempts detected (see details below)"
fi
echo ""
else
print_warning "NO EMAIL ACTIVITY FOUND"
echo ""
echo "No email sending, receiving, or mailbox access in the last $hours hours"
echo "Account may be inactive or not used during this time period"
echo ""
fi
################################################################################
# Show recent activity samples
################################################################################
if [ "$delivered" -gt 0 ]; then
print_header "RECENT SUCCESSFUL DELIVERIES (last 5 from past $hours hours)"
echo ""
print_info "PROOF - These emails were delivered recently:"
echo ""
while read line; do
# Extract timestamp if present
timestamp=$(echo "$line" | grep -oE '[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}|[A-Z][a-z]{2} [0-9]+ [0-9]{2}:[0-9]{2}:[0-9]{2}' | head -1)
if [ -n "$timestamp" ]; then
echo -e " ${GREEN}[$timestamp]${NC} $line"
else
echo " $line"
fi
done < <(grep -i "=> .*$search_pattern\|delivered.*$search_pattern" "$TEMP_MATCHES" | tail -5)
echo ""
fi
if [ "$bounced" -gt 0 ]; then
print_header "DELIVERY FAILURE ANALYSIS"
echo ""
# Get all bounce lines
TEMP_BOUNCES="/tmp/email_bounces_$$.txt"
grep -i "$search_pattern" "$TEMP_MATCHES" | grep -v "authenticator failed\|Authentication failed\|saved mail to\|=>" | grep -i "550\|551\|552\|553\|554\|bounced\|Mail delivery failed\|** " > "$TEMP_BOUNCES" 2>/dev/null
# Categorize failures (sanitize counts to remove newlines)
recipient_unknown=$(grep -ci "user unknown\|No such user\|does not exist\|recipient rejected\|Recipient address rejected\|550.*User" "$TEMP_BOUNCES" 2>/dev/null || echo 0)
recipient_unknown=$(echo "$recipient_unknown" | head -1 | tr -d '\n\r')
mailbox_full=$(grep -ci "mailbox.*full\|quota.*exceeded\|552\|insufficient.*space\|over.*quota" "$TEMP_BOUNCES" 2>/dev/null || echo 0)
mailbox_full=$(echo "$mailbox_full" | head -1 | tr -d '\n\r')
relay_denied=$(grep -ci "relay.*denied\|relay.*not.*permitted\|relaying denied\|554.*relay" "$TEMP_BOUNCES" 2>/dev/null || echo 0)
relay_denied=$(echo "$relay_denied" | head -1 | tr -d '\n\r')
# Only count actual blacklist/RBL rejections, exclude common false positives
blocked=$(grep -iE "blacklist|block list|RBL|DNSBL|listed in|blocked using|on our block list" -- "$TEMP_BOUNCES" 2>/dev/null | \
grep -v "mailbox.*full\|quota.*exceeded\|authentication\|auth.*failed\|SPF.*fail\|DKIM.*fail\|user unknown\|does not exist\|relay.*denied\|content.*filter\|rejected due to content\|greylisted\|greylist" | \
wc -l 2>/dev/null || echo 0)
blocked=$(echo "$blocked" | head -1 | tr -d '\n\r')
dns_failure=$(grep -ci "domain.*not.*found\|Host.*unknown\|Name.*not.*resolve\|MX.*not.*found" "$TEMP_BOUNCES" 2>/dev/null || echo 0)
dns_failure=$(echo "$dns_failure" | head -1 | tr -d '\n\r')
connection_fail=$(grep -ci "timeout\|connection.*refused\|connection.*failed\|Network.*unreachable" "$TEMP_BOUNCES" 2>/dev/null || echo 0)
connection_fail=$(echo "$connection_fail" | head -1 | tr -d '\n\r')
print_info "Failure breakdown by reason:"
echo ""
if [ "$recipient_unknown" -gt 0 ]; then
print_error " Recipient doesn't exist: $recipient_unknown emails"
echo " Reason: Email address is invalid or doesn't exist on recipient server"
grep -i "user unknown\|No such user\|does not exist\|recipient rejected\|550.*User" "$TEMP_BOUNCES" | head -2 | while read line; do
echo " Example: $(echo "$line" | grep -oE '[^ ]+@[^ ,]+' | head -1)"
done
echo ""
fi
if [ "$mailbox_full" -gt 0 ]; then
print_warning " Mailbox full: $mailbox_full emails"
echo " Reason: Recipient's mailbox has exceeded storage quota"
grep -i "mailbox.*full\|quota.*exceeded\|552" "$TEMP_BOUNCES" | head -2 | while read line; do
echo " Example: $(echo "$line" | grep -oE '[^ ]+@[^ ,]+' | head -1)"
done
echo ""
fi
if [ "$relay_denied" -gt 0 ]; then
print_error " Relay denied: $relay_denied emails"
echo " Reason: Server not authorized to send to this domain"
echo " Solution: Check if server IP needs to be whitelisted"
echo ""
fi
if [ "$blocked" -gt 0 ]; then
print_error " Blocked/Spam filtered: $blocked emails"
echo " Reason: Sender IP or domain is blacklisted, or content flagged as spam"
echo ""
# Extract specific blacklists from rejection messages (strict filter to avoid false positives)
TEMP_BLACKLISTS="/tmp/email_blacklists_$$.txt"
TEMP_BLACKLISTS_FILTERED="/tmp/email_blacklists_filtered_$$.txt"
# Initial extraction with broad pattern
grep -iE "blacklist|block list|RBL|DNSBL|listed in|blocked using|on our block list|S3150|S3140|AS\(48|CS01|local policy|gmail.*(suspicious|reputation|spam|detected).*reputation|gmail.*detected.*suspicious|spamhaus|barracuda|spamcop|sorbs|abuseat|yahoo.*block|yahoo.*reject|aol.*block|aol.*reject|me\.com.*reject|icloud.*reject|mac\.com.*reject|protonmail.*block|protonmail.*reject|pm\.me.*reject|zoho.*block|zoho.*reject|fastmail.*block|fastmail.*reject|outlook.*block|hotmail.*block|live\.com.*block|msn\.com.*block" "$TEMP_BOUNCES" > "$TEMP_BLACKLISTS" 2>/dev/null || true
# ENHANCED: Filter out false positives with strict exclusions
# Exclude negation keywords, question contexts, and non-RBL blocks
if [ -s "$TEMP_BLACKLISTS" ]; then
grep -vE "not blacklist|not listed|NOT listed|no.*longer|removed from|delisted|successfully delisted|you.*can.*now|check if|if.*server|if your|we block|some.*block|unlike|rarely|are rare|except|not.*block|not.*in|but.*policy|policy.*block|firewall|rate limit|internally|internal.*block|local.*block|rejected.*not.*blacklist|based on sender|blocks are" "$TEMP_BLACKLISTS" > "$TEMP_BLACKLISTS_FILTERED" 2>/dev/null || true
# Use filtered version if it has content, otherwise use original
if [ -s "$TEMP_BLACKLISTS_FILTERED" ]; then
mv "$TEMP_BLACKLISTS_FILTERED" "$TEMP_BLACKLISTS"
else
# All messages were false positives, clear the file
> "$TEMP_BLACKLISTS"
fi
fi
# Try to extract server IP from rejection messages
extracted_ip=""
if grep -qiE '\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\]|from [0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}' "$TEMP_BLACKLISTS" 2>/dev/null; then
extracted_ip=$(grep -oE '\[?[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\]?' "$TEMP_BLACKLISTS" 2>/dev/null | head -1 | tr -d '[]')
fi
if [ -s "$TEMP_BLACKLISTS" ]; then
# Blacklist/Provider detection with real-world message patterns
# Format: "name|display_name|removal_url|detection_keywords"
blacklist_db=(
# Traditional RBLs - Format: "id|name|url|patterns|difficulty|avg_time"
"spamhaus|Spamhaus (ZEN/SBL/XBL)|https://check.spamhaus.org/|spamhaus|sbl.spamhaus|zen.spamhaus|xbl.spamhaus|pbl.spamhaus|HARD|1-7 days"
"barracuda|Barracuda Central BRBL|https://www.barracudacentral.org/rbl/removal-request|barracuda|MODERATE|1-3 days"
"spamcop|SpamCop Blocking List|https://www.spamcop.net/bl.shtml|spamcop|bl.spamcop|EASY|Same day"
"sorbs|SORBS DNSBL|http://www.sorbs.net/lookup.shtml|sorbs|dnsbl.sorbs|MODERATE|1-2 days"
"cbl|CBL (Composite Block List)|https://cbl.abuseat.org/lookup.cgi|cbl.abuseat|abuseat|MODERATE|1-3 days"
"psbl|PSBL (Passive Spam Block List)|https://psbl.org/|psbl.surriel|psbl|MODERATE|1-2 days"
"uceprotect|UCEPROTECT Network|http://www.uceprotect.net/en/rblcheck.php|uceprotect|HARD|3-7 days"
"invaluement|Invaluement DNSBL|http://www.invaluement.com/removal/|invaluement|MODERATE|1-3 days"
"mailspike|Mailspike Blacklist|https://mailspike.net/anubis/lookup.html|mailspike|EASY|Same day"
"truncate|GBUdb (Truncate)|http://www.gbudb.com/|truncate.gbudb|gbudb|EASY|Same day"
"dnsrbl|DNSRBL.org|http://www.dnsrbl.org/|dnsrbl|MODERATE|1-2 days"
"backscatterer|Backscatterer.org|http://www.backscatterer.org/|backscatterer|EASY|Same day"
"dnswl|DNSWL (actually whitelist)|https://www.dnswl.org/|dnswl|N/A|N/A"
"mxtoolbox|MXToolbox Blacklist|https://mxtoolbox.com/blacklists.aspx|mxtoolbox|EASY|Same day"
# Major Email Providers (not traditional RBLs but they block based on reputation)
"microsoft|Microsoft/Outlook/Hotmail/Live Block|https://sendersupport.olc.protection.outlook.com/snds/|outlook.*block|hotmail.*block|live\.com.*block|msn\.com.*block|protection\.outlook.*block|on our block list|S3150|S3140|AS\(48|MODERATE|Same day"
"gmail|Gmail Reputation Filter|https://support.google.com/mail/contact/bulk_send_new|gmail.*suspicious|gmail.*reputation|gmail.*spam|gmail.*blocked|gmail.*detected|EASY|Same day"
"apple|Apple iCloud/me.com/mac.com Block|https://support.apple.com/|local policy|icloud.*reject|me\.com.*reject|mac\.com.*reject|CS01|HARD|3-7 days"
"yahoo|Yahoo/AOL Mail Block|https://senders.yahooinc.com/contact|yahoo.*block|yahoo.*reject|aol.*block|aol.*reject|verizonmedia.*block|MODERATE|1-2 days"
"zoho|Zoho Mail Block|https://www.zoho.com/mail/help/|zoho.*reject|zoho.*block|zohomail.*reject|EASY|Same day"
"protonmail|ProtonMail Block|https://protonmail.com/support/|protonmail.*reject|protonmail.*block|pm\.me.*reject|MODERATE|1-3 days"
"fastmail|Fastmail Block|https://www.fastmail.help/|fastmail.*reject|fastmail.*block|MODERATE|1-2 days"
"att|AT&T/SBC Block List|https://www.att.com/support/|att\.net.*block|sbcglobal.*block|MODERATE|1-3 days"
"comcast|Comcast/Xfinity Block|http://postmaster.comcast.net/|comcast.*block|xfinity.*block|MODERATE|1-3 days"
"cox|Cox Communications Block|https://www.cox.com/residential/support.html|cox\.net.*block|MODERATE|1-3 days"
"verizon|Verizon/Frontier Block|https://www.verizon.com/support/|verizon.*block|frontier.*block|MODERATE|1-3 days"
"spectrum|Spectrum/Charter Block|https://www.spectrum.net/support|spectrum.*block|charter.*block|rr\.com.*block|MODERATE|1-3 days"
)
detected_blacklists=""
# Check each blacklist pattern against rejection messages
for entry in "${blacklist_db[@]}"; do
IFS='|' read -r bl_id bl_name bl_url bl_patterns bl_difficulty bl_time <<< "$entry"
# Split patterns and check each one
matched=0
IFS='|' read -ra PATTERNS <<< "$bl_patterns"
for pattern in "${PATTERNS[@]}"; do
if grep -qiE "$pattern" "$TEMP_BLACKLISTS" 2>/dev/null; then
matched=1
break
fi
done
if [ $matched -eq 1 ]; then
detected_blacklists="${detected_blacklists}${bl_name}|${bl_url}|${bl_difficulty}|${bl_time}\n"
# Record in history database
HISTORY_FILE="$HOME/.email-diagnostics-history.json"
if [ ! -f "$HISTORY_FILE" ]; then
# Initialize history database
echo '{"server_ip":"'$extracted_ip'","events":[],"statistics":{"total_events":0,"unique_blacklists":0,"most_frequent":"N/A","last_clean":"N/A","current_listings":0}}' > "$HISTORY_FILE"
fi
# Append event to history (simple JSON append)
timestamp=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
# Update history with new event (this is simplified - in production would use jq or similar)
echo "# Historical event recorded: $bl_id at $timestamp" >> "$HISTORY_FILE" 2>/dev/null || true
fi
done
if [ -n "$detected_blacklists" ]; then
print_warning " ⚠ SPECIFIC BLACKLISTS/BLOCKS DETECTED:"
echo ""
if [ -n "$extracted_ip" ]; then
print_info " Server IP detected: $extracted_ip"
echo ""
fi
echo -e "$detected_blacklists" | sort -u | while IFS='|' read -r bl_name bl_url bl_difficulty bl_time; do
if [ -n "$bl_name" ]; then
# Format difficulty and time if available
if [ -n "$bl_difficulty" ] && [ -n "$bl_time" ]; then
print_error "$bl_name [$bl_difficulty - $bl_time]"
else
print_error "$bl_name"
fi
# Generate IP-specific lookup URL if IP was extracted
if [ -n "$extracted_ip" ]; then
case "$bl_url" in
*spamhaus.org*)
echo " Lookup: https://check.spamhaus.org/?ip=${extracted_ip}"
;;
*barracudacentral.org*)
echo " Lookup: https://www.barracudacentral.org/rbl/lookup?ip=${extracted_ip}"
;;
*spamcop.net*)
echo " Lookup: https://www.spamcop.net/query.html?ip=${extracted_ip}"
;;
*sorbs.net*)
echo " Lookup: http://www.sorbs.net/lookup.shtml?ip=${extracted_ip}"
;;
*)
echo " Removal/Info: $bl_url"
;;
esac
else
echo " Removal/Info: $bl_url"
fi
echo ""
fi
done
else
# Generic spam filter (not a specific blacklist)
echo " No specific blacklist detected in rejection message"
echo " May be content-based spam filtering or unlisted blacklist"
echo ""
fi
# Show example rejection messages
print_info " 📋 EXAMPLE REJECTION MESSAGES:"
echo ""
head -3 "$TEMP_BLACKLISTS" | while read line; do
# Truncate very long lines
echo " $(echo "$line" | cut -c1-120)"
done
echo ""
fi
echo " 🔧 RECOMMENDED ACTIONS:"
echo " 1. Check your server IP against the detected blacklists above"
echo " 2. Visit removal/delisting URLs to submit requests"
echo " 3. Verify SPF/DKIM/DMARC records are correctly configured"
echo " 4. Check if server has been compromised (sending spam)"
echo " 5. Review mail queue for suspicious outbound emails"
echo ""
# Show removal request templates for detected blacklists
print_info " 📧 REMOVAL REQUEST TEMPLATES:"
echo ""
# Function to generate removal template based on blacklist
generate_removal_template() {
local bl_id="$1"
local server_ip="${2:-YOUR_SERVER_IP}"
local server_hostname="${3:-YOUR_SERVER_HOSTNAME}"
local admin_email="${4:-admin@YOUR_DOMAIN.com}"
case "$bl_id" in
spamhaus)
cat <<'TEMPLATE'
Subject: Delisting Request for [SERVER_IP]
Dear Spamhaus,
I am writing to request removal of our mail server from your blacklist (SBL/PBL/ZEN).
Server Details:
- IP Address: [SERVER_IP]
- Hostname: [SERVER_HOSTNAME]
Actions Taken:
1. Verified server is not an open relay
2. Secured server against compromise and abuse
3. Implemented SPF/DKIM/DMARC authentication records
4. Configured proper mail server policies
We believe our server has been remediated and is no longer a spam source.
Please review and remove our IP from your blocklist.
Thank you,
[ADMIN_NAME]
[ADMIN_EMAIL]
TEMPLATE
;;
microsoft)
cat <<'TEMPLATE'
Subject: Delisting Request - IP [SERVER_IP]
Hello Microsoft Sender Support,
I need to request removal of our mail server IP from the Microsoft/Outlook blocklist.
Server Information:
- IP Address: [SERVER_IP]
- Hostname: [SERVER_HOSTNAME]
- Organization: [YOUR_ORGANIZATION]
Remediation Completed:
1. Resolved the security issue causing the block
2. Verified server authentication (SPF/DKIM/DMARC)
3. Removed malicious content from mail queue
4. Implemented additional security measures
Our server is now compliant with Microsoft sender requirements.
Please restore our sending reputation.
Best regards,
[ADMIN_NAME]
[ADMIN_EMAIL]
TEMPLATE
;;
gmail)
cat <<'TEMPLATE'
Subject: Delisting Request - Mail Server [SERVER_IP]
Hello Gmail Support,
Our mail server [SERVER_IP] has been blocked due to reputation issues.
We have taken corrective action and request review for restoration.
Server Details:
- IP Address: [SERVER_IP]
- Reverse DNS: [SERVER_HOSTNAME]
Actions Taken:
1. Fixed authentication headers and message formatting
2. Implemented DKIM and DMARC signing on all outgoing mail
3. Improved bounce handling procedures
4. Enhanced content filtering for suspicious messages
We are now complying with Gmail's sender requirements.
Please re-evaluate our server reputation.
Thank you,
[ADMIN_NAME]
[ADMIN_EMAIL]
TEMPLATE
;;
apple)
cat <<'TEMPLATE'
Subject: Mail Server Delisting Request - [SERVER_IP]
Hello Apple Support,
Our organization's mail server [SERVER_IP] has been blocked by Apple's
mail filtering system. We have addressed the underlying issues.
Server Information:
- IP Address: [SERVER_IP]
- Hostname: [SERVER_HOSTNAME]
Remediation Steps Completed:
1. Verified message authentication (SPF/DKIM/DMARC records)
2. Reviewed and removed compromised accounts
3. Implemented additional security hardening
4. Configured proper mail server protocols
Our server now fully complies with Apple's sender requirements.
Please review and remove the block.
Sincerely,
[ADMIN_NAME]
[ADMIN_EMAIL]
TEMPLATE
;;
barracuda)
cat <<'TEMPLATE'
Subject: Delisting Request - [SERVER_IP]
Hello Barracuda Central,
Our mail server at [SERVER_IP] is listed in your Barracuda Reputation Block List.
We have remediated the issue and request delisting.
Server Details:
- IP Address: [SERVER_IP]
- Hostname: [SERVER_HOSTNAME]
Corrective Actions:
1. Fixed all authentication record issues (SPF/DKIM)
2. Reviewed and removed spam/malware from queue
3. Disabled any open relay vulnerabilities
4. Verified reverse DNS configuration
We are committed to maintaining sender reputation and complying with best practices.
Please review our request for removal.
Regards,
[ADMIN_NAME]
[ADMIN_EMAIL]
TEMPLATE
;;
yahoo)
cat <<'TEMPLATE'
Subject: Mail Server Delisting - [SERVER_IP]
Hello Yahoo Postmaster,
Our mail server [SERVER_IP] needs to be removed from Yahoo's block list.
We have taken steps to address the issue.
Server Information:
- IP Address: [SERVER_IP]
- Hostname: [SERVER_HOSTNAME]
Steps Taken:
1. Implemented proper email authentication (SPF/DKIM/DMARC)
2. Configured appropriate rate limiting policies
3. Reviewed mail queue for suspicious content
4. Enhanced server security against compromise
Our server is now configured to meet Yahoo's sender guidelines.
We request review of our IP for delisting.
Thank you,
[ADMIN_NAME]
[ADMIN_EMAIL]
TEMPLATE
;;
*)
cat <<'TEMPLATE'
Subject: Delisting Request - Mail Server [SERVER_IP]
Hello [BLOCKLIST_NAME] Support,
We are writing to request removal of our mail server [SERVER_IP] from your blocklist.
Server Details:
- IP Address: [SERVER_IP]
- Hostname: [SERVER_HOSTNAME]
Actions Taken:
1. Identified and fixed the issue causing the listing
2. Implemented proper authentication protocols
3. Enhanced security measures
4. Verified compliance with best practices
Our server is now properly configured and we believe it should be delisted.
Please review our request.
Sincerely,
[ADMIN_NAME]
[ADMIN_EMAIL]
TEMPLATE
;;
esac
}
# Show templates for each detected blacklist
echo -e "$detected_blacklists" | sort -u | while IFS='|' read -r bl_name bl_url bl_difficulty bl_time; do
if [ -n "$bl_name" ]; then
# Extract ID from name (use first word as ID)
bl_id=$(echo "$bl_name" | cut -d' ' -f1 | tr '[:upper:]' '[:lower:]')
echo " ─────────────────────────────────────────────────"
echo " 📧 Template for: $bl_name"
echo " ─────────────────────────────────────────────────"
generate_removal_template "$bl_id" "$extracted_ip" "mail.example.com" "admin@example.com"
echo ""
echo " 💡 Copy-paste instructions:"
echo " 1. Copy the template above"
echo " 2. Replace placeholders: [SERVER_IP], [SERVER_HOSTNAME], [ADMIN_NAME], [ADMIN_EMAIL]"
echo " 3. Submit to: $bl_url"
echo ""
fi
done
# Real-time blacklist status checking (if IP was extracted)
if [ -n "$extracted_ip" ]; then
echo ""
print_info " 🔍 REAL-TIME BLACKLIST STATUS CHECK:"
echo ""
echo " Checking current listing status for: $extracted_ip"
echo ""
# Function to check if IP is currently listed on a blacklist RBL
check_blacklist_listing() {
local ip="$1"
local rbl_host="$2" # e.g., zen.spamhaus.org
local rbl_name="$3" # e.g., Spamhaus
# Reverse the IP octets: 1.2.3.4 → 4.3.2.1
local reversed_ip=$(echo "$ip" | awk -F. '{print $4"."$3"."$2"."$1}')
# Query the RBL with a 3-second timeout
local query="${reversed_ip}.${rbl_host}"
local result=$(dig +short +timeout=3 "$query" A 2>/dev/null | head -1)
if [ -n "$result" ]; then
# IP is listed - return the response code
echo "LISTED:$result"
else
# IP is not listed
echo "CLEAN"
fi
}
# Parse RBL servers from blacklist entries and check each
echo -e "$detected_blacklists" | sort -u | while IFS='|' read -r bl_name bl_url bl_difficulty bl_time; do
if [ -n "$bl_name" ]; then
# Extract RBL hostnames from URLs or use common patterns
case "$bl_name" in
*Spamhaus*)
rbl_host="zen.spamhaus.org"
short_name="Spamhaus"
;;
*Barracuda*)
rbl_host="bl.barracudacentral.org"
short_name="Barracuda"
;;
*SpamCop*)
rbl_host="bl.spamcop.net"
short_name="SpamCop"
;;
*SORBS*)
rbl_host="dnsbl.sorbs.net"
short_name="SORBS"
;;
*CBL*)
rbl_host="cbl.abuseat.org"
short_name="CBL"
;;
*)
# Skip email providers (not traditional RBLs)
continue
;;
esac
# Check current status
status=$(check_blacklist_listing "$extracted_ip" "$rbl_host" "$short_name")
if [[ "$status" == "LISTED"* ]]; then
response_code=$(echo "$status" | cut -d: -f2)
print_error "$short_name: CURRENTLY LISTED"
echo " Response: $response_code (meaning: check RBL for code details)"
echo " Action: Submit delisting request if not already done"
else
print_success "$short_name: NOT LISTED (Clean)"
fi
fi
done
echo ""
echo " 📌 Status Check Notes:"
echo " • DNS lookups may be cached - results reflect current RBL state"
echo " • Some RBLs may not respond within timeout window"
echo " • Check removal URLs above for detailed delisting status"
echo ""
fi
# Show historical statistics if history file exists
HISTORY_FILE="$HOME/.email-diagnostics-history.json"
if [ -f "$HISTORY_FILE" ] && [ -s "$HISTORY_FILE" ]; then
echo ""
print_info " 📊 HISTORICAL BLACKLIST TRACKING:"
echo ""
# Count recorded events from history file (simplified approach)
history_events=$(grep -c "# Historical event recorded:" "$HISTORY_FILE" 2>/dev/null || echo 0)
if [ "$history_events" -gt 0 ]; then
echo " 📈 Blacklist History Summary:"
echo " • Total recorded incidents: $history_events"
echo " • History location: $HISTORY_FILE"
echo ""
echo " 💡 Use this data to identify:"
echo " • Which blacklists repeatedly block your server"
echo " • Patterns in blocking frequency"
echo " • Whether server has a systemic listing problem"
echo ""
echo " 🔍 View detailed history:"
echo " cat $HISTORY_FILE"
echo ""
fi
fi
rm -f "$TEMP_BLACKLISTS"
fi
if [ "$dns_failure" -gt 0 ]; then
print_error " DNS/Domain issues: $dns_failure emails"
echo " Reason: Recipient domain doesn't exist or has no MX records"
grep -i "domain.*not.*found\|Host.*unknown" "$TEMP_BOUNCES" | head -2 | while read line; do
echo " Example: $(echo "$line" | grep -oE '[^ ]+@[^ ,]+' | head -1)"
done
echo ""
fi
if [ "$connection_fail" -gt 0 ]; then
print_warning " Connection failures: $connection_fail emails"
echo " Reason: Could not connect to recipient mail server (temporary)"
echo " Solution: These usually resolve themselves, check if persistent"
echo ""
fi
# Calculate uncategorized
total_categorized=$((recipient_unknown + mailbox_full + relay_denied + blocked + dns_failure + connection_fail))
if [ "$total_categorized" -lt "$bounced" ]; then
other=$((bounced - total_categorized))
print_warning " Other failures: $other emails"
echo " See detailed logs below for specific reasons"
echo ""
fi
# Show recent failures for reference
print_header "RECENT FAILURE EXAMPLES (last 3)"
echo ""
grep -i "$search_pattern" "$TEMP_MATCHES" | grep -v "authenticator failed\|Authentication failed\|saved mail to\|=>" | grep -i "550\|551\|552\|553\|554\|bounced\|Mail delivery failed\|** " | tail -3 | while read line; do
timestamp=$(echo "$line" | grep -oE '[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}|[A-Z][a-z]{2} [0-9]+ [0-9]{2}:[0-9]{2}:[0-9]{2}' | head -1)
if [ -n "$timestamp" ]; then
echo -e " ${RED}[$timestamp]${NC}"
# Extract the key error message
error_msg=$(echo "$line" | grep -oE "550 [^<]*|551 [^<]*|552 [^<]*|User unknown|does not exist|Mailbox full|relay denied" | head -1)
if [ -n "$error_msg" ]; then
echo " Error: $error_msg"
fi
recipient=$(echo "$line" | grep -oE '[^ ]+@[^ ,<>]+' | grep -v "$search_pattern" | head -1)
if [ -n "$recipient" ]; then
echo " To: $recipient"
fi
fi
echo ""
done
rm -f "$TEMP_BOUNCES"
fi
if [ "$rejected" -gt 0 ]; then
print_header "RECENT REJECTIONS (last 5)"
echo ""
grep -i "$search_pattern" "$TEMP_MATCHES" | grep -v "authenticator failed\|Authentication failed" | grep -i "rejected RCPT\|rejected.*relay.*denied\|rejected.*spam" | tail -5 | while read line; do
echo " $line"
done
echo ""
fi
if [ "$spam_rejected" -gt 0 ]; then
print_header "RECENT SPAM REJECTIONS (last 5)"
echo ""
print_info "Emails rejected as spam (not delivered):"
echo ""
while read line; do
timestamp=$(echo "$line" | grep -oE '[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}|[A-Z][a-z]{2} [0-9]+ [0-9]{2}:[0-9]{2}:[0-9]{2}' | head -1)
if [ -n "$timestamp" ]; then
echo -e " ${RED}[$timestamp]${NC} $line"
else
echo " $line"
fi
done < <(grep -i "$search_pattern" "$TEMP_MATCHES" | grep -i "spam" | grep -i "rejected\|blocked\|denied" | tail -5)
echo ""
fi
if [ "$greylist" -gt 0 ]; then
print_header "RECENT GREYLISTING EVENTS (last 5)"
echo ""
print_info "Note: Greylisting is temporary - emails usually deliver after retry"
echo ""
grep -i "greylist" "$TEMP_MATCHES" | tail -5 | while read line; do
echo " $line"
done
echo ""
fi
if [ "$spf_fail" -gt 0 ] || [ "$dkim_fail" -gt 0 ]; then
print_header "AUTHENTICATION ISSUES"
echo ""
if [ "$spf_fail" -gt 0 ]; then
echo "SPF failures detected:"
grep -i "SPF.*fail" "$TEMP_MATCHES" | head -3
echo ""
fi
if [ "$dkim_fail" -gt 0 ]; then
echo "DKIM failures detected:"
grep -i "DKIM.*fail" "$TEMP_MATCHES" | head -3
echo ""
fi
fi
################################################################################
# Recommendations
################################################################################
print_header "RECOMMENDATIONS"
echo ""
if [ "$bounced" -gt 0 ]; then
echo "To fix bounces:"
echo " 1. Verify the recipient email address is correct"
echo " 2. Check if recipient mailbox is full"
echo " 3. Ensure domain DNS is configured properly"
echo ""
fi
if [ "$rejected" -gt 0 ]; then
echo "To fix rejections:"
echo " 1. Check if server IP is blacklisted (use Blacklist Check tool)"
echo " 2. Verify SPF/DKIM/DMARC records are correct"
echo " 3. Check if recipient is blocking your domain"
echo ""
fi
if [ "$spf_fail" -gt 0 ]; then
echo "To fix SPF failures:"
echo " 1. Add this server's IP to domain's SPF record"
echo " 2. Use SPF/DKIM/DMARC Check tool for details"
echo ""
fi
if [ "$spam_rejected" -gt 0 ]; then
echo "To fix spam rejections:"
echo " 1. Check SpamAssassin score and adjust settings"
echo " 2. Verify email content isn't triggering spam filters"
echo " 3. Ensure proper SPF/DKIM authentication"
echo " 4. Check if sending domain has good reputation"
echo ""
fi
if [ "$greylist" -gt 0 ] && [ "$delivered" -eq 0 ]; then
echo "Greylisting delays:"
echo " • This is normal anti-spam behavior"
echo " • Emails should deliver after sender retries (5-15 minutes)"
echo " • No action needed unless emails never arrive"
echo ""
fi
if [ "${check_type:-1}" != "2" ] && [ "${account_found:-0}" -eq 0 ]; then
echo "Email account not found:"
echo " 1. Verify the email address is spelled correctly"
echo " 2. Check if domain DNS points to this server"
echo " 3. Create the email account if it should exist here"
echo " 4. Check if mail is handled by external service"
echo ""
fi
if [ "$auth_failed" -gt 0 ]; then
echo "Failed login attempts detected:"
echo " 1. Customer may be using wrong password"
echo " 2. Could be brute-force attack attempt"
echo " 3. Check IPs - block suspicious ones with firewall"
echo " 4. Advise customer to change password if compromised"
echo ""
fi
################################################################################
# Save full report
################################################################################
REPORT_FILE="/tmp/email_diag_${target//[@.]/_}_$(date +%Y%m%d_%H%M%S).txt"
cp "$TEMP_MATCHES" "$REPORT_FILE"
print_info "Full log saved to: $REPORT_FILE"
echo ""
# Cleanup
rm -f "$TEMP_MATCHES" "$TEMP_AUTH" "$TEMP_ALL" "$TEMP_BOUNCES"