client-common.c revision cca4ba2a504d70a9fe9fee37f8433997359de52c
183bea41fa640dc8117f3eb45ff935cd81377a84Timo Sirainen* Copyright (c) 2002-2013 Dovecot authors, see the included COPYING file */
029cfcdce65b284d5230adf1c920a5f526b03b5cTimo Sirainenstruct client *clients = NULL, *last_client = NULL;
029cfcdce65b284d5230adf1c920a5f526b03b5cTimo Sirainenstatic unsigned int clients_count = 0;
90c23747727c85f80e4e8eed7968f0edbeac7ac5Timo Sirainenstatic void client_idle_disconnect_timeout(struct client *client)
90c23747727c85f80e4e8eed7968f0edbeac7ac5Timo Sirainen unsigned int secs;
90c23747727c85f80e4e8eed7968f0edbeac7ac5Timo Sirainen user_reason = "Timeout while finishing login.";
90c23747727c85f80e4e8eed7968f0edbeac7ac5Timo Sirainen "Timeout while finishing login (waited %u secs)", secs);
90c23747727c85f80e4e8eed7968f0edbeac7ac5Timo Sirainen "Disconnected for inactivity during authentication.";
90c23747727c85f80e4e8eed7968f0edbeac7ac5Timo Sirainen "Disconnected: Inactivity during authentication";
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen user_reason = "Timeout while finishing login.";
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen "proxy: Logging in to %s:%u timed out "
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen "(state=%u, duration=%us)",
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen client_notify_disconnect(client, CLIENT_DISCONNECT_TIMEOUT, user_reason);
b397665e90fa0fc7c6a9156fdd6cf28b571e8e39Timo Sirainenstatic void client_open_streams(struct client *client)
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen i_stream_create_fd(client->fd, LOGIN_MAX_INBUF_SIZE, FALSE);
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen o_stream_create_fd(client->fd, LOGIN_MAX_OUTBUF_SIZE, FALSE);
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen o_stream_set_no_error_handling(client->output, TRUE);
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen if (iostream_rawlog_create(login_rawlog_dir, &client->input,
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainenstatic bool client_is_trusted(struct client *client)
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen const char *const *net;
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen unsigned int bits;
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen if (client->set->login_trusted_networks == NULL)
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen net = t_strsplit_spaces(client->set->login_trusted_networks, ", ");
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen if (net_parse_range(*net, &net_ip, &bits) < 0) {
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen if (net_is_in_network(&client->ip, &net_ip, bits))
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen const struct master_service_ssl_settings *ssl_set,
029cfcdce65b284d5230adf1c920a5f526b03b5cTimo Sirainen const struct ip_addr *local_ip, const struct ip_addr *remote_ip)
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen client = login_binary->client_vfuncs->alloc(pool);
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen client->v.auth_send_challenge = client_auth_send_challenge;
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen client->v.auth_parse_response = client_auth_parse_response;
85144b5f0bc763de14c7d87291a90ef74ac241a2Timo Sirainenvoid client_destroy(struct client *client, const char *reason)
85144b5f0bc763de14c7d87291a90ef74ac241a2Timo Sirainen if (!client->login_success && reason != NULL) {
f153a2cec0319f549388d28f8cfd4d50229d1132Timo Sirainen client_get_extra_disconnect_reason(client), NULL);
85144b5f0bc763de14c7d87291a90ef74ac241a2Timo Sirainen master_auth_request_abort(master_auth, client->master_tag);
aef92409cf369afdd2ecd81a4f80083cd4082f46Timo Sirainen if (client_unref(&client) && initial_service_count == 1) {
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen /* as soon as this connection is done with proxying
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen (or whatever), the process will die. there's no need for
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen authentication anymore, so close the connection.
2d49f150b4bce6f2f59a84e268e4777901c3e42cTimo Sirainen do this only with initial service_count=1, in case there
14c474d9f4591c397ed0b5206af6537c7b52c924Timo Sirainen are other clients with pending authentications */
14c474d9f4591c397ed0b5206af6537c7b52c924Timo Sirainen auth_client_disconnect(auth_client, "unnecessary connection");
2d49f150b4bce6f2f59a84e268e4777901c3e42cTimo Sirainenvoid client_destroy_success(struct client *client, const char *reason)
b6612c334604eeb27e1ca2bd804ac66dcbc2eaadTimo Sirainenvoid client_destroy_internal_failure(struct client *client)
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen client_notify_disconnect(client, CLIENT_DISCONNECT_INTERNAL_ERROR,
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen "Internal login failure. "
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen "Refer to server log for more information.");
e64d7b6f388fecd0c83a4f2acb54e30d5ac98c6cTimo Sirainen "Internal login failure (pid=%s id=%u)",
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen master_service_client_connection_destroyed(master_service);
bdd7a96c363346f7c38f389791be1487ca08775bTimo Sirainen /* we have no clients */
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen /* destroy the last client that hasn't successfully authenticated yet.
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen this is usually the last client, but don't kill it if it's just
d9fda7e3a0fa5551547ac3e3054b837fc77f4bfbTimo Sirainen waiting for master to finish its job. */
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen for (client = last_client; client != NULL; client = client->prev) {
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen client_notify_disconnect(client, CLIENT_DISCONNECT_RESOURCE_CONSTRAINT,
bdd7a96c363346f7c38f389791be1487ca08775bTimo Sirainen "Connection queue full");
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen client_destroy(client, "Disconnected: Connection queue full");
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainenvoid clients_destroy_all_reason(const char *reason)
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen for (client = clients; client != NULL; client = next) {
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen clients_destroy_all_reason("Disconnected: Shutting down");
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainenstatic void client_start_tls(struct client *client)
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen if (!client_unref(&client) || client->destroyed)
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen fd_ssl = ssl_proxy_alloc(client->fd, &client->ip, client->pool,
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen "TLS initialization failed.");
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen "Disconnected: TLS initialization failed.");
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen ssl_proxy_set_client(client->ssl_proxy, client);
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen client->io = io_add(client->fd, IO_READ, client_input, client);
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainenstatic int client_output_starttls(struct client *client)
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen if ((ret = o_stream_flush(client->output)) < 0) {
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen o_stream_unset_flush_callback(client->output);
d9fda7e3a0fa5551547ac3e3054b837fc77f4bfbTimo Sirainenvoid client_cmd_starttls(struct client *client)
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen client->v.notify_starttls(client, FALSE, "TLS is already active.");
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen client->v.notify_starttls(client, FALSE, "TLS support isn't enabled.");
d9fda7e3a0fa5551547ac3e3054b837fc77f4bfbTimo Sirainen /* remove input handler, SSL proxy gives us a new fd. we also have to
d9fda7e3a0fa5551547ac3e3054b837fc77f4bfbTimo Sirainen remove it in case we have to wait for buffer to be flushed */
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen client->v.notify_starttls(client, TRUE, "Begin TLS negotiation now.");
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen /* uncork the old fd */
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen /* the buffer has to be flushed */
aef92409cf369afdd2ecd81a4f80083cd4082f46Timo Sirainen o_stream_set_flush_pending(client->output, TRUE);
aef92409cf369afdd2ecd81a4f80083cd4082f46Timo Sirainenunsigned int clients_get_count(void)
63e2edd14ae7b1dc4a08e2e659501dbf519462f9Timo Sirainenconst char *client_get_session_id(struct client *client)
9061a2a9a7f8da780a5b50af3603f828167c6b13Timo Sirainen unsigned int i;
aef92409cf369afdd2ecd81a4f80083cd4082f46Timo Sirainen buf = buffer_create_dynamic(pool_datastack_create(), 24);
9061a2a9a7f8da780a5b50af3603f828167c6b13Timo Sirainen base64_buf = buffer_create_dynamic(pool_datastack_create(), 24*2);
9061a2a9a7f8da780a5b50af3603f828167c6b13Timo Sirainen timestamp = tv.tv_usec + (long long)tv.tv_sec * 1000ULL*1000ULL;
aef92409cf369afdd2ecd81a4f80083cd4082f46Timo Sirainen /* add lowest 48 bits of the timestamp. this gives us a bit less than
aef92409cf369afdd2ecd81a4f80083cd4082f46Timo Sirainen 9 years until it wraps */
9061a2a9a7f8da780a5b50af3603f828167c6b13Timo Sirainen buffer_append_c(buf, (timestamp >> i) & 0xff);
9061a2a9a7f8da780a5b50af3603f828167c6b13Timo Sirainen buffer_append_c(buf, client->remote_port & 0xff);
9061a2a9a7f8da780a5b50af3603f828167c6b13Timo Sirainen buffer_append_c(buf, (client->remote_port >> 16) & 0xff);
9061a2a9a7f8da780a5b50af3603f828167c6b13Timo Sirainen buffer_append(buf, &client->ip.u.ip6, sizeof(client->ip.u.ip6));
aef92409cf369afdd2ecd81a4f80083cd4082f46Timo Sirainen buffer_append(buf, &client->ip.u.ip4, sizeof(client->ip.u.ip4));
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen base64_encode(buf->data, buf->used, base64_buf);
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen client->session_id = p_strdup(client->pool, str_c(base64_buf));
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainenstatic struct var_expand_table login_var_expand_empty_tab[] = {
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainenstatic const struct var_expand_table *
aef92409cf369afdd2ecd81a4f80083cd4082f46Timo Sirainen unsigned int i;
aef92409cf369afdd2ecd81a4f80083cd4082f46Timo Sirainen tab = t_malloc(sizeof(login_var_expand_empty_tab));
aef92409cf369afdd2ecd81a4f80083cd4082f46Timo Sirainen tab[1].value = t_strcut(client->virtual_user, '@');
aef92409cf369afdd2ecd81a4f80083cd4082f46Timo Sirainen tab[2].value = strchr(client->virtual_user, '@');
aef92409cf369afdd2ecd81a4f80083cd4082f46Timo Sirainen for (i = 0; i < 3; i++)
aef92409cf369afdd2ecd81a4f80083cd4082f46Timo Sirainen tab[i].value = str_sanitize(tab[i].value, 80);
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen tab[5].value = net_ip2addr(&client->local_ip);
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen tab[8].value = client->auth_mech_name == NULL ? NULL :
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen str_sanitize(client->auth_mech_name, MAX_MECH_NAME);
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen tab[11].value = client->secured ? "secured" : NULL;
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen tab[11].value = ssl_error == NULL ? ssl_state :
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen t_strdup_printf("%s: %s", ssl_state, ssl_error);
94aa90d2d17a7aebcda5a4193a62e80ddbb169b7Timo Sirainen ssl_proxy_get_security_string(client->ssl_proxy);
2d49f150b4bce6f2f59a84e268e4777901c3e42cTimo Sirainen tab[14].value = client_get_session_id(client);
2d49f150b4bce6f2f59a84e268e4777901c3e42cTimo Sirainen tab[15].value = net_ip2addr(&client->real_ip);
90c23747727c85f80e4e8eed7968f0edbeac7ac5Timo Sirainenstatic const char *
90c23747727c85f80e4e8eed7968f0edbeac7ac5Timo Sirainenclient_get_log_str(struct client *client, const char *msg)
90c23747727c85f80e4e8eed7968f0edbeac7ac5Timo Sirainen static struct var_expand_table static_tab[3] = {
90c23747727c85f80e4e8eed7968f0edbeac7ac5Timo Sirainen const struct var_expand_table *var_expand_table;
90c23747727c85f80e4e8eed7968f0edbeac7ac5Timo Sirainen char *const *e;
90c23747727c85f80e4e8eed7968f0edbeac7ac5Timo Sirainen unsigned int pos;
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen var_expand_table = get_var_expand_table(client);
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen for (e = client->set->log_format_elements_split; *e != NULL; e++) {
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen /* username is added even if it's empty */
85da8c055280cd45553b6b335e9fb226d6e2801eTimo Sirainen var_expand(str2, *e, login_var_expand_empty_tab);
029cfcdce65b284d5230adf1c920a5f526b03b5cTimo Sirainen if (strcmp(str_c(str)+pos, str_c(str2)) == 0) {
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen /* empty %variables, don't add */
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen var_expand(str, client->set->login_log_format, tab);
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainenvoid client_log(struct client *client, const char *msg)
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen i_info("%s", client_get_log_str(client, msg));
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainenvoid client_log_err(struct client *client, const char *msg)
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen i_error("%s", client_get_log_str(client, msg));
04b8a90af181cc4c7959266855e8ed50a22ed413Timo Sirainenvoid client_log_warn(struct client *client, const char *msg)
04b8a90af181cc4c7959266855e8ed50a22ed413Timo Sirainen i_warning("%s", client_get_log_str(client, msg));
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainenbool client_is_tls_enabled(struct client *client)
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen return ssl_initialized && strcmp(client->ssl_set->ssl, "no") != 0;
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainenconst char *client_get_extra_disconnect_reason(struct client *client)
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen unsigned int auth_secs = client->auth_first_started == 0 ? 0 :
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen if (client->set->auth_ssl_require_client_cert &&
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen if (ssl_proxy_has_broken_client_cert(client->ssl_proxy))
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen return "(client sent an invalid cert)";
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen if (!ssl_proxy_has_valid_client_cert(client->ssl_proxy))
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen return "(client didn't send a cert)";
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen "(disconnected before auth was ready, waited %u secs)",
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen (unsigned int)(ioloop_time - client->created));
029cfcdce65b284d5230adf1c920a5f526b03b5cTimo Sirainen return t_strdup_printf("(no auth attempts in %u secs)",
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen (unsigned int)(ioloop_time - client->created));
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen /* some auth attempts without SSL/TLS */
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen return "(tried to use disallowed plaintext auth)";
347acd14d8da653ce3757b3e29981326502bed6bTimo Sirainen if (client->set->auth_ssl_require_client_cert &&
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen return "(cert required, client didn't start TLS)";
8cd0a1a2200e65cd134d03fe3f93ec02f1746359Timo Sirainen return "(tried to use unsupported auth mechanism)";
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen if (client->auth_waiting && client->auth_attempts == 1) {
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen return t_strdup_printf("(client didn't finish SASL auth, "
029cfcdce65b284d5230adf1c920a5f526b03b5cTimo Sirainen if (client->auth_request != NULL && client->auth_attempts == 1) {
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen return t_strdup_printf("(disconnected while authenticating, "
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen if (client->authenticating && client->auth_attempts == 1) {
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen return t_strdup_printf("(disconnected while finishing login, "
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen if (client->auth_try_aborted && client->auth_attempts == 1)
bf8f4f90cb5e5f32c2611ba3425557964b9c47fcTimo Sirainen return "(aborted authentication)";
bf8f4f90cb5e5f32c2611ba3425557964b9c47fcTimo Sirainen return "(auth process communication failure)";
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen return "(proxy dest auth failed)";
029cfcdce65b284d5230adf1c920a5f526b03b5cTimo Sirainen return t_strdup_printf("(internal failure, %u successful auths)",
51b979b6414b940f04677a7e2d064be119345954Timo Sirainen return "(user disabled)";
029cfcdce65b284d5230adf1c920a5f526b03b5cTimo Sirainen return "(password expired)";
029cfcdce65b284d5230adf1c920a5f526b03b5cTimo Sirainen return t_strdup_printf("(auth failed, %u attempts in %u secs)",
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainenvoid client_notify_disconnect(struct client *client,
629600d9a85e8025c15a5eaeb80329e116e022c9Timo Sirainen client->v.notify_disconnect(client, reason, text);
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainenvoid client_notify_auth_ready(struct client *client)
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainenvoid client_notify_status(struct client *client, bool bad, const char *text)
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainenvoid client_send_raw_data(struct client *client, const void *data, size_t size)
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen ret = o_stream_send(client->output, data, size);
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen /* either disconnection or buffer full. in either case we want
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen this connection destroyed. however destroying it here might
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen break things if client is still tried to be accessed without
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen being referenced.. */
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainenvoid client_send_raw(struct client *client, const char *data)
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen client_send_raw_data(client, data, strlen(data));
bdd7a96c363346f7c38f389791be1487ca08775bTimo Sirainen /* buffer full */
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen "Input buffer full, aborting");
4bc96ba6f1d67a90a75fa131bcd2cd508ea5a05aTimo Sirainen client_destroy(client, "Disconnected: Input buffer full");
6bc0f424bcdb9119d8159874cf98adfa53eefd9aTimo Sirainen /* disconnected */
6bc0f424bcdb9119d8159874cf98adfa53eefd9aTimo Sirainen /* nothing new read */
bdd7a96c363346f7c38f389791be1487ca08775bTimo Sirainen /* something was read */