Skip to content

Instantly share code, notes, and snippets.

@alexflint
Created March 27, 2024 15:30
Show Gist options
  • Save alexflint/ec5f03cd68c24db33e502baa25c2f7e0 to your computer and use it in GitHub Desktop.
Save alexflint/ec5f03cd68c24db33e502baa25c2f7e0 to your computer and use it in GitHub Desktop.
package main
import (
"crypto/tls"
"fmt"
"log"
"net"
"net/mail"
"net/smtp"
"os"
)
// SSL/TLS Email Example
func Main() error {
from := mail.Address{Name: "First Last", Address: "[email protected]"}
to := mail.Address{Name: "", Address: "[email protected]"}
subj := "This is the email subject"
body := "This is an example body."
// Setup headers
headers := make(map[string]string)
headers["From"] = from.String()
headers["To"] = to.String()
headers["Subject"] = subj
// Setup message
message := ""
for k, v := range headers {
message += fmt.Sprintf("%s: %s\r\n", k, v)
}
message += "\r\n" + body
// Connect to the SMTP Server
servername := "smtp-relay.gmail.com:587"
host, _, err := net.SplitHostPort(servername)
if err != nil {
return fmt.Errorf("error at DATA command: %w", err)
}
auth := smtp.PlainAuth("", "<your google email here>", "<app password here>", host)
// TLS config
tlsconfig := &tls.Config{
InsecureSkipVerify: true,
ServerName: host,
}
// c, err := smtp.NewClient(conn, host)
c, err := smtp.Dial(servername)
if err != nil {
return fmt.Errorf("failed to create smtp client: %w", err)
}
err = c.Hello("smtp-relay.gmail.com")
if err != nil {
return fmt.Errorf("error at EHLO: %w", err)
}
err = c.StartTLS(tlsconfig)
if err != nil {
return fmt.Errorf("error starting TLS: %w", err)
}
// Auth
if err = c.Auth(auth); err != nil {
return fmt.Errorf("failed to authenticate: %w", err)
}
// To && From
if err = c.Mail(from.Address); err != nil {
return fmt.Errorf("failed at MAIL command: %w", err)
}
if err = c.Rcpt(to.Address); err != nil {
return fmt.Errorf("failed at RCPT command: %w", err)
}
// Data
w, err := c.Data()
if err != nil {
return fmt.Errorf("error at DATA command: %w", err)
}
_, err = w.Write([]byte(message))
if err != nil {
return fmt.Errorf("error writing message: %w", err)
}
err = w.Close()
if err != nil {
return fmt.Errorf("error closing SMTP connection: %w", err)
}
return c.Quit()
}
func main() {
log.SetOutput(os.Stdout)
log.SetFlags(0)
err := Main()
if err != nil {
log.Fatal(err)
}
}
@alexflint
Copy link
Author

Google has two public SMTP servers. smtp.gmail.com allows you send email from the address that you are authenticated as. smtp-relay.gmail.com is only for Google Workspaces, and allows you send email from any address once configured.

First follow the instructions here to email the SMTP relay for your Google Workspaces domain: https://support.google.com/a/answer/2956491?hl=en&fl=1&sjid=6884875425010557997-NA

Next create an app password for a google account associated with your Google Workspaces domain as described here: https://support.google.com/accounts/answer/185833?hl=en

Next run the code above, filling in the strings "", and "". It will not work to use the ordinary password for a google account.

Some of the difficulties I overcame:

  • Google's smtp relay requires that you send a EHLO before doing STARTTLS. This seems to be nonstandard and caused many libraries I tried to not work. This behavior is different at smtp.gmail.com, which confused me.
  • With SMTP the initial connection is plaintext, then it becomes TLS when you run the STARTTLS command. This means you cannot start by opening a raw TLS connection.
  • Use port 587.
  • As per the first link above, in the google admin console, make sure to enable sending from any email.
  • As per the second link above, make sure to create an app password rather than using the main google password for an account.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment