draft/channel-rename: services-arbitrated registered-channel rename - #94
Draft
MrLenin wants to merge 9 commits into
Draft
draft/channel-rename: services-arbitrated registered-channel rename#94MrLenin wants to merge 9 commits into
MrLenin wants to merge 9 commits into
Conversation
…nd burst
Upstream re-synthesizes SERVER-line flags from bitflags at every relay and
burst site rather than echoing parv[7] verbatim (set_server_flags() has no
default case, so unknown letters are silently discarded). This means a
capability flag only survives crossing a hub if it is plumbed into (a) the
parser, (b) a Client flag bit, and (c) every single site that reconstructs
the flag string for a peer. Miss one and the flag silently dies one hop out.
Add FLAG_RENAME_CAPABLE ('r') as the dedicated signal for "this server
applies and relays RENAME" (decision recorded in
nefarious-upstream-rename-backport.md §3 — a dedicated flag rather than
overloading 'v', which would falsely advertise the full fork v3 S2S dialect
to a peer that only understands RENAME).
Plumbing:
- include/client.h: FLAG_RENAME_CAPABLE in enum Flag (grouped with the other
server-capability flags: HUB/IPV6/SERVICE/OPLEVELS) + IsRenameCapable()/
SetRenameCapable() macros.
- ircd/m_server.c set_server_flags(): parse 'r'.
- Five emit sites, all updated:
1. ircd/s_bsd.c (own SERVER line, pre-registration handshake)
2. ircd/s_serv.c server_estab() (own SERVER line, post-link)
3. ircd/m_server.c (relaying a remote server's introduction to peers)
4. ircd/s_serv.c (bursting a newly-linked server's info to peers)
5. ircd/s_serv.c (bursting already-known servers to the new link)
Sites 1-2 advertise 'r' unconditionally: this branch is atomic for PR
purposes and the RN application/relay machinery lands in the same branch,
so there is no window where we'd claim the flag without honoring it. Sites
3-5 relay/burst IsRenameCapable(<the other server>) conditionally, exactly
mirroring how IsHub/IsService/IsIPv6/IsOpLevels are already handled at each
of those call sites.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Full RENAME (IRCv3 draft/channel-rename) support ported from the fork's feature/channel-rename branch (@ 6ad0c49), building on the previous commit's 'r' (rename-capable) SERVER flag. Ported pieces: - hChangeChannel() (hash.c/hash.h): rehash a channel under its new name. - rename_channel() (channel.c/channel.h): in-place or reallocating rename preserving members, modes, invites, and any pending destruct event. - draft/channel-rename client capability (capab.h + m_cap.c), gated by new FEAT_CAP_draft_channel_rename (default off, like the fork). - FEAT_RENAME_SERVICES (default off): gates the AC R services query; only a services build that disambiguates RENAME requests from account-stamp notifications on the AC 'R' subcommand may enable it. - send_fail() (send.c/send.h): minimal tag-free IRCv3 standard-replies FAIL emitter; wire format matches the fork's untagged branch exactly (bare "FAIL <cmd> <code> [<context>] :<description>"). - RENAME/RN message token (msg.h) + parse.c msgtab entry. - m_rename.c: client handler (cap/membership/chanop checks, legacy-server guard, immediate rename for unregistered channels, AC R cookie round- trip to services for +R channels with pending-rename table, 10s timeout, client-exit cleanup, and ZANNELS-off completion re-validation), server handler (apply + relay), RENAME-to-capable-clients with PART/JOIN+TOPIC+ NAMES fallback for clients without the cap. - m_account.c AC A/D branch: parc<4 relaxed to parc<3; non-server parv[1] is treated as a pending-rename cookie (complete/deny); the LOC reply path is untouched otherwise. - s_misc.c: pending_rename_client_exit() on client exit. - ircd.c: SetRenameCapable(&me) so GlobalClientList scans (which include &me) see this server as capable of what its SERVER lines advertise. Deliberate simplifications relative to the fork: - No bouncer subsystem: all alias/primary source rewrites dropped. - No batch/labeled-response/server-time decoration; send_fail is tag-free. - The guard and the RN broadcast key on FLAG_RENAME_CAPABLE (the 'r' SERVER flag) instead of the fork's IRCv3-aware 'v' flag; the broadcast is sendcmdto_flag_serv_butone(..., FLAG_RENAME_CAPABLE, FLAG_LAST_FLAG, ...) rather than the fork's sendcmdto_serv_butone_v3. - No separate legacy-services RN forward: a services package that wants RN advertises 'r' itself and is reached by the flag-filtered broadcast; non-capable services remain exempt from the guard and get nothing. - CapOwnHas -> CapActive (upstream's native active-cap check). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
struct PendingRename embeds its own struct Timer, but pending_rename_timeout_cb() was calling MyFree(pr) from the ET_EXPIRE branch. timer_run() (ircd_events.c) sets GEN_MARKED on the timer generator before dispatching ET_EXPIRE and keeps touching that same memory after the callback returns (clearing GEN_MARKED, then generating ET_DESTROY, which bumps gh_ref and calls through gh_call) -- all of which lands on freed memory once ET_EXPIRE has freed the container. The two timer_del() calls that used to sit alongside the free could not prevent this: timer_del() called while mid-dispatch just hits its "timer is being used" early return and does nothing. Fix: split unlink from free. pending_rename_unlink() removes pr from the pending_renames list without touching the timer or memory. ET_EXPIRE now calls only that, plus pr->timer_active = 0, and returns without freeing -- the struct survives until timer_run's post-callback bookkeeping is done. The actual MyFree(pr) moved to the ET_DESTROY case, which fires either automatically right after ET_EXPIRE (the timeout path) or synchronously out of timer_del() when the timer is NOT mid-dispatch (the complete/deny/client-exit paths, via the new pr->timer_active guard in pending_rename_remove() and pending_rename_client_exit()). This keeps a single free-once invariant across all five teardown paths (complete-success, complete-failure, deny, timeout, client-exit): every one of them ends up unlinking via pending_rename_unlink() and then either triggering timer_del()'s synchronous ET_DESTROY or leaving that to timer_run(), with MyFree(pr) happening in exactly one place -- pending_rename_timeout_cb()'s ET_DESTROY case. Mirrors the established ET_DESTROY-defers-the-free idiom already used for embedded-timer containers in this tree (s_auth.c's auth_timeout_callback, bouncer-fork descendants of this same file family). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
X3's AC rename-permission reply now carries an explicit RENAME
discriminator token after the A/D type ("AC <unum> A RENAME" / "AC
<unum> D RENAME :<reason>"). Restructure the type=='A'||'D' branch in
ms_account() to check for that discriminator FIRST and route straight
to pending_rename_find()-by-cookie, before ever calling FindNServer().
Previously the rename reply and a LOC reply were disambiguated only by
FindNServer(parv[1]) failing to resolve a server numeric — a decimal
rename cookie could alias a live server numeric on the wire and get
silently misrouted or dropped (F2). With the discriminator in place
first, cookie routing no longer depends on that guess, and the
now-redundant no-discriminator cookie fallback is removed; the LOC
path keeps its own explicit "if (!acptr) return 0" for genuine unknown
numerics.
The reason string shifts from parv[3] to parv[4] on deny, since parv[3]
is now the RENAME token itself.
Ported identically from the nefarious fork (feature/channel-rename);
this repo's pre-change m_account.c matched the fork's byte-for-byte in
this branch.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
When a channel has oplevels active (+A / MODE_APASS), ownership is founder-based; a plain chanop opped by the founder must not rename the channel out from under them. Gate both the request-time authorization and the services-completion re-validation on OpLevel(member)==0 when the channel carries +A. No effect on channels without oplevels. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Per draft/channel-rename the capability governs only how a client is NOTIFIED (RENAME message vs PART/JOIN fallback), not whether it may issue the command. Stop returning ERR_UNKNOWNCOMMAND to non-cap senders. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Companion to the X3 decision to keep 'z' as the registered-channel wire letter: without a bridge, no channel ever carries MODE_REGISTERED and m_rename's services-arbitration gate is unreachable -- a RENAME of a registered channel silently takes the direct path, bypassing services and desyncing the registration. New FEAT_REGISTERED_FROM_PERSIST (default ON): in mode_parse's flush, a persist-exmode transition also flips MODE_REGISTERED in t_mode. Internal-only mirror -- modebuf emissions are already built at that point, so no 'R' is relayed for a mirrored flip; the wire stays 'z'-driven. The 'z' setter gate (burst/services sources only) is the authority gate. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This reverts commit 344dd4f.
do_join() blocks BADCHAN-glined channels per joiner, but RENAME moved an entire membership onto a banned name without ever consulting the gline table — a wholesale bypass of the ban. Mirror do_join's exact check (GLINE_BADCHAN | GLINE_EXACT, active, oper-exempt) at request validation, and re-run it in pending_rename_complete since a badchan can land during the services round-trip and services arbitrate ownership, not G-line policy. Deliberately not enforced in ms_rename: local badchans never blocked remote users' presence (join-time, local policy), so remote-origin renames are accepted the same way remote joins are. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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 IRCv3
draft/channel-renamewith services arbitration for registered channels. Draft — for review; the end-to-end flow needs a services build (X3) that advertises the newrserver flag (see Dependency below), and the feature is behind a default-off feature flag.What this does
A client with
draft/channel-renamesendsRENAME <old> <new> :<reason>. For an unregistered channel the server renames in place and relays. For a registered channel (+R), the server asks the services server for permission and applies the rename only on approval:RENAME→ server emitsAC <unum> R <cookie> <#chan> RENAME <new>to the services server.AC <cookie> A(approve) /AC <cookie> D :<reason>(deny).rename_channel()re-keys the channel (in-place or realloc,hChangeChannel), members see PART/JOIN + TOPIC/NAMES re-presentation, andRN <old> <new> :<reason>relays to rename-capable peers.Pieces
r(rename-capable) SERVER flag (FLAG_RENAME_CAPABLE): advertised in our SERVER line, parsed inset_server_flags, and — importantly — re-emitted at every flag-synthesis site (relay + burst), since upstream rebuilds flag strings from bitflags rather than relaying them verbatim. A flag that isn't carried at every emit site dies crossing a hub.RENAMEis refused (honestFAIL) while any non-rename-capable, non-services server is linked — so a legacy server that can neither apply nor relayRNnever causes divergence. Auto-relaxes when the last one delinks.sendcmdto_flag_serv_butone.hChangeChannel+rename_channelchannel-rename mechanics.FEAT_RENAME_SERVICES(default off) +draft/channel-renameCAP +FEAT_CAP_draft_channel_rename.FAIL/standard-replies: adds a minimalsend_fail(source-prefixed, per the standard-replies spec).ms_accountto route the approve/deny reply back to the pending client.+R) that closes the ZANNELS-off destruct-and-recreate window.Dependency for end-to-end use
RN is delivered only to
r-advertising peers. A services package must advertiser(and implement the RN handler + AC R authorization) to participate; without it, services would authorize a rename it never learns completed. The companion X3 work is a separate draft PR.Notes
Deliberately excludes the fork's bouncer/message-tag/batch machinery — plain CAP, bare RENAME/PART/JOIN presentation. Backported from the evilnet fork's implementation, verified end-to-end on a test network against X3.
🤖 Generated with Claude Code