Skip to content

Throw h_errno specific exceptions after res_query errors. #17

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 12 commits into from
Mar 30, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions cbits/hs_resolv.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@
# include <netinet/in.h>
#endif

#if defined(HAVE_DECL_H_ERRNO)
# include <netdb.h>
#endif

#if defined(HAVE_ARPA_NAMESER_H)
# include <arpa/nameser.h>
#endif
Expand All @@ -30,6 +34,18 @@
# error broken invariant
#endif

/* Macro to calculate error code returned by hs_get_h_errno */
#define __HS_GET_H_ERRNO(h_errno) \
switch(h_errno) \
{ \
case HOST_NOT_FOUND: return 1; \
case NO_DATA: return 2; \
case NO_RECOVERY: return 3; \
case TRY_AGAIN: return 4; \
case 0: return 0; \
default: return -1; \
}

#if USE_RES_NQUERY

inline static int
Expand Down Expand Up @@ -86,7 +102,22 @@ hs_res_close(struct __res_state *s)
res_nclose(s);
}

inline static int
hs_get_h_errno(struct __res_state *s)
{
#if defined(HAVE_DECL_H_ERRNO)
#if defined(HAVE_STRUCT___RES_STATE_RES_H_ERRNO)
assert(s);
__HS_GET_H_ERRNO(s->res_h_errno)
#else
__HS_GET_H_ERRNO(h_errno)
#endif
#else
return -1;
#endif
}

#else /* USE_RES_NQUERY */

/* use non-reentrant API */

Expand Down Expand Up @@ -139,6 +170,16 @@ hs_res_close(void *s)
{
}

inline static int
hs_get_h_errno(void *s)
{
#if defined(HAVE_DECL_H_ERRNO)
__HS_GET_H_ERRNO(h_errno)
#else
return -1;
#endif
}

#endif /* USE_RES_NQUERY */

#endif /* HS_RESOLV_H */
15 changes: 15 additions & 0 deletions configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@ AC_CHECK_DECLS([res_query, res_nquery], [], [], [[
#include <resolv.h>
]])

AC_CHECK_DECLS([h_errno], [], [], [[
#include <netdb.h>
]])

dnl ----------------------------------------------------------------------------

RESOLV_SEARCH_LIBS([res_query],[res_query(0,0,0,0,0)],[resolv bind],[EXTRA_LIBS="$EXTRA_LIBS $ac_lib"],[
Expand Down Expand Up @@ -67,6 +71,17 @@ USE_RES_NQUERY=0
AC_MSG_WARN([could not figure out which C library contains res_nquery(3)])
])

AC_CHECK_MEMBERS([struct __res_state.res_h_errno],[],[],[[
#include <sys/types.h>
#ifdef HAVE_NETINET_IN_H
# include <netinet/in.h>
#endif
#ifdef HAVE_ARPA_NAMESER_H
# include <arpa/nameser.h>
#endif
#include <resolv.h>
]])

else

AC_MSG_WARN([could not determine sizeof(struct __res_state)])
Expand Down
35 changes: 29 additions & 6 deletions src/Network/DNS.hs
Original file line number Diff line number Diff line change
Expand Up @@ -98,18 +98,35 @@ import Compat
import Network.DNS.FFI
import Network.DNS.Message

-- | Exception thrown in case of errors while encoding or decoding into a 'Msg'.
-- | Exception thrown in case of errors while resolving or encoding/decoding into a 'Msg'.
--
-- @since 0.1.1.0
data DnsException = DnsEncodeException
| DnsDecodeException
deriving (Show, Typeable)
data DnsException
= DnsEncodeException
| DnsDecodeException
| DnsHostNotFound
-- ^ No such domain (authoritative)
--
-- @since 0.2.0.0
| DnsNoData
-- ^ No record for requested type
--
-- @since 0.2.0.0
| DnsNoRecovery
-- ^ Non recoverable errors, REFUSED, NOTIMP
--
-- @since 0.2.0.0
| DnsTryAgain
-- ^ No such domain (non-authoritative) or SERVERFAIL
--
-- @since 0.2.0.0
deriving (Show, Typeable)

instance Exception DnsException

-- | Send a query via @res_query(3)@ and decode its response into a 'Msg'
--
-- Throws 'DnsException' in case of encoding or decoding errors. May throw other IO exceptions in case of network errors.
-- Throws 'DnsException' in case of resolving or encoding/decoding errors. May throw other IO exceptions in case of network errors.
--
-- === Example
--
Expand Down Expand Up @@ -158,7 +175,13 @@ queryRaw (Class cls) (Name name) qtype = withCResState $ \stptr -> do
unless (errno == eOK) $
throwErrno "res_query"

fail "res_query(3) failed"
h_errno <- c_get_h_errno stptr
case h_errno of
1 -> throwIO DnsHostNotFound
2 -> throwIO DnsNoData
3 -> throwIO DnsNoRecovery
4 -> throwIO DnsTryAgain
_ -> fail "res_query(3) failed"

BS.packCStringLen (resptr, fromIntegral reslen)

Expand Down
3 changes: 3 additions & 0 deletions src/Network/DNS/FFI.hs
Original file line number Diff line number Diff line change
Expand Up @@ -81,3 +81,6 @@ foreign import capi safe "hs_resolv.h hs_res_mkquery" c_res_mkquery :: Ptr CResS

-- 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