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/