"unknown service", MSNメッセンジャー7.5の診断ツールには、「気をつけろ!」
仕事の関係のwebページについて、Meadowで編集してcygwinのlftpでアップしているが、ある日突然lftpを起動したらsocketをopenできないというエラーが表示された。おかしいと思い、色々試してみたらlftpだけでなくftpもtelnetも"unknown service"と言われて使えない。しかし、WindowsXPのffftpやDOS窓のftp.exeや、cygwin上のapacheは動作する、というわけのわからないトラブルに遭遇した。(そのときの日記)
状況をまとめておくと
- cygwinからソケットを使うプログラムが動作しない
- apache等のように設定ファイルでポート番号を指定するものは問題なく動作しているし、コマンドラインでポート番号を指定すると動作する
- 何故か"%SystemRoot%\System32\drivers\etc\services"が参照できなくなってしまっている(perlやCなどでgetservbynameするプログラムを書くと確認できる)
この状況に対して、以下のような対処をしたが無効だった
- %SystemRoot%\System32\drivers\etc\servicesのファイルを改行コードを変えて編集しなおしたりする
- %SystemRoot%\System32\drivers\etc\servicesのファイルはcygwinではシンボリックリンクとなっているが、実体をコピーしてみる
- %SystemRoot%\System32\drivers\etc\servicesのファイルの内容を編集して、ftp等を先頭に持っていってみる
- cygwinを再インストールしてみる
情報を求めて世界中のwebやblogを探したが、解決策は見つけられなかった。しかし、同じ症状を経験している人が国内に数名存在していた。
ここにメッセージを残しておいたところ、りばにしさんより11/5にコメントをいただいた。それによると犯人はcygwinではなくMSNメッセンジャーで、それも「診断ツール」だった。
上のリンクに書かれている内容に従い解決した。(T T)
MSNメッセンジャー7.5の診断ツールを実行すると、「"マイ コンピュータ\HKEY_LOCAL_MACHINES\SYSTEM\CurrentControlSet\Services\Tcpip\Parametersに存在する"DataBasePath"という変数("services"の置かれている場所を格納している)の型」が、本来「REG_EXPAND_SZ(展開可能な文字列値)」でなければならないのが「REG_SZ(文字列値)」に変更されてしまうらしい(極悪!!!)。その結果、getservbynameはサービス名からポート番号を調べることができず、getservbynameを呼び出したアプリケーションがサーバへの接続を確立できずエラーとなる。
もし、同じ症状でこのページに飛んでこられた方は以下の手順をおためしください。
- レジストリエディタを起動する
- マイ コンピュータ\HKEY_LOCAL_MACHINES\SYSTEM\CurrentControlSet\Services\Tcpip\Parametersをたどる
- DataBasePathという変数が存在し、型がREG_SZとなっていることを確認する
- DataBasePathの変数の値(多くの場合は"%SystemRoot%\System32\drivers\etc"のようになっているはず)をコピーしておく
- 同じ場所にREG_EXPAND_SZ(展開可能な文字列値)という型の変数を新規作成する
- 作成した変数の値にコピーしておいた値を貼り付ける
- DataBasePath(REG_SZ)を削除する
- 新規に作成した変数は「新しい変数 #1」という名前になっているはずなので、DataBasePathに名前を変更する
この問題を解決してくれたりばにしさん(river24)に大感謝!
参考
推測だが、おそらくREG_EXPAND_SZとREG_SZの違いは%SystemRoot%を評価してC:\Windowsに展開してくれるかどうかの違いなのだろう。"messenger reg_expand_sz"のように検索すると上記のような情報が見つかるが、問題は症状からそのキーワードが思いつかない点にあるし、感染していても自覚しにくい点もやっかいだ。今回の件でわかったのだが、cygwin環境では/etc/servicesはWindowsのservicesへのシンボリックリンクにしているが、実際にはそのファイルを参照していない(より正確には、cygwinのgetservbynameライブラリは/etc/servicesを参照していない)。仕様と言ってしまえばそれまでだが、本来/etc/servicesを参照すべきだと思う。