Skip to content

Commit

Permalink
Merge pull request #11 from gostalt/telnet-email
Browse files Browse the repository at this point in the history
Create TelnetEmail Validator
  • Loading branch information
tmus authored Dec 13, 2019
2 parents da7a330 + 6fe4054 commit 5f37dc1
Show file tree
Hide file tree
Showing 2 changed files with 73 additions and 11 deletions.
74 changes: 64 additions & 10 deletions rule.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package validate

import (
"bufio"
"context"
"fmt"
"net"
Expand Down Expand Up @@ -173,15 +174,10 @@ var MXEmail CheckFunc = func(r *http.Request, param string, o Options) error {
timeout = 5
}

parts := strings.Split(r.Form.Get(param), "@")
domain := parts[len(parts)-1]

rsv := net.Resolver{}
ctx, cancel := context.WithTimeout(r.Context(), time.Duration(timeout)*time.Second)
defer cancel()
records, err := rsv.LookupMX(ctx, domain)
domain := getDomain(r.Form.Get(param))
records, err := getMXRecords(r.Context(), domain, timeout)
if err != nil {
return fmt.Errorf("failed to look up MX records for %s", param)
return fmt.Errorf("the host %s is not a valid email provider", domain)
}

if len(records) == 0 {
Expand All @@ -191,6 +187,54 @@ var MXEmail CheckFunc = func(r *http.Request, param string, o Options) error {
return nil
}

// TelnetEmail uses a telnet connection to dial into the mail provider
// for the given email address and uses this connection to verify if
// an email address is valid.
//
// TODO: Mixed results on Outlook/Hotmail.
var TelnetEmail CheckFunc = func(r *http.Request, param string, _ Options) error {
if err := Email(r, param, nil); err != nil {
return err
}

address := r.Form.Get(param)

domain := getDomain(address)
records, err := getMXRecords(r.Context(), domain, 5)
if err != nil || len(records) == 0 {
return fmt.Errorf("no MX records exist for %s", param)
}

conn, err := net.Dial("tcp", records[0].Host+":25")
if err != nil {
return fmt.Errorf("unable to connect to %s to validate email", domain)
}
defer conn.Close()

responder := bufio.NewReader(conn)
// Discards the first line of the telnet connection
// so we are ready to send some commands to it.
responder.ReadString('\n')

commands := []string{
"helo hi",
"mail from: <validate@gostalt.com>",
fmt.Sprintf("rcpt to: <%s>", address),
}

var msg string
for _, cmd := range commands {
fmt.Fprintf(conn, cmd+"\n")
msg, _ = responder.ReadString('\n')
}

if msg[0:3] != "250" {
return fmt.Errorf("%s is not a valid email address", address)
}

return nil
}

// Email returns an error if the parameter value is not a valid
// email address.
var Email CheckFunc = func(r *http.Request, param string, _ Options) error {
Expand All @@ -204,8 +248,6 @@ var Email CheckFunc = func(r *http.Request, param string, _ Options) error {
return fmt.Errorf("%s is not a valid email address", param)
}

// TODO: This is a little basic, but will probably correctly
// verify a large number of emails. Maybe improve it.
if pass, _ := regexp.MatchString(`^[^@\s]+@[^@\s]+$`, value); pass {
return nil
}
Expand Down Expand Up @@ -299,3 +341,15 @@ var Date CheckFunc = func(r *http.Request, param string, o Options) error {

return fmt.Errorf("%s does not satisfy and date format", param)
}

func getMXRecords(ctx context.Context, domain string, timeout int) ([]*net.MX, error) {
rsv := net.Resolver{}
ctx, cancel := context.WithTimeout(ctx, time.Duration(timeout)*time.Second)
defer cancel()
return rsv.LookupMX(ctx, domain)
}

func getDomain(email string) string {
parts := strings.Split(email, "@")
return parts[len(parts)-1]
}
10 changes: 9 additions & 1 deletion rule_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,9 +72,17 @@ func TestRules(t *testing.T) {
nil,
},
{
// TODO: Test is a little flaky.
TelnetEmail,
[]string{"me@tomm.us", "oliver@leaversmith.com"},
[]string{"m123123e@invalid.domain", "notanemail@tomm.us"},
nil,
},
{
// TODO: Test is a little flaky.
MXEmail,
[]string{"me@tomm.us", "lucyduggleby@hotmail.co.uk"},
[]string{"me@something@addasadsdn2343567hgbf.com", "juststring", "me@space@tomm.us"},
[]string{"me@invalid.domain", "juststring", "me@space@tomm.us"},
Options{"timeout": 10},
},
{
Expand Down

0 comments on commit 5f37dc1

Please sign in to comment.