Channel rename support: RN handler, AC R rename arbitration - #57
Draft
MrLenin wants to merge 8 commits into
Draft
Conversation
MrLenin
force-pushed
the
feature/channel-rename
branch
from
August 1, 2026 06:38
b44a20b to
1d80d7d
Compare
Implement Task 1 of the channel-rename foundation: RenameChannel() primitive and rename hook array in X3's hash layer. The implementation follows the existing del-channel hook pattern (dcf_list): - reg_channel_rename_func() registers rename handlers with optional context - Hook array (crf_list) and extra-data list (crf_list_extra) with dynamic growth on first use (8-entry initial, doubling on capacity exhaustion) - RenameChannel(old_node, new_name) creates the new node with all state copied, updates dict keys, then fires hooks with BOTH nodes alive Key design constraint: old node's dict key is an interior pointer to name[]; removal must happen before free(). Hooks receive both nodes because pointer-compare holders need old node address while name-keyed dicts need old->name intact for any final cleanup lookups. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> (cherry picked from commit 61f8e11)
Task 2 of the channel-rename plan: each module that keeps a persistent struct chanNode * now registers a channel_rename_func_t hook (Task 1, 83af825) next to its existing reg_del_channel_func/reg_new_channel_func call, so a RenameChannel() call re-points every holder instead of leaving it dangling on the freed old node. - chanserv.c: new_chan->channel_info->channel back-pointer (the memcpy in RenameChannel moves channel_info onto new_chan but leaves chanData->channel targeting old_chan); walks adduser_pendings; swaps matches in chanserv_conf.support_channels. - opserv.c: compare-swap debug_channel/alert_channel/staff_auth_channel; walks opserv_user_alerts re-pointing each alert's discrim->channels[0..channel_count); removes the pending opserv_part_channel purge-lock timer for old_chan without re-adding (mirrors opserv_channel_delete, but ignores func too since the node is about to be freed); recomputes bad_channel on the new node. - spamserv.c: follows spamserv_cs_move_merge's existing chanInfo re-point + registered_channels_dict re-key pattern; additionally walks every connected_users_dict entry's spam/flood/joinflood node chains re-pointing ->channel. - mod-helpserv.c: walks helpserv_bots_dict re-pointing hs->helpchan and each hs->page_targets[PGSRC_COUNT]; re-keys the single helpserv_bots_bychan_dict entry for the renamed channel (key is the interior helpchan->name pointer). - mod-snoop.c, mod-track.c, mod-blacklist.c: single compare-swap of each module's channel config slot. mod-blacklist.c has a pre-existing compile break unrelated to this change (reg_new_user_func/reg_exit_func called with too few args, predating this commit) that already excludes it from the configured --enable-modules set; the new hook and its registration call are correctly typed and were verified with a standalone compile of just those lines. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> (cherry picked from commit b62a9f6)
The legacy P10 "AC <target> R <account>" stamp and the new ircd rename
permission query "AC <unum> R <cookie> <#chan> RENAME <new>" both use
subcommand R, and both send the SAME low argc/argv[3] positioning that
made them collide: naively treating every R as an account stamp would
poison the account cache with a cookie or a channel name instead of a
handle. Disambiguate on shape (argc >= 7 && argv[5] == "RENAME") before
falling through to the unchanged legacy call_account_func() path.
The rename branch replies with the cookie as parv[1] ("AC <cookie> A"
or "AC <cookie> D :<reason>"), NOT the LOC reply shape ("AC <servnum>
A <cookie>") — ircd's m_account.c keys pending renames on parv[1]
failing a server-numeric lookup, so the cookie must lead.
Authorization policy (chanserv_rename_allowed, chanserv.c/.h): owner
(UL_OWNER, access 500) only, mirroring cmd_move's DNR-against-the-new-
name gate, minus the IsHelping/"force" bypass — staff override here
comes only from _GetChannelUser()'s override=1 synthetic access entry.
Also denies on unauthenticated requester, protected/suspended source
channel, a blocked or already-registered new name, and unregistered
target channels are allowed through untouched (nothing to protect).
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
(cherry picked from commit a21f605)
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> (cherry picked from commit eb236a0)
…name Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> (cherry picked from commit 867c97f)
…annelName) IsChannelName() has no length ceiling, allowing overlong channel names to enter fixed-size sprintf buffers in hash.c and chanserv.c. Added length validation in RenameChannel() and chanserv_rename_allowed() to reject names exceeding CHANNELLEN (200 bytes). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> (cherry picked from commit cad2a2d)
reg_channel_rename_func(chanserv_channel_rename, NULL) was registered
inside init_chanserv()'s if(nick) block, but saxdb_register("ChanServ",
...) is unconditional. With the bot nick disabled (the "." convention),
registered channels still load from the DB via chanserv_saxdb_read, so
channel_info exists and is live -- but with the rename hook never
registered, an RN arriving over the wire runs RenameChannel() with no
callback to repoint channel_info->channel at the new node. It's left
pointing at the chanNode RenameChannel() just freed: a use-after-free at
the next saxdb write (or any other access through channel_info->channel).
chanserv_channel_rename() only touches channel_info, adduser_pendings,
and chanserv_conf.support_channels -- none of which depend on the bot
nick being enabled -- so it's safe to register unconditionally. Moved it
out of if(nick), alongside the other already-unconditional hook
registrations (reg_handle_rename_func, reg_unreg_func).
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
(cherry picked from commit 2b0e89c)
Change 1: irc_server() now emits +s6or (was +s6o) on both the initial J10 self-burst line and the relay P10/J10 line, advertising the ircu upstream-standard r (rename-capable) SERVER flag so an r-aware ircd delivers RN tokens to this link. No-op on the fork, which routes channel-rename traffic via IsService rather than the r flag. Change 2 (F2): the AC rename-permission reply (cmd_account, the R subtype) now carries an explicit RENAME discriminator token right after the A/D type: "AC <unum> A RENAME" / "AC <unum> D RENAME :<reason>". Previously the reply shape was indistinguishable on the wire from an AC LOC reply once the receiving ircd fell through its FindNServer() check — a decimal rename cookie could alias a live server numeric and get misrouted. The discriminator lets ms_account() on both ircd trees route by cookie unconditionally, without ever calling FindNServer() on the rename path. Companion ircd-side changes land in the nefarious and nefarious-upstream repos (m_account.c), same commit message, same branch strategy (feature/backport channel-rename); all three must ship together since X3 now always emits the RENAME token. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> (cherry picked from commit d213568)
MrLenin
force-pushed
the
feature/channel-rename
branch
from
August 30, 2026 06:07
1d80d7d to
1415576
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds services-side support for IRCv3 draft/channel-rename against nefarious2's services-arbitration machinery. Draft: functionally blocked on the upstream nefarious2 patch that emits the rename query and RN token — the X3 side is complete and E2E-verified against the fork carrying that machinery.
Split: the registered-mode
+Rremap that used to ride in this PR is now its own PR (#60) — merge that first, since the ircd only arbitrates renames for channels it sees as registered. The BX-P bouncer commits this branch previously sat on top of are #56 and have been dropped from here.What this does
RenameChannel()primitive + rename hook array (hash.c/h): re-keys a live channel under a new name — new tail-allocated node, members/banlists moved with backpointers re-pointed,channelsdict re-keyed (the key is an interior pointer into the node), locks inherited, per-module hooks (reg_channel_rename_func) fired with both nodes alive. Hooks added for chanserv, opserv, spamserv, helpserv, snoop, track, blacklist covering every persistentchanNode*holder (audited exhaustively). Hook registered regardless of bot presence.Rdisambiguation (proto-p10.c): the rename permission queryAC <unum> R <cookie> <#chan> RENAME <new>is now distinguished from the legacy account stamp (argc>=7 +RENAMEkeyword). This also fixes a real bug: the stamp path previously consumed the query's cookie as an account stamp, silently discarding the requesting user's legitimate stamp for the session. The reply carries an explicitRENAMEdiscriminator (AC <cookie> A RENAME/AC <cookie> D RENAME :<reason>) so the ircd can route it by cookie without aFindNServer()guess.chanserv_rename_allowed): denies below UL_OWNER, protected/suspended channels, bad/DNR'd/already-registered/overlong target names (CHANNELLEN cap —IsChannelNamehas no length ceiling).rename_dnr_duration, default 1d) so it can't be re-registered out from under the rename.rserver flag (rename-capable) in the SERVER line so an upstream-style ircd delivers RN to us.RenameChannelresult checked incmd_rename.Verification
Live E2E on the Afternet testnet against the fork: owner rename approved and applied with X3's registration following (INFO/ACCESS/MYACCESS track the new name), op-level user denied with the owner-only reason surfaced to the client, account stamp intact after a denied query, old name DNR-refused post-rename. Vitest suite (6 cases) in the testnet repo (
channel-rename-services.test.ts).🤖 Generated with Claude Code