pop3-proxy.c revision 45155bb1250cf5a120278f349465aded513a100f
2ronwalf/* Copyright (c) 2004-2008 Dovecot authors, see the included COPYING file */
2ronwalf
2ronwalf#include "common.h"
2ronwalf#include "ioloop.h"
2ronwalf#include "istream.h"
2ronwalf#include "ostream.h"
2ronwalf#include "base64.h"
2ronwalf#include "safe-memset.h"
38daenzerorama#include "str.h"
2ronwalf#include "str-sanitize.h"
2ronwalf#include "client.h"
2ronwalf#include "pop3-proxy.h"
2ronwalf
2ronwalfstatic void proxy_input(struct istream *input, struct ostream *output,
2ronwalf struct pop3_client *client)
38daenzerorama{
2ronwalf string_t *str;
2ronwalf const char *line, *msg;
2ronwalf
2ronwalf if (input == NULL) {
2ronwalf if (client->io != NULL) {
2ronwalf /* remote authentication failed, we're just
2ronwalf freeing the proxy */
2ronwalf return;
2ronwalf }
2ronwalf
2ronwalf if (client->destroyed) {
2ronwalf /* we came here from client_destroy() */
2ronwalf return;
2ronwalf }
2ronwalf
2ronwalf /* failed for some reason, probably server disconnected */
2ronwalf client_send_line(client,
2ronwalf "-ERR [IN-USE] Temporary login failure.");
2ronwalf client_destroy(client, NULL);
2ronwalf return;
2ronwalf }
2ronwalf
2ronwalf i_assert(!client->destroyed);
2ronwalf
2ronwalf switch (i_stream_read(input)) {
2ronwalf case -2:
2ronwalf /* buffer full */
2ronwalf client_syslog(&client->common,
2ronwalf "proxy: Remote input buffer full");
2ronwalf client_destroy_internal_failure(client);
2ronwalf return;
2ronwalf case -1:
2ronwalf /* disconnected */
2ronwalf client_destroy(client, "Proxy: Remote disconnected");
2ronwalf return;
2ronwalf }
2ronwalf
2ronwalf line = i_stream_next_line(input);
2ronwalf if (line == NULL)
2ronwalf return;
2ronwalf
2ronwalf switch (client->proxy_state) {
2ronwalf case 0:
2ronwalf /* this is a banner */
2ronwalf if (strncmp(line, "+OK", 3) != 0) {
2ronwalf client_syslog(&client->common, t_strdup_printf(
2ronwalf "proxy: Remote returned invalid banner: %s",
2ronwalf str_sanitize(line, 160)));
2ronwalf client_destroy_internal_failure(client);
2ronwalf return;
2ronwalf }
2ronwalf
2ronwalf /* send USER command */
2ronwalf str = t_str_new(128);
2ronwalf str_append(str, "USER ");
2ronwalf str_append(str, client->proxy_user);
2ronwalf str_append(str, "\r\n");
2ronwalf (void)o_stream_send(output, str_data(str), str_len(str));
2ronwalf
2ronwalf client->proxy_state++;
2ronwalf return;
2ronwalf case 1:
2ronwalf if (strncmp(line, "+OK", 3) != 0)
2ronwalf break;
2ronwalf
2ronwalf /* USER successful, send PASS */
2ronwalf str = t_str_new(128);
2ronwalf str_append(str, "PASS ");
2ronwalf str_append(str, client->proxy_password);
2ronwalf str_append(str, "\r\n");
2ronwalf (void)o_stream_send(output, str_data(str),
2ronwalf str_len(str));
2ronwalf
2ronwalf safe_memset(client->proxy_password, 0,
2ronwalf strlen(client->proxy_password));
2ronwalf i_free(client->proxy_password);
2ronwalf client->proxy_password = NULL;
2ronwalf
2ronwalf client->proxy_state++;
2ronwalf return;
2ronwalf case 2:
2ronwalf if (strncmp(line, "+OK", 3) != 0)
2ronwalf break;
2ronwalf
2ronwalf /* Login successful. Send this line to client. */
2ronwalf (void)o_stream_send_str(client->output, line);
2ronwalf (void)o_stream_send(client->output, "\r\n", 2);
2ronwalf
2ronwalf msg = t_strdup_printf("proxy(%s): started proxying to %s:%u",
2ronwalf client->common.virtual_user,
2ronwalf login_proxy_get_host(client->proxy),
2ronwalf login_proxy_get_port(client->proxy));
2ronwalf
2ronwalf login_proxy_detach(client->proxy, client->input,
2ronwalf client->output);
2ronwalf
2ronwalf client->proxy = NULL;
2ronwalf client->input = NULL;
2ronwalf client->output = NULL;
2ronwalf client->common.fd = -1;
2ronwalf client_destroy(client, msg);
2ronwalf return;
2ronwalf }
2ronwalf
2ronwalf /* Login failed. Send our own failure reply so client can't
2ronwalf figure out if user exists or not just by looking at the
2ronwalf reply string. */
2ronwalf client_send_line(client, "-ERR "AUTH_FAILED_MSG);
2ronwalf
2ronwalf /* allow client input again */
2ronwalf i_assert(client->io == NULL);
2ronwalf client->io = io_add(client->common.fd, IO_READ,
2ronwalf client_input, client);
2ronwalf
2ronwalf login_proxy_free(client->proxy);
2ronwalf client->proxy = NULL;
2ronwalf
2ronwalf if (client->proxy_password != NULL) {
2ronwalf safe_memset(client->proxy_password, 0,
2ronwalf strlen(client->proxy_password));
2ronwalf i_free(client->proxy_password);
2ronwalf client->proxy_password = NULL;
2ronwalf }
2ronwalf
2ronwalf i_free(client->proxy_user);
2ronwalf client->proxy_user = NULL;
2ronwalf}
2ronwalf
2ronwalfint pop3_proxy_new(struct pop3_client *client, const char *host,
2ronwalf unsigned int port, const char *user, const char *password)
2ronwalf{
2ronwalf i_assert(user != NULL);
2ronwalf i_assert(!client->destroyed);
2ronwalf
2ronwalf if (password == NULL) {
2ronwalf client_syslog(&client->common, "proxy: password not given");
2ronwalf return -1;
2ronwalf }
2ronwalf
2ronwalf i_assert(client->refcount > 1);
2ronwalf connection_queue_add(1);
2ronwalf
2ronwalf if (client->destroyed) {
2ronwalf /* connection_queue_add() decided that we were the oldest
2ronwalf connection and killed us. */
2ronwalf return -1;
2ronwalf }
38daenzerorama
38daenzerorama client->proxy = login_proxy_new(&client->common, host, port,
38daenzerorama proxy_input, client);
38daenzerorama if (client->proxy == NULL)
38daenzerorama return -1;
38daenzerorama
38daenzerorama client->proxy_state = 0;
38daenzerorama client->proxy_user = i_strdup(user);
38daenzerorama client->proxy_password = i_strdup(password);
2ronwalf
/* disable input until authentication is finished */
if (client->io != NULL)
io_remove(&client->io);
return 0;
}