Hi,

    I was going through the APR source and I noticed that currently the
    implementation of gethostbyname_r is incorrect (wrap everything i
    say with an imho ;).  Also, there was no implementation of the
    gethostbyaddr_r() function call.

    gethostbyname_r() and gethostbyaddr_r() are implemented differently
    on different os's.  There are three different implementations that I
    know of.  Currently the APR source only accounts for one of these
    functions, and doesn't detect the differences.  The attached patch is
    a configure.in patch to detect the correct version of the gethostby_r
    functions (glibc2, solaris, other), a set of wrapper functions in
    sa_common.c and the necessary source code updates.

    I don't have a variety of OS' to test this on, however, on a Linux
    glibc2 system, the client and the server programs in the tests
    directory both worked without issue.  I've also used similair code
    in other projects, and its worked there (ie, the concept is pretty
    well tested, this implementation needs testing on the other
    platforms).

    Ahh, well, that's pretty much it :).  Comments? Questions?

    Sterling Hughes
    [EMAIL PROTECTED]

    References: The MySQL source and cURL source use similair concepts
    and implementations (the configure macro is a slightly modified port
    of the MySQL macro's to APR).
Index: configure.in
===================================================================
RCS file: /home/cvspublic/apr/configure.in,v
retrieving revision 1.349
diff -u -r1.349 configure.in
--- configure.in        2001/07/30 18:06:26     1.349
+++ configure.in        2001/07/30 19:31:39
@@ -336,16 +336,86 @@
     fi
 fi
 
+
+AC_DEFUN(APR_CHECK_GETHOSTBY_R,[
+dnl Checks the definition of gethostbyname_r and gethostbyaddr_r
+dnl which are different for glibc, solaris and assorted other operating
+dnl systems
+ac_save_CFLAGS="$CFLAGS"
+AC_CACHE_CHECK([style of gethostby_r routines], apr_gethostby_r_style,
+AC_LANG_SAVE
+AC_LANG_C
+if test "$ac_cv_prog_gxx" = "yes"; then
+    CFLAGS="$CFLAGS -Werror"
+fi
+
+dnl Try and compile a glibc2 gethostbyname_r piece of code, and set the
+dnl style of the routines to glibc2 on success
+AC_TRY_COMPILE([
+#if !defined(SCO) && !defined(__osf__)
+#define _REENTRANT
+#endif
+
+#include <pthread.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>],[
+int tmp = gethostbyname_r((const char *) 0, (struct hostent *) 0, 
+                          (char *) 0, 0, (struct hostent **) 0, &tmp);
+],apr_gethostby_r_style=glibc2,apr_gethostby_r_style=other))
+
+AC_LANG_RESTORE
+CFLAGS="$ac_save_CFLAGS"
+
+if test "$apr_gethostby_r_style" = "glibc2"; then
+    AC_DEFINE(HAVE_GETHOSTBY_R_GLIBC2, , [ ])
+fi
+
+ac_save_CFLAGS="$CFLAGS"
+AC_CACHE_CHECK([3rd argument to the gethostby_r routines], 
apr_cv_gethostby_r_arg,
+AC_LANG_SAVE
+AC_LANG_C
+if test "$ac_cv_prog_gxx" = "yes"; then
+    CFLAGS="$CFLAGS -Werror"
+fi
+
+AC_TRY_COMPILE([
+#if !defined(SCO) && !defined(__osf)
+#define _REENTRANT
+#endif
+
+#include <pthread.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>],[
+int tmp = gethostbyname_r((const char *) 0, (struct hostent *) 0, 
+                          (struct hostent_data *) 0);],
+apr_cv_gethostby_r_arg=hostent_data, apr_cv_gethostby_r_arg=char))
+AC_LANG_RESTORE
+CFLAGS="$ac_save_CFLAGS"
+if test "$apr_cv_gethostby_r_arg" = "hostent_data"; then
+    AC_DEFINE(HAVE_GETHOSTBY_R_RETURN_INT, , [ ])
+fi
+])
+
 ac_cv_define_READDIR_IS_THREAD_SAFE=no
 ac_cv_define_GETHOSTBYNAME_IS_THREAD_SAFE=no
 if test "$threads" = "1"; then
     echo "APR will use threads"
     AC_CHECK_LIB(c_r, readdir, AC_DEFINE(READDIR_IS_THREAD_SAFE))
     AC_CHECK_LIB(c_r, gethostbyname, AC_DEFINE(GETHOSTBYNAME_IS_THREAD_SAFE))
+    AC_CHECK_LIB(c_r, gethostbyaddr, AC_DEFINE(GETHOSTBYADDR_IS_THREAD_SAFE))
     AC_CHECK_FUNCS(gethostbyname_r)
+    AC_CHECK_FUNCS(gethostbyaddr_r)
+    APR_CHECK_GETHOSTBY_R
 else
     echo "APR will be non-threaded"
 fi
+
 
 AC_CHECK_FUNCS(sigsuspend)
 AC_CHECK_FUNCS(sigwait, [ have_sigwait="1" ], [ have_sigwait="0" ]) 
Index: acconfig.h
===================================================================
RCS file: /home/cvspublic/apr/acconfig.h,v
retrieving revision 1.48
diff -u -r1.48 acconfig.h
--- acconfig.h  2001/07/25 18:00:51     1.48
+++ acconfig.h  2001/07/30 19:31:40
@@ -24,6 +24,7 @@
 
 #undef READDIR_IS_THREAD_SAFE
 #undef GETHOSTBYNAME_IS_THREAD_SAFE
+#undef GETHOSTBYADDR_IS_THREAD_SAFE
 
 #undef NEED_RLIM_T
 #undef USEBCOPY
Index: network_io/unix/sa_common.c
===================================================================
RCS file: /home/cvspublic/apr/network_io/unix/sa_common.c,v
retrieving revision 1.39
diff -u -r1.39 sa_common.c
--- network_io/unix/sa_common.c 2001/07/24 22:18:34     1.39
+++ network_io/unix/sa_common.c 2001/07/30 19:31:48
@@ -326,6 +326,105 @@
 }
 #endif
 
