Last active
November 2, 2019 06:14
-
-
Save robbywashere-zz/2312392937d26b97d2b219aefdcebc51 to your computer and use it in GitHub Desktop.
Email validation JSON endpoint, any error is returned as `code` non-zero value with message else "ok"
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"flag" | |
"fmt" | |
"net" | |
"net/http" | |
"net/smtp" | |
"regexp" | |
"strings" | |
"time" | |
) | |
var ( | |
smptTimeout *time.Duration | |
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])?)*$") | |
) | |
const ( | |
OK = iota | |
SMTP_ERROR | |
INVALID_MX_RECORD | |
INVALID_EMAIL_FORMAT | |
) | |
func response(code int, message string) []byte { | |
return []byte(fmt.Sprintf(`{ "message": "%s", "code": %d }`, message, code)) | |
} | |
func split(email string) (account, host string) { | |
i := strings.LastIndexByte(email, '@') | |
account = email[:i] | |
host = email[i+1:] | |
return | |
} | |
func dialTimeout(addr string, timeout time.Duration) (*smtp.Client, error) { | |
conn, err := net.DialTimeout("tcp", addr, timeout) | |
if err != nil { | |
return nil, err | |
} | |
t := time.AfterFunc(timeout, func() { conn.Close() }) | |
defer t.Stop() | |
host, _, _ := net.SplitHostPort(addr) | |
return smtp.NewClient(conn, host) | |
} | |
func checkEmail(email string) (code int, message string) { | |
if !emailRegexp.MatchString(email) { | |
return INVALID_EMAIL_FORMAT, "invalid email format" | |
} | |
_, host := split(email) | |
mx, err := net.LookupMX(host) | |
if err != nil { | |
return INVALID_MX_RECORD, "invalid mx record" | |
} | |
client, err := dialTimeout(fmt.Sprintf("%s:%d", mx[0].Host, 25), *smptTimeout) | |
if err != nil { | |
return SMTP_ERROR, "smtp dial error" | |
} | |
defer client.Close() | |
err = client.Hello("example.com") | |
if err != nil { | |
return SMTP_ERROR, "smtp hello error" | |
} | |
err = client.Mail("[email protected]") | |
if err != nil { | |
return SMTP_ERROR, "smtp mail error" | |
} | |
err = client.Rcpt(email) | |
if err != nil { | |
return SMTP_ERROR, "smtp reciept error" | |
} | |
return OK, "ok" | |
} | |
func handler(w http.ResponseWriter, req *http.Request) { | |
w.Header().Set("Content-Type", "application/json") | |
keys, ok := req.URL.Query()["email"] | |
if !ok || len(keys[0]) < 1 { | |
w.WriteHeader(http.StatusBadRequest) | |
w.Write(response(-1, "url param 'email' is missing")) | |
return | |
} | |
w.Write(response(checkEmail(keys[0]))) | |
return | |
} | |
func main() { | |
smptTimeout = flag.Duration("timeout", 5*time.Second, "timeout in seconds for smtp") | |
port := flag.Int("port", 3001, "server http port") | |
flag.Parse() | |
http.HandleFunc("/", handler) | |
http.ListenAndServe(fmt.Sprintf(":%d", *port), nil) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment