IPv6Addressを正規表現でマッチする。

より正しい正規表現を別なエントリとして記述しました。

以下の内容は、僕が自力で頑張った為、正規表現として不適切な部分があります。



IPv6アドレスのフォーマットが良く分からないので、正規表現で書き下す事で理解しようと試みます。
そこそこ良い感じに出来た様な気がしないでも無いのだけど、正しくない部分も相応にあります。
結論としては、大体こういう正規表現になりました。
どの正規表現もポート番号部分が無くてもひっかかります。

  • IPv4アドレスとポート番号がセットになっているものをマッチする。
(25[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3}(:\d{1,4})?
  • IPv6アドレスとポート番号がセットになっているものをマッチする。
(([0-9a-f]{1,4})(:([0-9a-f]{1,4})){7}((\.|#|p| port )\d{1,4})?)|\[([0-9a-f]{1,4})(:([0-9a-f]{1,4})){7}\]:\d{1,4}
  • IPv6アドレスの末尾がIPv4アドレスになっているものをマッチする。ポート番号については考慮しない
(([0-9a-f]{1,4}):){6}(25[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3}
  • 圧縮済みのIPv6アドレスとポート番号がセットになっているものをマッチする。
((([0-9a-f]{1,4})(:([0-9a-f]{1,4})){0,5})?::(([0-9a-f]{1,4})(:([0-9a-f]{1,4})){0,5})?((\.|#|p| port )\d{1,4})?)|\[(([0-9a-f]{1,4})(:([0-9a-f]{1,4})){0,5})?::(([0-9a-f]{1,4})(:([0-9a-f]{1,4})){0,5})?\]:\d{1,4}
  • 圧縮済みのIPv6アドレスの末尾がIPv4アドレスになっており更にポート番号がセットになっているものをマッチする。
((([0-9a-f]{1,4})(:([0-9a-f]{1,4})){0,5})?::(([0-9a-f]{1,4}):){0,5}(25[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3}((\.|#|p| port )\d{1,4})?)|\[(([0-9a-f]{1,4})(:([0-9a-f]{1,4})){0,5})?::(([0-9a-f]{1,4}):){0,5}(25[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3}\]:\d{1,4}


JUnit4で動作するテストコードをGistにアップしました。


制限事項と言うか、どうしても僕が正規表現で記述出来なかった部分があります。
僕の書いた正規表現では、例えばこういうアドレスもマッチしてしまいます。

2001:db8:aaaa:bbbb:cccc:dddd::1:03:ff:2b

圧縮されているので、こんなに沢山のフィールドを持っているのはおかしいですね。
これは、::より前と後ろに全く関連性が無いからおきています。
正規表現としてはこの辺ですね。

(([0-9a-f]{1,4})(:([0-9a-f]{1,4})){0,5})?::(([0-9a-f]{1,4})(:([0-9a-f]{1,4})){0,5})?
の手前と後で全く同じ記述がなされており、どちらも最大で5つのフィールドを持っていてもマッチしてしまいます。


僕の気持ちとしては、::の手前でマッチした数を::の後ろで数えつつ、合計が8越えたらマッチしない様にしたいのですけども、やり方がどうしても分かりませぬ。
そもそも、そういう考え方が間違っているのかもしれない、と思いつつここで力尽きました。


他にも実は気が付いていないけども、正しくないアドレスをマッチしてしまうのではないか?と言う疑念もあるにはありますが、正直に申し上げてもう分かりません。


という訳で正規表現に詳しい方、どなたかお助け下さい。


ついでに、IPv6アドレスにおいてポート番号を記述する際に:を使えるとある*1のですけども、これが正しく処理出来る理由が全く分からないので、マッチの対象としていません。

追記:

RFC5952 section4.3 では、アルファベット部分において全て小文字を使う様にとありますが、最初の正規表現ではどっちもOKにしており、明らかに正しくないので修正しました。


RFC5952 section6には、ポート番号を付記する際のサンプルとして、以下の6つのパターンを例示しています。

  1. [2001:db8::1]:80
  2. 2001:db8::1:80
  3. 2001:db8::1.80
  4. 2001:db8::1 port 80
  5. 2001:db8::1p80
  6. 2001:db8::1#80

僕が対応していないケースは、この2番目のものです。角括弧で括ってくれるケースは対応しています。
括弧書きもなしに:だけがポンと出てくるケースでは、機械的に評価するのは難しいのではないかなぁ…と思います。
この二番目のサンプルでは::でアドレスが圧縮されている事が分っているものの、
いくつのフィールドが圧縮されているか分らないので、
最後の80がポート番号なのか、アドレスのフィールドなのか評価しようが無いのでは無いか?と言うことです。


URIの一部として記述する際には、RFC3986にあるとおり、一番目の記述方法を使うべきであると書いてあります。


気になったので、一般的にポート番号付きのIPv6アドレスがどの様に記述されているのかもう少し調べてみました。

  • Apache2.0では角括弧付きで記述する様にマニュアルには記載があります。*2
  • BIND9では、#を使ってアドレスとポート番号をセットにしています。*3

追記:その2 IPv6 Address Validationがスゲェ。

jsで実装されているのだけども、特にテキスト表現としてもっとこうしたら良いってのを推奨してくれる機能までもが実装されています。

参考文献