GEEKy Script Writer [perl and more!]
You should permit the JavaScript!!
スポンサーサイト
上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。
2015年は本気出す
2014年は適当すぎたから2015年はちょっと本気出すけど2016年は全力でサボるわ。
スポンサーサイト
2013年が終わって1年が経とうとしている
まだ感覚的には2012年の年末なんだよ。わかるだろ?
気づいたら2013年が終わってた。
いやまだあと1日残ってる。俺らの2013年はまだ終わらない。
コンパイラのバグだ!→違う OSのバグだ!→違う Gmailのバグだ→違った
Perlからメールを送信する必要があったので、Net::SMTPあたりのモジュールを入れて
さくっとスクリプトを書きたかったのだが、今ではどのISPでも当たり前となったOP25Bによって
自宅サーバーからはメールが送れないので、仕方なくGmailのSMTPサーバーを使って
メールを送ることにした。

GmailのSMTPサーバー(ポート587)はSTARTTLSの使用が強制になっている。


$ telnet smtp.gmail.com 587
Trying 74.125.25.108...
Connected to gmail-smtp-msa.l.google.com.
Escape character is '^]'.
220 mx.google.com ESMTP xs1sm2390861pac.7 - gsmtp
HELO kobanza.me
250 mx.google.com at your service
MAIL FROM: ■■■@gmail.com
530 5.7.0 Must issue a STARTTLS command first. xs1sm2390861pac.7 - gsmtp


「Perl Gmail」でぐぐってみると、Net::SMTP::TLSというCPANにあるモジュールを使えば
よい(STARTTLSでSMTP通信をしてくれる)ことがわかったので、これを使うことにした。

とりあえず何通かThunderbirdとかで適当に作ったメッセージを送ってみると、
FromヘッダのメールアドレスがログインしているGmailのメールアドレスに
書き換えられてしまうが、それ以外は特に問題なく送信できた。

普通のメールは問題なく送れることがわかったので、
次はHTML形式かつ472KBほどの添付ファイルつきメールを送ってみたのだが...

送ったメールを受信側で開くと、同じ添付ファイルとHTMLの文書が
41個存在するとんでもないメールになっていた。

メールのソースを見ると・・・


QRbmd/uXuvuayOzrL1y6dh2leYdPnkZWe/PO3VOoIZ9aj6KBvtXDnf0rkeZp7OhGzAo/PAK2
i9YM2M3fHNjw2QiME7NxcimCzy6jwHgBRMCmwHguKSWnMDkbKkjKImYnZuYlZOSB2fE4n6YT
s2PTsh2pYDaAzU3204Et0prHxhmwk8TA+NfGxu2c1u7ABq3nAPuPBmMDXLv5/wbhdbr0DcYi
T4xj5lKcPxsGQPhgMSpCYAPMVppRIUipCo0NsRAwG+WEydHxxGyG7VTIRet0c/KMLBi0IUWC
ARwdFRjJuZSaZWOyY5znkgOHUBqAjcNpRnEYF3JlkuKz8kQl0NiRlIhxpJtEYCflFELJuUXu
4sxm2J6BtGu9xJlPeiqzsTENY1mmJyBdnl70FGUUVwjCgBgj4zJSdkWVqJzKaq7cqpo8qLoW
yodq6gqg2vReturn-Path: <■■■@□□□□□□>
X-Original-To: ■■■@kobanza.me
Delivered-To: ■■■@kobanza.me
Received: from smtpa25.m3.home.ne.jp (smtpa25.m3.home.ne.jp [220.152.44.32])
by kobanza.me (unknown) with ESMTP id 89B76B9D1B
for <■■■@kobanza.me>; Thu, 15 Aug 2013 22:08:13 +0900 (JST)
(メールアドレスを一部伏せています)


?!

Base64エンコードされた添付ファイルのパートの途中から、
なぜかメールの先頭にあるはずのヘッダが続いている・・・!
しかもこれがあと40回同じように繰り返し出現する。(そのためメーラーで41個添付ファイルがあるように見えた)

これは・・・Gmailのバグだ!
私は、このとき愚かにもそれを確信した。

なぜそう思ったかというと、Gmailは送信時にFromヘッダを書き換えたりしているから
きっとそこで変なことをしてバグっているんだろう、という安直な考えから。。

当然、送信側(つまりNet::SMTP::TLS)に問題がある可能性もあるわけで
その切り分けすらまだしていないじゃないか。
Gmailのバグとかいうのはまだ早い。

