Ruby 2.1.0 に Socket.getifaddrs が登場
ネットワーク畑の人間からすれば、 Ruby 2.1.0 の目玉機能は間違いなく Socket.getifaddrs
だ。これがあれば、「インターフェイス名からそのインターフェイスの持っているIPアドレスを確認する」ことが簡単にできるようになる。
これまで
これまでは、 ifconfig や ip コマンドの結果から頑張って取ってこなくてはならなかった。
res = `ifconfig eth0`
res =~ /inet addr:(\d+\.\d+\.\d+\.\d+)/
addr = $1
できるけど気持ち悪いし、v6アドレスになると死ぬし、外部コマンド実行をするとエラー対応とかセキュリティとかが厄介な問題になる。
これから
Ruby 2.1.0 からは、 Socket.getifaddrs
メソッドが追加されている。これからは、Ruby に完結したコードでアドレスが取れるようになる。
拡張ライブラリ socket に Socket.getifaddrs というメソッドを新設しています。 これは getifaddrs(3) のラッパで、Socket::Ifaddr というネットワークインタフェースの情報を保持するクラスを追加してその配列を返すようにしています。これによりローカルアドレスとそのネットワークインタフェースの対応やネットマスク、Macアドレスの対応などがわかるようになります。 ifconfig の結果を parse してゴニョゴニョやってたりしたのが簡単にできるようになるんじゃないかと思います。
http://d.hatena.ne.jp/nagachika/20130511/ruby_trunk_changes_40634_40646
ということで、あ、やっぱり ifconfig ゴニョゴニョするの楽にしたいって話はあったんだ。すごく…嬉しいです…。
使い方
単純に出力を見てみる
require 'socket'
Socket.getifaddrs
#=> [#<Socket::Ifaddr lo UP,LOOPBACK,RUNNING,0x10000 PACKET[protocol=0 lo hatype=772 HOST hwaddr=00:00:00:00:00:00]>,
#<Socket::Ifaddr eth0 UP,BROADCAST,RUNNING,MULTICAST,0x10000 PACKET[protocol=0 eth0 hatype=1 HOST hwaddr=00:0c:29:XX:XX:XX] broadcast=PACKET[protocol=0 eth0 hatype=1 HOST hwaddr=ff:ff:ff:ff:ff:ff]>,
#<Socket::Ifaddr tun0 UP,POINTOPOINT,RUNNING,NOARP,MULTICAST,0x10000>,
#<Socket::Ifaddr lo UP,LOOPBACK,RUNNING,0x10000 127.0.0.1 netmask=255.0.0.0>,
#<Socket::Ifaddr eth0 UP,BROADCAST,RUNNING,MULTICAST,0x10000 192.168.106.129 netmask=255.255.255.0 broadcast=192.168.106.255>,
#<Socket::Ifaddr tun0 UP,POINTOPOINT,RUNNING,NOARP,MULTICAST,0x10000 10.111.0.2 netmask=255.255.255.255 dstaddr=10.111.0.1>,
#<Socket::Ifaddr lo UP,LOOPBACK,RUNNING,0x10000 ::1 netmask=ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff>,
#<Socket::Ifaddr eth0 UP,BROADCAST,RUNNING,MULTICAST,0x10000 fe80::XXXX:XXXX:XXXX:XXXX%eth0 netmask=ffff:ffff:ffff:ffff::>]
Socket.getifaddrs
は、 Socket::Ifaddr
オブジェクトの配列を返す。
インターフェイスに紐付くオブジェクトを取得する
返ってくるのが単なる Array
オブジェクトなので、普通に Array#select
で絞り込めばわかりやすい。
require 'socket'
Socket.getifaddrs.select{|x| x.name == "eth0"}
#=> [#<Socket::Ifaddr eth0 UP,BROADCAST,RUNNING,MULTICAST,0x10000 PACKET[protocol=0 eth0 hatype=1 HOST hwaddr=00:0c:29:XX:XX:XX] broadcast=PACKET[protocol=0 eth0 hatype=1 HOST hwaddr=ff:ff:ff:ff:ff:ff]>,
#<Socket::Ifaddr eth0 UP,BROADCAST,RUNNING,MULTICAST,0x10000 192.168.106.129 netmask=255.255.255.0 broadcast=192.168.106.255>,
#<Socket::Ifaddr eth0 UP,BROADCAST,RUNNING,MULTICAST,0x10000 fe80::XXXX:XXXX:XXXX:XXXX%eth0 netmask=ffff:ffff:ffff:ffff::>]
これで、 eth0
という名前のインターフェイスに紐付くアドレス情報がすべて手に入った。
欲しいアドレスファミリを指定する
IPv4 アドレスだけがほしいなら、条件をちょっと追加してやればいい。
require 'socket'
Socket.getifaddrs.select{|x| x.name == "eth0" and x.addr.ipv4?}
#=> [#<Socket::Ifaddr eth0 UP,BROADCAST,RUNNING,MULTICAST,0x10000 192.168.106.129 netmask=255.255.255.0 broadcast=192.168.106.255>]
上の x.addr
は Addrinfo
オブジェクトなので、ほかにもアドレスに関する各種関数が利用できる。マルチキャストか、プライベートか、といったあたりなら一発だ。
あるインターフェイスに紐付いた IPv4 アドレスを文字列として取得する
eth0
に紐付いている IPv4 アドレスの文字列が欲しい!と思ったときは、下のように書けばいいことになる。
require 'socket'
Socket.getifaddrs.select{|x|
x.name == "eth0" and x.addr.ipv4?
}.first.addr.ip_address
#=> "192.168.106.129"
意味するところがとっても明確。
とはいえ、上の例のメソッドチェーンは長い。メソッドチェーンが長いってことは、真面目に作るならエラー処理もたくさん必要ってこと。実際にコードに利用する際は気をつけて。
まとめ
ということで、 Ruby 2.1.0 からは Socket.getifaddrs
メソッドが使用できるようになった。
これまでは、アドレス情報を取ろうとしたら ifconfig からカリカリと取得しなければならなかったけれど、これからはそんなことをしなくてもよくなった。もっと構造化された枠内で、 Ruby らしいコードでアドレスを取得できる。幸せ。
akrさん、usaさんはじめ、コミッタのみなさんに感謝。
https://groups.google.com/forum/#!msg/ruby-core-google/BnOLZkp1CQk/zqqQ-F57RxgJ
http://magazine.rubyist.net/?0044-CRubyCommittersWhosWho2013