str_append(str, "\r\n");
}
+static bool auth_resp_code_is_tempfail(const char *resp_code)
+{
+ /* Dovecot uses [UNAVAILABLE] for failures that can be retried.
+ Non-retriable failures are [SERVERBUG]. */
+ return strcasecmp(resp_code, IMAP_RESP_CODE_UNAVAILABLE) == 0;
+}
+
int imap_proxy_parse_line(struct client *client, const char *line)
{
struct imap_client *imap_client = (struct imap_client *)client;
const char *log_line = line;
if (strncasecmp(log_line, "NO ", 3) == 0)
log_line += 3;
+ enum login_proxy_failure_type failure_type =
+ LOGIN_PROXY_FAILURE_TYPE_AUTH;
#define STR_NO_IMAP_RESP_CODE_AUTHFAILED "NO ["IMAP_RESP_CODE_AUTHFAILED"]"
if (str_begins(line, STR_NO_IMAP_RESP_CODE_AUTHFAILED)) {
/* the remote sent a generic "authentication failed"
AUTH_FAILED_MSG);
} else if (str_begins(line, "NO [")) {
/* remote sent some other resp-code. forward it. */
- client_send_raw(client, t_strconcat(
- imap_client->cmd_tag, " ", line, "\r\n", NULL));
+ const char *resp_code = t_strcut(line + 4, ']');
+ if (auth_resp_code_is_tempfail(resp_code))
+ failure_type = LOGIN_PROXY_FAILURE_TYPE_AUTH_TEMPFAIL;
+ else {
+ client_send_raw(client, t_strconcat(
+ imap_client->cmd_tag, " ", line, "\r\n", NULL));
+ }
} else {
/* there was no [resp-code], so remote isn't Dovecot
v1.2+. we could either forward the line as-is and
login_proxy_failed(client->login_proxy,
login_proxy_get_event(client->login_proxy),
- LOGIN_PROXY_FAILURE_TYPE_AUTH, log_line);
+ failure_type, log_line);
return -1;
} else if (strncasecmp(line, "* CAPABILITY ", 13) == 0) {
i_free(imap_client->proxy_backend_capability);
static void
imap_proxy_send_failure_reply(struct imap_client *imap_client,
enum login_proxy_failure_type type,
- const char *reason ATTR_UNUSED)
+ const char *reason)
{
switch (type) {
case LOGIN_PROXY_FAILURE_TYPE_CONNECT:
IMAP_RESP_CODE_SERVERBUG,
LOGIN_PROXY_FAILURE_MSG);
break;
+ case LOGIN_PROXY_FAILURE_TYPE_AUTH_TEMPFAIL:
+ client_send_raw(&imap_client->common, t_strconcat(
+ imap_client->cmd_tag, " NO ", reason, "\r\n", NULL));
+ break;
case LOGIN_PROXY_FAILURE_TYPE_AUTH:
/* reply was already sent */
break;
case LOGIN_PROXY_FAILURE_TYPE_PROTOCOL:
break;
case LOGIN_PROXY_FAILURE_TYPE_AUTH:
+ case LOGIN_PROXY_FAILURE_TYPE_AUTH_TEMPFAIL:
client->proxy_auth_failed = TRUE;
break;
}
log_prefix = "";
try_reconnect = FALSE;
break;
+ case LOGIN_PROXY_FAILURE_TYPE_AUTH_TEMPFAIL:
+ log_prefix = "";
+ break;
default:
i_unreached();
}
return TRUE;
}
- if (type != LOGIN_PROXY_FAILURE_TYPE_AUTH)
+ if (type != LOGIN_PROXY_FAILURE_TYPE_AUTH &&
+ type != LOGIN_PROXY_FAILURE_TYPE_AUTH_TEMPFAIL)
e_error(event, "%s%s", log_prefix, reason);
else if (proxy->client->set->auth_verbose)
client_proxy_log_failure(proxy->client, reason);
/* Authentication failed to backend. The LOGIN/AUTH command reply was
already sent to the client. */
LOGIN_PROXY_FAILURE_TYPE_AUTH,
+ /* Authentication failed with a temporary failure code. Attempting it
+ again might work. */
+ LOGIN_PROXY_FAILURE_TYPE_AUTH_TEMPFAIL,
};
struct login_proxy_settings {
So for now we'll just forward the error message. This
shouldn't be a real problem since of course everyone will
be using only Dovecot as their backend :) */
+ enum login_proxy_failure_type failure_type =
+ LOGIN_PROXY_FAILURE_TYPE_AUTH;
if (!str_begins(line, "-ERR ")) {
client_send_reply(client, POP3_CMD_REPLY_ERROR,
AUTH_FAILED_MSG);
+ } else if (str_begins(line, "-ERR [SYS/TEMP]")) {
+ /* delay sending the reply until we know if we reconnect */
+ failure_type = LOGIN_PROXY_FAILURE_TYPE_AUTH_TEMPFAIL;
+ line += 5;
} else {
client_send_raw(client, t_strconcat(line, "\r\n", NULL));
+ line += 5;
}
- if (str_begins(line, "-ERR "))
- line += 5;
login_proxy_failed(client->login_proxy,
login_proxy_get_event(client->login_proxy),
- LOGIN_PROXY_FAILURE_TYPE_AUTH, line);
+ failure_type, line);
return -1;
}
static void
pop3_proxy_send_failure_reply(struct client *client,
enum login_proxy_failure_type type,
- const char *reason ATTR_UNUSED)
+ const char *reason)
{
switch (type) {
case LOGIN_PROXY_FAILURE_TYPE_CONNECT:
client_send_reply(client, POP3_CMD_REPLY_ERROR,
LOGIN_PROXY_FAILURE_MSG);
break;
+ case LOGIN_PROXY_FAILURE_TYPE_AUTH_TEMPFAIL:
+ client_send_reply(client, POP3_CMD_REPLY_ERROR, reason);
+ break;
case LOGIN_PROXY_FAILURE_TYPE_AUTH:
/* reply was already sent */
break;
So for now we'll just forward the error message. This
shouldn't be a real problem since of course everyone will
be using only Dovecot as their backend :) */
- i_assert((status / 100) != 2);
- i_assert(subm_client->proxy_reply != NULL);
- smtp_server_reply_submit(subm_client->proxy_reply);
- subm_client->pending_auth = NULL;
+ enum login_proxy_failure_type failure_type =
+ LOGIN_PROXY_FAILURE_TYPE_AUTH;
+ if ((status / 100) == 4)
+ failure_type = LOGIN_PROXY_FAILURE_TYPE_AUTH_TEMPFAIL;
+ else {
+ i_assert((status / 100) != 2);
+ i_assert(subm_client->proxy_reply != NULL);
+ smtp_server_reply_submit(subm_client->proxy_reply);
+ subm_client->pending_auth = NULL;
+ }
login_proxy_failed(client->login_proxy,
login_proxy_get_event(client->login_proxy),
- LOGIN_PROXY_FAILURE_TYPE_AUTH, text);
+ failure_type, text);
return -1;
}
subm_client->pending_auth = NULL;
smtp_server_reply(cmd, 454, "4.7.0", LOGIN_PROXY_FAILURE_MSG);
break;
+ case LOGIN_PROXY_FAILURE_TYPE_AUTH_TEMPFAIL:
+ i_assert(cmd != NULL);
+ subm_client->pending_auth = NULL;
+
+ i_assert(subm_client->proxy_reply != NULL);
+ smtp_server_reply_submit(subm_client->proxy_reply);
+ break;
case LOGIN_PROXY_FAILURE_TYPE_AUTH:
/* reply was already sent */
i_assert(cmd == NULL);