mirror of
https://blitiri.com.ar/repos/chasquid
synced 2025-12-17 14:37:02 +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:
40
chasquid.go
40
chasquid.go
@@ -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
|
||||||
|
|
||||||
@@ -125,9 +134,11 @@ func (s *Server) serve(l net.Listener) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
sc := &Conn{
|
sc := &Conn{
|
||||||
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 {
|
||||||
|
|||||||
Reference in New Issue
Block a user