#! /usr/bin/python2.6 # -*- coding: utf-8 -*- # # Copyright (C) <2012> # # This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. # # You should have received a copy of the GNU General Public License along with this program. If not, see . ### Revision 0.0.1 2012/02/16 16:00:00 Test version. ### Change DNSCheck ### Revision 0.0.0 2012/01/13 14:00:00 Test version. ### Vre 0.0.0 import sys import time import traceback import Milter from Milter.dynip import is_dynip as dynip from Milter.utils import parseaddr, parse_addr from Milter.dns import Session as dnsSession import re import smtplib from email.MIMEText import MIMEText from email.Header import Header from email.Utils import formatdate import logging import logging.handlers socketname = "inet:1025@localhost" sockettimeout = 600 fqdn = re.compile(r'^[a-z0-9][-a-z0-9]*(\.[a-z0-9][-a-z0-9]*)*(\.[a-z]{2,10})$') fqdnjp = re.compile(r'\.[a-z]{2,2}\.jp$|([a-z0-9][-a-z0-9]{2,63}\.[a-z]{2,10})$') ## %(levelno)s Numeric logging level for the message (DEBUG, INFO, ## WARNING, ERROR, CRITICAL) log_filename = "/var/log/pwmail/pwfilter.log" log_level = logging.INFO my_logger = logging.getLogger("pwfilter") my_logger.setLevel(log_level) log_fh = logging.handlers.RotatingFileHandler(log_filename, maxBytes=1024000, backupCount=10) log_fh.setLevel(logging.DEBUG) log_fm = logging.Formatter("%(asctime)s - %(levelname)s - %(message)s") log_fh.setFormatter(log_fm) my_logger.addHandler(log_fh) my_domainlist = () sendmail_port = 1026 ## ロギング用メールポート to_pwadmin = "pwadmin@xxxx1.jp" ## ロギング用メールアドレス(全て小文字) def parse_header(val): """Decode headers gratuitously encoded to hide the content. """ try: h = decode_header(val) if not len(h): return val u = [] for s,enc in h: if enc: try: u.append(unicode(s,enc)) except LookupError: u.append(unicode(s)) else: if isinstance(s,unicode): u.append(s) else: for enc1 in ('cp932','utf8'): try: u.append(unicode(s,enc1)) except UnicodeDecodeError: continue break u = ''.join(u) for enc in ('us-ascii','iso-8859-1','utf8'): try: return u.encode(enc) except UnicodeError: continue except UnicodeDecodeError: pass except LookupError: pass except email.Errors.HeaderParseError: pass return val class myMilter(Milter.Base): def __init__(self): # A new instance with each new connection. self.id = Milter.uniqueID() # Integer incremented with each call. def msg_cnvt(self, s): for enc1 in ('cp932','utf8'): try: u = unicode(s,enc1) except UnicodeDecodeError: continue break return u def msg_connect(self, rcpt_addr): if not self.Cname: Cname = u"" else: Cname = self.msg_cnvt(self.Cname) if not self.IP: IP = u"" else: IP = self.msg_cnvt(self.IP) if not self.Hname: Hname = u"" else: Hname = self.msg_cnvt(self.Hname) if not self.Fname: Fname = u"" else: Fname = self.msg_cnvt(self.Fname) if not rcpt_addr: wrcpt_addr = u"" else: wrcpt_addr = self.msg_cnvt(rcpt_addr) try: return u"connect from %s at %s\nhelo: %s\nmail from: %s\nrcpt to: %s\n" % (Cname, IP, Hname, Fname, wrcpt_addr) except: self.log_warning("msg_connect:", traceback.format_exc()) return u"" def send_admin1(self, rcpt_addr, subject, ERlevel): encoding = "ISO-2022-JP" body = self.msg_connect(rcpt_addr) + u"X-PWmail: %s\n" % self.PWmsg mf = parse_addr(rcpt_addr) un = mf[0] pos = un.find('+') if pos != -1: un = un[:pos] from_addr = un + "+admin@" + mf[1] to_addr = un + "+" + ERlevel + "@" + mf[1] try: msg = MIMEText(body.encode(encoding,'replace'), 'plain', encoding) msg['Subject'] = Header(subject, encoding) msg['From'] = from_addr msg['To'] = to_addr msg['Date'] = formatdate() except: self.log_warning('send_admin1 MIMEText:',traceback.format_exc()) return # SMTPの引数を省略した場合はlocalhost:25 s = smtplib.SMTP('localhost',sendmail_port,'localhost') try: s.sendmail(from_addr, [to_pwadmin], msg.as_string()) except: self.log_warning('send_admin1 sendmail:',traceback.format_exc()) s.quit() def msg_Header(self, hd): if not self.HFrom: hfrom = u"" else: hfrom = unicode(self.HFrom, 'utf8', 'replace') if not self.Subject: subject = u"" else: subject = unicode(self.Subject, 'utf8', 'replace') try: return u"Header-From: %s\nHeader-Subject: %s\nHeader-Date: %s\n" % (hfrom, subject, hd) except: self.log_warning("msg_Header:", traceback.format_exc()) return u"" def send_admin2(self, rcpt_addr, subject, ERlevel): encoding = "ISO-2022-JP" if not self.HDate: hdate = "" else: hdate = self.HDate body = self.msg_connect(rcpt_addr) + self.msg_Header(hdate) + u"X-PWmail: %s\n" % (self.PWmsg) mf = parse_addr(rcpt_addr) un = mf[0] pos = un.find('+') if pos != -1: un = un[:pos] from_addr = un + "+admin@" + mf[1] to_addr = un + "+" + ERlevel + "@" + mf[1] try: msg = MIMEText(body.encode(encoding,'replace'), 'plain', encoding) msg['Subject'] = Header(subject, encoding) msg['From'] = from_addr msg['To'] = to_addr msg['Date'] = hdate except: self.log_warning('send_admin2 MIMEText:',traceback.format_exc()) return # SMTPの引数を省略した場合はlocalhost:25 s = smtplib.SMTP('localhost',sendmail_port,'localhost') try: s.sendmail(from_addr, [to_pwadmin], msg.as_string()) except: self.log_warning('send_admin2 sendmail:',traceback.format_exc()) s.quit() def send_admin3(self, rcpt_addr, subject, ERlevel): encoding = "ISO-2022-JP" if not self.HDate: hdate = "" else: hdate = self.HDate body = self.msg_connect(rcpt_addr) + self.msg_Header(hdate) + u"X-PWmail: %s\n" % (self.PWmsg) mf = parse_addr(rcpt_addr) un = mf[0] pos = un.find('+') if pos != -1: un = un[:pos] from_addr = un + "+admin@" + mf[1] to_addr = un + "+" + ERlevel + "@" + mf[1] try: msg = MIMEText(body.encode(encoding,'replace'), 'plain', encoding) msg['Subject'] = Header(subject, encoding) msg['From'] = from_addr msg['To'] = to_addr msg['Date'] = hdate except: self.log_warning('send_admin3 MIMEText:',traceback.format_exc()) return # SMTPの引数を省略した場合はlocalhost:25 s = smtplib.SMTP('localhost',sendmail_port,'localhost') try: s.sendmail(from_addr, [to_addr,to_pwadmin], msg.as_string()) except: self.log_warning('send_admin3 sendmail:',traceback.format_exc()) s.quit() # @Milter.noreply def connect(self, IPname, family, hostaddr): # ## self.IPname # self.Cname # self.Cfqdn # self.Cdynip # self.Cipok # self.Sabort # self.log("connect from %s at %s" % (IPname, hostaddr) ) # Ini Setup if hostaddr and len(hostaddr) > 0: self.IP = hostaddr[0] else: self.log_critical("REJECT: connect attacks") self.setreply('550','5.7.1', 'Banned for connect attacks') return Milter.REJECT self.IP = hostaddr[0] ## self.port = hostaddr[1] self.Cname = IPname.lower() # Name from a reverse IP lookup if fqdn.match(self.Cname): self.Cfqdn = True self.Cdynip = dynip(self.Cname, self.IP) self.Cipok = self.DNSCheck(IPname, self.IP) if (not self.Cipok[0]) and (self.Cname != IPname): self.Cipok = self.DNSCheck(self.Cname, self.IP) else: self.Cfqdn = False self.Cipok = (None, '', '') self.Cdynip = None self.Hname = None self.Hmyd = None self.Hipok = (None, '', '') self.Sabort = None return Milter.CONTINUE # @Milter.noreply def hello(self, heloname): # ## self.heloname # self.Hname # self.Hfqdn # self.Hmyd # self.Hdynip # self.Hipok # self.log("HELO",heloname) # Ini Setup self.PWmsg = "" if self.Cfqdn: if self.Cdynip: self.PWmsg = self.PWmsg + "Cdynip " # yellow if self.Cipok[0] == None: self.PWmsg = self.PWmsg + "ngCipok " # error elif self.Cipok[0] == False: self.PWmsg = self.PWmsg + "noCipok " # error gray else: self.PWmsg = self.PWmsg + "noCfqdn " self.Hname = heloname.lower() self.Hmyd = None self.Hipok = (None, '', '') if not fqdn.match(self.Hname): self.Hfqdn = False self.Hdynip = None self.PWmsg = self.PWmsg + "noHfqdn " # error gray return Milter.CONTINUE self.Hfqdn = True self.Hmyd = self.my_domain_check(self.Hname) if self.Hmyd: self.PWmsg = self.PWmsg + "Hmyd " # error return Milter.CONTINUE self.Hdynip = dynip(self.Hname, self.IP) if self.Hdynip: self.PWmsg = self.PWmsg + "Hdynip " # yellow if self.Cfqdn: if self.Cname == self.Hname: self.Hipok = self.Cipok if self.Hipok[0] == None: self.PWmsg = self.PWmsg + "ngHipok " # error elif self.Hipok[0] == False: self.PWmsg = self.PWmsg + "noHipok " # error gray return Milter.CONTINUE if self.Cipok[0]: if self.eq_domain_check(self.Cname, self.Hname): #type1 wHipok = self.DNSCheck(self.Hname, self.IP) if wHipok[0]: self.Hipok = wHipok else: self.Hipok = (True, self.Cipok[1], wHipok[2]) return Milter.CONTINUE # 同一ドメイン内のリレー及び記述ミスの回避 Hdmain = self.Hname pos = Hdmain.find('.') Hdmain = Hdmain[pos+1:] if fqdnjp.search(Hdmain): if self.eq_domain_check(self.Cname, Hdmain): #type2 wHipok = self.DNSCheck(Hdmain, self.IP) if wHipok[0]: self.Hipok = wHipok else: self.Hipok = (True, self.Cipok[1], wHipok[2]) return Milter.CONTINUE self.Hipok = self.DNSCheck(self.Hname, self.IP) # 送信ホスト名のDNSの記述ミスの回避(ダイナミックIPも含む) if (not self.Hipok[0]) and (self.Hname != heloname): self.Hipok = self.DNSCheck(heloname, self.IP) if self.Hipok[0] == None: self.PWmsg = self.PWmsg + "ngHipok " # error elif self.Hipok[0] == False: self.PWmsg = self.PWmsg + "noHipok " # error gray return Milter.CONTINUE # @Milter.noreply def envfrom(self, mailfrom, *str): self.log("mail from:", mailfrom, *str) # Ini Setup self.Fname = mailfrom self.Fad = '' self.Fd = '' self.Ffqdn = None self.Fmyd = False self.Fdynip = None self.Fipok = (None, '', '') self.Relay = False self.Rname = [] # list of recipients if self.Fname == '<>': return Milter.CONTINUE mb = parseaddr(self.Fname) if (mb[1] == None) or (mb[1] == ''): self.Ffqdn = False self.PWmsg = self.PWmsg + "noFfqdn " # error return Milter.CONTINUE self.Fad = mb[1] mf = parse_addr(mb[1]) if len(mf) != 2: self.Ffqdn = False self.PWmsg = self.PWmsg + "noFfqdn " # error return Milter.CONTINUE self.Fd = mf[1].lower() if not fqdn.match(self.Fd): self.Ffqdn = False self.PWmsg = self.PWmsg + "noFfqdn " # error return Milter.CONTINUE self.Ffqdn = True self.Fmyd = self.my_domain_check(self.Fd) if self.Fmyd: self.PWmsg = self.PWmsg + "Fmyd " # error return Milter.CONTINUE self.Fdynip = dynip(self.Fd, self.IP) if self.Fdynip: self.PWmsg = self.PWmsg + "Fdynip " # error self.Fipok = self.DNSCheck(self.Fd, self.IP) if self.Fipok[0] == None: self.PWmsg = self.PWmsg + "ngFipok " # error not(MX/A) return Milter.CONTINUE if self.Fipok[0] == False: self.PWmsg = self.PWmsg + "noFipok " # OK if self.Hipok[0]: if self.eq_domain_check(self.Hname, self.Fd): #type1 return Milter.CONTINUE if self.Cipok[0]: if self.eq_domain_check(self.Cname, self.Fd): #type1 return Milter.CONTINUE Fdmain = self.Fd pos = Fdmain.find('.') Fdmain = Fdmain[pos+1:] if fqdnjp.search(Fdmain): if self.Hipok[0]: if self.eq_domain_check(self.Hname, Fdmain): #type2 return Milter.CONTINUE if self.Cipok[0]: if self.eq_domain_check(self.Cname, Fdmain): #type2 return Milter.CONTINUE self.Relay = True self.PWmsg = self.PWmsg + "relay " # yellow return Milter.CONTINUE # @Milter.noreply def envrcpt(self, recipient, *str): self.log("rcpt to:", recipient, ":", *str) self.Rname.append(recipient) ## 外部からはロギング用メールアドレスを拒否する。 ## 拒否する場合、コメントアウトする ##if recipient.lower() == to_pwadmin: ## self.setreply('550','5.1.1','<%s>: Recipient address rejected: User unknown.' % (recipient)) ## self.log_critical('550','(%s:%s) %s %s: Recipient address rejected: User unknown.' % (self.Hname,self.IP,self.Fname,recipient)) ## ## ロギング用メールアドレスに記録を残す場合、コメントアウトする ## ##self.send_admin1(recipient, u"ユーザ名エラー", "abort") ## return Milter.REJECT ## 外部からは拡張アドレスを拒否する。 ## ユーザ名に+が入っているとエラー処理(postfix recipient_delimiter = +)の設定で修正必要 if recipient.find('+') != -1: self.setreply('550','5.1.1','<%s>: Recipient address rejected: User unknown.' % (recipient)) self.log_critical('550','(%s:%s) %s %s: Recipient address rejected: User unknown.' % (self.Hname,self.IP,self.Fname,recipient)) ## ロギング用メールアドレスに記録を残す場合、コメントアウトする ## ロギングパターンを選別する ##self.send_admin1(recipient, u"ユーザ名エラー", "abort") return Milter.REJECT return Milter.CONTINUE # @Milter.noreply def data(self): ## self.log("data") self.log_debug("data") ## Abort 条件の追加 ###++++++++++++++++++++Error if (not self.Hfqdn) and (not self.Cipok[0]) and (not self.Fipok[0]): self.setreply('504','5.5.2','<%s>: Helo command rejected: need fully-qualified hostname.' % (self.Hname)) self.log_critical('504','(%s:%s) %s %s: Helo command rejected: need fully-qualified hostname.' % (self.Hname,self.IP,self.Fname,self.Rname)) return Milter.REJECT ###++++++++++++++++++++Error if (not self.Cipok[0]) and (not self.Hipok[0]) and (not self.Fipok[0]): self.setreply('550','5.7.1','(%s:%s): Helo command rejected: Host not found.' % (self.Hname,self.IP)) self.log_critical('550','(%s:%s) %s %s: Helo command rejected: Host not found.' % (self.Hname,self.IP,self.Fname,self.Rname)) return Milter.REJECT #######################Abort if self.Hmyd: self.setreply('504','5.5.2','<%s>: Helo command rejected: Breach of Local Policy.' % (self.Hname)) self.log_critical('504','(%s:%s) %s %s: Helo command rejected: Breach of Local Policy.' % (self.Hname,self.IP,self.Fname,self.Rname)) ## ロギング用メールアドレスに記録を残す場合、コメントアウトする ##for rad in self.Rname: ## self.send_admin1(rad, u"送信ホスト名がマイドメイン", "abort") return Milter.REJECT if self.Fname != '<>': # (postmaster) OK #######################Abort if (self.Ffqdn == False) or (self.Fdynip): self.setreply('504','5.5.2','%s: Sender address rejected: need fully-qualified address.' % (self.Fname)) self.log_critical('504','(%s:%s) %s %s: Sender address rejected: need fully-qualified address.' % (self.Hname,self.IP,self.Fname,self.Rname)) ## ロギング用メールアドレスに記録を残す場合、コメントアウトする ##for rad in self.Rname: ## self.send_admin1(rad, u"送信者名エラー", "abort") return Milter.REJECT #######################Abort if self.Fmyd: self.setreply('504','5.5.2','%s: Sender address rejected: Breach of Local Policy.' % (self.Fname)) self.log_critical('504','(%s:%s) %s %s: Sender address rejected: Breach of Local Policy.' % (self.Hname,self.IP,self.Fname,self.Rname)) ## ロギング用メールアドレスに記録を残す場合、コメントアウトする ##for rad in self.Rname: ## self.send_admin1(rad, u"送信者がマイドメイン", "abort") return Milter.REJECT ## リトライで真意の確認をする必要性があるのか? ## その場合は、Message-ID の確認が必要と思われる #######################Abort self.Fname != '<>' if self.Fipok[0] == None: self.setreply('550','5.7.1','%s: Sender address rejected: Breach of Domain.' % (self.Fname)) self.log_critical('550','(%s:%s) %s %s: Sender address rejected: Breach of Domain.' % (self.Hname,self.IP,self.Fname,self.Rname)) ## ロギング用メールアドレスに記録を残す場合、コメントアウトする ##for rad in self.Rname: ## self.send_admin1(rad, u"送信者ドメインエラー", "abort") return Milter.REJECT # Ini Setup self.FromAD = [] self.HFrom = None self.HDate = None self.Subject = None self.HMid = None self.HList = None return Milter.CONTINUE # @Milter.noreply def header(self, name, hval): ### self.log_debug("header:%s: %s" % (name,hval)) nbuf = name.lower() if nbuf == "from": ms = [] adbuf = hval.split(',') for ad in adbuf: ma = parseaddr(ad) mn = parse_header(ma[0]) ms.append(mn + ' <' + ma[1] + '>') self.FromAD.append((mn, ma[1])) mf = ",".join(ms) if not self.HFrom: self.HFrom = mf else: self.HFrom = self.HFrom + ',' + mf self.log_debug("Header-From-B:", hval) self.log("Header-From:", mf) elif nbuf == "date": self.HDate = hval elif nbuf == "subject": self.Subject = parse_header(hval) self.log_debug("Subject-B:", hval) self.log("Subject:", self.Subject) elif nbuf == "message-id": self.log("Message-ID:", hval) self.HMid = hval elif nbuf.startswith("list-"): self.HList = True return Milter.CONTINUE # @Milter.noreply def eoh(self): ## self.log("eoh") self.log_debug("eoh") if not self.HDate: self.PWmsg = self.PWmsg + "noHDate " # error if not self.HMid: self.PWmsg = self.PWmsg + "noHMid " # yellow (self.Cdynip,self.Hdynip,self.Relay) True:Error # Ini Setup self.HFromAD = None self.HFfqdn = None self.HFmyd = False self.HFdynip = None self.HFipok = (None, '', '') self.HFdnok = None self.HFadok = None if len(self.FromAD) != 1: self.HFadER = True self.PWmsg = self.PWmsg + "HFadER " else: self.HFadER = False ad = self.FromAD[0][1] self.HFromAD = ad ### アマゾン特別対応 if (ad[0] == "'") and (ad[-1] == "'"): ad = ad[1:-1] ### アマゾン特別対応 if (ad == None) or (ad == ''): self.HFfqdn = False self.PWmsg = self.PWmsg + "noHFfqdn " else: mf = parse_addr(ad) if len(mf) != 2: self.HFfqdn = False self.PWmsg = self.PWmsg + "noHFfqdn " else: dn = mf[1].lower() if not fqdn.match(dn): self.HFfqdn = False self.PWmsg = self.PWmsg + "noHFfqdn " else: self.HFfqdn = True self.HFmyd = self.my_domain_check(dn) if self.HFmyd: self.PWmsg = self.PWmsg + "HFmyd " else: self.HFdynip = dynip(dn, self.IP) if self.HFdynip: self.PWmsg = self.PWmsg + "HFdynip " self.HFipok = self.DNSCheck(dn, self.IP) if self.HFipok[0] == None: self.PWmsg = self.PWmsg + "ngHFipok " elif self.HFipok[0] == False: self.PWmsg = self.PWmsg + "noHFipok " if self.Ffqdn: if self.Fd == dn: self.HFdnok = True else: self.HFdnok = False self.PWmsg = self.PWmsg + "noHFdnok " if self.Fad == ad: self.HFadok = True else: self.HFadok = False self.PWmsg = self.PWmsg + "noHFadok " if self.HList: self.PWmsg = self.PWmsg + "List " ###--------------------Abort if not self.HDate: self.setreply('550','5.7.1','Breach of Header-Date Policy.') self.log_critical('550','Breach of Header-Date Policy.') ## ロギング用メールアドレスに記録を残す場合、コメントアウトする ##for rad in self.Rname: ## self.send_admin2(rad, u"ヘッダー日付エラー", "abort") return Milter.REJECT ###--------------------Abort # 下位ロジックに纏める。 #if not self.HFfqdn: # self.setreply('550','5.7.1','%s: Header-From address rejected: need fully-qualified address.' % (self.HFromAD)) # self.log_critical('550','%s: Header-From address rejected: need fully-qualified address.' % (self.HFromAD)) # ## ロギング用メールアドレスに記録を残す場合、コメントアウトする # ##for rad in self.Rname: # ## self.send_admin2(rad, u"差出人名エラー", "abort") # return Milter.REJECT ###--------------------Abort if (self.HFadER) or (not self.HFfqdn) or (self.HFmyd) or (self.HFdynip): self.setreply('550','5.7.1','%s: Breach of Header-From Local Policy.' % (','.join([m for d, m in self.FromAD]))) self.log_critical('550','(%s): Breach of Header-From Local Policy.' % (','.join([m for d, m in self.FromAD]))) ## ロギング用メールアドレスに記録を残す場合、コメントアウトする ##for rad in self.Rname: ## self.send_admin2(rad, u"差出人エラー", "abort") return Milter.REJECT ###--------------------Abort if not self.HMid: self.setreply('550','5.7.1','%s: Breach of Message-ID Local Policy.' % (','.join([m for d, m in self.FromAD]))) self.log_critical('550','(%s): Breach of Message-ID Local Policy.' % (','.join([m for d, m in self.FromAD]))) ## ロギング用メールアドレスに記録を残す場合、コメントアウトする ##for rad in self.Rname: ## self.send_admin2(rad, u"Message-IDエラー", "abort") return Milter.REJECT ## ダイナミックDNSの場合の処理   black ###--------------------Abort if (self.Cdynip) and ((self.Hipok[0] == None) or (not self.Hfqdn)): self.setreply('550','5.7.1','(%s:%s): Helo command rejected: Host not found.' % (self.Hname,self.IP)) self.log_critical('550','(%s:%s) %s %s: Helo command rejected: Host not found.' % (self.Hname,self.IP,self.Fname,self.Rname)) ## ロギング用メールアドレスに記録を残す場合、コメントアウトする ##for rad in self.Rname: ## self.send_admin2(rad, u"ダイナミックDNSエラー", "abort") # admin Only ## self.send_admin3(rad, u"ダイナミックDNSエラー", "abort") # admin or user return Milter.REJECT ## 警告用ログを残す。 ## ロギング用メールアドレスに記録を残す場合、コメントアウトする ##if (self.Cdynip) or (self.Hdynip) or ((self.Relay) and (self.HList)): ## msg = u"要注意:" ## if (self.Cdynip) or (self.Hdynip): ## msg = msg + u"ダイナミックDNS " ## if (self.Relay) and (self.HList): ## msg = msg + u"MLリレーサーバ " ## for rad in self.Rname: ## self.send_admin2(rad, msg, "black") # admin Only ## self.send_admin3(rad, msg, "black") # admin or user return Milter.CONTINUE def eom(self): ## self.log("eom") self.log_debug("eom") # ヘッダーにセンダーアドレスを追加する 迷惑メール対応をメーラーで行う為 self.addheader('X-PWfrom',self.Fname) # ヘッダーにフィルターステイタスを追加する 迷惑メール対応をメーラーで行う為 if self.PWmsg != "": self.addheader('X-PWmail',self.PWmsg) return Milter.CONTINUE def abort(self): self.log_debug("abort") self.Sabort = True return Milter.CONTINUE def close(self): # # End Setup # # abort 時の注意点を指示する。 if self.Sabort: self.log_warning("sever abort: mail server log read") if self.PWmsg != "": self.log("X-PWmail:", self.PWmsg) self.log("close") return Milter.CONTINUE ## === Support Functions === def my_domain_check(self,td): tl = len(td) for d, l in my_domainlist: if tl == l: if td == d: return True elif tl > l: if td.endswith(d): pw = td[tl - l -1] if (pw == '.') or (pw == '@'): return True return False def eq_domain_check(self, td, d): tl = len(td) l = len(d) if tl == l: if td == d: return True elif tl > l: if td.endswith(d): if td[tl - l -1] == '.': return True return False def DNSCheck(self,name,ipad): adok = False ad4ok = False adng = False mxok = False mx4ok = False mx6ok = False mxng = False rv = False rm = rt = '' s = dnsSession() try: ads = s.dns(name, 'A') if len(ads) > 0: a4ok = True for a in ads: if a == ipad: adok = True elif not a: adng = True mxr = s.dns(name, 'MX') if len(mxr) > 0: for v,n in mxr: ads = s.dns(n, 'A') if len(ads) > 0: mx4ok = True for a in ads: if a == ipad: mxok = True elif not a: mxng = True else: ads = s.dns(n, 'AAAA') if len(ads) > 0: mx6ok = True for a in ads: if not a: mxng = True else: mxng = True if adng or mxng or ((not a4ok) and (not mx4ok)): rv = None else: if adok: rv = True rm += 'A' if mxok: rv = True rm += 'M' if a4ok: rt += 'A' if mx4ok: rt += 'M' if mx6ok: rt += '6' except: self.log_warning("DNS:", traceback.format_exc()) rv = None rm = rt = 'DNS' return (rv, rm, rt) def log_debug(self, *msg): my_logger.debug('[%d] %s',self.id,' '.join([str(m) for m in msg])) def log(self,*msg): my_logger.info('[%d] %s',self.id,' '.join([str(m) for m in msg])) def log_warning(self, *msg): my_logger.warning('[%d] %s',self.id,' '.join([str(m) for m in msg])) def log_error(self, *msg): my_logger.error('[%d] %s',self.id,' '.join([str(m) for m in msg])) def log_critical(self, *msg): my_logger.critical('[%d] %s',self.id,' '.join([str(m) for m in msg])) ## === def main(): my_logger.info("pwfilter startup") global my_domainlist s = sys.argv[1] for v in s.split(','): p = (v,len(v)) my_domainlist = my_domainlist + (p,) my_logger.info("mydomain:" + str(my_domainlist)) # Register to have the Milter factory create instances of your class: Milter.factory = myMilter flags = Milter.ADDHDRS Milter.set_flags(flags) # tell Sendmail which features we use Milter.runmilter("pwfilter",socketname,sockettimeout) my_logger.info("pwfilter shutdown") if __name__ == "__main__": main()