smtpproxy

package module
v0.0.0-...-04e888c Latest Latest
Warning

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

Go to latest
Published: Mar 20, 2020 License: Apache-2.0 Imports: 15 Imported by: 4

README

go-smtpproxy

Build Status Coverage Status

Go package, based heavily on emersion's go-gmtp, with increased transparency of response codes and no sasl dependency. The purpose of this is to provide functions that act as a server to receive SMTP messages from your downstream client. These SMTP messages are relayed through to an upstream server.

The command / response exchanges are passed on transparently.

STARTTLS can be offered to the downstream client if you configure a valid certificate/key pair.

STARTTLS can be requested from the upstream server.

Line splitting functions are included for base64 encoded email handling by your app.

Get this project with go get github.com/tuck1s/go-smtpproxy.

cmd/proxy contains an example command-line app using this library:

cd cmd/proxy
go build
./proxy -h

SMTP proxy that accepts incoming messages from your downstream client, and relays on to an upstream server.
Usage of ./proxy:
  -certfile string
        Certificate file for this server
  -downstream_debug string
        File to write downstream server SMTP conversation for debugging
  -in_hostport string
        Port number to serve incoming SMTP requests (default "localhost:587")
  -insecure_skip_verify
        Skip check of peer cert on upstream side
  -logfile string
        File written with message logs (also to stdout)
  -out_hostport string
        host:port for onward routing of SMTP requests (default "smtp.sparkpostmail.com:587")
  -privkeyfile string
        Private key file for this server
  -verbose
        print out lots of messages

Documentation

Overview

Package smtpproxy is based heavily on https://github.com/emersion/go-smtp, with increased transparency of response codes and no sasl dependency.

Package smtpproxy is based heavily on https://github.com/emersion/go-smtp, with increased transparency of response codes and no sasl dependency.

Package smtpproxy is based heavily on https://github.com/emersion/go-smtp, with increased transparency of response codes and no sasl dependency.

Package smtpproxy is based heavily on https://github.com/emersion/go-smtp, with increased transparency of response codes and no sasl dependency.

Package smtpproxy is based heavily on https://github.com/emersion/go-smtp, with increased transparency of response codes and no sasl dependency.

Package smtpproxy is based heavily on https://github.com/emersion/go-smtp, with increased transparency of response codes and no sasl dependency.

Package smtpproxy is based heavily on https://github.com/emersion/go-smtp, with increased transparency of response codes and no sasl dependency.

Package smtpproxy is based heavily on https://github.com/emersion/go-smtp, with increased transparency of response codes and no sasl dependency.

Index

Constants

This section is empty.

Variables

View Source
var EnhancedCodeNotSet = EnhancedCode{0, 0, 0}

EnhancedCodeNotSet is a nil value of EnhancedCode field in SMTPError, used to indicate that backend failed to provide enhanced status code. X.0.0 will be used (X is derived from error code).

View Source
var NoEnhancedCode = EnhancedCode{-1, -1, -1}

NoEnhancedCode is used to indicate that enhanced error code should not be included in response.

Note that RFC 2034 requires an enhanced code to be included in all 2xx, 4xx and 5xx responses. This constant is exported for use by extensions, you should probably use EnhancedCodeNotSet instead.

Functions

func CreateProxy

func CreateProxy(inHostPort, outHostPort string, verboseOpt bool, cert, privkey []byte, insecureSkipVerify bool, dbgFile *os.File) (*Server, *ProxyBackend, error)

CreateProxy sets up a proxy server with provided parameters and options, also returns the backend.

func NewLineSplitterWriter

func NewLineSplitterWriter(len int, sep []byte, w io.Writer) io.Writer

NewLineSplitterWriter creates a new instance

func ParseCmd

func ParseCmd(line string) (cmd string, arg string, err error)

ParseCmd parses an SMTP command line

Types

type Backend

type Backend interface {
	// Create a session
	Init() (Session, error)
}

Backend for a SMTP server

type Client

