From a445c2ed6321a5e7b350468e55b1a843ba944730 Mon Sep 17 00:00:00 2001 From: David Schultz Date: Mon, 15 May 2023 17:47:30 -0500 Subject: [PATCH 01/30] doc/connecting-servers: update example atheme protocol module Atheme has had a protocol module designed specifically for Solanum for some time now that includes proper support for new IRCd features. --- doc/connecting-servers.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/connecting-servers.rst b/doc/connecting-servers.rst index c97b6176b..3ca80009b 100644 --- a/doc/connecting-servers.rst +++ b/doc/connecting-servers.rst @@ -112,7 +112,7 @@ hostname. For example, with atheme:: - loadmodule "modules/protocol/charybdis"; + loadmodule "modules/protocol/solanum"; uplink "a.example.org" { host = "localhost"; From 1608b0e70eccd97485af83cf5c80b4e09e95ba93 Mon Sep 17 00:00:00 2001 From: David Schultz Date: Thu, 18 May 2023 14:44:23 -0500 Subject: [PATCH 02/30] m_shedding: user shedding module based on oftc-hybrid --- doc/reference.conf | 1 + extensions/Makefile.am | 1 + extensions/m_shedding.c | 176 ++++++++++++++++++++++++++++++++++++++++ help/opers/shedding | 9 ++ 4 files changed, 187 insertions(+) create mode 100644 extensions/m_shedding.c create mode 100644 help/opers/shedding diff --git a/doc/reference.conf b/doc/reference.conf index a68379a4f..9df3494c4 100644 --- a/doc/reference.conf +++ b/doc/reference.conf @@ -479,6 +479,7 @@ privset "local_op" { * usermode:helpops allows setting +h (from extensions/helpops) * auspex:usertimes: * allows viewing user idle/connect times even when +I is set (from extensions/umode_hide_idle_time) + * oper:shedding: allows the SHEDDING command (from extensions/m_shedding) */ privs = oper:general, oper:privs, oper:testline, oper:kill, oper:operwall, oper:message, usermode:servnotice, auspex:oper, auspex:hostname, auspex:umodes, auspex:cmodes; diff --git a/extensions/Makefile.am b/extensions/Makefile.am index e404a9eea..4ab221fa2 100644 --- a/extensions/Makefile.am +++ b/extensions/Makefile.am @@ -55,6 +55,7 @@ extension_LTLIBRARIES = \ m_omode.la \ m_opme.la \ m_sendbans.la \ + m_shedding.la \ m_webirc.la \ m_remove.la \ hide_uncommon_channels.la \ diff --git a/extensions/m_shedding.c b/extensions/m_shedding.c new file mode 100644 index 000000000..a5cdc19d9 --- /dev/null +++ b/extensions/m_shedding.c @@ -0,0 +1,176 @@ +/* + * Solanum: a slightly advanced ircd + * shedding.c: Enables/disables user shedding. + * + * Based on oftc-hybrid's m_shedding.c + * + * Copyright (C) 2021 David Schultz + * Copyright (C) 2002 by the past and present ircd coders, and others. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + * USA + * + * + */ + +#include "stdinc.h" +#include "modules.h" +#include "hook.h" +#include "client.h" +#include "ircd.h" +#include "send.h" +#include "s_conf.h" +#include "s_serv.h" +#include "s_newconf.h" +#include "messages.h" +#include "numeric.h" + +#define SHED_RATE_MIN 5 + +static struct ev_entry *user_shedding_ev = NULL; + +static const char shed_desc[] = "Enables/disables user shedding."; + +static void mo_shedding(struct MsgBuf *msgbuf_p, struct Client *client_p, struct Client *source_p, int parc, const char *parv[]); +static void me_shedding(struct MsgBuf *msgbuf, struct Client *client_p, struct Client *source_p, int parc, const char *parv[]); +static void do_user_shedding(void *unused); + +static struct Message shedding_msgtab = { + "SHEDDING", 0, 0, 0, 0, + {mg_unreg, mg_not_oper, mg_ignore, mg_ignore, {me_shedding, 2}, {mo_shedding, 3}} +}; + +mapi_clist_av1 shedding_clist[] = { &shedding_msgtab, NULL }; + +static void +moddeinit(void) +{ + rb_event_delete(user_shedding_ev); +} + +DECLARE_MODULE_AV2(shed, NULL, moddeinit, shedding_clist, NULL, NULL, NULL, NULL, shed_desc); + +static void +set_shedding_state(struct Client *source_p, const char *chr, const char *reason) +{ + int rate; + if (irccmp(chr, "OFF") == 0) + { + // disable shedding + sendto_realops_snomask(SNO_GENERAL, L_ALL | L_NETWIDE, "%s disabled user shedding", get_oper_name(source_p)); + rb_event_delete(user_shedding_ev); + user_shedding_ev = NULL; + return; + } + + rate = atoi(chr); + + if(rate < SHED_RATE_MIN) + { + sendto_one_notice(source_p, "Shedding rate must be at least %d", SHED_RATE_MIN); + return; + } + + sendto_realops_snomask(SNO_GENERAL, L_ALL | L_NETWIDE, "%s enabled user shedding (interval: %d seconds, reason: %s)", + get_oper_name(source_p), rate, reason); + + rb_event_delete(user_shedding_ev); + user_shedding_ev = NULL; + user_shedding_ev = rb_event_add("user shedding event", do_user_shedding, NULL, rate); +} + +/* + * mo_shedding + * + * inputs - pointer to server + * - pointer to client + * - parameter count + * - parameter list + * output - + * side effects - user shedding is enabled or disabled + * + * SHEDDING OFF - disable shedding + * SHEDDING : + * (parv[#] 1 2 3) + * + */ +static void +mo_shedding(struct MsgBuf *msgbuf_p, struct Client *client_p, struct Client *source_p, int parc, const char *parv[]) +{ + if (!HasPrivilege(source_p, "oper:shedding")) + { + sendto_one(source_p, form_str(ERR_NOPRIVS), me.name, source_p->name, "SHEDDING"); + return; + } + + /* I can think of a thousand ways this could go wrong... */ + if (strchr(parv[1], '*') != NULL) + { + sendto_one_notice(source_p, "Wildcards are not permitted for shedding targets"); + return; + } + + if (parc != 4 && !(parc == 3 && irccmp(parv[2], "OFF") == 0)) + { + sendto_one(source_p, form_str(ERR_NEEDMOREPARAMS), + me.name, source_p->name, "SHEDDING"); + return; + } + + if (irccmp(parv[1], me.name) != 0) { + /* it's not for us, pass it around */ + if (irccmp(parv[2], "OFF") == 0) + sendto_match_servs(source_p, parv[1], + CAP_ENCAP, NOCAPS, + "ENCAP %s SHEDDING OFF", parv[1]); + else + sendto_match_servs(source_p, parv[1], + CAP_ENCAP, NOCAPS, + "ENCAP %s SHEDDING %s :%s", + parv[1], parv[2], parv[3]); + return; + } + + set_shedding_state(source_p, parv[2], parv[3]); +} + +static void +me_shedding(struct MsgBuf *msgbuf, struct Client *client_p, struct Client *source_p, int parc, const char *parv[]) +{ + if(!IsPerson(source_p)) + return; + + set_shedding_state(source_p, parv[1], parv[2]); +} + + +static void +do_user_shedding(void *unused) +{ + rb_dlink_node *ptr; + struct Client *client_p; + + RB_DLINK_FOREACH_PREV(ptr, lclient_list.tail) + { + client_p = ptr->data; + + if (!IsClient(client_p)) /* It could be servers */ + continue; + if (IsExemptKline(client_p)) + continue; + exit_client(client_p, client_p, &me, "Server closed connection"); + break; + } +} diff --git a/help/opers/shedding b/help/opers/shedding new file mode 100644 index 000000000..e7dfa5c1b --- /dev/null +++ b/help/opers/shedding @@ -0,0 +1,9 @@ +SHEDDING : +SHEDDING OFF + +Turns user shedding on or off on the specified server. When a server is +shedding, a client will be disconnected from the server at a time period +defined by rate, until shedding is turned off. Clients with kline_exempt +are never affected by this feature. + +- Requires Oper Priv: oper:shedding From f5e8ad65c1c3548eca499f05933e67bc51cf26cd Mon Sep 17 00:00:00 2001 From: David Schultz Date: Thu, 18 May 2023 22:23:19 -0500 Subject: [PATCH 03/30] reference.conf: document `hide_opers` --- doc/reference.conf | 3 +++ 1 file changed, 3 insertions(+) diff --git a/doc/reference.conf b/doc/reference.conf index 9df3494c4..e4a16dbc4 100644 --- a/doc/reference.conf +++ b/doc/reference.conf @@ -1455,6 +1455,9 @@ general { /* hide_opers_in_whois: if set to YES, then oper status will be hidden in /WHOIS output. */ hide_opers_in_whois = no; + + /* hide_opers: Hide all opers from unprivileged users */ + hide_opers = no; /* tls_ciphers_oper_only: show the TLS cipher string in /WHOIS only to opers and self */ tls_ciphers_oper_only = no; From 2b0410bc2c6d8adbee56cc94b998e0812ee776cf Mon Sep 17 00:00:00 2001 From: Tom Wesley Date: Sat, 3 Jun 2023 18:48:08 +0100 Subject: [PATCH 04/30] Replace libera reps with amdj and spb --- CREDITS | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CREDITS b/CREDITS index d8b548419..8ce81f155 100644 --- a/CREDITS +++ b/CREDITS @@ -4,8 +4,8 @@ Development is led by a group of representatives from Libera Chat and OFTC: dwfreed, Doug Freed -edk, Ed Kellett -glguy, Eric Mertens +spb, Stephen Bennet +amdj, Aaron Jones ilbelkyr, Nicole Kleinhoff mcintosh, Richie McIntosh Myon, Christoph Berg From fe83a335267c6226c2f4429035638f60e9e80b08 Mon Sep 17 00:00:00 2001 From: Tom Wesley Date: Mon, 5 Jun 2023 19:08:57 +0100 Subject: [PATCH 05/30] Sort reps in CREDITS case insensitive alphabetically --- CREDITS | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CREDITS b/CREDITS index 8ce81f155..5f616b7d0 100644 --- a/CREDITS +++ b/CREDITS @@ -3,12 +3,12 @@ Solanum is based on Charybdis, which was based on ircd-ratbox. Development is led by a group of representatives from Libera Chat and OFTC: -dwfreed, Doug Freed -spb, Stephen Bennet amdj, Aaron Jones +dwfreed, Doug Freed ilbelkyr, Nicole Kleinhoff mcintosh, Richie McIntosh Myon, Christoph Berg +spb, Stephen Bennet tomaw, Tom Wesley The Charybdis team was: From d1c028f2122f9da00203f415d76a990d7ef8843e Mon Sep 17 00:00:00 2001 From: David Schultz Date: Tue, 13 Jun 2023 09:13:42 -0500 Subject: [PATCH 06/30] Warn opers about unresponsive servers --- doc/reference.conf | 7 +++++++ include/client.h | 1 + include/s_conf.h | 1 + ircd/client.c | 21 +++++++++++++++++++++ ircd/newconf.c | 1 + ircd/packet.c | 17 +++++++++++++++++ modules/m_info.c | 5 +++++ 7 files changed, 53 insertions(+) diff --git a/doc/reference.conf b/doc/reference.conf index e4a16dbc4..58b5fed1f 100644 --- a/doc/reference.conf +++ b/doc/reference.conf @@ -1315,6 +1315,13 @@ general { */ ping_cookie = no; + /* ping warn time: how long to wait after pinging a server before starting + * to complain it is unresponsive. Note that the ping check interval is 30 + * seconds, so the first complaint will come at the next check after this + * time has passed. + */ + ping_warn_time = 15 seconds; + /* connect timeout: sets how long we should wait for a connection * request to succeed */ diff --git a/include/client.h b/include/client.h index 7dfc8f14f..2d61d5f9b 100644 --- a/include/client.h +++ b/include/client.h @@ -419,6 +419,7 @@ struct ListClient #define FLAGS_EXEMPTSHIDE 0x04000000 #define FLAGS_EXEMPTJUPE 0x08000000 #define FLAGS_IDENTIFIED 0x10000000 /* owns their current nick */ +#define FLAGS_PINGWARN 0x20000000 /* whether we've warned about this client being unresponsive */ /* flags for local clients, this needs stuff moved from above to here at some point */ diff --git a/include/s_conf.h b/include/s_conf.h index acc4f1b82..53233ee1c 100644 --- a/include/s_conf.h +++ b/include/s_conf.h @@ -204,6 +204,7 @@ struct config_file_entry int operspy_admin_only; int pace_wait; int pace_wait_simple; + int ping_warn_time; int short_motd; int no_oper_flood; int hide_server; diff --git a/ircd/client.c b/ircd/client.c index 24738ae01..a06fda58d 100644 --- a/ircd/client.c +++ b/ircd/client.c @@ -420,6 +420,27 @@ check_pings_list(rb_dlink_list * list) client_p->localClient->lasttime = rb_current_time() - ping; sendto_one(client_p, "PING :%s", me.name); } + else if (ConfigFileEntry.ping_warn_time > 0 && (IsServer(client_p) || IsHandshake(client_p)) && + (rb_current_time() - client_p->localClient->lasttime) >= (ping + ConfigFileEntry.ping_warn_time)) + { + /* + * if we haven't heard from a server in a while, + * warn opers that something could be wrong... + * + * we'll do this about every 30 seconds until + * the server either becomes responsive or + * pings out. whichever comes first. + */ + client_p->flags |= FLAGS_PINGWARN; + sendto_realops_snomask(SNO_GENERAL, L_NETWIDE, + "Warning: No response from %s for %ld seconds", + client_p->name, + (rb_current_time() - client_p->localClient->lasttime - ping)); + ilog(L_SERVER, + "Warning: No response from %s for %ld seconds", + log_client_name(client_p, HIDE_IP), + (rb_current_time() - client_p->localClient->lasttime - ping)); + } } /* ping_timeout: */ diff --git a/ircd/newconf.c b/ircd/newconf.c index d5c072582..33bfd4d1a 100644 --- a/ircd/newconf.c +++ b/ircd/newconf.c @@ -2751,6 +2751,7 @@ static struct ConfEntry conf_general_table[] = { "pace_wait", CF_TIME, NULL, 0, &ConfigFileEntry.pace_wait }, { "pace_wait_simple", CF_TIME, NULL, 0, &ConfigFileEntry.pace_wait_simple }, { "ping_cookie", CF_YESNO, NULL, 0, &ConfigFileEntry.ping_cookie }, + { "ping_warn_time", CF_TIME, NULL, 0, &ConfigFileEntry.ping_warn_time }, { "reject_after_count", CF_INT, NULL, 0, &ConfigFileEntry.reject_after_count }, { "reject_ban_time", CF_TIME, NULL, 0, &ConfigFileEntry.reject_ban_time }, { "reject_duration", CF_TIME, NULL, 0, &ConfigFileEntry.reject_duration }, diff --git a/ircd/packet.c b/ircd/packet.c index ee8429556..f0350d5ea 100644 --- a/ircd/packet.c +++ b/ircd/packet.c @@ -272,6 +272,23 @@ read_packet(rb_fde_t * F, void *data) client_p->localClient->lasttime = rb_current_time(); client_p->flags &= ~FLAGS_PINGSENT; + if (client_p->flags & FLAGS_PINGWARN) + { + /* + * if we warned about this server being unresponsive + * before, let's let everyone know there's no need + * to panic + */ + client_p->flags &= ~FLAGS_PINGWARN; + sendto_realops_snomask(SNO_GENERAL, L_NETWIDE, + "Received response from previously unresponsive link %s", + client_p->name); + ilog(L_SERVER, + "Received response from previously unresponsive link %s", + log_client_name(client_p, HIDE_IP)); + } + + /* * Before we even think of parsing what we just read, stick * it on the end of the receive queue and do it when its diff --git a/modules/m_info.c b/modules/m_info.c index f6d03659b..6742bafb1 100644 --- a/modules/m_info.c +++ b/modules/m_info.c @@ -421,6 +421,11 @@ static struct InfoStruct info_table[] = { "Require ping cookies to connect", INFO_INTBOOL(&ConfigFileEntry.ping_cookie), }, + { + "ping_warn_time", + "Amount of time between warnings about unresponsive servers", + INFO_DECIMAL(&ConfigFileEntry.ping_warn_time), + }, { "reject_after_count", "Client rejection threshold setting", From a6ad35e5d81fba5925ca1405177a188f5a8077c8 Mon Sep 17 00:00:00 2001 From: TheDaemoness Date: Sat, 1 Jul 2023 11:09:41 -0700 Subject: [PATCH 07/30] Add extban type for unidentified connections (#411) * Add extension extb_guest This module provides an extban type that acts as a normal ban but only matches unidentified users. * Document extban g in help/opers/extban * extensions/extb_guest: support CIDR masks in $g extbans This allows a channel operator to set a channel ban such as "$g:*!*@192.0.2.0/24#*web.libera.chat*" and have it function as intended. --------- Co-authored-by: Aaron Jones --- extensions/Makefile.am | 1 + extensions/README | 1 + extensions/extb_guest.c | 73 +++++++++++++++++++++++++++++++++++++++++ help/opers/extban | 1 + 4 files changed, 76 insertions(+) create mode 100644 extensions/extb_guest.c diff --git a/extensions/Makefile.am b/extensions/Makefile.am index 4ab221fa2..b09e18241 100644 --- a/extensions/Makefile.am +++ b/extensions/Makefile.am @@ -19,6 +19,7 @@ extension_LTLIBRARIES = \ extb_account.la \ extb_canjoin.la \ extb_channel.la \ + extb_guest.la \ extb_hostmask.la \ extb_oper.la \ extb_server.la \ diff --git a/extensions/README b/extensions/README index 358b9dcc5..16b107ce6 100644 --- a/extensions/README +++ b/extensions/README @@ -79,6 +79,7 @@ extb_account.so - Account bans (+b $a[:mask]) extb_canjoin.so - Banned from another channel (+b $j:mask) extb_channel.so - Other-channel bans (+b $c:mask) extb_extgecos.so - Extended ban (+b $x:mask) +extb_guest.so - Unidentified bans (+b $g:mask) extb_oper.so - Oper bans (+b $o) extb_realname.so - Realname (gecos) bans (+b $r:mask) extb_server.so - Server bans (+b $s:mask) diff --git a/extensions/extb_guest.c b/extensions/extb_guest.c new file mode 100644 index 000000000..22a1e4368 --- /dev/null +++ b/extensions/extb_guest.c @@ -0,0 +1,73 @@ +/* + * Guest extban type: bans unidentified users matching nick!user@host. + * -- TheDaemoness + */ + +#include "stdinc.h" +#include "modules.h" +#include "client.h" +#include "ircd.h" + +static const char extb_desc[] = "Guest ($g) extban type - bans unidentified users matching nick!user@host"; + +static int _modinit(void); +static void _moddeinit(void); +static int eb_guest(const char *data, struct Client *client_p, struct Channel *chptr, long mode_type); + +DECLARE_MODULE_AV2(extb_guest, _modinit, _moddeinit, NULL, NULL, NULL, NULL, NULL, extb_desc); + +static int +_modinit(void) +{ + extban_table['g'] = eb_guest; + + return 0; +} + +static void +_moddeinit(void) +{ + extban_table['g'] = NULL; +} + +static int eb_guest(const char *data, struct Client *client_p, + struct Channel *chptr, long mode_type) +{ + (void)chptr; + + if (data == NULL) + return EXTBAN_INVALID; + + const char *idx = strchr(data, '#'); + + if (idx != NULL && idx[1] == '\0') + /* Users cannot have empty realnames, + * so don't let a ban be set matching one + */ + return EXTBAN_INVALID; + + if (!EmptyString(client_p->user->suser)) + return EXTBAN_NOMATCH; + + if (idx != NULL) + { + char buf[BUFSIZE]; + + // Copy the nick!user@host part of the ban + memcpy(buf, data, (idx - data)); + buf[(idx - data)] = '\0'; + + // Advance to the realname part of the ban + idx++; + + if (client_matches_mask(client_p, buf) && match(idx, client_p->info)) + return EXTBAN_MATCH; + + return EXTBAN_NOMATCH; + } + + if (client_matches_mask(client_p, data)) + return EXTBAN_MATCH; + + return EXTBAN_NOMATCH; +} diff --git a/help/opers/extban b/help/opers/extban index 5a0e8e68b..73a979dd7 100644 --- a/help/opers/extban +++ b/help/opers/extban @@ -19,6 +19,7 @@ Unless noted below, all types can be used with +b, +q, +e and +I. $a: - Matches users logged in with a username matching the mask (* and ? wildcards) $c: - Matches users who are on the given channel + $g: - Matches as a normal ban but excludes logged in users $o - Matches opers (most useful with +I) $r: - Matches users with a realname (gecos) matching the mask (* and ? wildcards); this can only be used with +b and +q From 5ca20c098a125787a130daaa5c539d10784236cf Mon Sep 17 00:00:00 2001 From: Aaron Jones Date: Sat, 1 Jul 2023 18:16:27 +0000 Subject: [PATCH 08/30] extensions/extb_extgecos: support CIDR masks in $x extbans (#414) This allows a channel operator to set a channel ban such as "$x:*!*@192.0.2.0/24#*web.libera.chat*" and have it function as intended. Closes solanum-ircd/solanum#26 --- extensions/extb_extgecos.c | 33 ++++++++++++++++++++++++++++----- 1 file changed, 28 insertions(+), 5 deletions(-) diff --git a/extensions/extb_extgecos.c b/extensions/extb_extgecos.c index 11d06b011..4f6a0d770 100644 --- a/extensions/extb_extgecos.c +++ b/extensions/extb_extgecos.c @@ -34,15 +34,38 @@ _moddeinit(void) static int eb_extended(const char *data, struct Client *client_p, struct Channel *chptr, long mode_type) { - char buf[BUFSIZE]; - (void)chptr; if (data == NULL) return EXTBAN_INVALID; - snprintf(buf, sizeof buf, "%s!%s@%s#%s", - client_p->name, client_p->username, client_p->host, client_p->info); + const char *idx = strchr(data, '#'); + + if (idx != NULL && idx[1] == '\0') + /* Users cannot have empty realnames, + * so don't let a ban be set matching one + */ + return EXTBAN_INVALID; + + if (idx != NULL) + { + char buf[BUFSIZE]; + + // Copy the nick!user@host part of the ban + memcpy(buf, data, (idx - data)); + buf[(idx - data)] = '\0'; + + // Advance to the realname part of the ban + idx++; + + if (client_matches_mask(client_p, buf) && match(idx, client_p->info)) + return EXTBAN_MATCH; + + return EXTBAN_NOMATCH; + } + + if (client_matches_mask(client_p, data)) + return EXTBAN_MATCH; - return match(data, buf) ? EXTBAN_MATCH : EXTBAN_NOMATCH; + return EXTBAN_NOMATCH; } From c33da0d24ed3cd737263e0469fa600681ec75d42 Mon Sep 17 00:00:00 2001 From: Jess Porter Date: Sat, 1 Jul 2023 19:22:40 +0100 Subject: [PATCH 09/30] support RSFNC indicating type of FNC (e.g. FORCE vs REGAIN) (#406) --- ircd/s_serv.c | 2 ++ modules/m_services.c | 13 +++++++++++++ 2 files changed, 15 insertions(+) diff --git a/ircd/s_serv.c b/ircd/s_serv.c index dca66b2b8..92539389c 100644 --- a/ircd/s_serv.c +++ b/ircd/s_serv.c @@ -81,6 +81,7 @@ unsigned int CAP_ENCAP; unsigned int CAP_TS6; unsigned int CAP_SERVICE; unsigned int CAP_RSFNC; +unsigned int CAP_RSFNCF; unsigned int CAP_SAVE; unsigned int CAP_EUID; unsigned int CAP_EOPMOD; @@ -122,6 +123,7 @@ init_builtin_capabs(void) CAP_ENCAP = capability_put(serv_capindex, "ENCAP", NULL); CAP_SERVICE = capability_put(serv_capindex, "SERVICES", NULL); CAP_RSFNC = capability_put(serv_capindex, "RSFNC", NULL); + CAP_RSFNCF = capability_put(serv_capindex, "RSFNCF", NULL); CAP_SAVE = capability_put(serv_capindex, "SAVE", NULL); CAP_EUID = capability_put(serv_capindex, "EUID", NULL); CAP_EOPMOD = capability_put(serv_capindex, "EOPMOD", NULL); diff --git a/modules/m_services.c b/modules/m_services.c index 768df637d..0127887ef 100644 --- a/modules/m_services.c +++ b/modules/m_services.c @@ -163,6 +163,13 @@ me_login(struct MsgBuf *msgbuf_p, struct Client *client_p, struct Client *source rb_strlcpy(source_p->user->suser, parv[1], sizeof(source_p->user->suser)); } +/* me_rsfnc() + * parv[1] = current user nickname + * parv[2] = target nickname + * parv[3] = new nickts + * parv[4] = current nickts + * parv[5] = optional; 0 (don't override RESVs) or 1 (override RESVs) + */ static void me_rsfnc(struct MsgBuf *msgbuf_p, struct Client *client_p, struct Client *source_p, int parc, const char *parv[]) @@ -198,6 +205,12 @@ me_rsfnc(struct MsgBuf *msgbuf_p, struct Client *client_p, struct Client *source if(target_p->tsinfo != curts) return; + /* received a non-forced RSFNC for a nickname that is RESV. + * silently ignore it. ~jess + */ + if(parc > 5 && atoi(parv[5]) == 0 && find_nick_resv(parv[2])) + return; + if((exist_p = find_named_client(parv[2]))) { char buf[BUFSIZE]; From 7a2ccb077fb76ef7a532d18242fa208bcff414ca Mon Sep 17 00:00:00 2001 From: Doug Freed Date: Fri, 23 Jun 2023 00:00:55 +0000 Subject: [PATCH 10/30] modules: clear module list and mod paths in init If main is called more than once (like in tests), everything is reinitialized except the loaded module list and module paths, so clear them too so that modules are loaded again and the path list is correct. --- ircd/modules.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ircd/modules.c b/ircd/modules.c index bf010cb27..2af84083b 100644 --- a/ircd/modules.c +++ b/ircd/modules.c @@ -77,6 +77,9 @@ init_modules(void) exit(EXIT_FAILURE); } + memset(&module_list, 0, sizeof(module_list)); + memset(&mod_paths, 0, sizeof(mod_paths)); + /* Add the default paths we look in to the module system --nenolod */ mod_add_path(ircd_paths[IRCD_PATH_MODULES]); mod_add_path(ircd_paths[IRCD_PATH_AUTOLOAD_MODULES]); From cc95a6722e0f4179337f6afea7fa38deb9e53e22 Mon Sep 17 00:00:00 2001 From: Doug Freed Date: Thu, 22 Jun 2023 23:11:35 +0000 Subject: [PATCH 11/30] modules: quietly succeed at loading a module if already loaded This allows explicitly loading a module in the config so it's available for later config items that might need it, and skips the double load when main loads all modules, which would cause errors --- ircd/modules.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ircd/modules.c b/ircd/modules.c index 2af84083b..93e43acca 100644 --- a/ircd/modules.c +++ b/ircd/modules.c @@ -465,6 +465,10 @@ load_a_module(const char *path, bool warn, int origin, bool core) if((c = rb_strcasestr(mod_displayname, LT_MODULE_EXT)) != NULL) *c = '\0'; + /* Quietly succeed if the module is already loaded */ + if(findmodule_byname(mod_displayname) != NULL) + return true; + tmpptr = lt_dlopenext(path); if(tmpptr == NULL) From 83f7888d72cf1faba78a4c9ed9df4780b11534b3 Mon Sep 17 00:00:00 2001 From: Valentin Lorentz Date: Wed, 23 Nov 2022 20:56:35 +0100 Subject: [PATCH 12/30] Add ACCOUNTEXTBAN ISUPPORT token To support the draft IRCv3 spec: https://github.com/ircv3/ircv3-specifications/pull/464 --- extensions/extb_account.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/extensions/extb_account.c b/extensions/extb_account.c index 82af58899..a0fceb935 100644 --- a/extensions/extb_account.c +++ b/extensions/extb_account.c @@ -7,6 +7,7 @@ #include "modules.h" #include "client.h" #include "ircd.h" +#include "supported.h" static const char extb_desc[] = "Account ($a) extban type"; @@ -20,6 +21,7 @@ static int _modinit(void) { extban_table['a'] = eb_account; + add_isupport("ACCOUNTEXTBAN", isupport_string, "a"); return 0; } @@ -28,6 +30,7 @@ static void _moddeinit(void) { extban_table['a'] = NULL; + delete_isupport("ACCOUNTEXTBAN"); } static int eb_account(const char *data, struct Client *client_p, From c5a067165c023d3ed7acc9b19ea731796a662809 Mon Sep 17 00:00:00 2001 From: jesopo Date: Thu, 12 Jan 2023 00:24:15 +0000 Subject: [PATCH 13/30] show opers a reasonable message when auspex allows them to see idle time --- modules/m_whois.c | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/modules/m_whois.c b/modules/m_whois.c index 7ed576434..4fcffff39 100644 --- a/modules/m_whois.c +++ b/modules/m_whois.c @@ -386,15 +386,25 @@ single_whois(struct Client *source_p, struct Client *target_p, int operspy) sendto_one_numeric(source_p, RPL_WHOISIDLE, form_str(RPL_WHOISIDLE), target_p->name, - hdata_showidle.approved ? (long)(rb_current_time() - target_p->localClient->last) : 0, + hdata_showidle.approved != WHOIS_IDLE_HIDE ? (long)(rb_current_time() - target_p->localClient->last) : 0, (unsigned long)target_p->localClient->firsttime); - if (hdata_showidle.approved == WHOIS_IDLE_HIDE && (source_p->umodes & user_modes['I'])) - /* if the source has hidden their idle time, notify the source that they can't view others' idle times either */ - sendto_one_numeric(source_p, RPL_WHOISTEXT, form_str(RPL_WHOISTEXT), target_p->name, "has a hidden idle time because your own idle time is hidden"); - else if (hdata_showidle.approved != WHOIS_IDLE_SHOW) + if (hdata_showidle.approved != WHOIS_IDLE_SHOW && (source_p->umodes & user_modes['I'])) + { + if (hdata_showidle.approved == WHOIS_IDLE_HIDE) + /* if the source has hidden their idle time, notify the source that they can't view others' idle times either */ + sendto_one_numeric(source_p, RPL_WHOISTEXT, form_str(RPL_WHOISTEXT), target_p->name, "has a hidden idle time because your own idle time is hidden"); + else + /* client has auspex to be able to see idle time, but make sure they know that's why they're seeing it */ + sendto_one_numeric(source_p, RPL_WHOISTEXT, form_str(RPL_WHOISTEXT), target_p->name, + "has a hidden idle time because your own idle time is hidden, but you have auspex"); + } + else if (hdata_showidle.approved == WHOIS_IDLE_HIDE) /* if the target has hidden their idle time, notify the source */ sendto_one_numeric(source_p, RPL_WHOISTEXT, form_str(RPL_WHOISTEXT), target_p->name, "is hiding their idle time"); + else if (hdata_showidle.approved == WHOIS_IDLE_AUSPEX) + /* if the target has hidden their idle time, notify the source */ + sendto_one_numeric(source_p, RPL_WHOISTEXT, form_str(RPL_WHOISTEXT), target_p->name, "is hiding their idle time, but you have auspex"); } else { From fa33bdd14d0683c72f89ee931be1e888d22f80b9 Mon Sep 17 00:00:00 2001 From: jesopo Date: Sat, 25 Feb 2023 16:03:59 +0000 Subject: [PATCH 14/30] prioritise telling opers that a auspexed target is +I --- modules/m_whois.c | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/modules/m_whois.c b/modules/m_whois.c index 4fcffff39..d82f65fa1 100644 --- a/modules/m_whois.c +++ b/modules/m_whois.c @@ -389,7 +389,18 @@ single_whois(struct Client *source_p, struct Client *target_p, int operspy) hdata_showidle.approved != WHOIS_IDLE_HIDE ? (long)(rb_current_time() - target_p->localClient->last) : 0, (unsigned long)target_p->localClient->firsttime); - if (hdata_showidle.approved != WHOIS_IDLE_SHOW && (source_p->umodes & user_modes['I'])) + if (hdata_showidle.approved == WHOIS_IDLE_SHOW) + ; + else if (target_p->umodes & user_modes['I']) + { + if (hdata_showidle.approved == WHOIS_IDLE_HIDE) + /* if the target has hidden their idle time, notify the source */ + sendto_one_numeric(source_p, RPL_WHOISTEXT, form_str(RPL_WHOISTEXT), target_p->name, "is hiding their idle time"); + else + /* if the target has hidden their idle time, notify the source */ + sendto_one_numeric(source_p, RPL_WHOISTEXT, form_str(RPL_WHOISTEXT), target_p->name, "is hiding their idle time, but you have auspex"); + } + else { if (hdata_showidle.approved == WHOIS_IDLE_HIDE) /* if the source has hidden their idle time, notify the source that they can't view others' idle times either */ @@ -399,12 +410,6 @@ single_whois(struct Client *source_p, struct Client *target_p, int operspy) sendto_one_numeric(source_p, RPL_WHOISTEXT, form_str(RPL_WHOISTEXT), target_p->name, "has a hidden idle time because your own idle time is hidden, but you have auspex"); } - else if (hdata_showidle.approved == WHOIS_IDLE_HIDE) - /* if the target has hidden their idle time, notify the source */ - sendto_one_numeric(source_p, RPL_WHOISTEXT, form_str(RPL_WHOISTEXT), target_p->name, "is hiding their idle time"); - else if (hdata_showidle.approved == WHOIS_IDLE_AUSPEX) - /* if the target has hidden their idle time, notify the source */ - sendto_one_numeric(source_p, RPL_WHOISTEXT, form_str(RPL_WHOISTEXT), target_p->name, "is hiding their idle time, but you have auspex"); } else { From 309e1e624e4920db7f97f7b50a203617ce35b750 Mon Sep 17 00:00:00 2001 From: Stephen Bennett Date: Sat, 1 Jul 2023 19:28:01 +0100 Subject: [PATCH 15/30] Clarify messages when auspex users have hidden their own idle times Co-authored-by: Doug Freed --- modules/m_whois.c | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/modules/m_whois.c b/modules/m_whois.c index d82f65fa1..117acadb9 100644 --- a/modules/m_whois.c +++ b/modules/m_whois.c @@ -389,20 +389,18 @@ single_whois(struct Client *source_p, struct Client *target_p, int operspy) hdata_showidle.approved != WHOIS_IDLE_HIDE ? (long)(rb_current_time() - target_p->localClient->last) : 0, (unsigned long)target_p->localClient->firsttime); - if (hdata_showidle.approved == WHOIS_IDLE_SHOW) - ; - else if (target_p->umodes & user_modes['I']) + if (hdata_showidle.approved != WHOIS_IDLE_SHOW) { - if (hdata_showidle.approved == WHOIS_IDLE_HIDE) - /* if the target has hidden their idle time, notify the source */ - sendto_one_numeric(source_p, RPL_WHOISTEXT, form_str(RPL_WHOISTEXT), target_p->name, "is hiding their idle time"); - else - /* if the target has hidden their idle time, notify the source */ - sendto_one_numeric(source_p, RPL_WHOISTEXT, form_str(RPL_WHOISTEXT), target_p->name, "is hiding their idle time, but you have auspex"); - } - else - { - if (hdata_showidle.approved == WHOIS_IDLE_HIDE) + if (target_p->umodes & user_modes['I']) + { + if (hdata_showidle.approved == WHOIS_IDLE_HIDE) + /* if the target has hidden their idle time, notify the source */ + sendto_one_numeric(source_p, RPL_WHOISTEXT, form_str(RPL_WHOISTEXT), target_p->name, "is hiding their idle time"); + else + /* if the target has hidden their idle time, notify the source */ + sendto_one_numeric(source_p, RPL_WHOISTEXT, form_str(RPL_WHOISTEXT), target_p->name, "is hiding their idle time, but you have auspex"); + } + else if (hdata_showidle.approved == WHOIS_IDLE_HIDE) /* if the source has hidden their idle time, notify the source that they can't view others' idle times either */ sendto_one_numeric(source_p, RPL_WHOISTEXT, form_str(RPL_WHOISTEXT), target_p->name, "has a hidden idle time because your own idle time is hidden"); else From 0ca18d072a7d1dfcfbdff6dcf4f2788f52935a03 Mon Sep 17 00:00:00 2001 From: TheDaemoness Date: Mon, 3 Jul 2023 13:50:46 -0700 Subject: [PATCH 16/30] extensions/extb_extgecos: Fix breakage This commit returns $x's old behavior as long as the mask does not contain a #, otherwise it uses the new behavior that supports CIDR notation. This fixes `$x:*badword*` not matching realnames containing "badword". --- extensions/extb_extgecos.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/extensions/extb_extgecos.c b/extensions/extb_extgecos.c index 4f6a0d770..f636774e1 100644 --- a/extensions/extb_extgecos.c +++ b/extensions/extb_extgecos.c @@ -47,10 +47,10 @@ static int eb_extended(const char *data, struct Client *client_p, */ return EXTBAN_INVALID; + char buf[BUFSIZE]; + if (idx != NULL) { - char buf[BUFSIZE]; - // Copy the nick!user@host part of the ban memcpy(buf, data, (idx - data)); buf[(idx - data)] = '\0'; @@ -60,12 +60,16 @@ static int eb_extended(const char *data, struct Client *client_p, if (client_matches_mask(client_p, buf) && match(idx, client_p->info)) return EXTBAN_MATCH; - - return EXTBAN_NOMATCH; } + else + { + // Treat data as a pattern to match against the full nick!user@host#gecos. + snprintf(buf, sizeof buf, "%s!%s@%s#%s", + client_p->name, client_p->username, client_p->host, client_p->info); - if (client_matches_mask(client_p, data)) - return EXTBAN_MATCH; + if (match(data, buf)) + return EXTBAN_MATCH; + } return EXTBAN_NOMATCH; } From 28bcd75330ad1cedaf1f680b7a263ba5356bf752 Mon Sep 17 00:00:00 2001 From: Mike Quin Date: Sun, 6 Aug 2023 19:02:14 +0100 Subject: [PATCH 17/30] Add documentation for extensions/filter --- doc/features/filter.txt | 46 +++++++++++++++++++++++++++++++++++++++++ help/opers/setfilter | 27 ++++++++++++++++++++++++ 2 files changed, 73 insertions(+) create mode 100644 doc/features/filter.txt create mode 100644 help/opers/setfilter diff --git a/doc/features/filter.txt b/doc/features/filter.txt new file mode 100644 index 000000000..331eb16f8 --- /dev/null +++ b/doc/features/filter.txt @@ -0,0 +1,46 @@ +extensions/filter module documentation +-------------------------------------- + +The filter extension implements message content filtering using +solanum's hook framework and Intel's Hyperscan regular expression +matching library. + +It requires an x86_64 processor with SSSE3 extensions. + +To operate, the filter requires a database of regular expessions +that have been compiled using the Hyperscan library's +hs_compile_multi() or hs_compile_ext_multi() functions. + +The command SETFILTER is used to manage operation of the filter and to +load compiled Hyperscan databases. + +General documenation of SETFILTER is available using the 'HELP SETFILTER' +command. + +For each expression in the database, the three least significant bits +of the expression ID are used to indicate which action the ircd should +take in the event of a match: + +001 (1) DROP - The message will be dropped and the client will be sent + an ERR_CANNOTSENDTOCHAN message. +010 (2) KILL - The connection from which the message was recevied will + be closed. +100 (4) ALARM - A Server Notice will be generated indicating that an + expression was matched. The nick, user, hostname and + IP address will be reported. For privacy, the expression + that has been matched will not be disclosed. + +Messages are passed to the filter module in a format similar to an +IRC messages: + +0:nick!user@host#1 PRIVMSG #help :hello! + +The number at the start of the line indicates the scanning pass: +Messages are scanned twice, once as they were received (0), and once +with any formatting or unprintable characters stripped (1). + +By default, 'nick', 'user' and 'host' will contain *. This behaviour +can be changed at build time if filtering on these fields is required. + +The number after the # will be 0 or 1 depending on whether the sending +client was identified to a NickServ account. diff --git a/help/opers/setfilter b/help/opers/setfilter new file mode 100644 index 000000000..07c051e6b --- /dev/null +++ b/help/opers/setfilter @@ -0,0 +1,27 @@ +SETFILTER * ENABLE +SETFILTER * DISABLE +SETFILTER * DROP +SETFILTER * ABORT +SETFILTER [server-mask] { NEW | APPLY | + } + +Manages Hyperscan message filtering. + +ENABLE activates filtering. + +DISABLE deactivates filtering. It can be re-enabled with ENABLE. + +DROP unloads the currently loaded Hyperscan database, if any. + +ABORT cancels a database load operation started with NEW. + +NEW prepares a buffer to accept a new Hyperscan database. + + is a base64 encoded chunk of a serialized hyperscan database. + +APPLY deserialises the buffer and sets the resulting hyperscan database +as the one to use for filtering. + + can be any string and must be the same for all NEW, +, and +APPLY commands for a single hyperscan database. + +Requires Oper Priv: oper:admin \ No newline at end of file From 21dbde201c3422a122ad6e8d11daba5dfc349c12 Mon Sep 17 00:00:00 2001 From: Mike Quin Date: Mon, 7 Aug 2023 08:39:30 +0100 Subject: [PATCH 18/30] Describe database loading. --- doc/features/filter.txt | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/doc/features/filter.txt b/doc/features/filter.txt index 331eb16f8..5c3b983b4 100644 --- a/doc/features/filter.txt +++ b/doc/features/filter.txt @@ -44,3 +44,15 @@ can be changed at build time if filtering on these fields is required. The number after the # will be 0 or 1 depending on whether the sending client was identified to a NickServ account. + +The process for loading filters is as follows: + +1. The Hyperscan database is serialized using hs_serialize_database(). +2. The serialized data is base64 encoded +3. A 'SETFILTER NEW' command is sent. +4. The base64 data is split into chunks short enough to fit into + a 512 byte IRC line, taking into account space needed for the + command, check field, and server mask, and send using 'SETFILTER +' + commands. +5. Once the entire database has been sent, a 'SETFILTER APPLY' command + is sent to commit it. From a4db1d4784bfda4e2fa7e4f7dd1899c90efe8bd3 Mon Sep 17 00:00:00 2001 From: Mike Quin Date: Mon, 7 Aug 2023 14:28:59 +0100 Subject: [PATCH 19/30] Correct order of chunking and encoding steps. --- doc/features/filter.txt | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/doc/features/filter.txt b/doc/features/filter.txt index 5c3b983b4..40d23faee 100644 --- a/doc/features/filter.txt +++ b/doc/features/filter.txt @@ -48,11 +48,12 @@ client was identified to a NickServ account. The process for loading filters is as follows: 1. The Hyperscan database is serialized using hs_serialize_database(). -2. The serialized data is base64 encoded -3. A 'SETFILTER NEW' command is sent. -4. The base64 data is split into chunks short enough to fit into - a 512 byte IRC line, taking into account space needed for the - command, check field, and server mask, and send using 'SETFILTER +' - commands. +2. A 'SETFILTER NEW' command is sent. +3. The serialized data is split into chunks and base64 encoded. + The chunk size need to be chosen to ensure that the resuliting + strings are short enough to fit into a 510 byte IRC line, taking + into account space needed for the 'SETFILTER +' command, check field, + server mask, and base64 overhead. +4. The encoded chunks are sent using 'SETFILTER +' commands 5. Once the entire database has been sent, a 'SETFILTER APPLY' command is sent to commit it. From 7a7aa92f8aead19253e89d8934c5343a768d9c52 Mon Sep 17 00:00:00 2001 From: Mike Quin Date: Mon, 7 Aug 2023 14:38:31 +0100 Subject: [PATCH 20/30] Fix typo --- doc/features/filter.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/features/filter.txt b/doc/features/filter.txt index 40d23faee..6a4050614 100644 --- a/doc/features/filter.txt +++ b/doc/features/filter.txt @@ -50,7 +50,7 @@ The process for loading filters is as follows: 1. The Hyperscan database is serialized using hs_serialize_database(). 2. A 'SETFILTER NEW' command is sent. 3. The serialized data is split into chunks and base64 encoded. - The chunk size need to be chosen to ensure that the resuliting + The chunk size needs to be chosen to ensure that the resuliting strings are short enough to fit into a 510 byte IRC line, taking into account space needed for the 'SETFILTER +' command, check field, server mask, and base64 overhead. From 1b4957f617122e51a4ddbe81487e1ae59f0b4e51 Mon Sep 17 00:00:00 2001 From: Mike Quin Date: Mon, 7 Aug 2023 15:04:21 +0100 Subject: [PATCH 21/30] Terminate last line in setfilter help --- help/opers/setfilter | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/help/opers/setfilter b/help/opers/setfilter index 07c051e6b..651668305 100644 --- a/help/opers/setfilter +++ b/help/opers/setfilter @@ -24,4 +24,4 @@ as the one to use for filtering. can be any string and must be the same for all NEW, +, and APPLY commands for a single hyperscan database. -Requires Oper Priv: oper:admin \ No newline at end of file +Requires Oper Priv: oper:admin From 2a0f0393d22c517b03fa1274bfdc0ea3ac8e086c Mon Sep 17 00:00:00 2001 From: Svetlana T Date: Wed, 30 Aug 2023 02:12:01 +1000 Subject: [PATCH 22/30] document +M cmode (#417) --- help/opers/cmode | 2 ++ help/users/cmode | 72 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 74 insertions(+) create mode 100644 help/users/cmode diff --git a/help/opers/cmode b/help/opers/cmode index e6ba9d0a4..fa2a64132 100644 --- a/help/opers/cmode +++ b/help/opers/cmode @@ -37,6 +37,8 @@ NO PARAMETERS: +C - Disable CTCP. All CTCP messages to the channel, except ACTION, are disallowed. ? +O - IRC Operator only channel. + ? +M - IRC Operators can not be kicked. Only settable by opers. Only + viewable by opers. ? +A - IRC server administrator only channel. ? +T - No NOTICEs allowed in the channel. ? +S - Only users connected via SSL/TLS may join the channel while this diff --git a/help/users/cmode b/help/users/cmode new file mode 100644 index 000000000..e6ba9d0a4 --- /dev/null +++ b/help/users/cmode @@ -0,0 +1,72 @@ +MODE <+|-> [parameters] + +? designates that the cmode is provided by an extension +and may not be present on this server. + +CHANNELMODE - DESCRIPTION +------------------------------------------------------------------------ +NO PARAMETERS: + +n - No external messages. Only channel members may talk in + the channel. + +t - Ops Topic. Only opped (+o) users may set the topic. + +s - Secret. Channel will not be shown in /whois and /list etc. + +p - Private. Disables /knock to the channel. + +m - Moderated. Only opped/voiced users may talk in channel. + +i - Invite only. Users need to be invited or match a +I to + join the channel. + +r - Registered users only. Only users identified to services + may join. + ? +R - Blocks messages from unregistered users. Only users + identified to services may talk in the channel. + +c - No color. All markup (color, bold, underline, etc.) in + messages is stripped. + +g - Free invite. Everyone may invite users. Significantly + weakens +i control. + ? +u - Unfiltered. Receive messages that would otherwise be filtered + server side based on content. + +z - Op moderated. Messages blocked by +m, +b and +q are instead + sent to ops. + +L - Large ban list. Increase maximum number of +beIq entries. + Only settable by opers. + +P - Permanent. Channel does not disappear when empty. Only + settable by opers. + +F - Free target. Anyone may set forwards to this (otherwise + ops are necessary). + +Q - Disable forward. Users cannot be forwarded to the channel + (however, new forwards can still be set subject to +F). + +C - Disable CTCP. All CTCP messages to the channel, except ACTION, + are disallowed. + ? +O - IRC Operator only channel. + ? +A - IRC server administrator only channel. + ? +T - No NOTICEs allowed in the channel. + ? +S - Only users connected via SSL/TLS may join the channel while this + mode is set. Users already in the channel are not affected. + +WITH PARAMETERS: + +f - Forward. Forwards users who cannot join because of +i, + +j, +l or +r. + PARAMS: /mode #channel +f #channel2 + +j - Join throttle. Limits number of joins to the channel per time. + PARAMS: /mode #channel +j count:time + +k - Key. Requires users to issue /join #channel KEY to join. + PARAMS: /mode #channel +k key + +l - Limit. Impose a maximum number of LIMIT people in the channel. + PARAMS: /mode #channel +l limit + +v - Voice. Allows a user to talk in a +m channel. Noted by +nick. + PARAMS: /mode #channel +v nick + +o - Op. Allows a user full control over the channel. + PARAMS: /mode #channel +o nick + +b - Ban. Prevents a user from entering the channel, and from + sending or changing nick if they are on it, based on a + nick!ident@host match. + PARAMS: /mode #channel +b nick!user@host + +q - Quiet. Prevents a user from sending to the channel or changing + nick, based on a nick!ident@host match. + PARAMS: /mode #channel +q nick!user@host + +e - Exempt. Allows a user to join a channel and send to it even if + they are banned (+b) or quieted (+q), based on a nick!ident@host + match. + PARAMS: /mode #channel +e nick!user@host + +I - Invite Exempt. Allows a user to join a +i channel without an + invite, based on a nick!user@host match. + PARAMS: /mode #channel +I nick!user@host From 460e7932744e7d48795875270a1a610a6a8dfa31 Mon Sep 17 00:00:00 2001 From: Eric Mertens Date: Tue, 3 Oct 2023 16:35:52 -0700 Subject: [PATCH 23/30] Replace RPL_WHOISTEXT(337) with RPL_WHOISSPECIAL(320) (#419) Reasons: * 337 conflicts with other IRCds use as RPL_ENDOFINVITELIST * 320 is commonly used for extra human-readable information --- include/messages.h | 2 +- include/numeric.h | 2 +- modules/m_whois.c | 10 +++++----- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/include/messages.h b/include/messages.h index 0130111e9..a50d9a6db 100644 --- a/include/messages.h +++ b/include/messages.h @@ -96,6 +96,7 @@ #define NUMERIC_STR_317 "%s %ld %lu :seconds idle, signon time" #define NUMERIC_STR_318 "%s :End of /WHOIS list." #define NUMERIC_STR_319 ":%s 319 %s %s :" +#define NUMERIC_STR_320 "%s :%s" #define NUMERIC_STR_321 ":%s 321 %s Channel :Users Name" #define NUMERIC_STR_322 ":%s 322 %s %s%s %lu :%s" #define NUMERIC_STR_323 ":%s 323 %s :End of /LIST" @@ -106,7 +107,6 @@ #define NUMERIC_STR_331 ":%s 331 %s %s :No topic is set." #define NUMERIC_STR_332 ":%s 332 %s %s :%s" #define NUMERIC_STR_333 ":%s 333 %s %s %s %lld" -#define NUMERIC_STR_337 "%s :%s" #define NUMERIC_STR_338 "%s %s :actually using host" #define NUMERIC_STR_341 ":%s 341 %s %s %s" #define NUMERIC_STR_346 ":%s 346 %s %s %s %s %lu" diff --git a/include/numeric.h b/include/numeric.h index dcabff612..9132b7d1c 100644 --- a/include/numeric.h +++ b/include/numeric.h @@ -144,6 +144,7 @@ #define RPL_ENDOFWHOIS 318 #define RPL_WHOISCHANNELS 319 +#define RPL_WHOISSPECIAL 320 #define RPL_LISTSTART 321 #define RPL_LIST 322 @@ -159,7 +160,6 @@ #define RPL_NOTOPIC 331 #define RPL_TOPIC 332 #define RPL_TOPICWHOTIME 333 -#define RPL_WHOISTEXT 337 #define RPL_WHOISACTUALLY 338 #define RPL_INVITING 341 diff --git a/modules/m_whois.c b/modules/m_whois.c index 117acadb9..bb37a5ba3 100644 --- a/modules/m_whois.c +++ b/modules/m_whois.c @@ -370,7 +370,7 @@ single_whois(struct Client *source_p, struct Client *target_p, int operspy) { rb_inet_ntop_sock((struct sockaddr *)&ip4, buf, sizeof buf); - sendto_one_numeric(source_p, RPL_WHOISTEXT, + sendto_one_numeric(source_p, RPL_WHOISSPECIAL, "%s :Underlying IPv4 is %s", target_p->name, buf); } @@ -395,17 +395,17 @@ single_whois(struct Client *source_p, struct Client *target_p, int operspy) { if (hdata_showidle.approved == WHOIS_IDLE_HIDE) /* if the target has hidden their idle time, notify the source */ - sendto_one_numeric(source_p, RPL_WHOISTEXT, form_str(RPL_WHOISTEXT), target_p->name, "is hiding their idle time"); + sendto_one_numeric(source_p, RPL_WHOISSPECIAL, form_str(RPL_WHOISSPECIAL), target_p->name, "is hiding their idle time"); else /* if the target has hidden their idle time, notify the source */ - sendto_one_numeric(source_p, RPL_WHOISTEXT, form_str(RPL_WHOISTEXT), target_p->name, "is hiding their idle time, but you have auspex"); + sendto_one_numeric(source_p, RPL_WHOISSPECIAL, form_str(RPL_WHOISSPECIAL), target_p->name, "is hiding their idle time, but you have auspex"); } else if (hdata_showidle.approved == WHOIS_IDLE_HIDE) /* if the source has hidden their idle time, notify the source that they can't view others' idle times either */ - sendto_one_numeric(source_p, RPL_WHOISTEXT, form_str(RPL_WHOISTEXT), target_p->name, "has a hidden idle time because your own idle time is hidden"); + sendto_one_numeric(source_p, RPL_WHOISSPECIAL, form_str(RPL_WHOISSPECIAL), target_p->name, "has a hidden idle time because your own idle time is hidden"); else /* client has auspex to be able to see idle time, but make sure they know that's why they're seeing it */ - sendto_one_numeric(source_p, RPL_WHOISTEXT, form_str(RPL_WHOISTEXT), target_p->name, + sendto_one_numeric(source_p, RPL_WHOISSPECIAL, form_str(RPL_WHOISSPECIAL), target_p->name, "has a hidden idle time because your own idle time is hidden, but you have auspex"); } } From 59ea3c6753e80d051b20223cb327294cac985afe Mon Sep 17 00:00:00 2001 From: Ed Kellett Date: Sun, 5 Nov 2023 22:05:41 +0000 Subject: [PATCH 24/30] Delay rehashing until we're not processing events Fixes crash introduced by 0ab6dbbc651ddd1c26cb7baa6e6cf86890a4abd2. It's probably a regression since it defeats a system designed to stop this from happening, but I didn't dig through the history. rehash() closes listeners. If we happen to get a single epoll() result that wants to first rehash and then accept a connection, the epoll info will point to a freed rb_fde_t. Other selectors should have similar problems, but we didn't investigate that. rb_fde_ts are normally batched up and freed outside the event processing, but as of the above commit close_listeners() screws that up by closing pending FDs immediately in order to create new listeners. I think it might be a bit better to revert this behaviour and simply not close listeners if we are going to open new ones over them, but have opted for the smallest reasonable change I can think of. Helped-by: Eric Mertens --- ircd/s_conf.c | 35 ++++++++++++++++++++++++++--------- librb/include/rb_commio.h | 1 + librb/src/commio.c | 25 +++++++++++++++++++++++++ librb/src/export-syms.txt | 1 + 4 files changed, 53 insertions(+), 9 deletions(-) diff --git a/ircd/s_conf.c b/ircd/s_conf.c index cb86811ff..4c6b9908b 100644 --- a/ircd/s_conf.c +++ b/ircd/s_conf.c @@ -642,16 +642,18 @@ attach_conf(struct Client *client_p, struct ConfItem *aconf) return (0); } -/* - * rehash - * - * Actual REHASH service routine. Called with sig == 0 if it has been called - * as a result of an operator issuing this command, else assume it has been - * called as a result of the server receiving a HUP signal. - */ -bool -rehash(bool sig) +struct rehash_data { + bool sig; +}; + +static void +service_rehash(void *data_) { + struct rehash_data *data = data_; + bool sig = data->sig; + + free(data); + rb_dlink_node *n; hook_data_rehash hdata = { sig }; @@ -684,6 +686,21 @@ rehash(bool sig) privilegeset_cleanup_rehash(); call_hook(h_rehash, &hdata); +} + +/* + * rehash + * + * Called with sig == 0 if it has been called as a result of an operator + * issuing this command, else assume it has been called as a result of the + * server receiving a HUP signal. + */ +bool +rehash(bool sig) +{ + struct rehash_data *data = rb_malloc(sizeof *data); + data->sig = sig; + rb_defer(service_rehash, data); return false; } diff --git a/librb/include/rb_commio.h b/librb/include/rb_commio.h index dde5ca9c7..79d88461c 100644 --- a/librb/include/rb_commio.h +++ b/librb/include/rb_commio.h @@ -159,6 +159,7 @@ int rb_ignore_errno(int); void rb_setselect(rb_fde_t *, unsigned int type, PF * handler, void *client_data); void rb_init_netio(void); int rb_select(unsigned long); +void rb_defer(void (*)(void *), void *); int rb_fd_ssl(rb_fde_t *F); int rb_get_fd(rb_fde_t *F); const char *rb_get_ssl_strerror(rb_fde_t *F); diff --git a/librb/src/commio.c b/librb/src/commio.c index 490706f9e..47a042c5e 100644 --- a/librb/src/commio.c +++ b/librb/src/commio.c @@ -51,6 +51,14 @@ static rb_bh *fd_heap; static rb_dlink_list timeout_list; static rb_dlink_list closed_list; +struct defer +{ + rb_dlink_node node; + void (*fn)(void *); + void *data; +}; +static rb_dlink_list defer_list; + static struct ev_entry *rb_timeout_ev; @@ -2015,10 +2023,27 @@ rb_setselect(rb_fde_t *F, unsigned int type, PF * handler, void *client_data) setselect_handler(F, type, handler, client_data); } +void +rb_defer(void (*fn)(void *), void *data) +{ + struct defer *defer = rb_malloc(sizeof *defer); + defer->fn = fn; + defer->data = data; + rb_dlinkAdd(defer, &defer->node, &defer_list); +} + int rb_select(unsigned long timeout) { int ret = select_handler(timeout); + rb_dlink_node *ptr, *next; + RB_DLINK_FOREACH_SAFE(ptr, next, defer_list.head) + { + struct defer *defer = ptr->data; + defer->fn(defer->data); + rb_dlinkDelete(ptr, &defer_list); + rb_free(defer); + } rb_close_pending_fds(); return ret; } diff --git a/librb/src/export-syms.txt b/librb/src/export-syms.txt index e6ec7af53..6d9a0b899 100644 --- a/librb/src/export-syms.txt +++ b/librb/src/export-syms.txt @@ -27,6 +27,7 @@ rb_ctime rb_current_time rb_current_time_tv rb_date +rb_defer rb_destroy_patricia rb_dictionary_add rb_dictionary_create From f718aed2ae19c1a6ec224939865c6ca7fa834ee9 Mon Sep 17 00:00:00 2001 From: Eric Mertens Date: Sun, 5 Nov 2023 17:29:19 -0800 Subject: [PATCH 25/30] Replace free with rb_free --- ircd/s_conf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ircd/s_conf.c b/ircd/s_conf.c index 4c6b9908b..dadaf7a41 100644 --- a/ircd/s_conf.c +++ b/ircd/s_conf.c @@ -652,7 +652,7 @@ service_rehash(void *data_) struct rehash_data *data = data_; bool sig = data->sig; - free(data); + rb_free(data); rb_dlink_node *n; From c790520218b6da02e1c633e1ed57b310efe53ff4 Mon Sep 17 00:00:00 2001 From: Aaron Jones Date: Wed, 4 Oct 2023 18:48:42 +0000 Subject: [PATCH 26/30] modules/m_challenge.c: log correct mechanism I suspect this is a copy/paste omission when duplicating the code from modules/m_oper.c when it was created. --- modules/m_challenge.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/modules/m_challenge.c b/modules/m_challenge.c index ff390deee..75fd73061 100644 --- a/modules/m_challenge.c +++ b/modules/m_challenge.c @@ -188,7 +188,7 @@ m_challenge(struct MsgBuf *msgbuf_p, struct Client *client_p, struct Client *sou if(oper_p == NULL) { sendto_one_numeric(source_p, ERR_NOOPERHOST, form_str(ERR_NOOPERHOST)); - ilog(L_FOPER, "FAILED OPER (%s) by (%s!%s@%s) (%s)", + ilog(L_FOPER, "FAILED CHALLENGE (%s) by (%s!%s@%s) (%s)", source_p->user->opername, source_p->name, source_p->username, source_p->host, source_p->sockhost); @@ -206,7 +206,7 @@ m_challenge(struct MsgBuf *msgbuf_p, struct Client *client_p, struct Client *sou oper_up(source_p, oper_p); - ilog(L_OPERED, "OPER %s by %s!%s@%s (%s)", + ilog(L_OPERED, "CHALLENGE %s by %s!%s@%s (%s)", source_p->user->opername, source_p->name, source_p->username, source_p->host, source_p->sockhost); return; @@ -220,7 +220,7 @@ m_challenge(struct MsgBuf *msgbuf_p, struct Client *client_p, struct Client *sou if(oper_p == NULL) { sendto_one_numeric(source_p, ERR_NOOPERHOST, form_str(ERR_NOOPERHOST)); - ilog(L_FOPER, "FAILED OPER (%s) by (%s!%s@%s) (%s)", + ilog(L_FOPER, "FAILED CHALLENGE (%s) by (%s!%s@%s) (%s)", parv[1], source_p->name, source_p->username, source_p->host, source_p->sockhost); @@ -258,14 +258,14 @@ m_challenge(struct MsgBuf *msgbuf_p, struct Client *client_p, struct Client *sou if (source_p->certfp == NULL || rb_strcasecmp(source_p->certfp, oper_p->certfp)) { sendto_one_numeric(source_p, ERR_NOOPERHOST, form_str(ERR_NOOPERHOST)); - ilog(L_FOPER, "FAILED OPER (%s) by (%s!%s@%s) (%s) -- client certificate fingerprint mismatch", + ilog(L_FOPER, "FAILED CHALLENGE (%s) by (%s!%s@%s) (%s) -- client certificate fingerprint mismatch", parv[1], source_p->name, source_p->username, source_p->host, source_p->sockhost); if(ConfigFileEntry.failed_oper_notice) { sendto_realops_snomask(SNO_GENERAL, L_NETWIDE, - "Failed OPER attempt - client certificate fingerprint mismatch by %s (%s@%s)", + "Failed CHALLENGE attempt - client certificate fingerprint mismatch by %s (%s@%s)", source_p->name, source_p->username, source_p->host); } return; From 3b24363e9174d3c07c863ff39f794383f377101d Mon Sep 17 00:00:00 2001 From: Aaron Jones Date: Wed, 4 Oct 2023 18:52:47 +0000 Subject: [PATCH 27/30] modules/m_challenge.c: give a better error message for failure to find o:line The snotice sent to other opers can be misleading. For example, it will say host mismatch even if the host is correct but the username is wrong, or if the oper name given does not exist in the configuration. --- modules/m_challenge.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/m_challenge.c b/modules/m_challenge.c index 75fd73061..6ea23e943 100644 --- a/modules/m_challenge.c +++ b/modules/m_challenge.c @@ -226,8 +226,8 @@ m_challenge(struct MsgBuf *msgbuf_p, struct Client *client_p, struct Client *sou if(ConfigFileEntry.failed_oper_notice) sendto_realops_snomask(SNO_GENERAL, L_NETWIDE, - "Failed CHALLENGE attempt - host mismatch by %s (%s@%s)", - source_p->name, source_p->username, source_p->host); + "Failed CHALLENGE attempt - user@host mismatch or no operator block for %s by %s (%s@%s)", + parv[1], source_p->name, source_p->username, source_p->host); return; } From fd241b5fc86bdc7bf8213322c6c777b8e2146253 Mon Sep 17 00:00:00 2001 From: Aaron Jones Date: Wed, 4 Oct 2023 18:54:05 +0000 Subject: [PATCH 28/30] modules/m_oper.c: give a better error message for failure to find o:line The snotice sent to other opers can be misleading. For example, it will say host mismatch even if the host is correct but the username is wrong, or if the oper name given does not exist in the configuration. --- modules/m_oper.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/m_oper.c b/modules/m_oper.c index 4c734f7c9..58fb9eaa1 100644 --- a/modules/m_oper.c +++ b/modules/m_oper.c @@ -107,8 +107,8 @@ m_oper(struct MsgBuf *msgbuf_p, struct Client *client_p, struct Client *source_p if(ConfigFileEntry.failed_oper_notice) { sendto_realops_snomask(SNO_GENERAL, L_NETWIDE, - "Failed OPER attempt - host mismatch by %s (%s@%s)", - source_p->name, source_p->username, source_p->host); + "Failed OPER attempt - user@host mismatch or no operator block for %s by %s (%s@%s)", + name, source_p->name, source_p->username, source_p->host); } return; From c1b01bf5ec318463ab076ac2d7c0a4b02631f745 Mon Sep 17 00:00:00 2001 From: Doug Freed Date: Sun, 3 Sep 2023 23:19:00 +0000 Subject: [PATCH 29/30] client: refactor del_all_accepts to allow skipping own accept list This allows reusing this function for other uses that just need to remove this client from others' accept lists on nick change and not have duplicates of this code everywhere --- include/client.h | 2 +- ircd/client.c | 16 ++++++++++++---- ircd/s_user.c | 2 +- modules/core/m_nick.c | 17 +---------------- modules/m_services.c | 5 ++++- 5 files changed, 19 insertions(+), 23 deletions(-) diff --git a/include/client.h b/include/client.h index 2d61d5f9b..bf7ca79b5 100644 --- a/include/client.h +++ b/include/client.h @@ -603,7 +603,7 @@ extern struct Client *find_named_person(const char *); extern struct Client *next_client(struct Client *, const char *); #define accept_message(s, t) ((s) == (t) || (rb_dlinkFind((s), &((t)->localClient->allow_list)))) -extern void del_all_accepts(struct Client *client_p); +extern void del_all_accepts(struct Client *client_p, bool self_too); extern void dead_link(struct Client *client_p, int sendqex); extern int show_ip(struct Client *source_p, struct Client *target_p); diff --git a/ircd/client.c b/ircd/client.c index a06fda58d..9482b37c5 100644 --- a/ircd/client.c +++ b/ircd/client.c @@ -1385,7 +1385,7 @@ exit_generic_client(struct Client *client_p, struct Client *source_p, struct Cli } /* Clean up allow lists */ - del_all_accepts(source_p); + del_all_accepts(source_p, true); whowas_add_history(source_p, 0); whowas_off_history(source_p); @@ -1793,19 +1793,19 @@ count_remote_client_memory(size_t * count, size_t * remote_client_memory_used) /* * del_all_accepts * - * inputs - pointer to exiting client + * inputs - pointer to exiting client, flag to include own allow_list * output - NONE * side effects - Walk through given clients allow_list and on_allow_list * remove all references to this client */ void -del_all_accepts(struct Client *client_p) +del_all_accepts(struct Client *client_p, bool self_too) { rb_dlink_node *ptr; rb_dlink_node *next_ptr; struct Client *target_p; - if(MyClient(client_p) && client_p->localClient->allow_list.head) + if(self_too && MyClient(client_p) && client_p->localClient->allow_list.head) { /* clear this clients accept list, and remove them from * everyones on_accept_list @@ -1813,6 +1813,7 @@ del_all_accepts(struct Client *client_p) RB_DLINK_FOREACH_SAFE(ptr, next_ptr, client_p->localClient->allow_list.head) { target_p = ptr->data; + rb_dlinkFindDestroy(client_p, &target_p->on_allow_list); rb_dlinkDestroy(ptr, &client_p->localClient->allow_list); } @@ -1822,6 +1823,13 @@ del_all_accepts(struct Client *client_p) RB_DLINK_FOREACH_SAFE(ptr, next_ptr, client_p->on_allow_list.head) { target_p = ptr->data; + + /* If we're not doing our own, we're doing this because of a nick change. + * Skip those that would see the nick change anyway + */ + if(!self_too && has_common_channel(client_p, target_p)) + continue; + rb_dlinkFindDestroy(client_p, &target_p->localClient->allow_list); rb_dlinkDestroy(ptr, &client_p->on_allow_list); } diff --git a/ircd/s_user.c b/ircd/s_user.c index 48acff5ba..94fee03c2 100644 --- a/ircd/s_user.c +++ b/ircd/s_user.c @@ -1725,7 +1725,7 @@ change_nick_user_host(struct Client *target_p, const char *nick, const char *use if(changed) { monitor_signon(target_p); - del_all_accepts(target_p); + del_all_accepts(target_p, false); } } diff --git a/modules/core/m_nick.c b/modules/core/m_nick.c index 890d0c2b3..dcdd48371 100644 --- a/modules/core/m_nick.c +++ b/modules/core/m_nick.c @@ -619,8 +619,6 @@ static void change_local_nick(struct Client *client_p, struct Client *source_p, char *nick, int dosend) { - struct Client *target_p; - rb_dlink_node *ptr, *next_ptr; struct Channel *chptr; char note[NICKLEN + 10]; int samenick; @@ -704,20 +702,7 @@ change_local_nick(struct Client *client_p, struct Client *source_p, /* Make sure everyone that has this client on its accept list * loses that reference. */ - /* we used to call del_all_accepts() here, but theres no real reason - * to clear a clients own list of accepted clients. So just remove - * them from everyone elses list --anfl - */ - RB_DLINK_FOREACH_SAFE(ptr, next_ptr, source_p->on_allow_list.head) - { - target_p = ptr->data; - - if (!has_common_channel(source_p, target_p)) - { - rb_dlinkFindDestroy(source_p, &target_p->localClient->allow_list); - rb_dlinkDestroy(ptr, &source_p->on_allow_list); - } - } + del_all_accepts(source_p, false); snprintf(note, sizeof(note), "Nick: %s", nick); rb_note(client_p->localClient->F, note); diff --git a/modules/m_services.c b/modules/m_services.c index 0127887ef..bf378600b 100644 --- a/modules/m_services.c +++ b/modules/m_services.c @@ -280,7 +280,10 @@ me_rsfnc(struct MsgBuf *msgbuf_p, struct Client *client_p, struct Client *source monitor_signon(target_p); - del_all_accepts(target_p); + /* Make sure everyone that has this client on its accept list + * loses that reference. + */ + del_all_accepts(target_p, false); snprintf(note, sizeof(note), "Nick: %s", target_p->name); rb_note(target_p->localClient->F, note); From 642c73dddb3ce7c9ae4824fa95ea9ebad41eb848 Mon Sep 17 00:00:00 2001 From: Aaron Jones Date: Thu, 15 Jun 2023 22:32:12 +0000 Subject: [PATCH 30/30] ircd/listener: return a fatal TLS alert for early rejected TLS clients This is in furtherance of commit 3fdf26aa19628d5e12a3 which added functionality to reply with a TLS record layer alert for D-Lined TLS clients. It turns out that there are other plaintext error messages in this same function that should receive the same treatment. Also move another error string to a variable and use a compile-time optimised-out strlen for it too, to use the same approach as an existing error string. Finally, use a different alert (internal_error) for the case where IRCd is simply unable to accept more connections. --- include/reject.h | 2 +- ircd/listener.c | 24 +++++++++++++++++++----- ircd/reject.c | 41 +++++++++++++++++++++++++++-------------- 3 files changed, 47 insertions(+), 20 deletions(-) diff --git a/include/reject.h b/include/reject.h index 2ca378fe8..5ed023427 100644 --- a/include/reject.h +++ b/include/reject.h @@ -28,7 +28,7 @@ #define DELAYED_EXIT_TIME 10 void init_reject(void); -int check_reject(rb_fde_t *F, struct sockaddr *addr); +int check_reject(rb_fde_t *F, struct sockaddr *addr, bool ssl); void add_reject(struct Client *, const char *mask1, const char *mask2, struct ConfItem *aconf, const char *reason); int is_reject_ip(struct sockaddr *addr); void flush_reject(void); diff --git a/ircd/listener.c b/ircd/listener.c index 80947af85..02b6dabdc 100644 --- a/ircd/listener.c +++ b/ircd/listener.c @@ -581,13 +581,19 @@ accept_precallback(rb_fde_t *F, struct sockaddr *addr, rb_socklen_t addrlen, voi static time_t last_oper_notice = 0; int len; + static const char *allinuse = "ERROR :All connections in use\r\n"; static const char *toofast = "ERROR :Reconnecting too fast, throttled.\r\n"; - static const unsigned char sslerrcode[] = { + static const unsigned char ssldeniederrcode[] = { // SSLv3.0 Fatal Alert: Access Denied 0x15, 0x03, 0x00, 0x00, 0x02, 0x02, 0x31 }; + static const unsigned char sslinternalerrcode[] = { + // SSLv3.0 Fatal Alert: Internal Error + 0x15, 0x03, 0x00, 0x00, 0x02, 0x02, 0x50 + }; + if(listener->ssl && (!ircd_ssl_ok || !get_ssld_count())) { rb_close(F); @@ -608,7 +614,11 @@ accept_precallback(rb_fde_t *F, struct sockaddr *addr, rb_socklen_t addrlen, voi last_oper_notice = rb_current_time(); } - rb_write(F, "ERROR :All connections in use\r\n", 31); + if(listener->ssl) + rb_write(F, sslinternalerrcode, sizeof(sslinternalerrcode)); + else + rb_write(F, allinuse, strlen(allinuse)); + rb_close(F); return 0; } @@ -625,7 +635,7 @@ accept_precallback(rb_fde_t *F, struct sockaddr *addr, rb_socklen_t addrlen, voi if(listener->ssl) { - rb_write(F, sslerrcode, sizeof(sslerrcode)); + rb_write(F, ssldeniederrcode, sizeof(ssldeniederrcode)); } else if(ConfigFileEntry.dline_with_reason) { @@ -648,7 +658,7 @@ accept_precallback(rb_fde_t *F, struct sockaddr *addr, rb_socklen_t addrlen, voi return 0; } - if(check_reject(F, addr)) { + if(check_reject(F, addr, listener->ssl)) { /* Reject the connection without closing the socket * because it is now on the delay_exit list. */ return 0; @@ -656,7 +666,11 @@ accept_precallback(rb_fde_t *F, struct sockaddr *addr, rb_socklen_t addrlen, voi if(throttle_add(addr)) { - rb_write(F, toofast, strlen(toofast)); + if(listener->ssl) + rb_write(F, ssldeniederrcode, sizeof(ssldeniederrcode)); + else + rb_write(F, toofast, strlen(toofast)); + rb_close(F); return 0; } diff --git a/ircd/reject.c b/ircd/reject.c index 801af5ffc..767dc0873 100644 --- a/ircd/reject.c +++ b/ircd/reject.c @@ -58,6 +58,7 @@ typedef struct _delay_data rb_fde_t *F; struct ConfItem *aconf; const char *reason; + bool ssl; } delay_t; typedef struct _throttle @@ -92,28 +93,39 @@ reject_exit(void *unused) delay_t *ddata; static const char *errbuf = "ERROR :Closing Link: (*** Banned (cache))\r\n"; + static const unsigned char ssldeniederrcode[] = { + // SSLv3.0 Fatal Alert: Access Denied + 0x15, 0x03, 0x00, 0x00, 0x02, 0x02, 0x31 + }; + RB_DLINK_FOREACH_SAFE(ptr, ptr_next, delay_exit.head) { ddata = ptr->data; - *dynamic_reason = '\0'; - - if (ddata->aconf) + if (ddata->ssl) { - snprintf(dynamic_reason, sizeof dynamic_reason, form_str(ERR_YOUREBANNEDCREEP) "\r\n", - me.name, "*", get_user_ban_reason(ddata->aconf)); - rb_write(ddata->F, dynamic_reason, strlen(dynamic_reason)); - - deref_conf(ddata->aconf); + rb_write(ddata->F, ssldeniederrcode, sizeof(ssldeniederrcode)); } - else if (ddata->reason) + else { - snprintf(dynamic_reason, sizeof dynamic_reason, ":%s 465 %s :%s\r\n", - me.name, "*", ddata->reason); - rb_write(ddata->F, dynamic_reason, strlen(dynamic_reason)); + *dynamic_reason = '\0'; + + if (ddata->aconf) + snprintf(dynamic_reason, sizeof dynamic_reason, form_str(ERR_YOUREBANNEDCREEP) "\r\n", + me.name, "*", get_user_ban_reason(ddata->aconf)); + else if (ddata->reason) + snprintf(dynamic_reason, sizeof dynamic_reason, ":%s 465 %s :%s\r\n", + me.name, "*", ddata->reason); + + if (*dynamic_reason) + rb_write(ddata->F, dynamic_reason, strlen(dynamic_reason)); + + rb_write(ddata->F, errbuf, strlen(errbuf)); } - rb_write(ddata->F, errbuf, strlen(errbuf)); + if (ddata->aconf) + deref_conf(ddata->aconf); + rb_close(ddata->F); rb_free(ddata); } @@ -228,7 +240,7 @@ add_reject(struct Client *client_p, const char *mask1, const char *mask2, struct } int -check_reject(rb_fde_t *F, struct sockaddr *addr) +check_reject(rb_fde_t *F, struct sockaddr *addr, bool ssl) { rb_patricia_node_t *pnode; reject_t *rdata; @@ -276,6 +288,7 @@ check_reject(rb_fde_t *F, struct sockaddr *addr) ddata->reason = NULL; } ddata->F = F; + ddata->ssl = ssl; rb_dlinkAdd(ddata, &ddata->node, &delay_exit); return 1; }