conn.go raw
1 package smtp
2
3 import (
4 "crypto/tls"
5 "encoding/base64"
6 "errors"
7 "fmt"
8 "io"
9 "io/ioutil"
10 "net"
11 "net/textproto"
12 "regexp"
13 "runtime/debug"
14 "strconv"
15 "strings"
16 "sync"
17 "time"
18
19 "github.com/emersion/go-sasl"
20 )
21
22 // Number of errors we'll tolerate per connection before closing. Defaults to 3.
23 const errThreshold = 3
24
25 type Conn struct {
26 conn net.Conn
27 text *textproto.Conn
28 server *Server
29 helo string
30
31 // Number of errors witnessed on this connection
32 errCount int
33
34 session Session
35 locker sync.Mutex
36 binarymime bool
37
38 lineLimitReader *lineLimitReader
39 bdatPipe *io.PipeWriter
40 bdatStatus *statusCollector // used for BDAT on LMTP
41 dataResult chan error
42 bytesReceived int64 // counts total size of chunks when BDAT is used
43
44 fromReceived bool
45 recipients []string
46 didAuth bool
47 }
48
49 func newConn(c net.Conn, s *Server) *Conn {
50 sc := &Conn{
51 server: s,
52 conn: c,
53 }
54
55 sc.init()
56 return sc
57 }
58
59 func (c *Conn) init() {
60 c.lineLimitReader = &lineLimitReader{
61 R: c.conn,
62 LineLimit: c.server.MaxLineLength,
63 }
64 rwc := struct {
65 io.Reader
66 io.Writer
67 io.Closer
68 }{
69 Reader: c.lineLimitReader,
70 Writer: c.conn,
71 Closer: c.conn,
72 }
73
74 if c.server.Debug != nil {
75 rwc = struct {
76 io.Reader
77 io.Writer
78 io.Closer
79 }{
80 io.TeeReader(rwc.Reader, c.server.Debug),
81 io.MultiWriter(rwc.Writer, c.server.Debug),
82 rwc.Closer,
83 }
84 }
85
86 c.text = textproto.NewConn(rwc)
87 }
88
89 // Commands are dispatched to the appropriate handler functions.
90 func (c *Conn) handle(cmd string, arg string) {
91 // If panic happens during command handling - send 421 response
92 // and close connection.
93 defer func() {
94 if err := recover(); err != nil {
95 c.writeResponse(421, EnhancedCode{4, 0, 0}, "Internal server error")
96 c.Close()
97
98 stack := debug.Stack()
99 c.server.ErrorLog.Printf("panic serving %v: %v\n%s", c.conn.RemoteAddr(), err, stack)
100 }
101 }()
102
103 if cmd == "" {
104 c.protocolError(500, EnhancedCode{5, 5, 2}, "Error: bad syntax")
105 return
106 }
107
108 cmd = strings.ToUpper(cmd)
109 switch cmd {
110 case "SEND", "SOML", "SAML", "EXPN", "HELP", "TURN":
111 // These commands are not implemented in any state
112 c.writeResponse(502, EnhancedCode{5, 5, 1}, fmt.Sprintf("%v command not implemented", cmd))
113 case "HELO", "EHLO", "LHLO":
114 lmtp := cmd == "LHLO"
115 enhanced := lmtp || cmd == "EHLO"
116 if c.server.LMTP && !lmtp {
117 c.writeResponse(500, EnhancedCode{5, 5, 1}, "This is a LMTP server, use LHLO")
118 return
119 }
120 if !c.server.LMTP && lmtp {
121 c.writeResponse(500, EnhancedCode{5, 5, 1}, "This is not a LMTP server")
122 return
123 }
124 c.handleGreet(enhanced, arg)
125 case "MAIL":
126 c.handleMail(arg)
127 case "RCPT":
128 c.handleRcpt(arg)
129 case "VRFY":
130 c.writeResponse(252, EnhancedCode{2, 5, 0}, "Cannot VRFY user, but will accept message")
131 case "NOOP":
132 c.writeResponse(250, EnhancedCode{2, 0, 0}, "I have successfully done nothing")
133 case "RSET": // Reset session
134 c.reset()
135 c.writeResponse(250, EnhancedCode{2, 0, 0}, "Session reset")
136 case "BDAT":
137 c.handleBdat(arg)
138 case "DATA":
139 c.handleData(arg)
140 case "QUIT":
141 c.writeResponse(221, EnhancedCode{2, 0, 0}, "Bye")
142 c.Close()
143 case "AUTH":
144 c.handleAuth(arg)
145 case "STARTTLS":
146 c.handleStartTLS()
147 default:
148 msg := fmt.Sprintf("Syntax errors, %v command unrecognized", cmd)
149 c.protocolError(500, EnhancedCode{5, 5, 2}, msg)
150 }
151 }
152
153 func (c *Conn) Server() *Server {
154 return c.server
155 }
156
157 func (c *Conn) Session() Session {
158 c.locker.Lock()
159 defer c.locker.Unlock()
160 return c.session
161 }
162
163 func (c *Conn) setSession(session Session) {
164 c.locker.Lock()
165 defer c.locker.Unlock()
166 c.session = session
167 }
168
169 func (c *Conn) Close() error {
170 c.locker.Lock()
171 defer c.locker.Unlock()
172
173 if c.bdatPipe != nil {
174 c.bdatPipe.CloseWithError(ErrDataReset)
175 c.bdatPipe = nil
176 }
177
178 if c.session != nil {
179 c.session.Logout()
180 c.session = nil
181 }
182
183 return c.conn.Close()
184 }
185
186 // TLSConnectionState returns the connection's TLS connection state.
187 // Zero values are returned if the connection doesn't use TLS.
188 func (c *Conn) TLSConnectionState() (state tls.ConnectionState, ok bool) {
189 tc, ok := c.conn.(*tls.Conn)
190 if !ok {
191 return
192 }
193 return tc.ConnectionState(), true
194 }
195
196 func (c *Conn) Hostname() string {
197 return c.helo
198 }
199
200 func (c *Conn) Conn() net.Conn {
201 return c.conn
202 }
203
204 func (c *Conn) authAllowed() bool {
205 _, isTLS := c.TLSConnectionState()
206 return isTLS || c.server.AllowInsecureAuth
207 }
208
209 // protocolError writes errors responses and closes the connection once too many
210 // have occurred.
211 func (c *Conn) protocolError(code int, ec EnhancedCode, msg string) {
212 c.writeResponse(code, ec, msg)
213
214 c.errCount++
215 if c.errCount > errThreshold {
216 c.writeResponse(500, EnhancedCode{5, 5, 1}, "Too many errors. Quiting now")
217 c.Close()
218 }
219 }
220
221 // GREET state -> waiting for HELO
222 func (c *Conn) handleGreet(enhanced bool, arg string) {
223 domain, err := parseHelloArgument(arg)
224 if err != nil {
225 c.writeResponse(501, EnhancedCode{5, 5, 2}, "Domain/address argument required for HELO")
226 return
227 }
228 // c.helo is populated before NewSession so
229 // NewSession can access it via Conn.Hostname.
230 c.helo = domain
231
232 // RFC 5321: "An EHLO command MAY be issued by a client later in the session"
233 if c.session != nil {
234 // RFC 5321: "... the SMTP server MUST clear all buffers
235 // and reset the state exactly as if a RSET command has been issued."
236 c.reset()
237 } else {
238 sess, err := c.server.Backend.NewSession(c)
239 if err != nil {
240 c.helo = ""
241 c.writeError(451, EnhancedCode{4, 0, 0}, err)
242 return
243 }
244
245 c.setSession(sess)
246 }
247
248 if !enhanced {
249 c.writeResponse(250, EnhancedCode{2, 0, 0}, fmt.Sprintf("Hello %s", domain))
250 return
251 }
252
253 caps := []string{
254 "PIPELINING",
255 "8BITMIME",
256 "ENHANCEDSTATUSCODES",
257 "CHUNKING",
258 }
259 if _, isTLS := c.TLSConnectionState(); c.server.TLSConfig != nil && !isTLS {
260 caps = append(caps, "STARTTLS")
261 }
262 if c.authAllowed() {
263 mechs := c.authMechanisms()
264
265 authCap := "AUTH"
266 for _, name := range mechs {
267 authCap += " " + name
268 }
269
270 if len(mechs) > 0 {
271 caps = append(caps, authCap)
272 }
273 }
274 if c.server.EnableSMTPUTF8 {
275 caps = append(caps, "SMTPUTF8")
276 }
277 if _, isTLS := c.TLSConnectionState(); isTLS && c.server.EnableREQUIRETLS {
278 caps = append(caps, "REQUIRETLS")
279 }
280 if c.server.EnableBINARYMIME {
281 caps = append(caps, "BINARYMIME")
282 }
283 if c.server.EnableDSN {
284 caps = append(caps, "DSN")
285 }
286 if c.server.MaxMessageBytes > 0 {
287 caps = append(caps, fmt.Sprintf("SIZE %v", c.server.MaxMessageBytes))
288 } else {
289 caps = append(caps, "SIZE")
290 }
291 if c.server.MaxRecipients > 0 {
292 caps = append(caps, fmt.Sprintf("LIMITS RCPTMAX=%v", c.server.MaxRecipients))
293 }
294 if c.server.EnableRRVS {
295 caps = append(caps, "RRVS")
296 }
297 if c.server.EnableDELIVERBY {
298 if c.server.MinimumDeliverByTime == 0 {
299 caps = append(caps, "DELIVERBY")
300 } else {
301 caps = append(caps, fmt.Sprintf("DELIVERBY %d", int(c.server.MinimumDeliverByTime.Seconds())))
302 }
303 }
304 if c.server.EnableMTPRIORITY {
305 if c.server.MtPriorityProfile == PriorityUnspecified {
306 caps = append(caps, "MT-PRIORITY")
307 } else {
308 caps = append(caps, fmt.Sprintf("MT-PRIORITY %s", c.server.MtPriorityProfile))
309 }
310 }
311
312 args := []string{"Hello " + domain}
313 args = append(args, caps...)
314 c.writeResponse(250, NoEnhancedCode, args...)
315 }
316
317 // READY state -> waiting for MAIL
318 func (c *Conn) handleMail(arg string) {
319 if c.helo == "" {
320 c.writeResponse(502, EnhancedCode{5, 5, 1}, "Please introduce yourself first.")
321 return
322 }
323 if c.bdatPipe != nil {
324 c.writeResponse(502, EnhancedCode{5, 5, 1}, "MAIL not allowed during message transfer")
325 return
326 }
327
328 arg, ok := cutPrefixFold(arg, "FROM:")
329 if !ok {
330 c.writeResponse(501, EnhancedCode{5, 5, 2}, "Was expecting MAIL arg syntax of FROM:<address>")
331 return
332 }
333
334 p := parser{s: strings.TrimSpace(arg)}
335 from, err := p.parseReversePath()
336 if err != nil {
337 c.writeResponse(501, EnhancedCode{5, 5, 2}, "Was expecting MAIL arg syntax of FROM:<address>")
338 return
339 }
340 args, err := parseArgs(p.s)
341 if err != nil {
342 c.writeResponse(501, EnhancedCode{5, 5, 4}, "Unable to parse MAIL ESMTP parameters")
343 return
344 }
345
346 opts := &MailOptions{}
347
348 c.binarymime = false
349 // This is where the Conn may put BODY=8BITMIME, but we already
350 // read the DATA as bytes, so it does not effect our processing.
351 for key, value := range args {
352 switch key {
353 case "SIZE":
354 size, err := strconv.ParseUint(value, 10, 32)
355 if err != nil {
356 c.writeResponse(501, EnhancedCode{5, 5, 4}, "Unable to parse SIZE as an integer")
357 return
358 }
359
360 if c.server.MaxMessageBytes > 0 && int64(size) > c.server.MaxMessageBytes {
361 c.writeResponse(552, EnhancedCode{5, 3, 4}, "Max message size exceeded")
362 return
363 }
364
365 opts.Size = int64(size)
366 case "SMTPUTF8":
367 if !c.server.EnableSMTPUTF8 {
368 c.writeResponse(504, EnhancedCode{5, 5, 4}, "SMTPUTF8 is not implemented")
369 return
370 }
371 opts.UTF8 = true
372 case "REQUIRETLS":
373 if !c.server.EnableREQUIRETLS {
374 c.writeResponse(504, EnhancedCode{5, 5, 4}, "REQUIRETLS is not implemented")
375 return
376 }
377 opts.RequireTLS = true
378 case "BODY":
379 value = strings.ToUpper(value)
380 switch BodyType(value) {
381 case BodyBinaryMIME:
382 if !c.server.EnableBINARYMIME {
383 c.writeResponse(504, EnhancedCode{5, 5, 4}, "BINARYMIME is not implemented")
384 return
385 }
386 c.binarymime = true
387 case Body7Bit, Body8BitMIME:
388 // This space is intentionally left blank
389 default:
390 c.writeResponse(501, EnhancedCode{5, 5, 4}, "Unknown BODY value")
391 return
392 }
393 opts.Body = BodyType(value)
394 case "RET":
395 if !c.server.EnableDSN {
396 c.writeResponse(504, EnhancedCode{5, 5, 4}, "RET is not implemented")
397 return
398 }
399 value = strings.ToUpper(value)
400 switch DSNReturn(value) {
401 case DSNReturnFull, DSNReturnHeaders:
402 // This space is intentionally left blank
403 default:
404 c.writeResponse(501, EnhancedCode{5, 5, 4}, "Unknown RET value")
405 return
406 }
407 opts.Return = DSNReturn(value)
408 case "ENVID":
409 if !c.server.EnableDSN {
410 c.writeResponse(504, EnhancedCode{5, 5, 4}, "ENVID is not implemented")
411 return
412 }
413 value, err := decodeXtext(value)
414 if err != nil || value == "" || !isPrintableASCII(value) {
415 c.writeResponse(501, EnhancedCode{5, 5, 4}, "Malformed ENVID parameter value")
416 return
417 }
418 opts.EnvelopeID = value
419 case "AUTH":
420 value, err := decodeXtext(value)
421 if err != nil || value == "" {
422 c.writeResponse(500, EnhancedCode{5, 5, 4}, "Malformed AUTH parameter value")
423 return
424 }
425 if value == "<>" {
426 value = ""
427 } else {
428 p := parser{s: value}
429 value, err = p.parseMailbox()
430 if err != nil || p.s != "" {
431 c.writeResponse(500, EnhancedCode{5, 5, 4}, "Malformed AUTH parameter mailbox")
432 return
433 }
434 }
435 opts.Auth = &value
436 default:
437 c.writeResponse(500, EnhancedCode{5, 5, 4}, "Unknown MAIL FROM argument")
438 return
439 }
440 }
441
442 if err := c.Session().Mail(from, opts); err != nil {
443 c.writeError(451, EnhancedCode{4, 0, 0}, err)
444 return
445 }
446
447 c.writeResponse(250, EnhancedCode{2, 0, 0}, fmt.Sprintf("Roger, accepting mail from <%v>", from))
448 c.fromReceived = true
449 }
450
451 // This regexp matches 'hexchar' token defined in
452 // https://tools.ietf.org/html/rfc4954#section-8 however it is intentionally
453 // relaxed by requiring only '+' to be present. It allows us to detect
454 // malformed values such as +A or +HH and report them appropriately.
455 var hexcharRe = regexp.MustCompile(`\+[0-9A-F]?[0-9A-F]?`)
456
457 func decodeXtext(val string) (string, error) {
458 if !strings.Contains(val, "+") {
459 return val, nil
460 }
461
462 var replaceErr error
463 decoded := hexcharRe.ReplaceAllStringFunc(val, func(match string) string {
464 if len(match) != 3 {
465 replaceErr = errors.New("incomplete hexchar")
466 return ""
467 }
468 char, err := strconv.ParseInt(match, 16, 8)
469 if err != nil {
470 replaceErr = err
471 return ""
472 }
473
474 return string(rune(char))
475 })
476 if replaceErr != nil {
477 return "", replaceErr
478 }
479
480 return decoded, nil
481 }
482
483 // This regexp matches 'EmbeddedUnicodeChar' token defined in
484 // https://datatracker.ietf.org/doc/html/rfc6533.html#section-3
485 // however it is intentionally relaxed by requiring only '\x{HEX}' to be
486 // present. It also matches disallowed characters in QCHAR and QUCHAR defined
487 // in above.
488 // So it allows us to detect malformed values and report them appropriately.
489 var eUOrDCharRe = regexp.MustCompile(`\\x[{][0-9A-F]+[}]|[[:cntrl:] \\+=]`)
490
491 // Decodes the utf-8-addr-xtext or the utf-8-addr-unitext form.
492 func decodeUTF8AddrXtext(val string) (string, error) {
493 var replaceErr error
494 decoded := eUOrDCharRe.ReplaceAllStringFunc(val, func(match string) string {
495 if len(match) == 1 {
496 replaceErr = errors.New("disallowed character:" + match)
497 return ""
498 }
499
500 hexpoint := match[3 : len(match)-1]
501 char, err := strconv.ParseUint(hexpoint, 16, 21)
502 if err != nil {
503 replaceErr = err
504 return ""
505 }
506 switch len(hexpoint) {
507 case 2:
508 switch {
509 // all xtext-specials
510 case 0x01 <= char && char <= 0x09 ||
511 0x11 <= char && char <= 0x19 ||
512 char == 0x10 || char == 0x20 ||
513 char == 0x2B || char == 0x3D || char == 0x7F:
514 // 2-digit forms
515 case char == 0x5C || 0x80 <= char && char <= 0xFF:
516 // This space is intentionally left blank
517 default:
518 replaceErr = errors.New("illegal hexpoint:" + hexpoint)
519 return ""
520 }
521 // 3-digit forms
522 case 3:
523 switch {
524 case 0x100 <= char && char <= 0xFFF:
525 // This space is intentionally left blank
526 default:
527 replaceErr = errors.New("illegal hexpoint:" + hexpoint)
528 return ""
529 }
530 // 4-digit forms excluding surrogate
531 case 4:
532 switch {
533 case 0x1000 <= char && char <= 0xD7FF:
534 case 0xE000 <= char && char <= 0xFFFF:
535 // This space is intentionally left blank
536 default:
537 replaceErr = errors.New("illegal hexpoint:" + hexpoint)
538 return ""
539 }
540 // 5-digit forms
541 case 5:
542 switch {
543 case 0x1_0000 <= char && char <= 0xF_FFFF:
544 // This space is intentionally left blank
545 default:
546 replaceErr = errors.New("illegal hexpoint:" + hexpoint)
547 return ""
548 }
549 // 6-digit forms
550 case 6:
551 switch {
552 case 0x10_0000 <= char && char <= 0x10_FFFF:
553 // This space is intentionally left blank
554 default:
555 replaceErr = errors.New("illegal hexpoint:" + hexpoint)
556 return ""
557 }
558 // the other invalid forms
559 default:
560 replaceErr = errors.New("illegal hexpoint:" + hexpoint)
561 return ""
562 }
563
564 return string(rune(char))
565 })
566 if replaceErr != nil {
567 return "", replaceErr
568 }
569
570 return decoded, nil
571 }
572
573 func decodeTypedAddress(val string) (DSNAddressType, string, error) {
574 tv := strings.SplitN(val, ";", 2)
575 if len(tv) != 2 || tv[0] == "" || tv[1] == "" {
576 return "", "", errors.New("bad address")
577 }
578 aType, aAddr := strings.ToUpper(tv[0]), tv[1]
579
580 var err error
581 switch DSNAddressType(aType) {
582 case DSNAddressTypeRFC822:
583 aAddr, err = decodeXtext(aAddr)
584 if err == nil && !isPrintableASCII(aAddr) {
585 err = errors.New("illegal address:" + aAddr)
586 }
587 case DSNAddressTypeUTF8:
588 aAddr, err = decodeUTF8AddrXtext(aAddr)
589 default:
590 err = errors.New("unknown address type:" + aType)
591 }
592 if err != nil {
593 return "", "", err
594 }
595
596 return DSNAddressType(aType), aAddr, nil
597 }
598
599 func encodeXtext(raw string) string {
600 var out strings.Builder
601 out.Grow(len(raw))
602
603 for _, ch := range raw {
604 switch {
605 case ch >= '!' && ch <= '~' && ch != '+' && ch != '=':
606 // printable non-space US-ASCII except '+' and '='
607 out.WriteRune(ch)
608 default:
609 out.WriteRune('+')
610 out.WriteString(strings.ToUpper(strconv.FormatInt(int64(ch), 16)))
611 }
612 }
613 return out.String()
614 }
615
616 // Encodes raw string to the utf-8-addr-xtext form in RFC 6533.
617 func encodeUTF8AddrXtext(raw string) string {
618 var out strings.Builder
619 out.Grow(len(raw))
620
621 for _, ch := range raw {
622 switch {
623 case ch >= '!' && ch <= '~' && ch != '+' && ch != '=':
624 // printable non-space US-ASCII except '+' and '='
625 out.WriteRune(ch)
626 default:
627 out.WriteRune('\\')
628 out.WriteRune('x')
629 out.WriteRune('{')
630 out.WriteString(strings.ToUpper(strconv.FormatInt(int64(ch), 16)))
631 out.WriteRune('}')
632 }
633 }
634 return out.String()
635 }
636
637 // Encodes raw string to the utf-8-addr-unitext form in RFC 6533.
638 func encodeUTF8AddrUnitext(raw string) string {
639 var out strings.Builder
640 out.Grow(len(raw))
641
642 for _, ch := range raw {
643 switch {
644 case ch >= '!' && ch <= '~' && ch != '+' && ch != '=':
645 // printable non-space US-ASCII except '+' and '='
646 out.WriteRune(ch)
647 case ch <= '\x7F':
648 // other ASCII: CTLs, space and specials
649 out.WriteRune('\\')
650 out.WriteRune('x')
651 out.WriteRune('{')
652 out.WriteString(strings.ToUpper(strconv.FormatInt(int64(ch), 16)))
653 out.WriteRune('}')
654 default:
655 // UTF-8 non-ASCII
656 out.WriteRune(ch)
657 }
658 }
659 return out.String()
660 }
661
662 func isPrintableASCII(val string) bool {
663 for _, ch := range val {
664 if ch < ' ' || '~' < ch {
665 return false
666 }
667 }
668 return true
669 }
670
671 // MAIL state -> waiting for RCPTs followed by DATA
672 func (c *Conn) handleRcpt(arg string) {
673 if !c.fromReceived {
674 c.writeResponse(502, EnhancedCode{5, 5, 1}, "Missing MAIL FROM command.")
675 return
676 }
677 if c.bdatPipe != nil {
678 c.writeResponse(502, EnhancedCode{5, 5, 1}, "RCPT not allowed during message transfer")
679 return
680 }
681
682 arg, ok := cutPrefixFold(arg, "TO:")
683 if !ok {
684 c.writeResponse(501, EnhancedCode{5, 5, 2}, "Was expecting RCPT arg syntax of TO:<address>")
685 return
686 }
687
688 p := parser{s: strings.TrimSpace(arg)}
689 recipient, err := p.parsePath()
690 if err != nil {
691 c.writeResponse(501, EnhancedCode{5, 5, 2}, "Was expecting RCPT arg syntax of TO:<address>")
692 return
693 }
694
695 if c.server.MaxRecipients > 0 && len(c.recipients) >= c.server.MaxRecipients {
696 c.writeResponse(452, EnhancedCode{4, 5, 3}, fmt.Sprintf("Maximum limit of %v recipients reached", c.server.MaxRecipients))
697 return
698 }
699
700 args, err := parseArgs(p.s)
701 if err != nil {
702 c.writeResponse(501, EnhancedCode{5, 5, 4}, "Unable to parse RCPT ESMTP parameters")
703 return
704 }
705
706 opts := &RcptOptions{}
707
708 for key, value := range args {
709 switch key {
710 case "NOTIFY":
711 if !c.server.EnableDSN {
712 c.writeResponse(504, EnhancedCode{5, 5, 4}, "NOTIFY is not implemented")
713 return
714 }
715 notify := []DSNNotify{}
716 for _, val := range strings.Split(value, ",") {
717 notify = append(notify, DSNNotify(strings.ToUpper(val)))
718 }
719 if err := checkNotifySet(notify); err != nil {
720 c.writeResponse(501, EnhancedCode{5, 5, 4}, "Malformed NOTIFY parameter value")
721 return
722 }
723 opts.Notify = notify
724 case "ORCPT":
725 if !c.server.EnableDSN {
726 c.writeResponse(504, EnhancedCode{5, 5, 4}, "ORCPT is not implemented")
727 return
728 }
729 aType, aAddr, err := decodeTypedAddress(value)
730 if err != nil || aAddr == "" {
731 c.writeResponse(501, EnhancedCode{5, 5, 4}, "Malformed ORCPT parameter value")
732 return
733 }
734 opts.OriginalRecipientType = aType
735 opts.OriginalRecipient = aAddr
736 case "RRVS":
737 if !c.server.EnableRRVS {
738 c.writeResponse(504, EnhancedCode{5, 5, 4}, "RRVS is not implemented")
739 return
740 }
741 value, _, _ = strings.Cut(value, ";") // discard the no-support action
742 rrvsTime, err := time.Parse(time.RFC3339, value)
743 if err != nil {
744 c.writeResponse(501, EnhancedCode{5, 5, 4}, "Malformed RRVS parameter value")
745 return
746 }
747 opts.RequireRecipientValidSince = rrvsTime
748 case "BY":
749 if !c.server.EnableDELIVERBY {
750 c.writeResponse(504, EnhancedCode{5, 5, 4}, "DELIVERBY is not implemented")
751 return
752 }
753 deliverBy := parseDeliverByArgument(value)
754 if deliverBy == nil {
755 c.writeResponse(501, EnhancedCode{5, 5, 4}, "Malformed BY parameter value")
756 return
757 }
758 if c.server.MinimumDeliverByTime != 0 &&
759 deliverBy.Mode == DeliverByReturn &&
760 deliverBy.Time < c.server.MinimumDeliverByTime {
761 c.writeResponse(501, EnhancedCode{5, 5, 4}, "BY parameter is below server minimum")
762 return
763 }
764 opts.DeliverBy = deliverBy
765 case "MT-PRIORITY":
766 if !c.server.EnableMTPRIORITY {
767 c.writeResponse(504, EnhancedCode{5, 5, 4}, "MT-PRIORITY is not implemented")
768 return
769 }
770 mtPriority, err := strconv.Atoi(value)
771 if err != nil {
772 c.writeResponse(501, EnhancedCode{5, 5, 4}, "Malformed MT-PRIORITY parameter value")
773 return
774 }
775 if mtPriority < -9 || mtPriority > 9 {
776 c.writeResponse(501, EnhancedCode{5, 5, 4}, "MT-PRIORITY is outside valid range")
777 return
778 }
779 opts.MTPriority = &mtPriority
780 default:
781 c.writeResponse(500, EnhancedCode{5, 5, 4}, "Unknown RCPT TO argument")
782 return
783 }
784 }
785
786 if err := c.Session().Rcpt(recipient, opts); err != nil {
787 c.writeError(451, EnhancedCode{4, 0, 0}, err)
788 return
789 }
790 c.recipients = append(c.recipients, recipient)
791 c.writeResponse(250, EnhancedCode{2, 0, 0}, fmt.Sprintf("I'll make sure <%v> gets this", recipient))
792 }
793
794 func checkNotifySet(values []DSNNotify) error {
795 if len(values) == 0 {
796 return errors.New("Malformed NOTIFY parameter value")
797 }
798
799 seen := map[DSNNotify]struct{}{}
800 for _, val := range values {
801 switch val {
802 case DSNNotifyNever, DSNNotifyDelayed, DSNNotifyFailure, DSNNotifySuccess:
803 if _, ok := seen[val]; ok {
804 return errors.New("Malformed NOTIFY parameter value")
805 }
806 default:
807 return errors.New("Malformed NOTIFY parameter value")
808 }
809 seen[val] = struct{}{}
810 }
811 if _, ok := seen[DSNNotifyNever]; ok && len(seen) > 1 {
812 return errors.New("Malformed NOTIFY parameter value")
813 }
814
815 return nil
816 }
817
818 func (c *Conn) handleAuth(arg string) {
819 if c.helo == "" {
820 c.writeResponse(502, EnhancedCode{5, 5, 1}, "Please introduce yourself first.")
821 return
822 }
823 if c.didAuth {
824 c.writeResponse(503, EnhancedCode{5, 5, 1}, "Already authenticated")
825 return
826 }
827
828 parts := strings.Fields(arg)
829 if len(parts) == 0 {
830 c.writeResponse(502, EnhancedCode{5, 5, 4}, "Missing parameter")
831 return
832 }
833
834 if !c.authAllowed() {
835 c.writeResponse(523, EnhancedCode{5, 7, 10}, "TLS is required")
836 return
837 }
838
839 mechanism := strings.ToUpper(parts[0])
840
841 // Parse client initial response if there is one
842 var ir []byte
843 if len(parts) > 1 {
844 var err error
845 ir, err = decodeSASLResponse(parts[1])
846 if err != nil {
847 c.writeResponse(454, EnhancedCode{4, 7, 0}, "Invalid base64 data")
848 return
849 }
850 }
851
852 sasl, err := c.auth(mechanism)
853 if err != nil {
854 c.writeError(454, EnhancedCode{4, 7, 0}, err)
855 return
856 }
857
858 response := ir
859 for {
860 challenge, done, err := sasl.Next(response)
861 if err != nil {
862 c.writeError(454, EnhancedCode{4, 7, 0}, err)
863 return
864 }
865
866 if done {
867 break
868 }
869
870 encoded := ""
871 if len(challenge) > 0 {
872 encoded = base64.StdEncoding.EncodeToString(challenge)
873 }
874 c.writeResponse(334, NoEnhancedCode, encoded)
875
876 encoded, err = c.readLine()
877 if err != nil {
878 return // TODO: error handling
879 }
880
881 if encoded == "*" {
882 // https://tools.ietf.org/html/rfc4954#page-4
883 c.writeResponse(501, EnhancedCode{5, 0, 0}, "Negotiation cancelled")
884 return
885 }
886
887 response, err = decodeSASLResponse(encoded)
888 if err != nil {
889 c.writeResponse(454, EnhancedCode{4, 7, 0}, "Invalid base64 data")
890 return
891 }
892 }
893
894 c.writeResponse(235, EnhancedCode{2, 0, 0}, "Authentication succeeded")
895 c.didAuth = true
896 }
897
898 func decodeSASLResponse(s string) ([]byte, error) {
899 if s == "=" {
900 return []byte{}, nil
901 }
902 return base64.StdEncoding.DecodeString(s)
903 }
904
905 func (c *Conn) authMechanisms() []string {
906 if authSession, ok := c.Session().(AuthSession); ok {
907 return authSession.AuthMechanisms()
908 }
909 return nil
910 }
911
912 func (c *Conn) auth(mech string) (sasl.Server, error) {
913 if authSession, ok := c.Session().(AuthSession); ok {
914 return authSession.Auth(mech)
915 }
916 return nil, ErrAuthUnknownMechanism
917 }
918
919 func (c *Conn) handleStartTLS() {
920 if _, isTLS := c.TLSConnectionState(); isTLS {
921 c.writeResponse(502, EnhancedCode{5, 5, 1}, "Already running in TLS")
922 return
923 }
924
925 if c.server.TLSConfig == nil {
926 c.writeResponse(502, EnhancedCode{5, 5, 1}, "TLS not supported")
927 return
928 }
929
930 c.writeResponse(220, EnhancedCode{2, 0, 0}, "Ready to start TLS")
931
932 // Upgrade to TLS
933 tlsConn := tls.Server(c.conn, c.server.TLSConfig)
934
935 if err := tlsConn.Handshake(); err != nil {
936 c.writeResponse(550, EnhancedCode{5, 0, 0}, "Handshake error")
937 return
938 }
939
940 c.conn = tlsConn
941 c.init()
942
943 // Reset all state and close the previous Session.
944 // This is different from just calling reset() since we want the Backend to
945 // be able to see the information about TLS connection in the
946 // ConnectionState object passed to it.
947 if session := c.Session(); session != nil {
948 session.Logout()
949 c.setSession(nil)
950 }
951 c.helo = ""
952 c.didAuth = false
953 c.reset()
954 }
955
956 // DATA
957 func (c *Conn) handleData(arg string) {
958 if arg != "" {
959 c.writeResponse(501, EnhancedCode{5, 5, 4}, "DATA command should not have any arguments")
960 return
961 }
962 if c.bdatPipe != nil {
963 c.writeResponse(502, EnhancedCode{5, 5, 1}, "DATA not allowed during message transfer")
964 return
965 }
966 if c.binarymime {
967 c.writeResponse(502, EnhancedCode{5, 5, 1}, "DATA not allowed for BINARYMIME messages")
968 return
969 }
970
971 if !c.fromReceived || len(c.recipients) == 0 {
972 c.writeResponse(502, EnhancedCode{5, 5, 1}, "Missing RCPT TO command.")
973 return
974 }
975
976 // We have recipients, go to accept data
977 c.writeResponse(354, NoEnhancedCode, "Go ahead. End your data with <CR><LF>.<CR><LF>")
978
979 defer c.reset()
980
981 if c.server.LMTP {
982 c.handleDataLMTP()
983 return
984 }
985
986 r := newDataReader(c)
987 code, enhancedCode, msg := dataErrorToStatus(c.Session().Data(r))
988 r.limited = false
989 io.Copy(ioutil.Discard, r) // Make sure all the data has been consumed
990 c.writeResponse(code, enhancedCode, msg)
991 }
992
993 func (c *Conn) handleBdat(arg string) {
994 args := strings.Fields(arg)
995 if len(args) == 0 {
996 c.writeResponse(501, EnhancedCode{5, 5, 4}, "Missing chunk size argument")
997 return
998 }
999 if len(args) > 2 {
1000 c.writeResponse(501, EnhancedCode{5, 5, 4}, "Too many arguments")
1001 return
1002 }
1003
1004 if !c.fromReceived || len(c.recipients) == 0 {
1005 c.writeResponse(502, EnhancedCode{5, 5, 1}, "Missing RCPT TO command.")
1006 return
1007 }
1008
1009 last := false
1010 if len(args) == 2 {
1011 if !strings.EqualFold(args[1], "LAST") {
1012 c.writeResponse(501, EnhancedCode{5, 5, 4}, "Unknown BDAT argument")
1013 return
1014 }
1015 last = true
1016 }
1017
1018 // ParseUint instead of Atoi so we will not accept negative values.
1019 size, err := strconv.ParseUint(args[0], 10, 32)
1020 if err != nil {
1021 c.writeResponse(501, EnhancedCode{5, 5, 4}, "Malformed size argument")
1022 return
1023 }
1024
1025 if c.server.MaxMessageBytes != 0 && c.bytesReceived+int64(size) > c.server.MaxMessageBytes {
1026 c.writeResponse(552, EnhancedCode{5, 3, 4}, "Max message size exceeded")
1027
1028 // Discard chunk itself without passing it to backend.
1029 io.Copy(ioutil.Discard, io.LimitReader(c.text.R, int64(size)))
1030
1031 c.reset()
1032 return
1033 }
1034
1035 if c.bdatStatus == nil && c.server.LMTP {
1036 c.bdatStatus = c.createStatusCollector()
1037 }
1038
1039 if c.bdatPipe == nil {
1040 var r *io.PipeReader
1041 r, c.bdatPipe = io.Pipe()
1042
1043 c.dataResult = make(chan error, 1)
1044
1045 go func() {
1046 defer func() {
1047 if err := recover(); err != nil {
1048 c.handlePanic(err, c.bdatStatus)
1049
1050 c.dataResult <- errPanic
1051 r.CloseWithError(errPanic)
1052 }
1053 }()
1054
1055 var err error
1056 if !c.server.LMTP {
1057 err = c.Session().Data(r)
1058 } else {
1059 lmtpSession, ok := c.Session().(LMTPSession)
1060 if !ok {
1061 err = c.Session().Data(r)
1062 for _, rcpt := range c.recipients {
1063 c.bdatStatus.SetStatus(rcpt, err)
1064 }
1065 } else {
1066 err = lmtpSession.LMTPData(r, c.bdatStatus)
1067 }
1068 }
1069
1070 c.dataResult <- err
1071 r.CloseWithError(err)
1072 }()
1073 }
1074
1075 c.lineLimitReader.LineLimit = 0
1076
1077 chunk := io.LimitReader(c.text.R, int64(size))
1078 _, err = io.Copy(c.bdatPipe, chunk)
1079 if err != nil {
1080 // Backend might return an error early using CloseWithError without consuming
1081 // the whole chunk.
1082 io.Copy(ioutil.Discard, chunk)
1083
1084 c.writeResponse(dataErrorToStatus(err))
1085
1086 if err == errPanic {
1087 c.Close()
1088 }
1089
1090 c.reset()
1091 c.lineLimitReader.LineLimit = c.server.MaxLineLength
1092 return
1093 }
1094
1095 c.bytesReceived += int64(size)
1096
1097 if last {
1098 c.lineLimitReader.LineLimit = c.server.MaxLineLength
1099
1100 c.bdatPipe.Close()
1101
1102 err := <-c.dataResult
1103
1104 if c.server.LMTP {
1105 c.bdatStatus.fillRemaining(err)
1106 for i, rcpt := range c.recipients {
1107 code, enchCode, msg := dataErrorToStatus(<-c.bdatStatus.status[i])
1108 c.writeResponse(code, enchCode, "<"+rcpt+"> "+msg)
1109 }
1110 } else {
1111 c.writeResponse(dataErrorToStatus(err))
1112 }
1113
1114 if err == errPanic {
1115 c.Close()
1116 return
1117 }
1118
1119 c.reset()
1120 } else {
1121 c.writeResponse(250, EnhancedCode{2, 0, 0}, "Continue")
1122 }
1123 }
1124
1125 // ErrDataReset is returned by Reader pased to Data function if client does not
1126 // send another BDAT command and instead closes connection or issues RSET command.
1127 var ErrDataReset = errors.New("smtp: message transmission aborted")
1128
1129 var errPanic = &SMTPError{
1130 Code: 421,
1131 EnhancedCode: EnhancedCode{4, 0, 0},
1132 Message: "Internal server error",
1133 }
1134
1135 func (c *Conn) handlePanic(err interface{}, status *statusCollector) {
1136 if status != nil {
1137 status.fillRemaining(errPanic)
1138 }
1139
1140 stack := debug.Stack()
1141 c.server.ErrorLog.Printf("panic serving %v: %v\n%s", c.conn.RemoteAddr(), err, stack)
1142 }
1143
1144 func (c *Conn) createStatusCollector() *statusCollector {
1145 rcptCounts := make(map[string]int, len(c.recipients))
1146
1147 status := &statusCollector{
1148 statusMap: make(map[string]chan error, len(c.recipients)),
1149 status: make([]chan error, 0, len(c.recipients)),
1150 }
1151 for _, rcpt := range c.recipients {
1152 rcptCounts[rcpt]++
1153 }
1154 // Create channels with buffer sizes necessary to fit all
1155 // statuses for a single recipient to avoid deadlocks.
1156 for rcpt, count := range rcptCounts {
1157 status.statusMap[rcpt] = make(chan error, count)
1158 }
1159 for _, rcpt := range c.recipients {
1160 status.status = append(status.status, status.statusMap[rcpt])
1161 }
1162
1163 return status
1164 }
1165
1166 type statusCollector struct {
1167 // Contains map from recipient to list of channels that are used for that
1168 // recipient.
1169 statusMap map[string]chan error
1170
1171 // Contains channels from statusMap, in the same
1172 // order as Conn.recipients.
1173 status []chan error
1174 }
1175
1176 // fillRemaining sets status for all recipients SetStatus was not called for before.
1177 func (s *statusCollector) fillRemaining(err error) {
1178 // Amount of times certain recipient was specified is indicated by the channel
1179 // buffer size, so once we fill it, we can be confident that we sent
1180 // at least as much statuses as needed. Extra statuses will be ignored anyway.
1181 chLoop:
1182 for _, ch := range s.statusMap {
1183 for {
1184 select {
1185 case ch <- err:
1186 default:
1187 continue chLoop
1188 }
1189 }
1190 }
1191 }
1192
1193 func (s *statusCollector) SetStatus(rcptTo string, err error) {
1194 ch := s.statusMap[rcptTo]
1195 if ch == nil {
1196 panic("SetStatus is called for recipient that was not specified before")
1197 }
1198
1199 select {
1200 case ch <- err:
1201 default:
1202 // There enough buffer space to fit all statuses at once, if this is
1203 // not the case - backend is doing something wrong.
1204 panic("SetStatus is called more times than particular recipient was specified")
1205 }
1206 }
1207
1208 func (c *Conn) handleDataLMTP() {
1209 r := newDataReader(c)
1210 status := c.createStatusCollector()
1211
1212 done := make(chan bool, 1)
1213
1214 lmtpSession, ok := c.Session().(LMTPSession)
1215 if !ok {
1216 // Fallback to using a single status for all recipients.
1217 err := c.Session().Data(r)
1218 io.Copy(ioutil.Discard, r) // Make sure all the data has been consumed
1219 for _, rcpt := range c.recipients {
1220 status.SetStatus(rcpt, err)
1221 }
1222 done <- true
1223 } else {
1224 go func() {
1225 defer func() {
1226 if err := recover(); err != nil {
1227 status.fillRemaining(&SMTPError{
1228 Code: 421,
1229 EnhancedCode: EnhancedCode{4, 0, 0},
1230 Message: "Internal server error",
1231 })
1232
1233 stack := debug.Stack()
1234 c.server.ErrorLog.Printf("panic serving %v: %v\n%s", c.conn.RemoteAddr(), err, stack)
1235 done <- false
1236 }
1237 }()
1238
1239 status.fillRemaining(lmtpSession.LMTPData(r, status))
1240 io.Copy(ioutil.Discard, r) // Make sure all the data has been consumed
1241 done <- true
1242 }()
1243 }
1244
1245 for i, rcpt := range c.recipients {
1246 code, enchCode, msg := dataErrorToStatus(<-status.status[i])
1247 c.writeResponse(code, enchCode, "<"+rcpt+"> "+msg)
1248 }
1249
1250 // If done gets false, the panic occured in LMTPData and the connection
1251 // should be closed.
1252 if !<-done {
1253 c.Close()
1254 }
1255 }
1256
1257 func dataErrorToStatus(err error) (code int, enchCode EnhancedCode, msg string) {
1258 if err != nil {
1259 if smtperr, ok := err.(*SMTPError); ok {
1260 return smtperr.Code, smtperr.EnhancedCode, smtperr.Message
1261 } else {
1262 return 554, EnhancedCode{5, 0, 0}, "Error: transaction failed: " + err.Error()
1263 }
1264 }
1265
1266 return 250, EnhancedCode{2, 0, 0}, "OK: queued"
1267 }
1268
1269 func (c *Conn) Reject() {
1270 c.writeResponse(421, EnhancedCode{4, 4, 5}, "Too busy. Try again later.")
1271 c.Close()
1272 }
1273
1274 func (c *Conn) greet() {
1275 protocol := "ESMTP"
1276 if c.server.LMTP {
1277 protocol = "LMTP"
1278 }
1279 c.writeResponse(220, NoEnhancedCode, fmt.Sprintf("%v %s Service Ready", c.server.Domain, protocol))
1280 }
1281
1282 func (c *Conn) writeResponse(code int, enhCode EnhancedCode, text ...string) {
1283 // TODO: error handling
1284 if c.server.WriteTimeout != 0 {
1285 c.conn.SetWriteDeadline(time.Now().Add(c.server.WriteTimeout))
1286 }
1287
1288 // All responses must include an enhanced code, if it is missing - use
1289 // a generic code X.0.0.
1290 if enhCode == EnhancedCodeNotSet {
1291 cat := code / 100
1292 switch cat {
1293 case 2, 4, 5:
1294 enhCode = EnhancedCode{cat, 0, 0}
1295 default:
1296 enhCode = NoEnhancedCode
1297 }
1298 }
1299
1300 // transform each single line with \n, into separate lines
1301 text = strings.Split(strings.Join(text, "\n"), "\n")
1302
1303 lastLineIndex := len(text) - 1
1304 for i := 0; i < lastLineIndex; i++ {
1305 c.text.PrintfLine("%d-%v", code, text[i])
1306 }
1307 if enhCode == NoEnhancedCode {
1308 c.text.PrintfLine("%d %v", code, text[lastLineIndex])
1309 } else {
1310 c.text.PrintfLine("%d %v.%v.%v %v", code, enhCode[0], enhCode[1], enhCode[2], text[lastLineIndex])
1311 }
1312 }
1313
1314 func (c *Conn) writeError(code int, enhCode EnhancedCode, err error) {
1315 if smtpErr, ok := err.(*SMTPError); ok {
1316 c.writeResponse(smtpErr.Code, smtpErr.EnhancedCode, smtpErr.Message)
1317 } else {
1318 c.writeResponse(code, enhCode, err.Error())
1319 }
1320 }
1321
1322 // Reads a line of input
1323 func (c *Conn) readLine() (string, error) {
1324 if c.server.ReadTimeout != 0 {
1325 if err := c.conn.SetReadDeadline(time.Now().Add(c.server.ReadTimeout)); err != nil {
1326 return "", err
1327 }
1328 }
1329
1330 return c.text.ReadLine()
1331 }
1332
1333 func (c *Conn) reset() {
1334 c.locker.Lock()
1335 defer c.locker.Unlock()
1336
1337 if c.bdatPipe != nil {
1338 c.bdatPipe.CloseWithError(ErrDataReset)
1339 c.bdatPipe = nil
1340 }
1341 c.bdatStatus = nil
1342 c.bytesReceived = 0
1343
1344 if c.session != nil {
1345 c.session.Reset()
1346 }
1347
1348 c.fromReceived = false
1349 c.recipients = nil
1350 }
1351