首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >使用Office 365设置标准Go net/smtp失败,"Error tls: first record不像TLS握手“

使用Office 365设置标准Go net/smtp失败,"Error tls: first record不像TLS握手“
EN

Stack Overflow用户
提问于 2019-11-11 16:06:23
回答 4查看 3.2K关注 0票数 0

我正在尝试使用默认的Go包net/smtp创建一个简单的Go电子邮件服务--我知道有gomailer,但我想使用标准库

我需要帮助将tls/server设置配置为使用Office365

我相信我有正确的主人:

代码语言:javascript
复制
smtp.office365.com:587

但是,在复制Microsoft提供的smtp文档时,在运行以下代码时,控制台中会出现以下错误:

错误: tls:第一条记录看起来不像TLS握手恐慌:运行时错误:无效的内存地址或零指针取消引用

代码语言:javascript
复制
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客户端的例子都会受到赞赏,或者任何人能发现的任何看起来可疑的东西都会很好。

谢谢

EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2019-11-12 15:00:46

所以问题都是关于授权的。首先,我需要在客户机上使用StartTLS方法,还需要编写一个函数和方法来支持登录,这是标准Go库所不支持的(无论出于什么原因)。

请参阅main()上面的函数和结构。

下面是带有助手函数的完整代码,它现在可以通过我的O365帐户成功地发送电子邮件:

代码语言:javascript
复制
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()
}
票数 -1
EN

Stack Overflow用户

发布于 2019-11-11 17:22:36

错误消息Error: tls: first record does not look like a TLS handshake告诉您问题是什么:-)。如果您尝试连接到服务器,您将看到(与任何SMTP服务器一样)它使用纯文本:

代码语言:javascript
复制
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页面所指向的RFCs )。

在Go中,它是https://golang.org/pkg/net/smtp/#Client.StartTLS

在你的代码中我注意到

代码语言:javascript
复制
tlsconfig := &tls.Config{
    InsecureSkipVerify: true,   <== REMOVE THIS
    ServerName:         host,
}

请删除InsecureSkipVerify,顾名思义,它是不安全的,与您所面临的错误无关。

票数 2
EN

Stack Overflow用户

发布于 2020-08-19 14:23:57

自2017年8月以来,

  1. Outlook.com不再支持AUTH简单身份验证。

https://support.microsoft.com/en-us/office/outlook-com-no-longer-supports-auth-plain-authentication-07f7d5e9-1697-465f-84d2-4513d4ff0145?ui=en-us&rs=en-us&ad=us

  1. 使用AUTH登录

以下代码实现AUTH登录

代码语言:javascript
复制
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
}

  1. 删除"InsecureSkipVerify: true"

代码语言:javascript
复制
tlsconfig := &tls.Config {
    ServerName: host,
}

  1. 不使用tsl.Dial(),使用net.Dial()

代码语言:javascript
复制
conn, err := net.Dial("tcp", "smtp.office365.com:587")
if err != nil {
    return err
}

smtp.NewClient()后的

  1. 调用StartTLS()

代码语言:javascript
复制
c, err := smtp.NewClient(conn, host)
if err != nil {
    return err
}

if err = c.StartTLS(tlsconfig); err != nil {
    return err
}

  1. 使用AUTH登录

代码语言:javascript
复制
auth := LoginAuth(fromAddress, password) 

if err = c.Auth(auth); err != nil {
    return err
}
票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/58804817

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档