把 CONNECT 隧道交给 Go 的 SMTP 客户端
在已建立的隧道上使用 smtp.NewClient,并把 SMTP 主机名和 STARTTLS 握手与代理设置分开。
smtp.Dial 会向自己的地址打开新连接。通过 HTTP CONNECT 代理取得连接后,应把这条现有流交给 smtp.NewClient,而不是再用它拨号。
对于要求在提交端口上使用 STARTTLS 的 SMTP 服务器,交接可以这样写:
func smtpOverTunnel(conn net.Conn, mailHost string) (*smtp.Client, error) {
client, err := smtp.NewClient(conn, mailHost)
if err != nil {
conn.Close()
return nil, err
}
err = client.StartTLS(&tls.Config{
ServerName: mailHost,
MinVersion: tls.VersionTLS12,
})
if err != nil {
client.Close()
return nil, err
}
return client, nil
}涉及的包是 net、net/smtp 和 crypto/tls。调用方提供的隧道已经连接到目标 SMTP 主机和端口,并且 代理的 CONNECT 交换已经完成。只向代理打开 TCP 是不够的:HTTP 响应并不是 SMTP 问候语。
mailHost 是 SMTP 服务器的主机名,不含端口。SMTP 认证和证书校验都要沿用这个身份。到 HTTPS 代理的 TLS 连接保护的是路由的另一段。
返回的客户端可直接用于应用已有的 SMTP 认证和邮件事务。用完后关闭它。拨号、CONNECT 以及后续协议操作都应受应用的截止时间策略约束。
代理必须允许 CONNECT 到所选邮件端口。有些代理只允许 443 端口上的 HTTPS 目标。对于隐式 TLS 的 SMTP,应在调用 NewClient 之前建立目标 TLS 会话,而不是发出 STARTTLS。