Proposal
In the P11 protocol revision, change the server-to-server KILL (D)
syntax so the kill path is its own parameter and the trailing parameter
carries only the reason, and cap the path length so it can no longer
squeeze the reason out of the line.
- Today:
… D <victim> :<path> <reason> — one trailing parameter,
split on the first space.
- Proposed (P11):
… D <victim> <path> :<reason> — path as a middle
parameter, reason as a clean trailing, with a bounded path.
The problem
The server relay packs path and reason into a single trailing parameter
separated by the first space (m_kill.c:135):
sendcmdto_serv_butone(sptr, CMD_KILL, cptr, "%C :%s!%s %s",
victim, inpath, path, msg);
The receiver reconstructs the two halves positionally
(m_kill.c:194-197):
if (!(msg = strchr(path, ' '))) /* everything after first space = reason */
msg = "(No reason supplied)";
else
*(msg++) = '\0'; /* everything before it = path */
The path grows on every hop — each relaying server prepends its own name
via inpath. Two consequences follow:
- The reason is last in the buffer, so it is what gets truncated.
The whole trailing parameter is bounded by BUFSIZE (512). As the
accumulated path lengthens across hops (server names up to
HOSTLEN, and longer still after HIS rewriting), it consumes the
line from the front and the reason at the tail is clipped — on a wide
network it can be lost entirely.
- The split is positional and fragile. It relies on the path
containing no spaces (true today only because server names and nicks
have none) and on the reason being "whatever is left". It is
parse-by-convention, not by protocol structure.
Why the proposed shape fixes it
- The reason becomes unclippable by the path. It is its own trailing
token; capping the path to a fixed budget (e.g. a KILLPATHLEN) makes
a runaway path truncate itself instead of the reason. This inverts
today's failure mode.
- The parse becomes structural. No more split-on-first-space; path
and reason arrive as distinct parv[] entries, and the reason may
contain any spaces freely (it already does).
- Moving the path to a middle parameter is safe. Middle parameters
cannot contain spaces, but the path (s1!s2!nick) never does.
Wrinkles to plan for
- Per-link formatting during relay is the real cost.
KILL
propagates across links that may be mixed P10/P11.
sendcmdto_serv_butone broadcasts one format string to all server
links, so the relay must emit the P10 combined form to
Protocol() < 11 links and the P11 split form to Protocol() >= 11
links — two passes or a per-target format. Same protocol-gating
discipline feat/p11 already uses (29794e4), applied at a fan-out point.
- Truncation direction. The path accumulates newest-hop-first
(inpath prepended). Decide what to keep when capping — likely the
originating end plus nearest hops, trimming the middle — and whether
to mark a truncated path (e.g. a trailing marker) so a reader knows it
is incomplete. The path is informational only (oper snotices at
m_kill.c:121, kill logging at :125), so lossiness is acceptable.
- Worth deciding: keep the path at all? With HIS and
FEAT_HIS_KILLWHO already rewriting or hiding the who/path shown to
users, the accumulated path is mostly an oper-debugging trace for kill
loops. A more aggressive option is to drop it and send only the
originating server plus the reason. The "separate and truncate"
proposal is the conservative choice that preserves the trace; that is
the recommended default unless the path is considered vestigial.
Tests
Following the S2S remote-observer convention (assert on the receiving
leaf, not just locally):
- Relay a
KILL through enough hops to overflow the old combined
trailing parameter; assert the reason arrives intact on the far side
(the regression test for the reason-loss bug).
- Mixed P10/P11 relay: assert a
Protocol() < 11 link receives the
combined :<path> <reason> form and a >= 11 link receives the split
<path> :<reason> form.
- Path-cap: relay a
KILL whose path exceeds the cap; assert the path
is truncated and the reason is untouched.
Relationship to other P11 work
Companion to the other P11 protocol changes: #114 (mandatory MODE
timestamp), #116 (burst ban setter/timestamp), #118 (deprecating P9),
and #120 (per-member delayed-join state in BURST). Happy to prototype
the relay change on a branch if there's agreement.
Proposal
In the P11 protocol revision, change the server-to-server
KILL(D)syntax so the kill path is its own parameter and the trailing parameter
carries only the reason, and cap the path length so it can no longer
squeeze the reason out of the line.
… D <victim> :<path> <reason>— one trailing parameter,split on the first space.
… D <victim> <path> :<reason>— path as a middleparameter, reason as a clean trailing, with a bounded path.
The problem
The server relay packs path and reason into a single trailing parameter
separated by the first space (
m_kill.c:135):The receiver reconstructs the two halves positionally
(
m_kill.c:194-197):The path grows on every hop — each relaying server prepends its own name
via
inpath. Two consequences follow:The whole trailing parameter is bounded by
BUFSIZE(512). As theaccumulated path lengthens across hops (server names up to
HOSTLEN, and longer still after HIS rewriting), it consumes theline from the front and the reason at the tail is clipped — on a wide
network it can be lost entirely.
containing no spaces (true today only because server names and nicks
have none) and on the reason being "whatever is left". It is
parse-by-convention, not by protocol structure.
Why the proposed shape fixes it
token; capping the path to a fixed budget (e.g. a
KILLPATHLEN) makesa runaway path truncate itself instead of the reason. This inverts
today's failure mode.
and reason arrive as distinct
parv[]entries, and the reason maycontain any spaces freely (it already does).
cannot contain spaces, but the path (
s1!s2!nick) never does.Wrinkles to plan for
KILLpropagates across links that may be mixed P10/P11.
sendcmdto_serv_butonebroadcasts one format string to all serverlinks, so the relay must emit the P10 combined form to
Protocol() < 11links and the P11 split form toProtocol() >= 11links — two passes or a per-target format. Same protocol-gating
discipline feat/p11 already uses (29794e4), applied at a fan-out point.
(
inpathprepended). Decide what to keep when capping — likely theoriginating end plus nearest hops, trimming the middle — and whether
to mark a truncated path (e.g. a trailing marker) so a reader knows it
is incomplete. The path is informational only (oper snotices at
m_kill.c:121, kill logging at:125), so lossiness is acceptable.FEAT_HIS_KILLWHOalready rewriting or hiding the who/path shown tousers, the accumulated path is mostly an oper-debugging trace for kill
loops. A more aggressive option is to drop it and send only the
originating server plus the reason. The "separate and truncate"
proposal is the conservative choice that preserves the trace; that is
the recommended default unless the path is considered vestigial.
Tests
Following the S2S remote-observer convention (assert on the receiving
leaf, not just locally):
KILLthrough enough hops to overflow the old combinedtrailing parameter; assert the reason arrives intact on the far side
(the regression test for the reason-loss bug).
Protocol() < 11link receives thecombined
:<path> <reason>form and a>= 11link receives the split<path> :<reason>form.KILLwhose path exceeds the cap; assert the pathis truncated and the reason is untouched.
Relationship to other P11 work
Companion to the other P11 protocol changes: #114 (mandatory
MODEtimestamp), #116 (burst ban setter/timestamp), #118 (deprecating P9),
and #120 (per-member delayed-join state in
BURST). Happy to prototypethe relay change on a branch if there's agreement.