1/*2Maddy Mail Server - Composable all-in-one email server.3Copyright © 2019-2020 Max Mazurov <fox.cpp@disroot.org>, Maddy Mail Server contributors45This program is free software: you can redistribute it and/or modify6it under the terms of the GNU General Public License as published by7the Free Software Foundation, either version 3 of the License, or8(at your option) any later version.910This program is distributed in the hope that it will be useful,11but WITHOUT ANY WARRANTY; without even the implied warranty of12MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the13GNU General Public License for more details.1415You should have received a copy of the GNU General Public License16along with this program. If not, see <https://www.gnu.org/licenses/>.17*/1819package smtp2021import (22 "strings"23 "testing"2425 "github.com/emersion/go-smtp"26 "github.com/foxcpp/go-mockdns"27 "github.com/foxcpp/maddy/framework/exterrors"28 "github.com/foxcpp/maddy/framework/module"29 "github.com/foxcpp/maddy/internal/testutils"30)3132func TestSMTPUTF8_MangleStatusMessage(t *testing.T) {33 tgt := testutils.Target{}34 endp := testEndpoint(t, "smtp", nil, &tgt, []module.Check{35 &testutils.Check{36 ConnRes: module.CheckResult{37 Reason: &exterrors.SMTPError{38 Code: 523,39 Message: "Hey 凱凱",40 },41 Reject: true,42 },43 },44 }, nil)45 endp.deferServerReject = false46 defer endp.Close()47 defer testutils.WaitForConnsClose(t, endp.serv)4849 cl, err := smtp.Dial("127.0.0.1:" + testPort)50 if err != nil {51 t.Fatal(err)52 }53 defer cl.Close()5455 err = cl.Mail("sender@example.org", nil)56 if err == nil {57 t.Fatal("Expected an error, got none")58 }59 smtpErr, ok := err.(*smtp.SMTPError)60 if !ok {61 t.Fatal("Non-SMTPError returned")62 }6364 if smtpErr.Code != 523 {65 t.Fatal("Wrong SMTP code:", smtpErr.Code)66 }67 if !strings.HasPrefix(smtpErr.Message, "Hey ??") {68 t.Fatal("Wrong SMTP message:", smtpErr.Message)69 }70}7172func TestSMTP_RejectNonASCIIFrom(t *testing.T) {73 tgt := testutils.Target{}74 endp := testEndpoint(t, "smtp", nil, &tgt, nil, nil)75 endp.deferServerReject = false76 defer endp.Close()77 defer testutils.WaitForConnsClose(t, endp.serv)7879 cl, err := smtp.Dial("127.0.0.1:" + testPort)80 if err != nil {81 t.Fatal(err)82 }83 defer cl.Close()8485 err = submitMsg(t, cl, "ѣ@example.org", []string{"rcpt@example.com"}, testMsg)8687 smtpErr, ok := err.(*smtp.SMTPError)88 if !ok {89 t.Fatal("Non-SMTPError returned")90 }91 if smtpErr.Code != 550 {92 t.Fatal("Wrong SMTP code:", smtpErr.Code)93 }94 if smtpErr.EnhancedCode != (smtp.EnhancedCode{5, 6, 7}) {95 t.Fatal("Wrong SMTP ench. code:", smtpErr.EnhancedCode)96 }97}9899func TestSMTPUTF8_NormalizeCaseFoldFrom(t *testing.T) {100 tgt := testutils.Target{}101 endp := testEndpoint(t, "smtp", nil, &tgt, nil, nil)102 endp.deferServerReject = false103 defer endp.Close()104 defer testutils.WaitForConnsClose(t, endp.serv)105106 cl, err := smtp.Dial("127.0.0.1:" + testPort)107 if err != nil {108 t.Fatal(err)109 }110 defer cl.Close()111112 err = submitMsgOpts(t, cl, "foo@E\u0301.example.org", []string{"rcpt@example.com"}, &smtp.MailOptions{113 UTF8: true,114 }, testMsg)115 if err != nil {116 t.Fatal(err)117 }118119 if len(tgt.Messages) != 1 {120 t.Fatal("Expected a message, got", len(tgt.Messages))121 }122 msg := tgt.Messages[0]123 testutils.CheckMsgID(t, &msg, "foo@é.example.org", []string{"rcpt@example.com"}, "")124}125126func TestSMTP_RejectNonASCIIRcpt(t *testing.T) {127 tgt := testutils.Target{}128 endp := testEndpoint(t, "smtp", nil, &tgt, nil, nil)129 endp.deferServerReject = false130 defer endp.Close()131 defer testutils.WaitForConnsClose(t, endp.serv)132133 cl, err := smtp.Dial("127.0.0.1:" + testPort)134 if err != nil {135 t.Fatal(err)136 }137 defer cl.Close()138139 err = submitMsg(t, cl, "x@example.org", []string{"ѣ@example.org"}, testMsg)140141 smtpErr, ok := err.(*smtp.SMTPError)142 if !ok {143 t.Fatal("Non-SMTPError returned")144 }145 if smtpErr.Code != 553 {146 t.Fatal("Wrong SMTP code:", smtpErr.Code)147 }148 if smtpErr.EnhancedCode != (smtp.EnhancedCode{5, 6, 7}) {149 t.Fatal("Wrong SMTP ench. code:", smtpErr.EnhancedCode)150 }151}152153func TestSMTPUTF8_NormalizeCaseFoldRcpt(t *testing.T) {154 tgt := testutils.Target{}155 endp := testEndpoint(t, "smtp", nil, &tgt, nil, nil)156 endp.deferServerReject = false157 defer endp.Close()158 defer testutils.WaitForConnsClose(t, endp.serv)159160 cl, err := smtp.Dial("127.0.0.1:" + testPort)161 if err != nil {162 t.Fatal(err)163 }164 defer cl.Close()165166 err = submitMsgOpts(t, cl, "x@example.org", []string{"foo@E\u0301.example.org"}, &smtp.MailOptions{167 UTF8: true,168 }, testMsg)169 if err != nil {170 t.Fatal(err)171 }172173 if len(tgt.Messages) != 1 {174 t.Fatal("Expected a message, got", len(tgt.Messages))175 }176 msg := tgt.Messages[0]177 testutils.CheckMsgID(t, &msg, "x@example.org", []string{"foo@é.example.org"}, "")178}179180func TestSMTPUTF8_NoMangleStatusMessage(t *testing.T) {181 tgt := testutils.Target{}182 endp := testEndpoint(t, "smtp", nil, &tgt, []module.Check{183 &testutils.Check{184 ConnRes: module.CheckResult{185 Reason: &exterrors.SMTPError{186 Code: 523,187 Message: "Hey 凱凱",188 },189 Reject: true,190 },191 },192 }, nil)193 endp.deferServerReject = false194 defer endp.Close()195 defer testutils.WaitForConnsClose(t, endp.serv)196197 cl, err := smtp.Dial("127.0.0.1:" + testPort)198 if err != nil {199 t.Fatal(err)200 }201 defer cl.Close()202203 err = cl.Mail("sender@example.org", &smtp.MailOptions{204 UTF8: true,205 })206 if err == nil {207 t.Fatal("Expected an error, got none")208 }209 smtpErr, ok := err.(*smtp.SMTPError)210 if !ok {211 t.Fatal("Non-SMTPError returned")212 }213214 if smtpErr.Code != 523 {215 t.Fatal("Wrong SMTP code:", smtpErr.Code)216 }217 if !strings.HasPrefix(smtpErr.Message, "Hey 凱凱") {218 t.Fatal("Wrong SMTP message:", smtpErr.Message)219 }220}221222func TestSMTPUTF8_Received_EHLO_ALabel(t *testing.T) {223 tgt := testutils.Target{}224 endp := testEndpoint(t, "smtp", nil, &tgt, nil, nil)225 defer endp.Close()226 defer testutils.WaitForConnsClose(t, endp.serv)227228 cl, err := smtp.Dial("127.0.0.1:" + testPort)229 if err != nil {230 t.Fatal(err)231 }232 defer cl.Close()233234 if err := cl.Hello("凱凱.invalid"); err != nil {235 t.Fatal(err)236 }237238 err = submitMsg(t, cl, "sender@example.org", []string{"rcpt1@example.com", "rcpt2@example.com"}, testMsg)239 if err != nil {240 t.Fatal(err)241 }242243 if len(tgt.Messages) != 1 {244 t.Fatal("Expected a message, got", len(tgt.Messages))245 }246 msg := tgt.Messages[0]247 msgID := testutils.CheckMsgID(t, &msg, "sender@example.org", []string{"rcpt1@example.com", "rcpt2@example.com"}, "")248249 receivedPrefix := `from xn--y9qa.invalid (mx.example.org [127.0.0.1]) by mx.example.com (envelope-sender <sender@example.org>) with ESMTP id ` + msgID250251 if !strings.HasPrefix(msg.Header.Get("Received"), receivedPrefix) {252 t.Error("Wrong Received contents:", msg.Header.Get("Received"))253 }254}255256func TestSMTPUTF8_Received_rDNS_ALabel(t *testing.T) {257 tgt := testutils.Target{}258 endp := testEndpoint(t, "smtp", nil, &tgt, nil, nil)259 defer endp.Close()260 defer testutils.WaitForConnsClose(t, endp.serv)261262 endp.resolver.(*mockdns.Resolver).Zones["1.0.0.127.in-addr.arpa."] = mockdns.Zone{263 PTR: []string{"凱凱.invalid."},264 }265266 cl, err := smtp.Dial("127.0.0.1:" + testPort)267 if err != nil {268 t.Fatal(err)269 }270 defer cl.Close()271272 err = submitMsg(t, cl, "sender@example.org", []string{"rcpt1@example.com", "rcpt2@example.com"}, testMsg)273 if err != nil {274 t.Fatal(err)275 }276277 if len(tgt.Messages) != 1 {278 t.Fatal("Expected a message, got", len(tgt.Messages))279 }280 msg := tgt.Messages[0]281 msgID := testutils.CheckMsgID(t, &msg, "sender@example.org", []string{"rcpt1@example.com", "rcpt2@example.com"}, "")282283 receivedPrefix := `from mx.example.org (xn--y9qa.invalid [127.0.0.1]) by mx.example.com (envelope-sender <sender@example.org>) with ESMTP id ` + msgID284285 if !strings.HasPrefix(msg.Header.Get("Received"), receivedPrefix) {286 t.Error("Wrong Received contents:", msg.Header.Get("Received"))287 }288}289290func TestSMTPUTF8_Received_rDNS_ULabel(t *testing.T) {291 tgt := testutils.Target{}292 endp := testEndpoint(t, "smtp", nil, &tgt, nil, nil)293 defer endp.Close()294 defer testutils.WaitForConnsClose(t, endp.serv)295296 endp.resolver.(*mockdns.Resolver).Zones["1.0.0.127.in-addr.arpa."] = mockdns.Zone{297 PTR: []string{"凱凱.invalid."},298 }299300 cl, err := smtp.Dial("127.0.0.1:" + testPort)301 if err != nil {302 t.Fatal(err)303 }304 defer cl.Close()305306 err = submitMsgOpts(t, cl, "sender@example.org", []string{"rcpt1@example.com", "rcpt2@example.com"}, &smtp.MailOptions{307 UTF8: true,308 }, testMsg)309 if err != nil {310 t.Fatal(err)311 }312313 if len(tgt.Messages) != 1 {314 t.Fatal("Expected a message, got", len(tgt.Messages))315 }316 msg := tgt.Messages[0]317 msgID := testutils.CheckMsgID(t, &msg, "sender@example.org", []string{"rcpt1@example.com", "rcpt2@example.com"}, "")318319 receivedPrefix := `from mx.example.org (凱凱.invalid [127.0.0.1]) by mx.example.com (envelope-sender <sender@example.org>) with UTF8ESMTP id ` + msgID320321 if !strings.HasPrefix(msg.Header.Get("Received"), receivedPrefix) {322 t.Error("Wrong Received contents:", msg.Header.Get("Received"))323 }324}325326func TestSMTPUTF8_Received_EHLO_ULabel(t *testing.T) {327 tgt := testutils.Target{}328 endp := testEndpoint(t, "smtp", nil, &tgt, nil, nil)329 defer endp.Close()330 defer testutils.WaitForConnsClose(t, endp.serv)331332 cl, err := smtp.Dial("127.0.0.1:" + testPort)333 if err != nil {334 t.Fatal(err)335 }336 defer cl.Close()337338 if err := cl.Hello("凱凱.invalid"); err != nil {339 t.Fatal(err)340 }341342 err = submitMsgOpts(t, cl, "sender@example.org", []string{"rcpt@example.com"}, &smtp.MailOptions{343 UTF8: true,344 }, testMsg)345 if err != nil {346 t.Fatal(err)347 }348349 if len(tgt.Messages) != 1 {350 t.Fatal("Expected a message, got", len(tgt.Messages))351 }352 msg := tgt.Messages[0]353 msgID := testutils.CheckMsgID(t, &msg, "sender@example.org", []string{"rcpt@example.com"}, "")354355 // Also, 'with UTF8ESMTP'.356 receivedPrefix := `from 凱凱.invalid (mx.example.org [127.0.0.1]) by mx.example.com (envelope-sender <sender@example.org>) with UTF8ESMTP id ` + msgID357358 if !strings.HasPrefix(msg.Header.Get("Received"), receivedPrefix) {359 t.Error("Wrong Received contents:", msg.Header.Get("Received"))360 }361}