From: Michael Poole Date: Thu, 22 Jul 2004 00:06:23 +0000 (+0000) Subject: Import new (much simpler) resolver code from Hybrid. X-Git-Url: http://git.pk910.de/?p=ircu2.10.12-pk.git;a=commitdiff_plain;h=a43a98c096947864d5c9e4401fe6e15aa4de61ae Import new (much simpler) resolver code from Hybrid. git-svn-id: file:///home/klmitch/undernet-ircu/undernet-ircu-svn/ircu2/trunk@1087 c9e4aea6-c8fd-4c43-8297-357d70d61c8c --- diff --git a/ChangeLog b/ChangeLog index 9a24f95..5da4b42 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,31 @@ +2004-07-21 Michael Poole + + New DNS resolver code, courtesy of Dianora and the rest of the + Hybrid team. (Bugs are of course my fault.) + + * configure.in, Makefile.in, ircd/Makefile.in: Remove adns and + libresolv from the build process. Update dependencies. + + * configure: Regenerate. + + * include/client.h: Change connection's DNS reply type. + + * include/ircd_features.h, ircd/ircd_features.c: New HIS_STATS_a. + + * include/numeric.h, ircd/s_err.c, ircd/s_stats.c: Add new + RPL_STATSALINE and /stats a to list DNS servers. + + * include/ircd_addrinfo.h, include/ircd_reslib.h, include/res.h, + ircd/ircd_getaddrinfo.c, ircd/ircd_getnameinfo.c, ircd/ircd_res.c, + ircd/ircd_reslib.c: New resolver files. + + * ircd/ircd_auth.c, ircd/s_auth.c, ircd/s_bsd.c, ircd/s_conf.c: + Update to new resolver callback interface and to only deal with + one IP and one name per DNS reply. + + * ircd/parse.c: Remove /DNS command, since new resolver does not + track those statistics. + 2004-07-20 Michael Poole * doc/readme.features: Change references to O:, Y:, etc lines into diff --git a/Makefile.in b/Makefile.in index 854f9f1..e2ba6b0 100644 --- a/Makefile.in +++ b/Makefile.in @@ -25,12 +25,11 @@ VPATH = @srcdir@ SHELL = @SHPROG@ RM = @RMPROG@ AWK = @AWK@ -adns_subdir = @ADNS_SUBDIR@ @SET_MAKE@ #### End of system configuration section. #### -SUBDIRS = doc ${adns_subdir} ircd -IRCD_MAKEFILES = Makefile doc/Makefile adns/Makefile ircd/Makefile +SUBDIRS = doc ircd +IRCD_MAKEFILES = Makefile doc/Makefile ircd/Makefile all: build diff --git a/configure b/configure index 8149da9..a47b969 100755 --- a/configure +++ b/configure @@ -310,8 +310,7 @@ ac_includes_default="\ # include #endif" -ac_subdirs_all="$ac_subdirs_all adns" -ac_subst_vars='SHELL PATH_SEPARATOR PACKAGE_NAME PACKAGE_TARNAME PACKAGE_VERSION PACKAGE_STRING PACKAGE_BUGREPORT exec_prefix prefix program_transform_name bindir sbindir libexecdir datadir sysconfdir sharedstatedir localstatedir libdir includedir oldincludedir infodir mandir build_alias host_alias target_alias DEFS ECHO_C ECHO_N ECHO_T LIBS build build_cpu build_vendor build_os host host_cpu host_vendor host_os CC CFLAGS LDFLAGS CPPFLAGS ac_ct_CC EXEEXT OBJEXT CPP EGREP AWK SET_MAKE INSTALL_PROGRAM INSTALL_SCRIPT INSTALL_DATA LN_S RMPROG SHPROG OSDEP_C ENGINE_C subdirs RES_C ADNS_SUBDIR INSTALL_RULE SYMLINK IRCDMODE IRCDOWN IRCDGRP DPATH LIBOBJS LTLIBOBJS' +ac_subst_vars='SHELL PATH_SEPARATOR PACKAGE_NAME PACKAGE_TARNAME PACKAGE_VERSION PACKAGE_STRING PACKAGE_BUGREPORT exec_prefix prefix program_transform_name bindir sbindir libexecdir datadir sysconfdir sharedstatedir localstatedir libdir includedir oldincludedir infodir mandir build_alias host_alias target_alias DEFS ECHO_C ECHO_N ECHO_T LIBS build build_cpu build_vendor build_os host host_cpu host_vendor host_os CC CFLAGS LDFLAGS CPPFLAGS ac_ct_CC EXEEXT OBJEXT CPP EGREP AWK SET_MAKE INSTALL_PROGRAM INSTALL_SCRIPT INSTALL_DATA LN_S RMPROG SHPROG OSDEP_C ENGINE_C INSTALL_RULE SYMLINK IRCDMODE IRCDOWN IRCDGRP DPATH LIBOBJS LTLIBOBJS' ac_subst_files='' # Initialize some variables set by options. @@ -858,7 +857,6 @@ Optional Features: --disable-devpoll Disable the /dev/poll-based engine --disable-kqueue Disable the kqueue-based engine --disable-epoll Disable the epoll-based engine - --disable-adns Disable adns resolver Optional Packages: --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] @@ -8354,173 +8352,6 @@ _ACEOF fi -echo "$as_me:$LINENO: checking whether to enable the adns resolver engine" >&5 -echo $ECHO_N "checking whether to enable the adns resolver engine... $ECHO_C" >&6 -# Check whether --enable-adns or --disable-adns was given. -if test "${enable_adns+set}" = set; then - enableval="$enable_adns" - unet_cv_enable_adns=$enable_adns -else - if test "${unet_cv_enable_adns+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - unet_cv_enable_adns=yes -fi - -fi; - -echo "$as_me:$LINENO: result: $unet_cv_enable_adns" >&5 -echo "${ECHO_T}$unet_cv_enable_adns" >&6 - -if test x"$unet_cv_enable_adns" != xno; then - -cat >>confdefs.h <<\_ACEOF -#define USE_ADNS -_ACEOF - - - -subdirs="$subdirs adns" - - RES_C="res_adns.c" - LIBS="../adns/src/libadns.a $LIBS" - ADNS_SUBDIR="adns" -else - RES_C="res_libresolv.c" - ADNS_SUBDIR="" - - echo "$as_me:$LINENO: checking for library containing res_mkquery" >&5 -echo $ECHO_N "checking for library containing res_mkquery... $ECHO_C" >&6 -if test "${ac_cv_search_res_mkquery+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - ac_func_search_save_LIBS=$LIBS -ac_cv_search_res_mkquery=no -cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ - -/* Override any gcc2 internal prototype to avoid an error. */ -#ifdef __cplusplus -extern "C" -#endif -/* We use char because int might match the return type of a gcc2 - builtin and then its argument prototype would still apply. */ -char res_mkquery (); -int -main () -{ -res_mkquery (); - ; - return 0; -} -_ACEOF -rm -f conftest.$ac_objext conftest$ac_exeext -if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 - (eval $ac_link) 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; } && - { ac_try='test -s conftest$ac_exeext' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; }; then - ac_cv_search_res_mkquery="none required" -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - -fi -rm -f conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext -if test "$ac_cv_search_res_mkquery" = no; then - for ac_lib in resolv; do - LIBS="-l$ac_lib $ac_func_search_save_LIBS" - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ - -/* Override any gcc2 internal prototype to avoid an error. */ -#ifdef __cplusplus -extern "C" -#endif -/* We use char because int might match the return type of a gcc2 - builtin and then its argument prototype would still apply. */ -char res_mkquery (); -int -main () -{ -res_mkquery (); - ; - return 0; -} -_ACEOF -rm -f conftest.$ac_objext conftest$ac_exeext -if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 - (eval $ac_link) 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && - { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; } && - { ac_try='test -s conftest$ac_exeext' - { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 - (eval $ac_try) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; }; then - ac_cv_search_res_mkquery="-l$ac_lib" -break -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - -fi -rm -f conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext - done -fi -LIBS=$ac_func_search_save_LIBS -fi -echo "$as_me:$LINENO: result: $ac_cv_search_res_mkquery" >&5 -echo "${ECHO_T}$ac_cv_search_res_mkquery" >&6 -if test "$ac_cv_search_res_mkquery" != no; then - test "$ac_cv_search_res_mkquery" = "none required" || LIBS="$ac_cv_search_res_mkquery $LIBS" - -else - { { echo "$as_me:$LINENO: error: Unable to find library containing res_mkquery()" >&5 -echo "$as_me: error: Unable to find library containing res_mkquery()" >&2;} - { (exit 1); exit 1; }; } -fi - -fi - - - echo "$as_me:$LINENO: checking what name to give the symlink" >&5 echo $ECHO_N "checking what name to give the symlink... $ECHO_C" >&6 @@ -9572,9 +9403,6 @@ s,@RMPROG@,$RMPROG,;t t s,@SHPROG@,$SHPROG,;t t s,@OSDEP_C@,$OSDEP_C,;t t s,@ENGINE_C@,$ENGINE_C,;t t -s,@subdirs@,$subdirs,;t t -s,@RES_C@,$RES_C,;t t -s,@ADNS_SUBDIR@,$ADNS_SUBDIR,;t t s,@INSTALL_RULE@,$INSTALL_RULE,;t t s,@SYMLINK@,$SYMLINK,;t t s,@IRCDMODE@,$IRCDMODE,;t t @@ -10193,183 +10021,6 @@ if test "$no_create" != yes; then $ac_cs_success || { (exit 1); exit 1; } fi -# -# CONFIG_SUBDIRS section. -# -if test "$no_recursion" != yes; then - - # Remove --cache-file and --srcdir arguments so they do not pile up. - ac_sub_configure_args= - ac_prev= - for ac_arg in $ac_configure_args; do - if test -n "$ac_prev"; then - ac_prev= - continue - fi - case $ac_arg in - -cache-file | --cache-file | --cache-fil | --cache-fi \ - | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) - ac_prev=cache_file ;; - -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ - | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* \ - | --c=*) - ;; - --config-cache | -C) - ;; - -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) - ac_prev=srcdir ;; - -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) - ;; - -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) - ac_prev=prefix ;; - -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) - ;; - *) ac_sub_configure_args="$ac_sub_configure_args $ac_arg" ;; - esac - done - - # Always prepend --prefix to ensure using the same prefix - # in subdir configurations. - ac_sub_configure_args="--prefix=$prefix $ac_sub_configure_args" - - ac_popdir=`pwd` - for ac_dir in : $subdirs; do test "x$ac_dir" = x: && continue - - # Do not complain, so a configure script can configure whichever - # parts of a large source tree are present. - test -d $srcdir/$ac_dir || continue - - { echo "$as_me:$LINENO: configuring in $ac_dir" >&5 -echo "$as_me: configuring in $ac_dir" >&6;} - { if $as_mkdir_p; then - mkdir -p "$ac_dir" - else - as_dir="$ac_dir" - as_dirs= - while test ! -d "$as_dir"; do - as_dirs="$as_dir $as_dirs" - as_dir=`(dirname "$as_dir") 2>/dev/null || -$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ - X"$as_dir" : 'X\(//\)[^/]' \| \ - X"$as_dir" : 'X\(//\)$' \| \ - X"$as_dir" : 'X\(/\)' \| \ - . : '\(.\)' 2>/dev/null || -echo X"$as_dir" | - sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } - /^X\(\/\/\)[^/].*/{ s//\1/; q; } - /^X\(\/\/\)$/{ s//\1/; q; } - /^X\(\/\).*/{ s//\1/; q; } - s/.*/./; q'` - done - test ! -n "$as_dirs" || mkdir $as_dirs - fi || { { echo "$as_me:$LINENO: error: cannot create directory \"$ac_dir\"" >&5 -echo "$as_me: error: cannot create directory \"$ac_dir\"" >&2;} - { (exit 1); exit 1; }; }; } - - ac_builddir=. - -if test "$ac_dir" != .; then - ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'` - # A "../" for each directory in $ac_dir_suffix. - ac_top_builddir=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,../,g'` -else - ac_dir_suffix= ac_top_builddir= -fi - -case $srcdir in - .) # No --srcdir option. We are building in place. - ac_srcdir=. - if test -z "$ac_top_builddir"; then - ac_top_srcdir=. - else - ac_top_srcdir=`echo $ac_top_builddir | sed 's,/$,,'` - fi ;; - [\\/]* | ?:[\\/]* ) # Absolute path. - ac_srcdir=$srcdir$ac_dir_suffix; - ac_top_srcdir=$srcdir ;; - *) # Relative path. - ac_srcdir=$ac_top_builddir$srcdir$ac_dir_suffix - ac_top_srcdir=$ac_top_builddir$srcdir ;; -esac - -# Do not use `cd foo && pwd` to compute absolute paths, because -# the directories may not exist. -case `pwd` in -.) ac_abs_builddir="$ac_dir";; -*) - case "$ac_dir" in - .) ac_abs_builddir=`pwd`;; - [\\/]* | ?:[\\/]* ) ac_abs_builddir="$ac_dir";; - *) ac_abs_builddir=`pwd`/"$ac_dir";; - esac;; -esac -case $ac_abs_builddir in -.) ac_abs_top_builddir=${ac_top_builddir}.;; -*) - case ${ac_top_builddir}. in - .) ac_abs_top_builddir=$ac_abs_builddir;; - [\\/]* | ?:[\\/]* ) ac_abs_top_builddir=${ac_top_builddir}.;; - *) ac_abs_top_builddir=$ac_abs_builddir/${ac_top_builddir}.;; - esac;; -esac -case $ac_abs_builddir in -.) ac_abs_srcdir=$ac_srcdir;; -*) - case $ac_srcdir in - .) ac_abs_srcdir=$ac_abs_builddir;; - [\\/]* | ?:[\\/]* ) ac_abs_srcdir=$ac_srcdir;; - *) ac_abs_srcdir=$ac_abs_builddir/$ac_srcdir;; - esac;; -esac -case $ac_abs_builddir in -.) ac_abs_top_srcdir=$ac_top_srcdir;; -*) - case $ac_top_srcdir in - .) ac_abs_top_srcdir=$ac_abs_builddir;; - [\\/]* | ?:[\\/]* ) ac_abs_top_srcdir=$ac_top_srcdir;; - *) ac_abs_top_srcdir=$ac_abs_builddir/$ac_top_srcdir;; - esac;; -esac - - - cd $ac_dir - - # Check for guested configure; otherwise get Cygnus style configure. - if test -f $ac_srcdir/configure.gnu; then - ac_sub_configure="$SHELL '$ac_srcdir/configure.gnu'" - elif test -f $ac_srcdir/configure; then - ac_sub_configure="$SHELL '$ac_srcdir/configure'" - elif test -f $ac_srcdir/configure.in; then - ac_sub_configure=$ac_configure - else - { echo "$as_me:$LINENO: WARNING: no configuration information is in $ac_dir" >&5 -echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2;} - ac_sub_configure= - fi - - # The recursion is here. - if test -n "$ac_sub_configure"; then - # Make the cache file name correct relative to the subdirectory. - case $cache_file in - [\\/]* | ?:[\\/]* ) ac_sub_cache_file=$cache_file ;; - *) # Relative path. - ac_sub_cache_file=$ac_top_builddir$cache_file ;; - esac - - { echo "$as_me:$LINENO: running $ac_sub_configure $ac_sub_configure_args --cache-file=$ac_sub_cache_file --srcdir=$ac_srcdir" >&5 -echo "$as_me: running $ac_sub_configure $ac_sub_configure_args --cache-file=$ac_sub_cache_file --srcdir=$ac_srcdir" >&6;} - # The eval makes quoting arguments work. - eval $ac_sub_configure $ac_sub_configure_args \ - --cache-file=$ac_sub_cache_file --srcdir=$ac_srcdir || - { { echo "$as_me:$LINENO: error: $ac_sub_configure failed for $ac_dir" >&5 -echo "$as_me: error: $ac_sub_configure failed for $ac_dir" >&2;} - { (exit 1); exit 1; }; } - fi - - cd "$ac_popdir" - done -fi - ac_config_commands="$ac_config_commands default-1" diff --git a/configure.in b/configure.in index 875eca3..1c40b32 100644 --- a/configure.in +++ b/configure.in @@ -424,34 +424,6 @@ if test "$unet_cv_c___va_copy" = "yes" ; then AC_DEFINE(HAVE___VA_COPY, 1, [Define if we have __va_copy]) fi -dnl --disable-adns check... -AC_MSG_CHECKING([whether to enable the adns resolver engine]) -AC_ARG_ENABLE([adns], -[ --disable-adns Disable adns resolver], -[unet_cv_enable_adns=$enable_adns], -[AC_CACHE_VAL(unet_cv_enable_adns, -[unet_cv_enable_adns=yes])]) - -AC_MSG_RESULT([$unet_cv_enable_adns]) - -if test x"$unet_cv_enable_adns" != xno; then - AC_DEFINE([USE_ADNS], , [Define to enable the adns resolver]) - AC_CONFIG_SUBDIRS([adns]) - RES_C="res_adns.c" - LIBS="../adns/src/libadns.a $LIBS" - ADNS_SUBDIR="adns" -else - RES_C="res_libresolv.c" - ADNS_SUBDIR="" - - dnl Look for res_mkquery. Done after AC_LIBRARY_NET in case res_mkquery - dnl is in one of those libraries somewhere. - AC_SEARCH_LIBS(res_mkquery, resolv, , - [AC_MSG_ERROR([Unable to find library containing res_mkquery()])]) -fi -AC_SUBST(RES_C) -AC_SUBST(ADNS_SUBDIR) - dnl --with-symlink lets us set the name of the symlink; defaults to "ircd" AC_MSG_CHECKING([what name to give the symlink]) AC_ARG_WITH([symlink], diff --git a/include/client.h b/include/client.h index 478f9bc..42049d8 100644 --- a/include/client.h +++ b/include/client.h @@ -193,7 +193,7 @@ struct Connection struct SLink* con_confs; /* Configuration record associated */ HandlerType con_handler; /* message index into command table for parsing */ - struct hostent* con_dns_reply; /* DNS reply used during client + struct DNSReply* con_dns_reply; /* DNS reply used during client registration */ struct ListingArgs* con_listing; unsigned int con_max_sendq; /* cached max send queue for client */ diff --git a/include/ircd_addrinfo.h b/include/ircd_addrinfo.h new file mode 100644 index 0000000..e6777d5 --- /dev/null +++ b/include/ircd_addrinfo.h @@ -0,0 +1,10 @@ +#include +#include +#include +#include + +int irc_getaddrinfo(const char *hostname, const char *servname, + const struct addrinfo *hints, struct addrinfo **res); +int irc_getnameinfo(const struct sockaddr *sa, socklen_t salen, char *host, + size_t hostlen, char *serv, size_t servlen, int flags); +void irc_freeaddrinfo(struct addrinfo *ai); diff --git a/include/ircd_features.h b/include/ircd_features.h index 50ac4c4..05ea365 100644 --- a/include/ircd_features.h +++ b/include/ircd_features.h @@ -105,6 +105,7 @@ enum Feature { FEAT_HIS_TRACE, FEAT_HIS_STATS_l, FEAT_HIS_STATS_L, + FEAT_HIS_STATS_a, FEAT_HIS_STATS_c, FEAT_HIS_STATS_g, FEAT_HIS_STATS_h, diff --git a/include/ircd_reslib.h b/include/ircd_reslib.h new file mode 100644 index 0000000..9dd6e25 --- /dev/null +++ b/include/ircd_reslib.h @@ -0,0 +1,68 @@ +/* + * include/ircd_reslib.h + * (C)opyright 1992 Darren Reed. + * + * $Id$ + */ +#ifndef INCLUDED_ircdreslib_h +#define INCLUDED_ircdreslib_h + +#include + +/* + * Inline versions of get/put short/long. Pointer is advanced. + */ +#define IRC_NS_GET16(s, cp) { \ + const unsigned char *t_cp = (const unsigned char *)(cp); \ + (s) = ((u_int16_t)t_cp[0] << 8) \ + | ((u_int16_t)t_cp[1]) \ + ; \ + (cp) += NS_INT16SZ; \ +} + +#define IRC_NS_GET32(l, cp) { \ + const unsigned char *t_cp = (const unsigned char *)(cp); \ + (l) = ((u_int32_t)t_cp[0] << 24) \ + | ((u_int32_t)t_cp[1] << 16) \ + | ((u_int32_t)t_cp[2] << 8) \ + | ((u_int32_t)t_cp[3]) \ + ; \ + (cp) += NS_INT32SZ; \ +} + +#define IRC_NS_PUT16(s, cp) { \ + u_int16_t t_s = (u_int16_t)(s); \ + unsigned char *t_cp = (unsigned char *)(cp); \ + *t_cp++ = t_s >> 8; \ + *t_cp = t_s; \ + (cp) += NS_INT16SZ; \ +} + +#define IRC_NS_PUT32(l, cp) { \ + u_int32_t t_l = (u_int32_t)(l); \ + unsigned char *t_cp = (unsigned char *)(cp); \ + *t_cp++ = t_l >> 24; \ + *t_cp++ = t_l >> 16; \ + *t_cp++ = t_l >> 8; \ + *t_cp = t_l; \ + (cp) += NS_INT32SZ; \ +} + +#define IRCD_MAXNS 8 + +int irc_res_init(void); +int irc_dn_expand(const unsigned char *msg, const unsigned char *eom, const unsigned char *src, char *dst, int dstsiz); +int irc_ns_name_uncompress(const unsigned char *msg, const unsigned char *eom, const unsigned char *src, char *dst, size_t dstsiz); +int irc_ns_name_unpack(const unsigned char *msg, const unsigned char *eom, const unsigned char *src, unsigned char *dst, size_t dstsiz); +int irc_ns_name_ntop(const char *src, char *dst, size_t dstsiz); +int irc_dn_comp(const char *src, unsigned char *dst, int dstsiz, unsigned char **dnptrs, unsigned char **lastdnptr); +int irc_dn_skipname(const unsigned char *ptr, const unsigned char *eom); +int irc_ns_name_skip(const unsigned char **ptrptr, const unsigned char *eom); +unsigned int irc_ns_get16(const unsigned char *src); +unsigned long irc_ns_get32(const unsigned char *src); +void irc_ns_put16(unsigned int src, unsigned char *dst); +void irc_ns_put32(unsigned long src, unsigned char *dst); +int irc_ns_name_pton(const char *src, unsigned char *dst, size_t dstsiz); +int irc_ns_name_pack(const unsigned char *src, unsigned char *dst, int dstsiz, const unsigned char **dnptrs, const unsigned char **lastdnptr); +int irc_res_mkquery(const char *dname, int class, int type, unsigned char *buf, int buflen); +#endif /* INCLUDED_res_h */ diff --git a/include/numeric.h b/include/numeric.h index c2f8d44..0dfb1a2 100644 --- a/include/numeric.h +++ b/include/numeric.h @@ -112,6 +112,7 @@ extern const struct Numeric* get_error_numeric(int err); RPL_STATSNLINE 226 unreal RPL_STATSGLINE 227 Dalnet RPL_STATSVLINE 227 unreal */ +#define RPL_STATSALINE 226 /* Hybrid, Undernet */ #define RPL_STATSQLINE 228 /* Undernet extension */ #define RPL_SERVICEINFO 231 /* unused */ diff --git a/include/res.h b/include/res.h index 6a10c08..bfd9b2e 100644 --- a/include/res.h +++ b/include/res.h @@ -1,45 +1,102 @@ /* - * irc2.7.2/ircd/res.h (C)opyright 1992 Darren Reed. + * include/ircd_res.h for referencing functions in ircd/ircd_res.c * * $Id$ */ + #ifndef INCLUDED_res_h #define INCLUDED_res_h -#ifndef INCLUDED_sys_types_h -#include /* time_t */ -#define INCLUDED_sys_types_h -#endif +#include "listener.h" + +struct StatDesc; -struct Client; -struct hostent; +/* Here we define some values lifted from nameser.h */ +#define NS_NOTIFY_OP 4 +#define NS_INT16SZ 2 +#define NS_IN6ADDRSZ 16 +#define NS_INADDRSZ 4 +#define NS_INT32SZ 4 +#define NS_CMPRSFLGS 0xc0 +#define NS_MAXCDNAME 255 +#define QUERY 0 +#define IQUERY 1 +#define NO_ERRORS 0 +#define SERVFAIL 2 +#define T_A 1 +#define T_AAAA 28 +#define T_PTR 12 +#define T_CNAME 5 +#define T_NULL 10 +#define C_IN 1 +#define QFIXEDSZ 4 +#define RRFIXEDSZ 10 +#define HFIXEDSZ 12 -struct DNSQuery { - void* vptr; /* pointer used by callback to identify request */ - void (*callback)(void* vptr, struct hostent* he); /* callback to call */ +struct irc_ssaddr { + struct sockaddr_storage ss; + size_t ss_len; }; -extern int ResolverFileDescriptor; /* GLOBAL - file descriptor (s_bsd.c) */ +struct DNSReply +{ + char *h_name; + int h_addrtype; + struct irc_ssaddr addr; +}; -extern void get_res(void); -extern void gethost_byname(const char* name, const struct DNSQuery* req); -extern void gethost_byaddr(const char* name, const struct DNSQuery* req); -extern int init_resolver(void); -extern void restart_resolver(void); -extern time_t timeout_resolver(time_t now); +struct DNSQuery +{ + void *vptr; /* pointer used by callback to identify request */ + void (*callback)(void* vptr, struct DNSReply *reply); /* callback to call */ +}; -/* - * delete_resolver_queries - delete all outstanding queries for the - * pointer arg, DO NOT call this from a resolver callback function the - * resolver will delete the query itself for the affected client. - */ -extern void delete_resolver_queries(const void* vptr); -extern size_t cres_mem(struct Client* cptr); -extern int m_dns(struct Client* cptr, struct Client* sptr, - int parc, char* parv[]); -extern int resolver_read(void); -extern void resolver_read_multiple(int count); -extern void flush_resolver_cache(void); +typedef struct +{ + unsigned id :16; /* query identification number */ +#ifdef WORDS_BIGENDIAN + /* fields in third byte */ + unsigned qr: 1; /* response flag */ + unsigned opcode: 4; /* purpose of message */ + unsigned aa: 1; /* authoritive answer */ + unsigned tc: 1; /* truncated message */ + unsigned rd: 1; /* recursion desired */ + /* fields in fourth byte */ + unsigned ra: 1; /* recursion available */ + unsigned unused :1; /* unused bits (MBZ as of 4.9.3a3) */ + unsigned ad: 1; /* authentic data from named */ + unsigned cd: 1; /* checking disabled by resolver */ + unsigned rcode :4; /* response code */ +#else + /* fields in third byte */ + unsigned rd :1; /* recursion desired */ + unsigned tc :1; /* truncated message */ + unsigned aa :1; /* authoritive answer */ + unsigned opcode :4; /* purpose of message */ + unsigned qr :1; /* response flag */ + /* fields in fourth byte */ + unsigned rcode :4; /* response code */ + unsigned cd: 1; /* checking disabled by resolver */ + unsigned ad: 1; /* authentic data from named */ + unsigned unused :1; /* unused bits (MBZ as of 4.9.3a3) */ + unsigned ra :1; /* recursion available */ +#endif + /* remaining bytes */ + unsigned qdcount :16; /* number of question entries */ + unsigned ancount :16; /* number of answer entries */ + unsigned nscount :16; /* number of authority entries */ + unsigned arcount :16; /* number of resource entries */ +} HEADER; -#endif /* INCLUDED_res_h */ +extern int init_resolver(void); +extern void restart_resolver(void); +extern void add_local_domain(char *hname, size_t size); +extern size_t cres_mem(struct Client* cptr); +extern void delete_resolver_queries(const void *vptr); +extern void report_dns_servers(struct Client *source_p, struct StatDesc *sd, int stat, char *param); +extern void gethost_byname_type(const char *name, const struct DNSQuery *query, int type); +extern void gethost_byname(const char *name, const struct DNSQuery *query); +extern void gethost_byaddr(const struct irc_ssaddr *addr, const struct DNSQuery *query); +extern void gethost_byinaddr(const struct in_addr *addr, const struct DNSQuery *query); +#endif diff --git a/ircd/Makefile.in b/ircd/Makefile.in index 9842427..cb6f563 100644 --- a/ircd/Makefile.in +++ b/ircd/Makefile.in @@ -43,7 +43,6 @@ YACC = bison -y GREP = grep OSDEP_C = @OSDEP_C@ ENGINE_C = @ENGINE_C@ -RES_C = @RES_C@ @SET_MAKE@ BINDIR = @bindir@ @@ -79,10 +78,6 @@ ENGINE_SRC = \ engine_kqueue.c \ engine_select.c -RES_SRC = \ - res_adns.c \ - res_libresolv.c - CRYPTO_SRC = \ ircd_md5.c \ ircd_crypt_plain.c \ @@ -111,9 +106,13 @@ IRCD_SRC = \ ircd_crypt.c \ ircd_events.c \ ircd_features.c \ + ircd_getaddrinfo.c \ + ircd_getnameinfo.c \ ircd_log.c \ ircd_relay.c \ ircd_reply.c \ + ircd_res.c \ + ircd_reslib.c \ ircd_signal.c \ ircd_snprintf.c \ ircd_string.c \ @@ -221,7 +220,7 @@ IRCD_SRC = \ whowas.c \ y.tab.c -SRC = ${IRCD_SRC} ${OSDEP_C} ${ENGINE_C} ${RES_C} ${CRYPTO_SRC} +SRC = ${IRCD_SRC} ${OSDEP_C} ${ENGINE_C} ${CRYPTO_SRC} OBJS = ${SRC:%.c=%.o} @@ -445,11 +444,11 @@ ircd.o: ircd.c ../config.h ../include/ircd.h ../include/struct.h \ ../include/ircd_crypt.h ../include/jupe.h ../include/list.h \ ../include/match.h ../include/motd.h ../include/msg.h \ ../include/numeric.h ../include/numnicks.h ../include/opercmds.h \ - ../include/parse.h ../include/res.h ../include/s_auth.h \ - ../include/s_bsd.h ../include/s_conf.h ../include/s_debug.h \ - ../include/s_misc.h ../include/s_stats.h ../include/send.h \ - ../include/sys.h ../include/uping.h ../include/userload.h \ - ../include/version.h ../include/whowas.h + ../include/parse.h ../include/res.h ../include/listener.h \ + ../include/s_auth.h ../include/s_bsd.h ../include/s_conf.h \ + ../include/s_debug.h ../include/s_misc.h ../include/s_stats.h \ + ../include/send.h ../include/sys.h ../include/uping.h \ + ../include/userload.h ../include/version.h ../include/whowas.h ircd_alloc.o: ircd_alloc.c ../config.h ../include/ircd_alloc.h \ ../include/ircd_string.h ../include/ircd_chattr.h ../include/s_debug.h \ ../include/ircd_defs.h @@ -461,8 +460,8 @@ ircd_auth.o: ircd_auth.c ../config.h ../include/client.h \ ../include/ircd_osdep.h ../include/ircd_snprintf.h \ ../include/ircd_string.h ../include/ircd_chattr.h ../include/ircd.h \ ../include/struct.h ../include/msg.h ../include/res.h \ - ../include/s_bsd.h ../include/s_misc.h ../include/s_user.h \ - ../include/send.h + ../include/listener.h ../include/s_bsd.h ../include/s_misc.h \ + ../include/s_user.h ../include/send.h ircd_crypt.o: ircd_crypt.c ../config.h ../include/ircd_crypt.h \ ../include/ircd_alloc.h ../include/ircd_features.h \ ../include/ircd_string.h ../include/ircd_chattr.h ../include/s_debug.h \ @@ -483,6 +482,9 @@ ircd_features.o: ircd_features.c ../config.h ../include/ircd_features.h \ ../include/random.h ../include/s_bsd.h ../include/s_debug.h \ ../include/s_misc.h ../include/send.h ../include/support.h \ ../include/sys.h ../include/whowas.h +ircd_getaddrinfo.o: ircd_getaddrinfo.c ../include/ircd_addrinfo.h +ircd_getnameinfo.o: ircd_getnameinfo.c ../include/ircd_addrinfo.h \ + ../include/ircd_string.h ../include/ircd_chattr.h ircd_log.o: ircd_log.c ../config.h ../include/ircd_log.h \ ../include/client.h ../include/ircd_defs.h ../include/dbuf.h \ ../include/msgq.h ../include/ircd_events.h ../include/ircd_handler.h \ @@ -505,6 +507,21 @@ ircd_reply.o: ircd_reply.c ../config.h ../include/ircd_reply.h \ ../include/ircd.h ../include/struct.h ../include/ircd_snprintf.h \ ../include/msg.h ../include/numeric.h ../include/s_conf.h \ ../include/s_debug.h ../include/send.h +ircd_res.o: ircd_res.c ../include/client.h ../include/ircd_defs.h \ + ../include/dbuf.h ../include/msgq.h ../include/ircd_events.h \ + ../config.h ../include/ircd_handler.h ../include/ircd_alloc.h \ + ../include/ircd_log.h ../include/ircd_osdep.h ../include/ircd_reply.h \ + ../include/ircd_string.h ../include/ircd_chattr.h \ + ../include/ircd_snprintf.h ../include/ircd.h ../include/struct.h \ + ../include/numeric.h ../include/fileio.h ../include/s_bsd.h \ + ../include/s_stats.h ../include/ircd_features.h ../include/send.h \ + ../include/sys.h ../include/res.h ../include/listener.h \ + ../include/ircd_reslib.h ../include/ircd_addrinfo.h +ircd_reslib.o: ircd_reslib.c ../include/ircd.h ../include/struct.h \ + ../include/ircd_defs.h ../include/res.h ../include/listener.h \ + ../include/ircd_events.h ../config.h ../include/ircd_reslib.h \ + ../include/fileio.h ../include/ircd_string.h ../include/ircd_chattr.h \ + ../include/ircd_addrinfo.h ircd_signal.o: ircd_signal.c ../config.h ../include/ircd.h \ ../include/struct.h ../include/ircd_defs.h ../include/ircd_events.h \ ../include/ircd_signal.h ../include/s_conf.h ../include/client.h \ @@ -1112,10 +1129,10 @@ parse.o: parse.c ../config.h ../include/parse.h ../include/client.h \ ../include/ircd_features.h ../include/ircd_reply.h \ ../include/ircd_string.h ../include/msg.h ../include/numeric.h \ ../include/numnicks.h ../include/opercmds.h ../include/querycmds.h \ - ../include/res.h ../include/s_bsd.h ../include/s_conf.h \ - ../include/s_debug.h ../include/s_misc.h ../include/s_numeric.h \ - ../include/s_user.h ../include/send.h ../include/sys.h \ - ../include/whocmds.h ../include/whowas.h + ../include/res.h ../include/listener.h ../include/s_bsd.h \ + ../include/s_conf.h ../include/s_debug.h ../include/s_misc.h \ + ../include/s_numeric.h ../include/s_user.h ../include/send.h \ + ../include/sys.h ../include/whocmds.h ../include/whowas.h querycmds.o: querycmds.c ../config.h ../include/querycmds.h \ ../include/ircd_features.h random.o: random.c ../config.h ../include/random.h ../include/client.h \ @@ -1130,9 +1147,9 @@ s_auth.o: s_auth.c ../config.h ../include/s_auth.h \ ../include/ircd_features.h ../include/ircd_log.h \ ../include/ircd_osdep.h ../include/ircd_snprintf.h \ ../include/ircd_string.h ../include/list.h ../include/numeric.h \ - ../include/querycmds.h ../include/res.h ../include/s_bsd.h \ - ../include/s_debug.h ../include/s_misc.h ../include/send.h \ - ../include/sys.h + ../include/querycmds.h ../include/res.h ../include/listener.h \ + ../include/s_bsd.h ../include/s_debug.h ../include/s_misc.h \ + ../include/send.h ../include/sys.h s_bsd.o: s_bsd.c ../config.h ../include/s_bsd.h ../include/client.h \ ../include/ircd_defs.h ../include/dbuf.h ../include/msgq.h \ ../include/ircd_events.h ../include/ircd_handler.h ../include/IPcheck.h \ @@ -1170,9 +1187,9 @@ s_debug.o: s_debug.c ../config.h ../include/s_debug.h \ ../include/ircd_log.h ../include/ircd_osdep.h ../include/ircd_reply.h \ ../include/ircd.h ../include/struct.h ../include/jupe.h \ ../include/list.h ../include/motd.h ../include/numeric.h \ - ../include/numnicks.h ../include/res.h ../include/s_bsd.h \ - ../include/s_conf.h ../include/s_stats.h ../include/send.h \ - ../include/sys.h ../include/whowas.h + ../include/numnicks.h ../include/res.h ../include/listener.h \ + ../include/s_bsd.h ../include/s_conf.h ../include/s_stats.h \ + ../include/send.h ../include/sys.h ../include/whowas.h s_err.o: s_err.c ../config.h ../include/numeric.h ../include/s_debug.h \ ../include/ircd_defs.h s_misc.o: s_misc.c ../config.h ../include/s_misc.h ../include/IPcheck.h \ @@ -1185,10 +1202,10 @@ s_misc.o: s_misc.c ../config.h ../include/s_misc.h ../include/IPcheck.h \ ../include/ircd_string.h ../include/ircd_chattr.h ../include/list.h \ ../include/match.h ../include/msg.h ../include/numeric.h \ ../include/numnicks.h ../include/parse.h ../include/querycmds.h \ - ../include/res.h ../include/s_bsd.h ../include/s_conf.h \ - ../include/s_debug.h ../include/s_stats.h ../include/s_user.h \ - ../include/send.h ../include/support.h ../include/sys.h \ - ../include/uping.h ../include/userload.h + ../include/res.h ../include/listener.h ../include/s_bsd.h \ + ../include/s_conf.h ../include/s_debug.h ../include/s_stats.h \ + ../include/s_user.h ../include/send.h ../include/support.h \ + ../include/sys.h ../include/uping.h ../include/userload.h s_numeric.o: s_numeric.c ../config.h ../include/s_numeric.h \ ../include/channel.h ../include/ircd_defs.h ../include/client.h \ ../include/dbuf.h ../include/msgq.h ../include/ircd_events.h \ @@ -1216,10 +1233,10 @@ s_stats.o: s_stats.c ../config.h ../include/class.h ../include/client.h \ ../include/ircd_log.h ../include/ircd_reply.h ../include/ircd_string.h \ ../include/listener.h ../include/list.h ../include/match.h \ ../include/motd.h ../include/msg.h ../include/numeric.h \ - ../include/numnicks.h ../include/s_bsd.h ../include/s_conf.h \ - ../include/s_debug.h ../include/s_misc.h ../include/s_serv.h \ - ../include/s_stats.h ../include/s_user.h ../include/send.h \ - ../include/userload.h + ../include/numnicks.h ../include/res.h ../include/s_bsd.h \ + ../include/s_conf.h ../include/s_debug.h ../include/s_misc.h \ + ../include/s_serv.h ../include/s_stats.h ../include/s_user.h \ + ../include/send.h ../include/userload.h s_user.o: s_user.c ../config.h ../include/s_user.h ../include/IPcheck.h \ ../include/channel.h ../include/ircd_defs.h ../include/class.h \ ../include/client.h ../include/dbuf.h ../include/msgq.h \ diff --git a/ircd/ircd_auth.c b/ircd/ircd_auth.c index e3629d0..c697e90 100644 --- a/ircd/ircd_auth.c +++ b/ircd/ircd_auth.c @@ -314,7 +314,7 @@ static void iauth_disconnect(struct IAuth *iauth) s_fd(&i_socket(iauth)) = -1; } -static void iauth_dns_callback(void *vptr, struct hostent *he) +static void iauth_dns_callback(void *vptr, struct DNSReply *he) { struct IAuth *iauth = vptr; if (!he) { @@ -322,8 +322,8 @@ static void iauth_dns_callback(void *vptr, struct hostent *he) } else if (he->h_addrtype != AF_INET) { sendto_opmask_butone(0, SNO_OLDSNO, "IAuth connection to %s failed: bad host type %d", i_host(iauth), he->h_addrtype); } else { - assert(he->h_addrtype == sizeof(i_addr(iauth))); - memcpy(&i_addr(iauth), he->h_addr_list[0], sizeof(i_addr(iauth))); + struct sockaddr_in *sin = (struct sockaddr_in*)&he->addr; + i_addr(iauth) = sin->sin_addr.s_addr; if (INADDR_NONE == i_addr(iauth)) { sendto_opmask_butone(0, SNO_OLDSNO, "IAuth connection to %s failed: host came back as INADDR_NONE", i_host(iauth)); return; diff --git a/ircd/ircd_features.c b/ircd/ircd_features.c index 1fb6e44..5409d22 100644 --- a/ircd/ircd_features.c +++ b/ircd/ircd_features.c @@ -311,6 +311,7 @@ static struct FeatureDesc { F_B(HIS_TRACE, 0, 1, 0), F_B(HIS_STATS_l, 0, 1, 0), F_B(HIS_STATS_L, 0, 1, 0), + F_B(HIS_STATS_a, 0, 1, 0), F_B(HIS_STATS_c, 0, 1, 0), F_B(HIS_STATS_g, 0, 1, 0), F_B(HIS_STATS_h, 0, 1, 0), diff --git a/ircd/ircd_getaddrinfo.c b/ircd/ircd_getaddrinfo.c new file mode 100644 index 0000000..3aff6c1 --- /dev/null +++ b/ircd/ircd_getaddrinfo.c @@ -0,0 +1,631 @@ +/* + * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "ircd_addrinfo.h" +#include /* for offsetof() */ +#include /* for free() */ +#include /* for memset() */ +#include /* for close() */ +#include /* for inet_aton(), inet_pton() */ +#include /* for errno */ + +#ifndef AI_MASK +#define AI_MASK (AI_NUMERICHOST | AI_PASSIVE) +#endif + +/* $Id$ */ + +static const char in_addrany[] = { 0, 0, 0, 0 }; +static const char in_loopback[] = { 127, 0, 0, 1 }; +static const char in6_addrany[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; +static const char in6_loopback[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 +}; + +static const struct afd { + int a_af; + int a_addrlen; + int a_socklen; + int a_off; + const char *a_addrany; + const char *a_loopback; + int a_scoped; +} afdl [] = { +#define N_INET6 0 +#ifdef IPV6 + {PF_INET6, sizeof(struct in6_addr), + sizeof(struct sockaddr_in6), + offsetof(struct sockaddr_in6, sin6_addr), + in6_addrany, in6_loopback, 1}, +#endif +#define N_INET 1 + {PF_INET, sizeof(struct in_addr), + sizeof(struct sockaddr_in), + offsetof(struct sockaddr_in, sin_addr), + in_addrany, in_loopback, 0}, + {0, 0, 0, 0, NULL, NULL, 0}, +}; + +struct explore { + int e_af; + int e_socktype; + int e_protocol; + const char *e_protostr; + int e_wild; +#define WILD_AF(ex) ((ex)->e_wild & 0x01) +#define WILD_SOCKTYPE(ex) ((ex)->e_wild & 0x02) +#define WILD_PROTOCOL(ex) ((ex)->e_wild & 0x04) +}; + +#define ANY 0 + +static const struct explore explore[] = { +#ifdef IPV6 + { PF_INET6, SOCK_DGRAM, IPPROTO_UDP, "udp", 0x07 }, + { PF_INET6, SOCK_STREAM, IPPROTO_TCP, "tcp", 0x07 }, + { PF_INET6, SOCK_RAW, ANY, NULL, 0x05 }, +#endif + { PF_INET, SOCK_DGRAM, IPPROTO_UDP, "udp", 0x07 }, + { PF_INET, SOCK_STREAM, IPPROTO_TCP, "tcp", 0x07 }, + { PF_INET, SOCK_RAW, ANY, NULL, 0x05 }, + { PF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP, "udp", 0x07 }, + { PF_UNSPEC, SOCK_STREAM, IPPROTO_TCP, "tcp", 0x07 }, + { PF_UNSPEC, SOCK_RAW, ANY, NULL, 0x05 }, + { -1, 0, 0, NULL, 0 }, +}; + +#define PTON_MAX 16 + +static int str_isnumber(const char *); +static int explore_null(const struct addrinfo *, + const char *, struct addrinfo **); +static int explore_numeric(const struct addrinfo *, const char *, + const char *, struct addrinfo **); +static struct addrinfo *get_ai(const struct addrinfo *, + const struct afd *, const char *); +static int get_portmatch(const struct addrinfo *, const char *); +static int get_port(struct addrinfo *, const char *, int); +static const struct afd *find_afd(int); +#if 0 +/* We will need this should we ever want gai_strerror(). + * Note though that GNU libc doesn't define EAI_BADHINTS. */ +static char *ai_errlist[] = { + "Success", + "Address family for hostname not supported", /* EAI_ADDRFAMILY */ + "Temporary failure in name resolution", /* EAI_AGAIN */ + "Invalid value for ai_flags", /* EAI_BADFLAGS */ + "Non-recoverable failure in name resolution", /* EAI_FAIL */ + "ai_family not supported", /* EAI_FAMILY */ + "Memory allocation failure", /* EAI_MEMORY */ + "No address associated with hostname", /* EAI_NODATA */ + "hostname nor servname provided, or not known", /* EAI_NONAME */ + "servname not supported for ai_socktype", /* EAI_SERVICE */ + "ai_socktype not supported", /* EAI_SOCKTYPE */ + "System error returned in errno", /* EAI_SYSTEM */ + "Invalid value for hints", /* EAI_BADHINTS */ + "Resolved protocol is unknown", /* EAI_PROTOCOL */ + "Unknown error", /* EAI_MAX */ +}; +#endif +/* XXX macros that make external reference is BAD. */ + +#define GET_AI(ai, afd, addr) \ +do { \ + /* external reference: pai, error, and label free */ \ + (ai) = get_ai(pai, (afd), (addr)); \ + if ((ai) == NULL) { \ + error = EAI_MEMORY; \ + goto free; \ + } \ +} while (/*CONSTCOND*/0) + +#define GET_PORT(ai, serv) \ +do { \ + /* external reference: error and label free */ \ + error = get_port((ai), (serv), 0); \ + if (error != 0) \ + goto free; \ +} while (/*CONSTCOND*/0) + +#define ERR(err) \ +do { \ + /* external reference: error, and label bad */ \ + error = (err); \ + goto bad; \ + /*NOTREACHED*/ \ +} while (/*CONSTCOND*/0) + +#define MATCH_FAMILY(x, y, w) \ + ((x) == (y) || (/*CONSTCOND*/(w) && ((x) == PF_UNSPEC || (y) == PF_UNSPEC))) +#define MATCH(x, y, w) \ + ((x) == (y) || (/*CONSTCOND*/(w) && ((x) == ANY || (y) == ANY))) + +#if 0 +/* We will need this should we ever want gai_strerror() */ +char * +irc_gai_strerror(int ecode) +{ + if (ecode < 0 || ecode > EAI_MAX) + ecode = EAI_MAX; + return ai_errlist[ecode]; +} +#endif + +void +irc_freeaddrinfo(struct addrinfo *ai) +{ + struct addrinfo *next; + + do { + next = ai->ai_next; + if (ai->ai_canonname) + free(ai->ai_canonname); + /* no need to free(ai->ai_addr) */ + free(ai); + ai = next; + } while (ai); +} + +static int +str_isnumber(const char *p) +{ + char *ep; + + if (*p == '\0') + return 0; + ep = NULL; + errno = 0; + (void)strtoul(p, &ep, 10); + if (errno == 0 && ep && *ep == '\0') + return 1; + else + return 0; +} + +int +irc_getaddrinfo(const char *hostname, const char *servname, + const struct addrinfo *hints, struct addrinfo **res) +{ + struct addrinfo sentinel; + struct addrinfo *cur; + int error = 0; + struct addrinfo ai; + struct addrinfo ai0; + struct addrinfo *pai; + const struct explore *ex; + + memset(&sentinel, 0, sizeof(sentinel)); + cur = &sentinel; + pai = &ai; + pai->ai_flags = 0; + pai->ai_family = PF_UNSPEC; + pai->ai_socktype = ANY; + pai->ai_protocol = ANY; + pai->ai_addrlen = 0; + pai->ai_canonname = NULL; + pai->ai_addr = NULL; + pai->ai_next = NULL; + + if (hostname == NULL && servname == NULL) + return EAI_NONAME; + if (hints) { + /* error check for hints */ + if (hints->ai_addrlen || hints->ai_canonname || + hints->ai_addr || hints->ai_next) + { +#ifdef EAI_BADHINTS + ERR(EAI_BADHINTS); /* xxx */ +#else + errno = EINVAL; + ERR(EAI_SYSTEM); +#endif + } + if (hints->ai_flags & ~AI_MASK) + ERR(EAI_BADFLAGS); + switch (hints->ai_family) { + case PF_UNSPEC: + case PF_INET: +#ifdef IPV6 + case PF_INET6: +#endif + break; + default: + ERR(EAI_FAMILY); + } + memcpy(pai, hints, sizeof(*pai)); + + /* + * if both socktype/protocol are specified, check if they + * are meaningful combination. + */ + if (pai->ai_socktype != ANY && pai->ai_protocol != ANY) { + for (ex = explore; ex->e_af >= 0; ex++) { + if (pai->ai_family != ex->e_af) + continue; + if (ex->e_socktype == ANY) + continue; + if (ex->e_protocol == ANY) + continue; + if (pai->ai_socktype == ex->e_socktype && + pai->ai_protocol != ex->e_protocol) { +#ifdef EAI_BADHINTS + ERR(EAI_BADHINTS); /* xxx */ +#else + errno = EINVAL; + ERR(EAI_SYSTEM); +#endif + } + } + } + } + + /* + * check for special cases. (1) numeric servname is disallowed if + * socktype/protocol are left unspecified. (2) servname is disallowed + * for raw and other inet{,6} sockets. + */ + if (MATCH_FAMILY(pai->ai_family, PF_INET, 1) +#ifdef IPV6 + || MATCH_FAMILY(pai->ai_family, PF_INET6, 1) +#endif + ) { + ai0 = *pai; /* backup *pai */ + + if (pai->ai_family == PF_UNSPEC) { +#ifdef IPV6 + pai->ai_family = PF_INET6; +#else + pai->ai_family = PF_INET; +#endif + } + error = get_portmatch(pai, servname); + if (error) + ERR(error); + + *pai = ai0; + } + + ai0 = *pai; + + /* NULL hostname, or numeric hostname */ + for (ex = explore; ex->e_af >= 0; ex++) { + *pai = ai0; + + /* PF_UNSPEC entries are prepared for DNS queries only */ + if (ex->e_af == PF_UNSPEC) + continue; + + if (!MATCH_FAMILY(pai->ai_family, ex->e_af, WILD_AF(ex))) + continue; + if (!MATCH(pai->ai_socktype, ex->e_socktype, WILD_SOCKTYPE(ex))) + continue; + if (!MATCH(pai->ai_protocol, ex->e_protocol, WILD_PROTOCOL(ex))) + continue; + + if (pai->ai_family == PF_UNSPEC) + pai->ai_family = ex->e_af; + if (pai->ai_socktype == ANY && ex->e_socktype != ANY) + pai->ai_socktype = ex->e_socktype; + if (pai->ai_protocol == ANY && ex->e_protocol != ANY) + pai->ai_protocol = ex->e_protocol; + + if (hostname == NULL) + error = explore_null(pai, servname, &cur->ai_next); + else + error = explore_numeric(pai, hostname, servname, &cur->ai_next); + + if (error) + goto free; + + while (cur && cur->ai_next) + cur = cur->ai_next; + } + + /* + * XXX + * If numeric representation of AF1 can be interpreted as FQDN + * representation of AF2, we need to think again about the code below. + */ + if (sentinel.ai_next) + goto good; + + if (pai->ai_flags & AI_NUMERICHOST) + ERR(EAI_NONAME); + if (hostname == NULL) + ERR(EAI_NODATA); + + /* XXX */ + if (sentinel.ai_next) + error = 0; + + if (error) + goto free; + if (error == 0) { + if (sentinel.ai_next) { + good: + *res = sentinel.ai_next; + return 0; + } else + error = EAI_FAIL; + } + free: + bad: + if (sentinel.ai_next) + irc_freeaddrinfo(sentinel.ai_next); + *res = NULL; + return error; +} + +/* + * hostname == NULL. + * passive socket -> anyaddr (0.0.0.0 or ::) + * non-passive socket -> localhost (127.0.0.1 or ::1) + */ +static int +explore_null(const struct addrinfo *pai, const char *servname, struct addrinfo **res) +{ + int s; + const struct afd *afd; + struct addrinfo *cur; + struct addrinfo sentinel; + int error; + + *res = NULL; + sentinel.ai_next = NULL; + cur = &sentinel; + + /* + * filter out AFs that are not supported by the kernel + * XXX errno? + */ + s = socket(pai->ai_family, SOCK_DGRAM, 0); + if (s < 0) { + if (errno != EMFILE) + return 0; + } else + close(s); + + /* + * if the servname does not match socktype/protocol, ignore it. + */ + if (get_portmatch(pai, servname) != 0) + return 0; + + afd = find_afd(pai->ai_family); + if (afd == NULL) + return 0; + + if (pai->ai_flags & AI_PASSIVE) { + GET_AI(cur->ai_next, afd, afd->a_addrany); + GET_PORT(cur->ai_next, servname); + } else { + GET_AI(cur->ai_next, afd, afd->a_loopback); + GET_PORT(cur->ai_next, servname); + } + cur = cur->ai_next; + + *res = sentinel.ai_next; + return 0; + +free: + if (sentinel.ai_next) + irc_freeaddrinfo(sentinel.ai_next); + return error; +} + +/* + * numeric hostname + */ +static int +explore_numeric(const struct addrinfo *pai, const char *hostname, + const char *servname, struct addrinfo **res) +{ + const struct afd *afd; + struct addrinfo *cur; + struct addrinfo sentinel; + int error; + char pton[PTON_MAX]; + + *res = NULL; + sentinel.ai_next = NULL; + cur = &sentinel; + + /* + * if the servname does not match socktype/protocol, ignore it. + */ + if (get_portmatch(pai, servname) != 0) + return 0; + + afd = find_afd(pai->ai_family); + if (afd == NULL) + return 0; + + switch (afd->a_af) { +#if 1 /*X/Open spec*/ + case AF_INET: + if (inet_aton(hostname, (struct in_addr *)pton) == 1) { + if (pai->ai_family == afd->a_af || + pai->ai_family == PF_UNSPEC /*?*/) { + GET_AI(cur->ai_next, afd, pton); + GET_PORT(cur->ai_next, servname); + while (cur && cur->ai_next) + cur = cur->ai_next; + } else + ERR(EAI_FAMILY); /*xxx*/ + } + break; +#endif + default: + if (inet_pton(afd->a_af, hostname, pton) == 1) { + if (pai->ai_family == afd->a_af || + pai->ai_family == PF_UNSPEC /*?*/) { + GET_AI(cur->ai_next, afd, pton); + GET_PORT(cur->ai_next, servname); + while (cur && cur->ai_next) + cur = cur->ai_next; + } else + ERR(EAI_FAMILY); /* XXX */ + } + break; + } + + *res = sentinel.ai_next; + return 0; + +free: +bad: + if (sentinel.ai_next) + irc_freeaddrinfo(sentinel.ai_next); + return error; +} + +static struct addrinfo * +get_ai(const struct addrinfo *pai, const struct afd *afd, const char *addr) +{ + char *p; + struct addrinfo *ai; + + ai = (struct addrinfo *)malloc(sizeof(struct addrinfo) + + (afd->a_socklen)); + if (ai == NULL) + return NULL; + + memcpy(ai, pai, sizeof(struct addrinfo)); + ai->ai_addr = (struct sockaddr *)(void *)(ai + 1); + memset(ai->ai_addr, 0, (size_t)afd->a_socklen); + ai->ai_addrlen = afd->a_socklen; + ai->ai_addr->sa_family = ai->ai_family = afd->a_af; + p = (char *)(void *)(ai->ai_addr); + memcpy(p + afd->a_off, addr, (size_t)afd->a_addrlen); + return ai; +} + +static int +get_portmatch(const struct addrinfo *ai, const char *servname) +{ + /* get_port does not touch first argument. when matchonly == 1. */ + /* LINTED const cast */ + return(get_port((struct addrinfo *)ai, servname, 1)); +} + +static int +get_port(struct addrinfo *ai, const char *servname, int matchonly) +{ + const char *proto; + struct servent *sp; + int port; + int allownumeric; + + if (servname == NULL) + return 0; + switch (ai->ai_family) { + case AF_INET: +#ifdef AF_INET6 + case AF_INET6: +#endif + break; + default: + return 0; + } + + switch (ai->ai_socktype) { + case SOCK_RAW: + return EAI_SERVICE; + case SOCK_DGRAM: + case SOCK_STREAM: + allownumeric = 1; + break; + case ANY: + allownumeric = 0; + break; + default: + return EAI_SOCKTYPE; + } + + if (str_isnumber(servname)) { + if (!allownumeric) + return EAI_SERVICE; + port = atoi(servname); + if (port < 0 || port > 65535) + return EAI_SERVICE; + port = htons(port); + } else { + switch (ai->ai_socktype) { + case SOCK_DGRAM: + proto = "udp"; + break; + case SOCK_STREAM: + proto = "tcp"; + break; + default: + proto = NULL; + break; + } + + if ((sp = getservbyname(servname, proto)) == NULL) + return EAI_SERVICE; + port = sp->s_port; + } + + if (!matchonly) { + switch (ai->ai_family) { + case AF_INET: + ((struct sockaddr_in *)(void *) + ai->ai_addr)->sin_port = port; + break; +#ifdef IPV6 + case AF_INET6: + ((struct sockaddr_in6 *)(void *) + ai->ai_addr)->sin6_port = port; + break; +#endif + } + } + + return 0; +} + +static const struct afd * +find_afd(int af) +{ + const struct afd *afd; + + if (af == PF_UNSPEC) + return(NULL); + + for (afd = afdl; afd->a_af; afd++) + { + if (afd->a_af == af) + return(afd); + } + + return(NULL); +} diff --git a/ircd/ircd_getnameinfo.c b/ircd/ircd_getnameinfo.c new file mode 100644 index 0000000..0224505 --- /dev/null +++ b/ircd/ircd_getnameinfo.c @@ -0,0 +1,243 @@ +/* + * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Issues to be discussed: + * - Thread safe-ness must be checked + * - RFC2553 says that we should raise error on short buffer. X/Open says + * we need to truncate the result. We obey RFC2553 (and X/Open should be + * modified). ipngwg rough consensus seems to follow RFC2553. + * - What is "local" in NI_FQDN? + * - NI_NAMEREQD and NI_NUMERICHOST conflict with each other. + * - (KAME extension) always attach textual scopeid (fe80::1%lo0), if + * sin6_scope_id is filled - standardization status? + * XXX breaks backward compat for code that expects no scopeid. + * beware on merge. + */ + +#include "ircd_addrinfo.h" +#include "ircd_string.h" +#include /* for offsetof() */ +#include /* for snprintf() */ +#include /* for strlen() */ +#include /* for inet_aton(), inet_pton() */ + +/* $Id$ */ + +static const struct afd { + int a_af; + int a_addrlen; + socklen_t a_socklen; + int a_off; +} afdl [] = { +#ifdef IPV6 + {PF_INET6, sizeof(struct in6_addr), sizeof(struct sockaddr_in6), + offsetof(struct sockaddr_in6, sin6_addr)}, +#endif + {PF_INET, sizeof(struct in_addr), sizeof(struct sockaddr_in), + offsetof(struct sockaddr_in, sin_addr)}, + {0, 0, 0, 0}, +}; + +struct sockinet +{ + unsigned char si_len; + unsigned char si_family; + unsigned short si_port; +}; + +#ifdef IPV6 +static int ip6_parsenumeric(const struct sockaddr *, const char *, char *, + size_t, int); +#endif + +int +irc_getnameinfo(const struct sockaddr *sa, socklen_t salen, char *host, + size_t hostlen, char *serv, size_t servlen, int flags) +{ + const struct afd *afd; + struct servent *sp; + unsigned short port; + int family, i; + const char *addr; + u_int32_t v4a; + char numserv[512]; + char numaddr[512]; + + if (sa == NULL) + return EAI_FAIL; + +/* if (sa->sa_len != salen) + return EAI_FAIL; +*/ + family = sa->sa_family; + for (i = 0; afdl[i].a_af; i++) + if (afdl[i].a_af == family) { + afd = &afdl[i]; + goto found; + } + return EAI_FAMILY; + + found: + if (salen != afd->a_socklen) + return EAI_FAIL; + + /* network byte order */ + port = ((const struct sockinet *)sa)->si_port; + addr = (const char *)sa + afd->a_off; + + if (serv == NULL || servlen == 0) { + /* + * do nothing in this case. + * in case you are wondering if "&&" is more correct than + * "||" here: rfc2553bis-03 says that serv == NULL OR + * servlen == 0 means that the caller does not want the result. + */ + } else { + if (flags & NI_NUMERICSERV) + sp = NULL; + else { + sp = getservbyport(port, + (flags & NI_DGRAM) ? "udp" : "tcp"); + } + if (sp) { + if (strlen(sp->s_name) + 1 > servlen) + return EAI_MEMORY; + ircd_strncpy(serv, sp->s_name, servlen); + } else { + snprintf(numserv, sizeof(numserv), "%u", ntohs(port)); + if (strlen(numserv) + 1 > servlen) + return EAI_MEMORY; + ircd_strncpy(serv, numserv, servlen); + } + } + + switch (sa->sa_family) { + case AF_INET: + v4a = (u_int32_t) + ntohl(((const struct sockaddr_in *)sa)->sin_addr.s_addr); + if (IN_MULTICAST(v4a) || IN_EXPERIMENTAL(v4a)) + flags |= NI_NUMERICHOST; + v4a >>= IN_CLASSA_NSHIFT; + if (v4a == 0) + flags |= NI_NUMERICHOST; + break; +#ifdef IPV6 + case AF_INET6: + { + const struct sockaddr_in6 *sin6; + sin6 = (const struct sockaddr_in6 *)sa; + switch (sin6->sin6_addr.s6_addr[0]) { + case 0x00: + if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) + ; + else if (IN6_IS_ADDR_LOOPBACK(&sin6->sin6_addr)) + ; + else + flags |= NI_NUMERICHOST; + break; + default: + if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) { + flags |= NI_NUMERICHOST; + } + else if (IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr)) + flags |= NI_NUMERICHOST; + break; + } + } + break; +#endif + } + if (host == NULL || hostlen == 0) { + /* + * do nothing in this case. + * in case you are wondering if "&&" is more correct than + * "||" here: rfc2553bis-03 says that host == NULL or + * hostlen == 0 means that the caller does not want the result. + */ + } else if (flags & NI_NUMERICHOST) { + size_t numaddrlen; + + /* NUMERICHOST and NAMEREQD conflicts with each other */ + if (flags & NI_NAMEREQD) + return EAI_NONAME; + + switch(afd->a_af) { +#ifdef IPV6 + case AF_INET6: + { + int error; + + if ((error = ip6_parsenumeric(sa, addr, host, + hostlen, flags)) != 0) + return(error); + break; + } +#endif + default: + if (inet_ntop(afd->a_af, addr, numaddr, sizeof(numaddr)) + == NULL) + return EAI_SYSTEM; + numaddrlen = strlen(numaddr); + if (numaddrlen + 1 > hostlen) /* don't forget terminator */ + return EAI_MEMORY; + ircd_strncpy(host, numaddr, hostlen); + break; + } + } + return(0); +} + +#ifdef IPV6 +static int +ip6_parsenumeric(const struct sockaddr *sa, const char *addr, + char *host, size_t hostlen, int flags) +{ + size_t numaddrlen; + char numaddr[512]; + + if (inet_ntop(AF_INET6, addr, numaddr, sizeof(numaddr)) == NULL) + return(EAI_SYSTEM); + + numaddrlen = strlen(numaddr); + + if (numaddrlen + 1 > hostlen) /* don't forget terminator */ + return(EAI_MEMORY); + + if (*numaddr == ':') + { + *host = '0'; + ircd_strncpy(host+1, numaddr, hostlen-1); + } + else + ircd_strncpy(host, numaddr, hostlen); + + return(0); +} +#endif diff --git a/ircd/ircd_res.c b/ircd/ircd_res.c new file mode 100644 index 0000000..2843fe2 --- /dev/null +++ b/ircd/ircd_res.c @@ -0,0 +1,993 @@ +/* + * A rewrite of Darren Reeds original res.c As there is nothing + * left of Darrens original code, this is now licensed by the hybrid group. + * (Well, some of the function names are the same, and bits of the structs..) + * You can use it where it is useful, free even. Buy us a beer and stuff. + * + * The authors takes no responsibility for any damage or loss + * of property which results from the use of this software. + * + * $Id$ + * + * July 1999 - Rewrote a bunch of stuff here. Change hostent builder code, + * added callbacks and reference counting of returned hostents. + * --Bleep (Thomas Helvey ) + * + * This was all needlessly complicated for irc. Simplified. No more hostent + * All we really care about is the IP -> hostname mappings. Thats all. + * + * Apr 28, 2003 --cryogen and Dianora + */ + +#include "client.h" +#include "ircd_alloc.h" +#include "ircd_log.h" +#include "ircd_osdep.h" +#include "ircd_reply.h" +#include "ircd_string.h" +#include "ircd_snprintf.h" +#include "ircd.h" +#include "numeric.h" +#include "fileio.h" /* for fbopen / fbclose / fbputs */ +#include "s_bsd.h" +#include "s_stats.h" +#include "send.h" +#include "sys.h" +#include "res.h" +#include "ircd_reslib.h" +#include "ircd_addrinfo.h" + +#include +#include +#include +#include +#include + +#if (CHAR_BIT != 8) +#error this code needs to be able to address individual octets +#endif + +static struct Socket res_socket; +static struct Timer res_timeout; + +#define MAXPACKET 1024 /* rfc sez 512 but we expand names so ... */ +#define RES_MAXALIASES 35 /* maximum aliases allowed */ +#define RES_MAXADDRS 35 /* maximum addresses allowed */ +#define AR_TTL 600 /* TTL in seconds for dns cache entries */ + +/* RFC 1104/1105 wasn't very helpful about what these fields + * should be named, so for now, we'll just name them this way. + * we probably should look at what named calls them or something. + */ +#define TYPE_SIZE (size_t)2 +#define CLASS_SIZE (size_t)2 +#define TTL_SIZE (size_t)4 +#define RDLENGTH_SIZE (size_t)2 +#define ANSWER_FIXED_SIZE (TYPE_SIZE + CLASS_SIZE + TTL_SIZE + RDLENGTH_SIZE) + +typedef enum +{ + REQ_IDLE, /* We're doing not much at all */ + REQ_PTR, /* Looking up a PTR */ + REQ_A, /* Looking up an A, possibly because AAAA failed */ +#ifdef IPV6 + REQ_AAAA, /* Looking up an AAAA */ +#endif + REQ_CNAME, /* We got a CNAME in response, we better get a real answer next */ + REQ_INT /* ip6.arpa failed, falling back to ip6.int */ +} request_state; + +struct dlink +{ + struct dlink *prev; + struct dlink *next; +}; + +struct reslist +{ + struct dlink node; + int id; + int sent; /* number of requests sent */ + request_state state; /* State the resolver machine is in */ + time_t ttl; + char type; + char retries; /* retry counter */ + char sends; /* number of sends (>1 means resent) */ + char resend; /* send flag. 0 == dont resend */ + time_t sentat; + time_t timeout; + struct irc_ssaddr addr; + char *name; + struct DNSQuery query; /* query callback for this request */ +}; + +static struct dlink request_list; + +static void rem_request(struct reslist *request); +static struct reslist *make_request(const struct DNSQuery *query); +static void do_query_name(const struct DNSQuery *query, + const char* name, struct reslist *request, int); +static void do_query_number(const struct DNSQuery *query, + const struct irc_ssaddr *, + struct reslist *request); +static void query_name(const char *name, int query_class, int query_type, + struct reslist *request); +static int send_res_msg(const char *buf, int len, int count); +static void resend_query(struct reslist *request); +static int proc_answer(struct reslist *request, HEADER *header, char *, char *); +static struct reslist *find_id(int id); +static struct DNSReply *make_dnsreply(struct reslist *request); +static void res_readreply(struct Event *ev); +static void timeout_resolver(struct Event *notused); + +extern struct irc_ssaddr irc_nsaddr_list[IRCD_MAXNS]; +extern int irc_nscount; +extern char irc_domain[HOSTLEN]; + +/* + * int + * res_ourserver(inp) + * looks up "inp" in irc_nsaddr_list[] + * returns: + * 0 : not found + * >0 : found + * author: + * paul vixie, 29may94 + * revised for ircd, cryogen(stu) may03 + */ +static int +res_ourserver(const struct irc_ssaddr *inp) +{ +#ifdef IPV6 + struct sockaddr_in6 *v6; + struct sockaddr_in6 *v6in = (struct sockaddr_in6 *)inp; +#endif + struct sockaddr_in *v4; + struct sockaddr_in *v4in = (struct sockaddr_in *)inp; + int ns; + + for (ns = 0; ns < irc_nscount; ns++) + { + const struct irc_ssaddr *srv = &irc_nsaddr_list[ns]; +#ifdef IPV6 + v6 = (struct sockaddr_in6 *)srv; +#endif + v4 = (struct sockaddr_in *)srv; + + /* could probably just memcmp(srv, inp, srv.ss_len) here + * but we'll air on the side of caution - stu + * + */ + switch (srv->ss.ss_family) + { +#ifdef IPV6 + case AF_INET6: + if (srv->ss.ss_family == inp->ss.ss_family) + if (v6->sin6_port == v6in->sin6_port) + if ((memcmp(&v6->sin6_addr.s6_addr, &v6in->sin6_addr.s6_addr, + sizeof(struct in6_addr)) == 0) || + (memcmp(&v6->sin6_addr.s6_addr, &in6addr_any, + sizeof(struct in6_addr)) == 0)) + return(1); + break; +#endif + case AF_INET: + if (srv->ss.ss_family == inp->ss.ss_family) + if (v4->sin_port == v4in->sin_port) + if ((v4->sin_addr.s_addr == INADDR_ANY) || + (v4->sin_addr.s_addr == v4in->sin_addr.s_addr)) + return(1); + break; + default: + break; + } + } + + return(0); +} + +/* + * start_resolver - do everything we need to read the resolv.conf file + * and initialize the resolver file descriptor if needed + */ +static void +start_resolver(void) +{ + irc_res_init(); + + if (!request_list.next) + request_list.next = request_list.prev = &request_list; + + if (!s_active(&res_socket)) + { + int fd; + fd = socket(irc_nsaddr_list[0].ss.ss_family, SOCK_DGRAM, 0); + if (fd < 0) {} /* TODO: bail on socket() failure */ + if (!os_set_nonblocking(fd)) {} /* TODO: bail on failure */ + if (!socket_add(&res_socket, res_readreply, NULL, SS_DATAGRAM, + SOCK_EVENT_READABLE, fd)) {} /* TODO: bail on socket_add() failure */ + timer_init(&res_timeout); + timer_add(&res_timeout, timeout_resolver, NULL, TT_PERIODIC, 1); + } +} + +/* + * init_resolver - initialize resolver and resolver library + */ +int +init_resolver(void) +{ +#ifdef LRAND48 + srand48(CurrentTime); +#endif + start_resolver(); + return(s_fd(&res_socket)); +} + +/* + * restart_resolver - reread resolv.conf, reopen socket + */ +void +restart_resolver(void) +{ + start_resolver(); +} + +/* + * add_local_domain - Add the domain to hostname, if it is missing + * (as suggested by eps@TOASTER.SFSU.EDU) + */ +void +add_local_domain(char* hname, size_t size) +{ + /* try to fix up unqualified names + */ + if (strchr(hname, '.') == NULL) + { + if (irc_domain[0]) + { + size_t len = strlen(hname); + + if ((strlen(irc_domain) + len + 2) < size) + { + hname[len++] = '.'; + strcpy(hname + len, irc_domain); + } + } + } +} + +/* + * remove_dlink - remove a link from a doubly linked list + */ +static void +remove_dlink(struct dlink *node) +{ + node->prev->next = node->next; + node->next->prev = node->prev; +} + +/* + * add_dlink - add a link to a doubly linked list + */ +static void +add_dlink(struct dlink *node, struct dlink *next) +{ + node->prev = next->prev; + node->next = next; + node->prev->next = node; + node->next->prev = node; +} + +/* + * rem_request - remove a request from the list. + * This must also free any memory that has been allocated for + * temporary storage of DNS results. + */ +static void +rem_request(struct reslist *request) +{ + remove_dlink(&request->node); + MyFree(request->name); + MyFree(request); +} + +/* + * make_request - Create a DNS request record for the server. + */ +static struct reslist * +make_request(const struct DNSQuery* query) +{ + struct reslist *request; + + request = (struct reslist *)MyMalloc(sizeof(struct reslist)); + memset(request, 0, sizeof(struct reslist)); + + request->sentat = CurrentTime; + request->retries = 3; + request->resend = 1; + request->timeout = 4; /* start at 4 and exponential inc. */ + memset(&request->addr, 0, sizeof(request->addr)); + request->query.vptr = query->vptr; + request->query.callback = query->callback; + request->state = REQ_IDLE; + + add_dlink(&request->node, &request_list); + return(request); +} + +/* + * timeout_query_list - Remove queries from the list which have been + * there too long without being resolved. + */ +static time_t +timeout_query_list(time_t now) +{ + struct dlink *ptr, *next_ptr; + struct reslist *request; + time_t next_time = 0; + time_t timeout = 0; + + for (ptr = request_list.next; ptr != &request_list; ptr = next_ptr) + { + next_ptr = ptr->next; + request = (struct reslist*)ptr; + timeout = request->sentat + request->timeout; + + if (now >= timeout) + { + if (--request->retries <= 0) + { + (*request->query.callback)(request->query.vptr, 0); + rem_request(request); + continue; + } + else + { + request->sentat = now; + request->timeout += request->timeout; + resend_query(request); + } + } + + if ((next_time == 0) || timeout < next_time) + { + next_time = timeout; + } + } + + return((next_time > now) ? next_time : (now + AR_TTL)); +} + +/* + * timeout_resolver - check request list + */ +static void +timeout_resolver(struct Event *notused) +{ + timeout_query_list(CurrentTime); +} + +/* + * delete_resolver_queries - cleanup outstanding queries + * for which there no longer exist clients or conf lines. + */ +void +delete_resolver_queries(const void *vptr) +{ + struct dlink *ptr, *next_ptr; + struct reslist *request; + + for (ptr = request_list.next; ptr != &request_list; ptr = next_ptr) + { + next_ptr = ptr->next; + request = (struct reslist*)ptr; + if (vptr == request->query.vptr) + rem_request(request); + } +} + +/* + * send_res_msg - sends msg to all nameservers found in the "_res" structure. + * This should reflect /etc/resolv.conf. We will get responses + * which arent needed but is easier than checking to see if nameserver + * isnt present. Returns number of messages successfully sent to + * nameservers or -1 if no successful sends. + */ +static int +send_res_msg(const char *msg, int len, int rcount) +{ + int i; + int sent = 0; + int max_queries = IRCD_MIN(irc_nscount, rcount); + + /* RES_PRIMARY option is not implemented + * if (res.options & RES_PRIMARY || 0 == max_queries) + */ + if (max_queries == 0) + max_queries = 1; + + for (i = 0; i < max_queries; i++) + { + if (sendto(s_fd(&res_socket), msg, len, 0, + (struct sockaddr*)&(irc_nsaddr_list[i]), + irc_nsaddr_list[i].ss_len) == len) + ++sent; + } + + return(sent); +} + +/* + * find_id - find a dns request id (id is determined by dn_mkquery) + */ +static struct reslist * +find_id(int id) +{ + struct dlink *ptr; + struct reslist *request; + + for (ptr = request_list.next; ptr != &request_list; ptr = ptr->next) + { + request = (struct reslist*)ptr; + + if (request->id == id) + return(request); + } + + return(NULL); +} + +/* + * gethost_byname_type - get host address from name + * + */ +void +gethost_byname_type(const char *name, const struct DNSQuery *query, int type) +{ + assert(name != 0); + do_query_name(query, name, NULL, type); +} + +/* + * gethost_byname - wrapper for _type - send T_AAAA first + */ +void +gethost_byname(const char *name, const struct DNSQuery *query) +{ + gethost_byname_type(name, query, T_AAAA); +} + +/* + * gethost_byaddr - get host name from address + */ +void +gethost_byaddr(const struct irc_ssaddr *addr, const struct DNSQuery *query) +{ + do_query_number(query, addr, NULL); +} + +/* + * gethost_byinaddr - kludgy hack for IPv4-only compatibility + */ +void +gethost_byinaddr(const struct in_addr *addr, const struct DNSQuery *query) +{ + struct irc_ssaddr new_addr; + struct sockaddr_in *sin; + memset(&new_addr, 0, sizeof(new_addr)); + sin = (struct sockaddr_in*)&new_addr.ss; + sin->sin_family = AF_INET; + memcpy(&sin->sin_addr, addr, sizeof(sin->sin_addr)); + new_addr.ss_len = sizeof(*sin); + gethost_byaddr(&new_addr, query); +} + +/* + * do_query_name - nameserver lookup name + */ +static void +do_query_name(const struct DNSQuery *query, const char *name, + struct reslist *request, int type) +{ + char host_name[HOSTLEN + 1]; + + ircd_strncpy(host_name, name, HOSTLEN); + add_local_domain(host_name, HOSTLEN); + + if (request == NULL) + { + request = make_request(query); + request->name = (char *)MyMalloc(strlen(host_name) + 1); + request->type = type; + strcpy(request->name, host_name); +#ifdef IPV6 + if (type != T_A) + request->state = REQ_AAAA; + else +#endif + request->state = REQ_A; + } + + request->type = type; + query_name(host_name, C_IN, type, request); +} + +/* + * do_query_number - Use this to do reverse IP# lookups. + */ +static void +do_query_number(const struct DNSQuery *query, const struct irc_ssaddr *addr, + struct reslist *request) +{ + char ipbuf[128]; + const unsigned char *cp; +#ifdef IPV6 + const char *intarpa; +#endif + if (addr->ss.ss_family == AF_INET) + { + struct sockaddr_in *v4 = (struct sockaddr_in *)addr; + cp = (const unsigned char*)&v4->sin_addr.s_addr; + + ircd_snprintf(NULL, ipbuf, sizeof(ipbuf), + "%u.%u.%u.%u.in-addr.arpa.", + (unsigned int)(cp[3]), (unsigned int)(cp[2]), + (unsigned int)(cp[1]), (unsigned int)(cp[0])); + } +#ifdef IPV6 + else if (addr->ss.ss_family == AF_INET6) + { + struct sockaddr_in6 *v6 = (struct sockaddr_in6 *)addr; + cp = (const unsigned char *)&v6->sin6_addr.s6_addr; + + if (request != NULL && request->state == REQ_INT) + intarpa = "int"; + else + intarpa = "arpa"; + + ircd_snprintf(NULL, ipbuf, sizeof(ipbuf), + "%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x." + "%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.ip6.%s.", + (unsigned int)(cp[15]&0xf), (unsigned int)(cp[15]>>4), + (unsigned int)(cp[14]&0xf), (unsigned int)(cp[14]>>4), + (unsigned int)(cp[13]&0xf), (unsigned int)(cp[13]>>4), + (unsigned int)(cp[12]&0xf), (unsigned int)(cp[12]>>4), + (unsigned int)(cp[11]&0xf), (unsigned int)(cp[11]>>4), + (unsigned int)(cp[10]&0xf), (unsigned int)(cp[10]>>4), + (unsigned int)(cp[9]&0xf), (unsigned int)(cp[9]>>4), + (unsigned int)(cp[8]&0xf), (unsigned int)(cp[8]>>4), + (unsigned int)(cp[7]&0xf), (unsigned int)(cp[7]>>4), + (unsigned int)(cp[6]&0xf), (unsigned int)(cp[6]>>4), + (unsigned int)(cp[5]&0xf), (unsigned int)(cp[5]>>4), + (unsigned int)(cp[4]&0xf), (unsigned int)(cp[4]>>4), + (unsigned int)(cp[3]&0xf), (unsigned int)(cp[3]>>4), + (unsigned int)(cp[2]&0xf), (unsigned int)(cp[2]>>4), + (unsigned int)(cp[1]&0xf), (unsigned int)(cp[1]>>4), + (unsigned int)(cp[0]&0xf), (unsigned int)(cp[0]>>4), intarpa); + } +#endif + if (request == NULL) + { + request = make_request(query); + request->type = T_PTR; + memcpy(&request->addr, addr, sizeof(struct irc_ssaddr)); + request->name = (char *)MyMalloc(HOSTLEN + 1); + } + + query_name(ipbuf, C_IN, T_PTR, request); +} + +/* + * query_name - generate a query based on class, type and name. + */ +static void +query_name(const char *name, int query_class, int type, + struct reslist *request) +{ + char buf[MAXPACKET]; + int request_len = 0; + + memset(buf, 0, sizeof(buf)); + + if ((request_len = irc_res_mkquery(name, query_class, type, + (unsigned char *)buf, sizeof(buf))) > 0) + { + HEADER *header = (HEADER *)buf; +#ifndef LRAND48 + int k = 0; + struct timeval tv; +#endif + /* + * generate an unique id + * NOTE: we don't have to worry about converting this to and from + * network byte order, the nameserver does not interpret this value + * and returns it unchanged + */ +#ifdef LRAND48 + do + { + header->id = (header->id + lrand48()) & 0xffff; + } while (find_id(header->id)); +#else + gettimeofday(&tv, NULL); + + do + { + header->id = (header->id + k + tv.tv_usec) & 0xffff; + k++; + } while (find_id(header->id)); +#endif /* LRAND48 */ + request->id = header->id; + ++request->sends; + + request->sent += send_res_msg(buf, request_len, request->sends); + } +} + +static void +resend_query(struct reslist *request) +{ + if (request->resend == 0) + return; + + switch(request->type) + { + case T_PTR: + do_query_number(NULL, &request->addr, request); + break; + case T_A: + do_query_name(NULL, request->name, request, request->type); + break; +#ifdef IPV6 + case T_AAAA: + /* didnt work, try A */ + if (request->state == REQ_AAAA) + do_query_name(NULL, request->name, request, T_A); +#endif + default: + break; + } +} + +/* + * proc_answer - process name server reply + */ +static int +proc_answer(struct reslist *request, HEADER* header, char* buf, char* eob) +{ + char hostbuf[HOSTLEN + 100]; /* working buffer */ + unsigned char *current; /* current position in buf */ + int query_class; /* answer class */ + int type; /* answer type */ + int n; /* temp count */ + int rd_length; + struct sockaddr_in *v4; /* conversion */ +#ifdef IPV6 + struct sockaddr_in6 *v6; +#endif + current = (unsigned char *)buf + sizeof(HEADER); + + for (; header->qdcount > 0; --header->qdcount) + { + if ((n = irc_dn_skipname(current, (unsigned char *)eob)) < 0) + break; + + current += (size_t) n + QFIXEDSZ; + } + + /* + * process each answer sent to us blech. + */ + while (header->ancount > 0 && (char *)current < eob) + { + header->ancount--; + + n = irc_dn_expand((unsigned char *)buf, (unsigned char *)eob, current, + hostbuf, sizeof(hostbuf)); + + if (n < 0) + { + /* + * broken message + */ + return(0); + } + else if (n == 0) + { + /* + * no more answers left + */ + return(0); + } + + hostbuf[HOSTLEN] = '\0'; + + /* With Address arithmetic you have to be very anal + * this code was not working on alpha due to that + * (spotted by rodder/jailbird/dianora) + */ + current += (size_t) n; + + if (!(((char *)current + ANSWER_FIXED_SIZE) < eob)) + break; + + type = irc_ns_get16(current); + current += TYPE_SIZE; + + query_class = irc_ns_get16(current); + current += CLASS_SIZE; + + request->ttl = irc_ns_get32(current); + current += TTL_SIZE; + + rd_length = irc_ns_get16(current); + current += RDLENGTH_SIZE; + + /* + * Wait to set request->type until we verify this structure + */ + switch (type) + { + case T_A: + if (request->type != T_A) + return(0); + + /* + * check for invalid rd_length or too many addresses + */ + if (rd_length != sizeof(struct in_addr)) + return(0); + v4 = (struct sockaddr_in *)&request->addr; + request->addr.ss_len = sizeof(struct sockaddr_in); + v4->sin_family = AF_INET; + memcpy(&v4->sin_addr, current, sizeof(struct in_addr)); + return(1); + break; +#ifdef IPV6 + case T_AAAA: + if (request->type != T_AAAA) + return(0); + if (rd_length != sizeof(struct in6_addr)) + return(0); + request->addr.ss_len = sizeof(struct sockaddr_in6); + v6 = (struct sockaddr_in6 *)&request->addr; + v6->sin6_family = AF_INET6; + memcpy(&v6->sin6_addr, current, sizeof(struct in6_addr)); + return(1); + break; +#endif + case T_PTR: + if (request->type != T_PTR) + return(0); + n = irc_dn_expand((unsigned char *)buf, (unsigned char *)eob, + current, hostbuf, sizeof(hostbuf)); + if (n < 0) + return(0); /* broken message */ + else if (n == 0) + return(0); /* no more answers left */ + + ircd_strncpy(request->name, hostbuf, HOSTLEN); + + return(1); + break; + case T_CNAME: /* first check we already havent started looking + into a cname */ + if (request->type != T_PTR) + return(0); + + if (request->state == REQ_CNAME) + { + n = irc_dn_expand((unsigned char *)buf, (unsigned char *)eob, + current, hostbuf, sizeof(hostbuf)); + + if (n < 0) + return(0); + return(1); + } + + request->state = REQ_CNAME; + current += rd_length; + break; + + default: + /* XXX I'd rather just throw away the entire bogus thing + * but its possible its just a broken nameserver with still + * valid answers. But lets do some rudimentary logging for now... + */ + log_write(LS_RESOLVER, L_ERROR, 0, "irc_res.c bogus type %d", type); + break; + } + } + + return(1); +} + +/* + * res_readreply - read a dns reply from the nameserver and process it. + */ +static void +res_readreply(struct Event *ev) +{ + struct Socket *sock; + char buf[sizeof(HEADER) + MAXPACKET]; + HEADER *header; + struct reslist *request = NULL; + struct DNSReply *reply = NULL; + int rc; + int answer_count; + socklen_t len = sizeof(struct irc_ssaddr); + struct irc_ssaddr lsin; + + assert(ev_socket(ev) == &res_socket); + sock = ev_socket(ev); + + rc = recvfrom(s_fd(sock), buf, sizeof(buf), 0, (struct sockaddr *)&lsin, &len); + /* Better to cast the sizeof instead of rc */ + if (rc <= (int)(sizeof(HEADER))) + return; + + /* + * convert DNS reply reader from Network byte order to CPU byte order. + */ + header = (HEADER *)buf; + header->ancount = ntohs(header->ancount); + header->qdcount = ntohs(header->qdcount); + header->nscount = ntohs(header->nscount); + header->arcount = ntohs(header->arcount); + + /* + * response for an id which we have already received an answer for + * just ignore this response. + */ + if (0 == (request = find_id(header->id))) + return; + + /* + * check against possibly fake replies + */ + if (!res_ourserver(&lsin)) + return; + + if ((header->rcode != NO_ERRORS) || (header->ancount == 0)) + { + if (SERVFAIL == header->rcode) + resend_query(request); + else + { + /* + * If we havent already tried this, and we're looking up AAAA, try A + * now + */ + +#ifdef IPV6 + if (request->state == REQ_AAAA && request->type == T_AAAA) + { + request->timeout += 4; + resend_query(request); + } + else if (request->type == T_PTR && request->state != REQ_INT && + request->addr.ss.ss_family == AF_INET6) + { + request->state = REQ_INT; + request->timeout += 4; + resend_query(request); + } + else +#endif + { + /* + * If a bad error was returned, we stop here and dont send + * send any more (no retries granted). + */ + (*request->query.callback)(request->query.vptr, 0); + rem_request(request); + } + } + + return; + } + /* + * If this fails there was an error decoding the received packet, + * try it again and hope it works the next time. + */ + answer_count = proc_answer(request, header, buf, buf + rc); + + if (answer_count) + { + if (request->type == T_PTR) + { + if (request->name == NULL) + { + /* + * got a PTR response with no name, something bogus is happening + * don't bother trying again, the client address doesn't resolve + */ + (*request->query.callback)(request->query.vptr, reply); + rem_request(request); + return; + } + + /* + * Lookup the 'authoritative' name that we were given for the + * ip#. + * + */ +#ifdef IPV6 + if (request->addr.ss.ss_family == AF_INET6) + gethost_byname_type(request->name, &request->query, T_AAAA); + else +#endif + gethost_byname_type(request->name, &request->query, T_A); + rem_request(request); + } + else + { + /* + * got a name and address response, client resolved + */ + reply = make_dnsreply(request); + (*request->query.callback)(request->query.vptr, (reply) ? reply : 0); + rem_request(request); + } + } + else if (!request->sent) + { + /* XXX - we got a response for a query we didn't send with a valid id? + * this should never happen, bail here and leave the client unresolved + */ + assert(0); + + /* XXX don't leak it */ + rem_request(request); + } +} + +static struct DNSReply * +make_dnsreply(struct reslist *request) +{ + struct DNSReply *cp; + assert(request != 0); + + cp = (struct DNSReply *)MyMalloc(sizeof(struct DNSReply)); + + DupString(cp->h_name, request->name); + memcpy(&cp->addr, &request->addr, sizeof(cp->addr)); + return(cp); +} + +void +report_dns_servers(struct Client *source_p, struct StatDesc *sd, int stat, char *param) +{ + int i; + char ipaddr[128]; + + for (i = 0; i < irc_nscount; i++) + { + irc_getnameinfo((struct sockaddr *)&(irc_nsaddr_list[i]), + irc_nsaddr_list[i].ss_len, ipaddr, sizeof(ipaddr), NULL, 0, + NI_NUMERICHOST); + send_reply(source_p, RPL_STATSALINE, ipaddr); + } +} + +size_t +cres_mem(struct Client* sptr) +{ + struct dlink *dlink; + struct reslist *request; + size_t request_mem = 0; + int request_count = 0; + + for (dlink = request_list.next; dlink != &request_list; dlink = dlink->next) { + request = (struct reslist*)dlink; + request_mem += sizeof(*request); + if (request->name) + request_mem += strlen(request->name) + 1; + ++request_count; + } + + send_reply(sptr, SND_EXPLICIT | RPL_STATSDEBUG, + ":Resolver: requests %d(%d)", request_count, request_mem); + return request_mem; +} diff --git a/ircd/ircd_reslib.c b/ircd/ircd_reslib.c new file mode 100644 index 0000000..dac3b3e --- /dev/null +++ b/ircd/ircd_reslib.c @@ -0,0 +1,1174 @@ +/* + * Copyright (c) 1985, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Portions Copyright (c) 1993 by Digital Equipment Corporation. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies, and that + * the name of Digital Equipment Corporation not be used in advertising or + * publicity pertaining to distribution of the document or software without + * specific, written prior permission. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT + * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. + */ + +/* + * Portions Copyright (c) 1996-1999 by Internet Software Consortium. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS + * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE + * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. + */ + +/* Original copyright ISC as above. + * Code modified specifically for ircd use from the following orginal files + * in bind ... + * + * res_comp.c + * ns_name.c + * ns_netint.c + * res_init.c + * + * - Dianora + */ + +#include "ircd.h" +#include "res.h" +#include "ircd_reslib.h" +#include "ircd_defs.h" +#include "fileio.h" +#include "ircd_string.h" +#include "ircd_addrinfo.h" + +#include +#include +#include +#include +#include + +#define NS_TYPE_ELT 0x40 /* EDNS0 extended label type */ +#define DNS_LABELTYPE_BITSTRING 0x41 +#define MAXLINE 128 + +/* $Id$ */ + +struct irc_ssaddr irc_nsaddr_list[IRCD_MAXNS]; +int irc_nscount = 0; +char irc_domain[HOSTLEN + 1]; + +static const char digitvalue[256] = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*16*/ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*32*/ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*48*/ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, /*64*/ + -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*80*/ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*96*/ + -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*112*/ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*128*/ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /*256*/ +}; + +static int parse_resvconf(void); +static void add_nameserver(char *arg); + +static const char digits[] = "0123456789"; +static int labellen(const unsigned char *lp); +static int special(int ch); +static int printable(int ch); +static int irc_decode_bitstring(const char **cpp, char *dn, const char *eom); +static int irc_ns_name_compress(const char *src, unsigned char *dst, size_t dstsiz, + const unsigned char **dnptrs, const unsigned char **lastdnptr); +static int irc_dn_find(const unsigned char *, const unsigned char *, const unsigned char * const *, + const unsigned char * const *); +static int irc_encode_bitsring(const char **, const char *, unsigned char **, unsigned char **, + const char *); +static int mklower(int ch); + +int +irc_res_init(void) +{ + irc_nscount = 0; + return(parse_resvconf()); +} + +/* parse_resvconf() + * + * inputs - NONE + * output - -1 if failure 0 if success + * side effects - fills in irc_nsaddr_list + */ +static int +parse_resvconf(void) +{ + char *p; + char *opt; + char *arg; + char input[MAXLINE]; + FBFILE *file; + + /* XXX "/etc/resolv.conf" should be from a define in setup.h perhaps + * for cygwin support etc. this hardcodes it to unix for now -db + */ + if ((file = fbopen("/etc/resolv.conf", "r")) == NULL) + return(-1); + + while (fbgets(input, MAXLINE, file) != NULL) + { + /* blow away any newline */ + if ((p = strpbrk(input, "\r\n")) != NULL) + *p = '\0'; + + /* Ignore comment lines immediately */ + if (*input == '#') + continue; + + p = input; + /* skip until something thats not a space is seen */ + while (IsSpace(*p)) + p++; + /* if at this point, have a '\0' then continue */ + if (*p == '\0') + continue; + + /* skip until a space is found */ + opt = input; + while (!IsSpace(*p)) + if (*p++ == '\0') + continue; /* no arguments?.. ignore this line */ + /* blow away the space character */ + *p++ = '\0'; + + /* skip these spaces that are before the argument */ + while (IsSpace(*p)) + p++; + /* Now arg should be right where p is pointing */ + arg = p; + if ((p = strpbrk(arg, " \t")) != NULL) + *p = '\0'; /* take the first word */ + + if (strcasecmp(opt, "domain") == 0) + ircd_strncpy(irc_domain, arg, HOSTLEN); + else if (strcasecmp(opt, "nameserver") == 0) + add_nameserver(arg); + } + + fbclose(file); + return(0); +} + +/* add_nameserver() + * + * input - either an IPV4 address in dotted quad + * or an IPV6 address in : format + * output - NONE + * side effects - entry in irc_nsaddr_list is filled in as needed + */ +static void +add_nameserver(char *arg) +{ + struct addrinfo hints, *res; + /* Done max number of nameservers? */ + if ((irc_nscount + 1) >= IRCD_MAXNS) + return; + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = PF_UNSPEC; + hints.ai_socktype = SOCK_DGRAM; + hints.ai_flags = AI_PASSIVE | AI_NUMERICHOST; + + if (irc_getaddrinfo(arg, "domain", &hints, &res)) + return; + + if (res == NULL) + return; + + memcpy(&irc_nsaddr_list[irc_nscount].ss, res->ai_addr, res->ai_addrlen); + irc_nsaddr_list[irc_nscount].ss_len = res->ai_addrlen; + irc_nscount++; + irc_freeaddrinfo(res); +} + +/* + * Expand compressed domain name 'comp_dn' to full domain name. + * 'msg' is a pointer to the begining of the message, + * 'eomorig' points to the first location after the message, + * 'exp_dn' is a pointer to a buffer of size 'length' for the result. + * Return size of compressed name or -1 if there was an error. + */ +int +irc_dn_expand(const unsigned char *msg, const unsigned char *eom, + const unsigned char *src, char *dst, int dstsiz) +{ + int n = irc_ns_name_uncompress(msg, eom, src, dst, (size_t)dstsiz); + + if (n > 0 && dst[0] == '.') + dst[0] = '\0'; + return(n); +} + +/* + * irc_ns_name_uncompress(msg, eom, src, dst, dstsiz) + * Expand compressed domain name to presentation format. + * return: + * Number of bytes read out of `src', or -1 (with errno set). + * note: + * Root domain returns as "." not "". + */ +int +irc_ns_name_uncompress(const unsigned char *msg, const unsigned char *eom, + const unsigned char *src, char *dst, size_t dstsiz) +{ + unsigned char tmp[NS_MAXCDNAME]; + int n; + + if ((n = irc_ns_name_unpack(msg, eom, src, tmp, sizeof tmp)) == -1) + return(-1); + if (irc_ns_name_ntop(tmp, dst, dstsiz) == -1) + return(-1); + return(n); +} +/* + * irc_ns_name_unpack(msg, eom, src, dst, dstsiz) + * Unpack a domain name from a message, source may be compressed. + * return: + * -1 if it fails, or consumed octets if it succeeds. + */ +int +irc_ns_name_unpack(const unsigned char *msg, const unsigned char *eom, + const unsigned char *src, unsigned char *dst, + size_t dstsiz) +{ + const unsigned char *srcp, *dstlim; + unsigned char *dstp; + int n, len, checked, l; + + len = -1; + checked = 0; + dstp = dst; + srcp = src; + dstlim = dst + dstsiz; + if (srcp < msg || srcp >= eom) { + errno = EMSGSIZE; + return (-1); + } + /* Fetch next label in domain name. */ + while ((n = *srcp++) != 0) { + /* Check for indirection. */ + switch (n & NS_CMPRSFLGS) { + case 0: + case NS_TYPE_ELT: + /* Limit checks. */ + if ((l = labellen(srcp - 1)) < 0) { + errno = EMSGSIZE; + return(-1); + } + if (dstp + l + 1 >= dstlim || srcp + l >= eom) { + errno = EMSGSIZE; + return (-1); + } + checked += l + 1; + *dstp++ = n; + memcpy(dstp, srcp, l); + dstp += l; + srcp += l; + break; + + case NS_CMPRSFLGS: + if (srcp >= eom) { + errno = EMSGSIZE; + return (-1); + } + if (len < 0) + len = srcp - src + 1; + srcp = msg + (((n & 0x3f) << 8) | (*srcp & 0xff)); + if (srcp < msg || srcp >= eom) { /* Out of range. */ + errno = EMSGSIZE; + return (-1); + } + checked += 2; + /* + * Check for loops in the compressed name; + * if we've looked at the whole message, + * there must be a loop. + */ + if (checked >= eom - msg) { + errno = EMSGSIZE; + return (-1); + } + break; + + default: + errno = EMSGSIZE; + return (-1); /* flag error */ + } + } + *dstp = '\0'; + if (len < 0) + len = srcp - src; + return (len); +} + +/* + * irc_ns_name_ntop(src, dst, dstsiz) + * Convert an encoded domain name to printable ascii as per RFC1035. + * return: + * Number of bytes written to buffer, or -1 (with errno set) + * notes: + * The root is returned as "." + * All other domains are returned in non absolute form + */ +int +irc_ns_name_ntop(const char *src, char *dst, size_t dstsiz) +{ + const char *cp; + char *dn, *eom; + unsigned char c; + unsigned int n; + int l; + + cp = src; + dn = dst; + eom = dst + dstsiz; + + while ((n = *cp++) != 0) { + if ((n & NS_CMPRSFLGS) == NS_CMPRSFLGS) { + /* Some kind of compression pointer. */ + errno = EMSGSIZE; + return (-1); + } + if (dn != dst) { + if (dn >= eom) { + errno = EMSGSIZE; + return (-1); + } + *dn++ = '.'; + } + if ((l = labellen(cp - 1)) < 0) { + errno = EMSGSIZE; /* XXX */ + return(-1); + } + if (dn + l >= eom) { + errno = EMSGSIZE; + return (-1); + } + if ((n & NS_CMPRSFLGS) == NS_TYPE_ELT) { + int m; + + if (n != DNS_LABELTYPE_BITSTRING) { + /* XXX: labellen should reject this case */ + errno = EINVAL; + return(-1); + } + if ((m = irc_decode_bitstring(&cp, dn, eom)) < 0) + { + errno = EMSGSIZE; + return(-1); + } + dn += m; + continue; + } + for ((void)NULL; l > 0; l--) { + c = *cp++; + if (special(c)) { + if (dn + 1 >= eom) { + errno = EMSGSIZE; + return (-1); + } + *dn++ = '\\'; + *dn++ = (char)c; + } else if (!printable(c)) { + if (dn + 3 >= eom) { + errno = EMSGSIZE; + return (-1); + } + *dn++ = '\\'; + *dn++ = digits[c / 100]; + *dn++ = digits[(c % 100) / 10]; + *dn++ = digits[c % 10]; + } else { + if (dn >= eom) { + errno = EMSGSIZE; + return (-1); + } + *dn++ = (char)c; + } + } + } + if (dn == dst) { + if (dn >= eom) { + errno = EMSGSIZE; + return (-1); + } + *dn++ = '.'; + } + if (dn >= eom) { + errno = EMSGSIZE; + return (-1); + } + *dn++ = '\0'; + return (dn - dst); +} + +/* + * Pack domain name 'exp_dn' in presentation form into 'comp_dn'. + * Return the size of the compressed name or -1. + * 'length' is the size of the array pointed to by 'comp_dn'. + */ +int +irc_dn_comp(const char *src, unsigned char *dst, int dstsiz, + unsigned char **dnptrs, unsigned char **lastdnptr) +{ + return(irc_ns_name_compress(src, dst, (size_t)dstsiz, + (const unsigned char **)dnptrs, + (const unsigned char **)lastdnptr)); +} + +/* + * Skip over a compressed domain name. Return the size or -1. + */ +int +irc_dn_skipname(const unsigned char *ptr, const unsigned char *eom) { + const unsigned char *saveptr = ptr; + + if (irc_ns_name_skip(&ptr, eom) == -1) + return(-1); + return(ptr - saveptr); +} + +/* + * ns_name_skip(ptrptr, eom) + * Advance *ptrptr to skip over the compressed name it points at. + * return: + * 0 on success, -1 (with errno set) on failure. + */ +int +irc_ns_name_skip(const unsigned char **ptrptr, const unsigned char *eom) +{ + const unsigned char *cp; + unsigned int n; + int l; + + cp = *ptrptr; + + while (cp < eom && (n = *cp++) != 0) + { + /* Check for indirection. */ + switch (n & NS_CMPRSFLGS) + { + case 0: /* normal case, n == len */ + cp += n; + continue; + case NS_TYPE_ELT: /* EDNS0 extended label */ + if ((l = labellen(cp - 1)) < 0) + { + errno = EMSGSIZE; /* XXX */ + return(-1); + } + + cp += l; + continue; + case NS_CMPRSFLGS: /* indirection */ + cp++; + break; + default: /* illegal type */ + errno = EMSGSIZE; + return(-1); + } + + break; + } + + if (cp > eom) + { + errno = EMSGSIZE; + return (-1); + } + + *ptrptr = cp; + return(0); +} + +unsigned int +irc_ns_get16(const unsigned char *src) +{ + unsigned int dst; + + IRC_NS_GET16(dst, src); + return(dst); +} + +unsigned long +irc_ns_get32(const unsigned char *src) +{ + unsigned long dst; + + IRC_NS_GET32(dst, src); + return(dst); +} + +void +irc_ns_put16(unsigned int src, unsigned char *dst) +{ + IRC_NS_PUT16(src, dst); +} + +void +irc_ns_put32(unsigned long src, unsigned char *dst) +{ + IRC_NS_PUT32(src, dst); +} + +/* From ns_name.c */ + +/* + * special(ch) + * Thinking in noninternationalized USASCII (per the DNS spec), + * is this characted special ("in need of quoting") ? + * return: + * boolean. + */ +static int +special(int ch) +{ + switch (ch) + { + case 0x22: /* '"' */ + case 0x2E: /* '.' */ + case 0x3B: /* ';' */ + case 0x5C: /* '\\' */ + case 0x28: /* '(' */ + case 0x29: /* ')' */ + /* Special modifiers in zone files. */ + case 0x40: /* '@' */ + case 0x24: /* '$' */ + return(1); + default: + return(0); + } +} + +static int +labellen(const unsigned char *lp) +{ + int bitlen; + unsigned char l = *lp; + + if ((l & NS_CMPRSFLGS) == NS_CMPRSFLGS) + { + /* should be avoided by the caller */ + return(-1); + } + + if ((l & NS_CMPRSFLGS) == NS_TYPE_ELT) + { + if (l == DNS_LABELTYPE_BITSTRING) + { + if ((bitlen = *(lp + 1)) == 0) + bitlen = 256; + return((bitlen + 7 ) / 8 + 1); + } + + return(-1); /* unknwon ELT */ + } + + return(l); +} + + +/* + * printable(ch) + * Thinking in noninternationalized USASCII (per the DNS spec), + * is this character visible and not a space when printed ? + * return: + * boolean. + */ +static int +printable(int ch) +{ + return(ch > 0x20 && ch < 0x7f); +} + +static int +irc_decode_bitstring(const char **cpp, char *dn, const char *eom) +{ + const char *cp = *cpp; + char *beg = dn, tc; + int b, blen, plen; + + if ((blen = (*cp & 0xff)) == 0) + blen = 256; + plen = (blen + 3) / 4; + plen += sizeof("\\[x/]") + (blen > 99 ? 3 : (blen > 9) ? 2 : 1); + if (dn + plen >= eom) + return(-1); + + cp++; + dn += sprintf(dn, "\\[x"); + for (b = blen; b > 7; b -= 8, cp++) + dn += sprintf(dn, "%02x", *cp & 0xff); + if (b > 4) { + tc = *cp++; + dn += sprintf(dn, "%02x", tc & (0xff << (8 - b))); + } else if (b > 0) { + tc = *cp++; + dn += sprintf(dn, "%1x", + ((tc >> 4) & 0x0f) & (0x0f << (4 - b))); + } + dn += sprintf(dn, "/%d]", blen); + + *cpp = cp; + return(dn - beg); +} + +/* + * irc_ns_name_pton(src, dst, dstsiz) + * Convert a ascii string into an encoded domain name as per RFC1035. + * return: + * -1 if it fails + * 1 if string was fully qualified + * 0 is string was not fully qualified + * notes: + * Enforces label and domain length limits. + */ +int +irc_ns_name_pton(const char *src, unsigned char *dst, size_t dstsiz) +{ + unsigned char *label, *bp, *eom; + char *cp; + int c, n, escaped, e = 0; + + escaped = 0; + bp = dst; + eom = dst + dstsiz; + label = bp++; + + + while ((c = *src++) != 0) { + if (escaped) { + if (c == '[') { /* start a bit string label */ + if ((cp = strchr(src, ']')) == NULL) { + errno = EINVAL; /* ??? */ + return(-1); + } + if ((e = irc_encode_bitsring(&src, + cp + 2, + &label, + &bp, + (const char *)eom)) + != 0) { + errno = e; + return(-1); + } + escaped = 0; + label = bp++; + if ((c = *src++) == 0) + goto done; + else if (c != '.') { + errno = EINVAL; + return(-1); + } + continue; + } + else if ((cp = strchr(digits, c)) != NULL) { + n = (cp - digits) * 100; + if ((c = *src++) == 0 || + (cp = strchr(digits, c)) == NULL) { + errno = EMSGSIZE; + return (-1); + } + n += (cp - digits) * 10; + if ((c = *src++) == 0 || + (cp = strchr(digits, c)) == NULL) { + errno = EMSGSIZE; + return (-1); + } + n += (cp - digits); + if (n > 255) { + errno = EMSGSIZE; + return (-1); + } + c = n; + } + escaped = 0; + } else if (c == '\\') { + escaped = 1; + continue; + } else if (c == '.') { + c = (bp - label - 1); + if ((c & NS_CMPRSFLGS) != 0) { /* Label too big. */ + errno = EMSGSIZE; + return (-1); + } + if (label >= eom) { + errno = EMSGSIZE; + return (-1); + } + *label = c; + /* Fully qualified ? */ + if (*src == '\0') { + if (c != 0) { + if (bp >= eom) { + errno = EMSGSIZE; + return (-1); + } + *bp++ = '\0'; + } + if ((bp - dst) > NS_MAXCDNAME) { + errno = EMSGSIZE; + return (-1); + } + return (1); + } + if (c == 0 || *src == '.') { + errno = EMSGSIZE; + return (-1); + } + label = bp++; + continue; + } + if (bp >= eom) { + errno = EMSGSIZE; + return (-1); + } + *bp++ = (unsigned char)c; + } + c = (bp - label - 1); + if ((c & NS_CMPRSFLGS) != 0) { /* Label too big. */ + errno = EMSGSIZE; + return (-1); + } + done: + if (label >= eom) { + errno = EMSGSIZE; + return (-1); + } + *label = c; + if (c != 0) { + if (bp >= eom) { + errno = EMSGSIZE; + return (-1); + } + *bp++ = 0; + } + + if ((bp - dst) > NS_MAXCDNAME) + { /* src too big */ + errno = EMSGSIZE; + return (-1); + } + + return (0); +} + +/* + * irc_ns_name_pack(src, dst, dstsiz, dnptrs, lastdnptr) + * Pack domain name 'domain' into 'comp_dn'. + * return: + * Size of the compressed name, or -1. + * notes: + * 'dnptrs' is an array of pointers to previous compressed names. + * dnptrs[0] is a pointer to the beginning of the message. The array + * ends with NULL. + * 'lastdnptr' is a pointer to the end of the array pointed to + * by 'dnptrs'. + * Side effects: + * The list of pointers in dnptrs is updated for labels inserted into + * the message as we compress the name. If 'dnptr' is NULL, we don't + * try to compress names. If 'lastdnptr' is NULL, we don't update the + * list. + */ +int +irc_ns_name_pack(const unsigned char *src, unsigned char *dst, int dstsiz, + const unsigned char **dnptrs, const unsigned char **lastdnptr) +{ + unsigned char *dstp; + const unsigned char **cpp, **lpp, *eob, *msg; + const unsigned char *srcp; + int n, l, first = 1; + + srcp = src; + dstp = dst; + eob = dstp + dstsiz; + lpp = cpp = NULL; + if (dnptrs != NULL) { + if ((msg = *dnptrs++) != NULL) { + for (cpp = dnptrs; *cpp != NULL; cpp++) + (void)NULL; + lpp = cpp; /* end of list to search */ + } + } else + msg = NULL; + + /* make sure the domain we are about to add is legal */ + l = 0; + do { + int l0; + + n = *srcp; + if ((n & NS_CMPRSFLGS) == NS_CMPRSFLGS) { + errno = EMSGSIZE; + return (-1); + } + if ((l0 = labellen(srcp)) < 0) { + errno = EINVAL; + return(-1); + } + l += l0 + 1; + if (l > NS_MAXCDNAME) { + errno = EMSGSIZE; + return (-1); + } + srcp += l0 + 1; + } while (n != 0); + + /* from here on we need to reset compression pointer array on error */ + srcp = src; + do { + /* Look to see if we can use pointers. */ + n = *srcp; + if (n != 0 && msg != NULL) { + l = irc_dn_find(srcp, msg, (const unsigned char * const *)dnptrs, + (const unsigned char * const *)lpp); + if (l >= 0) { + if (dstp + 1 >= eob) { + goto cleanup; + } + *dstp++ = (l >> 8) | NS_CMPRSFLGS; + *dstp++ = l % 256; + return (dstp - dst); + } + /* Not found, save it. */ + if (lastdnptr != NULL && cpp < lastdnptr - 1 && + (dstp - msg) < 0x4000 && first) { + *cpp++ = dstp; + *cpp = NULL; + first = 0; + } + } + /* copy label to buffer */ + if ((n & NS_CMPRSFLGS) == NS_CMPRSFLGS) { + /* Should not happen. */ + goto cleanup; + } + n = labellen(srcp); + if (dstp + 1 + n >= eob) { + goto cleanup; + } + memcpy(dstp, srcp, n + 1); + srcp += n + 1; + dstp += n + 1; + } while (n != 0); + + if (dstp > eob) { +cleanup: + if (msg != NULL) + *lpp = NULL; + errno = EMSGSIZE; + return (-1); + } + return(dstp - dst); +} + +static int +irc_ns_name_compress(const char *src, unsigned char *dst, size_t dstsiz, + const unsigned char **dnptrs, const unsigned char **lastdnptr) +{ + unsigned char tmp[NS_MAXCDNAME]; + + if (irc_ns_name_pton(src, tmp, sizeof tmp) == -1) + return(-1); + return(irc_ns_name_pack(tmp, dst, dstsiz, dnptrs, lastdnptr)); +} + +static int +irc_encode_bitsring(const char **bp, const char *end, unsigned char **labelp, + unsigned char **dst, const char *eom) +{ + int afterslash = 0; + const char *cp = *bp; + char *tp, c; + const char *beg_blen; + char *end_blen = NULL; + int value = 0, count = 0, tbcount = 0, blen = 0; + + beg_blen = end_blen = NULL; + + /* a bitstring must contain at least 2 characters */ + if (end - cp < 2) + return(EINVAL); + + /* XXX: currently, only hex strings are supported */ + if (*cp++ != 'x') + return(EINVAL); + if (!isxdigit((*cp) & 0xff)) /* reject '\[x/BLEN]' */ + return(EINVAL); + + for (tp = *dst + 1; cp < end && tp < eom; cp++) { + switch((c = *cp)) { + case ']': /* end of the bitstring */ + if (afterslash) { + if (beg_blen == NULL) + return(EINVAL); + blen = (int)strtol(beg_blen, &end_blen, 10); + if (*end_blen != ']') + return(EINVAL); + } + if (count) + *tp++ = ((value << 4) & 0xff); + cp++; /* skip ']' */ + goto done; + case '/': + afterslash = 1; + break; + default: + if (afterslash) { + if (!isdigit(c&0xff)) + return(EINVAL); + if (beg_blen == NULL) { + + if (c == '0') { + /* blen never begings with 0 */ + return(EINVAL); + } + beg_blen = cp; + } + } else { + if (!isxdigit(c&0xff)) + return(EINVAL); + value <<= 4; + value += digitvalue[(int)c]; + count += 4; + tbcount += 4; + if (tbcount > 256) + return(EINVAL); + if (count == 8) { + *tp++ = value; + count = 0; + } + } + break; + } + } + done: + if (cp >= end || tp >= eom) + return(EMSGSIZE); + + /* + * bit length validation: + * If a is present, the number of digits in the + * MUST be just sufficient to contain the number of bits specified + * by the . If there are insignificant bits in a final + * hexadecimal or octal digit, they MUST be zero. + * RFC 2673, Section 3.2. + */ + if (blen > 0) { + int traillen; + + if (((blen + 3) & ~3) != tbcount) + return(EINVAL); + traillen = tbcount - blen; /* between 0 and 3 */ + if (((value << (8 - traillen)) & 0xff) != 0) + return(EINVAL); + } + else + blen = tbcount; + if (blen == 256) + blen = 0; + + /* encode the type and the significant bit fields */ + **labelp = DNS_LABELTYPE_BITSTRING; + **dst = blen; + + *bp = cp; + *dst = tp; + + return(0); +} + +/* + * dn_find(domain, msg, dnptrs, lastdnptr) + * Search for the counted-label name in an array of compressed names. + * return: + * offset from msg if found, or -1. + * notes: + * dnptrs is the pointer to the first name on the list, + * not the pointer to the start of the message. + */ +static int +irc_dn_find(const unsigned char *domain, const unsigned char *msg, + const unsigned char * const *dnptrs, + const unsigned char * const *lastdnptr) +{ + const unsigned char *dn, *cp, *sp; + const unsigned char * const *cpp; + unsigned int n; + + for (cpp = dnptrs; cpp < lastdnptr; cpp++) + { + sp = *cpp; + /* + * terminate search on: + * root label + * compression pointer + * unusable offset + */ + while (*sp != 0 && (*sp & NS_CMPRSFLGS) == 0 && + (sp - msg) < 0x4000) { + dn = domain; + cp = sp; + while ((n = *cp++) != 0) { + /* + * check for indirection + */ + switch (n & NS_CMPRSFLGS) { + case 0: /* normal case, n == len */ + n = labellen(cp - 1); /* XXX */ + + if (n != *dn++) + goto next; + + for ((void)NULL; n > 0; n--) + if (mklower(*dn++) != + mklower(*cp++)) + goto next; + /* Is next root for both ? */ + if (*dn == '\0' && *cp == '\0') + return (sp - msg); + if (*dn) + continue; + goto next; + case NS_CMPRSFLGS: /* indirection */ + cp = msg + (((n & 0x3f) << 8) | *cp); + break; + + default: /* illegal type */ + errno = EMSGSIZE; + return (-1); + } + } + next: ; + sp += *sp + 1; + } + } + errno = ENOENT; + return (-1); +} + +/* + * Thinking in noninternationalized USASCII (per the DNS spec), + * convert this character to lower case if it's upper case. + */ +static int +mklower(int ch) +{ + if (ch >= 0x41 && ch <= 0x5A) + return(ch + 0x20); + + return(ch); +} + +/* From resolv/mkquery.c */ + +/* + * Form all types of queries. + * Returns the size of the result or -1. + */ +int +irc_res_mkquery( + const char *dname, /* domain name */ + int class, int type, /* class and type of query */ + unsigned char *buf, /* buffer to put query */ + int buflen) /* size of buffer */ +{ + HEADER *hp; + unsigned char *cp; + int n; + unsigned char *dnptrs[20], **dpp, **lastdnptr; + + /* + * Initialize header fields. + */ + if ((buf == NULL) || (buflen < HFIXEDSZ)) + return (-1); + memset(buf, 0, HFIXEDSZ); + hp = (HEADER *) buf; + + hp->id = 0; + hp->opcode = QUERY; + hp->rd = 1; /* recurse */ + hp->rcode = NO_ERRORS; + cp = buf + HFIXEDSZ; + buflen -= HFIXEDSZ; + dpp = dnptrs; + *dpp++ = buf; + *dpp++ = NULL; + lastdnptr = dnptrs + sizeof dnptrs / sizeof dnptrs[0]; + + if ((buflen -= QFIXEDSZ) < 0) + return (-1); + if ((n = irc_dn_comp(dname, cp, buflen, dnptrs, lastdnptr)) < 0) + return (-1); + + cp += n; + buflen -= n; + IRC_NS_PUT16(type, cp); + IRC_NS_PUT16(class, cp); + hp->qdcount = htons(1); + + return (cp - buf); +} diff --git a/ircd/parse.c b/ircd/parse.c index ea7c836..d2343d7 100644 --- a/ircd/parse.c +++ b/ircd/parse.c @@ -539,13 +539,6 @@ struct Message msgtab[] = { /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_unregistered, m_hash, m_hash, m_hash, m_ignore } }, - { - MSG_DNS, - TOK_DNS, - 0, MAXPARA, MFLG_SLOW, 0, NULL, - /* UNREG, CLIENT, SERVER, OPER, SERVICE */ - { m_unregistered, m_ignore, m_ignore, m_dns, m_ignore } - }, { MSG_REHASH, TOK_REHASH, diff --git a/ircd/s_auth.c b/ircd/s_auth.c index 935d700..6428692 100644 --- a/ircd/s_auth.c +++ b/ircd/s_auth.c @@ -335,7 +335,7 @@ static void auth_kill_client(struct AuthRequest* auth) * set the client on it's way to a connection completion, regardless * of success of failure */ -static void auth_dns_callback(void* vptr, struct hostent* hp) +static void auth_dns_callback(void* vptr, struct DNSReply* hp) { struct AuthRequest* auth = (struct AuthRequest*) vptr; assert(auth); @@ -347,22 +347,17 @@ static void auth_dns_callback(void* vptr, struct hostent* hp) ClearDNSPending(auth); if (hp) { - int i; + struct sockaddr_in *sin = (struct sockaddr_in*)&hp->addr; /* * Verify that the host to ip mapping is correct both ways and that * the ip#(s) for the socket is listed for the host. */ - for (i = 0; hp->h_addr_list[i]; ++i) { - if (0 == memcmp(hp->h_addr_list[i], &(cli_ip(auth->client)), - sizeof(struct in_addr))) - break; - } - if (!hp->h_addr_list[i]) { + if (memcmp(&sin->sin_addr, &cli_ip(auth->client), sizeof(struct in_addr))) { if (IsUserPort(auth->client)) sendheader(auth->client, REPORT_IP_MISMATCH); sendto_opmask_butone(0, SNO_IPMISMATCH, "IP# Mismatch: %s != %s[%s]", cli_sock_ip(auth->client), hp->h_name, - ircd_ntoa(hp->h_addr_list[0])); + ircd_ntoa((const char*)&sin->sin_addr)); if (feature_bool(FEAT_KILL_IPMISMATCH)) { auth_kill_client(auth); return; @@ -614,7 +609,7 @@ void start_auth(struct Client* client) if (IsUserPort(auth->client)) sendheader(client, REPORT_DO_DNS); - gethost_byaddr((const char*) &(cli_ip(client)), &query); + gethost_byinaddr(&(cli_ip(client)), &query); SetDNSPending(auth); } } diff --git a/ircd/s_bsd.c b/ircd/s_bsd.c index 27d6690..0d460ba 100644 --- a/ircd/s_bsd.c +++ b/ircd/s_bsd.c @@ -58,12 +58,10 @@ #include "version.h" #include -#include #include #include #include #include -#include #include #include #include @@ -177,13 +175,14 @@ void report_error(const char* text, const char* who, int err) * a non-null pointer, otherwise reply will be null. * if successful start the connection, otherwise notify opers */ -static void connect_dns_callback(void* vptr, struct hostent* hp) +static void connect_dns_callback(void* vptr, struct DNSReply* hp) { struct ConfItem* aconf = (struct ConfItem*) vptr; assert(aconf); aconf->dns_pending = 0; if (hp) { - memcpy(&aconf->ipnum, hp->h_addr, sizeof(struct in_addr)); + struct sockaddr_in *sin = (struct sockaddr_in*)&hp->addr; + memcpy(&aconf->ipnum, &sin->sin_addr, sizeof(struct in_addr)); MyFree(hp); connect_server(aconf, 0); } @@ -396,6 +395,7 @@ void release_dns_reply(struct Client* cptr) assert(MyConnect(cptr)); if (cli_dns_reply(cptr)) { + MyFree(cli_dns_reply(cptr)->h_name); MyFree(cli_dns_reply(cptr)); cli_dns_reply(cptr) = 0; } diff --git a/ircd/s_conf.c b/ircd/s_conf.c index f386b98..8d6d6b2 100644 --- a/ircd/s_conf.c +++ b/ircd/s_conf.c @@ -204,13 +204,14 @@ static void detach_conf(struct Client* cptr, struct ConfItem* aconf) * a non-null pointer, otherwise hp will be null. * if successful save hp in the conf item it was called with */ -static void conf_dns_callback(void* vptr, struct hostent* hp) +static void conf_dns_callback(void* vptr, struct DNSReply* hp) { struct ConfItem* aconf = (struct ConfItem*) vptr; assert(aconf); aconf->dns_pending = 0; if (hp) { - memcpy(&aconf->ipnum, hp->h_addr, sizeof(struct in_addr)); + struct sockaddr_in *sin = (struct sockaddr_in*)&hp->addr; + memcpy(&aconf->ipnum, &sin->sin_addr, sizeof(struct in_addr)); MyFree(hp); } } @@ -367,11 +368,9 @@ check_limit_and_attach(struct Client* cptr, struct ConfItem* aconf) enum AuthorizationCheckResult attach_iline(struct Client* cptr) { struct ConfItem* aconf; - const char* hname; - int i; static char uhost[HOSTLEN + USERLEN + 3]; static char fullname[HOSTLEN + 1]; - struct hostent* hp = 0; + struct DNSReply* hp = 0; assert(0 != cptr); @@ -386,25 +385,23 @@ enum AuthorizationCheckResult attach_iline(struct Client* cptr) if (!aconf->host || !aconf->name) continue; if (hp) { - for (i = 0, hname = hp->h_name; hname; hname = hp->h_aliases[i++]) { - ircd_strncpy(fullname, hname, HOSTLEN); - fullname[HOSTLEN] = '\0'; - - Debug((DEBUG_DNS, "a_il: %s->%s", cli_sockhost(cptr), fullname)); - - if (strchr(aconf->name, '@')) { - strcpy(uhost, cli_username(cptr)); - strcat(uhost, "@"); - } - else - *uhost = '\0'; - strncat(uhost, fullname, sizeof(uhost) - 1 - strlen(uhost)); - uhost[sizeof(uhost) - 1] = 0; - if (0 == match(aconf->name, uhost)) { - if (strchr(uhost, '@')) - SetFlag(cptr, FLAG_DOID); - return check_limit_and_attach(cptr, aconf); - } + ircd_strncpy(fullname, hp->h_name, HOSTLEN); + fullname[HOSTLEN] = '\0'; + + Debug((DEBUG_DNS, "a_il: %s->%s", cli_sockhost(cptr), fullname)); + + if (strchr(aconf->name, '@')) { + strcpy(uhost, cli_username(cptr)); + strcat(uhost, "@"); + } + else + *uhost = '\0'; + strncat(uhost, fullname, sizeof(uhost) - 1 - strlen(uhost)); + uhost[sizeof(uhost) - 1] = 0; + if (0 == match(aconf->name, uhost)) { + if (strchr(uhost, '@')) + SetFlag(cptr, FLAG_DOID); + return check_limit_and_attach(cptr, aconf); } } if (strchr(aconf->host, '@')) { @@ -1294,25 +1291,16 @@ int conf_check_server(struct Client *cptr) if (!c_conf) { if (cli_dns_reply(cptr)) { - int i; - struct hostent* hp = cli_dns_reply(cptr); + struct DNSReply* hp = cli_dns_reply(cptr); const char* name = hp->h_name; /* * If we are missing a C or N line from above, search for * it under all known hostnames we have for this ip#. */ - for (i = 0; name; name = hp->h_aliases[i++]) { - if ((c_conf = find_conf_byhost(lp, name, CONF_SERVER))) { - ircd_strncpy(cli_sockhost(cptr), name, HOSTLEN); - break; - } - } - if (!c_conf) { - for (i = 0; hp->h_addr_list[i]; i++) { - if ((c_conf = find_conf_byip(lp, hp->h_addr_list[i], CONF_SERVER))) - break; - } - } + if ((c_conf = find_conf_byhost(lp, hp->h_name, CONF_SERVER))) + ircd_strncpy(cli_sockhost(cptr), name, HOSTLEN); + else + c_conf = find_conf_byip(lp, (char*)&((struct sockaddr_in*)&hp->addr)->sin_addr, CONF_SERVER); } else { /* diff --git a/ircd/s_err.c b/ircd/s_err.c index 5642402..79da7b9 100644 --- a/ircd/s_err.c +++ b/ircd/s_err.c @@ -484,7 +484,7 @@ static Numeric replyTable[] = { /* 225 */ { 0 }, /* 226 */ - { 0 }, + { RPL_STATSALINE, "%s", "226" }, /* 227 */ { 0 }, /* 228 */ diff --git a/ircd/s_stats.c b/ircd/s_stats.c index 56c224b..75d3b8a 100644 --- a/ircd/s_stats.c +++ b/ircd/s_stats.c @@ -42,6 +42,7 @@ #include "msgq.h" #include "numeric.h" #include "numnicks.h" +#include "res.h" #include "s_bsd.h" #include "s_conf.h" #include "s_debug.h" @@ -454,6 +455,9 @@ stats_help(struct Client* to, struct StatDesc* sd, int stat, char* param) * stats. Struct StatDesc is defined in s_stats.h. */ struct StatDesc statsinfo[] = { + { 'a', STAT_FLAG_OPERFEAT, FEAT_HIS_STATS_a, + report_dns_servers, 0, + "DNS servers." }, { 'c', STAT_FLAG_OPERFEAT, FEAT_HIS_STATS_c, stats_configured_links, CONF_SERVER, "Remote server connection lines." },