]>
jfr.im git - irc/evilnet/x3.git/blob - src/gline.c
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_ISSUER "issuer"
44 #define KEY_ISSUED "issued"
46 static heap_t gline_heap
; /* key: expiry time, data: struct gline_entry* */
47 static dict_t gline_dict
; /* key: target, data: struct gline_entry* */
50 gline_comparator(const void *a
, const void *b
)
52 const struct gline
*ga
=a
, *gb
=b
;
53 return ga
->expires
- gb
->expires
;
57 free_gline_from_dict(void *data
)
59 struct gline
*ent
= data
;
67 free_gline(struct gline
*ent
)
69 dict_remove(gline_dict
, ent
->target
);
73 gline_for_p(UNUSED_ARG(void *key
), void *data
, void *extra
)
75 struct gline
*ge
= data
;
76 return !irccasecmp(ge
->target
, extra
);
80 delete_gline_for_p(UNUSED_ARG(void *key
), void *data
, void *extra
)
82 struct gline
*ge
= data
;
84 if (!irccasecmp(ge
->target
, extra
)) {
93 gline_expire(UNUSED_ARG(void *data
))
99 while (heap_size(gline_heap
)) {
100 heap_peek(gline_heap
, 0, &wraa
);
101 stopped
= ((struct gline
*)wraa
)->expires
;
104 heap_pop(gline_heap
);
107 if (heap_size(gline_heap
))
108 timeq_add(stopped
, gline_expire
, NULL
);
112 gline_remove(const char *target
, int announce
)
114 int res
= dict_find(gline_dict
, target
, NULL
) ? 1 : 0;
115 if (heap_remove_pred(gline_heap
, delete_gline_for_p
, (char*)target
)) {
117 struct gline
*new_first
;
118 heap_peek(gline_heap
, 0, &argh
);
121 timeq_del(0, gline_expire
, 0, TIMEQ_IGNORE_WHEN
|TIMEQ_IGNORE_DATA
);
122 timeq_add(new_first
->expires
, gline_expire
, 0);
125 #ifdef WITH_PROTOCOL_BAHAMUT
126 /* Bahamut is sort of lame: It permanently remembers any AKILLs
127 * with durations longer than a day, and will never auto-expire
128 * them. So when the time comes, we'd better remind it. */
137 gline_add(const char *issuer
, const char *target
, unsigned long duration
, const char *reason
, time_t issued
, int announce
)
140 struct gline
*prev_first
;
143 heap_peek(gline_heap
, 0, &argh
);
145 ent
= dict_find(gline_dict
, target
, NULL
);
147 heap_remove_pred(gline_heap
, gline_for_p
, (char*)target
);
148 if (ent
->expires
< (time_t)(now
+ duration
))
149 ent
->expires
= now
+ duration
;
151 ent
= malloc(sizeof(*ent
));
152 ent
->issued
= issued
;
153 ent
->issuer
= strdup(issuer
);
154 ent
->target
= strdup(target
);
155 ent
->expires
= now
+ duration
;
156 ent
->reason
= strdup(reason
);
157 dict_insert(gline_dict
, ent
->target
, ent
);
159 heap_insert(gline_heap
, ent
, ent
);
160 if (!prev_first
|| (ent
->expires
< prev_first
->expires
)) {
161 timeq_del(0, gline_expire
, 0, TIMEQ_IGNORE_WHEN
|TIMEQ_IGNORE_DATA
);
162 timeq_add(ent
->expires
, gline_expire
, 0);
165 irc_gline(NULL
, ent
);
170 gline_alternate_target(const char *target
)
172 const char *hostname
;
176 /* If no host part, bail. */
177 if (!(hostname
= strchr(target
, '@')))
179 /* If host part contains wildcards, bail. */
180 if (hostname
[strcspn(hostname
, "*?/")])
182 /* If host part looks like an IP, parse it that way. */
183 if (!hostname
[strspn(hostname
+1, "0123456789.")+1]) {
186 if (inet_aton(hostname
+1, &in
)
187 && (he
= gethostbyaddr((char*)&in
, sizeof(in
), AF_INET
))) {
188 res
= malloc((hostname
- target
) + 2 + strlen(he
->h_name
));
189 sprintf(res
, "%.*s@%s", hostname
- target
, target
, he
->h_name
);
193 } else if (getipbyname(hostname
+1, &ip
)) {
194 res
= malloc((hostname
- target
) + 18);
195 sprintf(res
, "%.*s@%lu.%lu.%lu.%lu", hostname
- target
, target
, ip
& 255, (ip
>> 8) & 255, (ip
>> 16) & 255, (ip
>> 24) & 255);
202 gline_find(const char *target
)
208 res
= dict_find(gline_dict
, target
, NULL
);
211 /* Stock ircu requires BADCHANs to match exactly. */
212 if ((target
[0] == '#') || (target
[0] == '&'))
214 else if (target
[strcspn(target
, "*?")]) {
215 /* Wildcard: do an obnoxiously long search. */
216 for (it
= dict_first(gline_dict
); it
; it
= iter_next(it
)) {
218 if (match_ircglob(target
, res
->target
))
222 /* See if we can resolve the hostname part of the mask. */
223 if ((alt_target
= gline_alternate_target(target
))) {
224 res
= gline_find(alt_target
);
232 gline_refresh_helper(UNUSED_ARG(void *key
), void *data
, void *extra
)
234 struct gline
*ge
= data
;
235 irc_gline(extra
, ge
);
240 gline_refresh_server(struct server
*srv
)
242 heap_remove_pred(gline_heap
, gline_refresh_helper
, srv
);
246 gline_refresh_all(void)
248 heap_remove_pred(gline_heap
, gline_refresh_helper
, 0);
254 return dict_size(gline_dict
);
258 gline_add_record(const char *key
, void *data
, UNUSED_ARG(void *extra
))
260 struct record_data
*rd
= data
;
261 const char *issuer
, *reason
, *dstr
;
262 time_t issued
, expiration
;
264 if (!(reason
= database_get_data(rd
->d
.object
, KEY_REASON
, RECDB_QSTRING
))) {
265 log_module(MAIN_LOG
, LOG_ERROR
, "Missing reason for gline %s", key
);
268 if (!(dstr
= database_get_data(rd
->d
.object
, KEY_EXPIRES
, RECDB_QSTRING
))) {
269 log_module(MAIN_LOG
, LOG_ERROR
, "Missing expiration for gline %s", key
);
272 expiration
= strtoul(dstr
, NULL
, 0);
273 if ((dstr
= database_get_data(rd
->d
.object
, KEY_ISSUED
, RECDB_QSTRING
))) {
274 issued
= strtoul(dstr
, NULL
, 0);
278 if (!(issuer
= database_get_data(rd
->d
.object
, KEY_ISSUER
, RECDB_QSTRING
))) {
279 issuer
= "<unknown>";
281 if (expiration
> now
)
282 gline_add(issuer
, key
, expiration
- now
, reason
, issued
, 0);
287 gline_saxdb_read(struct dict
*db
)
289 return dict_foreach(db
, gline_add_record
, 0) != NULL
;
293 gline_write_entry(UNUSED_ARG(void *key
), void *data
, void *extra
)
295 struct gline
*ent
= data
;
296 struct saxdb_context
*ctx
= extra
;
298 saxdb_start_record(ctx
, ent
->target
, 0);
299 saxdb_write_int(ctx
, KEY_EXPIRES
, ent
->expires
);
300 saxdb_write_int(ctx
, KEY_ISSUED
, ent
->issued
);
301 saxdb_write_string(ctx
, KEY_REASON
, ent
->reason
);
302 saxdb_write_string(ctx
, KEY_ISSUER
, ent
->issuer
);
303 saxdb_end_record(ctx
);
308 gline_saxdb_write(struct saxdb_context
*ctx
)
310 heap_remove_pred(gline_heap
, gline_write_entry
, ctx
);
315 gline_db_cleanup(void)
317 heap_delete(gline_heap
);
318 dict_delete(gline_dict
);
324 gline_heap
= heap_new(gline_comparator
);
325 gline_dict
= dict_new();
326 dict_set_free_data(gline_dict
, free_gline_from_dict
);
327 saxdb_register("gline", gline_saxdb_read
, gline_saxdb_write
);
328 reg_exit_func(gline_db_cleanup
);
331 struct gline_discrim
*
332 gline_discrim_create(struct userNode
*user
, struct userNode
*src
, unsigned int argc
, char *argv
[])
335 struct gline_discrim
*discrim
;
337 discrim
= calloc(1, sizeof(*discrim
));
338 discrim
->max_issued
= now
;
341 for (i
=0; i
<argc
; i
++) {
343 send_message(user
, src
, "MSG_MISSING_PARAMS", argv
[i
]);
345 } else if (!irccasecmp(argv
[i
], "mask") || !irccasecmp(argv
[i
], "host")) {
346 if (!irccasecmp(argv
[++i
], "exact"))
347 discrim
->target_mask_type
= EXACT
;
348 else if (!irccasecmp(argv
[i
], "subset"))
349 discrim
->target_mask_type
= SUBSET
;
350 else if (!irccasecmp(argv
[i
], "superset"))
351 discrim
->target_mask_type
= SUPERSET
;
353 discrim
->target_mask_type
= SUBSET
, i
--;
355 send_message(user
, src
, "MSG_MISSING_PARAMS", argv
[i
-1]);
358 if (!is_gline(argv
[i
]) && !IsChannelName(argv
[i
])) {
359 send_message(user
, src
, "MSG_INVALID_GLINE", argv
[i
]);
362 discrim
->target_mask
= argv
[i
];
363 discrim
->alt_target_mask
= gline_alternate_target(discrim
->target_mask
);
364 } else if (!irccasecmp(argv
[i
], "limit"))
365 discrim
->limit
= strtoul(argv
[++i
], NULL
, 0);
366 else if (!irccasecmp(argv
[i
], "reason"))
367 discrim
->reason_mask
= argv
[++i
];
368 else if (!irccasecmp(argv
[i
], "issuer"))
369 discrim
->issuer_mask
= argv
[++i
];
370 else if (!irccasecmp(argv
[i
], "after"))
371 discrim
->min_expire
= now
+ ParseInterval(argv
[++i
]);
372 else if (!irccasecmp(argv
[i
], "before"))
373 discrim
->max_issued
= now
- ParseInterval(argv
[++i
]);
375 send_message(user
, src
, "MSG_INVALID_CRITERIA", argv
[i
]);
381 free(discrim
->alt_target_mask
);
386 struct gline_search
{
387 struct gline_discrim
*discrim
;
388 gline_search_func func
;
394 gline_discrim_match(struct gline
*gline
, struct gline_discrim
*discrim
)
396 if ((discrim
->issuer_mask
&& !match_ircglob(gline
->issuer
, discrim
->issuer_mask
))
397 || (discrim
->reason_mask
&& !match_ircglob(gline
->reason
, discrim
->reason_mask
))
398 || (discrim
->target_mask
399 && (((discrim
->target_mask_type
== SUBSET
)
400 && !match_ircglobs(discrim
->target_mask
, gline
->target
)
401 && (!discrim
->alt_target_mask
402 || !match_ircglobs(discrim
->alt_target_mask
, gline
->target
)))
403 || ((discrim
->target_mask_type
== EXACT
)
404 && irccasecmp(discrim
->target_mask
, gline
->target
)
405 && (!discrim
->alt_target_mask
406 || !irccasecmp(discrim
->alt_target_mask
, gline
->target
)))
407 || ((discrim
->target_mask_type
== SUPERSET
)
408 && !match_ircglobs(gline
->target
, discrim
->target_mask
)
409 && (!discrim
->alt_target_mask
410 || !match_ircglobs(discrim
->alt_target_mask
, gline
->target
)))))
411 || (discrim
->max_issued
< gline
->issued
)
412 || (discrim
->min_expire
> gline
->expires
)) {
419 gline_search_helper(UNUSED_ARG(void *key
), void *data
, void *extra
)
421 struct gline
*gline
= data
;
422 struct gline_search
*search
= extra
;
424 if (gline_discrim_match(gline
, search
->discrim
)
425 && (search
->hits
++ < search
->discrim
->limit
)) {
426 search
->func(gline
, search
->data
);
432 gline_discrim_search(struct gline_discrim
*discrim
, gline_search_func gsf
, void *data
)
434 struct gline_search search
;
435 search
.discrim
= discrim
;
439 heap_remove_pred(gline_heap
, gline_search_helper
, &search
);