正确使用HsOpenSSL API实施TLS服务器


141

我试图弄清楚如何在并发上下文中正确使用OpenSSL.Session API

例如,假设我要实现stunnel-style ssl-wrapper,我希望具有以下基本框架结构,该结构可以实现幼稚full-duplex tcp-port-forwarder:

runProxy :: PortID -> AddrInfo -> IO ()
runProxy localPort@(PortNumber lpn) serverAddrInfo = do
  listener <- listenOn localPort

  forever $ do
    (sClient, clientAddr) <- accept listener

    let finalize sServer = do
            sClose sServer
            sClose sClient

    forkIO $ do
        tidToServer <- myThreadId
        bracket (connectToServer serverAddrInfo) finalize $ \sServer -> do
            -- execute one 'copySocket' thread for each data direction
            -- and make sure that if one direction dies, the other gets
            -- pulled down as well
            bracket (forkIO (copySocket sServer sClient
                             `finally` killThread tidToServer))
                    (killThread) $ \_ -> do
                copySocket sClient sServer -- "controlling" thread

 where
  -- |Copy data from source to dest until EOF occurs on source
  -- Copying may also be aborted due to exceptions
  copySocket :: Socket -> Socket -> IO ()
  copySocket src dst = go
   where
    go = do
        buf <- B.recv src 4096
        unless (B.null buf) $ do
            B.sendAll dst buf
            go

  -- |Create connection to given AddrInfo target and return socket
  connectToServer saddr = do
    sServer <- socket (addrFamily saddr) Stream defaultProtocol
    connect sServer (addrAddress saddr)
    return sServer

如何将上面的骨架转换为full-duplex ssl-wrapping tcp-forwarding proxy?WRT对HsOpenSSL API提供的函数调用的并发/并行执行(在上述用例的上下文中)有何危险?

PS:我仍在努力全面理解如何使代码对异常和资源泄漏更为可靠。因此,尽管不是这个问题的主要焦点,但是如果您在上面的代码中发现了不好的地方,请发表评论。


11
我认为这对于SO来说可能是一个过于广泛的问题。
唐·斯图尔特

1
我会就此回复您:-)
Abhineet 2012年

2
链接到文档被破坏,这里是一个谁的工作:hackage.haskell.org/packages/archive/HsOpenSSL/0.10.2/doc/html/...
帕斯卡尔QYY

4
我做了类似的(full-duplex ssl-rewrapping tcp-forwarding),但改用Network.TLS(package tls)。而且很丑。如果有兴趣,可以在这里找到。

Answers:


7

为此,您需要替换copySocket为两个不同的函数,一个处理从普通套接字到SSL的数据,另一个处理从SSL到普通套接字的数据:

  copyIn :: SSL.SSL -> Socket -> IO ()
  copyIn src dst = go
   where
    go = do
        buf <- SSL.read src 4096
        unless (B.null buf) $ do
            SB.sendAll dst buf
            go

  copyOut :: Socket -> SSL.SSL -> IO ()
  copyOut src dst = go
   where
    go = do
        buf <- SB.recv src 4096
        unless (B.null buf) $ do
            SSL.write dst buf
            go

然后,您需要进行修改,connectToServer以便建立SSL连接

  -- |Create connection to given AddrInfo target and return socket
  connectToServer saddr = do
    sServer <- socket (addrFamily saddr) Stream defaultProtocol
    putStrLn "connecting"
    connect sServer (addrAddress saddr)
    putStrLn "establishing ssl context"
    ctx <- SSL.context
    putStrLn "setting ciphers"
    SSL.contextSetCiphers ctx "DEFAULT"
    putStrLn "setting verfication mode"
    SSL.contextSetVerificationMode ctx SSL.VerifyNone
    putStrLn "making ssl connection"
    sslServer <- SSL.connection ctx sServer
    putStrLn "doing handshake"
    SSL.connect sslServer
    putStrLn "connected"
    return sslServer

并更改finalize以关闭SSL会话

let finalize sServer = do
        putStrLn "shutting down ssl"
        SSL.shutdown sServer SSL.Unidirectional
        putStrLn "closing server socket"
        maybe (return ()) sClose (SSL.sslSocket sServer)
        putStrLn "closing client socket"
        sClose sClient

最后,不要忘记withOpenSSL像这样在其中运行主要内容

main = withOpenSSL $ do
    let hints = defaultHints { addrSocketType = Stream, addrFamily = AF_INET }
    addrs <- getAddrInfo (Just hints) (Just "localhost") (Just "22222")
    let addr = head addrs
    print addr
    runProxy (PortNumber 11111) addr

这已经很有帮助;这提供了与stunnels客户端模式相对应的本地非SSL远程ssl代理,您是否还可以提供一个示例来了解如何侦听本地ssl套接字(例如,提供本地非SSL远程非ssl代理)ssl代理)?
hvr
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.