milter

package module
v0.4.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 24, 2023 License: BSD-2-Clause Imports: 13 Imported by: 6

README

go-milter

GoDoc builds.sr.ht status

A Go library to write mail filters.

License

BSD 2-Clause

Documentation

Overview

Package milter provides an interface to implement milter mail filters

Index

Constants

View Source
const (
	RespAccept   = SimpleResponse(ActAccept)
	RespContinue = SimpleResponse(ActContinue)
	RespDiscard  = SimpleResponse(ActDiscard)
	RespReject   = SimpleResponse(ActReject)
	RespTempFail = SimpleResponse(ActTempFail)
)

Define standard responses with no data

View Source
const MaxBodyChunk = 65535

Variables

View Source
var ErrServerClosed = errors.New("milter: server closed")

ErrServerClosed is returned by the Server's Serve method after a call to Close.

View Source
var ErrUnsupportedMilterVersion = fmt.Errorf("milter: negotiate: unsupported milter version")

Functions

This section is empty.

Types

type Action added in v0.2.0

type Action struct {
	Code ActionCode

	// SMTP code if Code == ActReplyCode.
	SMTPCode int
	// Reply text if Code == ActReplyCode.
	SMTPText string
}

type ActionCode added in v0.2.0

type ActionCode byte
const (
	ActAccept    ActionCode = 'a' // SMFIR_ACCEPT
	ActContinue  ActionCode = 'c' // SMFIR_CONTINUE
	ActDiscard   ActionCode = 'd' // SMFIR_DISCARD
	ActReject    ActionCode = 'r' // SMFIR_REJECT
	ActTempFail  ActionCode = 't' // SMFIR_TEMPFAIL
	ActReplyCode ActionCode = 'y' // SMFIR_REPLYCODE

	// [v6]
	ActSkip ActionCode = 's' // SMFIR_SKIP
)

type Client added in v0.2.0

type Client struct {
	// contains filtered or unexported fields
}

Client is a wrapper for managing milter connections.

Currently, it just creates new connections using provided Dialer.

func NewClientWithOptions added in v0.2.0

func NewClientWithOptions(network, address string, opts ClientOptions) *Client

NewClientWithOptions creates a new Client object using provided options.

You generally want to use options to restrict ActionMask to what your code supports and ProtocolMask to what you intend to submit.

If opts.Dialer is not set, empty net.Dialer object will be used.

func NewDefaultClient added in v0.2.0

func NewDefaultClient(network, address string) *Client

NewDefaultClient creates a new Client object using default options.

It uses 10 seconds for connection/read/write timeouts and allows milter to send any actions supported by library.

func (*Client) Close added in v0.2.0

func (c *Client) Close() error

func (*Client) Session added in v0.2.0

func (c *Client) Session() (*ClientSession, error)

type ClientOptions added in v0.2.0

type ClientOptions struct {
	Dialer       Dialer
	ReadTimeout  time.Duration
	WriteTimeout time.Duration
	ActionMask   OptAction
	ProtocolMask OptProtocol
}

type ClientSession added in v0.2.0

type ClientSession struct {

	// Bitmask of negotiated action options.
	ActionOpts OptAction

	// Bitmask of negotiated protocol options.
	ProtocolOpts OptProtocol
	// contains filtered or unexported fields
}

func (*ClientSession) Abort added in v0.4.0

func (s *ClientSession) Abort() error

Abort sends Abort to the milter.

This is called for an unexpected end to an email outside the milters control.

func (*ClientSession) ActionOption added in v0.2.0

func (s *ClientSession) ActionOption(opt OptAction) bool

ActionOption checks whether the option is set in negotiated options, that is, requested by both sides.

func (*ClientSession) BodyChunk added in v0.2.0

func (s *ClientSession) BodyChunk(chunk []byte) (*Action, error)

BodyChunk sends a single body chunk to the milter.

It is callers responsibility to ensure every chunk is not bigger than MaxBodyChunk.

If OptSkip was specified during negotiation, caller should be ready to handle return ActSkip and stop sending body chunks if it is returned.

func (*ClientSession) BodyReadFrom added in v0.2.0

func (s *ClientSession) BodyReadFrom(r io.Reader) ([]ModifyAction, *Action, error)

BodyReadFrom is a helper function that calls BodyChunk repeately to transmit entire body from io.Reader and then calls End.

See documentation for these functions for details.

func (*ClientSession) Close added in v0.2.0

func (s *ClientSession) Close() error

Close releases resources associated with the session.

If there a milter sequence in progress - it is aborted.

func (*ClientSession) Conn added in v0.2.0

func (s *ClientSession) Conn(hostname string, family ProtoFamily, port uint16, addr string) (*Action, error)

Conn sends the connection information to the milter.

It should be called once per milter session (from Session to Close).

func (*ClientSession) End added in v0.2.0

func (s *ClientSession) End() ([]ModifyAction, *Action, error)

End sends the EOB message and resets session back to the state before Mail call. The same ClientSession can be used to check another message arrived within the same SMTP connection (Helo and Conn information is preserved).

Close should be called to conclude session.

func (*ClientSession) Header added in v0.2.0

func (s *ClientSession) Header(hdr textproto.Header) (*Action, error)

Header sends each field from textproto.Header followed by EOH unless header messages are disabled during negotiation.

func (*ClientSession) HeaderEnd added in v0.2.0

func (s *ClientSession) HeaderEnd() (*Action, error)

HeaderEnd send the EOH (End-Of-Header) message to the milter.

No HeaderField calls are allowed after this point.

func (*ClientSession) HeaderField added in v0.2.0

func (s *ClientSession) HeaderField(key, value string) (*Action, error)

HeaderField sends a single header field to the milter.

Value should be the original field value without any unfolding applied.

HeaderEnd() must be called after the last field.

func (*ClientSession) Helo added in v0.2.0

func (s *ClientSession) Helo(helo string) (*Action, error)

Helo sends the HELO hostname to the milter.

It should be called once per milter session (from Session to Close).

func (*ClientSession) Macros added in v0.2.0

func (s *ClientSession) Macros(code Code, kv ...string) error

func (*ClientSession) Mail added in v0.2.0

func (s *ClientSession) Mail(sender string, esmtpArgs []string) (*Action, error)

func (*ClientSession) ProtocolOption added in v0.2.0

func (s *ClientSession) ProtocolOption(opt OptProtocol) bool

ProtocolOption checks whether the option is set in negotiated options, that is, requested by both sides.

func (*ClientSession) Rcpt added in v0.2.0

func (s *ClientSession) Rcpt(rcpt string, esmtpArgs []string) (*Action, error)

type Code added in v0.2.0

type Code byte
const (
	CodeOptNeg Code = 'O' // SMFIC_OPTNEG
	CodeMacro  Code = 'D' // SMFIC_MACRO
	CodeConn   Code = 'C' // SMFIC_CONNECT
	CodeQuit   Code = 'Q' // SMFIC_QUIT
	CodeHelo   Code = 'H' // SMFIC_HELO
	CodeMail   Code = 'M' // SMFIC_MAIL
	CodeRcpt   Code = 'R' // SMFIC_RCPT
	CodeHeader Code = 'L' // SMFIC_HEADER
	CodeEOH    Code = 'N' // SMFIC_EOH
	CodeBody   Code = 'B' // SMFIC_BODY
	CodeEOB    Code = 'E' // SMFIC_BODYEOB
	CodeAbort  Code = 'A' // SMFIC_ABORT
	CodeData   Code = 'T' // SMFIC_DATA

	// [v6]
	CodeQuitNewConn Code = 'K' // SMFIC_QUIT_NC
)

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

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 Dialer added in v0.2.0

type Dialer interface {
	Dial(network string, addr string) (net.Conn, error)
}

type Message

type Message struct {
	Code byte
	Data []byte
}

Message represents a command sent from milter client

type Milter

type Milter interface {
	// Connect is called to provide SMTP connection data for incoming message.
	// Suppress with OptNoConnect.
	Connect(host string, family string, port uint16, addr net.IP, m *Modifier) (Response, error)

	// Helo is called to process any HELO/EHLO related filters. Suppress with
	// OptNoHelo.
	Helo(name string, m *Modifier) (Response, error)

	// MailFrom is called to process filters on envelope FROM address. Suppress
	// with OptNoMailFrom.
	MailFrom(from string, m *Modifier) (Response, error)

	// RcptTo is called to process filters on envelope TO address. Suppress with
	// OptNoRcptTo.
	RcptTo(rcptTo string, m *Modifier) (Response, error)

	// Header is called once for each header in incoming message. Suppress with
	// OptNoHeaders.
	Header(name string, value string, m *Modifier) (Response, error)

	// Headers is called when all message headers have been processed. Suppress
	// with OptNoEOH.
	Headers(h textproto.MIMEHeader, m *Modifier) (Response, error)

	// BodyChunk is called to process next message body chunk data (up to 64KB
	// in size). Suppress with OptNoBody.
	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)

	// Abort is called is the current message has been aborted. All message data
	// should be reset to prior to the Helo callback. Connection data should be
	// preserved.
	Abort(m *Modifier) error
}

