int server_connection_create(struct doveadm_server *server,
			     struct server_connection **conn_r)
{
#define DOVEADM_SERVER_HANDSHAKE "VERSION\tdoveadm-server\t1\t0\n"
	struct server_connection *conn;
	pool_t pool;

	pool = pool_alloconly_create("doveadm server connection", 1024*16);
	conn = p_new(pool, struct server_connection, 1);
	conn->pool = pool;
	conn->server = server;
	conn->fd = doveadm_connect_with_default_port(server->name,
						     doveadm_settings->doveadm_port);
	net_set_nonblock(conn->fd, TRUE);
	conn->io = io_add(conn->fd, IO_READ, server_connection_input, conn);
	conn->input = i_stream_create_fd(conn->fd, MAX_INBUF_SIZE, FALSE);
	conn->output = o_stream_create_fd(conn->fd, (size_t)-1, FALSE);

	array_append(&conn->server->connections, &conn, 1);

	if (server_connection_read_settings(conn) < 0 ||
	    server_connection_init_ssl(conn) < 0) {
		server_connection_destroy(&conn);
		return -1;
	}

	o_stream_set_no_error_handling(conn->output, TRUE);
	conn->state = SERVER_REPLY_STATE_DONE;
	o_stream_nsend_str(conn->output, DOVEADM_SERVER_HANDSHAKE);