+#if APR_HAS_THREADS && !defined(GETHOSTBYNAME_IS_THREAD_SAFE) && \
+    defined(HAVE_GETHOSTBYNAME_R)
+# if defined(HAVE_GETHOSTBY_R_RETURN_INT)
+#  define APR_NAMELOOKUP_SIZE sizeof(struct hostent_data)
+# else
+#  define APR_NAMELOOKUP_SIZE 2048
+# endif
+#endif
+
+static apr_status_t apr_gethostbyname(apr_pool_t *p, 
+                                 char *hostname, 
+                                 struct hostent *h)
+{
+#if APR_HAS_THREADS && !defined(GETHOSTBYNAME_IS_THREAD_SAFE) && \
+    defined(HAVE_GETHOSTBYNAME_R)
+    char            buf[APR_NAMELOOKUP_SIZE];
+    int             h_errno;
+
+    /* Required for QNX and probably some other systems */
+    memset(buf, 0, sizeof(buf));
+
+# if defined(HAVE_GETHOSTBY_R_RETURN_INT)
+    /* Solaris, Irix, and some others */
+    h = gethostbyname_r(hostname, (struct hostent *) buf, 
+                        buf + sizeof(struct hostent),
+                        APR_NAMELOOKUP_SIZE - sizeof(struct hostent),
+                        &h_errno);
+    if (h == NULL)
+        return h_errno;
+# elif defined(HAVE_GETHOSTBY_R_GLIBC2)
+    /* Linux */
+    if (gethostbyname_r(hostname, (struct hostent *) buf, 
+                        buf + sizeof(struct hostent),
+                        APR_NAMELOOKUP_SIZE - sizeof(struct hostent),
+                        &h, &h_errno))
+        return h_errno;
+# else
+    /* AIX, Digital Unix, HPUX 10 */
+    if (gethostbyname_r(hostname, (struct hostent *) buf, 
+                        (hostent_data *) (buf + sizeof(struct hostent)))) {
+        h = (struct hostent *) buf;
+    }
+    else { 
+        return errno; 
+    }
+# endif
+#else
+    h = gethostbyname(hostname);
+    if (h == NULL)
+        return errno;
+#endif
+
+    return 0;
+}   
+
+/* See apr_gethostbyname for explanations :) */
+static apr_status_t apr_gethostbyaddr(apr_pool_t *p,
+                                char *address, int size, int type,
+                                struct hostent *h)
+{
+#if APR_HAS_THREADS && !defined(GETHOSTBYADDR_IS_THREAD_SAFE) && \
+    defined(HAVE_GETHOSTBYADDR_R)
+    char            buf[APR_NAMELOOKUP_SIZE];
+    int             h_errnop;
+    
+    memset(buf, 0, sizeof(buf));
+    
+# if defined(HAVE_GETHOSTBY_R_RETURN_INT)
+    h = gethostbyaddr_r(address, size, type, (struct hostent *) buf,
+                        buf + sizeof(struct hostent),
+                        APR_NAMELOOKUP_SIZE - sizeof(struct hostent),
+                        &h_errnop);
+    if (h == NULL)
+        return h_errnop;
+# elif defined(HAVE_GETHOSTBY_R_GLIBC2)
+    if (gethostbyaddr_r(address, size, type, (struct hostent *) buf,
+                        buf + sizeof(struct hostent), 
+                        APR_NAMELOOKUP_SIZE - sizeof(struct hostent),
+                        &h, &h_errnop))
+        return h_errnop;
+# else
+    if (gethostbyaddr_r(address, size, type, (struct hostent *) buf,
+                        (struct hostent_data *) (buf + sizeof(struct 
hostent)))) {
+        h = (struct hostent *) buf;
+    }
+    else {
+        return errno;
+    }
+# endif
+#else
+    h = gethostbyaddr(address, size, type);
+    if (h == NULL)
+        return errno;
+#endif
+
+    return 0;
+}
+
+
 APR_DECLARE(apr_status_t) apr_sockaddr_info_get(apr_sockaddr_t **sa,
                                           const char *hostname, 
                                           apr_int32_t family, apr_port_t port,
@@ -392,12 +491,6 @@
         struct hostent *hp;
         apr_sockaddr_t *cursa;
         int curaddr;
-#if APR_HAS_THREADS && !defined(GETHOSTBYNAME_IS_THREAD_SAFE) && \
-    defined(HAVE_GETHOSTBYNAME_R) && !defined(BEOS)
-        char tmp[GETHOSTBYNAME_BUFLEN];
-        int hosterror;
-        struct hostent hs;
-#endif
 
         if (family == APR_UNSPEC) {
             family = APR_INET; /* we don't support IPv6 here */
@@ -413,24 +506,14 @@
         }
         else {
 #endif
-#if APR_HAS_THREADS && !defined(GETHOSTBYNAME_IS_THREAD_SAFE) && \
-    defined(HAVE_GETHOSTBYNAME_R) && !defined(BEOS)
-        hp = gethostbyname_r(hostname, &hs, tmp, GETHOSTBYNAME_BUFLEN - 1, 
-                             &hosterror);
-#else
-        hp = gethostbyname(hostname);
-#endif
-
-        if (!hp)  {
-#ifdef WIN32
+        int error = apr_gethostbyname(p, hostname, h);
+        if (error) {
+#if defined(WIN32)
             apr_get_netos_error();
-#elif APR_HAS_THREADS && !defined(GETHOSTBYNAME_IS_THREAD_SAFE) && \
-    defined(HAVE_GETHOSTBYNAME_R) && !defined(BEOS)
-            /* If you see ERANGE, that means GETHOSBYNAME_BUFLEN needs to be
-             * bumped. */
-            return (hosterror + APR_OS_START_SYSERR);
+#elif defined(OS2)
+            return (error);
 #else
-            return (h_errno + APR_OS_START_SYSERR);
+            return (error + APR_OS_START_SYSERR);
 #endif
         }
         cursa = *sa;
@@ -503,22 +586,26 @@
     return APR_SUCCESS;
 #else
     struct hostent *hptr;
+    int             error;
 
-    hptr = gethostbyaddr((char *)&sockaddr->sa.sin.sin_addr, 
-                         sizeof(struct in_addr), 
-                         AF_INET);
-    if (hptr) {
+    error = apr_gethostbyaddr(sockaddr->pool, 
+                              (char *) &sockaddr->sa.sin.sin_addr,
+                              sizeof(struct inaddr), AF_INET,
+                              hptr);
+    if (!error) {
         *hostname = sockaddr->hostname = apr_pstrdup(sockaddr->pool, 
hptr->h_name);
         return APR_SUCCESS;
     }
-    *hostname = NULL;
+    else {
+        *hostname = NULL;
 #if defined(WIN32)
-    return apr_get_netos_error();
+        return apr_get_netos_error();
 #elif defined(OS2)
-    return h_errno;
+        return error;
 #else
-    return h_errno + APR_OS_START_SYSERR;
+        return error + APR_OS_START_SYSERR;
 #endif
+    }
 #endif
 }
 

Reply via email to