qmail のダブルバウンスのお話。元は postfix-jp ML に投げた質問。
これは初心者向けではなく、メールサーバ管理者を対象に書いているつもりなので、いちおう解説はするが、手短に済ませる。知っていれば読み飛ばし可。知らなければ恥じること。
宛先メールアドレスが存在しないとか宛先サーバ障害で長期間配送できないとかいう場合、メールは送っても届かない。届かないのでメールシステムが「届けられなかった」というメールを送信元に送る。このメールは一般的にはエラーメールと呼ばれることが多いが、メール屋の専門用語では「バウンス」と呼ぶ。
バウンスメールの特徴としては、どこか他から送られてくるのではなく、メールシステム自身が送信するメールであるということのほかに、その送信者アドレスが空であるということが挙げられる。バウンスメールのヘッダ自体は From: <MAILER-DAEMON@example.com> だったりするが、SMTP で送るときの envelepe sender は MAIL FROM: <> と空文字で送信される。空の envelope sender というのは、sendmail だろうが qmail だろうが MS Exchange だろうが、大手 ISP だろうが個人サーバだろうが、SMTP でバウンスメールを送るときに使うというのはお約束である(SMTP 以外のプロトコルでメールが送られるときはこの限りではない)。なお、envelope sender が何なのかということはここでは説明しない。
さて、MAIL FROM: <> なバウンスメールも SMTP という枠組で送られる以上、エラーになって届かないことがありうる。このような場合に送られるのがダブルバウンスである。たとえば、送信元 A も送信先 B もメールアドレスを間違えて送った場合、まず宛先 B が存在しないので、A 宛のバウンスが発生する。しかし、A が自分のアドレスも間違っているとこれも届かない。このときにダブルバウンスが発生する。
SMTP というプロトコル上の規定では、バウンスの際の <> とは異なり、ダブルバウンスについては特に指示はない。ただ、シングルバウンスと区別するため、MAIL FROM: <> で送ることはしない。qmail の場合、ダブルバウンス送信用のアドレスとして #@[] を使う。これが、実は問題なのである。
そういうわけで、qmail はダブルバウンスを #@[] というアドレスから送信するのだが、端的に言うと、これはメールアドレスとして認められている形式ではない。バウンスメールで使われる空アドレスは用途が指定されてちゃんと認められている。が、#@[] なんてのは RFC のどこをひっくり返してみても有効とはいえないアドレスである。qmail 自身はこれをちゃんと処理できるように作られているが、qmail 以外のホストに #@[] なんてアドレスでメール送った場合には不正なアドレスとして拒否されても文句はいえない。
ただ、#@[] はたしかに不正であるが、多少の間違いには目をつぶる、という方針を取って、こんなアドレスでも受け入れてくれる実装の MTA がほとんどだった。受け入れてくれれば、それ以外は通常のメールなので問題が起きることはない。しかし、状況は変わった。
Postfix が、バージョン 2.1 からこの #@[] を不正なアドレスとしてエラーにするようになったのだ。spam や不正中継の拒否といったポリシーによる受信拒否ではなく、純粋にメールアドレスの記法間違いとみなされるので、設定によって変更することはできない。Postfix 作者の Wietse Venema 氏はそんなアドレスを外に出すべきではないと発言しており、救済するつもりはないようだ。qmail の側で何とかしなければならない。不正なアドレスを使っているのは qmail の方なのだから、Postfix を責める筋合はない。
(2005/9/13 追記) 現在βテスト中の Sendmail X でもエラーになることを確認。
qmail では特に設定しなければダブルバウンスは自ホストの postmaster 宛に送られ、外に出てこんな問題を起こすことはない。つまり、#@[] が外に出ていってしまうのは以下の場合である。
前者は locals の中に me で設定されているホスト名が含まれていなかったり、ローカル配送はしてあるが postmaster の .qmail で別ホストに転送していたりする場合である。後者は doublebouncehost がローカルでない外部宛になっている場合になる。
対処としては以下のどちらかになる。
これは簡単である。というか、それがデフォルトだ。わざわざ外部に送るように設定するからこんな問題が顕在化するんだから。doublebouncehost、それが存在しない場合には me が指しているホストがローカル処理されるようにすればいい。ただし、ローカル配送された後で .qmail などから外部に転送していたらまったく意味がないので注意。
ただ、ダブルバウンスをわざわざ外部に送るのはたいていは理由があってそうしていたのだと思うので、内部で完結しろ、というのは答にならないかもしれない(が、本来はローカルで処理すべきである)。そのような場合は、問題となる #@[] を問題ないものに書き換えればいい。
しかし、sendmail や postfix と異なり、qmail のアドレス書き換え機能は極めて貧弱である。というか、実装されていない。ほかの方法で実現可能だから実装しなかったんだと思うけど。実現するには、いったんローカルで受信してから .qmail の中でコマンドを起動し、別のメールとして再送信する。
具体的には、ダブルバウンス受信者となるアドレスの .qmail、たとえば /var/qmail/alias/.qmail-postmaster を以下のように書く。
| [ "x$SENDER" = "x#@[]" ] && qmail-inject -f MAILER-DAEMON -- "$RECIPIENT" && exit 99 || exit 0 &double-bounce@another.host
1行目。envelope sender が #@[] ならば MAILER-DAEMON に書き換えて届いたメールを再送信する。exit 99 により、この条件にマッチした場合は2行目の解釈はおこなわれない。2行目。送信者が #@[] でなければ、double-bounce@another.host にメールを転送する。以上により、#@[] は書き換えられ、それ以外はそのまま外部ホストにメールが転送される。
あるいは以下のようにしてもいい。
% echo double-bounce > /var/qmail/control/doublebounceto % cat /var/qmail/alias/.qmail-double-bounce | bouncesaying 'Permission denied' [ "x$SENDER" != "x#@[]" ] | qmail-inject -f MAILER-DAEMON -- postmaster@another.host
まず、ダブルバウンスの宛先を専用のアカウント double-bounce とする。そして、その double-bounce のエイリアスの中でアドレスの書き換えをおこなう。.qmail-double-bounce の内容は先の例とかなり違うように見えるが、実はほとんど違いはない。
いずれの場合も .qmail をローカルで処理させなければならない。localhost 宛メールでさえも外部に転送する設定をしている場合は、ダミーのバーチャルホストを作ってそれをローカルで処理させるなどの設定が必要である。qmail のドキュメントのうち、このアドレス書き換えの参考になる部分。
なお、qmail では #@[] というアドレスは特別扱いされていて、このアドレスがエラーになってもそれ以上バウンスはしないようになっている。#@[] を書き換えてしまうとこれが効かなくなり、最悪の場合無限ループすることがある。これは qmail に限らず sendmail や postfix でも同じである。十分注意すること。
まあ、そういうわけで、特殊な形式のメールアドレスで送るときにはルールを守りましょう、ということで。
ところで、わしのところは「#@[] で送ってる qmail サーバ」ではなく、「そこの転送先の Postfix 2.1 サーバ」だ。なんで qmail のトラブルシューティングをこっちがやらにゃならんのだ。よけいな手間かけさせやがって…。