From b323d7d54c1f9d22799f636014a438f9da951f3f Mon Sep 17 00:00:00 2001 From: akr Date: Sat, 11 May 2013 08:32:26 +0000 Subject: * ext/socket: New method, Socket.getifaddrs, implemented. [ruby-core:54777] [Feature #8368] git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@40639 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- ext/socket/ifaddr.c | 434 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 434 insertions(+) create mode 100644 ext/socket/ifaddr.c (limited to 'ext/socket/ifaddr.c') diff --git a/ext/socket/ifaddr.c b/ext/socket/ifaddr.c new file mode 100644 index 0000000000..64aaf5a04a --- /dev/null +++ b/ext/socket/ifaddr.c @@ -0,0 +1,434 @@ +#include "rubysocket.h" + +#ifdef HAVE_GETIFADDRS +VALUE rb_cSockIfaddr; + +typedef struct rb_ifaddr_tag rb_ifaddr_t; +typedef struct rb_ifaddr_root_tag rb_ifaddr_root_t; + +struct rb_ifaddr_tag { + int ord; + struct ifaddrs *ifaddr; + rb_ifaddr_root_t *root; +}; + +struct rb_ifaddr_root_tag { + int refcount; + int numifaddrs; + rb_ifaddr_t ary[1]; +}; + +static rb_ifaddr_root_t * +get_root(const rb_ifaddr_t *ifaddr) +{ + return (rb_ifaddr_root_t *)((char *)&ifaddr[-ifaddr->ord] - + offsetof(rb_ifaddr_root_t, ary)); +} + +static void +ifaddr_mark(void *ptr) +{ +} + +static void +ifaddr_free(void *ptr) +{ + rb_ifaddr_t *ifaddr = ptr; + rb_ifaddr_root_t *root = get_root(ifaddr); + root->refcount--; + if (root->refcount == 0) { + freeifaddrs(root->ary[0].ifaddr); + xfree(root); + } +} + +static size_t +ifaddr_memsize(const void *ptr) +{ + const rb_ifaddr_t *ifaddr; + const rb_ifaddr_root_t *root; + if (ptr == NULL) + return 0; + ifaddr = ptr; + root = get_root(ifaddr); + return sizeof(rb_ifaddr_root_t) + (root->numifaddrs - 1) * sizeof(rb_ifaddr_t); +} + +static const rb_data_type_t ifaddr_type = { + "socket/ifaddr", + {ifaddr_mark, ifaddr_free, ifaddr_memsize,}, +}; + +#define IS_IFADDRS(obj) rb_typeddata_is_kind_of((obj), &ifaddr_type) +static inline rb_ifaddr_t * +check_ifaddr(VALUE self) +{ + return rb_check_typeddata(self, &ifaddr_type); +} + +static rb_ifaddr_t * +get_ifaddr(VALUE self) +{ + rb_ifaddr_t *rifaddr = check_ifaddr(self); + + if (!rifaddr) { + rb_raise(rb_eTypeError, "uninitialized ifaddr"); + } + return rifaddr; +} + +static VALUE +rsock_getifaddrs(void) +{ + int ret; + int numifaddrs, i; + struct ifaddrs *ifaddrs, *ifa; + rb_ifaddr_root_t *root; + VALUE result; + + ret = getifaddrs(&ifaddrs); + if (ret == -1) + rb_sys_fail("getifaddrs"); + + numifaddrs = 0; + for (ifa = ifaddrs; ifa != NULL; ifa = ifa->ifa_next) + numifaddrs++; + + root = xmalloc(sizeof(rb_ifaddr_root_t) + (numifaddrs-1) * sizeof(rb_ifaddr_t)); + root->refcount = root->numifaddrs = numifaddrs; + + ifa = ifaddrs; + for (i = 0; i < numifaddrs; i++) { + root->ary[i].ord = i; + root->ary[i].ifaddr = ifa; + root->ary[i].root = root; + ifa = ifa->ifa_next; + } + + result = rb_ary_new2(numifaddrs); + for (i = 0; i < numifaddrs; i++) { + rb_ary_push(result, TypedData_Wrap_Struct(rb_cSockIfaddr, &ifaddr_type, &root->ary[i])); + } + + return result; +} + +/* + * call-seq: + * getifaddr.name => string + * + * Returns the interface name of _getifaddr_. + */ + +static VALUE +ifaddr_name(VALUE self) +{ + rb_ifaddr_t *rifaddr = get_ifaddr(self); + struct ifaddrs *ifa = rifaddr->ifaddr; + return rb_str_new_cstr(ifa->ifa_name); +} + +/* + * call-seq: + * getifaddr.ifindex => integer + * + * Returns the interface index of _getifaddr_. + */ + +static VALUE +ifaddr_ifindex(VALUE self) +{ + rb_ifaddr_t *rifaddr = get_ifaddr(self); + struct ifaddrs *ifa = rifaddr->ifaddr; + unsigned int ifindex = if_nametoindex(ifa->ifa_name); + if (ifindex == 0) { + rb_raise(rb_eArgError, "invalid interface name: %s", ifa->ifa_name); + } + return UINT2NUM(ifindex); +} + +/* + * call-seq: + * getifaddr.flags => integer + * + * Returns the flags of _getifaddr_. + */ + +static VALUE +ifaddr_flags(VALUE self) +{ + rb_ifaddr_t *rifaddr = get_ifaddr(self); + struct ifaddrs *ifa = rifaddr->ifaddr; + return UINT2NUM(ifa->ifa_flags); +} + +/* + * call-seq: + * getifaddr.addr => addrinfo + * + * Returns the address of _getifaddr_. + * nil is returned if address is not available in _getifaddr_. + */ + +static VALUE +ifaddr_addr(VALUE self) +{ + rb_ifaddr_t *rifaddr = get_ifaddr(self); + struct ifaddrs *ifa = rifaddr->ifaddr; + if (ifa->ifa_addr) + return rsock_sockaddr_obj(ifa->ifa_addr, rsock_sockaddr_len(ifa->ifa_addr)); + return Qnil; +} + +/* + * call-seq: + * getifaddr.netmask => addrinfo + * + * Returns the netmask address of _getifaddr_. + * nil is returned if netmask is not available in _getifaddr_. + */ + +static VALUE +ifaddr_netmask(VALUE self) +{ + rb_ifaddr_t *rifaddr = get_ifaddr(self); + struct ifaddrs *ifa = rifaddr->ifaddr; + if (ifa->ifa_netmask) + return rsock_sockaddr_obj(ifa->ifa_netmask, rsock_sockaddr_len(ifa->ifa_netmask)); + return Qnil; +} + +/* + * call-seq: + * getifaddr.broadaddr => addrinfo + * + * Returns the broadcast address of _getifaddr_. + * nil is returned if the flags doesn't have IFF_BROADCAST. + */ + +static VALUE +ifaddr_broadaddr(VALUE self) +{ + rb_ifaddr_t *rifaddr = get_ifaddr(self); + struct ifaddrs *ifa = rifaddr->ifaddr; + if ((ifa->ifa_flags & IFF_BROADCAST) && ifa->ifa_broadaddr) + return rsock_sockaddr_obj(ifa->ifa_broadaddr, rsock_sockaddr_len(ifa->ifa_broadaddr)); + return Qnil; +} + +/* + * call-seq: + * getifaddr.dstaddr => addrinfo + * + * Returns the destination address of _getifaddr_. + * nil is returned if the flags doesn't have IFF_POINTOPOINT. + */ + +static VALUE +ifaddr_dstaddr(VALUE self) +{ + rb_ifaddr_t *rifaddr = get_ifaddr(self); + struct ifaddrs *ifa = rifaddr->ifaddr; + if ((ifa->ifa_flags & IFF_POINTOPOINT) && ifa->ifa_dstaddr) + return rsock_sockaddr_obj(ifa->ifa_dstaddr, rsock_sockaddr_len(ifa->ifa_dstaddr)); + return Qnil; +} + +static void +ifaddr_inspect_flags(unsigned int flags, VALUE result) +{ + const char *sep = " "; +#define INSPECT_BIT(bit, name) \ + if (flags & (bit)) { rb_str_catf(result, "%s" name, sep); flags &= ~(bit); sep = ","; } +#ifdef IFF_UP + INSPECT_BIT(IFF_UP, "UP") +#endif +#ifdef IFF_BROADCAST + INSPECT_BIT(IFF_BROADCAST, "BROADCAST") +#endif +#ifdef IFF_DEBUG + INSPECT_BIT(IFF_DEBUG, "DEBUG") +#endif +#ifdef IFF_LOOPBACK + INSPECT_BIT(IFF_LOOPBACK, "LOOPBACK") +#endif +#ifdef IFF_POINTOPOINT + INSPECT_BIT(IFF_POINTOPOINT, "POINTOPOINT") +#endif +#ifdef IFF_RUNNING + INSPECT_BIT(IFF_RUNNING, "RUNNING") +#endif +#ifdef IFF_NOARP + INSPECT_BIT(IFF_NOARP, "NOARP") +#endif +#ifdef IFF_PROMISC + INSPECT_BIT(IFF_PROMISC, "PROMISC") +#endif +#ifdef IFF_NOTRAILERS + INSPECT_BIT(IFF_NOTRAILERS, "NOTRAILERS") +#endif +#ifdef IFF_ALLMULTI + INSPECT_BIT(IFF_ALLMULTI, "ALLMULTI") +#endif +#ifdef IFF_MASTER + INSPECT_BIT(IFF_MASTER, "MASTER") +#endif +#ifdef IFF_SLAVE + INSPECT_BIT(IFF_SLAVE, "SLAVE") +#endif +#ifdef IFF_MULTICAST + INSPECT_BIT(IFF_MULTICAST, "MULTICAST") +#endif +#ifdef IFF_PORTSEL + INSPECT_BIT(IFF_PORTSEL, "PORTSEL") +#endif +#ifdef IFF_AUTOMEDIA + INSPECT_BIT(IFF_AUTOMEDIA, "AUTOMEDIA") +#endif +#ifdef IFF_DYNAMIC + INSPECT_BIT(IFF_DYNAMIC, "DYNAMIC") +#endif +#ifdef IFF_LOWER_UP + INSPECT_BIT(IFF_LOWER_UP, "LOWER_UP") +#endif +#ifdef IFF_DORMANT + INSPECT_BIT(IFF_DORMANT, "DORMANT") +#endif +#ifdef IFF_ECHO + INSPECT_BIT(IFF_ECHO, "ECHO") +#endif +#undef INSPECT_BIT + if (flags) { + rb_str_catf(result, "%s%#x", sep, flags); + } +} + +/* + * call-seq: + * getifaddr.inspect => string + * + * Returns a string to show contents of _getifaddr_. + */ + +static VALUE +ifaddr_inspect(VALUE self) +{ + rb_ifaddr_t *rifaddr = get_ifaddr(self); + struct ifaddrs *ifa; + VALUE result; + + ifa = rifaddr->ifaddr; + + result = rb_str_new_cstr("#<"); + + rb_str_append(result, rb_class_name(CLASS_OF(self))); + rb_str_cat2(result, " "); + rb_str_cat2(result, ifa->ifa_name); + + if (ifa->ifa_flags) + ifaddr_inspect_flags(ifa->ifa_flags, result); + + if (ifa->ifa_addr) { + rb_str_cat2(result, " "); + rsock_inspect_sockaddr(ifa->ifa_addr, + rsock_sockaddr_len(ifa->ifa_addr), + result); + } + if (ifa->ifa_netmask) { + rb_str_cat2(result, " netmask="); + rsock_inspect_sockaddr(ifa->ifa_netmask, + rsock_sockaddr_len(ifa->ifa_netmask), + result); + } + + if ((ifa->ifa_flags & IFF_BROADCAST) && ifa->ifa_broadaddr) { + rb_str_cat2(result, " broadcast="); + rsock_inspect_sockaddr(ifa->ifa_broadaddr, + rsock_sockaddr_len(ifa->ifa_broadaddr), + result); + } + + if ((ifa->ifa_flags & IFF_POINTOPOINT) && ifa->ifa_dstaddr) { + rb_str_cat2(result, " dstaddr="); + rsock_inspect_sockaddr(ifa->ifa_dstaddr, + rsock_sockaddr_len(ifa->ifa_dstaddr), + result); + } + + rb_str_cat2(result, ">"); + return result; +} +#endif + +#ifdef HAVE_GETIFADDRS +/* + * call-seq: + * Socket.getifaddrs => [ifaddr1, ...] + * + * Returns an array of interface addresses. + * An element of the array is an instance of Socket::Ifaddr. + * + * This method can be used to find multicast-enabled interfaces: + * + * pp Socket.getifaddrs.reject {|ifaddr| + * !ifaddr.addr.ip? || (ifaddr.flags & Socket::IFF_MULTICAST == 0) + * }.map {|ifaddr| [ifaddr.name, ifaddr.ifindex, ifaddr.addr] } + * #=> [["eth0", 2, #], + * # ["eth0", 2, #]] + * + * Example result on GNU/Linux: + * pp Socket.getifaddrs + * #=> [#, + * # #, + * # #, + * # #, + * # #, + * # #, + * # #] + * + * Example result on FreeBSD: + * pp Socket.getifaddrs + * #=> [#, + * # #, + * # #, + * # #, + * # #, + * # #, + * # #, + * # #, + * # #, + * # #] + * + */ + +static VALUE +socket_s_getifaddrs(VALUE self) +{ + return rsock_getifaddrs(); +} +#else +#define socket_s_getifaddrs rb_f_notimplement +#endif + +void +rsock_init_sockifaddr(void) +{ +#ifdef HAVE_GETIFADDRS + /* + * Document-class: Socket::Ifaddr + * + * Socket::Ifaddr represents a result of getifaddrs() function. + */ + rb_cSockIfaddr = rb_define_class_under(rb_cSocket, "Ifaddr", rb_cData); + rb_define_method(rb_cSockIfaddr, "inspect", ifaddr_inspect, 0); + rb_define_method(rb_cSockIfaddr, "name", ifaddr_name, 0); + rb_define_method(rb_cSockIfaddr, "ifindex", ifaddr_ifindex, 0); + rb_define_method(rb_cSockIfaddr, "flags", ifaddr_flags, 0); + rb_define_method(rb_cSockIfaddr, "addr", ifaddr_addr, 0); + rb_define_method(rb_cSockIfaddr, "netmask", ifaddr_netmask, 0); + rb_define_method(rb_cSockIfaddr, "broadaddr", ifaddr_broadaddr, 0); + rb_define_method(rb_cSockIfaddr, "dstaddr", ifaddr_dstaddr, 0); +#endif + + rb_define_singleton_method(rb_cSocket, "getifaddrs", socket_s_getifaddrs, 0); +} -- cgit v1.2.3