Documentation ¶
Overview ¶
Package smtp provide a library for building SMTP server and client.
Server ¶
By default, server will listen on port 25 and 465.
Port 25 is only used to receive message relay from other mail server. Any command that require authentication will be rejected by this port.
Port 465 is used to receive message submission from SMTP accounts with authentication.
Server Environment ¶
The server require one primary domain with one primary account called "postmaster". Domain can have two or more accounts. Domain can have their own DKIM certificate.
Limitations ¶
The server favor implicit TLS over STARTTLS (RFC 8314) on port 465 for message submission.
Index ¶
- Constants
- Variables
- func ParseMailbox(data []byte) (mailbox []byte)
- func ParsePath(path []byte) (mailbox []byte, err error)
- type Account
- type Client
- func (cl *Client) Authenticate(mech Mechanism, username, password string) (res *Response, err error)
- func (cl *Client) Expand(mlist string) (res *Response, err error)
- func (cl *Client) Help(cmdName string) (res *Response, err error)
- func (cl *Client) MailTx(mail *MailTx) (res *Response, err error)
- func (cl *Client) Quit() (res *Response, err error)
- func (cl *Client) SendCommand(cmd []byte) (res *Response, err error)
- func (cl *Client) StartTLS() (res *Response, err error)
- func (cl *Client) Verify(mailbox string) (res *Response, err error)
- type Command
- type CommandKind
- type DKIMOptions
- type Domain
- type Environment
- type Extension
- type Handler
- type LocalHandler
- func (lh *LocalHandler) ServeAuth(username, password string) (res *Response, err error)
- func (lh *LocalHandler) ServeBounce(mail *MailTx) (res *Response, err error)
- func (lh *LocalHandler) ServeExpand(mailingList string) (res *Response, err error)
- func (lh *LocalHandler) ServeMailTx(mail *MailTx) (res *Response, err error)
- func (lh *LocalHandler) ServeVerify(username string) (res *Response, err error)
- type LocalStorage
- func (fs *LocalStorage) MailBounce(id string) error
- func (fs *LocalStorage) MailDelete(id string) (err error)
- func (fs *LocalStorage) MailLoad(id string) (mail *MailTx, err error)
- func (fs *LocalStorage) MailLoadAll() (mails []*MailTx, err error)
- func (fs *LocalStorage) MailSave(mail *MailTx) (err error)
- type MailTx
- type Mailbox
- type Mechanism
- type Response
- type Server
- type ServerInfo
- type Storage
Examples ¶
Constants ¶
const ( CommandZERO CommandKind = 0 CommandHELO = 1 << iota CommandEHLO CommandAUTH CommandMAIL CommandRCPT CommandDATA CommandRSET CommandVRFY CommandEXPN CommandHELP CommandNOOP CommandQUIT )
List of SMTP commands.
const ( // // 2yz Positive Completion reply // // The requested action has been successfully completed. A new // request may be initiated. // StatusSystem = 211 StatusHelp = 214 StatusReady = 220 StatusClosing = 221 StatusAuthenticated = 235 // RFC 4954 StatusOK = 250 StatusAddressChange = 251 // RFC 5321, section 3.4. StatusVerifyFailed = 252 // RFC 5321, section 3.5.3. // // 3xx Positive Intermediate reply. // // The command has been accepted, but the requested action is being // held in abeyance, pending receipt of further information. The // SMTP client should send another command specifying this // information. This reply is used in command DATA. // StatusAuthReady = 334 StatusDataReady = 354 // // 4xx Transient Negative Completion reply // // The command was not accepted, and the requested action did not // occur. However, the error condition is temporary, and the action // may be requested again. The sender should return to the beginning // of the command sequence (if any). It is difficult to assign a // meaning to "transient" when two different sites (receiver- and // sender-SMTP agents) must agree on the interpretation. Each reply // in this category might have a different time value, but the SMTP // client SHOULD try again. A rule of thumb to determine whether a // reply fits into the 4yz or the 5yz category (see below) is that // replies are 4yz if they can be successful if repeated without any // change in command form or in properties of the sender or receiver // (that is, the command is repeated identically and the receiver // does not put up a new implementation). // StatusShuttingDown = 421 StatusLocalError = 451 StatusNoStorage = 452 StatusParameterUnprocessable = 455 // // 5xx indicate permanent failure. // // The command was not accepted and the requested action did not // occur. The SMTP client SHOULD NOT repeat the exact request (in // the same sequence). Even some "permanent" error conditions can be // corrected, so the human user may want to direct the SMTP client to // reinitiate the command sequence by direct action at some point in // the future (e.g., after the spelling has been changed, or the user // has altered the account status). // StatusCmdUnknown = 500 // RFC 5321, section 4.2.4. StatusCmdTooLong = 500 // RFC 5321, section 4.3.2. StatusCmdSyntaxError = 501 StatusCmdNotImplemented = 502 // RFC 5321, section 4.2.4. StatusCmdBadSequence = 503 StatusParamUnimplemented = 504 StatusNotAuthenticated = 530 StatusInvalidCredential = 535 StatusMailboxNotFound = 550 StatusAddressChangeAborted = 551 // RFC 5321, section 3.4. StatusMailNoStorage = 552 StatusMailboxIncorrect = 553 StatusTransactionFailed = 554 StatusMailRcptParamUnknown = 555 )
List of SMTP status codes.
Variables ¶
var ( ErrInvalidCredential = &errors.E{ Code: StatusInvalidCredential, Message: "5.7.8 Authentication credentials invalid", } )
List of errors.
Functions ¶
func ParseMailbox ¶
ParseMailbox parse the mailbox, remove comment or any escaped characters insided quoted-string.
func ParsePath ¶
ParsePath parse the Reverse-path or Forward-path as in argument of MAIL and RCPT commands. This function ignore the source route and only return the mailbox. Empty mailbox without an error is equal to Null Reverse-Path "<>".
Example ¶
mb, _ := ParsePath([]byte(`<@domain.com,@domain.net:local.part@domain.com>`)) fmt.Printf("%s\n", mb) mb, _ = ParsePath([]byte(`<local.part@domain.com>`)) fmt.Printf("%s\n", mb) mb, _ = ParsePath([]byte(`<local>`)) fmt.Printf("%s\n", mb)
Output: local.part@domain.com local.part@domain.com local
Types ¶
type Account ¶
type Account struct { Mailbox // HashPass user password that has been hashed using bcrypt. HashPass string }
Account represent an SMTP account in the server that can send and receive email.
func NewAccount ¶
NewAccount create new account. Password will be hashed using bcrypt. An account with empty password is system account, which mean it will not allowed in SMTP AUTH.
func (*Account) Authenticate ¶
Authenticate a user using plain text password. It will return an error if password does not match.
type Client ¶
type Client struct { // ServerInfo contains the server information, from the response of // EHLO command. ServerInfo *ServerInfo // contains filtered or unexported fields }
Client for SMTP.
func NewClient ¶
NewClient create and initialize connection to remote SMTP server.
The localName define the client domain address, used when issuing EHLO command to server. If its empty, it will set to current operating system's hostname.
The remoteURL use the following format,
remoteURL = [ scheme "://" ](domain | IP-address [":" port]) scheme = "smtp" / "smtps" / "smtp+starttls"
If scheme is "smtp" and no port is given, client will connect to remote address at port 25. If scheme is "smtps" and no port is given, client will connect to remote address at port 465 (implicit TLS). If scheme is "smtp+starttls" and no port is given, client will connect to remote address at port 587.
The "insecure" parameter, if set to true, will disable verifying remote certificate when connecting with TLS or STARTTLS.
On success, it will return connected client, with implicit EHLO command issued to server immediately. If scheme is "smtp+starttls", the connection also automatically upgraded to TLS after EHLO command success.
On fail, it will return nil client with an error.
func (*Client) Authenticate ¶
func (cl *Client) Authenticate(mech Mechanism, username, password string) ( res *Response, err error, )
Authenticate to server using one of SASL mechanism. Currently, the only mechanism available is PLAIN.
func (*Client) MailTx ¶
MailTx send the mail to server. This function is implementation of mail transaction (MAIL, RCPT, and DATA commands as described in RFC 5321, section 3.3). The MailTx.Data must be internet message format which contains headers and content as defined by RFC 5322.
On success, it will return the last response, which is the success status of data transaction (250).
On fail, it will return response from the failed command with error is string combination of command, response code and message.
func (*Client) SendCommand ¶
SendCommand send any custom command to server.
type Command ¶
type Command struct { Kind CommandKind Arg string Param string Params map[string]string }
Command represent a single SMTP command with its parsed argument and parameters.
type DKIMOptions ¶ added in v0.6.0
type DKIMOptions struct { Signature *dkim.Signature PrivateKey *rsa.PrivateKey }
DKIMOptions contains the DKIM signature fields and private key to sign the incoming message.
type Domain ¶
type Domain struct { Name string Accounts map[string]*Account // contains filtered or unexported fields }
Domain contains a host name and list of accounts in domain, with optional DKIM feature.
func NewDomain ¶
func NewDomain(name string, dkimOpts *DKIMOptions) (domain *Domain)
NewDomain create new domain with single main user, "postmaster".
type Environment ¶
type Environment struct { // PrimaryDomain of the SMTP server. // This field is required. PrimaryDomain *Domain // VirtualDomains contains list of virtual domain handled by server. // This field is optional. VirtualDomains map[string]*Domain }
Environment contains SMTP server environment.
type Extension ¶
type Extension interface { // // Name return the SMTP extension name to be used on reply of EHLO. // Name() string // // Params return the SMTP extension parameters. // Params() string // // ValidateCommand validate the command parameters, if the extension // provide custom parameters. // ValidateCommand(cmd *Command) error }
Extension is an interface to implement extension for SMTP server.
type Handler ¶
type Handler interface { // ServeAuth handle SMTP AUTH parameter username and password. ServeAuth(username, password string) (*Response, error) // ServeBounce handle email transaction that with unknown or invalid // recipent. ServeBounce(mail *MailTx) (*Response, error) // ServeExpand handle SMTP EXPN command. ServeExpand(mailingList string) (*Response, error) // ServeMailTx handle termination on email transaction. ServeMailTx(mail *MailTx) (*Response, error) // ServeVerify handle SMTP VRFY command. ServeVerify(username string) (*Response, error) }
Handler define an interface to handle bouncing and incoming mail message, and handling EXPN and VRFY commands.
func NewLocalHandler ¶
func NewLocalHandler(env *Environment) Handler
NewLocalHandler create an handler using local environment.
type LocalHandler ¶
type LocalHandler struct {
// contains filtered or unexported fields
}
LocalHandler is an handler using local environment.
func (*LocalHandler) ServeAuth ¶
func (lh *LocalHandler) ServeAuth(username, password string) ( res *Response, err error, )
ServeAuth handle SMTP AUTH parameter username and password.
func (*LocalHandler) ServeBounce ¶
func (lh *LocalHandler) ServeBounce(mail *MailTx) (res *Response, err error)
ServeBounce handle email transaction with unknown or invalid recipent.
func (*LocalHandler) ServeExpand ¶
func (lh *LocalHandler) ServeExpand(mailingList string) (res *Response, err error)
ServeExpand handle SMTP EXPN command.
BUG: The group feature currently is not supported.
func (*LocalHandler) ServeMailTx ¶
func (lh *LocalHandler) ServeMailTx(mail *MailTx) (res *Response, err error)
ServeMailTx handle processing the final delivery of incoming mail.
func (*LocalHandler) ServeVerify ¶
func (lh *LocalHandler) ServeVerify(username string) (res *Response, err error)
ServeVerify handle SMTP VRFY command. The username must be in the format of mailbox, "local@domain".
type LocalStorage ¶
type LocalStorage struct {
// contains filtered or unexported fields
}
LocalStorage implement the Storage interface where mail object is save and retrieved in file system inside a directory.
func (*LocalStorage) MailBounce ¶
func (fs *LocalStorage) MailBounce(id string) error
MailBounce move the incoming mail to bounced state. In this storage service, the mail file is moved to "{dir}/bounce".
func (*LocalStorage) MailDelete ¶
func (fs *LocalStorage) MailDelete(id string) (err error)
MailDelete the mail object on file system by ID.
func (*LocalStorage) MailLoad ¶
func (fs *LocalStorage) MailLoad(id string) (mail *MailTx, err error)
MailLoad read the mail object from file system by ID.
func (*LocalStorage) MailLoadAll ¶
func (fs *LocalStorage) MailLoadAll() (mails []*MailTx, err error)
MailLoadAll mail objects from file system.
func (*LocalStorage) MailSave ¶
func (fs *LocalStorage) MailSave(mail *MailTx) (err error)
MailSave save the mail object into file system.
type MailTx ¶
type MailTx struct { // ID of message. // This field is ignored in Client.Send(). ID string // From contains originator address. // This field is required in Client.Send(). From string // Recipients contains list of the destination address. // This field is required in Client.Send(). Recipients []string // Data contains content of message. // This field is optional in Client.Send(). Data []byte Postpone time.Time // Received contains the time when the message arrived on server. // This field is ignored in Client.Send(). Received time.Time Retry int }
MailTx define a mail transaction.
type Mailbox ¶
type Mailbox struct { Name string // Name of user in system. Local string // Local part. Domain string // Domain part. }
Mailbox represent a mailbox format.
type Mechanism ¶
type Mechanism int
Mechanism represent Simple Authentication and Security Layer (SASL) mechanism (RFC 4422).
const (
MechanismPLAIN Mechanism = 1
)
List of available SASL mechanism.
type Response ¶
Response represent a generic single or multilines response from server.
func NewResponse ¶
NewResponse create and initialize new Response from parsing the raw response text.
type Server ¶
type Server struct { // TLSCert the server certificate for TLS or nil if no certificate. // This field is optional, if its non nil, the server will also listen // on address defined in TLSAddress. TLSCert *tls.Certificate // Env contains server environment. Env *Environment // // Exts define list of custom extensions that the server will provide. // Exts []Extension // // Handler define an interface that will process the bouncing email, // incoming email, EXPN command, and VRFY command. // This field is optional, if not set, it will default to // LocalHandler. // Handler Handler // contains filtered or unexported fields }
Server defines parameters for running an SMTP server.
func (*Server) LoadCertificate ¶
LoadCertificate load TLS certificate and its private key from file.
type ServerInfo ¶
ServerInfo provide information about server from response of EHLO or HELO command.
func NewServerInfo ¶
func NewServerInfo(res *Response) (srvInfo *ServerInfo)
NewServerInfo create and initialize ServerInfo from EHLO/HELO response.
type Storage ¶
type Storage interface { MailBounce(id string) error MailDelete(id string) error MailLoad(id string) (mail *MailTx, err error) MailLoadAll() (mail []*MailTx, err error) MailSave(mail *MailTx) error }
Storage define an interface for storing and retrieving mail object into permanent storage (for example, file system or database).
func NewLocalStorage ¶
NewLocalStorage create and initialize new file storage. If directory is empty, the default storage is located at "/var/spool/smtp/".