From e32c2e0c5dc7835d8e2577b18c4ba59244d23462 Mon Sep 17 00:00:00 2001 From: "Moises P. Sena" Date: Fri, 26 Jun 2020 10:42:38 -0300 Subject: [PATCH 1/2] feat: add function ValidateHostAndUser to validate host and user preventing SPAN and BOT --- README.md | 46 +++++++++++++++++++++++++++------------------- checkmail.go | 23 +++++++++++++++++++++-- checkmail_test.go | 37 +++++++++++++++++++++++++++++++++---- 3 files changed, 81 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index 1c21bf4..c663250 100644 --- a/README.md +++ b/README.md @@ -11,34 +11,42 @@ ### 1. Format ```go - func main() { - err := checkmail.ValidateFormat("ç$€§/az@gmail.com") - if err != nil { - fmt.Println(err) - } - } +func main() { + err := checkmail.ValidateFormat("ç$€§/az@gmail.com") + if err != nil { + fmt.Println(err) + } +} ``` output: `invalid format` ### 2. Domain ```go - func main() { - err := checkmail.ValidateHost("email@x-unkown-domain.com") - if err != nil { - fmt.Println(err) - } - } +func main() { + err := checkmail.ValidateHost("email@x-unkown-domain.com") + if err != nil { + fmt.Println(err) + } +} ``` output: `unresolvable host` -### 3. User +### 3. Host and User + +If host is valid, requires valid SMTP `serverHostName` (see to [online validator](https://mxtoolbox.com/SuperTool.aspx)) and `serverMailAddress` to reverse validation +for prevent SPAN and BOTS. + ```go - func main() { - err := checkmail.ValidateHost("unknown-user-129083726@gmail.com") - if smtpErr, ok := err.(checkmail.SmtpError); ok && err != nil { - fmt.Printf("Code: %s, Msg: %s", smtpErr.Code(), smtpErr) - } - } +func main() { + var ( + serverHostName = "smtp.myserver.com" + serverMailAddress = "validuser@myserver.com" + ) + err := checkmail.ValidateHostAndUser(serverHostName, serverMailAddress, "unknown-user-129083726@gmail.com") + if smtpErr, ok := err.(checkmail.SmtpError); ok && err != nil { + fmt.Printf("Code: %s, Msg: %s", smtpErr.Code(), smtpErr) + } +} ``` output: `Code: 550, Msg: 550 5.1.1 The email account that you tried to reach does not exist.` diff --git a/checkmail.go b/checkmail.go index 3e16ac8..623e163 100644 --- a/checkmail.go +++ b/checkmail.go @@ -37,6 +37,7 @@ var ( emailRegexp = regexp.MustCompile("^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$") ) + func ValidateFormat(email string) error { if !emailRegexp.MatchString(email) { return ErrBadFormat @@ -44,24 +45,42 @@ func ValidateFormat(email string) error { return nil } +// ValidateHost validate mail host. func ValidateHost(email string) error { _, host := split(email) mx, err := net.LookupMX(host) if err != nil { return ErrUnresolvableHost } + client, err := DialTimeout(fmt.Sprintf("%s:%d", mx[0].Host, 25), forceDisconnectAfter) + if err != nil { + return NewSmtpError(err) + } + client.Close() + return nil +} +// ValidateHostAndUser validate mail host and user. +// If host is valid, requires valid SMTP [1] serverHostName and serverMailAddress to reverse validation +// for prevent SPAN and BOTS. +// [1] https://mxtoolbox.com/SuperTool.aspx +func ValidateHostAndUser(serverHostName, serverMailAddress, email string) error { + _, host := split(email) + mx, err := net.LookupMX(host) + if err != nil { + return ErrUnresolvableHost + } client, err := DialTimeout(fmt.Sprintf("%s:%d", mx[0].Host, 25), forceDisconnectAfter) if err != nil { return NewSmtpError(err) } defer client.Close() - err = client.Hello("checkmail.me") + err = client.Hello(serverHostName) if err != nil { return NewSmtpError(err) } - err = client.Mail("lansome-cowboy@gmail.com") + err = client.Mail(serverMailAddress) if err != nil { return NewSmtpError(err) } diff --git a/checkmail_test.go b/checkmail_test.go index cdf3962..bb050e3 100644 --- a/checkmail_test.go +++ b/checkmail_test.go @@ -1,9 +1,10 @@ -package checkmail_test +package checkmail import ( + "fmt" + "os" "testing" - "github.com/badoux/checkmail" ) var ( @@ -35,7 +36,27 @@ func TestValidateHost(t *testing.T) { continue } - err := checkmail.ValidateHost(s.mail) + err := ValidateHost(s.mail) + if err != nil && s.account == true { + t.Errorf(`"%s" => unexpected error: "%v"`, s.mail, err) + } + if err == nil && s.account == false { + t.Errorf(`"%s" => expected error`, s.mail) + } + } +} + +func TestValidateHostAndUser(t *testing.T) { + var ( + serverHostName = getenv(t, "self_hostname") + serverMailAddress = getenv(t, "self_mail") + ) + for _, s := range samples { + if !s.format { + continue + } + + err := ValidateHostAndUser(serverHostName, serverMailAddress, s.mail) if err != nil && s.account == true { t.Errorf(`"%s" => unexpected error: "%v"`, s.mail, err) } @@ -47,7 +68,7 @@ func TestValidateHost(t *testing.T) { func TestValidateFormat(t *testing.T) { for _, s := range samples { - err := checkmail.ValidateFormat(s.mail) + err := ValidateFormat(s.mail) if err != nil && s.format == true { t.Errorf(`"%s" => unexpected error: "%v"`, s.mail, err) } @@ -56,3 +77,11 @@ func TestValidateFormat(t *testing.T) { } } } + +func getenv(t *testing.T, name string) (value string) { + name = "test_checkmail_"+name + if value = os.Getenv(name); value =="" { + panic(fmt.Errorf("enviroment variable %q is not defined", name)) + } + return +} \ No newline at end of file From 5d32c4e0ffe5037e6b314e6810bb629ad07170b7 Mon Sep 17 00:00:00 2001 From: "Moises P. Sena" Date: Fri, 26 Jun 2020 10:46:02 -0300 Subject: [PATCH 2/2] update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index c663250..b597a7d 100644 --- a/README.md +++ b/README.md @@ -39,8 +39,8 @@ for prevent SPAN and BOTS. ```go func main() { var ( - serverHostName = "smtp.myserver.com" - serverMailAddress = "validuser@myserver.com" + serverHostName = "smtp.myserver.com" // set your SMTP server here + serverMailAddress = "validuser@myserver.com" // set your valid mail address here ) err := checkmail.ValidateHostAndUser(serverHostName, serverMailAddress, "unknown-user-129083726@gmail.com") if smtpErr, ok := err.(checkmail.SmtpError); ok && err != nil {