2014-07-20 19:53:47 +00:00
// Package smtpd implements an SMTP server with support for STARTTLS, authentication (PLAIN/LOGIN), XCLIENT and optional restrictions on the different stages of the SMTP session.
2014-07-13 21:24:13 +00:00
package smtpd
import (
"bufio"
"crypto/tls"
"fmt"
"log"
"net"
2014-07-14 12:20:36 +00:00
"time"
2014-07-13 21:24:13 +00:00
)
2014-07-14 12:51:31 +00:00
// Server defines the parameters for running the SMTP server
2014-07-13 21:24:13 +00:00
type Server struct {
2014-07-21 10:43:42 +00:00
Hostname string // Server hostname. (default: "localhost.localdomain")
2014-07-15 09:16:34 +00:00
WelcomeMessage string // Initial server banner. (default: "<hostname> ESMTP ready.")
2014-07-13 21:24:13 +00:00
2014-07-15 09:16:34 +00:00
ReadTimeout time . Duration // Socket timeout for read operations. (default: 60s)
WriteTimeout time . Duration // Socket timeout for write operations. (default: 60s)
2014-07-17 14:00:44 +00:00
DataTimeout time . Duration // Socket timeout for DATA command (default: 5m)
2014-07-13 21:24:13 +00:00
2014-07-15 09:16:34 +00:00
MaxConnections int // Max concurrent connections, use -1 to disable. (default: 100)
2014-07-17 14:00:44 +00:00
MaxMessageSize int // Max message size in bytes. (default: 10240000)
MaxRecipients int // Max RCPT TO calls for each envelope. (default: 100)
2014-07-15 08:07:58 +00:00
2014-07-13 21:24:13 +00:00
// New e-mails are handed off to this function.
2014-07-14 11:55:41 +00:00
// Can be left empty for a NOOP server.
// If an error is returned, it will be reported in the SMTP session.
2014-07-13 21:24:13 +00:00
Handler func ( peer Peer , env Envelope ) error
2014-07-14 11:55:41 +00:00
// Enable various checks during the SMTP session.
// Can be left empty for no restrictions.
// If an error is returned, it will be reported in the SMTP session.
2014-07-15 09:16:34 +00:00
// Use the Error struct for access to error codes.
ConnectionChecker func ( peer Peer ) error // Called upon new connection.
2014-07-18 06:46:39 +00:00
HeloChecker func ( peer Peer , name string ) error // Called after HELO/EHLO.
2014-07-15 09:16:34 +00:00
SenderChecker func ( peer Peer , addr string ) error // Called after MAIL FROM.
RecipientChecker func ( peer Peer , addr string ) error // Called after each RCPT TO.
2014-07-14 11:55:41 +00:00
// Enable PLAIN/LOGIN authentication, only available after STARTTLS.
// Can be left empty for no authentication support.
2014-07-13 21:24:13 +00:00
Authenticator func ( peer Peer , username , password string ) error
2014-07-20 19:51:39 +00:00
EnableXCLIENT bool // Enable XCLIENT support (default: false)
2014-07-15 09:16:34 +00:00
TLSConfig * tls . Config // Enable STARTTLS support.
ForceTLS bool // Force STARTTLS usage.
2014-07-13 21:24:13 +00:00
}
2014-07-20 19:51:39 +00:00
// Protocol represents the protocol used in the SMTP session
type Protocol string
const (
SMTP Protocol = "SMTP"
ESMTP = "ESMTP"
)
2014-07-14 12:51:31 +00:00
// Peer represents the client connecting to the server
2014-07-13 21:24:13 +00:00
type Peer struct {
2014-07-21 10:43:42 +00:00
HeloName string // Server name used in HELO/EHLO command
Username string // Username from authentication, if authenticated
Password string // Password from authentication, if authenticated
Protocol Protocol // Protocol used, SMTP or ESMTP
ServerName string // A copy of Server.Hostname
Addr net . Addr // Network address
TLS * tls . ConnectionState // TLS Connection details, if on TLS
2014-07-13 21:24:13 +00:00
}
2014-07-15 09:16:34 +00:00
// Error represents an Error reported in the SMTP session.
type Error struct {
Code int // The integer error code
Message string // The error message
}
// Error returns a string representation of the SMTP error
func ( e Error ) Error ( ) string { return fmt . Sprintf ( "%d %s" , e . Code , e . Message ) }
2014-07-14 11:55:41 +00:00
type session struct {
server * Server
2014-07-14 12:20:36 +00:00
peer Peer
2014-07-14 11:55:41 +00:00
envelope * Envelope
2014-07-14 12:20:36 +00:00
conn net . Conn
2014-07-14 11:55:41 +00:00
2014-07-14 12:20:36 +00:00
reader * bufio . Reader
writer * bufio . Writer
2014-07-14 11:55:41 +00:00
scanner * bufio . Scanner
2014-07-14 12:20:36 +00:00
tls bool
2014-07-14 11:55:41 +00:00
}
2014-07-15 08:11:37 +00:00
func ( srv * Server ) newSession ( c net . Conn ) ( s * session ) {
2014-07-13 21:24:13 +00:00
s = & session {
server : srv ,
conn : c ,
reader : bufio . NewReader ( c ) ,
writer : bufio . NewWriter ( c ) ,
2014-07-21 10:43:42 +00:00
peer : Peer {
Addr : c . RemoteAddr ( ) ,
ServerName : srv . Hostname ,
} ,
2014-07-13 21:24:13 +00:00
}
2014-07-14 12:20:36 +00:00
2014-07-14 11:55:41 +00:00
s . scanner = bufio . NewScanner ( s . reader )
2014-07-13 21:24:13 +00:00
2014-07-15 08:11:37 +00:00
return
2014-07-13 21:24:13 +00:00
}
2014-07-21 10:43:42 +00:00
// ListenAndServe starts the SMTP server and listens on the address provided
func ( srv * Server ) ListenAndServe ( addr string ) error {
2014-07-14 11:55:41 +00:00
srv . configureDefaults ( )
2014-07-21 10:43:42 +00:00
l , err := net . Listen ( "tcp" , addr )
2014-07-13 21:24:13 +00:00
if err != nil {
return err
}
2014-07-14 17:44:10 +00:00
2014-07-13 21:24:13 +00:00
return srv . Serve ( l )
}
2014-07-14 12:51:31 +00:00
// Serve starts the SMTP server and listens on the Listener provided
2014-07-13 21:24:13 +00:00
func ( srv * Server ) Serve ( l net . Listener ) error {
srv . configureDefaults ( )
defer l . Close ( )
2014-07-15 08:07:58 +00:00
var limiter chan struct { }
if srv . MaxConnections > 0 {
limiter = make ( chan struct { } , srv . MaxConnections )
} else {
limiter = nil
}
2014-07-13 21:24:13 +00:00
for {
conn , e := l . Accept ( )
if e != nil {
if ne , ok := e . ( net . Error ) ; ok && ne . Temporary ( ) {
time . Sleep ( time . Second )
continue
}
return e
}
2014-07-15 08:11:37 +00:00
session := srv . newSession ( conn )
2014-07-13 21:24:13 +00:00
2014-07-15 08:07:58 +00:00
if limiter != nil {
go func ( ) {
select {
case limiter <- struct { } { } :
session . serve ( )
<- limiter
default :
session . reject ( )
}
} ( )
} else {
go session . serve ( )
}
2014-07-13 21:24:13 +00:00
}
}
func ( srv * Server ) configureDefaults ( ) {
if srv . MaxMessageSize == 0 {
srv . MaxMessageSize = 10240000
}
2014-07-15 08:07:58 +00:00
if srv . MaxConnections == 0 {
srv . MaxConnections = 100
}
2014-07-17 14:00:44 +00:00
if srv . MaxRecipients == 0 {
srv . MaxRecipients = 100
}
2014-07-13 21:24:13 +00:00
if srv . ReadTimeout == 0 {
srv . ReadTimeout = time . Second * 60
}
if srv . WriteTimeout == 0 {
srv . WriteTimeout = time . Second * 60
}
2014-07-17 14:00:44 +00:00
if srv . DataTimeout == 0 {
srv . DataTimeout = time . Minute * 5
}
2014-07-13 21:24:13 +00:00
if srv . ForceTLS && srv . TLSConfig == nil {
log . Fatal ( "Cannot use ForceTLS with no TLSConfig" )
}
2014-07-21 10:43:42 +00:00
if srv . Hostname == "" {
srv . Hostname = "localhost.localdomain"
2014-07-14 11:55:41 +00:00
}
2014-07-13 21:24:13 +00:00
2014-07-14 11:55:41 +00:00
if srv . WelcomeMessage == "" {
2014-07-21 10:43:42 +00:00
srv . WelcomeMessage = fmt . Sprintf ( "%s ESMTP ready." , srv . Hostname )
2014-07-14 11:55:41 +00:00
}
2014-07-13 21:24:13 +00:00
2014-07-14 11:55:41 +00:00
}
2014-07-13 21:24:13 +00:00
2014-07-14 11:55:41 +00:00
func ( session * session ) serve ( ) {
2014-07-13 21:24:13 +00:00
2014-07-14 11:55:41 +00:00
defer session . close ( )
2014-07-13 21:24:13 +00:00
2014-07-15 09:16:34 +00:00
session . welcome ( )
2014-07-13 21:24:13 +00:00
2014-07-19 18:55:40 +00:00
for {
for session . scanner . Scan ( ) {
session . handle ( session . scanner . Text ( ) )
}
err := session . scanner . Err ( )
if err == bufio . ErrTooLong {
session . reply ( 500 , "Line too long" )
// Advance reader to the next newline
session . reader . ReadString ( '\n' )
session . scanner = bufio . NewScanner ( session . reader )
// Reset and have the client start over.
session . reset ( )
continue
}
break
2014-07-13 21:24:13 +00:00
}
2014-07-15 08:07:58 +00:00
}
func ( session * session ) reject ( ) {
2014-07-17 14:00:44 +00:00
session . reply ( 421 , "Too busy. Try again later." )
2014-07-15 08:07:58 +00:00
session . close ( )
2014-07-13 21:24:13 +00:00
}
2014-07-19 18:55:40 +00:00
func ( session * session ) reset ( ) {
session . envelope = nil
}
2014-07-15 09:16:34 +00:00
func ( session * session ) welcome ( ) {
if session . server . ConnectionChecker != nil {
err := session . server . ConnectionChecker ( session . peer )
if err != nil {
session . error ( err )
session . close ( )
return
}
}
session . reply ( 220 , session . server . WelcomeMessage )
}
2014-07-13 21:24:13 +00:00
func ( session * session ) reply ( code int , message string ) {
fmt . Fprintf ( session . writer , "%d %s\r\n" , code , message )
2014-07-17 14:00:44 +00:00
session . flush ( )
}
2014-07-13 21:24:13 +00:00
2014-07-17 14:00:44 +00:00
func ( session * session ) flush ( ) {
2014-07-13 21:24:13 +00:00
session . conn . SetWriteDeadline ( time . Now ( ) . Add ( session . server . WriteTimeout ) )
session . writer . Flush ( )
session . conn . SetReadDeadline ( time . Now ( ) . Add ( session . server . ReadTimeout ) )
}
2014-07-14 11:55:41 +00:00
func ( session * session ) error ( err error ) {
2014-07-15 09:16:34 +00:00
if smtpdError , ok := err . ( Error ) ; ok {
session . reply ( smtpdError . Code , smtpdError . Message )
} else {
session . reply ( 502 , fmt . Sprintf ( "%s" , err ) )
}
2014-07-14 11:55:41 +00:00
}
func ( session * session ) extensions ( ) [ ] string {
2014-07-13 21:24:13 +00:00
extensions := [ ] string {
2014-07-14 12:51:31 +00:00
fmt . Sprintf ( "SIZE %d" , session . server . MaxMessageSize ) ,
2014-07-14 17:44:10 +00:00
"8BITMIME" ,
2014-07-17 14:00:44 +00:00
"PIPELINING" ,
2014-07-13 21:24:13 +00:00
}
2014-07-20 19:51:39 +00:00
if session . server . EnableXCLIENT {
extensions = append ( extensions , "XCLIENT" )
}
2014-07-13 21:24:13 +00:00
if session . server . TLSConfig != nil && ! session . tls {
extensions = append ( extensions , "STARTTLS" )
}
2014-07-14 11:59:30 +00:00
if session . server . Authenticator != nil && session . tls {
2014-07-13 21:24:13 +00:00
extensions = append ( extensions , "AUTH PLAIN LOGIN" )
}
2014-07-14 11:55:41 +00:00
return extensions
2014-07-13 21:24:13 +00:00
}
2014-07-14 11:55:41 +00:00
func ( session * session ) deliver ( ) error {
2014-07-13 21:24:13 +00:00
if session . server . Handler != nil {
2014-07-14 11:55:41 +00:00
return session . server . Handler ( session . peer , * session . envelope )
2014-07-13 21:24:13 +00:00
}
2014-07-14 12:51:31 +00:00
return nil
2014-07-13 21:24:13 +00:00
}
2014-07-14 11:55:41 +00:00
func ( session * session ) close ( ) {
session . writer . Flush ( )
2014-07-15 10:37:25 +00:00
time . Sleep ( 200 * time . Millisecond )
2014-07-14 11:55:41 +00:00
session . conn . Close ( )
2014-07-13 21:24:13 +00:00
}