client.c revision bc6294e4f0d7a54ff601257adaa44331a91b234e
1968N/A/* Copyright (C) 2002 Timo Sirainen */
1968N/A
1968N/A#include "common.h"
1968N/A#include "buffer.h"
1968N/A#include "hash.h"
1968N/A#include "ioloop.h"
1968N/A#include "istream.h"
1968N/A#include "ostream.h"
1968N/A#include "process-title.h"
1968N/A#include "safe-memset.h"
1968N/A#include "strescape.h"
1968N/A#include "imap-parser.h"
1968N/A#include "client.h"
1968N/A#include "client-authenticate.h"
1968N/A#include "auth-client.h"
1968N/A#include "ssl-proxy.h"
1968N/A
1968N/A/* max. size of one parameter in line */
1968N/A#define MAX_INBUF_SIZE 512
1968N/A
1968N/A#define MAX_OUTBUF_SIZE 1024
1968N/A
1968N/A/* maximum length for IMAP command line. */
2616N/A#define MAX_IMAP_LINE 8192
1968N/A
1968N/A/* Disconnect client after idling this many seconds */
1968N/A#define CLIENT_LOGIN_IDLE_TIMEOUT 60
1968N/A
1968N/A/* Disconnect client when it sends too many bad commands */
1968N/A#define CLIENT_MAX_BAD_COMMANDS 10
1968N/A
1968N/A/* When max. number of simultaneous connections is reached, few of the
1968N/A oldest connections are disconnected. Since we have to go through the whole
1968N/A client hash, it's faster if we disconnect multiple clients. */
1968N/A#define CLIENT_DESTROY_OLDEST_COUNT 16
2510N/A
1968N/A#if CLIENT_LOGIN_IDLE_TIMEOUT >= AUTH_REQUEST_TIMEOUT
2028N/A# error client idle timeout must be smaller than authentication timeout
2028N/A#endif
2028N/A
2028N/Astatic struct hash_table *clients;
2301N/Astatic struct timeout *to_idle;
2028N/A
1968N/Astatic void client_set_title(struct imap_client *client)
1968N/A{
1968N/A const char *addr;
1968N/A
1968N/A if (!verbose_proctitle || !process_per_connection)
2028N/A return;
2028N/A
1968N/A addr = net_ip2addr(&client->common.ip);
2028N/A if (addr == NULL)
2843N/A addr = "??";
2028N/A
1968N/A process_title_set(t_strdup_printf(client->tls ? "[%s TLS]" : "[%s]",
1968N/A addr));
1968N/A}
1968N/A
1968N/Astatic void client_open_streams(struct imap_client *client, int fd)
2028N/A{
1968N/A client->input = i_stream_create_file(fd, default_pool,
2616N/A MAX_INBUF_SIZE, FALSE);
2301N/A client->output = o_stream_create_file(fd, default_pool, MAX_OUTBUF_SIZE,
1968N/A FALSE);
2028N/A client->parser = imap_parser_create(client->input, client->output,
1968N/A MAX_IMAP_LINE);
1968N/A}
1968N/A
1968N/A/* Skip incoming data until newline is found,
2028N/A returns TRUE if newline was found. */
2028N/Astatic int client_skip_line(struct imap_client *client)
2028N/A{
2028N/A const unsigned char *data;
2028N/A size_t i, data_size;
2028N/A
1968N/A data = i_stream_get_data(client->input, &data_size);
2301N/A
1968N/A for (i = 0; i < data_size; i++) {
1968N/A if (data[i] == '\n') {
1968N/A i_stream_skip(client->input, i+1);
1968N/A return TRUE;
1968N/A }
1968N/A }
1968N/A
1968N/A return FALSE;
1968N/A}
1968N/A
1968N/Astatic int cmd_capability(struct imap_client *client)
1968N/A{
1968N/A const char *capability, *auths;
1968N/A
1968N/A auths = client_authenticate_get_capabilities(client->secured);
1968N/A capability = t_strconcat("* CAPABILITY " CAPABILITY_STRING,
1968N/A (ssl_initialized && !client->tls) ?
1968N/A " STARTTLS" : "",
1968N/A disable_plaintext_auth && !client->secured ?
1968N/A " LOGINDISABLED" : "", auths, NULL);
1968N/A client_send_line(client, capability);
1968N/A client_send_tagline(client, "OK Capability completed.");
1968N/A return TRUE;
2301N/A}
2301N/A
2301N/Astatic int cmd_starttls(struct imap_client *client)
2301N/A{
2301N/A int fd_ssl;
2301N/A
2301N/A if (client->tls) {
2301N/A client_send_tagline(client, "BAD TLS is already active.");
2301N/A return TRUE;
2301N/A }
2301N/A
2301N/A if (!ssl_initialized) {
2301N/A client_send_tagline(client, "BAD TLS support isn't enabled.");
1968N/A return TRUE;
1968N/A }
1968N/A
1968N/A client_send_tagline(client, "OK Begin TLS negotiation now.");
1968N/A o_stream_flush(client->output);
1968N/A
1968N/A /* must be removed before ssl_proxy_new(), since it may
1968N/A io_add() the same fd. */
1968N/A if (client->common.io != NULL) {
1968N/A io_remove(client->common.io);
1968N/A client->common.io = NULL;
1968N/A }
1968N/A
1968N/A fd_ssl = ssl_proxy_new(client->common.fd, &client->common.ip,
1968N/A &client->common.proxy);
1968N/A if (fd_ssl != -1) {
1978N/A client->tls = TRUE;
1968N/A client->secured = TRUE;
1968N/A client_set_title(client);
2510N/A
2028N/A /* we skipped it already, so don't ignore next command */
2301N/A client->skip_line = FALSE;
2142N/A
2301N/A client->common.fd = fd_ssl;
2028N/A
2028N/A i_stream_unref(client->input);
2028N/A o_stream_unref(client->output);
2301N/A imap_parser_destroy(client->parser);
1978N/A
2510N/A client_open_streams(client, fd_ssl);
2510N/A client->common.io = io_add(client->common.fd, IO_READ,
2510N/A client_input, client);
2301N/A } else {
2132N/A client_send_line(client, "* BYE TLS initialization failed.");
2028N/A client_destroy(client, "TLS initialization failed.");
2301N/A }
2132N/A
2028N/A return TRUE;
2301N/A}
2301N/A
2301N/Astatic int cmd_noop(struct imap_client *client)
2301N/A{
2028N/A client_send_tagline(client, "OK NOOP completed.");
2028N/A return TRUE;
2028N/A}
2843N/A
2843N/Astatic int cmd_logout(struct imap_client *client)
2843N/A{
2843N/A client_send_line(client, "* BYE Logging out");
2028N/A client_send_tagline(client, "OK Logout completed.");
2028N/A client_destroy(client, "Aborted login");
1968N/A return TRUE;
1968N/A}
1978N/A
1978N/Astatic int client_command_execute(struct imap_client *client, const char *cmd,
1968N/A struct imap_arg *args)
1968N/A{
1968N/A cmd = t_str_ucase(cmd);
1968N/A if (strcmp(cmd, "LOGIN") == 0)
1968N/A return cmd_login(client, args);
1968N/A if (strcmp(cmd, "AUTHENTICATE") == 0)
1968N/A return cmd_authenticate(client, args);
1968N/A if (strcmp(cmd, "CAPABILITY") == 0)
1968N/A return cmd_capability(client);
1968N/A if (strcmp(cmd, "STARTTLS") == 0)
1968N/A return cmd_starttls(client);
1968N/A if (strcmp(cmd, "NOOP") == 0)
1968N/A return cmd_noop(client);
1968N/A if (strcmp(cmd, "LOGOUT") == 0)
1968N/A return cmd_logout(client);
2028N/A
1968N/A return FALSE;
1968N/A}
2301N/A
2301N/Astatic int client_handle_input(struct imap_client *client)
2301N/A{
2301N/A struct imap_arg *args;
2301N/A const char *msg;
2301N/A int fatal;
2301N/A
2301N/A if (client->authenticating)
2301N/A return FALSE; /* wait until authentication is finished */
2301N/A
2301N/A if (client->cmd_finished) {
2301N/A /* clear the previous command from memory. don't do this
2301N/A immediately after handling command since we need the
2301N/A cmd_tag to stay some time after authentication commands. */
2301N/A client->cmd_tag = NULL;
2301N/A client->cmd_name = NULL;
2301N/A imap_parser_reset(client->parser);
2301N/A
2301N/A /* remove \r\n */
2301N/A if (client->skip_line) {
2301N/A if (!client_skip_line(client))
2301N/A return FALSE;
2301N/A client->skip_line = FALSE;
2301N/A }
2301N/A
2301N/A client->cmd_finished = FALSE;
2301N/A }
2301N/A
2301N/A if (client->cmd_tag == NULL) {
2301N/A client->cmd_tag = imap_parser_read_word(client->parser);
2301N/A if (client->cmd_tag == NULL)
2301N/A return FALSE; /* need more data */
2301N/A }
2301N/A
2301N/A if (client->cmd_name == NULL) {
2301N/A client->cmd_name = imap_parser_read_word(client->parser);
2301N/A if (client->cmd_name == NULL)
2301N/A return FALSE; /* need more data */
2301N/A }
2301N/A
2301N/A switch (imap_parser_read_args(client->parser, 0, 0, &args)) {
2301N/A case -1:
2301N/A /* error */
2301N/A msg = imap_parser_get_error(client->parser, &fatal);
2301N/A if (fatal) {
2301N/A client_send_line(client, t_strconcat("* BYE ",
2301N/A msg, NULL));
2301N/A client_destroy(client, t_strconcat("Disconnected: ",
2301N/A msg, NULL));
2301N/A return FALSE;
2301N/A }
2301N/A
2301N/A client_send_tagline(client, t_strconcat("BAD ", msg, NULL));
2301N/A client->cmd_finished = TRUE;
2301N/A client->skip_line = TRUE;
2301N/A return TRUE;
2301N/A case -2:
2301N/A /* not enough data */
2301N/A return FALSE;
2301N/A }
2301N/A client->skip_line = TRUE;
2301N/A
2028N/A if (*client->cmd_tag == '\0' ||
2028N/A !client_command_execute(client, client->cmd_name, args)) {
2028N/A if (*client->cmd_tag == '\0')
2028N/A client->cmd_tag = "*";
2028N/A if (++client->bad_counter >= CLIENT_MAX_BAD_COMMANDS) {
2028N/A client_send_line(client,
2028N/A "* BYE Too many invalid IMAP commands.");
2028N/A client_destroy(client, "Disconnected: "
2028N/A "Too many invalid commands");
2028N/A return FALSE;
2028N/A }
2028N/A client_send_tagline(client,
2028N/A "BAD Error in IMAP command received by server.");
2028N/A }
2028N/A
2843N/A client->cmd_finished = TRUE;
2028N/A return TRUE;
2028N/A}
2028N/A
2028N/Aint client_read(struct imap_client *client)
2028N/A{
2028N/A switch (i_stream_read(client->input)) {
2028N/A case -2:
2028N/A /* buffer full */
2028N/A client_send_line(client, "* BYE Input buffer full, aborting");
2028N/A client_destroy(client, "Disconnected: Input buffer full");
2028N/A return FALSE;
2028N/A case -1:
2028N/A /* disconnected */
2028N/A client_destroy(client, "Disconnected");
2028N/A return FALSE;
2028N/A default:
2028N/A /* something was read */
2073N/A return TRUE;
2073N/A }
2028N/A}
2028N/A
2028N/Avoid client_input(void *context)
2028N/A{
2028N/A struct imap_client *client = context;
2028N/A
2028N/A client->last_input = ioloop_time;
1968N/A
2142N/A if (!client_read(client))
2142N/A return;
2142N/A
2142N/A if (!auth_client_is_connected(auth_client)) {
2142N/A /* we're not yet connected to auth process -
2142N/A don't allow any commands */
2142N/A client_send_line(client,
2142N/A "* OK Waiting for authentication process to respond..");
2142N/A client->input_blocked = TRUE;
2142N/A return;
2142N/A }
2142N/A
2142N/A client_ref(client);
2142N/A
2142N/A o_stream_cork(client->output);
2142N/A while (client_handle_input(client)) ;
2142N/A
2142N/A if (client_unref(client))
2142N/A o_stream_flush(client->output);
2142N/A}
2142N/A
2142N/Astatic void client_destroy_oldest(void)
2142N/A{
2142N/A struct hash_iterate_context *iter;
2142N/A void *key, *value;
2142N/A struct imap_client *destroy_buf[CLIENT_DESTROY_OLDEST_COUNT];
2142N/A int i;
2142N/A
2142N/A /* find the oldest clients and put them to destroy-buffer */
2142N/A memset(destroy_buf, 0, sizeof(destroy_buf));
2142N/A
2142N/A iter = hash_iterate_init(clients);
2142N/A while (hash_iterate(iter, &key, &value)) {
2142N/A struct imap_client *client = key;
2142N/A
2142N/A for (i = 0; i < CLIENT_DESTROY_OLDEST_COUNT; i++) {
2142N/A if (destroy_buf[i] == NULL ||
2142N/A destroy_buf[i]->created > client->created) {
2142N/A /* @UNSAFE */
2142N/A memmove(destroy_buf+i+1, destroy_buf+i,
2142N/A sizeof(destroy_buf) -
2142N/A (i+1) * sizeof(struct imap_client *));
2142N/A destroy_buf[i] = client;
2142N/A break;
2142N/A }
2142N/A }
2142N/A }
2142N/A hash_iterate_deinit(iter);
2142N/A
2142N/A /* then kill them */
2142N/A for (i = 0; i < CLIENT_DESTROY_OLDEST_COUNT; i++) {
2142N/A if (destroy_buf[i] == NULL)
2142N/A break;
2142N/A
2142N/A client_destroy(destroy_buf[i],
2142N/A "Disconnected: Connection queue full");
2142N/A }
2142N/A}
2142N/A
2142N/Astruct client *client_create(int fd, struct ip_addr *ip, int ssl)
2142N/A{
2142N/A struct imap_client *client;
2028N/A const char *addr;
2028N/A
2028N/A if (max_logging_users > CLIENT_DESTROY_OLDEST_COUNT &&
2028N/A hash_size(clients) >= max_logging_users) {
2028N/A /* reached max. users count, kill few of the
2028N/A oldest connections */
2028N/A client_destroy_oldest();
2028N/A }
2028N/A
2028N/A /* always use nonblocking I/O */
2028N/A net_set_nonblock(fd, TRUE);
2028N/A
2028N/A client = i_new(struct imap_client, 1);
2028N/A client->created = ioloop_time;
2028N/A client->refcount = 1;
2028N/A client->tls = ssl;
2028N/A
2028N/A addr = net_ip2addr(ip);
2028N/A client->secured = ssl ||
1968N/A (IPADDR_IS_V4(ip) && strncmp(addr, "127.", 4) == 0) ||
2028N/A (IPADDR_IS_V6(ip) && strcmp(addr, "::1") == 0);
2028N/A
2028N/A client->common.ip = *ip;
2028N/A client->common.fd = fd;
2028N/A
2028N/A client_open_streams(client, fd);
2028N/A client->common.io = io_add(fd, IO_READ, client_input, client);
2028N/A
2028N/A client->last_input = ioloop_time;
2028N/A hash_insert(clients, client, client);
2028N/A
2028N/A main_ref();
2028N/A
2028N/A client_send_line(client, "* OK " PACKAGE " ready.");
2028N/A client_set_title(client);
2028N/A return &client->common;
2028N/A}
2028N/A
2028N/Avoid client_destroy(struct imap_client *client, const char *reason)
2028N/A{
2028N/A if (client->destroyed)
2028N/A return;
2028N/A client->destroyed = TRUE;
2028N/A
1968N/A if (reason != NULL)
1968N/A client_syslog(client, reason);
2028N/A
1968N/A hash_remove(clients, client);
2028N/A
1968N/A i_stream_close(client->input);
1968N/A o_stream_close(client->output);
1968N/A
1968N/A if (client->common.auth_request != NULL) {
1968N/A auth_client_request_abort(client->common.auth_request);
1968N/A client->common.auth_request = NULL;
1968N/A }
2510N/A
1968N/A if (client->common.master_tag != 0)
1968N/A master_request_abort(&client->common);
1968N/A
2028N/A if (client->common.io != NULL) {
2028N/A io_remove(client->common.io);
2028N/A client->common.io = NULL;
2028N/A }
2028N/A
2028N/A if (client->common.fd != -1) {
2028N/A net_disconnect(client->common.fd);
2028N/A client->common.fd = -1;
2028N/A }
2073N/A
2028N/A if (client->common.proxy != NULL)
2028N/A ssl_proxy_free(client->common.proxy);
2028N/A client_unref(client);
2028N/A}
2028N/A
2028N/Avoid client_ref(struct imap_client *client)
2028N/A{
2028N/A client->refcount++;
2028N/A}
2028N/A
2028N/Aint client_unref(struct imap_client *client)
1968N/A{
1968N/A if (--client->refcount > 0)
2028N/A return TRUE;
2028N/A
2028N/A imap_parser_destroy(client->parser);
1968N/A
1968N/A i_stream_unref(client->input);
1968N/A o_stream_unref(client->output);
1968N/A
1968N/A i_free(client->common.virtual_user);
1968N/A i_free(client);
1968N/A
1968N/A main_unref();
1968N/A return FALSE;
1968N/A}
1968N/A
1968N/Avoid client_send_line(struct imap_client *client, const char *line)
1968N/A{
2028N/A o_stream_send_str(client->output, line);
2028N/A o_stream_send(client->output, "\r\n", 2);
2028N/A}
2028N/A
1968N/Avoid client_send_tagline(struct imap_client *client, const char *line)
1968N/A{
1968N/A client_send_line(client, t_strconcat(client->cmd_tag, " ", line, NULL));
1968N/A}
1968N/A
1968N/Avoid client_syslog(struct imap_client *client, const char *text)
1968N/A{
1968N/A const char *addr;
2028N/A
1968N/A addr = net_ip2addr(&client->common.ip);
1968N/A if (addr == NULL)
1968N/A addr = "??";
1968N/A
1968N/A i_info("%s [%s]", text, addr);
1968N/A}
1968N/A
1968N/Astatic void client_check_idle(struct imap_client *client)
1968N/A{
1968N/A if (ioloop_time - client->last_input >= CLIENT_LOGIN_IDLE_TIMEOUT) {
1968N/A client_send_line(client, "* BYE Disconnected for inactivity.");
2510N/A client_destroy(client, "Disconnected: Inactivity");
2510N/A }
2510N/A}
1968N/A
2510N/Astatic void idle_timeout(void *context __attr_unused__)
1968N/A{
1968N/A struct hash_iterate_context *iter;
1968N/A void *key, *value;
1968N/A
1968N/A iter = hash_iterate_init(clients);
2028N/A while (hash_iterate(iter, &key, &value)) {
2453N/A struct imap_client *client = key;
2510N/A
2453N/A client_check_idle(client);
2453N/A }
1968N/A hash_iterate_deinit(iter);
1968N/A}
1968N/A
2028N/Aunsigned int clients_get_count(void)
1968N/A{
1968N/A return hash_size(clients);
1968N/A}
1968N/A
1968N/Avoid clients_notify_auth_connected(void)
1968N/A{
1968N/A struct hash_iterate_context *iter;
1968N/A void *key, *value;
2510N/A
2510N/A iter = hash_iterate_init(clients);
1968N/A while (hash_iterate(iter, &key, &value)) {
2028N/A struct imap_client *client = key;
2028N/A
2028N/A if (client->input_blocked) {
2028N/A client->input_blocked = FALSE;
2028N/A client_input(client);
2028N/A }
2510N/A }
1968N/A hash_iterate_deinit(iter);
2028N/A}
2028N/A
2028N/Avoid clients_destroy_all(void)
2510N/A{
2510N/A struct hash_iterate_context *iter;
2510N/A void *key, *value;
2510N/A
2510N/A iter = hash_iterate_init(clients);
2028N/A while (hash_iterate(iter, &key, &value)) {
2028N/A struct imap_client *client = key;
2028N/A
2028N/A client_destroy(client, NULL);
2028N/A }
2028N/A hash_iterate_deinit(iter);
2028N/A}
2028N/A
2028N/Avoid clients_init(void)
2132N/A{
2132N/A clients = hash_create(default_pool, default_pool, 128, NULL, NULL);
2132N/A to_idle = timeout_add(1000, idle_timeout, NULL);
2132N/A}
2132N/A
2132N/Avoid clients_deinit(void)
2132N/A{
2132N/A clients_destroy_all();
2132N/A hash_destroy(clients);
2132N/A
2132N/A timeout_remove(to_idle);
2132N/A}
2028N/A