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を打つことができない。