Skip to content
57 changes: 53 additions & 4 deletions hub/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import ipaddress
import logging
import logging.handlers
import socket
import typing
import collections
from bisect import insort_right
Expand Down Expand Up @@ -153,6 +154,38 @@ def protocol_version(client_req, min_tuple, max_tuple):
return result, client_min


async def resolve_host(url: str, port: int, proto: str,
family: int = socket.AF_INET, all_results: bool = False) \
-> typing.Union[str, typing.List[str]]:
if proto not in ['udp', 'tcp']:
raise Exception("invalid protocol")
try:
if ipaddress.ip_address(url):
return [url] if all_results else url
except ValueError:
pass
loop = asyncio.get_running_loop()
records = await loop.getaddrinfo(
url, port,
proto=socket.IPPROTO_TCP if proto == 'tcp' else socket.IPPROTO_UDP,
type=socket.SOCK_STREAM if proto == 'tcp' else socket.SOCK_DGRAM,
family=family,
)
def addr_not_ipv4_mapped(rec):
_, _, _, _, sockaddr = rec
ipaddr = ipaddress.ip_address(sockaddr[0])
return ipaddr.version != 6 or not ipaddr.ipv4_mapped
records = filter(addr_not_ipv4_mapped, records)
results = [sockaddr[0] for fam, type, prot, canonname, sockaddr in records]
if not results and not all_results:
raise socket.gaierror(
socket.EAI_ADDRFAMILY,
'The specified network host does not have any network '
'addresses in the requested address family'
)
return results if all_results else results[0]


class LRUCacheWithMetrics:
__slots__ = [
'capacity',
Expand Down Expand Up @@ -577,10 +610,10 @@ def __len__(self):
def is_valid_public_ipv4(address, allow_localhost: bool = False, allow_lan: bool = False):
try:
parsed_ip = ipaddress.ip_address(address)
if parsed_ip.is_loopback and allow_localhost:
return True
if allow_lan and parsed_ip.is_private:
return True
if parsed_ip.is_loopback:
return allow_localhost
if parsed_ip.is_private:
return allow_lan
if any((parsed_ip.version != 4, parsed_ip.is_unspecified, parsed_ip.is_link_local, parsed_ip.is_loopback,
parsed_ip.is_multicast, parsed_ip.is_reserved, parsed_ip.is_private)):
return False
Expand All @@ -590,6 +623,22 @@ def is_valid_public_ipv4(address, allow_localhost: bool = False, allow_lan: bool
except (ipaddress.AddressValueError, ValueError):
return False

def is_valid_public_ipv6(address, allow_localhost: bool = False, allow_lan: bool = False):
try:
parsed_ip = ipaddress.ip_address(address)
if parsed_ip.is_loopback:
return allow_localhost
if parsed_ip.is_private:
return allow_lan
return not any((parsed_ip.version != 6, parsed_ip.is_unspecified,
parsed_ip.is_link_local, parsed_ip.is_loopback,
parsed_ip.is_multicast, parsed_ip.is_reserved,
parsed_ip.is_private, parsed_ip.ipv4_mapped))
except (ipaddress.AddressValueError, ValueError):
return False

def is_valid_public_ip(address, **kwargs):
return is_valid_public_ipv6(address, **kwargs) or is_valid_public_ipv4(address, **kwargs)

def sha256(x):
"""Simple wrapper of hashlib sha256."""
Expand Down
4 changes: 0 additions & 4 deletions hub/env.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,10 +117,6 @@ def cs_host(self):
result = [part.strip() for part in host.split(',')]
if len(result) == 1:
result = result[0]
if result == 'localhost':
# 'localhost' resolves to ::1 (ipv6) on many systems, which fails on default setup of
# docker, using 127.0.0.1 instead forces ipv4
result = '127.0.0.1'
return result

def sane_max_sessions(self):
Expand Down
19 changes: 15 additions & 4 deletions hub/herald/service.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import errno
import time
import typing
import asyncio
Expand Down Expand Up @@ -170,10 +171,20 @@ async def failover_elastic_services(self, synchronized: asyncio.Event):

async def start_status_server(self):
if self.env.udp_port and int(self.env.udp_port):
await self.status_server.start(
0, bytes.fromhex(self.env.coin.GENESIS_HASH)[::-1], self.env.country,
self.env.host, self.env.udp_port, self.env.allow_lan_udp
)
hosts = self.env.cs_host()
started = False
while not started:
try:
await self.status_server.start(
0, bytes.fromhex(self.env.coin.GENESIS_HASH)[::-1], self.env.country,
hosts, self.env.udp_port, self.env.allow_lan_udp
)
started = True
except OSError as e:
if e.errno is errno.EADDRINUSE:
await asyncio.sleep(3)
continue
raise

def _iter_start_tasks(self):
yield self.start_status_server()
Expand Down
3 changes: 2 additions & 1 deletion hub/herald/session.py
Original file line number Diff line number Diff line change
Expand Up @@ -271,7 +271,8 @@ async def _start_server(self, kind, *args, **kw_args):
f'{host}:{port:d} : {e!r}')
raise
else:
self.logger.info(f'{kind} server listening on {host}:{port:d}')
for s in self.servers[kind].sockets:
self.logger.info(f'{kind} server listening on {s.getsockname()[:2]}')

async def _start_external_servers(self):
"""Start listening on TCP and SSL ports, but only if the respective
Expand Down
Loading