	*conn_r = conn;
	return 0;
}
Exemple #2
0
static int server_connection_output(struct server_connection *conn)
{
	int ret;

	ret = o_stream_flush(conn->output);
	if (ret > 0 && conn->cmd_input != NULL && conn->delayed_cmd == NULL)
		ret = server_connection_send_cmd_input_more(conn);
	if (ret < 0)
		server_connection_destroy(&conn);
	return ret;
}
static void
server_handle_input(struct server_connection *conn,
		    const unsigned char *data, size_t size)
{
	string_t *str;
	size_t i, start;

	if (printing_conn == conn) {
		/* continue printing */
	} else if (printing_conn == NULL) {
		printing_conn = conn;
	} else {
		/* someone else is printing. don't continue until it
		   goes away */
		io_remove(&conn->io);
		return;
	}

	if (data[size-1] == '\001') {
		/* last character is an escape */
		size--;
	}

	str = t_str_new(128);
	for (i = start = 0; i < size; i++) {
		if (data[i] == '\n') {
			if (i != start) {
				i_error("doveadm server sent broken print input");
				server_connection_destroy(&conn);
				return;
			}
			conn->state = SERVER_REPLY_STATE_RET;
			i_stream_skip(conn->input, i + 1);

			print_connection_released();
			return;
		}
		if (data[i] == '\t') {
			server_flush_field(conn, str, data + start, i - start);
			start = i + 1;
		}
	}
	if (start != size) {
		conn->streaming = TRUE;
		stream_data(str, data + start, size - start);
	}
	i_stream_skip(conn->input, size);
}
static void server_connection_input(struct server_connection *conn)
{
	const unsigned char *data;
	size_t size;
	const char *line;
	int exit_code;

	if (!conn->handshaked) {
		if ((line = i_stream_read_next_line(conn->input)) == NULL) {
			if (conn->input->eof || conn->input->stream_errno != 0) {
				server_log_disconnect_error(conn);
				server_connection_destroy(&conn);
			}
			return;
		}

		conn->handshaked = TRUE;
		if (strcmp(line, "+") == 0)
			server_connection_authenticated(conn);
		else if (strcmp(line, "-") == 0) {
			if (server_connection_authenticate(conn) < 0) {
				server_connection_destroy(&conn);
				return;
			}
			return;
		} else {
			i_error("doveadm server sent invalid handshake: %s",
				line);
			server_connection_destroy(&conn);
			return;
		}
	}

	if (i_stream_read(conn->input) < 0) {
		/* disconnected */
		server_log_disconnect_error(conn);
		server_connection_destroy(&conn);
		return;
	}

	if (!conn->authenticated) {
		if ((line = i_stream_next_line(conn->input)) == NULL)
			return;
		if (strcmp(line, "+") == 0)
			server_connection_authenticated(conn);
		else {
			i_error("doveadm authentication failed (%s)", line+1);
			server_connection_destroy(&conn);
			return;
		}
	}

	data = i_stream_get_data(conn->input, &size);
	if (size == 0)
		return;

	switch (conn->state) {
	case SERVER_REPLY_STATE_DONE:
		i_error("doveadm server sent unexpected input");
		server_connection_destroy(&conn);
		return;
	case SERVER_REPLY_STATE_PRINT:
		server_handle_input(conn, data, size);
		if (conn->state != SERVER_REPLY_STATE_RET)
			break;
		/* fall through */
	case SERVER_REPLY_STATE_RET:
		line = i_stream_next_line(conn->input);
		if (line == NULL)
			return;
		if (line[0] == '+')
			server_connection_callback(conn, 0, "");
		else if (line[0] == '-') {
			line++;
			if (strcmp(line, "NOUSER") == 0)
				exit_code = EX_NOUSER;
			else if (str_to_int(line, &exit_code) < 0) {
				/* old doveadm-server */
				exit_code = EX_TEMPFAIL;
			}
			server_connection_callback(conn, exit_code, line);
		} else {
			i_error("doveadm server sent broken input "
				"(expected cmd reply): %s", line);
			server_connection_destroy(&conn);
			break;
		}
		if (conn->callback == NULL) {
			/* we're finished, close the connection */
			server_connection_destroy(&conn);
		}
		break;
	}
}
Exemple #5
0
static int
dsync_connect_tcp(struct dsync_cmd_context *ctx,
		  const struct mail_storage_settings *mail_set,
		  const char *target, bool ssl, const char **error_r)
{
	struct doveadm_server *server;
	struct server_connection *conn;
	struct ioloop *ioloop;
	string_t *cmd;
	const char *error;

	server = p_new(ctx->ctx.pool, struct doveadm_server, 1);
	server->name = p_strdup(ctx->ctx.pool, target);
	if (ssl) {
		if (dsync_init_ssl_ctx(ctx, mail_set, &error) < 0) {
			*error_r = t_strdup_printf(
				"Couldn't initialize SSL context: %s", error);
			return -1;
		}
		server->ssl_ctx = ctx->ssl_ctx;
	}
	p_array_init(&server->connections, ctx->ctx.pool, 1);
	p_array_init(&server->queue, ctx->ctx.pool, 1);

	ioloop = io_loop_create();

	if (server_connection_create(server, &conn) < 0) {
		*error_r = "Couldn't create server connection";
		return -1;
	}

	/* <flags> <username> <command> [<args>] */
	cmd = t_str_new(256);
	if (doveadm_debug)
		str_append_c(cmd, 'D');
	str_append_c(cmd, '\t');
	str_append_tabescaped(cmd, ctx->ctx.cur_username);
	str_append(cmd, "\tdsync-server\t-u");
	str_append_tabescaped(cmd, ctx->ctx.cur_username);
	if (ctx->replicator_notify)
		str_append(cmd, "\t-U");
	str_append_c(cmd, '\n');

	ctx->tcp_conn = conn;
	server_connection_cmd(conn, str_c(cmd),
			      dsync_connected_callback, ctx);
	io_loop_run(ioloop);
	ctx->tcp_conn = NULL;

	if (array_count(&server->connections) > 0)
		server_connection_destroy(&conn);
	io_loop_destroy(&ioloop);

	if (ctx->error != NULL) {
		*error_r = ctx->error;
		ctx->error = NULL;
		return -1;
	}
	ctx->run_type = DSYNC_RUN_TYPE_STREAM;
	return 0;
}