maddy

Fork https://github.com/foxcpp/maddy

git clone git://git.lin.moe/go/maddy.git

  1/*
  2Maddy Mail Server - Composable all-in-one email server.
  3Copyright © 2019-2020 Max Mazurov <fox.cpp@disroot.org>, Maddy Mail Server contributors
  4
  5This program is free software: you can redistribute it and/or modify
  6it under the terms of the GNU General Public License as published by
  7the Free Software Foundation, either version 3 of the License, or
  8(at your option) any later version.
  9
 10This program is distributed in the hope that it will be useful,
 11but WITHOUT ANY WARRANTY; without even the implied warranty of
 12MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 13GNU General Public License for more details.
 14
 15You should have received a copy of the GNU General Public License
 16along with this program.  If not, see <https://www.gnu.org/licenses/>.
 17*/
 18
 19package smtp
 20
 21import (
 22	"strings"
 23	"testing"
 24
 25	"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)
 31
 32func 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 = false
 46	defer endp.Close()
 47	defer testutils.WaitForConnsClose(t, endp.serv)
 48
 49	cl, err := smtp.Dial("127.0.0.1:" + testPort)
 50	if err != nil {
 51		t.Fatal(err)
 52	}
 53	defer cl.Close()
 54
 55	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	}
 63
 64	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}
 71
 72func TestSMTP_RejectNonASCIIFrom(t *testing.T) {
 73	tgt := testutils.Target{}
 74	endp := testEndpoint(t, "smtp", nil, &tgt, nil, nil)
 75	endp.deferServerReject = false
 76	defer endp.Close()
 77	defer testutils.WaitForConnsClose(t, endp.serv)
 78
 79	cl, err := smtp.Dial("127.0.0.1:" + testPort)
 80	if err != nil {
 81		t.Fatal(err)
 82	}
 83	defer cl.Close()
 84
 85	err = submitMsg(t, cl, "ѣ@example.org", []string{"rcpt@example.com"}, testMsg)
 86
 87	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}
 98
 99func TestSMTPUTF8_NormalizeCaseFoldFrom(t *testing.T) {
100	tgt := testutils.Target{}
101	endp := testEndpoint(t, "smtp", nil, &tgt, nil, nil)
102	endp.deferServerReject = false
103	defer endp.Close()
104	defer testutils.WaitForConnsClose(t, endp.serv)
105
106	cl, err := smtp.Dial("127.0.0.1:" + testPort)
107	if err != nil {
108		t.Fatal(err)
109	}
110	defer cl.Close()
111
112	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	}
118
119	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}
125
126func TestSMTP_RejectNonASCIIRcpt(t *testing.T) {
127	tgt := testutils.Target{}
128	endp := testEndpoint(t, "smtp", nil, &tgt, nil, nil)
129	endp.deferServerReject = false
130	defer endp.Close()
131	defer testutils.WaitForConnsClose(t, endp.serv)
132
133	cl, err := smtp.Dial("127.0.0.1:" + testPort)
134	if err != nil {
135		t.Fatal(err)
136	}
137	defer cl.Close()
138
139	err = submitMsg(t, cl, "x@example.org", []string{"ѣ@example.org"}, testMsg)
140
141	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}
152
153func TestSMTPUTF8_NormalizeCaseFoldRcpt(t *testing.T) {
154	tgt := testutils.Target{}
155	endp := testEndpoint(t, "smtp", nil, &tgt, nil, nil)
156	endp.deferServerReject = false
157	defer endp.Close()
158	defer testutils.WaitForConnsClose(t, endp.serv)
159
160	cl, err := smtp.Dial("127.0.0.1:" + testPort)
161	if err != nil {
162		t.Fatal(err)
163	}
164	defer cl.Close()
165
166	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	}
172
173	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}
179
180func 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 = false
194	defer endp.Close()
195	defer testutils.WaitForConnsClose(t, endp.serv)
196
197	cl, err := smtp.Dial("127.0.0.1:" + testPort)
198	if err != nil {
199		t.Fatal(err)
200	}
201	defer cl.Close()
202
203	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	}
213
214	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}
221
222func 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)
227
228	cl, err := smtp.Dial("127.0.0.1:" + testPort)
229	if err != nil {
230		t.Fatal(err)
231	}
232	defer cl.Close()
233
234	if err := cl.Hello("凱凱.invalid"); err != nil {
235		t.Fatal(err)
236	}
237
238	err = submitMsg(t, cl, "sender@example.org", []string{"rcpt1@example.com", "rcpt2@example.com"}, testMsg)
239	if err != nil {
240		t.Fatal(err)
241	}
242
243	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"}, "")
248
249	receivedPrefix := `from xn--y9qa.invalid (mx.example.org [127.0.0.1]) by mx.example.com (envelope-sender <sender@example.org>) with ESMTP id ` + msgID
250
251	if !strings.HasPrefix(msg.Header.Get("Received"), receivedPrefix) {
252		t.Error("Wrong Received contents:", msg.Header.Get("Received"))
253	}
254}
255
256func 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)
261
262	endp.resolver.(*mockdns.Resolver).Zones["1.0.0.127.in-addr.arpa."] = mockdns.Zone{
263		PTR: []string{"凱凱.invalid."},
264	}
265
266	cl, err := smtp.Dial("127.0.0.1:" + testPort)
267	if err != nil {
268		t.Fatal(err)
269	}
270	defer cl.Close()
271
272	err = submitMsg(t, cl, "sender@example.org", []string{"rcpt1@example.com", "rcpt2@example.com"}, testMsg)
273	if err != nil {
274		t.Fatal(err)
275	}
276
277	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"}, "")
282
283	receivedPrefix := `from mx.example.org (xn--y9qa.invalid [127.0.0.1]) by mx.example.com (envelope-sender <sender@example.org>) with ESMTP id ` + msgID
284
285	if !strings.HasPrefix(msg.Header.Get("Received"), receivedPrefix) {
286		t.Error("Wrong Received contents:", msg.Header.Get("Received"))
287	}
288}
289
290func 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)
295
296	endp.resolver.(*mockdns.Resolver).Zones["1.0.0.127.in-addr.arpa."] = mockdns.Zone{
297		PTR: []string{"凱凱.invalid."},
298	}
299
300	cl, err := smtp.Dial("127.0.0.1:" + testPort)
301	if err != nil {
302		t.Fatal(err)
303	}
304	defer cl.Close()
305
306	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	}
312
313	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"}, "")
318
319	receivedPrefix := `from mx.example.org (凱凱.invalid [127.0.0.1]) by mx.example.com (envelope-sender <sender@example.org>) with UTF8ESMTP id ` + msgID
320
321	if !strings.HasPrefix(msg.Header.Get("Received"), receivedPrefix) {
322		t.Error("Wrong Received contents:", msg.Header.Get("Received"))
323	}
324}
325
326func 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)
331
332	cl, err := smtp.Dial("127.0.0.1:" + testPort)
333	if err != nil {
334		t.Fatal(err)
335	}
336	defer cl.Close()
337
338	if err := cl.Hello("凱凱.invalid"); err != nil {
339		t.Fatal(err)
340	}
341
342	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	}
348
349	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"}, "")
354
355	// 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 ` + msgID
357
358	if !strings.HasPrefix(msg.Header.Get("Received"), receivedPrefix) {
359		t.Error("Wrong Received contents:", msg.Header.Get("Received"))
360	}
361}