1 /* gline.c - Gline database
2 * Copyright 2000-2004 srvx Development Team
4 * This file is part of srvx.
6 * srvx is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with srvx; if not, write to the Free Software Foundation,
18 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
28 #ifdef HAVE_SYS_SOCKET_H
29 #include <sys/socket.h>
31 #ifdef HAVE_NETINET_IN_H
32 #include <netinet/in.h>
34 #ifdef HAVE_ARPA_INET_H
35 #include <arpa/inet.h>
41 #define KEY_REASON "reason"
42 #define KEY_EXPIRES "expires"
43 #define KEY_LASTMOD "lastmod"
44 #define KEY_ISSUER "issuer"
45 #define KEY_ISSUED "issued"
46 #define KEY_LIFETIME "lifetime"
48 static heap_t gline_heap; /* key: expiry time, data: struct gline_entry* */
49 static dict_t gline_dict; /* key: target, data: struct gline_entry* */
52 gline_comparator(const void *a, const void *b)
54 const struct gline *ga=a, *gb=b;
55 return ga->expires - gb->expires;
59 free_gline_from_dict(void *data)
61 struct gline *ent = data;
69 free_gline(struct gline *ent)
71 dict_remove(gline_dict, ent->target);
75 gline_equal_p(UNUSED_ARG(void *key), void *data, void *extra)
81 gline_expire(UNUSED_ARG(void *data))
83 unsigned long stopped;
87 while (heap_size(gline_heap)) {
88 heap_peek(gline_heap, 0, &wraa);
89 stopped = ((struct gline*)wraa)->lifetime;
95 if (heap_size(gline_heap))
96 timeq_add(stopped, gline_expire, NULL);
100 gline_remove(const char *target, int announce)
104 gl = dict_find(gline_dict, target, NULL);
113 gline_add(const char *issuer, const char *target, unsigned long duration, const char *reason, unsigned long issued, unsigned long lastmod, unsigned long lifetime, int announce)
116 struct gline *prev_first;
118 unsigned long expires;
120 heap_peek(gline_heap, 0, &argh);
122 expires = now + duration;
123 if (lifetime < expires)
125 ent = dict_find(gline_dict, target, NULL);
127 heap_remove_pred(gline_heap, gline_equal_p, ent);
128 if (ent->issued > lastmod)
129 ent->issued = lastmod;
130 if (ent->lastmod < lastmod)
131 ent->lastmod = lastmod;
132 if (ent->expires != expires)
133 ent->expires = expires;
134 if (ent->lifetime < lifetime)
135 ent->lifetime = lifetime;
136 if (strcmp(ent->issuer, issuer)) {
138 ent->issuer = strdup(issuer);
140 if (strcmp(ent->reason, reason)) {
142 ent->reason = strdup(reason);
145 ent = malloc(sizeof(*ent));
146 ent->issued = issued;
147 ent->lastmod = lastmod;
148 ent->issuer = strdup(issuer);
149 ent->target = strdup(target);
150 ent->expires = expires;
151 ent->lifetime = lifetime;
152 ent->reason = strdup(reason);
153 dict_insert(gline_dict, ent->target, ent);
155 heap_insert(gline_heap, ent, ent);
156 if (!prev_first || (ent->lifetime < prev_first->lifetime)) {
157 timeq_del(0, gline_expire, 0, TIMEQ_IGNORE_WHEN|TIMEQ_IGNORE_DATA);
158 timeq_add(ent->lifetime, gline_expire, 0);
161 irc_gline(NULL, ent);
166 gline_alternate_target(const char *target)
168 const char *hostname;
170 /* If no host part, bail. */
171 if (!(hostname = strchr(target, '@')))
173 /* If host part contains wildcards, bail. */
174 if (hostname[strcspn(hostname, "*?/")])
176 /* Get parsed address and canonical name for host. */
178 irc_in_addr_t in; /* move this to the right place */
179 if (irc_pton(&in, NULL, hostname+1)) {
180 if (getnameinfo(/*TODO*/))
182 } else if (!getaddrinfo(/*TODO*/)) {
190 gline_find(const char *target)
196 res = dict_find(gline_dict, target, NULL);
199 /* Stock ircu requires BADCHANs to match exactly. */
200 if ((target[0] == '#') || (target[0] == '&'))
202 else if (target[strcspn(target, "*?")]) {
203 /* Wildcard: do an obnoxiously long search. */
204 for (it = dict_first(gline_dict); it; it = iter_next(it)) {
206 if (match_ircglob(target, res->target))
210 /* See if we can resolve the hostname part of the mask. */
211 if ((alt_target = gline_alternate_target(target))) {
212 res = gline_find(alt_target);
220 gline_refresh_helper(UNUSED_ARG(void *key), void *data, void *extra)
222 struct gline *ge = data;
223 irc_gline(extra, ge);
228 gline_refresh_server(struct server *srv)
230 heap_remove_pred(gline_heap, gline_refresh_helper, srv);
234 gline_refresh_all(void)
236 heap_remove_pred(gline_heap, gline_refresh_helper, 0);
242 return dict_size(gline_dict);
246 gline_add_record(const char *key, void *data, UNUSED_ARG(void *extra))
248 struct record_data *rd = data;
249 const char *issuer, *reason, *dstr;
250 unsigned long issued, expiration, lastmod, lifetime;
252 if (!(reason = database_get_data(rd->d.object, KEY_REASON, RECDB_QSTRING))) {
253 log_module(MAIN_LOG, LOG_ERROR, "Missing reason for gline %s", key);
256 if (!(dstr = database_get_data(rd->d.object, KEY_EXPIRES, RECDB_QSTRING))) {
257 log_module(MAIN_LOG, LOG_ERROR, "Missing expiration for gline %s", key);
260 expiration = strtoul(dstr, NULL, 0);
261 dstr = database_get_data(rd->d.object, KEY_LIFETIME, RECDB_QSTRING);
262 lifetime = dstr ? strtoul(dstr, NULL, 0) : expiration;
263 dstr = database_get_data(rd->d.object, KEY_LASTMOD, RECDB_QSTRING);
264 lastmod = dstr ? strtoul(dstr, NULL, 0) : 0;
265 dstr = database_get_data(rd->d.object, KEY_ISSUED, RECDB_QSTRING);
266 issued = dstr ? strtoul(dstr, NULL, 0) : now;
267 if (!(issuer = database_get_data(rd->d.object, KEY_ISSUER, RECDB_QSTRING)))
268 issuer = "<unknown>";
270 gline_add(issuer, key, expiration - now, reason, issued, lastmod, lifetime, 0);
275 gline_saxdb_read(struct dict *db)
277 return dict_foreach(db, gline_add_record, 0) != NULL;
281 gline_write_entry(UNUSED_ARG(void *key), void *data, void *extra)
283 struct gline *ent = data;
284 struct saxdb_context *ctx = extra;
286 saxdb_start_record(ctx, ent->target, 0);
287 saxdb_write_int(ctx, KEY_EXPIRES, ent->expires);
288 saxdb_write_int(ctx, KEY_ISSUED, ent->issued);
289 saxdb_write_int(ctx, KEY_LIFETIME, ent->lifetime);
291 saxdb_write_int(ctx, KEY_LASTMOD, ent->lastmod);
292 saxdb_write_string(ctx, KEY_REASON, ent->reason);
293 saxdb_write_string(ctx, KEY_ISSUER, ent->issuer);
294 saxdb_end_record(ctx);
299 gline_saxdb_write(struct saxdb_context *ctx)
301 heap_remove_pred(gline_heap, gline_write_entry, ctx);
306 gline_db_cleanup(void)
308 heap_delete(gline_heap);
309 dict_delete(gline_dict);
315 gline_heap = heap_new(gline_comparator);
316 gline_dict = dict_new();
317 dict_set_free_data(gline_dict, free_gline_from_dict);
318 saxdb_register("gline", gline_saxdb_read, gline_saxdb_write);
319 reg_exit_func(gline_db_cleanup);
322 struct gline_discrim *
323 gline_discrim_create(struct userNode *user, struct userNode *src, unsigned int argc, char *argv[])
326 struct gline_discrim *discrim;
328 discrim = calloc(1, sizeof(*discrim));
330 discrim->max_issued = ULONG_MAX;
331 discrim->max_lastmod = ULONG_MAX;
332 discrim->max_lifetime = ULONG_MAX;
334 for (i=0; i<argc; i++) {
336 send_message(user, src, "MSG_MISSING_PARAMS", argv[i]);
338 } else if (!irccasecmp(argv[i], "mask") || !irccasecmp(argv[i], "host")) {
339 if (!irccasecmp(argv[++i], "exact"))
340 discrim->target_mask_type = EXACT;
341 else if (!irccasecmp(argv[i], "subset"))
342 discrim->target_mask_type = SUBSET;
343 else if (!irccasecmp(argv[i], "superset"))
344 discrim->target_mask_type = SUPERSET;
346 discrim->target_mask_type = SUBSET, i--;
348 send_message(user, src, "MSG_MISSING_PARAMS", argv[i-1]);
351 if (!is_gline(argv[i]) && !IsChannelName(argv[i])) {
352 send_message(user, src, "MSG_INVALID_GLINE", argv[i]);
355 discrim->target_mask = argv[i];
356 discrim->alt_target_mask = gline_alternate_target(discrim->target_mask);
357 } else if (!irccasecmp(argv[i], "limit"))
358 discrim->limit = strtoul(argv[++i], NULL, 0);
359 else if (!irccasecmp(argv[i], "reason"))
360 discrim->reason_mask = argv[++i];
361 else if (!irccasecmp(argv[i], "issuer"))
362 discrim->issuer_mask = argv[++i];
363 else if (!irccasecmp(argv[i], "after"))
364 discrim->min_expire = now + ParseInterval(argv[++i]);
365 else if (!irccasecmp(argv[i], "before"))
366 discrim->max_issued = now - ParseInterval(argv[++i]);
367 else if (!irccasecmp(argv[i], "lastmod")) {
368 const char *cmp = argv[++i];
371 discrim->min_lastmod = now - ParseInterval(cmp + 2);
373 discrim->min_lastmod = now - (ParseInterval(cmp + 1) - 1);
375 } else if (cmp[0] == '>') {
377 discrim->max_lastmod = now - ParseInterval(cmp + 2);
379 discrim->max_lastmod = now - (ParseInterval(cmp + 1) - 1);
382 discrim->min_lastmod = now - ParseInterval(cmp + 2);
384 } else if (!irccasecmp(argv[i], "lifetime")) {
385 const char *cmp = argv[++i];
388 discrim->min_lifetime = now - ParseInterval(cmp + 2);
390 discrim->min_lifetime = now - (ParseInterval(cmp + 1) - 1);
392 } else if (cmp[0] == '>') {
394 discrim->max_lifetime = now - ParseInterval(cmp + 2);
396 discrim->max_lifetime = now - (ParseInterval(cmp + 1) - 1);
399 discrim->min_lifetime = now - ParseInterval(cmp + 2);
402 send_message(user, src, "MSG_INVALID_CRITERIA", argv[i]);
408 free(discrim->alt_target_mask);
413 struct gline_search {
414 struct gline_discrim *discrim;
415 gline_search_func func;
421 gline_discrim_match(struct gline *gline, struct gline_discrim *discrim)
423 if ((discrim->issuer_mask && !match_ircglob(gline->issuer, discrim->issuer_mask))
424 || (discrim->reason_mask && !match_ircglob(gline->reason, discrim->reason_mask))
425 || (discrim->target_mask
426 && (((discrim->target_mask_type == SUBSET)
427 && !match_ircglobs(discrim->target_mask, gline->target)
428 && (!discrim->alt_target_mask
429 || !match_ircglobs(discrim->alt_target_mask, gline->target)))
430 || ((discrim->target_mask_type == EXACT)
431 && irccasecmp(discrim->target_mask, gline->target)
432 && (!discrim->alt_target_mask
433 || !irccasecmp(discrim->alt_target_mask, gline->target)))
434 || ((discrim->target_mask_type == SUPERSET)
435 && !match_ircglobs(gline->target, discrim->target_mask)
436 && (!discrim->alt_target_mask
437 || !match_ircglobs(discrim->alt_target_mask, gline->target)))))
438 || (discrim->max_issued < gline->issued)
439 || (discrim->min_expire > gline->expires)
440 || (discrim->min_lastmod > gline->lastmod)
441 || (discrim->max_lastmod < gline->lastmod)
442 || (discrim->min_lifetime > gline->lifetime)
443 || (discrim->max_lifetime < gline->lifetime)) {
450 gline_search_helper(UNUSED_ARG(void *key), void *data, void *extra)
452 struct gline *gline = data;
453 struct gline_search *search = extra;
455 if (gline_discrim_match(gline, search->discrim)
456 && (search->hits++ < search->discrim->limit)) {
457 search->func(gline, search->data);
463 gline_discrim_search(struct gline_discrim *discrim, gline_search_func gsf, void *data)
465 struct gline_search search;
466 search.discrim = discrim;
470 heap_remove_pred(gline_heap, gline_search_helper, &search);