A Python port of the Invisible Internet Project (I2P)
at main 72 lines 2.2 kB view raw
1"""Naming service — resolve .i2p hostnames to Destinations.""" 2 3from abc import ABC, abstractmethod 4 5from i2p_data.data_helper import from_base64 6 7 8class LookupResult: 9 """Result of a hostname lookup.""" 10 11 def __init__(self, success: bool, destination_data: bytes | None = None, 12 error_code: int | None = None): 13 self.success = success 14 self.destination_data = destination_data 15 self.error_code = error_code 16 17 18class NamingService(ABC): 19 """Abstract base for hostname resolution.""" 20 21 @abstractmethod 22 def lookup(self, hostname: str) -> LookupResult: 23 """Resolve a hostname to a Destination.""" 24 25 @abstractmethod 26 def list_all(self) -> dict[str, bytes]: 27 """List all known hostname→destination mappings.""" 28 29 30class HostsTxtService(NamingService): 31 """File-based hosts.txt resolver. 32 33 Format: hostname=base64destination (one per line). 34 Comments (#) and blank lines are ignored. 35 Lookups are case-insensitive. 36 """ 37 38 def __init__(self, entries: dict[str, bytes]): 39 self._entries = entries 40 41 @classmethod 42 def from_string(cls, text: str) -> "HostsTxtService": 43 entries: dict[str, bytes] = {} 44 for line in text.splitlines(): 45 line = line.strip() 46 if not line or line.startswith("#"): 47 continue 48 if "=" not in line: 49 continue 50 hostname, b64 = line.split("=", 1) 51 hostname = hostname.strip().lower() 52 try: 53 dest_data = from_base64(b64.strip()) 54 except Exception: 55 continue 56 entries[hostname] = dest_data 57 return cls(entries) 58 59 @classmethod 60 def from_file(cls, path: str) -> "HostsTxtService": 61 with open(path, "r") as f: 62 return cls.from_string(f.read()) 63 64 def lookup(self, hostname: str) -> LookupResult: 65 key = hostname.lower() 66 dest = self._entries.get(key) 67 if dest is not None: 68 return LookupResult(success=True, destination_data=dest) 69 return LookupResult(success=False, error_code=1) 70 71 def list_all(self) -> dict[str, bytes]: 72 return dict(self._entries)