golang笔记:net/smtp
跟go语言的net/smtp斗争了一天,记录下历程。
先用最标准的例子
host := net.JoinHostPort(hostname, port)
auth := smtp.PlainAuth("", username, password, hostname)
to := []string{address}
msg := []byte("To: " +
address +
"\r\n" +
"Subject:" +
title +
"\r\n" +
"\r\n" +
content +
"\r\n")
err := smtp.SendMail(host, auth, from, to, msg)
程序持续报一个 unencrypted connection 的错误。原来新版本的smtp为了防止密码以明文传输,强制以SSL连接发送邮件。但我手上的服务器没有SSL连接,只好去库里看在哪儿做的判断,找到auth.go里面func Start()中的这样一段话
if !server.TLS {
advertised := false
for _, mechanism := range server.Auth {
if mechanism == "PLAIN" {
advertised = true
break
}
}
if !advertised {
return "", nil, errors.New("unencrypted connection")
}
}
看样子判断是在这里进行的了。在网上找到一个伪装TLS链接的方法。
首先,在代码里加上
/*use unSSL to link mail server*/
type unencryptedAuth struct {
smtp.Auth
}
func (a unencryptedAuth) Start(server *smtp.ServerInfo) (string, []byte, error) {
s := *server
s.TLS = true
login, resp, th := a.Auth.Start(&s)
return "LOGIN", resp, th
}
将TLS的值设为true, 发邮件部分这样写
auth := unencryptedAuth {
smtp.PlainAuth(
"",
username,
password,
hostname,
)
}
err := smtp.SendMail(host, auth, from, to, msg)
这样链接成立了,报的错误变成 unrecognized authentication type. 查到func Start() 的返回值为
return "PLAIN", resp, nil
原来这里强制以plain登陆。参考前人的方法修改思路,重写Start方法
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", nil, nil
}
func (a *loginAuth) Next(fromServer []byte, more bool) ([]byte, error) {
command := string(fromServer)
command = strings.TrimSpace(command)
command = strings.TrimSuffix(command, ":")
command = strings.ToLower(command)
if more {
if (command == "username") {
return []byte(fmt.Sprintf("%s", a.username)), nil
} else if (command == "password") {
return []byte(fmt.Sprintf("%s", a.password)), nil
} else {
// We've already sent everything.
return nil, fmt.Errorf("unexpected server challenge: %s", command)
}
}
return nil, nil
}
Login的认证方式协议和Plain不同,所以Next方法也重写了,不然报那个unexpected server challenge的错误,这样就能顺利地使用用户名和密码认证,发邮件的认证部分这样写:
auth := LoginAuth("username, password)
如此一来就可以成功发送邮件了。
但是当我换用另一台邮件服务器时,又出现了certificate signed by unknown authority
部署到服务器上时,错误显示为cannot validate certificate for 10.11.64.80 because it doesn't contain any IP SANS
总之都是类似于认证的问题。这两台服务器的区别是第一台使用465端口,即smtps,而第二台使用25端口。
查看smtp.go发现func SendMail()中有这样一段
if ok, _ := c.Extension("STARTTLS"); ok {
config := &tls.Config{ServerName: c.serverName}
if testHookStartTLS != nil {
testHookStartTLS(config)
}
if err = c.StartTLS(config); err != nil {
return err
}
}
我干脆把SendMail方法拷出来,去掉这一段判断,同时smtp里面涉及到的func和struct都拷出来,写了一个新的.go,在发邮件的时候直接使用这个新的SendMail。部分原有的公共方法和结构不用拷出,直接以smtp.调用,如此一来就能直接用端口25的那台服务器发邮件了。
虽然邮件发送成功,但是查看日志里总输出一个错误250 Mail OK queued as XXXX,看着很不爽,但这输出又不像错误。按照telnet hostname port后的操作对照SendMail的执行过程。发现在发送DATA指令之后,会收到一个回复码354,接收输入邮件内容,以句号回车结尾后,会再收到一个250的回复。在代码中,发送了DATA,收到354,接着发送邮件内容,代码并未接收这个250。最后发送QUIT,这里收到的是上一个回复码250,和QUIT的正常回复码221作比较,程序就会返回error。我也不知道哪个函数可以只接收回复,简单起见,干脆在Quit函数里发了两遍QUIT,判断第一个返回250,第二个返回221,终于不再报错。
研究完这个函数,对smtp就从一无所知到相当了解了。另外,要从根本上解决问题,还是升级为SSL吧!
golang笔记:net/smtp的更多相关文章
- golang笔记1
golang笔记1 go代码是用包来组织的,每个包有一个或多个go文件组成,这些go文件文件放在一个文件夹中 每个源文件开始都用一个package声明,指明本源文件属于哪个包 pakage声明后紧跟这 ...
- Golang笔记(二)面向对象的设计
Golang笔记(二)面向对象的设计 Golang本质还是面向过程的语言,但它实现了一些OOP的特性,包括抽象.封装.继承和多态. 抽象和封装 Golang和C语言一样以struct为数据结构核心,不 ...
- Golang笔记(一)简洁的语言风格
Golang笔记(一)简洁的语言风格 概述 Golang继承了很多C语言的风格,寡人使用了十几年C语言,切换到Golang时上手很快,并且随着深入的使用,越来越喜欢这门语言.Golang最直观的感受是 ...
- Golang笔记集
学习Golang了, 下面分享我的, 还有我收集的Golang的学习资料 我的基础笔记地址: https://github.com/zhuchangwu/go-study-notes 其他参考: Go ...
- Golang笔记整理--One day
题外话: 很早就有整理学习笔记的想法,今天将想法付诸于行动,将Golang相关知识系统整理一遍,此分类为Golang学习笔记,最近开始学习这门语言的同学可以参考. 一 第一个Go程序: hello.g ...
- golang笔记——函数与方法
如果你遇到没有函数体的函数声明,表示该函数不是以Go实现的. package math func Sin(x float64) float //implemented in assembly lang ...
- golang笔记——IDE
可选方案有 Lite IDE\GoSublime\Visual Studio Code\Goclipse\Vim 1.Lite IDE 这是国人开发的开源且跨平台的 golang 专属IDE,也算是目 ...
- golang笔记——包
1.包简述 GO本身没有项目的概念,只有包,包括可执行包和不可执行包,而不管什么包,都应该包含在 $GOPATH/src 目录下,GO命令和编译器会在 $GOPATH/src 目录下搜索相应的包.比如 ...
- golang笔记——环境搭建
1.下载安装 从 https://golang.org/dl/ 这里下载最新版本的 golang 安装包,分别有 Windows\Linux\Apple OSX\源码包. golang的官方网站是 h ...
随机推荐
- 【题解】SHOI2001化工厂装箱员
————传送:洛谷P2530 这道题目还是挺简单的,状态也容易想到. 数据范围非常的小,所以即便是很多维度,复杂度也完全可以接受.定义状态:dp[i][a][b][c]为手上的货物拿到第i个时三种物品 ...
- bzoj2827: 千山鸟飞绝 平衡树 替罪羊树 蜜汁标记
这道题首先可以看出坐标没有什么意义离散掉就好了. 然后你就会发现你要每次都更改坐标,而一旦更改受影响的是坐标里的所有数,要是一个一个的改,会不可描述. 所以换个视角,我们要找的是某只鸟所到每个坐标时遇 ...
- Intellij Idea debug 远程部署的的tomcat项目
web项目部署到tomcat上之后,有时需要打断点单步调试,如果用的是Intellij idea,可以通过如下方法实现: 开启debug端口,启动tomcat 以tomcat7.0.75为例,打开bi ...
- Codeforces Round #351 (VK Cup 2016 Round 3, Div. 2 Edition) D
D. Bear and Two Paths time limit per test 2 seconds memory limit per test 256 megabytes input standa ...
- WebForm 在 Global.asax 中捕获全局异常
/// <summary> /// 捕获全局异常 /// </summary> /// <param name="sender">sender& ...
- java 身份证15位转18位
/** * 根据身份证号获取性别 * * @param pid * 身份证号 * @return 性别 F为女M为男 */ public static String getSexByPid(Strin ...
- JS向右弹出DIV,点击可向左隐藏。我用jquery可以从左下角像右上角隐藏,怎么从做向右隐藏呢?
弹出的DIV如果是绝对定位,就用right固定位子,如果不是就用float:right:Jquery中有个函数animate是自定义动画效果,$("#shou").click(fu ...
- jquery中lhgdialog插件(一)
一:前言 最近在使用jquery的控件,其实以前也写但是突然之间遇到了需要从弹出窗口传值到父窗口,突然觉得这种传值的方式其实也是需要javascript的基础的,但是我自己还没有去真正的做过,所以还是 ...
- 【BZOJ2326】【HNOI2011】数学作业 [矩阵乘法][DP]
数学作业 Time Limit: 10 Sec Memory Limit: 128 MB[Submit][Status][Discuss] Description Input 输入文件只有一行为用空 ...
- BestCoder Round #39 解题报告
现场只做出前三题w 不过不管怎样这既是第一次认真打BC 又是第一次体验用在线编译器调代码 订正最后一题花了今天一整个下午(呜呜 收获还是比较大的^_^ Delete wld有n个数(a1,a2,... ...