Skip to content

Commit 85ee8a7

Browse files
authored
Merge pull request #28 from unapu-go/master
feat: add function ValidateHostAndUser to validate host and user preventing SPAN and BOT
2 parents f9f80cb + 5d32c4e commit 85ee8a7

File tree

3 files changed

+81
-25
lines changed

3 files changed

+81
-25
lines changed

README.md

Lines changed: 27 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -11,34 +11,42 @@
1111

1212
### 1. Format
1313
```go
14-
func main() {
15-
err := checkmail.ValidateFormat("ç$€§/[email protected]")
16-
if err != nil {
17-
fmt.Println(err)
18-
}
19-
}
14+
func main() {
15+
err := checkmail.ValidateFormat("ç$€§/[email protected]")
16+
if err != nil {
17+
fmt.Println(err)
18+
}
19+
}
2020
```
2121
output: `invalid format`
2222

2323
### 2. Domain
2424
```go
25-
func main() {
26-
err := checkmail.ValidateHost("[email protected]")
27-
if err != nil {
28-
fmt.Println(err)
29-
}
30-
}
25+
func main() {
26+
err := checkmail.ValidateHost("[email protected]")
27+
if err != nil {
28+
fmt.Println(err)
29+
}
30+
}
3131
```
3232
output: `unresolvable host`
3333

34-
### 3. User
34+
### 3. Host and User
35+
36+
If host is valid, requires valid SMTP `serverHostName` (see to [online validator](https://mxtoolbox.com/SuperTool.aspx)) and `serverMailAddress` to reverse validation
37+
for prevent SPAN and BOTS.
38+
3539
```go
36-
func main() {
37-
err := checkmail.ValidateHost("[email protected]")
38-
if smtpErr, ok := err.(checkmail.SmtpError); ok && err != nil {
39-
fmt.Printf("Code: %s, Msg: %s", smtpErr.Code(), smtpErr)
40-
}
41-
}
40+
func main() {
41+
var (
42+
serverHostName = "smtp.myserver.com" // set your SMTP server here
43+
serverMailAddress = "[email protected]" // set your valid mail address here
44+
)
45+
err := checkmail.ValidateHostAndUser(serverHostName, serverMailAddress, "[email protected]")
46+
if smtpErr, ok := err.(checkmail.SmtpError); ok && err != nil {
47+
fmt.Printf("Code: %s, Msg: %s", smtpErr.Code(), smtpErr)
48+
}
49+
}
4250
```
4351
output: `Code: 550, Msg: 550 5.1.1 The email account that you tried to reach does not exist.`
4452

checkmail.go

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,31 +37,50 @@ var (
3737
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])?)*$")
3838
)
3939

40+
4041
func ValidateFormat(email string) error {
4142
if !emailRegexp.MatchString(email) {
4243
return ErrBadFormat
4344
}
4445
return nil
4546
}
4647

48+
// ValidateHost validate mail host.
4749
func ValidateHost(email string) error {
4850
_, host := split(email)
4951
mx, err := net.LookupMX(host)
5052
if err != nil {
5153
return ErrUnresolvableHost
5254
}
55+
client, err := DialTimeout(fmt.Sprintf("%s:%d", mx[0].Host, 25), forceDisconnectAfter)
56+
if err != nil {
57+
return NewSmtpError(err)
58+
}
59+
client.Close()
60+
return nil
61+
}
5362

63+
// ValidateHostAndUser validate mail host and user.
64+
// If host is valid, requires valid SMTP [1] serverHostName and serverMailAddress to reverse validation
65+
// for prevent SPAN and BOTS.
66+
// [1] https://mxtoolbox.com/SuperTool.aspx
67+
func ValidateHostAndUser(serverHostName, serverMailAddress, email string) error {
68+
_, host := split(email)
69+
mx, err := net.LookupMX(host)
70+
if err != nil {
71+
return ErrUnresolvableHost
72+
}
5473
client, err := DialTimeout(fmt.Sprintf("%s:%d", mx[0].Host, 25), forceDisconnectAfter)
5574
if err != nil {
5675
return NewSmtpError(err)
5776
}
5877
defer client.Close()
5978

60-
err = client.Hello("checkmail.me")
79+
err = client.Hello(serverHostName)
6180
if err != nil {
6281
return NewSmtpError(err)
6382
}
64-
err = client.Mail("[email protected]")
83+
err = client.Mail(serverMailAddress)
6584
if err != nil {
6685
return NewSmtpError(err)
6786
}

checkmail_test.go

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1-
package checkmail_test
1+
package checkmail
22

33
import (
4+
"fmt"
5+
"os"
46
"testing"
57

6-
"github.com/badoux/checkmail"
78
)
89

910
var (
@@ -35,7 +36,27 @@ func TestValidateHost(t *testing.T) {
3536
continue
3637
}
3738

38-
err := checkmail.ValidateHost(s.mail)
39+
err := ValidateHost(s.mail)
40+
if err != nil && s.account == true {
41+
t.Errorf(`"%s" => unexpected error: "%v"`, s.mail, err)
42+
}
43+
if err == nil && s.account == false {
44+
t.Errorf(`"%s" => expected error`, s.mail)
45+
}
46+
}
47+
}
48+
49+
func TestValidateHostAndUser(t *testing.T) {
50+
var (
51+
serverHostName = getenv(t, "self_hostname")
52+
serverMailAddress = getenv(t, "self_mail")
53+
)
54+
for _, s := range samples {
55+
if !s.format {
56+
continue
57+
}
58+
59+
err := ValidateHostAndUser(serverHostName, serverMailAddress, s.mail)
3960
if err != nil && s.account == true {
4061
t.Errorf(`"%s" => unexpected error: "%v"`, s.mail, err)
4162
}
@@ -47,7 +68,7 @@ func TestValidateHost(t *testing.T) {
4768

4869
func TestValidateFormat(t *testing.T) {
4970
for _, s := range samples {
50-
err := checkmail.ValidateFormat(s.mail)
71+
err := ValidateFormat(s.mail)
5172
if err != nil && s.format == true {
5273
t.Errorf(`"%s" => unexpected error: "%v"`, s.mail, err)
5374
}
@@ -56,3 +77,11 @@ func TestValidateFormat(t *testing.T) {
5677
}
5778
}
5879
}
80+
81+
func getenv(t *testing.T, name string) (value string) {
82+
name = "test_checkmail_"+name
83+
if value = os.Getenv(name); value =="" {
84+
panic(fmt.Errorf("enviroment variable %q is not defined", name))
85+
}
86+
return
87+
}

0 commit comments

Comments
 (0)