{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE OverloadedStrings #-}
module Main (main) where
import Control.Applicative
import Pipes
import qualified Pipes.Prelude as P
import qualified Data.ByteString.Char8 as B
import Data.Certificate.X509 (X509)
import Data.Char (toUpper)
import Data.Monoid ((<>))
import qualified Network.Simple.TCP.TLS as TLS
import qualified Pipes.Network.TCP.TLS as TLS
import Network.TLS.Extra as TE
import qualified Network.TLS as T
import System.Console.GetOpt
import System.Environment (getProgName, getArgs)
import qualified Data.CertificateStore as C
server :: TLS.Credential -> TLS.HostPreference -> TLS.ServiceName
-> Maybe C.CertificateStore -> IO ()
server cred hp port mcs = do
let ss = TLS.makeServerSettings cred mcs
TLS.serve ss hp port $ \(ctx,caddr) -> do
putStrLn $ show caddr <> " joined."
runEffect $ TLS.fromContext ctx >-> P.map (B.map toUpper) >-> TLS.toContext ctx
putStrLn $ show caddr <> " quit."
main :: IO ()
main = TLS.withSocketsDo $ do
args <- getArgs
case getOpt RequireOrder options args of
(actions, [hostname,port], _) -> do
opts <- foldl (>>=) (return defaultOptions) actions
let !cred = TLS.Credential (optServerCert opts) (optServerKey opts) []
server cred (TLS.Host hostname) port
(C.makeCertificateStore . pure <$> optCACert opts)
(_,_,msgs) -> do
pn <- getProgName
let header = "Usage: " <> pn <> " [OPTIOTLS] HOSTNAME PORT"
error $ concat msgs ++ usageInfo header options
--------------------------------------------------------------------------------
-- The boring stuff below is related to command line parsing
data Options = Options
{ optServerCert :: X509
, optServerKey :: T.PrivateKey
, optCACert :: Maybe X509
} deriving (Show)
defaultOptions :: Options
defaultOptions = Options
{ optServerCert = error "Missing optServerCert"
, optServerKey = error "Missing optServerKey"
, optCACert = Nothing
}
options :: [OptDescr (Options -> IO Options)]
options =
[ Option [] ["cert"] (ReqArg readServerCert "FILE") "Server certificate"
, Option [] ["key"] (ReqArg readServerKey "FILE") "Server private key"
, Option [] ["cacert"] (OptArg readCACert "FILE")
"CA certificate to verify a client certificate, if given"
]
readServerCert :: FilePath -> Options -> IO Options
readServerCert arg opt = do
cert <- TE.fileReadCertificate arg
return $ opt { optServerCert = cert }
readServerKey :: FilePath -> Options -> IO Options
readServerKey arg opt = do
key <- TE.fileReadPrivateKey arg
return $ opt { optServerKey = key }
readCACert :: Maybe FilePath -> Options -> IO Options
readCACert Nothing opt = return opt
readCACert (Just arg) opt = do
cert <- TE.fileReadCertificate arg
return $ opt { optCACert = Just cert }