]> jfr.im git - irc/evilnet/x3.git/blame - src/shun.c
Fixed incorrect uplink password value name in example config
[irc/evilnet/x3.git] / src / shun.c
CommitLineData
d914d1cb 1/* shun.c - Shun database
2 * Copyright 2000-2004 srvx Development Team
3 *
4 * This file is part of x3.
5 *
d0f04f71 6 * x3 is free software; you can redistribute it and/or modify
d914d1cb 7 * it under the terms of the GNU General Public License as published by
348683aa 8 * the Free Software Foundation; either version 3 of the License, or
d914d1cb 9 * (at your option) any later version.
10 *
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.
15 *
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.
19 */
20
21#include "heap.h"
22#include "helpfile.h"
23#include "log.h"
24#include "saxdb.h"
25#include "timeq.h"
26#include "shun.h"
27
28#ifdef HAVE_SYS_SOCKET_H
29#include <sys/socket.h>
30#endif
31#ifdef HAVE_NETINET_IN_H
32#include <netinet/in.h>
33#endif
34#ifdef HAVE_ARPA_INET_H
35#include <arpa/inet.h>
36#endif
37#ifdef HAVE_NETDB_H
38#include <netdb.h>
39#endif
40
41#define KEY_REASON "reason"
42#define KEY_EXPIRES "expires"
43#define KEY_ISSUER "issuer"
44#define KEY_ISSUED "issued"
45
46static heap_t shun_heap; /* key: expiry time, data: struct shun_entry* */
47static dict_t shun_dict; /* key: target, data: struct shun_entry* */
48
49static int
50shun_comparator(const void *a, const void *b)
51{
52 const struct shun *ga=a, *gb=b;
53 return ga->expires - gb->expires;
54}
55
56static void
57free_shun_from_dict(void *data)
58{
59 struct shun *ent = data;
60 free(ent->issuer);
61 free(ent->target);
62 free(ent->reason);
63 free(ent);
64}
65
66static void
67free_shun(struct shun *ent)
68{
69 dict_remove(shun_dict, ent->target);
70}
71
72static int
73shun_for_p(UNUSED_ARG(void *key), void *data, void *extra)
74{
75 struct shun *ge = data;
76 return !irccasecmp(ge->target, extra);
77}
78
79static int
80delete_shun_for_p(UNUSED_ARG(void *key), void *data, void *extra)
81{
82 struct shun *ge = data;
83
84 if (!irccasecmp(ge->target, extra)) {
85 free_shun(ge);
86 return 1;
87 } else {
88 return 0;
89 }
90}
91
92static void
93shun_expire(UNUSED_ARG(void *data))
94{
95 time_t stopped;
96 void *wraa;
97
98 stopped = 0;
99 while (heap_size(shun_heap)) {
100 heap_peek(shun_heap, 0, &wraa);
101 stopped = ((struct shun*)wraa)->expires;
102 if (stopped > now)
103 break;
104 heap_pop(shun_heap);
105 free_shun(wraa);
106 }
107 if (heap_size(shun_heap))
108 timeq_add(stopped, shun_expire, NULL);
109}
110
111int
112shun_remove(const char *target, int announce)
113{
114 int res = dict_find(shun_dict, target, NULL) ? 1 : 0;
115 if (heap_remove_pred(shun_heap, delete_shun_for_p, (char*)target)) {
116 void *argh;
117 struct shun *new_first;
118 heap_peek(shun_heap, 0, &argh);
119 if (argh) {
120 new_first = argh;
121 timeq_del(0, shun_expire, 0, TIMEQ_IGNORE_WHEN|TIMEQ_IGNORE_DATA);
122 timeq_add(new_first->expires, shun_expire, 0);
123 }
124 }
d914d1cb 125 if (announce)
126 irc_unshun(target);
127 return res;
128}
129
130struct shun *
995043b4 131shun_add(const char *issuer, const char *target, unsigned long duration, const char *reason, time_t issued, int announce)
d914d1cb 132{
133 struct shun *ent;
134 struct shun *prev_first;
135 void *argh;
136
137 heap_peek(shun_heap, 0, &argh);
138 prev_first = argh;
139 ent = dict_find(shun_dict, target, NULL);
140 if (ent) {
141 heap_remove_pred(shun_heap, shun_for_p, (char*)target);
142 if (ent->expires < (time_t)(now + duration))
143 ent->expires = now + duration;
144 } else {
145 ent = malloc(sizeof(*ent));
146 ent->issued = issued;
147 ent->issuer = strdup(issuer);
148 ent->target = strdup(target);
149 ent->expires = now + duration;
150 ent->reason = strdup(reason);
151 dict_insert(shun_dict, ent->target, ent);
152 }
153 heap_insert(shun_heap, ent, ent);
154 if (!prev_first || (ent->expires < prev_first->expires)) {
155 timeq_del(0, shun_expire, 0, TIMEQ_IGNORE_WHEN|TIMEQ_IGNORE_DATA);
156 timeq_add(ent->expires, shun_expire, 0);
157 }
158 if (announce)
159 irc_shun(NULL, ent);
160 return ent;
161}
162
163static char *
164shun_alternate_target(const char *target)
165{
166 const char *hostname;
d914d1cb 167
168 /* If no host part, bail. */
169 if (!(hostname = strchr(target, '@')))
170 return NULL;
171 /* If host part contains wildcards, bail. */
172 if (hostname[strcspn(hostname, "*?/")])
173 return NULL;
2f61d1d7 174 /* Get parsed address and canonical name for host. */
175#if 0
176 irc_in_addr_t in; /* move this to the right place */
177 if (irc_pton(&in, NULL, hostname+1)) {
178 if (getnameinfo(/*TODO*/))
179 return NULL;
180 } else if (!getaddrinfo(/*TODO*/)) {
181 } else return NULL;
182#else
183 return NULL;
184#endif
d914d1cb 185}
186
187struct shun *
188shun_find(const char *target)
189{
190 struct shun *res;
191 dict_iterator_t it;
192 char *alt_target;
193
194 res = dict_find(shun_dict, target, NULL);
195 if (res)
196 return res;
197 /* Stock ircu requires BADCHANs to match exactly. */
198 if ((target[0] == '#') || (target[0] == '&'))
199 return NULL;
200 else if (target[strcspn(target, "*?")]) {
201 /* Wildcard: do an obnoxiously long search. */
202 for (it = dict_first(shun_dict); it; it = iter_next(it)) {
203 res = iter_data(it);
204 if (match_ircglob(target, res->target))
205 return res;
206 }
207 }
208 /* See if we can resolve the hostname part of the mask. */
209 if ((alt_target = shun_alternate_target(target))) {
210 res = shun_find(alt_target);
211 free(alt_target);
212 return res;
213 }
214 return NULL;
215}
216
217static int
218shun_refresh_helper(UNUSED_ARG(void *key), void *data, void *extra)
219{
220 struct shun *ge = data;
221 irc_shun(extra, ge);
222 return 0;
223}
224
225void
226shun_refresh_server(struct server *srv)
227{
228 heap_remove_pred(shun_heap, shun_refresh_helper, srv);
229}
230
231void
232shun_refresh_all(void)
233{
234 heap_remove_pred(shun_heap, shun_refresh_helper, 0);
235}
236
237unsigned int
238shun_count(void)
239{
240 return dict_size(shun_dict);
241}
242
243static int
244shun_add_record(const char *key, void *data, UNUSED_ARG(void *extra))
245{
246 struct record_data *rd = data;
247 const char *issuer, *reason, *dstr;
995043b4 248 time_t issued, expiration;
d914d1cb 249
250 if (!(reason = database_get_data(rd->d.object, KEY_REASON, RECDB_QSTRING))) {
251 log_module(MAIN_LOG, LOG_ERROR, "Missing reason for shun %s", key);
252 return 0;
253 }
254 if (!(dstr = database_get_data(rd->d.object, KEY_EXPIRES, RECDB_QSTRING))) {
255 log_module(MAIN_LOG, LOG_ERROR, "Missing expiration for shun %s", key);
256 return 0;
257 }
258 expiration = strtoul(dstr, NULL, 0);
259 if ((dstr = database_get_data(rd->d.object, KEY_ISSUED, RECDB_QSTRING))) {
260 issued = strtoul(dstr, NULL, 0);
261 } else {
262 issued = now;
263 }
264 if (!(issuer = database_get_data(rd->d.object, KEY_ISSUER, RECDB_QSTRING))) {
265 issuer = "<unknown>";
266 }
267 if (expiration > now)
995043b4 268 shun_add(issuer, key, expiration - now, reason, issued, 0);
d914d1cb 269 return 0;
270}
271
272static int
273shun_saxdb_read(struct dict *db)
274{
275 return dict_foreach(db, shun_add_record, 0) != NULL;
276}
277
278static int
279shun_write_entry(UNUSED_ARG(void *key), void *data, void *extra)
280{
281 struct shun *ent = data;
282 struct saxdb_context *ctx = extra;
283
284 saxdb_start_record(ctx, ent->target, 0);
285 saxdb_write_int(ctx, KEY_EXPIRES, ent->expires);
286 saxdb_write_int(ctx, KEY_ISSUED, ent->issued);
287 saxdb_write_string(ctx, KEY_REASON, ent->reason);
288 saxdb_write_string(ctx, KEY_ISSUER, ent->issuer);
289 saxdb_end_record(ctx);
290 return 0;
291}
292
293static int
294shun_saxdb_write(struct saxdb_context *ctx)
295{
296 heap_remove_pred(shun_heap, shun_write_entry, ctx);
297 return 0;
298}
299
300static void
30874d66 301shun_db_cleanup(UNUSED_ARG(void *extra))
d914d1cb 302{
303 heap_delete(shun_heap);
304 dict_delete(shun_dict);
305}
306
307void
308shun_init(void)
309{
310 shun_heap = heap_new(shun_comparator);
311 shun_dict = dict_new();
312 dict_set_free_data(shun_dict, free_shun_from_dict);
313 saxdb_register("shun", shun_saxdb_read, shun_saxdb_write);
30874d66 314 reg_exit_func(shun_db_cleanup, NULL);
d914d1cb 315}
316
317struct shun_discrim *
318shun_discrim_create(struct userNode *user, struct userNode *src, unsigned int argc, char *argv[])
319{
320 unsigned int i;
321 struct shun_discrim *discrim;
322
323 discrim = calloc(1, sizeof(*discrim));
995043b4 324 discrim->max_issued = now;
d914d1cb 325 discrim->limit = 50;
326
327 for (i=0; i<argc; i++) {
328 if (i + 2 > argc) {
329 send_message(user, src, "MSG_MISSING_PARAMS", argv[i]);
330 goto fail;
331 } else if (!irccasecmp(argv[i], "mask") || !irccasecmp(argv[i], "host")) {
332 if (!irccasecmp(argv[++i], "exact"))
333 discrim->target_mask_type = SEXACT;
334 else if (!irccasecmp(argv[i], "subset"))
335 discrim->target_mask_type = SSUBSET;
336 else if (!irccasecmp(argv[i], "superset"))
337 discrim->target_mask_type = SSUPERSET;
338 else
339 discrim->target_mask_type = SSUBSET, i--;
340 if (++i == argc) {
341 send_message(user, src, "MSG_MISSING_PARAMS", argv[i-1]);
342 goto fail;
343 }
344 if (!is_shun(argv[i]) && !IsChannelName(argv[i])) {
345 send_message(user, src, "MSG_INVALID_SHUN", argv[i]);
346 goto fail;
347 }
348 discrim->target_mask = argv[i];
349 discrim->alt_target_mask = shun_alternate_target(discrim->target_mask);
350 } else if (!irccasecmp(argv[i], "limit"))
351 discrim->limit = strtoul(argv[++i], NULL, 0);
352 else if (!irccasecmp(argv[i], "reason"))
353 discrim->reason_mask = argv[++i];
354 else if (!irccasecmp(argv[i], "issuer"))
355 discrim->issuer_mask = argv[++i];
356 else if (!irccasecmp(argv[i], "after"))
357 discrim->min_expire = now + ParseInterval(argv[++i]);
358 else if (!irccasecmp(argv[i], "before"))
359 discrim->max_issued = now - ParseInterval(argv[++i]);
995043b4 360 else {
d914d1cb 361 send_message(user, src, "MSG_INVALID_CRITERIA", argv[i]);
362 goto fail;
363 }
364 }
365 return discrim;
366 fail:
367 free(discrim->alt_target_mask);
368 free(discrim);
369 return NULL;
370}
371
372struct shun_search {
373 struct shun_discrim *discrim;
374 shun_search_func func;
375 void *data;
376 unsigned int hits;
377};
378
379static int
380shun_discrim_match(struct shun *shun, struct shun_discrim *discrim)
381{
382 if ((discrim->issuer_mask && !match_ircglob(shun->issuer, discrim->issuer_mask))
383 || (discrim->reason_mask && !match_ircglob(shun->reason, discrim->reason_mask))
384 || (discrim->target_mask
385 && (((discrim->target_mask_type == SSUBSET)
386 && !match_ircglobs(discrim->target_mask, shun->target)
387 && (!discrim->alt_target_mask
388 || !match_ircglobs(discrim->alt_target_mask, shun->target)))
389 || ((discrim->target_mask_type == SEXACT)
390 && irccasecmp(discrim->target_mask, shun->target)
391 && (!discrim->alt_target_mask
392 || !irccasecmp(discrim->alt_target_mask, shun->target)))
393 || ((discrim->target_mask_type == SSUPERSET)
394 && !match_ircglobs(shun->target, discrim->target_mask)
395 && (!discrim->alt_target_mask
396 || !match_ircglobs(discrim->alt_target_mask, shun->target)))))
397 || (discrim->max_issued < shun->issued)
995043b4 398 || (discrim->min_expire > shun->expires)) {
d914d1cb 399 return 0;
400 }
401 return 1;
402}
403
404static int
405shun_search_helper(UNUSED_ARG(void *key), void *data, void *extra)
406{
407 struct shun *shun = data;
408 struct shun_search *search = extra;
409
410 if (shun_discrim_match(shun, search->discrim)
411 && (search->hits++ < search->discrim->limit)) {
412 search->func(shun, search->data);
413 }
414 return 0;
415}
416
417unsigned int
418shun_discrim_search(struct shun_discrim *discrim, shun_search_func gsf, void *data)
419{
420 struct shun_search search;
421 search.discrim = discrim;
422 search.func = gsf;
423 search.data = data;
424 search.hits = 0;
425 heap_remove_pred(shun_heap, shun_search_helper, &search);
426 return search.hits;
427}
995043b4 428