client.c revision 31e020ffe023c80d3dc70d3625c0633187620638
183bea41fa640dc8117f3eb45ff935cd81377a84Timo Sirainen/* Copyright (C) 2002 Timo Sirainen */
5d03d9f439e41c90215a3c938ffebe4c2a8ae257Timo Sirainen/* max. size of one parameter in line */
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen/* maximum length for IMAP command line. */
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen/* Disconnect client after idling this many seconds */
542e28b384a6b26695f3e8de38fd5727d06f3333Timo Sirainen/* Disconnect client when it sends too many bad commands */
542e28b384a6b26695f3e8de38fd5727d06f3333Timo Sirainen/* When max. number of simultaneous connections is reached, few of the
542e28b384a6b26695f3e8de38fd5727d06f3333Timo Sirainen oldest connections are disconnected. Since we have to go through the whole
c3a636e4c9ae776e0eed06b6d7ad1ccfb6003afdTimo Sirainen client hash, it's faster if we disconnect multiple clients. */
c3a636e4c9ae776e0eed06b6d7ad1ccfb6003afdTimo Sirainen#if CLIENT_LOGIN_IDLE_TIMEOUT >= AUTH_REQUEST_TIMEOUT
3b22894b8805b186c73d8b754001e8d7e944be85Timo Sirainen# error client idle timeout must be smaller than authentication timeout
fa2433aebcf3fccfa30ca9eed9b1a9166cf92ee2Timo Sirainenstatic void client_set_title(struct imap_client *client)
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen if (!verbose_proctitle || !process_per_connection)
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen process_title_set(t_strdup_printf(client->tls ? "[%s TLS]" : "[%s]",
b9b841558c5f91db7f5fc71c0ac62aad1bbf6418Timo Sirainenstatic void client_open_streams(struct imap_client *client, int fd)
f3e17726502b6cf1912f30aae7e283b5d31ea69cTimo Sirainen client->input = i_stream_create_file(fd, default_pool,
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainen client->output = o_stream_create_file(fd, default_pool, MAX_OUTBUF_SIZE,
367e28a16854ee9f7247b2518f36f5e9163fcc10Timo Sirainen client->parser = imap_parser_create(client->input, client->output,
c07d7eb3ca9754367697c98f5e66a3982a45d142Timo Sirainen/* Skip incoming data until newline is found,
c07d7eb3ca9754367697c98f5e66a3982a45d142Timo Sirainen returns TRUE if newline was found. */
c07d7eb3ca9754367697c98f5e66a3982a45d142Timo Sirainenstatic int client_skip_line(struct imap_client *client)
c07d7eb3ca9754367697c98f5e66a3982a45d142Timo Sirainen const unsigned char *data;
c07d7eb3ca9754367697c98f5e66a3982a45d142Timo Sirainen data = i_stream_get_data(client->input, &data_size);
c07d7eb3ca9754367697c98f5e66a3982a45d142Timo Sirainen for (i = 0; i < data_size; i++) {
c07d7eb3ca9754367697c98f5e66a3982a45d142Timo Sirainenstatic int cmd_capability(struct imap_client *client)
c07d7eb3ca9754367697c98f5e66a3982a45d142Timo Sirainen auths = client_authenticate_get_capabilities(client->secured);
c07d7eb3ca9754367697c98f5e66a3982a45d142Timo Sirainen capability = t_strconcat("* CAPABILITY " CAPABILITY_STRING,
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen client_send_tagline(client, "OK Capability completed.");
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainenstatic int cmd_starttls(struct imap_client *client)
c07d7eb3ca9754367697c98f5e66a3982a45d142Timo Sirainen client_send_tagline(client, "BAD TLS is already active.");
c07d7eb3ca9754367697c98f5e66a3982a45d142Timo Sirainen client_send_tagline(client, "BAD TLS support isn't enabled.");
c07d7eb3ca9754367697c98f5e66a3982a45d142Timo Sirainen client_send_tagline(client, "OK Begin TLS negotiation now.");
c07d7eb3ca9754367697c98f5e66a3982a45d142Timo Sirainen /* must be removed before ssl_proxy_new(), since it may
c07d7eb3ca9754367697c98f5e66a3982a45d142Timo Sirainen io_add() the same fd. */
c07d7eb3ca9754367697c98f5e66a3982a45d142Timo Sirainen fd_ssl = ssl_proxy_new(client->common.fd, &client->common.ip);
c07d7eb3ca9754367697c98f5e66a3982a45d142Timo Sirainen /* we skipped it already, so don't ignore next command */
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen client->common.io = io_add(client->common.fd, IO_READ,
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen client_send_line(client, "* BYE TLS initialization failed.");
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen client_destroy(client, "TLS initialization failed.");
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainenstatic int cmd_noop(struct imap_client *client)
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen client_send_tagline(client, "OK NOOP completed.");
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainenstatic int cmd_logout(struct imap_client *client)
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen client_send_line(client, "* BYE Logging out");
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen client_send_tagline(client, "OK Logout completed.");
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainenstatic int client_command_execute(struct imap_client *client, const char *cmd,
f3e17726502b6cf1912f30aae7e283b5d31ea69cTimo Sirainenstatic int client_handle_input(struct imap_client *client)
002179a890bf4f1942cad6463787719eaa9fd6c0Timo Sirainen return FALSE; /* wait until authentication is finished */
002179a890bf4f1942cad6463787719eaa9fd6c0Timo Sirainen /* clear the previous command from memory. don't do this
002179a890bf4f1942cad6463787719eaa9fd6c0Timo Sirainen immediately after handling command since we need the
002179a890bf4f1942cad6463787719eaa9fd6c0Timo Sirainen cmd_tag to stay some time after authentication commands. */
002179a890bf4f1942cad6463787719eaa9fd6c0Timo Sirainen /* remove \r\n */
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen client->cmd_tag = imap_parser_read_word(client->parser);
542e28b384a6b26695f3e8de38fd5727d06f3333Timo Sirainen client->cmd_name = imap_parser_read_word(client->parser);
542e28b384a6b26695f3e8de38fd5727d06f3333Timo Sirainen switch (imap_parser_read_args(client->parser, 0, 0, &args)) {
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen /* not enough data */
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen !client_command_execute(client, client->cmd_name, args)) {
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen if (++client->bad_counter >= CLIENT_MAX_BAD_COMMANDS) {
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen "* BYE Too many invalid IMAP commands.");
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen "Too many invalid commands");
fc464e5b2b2ab4d415a5d5b90ce4475d34620a75Timo Sirainen "BAD Error in IMAP command received by server.");
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen /* buffer full */
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen client_send_line(client, "* BYE Input buffer full, aborting");
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen client_destroy(client, "Disconnected: Input buffer full");
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen /* disconnected */
3b22894b8805b186c73d8b754001e8d7e944be85Timo Sirainen /* something was read */
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen /* we're not yet connected to auth process -
70afae43cc78ea6ecca83f6c587072c442a15ec1Timo Sirainen don't allow any commands */
70afae43cc78ea6ecca83f6c587072c442a15ec1Timo Sirainen "* OK Waiting for authentication process to respond..");
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainenstatic void client_hash_destroy_oldest(void *key, void *value __attr_unused__,
70afae43cc78ea6ecca83f6c587072c442a15ec1Timo Sirainen destroy_clients = buffer_get_data(destroy_buf, &count);
70afae43cc78ea6ecca83f6c587072c442a15ec1Timo Sirainen for (i = 0; i < count; i++) {
70afae43cc78ea6ecca83f6c587072c442a15ec1Timo Sirainen if (destroy_clients[i]->created > client->created) {
70afae43cc78ea6ecca83f6c587072c442a15ec1Timo Sirainen i * sizeof(struct imap_client *),
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen /* find the oldest clients and put them to destroy-buffer */
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen destroy_buf = buffer_create_static_hard(data_stack_pool,
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen sizeof(struct imap_client *) *
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen hash_foreach(clients, client_hash_destroy_oldest, destroy_buf);
70afae43cc78ea6ecca83f6c587072c442a15ec1Timo Sirainen /* then kill them */
70afae43cc78ea6ecca83f6c587072c442a15ec1Timo Sirainen destroy_clients = buffer_get_data(destroy_buf, &count);
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen for (i = 0; i < count; i++) {
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen "Disconnected: Connection queue full");
216cd45a5f47c9bd46fe67c1b3bd6b1a42f6e39cTimo Sirainenstruct client *client_create(int fd, struct ip_addr *ip, int ssl)
7f472e15b5f19a3536634863950c80a88079da23Timo Sirainen if (max_logging_users > CLIENT_DESTROY_OLDEST_COUNT &&
0139fcb57a88f6ed27a1bb4a1bd537b04fd2b5d6Timo Sirainen /* reached max. users count, kill few of the
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen oldest connections */
70afae43cc78ea6ecca83f6c587072c442a15ec1Timo Sirainen /* always use nonblocking I/O */
c06d6ea0766d0520af1a93e6000c0e73f350e0a2Timo Sirainen (IPADDR_IS_V4(ip) && strncmp(addr, "127.", 4) == 0) ||
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen (IPADDR_IS_V6(ip) && strcmp(addr, "::1") == 0);
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen client->common.io = io_add(fd, IO_READ, client_input, client);
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen client->plain_login = buffer_create_dynamic(system_pool, 128, 8192);
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen client_send_line(client, "* OK " PACKAGE " ready.");
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainenvoid client_destroy(struct imap_client *client, const char *reason)
3c652e7a569c2623d22b4ab30279aebddce4d396Timo Sirainen auth_client_request_abort(client->common.auth_request);
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainenvoid client_send_line(struct imap_client *client, const char *line)
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainenvoid client_send_tagline(struct imap_client *client, const char *line)
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainen client_send_line(client, t_strconcat(client->cmd_tag, " ", line, NULL));
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainenvoid client_syslog(struct imap_client *client, const char *text)
b9b841558c5f91db7f5fc71c0ac62aad1bbf6418Timo Sirainenstatic void client_hash_check_idle(void *key, void *value __attr_unused__,
b9b841558c5f91db7f5fc71c0ac62aad1bbf6418Timo Sirainen if (ioloop_time - client->last_input >= CLIENT_LOGIN_IDLE_TIMEOUT) {
47e9fdee55c2074425cf0316f4f64fbbb790301cTimo Sirainen client_send_line(client, "* BYE Disconnected for inactivity.");
47e9fdee55c2074425cf0316f4f64fbbb790301cTimo Sirainen client_destroy(client, "Disconnected: Inactivity");
0ee3fdb5e94ae6f34cb873ca3c9858342621e55fTimo Sirainenstatic void idle_timeout(void *context __attr_unused__)
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen hash_foreach(clients, client_hash_check_idle, NULL);
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainenunsigned int clients_get_count(void)
1b04762685272a53643ac2179939537a44c7c044Timo Sirainenstatic void client_hash_check_io(void *key, void *value __attr_unused__,
d1fff80640050631b06bfab904a34b2ad24601e8Timo Sirainen hash_foreach(clients, client_hash_check_io, NULL);
fa2a11210f20fb8998ed656f75e163191c8047e6Timo Sirainenstatic void client_hash_destroy(void *key, void *value __attr_unused__,
fa2a11210f20fb8998ed656f75e163191c8047e6Timo Sirainen hash_foreach(clients, client_hash_destroy, NULL);
fa2a11210f20fb8998ed656f75e163191c8047e6Timo Sirainen clients = hash_create(default_pool, default_pool, 128, NULL, NULL);