diff options
-rw-r--r-- | .hgignore | 8 | ||||
-rw-r--r-- | CHANGELOG | 61 | ||||
-rw-r--r-- | PKG-INFO | 25 | ||||
-rw-r--r-- | README.rst | 194 | ||||
-rw-r--r-- | netifaces.c | 2619 | ||||
-rw-r--r-- | pypi_windows_packages.bat | 4 | ||||
-rw-r--r-- | setup.cfg | 5 | ||||
-rw-r--r-- | setup.py | 539 | ||||
-rw-r--r-- | test.py | 50 |
9 files changed, 3505 insertions, 0 deletions
diff --git a/.hgignore b/.hgignore new file mode 100644 index 0000000..c353c18 --- /dev/null +++ b/.hgignore @@ -0,0 +1,8 @@ +syntax: glob +build +dist +*.pyc +*.pyd +*.so +*~ +*.egg-info diff --git a/CHANGELOG b/CHANGELOG new file mode 100644 index 0000000..c74ffc9 --- /dev/null +++ b/CHANGELOG @@ -0,0 +1,61 @@ +2014-05-19 Version 0.10.4 + + * Fixed a problem with multi-part Netlink messages on Linux. + +2014-05-06 Version 0.10.3 + + * When using Netlink (i.e. Linux), check for the constant + NLM_F_DUMP_INTR, since it only appeared in kernel version 3.1 + +2014-05-03 Version 0.10.2 + + * Added a workaround for broken netlink headers on certain Linux + distributions (the problem being that the netlink headers fail to + include <bits/sockaddr.h>, which causes a build failure). + +2014-05-02 Version 0.10.1 + + * Fixed a problem with the gateways() function that caused it a + crash on PyPy. + * The CI system is now set up to build for PyPy as well, so + problems will be found sooner. + +2014-05-01 Version 0.10.0 + + * Python 3 compatibility. + * Improved Windows source code compatibility. + * Added gateway detection via gateways() function. + * New version number format (skipped 0.9.0 due to its use by + the netifaces-merged fork). + +2012-01-31 Version 0.8 + + All changes in this version relate to the ioctl( ) code path, + which is not used on Windows or on any modern UNIX or UNIX-like + system that implements getaddrinfo(). + + * Fixed bit-rot in the ioctl() code path. + * Fixed a problem with setup.py that might manifest itself if the + config.cache file was manually edited. + * Fixed the ioctl() code path to cope with systems that have + sa_len and return longer than normal struct ifreq requests from + SIOCG[L]IFCONF (for instance, Mac OS X). + +2012-01-30 Version 0.7 + + * Dropped support for Win2K and earlier + * Added support for addresses other than IPv4 on Windows + * Removed empty 'addr' entries for interfaces that don't provide + any addresses. + * Fixed problems with setup script that prevented it running on + Windows, and improved the chances of it working with + cross-compilers somewhat. + * Added a verion property to the module that you can test at + runtime. + +2011-11-04 Version 0.6 + + * Added a workaround for a FreeBSD kernel bug (kern/152036). + * Added address_families dictionary to allow code to look up the + symbolic name corresponding to a given numberic address family + code. diff --git a/PKG-INFO b/PKG-INFO new file mode 100644 index 0000000..8fe1dcf --- /dev/null +++ b/PKG-INFO @@ -0,0 +1,25 @@ +Metadata-Version: 1.0 +Name: netifaces +Version: 0.5 +Summary: Portable network interface information. +Home-page: http://alastairs-place.net/netifaces +Author: Alastair Houghton +Author-email: alastair@alastairs-place.net +License: MIT License +Description: netifaces provides a (hopefully portable-ish) way for Python programmers to + get access to a list of the network interfaces on the local machine, and to + obtain the addresses of those network interfaces. + + The package has been tested on Mac OS X, Windows XP, Windows Vista, Linux + and Solaris. On Windows, it is currently not able to retrieve IPv6 + addresses, owing to shortcomings of the Windows API. + + It should work on other UNIX-like systems provided they implement + either getifaddrs() or support the SIOCGIFxxx socket options, although the + data provided by the socket options is normally less complete. + +Platform: UNKNOWN +Classifier: Development Status :: 4 - Beta +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: MIT License +Classifier: Topic :: System :: Networking diff --git a/README.rst b/README.rst new file mode 100644 index 0000000..67889c8 --- /dev/null +++ b/README.rst @@ -0,0 +1,194 @@ +netifaces 0.10.4 +================ + +.. image:: https://drone.io/bitbucket.org/al45tair/netifaces/status.png + :target: https://drone.io/bitbucket.org/al45tair/netifaces/latest + :alt: Build Status + +1. What is this? +---------------- + +It's been annoying me for some time that there's no easy way to get the +address(es) of the machine's network interfaces from Python. There is +a good reason for this difficulty, which is that it is virtually impossible +to do so in a portable manner. However, it seems to me that there should +be a package you can easy_install that will take care of working out the +details of doing so on the machine you're using, then you can get on with +writing Python code without concerning yourself with the nitty gritty of +system-dependent low-level networking APIs. + +This package attempts to solve that problem. + +2. How do I use it? +------------------- + +First you need to install it, which you can do by typing:: + + tar xvzf netifaces-0.10.4.tar.gz + cd netifaces-0.10.4 + python setup.py install + +Once that's done, you'll need to start Python and do something like the +following:: + +>>> import netifaces + +Then if you enter + +>>> netifaces.interfaces() +['lo0', 'gif0', 'stf0', 'en0', 'en1', 'fw0'] + +you'll see the list of interface identifiers for your machine. + +You can ask for the addresses of a particular interface by doing + +>>> netifaces.ifaddresses('lo0') +{18: [{'addr': ''}], 2: [{'peer': '127.0.0.1', 'netmask': '255.0.0.0', 'addr': '127.0.0.1'}], 30: [{'peer': '::1', 'netmask': 'ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff', 'addr': '::1'}, {'peer': '', 'netmask': 'ffff:ffff:ffff:ffff::', 'addr': 'fe80::1%lo0'}]} + +Hmmmm. That result looks a bit cryptic; let's break it apart and explain +what each piece means. It returned a dictionary, so let's look there first:: + + { 18: [...], 2: [...], 30: [...] } + +Each of the numbers refers to a particular address family. In this case, we +have three address families listed; on my system, 18 is ``AF_LINK`` (which means +the link layer interface, e.g. Ethernet), 2 is ``AF_INET`` (normal Internet +addresses), and 30 is ``AF_INET6`` (IPv6). + +But wait! Don't use these numbers in your code. The numeric values here are +system dependent; fortunately, I thought of that when writing netifaces, so +the module declares a range of values that you might need. e.g. + +>>> netifaces.AF_LINK +18 + +Again, on your system, the number may be different. + +So, what we've established is that the dictionary that's returned has one +entry for each address family for which this interface has an address. Let's +take a look at the ``AF_INET`` addresses now: + +>>> addrs = netifaces.ifaddresses('lo0') +>>> addrs[netifaces.AF_INET] +[{'peer': '127.0.0.1', 'netmask': '255.0.0.0', 'addr': '127.0.0.1'}] + +You might be wondering why this value is a list. The reason is that it's +possible for an interface to have more than one address, even within the +same family. I'll say that again: *you can have more than one address of +the same type associated with each interface*. + +*Asking for "the" address of a particular interface doesn't make sense.* + +Right, so, we can see that this particular interface only has one address, +and, because it's a loopback interface, it's point-to-point and therefore +has a *peer* address rather than a broadcast address. + +Let's look at a more interesting interface. + +>>> addrs = netifaces.ifaddresses('en0') +>>> addrs[netifaces.AF_INET] +[{'broadcast': '10.15.255.255', 'netmask': '255.240.0.0', 'addr': '10.0.1.4'}, {'broadcast': '192.168.0.255', 'addr': '192.168.0.47'}] + +This interface has two addresses (see, I told you...) Both of them are +regular IPv4 addresses, although in one case the netmask has been changed +from its default. The netmask *may not* appear on your system if it's set +to the default for the address range. + +Because this interface isn't point-to-point, it also has broadcast addresses. + +Now, say we want, instead of the IP addresses, to get the MAC address; that +is, the hardware address of the Ethernet adapter running this interface. We +can do + +>>> addrs[netifaces.AF_LINK] +[{'addr': '00:12:34:56:78:9a'}] + +Note that this may not be available on platforms without getifaddrs(), unless +they happen to implement ``SIOCGIFHWADDR``. Note also that you just get the +address; it's unlikely that you'll see anything else with an ``AF_LINK`` address. +Oh, and don't assume that all ``AF_LINK`` addresses are Ethernet; you might, for +instance, be on a Mac, in which case: + +>>> addrs = netifaces.ifaddresses('fw0') +>>> addrs[netifaces.AF_LINK] +[{'addr': '00:12:34:56:78:9a:bc:de'}] + +No, that isn't an exceptionally long Ethernet MAC address---it's a FireWire +address. + +As of version 0.10.0, you can also obtain a list of gateways on your +machine: + +>>> netifaces.gateways() +{2: [('10.0.1.1', 'en0', True), ('10.2.1.1', 'en1', False)], 30: [('fe80::1', 'en0', True)], 'default': { 2: ('10.0.1.1', 'en0'), 30: ('fe80::1', 'en0') }} + +This dictionary is keyed on address family---in this case, ``AF_INET``---and +each entry is a list of gateways as ``(address, interface, is_default)`` tuples. +Notice that here we have two separate gateways for IPv4 (``AF_INET``); some +operating systems support configurations like this and can either route packets +based on their source, or based on administratively configured routing tables. + +For convenience, we also allow you to index the dictionary with the special +value ``'default'``, which returns a dictionary mapping address families to the +default gateway in each case. Thus you can get the default IPv4 gateway with + +>>> gws = netifaces.gateways() +>>> gws['default'][netifaces.AF_INET] +('10.0.1.1', 'en0') + +Do note that there may be no default gateway for any given address family; +this is currently very common for IPv6 and much less common for IPv4 but it +can happen even for ``AF_INET``. + +BTW, if you're trying to configure your machine to have multiple gateways for +the same address family, it's a very good idea to check the documentation for +your operating system *very* carefully, as some systems become extremely +confused or route packets in a non-obvious manner. + +I'm very interested in hearing from anyone (on any platform) for whom the +``gateways()`` method doesn't produce the expected results. It's quite +complicated extracting this information from the operating system (whichever +operating system we're talking about), and so I expect there's at least one +system out there where this just won't work. + +3. This is great! What platforms does it work on? +-------------------------------------------------- + +It gets regular testing on OS X, Linux and Windows. It has also been used +successfully on Solaris, and it's expected to work properly on other UNIX-like +systems as well. If you are running something that is not supported, and +wish to contribute a patch, please use BitBucket to send a pull request. + +4. What license is this under? +------------------------------ + +It's an MIT-style license. Here goes: + +Copyright (c) 2007-2014 Alastair Houghton + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +5. Why the jump to 0.10.0? +-------------------------- + +Because someone released a fork of netifaces with the version 0.9.0. +Hopefully skipping the version number should remove any confusion. In +addition starting with 0.10.0 Python 3 is now supported and other +features/bugfixes have been included as well. See the CHANGELOG for a +more complete list of changes. diff --git a/netifaces.c b/netifaces.c new file mode 100644 index 0000000..8b82819 --- /dev/null +++ b/netifaces.c @@ -0,0 +1,2619 @@ +#include <Python.h> + +/* Python 3 compatibility */ +#if PY_MAJOR_VERSION >= 3 +#define PyInt_FromLong PyLong_FromLong +#define PyString_FromString PyUnicode_FromString + +#define MODULE_ERROR NULL +#define MODULE_RETURN(v) return (v) +#define MODULE_INIT(name) PyMODINIT_FUNC PyInit_##name(void) +#define MODULE_DEF(name,doc,methods) \ + static struct PyModuleDef moduledef = { \ + PyModuleDef_HEAD_INIT, (name), (doc), -1, (methods), }; +#define MODULE_CREATE(obj,name,doc,methods) \ + obj = PyModule_Create(&moduledef); +#else /* PY_MAJOR_VERSION < 3 */ +#define MODULE_ERROR +#define MODULE_RETURN(v) +#define MODULE_INIT(name) void init##name(void) +#define MODULE_DEF(name,doc,methods) +#define MODULE_CREATE(obj,name,doc,methods) \ + obj = Py_InitModule3((name), (methods), (doc)); +#endif + +#ifndef WIN32 + +# include <sys/types.h> +# include <sys/socket.h> +# include <net/if.h> +# include <netdb.h> + +# if HAVE_PF_ROUTE +# include <net/route.h> +# endif + +# if HAVE_SYSCTL_CTL_NET +# include <sys/sysctl.h> +# include <net/route.h> +# endif + +# if HAVE_PF_NETLINK +# include <asm/types.h> +# include <linux/netlink.h> +# include <linux/rtnetlink.h> +# include <arpa/inet.h> +# endif + +# if HAVE_SOCKET_IOCTLS +# include <sys/ioctl.h> +# include <netinet/in.h> +# include <arpa/inet.h> +# if defined(__sun) +# include <unistd.h> +# include <stropts.h> +# include <sys/sockio.h> +# endif +# endif /* HAVE_SOCKET_IOCTLS */ + +/* For logical interfaces support we convert all names to same name prefixed + with l */ +#if HAVE_SIOCGLIFNUM +#define CNAME(x) l##x +#else +#define CNAME(x) x +#endif + +#if HAVE_NET_IF_DL_H +# include <net/if_dl.h> +#endif + +/* For the benefit of stupid platforms (Linux), include all the sockaddr + definitions we can lay our hands on. It can also be useful for the benefit + of another stupid platform (FreeBSD, see PR 152036). */ +#include <netinet/in.h> +# if HAVE_NETASH_ASH_H +# include <netash/ash.h> +# endif +# if HAVE_NETATALK_AT_H +# include <netatalk/at.h> +# endif +# if HAVE_NETAX25_AX25_H +# include <netax25/ax25.h> +# endif +# if HAVE_NETECONET_EC_H +# include <neteconet/ec.h> +# endif +# if HAVE_NETIPX_IPX_H +# include <netipx/ipx.h> +# endif +# if HAVE_NETPACKET_PACKET_H +# include <netpacket/packet.h> +# endif +# if HAVE_NETROSE_ROSE_H +# include <netrose/rose.h> +# endif +# if HAVE_LINUX_IRDA_H +# include <linux/irda.h> +# endif +# if HAVE_LINUX_ATM_H +# include <linux/atm.h> +# endif +# if HAVE_LINUX_LLC_H +# include <linux/llc.h> +# endif +# if HAVE_LINUX_TIPC_H +# include <linux/tipc.h> +# endif +# if HAVE_LINUX_DN_H +# include <linux/dn.h> +# endif + +/* Map address families to sizes of sockaddr structs */ +static int af_to_len(int af) +{ + switch (af) { + case AF_INET: return sizeof (struct sockaddr_in); +#if defined(AF_INET6) && HAVE_SOCKADDR_IN6 + case AF_INET6: return sizeof (struct sockaddr_in6); +#endif +#if defined(AF_AX25) && HAVE_SOCKADDR_AX25 +# if defined(AF_NETROM) + case AF_NETROM: /* I'm assuming this is carried over x25 */ +# endif + case AF_AX25: return sizeof (struct sockaddr_ax25); +#endif +#if defined(AF_IPX) && HAVE_SOCKADDR_IPX + case AF_IPX: return sizeof (struct sockaddr_ipx); +#endif +#if defined(AF_APPLETALK) && HAVE_SOCKADDR_AT + case AF_APPLETALK: return sizeof (struct sockaddr_at); +#endif +#if defined(AF_ATMPVC) && HAVE_SOCKADDR_ATMPVC + case AF_ATMPVC: return sizeof (struct sockaddr_atmpvc); +#endif +#if defined(AF_ATMSVC) && HAVE_SOCKADDR_ATMSVC + case AF_ATMSVC: return sizeof (struct sockaddr_atmsvc); +#endif +#if defined(AF_X25) && HAVE_SOCKADDR_X25 + case AF_X25: return sizeof (struct sockaddr_x25); +#endif +#if defined(AF_ROSE) && HAVE_SOCKADDR_ROSE + case AF_ROSE: return sizeof (struct sockaddr_rose); +#endif +#if defined(AF_DECnet) && HAVE_SOCKADDR_DN + case AF_DECnet: return sizeof (struct sockaddr_dn); +#endif +#if defined(AF_PACKET) && HAVE_SOCKADDR_LL + case AF_PACKET: return sizeof (struct sockaddr_ll); +#endif +#if defined(AF_ASH) && HAVE_SOCKADDR_ASH + case AF_ASH: return sizeof (struct sockaddr_ash); +#endif +#if defined(AF_ECONET) && HAVE_SOCKADDR_EC + case AF_ECONET: return sizeof (struct sockaddr_ec); +#endif +#if defined(AF_IRDA) && HAVE_SOCKADDR_IRDA + case AF_IRDA: return sizeof (struct sockaddr_irda); +#endif +#if defined(AF_LINK) && HAVE_SOCKADDR_DL + case AF_LINK: return sizeof (struct sockaddr_dl); +#endif + } + return sizeof (struct sockaddr); +} + +#if !HAVE_SOCKADDR_SA_LEN +#define SA_LEN(sa) af_to_len(sa->sa_family) +#if HAVE_SIOCGLIFNUM +#define SS_LEN(sa) af_to_len(sa->ss_family) +#else +#define SS_LEN(sa) SA_LEN(sa) +#endif +#else +#define SA_LEN(sa) sa->sa_len +#endif /* !HAVE_SOCKADDR_SA_LEN */ + +# if HAVE_GETIFADDRS +# include <ifaddrs.h> +# endif /* HAVE_GETIFADDRS */ + +# if !HAVE_GETIFADDRS && (!HAVE_SOCKET_IOCTLS || !HAVE_SIOCGIFCONF) +/* If the platform doesn't define, what we need, barf. If you're seeing this, + it means you need to write suitable code to retrieve interface information + on your system. */ +# error You need to add code for your platform. +# endif + +#else /* defined(WIN32) */ + +#define _WIN32_WINNT 0x0501 + +# include <winsock2.h> +# include <ws2tcpip.h> +# include <iphlpapi.h> +# include <netioapi.h> + +#endif /* defined(WIN32) */ + +#ifndef TRUE +#define TRUE 1 +#endif + +#ifndef FALSE +#define FALSE 0 +#endif + +/* On systems without AF_LINK (Windows, for instance), define it anyway, but + give it a crazy value. On Linux, which has AF_PACKET but not AF_LINK, + define AF_LINK as the latter instead. */ +#ifndef AF_LINK +# ifdef AF_PACKET +# define AF_LINK AF_PACKET +# else +# define AF_LINK -1000 +# endif +# define HAVE_AF_LINK 0 +#else +# define HAVE_AF_LINK 1 +#endif + +/* -- Utility Functions ----------------------------------------------------- */ + +#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) +{ + struct sockaddr* bigaddr = 0; + int failure; + struct sockaddr* gniaddr; + socklen_t gnilen; + + if (!addr || addr->sa_family == AF_UNSPEC) + return -1; + + if (SA_LEN(addr) < af_to_len(addr->sa_family)) { + /* Sometimes ifa_netmask can be truncated. So let's detruncate it. FreeBSD + PR: kern/152036: getifaddrs(3) returns truncated sockaddrs for netmasks + -- http://www.freebsd.org/cgi/query-pr.cgi?pr=152036 */ + gnilen = af_to_len(addr->sa_family); + bigaddr = calloc(1, gnilen); + if (!bigaddr) + return -1; + memcpy(bigaddr, addr, SA_LEN(addr)); +#if HAVE_SOCKADDR_SA_LEN + bigaddr->sa_len = gnilen; +#endif + gniaddr = bigaddr; + } else { + gnilen = SA_LEN(addr); + gniaddr = addr; + } + + failure = getnameinfo (gniaddr, gnilen, + buffer, buflen, + NULL, 0, + NI_NUMERICHOST); + + if (bigaddr) { + free(bigaddr); + bigaddr = 0; + } + + if (failure) { + size_t 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; + 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; + 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) + return -1; + + ptr = buffer; + buffer[0] = '\0'; + + for (n = 0; n < len; ++n) { + sprintf (ptr, "%02x:", data[n] & 0xff); + ptr += 3; + } + *--ptr = '\0'; + } + + if (!buffer[0]) + return -1; + + return 0; +} +#endif /* !defined(WIN32) */ + +#if defined(WIN32) +static int +compare_bits (const void *pva, + const void *pvb, + unsigned bits) +{ + const unsigned char *pa = (const unsigned char *)pva; + const unsigned char *pb = (const unsigned char *)pvb; + unsigned char a, b; + static unsigned char masks[] = { + 0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe + }; + while (bits >= 8) { + a = *pa++; + b = *pb++; + if (a < b) + return -1; + else if (a > b) + return +1; + bits -= 8; + } + + if (bits) { + a = *pa++ & masks[bits]; + b = *pb++ & masks[bits]; + if (a < b) + return -1; + else if (a > b) + return +1; + } + + return 0; +} +#endif + +static int +add_to_family (PyObject *result, int family, PyObject *obj) +{ + PyObject *py_family; + PyObject *list; + + if (!PyObject_Size (obj)) + return TRUE; + + py_family = PyInt_FromLong (family); + list = PyDict_GetItem (result, py_family); + + if (!py_family) { + Py_DECREF (obj); + Py_XDECREF (list); + return FALSE; + } + + if (!list) { + list = PyList_New (1); + if (!list) { + Py_DECREF (obj); + Py_DECREF (py_family); + return FALSE; + } + + PyList_SET_ITEM (list, 0, obj); + PyDict_SetItem (result, py_family, list); + Py_DECREF (list); + } else { + PyList_Append (list, obj); + Py_DECREF (obj); + } + + return TRUE; +} + +/* -- ifaddresses() --------------------------------------------------------- */ + +static PyObject * +ifaddrs (PyObject *self, PyObject *args) +{ + const char *ifname; + PyObject *result; + int found = FALSE; +#if defined(WIN32) + PIP_ADAPTER_ADDRESSES pAdapterAddresses = NULL, pInfo = NULL; + ULONG ulBufferLength = 0; + DWORD dwRet; + PIP_ADAPTER_UNICAST_ADDRESS pUniAddr; +#elif HAVE_GETIFADDRS + struct ifaddrs *addrs = NULL; + struct ifaddrs *addr = NULL; +#endif + + if (!PyArg_ParseTuple (args, "s", &ifname)) + return NULL; + + result = PyDict_New (); + + if (!result) + return NULL; + +#if defined(WIN32) + /* .. Win32 ............................................................... */ + + /* First, retrieve the adapter information. We do this in a loop, in + case someone adds or removes adapters in the meantime. */ + do { + dwRet = GetAdaptersAddresses (AF_UNSPEC, GAA_FLAG_INCLUDE_PREFIX, NULL, + pAdapterAddresses, &ulBufferLength); + + if (dwRet == ERROR_BUFFER_OVERFLOW) { + if (pAdapterAddresses) + free (pAdapterAddresses); + pAdapterAddresses = (PIP_ADAPTER_ADDRESSES)malloc (ulBufferLength); + + if (!pAdapterAddresses) { + Py_DECREF (result); + PyErr_SetString (PyExc_MemoryError, "Not enough memory"); + return NULL; + } + } + } while (dwRet == ERROR_BUFFER_OVERFLOW); + + /* If we failed, then fail in Python too */ + if (dwRet != ERROR_SUCCESS && dwRet != ERROR_NO_DATA) { + Py_DECREF (result); + if (pAdapterAddresses) + free (pAdapterAddresses); + + PyErr_SetString (PyExc_OSError, + "Unable to obtain adapter information."); + return NULL; + } + + for (pInfo = pAdapterAddresses; pInfo; pInfo = pInfo->Next) { + char buffer[256]; + + if (strcmp (pInfo->AdapterName, ifname) != 0) + continue; + + found = TRUE; + + /* Do the physical address */ + if (256 >= 3 * pInfo->PhysicalAddressLength) { + PyObject *hwaddr, *dict; + char *ptr = buffer; + unsigned n; + + *ptr = '\0'; + for (n = 0; n < pInfo->PhysicalAddressLength; ++n) { + sprintf (ptr, "%02x:", pInfo->PhysicalAddress[n] & 0xff); + ptr += 3; + } + *--ptr = '\0'; + + hwaddr = PyString_FromString (buffer); + dict = PyDict_New (); + + if (!dict) { + Py_XDECREF (hwaddr); + Py_DECREF (result); + free (pAdapterAddresses); + return NULL; + } + + PyDict_SetItemString (dict, "addr", hwaddr); + Py_DECREF (hwaddr); + + if (!add_to_family (result, AF_LINK, dict)) { + Py_DECREF (result); + free (pAdapterAddresses); + return NULL; + } + } + + for (pUniAddr = pInfo->FirstUnicastAddress; + pUniAddr; + pUniAddr = pUniAddr->Next) { + DWORD dwLen = sizeof (buffer); + INT iRet = WSAAddressToStringA (pUniAddr->Address.lpSockaddr, + pUniAddr->Address.iSockaddrLength, + NULL, + buffer, + &dwLen); + PyObject *addr; + PyObject *mask = NULL; + PyObject *bcast = NULL; + PIP_ADAPTER_PREFIX pPrefix; + short family = pUniAddr->Address.lpSockaddr->sa_family; + + if (iRet) + continue; + + addr = PyString_FromString (buffer); + + /* Find the netmask, where possible */ + if (family == AF_INET) { + struct sockaddr_in *pAddr + = (struct sockaddr_in *)pUniAddr->Address.lpSockaddr; + + for (pPrefix = pInfo->FirstPrefix; + pPrefix; + pPrefix = pPrefix->Next) { + struct sockaddr_in *pPrefixAddr + = (struct sockaddr_in *)pPrefix->Address.lpSockaddr; + struct sockaddr_in maskAddr, bcastAddr; + unsigned toDo; + unsigned wholeBytes, remainingBits; + unsigned char *pMaskBits, *pBcastBits; + + if (pPrefixAddr->sin_family != AF_INET) + continue; + + if (compare_bits (&pPrefixAddr->sin_addr, + &pAddr->sin_addr, + pPrefix->PrefixLength) != 0) + continue; + + memcpy (&maskAddr, + pPrefix->Address.lpSockaddr, + sizeof (maskAddr)); + memcpy (&bcastAddr, + pPrefix->Address.lpSockaddr, + sizeof (bcastAddr)); + + wholeBytes = pPrefix->PrefixLength >> 3; + remainingBits = pPrefix->PrefixLength & 7; + + if (wholeBytes >= 4) + continue; + + toDo = wholeBytes; + pMaskBits = (unsigned char *)&maskAddr.sin_addr; + + while (toDo--) + *pMaskBits++ = 0xff; + + toDo = 4 - wholeBytes; + + pBcastBits = (unsigned char *)&bcastAddr.sin_addr + wholeBytes; + + if (remainingBits) { + static const unsigned char masks[] = { + 0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe + }; + *pMaskBits++ = masks[remainingBits]; + *pBcastBits &= masks[remainingBits]; + *pBcastBits++ |= ~masks[remainingBits]; + --toDo; + } + + while (toDo--) { + *pMaskBits++ = 0; + *pBcastBits++ = 0xff; + } + + dwLen = sizeof (buffer); + iRet = WSAAddressToStringA ((SOCKADDR *)&maskAddr, + sizeof (maskAddr), + NULL, + buffer, + &dwLen); + + if (iRet == 0) + mask = PyString_FromString (buffer); + + dwLen = sizeof (buffer); + iRet = WSAAddressToStringA ((SOCKADDR *)&bcastAddr, + sizeof (bcastAddr), + NULL, + buffer, + &dwLen); + + if (iRet == 0) + bcast = PyString_FromString (buffer); + + break; + } + } else if (family == AF_INET6) { + struct sockaddr_in6 *pAddr + = (struct sockaddr_in6 *)pUniAddr->Address.lpSockaddr; + + for (pPrefix = pInfo->FirstPrefix; + pPrefix; + pPrefix = pPrefix->Next) { + struct sockaddr_in6 *pPrefixAddr + = (struct sockaddr_in6 *)pPrefix->Address.lpSockaddr; + struct sockaddr_in6 maskAddr, bcastAddr; + unsigned toDo; + unsigned wholeBytes, remainingBits; + unsigned char *pMaskBits, *pBcastBits; + + if (pPrefixAddr->sin6_family != AF_INET6) + continue; + + if (compare_bits (&pPrefixAddr->sin6_addr, + &pAddr->sin6_addr, + pPrefix->PrefixLength) != 0) + continue; + + memcpy (&maskAddr, + pPrefix->Address.lpSockaddr, + sizeof (maskAddr)); + memcpy (&bcastAddr, + pPrefix->Address.lpSockaddr, + sizeof (bcastAddr)); + + wholeBytes = pPrefix->PrefixLength >> 3; + remainingBits = pPrefix->PrefixLength & 7; + + if (wholeBytes >= 8) + continue; + + toDo = wholeBytes; + pMaskBits = (unsigned char *)&maskAddr.sin6_addr; + + while (toDo--) + *pMaskBits++ = 0xff; + + toDo = 8 - wholeBytes; + + pBcastBits = (unsigned char *)&bcastAddr.sin6_addr + wholeBytes; + + if (remainingBits) { + static const unsigned char masks[] = { + 0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe + }; + *pMaskBits++ = masks[remainingBits]; + *pBcastBits &= masks[remainingBits]; + *pBcastBits++ |= ~masks[remainingBits]; + --toDo; + } + + while (toDo--) { + *pMaskBits++ = 0; + *pBcastBits++ = 0xff; + } + + dwLen = sizeof (buffer); + iRet = WSAAddressToStringA ((SOCKADDR *)&maskAddr, + sizeof (maskAddr), + NULL, + buffer, + &dwLen); + + if (iRet == 0) + mask = PyString_FromString (buffer); + + dwLen = sizeof (buffer); + iRet = WSAAddressToStringA ((SOCKADDR *)&bcastAddr, + sizeof (bcastAddr), + NULL, + buffer, + &dwLen); + + if (iRet == 0) + bcast = PyString_FromString (buffer); + + break; + } + } + + { + PyObject *dict; + + dict = PyDict_New (); + + if (!dict) { + Py_XDECREF (addr); + Py_XDECREF (mask); + Py_XDECREF (bcast); + Py_DECREF (result); + free (pAdapterAddresses); + return NULL; + } + + if (addr) + PyDict_SetItemString (dict, "addr", addr); + if (mask) + PyDict_SetItemString (dict, "netmask", mask); + if (bcast) + PyDict_SetItemString (dict, "broadcast", bcast); + + Py_XDECREF (addr); + Py_XDECREF (mask); + Py_XDECREF (bcast); + + if (!add_to_family (result, family, dict)) { + Py_DECREF (result); + free ((void *)pAdapterAddresses); + return NULL; + } + } + } + } + + free ((void *)pAdapterAddresses); +#elif HAVE_GETIFADDRS + /* .. UNIX, with getifaddrs() ............................................. */ + + if (getifaddrs (&addrs) < 0) { + Py_DECREF (result); + PyErr_SetFromErrno (PyExc_OSError); + return NULL; + } + + for (addr = addrs; addr; addr = addr->ifa_next) { + char buffer[256]; + PyObject *pyaddr = NULL, *netmask = NULL, *braddr = NULL; + + if (strcmp (addr->ifa_name, ifname) != 0) + continue; + + /* We mark the interface as found, even if there are no addresses; + this results in sensible behaviour for these few cases. */ + found = TRUE; + + /* 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; + + if (string_from_sockaddr (addr->ifa_addr, buffer, sizeof (buffer)) == 0) + pyaddr = PyString_FromString (buffer); + + if (string_from_sockaddr (addr->ifa_netmask, buffer, sizeof (buffer)) == 0) + netmask = PyString_FromString (buffer); + + if (string_from_sockaddr (addr->ifa_broadaddr, buffer, sizeof (buffer)) == 0) + braddr = PyString_FromString (buffer); + + { + PyObject *dict = PyDict_New(); + + if (!dict) { + Py_XDECREF (pyaddr); + Py_XDECREF (netmask); + Py_XDECREF (braddr); + Py_DECREF (result); + freeifaddrs (addrs); + return NULL; + } + + if (pyaddr) + PyDict_SetItemString (dict, "addr", pyaddr); + if (netmask) + PyDict_SetItemString (dict, "netmask", netmask); + + if (braddr) { + if (addr->ifa_flags & (IFF_POINTOPOINT | IFF_LOOPBACK)) + PyDict_SetItemString (dict, "peer", braddr); + else + PyDict_SetItemString (dict, "broadcast", braddr); + } + + Py_XDECREF (pyaddr); + Py_XDECREF (netmask); + Py_XDECREF (braddr); + + if (!add_to_family (result, addr->ifa_addr->sa_family, dict)) { + Py_DECREF (result); + freeifaddrs (addrs); + return NULL; + } + } + } + + freeifaddrs (addrs); +#elif HAVE_SOCKET_IOCTLS + /* .. UNIX, with SIOC ioctls() ............................................ */ + + int sock = socket(AF_INET, SOCK_DGRAM, 0); + + if (sock < 0) { + Py_DECREF (result); + PyErr_SetFromErrno (PyExc_OSError); + return NULL; + } + + struct CNAME(ifreq) ifr; + PyObject *addr = NULL, *netmask = NULL, *braddr = NULL, *dstaddr = NULL; + int is_p2p = FALSE; + char buffer[256]; + + strncpy (ifr.CNAME(ifr_name), ifname, IFNAMSIZ); + +#if HAVE_SIOCGIFHWADDR + if (ioctl (sock, SIOCGIFHWADDR, &ifr) == 0) { + found = TRUE; + + if (string_from_sockaddr ((struct sockaddr *)&ifr.CNAME(ifr_addr), buffer, sizeof (buffer)) == 0) { + PyObject *hwaddr = PyString_FromString (buffer); + PyObject *dict = PyDict_New (); + + if (!hwaddr || !dict) { + Py_XDECREF (hwaddr); + Py_XDECREF (dict); + Py_XDECREF (result); + close (sock); + return NULL; + } + + PyDict_SetItemString (dict, "addr", hwaddr); + Py_DECREF (hwaddr); + + if (!add_to_family (result, AF_LINK, dict)) { + Py_DECREF (result); + close (sock); + return NULL; + } + } + } +#endif + +#if HAVE_SIOCGIFADDR +#if HAVE_SIOCGLIFNUM + if (ioctl (sock, SIOCGLIFADDR, &ifr) == 0) { +#else + if (ioctl (sock, SIOCGIFADDR, &ifr) == 0) { +#endif + found = TRUE; + + if (string_from_sockaddr ((struct sockaddr *)&ifr.CNAME(ifr_addr), buffer, sizeof (buffer)) == 0) + addr = PyString_FromString (buffer); + } +#endif + +#if HAVE_SIOCGIFNETMASK +#if HAVE_SIOCGLIFNUM + if (ioctl (sock, SIOCGLIFNETMASK, &ifr) == 0) { +#else + if (ioctl (sock, SIOCGIFNETMASK, &ifr) == 0) { +#endif + found = TRUE; + + if (string_from_sockaddr ((struct sockaddr *)&ifr.CNAME(ifr_addr), buffer, sizeof (buffer)) == 0) + netmask = PyString_FromString (buffer); + } +#endif + +#if HAVE_SIOCGIFFLAGS +#if HAVE_SIOCGLIFNUM + if (ioctl (sock, SIOCGLIFFLAGS, &ifr) == 0) { +#else + if (ioctl (sock, SIOCGIFFLAGS, &ifr) == 0) { +#endif + found = TRUE; + + 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 + found = TRUE; + + if (string_from_sockaddr ((struct sockaddr *)&ifr.CNAME(ifr_addr), buffer, sizeof (buffer)) == 0) + braddr = PyString_FromString (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 + found = TRUE; + + if (string_from_sockaddr ((struct sockaddr *)&ifr.CNAME(ifr_addr), buffer, sizeof (buffer)) == 0) + dstaddr = PyString_FromString (buffer); + } +#endif + + { + PyObject *dict = PyDict_New(); + + if (!dict) { + Py_XDECREF (addr); + Py_XDECREF (netmask); + Py_XDECREF (braddr); + Py_XDECREF (dstaddr); + Py_DECREF (result); + close (sock); + return NULL; + } + + if (addr) + PyDict_SetItemString (dict, "addr", addr); + if (netmask) + PyDict_SetItemString (dict, "netmask", netmask); + if (braddr) + PyDict_SetItemString (dict, "broadcast", braddr); + if (dstaddr) + PyDict_SetItemString (dict, "peer", dstaddr); + + Py_XDECREF (addr); + Py_XDECREF (netmask); + Py_XDECREF (braddr); + Py_XDECREF (dstaddr); + + if (!add_to_family (result, AF_INET, dict)) { + Py_DECREF (result); + close (sock); + return NULL; + } + } + + close (sock); +#endif /* HAVE_SOCKET_IOCTLS */ + + if (found) + return result; + else { + Py_DECREF (result); + PyErr_SetString (PyExc_ValueError, + "You must specify a valid interface name."); + return NULL; + } +} + +/* -- interfaces() ---------------------------------------------------------- */ + +static PyObject * +interfaces (PyObject *self) +{ + PyObject *result; + +#if defined(WIN32) + /* .. Win32 ............................................................... */ + + PIP_ADAPTER_ADDRESSES pAdapterAddresses = NULL, pInfo = NULL; + ULONG ulBufferLength = 0; + DWORD dwRet; + + /* First, retrieve the adapter information */ + do { + dwRet = GetAdaptersAddresses(AF_UNSPEC, 0, NULL, + pAdapterAddresses, &ulBufferLength); + + if (dwRet == ERROR_BUFFER_OVERFLOW) { + if (pAdapterAddresses) + free (pAdapterAddresses); + pAdapterAddresses = (PIP_ADAPTER_ADDRESSES)malloc (ulBufferLength); + + if (!pAdapterAddresses) { + PyErr_SetString (PyExc_MemoryError, "Not enough memory"); + return NULL; + } + } + } while (dwRet == ERROR_BUFFER_OVERFLOW); + + /* If we failed, then fail in Python too */ + if (dwRet != ERROR_SUCCESS && dwRet != ERROR_NO_DATA) { + if (pAdapterAddresses) + free (pAdapterAddresses); + + PyErr_SetString (PyExc_OSError, + "Unable to obtain adapter information."); + return NULL; + } + + result = PyList_New(0); + + if (dwRet == ERROR_NO_DATA) { + free (pAdapterAddresses); + return result; + } + + for (pInfo = pAdapterAddresses; pInfo; pInfo = pInfo->Next) { + PyObject *ifname = (PyObject *)PyString_FromString (pInfo->AdapterName); + + PyList_Append (result, ifname); + Py_DECREF (ifname); + } + + free (pAdapterAddresses); +#elif HAVE_GETIFADDRS + /* .. UNIX, with getifaddrs() ............................................. */ + + const char *prev_name = NULL; + struct ifaddrs *addrs = NULL; + struct ifaddrs *addr = NULL; + + result = PyList_New (0); + + if (getifaddrs (&addrs) < 0) { + Py_DECREF (result); + PyErr_SetFromErrno (PyExc_OSError); + return NULL; + } + + for (addr = addrs; addr; addr = addr->ifa_next) { + if (!prev_name || strncmp (addr->ifa_name, prev_name, IFNAMSIZ) != 0) { + PyObject *ifname = PyString_FromString (addr->ifa_name); + + if (!PySequence_Contains (result, ifname)) + PyList_Append (result, ifname); + Py_DECREF (ifname); + prev_name = addr->ifa_name; + } + } + + freeifaddrs (addrs); +#elif HAVE_SIOCGIFCONF + /* .. UNIX, with SIOC ioctl()s ............................................ */ + + const char *prev_name = NULL; + int fd = socket (AF_INET, SOCK_DGRAM, 0); + struct CNAME(ifconf) ifc; + int len = -1; + + if (fd < 0) { + PyErr_SetFromErrno (PyExc_OSError); + return NULL; + } + + // Try to find out how much space we need +#if HAVE_SIOCGSIZIFCONF + if (ioctl (fd, SIOCGSIZIFCONF, &len) < 0) + len = -1; +#elif HAVE_SIOCGLIFNUM + { 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) = (int)(len * sizeof (struct CNAME(ifreq))); + ifc.CNAME(ifc_buf) = malloc (ifc.CNAME(ifc_len)); + + if (!ifc.CNAME(ifc_buf)) { + PyErr_SetString (PyExc_MemoryError, "Not enough memory"); + close (fd); + return NULL; + } + +#if HAVE_SIOCGLIFNUM + if (ioctl (fd, SIOCGLIFCONF, &ifc) < 0) { +#else + if (ioctl (fd, SIOCGIFCONF, &ifc) < 0) { +#endif + free (ifc.CNAME(ifc_req)); + PyErr_SetFromErrno (PyExc_OSError); + close (fd); + return NULL; + } + + result = PyList_New (0); + struct CNAME(ifreq) *pfreq = ifc.CNAME(ifc_req); + struct CNAME(ifreq) *pfreqend = (struct CNAME(ifreq) *)((char *)pfreq + + ifc.CNAME(ifc_len)); + while (pfreq < pfreqend) { + if (!prev_name || strncmp (prev_name, pfreq->CNAME(ifr_name), IFNAMSIZ) != 0) { + PyObject *name = PyString_FromString (pfreq->CNAME(ifr_name)); + + if (!PySequence_Contains (result, name)) + PyList_Append (result, name); + Py_XDECREF (name); + + prev_name = pfreq->CNAME(ifr_name); + } + +#if !HAVE_SOCKADDR_SA_LEN + ++pfreq; +#else + /* On some platforms, the ifreq struct can *grow*(!) if the socket address + is very long. Mac OS X is such a platform. */ + { + size_t len = sizeof (struct CNAME(ifreq)); + if (pfreq->ifr_addr.sa_len > sizeof (struct sockaddr)) + len = len - sizeof (struct sockaddr) + pfreq->ifr_addr.sa_len; + pfreq = (struct CNAME(ifreq) *)((char *)pfreq + len); + } +#endif + } + + free (ifc.CNAME(ifc_buf)); + close (fd); +#endif /* HAVE_SIOCGIFCONF */ + + return result; +} + +/* -- gateways() ------------------------------------------------------------ */ + +static PyObject * +gateways (PyObject *self) +{ + PyObject *result, *defaults; + +#if defined(WIN32) + /* .. Win32 ............................................................... */ + + /* We try to access GetIPForwardTable2() and FreeMibTable() through + function pointers so that this code will still run on machines + running Windows versions prior to Vista. On those systems, we + fall back to the older GetIPForwardTable() API (and can only find + IPv4 gateways as a result). + + We also fall back to the older API if the newer code fails for + some reason. */ + + HANDLE hIpHelper; + typedef NTSTATUS (WINAPI *PGETIPFORWARDTABLE2)(ADDRESS_FAMILY Family, + PMIB_IPFORWARD_TABLE2 *pTable); + typedef VOID (WINAPI *PFREEMIBTABLE)(PVOID Memory); + + PGETIPFORWARDTABLE2 pGetIpForwardTable2; + PFREEMIBTABLE pFreeMibTable; + + hIpHelper = GetModuleHandle (TEXT("iphlpapi.dll")); + + result = NULL; + pGetIpForwardTable2 = (PGETIPFORWARDTABLE2) + GetProcAddress (hIpHelper, "GetIpForwardTable2"); + pFreeMibTable = (PFREEMIBTABLE) + GetProcAddress (hIpHelper, "FreeMibTable"); + + if (pGetIpForwardTable2) { + PMIB_IPFORWARD_TABLE2 table; + DWORD dwErr = pGetIpForwardTable2 (AF_UNSPEC, &table); + + if (dwErr == NO_ERROR) { + DWORD n; + BOOL bFirstInet = TRUE, bFirstInet6 = TRUE; + + result = PyDict_New(); + defaults = PyDict_New(); + PyDict_SetItemString (result, "default", defaults); + Py_DECREF(defaults); + + /* This prevents a crash on PyPy */ + defaults = PyDict_GetItemString (result, "default"); + + for (n = 0; n < table->NumEntries; ++n) { + MIB_IFROW ifRow; + PyObject *ifname; + PyObject *gateway; + PyObject *isdefault; + PyObject *tuple, *deftuple = NULL; + char gwbuf[256]; + WCHAR *pwcsName; + DWORD dwFamily = table->Table[n].NextHop.si_family; + BOOL bFirst; + DWORD dwLen; + INT iRet; + + if (table->Table[n].DestinationPrefix.PrefixLength) + continue; + + switch (dwFamily) { + case AF_INET: + if (!table->Table[n].NextHop.Ipv4.sin_addr.s_addr) + continue; + break; + case AF_INET6: + if (memcmp (&table->Table[n].NextHop.Ipv6.sin6_addr, + &in6addr_any, + sizeof (struct in6_addr)) == 0) + continue; + break; + default: + continue; + } + + memset (&ifRow, 0, sizeof (ifRow)); + ifRow.dwIndex = table->Table[n].InterfaceIndex; + if (GetIfEntry (&ifRow) != NO_ERROR) + continue; + + dwLen = sizeof (gwbuf); + iRet = WSAAddressToStringA ((SOCKADDR *)&table->Table[n].NextHop, + sizeof (table->Table[n].NextHop), + NULL, + gwbuf, + &dwLen); + + if (iRet != NO_ERROR) + continue; + + /* Strip the prefix from the interface name */ + pwcsName = ifRow.wszName; + if (_wcsnicmp (L"\\DEVICE\\TCPIP_", pwcsName, 14) == 0) + pwcsName += 14; + + switch (dwFamily) { + case AF_INET: + bFirst = bFirstInet; + bFirstInet = FALSE; + break; + case AF_INET6: + bFirst = bFirstInet6; + bFirstInet6 = FALSE; + break; + } + + ifname = PyUnicode_FromUnicode (pwcsName, wcslen (pwcsName)); + gateway = PyString_FromString (gwbuf); + isdefault = bFirst ? Py_True : Py_False; + + tuple = PyTuple_Pack (3, gateway, ifname, isdefault); + + if (PyObject_IsTrue (isdefault)) + deftuple = PyTuple_Pack (2, gateway, ifname); + + Py_DECREF (gateway); + Py_DECREF (ifname); + + if (tuple && !add_to_family (result, dwFamily, tuple)) { + Py_DECREF (deftuple); + Py_DECREF (result); + free (table); + return NULL; + } + + if (deftuple) { + PyObject *pyfamily = PyInt_FromLong (dwFamily); + + PyDict_SetItem (defaults, pyfamily, deftuple); + + Py_DECREF (pyfamily); + Py_DECREF (deftuple); + } + } + + pFreeMibTable (table); + } + } + + if (!result) { + PMIB_IPFORWARDTABLE table = NULL; + DWORD dwRet; + DWORD dwSize = 0; + DWORD n; + BOOL bFirst = TRUE; + + do { + dwRet = GetIpForwardTable (table, &dwSize, FALSE); + + if (dwRet == ERROR_INSUFFICIENT_BUFFER) { + PMIB_IPFORWARDTABLE tbl = (PMIB_IPFORWARDTABLE)realloc (table, dwSize); + + if (!tbl) { + free (table); + PyErr_NoMemory(); + return NULL; + } + + table = tbl; + } + } while (dwRet == ERROR_INSUFFICIENT_BUFFER); + + if (dwRet != NO_ERROR) { + free (table); + PyErr_SetFromWindowsErr (dwRet); + return NULL; + } + + result = PyDict_New(); + defaults = PyDict_New(); + PyDict_SetItemString (result, "default", defaults); + Py_DECREF(defaults); + + /* This prevents a crash on PyPy */ + defaults = PyDict_GetItemString (result, "default"); + + for (n = 0; n < table->dwNumEntries; ++n) { + MIB_IFROW ifRow; + PyObject *ifname; + PyObject *gateway; + PyObject *isdefault; + PyObject *tuple, *deftuple = NULL; + DWORD dwGateway; + char gwbuf[16]; + WCHAR *pwcsName; + + if (table->table[n].dwForwardDest + || !table->table[n].dwForwardNextHop + || table->table[n].dwForwardType != MIB_IPROUTE_TYPE_INDIRECT) + continue; + + memset (&ifRow, 0, sizeof (ifRow)); + ifRow.dwIndex = table->table[n].dwForwardIfIndex; + if (GetIfEntry (&ifRow) != NO_ERROR) + continue; + + dwGateway = ntohl (table->table[n].dwForwardNextHop); + + sprintf (gwbuf, "%u.%u.%u.%u", + (dwGateway >> 24) & 0xff, + (dwGateway >> 16) & 0xff, + (dwGateway >> 8) & 0xff, + dwGateway & 0xff); + + /* Strip the prefix from the interface name */ + pwcsName = ifRow.wszName; + if (_wcsnicmp (L"\\DEVICE\\TCPIP_", pwcsName, 14) == 0) + pwcsName += 14; + + ifname = PyUnicode_FromUnicode (pwcsName, wcslen (pwcsName)); + gateway = PyString_FromString (gwbuf); + isdefault = bFirst ? Py_True : Py_False; + bFirst = FALSE; + + tuple = PyTuple_Pack (3, gateway, ifname, isdefault); + + if (PyObject_IsTrue (isdefault)) + deftuple = PyTuple_Pack (2, gateway, ifname); + + Py_DECREF (gateway); + Py_DECREF (ifname); + + if (tuple && !add_to_family (result, AF_INET, tuple)) { + Py_DECREF (deftuple); + Py_DECREF (result); + free (table); + return NULL; + } + + if (deftuple) { + PyObject *pyfamily = PyInt_FromLong (AF_INET); + + PyDict_SetItem (defaults, pyfamily, deftuple); + + Py_DECREF (pyfamily); + Py_DECREF (deftuple); + } + } + } +#elif defined(HAVE_PF_NETLINK) + /* .. Linux (PF_NETLINK socket) ........................................... */ + + /* PF_NETLINK is pretty poorly documented and it looks to be quite easy to + get wrong. This *appears* to be the right way to do it, even though a + lot of the code out there on the 'Net is very different! */ + + struct routing_msg { + struct nlmsghdr hdr; + struct rtmsg rt; + char data[0]; + } *pmsg, *msgbuf; + int s; + int seq = 0; + ssize_t ret; + struct sockaddr_nl sanl; + static const struct sockaddr_nl sanl_kernel = { .nl_family = AF_NETLINK }; + socklen_t sanl_len; + int pagesize = getpagesize(); + int bufsize = pagesize < 8192 ? pagesize : 8192; + int is_multi = 0; + int interrupted = 0; + + result = PyDict_New(); + defaults = PyDict_New(); + PyDict_SetItemString (result, "default", defaults); + Py_DECREF (defaults); + + /* This prevents a crash on PyPy */ + defaults = PyDict_GetItemString (result, "default"); + + msgbuf = (struct routing_msg *)malloc (bufsize); + + if (!msgbuf) { + PyErr_NoMemory (); + Py_DECREF (result); + return NULL; + } + + s = socket (PF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + + if (s < 0) { + PyErr_SetFromErrno (PyExc_OSError); + Py_DECREF (result); + free (msgbuf); + return NULL; + } + + sanl.nl_family = AF_NETLINK; + sanl.nl_groups = 0; + sanl.nl_pid = 0; + + if (bind (s, (struct sockaddr *)&sanl, sizeof (sanl)) < 0) { + PyErr_SetFromErrno (PyExc_OSError); + Py_DECREF (result); + free (msgbuf); + close (s); + return NULL; + } + + sanl_len = sizeof (sanl); + if (getsockname (s, (struct sockaddr *)&sanl, &sanl_len) < 0) { + PyErr_SetFromErrno (PyExc_OSError); + Py_DECREF (result); + free (msgbuf); + close (s); + return NULL; + } + + do { + interrupted = 0; + + pmsg = msgbuf; + memset (pmsg, 0, sizeof (struct routing_msg)); + pmsg->hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); + pmsg->hdr.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST; + pmsg->hdr.nlmsg_seq = ++seq; + pmsg->hdr.nlmsg_type = RTM_GETROUTE; + pmsg->hdr.nlmsg_pid = 0; + + pmsg->rt.rtm_family = 0; + + if (sendto (s, pmsg, pmsg->hdr.nlmsg_len, 0, + (struct sockaddr *)&sanl_kernel, sizeof(sanl_kernel)) < 0) { + PyErr_SetFromErrno (PyExc_OSError); + Py_DECREF (result); + free (msgbuf); + close (s); + return NULL; + } + + do { + struct sockaddr_nl sanl_from; + struct iovec iov = { msgbuf, bufsize }; + struct msghdr msghdr = { + &sanl_from, + sizeof(sanl_from), + &iov, + 1, + NULL, + 0, + 0 + }; + int nllen; + + ret = recvmsg (s, &msghdr, 0); + + if (msghdr.msg_flags & MSG_TRUNC) { + PyErr_SetString (PyExc_OSError, "netlink message truncated"); + Py_DECREF (result); + free (msgbuf); + close (s); + return NULL; + } + + if (ret < 0) { + PyErr_SetFromErrno (PyExc_OSError); + Py_DECREF (result); + free (msgbuf); + close (s); + return NULL; + } + + nllen = ret; + pmsg = msgbuf; + while (NLMSG_OK (&pmsg->hdr, nllen)) { + void *dst = NULL; + void *gw = NULL; + int ifndx = -1; + struct rtattr *attrs, *attr; + int len; + + /* Ignore messages not for us */ + if (pmsg->hdr.nlmsg_seq != seq || pmsg->hdr.nlmsg_pid != sanl.nl_pid) + goto next; + + /* This is only defined on Linux kernel versions 3.1 and higher */ +#ifdef NLM_F_DUMP_INTR + if (pmsg->hdr.nlmsg_flags & NLM_F_DUMP_INTR) { + /* The dump was interrupted by a signal; we need to go round again */ + interrupted = 1; + is_multi = 0; + break; + } +#endif + + is_multi = pmsg->hdr.nlmsg_flags & NLM_F_MULTI; + + if (pmsg->hdr.nlmsg_type == NLMSG_DONE) { + is_multi = interrupted = 0; + break; + } + + if (pmsg->hdr.nlmsg_type == NLMSG_ERROR) { + struct nlmsgerr *perr = (struct nlmsgerr *)&pmsg->rt; + errno = -perr->error; + PyErr_SetFromErrno (PyExc_OSError); + Py_DECREF (result); + free (msgbuf); + close (s); + return NULL; + } + + attr = attrs = RTM_RTA(&pmsg->rt); + len = RTM_PAYLOAD(&pmsg->hdr); + + while (RTA_OK(attr, len)) { + switch (attr->rta_type) { + case RTA_GATEWAY: + gw = RTA_DATA(attr); + break; + case RTA_DST: + dst = RTA_DATA(attr); + break; + case RTA_OIF: + ifndx = *(int *)RTA_DATA(attr); + break; + default: + break; + } + + attr = RTA_NEXT(attr, len); + } + + /* We're looking for gateways with no destination */ + if (!dst && gw && ifndx >= 0) { + char buffer[256]; + char ifnamebuf[IF_NAMESIZE]; + char *ifname; + const char *addr; + PyObject *pyifname; + PyObject *pyaddr; + PyObject *isdefault; + PyObject *tuple = NULL, *deftuple = NULL; + + ifname = if_indextoname (ifndx, ifnamebuf); + + if (!ifname) + goto next; + + addr = inet_ntop (pmsg->rt.rtm_family, gw, buffer, sizeof (buffer)); + + if (!addr) + goto next; + + /* We set isdefault to True if this route came from the main table; + this should correspond with the way most people set up alternate + routing tables on Linux. */ + + isdefault = pmsg->rt.rtm_table == RT_TABLE_MAIN ? Py_True : Py_False; + pyifname = PyString_FromString (ifname); + pyaddr = PyString_FromString (buffer); + + tuple = PyTuple_Pack (3, pyaddr, pyifname, isdefault); + + if (PyObject_IsTrue (isdefault)) + deftuple = PyTuple_Pack (2, pyaddr, pyifname); + + Py_DECREF (pyaddr); + Py_DECREF (pyifname); + + if (tuple && !add_to_family (result, pmsg->rt.rtm_family, tuple)) { + Py_XDECREF (deftuple); + Py_DECREF (result); + free (msgbuf); + close (s); + return NULL; + } + + if (deftuple) { + PyObject *pyfamily = PyInt_FromLong (pmsg->rt.rtm_family); + + PyDict_SetItem (defaults, pyfamily, deftuple); + + Py_DECREF (pyfamily); + Py_DECREF (deftuple); + } + } + + next: + pmsg = (struct routing_msg *)NLMSG_NEXT(&pmsg->hdr, nllen); + } + } while (is_multi); + } while (interrupted); + + free (msgbuf); + close (s); +#elif defined(HAVE_SYSCTL_CTL_NET) + /* .. UNIX, via sysctl() .................................................. */ + + int mib[] = { CTL_NET, PF_ROUTE, 0, 0, NET_RT_FLAGS, + RTF_UP | RTF_GATEWAY }; + size_t len; + char *buffer = NULL, *ptr, *end; + int ret; + char ifnamebuf[IF_NAMESIZE]; + char *ifname; + + result = PyDict_New(); + defaults = PyDict_New(); + PyDict_SetItemString (result, "default", defaults); + Py_DECREF (defaults); + + /* This prevents a crash on PyPy */ + defaults = PyDict_GetItemString (result, "default"); + + /* Remembering that the routing table may change while we're reading it, + we need to do this in a loop until we succeed. */ + do { + if (sysctl (mib, 6, 0, &len, 0, 0) < 0) { + PyErr_SetFromErrno (PyExc_OSError); + free (buffer); + Py_DECREF (result); + return NULL; + } + + ptr = realloc(buffer, len); + if (!ptr) { + PyErr_NoMemory(); + free (buffer); + Py_DECREF (result); + return NULL; + } + + buffer = ptr; + + ret = sysctl (mib, 6, buffer, &len, 0, 0); + } while (ret != 0 || errno == ENOMEM || errno == EINTR); + + if (ret < 0) { + PyErr_SetFromErrno (PyExc_OSError); + free (buffer); + Py_DECREF (result); + return NULL; + } + + ptr = buffer; + end = buffer + len; + + while (ptr + sizeof (struct rt_msghdr) <= end) { + struct rt_msghdr *msg = (struct rt_msghdr *)ptr; + char *msgend = (char *)msg + msg->rtm_msglen; + int addrs = msg->rtm_addrs; + int addr = RTA_DST; + PyObject *pyifname; + + if (msgend > end) + break; + + ifname = if_indextoname (msg->rtm_index, ifnamebuf); + + if (!ifname) { + ptr = msgend; + continue; + } + + pyifname = PyString_FromString (ifname); + + ptr = (char *)(msg + 1); + while (ptr + sizeof (struct sockaddr) <= msgend && addrs) { + struct sockaddr *sa = (struct sockaddr *)ptr; + int len = SA_LEN(sa); + + if (!len) + len = 4; + else + len = (len + 3) & ~3; + + if (ptr + len > msgend) + break; + + while (!(addrs & addr)) + addr <<= 1; + + addrs &= ~addr; + + if (addr == RTA_DST) { + if (sa->sa_family == AF_INET) { + struct sockaddr_in *sin = (struct sockaddr_in *)sa; + if (sin->sin_addr.s_addr != INADDR_ANY) + break; +#ifdef AF_INET6 + } else if (sa->sa_family == AF_INET6) { + struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa; + if (memcmp (&sin6->sin6_addr, &in6addr_any, sizeof (in6addr_any)) != 0) + break; +#endif + } else { + break; + } + } + + if (addr == RTA_GATEWAY) { + char buffer[256]; + PyObject *tuple = NULL; + PyObject *deftuple = NULL; + + if (string_from_sockaddr (sa, buffer, sizeof(buffer)) == 0) { + PyObject *pyaddr = PyString_FromString (buffer); +#ifdef RTF_IFSCOPE + PyObject *isdefault = PyBool_FromLong (!(msg->rtm_flags & RTF_IFSCOPE)); +#else + Py_INCREF(Py_True); + PyObject *isdefault = Py_True; +#endif + tuple = PyTuple_Pack (3, pyaddr, pyifname, isdefault); + + if (PyObject_IsTrue (isdefault)) + deftuple = PyTuple_Pack (2, pyaddr, pyifname); + + Py_DECREF (pyaddr); + Py_DECREF (isdefault); + } + + if (tuple && !add_to_family (result, sa->sa_family, tuple)) { + Py_DECREF (deftuple); + Py_DECREF (result); + Py_DECREF (pyifname); + free (buffer); + return NULL; + } + + if (deftuple) { + PyObject *pyfamily = PyInt_FromLong (sa->sa_family); + + PyDict_SetItem (defaults, pyfamily, deftuple); + + Py_DECREF (pyfamily); + Py_DECREF (deftuple); + } + } + + /* These are aligned on a 4-byte boundary */ + ptr += len; + } + + Py_DECREF (pyifname); + ptr = msgend; + } + + free (buffer); +#elif defined(HAVE_PF_ROUTE) + /* .. UNIX, via PF_ROUTE socket ........................................... */ + + /* The PF_ROUTE code will only retrieve gateway information for AF_INET and + AF_INET6. This is because it would need to loop through all possible + values, and the messages it needs to send in each case are potentially + different. It is also very likely to return a maximum of one gateway + in each case (since we can't read the entire routing table this way, we + can only ask about routes). */ + + int pagesize = getpagesize(); + int bufsize = pagesize < 8192 ? 8192 : pagesize; + struct rt_msghdr *pmsg; + int s; + int seq = 0; + int pid = getpid(); + ssize_t ret; + struct sockaddr_in *sin_dst, *sin_netmask; + struct sockaddr_dl *sdl_ifp; + struct sockaddr_in6 *sin6_dst; + size_t msglen; + char ifnamebuf[IF_NAMESIZE]; + char *ifname; + int skip; + + result = PyDict_New(); + defaults = PyDict_New(); + PyDict_SetItemString (result, "default", defaults); + Py_DECREF(defaults); + + pmsg = (struct rt_msghdr *)malloc (bufsize); + + if (!pmsg) { + PyErr_NoMemory(); + return NULL; + } + + s = socket (PF_ROUTE, SOCK_RAW, 0); + + if (s < 0) { + PyErr_SetFromErrno (PyExc_OSError); + free (pmsg); + return NULL; + } + + msglen = (sizeof (struct rt_msghdr) + + 2 * sizeof (struct sockaddr_in) + + sizeof (struct sockaddr_dl)); + memset (pmsg, 0, msglen); + + /* AF_INET first */ + pmsg->rtm_msglen = msglen; + pmsg->rtm_type = RTM_GET; + pmsg->rtm_index = 0; + pmsg->rtm_flags = RTF_UP | RTF_GATEWAY; + pmsg->rtm_version = RTM_VERSION; + pmsg->rtm_seq = ++seq; + pmsg->rtm_pid = 0; + pmsg->rtm_addrs = RTA_DST | RTA_NETMASK | RTA_IFP; + + sin_dst = (struct sockaddr_in *)(pmsg + 1); + sin_netmask = (struct sockaddr_in *)(sin_dst + 1); + sdl_ifp = (struct sockaddr_dl *)(sin_netmask + 1); + + sin_dst->sin_family = AF_INET; + sin_netmask->sin_family = AF_INET; + sdl_ifp->sdl_family = AF_LINK; + +#if HAVE_SOCKADDR_SA_LEN + sin_dst->sin_len = sizeof (struct sockaddr_in); + sin_netmask->sin_len = sizeof (struct sockaddr_in); + sdl_ifp->sdl_len = sizeof (struct sockaddr_dl); +#endif + + skip = 0; + if (send (s, pmsg, msglen, 0) < 0) { + if (errno == ESRCH) + skip = 1; + else { + PyErr_SetFromErrno (PyExc_OSError); + close (s); + free (pmsg); + return NULL; + } + } + + while (!skip && !(pmsg->rtm_flags & RTF_DONE)) { + char *ptr; + char *msgend; + int addrs; + int addr; + struct sockaddr_in *dst = NULL; + struct sockaddr_in *gw = NULL; + struct sockaddr_dl *ifp = NULL; + PyObject *tuple = NULL; + PyObject *deftuple = NULL; + + do { + ret = recv (s, pmsg, bufsize, 0); + } while ((ret < 0 && errno == EINTR) + || (ret > 0 && (pmsg->rtm_seq != seq || pmsg->rtm_pid != pid))); + + if (ret < 0) { + PyErr_SetFromErrno (PyExc_OSError); + close (s); + free (pmsg); + return NULL; + } + + if (pmsg->rtm_errno != 0) { + if (pmsg->rtm_errno == ESRCH) + skip = 1; + else { + errno = pmsg->rtm_errno; + PyErr_SetFromErrno (PyExc_OSError); + close (s); + free (pmsg); + return NULL; + } + } + + if (skip) + break; + + ptr = (char *)(pmsg + 1); + msgend = (char *)pmsg + pmsg->rtm_msglen; + addrs = pmsg->rtm_addrs; + addr = RTA_DST; + while (ptr + sizeof (struct sockaddr) <= msgend && addrs) { + struct sockaddr *sa = (struct sockaddr *)ptr; + int len = SA_LEN(sa); + + if (!len) + len = 4; + else + len = (len + 3) & ~3; + + if (ptr + len > msgend) + break; + + while (!(addrs & addr)) + addr <<= 1; + + addrs &= ~addr; + + switch (addr) { + case RTA_DST: + dst = (struct sockaddr_in *)sa; + break; + case RTA_GATEWAY: + gw = (struct sockaddr_in *)sa; + break; + case RTA_IFP: + ifp = (struct sockaddr_dl *)sa; + break; + } + + ptr += len; + } + + if ((dst && dst->sin_family != AF_INET) + || (gw && gw->sin_family != AF_INET) + || (ifp && ifp->sdl_family != AF_LINK)) { + dst = gw = NULL; + ifp = NULL; + } + + if (dst && dst->sin_addr.s_addr == INADDR_ANY) + dst = NULL; + + if (!dst && gw && ifp) { + char buffer[256]; + + if (ifp->sdl_index) + ifname = if_indextoname (ifp->sdl_index, ifnamebuf); + else { + memcpy (ifnamebuf, ifp->sdl_data, ifp->sdl_nlen); + ifnamebuf[ifp->sdl_nlen] = '\0'; + ifname = ifnamebuf; + } + + if (string_from_sockaddr ((struct sockaddr *)gw, + buffer, sizeof(buffer)) == 0) { + PyObject *pyifname = PyString_FromString (ifname); + PyObject *pyaddr = PyString_FromString (buffer); +#ifdef RTF_IFSCOPE + PyObject *isdefault = PyBool_FromLong (!(pmsg->rtm_flags & RTF_IFSCOPE)); +#else + PyObject *isdefault = Py_True; + Py_INCREF(isdefault); +#endif + + tuple = PyTuple_Pack (3, pyaddr, pyifname, isdefault); + + if (PyObject_IsTrue (isdefault)) + deftuple = PyTuple_Pack (2, pyaddr, pyifname); + + Py_DECREF (pyaddr); + Py_DECREF (pyifname); + Py_DECREF (isdefault); + } + + if (tuple && !add_to_family (result, AF_INET, tuple)) { + Py_DECREF (deftuple); + Py_DECREF (result); + free (pmsg); + return NULL; + } + + if (deftuple) { + PyObject *pyfamily = PyInt_FromLong (AF_INET); + + PyDict_SetItem (defaults, pyfamily, deftuple); + + Py_DECREF (pyfamily); + Py_DECREF (deftuple); + } + } + } + + /* The code below is very similar to, but not identical to, the code above. + We could probably refactor some of it, but take care---there are subtle + differences! */ + +#ifdef AF_INET6 + /* AF_INET6 now */ + msglen = (sizeof (struct rt_msghdr) + + sizeof (struct sockaddr_in6) + + sizeof (struct sockaddr_dl)); + memset (pmsg, 0, msglen); + + pmsg->rtm_msglen = msglen; + pmsg->rtm_type = RTM_GET; + pmsg->rtm_index = 0; + pmsg->rtm_flags = RTF_UP | RTF_GATEWAY; + pmsg->rtm_version = RTM_VERSION; + pmsg->rtm_seq = ++seq; + pmsg->rtm_pid = 0; + pmsg->rtm_addrs = RTA_DST | RTA_IFP; + + sin6_dst = (struct sockaddr_in6 *)(pmsg + 1); + sdl_ifp = (struct sockaddr_dl *)(sin6_dst + 1); + + sin6_dst->sin6_family = AF_INET6; + sin6_dst->sin6_addr = in6addr_any; + sdl_ifp->sdl_family = AF_LINK; + +#if HAVE_SOCKADDR_SA_LEN + sin6_dst->sin6_len = sizeof (struct sockaddr_in6); + sdl_ifp->sdl_len = sizeof (struct sockaddr_dl); +#endif + + skip = 0; + if (send (s, pmsg, msglen, 0) < 0) { + if (errno == ESRCH) + skip = 1; + else { + PyErr_SetFromErrno (PyExc_OSError); + close (s); + free (pmsg); + return NULL; + } + } + + while (!skip && !(pmsg->rtm_flags & RTF_DONE)) { + char *ptr; + char *msgend; + int addrs; + int addr; + struct sockaddr_in6 *dst = NULL; + struct sockaddr_in6 *gw = NULL; + struct sockaddr_dl *ifp = NULL; + PyObject *tuple = NULL; + PyObject *deftuple = NULL; + + do { + ret = recv (s, pmsg, bufsize, 0); + } while ((ret < 0 && errno == EINTR) + || (ret > 0 && (pmsg->rtm_seq != seq || pmsg->rtm_pid != pid))); + + if (ret < 0) { + PyErr_SetFromErrno (PyExc_OSError); + close (s); + free (pmsg); + return NULL; + } + + if (pmsg->rtm_errno != 0) { + if (pmsg->rtm_errno == ESRCH) + skip = 1; + else { + errno = pmsg->rtm_errno; + PyErr_SetFromErrno (PyExc_OSError); + close (s); + free (pmsg); + return NULL; + } + } + + if (skip) + break; + + ptr = (char *)(pmsg + 1); + msgend = (char *)pmsg + pmsg->rtm_msglen; + addrs = pmsg->rtm_addrs; + addr = RTA_DST; + while (ptr + sizeof (struct sockaddr) <= msgend && addrs) { + struct sockaddr *sa = (struct sockaddr *)ptr; + int len = SA_LEN(sa); + + if (!len) + len = 4; + else + len = (len + 3) & ~3; + + if (ptr + len > msgend) + break; + + while (!(addrs & addr)) + addr <<= 1; + + addrs &= ~addr; + + switch (addr) { + case RTA_DST: + dst = (struct sockaddr_in6 *)sa; + break; + case RTA_GATEWAY: + gw = (struct sockaddr_in6 *)sa; + break; + case RTA_IFP: + ifp = (struct sockaddr_dl *)sa; + break; + } + + ptr += len; + } + + if ((dst && dst->sin6_family != AF_INET6) + || (gw && gw->sin6_family != AF_INET6) + || (ifp && ifp->sdl_family != AF_LINK)) { + dst = gw = NULL; + ifp = NULL; + } + + if (dst && memcmp (&dst->sin6_addr, &in6addr_any, + sizeof(struct in6_addr)) == 0) + dst = NULL; + + if (!dst && gw && ifp) { + char buffer[256]; + + if (ifp->sdl_index) + ifname = if_indextoname (ifp->sdl_index, ifnamebuf); + else { + memcpy (ifnamebuf, ifp->sdl_data, ifp->sdl_nlen); + ifnamebuf[ifp->sdl_nlen] = '\0'; + ifname = ifnamebuf; + } + + if (string_from_sockaddr ((struct sockaddr *)gw, + buffer, sizeof(buffer)) == 0) { + PyObject *pyifname = PyString_FromString (ifname); + PyObject *pyaddr = PyString_FromString (buffer); +#ifdef RTF_IFSCOPE + PyObject *isdefault = PyBool_FromLong (!(pmsg->rtm_flags & RTF_IFSCOPE)); +#else + PyObject *isdefault = Py_True; + Py_INCREF (isdefault); +#endif + + tuple = PyTuple_Pack (3, pyaddr, pyifname, isdefault); + + if (PyObject_IsTrue (isdefault)) + deftuple = PyTuple_Pack (2, pyaddr, pyifname); + + Py_DECREF (pyaddr); + Py_DECREF (pyifname); + Py_DECREF (isdefault); + } + + if (tuple && !add_to_family (result, AF_INET6, tuple)) { + Py_DECREF (deftuple); + Py_DECREF (result); + free (pmsg); + return NULL; + } + + if (deftuple) { + PyObject *pyfamily = PyInt_FromLong (AF_INET6); + + PyDict_SetItem (defaults, pyfamily, deftuple); + + Py_DECREF (pyfamily); + Py_DECREF (deftuple); + } + } + } +#endif /* AF_INET6 */ + + free (pmsg); +#else + /* If we don't know how to implement this on your platform, we raise an + exception. */ + PyErr_SetString (PyExc_OSError, + "Unable to obtain gateway information on your platform."); +#endif + + return result; +} + +/* -- Python Module --------------------------------------------------------- */ + +static PyMethodDef methods[] = { + { "ifaddresses", (PyCFunction)ifaddrs, METH_VARARGS, + "Obtain information about the specified network interface.\n" +"\n" +"Returns a dict whose keys are equal to the address family constants,\n" +"e.g. netifaces.AF_INET, and whose values are a list of addresses in\n" +"that family that are attached to the network interface." }, + { "interfaces", (PyCFunction)interfaces, METH_NOARGS, + "Obtain a list of the interfaces available on this machine." }, + { "gateways", (PyCFunction)gateways, METH_NOARGS, + "Obtain a list of the gateways on this machine.\n" +"\n" +"Returns a dict whose keys are equal to the address family constants,\n" +"e.g. netifaces.AF_INET, and whose values are a list of tuples of the\n" +"format (<address>, <interface>, <is_default>).\n" +"\n" +"There is also a special entry with the key 'default', which you can use\n" +"to quickly obtain the default gateway for a particular address family.\n" +"\n" +"There may in general be multiple gateways; different address\n" +"families may have different gateway settings (e.g. AF_INET vs AF_INET6)\n" +"and on some systems it's also possible to have interface-specific\n" +"default gateways.\n" }, + { NULL, NULL, 0, NULL } +}; + +MODULE_DEF("netifaces", NULL, methods); + +MODULE_INIT(netifaces) +{ + PyObject *address_family_dict; + PyObject *m; + +#ifdef WIN32 + WSADATA wsad; + + WSAStartup(MAKEWORD (2, 2), &wsad); +#endif + + MODULE_CREATE(m, "netifaces", NULL, methods); + if (!m) + return MODULE_ERROR; + + /* Address families (auto-detect using #ifdef) */ + address_family_dict = PyDict_New(); +#ifdef AF_UNSPEC + PyModule_AddIntConstant (m, "AF_UNSPEC", AF_UNSPEC); + PyDict_SetItem(address_family_dict, PyInt_FromLong(AF_UNSPEC), + PyString_FromString("AF_UNSPEC")); +#endif +#ifdef AF_UNIX + PyModule_AddIntConstant (m, "AF_UNIX", AF_UNIX); + PyDict_SetItem(address_family_dict, PyInt_FromLong(AF_UNIX), + PyString_FromString("AF_UNIX")); +#endif +#ifdef AF_FILE + PyModule_AddIntConstant (m, "AF_FILE", AF_FILE); + PyDict_SetItem(address_family_dict, PyInt_FromLong(AF_FILE), + PyString_FromString("AF_FILE")); +#endif +#ifdef AF_INET + PyModule_AddIntConstant (m, "AF_INET", AF_INET); + PyDict_SetItem(address_family_dict, PyInt_FromLong(AF_INET), + PyString_FromString("AF_INET")); +#endif +#ifdef AF_AX25 + PyModule_AddIntConstant (m, "AF_AX25", AF_AX25); + PyDict_SetItem(address_family_dict, PyInt_FromLong(AF_AX25), + PyString_FromString("AF_AX25")); +#endif +#ifdef AF_IMPLINK + PyModule_AddIntConstant (m, "AF_IMPLINK", AF_IMPLINK); + PyDict_SetItem(address_family_dict, PyInt_FromLong(AF_IMPLINK), + PyString_FromString("AF_IMPLINK")); +#endif +#ifdef AF_PUP + PyModule_AddIntConstant (m, "AF_PUP", AF_PUP); + PyDict_SetItem(address_family_dict, PyInt_FromLong(AF_PUP), + PyString_FromString("AF_PUP")); +#endif +#ifdef AF_CHAOS + PyModule_AddIntConstant (m, "AF_CHAOS", AF_CHAOS); + PyDict_SetItem(address_family_dict, PyInt_FromLong(AF_CHAOS), + PyString_FromString("AF_CHAOS")); +#endif +#ifdef AF_NS + PyModule_AddIntConstant (m, "AF_NS", AF_NS); + PyDict_SetItem(address_family_dict, PyInt_FromLong(AF_NS), + PyString_FromString("AF_NS")); +#endif +#ifdef AF_ISO + PyModule_AddIntConstant (m, "AF_ISO", AF_ISO); + PyDict_SetItem(address_family_dict, PyInt_FromLong(AF_ISO), + PyString_FromString("AF_ISO")); +#endif +#ifdef AF_ECMA + PyModule_AddIntConstant (m, "AF_ECMA", AF_ECMA); + PyDict_SetItem(address_family_dict, PyInt_FromLong(AF_ECMA), + PyString_FromString("AF_ECMA")); +#endif +#ifdef AF_DATAKIT + PyModule_AddIntConstant (m, "AF_DATAKIT", AF_DATAKIT); + PyDict_SetItem(address_family_dict, PyInt_FromLong(AF_DATAKIT), + PyString_FromString("AF_DATAKIT")); +#endif +#ifdef AF_CCITT + PyModule_AddIntConstant (m, "AF_CCITT", AF_CCITT); + PyDict_SetItem(address_family_dict, PyInt_FromLong(AF_CCITT), + PyString_FromString("AF_CCITT")); +#endif +#ifdef AF_SNA + PyModule_AddIntConstant (m, "AF_SNA", AF_SNA); + PyDict_SetItem(address_family_dict, PyInt_FromLong(AF_SNA), + PyString_FromString("AF_SNA")); +#endif +#ifdef AF_DECnet + PyModule_AddIntConstant (m, "AF_DECnet", AF_DECnet); + PyDict_SetItem(address_family_dict, PyInt_FromLong(AF_DECnet), + PyString_FromString("AF_DECnet")); +#endif +#ifdef AF_DLI + PyModule_AddIntConstant (m, "AF_DLI", AF_DLI); + PyDict_SetItem(address_family_dict, PyInt_FromLong(AF_DLI), + PyString_FromString("AF_DLI")); +#endif +#ifdef AF_LAT + PyModule_AddIntConstant (m, "AF_LAT", AF_LAT); + PyDict_SetItem(address_family_dict, PyInt_FromLong(AF_LAT), + PyString_FromString("AF_LAT")); +#endif +#ifdef AF_HYLINK + PyModule_AddIntConstant (m, "AF_HYLINK", AF_HYLINK); + PyDict_SetItem(address_family_dict, PyInt_FromLong(AF_HYLINK), + PyString_FromString("AF_HYLINK")); +#endif +#ifdef AF_APPLETALK + PyModule_AddIntConstant (m, "AF_APPLETALK", AF_APPLETALK); + PyDict_SetItem(address_family_dict, PyInt_FromLong(AF_APPLETALK), + PyString_FromString("AF_APPLETALK")); +#endif +#ifdef AF_ROUTE + PyModule_AddIntConstant (m, "AF_ROUTE", AF_ROUTE); + PyDict_SetItem(address_family_dict, PyInt_FromLong(AF_ROUTE), + PyString_FromString("AF_ROUTE")); +#endif +#ifdef AF_LINK + PyModule_AddIntConstant (m, "AF_LINK", AF_LINK); + PyDict_SetItem(address_family_dict, PyInt_FromLong(AF_LINK), + PyString_FromString("AF_LINK")); +#endif +#ifdef AF_PACKET + PyModule_AddIntConstant (m, "AF_PACKET", AF_PACKET); + PyDict_SetItem(address_family_dict, PyInt_FromLong(AF_PACKET), + PyString_FromString("AF_PACKET")); +#endif +#ifdef AF_COIP + PyModule_AddIntConstant (m, "AF_COIP", AF_COIP); + PyDict_SetItem(address_family_dict, PyInt_FromLong(AF_COIP), + PyString_FromString("AF_COIP")); +#endif +#ifdef AF_CNT + PyModule_AddIntConstant (m, "AF_CNT", AF_CNT); + PyDict_SetItem(address_family_dict, PyInt_FromLong(AF_CNT), + PyString_FromString("AF_CNT")); +#endif +#ifdef AF_IPX + PyModule_AddIntConstant (m, "AF_IPX", AF_IPX); + PyDict_SetItem(address_family_dict, PyInt_FromLong(AF_IPX), + PyString_FromString("AF_IPX")); +#endif +#ifdef AF_SIP + PyModule_AddIntConstant (m, "AF_SIP", AF_SIP); + PyDict_SetItem(address_family_dict, PyInt_FromLong(AF_SIP), + PyString_FromString("AF_SIP")); +#endif +#ifdef AF_NDRV + PyModule_AddIntConstant (m, "AF_NDRV", AF_NDRV); + PyDict_SetItem(address_family_dict, PyInt_FromLong(AF_NDRV), + PyString_FromString("AF_NDRV")); +#endif +#ifdef AF_ISDN + PyModule_AddIntConstant (m, "AF_ISDN", AF_ISDN); + PyDict_SetItem(address_family_dict, PyInt_FromLong(AF_ISDN), + PyString_FromString("AF_ISDN")); +#endif +#ifdef AF_INET6 + PyModule_AddIntConstant (m, "AF_INET6", AF_INET6); + PyDict_SetItem(address_family_dict, PyInt_FromLong(AF_INET6), + PyString_FromString("AF_INET6")); +#endif +#ifdef AF_NATM + PyModule_AddIntConstant (m, "AF_NATM", AF_NATM); + PyDict_SetItem(address_family_dict, PyInt_FromLong(AF_NATM), + PyString_FromString("AF_NATM")); +#endif +#ifdef AF_SYSTEM + PyModule_AddIntConstant (m, "AF_SYSTEM", AF_SYSTEM); + PyDict_SetItem(address_family_dict, PyInt_FromLong(AF_SYSTEM), + PyString_FromString("AF_SYSTEM")); +#endif +#ifdef AF_NETBIOS + PyModule_AddIntConstant (m, "AF_NETBIOS", AF_NETBIOS); + PyDict_SetItem(address_family_dict, PyInt_FromLong(AF_NETBIOS), + PyString_FromString("AF_NETBIOS")); +#endif +#ifdef AF_NETBEUI + PyModule_AddIntConstant (m, "AF_NETBEUI", AF_NETBEUI); + PyDict_SetItem(address_family_dict, PyInt_FromLong(AF_NETBEUI), + PyString_FromString("AF_NETBEUI")); +#endif +#ifdef AF_PPP + PyModule_AddIntConstant (m, "AF_PPP", AF_PPP); + PyDict_SetItem(address_family_dict, PyInt_FromLong(AF_PPP), + PyString_FromString("AF_PPP")); +#endif +#ifdef AF_ATM + PyModule_AddIntConstant (m, "AF_ATM", AF_ATM); + PyDict_SetItem(address_family_dict, PyInt_FromLong(AF_ATM), + PyString_FromString("AF_ATM")); +#endif +#ifdef AF_ATMPVC + PyModule_AddIntConstant (m, "AF_ATMPVC", AF_ATMPVC); + PyDict_SetItem(address_family_dict, PyInt_FromLong(AF_ATMPVC), + PyString_FromString("AF_ATMPVC")); +#endif +#ifdef AF_ATMSVC + PyModule_AddIntConstant (m, "AF_ATMSVC", AF_ATMSVC); + PyDict_SetItem(address_family_dict, PyInt_FromLong(AF_ATMSVC), + PyString_FromString("AF_ATMSVC")); +#endif +#ifdef AF_NETGRAPH + PyModule_AddIntConstant (m, "AF_NETGRAPH", AF_NETGRAPH); + PyDict_SetItem(address_family_dict, PyInt_FromLong(AF_NETGRAPH), + PyString_FromString("AF_NETGRAPH")); +#endif +#ifdef AF_VOICEVIEW + PyModule_AddIntConstant (m, "AF_VOICEVIEW", AF_VOICEVIEW); + PyDict_SetItem(address_family_dict, PyInt_FromLong(AF_VOICEVIEW), + PyString_FromString("AF_VOICEVIEW")); +#endif +#ifdef AF_FIREFOX + PyModule_AddIntConstant (m, "AF_FIREFOX", AF_FIREFOX); + PyDict_SetItem(address_family_dict, PyInt_FromLong(AF_FIREFOX), + PyString_FromString("AF_FIREFOX")); +#endif +#ifdef AF_UNKNOWN1 + PyModule_AddIntConstant (m, "AF_UNKNOWN1", AF_UNKNOWN1); + PyDict_SetItem(address_family_dict, PyInt_FromLong(AF_UNKNOWN1), + PyString_FromString("AF_UNKNOWN1")); +#endif +#ifdef AF_BAN + PyModule_AddIntConstant (m, "AF_BAN", AF_BAN); + PyDict_SetItem(address_family_dict, PyInt_FromLong(AF_BAN), + PyString_FromString("AF_BAN")); +#endif +#ifdef AF_CLUSTER + PyModule_AddIntConstant (m, "AF_CLUSTER", AF_CLUSTER); + PyDict_SetItem(address_family_dict, PyInt_FromLong(AF_CLUSTER), + PyString_FromString("AF_CLUSTER")); +#endif +#ifdef AF_12844 + PyModule_AddIntConstant (m, "AF_12844", AF_12844); + PyDict_SetItem(address_family_dict, PyInt_FromLong(AF_12844), + PyString_FromString("AF_12844")); +#endif +#ifdef AF_IRDA + PyModule_AddIntConstant (m, "AF_IRDA", AF_IRDA); + PyDict_SetItem(address_family_dict, PyInt_FromLong(AF_IRDA), + PyString_FromString("AF_IRDA")); +#endif +#ifdef AF_NETDES + PyModule_AddIntConstant (m, "AF_NETDES", AF_NETDES); + PyDict_SetItem(address_family_dict, PyInt_FromLong(AF_NETDES), + PyString_FromString("AF_NETDES")); +#endif +#ifdef AF_NETROM + PyModule_AddIntConstant (m, "AF_NETROM", AF_NETROM); + PyDict_SetItem(address_family_dict, PyInt_FromLong(AF_NETROM), + PyString_FromString("AF_NETROM")); +#endif +#ifdef AF_BRIDGE + PyModule_AddIntConstant (m, "AF_BRIDGE", AF_BRIDGE); + PyDict_SetItem(address_family_dict, PyInt_FromLong(AF_BRIDGE), + PyString_FromString("AF_BRIDGE")); +#endif +#ifdef AF_X25 + PyModule_AddIntConstant (m, "AF_X25", AF_X25); + PyDict_SetItem(address_family_dict, PyInt_FromLong(AF_X25), + PyString_FromString("AF_X25")); +#endif +#ifdef AF_ROSE + PyModule_AddIntConstant (m, "AF_ROSE", AF_ROSE); + PyDict_SetItem(address_family_dict, PyInt_FromLong(AF_ROSE), + PyString_FromString("AF_ROSE")); +#endif +#ifdef AF_SECURITY + PyModule_AddIntConstant (m, "AF_SECURITY", AF_SECURITY); + PyDict_SetItem(address_family_dict, PyInt_FromLong(AF_SECURITY), + PyString_FromString("AF_SECURITY")); +#endif +#ifdef AF_KEY + PyModule_AddIntConstant (m, "AF_KEY", AF_KEY); + PyDict_SetItem(address_family_dict, PyInt_FromLong(AF_KEY), + PyString_FromString("AF_KEY")); +#endif +#ifdef AF_NETLINK + PyModule_AddIntConstant (m, "AF_NETLINK", AF_NETLINK); + PyDict_SetItem(address_family_dict, PyInt_FromLong(AF_NETLINK), + PyString_FromString("AF_NETLINK")); +#endif +#ifdef AF_ASH + PyModule_AddIntConstant (m, "AF_ASH", AF_ASH); + PyDict_SetItem(address_family_dict, PyInt_FromLong(AF_ASH), + PyString_FromString("AF_ASH")); +#endif +#ifdef AF_ECONET + PyModule_AddIntConstant (m, "AF_ECONET", AF_ECONET); + PyDict_SetItem(address_family_dict, PyInt_FromLong(AF_ECONET), + PyString_FromString("AF_ECONET")); +#endif +#ifdef AF_SNA + PyModule_AddIntConstant (m, "AF_SNA", AF_SNA); + PyDict_SetItem(address_family_dict, PyInt_FromLong(AF_SNA), + PyString_FromString("AF_SNA")); +#endif +#ifdef AF_PPPOX + PyModule_AddIntConstant (m, "AF_PPPOX", AF_PPPOX); + PyDict_SetItem(address_family_dict, PyInt_FromLong(AF_PPPOX), + PyString_FromString("AF_PPPOX")); +#endif +#ifdef AF_WANPIPE + PyModule_AddIntConstant (m, "AF_WANPIPE", AF_WANPIPE); + PyDict_SetItem(address_family_dict, PyInt_FromLong(AF_WANPIPE), + PyString_FromString("AF_WANPIPE")); +#endif +#ifdef AF_BLUETOOTH + PyModule_AddIntConstant (m, "AF_BLUETOOTH", AF_BLUETOOTH); + PyDict_SetItem(address_family_dict, PyInt_FromLong(AF_BLUETOOTH), + PyString_FromString("AF_BLUETOOTH")); +#endif + PyModule_AddObject(m, "address_families", address_family_dict); + + // Add-in the version number from setup.py +#undef STR +#undef _STR +#define _STR(x) #x +#define STR(x) _STR(x) + + PyModule_AddStringConstant(m, "version", STR(NETIFACES_VERSION)); + + MODULE_RETURN(m); +} diff --git a/pypi_windows_packages.bat b/pypi_windows_packages.bat new file mode 100644 index 0000000..297e387 --- /dev/null +++ b/pypi_windows_packages.bat @@ -0,0 +1,4 @@ +C:\Python26\python.exe setup.py clean bdist_egg bdist_wininst bdist_wheel upload
+C:\Python27\python.exe setup.py clean bdist_egg bdist_wininst bdist_wheel upload
+C:\Python33\python.exe setup.py clean bdist_egg bdist_wininst bdist_wheel upload
+C:\Python34\python.exe setup.py clean bdist_egg bdist_wininst bdist_wheel upload
diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000..861a9f5 --- /dev/null +++ b/setup.cfg @@ -0,0 +1,5 @@ +[egg_info] +tag_build = +tag_date = 0 +tag_svn_revision = 0 + diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..30d7915 --- /dev/null +++ b/setup.py @@ -0,0 +1,539 @@ +import setuptools +import os +import sys +import distutils.spawn +from setuptools import setup, Extension +from setuptools.command.build_ext import build_ext +from distutils.errors import * +import pickle + +if sys.version_info[0] == 2: + def output(*args, **kwargs): + end = kwargs.get('end', '\n') + f = kwargs.get('file', sys.stdout) + f.write(' '.join(str(a) for a in args)) + f.write(end) +else: + try: + import builtins + except ImportError: + import __builtin__ + builtins = __builtin__ + + output = getattr(builtins, 'print') + +__version__ = "0.10.4" + +# Disable hard links, otherwise building distributions fails on OS X +try: + del os.link +except: + pass + +# On Windows, we need ws2_32 and iphlpapi +if getattr(sys, 'getwindowsversion', None): + libraries = ['ws2_32', 'iphlpapi'] + def_macros = [('WIN32', 1)] +else: + mos = getattr(sys, 'platform', None) + libraries = [] + if mos.startswith('sunos'): + libraries = ['socket', 'nsl'] + def_macros = [] + +def_macros.append(("NETIFACES_VERSION", __version__)) + +iface_mod = Extension('netifaces', sources=['netifaces.c'], + libraries=libraries, + define_macros=def_macros) + +# +# There must be a better way to do this... +# +class my_build_ext(build_ext): + def build_extensions(self): + self.check_requirements() + build_ext.build_extensions(self) + + def test_build(self, contents, link=True, execute=False, libraries=None, + include_dirs=None, library_dirs=None): + name = os.path.join(self.build_temp, 'conftest-%s.c' % self.conftestidx) + self.conftestidx += 1 + if os.path.exists(name): + os.unlink(name) + thefile = open(name, 'w') + thefile.write(contents) + thefile.close() + + sys.stdout.flush() + sys.stderr.flush() + mystdout = os.dup(1) + mystderr = os.dup(2) + result = True + try: + os.dup2(self.ctout, 1) + os.dup2(self.ctout, 2) + try: + objects = self.compiler.compile([name], + output_dir=self.build_temp, + include_dirs=include_dirs, + debug=self.debug) + if link: + self.compiler.link_executable(objects, + 'conftest', + output_dir=self.build_temp, + library_dirs=library_dirs, + libraries=libraries, + debug=self.debug) + if execute: + abspath = os.path.abspath(os.path.join(self.build_temp, + 'conftest')) + pipe = os.popen(abspath, 'r') + result = pipe.read().strip() + status = pipe.close() + if status is None: + status = 0 + if result == '': + result = True + if status != 0: + result = False + + finally: + os.dup2(mystdout, 1) + os.dup2(mystderr, 2) + except CompileError: + return False + except LinkError: + return False + except DistutilsExecError: + return False + return result + + def check_requirements(self): + # Load the cached config data from a previous run if possible; compiling + # things to test for features is slow + cache_file = os.path.join(self.build_temp, 'config.cache') + if os.path.exists(cache_file): + myfile = open(cache_file, 'rb') + try: + results = pickle.load(myfile) + finally: + myfile.close() + else: + results = {} + + self.conftestidx = 0 + + output("checking for getifaddrs...", end='') + + result = results.get('have_getifaddrs', None) + if result is not None: + cached = '(cached)' + else: + cached = '' + + if not os.path.exists(self.build_temp): + os.makedirs(self.build_temp) + outname = os.path.join(self.build_temp, 'conftest.out') + self.ctout = os.open(outname, os.O_RDWR | os.O_CREAT | os.O_TRUNC) + testrig = """ + #include <sys/types.h> + #include <sys/socket.h> + #include <ifaddrs.h> + int main(void) { + struct ifaddrs *addrs; + int ret; + ret = getifaddrs(&addrs); + freeifaddrs (addrs); + return 0; + } + """ + if self.test_build(testrig): + result = True + else: + result = False + + if result: + output("found. %s" % cached) + self.compiler.define_macro('HAVE_GETIFADDRS', 1) + else: + output("not found. %s" % cached) + + results['have_getifaddrs'] = result + + output("checking for getnameinfo...", end='') + + result = results.get('have_getnameinfo', None) + if result is not None: + cached = '(cached)' + else: + cached = '' + + if not os.path.exists(self.build_temp): + os.makedirs(self.build_temp) + outname = os.path.join(self.build_temp, 'conftest2.out') + self.ctout = os.open(outname, os.O_RDWR | os.O_CREAT | os.O_TRUNC) + testrig = """ + #include <sys/types.h> + #include <sys/socket.h> + #include <arpa/inet.h> + #include <netdb.h> + #include <stdlib.h> + int main(void) { + struct sockaddr_in sin; + char buffer[256]; + int ret; + + sin.sin_family = AF_INET; + sin.sin_port = 0; + sin.sin_addr.s_addr = htonl (INADDR_LOOPBACK); + + ret = getnameinfo ((struct sockaddr *)&sin, sizeof (sin), + buffer, sizeof (buffer), + NULL, 0, + NI_NUMERICHOST); + + return 0; + } + """ + if self.test_build(testrig,libraries=libraries): + result = True + else: + result = False + + if result: + output("found. %s" % cached) + self.compiler.define_macro('HAVE_GETNAMEINFO', 1) + else: + output("not found. %s" % cached) + + results['have_getnameinfo'] = result + + if not results['have_getifaddrs']: + output("checking for socket IOCTLs...", end='') + + result = results.get('have_socket_ioctls', None) + if result is not None: + cached = '(cached)' + else: + cached = '' + + if not os.path.exists(self.build_temp): + os.makedirs(self.build_temp) + outname = os.path.join(self.build_temp, 'conftest3.out') + self.ctout = os.open(outname, os.O_RDWR | os.O_CREAT | os.O_TRUNC) + + result = [] + ioctls = ('SIOCGIFCONF', + 'SIOCGSIZIFCONF', + 'SIOCGIFHWADDR', + 'SIOCGIFADDR', + 'SIOCGIFFLAGS', + 'SIOCGIFDSTADDR', + 'SIOCGIFBRDADDR', + 'SIOCGIFNETMASK', + 'SIOCGLIFNUM', + 'SIOCGLIFCONF', + 'SIOCGLIFFLAGS') + added_includes = "" + if mos.startswith('sunos'): + added_includes = """ + #include <unistd.h> + #include <stropts.h> + #include <sys/sockio.h> + """ + + for ioctl in ioctls: + testrig = """ + #include <sys/types.h> + #include <sys/socket.h> + #include <sys/ioctl.h> + #include <net/if.h> + #include <netinet/in.h> + #include <arpa/inet.h> + %(addedinc)s + int main(void) { + int fd = socket (AF_INET, SOCK_DGRAM, IPPROTO_IP); + struct ifreq ifreq; + + ioctl(fd, %(ioctl)s, &ifreq); + + return 0; + } + """ % { 'ioctl': ioctl , 'addedinc': added_includes} + + if self.test_build(testrig,libraries=libraries): + result.append(ioctl) + + if result: + output("%r. %s" % (result, cached)) + for ioctl in result: + self.compiler.define_macro('HAVE_%s' % ioctl, 1) + self.compiler.define_macro('HAVE_SOCKET_IOCTLS', 1) + else: + output("not found. %s" % cached) + + results['have_socket_ioctls'] = result + + output("checking for optional header files...", end='') + + result = results.get('have_headers', None) + if result is not None: + cached = '(cached)' + else: + cached = '' + + result =[] + headers = ('net/if_dl.h', 'netash/ash.h', + 'netatalk/at.h', 'netax25/ax25.h', + 'neteconet/ec.h', 'netipx/ipx.h', + 'netpacket/packet.h', 'netrose/rose.h', + 'linux/irda.h', 'linux/atm.h', + 'linux/llc.h', 'linux/tipc.h', + 'linux/dn.h') + + for header in headers: + testrig = """ + #include <sys/types.h> + #include <sys/socket.h> + #include <net/if.h> + #include <%s> + int main (void) { return 0; } + """ % header + + if self.test_build(testrig, link=False): + result.append(header) + + if result: + output("%s. %s" % (' '.join(result), cached)) + for header in result: + macro = header.upper().replace('.', '_').replace('/', '_') + self.compiler.define_macro('HAVE_%s' % macro, 1) + else: + output("none found. %s" % cached) + + optional_headers = result + results['have_headers'] = result + + output("checking whether struct sockaddr has a length field...", end='') + + result = results.get('have_sockaddr_sa_len', None) + if result is not None: + cached = '(cached)' + else: + cached = '' + + testrig = """ + #include <sys/types.h> + #include <sys/socket.h> + #include <net/if.h> + + int main (void) { + struct sockaddr sa; + sa.sa_len = 5; + return 0; + } + """ + + result = self.test_build(testrig) + + if result: + output('yes. %s' % cached) + self.compiler.define_macro('HAVE_SOCKADDR_SA_LEN', 1) + else: + output('no. %s' % cached) + + results['have_sockaddr_sa_len'] = result + + if not results['have_sockaddr_sa_len']: + # GAK! On certain stupid platforms (Linux), there's no sa_len. + # Macho Linux programmers apparently think that it's not needed, + # however, unfortunately, getifaddrs() doesn't return the + # lengths, because they're in the sa_len field on just about + # everything but Linux. + output("checking which sockaddr_xxx structs are defined...", end='') + + result = results.get('have_sockaddrs', None) + if result is not None: + cached = '(cached)' + else: + cached = '' + + if not os.path.exists(self.build_temp): + os.makedirs(self.build_temp) + outname = os.path.join(self.build_temp, 'conftest4.out') + self.ctout = os.open(outname, os.O_RDWR | os.O_CREAT | os.O_TRUNC) + + sockaddrs = ('at', 'ax25', 'dl', 'eon', 'in', 'in6', + 'inarp', 'ipx', 'iso', 'ns', 'un', 'x25', + 'rose', 'ash', 'ec', 'll', 'atmpvc', 'atmsvc', + 'dn', 'irda', 'llc') + result = [] + for sockaddr in sockaddrs: + testrig = """ + #include <sys/types.h> + #include <sys/socket.h> + #include <sys/un.h> + #include <net/if.h> + #include <netinet/in.h> + %(includes)s + + int main (void) { + struct sockaddr_%(sockaddr)s sa; + return 0; + } + """ % { 'includes': '\n'.join(["#include <%s>" % header + for header + in optional_headers]), + 'sockaddr': sockaddr } + + if self.test_build(testrig): + result.append(sockaddr) + + if result: + output('%s. %s' % (' '.join(result), cached)) + for sockaddr in result: + self.compiler.define_macro('HAVE_SOCKADDR_%s' \ + % sockaddr.upper(), 1) + else: + output('none! %s' % cached) + + results['have_sockaddrs'] = result + + # Reading routing tables is very OS dependent; check for a few + # different approaches. + output("checking for routing socket support...", end='') + + result = results.get('have_pf_route', None) + if result is not None: + cached = '(cached)' + else: + cached = '' + + testrig = """ + #include <sys/types.h> + #include <sys/socket.h> + #include <net/route.h> + + int main (void) { + struct rt_msghdr msg; + int s = socket (PF_ROUTE, SOCK_RAW, 0); + return 0; + } + """ + + result = self.test_build(testrig) + + if result: + output('yes. %s' % cached) + self.compiler.define_macro('HAVE_PF_ROUTE', 1) + else: + output('no. %s' % cached) + + results['have_pf_route'] = result + + output("checking for sysctl(CTL_NET...) support...", end='') + + result = results.get('have_sysctl_ctl_net', None) + if result is not None: + cached = '(cached)' + else: + cached = '' + + testrig = """ + #include <sys/types.h> + #include <sys/socket.h> + #include <sys/sysctl.h> + #include <net/route.h> + + int main (void) { + int mib[] = { CTL_NET, PF_ROUTE, 0, AF_INET, NET_RT_FLAGS, + RTF_UP | RTF_GATEWAY }; + return 0; + } + """ + + result = self.test_build(testrig) + + if result: + output('yes. %s' % cached) + self.compiler.define_macro('HAVE_SYSCTL_CTL_NET', 1) + else: + output('no. %s' % cached) + + results['have_sysctl_ctl_net'] = result + + output("checking for netlink support...", end='') + + result = results.get('have_pf_netlink', None) + if result is not None: + cached = '(cached)' + else: + cached = '' + + testrig = """ + #include <asm/types.h> + #include <sys/socket.h> + #include <linux/netlink.h> + #include <linux/rtnetlink.h> + + int main (void) { + int s = socket (PF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + return 0; + } + """ + + result = self.test_build(testrig) + + if result: + output('yes. %s' % cached) + self.compiler.define_macro('HAVE_PF_NETLINK', 1) + else: + output('no. %s' % cached) + + results['have_pf_netlink'] = result + + if results['have_pf_netlink']: + output('will use netlink to read routing table') + elif results['have_sysctl_ctl_net']: + output('will use sysctl() to read routing table') + elif results['have_pf_route']: + output('will use routing socket to read routing table') + + # Save the results to our config.cache file + myfile = open(cache_file, 'wb') + try: + pickle.dump(results, myfile) + finally: + myfile.close() + +# Don't bother detecting socket ioctls on Windows +if not getattr(sys, 'getwindowsversion', None): + setuptools.command.build_ext.build_ext = my_build_ext + +readme_path = os.path.join(os.path.abspath(os.path.dirname(__file__)), + 'README.rst') +long_desc = open(readme_path, 'r').read() + +setup (name='netifaces', + version=__version__, + zip_safe=True, + description="Portable network interface information.", + license="MIT License", + long_description=long_desc, + author='Alastair Houghton', + author_email='alastair@alastairs-place.net', + url='https://bitbucket.org/al45tair/netifaces', + classifiers=[ + 'Development Status :: 4 - Beta', + 'Intended Audience :: Developers', + 'License :: OSI Approved :: MIT License', + 'Topic :: System :: Networking', + 'Programming Language :: Python', + 'Programming Language :: Python :: 2', + 'Programming Language :: Python :: 2.5', + 'Programming Language :: Python :: 2.6', + 'Programming Language :: Python :: 2.7', + 'Programming Language :: Python :: 3', + ], + ext_modules=[iface_mod]) @@ -0,0 +1,50 @@ +import netifaces + +print('Found interfaces:') +for iface in netifaces.interfaces(): + print(' %s' % iface) + +print('') + +for iface in netifaces.interfaces(): + allAddrs = netifaces.ifaddresses(iface) + + print('Interface %s:' % iface) + + for family in allAddrs: + addrs = allAddrs[family] + fam_name = netifaces.address_families[family] + print(' Address family: %s' % fam_name) + for addr in addrs: + print(' Address : %s' % addr['addr']) + nmask = addr.get('netmask', None) + if nmask: + print(' Netmask : %s' % nmask) + bcast = addr.get('broadcast', None) + if bcast: + print(' Broadcast: %s' % bcast) + + print('') + +print('Found gateways:') +gateway_info = netifaces.gateways() +for family in gateway_info: + if family == 'default': + continue + + fam_name = netifaces.address_families[family] + print(' Family: %s' % fam_name) + for gateway,interface,default in gateway_info[family]: + if default: + def_text = ', default' + else: + def_text = '' + print(' %s (via %s%s)' % (gateway, interface, def_text)) + print('') + +print('Default gateways:') +default_gateways = gateway_info['default'] +for family in default_gateways: + fam_name = netifaces.address_families[family] + gateway, interface = default_gateways[family] + print(' %s: %s (via %s)' % (fam_name, gateway, interface)) |