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/extconf.rb | 8 +- ext/socket/ifaddr.c | 434 ++++++++++++++++++++++++++++++++++++++++++++++ ext/socket/init.c | 1 + ext/socket/mkconstants.rb | 59 +++++++ ext/socket/raddrinfo.c | 236 +++++++++++++++++++++---- ext/socket/rubysocket.h | 17 ++ ext/socket/socket.c | 17 ++ 7 files changed, 740 insertions(+), 32 deletions(-) create mode 100644 ext/socket/ifaddr.c (limited to 'ext') diff --git a/ext/socket/extconf.rb b/ext/socket/extconf.rb index 29e401a5ba..f7470c06dc 100644 --- a/ext/socket/extconf.rb +++ b/ext/socket/extconf.rb @@ -306,6 +306,8 @@ end netinet/tcp.h netinet/udp.h arpa/inet.h + netpacket/packet.h + net/ethernet.h sys/un.h ifaddrs.h sys/ioctl.h @@ -314,6 +316,7 @@ end sys/param.h sys/ucred.h ucred.h + net/if_dl.h arpa/nameser.h resolv.h ].each {|h| @@ -329,6 +332,8 @@ if have_type("struct sockaddr_un", headers) # POSIX have_struct_member("struct sockaddr_un", "sun_len", headers) # 4.4BSD end +have_type("struct sockaddr_dl", headers) # AF_LINK address. 4.4BSD since Net2 + have_type("struct sockaddr_storage", headers) have_type("struct addrinfo", headers) @@ -554,7 +559,8 @@ EOS "unixserver.#{$OBJEXT}", "option.#{$OBJEXT}", "ancdata.#{$OBJEXT}", - "raddrinfo.#{$OBJEXT}" + "raddrinfo.#{$OBJEXT}", + "ifaddr.#{$OBJEXT}" ] if getaddr_info_ok == :wide 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); +} diff --git a/ext/socket/init.c b/ext/socket/init.c index 1360800be9..a5ffe7bfa5 100644 --- a/ext/socket/init.c +++ b/ext/socket/init.c @@ -627,5 +627,6 @@ rsock_init_socket_init() rsock_init_sockopt(); rsock_init_ancdata(); rsock_init_addrinfo(); + rsock_init_sockifaddr(); rsock_init_socket_constants(); } diff --git a/ext/socket/mkconstants.rb b/ext/socket/mkconstants.rb index 7f8e1193bd..b86d726aa5 100644 --- a/ext/socket/mkconstants.rb +++ b/ext/socket/mkconstants.rb @@ -703,3 +703,62 @@ SCM_UCRED nil User credentials LOCAL_PEERCRED nil Retrieve peer credentials LOCAL_CREDS nil Pass credentials to receiver LOCAL_CONNWAIT nil Connect blocks until accepted + +IFF_802_1Q_VLAN nil 802.1Q VLAN device +IFF_ALLMULTI nil receive all multicast packets +IFF_ALTPHYS nil use alternate physical connection +IFF_AUTOMEDIA nil auto media select active +IFF_BONDING nil bonding master or slave +IFF_BRIDGE_PORT nil device used as bridge port +IFF_BROADCAST nil broadcast address valid +IFF_CANTCONFIG nil unconfigurable using ioctl(2) +IFF_DEBUG nil turn on debugging +IFF_DISABLE_NETPOLL nil disable netpoll at run-time +IFF_DONT_BRIDGE nil disallow bridging this ether dev +IFF_DORMANT nil driver signals dormant +IFF_DRV_OACTIVE nil tx hardware queue is full +IFF_DRV_RUNNING nil resources allocated +IFF_DYING nil interface is winding down +IFF_DYNAMIC nil dialup device with changing addresses +IFF_EBRIDGE nil ethernet bridging device +IFF_ECHO nil echo sent packets +IFF_ISATAP nil ISATAP interface (RFC4214) +IFF_LINK0 nil per link layer defined bit 0 +IFF_LINK1 nil per link layer defined bit 1 +IFF_LINK2 nil per link layer defined bit 2 +IFF_LIVE_ADDR_CHANGE nil hardware address change when it's running +IFF_LOOPBACK nil loopback net +IFF_LOWER_UP nil driver signals L1 up +IFF_MACVLAN_PORT nil device used as macvlan port +IFF_MASTER nil master of a load balancer +IFF_MASTER_8023AD nil bonding master, 802.3ad. +IFF_MASTER_ALB nil bonding master, balance-alb. +IFF_MASTER_ARPMON nil bonding master, ARP mon in use +IFF_MONITOR nil user-requested monitor mode +IFF_MULTICAST nil supports multicast +IFF_NOARP nil no address resolution protocol +IFF_NOTRAILERS nil avoid use of trailers +IFF_OACTIVE nil transmission in progress +IFF_OVS_DATAPATH nil device used as Open vSwitch datapath port +IFF_POINTOPOINT nil point-to-point link +IFF_PORTSEL nil can set media type +IFF_PPROMISC nil user-requested promisc mode +IFF_PROMISC nil receive all packets +IFF_RENAMING nil interface is being renamed +IFF_ROUTE nil routing entry installed +IFF_RUNNING nil resources allocated +IFF_SIMPLEX nil can't hear own transmissions +IFF_SLAVE nil slave of a load balancer +IFF_SLAVE_INACTIVE nil bonding slave not the curr. active +IFF_SLAVE_NEEDARP nil need ARPs for validation +IFF_SMART nil interface manages own routes +IFF_STATICARP nil static ARP +IFF_SUPP_NOFCS nil sending custom FCS +IFF_TEAM_PORT nil used as team port +IFF_TX_SKB_SHARING nil sharing skbs on transmit +IFF_UNICAST_FLT nil unicast filtering +IFF_UP nil interface is up +IFF_WAN_HDLC nil WAN HDLC device +IFF_XMIT_DST_RELEASE nil dev_hard_start_xmit() is allowed to release skb->dst +IFF_VOLATILE nil volatile flags +IFF_CANTCHANGE nil flags not changeable diff --git a/ext/socket/raddrinfo.c b/ext/socket/raddrinfo.c index 327218f222..6ef40d649f 100644 --- a/ext/socket/raddrinfo.c +++ b/ext/socket/raddrinfo.c @@ -953,34 +953,62 @@ static VALUE inspect_sockaddr(VALUE addrinfo, VALUE ret) { rb_addrinfo_t *rai = get_addrinfo(addrinfo); + union_sockaddr *sockaddr = &rai->addr; + socklen_t socklen = rai->sockaddr_len; + return rsock_inspect_sockaddr((struct sockaddr *)sockaddr, socklen, ret); +} - if (rai->sockaddr_len == 0) { +VALUE +rsock_inspect_sockaddr(struct sockaddr *sockaddr_arg, socklen_t socklen, VALUE ret) +{ + union_sockaddr *sockaddr = (union_sockaddr *)sockaddr_arg; + if (socklen == 0) { rb_str_cat2(ret, "empty-sockaddr"); } - else if ((long)rai->sockaddr_len < ((char*)&rai->addr.addr.sa_family + sizeof(rai->addr.addr.sa_family)) - (char*)&rai->addr) + else if ((long)socklen < ((char*)&sockaddr->addr.sa_family + sizeof(sockaddr->addr.sa_family)) - (char*)sockaddr) rb_str_cat2(ret, "too-short-sockaddr"); else { - switch (rai->addr.addr.sa_family) { + switch (sockaddr->addr.sa_family) { + case AF_UNSPEC: + { + rb_str_cat2(ret, "UNSPEC"); + break; + } + case AF_INET: { struct sockaddr_in *addr; int port; - if (rai->sockaddr_len < (socklen_t)sizeof(struct sockaddr_in)) { - rb_str_cat2(ret, "too-short-AF_INET-sockaddr"); - } - else { - addr = &rai->addr.in; - rb_str_catf(ret, "%d.%d.%d.%d", - ((unsigned char*)&addr->sin_addr)[0], - ((unsigned char*)&addr->sin_addr)[1], - ((unsigned char*)&addr->sin_addr)[2], - ((unsigned char*)&addr->sin_addr)[3]); - port = ntohs(addr->sin_port); - if (port) - rb_str_catf(ret, ":%d", port); - if ((socklen_t)sizeof(struct sockaddr_in) < rai->sockaddr_len) - rb_str_catf(ret, "(sockaddr %d bytes too long)", (int)(rai->sockaddr_len - sizeof(struct sockaddr_in))); - } + addr = &sockaddr->in; + if (((char*)&addr->sin_addr)-(char*)addr+0+1 <= socklen) + rb_str_catf(ret, "%d", ((unsigned char*)&addr->sin_addr)[0]); + else + rb_str_cat2(ret, "?"); + if (((char*)&addr->sin_addr)-(char*)addr+1+1 <= socklen) + rb_str_catf(ret, ".%d", ((unsigned char*)&addr->sin_addr)[1]); + else + rb_str_cat2(ret, ".?"); + if (((char*)&addr->sin_addr)-(char*)addr+2+1 <= socklen) + rb_str_catf(ret, ".%d", ((unsigned char*)&addr->sin_addr)[2]); + else + rb_str_cat2(ret, ".?"); + if (((char*)&addr->sin_addr)-(char*)addr+3+1 <= socklen) + rb_str_catf(ret, ".%d", ((unsigned char*)&addr->sin_addr)[3]); + else + rb_str_cat2(ret, ".?"); + + if (((char*)&addr->sin_port)-(char*)addr+(int)sizeof(addr->sin_port) < socklen) { + port = ntohs(addr->sin_port); + if (port) + rb_str_catf(ret, ":%d", port); + } + else { + rb_str_cat2(ret, ":?"); + } + if ((socklen_t)sizeof(struct sockaddr_in) != socklen) + rb_str_catf(ret, " (%d bytes for %d bytes sockaddr_in)", + (int)socklen, + (int)sizeof(struct sockaddr_in)); break; } @@ -991,16 +1019,16 @@ inspect_sockaddr(VALUE addrinfo, VALUE ret) char hbuf[1024]; int port; int error; - if (rai->sockaddr_len < (socklen_t)sizeof(struct sockaddr_in6)) { - rb_str_cat2(ret, "too-short-AF_INET6-sockaddr"); + if (socklen < (socklen_t)sizeof(struct sockaddr_in6)) { + rb_str_catf(ret, "too-short-AF_INET6-sockaddr %d bytes", (int)socklen); } else { - addr = &rai->addr.in6; + addr = &sockaddr->in6; /* use getnameinfo for scope_id. * RFC 4007: IPv6 Scoped Address Architecture * draft-ietf-ipv6-scope-api-00.txt: Scoped Address Extensions to the IPv6 Basic Socket API */ - error = getnameinfo(&rai->addr.addr, rai->sockaddr_len, + error = getnameinfo(&sockaddr->addr, socklen, hbuf, (socklen_t)sizeof(hbuf), NULL, 0, NI_NUMERICHOST|NI_NUMERICSERV); if (error) { @@ -1013,8 +1041,8 @@ inspect_sockaddr(VALUE addrinfo, VALUE ret) port = ntohs(addr->sin6_port); rb_str_catf(ret, "[%s]:%d", hbuf, port); } - if ((socklen_t)sizeof(struct sockaddr_in6) < rai->sockaddr_len) - rb_str_catf(ret, "(sockaddr %d bytes too long)", (int)(rai->sockaddr_len - sizeof(struct sockaddr_in6))); + if ((socklen_t)sizeof(struct sockaddr_in6) < socklen) + rb_str_catf(ret, "(sockaddr %d bytes too long)", (int)(socklen - sizeof(struct sockaddr_in6))); } break; } @@ -1023,10 +1051,10 @@ inspect_sockaddr(VALUE addrinfo, VALUE ret) #ifdef HAVE_SYS_UN_H case AF_UNIX: { - struct sockaddr_un *addr = &rai->addr.un; + struct sockaddr_un *addr = &sockaddr->un; char *p, *s, *e; s = addr->sun_path; - e = (char*)addr + rai->sockaddr_len; + e = (char*)addr + socklen; while (s < e && *(e-1) == '\0') e--; if (e < s) @@ -1042,11 +1070,11 @@ inspect_sockaddr(VALUE addrinfo, VALUE ret) } if (printable_only) { /* only printable, no space */ if (s[0] != '/') /* relative path */ - rb_str_cat2(ret, "AF_UNIX "); + rb_str_cat2(ret, "UNIX "); rb_str_cat(ret, s, p - s); } else { - rb_str_cat2(ret, "AF_UNIX"); + rb_str_cat2(ret, "UNIX"); while (s < e) rb_str_catf(ret, ":%02x", (unsigned char)*s++); } @@ -1055,11 +1083,157 @@ inspect_sockaddr(VALUE addrinfo, VALUE ret) } #endif +#ifdef AF_PACKET + /* GNU/Linux */ + case AF_PACKET: + { + struct sockaddr_ll *addr; + const char *sep = "["; +#define CATSEP do { rb_str_cat2(ret, sep); sep = " "; } while (0); + + addr = (struct sockaddr_ll *)sockaddr; + + rb_str_cat2(ret, "PACKET"); + + if (offsetof(struct sockaddr_ll, sll_protocol) + sizeof(addr->sll_protocol) <= socklen) { + CATSEP; + rb_str_catf(ret, "protocol=%d", ntohs(addr->sll_protocol)); + } + if (offsetof(struct sockaddr_ll, sll_ifindex) + sizeof(addr->sll_ifindex) <= socklen) { + char buf[IFNAMSIZ]; + CATSEP; + if (if_indextoname(addr->sll_ifindex, buf) == NULL) + rb_str_catf(ret, "ifindex=%d", addr->sll_ifindex); + else + rb_str_catf(ret, "%s", buf); + } + if (offsetof(struct sockaddr_ll, sll_hatype) + sizeof(addr->sll_hatype) <= socklen) { + CATSEP; + rb_str_catf(ret, "hatype=%d", addr->sll_hatype); + } + if (offsetof(struct sockaddr_ll, sll_pkttype) + sizeof(addr->sll_pkttype) <= socklen) { + CATSEP; + if (addr->sll_pkttype == PACKET_HOST) + rb_str_cat2(ret, "HOST"); + else if (addr->sll_pkttype == PACKET_BROADCAST) + rb_str_cat2(ret, "BROADCAST"); + else if (addr->sll_pkttype == PACKET_MULTICAST) + rb_str_cat2(ret, "MULTICAST"); + else if (addr->sll_pkttype == PACKET_OTHERHOST) + rb_str_cat2(ret, "OTHERHOST"); + else if (addr->sll_pkttype == PACKET_OUTGOING) + rb_str_cat2(ret, "OUTGOING"); + else + rb_str_catf(ret, "pkttype=%d", addr->sll_pkttype); + } + if (socklen != (socklen_t)(offsetof(struct sockaddr_ll, sll_addr) + addr->sll_halen)) { + CATSEP; + if (offsetof(struct sockaddr_ll, sll_halen) + sizeof(addr->sll_halen) <= socklen) { + rb_str_catf(ret, "halen=%d", addr->sll_halen); + } + } + if (offsetof(struct sockaddr_ll, sll_addr) < socklen) { + socklen_t len, i; + CATSEP; + rb_str_cat2(ret, "hwaddr"); + len = addr->sll_halen; + if (socklen < offsetof(struct sockaddr_ll, sll_addr) + len) + len = socklen - offsetof(struct sockaddr_ll, sll_addr); + for (i = 0; i < len; i++) { + rb_str_cat2(ret, i == 0 ? "=" : ":"); + rb_str_catf(ret, "%02x", addr->sll_addr[i]); + } + } + + if (socklen < (socklen_t)(offsetof(struct sockaddr_ll, sll_halen) + sizeof(addr->sll_halen)) || + (socklen_t)(offsetof(struct sockaddr_ll, sll_addr) + addr->sll_halen) != socklen) { + CATSEP; + rb_str_catf(ret, "(%d bytes for %d bytes sockaddr_ll)", + (int)socklen, (int)sizeof(struct sockaddr_ll)); + } + + rb_str_cat2(ret, "]"); +#undef CATSEP + + break; + } +#endif + +#ifdef AF_LINK + /* AF_LINK is defined in 4.4BSD derivations since Net2. + link_ntoa is also defined at Net2. + However Debian GNU/kFreeBSD defines AF_LINK but + don't have link_ntoa. */ + case AF_LINK: + { + /* + * Simple implementation using link_ntoa(): + * This doesn't work on Debian GNU/kFreeBSD 6.0.7 (squeeze). + * Also, the format is bit different. + * + * rb_str_catf(ret, "LINK %s", link_ntoa(&sockaddr->dl)); + * break; + */ + struct sockaddr_dl *addr = &sockaddr->dl; + char *np = NULL, *ap = NULL, *endp; + int nlen = 0, alen = 0; + int i, off; + const char *sep = "["; +#define CATSEP do { rb_str_cat2(ret, sep); sep = " "; } while (0); + + rb_str_cat2(ret, "LINK"); + + endp = ((char *)addr) + socklen; + + if (offsetof(struct sockaddr_dl, sdl_data) < socklen) { + np = addr->sdl_data; + nlen = addr->sdl_nlen; + if (endp - np < nlen) + nlen = endp - np; + } + off = addr->sdl_nlen; + + if (offsetof(struct sockaddr_dl, sdl_data) + off < socklen) { + ap = addr->sdl_data + off; + alen = addr->sdl_alen; + if (endp - ap < alen) + alen = endp - ap; + } + + CATSEP; + if (np) + rb_str_catf(ret, "%.*s", nlen, np); + else + rb_str_cat2(ret, "?"); + + if (ap && 0 < alen) { + CATSEP; + for (i = 0; i < alen; i++) + rb_str_catf(ret, "%s%02x", i == 0 ? "" : ":", (unsigned char)ap[i]); + } + + if (socklen < (socklen_t)(offsetof(struct sockaddr_dl, sdl_nlen) + sizeof(addr->sdl_nlen)) || + socklen < (socklen_t)(offsetof(struct sockaddr_dl, sdl_alen) + sizeof(addr->sdl_alen)) || + socklen < (socklen_t)(offsetof(struct sockaddr_dl, sdl_slen) + sizeof(addr->sdl_slen)) || + /* longer length is possible behavior because struct sockaddr_dl has "minimum work area, can be larger" as the last field. + * cf. Net2:/usr/src/sys/net/if_dl.h. */ + socklen < (socklen_t)(offsetof(struct sockaddr_dl, sdl_data) + addr->sdl_nlen + addr->sdl_alen + addr->sdl_slen)) { + CATSEP; + rb_str_catf(ret, "(%d bytes for %d bytes sockaddr_dl)", + (int)socklen, (int)sizeof(struct sockaddr_dl)); + } + + rb_str_cat2(ret, "]"); +#undef CATSEP + break; + } +#endif + default: { - ID id = rsock_intern_family(rai->addr.addr.sa_family); + ID id = rsock_intern_family(sockaddr->addr.sa_family); if (id == 0) - rb_str_catf(ret, "unknown address family %d", rai->addr.addr.sa_family); + rb_str_catf(ret, "unknown address family %d", sockaddr->addr.sa_family); else rb_str_catf(ret, "%s address format unknown", rb_id2name(id)); break; diff --git a/ext/socket/rubysocket.h b/ext/socket/rubysocket.h index 8144872007..d049d0f570 100644 --- a/ext/socket/rubysocket.h +++ b/ext/socket/rubysocket.h @@ -44,6 +44,13 @@ # include #endif +#ifdef HAVE_NETPACKET_PACKET_H +# include +#endif +#ifdef HAVE_NET_ETHERNET_H +# include +#endif + #include #ifdef HAVE_SYS_UN_H @@ -87,6 +94,9 @@ #ifdef HAVE_UCRED_H # include #endif +#ifdef HAVE_NET_IF_DL_H +# include +#endif #ifndef HAVE_TYPE_SOCKLEN_T typedef int socklen_t; @@ -168,6 +178,9 @@ typedef union { #endif #ifdef HAVE_TYPE_STRUCT_SOCKADDR_UN struct sockaddr_un un; +#endif +#ifdef HAVE_TYPE_STRUCT_SOCKADDR_DL + struct sockaddr_dl dl; /* AF_LINK */ #endif struct sockaddr_storage storage; char place_holder[2048]; /* sockaddr_storage is not enough for Unix domain sockets on SunOS and Darwin. */ @@ -267,6 +280,9 @@ VALUE rsock_addrinfo_inspect_sockaddr(VALUE rai); VALUE rsock_make_ipaddr(struct sockaddr *addr, socklen_t addrlen); VALUE rsock_ipaddr(struct sockaddr *sockaddr, socklen_t sockaddrlen, int norevlookup); VALUE rsock_make_hostent(VALUE host, struct addrinfo *addr, VALUE (*ipaddr)(struct sockaddr *, socklen_t)); +VALUE rsock_inspect_sockaddr(struct sockaddr *addr, socklen_t socklen, VALUE ret); +socklen_t rsock_sockaddr_len(struct sockaddr *addr); +VALUE rsock_sockaddr_obj(struct sockaddr *addr, socklen_t len); int rsock_revlookup_flag(VALUE revlookup, int *norevlookup); @@ -344,6 +360,7 @@ void rsock_init_socket_constants(void); void rsock_init_ancdata(void); void rsock_init_addrinfo(void); void rsock_init_sockopt(void); +void rsock_init_sockifaddr(void); void rsock_init_socket_init(void); NORETURN(void rsock_sys_fail_host_port(const char *, VALUE, VALUE)); diff --git a/ext/socket/socket.c b/ext/socket/socket.c index 8172ae4ddb..8c5ffed836 100644 --- a/ext/socket/socket.c +++ b/ext/socket/socket.c @@ -1584,11 +1584,22 @@ sockaddr_len(struct sockaddr *addr) return (socklen_t)sizeof(struct sockaddr_un); #endif +#ifdef AF_PACKET + case AF_PACKET: + return (socklen_t)(offsetof(struct sockaddr_ll, sll_addr) + ((struct sockaddr_ll *)addr)->sll_halen); +#endif + default: return (socklen_t)(offsetof(struct sockaddr, sa_family) + sizeof(addr->sa_family)); } } +socklen_t +rsock_sockaddr_len(struct sockaddr *addr) +{ + return sockaddr_len(addr); +} + static VALUE sockaddr_obj(struct sockaddr *addr, socklen_t len) { @@ -1622,6 +1633,12 @@ sockaddr_obj(struct sockaddr *addr, socklen_t len) return rsock_addrinfo_new(addr, len, addr->sa_family, 0, 0, Qnil, Qnil); } +VALUE +rsock_sockaddr_obj(struct sockaddr *addr, socklen_t len) +{ + return sockaddr_obj(addr, len); +} + #endif #if defined(HAVE_GETIFADDRS) || (defined(SIOCGLIFCONF) && defined(SIOCGLIFNUM) && !defined(__hpux)) || defined(SIOCGIFCONF) || defined(_WIN32) -- cgit v1.2.3