Milter is an interface for milter callback handlers.

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) AddHeader

func (m *Modifier) AddHeader(name, value string) error

AddHeader appends a new email message header the message

func (*Modifier) AddRecipient

func (m *Modifier) AddRecipient(r string) error

AddRecipient appends a new envelope recipient for current message

func (*Modifier) ChangeFrom

func (m *Modifier) ChangeFrom(value string) error

ChangeFrom replaces the FROM envelope header with a new one

func (*Modifier) ChangeHeader

func (m *Modifier) ChangeHeader(index int, name, value string) error

ChangeHeader replaces the header at the specified position with a new one. The index is per name.

func (*Modifier) DeleteRecipient

func (m *Modifier) DeleteRecipient(r string) error

DeleteRecipient removes an envelope recipient address from message

func (*Modifier) InsertHeader

func (m *Modifier) InsertHeader(index int, name, value string) error

InsertHeader inserts the header at the specified position

func (*Modifier) Quarantine

func (m *Modifier) Quarantine(reason string) error

Quarantine a message by giving a reason to hold it

func (*Modifier) ReplaceBody

func (m *Modifier) ReplaceBody(body []byte) error

ReplaceBody substitutes message body with provided body

type ModifyActCode added in v0.2.0

type ModifyActCode byte
const (
	ActAddRcpt      ModifyActCode = '+' // SMFIR_ADDRCPT
	ActDelRcpt      ModifyActCode = '-' // SMFIR_DELRCPT
	ActReplBody     ModifyActCode = 'b' // SMFIR_ACCEPT
	ActAddHeader    ModifyActCode = 'h' // SMFIR_ADDHEADER
	ActChangeHeader ModifyActCode = 'm' // SMFIR_CHGHEADER
	ActInsertHeader ModifyActCode = 'i' // SMFIR_INSHEADER
	ActQuarantine   ModifyActCode = 'q' // SMFIR_QUARANTINE

	// [v6]
	ActChangeFrom ModifyActCode = 'e' // SMFIR_CHGFROM
)

type ModifyAction added in v0.2.0

type ModifyAction struct {
	Code ModifyActCode

	// Recipient to add/remove if Code == ActAddRcpt or ActDelRcpt.
	Rcpt string

	// New envelope sender if Code = ActChangeFrom.
	From string

	// ESMTP arguments for envelope sender if Code = ActChangeFrom.
	FromArgs []string

	// Portion of body to be replaced if Code == ActReplBody.
	Body []byte

	// Index of the header field to be changed if Code = ActChangeHeader or Code = ActInsertHeader.
	// Index is 1-based and is per value of HdrName.
	// E.g. HeaderIndex = 3 and HdrName = "DKIM-Signature" mean "change third
	// DKIM-Signature field". Order is the same as of HeaderField calls.
	HeaderIndex uint32

	// Header field name to be added/changed if Code == ActAddHeader or
	// ActChangeHeader or ActInsertHeader.
	HeaderName string

	// Header field value to be added/changed if Code == ActAddHeader or
	// ActChangeHeader or ActInsertHeader. If set to empty string - the field
	// should be removed.
	HeaderValue string

	// Quarantine reason if Code == ActQuarantine.
	Reason string
}

type NoOpMilter added in v0.3.1

type NoOpMilter struct{}

NoOpMilter is a dummy Milter implementation that does nothing.

func (NoOpMilter) Abort added in v0.4.0

func (NoOpMilter) Abort(m *Modifier) error

func (NoOpMilter) Body added in v0.3.1

func (NoOpMilter) Body(m *Modifier) (Response, error)

func (NoOpMilter) BodyChunk added in v0.3.1

func (NoOpMilter) BodyChunk(chunk []byte, m *Modifier) (Response, error)

func (NoOpMilter) Connect added in v0.3.1

func (NoOpMilter) Connect(host string, family string, port uint16, addr net.IP, m *Modifier) (Response, error)

func (NoOpMilter) Header added in v0.3.1

func (NoOpMilter) Header(name string, value string, m *Modifier) (Response, error)

func (NoOpMilter) Headers added in v0.3.1

func (NoOpMilter) Helo added in v0.3.1

func (NoOpMilter) Helo(name string, m *Modifier) (Response, error)

func (NoOpMilter) MailFrom added in v0.3.1

func (NoOpMilter) MailFrom(from string, m *Modifier) (Response, error)

