Add c-ares support

Every socket which is based on CZNCSock will now use c-ares for its name
resolving. This is possible thanks to CSocket's Csock::GetAddrInfo() which
lets one override the DNS lookup.

This can be disabled with --disable-c-ares.

If IPv6 is enabled and Csocket didn't specify which kind of lookup (ipv4/ipv6)
it wants, we first do an ipv4 lookup. If that lookup doesn't yield any useful
result, we try again with an ipv6 lookup. If one wants to force ipv6 usage on a
domain which also resolves to an ipv4 address, he has to set an ipv6 vhost.


git-svn-id: https://znc.svn.sourceforge.net/svnroot/znc/trunk@1551 726aef4b-f618-498e-8847-2d620e286838
This commit is contained in:
psychon
2009-06-30 09:56:01 +00:00
parent 442aee2baf
commit f77e526652
5 changed files with 441 additions and 11 deletions
+1 -1
View File
@@ -1826,7 +1826,7 @@ protected:
{
return( select( nfds, readfds, writefds, exceptfds, timeout ) );
}
private:
protected:
/**
* fills a map of socks to a message for check
* map is empty if none are ready, check GetErrno() for the error, if not SUCCESS Select() failed
+180 -4
View File
@@ -7,11 +7,187 @@
*/
#include "Socket.h"
#ifdef HAVE_ARES
#include <ares.h>
#endif
CSockManager::CSockManager() : TSocketManager<CZNCSock>()
{
#ifdef HAVE_ARES
struct DNSLookup {
bool bSocketDead;
bool bLookupDone;
// When both of the above are true, this struct can be freed
// Query
CString sHost;
CSSockAddr::EAFRequire family;
// Result
int ares_status;
CSSockAddr addr;
};
static ares_channel& GetAres() {
static ares_channel m_ares;
return m_ares;
}
#endif
CZNCSock::~CZNCSock() {
#ifdef HAVE_ARES
if (m_dns_lookup) {
m_dns_lookup->bSocketDead = true;
if (m_dns_lookup->bLookupDone)
delete m_dns_lookup;
m_dns_lookup = NULL;
}
#endif
}
CSockManager::~CSockManager()
{
CSockManager::CSockManager() : TSocketManager<CZNCSock>() {
#ifdef HAVE_ARES
int i = ares_init(&GetAres());
if (i != ARES_SUCCESS) {
CUtils::PrintError("Could not initialize c-ares: " + CString(ares_strerror(i)));
exit(0);
}
DEBUG("Successfully initialized c-ares");
#endif
}
CSockManager::~CSockManager() {
#ifdef HAVE_ARES
ares_destroy(GetAres());
#endif
}
#ifdef HAVE_ARES
int CSockManager::Select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout)
{
int ret;
// We assume that nfds is already the max. number of sockets allowed by
// the OS, so we don't need to update it here.
ares_fds(GetAres(), readfds, writefds);
ares_timeout(GetAres(), timeout, timeout);
ret = ::select(nfds, readfds, writefds, exceptfds, timeout);
ares_process(GetAres(), readfds, writefds);
return ret;
}
void CZNCSock::ares_callback(void *lookup, int status, int timeout, struct hostent *h) {
struct DNSLookup *p = (struct DNSLookup *) lookup;
p->bLookupDone = true;
DEBUG("DNS lookup done for " << p->sHost);
if (p->bSocketDead) {
delete p;
return;
}
p->ares_status = status;
if (!h) {
#ifdef HAVE_IPV6
if (p->family == CSSockAddr::RAF_ANY) {
// Try an AAAA lookup
p->family = CSSockAddr::RAF_INET6;
DEBUG("Retrying with AAAA");
p->bLookupDone = false;
ares_gethostbyname(GetAres(), p->sHost.c_str(), p->family,
ares_callback, p);
return;
}
#endif
if (status == ARES_SUCCESS)
p->ares_status = ARES_ENOTIMP;
return;
}
if (h->h_addrtype == AF_INET && h->h_length == sizeof(in_addr)) {
memcpy(p->addr.GetAddr(), h->h_addr_list[0], sizeof(in_addr));
p->addr.SetIPv6(false);
}
#ifdef HAVE_IPV6
else if (h->h_addrtype == AF_INET6 && h->h_length == sizeof(in6_addr)) {
memcpy(p->addr.GetAddr6(), h->h_addr_list[0], sizeof(in6_addr));
p->addr.SetIPv6(true);
}
#endif
else
DEBUG(__PRETTY_FUNCTION__ << ": Got unknown address with length " << h->h_length);
}
int CZNCSock::GetAddrInfo(const CS_STRING &sHostname, CSSockAddr &csSockAddr) {
// If this is an ip address, no lookup is necessary
#ifdef HAVE_IPV6
if (inet_pton(AF_INET6, sHostname.c_str(), csSockAddr.GetAddr6()) > 0) {
csSockAddr.SetIPv6(true);
SetIPv6(true);
return 0;
}
#endif
if (inet_pton(AF_INET, sHostname.c_str(), csSockAddr.GetAddr()) > 0) {
csSockAddr.SetIPv6(false);
SetIPv6(false);
return 0;
}
if (!m_dns_lookup) {
DEBUG("New dns lookup for " << sHostname);
m_dns_lookup = new struct DNSLookup;
m_dns_lookup->bSocketDead = false;
m_dns_lookup->bLookupDone = false;
m_dns_lookup->sHost = sHostname;
m_dns_lookup->ares_status = 0;
m_dns_lookup->family = csSockAddr.GetAFRequire();
CSSockAddr::EAFRequire family = csSockAddr.GetAFRequire();
if (family == CSSockAddr::RAF_ANY) {
// post-1.6.0 c-ares (=current CVS versions) support
// lookups with AF_UNSPEC which means it first tries an
// AAAA lookup and then fails back to an A lookup.
// Older versions (= any version out there) just
// generate an "address family not supported" error
// message if you feed them, so we can't use this nice
// feature for now.
family = CSSockAddr::RAF_INET;
}
ares_gethostbyname(GetAres(), sHostname.c_str(), family,
ares_callback, m_dns_lookup);
}
if (m_dns_lookup->sHost != sHostname)
// This *cannot* happen
DEBUG(__PRETTY_FUNCTION__ << ": Query target for an active DNS query changed!");
if (!m_dns_lookup->bLookupDone) {
DEBUG("waiting for dns on [" << sHostname << "] to finish...");
return EAGAIN;
}
if (m_dns_lookup->ares_status != ARES_SUCCESS) {
DEBUG("Error while looking up [" << sHostname << "]: "
<< ares_strerror(m_dns_lookup->ares_status));
delete m_dns_lookup;
m_dns_lookup = NULL;
return ETIMEDOUT;
}
#ifdef HAVE_IPV6
memcpy(csSockAddr.GetAddr6(), m_dns_lookup->addr.GetAddr6(), sizeof(in6_addr));
#endif
memcpy(csSockAddr.GetAddr(), m_dns_lookup->addr.GetAddr(), sizeof(in_addr));
csSockAddr.SetIPv6(m_dns_lookup->addr.GetIPv6());
SetIPv6(csSockAddr.GetIPv6());
DEBUG("GetAddrInfo() done for " << sHostname.c_str());
delete m_dns_lookup;
m_dns_lookup = NULL;
return 0;
}
#endif
+30 -3
View File
@@ -11,11 +11,32 @@
#include "Csocket.h"
#ifdef HAVE_ARES
struct DNSLookup;
#endif
class CZNCSock : public Csock {
public:
CZNCSock(int timeout = 60) : Csock(timeout) {}
CZNCSock(const CString& sHost, u_short port, int timeout = 60) : Csock(sHost, port, timeout) {}
~CZNCSock() {}
CZNCSock(int timeout = 60) : Csock(timeout) {
#ifdef HAVE_ARES
m_dns_lookup = NULL;
#endif
}
CZNCSock(const CString& sHost, u_short port, int timeout = 60) : Csock(sHost, port, timeout) {
#ifdef HAVE_ARES
m_dns_lookup = NULL;
#endif
}
~CZNCSock();
#ifdef HAVE_ARES
static void ares_callback(void *lookup, int status, int timeout, struct hostent *h);
virtual int GetAddrInfo(const CS_STRING & sHostname, CSSockAddr & csSockAddr);
private:
struct DNSLookup *m_dns_lookup;
#endif
};
class CSockManager : public TSocketManager<CZNCSock> {
@@ -79,6 +100,12 @@ public:
}
private:
protected:
#ifdef HAVE_ARES
int Select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
private:
using TSocketManager<CZNCSock>::Select;
#endif /* HAVE_ARES */
};
#endif /* SOCKET_H */
Vendored
+218 -2
View File
@@ -612,6 +612,9 @@ LIBZNCDIR
LIBZNC
MODFLAGS
PERL
c_ares_LIBS
c_ares_CFLAGS
PKG_CONFIG
host_os
host_vendor
host_cpu
@@ -675,6 +678,7 @@ enable_openssl
enable_perl
enable_sasl
enable_optimization
enable_c_ares
with_module_prefix
with_module_data_prefix
'
@@ -686,7 +690,10 @@ CXXFLAGS
LDFLAGS
LIBS
CPPFLAGS
CCC'
CCC
PKG_CONFIG
c_ares_CFLAGS
c_ares_LIBS'
# Initialize some variables set by options.
@@ -1320,6 +1327,7 @@ Optional Features:
--enable-sasl enable sasl
--disable-optimization Disable some compiler optimizations to decrease
memory usage while compiling
--disable-c-ares disable c-ares usage
Optional Packages:
--with-PACKAGE[=ARG] use PACKAGE [ARG=yes]
@@ -1337,6 +1345,10 @@ Some influential environment variables:
LIBS libraries to pass to the linker, e.g. -l<library>
CPPFLAGS C/C++/Objective C preprocessor flags, e.g. -I<include dir> if
you have headers in a nonstandard directory <include dir>
PKG_CONFIG path to pkg-config utility
c_ares_CFLAGS
C compiler flags for c_ares, overriding pkg-config
c_ares_LIBS linker flags for c_ares, overriding pkg-config
Use these variables to override the choices made by `configure' or to help
it to find libraries and programs with nonstandard names/locations.
@@ -2634,6 +2646,13 @@ else
OPTIMIZE="yes"
fi
# Check whether --enable-c-ares was given.
if test "${enable_c_ares+set}" = set; then
enableval=$enable_c_ares; ARES="$enableval"
else
ARES="yes"
fi
if test "$DEBUG" != "no"; then
appendCXX -ggdb -D_DEBUG
@@ -2819,6 +2838,202 @@ if test "x$ac_cv_func_lstat" = x""yes; then
fi
if test "x$ARES" = "xyes"; then
if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then
if test -n "$ac_tool_prefix"; then
# Extract the first word of "${ac_tool_prefix}pkg-config", so it can be a program name with args.
set dummy ${ac_tool_prefix}pkg-config; ac_word=$2
{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
if test "${ac_cv_path_PKG_CONFIG+set}" = set; then
$as_echo_n "(cached) " >&6
else
case $PKG_CONFIG in
[\\/]* | ?:[\\/]*)
ac_cv_path_PKG_CONFIG="$PKG_CONFIG" # Let the user override the test with a path.
;;
*)
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
ac_cv_path_PKG_CONFIG="$as_dir/$ac_word$ac_exec_ext"
$as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
fi
done
done
IFS=$as_save_IFS
;;
esac
fi
PKG_CONFIG=$ac_cv_path_PKG_CONFIG
if test -n "$PKG_CONFIG"; then
{ $as_echo "$as_me:$LINENO: result: $PKG_CONFIG" >&5
$as_echo "$PKG_CONFIG" >&6; }
else
{ $as_echo "$as_me:$LINENO: result: no" >&5
$as_echo "no" >&6; }
fi
fi
if test -z "$ac_cv_path_PKG_CONFIG"; then
ac_pt_PKG_CONFIG=$PKG_CONFIG
# Extract the first word of "pkg-config", so it can be a program name with args.
set dummy pkg-config; ac_word=$2
{ $as_echo "$as_me:$LINENO: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
if test "${ac_cv_path_ac_pt_PKG_CONFIG+set}" = set; then
$as_echo_n "(cached) " >&6
else
case $ac_pt_PKG_CONFIG in
[\\/]* | ?:[\\/]*)
ac_cv_path_ac_pt_PKG_CONFIG="$ac_pt_PKG_CONFIG" # Let the user override the test with a path.
;;
*)
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
ac_cv_path_ac_pt_PKG_CONFIG="$as_dir/$ac_word$ac_exec_ext"
$as_echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
fi
done
done
IFS=$as_save_IFS
;;
esac
fi
ac_pt_PKG_CONFIG=$ac_cv_path_ac_pt_PKG_CONFIG
if test -n "$ac_pt_PKG_CONFIG"; then
{ $as_echo "$as_me:$LINENO: result: $ac_pt_PKG_CONFIG" >&5
$as_echo "$ac_pt_PKG_CONFIG" >&6; }
else
{ $as_echo "$as_me:$LINENO: result: no" >&5
$as_echo "no" >&6; }
fi
if test "x$ac_pt_PKG_CONFIG" = x; then
PKG_CONFIG=""
else
case $cross_compiling:$ac_tool_warned in
yes:)
{ $as_echo "$as_me:$LINENO: WARNING: using cross tools not prefixed with host triplet" >&5
$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
ac_tool_warned=yes ;;
esac
PKG_CONFIG=$ac_pt_PKG_CONFIG
fi
else
PKG_CONFIG="$ac_cv_path_PKG_CONFIG"
fi
fi
if test -n "$PKG_CONFIG"; then
_pkg_min_version=0.9.0
{ $as_echo "$as_me:$LINENO: checking pkg-config is at least version $_pkg_min_version" >&5
$as_echo_n "checking pkg-config is at least version $_pkg_min_version... " >&6; }
if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then
{ $as_echo "$as_me:$LINENO: result: yes" >&5
$as_echo "yes" >&6; }
else
{ $as_echo "$as_me:$LINENO: result: no" >&5
$as_echo "no" >&6; }
PKG_CONFIG=""
fi
fi
pkg_failed=no
{ $as_echo "$as_me:$LINENO: checking for c_ares" >&5
$as_echo_n "checking for c_ares... " >&6; }
if test -n "$PKG_CONFIG"; then
if test -n "$c_ares_CFLAGS"; then
pkg_cv_c_ares_CFLAGS="$c_ares_CFLAGS"
else
if test -n "$PKG_CONFIG" && \
{ ($as_echo "$as_me:$LINENO: \$PKG_CONFIG --exists --print-errors \"libcares\"") >&5
($PKG_CONFIG --exists --print-errors "libcares") 2>&5
ac_status=$?
$as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
(exit $ac_status); }; then
pkg_cv_c_ares_CFLAGS=`$PKG_CONFIG --cflags "libcares" 2>/dev/null`
else
pkg_failed=yes
fi
fi
else
pkg_failed=untried
fi
if test -n "$PKG_CONFIG"; then
if test -n "$c_ares_LIBS"; then
pkg_cv_c_ares_LIBS="$c_ares_LIBS"
else
if test -n "$PKG_CONFIG" && \
{ ($as_echo "$as_me:$LINENO: \$PKG_CONFIG --exists --print-errors \"libcares\"") >&5
($PKG_CONFIG --exists --print-errors "libcares") 2>&5
ac_status=$?
$as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
(exit $ac_status); }; then
pkg_cv_c_ares_LIBS=`$PKG_CONFIG --libs "libcares" 2>/dev/null`
else
pkg_failed=yes
fi
fi
else
pkg_failed=untried
fi
if test $pkg_failed = yes; then
if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
_pkg_short_errors_supported=yes
else
_pkg_short_errors_supported=no
fi
if test $_pkg_short_errors_supported = yes; then
c_ares_PKG_ERRORS=`$PKG_CONFIG --short-errors --errors-to-stdout --print-errors "libcares"`
else
c_ares_PKG_ERRORS=`$PKG_CONFIG --errors-to-stdout --print-errors "libcares"`
fi
# Put the nasty error message in config.log where it belongs
echo "$c_ares_PKG_ERRORS" >&5
{ $as_echo "$as_me:$LINENO: result: no" >&5
$as_echo "no" >&6; }
{ { $as_echo "$as_me:$LINENO: error: c-ares not found. Try --disable-c-ares." >&5
$as_echo "$as_me: error: c-ares not found. Try --disable-c-ares." >&2;}
{ (exit 1); exit 1; }; }
elif test $pkg_failed = untried; then
{ { $as_echo "$as_me:$LINENO: error: c-ares not found. Try --disable-c-ares." >&5
$as_echo "$as_me: error: c-ares not found. Try --disable-c-ares." >&2;}
{ (exit 1); exit 1; }; }
else
c_ares_CFLAGS=$pkg_cv_c_ares_CFLAGS
c_ares_LIBS=$pkg_cv_c_ares_LIBS
{ $as_echo "$as_me:$LINENO: result: yes" >&5
$as_echo "yes" >&6; }
:
fi
appendCXX $c_ares_CFLAGS -DHAVE_ARES
appendLib $c_ares_LIBS
fi
if test -z "$NOSSL"; then
if test -n "$OPENSSL"; then
appendLib -L${OPENSSL}/lib
@@ -4584,7 +4799,8 @@ if test x"$NOSSL" = "x1" ; then
else
echo "openssl: yes"
fi
echo modules: $MODULES
echo "c-ares: $ARES"
echo "modules: $MODULES"
if test x"$PERL" = "x" ; then
echo "perl: no"
else
+12 -1
View File
@@ -90,6 +90,10 @@ AC_ARG_ENABLE( [optimization],
decrease memory usage while compiling]),
[OPTIMIZE="$enableval"],
[OPTIMIZE="yes"])
AC_ARG_ENABLE( [c-ares],
AS_HELP_STRING([--disable-c-ares], [disable c-ares usage]),
[ARES="$enableval"],
[ARES="yes"])
if test "$DEBUG" != "no"; then
appendCXX -ggdb -D_DEBUG
@@ -112,6 +116,12 @@ fi
AC_CHECK_LIB( gnugetopt, getopt_long,)
AC_CHECK_FUNC([lstat], [appendCXX -DHAVE_LSTAT])
if test "x$ARES" = "xyes"; then
PKG_CHECK_MODULES([c_ares], [libcares],, AC_MSG_ERROR([c-ares not found. Try --disable-c-ares.]))
appendCXX $c_ares_CFLAGS -DHAVE_ARES
appendLib $c_ares_LIBS
fi
if test -z "$NOSSL"; then
if test -n "$OPENSSL"; then
appendLib -L${OPENSSL}/lib
@@ -254,7 +264,8 @@ if test x"$NOSSL" = "x1" ; then
else
echo "openssl: yes"
fi
echo modules: $MODULES
echo "c-ares: $ARES"
echo "modules: $MODULES"
if test x"$PERL" = "x" ; then
echo "perl: no"
else