提案なんですが、ホストが持っている IP アドレスのリストを返す
メソッドを用意するのはどうでしょうか。

% ./ruby -rsocket -rpp -e 'pp AddrInfo.list_ipaddr'
[#<AddrInfo: 127.0.0.1>,
 #<AddrInfo: 150.82.175.199>,
 #<AddrInfo: ::1>,
 #<AddrInfo: fe80::20b:97ff:fe2b:8539%eth0>]

これがなぜ必要かというと、まず、IPv6 が利用できないホストを
判断したいからです。上記のリストでは ::1 と
fe80::20b:97ff:fe2b:8539%eth0 というふたつの IPv6 アドレスが
ありますが、これらは loopback address と link local address
というもので、グローバルには使えないものです。この判断を行う
ことにより、resolv.rb で使えもしない IPv6 アドレスを問い合わ
せたり返したりすることを防ぐことができます。

また、IP アドレスのリストは UDP でサーバを作る場合にも重要で
す。UDP のサーバは到着したパケットの宛先から返事を送ることが
望まれます。[RFC 1123] まぁ、送ったところ以外から返すと、ク
ライアントは関係ないところから (あるいは危ないひとから) 来た
パケットだと思って無視するかもしれないわけです。

外につながっている IP アドレスがひとつであれば問題にはならな
いんですが、複数の IP アドレスを持つマルチホームな環境で問題
になります。

では、パケットを受け取るときに宛先を得れば、と思うわけですが、
残念なことに recvfrom では宛先は得られません。recvfrom で得
られるアドレスはパケットの送信元であって宛先ではないからです。
また、同様に sendto でもサーバ側での送信元を指定することはで
きません。

これを解決するひとつの方法は、ソケットをあらかじめ各アドレス
ごとにつくって bind しておくというものですが、そこで IP アド
レスのリストが必要になります。

それ以外の方法としては、IPv6 では IPV6_RECVPKTINFO とか
setsockopt すると recvmsg/sendmsg で可能なようです。[RFC 3542]
が、IPv4 では標準化されたあるいは広く使えるものはないようで
す。(とくに送信元を指定する方法が)

この要求は過去にも何回があがっています。
[ruby-talk:162689], [ruby-talk:260514], [ruby-talk:324043]

たいてい、Socket.gethostname の結果を逆引きする、という話に
なるんですが、これは /etc/hosts や DNS に依存するなど、確実
に使えるというものではありません。やはりネットワークインター
フェースの情報が欲しければ、 インターフェース自身に尋ねるべ
きだと思います。

実際に尋ねる方法は、残念なことに IP アドレスのリストを得る方
法は標準化されていないようなので、以下を実装してあります。

* getifaddrs:
  BSD/OS, FreeBSD, NetBSD, OpenBSD, DragonFly BSD, MirOS BSD,
  GNU/Linux, MacOS X, AIX
* SIOCGLIFCONF: Solaris
* SIOCGIFCONF: 4.3BSD

どうやら、getifaddrs は普及が進んでいるようです。

4.3BSD の SIOCGIFCONF を使うものを実装してあるので、そこに由
来するものはだいたい動くんじゃないかと思います。ただ、その場
合は IPv4 限定になりますが。
(なお、4.4BSD の SIOCGIFCONF は拡張されて IPv6 も扱えて、い
ちおうその場合も動くようにしてありますが、4.4BSD で
getifaddrs がない環境は残っていないように思えます。)

なお、ここで抜けているものとして HP-UX がありますが、これに
は SIOCGLIFCONF があるようです。ただし Solaris とは構造体が
違うようで、テスト環境がないので実装していません。実機があれ
ばそんなに難しくないと思います。

というわけでどうでしょうか。

% svn diff --diff-cmd diff -x '-u -p'
Index: ext/socket/socket.c
===================================================================
--- ext/socket/socket.c	(revision 21486)
+++ ext/socket/socket.c	(working copy)
@@ -67,6 +67,20 @@
 #include <fcntl.h>
 #endif
 #endif
+
+#ifdef HAVE_IFADDRS_H
+#include <ifaddrs.h>
+#endif
+#ifdef HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
+#ifdef HAVE_SYS_SOCKIO_H
+#include <sys/sockio.h>
+#endif
+#ifdef HAVE_NET_IF_H
+#include <net/if.h>
+#endif
+
 #ifndef EWOULDBLOCK
 #define EWOULDBLOCK EAGAIN
 #endif
@@ -5403,6 +5417,235 @@ addrinfo_s_unix(VALUE self, VALUE path)
 
 #endif
 
+#if defined(HAVE_GETIFADDRS) || defined(SIOCGLIFCONF) || defined(SIOCGIFCONF)
+static VALUE
+sockaddr_obj(struct sockaddr *addr)
+{
+    socklen_t len;
+
+    if (addr == NULL)
+        return Qnil;
+
+    switch (addr->sa_family) {
+#ifdef HAVE_SYS_UN_H
+      case AF_UNIX:
+        len = sizeof(struct sockaddr_un);
+        break;
+#endif
+
+      case AF_INET:
+        len = sizeof(struct sockaddr_in);
+        break;
+
+#ifdef AF_INET6
+      case AF_INET6:
+        len = sizeof(struct sockaddr_in6);
+        break;
+#endif
+
+      default:
+        len = sizeof(struct sockaddr_in);
+        break;
+    }
+#ifdef SA_LEN
+    if (len < SA_LEN(addr))
+	len = SA_LEN(addr);
+#endif
+
+    return addrinfo_new(addr, len, 0, 0, 0, Qnil, Qnil);
+}
+#endif
+
+/*
+ * call-seq:
+ *   AddrInfo.list_ipaddr => array
+ *
+ * Returns local IP addresses as an array.
+ *
+ * The array contains AddrInfo objects.
+ *
+ *  pp AddrInfo.list_ipaddr
+ *  #=> [#<AddrInfo: 127.0.0.1>,
+ *       #<AddrInfo: 192.168.0.128>,
+ *       #<AddrInfo: ::1>,
+ *       ...]
+ *
+ */
+static VALUE
+addrinfo_s_list_ipaddr(VALUE self)
+{
+#if defined(HAVE_GETIFADDRS)
+    struct ifaddrs *ifp = NULL;
+    struct ifaddrs *p;
+    int ret;
+    VALUE list;
+
+    ret = getifaddrs(&ifp);
+    if (ret == -1) {
+        rb_sys_fail("getifaddrs");
+    }
+
+    list = rb_ary_new();
+    for (p = ifp; p; p = p->ifa_next) {
+        if (IS_IP_FAMILY(p->ifa_addr->sa_family)) {
+            rb_ary_push(list, sockaddr_obj(p->ifa_addr));
+        }
+    }
+
+    freeifaddrs(ifp);
+
+    return list;
+#elif defined(SIOCGLIFCONF) && defined(SIOCGLIFNUM)
+    /* Solaris if_tcp(7P) */
+    int fd = -1;
+    int ret;
+    struct lifnum ln;
+    struct lifconf lc;
+    char *reason = NULL;
+    int save_errno;
+    int i;
+    VALUE list;
+
+    lc.lifc_buf = NULL;
+
+    fd = socket(AF_INET, SOCK_DGRAM, 0);
+    if (fd == -1)
+        rb_sys_fail("socket");
+
+    memset(&ln, 0, sizeof(ln));
+    ln.lifn_family = AF_UNSPEC;
+
+    ret = ioctl(fd, SIOCGLIFNUM, &ln);
+    if (ret == -1) {
+	reason = "SIOCGLIFNUM";
+	goto finish;
+    }
+
+    memset(&lc, 0, sizeof(lc));
+    lc.lifc_family = AF_UNSPEC;
+    lc.lifc_flags = 0;
+    lc.lifc_len = sizeof(struct lifreq) * ln.lifn_count;
+    lc.lifc_req = xmalloc(lc.lifc_len);
+
+    ret = ioctl(fd, SIOCGLIFCONF, &lc);
+    if (ret == -1) {
+	reason = "SIOCGLIFCONF";
+	goto finish;
+    }
+
+    close(fd);
+    fd = -1;
+
+    list = rb_ary_new();
+    for (i = 0; i < ln.lifn_count; i++) {
+	struct lifreq *req = &lc.lifc_req[i];
+        if (IS_IP_FAMILY(req->lifr_addr.ss_family)) {
+            rb_ary_push(list, sockaddr_obj((struct sockaddr *)&req->lifr_addr));
+        }
+    }
+
+  finish:
+    save_errno = errno;
+    if (lc.lifc_buf != NULL)
+	xfree(lc.lifc_req);
+    if (fd != -1)
+	close(fd);
+    errno = save_errno;
+
+    if (reason)
+	rb_sys_fail(reason);
+    return list;
+
+#elif defined(SIOCGIFCONF)
+    int fd = -1;
+    int ret;
+#define EXTRA_SPACE (sizeof(struct ifconf) + sizeof(struct sockaddr_storage))
+    char initbuf[4096+EXTRA_SPACE];
+    char *buf = initbuf;
+    int bufsize;
+    struct ifconf conf;
+    struct ifreq *req;
+    VALUE list = Qnil;
+    char *reason = NULL;
+    int save_errno;
+
+    fd = socket(AF_INET, SOCK_DGRAM, 0);
+    if (fd == -1)
+        rb_sys_fail("socket");
+
+    bufsize = sizeof(initbuf);
+    buf = initbuf;
+
+  retry:
+    conf.ifc_len = bufsize;
+    conf.ifc_req = (struct ifreq *)buf;
+
+    /* fprintf(stderr, "bufsize: %d\n", bufsize); */
+
+    ret = ioctl(fd, SIOCGIFCONF, &conf);
+    if (ret == -1) {
+        reason = "SIOCGIFCONF";
+        goto finish;
+    }
+
+    /* fprintf(stderr, "conf.ifc_len: %d\n", conf.ifc_len); */
+
+    if (bufsize - EXTRA_SPACE < conf.ifc_len) {
+	if (bufsize < conf.ifc_len) {
+	    /* NetBSD returns required size for all interfaces. */
+	    bufsize = conf.ifc_len + EXTRA_SPACE;
+	}
+	else {
+	    bufsize = bufsize << 1;
+	}
+	if (buf == initbuf)
+	    buf = NULL;
+	buf = xrealloc(buf, bufsize);
+	goto retry;
+    }
+
+    close(fd);
+    fd = -1;
+
+    list = rb_ary_new();
+    req = conf.ifc_req;
+    while ((char*)req < (char*)conf.ifc_req + conf.ifc_len) {
+	struct sockaddr *addr = &req->ifr_addr;
+        if (IS_IP_FAMILY(addr->sa_family)) {
+	    rb_ary_push(list, sockaddr_obj(addr));
+	}
+#ifdef HAVE_SA_LEN
+# ifndef _SIZEOF_ADDR_IFREQ
+#  define _SIZEOF_ADDR_IFREQ(r) (sizeof(struct ifreq) + \
+                                 (sizeof(struct sockaddr) < (r).ifr_addr.sa_len ? \
+				  (r).ifr_addr.sa_len - sizeof(struct sockaddr) : \
+				  0))
+# endif
+	req = (struct ifreq *)((char*)req + _SIZEOF_ADDR_IFREQ(*req));
+#else
+	req = (struct ifreq *)((char*)req + sizeof(struct ifreq));
+#endif
+    }
+
+  finish:
+
+    save_errno = errno;
+    if (buf != initbuf)
+        xfree(buf);
+    if (fd != -1)
+	close(fd);
+    errno = save_errno;
+
+    if (reason)
+	rb_sys_fail(reason);
+    return list;
+
+#undef EXTRA_SPACE
+#else
+    rb_notimplement();
+#endif
+}
+
 static VALUE
 sockaddr_string_value(volatile VALUE *v)
 {
@@ -5605,6 +5848,8 @@ Init_socket()
 
     rb_define_method(rb_cAddrInfo, "getnameinfo", addrinfo_getnameinfo, -1);
 
+    rb_define_singleton_method(rb_cAddrInfo, "list_ipaddr", addrinfo_s_list_ipaddr, 0);
+
     /* constants */
     mConst = rb_define_module_under(rb_cSocket, "Constants");
     init_constants(mConst);
Index: ext/socket/extconf.rb
===================================================================
--- ext/socket/extconf.rb	(revision 21486)
+++ ext/socket/extconf.rb	(working copy)
@@ -258,6 +258,12 @@ unless getaddr_info_ok and have_func("ge
   have_header("resolv.h")
 end
 
+have_header("ifaddrs.h")
+have_func("getifaddrs")
+have_header("sys/ioctl.h")
+have_header("sys/sockio.h")
+have_header("net/if.h")
+
 unless have_type("socklen_t", headers)
   $defs << "-Dsocklen_t=int"
 end
Index: test/socket/test_addrinfo.rb
===================================================================
--- test/socket/test_addrinfo.rb	(revision 21486)
+++ test/socket/test_addrinfo.rb	(working copy)
@@ -39,6 +39,17 @@ class TestSocketAddrInfo < Test::Unit::T
     assert_equal(0, ai.protocol)
   end
 
+  def test_list_ipaddr
+    begin
+      list = AddrInfo.list_ipaddr
+    rescue NotImplementedError
+      return
+    end
+    list.each {|ai|
+      assert_instance_of(AddrInfo, ai)
+    }
+  end
+
   def test_addrinfo_predicates
     ipv4_ai = AddrInfo.new(Socket.sockaddr_in(80, "192.168.0.1"))
     assert(ipv4_ai.ip?)
-- 
[田中 哲][たなか あきら][Tanaka Akira]