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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 30 additions & 27 deletions doc/example.conf
Original file line number Diff line number Diff line change
Expand Up @@ -889,33 +889,35 @@ features
# "MAXBANS"="30";
# "MAXSILES"="15";
# "HANGONGOODLINK"="300";
# "HANGONRETRYDELAY" = "10";
# "CONNECTTIMEOUT" = "90";
# "PINGFREQUENCY" = "120";
# "CONNECTFREQUENCY" = "600";
# "DEFAULTMAXSENDQLENGTH" = "40000";
# "GLINEMAXUSERCOUNT" = "20";
# "MPATH" = "ircd.motd";
# "RPATH" = "remote.motd";
# "PPATH" = "ircd.pid";
# "TOS_SERVER" = "0x08";
# "TOS_CLIENT" = "0x08";
# "POLLS_PER_LOOP" = "200";
# "IRCD_RES_TIMEOUT" = "4";
# "IRCD_RES_RETRIES" = "2";
# "AUTH_TIMEOUT" = "9";
# "IPCHECK_CLONE_LIMIT" = "4";
# "IPCHECK_CLONE_PERIOD" = "40";
# "IPCHECK_CLONE_DELAY" = "600";
# "CHANNELLEN" = "200";
# "CONFIG_OPERCMDS" = "FALSE";
# "OPLEVELS" = "FALSE";
# "ZANNELS" = "FALSE";
# "LOCAL_CHANNELS" = "TRUE";
# "STRICT_USERNAME" = "FALSE";
# "TOPIC_BURST" = "TRUE"
# "AWAY_BURST" = "TRUE";
# "ANNOUNCE_INVITES" = "FALSE";
# "HANGONRETRYDELAY" = "10";
# "CONNECTTIMEOUT" = "90";
# "PINGFREQUENCY" = "120";
# "CONNECTFREQUENCY" = "600";
# "DEFAULTMAXSENDQLENGTH" = "40000";
# "GLINEMAXUSERCOUNT" = "20";
# "DISABLE_GLINES" = "FALSE";
# "DISABLE_SLINES" = "FALSE";
# "MPATH" = "ircd.motd";
# "RPATH" = "remote.motd";
# "PPATH" = "ircd.pid";
# "TOS_SERVER" = "0x08";
# "TOS_CLIENT" = "0x08";
# "POLLS_PER_LOOP" = "200";
# "IRCD_RES_TIMEOUT" = "4";
# "IRCD_RES_RETRIES" = "2";
# "AUTH_TIMEOUT" = "9";
# "IPCHECK_CLONE_LIMIT" = "4";
# "IPCHECK_CLONE_PERIOD" = "40";
# "IPCHECK_CLONE_DELAY" = "600";
# "CHANNELLEN" = "200";
# "CONFIG_OPERCMDS" = "FALSE";
# "OPLEVELS" = "FALSE";
# "ZANNELS" = "FALSE";
# "LOCAL_CHANNELS" = "TRUE";
# "STRICT_USERNAME" = "FALSE";
# "TOPIC_BURST" = "TRUE"
# "AWAY_BURST" = "TRUE";
# "ANNOUNCE_INVITES" = "FALSE";
# "CAP_ACCOUNTNOTIFY" = "TRUE";
# "CAP_AWAYNOTIFY" = "TRUE";
# "CAP_CHGHOST" = "TRUE";
Expand Down Expand Up @@ -952,6 +954,7 @@ features
# "HIS_STATS_q" = "TRUE";
# "HIS_STATS_r" = "TRUE";
# "HIS_STATS_R" = "TRUE";
# "HIS_STATS_s" = "TRUE";
# "HIS_STATS_t" = "TRUE";
# "HIS_STATS_T" = "TRUE";
# "HIS_STATS_u" = "FALSE";
Expand Down
7 changes: 7 additions & 0 deletions include/client.h
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,7 @@ enum Flag
FLAG_ACCOUNT, /**< account name has been set */
FLAG_HIDDENHOST, /**< user's host is hidden */
FLAG_CAP302, /**< client supports IRCv3.2 */
FLAG_SPAMHOLD, /**< user is the sender or recipient of a message on hold */
FLAG_LAST_FLAG, /**< number of flags */
FLAG_LOCAL_UMODES = FLAG_LOCOP, /**< First local mode flag */
FLAG_GLOBAL_UMODES = FLAG_OPER /**< First global mode flag */
Expand Down Expand Up @@ -614,6 +615,8 @@ struct Client {
#define IsHiddenHost(x) HasFlag(x, FLAG_HIDDENHOST)
/** Return non-zero if the client has an active PING request. */
#define IsPingSent(x) HasFlag(x, FLAG_PINGSENT)
/** Return non-zero if the client is the sender or recipient of a message on hold (spamfilter) */
#define IsSpamHold(x) HasFlag(x, FLAG_SPAMHOLD)

/** Return non-zero if the client has operator or server privileges. */
#define IsPrivileged(x) (IsAnOper(x) || IsServer(x))
Expand Down Expand Up @@ -660,6 +663,8 @@ struct Client {
#define SetHiddenHost(x) SetFlag(x, FLAG_HIDDENHOST)
/** Mark a client as having a pending PING. */
#define SetPingSent(x) SetFlag(x, FLAG_PINGSENT)
/** Mark a client as being the sender or recipient of a message on hold (spamfilter). */
#define SetSpamHold(x) SetFlag(x, FLAG_SPAMHOLD)

/** Return non-zero if \a sptr sees \a acptr as an operator. */
#define SeeOper(sptr,acptr) (IsAnOper(acptr) && (HasPriv(acptr, PRIV_DISPLAY) \
Expand Down Expand Up @@ -695,6 +700,8 @@ struct Client {
#define ClearPingSent(x) ClrFlag(x, FLAG_PINGSENT)
/** Clear the client's HUB flag. */
#define ClearHub(x) ClrFlag(x, FLAG_HUB)
/** Clear the client's spam hold flag. */
#define ClearSpamHold(x) ClrFlag(x, FLAG_SPAMHOLD)

/* free flags */
#define FREEFLAG_SOCKET 0x0001 /**< socket needs to be freed */
Expand Down
1 change: 1 addition & 0 deletions include/handlers.h
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,7 @@ extern int ms_rpong(struct Client*, struct Client*, int, char*[]);
extern int ms_server(struct Client*, struct Client*, int, char*[]);
extern int ms_settime(struct Client*, struct Client*, int, char*[]);
extern int ms_silence(struct Client*, struct Client*, int, char*[]);
extern int ms_sline(struct Client*, struct Client*, int, char*[]);
extern int ms_squit(struct Client*, struct Client*, int, char*[]);
extern int ms_stats(struct Client*, struct Client*, int, char*[]);
extern int ms_topic(struct Client*, struct Client*, int, char*[]);
Expand Down
5 changes: 4 additions & 1 deletion include/ircd_defs.h
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,10 @@
#define TOPICLEN 160
/** Maximum length for away messages.
*/
#define AWAYLEN 160
#define AWAYLEN 160
/** Maximum length of S:line patterns.
*/
#define SLINELEN 470
/** Exactly long enough to hold one protocol message (RFC 1459)
* including the line termination (\\r\\n). DO NOT CHANGE THIS!!!!
*/
Expand Down
2 changes: 2 additions & 0 deletions include/ircd_features.h
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ enum Feature {
FEAT_TOPIC_BURST,
FEAT_AWAY_BURST,
FEAT_DISABLE_GLINES,
FEAT_DISABLE_SLINES,
FEAT_JOIN_TARGET,

/* features that probably should not be touched */
Expand Down Expand Up @@ -142,6 +143,7 @@ enum Feature {
FEAT_HIS_STATS_q,
FEAT_HIS_STATS_r,
FEAT_HIS_STATS_R,
FEAT_HIS_STATS_s,
FEAT_HIS_STATS_S,
FEAT_HIS_STATS_t,
FEAT_HIS_STATS_T,
Expand Down
5 changes: 5 additions & 0 deletions include/ircd_netconf.h
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,11 @@ enum NetConf {
NETCONF_SASL_MECHANISMS,
NETCONF_SASL_TIMEOUT,

/* S:line related settings */
NETCONF_SLINE_SERVER,
NETCONF_SLINE_HOLD_TIMEOUT,
NETCONF_SLINE_HOLD_TIMEOUT_BLOCK,

NETCONF_LAST_NC
};

Expand Down
4 changes: 4 additions & 0 deletions include/msg.h
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,10 @@ struct Client;
#define TOK_GLINE "GL"
#define CMD_GLINE MSG_GLINE, TOK_GLINE

#define MSG_SLINE "SLINE" /* SLIN */
#define TOK_SLINE "SL"
#define CMD_SLINE MSG_SLINE, TOK_SLINE

#define MSG_BURST "BURST" /* BURS */
#define TOK_BURST "B"
#define CMD_BURST MSG_BURST, TOK_BURST
Expand Down
2 changes: 1 addition & 1 deletion include/numeric.h
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ extern const struct Numeric* get_error_numeric(int err);
#define RPL_STATSENGINE 237 /* Undernet engine name */
#define RPL_STATSFLINE 238 /* Undernet feature lines */
/* RPL_STATSIAUTH 239 IRCnet extension */
/* RPL_STATSVLINE 240 IRCnet extension */
#define RPL_STATSSLINE 240 /* Undernet extension */
/* RPL_STATSXLINE 240 austnet */
#define RPL_STATSLLINE 241 /* Undernet dynamicly loaded modules */
#define RPL_STATSUPTIME 242
Expand Down
97 changes: 97 additions & 0 deletions include/sline.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
#ifndef INCLUDED_sline_h
#define INCLUDED_sline_h
/*
* IRC - Internet Relay Chat, include/sline.h
* Copyright (C) 2025 MrIron <mriron@undernet.org>
*
* 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, 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., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

#ifndef INCLUDED_sys_types_h
#include <sys/types.h>
#define INCLUDED_sys_types_h
#endif

#include <stdint.h>
#include <regex.h>

struct Client;
struct StatDesc;
struct Channel;

/** Forward declaration for hold queue entry */
struct HoldQueueEntry;

/* Regex capture group limits */
#define SLINE_MAX_CAPTURES 16 /**< Maximum regex capture groups supported (including full match).
If an S-line regex pattern contains more than 15 capture groups,
only the first 15 will be captured and passed to spam filters. */

/* Message type flags */
#define SLINE_PRIVATE 0x0001 /**< Match private messages. */
#define SLINE_CHANNEL 0x0002 /**< Match channel messages. */
#define SLINE_PART 0x0004 /**< Match partial messages. */
#define SLINE_QUIT 0x0008 /**< Match quit messages. */

#define SLINE_ALL (SLINE_PRIVATE | SLINE_CHANNEL | SLINE_PART | SLINE_QUIT)

/** S:line flags */
#define SLINE_ACTIVE 0x0001 /**< S-line is active. */
#define SLINE_INVALID 0x0002 /**< S-line regex failed to compile. */

/* Flags to track update actions. */
#define SLINE_EXPIRE 0x0004 /**< S-line expire update. */
#define SLINE_MSGTYPE 0x0008 /**< S-line message type update. */
#define SLINE_STATE 0x0010 /**< S-line state update. */

/** Value to hold a set of message type bits. */
typedef unsigned short sl_msgtype_t;

/** Value to hold a set of S-line state bits. */
typedef unsigned short sl_flagtype_t;

/** Description of an S-line. */
struct Sline {
struct Sline *sl_next; /**< Next S-line in linked list. */
struct Sline **sl_prev_p; /**< Previous pointer to this S-line. */
char *sl_pattern; /**< Regex pattern to match against messages. */
time_t sl_lastmod; /**< When the S-line was last modified. */
time_t sl_expire; /**< When the S-line will expire. */
sl_msgtype_t sl_msgtype; /**< Message type to match against. */
sl_flagtype_t sl_flags; /**< S-line status flags. */
uint64_t sl_count; /**< Number of times this S-line has matched. */
regex_t sl_regex; /**< Precompiled regex for this pattern. */
};

extern int sline_add(struct Client *cptr, struct Client *sptr, char *pattern,
time_t lastmod, time_t expire, sl_msgtype_t msgtype, sl_flagtype_t flags);
extern void sline_modify(struct Client *sptr, struct Sline *sline, time_t lastmod,
time_t expire, sl_msgtype_t msgtype, sl_flagtype_t flags, unsigned int updates);

extern struct Sline *sline_find(char *pattern);
extern void sline_stats(struct Client *sptr, const struct StatDesc *sd,
char *param);
extern void sline_send_meminfo(struct Client* sptr);
extern void sline_burst(struct Client *cptr);
extern int sline_check_pattern_bool(const char *text, sl_msgtype_t msg_type);
extern int sline_check_privmsg(struct Client *sender, struct Client *recipient, const char *text, const char* cmd_type);
extern int sline_check_chanmsg(struct Client *sender, struct Channel *channel, const char *text, const char* cmd_type);
extern void sline_cleanup_client(struct Client *cptr);
extern void sline_cleanup_channel(struct Channel *chptr);
extern int sline_xreply_handler(struct Client *sptr, const char *token, const char *reply);
extern void sline_init(void);
extern const char *sline_flags_to_string(sl_msgtype_t msgtype);

#endif /* INCLUDED_sline_h */
2 changes: 2 additions & 0 deletions ircd/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ ircd_SOURCES = \
m_set.c \
m_settime.c \
m_silence.c \
m_sline.c \
m_squit.c \
m_stats.c \
m_time.c \
Expand Down Expand Up @@ -137,6 +138,7 @@ ircd_SOURCES = \
s_user.c \
sasl.c \
send.c \
sline.c \
uping.c \
userload.c \
whocmds.c \
Expand Down
7 changes: 7 additions & 0 deletions ircd/channel.c
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
#include "s_misc.h"
#include "s_user.h"
#include "send.h"
#include "sline.h"
#include "struct.h"
#include "sys.h"
#include "whowas.h"
Expand Down Expand Up @@ -330,6 +331,12 @@ int destruct_channel(struct Channel* chptr)
next = ban->next;
free_ban(ban);
}

/*
* Clean up S-line hold queue entries for this channel
*/
sline_cleanup_channel(chptr);

if (chptr->prev)
chptr->prev->next = chptr->next;
else
Expand Down
2 changes: 2 additions & 0 deletions ircd/ircd.c
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
#include "s_stats.h"
#include "sasl.h"
#include "send.h"
#include "sline.h"
#include "sys.h"
#include "uping.h"
#include "userload.h"
Expand Down Expand Up @@ -733,6 +734,7 @@ int main(int argc, char **argv) {
stats_init();

IPcheck_init();
sline_init();
timer_add(timer_init(&connect_timer), try_connections, 0, TT_RELATIVE, 1);
timer_add(timer_init(&ping_timer), check_pings, 0, TT_RELATIVE, 1);
timer_add(timer_init(&destruct_event_timer), exec_expired_destruct_events, 0, TT_PERIODIC, 60);
Expand Down
2 changes: 2 additions & 0 deletions ircd/ircd_features.c
Original file line number Diff line number Diff line change
Expand Up @@ -326,6 +326,7 @@ static struct FeatureDesc {
F_B(TOPIC_BURST, 0, 1, 0),
F_B(AWAY_BURST, 0, 1, 0),
F_B(DISABLE_GLINES, 0, 0, 0),
F_B(DISABLE_SLINES, 0, 0, 0),
F_B(JOIN_TARGET, 0, 0, 0),

/* features that probably should not be touched */
Expand Down Expand Up @@ -407,6 +408,7 @@ static struct FeatureDesc {
F_B(HIS_STATS_q, 0, 1, 0),
F_B(HIS_STATS_r, 0, 1, 0),
F_B(HIS_STATS_R, 0, 1, 0),
F_B(HIS_STATS_s, 0, 1, 0),
F_B(HIS_STATS_S, 0, 1, 0),
F_B(HIS_STATS_t, 0, 1, 0),
F_B(HIS_STATS_T, 0, 1, 0),
Expand Down
7 changes: 5 additions & 2 deletions ircd/ircd_netconf.c
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,11 @@ static struct NetConfDesc {
NC_S(SASL_MECHANISMS, "sasl.mechanisms", ""),
NC_I(SASL_TIMEOUT, "sasl.timeout", 30),

/* S:line related settings */
NC_S(SLINE_SERVER, "sline.server", ""),
NC_I(SLINE_HOLD_TIMEOUT, "sline.hold_timeout", 60),
NC_B(SLINE_HOLD_TIMEOUT_BLOCK, "sline.hold_timeout_block", 1),

{ NETCONF_LAST_NC, 0, 0, 0, 0, 0, 0 } /* sentinel */
};

Expand Down Expand Up @@ -308,12 +313,10 @@ void config_stats(struct Client *sptr, const struct StatDesc *sd, char *param)
static struct NetConfDesc *netconf_find(enum NetConf key)
{
int i;

for (i = 0; netconf_descs[i].netconf != NETCONF_LAST_NC; i++) {
if (netconf_descs[i].netconf == key)
return &netconf_descs[i];
}

return NULL;
}

Expand Down
Loading
Loading