
最近ルータのWAN側のアドレス、プロバイダから受け取っているグローバルアドレスが頻繁に変わるようになってきた。 今までは、電源を落とすか、ルータをリセットしない限り、そのままだったのに...
サーバーの再インストールのついでに二年ほど前に書いたUPnPのプログラムをアドレスの監視のために動かすことにした。 ところが、動きが妙だ。 最近メモリー不良を起こしたVista機で散々苦労したので、また「わけわか」の再来かと一瞬いやーな気になった。
デバッグをしてみると、ルータが HTTP/1.1 401 Unauthorized エラーを返している、 まれにそのエラーの後に壊れた構造のHTML文書が流れてくる。 ルータが変わったので、何か内部のデータ構造に変更があったのかと、ルータ相手に、手動で各種の .xml 文書を要求し眺めてみる。 serviceType の WANPPPConnection も存在し、GetExternalIPAddress もサポートしているのは確認した。 しかしエラーは止まらない。 ヘッダーに基本認証情報を付け足したり、危険だけれど一時ルーターのパスワードを解除してみたりもした。 問題は変わらない。
Intel のサイトから、IntelToolsForUPnPTechnologyという,.NET用のライブラリをダウンロードし、その中にある Devise Spy を動かしてみた。 無事にGetExternalIPAddressは動作しているようだ。 では、どんなパケットを飛ばしているか?ということでパケットをキャプチャーしてみた。 それが、残念ながら、じぶんが飛ばしているものと殆ど変わらない。 ヘッダーの項目の順番が違うくらいだ。 単純なSOAPリクエストなので、当然誰が書いてもそんなに違いが出るものではない。
パターン1
<?xml version="1.0" encoding="utf-8"?>
<s:Envelope s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Body>
<u:GetExternalIPAddress xmlns:u="urn:schemas-upnp-org:service:WANPPPConnection:1" />
</s:Body>
</s:Envelope>
パターン2
<?xml version="1.0" encoding="utf-8"?>
<s:Envelope s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Body>
<u:GetExternalIPAddress xmlns:u="urn:schemas-upnp-org:service:WANPPPConnection:1" />
</s:Body>
</s:Envelope>
パターン1とパターン2の違いは何だろうか? そうインデントがあるか?ないか?の違いだけだ。
XML文書ではインデントは必須ではないと規定されている、インデントした方が人間が見た時に理解しやすいですよ、との解説が多い。
結論から言うと、このインデントが問題だったようだ。 ボクはプログラムに送りつけるXML構文なので、インデントを入れていなかった。 最初の<s:Body>の頭に一つでも空白を入れれば、ルーターは元気に正しいグローバルアドレスを戻してくる。
Intel のツールキットでは律儀に空白三文字分のインデントを入れているので動作したわけだ。
この問題の解決に、他の仕事をやりながらだが、ほぼ12時間を費やした。 どうしても頑固なルーターの挙動に、一時は取替えを決心したほどだ。 しかし、いかにもその解決策はプログラマとして悔しい。 と言っても、半日もかけるのもなぁ...(笑) 今回はルーターのファームウエアのルール違反だね。 メーカー名と型番は言わないこととする。
おかげで、
IntelToolsForUPnPTechnology を発見。 なかなか優れものなようなので、そのうちいじってみよう。 Microsoft の UPnP SDK も?