]> jfr.im git - irc/rizon/plexus4.git/commitdiff
Add support for having session exceptions pushed from services
authorAdam <redacted>
Tue, 18 Oct 2016 22:15:10 +0000 (18:15 -0400)
committerAdam <redacted>
Tue, 18 Oct 2016 22:15:10 +0000 (18:15 -0400)
help/stats
include/conf.h
modules/Makefile.am
modules/m_session.c [new file with mode: 0644]
modules/m_stats.c
src/conf.c
src/hostmask.c
test/Makefile.am
test/plexus_test.h
test/test.c
test/tests/session.c [new file with mode: 0644]

index 2773fd9555c5e6362cfca83fa3dfcb3e1a05a516..418de1e0cf53aee2e492c6eb051adf09f1318ec8 100644 (file)
@@ -29,6 +29,7 @@ X f - Shows file descriptors
   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
index d36f91cfdc6720205710f398d2defa0020652054..0157b732f3e4f216b8254c4f4d8d65402bb4473b 100644 (file)
 #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 |\
@@ -125,6 +125,7 @@ enum maskitem_type
   CONF_SERVICE  = 1 << 11,
   CONF_OPER     = 1 << 12,
   CONF_DNSBL    = 1 << 13,
+  CONF_SESSION  = 1 << 14,
 };
 
 enum
index 8875f0ed423ec88c2d2bdf31799271c485de84be..679924eaee0d1fb9c40e971112fc5edfadd084cd 100644 (file)
@@ -59,6 +59,7 @@ modules_LTLIBRARIES = extban_account.la \
                       m_sasl.la      \
                       m_set.la       \
                       m_services.la  \
+                      m_session.la   \
                       m_shedding.la  \
                       m_snotice.la   \
                       m_stats.la     \
@@ -140,6 +141,7 @@ m_resv_la_LDFLAGS = $(MODULE_FLAGS)
 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)
@@ -220,6 +222,7 @@ m_resv_la_SOURCES = m_resv.c
 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
diff --git a/modules/m_session.c b/modules/m_session.c
new file mode 100644 (file)
index 0000000..5585ad6
--- /dev/null
@@ -0,0 +1,158 @@
+/*
+ *  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,
+};
+
index 9c191678c782415d9965a3dab1717e703ce3831a..70ada0d9d9325ee15e95d7de4ce2145a618d0640 100644 (file)
@@ -1151,6 +1151,29 @@ stats_service(struct Client *source_p, int parc, char *parv[])
   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[])
 {
@@ -1561,7 +1584,7 @@ static const struct StatsStruct
   { '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       },
index 96d78159ff334ebd728d369811b1c587087d9342..0a80e05a2086a672c5483b0a63a387b50b9b5da7 100644 (file)
@@ -465,7 +465,7 @@ attach_iline(struct Client *client_p, struct MaskItem *conf)
 {
   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);
@@ -478,20 +478,53 @@ attach_iline(struct Client *client_p, struct MaskItem *conf)
 
   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 */
index de0ca3bf3366e90b02333ad7be7207b0305702cf..1d7ed974adbfb2180eff12bc61a57bf8defe7077 100644 (file)
@@ -154,9 +154,12 @@ try_parse_v6_netmask(const char *text, struct irc_ssaddr *addr, int *b)
 
   /* 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;
@@ -236,8 +239,11 @@ try_parse_v4_netmask(const char *text, struct irc_ssaddr *addr, int *b)
   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;
@@ -253,6 +259,9 @@ try_parse_v4_netmask(const char *text, struct irc_ssaddr *addr, int *b)
 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
@@ -915,6 +924,15 @@ hostmask_expire_temporary(void)
         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);
index 6cd6ffca3616ebc8f77543ff133e83707e63a033..9aa9ffee5eb94cccca35f1c6888e5665bf4bc55f 100644 (file)
@@ -28,6 +28,7 @@ check_plexus_SOURCES = \
        tests/join.c \
        tests/mode.c \
        tests/nick.c \
+       tests/session.c \
        tests/upgrade.c
 
 uninstall-local:
index 9159c26c4a410aa06883fefb6c5e762cf825e2d8..d23c7909119bc660b5e9d2032e15ff5efe4bd446 100644 (file)
@@ -14,6 +14,7 @@
 #include "channel_mode.h"
 #include "upgrade.h"
 #include "channel.h"
+#include "hostmask.h"
 
 #include <check.h>
 
@@ -76,6 +77,7 @@ extern void upgrade_setup(Suite *);
 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 *, ...);
index aa7b2fb0091a71c9e04ffd17dc747d14c69fd147..19047a46339e377e30fca0d2e68b352e9a094ead 100644 (file)
@@ -17,6 +17,7 @@ add_testcases(Suite *s)
   dnsbl_setup(s);
   nick_setup(s);
   extban_setup(s);
+  session_setup(s);
 }
 
 int
diff --git a/test/tests/session.c b/test/tests/session.c
new file mode 100644 (file)
index 0000000..7749d62
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ *  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);
+}