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 では、STARTTLS を発行する代わりに NewClient を呼ぶ前に宛先の TLS セッションを確立します。