【メール入門 第9回】コマンドで調べる ― dig・nc・openssl・PowerShellでDNS・ポート・SMTP・TLS・認証を確かめる

メールサーバー
スポンサーリンク
スポンサーリンク

「複合機からのメールが届きません。PCからTest-NetConnectionで25番を試したら成功するんですが……」

メールのトラブルでは、どこまでが正常で、どこからがおかしいのかを切り分けることが大切です。そのためには、名前解決、TCPの疎通、SMTPの応答、TLS、認証を、下の層から順に手を動かして確かめます。

第9回では、LinuxとWindowsの両方で使えるコマンドを、層ごとに紹介します。第8回までに出てきた確認の方法も、ここでまとめて扱います。


下の層から順に確かめる

①名前解決(dig/Resolve-DnsName)、②TCPの疎通(nc -vz/Test-NetConnection)、③SMTPの応答(nc・telnet/TcpClient)、④TLS(openssl s_client/SslStream)、⑤認証(AUTH)を下から順に確かめ、②で失敗したときはtimed outなら途中で遮断、Connection refusedなら相手が待ち受けていないことと、最初に失敗した段に原因のある層があることを示す図
段確かめることLinuxWindows
①名前解決(MX・SPF 等)digResolve-DnsName
②TCP の疎通nc -vzTest-NetConnection
③SMTP の応答nc・telnetPowerShell の TcpClient
④TLS と証明書openssl s_clientPowerShell の SslStream
⑤認証openssl s_client で AUTH同左

最初に失敗した段に、原因のある層があります。順番に確かめていきましょう。


① MX・SPF・DMARCを引く

$ dig example.jp MX +short
10 mail1.example.jp.
20 mail2.example.jp.
$ dig example.jp TXT +short | grep spf1
"v=spf1 ip4:203.0.113.25 include:spf.protection.outlook.com -all"
$ dig _dmarc.example.jp TXT +short
"v=DMARC1; p=quarantine; rua=mailto:dmarc-rua@example.jp"
$ dig s1._domainkey.example.jp TXT +short     # DKIM(セレクターは DKIM-Signature の s=)
$ dig _mta-sts.example.jp TXT +short          # MTA-STS(TLS-RPT は _smtp._tls)

PS> Resolve-DnsName example.jp -Type MX -DnsOnly | Select-Object NameExchange, Preference
PS> (Resolve-DnsName example.jp -Type TXT -DnsOnly).Strings | Where-Object { $_ -like 'v=spf1*' }
PS> (Resolve-DnsName _dmarc.example.jp -Type TXT -DnsOnly).Strings
  • MXは優先度の値の小さい順に試される。MXが無いとA/AAAAへ送る。MX 0 .(Null MX)は受け取らない宣言(連載「DNS入門」第6回)
  • SPFはドメイン名に1つ、DMARCは_dmarc.、DKIMはセレクター._domainkey.、MTA-STSは_mta-sts.、TLS-RPTは_smtp._tls.の下のTXT(第4回〜第6回)
  • 社内DNSと外部DNSで答えが違う(スプリットホライズン)と、社外から見た答えと食い違う。外部のリゾルバーや権威DNSを、digの@や-Serverで指定して比べる(連載「DNS入門」第8回)
  • DANEのTLSA(_25._tcp.MXのホスト名)は、digで型にTLSAを指定して引く

② ポートの疎通を確かめる

$ nc -vz mail.example.jp 25
Connection to mail.example.jp (203.0.113.25) 25 port [tcp/smtp] succeeded!
$ nc -vz -w 5 mail.example.jp 587
nc: connect to mail.example.jp (203.0.113.25) port 587 (tcp) timed out: Operation now in progress

PS> Test-NetConnection mail.example.jp -Port 25
ComputerName     : mail.example.jp
RemoteAddress    : 203.0.113.25
RemotePort       : 25
InterfaceAlias   : Ethernet
SourceAddress    : 192.0.2.101
TcpTestSucceeded : True
  • まずTCPでつながるかだけを見る。つながらなければSMTP以前の問題(ファイアウォール・OP25B・サービス停止・名前解決)
  • timed outは途中で落とされている(ファイアウォール・OP25Bに多い)、Connection refusedは相手に届いたが待ち受けていない(サービス停止・ポート違い)
  • Test-NetConnectionはWindows標準。失敗すると警告とTcpTestSucceeded : Falseが出る。SMTPの応答やTLSまでは見ない
  • 送信元となる機器・サーバーから試す。ファイアウォールやリレーの許可は送信元IPで決まるため、自分の端末で通っても機器からは通らないことがある

