This is an automated email from the ASF dual-hosted git repository.

quantranhong1999 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/james-project.git

commit 68384c181a08c5f1ce4dbd82d748eb7acfacf904
Author: Quan Tran <[email protected]>
AuthorDate: Fri Jul 24 16:49:13 2026 +0700

    JAMES-4215 Make SMTP maximum line length configurable
    
    Kerberos GSSAPI SASL tokens can exceed SMTP's existing 8,192-byte line 
limit.
    
    Allow administrators to increase the SMTP command and SASL continuation 
line limit while preserving the existing default. Restrict configured values to 
between 1,000 bytes and 1 MiB to limit memory exposure.
---
 docs/modules/servers/partials/configure/smtp.adoc  |  5 ++++
 .../apache/james/smtpserver/netty/SMTPServer.java  | 10 +++++++-
 .../apache/james/smtpserver/SMTPServerTest.java    | 27 ++++++++++++++++++++++
 .../james/smtpserver/SMTPTestConfiguration.java    |  8 +++++++
 4 files changed, 49 insertions(+), 1 deletion(-)

diff --git a/docs/modules/servers/partials/configure/smtp.adoc 
b/docs/modules/servers/partials/configure/smtp.adoc
index eb687975e2..92721f56fc 100644
--- a/docs/modules/servers/partials/configure/smtp.adoc
+++ b/docs/modules/servers/partials/configure/smtp.adoc
@@ -51,6 +51,11 @@ The last character of the value may be a unit.
 Supported units are `B` for bytes, `K` for kibibyte (1,024 bytes), `M` for 
mebibyte (1,024 kibibytes), and `G` for gibibyte (1,024 mebibytes).
 If no unit is given, the value is interpreted as kibibytes.
 
+| `maxLineLength`
+| Sets the maximum SMTP command or SASL continuation line length in bytes.
+It is an optional integer that defaults to 8,192 and must be between 1,000 and 
1,048,576.
+Increase it only when required by measured SASL token sizes, as the limit 
applies to every SMTP input line and larger values increase memory exposure.
+
 | `heloEhloEnforcement`
 | Configures whether to enforce the use of the HELO / EHLO salutation before a 
MAIL command is accepted.
 It is an optional boolean that defaults to `true`.
diff --git 
a/server/protocols/protocols-smtp/src/main/java/org/apache/james/smtpserver/netty/SMTPServer.java
 
b/server/protocols/protocols-smtp/src/main/java/org/apache/james/smtpserver/netty/SMTPServer.java
index b60abaa58c..0fb5efb631 100644
--- 
a/server/protocols/protocols-smtp/src/main/java/org/apache/james/smtpserver/netty/SMTPServer.java
+++ 
b/server/protocols/protocols-smtp/src/main/java/org/apache/james/smtpserver/netty/SMTPServer.java
@@ -77,6 +77,8 @@ import io.netty.util.concurrent.GlobalEventExecutor;
  * NIO SMTPServer which use Netty
  */
 public class SMTPServer extends AbstractProtocolAsyncServer implements 
