server-connection.c revision b3c095d1fb0bb86695d92c2045eb09e985623934
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina/* Copyright (c) 2010-2017 Dovecot authors, see the included COPYING file */
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina#include "lib.h"
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina#include "array.h"
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina#include "base64.h"
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina#include "ioloop.h"
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina#include "net.h"
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina#include "istream.h"
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina#include "ostream.h"
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina#include "ostream-dot.h"
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina#include "str.h"
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina#include "strescape.h"
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina#include "iostream-ssl.h"
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina#include "master-service.h"
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina#include "master-service-settings.h"
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina#include "settings-parser.h"
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina#include "doveadm-print.h"
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina#include "doveadm-util.h"
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina#include "doveadm-server.h"
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina#include "doveadm-settings.h"
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina#include "server-connection.h"
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina#include <sysexits.h>
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina#include <unistd.h>
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina#define MAX_INBUF_SIZE (1024*32)
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březinaenum server_reply_state {
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina SERVER_REPLY_STATE_DONE = 0,
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina SERVER_REPLY_STATE_PRINT,
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina SERVER_REPLY_STATE_RET
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina};
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březinastruct server_connection {
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina struct doveadm_server *server;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina pool_t pool;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina struct doveadm_settings *set;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina int fd;
84060f52e782b079337ee7a99bb7ad17e8c84fbbPavel Březina struct io *io;
84060f52e782b079337ee7a99bb7ad17e8c84fbbPavel Březina struct istream *input;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina struct ostream *output;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina struct ssl_iostream *ssl_iostream;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina struct timeout *to_input;
60a715a0dd79873d2d2607eab8fdfaf0ffd2e7d3Hristo Venev
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina struct istream *cmd_input;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina struct ostream *cmd_output;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina const char *delayed_cmd;
64497d479e92ebc34717c20c3d017f1823f9e630Jakub Hrozek server_cmd_callback_t *callback;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina void *context;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina enum server_reply_state state;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina bool handshaked:1;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina bool authenticated:1;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina bool streaming:1;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina};
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březinastatic struct server_connection *printing_conn = NULL;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březinastatic ARRAY(struct doveadm_server *) print_pending_servers = ARRAY_INIT;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březinastatic void server_connection_input(struct server_connection *conn);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březinastatic bool server_connection_input_one(struct server_connection *conn);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březinastatic void server_set_print_pending(struct doveadm_server *server)
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina{
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina struct doveadm_server *const *serverp;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina if (!array_is_created(&print_pending_servers))
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina i_array_init(&print_pending_servers, 16);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina array_foreach(&print_pending_servers, serverp) {
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina if (*serverp == server)
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina return;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina }
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina array_append(&print_pending_servers, &server, 1);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina}
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březinastatic void server_print_connection_released(struct doveadm_server *server)
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina{
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina struct server_connection *const *conns;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina unsigned int i, count;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina conns = array_get(&server->connections, &count);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina for (i = 0; i < count; i++) {
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina if (conns[i]->io != NULL)
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina continue;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina conns[i]->io = io_add(conns[i]->fd, IO_READ,
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina server_connection_input, conns[i]);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina conns[i]->to_input = timeout_add_short(0,
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina server_connection_input, conns[i]);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina }
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina}
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březinastatic void print_connection_released(void)
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina{
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina struct doveadm_server *const *serverp;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina printing_conn = NULL;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina if (!array_is_created(&print_pending_servers))
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina return;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina array_foreach(&print_pending_servers, serverp)
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina server_print_connection_released(*serverp);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina array_free(&print_pending_servers);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina}
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březinastatic int server_connection_send_cmd_input_more(struct server_connection *conn)
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina{
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina enum ostream_send_istream_result res;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina int ret = -1;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina /* ostream-dot writes only up to max buffer size, so keep it non-zero */
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina o_stream_set_max_buffer_size(conn->cmd_output, IO_BLOCK_SIZE);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina res = o_stream_send_istream(conn->cmd_output, conn->cmd_input);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina o_stream_set_max_buffer_size(conn->cmd_output, (size_t)-1);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina switch (res) {
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina case OSTREAM_SEND_ISTREAM_RESULT_FINISHED:
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina break;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina case OSTREAM_SEND_ISTREAM_RESULT_WAIT_INPUT:
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina return 1;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina case OSTREAM_SEND_ISTREAM_RESULT_WAIT_OUTPUT:
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina return 0;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina case OSTREAM_SEND_ISTREAM_RESULT_ERROR_INPUT:
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina i_error("read(%s) failed: %s",
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina i_stream_get_name(conn->cmd_input),
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina i_stream_get_error(conn->cmd_input));
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina break;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina case OSTREAM_SEND_ISTREAM_RESULT_ERROR_OUTPUT:
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina i_error("write(%s) failed: %s",
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina o_stream_get_name(conn->cmd_output),
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina o_stream_get_error(conn->cmd_output));
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina break;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina }
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina if (res == OSTREAM_SEND_ISTREAM_RESULT_FINISHED) {
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina if ((ret = o_stream_flush(conn->cmd_output)) == 0)
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina return 0;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina else if (ret < 0) {
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina i_error("write(%s) failed: %s",
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina o_stream_get_name(conn->cmd_output),
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina o_stream_get_error(conn->cmd_output));
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina }
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina }
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina i_stream_destroy(&conn->cmd_input);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina o_stream_destroy(&conn->cmd_output);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina return ret;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina}
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březinastatic void server_connection_send_cmd_input(struct server_connection *conn)
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina{
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina if (conn->cmd_input == NULL)
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina return;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina conn->cmd_output = o_stream_create_dot(conn->output, TRUE);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina (void)server_connection_send_cmd_input_more(conn);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina}
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březinastatic int server_connection_output(struct server_connection *conn)
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina{
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina int ret;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina ret = o_stream_flush(conn->output);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina if (ret > 0 && conn->cmd_input != NULL && conn->delayed_cmd == NULL)
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina ret = server_connection_send_cmd_input_more(conn);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina if (ret < 0)
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina server_connection_destroy(&conn);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina return ret;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina}
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březinastatic void
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březinaserver_connection_callback(struct server_connection *conn,
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina int exit_code, const char *error)
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina{
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina server_cmd_callback_t *callback = conn->callback;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina conn->callback = NULL;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina callback(exit_code, error, conn->context);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina}
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březinastatic void stream_data(string_t *str, const unsigned char *data, size_t size)
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina{
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina str_truncate(str, 0);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina str_append_tabunescaped(str, data, size);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina doveadm_print_stream(str->data, str->used);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina}
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina
84060f52e782b079337ee7a99bb7ad17e8c84fbbPavel Březinastatic void server_flush_field(struct server_connection *conn, string_t *str,
84060f52e782b079337ee7a99bb7ad17e8c84fbbPavel Březina const unsigned char *data, size_t size)
64497d479e92ebc34717c20c3d017f1823f9e630Jakub Hrozek{
84060f52e782b079337ee7a99bb7ad17e8c84fbbPavel Březina if (conn->streaming) {
84060f52e782b079337ee7a99bb7ad17e8c84fbbPavel Březina conn->streaming = FALSE;
84060f52e782b079337ee7a99bb7ad17e8c84fbbPavel Březina if (size > 0)
84060f52e782b079337ee7a99bb7ad17e8c84fbbPavel Březina stream_data(str, data, size);
84060f52e782b079337ee7a99bb7ad17e8c84fbbPavel Březina doveadm_print_stream("", 0);
84060f52e782b079337ee7a99bb7ad17e8c84fbbPavel Březina } else {
84060f52e782b079337ee7a99bb7ad17e8c84fbbPavel Březina str_truncate(str, 0);
84060f52e782b079337ee7a99bb7ad17e8c84fbbPavel Březina str_append_tabunescaped(str, data, size);
84060f52e782b079337ee7a99bb7ad17e8c84fbbPavel Březina doveadm_print(str_c(str));
64497d479e92ebc34717c20c3d017f1823f9e630Jakub Hrozek }
84060f52e782b079337ee7a99bb7ad17e8c84fbbPavel Březina}
84060f52e782b079337ee7a99bb7ad17e8c84fbbPavel Březina
84060f52e782b079337ee7a99bb7ad17e8c84fbbPavel Březinastatic void
84060f52e782b079337ee7a99bb7ad17e8c84fbbPavel Březinaserver_handle_input(struct server_connection *conn,
84060f52e782b079337ee7a99bb7ad17e8c84fbbPavel Březina const unsigned char *data, size_t size)
64497d479e92ebc34717c20c3d017f1823f9e630Jakub Hrozek{
84060f52e782b079337ee7a99bb7ad17e8c84fbbPavel Březina string_t *str;
84060f52e782b079337ee7a99bb7ad17e8c84fbbPavel Březina size_t i, start;
84060f52e782b079337ee7a99bb7ad17e8c84fbbPavel Březina
84060f52e782b079337ee7a99bb7ad17e8c84fbbPavel Březina if (printing_conn == conn) {
84060f52e782b079337ee7a99bb7ad17e8c84fbbPavel Březina /* continue printing */
84060f52e782b079337ee7a99bb7ad17e8c84fbbPavel Březina } else if (printing_conn == NULL) {
84060f52e782b079337ee7a99bb7ad17e8c84fbbPavel Březina printing_conn = conn;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina } else {
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina /* someone else is printing. don't continue until it
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina goes away */
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina server_set_print_pending(conn->server);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina io_remove(&conn->io);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina return;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina }
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina if (data[size-1] == '\001') {
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina /* last character is an escape */
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina size--;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina }
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina str = t_str_new(128);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina for (i = start = 0; i < size; i++) {
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina if (data[i] == '\n') {
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina if (i != start) {
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina i_error("doveadm server sent broken print input");
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina server_connection_destroy(&conn);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina return;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina }
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina conn->state = SERVER_REPLY_STATE_RET;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina i_stream_skip(conn->input, i + 1);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina print_connection_released();
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina return;
84060f52e782b079337ee7a99bb7ad17e8c84fbbPavel Březina }
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina if (data[i] == '\t') {
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina server_flush_field(conn, str, data + start, i - start);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina start = i + 1;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina }
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina }
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina if (start != size) {
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina conn->streaming = TRUE;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina stream_data(str, data + start, size - start);
84060f52e782b079337ee7a99bb7ad17e8c84fbbPavel Březina }
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina i_stream_skip(conn->input, size);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina}
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina
b0c4eb194cf1414d3440e0cccfb9af9074388c08Pavel Březinastatic void server_connection_authenticated(struct server_connection *conn)
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina{
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina conn->authenticated = TRUE;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina if (conn->delayed_cmd != NULL) {
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina o_stream_nsend_str(conn->output, conn->delayed_cmd);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina conn->delayed_cmd = NULL;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina server_connection_send_cmd_input(conn);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina }
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina}
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březinastatic int
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březinaserver_connection_authenticate(struct server_connection *conn)
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina{
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina string_t *plain = t_str_new(128);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina string_t *cmd = t_str_new(128);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina if (*conn->set->doveadm_password == '\0') {
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina i_error("doveadm_password not set, "
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina "can't authenticate to remote server");
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina return -1;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina }
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina str_append_c(plain, '\0');
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina str_append(plain, conn->set->doveadm_username);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina str_append_c(plain, '\0');
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina str_append(plain, conn->set->doveadm_password);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina str_append(cmd, "PLAIN\t");
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina base64_encode(plain->data, plain->used, cmd);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina str_append_c(cmd, '\n');
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina o_stream_nsend(conn->output, cmd->data, cmd->used);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina return 0;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina}
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březinastatic void server_log_disconnect_error(struct server_connection *conn)
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina{
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina const char *error;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina error = conn->ssl_iostream == NULL ? NULL :
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina ssl_iostream_get_last_error(conn->ssl_iostream);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina if (error == NULL) {
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina error = conn->input->stream_errno == 0 ? "EOF" :
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina strerror(conn->input->stream_errno);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina }
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina i_error("doveadm server disconnected before handshake: %s", error);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina}
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březinastatic void server_connection_input(struct server_connection *conn)
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina{
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina const char *line;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina if (conn->to_input != NULL)
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina timeout_remove(&conn->to_input);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina if (!conn->handshaked) {
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina if ((line = i_stream_read_next_line(conn->input)) == NULL) {
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina if (conn->input->eof || conn->input->stream_errno != 0) {
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina server_log_disconnect_error(conn);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina server_connection_destroy(&conn);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina }
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina return;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina }
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina conn->handshaked = TRUE;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina if (strcmp(line, "+") == 0)
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina server_connection_authenticated(conn);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina else if (strcmp(line, "-") == 0) {
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina if (server_connection_authenticate(conn) < 0) {
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina server_connection_destroy(&conn);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina return;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina }
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina return;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina } else {
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina i_error("doveadm server sent invalid handshake: %s",
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina line);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina server_connection_destroy(&conn);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina return;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina }
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina }
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina if (i_stream_read(conn->input) < 0) {
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina /* disconnected */
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina server_log_disconnect_error(conn);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina server_connection_destroy(&conn);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina return;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina }
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina if (!conn->authenticated) {
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina if ((line = i_stream_next_line(conn->input)) == NULL)
64497d479e92ebc34717c20c3d017f1823f9e630Jakub Hrozek return;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina if (strcmp(line, "+") == 0)
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina server_connection_authenticated(conn);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina else {
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina i_error("doveadm authentication failed (%s)", line+1);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina server_connection_destroy(&conn);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina return;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina }
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina }
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina while (server_connection_input_one(conn)) ;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina}
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březinastatic bool server_connection_input_one(struct server_connection *conn)
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina{
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina const unsigned char *data;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina size_t size;
64497d479e92ebc34717c20c3d017f1823f9e630Jakub Hrozek const char *line;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina int exit_code;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina data = i_stream_get_data(conn->input, &size);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina if (size == 0)
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina return FALSE;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina switch (conn->state) {
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina case SERVER_REPLY_STATE_DONE:
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina i_error("doveadm server sent unexpected input");
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina server_connection_destroy(&conn);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina return FALSE;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina case SERVER_REPLY_STATE_PRINT:
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina server_handle_input(conn, data, size);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina if (conn->state != SERVER_REPLY_STATE_RET)
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina return FALSE;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina /* fall through */
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina case SERVER_REPLY_STATE_RET:
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina line = i_stream_next_line(conn->input);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina if (line == NULL)
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina return FALSE;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina if (line[0] == '+')
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina server_connection_callback(conn, 0, "");
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina else if (line[0] == '-') {
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina line++;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina if (strcmp(line, "NOUSER") == 0)
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina exit_code = EX_NOUSER;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina else if (str_to_int(line, &exit_code) < 0) {
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina /* old doveadm-server */
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina exit_code = EX_TEMPFAIL;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina }
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina server_connection_callback(conn, exit_code, line);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina } else {
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina i_error("doveadm server sent broken input "
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina "(expected cmd reply): %s", line);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina server_connection_destroy(&conn);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina return FALSE;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina }
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina if (conn->callback == NULL) {
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina /* we're finished, close the connection */
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina server_connection_destroy(&conn);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina return FALSE;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina }
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina return TRUE;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina }
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina i_unreached();
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina}
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březinastatic int server_connection_read_settings(struct server_connection *conn)
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina{
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina const struct setting_parser_info *set_roots[] = {
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina &doveadm_setting_parser_info,
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina NULL
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina };
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina struct master_service_settings_input input;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina struct master_service_settings_output output;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina const char *error;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina in_port_t port;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina void *set;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina i_zero(&input);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina input.roots = set_roots;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina input.service = "doveadm";
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina (void)net_getsockname(conn->fd, &input.local_ip, &port);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina (void)net_getpeername(conn->fd, &input.remote_ip, &port);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina if (master_service_settings_read(master_service, &input,
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina &output, &error) < 0) {
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina i_error("Error reading configuration: %s", error);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina return -1;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina }
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina set = master_service_settings_get_others(master_service)[0];
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina conn->set = settings_dup(&doveadm_setting_parser_info, set, conn->pool);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina return 0;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina}
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březinastatic int server_connection_ssl_handshaked(const char **error_r, void *context)
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina{
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina struct server_connection *conn = context;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina const char *host, *p;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina host = conn->server->name;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina p = strrchr(host, ':');
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina if (p != NULL)
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina host = t_strdup_until(host, p);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina if (ssl_iostream_check_cert_validity(conn->ssl_iostream, host, error_r) < 0)
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina return -1;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina if (doveadm_debug)
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina i_debug("%s: SSL handshake successful", conn->server->name);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina return 0;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina}
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březinastatic int server_connection_init_ssl(struct server_connection *conn)
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina{
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina struct ssl_iostream_settings ssl_set;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina const char *error;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina if (conn->server->ssl_ctx == NULL)
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina return 0;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina i_zero(&ssl_set);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina ssl_set.verbose_invalid_cert = TRUE;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina if (io_stream_create_ssl_client(conn->server->ssl_ctx,
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina conn->server->name, &ssl_set,
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina &conn->input, &conn->output,
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina &conn->ssl_iostream, &error) < 0) {
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina i_error("Couldn't initialize SSL client: %s", error);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina return -1;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina }
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina ssl_iostream_set_handshake_callback(conn->ssl_iostream,
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina server_connection_ssl_handshaked,
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina conn);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina if (ssl_iostream_handshake(conn->ssl_iostream) < 0) {
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina i_error("SSL handshake failed: %s",
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina ssl_iostream_get_last_error(conn->ssl_iostream));
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina return -1;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina }
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina return 0;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina}
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březinaint server_connection_create(struct doveadm_server *server,
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina struct server_connection **conn_r)
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina{
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina#define DOVEADM_SERVER_HANDSHAKE "VERSION\tdoveadm-server\t1\t0\n"
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina struct server_connection *conn;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina pool_t pool;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina pool = pool_alloconly_create("doveadm server connection", 1024*16);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina conn = p_new(pool, struct server_connection, 1);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina conn->pool = pool;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina conn->server = server;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina conn->fd = doveadm_connect_with_default_port(server->name,
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina doveadm_settings->doveadm_port);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina net_set_nonblock(conn->fd, TRUE);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina conn->io = io_add(conn->fd, IO_READ, server_connection_input, conn);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina conn->input = i_stream_create_fd(conn->fd, MAX_INBUF_SIZE);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina conn->output = o_stream_create_fd(conn->fd, (size_t)-1);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina o_stream_set_flush_callback(conn->output, server_connection_output, conn);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina i_stream_set_name(conn->input, server->name);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina o_stream_set_name(conn->output, server->name);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina array_append(&conn->server->connections, &conn, 1);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina if (server_connection_read_settings(conn) < 0 ||
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina server_connection_init_ssl(conn) < 0) {
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina server_connection_destroy(&conn);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina return -1;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina }
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina o_stream_set_no_error_handling(conn->output, TRUE);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina conn->state = SERVER_REPLY_STATE_DONE;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina o_stream_nsend_str(conn->output, DOVEADM_SERVER_HANDSHAKE);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina *conn_r = conn;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina return 0;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina}
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březinavoid server_connection_destroy(struct server_connection **_conn)
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina{
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina struct server_connection *conn = *_conn;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina struct server_connection *const *conns;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina const char *error;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina unsigned int i, count;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina *_conn = NULL;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina conns = array_get(&conn->server->connections, &count);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina for (i = 0; i < count; i++) {
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina if (conns[i] == conn) {
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina array_delete(&conn->server->connections, i, 1);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina break;
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina }
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina }
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina if (conn->callback != NULL) {
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina error = conn->ssl_iostream == NULL ? NULL :
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina ssl_iostream_get_last_error(conn->ssl_iostream);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina if (error == NULL) {
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina error = conn->input->stream_errno == 0 ? "EOF" :
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina strerror(conn->input->stream_errno);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina }
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina server_connection_callback(conn, SERVER_EXIT_CODE_DISCONNECTED,
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina error);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina }
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina if (printing_conn == conn)
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina print_connection_released();
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina if (conn->to_input != NULL)
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina timeout_remove(&conn->to_input);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina if (conn->input != NULL)
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina i_stream_destroy(&conn->input);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina if (conn->output != NULL)
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina o_stream_destroy(&conn->output);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina if (conn->cmd_input != NULL)
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina i_stream_destroy(&conn->cmd_input);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina /* close cmd_output after its parent, so the "." isn't sent */
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina if (conn->cmd_output != NULL)
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina o_stream_destroy(&conn->cmd_output);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina if (conn->ssl_iostream != NULL)
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina ssl_iostream_unref(&conn->ssl_iostream);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina if (conn->io != NULL)
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina io_remove(&conn->io);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina if (conn->fd != -1) {
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina if (close(conn->fd) < 0)
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina i_error("close(server) failed: %m");
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina }
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina pool_unref(&conn->pool);
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina}
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březinastruct doveadm_server *
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březinaserver_connection_get_server(struct server_connection *conn)
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina{
a641a13889d617aca6bd998025e9087e822ff7f0Pavel Březina return conn->server;
bc854800cc67271205d63136daaf68d7863cea6bJustin Stephenson}
bc854800cc67271205d63136daaf68d7863cea6bJustin Stephenson
bc854800cc67271205d63136daaf68d7863cea6bJustin Stephensonvoid server_connection_cmd(struct server_connection *conn, const char *line,
bc854800cc67271205d63136daaf68d7863cea6bJustin Stephenson struct istream *cmd_input,
bc854800cc67271205d63136daaf68d7863cea6bJustin Stephenson server_cmd_callback_t *callback, void *context)
bc854800cc67271205d63136daaf68d7863cea6bJustin Stephenson{
bc854800cc67271205d63136daaf68d7863cea6bJustin Stephenson i_assert(conn->delayed_cmd == NULL);
bc854800cc67271205d63136daaf68d7863cea6bJustin Stephenson
bc854800cc67271205d63136daaf68d7863cea6bJustin Stephenson conn->state = SERVER_REPLY_STATE_PRINT;
bc854800cc67271205d63136daaf68d7863cea6bJustin Stephenson if (cmd_input != NULL) {
bc854800cc67271205d63136daaf68d7863cea6bJustin Stephenson i_assert(conn->cmd_input == NULL);
84060f52e782b079337ee7a99bb7ad17e8c84fbbPavel Březina i_stream_ref(cmd_input);
84060f52e782b079337ee7a99bb7ad17e8c84fbbPavel Březina conn->cmd_input = cmd_input;
84060f52e782b079337ee7a99bb7ad17e8c84fbbPavel Březina }
84060f52e782b079337ee7a99bb7ad17e8c84fbbPavel Březina if (!conn->authenticated)
84060f52e782b079337ee7a99bb7ad17e8c84fbbPavel Březina conn->delayed_cmd = p_strdup(conn->pool, line);
84060f52e782b079337ee7a99bb7ad17e8c84fbbPavel Březina else {
84060f52e782b079337ee7a99bb7ad17e8c84fbbPavel Březina o_stream_nsend_str(conn->output, line);
84060f52e782b079337ee7a99bb7ad17e8c84fbbPavel Březina server_connection_send_cmd_input(conn);
84060f52e782b079337ee7a99bb7ad17e8c84fbbPavel Březina }
84060f52e782b079337ee7a99bb7ad17e8c84fbbPavel Březina conn->callback = callback;
84060f52e782b079337ee7a99bb7ad17e8c84fbbPavel Březina conn->context = context;
84060f52e782b079337ee7a99bb7ad17e8c84fbbPavel Březina}
84060f52e782b079337ee7a99bb7ad17e8c84fbbPavel Březina
84060f52e782b079337ee7a99bb7ad17e8c84fbbPavel Březinabool server_connection_is_idle(struct server_connection *conn)
84060f52e782b079337ee7a99bb7ad17e8c84fbbPavel Březina{
84060f52e782b079337ee7a99bb7ad17e8c84fbbPavel Březina return conn->callback == NULL;
84060f52e782b079337ee7a99bb7ad17e8c84fbbPavel Březina}
84060f52e782b079337ee7a99bb7ad17e8c84fbbPavel Březina
84060f52e782b079337ee7a99bb7ad17e8c84fbbPavel Březinavoid server_connection_extract(struct server_connection *conn,
84060f52e782b079337ee7a99bb7ad17e8c84fbbPavel Březina struct istream **istream_r,
84060f52e782b079337ee7a99bb7ad17e8c84fbbPavel Březina struct ostream **ostream_r,
84060f52e782b079337ee7a99bb7ad17e8c84fbbPavel Březina struct ssl_iostream **ssl_iostream_r)
84060f52e782b079337ee7a99bb7ad17e8c84fbbPavel Březina{
84060f52e782b079337ee7a99bb7ad17e8c84fbbPavel Březina *istream_r = conn->input;
84060f52e782b079337ee7a99bb7ad17e8c84fbbPavel Březina *ostream_r = conn->output;
84060f52e782b079337ee7a99bb7ad17e8c84fbbPavel Březina *ssl_iostream_r = conn->ssl_iostream;
84060f52e782b079337ee7a99bb7ad17e8c84fbbPavel Březina
84060f52e782b079337ee7a99bb7ad17e8c84fbbPavel Březina conn->input = NULL;
84060f52e782b079337ee7a99bb7ad17e8c84fbbPavel Březina conn->output = NULL;
84060f52e782b079337ee7a99bb7ad17e8c84fbbPavel Březina conn->ssl_iostream = NULL;
84060f52e782b079337ee7a99bb7ad17e8c84fbbPavel Březina if (conn->io != NULL)
84060f52e782b079337ee7a99bb7ad17e8c84fbbPavel Březina io_remove(&conn->io);
84060f52e782b079337ee7a99bb7ad17e8c84fbbPavel Březina conn->fd = -1;
84060f52e782b079337ee7a99bb7ad17e8c84fbbPavel Březina}
84060f52e782b079337ee7a99bb7ad17e8c84fbbPavel Březina