#include "llist.h"
#include "istream.h"
#include "ostream.h"
+#include "iostream-proxy.h"
#include "iostream-rawlog.h"
#include "process-title.h"
#include "hook-build.h"
if (client->output != NULL)
o_stream_uncork(client->output);
if (!client->login_success) {
+ io_remove(&client->io);
if (client->ssl_proxy != NULL)
ssl_proxy_destroy(client->ssl_proxy);
+ if (client->iostream_fd_proxy != NULL)
+ iostream_proxy_unref(&client->iostream_fd_proxy);
i_stream_close(client->input);
o_stream_close(client->output);
i_close_fd(&client->fd);
i_assert(!client->authenticating);
}
- io_remove(&client->io);
timeout_remove(&client->to_disconnect);
timeout_remove(&client->to_auth_waiting);
if (client->auth_response != NULL)
if (client->ssl_proxy != NULL)
ssl_proxy_free(&client->ssl_proxy);
+ if (client->iostream_fd_proxy != NULL)
+ iostream_proxy_unref(&client->iostream_fd_proxy);
i_stream_unref(&client->input);
o_stream_unref(&client->output);
i_close_fd(&client->fd);
}
}
+static void
+iostream_fd_proxy_finished(enum iostream_proxy_side side ATTR_UNUSED,
+ enum iostream_proxy_status status ATTR_UNUSED,
+ struct client *client)
+{
+ /* Destroy the proxy now. The other side of the proxy is still
+ unfinished and we don't want to get back here and unreference
+ the client twice. */
+ iostream_proxy_unref(&client->iostream_fd_proxy);
+ client_unref(&client);
+}
+
+int client_get_plaintext_fd(struct client *client, int *fd_r, bool *close_fd_r)
+{
+ int fds[2];
+
+ if (!client->tls) {
+ /* Plaintext connection - We can send the fd directly to
+ the post-login process without any proxying. */
+ *fd_r = client->fd;
+ *close_fd_r = FALSE;
+ return 0;
+ }
+
+ /* We'll have to start proxying from now on until either side
+ disconnects. Create a socketpair where login process is proxying on
+ one side and the other side is sent to the post-login process. */
+ if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds) < 0) {
+ client_log_err(client, t_strdup_printf("socketpair() failed: %m"));
+ return -1;
+ }
+ fd_set_nonblock(fds[0], TRUE);
+ fd_set_nonblock(fds[1], TRUE);
+
+ struct ostream *output = o_stream_create_fd(fds[0], IO_BLOCK_SIZE);
+ struct istream *input =
+ i_stream_create_fd_autoclose(&fds[0], IO_BLOCK_SIZE);
+ o_stream_set_no_error_handling(output, TRUE);
+
+ i_assert(client->io == NULL);
+
+ client_ref(client);
+ client->iostream_fd_proxy =
+ iostream_proxy_create(input, output,
+ client->input, client->output);
+ i_stream_unref(&input);
+ o_stream_unref(&output);
+
+ iostream_proxy_set_completion_callback(client->iostream_fd_proxy,
+ iostream_fd_proxy_finished,
+ client);
+ iostream_proxy_start(client->iostream_fd_proxy);
+
+ *fd_r = fds[1];
+ *close_fd_r = TRUE;
+ return 0;
+}
+
unsigned int clients_get_count(void)
{
return clients_count;
struct istream *input;
struct ostream *output;
struct io *io;
+ struct iostream_proxy *iostream_fd_proxy;
struct timeout *to_auth_waiting;
struct timeout *to_disconnect;
const struct master_service_ssl_settings *ssl_set);
void client_init(struct client *client, void **other_sets);
void client_destroy(struct client *client, const char *reason);
+/* Destroy the client after a successful login. Either the client fd was
+ sent to the post-login process, or the connection will be proxied. */
void client_destroy_success(struct client *client, const char *reason);
void client_ref(struct client *client);
int client_init_ssl(struct client *client);
void client_cmd_starttls(struct client *client);
+int client_get_plaintext_fd(struct client *client, int *fd_r, bool *close_fd_r);
+
unsigned int clients_get_count(void) ATTR_PURE;
void client_add_forward_field(struct client *client, const char *key,
call_client_callback(client, sasl_reply, data, NULL);
}
-static void master_send_request(struct anvil_request *anvil_request)
+static int master_send_request(struct anvil_request *anvil_request)
{
struct client *client = anvil_request->client;
struct master_auth_request_params params;
size_t size;
buffer_t *buf;
const char *session_id = client_get_session_id(client);
+ int fd;
+ bool close_fd;
+
+ if (client_get_plaintext_fd(client, &fd, &close_fd) < 0)
+ return -1;
i_zero(&req);
req.auth_pid = anvil_request->auth_pid;
client->master_auth_id = req.auth_id;
i_zero(¶ms);
- params.client_fd = client->fd;
+ params.client_fd = fd;
params.socket_path = client->postlogin_socket_path;
params.request = req;
params.data = buf->data;
master_auth_request_full(master_auth, ¶ms, master_auth_callback,
client, &client->master_tag);
+ if (close_fd)
+ i_close_fd(&fd);
+ return 0;
}
static void ATTR_NULL(1)
const struct login_settings *set = client->set;
const char *errmsg;
unsigned int conn_count;
+ int ret;
conn_count = 0;
if (reply != NULL && str_to_uint(reply, &conn_count) < 0)
/* reply=NULL if we didn't need to do anvil lookup,
or if the anvil lookup failed. allow failed anvil lookups in. */
- if (reply == NULL || conn_count < set->mail_max_userip_connections)
- master_send_request(req);
- else {
- client->authenticating = FALSE;
- auth_client_send_cancel(auth_client, req->auth_id);
+ if (reply == NULL || conn_count < set->mail_max_userip_connections) {
+ ret = master_send_request(req);
+ errmsg = NULL; /* client will see internal error */
+ } else {
+ ret = -1;
errmsg = t_strdup_printf(ERR_TOO_MANY_USERIP_CONNECTIONS,
set->mail_max_userip_connections);
+ }
+ if (ret < 0) {
+ client->authenticating = FALSE;
+ auth_client_send_cancel(auth_client, req->auth_id);
call_client_callback(client, SASL_SERVER_REPLY_MASTER_FAILED,
errmsg, NULL);
}