connection.c revision c85f67cfb8e1cef2de2b681debf4703d5818dc01
45312f52ff3a3d4c137447be4c7556500c2f8bf2Timo Sirainen/* Copyright (c) 2013-2018 Dovecot authors, see the included COPYING file */
df02611c44e9432e7961223bf9bfa3fb233b1789Timo Sirainen
df02611c44e9432e7961223bf9bfa3fb233b1789Timo Sirainen#include "lib.h"
df02611c44e9432e7961223bf9bfa3fb233b1789Timo Sirainen#include "ioloop.h"
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen#include "istream.h"
df02611c44e9432e7961223bf9bfa3fb233b1789Timo Sirainen#include "istream-unix.h"
3320f4770d1f6c2cdd10f3c4ca5a324beb335339Timo Sirainen#include "ostream.h"
df02611c44e9432e7961223bf9bfa3fb233b1789Timo Sirainen#include "ostream-unix.h"
b039dabf4c53f72454e795930e7643b6e0e625f9Timo Sirainen#include "iostream.h"
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen#include "net.h"
df02611c44e9432e7961223bf9bfa3fb233b1789Timo Sirainen#include "strescape.h"
df02611c44e9432e7961223bf9bfa3fb233b1789Timo Sirainen#include "llist.h"
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen#include "time-util.h"
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen#include "connection.h"
df02611c44e9432e7961223bf9bfa3fb233b1789Timo Sirainen
39afc7584d935b2dc7332c21966a7b20da03f1ecTimo Sirainen#include <unistd.h>
df02611c44e9432e7961223bf9bfa3fb233b1789Timo Sirainen
df02611c44e9432e7961223bf9bfa3fb233b1789Timo Sirainenstatic void connection_idle_timeout(struct connection *conn)
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen{
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen conn->disconnect_reason = CONNECTION_DISCONNECT_IDLE_TIMEOUT;
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen conn->list->v.destroy(conn);
b039dabf4c53f72454e795930e7643b6e0e625f9Timo Sirainen}
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen
df02611c44e9432e7961223bf9bfa3fb233b1789Timo Sirainenstatic void connection_connect_timeout(struct connection *conn)
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen{
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen conn->disconnect_reason = CONNECTION_DISCONNECT_CONNECT_TIMEOUT;
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen conn->list->v.destroy(conn);
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen}
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen
3320f4770d1f6c2cdd10f3c4ca5a324beb335339Timo Sirainenvoid connection_input_default(struct connection *conn)
39afc7584d935b2dc7332c21966a7b20da03f1ecTimo Sirainen{
39afc7584d935b2dc7332c21966a7b20da03f1ecTimo Sirainen const char *line;
39afc7584d935b2dc7332c21966a7b20da03f1ecTimo Sirainen struct istream *input;
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen struct ostream *output;
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen int ret = 0;
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen switch (connection_input_read(conn)) {
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen case -1:
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen return;
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen case 0: /* allow calling this function for buffered input */
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen case 1:
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen break;
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen default:
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen i_unreached();
82b990b0bb2a1dad5c2634a508a5ad87715db402Timo Sirainen }
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen input = conn->input;
3320f4770d1f6c2cdd10f3c4ca5a324beb335339Timo Sirainen output = conn->output;
3320f4770d1f6c2cdd10f3c4ca5a324beb335339Timo Sirainen i_stream_ref(input);
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen if (output != NULL) {
3320f4770d1f6c2cdd10f3c4ca5a324beb335339Timo Sirainen o_stream_ref(output);
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen o_stream_cork(output);
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen }
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen while (!input->closed && (line = i_stream_next_line(input)) != NULL) {
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen T_BEGIN {
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen ret = conn->list->v.input_line(conn, line);
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen } T_END;
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen if (ret <= 0)
3320f4770d1f6c2cdd10f3c4ca5a324beb335339Timo Sirainen break;
39afc7584d935b2dc7332c21966a7b20da03f1ecTimo Sirainen }
39afc7584d935b2dc7332c21966a7b20da03f1ecTimo Sirainen if (output != NULL) {
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen o_stream_uncork(output);
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen o_stream_unref(&output);
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen }
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen if (ret < 0 && !input->closed) {
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen conn->disconnect_reason = CONNECTION_DISCONNECT_DEINIT;
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen conn->list->v.destroy(conn);
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen }
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen i_stream_unref(&input);
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen}
3320f4770d1f6c2cdd10f3c4ca5a324beb335339Timo Sirainen
3320f4770d1f6c2cdd10f3c4ca5a324beb335339Timo Sirainenint connection_verify_version(struct connection *conn,
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen const char *const *args)
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen{
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen unsigned int recv_major_version;
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen /* VERSION <tab> service_name <tab> major version <tab> minor version */
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen if (str_array_length(args) != 4 ||
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen strcmp(args[0], "VERSION") != 0 ||
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen str_to_uint(args[2], &recv_major_version) < 0 ||
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen str_to_uint(args[3], &conn->minor_version) < 0) {
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen i_error("%s didn't reply with a valid VERSION line: %s",
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen conn->name, t_strarray_join(args, "\t"));
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen return -1;
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen }
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen if (strcmp(args[1], conn->list->set.service_name_in) != 0) {
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen i_error("%s: Connected to wrong socket type. "
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen "We want '%s', but received '%s'", conn->name,
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen conn->list->set.service_name_in, args[1]);
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen return -1;
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen }
b039dabf4c53f72454e795930e7643b6e0e625f9Timo Sirainen
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen if (recv_major_version != conn->list->set.major_version) {
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen i_error("%s: Socket supports major version %u, "
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen "but we support only %u (mixed old and new binaries?)",
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen conn->name, recv_major_version,
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen conn->list->set.major_version);
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen return -1;
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen }
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen return 0;
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen}
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainenint connection_input_line_default(struct connection *conn, const char *line)
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen{
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen const char *const *args;
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen args = t_strsplit_tabescaped(line);
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen if (!conn->version_received) {
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen if (connection_verify_version(conn, args) < 0)
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen return -1;
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen conn->version_received = TRUE;
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen return 1;
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen }
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen if (args[0] == NULL && !conn->list->set.allow_empty_args_input) {
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen i_error("%s: Unexpectedly received empty line", conn->name);
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen return -1;
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen }
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen return conn->list->v.input_args(conn, args);
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen}
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainenvoid connection_input_halt(struct connection *conn)
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen{
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen io_remove(&conn->io);
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen}
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainenvoid connection_input_resume(struct connection *conn)
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen{
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen const struct connection_settings *set = &conn->list->set;
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen if (conn->io != NULL)
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen return;
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen if (conn->from_streams || set->input_max_size != 0) {
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen conn->io = io_add_istream_to(conn->ioloop, conn->input,
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen *conn->list->v.input, conn);
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen } else {
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen conn->io = io_add_to(conn->ioloop, conn->fd_in, IO_READ,
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen *conn->list->v.input, conn);
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen }
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen}
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainenstatic void connection_init_streams(struct connection *conn)
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen{
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen const struct connection_settings *set = &conn->list->set;
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen i_assert(conn->io == NULL);
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen i_assert(conn->input == NULL);
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen i_assert(conn->output == NULL);
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen i_assert(conn->to == NULL);
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen conn->version_received = set->major_version == 0;
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen if (set->input_max_size != 0) {
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen if (conn->unix_socket)
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen conn->input = i_stream_create_unix(conn->fd_in,
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen set->input_max_size);
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen else
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen conn->input = i_stream_create_fd(conn->fd_in,
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen set->input_max_size);
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen i_stream_set_name(conn->input, conn->name);
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen }
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen if (set->output_max_size != 0) {
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen if (conn->unix_socket)
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen conn->output = o_stream_create_unix(conn->fd_out,
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen set->output_max_size);
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen else
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen conn->output = o_stream_create_fd(conn->fd_out,
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen set->output_max_size);
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen o_stream_set_no_error_handling(conn->output, TRUE);
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen o_stream_set_finish_via_child(conn->output, FALSE);
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen o_stream_set_name(conn->output, conn->name);
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen }
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen i_stream_switch_ioloop_to(conn->input, conn->ioloop);
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen o_stream_switch_ioloop_to(conn->output, conn->ioloop);
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen connection_input_resume(conn);
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen if (set->input_idle_timeout_secs != 0) {
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen conn->to = timeout_add_to(conn->ioloop,
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen set->input_idle_timeout_secs*1000,
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen connection_idle_timeout, conn);
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen }
df02611c44e9432e7961223bf9bfa3fb233b1789Timo Sirainen if (set->major_version != 0 && !set->dont_send_version) {
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen o_stream_nsend_str(conn->output, t_strdup_printf(
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen "VERSION\t%s\t%u\t%u\n", set->service_name_out,
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen set->major_version, set->minor_version));
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen }
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen}
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainenvoid connection_streams_changed(struct connection *conn)
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen{
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen const struct connection_settings *set = &conn->list->set;
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen if (set->input_max_size != 0 && conn->io != NULL) {
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen connection_input_halt(conn);
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen connection_input_resume(conn);
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen }
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen}
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainenstatic void connection_client_connected(struct connection *conn, bool success)
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen{
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen i_assert(conn->list->set.client);
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen conn->connect_finished = ioloop_timeval;
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen if (success)
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen connection_init_streams(conn);
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen if (conn->list->v.client_connected != NULL)
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen conn->list->v.client_connected(conn, success);
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen if (!success) {
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen conn->disconnect_reason =
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen CONNECTION_DISCONNECT_CONN_CLOSED;
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen conn->list->v.destroy(conn);
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen }
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen}
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainenvoid connection_init(struct connection_list *list,
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen struct connection *conn)
3320f4770d1f6c2cdd10f3c4ca5a324beb335339Timo Sirainen{
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen conn->ioloop = current_ioloop;
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen conn->fd_in = -1;
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen conn->fd_out = -1;
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen conn->name = NULL;
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen if (conn->list != NULL) {
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen i_assert(conn->list == list);
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen } else {
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen conn->list = list;
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen DLLIST_PREPEND(&list->connections, conn);
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen list->connections_count++;
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen }
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen}
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainenvoid connection_init_server(struct connection_list *list,
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen struct connection *conn, const char *name,
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen int fd_in, int fd_out)
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen{
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen i_assert(name != NULL);
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen i_assert(!list->set.client);
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen connection_init(list, conn);
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen conn->name = i_strdup(name);
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen conn->fd_in = fd_in;
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen conn->fd_out = fd_out;
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen connection_init_streams(conn);
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen}
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainenvoid connection_init_client_ip(struct connection_list *list,
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen struct connection *conn,
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen const struct ip_addr *ip, in_port_t port)
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen{
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen i_assert(list->set.client);
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen connection_init(list, conn);
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen conn->fd_in = conn->fd_out = -1;
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen conn->name = i_strdup_printf("%s:%u", net_ip2addr(ip), port);
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen conn->ip = *ip;
df02611c44e9432e7961223bf9bfa3fb233b1789Timo Sirainen conn->port = port;
df02611c44e9432e7961223bf9bfa3fb233b1789Timo Sirainen}
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainenvoid connection_init_client_unix(struct connection_list *list,
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen struct connection *conn, const char *path)
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen{
df02611c44e9432e7961223bf9bfa3fb233b1789Timo Sirainen i_assert(list->set.client);
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen connection_init(list, conn);
df02611c44e9432e7961223bf9bfa3fb233b1789Timo Sirainen
19e8adccba16ff419f5675b1575358c2956dce83Timo Sirainen conn->fd_in = conn->fd_out = -1;
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen conn->name = i_strdup(path);
19e8adccba16ff419f5675b1575358c2956dce83Timo Sirainen conn->unix_socket = TRUE;
df02611c44e9432e7961223bf9bfa3fb233b1789Timo Sirainen}
df02611c44e9432e7961223bf9bfa3fb233b1789Timo Sirainen
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainenvoid connection_init_from_streams(struct connection_list *list,
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen struct connection *conn, const char *name,
df02611c44e9432e7961223bf9bfa3fb233b1789Timo Sirainen struct istream *input, struct ostream *output)
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen{
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen i_assert(name != NULL);
3320f4770d1f6c2cdd10f3c4ca5a324beb335339Timo Sirainen
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen connection_init(list, conn);
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen conn->name = i_strdup(name);
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen conn->from_streams = TRUE;
3320f4770d1f6c2cdd10f3c4ca5a324beb335339Timo Sirainen conn->fd_in = i_stream_get_fd(input);
3320f4770d1f6c2cdd10f3c4ca5a324beb335339Timo Sirainen conn->fd_out = o_stream_get_fd(output);
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen
3320f4770d1f6c2cdd10f3c4ca5a324beb335339Timo Sirainen i_assert(conn->fd_in >= 0);
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen i_assert(conn->fd_out >= 0);
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen i_assert(conn->io == NULL);
3320f4770d1f6c2cdd10f3c4ca5a324beb335339Timo Sirainen i_assert(conn->input == NULL);
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen i_assert(conn->output == NULL);
df02611c44e9432e7961223bf9bfa3fb233b1789Timo Sirainen i_assert(conn->to == NULL);
df02611c44e9432e7961223bf9bfa3fb233b1789Timo Sirainen
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen conn->input = input;
df02611c44e9432e7961223bf9bfa3fb233b1789Timo Sirainen i_stream_ref(conn->input);
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen i_stream_set_name(conn->input, conn->name);
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen conn->output = output;
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen o_stream_ref(conn->output);
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen o_stream_set_no_error_handling(conn->output, TRUE);
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen o_stream_set_name(conn->output, conn->name);
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen connection_input_resume(conn);
df02611c44e9432e7961223bf9bfa3fb233b1789Timo Sirainen
df02611c44e9432e7961223bf9bfa3fb233b1789Timo Sirainen if (list->v.client_connected != NULL)
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen list->v.client_connected(conn, TRUE);
df02611c44e9432e7961223bf9bfa3fb233b1789Timo Sirainen}
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainenstatic void connection_socket_connected(struct connection *conn)
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen{
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen io_remove(&conn->io);
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen timeout_remove(&conn->to);
df02611c44e9432e7961223bf9bfa3fb233b1789Timo Sirainen
df02611c44e9432e7961223bf9bfa3fb233b1789Timo Sirainen errno = net_geterror(conn->fd_in);
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen connection_client_connected(conn, errno == 0);
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen}
1f6c210c30992e95b806d2f517e2b3625ed941c5Timo Sirainen
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainenint connection_client_connect(struct connection *conn)
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen{
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen const struct connection_settings *set = &conn->list->set;
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen int fd;
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen i_assert(conn->list->set.client);
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen i_assert(conn->fd_in == -1);
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen if (conn->port != 0)
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen fd = net_connect_ip(&conn->ip, conn->port, NULL);
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen else if (conn->list->set.unix_client_connect_msecs == 0)
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen fd = net_connect_unix(conn->name);
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen else
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen fd = net_connect_unix_with_retries(conn->name, conn->list->set.unix_client_connect_msecs);
1f6c210c30992e95b806d2f517e2b3625ed941c5Timo Sirainen if (fd == -1)
1f6c210c30992e95b806d2f517e2b3625ed941c5Timo Sirainen return -1;
1f6c210c30992e95b806d2f517e2b3625ed941c5Timo Sirainen conn->fd_in = conn->fd_out = fd;
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen conn->connect_started = ioloop_timeval;
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen if (conn->port != 0 ||
1f6c210c30992e95b806d2f517e2b3625ed941c5Timo Sirainen conn->list->set.delayed_unix_client_connected_callback) {
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen conn->io = io_add_to(conn->ioloop, conn->fd_out, IO_WRITE,
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen connection_socket_connected, conn);
1f6c210c30992e95b806d2f517e2b3625ed941c5Timo Sirainen if (set->client_connect_timeout_msecs != 0) {
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen conn->to = timeout_add_to(conn->ioloop,
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen set->client_connect_timeout_msecs,
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen connection_connect_timeout, conn);
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen }
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen } else {
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen connection_client_connected(conn, TRUE);
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen }
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen return 0;
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen}
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainenvoid connection_disconnect(struct connection *conn)
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen{
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen conn->last_input = 0;
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen i_zero(&conn->last_input_tv);
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen timeout_remove(&conn->to);
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen io_remove(&conn->io);
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen i_stream_close(conn->input);
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen i_stream_destroy(&conn->input);
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen o_stream_close(conn->output);
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen o_stream_destroy(&conn->output);
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen fd_close_maybe_stdio(&conn->fd_in, &conn->fd_out);
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen}
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainenvoid connection_deinit(struct connection *conn)
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen{
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen i_assert(conn->list->connections_count > 0);
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen conn->list->connections_count--;
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen DLLIST_REMOVE(&conn->list->connections, conn);
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen connection_disconnect(conn);
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen i_free(conn->name);
df02611c44e9432e7961223bf9bfa3fb233b1789Timo Sirainen}
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainenint connection_input_read(struct connection *conn)
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen{
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen conn->last_input = ioloop_time;
df02611c44e9432e7961223bf9bfa3fb233b1789Timo Sirainen conn->last_input_tv = ioloop_timeval;
df02611c44e9432e7961223bf9bfa3fb233b1789Timo Sirainen if (conn->to != NULL)
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen timeout_reset(conn->to);
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen switch (i_stream_read(conn->input)) {
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen case -2:
df02611c44e9432e7961223bf9bfa3fb233b1789Timo Sirainen /* buffer full */
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen switch (conn->list->set.input_full_behavior) {
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen case CONNECTION_BEHAVIOR_DESTROY:
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen conn->disconnect_reason =
df02611c44e9432e7961223bf9bfa3fb233b1789Timo Sirainen CONNECTION_DISCONNECT_BUFFER_FULL;
df02611c44e9432e7961223bf9bfa3fb233b1789Timo Sirainen conn->list->v.destroy(conn);
df02611c44e9432e7961223bf9bfa3fb233b1789Timo Sirainen return -1;
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen case CONNECTION_BEHAVIOR_ALLOW:
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen return -2;
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen }
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen i_unreached();
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen case -1:
82b990b0bb2a1dad5c2634a508a5ad87715db402Timo Sirainen /* disconnected */
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen conn->disconnect_reason =
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen CONNECTION_DISCONNECT_CONN_CLOSED;
82b990b0bb2a1dad5c2634a508a5ad87715db402Timo Sirainen conn->list->v.destroy(conn);
82b990b0bb2a1dad5c2634a508a5ad87715db402Timo Sirainen return -1;
82b990b0bb2a1dad5c2634a508a5ad87715db402Timo Sirainen case 0:
82b990b0bb2a1dad5c2634a508a5ad87715db402Timo Sirainen /* nothing new read */
82b990b0bb2a1dad5c2634a508a5ad87715db402Timo Sirainen return 0;
82b990b0bb2a1dad5c2634a508a5ad87715db402Timo Sirainen default:
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen /* something was read */
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen return 1;
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen }
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen}
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainenconst char *connection_disconnect_reason(struct connection *conn)
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen{
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen switch (conn->disconnect_reason) {
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen case CONNECTION_DISCONNECT_DEINIT:
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen return "Deinitializing";
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen case CONNECTION_DISCONNECT_CONNECT_TIMEOUT: {
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen unsigned int msecs =
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen conn->list->set.client_connect_timeout_msecs;
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen return t_strdup_printf("connect() timed out in %u.%03u secs",
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen msecs/1000, msecs%1000);
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen }
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen case CONNECTION_DISCONNECT_IDLE_TIMEOUT:
df02611c44e9432e7961223bf9bfa3fb233b1789Timo Sirainen return "Idle timeout";
df02611c44e9432e7961223bf9bfa3fb233b1789Timo Sirainen case CONNECTION_DISCONNECT_CONN_CLOSED:
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen if (conn->input == NULL)
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen return t_strdup_printf("connect() failed: %m");
df02611c44e9432e7961223bf9bfa3fb233b1789Timo Sirainen /* fall through */
df02611c44e9432e7961223bf9bfa3fb233b1789Timo Sirainen case CONNECTION_DISCONNECT_NOT:
df02611c44e9432e7961223bf9bfa3fb233b1789Timo Sirainen case CONNECTION_DISCONNECT_BUFFER_FULL:
df02611c44e9432e7961223bf9bfa3fb233b1789Timo Sirainen return io_stream_get_disconnect_reason(conn->input, conn->output);
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen }
df02611c44e9432e7961223bf9bfa3fb233b1789Timo Sirainen i_unreached();
}
const char *connection_input_timeout_reason(struct connection *conn)
{
if (conn->last_input_tv.tv_sec != 0) {
int diff = timeval_diff_msecs(&ioloop_timeval, &conn->last_input_tv);
return t_strdup_printf("No input for %u.%03u secs",
diff/1000, diff%1000);
} else if (conn->connect_finished.tv_sec != 0) {
int diff = timeval_diff_msecs(&ioloop_timeval, &conn->connect_finished);
return t_strdup_printf(
"No input since connected %u.%03u secs ago",
diff/1000, diff%1000);
} else {
int diff = timeval_diff_msecs(&ioloop_timeval, &conn->connect_started);
return t_strdup_printf("connect() timed out after %u.%03u secs",
diff/1000, diff%1000);
}
}
void connection_switch_ioloop_to(struct connection *conn,
struct ioloop *ioloop)
{
conn->ioloop = ioloop;
if (conn->io != NULL)
conn->io = io_loop_move_io_to(ioloop, &conn->io);
if (conn->to != NULL)
conn->to = io_loop_move_timeout_to(ioloop, &conn->to);
if (conn->input != NULL)
i_stream_switch_ioloop_to(conn->input, ioloop);
if (conn->output != NULL)
o_stream_switch_ioloop_to(conn->output, ioloop);
}
void connection_switch_ioloop(struct connection *conn)
{
connection_switch_ioloop_to(conn, current_ioloop);
}
struct connection_list *
connection_list_init(const struct connection_settings *set,
const struct connection_vfuncs *vfuncs)
{
struct connection_list *list;
i_assert(vfuncs->input != NULL ||
set->input_full_behavior != CONNECTION_BEHAVIOR_ALLOW);
i_assert(set->major_version == 0 ||
(set->service_name_in != NULL &&
set->service_name_out != NULL &&
set->output_max_size != 0));
list = i_new(struct connection_list, 1);
list->set = *set;
list->v = *vfuncs;
if (list->v.input == NULL)
list->v.input = connection_input_default;
if (list->v.input_line == NULL)
list->v.input_line = connection_input_line_default;
return list;
}
void connection_list_deinit(struct connection_list **_list)
{
struct connection_list *list = *_list;
struct connection *conn;
*_list = NULL;
while (list->connections != NULL) {
conn = list->connections;
conn->disconnect_reason = CONNECTION_DISCONNECT_DEINIT;
list->v.destroy(conn);
i_assert(conn != list->connections);
}
i_free(list);
}