]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
lib-smtp: Add support for XCLIENT DESTIP and DESTPORT
authorTimo Sirainen <timo.sirainen@open-xchange.com>
Mon, 2 Feb 2026 11:22:37 +0000 (13:22 +0200)
committeraki.tuomi <aki.tuomi@open-xchange.com>
Fri, 6 Feb 2026 07:58:43 +0000 (07:58 +0000)
src/lib-smtp/smtp-client-connection.c
src/lib-smtp/smtp-common.c
src/lib-smtp/smtp-common.h
src/lib-smtp/smtp-server-cmd-xclient.c
src/lib-smtp/smtp-server-connection.c
src/lib-smtp/smtp-server-reply.c

index e83c670d4b185502364768f62220f9ad6452457f..382a9eb84fb4d038f955803c0a512563964f4019 100644 (file)
@@ -718,6 +718,31 @@ void smtp_client_connection_send_xclient(struct smtp_client_connection *conn)
                                                   "ADDR", addr);
        }
 
+       /* DESTPORT */
+       if (xclient->dest_port != 0 &&
+           str_array_icase_find(xclient_args, "DESTPORT")) {
+               smtp_client_connection_xclient_addf(conn, str, offset,
+                                                   "DESTPORT", "%u",
+                                                   xclient->dest_port);
+       }
+
+       /* DESTADDR */
+       if (xclient->dest_ip.family != 0 &&
+           str_array_icase_find(xclient_args, "DESTADDR")) {
+               const char *addr = net_ip2addr(&xclient->dest_ip);
+
+               /* Older versions of Dovecot LMTP don't quite follow Postfix'
+                  specification of the XCLIENT command regarding IPv6
+                  addresses: the "IPV6:" prefix is omitted. For now, we
+                  maintain this deviation for LMTP. Newer versions of Dovecot
+                  LMTP can work with or without the prefix. */
+               if (conn->protocol != SMTP_PROTOCOL_LMTP &&
+                       xclient->dest_ip.family == AF_INET6)
+                       addr = t_strconcat("IPV6:", addr, NULL);
+               smtp_client_connection_xclient_add(conn, str, offset,
+                                                  "DESTADDR", addr);
+       }
+
        /* final XCLIENT command */
        if (str_len(str) > offset)
                smtp_client_connection_xclient_submit(conn, str_c(str));
index de80dde6a42ad1f324a556f6f594519e107930ef..41e0966fac820b36e0b1715c5590b4cde4b73743 100644 (file)
@@ -79,6 +79,11 @@ void smtp_proxy_data_merge(pool_t pool, struct smtp_proxy_data *dst,
                if (src->source_port != 0)
                        dst->source_port = src->source_port;
        }
+       if (src->dest_ip.family != 0) {
+               dst->dest_ip = src->dest_ip;
+               if (src->dest_port != 0)
+                       dst->dest_port = src->dest_port;
+       }
        if (src->helo != NULL && *src->helo != '\0')
                dst->helo = p_strdup(pool, src->helo);
        if (src->login != NULL && *src->login != '\0')
index dfa7e0cc6c55b9ca46c84d4fa355e82a5b068ab8..d95768ff94b652940c04f2c90346ad71da8f302e 100644 (file)
@@ -98,6 +98,10 @@ struct smtp_proxy_data {
        struct ip_addr source_ip;
        /* PORT */
        in_port_t source_port;
+       /* DESTADDR */
+       struct ip_addr dest_ip;
+       /* DESTPORT */
+       in_port_t dest_port;
        /* HELO, LOGIN */
        const char *helo, *login;
        /* SESSION */
index cbbd77434c02bcbc48f767a0444f84be7847d62a..d4e8b86020d93617fbc6a14b212100bcb741aed2 100644 (file)
@@ -150,6 +150,18 @@ void smtp_server_cmd_xclient(struct smtp_server_cmd_ctx *cmd,
                                        "Invalid ADDR parameter");
                                return;
                        }
+               } else if (strcmp(param.keyword, "DESTADDR") == 0) {
+                       bool ipv6 = FALSE;
+                       if (strcasecmp(param.value, "[UNAVAILABLE]") == 0)
+                               continue;
+                       if (str_begins_icase(param.value, "IPV6:", &param.value))
+                               ipv6 = TRUE;
+                       if (net_addr2ip(param.value, &proxy_data->dest_ip) < 0 ||
+                               (ipv6 && proxy_data->dest_ip.family != AF_INET6)) {
+                               smtp_server_reply(cmd, 501, "5.5.4",
+                                       "Invalid DESTADDR parameter");
+                               return;
+                       }
                } else if (strcmp(param.keyword, "HELO") == 0) {
                        if (strcasecmp(param.value, "[UNAVAILABLE]") == 0)
                                continue;
@@ -169,6 +181,14 @@ void smtp_server_cmd_xclient(struct smtp_server_cmd_ctx *cmd,
                                        "Invalid PORT parameter");
                                return;
                        }
+               } else if (strcmp(param.keyword, "DESTPORT") == 0) {
+                       if (strcasecmp(param.value, "[UNAVAILABLE]") == 0)
+                               continue;
+                       if (net_str2port(param.value, &proxy_data->dest_port) < 0) {
+                               smtp_server_reply(cmd, 501, "5.5.4",
+                                       "Invalid DESTPORT parameter");
+                               return;
+                       }
                } else if (strcmp(param.keyword, "PROTO") == 0) {
                        param.value = t_str_ucase(param.value);
                        if (strcmp(param.value, "SMTP") == 0)
index ed8e3a73cb02ba0b3f19d48e05d492f7416102c7..0d7640a450dff1e408052ebcbee1e27a3564bc9e 100644 (file)
@@ -1679,6 +1679,8 @@ void smtp_server_connection_get_proxy_data(struct smtp_server_connection *conn,
        i_zero(proxy_data);
        proxy_data->source_ip = conn->conn.remote_ip;
        proxy_data->source_port = conn->conn.remote_port;
+       proxy_data->dest_ip = conn->conn.local_ip;
+       proxy_data->dest_port = conn->conn.local_port;
        if (conn->proxy_helo != NULL)
                proxy_data->helo = conn->proxy_helo;
        else if (conn->helo.domain_valid)
index 6edf60eb1982ac41a17b173222b47161600930cd..51d5252f5801bc4c88c794781d6374cc4c3bbf9c 100644 (file)
@@ -867,7 +867,7 @@ void smtp_server_reply_ehlo_add_xclient(struct smtp_server_reply *reply)
 {
        static const char *base_fields =
                "ADDR PORT PROTO HELO LOGIN SESSION CLIENT-TRANSPORT TTL TIMEOUT "
-               "DESTNAME";
+               "DESTNAME DESTADDR DESTPORT";
        struct smtp_server_cmd_ctx *cmd = &reply->command->context;
        struct smtp_server_connection *conn = cmd->conn;