Javaで作ったシステムからWindowsの共有ファイルを読み書きするために、JCIFSってライブラリを使っているのだけれど、昔こいつでハマッたときのメモを発掘した。
実はつい先日も同じ現象に遭遇したのである。
頑張って調べてたなぁ、昔の自分。
JCIFSは、コンピュータ名からIPアドレスを解決する際に、ブロードキャストでPC名の問い合わせをするのだけれど、その際に嫌なことが起こっていた。
ブロードキャストで名前を問い合わせた際に、相手が複数のネットワークインターフェースを持っていると、レスポンスとして複数のアドレスが帰っているようなのだが、JCIFSはその中で最後に記述してあるものを使っている模様。
通信をしようとしているPCにはNICが2枚刺さっていたのだけれども、問題が発生した環境では、接続元のコンピュータからアクセスできないIPアドレスに繋げようとしていたようだ。
設定で解決する
JCIFSは名前解決に何を使うかの優先順位を決定するのに、jcifs.resolveOrder
ってプロパティを参照している。
デフォルトで「LMHOSTS,BCAST,DNS」(WINSがあれば「LMHOSTS,WINS,BCAST,DNS」)になっているので、これをDNS優先に設定してみた。(今回の環境にはDNSサーバが存在するので)
ライブラリの変更で解決する
※ 2009年1月時点のコードに対する対応策
jcifs.netbios.NameServiceClient
クラスの
getByName( Name name, InetAddress addr )
メソッド
が怪しい。
メソッドの冒頭のループ中にある、以下の返却処理を変更。
int last = response.addrEntry.length - 1; response.addrEntry[last].hostName.srcHashCode = addr.hashCode(); return response.addrEntry[last];
以下のように書き換えてみた。
for (int j = 0; j < response.addrEntry.length; j++) { try { if (response.addrEntry[j].getInetAddress().isReachable(1000)) { response.addrEntry[j].hostName.srcHashCode = addr.hashCode(); if (LogStream.level > 3) log.println(response.addrEntry[j].getInetAddress() + " is reachable."); return response.addrEntry[j]; } if (LogStream.level > 3) log.println(response.addrEntry[j].getInetAddress() + " is not reachable."); } catch (IOException e) { if (LogStream.level > 3) e.printStackTrace(log); } } break;
何となくうまくいっている気がする。
でも、SMBプロトコルに関してろくな知識はないし、JCIFSのコードも把握しきっていないので、なんか勘違いしている可能性は捨てきれない。
InetAddress.isReachable()
を使っているので、J2SE5.0以降でないと動かないのも悩ましいところ。
1.4じゃPureJavaでICMPを打つことができない。