diff --git a/conn.go b/conn.go index 0ca88a1..961d863 100644 --- a/conn.go +++ b/conn.go @@ -17,6 +17,9 @@ import ( "time" ) +// Number of errors we'll tolerate per connection before closing. Defaults to 3. +const errThreshold = 3 + type ConnectionState struct { Hostname string LocalAddr net.Addr @@ -25,11 +28,14 @@ type ConnectionState struct { } type Conn struct { - conn net.Conn - text *textproto.Conn - server *Server - helo string - nbrErrors int + conn net.Conn + text *textproto.Conn + server *Server + helo string + + // Number of errors witnessed on this connection + errCount int + session Session locker sync.Mutex binarymime bool @@ -82,16 +88,6 @@ func (c *Conn) init() { c.text = textproto.NewConn(rwc) } -func (c *Conn) unrecognizedCommand(cmd string) { - c.WriteResponse(500, EnhancedCode{5, 5, 2}, fmt.Sprintf("Syntax error, %v command unrecognized", cmd)) - - c.nbrErrors++ - if c.nbrErrors > 3 { - c.WriteResponse(500, EnhancedCode{5, 5, 2}, "Too many unrecognized commands") - c.Close() - } -} - // Commands are dispatched to the appropriate handler functions. func (c *Conn) handle(cmd string, arg string) { // If panic happens during command handling - send 421 response @@ -107,7 +103,7 @@ func (c *Conn) handle(cmd string, arg string) { }() if cmd == "" { - c.WriteResponse(500, EnhancedCode{5, 5, 2}, "Speak up") + c.protocolError(500, EnhancedCode{5, 5, 2}, "Speak up") return } @@ -148,14 +144,15 @@ func (c *Conn) handle(cmd string, arg string) { c.Close() case "AUTH": if c.server.AuthDisabled { - c.unrecognizedCommand(cmd) + c.protocolError(500, EnhancedCode{5, 5, 2}, "Syntax error, AUTH command unrecognized") } else { c.handleAuth(arg) } case "STARTTLS": c.handleStartTLS() default: - c.unrecognizedCommand(cmd) + msg := fmt.Sprintf("Syntax errors, %v command unrecognized", cmd) + c.protocolError(500, EnhancedCode{5, 5, 2}, msg) } } @@ -222,6 +219,18 @@ func (c *Conn) authAllowed() bool { return !c.server.AuthDisabled && (isTLS || c.server.AllowInsecureAuth) } +// protocolError writes errors responses and closes the connection once too many +// have occurred. +func (c *Conn) protocolError(code int, ec EnhancedCode, msg string) { + c.WriteResponse(code, ec, msg) + + c.errCount++ + if c.errCount > errThreshold { + c.WriteResponse(500, EnhancedCode{5, 5, 1}, "Too many errors. Quiting now") + c.Close() + } +} + // GREET state -> waiting for HELO func (c *Conn) handleGreet(enhanced bool, arg string) { if !enhanced { diff --git a/server.go b/server.go index 52506b5..a7c37c9 100644 --- a/server.go +++ b/server.go @@ -147,8 +147,7 @@ func (s *Server) handleConn(c *Conn) error { if err == nil { cmd, arg, err := parseCmd(line) if err != nil { - c.nbrErrors++ - c.WriteResponse(501, EnhancedCode{5, 5, 2}, "Bad command") + c.protocolError(501, EnhancedCode{5, 5, 2}, "Bad command") continue }