		/* Copyright 2000-2004 The Apache Software Foundation
		 *
		 * Licensed under the Apache License, Version 2.0 (the "License");
		 * you may not use this file except in compliance with the License.
		 * You may obtain a copy of the License at
		 *
		 *     http://www.apache.org/licenses/LICENSE-2.0
		 *
		 * Unless required by applicable law or agreed to in writing, software
		 * distributed under the License is distributed on an "AS IS" BASIS,
		 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
		 * See the License for the specific language governing permissions and
		 * limitations under the License.
		 */
		
		#include "apr_arch_networkio.h"
		#include "apr_strings.h"
		#include "apr.h"
		#include "apr_lib.h"
		#include "apr_strings.h"
		#include "apr_private.h"
		
		#if APR_HAVE_STDLIB_H
		#include <stdlib.h>
		#endif
		
		#define APR_WANT_STRFUNC
		#include "apr_want.h"
		
		struct apr_ipsubnet_t {
		    int family;
		#if APR_HAVE_IPV6
		    apr_uint32_t sub[4]; /* big enough for IPv4 and IPv6 addresses */
		    apr_uint32_t mask[4];
		#else
		    apr_uint32_t sub[1];
		    apr_uint32_t mask[1];
		#endif
		};
		
		#if !defined(NETWARE) && !defined(WIN32)
		#ifdef HAVE_SET_H_ERRNO
		#define SET_H_ERRNO(newval) set_h_errno(newval)
		#else
		#define SET_H_ERRNO(newval) h_errno = (newval)
		#endif
		#else
		#define SET_H_ERRNO(newval)
		#endif
		
		#if APR_HAS_THREADS && !defined(GETHOSTBYNAME_IS_THREAD_SAFE) && \
		    defined(HAVE_GETHOSTBYNAME_R)
		/* This is the maximum size that may be returned from the reentrant
		 * gethostbyname_r function.  If the system tries to use more, it
		 * should return ERANGE.
		 */
		#define GETHOSTBYNAME_BUFLEN 512
		#endif
		
		#ifdef _WIN32_WCE
		/* XXX: BS solution.  Need an HAVE_GETSERVBYNAME and actually
		 * do something here, to provide the obvious proto mappings.
		 */
		static void *getservbyname(const char *name, const char *proto)
		{
		    return NULL;
		}
		#endif
		
		static apr_status_t get_local_addr(apr_socket_t *sock)
      ######    {
      ######        sock->local_addr->salen = sizeof(sock->local_addr->sa);
      ######        if (getsockname(sock->socketdes, (struct sockaddr *)&sock->local_addr->sa,
		                    &sock->local_addr->salen) < 0) {
      ######            return apr_get_netos_error();
		    }
		    else {
      ######            sock->local_port_unknown = sock->local_interface_unknown = 0;
		        /* XXX assumes sin_port and sin6_port at same offset */
      ######            sock->local_addr->port = ntohs(sock->local_addr->sa.sin.sin_port);
      ######            return APR_SUCCESS;
		    }
		}
		
		static apr_status_t get_remote_addr(apr_socket_t *sock)
      ######    {
      ######        sock->remote_addr->salen = sizeof(sock->remote_addr->sa);
      ######        if (getpeername(sock->socketdes, (struct sockaddr *)&sock->remote_addr->sa,
		                    &sock->remote_addr->salen) < 0) {
      ######            return apr_get_netos_error();
		    }
		    else {
      ######            sock->remote_addr_unknown = 0;
		        /* XXX assumes sin_port and sin6_port at same offset */
      ######            sock->remote_addr->port = ntohs(sock->remote_addr->sa.sin.sin_port);
      ######            return APR_SUCCESS;
		    }
		}
		
		APR_DECLARE(apr_status_t) apr_sockaddr_ip_get(char **addr,
		                                         apr_sockaddr_t *sockaddr)
      ######    {
      ######        *addr = apr_palloc(sockaddr->pool, sockaddr->addr_str_len);
      ######        apr_inet_ntop(sockaddr->family,
		                  sockaddr->ipaddr_ptr,
		                  *addr,
		                  sockaddr->addr_str_len);
		#if APR_HAVE_IPV6
      ######        if (sockaddr->family == AF_INET6 &&
		        IN6_IS_ADDR_V4MAPPED((struct in6_addr *)sockaddr->ipaddr_ptr)) {
		        /* This is an IPv4-mapped IPv6 address; drop the leading
		         * part of the address string so we're left with the familiar
		         * IPv4 format.
		         */
      ######            *addr += strlen("::ffff:");
		    }
		#endif
      ######        return APR_SUCCESS;
		}
		
		void apr_sockaddr_vars_set(apr_sockaddr_t *addr, int family, apr_port_t port)
         314    {
         314        addr->family = family;
         314        addr->sa.sin.sin_family = family;
         314        if (port) {
		        /* XXX IPv6: assumes sin_port and sin6_port at same offset */
          64            addr->sa.sin.sin_port = htons(port);
          64            addr->port = port;
		    }
		
         314        if (family == APR_INET) {
         303            addr->salen = sizeof(struct sockaddr_in);
         303            addr->addr_str_len = 16;
         303            addr->ipaddr_ptr = &(addr->sa.sin.sin_addr);
         303            addr->ipaddr_len = sizeof(struct in_addr);
		    }
		#if APR_HAVE_IPV6
          11        else if (family == APR_INET6) {
          11            addr->salen = sizeof(struct sockaddr_in6);
          11            addr->addr_str_len = 46;
          11            addr->ipaddr_ptr = &(addr->sa.sin6.sin6_addr);
          11            addr->ipaddr_len = sizeof(struct in6_addr);
		    }
		#endif
		}
		
		APR_DECLARE(apr_status_t) apr_socket_addr_get(apr_sockaddr_t **sa,
		                                           apr_interface_e which,
		                                           apr_socket_t *sock)
      ######    {
      ######        if (which == APR_LOCAL) {
      ######            if (sock->local_interface_unknown || sock->local_port_unknown) {
      ######                apr_status_t rv = get_local_addr(sock);
		
      ######                if (rv != APR_SUCCESS) {
      ######                    return rv;
		            }
		        }
      ######            *sa = sock->local_addr;
		    }
      ######        else if (which == APR_REMOTE) {
      ######            if (sock->remote_addr_unknown) {
      ######                apr_status_t rv = get_remote_addr(sock);
		
      ######                if (rv != APR_SUCCESS) {
      ######                    return rv;
		            }
		        }
      ######            *sa = sock->remote_addr;
		    }
		    else {
      ######            *sa = NULL;
      ######            return APR_EINVAL;
		    }
      ######        return APR_SUCCESS;
		}
		
		APR_DECLARE(apr_status_t) apr_parse_addr_port(char **addr,
		                                              char **scope_id,
		                                              apr_port_t *port,
		                                              const char *str,
		                                              apr_pool_t *p)
      ######    {
      ######        const char *ch, *lastchar;
      ######        int big_port;
      ######        apr_size_t addrlen;
		
      ######        *addr = NULL;         /* assume not specified */
      ######        *scope_id = NULL;     /* assume not specified */
      ######        *port = 0;            /* assume not specified */
		
		    /* First handle the optional port number.  That may be all that
		     * is specified in the string.
		     */
      ######        ch = lastchar = str + strlen(str) - 1;
      ######        while (ch >= str && apr_isdigit(*ch)) {
      ######            --ch;
		    }
		
      ######        if (ch < str) {       /* Entire string is the port. */
      ######            big_port = atoi(str);
      ######            if (big_port < 1 || big_port > 65535) {
      ######                return APR_EINVAL;
		        }
      ######            *port = big_port;
      ######            return APR_SUCCESS;
		    }
		
      ######        if (*ch == ':' && ch < lastchar) { /* host and port number specified */
      ######            if (ch == str) {               /* string starts with ':' -- bad */
      ######                return APR_EINVAL;
		        }
      ######            big_port = atoi(ch + 1);
      ######            if (big_port < 1 || big_port > 65535) {
      ######                return APR_EINVAL;
		        }
      ######            *port = big_port;
      ######            lastchar = ch - 1;
		    }
		
		    /* now handle the hostname */
      ######        addrlen = lastchar - str + 1;
		
		/* XXX we don't really have to require APR_HAVE_IPV6 for this; 
		 * just pass char[] for ipaddr (so we don't depend on struct in6_addr)
		 * and always define APR_INET6 
		 */
		#if APR_HAVE_IPV6
      ######        if (*str == '[') {
      ######            const char *end_bracket = memchr(str, ']', addrlen);
      ######            struct in6_addr ipaddr;
      ######            const char *scope_delim;
		
      ######            if (!end_bracket || end_bracket != lastchar) {
      ######                *port = 0;
      ######                return APR_EINVAL;
		        }
		
		        /* handle scope id; this is the only context where it is allowed */
      ######            scope_delim = memchr(str, '%', addrlen);
      ######            if (scope_delim) {
      ######                if (scope_delim == end_bracket - 1) { /* '%' without scope id */
      ######                    *port = 0;
      ######                    return APR_EINVAL;
		            }
      ######                addrlen = scope_delim - str - 1;
      ######                *scope_id = apr_palloc(p, end_bracket - scope_delim);
      ######                memcpy(*scope_id, scope_delim + 1, end_bracket - scope_delim - 1);
      ######                (*scope_id)[end_bracket - scope_delim - 1] = '\0';
		        }
		        else {
      ######                addrlen = addrlen - 2; /* minus 2 for '[' and ']' */
		        }
		
      ######            *addr = apr_palloc(p, addrlen + 1);
      ######            memcpy(*addr,
		               str + 1,
		               addrlen);
      ######            (*addr)[addrlen] = '\0';
      ######            if (apr_inet_pton(AF_INET6, *addr, &ipaddr) != 1) {
      ######                *addr = NULL;
      ######                *scope_id = NULL;
      ######                *port = 0;
      ######                return APR_EINVAL;
		        }
		    }
		    else 
		#endif
		    {
		        /* XXX If '%' is not a valid char in a DNS name, we *could* check 
		         *     for bogus scope ids first.
		         */
      ######            *addr = apr_palloc(p, addrlen + 1);
      ######            memcpy(*addr, str, addrlen);
      ######            (*addr)[addrlen] = '\0';
		    }
      ######        return APR_SUCCESS;
		}
		
		#if defined(HAVE_GETADDRINFO)
		
		static apr_status_t call_resolver(apr_sockaddr_t **sa,
		                                  const char *hostname, apr_int32_t family,
		                                  apr_port_t port, apr_int32_t flags, 
		                                  apr_pool_t *p)
         183    {
         183        struct addrinfo hints, *ai, *ai_list;
         183        apr_sockaddr_t *prev_sa;
         183        int error;
         183        char *servname = NULL; 
		
         183        memset(&hints, 0, sizeof(hints));
         183        hints.ai_family = family;
         183        hints.ai_socktype = SOCK_STREAM;
		#ifdef HAVE_GAI_ADDRCONFIG
		    if (family == AF_UNSPEC) {
		        /* By default, only look up addresses using address types for
		         * which a local interface is configured, i.e. no IPv6 if no
		         * IPv6 interfaces configured. */
		        hints.ai_flags = AI_ADDRCONFIG;
		    }
		#endif
         183        if(hostname == NULL) {
		#ifdef AI_PASSIVE 
		        /* If hostname is NULL, assume we are trying to bind to all
		         * interfaces. */
           5            hints.ai_flags |= AI_PASSIVE;
		#endif
		        /* getaddrinfo according to RFC 2553 must have either hostname
		         * or servname non-NULL.
		         */
		#ifdef _AIX
		        /* But current AIX getaddrinfo() doesn't like servname = "0";
		         * the "1" won't hurt since we use the port parameter to fill
		         * in the returned socket addresses later
		         */
		        if (!port) {
		            servname = "1";
		        }
		        else
		#endif
           5            servname = apr_itoa(p, port);
		    }
         183        error = getaddrinfo(hostname, servname, &hints, &ai_list);
		#ifdef HAVE_GAI_ADDRCONFIG
		    if (error == EAI_BADFLAGS && family == AF_UNSPEC) {
		        /* Retry with no flags if AI_ADDRCONFIG was rejected. */
		        hints.ai_flags = 0;
		        error = getaddrinfo(hostname, servname, &hints, &ai_list);
		    }
		#endif
         183        if (error) {
		#ifndef WIN32
      ######            if (error == EAI_SYSTEM) {
      ######                return errno;
		        }
		        else 
		#endif
		        {
		            /* issues with representing this with APR's error scheme:
		             * glibc uses negative values for these numbers, perhaps so 
		             * they don't conflict with h_errno values...  Tru64 uses 
		             * positive values which conflict with h_errno values
		             */
		#if defined(NEGATIVE_EAI)
      ######                error = -error;
		#endif
      ######                return error + APR_OS_START_EAIERR;
		        }
		    }
		
         183        prev_sa = NULL;
         183        ai = ai_list;
         367        while (ai) { /* while more addresses to report */
         184            apr_sockaddr_t *new_sa;
		
		        /* Ignore anything bogus: getaddrinfo in some old versions of
		         * glibc will return AF_UNIX entries for AF_UNSPEC+AI_PASSIVE
		         * lookups. */
         184            if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6) {
      ######                ai = ai->ai_next;
      ######                continue;
		        }
		
         184            new_sa = apr_pcalloc(p, sizeof(apr_sockaddr_t));
		
         184            new_sa->pool = p;
         184            memcpy(&new_sa->sa, ai->ai_addr, ai->ai_addrlen);
         184            apr_sockaddr_vars_set(new_sa, ai->ai_family, port);
		
         184            if (!prev_sa) { /* first element in new list */
         183                if (hostname) {
         178                    new_sa->hostname = apr_pstrdup(p, hostname);
		            }
         183                *sa = new_sa;
		        }
		        else {
           1                new_sa->hostname = prev_sa->hostname;
           1                prev_sa->next = new_sa;
		        }
		
         184            prev_sa = new_sa;
         184            ai = ai->ai_next;
		    }
         183        freeaddrinfo(ai_list);
         183        return APR_SUCCESS;
		}
		
		static apr_status_t find_addresses(apr_sockaddr_t **sa, 
		                                   const char *hostname, apr_int32_t family,
		                                   apr_port_t port, apr_int32_t flags, 
		                                   apr_pool_t *p)
         183    {
         183        if (flags & APR_IPV4_ADDR_OK) {
      ######            apr_status_t error = call_resolver(sa, hostname, AF_INET, port, flags, p);
		
		#if APR_HAVE_IPV6
      ######            if (error) {
      ######                family = AF_INET6; /* try again */
		        }
		        else
		#endif
      ######            return error;
		    }
		#if APR_HAVE_IPV6
         183        else if (flags & APR_IPV6_ADDR_OK) {
      ######            apr_status_t error = call_resolver(sa, hostname, AF_INET6, port, flags, p);
		
      ######            if (error) {
      ######                family = AF_INET; /* try again */
		        }
		        else {
      ######                return APR_SUCCESS;
		        }
		    }
		#endif
		
         183        return call_resolver(sa, hostname, family, port, flags, p);
		}
		
		#else /* end of HAVE_GETADDRINFO code */
		
		static apr_status_t find_addresses(apr_sockaddr_t **sa, 
		                                   const char *hostname, apr_int32_t family,
		                                   apr_port_t port, apr_int32_t flags, 
		                                   apr_pool_t *p)
		{
		    struct hostent *hp;
		    apr_sockaddr_t *prev_sa;
		    int curaddr;
		#if APR_HAS_THREADS && !defined(GETHOSTBYNAME_IS_THREAD_SAFE) && \
		    defined(HAVE_GETHOSTBYNAME_R) && !defined(BEOS)
		#ifdef GETHOSTBYNAME_R_HOSTENT_DATA
		    struct hostent_data hd;
		#else
		    /* If you see ERANGE, that means GETHOSBYNAME_BUFLEN needs to be
		     * bumped. */
		    char tmp[GETHOSTBYNAME_BUFLEN];
		#endif
		    int hosterror;
		#endif
		    struct hostent hs;
		    struct in_addr ipaddr;
		    char *addr_list[2];
		    const char *orig_hostname = hostname;
		
		    if (hostname == NULL) {
		        /* if we are given a NULL hostname, assume '0.0.0.0' */
		        hostname = "0.0.0.0";
		    }
		
		    if (*hostname >= '0' && *hostname <= '9' &&
		        strspn(hostname, "0123456789.") == strlen(hostname)) {
		
		        ipaddr.s_addr = inet_addr(hostname);
		        addr_list[0] = (char *)&ipaddr;
		        addr_list[1] = NULL; /* just one IP in list */
		        hs.h_addr_list = (char **)addr_list;
		        hp = &hs;
		    }
		    else {
		#if APR_HAS_THREADS && !defined(GETHOSTBYNAME_IS_THREAD_SAFE) && \
		    defined(HAVE_GETHOSTBYNAME_R) && !defined(BEOS)
		#if defined(GETHOSTBYNAME_R_HOSTENT_DATA)
		        /* AIX, HP/UX, D/UX et alia */
		        gethostbyname_r(hostname, &hs, &hd);
		        hp = &hs;
		#else
		#if defined(GETHOSTBYNAME_R_GLIBC2)
		        /* Linux glibc2+ */
		        gethostbyname_r(hostname, &hs, tmp, GETHOSTBYNAME_BUFLEN - 1, 
		                        &hp, &hosterror);
		#else
		        /* Solaris, Irix et alia */
		        hp = gethostbyname_r(hostname, &hs, tmp, GETHOSTBYNAME_BUFLEN - 1,
		                             &hosterror);
		#endif /* !defined(GETHOSTBYNAME_R_GLIBC2) */
		        if (!hp) {
		            return (hosterror + APR_OS_START_SYSERR);
		        }
		#endif /* !defined(GETHOSTBYNAME_R_HOSTENT_DATA) */
		#else
		        hp = gethostbyname(hostname);
		#endif
		
		        if (!hp) {
		#ifdef WIN32
		            return apr_get_netos_error();
		#else
		            return (h_errno + APR_OS_START_SYSERR);
		#endif
		        }
		    }
		
		    prev_sa = NULL;
		    curaddr = 0;
		    while (hp->h_addr_list[curaddr]) {
		        apr_sockaddr_t *new_sa = apr_pcalloc(p, sizeof(apr_sockaddr_t));
		
		        new_sa->pool = p;
		        new_sa->sa.sin.sin_addr = *(struct in_addr *)hp->h_addr_list[curaddr];
		        apr_sockaddr_vars_set(new_sa, AF_INET, port);
		
		        if (!prev_sa) { /* first element in new list */
		            if (orig_hostname) {
		                new_sa->hostname = apr_pstrdup(p, orig_hostname);
		            }
		            *sa = new_sa;
		        }
		        else {
		            new_sa->hostname = prev_sa->hostname;
		            prev_sa->next = new_sa;
		        }
		
		        prev_sa = new_sa;
		        ++curaddr;
		    }
		
		    return APR_SUCCESS;
		}
		
		#endif /* end of !HAVE_GETADDRINFO code */
		
		APR_DECLARE(apr_status_t) apr_sockaddr_info_get(apr_sockaddr_t **sa,
		                                                const char *hostname, 
		                                                apr_int32_t family, apr_port_t port,
		                                                apr_int32_t flags, apr_pool_t *p)
         183    {
         183        apr_int32_t masked;
         183        *sa = NULL;
		
         183        if ((masked = flags & (APR_IPV4_ADDR_OK | APR_IPV6_ADDR_OK))) {
      ######            if (!hostname ||
		            family != AF_UNSPEC ||
		            masked == (APR_IPV4_ADDR_OK | APR_IPV6_ADDR_OK)) {
      ######                return APR_EINVAL;
		        }
		#if !APR_HAVE_IPV6
		        if (flags & APR_IPV6_ADDR_OK) {
		            return APR_ENOTIMPL;
		        }
		#endif
		    }
		#if !APR_HAVE_IPV6
		    /* What may happen is that APR is not IPv6-enabled, but we're still
		     * going to call getaddrinfo(), so we have to tell the OS we only
		     * want IPv4 addresses back since we won't know what to do with
		     * IPv6 addresses.
		     */
		    if (family == APR_UNSPEC) {
		        family = APR_INET;
		    }
		#endif
		
         183        return find_addresses(sa, hostname, family, port, flags, p);
		}
		
		APR_DECLARE(apr_status_t) apr_getnameinfo(char **hostname,
		                                          apr_sockaddr_t *sockaddr,
		                                          apr_int32_t flags)
      ######    {
		#if defined(HAVE_GETNAMEINFO)
      ######        int rc;
		#if defined(NI_MAXHOST)
      ######        char tmphostname[NI_MAXHOST];
		#else
		    char tmphostname[256];
		#endif
		
		    /* don't know if it is portable for getnameinfo() to set h_errno;
		     * clear it then see if it was set */
      ######        SET_H_ERRNO(0);
		
		    /* default flags are NI_NAMREQD; otherwise, getnameinfo() will return
		     * a numeric address string if it fails to resolve the host name;
		     * that is *not* what we want here
		     *
		     * Additionally, if we know getnameinfo() doesn't handle IPv4-mapped
		     * IPv6 addresses correctly, drop down to IPv4 before calling
		     * getnameinfo().
		     */
		#ifdef GETNAMEINFO_IPV4_MAPPED_FAILS
		    if (sockaddr->family == AF_INET6 &&
		        IN6_IS_ADDR_V4MAPPED(&sockaddr->sa.sin6.sin6_addr)) {
		        struct apr_sockaddr_t tmpsa;
		        tmpsa.sa.sin.sin_family = AF_INET;
		        tmpsa.sa.sin.sin_addr.s_addr = ((uint32_t *)sockaddr->ipaddr_ptr)[3];
		
		        rc = getnameinfo((const struct sockaddr *)&tmpsa.sa,
		                         sizeof(struct sockaddr_in),
		                         tmphostname, sizeof(tmphostname), NULL, 0,
		                         flags != 0 ? flags : NI_NAMEREQD);
		    }
		    else
		#endif
      ######        rc = getnameinfo((const struct sockaddr *)&sockaddr->sa, sockaddr->salen,
		                     tmphostname, sizeof(tmphostname), NULL, 0,
		                     flags != 0 ? flags : NI_NAMEREQD);
      ######        if (rc != 0) {
      ######            *hostname = NULL;
		
		#ifndef WIN32
		        /* something went wrong. Look at the EAI_ error code */
      ######            if (rc == EAI_SYSTEM) {
		            /* EAI_SYSTEM      System error returned in errno. */
		            /* IMHO, Implementations that set h_errno a simply broken. */
      ######                if (h_errno) { /* for broken implementations which set h_errno */
      ######                    return h_errno + APR_OS_START_SYSERR;
		            }
		            else { /* "normal" case */
      ######                    return errno + APR_OS_START_SYSERR;
		            }
		        }
		        else 
		#endif
		        {
		#if defined(NEGATIVE_EAI)
      ######                if (rc < 0) rc = -rc;
		#endif
      ######                return rc + APR_OS_START_EAIERR; /* return the EAI_ error */
		        }
		    }
      ######        *hostname = sockaddr->hostname = apr_pstrdup(sockaddr->pool, 
		                                                 tmphostname);
      ######        return APR_SUCCESS;
		#else
		#if APR_HAS_THREADS && !defined(GETHOSTBYADDR_IS_THREAD_SAFE) && \
		    defined(HAVE_GETHOSTBYADDR_R) && !defined(BEOS)
		#ifdef GETHOSTBYNAME_R_HOSTENT_DATA
		    struct hostent_data hd;
		#else
		    char tmp[GETHOSTBYNAME_BUFLEN];
		#endif
		    int hosterror;
		    struct hostent hs, *hptr;
		
		#if defined(GETHOSTBYNAME_R_HOSTENT_DATA)
		    /* AIX, HP/UX, D/UX et alia */
		    gethostbyaddr_r((char *)&sockaddr->sa.sin.sin_addr, 
		                  sizeof(struct in_addr), AF_INET, &hs, &hd);
		    hptr = &hs;
		#else
		#if defined(GETHOSTBYNAME_R_GLIBC2)
		    /* Linux glibc2+ */
		    gethostbyaddr_r((char *)&sockaddr->sa.sin.sin_addr, 
		                    sizeof(struct in_addr), AF_INET,
		                    &hs, tmp, GETHOSTBYNAME_BUFLEN - 1, &hptr, &hosterror);
		#else
		    /* Solaris, Irix et alia */
		    hptr = gethostbyaddr_r((char *)&sockaddr->sa.sin.sin_addr, 
		                           sizeof(struct in_addr), AF_INET,
		                           &hs, tmp, GETHOSTBYNAME_BUFLEN, &hosterror);
		#endif /* !defined(GETHOSTBYNAME_R_GLIBC2) */
		    if (!hptr) {
		        *hostname = NULL;
		        return hosterror + APR_OS_START_SYSERR;
		    }
		#endif /* !defined(GETHOSTBYNAME_R_HOSTENT_DATA) */
		#else
		    struct hostent *hptr;
		    hptr = gethostbyaddr((char *)&sockaddr->sa.sin.sin_addr, 
		                         sizeof(struct in_addr), AF_INET);
		#endif
		
		    if (hptr) {
		        *hostname = sockaddr->hostname = apr_pstrdup(sockaddr->pool, hptr->h_name);
		        return APR_SUCCESS;
		    }
		    *hostname = NULL;
		#if defined(WIN32)
		    return apr_get_netos_error();
		#elif defined(OS2)
		    return h_errno;
		#else
		    return h_errno + APR_OS_START_SYSERR;
		#endif
		#endif
		}
		
		APR_DECLARE(apr_status_t) apr_getservbyname(apr_sockaddr_t *sockaddr,
		                                            const char *servname)
      ######    {
      ######        struct servent *se;
		
      ######        if (servname == NULL)
      ######            return APR_EINVAL;
		
      ######        if ((se = getservbyname(servname, NULL)) != NULL){
      ######            sockaddr->port = htons(se->s_port);
      ######            sockaddr->servname = apr_pstrdup(sockaddr->pool, servname);
      ######            sockaddr->sa.sin.sin_port = se->s_port;
      ######            return APR_SUCCESS;
		    }
      ######        return errno;
		}
		
		#define V4MAPPED_EQUAL(a,b)                                   \
		((a)->sa.sin.sin_family == AF_INET &&                         \
		 (b)->sa.sin.sin_family == AF_INET6 &&                        \
		 IN6_IS_ADDR_V4MAPPED((struct in6_addr *)(b)->ipaddr_ptr) &&  \
		 !memcmp((a)->ipaddr_ptr,                                     \
		         &((struct in6_addr *)(b)->ipaddr_ptr)->s6_addr[12],  \
		         (a)->ipaddr_len))
		
		APR_DECLARE(int) apr_sockaddr_equal(const apr_sockaddr_t *addr1,
		                                    const apr_sockaddr_t *addr2)
      ######    {
      ######        if (addr1->ipaddr_len == addr2->ipaddr_len &&
		        !memcmp(addr1->ipaddr_ptr, addr2->ipaddr_ptr, addr1->ipaddr_len)) {
      ######            return 1;
		    }
		#if APR_HAVE_IPV6
      ######        if (V4MAPPED_EQUAL(addr1, addr2)) {
      ######            return 1;
		    }
      ######        if (V4MAPPED_EQUAL(addr2, addr1)) {
      ######            return 1;
		    }
		#endif
      ######        return 0; /* not equal */
		}
		
		static apr_status_t parse_network(apr_ipsubnet_t *ipsub, const char *network)
           4    {
		    /* legacy syntax for ip addrs: a.b.c. ==> a.b.c.0/24 for example */
           4        int shift;
           4        char *s, *t;
           4        int octet;
           4        char buf[sizeof "255.255.255.255"];
		
           4        if (strlen(network) < sizeof buf) {
           4            strcpy(buf, network);
		    }
		    else {
      ######            return APR_EBADIP;
		    }
		
		    /* parse components */
           4        s = buf;
           4        ipsub->sub[0] = 0;
           4        ipsub->mask[0] = 0;
           4        shift = 24;
          14        while (*s) {
          12            t = s;
          12            if (!apr_isdigit(*t)) {
      ######                return APR_EBADIP;
		        }
          33            while (apr_isdigit(*t)) {
          21                ++t;
		        }
          12            if (*t == '.') {
           8                *t++ = 0;
		        }
           4            else if (*t) {
      ######                return APR_EBADIP;
		        }
          12            if (shift < 0) {
           1                return APR_EBADIP;
		        }
          11            octet = atoi(s);
          11            if (octet < 0 || octet > 255) {
           1                return APR_EBADIP;
		        }
          10            ipsub->sub[0] |= octet << shift;
          10            ipsub->mask[0] |= 0xFFUL << shift;
          10            s = t;
          10            shift -= 8;
		    }
           2        ipsub->sub[0] = ntohl(ipsub->sub[0]);
           2        ipsub->mask[0] = ntohl(ipsub->mask[0]);
           2        ipsub->family = AF_INET;
           2        return APR_SUCCESS;
		}
		
		/* return values:
		 * APR_EINVAL     not an IP address; caller should see if it is something else
		 * APR_BADIP      IP address portion is is not valid
		 * APR_BADMASK    mask portion is not valid
		 */
		
		static apr_status_t parse_ip(apr_ipsubnet_t *ipsub, const char *ipstr, int network_allowed)
          47    {
		    /* supported flavors of IP:
		     *
		     * . IPv6 numeric address string (e.g., "fe80::1")
		     * 
		     *   IMPORTANT: Don't store IPv4-mapped IPv6 address as an IPv6 address.
		     *
		     * . IPv4 numeric address string (e.g., "127.0.0.1")
		     *
		     * . IPv4 network string (e.g., "9.67")
		     *
		     *   IMPORTANT: This network form is only allowed if network_allowed is on.
		     */
          47        int rc;
		
		#if APR_HAVE_IPV6
          47        rc = apr_inet_pton(AF_INET6, ipstr, ipsub->sub);
          47        if (rc == 1) {
          14            if (IN6_IS_ADDR_V4MAPPED((struct in6_addr *)ipsub->sub)) {
		            /* apr_ipsubnet_test() assumes that we don't create IPv4-mapped IPv6
		             * addresses; this of course forces the user to specify IPv4 addresses
		             * in a.b.c.d style instead of ::ffff:a.b.c.d style.
		             */
           1                return APR_EBADIP;
		        }
          13            ipsub->family = AF_INET6;
		    }
		    else
		#endif
		    {
          33            rc = apr_inet_pton(AF_INET, ipstr, ipsub->sub);
          33            if (rc == 1) {
          27                ipsub->family = AF_INET;
		        }
		    }
          46        if (rc != 1) {
           6            if (network_allowed) {
           4                return parse_network(ipsub, ipstr);
		        }
		        else {
           2                return APR_EBADIP;
		        }
		    }
          40        return APR_SUCCESS;
		}
		
		static int looks_like_ip(const char *ipstr)
          48    {
          48        if (strchr(ipstr, ':')) {
		        /* definitely not a hostname; assume it is intended to be an IPv6 address */
          14            return 1;
		    }
		
		    /* simple IPv4 address string check */
         362        while ((*ipstr == '.') || apr_isdigit(*ipstr))
         328            ipstr++;
          34        return (*ipstr == '\0');
		}
		
		static void fix_subnet(apr_ipsubnet_t *ipsub)
          32    {
		    /* in case caller specified more bits in network address than are
		     * valid according to the mask, turn off the extra bits
		     */
          32        int i;
		
         160        for (i = 0; i < sizeof ipsub->mask / sizeof(apr_int32_t); i++) {
         128            ipsub->sub[i] &= ipsub->mask[i];
		    }
		}
		
		/* be sure not to store any IPv4 address as a v4-mapped IPv6 address */
		APR_DECLARE(apr_status_t) apr_ipsubnet_create(apr_ipsubnet_t **ipsub, const char *ipstr, 
		                                              const char *mask_or_numbits, apr_pool_t *p)
          48    {
          48        apr_status_t rv;
          48        char *endptr;
          48        long bits, maxbits = 32;
		
		    /* filter out stuff which doesn't look remotely like an IP address; this helps 
		     * callers like mod_access which have a syntax allowing hostname or IP address;
		     * APR_EINVAL tells the caller that it was probably not intended to be an IP
		     * address
		     */
          48        if (!looks_like_ip(ipstr)) {
           1            return APR_EINVAL;
		    }
		
          47        *ipsub = apr_pcalloc(p, sizeof(apr_ipsubnet_t));
		
		    /* assume ipstr is an individual IP address, not a subnet */
          47        memset((*ipsub)->mask, 0xFF, sizeof (*ipsub)->mask);
		
          47        rv = parse_ip(*ipsub, ipstr, mask_or_numbits == NULL);
          47        if (rv != APR_SUCCESS) {
           5            return rv;
		    }
		
          42        if (mask_or_numbits) {
		#if APR_HAVE_IPV6
          27            if ((*ipsub)->family == AF_INET6) {
          12                maxbits = 128;
		        }
		#endif
          27            bits = strtol(mask_or_numbits, &endptr, 10);
          27            if (*endptr == '\0' && bits > 0 && bits <= maxbits) {
		            /* valid num-bits string; fill in mask appropriately */
          14                int cur_entry = 0;
          14                apr_int32_t cur_bit_value;
		
          14                memset((*ipsub)->mask, 0, sizeof (*ipsub)->mask);
          18                while (bits > 32) {
           4                    (*ipsub)->mask[cur_entry] = 0xFFFFFFFF; /* all 32 bits */
           4                    bits -= 32;
           4                    ++cur_entry;
		            }
          14                cur_bit_value = 0x80000000;
         216                while (bits) {
         202                    (*ipsub)->mask[cur_entry] |= cur_bit_value;
         202                    --bits;
         202                    cur_bit_value /= 2;
		            }
          14                (*ipsub)->mask[cur_entry] = htonl((*ipsub)->mask[cur_entry]);
		        }
          13            else if (apr_inet_pton(AF_INET, mask_or_numbits, (*ipsub)->mask) == 1 &&
		            (*ipsub)->family == AF_INET) {
		            /* valid IPv4 netmask */
		        }
		        else {
          10                return APR_EBADMASK;
		        }
		    }
		
          32        fix_subnet(*ipsub);
		
          32        return APR_SUCCESS;
		}
		
		APR_DECLARE(int) apr_ipsubnet_test(apr_ipsubnet_t *ipsub, apr_sockaddr_t *sa)
         120    {
		#if APR_HAVE_IPV6
		    /* XXX This line will segv on Win32 build with APR_HAVE_IPV6,
		     * but without the IPV6 drivers installed.
		     */
         120        if (sa->sa.sin.sin_family == AF_INET) {
         110            if (ipsub->family == AF_INET &&
		            ((sa->sa.sin.sin_addr.s_addr & ipsub->mask[0]) == ipsub->sub[0])) {
          15                return 1;
		        }
		    }
          10        else if (IN6_IS_ADDR_V4MAPPED((struct in6_addr *)sa->ipaddr_ptr)) {
           2            if (ipsub->family == AF_INET &&
		            (((apr_uint32_t *)sa->ipaddr_ptr)[3] & ipsub->mask[0]) == ipsub->sub[0]) {
           2                return 1;
		        }
		    }
		    else {
           8            apr_uint32_t *addr = (apr_uint32_t *)sa->ipaddr_ptr;
		
           8            if ((addr[0] & ipsub->mask[0]) == ipsub->sub[0] &&
		            (addr[1] & ipsub->mask[1]) == ipsub->sub[1] &&
		            (addr[2] & ipsub->mask[2]) == ipsub->sub[2] &&
		            (addr[3] & ipsub->mask[3]) == ipsub->sub[3]) {
           3                return 1;
		        }
		    }
		#else
		    if ((sa->sa.sin.sin_addr.s_addr & ipsub->mask[0]) == ipsub->sub[0]) {
		        return 1;
		    }
		#endif /* APR_HAVE_IPV6 */
         100        return 0; /* no match */
		}
