From: Timo Sirainen Date: Tue, 10 Nov 2009 20:06:43 +0000 (-0500) Subject: anvil: Added support for penalty tracking. X-Git-Tag: 2.0.beta1~181 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=7e7cdca78e6a67757188406c8de9db42fcd17881;p=thirdparty%2Fdovecot%2Fcore.git anvil: Added support for penalty tracking. --HG-- branch : HEAD --- diff --git a/src/anvil/Makefile.am b/src/anvil/Makefile.am index eaf0002801..6857abe4c0 100644 --- a/src/anvil/Makefile.am +++ b/src/anvil/Makefile.am @@ -17,9 +17,11 @@ anvil_SOURCES = \ main.c \ anvil-connection.c \ anvil-settings.c \ - connect-limit.c + connect-limit.c \ + penalty.c noinst_HEADERS = \ anvil-connection.h \ common.h \ - connect-limit.h + connect-limit.h \ + penalty.h diff --git a/src/anvil/anvil-connection.c b/src/anvil/anvil-connection.c index 07dc61b0d4..432a251ced 100644 --- a/src/anvil/anvil-connection.c +++ b/src/anvil/anvil-connection.c @@ -7,6 +7,7 @@ #include "master-service.h" #include "master-interface.h" #include "connect-limit.h" +#include "penalty.h" #include "anvil-connection.h" #include @@ -46,7 +47,8 @@ anvil_connection_request(struct anvil_connection *conn, const char *const *args, const char **error_r) { const char *cmd = args[0]; - unsigned int count; + unsigned int value; + time_t stamp; pid_t pid; args++; @@ -57,7 +59,6 @@ anvil_connection_request(struct anvil_connection *conn, } pid = strtol(args[0], NULL, 10); connect_limit_connect(connect_limit, pid, args[1]); - return 0; } else if (strcmp(cmd, "DISCONNECT") == 0) { if (args[0] == NULL || args[1] == NULL) { *error_r = "DISCONNECT: Not enough parameters"; @@ -65,10 +66,8 @@ anvil_connection_request(struct anvil_connection *conn, } pid = strtol(args[0], NULL, 10); connect_limit_disconnect(connect_limit, pid, args[1]); - return 0; } else if (strcmp(cmd, "CONNECT-DUMP") == 0) { connect_limit_dump(connect_limit, conn->output); - return 0; } else if (strcmp(cmd, "KILL") == 0) { if (args[0] == NULL) { *error_r = "KILL: Not enough parameters"; @@ -80,7 +79,6 @@ anvil_connection_request(struct anvil_connection *conn, } pid = strtol(args[0], NULL, 10); connect_limit_disconnect_pid(connect_limit, pid); - return 0; } else if (strcmp(cmd, "LOOKUP") == 0) { if (args[0] == NULL) { *error_r = "LOOKUP: Not enough parameters"; @@ -90,14 +88,35 @@ anvil_connection_request(struct anvil_connection *conn, *error_r = "LOOKUP on a FIFO, can't send reply"; return -1; } - count = connect_limit_lookup(connect_limit, args[0]); + value = connect_limit_lookup(connect_limit, args[0]); (void)o_stream_send_str(conn->output, - t_strdup_printf("%u\n", count)); - return 0; + t_strdup_printf("%u\n", value)); + } else if (strcmp(cmd, "PENALTY-GET") == 0) { + if (args[0] == NULL) { + *error_r = "PENALTY-GET: Not enough parameters"; + return -1; + } + value = penalty_get(penalty, args[0], &stamp); + (void)o_stream_send_str(conn->output, + t_strdup_printf("%u %s\n", value, dec2str(stamp))); + } else if (strcmp(cmd, "PENALTY-SET") == 0) { + if (args[0] == NULL || args[1] == NULL) { + *error_r = "PENALTY-SET: Not enough parameters"; + return -1; + } + penalty_set(penalty, args[0], strtoul(args[1], NULL, 10)); + } else if (strcmp(cmd, "PENALTY-SET-EXPIRE-SECS") == 0) { + if (args[0] == NULL) { + *error_r = "PENALTY-SET-EXPIRE-SECS: " + "Not enough parameters"; + return -1; + } + penalty_set_expire_secs(penalty, atoi(args[0])); } else { *error_r = t_strconcat("Unknown command: ", cmd, NULL); return -1; } + return 0; } static void anvil_connection_input(void *context) diff --git a/src/anvil/anvil-settings.c b/src/anvil/anvil-settings.c index 1d7a258e27..e268a907ca 100644 --- a/src/anvil/anvil-settings.c +++ b/src/anvil/anvil-settings.c @@ -9,10 +9,12 @@ /* */ static struct file_listener_settings anvil_unix_listeners_array[] = { - { "anvil", 0600, "", "" } + { "anvil", 0600, "", "" }, + { "anvil-auth-penalty", 0600, "", "" } }; static struct file_listener_settings *anvil_unix_listeners[] = { - &anvil_unix_listeners_array[0] + &anvil_unix_listeners_array[0], + &anvil_unix_listeners_array[1] }; static buffer_t anvil_unix_listeners_buf = { anvil_unix_listeners, sizeof(anvil_unix_listeners), { 0, } diff --git a/src/anvil/common.h b/src/anvil/common.h index e6468df4e6..2620817b08 100644 --- a/src/anvil/common.h +++ b/src/anvil/common.h @@ -4,5 +4,6 @@ #include "lib.h" extern struct connect_limit *connect_limit; +extern struct penalty *penalty; #endif diff --git a/src/anvil/main.c b/src/anvil/main.c index 31bebb1a04..e09a678c81 100644 --- a/src/anvil/main.c +++ b/src/anvil/main.c @@ -6,9 +6,11 @@ #include "master-service.h" #include "master-interface.h" #include "connect-limit.h" +#include "penalty.h" #include "anvil-connection.h" struct connect_limit *connect_limit; +struct penalty *penalty; static void client_connected(const struct master_service_connection *conn) { @@ -27,9 +29,11 @@ int main(int argc, char *argv[]) master_service_init_log(master_service, "anvil: "); master_service_init_finish(master_service); connect_limit = connect_limit_init(); + penalty = penalty_init(); master_service_run(master_service, client_connected); + penalty_deinit(&penalty); connect_limit_deinit(&connect_limit); anvil_connections_destroy_all(); master_service_deinit(&master_service); diff --git a/src/anvil/penalty.c b/src/anvil/penalty.c new file mode 100644 index 0000000000..cd91b4976b --- /dev/null +++ b/src/anvil/penalty.c @@ -0,0 +1,125 @@ +/* Copyright (C) 2009 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "ioloop.h" +#include "hash.h" +#include "llist.h" +#include "penalty.h" + +#include + +#define PENALTY_DEFAULT_EXPIRE_SECS (60*60) + +struct penalty_rec { + /* ordered by last_update */ + struct penalty_rec *prev, *next; + + char *ident; + unsigned int penalty; + time_t last_update; +}; + +struct penalty { + /* ident => penalty_rec */ + struct hash_table *hash; + struct penalty_rec *oldest, *newest; + + unsigned int expire_secs; + struct timeout *to; +}; + +struct penalty *penalty_init(void) +{ + struct penalty *penalty; + + penalty = i_new(struct penalty, 1); + penalty->hash = + hash_table_create(default_pool, default_pool, 0, + str_hash, (hash_cmp_callback_t *)strcmp); + penalty->expire_secs = PENALTY_DEFAULT_EXPIRE_SECS; + return penalty; +} + +static void penalty_rec_free(struct penalty *penalty, struct penalty_rec *rec) +{ + DLLIST2_REMOVE(&penalty->oldest, &penalty->newest, rec); + i_free(rec->ident); + i_free(rec); +} + +void penalty_deinit(struct penalty **_penalty) +{ + struct penalty *penalty = *_penalty; + + *_penalty = NULL; + + while (penalty->oldest != NULL) + penalty_rec_free(penalty, penalty->oldest); + hash_table_destroy(&penalty->hash); + + if (penalty->to != NULL) + timeout_remove(&penalty->to); + i_free(penalty); +} + +void penalty_set_expire_secs(struct penalty *penalty, unsigned int expire_secs) +{ + penalty->expire_secs = expire_secs; +} + +unsigned int penalty_get(struct penalty *penalty, const char *ident, + time_t *last_update_r) +{ + struct penalty_rec *rec; + + rec = hash_table_lookup(penalty->hash, ident); + if (rec == NULL) { + *last_update_r = 0; + return 0; + } else { + *last_update_r = rec->last_update; + return rec->penalty; + } +} + +static void penalty_timeout(struct penalty *penalty) +{ + time_t expire_time; + + expire_time = ioloop_time - penalty->expire_secs; + while (penalty->oldest != NULL && + penalty->oldest->last_update <= expire_time) { + hash_table_remove(penalty->hash, penalty->oldest->ident); + penalty_rec_free(penalty, penalty->oldest); + } + + timeout_remove(&penalty->to); + if (penalty->oldest != NULL) { + unsigned int diff = penalty->oldest->last_update - expire_time; + penalty->to = timeout_add(diff * 1000, + penalty_timeout, penalty); + } +} + +void penalty_set(struct penalty *penalty, const char *ident, + unsigned int value) +{ + struct penalty_rec *rec; + + rec = hash_table_lookup(penalty->hash, ident); + if (rec == NULL) { + rec = i_new(struct penalty_rec, 1); + rec->ident = i_strdup(ident); + hash_table_insert(penalty->hash, rec->ident, rec); + } else { + DLLIST2_REMOVE(&penalty->oldest, &penalty->newest, rec); + } + rec->penalty = value; + rec->last_update = time(NULL); + DLLIST2_APPEND(&penalty->oldest, &penalty->newest, rec); + + if (penalty->to == NULL) { + penalty->to = timeout_add(penalty->expire_secs * 1000, + penalty_timeout, penalty); + } +} diff --git a/src/anvil/penalty.h b/src/anvil/penalty.h new file mode 100644 index 0000000000..7c5ac38755 --- /dev/null +++ b/src/anvil/penalty.h @@ -0,0 +1,14 @@ +#ifndef PENALTY_H +#define PENALTY_H + +struct penalty *penalty_init(void); +void penalty_deinit(struct penalty **penalty); + +void penalty_set_expire_secs(struct penalty *penalty, unsigned int expire_secs); + +unsigned int penalty_get(struct penalty *penalty, const char *ident, + time_t *last_update_r); +void penalty_set(struct penalty *penalty, const char *ident, + unsigned int value); + +#endif