A Python port of the Invisible Internet Project (I2P)
1"""IRC server tunnel — exposes local IRC server as I2P destination.
2
3Extends ServerTunnelTask with USER command mangling to replace
4the connecting user's identity with an I2P-derived cloak,
5preventing real hostname/IP leakage.
6
7Ported from net.i2p.i2ptunnel.I2PTunnelIRCServer.
8"""
9
10from __future__ import annotations
11
12import hashlib
13import logging
14
15from i2p_apps.i2ptunnel.config import TunnelDefinition, TunnelType
16from i2p_apps.i2ptunnel.tasks import ServerTunnelTask
17
18logger = logging.getLogger(__name__)
19
20
21class IRCServerTask(ServerTunnelTask):
22 """IRC server tunnel with USER command mangling.
23
24 Accepts I2P connections and forwards to a local IRC server,
25 replacing the USER command's username/hostname with an I2P-derived
26 cloak so the real identity is never exposed.
27 """
28
29 def __init__(self, config: TunnelDefinition, session) -> None:
30 super().__init__(config, session)
31
32 def _compute_cloak(self, remote_dest: str) -> str:
33 """Compute a deterministic .b32.i2p cloak from a destination.
34
35 Uses SHA-256 of the destination to produce a stable,
36 privacy-preserving identifier.
37 """
38 h = hashlib.sha256(remote_dest.encode("utf-8")).hexdigest()[:16]
39 return f"{h}.b32.i2p"
40
41 def _mangle_user(self, line: str, remote_dest: str) -> str:
42 """Mangle a USER command, replacing the username with a cloak.
43
44 If the line is not a USER command, returns it unchanged.
45 Format: USER <username> <mode> <unused> :<realname>
46 """
47 if not line.upper().startswith("USER "):
48 return line
49
50 parts = line.split(" ", 4)
51 if len(parts) < 5:
52 return line
53
54 cloak = self._compute_cloak(remote_dest)
55 # Replace username (parts[1]) with cloak, preserve realname
56 parts[1] = cloak
57 return " ".join(parts)