切り分けは簡単で、接続するSMTPサーバーをローカルのPostfixとかに変えて
同じメールを送ってみればよくて、試した結果、同じようにメールが壊れた。。
これで、Net::SMTP::TLS側の問題ということがわかった。
わかった。

とりあえず、既知のバグかどうか調べるため、
https://rt.cpan.org/Public/Dist/Display.html?Name=Net-SMTP-TLS
を見てみた。

まず、すべてステータスが「新規」か「オープン」になっているのを見て
不穏な予感がするが、それっぽいものを探すと、それっぽいものを一つ見つけた。

https://rt.cpan.org/Public/Bug/Display.html?id=63538


When I try to send an e-mail with multiple attachments through
Net::SMTP::TLS, the attachments get corrupted.

書いてある内容は今回の件と同じっぽい。

ありがたいことに、修正する箇所とコードまで書いてある。

The error can be resolved by adding a single line to TLS.pm (by analogy
with NET::SMTP) -- the error is in the "datasend" subroutine.

Just after line 369 ($len -= $w;) add a new line:

$offset += $w;


試しに、TLS.pmをこのように書き換えてメールを送ってみたところ
受信側でメールは壊れなくなった。
ということで、原因はやはりNet::SMTP::TLSのバグだった・・・。

以下、バグの詳解


TLS.pmのdatasend()内、350行目から原因箇所を抜粋。

350 my $len = length($line);
351 my $offset = 0;
352 my $win = "";
353 vec($win,fileno($cmd->{sock}),1) = 1;
354 my $timeout = $cmd->{sock}->timeout || undef;
355
356 local $SIG{PIPE} = 'IGNORE' unless $^O eq 'MacOS';
357
358 while($len)
359 {
360 my $wout;
361 if (select(undef,$wout=$win, undef, $timeout) > 0 or -f $cmd->{sock}) # -f for testing on win32
362 {
363 my $w = syswrite($cmd->{sock}, $line, $len, $offset);
364 unless (defined($w))
365 {
366 carp("Error: $!");
367 return undef;
368 }
369 $len -= $w;
370 }
371 else
372 {
373 carp("Error: Timeout");
374 return undef;
375 }
376 }
377
378 }

363行目と369行目に注目。
syswrite()が1回で$lineの内容を全て出力してくれれば問題ないが、
$lineのサイズが大きい場合、1回で全て出力されず、
出力できた分の長さが戻り値として返ってくる場合がある。
そのとき、369行目で$line全体の長さから出力できた長さを引いて、
次のsyswrite()が呼び出されることになるが、
$offsetの値に出力した長さを加算していないため、
毎回$lineの先頭から同じデータが出力される。
こうして、今回の壊れたメールが出来たというわけだ。

試しに、369行目の直前に、printで$wの値を出力してみると
w: 16384
w: 16384
w: 16384
w: 16384
w: 16384
w: 16384
w: 16384
w: 16384
w: 16384
w: 16384
w: 16384
w: 16384
w: 16384
w: 16384
w: 16384
w: 16384
w: 16384
w: 16384
w: 16384
w: 16384
w: 16384
w: 16384
w: 16384
w: 16384
w: 16384
w: 16384
w: 9325
のように、41回出力されているのがわかる。

2013年8月現在、 Net-SMTP-TLS-0.12 ではこの問題は未修正で、
使用時には先ほどのパッチを当てる必要がある。
また、他にも未解決のバグがいくつかあり、
長い間メンテナンスされていないようなので、
このモジュールを使う際は気を付けたほうがよい。
というかできるだけ使わないほうがよいかもしれない。

教訓


  • 多数の記事で紹介されていて、広く使われていそうなCPANモジュールでも、基本的なバグが残っていることがある

  • CPANモジュールを使う際は、まず登録されているバグの数とメンテナンスされているかを調べたほうがよい

  • WindowsのバグとかGoogleのバグとか安易に言わないほうがいよい。だいたい間違っている

  • ライブラリのバグだ!は割とあるので、使う際にはちゃんとテストをしよう

  • テストもコードレビューもしてないような糞モジュールをうっかりCPANで公開して放置すると、
    異国の糞ブログで晒されたりするので気を付けよう


  • 検索用


    Google 神 Gmail SMTP メール Perl STARTTLS
    CPAN カス Net::SMTP::TLS 糞モジュール Ruby厨 去れ

    気づいたら2013年も半分が過ぎて笑えない。
    ここの存在を完全に忘れてた。特にかけるようなネタもないのがつらい。何より時間がない気がする。やべえテンションあがってきた。
    copyright © 2005 GEEKy Script Writer [perl and more!] all rights reserved.
    Powered by FC2ブログ.
    上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。