pipes-cacophony-0.1.0: examples/echo-server/Main.hs
{-# LANGUAGE OverloadedStrings #-}
module Main where
import Control.AutoUpdate (mkAutoUpdate, defaultUpdateSettings, updateAction)
import Control.Exception (SomeException, displayException, handle)
import Data.Aeson (encode, object, (.=))
import Data.ByteString (ByteString, readFile, writeFile)
import Data.ByteString.Char8 (unpack)
import Data.ByteString.Lazy.Char8 (append)
import Data.Traversable (forM)
import Data.UnixTime (formatUnixTime, fromEpochTime)
import Pipes.Network.TCP
import Prelude hiding (readFile, writeFile)
import System.Directory (doesFileExist)
import System.Environment (getArgs)
import System.Log.FastLogger (toLogStr, pushLogStr, LoggerSet, newFileLoggerSet)
import System.Posix (epochTime)
import System.Posix.Files (setFileCreationMask)
import Crypto.Noise.Curve
import Crypto.Noise.Curve.Curve25519
import Crypto.Noise.Types (bsToSB', sbToBS')
import Handshakes
readKey :: FilePath -> IO (KeyPair Curve25519)
readKey f = fmap (curveBytesToPair . bsToSB') (readFile f)
genAndWriteKey :: FilePath -> IO (KeyPair Curve25519)
genAndWriteKey f = do
pair@(sec, pub) <- curveGenKey
writeFile f $ (sbToBS' . curveSecToBytes) sec
writeFile (f `mappend` ".pub") $ (sbToBS' . curvePubToBytes) pub
return pair
processKey :: FilePath -> IO (KeyPair Curve25519)
processKey f = do
exists <- doesFileExist f
if exists then
readKey f
else
genAndWriteKey f
main :: IO ()
main = do
[port] <- getArgs
[is, rs, re] <- forM ["init_static", "resp_static", "resp_ephemeral"] processKey
logHandle <- openLog "debug.log"
au <- mkAutoUpdate defaultUpdateSettings { updateAction = getDateTime }
let exLogger = logException logHandle au
keys = HandshakeKeys is rs re
serve HostAny port $ \(s, ip) -> do
let clientReceiver = fromSocketTimeout 120000000 s 4096
clientSender = toSocket s
handle (exLogger ip) $ processHandshake keys (clientReceiver, clientSender)
openLog :: FilePath -> IO LoggerSet
openLog file = do
_ <- setFileCreationMask 0o000
newFileLoggerSet 1 file
logException :: LoggerSet
-> IO ByteString
-> SockAddr
-> SomeException
-> IO ()
logException ls getCachedDate ip ex = do
zdt <- getCachedDate
(pushLogStr ls . toLogStr) . (`append` "\n") . encode $
object [ "date" .= unpack zdt
, "exception" .= displayException ex
, "ip" .= show ip
]
getDateTime :: IO ByteString
getDateTime = epochTime >>= formatUnixTime "%Y-%m-%d %H:%M:%S %z" . fromEpochTime