2008-10-21 本当は今日は別の作業をやる予定だったのだが……
面白いネタがあったので緊急更新。従って別作業は遅延(苦笑
◆ [日記][Unix][情報] tcpserver(てかもしかしてucspi-tcp全般か?)においては「ローカルホスト」≠「localhost」である
何の話かというと、tcpserverが設定する環境変数TCPLOCALIPとTCPLOCALHOSTの話だ。ぶっちゃけると、DNSがおかしい状況では、引数に-l servernameがいるんじゃね? という話だ。
何があったか、順を追って説明していこう。
今日、某所からヘルプが来た。以下、サーバ関係が特定される情報が含まれるので、IPアドレスとして、192.168.1.1と192.168.1.2、ドメイン名としてhost1.example.com(便宜上host1と書くこともある)とhost2.example.com(便宜上host2と書くこともある)を使って話を進める。
読み進める上での理解のしやすさからちょこッと書いておくと、管理者サイドからは、(host1=)host1.example.com=192.168.1.1、(host2=)host2.example.com=192.168.1.2と設定したつもりだった。
ちなみにヘルプ内容は大体こんな感じ。
host2.example.comのPOP3アクセスが、環境変わったらすっごい遅いんだけど、原因分かる?
POP3とは、まぁメールの受信によく使われるプロトコルの名前である。
自分の所から、おもむろにtelnet host2.example.com 110とか入力してhost2のPOP3ポートにアクセスしてみる。
……確かに、POP3で一番最初に出力されるはずの
+OK なんとかかんとか
という文字の表示までに結構イライラするくらい待たされる。数十秒単位だと思えばよし。
さっそくhost2にログインして調査を開始。ちなみに、host2のPOP3はtcpserverを使っていた。tcpserver≒TCP1ポートしかサポートしてないinetd≒TCP1ポートしかサポートしてないxinetdと言えば分かりやすいだろうか。ようは、特定のTCPポートでクライアントからの接続を待ち受けて、アクセスが来たら、子プロセスを生成してそっちに処理を渡すプログラムだ。
何かに例えろ? うーん。誤解を恐れずに書くと、ウェイトレスが一人しかいないレストランのようなものだ。ウェイトレス一人以外にウェイトレスやウェイターがいる訳ではなく、注文はすべて一旦ウェイトレス(=tcpserver)が受け取る。受け取った料理は普通、シェフ(子プロセス)に渡される*1。
多分この後の説明でもウェイトレスが登場するはずなので、適当な名前をつけておこう。「長門」なんてどうだろうか? 当然、涼宮ハルヒシリーズのあの長門さんである。が、まぁ命名は趣味なのであんまり気にしないでくれ。色々と妄想はふくれあがるが気にしないことにする。あぁそうだ。一回しか出てこないがついでに命名しておこう。シェフは「朝倉」でどうだろうか? 長門の命令通りに動かないといけない所がそっくりだ。ちなみに「朝倉」は量産できるし、自分の仕事が終わると帰ってしまう(笑)
ついでなので、説明の時に使う他の登場人物にも名前をつけよう。具体的にはhost1とhost2と自分の所。
host1は「古泉」でhost2は「キョン」あたりが適切か。ハルヒとみくるがいないが、まぁ彼女らは外で客引きをしている事にしておこう。あぁ、そうそう。長門はキョンの下で動いてるんで、その辺ヨロシク。
で、何の話だったか……あぁ、そうそう。tcpserver長門の話であった。
まず、長門は呼び出されると、お客さん(=アクセス元)の注文を待つ。
注文が来ると、長門は即座に動き出す。でも長門は一人しかいないので、自分で料理をすると凄いお客さんに迷惑だ。なので、長門は注文を受け取ると、朝倉さんを呼び出して「このお客さんにこの料理つくって届けて。よろしく」という感じで朝倉さんに注文をすべて放り投げて、長門は再びお客さんの注文を待つのだ。
こうすることで、長門はひたすらお客さんの注文を聞きまくればいいし、量産できる朝倉さんはひたすら料理&お届けをし続ければいいのだ。ほら、いい感じに仕事が分担されているだろう?
で、最初の方に書いたヘルプ内容「POP3で一番最初に出力されるはずの+OK なんたらかんたら」の表示までに時間がかかるという話だが、長門と朝倉さんに例えると、朝倉さんに仕事がいくのにえらい時間がかかっている。普通は謎の言語で即朝倉を呼び出しているハズだが、なんか考え込んでいる。なぜ長門が考え込んでいるか分かるかというと、朝倉さんは呼び出された瞬間、にこやかな顔をして「+OK」と返事を返すはずだから。多分ナイフ付きで。
これはつまり、host2でtcpserverから先の処理が実行されるまでに時間がかかることを意味している。
というわけで、tcpserver長門が悪いことは分かった。
んじゃぁ、長門は何が悪いんだ?
往々にして、「ログインに時間がかかる」だの「なんか知らないけど数十秒単位で時間がかかる」という事象は、DNSを引きに行ってしまって待っている事が多い。というか、それ以外は子プロセスがバグってない限りあり得ない(そしてこれは無いことを上で説明した)
DNSの話は、長門チックに言うと「あなた、誰?」と聞いているが、答えが得られなくて固まっている状況を想像して貰えると分かりやすいかも知れない。他にも長門がバグってるとか考えられるが、別世界の長門(=別のポートにbindしているtcpserver)はバグっていないので、多分「あなた、誰?」状態なのだろう。というか、今まで見てきた事例のほとんどがこのパターンだ。というわけで、まずはそこを中心にチェックしてみる。
tcpserver - options(オプションの翻訳版)で見ると、DNSに関係しそうなパラメータは、「リモートホスト名を調べる」だ。それ以外にも経験上「IDENTを行う」と相手が(ファイアウォールなどで)無反応だとタイムアウト待ちになるので、IDENTももしかしたら関係するかも知れない。
長門調に言ってくれって? 「相手が誰か」(リモートホスト名)を調べるのと、「相手の情報」(IDENT)を調べてるんだよ、多分。
というわけで、早速オプションを調べてみる。
……-H -Rが指定してあった。つまり、リモートホスト名も調べていないし、IDENTも調べていない。長門チックに言うと「何も聞いていない」のだ。
なんだなんだ? 何が問題なんだ?
他に何か調べてるものは……
-l localname
ローカルホスト名を調べるのをやめて、localname を TCPLOCALHOST にセットする
これくらいのもんだ。ローカルホスト名って、多少ネットに関わってる人なら即座に「localhost」だろうと想像するだろう。自分もそう思った。長門に例えろ? 「私は、長門有希」って言っているようなモンだ。実際にtcpserver - environ(環境変数の翻訳版)を見ると、
TCPLOCALHOST ローカルホスト ドメイン名 得られない場合は変数はセットされない
とあった。また、
TCPLOCALIP ローカルホスト IPアドレス
もセットされていて、普通に考えると127.0.0.1だろう。長門調に……難しいな。長門の所属してる情報統合思念体に「長門は127.0.0.1番だから!」って管理番号が書いてあると思ってくれ。そう思っていたさ。ちなみに、情報統合思念体には「古泉は192.168.1.1番。キョンは192.168.1.2番」とこの時点では思っている自分。
で、一応念のため確認する。確認は重要だ。
というわけで、こんな感じのスクリプトを組んでtcpserverから実行してみた。
#!/bin/sh
echo $TCPLOCALHOST
echo $TCPLOCALIP
環境変数を表示するだけのプログラム。長門に「名前と管理番号言ってみて?」と聞いたと思えばよい。
ワシが想定していた答えは
localhost
127.0.0.1
とまぁ、自分の説明をすると思っていた。しかし帰ってきたのはこんな答え
192.168.1.2
1行目の空白はミスではない。空行。
何でhost2のIPが帰ってくるんだ? しかもhost2.example.comって名前が引けてないし!
自分は長門だと思っていたら、キョンの管理番号が出てきて、しかもキョンの名前がわからんと来たもんだ。
……ワケガワカラン。
ここで、問題をまとめてみよう。
1. なんで遅いのか?
2. なんでTCPLOCALHOSTが空白なのか。
3. なんでTCPLOCALIPが127.0.0.1じゃないのか。
長門さん的に言うと、
3. なんで長門じゃなくてキョンの管理番号なのか。
2. キョンの名前が出てこないのは何故なのか。
1. 長門は何をやってて遅くなってるのか。
で、だ。
こういう時は、大元のドキュメントかソースコードを読むに限る。
ucspi-tcpの作者さんであらせられるdjb大先生のプログラムは慣れてる部分じゃないととっても読みにくいので、とりあえずドキュメントを読む。TCP environment variablesあたり。
$TCPLOCALIP is the IP address of the local host, in dotted-decimal form.
$TCPLOCALHOST is the name listed in DNS for the local host. If no name is available, $TCPLOCALHOST is not set. Beware that $TCPLOCALHOST can contain arbitrary characters.
自分には、the IP address of the local hostと言っているようにしか見えないのだが……なんで長門じゃなくてキョンになるんだ?
やっぱりドキュメントじゃだめだ。ソースコード読まないと。ここは例えにくいので専門用語のみで。
djb大先生の名前の付け方からしてtcpserver.cにコードがあると考えるのが自然だ。しかもいつものパターンならグローバル変数にそれっぽい名前のものが用意されていて、あちこちで弄ったり参照したりしているハズ。
さて、どこで環境変数が設定されてるだろう……と追いかける。
158行目あたりにこんなのがあった。
if (socket_local4(t,localip,&localport) == -1)
strerr_die2sys(111,DROP,"unable to get local address: ");
localipstr[ip4_fmt(localipstr,localip)] = 0;
remoteportstr[fmt_ulong(remoteportstr,remoteport)] = 0;
if (!localhost)
if (dns_name4(&localhostsa,localip) == 0)
if (localhostsa.len) {
if (!stralloc_0(&localhostsa)) drop_nomem();
localhost = localhostsa.s;
}
env("PROTO","TCP");
env("TCPLOCALIP",localipstr);
env("TCPLOCALPORT",localportstr);
env("TCPLOCALHOST",localhost);
とってもこれっぽい。
socketからlocalipをとってきて文字列にしてDNS引いて設定してるっぽい。
え? socketからlocalipを取ってきて? つまりこれって……(次の段落に続く)
ちなみに宣言は44行目あたりにこんな感じ。
char localip[4];
char localipstr[IP4_FMT];
static stralloc localhostsa;
char *localhost = 0;
IPv6対応なんてシラネーゼってあたりがしびれる。
ちなみに最初、main関数側でlocalipを、入力されたホスト名(今回の場合は、"0"なので、"0.0.0.0")にしていて頭がパニック状態になったが、使い回しているだけっぽい。さすがグローバル変数。さすがdjb大先生。
ここから長門さん復活。
socketからlocalipを取ってきてTCPLOCALIPに設定していると言うことは何を意味するかというと、「TCP接続を受けたIPアドレス」……今回はtcpserverがhost2で動いてるので、そのIPアドレスである192.168.1.2が取れるハズである。これは実証結果と一致する。これにより問題の2.が解決する。
何を言っているかというと、tcpserverにおける「local host」=「ローカルホスト」=「TCPのコネクション結果、ローカル側についているIPのホスト(192.168.1.2)」≠「localhost(127.0.0.1)」ということだ。あぁややこしい。
それに従って192.168.1.2を逆引きした結果がTCPLOCALHOSTに入っているはずだが、これはまた別の問題なので次の段落以降で話そう。
長門さんでまとめよう。今まで「あなたの名前は?」と聞いた時に「長門の名前」を聞いたつもりになっていたが、長門さんは、長門さんが動いてる「キョンの名前」を答えていたということだ。びっくりだ。今までの認識を改めないと行けないようだ。これをlocal hostと言うのか。確かに、localなhostだけどさ。
さて、これで問題2は解決した。これがここのタイトルとなっている『「ローカルホスト」≠「localhost」』という事が分かったであろうか。自分的にはかなり衝撃的な事実だった。
分かりにくいよ!!
さて、問題はまだ解決していない。
1. 遅いこと、と、2. TCPLOCALHOSTが空白、である理由はまだ分からないのだ。
ぶちまけて言うと、これはDNSの設定ミスであった。host2でnslookup 192.168.1.2とする、遅くて、かつ、hostが帰ってこない。nslookup何か使うなよ、というな。自分はdigはいまだにヘルプ見ながらじゃないとわからんのだ。その代わりdjbdns関連のコマンドは大体使えるが(笑)
長門バージョン。情報統合思念体がイケズ。
行った作業を順にトレース。
まず、192.168.1.2の管理サーバを探し出した。具体的にはroot-servers.net群の適当なIPから辿って、2.1.168.192.in-addr.arpaのNSレコードを追いかけた。そしたら、請け負ってるのがhost1でIPが192.168.1.1だと言われた。
で、192.168.1.1に2.1.168.192.in-addr.arpaのPTRレコードよこせ、と言ったら、2.1.168.192.in-addr.arpaは別名(CNAME)で2.1/255.168.192.in-addr.arpaを見ろ、それはhost2.example.comが知っている(NSだ)と言われた。
で、host2.example.comを検索したら、192.168.1.2だと言うことが分かった。
で、192.168.1.2に2.1/255.168.192.in-addr.arpaのPTRを教えて、と言ったら、それはhost2.example.comが知っている(NSだ)と言われた。ちなみにPTRレコードは返してこない。
状況としては、192.168.1.2については、host2が知っている。しかし、host2はhost2が知っているよ、という情報しか教えてくれない。こりゃDNSひけないわけだ。
長門チックには、キョンの名前(この時点では長門はまだ「キョン」という名前であることを知らない)を聞こうと思ったら、古泉が知っていると言われた。古泉に聞きに言ったら、キョンの管理番号を持っている人に聞けと言われた(くどいようだが、この時点では長門はまだ「キョン」という名前であることを知らない)。キョンの管理番号を持っている人に、キョンの名前を教えてと言ったら、「俺が知っている」と意地悪された。そりゃわからん。
で、DNSの設定をなおしたら、ちゃんと普通にDNSもひけて、遅いのもなおりました。
キョンに「意地悪やめ」とハルヒあたりから突っ込まれたんだろうな。「はい、私がキョンです」と教えてくれるようになって、無事長門さんの仕事が滞り無く進むようになった、と。
で、local host(くどいが、localhostではない)のIPを毎回聞きに行くのは無駄以外の何物でもない*2ので、-l servernameとしておくと、トラフィックも改善されて妙なところではまらなくなるんじゃないかなぁ、と。このオプションがないと、DNSの逆引きが狂ったら(今回みたいにループしたら。NXDOMAINな分にはかまわないハズ)システムが遅くなるからね。
長門的に? 「私は、キョンの下で動いている」って最初に言っておけば妙な意地悪された時に困らないジャン、と。
長くなりましたが、こんな感じでした。ビックリです。そりゃ、万能長門さんだって困ることでしょう。
原因追及よりこの文章を書く方が時間かかっちゃったよ……
Hi my name is Emily and I just wanted to send you a quick note here instead of calling you. I discovered your いつきログ(2008-10-21) website and noticed you could have a lot more hits. I have found that the key to running a successful website is making sure the visitors you are getting are interested in your subject matter. There is a company that you can get keyword targeted visitors from and they let you try their service for free for 7 days. I managed to get over 300 targeted visitors to day to my site. Visit them here: http://i7n.co/2ck97