type Client struct {
	Text *textproto.Conn // Text is the textproto.Conn used by the Client. It is exported to allow for clients to add extensions.

	DataResponseCode int // proxy error reporting for data phase (as writeCloser can only return "error" class)
	DataResponseMsg  string
	// contains filtered or unexported fields
}

A Client represents a client connection to an SMTP server. Stripped out unused functionality for proxy

func Dial

func Dial(addr string) (*Client, error)

Dial returns a new Client connected to an SMTP server at addr. The addr must include a port, as in "mail.example.com:smtp".

func DialTLS

func DialTLS(addr string, tlsConfig *tls.Config) (*Client, error)

DialTLS returns a new Client connected to an SMTP server via TLS at addr. The addr must include a port, as in "mail.example.com:smtps".

func NewClient

func NewClient(conn net.Conn, host string) (*Client, error)

NewClient returns a new Client using an existing connection and host as a server name to be used when authenticating.

func (*Client) Capabilities

func (c *Client) Capabilities() []string

Capabilities reports all supported by the client, as a slice of strings Second param indicates is STARTTLS is available Return in lexically sorted order, so we get the same results each time

func (*Client) Close

func (c *Client) Close() error

Close closes the connection.

func (*Client) Data

func (c *Client) Data() (io.WriteCloser, int, string, error)

Data issues a DATA command to the server and returns a writer that can be used to write the mail headers and body. The caller should close the writer before calling any more methods on c. A call to Data must be preceded by one or more calls to Rcpt.

func (*Client) Extension

func (c *Client) Extension(ext string) (bool, string)

Extension reports whether an extension is support by the server. The extension name is case-insensitive. If the extension is supported, Extension also returns a string that contains any parameters the server specifies for the extension.

func (*Client) Hello

func (c *Client) Hello(localName string) (int, string, error)

Hello sends a HELO or EHLO to the server as the given host name. Calling this method is only necessary if the client needs control over the host name used. The client will introduce itself as "localhost" automatically otherwise. If Hello is called, it must be called before any of the other methods.

This version does not specifically check for repeat calling of (E)HELO, we'll let the upstream server tell us that

func (*Client) MyCmd

func (c *Client) MyCmd(expectCode int, format string, args ...interface{}) (int, string, error)

MyCmd - is a wrapper for underlying method

func (*Client) StartTLS

func (c *Client) StartTLS(config *tls.Config) (int, string, error)

StartTLS sends the STARTTLS command and encrypts all further communication. This is stripped down to not attempt (E)HLOs first.

func (*Client) TLSConnectionState

func (c *Client) TLSConnectionState() (state tls.ConnectionState, ok bool)

TLSConnectionState returns the client's TLS connection state. The return values are their zero values if StartTLS did not succeed.

type Conn

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

Conn is the incoming connection

func (*Conn) Close

func (c *Conn) Close() error

Close this connection

func (*Conn) ReadLine

func (c *Conn) ReadLine() (string, error)

ReadLine reads a line of input from the incoming connection

func (*Conn) Server

func (c *Conn) Server() *Server

Server name of this connection

func (*Conn) Session

func (c *Conn) Session() Session

Session associated with this connection

func (*Conn) SetSession

func (c *Conn) SetSession(session Session)

SetSession - setting the user resets any message being generated

func (*Conn) State

func (c *Conn) State() ConnectionState

State of this connection

func (*Conn) TLSConnectionState

func (c *Conn) TLSConnectionState() (state tls.ConnectionState, ok bool)

TLSConnectionState returns the connection's TLS connection state. Zero values are returned if the connection doesn't use TLS.

func (*Conn) WriteResponse

func (c *Conn) WriteResponse(code int, enhCode EnhancedCode, text ...string)

WriteResponse back to the incoming connection. If you do not want an enhanced code added, pass in value NoEnhancedCode.

type ConnectionState

type ConnectionState struct {
	Hostname   string
	LocalAddr  net.Addr
	RemoteAddr net.Addr
	TLS        tls.ConnectionState
}

