diff --git a/include/batch.h b/include/batch.h new file mode 100644 index 00000000..76c265e7 --- /dev/null +++ b/include/batch.h @@ -0,0 +1,47 @@ +/* ircd/batch.h - batch management + * Copyright (c) 2016 Elizabeth Myers + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice is present in all copies. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "stdinc.h" +#include "client.h" + +typedef enum +{ + BATCH_NETSPLIT, + BATCH_NETJOIN, + BATCH_LAST, +} batch_type; + +/* Used for netsplits/netjoins */ +struct Batch +{ + batch_type batch; /* Type of batch */ + char id[8]; /* Id of batch */ + void *data; /* Batch-specific data */ + void *pdata; /* Private data */ + int parc; /* Batch parameter count */ + char **parv; /* Batch parameters */ + + rb_dlink_node node; +}; + +struct Batch *start_batch(batch_type batch, void *data, int parc, ...); +void finish_batch(struct Batch *batch_p); + +struct Batch *find_batch(batch_type batch, void *data); diff --git a/include/s_serv.h b/include/s_serv.h index 6808c37e..9b161b60 100644 --- a/include/s_serv.h +++ b/include/s_serv.h @@ -68,6 +68,7 @@ extern unsigned int CLICAP_USERHOST_IN_NAMES; extern unsigned int CLICAP_CAP_NOTIFY; extern unsigned int CLICAP_CHGHOST; extern unsigned int CLICAP_ECHO_MESSAGE; +extern unsigned int CLICAP_BATCH; /* * XXX: this is kind of ugly, but this allows us to have backwards diff --git a/ircd/Makefile.am b/ircd/Makefile.am index d4b1e31b..222efb87 100644 --- a/ircd/Makefile.am +++ b/ircd/Makefile.am @@ -18,9 +18,10 @@ if MINGW EXTRA_FLAGS = -Wl,--enable-runtime-pseudo-reloc -export-symbols-regex '*' endif -libircd_la_SOURCES = \ +libircd_la_SOURCES = \ authproc.c \ bandbi.c \ + batch.c \ cache.c \ capability.c \ channel.c \ diff --git a/ircd/batch.c b/ircd/batch.c new file mode 100644 index 00000000..16c739fc --- /dev/null +++ b/ircd/batch.c @@ -0,0 +1,202 @@ +/* ircd/batch.c - batch management + * Copyright (c) 2016 Elizabeth Myers + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice is present in all copies. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "stdinc.h" +#include "batch.h" +#include "client.h" +#include "s_serv.h" +#include "send.h" +#include "channel.h" +#include "hash.h" +#include "s_assert.h" +#include "rb_radixtree.h" + +/* Multiple batches may be in progress for each slot. */ +rb_dlink_list batches[BATCH_LAST]; + +static inline void +generate_batch_id(char *ptr, size_t len) +{ + size_t i; + const char batchchars[65] = + "\0._0123456789" /* Zero-indexed */ + "abcdefghijklmnopqrstuvwxyz" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + + len--; /* Room for \0 */ + for(i = 0; i < len; i++) + { + char r; + + do + { + r = batchchars[rand() & 0x7F]; /* random int between 0-64 */ + + if(r == '\0' && i > 3) + /* We have enough chars */ + goto end; + } while(r == '\0'); + + ptr[i] = r; + } + +end: + ptr[i] = '\0'; +} + +struct Batch * +start_batch(batch_type batch, void *data, int parc, ...) +{ + struct Batch *batch_p = rb_malloc(sizeof(struct Batch)); + + batch_p->batch = batch; + generate_batch_id(batch_p->id, sizeof(batch_p->id)); + batch_p->data = data; + batch_p->parc = parc; + if(parc > 0) + { + /* Get the argument list */ + va_list args; + + batch_p->parv = rb_malloc(sizeof(char *) * parc); + + va_start(args, parc); + for(size_t i = 0; i < parc; i++) + batch_p->parv[i] = va_arg(args, char *); + va_end(args); + } + + /* Batch-type specific processing */ + switch(batch) + { + case BATCH_NETSPLIT: + case BATCH_NETJOIN: + { + /* Build list of channels affected by the batch */ + rb_dlink_list *clist; + rb_radixtree_iteration_state iter; + struct Channel *chptr; + + batch_p->pdata = clist = rb_malloc(sizeof(rb_dlink_list)); + + /* Look for channels we need to send the batch to */ + RB_RADIXTREE_FOREACH(chptr, &iter, channel_tree) + { + rb_dlink_node *ptr; + + if(rb_dlink_list_length(&chptr->locmembers) == 0) + /* They're all remotes, so don't send a batch */ + continue; + + /* Hunt for members in the channel from the target server + * If we find one, send the channel a BATCH message */ + RB_DLINK_FOREACH(ptr, chptr->members.head) + { + struct Client *client_p = ptr->data; + + if(client_p->from == data) + { + rb_dlinkAddAlloc(rb_strdup(chptr->chname), clist); + sendto_channel_local_with_capability(ALL_MEMBERS, CLICAP_BATCH, NOCAPS, + chptr, ":%s BATCH +%s %s %s", + me.name, + batch == BATCH_NETSPLIT ? "netsplit" : "netjoin", + batch_p->parv[0], batch_p->parv[1]); + break; + } + } + } + } + break; + default: + s_assert(0); + break; + } + + rb_dlinkAdd(batch_p, &batch_p->node, &batches[batch]); + + return batch_p; +} + +void +finish_batch(struct Batch *batch_p) +{ + if(batch_p == NULL) + return; + + /* Batch type-specific processing */ + switch(batch_p->batch) + { + case BATCH_NETSPLIT: + case BATCH_NETJOIN: + { + rb_dlink_list *clist = batch_p->pdata; + rb_dlink_node *ptr, *nptr; + + RB_DLINK_FOREACH_SAFE(ptr, nptr, clist->head) + { + struct Channel *chptr = find_channel(ptr->data); + + if(chptr != NULL) /* Shouldn't be but just in case... */ + { + sendto_channel_local_with_capability(ALL_MEMBERS, CLICAP_BATCH, NOCAPS, + chptr, ":%s BATCH -%s %s %s", + me.name, + batch_p->batch == BATCH_NETSPLIT ? "netsplit" : "netjoin", + batch_p->parv[0], batch_p->parv[1]); + } + + rb_free(ptr->data); + rb_dlinkDestroy(ptr, clist); + } + + rb_free(clist); + } + break; + default: + s_assert(0); + break; + } + + /* Free all the strings */ + for(size_t i = 0; i < (batch_p->parc - 1); i++) + rb_free(batch_p->parv[i]); + + rb_free(batch_p->parv); + + rb_dlinkDelete(&batch_p->node, &batches[batch_p->batch]); + rb_free(batch_p); +} + +struct Batch * +find_batch(batch_type batch, void *data) +{ + rb_dlink_node *ptr; + + RB_DLINK_FOREACH(ptr, batches[batch].head) + { + struct Batch *batch_p = ptr->data; + + if(batch_p->data == data) + return batch_p; + } + + return NULL; +} diff --git a/ircd/client.c b/ircd/client.c index 312df13f..67824363 100644 --- a/ircd/client.c +++ b/ircd/client.c @@ -53,6 +53,7 @@ #include "sslproc.h" #include "wsproc.h" #include "s_assert.h" +#include "batch.h" #define DEBUG_EXITED_CLIENTS @@ -1385,14 +1386,25 @@ exit_remote_server(struct Client *client_p, struct Client *source_p, struct Clie static char comment1[(HOSTLEN*2)+2]; static char newcomment[BUFSIZE]; struct Client *target_p; + struct Batch *batch_p; if(ConfigServerHide.flatten_links) + { strcpy(comment1, "*.net *.split"); + + /* rb_strdup since they are later freed */ + batch_p = start_batch(BATCH_NETSPLIT, source_p, 2, + rb_strdup("*.net"), rb_strdup("*.split")); + } else { strcpy(comment1, source_p->servptr->name); strcat(comment1, " "); strcat(comment1, source_p->name); + + batch_p = start_batch(BATCH_NETSPLIT, source_p, 2, + rb_strdup(source_p->servptr->name), + rb_strdup(source_p->name)); } if (IsPerson(from)) snprintf(newcomment, sizeof(newcomment), "by %s: %s", @@ -1430,6 +1442,7 @@ exit_remote_server(struct Client *client_p, struct Client *source_p, struct Clie #else rb_dlinkAddAlloc(source_p, &dead_list); #endif + finish_batch(batch_p); return 0; } @@ -1463,6 +1476,7 @@ exit_local_server(struct Client *client_p, struct Client *source_p, struct Clien static char comment1[(HOSTLEN*2)+2]; static char newcomment[BUFSIZE]; unsigned int sendk, recvk; + struct Batch *batch_p; rb_dlinkDelete(&source_p->localClient->tnode, &serv_list); rb_dlinkFindDestroy(source_p, &global_serv_list); @@ -1493,12 +1507,18 @@ exit_local_server(struct Client *client_p, struct Client *source_p, struct Clien close_connection(source_p); if(ConfigServerHide.flatten_links) + { strcpy(comment1, "*.net *.split"); + batch_p = start_batch(BATCH_NETSPLIT, source_p, 2, + rb_strdup("*.net"), rb_strdup("*.split")); + } else { strcpy(comment1, source_p->servptr->name); strcat(comment1, " "); strcat(comment1, source_p->name); + batch_p = start_batch(BATCH_NETSPLIT, source_p, 2, + rb_strdup(source_p->servptr->name), rb_strdup(source_p->name)); } if(source_p->serv != NULL) @@ -1520,6 +1540,7 @@ exit_local_server(struct Client *client_p, struct Client *source_p, struct Clien SetDead(source_p); rb_dlinkAddAlloc(source_p, &dead_list); + finish_batch(batch_p); return 0; } diff --git a/ircd/s_serv.c b/ircd/s_serv.c index 4b449323..d103d573 100644 --- a/ircd/s_serv.c +++ b/ircd/s_serv.c @@ -100,6 +100,7 @@ unsigned int CLICAP_USERHOST_IN_NAMES; unsigned int CLICAP_CAP_NOTIFY; unsigned int CLICAP_CHGHOST; unsigned int CLICAP_ECHO_MESSAGE; +unsigned int CLICAP_BATCH; /* * initialize our builtin capability table. --nenolod @@ -147,6 +148,7 @@ init_builtin_capabs(void) CLICAP_CAP_NOTIFY = capability_put(cli_capindex, "cap-notify", NULL); CLICAP_CHGHOST = capability_put(cli_capindex, "chghost", NULL); CLICAP_ECHO_MESSAGE = capability_put(cli_capindex, "echo-message", NULL); + CLICAP_BATCH = capability_put(cli_capindex, "batch", NULL); } static CNCB serv_connect_callback; diff --git a/modules/Makefile.am b/modules/Makefile.am index 82653821..3e8731db 100644 --- a/modules/Makefile.am +++ b/modules/Makefile.am @@ -9,6 +9,7 @@ auto_load_moddir=@moduledir@/autoload auto_load_mod_LTLIBRARIES = \ cap_account_tag.la \ + cap_batch.la \ cap_server_time.la \ chm_nocolour.la \ chm_noctcp.la \ diff --git a/modules/cap_batch.c b/modules/cap_batch.c new file mode 100644 index 00000000..9c0ed960 --- /dev/null +++ b/modules/cap_batch.c @@ -0,0 +1,70 @@ +/* + * charybdis: an advanced ircd. + * cap_batch.c: implement the batch IRCv3.2 capability + * + * Copyright (c) 2016 Elizabeth Myers + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice is present in all copies. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "stdinc.h" +#include "modules.h" +#include "hook.h" +#include "client.h" +#include "ircd.h" +#include "send.h" +#include "s_conf.h" +#include "s_user.h" +#include "s_serv.h" +#include "numeric.h" +#include "chmode.h" +#include "batch.h" +#include "inline/stringops.h" + +static const char cap_batch_desc[] = + "Provides the batch client capability"; + +static void cap_batch_process(hook_data *); + +mapi_hfn_list_av1 cap_batch_hfnlist[] = { + { "outbound_msgbuf", (hookfn) cap_batch_process }, + { NULL, NULL } +}; + +static void +cap_batch_process(hook_data *data) +{ + struct MsgBuf *msgbuf = data->arg1; + struct Client *client_p = data->client; + struct Batch *batch_p; + + if(rb_strcasecmp(msgbuf->cmd, "quit") == 0) + { + if(!IsClient(client_p) || MyConnect(client_p)) + /* Remote users only please */ + return; + + /* Now find our batch... */ + if((batch_p = find_batch(BATCH_NETSPLIT, client_p->from)) == NULL) + return; + + /* Boom */ + msgbuf_append_tag(msgbuf, "batch", batch_p->id, CLICAP_BATCH); + } +} + +DECLARE_MODULE_AV2(cap_batch, NULL, NULL, NULL, NULL, cap_batch_hfnlist, NULL, NULL, cap_batch_desc);