51b4dbde1e
Added parameter expansion with defaults to prevent comparison errors
on potentially empty variables:
- live-attack-monitor-v2.sh: IPSET_CREATE_EXIT, IPTABLES_EXIT
- live-attack-monitor.sh: IPSET_CREATE_EXIT, IPTABLES_EXIT
- malware-scanner.sh: START_EXIT
- email-diagnostics.sh: check_type, account_found
Pattern: Changed "$VAR" to "${VAR:-default}" in integer comparisons
to ensure safe comparisons even if variable is unexpectedly empty.
742 lines
28 KiB
Bash
Executable File
742 lines
28 KiB
Bash
Executable File
#!/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 (who this email is sending to)
|
|
if [ "$sent" -gt 0 ]; then
|
|
print_info "Top 5 recipients (emails sent TO):"
|
|
grep -i "<= .*$search_pattern" "$TEMP_MATCHES" | grep -oE "=> [^@]+@[^ ]+" | sed 's/=> //' | sort | uniq -c | sort -rn | head -5 | while read count recipient; do
|
|
echo " $recipient - $count emails"
|
|
done
|
|
echo ""
|
|
fi
|
|
|
|
# Top senders (who is sending to this email)
|
|
if [ "$received" -gt 0 ]; then
|
|
print_info "Top 5 senders (emails received FROM):"
|
|
grep -i "=> .*$search_pattern" "$TEMP_MATCHES" | grep -oE "<= [^@]+@[^ ]+" | sed 's/<= //' | sort | uniq -c | sort -rn | head -5 | while read count sender; do
|
|
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 ""
|
|
grep -i "=> .*$search_pattern\|delivered.*$search_pattern" "$TEMP_MATCHES" | tail -5 | 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
|
|
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')
|
|
blocked=$(grep -ci "blocked\|blacklist\|550.*spam\|554.*spam\|Policy rejection" "$TEMP_BOUNCES" 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 " Solution: Check IP reputation, SPF/DKIM records"
|
|
echo ""
|
|
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 ""
|
|
grep -i "$search_pattern" "$TEMP_MATCHES" | grep -i "spam" | grep -i "rejected\|blocked\|denied" | tail -5 | 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
|
|
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"
|