1
0
mirror of https://github.com/unrealircd/unrealircd.git synced 2026-07-01 02:56:37 +02:00

Add unreal_server_compat module which rewrites named extbans in

server to server traffic to be letter extbans.
Yeah this is a tad ugly, but the alternative was worse, see
header of the file for the full story.

Module is loaded by default (obviously).

Still to do: only do this for non-U6 servers (add some PROTOCTL)

And probably alter clean_ban_mask because I don't like the
magic on NULL client at the moment.
This commit is contained in:
Bram Matthys
2021-08-14 16:42:33 +02:00
parent f6704cfd1c
commit 5f31f7a5cc
6 changed files with 230 additions and 2 deletions
+1
View File
@@ -121,6 +121,7 @@ loadmodule "umode2";
loadmodule "sinfo";
loadmodule "require-module";
loadmodule "slog";
loadmodule "unreal_server_compat";
// Services commands
// You could disable these if you don't use Services
+1
View File
@@ -375,6 +375,7 @@ typedef struct {
int what; /**< MODE_ADD or MODE_DEL (for is_ok) */
int what2; /**< EXBTYPE_BAN or EXBTYPE_EXCEPT (for is_ok) */
int is_ok_checktype; /**< One of EXBCHK_* (for is_ok) */
int write_letter_bans; /**< If set to 1 then we will always use letter extbans */
} BanContext;
typedef struct Extban Extban;
+1 -1
View File
@@ -337,7 +337,7 @@ char *prefix_with_extban(char *remainder, BanContext *b, Extban *extban, char *b
if (remainder == NULL)
return NULL;
if (iConf.named_extended_bans)
if (iConf.named_extended_bans && !b->write_letter_bans)
snprintf(buf, buflen, "~%s:%s", extban->name, remainder);
else
snprintf(buf, buflen, "~%c:%s", extban->letter, remainder);
+4
View File
@@ -863,6 +863,9 @@ char *clean_ban_mask(char *mask, int what, Client *client)
b->client = client;
b->what = what;
b->banstr = nextbanstr;
/* Hmm.. i may regret this :D */
if (client == NULL)
b->write_letter_bans = 1;
ret = extban->conv_param(b, extban);
ret = prefix_with_extban(ret, b, extban, retbuf, sizeof(retbuf));
safe_free(b);
@@ -1255,6 +1258,7 @@ int parse_chanmode(ParseMode *pm, char *modebuf_in, char *parabuf_in)
strlcpy(pm->buf, start, sizeof(pm->buf));
pm->parabuf = pm->parabuf + strlen(pm->parabuf); /* point to \0 at end */
}
stripcrlf(pm->buf); /* needed for unreal_server_compat.c */
pm->param = pm->buf;
} else {
pm->modebuf++;
+6 -1
View File
@@ -75,7 +75,8 @@ R_MODULES= \
typing-indicator.so \
ident_lookup.so history.so chathistory.so \
targetfloodprot.so clienttagdeny.so watch-backend.so \
monitor.so slog.so
monitor.so slog.so \
unreal_server_compat.so
MODULES=cloak.so $(R_MODULES)
MODULEFLAGS=@MODULEFLAGS@
@@ -670,6 +671,10 @@ slog.so: slog.c $(INCLUDES)
$(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \
-o slog.so slog.c
unreal_server_compat.so: unreal_server_compat.c $(INCLUDES)
$(CC) $(CFLAGS) $(MODULEFLAGS) -DDYNAMIC_LINKING \
-o unreal_server_compat.so unreal_server_compat.c
#############################################################################
# capabilities
#############################################################################
+217
View File
@@ -0,0 +1,217 @@
/*
* unreal_server_compat - Compatibility with pre-U6 servers
* (C) Copyright 2016-2021 Bram Matthys (Syzop)
* License: GPLv2
*
* Currently the only purpose of this module is to rewrite
* MODE lines to older servers so any bans/exempts/invex
* will show up with their single letter syntax,
* eg "MODE #test +b ~account:someacc" will be rewritten
* as "MODE #test +b ~a:someacc".
* It uses rather complex mode reparsing techniques to
* achieve this, but this was deemed to be the only way
* that we could achieve this in a doable way.
* The alternative was complicating the mode.c code with
* creating multiple strings for multiple clients, and
* doing the same in any other MODE change routine.
* That would have caused rather intrussive compatibility
* code, so I don't want that.
* With this we can just rip out the module at some point
* that we no longer want to support pre-U6 protocol.
* -- Syzop
*/
#include "unrealircd.h"
ModuleHeader MOD_HEADER
= {
"unreal_server_compat",
"1.0.0",
"Provides compatibility with non-U6 servers",
"Bram Matthys (Syzop)",
"unrealircd-6"
};
/* Forward declarations */
int usc_packet(Client *from, Client *to, Client *intended_to, char **msg, int *length);
int usc_reparsemode(char **msg, char *p, int *length);
void skip_spaces(char **p);
void read_until_space(char **p);
int eat_parameter(char **p);
MOD_INIT()
{
MARK_AS_OFFICIAL_MODULE(modinfo);
HookAdd(modinfo->handle, HOOKTYPE_PACKET, 0, usc_packet);
return MOD_SUCCESS;
}
MOD_LOAD()
{
return MOD_SUCCESS;
}
MOD_UNLOAD()
{
return MOD_SUCCESS;
}
int usc_packet(Client *from, Client *to, Client *intended_to, char **msg, int *length)
{
char *p, *buf = *msg;
/* We are only interested in outgoing data. Also ircops get to see everything as-is */
if (IsMe(to) || !IsServer(to) || !buf || !length || !*length)
return 0;
buf[*length] = '\0'; /* safety */
p = *msg;
skip_spaces(&p);
/* Skip over message tags */
if (*p == '@')
{
read_until_space(&p);
if (*p == '\0')
return 0; /* unexpected ending */
p++;
}
skip_spaces(&p);
if (*p == '\0')
return 0;
/* Skip origin */
if (*p == ':')
{
read_until_space(&p);
if (*p == '\0')
return 0; /* unexpected ending */
}
skip_spaces(&p);
if (*p == '\0')
return 0;
if (!strncmp(p, "MODE ", 5))
{
read_until_space(&p);
skip_spaces(&p);
if (*p == '\0')
return 0; /* unexpected */
/* p now points to #channel */
/* Now it gets interesting... we have to re-parse and re-write the entire MODE line. */
return usc_reparsemode(msg, p, length);
}
return 0;
}
int usc_reparsemode(char **msg, char *p, int *length)
{
static char obuf[8192];
char modebuf[512], *mode_buf_p, *para_buf_p;
char *channel_name;
int i;
int n;
ParseMode pm;
int modes_processed = 0;
channel_name = p;
if (!eat_parameter(&p))
return 0;
mode_buf_p = p;
if (!eat_parameter(&p))
return 0;
*modebuf = '\0';
strlncat(modebuf, mode_buf_p, sizeof(modebuf), p - mode_buf_p); // FIXME: verify that length calculation (last arg) is correct and doesnt need +1 or -1 etc.
/* If we get here then it is (for example) a
* MODE #channel +b nick!user@host
* So, has at least one parameter (nick!user@host in the example).
* p now points exactly to the 'n' from nick!user@host.
*
* Now, what we will do:
* everything BEFORE p is the 'header' that we will
* send exactly as-is.
* The only thing we may (potentially) change is
* everything AFTER p!
*/
/* Fill 'obuf' with that 'header' */
*obuf = '\0'; // we should really get strlncpy ;D
strlncat(obuf, *msg, sizeof(obuf), p - *msg); // FIXME: verify that p-msg is correct and should not be -1 or +1 or anything :D
para_buf_p = p;
/* Now parse the modes */
for (n = parse_chanmode(&pm, modebuf, para_buf_p); n; n = parse_chanmode(&pm, NULL, NULL))
{
/* We only rewrite the parameters, so don't care about paramless modes.. */
if (!pm.param)
continue;
if ((pm.modechar == 'b') || (pm.modechar == 'e') || (pm.modechar == 'I'))
{
char *result = clean_ban_mask(pm.param, pm.what, NULL);
strlcat(obuf, result?result:"<invalid>", sizeof(obuf));
strlcat(obuf, " ", sizeof(obuf));
} else
{
/* as-is */
strlcat(obuf, pm.param, sizeof(obuf));
strlcat(obuf, " ", sizeof(obuf));
}
modes_processed++;
}
/* Send line as-is */
if (modes_processed == 0)
return 0;
/* Strip final whitespace */
if (obuf[strlen(obuf)-1] == ' ')
obuf[strlen(obuf)-1] = '\0';
if (pm.parabuf && *pm.parabuf)
{
strlcat(obuf, " ", sizeof(obuf));
strlcat(obuf, pm.parabuf, sizeof(obuf));
}
/* Add CRLF */
if (obuf[strlen(obuf)-1] != '\n')
strlcat(obuf, "\r\n", sizeof(obuf));
/* Line modified, use it! */
*msg = obuf;
*length = strlen(obuf);
return 0;
}
/** Skip space(s), if any. */
void skip_spaces(char **p)
{
for (; **p == ' '; *p = *p + 1);
}
/** Keep reading until we hit space. */
void read_until_space(char **p)
{
for (; **p && (**p != ' '); *p = *p + 1);
}
int eat_parameter(char **p)
{
read_until_space(p);
if (**p == '\0')
return 0; /* was just a "MODE #channel" query - wait.. that's weird we are a server sending this :D */
skip_spaces(p);
if (**p == '\0')
return 0; // impossible
return 1;
}