client-common.c revision 54533aa265f5c87730022cc7576090bc51370f97
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen/* Copyright (c) 2002-2009 Dovecot authors, see the included COPYING file */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include "common.h"
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include "hostpid.h"
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include "llist.h"
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include "istream.h"
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include "ostream.h"
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include "process-title.h"
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include "str.h"
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include "str-sanitize.h"
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include "safe-memset.h"
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include "var-expand.h"
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include "master-service.h"
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include "master-auth.h"
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include "auth-client.h"
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include "login-proxy.h"
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include "ssl-proxy.h"
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include "client-common.h"
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen#include <stdlib.h>
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenstruct client *clients = NULL, *last_client = NULL;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenstatic unsigned int clients_count = 0;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenstatic void client_idle_disconnect_timeout(struct client *client)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen{
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen client_send_line(client, CLIENT_CMD_REPLY_BYE,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen "Disconnected for inactivity.");
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen client_destroy(client, "Disconnected: Inactivity");
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen}
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenstatic void client_open_streams(struct client *client)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen{
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen client->input =
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen i_stream_create_fd(client->fd, LOGIN_MAX_INBUF_SIZE, FALSE);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen client->output =
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen o_stream_create_fd(client->fd, LOGIN_MAX_OUTBUF_SIZE, FALSE);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen}
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenstruct client *
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenclient_create(int fd, bool ssl, pool_t pool,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen const struct login_settings *set, void **other_sets,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen const struct ip_addr *local_ip, const struct ip_addr *remote_ip)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen{
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen struct client *client;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen i_assert(fd != -1);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen client = client_vfuncs.alloc(pool);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen client->v = client_vfuncs;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (client->v.auth_send_challenge == NULL)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen client->v.auth_send_challenge = client_auth_send_challenge;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (client->v.auth_parse_response == NULL)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen client->v.auth_parse_response = client_auth_parse_response;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen client->created = ioloop_time;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen client->refcount = 1;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen client->pool = pool;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen client->set = set;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen client->local_ip = *local_ip;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen client->ip = *remote_ip;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen client->fd = fd;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen client->tls = ssl;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen client->trusted = client_is_trusted(client);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen client->secured = ssl || client->trusted ||
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen net_ip_compare(remote_ip, local_ip);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (last_client == NULL)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen last_client = client;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen DLLIST_PREPEND(&clients, client);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen clients_count++;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen client->to_idle_disconnect =
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen timeout_add(CLIENT_LOGIN_IDLE_TIMEOUT_MSECS,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen client_idle_disconnect_timeout, client);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen client_open_streams(client);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen client->v.create(client, other_sets);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (auth_client_is_connected(auth_client))
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen client->v.send_greeting(client);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen else
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen client_set_auth_waiting(client);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen login_refresh_proctitle();
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return client;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen}
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenvoid client_destroy(struct client *client, const char *reason)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen{
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (client->destroyed)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen client->destroyed = TRUE;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (!client->login_success && reason != NULL) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen reason = t_strconcat(reason, " ",
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen client_get_extra_disconnect_reason(client), NULL);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (reason != NULL)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen client_log(client, reason);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (last_client == client)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen last_client = client->prev;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen DLLIST_REMOVE(&clients, client);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (client->input != NULL)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen i_stream_close(client->input);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (client->output != NULL)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen o_stream_close(client->output);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (client->master_tag != 0) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen i_assert(client->auth_request == NULL);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen i_assert(client->authenticating);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen i_assert(client->refcount > 1);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen client->authenticating = FALSE;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen master_auth_request_abort(master_auth, client->master_tag);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen client->refcount--;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen } else if (client->auth_request != NULL) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen i_assert(client->authenticating);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen sasl_server_auth_abort(client);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen } else {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen i_assert(!client->authenticating);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (client->io != NULL)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen io_remove(&client->io);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (client->to_idle_disconnect != NULL)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen timeout_remove(&client->to_idle_disconnect);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (client->to_auth_waiting != NULL)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen timeout_remove(&client->to_auth_waiting);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (client->to_authfail_delay != NULL)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen timeout_remove(&client->to_authfail_delay);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (client->auth_response != NULL)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen str_free(&client->auth_response);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (client->fd != -1) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen net_disconnect(client->fd);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen client->fd = -1;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (client->proxy_password != NULL) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen safe_memset(client->proxy_password, 0,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen strlen(client->proxy_password));
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen i_free_and_null(client->proxy_password);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen i_free_and_null(client->proxy_user);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen i_free_and_null(client->proxy_master_user);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (client->login_proxy != NULL)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen login_proxy_free(&client->login_proxy);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (client->ssl_proxy != NULL)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen ssl_proxy_free(&client->ssl_proxy);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen client->v.destroy(client);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (client_unref(&client) &&
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen master_service_get_service_count(master_service) == 1) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen /* as soon as this connection is done with proxying
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen (or whatever), the process will die. there's no need for
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen authentication anymore, so close the connection. */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (auth_client != NULL)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen auth_client_deinit(&auth_client);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen login_refresh_proctitle();
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen}
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenvoid client_destroy_success(struct client *client, const char *reason)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen{
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen client->login_success = TRUE;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen client_destroy(client, reason);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen}
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenvoid client_destroy_internal_failure(struct client *client)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen{
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen client_send_line(client, CLIENT_CMD_REPLY_AUTH_FAIL_TEMP,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen "Internal login failure. "
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen "Refer to server log for more information.");
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen client_destroy(client, "Internal login failure");
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen}
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenvoid client_ref(struct client *client)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen{
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen client->refcount++;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen}
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenbool client_unref(struct client **_client)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen{
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen struct client *client = *_client;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen i_assert(client->refcount > 0);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (--client->refcount > 0)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return TRUE;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen *_client = NULL;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen i_assert(client->destroyed);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen i_assert(client->ssl_proxy == NULL);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen i_assert(client->login_proxy == NULL);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (client->input != NULL)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen i_stream_unref(&client->input);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (client->output != NULL)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen o_stream_unref(&client->output);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen i_free(client->virtual_user);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen i_free(client->auth_mech_name);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen pool_unref(&client->pool);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen i_assert(clients_count > 0);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen clients_count--;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen master_service_client_connection_destroyed(master_service);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen login_refresh_proctitle();
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return FALSE;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen}
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenvoid client_destroy_oldest(void)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen{
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen struct client *client;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (last_client == NULL) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen /* we have no clients */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen /* destroy the last client that hasn't successfully authenticated yet.
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen this is usually the last client, but don't kill it if it's just
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen waiting for master to finish its job. */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen for (client = last_client; client != NULL; client = client->prev) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (client->master_tag == 0)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen break;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (client == NULL)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen client = last_client;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen client_destroy(client, "Disconnected: Connection queue full");
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen}
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenvoid clients_destroy_all(void)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen{
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen struct client *client, *next;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen for (client = clients; client != NULL; client = next) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen next = client->next;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen client_destroy(client, "Disconnected: Shutting down");
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen}
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenstatic void client_start_tls(struct client *client)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen{
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen int fd_ssl;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen client_ref(client);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (!client_unref(&client) || client->destroyed)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen fd_ssl = ssl_proxy_new(client->fd, &client->ip,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen client->set, &client->ssl_proxy);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (fd_ssl == -1) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen client_send_line(client, CLIENT_CMD_REPLY_BYE,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen "TLS initialization failed.");
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen client_destroy(client,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen "Disconnected: TLS initialization failed.");
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen ssl_proxy_set_client(client->ssl_proxy, client);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen client->starttls = TRUE;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen client->tls = TRUE;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen client->secured = TRUE;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen login_refresh_proctitle();
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen client->fd = fd_ssl;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen client->io = io_add(client->fd, IO_READ, client_input, client);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen i_stream_unref(&client->input);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen o_stream_unref(&client->output);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen client_open_streams(client);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen client->v.starttls(client);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen}
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenstatic int client_output_starttls(struct client *client)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen{
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen int ret;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if ((ret = o_stream_flush(client->output)) < 0) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen client_destroy(client, "Disconnected");
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return 1;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (ret > 0) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen o_stream_unset_flush_callback(client->output);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen client_start_tls(client);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return 1;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen}
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenvoid client_cmd_starttls(struct client *client)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen{
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (client->tls) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen client_send_line(client, CLIENT_CMD_REPLY_BAD,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen "TLS is already active.");
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return;
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen }
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (!ssl_initialized) {
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen client_send_line(client, CLIENT_CMD_REPLY_BAD,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen "TLS support isn't enabled.");
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen /* remove input handler, SSL proxy gives us a new fd. we also have to
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen remove it in case we have to wait for buffer to be flushed */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (client->io != NULL)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen io_remove(&client->io);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen client_send_line(client, CLIENT_CMD_REPLY_OK,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen "Begin TLS negotiation now.");
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen /* uncork the old fd */
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen o_stream_uncork(client->output);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (o_stream_flush(client->output) <= 0) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen /* the buffer has to be flushed */
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen o_stream_set_flush_pending(client->output, TRUE);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen o_stream_set_flush_callback(client->output,
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen client_output_starttls, client);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen } else {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen client_start_tls(client);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen}
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenunsigned int clients_get_count(void)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen{
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen return clients_count;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen}
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenstatic const struct var_expand_table *
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainenget_var_expand_table(struct client *client)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen{
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen static struct var_expand_table static_tab[] = {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen { 'u', NULL, "user" },
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen { 'n', NULL, "username" },
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen { 'd', NULL, "domain" },
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen { 's', NULL, "service" },
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen { 'h', NULL, "home" },
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen { 'l', NULL, "lip" },
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen { 'r', NULL, "rip" },
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen { 'p', NULL, "pid" },
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen { 'm', NULL, "mech" },
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen { 'a', NULL, "lport" },
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen { 'b', NULL, "rport" },
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen { 'c', NULL, "secured" },
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen { 'k', NULL, "ssl_security" },
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen { 'e', NULL, "mail_pid" },
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen { '\0', NULL, NULL }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen };
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen struct var_expand_table *tab;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen unsigned int i;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen tab = t_malloc(sizeof(static_tab));
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen memcpy(tab, static_tab, sizeof(static_tab));
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (client->virtual_user != NULL) {
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen tab[0].value = client->virtual_user;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen tab[1].value = t_strcut(client->virtual_user, '@');
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen tab[2].value = strchr(client->virtual_user, '@');
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen if (tab[2].value != NULL) tab[2].value++;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen for (i = 0; i < 3; i++)
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen tab[i].value = str_sanitize(tab[i].value, 80);
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen }
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen tab[3].value = login_protocol;
1ac7c8e9040e0d0b7e9f849e45b94bfe919595a9Timo Sirainen tab[4].value = getenv("HOME");
tab[5].value = net_ip2addr(&client->local_ip);
tab[6].value = net_ip2addr(&client->ip);
tab[7].value = my_pid;
tab[8].value = client->auth_mech_name == NULL ? NULL :
str_sanitize(client->auth_mech_name, MAX_MECH_NAME);
tab[9].value = dec2str(client->local_port);
tab[10].value = dec2str(client->remote_port);
if (!client->tls) {
tab[11].value = client->secured ? "secured" : NULL;
tab[12].value = "";
} else {
const char *ssl_state =
ssl_proxy_is_handshaked(client->ssl_proxy) ?
"TLS" : "TLS handshaking";
const char *ssl_error =
ssl_proxy_get_last_error(client->ssl_proxy);
tab[11].value = ssl_error == NULL ? ssl_state :
t_strdup_printf("%s: %s", ssl_state, ssl_error);
tab[12].value =
ssl_proxy_get_security_string(client->ssl_proxy);
}
tab[13].value = dec2str(client->mail_pid);
return tab;
}
static bool have_key(const struct var_expand_table *table, const char *str)
{
char key;
unsigned int i;
key = var_get_key(str);
for (i = 0; table[i].key != '\0'; i++) {
if (table[i].key == key) {
return table[i].value != NULL &&
table[i].value[0] != '\0';
}
}
return FALSE;
}
static const char *
client_get_log_str(struct client *client, const char *msg)
{
static struct var_expand_table static_tab[3] = {
{ 's', NULL, NULL },
{ '$', NULL, NULL },
{ '\0', NULL, NULL }
};
const struct var_expand_table *var_expand_table;
struct var_expand_table *tab;
const char *p;
char *const *e;
string_t *str;
var_expand_table = get_var_expand_table(client);
tab = t_malloc(sizeof(static_tab));
memcpy(tab, static_tab, sizeof(static_tab));
str = t_str_new(256);
for (e = client->set->log_format_elements_split; *e != NULL; e++) {
for (p = *e; *p != '\0'; p++) {
if (*p != '%' || p[1] == '\0')
continue;
p++;
if (have_key(var_expand_table, p)) {
if (str_len(str) > 0)
str_append(str, ", ");
var_expand(str, *e, var_expand_table);
break;
}
}
}
tab[0].value = t_strdup(str_c(str));
tab[1].value = msg;
str_truncate(str, 0);
var_expand(str, client->set->login_log_format, tab);
return str_c(str);
}
void client_log(struct client *client, const char *msg)
{
T_BEGIN {
i_info("%s", client_get_log_str(client, msg));
} T_END;
}
void client_log_err(struct client *client, const char *msg)
{
T_BEGIN {
i_error("%s", client_get_log_str(client, msg));
} T_END;
}
bool client_is_trusted(struct client *client)
{
const char *const *net;
struct ip_addr net_ip;
unsigned int bits;
if (client->set->login_trusted_networks == NULL)
return FALSE;
net = t_strsplit_spaces(client->set->login_trusted_networks, ", ");
for (; *net != NULL; net++) {
if (net_parse_range(*net, &net_ip, &bits) < 0) {
i_error("login_trusted_networks: "
"Invalid network '%s'", *net);
break;
}
if (net_is_in_network(&client->ip, &net_ip, bits))
return TRUE;
}
return FALSE;
}
const char *client_get_extra_disconnect_reason(struct client *client)
{
if (client->set->ssl_require_client_cert && client->ssl_proxy != NULL) {
if (ssl_proxy_has_broken_client_cert(client->ssl_proxy))
return "(client sent an invalid cert)";
if (!ssl_proxy_has_valid_client_cert(client->ssl_proxy))
return "(client didn't send a cert)";
}
if (client->auth_attempts == 0)
return "(no auth attempts)";
/* some auth attempts without SSL/TLS */
if (client->auth_tried_disabled_plaintext)
return "(tried to use disabled plaintext auth)";
if (client->set->ssl_require_client_cert)
return "(cert required, client didn't start TLS)";
if (client->auth_tried_unsupported_mech)
return "(tried to use unsupported auth mechanism)";
if (client->auth_request != NULL && client->auth_attempts == 1)
return "(disconnected while authenticating)";
if (client->auth_try_aborted && client->auth_attempts == 1)
return "(aborted authentication)";
return t_strdup_printf("(auth failed, %u attempts)",
client->auth_attempts);
}
void client_send_line(struct client *client, enum client_cmd_reply reply,
const char *text)
{
client->v.send_line(client, reply, text);
}
void client_send_raw_data(struct client *client, const void *data, size_t size)
{
ssize_t ret;
ret = o_stream_send(client->output, data, size);
if (ret < 0 || (size_t)ret != size) {
/* either disconnection or buffer full. in either case we want
this connection destroyed. however destroying it here might
break things if client is still tried to be accessed without
being referenced.. */
i_stream_close(client->input);
}
}
void client_send_raw(struct client *client, const char *data)
{
client_send_raw_data(client, data, strlen(data));
}
bool client_read(struct client *client)
{
switch (i_stream_read(client->input)) {
case -2:
/* buffer full */
client_send_line(client, CLIENT_CMD_REPLY_BYE,
"Input buffer full, aborting");
client_destroy(client, "Disconnected: Input buffer full");
return FALSE;
case -1:
/* disconnected */
client_destroy(client, "Disconnected");
return FALSE;
case 0:
/* nothing new read */
return TRUE;
default:
/* something was read */
timeout_reset(client->to_idle_disconnect);
return TRUE;
}
}
void client_input(struct client *client)
{
client->v.input(client);
}