"""IRC server tunnel — exposes local IRC server as I2P destination. Extends ServerTunnelTask with USER command mangling to replace the connecting user's identity with an I2P-derived cloak, preventing real hostname/IP leakage. Ported from net.i2p.i2ptunnel.I2PTunnelIRCServer. """ from __future__ import annotations import hashlib import logging from i2p_apps.i2ptunnel.config import TunnelDefinition, TunnelType from i2p_apps.i2ptunnel.tasks import ServerTunnelTask logger = logging.getLogger(__name__) class IRCServerTask(ServerTunnelTask): """IRC server tunnel with USER command mangling. Accepts I2P connections and forwards to a local IRC server, replacing the USER command's username/hostname with an I2P-derived cloak so the real identity is never exposed. """ def __init__(self, config: TunnelDefinition, session) -> None: super().__init__(config, session) def _compute_cloak(self, remote_dest: str) -> str: """Compute a deterministic .b32.i2p cloak from a destination. Uses SHA-256 of the destination to produce a stable, privacy-preserving identifier. """ h = hashlib.sha256(remote_dest.encode("utf-8")).hexdigest()[:16] return f"{h}.b32.i2p" def _mangle_user(self, line: str, remote_dest: str) -> str: """Mangle a USER command, replacing the username with a cloak. If the line is not a USER command, returns it unchanged. Format: USER : """ if not line.upper().startswith("USER "): return line parts = line.split(" ", 4) if len(parts) < 5: return line cloak = self._compute_cloak(remote_dest) # Replace username (parts[1]) with cloak, preserve realname parts[1] = cloak return " ".join(parts)