Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions doc/reference.conf
Original file line number Diff line number Diff line change
Expand Up @@ -1484,6 +1484,23 @@ general {
* requires extensions/drain to be loaded.
*/
drain_reason = "This server is not accepting connections.";

/* filter_sees_user_info: Whether the filter is given the nick!user@host of message senders.
* If set to NO, the filter is given a literal "*!*@*" instead.
* Requires extension/filter to be loaded.
*/
filter_sees_user_info = no;

/* filter_bypass_all: If set to NO, the only filter action that can bypass +u is DROP when paired with BYPASS.
* If set to YES, all filter actions can bypass +u when paired with BYPASS.
* Requires extension/filter to be loaded.
*/
filter_bypass_all = no;

/* filter_exit_message: The QUIT message shown when a user is disconnected due to the filter KILL action.
* Requires extension/filter to be loaded.
*/
filter_exit_message = "Connection closed";
};

modules {
Expand Down
2 changes: 2 additions & 0 deletions doc/server-version-info.txt
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
+----------------------------+
| 'e' | USE_EXCEPT |
|------+---------------------|
| 'F' | FILTER_CAN_SPY |
|------+---------------------|
| 'I' | USE_INVEX |
|------+---------------------|
| 'K' | USE_KNOCK |
Expand Down
89 changes: 50 additions & 39 deletions extensions/filter.c
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
#include "numeric.h"
#include "send.h"
#include "s_newconf.h"
#include "s_conf.h"
#include "s_serv.h"
#include "s_user.h"
#include "msg.h"
Expand All @@ -45,11 +46,7 @@
#include <hs_common.h>
#include <hs_runtime.h>

#define FILTER_NICK 0
#define FILTER_USER 0
#define FILTER_HOST 0

#define FILTER_EXIT_MSG "Connection closed"
#define FILTER_DEFAULT_EXIT_MSG "Connection closed"

static const char filter_desc[] = "Filter messages using a precompiled Hyperscan database";

Expand Down Expand Up @@ -80,9 +77,15 @@ enum filter_state {
FILTER_LOADED
};

struct match_context {
unsigned int actions;
bool require_bypass;
};

#define ACT_DROP (1 << 0)
#define ACT_KILL (1 << 1)
#define ACT_ALARM (1 << 2)
#define ACT_BYPASS (1 << 3)

static enum filter_state state = FILTER_EMPTY;
static char check_str[21] = "";
Expand Down Expand Up @@ -327,8 +330,17 @@ int match_callback(unsigned id,
unsigned flags,
void *context_)
{
unsigned *context = context_;
*context |= id;
struct match_context *context = context_;

if (context->require_bypass)
{
unsigned int bypass_mask = ConfigFileEntry.filter_bypass_all ? UINT_MAX : ACT_DROP;
if (id & ACT_BYPASS)
context->actions |= id & bypass_mask;
}
else
context->actions |= id;

return 0;
}

Expand All @@ -337,50 +349,42 @@ static char clean_buffer[BUFSIZE];

unsigned match_message(const char *prefix,
struct Client *source,
bool require_bypass,
const char *command,
const char *target,
const char *msg)
{
unsigned state = 0;
struct match_context ctx = { 0, require_bypass };

if (!filter_enable)
return 0;
if (!filter_db)
return 0;
if (!command)
return 0;

snprintf(check_buffer, sizeof check_buffer, "%s:%s!%s@%s#%c %s%s%s :%s",
prefix,
#if FILTER_NICK
source->name,
#else
"*",
#endif
#if FILTER_USER
source->username,
#else
"*",
#endif
#if FILTER_HOST
source->host,
#else
"*",
#endif
ConfigFileEntry.filter_sees_user_info ? source->name : "*",
ConfigFileEntry.filter_sees_user_info ? source->username : "*",
ConfigFileEntry.filter_sees_user_info ? source->host : "*",
source->user && source->user->suser[0] != '\0' ? '1' : '0',
command,
target ? " " : "",
target ? target : "",
msg);
hs_error_t r = hs_scan(filter_db, check_buffer, strlen(check_buffer), 0, filter_scratch, match_callback, &state);
hs_error_t r = hs_scan(filter_db, check_buffer, strlen(check_buffer), 0, filter_scratch, match_callback, &ctx);
if (r != HS_SUCCESS && r != HS_SCAN_TERMINATED)
return 0;
return state;
return ctx.actions;
}

void
filter_msg_user(void *data_)
{
hook_data_privmsg_user *data = data_;
struct Client *s = data->source_p;

/* we only need to filter once */
if (!MyClient(s)) {
return;
Expand All @@ -391,14 +395,14 @@ filter_msg_user(void *data_)
if (IsOper(s) || IsOper(data->target_p)) {
return;
}
if (data->target_p->umodes & filter_umode) {
return;
}

bool require_bypass = (data->target_p->umodes & filter_umode) == filter_umode;
char *text = strcpy(clean_buffer, data->text);
strip_colour(text);
strip_unprintable(text);
unsigned r = match_message("0", s, cmdname[data->msgtype], "0", data->text) |
match_message("1", s, cmdname[data->msgtype], "0", text);
unsigned r = match_message("0", s, require_bypass, cmdname[data->msgtype], "0", data->text) |
match_message("1", s, require_bypass, cmdname[data->msgtype], "0", text);

if (r & ACT_DROP) {
if (data->msgtype == MESSAGE_TYPE_PRIVMSG) {
sendto_one_numeric(s, ERR_CANNOTSENDTOCHAN,
Expand All @@ -413,8 +417,11 @@ filter_msg_user(void *data_)
s->name, s->username, s->host, s->sockhost);
}
if (r & ACT_KILL) {
const char *msg = ConfigFileEntry.filter_exit_message;
if (msg == NULL)
msg = FILTER_DEFAULT_EXIT_MSG;
data->approved = 1;
exit_client(NULL, s, s, FILTER_EXIT_MSG);
exit_client(NULL, s, s, msg);
}
}

Expand All @@ -423,6 +430,7 @@ filter_msg_channel(void *data_)
{
hook_data_privmsg_channel *data = data_;
struct Client *s = data->source_p;

/* we only need to filter once */
if (!MyClient(s)) {
return;
Expand All @@ -432,14 +440,14 @@ filter_msg_channel(void *data_)
if (IsOper(s)) {
return;
}
if (data->chptr->mode.mode & filter_chmode) {
return;
}

bool require_bypass = (data->chptr->mode.mode & filter_chmode) == filter_chmode;
char *text = strcpy(clean_buffer, data->text);
strip_colour(text);
strip_unprintable(text);
unsigned r = match_message("0", s, cmdname[data->msgtype], data->chptr->chname, data->text) |
match_message("1", s, cmdname[data->msgtype], data->chptr->chname, text);
unsigned r = match_message("0", s, require_bypass, cmdname[data->msgtype], data->chptr->chname, data->text) |
match_message("1", s, require_bypass, cmdname[data->msgtype], data->chptr->chname, text);

if (r & ACT_DROP) {
if (data->msgtype == MESSAGE_TYPE_PRIVMSG) {
sendto_one_numeric(s, ERR_CANNOTSENDTOCHAN,
Expand All @@ -454,8 +462,11 @@ filter_msg_channel(void *data_)
s->name, s->username, s->host, s->sockhost);
}
if (r & ACT_KILL) {
const char *msg = ConfigFileEntry.filter_exit_message;
if (msg == NULL)
msg = FILTER_DEFAULT_EXIT_MSG;
data->approved = 1;
exit_client(NULL, s, s, FILTER_EXIT_MSG);
exit_client(NULL, s, s, msg);
}
}

Expand All @@ -470,8 +481,8 @@ filter_client_quit(void *data_)
char *text = strcpy(clean_buffer, data->orig_reason);
strip_colour(text);
strip_unprintable(text);
unsigned r = match_message("0", s, "QUIT", NULL, data->orig_reason) |
match_message("1", s, "QUIT", NULL, text);
unsigned r = match_message("0", s, false, "QUIT", NULL, data->orig_reason) |
match_message("1", s, false, "QUIT", NULL, text);
if (r & ACT_DROP) {
data->reason = NULL;
}
Expand Down
3 changes: 3 additions & 0 deletions include/s_conf.h
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,8 @@ struct config_file_entry
int away_interval;
int tls_ciphers_oper_only;
int oper_secure_only;
int filter_sees_user_info;
int filter_bypass_all;

char **hidden_caps;

Expand All @@ -268,6 +270,7 @@ struct config_file_entry
char *server_full_client_message;
char *illegal_name_long_client_message;
char *illegal_name_short_client_message;
char *filter_exit_message;
};

struct config_channel_entry
Expand Down
3 changes: 3 additions & 0 deletions ircd/newconf.c
Original file line number Diff line number Diff line change
Expand Up @@ -2781,6 +2781,9 @@ static struct ConfEntry conf_general_table[] =
{ "illegal_name_short_client_message", CF_QSTRING, NULL, BUFSIZE, &ConfigFileEntry.illegal_name_short_client_message },
{ "tls_ciphers_oper_only", CF_YESNO, NULL, 0, &ConfigFileEntry.tls_ciphers_oper_only },
{ "oper_secure_only", CF_YESNO, NULL, 0, &ConfigFileEntry.oper_secure_only },
{ "filter_sees_user_info", CF_YESNO, NULL, 0, &ConfigFileEntry.filter_sees_user_info },
{ "filter_bypass_all", CF_YESNO, NULL, 0, &ConfigFileEntry.filter_bypass_all },
{ "filter_exit_message", CF_QSTRING, NULL, BUFSIZE, &ConfigFileEntry.filter_exit_message },
{ "\0", 0, NULL, 0, NULL }
};

Expand Down
2 changes: 2 additions & 0 deletions ircd/s_conf.c
Original file line number Diff line number Diff line change
Expand Up @@ -1561,6 +1561,8 @@ clear_out_old_conf(void)
ConfigFileEntry.illegal_name_long_client_message = NULL;
rb_free(ConfigFileEntry.illegal_name_short_client_message);
ConfigFileEntry.illegal_name_short_client_message = NULL;
rb_free(ConfigFileEntry.filter_exit_message);
ConfigFileEntry.filter_exit_message = NULL;

if (ConfigFileEntry.hidden_caps != NULL)
{
Expand Down
15 changes: 15 additions & 0 deletions modules/m_info.c
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,11 @@ static struct InfoStruct info_table[] = {
"Short message to users when their username contains illegal characters.",
INFO_STRING(&ConfigFileEntry.illegal_name_short_client_message),
},
{
"filter_exit_message",
"Message to quit users with if they hit a filter KILL action.",
INFO_STRING(&ConfigFileEntry.filter_exit_message),
},
{
"disable_auth",
"Controls whether auth checking is disabled or not",
Expand Down Expand Up @@ -675,6 +680,16 @@ static struct InfoStruct info_table[] = {
"Require TLS to become an oper",
INFO_INTBOOL_YN(&ConfigFileEntry.oper_secure_only),
},
{
"filter_sees_user_info",
"Let the spamfilter engine see the hostmasks of senders",
INFO_INTBOOL_YN(&ConfigFileEntry.filter_sees_user_info),
},
{
"filter_bypass_all",
"Let the spamfilter BYPASS action work on KILL/ALARM in addition to DROP",
INFO_INTBOOL_YN(&ConfigFileEntry.filter_bypass_all),
},

{ NULL, NULL, 0, { NULL } },
};
Expand Down
3 changes: 3 additions & 0 deletions modules/m_version.c
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,9 @@ confopts(void)
if(ConfigChannel.use_except)
*p++ = 'e';

if (ConfigFileEntry.filter_sees_user_info || ConfigFileEntry.filter_bypass_all)
*p++ = 'F';

if(ConfigChannel.use_invex)
*p++ = 'I';

Expand Down