0%

SMTP465端口和587端口

今天在做concourse发邮件这部分,一看官网上有开箱即用的resource, 心想今天能早点下班了。官方地址如下:

https://github.com/pivotal-cf/email-resource

问题

  1. 根据README.md配置了关于邮件发送这块的resource,发现一直卡Dial状态,接着就超时了。

我想是官方pivotal-cf group下的,再怎么也不会出问题吧,应该是我哪里配置配的有问题,然后我又捣鼓了一个小时,还是没搞定。

pipeline配置

1
2
3
4
5
6
7
8
9
10
11
12
resources:
- name: send-an-email
type: email
source:
smtp:
host: smtp.example.com
port: "465" # this must be a string
username: a-user
password: my-password
from: [email protected]
to: [ "[email protected]", "[email protected]" ] #optional if `params.additional_recipient` is specified

我的pipeline resource定义完全就是照搬的github上的样例,就是把他的587端口修改成了465,因为我司是用的阿里云邮箱,使用的是465端口,为什么就无法发送了呢?

原因分析

  1. 接着我选了个587端口的邮箱,gmail,发现是可以正常发送的。
  2. 难道是被阿里云禁止? 这个应该不可能,因为换成用python的脚本是可以正常发送,而且阿里云只禁止25端口

465端口和587端口的区别

好好的为什么会有两个端口,我就去谷歌上查了下。

  1. 465端口可以理解为全程走TLS,在你的本地mail sender和mail server建联的时候就是采用了TLS。
  2. 而587不是,587端口你可以通过明文,如果可能,则可以升级使用TLS方式发送邮件。采用的是STARTTLS协议
  3. 两者在行为上还是存在区别。

源码分析

https://github.com/pivotal-cf/email-resource/blob/master/out/sender.go

1
2
3
4
5
6
7
8
9
10
11
12
13
14
c, err = smtp.Dial(fmt.Sprintf("%s:%s", s.host, s.port))

...
..
if ok, _ := c.Extension("STARTTLS"); ok {
config := s.tlsConfig()

if err = c.StartTLS(config); err != nil {
return errors.Wrap(err, "unable to start TLS")
}
}

..

可以看到他用smtp.Dial方法去连接,然后调用StartTLS方法传入配置

如果查看smtp.Dial方法的源码,可以看到其方法是调用的net.Dial方法,这个方法并不是用来建立SSL连接的

1
2
3
4
5
6
7
8
9
10

func Dial(addr string) (*Client, error) {
conn, err := net.Dial("tcp", addr)
if err != nil {
return nil, err
}
host, _, _ := net.SplitHostPort(addr)
return NewClient(conn, host)
}

解决方法

既然知道了465的行为,也就是在一开始的时候就需要去做ssl的建联,而非通过smtp.Dial,所以将代码修改如下:

https://github.com/huangyisan/email-resource/blob/master/out/sender.go

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
if s.port == "465" {
fmt.Fprintln(os.Stderr, "Dialing with TLS for 465 Port...")
conn, err := tls.Dial("tcp", fmt.Sprintf("%s:%s",s.host,s.port), s.tlsConfig())
if err != nil {
return errors.Wrap(err, "unable to start TLS")
}
c, err = smtp.NewClient(conn, s.host)
defer c.Close()

} else {
c, err = smtp.Dial(fmt.Sprintf("%s:%s", s.host, s.port))
if err != nil {
return errors.Wrap(err, "Error Dialing smtp server")
}
defer c.Close()

if ok, _ := c.Extension("STARTTLS"); ok {
config := s.tlsConfig()

if err = c.StartTLS(config); err != nil {
return errors.Wrap(err, "unable to start TLS")
}
}
}

在建联初期,使用tls.Dial方法,传入tls的配置进行建联,得到一个conn对象,然后使用smtp.NewClient方法传入conn对象,得到*smtp.Client对象, 让该对象进行邮件发送处理。

refer

https://stackoverflow.com/questions/15796530/what-is-the-difference-between-ports-465-and-587

https://sendgrid.com/blog/what-is-starttls/

https://sendgrid.com/blog/whats-the-difference-between-ports-465-and-587/

https://gist.github.com/chrisgillis/10888032

坚持原创技术分享,您的支持将鼓励我继续创作!