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
#include "master-service.h"
#include "master-interface.h"
#include "connect-limit.h"
+#include "penalty.h"
#include "anvil-connection.h"
#include <stdlib.h>
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++;
}
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";
}
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";
}
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";
*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)
/* <settings checks> */
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, }
#include "lib.h"
extern struct connect_limit *connect_limit;
+extern struct penalty *penalty;
#endif
#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)
{
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);
--- /dev/null
+/* 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 <time.h>
+
+#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);
+ }
+}
--- /dev/null
+#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