mrubyでSMTP


IoTなネットワークメソッドはHTTP,HTTPS,MQTTなどが一般に使われていますが、なにか忘れていませんか?そうHTTPなんかよりも古くからあるSMTPです。

ということで、mrubyでメールを送ってみました。

SMTPサーバはヤフーのアカウントでヤフーメールを使いました。最近のSMTPはSSLだったりするのでmruby-polarsslを使います。認証はSMTP_AUTHなのでmruby-base64も使いました。

余談ですがpolarsslは去年ARMに買収されてmbedドメインになっていますね。

ZRouterでクロスのmruby-polarssl/polarsslのビルドはひっかかるかなとおもったんですが、すんなりビルドできました。

def oneline(s)
  line = ""
  while ch = s.read(1)
    line << ch
    if ch == "\n"
      break
    end
  end
  puts line
  line
end

myaddr = "[email protected]"
toaddr = "[email protected]"
pw = "himitu"
header = "From: " << myaddr << "\r\nSubject: test\r\n"
body = "MORI MORI Test"

socket = TCPSocket.new('smtp.mail.yahoo.co.jp', 465)

entropy = PolarSSL::Entropy.new
ctr_drbg = PolarSSL::CtrDrbg.new(entropy)

ssl = PolarSSL::SSL.new
ssl.set_endpoint(PolarSSL::SSL::SSL_IS_CLIENT)
ssl.set_authmode(PolarSSL::SSL::SSL_VERIFY_NONE)
ssl.set_rng(ctr_drbg)
ssl.set_socket(socket)

ssl.handshake

oneline(ssl)

ssl.write("EHLO smtp.mail.yahoo.co.jp\r\n")

while true
  res = oneline(ssl)
  if res[0,4] == "250 "
    break
  end
end

plain = Base64::encode(sprintf("%s¥0%s¥0%s", myaddr, myaddr, pw))
auth = "AUTH PLAIN " << plain << "\r\n"

ssl.write(auth)

oneline(ssl)

ssl.write("MAIL FROM:" << myaddr << "\r\n")

oneline(ssl)

ssl.write("RCPT TO:" << toaddr << "\r\n")

oneline(ssl)

ssl.write("DATA\r\n")

oneline(ssl)

mail = ""
mail << header << "\r\n" << body << "\r\n.\r\n"
ssl.write(mail)

oneline(ssl)

ssl.write("QUIT\r\n")

oneline(ssl)

ssl.close_notify

socket.close

ssl.close

plainを作っているところは最初<<で連結したら不思議な動作になってしまったのでsprintfにしました。¥0が含まれる文字列ってサポートされてるのかな。。。

FreeBSD/mipsなmrubyで実行するとこんな感じです。

# mruby smtp.rb
220 smtp531.mail.kks.yahoo.co.jp ESMTP
250-smtp531.mail.kks.yahoo.co.jp
250-AUTH LOGIN PLAIN XYMCOOKIE
250-PIPELINING
250 8BITMIME
235 ok, go ahead (#2.0.0)
250 ok
250 ok
354 go ahead
250 ok 1457748965 qp 87684
221 smtp531.mail.kks.yahoo.co.jp

この仕組みは何かの通知に使えると思いますが、パスワードの管理の問題があります。何にも使ってないアカウントであってもパスワードが漏洩すると偽装の可能性があります。