A Python port of the Invisible Internet Project (I2P)
at main 116 lines 3.4 kB view raw
1"""Base transport interface. 2 3All transports (NTCP2, SSU2) implement this interface so TransportManager 4can coordinate between them. 5 6Ported from net.i2p.router.transport.Transport. 7""" 8 9import enum 10from abc import ABC, abstractmethod 11 12 13class TransportStyle(enum.Enum): 14 NTCP2 = "NTCP2" 15 SSU2 = "SSU2" 16 17 18class ReachabilityStatus(enum.Enum): 19 OK = "ok" # Directly reachable 20 FIREWALLED = "firewalled" # Behind NAT, needs relay 21 UNKNOWN = "unknown" # Not yet determined 22 TESTING = "testing" # Currently running peer test 23 SYMMETRIC_NAT = "symmetric_nat" # Symmetric NAT (hardest case) 24 25 26# Reachability ordering — lower index is better 27_REACHABILITY_ORDER = [ 28 ReachabilityStatus.OK, 29 ReachabilityStatus.TESTING, 30 ReachabilityStatus.FIREWALLED, 31 ReachabilityStatus.SYMMETRIC_NAT, 32 ReachabilityStatus.UNKNOWN, 33] 34 35 36def _reachability_rank(status: ReachabilityStatus) -> int: 37 """Return numeric rank for a reachability status (lower is better).""" 38 try: 39 return _REACHABILITY_ORDER.index(status) 40 except ValueError: 41 return len(_REACHABILITY_ORDER) 42 43 44class TransportBid: 45 """A bid from a transport to carry a message to a peer. 46 47 The TransportManager collects bids from all registered transports 48 and selects the lowest (best) bid. 49 """ 50 51 # Sentinel values 52 TRANSIENT_FAIL = 999 53 WILL_NOT_SEND = -1 54 55 def __init__(self, latency_ms: int, transport: "Transport", 56 preference: int = 0): 57 self.latency_ms = latency_ms 58 self.transport = transport 59 self.preference = preference # Tiebreaker (lower = preferred) 60 61 def __lt__(self, other: "TransportBid") -> bool: 62 if self.latency_ms != other.latency_ms: 63 return self.latency_ms < other.latency_ms 64 return self.preference < other.preference 65 66 def __repr__(self) -> str: 67 return (f"TransportBid(latency_ms={self.latency_ms}, " 68 f"transport={self.transport.style.value}, " 69 f"preference={self.preference})") 70 71 72class Transport(ABC): 73 """Abstract base for I2P transports.""" 74 75 @property 76 @abstractmethod 77 def style(self) -> TransportStyle: 78 """The transport style (NTCP2 or SSU2).""" 79 80 @abstractmethod 81 async def start(self) -> None: 82 """Start the transport.""" 83 84 @abstractmethod 85 async def stop(self) -> None: 86 """Stop the transport.""" 87 88 @property 89 @abstractmethod 90 def is_running(self) -> bool: 91 """Whether the transport is currently running.""" 92 93 @abstractmethod 94 async def bid(self, peer_hash: bytes) -> TransportBid: 95 """Bid to send a message to the given peer. 96 97 Returns a TransportBid with latency_ms=WILL_NOT_SEND if this 98 transport cannot reach the peer. 99 """ 100 101 @abstractmethod 102 async def send(self, peer_hash: bytes, data: bytes) -> bool: 103 """Send data to peer. Returns True on success.""" 104 105 @property 106 @abstractmethod 107 def reachability(self) -> ReachabilityStatus: 108 """Current reachability status of this transport.""" 109 110 @property 111 @abstractmethod 112 def current_address(self) -> dict | None: 113 """Published address for this transport (IP, port, options). 114 115 Returns None if no address is available (e.g. transport not started). 116 """