冒頭の「PCからは成功する」は、まさにこの落とし穴です。


③ 手でSMTPを話す(nc・telnet)

$ nc -C relay.example.jp 25        # -C で改行を CRLF にする(RHEL の ncat も -C)
220 relay.example.jp ESMTP Postfix
EHLO app01.example.jp
250-relay.example.jp
250-PIPELINING
250-SIZE 10240000
250-STARTTLS
(略)
250 CHUNKING
MAIL FROM:<alert@example.jp>
250 2.1.0 Ok
RCPT TO:<ops@example.jp>
250 2.1.5 Ok
QUIT
221 2.0.0 Bye
  • FreeBSD 14 の nc には -C が無い。Postfix は改行が LF だけの行も既定で受け付けるので、nc <サーバー> 25 のままで試せる(smtpd_forbid_bare_newline = reject の場合を除く)
  • 機器と同じ送信元・宛先でMAIL FROM・RCPT TOを打てば、拒否の理由(Relay access denied、Sender address rejected等)がその場で分かる。RCPT TOまで確かめてQUITすれば、メールは送られない
  • telnetでも同じ(telnet relay.example.jp 25)。Windowsのtelnetクライアントは既定で無効で、使うにはWindowsの機能の追加(管理者権限)が要る。Windowsでは次のPowerShellの方法を勧める
  • STARTTLSのあとは暗号化されるので、nc・telnetでは続けられない。暗号化後はopensslを使う
  • 公開のMTAや他組織のサーバーで試さない。自組織のサーバー、または許可を得た相手に限る

PowerShellでSMTPを話す(TcpClient)

$tcp = [System.Net.Sockets.TcpClient]::new('relay.example.jp', 25)
$st  = $tcp.GetStream()
$r   = [System.IO.StreamReader]::new($st)
$w   = [System.IO.StreamWriter]::new($st); $w.NewLine = "`r`n"; $w.AutoFlush = $true
function Read-Reply { do { $l = $r.ReadLine(); "S: $l" } while ($l -match '^\d{3}-') }
function Send-Cmd($c) { "C: $c"; $w.WriteLine($c); Read-Reply }
Read-Reply
Send-Cmd 'EHLO pc01.example.jp'
Send-Cmd 'MAIL FROM:<alert@example.jp>'
Send-Cmd 'RCPT TO:<user@example.com>'
Send-Cmd 'QUIT'
$tcp.Dispose()
S: 220 relay.example.jp ESMTP Postfix
C: EHLO pc01.example.jp
S: 250-relay.example.jp
(略)
C: RCPT TO:<user@example.com>
S: 454 4.7.1 <user@example.com>: Relay access denied
C: QUIT
S: 221 2.0.0 Bye
  • Windows PowerShell 5.1・PowerShell 7のどちらでも、追加のインストールなしで動く。.ps1の実行がポリシーで止められる場合は、コンソールに貼り付けて実行する。S:がサーバーの応答、C:が送ったコマンド
  • Read-Replyは、250-のように3桁の後ろがハイフンの行が続く限り読み、複数行の応答を最後まで受け取る
  • 例では、このPCのIPがリレーを許されていないため454 4.7.1 … Relay access deniedになった(第8回)。機器のIPから試すと結果が変わる

④ openssl s_clientの出力の読み方

$ openssl s_client -starttls smtp -connect mail.example.jp:25 -crlf \
    -name app01.example.jp -verify_hostname mail.example.jp -nocommands
(抜粋)
Certificate chain
 0 s:CN=mail.example.jp
   i:C=US, O=Let's Encrypt, CN=YR1
 1 s:C=US, O=Let's Encrypt, CN=YR1
   i:C=US, O=ISRG, CN=Root YR
 2 s:C=US, O=ISRG, CN=Root YR
   i:C=US, O=Internet Security Research Group, CN=ISRG Root X1
