From bd3cdcf3940d1647bf86a3cce1e770a8be31e091 Mon Sep 17 00:00:00 2001 From: akr Date: Tue, 20 Jan 2009 11:19:09 +0000 Subject: * 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. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@21699 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- ext/socket/extconf.rb | 6 ++ ext/socket/raddrinfo.c | 2 +- ext/socket/rubysocket.h | 15 ++++ ext/socket/socket.c | 233 ++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 255 insertions(+), 1 deletion(-) (limited to 'ext/socket') diff --git a/ext/socket/extconf.rb b/ext/socket/extconf.rb index 718aa1f119..24eb736af0 100644 --- a/ext/socket/extconf.rb +++ b/ext/socket/extconf.rb @@ -285,6 +285,12 @@ EOF 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 diff --git a/ext/socket/raddrinfo.c b/ext/socket/raddrinfo.c index 05cd2b2138..f5ea91af85 100644 --- a/ext/socket/raddrinfo.c +++ b/ext/socket/raddrinfo.c @@ -550,7 +550,7 @@ init_addrinfo(rb_addrinfo_t *rai, struct sockaddr *sa, size_t len, rai->inspectname = inspectname; } -static VALUE +VALUE addrinfo_new(struct sockaddr *addr, socklen_t len, int family, int socktype, int protocol, VALUE canonname, VALUE inspectname) diff --git a/ext/socket/rubysocket.h b/ext/socket/rubysocket.h index 6d4a70a95d..0ba473a216 100644 --- a/ext/socket/rubysocket.h +++ b/ext/socket/rubysocket.h @@ -60,6 +60,19 @@ #endif #endif +#ifdef HAVE_IFADDRS_H +#include +#endif +#ifdef HAVE_SYS_IOCTL_H +#include +#endif +#ifdef HAVE_SYS_SOCKIO_H +#include +#endif +#ifdef HAVE_NET_IF_H +#include +#endif + #ifndef EWOULDBLOCK #define EWOULDBLOCK EAGAIN #endif @@ -184,6 +197,8 @@ struct addrinfo* sock_getaddrinfo(VALUE host, VALUE port, struct addrinfo *hints 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)); diff --git a/ext/socket/socket.c b/ext/socket/socket.c index 6334dd2a8c..73d3a85cf0 100644 --- a/ext/socket/socket.c +++ b/ext/socket/socket.c @@ -1386,6 +1386,237 @@ sock_s_unpack_sockaddr_un(VALUE self, VALUE addr) } #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 + * #=> [#, + * #, + * #, + * ...] + * + */ +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 @@ -1449,4 +1680,6 @@ Init_socket() 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); } -- cgit v1.2.3