client.c revision 74ee5590487e89b25dffb58560ab1fea79fc21d9
2e37d45867d081db150ab78dad303b9077aea24fTimo Sirainen/* Copyright (c) 2002-2009 Dovecot authors, see the included COPYING file */
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen/* max. size of output buffer. if it gets full, the client is disconnected.
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen SASL authentication gives the largest output. */
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen/* maximum length for IMAP command line. */
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen/* Disconnect client when it sends too many bad commands */
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen/* When max. number of simultaneous connections is reached, few of the
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen oldest connections are disconnected. Since we have to go through all of the
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen clients, it's faster if we disconnect multiple clients. */
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen/* If we've been waiting auth server to respond for over this many milliseconds,
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen send a "waiting" message. */
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen#if CLIENT_LOGIN_IDLE_TIMEOUT_MSECS < AUTH_REQUEST_TIMEOUT*1000
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen# error client idle timeout must be larger than authentication timeout
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen "* OK Waiting for authentication process to respond.."
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen "* OK Waiting for authentication master process to respond.."
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainenstatic void client_set_title(struct imap_client *client)
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen !client->common.set->login_process_per_connection)
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen process_title_set(t_strdup_printf(client->common.tls ?
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainenstatic void client_open_streams(struct imap_client *client, int fd)
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen i_stream_create_fd(fd, LOGIN_MAX_INBUF_SIZE, FALSE);
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen client->output = o_stream_create_fd(fd, MAX_OUTBUF_SIZE, FALSE);
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen client->parser = imap_parser_create(client->common.input,
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen/* Skip incoming data until newline is found,
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen returns TRUE if newline was found. */
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainenbool client_skip_line(struct imap_client *client)
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen const unsigned char *data;
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen data = i_stream_get_data(client->common.input, &data_size);
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen for (i = 0; i < data_size; i++) {
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainenstatic const char *get_capability(struct imap_client *client)
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen auths = client_authenticate_get_capabilities(client);
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainenstatic int cmd_capability(struct imap_client *client)
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen /* Client is required to send CAPABILITY after STARTTLS, so the
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen capability resp-code workaround checks only pre-STARTTLS
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen CAPABILITY commands. */
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen client->client_ignores_capability_resp_code = TRUE;
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen "* CAPABILITY ", get_capability(client), NULL));
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen client_send_tagline(client, "OK Capability completed.");
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainenstatic void client_start_tls(struct imap_client *client)
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen if (!client_unref(client) || client->destroyed)
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen fd_ssl = ssl_proxy_new(client->common.fd, &client->common.ip,
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen client_send_line(client, "* BYE TLS initialization failed.");
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen "Disconnected: TLS initialization failed.");
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen /* CRLF is lost from buffer when streams are reopened. */
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen client->io = io_add(client->common.fd, IO_READ, client_input, client);
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainenstatic int client_output_starttls(struct imap_client *client)
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen if ((ret = o_stream_flush(client->output)) < 0) {
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen o_stream_unset_flush_callback(client->output);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenstatic int cmd_starttls(struct imap_client *client)
4dc8837ab37c1a606add1067e21ed868754db4e3Timo Sirainen client_send_tagline(client, "BAD TLS is already active.");
5fb3bff645380804c9db2510940c41db6b8fdb01Timo Sirainen client_send_tagline(client, "BAD TLS support isn't enabled.");
2abfef71398a61e5ed97c23a1ceb71461933ccb8Timo Sirainen /* remove input handler, SSL proxy gives us a new fd. we also have to
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen remove it in case we have to wait for buffer to be flushed */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen client_send_tagline(client, "OK Begin TLS negotiation now.");
b66a7b7ab0db2c9ad425912d3f21a36fcf76d876Timo Sirainen /* uncork the old fd */
b66a7b7ab0db2c9ad425912d3f21a36fcf76d876Timo Sirainen /* the buffer has to be flushed */
b66a7b7ab0db2c9ad425912d3f21a36fcf76d876Timo Sirainen o_stream_set_flush_pending(client->output, TRUE);
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainenclient_update_info(struct imap_client *client, const struct imap_arg *args)
44dc970b18c4e2d06f34cb908924152156e4a45bTimo Sirainen else if (strcasecmp(key, "x-originating-port") == 0)
44dc970b18c4e2d06f34cb908924152156e4a45bTimo Sirainen else if (strcasecmp(key, "x-connected-ip") == 0)
6bf1543bb7af03324c04e8f9ac8e430f395989aeTimo Sirainen (void)net_addr2ip(value, &client->common.local_ip);
6bf1543bb7af03324c04e8f9ac8e430f395989aeTimo Sirainen else if (strcasecmp(key, "x-connected-port") == 0)
b00787191c3c31bebb939c3d00f1fcdb67356c69Timo Sirainenstatic int cmd_id(struct imap_client *client, const struct imap_arg *args)
44dc970b18c4e2d06f34cb908924152156e4a45bTimo Sirainen value = imap_id_args_get_log_reply(args, env);
e30b748edcef3cf3352478bf21fa8f785bdc773aTimo Sirainen client_send_line(client, t_strdup_printf("* ID %s",
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen client_send_tagline(client, "OK ID completed.");
ca98892a6b8a30ffc1fe26fcf02c7d59e3204e7eTimo Sirainenstatic int cmd_noop(struct imap_client *client)
44dc970b18c4e2d06f34cb908924152156e4a45bTimo Sirainen client_send_tagline(client, "OK NOOP completed.");
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenstatic int cmd_logout(struct imap_client *client)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen client_send_line(client, "* BYE Logging out");
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen client_send_tagline(client, "OK Logout completed.");
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenstatic int cmd_enable(struct imap_client *client)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen "OK ENABLE ignored in non-authenticated state.");
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenstatic int client_command_execute(struct imap_client *client, const char *cmd,
992a9e2d6c6ee45d87089ac54267e0198a7802c3Timo Sirainenstatic bool client_handle_input(struct imap_client *client)
992a9e2d6c6ee45d87089ac54267e0198a7802c3Timo Sirainen const char *msg;
6925fd9cd70c30884406d50f1d85efb6561e776cTimo Sirainen /* clear the previous command from memory. don't do this
6925fd9cd70c30884406d50f1d85efb6561e776cTimo Sirainen immediately after handling command since we need the
992a9e2d6c6ee45d87089ac54267e0198a7802c3Timo Sirainen cmd_tag to stay some time after authentication commands. */
538303a216166f3526c0ae9658c9978275cfa100Timo Sirainen /* remove \r\n */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen client->cmd_tag = imap_parser_read_word(client->parser);
8e5fedd9ada47735be8ac0f8af2a66e8528bd776Timo Sirainen client->cmd_name = imap_parser_read_word(client->parser);
32ee977e189266744ef69ac4e832fd3111d6f949Timo Sirainen switch (imap_parser_read_args(client->parser, 0, 0, &args)) {
32ee977e189266744ef69ac4e832fd3111d6f949Timo Sirainen msg = imap_parser_get_error(client->parser, &fatal);
1d2b188f0eedc3cab6e27ceac5425a037f38042eTimo Sirainen client_send_line(client, t_strconcat("* BYE ",
8e5fedd9ada47735be8ac0f8af2a66e8528bd776Timo Sirainen client_send_tagline(client, t_strconcat("BAD ", msg, NULL));
ccc895c0358108d2304239063e940b7d75f364abTimo Sirainen /* not enough data */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* we read the entire line - skip over the CRLF */
8bb360f9e5de1c25e4f875205bb06e8bf15dae14Timo Sirainen ret = client_command_execute(client, client->cmd_name, args);
32ee977e189266744ef69ac4e832fd3111d6f949Timo Sirainen if (++client->bad_counter >= CLIENT_MAX_BAD_COMMANDS) {
32ee977e189266744ef69ac4e832fd3111d6f949Timo Sirainen "* BYE Too many invalid IMAP commands.");
4dc8837ab37c1a606add1067e21ed868754db4e3Timo Sirainen "Disconnected: Too many invalid commands");
4dc8837ab37c1a606add1067e21ed868754db4e3Timo Sirainen "BAD Error in IMAP command received by server.");
8e5fedd9ada47735be8ac0f8af2a66e8528bd776Timo Sirainen return ret != 0;
d61a5e0e4ff58d1aa613f0d51161e5bb0f092514Timo Sirainen switch (i_stream_read(client->common.input)) {
d61a5e0e4ff58d1aa613f0d51161e5bb0f092514Timo Sirainen /* buffer full */
d61a5e0e4ff58d1aa613f0d51161e5bb0f092514Timo Sirainen client_send_line(client, "* BYE Input buffer full, aborting");
d61a5e0e4ff58d1aa613f0d51161e5bb0f092514Timo Sirainen client_destroy(client, "Disconnected: Input buffer full");
d61a5e0e4ff58d1aa613f0d51161e5bb0f092514Timo Sirainen /* disconnected */
d61a5e0e4ff58d1aa613f0d51161e5bb0f092514Timo Sirainen /* nothing new read */
d61a5e0e4ff58d1aa613f0d51161e5bb0f092514Timo Sirainen /* something was read */
d77c309fccbc6a7594f8cb08fb01009fa613c568Timo Sirainen /* we're not yet connected to auth process -
d77c309fccbc6a7594f8cb08fb01009fa613c568Timo Sirainen don't allow any commands */
d22301419109ed4a38351715e6760011421dadecTimo Sirainen client_send_line(client, AUTH_SERVER_WAITING_MSG);
8754bb7a1f24705ffa5434f9e10d57e0b3b88d6eTimo Sirainen struct imap_client *destroy_buf[CLIENT_DESTROY_OLDEST_COUNT];
641f0c0900ee6e7cf9667f4b40ed95cec7d0cdcaTimo Sirainen unsigned int i, destroy_count;
d61a5e0e4ff58d1aa613f0d51161e5bb0f092514Timo Sirainen /* find the oldest clients and put them to destroy-buffer */
641f0c0900ee6e7cf9667f4b40ed95cec7d0cdcaTimo Sirainen destroy_count = max_connections > CLIENT_DESTROY_OLDEST_COUNT*2 ?
641f0c0900ee6e7cf9667f4b40ed95cec7d0cdcaTimo Sirainen CLIENT_DESTROY_OLDEST_COUNT : I_MIN(max_connections/2, 1);
641f0c0900ee6e7cf9667f4b40ed95cec7d0cdcaTimo Sirainen for (client = clients; client != NULL; client = client->next) {
d61a5e0e4ff58d1aa613f0d51161e5bb0f092514Timo Sirainen struct imap_client *imap_client = (struct imap_client *)client;
1d3f7c1278168d5b1cbfa9a2cc9929a0909056b4Timo Sirainen for (i = 0; i < destroy_count; i++) {
9a099a65160987349f441c82ab0e38f32b747adbTimo Sirainen destroy_buf[i]->created > imap_client->created) {
9a099a65160987349f441c82ab0e38f32b747adbTimo Sirainen /* @UNSAFE */
b60baf6af900a610b2b2ddd24a46f8311acc3386Timo Sirainen /* then kill them */
b60baf6af900a610b2b2ddd24a46f8311acc3386Timo Sirainen for (i = 0; i < destroy_count; i++) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen "Disconnected: Connection queue full");
d3eff05aaa4c2bc0a7580ee87a54f6693f4a8241Timo Sirainenstatic void client_send_greeting(struct imap_client *client)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen str_printfa(greet, "[CAPABILITY %s] ", get_capability(client));
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen str_append(greet, client->common.set->login_greeting);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenstatic void client_idle_disconnect_timeout(struct imap_client *client)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen client_send_line(client, "* BYE Disconnected for inactivity.");
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen client_destroy(client, "Disconnected: Inactivity");
38df0cacce475112991e60d796f8f2105c616f01Timo Sirainenstatic void client_auth_waiting_timeout(struct imap_client *client)
38df0cacce475112991e60d796f8f2105c616f01Timo Sirainen client_send_line(client, client->common.master_tag == 0 ?
eae1d6e75713d3d658908ac39b719992e2f8a456Timo Sirainen AUTH_SERVER_WAITING_MSG : AUTH_MASTER_WAITING_MSG);
41264e5dcef8335ab7ba422822b3ab518b7a327aTimo Sirainenvoid client_set_auth_waiting(struct imap_client *client)
41264e5dcef8335ab7ba422822b3ab518b7a327aTimo Sirainenstruct client *client_create(int fd, bool ssl, pool_t pool,
43955c82f9f52c969c777b3da00bc170183dfdf2Timo Sirainen if (clients_get_count() >= set->login_max_connections) {
785d9cca224d33ca3938e9166784f6483e8a27d7Timo Sirainen /* reached max. users count, kill few of the
785d9cca224d33ca3938e9166784f6483e8a27d7Timo Sirainen oldest connections */
785d9cca224d33ca3938e9166784f6483e8a27d7Timo Sirainen /* always use nonblocking I/O */
d61a5e0e4ff58d1aa613f0d51161e5bb0f092514Timo Sirainen client->common.trusted = client_is_trusted(&client->common);
44dc970b18c4e2d06f34cb908924152156e4a45bTimo Sirainen client->common.secured = ssl || client->common.trusted ||
3a854fc26bcccb0398f0a9a6fa72db1a4ab8f0b8Timo Sirainen client->io = io_add(fd, IO_READ, client_input, client);
3a854fc26bcccb0398f0a9a6fa72db1a4ab8f0b8Timo Sirainenvoid client_destroy(struct imap_client *client, const char *reason)
d61a5e0e4ff58d1aa613f0d51161e5bb0f092514Timo Sirainen if (!client->login_success && reason != NULL) {
a3d22d3cb0e5436128ca7287cedc921f1789b2c8Timo Sirainen client_get_extra_disconnect_reason(&client->common),
d61a5e0e4ff58d1aa613f0d51161e5bb0f092514Timo Sirainen i_assert(client->common.auth_request == NULL);
d61a5e0e4ff58d1aa613f0d51161e5bb0f092514Timo Sirainen } else if (client->common.auth_request != NULL) {
d61a5e0e4ff58d1aa613f0d51161e5bb0f092514Timo Sirainen i_free_and_null(client->proxy_backend_capability);
d61a5e0e4ff58d1aa613f0d51161e5bb0f092514Timo Sirainenvoid client_destroy_success(struct imap_client *client, const char *reason)
d61a5e0e4ff58d1aa613f0d51161e5bb0f092514Timo Sirainenvoid client_destroy_internal_failure(struct imap_client *client)
d61a5e0e4ff58d1aa613f0d51161e5bb0f092514Timo Sirainen client_send_line(client, "* BYE Internal login failure. "
7c65a07e04f28f2a881d83989f85f9ad0e87a7b4Timo Sirainen "Refer to server log for more information.");
60f9b96be55e63f0113e273dda8ba3b883c6f095Timo Sirainen client_destroy(client, "Internal login failure");
7895c4845da515b0aa9bb156674a1fca40803f44Timo Sirainen master_service_client_connection_destroyed(master_service);
d22301419109ed4a38351715e6760011421dadecTimo Sirainenvoid client_send_line(struct imap_client *client, const char *line)
c2cb5e469cd11759da22d82083d4fbb564d06dfaTimo Sirainen if (ret < 0 || (size_t)ret != iov[0].iov_len + iov[1].iov_len) {
c2cb5e469cd11759da22d82083d4fbb564d06dfaTimo Sirainen /* either disconnection or buffer full. in either case we
c2cb5e469cd11759da22d82083d4fbb564d06dfaTimo Sirainen want this connection destroyed. however destroying it here
ced118ac5caf6fe83d34339c2c65c63b2aa768acTimo Sirainen might break things if client is still tried to be accessed
c2cb5e469cd11759da22d82083d4fbb564d06dfaTimo Sirainen without being referenced.. */
c2cb5e469cd11759da22d82083d4fbb564d06dfaTimo Sirainenvoid client_send_tagline(struct imap_client *client, const char *line)
0ed9ccd0047f75df54a49bc117ca301eb398e447Timo Sirainen client_send_line(client, t_strconcat(client->cmd_tag, " ", line, NULL));
8d889b6d842e96ecbe7b6493920bbb6df8e0ed30Timo Sirainen for (client = clients; client != NULL; client = client->next) {
8d889b6d842e96ecbe7b6493920bbb6df8e0ed30Timo Sirainen struct imap_client *imap_client = (struct imap_client *)client;
c2cb5e469cd11759da22d82083d4fbb564d06dfaTimo Sirainen timeout_remove(&imap_client->to_auth_waiting);
c2cb5e469cd11759da22d82083d4fbb564d06dfaTimo Sirainen for (client = clients; client != NULL; client = next) {
c2cb5e469cd11759da22d82083d4fbb564d06dfaTimo Sirainen struct imap_client *imap_client = (struct imap_client *)client;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen client_destroy(imap_client, "Disconnected: Shutting down");
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* Nothing to initialize for IMAP */