haskell - 正确使用 HsOpenSSL API 来实现 TLS 服务器

标签 haskell openssl ssl

我正在尝试弄清楚如何正确使用 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 ? HsOpenSSL API 提供的函数调用的并发/并行执行(在上述用例的上下文中)的危险在哪里?

PS:我仍在努力完全理解如何使代码健壮 w.r.t.异常和资源泄漏。因此,尽管这不是这个问题的主要焦点,但如果您发现上面的代码有问题,请发表评论。

最佳答案

为此,您需要将 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 session

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

关于haskell - 正确使用 HsOpenSSL API 来实现 TLS 服务器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8241821/

相关文章:

haskell - 如何在 Windows 7 中配置 cabal?

c# - Java/C#类型系统有哪些不足?

c++ - 如果winsock2套接字是非阻塞的,与其关联的SSL对象是否也会表现出非阻塞行为?

android - 如何使 Android 上的 NanoHTTPD 接受来自具有专用客户端证书的客户端的连接

ssl - 如何创建受信任的自签名 SSL 证书

ssl - CSR 是如何编码的?

arrays - Haskell repa --- 使用索引进行映射

exception - 为什么Haskell中的()是Enum类型却没有实现succ函数

r - 安装 R 包 Ubuntu 22.04.1 LTE 时的包 libcurl4-openssl-dev 问题

java - 无法在java中读取公钥进行验证