diff --git a/extensions/filter.c b/extensions/filter.c index bebcbdfc..54548308 100644 --- a/extensions/filter.c +++ b/extensions/filter.c @@ -41,6 +41,8 @@ #include "operhash.h" #include "inline/stringops.h" #include "msgbuf.h" +#include "hostmask.h" +#include "s_conf.h" #include #include @@ -56,6 +58,8 @@ static const char filter_desc[] = "Filter messages using a precompiled Hyperscan static void filter_msg_user(void *data); static void filter_msg_channel(void *data); static void filter_client_quit(void *data); +static void filter_client_nick_set(void *data); +static void filter_client_nick_change(void *data); static void on_client_exit(void *data); static void mo_setfilter(struct MsgBuf *, struct Client *, struct Client *, int, const char **); @@ -93,6 +97,8 @@ mapi_hfn_list_av1 filter_hfnlist[] = { { "privmsg_user", filter_msg_user }, { "privmsg_channel", filter_msg_channel }, { "client_quit", filter_client_quit }, + { "local_nick_set_approve", filter_client_nick_set }, + { "local_nick_change_approve", filter_client_nick_change }, { "client_exit", on_client_exit }, { NULL, NULL } }; @@ -351,21 +357,21 @@ unsigned match_message(const char *prefix, snprintf(check_buffer, sizeof check_buffer, "%s:%s!%s@%s#%c %s%s%s :%s", prefix, #if FILTER_NICK - source->name, + source ? source->name : "*", #else "*", #endif #if FILTER_USER - source->username, + source ? source->username : "*", #else "*", #endif #if FILTER_HOST - source->host, + source ? source->host : "*", #else "*", #endif - source->user && source->user->suser[0] != '\0' ? '1' : '0', + source && source->user && source->user->suser[0] != '\0' ? '1' : '0', command, target ? " " : "", target ? target : "", @@ -483,6 +489,54 @@ filter_client_quit(void *data_) /* No point in doing anything with ACT_KILL */ } +void +filter_client_nick_change(void *data_) +{ + hook_data_nick_approval *data = data_; + struct Client *s = data->client; + if (IsOper(s)) { + return; + } + + unsigned r = match_message("0", s, "NICK", NULL, data->nick); + if (r & ACT_DROP) { + data->approved = 0; + } + if (r & ACT_ALARM) { + sendto_realops_snomask(SNO_GENERAL, L_ALL | L_NETWIDE, + "FILTER: %s!%s@%s [%s]", + s->name, s->username, s->host, s->sockhost); + } + if (r & ACT_KILL) { + exit_client(NULL, s, s, FILTER_EXIT_MSG); + } +} + +void +filter_client_nick_set(void *data_) +{ + hook_data_nick_approval *data = data_; + struct Client *s = data->client; + struct sockaddr *addr = (void *)&s->localClient->ip; + + if(find_conf_by_address( + NULL, NULL, NULL, addr, CONF_EXEMPTDLINE | 1, GET_SS_FAMILY(addr), NULL, NULL)) + return; + + unsigned r = match_message("0", NULL, "NICK", NULL, data->nick); + if (r & ACT_DROP) { + data->approved = 0; + } + if (r & ACT_ALARM) { + sendto_realops_snomask(SNO_GENERAL, L_ALL | L_NETWIDE, + "FILTER:REGISTER: %s [%s]", + s->host, s->sockhost); + } + if (r & ACT_KILL) { + exit_client(NULL, s, s, FILTER_EXIT_MSG); + } +} + void on_client_exit(void *data_) { diff --git a/include/hook.h b/include/hook.h index fc56b288..9e6cc6df 100644 --- a/include/hook.h +++ b/include/hook.h @@ -164,6 +164,14 @@ typedef struct int del; } hook_data_cap_change; +typedef struct +{ + struct Client *client; + const void *nick; + int approved; +} hook_data_nick_approval; + + enum message_type { MESSAGE_TYPE_NOTICE, MESSAGE_TYPE_PRIVMSG, diff --git a/modules/core/m_nick.c b/modules/core/m_nick.c index dcdd4837..80724257 100644 --- a/modules/core/m_nick.c +++ b/modules/core/m_nick.c @@ -79,7 +79,9 @@ static void perform_nick_collides(struct Client *, struct Client *, static void perform_nickchange_collides(struct Client *, struct Client *, struct Client *, int, const char **, time_t, const char *); +static int h_local_nick_set_approve; static int h_local_nick_change; +static int h_local_nick_change_approve; static int h_remote_nick_change; struct Message nick_msgtab = { @@ -103,7 +105,9 @@ mapi_clist_av1 nick_clist[] = { &nick_msgtab, &uid_msgtab, &euid_msgtab, &save_msgtab, NULL }; mapi_hlist_av1 nick_hlist[] = { + { "local_nick_set_approve", &h_local_nick_set_approve }, { "local_nick_change", &h_local_nick_change }, + { "local_nick_change_approve", &h_local_nick_change_approve }, { "remote_nick_change", &h_remote_nick_change }, { NULL, NULL } }; @@ -121,6 +125,7 @@ mr_nick(struct MsgBuf *msgbuf_p, struct Client *client_p, struct Client *source_ { struct Client *target_p; char nick[NICKLEN]; + hook_data_nick_approval hook_approve; if (strlen(client_p->id) == 3 || (source_p->preClient && !EmptyString(source_p->preClient->id))) { @@ -161,6 +166,18 @@ mr_nick(struct MsgBuf *msgbuf_p, struct Client *client_p, struct Client *source_ return; } + hook_approve.client = source_p; + hook_approve.nick = nick; + hook_approve.approved = 1; + call_hook(h_local_nick_set_approve, &hook_approve); + + if (!hook_approve.approved) { + /* send the same error they'd get if this nick was RESV */ + sendto_one(source_p, form_str(ERR_ERRONEUSNICKNAME), + me.name, EmptyString(source_p->name) ? "*" : source_p->name, nick); + return; + } + if((target_p = find_named_client(nick)) == NULL) set_initial_nick(client_p, source_p, nick); else if(source_p == target_p) @@ -622,6 +639,7 @@ change_local_nick(struct Client *client_p, struct Client *source_p, struct Channel *chptr; char note[NICKLEN + 10]; int samenick; + hook_data_nick_approval hook_approve; hook_cdata hook_info; if (dosend) @@ -666,6 +684,18 @@ change_local_nick(struct Client *client_p, struct Client *source_p, invalidate_bancache_user(source_p); } + hook_approve.client = source_p; + hook_approve.nick = nick; + hook_approve.approved = 1; + call_hook(h_local_nick_change_approve, &hook_approve); + + if (!hook_approve.approved) { + /* send the same error they'd get if this nick was RESV */ + sendto_one(source_p, form_str(ERR_ERRONEUSNICKNAME), + me.name, EmptyString(source_p->name) ? "*" : source_p->name, nick); + return; + } + hook_info.client = source_p; hook_info.arg1 = source_p->name; hook_info.arg2 = nick;