Documentation ¶
Index ¶
- Constants
- Variables
- func MimeHeaderDecode(str string) string
- func NewClient(conn net.Conn, clientID uint64, logger log.ChameleonLogger, ...) *client
- type Address
- type ClientState
- type EnhancedStatusCode
- type Envelope
- func (e *Envelope) Len() int
- func (e *Envelope) NewReader() io.Reader
- func (e *Envelope) ParseHeaders() error
- func (e *Envelope) PopRcpt() Address
- func (e *Envelope) PushRcpt(addr Address)
- func (e *Envelope) Reseed(remoteIP string, clientID uint64)
- func (e *Envelope) ResetTransaction()
- func (e *Envelope) String() string
- type EnvelopePool
- type Errors
- type Handler
- type Pool
- func (p *Pool) Borrow(conn net.Conn, clientID uint64, logger log.ChameleonLogger, ep *EnvelopePool) (Poolable, error)
- func (p *Pool) GetActiveClientsCount() int
- func (p *Pool) IsShuttingDown() bool
- func (p *Pool) Return(c Poolable)
- func (p *Pool) SetTimeout(duration time.Duration)
- func (p *Pool) ShutdownState()
- func (p *Pool) ShutdownWait()
- func (p *Pool) Start()
- type Poolable
- type Response
- type Responses
- type Result
- type Server
- type ServerConfig
- type ServerTLSConfig
Constants ¶
const ( // The client has connected, and is awaiting our first response ClientGreeting = iota // We have responded to the client's connection and are awaiting a command ClientCmd // We have received the sender and recipient information ClientData // We have agreed with the client to secure the connection over TLS ClientStartTLS // Server will shutdown, client to shutdown on next command turn ClientShutdown )
const ( // ClassSuccess specifies that the DSN is reporting a positive delivery // action. Detail sub-codes may provide notification of // transformations required for delivery. ClassSuccess = 2 // ClassTransientFailure - a persistent transient failure is one in which the message as // sent is valid, but persistence of some temporary condition has // caused abandonment or delay of attempts to send the message. // If this code accompanies a delivery failure report, sending in // the future may be successful. ClassTransientFailure = 4 // ClassPermanentFailure - a permanent failure is one which is not likely to be resolved // by resending the message in the current form. Some change to // the message or the destination must be made for successful // delivery. ClassPermanentFailure = 5 )
const ( OtherStatus = ".0.0" OtherAddressStatus = ".1.0" BadDestinationMailboxAddress = ".1.1" BadDestinationSystemAddress = ".1.2" BadDestinationMailboxAddressSyntax = ".1.3" DestinationMailboxAddressAmbiguous = ".1.4" DestinationMailboxAddressValid = ".1.5" MailboxHasMoved = ".1.6" BadSendersMailboxAddressSyntax = ".1.7" BadSendersSystemAddress = ".1.8" OtherOrUndefinedMailboxStatus = ".2.0" MailboxDisabled = ".2.1" MailboxFull = ".2.2" MessageLengthExceedsAdministrativeLimit = ".2.3" MailingListExpansionProblem = ".2.4" OtherOrUndefinedMailSystemStatus = ".3.0" MailSystemFull = ".3.1" SystemNotAcceptingNetworkMessages = ".3.2" SystemNotCapableOfSelectedFeatures = ".3.3" MessageTooBigForSystem = ".3.4" OtherOrUndefinedNetworkOrRoutingStatus = ".4.0" NoAnswerFromHost = ".4.1" BadConnection = ".4.2" RoutingServerFailure = ".4.3" UnableToRoute = ".4.4" NetworkCongestion = ".4.5" RoutingLoopDetected = ".4.6" DeliveryTimeExpired = ".4.7" OtherOrUndefinedProtocolStatus = ".5.0" InvalidCommand = ".5.1" SyntaxError = ".5.2" TooManyRecipients = ".5.3" InvalidCommandArguments = ".5.4" WrongProtocolVersion = ".5.5" OtherOrUndefinedMediaError = ".6.0" MediaNotSupported = ".6.1" ConversionRequiredAndProhibited = ".6.2" ConversionRequiredButNotSupported = ".6.3" ConversionWithLossPerformed = ".6.4" ConversionFailed = ".6.5" )
DefaultMap contains defined default codes (RfC 3463)
const ( CommandVerbMaxLength = 16 CommandLineMaxLength = 1024 // Number of allowed unrecognized commands before we terminate the connection MaxUnrecognizedCommands = 5 )
const ( // server has just been created ServerStateNew = iota // Server has just been stopped ServerStateStopped // Server has been started and is running ServerStateRunning // Server could not start due to an error ServerStateStartError )
const SP = " "
space char
Variables ¶
var ( LineLimitExceeded = errors.New("maximum line length exceeded") MessageSizeExceeded = errors.New("maximum message size exceeded") )
var Dec mime.WordDecoder
A WordDecoder decodes MIME headers containing RFC 2047 encoded-words. Used by the MimeHeaderDecode function. It's exposed public so that an alternative decoder can be set, eg Gnu iconv by importing the mail/inconv package. Another alternative would be to use https://godoc.org/golang.org/x/text/encoding
var (
ErrPoolShuttingDown = errors.New("server pool: shutting down")
)
var TLSCiphers = map[string]uint16{ "TLS_RSA_WITH_3DES_EDE_CBC_SHA": tls.TLS_RSA_WITH_3DES_EDE_CBC_SHA, "TLS_RSA_WITH_AES_128_CBC_SHA": tls.TLS_RSA_WITH_AES_128_CBC_SHA, "TLS_RSA_WITH_AES_256_CBC_SHA": tls.TLS_RSA_WITH_AES_256_CBC_SHA, "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA": tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA": tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, "TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA": tls.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA": tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA": tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, "TLS_RSA_WITH_RC4_128_SHA": tls.TLS_RSA_WITH_RC4_128_SHA, "TLS_RSA_WITH_AES_128_GCM_SHA256": tls.TLS_RSA_WITH_AES_128_GCM_SHA256, "TLS_RSA_WITH_AES_256_GCM_SHA384": tls.TLS_RSA_WITH_AES_256_GCM_SHA384, "TLS_ECDHE_ECDSA_WITH_RC4_128_SHA": tls.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, "TLS_ECDHE_RSA_WITH_RC4_128_SHA": tls.TLS_ECDHE_RSA_WITH_RC4_128_SHA, "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256": tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384": tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384": tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, }
https://golang.org/pkg/crypto/tls/#pkg-constants Ciphers introduced before Go 1.7 are listed here, ciphers since Go 1.8, see tls_go1.8.go ....... since Go 1.13, see tls_go1.13.go
var TLSClientAuthTypes = map[string]tls.ClientAuthType{ "NoClientCert": tls.NoClientCert, "RequestClientCert": tls.RequestClientCert, "RequireAnyClientCert": tls.RequireAnyClientCert, "VerifyClientCertIfGiven": tls.VerifyClientCertIfGiven, "RequireAndVerifyClientCert": tls.RequireAndVerifyClientCert, }
https://golang.org/pkg/crypto/tls/#ClientAuthType
var TLSCurves = map[string]tls.CurveID{ "P256": tls.CurveP256, "P384": tls.CurveP384, "P521": tls.CurveP521, }
https://golang.org/pkg/crypto/tls/#CurveID
var TLSProtocols = map[string]uint16{ "tls1.0": tls.VersionTLS10, "tls1.1": tls.VersionTLS11, "tls1.2": tls.VersionTLS12, }
Functions ¶
func MimeHeaderDecode ¶
MimeHeaderDecode converts 7 bit encoded mime header strings to UTF-8
func NewClient ¶
func NewClient(conn net.Conn, clientID uint64, logger log.ChameleonLogger, envelope *EnvelopePool) *client
NewClient allocates a new client.
Types ¶
type Address ¶
type Address struct { // User is local part User string // Host is the domain Host string // ADL is at-domain list if matched ADL []string // PathParams contains any ESTMP parameters that were matched PathParams [][]string // NullPath is true if <> was received NullPath bool // Quoted indicates if the local-part needs quotes Quoted bool // IP stores the IP Address, if the Host is an IP IP net.IP // DisplayName is a label before the address (RFC5322) DisplayName string // DisplayNameQuoted is true when DisplayName was quoted DisplayNameQuoted bool }
Address encodes an email address of the form `<user@host>`
func NewAddress ¶
NewAddress takes a string of an RFC 5322 address of the form "Gogh Fir <gf@example.com>" or "foo@example.com".
func (*Address) IsPostmaster ¶
type ClientState ¶
type ClientState int
ClientState indicates which part of the SMTP transaction a given client is in.
type EnhancedStatusCode ¶
type EnhancedStatusCode struct { Class class SubjectDetailCode subjectDetail }
EnhancedStatus are the ones that look like 2.1.0
func (EnhancedStatusCode) String ¶
func (e EnhancedStatusCode) String() string
String returns a string representation of EnhancedStatus
type Envelope ¶
type Envelope struct { // Remote IP address RemoteIP string // Message sent in EHLO command Helo string // Sender MailFrom Address // Recipients RcptTo []Address // Data stores the header and message body Data bytes.Buffer // Subject stores the subject of the email, extracted and decoded after calling ParseHeaders() Subject string // TLS is true if the email was received using a TLS connection TLS bool // Header stores the results from ParseHeaders() Header textproto.MIMEHeader // Values hold the values generated when processing the envelope by the backend Values map[string]interface{} // Hashes of each email on the rcpt Hashes []string // additional delivery header that may be added DeliveryHeader string // Email(s) will be queued with this id QueuedId string // ESMTP: true if EHLO was used ESMTP bool // When locked, it means that the envelope is being processed by the backend sync.Mutex }
Envelope of Email represents a single SMTP message.
func NewEnvelope ¶
func (*Envelope) Len ¶
Len returns the number of bytes that would be in the reader returned by NewReader()
func (*Envelope) NewReader ¶
NewReader returns a new reader for reading the email contents, including the delivery headers
func (*Envelope) ParseHeaders ¶
ParseHeaders parses the headers into Header field of the Envelope struct. Data buffer must be full before calling. It assumes that at most 30kb of email data can be a header Decoding of encoding to UTF is only done on the Subject, where the result is assigned to the Subject field
func (*Envelope) ResetTransaction ¶
func (e *Envelope) ResetTransaction()
ResetTransaction is called when the transaction is reset (keeping the connection open)
type EnvelopePool ¶
type EnvelopePool struct {
// contains filtered or unexported fields
}
func NewEnvelopePool ¶
func NewEnvelopePool(poolSize int) *EnvelopePool
func (*EnvelopePool) Borrow ¶
func (p *EnvelopePool) Borrow(remoteAddr string, clientID uint64) *Envelope
func (*EnvelopePool) Return ¶
func (p *EnvelopePool) Return(e *Envelope)
Return returns an envelope back to the envelope pool Make sure that envelope finished processing before calling this
type Handler ¶
type Handler interface { ValidateRcpt(e *Envelope, logger log.ChameleonLogger) error Handle(e *Envelope, logger log.ChameleonLogger) Result }
type Pool ¶
type Pool struct { ShutdownChan chan int // contains filtered or unexported fields }
Pool holds Clients.
func (*Pool) Borrow ¶
func (p *Pool) Borrow(conn net.Conn, clientID uint64, logger log.ChameleonLogger, ep *EnvelopePool) (Poolable, error)
Borrow a Client from the pool. Will block if len(activeClients) > maxClients
func (*Pool) GetActiveClientsCount ¶
Gets the number of active clients that are currently out of the pool and busy serving
func (*Pool) IsShuttingDown ¶
returns true if the pool is shutting down
func (*Pool) SetTimeout ¶
set a timeout for all lent clients
func (*Pool) ShutdownState ¶
func (p *Pool) ShutdownState()
Lock the pool from borrowing then remove all active clients each active client's timeout is lowered to 1 sec and notified to stop accepting commands
func (*Pool) ShutdownWait ¶
func (p *Pool) ShutdownWait()
type Poolable ¶
type Poolable interface {
// contains filtered or unexported methods
}
a struct can be pooled if it has the following interface
type Response ¶
type Response struct { EnhancedCode subjectDetail BasicCode int Class class // Comment is optional Comment string // contains filtered or unexported fields }
Response type for Stringer interface
type Responses ¶
type Responses struct { // The 500's FailLineTooLong *Response FailNestedMailCmd *Response FailNoSenderDataCmd *Response FailNoRecipientsDataCmd *Response FailUnrecognizedCmd *Response FailMaxUnrecognizedCmd *Response FailSyntaxError *Response FailReadLimitExceededDataCmd *Response FailMessageSizeExceeded *Response FailReadErrorDataCmd *Response FailPathTooLong *Response FailInvalidAddress *Response FailLocalPartTooLong *Response FailDomainTooLong *Response FailBackendNotRunning *Response FailBackendTransaction *Response FailBackendTimeout *Response FailRcptCmd *Response // The 400's ErrorTooManyRecipients *Response ErrorRelayDenied *Response ErrorShutdown *Response // The 200's SuccessMailCmd *Response SuccessRcptCmd *Response SuccessResetCmd *Response SuccessVerifyCmd *Response SuccessNoopCmd *Response SuccessQuitCmd *Response SuccessDataCmd *Response SuccessStartTLSCmd *Response SuccessMessageQueued *Response }
Responses has some already pre-constructed responses
var ( // Canned is to be read-only, except in the init() function Canned Responses )
type Result ¶
type Result interface { fmt.Stringer // Code should return the SMTP code associated with this response, ie. `250` Code() int }
Result represents a response to an SMTP client after receiving DATA. The String method should return an SMTP message ready to send back to the client, for example `250 OK: Message received`.
type Server ¶
type Server struct {
// contains filtered or unexported fields
}
Server listens for SMTP clients on the port specified in its config
func NewServer ¶
func NewServer(sc *ServerConfig, handler Handler, log log.ChameleonLogger) (*Server, error)
Creates and returns a new ready-to-run Server from a ServerConfig configuration
func (*Server) GetActiveClientsCount ¶
func (*Server) SetAllowedHosts ¶
Set the allowed hosts for the server
type ServerConfig ¶
type ServerConfig struct { // TLS Configuration TLS *ServerTLSConfig `yaml:"tls,omitempty"` // Hostname will be used in the server's reply to HELO/EHLO // If TLS enabled, make sure that the Hostname matches the cert // Defaults to os.Hostname() // Hostname will also be used to fill the 'Host' property when the "RCPT TO" address is addressed to just <postmaster> Hostname string `yaml:"hostname"` // Listen interface specified in <ip>:<port> - defaults to 127.0.0.1:2525 ListenInterface string `yaml:"listen-interface"` // MaxSize is the maximum size of an email that will be accepted for delivery // Defaults to 10 Mebibytes MaxSize int64 `yaml:"max-mail-size"` // Timeout specifies the connection timeout in seconds. Defaults to 30 Timeout int `yaml:"timeout"` // MaxClients controls how many maximum clients we can handle at once // Defaults to defaultMaxClients MaxClients int `yaml:"max-clients"` // AllowedHosts lists which hosts to accept email for. Defaults to os.Hostname AllowedHosts []string `yaml:"allowed-hosts"` // XClientOn when using a proxy such as Nginx, XCLIENT command is used to pass the // original client's IP address & client's HELO XClientOn bool `yaml:"xclient-on,omitempty"` }
ServerConfig specifies config options for a single server
func (*ServerConfig) SetDefaults ¶
func (c *ServerConfig) SetDefaults() error
SetDefaults fills in default server settings for values that were not configured The defaults are: * Server listening to 127.0.0.1:2525 * use your hostname to determine your which hosts to accept email for * 100 maximum clients * 10MB max message size * log to Stderr, * log level set to "`debug`" * timeout to 30 sec * Backend configured with the following processors: `HeadersParser|Header|Debugger` where it will log the received emails.
type ServerTLSConfig ¶
type ServerTLSConfig struct { // TLS Protocols to use. [0] = min, [1]max // Use Go's default if empty Protocols []string `yaml:"protocols,omitempty"` // TLS Ciphers to use. // Use Go's default if empty Ciphers []string `yaml:"ciphers,omitempty"` // TLS Curves to use. // Use Go's default if empty Curves []string `yaml:"curves,omitempty"` // PrivateKeyFile path to cert private key in PEM format. PrivateKeyFile string `yaml:"private-key-file"` // PublicKeyFile path to cert (public key) chain in PEM format. PublicKeyFile string `yaml:"public-key-file"` // TLS Root cert authorities to use. "A PEM encoded CA's certificate file. // Defaults to system's root CA file if empty RootCAs string `yaml:"root-cas-file,omitempty"` // declares the policy the server will follow for TLS Client Authentication. // Use Go's default if empty ClientAuthType string `yaml:"client-auth-type,omitempty"` // controls whether the server selects the // client's most preferred cipher suite PreferServerCipherSuites bool `yaml:"prefer-server-cipher-suites,omitempty"` // StartTLSOn should we offer STARTTLS command. Cert must be valid. // False by default StartTLSOn bool `yaml:"start-tls-on,omitempty"` // AlwaysOn run this server as a pure TLS server, i.e. SMTPS AlwaysOn bool `yaml:"always-on,omitempty"` // contains filtered or unexported fields }
func (*ServerTLSConfig) SetDefaults ¶
func (stc *ServerTLSConfig) SetDefaults() error
func (*ServerTLSConfig) Validate ¶
func (stc *ServerTLSConfig) Validate() error