update acc. to bounces
This commit is contained in:
parent
6df8674b72
commit
1d1a384d1b
|
|
@ -0,0 +1,137 @@
|
||||||
|
{
|
||||||
|
"version": "0",
|
||||||
|
"id": "68eb43ad-3ad6-25ef-2b49-2389fc4460cc",
|
||||||
|
"detail-type": "Email Bounced",
|
||||||
|
"source": "aws.ses",
|
||||||
|
"account": "339712845857",
|
||||||
|
"time": "2025-12-19T02:24:37Z",
|
||||||
|
"region": "us-east-2",
|
||||||
|
"resources": [
|
||||||
|
"arn:aws:ses:us-east-2:339712845857:configuration-set/relay-outbound"
|
||||||
|
],
|
||||||
|
"detail": {
|
||||||
|
"eventType": "Bounce",
|
||||||
|
"bounce": {
|
||||||
|
"feedbackId": "010f019b346c64dc-ebd1959f-ac85-4d28-b2c2-e2db414889d2-000000",
|
||||||
|
"bounceType": "Permanent",
|
||||||
|
"bounceSubType": "General",
|
||||||
|
"bouncedRecipients": [
|
||||||
|
{
|
||||||
|
"emailAddress": "pishing@paypal.com",
|
||||||
|
"action": "failed",
|
||||||
|
"status": "5.0.0",
|
||||||
|
"diagnosticCode": "smtp; 5.1.0 - Unknown address error 550-'5.4.1 Recipient address rejected: Access denied. For more information see https: //aka.ms/EXOSmtpErrors [DS2PEPF00003441.namprd04.prod.outlook.com 2025-12-19T02:24:36.588Z 08DE3C04B3813774] (delivery attempts: 0)"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"timestamp": "2025-12-19T02:24:37.521Z",
|
||||||
|
"reportingMTA": "dns; mx2.paypalcorp.com"
|
||||||
|
},
|
||||||
|
"mail": {
|
||||||
|
"timestamp": "2025-12-19T02:24:34.082Z",
|
||||||
|
"source": "andreas.knuth@bayarea-cc.com",
|
||||||
|
"sourceArn": "arn:aws:ses:us-east-2:339712845857:identity/bayarea-cc.com",
|
||||||
|
"sendingAccountId": "339712845857",
|
||||||
|
"messageId": "010f019b346c5722-7f94b168-0d66-444c-8333-99f80801ee6e-000000",
|
||||||
|
"destination": [
|
||||||
|
"pishing@paypal.com"
|
||||||
|
],
|
||||||
|
"headersTruncated": False,
|
||||||
|
"headers": [
|
||||||
|
{
|
||||||
|
"name": "Received",
|
||||||
|
"value": "from mail.email-srvr.com (mail.email-srvr.com [2.56.188.138]) by email-smtp.amazonaws.com with SMTP (SimpleEmailService-d-4T8YRF3HF) id JWwKWtbMKwPcuMJWmawg for pishing@paypal.com; Fri, 19 Dec 2025 02:24:34 +0000 (UTC)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "DKIM-Signature",
|
||||||
|
"value": "v=1; a=rsa-sha256; c=relaxed/simple; d=bayarea-cc.com; s=mail; t=1766111073; bh=489KasDOSypdn6kagJw8c/vBfll20acGANR7WEnsNq8=; h=From:To:Subject:Reply-To:In-Reply-To:References; b=axFSO5cJaEy+bSCreaVfYY8ThHUvEAJmiVV26Qpw2sZG4YFoYglcNry2Gv2B+99ctJwcTAlxa/XzB0mJzzSpyU7WU0D03Kw/4k+8Mdl0mu+Li8icoINPJ0v5Kap2hVMRVp+ge6w7wAZR+rS46oAvL++piRZYr+85FGiHpFtJIK8e4a06sXtkHB4kDDNTDzKiTM7tTH6/oD4LV3LxeL29notQih5atTUOSo5LHN1QNp5Hq05A4sih7rM6J7CNKIouvqm1ku8I2+xUsgNu0neWnddBDV8njD24Gc70Flab22q5GDqVQ0caql7odpMlrCQjdmAgyEmeVP+JWjB3EnZ3DQ=="
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Received",
|
||||||
|
"value": "from app.email-bayarea.com (roundcube-new.mail_network [172.18.0.5]) (Authenticated sender: andreas.knuth@bayarea-cc.com) by mail.email-srvr.com (Postfix) with ESMTPSA id 6CD2F2E60092 for <pishing@paypal.com>; Thu, 18 Dec 2025 20:24:33 -0600 (CST)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "MIME-Version",
|
||||||
|
"value": "1.0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Date",
|
||||||
|
"value": "Thu, 18 Dec 2025 20:24:33 -0600"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "From",
|
||||||
|
"value": "andreas.knuth@bayarea-cc.com"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "To",
|
||||||
|
"value": "pishing@paypal.com"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Subject",
|
||||||
|
"value": "Fwd: A one-time merchant setup fee of $249.99 has been applied and will appear on your bank statement wit"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Reply-To",
|
||||||
|
"value": "andreas.knuth@bayarea-cc.com"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Mail-Reply-To",
|
||||||
|
"value": "andreas.knuth@bayarea-cc.com"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "In-Reply-To",
|
||||||
|
"value": "<6061d865685c1bb406c127f32451d22d@bayarea-cc.com>"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "References",
|
||||||
|
"value": "<boLgON9OSkmzVWOPwCp8qQ@geopod-ismtpd-45> <6061d865685c1bb406c127f32451d22d@bayarea-cc.com>"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Message-ID",
|
||||||
|
"value": "<bf937f16310bd1be5350425b2dfc3d65@bayarea-cc.com>"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "X-Sender",
|
||||||
|
"value": "andreas.knuth@bayarea-cc.com"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Content-Type",
|
||||||
|
"value": "multipart/alternative; boundary='=_d6bdf41daf974c2c1b77e9250e4348a7'"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"commonHeaders": {
|
||||||
|
"from": [
|
||||||
|
"andreas.knuth@bayarea-cc.com"
|
||||||
|
],
|
||||||
|
"replyTo": [
|
||||||
|
"andreas.knuth@bayarea-cc.com"
|
||||||
|
],
|
||||||
|
"date": "Thu, 18 Dec 2025 20:24:33 -0600",
|
||||||
|
"to": [
|
||||||
|
"pishing@paypal.com"
|
||||||
|
],
|
||||||
|
"messageId": "010f019b346c5722-7f94b168-0d66-444c-8333-99f80801ee6e-000000",
|
||||||
|
"subject": "Fwd: A one-time merchant setup fee of $249.99 has been applied and will appear on your bank statement wit"
|
||||||
|
},
|
||||||
|
"tags": {
|
||||||
|
"ses:source-tls-version": [
|
||||||
|
"TLSv1.3"
|
||||||
|
],
|
||||||
|
"ses:operation": [
|
||||||
|
"SendSmtpEmail"
|
||||||
|
],
|
||||||
|
"ses:configuration-set": [
|
||||||
|
"relay-outbound"
|
||||||
|
],
|
||||||
|
"ses:source-ip": [
|
||||||
|
"2.56.188.138"
|
||||||
|
],
|
||||||
|
"ses:from-domain": [
|
||||||
|
"bayarea-cc.com"
|
||||||
|
],
|
||||||
|
"ses:caller-identity": [
|
||||||
|
"bizmatch.net"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,125 @@
|
||||||
|
{
|
||||||
|
"version": "0",
|
||||||
|
"id": "b1198c79-d4df-6d77-a472-12c05eb99a39",
|
||||||
|
"detail-type": "Email Bounced",
|
||||||
|
"source": "aws.ses",
|
||||||
|
"account": "339712845857",
|
||||||
|
"time": "2025-12-19T01:59:01Z",
|
||||||
|
"region": "us-east-2",
|
||||||
|
"resources": [
|
||||||
|
"arn:aws:ses:us-east-2:339712845857:configuration-set/relay-outbound"
|
||||||
|
],
|
||||||
|
"detail": {
|
||||||
|
"eventType": "Bounce",
|
||||||
|
"bounce": {
|
||||||
|
"feedbackId": "010f019b3454f3b9-6b92ce4e-e1f2-420b-8dd3-e48e062f0f88-000000",
|
||||||
|
"bounceType": "Transient",
|
||||||
|
"bounceSubType": "General",
|
||||||
|
"bouncedRecipients": [
|
||||||
|
{
|
||||||
|
"emailAddress": "frankie@iitwelders.com"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"timestamp": "2025-12-19T01:59:01.245Z"
|
||||||
|
},
|
||||||
|
"mail": {
|
||||||
|
"timestamp": "2025-12-19T01:58:58.255Z",
|
||||||
|
"source": "andreas.knuth@bayarea-cc.com",
|
||||||
|
"sourceArn": "arn:aws:ses:us-east-2:339712845857:identity/bayarea-cc.com",
|
||||||
|
"sendingAccountId": "339712845857",
|
||||||
|
"messageId": "010f019b3454e7cf-36b8560d-7880-4913-9e5d-dd87f336b0dd-000000",
|
||||||
|
"destination": [
|
||||||
|
"frankie@iitwelders.com"
|
||||||
|
],
|
||||||
|
"headersTruncated": False,
|
||||||
|
"headers": [
|
||||||
|
{
|
||||||
|
"name": "Received",
|
||||||
|
"value": "from mail.email-srvr.com (mail.email-srvr.com [2.56.188.138]) by email-smtp.amazonaws.com with SMTP (SimpleEmailService-d-Z6YSX0FGF) id d7Quc01fG0CsS9eS7yfX for frankie@iitwelders.com; Fri, 19 Dec 2025 01:58:58 +0000 (UTC)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "DKIM-Signature",
|
||||||
|
"value": "v=1; a=rsa-sha256; c=relaxed/simple; d=bayarea-cc.com; s=mail; t=1766109537; bh=S/AVMjQHFbdT0GdJ56RlBKNMvace1V8iv+n0iBHTPYQ=; h=From:To:Subject:Reply-To; b=CX4lHSxen4aqQ5+3mlfl51hmyoK3mkP3gVu9mfILqPaxafH8aXNYfUYBxpRct9sQHNuN2OhgUfdjrTM/75WnKrV50wo13HeKw3D2b3d/N3zj447KG2eAGycm/guNibrcjhduLDERGVwMFaeWAAKHbbWfWnAw68yEFKkcnTCNB1imyAn9diDew5zO9q2ZuA0fOm3YXZ7qFmVtmmX4z6la0Rfa39gEM6wBiOhpZTtODyTqkmABFolVTEqc1VqYH27jB8ZVHi1bO4M42VGoRcDzvjOfkxq5ad/UQeho7HOsLuWnVG7H3BarTom/TdZYMrt2ZllH5N+nf2ec90/lH20CxA=="
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Received",
|
||||||
|
"value": "from app.email-bayarea.com (roundcube-new.mail_network [172.18.0.5]) (Authenticated sender: andreas.knuth@bayarea-cc.com) by mail.email-srvr.com (Postfix) with ESMTPSA id EC1B02E5FD51 for <frankie@iitwelders.com>; Thu, 18 Dec 2025 19:58:56 -0600 (CST)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "MIME-Version",
|
||||||
|
"value": "1.0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Date",
|
||||||
|
"value": "Thu, 18 Dec 2025 19:58:56 -0600"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "From",
|
||||||
|
"value": "andreas.knuth@bayarea-cc.com"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "To",
|
||||||
|
"value": "Frankie <frankie@iitwelders.com>"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Subject",
|
||||||
|
"value": "12/18/25 7:58"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Reply-To",
|
||||||
|
"value": "andreas.knuth@bayarea-cc.com"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Mail-Reply-To",
|
||||||
|
"value": "andreas.knuth@bayarea-cc.com"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Message-ID",
|
||||||
|
"value": "<17a781e80ecae12285697c536cc46033@bayarea-cc.com>"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "X-Sender",
|
||||||
|
"value": "andreas.knuth@bayarea-cc.com"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Content-Type",
|
||||||
|
"value": "multipart/alternative; boundary='=_46eb06b0a62a2efa142c40c5eadbbc54'"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"commonHeaders": {
|
||||||
|
"from": [
|
||||||
|
"andreas.knuth@bayarea-cc.com"
|
||||||
|
],
|
||||||
|
"replyTo": [
|
||||||
|
"andreas.knuth@bayarea-cc.com"
|
||||||
|
],
|
||||||
|
"date": "Thu, 18 Dec 2025 19:58:56 -0600",
|
||||||
|
"to": [
|
||||||
|
"Frankie <frankie@iitwelders.com>"
|
||||||
|
],
|
||||||
|
"messageId": "010f019b3454e7cf-36b8560d-7880-4913-9e5d-dd87f336b0dd-000000",
|
||||||
|
"subject": "12/18/25 7:58"
|
||||||
|
},
|
||||||
|
"tags": {
|
||||||
|
"ses:source-tls-version": [
|
||||||
|
"TLSv1.3"
|
||||||
|
],
|
||||||
|
"ses:operation": [
|
||||||
|
"SendSmtpEmail"
|
||||||
|
],
|
||||||
|
"ses:configuration-set": [
|
||||||
|
"relay-outbound"
|
||||||
|
],
|
||||||
|
"ses:source-ip": [
|
||||||
|
"2.56.188.138"
|
||||||
|
],
|
||||||
|
"ses:from-domain": [
|
||||||
|
"bayarea-cc.com"
|
||||||
|
],
|
||||||
|
"ses:caller-identity": [
|
||||||
|
"bizmatch.net"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,125 @@
|
||||||
|
{
|
||||||
|
"version": "0",
|
||||||
|
"id": "4d37ae3d-e411-2b83-8a83-6489a5fa1a00",
|
||||||
|
"detail-type": "Email Bounced",
|
||||||
|
"source": "aws.ses",
|
||||||
|
"account": "339712845857",
|
||||||
|
"time": "2025-12-19T02:10:33Z",
|
||||||
|
"region": "us-east-2",
|
||||||
|
"resources": [
|
||||||
|
"arn:aws:ses:us-east-2:339712845857:configuration-set/relay-outbound"
|
||||||
|
],
|
||||||
|
"detail": {
|
||||||
|
"eventType": "Bounce",
|
||||||
|
"bounce": {
|
||||||
|
"feedbackId": "010f019b345f8461-3382d3a0-42bb-4861-977f-e62606a24cb7-000000",
|
||||||
|
"bounceType": "Transient",
|
||||||
|
"bounceSubType": "General",
|
||||||
|
"bouncedRecipients": [
|
||||||
|
{
|
||||||
|
"emailAddress": "remote@gregknoppcpa.com"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"timestamp": "2025-12-19T02:10:33.636Z"
|
||||||
|
},
|
||||||
|
"mail": {
|
||||||
|
"timestamp": "2025-12-19T02:10:32.560Z",
|
||||||
|
"source": "andreas.knuth@bayarea-cc.com",
|
||||||
|
"sourceArn": "arn:aws:ses:us-east-2:339712845857:identity/bayarea-cc.com",
|
||||||
|
"sendingAccountId": "339712845857",
|
||||||
|
"messageId": "010f019b345f7ff0-e22c2d38-c499-48ed-8992-abbf1c44b6a1-000000",
|
||||||
|
"destination": [
|
||||||
|
"remote@gregknoppcpa.com"
|
||||||
|
],
|
||||||
|
"headersTruncated": False,
|
||||||
|
"headers": [
|
||||||
|
{
|
||||||
|
"name": "Received",
|
||||||
|
"value": "from mail.email-srvr.com (mail.email-srvr.com [2.56.188.138]) by email-smtp.amazonaws.com with SMTP (SimpleEmailService-d-V0JPVCFGF) id 6KbS70pRiY9lOcyjIONV for remote@gregknoppcpa.com; Fri, 19 Dec 2025 02:10:32 +0000 (UTC)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "DKIM-Signature",
|
||||||
|
"value": "v=1; a=rsa-sha256; c=relaxed/simple; d=bayarea-cc.com; s=mail; t=1766110231; bh=sU5OepBQM0PVwu+hgNjl2gP+fBXM9lfNeDiFo9j+0BQ=; h=From:To:Subject:Reply-To; b=lK1PWF722nu9AuCE0SRq7VBVHBrznhyiozlM2kxSSVFVUNHtV4abBKHMPdzE0c6oYN4blSogNMi9/qJA4EKSpoegMHertvETZpHHTM51M083wtzodojc5ZPKoOZjLpjWOVf3oqomccwUxTwqNXmyEdQcUH/lYz52o+b6GFFb7X7MkxQfA0VXgIYL5v0rIKszOoLAour3lfx99uoJSwIIVLZi4f5LFWa+FB48bGH67FaojHRqQzeioMQyLwa9fSKMG/bifT1/jPSmCauRPMSxzsdDBvk0nuVitr8/RgAno8FqfBH+UWJIw8Wt3gVQDLNL82hi5qWUgsXKwY3LFo2LkA=="
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Received",
|
||||||
|
"value": "from app.email-bayarea.com (roundcube-new.mail_network [172.18.0.5]) (Authenticated sender: andreas.knuth@bayarea-cc.com) by mail.email-srvr.com (Postfix) with ESMTPSA id D9D3F2E5FD51 for <remote@gregknoppcpa.com>; Thu, 18 Dec 2025 20:10:31 -0600 (CST)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "MIME-Version",
|
||||||
|
"value": "1.0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Date",
|
||||||
|
"value": "Thu, 18 Dec 2025 20:10:31 -0600"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "From",
|
||||||
|
"value": "andreas.knuth@bayarea-cc.com"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "To",
|
||||||
|
"value": "remote@gregknoppcpa.com"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Subject",
|
||||||
|
"value": "testing out-of-office messages"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Reply-To",
|
||||||
|
"value": "andreas.knuth@bayarea-cc.com"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Mail-Reply-To",
|
||||||
|
"value": "andreas.knuth@bayarea-cc.com"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Message-ID",
|
||||||
|
"value": "<95264ff6f55b9cc3ffcd451d6b27f7f0@bayarea-cc.com>"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "X-Sender",
|
||||||
|
"value": "andreas.knuth@bayarea-cc.com"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Content-Type",
|
||||||
|
"value": "multipart/alternative; boundary='=_7ffce281e198378b2420ed61fd6b9156'"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"commonHeaders": {
|
||||||
|
"from": [
|
||||||
|
"andreas.knuth@bayarea-cc.com"
|
||||||
|
],
|
||||||
|
"replyTo": [
|
||||||
|
"andreas.knuth@bayarea-cc.com"
|
||||||
|
],
|
||||||
|
"date": "Thu, 18 Dec 2025 20:10:31 -0600",
|
||||||
|
"to": [
|
||||||
|
"remote@gregknoppcpa.com"
|
||||||
|
],
|
||||||
|
"messageId": "010f019b345f7ff0-e22c2d38-c499-48ed-8992-abbf1c44b6a1-000000",
|
||||||
|
"subject": "testing out-of-office messages"
|
||||||
|
},
|
||||||
|
"tags": {
|
||||||
|
"ses:source-tls-version": [
|
||||||
|
"TLSv1.3"
|
||||||
|
],
|
||||||
|
"ses:operation": [
|
||||||
|
"SendSmtpEmail"
|
||||||
|
],
|
||||||
|
"ses:configuration-set": [
|
||||||
|
"relay-outbound"
|
||||||
|
],
|
||||||
|
"ses:source-ip": [
|
||||||
|
"2.56.188.138"
|
||||||
|
],
|
||||||
|
"ses:from-domain": [
|
||||||
|
"bayarea-cc.com"
|
||||||
|
],
|
||||||
|
"ses:caller-identity": [
|
||||||
|
"bizmatch.net"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,133 @@
|
||||||
|
{
|
||||||
|
"version": "0",
|
||||||
|
"id": "ddfd563e-49f6-1f59-6d1e-c67158ab5eec",
|
||||||
|
"detail-type": "Email Bounced",
|
||||||
|
"source": "aws.ses",
|
||||||
|
"account": "339712845857",
|
||||||
|
"time": "2025-12-19T02:33:55Z",
|
||||||
|
"region": "us-east-2",
|
||||||
|
"resources": [
|
||||||
|
"arn:aws:ses:us-east-2:339712845857:configuration-set/relay-outbound"
|
||||||
|
],
|
||||||
|
"detail": {
|
||||||
|
"eventType": "Bounce",
|
||||||
|
"bounce": {
|
||||||
|
"feedbackId": "010f019b3474e821-12fa60c3-e47e-4289-a4b6-47ac55d996a2-000000",
|
||||||
|
"bounceType": "Undetermined",
|
||||||
|
"bounceSubType": "Undetermined",
|
||||||
|
"bouncedRecipients": [
|
||||||
|
{
|
||||||
|
"emailAddress": "phishing@paypal.com"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"timestamp": "2025-12-19T02:33:55.434Z"
|
||||||
|
},
|
||||||
|
"mail": {
|
||||||
|
"timestamp": "2025-12-19T02:33:53.244Z",
|
||||||
|
"source": "andreas.knuth@bayarea-cc.com",
|
||||||
|
"sourceArn": "arn:aws:ses:us-east-2:339712845857:identity/bayarea-cc.com",
|
||||||
|
"sendingAccountId": "339712845857",
|
||||||
|
"messageId": "010f019b3474df5c-c634e6cc-8ebb-4b13-957e-0e9b84e39917-000000",
|
||||||
|
"destination": [
|
||||||
|
"phishing@paypal.com"
|
||||||
|
],
|
||||||
|
"headersTruncated": False,
|
||||||
|
"headers": [
|
||||||
|
{
|
||||||
|
"name": "Received",
|
||||||
|
"value": "from mail.email-srvr.com (mail.email-srvr.com [2.56.188.138]) by email-smtp.amazonaws.com with SMTP (SimpleEmailService-d-V0JPVCFGF) id XSfVNEIjPhLtO2NEYG88 for phishing@paypal.com; Fri, 19 Dec 2025 02:33:53 +0000 (UTC)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "DKIM-Signature",
|
||||||
|
"value": "v=1; a=rsa-sha256; c=relaxed/simple; d=bayarea-cc.com; s=mail; t=1766111632; bh=ycI1TnY3sqcJF4JmY2LCeBTlZ8Zv+aR+7YbjD2Y1n0Y=; h=From:To:Subject:Reply-To:In-Reply-To:References; b=YQ/EtiYxQIi4Ykwx4ELKXP6gd5u+sev5/GnN97t2rkfxFjrGAZHFdUS9IHipOi/KG5VCAbW89ocW6vPZrdC9SpSxrxr+NMncceSBfvun7SgMQM7ja12clsMfOPebbLsp+TEoSwo43QW4IYsNJep8B7OTInTpadABgeiKd+yWe0BLfsa56tGr6OdIcCBKmxXm/qEZoEjkXooYWu0A5yWCrfpfpdvgZTKKaArturPAtiPUcQiUuDRx7jMkDQkofmBNTtrDbmaLzfEbPqfI2usavV7DCDpa70N6/fbVY2RgnFpcDYP3zd1gf4qDGdnsy9+8B848D1QV/HrEVDsh/Opoxw=="
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Received",
|
||||||
|
"value": "from app.email-bayarea.com (roundcube-new.mail_network [172.18.0.5]) (Authenticated sender: andreas.knuth@bayarea-cc.com) by mail.email-srvr.com (Postfix) with ESMTPSA id 9685E2E60092 for <phishing@paypal.com>; Thu, 18 Dec 2025 20:33:52 -0600 (CST)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "MIME-Version",
|
||||||
|
"value": "1.0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Date",
|
||||||
|
"value": "Thu, 18 Dec 2025 20:33:52 -0600"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "From",
|
||||||
|
"value": "andreas.knuth@bayarea-cc.com"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "To",
|
||||||
|
"value": "phishing@paypal.com"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Subject",
|
||||||
|
"value": "Fwd: A one-time merchant setup fee of $249.99 has been applied and will appear on your bank statement wit"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Reply-To",
|
||||||
|
"value": "andreas.knuth@bayarea-cc.com"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Mail-Reply-To",
|
||||||
|
"value": "andreas.knuth@bayarea-cc.com"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "In-Reply-To",
|
||||||
|
"value": "<6061d865685c1bb406c127f32451d22d@bayarea-cc.com>"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "References",
|
||||||
|
"value": "<boLgON9OSkmzVWOPwCp8qQ@geopod-ismtpd-45> <6061d865685c1bb406c127f32451d22d@bayarea-cc.com>"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Message-ID",
|
||||||
|
"value": "<e7ec8070400b953e735b6fbe5439fa1e@bayarea-cc.com>"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "X-Sender",
|
||||||
|
"value": "andreas.knuth@bayarea-cc.com"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Content-Type",
|
||||||
|
"value": "multipart/alternative; boundary='=_eb88e98e1904b7ce5ebf2be21b8909fd'"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"commonHeaders": {
|
||||||
|
"from": [
|
||||||
|
"andreas.knuth@bayarea-cc.com"
|
||||||
|
],
|
||||||
|
"replyTo": [
|
||||||
|
"andreas.knuth@bayarea-cc.com"
|
||||||
|
],
|
||||||
|
"date": "Thu, 18 Dec 2025 20:33:52 -0600",
|
||||||
|
"to": [
|
||||||
|
"phishing@paypal.com"
|
||||||
|
],
|
||||||
|
"messageId": "010f019b3474df5c-c634e6cc-8ebb-4b13-957e-0e9b84e39917-000000",
|
||||||
|
"subject": "Fwd: A one-time merchant setup fee of $249.99 has been applied and will appear on your bank statement wit"
|
||||||
|
},
|
||||||
|
"tags": {
|
||||||
|
"ses:source-tls-version": [
|
||||||
|
"TLSv1.3"
|
||||||
|
],
|
||||||
|
"ses:operation": [
|
||||||
|
"SendSmtpEmail"
|
||||||
|
],
|
||||||
|
"ses:configuration-set": [
|
||||||
|
"relay-outbound"
|
||||||
|
],
|
||||||
|
"ses:source-ip": [
|
||||||
|
"2.56.188.138"
|
||||||
|
],
|
||||||
|
"ses:from-domain": [
|
||||||
|
"bayarea-cc.com"
|
||||||
|
],
|
||||||
|
"ses:caller-identity": [
|
||||||
|
"bizmatch.net"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,74 +1,97 @@
|
||||||
|
import json
|
||||||
import boto3
|
import boto3
|
||||||
import os
|
import os
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
dynamo = boto3.resource('dynamodb', region_name='us-east-2')
|
# AWS Clients
|
||||||
table = dynamo.Table('ses-outbound-messages')
|
s3 = boto3.client('s3')
|
||||||
|
sqs = boto3.client('sqs')
|
||||||
|
dynamodb = boto3.resource('dynamodb')
|
||||||
|
|
||||||
|
# DynamoDB Table
|
||||||
|
OUTBOUND_TABLE = os.environ.get('OUTBOUND_TABLE', 'ses-outbound-messages')
|
||||||
|
table = dynamodb.Table(OUTBOUND_TABLE)
|
||||||
|
|
||||||
def lambda_handler(event, context):
|
def lambda_handler(event, context):
|
||||||
print(f"Received event: {event}")
|
"""
|
||||||
|
Verarbeitet SES Events:
|
||||||
|
- Bounce Events: Speichert bounce details in DynamoDB
|
||||||
|
- Send Events: Ignoriert (nicht mehr benötigt)
|
||||||
|
"""
|
||||||
|
|
||||||
detail = event.get('detail', {})
|
print(f"Received event: {json.dumps(event)}")
|
||||||
mail = detail.get('mail', {})
|
|
||||||
msg_id = mail.get('messageId')
|
|
||||||
|
|
||||||
if not msg_id:
|
# SNS Wrapper entpacken
|
||||||
print("No MessageId in event")
|
for record in event.get('Records', []):
|
||||||
return
|
if 'Sns' in record:
|
||||||
|
message = json.loads(record['Sns']['Message'])
|
||||||
|
else:
|
||||||
|
message = record
|
||||||
|
|
||||||
# Event-Type aus dem Event extrahieren
|
event_type = message.get('eventType')
|
||||||
event_type = detail.get('eventType')
|
|
||||||
|
|
||||||
if event_type == 'Send':
|
if event_type == 'Bounce':
|
||||||
source = mail.get('source')
|
handle_bounce(message)
|
||||||
destinations = mail.get('destination', [])
|
elif event_type == 'Send':
|
||||||
|
# Ignorieren - wird nicht mehr benötigt
|
||||||
|
print(f"Ignoring Send event (no longer needed)")
|
||||||
|
else:
|
||||||
|
print(f"Unknown event type: {event_type}")
|
||||||
|
|
||||||
|
return {'statusCode': 200}
|
||||||
|
|
||||||
|
|
||||||
|
def handle_bounce(message):
|
||||||
|
"""
|
||||||
|
Verarbeitet Bounce Events und speichert Details in DynamoDB
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
bounce = message.get('bounce', {})
|
||||||
|
mail = message.get('mail', {})
|
||||||
|
|
||||||
|
# Extrahiere relevante Daten
|
||||||
|
feedback_id = bounce.get('feedbackId') # Das ist die Message-ID!
|
||||||
|
bounce_type = bounce.get('bounceType', 'Unknown')
|
||||||
|
bounce_subtype = bounce.get('bounceSubType', 'Unknown')
|
||||||
|
bounced_recipients = [r['emailAddress'] for r in bounce.get('bouncedRecipients', [])]
|
||||||
|
timestamp = bounce.get('timestamp')
|
||||||
|
|
||||||
|
# Original Message Daten
|
||||||
|
original_source = mail.get('source')
|
||||||
|
original_message_id = mail.get('messageId')
|
||||||
|
|
||||||
|
if not feedback_id:
|
||||||
|
print(f"Warning: No feedbackId in bounce event")
|
||||||
|
return
|
||||||
|
|
||||||
|
print(f"Processing bounce: feedbackId={feedback_id}, type={bounce_type}/{bounce_subtype}")
|
||||||
|
print(f"Bounced recipients: {bounced_recipients}")
|
||||||
|
|
||||||
|
# Speichere in DynamoDB (feedback_id ist die Message-ID der Bounce-Mail!)
|
||||||
table.put_item(
|
table.put_item(
|
||||||
Item={
|
Item={
|
||||||
'MessageId': msg_id,
|
'MessageId': feedback_id, # Primary Key
|
||||||
'source': source,
|
'original_message_id': original_message_id, # SES MessageId der Original-Mail
|
||||||
'destinations': destinations,
|
'original_source': original_source,
|
||||||
'timestamp': mail.get('timestamp')
|
'bounceType': bounce_type,
|
||||||
|
'bounceSubType': bounce_subtype,
|
||||||
|
'bouncedRecipients': bounced_recipients, # Liste von Email-Adressen
|
||||||
|
'timestamp': timestamp or datetime.utcnow().isoformat(),
|
||||||
|
'event_type': 'bounce'
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
print(f"Stored SEND event for {msg_id}")
|
|
||||||
return
|
|
||||||
|
|
||||||
if event_type == 'Bounce':
|
print(f"✓ Stored bounce info for feedbackId {feedback_id}")
|
||||||
bounce = detail.get('bounce', {})
|
|
||||||
bounced = [
|
|
||||||
r.get('emailAddress')
|
|
||||||
for r in bounce.get('bouncedRecipients', [])
|
|
||||||
if r.get('emailAddress')
|
|
||||||
]
|
|
||||||
if not bounced:
|
|
||||||
print("No bouncedRecipients in bounce event")
|
|
||||||
return
|
|
||||||
|
|
||||||
table.update_item(
|
except Exception as e:
|
||||||
Key={'MessageId': msg_id},
|
print(f"Error handling bounce: {e}")
|
||||||
UpdateExpression="ADD bouncedRecipients :b",
|
import traceback
|
||||||
ExpressionAttributeValues={
|
traceback.print_exc()
|
||||||
':b': set(bounced)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
print(f"Updated {msg_id} with bouncedRecipients={bounced}")
|
|
||||||
return
|
|
||||||
|
|
||||||
if event_type == 'Complaint':
|
|
||||||
complaint = detail.get('complaint', {})
|
|
||||||
complained = [
|
|
||||||
r.get('emailAddress')
|
|
||||||
for r in complaint.get('complainedRecipients', [])
|
|
||||||
if r.get('emailAddress')
|
|
||||||
]
|
|
||||||
if not complained:
|
|
||||||
return
|
|
||||||
|
|
||||||
table.update_item(
|
def handle_send(message):
|
||||||
Key={'MessageId': msg_id},
|
"""
|
||||||
UpdateExpression="ADD complaintRecipients :c",
|
DEPRECATED - Wird nicht mehr benötigt
|
||||||
ExpressionAttributeValues={
|
Send Events werden jetzt ignoriert
|
||||||
':c': set(complained)
|
"""
|
||||||
}
|
pass
|
||||||
)
|
|
||||||
print(f"Updated {msg_id} with complaintRecipients={complained}")
|
|
||||||
return
|
|
||||||
|
|
@ -0,0 +1,164 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
Test script für Message-ID Extraktion - VERBESSERTE VERSION
|
||||||
|
Kann lokal ausgeführt werden ohne AWS-Verbindung
|
||||||
|
"""
|
||||||
|
|
||||||
|
import re
|
||||||
|
from email.parser import BytesParser
|
||||||
|
from email.policy import SMTP as SMTPPolicy
|
||||||
|
|
||||||
|
def log(message: str, level: str = 'INFO'):
|
||||||
|
"""Dummy log für Tests"""
|
||||||
|
print(f"[{level}] {message}")
|
||||||
|
|
||||||
|
def extract_original_message_id(parsed):
|
||||||
|
"""
|
||||||
|
Extrahiert Original SES Message-ID aus Email
|
||||||
|
SES Format: 010f[hex32]-[hex8]-[hex4]-[hex4]-[hex4]-[hex12]-000000
|
||||||
|
"""
|
||||||
|
import re
|
||||||
|
|
||||||
|
# SES Message-ID Pattern (endet immer mit -000000)
|
||||||
|
ses_pattern = re.compile(r'010f[0-9a-f]{12}-[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}-000000')
|
||||||
|
|
||||||
|
# Die Message-ID der aktuellen Email (Bounce selbst) - diese wollen wir NICHT
|
||||||
|
current_msg_id = (parsed.get('Message-ID') or '').strip()
|
||||||
|
current_match = ses_pattern.search(current_msg_id)
|
||||||
|
current_id = current_match.group(0) if current_match else None
|
||||||
|
|
||||||
|
log(f"Current Message-ID: {current_id}", 'DEBUG')
|
||||||
|
|
||||||
|
# 1. Versuche Standard-Header (In-Reply-To, References)
|
||||||
|
for header in ['In-Reply-To', 'References']:
|
||||||
|
value = (parsed.get(header) or '').strip()
|
||||||
|
if value:
|
||||||
|
match = ses_pattern.search(value)
|
||||||
|
if match:
|
||||||
|
found_id = match.group(0)
|
||||||
|
# Nur nehmen wenn es NICHT die aktuelle Bounce-ID ist
|
||||||
|
if found_id != current_id:
|
||||||
|
log(f" Found Message-ID in {header}: {found_id}")
|
||||||
|
return found_id
|
||||||
|
|
||||||
|
# 2. Durchsuche den kompletten Email-Body (inkl. ALLE Attachments/Parts)
|
||||||
|
try:
|
||||||
|
body_text = ''
|
||||||
|
|
||||||
|
# Hole den kompletten Body als String
|
||||||
|
if parsed.is_multipart():
|
||||||
|
for part in parsed.walk():
|
||||||
|
content_type = part.get_content_type()
|
||||||
|
|
||||||
|
# SPEZIALFALL: message/rfc822 (eingebettete Messages)
|
||||||
|
if content_type == 'message/rfc822':
|
||||||
|
log(f" Processing embedded message/rfc822", 'DEBUG')
|
||||||
|
try:
|
||||||
|
# get_payload() gibt eine Liste mit einem EmailMessage-Objekt zurück!
|
||||||
|
payload = part.get_payload()
|
||||||
|
if isinstance(payload, list) and len(payload) > 0:
|
||||||
|
embedded_msg = payload[0]
|
||||||
|
# Hole Message-ID aus dem eingebetteten Message
|
||||||
|
embedded_id = (embedded_msg.get('Message-ID') or '').strip()
|
||||||
|
match = ses_pattern.search(embedded_id)
|
||||||
|
if match:
|
||||||
|
found_id = match.group(0)
|
||||||
|
log(f" Found ID in embedded msg: {found_id}", 'DEBUG')
|
||||||
|
# Nur nehmen wenn es NICHT die aktuelle Bounce-ID ist
|
||||||
|
if found_id != current_id:
|
||||||
|
log(f" ✓ Found Message-ID in embedded message: {found_id}")
|
||||||
|
return found_id
|
||||||
|
# Fallback: Konvertiere eingebettete Message zu String
|
||||||
|
body_text += embedded_msg.as_string()
|
||||||
|
except Exception as e:
|
||||||
|
log(f" Warning: Could not process embedded message: {e}", 'WARNING')
|
||||||
|
|
||||||
|
# Durchsuche ALLE anderen Parts (außer Binärdaten wie images)
|
||||||
|
elif content_type.startswith('text/') or content_type.startswith('application/'):
|
||||||
|
try:
|
||||||
|
payload = part.get_payload(decode=True)
|
||||||
|
if payload:
|
||||||
|
# Versuche als UTF-8, fallback auf Latin-1
|
||||||
|
try:
|
||||||
|
body_text += payload.decode('utf-8', errors='ignore')
|
||||||
|
except:
|
||||||
|
try:
|
||||||
|
body_text += payload.decode('latin-1', errors='ignore')
|
||||||
|
except:
|
||||||
|
# Letzter Versuch: als ASCII mit ignore
|
||||||
|
body_text += str(payload, errors='ignore')
|
||||||
|
except:
|
||||||
|
# Falls decode fehlschlägt, String-Payload holen
|
||||||
|
payload = part.get_payload()
|
||||||
|
if isinstance(payload, str):
|
||||||
|
body_text += payload
|
||||||
|
else:
|
||||||
|
# Nicht-Multipart Message
|
||||||
|
payload = parsed.get_payload(decode=True)
|
||||||
|
if payload:
|
||||||
|
try:
|
||||||
|
body_text = payload.decode('utf-8', errors='ignore')
|
||||||
|
except:
|
||||||
|
body_text = payload.decode('latin-1', errors='ignore')
|
||||||
|
|
||||||
|
# Suche alle SES Message-IDs im Body
|
||||||
|
matches = ses_pattern.findall(body_text)
|
||||||
|
if matches:
|
||||||
|
log(f" Found {len(matches)} total IDs in body: {matches}", 'DEBUG')
|
||||||
|
# Filtere die aktuelle Bounce-ID raus
|
||||||
|
candidates = [m for m in matches if m != current_id]
|
||||||
|
|
||||||
|
if candidates:
|
||||||
|
# Nehme die ERSTE der verbleibenden (meist die Original-ID)
|
||||||
|
log(f" Found {len(matches)} SES Message-ID(s) in body, using first (not bounce): {candidates[0]}")
|
||||||
|
return candidates[0]
|
||||||
|
else:
|
||||||
|
log(f" Found {len(matches)} SES Message-ID(s) but all match the bounce ID")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
log(f" Warning: Could not search body for Message-ID: {e}", 'WARNING')
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def test_with_file(filepath: str):
|
||||||
|
"""Test mit einer echten Email-Datei"""
|
||||||
|
print(f"\n{'='*70}")
|
||||||
|
print(f"Testing: {filepath}")
|
||||||
|
print('='*70)
|
||||||
|
|
||||||
|
with open(filepath, 'rb') as f:
|
||||||
|
raw_bytes = f.read()
|
||||||
|
|
||||||
|
parsed = BytesParser(policy=SMTPPolicy).parsebytes(raw_bytes)
|
||||||
|
|
||||||
|
print(f"\nEmail Headers:")
|
||||||
|
print(f" From: {parsed.get('From')}")
|
||||||
|
print(f" To: {parsed.get('To')}")
|
||||||
|
print(f" Subject: {parsed.get('Subject')}")
|
||||||
|
print(f" Message-ID: {parsed.get('Message-ID')}")
|
||||||
|
print(f" In-Reply-To: {parsed.get('In-Reply-To')}")
|
||||||
|
print(f" References: {parsed.get('References')}")
|
||||||
|
|
||||||
|
print(f"\n--- EXTRACTION ---")
|
||||||
|
result = extract_original_message_id(parsed)
|
||||||
|
|
||||||
|
print(f"\n{'='*70}")
|
||||||
|
print(f"RESULT: {result}")
|
||||||
|
print('='*70)
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
import sys
|
||||||
|
|
||||||
|
if len(sys.argv) > 1:
|
||||||
|
# Email-Datei als Argument
|
||||||
|
result = test_with_file(sys.argv[1])
|
||||||
|
|
||||||
|
# Exit code: 0 = success (ID found), 1 = failure (no ID)
|
||||||
|
sys.exit(0 if result else 1)
|
||||||
|
else:
|
||||||
|
print("Usage: python3 test_extract_v2.py <email_file>")
|
||||||
|
sys.exit(1)
|
||||||
170
worker.py
170
worker.py
|
|
@ -51,143 +51,97 @@ def get_bucket_name(domain):
|
||||||
"""Konvention: domain.tld -> domain-tld-emails"""
|
"""Konvention: domain.tld -> domain-tld-emails"""
|
||||||
return domain.replace('.', '-') + '-emails'
|
return domain.replace('.', '-') + '-emails'
|
||||||
|
|
||||||
def is_ses_bounce_or_autoreply(parsed):
|
def is_ses_bounce_notification(parsed):
|
||||||
"""Erkennt SES Bounces"""
|
"""
|
||||||
|
Prüft ob Email von SES MAILER-DAEMON ist
|
||||||
|
"""
|
||||||
from_h = (parsed.get('From') or '').lower()
|
from_h = (parsed.get('From') or '').lower()
|
||||||
auto_sub = (parsed.get('Auto-Submitted') or '').lower()
|
return 'mailer-daemon@us-east-2.amazonses.com' in from_h
|
||||||
is_mailer_daemon = 'mailer-daemon@' in from_h and 'amazonses.com' in from_h
|
|
||||||
is_auto_replied = 'auto-replied' in auto_sub or 'auto-generated' in auto_sub
|
|
||||||
return is_mailer_daemon or is_auto_replied
|
|
||||||
|
|
||||||
def extract_original_message_id(parsed):
|
|
||||||
|
def get_bounce_info_from_dynamodb(message_id):
|
||||||
"""
|
"""
|
||||||
Extrahiert Original SES Message-ID aus Email
|
Sucht Bounce-Info in DynamoDB anhand der Message-ID
|
||||||
SES Format: 010f[hex32]-[hex8]-[hex4]-[hex4]-[hex4]-[hex12]-[hex6]
|
Returns: dict mit bounce info oder None
|
||||||
"""
|
"""
|
||||||
import re
|
|
||||||
|
|
||||||
# SES Message-ID Pattern (endet immer mit -000000)
|
|
||||||
ses_pattern = re.compile(r'010f[0-9a-f]{12}-[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}-000000')
|
|
||||||
|
|
||||||
# 1. Versuche Standard-Header (In-Reply-To, References)
|
|
||||||
for header in ['In-Reply-To', 'References']:
|
|
||||||
value = (parsed.get(header) or '').strip()
|
|
||||||
if value:
|
|
||||||
match = ses_pattern.search(value)
|
|
||||||
if match:
|
|
||||||
log(f" Found Message-ID in {header}: {match.group(0)}")
|
|
||||||
return match.group(0)
|
|
||||||
|
|
||||||
# 2. Durchsuche Message-ID Header (manchmal steht dort die Original-ID)
|
|
||||||
msg_id_header = (parsed.get('Message-ID') or '').strip()
|
|
||||||
if msg_id_header:
|
|
||||||
match = ses_pattern.search(msg_id_header)
|
|
||||||
if match:
|
|
||||||
# Aber nur wenn es nicht die ID der aktuellen Bounce-Message ist
|
|
||||||
# (die beginnt oft auch mit 010f...)
|
|
||||||
pass # Wir überspringen das erstmal
|
|
||||||
|
|
||||||
# 3. Durchsuche den kompletten Email-Body (inkl. ALLE Attachments/Parts)
|
|
||||||
# Das fängt auch attached messages, text attachments, etc. ab
|
|
||||||
try:
|
try:
|
||||||
body_text = ''
|
response = msg_table.get_item(Key={'MessageId': message_id})
|
||||||
|
item = response.get('Item')
|
||||||
|
|
||||||
# Hole den kompletten Body als String
|
if not item:
|
||||||
if parsed.is_multipart():
|
log(f"⚠ No bounce record found for Message-ID: {message_id}")
|
||||||
for part in parsed.walk():
|
return None
|
||||||
content_type = part.get_content_type()
|
|
||||||
|
|
||||||
# Durchsuche ALLE Parts (außer Binärdaten wie images)
|
return {
|
||||||
# Text-Parts, HTML, attached messages, und auch application/* Parts
|
'original_source': item.get('original_source', ''),
|
||||||
if content_type.startswith('text/') or \
|
'bounceType': item.get('bounceType', 'Unknown'),
|
||||||
content_type == 'message/rfc822' or \
|
'bounceSubType': item.get('bounceSubType', 'Unknown'),
|
||||||
content_type.startswith('application/'):
|
'bouncedRecipients': item.get('bouncedRecipients', []),
|
||||||
try:
|
'timestamp': item.get('timestamp', '')
|
||||||
payload = part.get_payload(decode=True)
|
}
|
||||||
if payload:
|
|
||||||
# Versuche als UTF-8, fallback auf Latin-1
|
|
||||||
try:
|
|
||||||
body_text += payload.decode('utf-8', errors='ignore')
|
|
||||||
except:
|
|
||||||
try:
|
|
||||||
body_text += payload.decode('latin-1', errors='ignore')
|
|
||||||
except:
|
|
||||||
# Letzter Versuch: als ASCII mit ignore
|
|
||||||
body_text += str(payload, errors='ignore')
|
|
||||||
except:
|
|
||||||
# Falls decode fehlschlägt, String-Payload holen
|
|
||||||
payload = part.get_payload()
|
|
||||||
if isinstance(payload, str):
|
|
||||||
body_text += payload
|
|
||||||
else:
|
|
||||||
# Nicht-Multipart Message
|
|
||||||
payload = parsed.get_payload(decode=True)
|
|
||||||
if payload:
|
|
||||||
try:
|
|
||||||
body_text = payload.decode('utf-8', errors='ignore')
|
|
||||||
except:
|
|
||||||
body_text = payload.decode('latin-1', errors='ignore')
|
|
||||||
|
|
||||||
# Suche alle SES Message-IDs im Body
|
|
||||||
matches = ses_pattern.findall(body_text)
|
|
||||||
if matches:
|
|
||||||
# Nehme die ERSTE gefundene ID (meist die Original-ID)
|
|
||||||
# Die letzte ist oft die Bounce-Message selbst
|
|
||||||
log(f" Found {len(matches)} SES Message-ID(s) in body, using first: {matches[0]}")
|
|
||||||
return matches[0]
|
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
log(f" Warning: Could not search body for Message-ID: {e}", 'WARNING')
|
log(f"⚠ DynamoDB Error: {e}", 'ERROR')
|
||||||
|
return None
|
||||||
|
|
||||||
return None
|
|
||||||
|
|
||||||
def apply_bounce_logic(parsed, subject):
|
def apply_bounce_logic(parsed, subject):
|
||||||
"""
|
"""
|
||||||
Prüft auf Bounce, sucht in DynamoDB und schreibt Header um.
|
Prüft auf SES Bounce, sucht in DynamoDB und schreibt Header um.
|
||||||
Returns: (parsed_email_object, was_modified_bool)
|
Returns: (parsed_email_object, was_modified_bool)
|
||||||
"""
|
"""
|
||||||
if not is_ses_bounce_or_autoreply(parsed):
|
if not is_ses_bounce_notification(parsed):
|
||||||
return parsed, False
|
return parsed, False
|
||||||
|
|
||||||
log("🔍 Detected auto-response/bounce. Checking DynamoDB...")
|
log("🔍 Detected SES MAILER-DAEMON bounce notification")
|
||||||
original_msg_id = extract_original_message_id(parsed)
|
|
||||||
|
|
||||||
if not original_msg_id:
|
# Message-ID aus Header extrahieren
|
||||||
log("⚠ Could not extract original Message-ID")
|
message_id = (parsed.get('Message-ID') or '').strip('<>')
|
||||||
|
|
||||||
|
if not message_id:
|
||||||
|
log("⚠ Could not extract Message-ID from bounce notification")
|
||||||
return parsed, False
|
return parsed, False
|
||||||
|
|
||||||
try:
|
log(f" Looking up Message-ID: {message_id}")
|
||||||
# Lookup in DynamoDB
|
|
||||||
result = msg_table.get_item(Key={'MessageId': original_msg_id})
|
|
||||||
item = result.get('Item')
|
|
||||||
|
|
||||||
if not item:
|
# Lookup in DynamoDB
|
||||||
log(f"⚠ No DynamoDB record found for {original_msg_id}")
|
bounce_info = get_bounce_info_from_dynamodb(message_id)
|
||||||
return parsed, False
|
|
||||||
|
|
||||||
# Treffer!
|
if not bounce_info:
|
||||||
orig_source = item.get('source', '')
|
return parsed, False
|
||||||
orig_destinations = item.get('destinations', [])
|
|
||||||
original_recipient = orig_destinations[0] if orig_destinations else ''
|
|
||||||
|
|
||||||
if original_recipient:
|
# Bounce Info ausgeben
|
||||||
log(f"✓ Found original sender: {orig_source} -> intended for {original_recipient}")
|
original_source = bounce_info['original_source']
|
||||||
|
bounced_recipients = bounce_info['bouncedRecipients']
|
||||||
|
bounce_type = bounce_info['bounceType']
|
||||||
|
bounce_subtype = bounce_info['bounceSubType']
|
||||||
|
|
||||||
# Rewrite Headers
|
log(f"✓ Found bounce info:")
|
||||||
parsed['X-Original-SES-From'] = parsed.get('From', '')
|
log(f" Original sender: {original_source}")
|
||||||
parsed.replace_header('From', original_recipient)
|
log(f" Bounce type: {bounce_type}/{bounce_subtype}")
|
||||||
|
log(f" Bounced recipients: {bounced_recipients}")
|
||||||
|
|
||||||
if not parsed.get('Reply-To'):
|
# Nehme den ersten bounced recipient als neuen Absender
|
||||||
parsed['Reply-To'] = original_recipient
|
# (bei Multiple Recipients kann es mehrere geben)
|
||||||
|
if bounced_recipients:
|
||||||
|
new_from = bounced_recipients[0]
|
||||||
|
|
||||||
if 'delivery status notification' in subject.lower():
|
# Rewrite Headers
|
||||||
parsed.replace_header('Subject', f"Delivery Status: {original_recipient}")
|
parsed['X-Original-SES-From'] = parsed.get('From', '')
|
||||||
|
parsed['X-Bounce-Type'] = f"{bounce_type}/{bounce_subtype}"
|
||||||
|
parsed.replace_header('From', new_from)
|
||||||
|
|
||||||
return parsed, True
|
if not parsed.get('Reply-To'):
|
||||||
|
parsed['Reply-To'] = new_from
|
||||||
|
|
||||||
except Exception as e:
|
# Subject anpassen
|
||||||
log(f"⚠ DynamoDB Error: {e}")
|
if 'delivery status notification' in subject.lower() or 'thanks for your submission' in subject.lower():
|
||||||
|
parsed.replace_header('Subject', f"Delivery Status: {new_from}")
|
||||||
|
|
||||||
|
log(f"✓ Rewritten FROM: {new_from}")
|
||||||
|
return parsed, True
|
||||||
|
|
||||||
|
log("⚠ No bounced recipients found in bounce info")
|
||||||
return parsed, False
|
return parsed, False
|
||||||
|
|
||||||
def signal_handler(signum, frame):
|
def signal_handler(signum, frame):
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue