solanum-ircd / solanum

An IRCd for unified networks
https://solanum.chat/
GNU General Public License v2.0
225 stars 54 forks source link

Ban redirects are not applied in other channels on `$j` extbans #316

Open JustAnotherArchivist opened 2 years ago

JustAnotherArchivist commented 2 years ago

Setup:

When nick now joins #channel1, they simply get ERR_BANNEDFROMCHAN. The intuitive expectation is that they would get redirected to #channel2.

The real-world use case for this behaviour is effective and efficient cross-channel bans. With dozens of channels, keeping their ban lists synchronised is just not going to happen. So we instead want to have one single source of truth for 'global' bans and then apply them everywhere via $j extbans in the rest of the channels. In some cases, we'd want this to be a straight ban, which works correctly. But in others, we want to redirect the users to a certain separate channel, e.g. when they're spamming the channels with quit/rejoin messages due to a network issues. The above setup is what we believed to work correctly, only to realise that it actually only does the right thing when the user joins #channel0.

This was discovered on hackint, which still runs Charybdis, but if I'm reading the code correctly, Solanum should behave exactly the same way. The canjoin extban calls is_banned, which handles the bans and also returns the forward channel if the last argument is non-NULL, but the canjoin extban extension calls it with NULL and thus never knows about the forwarding:

https://github.com/solanum-ircd/solanum/blob/22ebfd257e7f6959dda7253f8f39d27e8c118924/extensions/extb_canjoin.c#L66

The extban extension is called from is_banned_list itself, so it shouldn't be too difficult to add a forward target pointer there and pass the information back to can_join and check_forward that way.

As a workaround, we're now switching to a setup where we use a normal ban in #channel0 and a redirecting extban $j:#channel0$#channel2 in #channel1. This unfortunately means it's not possible to differentiate whether or where people get redirected; everyone banned from #channel0 ends up in #channel2 (unless they're banned there as well).

dwfreed commented 2 years ago

As a workaround, you can set the $j ban to forward to the channel it is sourcing its bans from, and then forwards on those bans will be applied; eg, $j:#channel0$#channel0. Solanum will follow forwards up to a limit of 16 times, if I remember correctly.

JustAnotherArchivist commented 2 years ago

Yep, that's the workaround I mentioned at the end, although it requires one ban list channel per redirect target. We had multiple for different circumstances so far, e.g. quit/join spamming to one channel, questionable connections to one, and undesired users to another (or just banned entirely). That's too annoying, so we're collapsing it into one, which is fine but still a bit unfortunate.

dwfreed commented 2 years ago

The workaround you mentioned is different; you're setting the forward in the $j to the intended destination; my workaround sets the forward in the $j to the same channel as the $j is looking at; then the $j target channel can have individual forwards on its bans, and whichever one you match is where you end up. eg:

#channel0: +bb nick1!*@*$#channel2 nick2!*@*$#channel3 #channel1: +b $j:#channel0$#channel0

nick1 will end up in #channel2 and nick2 will end up in #channel3 without #channel1 needing to be aware of this in any way.

JustAnotherArchivist commented 2 years ago

Oh, I misread that. Brilliant, thanks for the idea!