Я пытаюсь понять, как правильно использовать API OpenSSL.Session в параллельном контексте.
Например, предположим, что я хочу реализовать a 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 для одновременного / параллельного выполнения (в контексте приведенного выше варианта использования) вызовов функций, предоставляемых API HsOpenSSL?
PS: Я все еще пытаюсь полностью понять, как сделать код устойчивым к исключениям и утечкам ресурсов. Так что, хотя этот вопрос не является основным, но если вы заметили что-то плохое в приведенном выше коде, оставьте комментарий.
full-duplex ssl-rewrapping tcp-forwarding
), но вместо него использовалNetwork.TLS
(packagetls
). И это было некрасиво. Вы можете найти его здесь , если вам это интересно.Ответы:
Для этого вам нужно заменить
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
чтобы завершить сеанс SSLlet 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 (например, чтобы предоставить local-ssl-to-remote-non- ssl прокси)?