Skip to content

Commit

Permalink
Merge pull request #28 from unapu-go/master
Browse files Browse the repository at this point in the history
feat: add function ValidateHostAndUser to validate host and user preventing SPAN and BOT
  • Loading branch information
badoux committed Sep 9, 2020
2 parents f9f80cb + 5d32c4e commit 85ee8a7
Show file tree
Hide file tree
Showing 3 changed files with 81 additions and 25 deletions.
46 changes: 27 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,34 +11,42 @@

### 1. Format
```go
func main() {
err := checkmail.ValidateFormat("ç$€§/[email protected]")
if err != nil {
fmt.Println(err)
}
}
func main() {
err := checkmail.ValidateFormat("ç$€§/[email protected]")
if err != nil {
fmt.Println(err)
}
}
```
output: `invalid format`

### 2. Domain
```go
func main() {
err := checkmail.ValidateHost("[email protected]")
if err != nil {
fmt.Println(err)
}
}
func main() {
err := checkmail.ValidateHost("[email protected]")
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("[email protected]")
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" // set your SMTP server here
serverMailAddress = "[email protected]" // set your valid mail address here
)
err := checkmail.ValidateHostAndUser(serverHostName, serverMailAddress, "[email protected]")
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.`

Expand Down
23 changes: 21 additions & 2 deletions checkmail.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,31 +37,50 @@ 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
}
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("[email protected]")
err = client.Mail(serverMailAddress)
if err != nil {
return NewSmtpError(err)
}
Expand Down
37 changes: 33 additions & 4 deletions checkmail_test.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
package checkmail_test
package checkmail

import (
"fmt"
"os"
"testing"

"github.com/badoux/checkmail"
)

var (
Expand Down Expand Up @@ -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)
}
Expand All @@ -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)
}
Expand All @@ -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
}

0 comments on commit 85ee8a7

Please sign in to comment.