SMTPServerMBean, Disconnector, ConnectionDescriptionSupplier {
+    private static final int MIN_LINE_LENGTH = 1000;
+    private static final int MAX_LINE_LENGTH = 1024 * 1024;
     private static final Logger LOGGER = 
LoggerFactory.getLogger(SMTPServer.class);
     private SMTPProtocol transport;
 
@@ -166,6 +168,7 @@ public class SMTPServer extends AbstractProtocolAsyncServer 
implements SMTPServe
      * 0, means no limit.
      */
     private long maxMessageSize = 0;
+    private int maxLineLength = AbstractChannelPipelineFactory.MAX_LINE_LENGTH;
 
     /**
      * The configuration data to be passed to the handler
@@ -244,6 +247,11 @@ public class SMTPServer extends 
AbstractProtocolAsyncServer implements SMTPServe
                 LOGGER.info("No maximum message size is enforced for this 
server.");
             }
 
+            maxLineLength = configuration.getInt("maxLineLength", 
AbstractChannelPipelineFactory.MAX_LINE_LENGTH);
+            if (maxLineLength < MIN_LINE_LENGTH || maxLineLength > 
MAX_LINE_LENGTH) {
+                throw new ConfigurationException(String.format("maxLineLength 
must be between %d and %d", MIN_LINE_LENGTH, MAX_LINE_LENGTH));
+            }
+
             heloEhloEnforcement = 
configuration.getBoolean("heloEhloEnforcement", true);
 
             // get the smtpGreeting
@@ -399,7 +407,7 @@ public class SMTPServer extends AbstractProtocolAsyncServer 
implements SMTPServe
 
     @Override
     protected ChannelHandlerFactory createFrameHandlerFactory() {
-        return new AllButStartTlsLineChannelHandlerFactory("starttls", 
AbstractChannelPipelineFactory.MAX_LINE_LENGTH);
+        return new AllButStartTlsLineChannelHandlerFactory("starttls", 
maxLineLength);
     }
 
     public AuthenticationAnnounceMode getAuthRequired() {
diff --git 
a/server/protocols/protocols-smtp/src/test/java/org/apache/james/smtpserver/SMTPServerTest.java
 
b/server/protocols/protocols-smtp/src/test/java/org/apache/james/smtpserver/SMTPServerTest.java
index 15943f6863..a62bea29d1 100644
--- 
a/server/protocols/protocols-smtp/src/test/java/org/apache/james/smtpserver/SMTPServerTest.java
+++ 
b/server/protocols/protocols-smtp/src/test/java/org/apache/james/smtpserver/SMTPServerTest.java
@@ -20,6 +20,7 @@ package org.apache.james.smtpserver;
 
 import static java.nio.charset.StandardCharsets.UTF_8;
 import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
 import static org.assertj.core.api.Fail.fail;
 
 import java.io.BufferedReader;
@@ -190,6 +191,32 @@ public class SMTPServerTest {
         smtpProtocol.disconnect();
     }
 
+    @Test
+    void configuredMaxLineLengthShouldAcceptLargerCommand() throws Exception {
+        smtpConfiguration.setMaxLineLength(65536);
+        init(smtpConfiguration);
+
+        SMTPClient smtpProtocol = new SMTPClient();
+        InetSocketAddress bindedAddress = testSystem.getBindedAddress();
+        smtpProtocol.connect(bindedAddress.getAddress().getHostAddress(), 
bindedAddress.getPort());
+
+        smtpProtocol.sendCommand("EHLO " + 
"A".repeat(AbstractChannelPipelineFactory.MAX_LINE_LENGTH * 2));
+
+        assertThat(smtpProtocol.getReplyCode()).isEqualTo(250);
+        smtpProtocol.disconnect();
+    }
+
+    @ParameterizedTest
+    @ValueSource(ints = { 999, 1048577 })
+    void maxLineLengthShouldBeBounded(int maxLineLength) {
+        smtpConfiguration.setMaxLineLength(maxLineLength);
+        smtpConfiguration.init();
+
+        assertThatThrownBy(() -> initSMTPServer(smtpConfiguration))
+            
.isInstanceOf(org.apache.commons.configuration2.ex.ConfigurationException.class)
+            .hasMessage("maxLineLength must be between 1000 and 1048576");
+    }
+
     @Test
     public void testConnectionLimit() throws Exception {
         smtpConfiguration.setConnectionLimit(2);
diff --git 
a/server/protocols/protocols-smtp/src/test/java/org/apache/james/smtpserver/SMTPTestConfiguration.java
 
b/server/protocols/protocols-smtp/src/test/java/org/apache/james/smtpserver/SMTPTestConfiguration.java
index 2aea182b69..5e989bf8c2 100644
--- 
a/server/protocols/protocols-smtp/src/test/java/org/apache/james/smtpserver/SMTPTestConfiguration.java
+++ 
b/server/protocols/protocols-smtp/src/test/java/org/apache/james/smtpserver/SMTPTestConfiguration.java
@@ -29,6 +29,7 @@ import 
org.apache.james.smtpserver.fastfail.ValidSenderDomainHandler;
 public class SMTPTestConfiguration extends BaseHierarchicalConfiguration {
 
     private int maxMessageSizeKB = 0;
+    private Integer maxLineLength;
     private String authorizedAddresses = "127.0.0.0/8";
     private String authorizingMode = "false";
     private boolean verifyIdentity = false;
@@ -54,6 +55,10 @@ public class SMTPTestConfiguration extends 
BaseHierarchicalConfiguration {
         maxMessageSizeKB = kilobytes;
     }
 
+    public void setMaxLineLength(int maxLineLength) {
+        this.maxLineLength = maxLineLength;
+    }
+
     public void setAuthorizedAddresses(String authorizedAddresses) {
         this.authorizedAddresses = authorizedAddresses;
     }
@@ -128,6 +133,9 @@ public class SMTPTestConfiguration extends 
BaseHierarchicalConfiguration {
         addProperty("connectiontimeout", 360000);
         addProperty("authorizedAddresses", authorizedAddresses);
         addProperty("maxmessagesize", maxMessageSizeKB);
+        if (maxLineLength != null) {
+            addProperty("maxLineLength", maxLineLength);
+        }
         addProperty("authRequired", authorizingMode);
         addProperty("heloEhloEnforcement", heloEhloEnforcement);
         addProperty("addressBracketsEnforcement", addressBracketsEnforcement);


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to