client.c revision ccef83820a01bb37ad48653a05a9c5aa6560826a
183bea41fa640dc8117f3eb45ff935cd81377a84Timo Sirainen/* Copyright (c) 2002-2008 Dovecot authors, see the included COPYING file */
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen/* max. size of output buffer. if it gets full, the client is disconnected.
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen SASL authentication gives the largest output. */
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen/* maximum length for IMAP command line. */
542e28b384a6b26695f3e8de38fd5727d06f3333Timo Sirainen/* Disconnect client after idling this many milliseconds */
542e28b384a6b26695f3e8de38fd5727d06f3333Timo Sirainen#define CLIENT_LOGIN_IDLE_TIMEOUT_MSECS (3*60*1000)
542e28b384a6b26695f3e8de38fd5727d06f3333Timo Sirainen/* Disconnect client when it sends too many bad commands */
c3a636e4c9ae776e0eed06b6d7ad1ccfb6003afdTimo Sirainen/* When max. number of simultaneous connections is reached, few of the
c3a636e4c9ae776e0eed06b6d7ad1ccfb6003afdTimo Sirainen oldest connections are disconnected. Since we have to go through all of the
c3a636e4c9ae776e0eed06b6d7ad1ccfb6003afdTimo Sirainen clients, it's faster if we disconnect multiple clients. */
3b22894b8805b186c73d8b754001e8d7e944be85Timo Sirainen/* If we've been waiting auth server to respond for over this many milliseconds,
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen send a "waiting" message. */
fa2433aebcf3fccfa30ca9eed9b1a9166cf92ee2Timo Sirainen#if CLIENT_LOGIN_IDLE_TIMEOUT_MSECS >= AUTH_REQUEST_TIMEOUT*1000
fa2433aebcf3fccfa30ca9eed9b1a9166cf92ee2Timo Sirainen# error client idle timeout must be smaller than authentication timeout
5d03d9f439e41c90215a3c938ffebe4c2a8ae257Timo Sirainen "* OK Waiting for authentication process to respond.."
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainenconst char *capability_string = CAPABILITY_STRING;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainenstatic void client_set_title(struct imap_client *client)
d1fff80640050631b06bfab904a34b2ad24601e8Timo Sirainen if (!verbose_proctitle || !process_per_connection)
367e28a16854ee9f7247b2518f36f5e9163fcc10Timo Sirainen process_title_set(t_strdup_printf(client->common.tls ?
c07d7eb3ca9754367697c98f5e66a3982a45d142Timo Sirainenstatic void client_open_streams(struct imap_client *client, int fd)
c07d7eb3ca9754367697c98f5e66a3982a45d142Timo Sirainen i_stream_create_fd(fd, LOGIN_MAX_INBUF_SIZE, FALSE);
c07d7eb3ca9754367697c98f5e66a3982a45d142Timo Sirainen client->output = o_stream_create_fd(fd, MAX_OUTBUF_SIZE, FALSE);
c07d7eb3ca9754367697c98f5e66a3982a45d142Timo Sirainen client->parser = imap_parser_create(client->common.input,
c07d7eb3ca9754367697c98f5e66a3982a45d142Timo Sirainen/* Skip incoming data until newline is found,
c07d7eb3ca9754367697c98f5e66a3982a45d142Timo Sirainen returns TRUE if newline was found. */
c07d7eb3ca9754367697c98f5e66a3982a45d142Timo Sirainenbool client_skip_line(struct imap_client *client)
2eb2cf8eeb763bd5ca9b6848dce32f0303e88ec1Timo Sirainen const unsigned char *data;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen data = i_stream_get_data(client->common.input, &data_size);
c07d7eb3ca9754367697c98f5e66a3982a45d142Timo Sirainen for (i = 0; i < data_size; i++) {
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainenstatic const char *get_capability(struct imap_client *client, bool full)
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen auths = client_authenticate_get_capabilities(client->common.secured);
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen return t_strconcat(full ? capability_string : CAPABILITY_BANNER_STRING,
c07d7eb3ca9754367697c98f5e66a3982a45d142Timo Sirainen disable_plaintext_auth && !client->common.secured ?
c07d7eb3ca9754367697c98f5e66a3982a45d142Timo Sirainenstatic int cmd_capability(struct imap_client *client)
c07d7eb3ca9754367697c98f5e66a3982a45d142Timo Sirainen "* CAPABILITY ", get_capability(client, TRUE), NULL));
c07d7eb3ca9754367697c98f5e66a3982a45d142Timo Sirainen client_send_tagline(client, "OK Capability completed.");
c07d7eb3ca9754367697c98f5e66a3982a45d142Timo Sirainenstatic void client_start_tls(struct imap_client *client)
c07d7eb3ca9754367697c98f5e66a3982a45d142Timo Sirainen if (!client_unref(client) || client->destroyed)
c07d7eb3ca9754367697c98f5e66a3982a45d142Timo Sirainen fd_ssl = ssl_proxy_new(client->common.fd, &client->common.ip,
c07d7eb3ca9754367697c98f5e66a3982a45d142Timo Sirainen client_send_line(client, "* BYE TLS initialization failed.");
c07d7eb3ca9754367697c98f5e66a3982a45d142Timo Sirainen "Disconnected: TLS initialization failed.");
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen /* CRLF is lost from buffer when streams are reopened. */
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen client->io = io_add(client->common.fd, IO_READ, client_input, client);
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainenstatic int client_output_starttls(struct imap_client *client)
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen if ((ret = o_stream_flush(client->output)) < 0) {
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen o_stream_unset_flush_callback(client->output);
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainenstatic int cmd_starttls(struct imap_client *client)
002179a890bf4f1942cad6463787719eaa9fd6c0Timo Sirainen client_send_tagline(client, "BAD TLS is already active.");
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen client_send_tagline(client, "BAD TLS support isn't enabled.");
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen /* remove input handler, SSL proxy gives us a new fd. we also have to
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen remove it in case we have to wait for buffer to be flushed */
f3e17726502b6cf1912f30aae7e283b5d31ea69cTimo Sirainen client_send_tagline(client, "OK Begin TLS negotiation now.");
c07d7eb3ca9754367697c98f5e66a3982a45d142Timo Sirainen /* uncork the old fd */
002179a890bf4f1942cad6463787719eaa9fd6c0Timo Sirainen /* the buffer has to be flushed */
002179a890bf4f1942cad6463787719eaa9fd6c0Timo Sirainen o_stream_set_flush_pending(client->output, TRUE);
002179a890bf4f1942cad6463787719eaa9fd6c0Timo Sirainenclient_update_info(struct imap_client *client, const struct imap_arg *args)
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen else if (strcasecmp(key, "x-originating-port") == 0)
542e28b384a6b26695f3e8de38fd5727d06f3333Timo Sirainen else if (strcasecmp(key, "x-connected-ip") == 0)
542e28b384a6b26695f3e8de38fd5727d06f3333Timo Sirainen (void)net_addr2ip(value, &client->common.local_ip);
542e28b384a6b26695f3e8de38fd5727d06f3333Timo Sirainen else if (strcasecmp(key, "x-connected-port") == 0)
542e28b384a6b26695f3e8de38fd5727d06f3333Timo Sirainenstatic int cmd_id(struct imap_client *client, const struct imap_arg *args)
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen value = imap_id_args_get_log_reply(args, env);
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen client_send_line(client, t_strdup_printf("* ID %s",
fc464e5b2b2ab4d415a5d5b90ce4475d34620a75Timo Sirainen client_send_tagline(client, "OK ID completed.");
fc464e5b2b2ab4d415a5d5b90ce4475d34620a75Timo Sirainenstatic int cmd_noop(struct imap_client *client)
fc464e5b2b2ab4d415a5d5b90ce4475d34620a75Timo Sirainen client_send_tagline(client, "OK NOOP completed.");
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainenstatic int cmd_logout(struct imap_client *client)
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen client_send_line(client, "* BYE Logging out");
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen client_send_tagline(client, "OK Logout completed.");
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainenstatic int cmd_enable(struct imap_client *client)
3b22894b8805b186c73d8b754001e8d7e944be85Timo Sirainen "OK ENABLE ignored in non-authenticated state.");
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainenstatic int client_command_execute(struct imap_client *client, const char *cmd,
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainenstatic bool client_handle_input(struct imap_client *client)
9261dbf0675204898c6557591c7aa376e23a52b2Timo Sirainen const char *msg;
70afae43cc78ea6ecca83f6c587072c442a15ec1Timo Sirainen /* clear the previous command from memory. don't do this
70afae43cc78ea6ecca83f6c587072c442a15ec1Timo Sirainen immediately after handling command since we need the
70afae43cc78ea6ecca83f6c587072c442a15ec1Timo Sirainen cmd_tag to stay some time after authentication commands. */
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen /* remove \r\n */
70afae43cc78ea6ecca83f6c587072c442a15ec1Timo Sirainen client->cmd_tag = imap_parser_read_word(client->parser);
70afae43cc78ea6ecca83f6c587072c442a15ec1Timo Sirainen client->cmd_name = imap_parser_read_word(client->parser);
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen switch (imap_parser_read_args(client->parser, 0, 0, &args)) {
c06d6ea0766d0520af1a93e6000c0e73f350e0a2Timo Sirainen msg = imap_parser_get_error(client->parser, &fatal);
216cd45a5f47c9bd46fe67c1b3bd6b1a42f6e39cTimo Sirainen client_send_line(client, t_strconcat("* BYE ",
0139fcb57a88f6ed27a1bb4a1bd537b04fd2b5d6Timo Sirainen client_send_tagline(client, t_strconcat("BAD ", msg, NULL));
70afae43cc78ea6ecca83f6c587072c442a15ec1Timo Sirainen /* not enough data */
c06d6ea0766d0520af1a93e6000c0e73f350e0a2Timo Sirainen /* we read the entire line - skip over the CRLF */
216cd45a5f47c9bd46fe67c1b3bd6b1a42f6e39cTimo Sirainen ret = client_command_execute(client, client->cmd_name, args);
0139fcb57a88f6ed27a1bb4a1bd537b04fd2b5d6Timo Sirainen if (++client->bad_counter >= CLIENT_MAX_BAD_COMMANDS) {
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen "* BYE Too many invalid IMAP commands.");
70afae43cc78ea6ecca83f6c587072c442a15ec1Timo Sirainen "Disconnected: Too many invalid commands");
c06d6ea0766d0520af1a93e6000c0e73f350e0a2Timo Sirainen "BAD Error in IMAP command received by server.");
1503ac7619d97193d6690292b5f9523552c5d6ceTimo Sirainen return ret != 0;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen switch (i_stream_read(client->common.input)) {
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen /* buffer full */
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen client_send_line(client, "* BYE Input buffer full, aborting");
f38485358ffc04c3466b917770575e29deef24c3Timo Sirainen client_destroy(client, "Disconnected: Input buffer full");
3c652e7a569c2623d22b4ab30279aebddce4d396Timo Sirainen /* disconnected */
3c652e7a569c2623d22b4ab30279aebddce4d396Timo Sirainen /* something was read */
c06d6ea0766d0520af1a93e6000c0e73f350e0a2Timo Sirainen /* we're not yet connected to auth process -
c06d6ea0766d0520af1a93e6000c0e73f350e0a2Timo Sirainen don't allow any commands */
62fc2fe221eccc834ac6b11b94b55335d5027cd1Timo Sirainen struct imap_client *destroy_buf[CLIENT_DESTROY_OLDEST_COUNT];
62fc2fe221eccc834ac6b11b94b55335d5027cd1Timo Sirainen unsigned int i, destroy_count;
62fc2fe221eccc834ac6b11b94b55335d5027cd1Timo Sirainen /* find the oldest clients and put them to destroy-buffer */
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen destroy_count = max_connections > CLIENT_DESTROY_OLDEST_COUNT*2 ?
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen CLIENT_DESTROY_OLDEST_COUNT : I_MIN(max_connections/2, 1);
c06d6ea0766d0520af1a93e6000c0e73f350e0a2Timo Sirainen for (client = clients; client != NULL; client = client->next) {
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen struct imap_client *imap_client = (struct imap_client *)client;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen for (i = 0; i < destroy_count; i++) {
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainen destroy_buf[i]->created > imap_client->created) {
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainen /* @UNSAFE */
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainen /* then kill them */
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainen for (i = 0; i < destroy_count; i++) {
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen "Disconnected: Connection queue full");
b9b841558c5f91db7f5fc71c0ac62aad1bbf6418Timo Sirainenstatic void client_send_greeting(struct imap_client *client)
47e9fdee55c2074425cf0316f4f64fbbb790301cTimo Sirainen str_printfa(greet, "[CAPABILITY %s] ", get_capability(client, FALSE));
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainenstatic void client_idle_disconnect_timeout(struct imap_client *client)
e5acc283bf030b0b5c79ca4e52d315c516a299faPascal Volk client_send_line(client, "* BYE Disconnected for inactivity.");
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen client_destroy(client, "Disconnected: Inactivity");
1b04762685272a53643ac2179939537a44c7c044Timo Sirainenstatic void client_auth_waiting_timeout(struct imap_client *client)
303e375b7e76278f4ec541f49af0476d3e4ee710Timo Sirainenvoid client_set_auth_waiting(struct imap_client *client)
fa2a11210f20fb8998ed656f75e163191c8047e6Timo Sirainenstruct client *client_create(int fd, bool ssl, const struct ip_addr *local_ip,
d1fff80640050631b06bfab904a34b2ad24601e8Timo Sirainen /* always use nonblocking I/O */
d1fff80640050631b06bfab904a34b2ad24601e8Timo Sirainen client->common.trusted = client_is_trusted(&client->common);
d1fff80640050631b06bfab904a34b2ad24601e8Timo Sirainen client->common.secured = ssl || client->common.trusted ||
d1fff80640050631b06bfab904a34b2ad24601e8Timo Sirainen client->io = io_add(fd, IO_READ, client_input, client);
fa2a11210f20fb8998ed656f75e163191c8047e6Timo Sirainenvoid client_destroy(struct imap_client *client, const char *reason)
12797080b552a3c1727b73b61cc7427bec0c7472Timo Sirainen if (!client->login_success && reason != NULL) {
d1fff80640050631b06bfab904a34b2ad24601e8Timo Sirainen client_get_extra_disconnect_reason(&client->common),
c3a636e4c9ae776e0eed06b6d7ad1ccfb6003afdTimo Sirainen sasl_server_auth_client_error(&client->common, NULL);
c3a636e4c9ae776e0eed06b6d7ad1ccfb6003afdTimo Sirainenvoid client_destroy_success(struct imap_client *client, const char *reason)
5d03d9f439e41c90215a3c938ffebe4c2a8ae257Timo Sirainenvoid client_destroy_internal_failure(struct imap_client *client)
5d03d9f439e41c90215a3c938ffebe4c2a8ae257Timo Sirainen client_send_line(client, "* BYE Internal login failure. "
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen "Refer to server log for more information.");
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen client_destroy(client, "Internal login failure");
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainenvoid client_send_line(struct imap_client *client, const char *line)
b9b841558c5f91db7f5fc71c0ac62aad1bbf6418Timo Sirainen if (ret < 0 || (size_t)ret != iov[0].iov_len + iov[1].iov_len) {
b9b841558c5f91db7f5fc71c0ac62aad1bbf6418Timo Sirainen /* either disconnection or buffer full. in either case we
b9b841558c5f91db7f5fc71c0ac62aad1bbf6418Timo Sirainen want this connection destroyed. however destroying it here
b9b841558c5f91db7f5fc71c0ac62aad1bbf6418Timo Sirainen might break things if client is still tried to be accessed
b9b841558c5f91db7f5fc71c0ac62aad1bbf6418Timo Sirainen without being referenced.. */
b9b841558c5f91db7f5fc71c0ac62aad1bbf6418Timo Sirainenvoid client_send_tagline(struct imap_client *client, const char *line)
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainen client_send_line(client, t_strconcat(client->cmd_tag, " ", line, NULL));
9b78c0d5e13141f4df6c6e483f854e5acb861288Timo Sirainen for (client = clients; client != NULL; client = client->next) {
5d03d9f439e41c90215a3c938ffebe4c2a8ae257Timo Sirainen struct imap_client *imap_client = (struct imap_client *)client;
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainen timeout_remove(&imap_client->to_auth_waiting);
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainen for (client = clients; client != NULL; client = client->next) {
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen struct imap_client *imap_client = (struct imap_client *)client;
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen client_destroy(imap_client, "Disconnected: Shutting down");
674f541b16689c0ed090dac32db94463c5af3977Timo Sirainen /* Nothing to initialize for IMAP */