Verification: OK
Verified peername: mail.example.jp
New, TLSv1.3, Cipher is TLS_AES_256_GCM_SHA384
250 CHUNKING
openssl s_clientのCertificate chainで、0のサーバー証明書の発行者i:が1の中間証明書の主体s:に、1の発行者が2の主体につながり、2の発行者のISRG Root X1はクライアントの信頼ストアにあることと、Verification: OKとVerified peernameが出れば鎖と名前の両方を検証でき、中間証明書が送られていないとVerify return code: 21になることを示す図
  • -starttls smtpで、EHLOとSTARTTLSまでを自動で行う。EHLOの名前は-name(省略時はmail.example.com)。465番では-starttls smtpを外す。-crlfで改行をCRLFにする
  • Certificate chain:0がサーバー証明書、1以降がサーバーの送った中間証明書。各番号のi:が次の番号のs:とつながるかを見る。かつてのLet’s EncryptのDST Root CA X3は2021年に期限切れで、現在はYR1→Root YR→ISRG Root X1のような鎖になる(連載「証明書入門」第7回)
  • Verification: OKとVerified peername:が出れば、チェーンと名前の両方の検証に成功している。名前は-verify_hostnameを付けたときだけ検証する
  • 最後の250 …はSTARTTLSの前のEHLOの応答の残りで、ここからEHLOを打ち直す
  • 対話中にRCPT TOやQUITを打つと、行頭のR・Qがs_clientのコマンド(再ネゴシエーション・終了)と解釈される。-nocommandsを付けるか、小文字で打つ

WindowsでSTARTTLS後の証明書を見る(SslStream)

$tcp = [System.Net.Sockets.TcpClient]::new('mail.example.jp', 25)
$st = $tcp.GetStream(); $r = [System.IO.StreamReader]::new($st)
$w = [System.IO.StreamWriter]::new($st); $w.NewLine = "`r`n"; $w.AutoFlush = $true
function Read-Reply { do { $l = $r.ReadLine(); "S: $l" } while ($l -match '^\d{3}-') }
Read-Reply | Out-Null
$w.WriteLine('EHLO pc01.example.jp'); Read-Reply | Out-Null
$w.WriteLine('STARTTLS'); Read-Reply           # S: 220 2.0.0 Ready to start TLS
$ssl = [System.Net.Security.SslStream]::new($st, $false)
$ssl.AuthenticateAsClient('mail.example.jp')    # 検証(名前を含む)に失敗すると例外
$ssl.SslProtocol                                # Tls13
$c = [System.Security.Cryptography.X509Certificates.X509Certificate2]::new($ssl.RemoteCertificate)
$c | Format-List Subject, Issuer, NotAfter
$ssl.Dispose(); $tcp.Dispose()
Subject  : CN=mail.example.jp
Issuer   : CN=YR1, O=Let's Encrypt, C=US
NotAfter : 2026/11/25 14:33:41
  • STARTTLSへの220を受け取ったら、同じストリームの上でSslStreamのTLSハンドシェイクを始める。opensslの-starttls smtpと同じ動き
  • 検証に失敗するとAuthenticateAsClientが例外を出す。中身だけを見たいときは、連載「証明書入門」第4回と同じ、検証を省く指定({ $true })を調べる目的に限って使う
  • Windowsの表示では、Issuerの並びがopensslと逆(CNが先)になる。PowerShell 7なら$ssl.NegotiatedCipherSuiteで暗号スイートも分かる

⑤ AUTHを試す

$ printf '\0alert@example.jp\0dummy-pass' | base64       # AUTH PLAIN:\0 ID \0 パスワード
AGFsZXJ0QGV4YW1wbGUuanAAZHVtbXktcGFzcw==
PS> [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes("`0alert@example.jp`0dummy-pass"))
AGFsZXJ0QGV4YW1wbGUuanAAZHVtbXktcGFzcw==

