一覧NW-17-1 · 周辺プロトコル

SMTPとIMAPによるメールの送受信

一通をSMTPで送り、配送記録からIMAPのUIDへ対応付けます。内容の取得と既読フラグの更新までを追い、どの応答で何が完了したか確認します。

説明

メールは、送る側のSMTPと、受信箱を扱うIMAPで役割が分かれます。表示上の差出人・宛先と、配送時に使うアドレスも同じ欄ではありません。

一通をSMTPで送り、配送記録からIMAPのUIDへ対応付けます。内容の取得と既読フラグの更新までを追い、どの応答で何が完了したか確認します。

1SMTPの接続と送信者

smtp.example.testのTCP25へ接続済みで、220の挨拶を受け取った状態から始めます。この配送サーバーは演習用中継ホストからのメールを受け付けます。EHLOで機能を確認します。

EHLO拡張SMTPの挨拶 relay.example.test接続元の名前

250-は応答の続き、最後の250と空白は応答の終端です。SIZEの値はこのサーバーが提示するメッセージサイズの上限です。ここではメッセージ自体はまだ送っていません。

2配送エラーの返送先

MAIL FROMはSMTPの封筒情報です。配送エラーを返すアドレスを設定します。

MAIL FROM:封筒の差出人<bounce@example.test>エラーの返送先

このアドレスは本文のFromヘッダーとは別です。エラー返送先を専用アドレスにする配信などでは、両者が異なることがあります。まだ本文の差出人欄は送っていません。

3実際に配送する宛先

ayaの受信箱へ送るため、封筒の宛先を指定します。

RCPT TO:封筒の宛先<aya@example.test>配送する受信箱

配送先はこのRCPT TOで決まります。後から送るToヘッダーを書き換えても、ここで受け付けた配送先が自動的に変わるわけではありません。宛先が複数ならRCPT TOを繰り返します。

4本文を送る準備

DATAに対する354を待ってから、ヘッダーと本文を送ります。

DATAメッセージ内容の入力開始

354は内容を送ってよいという中間応答です。ヘッダーと本文は空行で区切り、ピリオドだけの行でDATAを終了します。本文内で行頭にピリオドがある場合はもう一つ付けて送ります。

5ヘッダーと本文を送信

Toにはteam@example.testを表示しますが、封筒の宛先はayaのままです。Date、Message-ID、Subjectも含めて送信します。

From: Aya <aya@example.test>本文に表示する差出人 To: Team <team@example.test>本文に表示する宛先 Date: Mon, 9 Nov 2026 10:00:00 +0900作成日時 Message-ID: <lab-42@example.test>メッセージ識別子 Subject: Meeting件名 Start at 10:30.本文 .DATAの終端

Q42はこのサーバーのキュー識別子です。250は引受け完了であり、受信者が読んだという意味ではありません。本文のMessage-IDはメッセージの識別用、Q42は中継サーバー内部の処理追跡用です。

6配送側の処理記録

SMTPをQUITで終了し、端末のシェルへ戻った後の操作です。Q42に対応する配送記録を探します。

grep 'Q42'引受け時の識別子で抽出 delivery.log配送処理の記録

記録はUTCの01:00:01Zで、メッセージの+0900では10:00:01です。ayaのINBOXへUID42として格納されました。中継を挟む構成では各サーバーのキューIDが変わるため、時刻・宛先・Message-IDも照合します。

7IMAPで受信箱を選択

ここからはayaとして認証済みのIMAP接続です。IMAPはサーバー上のメールを参照し、既読などの状態も保持します。a1は要求と応答を対応付けるタグです。

a1応答を対応付けるタグ SELECT INBOX受信箱を開く

EXISTSは現在1通、UIDNEXTは次に割り当てるUIDの予測値です。UID42は受信箱の中で識別する番号であり、先頭からの順番1とは別です。UIDVALIDITYが変われば、以前保存したUIDとの対応を見直します。

8既読にせず内容を取得

UID42の内容とフラグを取得します。BODY.PEEK[]なら、取得自体では既読フラグを付けません。

a2 UID FETCH 42UID42を取得 (FLAGS既読などの状態 BODY.PEEK[]既読を付けず全文取得)

FLAGS ()はフラグなしです。波括弧の数は続く内容のバイト数で、改行はCRLFの2バイトとして数えます。Toはteamでも、このメッセージを持つ受信箱は封筒で指定したayaです。

9既読を付けて状態を再取得

既読は端末の画面表示だけでなく、サーバーに保存するフラグです。まず追加し、次の要求で確認します。

a3 UID STORE 42UID42の状態を更新 +FLAGS (\Seen)既読を追加

+FLAGSは既存のフラグを残したまま追加します。FLAGSだけで置き換える操作とは異なります。同じ受信箱を参照する別端末も、更新されたフラグを取得できます。

10サーバーに残ったフラグ

同じUIDを改めて問い合わせ、更新結果を確認します。

a4 UID FETCH 42同じメッセージを再取得 (FLAGS)状態だけを要求

既読フラグが返りました。SMTPの250、配送ログのdelivered、IMAPの\Seenは、それぞれ引受け・受信箱への配送・既読状態の保存を表します。いずれも受信者が内容を理解したことまで証明するものではありません。

まとめ

SMTPの封筒とメッセージヘッダーは別の情報です。

引受け、配送、受信箱のフラグ更新を、それぞれの識別子と応答で確認します。

操作履歴と解説を振り返れます。

SMTP / 配送記録 / IMAP

1
EHLO relay.example.test
250-smtp.example.test
250 SIZE 1048576
2
MAIL FROM:<bounce@example.test>
250 2.1.0 Sender OK
3
RCPT TO:<aya@example.test>
250 2.1.5 Recipient OK
4
DATA
354 End data with <CR><LF>.<CR><LF>
5
From: Aya <aya@example.test> To: Team <team@example.test> Date: Mon, 9 Nov 2026 10:00:00 +0900 Message-ID: <lab-42@example.test> Subject: Meeting Start at 10:30. .
250 2.0.0 queued as Q42
6
grep 'Q42' delivery.log
2026-11-09T01:00:01Z Q42 from=<bounce@example.test> to=<aya@example.test> status=delivered mailbox=INBOX uid=42
7
a1 SELECT INBOX
* FLAGS (\Answered \Flagged \Deleted \Seen \Draft)
* 1 EXISTS
* OK [UIDVALIDITY 7] UIDs valid
* OK [UIDNEXT 43] Predicted next UID
* OK [PERMANENTFLAGS (\Answered \Flagged \Deleted \Seen \Draft)] Flags permitted
a1 OK [READ-WRITE] SELECT completed
8
a2 UID FETCH 42 (FLAGS BODY.PEEK[])
* 1 FETCH (UID 42 FLAGS () BODY[] {170}
From: Aya <aya@example.test>
To: Team <team@example.test>
Date: Mon, 9 Nov 2026 10:00:00 +0900
Message-ID: <lab-42@example.test>
Subject: Meeting

Start at 10:30.
)
a2 OK FETCH completed
9
a3 UID STORE 42 +FLAGS (\Seen)
* 1 FETCH (UID 42 FLAGS (\Seen))
a3 OK STORE completed
10
a4 UID FETCH 42 (FLAGS)
* 1 FETCH (UID 42 FLAGS (\Seen))
a4 OK FETCH completed

演習完了