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 セッションを確立します。

関連記事