From 9904260b83596e5e9a8a15eeaa31a0c9007625f9 Mon Sep 17 00:00:00 2001 From: Michael Poole Date: Wed, 17 Mar 2010 22:11:00 -0400 Subject: [PATCH] Support G-line lifetimes. This involves using the old "expiration" as when the G-line becomes inactive, and adding a new "lifetime" that is the latest of all the expiration times that the G-line has ever had. For convenience, gline_add() will compare its new "lifetime" argument with its calculated expiration time -- so callers can safely pass 0 as the lifetime. src/gline.h (struct gline): Add the lifetime field. Add Doxygen comments for future reference. (struct gline_discrim): Add bounds for the lifetime field. (gline_add): Add lifetime argument. src/gline.c (KEY_LIFETIME): New database key. (delete_gline_for_p): Delete. (gline_expire): Use lifetime to determine whether a G-line expired. (gline_remove): Simplify greatly, because we don't actually remove G-lines any more (we just mark them immediately expired). (gline_add): Add parameter for lifetime, and use it. (gline_add_record): Parse lifetime field if it is present. Simplify the parsing of the issued field. (gline_write_entry): Write the lifetime. (gline_discrim_create): Use ULONG_MAX rather than INT_MAX for maximum timestamps. Parse "lifetime" arguments. (gline_discrim_match): Check the lifetime bounds. src/mod-blacklist.c (dnsbl_hit): Pass 0 lifetime for gline_add(). (blacklist_check_user): Likewise. src/mod-sockcheck.c (sockcheck_issue_gline): Likewise. src/opserv.c (OSMSG_GTRACE_FORMAT): Add lifetime field. (opserv_block): Pass 0 lifetime for gline_add(). (cmd_gline): Likewise. (opserv_new_user_check): Likewise. (gtrace_print_func): Format the lifetime. src/proto-p10.c (irc_gline): Include the lifetime in the correct place. (irc_ungline): Include the current timestamp (last modified time). (cmd_num_gline): When the lifetime argument is given, parse it. (cmd_gline): Likewise. Also implement the normal ircu check for expiration as an absolute versus relative time. --- src/gline.c | 112 ++++++++++++++++++++++++-------------------- src/gline.h | 14 +++++- src/mod-blacklist.c | 4 +- src/mod-sockcheck.c | 2 +- src/opserv.c | 12 +++-- src/proto-p10.c | 24 +++++++--- 6 files changed, 101 insertions(+), 67 deletions(-) diff --git a/src/gline.c b/src/gline.c index 3ca7e1a..0b2ed9b 100644 --- a/src/gline.c +++ b/src/gline.c @@ -43,6 +43,7 @@ #define KEY_LASTMOD "lastmod" #define KEY_ISSUER "issuer" #define KEY_ISSUED "issued" +#define KEY_LIFETIME "lifetime" static heap_t gline_heap; /* key: expiry time, data: struct gline_entry* */ static dict_t gline_dict; /* key: target, data: struct gline_entry* */ @@ -77,19 +78,6 @@ gline_for_p(UNUSED_ARG(void *key), void *data, void *extra) return !irccasecmp(ge->target, extra); } -static int -delete_gline_for_p(UNUSED_ARG(void *key), void *data, void *extra) -{ - struct gline *ge = data; - - if (!irccasecmp(ge->target, extra)) { - free_gline(ge); - return 1; - } else { - return 0; - } -} - static void gline_expire(UNUSED_ARG(void *data)) { @@ -99,7 +87,7 @@ gline_expire(UNUSED_ARG(void *data)) stopped = 0; while (heap_size(gline_heap)) { heap_peek(gline_heap, 0, &wraa); - stopped = ((struct gline*)wraa)->expires; + stopped = ((struct gline*)wraa)->lifetime; if (stopped > now) break; heap_pop(gline_heap); @@ -112,58 +100,61 @@ gline_expire(UNUSED_ARG(void *data)) int gline_remove(const char *target, int announce) { - int res = dict_find(gline_dict, target, NULL) ? 1 : 0; - if (heap_remove_pred(gline_heap, delete_gline_for_p, (char*)target)) { - void *argh; - struct gline *new_first; - heap_peek(gline_heap, 0, &argh); - if (argh) { - new_first = argh; - timeq_del(0, gline_expire, 0, TIMEQ_IGNORE_WHEN|TIMEQ_IGNORE_DATA); - timeq_add(new_first->expires, gline_expire, 0); - } - } -#ifdef WITH_PROTOCOL_BAHAMUT - /* Bahamut is sort of lame: It permanently remembers any AKILLs - * with durations longer than a day, and will never auto-expire - * them. So when the time comes, we'd better remind it. */ - announce = 1; -#endif + struct gline *gl; + + gl = dict_find(gline_dict, target, NULL); + if (gl != NULL) + gl->expires = now; if (announce) irc_ungline(target); - return res; + return gl != NULL; } struct gline * -gline_add(const char *issuer, const char *target, unsigned long duration, const char *reason, unsigned long issued, unsigned long lastmod, int announce) +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) { struct gline *ent; struct gline *prev_first; void *argh; + unsigned long expires; heap_peek(gline_heap, 0, &argh); prev_first = argh; + expires = now + duration; + if (lifetime < expires) + lifetime = expires; ent = dict_find(gline_dict, target, NULL); if (ent) { heap_remove_pred(gline_heap, gline_for_p, (char*)target); - if (ent->expires < now + duration) - ent->expires = now + duration; + if (ent->expires != expires) + ent->expires = expires; + if (ent->lifetime < lifetime) + ent->lifetime = lifetime; if (ent->lastmod < lastmod) ent->lastmod = lastmod; + if (strcmp(ent->issuer, issuer)) { + free(ent->issuer); + ent->issuer = strdup(issuer); + } + if (strcmp(ent->reason, reason)) { + free(ent->reason); + ent->reason = strdup(reason); + } } else { ent = malloc(sizeof(*ent)); ent->issued = issued; ent->lastmod = lastmod; ent->issuer = strdup(issuer); ent->target = strdup(target); - ent->expires = now + duration; + ent->expires = expires; + ent->lifetime = lifetime; ent->reason = strdup(reason); dict_insert(gline_dict, ent->target, ent); } heap_insert(gline_heap, ent, ent); - if (!prev_first || (ent->expires < prev_first->expires)) { + if (!prev_first || (ent->lifetime < prev_first->lifetime)) { timeq_del(0, gline_expire, 0, TIMEQ_IGNORE_WHEN|TIMEQ_IGNORE_DATA); - timeq_add(ent->expires, gline_expire, 0); + timeq_add(ent->lifetime, gline_expire, 0); } if (announce) irc_gline(NULL, ent); @@ -255,7 +246,7 @@ gline_add_record(const char *key, void *data, UNUSED_ARG(void *extra)) { struct record_data *rd = data; const char *issuer, *reason, *dstr; - unsigned long issued, expiration, lastmod; + unsigned long issued, expiration, lastmod, lifetime; if (!(reason = database_get_data(rd->d.object, KEY_REASON, RECDB_QSTRING))) { log_module(MAIN_LOG, LOG_ERROR, "Missing reason for gline %s", key); @@ -266,18 +257,16 @@ gline_add_record(const char *key, void *data, UNUSED_ARG(void *extra)) return 0; } expiration = strtoul(dstr, NULL, 0); + dstr = database_get_data(rd->d.object, KEY_LIFETIME, RECDB_QSTRING); + lifetime = dstr ? strtoul(dstr, NULL, 0) : expiration; dstr = database_get_data(rd->d.object, KEY_LASTMOD, RECDB_QSTRING); lastmod = dstr ? strtoul(dstr, NULL, 0) : 0; - if ((dstr = database_get_data(rd->d.object, KEY_ISSUED, RECDB_QSTRING))) { - issued = strtoul(dstr, NULL, 0); - } else { - issued = now; - } - if (!(issuer = database_get_data(rd->d.object, KEY_ISSUER, RECDB_QSTRING))) { + dstr = database_get_data(rd->d.object, KEY_ISSUED, RECDB_QSTRING); + issued = dstr ? strtoul(dstr, NULL, 0) : now; + if (!(issuer = database_get_data(rd->d.object, KEY_ISSUER, RECDB_QSTRING))) issuer = ""; - } - if (expiration > now) - gline_add(issuer, key, expiration - now, reason, issued, lastmod, 0); + if (lifetime > now) + gline_add(issuer, key, expiration - now, reason, issued, lastmod, lifetime, 0); return 0; } @@ -296,6 +285,7 @@ gline_write_entry(UNUSED_ARG(void *key), void *data, void *extra) saxdb_start_record(ctx, ent->target, 0); saxdb_write_int(ctx, KEY_EXPIRES, ent->expires); saxdb_write_int(ctx, KEY_ISSUED, ent->issued); + saxdb_write_int(ctx, KEY_LIFETIME, ent->lifetime); if (ent->lastmod) saxdb_write_int(ctx, KEY_LASTMOD, ent->lastmod); saxdb_write_string(ctx, KEY_REASON, ent->reason); @@ -336,8 +326,9 @@ gline_discrim_create(struct userNode *user, struct userNode *src, unsigned int a discrim = calloc(1, sizeof(*discrim)); discrim->limit = 50; - discrim->max_issued = INT_MAX; - discrim->max_lastmod = INT_MAX; + discrim->max_issued = ULONG_MAX; + discrim->max_lastmod = ULONG_MAX; + discrim->max_lifetime = ULONG_MAX; for (i=0; i argc) { @@ -389,6 +380,23 @@ gline_discrim_create(struct userNode *user, struct userNode *src, unsigned int a } else { discrim->min_lastmod = now - ParseInterval(cmp + 2); } + } else if (!irccasecmp(argv[i], "lifetime")) { + const char *cmp = argv[++i]; + if (cmp[0] == '<') { + if (cmp[1] == '=') { + discrim->min_lifetime = now - ParseInterval(cmp + 2); + } else { + discrim->min_lifetime = now - (ParseInterval(cmp + 1) - 1); + } + } else if (cmp[0] == '>') { + if (cmp[1] == '=') { + discrim->max_lifetime = now - ParseInterval(cmp + 2); + } else { + discrim->max_lifetime = now - (ParseInterval(cmp + 1) - 1); + } + } else { + discrim->min_lifetime = now - ParseInterval(cmp + 2); + } } else { send_message(user, src, "MSG_INVALID_CRITERIA", argv[i]); goto fail; @@ -429,7 +437,9 @@ gline_discrim_match(struct gline *gline, struct gline_discrim *discrim) || (discrim->max_issued < gline->issued) || (discrim->min_expire > gline->expires) || (discrim->min_lastmod > gline->lastmod) - || (discrim->max_lastmod < gline->lastmod)) { + || (discrim->max_lastmod < gline->lastmod) + || (discrim->min_lifetime > gline->lifetime) + || (discrim->max_lifetime < gline->lifetime)) { return 0; } return 1; diff --git a/src/gline.h b/src/gline.h index d81a2e6..65f17a2 100644 --- a/src/gline.h +++ b/src/gline.h @@ -24,11 +24,21 @@ #include "hash.h" struct gline { + /** When the G-line was first created. */ unsigned long issued; + /** When the G-line was last modified. */ unsigned long lastmod; + /** When the G-line becomes ineffective. */ unsigned long expires; + /** When the G-line may be forgotten (the maximum "expires" value + * from any modification period). + */ + unsigned long lifetime; + /** Account or nick name of the person creating the G-line. */ char *issuer; + /** user@host mask covered by the G-line. */ char *target; + /** What to tell affected users. */ char *reason; }; @@ -43,10 +53,12 @@ struct gline_discrim { unsigned long min_expire; unsigned long min_lastmod; unsigned long max_lastmod; + unsigned long min_lifetime; + unsigned long max_lifetime; }; void gline_init(void); -struct gline *gline_add(const char *issuer, const char *target, unsigned long duration, const char *reason, unsigned long issued, unsigned long lastmod, int announce); +struct gline *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); struct gline *gline_find(const char *target); int gline_remove(const char *target, int announce); void gline_refresh_server(struct server *srv); diff --git a/src/mod-blacklist.c b/src/mod-blacklist.c index 0e4b58b..e202f73 100644 --- a/src/mod-blacklist.c +++ b/src/mod-blacklist.c @@ -155,7 +155,7 @@ dnsbl_hit(struct sar_request *req, struct dns_header *hdr, struct dns_rr *rr, un target[0] = '*'; target[1] = '@'; strcpy(target + 2, data->client_ip); - gline_add(self->name, target, zone->duration, reason, now, now, 1); + gline_add(self->name, target, zone->duration, reason, now, now, 0, 1); } } free(txt); @@ -194,7 +194,7 @@ blacklist_check_user(struct userNode *user) target[1] = '@'; strcpy(target + 2, host); /* We do not prepend AUTO here so it can be done in the blacklist file. */ - gline_add(self->name, target, conf.gline_duration, reason, now, now, 1); + gline_add(self->name, target, conf.gline_duration, reason, now, now, 0, 1); } /* Figure out the base part of a DNS blacklist hostname. */ diff --git a/src/mod-sockcheck.c b/src/mod-sockcheck.c index 6652528..ab68da0 100644 --- a/src/mod-sockcheck.c +++ b/src/mod-sockcheck.c @@ -206,7 +206,7 @@ sockcheck_issue_gline(sockcheck_cache_info sci) char addr[IRC_NTOP_MAX_SIZE + 2] = {'*', '@', '\0'}; irc_ntop(addr + 2, sizeof(addr) - 2, &sci->addr); log_module(PC_LOG, LOG_INFO, "Issuing gline for client at %s: %s", addr + 2, sci->reason); - gline_add("ProxyCheck", addr, sockcheck_conf.gline_duration, sci->reason, now, now, 1); + gline_add("ProxyCheck", addr, sockcheck_conf.gline_duration, sci->reason, now, now, 0, 1); } static struct sockcheck_client * diff --git a/src/opserv.c b/src/opserv.c index d7bddd5..4bc6be2 100644 --- a/src/opserv.c +++ b/src/opserv.c @@ -201,7 +201,7 @@ static const struct message_entry msgtab[] = { { "OSMSG_GLINE_SEARCH_RESULTS", "The following glines were found:" }, { "OSMSG_LOG_SEARCH_RESULTS", "The following log entries were found:" }, { "OSMSG_GSYNC_RUNNING", "Synchronizing glines from %s." }, - { "OSMSG_GTRACE_FORMAT", "%s (issued %s by %s, lastmod %s, expires %s): %s" }, + { "OSMSG_GTRACE_FORMAT", "%1$s (issued %2$s ago by %3$s, lastmod %4$s ago, expires %5$s, lifetime %7$s): %6$s" }, { "OSMSG_GAG_APPLIED", "Gagged $b%s$b, affecting %d users." }, { "OSMSG_GAG_ADDED", "Gagged $b%s$b." }, { "OSMSG_REDUNDANT_GAG", "Gag $b%s$b is redundant." }, @@ -789,7 +789,7 @@ opserv_block(struct userNode *target, char *src_handle, char *reason, unsigned l "G-line requested by %s.", src_handle); if (!duration) duration = opserv_conf.block_gline_duration; - return gline_add(src_handle, mask, duration, reason, now, now, 1); + return gline_add(src_handle, mask, duration, reason, now, now, 0, 1); } static MODCMD_FUNC(cmd_block) @@ -863,7 +863,7 @@ static MODCMD_FUNC(cmd_gline) reply("MSG_INVALID_DURATION", argv[2]); return 0; } - gline = gline_add(user->handle_info->handle, argv[1], duration, reason, now, now, 1); + gline = gline_add(user->handle_info->handle, argv[1], duration, reason, now, now, 0, 1); reply("OSMSG_GLINE_ISSUED", gline->target); return 1; } @@ -1882,7 +1882,7 @@ opserv_new_user_check(struct userNode *user) } else if (ohi->clients.used > limit) { char target[IRC_NTOP_MAX_SIZE + 3] = { '*', '@', '\0' }; strcpy(target + 2, addr); - gline_add(opserv->nick, target, opserv_conf.clone_gline_duration, "AUTO Excessive connections from a single host.", now, now, 1); + gline_add(opserv->nick, target, opserv_conf.clone_gline_duration, "AUTO Excessive connections from a single host.", now, now, 0, 1); } } } @@ -3801,6 +3801,7 @@ gtrace_print_func(struct gline *gline, void *extra) char issued[INTERVALLEN]; char lastmod[INTERVALLEN]; char expires[INTERVALLEN]; + char lifetime[INTERVALLEN]; intervalString(issued, now - gline->issued, xtra->user->handle_info); if (gline->lastmod) @@ -3811,7 +3812,8 @@ gtrace_print_func(struct gline *gline, void *extra) intervalString(expires, gline->expires - now, xtra->user->handle_info); else strcpy(expires, "never"); - send_message(xtra->user, opserv, "OSMSG_GTRACE_FORMAT", gline->target, issued, gline->issuer, lastmod, expires, gline->reason); + intervalString(lifetime, gline->lifetime - now, xtra->user->handle_info); + send_message(xtra->user, opserv, "OSMSG_GTRACE_FORMAT", gline->target, issued, gline->issuer, lastmod, expires, gline->reason, lifetime); } static MODCMD_FUNC(cmd_stats_glines) { diff --git a/src/proto-p10.c b/src/proto-p10.c index 3d291a5..bb62c0b 100644 --- a/src/proto-p10.c +++ b/src/proto-p10.c @@ -655,11 +655,11 @@ void irc_gline(struct server *srv, struct gline *gline) { if (gline->lastmod) - putsock("%s " P10_GLINE " %s +%s %lu %lu :%s", - self->numeric, (srv ? srv->numeric : "*"), gline->target, (unsigned long)(gline->expires-now), (unsigned long)gline->lastmod, gline->reason); + putsock("%s " P10_GLINE " %s +%s %lu %lu %lu :%s", + self->numeric, (srv ? srv->numeric : "*"), gline->target, gline->expires, gline->lastmod, gline->lifetime, gline->reason); else putsock("%s " P10_GLINE " %s +%s %lu :%s", - self->numeric, (srv ? srv->numeric : "*"), gline->target, (unsigned long)(gline->expires-now), gline->reason); + self->numeric, (srv ? srv->numeric : "*"), gline->target, gline->expires, gline->reason); } void @@ -674,7 +674,7 @@ irc_settime(const char *srv_name_mask, unsigned long new_time) void irc_ungline(const char *mask) { - putsock("%s " P10_GLINE " * -%s", self->numeric, mask); + putsock("%s " P10_GLINE " * -%s %lu", self->numeric, mask, now); } /* Return negative if *(struct modeNode**)pa is "less than" pb, @@ -1494,10 +1494,13 @@ static CMD_FUNC(cmd_num_topic) static CMD_FUNC(cmd_num_gline) { unsigned long lastmod; + unsigned long lifetime; + if (argc < 6) return 0; lastmod = (argc > 5) ? strtoul(argv[5], NULL, 0) : 0; - gline_add(origin, argv[3], atoi(argv[4])-now, argv[argc - 1], now, lastmod, 0); + lifetime = (argc > 6) ? strtoul(argv[6], NULL, 0) : 0; + gline_add(origin, argv[3], atoi(argv[4])-now, argv[argc - 1], now, lastmod, lifetime, 0); return 1; } @@ -1612,15 +1615,22 @@ static CMD_FUNC(cmd_away) static CMD_FUNC(cmd_gline) { +#define PASTWATCH (5*365*24*3600) unsigned long lastmod; + unsigned long lifetime; + unsigned long expiration; if (argc < 3) return 0; if (argv[2][0] == '+') { if (argc < 5) return 0; - lastmod = (argc > 5) ? strtoul(argv[5], NULL, 0) : 0; - gline_add(origin, argv[2]+1, strtoul(argv[3], NULL, 0), argv[argc-1], now, lastmod, 0); + expiration = strtoul(argv[3], NULL, 10); + if (expiration < now - PASTWATCH) + expiration += now; + lastmod = (argc > 5) ? strtoul(argv[4], NULL, 10) : 0; + lifetime = (argc > 6) ? strtoul(argv[5], NULL, 10) : 0; + gline_add(origin, argv[2]+1, expiration - now, argv[argc-1], now, lastmod, lifetime, 0); return 1; } else if (argv[2][0] == '-') { gline_remove(argv[2]+1, 0); -- 2.20.1