Add DNS resolver glue layer. Use it
authorTom Yu <tlyu@mit.edu>
Tue, 21 Sep 2004 18:06:56 +0000 (18:06 +0000)
committerTom Yu <tlyu@mit.edu>
Tue, 21 Sep 2004 18:06:56 +0000 (18:06 +0000)
ticket: 2710

git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@16769 dc483132-0cff-0310-8789-dd5450dbe970

src/ChangeLog
src/aclocal.m4
src/include/ChangeLog
src/include/configure.in
src/lib/krb5/os/ChangeLog
src/lib/krb5/os/Makefile.in
src/lib/krb5/os/dnsglue.c [new file with mode: 0644]
src/lib/krb5/os/dnsglue.h [new file with mode: 0644]
src/lib/krb5/os/dnssrv.c
src/lib/krb5/os/hst_realm.c

index 82ae263cc909d59b599be1a48c39ed848f7741f8..54a49cb659a41e88116030d7ae44d60de8a6e784 100644 (file)
@@ -1,3 +1,14 @@
+2004-09-21  Tom Yu  <tlyu@mit.edu>
+
+       * aclocal.m4 (KRB5_AC_NEED_BIND_8_COMPAT): Remove.
+       (AC_LIBRARY_NET): Remove KRB5_AC_NEED_BIND_8_COMPAT.  Call
+       _KRB5_AC_CHECK_RES_FUNCS to check declarations and linkability of
+       vairous resolver functions.  Explicitly check linkability of
+       res_search() in case it's not explicitly declared.
+       (_KRB5_AC_CHECK_RES_FUNCS, _KRB5_AC_CHECK_RES_FUNC): New
+       functions.  Check resolver library function prototypes and
+       linkability.
+
 2004-09-17  Tom Yu  <tlyu@mit.edu>
 
        * Makefile.in (install-unix): Use $(INSTALL_SCRIPT) for scripts.
