123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177 |
- # Copyright (c) Twisted Matrix Laboratories.
- # See LICENSE for details.
- """
- POSIX implementation of local network interface enumeration.
- """
- from __future__ import division, absolute_import
- import sys, socket
- from socket import AF_INET, AF_INET6, inet_ntop
- from ctypes import (
- CDLL, POINTER, Structure, c_char_p, c_ushort, c_int,
- c_uint32, c_uint8, c_void_p, c_ubyte, pointer, cast)
- from ctypes.util import find_library
- from twisted.python.compat import _PY3, nativeString
- if _PY3:
- # Once #6070 is implemented, this can be replaced with the implementation
- # from that ticket:
- def chr(i):
- """
- Python 3 implementation of Python 2 chr(), i.e. convert an integer to
- corresponding byte.
- """
- return bytes([i])
- libc = CDLL(find_library("c"))
- if sys.platform.startswith('freebsd') or sys.platform == 'darwin':
- _sockaddrCommon = [
- ("sin_len", c_uint8),
- ("sin_family", c_uint8),
- ]
- else:
- _sockaddrCommon = [
- ("sin_family", c_ushort),
- ]
- class in_addr(Structure):
- _fields_ = [
- ("in_addr", c_ubyte * 4),
- ]
- class in6_addr(Structure):
- _fields_ = [
- ("in_addr", c_ubyte * 16),
- ]
- class sockaddr(Structure):
- _fields_ = _sockaddrCommon + [
- ("sin_port", c_ushort),
- ]
- class sockaddr_in(Structure):
- _fields_ = _sockaddrCommon + [
- ("sin_port", c_ushort),
- ("sin_addr", in_addr),
- ]
- class sockaddr_in6(Structure):
- _fields_ = _sockaddrCommon + [
- ("sin_port", c_ushort),
- ("sin_flowinfo", c_uint32),
- ("sin_addr", in6_addr),
- ]
- class ifaddrs(Structure):
- pass
- ifaddrs_p = POINTER(ifaddrs)
- ifaddrs._fields_ = [
- ('ifa_next', ifaddrs_p),
- ('ifa_name', c_char_p),
- ('ifa_flags', c_uint32),
- ('ifa_addr', POINTER(sockaddr)),
- ('ifa_netmask', POINTER(sockaddr)),
- ('ifa_dstaddr', POINTER(sockaddr)),
- ('ifa_data', c_void_p)]
- getifaddrs = libc.getifaddrs
- getifaddrs.argtypes = [POINTER(ifaddrs_p)]
- getifaddrs.restype = c_int
- freeifaddrs = libc.freeifaddrs
- freeifaddrs.argtypes = [ifaddrs_p]
- def _maybeCleanupScopeIndex(family, packed):
- """
- On FreeBSD, kill the embedded interface indices in link-local scoped
- addresses.
- @param family: The address family of the packed address - one of the
- I{socket.AF_*} constants.
- @param packed: The packed representation of the address (ie, the bytes of a
- I{in_addr} field).
- @type packed: L{bytes}
- @return: The packed address with any FreeBSD-specific extra bits cleared.
- @rtype: L{bytes}
- @see: U{https://twistedmatrix.com/trac/ticket/6843}
- @see: U{http://www.freebsd.org/doc/en/books/developers-handbook/ipv6.html#ipv6-scope-index}
- @note: Indications are that the need for this will be gone in FreeBSD >=10.
- """
- if sys.platform.startswith('freebsd') and packed[:2] == b"\xfe\x80":
- return packed[:2] + b"\x00\x00" + packed[4:]
- return packed
- def _interfaces():
- """
- Call C{getifaddrs(3)} and return a list of tuples of interface name, address
- family, and human-readable address representing its results.
- """
- ifaddrs = ifaddrs_p()
- if getifaddrs(pointer(ifaddrs)) < 0:
- raise OSError()
- results = []
- try:
- while ifaddrs:
- if ifaddrs[0].ifa_addr:
- family = ifaddrs[0].ifa_addr[0].sin_family
- if family == AF_INET:
- addr = cast(ifaddrs[0].ifa_addr, POINTER(sockaddr_in))
- elif family == AF_INET6:
- addr = cast(ifaddrs[0].ifa_addr, POINTER(sockaddr_in6))
- else:
- addr = None
- if addr:
- packed = b''.join(map(chr, addr[0].sin_addr.in_addr[:]))
- packed = _maybeCleanupScopeIndex(family, packed)
- results.append((
- ifaddrs[0].ifa_name,
- family,
- inet_ntop(family, packed)))
- ifaddrs = ifaddrs[0].ifa_next
- finally:
- freeifaddrs(ifaddrs)
- return results
- def posixGetLinkLocalIPv6Addresses():
- """
- Return a list of strings in colon-hex format representing all the link local
- IPv6 addresses available on the system, as reported by I{getifaddrs(3)}.
- """
- retList = []
- for (interface, family, address) in _interfaces():
- interface = nativeString(interface)
- address = nativeString(address)
- if family == socket.AF_INET6 and address.startswith('fe80:'):
- retList.append('%s%%%s' % (address, interface))
- return retList
|