Bug #6479

ipaddr.rbの受け付ける書式が、プラットフォームによって異なる

Added by Kenichi Kamiya almost 2 years ago. Updated about 1 year ago.

[ruby-dev:45670]
Status:Closed
Priority:Normal
Assignee:Akinori MUSHA
Category:lib
Target version:next minor
ruby -v:ruby 1.9.3p194 (2012-04-20 revision 35410) [i686-linux] Backport:

Description

状況

標準添付ライブラリの ipaddr.rb に於いて、アドレス書式チェック時の厳しさがプラットフォームによって異なるよう感じました。
IPv4で気がついた限りですが、次の2点でWindowsだと常に例外を吐き、Linuxだと書式によっては(自分にとって)想定し難い値を返します。

  • 0埋めを含んだ場合
  • 改行文字を含んだ場合

動作例を載せます。

共通

require 'ipaddr'

p IPAddr.new("11.22.33.45") #=> #<IPAddr: IPv4:11.22.33.45/255.255.255.255>

Windows(7) / ruby 1.9.3p194 (2012-04-20) [i386-mingw32]

# 以下全てで、同じ例外を吐きます。
p IPAddr.new("011.22.33.45")
p IPAddr.new("011.0022.00033.000045")
p IPAddr.new("011.0022.00033.000045\n")
p IPAddr.new("011.0022.00033.000045\nfoo32/0024/bar \n/foobar  ")
p IPAddr.new("011.0022.00033.000045\n056.0067.00078.00089\nfoo32/0024/bar \n/foobar  ")

