Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Incorrect handling of multiline errors #275

Open
pch opened this issue Nov 2, 2024 · 0 comments
Open

Incorrect handling of multiline errors #275

pch opened this issue Nov 2, 2024 · 0 comments

Comments

@pch
Copy link

pch commented Nov 2, 2024

I believe that multiline errors aren't being handled properly. Consider this example:

ErrRecipientDomainMxCheckFailed = &smtp.SMTPError{
	Code:         550,
	EnhancedCode: smtp.EnhancedCode{5, 1, 2},
	Message:      "Recipient domain MX check failed\nthis is another line\nand another one",
}

Returning this error will cause the following exchange:

 ~> RCPT TO:<[email protected]>
<~* 550 5.1.2 Recipient domain MX check failed
 ~> QUIT
<~  this is another line
<~  and another one
<~  221 2.0.0 Bye

This causes incorrect/premature QUIT and breaks client response readers.

Correct response should be:

<~* 550-5.1.2 Recipient domain MX check failed
<~* 550-5.1.2 this is another line
<~* 550 5.1.2 and another one

The issue seems to be here:

func (c *Conn) writeResponse(code int, enhCode EnhancedCode, text ...string) {

This function looks to be handling multiline responses correclty, but it's expecting text to be multiple arguments and it's getting a single multiline string.

A (naive) fix would be something like:

-func (c *Conn) writeResponse(code int, enhCode EnhancedCode, text ...string) {
+func (c *Conn) writeResponse(code int, enhCode EnhancedCode, text string) {
+       lines := strings.Split(text, "\n")
+
        // TODO: error handling
        if c.server.WriteTimeout != 0 {
                c.conn.SetWriteDeadline(time.Now().Add(c.server.WriteTimeout))
@@ -1236,13 +1238,13 @@ func (c *Conn) writeResponse(code int, enhCode EnhancedCode, text ...string) {
                }
        }

-       for i := 0; i < len(text)-1; i++ {
-               c.text.PrintfLine("%d-%v", code, text[i])
+       for i := 0; i < len(lines)-1; i++ {
+               c.text.PrintfLine("%d-%v", code, lines[i])
        }
        if enhCode == NoEnhancedCode {
-               c.text.PrintfLine("%d %v", code, text[len(text)-1])
+               c.text.PrintfLine("%d %v", code, lines[len(text)-1])
        } else {
-               c.text.PrintfLine("%d %v.%v.%v %v", code, enhCode[0], enhCode[1], enhCode[2], text[len(text)-1])
+               c.text.PrintfLine("%d %v.%v.%v %v", code, enhCode[0], enhCode[1], enhCode[2], lines[len(lines)-1])
        }
 }

Use case

The use case is handling gmail responses in a go-smtp server (relay mode). Here's what gmail returns:

550-5.1.1 The email account that you tried to reach does not exist. Please try
550-5.1.1 double-checking the recipient's email address for typos or
550-5.1.1 unnecessary spaces. Learn more at
550 5.1.1  https://support.google.com/mail/?p=NoSuchUser f199si8487887wmf.58 - gsmtp

Currently, it's not possible to return this error back to the client in the original form, because it's being formatted incorrectly.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants