Documentation ¶
Overview ¶
A Go library for milter support
Example ¶
package main import ( "io/ioutil" "log" "github.com/mschneider82/milter" ) // A Session embetted the SessionHandler Interface type Session struct { milter.DefaultSession } // Body in this case we just want to show some chars of the mail // All other Interactions are done by DefaultSession implementation func (s *Session) Body(m *milter.Modifier) (milter.Response, error) { b, _ := ioutil.ReadAll(s.Message) log.Printf("Mail's first 100 chars: %s", string(b[0:100])) return milter.RespAccept, nil } func main() { panichandler := func(e error) { log.Printf("Panic happend: %s\n", e.Error()) } setsymlist := make(milter.RequestMacros) setsymlist[milter.SMFIM_CONNECT] = []milter.Macro{milter.MACRO_DAEMON_NAME, milter.Macro("{client_addr}")} milterfactory := func() (milter.SessionHandler, milter.OptAction, milter.OptProtocol, milter.RequestMacros) { return &Session{}, milter.OptAllActions, // BitMask for wanted Actions 0, // BitMask for unwanted SMTP Parts; 0 = nothing to opt out setsymlist // optional: can be nil } m := milter.New(milterfactory, milter.WithTCPListener("127.0.0.1:12349"), milter.WithLogger(milter.StdOutLogger), milter.WithPanicHandler(panichandler), ) err := m.Run() if err != nil { log.Fatalf("Error: %s", err.Error()) } defer m.Close() }
Output:
Index ¶
- Constants
- Variables
- func Close()
- type CustomLogger
- type CustomResponse
- type DefaultSession
- func (e *DefaultSession) Body(m *Modifier) (Response, error)
- func (e *DefaultSession) BodyChunk(chunk []byte, m *Modifier) (Response, error)
- func (e *DefaultSession) Connect(name, family string, port uint16, ip net.IP, m *Modifier) (Response, error)
- func (e *DefaultSession) Disconnect()
- func (e *DefaultSession) Header(name, value string, m *Modifier) (Response, error)
- func (e *DefaultSession) Headers(headers textproto.MIMEHeader, m *Modifier) (Response, error)
- func (e *DefaultSession) Helo(name string, m *Modifier) (Response, error)
- func (e *DefaultSession) Init(sid, mid string)
- func (e *DefaultSession) MailFrom(from string, m *Modifier) (Response, error)
- func (e *DefaultSession) RcptTo(rcptTo string, m *Modifier) (Response, error)
- type ListenerOption
- type Macro
- type Message
- type MilterFactory
- type Modifier
- func (m *Modifier) AddHeader(name, value string) error
- func (m *Modifier) AddRecipient(r string) error
- func (m *Modifier) ChangeFrom(value string) error
- func (m *Modifier) ChangeHeader(index int, name, value string) error
- func (m *Modifier) DeleteRecipient(r string) error
- func (m *Modifier) InsertHeader(index int, name, value string) error
- func (m *Modifier) Quarantine(reason string) error
- func (m *Modifier) ReplaceBody(body []byte) error
- type OptAction
- type OptProtocol
- type Option
- type RequestMacros
- type Response
- type Server
- type SessionHandler
- type SimpleResponse
- type Stage
Examples ¶
Constants ¶
const ( SMFIC_ABORT = 'A' // Abort current filter checks SMFIC_BODY = 'B' // Body chunk SMFIC_CONNECT = 'C' // SMTP connection information SMFIC_MACRO = 'D' // Define macros SMFIC_BODYEOB = 'E' // End of body marker SMFIC_HELO = 'H' // HELO/EHLO name SMFIC_QUIT_NC = 'K' // QUIT but new connection follows SMFIC_HEADER = 'L' // Mail header SMFIC_MAIL = 'M' // MAIL FROM: information SMFIC_EOH = 'N' // End of headers marker SMFIC_OPTNEG = 'O' // Option negotiation SMFIC_QUIT = 'Q' // Quit milter communication SMFIC_RCPT = 'R' // RCPT TO: information SMFIC_DATA = 'T' // DATA SMFIC_UNKNOWN = 'U' // Any unknown command SMFIR_ADDRCPT = '+' // Add recipient (modification action) SMFIR_DELRCPT = '-' // Remove recipient (modification action) SMFIR_ADDRCPT_PAR = '2' // Add recipient (incl. ESMTP args) SMFIR_SHUTDOWN = '4' // 421: shutdown (internal to MTA) SMFIR_ACCEPT = 'a' // Accept message completely (accept/reject action) SMFIR_REPLBODY = 'b' // Replace body (modification action) SMFIR_CONTINUE = 'c' // Accept and keep processing (accept/reject action) SMFIR_DISCARD = 'd' // Set discard flag for entire message (accept/reject action) SMFIR_CHGFROM = 'e' // Change envelope sender (from) SMFIR_CONN_FAIL = 'f' // Cause a connection failure SMFIR_ADDHEADER = 'h' // Add header (modification action) SMFIR_INSHEADER = 'i' // Insert header SMFIR_SETSYMLIST = 'l' // Set list of symbols (macros) SMFIR_CHGHEADER = 'm' // Change header (modification action) SMFIR_PROGRESS = 'p' // Progress (asynchronous action) SMFIR_QUARANTINE = 'q' // Quarantine message (modification action) SMFIR_REJECT = 'r' // Reject command/recipient with a 5xx (accept/reject action) SMFIR_SKIP = 's' // Skip SMFIR_TEMPFAIL = 't' // Reject command/recipient with a 4xx (accept/reject action) SMFIR_REPLYCODE = 'y' // Send specific Nxx reply message (accept/reject action) SMFIA_INET = '4' SMFIA_INET6 = '6' SMFIA_UNIX = 'L' SMFIA_UNKNOWN = 'U' )
const ( SMFIS_KEEP = uint32(20) SMFIS_ABORT = uint32(21) SMFIS_OPTIONS = uint32(22) SMFIS_NOREPLY = uint32(7) )
const ( RespAccept = SimpleResponse(SMFIR_ACCEPT) RespContinue = SimpleResponse(SMFIR_CONTINUE) RespDiscard = SimpleResponse(SMFIR_DISCARD) RespReject = SimpleResponse(SMFIR_REJECT) RespTempFail = SimpleResponse(SMFIR_TEMPFAIL) )
Define standard responses with no data
Variables ¶
var ( ErrCloseSession = errors.New("Stop current milter processing") ErrMacroNoData = errors.New("Macro definition with no data") ErrNoListenAddr = errors.New("no listen addr specified") )
pre-defined errors
var NopLogger = CustomLogger(nopLogger{})
NopLogger can be used to discard all logs caused by milter library
var StdOutLogger = CustomLogger(stdoutLogger{})
StdOutLogger is the default logger used if no Logger was supplied
Functions ¶
Types ¶
type CustomLogger ¶
type CustomLogger interface {
Printf(format string, v ...interface{})
}
CustomLogger is a interface to inject a custom logger
type CustomResponse ¶
type CustomResponse struct {
// contains filtered or unexported fields
}
CustomResponse is a response instance used by callback handlers to indicate how the milter should continue processing of current message
func NewResponse ¶
func NewResponse(code byte, data []byte) *CustomResponse
NewResponse generates a new CustomResponse suitable for WritePacket
func NewResponseStr ¶
func NewResponseStr(code byte, data string) *CustomResponse
NewResponseStr generates a new CustomResponse with string payload code should be SMFIR_REPLYCODE == 'y' data can be "550 5.2.0 mailbox unavailable."
func (*CustomResponse) Continue ¶
func (c *CustomResponse) Continue() bool
Continue returns false if milter chain should be stopped, true otherwise
func (*CustomResponse) Response ¶
func (c *CustomResponse) Response() *Message
Response returns message instance with data
type DefaultSession ¶
type DefaultSession struct { SID string // Session ID MID string // Mail ID From string ClientName string HeloName string ClientIP net.IP Rcpts []string MessageHeaders textproto.MIMEHeader Message *bytes.Buffer }
A DefaultSession can be used as a basic implementation for SessionHandler Interface It has already a From and Rcpts[] field, mostly used to embett in your own Session struct.
func (*DefaultSession) Body ¶
func (e *DefaultSession) Body(m *Modifier) (Response, error)
Body is called when email message body has been sent
func (*DefaultSession) BodyChunk ¶
func (e *DefaultSession) BodyChunk(chunk []byte, m *Modifier) (Response, error)
accept body chunk
func (*DefaultSession) Disconnect ¶
func (e *DefaultSession) Disconnect()
func (*DefaultSession) Header ¶
func (e *DefaultSession) Header(name, value string, m *Modifier) (Response, error)
handle headers one by one
func (*DefaultSession) Headers ¶
func (e *DefaultSession) Headers(headers textproto.MIMEHeader, m *Modifier) (Response, error)
at end of headers initialize message buffer and add headers to it
func (*DefaultSession) Helo ¶
func (e *DefaultSession) Helo(name string, m *Modifier) (Response, error)
func (*DefaultSession) Init ¶
func (e *DefaultSession) Init(sid, mid string)
https://github.com/mschneider82/milter/blob/master/interface.go
type ListenerOption ¶
type ListenerOption interface {
// contains filtered or unexported methods
}
An ListenerOption configures a Server using the functional options paradigm popularized by Rob Pike.
func WithListener ¶
func WithListener(listener net.Listener) ListenerOption
WithListener adds an Listener
func WithTCPListener ¶
func WithTCPListener(address string) ListenerOption
WithTCPListener e.g. "127.0.0.1:12349"
func WithUnixSocket ¶
func WithUnixSocket(file string) ListenerOption
WithUnixSocket e.g. "/var/spool/postfix/var/run/milter/milter.sock" make sure that the file does not exist!
type Macro ¶
type Macro string
Macro http://www.postfix.org/MILTER_README.html#macros
const ( MACRO_QUEUEID Macro = "i" // DATA, EOH, EOM - Queue ID, also Postfix queue file name MACRO_MYHOSTNAME Macro = "j" // Always - Value of myhostname MACRO_VALIDCLIENTNAME Macro = "_" // Always - The validated client name and address MACRO_AUTH_SASL_LOGINNAME Macro = "{auth_authen}" // MAIL, DATA, EOH, EOM - SASL login name MACRO_AUTH_SASL_SENDER Macro = "{auth_author}" // MAIL, DATA, EOH, EOM - SASL sender MACRO_AUTH_SASL_LOGINMETHOD Macro = "{auth_type}" // MAIL, DATA, EOH, EOM - SASL login method MACRO_REMOTECLIENTIP Macro = "{client_addr}" // Always - Remote client IP address MACRO_CLIENT_CONNECTIONS Macro = "{client_connections}" // CONNECT - Connection concurrency for this client (zero if the client is excluded from all smtpd_client_* limits). MACRO_CLIENT_NAME Macro = "{client_name}" // Always - Remote client hostname address → name lookup or name → address verification fails: "unknown" MACRO_CLIENT_TCPPORT Macro = "{client_port}" // Always (Postfix ≥2.5) - Remote client TCP port MACRO_CLIENT_PTR Macro = "{client_ptr}" // CONNECT, HELO, MAIL, DATA - Client name from address → name lookup address → name lookup fails: "unknown" MACRO_CLIENT_TLS_CERT_ISSUER Macro = "{cert_issuer}" // HELO, MAIL, DATA, EOH, EOM - TLS client certificate issuer MACRO_CLIENT_TLS_CERT_SUBJECT Macro = "{cert_subject}" // HELO, MAIL, DATA, EOH, EOM - TLS client certificate subject MACRO_CLIENT_TLS_CIPHER_BITS Macro = "{cipher_bits}" // HELO, MAIL, DATA, EOH, EOM - TLS session key size MACRO_CLIENT_TLS_CIPHER Macro = "{cipher}" // HELO, MAIL, DATA, EOH, EOM - TLS cipher MACRO_DAEMON_ADDR Macro = "{daemon_addr}" // Always (Postfix ≥3.2) - Local server IP address MACRO_DAEMON_NAME Macro = "{daemon_name}" // Always - value of milter_macro_daemon_name MACRO_DAEMON_PORT Macro = "{daemon_port}" // Always (Postfix ≥3.2) -Local server TCP port MACRO_MAIL_ADDR Macro = "{mail_addr}" // MAIL - Sender address MACRO_MAIL_HOST Macro = "{mail_host}" // MAIL (Postfix ≥ 2.6, only with smtpd_milters) - Sender next-hop destination MACRO_MAIL_MAILER Macro = "{mail_mailer}" // MAIL (Postfix ≥ 2.6, only with smtpd_milters) - Sender mail delivery transport MACRO_RCPT_ADDR Macro = "{rcpt_addr}" // RCPT - Recipient address with rejected recipient: descriptive text MACRO_RCPT_HOST Macro = "{rcpt_host}" // RCPT (Postfix ≥ 2.6, only with smtpd_milters) - Recipient next-hop destination with rejected recipient: enhanced status code MACRO_RCPT_MAILER Macro = "{rcpt_mailer}" // RCPT (Postfix ≥ 2.6, only with smtpd_milters) - Recipient mail delivery transport With Protocol Stage: rejected recipient: "error" MACRO_TLS_VERSION Macro = "{tls_version}" // HELO, MAIL, DATA, EOH, EOM - TLS protocol version MACRO_V Macro = "v" // Always - value of milter_macro_v (default: $mail_name $mail_version) )
type MilterFactory ¶
type MilterFactory func() (SessionHandler, OptAction, OptProtocol, RequestMacros)
MilterFactory initializes milter options multiple options can be set using a bitmask
type Modifier ¶
type Modifier struct { Macros map[string]string Headers textproto.MIMEHeader // contains filtered or unexported fields }
Modifier provides access to Macros, Headers and Body data to callback handlers. It also defines a number of functions that can be used by callback handlers to modify processing of the email message
func (*Modifier) AddRecipient ¶
AddRecipient appends a new envelope recipient for current message
func (*Modifier) ChangeFrom ¶
ChangeFrom replaces the FROM envelope header with a new one
func (*Modifier) ChangeHeader ¶
ChangeHeader replaces the header at the specified position with a new one
func (*Modifier) DeleteRecipient ¶
DeleteRecipient removes an envelope recipient address from message
func (*Modifier) InsertHeader ¶
InsertHeader inserts the header at the pecified position
func (*Modifier) Quarantine ¶
Quarantine a message by giving a reason to hold it
func (*Modifier) ReplaceBody ¶
ReplaceBody substitutes message body with provided body
type OptAction ¶
type OptAction uint32
OptAction sets which actions the milter wants to perform. Multiple options can be set using a bitmask.
const ( // set which actions the milter wants to perform OptNone OptAction = 0x00 /* SMFIF_NONE no flags */ OptAddHeader OptAction = 0x01 /* SMFIF_ADDHDRS filter may add headers */ OptChangeBody OptAction = 0x02 /* SMFIF_CHGBODY filter may replace body */ OptAddRcpt OptAction = 0x04 /* SMFIF_ADDRCPT filter may add recipients */ OptRemoveRcpt OptAction = 0x08 /* SMFIF_DELRCPT filter may delete recipients */ OptChangeHeader OptAction = 0x10 /* SMFIF_CHGHDRS filter may change/delete headers */ OptQuarantine OptAction = 0x20 /* SMFIF_QUARANTINE filter may quarantine envelope */ OptChangeFrom OptAction = 0x40 /* SMFIF_CHGFROM filter may change "from" (envelope sender) */ OptAddRcptPartial OptAction = 0x80 /* SMFIF_ADDRCPT_PAR filter may add recipients, including ESMTP parameter to the envelope.*/ OptSetSymList OptAction = 0x100 /* SMFIF_SETSYMLIST filter can send set of symbols (macros) that it wants */ // OptAllActions SMFI_CURR_ACTS Set of all actions in the current milter version */ OptAllActions OptAction = OptAddHeader | OptChangeBody | OptAddRcpt | OptRemoveRcpt | OptChangeHeader | OptQuarantine | OptChangeFrom | OptAddRcptPartial | OptSetSymList )
type OptProtocol ¶
type OptProtocol uint32
OptProtocol masks out unwanted parts of the SMTP transaction. Multiple options can be set using a bitmask.
const ( // mask out unwanted parts of the SMTP transaction OptNoConnect OptProtocol = 0x01 /* SMFIP_NOCONNECT MTA should not send connect info */ OptNoHelo OptProtocol = 0x02 /* SMFIP_NOHELO MTA should not send HELO info */ OptNoMailFrom OptProtocol = 0x04 /* SMFIP_NOMAIL MTA should not send MAIL info */ OptNoRcptTo OptProtocol = 0x08 /* SMFIP_NORCPT MTA should not send RCPT info */ OptNoBody OptProtocol = 0x10 /* SMFIP_NOBODY MTA should not send body (chunk) */ OptNoHeaders OptProtocol = 0x20 /* SMFIP_NOHDRS MTA should not send headers */ OptNoEOH OptProtocol = 0x40 /* SMFIP_NOEOH MTA should not send EOH */ OptNrHdr OptProtocol = 0x80 /* SMFIP_NR_HDR SMFIP_NOHREPL No reply for headers */ OptNoUnknown OptProtocol = 0x100 /* SMFIP_NOUNKNOWN MTA should not send unknown commands */ OptNoData OptProtocol = 0x200 /* SMFIP_NODATA MTA should not send DATA */ OptSkip OptProtocol = 0x400 /* SMFIP_SKIP MTA understands SMFIS_SKIP */ OptRcptRej OptProtocol = 0x800 /* SMFIP_RCPT_REJ MTA should also send rejected RCPTs */ OptNrConn OptProtocol = 0x1000 /* SMFIP_NR_CONN No reply for connect */ OptNrHelo OptProtocol = 0x2000 /* SMFIP_NR_HELO No reply for HELO */ OptNrMailFrom OptProtocol = 0x4000 /* SMFIP_NR_MAIL No reply for MAIL */ OptNrRcptTo OptProtocol = 0x8000 /* SMFIP_NR_RCPT No reply for RCPT */ OptNrData OptProtocol = 0x10000 /* SMFIP_NR_DATA No reply for DATA */ OptNrUnknown OptProtocol = 0x20000 /* SMFIP_NR_UNKN No reply for UNKNOWN */ OptNrEOH OptProtocol = 0x40000 /* SMFIP_NR_EOH No reply for eoh */ OptNrBody OptProtocol = 0x80000 /* SMFIP_NR_BODY No reply for body chunk */ OptHdrLeadSpace OptProtocol = 0x100000 /* SMFIP_HDR_LEADSPC header value leading space */ OptMDS256K OptProtocol = 0x10000000 /* SMFIP_MDS_256K MILTER_MAX_DATA_SIZE=256K */ OptMDS1M OptProtocol = 0x20000000 /* SMFIP_MDS_1M MILTER_MAX_DATA_SIZE=1M */ )
type Option ¶
type Option interface {
// contains filtered or unexported methods
}
An Option configures a Server using the functional options paradigm popularized by Rob Pike.
func WithPanicHandler ¶
WithPanicHandler Adds the error panic handler Multiple panic handlers are supported
type RequestMacros ¶
RequestMacros - Also known as SetSymList: the list of macros that the milter wants to receive from the MTA for a protocol Stage (stages has the prefix SMFIM_). if nil, then there are not Macros requested and the default macros from MTA are used.
type Response ¶
Response represents a response structure returned by callback handlers to indicate how the milter server should proceed
type Server ¶
type Server struct {
// contains filtered or unexported fields
}
Server Milter for handling and processing incoming connections support panic handling via ErrHandler couple of func(error) could be provided for handling error
func New ¶
func New(milterfactory MilterFactory, lopt ListenerOption, opts ...Option) *Server
New generates a new Server
type SessionHandler ¶
type SessionHandler interface { // Init is called on begin of a new Mail, before Connect() and before MailFrom() // Can be used to Reset session state // On MailFrom mailID is available Init(sessionID, mailID string) // Connect is called to provide SMTP connection data for incoming message // supress with NoConnect Connect(host string, family string, port uint16, addr net.IP, m *Modifier) (Response, error) // Helo is called to process any HELO/EHLO related filters // supress with NoHelo Helo(name string, m *Modifier) (Response, error) // MailFrom is called to process filters on envelope FROM address // supress with NoMailForm MailFrom(from string, m *Modifier) (Response, error) // RcptTo is called to process filters on envelope TO address // supress with NoRcptTo RcptTo(rcptTo string, m *Modifier) (Response, error) // Header is called once for each header in incoming message // supress with NoHeaders Header(name string, value string, m *Modifier) (Response, error) // Headers is called when all message headers have been processed // supress with NoHeaders Headers(h textproto.MIMEHeader, m *Modifier) (Response, error) // BodyChunk is called to process next message body chunk data (up to 64KB in size) // supress with NoBody BodyChunk(chunk []byte, m *Modifier) (Response, error) // Body is called at the end of each message // all changes to message's content & attributes must be done here Body(m *Modifier) (Response, error) // Disconnect is called at the end of the message Handling loop Disconnect() }
SessionHandler is an interface for milter callback handlers
type SimpleResponse ¶
type SimpleResponse byte
SimpleResponse type to define list of pre-defined responses
func (SimpleResponse) Continue ¶
func (r SimpleResponse) Continue() bool
Continue to process milter messages only if current code is Continue
func (SimpleResponse) Response ¶
func (r SimpleResponse) Response() *Message
Response returns a Message object reference