[前][次][番号順一覧][スレッド一覧]

ruby-changes:10155

From: akr <ko1@a...>
Date: Tue, 20 Jan 2009 20:20:59 +0900 (JST)
Subject: [ruby-changes:10155] Ruby:r21699 (trunk): * ext/socket/socket.c (socket_s_list_ip_address): new method.

akr	2009-01-20 20:19:09 +0900 (Tue, 20 Jan 2009)

  New Revision: 21699

  http://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=rev&revision=21699

  Log:
    * ext/socket/socket.c (socket_s_list_ip_address): new method.
      (sockaddr_obj): new function.
    * ext/socket/rubysocket.h: include ifaddrs.h, sys/ioctl.h,
      sys/sockio.h, net/if.h if available.
      (addrinfo_new): declared.
    
    * ext/socket/raddrinfo.c (addrinfo_new): exported.
    
    * ext/socket/extconf.rb: check ifaddrs.h, sys/ioctl.h, sys/sockio.h,
      net/if.h and getifaddrs.

  Modified files:
    trunk/ChangeLog
    trunk/ext/socket/extconf.rb
    trunk/ext/socket/raddrinfo.c
    trunk/ext/socket/rubysocket.h
    trunk/ext/socket/socket.c
    trunk/test/socket/test_socket.rb

Index: ChangeLog
===================================================================
--- ChangeLog	(revision 21698)
+++ ChangeLog	(revision 21699)
@@ -1,3 +1,17 @@
+Tue Jan 20 20:16:21 2009  Tanaka Akira  <akr@f...>
+
+	* ext/socket/socket.c (socket_s_list_ip_address): new method.
+	  (sockaddr_obj): new function.
+
+	* ext/socket/rubysocket.h: include ifaddrs.h, sys/ioctl.h,
+	  sys/sockio.h, net/if.h if available.
+	  (addrinfo_new): declared.
+
+	* ext/socket/raddrinfo.c (addrinfo_new): exported.
+
+	* ext/socket/extconf.rb: check ifaddrs.h, sys/ioctl.h, sys/sockio.h,
+	  net/if.h and getifaddrs.
+
 Tue Jan 20 20:05:21 2009  Tanaka Akira  <akr@f...>
 
 	* ext/socket/rubysocket.h (pseudo_AF_FTIP): moved from mkconstants.rb.
Index: ext/socket/rubysocket.h
===================================================================
--- ext/socket/rubysocket.h	(revision 21698)
+++ ext/socket/rubysocket.h	(revision 21699)
@@ -60,6 +60,19 @@
 #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
@@ -184,6 +197,8 @@
 VALUE fd_socket_addrinfo(int fd, struct sockaddr *addr, socklen_t len);
 VALUE io_socket_addrinfo(VALUE io, struct sockaddr *addr, socklen_t len);
 
+VALUE addrinfo_new(struct sockaddr *addr, socklen_t len, int family, int socktype, int protocol, VALUE canonname, VALUE inspectname);
+
 VALUE make_ipaddr(struct sockaddr *addr);
 VALUE ipaddr(struct sockaddr *sockaddr, int norevlookup);
 VALUE make_hostent(VALUE host, struct addrinfo *addr, VALUE (*ipaddr)(struct sockaddr *, size_t));
Index: ext/socket/raddrinfo.c
===================================================================
--- ext/socket/raddrinfo.c	(revision 21698)
+++ ext/socket/raddrinfo.c	(revision 21699)
@@ -550,7 +550,7 @@
     rai->inspectname = inspectname;
 }
 
-static VALUE
+VALUE
 addrinfo_new(struct sockaddr *addr, socklen_t len,
              int family, int socktype, int protocol,
              VALUE canonname, VALUE inspectname)
Index: ext/socket/extconf.rb
===================================================================
--- ext/socket/extconf.rb	(revision 21698)
+++ ext/socket/extconf.rb	(revision 21699)
@@ -285,6 +285,12 @@
   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: ext/socket/socket.c
===================================================================
--- ext/socket/socket.c	(revision 21698)
+++ ext/socket/socket.c	(revision 21699)
@@ -1386,7 +1386,238 @@
 }
 #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) {
+      case AF_INET:
+        len = sizeof(struct sockaddr_in);
+        break;
+
+#ifdef AF_INET6
+      case AF_INET6:
+        len = sizeof(struct sockaddr_in6);
+        break;
+#endif
+
+#ifdef HAVE_SYS_UN_H
+      case AF_UNIX:
+        len = sizeof(struct sockaddr_un);
+        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:
+ *   Socket.list_ip_address => array
+ *
+ * Returns local IP addresses as an array.
+ *
+ * The array contains AddrInfo objects.
+ *
+ *  pp Socket.list_ip_address
+ *  #=> [#<AddrInfo: 127.0.0.1>,
+ *       #<AddrInfo: 192.168.0.128>,
+ *       #<AddrInfo: ::1>,
+ *       ...]
+ *
+ */
+static VALUE
+socket_s_list_ip_address(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 (p->ifa_addr != NULL && 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) && !defined(__hpux)
+    /* Solaris if_tcp(7P) */
+    /* HP-UX has SIOCGLIFCONF too.  But it uses differenet struct */
+    int fd = -1;
+    int ret;
+    struct lifnum ln;
+    struct lifconf lc;
+    char *reason = NULL;
+    int save_errno;
+    int i;
+    VALUE list = Qnil;
+
+    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
+}
+
+/*
  * Class +Socket+ provides access to the underlying operating system
  * socket implementations. It can be used to provide more operating system
  * specific functionality than the protocol-specific socket classes but at the
@@ -1449,4 +1680,6 @@
     rb_define_singleton_method(rb_cSocket, "pack_sockaddr_un", sock_s_pack_sockaddr_un, 1);
     rb_define_singleton_method(rb_cSocket, "unpack_sockaddr_un", sock_s_unpack_sockaddr_un, 1);
 #endif
+
+    rb_define_singleton_method(rb_cSocket, "list_ip_address", socket_s_list_ip_address, 0);
 }
Index: test/socket/test_socket.rb
===================================================================
--- test/socket/test_socket.rb	(revision 21698)
+++ test/socket/test_socket.rb	(revision 21699)
@@ -54,4 +54,16 @@
   def test_getnameinfo
     assert_raise(SocketError) { Socket.getnameinfo(["AF_UNIX", 80, "0.0.0.0"]) }
   end
+
+  def test_list_ip_address
+    begin
+      list = Socket.list_ip_address
+    rescue NotImplementedError
+      return
+    end
+    list.each {|ai|
+      assert_instance_of(AddrInfo, ai)
+      assert(ai.ip?)
+    }
+  end
 end if defined?(Socket)

--
ML: ruby-changes@q...
Info: http://www.atdot.net/~ko1/quickml/

[前][次][番号順一覧][スレッド一覧]