    {-# LANGUAGE CApiFFI #-}
    {-# LANGUAGE CPP     #-}

    module Network.DNS.FFI where

    import           Control.Concurrent.MVar
    import           Control.Monad           (unless)
    import           Control.Exception       (bracket)
    import           Foreign.C
    import           Foreign.Marshal.Alloc
    import           Foreign.Ptr
    import           System.IO.Unsafe        (unsafePerformIO)

    #if !defined(USE_RES_NQUERY)
    # error USE_RES_NQUERY not defined
    #endif

    {-# INLINE resIsReentrant #-}
    -- | Whether the reentrant DNS resolver C API (e.g. @res_nquery(3)@, @res_nsend(3)@) is being used.
    --
    -- If this this 'False', then as a fall-back
    -- @res_query(3)@/@res_send(3)@ are used, protected by a global mutex.
    --
    -- @since 0.1.1.0
    resIsReentrant :: Bool
    #if USE_RES_NQUERY
    resIsReentrant :: Bool
    resIsReentrant = Bool
    True
    #else
    resIsReentrant = False
    #endif

    #if !defined(SIZEOF_RES_STATE)
    # error SIZEOF_RES_STATE not defined
    #endif

    #if USE_RES_NQUERY && (SIZEOF_RES_STATE <= 0)
    # error broken invariant
    #endif

    {-# INLINE sizeOfResState #-}
    sizeOfResState :: CSize
    sizeOfResState :: CSize
    sizeOfResState = SIZEOF_RES_STATE

    data CResState

    {-# NOINLINE resolvLock #-}
    resolvLock :: MVar ()
    resolvLock :: MVar ()
    resolvLock = IO (MVar ()) -> MVar ()
    forall a. IO a -> a
    unsafePerformIO (IO (MVar ()) -> MVar ()) -> IO (MVar ()) -> MVar ()
    forall a b. (a -> b) -> a -> b
    $ () -> IO (MVar ())
    forall a. a -> IO (MVar a)
    newMVar ()

    withCResState :: (Ptr CResState -> IO a) -> IO a
    withCResState :: forall a. (Ptr CResState -> IO a) -> IO a
    withCResState Ptr CResState -> IO a
    act
      | Bool
    resIsReentrant = Int -> (Ptr CResState -> IO a) -> IO a
    forall a b. Int -> (Ptr a -> IO b) -> IO b
    allocaBytes (CSize -> Int
    forall a b. (Integral a, Num b) => a -> b
    fromIntegral CSize
    sizeOfResState) ((Ptr CResState -> IO a) -> IO a)
    -> (Ptr CResState -> IO a) -> IO a
    forall a b. (a -> b) -> a -> b
    $ \Ptr CResState
    ptr -> do
                             _ <- Ptr CResState -> CInt -> CSize -> IO (Ptr CResState)
    forall a. Ptr a -> CInt -> CSize -> IO (Ptr a)
    c_memset Ptr CResState
    ptr CInt
    0 CSize
    sizeOfResState
                             act ptr
      | Bool
    otherwise = MVar () -> (() -> IO a) -> IO a
    forall a b. MVar a -> (a -> IO b) -> IO b
    withMVar MVar ()
    resolvLock ((() -> IO a) -> IO a) -> (() -> IO a) -> IO a
    forall a b. (a -> b) -> a -> b
    $ \() -> Ptr CResState -> IO a
    act Ptr CResState
    forall a. Ptr a
    nullPtr

    withCResInit :: Ptr CResState -> IO a -> IO a
    withCResInit :: forall a. Ptr CResState -> IO a -> IO a
    withCResInit Ptr CResState
    stptr IO a
    act = IO () -> (() -> IO ()) -> (() -> IO a) -> IO a
    forall a b c. IO a -> (a -> IO b) -> (a -> IO c) -> IO c
    bracket IO ()
    initialize () -> IO ()
    forall {p}. p -> IO ()
    finalize ((() -> IO a) -> IO a) -> (() -> IO a) -> IO a
    forall a b. (a -> b) -> a -> b
    $ IO a -> () -> IO a
    forall a b. a -> b -> a
    const IO a
    act
      where
        initialize :: IO ()
    initialize = do
            rc1 <- Ptr CResState -> IO CInt
    c_res_opt_set_use_dnssec Ptr CResState
    stptr
            unless (rc1 == 0) $
                fail "res_init(3) failed"
            resetErrno
        finalize :: p -> IO ()
    finalize p
    _ = Ptr CResState -> IO ()
    c_res_close Ptr CResState
    stptr

    -- void *memset(void *s, int c, size_t n);
    foreign import capi unsafe "string.h memset" c_memset :: Ptr a -> CInt -> CSize -> IO (Ptr a)

    -- int res_query(void *, const char *dname, int class, int type, unsigned char *answer, int anslen);
    foreign import capi safe "hs_resolv.h hs_res_query" c_res_query :: Ptr CResState -> CString -> CInt -> CInt -> Ptr CChar -> CInt -> IO CInt

    -- int res_send(void *, const unsigned char *msg, int msglen, unsigned char *answer, int anslen);
    foreign import capi safe "hs_resolv.h hs_res_send" c_res_send :: Ptr CResState -> Ptr CChar -> CInt -> Ptr CChar -> CInt -> IO CInt

    -- int res_opt_set_use_dnssec(void *);
    foreign import capi safe "hs_resolv.h res_opt_set_use_dnssec" c_res_opt_set_use_dnssec :: Ptr CResState -> IO CInt

    -- int hs_res_mkquery(void *, const char *dname, int class, int type, unsigned char *req, int reqlen0);
    foreign import capi safe "hs_resolv.h hs_res_mkquery" c_res_mkquery :: Ptr CResState -> CString -> CInt -> CInt -> Ptr CChar -> CInt -> IO CInt

    -- void hs_res_close(void *);
    foreign import capi safe "hs_resolv.h hs_res_close" c_res_close :: Ptr CResState -> IO ()

    -- void *hs_get_h_errno(void *);
    foreign import capi unsafe "hs_resolv.h hs_get_h_errno" c_get_h_errno :: Ptr CResState -> IO CInt