ryhmrt’s blog

フィリピンから日本に戻った意識低い系プログラマの雑記

JCIFSで複数NICが刺さっているサーバに繋がらないことがある

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

でも、どうしても必要だったらJNI経由って手がある。 Linuxの例Windowsの例 を見つけた。