Proof of concept mechanical port of ircnet/ircd to Rust as part of a bit about C being insecure for network services
0

Configure Feed

Select the types of activity you want to include in your feed.

P5 — Command handlers (bulk): sub‑phase progress log#

This is the detailed, per‑cluster progress log for Phase P5 of the strangler‑fig C→Rust migration. It was split out of PLAN.md (whose P5 table row had grown to ~45 Ki on a single line). See the P5 row in PLAN.md for the module list, approach, and test gate.

Each entry is one ported command cluster, in order. "Drop" = the .c was removed from build.rs; partial ports keep the rest of the TU as C behind a *_link.o seam.

  • P5a DONE: s_service.c (first cluster — established the handler‑porting mechanism).
  • P5b DONE: s_debug.c (first utility/callee TU — STATS sub‑reports) + s_zip.c dropped for free (ZIP_LINKS off → only rcsid).
  • P5c DONE: s_misc.c (the exit_client/exit_one_client/exit_server exit cascade + client‑name/date/stats utilities; s_misc.o dropped).
  • P5d DONE: s_user.c WHO/WHOIS cluster (m_who/m_whois/who_one/who_channel/who_find/send_whois/parse_who_arg) via s_user_link.o partial port; rest of s_user.c stays C.
  • P5e DONE: s_user.c USERHOST/ISON cluster (m_userhost/m_ison) — same s_user_link.o partial port (guard extended).
  • P5f DONE: s_user.c message‑routing cluster (m_message/m_private/m_notice — PRIVMSG/NOTICE delivery core) — same s_user_link.o partial port; golden harness gained multi‑client Client + boot_standalone to cover #/+/& channel relay.
  • P5g DONE: s_user.c AWAY cluster (m_away + send_away) — same s_user_link.o partial port; send_away now resolves to Rust for the already‑ported m_message/m_whois callers.
  • P5h DONE: s_user.c UMODE cluster (m_umode + send_umode + send_umode_out + the user_modes[] table) — same s_user_link.o partial port; send_umode now resolves to Rust for the still‑C register_user/s_service.c/s_serv.c callers.
  • P5i DONE: s_user.c PING/PONG cluster (m_ping + m_pong) — same s_user_link.o partial port; pure routing handlers (no statics).
  • P5j DONE: s_user.c OPER cluster (m_oper) — same s_user_link.o partial port; reuses the Rust send_umode_out; new dedicated oper.conf fixture + boot_conf harness fn to L2‑test the auth paths (491/464/success).
  • P5k DONE: s_user.c QUIT/POST exit cluster (m_quit + m_post) — same s_user_link.o partial port; both terminate in the already‑Rust exit_client (P5c); L2 covers QUIT (registered) + POST (unregistered http‑proxy alias).
  • P5l DONE: s_user.c PASS cluster (m_pass) — same s_user_link.o partial port; registration‑time password/version‑staging handler (no wire output); L1 differential on cptr->passwd/cptr->info + L2 PASS‑then‑register banner byte‑identical.
  • P5m DONE: s_user.c canonize utility (canonize — the comma‑list deduplicator) — same s_user_link.o partial port; a pure callee (no msgtab row) reached from m_mode/m_part/m_topic (channel.c) + m_whowas (whowas.c) + the WHO cluster; L1 differential is the gate, WHOWAS gives an L2 dedup regression gate.
  • P5n DONE: s_user.c do_nick_name utility (do_nick_name — the nickname validator/canonicalizer) — same s_user_link.o partial port; a pure string callee (no msgtab row) reached from still‑C m_nick/m_user/register_user; L1 differential is the gate, the registration banner is the L2 regression gate (the nick is echoed in every reply).
  • P5o DONE: s_user.c is_allowed ACL gate (is_allowed — the per‑client O‑line/service privilege check) — same s_user_link.o partial port; a pure callee (no msgtab row, no globals/hash — a pure function of cptr) reached from ported parse.rs (penalty) + still‑C m_kill/s_serv/s_conf/channel/res/hash/s_bsd; L1 differential (conf‑walk) is the gate, oper‑KILL grant(K→401)/deny(T→481) is the L2 branch‑selection gate.
  • P5p DONE: s_user.c KILL handler (m_kill — the oper/server KILL command; gates on the Rust is_allowed (P5o), resolves the victim via Rust find_uid/find_client/get_history, terminates in the Rust exit_client (P5c)) — same s_user_link.o partial port; new multi‑client golden_s_user_kill L2 (oper alice KILLs registered bob → victim KILL notice + exit_client close byte‑identical).
  • P5q DONE: s_user.c register_user (the central client‑registration routine — ident‑prefix username canonicalization, check_client/find_kill gating, the welcome banner [001…RPL_YOURID + LUSERS + MOTD], send_umode, the leaf‑server UNICK feed) — same s_user_link.o partial port; a utility/callee (no msgtab row) reached from still‑C m_nick/m_user/m_unick; the registration banner (the P0‑L2 anchor scenario) is the L2 gate.
  • P5r DONE: s_user.c m_user (the USER registration handler — make_user/numeric sip, the +/-/RFC‑bit umode‑in‑USER parse, real‑name info capping, then the register_user (P5q) tail‑call) — same s_user_link.o partial port; a handler (msgtab USER=[m_nop,m_reg,m_reg,m_nop,m_user] → unregistered col only) reached on the registration path; the registration banner is the L2 gate (drives make_user + the "no flags" umode path).
  • P5s DONE: s_user.c NICK cluster (m_nick + m_unick + save_user [static SAVE emitter] + m_save — the connected component sharing save_user) — same s_user_link.o partial port; m_nick's goto badparamcountkills/goto nickkilldone control flow refactored into two faithful helper fns; L2 golden (golden_s_user_nick: local nick change echo, 432 erroneous nick, 433 nick‑in‑use) byte‑identical; m_unick/m_save/save_user's SAVE emit are S2S‑only (no L2 path under ‑s, covered by review). Only next_client/hunt_server (hash‑dependent → S2S/L2) now remain in s_user_link.o. s_auth.c deferred to P7/P‑IAUTH (no m_*; pure socket/ident/iauth‑pipe I/O, no L2 path under ‑s boot).**
  • P5t DONE: channel.c leaf‑predicate foundation cluster (is_chan_op/has_voice/find_channel/clean_channelname — the only truly‑leaf exports, no file‑static/shared‑buffer dep) via the new channel_link.o partial port (‑DPORT_CHANNEL_P5); establishes the channel.c port mechanism. L1 differential (4 fns) + L2 golden_channel (JOIN #chan → 353 @alice/366). can_send deferred to the mode cluster (calls the static match_modeid). P5‑S2S DONE: server‑link golden coverage — the new ircd-golden Peer fake‑server (completes the IRCnet 2.11 PASS/SERVER→burst handshake) + s2s.conf fixture (C/N/H lines) close the IsServer(cptr) paths every cluster above deferred to "S2S/L2": 13 golden_s2s_* scenarios byte‑identical (ref‑C == Rust) — link+remote‑WHOIS, PRIVMSG routing (P5f), AWAY (P5g), umode propagation (P5h), QUIT propagation (P5k), server‑KILL (P5p), SQUIT/exit_server reap (P5c), remote NICK + SAVE (P5s), USERHOST/ISON (P5e), remote WHO (P5d), register_user leaf‑UNICK feed (P5q), SERVICE intro+SQUERY (P5a).
  • P5u DONE: channel.c invite cluster (m_invite + the file‑statics add_invite/list_length + check_channelmask + del_invite) via the existing channel_link.o partial port (‑DPORT_CHANNEL_P5). Second channel.c sub‑phase; first to hit the static‑function‑with‑remaining‑C‑callers case: check_channelmask (called by m_join/m_part/m_kick/m_topic/m_njoin/set_mode/send_channel_*) has its forward static prototype switched to extern under the guard so the C remnant resolves to the Rust def (linkage‑only, faithful). del_invite (exported) now resolves for s_misc.rs + free_channel/m_kick/m_njoin too. L1 channel_invite_diff (del_invite chain relink + istat deltas; the statics add_invite/list_length/check_channelmask have no oracle symbol → L2‑covered); L2 golden_channel_invite (4 scenarios: success 341 + relayed INVITE [fires add_invite live], 401, 442, 476 check_channelmask).
  • P5v DONE: channel.c can_send + match_modeid cluster (can_send [exported send‑permission predicate] + match_modeid [file‑static ban/exception matcher, via the P5u extern‑prototype switch since can_join/reop_channel stay C]); resolves the already‑Rust m_message/who callers; also fixed a latent P5u is_member bug (the active IsMember macro walks the client's channel list via find_channel_link, not chptr->members). L1 channel_can_send_diff (8 cases vs cref_can_send) + L2 golden_channel_can_send (+m moderated / +n no‑external 404 rejects).
  • P5w DONE: channel.c channel_modes leaf (the "simple" channel mode‑string serializer, channel.c:831 — writes +‑flags into mbuf and the +l/+k params into pbuf) via the existing channel_link.o partial port (‑DPORT_CHANNEL_P5). Fourth channel.c sub‑phase; a true leaf (no file‑static — writes caller buffers; only IsMember/IsServer + libc sprintf/strcat), so a plain #ifndef guard drops the exported def and resolves the still‑C callers (m_mode query, set_mode, send_channel_modes) to the Rust def. L1 channel_modes_diff (17 cases vs cref_channel_modes: every bit + secret/private precedence + +l/+k params member/server vs suppressed) + L2 golden_channel_modes (MODE #chan query → 324 RPL_CHANNELMODEIS byte‑identical, exercising both +l sprintf and +k strcat).
  • P5x DONE: channel.c membership cluster (add_user_to_channel + remove_user_from_channel + change_chan_flag — the members/clist/user->channel list manipulators + istat counters) via the existing channel_link.o partial port (‑DPORT_CHANNEL_P5). Fifth channel.c sub‑phase; touches no shared buf/modebuf static. The two file‑statics (add_user_to_channel/change_chan_flag) get extern forward decls under the guard (no existing forward decl); the still‑C free_channel is kept C via a linkage‑only staticextern switch (the Rust remove_user_from_channel calls it on the last‑user teardown). remove_user_from_channel already resolves for the Rust s_misc.rs exit cascade (P5c). L1 channel_membership_diff (the exported remove_user_from_channel vs cref_: members/clist relink + user->channel + is_userc/is_chanusers deltas; the statics are L2‑covered). L2 golden_channel_part (PART with a second member observing the relay + NAMES/WHO omitting the parted user → remove_user_from_channel; MODE +o→NAMES @bobchange_chan_flag) + the JOIN golden_channel regression covers add_user_to_channel; S2S golden_s2s_membership (remote NJOIN → add_user_to_channel, remote PART → remove_user_from_channel, observed by a local member).
  • P5y DONE: channel.c m_topic handler (the TOPIC command — per‑channel query [331/332/333] or set [propagate + 482 reject]) via the existing channel_link.o partial port (‑DPORT_CHANNEL_P5). The cleanest channel.c handler: every callee is already Rust (canonize/strtoken/find_channel/check_channelmask/is_chan_op) or a C variadic sender, and m_topic has no file‑static of its own → a plain #ifndef guard drops the C def and the m_topic decl parse.rs imports resolves to the Rust def (no extern‑prototype switch, no parse.rs/build.rs edit). TOPIC_WHO_TIME defined → the topic_t/topic_nuh blocks ported (topic_nuh = byte‑builder, not format!; 333's topic_t masked as wall‑clock volatile). L2 golden_channel_topic (lifecycle: query‑empty 331 → set → query 332/333 → change → re‑query reflects the new value; rejects 442/403/482) + S2S golden_s2s_topic (remote bob TOPIC → relay to local member + 333 with bob's topic_nuh; local alice TOPIC → sendto_match_servs UID‑sourced propagation to the peer).
  • P5z DONE: channel.c m_list handler (the LIST command — channel listing: bare lists all visible channels [322 + LIST_ALIS_NOTE notice + 323], named/!‑mask forms) via the existing channel_link.o partial port (‑DPORT_CHANNEL_P5). Like m_topic a true leaf (no file‑static of its own) → a plain #ifndef guard drops the C def; every callee is already Rust (canonize/strtoken/find_channel/hash_find_channels/get_sendq) or a C extern (hunt_server, the variadic sendto_one — whose ircd‑common extern decls were unified to -> c_int so m_list's rlen += sendto_one(…) reply‑length throttle ports faithfully). L2 golden_channel_list (bare LIST shows two public channels + 323, a +s channel a non‑member can't see is absent [the inverse], named LIST #pub, LIST #nonexistent → only 323) + S2S golden_s2s_list (the hunt_server forwarding short‑circuit: LIST #chan peer.test → the peer receives the forwarded command, byte‑identical).
  • P5aa DONE: channel.c m_part handler (the PART command — leave one/more channels: per‑channel get_channel/check_channelmask/IsMember gating, the comma‑batched server broadcast via the private scratch buf, member relay, then the now‑Rust remove_user_from_channel) via the existing channel_link.o partial port (‑DPORT_CHANNEL_P5). Its only still‑C callee is the file‑static get_channel (extern‑prototype switch on both forward decl + definition, P5u/P5x mechanism — flag 0, no CREATE); every other callee is already Rust (canonize/strtoken/check_channelmask/remove_user_from_channel) or a C variadic sender (sendto_serv_butone/sendto_match_servs/sendto_channel_butserv); get_channelmask is the JAPANESE‑off macro rindex(name,':')=strrchr. The shared static buf[BUFSIZE] is pure scratch (init+flush in‑call) → a module‑private static mut BUF is faithful. L2: the existing golden_channel_part (local success + NAMES/WHO inverse) now drives the Rust m_part, extended with its own error numerics (403 NOSUCHCHANNEL / 442 NOTONCHANNEL); S2S golden_s2s_membership already covers the remote‑PART IsServer(cptr) / sptr->user->uid propagation branch.
  • P5bb DONE: channel.c m_kick handler + the file‑static find_chasing (the KICK command — kick one/more users from one/more channels: get_channel/check_channelmask/is_chan_op gating, find_chasing/find_uid victim resolution, the comma‑batched sendto_match_servs_v(SV_UID) UID propagation, member relay via sendto_channel_butserv, then the now‑Rust remove_user_from_channel) via the existing channel_link.o partial port (‑DPORT_CHANNEL_P5). find_chasing is the cluster's shared file‑static — also called by the still‑C m_mode/set_mode, so it gets the P5u/P5x extern‑prototype switch (its C body is guarded out and an extern forward decl added so the C remnant resolves to the Rust def); check_string (the other display static) is untouched (m_kick doesn't call it). Every other callee is already Rust (get_channel [C static, extern‑switched by P5aa]/check_channelmask/is_chan_op/remove_user_from_channel/find_uid/find_client/get_history/mystrdup) or a C variadic sender (sendto_flag/sendto_match_servs_v added to the extern block). L2 golden_channel_kick (chanop KICKs member → relay + NAMES inverse; error numerics 441 [find_chasing resolves a non‑member]/403/442/482) + S2S golden_s2s_kick (UID propagation :<kicker‑uid> KICK #chan <victim‑uid> to the peer; server‑sourced :001B KICK exercising the IsServer(cptr)+find_uid / IsServer(sptr) SID‑sender branches).
  • P5cc DONE: channel.c display cluster — m_names handler + the file‑static names_channel (the NAMES command + the per‑channel 353 RPL_NAMREPLY/366 RPL_ENDOFNAMES emitter) via the existing channel_link.o partial port (‑DPORT_CHANNEL_P5). names_channel is still called by the C m_join (the JOIN echo) → P5u/P5x extern‑prototype switch (its static forward decl + body guarded out, an extern forward decl added so the C remnant resolves to the Rust def); m_names is a plain #ifndef guard (msgtab NAMES=[m_nop,m_names,m_names,m_nop,m_unreg] → its decl parse.rs imports resolves to Rust). Every callee is already Rust (find_channel_link/find_channel/clean_channelname/strtoken) or a C extern (hunt_server, the variadic sendto_one). Both share the module BUF (the C buf[BUFSIZE]): names_channel fills+flushes it in‑call and m_names only writes buf itself after all its names_channel calls → one BUF is faithful (the still‑C m_join keeps its own C buf; its !‑autocreate name=buf cross‑call aliasing is a documented residual that vanishes when m_join ports). No socket‑free data op (read‑only + wire) → L2/L2‑S2S are the gates, no L1 (cf. m_list/m_topic). L2 golden_channel_names (NAMES #pub = #pub :@alice bob 353+366; secret‑channel member @ #secret marker; inverse — a non‑member's NAMES #secret yields only 366; bare NAMES all‑channels + * * : remaining‑users section) + JOIN‑echo regression via golden_channel; S2S golden_s2s_names (the parc>2 hunt_server forwarding short‑circuit: NAMES #chan peer.test → the peer receives the forwarded command, byte‑identical).
  • P5dd DONE: channel.c JOIN cluster — m_join handler + the file‑static can_join (the JOIN command + its access‑control predicate) via the existing channel_link.o partial port (‑DPORT_CHANNEL_P5). can_join is file‑static and its only caller (m_join) ports here too → no C caller remains, so it is a private Rust unsafe fn (no cref_can_join oracle → L2‑tested via m_join's reject numerics); both its forward decl + body are plainly guarded out. m_join is a plain #ifndef guard (msgtab JOIN=[m_nop,m_join,m_join,m_nop,m_unreg] → client+server cols; its decl parse.rs imports resolves to Rust). Every callee is already Rust (strtoken/check_channelmask/clean_channelname/find_channel/hash_find_channels/check_chid/get_chid/del_invite/add_user_to_channel/remove_user_from_channel/names_channel/exit_client/match_modeid/mycmp) or a C extern (get_channel [extern‑switched by P5aa], the variadic senders sendto_serv_v/sendto_channel_butone added to the block). m_join's function‑static jbuf[BUFSIZE] → a module‑private JBUF (distinct from the shared BUF it uses as the !‑channel‑id sprintf scratch — resolving the P5cc‑documented m_join/buf residual). No socket‑free data op (membership via the already‑L1'd add_user_to_channel, then wire) → L2/L2‑S2S are the gates, no L1 (cf. m_names/m_list). L2 golden_channel_join (+k wrong‑key 475 then keyed join, +i 473 then INVITE override, +l 1 full 471, duplicate‑JOIN no‑op, JOIN 0 part‑all + NAMES inverse) + the golden_channel JOIN/part/rejoin regression; S2S golden_s2s_join ((a) a local client's JOIN of a federated #chan propagates :000A NJOIN #chan :000AAAAAA to the peer [sendto_serv_v SV_UID, local SID + joiner UID]; (b) the IsServer(cptr) head — a peer‑sourced :<uid> JOIN 0 relays the remote user's PART to a local co‑member). m_njoin/reop_channel/the mode cluster stay C.
  • P5ee DONE: channel.c m_njoin handler (the server‑only NJOIN channel‑membership burst command) via the existing channel_link.o partial port (‑DPORT_CHANNEL_P5). Twelfth channel.c sub‑phase; a handler with no standalone client path (msgtab NJOIN=[m_njoin,m_nop,m_nop,m_nop,m_unreg] → col 0 only, server‑reachable) → L2‑S2S is its only gate (no L2, no L1). Plain #ifndef guard (its decl parse.rs imports resolves to Rust; not anyone's static callee). Every callee already Rust (check_channelmask/find_person/find_uid/add_user_to_channel/get_client_name) or a C extern (get_channel [extern‑switched by P5aa, CREATE], the variadic senders). The shared modebuf/parabuf file‑statics (the synthetic‑MODE scratch) → module‑private MODEBUF/PARABUF (pure in‑call scratch — faithful, cf. P5aa's BUF); uidbuf/mbuf are C autos → Rust stack arrays. New is_bursting helper (!(flags & FLAGS_EOB)). L2‑S2S golden_s2s_njoin (the @+ op+voice MODE‑burst the membership test doesn't cover: a peer NJOINs carol @+ onto a #chan a local member is on → the relayed JOIN + the synthetic :peer.test MODE #chan +ov carol carol burst, exercising the mbuf[1] double‑param path) + the existing golden_s2s_membership (plain add/remove) now drives the Rust m_njoin as a regression; the other NJOIN‑driving goldens (join/kick/topic) stay byte‑identical.
  • P5ff DONE: channel.c mode‑id list layer (check_string + make_bei/free_bei + add_modeid/del_modeid — the ban/exception/invite +b/+e/+I list‑item alloc + mlist add/del, the connected component over the file‑static asterix var) via the existing channel_link.o partial port (‑DPORT_CHANNEL_P5). Thirteenth channel.c sub‑phase; all five are file‑static in C → the P5u/P5x extern‑prototype switch on each (the still‑C set_mode calls check_string/make_bei/add_modeid/del_modeid; set_mode+free_channel call free_bei — so each static forward decl flips to extern under the guard; check_string had none → a new extern decl added). asterix is module‑private in Rust (static mut ASTERIX): C never references it outside the now‑ported check_string/make_bei/free_bei, so the pointer identity stays self‑consistent (make_bei stores it, free_bei compares it, C only ever passes the resulting aListItem). Every callee already Rust (collapse/mycmp P1, make_link/free_link P2, istat P2 with wrapping_add/sub — cf. P5u add_invite) or libc (MyMalloc/free/strncpy/isspace). No L1 — all five file‑statics → no cref_ oracle (cf. P5v match_modeid/P5dd can_join); L2 byte‑identity through the still‑C set_mode is the faithfulness gate. L2 golden_channel_ban (local chanop +b/+e/+I add → make_bei split + add_modeid store, ban‑list query 367/348/346 reads the Rust‑built mlist, duplicate +b BanExact dedup, then -b → del_modeid + free_bei + inverse re‑query the ban is gone) + S2S golden_s2s_ban (a peer server sets +b → the non‑MyClient add_modeid arm — the pure list‑add that skips every sendto_one, reachable only over a link — then server -b → del_modeid; alice's query confirms the round‑trip).
  • P5gg DONE: channel.c mode core (m_mode + set_mode + send_mode_list + send_channel_modes + send_channel_members — the MODE handler + the ~860‑line mode parser/applier + the server‑burst emitters; the connected component over the shared modebuf/parabuf/uparabuf statics, now module‑private since m_njoin [the only other user] is already Rust) via the existing channel_link.o partial port (‑DPORT_CHANNEL_P5). Fourteenth (final large) channel.c sub‑phase. All five take a plain #ifndef guard (no extern‑switch): set_mode/send_mode_list are file‑static with no remaining C caller (m_mode/send_channel_modes port here) → private Rust unsafe fns; send_channel_modes/send_channel_members are exported and still called by the C send_server_burst#[no_mangle] resolves s_serv's calls at link; m_mode's decl resolves via the parse.rs imports. Faithful port of the full mode grammar (+/- with o/v/O/k/b/e/I/R/l + the simple‑flag table, the opcnt chops reconstruction, parseNUH via the Rust make_bei, the k key sanitizer, the reop scheduling). Bug caught by the burst S2S test: aListItem.nick/user/host are *mut c_char pointers, not arrays — the first port took addr_of! and printed pointer bytes; fixed to pass the pointers directly. L2 golden_channel_mode_set (chanop +v/-v member‑voice relay seen by a second member + NAMES +bobbob inverse) + the regression suite already driving set_mode (golden_channel_modes simple/limit/key, golden_channel_ban +b/+e/+I, golden_channel_part +o→NAMES). S2S golden_s2s_mode (outbound: a local chanop's +m/+l 5sendto_match_servs_v(SV_UID) UID‑sourced propagation to the peer; link burst: a peer relink replays #chan via send_channel_members NJOIN + send_channel_modes +tnl/+b — the only way to burst a populated # channel in the single‑peer harness, since a split local server can't pre‑create one) + golden_s2s_ban/golden_s2s_membership the inbound‑server‑MODE regression.
  • P5hh DONE: channel.c lifecycle group (get_channel/free_channel/reop_channel/setup_server_channels/collect_channel_garbage + the channel global list head) — the 15th and final channel.c sub‑phase, so channel.o is now dropped outright (added to PORTED; the P5t…P5gg channel_link.o second‑compile + its -DPORT_CHANNEL_P5 substitution removed entirely; the cref oracle keeps the unguarded channel.o). get_channel/free_channel were file‑static (extern‑switched P5aa/P5x) and reop_channel purely file‑static (collect_channel_garbage's only caller → private Rust unsafe fn); setup_server_channels/collect_channel_garbage are channel_ext.h exports called from ircd.c (boot / io_loop timer). Classification: utility/callee TU (no msgtab handler). L1 (channel_lifecycle_diff.rs, the two exported entries have cref_ oracles; the three statics are transitively covered): setup_server_channels create‑cluster (14 server channels under USE_IAUTH‑on/CLIENTS_CHANNEL‑off — chname/topic/mode/lone‑chanop walk identical) + the inverse (oracle'd remove_user_from_channel on &ERRORSfree_channel unlinks it from list+hash, the other 13 survive) + collect_channel_garbage (hand‑built &dead empty+expired vs &live → identical return now+CHECKFREQ + free/keep; pinned timeofday so free_channel's clock re‑check fires). No new L2‑S2S — none of the six has an IsServer(cptr) dispatch branch or formats remote‑user fields; get_channel/free_channel are already L2/S2S‑covered transitively by every JOIN/PART golden (golden_channel*, golden_s2s_membership), and collect_channel_garbage/reop_channel are timer‑driven (300 s, unreachable in a short golden — the myrand reop arm is review‑covered). channel.c is now FULLY ported to Rust.
  • P5ii DONE: s_serv.c informational query foundation cluster (m_version + m_time + m_admin — the three leaf informational query handlers: each a hunt_server forward + a few sendto_one(replies[…]) replies, with no file‑static of its own) — the first s_serv.c sub‑phase, establishing the s_serv_link.o partial port (‑DPORT_SERV_P5, carrying the per‑object ‑DIRCDMOTD_PATH for the still‑C m_motd; mirrors s_user_link.o) for the last/largest P5 TU. Plain #ifndef guards (none is a file‑static or anyone's static callee). Every callee already resolves: hunt_server/find_admin/sendto_one (C externs), replies (P3), date (P5c), serveropts (P5b), me/me.serv->sid/IRC_VERSION. Classification: handler cluster (msgtab col 1, min‑params 0 → client‑reachable) with a server‑reachable hunt_server forward branch → L2 + L2‑S2S, no L1 (pure wire handlers, no socket‑free data op — cf. m_list/m_topic/m_names). L2 golden_s_serv_info (local VERSION→351 / TIME→391 [localtime masked] / ADMIN→256‑259 via minimal.conf's A line) + S2S golden_s2s_s_serv_info (the hunt_server forwarding short‑circuit: VERSION/TIME/ADMIN peer.test → the peer receives the forwarded command, byte‑identical).
  • P5jj DONE: s_serv.c network‑info query cluster (m_info + m_links + the file‑static check_link) via the same s_serv_link.o partial port (‑DPORT_SERV_P5). Second s_serv.c sub‑phase; first s_serv.c extern‑prototype switch: check_link (file‑static, still called by the C m_stats/m_motd) flips its forward decl staticextern under the guard so the C remnant resolves to the Rust def (P5u/P5x mechanism); m_info/m_links take plain #ifndef guards. Every callee already resolves (hunt_server/sendto_one C externs; infotext/creation/generation version.c externs; myctime/collapse/match_/svrtop/ircstp Rust; CHREPLLEN=8192 baked). Classification: handler cluster, no L1 (pure wire handlers; check_link's counter‑mutating load branches are file‑static → no cref_ oracle + non‑deterministic over a loaded link → review‑covered, the return‑0 local path is L2‑covered). Two new canonicalizer masks for m_info's volatile 371 trailers (:Birth Date: build stamp + :On‑line since boot clock). L2 golden_s_serv_info_links (INFO 371/374 + bare LINKS 364/365) + S2S golden_s2s_s_serv_info_links (INFO/LINKS hunt_server forwards to the peer + the bare‑LINKS tree walk before/after the link — the positive/inverse pair exercising m_links's remote up->name/sid/info formatting).
  • P5kk DONE: s_serv.c informational stub cluster (m_users + the file‑static send_users + m_summon + m_help) via the same s_serv_link.o partial port (‑DPORT_SERV_P5). Third s_serv.c sub‑phase; three leaf handlers with no file‑static of their own. Config‑resolved: USERS_RFC1459 off → m_users forwards (parc>1) then calls send_users (265/266); ENABLE_SUMMON off → m_summon445; m_help lists msgtab[].cmd as NOTICEs. send_users is static in C but has a remaining C caller (the still‑C m_lusers, s_serv.c:2386) → the P5u/P5x extern‑prototype switch on its forward decl (s_serv.c:52) so that caller resolves to the #[no_mangle] Rust def. No L1 (pure wire handlers; send_users has no cref_ oracle — GCC inlined it as .isra.0). L2 golden_s_serv_stubs (USERS 265/266 + SUMMON 445 + HELP NOTICE list anchored on the final :ETRACE) + S2S golden_s2s_s_serv_stubs (the hunt_server forwarding short‑circuit: USERS peer.test / SUMMON nobody peer.test → the peer receives the forwarded command; m_help has no forward path → not in the S2S scenario).
  • P5ll DONE: s_serv.c LUSERS handler (m_lusers) via the same s_serv_link.o partial port (‑DPORT_SERV_P5). Fourth s_serv.c sub‑phase; a single leaf handler with no file‑static of its own (plain #ifndef guard) that closes the P5kk send_users loop — its all‑path tail if (all) send_users(...) already resolves to the Rust send_users. Config‑resolved (USERS_RFC1459 off): bare/* LUSERS reads istat (the all path → 251/254/255 then 265/266 via send_users); LUSERS <server> resolves a remote find_clientIsServerusercnt[]; LUSERS <mask> walks svrtop/svctop summing remote usercnt[]; parc>2 forwards via hunt_server. New is_server/is_me one‑line status helpers. Faithfulness: int locals stay c_int (C's u_longint truncation = as c_int), istat.is_chan passed straight to the %d variadic as u_long (byte‑identical, cf. send_users), and the 253 RPL_LUSERUNKNOWN uses parv0 not BadTo(parv0) quirk ported literally. Classification: handler cluster, no L1 (pure wire handler, the lookups are read‑only counting terminating in sendto_one). L2 golden_s_serv_lusers (bare LUSERS → 251/254/255 + the send_users 265/266) + S2S golden_s2s_s_serv_lusers (two server‑reachable facets: the svrtop mask walk LUSERS *.test after a peer links — the 251 server count reflects the link + the IsMe 253 arm — diffed client‑side; and the parc>2 hunt_server forward LUSERS * peer.test → the peer receives the forwarded command).
  • P5mm DONE: s_serv.c map‑display cluster (m_map + the two recursive file‑statics dump_map [plain names] / dump_map_sid [names + usercount + SID + version]) via the same s_serv_link.o partial port (‑DPORT_SERV_P5). Fifth s_serv.c sub‑phase; a handler + its two tree‑renderer statics, all under one plain #ifndef guard (the statics have no remaining C caller → private Rust unsafe fns, no cref_ oracle; m_map's decl resolves via the parse.rs msgtab imports). The shared buf[BUFSIZE] → a module‑private BUF (filled+flushed in‑call → faithful, cf. P5aa/P5cc); the recursive tree walk over serv->down/->right accumulates the output line via raw pointer arithmetic (pbuf + 4 per nesting, backward tree‑char cleanup), each leaf flushing the whole buf with 015 RPL_MAP. me is SetEOB'd at boot → dump_map_sid(&me) always takes the deterministic non‑bursting branch. Classification: handler cluster, no L1 (pure wire handlers; m_map has no hunt_server forward but dump_map/dump_map_sid format remote‑server fields → L2 + L2‑S2S). L2 golden_s_serv_map (MAP → 018/015/017 single‑server + MAP s → local usercount/SID/version) + S2S golden_s2s_s_serv_map (the multi‑server tree walk: bare MAP before/after the peer links — : \- peer.testappears — thenMAP sadds: `- peer.test 0 001B 0211030000, the peer sent an EOB` first so the non‑bursting branch is deterministic).
  • P5nn DONE: s_serv.c TRACE cluster (m_trace + m_etrace + the file‑static trace_one + the internal‑linkage count_servers_users) via the same s_serv_link.o partial port (‑DPORT_SERV_P5). Sixth s_serv.c sub‑phase; a handler pair + their shared per‑client renderer. trace_one (file‑static, only callers m_trace×2 port here) → private Rust unsafe fn; count_servers_users (the static forward decl gives it internal linkage despite the non‑static def → GCC inlined it, no symbol) is still called by the C report_myserversextern‑prototype switch (its forward decl flips staticextern under the guard so the C remnant resolves to the #[no_mangle] Rust def). HUB off → count_servers_users uses the istat‑only branch (is_serv‑1, is_user[0]+is_user[1]‑is_myclnt); XLINE off → m_etrace's user2/user3 are the "-","-" literals; ENABLE_SIDTRACE off → m_sidtrace not compiled, not ported. The volatile 200 RPL_TRACELINK uptime/sendQ ported faithfully but only diffed peer‑side (the forwarded command) in S2S. Classification: handler cluster, no L1 — pure wire handlers terminating in sendto_one; count_servers_users is a data op but has no cref_ oracle (internal linkage in the oracle archive too), so it is L2‑S2S‑covered via the 206 servers/users it feeds (cf. P5jj check_link). L2 golden_s_serv_trace (bare TRACE → 205/262 plain client; OPER+ETRACE → 709/759 via new trace_oper.conf t/ACL_TRACE oper) + S2S golden_s2s_s_serv_trace (bare TRACE after a peer links → 206 RPL_TRACESERVER from trace_one+count_servers_users with the remote serv->version/by/user fields; TRACE peer.test → the IsServer passthru forwards the command, diffed peer‑side).
  • P5oo DONE: s_serv.c STATS / reporting cluster (m_stats + the five file‑static reporters report_myservers/report_configured_links/report_ping/report_fd/report_listeners + the report_array[18][3] table) via the same s_serv_link.o partial port (‑DPORT_SERV_P5). Seventh s_serv.c sub‑phase; completes the cluster‑map "Stats / reporting" component (its siblings trace_one/check_link/count_servers_users already ported P5jj/P5nn). All five reporters are file‑static with m_stats as their only caller → private Rust unsafe fns (no extern‑switch, no cref_ oracle); report_array is a module‑private static reproduced resolved to TKLINE‑on (the two CONF_T*KILL rows present, the *p/p+=3 row walk preserved); m_stats is a plain #ifndef guard (its decl resolves via the parse.rs msgtab imports). XLINE off → report_x_lines not compiled. Faithful: the full selector switch, the IsServer(cptr) check_link rate‑limit head, both hunt_server parc==3/parc>=3 forms, the report_configured_links K/V/H/Q‑passwd‑shown + TKLINE hold‑timeofday + CLIENT/OPERATOR iline/oline_flags branches, the STATS m msgtab command‑frequency loop; report_ping/report_fd use the module BUF/ipv6string as in‑call scratch (cf. dump_map P5mm). Classification: handler cluster, no L1 (pure wire reporters terminating in sendto_one; report_array is config‑gated data with no oracle). L2 golden_s_serv_stats (STATS i 215/o 243 via report_configured_links over oper.conf's I/O lines, y 218 via report_classes, m 212 the msgtab loop — each closing 219; volatile selectors l/L/P/f/F/?/u review/mask‑covered) + S2S golden_s2s_s_serv_stats (report_myserversSTATS ? after a peer links → 249 RPL_STATSDEBUG with the peer name/1S 0C/byte counts/BURST/%X version, diffed in full bar the masked (timeconnected) real‑clock token; the hunt_server forward STATS m peer.test → the peer receives the forwarded command).
  • P5pp DONE: s_serv.c m_motd handler (the MOTD command — check_link rate‑limit + hunt_server forward, then the motd‑list reply: 375/372 + the localtime(&motd_mtime) date line/376, or 422 when motd==NULL) via the same s_serv_link.o partial port (‑DPORT_SERV_P5). Eighth s_serv.c sub‑phase; a leaf handler with no file‑static of its own → a plain #ifndef guard (its decl resolves via the parse.rs msgtab imports). Every callee already resolves: check_link (Rust P5jj), hunt_server/sendto_one (C externs), reply() (P3), the motd/motd_mtime globals (bindgen statics), localtime/tm (libc). Classification: handler cluster, no L1 (pure wire handler terminating in sendto_one). L2 golden_s_serv_motd (MOTD → 422 ERR_NOMOTD under the no‑MOTD‑file test config — the realistic local path; the listing + date line are review‑covered, wall‑clock data from the file mtime) + S2S golden_s2s_s_serv_motd (the hunt_server forwarding short‑circuit: MOTD peer.test → the peer receives the forwarded command, byte‑identical).
  • P5qq DONE: s_serv.c server‑registration data‑structure cluster (find_server_string/find_server_num [the server_name[] string‑interning pair], add_server_to_tree/remove_server_from_tree [the aServer‑tree pointer rewiring], check_servername) via the same s_serv_link.o partial port (‑DPORT_SERV_P5). Ninth s_serv.c sub‑phase — the first s_serv.c utility/callee cluster (no msgtab row; pure data ops, no socket/wire). The server_name/server_max/server_num file‑statics → module‑private Rust static mut; check_servername_errors[3][2] stays C (the s_serv_ext.h decl is a tentative definition under EXTERN‑empty → a Rust #[no_mangle] strong def would collide with the C BSS symbol, and check_servername never reads the table — only its callers index it; ported when s_serv.o is dropped). All five take a plain #ifndef PORT_SERV_P5 guard (every caller stays C — m_server/m_server_estab s_serv.c, exit_server s_misc.c, boot ircd.c, register_user s_user.c — and resolves to the Rust defs at link). Classification: utility/callee TU → L1 is the gate (s_serv_servtree_diff: server_name[] interning round‑trip incl. the MyRealloc/server_max+=50 growth boundary + the no‑duplicate‑slot re‑intern inverse; the aServer‑tree add/remove/re‑add with the leaf and head unlink inverses + the servers count; check_servername rc corpus + the exported error table — all zero‑diff vs cref_). No new L2/S2S — none has an IsServer(cptr) dispatch branch of its own (they are unconditional callees of the still‑C link handler); every symbol runs on the existing 13 golden_s2s_* peer‑link/SQUIT scenarios (link → find_server_num+add_server_to_tree+check_servername; remote users → find_server_string; SQUIT → remove_server_from_tree), which stay byte‑identical as the regression gate.
  • P5rr DONE: s_serv.c oper‑maintenance cluster (m_close + m_rehash) via the same s_serv_link.o partial port (‑DPORT_SERV_P5). Tenth s_serv.c sub‑phase — two leaf oper handlers with no file‑static of their own (plain #ifndef guards; their msgtab decls resolve to the Rust defs at link). Config‑resolved: DELAY_CLOSE onm_close’s trailing is_delayclosewait/delay_close(-2) block ported; USE_SYSLOG offm_rehash’s syslog() line not compiled/ported. Faithful: the for(i=highest_fd;i;i--) walk never touches local[0], RPL_CLOSING carries acptr->status as the %d arg, exit_client(..,&me,..) (P5c) closes each UNKNOWN/CONNECTING/HANDSHAKE fd, m_rehash returns rehash(cptr,sptr, parc>1?parv[1][0]:0) (C extern). Classification: handler cluster (dispatch col 2/STAT_OPER only — a non‑oper client hits the col‑1 m_nopriv/481 at the parse table), no L1 (pure wire handlers terminating in sendto_one/sendto_flag, no socket‑free data op), no S2S (neither has an IsServer(cptr) dispatch branch nor formats remote‑user fields). L1: none. L2 golden_s_serv_maint (success via the new maint_oper.conf O‑line flags lr: REHASH → 382 RPL_REHASHING, CLOSE → 363 RPL_CLOSEEND; inverse via oper.conf no‑flags: the handler‑internal is_allowed reject → 481).
  • P5ss DONE: s_serv.c end‑of‑burst cluster (m_eob + m_eoback) via the same s_serv_link.o partial port (‑DPORT_SERV_P5). Eleventh s_serv.c sub‑phase — the thematic end‑of‑burst pair: a linked server's EOB (burst complete) + the EOBACK ack. Both server‑only (msgtab col 0 — a local client/oper hits m_nop; m_eob's first guard is IsServer(sptr)) with no file‑static of their own (m_eob's eobbuf[BUFSIZE] is a function‑local stack scratch → a Rust let mut array, not a module static) → plain #ifndef guards; their msgtab decls resolve to the Rust defs at link. Every callee already resolves: strtoken (P1), find_sid (P4), exit_client/check_split (P5c), istat (P2), the C variadic sendto_one/sendto_flag/sendto_serv_v(SV_UID); SetEOB is a new inline flag helper. Faithful: the IsBursting(sptr) branch (MyConnect → wall‑clock "after %d seconds" SCH_NOTICE / remote → un‑masked "End of burst" notice), SetEOB+is_eobservers++, the hopcount‑1 :000A EOBACK reply, the parc<2 bare‑EOB sendto_serv_v propagation, the parc≥2 mass‑SID strtoken/find_sid loop (unknown‑SID/wrong‑direction → exit_client link drop; the eobbuf comma‑SID builder + eobmaxlen flush via raw‑pointer walk + memcpy), then check_split; m_eoback clears FLAGS_CBURST+byuid[0]. Classification: handler cluster, server‑reachable → L2‑S2S is the only gate (no L1 — pure server‑protocol handlers with no socket‑free cref_ oracle; no plain L2 — a local client can't present a STAT_SERVER prefix; cf. m_njoin P5ee). L2‑S2S golden_s2s_eob (a bursting peer's EOB:000A EOBACK reply on the peer socket; inverse — a second EOB now finds !IsBursting → no reply, proving SetEOB changed state; m_eoback → link survives a follow‑up remote introduce+WHOIS) + golden_s2s_s_serv_map (which drives a peer EOB) the regression. The mass‑SID multi‑server loop is review‑covered (the single‑peer harness reaches only the directly‑linked bare‑EOB path).
  • P5tt DONE: s_serv.c m_set handler (the SET oper command — read/change the runtime tunables POOLSIZE/ACONNECT/CACCEPT, echoing each value back as a NOTICE) via the same s_serv_link.o partial port (‑DPORT_SERV_P5). Twelfth s_serv.c sub‑phase — a single leaf oper handler with no file‑static of its own (plain #ifndef guard; its msgtab decl resolves to the Rust def at link). Config‑resolved: set_table[] is unconditional (POOLSIZE/ACONNECT/CACCEPT); TSET_SHOWALL = (int)~0 = ‑1 (bindgen drops the cast macro → baked as const TSET_SHOWALL: c_int = -1, all bits set so the no‑arg form's three acmd & TSET_* echoes all fire). Faithful: the C switch/break‑to‑bottom‑echo control flow refactored into a set_finish helper (the two "Illegal value" arms break out of the switch straight to the trailing if(acmd) echo block → ported as an early return set_finish(...), acmd unchanged so the echo still fires); poolsize > tmp keeps the C signed→unsigned promotion (tmp as c_uint); poolsize/tmp passed to %d width‑matched (as c_int). Classification: handler cluster (dispatch col 2/STAT_OPER — a non‑oper client hits the col‑1 m_nopriv/481 at the parse table; the body re‑gates on is_allowed(ACL_SET)), no L1 (pure wire handler terminating in sendto_one/sendto_flag, no socket‑free data op), no S2S (no IsServer(cptr) dispatch branch, no remote‑user fields — reads/writes local tunables only). L2 golden_s_serv_set (new set_oper.conf O‑line flag e = ACL_SET → no‑arg SET showall POOLSIZE/ACONNECT/CACCEPT echoes, SET ACONNECT OFF apply+echo, SET BOGUS → "Invalid option BOGUS" with no trailing echo [acmd stays 0, the inverse of a hit]; the oper.conf no‑flags inverse → handler‑internal is_allowed reject → 481).
  • P5uu DONE: s_serv.c m_wallops handler (the WALLOPS broadcast — relay an operwall to every other server [origin link excluded] + emit the !from! framing to local &WALLOPS watchers via sendto_flag(SCH_WALLOP)) via the same s_serv_link.o partial port (‑DPORT_SERV_P5). Thirteenth s_serv.c sub‑phase — a leaf server‑only handler (msgtab WALLOPS=[m_wallops, m_nop, m_nop, m_nop, m_unreg] → col 0/STAT_SERVER only; a local client/oper hits m_nop) with no file‑static of its own → a plain #ifndef guard; its msgtab decl resolves to the Rust def at link. The whole body is sendto_ops_butone(IsServer(cptr)?cptr:NULL, parv[0], "%s", parv[1]) (the C variadic imported from bindings, P8); USE_SERVICES off → the check_services_butone block not compiled (sptr/parc unused). Classification: handler cluster, server‑reachable → L2‑S2S the only gate (no L1 — pure wire handler terminating in the variadic; no plain L2 — a local client can't present a STAT_SERVER prefix). L2‑S2S golden_s2s_wallops (a peer :001B WALLOPS → a local &WALLOPS member receives :irc.test NOTICE &WALLOPS :!peer.test! …; inverse 1 a non‑member gets nothing [PING‑fenced]; inverse 2 the originating peer does not get its own WALLOPS echoed back [PRIVMSG‑to‑remote fenced] — proving the IsServer(cptr)?cptr:NULL origin‑link exclusion).
  • P5vv DONE: s_serv.c m_error handler (the server‑protocol ERROR message — pass an error received over a server link on to local &ERRORS watchers via sendto_flag(SCH_ERROR)) via the same s_serv_link.o partial port (‑DPORT_SERV_P5). Fourteenth s_serv.c sub‑phase — a leaf server‑only handler (msgtab ERROR=[m_error, m_nop, m_nop, m_nop, m_unreg] → col 0/STAT_SERVER only; a registered client hits m_nop, an unregistered connection m_unreg) with no file‑static of its own → a plain #ifndef guard; its msgtab decl resolves to the Rust def at link. Faithful: para = (parc>1 && *parv[1]) ? parv[1] : "<>"; the reachable server branch emits :irc.test NOTICE &ERRORS :from %s -- %s (cptr==sptr, get_client_name(cptr,FALSE)) / from %s via %s -- %s (the "via" form) via the C variadic sendto_flag (P8); returns 2 (not FLUSH_BUFFER → link survives). DEBUGMODE off → the Debug() line not compiled; the IsPerson||IsUnknown||IsService → return 2 ignore guard ported for faithfulness but unreachable via the col‑0‑only dispatch. New private is_service helper. Classification: handler cluster, server‑reachable → L2‑S2S the only gate (no L1 — pure wire handler terminating in the variadic; no plain L2 — a local client can't present a STAT_SERVER prefix). L2‑S2S golden_s2s_error (a peer :001B ERROR :the disk is full → a local oper &ERRORS member receives :irc.test NOTICE &ERRORS :from peer.test -- the disk is full; inverse a non‑member gets nothing [PING‑fenced]). The boot‑created &ERRORS channel is +i (can_join admits only opers, channel.c:2091) → new s2s_oper.conf fixture (s2s.conf + an O‑line) and alice OPERs before subscribing; the cptr != sptr "via" form (needs a server introduced behind the peer) review‑covered.
  • P5ww DONE: s_serv.c server network‑registration cluster (register_server + unregister_server — head‑insert / unlink a server's aServer on the global svrtop doubly‑linked list, called whenever a server (re)associates or loses its client struct) via the same s_serv_link.o partial port (‑DPORT_SERV_P5). Fifteenth s_serv.c sub‑phase — the first pure utility/callee s_serv.c cluster since P5qq (no msgtab row, no socket, no wire); two EXTERN intrusive‑list ops under one plain #ifndef guard (no extern‑prototype switch — every caller stays C and resolves to the Rust def: boot ircd.c, link estab m_server/m_server_estab, exit_one_client). Faithful raw‑pointer rewiring (head‑insert via svrtop->prevs/cptr->serv->nexts; splice‑out relink + svrtop advance, then clear prevs/nexts/bcptr). Classification: utility/callee → L1 the gate, no new L2/S2S (no IsServer(cptr) branch — unconditional callee of the still‑C link handler / exit cascade). L1 s_serv_register_diff (register A/B/C head‑insert → [C,B,A] + full nexts/prevs cross‑links; inverses unregister middle/head/sole node → relink + nexts/prevs/bcptr cleared, svrtop advance/empty; re‑register → clean [C,A] round‑trip — zero‑diff vs cref_). Regression: golden_s2s_link (register_server) / golden_s2s_squit (unregister_server) stay byte‑identical.
  • P5xx DONE: s_serv.c operator CONNECT cluster (m_connect — the oper command to force a server link) via the same s_serv_link.o partial port (-DPORT_SERV_P5). Sixteenth s_serv.c sub-phase. A msgtab handler row CONNECT { m_nop, m_nopriv, m_connect, m_nop, m_unreg } — col 2 (STAT_OPER) only: a plain client hits m_nopriv (481) at the parse table. Self-contained handler (no file-statics shared with other clusters; only a local port var), so dropped via a fresh #ifndef PORT_SERV_P5 guard with no extern-prototype switch (every caller already resolves to the Rust def at link). Faithful port: is_allowed(sptr, parc>3 ? ACL_CONNECTREMOTE : ACL_CONNECTLOCAL) gate → m_nopriv; BOOT_STANDALONE short-circuit; hunt_server HUNTED_ISME routing; find_name/find_mask "already exists"; the two-pass conf walk (server names, then user@host hostnames via index(host,'@')+1 — replicated with strchr(...).wrapping_add(1) to mirror C's NULL+1 when there is no @); port resolution (atoi/abs); the !IsAnOper(cptr) "Remote CONNECT … from …" ops notice; and the connect_server retval switch (0/-1/-2/default with strerror). USE_SYSLOG/SYSLOG_CONNECT off → the syslog line not compiled. New libc externs abs/strerror; the local me_name() binding renamed myname (can't shadow the static me). Classification: handler cluster with a server-reachable path → L1 N/A (needs a live server/socket; no pure data op), L2 + L2-S2S the gates. L2 golden_s_serv_connect (local opered client): new connect_oper.conf fixture (O-line flags C = ACL_CONNECT) → CONNECT nosuch.invalidNOTICE alice :Connect: Host nosuch.invalid not listed in ircd.conf (no connect_server call); inverse via plain oper.conf (no flags) → handler-internal is_allowed reject → 481 — both byte-identical vs reference-C. L2-S2S golden_s2s_s_serv_connect (boot_s2s + Peer): a remote oper (+o → STAT_OPER) behind the peer issues a remote-form CONNECT nosuch.invalid 0 irc.test → reaches m_connect over the server link (IsServer(cptr) true) → the "not listed" reply routes back out the peer; inverse a plain remote client (col 1 → m_nopriv at parse) produces no such notice — both byte-identical. The !IsAnOper(cptr) ops notice + connect_server tail run only once a real C-line matches (a live outbound socket) → no deterministic golden form; faithful C-call passthroughs covered by review. Regression: golden_s2s_link / golden_s2s_squit / golden_s_serv_maint stay byte-identical; workspace builds 0 warnings.
  • P5yy DONE: s_serv.c m_squit handler (the SQUIT command — remove a server from the network) via the s_serv_link.o partial port (-DPORT_SERV_P5). A self-contained cluster: its only module-local state is the function-local static char comment2[TOPICLEN+1] (private scratch → a module-private static mut COMMENT2, not ABI); it touches none of the server-introduction file-statics (introduce_server/send_server/get_version), so it ports cleanly without dragging in the big server cluster. Every callee is already Rust (is_allowed P5o, exit_client P5c, find_sid/find_server/next_client P2, m_nopriv P4, replies[] P3) or a C extern (get_client_name/my_name_for_link/mycmp, the variadic sendto_one/sendto_ops_butone/sendto_flag trampolines). Config-resolved: the #if defined(USE_SYSLOG) && defined(SYSLOG_SQUIT) line is out (USE_SYSLOG #undef); a local oper reaches m_squit (col 2) only because OPER_SQUIT+OPER_SQUIT_REMOTE are defined and the O-line carries flag letter S (→ ACL_SQUIT). L2 golden_s_serv_squit (local oper: SQUIT irc.test → "you cannot SQUIT me" notice + SQUIT nosuch.invalid → 402; inverse no-flag oper → handler-internal 481, no 402) + S2S golden_s2s_s_serv_squit (remote oper's SQUIT nosuch.invalid → 402 routed back out the peer link; inverse plain remote client → no 402, proving col-2 prefix-is-oper gating). The exit_client link-teardown tail stays faithful passthrough, covered by the existing golden_s2s_squit peer-EOF netsplit net.
  • P5zz DONE: s_serv.c oper-shutdown cluster (m_die + m_restart) — the leaf oper shutdown/restart pair (analogue of P5rr's m_close/m_rehash) via the s_serv_link.o partial port (-DPORT_SERV_P5); plain #ifndef guards, no extern-switch. m_die uses a local killer[]; m_restart's file-static buf was in-call scratch → a module-private RESTARTBUF (the still-C check_version/m_server_estab keep the C buf). Both re-gate is_allowed(ACL_DIE/ACL_RESTART)m_nopriv/481. L2 gate = the non-destructive reject (golden_s_serv_shutdown: flag-less oper DIE/RESTART → 481 byte-identical); the notice-loop + terminal s_die(0)/restart(buf) are destructive → review-covered; no S2S (no IsServer(cptr) branch, no remote-user fields).
  • P5aaa DONE: s_serv.c ENCAP/SDIE server-broadcast stub cluster (m_encap + m_sdie) via the s_serv_link.o partial port (-DPORT_SERV_P5) — two server-only leaf handlers (msgtab col 0) that rebuild + sendto_serv_v(cptr, SV_UID, …)-propagate a line to every other server (origin link excluded); plain #ifndef guards, no file-static. No L1 (pure wire/broadcast, no socket-free data op), L2-S2S the only gate (golden_s2s_encap: a peer's :001B ENCAP …/:001B SDIE is NOT echoed back to its origin link + the link survives a follow-up routed PRIVMSG fence — byte-identical ref-C vs Rust). The forward-to-an-additional-server arm needs a 2nd direct peer link the harness lacks → review-covered.
  • P5bbb DONE: s_serv.c server‑introduction / link‑establishment / burst cluster (m_server + m_server_estab + m_smask + check_version/get_version + send_server/introduce_server/send_server_burst + the check_servername_errors[3][2] table) — the final s_serv.c sub‑phase, so s_serv.o is now dropped outright (added to PORTED; the P5ii…P5aaa s_serv_link.o second‑compile + ‑DPORT_SERV_P5 removed; the cref oracle keeps the unguarded s_serv.o). s_serv.c is now FULLY ported to Rust. Config‑resolved (all OFF): USE_SERVICES (no check_services_butone), ZIP_LINKS (no zip_init/FLAGS_ZIP arms — encr = cptr->passwd), JAPANESE, CRYPT_LINK_PASSWORD, HUB (the #ifndef HUB leaf‑check IS compiled), USE_SYSLOG. check_servername_errors (kept C through P5qq to avoid a BSS‑symbol collision) is now a #[no_mangle] pub static mut (raw‑pointer table → the replies[]‑in‑s_err.rs pattern) since the C def is gone and ircd.c:1056 reads it. Classification: handler cluster, server‑reachable. L1 (s_serv_version_diff): check_version (transitively get_version) vs cref_ on parallel make_client'd clients with empty conf — the 0210‑beta 991199/991200 boundary, the 021x fast‑path, else→SV_OLD, the in‑place info split, the ZFLAGS_ZIPRQ flag (with a P flag so the faithful BOOT_STRICTPROT "Unsafe mode" exit isn't taken — bootopt defaults to BOOT_PROT|BOOT_STRICTPROT, ircd.c:47), and the DefInfo early‑return — the only gate on the version branches the fixed‑PEER_VERSION S2S harness can't reach. L2‑S2S: the entire existing 37‑test golden_s2s_* suite is the regression gate — every test's Peer::link() PASS/SERVER handshake drives m_server(local)→check_versionm_server_estabsend_server_burst→EOB, and golden_s2s_reject drives m_server's reject path; all byte‑identical (the lone golden_s2s_s_serv_stats red is the documented pre‑existing reference‑C uninitialized‑sendq flake, not a regression). New golden_s2s_server covers the gap — m_server's IsServer(cptr) remote‑server‑introduction branch (a peer :001B SERVER sub.test 1 002C … → remote aServer creation + introduce_server, observed via LINKS). The server‑removal inverse + m_smask + multi‑server send_server/introduce_server are review/golden_s2s_squit‑covered (the single‑peer harness can't host a fully‑formed sub‑server — SQUITting one segfaults even reference‑C).
  • P5ccc DONE: s_user.c hunt_server / next_client cluster (final s_user.c) — the two network-routing callees (resolve a command's target-server arg → forward/handle-locally/402; find the next list client matching a wild mask) ported → s_user.o dropped outright (removed s_user_link.o second-compile + -DPORT_USER_P5; s_user.c now fully Rust, only s_auth.c left in C among P5 TUs). L1 s_user_hunt_diff (next_client exact/wildcard/continuation/inverse-NULL walk on parallel client lists+hash; hunt_server ISME/PASS/NOSUCH verdicts + parv[server] rewrite + forwarded-line/error sendQ bytes vs cref_) + S2S golden_s2s_hunt (VERSION → ISME 351 local / PASS :src VERSION :peer.test at peer / next_client peer.* wildcard forward / NOSUCH 402; the whole golden_s2s_* suite is the de-facto regression gate since it routes via hunt_server).
  • P5whowas DONE: whowas.c m_whowas handler (P2‑deferred → now ported, whowas.o dropped) — the WHOWAS command handler (the last C remnant kept behind whowas_link.o/-DPORT_WHOWAS_P2 since the P2 ring port) is ported into ircd-common/whowas.rs alongside the ring machinery → whowas.o added to PORTED and dropped outright (the P2c whowas_link.o second‑compile removed; the cref oracle keeps the full unguarded whowas.o for the L1 whowas_diff). Every callee already resolves: canonize (P5m), hunt_server (P5ccc), strtoken/mycmp/myctime (P1), replies[] (P3), was/ww_index/ww_size (P2, Rust‑owned), the C variadic sendto_one + libc atoi. Faithful: the do‑while ring walk from (ww_index?ww_index:ww_size)-1, the per‑token up reset, the j>=max early break, the !MyConnect(sptr) cap of 20, the NICKLEN truncation on the not‑found path. Classification: handler cluster, server‑reachable (formats up->server remote‑user field + hunt_server forward) → no L1 (pure wire handler; the ring is already L1'd by whowas_diff), L2 + L2‑S2S the gates. L2 golden_whowas (alice QUIT → bob WHOWAS alice → 314/312/369 found path + max 1 early break + unknown‑nick 406 inverse; 312 ctime masked locally) + S2S golden_s2s_whowas (remote rmuser QUIT → local WHOWAS renders 312 with peer.test; WHOWAS ghost 1 peer.testhunt_server forwards peer‑side). golden_s_user_canonize (the 406/369 dedup path) now drives the Rust m_whowas as a regression.
  • P5hash DONE: hash.c m_hash (HAZH) handler — ports the last C remnant of hash.c (P2 left m_hash in hash_link.o) into ircd-common/hash.rshash.o dropped outright (added to PORTED; the P2d hash_link.o second-compile deleted). A handler cluster reachable only from a local oper (msgtab HAZH col 2 = STAT_OPER, re-gated on is_allowed(ACL_HAZH)); DEBUGMODE off → show/list/show_hash_bucket not compiled. Faithful %f stat lines via (expr as f32) as f64 (C (float)(double) + varargs promotion). L2 golden_s_hash (oper HAZH/HAZH c usage+statistics; non-oper HAZH c → 481 m_nopriv, the dispatch-column inverse) byte-identical. No S2S (no IsServer branch / remote-user fields).