p - Shows opers connected and their idle times
* q - Shows resv'd nicks and channels
* r - Shows resource usage by ircd
+* S - Shows session exceptions
* s - Shows configured service {} blocks
* T - Shows configured motd {} blocks
* t - Shows generic server stats
#define SHARED_LOCOPS 0x0040
#define SHARED_DLINE 0x0080
#define SHARED_UNDLINE 0x0100
-//#define CONF_FLAGS_IN_DATABASE 0x4000
+
#define SHARED_ALL (SHARED_KLINE | SHARED_UNKLINE |\
SHARED_XLINE | SHARED_UNXLINE |\
SHARED_RESV | SHARED_UNRESV |\
CONF_SERVICE = 1 << 11,
CONF_OPER = 1 << 12,
CONF_DNSBL = 1 << 13,
+ CONF_SESSION = 1 << 14,
};
enum
m_sasl.la \
m_set.la \
m_services.la \
+ m_session.la \
m_shedding.la \
m_snotice.la \
m_stats.la \
m_sasl_la_LDFLAGS = $(MODULE_FLAGS)
m_set_la_LDFLAGS = $(MODULE_FLAGS)
m_services_la_LDFLAGS = $(MODULE_FLAGS)
+m_session_la_LDFLAGS = $(MODULE_FLAGS)
m_shedding_la_LDFLAGS = $(MODULE_FLAGS)
m_snotice_la_LDFLAGS = $(MODULE_FLAGS)
m_stats_la_LDFLAGS = $(MODULE_FLAGS)
m_sasl_la_SOURCES = m_sasl.c
m_set_la_SOURCES = m_set.c
m_services_la_SOURCES = m_services.c
+m_session_la_SOURCES = m_session.c
m_shedding_la_SOURCES = m_shedding.c
m_snotice_la_SOURCES = m_snotice.c
m_stats_la_SOURCES = m_stats.c
--- /dev/null
+/*
+ * ircd-hybrid: an advanced Internet Relay Chat Daemon(ircd).
+ *
+ * Copyright (C) 2016 Adam <Adam@anope.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 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 "client.h"
+#include "parse.h"
+#include "modules.h"
+#include "conf.h"
+#include "memory.h"
+#include "hostmask.h"
+#include "irc_string.h"
+#include "send.h"
+#include "s_misc.h"
+
+/* SESSION mask limit creator created expires reason */
+static void
+me_session(struct Client *client_p, struct Client *source_p, int parc, char *parv[])
+{
+ const char *mask = parv[1],
+ *limitstr = parv[2],
+ *creator = parv[3],
+ *createdstr = parv[4],
+ *expiresstr = parv[5],
+ *reason = parv[parc - 1];
+
+ if (!HasFlag(source_p, FLAGS_SERVICE))
+ return;
+
+ struct irc_ssaddr addr;
+ int bits;
+
+ int masktype = parse_netmask(mask, &addr, &bits);
+
+ struct MaskLookup lookup = {
+ .name = mask,
+ .fam = masktype != HM_HOST ? addr.ss.ss_family : 0,
+ .addr = masktype != HM_HOST ? &addr : NULL,
+ .cmpfunc = irccmp
+ };
+
+ struct MaskItem *session = find_conf_by_address(CONF_SESSION, &lookup);
+
+ if (session != NULL)
+ {
+ delete_one_address_conf(session->host, session);
+ }
+
+ session = conf_make(CONF_SESSION);
+
+ session->count = atol(limitstr);
+ session->user = xstrdup(creator);
+ session->host = xstrdup(mask);
+ session->setat = atol(createdstr);
+ session->until = atol(expiresstr);
+ session->reason = xstrdup(reason);
+
+ SetConfDatabase(session);
+
+ add_conf_by_address(CONF_SESSION, session);
+
+ sendto_snomask(SNO_DEBUG, L_ALL,
+ "%s added session exception for %s, limit %d, to expire on %s",
+ source_p->name, session->host, session->count, session->until != 0 ? myctime(session->until) : "never");
+}
+
+/* UNSESSION mask */
+static void
+me_unsession(struct Client *client_p, struct Client *source_p, int parc, char *parv[])
+{
+ const char *mask = parv[1];
+
+ if (!HasFlag(source_p, FLAGS_SERVICE))
+ return;
+
+ struct irc_ssaddr addr;
+ int bits;
+
+ int masktype = parse_netmask(mask, &addr, &bits);
+
+ struct MaskLookup lookup = {
+ .name = mask,
+ .fam = masktype != HM_HOST ? addr.ss.ss_family : 0,
+ .addr = masktype != HM_HOST ? &addr : NULL,
+ .cmpfunc = irccmp
+ };
+
+ struct MaskItem *session = find_conf_by_address(CONF_SESSION, &lookup);
+
+ if (session == NULL)
+ return;
+
+ sendto_snomask(SNO_DEBUG, L_ALL,
+ "%s removed session exception for %s",
+ source_p->name, session->host);
+
+ delete_one_address_conf(session->host, session);
+}
+
+static struct Message session_msgtab = {
+ .cmd = "SESSION",
+ .args_min = 6,
+ .args_max = MAXPARA,
+ .handlers[UNREGISTERED_HANDLER] = m_ignore,
+ .handlers[CLIENT_HANDLER] = m_ignore,
+ .handlers[SERVER_HANDLER] = m_ignore,
+ .handlers[ENCAP_HANDLER] = me_session,
+ .handlers[OPER_HANDLER] = m_ignore,
+};
+
+static struct Message unsession_msgtab = {
+ .cmd = "UNSESSION",
+ .args_min = 2,
+ .args_max = MAXPARA,
+ .handlers[UNREGISTERED_HANDLER] = m_ignore,
+ .handlers[CLIENT_HANDLER] = m_ignore,
+ .handlers[SERVER_HANDLER] = m_ignore,
+ .handlers[ENCAP_HANDLER] = me_unsession,
+ .handlers[OPER_HANDLER] = m_ignore,
+};
+
+static void
+module_init(void)
+{
+ mod_add_cmd(&session_msgtab);
+ mod_add_cmd(&unsession_msgtab);
+}
+
+static void
+module_exit(void)
+{
+ mod_del_cmd(&session_msgtab);
+ mod_del_cmd(&unsession_msgtab);
+}
+
+struct module module_entry = {
+ .version = "$Revision$",
+ .modinit = module_init,
+ .modexit = module_exit,
+};
+
report_confitem_types(source_p, CONF_SERVICE);
}
+static void
+stats_session(struct Client *source_p, int parc, char *parv[])
+{
+ for (int i = 0; i < ATABLE_SIZE; ++i)
+ {
+ dlink_node *ptr;
+
+ DLINK_FOREACH(ptr, atable[i].head)
+ {
+ struct AddressRec *arec = ptr->data;
+
+ if (arec->type != CONF_SESSION)
+ continue;
+
+ struct MaskItem *conf = arec->conf;
+
+ sendto_one(source_p, ":%s %d %s S :%s %d %s %ld %ld %s",
+ me.name, RPL_STATSDEBUG, source_p->name,
+ conf->host, conf->count, conf->user, conf->setat, conf->until, conf->reason);
+ }
+ }
+}
+
static void
stats_tstats(struct Client *source_p, int parc, char *parv[])
{
{ 'r', stats_usage, 1, 0 },
{ 'R', stats_usage, 1, 0 },
{ 's', stats_service, 1, 0 },
- { 'S', stats_service, 1, 0 },
+ { 'S', stats_session, 1, 0 },
{ 't', stats_tstats, 1, 0 },
{ 'T', motd_report, 1, 0 },
{ 'u', stats_uptime, 0, 0 },
{
struct ClassItem *class = NULL;
struct ip_entry *ip_found;
- int a_limit_reached = 0;
+ int conf_limit_reached = 0, host_limit_reached = 0;
unsigned int local = 0, global = 0;
assert(conf->class);
userhost_count(client_p->realhost, &global, &local);
- /* XXX blah. go down checking the various silly limits
- * setting a_limit_reached if any limit is reached.
- * - Dianora
- */
if (class->max_total != 0 && class->ref_count >= class->max_total)
- a_limit_reached = 1;
+ conf_limit_reached = 1;
else if (class->max_perip != 0 && ip_found->count > class->max_perip)
- a_limit_reached = 1;
+ host_limit_reached = 1;
else if (class->max_local != 0 && local >= class->max_local)
- a_limit_reached = 1;
+ host_limit_reached = 1;
else if (class->max_global != 0 && global >= class->max_global)
- a_limit_reached = 1;
+ host_limit_reached = 1;
+
+ if (host_limit_reached)
+ {
+ struct MaskLookup lookup = {
+ .client = client_p,
+ .name = client_p->realhost,
+ .ip = client_p->sockhost,
+ .addr = &client_p->localClient->ip,
+ .fam = client_p->localClient->aftype,
+ .cmpfunc = match
+ };
+
+ struct MaskItem *session = find_conf_by_address(CONF_SESSION, &lookup);
+ if (session != NULL && !IsConfExemptLimits(conf))
+ {
+ host_limit_reached = 0;
+
+ if (session->count != 0)
+ {
+ if (ip_found->count >= session->count)
+ host_limit_reached = 1;
+ else if (global >= session->count)
+ host_limit_reached = 1;
+ }
+
+ if (!host_limit_reached)
+ {
+ sendto_one(client_p,
+ ":%s NOTICE %s :*** You have reduced session limiting",
+ me.name, client_p->name);
+
+ sendto_snomask(SNO_DEBUG, L_ALL,
+ "User %s (%s@%s) [%s] matches session %s",
+ client_p->name, client_p->username, client_p->realhost, client_p->sockhost, session->host);
+ }
+ }
+ }
- if (a_limit_reached)
+ if (conf_limit_reached || host_limit_reached)
{
if (!IsConfExemptLimits(conf))
return TOO_MANY; /* Already at maximum allowed */
/* And assign... -A1kmm */
if (addr)
+ {
+ v6->sin6_family = AF_INET6;
for (dp = 0; dp < 8; dp++)
/* The cast is a kludge to make netbsd work. */
((unsigned short *)&v6->sin6_addr)[dp] = htons(dc[dp]);
+ }
if (b != NULL)
*b = bits;
for (n = bits / 8 + (bits % 8 ? 1 : 0); n < 4; n++)
addb[n] = 0;
if (addr)
+ {
+ v4->sin_family = AF_INET;
v4->sin_addr.s_addr =
htonl(addb[0] << 24 | addb[1] << 16 | addb[2] << 8 | addb[3]);
+ }
if (b != NULL)
*b = bits;
return HM_IPV4;
int
parse_netmask(const char *text, struct irc_ssaddr *addr, int *b)
{
+ if (addr)
+ memset(addr, 0, sizeof(struct irc_ssaddr));
+
if (strchr(text, '.'))
return try_parse_v4_netmask(text, addr, b);
#ifdef IPV6
case CONF_GLINE:
hostmask_send_expiration(arec);
+ dlinkDelete(&arec->node, &atable[i]);
+ conf_free(arec->conf);
+ MyFree(arec);
+ break;
+ case CONF_SESSION:
+ sendto_snomask(SNO_DEBUG, L_ALL,
+ "Session exception for %s expired",
+ arec->conf->host);
+
dlinkDelete(&arec->node, &atable[i]);
conf_free(arec->conf);
MyFree(arec);
tests/join.c \
tests/mode.c \
tests/nick.c \
+ tests/session.c \
tests/upgrade.c
uninstall-local:
#include "channel_mode.h"
#include "upgrade.h"
#include "channel.h"
+#include "hostmask.h"
#include <check.h>
extern void dnsbl_setup(Suite *);
extern void nick_setup(Suite *s);
extern void extban_setup(Suite *s);
+extern void session_setup(Suite *s);
#define tlog(fmt, ...) plexus_log(__FILE__, __LINE__, fmt, ##__VA_ARGS__)
extern void plexus_log(const char *, int, const char *, ...);
dnsbl_setup(s);
nick_setup(s);
extban_setup(s);
+ session_setup(s);
}
int
--- /dev/null
+/*
+ * ircd-hybrid: an advanced Internet Relay Chat Daemon(ircd).
+ *
+ * Copyright (C) 2016 Adam <Adam@anope.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 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 "plexus_test.h"
+
+static struct MaskItem *
+session_find(const char *mask)
+{
+ struct irc_ssaddr addr;
+ int bits;
+
+ int masktype = parse_netmask(mask, &addr, &bits);
+
+ struct MaskLookup lookup = {
+ .name = mask,
+ .fam = masktype != HM_HOST ? addr.ss.ss_family : 0,
+ .addr = masktype != HM_HOST ? &addr : NULL,
+ .cmpfunc = irccmp
+ };
+
+ return find_conf_by_address(CONF_SESSION, &lookup);
+}
+
+
+START_TEST(session_test)
+{
+ struct PlexusClient *server = server_register("plexus4.2");
+ AddFlag(server->client, FLAGS_SERVICE);
+
+ const time_t time = 123456789;
+
+ io_write(server, "ENCAP %s SESSION %s %d %s %lu %lu :%s",
+ me.name, "test.mask", 42, "Adam", time, time, "test session");
+
+ expect_pingwait(server, &me);
+
+ struct MaskItem *session = session_find("test.mask");
+ ck_assert_ptr_ne(session, NULL);
+
+ ck_assert_int_eq(session->count, 42);
+ ck_assert_str_eq(session->user, "Adam");
+ ck_assert_str_eq(session->host, "test.mask");
+ ck_assert_int_eq(session->setat, time);
+ ck_assert_int_eq(session->until, time);
+ ck_assert_str_eq(session->reason, "test session");
+
+ io_write(server, "ENCAP %s UNSESSION %s",
+ me.name, "test.mask");
+
+ expect_pingwait(server, &me);
+
+ session = session_find("test.mask");
+ ck_assert_ptr_eq(session, NULL);
+}
+END_TEST
+
+void
+session_setup(Suite *s)
+{
+ TCase *tc = tcase_create("session");
+
+ tcase_add_checked_fixture(tc, plexus_up, plexus_down);
+ tcase_add_test(tc, session_test);
+
+ suite_add_tcase(s, tc);
+}