[mew-dist 26717] ちょっぴり敗北宣言

Kazu Yamamoto ( 山本和彦 ) kazu at example.com
2005年 12月 9日 (金) 17:46:54 JST


山本です。

後で思い出すための技術的なメモです。技術に興味がある人のみお読み下さい。

フォールバック大作戦から学んだことの一つに、投稿ポートから SMTP ポート
へフォールバックする際は、それぞれで IPv6 から IPv4 へフォールバックし
なければならないというのがあります。

ややこしいので図に描くと、
	投稿ポート/IPv6
		↓
	投稿ポート/IPv4
		↓
	SMTPポート/IPv6
		↓
	SMTPポート/IPv4
とフォールバックしていかなければなりません。

ELisp ではサーバ名とポート名を指定しますので、ポートレベルのフォールバッ
クができます。

名前から IPv6/IPv4 へ変換するのは C レベルなので、IPv6 => IPv4 のフォー
ルバックをするのは、C レベルになります。

blocking な場合を図に描くとこんな感じ:

-----------------------------------------------------------------------
Elisp:	open-network-stream(サーバ名、ポート名)
-----------------------------------------------------------------------
C:	make_network_process {

		getaddrinfo();

		for (それぞれのアドレス) {
		    connect(); 
		    /* blocking なら IPv6 => IPv4 できるよーん */
		}

	}
-----------------------------------------------------------------------

さて、IPv6 => IPv4 のフォールバックを忘れ、non-blocking connect() の仕
組みを描くとこんな感じ:

-----------------------------------------------------------------------
Elisp:	make-network-process(サーバ名、ポート名)	sentinel(イベント)
-----------------------------------------------------------------------
C:	make_network_process {				別の C の関数 {

		getaddrinfo();				select();

		non-blocking connect(最初のアドレス)	接続できたら、
			     				sentinel へ
	}						}
-----------------------------------------------------------------------

というわけで、getaddrinfo() している関数と select() している関数が別な
ので、non-blocking connect() では IPv6 => IPv4 へのフォールバックができ
ません! (受け入れられる方法で C レベルを改造するのは無理!)

まとめ:
1) non-blocking connect() では、IPv6=>IPv4 フォールバックができない
2) blocing connect() では、IPv6=>IPv4 フォールバックができる

よって、いろいろ考えてた結果、
	a) 投稿ポートへは IPv4 を指定して non-blocking connect()
	b) SMTP ポートへは connect()
という風に Mew を改造します。

これにより、以下のようなフォールバックが実現できます。
	投稿ポート/IPv4
		↓
	SMTPポート/IPv6
		↓
	SMTPポート/IPv4

IPv6 普及委員会副会長としては、敗北感に打ちのめされていますが、それ以上
に迷惑メールを退治する方が大切です。

なお、「IPv4 を指定して non-blocking connect()」するには、
make-network-process() の引数 :family に AF_INET の値を指定しないといけ
ません。

BSD では AF_INET は 2 と #define されていますが、他の OS ではどうなって
いるでしょうか?

P.S.

remote サーバだと non-blocking connect() できるが、
local サーバだとできない問題ですが、分かりました。

remote サーバだと、non-blocking connect() した瞬間に「仕様通り」
EINPROGRESS が返ります。

FreeBSD/NetBSD で、local サーバに non-blocking connect() すると、
すぐに TCP RST が返るため、ECONNREFUSED が返ります。(このため、
この場合に限り、なんと IPv6 => IPv4 へフォールバックできます。)

make-network-process() は仕様にない ECONNREFUSED なんて知らないので、接
続できない場合、nil を返すのでした。

挙動さえ理解してしまえば、これはこれでいいかなと思っています。Mew では
対応済みだし。

--かず



Mew-dist メーリングリストの案内