client.c revision d5cebe7f98e63d4e2822863ef2faa4971e8b3a5d
5f5870385cff47efd2f58e7892f251cf13761528Timo Sirainen/* Copyright (C) 2002 Timo Sirainen */
1d4f710106fb498750456724628da6063e012e6dTimo Sirainen/* max. size of one parameter in line, or max reply length in SASL
1d4f710106fb498750456724628da6063e012e6dTimo Sirainen authentication */
6fdfa4d4cf14d1d7764d7faa8258f112e39c8dbeTimo Sirainen/* max. size of output buffer. if it gets full, the client is disconnected.
ab0d9eecd85f74acae18fe88529302e0776cc500Timo Sirainen SASL authentication gives the largest output. */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen/* maximum length for IMAP command line. */
7a94f950fd1dcc81537acfc8adb030b5e703d722Timo Sirainen/* Disconnect client after idling this many seconds */
7a94f950fd1dcc81537acfc8adb030b5e703d722Timo Sirainen/* Disconnect client when it sends too many bad commands */
7a94f950fd1dcc81537acfc8adb030b5e703d722Timo Sirainen/* When max. number of simultaneous connections is reached, few of the
7a94f950fd1dcc81537acfc8adb030b5e703d722Timo Sirainen oldest connections are disconnected. Since we have to go through the whole
7a94f950fd1dcc81537acfc8adb030b5e703d722Timo Sirainen client hash, it's faster if we disconnect multiple clients. */
7a94f950fd1dcc81537acfc8adb030b5e703d722Timo Sirainen#if CLIENT_LOGIN_IDLE_TIMEOUT >= AUTH_REQUEST_TIMEOUT
7a94f950fd1dcc81537acfc8adb030b5e703d722Timo Sirainen# error client idle timeout must be smaller than authentication timeout
7a94f950fd1dcc81537acfc8adb030b5e703d722Timo Sirainenstatic void client_set_title(struct imap_client *client)
7a94f950fd1dcc81537acfc8adb030b5e703d722Timo Sirainen if (!verbose_proctitle || !process_per_connection)
ad004e44be109684521494b5af2ad1da39b8bb27Timo Sirainen process_title_set(t_strdup_printf(client->common.tls ?
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenstatic void client_open_streams(struct imap_client *client, int fd)
baebb412a9a5a44b1756e01cfa3b99f5d8a846b6Timo Sirainen client->input = i_stream_create_file(fd, default_pool,
baebb412a9a5a44b1756e01cfa3b99f5d8a846b6Timo Sirainen client->output = o_stream_create_file(fd, default_pool, MAX_OUTBUF_SIZE,
baebb412a9a5a44b1756e01cfa3b99f5d8a846b6Timo Sirainen client->parser = imap_parser_create(client->input, client->output,
f059a046515f4b2b15a6c2a10a6f12f6166e39a5Timo Sirainen/* Skip incoming data until newline is found,
f059a046515f4b2b15a6c2a10a6f12f6166e39a5Timo Sirainen returns TRUE if newline was found. */
f059a046515f4b2b15a6c2a10a6f12f6166e39a5Timo Sirainenbool client_skip_line(struct imap_client *client)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen const unsigned char *data;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen data = i_stream_get_data(client->input, &data_size);
5a250816ffc4cc5db203f9410ea99b6601c7b91aTimo Sirainen for (i = 0; i < data_size; i++) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenstatic const char *get_capability(struct imap_client *client)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen auths = client_authenticate_get_capabilities(client->common.secured);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen disable_plaintext_auth && !client->common.secured ?
ad004e44be109684521494b5af2ad1da39b8bb27Timo Sirainenstatic int cmd_capability(struct imap_client *client)
5a9e240ebf8d0daaf029973973b52e415148070bTimo Sirainen client_send_line(client, t_strconcat("* CAPABILITY ",
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen client_send_tagline(client, "OK Capability completed.");
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenstatic void client_start_tls(struct imap_client *client)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen fd_ssl = ssl_proxy_new(client->common.fd, &client->common.ip,
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen client_send_line(client, "* BYE TLS initialization failed.");
ad004e44be109684521494b5af2ad1da39b8bb27Timo Sirainen client_destroy(client, "TLS initialization failed.");
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen /* CRLF is lost from buffer when streams are reopened. */
d3d769026fae5d21c2d29614d3bc4579e8d79e81Timo Sirainen client->io = io_add(client->common.fd, IO_READ, client_input, client);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenstatic int client_output_starttls(void *context)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if ((ret = o_stream_flush(client->output)) < 0) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenstatic int cmd_starttls(struct imap_client *client)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen client_send_tagline(client, "BAD TLS is already active.");
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen client_send_tagline(client, "BAD TLS support isn't enabled.");
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen /* remove input handler, SSL proxy gives us a new fd. we also have to
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen remove it in case we have to wait for buffer to be flushed */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen client_send_tagline(client, "OK Begin TLS negotiation now.");
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen /* uncork the old fd */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen /* the buffer has to be flushed */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen o_stream_set_flush_pending(client->output, TRUE);
2aac7ca853f63b62ea79ef8eae9ded83ed6063a5Timo Sirainenstatic int cmd_noop(struct imap_client *client)
4de2a17e0a2aed3b57a6c1057329b6a132b56ae2Timo Sirainen client_send_tagline(client, "OK NOOP completed.");
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenstatic int cmd_logout(struct imap_client *client)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen client_send_line(client, "* BYE Logging out");
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen client_send_tagline(client, "OK Logout completed.");
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenstatic int client_command_execute(struct imap_client *client, const char *cmd,
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainenstatic bool client_handle_input(struct imap_client *client)
ec23e16ed879e289d12c6e1a5f9745dd3979004aTimo Sirainen const char *msg;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen /* clear the previous command from memory. don't do this
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen immediately after handling command since we need the
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen cmd_tag to stay some time after authentication commands. */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen /* remove \r\n */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen client->cmd_tag = imap_parser_read_word(client->parser);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen client->cmd_name = imap_parser_read_word(client->parser);
6fdfa4d4cf14d1d7764d7faa8258f112e39c8dbeTimo Sirainen switch (imap_parser_read_args(client->parser, 0, 0, &args)) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen msg = imap_parser_get_error(client->parser, &fatal);
6fdfa4d4cf14d1d7764d7faa8258f112e39c8dbeTimo Sirainen client_send_line(client, t_strconcat("* BYE ",
6fdfa4d4cf14d1d7764d7faa8258f112e39c8dbeTimo Sirainen client_destroy(client, t_strconcat("Disconnected: ",
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen client_send_tagline(client, t_strconcat("BAD ", msg, NULL));
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen /* not enough data */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen ret = client_command_execute(client, client->cmd_name, args);
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen if (++client->bad_counter >= CLIENT_MAX_BAD_COMMANDS) {
cec3230c9b2a96bac1ea42c69475e8aea4b91eabTimo Sirainen "* BYE Too many invalid IMAP commands.");
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen "Too many invalid commands");
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen "BAD Error in IMAP command received by server.");
ec23e16ed879e289d12c6e1a5f9745dd3979004aTimo Sirainen return ret != 0;
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen /* buffer full */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen client_send_line(client, "* BYE Input buffer full, aborting");
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen client_destroy(client, "Disconnected: Input buffer full");
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen /* disconnected */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen /* something was read */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen /* we're not yet connected to auth process -
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen don't allow any commands */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen "* OK Waiting for authentication process to respond..");
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen struct imap_client *destroy_buf[CLIENT_DESTROY_OLDEST_COUNT];
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen /* find the oldest clients and put them to destroy-buffer */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen for (i = 0; i < CLIENT_DESTROY_OLDEST_COUNT; i++) {
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen /* @UNSAFE */
b5917cf6476ffb7cdeb2e2544057ea1605ea6fdcTimo Sirainen /* then kill them */
b5917cf6476ffb7cdeb2e2544057ea1605ea6fdcTimo Sirainen for (i = 0; i < CLIENT_DESTROY_OLDEST_COUNT; i++) {
b5917cf6476ffb7cdeb2e2544057ea1605ea6fdcTimo Sirainen "Disconnected: Connection queue full");
b5917cf6476ffb7cdeb2e2544057ea1605ea6fdcTimo Sirainenstatic void client_send_greeting(struct imap_client *client)
9439bed2f07d6475febd8a247cd2f0990fb32a13Timo Sirainen i_assert(auth_client_is_connected(auth_client));
9439bed2f07d6475febd8a247cd2f0990fb32a13Timo Sirainen str_printfa(greet, "[CAPABILITY %s] ", get_capability(client));
9439bed2f07d6475febd8a247cd2f0990fb32a13Timo Sirainenstruct client *client_create(int fd, bool ssl, const struct ip_addr *local_ip,
45155bb1250cf5a120278f349465aded513a100fTimo Sirainen if (max_logging_users > CLIENT_DESTROY_OLDEST_COUNT &&
9439bed2f07d6475febd8a247cd2f0990fb32a13Timo Sirainen /* reached max. users count, kill few of the
5a250816ffc4cc5db203f9410ea99b6601c7b91aTimo Sirainen oldest connections */
9439bed2f07d6475febd8a247cd2f0990fb32a13Timo Sirainen /* always use nonblocking I/O */
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen (IPADDR_IS_V4(ip) && strncmp(addr, "127.", 4) == 0) ||
bb8d0ec26bdd548624d7a7424071cca693b72f55Timo Sirainen (IPADDR_IS_V6(ip) && (strcmp(addr, "::1") == 0 ||
4ead43ecc06d10047998966c4dc0b142ecce4b66Timo Sirainen client->io = io_add(fd, IO_READ, client_input, client);
1d4f710106fb498750456724628da6063e012e6dTimo Sirainen if (!greeting_capability || auth_client_is_connected(auth_client))
88c92ce2caa8f9fa34708471c6ed4e974d5a7953Timo Sirainenvoid client_destroy(struct imap_client *client, const char *reason)
b5917cf6476ffb7cdeb2e2544057ea1605ea6fdcTimo Sirainen auth_client_request_abort(client->common.auth_request);
8372fc7efb6d64dff2e5f55fb4a3822c56869cfeTimo Sirainenvoid client_destroy_internal_failure(struct imap_client *client)
9439bed2f07d6475febd8a247cd2f0990fb32a13Timo Sirainen client_send_line(client, "* BYE Internal login failure. "
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen "Refer to server log for more information.");
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen client_destroy(client, "Internal login failure");
89795c6bbbc52bb382e88bc8617d22092223e9a5Timo Sirainenvoid client_send_line(struct imap_client *client, const char *line)
89795c6bbbc52bb382e88bc8617d22092223e9a5Timo Sirainen if (ret < 0 || (size_t)ret != iov[0].iov_len + iov[1].iov_len) {
89795c6bbbc52bb382e88bc8617d22092223e9a5Timo Sirainen /* either disconnection or buffer full. in either case we
89795c6bbbc52bb382e88bc8617d22092223e9a5Timo Sirainen want this connection destroyed. however destroying it here
38318f5e82662615cd88e99e398efe4a630ce020Timo Sirainen might break things if client is still tried to be accessed
38318f5e82662615cd88e99e398efe4a630ce020Timo Sirainen without being referenced.. */
434abef12f61881a5cfa28d27193d0854a9639a0Timo Sirainenvoid client_send_tagline(struct imap_client *client, const char *line)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen client_send_line(client, t_strconcat(client->cmd_tag, " ", line, NULL));
38318f5e82662615cd88e99e398efe4a630ce020Timo Sirainenstatic void client_check_idle(struct imap_client *client)
38318f5e82662615cd88e99e398efe4a630ce020Timo Sirainen if (ioloop_time - client->last_input >= CLIENT_LOGIN_IDLE_TIMEOUT) {
b9c76fe9d9ca194816606342da1ddbd9be6bc8abTimo Sirainen client_send_line(client, "* BYE Disconnected for inactivity.");
b9c76fe9d9ca194816606342da1ddbd9be6bc8abTimo Sirainen client_destroy(client, "Disconnected: Inactivity");
28dae6a0064e79f86da091625b0f2b92336a2a91Timo Sirainenstatic void idle_timeout(void *context __attr_unused__)
6998ca95b4947c90647ac5d4794ebd6311acada2Timo Sirainenunsigned int clients_get_count(void)
e248fe370c4047cee921a91b48edc37944ab0526Timo Sirainen clients = hash_create(default_pool, default_pool, 128, NULL, NULL);