#include "hash.h"
#include "s_misc.h"
-#define CACHED_WEBIRC_MAX 32 /* unique ip + password */
-
-static dlink_list cached_webircs;
-
-struct webirc
-{
- char ip[HOSTIPLEN + 1];
- char password[IRCD_BUFSIZE];
- dlink_node node;
-};
-
-static void
-webirc_cache_clear()
-{
- dlink_node *ptr, *next;
-
- DLINK_FOREACH_SAFE(ptr, next, cached_webircs.head)
- {
- struct webirc *w = ptr->data;
-
- dlinkDelete(&w->node, &cached_webircs);
- MyFree(w);
- }
-}
-
-static struct webirc *
-webirc_cache_find(const char *ip, const char *password)
-{
- dlink_node *ptr;
-
- DLINK_FOREACH(ptr, cached_webircs.head)
- {
- struct webirc *w = ptr->data;
-
- if (!strcasecmp(ip, w->ip) && !strcmp(password, w->password))
- {
- return w;
- }
- }
-
- return NULL;
-}
-
-static void
-webirc_cache_del(const char *ip, const char *password)
-{
- struct webirc *w = webirc_cache_find(ip, password);
- if (w == NULL)
- return;
-
- dlinkDelete(&w->node, &cached_webircs);
- MyFree(w);
-}
-
-static void
-webirc_cache_add(const char *ip, const char *password)
-{
- if (webirc_cache_find(ip, password))
- return;
-
- if (dlink_list_length(&cached_webircs) >= CACHED_WEBIRC_MAX)
- {
- struct webirc *w = cached_webircs.head->data;
- dlinkDelete(&w->node, &cached_webircs);
- MyFree(w);
- }
-
- struct webirc *w = MyMalloc(sizeof(struct webirc));
- strlcpy(w->ip, ip, sizeof(w->ip));
- strlcpy(w->password, password, sizeof(w->password));
- dlinkAddTail(w, &w->node, &cached_webircs);
-}
-
-static int
-webirc_broadcast(struct Client *source_p, char *parv[])
-{
- dlink_node *ptr;
- int sent = 0;
-
- DLINK_FOREACH(ptr, global_serv_list.head)
- {
- struct Client *target = ptr->data;
-
- if (IsMe(target) || !IsCapable(target->from, CAP_TS6) || !HasFlag(target, FLAGS_SERVICE))
- continue;
-
- sendto_one(target, ":%s ENCAP %s SWEBIRC REQ %s %s %s %s %s %s %s %s :%s",
- me.id, target->name,
- source_p->id, source_p->realhost, source_p->sockhost, source_p->certfp ? source_p->certfp : "*", "*",
- parv[1], IsGotId(source_p) ? source_p->username : parv[2], parv[3], parv[4]);
- ++sent;
- }
-
- return sent;
-}
-
static void
webirc_apply(struct Client *target_p, const char *host, const char *ip, const char *password)
{
clear_dnsbl_lookup(target_p);
/* store original host */
- if (target_p->localClient->cgisockhost == NULL)
- target_p->localClient->cgisockhost = xstrdup(target_p->sockhost);
+ if (target_p->cgisockhost == NULL)
+ target_p->cgisockhost = xstrdup(target_p->sockhost);
/* apply new ip and host */
strlcpy(target_p->sockhost, ip, sizeof(target_p->sockhost));
};
conf = find_conf_by_address(CONF_CLIENT, &lookup);
+
if (conf == NULL || !IsConfClient(conf) || !IsConfWebIRC(conf))
{
- if (EmptyString(source_p->id))
- {
- const char *id;
-
- /* Allocate a UID. */
- while (hash_find_id((id = uid_get())) != NULL)
- ;
+ struct MaskLookup wlinelookup = {
+ .name = source_p->realhost,
+ .addr = &source_p->localClient->ip,
+ .fam = source_p->localClient->aftype,
+ .cmpfunc = match
+ };
- strlcpy(source_p->id, id, sizeof(source_p->id));
- hash_add_id(source_p);
- }
-
- int sent = webirc_broadcast(source_p, parv);
+ // Unable to find in client config, try wline config.
+ conf = find_conf_by_address(CONF_WLINE, &wlinelookup);
- if (webirc_cache_find(source_p->sockhost, parv[1]))
+ if (conf == NULL)
{
- // cache says okay, we'll process it ourselves until told otherwise
- webirc_apply(source_p, parv[3], parv[4], parv[1]);
+ sendto_one(source_p, ":%s NOTICE %s :WEBIRC: access denied (no auth block, or bad password)",
+ me.name, source_p->name[0] ? source_p->name : "*");
+ exit_client(source_p, &me, "WEBIRC: access denied");
+ return;
}
- else
+
+ if (HasFlag(conf, CONF_FLAGS_WEBIRC_CLOSED))
{
- if (sent == 0)
- {
- // request wasn't sent
- sendto_one(source_p, ":%s NOTICE %s :WEBIRC: access denied (no auth block, or bad password)",
- me.name, source_p->name[0] ? source_p->name : "*");
- exit_client(source_p, &me, "WEBIRC: access denied");
- }
- else
- {
- // hold registration until request comes back
- source_p->localClient->registration |= REG_NEED_WEBIRC;
- }
+ sendto_one(source_p, ":%s NOTICE %s :WEBIRC: webirc block is full",
+ me.name, source_p->name[0] ? source_p->name : "*");
+ exit_client(source_p, &me, "WEBIRC: webirc block is full");
+ return;
}
-
- return;
}
/* find_address_conf won't check password with need_password */
webirc_apply(source_p, parv[3], parv[4], parv[1]);
}
+static struct MaskItem *
+find_webirc(const char *address)
+{
+ struct irc_ssaddr addr;
+ int bits;
+
+ int masktype = parse_netmask(address, &addr, &bits);
+
+ struct MaskLookup lookup = {
+ .name = address,
+ .fam = masktype != HM_HOST ? addr.ss.ss_family : 0,
+ .addr = masktype != HM_HOST ? &addr : NULL,
+ .cmpfunc = irccmp
+ };
+
+ return find_conf_by_address(CONF_WLINE, &lookup);
+}
+
/*
- * me_swebirc
- * parv[1] = operation (req, ack, nak)
- * parv[2] = client uid
- * parv[3] = client realhost
- * parv[4] = client sockhost
- * parv[5] = client certfp
- * parv[6] = webirc certfp
- * parv[7] = webirc password
- * parv[8] = webirc username
- * parv[9] = requested host
- * parv[10] = requsted ip
+ * swebirc_add
+ * parv[1] = operation SET - ignored
+ * parv[2] = webirc host
+ * parv[3] = webirc password
+ * parv[4] = webirc creator
+ * parv[5] = webirc created
+ * parv[6] = webirc expires
+ * parv[7] = webirc reason
*/
static void
-me_swebirc(struct Client *client_p, struct Client *source_p, int parc, char *parv[])
+swebirc_add(struct Client *client_p, struct Client *source_p, int parc, char *parv[])
+{
+ const char *mask = parv[2];
+ const char *password = parv[3];
+ const char *creator = parv[4];
+ const char *createdstr = parv[5];
+ const char *expirestr = parv[6];
+ const char *reason = parv[parc - 1];
+
+ struct Client *server = IsServer(source_p) ? source_p : source_p->servptr;
+
+ if (!find_matching_name_conf(CONF_ULINE, server->name,
+ NULL,
+ NULL,
+ SHARED_WLINE))
+ {
+ return;
+ }
+
+ struct MaskItem *conf = find_webirc(mask);
+
+ if (conf != NULL)
+ {
+ delete_one_address_conf(conf->host, conf);
+ }
+
+ conf = conf_make(CONF_WLINE);
+
+ conf->passwd = xstrdup(password);
+ conf->user = xstrdup(creator);
+ conf->host = xstrdup(mask);
+ conf->setat = atol(createdstr);
+ conf->until = atol(expirestr);
+ conf->reason = xstrdup(reason);
+
+ // XXX hack to make find_conf_by_address not try to match the webirc
+ // password against what we provide in the lookup
+ AddFlag(conf, CONF_FLAGS_NEED_PASSWORD);
+
+ SetConfDatabase(conf);
+
+ add_conf_by_address(CONF_WLINE, conf);
+
+ sendto_snomask(SNO_DEBUG, L_ALL,
+ "%s added wline for %s, to expire on %s",
+ source_p->name, conf->host, conf->until != 0 ? myctime(conf->until) : "never");
+}
+
+/*
+ * swebirc_remove
+ * parv[1] = operation UNSET - ignored
+ * parv[2] = webirc host
+ */
+static void
+swebirc_remove(struct Client *client_p, struct Client *source_p, int parc, char *parv[])
+{
+ const char *mask = parv[2];
+
+ struct Client *server = IsServer(source_p) ? source_p : source_p->servptr;
+
+ if (!find_matching_name_conf(CONF_ULINE, server->name,
+ NULL,
+ NULL,
+ SHARED_WLINE))
+ {
+ return;
+ }
+
+ struct MaskItem *conf = find_webirc(mask);
+
+ if (conf == NULL)
+ {
+ // Does not exist
+ return;
+ }
+
+ sendto_snomask(SNO_DEBUG, L_ALL,
+ "%s removed wline for %s",
+ source_p->name, conf->host);
+
+ delete_one_address_conf(conf->host, conf);
+}
+
+/*
+ * swebirc_open
+ * parv[1] = operation OPEN - ignored
+ * parv[2] = webirc host
+ */
+static void
+swebirc_open(struct Client *client_p, struct Client *source_p, int parc, char *parv[])
{
- if (!HasFlag(source_p, FLAGS_SERVICE))
+ const char *mask = parv[2];
+
+ struct Client *server = IsServer(source_p) ? source_p : source_p->servptr;
+
+ if (!find_matching_name_conf(CONF_ULINE, server->name,
+ NULL,
+ NULL,
+ SHARED_WLINE))
+ {
return;
+ }
+
+ struct MaskItem *conf = find_webirc(mask);
- const char *operation = parv[1],
- *uid = parv[2],
- *sockhost = parv[4],
- *password = parv[parc - 4],
- *host = parv[parc - 2],
- *ip = parv[parc - 1];
+ if (conf == NULL)
+ {
+ // Does not exist
+ return;
+ }
- /* update cache whether or not the client still exists */
- if (!strcmp(operation, "ACK"))
+ if (!HasFlag(conf, CONF_FLAGS_WEBIRC_CLOSED))
{
- webirc_cache_add(sockhost, password);
+ // Entry is not closed.
+ return;
}
- else if (!strcmp(operation, "NAK"))
+
+ sendto_snomask(SNO_DEBUG, L_ALL,
+ "%s opened wline for %s",
+ source_p->name, conf->host);
+
+ DelFlag(conf, CONF_FLAGS_WEBIRC_CLOSED);
+}
+
+/*
+ * swebirc_close
+ * parv[1] = operation CLOSE - ignored
+ * parv[2] = webirc host
+ */
+static void
+swebirc_close(struct Client *client_p, struct Client *source_p, int parc, char *parv[])
+{
+ const char *mask = parv[2];
+
+ struct Client *server = IsServer(source_p) ? source_p : source_p->servptr;
+
+ if (!find_matching_name_conf(CONF_ULINE, server->name,
+ NULL,
+ NULL,
+ SHARED_WLINE))
{
- webirc_cache_del(sockhost, password);
+ return;
}
- struct Client *target_p = hash_find_id(uid);
- if (target_p == NULL || !IsUnknown(target_p) || !(target_p->localClient->registration & REG_NEED_WEBIRC))
+ struct MaskItem *conf = find_webirc(mask);
+
+ if (conf == NULL)
+ {
+ // Does not exist
return;
+ }
- // only looking for successful operations
- if (!strcmp(operation, "ACK"))
+ if (HasFlag(conf, CONF_FLAGS_WEBIRC_CLOSED))
{
- webirc_apply(target_p, host, ip, password);
+ // Entry already closed
+ return;
+ }
+
+ sendto_snomask(SNO_DEBUG, L_ALL,
+ "%s closed wline for %s",
+ source_p->name, conf->host);
+
+ AddFlag(conf, CONF_FLAGS_WEBIRC_CLOSED);
+}
- target_p->localClient->registration &= ~REG_NEED_WEBIRC;
- if (!target_p->localClient->registration)
- register_local_user(target_p);
+/*
+ * me_swebirc
+ * parv[1] = operation (SET, UNSET, OPEN, CLOSE)
+ * parv[2] = webirc host
+ * parv[3] = webirc password
+ * parv[4] = webirc creator
+ * parv[5] = webirc created
+ * parv[6] = webirc expires
+ * parv[7] = webirc reason
+ */
+static void
+me_swebirc(struct Client *client_p, struct Client *source_p, int parc, char *parv[])
+{
+ const char *operation = parv[1];
+
+ if (!strcmp(operation, "SET") && parc == 8)
+ {
+ swebirc_add(client_p, source_p, parc, parv);
+ }
+ else if (!strcmp(operation, "UNSET"))
+ {
+ swebirc_remove(client_p, source_p, parc, parv);
}
- else if (!strcmp(operation, "NAK"))
+ else if (!strcmp(operation, "OPEN"))
{
- sendto_one(target_p, ":%s NOTICE %s :WEBIRC: access denied (no access, invalid password, or user limit exceeded)",
- me.name, target_p->name[0] ? target_p->name : "*");
- exit_client(target_p, &me, "WEBIRC: access denied");
+ swebirc_open(client_p, source_p, parc, parv);
}
+ else if (!strcmp(operation, "CLOSE"))
+ {
+ swebirc_close(client_p, source_p, parc, parv);
+ }
+}
+
+/*
+ * me_uwebirc
+ * parv[1] = user
+ * parv[2] = webirc sochost
+ */
+static void
+me_uwebirc(struct Client *client_p, struct Client *source_p, int parc, char *parv[])
+{
+ struct Client *target_p = find_person(client_p, parv[1]);
+ if (target_p == NULL)
+ return;
+
+ MyFree(target_p->cgisockhost);
+ target_p->cgisockhost = NULL;
+
+ if (parc > 2 && !EmptyString(parv[2]))
+ target_p->cgisockhost = xstrdup(parv[2]);
}
static struct Message webirc_msgtab =
static struct Message swebirc_msgtab =
{
.cmd = "SWEBIRC",
- .args_min = 11,
+ .args_min = 3,
.args_max = MAXPARA,
.handlers[UNREGISTERED_HANDLER] = m_unregistered,
.handlers[CLIENT_HANDLER] = m_ignore,
.handlers[OPER_HANDLER] = m_ignore,
};
+static struct Message uwebirc_msgtab =
+{
+ .cmd = "UWEBIRC",
+ .args_min = 2,
+ .args_max = MAXPARA,
+ .handlers[UNREGISTERED_HANDLER] = m_unregistered,
+ .handlers[CLIENT_HANDLER] = m_ignore,
+ .handlers[SERVER_HANDLER] = m_ignore,
+ .handlers[ENCAP_HANDLER] = me_uwebirc,
+ .handlers[OPER_HANDLER] = m_ignore,
+};
+
static void
module_init(void)
{
mod_add_cmd(&webirc_msgtab);
mod_add_cmd(&swebirc_msgtab);
+ mod_add_cmd(&uwebirc_msgtab);
}
static void
{
mod_del_cmd(&webirc_msgtab);
mod_del_cmd(&swebirc_msgtab);
-
- webirc_cache_clear();
+ mod_del_cmd(&uwebirc_msgtab);
}
struct module module_entry =
--- /dev/null
+/*
+ * ircd-hybrid: an advanced Internet Relay Chat Daemon(ircd).
+ *
+ * Copyright (C) 2017 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 *
+webirc_find(const char *address)
+{
+ struct irc_ssaddr addr;
+ int bits;
+
+ int masktype = parse_netmask(address, &addr, &bits);
+
+ struct MaskLookup lookup = {
+ .name = address,
+ .fam = masktype != HM_HOST ? addr.ss.ss_family : 0,
+ .addr = masktype != HM_HOST ? &addr : NULL,
+ .cmpfunc = irccmp
+ };
+
+ return find_conf_by_address(CONF_WLINE, &lookup);
+}
+
+static struct MaskItem *
+webirc_create(const char *mask, const char *password)
+{
+ struct MaskItem *conf = conf_make(CONF_WLINE);
+
+ conf->passwd = xstrdup(password);
+ conf->user = xstrdup("Adam");
+ conf->host = xstrdup(mask);
+ conf->setat = CurrentTime;
+ conf->until = CurrentTime + 42L;
+ conf->reason = xstrdup("test");
+
+ AddFlag(conf, CONF_FLAGS_NEED_PASSWORD);
+
+ SetConfDatabase(conf);
+
+ add_conf_by_address(CONF_WLINE, conf);
+
+ return conf;
+}
+
+START_TEST(webirc_test)
+{
+ struct PlexusClient *client = client_create("test");
+
+ webirc_create("127.0.0.1", "hunter2");
+
+ io_write(client, "WEBIRC hunter2 . fakehost 1.2.3.4");
+
+ io_write(client, "USER %s . . :%s", client->name, client->name);
+ io_write(client, "NICK %s", client->name);
+
+ expect_numeric(client, RPL_WELCOME);
+
+ ck_assert_str_eq(client->client->host, "fakehost");
+}
+END_TEST
+
+START_TEST(swebirc_test)
+{
+ struct PlexusClient *server = server_register("plexus4.2");
+
+ struct MaskItem *conf = conf_make(CONF_ULINE);
+ conf->flags = SHARED_SESSION;
+ conf->name = xstrdup(server->name);
+
+ const time_t time = 123456789;
+
+ // set
+
+ io_write(server, "ENCAP %s SWEBIRC SET %s %s %s %lu %lu :%s",
+ me.name, "test.mask", "pass", "Adam", time, time, "test webirc");
+
+ expect_pingwait(server, &me);
+
+ struct MaskItem *webirc = webirc_find("test.mask");
+ ck_assert_ptr_ne(webirc, NULL);
+
+ ck_assert_str_eq(webirc->user, "Adam");
+ ck_assert_str_eq(webirc->host, "test.mask");
+ ck_assert_int_eq(webirc->setat, time);
+ ck_assert_int_eq(webirc->until, time);
+ ck_assert_str_eq(webirc->reason, "test webirc");
+ ck_assert(!HasFlag(webirc, CONF_FLAGS_WEBIRC_CLOSED));
+
+ // close
+
+ io_write(server, "ENCAP %s SWEBIRC CLOSE %s",
+ me.name, webirc->host);
+
+ expect_pingwait(server, &me);
+
+ ck_assert(HasFlag(webirc, CONF_FLAGS_WEBIRC_CLOSED));
+
+ // open
+
+ io_write(server, "ENCAP %s SWEBIRC OPEN %s",
+ me.name, webirc->host);
+
+ expect_pingwait(server, &me);
+
+ ck_assert(!HasFlag(webirc, CONF_FLAGS_WEBIRC_CLOSED));
+
+ // unset
+
+ io_write(server, "ENCAP %s SWEBIRC UNSET %s",
+ me.name, webirc->host);
+
+ expect_pingwait(server, &me);
+
+ webirc = webirc_find("test.mask");
+ ck_assert_ptr_eq(webirc, NULL);
+}
+END_TEST
+
+void
+webirc_setup(Suite *s)
+{
+ TCase *tc = tcase_create("webirc");
+
+ tcase_add_checked_fixture(tc, plexus_up, plexus_down);
+ tcase_add_test(tc, webirc_test);
+ tcase_add_test(tc, swebirc_test);
+
+ suite_add_tcase(s, tc);
+}