ConnectionState gives useful info about the incoming connection, including the TLS status

type EnhancedCode

type EnhancedCode [3]int

EnhancedCode as per https://tools.ietf.org/html/rfc3463

type Logger

type Logger interface {
	Printf(format string, v ...interface{})
	Println(v ...interface{})
}

Logger interface is used by Server to report unexpected internal errors.

type ProxyBackend

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

The ProxyBackend implements SMTP server methods.

func NewBackend

func NewBackend(outHostPort string, verbose bool, insecureSkipVerify bool) *ProxyBackend

NewBackend creates a proxy backend with specified params

func (ProxyBackend) Init

func (bkd ProxyBackend) Init() (Session, error)

Init the backend. Here we establish the upstream connection

func (*ProxyBackend) MakeSession

func (bkd *ProxyBackend) MakeSession(c *Client) Session

MakeSession returns a session for this client and backend

func (*ProxyBackend) SetVerbose

func (bkd *ProxyBackend) SetVerbose(v bool)

SetVerbose allows changing logging options on-the-fly

type SMTPError

type SMTPError struct {
	Code         int
	EnhancedCode EnhancedCode
	Message      string
}

SMTPError specifies the error code and message that needs to be returned to the client

func (*SMTPError) Error

func (err *SMTPError) Error() string

type Server

type Server struct {
	// TCP or Unix address to listen on.
	Addr string
	// The server TLS configuration.
	TLSConfig *tls.Config

	Domain string

	Debug        io.Writer
	ErrorLog     Logger
	ReadTimeout  time.Duration
	WriteTimeout time.Duration

	// If set, the AUTH command will not be advertised and authentication
	// attempts will be rejected. This setting overrides AllowInsecureAuth.
	AuthDisabled bool

	// The server backend.
	Backend Backend
	// contains filtered or unexported fields
}

Server is an SMTP server.

func NewServer

func NewServer(be Backend) *Server

NewServer creates a new SMTP server, with a Backend interface, supporting many connections

func (*Server) Close

func (s *Server) Close()

Close function not needed

func (*Server) ListenAndServe

func (s *Server) ListenAndServe() error

ListenAndServe listens on the network address s.Addr and then calls Serve to handle requests on incoming connections.

If s.Addr is blank and LMTP is disabled, ":smtp" is used.

func (*Server) Serve

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

Serve accepts incoming connections on the Listener l.

func (*Server) ServeTLS

func (s *Server) ServeTLS(cert []byte, privkey []byte) error

ServeTLS configures the server with TLS credentials from supplied cert/key and sets the EHLO server name

type Session

type Session interface {
	// Greet a session. Returns capabilities of the upstream host
	Greet(ehlotype string) ([]string, int, string, error)

	// StartTLS requests the backend to upgrade its connection
	StartTLS() (int, string, error)

	// These backend functions follow a regular pattern matching SessionFunc above
	Auth(expectcode int, cmd, arg string) (int, string, error)

	Mail(expectcode int, cmd, arg string) (int, string, error)

	Rcpt(expectcode int, cmd, arg string) (int, string, error)

	Reset(expectcode int, cmd, arg string) (int, string, error)

	Quit(expectcode int, cmd, arg string) (int, string, error)

	// DataCommand pass upstream, returning a place to write the data AND the usual responses
	DataCommand() (w io.WriteCloser, code int, msg string, err error)

	// Data body (dot delimited) pass upstream, returning the usual responses
	Data(r io.Reader, w io.WriteCloser) (int, string, error)

	// This is called if we see any unknown command
	Unknown(expectcode int, cmd, arg string) (int, string, error)
}

Session backend functions

type SessionFunc

type SessionFunc func(expectcode int, cmd, arg string) (int, string, error)

SessionFunc Session backend functions

Directories

Path Synopsis
cmd
proxy
proxy command-line example
proxy command-line example

Jump to

Keyboard shortcuts

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