str_append_c(cmd, '\n');
ctx->tcp_conn = conn;
- server_connection_cmd(conn, str_c(cmd), NULL,
+ /* dsync command can't be proxied currently, so use TTL 1 */
+ server_connection_cmd(conn, 1, str_c(cmd), NULL,
dsync_connected_callback, ctx);
io_loop_run(current_ioloop);
ctx->tcp_conn = NULL;
#define DOVEADM_MAIL_SERVER_FAILED() \
(internal_failure || master_service_is_killed(master_service))
+struct doveadm_proxy_redirect {
+ struct ip_addr ip;
+ in_port_t port;
+};
+
struct doveadm_mail_server_cmd {
struct server_connection *conn;
char *username;
+
+ int proxy_ttl;
+ ARRAY(struct doveadm_proxy_redirect) redirect_path;
+
char *cmdline;
struct istream *input;
};
return;
i_stream_unref(&cmd->input);
+ array_free(&cmd->redirect_path);
i_free(cmd->cmdline);
i_free(cmd->username);
i_free(cmd);
}
+static bool
+doveadm_proxy_cmd_have_connected(struct doveadm_mail_server_cmd *servercmd,
+ const struct ip_addr *ip, in_port_t port)
+{
+ const struct doveadm_proxy_redirect *redirect;
+ struct ip_addr conn_ip;
+ in_port_t conn_port;
+
+ server_connection_get_dest(servercmd->conn, &conn_ip, &conn_port);
+ i_assert(conn_ip.family != 0);
+
+ if (net_ip_compare(&conn_ip, ip) && conn_port == port)
+ return TRUE;
+ if (!array_is_created(&servercmd->redirect_path))
+ return FALSE;
+
+ array_foreach(&servercmd->redirect_path, redirect) {
+ if (net_ip_compare(&redirect->ip, ip) && redirect->port == port)
+ return TRUE;
+ }
+ return FALSE;
+}
+
static int doveadm_cmd_redirect(struct doveadm_mail_server_cmd *servercmd,
const char *destination)
{
struct doveadm_server *orig_server, *new_server;
struct server_connection *conn;
+ struct doveadm_proxy_redirect *redirect;
struct ip_addr ip;
in_port_t port;
const char *destuser, *host, *error;
return -1;
}
+ if (doveadm_proxy_cmd_have_connected(servercmd, &ip,
+ orig_server->port)) {
+ i_error("%s: Proxying loops - already connected to %s:%u",
+ orig_server->name, net_ip2addr(&ip), orig_server->port);
+ return -1;
+ }
+
+ i_assert(servercmd->proxy_ttl > 0);
+ servercmd->proxy_ttl--;
+
+ /* Add current ip/port to redirect path */
+ if (!array_is_created(&servercmd->redirect_path))
+ i_array_init(&servercmd->redirect_path, 2);
+ redirect = array_append_space(&servercmd->redirect_path);
+ redirect->ip = ip;
+ redirect->port = orig_server->port;
+
new_server = doveadm_server_get(destination);
new_server->ip = ip;
new_server->ssl_flags = orig_server->ssl_flags;
servercmd->conn = conn;
if (servercmd->input != NULL)
i_stream_seek(servercmd->input, 0);
- server_connection_cmd(conn, servercmd->cmdline, servercmd->input,
+ server_connection_cmd(conn, servercmd->proxy_ttl,
+ servercmd->cmdline, servercmd->input,
doveadm_cmd_callback, servercmd);
return 0;
}
servercmd = i_new(struct doveadm_mail_server_cmd, 1);
servercmd->conn = conn;
servercmd->username = i_strdup(username);
+ servercmd->proxy_ttl = cmd_ctx->proxy_ttl;
servercmd->cmdline = i_strdup(str_c(cmd));
servercmd->input = cmd_ctx->cmd_input;
if (servercmd->input != NULL)
i_stream_ref(servercmd->input);
- server_connection_cmd(conn, str_c(cmd), cmd_ctx->cmd_input,
+ server_connection_cmd(conn, cmd_ctx->proxy_ttl,
+ str_c(cmd), cmd_ctx->cmd_input,
doveadm_cmd_callback, servercmd);
}
/* run it ourself */
return 0;
}
+ if (ctx->proxy_ttl <= 1) {
+ *error_r = "TTL reached zero - proxies appear to be looping?";
+ return -1;
+ }
if (referral != NULL) {
ctx->cctx->referral = referral;
return 1;
#include <stdio.h>
+/* See LOGIN_PROXY_TTL */
+#define DOVEADM_PROXY_TTL 5
#define DOVEADM_MAIL_CMD_INPUT_TIMEOUT_MSECS (5*60*1000)
struct force_resync_cmd_context {
ctx = cmd->alloc();
ctx->set = set;
ctx->cmd = cmd;
+ ctx->proxy_ttl = DOVEADM_PROXY_TTL;
if (ctx->v.init == NULL)
ctx->v.init = doveadm_mail_cmd_init_noop;
if (ctx->v.get_next_user == NULL)
/* This parameter allows to set additional
* mailbox transaction flags. */
mctx->transaction_flags = arg->value.v_int64;
+ } else if (strcmp(arg->name, "proxy-ttl") == 0) {
+ /* if this becomes <= 1, stop attempting to proxy */
+ mctx->proxy_ttl = arg->value.v_int64;
/* Keep all named special parameters above this line */
/* search args aren't set for all mail commands */
struct mail_search_args *search_args;
struct istream *users_list_input;
+ int proxy_ttl;
struct mail_storage_service_user *cur_service_user;
struct mail_user *cur_mail_user;
DOVEADM_CMD_PARAM('S', "socket-path", CMD_PARAM_STR, 0) \
DOVEADM_CMD_PARAM('u', "user", CMD_PARAM_STR, 0) \
DOVEADM_CMD_PARAM('\0', "trans-flags", CMD_PARAM_INT64, 0) \
-DOVEADM_CMD_PARAM('F', "user-file", CMD_PARAM_ISTREAM, 0)
+DOVEADM_CMD_PARAM('F', "user-file", CMD_PARAM_ISTREAM, 0) \
+DOVEADM_CMD_PARAM('\0', "proxy-ttl", CMD_PARAM_INT64, 0)
#define DOVEADM_CMD_MAIL_USAGE_PREFIX \
"[-u <user>|-A] [-S <socket_path>] "
#include "net.h"
#define DOVEADM_SERVER_PROTOCOL_VERSION_MAJOR 1
-#define DOVEADM_SERVER_PROTOCOL_VERSION_MINOR 2
-#define DOVEADM_SERVER_PROTOCOL_VERSION_LINE "VERSION\tdoveadm-server\t1\t2"
-#define DOVEADM_CLIENT_PROTOCOL_VERSION_LINE "VERSION\tdoveadm-client\t1\t2"
+#define DOVEADM_SERVER_PROTOCOL_VERSION_MINOR 3
+#define DOVEADM_SERVER_PROTOCOL_VERSION_LINE "VERSION\tdoveadm-server\t1\t3"
+#define DOVEADM_CLIENT_PROTOCOL_VERSION_LINE "VERSION\tdoveadm-client\t1\t3"
extern bool doveadm_verbose, doveadm_debug, doveadm_server;
#define DOVEADM_PROTO_MINOR_MIN_MULTIPLEX 1
#define DOVEADM_PROTO_MINOR_MIN_STARTTLS 2
+#define DOVEADM_PROTO_MINOR_MIN_PROXY_TTL 3
enum server_reply_state {
SERVER_REPLY_STATE_DONE = 0,
struct istream *cmd_input;
struct ostream *cmd_output;
const char *delayed_cmd;
+ int delayed_cmd_proxy_ttl;
server_cmd_callback_t *callback;
void *context;
i_stream_skip(conn->input, size);
}
+static void
+server_connection_send_cmd(struct server_connection *conn,
+ const char *cmdline, int proxy_ttl)
+{
+ i_assert(conn->authenticated);
+ i_assert(proxy_ttl >= 1);
+
+ if (conn->minor < DOVEADM_PROTO_MINOR_MIN_PROXY_TTL) {
+ o_stream_nsend_str(conn->output, cmdline);
+ return;
+ }
+
+ /* <flags> <username> <command> [<args>] -
+ Insert --proxy-ttl as the first arg. */
+ const char *p = strchr(cmdline, '\t');
+ i_assert(p != NULL);
+ p = strchr(p+1, '\t');
+ i_assert(p != NULL);
+ p = strchr(p+1, '\t');
+ i_assert(p != NULL);
+ size_t prefix_len = p - cmdline;
+
+ const char *proxy_ttl_str = t_strdup_printf(
+ "\t--proxy-ttl\t%d", proxy_ttl);
+ struct const_iovec iov[] = {
+ { cmdline, prefix_len },
+ { proxy_ttl_str, strlen(proxy_ttl_str) },
+ { cmdline + prefix_len, strlen(cmdline + prefix_len) },
+ };
+ o_stream_nsendv(conn->output, iov, N_ELEMENTS(iov));
+}
+
static void server_connection_authenticated(struct server_connection *conn)
{
conn->authenticated = TRUE;
if (conn->delayed_cmd != NULL) {
- o_stream_nsend_str(conn->output, conn->delayed_cmd);
+ server_connection_send_cmd(conn, conn->delayed_cmd,
+ conn->delayed_cmd_proxy_ttl);
conn->delayed_cmd = NULL;
server_connection_send_cmd_input(conn);
}
return conn->server;
}
-void server_connection_cmd(struct server_connection *conn, const char *line,
- struct istream *cmd_input,
+void server_connection_get_dest(struct server_connection *conn,
+ struct ip_addr *ip_r, in_port_t *port_r)
+{
+ if (net_getpeername(conn->fd, ip_r, port_r) < 0) {
+ i_zero(ip_r);
+ *port_r = 0;
+ }
+}
+
+void server_connection_cmd(struct server_connection *conn, int proxy_ttl,
+ const char *line, struct istream *cmd_input,
server_cmd_callback_t *callback, void *context)
{
i_assert(conn->delayed_cmd == NULL);
+ i_assert(proxy_ttl >= 1);
conn->state = SERVER_REPLY_STATE_PRINT;
if (cmd_input != NULL) {
i_stream_ref(cmd_input);
conn->cmd_input = cmd_input;
}
- if (!conn->authenticated)
+ if (!conn->authenticated) {
+ conn->delayed_cmd_proxy_ttl = proxy_ttl;
conn->delayed_cmd = p_strdup(conn->pool, line);
- else {
- o_stream_nsend_str(conn->output, line);
+ } else {
+ server_connection_send_cmd(conn, line, proxy_ttl);
server_connection_send_cmd_input(conn);
}
conn->callback = callback;
struct doveadm_server *
server_connection_get_server(struct server_connection *conn);
-void server_connection_cmd(struct server_connection *conn, const char *line,
- struct istream *cmd_input,
+void server_connection_get_dest(struct server_connection *conn,
+ struct ip_addr *ip_r, in_port_t *port_r);
+
+void server_connection_cmd(struct server_connection *conn, int proxy_ttl,
+ const char *line, struct istream *cmd_input,
server_cmd_callback_t *callback, void *context);
/* Returns TRUE if no command is being processed */
bool server_connection_is_idle(struct server_connection *conn);