1
0
mirror of https://blitiri.com.ar/repos/chasquid synced 2025-12-18 14:47:03 +00:00

Add timeouts for the connection and individual commands

This patch introduces a general connection timeout (20m); and a shorter one
(1m) for individual command round-trips.

DATA is excluded from the latter, because it is expected that it takes more
time; we use the general connection timeout for it.
This commit is contained in:
Alberto Bertogli
2015-10-26 03:55:32 +00:00
parent a5510607e8
commit 8c22b3beef

View File

@@ -54,11 +54,19 @@ type Server struct {
// TLS config. // TLS config.
tlsConfig *tls.Config tlsConfig *tls.Config
// Time before we give up on a connection, even if it's sending data.
connTimeout time.Duration
// Time we wait for command round-trips (excluding DATA).
commandTimeout time.Duration
} }
func NewServer(hostname string) *Server { func NewServer(hostname string) *Server {
return &Server{ return &Server{
hostname: hostname, hostname: hostname,
connTimeout: 20 * time.Minute,
commandTimeout: 1 * time.Minute,
} }
} }
@@ -87,6 +95,7 @@ func (s *Server) getTLSConfig() (*tls.Config, error) {
return conf, nil return conf, nil
} }
func (s *Server) ListenAndServe() { func (s *Server) ListenAndServe() {
var err error var err error
@@ -128,6 +137,8 @@ func (s *Server) serve(l net.Listener) {
netconn: conn, netconn: conn,
tc: textproto.NewConn(conn), tc: textproto.NewConn(conn),
tlsConfig: s.tlsConfig, tlsConfig: s.tlsConfig,
deadline: time.Now().Add(s.connTimeout),
commandTimeout: s.commandTimeout,
} }
go sc.Handle() go sc.Handle()
} }
@@ -145,13 +156,20 @@ type Conn struct {
mail_from string mail_from string
rcpt_to []string rcpt_to []string
data []byte data []byte
// When we should close this connection, no matter what.
deadline time.Time
// Time we wait for network operations.
commandTimeout time.Duration
} }
func (c *Conn) Handle() { func (c *Conn) Handle() {
defer c.netconn.Close() defer c.netconn.Close()
tr := trace.New("SMTP", "connection") tr := trace.New("SMTP", "Connection")
defer tr.Finish() defer tr.Finish()
tr.LazyPrintf("RemoteAddr: %s", c.netconn.RemoteAddr())
c.tc.PrintfLine("220 %s ESMTP charquid", hostname) c.tc.PrintfLine("220 %s ESMTP charquid", hostname)
@@ -160,6 +178,14 @@ func (c *Conn) Handle() {
loop: loop:
for { for {
if time.Since(c.deadline) > 0 {
tr.LazyPrintf("connection deadline exceeded")
err = fmt.Errorf("connection deadline exceeded")
break
}
c.netconn.SetDeadline(time.Now().Add(c.commandTimeout))
cmd, params, err = c.readCommand() cmd, params, err = c.readCommand()
if err != nil { if err != nil {
c.tc.PrintfLine("554 error reading command: %v", err) c.tc.PrintfLine("554 error reading command: %v", err)
@@ -340,6 +366,10 @@ func (c *Conn) DATA(params string, tr trace.Trace) (code int, msg string) {
tr.LazyPrintf("<- 354 You experience a strange sense of peace") tr.LazyPrintf("<- 354 You experience a strange sense of peace")
// Increase the deadline for the data transfer to the connection-level
// one, we don't want the command timeout to interfere.
c.netconn.SetDeadline(c.deadline)
dotr := io.LimitReader(c.tc.DotReader(), maxDataSize) dotr := io.LimitReader(c.tc.DotReader(), maxDataSize)
c.data, err = ioutil.ReadAll(dotr) c.data, err = ioutil.ReadAll(dotr)
if err != nil { if err != nil {