Documentation ¶
Index ¶
- Constants
- Variables
- func Serve(l net.Listener, h Handler) error
- type AuthConfig
- type AuthFunc
- type Command
- type Config
- type Conn
- func (c *Conn) Accept()
- func (c *Conn) AcceptData(id string)
- func (c *Conn) AcceptMsg(format string, elems ...interface{})
- func (c *Conn) AuthChallenge(data []byte)
- func (c *Conn) Authenticate(mech AuthFunc) (success bool)
- func (c *Conn) Next() EventInfo
- func (c *Conn) Reject()
- func (c *Conn) RejectData(id string)
- func (c *Conn) RejectMsg(format string, elems ...interface{})
- func (c *Conn) Tempfail()
- func (c *Conn) TempfailMsg(format string, elems ...interface{})
- type Envelope
- type Event
- type EventInfo
- type Handler
- type Limits
- type ParsedLine
- type Server
- type ServerConfig
Constants ¶
const ( Capability8BitMIME = "250-8BITMIME" CapabilityPipelining = "250-PIPELINING" CapabilityStartTLS = "250-STARTTLS" CapabilityAuth = "250-AUTH" // https://www.ietf.org/rfc/rfc2821.txt (4.1.1.1) CapabilityHelp = "250 HELP" // should be last in EHLO sequence(SMTP uglyness) ReplyNoHelp = "214 No help here" ReplyReadyForTLS = "220 Ready to start TLS" ReplyGoodbye = "221 Goodbye" ReplyHelo = "250 %s Hello %v" ReplyEhlo = "250-%s Hello %v" ReplyAuthOk = "235 Authentication successful" ReplyCmdOk = "250 Okay" ReplyCmdOkForNow = "250 Okay, I'll believe you for now" ReplyDataAccepted = "250 I've put it in a can" ReplyDataAcceptedWithID = "250 I've put it in a can called %s" ReplyAuthChallenge = "334 %s" ReplyDataSendAway = "354 Send away" ReplyServiceNotAvailable = "421 Service not available now" ReplyMailboxNotAvailable = "450 Not available" ReplyAuthTmpFail = "454 Temporary authentication failure" ReplyInvalidAuthResp = "501 Invalid authentication response" ReplyAuthAborted = "501 Authentication aborted" ReplySyntaxError = "501 Bad: %s" ReplyCmdNotSupported = "502 Not supported" ReplyCmdOutOfSeq = "503 Out of sequence command" ReplyCmdParamNotImplemented = "504 Command parameter not implemented" ReplyAuthRequired = "530 Authentication required" ReplyRejected = "550 Not accepted" ReplyBadAddr = "550 Bad address" ReplyInvalidAuth = "535 Authentication credentials invalid" ReplyTooManyBadCmds = "554 Too many bad commands" ReplyDataRejectedWithID = "554 Not put in a can called %s" ArgTooManyBadCmds = "too many bad commands" )
const TimeFmt = "2006-01-02 15:04:05 -0700"
The time format we log messages in.
Variables ¶
var ( // ErrCmdContainsNonASCII indicates that received command line contains // non 7-bit ASCII characters. ErrCmdContainsNonASCII = e.New("command contains non 7-bit ASCII") // ErrCmdUnrecognized indicates that we failed to extract the actual // SMTP command name from the command line. // Probably reason is non-RFC compliant format of the command line. ErrCmdUnrecognized = e.New("unrecognized command") // ErrCmdHasNoArg indicates that we have found arguments while this // command should not receive any arguments. ErrCmdHasNoArg = e.New("SMTP command does not take an argument") // ErrCmdRequiresArg indicates that this command has one argument, // but we received this command without the argument. ErrCmdRequiresArg = e.New("SMTP command requires an argument") // ErrCmdRequiresArgs indicates that this command has one argument, // but we received this command without the argument. ErrCmdRequiresArgs = e.New("SMTP command requires an arguments") // ErrCmdRequiresAddress indicates that address is required for this command. ErrCmdRequiresAddress = e.New("SMTP command requires an address") // ErrCmdImpropperArgFmt indicates that command arguments has improper format. ErrCmdImpropperArgFmt = e.New("improper argument formatting") )
var DefaultLimits = Limits{ CmdInput: 2 * time.Minute, MsgInput: 10 * time.Minute, ReplyOut: 2 * time.Minute, TLSSetup: 4 * time.Minute, MsgSize: 5 * 1024 * 1024, CmdLine: 512, AuthInput: 12288, BadCmds: 5, NoParams: true, }
The default limits that are applied if you do not specify anything. Two minutes for command input and command replies, ten minutes for receiving messages, and 5 Mbytes of message size.
Note that these limits are not necessarily RFC compliant, although they should be enough for real email clients.
var DefaultServerConfig = &ServerConfig{
PoolSize: 16,
ProcessThreads: 4,
}
DefaultServerConfig is a default values for Server to start with.
var ( // EnvelopeHashDelimiter is a delimiter that should be used // while calculating Hash. EnvelopeHashDelimiter = []byte{0} )
Functions ¶
Types ¶
type AuthConfig ¶
type AuthConfig struct { // Both slices should contain uppercase SASL mechanism names, // e.g. PLAIN, LOGIN, EXTERNAL. Mechanisms []string // supported mechanisms before STARTTLS. TLSMechanisms []string // supported mechanisms after STARTTLS. }
AuthConfig specifies the authentication mechanisms that the server announces as supported.
type AuthFunc ¶
An AuthFunc implements one step of a SASL authentication dialog. The parameter input is the decoded SASL response from the client. Each time it's called, the function should either call Accept/Reject on the connection or send a challenge using AuthChallenge. The input parameter may be nil (the client sent absolutely nothing) or empty (the client sent a '=').
TODO: is a completely blank line an RFC error that should cause the authentication to fail and the connection to abort?
If an AuthFunc is called and does none of these, it is currently equivalent to calling .AuthChallenge(nil). However doing this is considered an error, not a guaranteed API, and may someday have other effects (eg aborting the authentication dialog).
type Command ¶
type Command int
Command represents known SMTP commands in encoded form.
type Config ¶
type Config struct { TLSConfig *tls.Config // TLS configuration if TLS is to be enabled. Limits *Limits // the limits applied to the connection. Auth *AuthConfig // if non-nil, client must authenticate before MAIL FROM. Delay time.Duration // delay every character in replies by this much. SayTime bool // report the time and date in the server banner. LocalName string // the local hostname to use in messages. SftName string // the software name to use in messages. Announce string // extra stuff to announce in greeting banner. }
Config represents the configuration for a Conn. If unset, Limits is DefaultLimits, LocalName is 'localhost', and SftName is 'go-smtpd'.
type Conn ¶
type Conn struct { Config Config // Connection configuration. TLSOn bool // TLS is on in this connection. TLSState tls.ConnectionState // TLS connection state. // contains filtered or unexported fields }
Conn represents an ongoing SMTP connection. The TLS fields are read-only.
Note that this structure cannot be created by hand. Call NewConn.
Conn connections always advertise support for PIPELINING and 8BITMIME. STARTTLS is advertised if the Config passed to NewConn() has a non-nil TLSConfig. AUTH is advertised if the Config has a non-nil Auth.
Conn.Config can be altered to some degree after Conn is created in order to manipulate features on the fly. Note that Conn.Config.Limits is a pointer and so its fields should not be altered unless you know what you're doing and it's your Limits to start with.
func NewConn ¶
NewConn creates a new SMTP conversation from conn, the underlying network connection involved. servername is the server name displayed in the greeting banner. A trace of SMTP commands and responses (but not email messages) will be written to log if it's non-nil.
Log messages start with a character, then a space, then the message. 'r' means read from network (client input), 'w' means written to the network (server replies), '!' means an error, and '#' is tracking information for the start or the end of the connection. Further information is up to whatever is behind 'log' to add.
func (*Conn) Accept ¶
func (c *Conn) Accept()
Accept accepts the current SMTP command, ie gives an appropriate 2xx reply to the client.
func (*Conn) AcceptData ¶
AcceptData accepts a message (ie, a post-DATA blob) with an ID that is reported to the client in the 2xx message. It only does anything when the Conn needs to reply to a DATA blob.
func (*Conn) AcceptMsg ¶
AcceptMsg accepts MAIL FROM, RCPT TO, AUTH, DATA, or message bodies with the given fmt.Printf style message that you supply. The generated message may include embedded newlines for a multi-line reply. This cannot be applied to EHLO/HELO messages; if called for one of them, it is equivalent to Accept().
func (*Conn) AuthChallenge ¶
AuthChallenge sends an authentication challenge to the client. It only works during authentication.
func (*Conn) Authenticate ¶
Authenticate executes a SASL authentication dialog with the client. The given function is invoked until it calls Accept/Reject or the client aborts the dialog (or an error happens).
Note that Authenticate() may return after a network error. In this case calling Next() will immediately return an ABORT event. As a corollary there is no guarantee that your AuthFunc will be called even once.
Using a nil AuthFunc is an error. Authenticate() generously doesn't panic on you and instead immediately rejects the authentication.
func (*Conn) Next ¶
Next returns the next high-level event from the SMTP connection.
Next() guarantees that the SMTP protocol ordering requirements are followed and only returns HELO/EHLO, AUTH, MAIL FROM, RCPT TO, and DATA commands, and the actual message submitted. The caller must reset all accumulated information about a message when it sees either EHLO/HELO or MAIL FROM.
For commands and GOTDATA, the caller may call Reject() or Tempfail() to reject or tempfail the command. Calling Accept() is optional; Next() will do it for you implicitly. It is invalid to call Next() after it has returned a DONE or ABORT event.
For the AUTH command, Next() will return a COMMAND event where Arg is set to the mechanism requested by the client. The mechanism is validated against the list of mechanisms provided in the config. The AUTH command event begins an authentication dialog, during which one or more AUTHRESP events are returned. The first AUTHRESP event contains the initial response from the AUTH command and may be empty. The dialog ends if an AUTHABORT or ABORT event is returned or when the AUTH command is accepted/rejected. Next will not accept the AUTH command automatically. If no reply is sent for an AUTHRESP event, the client receives an empty challenge. Under almost all situations you want to respond to a AUTH command not directly through calling .Next() but by calling .Authenticate() to handle the full details.
Next() does almost no checks on the value of EHLO/HELO, MAIL FROM, and RCPT TO. For MAIL FROM and RCPT TO it requires them to actually be present, but that's about it. It will accept blank EHLO/HELO (ie, no argument at all). It is up to the caller to do more validation and then call Reject() (or Tempfail()) as appropriate. MAIL FROM addresses may be blank (""), indicating the null sender ('<>'). RCPT TO addresses cannot be; Next() will fail those itself.
TLSERROR is returned if the client tried STARTTLS on a TLS-enabled connection but the TLS setup failed for some reason (eg the client only supports SSLv2). The caller can use this to, eg, decide not to offer TLS to that client in the future. No further activity can happen on a connection once TLSERROR is returned; the connection is considered dead and calling .Next() again will yield an ABORT event. The Arg of a TLSERROR event is the TLS error in string form.
func (*Conn) Reject ¶
func (c *Conn) Reject()
Reject rejects the curent SMTP command, ie gives the client an appropriate 5xx message.
func (*Conn) RejectData ¶
RejectData rejects a message with an ID that is reported to the client in the 5xx message.
func (*Conn) RejectMsg ¶
RejectMsg rejects the current SMTP command with the fmt.Printf style message that you supply. The generated message may include embedded newlines for a multi-line reply.
func (*Conn) Tempfail ¶
func (c *Conn) Tempfail()
Tempfail temporarily rejects the current SMTP command, ie it gives the client an appropriate 4xx reply. Properly implemented clients will retry temporary failures later.
func (*Conn) TempfailMsg ¶
TempfailMsg temporarily rejects the current SMTP command with a 4xx code and the fmt.Printf style message that you supply. The generated message may include embedded newlines for a multi-line reply.
type Envelope ¶
Envelope is a wrapper around general message fields and data.
func (*Envelope) HashString ¶
HashString is a Hash variant that encodes a result with hex.
type Event ¶
type Event int
An Event is the sort of event that is returned by Conn.Next().
const ( COMMAND Event = iota AUTHRESP // client sent SASL response AUTHABORT // client aborted SASL dialog by sending '*' GOTDATA // received DATA DONE // client sent QUIT ABORT // input or output error or timeout. TLSERROR // error during TLS setup. Connection is dead. )
The different types of SMTP events returned by Next().
type EventInfo ¶
EventInfo is what Conn.Next() returns to represent events. Cmd and Arg come from ParsedLine.
type Limits ¶
type Limits struct { CmdInput time.Duration // client commands, eg MAIL FROM. MsgInput time.Duration // total time to get the email message itself. ReplyOut time.Duration // server replies to client commands. TLSSetup time.Duration // time limit to finish STARTTLS TLS setup. MsgSize int64 // total size of an email message. CmdLine int64 // command line length https://www.ietf.org/rfc/rfc2821.txt (4.5.3.1). AuthInput int64 // auth response length https://www.ietf.org/rfc/rfc4954.txt (4). BadCmds int // how many unknown commands before abort. NoParams bool // reject MAIL FROM/RCPT TO with parameters. }
Limits has the time(see time.Duration fields of a struct) and message limits for a Conn, as well as some additional options.
A Conn always accepts 'BODY=[7BIT|8BITMIME]' as the sole MAIL FROM parameter, since it advertises support for 8BITMIME.
type ParsedLine ¶
type ParsedLine struct { Cmd Command Arg string // Params is K=V for ESMTP MAIL FROM and RCPT TO // or the initial SASL response for AUTH Params string }
ParsedLine represents a parsed SMTP command line. Err is set if there was an error, empty otherwise. Cmd may be BadCmd or a command, even if there was an error.
See http://www.ietf.org/rfc/rfc1869.txt for the general discussion of params. We do not parse them.
func ParseCmd ¶
func ParseCmd(line string) (*ParsedLine, error)
ParseCmd parses a SMTP command line and returns the result. The line should have the ending CRLF already removed.
type Server ¶
type Server struct {
// contains filtered or unexported fields
}
Server describes a general data structures required to start SMTP server.
type ServerConfig ¶
ServerConfig is a configuration for Server.