upgrade deps; rewrite smtp session
This commit is contained in:
156
vendor/github.com/emersion/go-smtp/server.go
generated
vendored
156
vendor/github.com/emersion/go-smtp/server.go
generated
vendored
@@ -1,6 +1,7 @@
|
||||
package smtp
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"io"
|
||||
@@ -13,7 +14,9 @@ import (
|
||||
"github.com/emersion/go-sasl"
|
||||
)
|
||||
|
||||
var errTCPAndLMTP = errors.New("smtp: cannot start LMTP server listening on a TCP socket")
|
||||
var (
|
||||
ErrServerClosed = errors.New("smtp: server already closed")
|
||||
)
|
||||
|
||||
// A function that creates SASL servers.
|
||||
type SaslServerFactory func(conn *Conn) sasl.Server
|
||||
@@ -26,20 +29,20 @@ type Logger interface {
|
||||
|
||||
// A SMTP server.
|
||||
type Server struct {
|
||||
// The type of network, "tcp" or "unix".
|
||||
Network string
|
||||
// TCP or Unix address to listen on.
|
||||
Addr string
|
||||
// The server TLS configuration.
|
||||
TLSConfig *tls.Config
|
||||
// Enable LMTP mode, as defined in RFC 2033. LMTP mode cannot be used with a
|
||||
// TCP listener.
|
||||
// Enable LMTP mode, as defined in RFC 2033.
|
||||
LMTP bool
|
||||
|
||||
Domain string
|
||||
MaxRecipients int
|
||||
MaxMessageBytes int
|
||||
MaxMessageBytes int64
|
||||
MaxLineLength int
|
||||
AllowInsecureAuth bool
|
||||
Strict bool
|
||||
Debug io.Writer
|
||||
ErrorLog Logger
|
||||
ReadTimeout time.Duration
|
||||
@@ -57,6 +60,10 @@ type Server struct {
|
||||
// Should be used only if backend supports it.
|
||||
EnableBINARYMIME bool
|
||||
|
||||
// Advertise DSN (RFC 3461) capability.
|
||||
// Should be used only if backend supports it.
|
||||
EnableDSN bool
|
||||
|
||||
// If set, the AUTH command will not be advertised and authentication
|
||||
// attempts will be rejected. This setting overrides AllowInsecureAuth.
|
||||
AuthDisabled bool
|
||||
@@ -64,6 +71,8 @@ type Server struct {
|
||||
// The server backend.
|
||||
Backend Backend
|
||||
|
||||
wg sync.WaitGroup
|
||||
|
||||
caps []string
|
||||
auths map[string]SaslServerFactory
|
||||
done chan struct{}
|
||||
@@ -87,17 +96,15 @@ func NewServer(be Backend) *Server {
|
||||
sasl.Plain: func(conn *Conn) sasl.Server {
|
||||
return sasl.NewPlainServer(func(identity, username, password string) error {
|
||||
if identity != "" && identity != username {
|
||||
return errors.New("Identities not supported")
|
||||
return errors.New("identities not supported")
|
||||
}
|
||||
|
||||
state := conn.State()
|
||||
session, err := be.Login(&state, username, password)
|
||||
if err != nil {
|
||||
return err
|
||||
sess := conn.Session()
|
||||
if sess == nil {
|
||||
panic("No session when AUTH is called")
|
||||
}
|
||||
|
||||
conn.SetSession(session)
|
||||
return nil
|
||||
return sess.AuthPlain(username, password)
|
||||
})
|
||||
},
|
||||
},
|
||||
@@ -111,6 +118,8 @@ func (s *Server) Serve(l net.Listener) error {
|
||||
s.listeners = append(s.listeners, l)
|
||||
s.locker.Unlock()
|
||||
|
||||
var tempDelay time.Duration // how long to sleep on accept failure
|
||||
|
||||
for {
|
||||
c, err := l.Accept()
|
||||
if err != nil {
|
||||
@@ -119,11 +128,32 @@ func (s *Server) Serve(l net.Listener) error {
|
||||
// we called Close()
|
||||
return nil
|
||||
default:
|
||||
return err
|
||||
}
|
||||
if ne, ok := err.(net.Error); ok && ne.Temporary() {
|
||||
if tempDelay == 0 {
|
||||
tempDelay = 5 * time.Millisecond
|
||||
} else {
|
||||
tempDelay *= 2
|
||||
}
|
||||
if max := 1 * time.Second; tempDelay > max {
|
||||
tempDelay = max
|
||||
}
|
||||
s.ErrorLog.Printf("accept error: %s; retrying in %s", err, tempDelay)
|
||||
time.Sleep(tempDelay)
|
||||
continue
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
go s.handleConn(newConn(c, s))
|
||||
s.wg.Add(1)
|
||||
go func() {
|
||||
defer s.wg.Done()
|
||||
|
||||
err := s.handleConn(newConn(c, s))
|
||||
if err != nil {
|
||||
s.ErrorLog.Printf("handler error: %s", err)
|
||||
}
|
||||
}()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -140,10 +170,22 @@ func (s *Server) handleConn(c *Conn) error {
|
||||
s.locker.Unlock()
|
||||
}()
|
||||
|
||||
if tlsConn, ok := c.conn.(*tls.Conn); ok {
|
||||
if d := s.ReadTimeout; d != 0 {
|
||||
c.conn.SetReadDeadline(time.Now().Add(d))
|
||||
}
|
||||
if d := s.WriteTimeout; d != 0 {
|
||||
c.conn.SetWriteDeadline(time.Now().Add(d))
|
||||
}
|
||||
if err := tlsConn.Handshake(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
c.greet()
|
||||
|
||||
for {
|
||||
line, err := c.ReadLine()
|
||||
line, err := c.readLine()
|
||||
if err == nil {
|
||||
cmd, arg, err := parseCmd(line)
|
||||
if err != nil {
|
||||
@@ -153,34 +195,41 @@ func (s *Server) handleConn(c *Conn) error {
|
||||
|
||||
c.handle(cmd, arg)
|
||||
} else {
|
||||
if err == io.EOF {
|
||||
if err == io.EOF || errors.Is(err, net.ErrClosed) {
|
||||
return nil
|
||||
}
|
||||
if err == ErrTooLongLine {
|
||||
c.WriteResponse(500, EnhancedCode{5, 4, 0}, "Too long line, closing connection")
|
||||
c.writeResponse(500, EnhancedCode{5, 4, 0}, "Too long line, closing connection")
|
||||
return nil
|
||||
}
|
||||
|
||||
if neterr, ok := err.(net.Error); ok && neterr.Timeout() {
|
||||
c.WriteResponse(221, EnhancedCode{2, 4, 2}, "Idle timeout, bye bye")
|
||||
c.writeResponse(421, EnhancedCode{4, 4, 2}, "Idle timeout, bye bye")
|
||||
return nil
|
||||
}
|
||||
|
||||
c.WriteResponse(221, EnhancedCode{2, 4, 0}, "Connection error, sorry")
|
||||
c.writeResponse(421, EnhancedCode{4, 4, 0}, "Connection error, sorry")
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Server) network() string {
|
||||
if s.Network != "" {
|
||||
return s.Network
|
||||
}
|
||||
if s.LMTP {
|
||||
return "unix"
|
||||
}
|
||||
return "tcp"
|
||||
}
|
||||
|
||||
// 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 (s *Server) ListenAndServe() error {
|
||||
network := "tcp"
|
||||
if s.LMTP {
|
||||
network = "unix"
|
||||
}
|
||||
network := s.network()
|
||||
|
||||
addr := s.Addr
|
||||
if !s.LMTP && addr == "" {
|
||||
@@ -198,18 +247,16 @@ func (s *Server) ListenAndServe() error {
|
||||
// ListenAndServeTLS listens on the TCP network address s.Addr and then calls
|
||||
// Serve to handle requests on incoming TLS connections.
|
||||
//
|
||||
// If s.Addr is blank, ":smtps" is used.
|
||||
// If s.Addr is blank and LMTP is disabled, ":smtps" is used.
|
||||
func (s *Server) ListenAndServeTLS() error {
|
||||
if s.LMTP {
|
||||
return errTCPAndLMTP
|
||||
}
|
||||
network := s.network()
|
||||
|
||||
addr := s.Addr
|
||||
if addr == "" {
|
||||
if !s.LMTP && addr == "" {
|
||||
addr = ":smtps"
|
||||
}
|
||||
|
||||
l, err := tls.Listen("tcp", addr, s.TLSConfig)
|
||||
l, err := tls.Listen(network, addr, s.TLSConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -224,19 +271,19 @@ func (s *Server) ListenAndServeTLS() error {
|
||||
func (s *Server) Close() error {
|
||||
select {
|
||||
case <-s.done:
|
||||
return errors.New("smtp: server already closed")
|
||||
return ErrServerClosed
|
||||
default:
|
||||
close(s.done)
|
||||
}
|
||||
|
||||
var err error
|
||||
s.locker.Lock()
|
||||
for _, l := range s.listeners {
|
||||
if lerr := l.Close(); lerr != nil && err == nil {
|
||||
err = lerr
|
||||
}
|
||||
}
|
||||
|
||||
s.locker.Lock()
|
||||
for conn := range s.conns {
|
||||
conn.Close()
|
||||
}
|
||||
@@ -245,6 +292,44 @@ func (s *Server) Close() error {
|
||||
return err
|
||||
}
|
||||
|
||||
// Shutdown gracefully shuts down the server without interrupting any
|
||||
// active connections. Shutdown works by first closing all open
|
||||
// listeners and then waiting indefinitely for connections to return to
|
||||
// idle and then shut down.
|
||||
// If the provided context expires before the shutdown is complete,
|
||||
// Shutdown returns the context's error, otherwise it returns any
|
||||
// error returned from closing the Server's underlying Listener(s).
|
||||
func (s *Server) Shutdown(ctx context.Context) error {
|
||||
select {
|
||||
case <-s.done:
|
||||
return ErrServerClosed
|
||||
default:
|
||||
close(s.done)
|
||||
}
|
||||
|
||||
var err error
|
||||
s.locker.Lock()
|
||||
for _, l := range s.listeners {
|
||||
if lerr := l.Close(); lerr != nil && err == nil {
|
||||
err = lerr
|
||||
}
|
||||
}
|
||||
s.locker.Unlock()
|
||||
|
||||
connDone := make(chan struct{})
|
||||
go func() {
|
||||
defer close(connDone)
|
||||
s.wg.Wait()
|
||||
}()
|
||||
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
case <-connDone:
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// EnableAuth enables an authentication mechanism on this server.
|
||||
//
|
||||
// This function should not be called directly, it must only be used by
|
||||
@@ -252,12 +337,3 @@ func (s *Server) Close() error {
|
||||
func (s *Server) EnableAuth(name string, f SaslServerFactory) {
|
||||
s.auths[name] = f
|
||||
}
|
||||
|
||||
// ForEachConn iterates through all opened connections.
|
||||
func (s *Server) ForEachConn(f func(*Conn)) {
|
||||
s.locker.Lock()
|
||||
defer s.locker.Unlock()
|
||||
for conn := range s.conns {
|
||||
f(conn)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user