index 04e0f8b2108fc5fd270f926221634c84118461c2..c3acf0b5551d163f20fe2f1e0a9690193410f0b9 100644 (file)
@@ -1335,7 +1335,6 @@ dnl The check for libresolv is in case you are attempting to link statically
 dnl and happen to have a libresolv.a lying around (and no libnsl.a).
 dnl
 AC_DEFUN(AC_LIBRARY_NET, [
-AC_REQUIRE([KRB5_AC_NEED_BIND_8_COMPAT])
    # Most operating systems have gethostbyname() in the default searched
    # libraries (i.e. libc):
    AC_CHECK_FUNC(gethostbyname, , [
@@ -1361,36 +1360,44 @@ AC_REQUIRE([KRB5_AC_NEED_BIND_8_COMPAT])
     # We assume that if libresolv exists we can link against it.
     # This may get us a gethostby* that doesn't respect nsswitch.
     AC_CHECK_LIB(resolv, main)
-    krb5_res_search_found=no
-    AC_CHECK_DECL(res_nsearch,
-      [AC_DEFINE(HAVE_RES_NSEARCH, 1, [Have the res_nsearch function])
-krb5_res_search_found=yes], ,
-[[#include <sys/types.h>
-#include <netinet/in.h>
-#include <arpa/nameser.h>
-#include <resolv.h>
-]])
-    if test $krb5_res_search_found != yes; then
-      AC_CHECK_DECL(res_search,
-       [AC_DEFINE(HAVE_RES_SEARCH, 1, [Have the res_search function])
-krb5_res_search_found=yes], ,
-[[#include <sys/types.h>
-#include <netinet/in.h>
-#include <arpa/nameser.h>
-#include <resolv.h>
-]])
+_KRB5_AC_CHECK_RES_FUNCS(res_nsearch res_search ns_name_uncompress dn_skipname)
+    if test $krb5_cv_func_res_nsearch = no \
+      && test $krb5_cv_func_res_search = no; then
+       # Attempt to link with res_search(), in case it's not prototyped.
+       AC_CHECK_FUNC(res_search,
+         [AC_DEFINE(HAVE_RES_SEARCH, 1,
+           [Define to 1 if you have the `res_search' function])],
+         [AC_ERROR([cannot find res_nsearch or res_search])])
     fi
-    if test $krb5_res_search_found != yes; then
-      AC_CHECK_FUNC(res_search,
-       [AC_DEFINE(HAVE_RES_SEARCH, 1, [Have the res_search function])],
-       [AC_MSG_ERROR(Failed to find resolver search routine)],
-[[#include <sys/types.h>
+  fi
+])
+AC_DEFUN([_KRB5_AC_CHECK_RES_FUNCS],
+[AC_FOREACH([AC_Func], [$1],
+  [AH_TEMPLATE(AS_TR_CPP(HAVE_[]AC_Func),
+               [Define to 1 if you have the `]AC_Func[' function.])])dnl
+for krb5_func in $1; do
+_KRB5_AC_CHECK_RES_FUNC($krb5_func)
+done
+])
+AC_DEFUN([_KRB5_AC_CHECK_RES_FUNC], [
+# Solaris 9 prototypes ns_name_uncompress() in arpa/nameser.h, but
+# doesn't export it from libresolv.so, so we use extreme paranoia here
+# and check both for the declaration and that we can link against the
+# function.
+AC_CACHE_CHECK([for $1], [krb5_cv_func_$1], [AC_TRY_LINK(
+[#include <sys/types.h>
 #include <netinet/in.h>
 #include <arpa/nameser.h>
-#include <resolv.h>
-]])
-    fi
-  fi
+@%:@include <resolv.h>],
+[/*
+ * Use volatile, or else optimization can cause false positives.
+ */
+void (* volatile p)() = (void (*)())$1;],
+                            [AS_VAR_SET(krb5_cv_func_$1, yes)],
+                            [AS_VAR_SET(krb5_cv_func_$1, no)])])
+AS_IF([test AS_VAR_GET(krb5_cv_func_$1) = yes],
+      [AC_DEFINE_UNQUOTED(AS_TR_CPP([HAVE_$1]), 1,
+                         [Define to 1 if you have the `$1' function])])[]dnl
 ])
 dnl
 dnl
@@ -1606,26 +1613,6 @@ AC_SUBST(DB_LIB)
 AC_SUBST(KDB5_DB_LIB)
 ])
 dnl
-dnl
-dnl KRB5_AC_NEED_BIND_8_COMPAT --- check to see if we are on a bind 9 system
-dnl
-dnl
-AC_DEFUN(KRB5_AC_NEED_BIND_8_COMPAT,[
-AC_REQUIRE([AC_PROG_CC])dnl
-dnl
-dnl On a bind 9 system, we need to define BIND_8_COMPAT
-dnl
-AC_MSG_CHECKING(for bind 9 or higher)
-AC_CACHE_VAL(krb5_cv_need_bind_8_compat,[
-AC_TRY_COMPILE([#include <arpa/nameser.h>], [HEADER hdr;],
-krb5_cv_need_bind_8_compat=no, 
-[AC_TRY_COMPILE([#define BIND_8_COMPAT
-#include <arpa/nameser.h>], [HEADER hdr;],
-krb5_cv_need_bind_8_compat=yes, krb5_cv_need_bind_8_compat=no)])])
-AC_MSG_RESULT($krb5_cv_need_bind_8_compat)
-test $krb5_cv_need_bind_8_compat = yes && AC_DEFINE(BIND_8_COMPAT,1,[Define if OS has bind 9])
-])
-dnl
 dnl KRB5_AC_PRIOCNTL_HACK
 dnl
 dnl
index f87b1864cce7d9ea902e903ed7149c48949bb651..f21974d184c7f3e7eb1118431889202ac5cb21d6 100644 (file)
@@ -1,3 +1,7 @@
+2004-09-21  Tom Yu  <tlyu@mit.edu>
+
+       * configure.in: Remove KRB5_AC_NEED_BIND_8_COMPAT.
+
 2004-09-15  Tom Yu  <tlyu@mit.edu>
 
        * configure.in: Check for h_errno declaration in netdb.h.
index 277f206acc21fc2e14c1d3c87291cd8743857414..5c4181092922a20f87c49fdd80fe39ade77b820d 100644 (file)
@@ -255,9 +255,6 @@ if test $krb5_cv_header_netdb_h_h_errno = yes; then
 fi
 dnl
 dnl
-KRB5_AC_NEED_BIND_8_COMPAT
-dnl
-dnl
 AC_ARG_ENABLE([athena],
 [  --enable-athena         build with MIT Project Athena configuration],
 AC_DEFINE(KRB5_ATHENA_COMPAT,1,[Define if MIT Project Athena default configuration should be used]),)
index 2666431b06e9681de48baf358883b229d0c39945..903bf58fcded36fd37def305b4eb5abfbfc96f48 100644 (file)
@@ -1,3 +1,19 @@
+2004-09-20  Tom Yu  <tlyu@mit.edu>
+
+       * Makefile.in (STLIBOBJS, OBJS, SRCS): Add dnsglue.c.
+
+       * dnsglue.c: New file.  Implement resolver glue layer to abstract
+       away the details of calling res_search or res_nsearch, and of
+       parsing the reply packet.
+
+       * dnsglue.h: New file.
+
+       * dnssrv.c (krb5int_make_srv_query_realm): Use dnsglue.  Use
+       MAXDNAME from dnsglue.h or resolv.h instead of MAX_DNS_NAMELEN.
+
+       * hst_realm.c (krb5_try_realm_txt_rr): Use dnsglue.  Use MAXDNAME
+       from dnsglue.h or resolv.h instead of MAX_DNS_NAMELEN.
+
 2004-09-13  Tom Yu  <tlyu@mit.edu>
 
        * dnssrv.c: 
index bcf67749585bcf95d3ac53df21a088d8e4a410bf..8db0b7532dc63d931aa5cfe890a63748a1ed200b 100644 (file)
@@ -17,6 +17,7 @@ STLIBOBJS= \
        def_realm.o     \
        ccdefname.o     \
        changepw.o      \
+       dnsglue.o       \
        dnssrv.o        \
        free_krbhs.o    \
        free_hstrl.o    \
@@ -61,6 +62,7 @@ OBJS= \
        $(OUTPRE)def_realm.$(OBJEXT)    \
        $(OUTPRE)ccdefname.$(OBJEXT)    \
        $(OUTPRE)changepw.$(OBJEXT)     \
+       $(OUTPRE)dnsglue.$(OBJEXT)      \
        $(OUTPRE)dnssrv.$(OBJEXT)       \
        $(OUTPRE)free_krbhs.$(OBJEXT)   \
        $(OUTPRE)free_hstrl.$(OBJEXT)   \
@@ -105,6 +107,7 @@ SRCS= \
        $(srcdir)/def_realm.c   \
        $(srcdir)/ccdefname.c   \
        $(srcdir)/changepw.c    \
+       $(srcdir)/dnsglue.c     \
        $(srcdir)/dnssrv.c      \
        $(srcdir)/free_krbhs.c  \
        $(srcdir)/free_hstrl.c  \
diff --git a/src/lib/krb5/os/dnsglue.c b/src/lib/krb5/os/dnsglue.c
new file mode 100644 (file)
index 0000000..352134b
--- /dev/null
@@ -0,0 +1,299 @@
+/*
+ * lib/krb5/os/dnsglue.c
+ *
+ * Copyright 2004 by the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * Export of this software from the United States of America may
+ *   require a specific license from the United States Government.
+ *   It is the responsibility of any person or organization contemplating
+ *   export to obtain such a license before exporting.
+ * 
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission.  Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose.  It is provided "as is" without express
+ * or implied warranty.
+ * 
+ */
+#ifdef KRB5_DNS_LOOKUP
+
+#include "dnsglue.h"
+
+/*
+ * Opaque handle
+ */
+struct krb5int_dns_state {
+    int nclass;
+    int ntype;
+    void *ansp;
+    int anslen;
+    int ansmax;
+#if HAVE_RES_NSEARCH
+    int cur_ans;
+    ns_msg msg;
+#else
+    unsigned char *ptr;
+    unsigned short nanswers;
+#endif
+};
+
+#if !HAVE_RES_NSEARCH
+static int initparse(struct krb5int_dns_state *);
+#endif
+
+/*
+ * krb5int_dns_init()
+ *
+ * Initialize an opaue handl.  Do name lookup and initial parsing of
+ * reply, skipping question section.  Prepare to iterate over answer
+ * section.  Returns -1 on error, 0 on success.
+ */
+int
+krb5int_dns_init(struct krb5int_dns_state **dsp,
+                char *host, int nclass, int ntype)
+{
+#if HAVE_RES_NSEARCH
+    struct __res_state statbuf;
+#endif
+    struct krb5int_dns_state *ds;
+    int len;
+    size_t nextincr, maxincr;
+    unsigned char *p;
+
+    *dsp = ds = malloc(sizeof(*ds));
+    if (ds == NULL)
+       return -1;
+
+    ds->nclass = nclass;
+    ds->ntype = ntype;
+    ds->ansp = NULL;
+    ds->anslen = 0;
+    ds->ansmax = 0;
+    nextincr = 2048;
+    maxincr = INT_MAX;
+
+#if HAVE_RES_NSEARCH
+    ds->cur_ans = 0;
+    len = res_ninit(&statbuf);
+    if (len < 0)
+       return -1;
+#endif
+
+    do {
+       p = (ds->ansp == NULL)
+           ? malloc(nextincr) : realloc(ds->ansp, nextincr);
+
+       if (p == NULL && ds->ansp != NULL) {
+           free(ds->ansp);
+           return -1;
+       }
+       ds->ansp = p;
+       ds->ansmax = nextincr;
+
+#if HAVE_RES_NSEARCH
+       len = res_nsearch(&statbuf, host, ds->nclass, ds->ntype,
+                         ds->ansp, ds->ansmax);
+#else
+       len = res_search(host, ds->nclass, ds->ntype,
+                        ds->ansp, ds->ansmax);
+#endif
+       if (len > maxincr)
+           return -1;
+       while (nextincr < len)
+           nextincr *= 2;
+       if (len < 0 || nextincr > maxincr) {
+           free(ds->ansp);
+           return -1;
+       }
+    } while (len > ds->ansmax);
+
+    ds->anslen = len;
+#if HAVE_RES_NSEARCH
+    len = ns_initparse(ds->ansp, ds->anslen, &ds->msg);
+#else
+    len = initparse(ds);
+#endif
+    if (len < 0) {
+       free(ds->ansp);
+       return -1;
+    }
+
+    return 0;
+}
+
+#if HAVE_RES_NSEARCH
+/*
+ * krb5int_dns_nextans - get next matching answer record
+ *
+ * Sets pp to NULL if no more records.  Returns -1 on error, 0 on
+ * success.
+ */
+int
+krb5int_dns_nextans(struct krb5int_dns_state *ds,
+                   const unsigned char **pp, int *lenp)
+{
+    int len;
+    ns_rr rr;
+
+    *pp = NULL;
+    *lenp = 0;
+    while (ds->cur_ans < ns_msg_count(ds->msg, ns_s_an)) {
+       len = ns_parserr(&ds->msg, ns_s_an, ds->cur_ans, &rr);
+       if (len < 0)
+           return -1;
+       ds->cur_ans++;
+       if (ds->nclass == ns_rr_class(rr)
+           && ds->ntype == ns_rr_type(rr)) {
+           *pp = ns_rr_rdata(rr);
+           *lenp = ns_rr_rdlen(rr);
+           return 0;
+       }
+    }
+    return 0;
+}
+#endif
+
+/*
+ * krb5int_dns_expand - wrapper for dn_expand()
+ */
+int krb5int_dns_expand(struct krb5int_dns_state *ds,
+                      const unsigned char *p,
+                      char *buf, int len)
+{
+
+#if HAVE_NS_NAME_UNCOMPRESS
+    return ns_name_uncompress(ds->ansp,
+                             (unsigned char *)ds->ansp + ds->anslen,
+                             p, buf, (size_t)len);
+#else
+    return dn_expand(ds->ansp,
+                    (unsigned char *)ds->ansp + ds->anslen,
+                    p, buf, len);
+#endif
+}
+
+/*
+ * Free stuff.
+ */
+void
+krb5int_dns_fini(struct krb5int_dns_state *ds)
+{
+    if (ds->ansp != NULL)
+       free(ds->ansp);
+    if (ds != NULL)
+       free(ds);
+}
+
+/*
+ * Compat routines for BIND 4
+ */
+#if !HAVE_RES_NSEARCH
+
+/*
+ * initparse
+ *
+ * Skip header and question section of reply.  Set a pointer to the
+ * beginning of the answer section, and prepare to iterate over
+ * answer records.
+ */
+static int
+initparse(struct krb5int_dns_state *ds)
+{
+    HEADER *hdr;
+    unsigned char *p;
+    unsigned short nqueries, nanswers;
+    int len;
+#if !HAVE_DN_SKIPNAME
+    char host[MAXDNAME];
+#endif
+
+    if (ds->anslen < sizeof(HEADER))
+       return -1;
+
+    hdr = (HEADER *)ds->ansp;
+    p = ds->ansp;
+    nqueries = ntohs((unsigned short)hdr->qdcount);
+    nanswers = ntohs((unsigned short)hdr->ancount);
+    p += sizeof(HEADER);
+
+    /*
+     * Skip query records.
+     */
+    while (nqueries--) {
+#if HAVE_DN_SKIPNAME
+       len = dn_skipname(p, (unsigned char *)ds->ansp + ds->anslen);
+#else
+       len = dn_expand(ds->ansp, (unsigned char *)ds->ansp + ds->anslen,
+                       p, host, sizeof(host));
+#endif
+       if (len < 0 || !INCR_OK(ds->ansp, ds->anslen, p, len))
+           return -1;
+       p += len;
+    }
+    ds->ptr = p;
+    ds->nanswers = nanswers;
+    return 0;
+}
+
+/*
+ * krb5int_dns_nextans() - get next answer record
+ *
+ * Sets pp to NULL if no more records.
+ */
+int
+krb5int_dns_nextans(struct krb5int_dns_state *ds,
+                   const unsigned char **pp, int *lenp)
+{
+    int len;
+    unsigned char *p;
+    unsigned short ntype, nclass, rdlen;
+#if !HAVE_DN_SKIPNAME
+    char host[MAXDNAME];
+#endif
+
+    *pp = NULL;
+    *lenp = 0;
+    p = ds->ptr;
+
+    while (ds->nanswers--) {
+#if HAVE_DN_SKIPNAME
+       len = dn_skipname(ds->ansp, (unsigned char *)ds->ansp + ds->anslen);
+#else
+       len = dn_expand(ds->ansp, (unsigned char *)ds->ansp + ds->anslen,
+                       p, host, sizeof(host));
+#endif
+       if (len < 0 || !INCR_OK(ds->ansp, ds->anslen, p, len))
+           return -1;
+       SAFE_GETUINT16(ds->ansp, ds->anslen, p, 2, ntype, out);
+       /* Also skip 4 bytes of TTL */
+       SAFE_GETUINT16(ds->ansp, ds->anslen, p, 6, nclass, out);
+       SAFE_GETUINT16(ds->ansp, ds->anslen, p, 2, rdlen, out);
+
+       if (!INCR_OK(ds->ansp, ds->anslen, p, rdlen))
+           return -1;
+       if (rdlen > INT_MAX)
+           return -1;
+       if (nclass == ds->nclass && ntype == ds->ntype) {
+           *pp = p;
+           *lenp = rdlen;
+           ds->ptr = p + rdlen;
+           return 0;
+       }
+    }
+    return 0;
+out:
+    return -1;
+}
+
+#endif
+
+#endif /* KRB5_DNS_LOOKUP */
diff --git a/src/lib/krb5/os/dnsglue.h b/src/lib/krb5/os/dnsglue.h
new file mode 100644 (file)
index 0000000..d2d1927
--- /dev/null
@@ -0,0 +1,148 @@
+/*
+ * lib/krb5/os/dnsglue.h
+ *
+ * Copyright 2004 by the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * Export of this software from the United States of America may
+ *   require a specific license from the United States Government.
+ *   It is the responsibility of any person or organization contemplating
+ *   export to obtain such a license before exporting.
+ * 
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission.  Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose.  It is provided "as is" without express
+ * or implied warranty.
+ *
+ * Glue layer for DNS resolver, to make parsing of replies easier
+ * whether we are using BIND 4, 8, or 9.
+ */
+
+/*
+ * BIND 4 doesn't have the ns_initparse() API, so we need to do some
+ * manual parsing via the HEADER struct.  BIND 8 does have
+ * ns_initparse(), but has enums for the various protocol constants
+ * rather than the BIND 4 macros.  BIND 9 (at least on Mac OS X
+ * Panther) appears to disable res_nsearch() if BIND_8_COMPAT is
+ * defined (which is necessary to obtain the HEADER struct).
+ *
+ * We use ns_initparse() if available at all, and never define
+ * BIND_8_COMPAT.  If there is no ns_initparse(), we do manual parsing
+ * by using the HEADER struct.
+ */
+
+#ifndef KRB5_DNSGLUE_H
+#define KRB5_DNSGLUE_H
+
+#ifdef KRB5_DNS_LOOKUP
+
+#define NEED_SOCKETS
+#include "k5-int.h"
+#include "os-proto.h"
+#ifdef WSHELPER
+#include <wshelper.h>
+#else /* WSHELPER */
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <arpa/nameser.h>
+#include <resolv.h>
+#include <netdb.h>
+#endif /* WSHELPER */
+
+#if HAVE_SYS_PARAM_H
+#include <sys/param.h>         /* for MAXHOSTNAMELEN */
+#endif
+
+#ifndef MAXHOSTNAMELEN
+#define MAXHOSTNAMELEN 64      /* if we can't find it elswhere */
+#endif
+
+#ifndef MAXDNAME
+
+#ifdef NS_MAXDNAME
+#define MAXDNAME NS_MAXDNAME
+#else
+#ifdef MAXLABEL
+#define MAXDNAME (16 * MAXLABEL)
+#else
+#define MAXDNAME (16 * MAXHOSTNAMELEN)
+#endif
+#endif
+
+#endif
+
+#if HAVE_RES_NSEARCH
+/*
+ * Some BIND 8 / BIND 9 implementations disable the BIND 4 style
+ * constants.
+ */
+#ifndef C_IN
+#define C_IN ns_c_in
+#endif
+#ifndef T_SRV
+#define T_SRV ns_t_srv
+#endif
+#ifndef T_TXT
+#define T_TXT ns_t_txt
+#endif
+
+#else  /* !HAVE_RES_NSEARCH */
+
+/*
+ * Some BIND implementations might be old enough to lack these.
+ */
+#ifndef T_TXT
+#define T_TXT 15
+#endif
+#ifndef T_SRV
+#define T_SRV 33
+#endif
+
+#endif /* HAVE_RES_NSEARCH */
+
+/*
+ * INCR_OK
+ *
+ * Given moving pointer PTR offset from BASE, return true if adding
+ * INCR to PTR doesn't move it PTR than MAX bytes from BASE.
+ */
+#define INCR_OK(base, max, ptr, incr)                          \
+    ((incr) <= (max) - ((const unsigned char *)(ptr)           \
+                       - (const unsigned char *)(base)))
+
+/*
+ * SAFE_GETUINT16
+ *
+ * Given PTR offset from BASE, if at least INCR bytes are safe to
+ * read, get network byte order uint16 into S, and increment PTR.  On
+ * failure, goto LABEL.
+ */
+
+#define SAFE_GETUINT16(base, max, ptr, incr, s, label) \
+    do {                                               \
+       if (!INCR_OK(base, max, ptr, incr)) goto label; \
+       (s) = (unsigned short)(p)[0] << 8               \
+           | (unsigned short)(p)[1];                   \
+       (p) += (incr);                                  \
+    } while (0)
+
+struct krb5int_dns_state;
+
+int krb5int_dns_init(struct krb5int_dns_state **, char *, int, int);
+int krb5int_dns_nextans(struct krb5int_dns_state *,
+                       const unsigned char **, int *);
+int krb5int_dns_expand(struct krb5int_dns_state *,
+                      const unsigned char *, char *, int);
+void krb5int_dns_fini(struct krb5int_dns_state *);
+
+#endif /* KRB5_DNS_LOOKUP */
+#endif /* !defined(KRB5_DNSGLUE_H) */
index 32851d2b8aa46ef3ce2dd2988b2ebd2e51564214..e0c593010ecd3a304cc13bf58d07f51d6388c6ad 100644 (file)
  */
 
 #ifdef KRB5_DNS_LOOKUP
-#define NEED_SOCKETS
-#include "k5-int.h"
-#include "os-proto.h"
-#include <stdio.h>
-#ifdef WSHELPER
-#include <wshelper.h>
-#else /* WSHELPER */
-#include <netinet/in.h>
-#include <arpa/inet.h>
-#include <arpa/nameser.h>
-#include <resolv.h>
-#include <netdb.h>
-#endif /* WSHELPER */
-#ifndef T_SRV
-#define T_SRV 33
-#endif /* T_SRV */
 
-/* for old Unixes and friends ... */
-#ifndef MAXHOSTNAMELEN
-#define MAXHOSTNAMELEN 64
-#endif
-
-#define MAX_DNS_NAMELEN (15*(MAXHOSTNAMELEN + 1)+1)
+#include "dnsglue.h"
 
 /*
  * Lookup a KDC via DNS SRV records
@@ -79,16 +58,11 @@ krb5int_make_srv_query_realm(const krb5_data *realm,
                             const char *protocol,
                             struct srv_dns_entry **answers)
 {
-    union {
-        unsigned char bytes[2048];
-        HEADER hdr;
-    } answer;
-    unsigned char *p=NULL;
-    char host[MAX_DNS_NAMELEN], *h;
-    int type, rrclass;
-    int priority, weight, size, len, numanswers, numqueries, rdlen;
-    unsigned short port;
-    const int hdrsize = sizeof(HEADER);
+    const unsigned char *p = NULL, *base = NULL;
+    char host[MAXDNAME], *h;
+    int size, ret, rdlen, nlen;
+    unsigned short priority, weight, port;
+    struct krb5int_dns_state *ds = NULL;
 
     struct srv_dns_entry *head = NULL;
     struct srv_dns_entry *srv = NULL, *entry = NULL;
@@ -107,7 +81,7 @@ krb5int_make_srv_query_realm(const krb5_data *realm,
     if (memchr(realm->data, 0, realm->length))
        return 0;
     if ( strlen(service) + strlen(protocol) + realm->length + 6 
-         > MAX_DNS_NAMELEN )
+         > MAXDNAME )
        return 0;
     sprintf(host, "%s.%s.%.*s", service, protocol, (int) realm->length,
            realm->data);
@@ -129,173 +103,77 @@ krb5int_make_srv_query_realm(const krb5_data *realm,
     fprintf (stderr, "sending DNS SRV query for %s\n", host);
 #endif
 
-#ifdef HAVE_RES_NSEARCH
-    {
-       res_state statp;
-       /* Weird... the man pages I've been looking at (Solaris 9) say
-          we pass a res_state object (which is a pointer) into
-          various routines, but they don't say much of anything about
-          what it should point to initially or how it should be
-          allocated.
-
-          They also give no indication what the return value of
-          res_ninit is.  */
-       typedef union {
-           struct sockaddr_storage ss;
-           INT64_TYPE i64;
-           double d;
-       } aligned_thing;
-       aligned_thing statp_buf[(sizeof(*statp) + sizeof(aligned_thing) - 1) / sizeof(aligned_thing)];
-       int n;
-
-       statp = (res_state) &statp_buf;
-       memset(&statp_buf, 0, sizeof(statp_buf));
-       n = res_ninit(statp);
-       /* ignore n? */
-       size = res_nsearch(statp, host, C_IN, T_SRV,
-                          answer.bytes, sizeof(answer.bytes));
-    }
-#else
-    size = res_search(host, C_IN, T_SRV, answer.bytes, sizeof(answer.bytes));
-#endif
-
-    if ((size < hdrsize) || (size > sizeof(answer.bytes)))
+    size = krb5int_dns_init(&ds, host, C_IN, T_SRV);
+    if (size < 0)
        goto out;
 
-    /*
-     * We got an answer!  First off, parse the header and figure out how
-     * many answers we got back.
-     */
-
-    p = answer.bytes;
-
-    numqueries = ntohs(answer.hdr.qdcount);
-    numanswers = ntohs(answer.hdr.ancount);
-
-    p += sizeof(HEADER);
-
-    /*
-     * We need to skip over all of the questions, so we have to iterate
-     * over every query record.  dn_expand() is able to tell us the size
-     * of compress DNS names, so we use it.
-     */
-
-#define INCR_CHECK(x,y) x += y; if (x > size + answer.bytes) goto out
-#define CHECK(x,y) if (x + y > size + answer.bytes) goto out
-#define NTOHSP(x,y) x[0] << 8 | x[1]; x += y
-
-    while (numqueries--) {
-       len = dn_expand(answer.bytes, answer.bytes + size, p, host, sizeof(host));
-       if (len < 0)
+    for (;;) {
+       ret = krb5int_dns_nextans(ds, &base, &rdlen);
+       if (ret < 0 || base == NULL)
            goto out;
-       INCR_CHECK(p, len + 4);
-    }
 
-    /*
-     * We're now pointing at the answer records.  Only process them if
-     * they're actually T_SRV records (they might be CNAME records,
-     * for instance).
-     *
-     * But in a DNS reply, if you get a CNAME you always get the associated
-     * "real" RR for that CNAME.  RFC 1034, 3.6.2:
-     *
-     * CNAME RRs cause special action in DNS software.  When a name server
-     * fails to find a desired RR in the resource set associated with the
-     * domain name, it checks to see if the resource set consists of a CNAME
-     * record with a matching class.  If so, the name server includes the CNAME
-     * record in the response and restarts the query at the domain name
-     * specified in the data field of the CNAME record.  The one exception to
-     * this rule is that queries which match the CNAME type are not restarted.
-     *
-     * In other words, CNAMEs do not need to be expanded by the client.
-     */
+       p = base;
 
-    while (numanswers--) {
+       SAFE_GETUINT16(base, rdlen, p, 2, priority, out);
+       SAFE_GETUINT16(base, rdlen, p, 2, weight, out);
+       SAFE_GETUINT16(base, rdlen, p, 2, port, out);
 
-       /* First is the name; use dn_expand to get the compressed size */
-       len = dn_expand(answer.bytes, answer.bytes + size, p, host, sizeof(host));
-       if (len < 0)
+       /*
+        * RFC 2782 says the target is never compressed in the reply;
+        * do we believe that?  We need to flatten it anyway, though.
+        */
+       nlen = krb5int_dns_expand(ds, p, host, sizeof(host));
+       if (nlen < 0 || !INCR_OK(base, rdlen, p, nlen))
            goto out;
-       INCR_CHECK(p, len);
-
-       /* Next is the query type */
-        CHECK(p, 2);
-       type = NTOHSP(p,2);
-
-       /* Next is the query class; also skip over 4 byte TTL */
-        CHECK(p, 6);
-       rrclass = NTOHSP(p,6);
-
-       /* Record data length */
-
-        CHECK(p,2);
-       rdlen = NTOHSP(p,2);
 
        /*
-        * If this is an SRV record, process it.  Record format is:
-        *
-        * Priority
-        * Weight
-        * Port
-        * Server name
+        * We got everything!  Insert it into our list, but make sure
+        * it's in the right order.  Right now we don't do anything
+        * with the weight field
         */
 
-       if (rrclass == C_IN && type == T_SRV) {
-            CHECK(p,2);
-           priority = NTOHSP(p,2);
-           CHECK(p, 2);
-           weight = NTOHSP(p,2);
-           CHECK(p, 2);
-           port = NTOHSP(p,2);
-           len = dn_expand(answer.bytes, answer.bytes + size, p, host, sizeof(host));
-           if (len < 0)
-               goto out;
-           INCR_CHECK(p, len);
+       srv = (struct srv_dns_entry *) malloc(sizeof(struct srv_dns_entry));
+       if (srv == NULL)
+           goto out;
+       
+       srv->priority = priority;
+       srv->weight = weight;
+       srv->port = port;
+       srv->host = strdup(host);
+       if (srv->host == NULL) {
+           free(srv);
+           goto out;
+       }
 
+       if (head == NULL || head->priority > srv->priority) {
+           srv->next = head;
+           head = srv;
+       } else {
            /*
-            * We got everything!  Insert it into our list, but make sure
-            * it's in the right order.  Right now we don't do anything
-            * with the weight field
+            * This is confusing.  Only insert an entry into this
+            * spot if:
+            * The next person has a higher priority (lower priorities
+            * are preferred).
+            * Or
+            * There is no next entry (we're at the end)
             */
-
-           srv = (struct srv_dns_entry *) malloc(sizeof(struct srv_dns_entry));
-           if (srv == NULL)
-               goto out;
-       
-           srv->priority = priority;
-           srv->weight = weight;
-           srv->port = port;
-           srv->host = strdup(host);
-           if (srv->host == NULL) {
-               free(srv);
-               goto out;
+           for (entry = head; entry != NULL; entry = entry->next) {
+               if ((entry->next &&
+                    entry->next->priority > srv->priority) ||
+                   entry->next == NULL) {
+                   srv->next = entry->next;
+                   entry->next = srv;
+                   break;
+               }
            }
+       }
+    }
 
-           if (head == NULL || head->priority > srv->priority) {
-               srv->next = head;
-               head = srv;
-           } else
-               /*
-                * This is confusing.  Only insert an entry into this
-                * spot if:
-                * The next person has a higher priority (lower priorities
-                * are preferred).
-                * Or
-                * There is no next entry (we're at the end)
-                */
-               for (entry = head; entry != NULL; entry = entry->next)
-                   if ((entry->next &&
-                        entry->next->priority > srv->priority) ||
-                       entry->next == NULL) {
-                       srv->next = entry->next;
-                       entry->next = srv;
-                       break;
-                   }
-       } else
-           INCR_CHECK(p, rdlen);
+out:
+    if (ds != NULL) {
+       krb5int_dns_fini(ds);
+       ds = NULL;
     }
-       
-  out:
     *answers = head;
     return 0;
 }
index be538a025c082dd188cbabfa876fd24676835f3e..574fe70cb45995c67b67be2052c6a01b532385d8 100644 (file)
@@ -65,6 +65,8 @@
  * host names should be in the usual form (e.g. FOO.BAR.BAZ)
  */
 
+#include "dnsglue.h"
+
 #define NEED_SOCKETS
 #include "k5-int.h"
 #include "os-proto.h"
 #include <strings.h>
 #endif
 
-#ifdef KRB5_DNS_LOOKUP       
-#ifdef WSHELPER
-#include <wshelper.h>
-#else /* WSHELPER */
-#include <netinet/in.h>
-#include <arpa/inet.h>       
-#include <arpa/nameser.h>
-#ifndef T_TXT /* not defined on SunOS 4 */
-#  define T_TXT 15
-#endif
-#include <resolv.h>          
-#include <netdb.h>
-#endif /* WSHELPER */
-#endif /* KRB5_DNS_LOOKUP */ 
-
 #include "fake-addrinfo.h"
 
-/* for old Unixes and friends ... */
-#ifndef MAXHOSTNAMELEN
-#define MAXHOSTNAMELEN 64
-#endif
-
-#define MAX_DNS_NAMELEN (15*(MAXHOSTNAMELEN + 1)+1)
-
 #ifdef KRB5_DNS_LOOKUP
 /*
  * Try to look up a TXT record pointing to a Kerberos realm
 krb5_error_code
 krb5_try_realm_txt_rr(const char *prefix, const char *name, char **realm)
 {
-    union {
-        unsigned char bytes[2048];
-        HEADER hdr;
-    } answer;
-    unsigned char *p;
-    char host[MAX_DNS_NAMELEN], *h;
-    int size;
-    int type, rrclass, numanswers, numqueries, rdlen, len;
+    krb5_error_code retval = KRB5_ERR_HOST_REALM_UNKNOWN;
+    const unsigned char *p, *base;
+    char host[MAXDNAME], *h;
+    int ret, rdlen, len;
+    struct krb5int_dns_state *ds = NULL;
 
     /*
      * Form our query, and send it via DNS
@@ -126,7 +103,7 @@ krb5_try_realm_txt_rr(const char *prefix, const char *name, char **realm)
            return KRB5_ERR_HOST_REALM_UNKNOWN;
         strcpy(host,prefix);
     } else {
-        if ( strlen(prefix) + strlen(name) + 3 > MAX_DNS_NAMELEN )
+        if ( strlen(prefix) + strlen(name) + 3 > MAXDNAME )
             return KRB5_ERR_HOST_REALM_UNKNOWN;
         sprintf(host,"%s.%s", prefix, name);
 
@@ -144,119 +121,36 @@ krb5_try_realm_txt_rr(const char *prefix, const char *name, char **realm)
         if ((h > host) && (h[-1] != '.') && ((h - host + 1) < sizeof(host)))
             strcpy (h, ".");
     }
-#ifdef HAVE_RES_NSEARCH
-    {
-       res_state statp;
-       /* Weird... the man pages I've been looking at (Solaris 9) say
-          we pass a res_state object (which is a pointer) into
-          various routines, but they don't say much of anything about
-          what it should point to initially or how it should be
-          allocated.
-
-          They also give no indication what the return value of
-          res_ninit is.  */
-       typedef union {
-           struct sockaddr_storage ss;
-           INT64_TYPE i64;
-           double d;
-       } aligned_thing;
-       aligned_thing statp_buf[(sizeof(*statp) + sizeof(aligned_thing) - 1) / sizeof(aligned_thing)];
-       int n;
-
-       statp = (res_state) &statp_buf;
-       memset(&statp_buf, 0, sizeof(statp_buf));
-       n = res_ninit(statp);
-       /* ignore n? */
-       size = res_nsearch(statp, host, C_IN, T_TXT,
-                          answer.bytes, sizeof(answer.bytes));
+    ret = krb5int_dns_init(&ds, host, C_IN, T_TXT);
+    if (ret < 0)
+       goto errout;
+
+    ret = krb5int_dns_nextans(ds, &base, &rdlen);
+    if (ret < 0 || base == NULL)
+       goto errout;
+
+    p = base;
+    if (!INCR_OK(base, rdlen, p, 1))
+       goto errout;
+    len = *p++;
+    *realm = malloc((size_t)len + 1);
+    if (*realm == NULL) {
+       retval = ENOMEM;
+       goto errout;
     }
-#else
-    size = res_search(host, C_IN, T_TXT, answer.bytes, sizeof(answer.bytes));
-#endif
-
-    if ((size < sizeof(HEADER)) || (size > sizeof(answer.bytes)))
-       return KRB5_ERR_HOST_REALM_UNKNOWN;
-
-    p = answer.bytes;
-
-    numqueries = ntohs(answer.hdr.qdcount);
-    numanswers = ntohs(answer.hdr.ancount);
-
-    p += sizeof(HEADER);
-
-    /*
-     * We need to skip over the questions before we can get to the answers,
-     * which means we have to iterate over every query record.  We use
-     * dn_expand to tell us how long each compressed name is.
-     */
-
-#define INCR_CHECK(x, y) x += y; if (x > size + answer.bytes) \
-                         return KRB5_ERR_HOST_REALM_UNKNOWN
-#define CHECK(x, y) if (x + y > size + answer.bytes) \
-                         return KRB5_ERR_HOST_REALM_UNKNOWN
-#define NTOHSP(x, y) x[0] << 8 | x[1]; x += y
-
-    while (numqueries--) {
-       len = dn_expand(answer.bytes, answer.bytes + size, p, host, 
-                         sizeof(host));
-       if (len < 0)
-           return KRB5_ERR_HOST_REALM_UNKNOWN;
-       INCR_CHECK(p, len + 4);         /* Name plus type plus class */
-    }
-
-    /*
-     * We're now pointing at the answer records.  Process the first
-     * TXT record we find.
-     */
-
-    while (numanswers--) {
-       
-       /* First the name; use dn_expand to get the compressed size */
-       len = dn_expand(answer.bytes, answer.bytes + size, p,
-                       host, sizeof(host));
-       if (len < 0)
-           return KRB5_ERR_HOST_REALM_UNKNOWN;
-       INCR_CHECK(p, len);
-
-       /* Next is the query type */
-        CHECK(p, 2);
-       type = NTOHSP(p,2);
-
-       /* Next is the query class; also skip over 4 byte TTL */
-        CHECK(p,6);
-       rrclass = NTOHSP(p,6);
-
-       /* Record data length - make sure we aren't truncated */
-
-        CHECK(p,2);
-       rdlen = NTOHSP(p,2);
-
-       if (p + rdlen > answer.bytes + size)
-           return KRB5_ERR_HOST_REALM_UNKNOWN;
-
-       /*
-        * If this is a TXT record, return the string.  Note that the
-        * string has a 1-byte length in the front
-        */
-       /* XXX What about flagging multiple TXT records as an error?  */
-
-       if (rrclass == C_IN && type == T_TXT) {
-           len = *p++;
-           if (p + len > answer.bytes + size)
-               return KRB5_ERR_HOST_REALM_UNKNOWN;
-           *realm = malloc(len + 1);
-           if (*realm == NULL)
-               return ENOMEM;
-           strncpy(*realm, (char *) p, len);
-           (*realm)[len] = '\0';
-            /* Avoid a common error. */
-            if ( (*realm)[len-1] == '.' )
-                (*realm)[len-1] = '\0';
-           return 0;
-       }
+    strncpy(*realm, (const char *)p, (size_t)len);
+    (*realm)[len] = '\0';
+    /* Avoid a common error. */
+    if ( (*realm)[len-1] == '.' )
+       (*realm)[len-1] = '\0';
+    retval = 0;
+
+errout:
+    if (ds != NULL) {
+       krb5int_dns_fini(ds);
+       ds = NULL;
     }
-
-    return KRB5_ERR_HOST_REALM_UNKNOWN;
+    return retval;
 }
 #endif /* KRB5_DNS_LOOKUP */
 
@@ -301,7 +195,7 @@ krb5_get_host_realm(krb5_context context, const char *host, char ***realmsp)
     char *default_realm, *realm, *cp, *temp_realm;
     krb5_error_code retval;
     int l;
-    char local_host[MAX_DNS_NAMELEN+1];
+    char local_host[MAXDNAME+1];
 
     if (host) {
        /* Filter out numeric addresses if the caller utterly failed to
@@ -326,7 +220,7 @@ krb5_get_host_realm(krb5_context context, const char *host, char ***realmsp)
            /* IPv6 numeric address form?  Bye bye.  */
            return KRB5_ERR_NUMERIC_REALM;
 
-       /* Should probably error out if strlen(host) > MAX_DNS_NAMELEN.  */
+       /* Should probably error out if strlen(host) > MAXDNAME.  */
        strncpy(local_host, host, sizeof(local_host));
        local_host[sizeof(local_host) - 1] = '\0';
     } else {