$ openssl s_client -starttls smtp -connect relay.example.jp:587 -crlf -quiet -name app01.example.jp
EHLO app01.example.jp
(略)
250-AUTH PLAIN LOGIN
(略)
AUTH PLAIN AGFsZXJ0QGV4YW1wbGUuanAAZHVtbXktcGFzcw==
235 2.7.0 Authentication successful
  • AUTH PLAINは「\0 ID \0 パスワード」をBase64にして送る。AUTH LOGINは334 VXNlcm5hbWU6(Username:)と334 UGFzc3dvcmQ6(Password:)に、IDとパスワードを1つずつBase64で返す(RFC 4954、第4回)
  • Base64は暗号ではない。ログやパケットキャプチャに残れば誰でも元に戻せる。必ずTLSの後で送り、試験には試験用のアカウントを使い、終わったらパスワードを変える
  • コマンドの履歴(~/.bash_history、PowerShellの履歴)にパスワードが残る。read -sやGet-Credentialで入力して変数から組み立て、履歴を消す
  • 失敗すると535 5.7.8 Error: authentication failed(Postfix)や535 5.7.139 Authentication unsuccessful(Microsoft 365。テナントでSMTP AUTHが無効など)が返る

テストメールを送る:swaksとSend-MailMessage

$ swaks --server relay.example.jp --port 587 --tls \
    --auth LOGIN --auth-user alert@example.jp \
    --from alert@example.jp --to ops@example.jp \
    --ehlo app01.example.jp --header "Subject: swaks test"
(抜粋)
<-  220 relay.example.jp ESMTP Postfix
 -> STARTTLS
<-  220 2.0.0 Ready to start TLS
 ~> AUTH LOGIN
<~  235 2.7.0 Authentication successful
 ~> MAIL FROM:<alert@example.jp>
<~  250 2.1.0 Ok
  • swaksはSMTPの試験用ツール(最新は20240103.0)。--tls(STARTTLSが必須)、--tls-on-connect(465番)、--auth、--quit-after RCPT(RCPT TOまで試して送らない)など、手打ちの手順を1行にできる。->が送信、<-が受信、~付きはTLSの中のやり取り
  • パスワードは--auth-passwordで渡さず、プロンプトで入力する(履歴に残さない)
  • WindowsのSend-MailMessageは、Microsoftが非推奨(obsolete)としている。SMTPサーバーとの安全な接続を保証できないためで、PowerShellに直接の後継は無い。Microsoftは、サードパーティのMailKit、Exchange OnlineならMicrosoft GraphのSend-MgUserMailを案内している

疎通や拒否の理由を調べるだけなら、送らずに、TcpClientの方法でRCPT TOまで確かめれば足ります。


ヘッダーの解析(Receivedをたどる)

届いたメールからは、ヘッダーで経路をたどります(第3回)。

Authentication-Results: mx.example.com; spf=pass smtp.mailfrom=example.jp;
        dkim=pass header.d=example.jp; dmarc=pass header.from=example.jp
Received: from mx.example.com (mx.example.com [198.51.100.25])
        by mbox01.example.com with LMTP id 7fKp0Q; Sat, 26 Sep 2026 10:15:03 +0900   ← ③
Received: from relay.example.jp (relay.example.jp [203.0.113.30])
        by mx.example.com (Postfix) with ESMTPS id 4ZbQ1x2Kp3z9
        for <user@example.com>; Sat, 26 Sep 2026 10:15:02 +0900 (JST)             ← ②
Received: from app01.example.jp (app01.example.jp [192.0.2.21])
        by relay.example.jp (Postfix) with ESMTP id 8C1E0A1B2F
        for <user@example.com>; Sat, 26 Sep 2026 10:15:01 +0900 (JST)             ← ①
Receivedヘッダーを下の行から順に左から並べると、app01からrelay.example.jp(キューID 8C1E0A1B2F)へ10:15:01にESMTPで、mx.example.com(キューID 4ZbQ1x2Kp3z9)へ10:15:02にESMTPSで、mbox01(受信箱)へ10:15:03にLMTPで渡り、送信側が書いた行は偽って書けるので自組織が付けた行で判断することを示す図
  • 下から上へ読むと、最初のサーバーから受信箱までの経路になる。各行のfrom(送ってきた相手)と、1つ下の行のby(受け取ったサーバー)がつながっているかを見る
  • 時刻の差がそのサーバー間の遅延。with ESMTPSはTLSで受け取ったことを示す。idはそのサーバーのキューIDで、ログの追跡に使える
  • 自組織のサーバーが付けた行より下は、送信者が偽って書ける。判断は、自組織の受信サーバーの行とAuthentication-Resultsで行う
  • 長いヘッダーは、MicrosoftのMessage Header Analyzer(解析はブラウザーの中で行う)やGoogle Admin ToolboxのMessageheaderに貼り付けると、経路と遅延を表で見られる。後者は入力をサーバーへ送るため、個人情報を含むヘッダーの扱いに注意する

ログの追跡(キューID・Message-ID)

# ① 利用者のヘッダーの Message-ID から、このサーバーのキューIDを探す
$ grep 'message-id=<20260926011501.4F2A@app01.example.jp>' /var/log/maillog
Sep 26 10:15:01 relay postfix/cleanup[2346]: 8C1E0A1B2F: message-id=<20260926011501.4F2A@app01.example.jp>
# ② キューIDで1通の全行を出す(第8回)
$ grep 8C1E0A1B2F /var/log/maillog
# ③ 相手のキューID(queued as …)を相手の管理者に伝える
… status=sent (250 2.0.0 Ok: queued as 4ZbQ1x2Kp3z9)

# Microsoft 365 の場合(Exchange Online PowerShell)
PS> Get-MessageTraceV2 -MessageId '<20260926011501.4F2A@app01.example.jp>'
送信元のアプリapp01からrelay.example.jp(自組織、キューID 8C1E0A1B2F)、mx.example.com(相手、キューID 4ZbQ1x2Kp3z9)へ渡るとき、Message-IDはどのサーバーでも同じで、キューIDはサーバーごとに別なので、サーバーをまたぐときはqueued asのキューIDでつなぐことを示す図
  • Message-IDは全経路で変わらない(送信側が付けた値。無ければ最初のMTAが付ける)。キューIDはサーバーごとに変わる。Message-IDで入口を見つけ、キューIDでそのサーバーの中を追う
  • サーバーが複数台なら、時刻をそろえて(NTP・タイムゾーン)突き合わせる。ログはsyslogサーバーやSIEMに集めておく(連載「syslog入門」)
  • Microsoft 365はメッセージ追跡(Exchange管理センター、またはGet-MessageTraceV2。旧Get-MessageTraceは2025年9月から廃止が進んでいる)、Google Workspaceは管理コンソールのメールログ検索で追う(第10回)
  • 相手側に調べてもらうときは、時刻(タイムゾーン付き)・送信者・宛先・Message-ID・相手のキューIDを伝える

考えてみよう

  1. WindowsのPCからTest-NetConnection -Port 25は成功するのに、複合機からのメールは届きません。次に何を、どの順で確かめますか。
  2. openssl s_client -starttls smtpでVerify return code: 21 (unable to verify the first certificate)が出ました。何が原因と考えられ、誰に何を頼みますか。
  3. 「取引先に送ったメールが届いていないと言われた」と利用者から連絡がありました。分かっているのは送信日時と宛先だけです。自組織のログと相手への依頼で、何を手がかりに追いますか。

ヒント:この記事の「下の層から順に確かめる」図とTcpClientの方法、Certificate chainの図と連載「証明書入門」第5回、Message-IDとキューIDの図が答えにつながります。


まとめ

  • 名前解決 → TCP → SMTP → TLS → 認証の順に、下の層から確かめる。最初に失敗した段に原因がある
  • 疎通は送信元の機器から試す。timed outは遮断、Connection refusedは待ち受けなし
  • 手でSMTPを話すなら、Linuxはnc -C、WindowsはPowerShellのTcpClient。RCPT TOまでで止めれば送られない
  • TLSはopenssl s_client -starttls smtpかSslStream。Certificate chainはi:が次のs:につながるか
  • AUTHのBase64は暗号ではない。履歴にパスワードを残さない
  • Send-MailMessageは非推奨。試験送信はswaks
  • 1通を追うのはMessage-ID、サーバーの中を追うのはキューID。またぐときはqueued as

次回は、Microsoft 365・Google Workspace・Amazon SESといったクラウドのメールと、機器からの通知メールの送り方を扱います。


連載「メール入門」全12回

  1. メールが届くまで
  2. SMTPのやり取り
  3. メッセージの構造
  4. ポートと暗号化・認証
  5. 送信ドメイン認証 前編
  6. 送信ドメイン認証 後編
  7. 迷惑メール対策と配信
  8. MTAの運用
  9. コマンドで調べる(この記事)
  10. クラウドと現代のメール
  11. メールのトラブルシュート
  12. メールの設計演習

コメント

タイトルとURLをコピーしました