HttpServletRequest#getRemoteAddr()とIPv6 その4

ローカルホスト上でIE7からTomcatにリクエストを投げるとIPv6で解決されたアドレスが使われるため、該当のメソッドで"::1"というアドレスが戻ることが判明したのは前回書いた通りだ。

IPv6が使われているコールスタック

new Inet6Address()
 [ネイティブ・メソッド ]
PlainSocketImpl.socketAccept(SocketImpl)
SocksSocketImpl(PlainSocketImpl).accept(SocketImpl)
ServerSocket.implAccept(Socket)
ServerSocket.accept()

JavaIPv6対応に関してだが、JSDK1.4の時点ですでに問題が発生することを認識しており、IPv4IPv6それぞれのスタックの優先度を切り替えるシステムプロパティを用意している。

IPv6 関連のシステムプロパティ - ネットワーク IPv6 ユーザガイド (J2SDK/JRE 1.4)

J2SE1.6 + Tomcat5.5.20上のInetAddressクラスのコードにおいては、システムパラメタjava.net.preferIPv6Addressはfalseであることを確認している。にも関わらずIPv6ループバックアドレス"::1"が戻るのはjzkey氏のコメントにもあるように、

IE7にはそのような考慮は必要ないので、getaddrinfoの返す順(おそらくは[::1, 127.0.0.1])をそのまま使う。
・先に使う::1でconnectが成功するため、java側のピアアドレスで::1が出現する

という理由だ。これは間違いないだろう。

では、Windows Vista + IE7のアドレスを使うケースであっても従来通りのループバックである"127.0.0.1"を返すようにするにはどうすれば良いのだろうか。
以前のエントリで書いた通りIPv6プロトコルスタックを無効にしても良いが、今度は他のアプリケーションに影響が出るとも限らないので、できればjavaの世界だけで対応したい所だ。

この場合、上記のリンクで紹介したもうひとつのJavaシステムプロパティであるjava.net.preferIPv4Stackを使う。

-Djava.net.preferIPv4Stack=true

Javaアプリケーションの起動時に、このシステムプロパティを指定することでTomcatにおけるコールスタックは以下のように変化する。

IPv4が使われるコールスタック

new Inet4Address()
 [ネイティブ・メソッド ]
PlainSocketImpl.socketAccept(SocketImpl)
SocksSocketImpl(PlainSocketImpl).accept(SocketImpl)
ServerSocket.implAccept(Socket)
ServerSocket.accept()

コールスタック中の[ネイティブ・メソッド]では 恐らくJVMのネィティブ実装"net.dll"内のファンクションで、

preferIPv4Stack = ((Boolean)java.security.AccessController.doPrivileged(new GetBooleanAction("java.net.preferIPv4Stack"))).booleanValue();

などと書かれており、その後Inet6AddressではなくInet4Addressのインスタンスを生成するのだと考えられる。(さすがにJNI配下まではトレースしていないので"考えられる"としか書け無いが)

このようにシステムのIPスタック側の処理に関わらず、JavaIPv4スタックの処理を優先することができるわけだ。
Windows Vista + IE7に関しては恐らくこのままの実装だと思われるため、今後Java側で何か変更が無い限りはIPv4優先にしておいた方が無難かもしれないが、LongHornServer(もはやこう呼べないが)でも同様の現象が発生するといろいろな所で問題になりそうだ。

最後に我々だが、そろそろIPアドレスの文字列表現が"."ドット区切りであることを前提にするのは止めた方が良い時期だろう。