Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9e6d1a8cb7 |
@@ -1,214 +0,0 @@
|
|||||||
# Revision 1.69 2006/11/04 22:09:39 customdesigned
|
|
||||||
# Another lame DSN heuristic. Block PTR cache poisoning attack.
|
|
||||||
#
|
|
||||||
# Revision 1.68 2006/10/04 03:46:01 customdesigned
|
|
||||||
# Fix defaults.
|
|
||||||
#
|
|
||||||
# Revision 1.67 2006/10/01 01:44:06 customdesigned
|
|
||||||
# case_sensitive_localpart option, more delayed bounce heuristics,
|
|
||||||
# optional smart_alias section.
|
|
||||||
#
|
|
||||||
# Revision 1.66 2006/07/26 16:42:26 customdesigned
|
|
||||||
# Support CBV timeout
|
|
||||||
#
|
|
||||||
# Revision 1.65 2006/06/21 22:22:00 customdesigned
|
|
||||||
# Handle multi-line headers in delayed dsns.
|
|
||||||
#
|
|
||||||
# Revision 1.64 2006/06/21 21:12:04 customdesigned
|
|
||||||
# More delayed reject token headers.
|
|
||||||
# Don't require HELO pass for CBV.
|
|
||||||
#
|
|
||||||
# Revision 1.63 2006/05/21 03:41:44 customdesigned
|
|
||||||
# Fail dsn
|
|
||||||
#
|
|
||||||
# Revision 1.61 2006/05/17 21:28:07 customdesigned
|
|
||||||
# Create GOSSiP record only when connection will procede to DATA.
|
|
||||||
#
|
|
||||||
# Revision 1.60 2006/05/12 16:14:48 customdesigned
|
|
||||||
# Don't require SPF pass for white/black listing mail from trusted relay.
|
|
||||||
# Support localpart wildcard for white and black lists.
|
|
||||||
#
|
|
||||||
# Revision 1.59 2006/04/06 18:14:17 customdesigned
|
|
||||||
# Check whitelist/blacklist even when not checking SPF (e.g. trusted relay).
|
|
||||||
#
|
|
||||||
# Revision 1.58 2006/03/10 20:52:49 customdesigned
|
|
||||||
# Use re to recognize failure DSNs.
|
|
||||||
#
|
|
||||||
# Revision 1.57 2006/03/07 20:50:54 customdesigned
|
|
||||||
# Use signed Message-ID in delayed reject to blacklist senders
|
|
||||||
#
|
|
||||||
# Revision 1.56 2006/02/24 02:12:54 customdesigned
|
|
||||||
# Properly report hard PermError (lax mode fails also) by always setting
|
|
||||||
# perm_error attribute with PermError exception. Improve reporting of
|
|
||||||
# invalid domain PermError.
|
|
||||||
#
|
|
||||||
# Revision 1.55 2006/02/17 05:04:29 customdesigned
|
|
||||||
# Use SRS sign domain list.
|
|
||||||
# Accept but do not use for training whitelisted senders without SPF pass.
|
|
||||||
# Immediate rejection of unsigned bounces.
|
|
||||||
#
|
|
||||||
# Revision 1.54 2006/02/16 02:16:36 customdesigned
|
|
||||||
# User specific SPF receiver policy.
|
|
||||||
#
|
|
||||||
# Revision 1.53 2006/02/12 04:15:01 customdesigned
|
|
||||||
# Remove spf dependency for iniplist
|
|
||||||
#
|
|
||||||
# Revision 1.52 2006/02/12 02:12:08 customdesigned
|
|
||||||
# Use CIDR notation for internal connect list.
|
|
||||||
#
|
|
||||||
# Revision 1.51 2006/02/12 01:13:58 customdesigned
|
|
||||||
# Don't check rcpt user list when signed MFROM.
|
|
||||||
#
|
|
||||||
# Revision 1.50 2006/02/09 20:39:43 customdesigned
|
|
||||||
# Use CIDR notation for trusted_relay iplist
|
|
||||||
#
|
|
||||||
# Revision 1.49 2006/01/30 23:14:48 customdesigned
|
|
||||||
# put back eom condition
|
|
||||||
#
|
|
||||||
# Revision 1.48 2006/01/12 20:31:24 customdesigned
|
|
||||||
# Accelerate training via whitelist and blacklist.
|
|
||||||
#
|
|
||||||
# Revision 1.47 2005/12/29 04:49:10 customdesigned
|
|
||||||
# Do not auto-whitelist autoreplys
|
|
||||||
#
|
|
||||||
# Revision 1.46 2005/12/28 20:17:29 customdesigned
|
|
||||||
# Expire and renew AddrCache entries
|
|
||||||
#
|
|
||||||
# Revision 1.45 2005/12/23 22:34:46 customdesigned
|
|
||||||
# Put guessed result in separate header.
|
|
||||||
#
|
|
||||||
# Revision 1.44 2005/12/23 21:47:07 customdesigned
|
|
||||||
# Move Received-SPF header to top.
|
|
||||||
#
|
|
||||||
# Revision 1.43 2005/12/09 16:54:01 customdesigned
|
|
||||||
# Select neutral DSN template for best_guess
|
|
||||||
#
|
|
||||||
# Revision 1.42 2005/12/01 22:42:32 customdesigned
|
|
||||||
# improve gossip support.
|
|
||||||
# Initialize srs_domain from srs.srs config property. Should probably
|
|
||||||
# always block unsigned DSN when signing all.
|
|
||||||
#
|
|
||||||
# Revision 1.41 2005/12/01 18:59:25 customdesigned
|
|
||||||
# Fix neutral policy. pobox.com -> openspf.org
|
|
||||||
#
|
|
||||||
# Revision 1.40 2005/11/07 21:22:35 customdesigned
|
|
||||||
# GOSSiP support, local database only.
|
|
||||||
#
|
|
||||||
# Revision 1.39 2005/10/31 00:04:58 customdesigned
|
|
||||||
# Simple implementation of trusted_forwarder list. Inefficient for
|
|
||||||
# more than 1 or 2 entries.
|
|
||||||
#
|
|
||||||
# Revision 1.38 2005/10/28 19:36:54 customdesigned
|
|
||||||
# Don't check internal_domains for trusted_relay.
|
|
||||||
#
|
|
||||||
# Revision 1.37 2005/10/28 09:30:49 customdesigned
|
|
||||||
# Do not send quarantine DSN when sender is DSN.
|
|
||||||
#
|
|
||||||
# Revision 1.36 2005/10/23 16:01:29 customdesigned
|
|
||||||
# Consider MAIL FROM a match for supply_sender when a subdomain of From or Sender
|
|
||||||
#
|
|
||||||
# Revision 1.35 2005/10/20 18:47:27 customdesigned
|
|
||||||
# Configure auto_whitelist senders.
|
|
||||||
#
|
|
||||||
# Revision 1.34 2005/10/19 21:07:49 customdesigned
|
|
||||||
# access.db stores keys in lower case
|
|
||||||
#
|
|
||||||
# Revision 1.33 2005/10/19 19:37:50 customdesigned
|
|
||||||
# Train screener on whitelisted messages.
|
|
||||||
#
|
|
||||||
# Revision 1.32 2005/10/14 16:17:31 customdesigned
|
|
||||||
# Auto whitelist refinements.
|
|
||||||
#
|
|
||||||
# Revision 1.31 2005/10/14 01:14:08 customdesigned
|
|
||||||
# Auto whitelist feature.
|
|
||||||
#
|
|
||||||
# Revision 1.30 2005/10/12 16:36:30 customdesigned
|
|
||||||
# Release 0.8.3
|
|
||||||
#
|
|
||||||
# Revision 1.29 2005/10/11 22:50:07 customdesigned
|
|
||||||
# Always check HELO except for SPF pass, temperror.
|
|
||||||
#
|
|
||||||
# Revision 1.28 2005/10/10 23:50:20 customdesigned
|
|
||||||
# Use logging module to make logging threadsafe (avoid splitting log lines)
|
|
||||||
#
|
|
||||||
# Revision 1.27 2005/10/10 20:15:33 customdesigned
|
|
||||||
# Configure SPF policy via sendmail access file.
|
|
||||||
#
|
|
||||||
# Revision 1.26 2005/10/07 03:23:40 customdesigned
|
|
||||||
# Banned users option. Experimental feature to supply Sender when
|
|
||||||
# missing and MFROM domain doesn't match From. Log cipher bits for
|
|
||||||
# SMTP AUTH. Sketch access file feature.
|
|
||||||
#
|
|
||||||
# Revision 1.25 2005/09/08 03:55:08 customdesigned
|
|
||||||
# Handle perverse MFROM quoting.
|
|
||||||
#
|
|
||||||
# Revision 1.24 2005/08/18 03:36:54 customdesigned
|
|
||||||
# Don't innoculate with SCREENED mail.
|
|
||||||
#
|
|
||||||
# Revision 1.23 2005/08/17 19:35:27 customdesigned
|
|
||||||
# Send DSN before adding message to quarantine.
|
|
||||||
#
|
|
||||||
# Revision 1.22 2005/08/11 22:17:58 customdesigned
|
|
||||||
# Consider SMTP AUTH connections internal.
|
|
||||||
#
|
|
||||||
# Revision 1.21 2005/08/04 21:21:31 customdesigned
|
|
||||||
# Treat fail like softfail for selected (braindead) domains.
|
|
||||||
# Treat mail according to extended processing results, but
|
|
||||||
# report any PermError that would officially result via DSN.
|
|
||||||
#
|
|
||||||
# Revision 1.20 2005/08/02 18:04:35 customdesigned
|
|
||||||
# Keep screened honeypot mail, but optionally discard honeypot only mail.
|
|
||||||
#
|
|
||||||
# Revision 1.19 2005/07/20 03:30:04 customdesigned
|
|
||||||
# Check pydspam version for honeypot, include latest pyspf changes.
|
|
||||||
#
|
|
||||||
# Revision 1.18 2005/07/17 01:25:44 customdesigned
|
|
||||||
# Log as well as use extended result for best guess.
|
|
||||||
#
|
|
||||||
# Revision 1.17 2005/07/15 20:25:36 customdesigned
|
|
||||||
# Use extended results processing for best_guess.
|
|
||||||
#
|
|
||||||
# Revision 1.16 2005/07/14 03:23:33 customdesigned
|
|
||||||
# Make SES package optional. Initial honeypot support.
|
|
||||||
#
|
|
||||||
# Revision 1.15 2005/07/06 04:05:40 customdesigned
|
|
||||||
# Initial SES integration.
|
|
||||||
#
|
|
||||||
# Revision 1.14 2005/07/02 23:27:31 customdesigned
|
|
||||||
# Don't match hostnames for internal connects.
|
|
||||||
#
|
|
||||||
# Revision 1.13 2005/07/01 16:30:24 customdesigned
|
|
||||||
# Always log trusted Received and Received-SPF headers.
|
|
||||||
#
|
|
||||||
# Revision 1.12 2005/06/20 22:35:35 customdesigned
|
|
||||||
# Setreply for rejectvirus.
|
|
||||||
#
|
|
||||||
# Revision 1.11 2005/06/17 02:07:20 customdesigned
|
|
||||||
# Release 0.8.1
|
|
||||||
#
|
|
||||||
# Revision 1.10 2005/06/16 18:35:51 customdesigned
|
|
||||||
# Ignore HeaderParseError decoding header
|
|
||||||
#
|
|
||||||
# Revision 1.9 2005/06/14 21:55:29 customdesigned
|
|
||||||
# Check internal_domains for outgoing mail.
|
|
||||||
#
|
|
||||||
# Revision 1.8 2005/06/06 18:24:59 customdesigned
|
|
||||||
# Properly log exceptions from pydspam
|
|
||||||
#
|
|
||||||
# Revision 1.7 2005/06/04 19:41:16 customdesigned
|
|
||||||
# Fix bugs from testing RPM
|
|
||||||
#
|
|
||||||
# Revision 1.6 2005/06/03 04:57:05 customdesigned
|
|
||||||
# Organize config reader by section. Create defang section.
|
|
||||||
#
|
|
||||||
# Revision 1.5 2005/06/02 15:00:17 customdesigned
|
|
||||||
# Configure banned extensions. Scan zipfile option with test case.
|
|
||||||
#
|
|
||||||
# Revision 1.4 2005/06/02 04:18:55 customdesigned
|
|
||||||
# Update copyright notices after reading article on /.
|
|
||||||
#
|
|
||||||
# Revision 1.3 2005/06/02 02:09:00 customdesigned
|
|
||||||
# Record timestamp in send_dsn.log
|
|
||||||
#
|
|
||||||
# Revision 1.2 2005/06/02 01:00:36 customdesigned
|
|
||||||
# Support configurable templates for DSNs.
|
|
||||||
@@ -116,7 +116,7 @@ The CBV policy requires a valid HELO name. If the EHLO name is
|
|||||||
RFC2822 compliant, then a DSN is sent to the alleged sender. The
|
RFC2822 compliant, then a DSN is sent to the alleged sender. The
|
||||||
template for the DSN is selected according to the SPF result:
|
template for the DSN is selected according to the SPF result:
|
||||||
|
|
||||||
Fail: fail.txt
|
Fail: softfail.txt
|
||||||
SoftFail: softfail.txt
|
SoftFail: softfail.txt
|
||||||
Neutral: neutral.txt
|
Neutral: neutral.txt
|
||||||
PermError: permerror.txt
|
PermError: permerror.txt
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ include NEWS
|
|||||||
include HOWTO
|
include HOWTO
|
||||||
include CREDITS
|
include CREDITS
|
||||||
include README
|
include README
|
||||||
include ChangeLog
|
|
||||||
include MANIFEST.in
|
include MANIFEST.in
|
||||||
include testsample.py
|
include testsample.py
|
||||||
include testmime.py
|
include testmime.py
|
||||||
|
|||||||
+99
-40
@@ -4,25 +4,98 @@
|
|||||||
|
|
||||||
# Send DSNs, do call back verification,
|
# Send DSNs, do call back verification,
|
||||||
# and generate DSN messages from a template
|
# and generate DSN messages from a template
|
||||||
# $Log$
|
|
||||||
# Revision 1.12 2006/07/26 16:37:35 customdesigned
|
|
||||||
# Support timeout.
|
|
||||||
#
|
|
||||||
# Revision 1.11 2006/06/21 21:07:11 customdesigned
|
|
||||||
# Include header fields in DSN template.
|
|
||||||
#
|
|
||||||
# Revision 1.10 2006/05/24 20:56:35 customdesigned
|
|
||||||
# Remove default templates. Scrub test.
|
|
||||||
#
|
|
||||||
|
|
||||||
import smtplib
|
import smtplib
|
||||||
import spf
|
import spf
|
||||||
import socket
|
import socket
|
||||||
from email.Message import Message
|
from email.Message import Message
|
||||||
import Milter
|
import Milter
|
||||||
import time
|
|
||||||
|
|
||||||
def send_dsn(mailfrom,receiver,msg=None,timeout=600):
|
nospf_msg = """Subject: Critical mail server configuration error
|
||||||
|
|
||||||
|
This is an automatically generated Delivery Status Notification.
|
||||||
|
|
||||||
|
THIS IS A WARNING MESSAGE ONLY.
|
||||||
|
|
||||||
|
YOU DO *NOT* NEED TO RESEND YOUR MESSAGE.
|
||||||
|
|
||||||
|
Delivery to the following recipients has been delayed.
|
||||||
|
|
||||||
|
%(rcpt)s
|
||||||
|
|
||||||
|
Subject: %(subject)s
|
||||||
|
|
||||||
|
Someone at IP address %(connectip)s sent an email claiming
|
||||||
|
to be from %(sender)s.
|
||||||
|
|
||||||
|
If that wasn't you, then your domain, %(sender_domain)s,
|
||||||
|
was forged - i.e. used without your knowlege or authorization by
|
||||||
|
someone attempting to steal your mail identity. This is a very
|
||||||
|
serious problem, and you need to provide authentication for your
|
||||||
|
SMTP (email) servers to prevent criminals from forging your
|
||||||
|
domain. The simplest step is usually to publish an SPF record
|
||||||
|
with your Sender Policy.
|
||||||
|
|
||||||
|
For more information, see: http://spfhelp.net
|
||||||
|
|
||||||
|
I hate to annoy you with a DSN (Delivery Status
|
||||||
|
Notification) from a possibly forged email, but since you
|
||||||
|
have not published a sender policy, there is no other way
|
||||||
|
of bringing this to your attention.
|
||||||
|
|
||||||
|
If it *was* you that sent the email, then your email domain
|
||||||
|
or configuration is in error. If you don't know anything
|
||||||
|
about mail servers, then pass this on to your SMTP (mail)
|
||||||
|
server administrator. We have accepted the email anyway, in
|
||||||
|
case it is important, but we couldn't find anything about
|
||||||
|
the mail submitter at %(connectip)s to distinguish it from a
|
||||||
|
zombie (compromised/infected computer - usually a Windows
|
||||||
|
PC). There was no PTR record for its IP address (PTR names
|
||||||
|
that contain the IP address don't count). RFC2821 requires
|
||||||
|
that your hello name be a FQN (Fully Qualified domain Name,
|
||||||
|
i.e. at least one dot) that resolves to the IP address of
|
||||||
|
the mail sender. In addition, just like for PTR, we don't
|
||||||
|
accept a helo name that contains the IP, since this doesn't
|
||||||
|
help to identify you. The hello name you used,
|
||||||
|
%(heloname)s, was invalid.
|
||||||
|
|
||||||
|
Furthermore, there was no SPF record for the sending domain
|
||||||
|
%(sender_domain)s. We even tried to find its IP in any A or
|
||||||
|
MX records for your domain, but that failed also. We really
|
||||||
|
should reject mail from anonymous mail clients, but in case
|
||||||
|
it is important, we are accepting it anyway.
|
||||||
|
|
||||||
|
We are sending you this message to alert you to the fact that
|
||||||
|
|
||||||
|
Either - Someone is forging your domain.
|
||||||
|
Or - You have problems with your email configuration.
|
||||||
|
Or - Possibly both.
|
||||||
|
|
||||||
|
If you need further assistance, please do not hesitate to
|
||||||
|
contact me again.
|
||||||
|
|
||||||
|
Kind regards,
|
||||||
|
|
||||||
|
postmaster@%(receiver)s
|
||||||
|
"""
|
||||||
|
|
||||||
|
softfail_msg = """Subject: SPF softfail (POSSIBLE FORGERY)
|
||||||
|
|
||||||
|
This is an automatically generated Delivery Status Notification.
|
||||||
|
|
||||||
|
THIS IS A WARNING MESSAGE ONLY.
|
||||||
|
|
||||||
|
YOU DO *NOT* NEED TO RESEND YOUR MESSAGE.
|
||||||
|
|
||||||
|
Delivery to the following recipients has been delayed.
|
||||||
|
|
||||||
|
%(rcpt)s
|
||||||
|
|
||||||
|
Subject: %(subject)s
|
||||||
|
Received-SPF: %(spf_result)s
|
||||||
|
"""
|
||||||
|
|
||||||
|
def send_dsn(mailfrom,receiver,msg=None):
|
||||||
"""Send DSN. If msg is None, do callback verification.
|
"""Send DSN. If msg is None, do callback verification.
|
||||||
Mailfrom is original sender we are sending DSN or CBV to.
|
Mailfrom is original sender we are sending DSN or CBV to.
|
||||||
Receiver is the MTA sending the DSN.
|
Receiver is the MTA sending the DSN.
|
||||||
@@ -35,7 +108,6 @@ def send_dsn(mailfrom,receiver,msg=None,timeout=600):
|
|||||||
else:
|
else:
|
||||||
mxlist.sort()
|
mxlist.sort()
|
||||||
smtp = smtplib.SMTP()
|
smtp = smtplib.SMTP()
|
||||||
toolate = time.time() + timeout
|
|
||||||
for prior,host in mxlist:
|
for prior,host in mxlist:
|
||||||
try:
|
try:
|
||||||
smtp.connect(host)
|
smtp.connect(host)
|
||||||
@@ -73,17 +145,11 @@ def send_dsn(mailfrom,receiver,msg=None,timeout=600):
|
|||||||
pass # any other error, try next MX
|
pass # any other error, try next MX
|
||||||
except socket.error:
|
except socket.error:
|
||||||
pass # MX didn't accept connections, try next one
|
pass # MX didn't accept connections, try next one
|
||||||
except socket.timeout:
|
|
||||||
pass # MX too slow, try next one
|
|
||||||
smtp.close()
|
smtp.close()
|
||||||
if time.time() > toolate:
|
|
||||||
return (450,'No MX response within %f minutes'%(timeout/60.0))
|
|
||||||
return (450,'No MX servers available') # temp error
|
return (450,'No MX servers available') # temp error
|
||||||
|
|
||||||
def create_msg(q,rcptlist,origmsg=None,template=None):
|
def create_msg(q,rcptlist,origmsg=None,template=None):
|
||||||
"Create a DSN message from a template. Template must be '\n' separated."
|
"Create a DSN message from a template. Template must be '\n' separated."
|
||||||
if not template:
|
|
||||||
return None
|
|
||||||
heloname = q.h
|
heloname = q.h
|
||||||
sender = q.s
|
sender = q.s
|
||||||
connectip = q.i
|
connectip = q.i
|
||||||
@@ -100,37 +166,30 @@ def create_msg(q,rcptlist,origmsg=None,template=None):
|
|||||||
|
|
||||||
msg = Message()
|
msg = Message()
|
||||||
|
|
||||||
|
msg.add_header('To',sender)
|
||||||
|
msg.add_header('From','postmaster@%s'%receiver)
|
||||||
|
msg.add_header('Auto-Submitted','auto-generated (configuration error)')
|
||||||
msg.add_header('X-Mailer','PyMilter-'+Milter.__version__)
|
msg.add_header('X-Mailer','PyMilter-'+Milter.__version__)
|
||||||
msg.set_type('text/plain')
|
msg.set_type('text/plain')
|
||||||
|
|
||||||
hdrs,body = template.split('\n\n',1)
|
if not template:
|
||||||
|
if spf_result and spf_result.startswith('softfail'):
|
||||||
|
template = softfail_msg
|
||||||
|
else:
|
||||||
|
template = nospf_msg
|
||||||
|
hdrs,body = template.split('\n',1)
|
||||||
for ln in hdrs.splitlines():
|
for ln in hdrs.splitlines():
|
||||||
name,val = ln.split(':',1)
|
name,val = ln.split(':',1)
|
||||||
msg.add_header(name,(val % locals()).strip())
|
msg.add_header(name,(val % locals()).strip())
|
||||||
msg.set_payload(body % locals())
|
msg.set_payload(body % locals())
|
||||||
# add headers if missing from old template
|
|
||||||
if 'to' not in msg:
|
|
||||||
msg.add_header('To',sender)
|
|
||||||
if 'from' not in msg:
|
|
||||||
msg.add_header('From','postmaster@%s'%receiver)
|
|
||||||
if 'auto-submitted' not in msg:
|
|
||||||
msg.add_header('Auto-Submitted','auto-generated')
|
|
||||||
return msg
|
return msg
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
q = spf.query('192.168.9.50',
|
q = spf.query('192.168.9.50',
|
||||||
'SRS0=pmeHL=RH==stuart@example.com',
|
'SRS0=pmeHL=RH=bmsi.com=stuart@bmsi.com',
|
||||||
'red.example.com',receiver='mail.example.com')
|
'bmsred.bmsi.com',receiver='mail.bmsi.com')
|
||||||
q.result = 'softfail'
|
msg = create_msg(q,['charlie@jsconnor.com'],None,None)
|
||||||
q.perm_error = None
|
|
||||||
msg = create_msg(q,['charlie@example.com'],None,
|
|
||||||
"""From: postmaster@%(receiver)s
|
|
||||||
To: %(sender)s
|
|
||||||
Subject: Test
|
|
||||||
|
|
||||||
Test DSN template
|
|
||||||
"""
|
|
||||||
)
|
|
||||||
print msg.as_string()
|
print msg.as_string()
|
||||||
# print send_dsn(f,msg.as_string())
|
# print send_dsn(f,msg.as_string())
|
||||||
# print send_dsn(q.s,'mail.example.com',msg.as_string())
|
print send_dsn(q.s,'mail.bmsi.com',msg.as_string())
|
||||||
|
|||||||
@@ -1,26 +1,5 @@
|
|||||||
Here is a history of user visible changes to Python milter.
|
Here is a history of user visible changes to Python milter.
|
||||||
0.8.7 Move spf module to pyspf
|
|
||||||
Prevent PTR cache poisoning
|
|
||||||
More lame bounce heuristics
|
|
||||||
Do plain CBV when template is missing
|
|
||||||
0.8.6 Support CBV timeout
|
|
||||||
Support fail template, headers in templates
|
|
||||||
Create GOSSiP record only when connection will procede to DATA.
|
|
||||||
More SPF lax heuristics
|
|
||||||
Don't require SPF pass for white/black listing mail from trusted relay.
|
|
||||||
Support localpart wildcard for white and black lists.
|
|
||||||
Delay reject of unsigned RCPT for postmaster and abuse only
|
|
||||||
Fix dsn reporting of hard permerror
|
|
||||||
Resolve FIXME for wrap_close in miltermodule.c
|
|
||||||
Add Message-ID to DSNs
|
|
||||||
Use signed Message-ID in delayed reject to blacklist senders
|
|
||||||
Auto-train via blacklist and auto-whitelist
|
|
||||||
Don't check userlist for signed MFROM
|
|
||||||
Accept but skip DSPAM training for whitelisted senders without SPF PASS
|
|
||||||
Report GC stats
|
|
||||||
Support CIDR matching for IP lists
|
|
||||||
Support pysrs sign feature
|
|
||||||
Support localpart specific SPF policy in access file
|
|
||||||
0.8.5 Simple trusted_forwarder implementation.
|
0.8.5 Simple trusted_forwarder implementation.
|
||||||
Fix access_file neutral policy
|
Fix access_file neutral policy
|
||||||
Move Received-SPF header to beginning of headers
|
Move Received-SPF header to beginning of headers
|
||||||
|
|||||||
@@ -1,103 +1,3 @@
|
|||||||
When bms.py can't find templates, it passes None to dsn.create_msg(),
|
|
||||||
which uses local variable as backup, which no longer exist.
|
|
||||||
|
|
||||||
Purge old GOSSiP records nightly.
|
|
||||||
|
|
||||||
Find and use X-GOSSiP: header for SPAM: and FP: submissions. Would need to
|
|
||||||
keep tags longer.
|
|
||||||
|
|
||||||
Generate DSNs according to RFC 3464
|
|
||||||
|
|
||||||
Parse incoming 3464 DSNs for "Action: failed" to recognize delayed
|
|
||||||
failures. This works regardless of Subject.
|
|
||||||
|
|
||||||
Get temperror policy from access file.
|
|
||||||
|
|
||||||
When training with spam, REJECT after data so that mistakenly blacklisted
|
|
||||||
senders at least get an error.
|
|
||||||
|
|
||||||
Reporting explanation for failure should show source if sender
|
|
||||||
provided explanation.
|
|
||||||
|
|
||||||
Reports PROBATION even when rejecting message (works, but confusing in log).
|
|
||||||
|
|
||||||
Bug in Auto-whitelist. Recent Auto-whitelist doesn't override expired entry.
|
|
||||||
|
|
||||||
DONE Delayed_failure detection needs to handle multi-line header fields.
|
|
||||||
Also, delayed_failure should be recognized when addressed to
|
|
||||||
postmaster@helodomain
|
|
||||||
|
|
||||||
Need to use wildcards in blacklist.log: *.madcowsrecord.net
|
|
||||||
Need to exclude emails like !*-admin@example.com in whitelist_sender.
|
|
||||||
|
|
||||||
SPF permerror diagnostics should include corrected mechanism.
|
|
||||||
|
|
||||||
Delay SPF check until RCPT TO. Cache result to avoid repeating
|
|
||||||
for multiple RCPT. This avoids overhead for invalid RCPT, and
|
|
||||||
allows for per RCPT local policy.
|
|
||||||
|
|
||||||
Add auto-blacklisted senders to blacklist.log with timestamp.
|
|
||||||
|
|
||||||
Received-SPF header field should show identity that was checked.
|
|
||||||
|
|
||||||
Check SPF for outgoing mail (including local policy for internal addresses).
|
|
||||||
This could also solve the second part of the mail from relay problem below.
|
|
||||||
|
|
||||||
Whitelisted sender from trusted relay get PROBATION. Need to extracted
|
|
||||||
SPF result from headers - and in the case of mail internal to relay
|
|
||||||
(e.g. bmsi.com), supply 'pass' result.
|
|
||||||
|
|
||||||
FIXME: DSN for Permerror shows 'None' for error under some condition.
|
|
||||||
|
|
||||||
Another metaDSN format:
|
|
||||||
Subject: Delivery Report
|
|
||||||
...
|
|
||||||
Original-Envelope-ID: SRS0...@...
|
|
||||||
|
|
||||||
For selected domains, check rcpts via CBV before accepting mail. Cache
|
|
||||||
results. This will kick out dictonary attacks against a mail domain
|
|
||||||
behind a gateway sooner.
|
|
||||||
|
|
||||||
Allow blacklisted emails as well as domains in blacklist.log. Use same
|
|
||||||
data structure as autowhitelist.log. Add emails blacklisted via CBV
|
|
||||||
so that they are remembered across milter restarts.
|
|
||||||
|
|
||||||
Make all dictionaries work like honeypot. Do not train as ham unless
|
|
||||||
whitelisted. Train on blacklisted messages, or spam feedback. This
|
|
||||||
can be called Train On Error. Should be possible to startup
|
|
||||||
with training on everything to get dictionary built fast, then switch
|
|
||||||
to train on error to minimize labor.
|
|
||||||
|
|
||||||
Allow unsigned DSNs from selected domains (that don't accept signed MFROM,
|
|
||||||
e.g. verizon.net).
|
|
||||||
|
|
||||||
Added Message-ID header to DSN with SRS signed sender. When seen on incoming
|
|
||||||
rfc ignorant failure message, blacklist sender.
|
|
||||||
|
|
||||||
Allow verified hostnames for trusted_relay. E.g. HELO name that
|
|
||||||
passes SPF.
|
|
||||||
|
|
||||||
Table of sendmail macros for documentation.
|
|
||||||
|
|
||||||
When do we get two hello calls? STARTTLS is one reason.
|
|
||||||
|
|
||||||
Option: accept mail from auto-whitelisted senders even with spf-fail,
|
|
||||||
but do not update dspam. This can be done for individual senders or domains
|
|
||||||
using the access file.
|
|
||||||
|
|
||||||
pysrs: SRS doesn't get applied to proper recipients when there are
|
|
||||||
multiple recipients. This requires debugging cf scripts - yuk.
|
|
||||||
|
|
||||||
auto_whitelist false_positives from quarantine - perhaps only when
|
|
||||||
user selects special button (use special header to communicate
|
|
||||||
that from dspamcgi.py to milter.)
|
|
||||||
|
|
||||||
Use send_dsn.log for blacklist also. AddrCache needs localpart
|
|
||||||
wildcard (e.g. empty localpart).
|
|
||||||
|
|
||||||
Quarantined mail is missing headers modified/added by milter after
|
|
||||||
checking dspam.
|
|
||||||
|
|
||||||
Require signed MFROM for all incoming bounces when signing all outgoing mail -
|
Require signed MFROM for all incoming bounces when signing all outgoing mail -
|
||||||
except from trusted relays.
|
except from trusted relays.
|
||||||
|
|
||||||
|
|||||||
+153
@@ -0,0 +1,153 @@
|
|||||||
|
#!/usr/bin/python2.3
|
||||||
|
|
||||||
|
# Convert a MS Caller-ID entry (XML) to a SPF entry
|
||||||
|
#
|
||||||
|
# (c) 2004 by Ernesto Baschny
|
||||||
|
# (c) 2004 Python version by Stuart Gathman
|
||||||
|
#
|
||||||
|
# Date: 2004-02-25
|
||||||
|
# Version: 1.0
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# ./cid2spf.pl "<ep xmlns='http://ms.net/1'>...</ep>"
|
||||||
|
#
|
||||||
|
# Note that the 'include' directives will also have to be checked and
|
||||||
|
# "translated". Future versions of this script might be able to get a
|
||||||
|
# domain name as an argument and "crawl" the DNS for the necessary
|
||||||
|
# information.
|
||||||
|
#
|
||||||
|
# A complete reverse translation (SPF -> CID) might be impossible, since
|
||||||
|
# there are no way to handle:
|
||||||
|
# - PTR and EXISTS mechanism
|
||||||
|
# - MX mechanism with an different domain as argument
|
||||||
|
# - macros
|
||||||
|
#
|
||||||
|
# References:
|
||||||
|
# http://www.microsoft.com/mscorp/twc/privacy/spam_callerid.mspx
|
||||||
|
# http://spf.pobox.com/
|
||||||
|
#
|
||||||
|
# Known bugs:
|
||||||
|
# - Currently it won't handle the exclusions provided in the A and R
|
||||||
|
# tags (prefix '!'). They will show up "as-is" in the SPF record
|
||||||
|
# - I really haven't read the MS-CID specs in-depth, so there are probably
|
||||||
|
# other bugs too :)
|
||||||
|
#
|
||||||
|
# Ernesto Baschny <ernst@baschny.de>
|
||||||
|
#
|
||||||
|
|
||||||
|
import xml.sax
|
||||||
|
import spf
|
||||||
|
|
||||||
|
# -------------------------------------------------------------------------
|
||||||
|
class CIDParser(xml.sax.ContentHandler):
|
||||||
|
"Convert a MS Caller-ID entry (XML) to a SPF entry"
|
||||||
|
|
||||||
|
def __init__(self,q=None):
|
||||||
|
self.spf = []
|
||||||
|
self.action = '-all'
|
||||||
|
self.has_servers = None
|
||||||
|
self.spf_entry = None
|
||||||
|
if q:
|
||||||
|
self.spf_query = q
|
||||||
|
else:
|
||||||
|
self.spf_query = spf.query(i='127.0.0.1', s='localhost', h='unknown')
|
||||||
|
|
||||||
|
def startElement(self,tag,attr):
|
||||||
|
if tag == 'm':
|
||||||
|
if self.has_servers != None and not self.has_servers:
|
||||||
|
raise ValueError(
|
||||||
|
"Declared <noMailServers\> and later <m>, this CID entry is not valid."
|
||||||
|
)
|
||||||
|
self.has_servers = True
|
||||||
|
elif tag == 'noMailServers':
|
||||||
|
if self.has_servers:
|
||||||
|
raise ValueError(
|
||||||
|
"Declared <m> and later <noMailServers\>, this CID entry is not valid."
|
||||||
|
)
|
||||||
|
self.has_servers = False
|
||||||
|
elif tag == 'ep':
|
||||||
|
if attr.has_key('testing') and attr.getValue('testing') == 'true':
|
||||||
|
# A CID with 'testing' found:
|
||||||
|
# From the MS-specs:
|
||||||
|
# "Documents in which such attribute is present with a true
|
||||||
|
# value SHOULD be entirely ignored (one should act as if the
|
||||||
|
# document were absent)"
|
||||||
|
# From the SPF-specs:
|
||||||
|
# "Neutral (?): The SPF client MUST proceed as if a domain did
|
||||||
|
# not publish SPF data."
|
||||||
|
# So we set SPF action to "neutral":
|
||||||
|
self.action = '?all'
|
||||||
|
elif tag == 'mx':
|
||||||
|
# The empty MX-tag, same as SPF's MX-mechanism
|
||||||
|
self.spf.append('mx')
|
||||||
|
self.tag = tag
|
||||||
|
|
||||||
|
def characters(self,text):
|
||||||
|
tag = self.tag
|
||||||
|
# Remove starting and trailing spaces from text:
|
||||||
|
text = text.strip()
|
||||||
|
|
||||||
|
if tag == 'a' or tag == 'r':
|
||||||
|
# The A and R tags from MS-CID are both handled by the
|
||||||
|
# ipv4/6-mechanisms from SPF:
|
||||||
|
if text.find(':') < 0:
|
||||||
|
mechanism = 'ip4'
|
||||||
|
else:
|
||||||
|
mechanism = 'ip6'
|
||||||
|
self.spf.append(mechanism + ':' + text)
|
||||||
|
elif tag == 'indirect':
|
||||||
|
# MS-CID's indirect is "sort of" the include from SPF:
|
||||||
|
# Not really true, because the <indirect> tag from MS-CID also
|
||||||
|
# provides a fallback in case the included domain doesn't provide
|
||||||
|
# _ep-records: The inbound MX-servers of the included domains
|
||||||
|
# are added to the list of allowed outgoing mailservers for the
|
||||||
|
# domain that declared the _ep-record with the <indirect> tag.
|
||||||
|
# In SPF you would use the 'mx:domain' to handle this, but this
|
||||||
|
# wouldn't depend on referred domain having or not SPF-records.
|
||||||
|
cid_xml = self.cid_txt(text)
|
||||||
|
if cid_xml:
|
||||||
|
p = CIDParser()
|
||||||
|
xml.sax.parseString(cid_xml,p)
|
||||||
|
if p.has_servers != False:
|
||||||
|
self.spf += p.spf
|
||||||
|
else:
|
||||||
|
self.spf.append('mx:' + text)
|
||||||
|
|
||||||
|
def cid_txt(self,domain):
|
||||||
|
q = self.spf_query
|
||||||
|
domain='_ep.' + domain
|
||||||
|
a = q.dns_txt(domain)
|
||||||
|
if not a: return None
|
||||||
|
if a[0].lower().startswith('<ep ') and a[-1].lower().endswith('</ep>'):
|
||||||
|
return ''.join(a)
|
||||||
|
return None
|
||||||
|
|
||||||
|
def endElement(self,tag):
|
||||||
|
if tag == 'ep':
|
||||||
|
# This is the end... assemble what we've got
|
||||||
|
spf_entry = ['v=spf1']
|
||||||
|
if self.has_servers != False:
|
||||||
|
spf_entry += self.spf
|
||||||
|
spf_entry.append(self.action)
|
||||||
|
self.spf_entry = ' '.join(spf_entry)
|
||||||
|
|
||||||
|
def spf_txt(self,cid_xml):
|
||||||
|
if not cid_xml.startswith('<'):
|
||||||
|
cid_xml = self.cid_txt(cid_xml)
|
||||||
|
if not cid_xml: return None
|
||||||
|
# Parse the beast. Any XML-problem will be reported by xlm.sax
|
||||||
|
self.spf_entry = None
|
||||||
|
xml.sax.parseString(cid_xml,self)
|
||||||
|
return self.spf_entry
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
import sys
|
||||||
|
if len(sys.argv) < 2:
|
||||||
|
print >>sys.stderr, \
|
||||||
|
"""Usage: %s "<ep xmlns='http://ms.net/1'>...</ep>" """ % sys.argv[0]
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
cid_xml = sys.argv[1]
|
||||||
|
|
||||||
|
p = CIDParser()
|
||||||
|
print p.spf_txt(cid_xml)
|
||||||
-15
@@ -20,21 +20,6 @@ RedHat 7.2?
|
|||||||
<p> A. RedHat forgot to include the header in the RPM. See the
|
<p> A. RedHat forgot to include the header in the RPM. See the
|
||||||
<a href="requirements.html#rh72">RedHat 7.2 requirements</a>.
|
<a href="requirements.html#rh72">RedHat 7.2 requirements</a>.
|
||||||
<p>
|
<p>
|
||||||
<li> Q. Python milter compiles ok, but I get an error like this when
|
|
||||||
I try to import the milter module:
|
|
||||||
<pre>
|
|
||||||
ImportError: /usr/lib/python2.4/site-packages/milter.so: undefined symbol: smfi_setmlreply
|
|
||||||
</pre>
|
|
||||||
<p> A. Your libmilter.a is from sendmail-8.12 or earlier. You need
|
|
||||||
sendmail-8.13 or later to support setmlreply. You can disable
|
|
||||||
setmlreply by changing setup.py. Change:
|
|
||||||
<pre>
|
|
||||||
define_macros = [ ('MAX_ML_REPLY',32) ]
|
|
||||||
</pre>
|
|
||||||
in setup.py to
|
|
||||||
<pre>
|
|
||||||
define_macros = [ ('MAX_ML_REPLY',1) ]
|
|
||||||
</pre>
|
|
||||||
|
|
||||||
<h3> Running Python Milter </h3>
|
<h3> Running Python Milter </h3>
|
||||||
|
|
||||||
|
|||||||
-21
@@ -1,21 +0,0 @@
|
|||||||
<!-- -*- html -*- -->
|
|
||||||
<h3>Subsections</h3>
|
|
||||||
<li><a href="milter.html">Introduction</a>
|
|
||||||
<li><a href="changes.html">Changes</a>
|
|
||||||
<li><a href="requirements.html">Requirements</a>
|
|
||||||
<li><a href="http://sourceforge.net/project/showfiles.php?group_id=139894">Download</a>
|
|
||||||
<li><a href="faq.html">FAQ</a>
|
|
||||||
<li><a href="policy.html">Policies</a>
|
|
||||||
<li><a href="logmsgs.html">Log Messages</a>
|
|
||||||
<li><a href="http://bmsi.com/mailman/listinfo/pymilter">Mailing List</a>
|
|
||||||
<li><a href="credits.html">CREDITS</a>
|
|
||||||
<h3>Links</h3>
|
|
||||||
<li><a href="http://www.milter.org/milter_api/api.html">C API</a>
|
|
||||||
<li><a href="http://www.milter.org/">Milter.Org</a>
|
|
||||||
<li><a href="http://www.python.org/">Python.Org</a>
|
|
||||||
<li><a href="http://www.sendmail.org/">Sendmail.Org</a>
|
|
||||||
<li><a href="http://www.openspf.org/">SPF</a>
|
|
||||||
<li><a href="pysrs.html">pysrs</a>
|
|
||||||
<li><a href="http://cheeseshop.python.org/pypi/pyspf">pyspf</a>
|
|
||||||
<li><a href="http://bmsi.com/python/dspam.html">pydspam</a>
|
|
||||||
<li><a href="http://bmsi.com/libdspam/dspam.html">libdspam</a>
|
|
||||||
+6
-26
@@ -30,8 +30,8 @@ HELO name, and it did not pass.
|
|||||||
<dt> INNOC: richh
|
<dt> INNOC: richh
|
||||||
<dd> message was used to update richh's dspam dictionary
|
<dd> message was used to update richh's dspam dictionary
|
||||||
|
|
||||||
<dt> HONEYPOT: pooh@bwicorp.com
|
<dt> HONEYPOT: michaelb@jsconnor.com
|
||||||
<dd> message was sent to a honeypot address (pooh@bwicorp.com), the
|
<dd> message was sent to a honeypot address (michaelb@jsconnor.com), the
|
||||||
message was added to the honeypot dspam dictionary as spam
|
message was added to the honeypot dspam dictionary as spam
|
||||||
|
|
||||||
<dt> REJECT: numeric hello name: 63.217.19.146
|
<dt> REJECT: numeric hello name: 63.217.19.146
|
||||||
@@ -57,17 +57,8 @@ MX record, we told the sender to try again later
|
|||||||
<dt> REJECT: Subject: Cialis - No prescription needed!
|
<dt> REJECT: Subject: Cialis - No prescription needed!
|
||||||
<dd> message was rejected because its subject contained a bad expression
|
<dd> message was rejected because its subject contained a bad expression
|
||||||
|
|
||||||
<dt> REJECT: zombie PC at 192.168.3.37 sending MAIL FROM seajdr@amritind.com
|
<dt> DSPAM: tonyc tonyc@jsconnor.com
|
||||||
<dd> message was rejected because the connect ip was internal, but the
|
<dd> message was sent to tonyc@jsconnor.com and it was identified as spam
|
||||||
sender was not. This is usually because a Windows PC is infected with
|
|
||||||
malware.
|
|
||||||
|
|
||||||
<dt> X-Guessed-SPF: pass
|
|
||||||
<dd> When the SPF result is NONE, we guess a result based on the generic
|
|
||||||
SPF policy "v=spf1 a/24 mx/24 ptr".
|
|
||||||
|
|
||||||
<dt> DSPAM: tonyc tonyc@example.com
|
|
||||||
<dd> message was sent to tonyc@example.com and it was identified as spam
|
|
||||||
and placed in the tonyc dspam quarantine
|
and placed in the tonyc dspam quarantine
|
||||||
|
|
||||||
<dt> REJECT: CBV: 550 calvinalstonis@ix.netcom.com...User unknown
|
<dt> REJECT: CBV: 550 calvinalstonis@ix.netcom.com...User unknown
|
||||||
@@ -75,17 +66,6 @@ and placed in the tonyc dspam quarantine
|
|||||||
<dt> REJECT: CBV: 554 delivery error: dd This user doesn't have an account
|
<dt> REJECT: CBV: 554 delivery error: dd This user doesn't have an account
|
||||||
<dd> message was rejected because call back verification gave us a fatal
|
<dd> message was rejected because call back verification gave us a fatal
|
||||||
error
|
error
|
||||||
<dt> Auto-Whitelist: user@example.com
|
|
||||||
<dd> recipient has been added to auto_whitelist.log because the message
|
|
||||||
was sent from an internal IP and the recipient is not internal.
|
|
||||||
<dt> WHITELIST user@example.com
|
|
||||||
<dd> message is whitelisted because sender appears in auto_whitelist.log
|
|
||||||
<dt> BLACKLIST user@example.com
|
|
||||||
<dd> message is blacklisted because sender appears in blacklist.log or
|
|
||||||
failed a CBV test.
|
|
||||||
<dt> TRAINSPAM: honeypot X-Dspam-Score: 0.002278
|
|
||||||
<dd> message was used to train screener dictionary as spam
|
|
||||||
<dt> TRAIN: honeypot X-Dspam-Score: 0.980203
|
|
||||||
<dd> message was used to train screener dictionary as ham
|
|
||||||
</dl>
|
</dl>
|
||||||
<br>
|
|
||||||
|
Please add more tags to this list if you know of any. Thanks.
|
||||||
|
|||||||
+5
-12
@@ -31,20 +31,17 @@ log_headers = 0
|
|||||||
# Reject mail for domains mentioned unless user is mentioned here also
|
# Reject mail for domains mentioned unless user is mentioned here also
|
||||||
;check_user = joe@mycorp.com, mary@mycorp.com, file:bigcorp.com
|
;check_user = joe@mycorp.com, mary@mycorp.com, file:bigcorp.com
|
||||||
|
|
||||||
# Treat localparts in milter.cfg as case-insensitive
|
|
||||||
case_sensitive_localpart = true
|
|
||||||
|
|
||||||
# features intended to filter or block incoming mail
|
# features intended to filter or block incoming mail
|
||||||
[defang]
|
[defang]
|
||||||
|
|
||||||
# do virus scanning on attached messages also
|
# do virus scanning on attached messages also
|
||||||
scan_rfc822 = 0
|
scan_rfc822 = 1
|
||||||
# do virus scanning on attached zipfiles also
|
# do virus scanning on attached zipfiles also
|
||||||
scan_zip = 0
|
scan_zip = 0
|
||||||
# Comment out scripts in HTML attachments. Can be CPU intensive.
|
# Comment out scripts in HTML attachments. Can be CPU intensive.
|
||||||
scan_html = 0
|
scan_html = 0
|
||||||
# reject messages with asian fonts because we can't read them
|
# reject messages with asian fonts because we can't read them
|
||||||
block_chinese = 0
|
block_chinese = 1
|
||||||
# list users who hate forwarded mail
|
# list users who hate forwarded mail
|
||||||
;block_forward = egghead@mycorp.com, busybee@mycorp.com
|
;block_forward = egghead@mycorp.com, busybee@mycorp.com
|
||||||
# reject mail with these case insensitive strings in the subject
|
# reject mail with these case insensitive strings in the subject
|
||||||
@@ -128,15 +125,11 @@ blind = 1
|
|||||||
# discard outgoing mail without alerting sender
|
# discard outgoing mail without alerting sender
|
||||||
# can be used in conjunction with wiretap to censor outgoing mail
|
# can be used in conjunction with wiretap to censor outgoing mail
|
||||||
;discard_users = canned@bigcorp.com
|
;discard_users = canned@bigcorp.com
|
||||||
# archive copies all delivered mail to a file
|
|
||||||
;mail_archive = /var/log/mail_archive
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# smart aliases trigger on both sender and recipient
|
# smart aliases trigger on both sender and recipient
|
||||||
# alias = sender, recipient[, destination]
|
|
||||||
#
|
#
|
||||||
[smart_alias]
|
;smart_alias = copycust,walter,spy1,spy2
|
||||||
# multiple wiretap monitors. Smart aliases are applied after wiretap.
|
# multiple wiretap monitors
|
||||||
;spy1 = disloyal@bigcorp.com,spy@bigcorp.com
|
;spy1 = disloyal@bigcorp.com,spy@bigcorp.com
|
||||||
;spy2 = bigmouth@bigcorp.com,spy@bigcorp.com
|
;spy2 = bigmouth@bigcorp.com,spy@bigcorp.com
|
||||||
# mail from client@clientcorp.com to sue@bigcorp.com is redirected to
|
# mail from client@clientcorp.com to sue@bigcorp.com is redirected to
|
||||||
@@ -149,7 +142,7 @@ blind = 1
|
|||||||
;walter1 = cust@othercorp.com,walter@bigcorp.com,boss@bigcorp.com,
|
;walter1 = cust@othercorp.com,walter@bigcorp.com,boss@bigcorp.com,
|
||||||
; walter@bigcorp.com
|
; walter@bigcorp.com
|
||||||
;bulk = soruce@telex.com,bob@jsconnor.com
|
;bulk = soruce@telex.com,bob@jsconnor.com
|
||||||
;bulk1 = soruce@telex.com,larry@jsconnor.com,bulk
|
;bulk = soruce@telex.com,larry@jsconnor.com
|
||||||
|
|
||||||
# See http://bmsi.com/python/dspam.html
|
# See http://bmsi.com/python/dspam.html
|
||||||
[dspam]
|
[dspam]
|
||||||
|
|||||||
+19
-44
@@ -1,20 +1,23 @@
|
|||||||
%define name milter
|
%define name milter
|
||||||
%define version 0.8.7
|
%define version 0.8.5
|
||||||
%define release 1
|
%define release 1.RH7
|
||||||
# what version of RH are we building for?
|
# what version of RH are we building for?
|
||||||
%define redhat7 0
|
%define redhat9 0
|
||||||
|
%define redhat7 1
|
||||||
|
%define redhat6 0
|
||||||
|
|
||||||
# Options for Redhat version 6.x:
|
# Options for Redhat version 6.x:
|
||||||
# rpm -ba|--rebuild --define "rh7 1"
|
# rpm -ba|--rebuild --define "rh6 1"
|
||||||
%{?rh7:%define redhat7 1}
|
%{?rh6:%define redhat7 0}
|
||||||
|
%{?rh6:%define redhat6 1}
|
||||||
|
|
||||||
# some systems dont have initrddir defined
|
# some systems dont have initrddir defined
|
||||||
%{?_initrddir:%define _initrddir /etc/rc.d/init.d}
|
%{?_initrddir:%define _initrddir /etc/rc.d/init.d}
|
||||||
|
|
||||||
%if %{redhat7} # Redhat 7.x and earlier (multiple ps lines per thread)
|
%if %{redhat9}
|
||||||
%define sysvinit milter.rc7
|
|
||||||
%else
|
|
||||||
%define sysvinit milter.rc
|
%define sysvinit milter.rc
|
||||||
|
%else # Redhat 7.x and earlier (multiple ps lines per thread)
|
||||||
|
%define sysvinit milter.rc7
|
||||||
%endif
|
%endif
|
||||||
# RH9, other systems (single ps line per process)
|
# RH9, other systems (single ps line per process)
|
||||||
%ifos Linux
|
%ifos Linux
|
||||||
@@ -40,24 +43,23 @@ Requires: %{python} >= 2.4, sendmail >= 8.13
|
|||||||
%ifos Linux
|
%ifos Linux
|
||||||
Requires: chkconfig
|
Requires: chkconfig
|
||||||
%endif
|
%endif
|
||||||
BuildRequires: %{python}-devel >= 2.4, sendmail-devel >= 8.13
|
BuildRequires: %{python}-devel , sendmail-devel >= 8.13
|
||||||
|
|
||||||
%description
|
%description
|
||||||
This is a python extension module to enable python scripts to
|
This is a python extension module to enable python scripts to
|
||||||
attach to sendmail's libmilter functionality. Additional python
|
attach to sendmail's libmilter functionality. Additional python
|
||||||
modules provide for navigating and modifying MIME parts, sending
|
modules provide for navigating and modifying MIME parts.
|
||||||
DSNs, and doing CBV.
|
|
||||||
|
|
||||||
%prep
|
%prep
|
||||||
%setup
|
%setup
|
||||||
#patch -p0 -b .bms
|
#%patch -p0 -b .bms
|
||||||
|
|
||||||
%build
|
%build
|
||||||
%if %{redhat7}
|
if %{redhat9}; then
|
||||||
LDFLAGS="-s"
|
|
||||||
%else # Redhat builds debug packages after 7.3
|
|
||||||
LDFLAGS="-g"
|
LDFLAGS="-g"
|
||||||
%endif
|
else
|
||||||
|
LDFLAGS="-s"
|
||||||
|
fi
|
||||||
env CFLAGS="$RPM_OPT_FLAGS" LDFLAGS="$LDFLAGS" %{python} setup.py build
|
env CFLAGS="$RPM_OPT_FLAGS" LDFLAGS="$LDFLAGS" %{python} setup.py build
|
||||||
|
|
||||||
%install
|
%install
|
||||||
@@ -89,8 +91,6 @@ cat >$RPM_BUILD_ROOT/etc/cron.daily/milter <<'EOF'
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
|
|
||||||
find /var/log/milter/save -mtime +7 | xargs $R rm
|
find /var/log/milter/save -mtime +7 | xargs $R rm
|
||||||
# work around memory leak
|
|
||||||
/etc/init.d/milter condrestart
|
|
||||||
EOF
|
EOF
|
||||||
chmod a+x $RPM_BUILD_ROOT/etc/cron.daily/milter
|
chmod a+x $RPM_BUILD_ROOT/etc/cron.daily/milter
|
||||||
|
|
||||||
@@ -151,7 +151,7 @@ rm -rf $RPM_BUILD_ROOT
|
|||||||
|
|
||||||
%files -f INSTALLED_FILES
|
%files -f INSTALLED_FILES
|
||||||
%defattr(-,root,root)
|
%defattr(-,root,root)
|
||||||
%doc README HOWTO ChangeLog NEWS TODO CREDITS sample.py
|
%doc README HOWTO NEWS TODO CREDITS sample.py
|
||||||
/etc/logrotate.d/milter
|
/etc/logrotate.d/milter
|
||||||
/etc/cron.daily/milter
|
/etc/cron.daily/milter
|
||||||
%ifos aix4.1
|
%ifos aix4.1
|
||||||
@@ -174,31 +174,6 @@ rm -rf $RPM_BUILD_ROOT
|
|||||||
/usr/share/sendmail-cf/hack/rhsbl.m4
|
/usr/share/sendmail-cf/hack/rhsbl.m4
|
||||||
|
|
||||||
%changelog
|
%changelog
|
||||||
* Sat Nov 04 2006 Stuart Gathman <stuart@bmsi.com> 0.8.7-1
|
|
||||||
- More lame bounce heuristics
|
|
||||||
- SPF moved to pyspf RPM
|
|
||||||
- wiretap archive option
|
|
||||||
- Do plain CBV if missing template
|
|
||||||
* Tue May 23 2006 Stuart Gathman <stuart@bmsi.com> 0.8.6-2
|
|
||||||
- Support CBV timeout
|
|
||||||
- Support fail template, headers in templates
|
|
||||||
- Create GOSSiP record only when connection will procede to DATA.
|
|
||||||
- More SPF lax heuristics
|
|
||||||
- Don't require SPF pass for white/black listing mail from trusted relay.
|
|
||||||
- Support localpart wildcard for white and black lists.
|
|
||||||
* Thu Feb 23 2006 Stuart Gathman <stuart@bmsi.com> 0.8.6-1
|
|
||||||
- Delay reject of unsigned RCPT for postmaster and abuse only
|
|
||||||
- Fix dsn reporting of hard permerror
|
|
||||||
- Resolve FIXME for wrap_close in miltermodule.c
|
|
||||||
- Add Message-ID to DSNs
|
|
||||||
- Use signed Message-ID in delayed reject to blacklist senders
|
|
||||||
- Auto-train via blacklist and auto-whitelist
|
|
||||||
- Don't check userlist for signed MFROM
|
|
||||||
- Accept but skip DSPAM and training for whitelisted senders without SPF PASS
|
|
||||||
- Report GC stats
|
|
||||||
- Support CIDR matching for IP lists
|
|
||||||
- Support pysrs sign feature
|
|
||||||
- Support localpart specific SPF policy in access file
|
|
||||||
* Thu Dec 29 2005 Stuart Gathman <stuart@bmsi.com> 0.8.5-1
|
* Thu Dec 29 2005 Stuart Gathman <stuart@bmsi.com> 0.8.5-1
|
||||||
- Simple trusted_forwarder implementation.
|
- Simple trusted_forwarder implementation.
|
||||||
- Fix access_file neutral policy
|
- Fix access_file neutral policy
|
||||||
|
|||||||
+31
-29
@@ -34,9 +34,6 @@ $ python setup.py help
|
|||||||
libraries=["milter","smutil","resolv"]
|
libraries=["milter","smutil","resolv"]
|
||||||
|
|
||||||
* $Log$
|
* $Log$
|
||||||
* Revision 1.9 2005/12/23 21:46:36 customdesigned
|
|
||||||
* Compile on sendmail-8.12 (ifdef SMFIR_INSHEADER)
|
|
||||||
*
|
|
||||||
* Revision 1.8 2005/10/20 23:23:36 customdesigned
|
* Revision 1.8 2005/10/20 23:23:36 customdesigned
|
||||||
* Include smfi_progress is SMFIR_PROGRESS defined
|
* Include smfi_progress is SMFIR_PROGRESS defined
|
||||||
*
|
*
|
||||||
@@ -289,7 +286,7 @@ _find_context(PyObject *c) {
|
|||||||
if (c->ob_type == &milter_ContextType) {
|
if (c->ob_type == &milter_ContextType) {
|
||||||
milter_ContextObject *self = (milter_ContextObject *)c;
|
milter_ContextObject *self = (milter_ContextObject *)c;
|
||||||
ctx = self->ctx;
|
ctx = self->ctx;
|
||||||
if (ctx != NULL && smfi_getpriv(ctx) != self)
|
if (smfi_getpriv(ctx) != self)
|
||||||
ctx = NULL;
|
ctx = NULL;
|
||||||
}
|
}
|
||||||
if (ctx == NULL)
|
if (ctx == NULL)
|
||||||
@@ -297,6 +294,23 @@ _find_context(PyObject *c) {
|
|||||||
return ctx;
|
return ctx;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Release the Python Context for a SMFICTX. */
|
||||||
|
static void
|
||||||
|
_clear_context(SMFICTX *ctx) {
|
||||||
|
milter_ContextObject *self = smfi_getpriv(ctx);
|
||||||
|
if (self) {
|
||||||
|
PyThreadState *t = self->t;
|
||||||
|
PyEval_AcquireThread(t);
|
||||||
|
self->t = 0;
|
||||||
|
self->ctx = 0;
|
||||||
|
smfi_setpriv(ctx,0);
|
||||||
|
Py_DECREF(self);
|
||||||
|
PyThreadState_Clear(t);
|
||||||
|
PyEval_ReleaseThread(t);
|
||||||
|
PyThreadState_Delete(t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
milter_Context_dealloc(PyObject *s) {
|
milter_Context_dealloc(PyObject *s) {
|
||||||
milter_ContextObject *self = (milter_ContextObject *)s;
|
milter_ContextObject *self = (milter_ContextObject *)s;
|
||||||
@@ -530,19 +544,13 @@ milter_set_exception_policy(PyObject *self, PyObject *args) {
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
|
||||||
_release_thread(PyThreadState *t) {
|
|
||||||
if (t != NULL)
|
|
||||||
PyEval_ReleaseThread(t);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Report and clear any python exception before returning to libmilter.
|
/** Report and clear any python exception before returning to libmilter.
|
||||||
The interpreter is locked when we are called, and we unlock it. */
|
The interpreter is locked when we are called, and we unlock it. */
|
||||||
static int _report_exception(milter_ContextObject *self) {
|
static int _report_exception(milter_ContextObject *self) {
|
||||||
if (PyErr_Occurred()) {
|
if (PyErr_Occurred()) {
|
||||||
PyErr_Print();
|
PyErr_Print();
|
||||||
PyErr_Clear(); /* must clear since not returning to python */
|
PyErr_Clear(); /* must clear since not returning to python */
|
||||||
_release_thread(self->t);
|
PyEval_ReleaseThread(self->t);
|
||||||
switch (exception_policy) {
|
switch (exception_policy) {
|
||||||
case SMFIS_REJECT:
|
case SMFIS_REJECT:
|
||||||
smfi_setreply(self->ctx, "554", "5.3.0", "Filter failure");
|
smfi_setreply(self->ctx, "554", "5.3.0", "Filter failure");
|
||||||
@@ -553,7 +561,7 @@ static int _report_exception(milter_ContextObject *self) {
|
|||||||
}
|
}
|
||||||
return SMFIS_CONTINUE;
|
return SMFIS_CONTINUE;
|
||||||
}
|
}
|
||||||
_release_thread(self->t);
|
PyEval_ReleaseThread(self->t);
|
||||||
return SMFIS_CONTINUE;
|
return SMFIS_CONTINUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -572,7 +580,7 @@ _generic_wrapper(milter_ContextObject *self, PyObject *cb, PyObject *arglist) {
|
|||||||
retval = PyInt_AsLong(result);
|
retval = PyInt_AsLong(result);
|
||||||
Py_DECREF(result);
|
Py_DECREF(result);
|
||||||
if (PyErr_Occurred()) return _report_exception(self);
|
if (PyErr_Occurred()) return _report_exception(self);
|
||||||
_release_thread(self->t);
|
PyEval_ReleaseThread(self->t);
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -769,23 +777,17 @@ milter_wrap_close(SMFICTX *ctx) {
|
|||||||
PyObject *cb = close_callback;
|
PyObject *cb = close_callback;
|
||||||
milter_ContextObject *self = smfi_getpriv(ctx);
|
milter_ContextObject *self = smfi_getpriv(ctx);
|
||||||
int r = SMFIS_CONTINUE;
|
int r = SMFIS_CONTINUE;
|
||||||
if (self != NULL) {
|
if (self != NULL && cb != NULL && self->ctx == ctx) {
|
||||||
PyThreadState *t = self->t;
|
PyObject *arglist;
|
||||||
PyEval_AcquireThread(t);
|
PyEval_AcquireThread(self->t);
|
||||||
self->t = 0;
|
arglist = Py_BuildValue("(O)", self);
|
||||||
if (cb != NULL && self->ctx == ctx) {
|
r = _generic_wrapper(self, cb, arglist);
|
||||||
PyObject *arglist = Py_BuildValue("(O)", self);
|
|
||||||
/* Call python close callback, but do not ReleaseThread, because
|
|
||||||
* self->t is NULL */
|
|
||||||
r = _generic_wrapper(self, cb, arglist);
|
|
||||||
}
|
|
||||||
self->ctx = 0;
|
|
||||||
smfi_setpriv(ctx,0);
|
|
||||||
Py_DECREF(self);
|
|
||||||
PyThreadState_Clear(t);
|
|
||||||
PyEval_ReleaseThread(t);
|
|
||||||
PyThreadState_Delete(t);
|
|
||||||
}
|
}
|
||||||
|
/* FIXME: It is inefficient to have released the interp lock only to
|
||||||
|
acquire it again in _clear_context. We can tell _generic_return and
|
||||||
|
friends not to release the lock by, for instance, setting self->t to NULL.
|
||||||
|
However, first we make it work. */
|
||||||
|
_clear_context(ctx);
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,4 @@
|
|||||||
To: %(sender)s
|
|
||||||
From: postmaster@%(receiver)s
|
|
||||||
Subject: SPF %(result)s (POSSIBLE FORGERY)
|
Subject: SPF %(result)s (POSSIBLE FORGERY)
|
||||||
Auto-Submitted: auto-generated (sender verification)
|
|
||||||
|
|
||||||
This is an automatically generated Delivery Status Notification.
|
This is an automatically generated Delivery Status Notification.
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,4 @@
|
|||||||
To: %(sender)s
|
|
||||||
From: postmaster@%(receiver)s
|
|
||||||
Subject: Critical SPF configuration error
|
Subject: Critical SPF configuration error
|
||||||
Auto-Submitted: auto-generated (configuration error)
|
|
||||||
|
|
||||||
This is an automatically generated Delivery Status Notification.
|
This is an automatically generated Delivery Status Notification.
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,4 @@
|
|||||||
To: %(sender)s
|
|
||||||
From: postmaster@%(receiver)s
|
|
||||||
Subject: DELIVERY STATUS (POSSIBLE SPAM)
|
Subject: DELIVERY STATUS (POSSIBLE SPAM)
|
||||||
Auto-Submitted: auto-generated (content analysis)
|
|
||||||
|
|
||||||
This is an automatically generated Delivery Status Notification.
|
This is an automatically generated Delivery Status Notification.
|
||||||
|
|
||||||
@@ -22,19 +19,6 @@ their quarantined mail and may notice your message. If your message is
|
|||||||
important, please contact them via other means. You may also try sending
|
important, please contact them via other means. You may also try sending
|
||||||
them a simple plain text message.
|
them a simple plain text message.
|
||||||
|
|
||||||
If you never sent the above message, then your domain, %(sender_domain)s,
|
|
||||||
was forged - i.e. used without your knowlege or authorization by
|
|
||||||
someone attempting to steal your mail identity. This is a very
|
|
||||||
serious problem, and you need to provide authentication for your
|
|
||||||
SMTP (email) servers to prevent criminals from forging your
|
|
||||||
domain. The simplest step is usually to publish an SPF record
|
|
||||||
with your Sender Policy.
|
|
||||||
|
|
||||||
For more information, see: http://www.openspf.org
|
|
||||||
|
|
||||||
Your mail admin needs to publish a strict SPF record so that I can reject
|
|
||||||
those forgeries instead of bugging you with them.
|
|
||||||
|
|
||||||
If you need further assistance, please do not hesitate to contact me.
|
If you need further assistance, please do not hesitate to contact me.
|
||||||
|
|
||||||
Kind regards,
|
Kind regards,
|
||||||
|
|||||||
@@ -3,8 +3,6 @@ import sys
|
|||||||
from distutils.core import setup, Extension
|
from distutils.core import setup, Extension
|
||||||
|
|
||||||
# FIXME: on some versions of sendmail, smutil is renamed to sm
|
# FIXME: on some versions of sendmail, smutil is renamed to sm
|
||||||
# on slackware and debian, leave it out entirely. It depends
|
|
||||||
# on how libmilter was built by the sendmail package.
|
|
||||||
libs = ["milter", "smutil"]
|
libs = ["milter", "smutil"]
|
||||||
|
|
||||||
# patch distutils if it can't cope with the "classifiers" or
|
# patch distutils if it can't cope with the "classifiers" or
|
||||||
@@ -15,13 +13,13 @@ if sys.version < '2.2.3':
|
|||||||
DistributionMetadata.download_url = None
|
DistributionMetadata.download_url = None
|
||||||
|
|
||||||
# NOTE: importing Milter to obtain version fails when milter.so not built
|
# NOTE: importing Milter to obtain version fails when milter.so not built
|
||||||
setup(name = "milter", version = '0.8.7',
|
setup(name = "milter", version = '0.8.5',
|
||||||
description="Python interface to sendmail milter API",
|
description="Python interface to sendmail milter API",
|
||||||
long_description="""\
|
long_description="""\
|
||||||
This is a python extension module to enable python scripts to
|
This is a python extension module to enable python scripts to
|
||||||
attach to sendmail's libmilter functionality. Additional python
|
attach to sendmail's libmilter functionality. Additional python
|
||||||
modules provide for navigating and modifying MIME parts, and
|
modules provide for navigating and modifying MIME parts, and
|
||||||
sending DSNs or doing CBVs.
|
querying SPF records.
|
||||||
""",
|
""",
|
||||||
author="Jim Niemira",
|
author="Jim Niemira",
|
||||||
author_email="urmane@urmane.org",
|
author_email="urmane@urmane.org",
|
||||||
@@ -29,7 +27,7 @@ sending DSNs or doing CBVs.
|
|||||||
maintainer_email="stuart@bmsi.com",
|
maintainer_email="stuart@bmsi.com",
|
||||||
license="GPL",
|
license="GPL",
|
||||||
url="http://www.bmsi.com/python/milter.html",
|
url="http://www.bmsi.com/python/milter.html",
|
||||||
py_modules=["mime"],
|
py_modules=["mime","spf"],
|
||||||
packages = ['Milter'],
|
packages = ['Milter'],
|
||||||
ext_modules=[
|
ext_modules=[
|
||||||
Extension("milter", ["miltermodule.c"],
|
Extension("milter", ["miltermodule.c"],
|
||||||
|
|||||||
+1
-4
@@ -1,7 +1,4 @@
|
|||||||
To: %(sender)s
|
|
||||||
From: postmaster@%(receiver)s
|
|
||||||
Subject: SPF %(result)s (POSSIBLE FORGERY)
|
Subject: SPF %(result)s (POSSIBLE FORGERY)
|
||||||
Auto-Submitted: auto-generated (configuration error)
|
|
||||||
|
|
||||||
This is an automatically generated Delivery Status Notification.
|
This is an automatically generated Delivery Status Notification.
|
||||||
|
|
||||||
@@ -17,7 +14,7 @@ Subject: %(subject)s
|
|||||||
Received-SPF: %(spf_result)s
|
Received-SPF: %(spf_result)s
|
||||||
|
|
||||||
Your sender policy indicated that the above email was likely forged and that
|
Your sender policy indicated that the above email was likely forged and that
|
||||||
feedback was desired for debugging. If you are sending from a foreign ISP,
|
feedback was desired. If you are sending from a foreign ISP,
|
||||||
then you may need to follow your home ISPs instructions for configuring
|
then you may need to follow your home ISPs instructions for configuring
|
||||||
your outgoing mail server.
|
your outgoing mail server.
|
||||||
|
|
||||||
|
|||||||
Executable
+99
@@ -0,0 +1,99 @@
|
|||||||
|
#!/usr/bin/python2.3
|
||||||
|
|
||||||
|
# Author: Stuart D. Gathman <stuart@bmsi.com>
|
||||||
|
# Copyright 2004 Business Management Systems, Inc.
|
||||||
|
# This code is under the GNU General Public License. See COPYING for details.
|
||||||
|
|
||||||
|
# $Log$
|
||||||
|
# Revision 1.1.1.1 2005/05/31 18:07:19 customdesigned
|
||||||
|
# Release 0.6.9
|
||||||
|
#
|
||||||
|
# Revision 2.3 2004/04/19 22:12:11 stuart
|
||||||
|
# Release 0.6.9
|
||||||
|
#
|
||||||
|
# Revision 2.2 2004/04/18 03:29:35 stuart
|
||||||
|
# Pass most tests except -local and -rcpt-to
|
||||||
|
#
|
||||||
|
# Revision 2.1 2004/04/08 18:41:15 stuart
|
||||||
|
# Reject numeric hello names
|
||||||
|
#
|
||||||
|
# Driver for SPF test system
|
||||||
|
|
||||||
|
import spf
|
||||||
|
import sys
|
||||||
|
|
||||||
|
from optparse import OptionParser
|
||||||
|
|
||||||
|
class PerlOptionParser(OptionParser):
|
||||||
|
def _process_args (self, largs, rargs, values):
|
||||||
|
"""_process_args(largs : [string],
|
||||||
|
rargs : [string],
|
||||||
|
values : Values)
|
||||||
|
|
||||||
|
Process command-line arguments and populate 'values', consuming
|
||||||
|
options and arguments from 'rargs'. If 'allow_interspersed_args' is
|
||||||
|
false, stop at the first non-option argument. If true, accumulate any
|
||||||
|
interspersed non-option arguments in 'largs'.
|
||||||
|
"""
|
||||||
|
while rargs:
|
||||||
|
arg = rargs[0]
|
||||||
|
# We handle bare "--" explicitly, and bare "-" is handled by the
|
||||||
|
# standard arg handler since the short arg case ensures that the
|
||||||
|
# len of the opt string is greater than 1.
|
||||||
|
if arg == "--":
|
||||||
|
del rargs[0]
|
||||||
|
return
|
||||||
|
elif arg[0:2] == "--":
|
||||||
|
# process a single long option (possibly with value(s))
|
||||||
|
self._process_long_opt(rargs, values)
|
||||||
|
elif arg[:1] == "-" and len(arg) > 1:
|
||||||
|
# process a single perl style long option
|
||||||
|
rargs[0] = '-' + arg
|
||||||
|
self._process_long_opt(rargs, values)
|
||||||
|
elif self.allow_interspersed_args:
|
||||||
|
largs.append(arg)
|
||||||
|
del rargs[0]
|
||||||
|
else:
|
||||||
|
return
|
||||||
|
|
||||||
|
def format(q):
|
||||||
|
res,code,txt = q.check()
|
||||||
|
print res
|
||||||
|
if res in ('pass','neutral','unknown'): print
|
||||||
|
else: print txt
|
||||||
|
print 'spfquery:',q.get_header_comment(res)
|
||||||
|
print 'Received-SPF:',q.get_header(res,'spfquery')
|
||||||
|
|
||||||
|
def main(argv):
|
||||||
|
parser = PerlOptionParser()
|
||||||
|
parser.add_option("--file",dest="file")
|
||||||
|
parser.add_option("--ip",dest="ip")
|
||||||
|
parser.add_option("--sender",dest="sender")
|
||||||
|
parser.add_option("--helo",dest="hello_name")
|
||||||
|
parser.add_option("--local",dest="local_policy")
|
||||||
|
parser.add_option("--rcpt-to",dest="rcpt")
|
||||||
|
parser.add_option("--default-explanation",dest="explanation")
|
||||||
|
parser.add_option("--sanitize",type="int",dest="sanitize")
|
||||||
|
parser.add_option("--debug",type="int",dest="debug")
|
||||||
|
opts,args = parser.parse_args(argv)
|
||||||
|
if opts.ip:
|
||||||
|
q = spf.query(opts.ip,opts.sender,opts.hello_name,local=opts.local_policy)
|
||||||
|
if opts.explanation:
|
||||||
|
q.set_default_explanation(opts.explanation)
|
||||||
|
format(q)
|
||||||
|
if opts.file:
|
||||||
|
if opts.file == '0':
|
||||||
|
fp = sys.stdin
|
||||||
|
else:
|
||||||
|
fp = open(opts.file,'r')
|
||||||
|
for ln in fp:
|
||||||
|
ip,sender,helo,rcpt = ln.split(None,3)
|
||||||
|
q = spf.query(ip,sender,helo,local=opts.local_policy)
|
||||||
|
if opts.explanation:
|
||||||
|
q.set_default_explanation(opts.explanation)
|
||||||
|
format(q)
|
||||||
|
fp.close()
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
import sys
|
||||||
|
main(sys.argv[1:])
|
||||||
@@ -1,7 +1,4 @@
|
|||||||
To: %(sender)s
|
|
||||||
From: postmaster@%(receiver)s
|
|
||||||
Subject: Critical mail server configuration error
|
Subject: Critical mail server configuration error
|
||||||
Auto-Submitted: auto-generated (configuration error)
|
|
||||||
|
|
||||||
This is an automatically generated Delivery Status Notification.
|
This is an automatically generated Delivery Status Notification.
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -238,7 +238,7 @@ class BMSMilterTestCase(unittest.TestCase):
|
|||||||
milter = TestMilter()
|
milter = TestMilter()
|
||||||
milter.connect('testSmartAlias')
|
milter.connect('testSmartAlias')
|
||||||
# test smart alias feature
|
# test smart alias feature
|
||||||
key = ('foo@example.com','baz@bat.com')
|
key = ('foo@bar.com','baz@bat.com')
|
||||||
bms.smart_alias[key] = ['ham@eggs.com']
|
bms.smart_alias[key] = ['ham@eggs.com']
|
||||||
rc = milter.feedMsg('test8',key[0],key[1])
|
rc = milter.feedMsg('test8',key[0],key[1])
|
||||||
self.assertEqual(rc,Milter.ACCEPT)
|
self.assertEqual(rc,Milter.ACCEPT)
|
||||||
|
|||||||
Reference in New Issue
Block a user