connection.c revision ddaf416216a83e71bc1bfc1b6faf2ead9d774613
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen/* Copyright (c) 2013-2018 Dovecot authors, see the included COPYING file */
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen#include "lib.h"
bdd36cfdba3ff66d25570a9ff568d69e1eb543cfTimo Sirainen#include "ioloop.h"
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen#include "istream.h"
9c6a09aa16095ff72837799a37e0e3b3e93bb3d8Timo Sirainen#include "istream-unix.h"
31a9637b38d37451b649c86301b2c12e53a7810eTimo Sirainen#include "ostream.h"
9c6a09aa16095ff72837799a37e0e3b3e93bb3d8Timo Sirainen#include "ostream-unix.h"
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen#include "iostream.h"
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen#include "net.h"
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen#include "strescape.h"
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen#include "llist.h"
6d2b3ce2c6ef62334985ece4f0ab8b154e0e9560Timo Sirainen#include "time-util.h"
c6335901c67a4c9365319190a111a2168f3b06f5Timo Sirainen#include "connection.h"
c6335901c67a4c9365319190a111a2168f3b06f5Timo Sirainen
c6335901c67a4c9365319190a111a2168f3b06f5Timo Sirainen#include <unistd.h>
01230de017cd273de41143d88e9c18df1243ae8aTimo Sirainen
01230de017cd273de41143d88e9c18df1243ae8aTimo Sirainenstatic void connection_idle_timeout(struct connection *conn)
047c00cd3f7f403672f81569413669238df8c15aTimo Sirainen{
1f9d1bedae25d86f26c239055c5487499dfeeb58Timo Sirainen conn->disconnect_reason = CONNECTION_DISCONNECT_IDLE_TIMEOUT;
047c00cd3f7f403672f81569413669238df8c15aTimo Sirainen conn->list->v.destroy(conn);
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen}
f37ecd72aad9b806aae83f71bacafdce32146945Timo Sirainen
f37ecd72aad9b806aae83f71bacafdce32146945Timo Sirainenstatic void connection_connect_timeout(struct connection *conn)
e2bdca8201e4aa1cd31332ffbdd4c6eef9151d5eTimo Sirainen{
e2bdca8201e4aa1cd31332ffbdd4c6eef9151d5eTimo Sirainen conn->disconnect_reason = CONNECTION_DISCONNECT_CONNECT_TIMEOUT;
e2bdca8201e4aa1cd31332ffbdd4c6eef9151d5eTimo Sirainen conn->list->v.destroy(conn);
32b78da5dfbbf6a06b3dbdc9278c60b55714f9bcTimo Sirainen}
32b78da5dfbbf6a06b3dbdc9278c60b55714f9bcTimo Sirainen
32b78da5dfbbf6a06b3dbdc9278c60b55714f9bcTimo Sirainenvoid connection_input_default(struct connection *conn)
32b78da5dfbbf6a06b3dbdc9278c60b55714f9bcTimo Sirainen{
8b5c520883aa37bb55646286d375fdbae294d710Timo Sirainen const char *line;
8b5c520883aa37bb55646286d375fdbae294d710Timo Sirainen struct istream *input;
8b5c520883aa37bb55646286d375fdbae294d710Timo Sirainen struct ostream *output;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen int ret = 0;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen switch (connection_input_read(conn)) {
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen case -1:
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen return;
f7423cbbd9dea363a5df18ebb96da055a977ae79Timo Sirainen case 0: /* allow calling this function for buffered input */
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen case 1:
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen break;
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen default:
7487ff578435377bbeefffdbfb78ca09ed1292dfTimo Sirainen i_unreached();
89237470342ea6d4bbdf4cff9764037cfb3f6f45Timo Sirainen }
89237470342ea6d4bbdf4cff9764037cfb3f6f45Timo Sirainen
db693bf6fcae96d834567f1782257517b7207655Timo Sirainen input = conn->input;
db693bf6fcae96d834567f1782257517b7207655Timo Sirainen output = conn->output;
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen i_stream_ref(input);
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen if (output != NULL) {
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen o_stream_ref(output);
db693bf6fcae96d834567f1782257517b7207655Timo Sirainen o_stream_cork(output);
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen }
4d4d6d4745682790c20d759ba93dbea46b812c5dTimo Sirainen while (!input->closed && (line = i_stream_next_line(input)) != NULL) {
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen T_BEGIN {
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen ret = conn->list->v.input_line(conn, line);
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen } T_END;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen if (ret <= 0)
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen break;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen }
a3fe8c0c54d87822f4b4f8f0d10caac611861b2bTimo Sirainen if (output != NULL) {
578ef2538ccf42e2a48234c24a8b709397101d88Timo Sirainen o_stream_uncork(output);
578ef2538ccf42e2a48234c24a8b709397101d88Timo Sirainen o_stream_unref(&output);
578ef2538ccf42e2a48234c24a8b709397101d88Timo Sirainen }
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen if (ret < 0 && !input->closed) {
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen conn->disconnect_reason = CONNECTION_DISCONNECT_DEINIT;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen conn->list->v.destroy(conn);
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen }
d6b3cfd855c0eebed68be50d3111de1b5a6afeb0Timo Sirainen i_stream_unref(&input);
d6b3cfd855c0eebed68be50d3111de1b5a6afeb0Timo Sirainen}
d6b3cfd855c0eebed68be50d3111de1b5a6afeb0Timo Sirainen
d6b3cfd855c0eebed68be50d3111de1b5a6afeb0Timo Sirainenint connection_verify_version(struct connection *conn,
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen const char *const *args)
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen{
d5eb47a791ec56149fd711cd8e44efc8babeaae5Timo Sirainen unsigned int recv_major_version;
d5eb47a791ec56149fd711cd8e44efc8babeaae5Timo Sirainen
d5eb47a791ec56149fd711cd8e44efc8babeaae5Timo Sirainen /* VERSION <tab> service_name <tab> major version <tab> minor version */
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen if (str_array_length(args) != 4 ||
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen strcmp(args[0], "VERSION") != 0 ||
e0740628f6ca05f4bc79a9d8a90b650f4d38d4d0Timo Sirainen str_to_uint(args[2], &recv_major_version) < 0 ||
e0740628f6ca05f4bc79a9d8a90b650f4d38d4d0Timo Sirainen str_to_uint(args[3], &conn->minor_version) < 0) {
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen i_error("%s didn't reply with a valid VERSION line: %s",
3f603ef00e35fca21605afa0ad8d76e94fee2b96Timo Sirainen conn->name, t_strarray_join(args, "\t"));
3f603ef00e35fca21605afa0ad8d76e94fee2b96Timo Sirainen return -1;
3f603ef00e35fca21605afa0ad8d76e94fee2b96Timo Sirainen }
3f603ef00e35fca21605afa0ad8d76e94fee2b96Timo Sirainen
86791365b10f45982c88e70f2eb94fd6c3fea151Timo Sirainen if (strcmp(args[1], conn->list->set.service_name_in) != 0) {
86791365b10f45982c88e70f2eb94fd6c3fea151Timo Sirainen i_error("%s: Connected to wrong socket type. "
86791365b10f45982c88e70f2eb94fd6c3fea151Timo Sirainen "We want '%s', but received '%s'", conn->name,
86791365b10f45982c88e70f2eb94fd6c3fea151Timo Sirainen conn->list->set.service_name_in, args[1]);
86791365b10f45982c88e70f2eb94fd6c3fea151Timo Sirainen return -1;
ccf50662cc02b5e703039a4ff7f91a4470e25b71Timo Sirainen }
ccf50662cc02b5e703039a4ff7f91a4470e25b71Timo Sirainen
ccf50662cc02b5e703039a4ff7f91a4470e25b71Timo Sirainen if (recv_major_version != conn->list->set.major_version) {
ccf50662cc02b5e703039a4ff7f91a4470e25b71Timo Sirainen i_error("%s: Socket supports major version %u, "
ccf50662cc02b5e703039a4ff7f91a4470e25b71Timo Sirainen "but we support only %u (mixed old and new binaries?)",
6fdfa4d4cf14d1d7764d7faa8258f112e39c8dbeTimo Sirainen conn->name, recv_major_version,
6fdfa4d4cf14d1d7764d7faa8258f112e39c8dbeTimo Sirainen conn->list->set.major_version);
6fdfa4d4cf14d1d7764d7faa8258f112e39c8dbeTimo Sirainen return -1;
6fdfa4d4cf14d1d7764d7faa8258f112e39c8dbeTimo Sirainen }
6fdfa4d4cf14d1d7764d7faa8258f112e39c8dbeTimo Sirainen return 0;
3f603ef00e35fca21605afa0ad8d76e94fee2b96Timo Sirainen}
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainenint connection_input_line_default(struct connection *conn, const char *line)
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen{
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen const char *const *args;
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen
1ffb2afe6d7e8860a2231a4827078cf2ef9c22cdTimo Sirainen args = t_strsplit_tabescaped(line);
1ffb2afe6d7e8860a2231a4827078cf2ef9c22cdTimo Sirainen if (!conn->version_received) {
1ffb2afe6d7e8860a2231a4827078cf2ef9c22cdTimo Sirainen if (connection_verify_version(conn, args) < 0)
d4845c4245638fd6f02dc0cb92c3465fae763cbbTimo Sirainen return -1;
d4845c4245638fd6f02dc0cb92c3465fae763cbbTimo Sirainen conn->version_received = TRUE;
0161376aac025266d8654577c4b9ce371ffc87eaTimo Sirainen return 1;
0161376aac025266d8654577c4b9ce371ffc87eaTimo Sirainen }
0161376aac025266d8654577c4b9ce371ffc87eaTimo Sirainen if (args[0] == NULL && !conn->list->set.allow_empty_args_input) {
0161376aac025266d8654577c4b9ce371ffc87eaTimo Sirainen i_error("%s: Unexpectedly received empty line", conn->name);
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen return -1;
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen }
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen return conn->list->v.input_args(conn, args);
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen}
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainenstatic void connection_init_streams(struct connection *conn)
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen{
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen const struct connection_settings *set = &conn->list->set;
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen i_assert(conn->io == NULL);
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen i_assert(conn->input == NULL);
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen i_assert(conn->output == NULL);
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen i_assert(conn->to == NULL);
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen conn->version_received = set->major_version == 0;
b2d562f9c7fd13f9a16e9b3bcee904630b80b1feTimo Sirainen
b2d562f9c7fd13f9a16e9b3bcee904630b80b1feTimo Sirainen if (set->input_max_size != 0) {
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen if (conn->unix_socket)
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen conn->input = i_stream_create_unix(conn->fd_in,
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen set->input_max_size);
a10ed8c47534b4c6b6bf2711ccfe577e720a47b4Timo Sirainen else
a10ed8c47534b4c6b6bf2711ccfe577e720a47b4Timo Sirainen conn->input = i_stream_create_fd(conn->fd_in,
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen set->input_max_size);
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen i_stream_set_name(conn->input, conn->name);
275385a2ecc58e41dc7df3ce3cd943caaa58c4d1Timo Sirainen conn->io = io_add_istream(conn->input, *conn->list->v.input, conn);
275385a2ecc58e41dc7df3ce3cd943caaa58c4d1Timo Sirainen } else {
275385a2ecc58e41dc7df3ce3cd943caaa58c4d1Timo Sirainen conn->io = io_add(conn->fd_in, IO_READ, *conn->list->v.input, conn);
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen }
71df09024cea5f2faa93da3bb9513ee96ba6bf22Timo Sirainen if (set->output_max_size != 0) {
57397188558fcd1a9e24dbbbd2952eac9c45c20dTimo Sirainen if (conn->unix_socket)
57397188558fcd1a9e24dbbbd2952eac9c45c20dTimo Sirainen conn->output = o_stream_create_unix(conn->fd_out,
57397188558fcd1a9e24dbbbd2952eac9c45c20dTimo Sirainen set->output_max_size);
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen else
6c2ce1d5bf17b21e804a079eb0f973b7ab83e0d8Timo Sirainen conn->output = o_stream_create_fd(conn->fd_out,
6c2ce1d5bf17b21e804a079eb0f973b7ab83e0d8Timo Sirainen set->output_max_size);
db693bf6fcae96d834567f1782257517b7207655Timo Sirainen o_stream_set_no_error_handling(conn->output, TRUE);
db693bf6fcae96d834567f1782257517b7207655Timo Sirainen o_stream_set_finish_via_child(conn->output, FALSE);
21e6b4fd844fd074583b17f09e1f27b9835ee238Timo Sirainen o_stream_set_name(conn->output, conn->name);
21e6b4fd844fd074583b17f09e1f27b9835ee238Timo Sirainen }
21e6b4fd844fd074583b17f09e1f27b9835ee238Timo Sirainen if (set->input_idle_timeout_secs != 0) {
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen conn->to = timeout_add(set->input_idle_timeout_secs*1000,
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen connection_idle_timeout, conn);
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen }
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen if (set->major_version != 0 && !set->dont_send_version) {
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen o_stream_nsend_str(conn->output, t_strdup_printf(
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen "VERSION\t%s\t%u\t%u\n", set->service_name_out,
f158d9a303bb15a6848ca276c9391c7ca52e452bTimo Sirainen set->major_version, set->minor_version));
f158d9a303bb15a6848ca276c9391c7ca52e452bTimo Sirainen }
f158d9a303bb15a6848ca276c9391c7ca52e452bTimo Sirainen}
f158d9a303bb15a6848ca276c9391c7ca52e452bTimo Sirainen
f158d9a303bb15a6848ca276c9391c7ca52e452bTimo Sirainenstatic void connection_client_connected(struct connection *conn, bool success)
6303f32ad4af9cb08794561e6324df1c6c5fb637Timo Sirainen{
6303f32ad4af9cb08794561e6324df1c6c5fb637Timo Sirainen i_assert(conn->list->set.client);
6303f32ad4af9cb08794561e6324df1c6c5fb637Timo Sirainen
6303f32ad4af9cb08794561e6324df1c6c5fb637Timo Sirainen conn->connect_finished = ioloop_timeval;
f158d9a303bb15a6848ca276c9391c7ca52e452bTimo Sirainen if (success)
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen connection_init_streams(conn);
if (conn->list->v.client_connected != NULL)
conn->list->v.client_connected(conn, success);
if (!success) {
conn->disconnect_reason =
CONNECTION_DISCONNECT_CONN_CLOSED;
conn->list->v.destroy(conn);
}
}
void connection_init(struct connection_list *list,
struct connection *conn)
{
conn->fd_in = -1;
conn->fd_out = -1;
conn->name = NULL;
if (conn->list != NULL) {
i_assert(conn->list == list);
} else {
conn->list = list;
DLLIST_PREPEND(&list->connections, conn);
list->connections_count++;
}
}
void connection_init_server(struct connection_list *list,
struct connection *conn, const char *name,
int fd_in, int fd_out)
{
i_assert(name != NULL);
i_assert(!list->set.client);
connection_init(list, conn);
conn->name = i_strdup(name);
conn->fd_in = fd_in;
conn->fd_out = fd_out;
connection_init_streams(conn);
}
void connection_init_client_ip(struct connection_list *list,
struct connection *conn,
const struct ip_addr *ip, in_port_t port)
{
i_assert(list->set.client);
connection_init(list, conn);
conn->fd_in = conn->fd_out = -1;
conn->name = i_strdup_printf("%s:%u", net_ip2addr(ip), port);
conn->ip = *ip;
conn->port = port;
}
void connection_init_client_unix(struct connection_list *list,
struct connection *conn, const char *path)
{
i_assert(list->set.client);
connection_init(list, conn);
conn->fd_in = conn->fd_out = -1;
conn->name = i_strdup(path);
conn->unix_socket = TRUE;
}
void connection_init_from_streams(struct connection_list *list,
struct connection *conn, const char *name,
struct istream *input, struct ostream *output)
{
i_assert(name != NULL);
connection_init(list, conn);
conn->name = i_strdup(name);
conn->fd_in = i_stream_get_fd(input);
conn->fd_out = o_stream_get_fd(output);
i_assert(conn->fd_in >= 0);
i_assert(conn->fd_out >= 0);
i_assert(conn->io == NULL);
i_assert(conn->input == NULL);
i_assert(conn->output == NULL);
i_assert(conn->to == NULL);
conn->input = input;
i_stream_ref(conn->input);
i_stream_set_name(conn->input, conn->name);
conn->output = output;
o_stream_ref(conn->output);
o_stream_set_no_error_handling(conn->output, TRUE);
o_stream_set_name(conn->output, conn->name);
conn->io = io_add_istream(conn->input, *list->v.input, conn);
if (list->v.client_connected != NULL)
list->v.client_connected(conn, TRUE);
}
static void connection_socket_connected(struct connection *conn)
{
io_remove(&conn->io);
timeout_remove(&conn->to);
errno = net_geterror(conn->fd_in);
connection_client_connected(conn, errno == 0);
}
int connection_client_connect(struct connection *conn)
{
const struct connection_settings *set = &conn->list->set;
int fd;
i_assert(conn->list->set.client);
i_assert(conn->fd_in == -1);
if (conn->port != 0)
fd = net_connect_ip(&conn->ip, conn->port, NULL);
else if (conn->list->set.unix_client_connect_msecs == 0)
fd = net_connect_unix(conn->name);
else
fd = net_connect_unix_with_retries(conn->name, conn->list->set.unix_client_connect_msecs);
if (fd == -1)
return -1;
conn->fd_in = conn->fd_out = fd;
conn->connect_started = ioloop_timeval;
if (conn->port != 0 ||
conn->list->set.delayed_unix_client_connected_callback) {
conn->io = io_add(conn->fd_out, IO_WRITE,
connection_socket_connected, conn);
if (set->client_connect_timeout_msecs != 0) {
conn->to = timeout_add(set->client_connect_timeout_msecs,
connection_connect_timeout, conn);
}
} else {
connection_client_connected(conn, TRUE);
}
return 0;
}
void connection_disconnect(struct connection *conn)
{
conn->last_input = 0;
i_zero(&conn->last_input_tv);
timeout_remove(&conn->to);
io_remove(&conn->io);
i_stream_close(conn->input);
i_stream_destroy(&conn->input);
o_stream_close(conn->output);
o_stream_destroy(&conn->output);
fd_close_maybe_stdio(&conn->fd_in, &conn->fd_out);
}
void connection_deinit(struct connection *conn)
{
i_assert(conn->list->connections_count > 0);
conn->list->connections_count--;
DLLIST_REMOVE(&conn->list->connections, conn);
connection_disconnect(conn);
i_free(conn->name);
}
int connection_input_read(struct connection *conn)
{
conn->last_input = ioloop_time;
conn->last_input_tv = ioloop_timeval;
if (conn->to != NULL)
timeout_reset(conn->to);
switch (i_stream_read(conn->input)) {
case -2:
/* buffer full */
switch (conn->list->set.input_full_behavior) {
case CONNECTION_BEHAVIOR_DESTROY:
conn->disconnect_reason =
CONNECTION_DISCONNECT_BUFFER_FULL;
conn->list->v.destroy(conn);
return -1;
case CONNECTION_BEHAVIOR_ALLOW:
return -2;
}
i_unreached();
case -1:
/* disconnected */
conn->disconnect_reason =
CONNECTION_DISCONNECT_CONN_CLOSED;
conn->list->v.destroy(conn);
return -1;
case 0:
/* nothing new read */
return 0;
default:
/* something was read */
return 1;
}
}
const char *connection_disconnect_reason(struct connection *conn)
{
switch (conn->disconnect_reason) {
case CONNECTION_DISCONNECT_DEINIT:
return "Deinitializing";
case CONNECTION_DISCONNECT_CONNECT_TIMEOUT: {
unsigned int msecs =
conn->list->set.client_connect_timeout_msecs;
return t_strdup_printf("connect() timed out in %u.%03u secs",
msecs/1000, msecs%1000);
}
case CONNECTION_DISCONNECT_IDLE_TIMEOUT:
return "Idle timeout";
case CONNECTION_DISCONNECT_CONN_CLOSED:
if (conn->input == NULL)
return t_strdup_printf("connect() failed: %m");
/* fall through */
case CONNECTION_DISCONNECT_NOT:
case CONNECTION_DISCONNECT_BUFFER_FULL:
return io_stream_get_disconnect_reason(conn->input, conn->output);
}
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)
{
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);
}