177 lines
4.6 KiB
Python
177 lines
4.6 KiB
Python
# 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
|