Add intelligent failure categorization and analysis

New DELIVERY FAILURE ANALYSIS section that categorizes bounces:
- Recipient doesn't exist (invalid email addresses)
- Mailbox full (quota exceeded)
- Relay denied (not authorized to send)
- Blocked/Spam filtered (IP/domain blacklisted)
- DNS/Domain issues (domain not found, no MX records)
- Connection failures (timeout, refused)
- Other failures (uncategorized)

Each category shows:
- Count of failures
- Clear explanation of the reason
- Suggested solutions
- Example email addresses affected

Makes it easy to understand WHY emails are failing instead of
showing cryptic log entries.
This commit is contained in:
cschantz
2025-12-31 19:20:49 -05:00
parent 7be2f3bf93
commit 70db264f77
+93 -16
View File
@@ -507,25 +507,102 @@ if [ "$delivered" -gt 0 ]; then
fi fi
if [ "$bounced" -gt 0 ]; then if [ "$bounced" -gt 0 ]; then
print_header "RECENT BOUNCES/FAILURES (last 5 from past $hours hours)" print_header "DELIVERY FAILURE ANALYSIS"
echo ""
print_warning "PROOF - These emails failed delivery:"
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 -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 "" echo ""
# Extract bounce reasons # Get all bounce lines
print_header "COMMON BOUNCE REASONS" 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
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)
mailbox_full=$(grep -ci "mailbox.*full\|quota.*exceeded\|552\|insufficient.*space\|over.*quota" "$TEMP_BOUNCES" 2>/dev/null || echo 0)
relay_denied=$(grep -ci "relay.*denied\|relay.*not.*permitted\|relaying denied\|554.*relay" "$TEMP_BOUNCES" 2>/dev/null || echo 0)
blocked=$(grep -ci "blocked\|blacklist\|550.*spam\|554.*spam\|Policy rejection" "$TEMP_BOUNCES" 2>/dev/null || echo 0)
dns_failure=$(grep -ci "domain.*not.*found\|Host.*unknown\|Name.*not.*resolve\|MX.*not.*found" "$TEMP_BOUNCES" 2>/dev/null || echo 0)
connection_fail=$(grep -ci "timeout\|connection.*refused\|connection.*failed\|Network.*unreachable" "$TEMP_BOUNCES" 2>/dev/null || echo 0)
print_info "Failure breakdown by reason:"
echo "" echo ""
grep -i "$search_pattern" "$TEMP_MATCHES" | grep -v "authenticator failed\|Authentication failed\|saved mail to" | grep -i "550\|551\|bounced\|** " | grep -oE "550 [^(]*|551 [^(]*|User unknown|Mailbox.*full|Relay.*denied|No such user|does not exist" | sort | uniq -c | sort -rn | head -5
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 "" 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 fi
if [ "$rejected" -gt 0 ]; then if [ "$rejected" -gt 0 ]; then
@@ -655,4 +732,4 @@ print_info "Full log saved to: $REPORT_FILE"
echo "" echo ""
# Cleanup # Cleanup
rm -f "$TEMP_MATCHES" "$TEMP_AUTH" "$TEMP_ALL" rm -f "$TEMP_MATCHES" "$TEMP_AUTH" "$TEMP_ALL" "$TEMP_BOUNCES"