diff --git a/include/h.h b/include/h.h index 9d3af1894..fe8df6209 100644 --- a/include/h.h +++ b/include/h.h @@ -927,6 +927,8 @@ extern void (*crule_free)(CRuleNode **); extern const char *(*crule_errstring)(int errcode); extern void (*ban_act_set_reputation)(Client *client, BanAction *action); extern const char *(*get_central_api_key)(void); +extern int (*central_spamreport)(Client *target); +extern int (*central_spamreport_enabled)(void); /* /Efuncs */ /* TLS functions */ @@ -982,6 +984,8 @@ extern void cancel_ident_lookup_default_handler(Client *client); extern int spamreport_default_handler(Client *client, const char *ip, NameValuePrioList *details, const char *spamreport_block); extern void ban_act_set_reputation_default_handler(Client *client, BanAction *action); extern const char *get_central_api_key_default_handler(void); +extern int central_spamreport_default_handler(Client *target); +extern int central_spamreport_enabled_default_handler(void); /* End of default handlers for efunctions */ extern MODVAR MOTDFile opermotd, svsmotd, motd, botmotd, smotd, rules; diff --git a/include/modules.h b/include/modules.h index 0dce56995..d95ea1d6b 100644 --- a/include/modules.h +++ b/include/modules.h @@ -2685,6 +2685,8 @@ enum EfunctionType { EFUNC_CRULE_ERRSTRING, EFUNC_BAN_ACT_SET_REPUTATION, EFUNC_GET_CENTRAL_API_KEY, + EFUNC_CENTRAL_SPAMREPORT, + EFUNC_CENTRAL_SPAMREPORT_ENABLED, }; /* Module flags */ diff --git a/src/api-efunctions.c b/src/api-efunctions.c index e7a27cab9..6a5be49ce 100644 --- a/src/api-efunctions.c +++ b/src/api-efunctions.c @@ -172,6 +172,8 @@ void (*crule_free)(CRuleNode **); const char *(*crule_errstring)(int errcode); void (*ban_act_set_reputation)(Client *client, BanAction *action); const char *(*get_central_api_key)(void); +int (*central_spamreport)(Client *target); +int (*central_spamreport_enabled)(void); Efunction *EfunctionAddMain(Module *module, EfunctionType eftype, int (*func)(), void (*vfunc)(), void *(*pvfunc)(), char *(*stringfunc)(), const char *(*conststringfunc)()) { @@ -489,4 +491,6 @@ void efunctions_init(void) efunc_init_function(EFUNC_CRULE_ERRSTRING, crule_errstring, NULL, EFUNC_FLAG_EARLY); efunc_init_function(EFUNC_BAN_ACT_SET_REPUTATION, ban_act_set_reputation, ban_act_set_reputation_default_handler, 0); efunc_init_function(EFUNC_GET_CENTRAL_API_KEY, get_central_api_key, get_central_api_key_default_handler, 0); + efunc_init_function(EFUNC_CENTRAL_SPAMREPORT, central_spamreport, central_spamreport_default_handler, 0); + efunc_init_function(EFUNC_CENTRAL_SPAMREPORT_ENABLED, central_spamreport_enabled, central_spamreport_enabled_default_handler, 0); } diff --git a/src/misc.c b/src/misc.c index b22dd0ce2..fde66d18b 100644 --- a/src/misc.c +++ b/src/misc.c @@ -1897,6 +1897,16 @@ const char *get_central_api_key_default_handler(void) return NULL; } +int central_spamreport_default_handler(Client *target) +{ + return 0; +} + +int central_spamreport_enabled_default_handler(void) +{ + return 0; +} + /** my_timegm: mktime()-like function which will use GMT/UTC. * Strangely enough there is no standard function for this. * On some *NIX OS's timegm() may be available, sometimes only diff --git a/src/modules/central-blocklist.c b/src/modules/central-blocklist.c index 38d730fdf..2125bc865 100644 --- a/src/modules/central-blocklist.c +++ b/src/modules/central-blocklist.c @@ -58,7 +58,6 @@ struct cfgstruct { char *api_key; int max_downloads; int blocklist_enabled; - int spamreport_enabled; SecurityGroup *except; ScoreAction *actions; }; @@ -73,6 +72,7 @@ static struct reqstruct req; CBLTransfer *cbltransfers = NULL; /* Forward declarations */ +int _central_spamreport(Client *client); int cbl_config_test(ConfigFile *cf, ConfigEntry *ce, int type, int *errs); int cbl_config_posttest(int *errs); int cbl_config_run(ConfigFile *cf, ConfigEntry *ce, int type); @@ -106,7 +106,6 @@ void set_tag(Client *client, const char *tag, int value); CMD_OVERRIDE_FUNC(cbl_override); CMD_OVERRIDE_FUNC(cbl_override_spamreport_gather); -CMD_OVERRIDE_FUNC(cbl_override_spamreport_cmd); static void set_default_score_action(ScoreAction *action) { @@ -183,6 +182,7 @@ MOD_TEST() MARK_AS_OFFICIAL_MODULE(modinfo); HookAdd(modinfo->handle, HOOKTYPE_CONFIGTEST, 0, cbl_config_test); HookAdd(modinfo->handle, HOOKTYPE_CONFIGPOSTTEST, 0, cbl_config_posttest); + EfunctionAdd(modinfo->handle, EFUNC_CENTRAL_SPAMREPORT, _central_spamreport); return MOD_SUCCESS; } @@ -245,7 +245,7 @@ MOD_LOAD() do_command_overrides(modinfo); /* Enable gathering of "last 10 lines" for SPAMREPORT, only if SPAMREPORT is enabled: */ - if (cfg.spamreport_enabled) + if (central_spamreport_enabled()) { CommandOverrideAdd(modinfo->handle, "NICK", -2, cbl_override_spamreport_gather); CommandOverrideAdd(modinfo->handle, "PRIVMSG", -2, cbl_override_spamreport_gather); @@ -255,8 +255,6 @@ MOD_LOAD() CommandOverrideAdd(modinfo->handle, "KNOCK", -2, cbl_override_spamreport_gather); } - CommandOverrideAdd(modinfo->handle, "SPAMREPORT", -2, cbl_override_spamreport_cmd); - EventAdd(modinfo->handle, "centralblocklist_timeout_evt", centralblocklist_timeout_evt, NULL, 1000, 0); EventAdd(modinfo->handle, "centralblocklist_bundle_requests", centralblocklist_bundle_requests, NULL, 1000, 0); return MOD_SUCCESS; @@ -340,6 +338,12 @@ int cbl_config_test(ConfigFile *cf, ConfigEntry *ce, int type, int *errs) } else if (!strcmp(cep->name, "spamreport") || !strcmp(cep->name, "spamreport-enabled")) { + config_error("%s:%i: set::central-blocklist::%s: This setting is deprecated. " + "Please remove this setting, and, if you wish to use spamreport, add a " + "spamreport unrealircd { type central-spamreport; } block in your main config. " + "See https://www.unrealircd.org/docs/Central_spamreport", + cep->file->filename, cep->line_number, cep->name); + errors++; } else if (!strcmp(cep->name, "blocklist") || !strcmp(cep->name, "blocklist-enabled")) { @@ -436,10 +440,6 @@ int cbl_config_run(ConfigFile *cf, ConfigEntry *ce, int type) { safe_strdup(cfg.url, cep->value); } else - if (!strcmp(cep->name, "spamreport") || !strcmp(cep->name, "spamreport-enabled")) - { - cfg.spamreport_enabled = config_checkval(cep->value, CFG_YESNO); - } else if (!strcmp(cep->name, "blocklist-enabled")) { cfg.blocklist_enabled = config_checkval(cep->value, CFG_YESNO); @@ -770,7 +770,7 @@ int cbl_is_handshake_finished(Client *client) void cbl_allow(Client *client) { if (CBL(client)) - CBL(client)->allowed_in = 2; + CBL(client)->allowed_in = 1; if (is_handshake_finished(client)) register_user(client); @@ -952,16 +952,13 @@ void cbl_download_complete(OutgoingWebRequest *request, OutgoingWebResponse *res void cbl_mdata_free(ModData *m) { CBLUser *cbl = (CBLUser *)m->ptr; + int i; if (cbl) { json_decref(cbl->handshake); - if (cbl->allowed_in == 2) - { - int i; - for (i = 0; i < SPAMREPORT_NUM_REMEMBERED_CMDS; i++) - safe_free(cbl->last_cmds[i]); - } + for (i = 0; i < SPAMREPORT_NUM_REMEMBERED_CMDS; i++) + safe_free(cbl->last_cmds[i]); safe_free(cbl); m->ptr = NULL; } @@ -1056,7 +1053,7 @@ EVENT(centralblocklist_bundle_requests) /** Remember last # commands for SPAMREPORT */ CMD_OVERRIDE_FUNC(cbl_override_spamreport_gather) { - if (MyUser(client) && CBL(client) && (CBL(client)->allowed_in == 2)) + if (MyUser(client) && CBL(client)) { char record_cmd = 1; @@ -1078,7 +1075,7 @@ CMD_OVERRIDE_FUNC(cbl_override_spamreport_gather) CALL_NEXT_COMMAND_OVERRIDE(); } -void cbl_spamreport(Client *from, Client *client) +int _central_spamreport(Client *client) { json_t *j, *requests, *data, *cmds, *item; OutgoingWebRequest *w; @@ -1090,7 +1087,7 @@ void cbl_spamreport(Client *from, Client *client) int cnt = 0; if (!MyUser(client) || !CBL(client)) - return; /* Only possible if hot-loading */ + return 0; /* Only possible if hot-loading */ num = downloads_in_progress(); if (num > cfg.max_downloads) @@ -1098,7 +1095,7 @@ void cbl_spamreport(Client *from, Client *client) unreal_log(ULOG_WARNING, "central-blocklist", "CENTRAL_BLOCKLIST_TOO_MANY_CONCURRENT_REQUESTS", NULL, "Already $num_requests HTTP(S) requests in progress.", log_data_integer("num_requests", num)); - return; + return 0; } j = json_object(); @@ -1139,7 +1136,7 @@ void cbl_spamreport(Client *from, Client *client) unreal_log(ULOG_WARNING, "central-blocklist", "CENTRAL_BLOCKLIST_BUG_SERIALIZE", client, "Unable to serialize JSON request. Weird."); json_decref(j); - return; + return 0; } json_decref(j); @@ -1154,62 +1151,5 @@ void cbl_spamreport(Client *from, Client *client) w->max_redirects = 1; w->callback = download_complete_dontcare; url_start_async(w); -} - -CMD_OVERRIDE_FUNC(cbl_override_spamreport_cmd) -{ - if (ValidatePermissionsForPath("server-ban:spamreport",client,NULL,NULL,NULL) && - (parc > 1) && - !((parc > 2) && strcasecmp(parv[2], "unrealircd")) - ) - { - Client *target = find_user(parv[1], NULL); - if (target) - { - if (!MyUser(target)) - { - /* Forward it to other server */ - if (parc > 2) - { - sendto_one(target, NULL, ":%s SPAMREPORT %s %s", - client->id, parv[1], parv[2]); - } else { - sendto_one(target, NULL, ":%s SPAMREPORT %s", - client->id, parv[1]); - } - return; - } else { - /* My client. */ - int n; - - if (!cfg.spamreport_enabled) - { - if ((parc > 2) && !strcasecmp(parv[2], "unrealircd")) - { - sendnotice(client, "Spamreporting to UnrealIRCd is not enabled on this server. " - "To enable, add: set { central-blocklist { spamreport yes; } }"); - return; - } - CALL_NEXT_COMMAND_OVERRIDE(); - } - - /* Report to UnrealIRCd first... */ - sendnotice(client, "Sending spam report to UnrealIRCd..."); - cbl_spamreport(client, target); - if ((parc > 2) && !strcasecmp(parv[2], "unrealircd")) - { - sendnotice(client, "Sending spam report to %d target(s)", 1); - return; /* We are done */ - } - - /* There may be more spamreport blocks: */ - n = spamreport(target, target->ip, NULL, NULL); - if (n < 0) - n = 0; - sendnotice(client, "Sending spam report to %d target(s)", n + 1); /* +1 for unrealircd :D */ - return; - } - } - } - CALL_NEXT_COMMAND_OVERRIDE(); + return 1; } diff --git a/src/modules/spamreport.c b/src/modules/spamreport.c index d58d2faaa..016a17b4b 100644 --- a/src/modules/spamreport.c +++ b/src/modules/spamreport.c @@ -20,14 +20,15 @@ ModuleHeader MOD_HEADER typedef enum SpamreportType { SPAMREPORT_TYPE_SIMPLE = 1, SPAMREPORT_TYPE_DRONEBL = 2, -} SpamReportType; + SPAMREPORT_TYPE_CENTRAL_SPAMREPORT = 3, +} SpamreportType; typedef struct Spamreport Spamreport; struct Spamreport { Spamreport *prev, *next; char *name; /**< Name of the block, spamreport { } */ char *url; /**< URL to use */ - SpamReportType type; + SpamreportType type; HttpMethod http_method; NameValuePrioList *parameters; SecurityGroup *except; @@ -51,7 +52,9 @@ int tkl_config_run_spamreport(ConfigFile *, ConfigEntry *, int); Spamreport *find_spamreport_block(const char *name); void free_spamreport_blocks(void); int _spamreport(Client *client, const char *ip, NameValuePrioList *details, const char *spamreport_block); +int _central_spamreport_enabled(void); void spamreportcounters_free_all(ModData *m); +SpamreportType parse_spamreport_type(const char *s); /* Variables */ Spamreport *spamreports = NULL; @@ -61,6 +64,7 @@ MOD_TEST() { MARK_AS_OFFICIAL_MODULE(modinfo); EfunctionAdd(modinfo->handle, EFUNC_SPAMREPORT, _spamreport); + EfunctionAdd(modinfo->handle, EFUNC_CENTRAL_SPAMREPORT_ENABLED, _central_spamreport_enabled); HookAdd(modinfo->handle, HOOKTYPE_CONFIGTEST, 0, tkl_config_test_spamreport); return MOD_SUCCESS; } @@ -92,7 +96,7 @@ int tkl_config_test_spamreport(ConfigFile *cf, ConfigEntry *ce, int type, int *e ConfigEntry *cep, *cepp; int errors = 0; char has_url=0, has_type=0, has_type_dronebl=0, has_http_method=0; - char has_dronebl_type=0, has_dronebl_rpckey=0; + char has_dronebl_type, has_dronebl_rpckey=0; /* We are only interested in spamreport { } blocks */ if ((type != CONFIG_MAIN) || strcmp(ce->name, "spamreport")) @@ -103,12 +107,6 @@ int tkl_config_test_spamreport(ConfigFile *cf, ConfigEntry *ce, int type, int *e config_error("%s:%i: spamreport block has no name, should be like: spamfilter { }", ce->file->filename, ce->line_number); errors++; - } else - if (!strcasecmp(ce->value, "unrealircd")) - { - config_error("%s:%i: spamreport block cannot be named 'unrealircd', is a reserved name.", - ce->file->filename, ce->line_number); - errors++; } for (cep = ce->items; cep; cep = cep->next) @@ -160,15 +158,11 @@ int tkl_config_test_spamreport(ConfigFile *cf, ConfigEntry *ce, int type, int *e cep->line_number, "spamreport::type"); continue; } - has_type = 1; - if (!strcmp(cep->value, "simple")) - ; - else if (!strcmp(cep->value, "dronebl")) - has_type_dronebl = 1; - else + has_type = parse_spamreport_type(cep->value); + if (!has_type) { - config_error("%s:%i: spamreport::type: only 'simple' is supported at the moment", - cep->file->filename, cep->line_number); + config_error("%s:%i: spamreport::type: unknown type '%s', supported types are: simple, dronebl, central-spamreport.", + cep->file->filename, cep->line_number, cep->value); errors++; } } @@ -213,7 +207,11 @@ int tkl_config_test_spamreport(ConfigFile *cf, ConfigEntry *ce, int type, int *e errors++; } - if (has_type_dronebl) + if (has_type == SPAMREPORT_TYPE_CENTRAL_SPAMREPORT) + { + /* Nothing required */ + } else + if (has_type == SPAMREPORT_TYPE_DRONEBL) { if (!has_dronebl_rpckey || !has_dronebl_type) { @@ -269,12 +267,14 @@ int tkl_config_run_spamreport(ConfigFile *cf, ConfigEntry *ce, int type) } else if (!strcmp(cep->name, "type")) { - if (!strcmp(cep->value, "simple")) - s->type = SPAMREPORT_TYPE_SIMPLE; - else if (!strcmp(cep->value, "dronebl")) - s->type = SPAMREPORT_TYPE_DRONEBL; - else - abort(); + s->type = parse_spamreport_type(cep->value); + + if ((s->type == SPAMREPORT_TYPE_CENTRAL_SPAMREPORT) && + !is_module_loaded("central-blocklist")) + { + config_warn("%s:%d: blacklist block with type 'central-spamreport' but the 'central-blocklist' module is not loaded.", + ce->file->filename, ce->line_number); + } } else if (!strcmp(cep->name, "http-method")) { @@ -344,6 +344,17 @@ Spamreport *find_spamreport_block(const char *name) return NULL; } +SpamreportType parse_spamreport_type(const char *s) +{ + if (!strcmp(s, "simple")) + return SPAMREPORT_TYPE_SIMPLE; + else if (!strcmp(s, "dronebl")) + return SPAMREPORT_TYPE_DRONEBL; + else if (!strcmp(s, "central-spamreport")) + return SPAMREPORT_TYPE_CENTRAL_SPAMREPORT; + return 0; +} + /* Returns 1 if ratelimited (don't do the request), otherwise 0 */ int spamfilter_block_rate_limited(Spamreport *spamreport) { @@ -390,6 +401,16 @@ int spamfilter_block_rate_limited(Spamreport *spamreport) return 0; /* All OK */ } +/** Return 1 if there is a spamreport { type central-spamreport; } block */ +int _central_spamreport_enabled(void) +{ + Spamreport *s; + for (s = spamreports; s; s = s->next) + if (s->type == SPAMREPORT_TYPE_CENTRAL_SPAMREPORT) + return 1; + return 0; +} + int _spamreport(Client *client, const char *ip, NameValuePrioList *details, const char *spamreport_block) { Spamreport *s; @@ -421,7 +442,7 @@ int _spamreport(Client *client, const char *ip, NameValuePrioList *details, cons s = find_spamreport_block(spamreport_block); if (!s) - return -1; /* NOTFOUND */ + return 0; /* NOTFOUND */ if (s->except && client && user_allowed_by_security_group(client, s->except)) return 0; @@ -465,7 +486,13 @@ int _spamreport(Client *client, const char *ip, NameValuePrioList *details, cons safe_free_nvplist(list); // frees all the duplicated lists add_nvplist(&headers, 0, "Content-Type", "text/xml"); } else + if (s->type == SPAMREPORT_TYPE_CENTRAL_SPAMREPORT) + { + return central_spamreport(client); + } else + { abort(); + } #ifdef DEBUGMODE unreal_log(ULOG_DEBUG, "spamreport", "SPAMREPORT_SEND_REQUEST", NULL,