689 lines
15 KiB
C
689 lines
15 KiB
C
#include "ruby.h"
|
|
|
|
#ifndef RUBY_19
|
|
#include "rubysig.h"
|
|
#endif
|
|
|
|
#include "netifaces.h"
|
|
|
|
#if !defined(WIN32)
|
|
#if !HAVE_GETNAMEINFO
|
|
#undef getnameinfo
|
|
#undef NI_NUMERICHOST
|
|
|
|
#define getnameinfo our_getnameinfo
|
|
#define NI_NUMERICHOST 1
|
|
|
|
/* A very simple getnameinfo() for platforms without */
|
|
static int
|
|
getnameinfo (const struct sockaddr *addr, int addr_len,
|
|
char *buffer, int buflen,
|
|
char *buf2, int buf2len,
|
|
int flags)
|
|
{
|
|
switch (addr->sa_family)
|
|
{
|
|
case AF_INET:
|
|
{
|
|
const struct sockaddr_in *sin = (struct sockaddr_in *)addr;
|
|
const unsigned char *bytes = (unsigned char *)&sin->sin_addr.s_addr;
|
|
char tmpbuf[20];
|
|
|
|
sprintf (tmpbuf, "%d.%d.%d.%d",
|
|
bytes[0], bytes[1], bytes[2], bytes[3]);
|
|
|
|
strncpy (buffer, tmpbuf, buflen);
|
|
}
|
|
break;
|
|
#ifdef AF_INET6
|
|
case AF_INET6:
|
|
{
|
|
const struct sockaddr_in6 *sin = (const struct sockaddr_in6 *)addr;
|
|
const unsigned char *bytes = sin->sin6_addr.s6_addr;
|
|
int n;
|
|
char tmpbuf[80], *ptr = tmpbuf;
|
|
int done_double_colon = FALSE;
|
|
int colon_mode = FALSE;
|
|
|
|
for (n = 0; n < 8; ++n)
|
|
{
|
|
unsigned char b1 = bytes[2 * n];
|
|
unsigned char b2 = bytes[2 * n + 1];
|
|
|
|
if (b1)
|
|
{
|
|
if (colon_mode)
|
|
{
|
|
colon_mode = FALSE;
|
|
*ptr++ = ':';
|
|
}
|
|
sprintf (ptr, "%x%02x", b1, b2);
|
|
ptr += strlen (ptr);
|
|
*ptr++ = ':';
|
|
}
|
|
else if (b2)
|
|
{
|
|
if (colon_mode)
|
|
{
|
|
colon_mode = FALSE;
|
|
*ptr++ = ':';
|
|
}
|
|
sprintf (ptr, "%x", b2);
|
|
ptr += strlen (ptr);
|
|
*ptr++ = ':';
|
|
}
|
|
else {
|
|
if (!colon_mode)
|
|
{
|
|
if (done_double_colon)
|
|
{
|
|
*ptr++ = '0';
|
|
*ptr++ = ':';
|
|
}
|
|
else
|
|
{
|
|
if (n == 0)
|
|
*ptr++ = ':';
|
|
colon_mode = TRUE;
|
|
done_double_colon = TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (colon_mode)
|
|
{
|
|
colon_mode = FALSE;
|
|
*ptr++ = ':';
|
|
*ptr++ = '\0';
|
|
}
|
|
else
|
|
{
|
|
*--ptr = '\0';
|
|
}
|
|
|
|
strncpy (buffer, tmpbuf, buflen);
|
|
}
|
|
break;
|
|
#endif /* AF_INET6 */
|
|
default:
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
static int
|
|
string_from_sockaddr (struct sockaddr *addr,
|
|
char *buffer,
|
|
int buflen)
|
|
{
|
|
if (!addr || addr->sa_family == AF_UNSPEC)
|
|
return -1;
|
|
|
|
if (getnameinfo (addr, SA_LEN(addr),
|
|
buffer, buflen,
|
|
NULL, 0,
|
|
NI_NUMERICHOST) != 0)
|
|
{
|
|
int n, len;
|
|
char *ptr;
|
|
const char *data;
|
|
|
|
len = SA_LEN(addr);
|
|
|
|
#if HAVE_AF_LINK
|
|
/* BSD-like systems have AF_LINK */
|
|
if (addr->sa_family == AF_LINK)
|
|
{
|
|
struct sockaddr_dl *dladdr = (struct sockaddr_dl *)addr;
|
|
len = dladdr->sdl_alen;
|
|
if(len >=0)
|
|
data = LLADDR(dladdr);
|
|
}
|
|
else
|
|
{
|
|
#endif
|
|
#if defined(AF_PACKET)
|
|
/* Linux has AF_PACKET instead */
|
|
if (addr->sa_family == AF_PACKET)
|
|
{
|
|
struct sockaddr_ll *lladdr = (struct sockaddr_ll *)addr;
|
|
len = lladdr->sll_halen;
|
|
//amaloteaux: openbsd and maybe other systems have a len of 0 for enc0,pflog0 .. interfaces
|
|
if(len >=0)
|
|
data = (const char *)lladdr->sll_addr;
|
|
}
|
|
else
|
|
{
|
|
#endif
|
|
/* We don't know anything about this sockaddr, so just display
|
|
the entire data area in binary. */
|
|
len -= (sizeof (struct sockaddr) - sizeof (addr->sa_data));
|
|
data = addr->sa_data;
|
|
#if defined(AF_PACKET)
|
|
}
|
|
#endif
|
|
#if HAVE_AF_LINK
|
|
}
|
|
#endif
|
|
|
|
if ((buflen < 3 * len) || len <= 0)
|
|
return -1;
|
|
|
|
ptr = buffer;
|
|
buffer[0] = '\0';
|
|
|
|
for (n = 0; n < len; ++n)
|
|
{
|
|
sprintf (ptr, "%02x:", data[n] & 0xff);
|
|
ptr += 3;
|
|
}
|
|
*--ptr = '\0';
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
#endif /* !defined(WIN32) */
|
|
|
|
static VALUE add_to_family(VALUE result, VALUE family, VALUE value)
|
|
{
|
|
Check_Type(result, T_HASH);
|
|
Check_Type(family, T_FIXNUM);
|
|
Check_Type(value, T_HASH);
|
|
VALUE list;
|
|
|
|
list = rb_hash_aref(result, family);
|
|
|
|
if (list == Qnil)
|
|
list = rb_ary_new();
|
|
else
|
|
Check_Type(list, T_ARRAY);
|
|
|
|
rb_ary_push(list, value);
|
|
rb_hash_aset(result, family, list);
|
|
return result;
|
|
}
|
|
|
|
VALUE
|
|
rbnetifaces_s_addresses (VALUE class, VALUE dev)
|
|
{
|
|
Check_Type(dev, T_STRING);
|
|
|
|
VALUE result;
|
|
int found = FALSE;
|
|
result = rb_hash_new();
|
|
|
|
#if defined(WIN32)
|
|
PIP_ADAPTER_INFO pAdapterInfo = NULL;
|
|
PIP_ADAPTER_INFO pInfo = NULL;
|
|
ULONG ulBufferLength = 0;
|
|
DWORD dwRet;
|
|
PIP_ADDR_STRING str;
|
|
|
|
//First, retrieve the adapter information. We do this in a loop, in
|
|
//case someone adds or removes adapters in the meantime.
|
|
do
|
|
{
|
|
dwRet = GetAdaptersInfo(pAdapterInfo, &ulBufferLength);
|
|
|
|
if (dwRet == ERROR_BUFFER_OVERFLOW)
|
|
{
|
|
if (pAdapterInfo)
|
|
free (pAdapterInfo);
|
|
pAdapterInfo = (PIP_ADAPTER_INFO)malloc (ulBufferLength);
|
|
|
|
if (!pAdapterInfo)
|
|
{
|
|
rb_raise(rb_eRuntimeError, "Unknow error at OS level");
|
|
return Qnil;
|
|
}
|
|
}
|
|
} while (dwRet == ERROR_BUFFER_OVERFLOW);
|
|
|
|
// If we failed, then fail in Ruby too
|
|
if (dwRet != ERROR_SUCCESS && dwRet != ERROR_NO_DATA)
|
|
{
|
|
if (pAdapterInfo)
|
|
free (pAdapterInfo);
|
|
rb_raise(rb_eRuntimeError, "Unable to obtain adapter information.");
|
|
return Qnil;
|
|
}
|
|
|
|
for (pInfo = pAdapterInfo; pInfo; pInfo = pInfo->Next)
|
|
{
|
|
char buffer[256];
|
|
|
|
if (strcmp (pInfo->AdapterName, StringValuePtr(dev)) != 0)
|
|
continue;
|
|
|
|
VALUE rbhardw = Qnil;
|
|
VALUE rbaddr = Qnil;
|
|
VALUE rbnetmask = Qnil;
|
|
VALUE rbbraddr = Qnil;
|
|
|
|
found = TRUE;
|
|
|
|
// Do the physical address
|
|
if (256 >= 3 * pInfo->AddressLength)
|
|
{
|
|
VALUE hash_hardw;
|
|
hash_hardw = rb_hash_new();
|
|
|
|
char *ptr = buffer;
|
|
unsigned n;
|
|
|
|
*ptr = '\0';
|
|
for (n = 0; n < pInfo->AddressLength; ++n)
|
|
{
|
|
sprintf (ptr, "%02x:", pInfo->Address[n] & 0xff);
|
|
ptr += 3;
|
|
}
|
|
*--ptr = '\0';
|
|
|
|
rbhardw = rb_str_new2(buffer);
|
|
rb_hash_aset(hash_hardw, rb_str_new2("addr"), rbhardw);
|
|
result = add_to_family(result, INT2FIX(AF_LINK), hash_hardw);
|
|
}
|
|
|
|
for (str = &pInfo->IpAddressList; str; str = str->Next)
|
|
{
|
|
|
|
VALUE result2;
|
|
result2 = rb_hash_new();
|
|
|
|
if(str->IpAddress.String)
|
|
rbaddr = rb_str_new2(str->IpAddress.String);
|
|
if(str->IpMask.String)
|
|
rbnetmask = rb_str_new2(str->IpMask.String);
|
|
|
|
//If this isn't the loopback interface, work out the broadcast
|
|
//address, for better compatibility with other platforms.
|
|
if (pInfo->Type != MIB_IF_TYPE_LOOPBACK)
|
|
{
|
|
unsigned long inaddr = inet_addr (str->IpAddress.String);
|
|
unsigned long inmask = inet_addr (str->IpMask.String);
|
|
struct in_addr in;
|
|
char *brstr;
|
|
|
|
in.S_un.S_addr = (inaddr | ~inmask) & 0xfffffffful;
|
|
|
|
brstr = inet_ntoa (in);
|
|
|
|
if (brstr)
|
|
rbbraddr = rb_str_new2(brstr);
|
|
}
|
|
|
|
if (rbaddr)
|
|
rb_hash_aset(result2, rb_str_new2("addr"), rbaddr);
|
|
if (rbnetmask)
|
|
rb_hash_aset(result2, rb_str_new2("netmask"), rbnetmask);
|
|
if (rbbraddr)
|
|
rb_hash_aset(result2, rb_str_new2("broadcast"), rbbraddr);
|
|
|
|
result = add_to_family(result, INT2FIX(AF_INET), result2);
|
|
|
|
}
|
|
} // for
|
|
|
|
free (pAdapterInfo);
|
|
|
|
#elif HAVE_GETIFADDRS
|
|
struct ifaddrs *addrs = NULL;
|
|
struct ifaddrs *addr = NULL;
|
|
|
|
if (getifaddrs (&addrs) < 0)
|
|
{
|
|
rb_raise(rb_eRuntimeError, "Unknow error at OS level");
|
|
}
|
|
|
|
for (addr = addrs; addr; addr = addr->ifa_next)
|
|
{
|
|
char buffer[256];
|
|
VALUE rbaddr = Qnil;
|
|
VALUE rbnetmask = Qnil;
|
|
VALUE rbbraddr = Qnil;
|
|
|
|
if (strcmp (addr->ifa_name, StringValuePtr(dev)) != 0)
|
|
continue;
|
|
|
|
/* Sometimes there are records without addresses (e.g. in the case of a
|
|
dial-up connection via ppp, which on Linux can have a link address
|
|
record with no actual address). We skip these as they aren't useful.
|
|
Thanks to Christian Kauhaus for reporting this issue. */
|
|
if (!addr->ifa_addr)
|
|
continue;
|
|
|
|
found = TRUE;
|
|
|
|
if (string_from_sockaddr (addr->ifa_addr, buffer, sizeof (buffer)) == 0)
|
|
rbaddr = rb_str_new2(buffer);
|
|
|
|
if (string_from_sockaddr (addr->ifa_netmask, buffer, sizeof (buffer)) == 0)
|
|
rbnetmask = rb_str_new2(buffer);
|
|
|
|
if (string_from_sockaddr (addr->ifa_broadaddr, buffer, sizeof (buffer)) == 0)
|
|
rbbraddr = rb_str_new2(buffer);
|
|
|
|
VALUE result2;
|
|
result2 = rb_hash_new();
|
|
|
|
if (rbaddr)
|
|
rb_hash_aset(result2, rb_str_new2("addr"), rbaddr);
|
|
if (rbnetmask)
|
|
rb_hash_aset(result2, rb_str_new2("netmask"), rbnetmask);
|
|
if (rbbraddr)
|
|
{
|
|
if (addr->ifa_flags & (IFF_POINTOPOINT | IFF_LOOPBACK))
|
|
rb_hash_aset(result2, rb_str_new2("peer"), rbbraddr);
|
|
else
|
|
rb_hash_aset(result2, rb_str_new2("broadcast"), rbbraddr);
|
|
}
|
|
if (rbaddr || rbnetmask || rbbraddr)
|
|
result = add_to_family(result, INT2FIX(addr->ifa_addr->sa_family), result2);
|
|
}
|
|
freeifaddrs (addrs);
|
|
#elif HAVE_SOCKET_IOCTLS
|
|
|
|
int sock = socket(AF_INET, SOCK_DGRAM, 0);
|
|
|
|
if (sock < 0)
|
|
{
|
|
rb_raise(rb_eRuntimeError, "Unknow error at OS level");
|
|
return Qnil;
|
|
}
|
|
|
|
struct CNAME(ifreq) ifr;
|
|
|
|
char buffer[256];
|
|
int is_p2p = FALSE;
|
|
VALUE rbaddr = Qnil;
|
|
VALUE rbnetmask = Qnil;
|
|
VALUE rbbraddr = Qnil;
|
|
VALUE rbdstaddr = Qnil;
|
|
|
|
strncpy (ifr.CNAME(ifr_name), StringValuePtr(dev), IFNAMSIZ);
|
|
|
|
#if HAVE_SIOCGIFHWADDR
|
|
if (ioctl (sock, SIOCGIFHWADDR, &ifr) == 0)
|
|
{
|
|
if (string_from_sockaddr (&(ifr.CNAME(ifr_addr)), buffer, sizeof (buffer)) == 0)
|
|
{
|
|
found = TRUE;
|
|
|
|
VALUE rbhardw = Qnil;
|
|
VALUE hash_hardw;
|
|
hash_hardw = rb_hash_new();
|
|
rbhardw = rb_str_new2(buffer);
|
|
rb_hash_aset(hash_hardw, rb_str_new2("addr"), rbhardw);
|
|
result = add_to_family(result, INT2FIX(AF_LINK), hash_hardw);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
|
|
#if HAVE_SIOCGIFADDR
|
|
#if HAVE_SIOCGLIFNUM
|
|
if (ioctl (sock, SIOCGLIFADDR, &ifr) == 0)
|
|
{
|
|
#else
|
|
if (ioctl (sock, SIOCGIFADDR, &ifr) == 0)
|
|
{
|
|
#endif
|
|
if (string_from_sockaddr ((struct sockaddr *)&ifr.CNAME(ifr_addr), buffer, sizeof (buffer)) == 0)
|
|
{
|
|
found = TRUE;
|
|
rbaddr = rb_str_new2(buffer);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#if HAVE_SIOCGIFNETMASK
|
|
#if HAVE_SIOCGLIFNUM
|
|
if (ioctl (sock, SIOCGLIFNETMASK, &ifr) == 0)
|
|
{
|
|
#else
|
|
if (ioctl (sock, SIOCGIFNETMASK, &ifr) == 0)
|
|
{
|
|
#endif
|
|
if (string_from_sockaddr ((struct sockaddr *)&ifr.CNAME(ifr_addr), buffer, sizeof (buffer)) == 0)
|
|
{
|
|
found = TRUE;
|
|
rbnetmask = rb_str_new2(buffer);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#if HAVE_SIOCGIFFLAGS
|
|
#if HAVE_SIOCGLIFNUM
|
|
if (ioctl (sock, SIOCGLIFFLAGS, &ifr) == 0)
|
|
{
|
|
#else
|
|
if (ioctl (sock, SIOCGIFFLAGS, &ifr) == 0)
|
|
{
|
|
#endif
|
|
|
|
if (ifr.CNAME(ifr_flags) & IFF_POINTOPOINT)
|
|
{
|
|
is_p2p = TRUE;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#if HAVE_SIOCGIFBRDADDR
|
|
#if HAVE_SIOCGLIFNUM
|
|
if (!is_p2p && ioctl (sock, SIOCGLIFBRDADDR, &ifr) == 0)
|
|
{
|
|
#else
|
|
if (!is_p2p && ioctl (sock, SIOCGIFBRDADDR, &ifr) == 0)
|
|
{
|
|
#endif
|
|
|
|
|
|
if (string_from_sockaddr ((struct sockaddr *)&ifr.CNAME(ifr_addr), buffer, sizeof (buffer)) == 0)
|
|
{
|
|
found = TRUE;
|
|
rbbraddr = rb_str_new2(buffer);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#if HAVE_SIOCGIFDSTADDR
|
|
#if HAVE_SIOCGLIFNUM
|
|
if (is_p2p && ioctl (sock, SIOCGLIFBRDADDR, &ifr) == 0)
|
|
{
|
|
#else
|
|
if (is_p2p && ioctl (sock, SIOCGIFBRDADDR, &ifr) == 0)
|
|
{
|
|
#endif
|
|
if (string_from_sockaddr ((struct sockaddr *)&ifr.CNAME(ifr_addr), buffer, sizeof (buffer)) == 0)
|
|
{
|
|
found = TRUE;
|
|
rbdstaddr = rb_str_new2(buffer);
|
|
}
|
|
}
|
|
|
|
#endif
|
|
VALUE result2;
|
|
result2 = rb_hash_new();
|
|
|
|
if (rbaddr)
|
|
rb_hash_aset(result2, rb_str_new2("addr"), rbaddr);
|
|
if (rbnetmask)
|
|
rb_hash_aset(result2, rb_str_new2("netmask"), rbnetmask);
|
|
if (rbbraddr)
|
|
rb_hash_aset(result2, rb_str_new2("broadcast"), rbbraddr);
|
|
if (rbdstaddr)
|
|
rb_hash_aset(result2, rb_str_new2("peer"), rbbraddr);
|
|
|
|
if (rbaddr || rbnetmask || rbbraddr || rbdstaddr)
|
|
result = add_to_family(result, INT2FIX(AF_INET), result2);
|
|
|
|
close (sock);
|
|
#endif /* HAVE_SOCKET_IOCTLS */
|
|
|
|
if (found)
|
|
return result;
|
|
else
|
|
return Qnil;
|
|
|
|
}
|
|
|
|
VALUE
|
|
rbnetifaces_s_interfaces (VALUE self)
|
|
{
|
|
VALUE result;
|
|
result = rb_ary_new();
|
|
|
|
#if defined(WIN32)
|
|
PIP_ADAPTER_INFO pAdapterInfo = NULL;
|
|
PIP_ADAPTER_INFO pInfo = NULL;
|
|
ULONG ulBufferLength = 0;
|
|
DWORD dwRet;
|
|
|
|
// First, retrieve the adapter information
|
|
do {
|
|
dwRet = GetAdaptersInfo(pAdapterInfo, &ulBufferLength);
|
|
|
|
if (dwRet == ERROR_BUFFER_OVERFLOW)
|
|
{
|
|
if (pAdapterInfo)
|
|
free (pAdapterInfo);
|
|
pAdapterInfo = (PIP_ADAPTER_INFO)malloc (ulBufferLength);
|
|
|
|
if (!pAdapterInfo)
|
|
{
|
|
rb_raise(rb_eRuntimeError, "Unknow error at OS level");
|
|
}
|
|
}
|
|
} while (dwRet == ERROR_BUFFER_OVERFLOW);
|
|
|
|
// If we failed, then fail in Ruby too
|
|
if (dwRet != ERROR_SUCCESS && dwRet != ERROR_NO_DATA)
|
|
{
|
|
if (pAdapterInfo)
|
|
free (pAdapterInfo);
|
|
|
|
rb_raise(rb_eRuntimeError, "Unknow error at OS level");
|
|
return Qnil;
|
|
}
|
|
if (dwRet == ERROR_NO_DATA)
|
|
{
|
|
free (pAdapterInfo);
|
|
return result;
|
|
}
|
|
|
|
for (pInfo = pAdapterInfo; pInfo; pInfo = pInfo->Next)
|
|
{
|
|
VALUE ifname = rb_str_new2(pInfo->AdapterName);
|
|
//VALUE ifname = rb_str_new2(pInfo->Description);
|
|
if(!rb_ary_includes(result, ifname))
|
|
rb_ary_push(result, ifname);
|
|
}
|
|
|
|
free (pAdapterInfo);
|
|
|
|
#elif HAVE_GETIFADDRS
|
|
const char *prev_name = NULL;
|
|
struct ifaddrs *addrs = NULL;
|
|
struct ifaddrs *addr = NULL;
|
|
|
|
if (getifaddrs (&addrs) < 0)
|
|
{
|
|
rb_raise(rb_eRuntimeError, "Unknow error at OS level");
|
|
}
|
|
|
|
for (addr = addrs; addr; addr = addr->ifa_next)
|
|
{
|
|
if (!prev_name || strncmp (addr->ifa_name, prev_name, IFNAMSIZ) != 0)
|
|
{
|
|
VALUE ifname = rb_str_new2(addr->ifa_name);
|
|
|
|
if(!rb_ary_includes(result, ifname))
|
|
rb_ary_push(result, ifname);
|
|
|
|
prev_name = addr->ifa_name;
|
|
}
|
|
}
|
|
|
|
freeifaddrs (addrs);
|
|
#elif HAVE_SIOCGIFCONF
|
|
|
|
const char *prev_name = NULL;
|
|
int fd = socket (AF_INET, SOCK_DGRAM, 0);
|
|
struct CNAME(ifconf) ifc;
|
|
int len = -1, n;
|
|
if (fd < 0) {
|
|
rb_raise(rb_eRuntimeError, "Unknow error at OS level");
|
|
return Qnil;
|
|
}
|
|
|
|
// Try to find out how much space we need
|
|
#if HAVE_SIOCGSIZIFCONF
|
|
if (ioctl (fd, SIOCGSIZIFCONF, &len) < 0)
|
|
len = -1;
|
|
#elif HAVE_SIOCGLIFNUM
|
|
#error This code need to be checked first
|
|
/*
|
|
{ struct lifnum lifn;
|
|
lifn.lifn_family = AF_UNSPEC;
|
|
lifn.lifn_flags = LIFC_NOXMIT | LIFC_TEMPORARY | LIFC_ALLZONES;
|
|
ifc.lifc_family = AF_UNSPEC;
|
|
ifc.lifc_flags = LIFC_NOXMIT | LIFC_TEMPORARY | LIFC_ALLZONES;
|
|
if (ioctl (fd, SIOCGLIFNUM, (char *)&lifn) < 0)
|
|
len = -1;
|
|
else
|
|
len = lifn.lifn_count;
|
|
}
|
|
*/
|
|
#endif
|
|
|
|
// As a last resort, guess
|
|
if (len < 0)
|
|
len = 64;
|
|
|
|
ifc.CNAME(ifc_len) = len * sizeof (struct CNAME(ifreq));
|
|
ifc.CNAME(ifc_buf) = malloc (ifc.CNAME(ifc_len));
|
|
|
|
if (!ifc.CNAME(ifc_buf)) {
|
|
close (fd);
|
|
rb_raise(rb_eRuntimeError, "Not enough memory");
|
|
return Qnil;
|
|
}
|
|
|
|
#if HAVE_SIOCGLIFNUM
|
|
if (ioctl (fd, SIOCGLIFCONF, &ifc) < 0) {
|
|
#else
|
|
if (ioctl (fd, SIOCGIFCONF, &ifc) < 0) {
|
|
|
|
#endif
|
|
free (ifc.CNAME(ifc_req));
|
|
close (fd);
|
|
rb_raise(rb_eRuntimeError, "Unknow error at OS level");
|
|
return Qnil;
|
|
}
|
|
|
|
struct CNAME(ifreq) *pfreq = ifc.CNAME(ifc_req);
|
|
|
|
for (n = 0; n < ifc.CNAME(ifc_len)/sizeof(struct CNAME(ifreq));n++,pfreq++)
|
|
{
|
|
if (!prev_name || strncmp (prev_name, pfreq->CNAME(ifr_name), IFNAMSIZ) != 0)
|
|
{
|
|
VALUE ifname = rb_str_new2(pfreq->CNAME(ifr_name));
|
|
if(!rb_ary_includes(result, ifname))
|
|
rb_ary_push(result, ifname);
|
|
|
|
prev_name = pfreq->CNAME(ifr_name);
|
|
}
|
|
}
|
|
|
|
free (ifc.CNAME(ifc_buf));
|
|
close (fd);
|
|
|
|
#endif //
|
|
|
|
return result;
|
|
}
|
|
|
|
|