我正在尝试使用默认的Go包net / smtp创建一个简单的Go电子邮件服务-我知道有gomailer,但我想使用标准库
我需要配置tls /服务器设置以与 Office365
一起使用时需要帮助我相信我的主机正确:
smtp.office365.com:587
但是,通过复制Microsoft提供的smtp文档,运行以下代码时,控制台中出现以下错误:
错误:tls:第一条记录看起来不像TLS握手 紧急:运行时错误:无效的内存地址或nil指针取消引用
package main
import (
"fmt"
"net"
mail "net/mail"
smtp "net/smtp"
)
func main() {
from := mail.Address{"", "example@example.com"}
to := mail.Address{"", "example@example.com"}
subject := "My test subject"
body := "Test email body"
// Setup email headers
headers := make(map[string]string)
headers["From"] = from.String()
headers["To"] = to.String()
headers["Subject"] = subject
message := ""
for k, v := range headers {
message += fmt.Sprintf("%s: %s\r\n", k, v)
}
message += "\r\n" + body
servername := "smtp.office365.com:587"
host, _, _ := net.SplitHostPort(servername)
auth := smtp.PlainAuth("", "example@example.com", "password", host)
tlsconfig := &tls.Config{
InsecureSkipVerify: true,
ServerName: host,
}
conn, err := tls.Dial("tcp", "smtp.office365.com:587", tlsconfig)
if err != nil {
fmt.Println("tls.Dial Error: %s", err)
}
c, err := smtp.NewClient(conn, host)
if err != nil {
fmt.Println("smtp.NewClient Error: %s", err)
}
if err = c.Auth(auth); err != nil {
fmt.Println("c.Auth Error: %s", err)
}
if err = c.Mail(from.Address); err != nil {
fmt.Println("c.Mail Error: %s", err)
}
if err = c.Rcpt(to.Address); err != nil {
fmt.Println("c.Rcpt Error: %s", err)
}
w, err := c.Data()
if err != nil {
fmt.Println("c.Data Error: %s", err)
}
_, err = w.Write([]byte(message))
if err != nil {
fmt.Println("Error: %s", err)
}
err = w.Close()
if err != nil {
fmt.Println("reader Error: %s", err)
}
c.Quit()
}
任何O365客户端的示例都将受到赞赏,或者任何人都可以发现的看起来可疑的东西都是很棒的
谢谢
答案 0 :(得分:1)
错误消息Error: tls: first record does not look like a TLS handshake
告诉您问题出在哪里:-)。如果尝试连接到服务器,您将看到(与任何SMTP服务器一样)它使用纯文本:
telnet smtp.office365.com 587
Trying 2603:1026:c0b:10::2...
Connected to zrh-efz.ms-acdc.office.com.
Escape character is '^]'.
220 ZRAP278CA0003.outlook.office365.com Microsoft ESMTP MAIL Service ready at Mon, 11 Nov 2019 17:13:50 +0000
...
您需要使用STARTTLS
命令,请参见https://en.wikipedia.org/wiki/Opportunistic_TLS(以及该Wiki页面所指向的RFC)。
在Go中,它是https://golang.org/pkg/net/smtp/#Client.StartTLS。
在您的代码中,我注意到了
tlsconfig := &tls.Config{
InsecureSkipVerify: true, <== REMOVE THIS
ServerName: host,
}
请删除InsecureSkipVerify
,顾名思义,它是不安全的,与您面临的错误无关。
答案 1 :(得分:1)
以下代码实现了AUTH LOGIN
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(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
default:
return nil, errors.New("Unknown from server")
}
}
return nil, nil
}
tlsconfig := &tls.Config {
ServerName: host,
}
conn, err := net.Dial("tcp", "smtp.office365.com:587")
if err != nil {
return err
}
c, err := smtp.NewClient(conn, host)
if err != nil {
return err
}
if err = c.StartTLS(tlsconfig); err != nil {
return err
}
auth := LoginAuth(fromAddress, password)
if err = c.Auth(auth); err != nil {
return err
}
答案 2 :(得分:1)
以下与我合作很好:
package main
import (
"bytes"
"crypto/tls"
"errors"
"fmt"
"net"
"net/smtp"
"text/template"
)
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(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
default:
return nil, errors.New("Unknown from server")
}
}
return nil, nil
}
func main() {
// Sender data.
from := "O365 logging name"
password := "O365 logging pasword"
// Receiver email address.
to := []string{
"receiver email",
}
// smtp server configuration.
smtpHost := "smtp.office365.com"
smtpPort := "587"
conn, err := net.Dial("tcp", "smtp.office365.com:587")
if err != nil {
println(err)
}
c, err := smtp.NewClient(conn, smtpHost)
if err != nil {
println(err)
}
tlsconfig := &tls.Config{
ServerName: smtpHost,
}
if err = c.StartTLS(tlsconfig); err != nil {
println(err)
}
auth := LoginAuth(from, password)
if err = c.Auth(auth); err != nil {
println(err)
}
t, _ := template.ParseFiles("template.html")
var body bytes.Buffer
mimeHeaders := "MIME-version: 1.0;\nContent-Type: text/html; charset=\"UTF-8\";\n\n"
body.Write([]byte(fmt.Sprintf("Subject: This is a test subject \n%s\n\n", mimeHeaders)))
t.Execute(&body, struct {
Name string
Message string
}{
Name: "Hasan Yousef",
Message: "This is a test message in a HTML template",
})
// Sending email.
err = smtp.SendMail(smtpHost+":"+smtpPort, auth, from, to, body.Bytes())
if err != nil {
fmt.Println(err)
return
}
fmt.Println("Email Sent!")
}
使用以下模板作为奖励:)
<!-- template.html -->
<!DOCTYPE html>
<html>
<body>
<h3>Name:</h3><span>{{.Name}}</span><br/><br/>
<h3>Email:</h3><span>{{.Message}}</span><br/>
</body>
</html>
答案 3 :(得分:0)
所以问题全在授权上。首先要求我在客户端上使用StartTLS方法,还要求我编写一个函数和方法来支持LOGIN,而标准Go库则不支持(出于某种原因)
请参阅main()上方的函数和结构
这是带有帮助程序功能的完整代码,现在可以通过我的O365帐户成功发送电子邮件:
package main
import (
"fmt"
"net"
"errors"
mail "net/mail"
smtp "net/smtp"
)
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
}
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
default:
return nil, errors.New("Unknown fromServer")
}
}
return nil, nil
}
func main() {
from := mail.Address{"", "example@example.com"}
to := mail.Address{"", "example@example.com"}
subject := "My test subject"
body := "Test email body"
headers := make(map[string]string)
headers["From"] = from.String()
headers["To"] = to.String()
headers["Subject"] = subject
message := ""
for k, v := range headers {
message += fmt.Sprintf("%s: %s\r\n", k, v)
}
message += "\r\n" + body
tlsconfig := &tls.Config{
ServerName: host,
}
conn, err := tls.Dial("tcp", "smtp.office365.com:587", tlsconfig)
if err != nil {
fmt.Println("tls.Dial Error: ", err)
}
c, err := smtp.NewClient(conn, host)
if err != nil {
fmt.Println("smtp.NewClient Error: ", err)
}
if err = c.Auth(LoginAuth("example@example.com", "password")); err != nil {
fmt.Println("c.Auth Error: ", err)
return
}
if err = c.Mail(from.Address); err != nil {
fmt.Println("c.Mail Error: ", err)
}
if err = c.Rcpt(to.Address); err != nil {
fmt.Println("c.Rcpt Error: ", err)
}
w, err := c.Data()
if err != nil {
fmt.Println("c.Data Error: ", err)
}
_, err = w.Write([]byte(message))
if err != nil {
fmt.Println("Error: ", err)
}
err = w.Close()
if err != nil {
fmt.Println("reader Error: ", err)
}
c.Quit()
}