#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""
routines for getting network interface addresses
"""
# by Ben Wiley Sittler
__all__ = [
    'getifaddrs',
    'getaddrs',
    'getnetaddrs',
    'getbroadaddrs',
    'getdstaddrs',
    'main',
    'IFF_UP',
    'IFF_BROADCAST',
    'IFF_DEBUG',
    'IFF_LOOPBACK',
    'IFF_POINTOPOINT',
    'IFF_NOTRAILERS',
    'IFF_RUNNING',
    'IFF_NOARP',
    'IFF_PROMISC',
    'IFF_ALLMULTI',
    'IFF_MASTER',
    'IFF_SLAVE',
    'IFF_MULTICAST',
    'IFF_PORTSEL',
    'IFF_AUTOMEDIA',
    'IFF_DYNAMIC',
    'IFF_LOWER_UP',
    'IFF_DORMANT',
    'ARPHRD_NETROM',
    'ARPHRD_ETHER',
    'ARPHRD_EETHER',
    'ARPHRD_AX25',
    'ARPHRD_PRONET',
    'ARPHRD_CHAOS',
    'ARPHRD_IEEE802',
    'ARPHRD_ARCNET',
    'ARPHRD_APPLETLK',
    'ARPHRD_DLCI',
    'ARPHRD_ATM',
    'ARPHRD_METRICOM',
    'ARPHRD_IEEE1394',
    'ARPHRD_EUI64',
    'ARPHRD_INFINIBAND',
    'ARPHRD_SLIP',
    'ARPHRD_SLIP6',
    'ARPHRD_RSRVD',
    'ARPHRD_ADAPT',
    'ARPHRD_X25',
    'ARPHRD_HWX25',
    'ARPHRD_PPP',
    'ARPHRD_CISCO',
    'ARPHRD_HDLC',
    'ARPHRD_DDCMP',
    'ARPHRD_RAWHDLC',
    'ARPHRD_TUNNEL',
    'ARPHRD_TUNNEL6',
    'ARPHRD_FRAD',
    'ARPHRD_SKIP',
    'ARPHRD_LOOPBACK',
    'ARPHRD_LOCALTLK',
    'ARPHRD_FDDI',
    'ARPHRD_BIF',
    'ARPHRD_SIT',
    'ARPHRD_IPDDP',
    'ARPHRD_IPGRE',
    'ARPHRD_PIMREG',
    'ARPHRD_HIPPI',
    'ARPHRD_ASH',
    'ARPHRD_ECONET',
    'ARPHRD_IRDA',
    'ARPHRD_FCPP',
    'ARPHRD_FCAL',
    'ARPHRD_FCPL',
    'ARPHRD_FCFABRIC',
    'ARPHRD_IEEE802_TR',
    'ARPHRD_IEEE80211',
    'ARPHRD_IEEE80211_PRISM',
    'ARPHRD_IEEE80211_RADIOTAP',
    'ARPHRD_VOID',
    'ARPHRD_NONE',
    'ETH_P_LOOP',
    'ETH_P_PUP',
    'ETH_P_PUPAT',
    'ETH_P_IP',
    'ETH_P_X25',
    'ETH_P_ARP',
    'ETH_P_BPQ',
    'ETH_P_IEEEPUP',
    'ETH_P_IEEEPUPAT',
    'ETH_P_DEC',
    'ETH_P_DNA_DL',
    'ETH_P_DNA_RC',
    'ETH_P_DNA_RT',
    'ETH_P_LAT',
    'ETH_P_DIAG',
    'ETH_P_CUST',
    'ETH_P_SCA',
    'ETH_P_RARP',
    'ETH_P_ATALK',
    'ETH_P_AARP',
    'ETH_P_8021Q',
    'ETH_P_IPX',
    'ETH_P_IPV6',
    'ETH_P_SLOW',
    'ETH_P_WCCP',
    'ETH_P_PPP_DISC',
    'ETH_P_PPP_SES',
    'ETH_P_MPLS_UC',
    'ETH_P_MPLS_MC',
    'ETH_P_ATMMPOA',
    'ETH_P_ATMFATE',
    'ETH_P_AOE',
    'ETH_P_TIPC',
    'ETH_P_802_3',
    'ETH_P_AX25',
    'ETH_P_ALL',
    'ETH_P_802_2',
    'ETH_P_SNAP',
    'ETH_P_DDCMP',
    'ETH_P_WAN_PPP',
    'ETH_P_PPP_MP',
    'ETH_P_LOCALTALK',
    'ETH_P_PPPTALK',
    'ETH_P_TR_802_2',
    'ETH_P_MOBITEX',
    'ETH_P_CONTROL',
    'ETH_P_IRDA',
    'ETH_P_ECONET',
    'ETH_P_HDLC',
    'ETH_P_ARCNET',
    ]

import sys
import os
import socket
import struct
from ctypes import *

from BTL.obsoletepythonsupport import set

_libc = None

def libc():
    global _libc
    if _libc is None:
        uname = os.uname()
        assert uname[0] == 'Linux' and [ int(x) for x in uname[2].split('.')[:2] ] >= [ 2, 2 ]
        assert sizeof(c_ushort) == 2
        assert sizeof(c_uint) == 4
        _libc = CDLL('libc.so.6')
    return _libc

def errno():
    return cast(addressof(libc().errno), POINTER(POINTER(c_int)))[0][0]

uint16_t = c_ushort
uint32_t = c_uint
uint8_t = c_ubyte

sa_family_t = c_ushort 

def SOCKADDR_COMMON(prefix):
    """
    Common data: address family and length.
    """
    return [ (prefix + 'family', sa_family_t) ]

SOCKADDR_COMMON_SIZE = sum([ sizeof(t) for n, t in SOCKADDR_COMMON('') ])

class sockaddr(Structure):
    """
    Structure describing a generic socket address.
    """
    pass

sockaddr._fields_ = SOCKADDR_COMMON('sa_') + [ # Common data: address family and length.
        ('sa_data', ARRAY(c_byte, 14)), # Address data.
        ]

class sockaddr_storage(Structure):
    """
    Structure large enough to hold any socket address (with the historical exception of AF_UNIX).  We reserve 128 bytes.
    """
    pass

_SS_SIZE = 128
__ss_aligntype = c_ulong
sockaddr_storage._fields_ = SOCKADDR_COMMON('ss_') + [ # Address family, etc.
    ('__ss_align', __ss_aligntype), # Force desired alignment.
    ('__ss_padding', ARRAY(c_byte, _SS_SIZE - 2 * sizeof(__ss_aligntype))),
    ]

class sockaddr_ll(Structure):
    pass

sockaddr_ll._fields_ = SOCKADDR_COMMON('sll_') + [
    ('sll_protocol', c_ushort),
    ('sll_ifindex', c_int),
    ('sll_hatype', c_ushort),
    ('sll_pkttype', c_ubyte),
    ('sll_halen', c_ubyte),
    ('sll_addr', ARRAY(c_ubyte, 8)),
    ]

in_port_t = uint16_t
in_addr_t = uint32_t

class in_addr(Structure):
    """
    Internet address.
    """
    pass

in_addr._fields_ = [
    ('s_addr', in_addr_t)
    ]

class in6_u(Union):
    pass

in6_u._fields_ = [
    ('u6_addr8', ARRAY(uint8_t, 16)),
    ('u6_addr16', ARRAY(uint16_t, 8)),
    ('u6_addr32', ARRAY(uint32_t, 4)),
    ]

class in6_addr(Structure):
    """
    IPv6 address
    """
    pass

in6_addr._fields_ = [
    ('in6_u', in6_u),
    ]

class sockaddr_in(Structure):
    """
    Structure describing an Internet socket address.
    """
    pass

sockaddr_in._fields_ = SOCKADDR_COMMON('sin_') + [
    ('sin_port', in_port_t), # Port number.
    ('sin_addr', in_addr), # Internet address.
    ('sin_zero', ARRAY(c_ubyte, sizeof(sockaddr) - SOCKADDR_COMMON_SIZE - sizeof(in_port_t) - sizeof(in_addr))), # Pad to size of `struct sockaddr'.
    ]

class sockaddr_in6(Structure):
    """
    Structure describing an IPv6 socket address.
    """
    pass

sockaddr_in6._fields_ = SOCKADDR_COMMON('sin6_') + [
    ('sin6_port', in_port_t), # Transport layer port #
    ('sin6_flowinfo', uint32_t), # IPv6 flow information
    ('sin6_addr', in6_addr), # IPv6 address
    ('sin6_scope_id', uint32_t), # IPv6 scope-id
    ]

class net_device_stats(Structure):
    """
    Network device statistics.
    """
    pass

net_device_stats._fields_ = [
    ('rx_packets', c_ulong),
    ('tx_packets', c_ulong),
    ('rx_bytes', c_ulong),
    ('tx_bytes', c_ulong),
    ('rx_errors', c_ulong),
    ('tx_errors', c_ulong),
    ('rx_dropped', c_ulong),
    ('tx_dropped', c_ulong),
    ('multicast', c_ulong),
    ('collisions', c_ulong),
    ('rx_length_errors', c_ulong),
    ('rx_over_errors', c_ulong),
    ('rx_crc_errors', c_ulong),
    ('rx_frame_errors', c_ulong),
    ('rx_fifo_errors', c_ulong),
    ('rx_missed_errors', c_ulong),
    ('tx_aborted_errors', c_ulong),
    ('tx_carrier_errors', c_ulong),
    ('tx_fifo_errors', c_ulong),
    ('tx_heartbeat_errors', c_ulong),
    ('tx_window_errors', c_ulong),
    ('rx_compressed', c_ulong),
    ('tx_compressed', c_ulong),
    ]

class ifa_ifu(Union):
    """
   At most one of the following two is valid.  If the IFF_BROADCAST
   bit is set in `ifa_flags', then `ifa_broadaddr' is valid.  If the
   IFF_POINTOPOINT bit is set, then `ifa_dstaddr' is valid.
   It is never the case that both these bits are set at once. 
    """
    pass

ifa_ifu._fields_=[
    ('ifu_broadaddr', POINTER(sockaddr)), # Broadcast address of this interface.
    ('ifu_dstaddr', POINTER(sockaddr)), # Point-to-point destination address.
    ]

class ifaddrs(Structure):
    """
    The `getifaddrs' function generates a linked list of these structures.
    Each element of the list describes one network interface.
    """
    pass

ifaddrs._fields_=[
    ('ifa_next', POINTER(ifaddrs)), # Pointer to the next structure.
    ('ifa_name', c_char_p), # Name of this network interface.
    ('ifa_flags', c_uint), # Flags as from SIOCGIFFLAGS ioctl.
    ('ifa_addr', POINTER(sockaddr)), # Network address of this interface.
    ('ifa_netmask', POINTER(sockaddr)), # Netmask of this interface.
    ('ifa_ifu', ifa_ifu),
    ('ifa_data', c_void_p), # Address-specific data (may be unused).
    ]

class NamedLong(long):
    def __new__(self, name, value):
        self._long = long.__new__(self, value)
        self._long._name = name
        return self._long
    def __repr__(self):
        return self._name
    pass

class OrSet(set):
    def __repr__(self):
        return ' | '.join([ repr(x) for x in self ])

IFF_UP = NamedLong(name = 'IFF_UP', value = 0x1)
IFF_BROADCAST = NamedLong(name = 'IFF_BROADCAST', value = 0x2)
IFF_DEBUG = NamedLong(name = 'IFF_DEBUG', value = 0x4)
IFF_LOOPBACK = NamedLong(name = 'IFF_LOOPBACK', value = 0x8)
IFF_POINTOPOINT = NamedLong(name = 'IFF_POINTOPOINT', value = 0x10)
IFF_NOTRAILERS = NamedLong(name = 'IFF_NOTRAILERS', value = 0x20)
IFF_RUNNING = NamedLong(name = 'IFF_RUNNING', value = 0x40)
IFF_NOARP = NamedLong(name = 'IFF_NOARP', value = 0x80)
IFF_PROMISC = NamedLong(name = 'IFF_PROMISC', value = 0x100)
IFF_ALLMULTI = NamedLong(name = 'IFF_ALLMULTI', value = 0x200)
IFF_MASTER = NamedLong(name = 'IFF_MASTER', value = 0x400)
IFF_SLAVE = NamedLong(name = 'IFF_SLAVE', value = 0x800)
IFF_MULTICAST = NamedLong(name = 'IFF_MULTICAST', value = 0x1000)
IFF_PORTSEL = NamedLong(name = 'IFF_PORTSEL', value = 0x2000)
IFF_AUTOMEDIA = NamedLong(name = 'IFF_AUTOMEDIA', value = 0x4000)
IFF_DYNAMIC = NamedLong(name = 'IFF_DYNAMIC', value = 0x8000L)
IFF_LOWER_UP = NamedLong(name = 'IFF_LOWER_UP', value = 0x10000)
IFF_DORMANT = NamedLong(name = 'IFF_DORMANT', value = 0x20000)

ARPHRD_NETROM = NamedLong(name = 'ARPHRD_NETROM', value = 0)
ARPHRD_ETHER = NamedLong(name = 'ARPHRD_ETHER', value = 1)
ARPHRD_EETHER = NamedLong(name = 'ARPHRD_EETHER', value = 2)
ARPHRD_AX25 = NamedLong(name = 'ARPHRD_AX25', value = 3)
ARPHRD_PRONET = NamedLong(name = 'ARPHRD_PRONET', value = 4)
ARPHRD_CHAOS = NamedLong(name = 'ARPHRD_CHAOS', value = 5)
ARPHRD_IEEE802 = NamedLong(name = 'ARPHRD_IEEE802', value = 6)
ARPHRD_ARCNET = NamedLong(name = 'ARPHRD_ARCNET', value = 7)
ARPHRD_APPLETLK = NamedLong(name = 'ARPHRD_APPLETLK', value = 8)
ARPHRD_DLCI = NamedLong(name = 'ARPHRD_DLCI', value = 15)
ARPHRD_ATM = NamedLong(name = 'ARPHRD_ATM', value = 19)
ARPHRD_METRICOM = NamedLong(name = 'ARPHRD_METRICOM', value = 23)
ARPHRD_IEEE1394 = NamedLong(name = 'ARPHRD_IEEE1394', value = 24)
ARPHRD_EUI64 = NamedLong(name = 'ARPHRD_EUI64', value = 27)
ARPHRD_INFINIBAND = NamedLong(name = 'ARPHRD_INFINIBAND', value = 32)
ARPHRD_SLIP = NamedLong(name = 'ARPHRD_SLIP', value = 256)
ARPHRD_SLIP6 = NamedLong(name = 'ARPHRD_SLIP6', value = 258)
ARPHRD_RSRVD = NamedLong(name = 'ARPHRD_RSRVD', value = 260)
ARPHRD_ADAPT = NamedLong(name = 'ARPHRD_ADAPT', value = 264)
ARPHRD_X25 = NamedLong(name = 'ARPHRD_X25', value = 271)
ARPHRD_HWX25 = NamedLong(name = 'ARPHRD_HWX25', value = 272)
ARPHRD_PPP = NamedLong(name = 'ARPHRD_PPP', value = 512)
ARPHRD_CISCO = NamedLong(name = 'ARPHRD_CISCO', value = 513)
ARPHRD_HDLC = NamedLong(name = 'ARPHRD_HDLC', value = ARPHRD_CISCO)
ARPHRD_DDCMP = NamedLong(name = 'ARPHRD_DDCMP', value = 517)
ARPHRD_RAWHDLC = NamedLong(name = 'ARPHRD_RAWHDLC', value = 518)
ARPHRD_TUNNEL = NamedLong(name = 'ARPHRD_TUNNEL', value = 768)
ARPHRD_TUNNEL6 = NamedLong(name = 'ARPHRD_TUNNEL6', value = 769)
ARPHRD_FRAD = NamedLong(name = 'ARPHRD_FRAD', value = 770)
ARPHRD_SKIP = NamedLong(name = 'ARPHRD_SKIP', value = 771)
ARPHRD_LOOPBACK = NamedLong(name = 'ARPHRD_LOOPBACK', value = 772)
ARPHRD_LOCALTLK = NamedLong(name = 'ARPHRD_LOCALTLK', value = 773)
ARPHRD_FDDI = NamedLong(name = 'ARPHRD_FDDI', value = 774)
ARPHRD_BIF = NamedLong(name = 'ARPHRD_BIF', value = 775)
ARPHRD_SIT = NamedLong(name = 'ARPHRD_SIT', value = 776)
ARPHRD_IPDDP = NamedLong(name = 'ARPHRD_IPDDP', value = 777)
ARPHRD_IPGRE = NamedLong(name = 'ARPHRD_IPGRE', value = 778)
ARPHRD_PIMREG = NamedLong(name = 'ARPHRD_PIMREG', value = 779)
ARPHRD_HIPPI = NamedLong(name = 'ARPHRD_HIPPI', value = 780)
ARPHRD_ASH = NamedLong(name = 'ARPHRD_ASH', value = 781)
ARPHRD_ECONET = NamedLong(name = 'ARPHRD_ECONET', value = 782)
ARPHRD_IRDA = NamedLong(name = 'ARPHRD_IRDA', value = 783)
ARPHRD_FCPP = NamedLong(name = 'ARPHRD_FCPP', value = 784)
ARPHRD_FCAL = NamedLong(name = 'ARPHRD_FCAL', value = 785)
ARPHRD_FCPL = NamedLong(name = 'ARPHRD_FCPL', value = 786)
ARPHRD_FCFABRIC = NamedLong(name = 'ARPHRD_FCFABRIC', value = 787)
ARPHRD_IEEE802_TR = NamedLong(name = 'ARPHRD_IEEE802_TR', value = 800)
ARPHRD_IEEE80211 = NamedLong(name = 'ARPHRD_IEEE80211', value = 801)
ARPHRD_IEEE80211_PRISM = NamedLong(name = 'ARPHRD_IEEE80211_PRISM', value = 802)
ARPHRD_IEEE80211_RADIOTAP = NamedLong(name = 'ARPHRD_IEEE80211_RADIOTAP', value = 803)
ARPHRD_VOID = NamedLong(name = 'ARPHRD_VOID', value = 0xFFFF)
ARPHRD_NONE = NamedLong(name = 'ARPHRD_NONE', value = 0xFFFE)

ETH_P_LOOP = NamedLong(name = 'ETH_P_LOOP', value = 0x0060)
ETH_P_PUP = NamedLong(name = 'ETH_P_PUP', value = 0x0200)
ETH_P_PUPAT = NamedLong(name = 'ETH_P_PUPAT', value = 0x0201)
ETH_P_IP = NamedLong(name = 'ETH_P_IP', value = 0x0800)
ETH_P_X25 = NamedLong(name = 'ETH_P_X25', value = 0x0805)
ETH_P_ARP = NamedLong(name = 'ETH_P_ARP', value = 0x0806)
ETH_P_BPQ = NamedLong(name = 'ETH_P_BPQ', value = 0x08FF)
ETH_P_IEEEPUP = NamedLong(name = 'ETH_P_IEEEPUP', value = 0x0a00)
ETH_P_IEEEPUPAT = NamedLong(name = 'ETH_P_IEEEPUPAT', value = 0x0a01)
ETH_P_DEC = NamedLong(name = 'ETH_P_DEC', value = 0x6000)
ETH_P_DNA_DL = NamedLong(name = 'ETH_P_DNA_DL', value = 0x6001)
ETH_P_DNA_RC = NamedLong(name = 'ETH_P_DNA_RC', value = 0x6002)
ETH_P_DNA_RT = NamedLong(name = 'ETH_P_DNA_RT', value = 0x6003)
ETH_P_LAT = NamedLong(name = 'ETH_P_LAT', value = 0x6004)
ETH_P_DIAG = NamedLong(name = 'ETH_P_DIAG', value = 0x6005)
ETH_P_CUST = NamedLong(name = 'ETH_P_CUST', value = 0x6006)
ETH_P_SCA = NamedLong(name = 'ETH_P_SCA', value = 0x6007)
ETH_P_RARP = NamedLong(name = 'ETH_P_RARP', value = 0x8035)
ETH_P_ATALK = NamedLong(name = 'ETH_P_ATALK', value = 0x809B)
ETH_P_AARP = NamedLong(name = 'ETH_P_AARP', value = 0x80F3)
ETH_P_8021Q = NamedLong(name = 'ETH_P_8021Q', value = 0x8100)
ETH_P_IPX = NamedLong(name = 'ETH_P_IPX', value = 0x8137)
ETH_P_IPV6 = NamedLong(name = 'ETH_P_IPV6', value = 0x86DD)
ETH_P_SLOW = NamedLong(name = 'ETH_P_SLOW', value = 0x8809)
ETH_P_WCCP = NamedLong(name = 'ETH_P_WCCP', value = 0x883E)
ETH_P_PPP_DISC = NamedLong(name = 'ETH_P_PPP_DISC', value = 0x8863)
ETH_P_PPP_SES = NamedLong(name = 'ETH_P_PPP_SES', value = 0x8864)
ETH_P_MPLS_UC = NamedLong(name = 'ETH_P_MPLS_UC', value = 0x8847)
ETH_P_MPLS_MC = NamedLong(name = 'ETH_P_MPLS_MC', value = 0x8848)
ETH_P_ATMMPOA = NamedLong(name = 'ETH_P_ATMMPOA', value = 0x884c)
ETH_P_ATMFATE = NamedLong(name = 'ETH_P_ATMFATE', value = 0x8884)
ETH_P_AOE = NamedLong(name = 'ETH_P_AOE', value = 0x88A2)
ETH_P_TIPC = NamedLong(name = 'ETH_P_TIPC', value = 0x88CA)
ETH_P_802_3 = NamedLong(name = 'ETH_P_802_3', value = 0x0001)
ETH_P_AX25 = NamedLong(name = 'ETH_P_AX25', value = 0x0002)
ETH_P_ALL = NamedLong(name = 'ETH_P_ALL', value = 0x0003)
ETH_P_802_2 = NamedLong(name = 'ETH_P_802_2', value = 0x0004)
ETH_P_SNAP = NamedLong(name = 'ETH_P_SNAP', value = 0x0005)
ETH_P_DDCMP = NamedLong(name = 'ETH_P_DDCMP', value = 0x0006)
ETH_P_WAN_PPP = NamedLong(name = 'ETH_P_WAN_PPP', value = 0x0007)
ETH_P_PPP_MP = NamedLong(name = 'ETH_P_PPP_MP', value = 0x0008)
ETH_P_LOCALTALK = NamedLong(name = 'ETH_P_LOCALTALK', value = 0x0009)
ETH_P_PPPTALK = NamedLong(name = 'ETH_P_PPPTALK', value = 0x0010)
ETH_P_TR_802_2 = NamedLong(name = 'ETH_P_TR_802_2', value = 0x0011)
ETH_P_MOBITEX = NamedLong(name = 'ETH_P_MOBITEX', value = 0x0015)
ETH_P_CONTROL = NamedLong(name = 'ETH_P_CONTROL', value = 0x0016)
ETH_P_IRDA = NamedLong(name = 'ETH_P_IRDA', value = 0x0017)
ETH_P_ECONET = NamedLong(name = 'ETH_P_ECONET', value = 0x0018)
ETH_P_HDLC = NamedLong(name = 'ETH_P_HDLC', value = 0x0019)
ETH_P_ARCNET = NamedLong(name = 'ETH_P_ARCNET', value = 0x001A)

def NamedLongs(x, names):
    s = OrSet()
    for k in names:
        if x & k:
            s |= OrSet([k])
            x ^= k
    k = 1L
    while x:
        if x & k:
            s |= OrSet([k])
            x ^= k
        k+=k
    return s

def _getifaddrs(ifap):
    """
   Create a linked list of `struct ifaddrs' structures, one for each
   network interface on the host machine.  If successful, store the
   list in *IFAP and return 0.  On errors, return -1 and set `errno'.

   The storage returned in *IFAP is allocated dynamically and can
   only be properly freed by passing it to `freeifaddrs'.
    """
    __getifaddrs = libc().getifaddrs
    return CFUNCTYPE(c_int, POINTER(POINTER(ifaddrs)))(__getifaddrs)(ifap)

def _freeifaddrs(ifa):
    """
   Reclaim the storage allocated by a previous `getifaddrs' call.
    """
    __freeifaddrs = libc().freeifaddrs
    return CFUNCTYPE(None, POINTER(ifaddrs))(__freeifaddrs)(ifa)

def hardware_type(hatype):
    this = sys.modules[__name__]
    return ([ x for x in [ NamedLong(n, getattr(this, n)) for n in dir(this) if n[:len('ARPHRD_')] == 'ARPHRD_' ] if x == hatype ] + [ hatype ])[0]

def eth_protocol_type(protocol):
    this = sys.modules[__name__]
    return ([ x for x in [ NamedLong(n, getattr(this, n)) for n in dir(this) if n[:len('ETH_P_')] == 'ETH_P_' ] if x == protocol ] + [ protocol ])[0]

def packet_type(pkttype):
    return ([ x for x in [ NamedLong(n, getattr(socket, n)) for n in dir(socket) if n[:len('PACKET_')] == 'PACKET_' ] if x == pkttype ] + [ pkttype ])[0]

def addrfamily(family):
    return ([ x for x in [ NamedLong(n, getattr(socket, n)) for n in dir(socket) if n[:len('AF_')] == 'AF_' ] if x == family ] + [ family ])[0]

def sockaddr2addr(ifname, addr):
    """
    Convert a sockaddr pointer (addr) to a descriptive dict or None
    for a void pointer.
    """
    if addr:
        sa = addr[0]
    else:
        return None
    d = { 'family': addrfamily(sa.sa_family) }
    if hasattr(socket, 'AF_INET6') and sa.sa_family == socket.AF_INET6:
        sin6 = cast(addr, POINTER(sockaddr_in6))[0]
        d['port'] = sin6.sin6_port or None
        d['addr'] = ':'.join([ '%04.4x' % socket.ntohs(x) for x in sin6.sin6_addr.in6_u.u6_addr16 ])
        d['flowinfo'] = sin6.sin6_flowinfo or None
        d['scope_id'] = sin6.sin6_scope_id
    elif hasattr(socket, 'AF_INET') and sa.sa_family == socket.AF_INET:
        sin = cast(addr, POINTER(sockaddr_in))[0]
        d['port'] = sin.sin_port or None
        d['addr'] = '.'.join([ str(ord(x)) for x in struct.pack('I', sin.sin_addr.s_addr) ])
    elif hasattr(socket, 'AF_PACKET') and sa.sa_family == socket.AF_PACKET:
        sll = cast(addr, POINTER(sockaddr_ll))[0]
        #d['ifindex'] = sll.sll_ifindex
        hwaddr = None
        if sll.sll_hatype == ARPHRD_ETHER and sll.sll_halen == 6:
            hwaddr = ':'.join([ chr(x).encode('hex') for x in sll.sll_addr[:sll.sll_halen] ])
        elif sll.sll_hatype == ARPHRD_SIT and sll.sll_halen == 4:
            try:
                hwaddr = socket.inet_ntop(socket.AF_INET,
                                          ''.join([ chr(x) for x in sll.sll_addr[:sll.sll_halen] ]))
            except:
                pass
        d['addr'] = (ifname,
                     eth_protocol_type(sll.sll_protocol),
                     packet_type(sll.sll_pkttype),
                     hardware_type(sll.sll_hatype),
                     ) + ((hwaddr is not None) and (hwaddr,) or ())
    else:
        pass
    try:
        if 'addr' in d:
            d['addr'] = socket.inet_ntop(sa.sa_family,
                                         socket.inet_pton(sa.sa_family,
                                                          d['addr']))
    except:
        pass
    return dict([ (k, v) for k, v in d.items() if v is not None ])

def flagset(flagbits):
    if isinstance(flagbits, set):
        return flagbits
    return NamedLongs(flagbits,
                      (IFF_UP,
                       IFF_BROADCAST,
                       IFF_DEBUG,
                       IFF_LOOPBACK,
                       IFF_POINTOPOINT,
                       IFF_NOTRAILERS,
                       IFF_RUNNING,
                       IFF_NOARP,
                       IFF_PROMISC,
                       IFF_ALLMULTI,
                       IFF_MASTER,
                       IFF_SLAVE,
                       IFF_MULTICAST,
                       IFF_PORTSEL,
                       IFF_AUTOMEDIA,
                       IFF_DYNAMIC,
                       IFF_LOWER_UP,
                       IFF_DORMANT,
                       ))

def getifaddrs():
    """
   Create a list of ifaddrs, one for each
   network interface on the host machine.  If successful,
   return the list.  On errors, raises an exception.
    """
    ifa = POINTER(ifaddrs)()
    ret = _getifaddrs(byref(ifa))
    ifa0 = ifa
    if ret == -1:
        raise IOError(os.strerror(errno()))
    try:
        iflist = []
        while ifa:
            d = {
                'name': ifa[0].ifa_name,
                'flags': flagset(ifa[0].ifa_flags),
                }
            d['addr'] = sockaddr2addr(d['name'], ifa[0].ifa_addr)
            d['netmask'] = sockaddr2addr(d['name'], ifa[0].ifa_netmask)
            if ifa[0].ifa_flags & IFF_BROADCAST:
                d['broadaddr'] = sockaddr2addr(d['name'], ifa[0].ifa_ifu.ifu_broadaddr)
            elif ifa[0].ifa_flags & IFF_POINTOPOINT:
                d['dstaddr'] = sockaddr2addr(d['name'], ifa[0].ifa_ifu.ifu_dstaddr)
            #d['data'] = ifa[0].ifa_data or None
            if ifa[0].ifa_data:
                if d.get('addr') is not None and d['addr'].get('family') == socket.AF_PACKET:
                    nds = cast(ifa[0].ifa_data, POINTER(net_device_stats))[0]
                    d['data'] = {
                        'rx_packets': nds.rx_packets,
                        'tx_packets': nds.tx_packets,
                        'rx_bytes': nds.rx_bytes,
                        'tx_bytes': nds.tx_bytes,
                        'rx_errors': nds.rx_errors,
                        'tx_errors': nds.tx_errors,
                        'rx_dropped': nds.rx_dropped,
                        'tx_dropped': nds.tx_dropped,
                        'multicast': nds.multicast,
                        'collisions': nds.collisions,
                        'rx_length_errors': nds.rx_length_errors,
                        'rx_over_errors': nds.rx_over_errors,
                        'rx_crc_errors': nds.rx_crc_errors,
                        'rx_frame_errors': nds.rx_frame_errors,
                        'rx_fifo_errors': nds.rx_fifo_errors,
                        'rx_missed_errors': nds.rx_missed_errors,
                        'tx_aborted_errors': nds.tx_aborted_errors,
                        'tx_carrier_errors': nds.tx_carrier_errors,
                        'tx_fifo_errors': nds.tx_fifo_errors,
                        'tx_heartbeat_errors': nds.tx_heartbeat_errors,
                        'tx_window_errors': nds.tx_window_errors,
                        'rx_compressed': nds.rx_compressed,
                        'tx_compressed': nds.tx_compressed,
                        }
            iflist.append(dict([ (k, v) for k, v in d.items() if v is not None ]))
            ifa = ifa[0].ifa_next
        return iflist
    finally:
        _freeifaddrs(ifa0)

def getaddrs(family = None, flags = OrSet([IFF_UP, IFF_RUNNING])):
    flags = flagset(flags)
    for a in getifaddrs():
        if family is not None and a.get('addr', {}).get('family') != family:
            continue
        if flags:
            for flag in flags:
                if flag not in flagset(a.get('flags', 0)):
                    continue
        if 'addr' in a and 'addr' in a['addr']:
            yield a['addr']['addr']

def getnetaddrs(family = None, flags = OrSet([IFF_UP, IFF_RUNNING])):
    flags = flagset(flags)
    for a in getifaddrs():
        if family is not None and a.get('addr', {}).get('family') != family:
            continue
        if 'netmask' in a and a.get('netmask', {}).get('family') != a.get('addr', {}).get('family') != family:
            continue
        if flags:
            for flag in flags:
                if flag not in flagset(a.get('flags', 0)):
                    continue
        if 'addr' in a and 'addr' in a['addr']:
            yield str(a['addr']['addr']) + (('netmask' in a and 'addr' in a['netmask'] and a['netmask'].get('family') == a['addr']['family']) and '/' + str(a['netmask']['addr']) or '')

def getbroadaddrs(family = None, flags = OrSet([IFF_UP, IFF_RUNNING, IFF_BROADCAST])):
    flags = flagset(flags)
    for a in getifaddrs():
        if family is not None and a.get('broadaddr', {}).get('family') != family:
            continue
        if flags:
            for flag in flags:
                if flag not in flagset(a.get('flags', 0)):
                    continue
        if 'broadaddr' in a and 'addr' in a['broadaddr']:
            yield str(a['broadaddr']['addr']) + (('netmask' in a and 'addr' in a['netmask'] and a['netmask'].get('family') == a['broadaddr']['family']) and '/' + str(a['netmask']['addr']) or '')

def getdstaddrs(family = None, flags = OrSet([IFF_UP, IFF_RUNNING, IFF_POINTOPOINT])):
    flags = flagset(flags)
    for a in getifaddrs():
        if family is not None and a.get('dstaddr', {}).get('family') != family:
            continue
        if flags:
            for flag in flags:
                if flag not in flagset(a.get('flags', 0)):
                    continue
        if 'dstaddr' in a and 'addr' in a['dstaddr']:
            yield a['dstaddr']['addr']

def main():
    '''
    Print a list of network interfaces.
    '''
    print 'live interface addresses:'
    for a in getaddrs():
        print '\t' 'addr', a
    for a in getdstaddrs():
        print '\t' 'dstaddr', a
    for a in getnetaddrs():
        print '\t' 'netaddr', a
    for a in getbroadaddrs():
        print '\t' 'broadaddr', a
    print 'all interface details:'
    for a in getifaddrs():
        print '\t' + a['name'] + ':'
        i = a.items()
        i.sort()
        for k, v in i:
            if k not in ('name', 'data'):
                print '\t\t' + k, `v`
        if 'data' in a:
            print '\t\t' 'data' + ':'
            i = a['data'].items()
            i.sort()
            for k, v in i:
                print '\t\t\t' + k, `v`

if __name__ == '__main__':
    main()

