From 032ca175e4d671a57cdad03c3fbab5f63dea8183 Mon Sep 17 00:00:00 2001 From: Shivaram Lingamneni Date: Wed, 7 Jul 2021 07:12:15 -0400 Subject: [PATCH] add support for email timeouts --- default.yaml | 1 + irc/email/email.go | 4 +++- irc/smtp/smtp.go | 29 ++++++++++++++++++++++++----- traditional.yaml | 1 + 4 files changed, 29 insertions(+), 6 deletions(-) diff --git a/default.yaml b/default.yaml index 289165d2..d9055828 100644 --- a/default.yaml +++ b/default.yaml @@ -413,6 +413,7 @@ accounts: # password: "hunter2" blacklist-regexes: # - ".*@mailinator.com" + timeout: 60s # throttle account login attempts (to prevent either password guessing, or DoS # attacks on the server aimed at forcing repeated expensive bcrypt computations) diff --git a/irc/email/email.go b/irc/email/email.go index 11139fd9..d06f25e0 100644 --- a/irc/email/email.go +++ b/irc/email/email.go @@ -9,6 +9,7 @@ import ( "net" "regexp" "strings" + "time" "github.com/ergochat/ergo/irc/smtp" ) @@ -40,6 +41,7 @@ type MailtoConfig struct { MTAReal MTAConfig `yaml:"mta"` BlacklistRegexes []string `yaml:"blacklist-regexes"` blacklistRegexes []*regexp.Regexp + Timeout time.Duration } func (config *MailtoConfig) Postprocess(heloDomain string) (err error) { @@ -126,5 +128,5 @@ func SendMail(config MailtoConfig, recipient string, msg []byte) (err error) { addr = fmt.Sprintf("%s:smtp", mx) } - return smtp.SendMail(addr, auth, config.HeloDomain, config.Sender, []string{recipient}, msg, config.RequireTLS) + return smtp.SendMail(addr, auth, config.HeloDomain, config.Sender, []string{recipient}, msg, config.RequireTLS, config.Timeout) } diff --git a/irc/smtp/smtp.go b/irc/smtp/smtp.go index 80b76992..d17b09ae 100644 --- a/irc/smtp/smtp.go +++ b/irc/smtp/smtp.go @@ -24,6 +24,11 @@ import ( "net" "net/textproto" "strings" + "time" +) + +var ( + ErrTimedOut = errors.New("Timed out") ) // A Client represents a client connection to an SMTP server. @@ -48,11 +53,25 @@ type Client struct { // 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 Dial(addr string) (*Client, error) { - conn, err := net.Dial("tcp", addr) +func Dial(addr string, timeout time.Duration) (*Client, error) { + var conn net.Conn + var err error + start := time.Now() + if timeout == 0 { + conn, err = net.Dial("tcp", addr) + } else { + conn, err = net.DialTimeout("tcp", addr, timeout) + } if err != nil { return nil, err } + if timeout != 0 { + remaining := timeout - time.Since(start) + if remaining <= 0 { + return nil, ErrTimedOut + } + conn.SetDeadline(time.Now().Add(remaining)) + } host, _, _ := net.SplitHostPort(addr) return NewClient(conn, host) } @@ -316,8 +335,8 @@ var testHookStartTLS func(*tls.Config) // nil, except for tests // attachments (see the mime/multipart package), or other mail // functionality. Higher-level packages exist outside of the standard // library. -// XXX: modified in Oragono to add `requireTLS` and `heloDomain` arguments -func SendMail(addr string, a Auth, heloDomain string, from string, to []string, msg []byte, requireTLS bool) error { +// XXX: modified in Ergo to add `requireTLS`, `heloDomain`, and `timeout` arguments +func SendMail(addr string, a Auth, heloDomain string, from string, to []string, msg []byte, requireTLS bool, timeout time.Duration) error { if err := validateLine(from); err != nil { return err } @@ -326,7 +345,7 @@ func SendMail(addr string, a Auth, heloDomain string, from string, to []string, return err } } - c, err := Dial(addr) + c, err := Dial(addr, timeout) if err != nil { return err } diff --git a/traditional.yaml b/traditional.yaml index 5d431c60..bee90c4d 100644 --- a/traditional.yaml +++ b/traditional.yaml @@ -386,6 +386,7 @@ accounts: # password: "hunter2" blacklist-regexes: # - ".*@mailinator.com" + timeout: 60s # throttle account login attempts (to prevent either password guessing, or DoS # attacks on the server aimed at forcing repeated expensive bcrypt computations)