diff options
Diffstat (limited to '')
-rw-r--r-- | src/doveadm/doveadm-proxy.c | 214 |
1 files changed, 214 insertions, 0 deletions
diff --git a/src/doveadm/doveadm-proxy.c b/src/doveadm/doveadm-proxy.c new file mode 100644 index 0000000..04fde52 --- /dev/null +++ b/src/doveadm/doveadm-proxy.c @@ -0,0 +1,214 @@ +/* Copyright (c) 2011-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "ioloop.h" +#include "str.h" +#include "strescape.h" +#include "ipc-client.h" +#include "doveadm.h" +#include "doveadm-print.h" + +#include <stdio.h> +#include <unistd.h> + +struct proxy_context { + struct ipc_client *ipc; + const char *username_field; + const char *kick_hosts; +}; + +extern struct doveadm_cmd_ver2 doveadm_cmd_proxy[]; + +static void proxy_cmd_help(struct doveadm_cmd_context *cctx) ATTR_NORETURN; + +static struct proxy_context * +cmd_proxy_init(struct doveadm_cmd_context *cctx) +{ + struct proxy_context *ctx; + const char *socket_path; + + ctx = t_new(struct proxy_context, 1); + if (!doveadm_cmd_param_str(cctx, "socket-path", &socket_path)) { + socket_path = t_strconcat(doveadm_settings->base_dir, + "/ipc", NULL); + } + (void)doveadm_cmd_param_str(cctx, "passdb-field", &ctx->username_field); + (void)doveadm_cmd_param_str(cctx, "host", &ctx->kick_hosts); + ctx->ipc = ipc_client_init(socket_path); + return ctx; +} + +static void cmd_proxy_list_header(const char *const *args) +{ + struct { + const char *key; + const char *title; + } header_map[] = { + { "service", "proto" }, + { "src-ip", "src ip" }, + { "dest-ip", "dest ip" }, + { "dest-port", "port" }, + }; + for (unsigned int i = 0; args[i] != NULL; i++) { + const char *arg = args[i]; + + if (strcmp(arg, "username") == 0 || + str_begins(arg, "user_")) { + doveadm_print_header(arg, arg, + DOVEADM_PRINT_HEADER_FLAG_EXPAND); + continue; + } + const char *title = arg; + for (unsigned int j = 0; j < N_ELEMENTS(header_map); j++) { + if (strcmp(header_map[j].key, arg) == 0) { + title = header_map[j].title; + break; + } + } + doveadm_print_header(arg, title, 0); + } +} + +static void cmd_proxy_list_callback(enum ipc_client_cmd_state state, + const char *data, void *context) +{ + bool *seen_header = context; + + switch (state) { + case IPC_CLIENT_CMD_STATE_REPLY: { + const char *const *args = t_strsplit_tabescaped(data); + + if (!*seen_header) { + cmd_proxy_list_header(args); + *seen_header = TRUE; + } else { + for (; *args != NULL; args++) + doveadm_print(*args); + } + return; + } + case IPC_CLIENT_CMD_STATE_OK: + break; + case IPC_CLIENT_CMD_STATE_ERROR: + i_error("LIST-FULL failed: %s", data); + break; + } + io_loop_stop(current_ioloop); +} + +static void cmd_proxy_list(struct doveadm_cmd_context *cctx) +{ + struct proxy_context *ctx; + bool seen_header = FALSE; + + ctx = cmd_proxy_init(cctx); + + doveadm_print_init(DOVEADM_PRINT_TYPE_TABLE); + + io_loop_set_running(current_ioloop); + ipc_client_cmd(ctx->ipc, "proxy\t*\tLIST-FULL", + cmd_proxy_list_callback, &seen_header); + if (io_loop_is_running(current_ioloop)) + io_loop_run(current_ioloop); + ipc_client_deinit(&ctx->ipc); +} + +static void cmd_proxy_kick_callback(enum ipc_client_cmd_state state, + const char *data, void *context ATTR_UNUSED) +{ + switch (state) { + case IPC_CLIENT_CMD_STATE_REPLY: + return; + case IPC_CLIENT_CMD_STATE_OK: + if (data[0] == '\0') + data = "0"; + doveadm_print(data); + break; + case IPC_CLIENT_CMD_STATE_ERROR: + i_error("KICK failed: %s", data); + doveadm_exit_code = EX_TEMPFAIL; + break; + } + io_loop_stop(current_ioloop); +} + +static void cmd_proxy_kick(struct doveadm_cmd_context *cctx) +{ + struct proxy_context *ctx; + const char *const *users = NULL; + string_t *cmd; + + ctx = cmd_proxy_init(cctx); + (void)doveadm_cmd_param_array(cctx, "user", &users); + if (users == NULL && ctx->kick_hosts == NULL) { + proxy_cmd_help(cctx); + return; + } + + doveadm_print_init(DOVEADM_PRINT_TYPE_FORMATTED); + doveadm_print_formatted_set_format("%{count} connections kicked\n"); + doveadm_print_header_simple("count"); + + cmd = t_str_new(128); + str_append(cmd, "proxy\t*\t"); + if (ctx->kick_hosts != NULL) { + str_append(cmd, "KICK-HOST\t"); + str_append(cmd, ctx->kick_hosts); + } + else if (ctx->username_field == NULL) + str_append(cmd, "KICK"); + else { + str_append(cmd, "KICK-ALT\t"); + str_append_tabescaped(cmd, ctx->username_field); + } + if (users != NULL) { + for (unsigned int i = 0; users[i] != NULL; i++) { + str_append_c(cmd, '\t'); + str_append_tabescaped(cmd, users[i]); + } + } + ipc_client_cmd(ctx->ipc, str_c(cmd), cmd_proxy_kick_callback, NULL); + io_loop_run(current_ioloop); + ipc_client_deinit(&ctx->ipc); +} + +struct doveadm_cmd_ver2 doveadm_cmd_proxy[] = { +{ + .name = "proxy list", + .usage = "[-a <ipc socket path>]", + .cmd = cmd_proxy_list, +DOVEADM_CMD_PARAMS_START +DOVEADM_CMD_PARAM('a', "socket-path", CMD_PARAM_STR, 0) +DOVEADM_CMD_PARAMS_END +}, +{ + .name = "proxy kick", + .usage = "[-a <ipc socket path>] [-f <passdb field>] [-h <host> [...] | <user> [...]]", + .cmd = cmd_proxy_kick, +DOVEADM_CMD_PARAMS_START +DOVEADM_CMD_PARAM('a', "socket-path", CMD_PARAM_STR, 0) +DOVEADM_CMD_PARAM('f', "passdb-field", CMD_PARAM_STR, 0) +DOVEADM_CMD_PARAM('h', "host", CMD_PARAM_STR, 0) +DOVEADM_CMD_PARAM('\0', "user", CMD_PARAM_ARRAY, CMD_PARAM_FLAG_POSITIONAL) +DOVEADM_CMD_PARAMS_END +} +}; + +static void proxy_cmd_help(struct doveadm_cmd_context *cctx) +{ + unsigned int i; + + for (i = 0; i < N_ELEMENTS(doveadm_cmd_proxy); i++) { + if (doveadm_cmd_proxy[i].cmd == cctx->cmd->cmd) + help_ver2(&doveadm_cmd_proxy[i]); + } + i_unreached(); +} + +void doveadm_register_proxy_commands(void) +{ + unsigned int i; + + for (i = 0; i < N_ELEMENTS(doveadm_cmd_proxy); i++) + doveadm_cmd_register_ver2(&doveadm_cmd_proxy[i]); +} |