packages feed

network-transport-zeromq-0.1.0.0: src/System/ZMQ4/Utils.hs

{-# LANGUAGE OverloadedStrings #-}
module System.ZMQ4.Utils
  ( bindFromRangeRandom
  , bindFromRangeRandomM
  , authManager
  , closeZeroLinger
  )
  where

import Control.Monad ( forever )
import Control.Monad.Catch
import Control.Concurrent.Async
import Data.ByteString
import Data.List.NonEmpty
import System.Random ( randomRIO )
import Text.Printf


import           System.ZMQ4.Monadic
      ( ZMQ 
      )
import qualified System.ZMQ4.Monadic as M
import           System.ZMQ4
      ( Context
      , errno
      )
import qualified System.ZMQ4         as P

-- | Bind socket to the random port in a given range.
bindFromRangeRandomM :: M.Socket z t
                     -> String -- ^ Address
                     -> Int    -- ^ Min port
                     -> Int    -- ^ Max port
                     -> Int    -- ^ Max tries
                     -> ZMQ z Int
bindFromRangeRandomM sock addr mI mA tr = go tr
    where
      go 0 = error "!" -- XXX: throw correct error
      go x = do
        port   <- M.liftIO $ randomRIO (mI,mA)
        result <- try $ M.bind sock (printf "%s:%i" addr port)
        case result of
            Left e
              | errno e == -1 -> go (x - 1)
              | otherwise -> throwM e
            Right () -> return port

bindFromRangeRandom :: P.Socket t
                    -> String -- ^ Address
                    -> Int    -- ^ Min port
                    -> Int    -- ^ Max port
                    -> Int    -- ^ Max tries
                    -> IO Int
bindFromRangeRandom sock addr mI mA tr = go tr
    where
      go 0 = error "!" -- XXX: throw correct error
      go x = do
        port   <- randomRIO (mI,mA)
        result <- try $ P.bind sock (printf "%s:%i" addr port)
        case result of
            Left e
              | errno e == -1 -> go (x - 1)
              | otherwise -> throwM e
            Right () -> return port

-- | One possible password authentification
authManager :: P.Context -> ByteString -> ByteString -> IO (Async ())
authManager ctx user pass = do
    req <- P.socket ctx P.Rep
    P.bind req "inproc://zeromq.zap.01"
    async $ forever $ do
      ("1.0":requestId:domain:ipAddress:identity:mech:xs) <- P.receiveMulti req
      case mech of
        "PLAIN" -> case xs of
           (pass':user':_)
             | user == user' && pass == pass' -> do
                P.sendMulti req $ "1.0" :| [requestId, "200", "OK", "", ""]
             | otherwise -> P.sendMulti req $ "1.0" :| [requestId, "400", "Credentials are not implemented", "", ""]
           _ -> P.sendMulti req $ "1.0" :| [requestId, "500", "Method not implemented", "", ""]
        _ -> P.sendMulti req $ "1.0" :| [requestId, "500", "Method not implemented", "", ""]


-- | Close socket immideately.
closeZeroLinger :: P.Socket a -> IO ()
closeZeroLinger sock = do
  P.setLinger (P.restrict 0) sock
  P.close sock