server-connection.c revision 956b8eea7ae479a38b25175447fc8eac2df30480
e59faf65ce864fe95dc00f5d52b8323cdbd0608aTimo Sirainen/* Copyright (c) 2010-2013 Dovecot authors, see the included COPYING file */
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen#include "lib.h"
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen#include "array.h"
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen#include "base64.h"
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen#include "ioloop.h"
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen#include "net.h"
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen#include "istream.h"
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen#include "ostream.h"
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen#include "str.h"
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen#include "strescape.h"
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen#include "iostream-ssl.h"
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen#include "master-service.h"
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen#include "master-service-settings.h"
beffc30d933c5e134c45cc871852a8427eba7e70Timo Sirainen#include "settings-parser.h"
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen#include "doveadm-print.h"
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen#include "doveadm-util.h"
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen#include "doveadm-server.h"
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen#include "doveadm-settings.h"
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen#include "server-connection.h"
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen#include <sysexits.h>
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen#include <unistd.h>
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen#define MAX_INBUF_SIZE (1024*32)
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainenenum server_reply_state {
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen SERVER_REPLY_STATE_DONE = 0,
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen SERVER_REPLY_STATE_PRINT,
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen SERVER_REPLY_STATE_RET
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen};
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainenstruct server_connection {
5a0ac2e5ef482016e00575a7dce83f52c1704732Timo Sirainen struct doveadm_server *server;
5a0ac2e5ef482016e00575a7dce83f52c1704732Timo Sirainen
7823ef73e51bb81a17dcb306aff89016d4ce258fTimo Sirainen pool_t pool;
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen struct doveadm_settings *set;
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen int fd;
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen struct io *io;
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen struct istream *input;
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen struct ostream *output;
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen struct ssl_iostream *ssl_iostream;
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen const char *delayed_cmd;
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen server_cmd_callback_t *callback;
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen void *context;
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen enum server_reply_state state;
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen unsigned int handshaked:1;
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen unsigned int authenticated:1;
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen unsigned int streaming:1;
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen};
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainenstatic struct server_connection *printing_conn = NULL;
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen
beffc30d933c5e134c45cc871852a8427eba7e70Timo Sirainenstatic void server_connection_input(struct server_connection *conn);
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainenstatic void print_connection_released(void)
4ce6338bf945cccfff9e4ce7cc6aa2246851b84aTimo Sirainen{
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen struct doveadm_server *server = printing_conn->server;
beffc30d933c5e134c45cc871852a8427eba7e70Timo Sirainen struct server_connection *const *conns;
beffc30d933c5e134c45cc871852a8427eba7e70Timo Sirainen unsigned int i, count;
beffc30d933c5e134c45cc871852a8427eba7e70Timo Sirainen
beffc30d933c5e134c45cc871852a8427eba7e70Timo Sirainen printing_conn = NULL;
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen
4ce6338bf945cccfff9e4ce7cc6aa2246851b84aTimo Sirainen conns = array_get(&server->connections, &count);
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen for (i = 0; i < count; i++) {
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen if (conns[i]->io != NULL)
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen continue;
beffc30d933c5e134c45cc871852a8427eba7e70Timo Sirainen
64055bc6d2ed9e25b3b1db3b5b90d0bdb77cd715Timo Sirainen conns[i]->io = io_add(conns[i]->fd, IO_READ,
64055bc6d2ed9e25b3b1db3b5b90d0bdb77cd715Timo Sirainen server_connection_input, conns[i]);
64055bc6d2ed9e25b3b1db3b5b90d0bdb77cd715Timo Sirainen server_connection_input(conns[i]);
64055bc6d2ed9e25b3b1db3b5b90d0bdb77cd715Timo Sirainen if (printing_conn != NULL)
64055bc6d2ed9e25b3b1db3b5b90d0bdb77cd715Timo Sirainen break;
64055bc6d2ed9e25b3b1db3b5b90d0bdb77cd715Timo Sirainen }
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen}
beffc30d933c5e134c45cc871852a8427eba7e70Timo Sirainen
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainenstatic void
beffc30d933c5e134c45cc871852a8427eba7e70Timo Sirainenserver_connection_callback(struct server_connection *conn,
4ce6338bf945cccfff9e4ce7cc6aa2246851b84aTimo Sirainen int exit_code, const char *error)
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen{
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen server_cmd_callback_t *callback = conn->callback;
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen conn->callback = NULL;
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen callback(exit_code, error, conn->context);
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen}
beffc30d933c5e134c45cc871852a8427eba7e70Timo Sirainen
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainenstatic void stream_data(string_t *str, const unsigned char *data, size_t size)
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen{
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen const char *text;
7026c16186f543e11af12b8b87f396006db93297Timo Sirainen
7026c16186f543e11af12b8b87f396006db93297Timo Sirainen str_truncate(str, 0);
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen str_append_n(str, data, size);
7026c16186f543e11af12b8b87f396006db93297Timo Sirainen text = str_tabunescape(str_c_modifiable(str));
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen doveadm_print_stream(text, strlen(text));
beffc30d933c5e134c45cc871852a8427eba7e70Timo Sirainen}
beffc30d933c5e134c45cc871852a8427eba7e70Timo Sirainen
beffc30d933c5e134c45cc871852a8427eba7e70Timo Sirainenstatic void server_flush_field(struct server_connection *conn, string_t *str,
beffc30d933c5e134c45cc871852a8427eba7e70Timo Sirainen const unsigned char *data, size_t size)
beffc30d933c5e134c45cc871852a8427eba7e70Timo Sirainen{
beffc30d933c5e134c45cc871852a8427eba7e70Timo Sirainen if (conn->streaming) {
beffc30d933c5e134c45cc871852a8427eba7e70Timo Sirainen conn->streaming = FALSE;
beffc30d933c5e134c45cc871852a8427eba7e70Timo Sirainen if (size > 0)
beffc30d933c5e134c45cc871852a8427eba7e70Timo Sirainen stream_data(str, data, size);
beffc30d933c5e134c45cc871852a8427eba7e70Timo Sirainen doveadm_print_stream("", 0);
beffc30d933c5e134c45cc871852a8427eba7e70Timo Sirainen } else {
beffc30d933c5e134c45cc871852a8427eba7e70Timo Sirainen const char *text;
beffc30d933c5e134c45cc871852a8427eba7e70Timo Sirainen
beffc30d933c5e134c45cc871852a8427eba7e70Timo Sirainen str_truncate(str, 0);
beffc30d933c5e134c45cc871852a8427eba7e70Timo Sirainen str_append_n(str, data, size);
beffc30d933c5e134c45cc871852a8427eba7e70Timo Sirainen text = str_tabunescape(str_c_modifiable(str));
beffc30d933c5e134c45cc871852a8427eba7e70Timo Sirainen doveadm_print(text);
beffc30d933c5e134c45cc871852a8427eba7e70Timo Sirainen }
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen}
beffc30d933c5e134c45cc871852a8427eba7e70Timo Sirainen
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainenstatic void
beffc30d933c5e134c45cc871852a8427eba7e70Timo Sirainenserver_handle_input(struct server_connection *conn,
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen const unsigned char *data, size_t size)
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen{
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen string_t *str;
beffc30d933c5e134c45cc871852a8427eba7e70Timo Sirainen size_t i, start;
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen if (printing_conn == conn) {
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen /* continue printing */
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen } else if (printing_conn == NULL) {
64c48ffb71f1cf99acf375768fde4cff9b512648Timo Sirainen printing_conn = conn;
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen } else {
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen /* someone else is printing. don't continue until it
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen goes away */
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen io_remove(&conn->io);
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen return;
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen }
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen if (data[size-1] == '\001') {
beffc30d933c5e134c45cc871852a8427eba7e70Timo Sirainen /* last character is an escape */
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen size--;
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen }
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen
beffc30d933c5e134c45cc871852a8427eba7e70Timo Sirainen str = t_str_new(128);
4ce6338bf945cccfff9e4ce7cc6aa2246851b84aTimo Sirainen for (i = start = 0; i < size; i++) {
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen if (data[i] == '\n') {
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen if (i != start) {
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen i_error("doveadm server sent broken print input");
c09f9f95db314e7482c95e502e1c56ed6c555797Timo Sirainen server_connection_destroy(&conn);
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen return;
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen }
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen conn->state = SERVER_REPLY_STATE_RET;
beffc30d933c5e134c45cc871852a8427eba7e70Timo Sirainen i_stream_skip(conn->input, i + 1);
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen print_connection_released();
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen return;
beffc30d933c5e134c45cc871852a8427eba7e70Timo Sirainen }
beffc30d933c5e134c45cc871852a8427eba7e70Timo Sirainen if (data[i] == '\t') {
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen server_flush_field(conn, str, data + start, i - start);
beffc30d933c5e134c45cc871852a8427eba7e70Timo Sirainen start = i + 1;
beffc30d933c5e134c45cc871852a8427eba7e70Timo Sirainen }
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen }
4ce6338bf945cccfff9e4ce7cc6aa2246851b84aTimo Sirainen if (start != size) {
4ce6338bf945cccfff9e4ce7cc6aa2246851b84aTimo Sirainen conn->streaming = TRUE;
4ce6338bf945cccfff9e4ce7cc6aa2246851b84aTimo Sirainen stream_data(str, data + start, size - start);
beffc30d933c5e134c45cc871852a8427eba7e70Timo Sirainen }
beffc30d933c5e134c45cc871852a8427eba7e70Timo Sirainen i_stream_skip(conn->input, size);
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen}
beffc30d933c5e134c45cc871852a8427eba7e70Timo Sirainen
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainenstatic void server_connection_authenticated(struct server_connection *conn)
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen{
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen conn->authenticated = TRUE;
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen if (conn->delayed_cmd != NULL) {
beffc30d933c5e134c45cc871852a8427eba7e70Timo Sirainen o_stream_nsend_str(conn->output, conn->delayed_cmd);
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen conn->delayed_cmd = NULL;
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen }
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen}
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainenstatic int
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainenserver_connection_authenticate(struct server_connection *conn)
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen{
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen string_t *plain = t_str_new(128);
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen string_t *cmd = t_str_new(128);
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen if (*conn->set->doveadm_password == '\0') {
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen i_error("doveadm_password not set, "
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen "can't authenticate to remote server");
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen return -1;
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen }
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen str_append_c(plain, '\0');
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen str_append(plain, "doveadm");
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen str_append_c(plain, '\0');
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen str_append(plain, conn->set->doveadm_password);
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen str_append(cmd, "PLAIN\t");
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen base64_encode(plain->data, plain->used, cmd);
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen str_append_c(cmd, '\n');
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen o_stream_nsend(conn->output, cmd->data, cmd->used);
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen return 0;
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen}
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainenstatic void server_connection_input(struct server_connection *conn)
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen{
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen const unsigned char *data;
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen size_t size;
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen const char *line;
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen int exit_code;
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen if (!conn->handshaked) {
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen if ((line = i_stream_read_next_line(conn->input)) == NULL) {
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen if (conn->input->eof || conn->input->stream_errno != 0)
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen server_connection_destroy(&conn);
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen return;
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen }
beffc30d933c5e134c45cc871852a8427eba7e70Timo Sirainen
beffc30d933c5e134c45cc871852a8427eba7e70Timo Sirainen conn->handshaked = TRUE;
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen if (strcmp(line, "+") == 0)
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen server_connection_authenticated(conn);
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen else if (strcmp(line, "-") == 0) {
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen if (server_connection_authenticate(conn) < 0) {
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen server_connection_destroy(&conn);
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen return;
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen }
7026c16186f543e11af12b8b87f396006db93297Timo Sirainen return;
7026c16186f543e11af12b8b87f396006db93297Timo Sirainen } else {
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen i_error("doveadm server sent invalid handshake: %s",
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen line);
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen server_connection_destroy(&conn);
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen return;
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen }
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen }
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen if (i_stream_read(conn->input) < 0) {
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen /* disconnected */
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen server_connection_destroy(&conn);
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen return;
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen }
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen if (!conn->authenticated) {
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen if ((line = i_stream_next_line(conn->input)) == NULL)
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen return;
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen if (strcmp(line, "+") == 0)
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen server_connection_authenticated(conn);
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen else {
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen i_error("doveadm authentication failed (%s)", line+1);
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen server_connection_destroy(&conn);
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen return;
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen }
ab7b5b9286104974c2a572a499ccf8b56c5d2955Timo Sirainen }
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen
ab7b5b9286104974c2a572a499ccf8b56c5d2955Timo Sirainen data = i_stream_get_data(conn->input, &size);
ab7b5b9286104974c2a572a499ccf8b56c5d2955Timo Sirainen if (size == 0)
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen return;
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen
ab7b5b9286104974c2a572a499ccf8b56c5d2955Timo Sirainen switch (conn->state) {
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen case SERVER_REPLY_STATE_DONE:
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen i_error("doveadm server sent unexpected input");
ab7b5b9286104974c2a572a499ccf8b56c5d2955Timo Sirainen server_connection_destroy(&conn);
ab7b5b9286104974c2a572a499ccf8b56c5d2955Timo Sirainen return;
ab7b5b9286104974c2a572a499ccf8b56c5d2955Timo Sirainen case SERVER_REPLY_STATE_PRINT:
ab7b5b9286104974c2a572a499ccf8b56c5d2955Timo Sirainen server_handle_input(conn, data, size);
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen if (conn->state != SERVER_REPLY_STATE_RET)
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen break;
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen /* fall through */
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen case SERVER_REPLY_STATE_RET:
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen line = i_stream_next_line(conn->input);
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen if (line == NULL)
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen return;
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen if (line[0] == '+')
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen server_connection_callback(conn, 0, "");
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen else if (line[0] == '-') {
4ce6338bf945cccfff9e4ce7cc6aa2246851b84aTimo Sirainen line++;
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen if (strcmp(line, "NOUSER") == 0)
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen exit_code = EX_NOUSER;
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen else if (str_to_int(line, &exit_code) < 0) {
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen /* old doveadm-server */
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen exit_code = EX_TEMPFAIL;
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen }
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen server_connection_callback(conn, exit_code, line);
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen } else {
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen i_error("doveadm server sent broken input "
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen "(expected cmd reply): %s", line);
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen server_connection_destroy(&conn);
da2aa032ccfa8e7e4a4380ef738014549f4d2c2dTimo Sirainen break;
ab7b5b9286104974c2a572a499ccf8b56c5d2955Timo Sirainen }
ab7b5b9286104974c2a572a499ccf8b56c5d2955Timo Sirainen if (conn->callback == NULL) {
9511a40d933181045343110c8101b75887062aaeTimo Sirainen /* we're finished, close the connection */
f89cb43088c8b46d12d66ac924724b53ab14ce66Timo Sirainen server_connection_destroy(&conn);
}
break;
}
}
static int server_connection_read_settings(struct server_connection *conn)
{
const struct setting_parser_info *set_roots[] = {
&doveadm_setting_parser_info,
NULL
};
struct master_service_settings_input input;
struct master_service_settings_output output;
const char *error;
unsigned int port;
void *set;
memset(&input, 0, sizeof(input));
input.roots = set_roots;
input.service = "doveadm";
(void)net_getsockname(conn->fd, &input.local_ip, &port);
(void)net_getpeername(conn->fd, &input.remote_ip, &port);
if (master_service_settings_read(master_service, &input,
&output, &error) < 0) {
i_error("Error reading configuration: %s", error);
return -1;
}
set = master_service_settings_get_others(master_service)[0];
conn->set = settings_dup(&doveadm_setting_parser_info, set, conn->pool);
return 0;
}
static int server_connection_ssl_handshaked(const char **error_r, void *context)
{
struct server_connection *conn = context;
const char *host, *p;
host = conn->server->name;
p = strrchr(host, ':');
if (p != NULL)
host = t_strdup_until(host, p);
if (ssl_iostream_check_cert_validity(conn->ssl_iostream, host, error_r) < 0)
return -1;
if (doveadm_debug)
i_debug("%s: SSL handshake successful", conn->server->name);
return 0;
}
static int server_connection_init_ssl(struct server_connection *conn)
{
struct ssl_iostream_settings ssl_set;
const char *error;
if (conn->server->ssl_ctx == NULL)
return 0;
memset(&ssl_set, 0, sizeof(ssl_set));
ssl_set.verify_remote_cert = TRUE;
ssl_set.require_valid_cert = TRUE;
ssl_set.verbose_invalid_cert = TRUE;
if (io_stream_create_ssl_client(conn->server->ssl_ctx,
conn->server->name, &ssl_set,
&conn->input, &conn->output,
&conn->ssl_iostream, &error) < 0) {
i_error("Couldn't initialize SSL client: %s", error);
return -1;
}
ssl_iostream_set_handshake_callback(conn->ssl_iostream,
server_connection_ssl_handshaked,
conn);
if (ssl_iostream_handshake(conn->ssl_iostream) < 0) {
i_error("SSL handshake failed: %s",
ssl_iostream_get_last_error(conn->ssl_iostream));
return -1;
}
return 0;
}
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;
}
void server_connection_destroy(struct server_connection **_conn)
{
struct server_connection *conn = *_conn;
struct server_connection *const *conns;
const char *error;
unsigned int i, count;
*_conn = NULL;
conns = array_get(&conn->server->connections, &count);
for (i = 0; i < count; i++) {
if (conns[i] == conn) {
array_delete(&conn->server->connections, i, 1);
break;
}
}
if (conn->callback != NULL) {
error = conn->ssl_iostream == NULL ? NULL :
ssl_iostream_get_last_error(conn->ssl_iostream);
if (error == NULL) {
error = conn->input->stream_errno == 0 ? "EOF" :
strerror(conn->input->stream_errno);
}
server_connection_callback(conn, SERVER_EXIT_CODE_DISCONNECTED,
error);
}
if (printing_conn == conn)
print_connection_released();
if (conn->input != NULL)
i_stream_destroy(&conn->input);
if (conn->output != NULL)
o_stream_destroy(&conn->output);
if (conn->ssl_iostream != NULL)
ssl_iostream_unref(&conn->ssl_iostream);
if (conn->io != NULL)
io_remove(&conn->io);
if (conn->fd != -1) {
if (close(conn->fd) < 0)
i_error("close(server) failed: %m");
}
pool_unref(&conn->pool);
}
struct doveadm_server *
server_connection_get_server(struct server_connection *conn)
{
return conn->server;
}
void server_connection_cmd(struct server_connection *conn, const char *line,
server_cmd_callback_t *callback, void *context)
{
i_assert(conn->delayed_cmd == NULL);
conn->state = SERVER_REPLY_STATE_PRINT;
if (conn->authenticated)
o_stream_nsend_str(conn->output, line);
else
conn->delayed_cmd = p_strdup(conn->pool, line);
conn->callback = callback;
conn->context = context;
}
bool server_connection_is_idle(struct server_connection *conn)
{
return conn->callback == NULL;
}
void server_connection_extract(struct server_connection *conn,
struct istream **istream_r,
struct ostream **ostream_r,
struct ssl_iostream **ssl_iostream_r)
{
*istream_r = conn->input;
*ostream_r = conn->output;
*ssl_iostream_r = conn->ssl_iostream;
conn->input = NULL;
conn->output = NULL;
conn->ssl_iostream = NULL;
if (conn->io != NULL)
io_remove(&conn->io);
conn->fd = -1;
}