Skip to content

Instantly share code, notes, and snippets.

@coffeegoddd
Last active August 11, 2022 17:22
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save coffeegoddd/66f5aeec98640ff8a22a1b6910826667 to your computer and use it in GitHub Desktop.
Save coffeegoddd/66f5aeec98640ff8a22a1b6910826667 to your computer and use it in GitHub Desktop.
DoltLab `smtp_connection_helper`
package main
import (
"crypto/tls"
"errors"
"flag"
"fmt"
"github.com/emersion/go-sasl"
"github.com/emersion/go-smtp"
"io"
"log"
"strings"
)
type authMethod string
const (
authPlain authMethod = "plain"
authAnonymous authMethod = "anonymous"
authExternal authMethod = "external"
authOauthBearer authMethod = "oauthbearer"
authDisable authMethod = "disable"
authLogin authMethod = "login"
)
var sender = flag.String("from", "", "email address of sender")
var recipient = flag.String("to", "", "email address of recipient")
var host = flag.String("host", "", "smtp host")
var port = flag.Int("port", 0, "smtp port")
var auth = flag.String("auth", "", fmt.Sprintf("authentication method, one of '%s', '%s', '%s', '%s', '%s', '%s'", authPlain, authLogin, authAnonymous, authExternal, authOauthBearer, authDisable))
var implicitTLSArg = flag.Bool("implicit-tls", false, "use TLS Wrapper connection instead of STARTTLS connection")
var insecure = flag.Bool("insecure", false, "if used, sets TLS Config InsecureSkipVerify to true")
var username = flag.String("username", "", "smtp username")
var password = flag.String("password", "", "smtp password")
var identity = flag.String("identity", "", "smtp identity")
var token = flag.String("token", "", "oauthbearer token")
var trace = flag.String("trace", "", "trace argument passed to anonymous smtp client")
var subject = flag.String("subject", "Testing SMTP Server Connection", "email subject")
var message = flag.String("message", "This is a test email message sent with smtp_connection_helper!", "message to send to recipient")
var clientHostnameArg = flag.String("client-hostname", "localhost", "specifies client hostname passed SMTP server during HELO or EHLO")
var help = flag.Bool("help", false, "print 'smtp_connection_helper' usage")
func main() {
flag.Parse()
if *help {
printUsage()
} else {
if *host == "" || *port < 1 || *sender == "" || *recipient == "" || *subject == "" || *message == "" || *clientHostnameArg == "" {
printUsage()
log.Fatal(errors.New("one or all required arguments not supplied, must supply: --host, --port, --from, --to, --subject, --message, --client-hostname"))
}
var err error
switch {
case authMethod(*auth) == authPlain:
err = sendEmailPlainAuth(*host, *port, *identity, *username, *password, *sender, *recipient, *subject, *message, *clientHostnameArg, *implicitTLSArg, *insecure)
case authMethod(*auth) == authLogin:
err = sendEmailLoginAuth(*host, *port, *username, *password, *sender, *recipient, *subject, *message, *clientHostnameArg, *implicitTLSArg, *insecure)
case authMethod(*auth) == authAnonymous:
err = sendEmailAnonymousAuth(*host, *port, *trace, *sender, *recipient, *subject, *message, *clientHostnameArg, *implicitTLSArg, *insecure)
case authMethod(*auth) == authExternal:
err = sendEmailExternalAuth(*host, *port, *identity, *sender, *recipient, *subject, *message, *clientHostnameArg, *implicitTLSArg, *insecure)
case authMethod(*auth) == authOauthBearer:
err = sendEmailOauthBearerAuth(*host, *port, *username, *token, *sender, *recipient, *subject, *message, *clientHostnameArg, *implicitTLSArg, *insecure)
case authMethod(*auth) == authDisable:
err = sendEmailDisableAuth(*host, *port, *sender, *recipient, *subject, *message, *clientHostnameArg, *implicitTLSArg, *insecure)
default:
err = errors.New(fmt.Sprintf("Unsupported auth method: %s", *auth))
}
if err != nil {
log.Fatal(err)
}
fmt.Println("Successfully sent email!")
}
}
func sendEmailPlainAuth(host string, port int, identity, username, password, sender, recipient, subject, message, clientHostname string, implicitTLS, insecure bool) error {
if username == "" || password == "" {
return errors.New(fmt.Sprintf("username and password must not be empty for auth %s", authPlain))
}
smtpClient, err := getSmtpClient(host, port, implicitTLS, insecure)
if err != nil {
return err
}
authClient := sasl.NewPlainClient(identity, username, password)
return sendEmail(smtpClient, authClient, sender, recipient, subject, message, clientHostname, authPlain)
}
func sendEmailLoginAuth(host string, port int, username, password, sender, recipient, subject, message, clientHostname string, implicitTLS, insecure bool) error {
if username == "" || password == "" {
return errors.New(fmt.Sprintf("username and password must not be empty for auth %s", authLogin))
}
smtpClient, err := getSmtpClient(host, port, implicitTLS, insecure)
if err != nil {
return err
}
authClient := sasl.NewLoginClient(username, password)
return sendEmail(smtpClient, authClient, sender, recipient, subject, message, clientHostname, authLogin)
}
func sendEmailAnonymousAuth(host string, port int, trace, sender, recipient, subject, message, clientHostname string, implicitTLS, insecure bool) error {
smtpClient, err := getSmtpClient(host, port, implicitTLS, insecure)
if err != nil {
return err
}
authClient := sasl.NewAnonymousClient(trace)
return sendEmail(smtpClient, authClient, sender, recipient, subject, message, clientHostname, authAnonymous)
}
func sendEmailExternalAuth(host string, port int, identity, sender, recipient, subject, message, clientHostname string, implicitTLS, insecure bool) error {
smtpClient, err := getSmtpClient(host, port, implicitTLS, insecure)
if err != nil {
return err
}
authClient := sasl.NewExternalClient(identity)
return sendEmail(smtpClient, authClient, sender, recipient, subject, message, clientHostname, authExternal)
}
func sendEmailOauthBearerAuth(host string, port int, username, token, sender, recipient, subject, message, clientHostname string, implicitTLS, insecure bool) error {
if username == "" || token == "" {
return errors.New(fmt.Sprintf("username and token must not be empty for auth %s", authOauthBearer))
}
smtpClient, err := getSmtpClient(host, port, implicitTLS, insecure)
if err != nil {
return err
}
authClient := sasl.NewOAuthBearerClient(&sasl.OAuthBearerOptions{
Username: username,
Token: token,
Host: host,
Port: port,
})
return sendEmail(smtpClient, authClient, sender, recipient, subject, message, clientHostname, authOauthBearer)
}
func sendEmailDisableAuth(host string, port int, sender, recipient, subject, message, clientHostname string, implicitTLS, insecure bool) error {
smtpClient, err := getSmtpClient(host, port, implicitTLS, insecure)
if err != nil {
return err
}
return sendEmail(smtpClient, nil, sender, recipient, subject, message, clientHostname, authDisable)
}
func getSmtpClient(host string, port int, implicitTLS, insecure bool) (*smtp.Client, error) {
if implicitTLS {
return newImplicitTLSClient(host, port, insecure)
}
return newExplicitStartTLSClient(host, port)
}
func newImplicitTLSClient(host string, port int, insecure bool) (*smtp.Client, error) {
conn, err := tls.Dial("tcp", fmt.Sprintf("%s:%d", host, port), &tls.Config{
ServerName: host,
InsecureSkipVerify: insecure,
})
if err != nil {
return nil, err
}
return smtp.NewClient(conn, host)
}
func newExplicitStartTLSClient(host string, port int) (*smtp.Client, error) {
return smtp.Dial(fmt.Sprintf("%s:%d", host, port))
}
func sendEmail(smtpClient *smtp.Client, authClient sasl.Client, sender, recipient, subject, message, clientHostname string, method authMethod) (err error) {
defer func() {
rerr := smtpClient.Close()
if err == nil {
if rerr.Error() != "use of closed network connection" {
err = rerr
}
}
}()
body := "MIME-version: 1.0;\nContent-Type: text/html; charset=\"UTF-8\";\r\n"
body += fmt.Sprintf("From: %s\r\n", sender)
body += fmt.Sprintf("To: %s\r\n", recipient)
body += fmt.Sprintf("Subject: %s\r\n", subject)
body += fmt.Sprintf("\r\n%s\r\n", message)
fmt.Println("Sending email with auth method:", method)
err = smtpClient.Hello(clientHostname)
if err != nil {
return
}
if ok, _ := smtpClient.Extension("STARTTLS"); ok {
if err = smtpClient.StartTLS(nil); err != nil {
return
}
}
if authClient != nil {
ok, _ := smtpClient.Extension("AUTH")
if !ok {
return errors.New("smtp: server doesn't support AUTH")
} else {
err = smtpClient.Auth(authClient)
if err != nil {
return
}
}
}
err = smtpClient.Mail(sender, nil)
if err != nil {
return
}
err = smtpClient.Rcpt(recipient)
if err != nil {
return
}
var wc io.WriteCloser
wc, err = smtpClient.Data()
if err != nil {
return
}
_, err = io.Copy(wc, strings.NewReader(body))
if err != nil {
return
}
err = wc.Close()
if err != nil {
return
}
return smtpClient.Quit()
}
func printUsage() {
fmt.Println("")
fmt.Println("")
fmt.Println("'smtp_connection_helper' is a simple tool used to ensure you can successfully connect to an smtp server.")
fmt.Println("If the connection is successful, this tool will send a test email to a single recipient from a single sender.")
fmt.Println("By default 'smtp_connection_helper' will attempt to connect to the SMTP server with STARTTLS. To use implicit TLS, use --implicit-tls")
fmt.Println("")
fmt.Println(fmt.Sprintf("Usage:"))
fmt.Println("")
fmt.Println(fmt.Sprintf("./smtp_connection_helper \\"))
fmt.Println("--host <smtp hostname> \\")
fmt.Println("--port <smtp port> \\")
fmt.Println("--from <email address> \\")
fmt.Println("--to <email address> \\")
fmt.Println("--message {This is a test email message sent with smtp_connection_helper!} \\")
fmt.Println("--subject {Testing SMTP Server Connection} \\")
fmt.Println("--client-hostname {localhost} \\")
fmt.Println(fmt.Sprintf("--auth <%s|%s|%s|%s|%s|%s> \\", authPlain, authLogin, authExternal, authAnonymous, authOauthBearer, authDisable))
fmt.Println("[--username smtp username] \\")
fmt.Println("[--password smtp password] \\")
fmt.Println("[--token smtp oauth token] \\")
fmt.Println("[--identity smtp identity] \\")
fmt.Println("[--trace anonymous trace] \\")
fmt.Println("[--implicit-tls] \\")
fmt.Println("[--insecure]")
fmt.Println("")
fmt.Println("")
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment