Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 60bf78d3 authored by cketti's avatar cketti
Browse files

Ignore invalid SMTP EHLO response lines

parent c78d7071
Loading
Loading
Loading
Loading
+14 −9
Original line number Diff line number Diff line
@@ -115,20 +115,25 @@ internal class SmtpResponseParser(

    private fun parseEhloLine(ehloLine: String, keywords: MutableMap<String, List<String>>) {
        val parts = ehloLine.split(" ")

        try {
            val keyword = checkAndNormalizeEhloKeyword(parts[0])
            val parameters = checkEhloParameters(parts)

            if (keywords.containsKey(keyword)) {
            parserError("Same EHLO keyword present in more than one response line")
                parserError("Same EHLO keyword present in more than one response line", logging = false)
            }

            keywords[keyword] = parameters
        } catch (e: SmtpResponseParserException) {
            logger.log(e, "Ignoring EHLO keyword line: $ehloLine")
        }
    }

    private fun checkAndNormalizeEhloKeyword(text: String): String {
        val keyword = text.uppercase()
        if (!keyword[0].isCapitalAlphaDigit() || keyword.any { !it.isCapitalAlphaDigit() && it != DASH }) {
            parserError("EHLO keyword contains invalid character")
            parserError("EHLO keyword contains invalid character", logging = false)
        }

        return keyword
@@ -138,9 +143,9 @@ internal class SmtpResponseParser(
        for (i in 1..parts.lastIndex) {
            val parameter = parts[i]
            if (parameter.isEmpty()) {
                parserError("EHLO parameter must not be empty")
                parserError("EHLO parameter must not be empty", logging = false)
            } else if (parameter.any { it.code !in 33..126 }) {
                parserError("EHLO parameter contains invalid character")
                parserError("EHLO parameter contains invalid character", logging = false)
            }
        }

+40 −37
Original line number Diff line number Diff line
@@ -145,26 +145,36 @@ class SmtpResponseParserTest {
    }

    @Test
    fun `read EHLO response with invalid keyword`() {
    fun `read EHLO response with invalid keywords`() {
        val input = """
            250-smtp.domain.example
            250 KEY:WORD
            250-SIZE 52428800
            250-8BITMIME
            250-PIPELINING
            250-PIPE_CONNECT
            250-AUTH=PLAIN
            250 HELP
            """.toPeekableInputStream()
        val parser = SmtpResponseParser(logger, input)

        assertFailsWithMessage("EHLO keyword contains invalid character") {
            parser.readHelloResponse()
        val response = parser.readHelloResponse()

        assertType<SmtpHelloResponse.Hello>(response) { hello ->
            assertThat(hello.keywords.keys).containsExactly(
                "SIZE",
                "8BITMIME",
                "PIPELINING",
                "HELP"
            )
        }

        assertThat(logger.logEntries).containsExactly(
            LogEntry(
                throwable = null,
                message = """
                    SMTP response data on parser error:
                    250-smtp.domain.example
                    250 KEY:WORD
                """.trimIndent()
        assertThat(logger.logEntries.map { it.message }).containsExactly(
            "Ignoring EHLO keyword line: PIPE_CONNECT",
            "Ignoring EHLO keyword line: AUTH=PLAIN"
        )
        assertThat(logger.logEntries.map { it.throwable?.message }).containsExactly(
            "EHLO keyword contains invalid character",
            "EHLO keyword contains invalid character"
        )
    }

@@ -176,44 +186,37 @@ class SmtpResponseParserTest {
            """.toPeekableInputStream()
        val parser = SmtpResponseParser(logger, input)

        assertFailsWithMessage("EHLO parameter must not be empty") {
            parser.readHelloResponse()
        val response = parser.readHelloResponse()

        assertType<SmtpHelloResponse.Hello>(response) { hello ->
            assertThat(hello.keywords.keys).isEmpty()
        }

        assertThat(logger.logEntries).containsExactly(
            LogEntry(
                throwable = null,
                message = """
                    SMTP response data on parser error:
                    250-smtp.domain.example
                    250 KEYWORD${" "}
                """.trimIndent()
            )
        )
        assertThat(logger.logEntries).hasSize(1)
        assertThat(logger.logEntries.first().throwable).hasMessageThat().isEqualTo("EHLO parameter must not be empty")
        assertThat(logger.logEntries.first().message).isEqualTo("Ignoring EHLO keyword line: KEYWORD ")
    }

    @Test
    fun `read EHLO response with invalid parameter`() {
        val input = """
            250-smtp.domain.example
            250-8BITMIME
            250 KEYWORD para${"\t"}meter
            """.toPeekableInputStream()
        val parser = SmtpResponseParser(logger, input)

        assertFailsWithMessage("EHLO parameter contains invalid character") {
            parser.readHelloResponse()
        val response = parser.readHelloResponse()

        assertType<SmtpHelloResponse.Hello>(response) { hello ->
            assertThat(hello.keywords.keys).containsExactly("8BITMIME")
        }

        assertThat(logger.logEntries).containsExactly(
            LogEntry(
                throwable = null,
                message = """
                    SMTP response data on parser error:
                    250-smtp.domain.example
                    250 KEYWORD para${"\t"}meter
                """.trimIndent()
            )
        )
        assertThat(logger.logEntries).hasSize(1)
        assertThat(logger.logEntries.first().throwable)
            .hasMessageThat().isEqualTo("EHLO parameter contains invalid character")
        assertThat(logger.logEntries.first().message)
            .isEqualTo("Ignoring EHLO keyword line: KEYWORD para${"\t"}meter")
    }

    @Test