diff --git a/extensions/Makefile.am b/extensions/Makefile.am index 40e5afb27..0edd0d719 100644 --- a/extensions/Makefile.am +++ b/extensions/Makefile.am @@ -13,6 +13,7 @@ extension_LTLIBRARIES = \ chm_nonotice.la \ chm_operpeace.la \ chm_quietunreg_compat.la \ + chm_spamfilter.la \ chm_sslonly.la \ chm_sslonly_compat.la \ createauthonly.la \ diff --git a/extensions/chm_spamfilter.c b/extensions/chm_spamfilter.c new file mode 100644 index 000000000..fd0eaa2ad --- /dev/null +++ b/extensions/chm_spamfilter.c @@ -0,0 +1,162 @@ +/************************************************************************ + * charybdis: an advanced ircd. extensions/chm_spamfilter.c + * Copyright (C) 2016 Jason Volk + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "stdinc.h" +#include "s_conf.h" +#include "modules.h" +#include "hook.h" +#include "send.h" +#include "chmode.h" +#include "newconf.h" +#include "substitution.h" +#include "spamfilter.h" + + +int chm_spamfilter; +int h_spamfilter_query; +int h_spamfilter_reject; + +char reject_reason[BUFSIZE - CHANNELLEN - 32]; + + +static +void hook_privmsg_channel(hook_data_privmsg_channel *const hook) +{ + // Check for mootness by other hooks first + if(hook->approved != 0 || EmptyString(hook->text)) + return; + + // Assess channel related + if(~hook->chptr->mode.mode & chm_spamfilter) + return; + + // Assess client related + if(IsExemptSpambot(hook->source_p)) + return; + + // Assess type related + if(hook->msgtype != MESSAGE_TYPE_NOTICE && hook->msgtype != MESSAGE_TYPE_PRIVMSG) + return; + + // Invoke the active spamfilters + call_hook(h_spamfilter_query, hook); + if(hook->approved == 0) + return; + + call_hook(h_spamfilter_reject, hook); + sendto_realops_snomask(SNO_REJ|SNO_BOTS, L_NETWIDE, + "spamfilter: REJECT %s[%s@%s] on %s to %s (%s)", + hook->source_p->name, + hook->source_p->username, + hook->source_p->orighost, + hook->source_p->servptr->name, + hook->chptr->chname, + hook->reason?: "filter gave no reason"); + + hook->reason = reject_reason; +} + + +static +void substitute_reject_reason(void) +{ + rb_dlink_list subs = {0}; + substitution_append_var(&subs, "network-name", ServerInfo.network_name?: "${network-name}"); + substitution_append_var(&subs, "admin-email", AdminInfo.email?: "${admin-email}"); + const char *const substituted = substitution_parse(reject_reason, &subs); + rb_strlcpy(reject_reason, substituted, sizeof(reject_reason)); + substitution_free(&subs); +} + + +static +void set_reject_reason(void *const str) +{ + rb_strlcpy(reject_reason, str, sizeof(reject_reason)); + substitute_reject_reason(); +} + + +struct ConfEntry conf_spamfilter[] = +{ + { "reject_reason", CF_QSTRING, set_reject_reason, 0, NULL }, + { "\0", 0, NULL, 0, NULL } +}; + + +static +int modinit(void) +{ + chm_spamfilter = cflag_add(MODE_SPAMFILTER, CHM_D, chm_simple); + if(!chm_spamfilter) + return -1; + + add_top_conf("spamfilter", NULL, NULL, conf_spamfilter); + return 0; +} + + +static +void modfini(void) +{ + remove_top_conf("spamfilter"); + cflag_orphan(MODE_SPAMFILTER); +} + + +mapi_hlist_av1 hlist[] = +{ + { "spamfilter_query", &h_spamfilter_query, }, + { "spamfilter_reject", &h_spamfilter_reject, }, + { NULL, NULL } +}; + +mapi_hfn_list_av1 hfnlist[] = +{ + { "privmsg_channel", (hookfn)hook_privmsg_channel }, + { NULL, NULL } +}; + +static const char chm_spamfilter_desc[] = + "Adds channel mode +Y which enables various spam mitigations"; + +DECLARE_MODULE_AV2 +( + chm_spamfilter, + modinit, + modfini, + NULL, + hlist, + hfnlist, + NULL, + NULL, + chm_spamfilter_desc +); diff --git a/extensions/spamfilter.h b/extensions/spamfilter.h new file mode 100644 index 000000000..cd7b8bb49 --- /dev/null +++ b/extensions/spamfilter.h @@ -0,0 +1,33 @@ +/************************************************************************ + * charybdis: an advanced ircd. extensions/spamfilter.h + * Copyright (C) 2016 Jason Volk + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#define MODE_SPAMFILTER 'Y' +#define STATCHAR_SPAMFILTER 'S'