要約

  • RSET は、未完成のメールを長い SMTP セッションの中で破棄できる単位にした。送信者、宛先、データを消す一方、サーバーは接続を閉じてはならない。
  • 返答の 250 OK が証明するのはリセットの完了である。すでに完了した配送を取り消すものでも、TLS や認証を消すものでも、配達証明でもない。

拒否された二人目と、残った一人目

クライアントが MAIL FROM に続けて二つの RCPT TO を送る。サーバーは最初の宛先を受理し、次を拒否する。アプリケーション側には「二人とも受け取れないなら送らない」という条件がある。

後の拒否は、先の受理を取り消さない。サーバーには差出人と一人分の宛先が残っている。そこで DATA を送れば、意図を失った封筒に本文を流し込むことになる。新しい MAIL を始めれば、開いたままのトランザクションと衝突する。TCP を切れば確実に片付くが、再利用できたはずの接続まで失う。

RSET は「このメールだけを中止する」と伝える。サーバーの肯定応答を受けて初めて、次の封筒が前の宛先を引き継がないと双方が合意できる。

1982 年から存在した消去点

RFC 821 は当初から RSET を定義していた。保存済みの送信者、宛先、メールデータを破棄し、バッファと状態表をクリアし、受信側は成功を返す。同じ仕様は、一つのセッションにゼロ個以上のメールトランザクションが存在すると記した。

接続を複数メールで使うなら、一通だけを安全に捨てる方法が不可欠になる。失敗のたびに切断すれば再利用の利点は消える。捨てられなければ、状態が次のメールへ漏れる。リセットは例外処理の付属品ではなく、セッション再利用の前提だった。

RFC 821 は、以前のコマンドがエラーでも直ちに伝送路を閉じないよう求めた。接続が途中で失われた場合は、保留中の処理を取り消すが、完了済みの処理は元に戻さない。接続、進行中の試行、完了結果は別々に扱われていた。

自分が忘れただけでは足りない

クライアントは自分のメモリを消せる。しかし、サーバーのバッファを直接見ることはできない。安全な再利用には、相手側の状態遷移を示す返答が要る。

RFC 5321 では、引数のない RSET250 OK を返さなければならない。命令はいつでも送れる。開いたトランザクションがなければ実質的に何もしないが、あれば送信者、宛先、データを捨てる。そしてサーバーは RSET を理由に接続を閉じてはならない。切断を指示するのは QUIT である。

この 250 は前のメールの受理を意味しない。物理ストレージ上の全記録を消去したとの保証でもない。監査や不正利用の記録は残り得る。保証されるのは、前の封筒が次のトランザクションのプロトコル状態として働かないことだ。

残す記憶と捨てる記憶

「すべてのバッファと状態表をクリア」という文だけなら、セッション全体が初期化されるようにも読める。周辺の規則は範囲を狭くする。

TCP 接続は続く。サーバーの挨拶はやり直さない。捨てた封筒のためだけに、EHLO で得た全機能を調べ直す必要はない。TLS は解除されず、認証済みのセッションが匿名に戻るわけでもない。一方、前の送信者、宛先、途中の本文は絶対に次へ持ち越せない。

RFC 5321 は、セッション中に受理された新しい EHLORSET と同じように開いたトランザクションをリセットすると述べる。ただし EHLO は挨拶と機能応答を再実行するので、通常は余計な処理がある。同じなのは中途の封筒を終わらせる効果であり、命令全体の役割ではない。

STARTTLS はさらに広い境界を示す。RFC 3207 では TLS 確立後、双方がその前に得た知識を捨て、クライアントは改めて EHLO を送る。安全性の文脈が変わったため、セッションの能力認識まで再構築する。単なるメール中止に、その広い初期化は不要である。

完了した結果は巻き戻らない

メールトランザクションは MAIL で始まり、一つ以上の RCPT を経て、本文転送で終わる。RSET の対象はまだ完了していない現在の単位だ。最終本文が受理された後なら、責任はすでに成立している。後から reset を送っても、次のメールの準備になるだけで、前の受理を撤回できない。

長いセッション全体を一つのデータベース・トランザクションのように考えると、この点を誤る。各メールには個別の最終結果がある。接続が続くことと、過去を取り消せることは別である。

監視側も応答コードだけを集計してはいけない。宛先への 250、本文受理の 250、リセットへの 250 は、すべて対象が異なる。

PIPELINING が要求した、より厳密な帳簿

RFC 2920 は待ち時間を減らすため、一部の SMTP コマンドを応答待ちなしでまとめて送れるようにした。RSET、送信者コマンド、RCPT TO はグループ内に置ける。前のメールの最後の本文と、次の reset・新しい送信者を同じ TCP 送信に入れることさえできる。

しかし、境界がなくなったわけではない。応答は順番に対応させ、すべて確認する。前の本文、新しい reset、新しい送信者にはそれぞれ別の結果がある。近接して届くからといって、一つの成功にはならない。

高速化された経路ほど、クライアントのローカル状態より応答の対応関係が重要になる。相手が reset を処理した証拠なしに、次の封筒が清潔だとは言えない。

不確定なチャンクを片付ける

RFC 3030 の CHUNKING は、本文を BDAT のブロックで運ぶ。BDAT LAST 後の追加ブロック、同じトランザクションでの DATABDAT の混在、失敗して状態が不確定になったブロックなどでは、続行前に RSET が必要となる。

ここでは、すでに送った部分データを現在のメッセージから切り離すことが reset の仕事である。接続や TLS の履歴を消すのではなく、断片が次のメールとして解釈されない状態を回復する。

RFC 4954 は逆方向から範囲を守る。開いたメールトランザクション中の AUTH は許されず、503 で拒否される。セッションの認証主体を、送信者・宛先・本文の途中に差し込んではならない。

回復とは、全部捨てることではない

エラー後に何も起きなかったふりをするか、接続をすべて壊すか。その二択の間に RSET がある。捨てる対象を一通の未完了メールに限定し、相手の確認を得て、残せる会話を残す。

SMTP の歴史が示すのは、状態をなくす方法ではない。状態の所有範囲を分け、どこで終わったかを証明する方法である。

出典