#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* */
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))
{
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);
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);
{
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);
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 = "<unknown>";
- }
- 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;
}
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);
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; i++) {
if (i + 2 > argc) {
} 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;
|| (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;
#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;
};
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);
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);
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. */
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 *
{ "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." },
"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)
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;
}
} 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);
}
}
}
char issued[INTERVALLEN];
char lastmod[INTERVALLEN];
char expires[INTERVALLEN];
+ char lifetime[INTERVALLEN];
intervalString(issued, now - gline->issued, xtra->user->handle_info);
if (gline->lastmod)
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) {
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
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,
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;
}
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);