Send email from a Go application using the Direct Mail SMTP service. Two complete samples cover port 80 (plain connection with PLAIN or LOGIN authentication) and port 465 (implicit TLS).
Direct Mail exposes two SMTP endpoints. Use port 80 for a plain connection with PLAIN or LOGIN authentication. Use port 465 for implicit TLS, where the TLS handshake occurs before any SMTP commands. Both samples are verified on Go 1.16.5 and use smtpdm.aliyun.com as the SMTP host.
Prerequisites
Before you begin, make sure you have:
A verified sender address configured in the Direct Mail console
The SMTP password for that sender address
Use port 80
The sample below sends an HTML email with To, CC, and BCC recipients. MergeSlice combines all three recipient lists into a single SMTP envelope recipient list. This is required because the SMTP RCPT TO command determines which mailboxes receive the message, regardless of what the email headers show.
Replace the placeholder strings in main() with your sender address and SMTP password from the Direct Mail console.
To send over an encrypted connection, use the port 465 sample below. Do not use the port 80 code for TLS connections.
package main
import (
"fmt"
"net/smtp"
"strings"
"time"
)
func SendToMail(user, password, host, subject, date, body, mailtype, replyToAddress string, to, cc, bcc []string) error {
hp := strings.Split(host, ":")
auth := smtp.PlainAuth("", user, password, hp[0])
var content_type string
if mailtype == "html" {
content_type = "Content-Type: text/" + mailtype + "; charset=UTF-8"
} else {
content_type = "Content-Type: text/plain" + "; charset=UTF-8"
}
cc_address := strings.Join(cc, ";")
bcc_address := strings.Join(bcc, ";")
to_address := strings.Join(to, ";")
msg := []byte("To: " + to_address + "\r\nFrom: " + user + "\r\nSubject: " + subject+ "\r\nDate: " + date + "\r\nReply-To: " + replyToAddress + "\r\nCc: " + cc_address + "\r\nBcc: " + bcc_address + "\r\n" + content_type + "\r\n\r\n" + body)
send_to := MergeSlice(to, cc)
send_to = MergeSlice(send_to, bcc)
err := smtp.SendMail(host, auth, user, send_to, msg)
return err
}
func main() {
user := "The sender address created in the console"
password := "The SMTP password set in the console"
host := "smtpdm.aliyun.com:80"
to := []string{"recipient_address","recipient_address_1"}
cc := []string{"cc_address","cc_address_1"}
bcc := []string{"bcc_address","bcc_address_1"}
subject := "test Golang to sendmail"
date := fmt.Sprintf("%s", time.Now().Format(time.RFC1123Z))
mailtype :="html"
replyToAddress:="a***@example.net"
body := "<html><body><h3>Test send to email</h3></body></html>"
fmt.Println("send email")
err := SendToMail(user, password, host, subject,date, body, mailtype, replyToAddress, to, cc, bcc)
if err != nil {
fmt.Println("Send mail error!")
fmt.Println(err)
} else {
fmt.Println("Send mail success!")
}
}
func MergeSlice(s1 []string, s2 []string) []string {
slice := make([]string, len(s1)+len(s2))
copy(slice, s1)
copy(slice[len(s1):], s2)
return slice
}Go 1.9.2 compatibility note
Go 1.9.2 requires encrypted authentication and rejects a plain PlainAuth call with an "unencrypted connection" error. To resolve this, replace the smtp.PlainAuth call with the custom LoginAuth implementation below.
Add the following LoginAuth implementation to your file:
type loginAuth struct {
username, password string
}
func LoginAuth(username, password string) smtp.Auth {
return &loginAuth{username, password}
}
func (a *loginAuth) Start(server *smtp.ServerInfo) (string, []byte, error) {
// return "LOGIN", []byte{}, nil
return "LOGIN", []byte(a.username), nil
}
func (a *loginAuth) Next(fromServer []byte, more bool) ([]byte, error) {
if more {
switch string(fromServer) {
case "Username:":
return []byte(a.username), nil
case "Password:":
return []byte(a.password), nil
}
}
return nil, nil
}Then replace the auth line in main() with:
auth := LoginAuth(user, password)Use port 465
Port 465 uses implicit TLS: the TLS handshake happens before any SMTP commands, so net/smtp's built-in SendMail cannot be used directly. The sample defines a custom Dial function that opens a TLS connection using tls.Dial, and a SendMailUsingTLS function that drives the SMTP session manually.
package main
import (
"crypto/tls"
"fmt"
"log"
"net"
"net/smtp"
)
func main() {
host := "smtpdm.aliyun.com"
port := 465
email := "test***@example.net"
password := "TExxxxxst"
toEmail := "test1***@example.net"
header := make(map[string]string)
header["From"] = "test" + "<" + email + ">"
header["To"] = toEmail
header["Subject"] = "Email Title"
header["Content-Type"] = "text/html; charset=UTF-8"
body := "This is a test email!"
message := ""
for k, v := range header {
message += fmt.Sprintf("%s: %s\r\n", k, v)
}
message += "\r\n" + body
auth := smtp.PlainAuth(
"",
email,
password,
host,
)
err := SendMailUsingTLS(
fmt.Sprintf("%s:%d", host, port),
auth,
email,
[]string{toEmail},
[]byte(message),
)
if err != nil {
panic(err)
} else {
fmt.Println("Send mail success!")
}
}
// Dial opens an implicit TLS connection and returns an SMTP client.
func Dial(addr string) (*smtp.Client, error) {
conn, err := tls.Dial("tcp", addr, nil)
if err != nil {
log.Println("Dialing Error:", err)
return nil, err
}
// Split the host and port from the address string.
host, _, _ := net.SplitHostPort(addr)
return smtp.NewClient(conn, host)
}
// SendMailUsingTLS sends an email over an implicit TLS connection.
// When you use net. Dial to connect to a TLS (SSL) port, smtp.NewClient() may hang without returning an error.
// If len(to) is greater than 1, recipients from to[1] onwards are treated as BCC recipients.
func SendMailUsingTLS(addr string, auth smtp.Auth, from string,
to []string, msg []byte) (err error) {
// Create the SMTP client.
c, err := Dial(addr)
if err != nil {
log.Println("Create smpt client error:", err)
return err
}
defer c.Close()
if auth != nil {
if ok, _ := c.Extension("AUTH"); ok {
if err = c.Auth(auth); err != nil {
log.Println("Error during AUTH", err)
return err
}
}
}
if err = c.Mail(from); err != nil {
return err
}
for _, addr := range to {
if err = c.Rcpt(addr); err != nil {
return err
}
}
w, err := c.Data()
if err != nil {
return err
}
_, err = w.Write(msg)
if err != nil {
return err
}
err = w.Close()
if err != nil {
return err
}
return c.Quit()
}