client.c revision 2cfe9983ce7a6280636ee12beccc2e865111967b
e59faf65ce864fe95dc00f5d52b8323cdbd0608aTimo Sirainen/* Copyright (C) 2002 Timo Sirainen */
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen#include "common.h"
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen#include "base64.h"
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen#include "buffer.h"
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen#include "hash.h"
8cb72c59d5ea4e9e5f638d7ec840bb853f5a188eTimo Sirainen#include "ioloop.h"
8cb72c59d5ea4e9e5f638d7ec840bb853f5a188eTimo Sirainen#include "istream.h"
8cb72c59d5ea4e9e5f638d7ec840bb853f5a188eTimo Sirainen#include "ostream.h"
8cb72c59d5ea4e9e5f638d7ec840bb853f5a188eTimo Sirainen#include "randgen.h"
8cb72c59d5ea4e9e5f638d7ec840bb853f5a188eTimo Sirainen#include "process-title.h"
8cb72c59d5ea4e9e5f638d7ec840bb853f5a188eTimo Sirainen#include "safe-memset.h"
8cb72c59d5ea4e9e5f638d7ec840bb853f5a188eTimo Sirainen#include "strescape.h"
8cb72c59d5ea4e9e5f638d7ec840bb853f5a188eTimo Sirainen#include "client.h"
8cb72c59d5ea4e9e5f638d7ec840bb853f5a188eTimo Sirainen#include "client-authenticate.h"
8cb72c59d5ea4e9e5f638d7ec840bb853f5a188eTimo Sirainen#include "auth-client.h"
8cb72c59d5ea4e9e5f638d7ec840bb853f5a188eTimo Sirainen#include "ssl-proxy.h"
8cb72c59d5ea4e9e5f638d7ec840bb853f5a188eTimo Sirainen#include "pop3-proxy.h"
cd56a23e21f1df3f79648cf07e2f4385e2fadebbTimo Sirainen#include "hostpid.h"
cd56a23e21f1df3f79648cf07e2f4385e2fadebbTimo Sirainen
cd56a23e21f1df3f79648cf07e2f4385e2fadebbTimo Sirainen/* max. length of input command line (spec says 512), or max reply length in
cd56a23e21f1df3f79648cf07e2f4385e2fadebbTimo Sirainen SASL authentication */
cd56a23e21f1df3f79648cf07e2f4385e2fadebbTimo Sirainen#define MAX_INBUF_SIZE 4096
cd56a23e21f1df3f79648cf07e2f4385e2fadebbTimo Sirainen
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen/* max. size of output buffer. if it gets full, the client is disconnected.
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen SASL authentication gives the largest output. */
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainen#define MAX_OUTBUF_SIZE 4096
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen/* Disconnect client after idling this many seconds */
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen#define CLIENT_LOGIN_IDLE_TIMEOUT (3*60)
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainen/* Disconnect client when it sends too many bad commands */
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen#define CLIENT_MAX_BAD_COMMANDS 10
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen/* When max. number of simultaneous connections is reached, few of the
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen oldest connections are disconnected. Since we have to go through the whole
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen client hash, it's faster if we disconnect multiple clients. */
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainen#define CLIENT_DESTROY_OLDEST_COUNT 16
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen#if CLIENT_LOGIN_IDLE_TIMEOUT >= AUTH_REQUEST_TIMEOUT
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen# error client idle timeout must be smaller than authentication timeout
59151b71059df1190acd75d8717ed04a7920c862Timo Sirainen#endif
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen
211ed7806d8715ec2280ffbf5d10f0d6e4f1beb2Timo Sirainenconst char *login_protocol = "POP3";
211ed7806d8715ec2280ffbf5d10f0d6e4f1beb2Timo Sirainen
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainenstatic struct hash_table *clients;
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainenstatic struct timeout *to_idle;
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainenstatic void client_set_title(struct pop3_client *client)
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen{
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen const char *addr;
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen
59151b71059df1190acd75d8717ed04a7920c862Timo Sirainen if (!verbose_proctitle || !process_per_connection)
59151b71059df1190acd75d8717ed04a7920c862Timo Sirainen return;
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainen
59151b71059df1190acd75d8717ed04a7920c862Timo Sirainen addr = net_ip2addr(&client->common.ip);
59151b71059df1190acd75d8717ed04a7920c862Timo Sirainen if (addr == NULL)
59151b71059df1190acd75d8717ed04a7920c862Timo Sirainen addr = "??";
59151b71059df1190acd75d8717ed04a7920c862Timo Sirainen
59151b71059df1190acd75d8717ed04a7920c862Timo Sirainen process_title_set(t_strdup_printf(client->common.tls ?
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen "[%s TLS]" : "[%s]", addr));
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen}
8f32e59200da904613506f5649ffa4d9f5989cebTimo Sirainen
8f32e59200da904613506f5649ffa4d9f5989cebTimo Sirainenstatic void client_open_streams(struct pop3_client *client, int fd)
8f32e59200da904613506f5649ffa4d9f5989cebTimo Sirainen{
8f32e59200da904613506f5649ffa4d9f5989cebTimo Sirainen client->input = i_stream_create_file(fd, default_pool,
8f32e59200da904613506f5649ffa4d9f5989cebTimo Sirainen MAX_INBUF_SIZE, FALSE);
8f32e59200da904613506f5649ffa4d9f5989cebTimo Sirainen client->output = o_stream_create_file(fd, default_pool,
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen MAX_OUTBUF_SIZE, FALSE);
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen}
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainenstatic void client_start_tls(struct pop3_client *client)
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainen{
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen int fd_ssl;
c9bf63e9094761767a63ac6b189bcf60bcffdc44Timo Sirainen
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen client_ref(client);
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen connection_queue_add(1);
8f32e59200da904613506f5649ffa4d9f5989cebTimo Sirainen if (!client_unref(client))
8f32e59200da904613506f5649ffa4d9f5989cebTimo Sirainen return;
214b18002f289c15b4e48fef537a79db312c9d0fTimo Sirainen
214b18002f289c15b4e48fef537a79db312c9d0fTimo Sirainen fd_ssl = ssl_proxy_new(client->common.fd, &client->common.ip,
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen &client->common.proxy);
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen if (fd_ssl == -1) {
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen client_send_line(client, "-ERR TLS initialization failed.");
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen client_destroy(client,
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainen "Disconnected: TLS initialization failed.");
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen return;
c9bf63e9094761767a63ac6b189bcf60bcffdc44Timo Sirainen }
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen client->common.tls = TRUE;
8f32e59200da904613506f5649ffa4d9f5989cebTimo Sirainen client->common.secured = TRUE;
8f32e59200da904613506f5649ffa4d9f5989cebTimo Sirainen client_set_title(client);
214b18002f289c15b4e48fef537a79db312c9d0fTimo Sirainen
214b18002f289c15b4e48fef537a79db312c9d0fTimo Sirainen client->common.fd = fd_ssl;
7e6ad778c7de5be3bcb79152ef58e2e52015906dTimo Sirainen
214b18002f289c15b4e48fef537a79db312c9d0fTimo Sirainen i_stream_unref(&client->input);
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen o_stream_unref(&client->output);
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen client_open_streams(client, fd_ssl);
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen client->io = io_add(client->common.fd, IO_READ, client_input, client);
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainen}
8f32e59200da904613506f5649ffa4d9f5989cebTimo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainenstatic int client_output_starttls(struct pop3_client *client)
c9bf63e9094761767a63ac6b189bcf60bcffdc44Timo Sirainen{
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen int ret;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
53cd46dd843c22f21f7e6efcc52a3e0f76cd1e52Timo Sirainen if ((ret = o_stream_flush(client->output)) < 0) {
8f32e59200da904613506f5649ffa4d9f5989cebTimo Sirainen client_destroy(client, "Disconnected");
8f32e59200da904613506f5649ffa4d9f5989cebTimo Sirainen return 1;
8f32e59200da904613506f5649ffa4d9f5989cebTimo Sirainen }
8f32e59200da904613506f5649ffa4d9f5989cebTimo Sirainen
8f32e59200da904613506f5649ffa4d9f5989cebTimo Sirainen if (ret > 0) {
6cc0546c058f3e6253c6f99727b28dd602712974Timo Sirainen o_stream_unset_flush_callback(client->output);
6cc0546c058f3e6253c6f99727b28dd602712974Timo Sirainen client_start_tls(client);
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen }
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen return 1;
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainen}
5238111c460098d9cc8cc22527026138a278b9a4Timo Sirainen
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainenstatic bool cmd_stls(struct pop3_client *client)
5238111c460098d9cc8cc22527026138a278b9a4Timo Sirainen{
c9bf63e9094761767a63ac6b189bcf60bcffdc44Timo Sirainen if (client->common.tls) {
c9bf63e9094761767a63ac6b189bcf60bcffdc44Timo Sirainen client_send_line(client, "-ERR TLS is already active.");
c9bf63e9094761767a63ac6b189bcf60bcffdc44Timo Sirainen return TRUE;
8f32e59200da904613506f5649ffa4d9f5989cebTimo Sirainen }
8f32e59200da904613506f5649ffa4d9f5989cebTimo Sirainen
5238111c460098d9cc8cc22527026138a278b9a4Timo Sirainen if (!ssl_initialized) {
5238111c460098d9cc8cc22527026138a278b9a4Timo Sirainen client_send_line(client, "-ERR TLS support isn't enabled.");
68a4946b12583b88fa802e52ebee45cd96056772Timo Sirainen return TRUE;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen }
68a4946b12583b88fa802e52ebee45cd96056772Timo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen /* remove input handler, SSL proxy gives us a new fd. we also have to
8f32e59200da904613506f5649ffa4d9f5989cebTimo Sirainen remove it in case we have to wait for buffer to be flushed */
8f32e59200da904613506f5649ffa4d9f5989cebTimo Sirainen if (client->io != NULL)
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen io_remove(&client->io);
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
de954ff15b495be13007a8aca2c09fd1d356a283Timo Sirainen client_send_line(client, "+OK Begin TLS negotiation now.");
de954ff15b495be13007a8aca2c09fd1d356a283Timo Sirainen
de954ff15b495be13007a8aca2c09fd1d356a283Timo Sirainen /* uncork the old fd */
de954ff15b495be13007a8aca2c09fd1d356a283Timo Sirainen o_stream_uncork(client->output);
de954ff15b495be13007a8aca2c09fd1d356a283Timo Sirainen
de954ff15b495be13007a8aca2c09fd1d356a283Timo Sirainen if (o_stream_flush(client->output) <= 0) {
de954ff15b495be13007a8aca2c09fd1d356a283Timo Sirainen /* the buffer has to be flushed */
de954ff15b495be13007a8aca2c09fd1d356a283Timo Sirainen o_stream_set_flush_pending(client->output, TRUE);
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen o_stream_set_flush_callback(client->output,
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen client_output_starttls, client);
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainen } else {
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen client_start_tls(client);
c9bf63e9094761767a63ac6b189bcf60bcffdc44Timo Sirainen }
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen return TRUE;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen}
53cd46dd843c22f21f7e6efcc52a3e0f76cd1e52Timo Sirainen
8f32e59200da904613506f5649ffa4d9f5989cebTimo Sirainenstatic bool cmd_quit(struct pop3_client *client)
8f32e59200da904613506f5649ffa4d9f5989cebTimo Sirainen{
8f32e59200da904613506f5649ffa4d9f5989cebTimo Sirainen client_send_line(client, "+OK Logging out");
8f32e59200da904613506f5649ffa4d9f5989cebTimo Sirainen client_destroy(client, "Aborted login");
8f32e59200da904613506f5649ffa4d9f5989cebTimo Sirainen return TRUE;
8f32e59200da904613506f5649ffa4d9f5989cebTimo Sirainen}
8f32e59200da904613506f5649ffa4d9f5989cebTimo Sirainen
8f32e59200da904613506f5649ffa4d9f5989cebTimo Sirainenstatic bool client_command_execute(struct pop3_client *client, const char *cmd,
8f32e59200da904613506f5649ffa4d9f5989cebTimo Sirainen const char *args)
6cc0546c058f3e6253c6f99727b28dd602712974Timo Sirainen{
6cc0546c058f3e6253c6f99727b28dd602712974Timo Sirainen cmd = t_str_ucase(cmd);
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen if (strcmp(cmd, "CAPA") == 0)
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen return cmd_capa(client, args);
b92e979748a22925b0770d3004eaab043ed69574Timo Sirainen if (strcmp(cmd, "USER") == 0)
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen return cmd_user(client, args);
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen if (strcmp(cmd, "PASS") == 0)
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen return cmd_pass(client, args);
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen if (strcmp(cmd, "AUTH") == 0)
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen return cmd_auth(client, args);
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen if (strcmp(cmd, "APOP") == 0)
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen return cmd_apop(client, args);
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen if (strcmp(cmd, "STLS") == 0)
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen return cmd_stls(client);
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen if (strcmp(cmd, "QUIT") == 0)
0ce5f96804e81cb0f857e7df32c0272f1eed9377Timo Sirainen return cmd_quit(client);
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainen client_send_line(client, "-ERR Unknown command.");
0ce5f96804e81cb0f857e7df32c0272f1eed9377Timo Sirainen return FALSE;
0ce5f96804e81cb0f857e7df32c0272f1eed9377Timo Sirainen}
0ce5f96804e81cb0f857e7df32c0272f1eed9377Timo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainenbool client_read(struct pop3_client *client)
c9bf63e9094761767a63ac6b189bcf60bcffdc44Timo Sirainen{
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen switch (i_stream_read(client->input)) {
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen case -2:
53cd46dd843c22f21f7e6efcc52a3e0f76cd1e52Timo Sirainen /* buffer full */
0ce5f96804e81cb0f857e7df32c0272f1eed9377Timo Sirainen client_send_line(client, "-ERR Input line too long, aborting");
0ce5f96804e81cb0f857e7df32c0272f1eed9377Timo Sirainen client_destroy(client, "Disconnected: Input buffer full");
0dbcf4026ff8471b4d6d2e14f7e52321396bf087Timo Sirainen return FALSE;
0dbcf4026ff8471b4d6d2e14f7e52321396bf087Timo Sirainen case -1:
0ce5f96804e81cb0f857e7df32c0272f1eed9377Timo Sirainen /* disconnected */
0ce5f96804e81cb0f857e7df32c0272f1eed9377Timo Sirainen client_destroy(client, "Disconnected");
6cc0546c058f3e6253c6f99727b28dd602712974Timo Sirainen return FALSE;
6cc0546c058f3e6253c6f99727b28dd602712974Timo Sirainen default:
6cc0546c058f3e6253c6f99727b28dd602712974Timo Sirainen /* something was read */
6cc0546c058f3e6253c6f99727b28dd602712974Timo Sirainen return TRUE;
6cc0546c058f3e6253c6f99727b28dd602712974Timo Sirainen }
6cc0546c058f3e6253c6f99727b28dd602712974Timo Sirainen}
6cc0546c058f3e6253c6f99727b28dd602712974Timo Sirainen
6cc0546c058f3e6253c6f99727b28dd602712974Timo Sirainenvoid client_input(struct pop3_client *client)
0ce5f96804e81cb0f857e7df32c0272f1eed9377Timo Sirainen{
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen char *line, *args;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
b92e979748a22925b0770d3004eaab043ed69574Timo Sirainen client->last_input = ioloop_time;
555ebb032f9b8f0cdb66f27ce7374734833e7cacTimo Sirainen
555ebb032f9b8f0cdb66f27ce7374734833e7cacTimo Sirainen if (!client_read(client))
555ebb032f9b8f0cdb66f27ce7374734833e7cacTimo Sirainen return;
555ebb032f9b8f0cdb66f27ce7374734833e7cacTimo Sirainen
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen client_ref(client);
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen o_stream_cork(client->output);
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainen while (!client->output->closed &&
989cafb9d84d8c98d6441fc1ab45b4c37762a98aTimo Sirainen (line = i_stream_next_line(client->input)) != NULL) {
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen args = strchr(line, ' ');
c9bf63e9094761767a63ac6b189bcf60bcffdc44Timo Sirainen if (args != NULL)
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen *args++ = '\0';
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
53cd46dd843c22f21f7e6efcc52a3e0f76cd1e52Timo Sirainen if (client_command_execute(client, line,
989cafb9d84d8c98d6441fc1ab45b4c37762a98aTimo Sirainen args != NULL ? args : ""))
c9bf63e9094761767a63ac6b189bcf60bcffdc44Timo Sirainen client->bad_counter = 0;
989cafb9d84d8c98d6441fc1ab45b4c37762a98aTimo Sirainen else if (++client->bad_counter > CLIENT_MAX_BAD_COMMANDS) {
989cafb9d84d8c98d6441fc1ab45b4c37762a98aTimo Sirainen client_send_line(client, "-ERR Too many bad commands.");
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen client_destroy(client,
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen "Disconnected: Too many bad commands");
8cdb3234fe3c77e477c7a0e6934678f58fc54d4dTimo Sirainen }
8cdb3234fe3c77e477c7a0e6934678f58fc54d4dTimo Sirainen }
8cdb3234fe3c77e477c7a0e6934678f58fc54d4dTimo Sirainen
8cdb3234fe3c77e477c7a0e6934678f58fc54d4dTimo Sirainen if (client_unref(client))
8cdb3234fe3c77e477c7a0e6934678f58fc54d4dTimo Sirainen o_stream_uncork(client->output);
8cdb3234fe3c77e477c7a0e6934678f58fc54d4dTimo Sirainen}
8cdb3234fe3c77e477c7a0e6934678f58fc54d4dTimo Sirainen
8cdb3234fe3c77e477c7a0e6934678f58fc54d4dTimo Sirainenvoid client_destroy_oldest(void)
40fdb94d4db3d81e39f0f6655269ea7f6c268f32Timo Sirainen{
40fdb94d4db3d81e39f0f6655269ea7f6c268f32Timo Sirainen struct hash_iterate_context *iter;
40fdb94d4db3d81e39f0f6655269ea7f6c268f32Timo Sirainen void *key, *value;
40fdb94d4db3d81e39f0f6655269ea7f6c268f32Timo Sirainen struct pop3_client *destroy_buf[CLIENT_DESTROY_OLDEST_COUNT];
40fdb94d4db3d81e39f0f6655269ea7f6c268f32Timo Sirainen unsigned int i, destroy_count;
8cdb3234fe3c77e477c7a0e6934678f58fc54d4dTimo Sirainen
8cdb3234fe3c77e477c7a0e6934678f58fc54d4dTimo Sirainen /* find the oldest clients and put them to destroy-buffer */
8cdb3234fe3c77e477c7a0e6934678f58fc54d4dTimo Sirainen memset(destroy_buf, 0, sizeof(destroy_buf));
8cdb3234fe3c77e477c7a0e6934678f58fc54d4dTimo Sirainen
8cdb3234fe3c77e477c7a0e6934678f58fc54d4dTimo Sirainen destroy_count = max_connections > CLIENT_DESTROY_OLDEST_COUNT*2 ?
8cdb3234fe3c77e477c7a0e6934678f58fc54d4dTimo Sirainen CLIENT_DESTROY_OLDEST_COUNT : I_MIN(max_connections/2, 1);
8cdb3234fe3c77e477c7a0e6934678f58fc54d4dTimo Sirainen iter = hash_iterate_init(clients);
8cdb3234fe3c77e477c7a0e6934678f58fc54d4dTimo Sirainen while (hash_iterate(iter, &key, &value)) {
8cdb3234fe3c77e477c7a0e6934678f58fc54d4dTimo Sirainen struct pop3_client *client = key;
8f32e59200da904613506f5649ffa4d9f5989cebTimo Sirainen
8f32e59200da904613506f5649ffa4d9f5989cebTimo Sirainen for (i = 0; i < destroy_count; i++) {
8f32e59200da904613506f5649ffa4d9f5989cebTimo Sirainen if (destroy_buf[i] == NULL ||
8f32e59200da904613506f5649ffa4d9f5989cebTimo Sirainen destroy_buf[i]->created > client->created) {
8f32e59200da904613506f5649ffa4d9f5989cebTimo Sirainen /* @UNSAFE */
8f32e59200da904613506f5649ffa4d9f5989cebTimo Sirainen memmove(destroy_buf+i+1, destroy_buf+i,
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainen sizeof(destroy_buf) -
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen (i+1) * sizeof(struct pop3_client *));
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen destroy_buf[i] = client;
8f32e59200da904613506f5649ffa4d9f5989cebTimo Sirainen break;
8f32e59200da904613506f5649ffa4d9f5989cebTimo Sirainen }
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen }
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainen }
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen hash_iterate_deinit(iter);
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
8ab3d88c56dff2f567193f80cc29821a64e576d1Timo Sirainen /* then kill them */
8ab3d88c56dff2f567193f80cc29821a64e576d1Timo Sirainen for (i = 0; i < destroy_count; i++) {
8ab3d88c56dff2f567193f80cc29821a64e576d1Timo Sirainen if (destroy_buf[i] == NULL)
8ab3d88c56dff2f567193f80cc29821a64e576d1Timo Sirainen break;
8ab3d88c56dff2f567193f80cc29821a64e576d1Timo Sirainen
8ab3d88c56dff2f567193f80cc29821a64e576d1Timo Sirainen client_destroy(destroy_buf[i],
8ab3d88c56dff2f567193f80cc29821a64e576d1Timo Sirainen "Disconnected: Connection queue full");
8ab3d88c56dff2f567193f80cc29821a64e576d1Timo Sirainen }
8ab3d88c56dff2f567193f80cc29821a64e576d1Timo Sirainen}
8ab3d88c56dff2f567193f80cc29821a64e576d1Timo Sirainen
8ab3d88c56dff2f567193f80cc29821a64e576d1Timo Sirainenstatic char *get_apop_challenge(struct pop3_client *client)
8ab3d88c56dff2f567193f80cc29821a64e576d1Timo Sirainen{
8ab3d88c56dff2f567193f80cc29821a64e576d1Timo Sirainen struct auth_connect_id *id = &client->auth_id;
8ab3d88c56dff2f567193f80cc29821a64e576d1Timo Sirainen unsigned char buffer[16];
8ab3d88c56dff2f567193f80cc29821a64e576d1Timo Sirainen buffer_t *buf;
8ab3d88c56dff2f567193f80cc29821a64e576d1Timo Sirainen char *ret;
8ab3d88c56dff2f567193f80cc29821a64e576d1Timo Sirainen
8ab3d88c56dff2f567193f80cc29821a64e576d1Timo Sirainen if (!auth_client_reserve_connection(auth_client, "APOP", id))
8ab3d88c56dff2f567193f80cc29821a64e576d1Timo Sirainen return NULL;
8ab3d88c56dff2f567193f80cc29821a64e576d1Timo Sirainen
8ab3d88c56dff2f567193f80cc29821a64e576d1Timo Sirainen t_push();
8ab3d88c56dff2f567193f80cc29821a64e576d1Timo Sirainen random_fill(buffer, sizeof(buffer));
8ab3d88c56dff2f567193f80cc29821a64e576d1Timo Sirainen buf = buffer_create_static_hard(pool_datastack_create(),
8ab3d88c56dff2f567193f80cc29821a64e576d1Timo Sirainen MAX_BASE64_ENCODED_SIZE(sizeof(buffer)) + 1);
8ab3d88c56dff2f567193f80cc29821a64e576d1Timo Sirainen base64_encode(buffer, sizeof(buffer), buf);
8ab3d88c56dff2f567193f80cc29821a64e576d1Timo Sirainen buffer_append_c(buf, '\0');
8ab3d88c56dff2f567193f80cc29821a64e576d1Timo Sirainen
8ab3d88c56dff2f567193f80cc29821a64e576d1Timo Sirainen ret = i_strdup_printf("<%x.%x.%lx.%s@%s>",
8ab3d88c56dff2f567193f80cc29821a64e576d1Timo Sirainen id->server_pid, id->connect_uid,
8ab3d88c56dff2f567193f80cc29821a64e576d1Timo Sirainen (unsigned long)ioloop_time,
8ab3d88c56dff2f567193f80cc29821a64e576d1Timo Sirainen (const char *)buf->data, my_hostname);
8ab3d88c56dff2f567193f80cc29821a64e576d1Timo Sirainen t_pop();
8ab3d88c56dff2f567193f80cc29821a64e576d1Timo Sirainen return ret;
}
static void client_auth_ready(struct pop3_client *client)
{
client->io = io_add(client->common.fd, IO_READ, client_input, client);
client->apop_challenge = get_apop_challenge(client);
client_send_line(client, t_strconcat("+OK ", greeting,
client->apop_challenge != NULL ?
" " : NULL,
client->apop_challenge, NULL));
}
struct client *client_create(int fd, bool ssl, const struct ip_addr *local_ip,
const struct ip_addr *ip)
{
struct pop3_client *client;
i_assert(fd != -1);
connection_queue_add(1);
/* always use nonblocking I/O */
net_set_nonblock(fd, TRUE);
client = i_new(struct pop3_client, 1);
client->created = ioloop_time;
client->refcount = 1;
client->common.tls = ssl;
client->common.secured = ssl || net_ip_compare(ip, local_ip);
client->common.local_ip = *local_ip;
client->common.ip = *ip;
client->common.fd = fd;
client_open_streams(client, fd);
client->last_input = ioloop_time;
hash_insert(clients, client, client);
main_ref();
client->auth_connected = auth_client_is_connected(auth_client);
if (client->auth_connected)
client_auth_ready(client);
client_set_title(client);
return &client->common;
}
void client_destroy(struct pop3_client *client, const char *reason)
{
if (client->destroyed)
return;
client->destroyed = TRUE;
if (reason != NULL)
client_syslog(&client->common, reason);
hash_remove(clients, client);
if (client->input != NULL)
i_stream_close(client->input);
if (client->output != NULL)
o_stream_close(client->output);
if (client->common.master_tag != 0)
master_request_abort(&client->common);
if (client->common.auth_request != NULL) {
i_assert(client->common.authenticating);
sasl_server_auth_client_error(&client->common, NULL);
} else {
i_assert(!client->common.authenticating);
}
if (client->io != NULL)
io_remove(&client->io);
if (client->common.fd != -1) {
net_disconnect(client->common.fd);
client->common.fd = -1;
}
if (client->proxy_password != NULL) {
safe_memset(client->proxy_password, 0,
strlen(client->proxy_password));
i_free(client->proxy_password);
client->proxy_password = NULL;
}
i_free(client->proxy_user);
client->proxy_user = NULL;
if (client->proxy != NULL) {
login_proxy_free(client->proxy);
client->proxy = NULL;
}
if (client->common.proxy != NULL) {
ssl_proxy_free(client->common.proxy);
client->common.proxy = NULL;
}
client_unref(client);
main_listen_start();
main_unref();
}
void client_destroy_internal_failure(struct pop3_client *client)
{
client_send_line(client, "-ERR [IN-USE] Internal login failure. "
"Refer to server log for more information.");
client_destroy(client, "Internal login failure");
}
void client_ref(struct pop3_client *client)
{
client->refcount++;
}
bool client_unref(struct pop3_client *client)
{
i_assert(client->refcount > 0);
if (--client->refcount > 0)
return TRUE;
i_assert(client->destroyed);
if (client->input != NULL)
i_stream_unref(&client->input);
if (client->output != NULL)
o_stream_unref(&client->output);
i_free(client->last_user);
i_free(client->apop_challenge);
i_free(client->common.virtual_user);
i_free(client->common.auth_mech_name);
i_free(client);
return FALSE;
}
void client_send_line(struct pop3_client *client, const char *line)
{
struct const_iovec iov[2];
ssize_t ret;
iov[0].iov_base = line;
iov[0].iov_len = strlen(line);
iov[1].iov_base = "\r\n";
iov[1].iov_len = 2;
ret = o_stream_sendv(client->output, iov, 2);
if (ret < 0 || (size_t)ret != iov[0].iov_len + iov[1].iov_len) {
/* either disconnection or buffer full. in either case we
want this connection destroyed. however destroying it here
might break things if client is still tried to be accessed
without being referenced.. */
i_stream_close(client->input);
}
}
static void client_check_idle(struct pop3_client *client)
{
if (ioloop_time - client->last_input >= CLIENT_LOGIN_IDLE_TIMEOUT)
client_destroy(client, "Disconnected: Inactivity");
}
static void idle_timeout(void *context __attr_unused__)
{
struct hash_iterate_context *iter;
void *key, *value;
iter = hash_iterate_init(clients);
while (hash_iterate(iter, &key, &value)) {
struct pop3_client *client = key;
client_check_idle(client);
}
hash_iterate_deinit(iter);
}
unsigned int clients_get_count(void)
{
return hash_size(clients);
}
void clients_notify_auth_connected(void)
{
struct hash_iterate_context *iter;
void *key, *value;
iter = hash_iterate_init(clients);
while (hash_iterate(iter, &key, &value)) {
struct pop3_client *client = key;
if (!client->auth_connected) {
client->auth_connected = TRUE;
client_auth_ready(client);
}
}
hash_iterate_deinit(iter);
}
void clients_destroy_all(void)
{
struct hash_iterate_context *iter;
void *key, *value;
iter = hash_iterate_init(clients);
while (hash_iterate(iter, &key, &value)) {
struct pop3_client *client = key;
client_destroy(client, "Disconnected: Shutting down");
}
hash_iterate_deinit(iter);
}
void clients_init(void)
{
clients = hash_create(default_pool, default_pool, 128, NULL, NULL);
to_idle = timeout_add(1000, idle_timeout, NULL);
}
void clients_deinit(void)
{
clients_destroy_all();
hash_destroy(clients);
timeout_remove(&to_idle);
}