Compare commits

..

No commits in common. "da7a4433c020cb809a1aef281b5b922aec06047d" and "29693c6fe2620e04440e604ef1b0daffebdafa62" have entirely different histories.

19 changed files with 164 additions and 555 deletions

View File

@ -190,6 +190,7 @@ these dependencies, and when the `rns` package is installed with `pip`, they
will be downloaded and installed as well. will be downloaded and installed as well.
- [PyCA/cryptography](https://github.com/pyca/cryptography) - [PyCA/cryptography](https://github.com/pyca/cryptography)
- [netifaces](https://github.com/al45tair/netifaces)
- [pyserial](https://github.com/pyserial/pyserial) - [pyserial](https://github.com/pyserial/pyserial)
On more unusual systems, and in some rare cases, it might not be possible to On more unusual systems, and in some rare cases, it might not be possible to
@ -340,8 +341,8 @@ projects:
- [Curve25519.py](https://gist.github.com/nickovs/cc3c22d15f239a2640c185035c06f8a3#file-curve25519-py) by [Nicko van Someren](https://gist.github.com/nickovs), *Public Domain* - [Curve25519.py](https://gist.github.com/nickovs/cc3c22d15f239a2640c185035c06f8a3#file-curve25519-py) by [Nicko van Someren](https://gist.github.com/nickovs), *Public Domain*
- [I2Plib](https://github.com/l-n-s/i2plib) by [Viktor Villainov](https://github.com/l-n-s) - [I2Plib](https://github.com/l-n-s/i2plib) by [Viktor Villainov](https://github.com/l-n-s)
- [PySerial](https://github.com/pyserial/pyserial) by Chris Liechti, *BSD License* - [PySerial](https://github.com/pyserial/pyserial) by Chris Liechti, *BSD License*
- [Netifaces](https://github.com/al45tair/netifaces) by [Alastair Houghton](https://github.com/al45tair), *MIT License*
- [Configobj](https://github.com/DiffSK/configobj) by Michael Foord, Nicola Larosa, Rob Dennis & Eli Courtwright, *BSD License* - [Configobj](https://github.com/DiffSK/configobj) by Michael Foord, Nicola Larosa, Rob Dennis & Eli Courtwright, *BSD License*
- [Six](https://github.com/benjaminp/six) by [Benjamin Peterson](https://github.com/benjaminp), *MIT License* - [Six](https://github.com/benjaminp/six) by [Benjamin Peterson](https://github.com/benjaminp), *MIT License*
- [Umsgpack.py](https://github.com/vsergeev/u-msgpack-python) by [Ivan A. Sergeev](https://github.com/vsergeev) - [Umsgpack.py](https://github.com/vsergeev/u-msgpack-python) by [Ivan A. Sergeev](https://github.com/vsergeev)
- [ifaddr](https://github.com/pydron/ifaddr) by [Pydron](https://github.com/pydron), *MIT License*
- [Python](https://www.python.org) - [Python](https://www.python.org)

View File

@ -62,18 +62,16 @@ class AutoInterface(Interface):
link_local_addr = re.sub(r"fe80:[0-9a-f]*::","fe80::", link_local_addr) link_local_addr = re.sub(r"fe80:[0-9a-f]*::","fe80::", link_local_addr)
return link_local_addr return link_local_addr
def list_interfaces(self):
ifs = self.netinfo.interfaces()
return ifs
def list_addresses(self, ifname):
ifas = self.netinfo.ifaddresses(ifname)
return ifas
def __init__(self, owner, name, group_id=None, discovery_scope=None, discovery_port=None, data_port=None, allowed_interfaces=None, ignored_interfaces=None, configured_bitrate=None): def __init__(self, owner, name, group_id=None, discovery_scope=None, discovery_port=None, data_port=None, allowed_interfaces=None, ignored_interfaces=None, configured_bitrate=None):
from RNS.vendor.ifaddr import niwrapper import importlib
self.netinfo = niwrapper if importlib.util.find_spec('netifaces') != None:
import netifaces
else:
RNS.log("Using AutoInterface requires the netifaces module.", RNS.LOG_CRITICAL)
RNS.log("You can install it with the command: python3 -m pip install netifaces", RNS.LOG_CRITICAL)
RNS.panic()
self.netifaces = netifaces
self.rxb = 0 self.rxb = 0
self.txb = 0 self.txb = 0
@ -150,7 +148,7 @@ class AutoInterface(Interface):
self.mcast_discovery_address = "ff1"+self.discovery_scope+":"+gt self.mcast_discovery_address = "ff1"+self.discovery_scope+":"+gt
suitable_interfaces = 0 suitable_interfaces = 0
for ifname in self.list_interfaces(): for ifname in self.netifaces.interfaces():
if RNS.vendor.platformutils.is_darwin() and ifname in AutoInterface.DARWIN_IGNORE_IFS and not ifname in self.allowed_interfaces: if RNS.vendor.platformutils.is_darwin() and ifname in AutoInterface.DARWIN_IGNORE_IFS and not ifname in self.allowed_interfaces:
RNS.log(str(self)+" skipping Darwin AWDL or tethering interface "+str(ifname), RNS.LOG_EXTREME) RNS.log(str(self)+" skipping Darwin AWDL or tethering interface "+str(ifname), RNS.LOG_EXTREME)
elif RNS.vendor.platformutils.is_darwin() and ifname == "lo0": elif RNS.vendor.platformutils.is_darwin() and ifname == "lo0":
@ -165,10 +163,10 @@ class AutoInterface(Interface):
if len(self.allowed_interfaces) > 0 and not ifname in self.allowed_interfaces: if len(self.allowed_interfaces) > 0 and not ifname in self.allowed_interfaces:
RNS.log(str(self)+" ignoring interface "+str(ifname)+" since it was not allowed", RNS.LOG_EXTREME) RNS.log(str(self)+" ignoring interface "+str(ifname)+" since it was not allowed", RNS.LOG_EXTREME)
else: else:
addresses = self.list_addresses(ifname) addresses = self.netifaces.ifaddresses(ifname)
if self.netinfo.AF_INET6 in addresses: if self.netifaces.AF_INET6 in addresses:
link_local_addr = None link_local_addr = None
for address in addresses[self.netinfo.AF_INET6]: for address in addresses[self.netifaces.AF_INET6]:
if "addr" in address: if "addr" in address:
if address["addr"].startswith("fe80:"): if address["addr"].startswith("fe80:"):
link_local_addr = self.descope_linklocal(address["addr"]) link_local_addr = self.descope_linklocal(address["addr"])
@ -289,10 +287,10 @@ class AutoInterface(Interface):
for ifname in self.adopted_interfaces: for ifname in self.adopted_interfaces:
# Check that the link-local address has not changed # Check that the link-local address has not changed
try: try:
addresses = self.list_addresses(ifname) addresses = self.netifaces.ifaddresses(ifname)
if self.netinfo.AF_INET6 in addresses: if self.netifaces.AF_INET6 in addresses:
link_local_addr = None link_local_addr = None
for address in addresses[self.netinfo.AF_INET6]: for address in addresses[self.netifaces.AF_INET6]:
if "addr" in address: if "addr" in address:
if address["addr"].startswith("fe80:"): if address["addr"].startswith("fe80:"):
link_local_addr = self.descope_linklocal(address["addr"]) link_local_addr = self.descope_linklocal(address["addr"])

View File

@ -408,15 +408,25 @@ class TCPServerInterface(Interface):
@staticmethod @staticmethod
def get_address_for_if(name): def get_address_for_if(name):
import RNS.vendor.ifaddr.niwrapper as netinfo import importlib
ifaddr = netinfo.ifaddresses(name) if importlib.util.find_spec('netifaces') != None:
return ifaddr[netinfo.AF_INET][0]["addr"] import netifaces
return netifaces.ifaddresses(name)[netifaces.AF_INET][0]['addr']
else:
RNS.log("Getting interface addresses from device names requires the netifaces module.", RNS.LOG_CRITICAL)
RNS.log("You can install it with the command: python3 -m pip install netifaces", RNS.LOG_CRITICAL)
RNS.panic()
@staticmethod @staticmethod
def get_broadcast_for_if(name): def get_broadcast_for_if(name):
import RNS.vendor.ifaddr.niwrapper as netinfo import importlib
ifaddr = netinfo.ifaddresses(name) if importlib.util.find_spec('netifaces') != None:
return ifaddr[netinfo.AF_INET][0]["broadcast"] import netifaces
return netifaces.ifaddresses(name)[netifaces.AF_INET][0]['broadcast']
else:
RNS.log("Getting interface addresses from device names requires the netifaces module.", RNS.LOG_CRITICAL)
RNS.log("You can install it with the command: python3 -m pip install netifaces", RNS.LOG_CRITICAL)
RNS.panic()
def __init__(self, owner, name, device=None, bindip=None, bindport=None, i2p_tunneled=False): def __init__(self, owner, name, device=None, bindip=None, bindport=None, i2p_tunneled=False):
self.rxb = 0 self.rxb = 0

View File

@ -34,15 +34,25 @@ class UDPInterface(Interface):
@staticmethod @staticmethod
def get_address_for_if(name): def get_address_for_if(name):
import RNS.vendor.ifaddr.niwrapper as netinfo import importlib
ifaddr = netinfo.ifaddresses(name) if importlib.util.find_spec('netifaces') != None:
return ifaddr[netinfo.AF_INET][0]["addr"] import netifaces
return netifaces.ifaddresses(name)[netifaces.AF_INET][0]['addr']
else:
RNS.log("Getting interface addresses from device names requires the netifaces module.", RNS.LOG_CRITICAL)
RNS.log("You can install it with the command: python3 -m pip install netifaces", RNS.LOG_CRITICAL)
RNS.panic()
@staticmethod @staticmethod
def get_broadcast_for_if(name): def get_broadcast_for_if(name):
import RNS.vendor.ifaddr.niwrapper as netinfo import importlib
ifaddr = netinfo.ifaddresses(name) if importlib.util.find_spec('netifaces') != None:
return ifaddr[netinfo.AF_INET][0]["broadcast"] import netifaces
return netifaces.ifaddresses(name)[netifaces.AF_INET][0]['broadcast']
else:
RNS.log("Getting interface addresses from device names requires the netifaces module.", RNS.LOG_CRITICAL)
RNS.log("You can install it with the command: python3 -m pip install netifaces", RNS.LOG_CRITICAL)
RNS.panic()
def __init__(self, owner, name, device=None, bindip=None, bindport=None, forwardip=None, forwardport=None): def __init__(self, owner, name, device=None, bindip=None, bindport=None, forwardip=None, forwardport=None):
self.rxb = 0 self.rxb = 0

View File

@ -1,33 +0,0 @@
# Copyright (c) 2014 Stefan C. Mueller
# 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.
import os
from ifaddr._shared import Adapter, IP
if os.name == "nt":
from ifaddr._win32 import get_adapters
elif os.name == "posix":
from ifaddr._posix import get_adapters
else:
raise RuntimeError("Unsupported Operating System: %s" % os.name)
__all__ = ['Adapter', 'IP', 'get_adapters']

View File

@ -1,96 +0,0 @@
# Copyright (c) 2014 Stefan C. Mueller
# 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.
import os
import ctypes.util
import ipaddress
import collections
import socket
from typing import Iterable, Optional
import ifaddr._shared as shared
# from ifaddr._shared import sockaddr, Interface, sockaddr_to_ip, ipv6_prefixlength
class ifaddrs(ctypes.Structure):
pass
ifaddrs._fields_ = [
('ifa_next', ctypes.POINTER(ifaddrs)),
('ifa_name', ctypes.c_char_p),
('ifa_flags', ctypes.c_uint),
('ifa_addr', ctypes.POINTER(shared.sockaddr)),
('ifa_netmask', ctypes.POINTER(shared.sockaddr)),
]
libc = ctypes.CDLL(ctypes.util.find_library("socket" if os.uname()[0] == "SunOS" else "c"), use_errno=True) # type: ignore
def get_adapters(include_unconfigured: bool = False) -> Iterable[shared.Adapter]:
addr0 = addr = ctypes.POINTER(ifaddrs)()
retval = libc.getifaddrs(ctypes.byref(addr))
if retval != 0:
eno = ctypes.get_errno()
raise OSError(eno, os.strerror(eno))
ips = collections.OrderedDict()
def add_ip(adapter_name: str, ip: Optional[shared.IP]) -> None:
if adapter_name not in ips:
index = None # type: Optional[int]
try:
# Mypy errors on this when the Windows CI runs:
# error: Module has no attribute "if_nametoindex"
index = socket.if_nametoindex(adapter_name) # type: ignore
except (OSError, AttributeError):
pass
ips[adapter_name] = shared.Adapter(adapter_name, adapter_name, [], index=index)
if ip is not None:
ips[adapter_name].ips.append(ip)
while addr:
name = addr[0].ifa_name.decode(encoding='UTF-8')
ip_addr = shared.sockaddr_to_ip(addr[0].ifa_addr)
if ip_addr:
if addr[0].ifa_netmask and not addr[0].ifa_netmask[0].sa_familiy:
addr[0].ifa_netmask[0].sa_familiy = addr[0].ifa_addr[0].sa_familiy
netmask = shared.sockaddr_to_ip(addr[0].ifa_netmask)
if isinstance(netmask, tuple):
netmaskStr = str(netmask[0])
prefixlen = shared.ipv6_prefixlength(ipaddress.IPv6Address(netmaskStr))
else:
assert netmask is not None, f'sockaddr_to_ip({addr[0].ifa_netmask}) returned None'
netmaskStr = str('0.0.0.0/' + netmask)
prefixlen = ipaddress.IPv4Network(netmaskStr).prefixlen
ip = shared.IP(ip_addr, prefixlen, name)
add_ip(name, ip)
else:
if include_unconfigured:
add_ip(name, None)
addr = addr[0].ifa_next
libc.freeifaddrs(addr0)
return ips.values()

View File

@ -1,199 +0,0 @@
# Copyright (c) 2014 Stefan C. Mueller
# 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.
import ctypes
import socket
import ipaddress
import platform
from typing import List, Optional, Tuple, Union
class Adapter(object):
"""
Represents a network interface device controller (NIC), such as a
network card. An adapter can have multiple IPs.
On Linux aliasing (multiple IPs per physical NIC) is implemented
by creating 'virtual' adapters, each represented by an instance
of this class. Each of those 'virtual' adapters can have both
a IPv4 and an IPv6 IP address.
"""
def __init__(self, name: str, nice_name: str, ips: List['IP'], index: Optional[int] = None) -> None:
#: Unique name that identifies the adapter in the system.
#: On Linux this is of the form of `eth0` or `eth0:1`, on
#: Windows it is a UUID in string representation, such as
#: `{846EE342-7039-11DE-9D20-806E6F6E6963}`.
self.name = name
#: Human readable name of the adpater. On Linux this
#: is currently the same as :attr:`name`. On Windows
#: this is the name of the device.
self.nice_name = nice_name
#: List of :class:`ifaddr.IP` instances in the order they were
#: reported by the system.
self.ips = ips
#: Adapter index as used by some API (e.g. IPv6 multicast group join).
self.index = index
def __repr__(self) -> str:
return "Adapter(name={name}, nice_name={nice_name}, ips={ips}, index={index})".format(
name=repr(self.name), nice_name=repr(self.nice_name), ips=repr(self.ips), index=repr(self.index)
)
# Type of an IPv4 address (a string in "xxx.xxx.xxx.xxx" format)
_IPv4Address = str
# Type of an IPv6 address (a three-tuple `(ip, flowinfo, scope_id)`)
_IPv6Address = Tuple[str, int, int]
class IP(object):
"""
Represents an IP address of an adapter.
"""
def __init__(self, ip: Union[_IPv4Address, _IPv6Address], network_prefix: int, nice_name: str) -> None:
#: IP address. For IPv4 addresses this is a string in
#: "xxx.xxx.xxx.xxx" format. For IPv6 addresses this
#: is a three-tuple `(ip, flowinfo, scope_id)`, where
#: `ip` is a string in the usual collon separated
#: hex format.
self.ip = ip
#: Number of bits of the IP that represent the
#: network. For a `255.255.255.0` netmask, this
#: number would be `24`.
self.network_prefix = network_prefix
#: Human readable name for this IP.
#: On Linux is this currently the same as the adapter name.
#: On Windows this is the name of the network connection
#: as configured in the system control panel.
self.nice_name = nice_name
@property
def is_IPv4(self) -> bool:
"""
Returns `True` if this IP is an IPv4 address and `False`
if it is an IPv6 address.
"""
return not isinstance(self.ip, tuple)
@property
def is_IPv6(self) -> bool:
"""
Returns `True` if this IP is an IPv6 address and `False`
if it is an IPv4 address.
"""
return isinstance(self.ip, tuple)
def __repr__(self) -> str:
return "IP(ip={ip}, network_prefix={network_prefix}, nice_name={nice_name})".format(
ip=repr(self.ip), network_prefix=repr(self.network_prefix), nice_name=repr(self.nice_name)
)
if platform.system() == "Darwin" or "BSD" in platform.system():
# BSD derived systems use marginally different structures
# than either Linux or Windows.
# I still keep it in `shared` since we can use
# both structures equally.
class sockaddr(ctypes.Structure):
_fields_ = [
('sa_len', ctypes.c_uint8),
('sa_familiy', ctypes.c_uint8),
('sa_data', ctypes.c_uint8 * 14),
]
class sockaddr_in(ctypes.Structure):
_fields_ = [
('sa_len', ctypes.c_uint8),
('sa_familiy', ctypes.c_uint8),
('sin_port', ctypes.c_uint16),
('sin_addr', ctypes.c_uint8 * 4),
('sin_zero', ctypes.c_uint8 * 8),
]
class sockaddr_in6(ctypes.Structure):
_fields_ = [
('sa_len', ctypes.c_uint8),
('sa_familiy', ctypes.c_uint8),
('sin6_port', ctypes.c_uint16),
('sin6_flowinfo', ctypes.c_uint32),
('sin6_addr', ctypes.c_uint8 * 16),
('sin6_scope_id', ctypes.c_uint32),
]
else:
class sockaddr(ctypes.Structure): # type: ignore
_fields_ = [('sa_familiy', ctypes.c_uint16), ('sa_data', ctypes.c_uint8 * 14)]
class sockaddr_in(ctypes.Structure): # type: ignore
_fields_ = [
('sin_familiy', ctypes.c_uint16),
('sin_port', ctypes.c_uint16),
('sin_addr', ctypes.c_uint8 * 4),
('sin_zero', ctypes.c_uint8 * 8),
]
class sockaddr_in6(ctypes.Structure): # type: ignore
_fields_ = [
('sin6_familiy', ctypes.c_uint16),
('sin6_port', ctypes.c_uint16),
('sin6_flowinfo', ctypes.c_uint32),
('sin6_addr', ctypes.c_uint8 * 16),
('sin6_scope_id', ctypes.c_uint32),
]
def sockaddr_to_ip(sockaddr_ptr: 'ctypes.pointer[sockaddr]') -> Optional[Union[_IPv4Address, _IPv6Address]]:
if sockaddr_ptr:
if sockaddr_ptr[0].sa_familiy == socket.AF_INET:
ipv4 = ctypes.cast(sockaddr_ptr, ctypes.POINTER(sockaddr_in))
ippacked = bytes(bytearray(ipv4[0].sin_addr))
ip = str(ipaddress.ip_address(ippacked))
return ip
elif sockaddr_ptr[0].sa_familiy == socket.AF_INET6:
ipv6 = ctypes.cast(sockaddr_ptr, ctypes.POINTER(sockaddr_in6))
flowinfo = ipv6[0].sin6_flowinfo
ippacked = bytes(bytearray(ipv6[0].sin6_addr))
ip = str(ipaddress.ip_address(ippacked))
scope_id = ipv6[0].sin6_scope_id
return (ip, flowinfo, scope_id)
return None
def ipv6_prefixlength(address: ipaddress.IPv6Address) -> int:
prefix_length = 0
for i in range(address.max_prefixlen):
if int(address) >> i & 1:
prefix_length = prefix_length + 1
return prefix_length

View File

@ -1,145 +0,0 @@
# Copyright (c) 2014 Stefan C. Mueller
# 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.
import ctypes
from ctypes import wintypes
from typing import Iterable, List
import ifaddr._shared as shared
NO_ERROR = 0
ERROR_BUFFER_OVERFLOW = 111
MAX_ADAPTER_NAME_LENGTH = 256
MAX_ADAPTER_DESCRIPTION_LENGTH = 128
MAX_ADAPTER_ADDRESS_LENGTH = 8
AF_UNSPEC = 0
class SOCKET_ADDRESS(ctypes.Structure):
_fields_ = [('lpSockaddr', ctypes.POINTER(shared.sockaddr)), ('iSockaddrLength', wintypes.INT)]
class IP_ADAPTER_UNICAST_ADDRESS(ctypes.Structure):
pass
IP_ADAPTER_UNICAST_ADDRESS._fields_ = [
('Length', wintypes.ULONG),
('Flags', wintypes.DWORD),
('Next', ctypes.POINTER(IP_ADAPTER_UNICAST_ADDRESS)),
('Address', SOCKET_ADDRESS),
('PrefixOrigin', ctypes.c_uint),
('SuffixOrigin', ctypes.c_uint),
('DadState', ctypes.c_uint),
('ValidLifetime', wintypes.ULONG),
('PreferredLifetime', wintypes.ULONG),
('LeaseLifetime', wintypes.ULONG),
('OnLinkPrefixLength', ctypes.c_uint8),
]
class IP_ADAPTER_ADDRESSES(ctypes.Structure):
pass
IP_ADAPTER_ADDRESSES._fields_ = [
('Length', wintypes.ULONG),
('IfIndex', wintypes.DWORD),
('Next', ctypes.POINTER(IP_ADAPTER_ADDRESSES)),
('AdapterName', ctypes.c_char_p),
('FirstUnicastAddress', ctypes.POINTER(IP_ADAPTER_UNICAST_ADDRESS)),
('FirstAnycastAddress', ctypes.c_void_p),
('FirstMulticastAddress', ctypes.c_void_p),
('FirstDnsServerAddress', ctypes.c_void_p),
('DnsSuffix', ctypes.c_wchar_p),
('Description', ctypes.c_wchar_p),
('FriendlyName', ctypes.c_wchar_p),
]
iphlpapi = ctypes.windll.LoadLibrary("Iphlpapi") # type: ignore
def enumerate_interfaces_of_adapter(
nice_name: str, address: IP_ADAPTER_UNICAST_ADDRESS
) -> Iterable[shared.IP]:
# Iterate through linked list and fill list
addresses = [] # type: List[IP_ADAPTER_UNICAST_ADDRESS]
while True:
addresses.append(address)
if not address.Next:
break
address = address.Next[0]
for address in addresses:
ip = shared.sockaddr_to_ip(address.Address.lpSockaddr)
assert ip is not None, f'sockaddr_to_ip({address.Address.lpSockaddr}) returned None'
network_prefix = address.OnLinkPrefixLength
yield shared.IP(ip, network_prefix, nice_name)
def get_adapters(include_unconfigured: bool = False) -> Iterable[shared.Adapter]:
# Call GetAdaptersAddresses() with error and buffer size handling
addressbuffersize = wintypes.ULONG(15 * 1024)
retval = ERROR_BUFFER_OVERFLOW
while retval == ERROR_BUFFER_OVERFLOW:
addressbuffer = ctypes.create_string_buffer(addressbuffersize.value)
retval = iphlpapi.GetAdaptersAddresses(
wintypes.ULONG(AF_UNSPEC),
wintypes.ULONG(0),
None,
ctypes.byref(addressbuffer),
ctypes.byref(addressbuffersize),
)
if retval != NO_ERROR:
raise ctypes.WinError() # type: ignore
# Iterate through adapters fill array
address_infos = [] # type: List[IP_ADAPTER_ADDRESSES]
address_info = IP_ADAPTER_ADDRESSES.from_buffer(addressbuffer)
while True:
address_infos.append(address_info)
if not address_info.Next:
break
address_info = address_info.Next[0]
# Iterate through unicast addresses
result = [] # type: List[shared.Adapter]
for adapter_info in address_infos:
# We don't expect non-ascii characters here, so encoding shouldn't matter
name = adapter_info.AdapterName.decode()
nice_name = adapter_info.Description
index = adapter_info.IfIndex
if adapter_info.FirstUnicastAddress:
ips = enumerate_interfaces_of_adapter(
adapter_info.FriendlyName, adapter_info.FirstUnicastAddress[0]
)
ips = list(ips)
result.append(shared.Adapter(name, nice_name, ips, index=index))
elif include_unconfigured:
result.append(shared.Adapter(name, nice_name, [], index=index))
return result

View File

@ -1,38 +0,0 @@
import ipaddress
import ifaddr
import socket
from typing import List
AF_INET6 = socket.AF_INET6.value
AF_INET = socket.AF_INET.value
def interfaces() -> List[str]:
adapters = ifaddr.get_adapters(include_unconfigured=True)
return [a.name for a in adapters]
def ifaddresses(ifname) -> dict:
adapters = ifaddr.get_adapters(include_unconfigured=True)
ifa = {}
for a in adapters:
if a.name == ifname:
ipv4s = []
ipv6s = []
for ip in a.ips:
t = {}
if ip.is_IPv4:
net = ipaddress.ip_network(str(ip.ip)+"/"+str(ip.network_prefix), strict=False)
t["addr"] = ip.ip
t["prefix"] = ip.network_prefix
t["broadcast"] = str(net.broadcast_address)
ipv4s.append(t)
if ip.is_IPv6:
t["addr"] = ip.ip[0]
ipv6s.append(t)
if len(ipv4s) > 0:
ifa[AF_INET] = ipv4s
if len(ipv6s) > 0:
ifa[AF_INET6] = ipv6s
return ifa

View File

View File

@ -38,6 +38,7 @@ These efforts are aimed at improving the ease of which Reticulum is understood,
- Update NomadNet screenshots - Update NomadNet screenshots
- Update Sideband screenshots - Update Sideband screenshots
- Installation - Installation
- Install docs for fedora, needs `python3-netifaces`
- Add a *Reticulum On Raspberry Pi* section - Add a *Reticulum On Raspberry Pi* section
- Update *Reticulum On Android* section if necessary - Update *Reticulum On Android* section if necessary
- Update Android install documentation. - Update Android install documentation.

Binary file not shown.

Binary file not shown.

View File

@ -25,13 +25,30 @@ and install them offline using ``pip``:
.. code:: .. code::
pip install ./rns-0.5.1-py3-none-any.whl pip install ./rns-0.4.6-py3-none-any.whl
Resolving Dependency & Installation Issues Resolving Dependency & Installation Issues
============================================= =============================================
On some platforms, there may not be binary packages available for all dependencies, and On some platforms, there may not be binary packages available for all dependencies, and
``pip`` installation may fail with an error message. In these cases, the issue can usually ``pip`` installation may fail with an error message. Most often, this is simply due to
the ``netifaces`` package not having a pre-compiled package available via ``pip``.
Usually, this can be resolved by simply installing ``netifaces`` via the system package
manager:
.. code::
# Debian / Ubuntu / Derivatives
sudo apt install python3-netifaces
# Arch / Manjaro / Derivatives
sudo pamac install python3-netifaces
# Fedora
sudo dnf install python3-netifaces
More rarely, other dependencies cannot be resolved. In these cases, the issue can usually
be resolved by installing the development essentials packages for your platform: be resolved by installing the development essentials packages for your platform:
.. code:: .. code::
@ -291,7 +308,7 @@ For extended functionality, you can install optional dependencies:
.. code:: .. code::
pip3 install pyserial pip3 install pyserial netifaces
Further information can be found in the :ref:`API Reference<api-main>`. Further information can be found in the :ref:`API Reference<api-main>`.
@ -306,7 +323,7 @@ don't use pip, but try this recipe:
.. code:: .. code::
# Install dependencies # Install dependencies
pip3 install cryptography pyserial pip3 install cryptography pyserial netifaces
# Clone repository # Clone repository
git clone https://github.com/markqvist/Reticulum.git git clone https://github.com/markqvist/Reticulum.git
@ -444,6 +461,24 @@ it will require manually configuring and installing some packages, and is not
detailed in this manual. detailed in this manual.
Fedora
^^^^^^^^^^^^^^^^^^^^^^^^
On Fedora, ``pip`` installation may fail with an error message, since the ``netifaces``
package cannot be installed natively via ``pip``. This can be resolved by installing
the ``python3-netifaces`` package via ``dnf`` first:
.. code::
sudo dnf install python3-netifaces
Alternatively, you can install basic development packages, which will allow
``pip`` to install any required packages from source:
.. code::
sudo dnf groupinstall "Development Tools" "Development Libraries"
Debian Bookworm Debian Bookworm
^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^
On versions of Debian released after April 2023, it is no longer possible On versions of Debian released after April 2023, it is no longer possible

View File

@ -238,14 +238,28 @@ of your system with a command like <code class="docutils literal notranslate"><s
<code class="docutils literal notranslate"><span class="pre">sudo</span> <span class="pre">pamac</span> <span class="pre">install</span> <span class="pre">python-pip</span></code> or similar.</p> <code class="docutils literal notranslate"><span class="pre">sudo</span> <span class="pre">pamac</span> <span class="pre">install</span> <span class="pre">python-pip</span></code> or similar.</p>
<p>You can also dowload the Reticulum release wheels from GitHub, or other release channels, <p>You can also dowload the Reticulum release wheels from GitHub, or other release channels,
and install them offline using <code class="docutils literal notranslate"><span class="pre">pip</span></code>:</p> and install them offline using <code class="docutils literal notranslate"><span class="pre">pip</span></code>:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">pip</span> <span class="n">install</span> <span class="o">./</span><span class="n">rns</span><span class="o">-</span><span class="mf">0.5.1</span><span class="o">-</span><span class="n">py3</span><span class="o">-</span><span class="n">none</span><span class="o">-</span><span class="nb">any</span><span class="o">.</span><span class="n">whl</span> <div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">pip</span> <span class="n">install</span> <span class="o">./</span><span class="n">rns</span><span class="o">-</span><span class="mf">0.4.6</span><span class="o">-</span><span class="n">py3</span><span class="o">-</span><span class="n">none</span><span class="o">-</span><span class="nb">any</span><span class="o">.</span><span class="n">whl</span>
</pre></div> </pre></div>
</div> </div>
</section> </section>
<section id="resolving-dependency-installation-issues"> <section id="resolving-dependency-installation-issues">
<h2>Resolving Dependency &amp; Installation Issues<a class="headerlink" href="#resolving-dependency-installation-issues" title="Permalink to this heading">#</a></h2> <h2>Resolving Dependency &amp; Installation Issues<a class="headerlink" href="#resolving-dependency-installation-issues" title="Permalink to this heading">#</a></h2>
<p>On some platforms, there may not be binary packages available for all dependencies, and <p>On some platforms, there may not be binary packages available for all dependencies, and
<code class="docutils literal notranslate"><span class="pre">pip</span></code> installation may fail with an error message. In these cases, the issue can usually <code class="docutils literal notranslate"><span class="pre">pip</span></code> installation may fail with an error message. Most often, this is simply due to
the <code class="docutils literal notranslate"><span class="pre">netifaces</span></code> package not having a pre-compiled package available via <code class="docutils literal notranslate"><span class="pre">pip</span></code>.</p>
<p>Usually, this can be resolved by simply installing <code class="docutils literal notranslate"><span class="pre">netifaces</span></code> via the system package
manager:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="c1"># Debian / Ubuntu / Derivatives</span>
<span class="n">sudo</span> <span class="n">apt</span> <span class="n">install</span> <span class="n">python3</span><span class="o">-</span><span class="n">netifaces</span>
<span class="c1"># Arch / Manjaro / Derivatives</span>
<span class="n">sudo</span> <span class="n">pamac</span> <span class="n">install</span> <span class="n">python3</span><span class="o">-</span><span class="n">netifaces</span>
<span class="c1"># Fedora</span>
<span class="n">sudo</span> <span class="n">dnf</span> <span class="n">install</span> <span class="n">python3</span><span class="o">-</span><span class="n">netifaces</span>
</pre></div>
</div>
<p>More rarely, other dependencies cannot be resolved. In these cases, the issue can usually
be resolved by installing the development essentials packages for your platform:</p> be resolved by installing the development essentials packages for your platform:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="c1"># Debian / Ubuntu / Derivatives</span> <div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="c1"># Debian / Ubuntu / Derivatives</span>
<span class="n">sudo</span> <span class="n">apt</span> <span class="n">install</span> <span class="n">build</span><span class="o">-</span><span class="n">essential</span> <span class="n">sudo</span> <span class="n">apt</span> <span class="n">install</span> <span class="n">build</span><span class="o">-</span><span class="n">essential</span>
@ -450,7 +464,7 @@ started is to install the latest release of Reticulum via pip:</p>
ready to import and use RNS in your own programs. The next step will most ready to import and use RNS in your own programs. The next step will most
likely be to look at some <a class="reference internal" href="examples.html#examples-main"><span class="std std-ref">Example Programs</span></a>.</p> likely be to look at some <a class="reference internal" href="examples.html#examples-main"><span class="std std-ref">Example Programs</span></a>.</p>
<p>For extended functionality, you can install optional dependencies:</p> <p>For extended functionality, you can install optional dependencies:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">pip3</span> <span class="n">install</span> <span class="n">pyserial</span> <div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">pip3</span> <span class="n">install</span> <span class="n">pyserial</span> <span class="n">netifaces</span>
</pre></div> </pre></div>
</div> </div>
<p>Further information can be found in the <a class="reference internal" href="reference.html#api-main"><span class="std std-ref">API Reference</span></a>.</p> <p>Further information can be found in the <a class="reference internal" href="reference.html#api-main"><span class="std std-ref">API Reference</span></a>.</p>
@ -461,7 +475,7 @@ likely be to look at some <a class="reference internal" href="examples.html#exam
utilities, youll want to get the latest source from GitHub. In that case, utilities, youll want to get the latest source from GitHub. In that case,
dont use pip, but try this recipe:</p> dont use pip, but try this recipe:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="c1"># Install dependencies</span> <div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="c1"># Install dependencies</span>
<span class="n">pip3</span> <span class="n">install</span> <span class="n">cryptography</span> <span class="n">pyserial</span> <span class="n">pip3</span> <span class="n">install</span> <span class="n">cryptography</span> <span class="n">pyserial</span> <span class="n">netifaces</span>
<span class="c1"># Clone repository</span> <span class="c1"># Clone repository</span>
<span class="n">git</span> <span class="n">clone</span> <span class="n">https</span><span class="p">:</span><span class="o">//</span><span class="n">github</span><span class="o">.</span><span class="n">com</span><span class="o">/</span><span class="n">markqvist</span><span class="o">/</span><span class="n">Reticulum</span><span class="o">.</span><span class="n">git</span> <span class="n">git</span> <span class="n">clone</span> <span class="n">https</span><span class="p">:</span><span class="o">//</span><span class="n">github</span><span class="o">.</span><span class="n">com</span><span class="o">/</span><span class="n">markqvist</span><span class="o">/</span><span class="n">Reticulum</span><span class="o">.</span><span class="n">git</span>
@ -584,6 +598,20 @@ dont always have packages available for some dependencies.</p>
it will require manually configuring and installing some packages, and is not it will require manually configuring and installing some packages, and is not
detailed in this manual.</p> detailed in this manual.</p>
</section> </section>
<section id="fedora">
<h3>Fedora<a class="headerlink" href="#fedora" title="Permalink to this heading">#</a></h3>
<p>On Fedora, <code class="docutils literal notranslate"><span class="pre">pip</span></code> installation may fail with an error message, since the <code class="docutils literal notranslate"><span class="pre">netifaces</span></code>
package cannot be installed natively via <code class="docutils literal notranslate"><span class="pre">pip</span></code>. This can be resolved by installing
the <code class="docutils literal notranslate"><span class="pre">python3-netifaces</span></code> package via <code class="docutils literal notranslate"><span class="pre">dnf</span></code> first:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">sudo</span> <span class="n">dnf</span> <span class="n">install</span> <span class="n">python3</span><span class="o">-</span><span class="n">netifaces</span>
</pre></div>
</div>
<p>Alternatively, you can install basic development packages, which will allow
<code class="docutils literal notranslate"><span class="pre">pip</span></code> to install any required packages from source:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">sudo</span> <span class="n">dnf</span> <span class="n">groupinstall</span> <span class="s2">&quot;Development Tools&quot;</span> <span class="s2">&quot;Development Libraries&quot;</span>
</pre></div>
</div>
</section>
<section id="debian-bookworm"> <section id="debian-bookworm">
<h3>Debian Bookworm<a class="headerlink" href="#debian-bookworm" title="Permalink to this heading">#</a></h3> <h3>Debian Bookworm<a class="headerlink" href="#debian-bookworm" title="Permalink to this heading">#</a></h3>
<p>On versions of Debian released after April 2023, it is no longer possible <p>On versions of Debian released after April 2023, it is no longer possible
@ -721,6 +749,7 @@ section of this manual.</p>
<li><a class="reference internal" href="#android">Android</a></li> <li><a class="reference internal" href="#android">Android</a></li>
<li><a class="reference internal" href="#arm64">ARM64</a></li> <li><a class="reference internal" href="#arm64">ARM64</a></li>
<li><a class="reference internal" href="#raspberry-pi">Raspberry Pi</a></li> <li><a class="reference internal" href="#raspberry-pi">Raspberry Pi</a></li>
<li><a class="reference internal" href="#fedora">Fedora</a></li>
<li><a class="reference internal" href="#debian-bookworm">Debian Bookworm</a></li> <li><a class="reference internal" href="#debian-bookworm">Debian Bookworm</a></li>
<li><a class="reference internal" href="#ubuntu-lunar">Ubuntu Lunar</a></li> <li><a class="reference internal" href="#ubuntu-lunar">Ubuntu Lunar</a></li>
</ul> </ul>

View File

@ -260,6 +260,7 @@ to participate in the development of Reticulum itself.</p>
<li class="toctree-l3"><a class="reference internal" href="gettingstartedfast.html#android">Android</a></li> <li class="toctree-l3"><a class="reference internal" href="gettingstartedfast.html#android">Android</a></li>
<li class="toctree-l3"><a class="reference internal" href="gettingstartedfast.html#arm64">ARM64</a></li> <li class="toctree-l3"><a class="reference internal" href="gettingstartedfast.html#arm64">ARM64</a></li>
<li class="toctree-l3"><a class="reference internal" href="gettingstartedfast.html#raspberry-pi">Raspberry Pi</a></li> <li class="toctree-l3"><a class="reference internal" href="gettingstartedfast.html#raspberry-pi">Raspberry Pi</a></li>
<li class="toctree-l3"><a class="reference internal" href="gettingstartedfast.html#fedora">Fedora</a></li>
<li class="toctree-l3"><a class="reference internal" href="gettingstartedfast.html#debian-bookworm">Debian Bookworm</a></li> <li class="toctree-l3"><a class="reference internal" href="gettingstartedfast.html#debian-bookworm">Debian Bookworm</a></li>
<li class="toctree-l3"><a class="reference internal" href="gettingstartedfast.html#ubuntu-lunar">Ubuntu Lunar</a></li> <li class="toctree-l3"><a class="reference internal" href="gettingstartedfast.html#ubuntu-lunar">Ubuntu Lunar</a></li>
</ul> </ul>

File diff suppressed because one or more lines are too long

View File

@ -25,13 +25,30 @@ and install them offline using ``pip``:
.. code:: .. code::
pip install ./rns-0.5.1-py3-none-any.whl pip install ./rns-0.4.6-py3-none-any.whl
Resolving Dependency & Installation Issues Resolving Dependency & Installation Issues
============================================= =============================================
On some platforms, there may not be binary packages available for all dependencies, and On some platforms, there may not be binary packages available for all dependencies, and
``pip`` installation may fail with an error message. In these cases, the issue can usually ``pip`` installation may fail with an error message. Most often, this is simply due to
the ``netifaces`` package not having a pre-compiled package available via ``pip``.
Usually, this can be resolved by simply installing ``netifaces`` via the system package
manager:
.. code::
# Debian / Ubuntu / Derivatives
sudo apt install python3-netifaces
# Arch / Manjaro / Derivatives
sudo pamac install python3-netifaces
# Fedora
sudo dnf install python3-netifaces
More rarely, other dependencies cannot be resolved. In these cases, the issue can usually
be resolved by installing the development essentials packages for your platform: be resolved by installing the development essentials packages for your platform:
.. code:: .. code::
@ -291,7 +308,7 @@ For extended functionality, you can install optional dependencies:
.. code:: .. code::
pip3 install pyserial pip3 install pyserial netifaces
Further information can be found in the :ref:`API Reference<api-main>`. Further information can be found in the :ref:`API Reference<api-main>`.
@ -306,7 +323,7 @@ don't use pip, but try this recipe:
.. code:: .. code::
# Install dependencies # Install dependencies
pip3 install cryptography pyserial pip3 install cryptography pyserial netifaces
# Clone repository # Clone repository
git clone https://github.com/markqvist/Reticulum.git git clone https://github.com/markqvist/Reticulum.git
@ -444,6 +461,24 @@ it will require manually configuring and installing some packages, and is not
detailed in this manual. detailed in this manual.
Fedora
^^^^^^^^^^^^^^^^^^^^^^^^
On Fedora, ``pip`` installation may fail with an error message, since the ``netifaces``
package cannot be installed natively via ``pip``. This can be resolved by installing
the ``python3-netifaces`` package via ``dnf`` first:
.. code::
sudo dnf install python3-netifaces
Alternatively, you can install basic development packages, which will allow
``pip`` to install any required packages from source:
.. code::
sudo dnf groupinstall "Development Tools" "Development Libraries"
Debian Bookworm Debian Bookworm
^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^
On versions of Debian released after April 2023, it is no longer possible On versions of Debian released after April 2023, it is no longer possible

View File

@ -20,7 +20,7 @@ if pure_python:
long_description = long_description.replace("</p>", "</p>"+pure_notice) long_description = long_description.replace("</p>", "</p>"+pure_notice)
else: else:
pkg_name = "rns" pkg_name = "rns"
requirements = ['cryptography>=3.4.7', 'pyserial>=3.5'] requirements = ['cryptography>=3.4.7', 'pyserial>=3.5', 'netifaces']
excluded_modules = exclude=["tests.*", "tests"] excluded_modules = exclude=["tests.*", "tests"]
packages = setuptools.find_packages(exclude=excluded_modules) packages = setuptools.find_packages(exclude=excluded_modules)