smtpd.SMTPServer.process_messageで受信者を拒否する方法は?
質問
次のようなオーバーライドされたクラスでメッセージを処理するとしましょう:
class MailProcessorServer(smtpd.SMTPServer):
def process_message(self, peer, sender, rcpttos, data):
badrecipients = []
for rcpt in rcpttos:
badrecipients.append(rcpt)
#Here I want to warn the sender via a bounced email
# that the recipient does not exist
raise smtplib.SMTPRecipientsRefused(badrecipients)
#but this just crashes the process and eventually the sender times out,
# not good enough
ただちに送信者に返送したい。代わりに、送信サービス(たとえば、GMail)は最終的にあきらめて、何時間も後にユーザーに警告します。 ドキュメントはかなりまばらに見えます。
解決
ソース(申し訳ありません!)、 process_message
の仕様には以下が含まれます:
この関数はNoneを返すべきです。 通常の「250 Ok」応答。さもないと 目的の応答文字列を返します RFC 821形式。
「554悪い受信者%s」%badrecipientsを返すことができます」その raise
ステートメントを使用する代わりに、完全に満足のいくものではありません(RFC 821により「250 Ok」を返すべきである善と悪の混在を適切に説明していません)後で警告メールを送信することもできます)が、「すぐに返送する」ようです。 raise
で探している効果。
他のヒント
メッセージを拒否する方法は、 process_message
メソッドからエラーコードを含む文字列を返すことです。例:
return '550 No such user here'
ただし、RFC 821では、メッセージデータの転送後にエラーコード550を返すことが許可されておらず( RCPT
コマンドの後に返される必要があります)、残念ながらsmtpdモジュールでは返されませんその段階でエラーコードを返す簡単な方法を提供します。さらに、smtpd.pyは、自動マングリング「プライベート」を使用してクラスをサブクラス化することを困難にします。ダブルアンダースコア属性。
次のsmtpdクラスのカスタムサブクラスを使用できる場合がありますが、このコードはテストしていません。
class RecipientValidatingSMTPChannel(smtpd.SMTPChannel):
def smtp_RCPT(self, arg):
print >> smtpd.DEBUGSTREAM, '===> RCPT', arg
if not self._SMTPChannel__mailfrom:
self.push('503 Error: need MAIL command')
return
address = self._SMTPChannel__getaddr('TO:', arg)
if not address:
self.push('501 Syntax: RCPT TO: <address>')
return
if self._SMTPChannel__server.is_valid_recipient(address):
self._SMTPChannel__rcpttos.append(address)
print >> smtpd.DEBUGSTREAM, 'recips:', self._SMTPChannel__rcpttos
self.push('250 Ok')
else:
self.push('550 No such user here')
class MailProcessorServer(smtpd.SMTPServer):
def handle_accept(self):
conn, addr = self.accept()
print >> smtpd.DEBUGSTREAM, 'Incoming connection from %s' % repr(addr)
channel = RecipientValidatingSMTPChannel(self, conn, addr)
def is_valid_recipient(self, address):
# insert your own tests here, return True if it's valid
return False
以下は、メールをバウンスせずに破棄します。
return '554-5.7.1'
問題:バウンスせずにメールを拒否すると、送信者MTAは何度もメールを再送信しようとします。
エラーコード 550
は電子メールをバウンスします。これは、スパムサーバーにメールサーバーに関する情報を提供したくないため、悪い考えです。それに注意してください。
return '550'
両方のエラーで smtplib.SMTPException
が発生します。このような例外を処理するために使用する簡略化されたコードを次に示します。
try:
if bounce:
return '550 Bad address'
else:
self.send_and_quit(sender, recipients, data)
except smtplib.SMTPException as e:
raise e
except Exception as e:
# Catch any other exception
logging.error(traceback.format_exc())
if not isinstance(e, smtplib.SMTPException):
self.send_and_quit(sender, recipients, data)
else:
raise e