Enhance mailtrace script: add final delivery filtering and improve status output descriptions
This commit is contained in:
67
mailtrace.sh
67
mailtrace.sh
@@ -89,6 +89,7 @@ NO_NOTE=0 # suppress note lines in block output when TLS unknown
|
|||||||
|
|
||||||
DEDUP=0
|
DEDUP=0
|
||||||
DEDUP_PREFER="final" # final|amavis|first|last
|
DEDUP_PREFER="final" # final|amavis|first|last
|
||||||
|
FINAL_ONLY=0
|
||||||
|
|
||||||
SASL_ONLY=0
|
SASL_ONLY=0
|
||||||
NO_SASL=0
|
NO_SASL=0
|
||||||
@@ -99,7 +100,7 @@ DEBUG=0
|
|||||||
|
|
||||||
usage() {
|
usage() {
|
||||||
cat <<'USAGE'
|
cat <<'USAGE'
|
||||||
mailtrace – Postfix/Dovecot LMTP Zustellungen aus /var/log/mail.log korrelieren
|
mailtrace – Postfix SMTP/LMTP Zustellungen aus /var/log/mail.log korrelieren
|
||||||
|
|
||||||
Ausgabeformate:
|
Ausgabeformate:
|
||||||
(default) Blockformat (lesbar, mit Leerzeile davor)
|
(default) Blockformat (lesbar, mit Leerzeile davor)
|
||||||
@@ -114,8 +115,8 @@ Filter:
|
|||||||
--message-id <substr> match Header Message-ID (cleanup: message-id=<...>)
|
--message-id <substr> match Header Message-ID (cleanup: message-id=<...>)
|
||||||
|
|
||||||
Status-Filter:
|
Status-Filter:
|
||||||
--success nur erfolgreiche Outcomes (sent|delivered|deliverable)
|
--success nur erfolgreiche Endzustellungen (status=success)
|
||||||
--fail nur nicht erfolgreiche Outcomes (alles andere)
|
--fail nur fehlgeschlagene Endzustellungen (status=failed oder handoff)
|
||||||
|
|
||||||
AUTH / Richtung:
|
AUTH / Richtung:
|
||||||
--sasl-only nur Einlieferungen mit SMTP AUTH (sasl_username vorhanden)
|
--sasl-only nur Einlieferungen mit SMTP AUTH (sasl_username vorhanden)
|
||||||
@@ -126,6 +127,7 @@ AUTH / Richtung:
|
|||||||
Dedup (ein Eintrag pro Empfänger):
|
Dedup (ein Eintrag pro Empfänger):
|
||||||
--dedup dedupliziere pro (message-id,to). Fallback: (qid,to) falls msgid fehlt
|
--dedup dedupliziere pro (message-id,to). Fallback: (qid,to) falls msgid fehlt
|
||||||
--dedup-prefer <mode> final|amavis|first|last (Default: final)
|
--dedup-prefer <mode> final|amavis|first|last (Default: final)
|
||||||
|
--final-only nur endgültige Zustellung ausgeben (remote SMTP oder lokales LMTP), keine Amavis-Handoffs
|
||||||
|
|
||||||
TLS/Notizen:
|
TLS/Notizen:
|
||||||
--no-note im Blockformat keine Hinweiszeile ausgeben, wenn TLS nicht korrelierbar ist
|
--no-note im Blockformat keine Hinweiszeile ausgeben, wenn TLS nicht korrelierbar ist
|
||||||
@@ -160,6 +162,7 @@ while [[ $# -gt 0 ]]; do
|
|||||||
--no-note) NO_NOTE=1 ;;
|
--no-note) NO_NOTE=1 ;;
|
||||||
--dedup) DEDUP=1 ;;
|
--dedup) DEDUP=1 ;;
|
||||||
--dedup-prefer) DEDUP_PREFER="${2:-}"; shift ;;
|
--dedup-prefer) DEDUP_PREFER="${2:-}"; shift ;;
|
||||||
|
--final-only) FINAL_ONLY=1 ;;
|
||||||
--sasl-only) SASL_ONLY=1 ;;
|
--sasl-only) SASL_ONLY=1 ;;
|
||||||
--no-sasl) NO_SASL=1 ;;
|
--no-sasl) NO_SASL=1 ;;
|
||||||
--inbound-only) INBOUND_ONLY=1 ;;
|
--inbound-only) INBOUND_ONLY=1 ;;
|
||||||
@@ -221,6 +224,7 @@ fi
|
|||||||
-v no_note="$NO_NOTE" \
|
-v no_note="$NO_NOTE" \
|
||||||
-v dedup="$DEDUP" \
|
-v dedup="$DEDUP" \
|
||||||
-v dedup_prefer="$DEDUP_PREFER" \
|
-v dedup_prefer="$DEDUP_PREFER" \
|
||||||
|
-v final_only="$FINAL_ONLY" \
|
||||||
-v follow_mode="$FOLLOW" \
|
-v follow_mode="$FOLLOW" \
|
||||||
-v sasl_only="$SASL_ONLY" \
|
-v sasl_only="$SASL_ONLY" \
|
||||||
-v no_sasl="$NO_SASL" \
|
-v no_sasl="$NO_SASL" \
|
||||||
@@ -231,7 +235,30 @@ fi
|
|||||||
# Helferfunktionen
|
# Helferfunktionen
|
||||||
###############################################################################
|
###############################################################################
|
||||||
function grab(re, s, m) { return match(s, re, m) ? m[1] : "" }
|
function grab(re, s, m) { return match(s, re, m) ? m[1] : "" }
|
||||||
function is_success(st) { return (st=="sent" || st=="delivered" || st=="deliverable") }
|
function is_amavis_handoff(relay) {
|
||||||
|
return (relay ~ /(127\.0\.0\.1|localhost).*:1002[456]$/ || relay ~ /\[127\.0\.0\.1\]:1002[456]$/)
|
||||||
|
}
|
||||||
|
function is_success_raw(st) { return (st=="sent" || st=="delivered" || st=="deliverable") }
|
||||||
|
function outcome_status(st, relay) {
|
||||||
|
if (is_amavis_handoff(relay)) return "handoff"
|
||||||
|
return is_success_raw(st) ? "success" : "failed"
|
||||||
|
}
|
||||||
|
function is_success(st) { return (st=="success") }
|
||||||
|
function is_final_relay(relay, transport) {
|
||||||
|
if (is_amavis_handoff(relay)) return 0
|
||||||
|
if (transport == "lmtp" && relay ~ /private\/dovecot-lmtp/) return 1
|
||||||
|
if (transport == "smtp") return 1
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
function annotate_sender(from, qid, reason) {
|
||||||
|
if (from != "") return from
|
||||||
|
if (DSN_KIND[qid] != "") {
|
||||||
|
reason = DSN_REASON[qid]
|
||||||
|
if (reason != "") return "MAILER-DAEMON (" DSN_KIND[qid] ": " reason ")"
|
||||||
|
return "MAILER-DAEMON (" DSN_KIND[qid] ")"
|
||||||
|
}
|
||||||
|
return from
|
||||||
|
}
|
||||||
|
|
||||||
function jesc(s) {
|
function jesc(s) {
|
||||||
gsub(/\\/,"\\\\",s); gsub(/"/,"\\\"",s)
|
gsub(/\\/,"\\\\",s); gsub(/"/,"\\\"",s)
|
||||||
@@ -288,7 +315,7 @@ function remember_tls(line, ip, proto, cipher, bits) {
|
|||||||
function dedup_score(relay, idx) {
|
function dedup_score(relay, idx) {
|
||||||
if (dedup_prefer == "first") return -idx
|
if (dedup_prefer == "first") return -idx
|
||||||
if (dedup_prefer == "last") return idx
|
if (dedup_prefer == "last") return idx
|
||||||
if (dedup_prefer == "final") { if (relay ~ /private\/dovecot-lmtp/) return 1000000000 + idx; return idx }
|
if (dedup_prefer == "final") { if (!is_amavis_handoff(relay)) return 1000000000 + idx; return idx }
|
||||||
if (dedup_prefer == "amavis") { if (relay ~ /127\.0\.0\.1:10024/ || relay ~ /\[127\.0\.0\.1\]:10024/) return 1000000000 + idx; return idx }
|
if (dedup_prefer == "amavis") { if (relay ~ /127\.0\.0\.1:10024/ || relay ~ /\[127\.0\.0\.1\]:10024/) return 1000000000 + idx; return idx }
|
||||||
return idx
|
return idx
|
||||||
}
|
}
|
||||||
@@ -461,7 +488,7 @@ END {
|
|||||||
}
|
}
|
||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
# Hauptparser: pro Logzeile State sammeln, bei LMTP status= ausgeben
|
# Hauptparser: pro Logzeile State sammeln, bei Zustell-Outcome ausgeben
|
||||||
###############################################################################
|
###############################################################################
|
||||||
{
|
{
|
||||||
ts = $1
|
ts = $1
|
||||||
@@ -505,6 +532,15 @@ END {
|
|||||||
# pickup uid: lokal erzeugt
|
# pickup uid: lokal erzeugt
|
||||||
if ($0 ~ / postfix\/pickup\[/ && $0 ~ / uid=/) UID[qid] = grab(" uid=([0-9]+)", $0)
|
if ($0 ~ / postfix\/pickup\[/ && $0 ~ / uid=/) UID[qid] = grab(" uid=([0-9]+)", $0)
|
||||||
|
|
||||||
|
# postfix/bounce: neue DSN-Queue-ID einer fehlgeschlagenen Ursprungszustellung zuordnen
|
||||||
|
if ($0 ~ / postfix\/bounce\[/ && $0 ~ / sender non-delivery notification: /) {
|
||||||
|
newqid = grab(" sender non-delivery notification: ([A-F0-9]{7,20})", $0)
|
||||||
|
if (newqid != "") {
|
||||||
|
DSN_KIND[newqid] = "non-delivery notification"
|
||||||
|
if (LAST_FAILURE[qid] != "") DSN_REASON[newqid] = LAST_FAILURE[qid]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
# smtpd: client/orig_client, HELO fallback, TLS map via clientip
|
# smtpd: client/orig_client, HELO fallback, TLS map via clientip
|
||||||
if ($0 ~ / postfix\/smtpd\[/ && $0 ~ / client=/) {
|
if ($0 ~ / postfix\/smtpd\[/ && $0 ~ / client=/) {
|
||||||
CLIENT[qid] = grab(" client=([^\\[]+)", $0)
|
CLIENT[qid] = grab(" client=([^\\[]+)", $0)
|
||||||
@@ -541,13 +577,10 @@ END {
|
|||||||
# SMTP AUTH User
|
# SMTP AUTH User
|
||||||
if ($0 ~ / postfix\/smtpd\[/ && $0 ~ / sasl_username=/) SASL[qid] = grab(" sasl_username=([^, ]+)", $0)
|
if ($0 ~ / postfix\/smtpd\[/ && $0 ~ / sasl_username=/) SASL[qid] = grab(" sasl_username=([^, ]+)", $0)
|
||||||
|
|
||||||
# ===================== LMTP Outcome (Ausgabezeitpunkt) =====================
|
# ===================== Delivery Outcome (Ausgabezeitpunkt) =====================
|
||||||
if ($0 ~ / postfix\/lmtp\[/ && $0 ~ / status=/) {
|
if ($0 ~ / postfix\/(lmtp|smtp)\[/ && $0 ~ / status=/) {
|
||||||
status = grab(" status=([^ ]+)", $0)
|
raw_status = grab(" status=([^ ]+)", $0)
|
||||||
|
transport = ($0 ~ / postfix\/smtp\[/ ? "smtp" : "lmtp")
|
||||||
# Statusfilter
|
|
||||||
if (success_only == 1 && !is_success(status)) next
|
|
||||||
if (fail_only == 1 && is_success(status)) next
|
|
||||||
|
|
||||||
from = (FROM[qid] ? FROM[qid] : "-")
|
from = (FROM[qid] ? FROM[qid] : "-")
|
||||||
to = (TO[qid] ? TO[qid] : "-")
|
to = (TO[qid] ? TO[qid] : "-")
|
||||||
@@ -561,13 +594,21 @@ END {
|
|||||||
|
|
||||||
dsn = grab(" dsn=([^, ]+)", $0)
|
dsn = grab(" dsn=([^, ]+)", $0)
|
||||||
relay = grab(" relay=([^, ]+)", $0)
|
relay = grab(" relay=([^, ]+)", $0)
|
||||||
|
if (final_only == 1 && !is_final_relay(relay, transport)) next
|
||||||
|
status = outcome_status(raw_status, relay)
|
||||||
msg = grab(" status=[^ ]+ \\((.*)\\)$", $0)
|
msg = grab(" status=[^ ]+ \\((.*)\\)$", $0)
|
||||||
|
if (status == "failed" && msg != "") LAST_FAILURE[qid] = msg
|
||||||
|
|
||||||
|
# Statusfilter
|
||||||
|
if (success_only == 1 && !is_success(status)) next
|
||||||
|
if (fail_only == 1 && is_success(status)) next
|
||||||
|
|
||||||
client = (CLIENT[qid] ? CLIENT[qid] : "-")
|
client = (CLIENT[qid] ? CLIENT[qid] : "-")
|
||||||
clientip = (CLIENTIP[qid] ? CLIENTIP[qid] : "-")
|
clientip = (CLIENTIP[qid] ? CLIENTIP[qid] : "-")
|
||||||
helo = (HELO[qid] ? HELO[qid] : "-")
|
helo = (HELO[qid] ? HELO[qid] : "-")
|
||||||
sasl = (SASL[qid] ? SASL[qid] : "-")
|
sasl = (SASL[qid] ? SASL[qid] : "-")
|
||||||
uid = (UID[qid] ? UID[qid] : "")
|
uid = (UID[qid] ? UID[qid] : "")
|
||||||
|
if (from == "-") from = annotate_sender("", qid)
|
||||||
|
|
||||||
# SASL Filter
|
# SASL Filter
|
||||||
if (sasl_only == 1 && (sasl == "" || sasl == "-")) next
|
if (sasl_only == 1 && (sasl == "" || sasl == "-")) next
|
||||||
|
|||||||
Reference in New Issue
Block a user