Skip to content

Commit ab0f4af

Browse files
progvalspb
authored andcommitted
NOTICE: Do not return error on invalid/non-existent target
like the RFCs say, and all IRCds (but InspIRCd UnrealIRCd) do This implements PositionalArgument for `Result<T, &'a str>`, and re-uses it in NAMES which also needs to do some special handling in case of error while parsing its arguments
1 parent 3b9cd5b commit ab0f4af

File tree

3 files changed

+64
-16
lines changed

3 files changed

+64
-16
lines changed

sable_ircd/src/command/handlers/names.rs

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,17 @@ fn handle_names(
55
server: &ClientServer,
66
response: &dyn CommandResponse,
77
source: UserSource,
8-
channel: &str,
8+
channel: Result<wrapper::Channel, &str>,
99
) -> CommandResult {
10-
if let Ok(channel_name) = &ChannelName::from_str(channel) {
11-
if let Ok(channel) = server.network().channel_by_name(channel_name) {
12-
crate::utils::send_channel_names(server, &response, &source, &channel)?;
13-
return Ok(());
10+
match channel {
11+
Ok(channel) => Ok(crate::utils::send_channel_names(
12+
server, &response, &source, &channel,
13+
)?),
14+
Err(channel_name) => {
15+
// "If the channel name is invalid or the channel does not exist, one RPL_ENDOFNAMES numeric
16+
// containing the given channel name should be returned." -- https://modern.ircdocs.horse/#names-message
17+
response.numeric(make_numeric!(EndOfNames, channel_name));
18+
Ok(())
1419
}
1520
}
16-
17-
// "If the channel name is invalid or the channel does not exist, one RPL_ENDOFNAMES numeric
18-
// containing the given channel name should be returned." -- https://modern.ircdocs.horse/#names-message
19-
response.numeric(make_numeric!(EndOfNames, channel));
20-
21-
Ok(())
2221
}

sable_ircd/src/command/handlers/notice.rs

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,35 @@ async fn handle_notice(
55
server: &ClientServer,
66
source: UserSource<'_>,
77
cmd: &dyn Command,
8-
target: TargetParameter<'_>,
8+
target: Result<TargetParameter<'_>, &str>,
99
msg: &str,
1010
) -> CommandResult {
11+
let Ok(target) = target else {
12+
/* No such target. However, the spec say we should not send an error:
13+
*
14+
* "automatic replies must never be
15+
* sent in response to a NOTICE message. This rule applies to servers
16+
* too - they must not send any error reply back to the client on
17+
* receipt of a notice"
18+
* -- <https://tools.ietf.org/html/rfc1459#section-4.4.2>
19+
*
20+
* "automatic replies MUST NEVER be sent in response to a NOTICE message.
21+
* This rule applies to servers too - they MUST NOT send any error repl
22+
* back to the client on receipt of a notice."
23+
* -- <https://tools.ietf.org/html/rfc2812#section-3.3.2>
24+
*
25+
* "This rule also applies to servers – they must not send any error back
26+
* to the client on receipt of a NOTICE command"
27+
* -- https://modern.ircdocs.horse/#notice-message
28+
*
29+
* and most other servers agree with the specs, at least on non-existent
30+
* channels.
31+
*/
32+
return Ok(());
33+
};
1134
if msg.len() == 0 {
12-
return numeric_error!(NoTextToSend);
35+
// Ditto
36+
return Ok(());
1337
}
1438

1539
if let Some(user) = target.user() {
@@ -19,7 +43,10 @@ async fn handle_notice(
1943
}
2044
}
2145
if let Some(channel) = target.channel() {
22-
server.policy().can_send(&source, &channel, msg)?;
46+
if server.policy().can_send(&source, &channel, msg).is_err() {
47+
// Silent error, see above
48+
return Ok(());
49+
}
2350
}
2451

2552
let details = event::details::NewMessage {

sable_ircd/src/command/plumbing/argument_type.rs

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -144,11 +144,33 @@ impl<'a> PositionalArgument<'a> for u32 {
144144
}
145145

146146
impl<'a, T: PositionalArgument<'a>> PositionalArgument<'a> for Option<T> {
147-
fn parse<'b>(ctx: &'a dyn Command, arg: &'b mut ArgListIter<'a>) -> Result<Self, CommandError>
147+
fn parse<'b>(
148+
ctx: &'a dyn Command,
149+
arg_list: &'b mut ArgListIter<'a>,
150+
) -> Result<Self, CommandError>
148151
where
149152
'a: 'b,
150153
{
151-
Ok(T::parse(ctx, arg).ok())
154+
Ok(T::parse(ctx, arg_list).ok())
155+
}
156+
157+
fn parse_str(_ctx: &'a dyn Command, _value: &'a str) -> Result<Self, CommandError> {
158+
unreachable!();
159+
}
160+
}
161+
162+
/// Either Ok(parsed_value) or Err(raw_value) if it cannot be parsed.
163+
/// Never actually returns a CommandError.
164+
impl<'a, T: PositionalArgument<'a>> PositionalArgument<'a> for Result<T, &'a str> {
165+
fn parse<'b>(
166+
ctx: &'a dyn Command,
167+
arg_list: &'b mut ArgListIter<'a>,
168+
) -> Result<Self, CommandError>
169+
where
170+
'a: 'b,
171+
{
172+
let s = arg_list.next().ok_or(CommandError::NotEnoughParameters)?;
173+
Ok(T::parse_str(ctx, s).map_err(|_| s))
152174
}
153175

154176
fn parse_str(_ctx: &'a dyn Command, _value: &'a str) -> Result<Self, CommandError> {

0 commit comments

Comments
 (0)