山本様、皆様
細田です。
おせわになっております。
PR #188 Fix stopping during greeting on some POPs servers
について、お恥ずかしながら私の英語力があやしいところもあり、
日本語で補足説明させていただければと思います。
PR で最初にお出ししたパッチは
他の環境を十分考慮できておらず申し訳ありませんでした。
これは完全に破棄して昨日まったく別の修正を force push しています。
昨日修正した現在の PR では
・平文 POP (ポート 110)
・GnuTLS で POP over TLS/SSL (ポート 995)
・GnuTLS で STARTTLS (ポート 110)
・stunnel で POP over TLS/SSL (ポート 995)
・stunnel で STARTTLS (ポート 110)
いずれでも動作することを確認しております。
これをマージしていただけるとありがたいと思っております。
以下、発生事象と解析した内容などを書かせていただきます。
・発生事象
Mew で GnuTLS を使用して POP over TLS/SSL で
secure.plala.or.jp:995
へ接続するとパスワードを聞かれる前に止まってしまいます。
#ぷららユーザが使用する標準的な POP over TLS/SSL サーバです。
#つまりほとんどのぷららユーザが影響を受けていると考えています。
再現用の設定とその際の Mew-debug は
https://github.com/kazu-yamamoto/Mew/pull/188#issuecomment-1975051666
にあります。
Mew-debug を見ると、ユーザ名
`USER
te...@example.com`
を送出せずにその手前で止まっていました。
なお、POP over TLS/SSL ではなくて
GnuTLS の STARTTLS を使って
secure.plala.or.jp:110
へ接続しても同様の事象となります。
・最初の分析
POP の greeting は本来 1 行で送られてくるものなんでしょうが、
Mew-debug を見ると `<GREETING>` に複数行の CAPA レスポンスが入っていて、
しかもその最終行が `.` になっていませんでした。
secure.plala.or.jp:995 以外の正常動作する POP over TLS/SSL サーバがあったので、
同様の設定で試したところ、やはり `<GREETING>` に
複数行の CAPA レスポンスが入っていましたが、
最終行が `.` になっており、ユーザ名の送出へ進んでいました。
つまり、うまくいかない
secure.plala.or.jp:995 は CAPA レスポンスを
2 つのパケットに分割して返してきていて、
1 つ目は
```
+OK Capability list follows
TOP
USER
RESP_CODES
PIPELINING
EXPIRE NEVER
UIDL
```
2 つ目は
```
.
```
となっています。時間差はほとんどなくて人間の目では
分割されていることがわからないぐらいです。
一方、うまくいく POP over TLS/SSL サーバは CAPA レスポンスを
1 パケットで分割せずに返していて、最終行が `.` になっていました。
・最初の修正案
最初に作った PR は単純に greeting は 1 行ではなくて複数行だとするものでした。
GnuTLS を使っていると上記のように複数行になるので良いのですが、
ご指摘いただいてそれ以外では 1 行しかないのでマズいと気が付きました。
・なぜ GnuTLS だと greeting が無いのか
元々
https://github.com/kazu-yamamoto/Mew/blob/d41dc93785d231f1e391ba61893aacd1331d5726/elisp/mew-pop.el#L777
の直前のコメントに書いてありましたが、GnuTLS を使うと greeting を受信できない、
先にクライアントから何か送出する必要がある、ということだと理解しています。
#正確には、open-network-stream 関数のリターン値から
# greeting を得ることは可能だが、
#受信データには入ってこないということだと思います。
これは POP over TLS/SSL であっても STARTTLS であっても同様という認識です。
# GnuTLS による STARTTLS は open-network-stream 関数の引数で、
#何を受信したら何を送信して、という手順を指定し、
# open-network-stream 関数の内部ですべて処理され、
# Mew へ返ってきた時には既に TLS アップグレードされた状態という認識です。
#また、Mew は open-network-stream 関数へ STARTTLS に失敗したら
#切断される設定をしているようなので、
# TLS アップグレードできなかったらエラーになると思います。
そのためとにかくダミーで CAPA を送信し、
そのレスポンスを greeting とみなして動作が継続できるようにしている、
と理解しました。
#本来は open-network-stream 関数のリターン値から greeting を得て
# greeting の処理をすべきなのかもしれませんが、
#受信データとして得られるわけではないので、
#ヤヤコシくなりそうに思います。
大抵の POP over TLS/SSL サーバは
この CAPA レスポンスを greeting とみなす workaround で大丈夫だったのでしょうが、
本来 1 行であるべきところ複数行になってしまっているのがよろしくないですし、
secure.plala.or.jp:995 は、さらにパケット分割されているため
引っ掛かってしまった、
ということだと思います。
・現在の PR
最初の修正は完全に破棄しました。
動作としては greeting を待つところから開始、
となっていたところを、GnuTLS を使っている場合は greeting 待ちをスキップして、
CAPA レスポンスを待つところから開始するようにしました。
元々、GnuTLS の場合は先に CAPA を送信するようになっていたので、
greeting を待たずにそのまま CAPA レスポンスを待てば普通に greeting ではなくて
CAPA レスポンスの複数行の処理が行われてそのまま処理が継続できる、
という形になっています。
最初の修正は GnuTLS 以外を考慮していませんでしたが、
今回の修正は GnuTLS 以外には影響がないので、特に問題は起きないものと思います。
GnuTLS で
secure.plala.or.jp:995 以外に接続した場合であっても、
greeting は受信できず CAPA から始まることには変わりないですし、
CAPA レスポンスが複数行なのも同じですから問題ないものと思っています。
長々とすみません。
よろしくおねがいいたします。
---
細田 真道 <
true...@trueroad.jp>