4

gmail 経由でメールを送信するための小さなスクリプトを作成しようとしています。
gmail に接続できますが、hanshake しようとすると失敗します。
握手をするための指針はありますか?

コードは次のとおりです。

import Network.Socket
import Network
import Network.TLS
import Network.TLS.Extra

import Crypto.Random
import Data.CertificateStore -- to remove
import System.Certificate.X509 --to use I think

import System.IO
import Text.Printf

import Control.Monad (forever)
import qualified Data.ByteString.Char8 as B


main :: IO ()
main = emailGmail2

tListen :: Context -> IO ()
tListen ctx =
    forever $ recvData ctx >>= B.putStrLn

cWrite :: Handle -> String -> IO ()
cWrite h s  = do
    hPrintf h "%s\r\n" s
    printf    "> %s\n" s

cListen :: Handle -> IO () 
cListen h =
    forever $ hGetLine h >>= putStrLn

emailGmail2 = do
    let
        host = "smtp.gmail.com"
        port = 587
        params = defaultParamsClient
    g <- newGenIO :: IO SystemRandom
    h <- connectTo host (PortNumber (fromIntegral port))
    hSetBuffering h LineBuffering
    cWrite h "EHLO"
    cWrite h "STARTTLS"
    --cListen h
    con <- contextNewOnHandle h params g
    handshake con
    tListen con

そして、ここにエラーがあります:

HandshakeFailed (Error_Packet_Parsing "読み取りに失敗しました: 無効なヘッダー タイプ: 50\nFrom:\theader\n\n")

4

1 に答える 1

7

このエラーは、部分的に SMTP プロトコルの処理に関係しています。Secure SMTP over TLS RFC 2487の RFC を見ると、クライアントとサーバーの対話の例があります。

S: <waits for connection on TCP port 25>
C: <opens connection>
S: 220 mail.imc.org SMTP service ready
C: EHLO mail.ietf.org
S: 250-mail.imc.org offers a warm hug of welcome
S: 250 STARTTLS
C: STARTTLS
S: 220 Go ahead
C: <starts TLS negotiation>
C & S: <negotiate a TLS session>
C & S: <check result of negotiation>
C: <continues by sending an SMTP command>
. . .

あなたのコードでは、EHLO と STARTTLS を送信し、すぐにハンドシェイク ネゴシエーションを開始しています。サーバーがまだ上記の 250 および 220 コードの一部を送信しており、TLS ライブラリがこれらを TLS メッセージとして解釈しようとしており、これが問題を引き起こしていると思われます。

実際、別のターミナルを開いて netcat でポート 587 をリッスンし、プログラムを変更して localhost に接続すると、「250 STARTTLS」で応答すると同じエラーが発生します。

これを変更すると、プログラムが機能するようになりました。

import Network.Socket
import Network
import Network.TLS
import Network.TLS.Extra

import Crypto.Random
import qualified Crypto.Random.AESCtr as RNG
import System.IO
import Text.Printf

import Control.Monad (when)
import Data.List (isPrefixOf)

ciphers :: [Cipher]
ciphers =
        [ cipher_AES128_SHA1
        , cipher_AES256_SHA1
        , cipher_RC4_128_MD5
        , cipher_RC4_128_SHA1
        ]

main :: IO ()
main = emailGmail2

cWrite :: Handle -> String -> IO ()
cWrite h s  = do
    hPrintf h "%s\r\n" s
    printf    "> %s\n" s

cWaitFor :: Handle -> String -> IO ()
cWaitFor h str = do
    ln <- hGetLine h
    putStrLn ln
    when (not $ str `isPrefixOf` ln) (cWaitFor h str)

emailGmail2 = do
    let
        host = "smtp.gmail.com"
        port = 587
        params = defaultParamsClient{pCiphers = ciphers}
    g <- RNG.makeSystem
    h <- connectTo host (PortNumber (fromIntegral port))
    hSetBuffering h LineBuffering
    cWrite h "EHLO"
    cWaitFor h "250-STARTTLS"
    cWrite h "STARTTLS"
    cWaitFor h "220"
    con <- contextNewOnHandle h params g
    handshake con
    bye con

また、いくつかの暗号を追加する必要があることにも注意してNetwork.TLS.Extrasください。そうしないと、Illegal Parameter エラーが発生します。このためのコードと、Vincent のテストからのロギングを Githubページに追加するコードを見つけました。

別の注意: さらに問題が発生した場合に備えて、コマンド ライン プログラム gnutls-cli と ssldump を使用して、上記の暗号の問題をデバッグしたことを指摘しておきます。

于 2012-11-29T20:58:13.513 に答える