# 例外
ArgumentError: invalid address
from C:/Ruby193/lib/ruby/1.9.1/ipaddr.rb:496:in `rescue in initialize'

Linux(Mint12) / ruby 1.9.3p194 (2012-04-20 revision 35410) [i686-linux]

# Windowsの際と同じ引数に対して、一つも例外を吐きません
p IPAddr.new("011.22.33.45")                                                            #=> #<IPAddr: IPv4:11.22.33.45/255.255.255.255>
p IPAddr.new("011.0022.00033.000045")                                                   #=> #<IPAddr: IPv4:11.22.33.45/255.255.255.255>
p IPAddr.new("011.0022.00033.000045\n")                                                 #=> #<IPAddr: IPv4:11.22.33.45/255.255.255.255>
p IPAddr.new("011.0022.00033.000045\nfoo32/0024/bar \n/foobar  ")                       #=> #<IPAddr: IPv4:11.22.33.0/255.255.255.0>
p IPAddr.new("011.0022.00033.000045\n056.0067.00078.00089\nfoo32/0024/bar \n/foobar  ") #=> #<IPAddr: IPv4:45.67.78.0/255.255.255.0>

希望

Windows環境下の例にみるよう、上記全てのパターンで例外を吐いてくれる方が自然に感じました。
特にLinux上での0埋めに関しては、桁数に制限が見られ無い点と、IPSocket.getaddress(8進数?)と返す値の異なってしまう点が気になります。

Linux(Mint12) / ruby 1.9.3p194 (2012-04-20 revision 35410) [i686-linux]

require 'socket'

p IPSocket.getaddress("011.0022.00033.000045") #=> "9.18.27.37"

参考

ipaddr.rbを自分なりに読んでみた限り、次の要素に起因するのではないかとあたりをつけてみましたが・・・確信はありません。

  • 全体的に、チェック用正規表現のメタ文字へ \A\z を使わず、 \A\Z や $ で括っていること
  • #initialize で、 IPSocket.getaddress が例外を返すかどうかに、正常異常の判断を大きく委ねていること
  • #in_addr で、1つのオクテットを \d+ のみでキャプチャしていること

とりあえず、#in_addrのみを次のように上書きした際、Windowsと同様に動作することまでは確認できました。

require 'ipaddr'

class IPAddr

  private

  remove_method :in_addr

  def in_addr(addr)
    if addr =~ /\A(?:(?:0|[1-9]\d{0,2})\.){3}(?:0|[1-9]\d{0,2})\z/  
      return addr.split('.').inject(0) { |i, s|
        n = s.to_i
        raise ArgumentError, 'invalid address' unless n <= 255

        i << 8 | n
      }
    end
    return nil
  end

end

noname (205 Bytes) Anonymous, 05/25/2012 02:23 PM

History

#1 Updated by Akinori MUSHA almost 2 years ago

  • Status changed from Open to Assigned
  • Assignee set to Akinori MUSHA

#2 Updated by Anonymous almost 2 years ago

At Wed, 23 May 2012 17:23:23 +0900,
U.Nakamura wrote:

In message " [ruby-trunk - Bug #6479][Assigned] ipaddr.rbの受け付ける書式が、プラットフォームによって異なる"
on May.23,2012 16:16:40, knu@ruby-lang.org wrote:

とりあえず、#in_addrのみを次のように上書きした際、Windowsと同様に動作することまでは確認できました。

Socket::AF_INET6がない場合にIPSocket.getaddressを上書きしてい
るコードがipaddr.rbの冒頭にあると思いますので、プラットフォー
ム提供のそれが信用できないならこっちを常に有効にするのもあり
かもしれません。
Windowsだとたぶんそれを使ってるんだと思います。

IPAddr.new における IPSocket.getaddress の呼び出しですが、推測するに、
Socket.getaddrinfo(..., Socket::AI_NUMERICHOST) が意図通り動いてくれな
かったので IPSocket.getaddress に書き換えたが、想定(socket のIPv6対応
は完了していたので…)に反してこいつのIPv6アドレス対応がプラットフォー
ム依存だったということではないかと思います。

いずれにしても、 IPAddr レベルでのプラットフォーム依存性は望ましくない
ので、これを呼ぶ前に検査および正規化(ゼロ埋め排除等)するようにします。

なお、 IPSocket.valid*? 条件付きで表に出るのは変なのでここは private に
し、 IPSocket.getaddress の上書きについても AF_INET6 がない場合の
IPv6アドレス対応の付加程度に止めたいと思います。

--
Akinori MUSHA / http://akinori.org/

#3 Updated by Akinori MUSHA almost 2 years ago

本件の実装の大枠はもうできていて、あとはどういう表記を認めるかというところで諸方面に確認中ですが、ゼロ埋めについては不許可にしたいと思うようになりました。

というのもアドレスのパースに用いられるCの inet_addr() の仕様を見ると、ドット区切りの各オクテットが0で始まるときは8進数として扱うべしとあり、一方でゼロ埋めの十進3桁固定方式も入力フォーム等で見られるため、ライブラリ側ではいずれとも判断しかねるからです。

cf. http://pubs.opengroup.org/onlinepubs/009696799/functions/inet_addr.html

従来もWindowsではエラーになっていたことですし、ここで禁止とするのもありかなと思います。

なお、あとは x:x:x:x:x:x:d.d.d.d 形式で今までは意図せずエラーとなっていた部分について仕様を詰めており、これが詰まり次第commitおよびbackportする予定です。もうしばらくお待ちください。

#4 Updated by Kenichi Kamiya almost 2 years ago

武者さん、なかむら(う)さん

お二方とも、御返信下さり有難う御座います。

cf. http://pubs.opengroup.org/onlinepubs/009696799/functions/inet_addr.html

0埋め時のC側に於ける解釈は、8進数にする旨明記されていたのですね
由来が把握できて、すっきりしました。

なお、あとは x:x:x:x:x:x:d.d.d.d 形式で今までは意図せずエラーとなっていた部分について仕様を詰めており、これが詰まり次第commitおよびbackportする予定です。もうしばらくお待ちください。

御丁寧に有難うございます。
手元の ipaddr.rb を利用しているコード内で問題が表面化している/いそうなわけでもないので、気長に待つつもりでした。
お手すきの際でも結構ですので、引き続き御検討頂けると嬉しいです。

#5 Updated by Yui NARUSE about 1 year ago

  • Target version changed from 1.9.3 to next minor

#6 Updated by Akinori MUSHA about 1 year ago

  • Status changed from Assigned to Closed

本件を修正して様子を見るうちに2.0.0が正式リリースとなったので、すみませんが旧シリーズとなった1.9ではこのままとしたいと思います。
報告ありがとうございました。

Also available in: Atom PDF