import asyncio
import random
import secrets
import socket
import psutil
PORT_MIN = 1024
PORT_MAX = 65535
mapped_ports = {}
async def get_port(n, service):
key = f"{service}:{n}"
mapped = mapped_ports.get(key)
if mapped is not None:
return mapped
port = await get_random_unused_port()
print(f"providing port {port} to {service}:{n}")
mapped_ports[key] = port
return port
async def try_bind_port(port: int) -> bool:
try:
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.bind(("localhost", port))
return True
except OSError:
return False
async def is_port_in_use(port: int) -> bool:
async def check_port():
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
return s.connect_ex(("localhost", port)) == 0
if await check_port():
await asyncio.sleep(random.uniform(0, 3))
return await check_port()
return False
taken_ports = []
async def get_random_unused_port():
global taken_ports
if len(taken_ports) == 0:
seed = int.from_bytes(secrets.token_bytes(16), "big")
random.seed(seed)
try:
connections = psutil.net_connections()
taken_ports = {
conn.laddr.port for conn in connections if conn.status == "LISTEN"
}
except psutil.AccessDenied:
taken_ports = set()
except Exception as e:
print(f"Unexpected exception: {e}")
taken_ports = set()
while True:
port = random.randint(PORT_MIN, PORT_MAX)
if port in taken_ports:
continue
taken_ports.add(port)
if await try_bind_port(port):
return port
async def p2p_port(n):
return await get_port(n, "p2p")
async def rpc_port(n):
return await get_port(n, "rpc")
async def electrum_rpc_port(n):
return await get_port(n, "electrum_rpc")
async def electrum_ws_port(n):
return await get_port(n, "electrum_ws")
async def electrum_http_port(n):
return await get_port(n, "electrum_http")
async def electrum_monitoring_port(n):
return await get_port(n, "electrum_monitoring")
async def electrum_rpc_ssl_port(n):
return await get_port(n, "electrum_rpc_ssl")
async def electrum_ws_ssl_port(n):
return await get_port(n, "electrum_ws_ssl")
async def electrum_https_port(n):
return await get_port(n, "electrum_https")