func (NoOpMilter) RcptTo added in v0.3.1

func (NoOpMilter) RcptTo(rcptTo string, m *Modifier) (Response, error)

type OptAction

type OptAction uint32

OptAction sets which actions the milter wants to perform. Multiple options can be set using a bitmask.

const (
	OptAddHeader    OptAction = 1 << 0 // SMFIF_ADDHDRS
	OptChangeBody   OptAction = 1 << 1 // SMFIF_CHGBODY
	OptAddRcpt      OptAction = 1 << 2 // SMFIF_ADDRCPT
	OptRemoveRcpt   OptAction = 1 << 3 // SMFIF_DELRCPT
	OptChangeHeader OptAction = 1 << 4 // SMFIF_CHGHDRS
	OptQuarantine   OptAction = 1 << 5 // SMFIF_QUARANTINE

	// [v6]
	OptChangeFrom      OptAction = 1 << 6 // SMFIF_CHGFROM
	OptAddRcptWithArgs OptAction = 1 << 7 // SMFIF_ADDRCPT_PAR
	OptSetSymList      OptAction = 1 << 8 // SMFIF_SETSYMLIST
)

Set which actions the milter wants to perform.

type OptProtocol

type OptProtocol uint32

OptProtocol masks out unwanted parts of the SMTP transaction. Multiple options can be set using a bitmask.

const (
	OptNoConnect  OptProtocol = 1 << 0 // SMFIP_NOCONNECT
	OptNoHelo     OptProtocol = 1 << 1 // SMFIP_NOHELO
	OptNoMailFrom OptProtocol = 1 << 2 // SMFIP_NOMAIL
	OptNoRcptTo   OptProtocol = 1 << 3 // SMFIP_NORCPT
	OptNoBody     OptProtocol = 1 << 4 // SMFIP_NOBODY
	OptNoHeaders  OptProtocol = 1 << 5 // SMFIP_NOHDRS
	OptNoEOH      OptProtocol = 1 << 6 // SMFIP_NOEOH
	OptNoUnknown  OptProtocol = 1 << 8 // SMFIP_NOUNKNOWN
	OptNoData     OptProtocol = 1 << 9 // SMFIP_NODATA

	// [v6] MTA supports ActSkip
	OptSkip OptProtocol = 1 << 10 // SMFIP_SKIP
	// [v6] Filter wants rejected RCPTs
	OptRcptRej OptProtocol = 1 << 11 // SMFIP_RCPT_REJ

	// Milter will not send action response for the following MTA messages
	OptNoHeaderReply OptProtocol = 1 << 7 // SMFIP_NR_HDR, SMFIP_NOHREPL
	// [v6]
	OptNoConnReply    OptProtocol = 1 << 12 // SMFIP_NR_CONN
	OptNoHeloReply    OptProtocol = 1 << 13 // SMFIP_NR_HELO
	OptNoMailReply    OptProtocol = 1 << 14 // SMFIP_NR_MAIL
	OptNoRcptReply    OptProtocol = 1 << 15 // SMFIP_NR_RCPT
	OptNoDataReply    OptProtocol = 1 << 16 // SMFIP_NR_DATA
	OptNoUnknownReply OptProtocol = 1 << 17 // SMFIP_NR_UNKN
	OptNoEOHReply     OptProtocol = 1 << 18 // SMFIP_NR_EOH
	OptNoBodyReply    OptProtocol = 1 << 19 // SMFIP_NR_BODY

	// [v6]
	OptHeaderLeadingSpace OptProtocol = 1 << 20 // SMFIP_HDR_LEADSPC
)

type ProtoFamily added in v0.2.0

type ProtoFamily byte
const (
	FamilyUnknown ProtoFamily = 'U' // SMFIA_UNKNOWN
	FamilyUnix    ProtoFamily = 'L' // SMFIA_UNIX
	FamilyInet    ProtoFamily = '4' // SMFIA_INET
	FamilyInet6   ProtoFamily = '6' // SMFIA_INET6
)

type Response

type Response interface {
	Response() *Message
	Continue() bool
}

Response represents a response structure returned by callback handlers to indicate how the milter server should proceed

type Server

type Server struct {
	NewMilter func() Milter
	Actions   OptAction
	Protocol  OptProtocol
	// contains filtered or unexported fields
}

Server is a milter server.

func (*Server) Close

func (s *Server) Close() error

func (*Server) Serve

func (s *Server) Serve(ln net.Listener) error

Serve starts the server.

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

Directories

Path Synopsis
cmd

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL