コアダンプの数だけ強くなれるよ

見習いエンジニアの備忘log

PowerShellでメールを受信してみる

とあるソフトウェアの試験で対象の装置からSMTPサーバへログが記載されたメールが飛ぶかどうか確認する必要がありました。フリーのSMTPサーバを使えれば良いのですが、試験環境にはインターネットから切り離されたWindowsのPCしか無い状況。しかも勝手にソフトは入れられない。

どうしようかと悩んでいた時、前にPowerShellでSyslogを受信して確認した事があったのを思いだしメールの受信もできるはず、ということでPowerShellで動く簡易的なSMTPサーバを作ってみました。

SMTPサーバのスクリプト

動けば良いやで作ったのでアホみたいな実装ですがご容赦ください。

# SMTP_S.ps1

function Start-Smtp-Server([int]$port=25, [string]$IPAdress="127.0.0.1", [switch]$Echo=$false){
    $listener = new-object System.Net.Sockets.TcpListener([System.Net.IPAddress]::Parse($IPAdress), $port)
    $listener.start()

    write-host "Waiting for a connection on port $port..."
    $client = $listener.AcceptTcpClient()
    write-host "Connected from $($client.Client.RemoteEndPoint)"
    
    $stream = $client.GetStream()
    
    Start-Sleep -m 500
    
    # 送信 220
    $text = "220 localhost testshell ready `r`n"
    $Sendbytes = [System.Text.Encoding]::UTF8.GetBytes($text)
    $stream.Write($Sendbytes, 0, $Sendbytes.Length)
    write-host "SERVER ==> CLIENT : SEND 220"
    
    # 受信 EHLO or HELO
    [byte[]]$Readbytes = 0..255|%{0}
    $stream.Read($Readbytes, 0, $Readbytes.Length)
    $text = [System.Text.Encoding]::UTF8.GetString($Readbytes)
    write-host "CLIENT ==> SERVER : $text"
    
    # 送信 250 色々
    $text = "250-Nice to meet you.`r`n"
    $text = $text + "250-8BITMIME`r`n"
    $text = $text + "250-STARTTLS`r`n"
    $text = $text + "250-AUTH=CRAM-MD5 PLAIN LOGIN ANONYMOUS`r`n"
    $text = $text + "250 SIZE`r`n"
    $Sendbytes = [System.Text.Encoding]::UTF8.GetBytes($text)
    $stream.Write($Sendbytes, 0, $Sendbytes.Length)
    write-host "SERVER ==> CLIENT : SEND 250"
    
    # 受信 MAIL
    [byte[]]$Readbytes = 0..255|%{0}
    $stream.Read($Readbytes, 0, $Readbytes.Length)
    $text = [System.Text.Encoding]::UTF8.GetString($Readbytes)
    write-host "CLIENT ==> SERVER : $text"
    
    # 送信 250 OK
    $text = "250 Okey dokey`r`n"
    $Sendbytes = [System.Text.Encoding]::UTF8.GetBytes($text)
    $stream.Write($Sendbytes, 0, $Sendbytes.Length)
    write-host "SERVER ==> CLIENT : SEND 250 OK"
    
    # 受信 RCPT
    [byte[]]$Readbytes = 0..255|%{0}
    $stream.Read($Readbytes, 0, $Readbytes.Length)
    $text = [System.Text.Encoding]::UTF8.GetString($Readbytes)
    write-host "CLIENT ==> SERVER : $text"
    
    # 送信 250 OK
    $text = "250 Recipient accepted`r`n"
    $Sendbytes = [System.Text.Encoding]::UTF8.GetBytes($text)
    $stream.Write($Sendbytes, 0, $Sendbytes.Length)
    write-host "SERVER ==> CLIENT : SEND 250 Recipient accepted"
    
    # 受信 DATA
    [byte[]]$Readbytes = 0..255|%{0}
    $stream.Read($Readbytes, 0, $Readbytes.Length)
    $text = [System.Text.Encoding]::UTF8.GetString($Readbytes)
    write-host "CLIENT ==> SERVER : $text"
    
    # 送信 354 End message with period
    $text = "354 End message with period`r`n"
    $Sendbytes = [System.Text.Encoding]::UTF8.GetBytes($text)
    $stream.Write($Sendbytes, 0, $Sendbytes.Length)
    write-host "SERVER ==> CLIENT : SEND 354 End message with period"
    
    
    # 分割受信(fragment)に対応するのは面倒なので受信しきるまで適度にwait...
    Start-Sleep -m 1000
    
    # 受信 DATA
    [byte[]]$Readbytes = 0..2048|%{0}
    $stream.Read($Readbytes, 0, $Readbytes.Length)
    $text = [System.Text.Encoding]::UTF8.GetString($Readbytes)
    write-host "CLIENT ==> SERVER : $text"
    
    
    # 送信 250 Mail accepted
    $text = "250 Mail accepted`r`n"
    $Sendbytes = [System.Text.Encoding]::UTF8.GetBytes($text)
    $stream.Write($Sendbytes, 0, $Sendbytes.Length)
    write-host "SERVER ==> CLIENT : SEND 250 Mail accepted"
    
    write-host "`nGoodbye! See You!`n"
    
    $client.Close()
    $listener.Stop()
    write-host "Connection closed."
}

Start-Smtp-Server -ip $Args[0] -port $Args[1]


動作確認用のMail送信スクリプト

# Mail.ps1

# 送信先メールアドレス
$to = "you@sukekeke.com"
 
# 送信元メールアドレス
$from = "me@sukekeke.com"

# メール件名
$subject = "Powershell Message"

# 本文
$body += "Mail to Server From Powershell`n"
$body += "Hello World !!"

# SMTPサーバのIPアドレス
$smtp = "192.168.2.101"


# メール送信
Send-MailMessage -To $to -From $from -SmtpServer $smtp -Subject $subject -Body $body -Encoding UTF8


結果

# サーバ起動

PS C:\ > .\SMTP_S.ps1 192.168.2.101 25
Waiting for a connection on port 25...
# メール送信

PS C:\ > .\Mail.ps1


キャプチャして確認


f:id:segmentation-fault:20170927233903p:plain

f:id:segmentation-fault:20170927233913p:plain


キャプチャを見ると無事にメールを受信できていました。 ただ、このSMTPサーバはクライアントから受信した内容はガン無視してシーケンスを進めるので少しでも想定と違うパケットを受信するとアウトです。

今回はちょっとした確認用途なので、これ以上は頑張りません。

まとめ

Google先生に聞いても、PowerShellからMailを送信するサンプルコードは沢山ありますが、受信する例は中々見つからなくて苦戦しました。(私がWindows系のプログラミングに疎いというのもありますが...)

あまり必要になるケースが無いの仕方ないですね(^-^;)


参考

下記を参考にさせていただきました。

Simple TCP Listener – Null Payload

Powershellでメールを送信する最も簡単な方法 | いろんなサーバー設定研究所