1 /* Licensed to the Apache Software Foundation (ASF) under one or more
2  * contributor license agreements.  See the NOTICE file distributed with
3  * this work for additional information regarding copyright ownership.
4  * The ASF licenses this file to You under the Apache License, Version 2.0
5  * (the "License"); you may not use this file except in compliance with
6  * the License.  You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "apr_arch_networkio.h"
18 #include "apr_strings.h"
19 #include "apr.h"
20 #include "apr_lib.h"
21 #include "apr_strings.h"
22 #include "apr_private.h"
23 
24 #if APR_HAVE_STDLIB_H
25 #include <stdlib.h>
26 #endif
27 
28 #ifdef HAVE_NET_IF_H
29 #include <net/if.h>
30 #endif
31 
32 #if defined(HAVE_IF_INDEXTONAME) && defined(_MSC_VER)
33 #include "arch/win32/apr_arch_misc.h"
34 #endif
35 
36 #define APR_WANT_STRFUNC
37 #include "apr_want.h"
38 
39 struct apr_ipsubnet_t {
40     int family;
41 #if APR_HAVE_IPV6
42     apr_uint32_t sub[4]; /* big enough for IPv4 and IPv6 addresses */
43     apr_uint32_t mask[4];
44 #else
45     apr_uint32_t sub[1];
46     apr_uint32_t mask[1];
47 #endif
48 };
49 
50 #if !defined(NETWARE) && !defined(WIN32)
51 #ifdef HAVE_SET_H_ERRNO
52 #define SET_H_ERRNO(newval) set_h_errno(newval)
53 #else
54 #define SET_H_ERRNO(newval) h_errno = (newval)
55 #endif
56 #else
57 #define SET_H_ERRNO(newval)
58 #endif
59 
60 #if APR_HAS_THREADS && !defined(GETHOSTBYNAME_IS_THREAD_SAFE) && \
61     defined(HAVE_GETHOSTBYNAME_R)
62 /* This is the maximum size that may be returned from the reentrant
63  * gethostbyname_r function.  If the system tries to use more, it
64  * should return ERANGE.
65  */
66 #define GETHOSTBYNAME_BUFLEN 512
67 #endif
68 
69 #ifdef _AIX
70 /* Some levels of AIX getaddrinfo() don't like servname = "0", so
71  * set servname to "1" when port is 0 and fix it up later.
72  */
73 #define AIX_SERVNAME_HACK 1
74 #else
75 #define AIX_SERVNAME_HACK 0
76 #endif
77 
78 #ifdef _WIN32_WCE
79 /* XXX: BS solution.  Need an HAVE_GETSERVBYNAME and actually
80  * do something here, to provide the obvious proto mappings.
81  */
getservbyname(const char * name,const char * proto)82 static void *getservbyname(const char *name, const char *proto)
83 {
84     return NULL;
85 }
86 #endif
87 
get_local_addr(apr_socket_t * sock)88 static apr_status_t get_local_addr(apr_socket_t *sock)
89 {
90     sock->local_addr->salen = sizeof(sock->local_addr->sa);
91     if (getsockname(sock->socketdes, (struct sockaddr *)&sock->local_addr->sa,
92                     &sock->local_addr->salen) < 0) {
93         return apr_get_netos_error();
94     }
95     else {
96         sock->local_port_unknown = sock->local_interface_unknown = 0;
97         /* XXX assumes sin_port and sin6_port at same offset */
98         sock->local_addr->port = ntohs(sock->local_addr->sa.sin.sin_port);
99         return APR_SUCCESS;
100     }
101 }
102 
get_remote_addr(apr_socket_t * sock)103 static apr_status_t get_remote_addr(apr_socket_t *sock)
104 {
105     sock->remote_addr->salen = sizeof(sock->remote_addr->sa);
106     if (getpeername(sock->socketdes, (struct sockaddr *)&sock->remote_addr->sa,
107                     &sock->remote_addr->salen) < 0) {
108         return apr_get_netos_error();
109     }
110     else {
111         sock->remote_addr_unknown = 0;
112         /* XXX assumes sin_port and sin6_port at same offset */
113         sock->remote_addr->port = ntohs(sock->remote_addr->sa.sin.sin_port);
114         return APR_SUCCESS;
115     }
116 }
117 
apr_sockaddr_ip_getbuf(char * buf,apr_size_t buflen,apr_sockaddr_t * sockaddr)118 APR_DECLARE(apr_status_t) apr_sockaddr_ip_getbuf(char *buf, apr_size_t buflen,
119                                                  apr_sockaddr_t *sockaddr)
120 {
121     if (!apr_inet_ntop(sockaddr->family, sockaddr->ipaddr_ptr, buf, buflen)) {
122         return APR_ENOSPC;
123     }
124 
125 #if APR_HAVE_IPV6
126     if (sockaddr->family == AF_INET6
127         && IN6_IS_ADDR_V4MAPPED((struct in6_addr *)sockaddr->ipaddr_ptr)
128         && buflen > strlen("::ffff:")) {
129         /* This is an IPv4-mapped IPv6 address; drop the leading
130          * part of the address string so we're left with the familiar
131          * IPv4 format.
132          */
133         memmove(buf, buf + strlen("::ffff:"),
134                 strlen(buf + strlen("::ffff:"))+1);
135     }
136 
137     /* ensure NUL termination if the buffer is too short */
138     buf[buflen-1] = '\0';
139 
140 #ifdef HAVE_IF_INDEXTONAME
141     /* Append scope name for link-local addresses. */
142     if (sockaddr->family == AF_INET6
143         && IN6_IS_ADDR_LINKLOCAL((struct in6_addr *)sockaddr->ipaddr_ptr)) {
144         char scbuf[IF_NAMESIZE], *p = buf + strlen(buf);
145 
146         if (if_indextoname(sockaddr->sa.sin6.sin6_scope_id, scbuf) == scbuf) {
147             /* Space check, need room for buf + '%' + scope + '\0'.
148              * Assert: buflen >= strlen(buf) + strlen(scbuf) + 2
149              * Equiv:  buflen >= (p-buf) + strlen(buf) + 2
150              * Thus, fail in inverse condition: */
151             if (buflen < strlen(scbuf) + (p - buf) + 2) {
152                 return APR_ENOSPC;
153             }
154             *p++ = '%';
155             memcpy(p, scbuf, strlen(scbuf) + 1);
156         }
157     }
158 #endif /* HAVE_IF_INDEXTONAME */
159 #endif /* APR_HAVE_IPV6 */
160 
161     return APR_SUCCESS;
162 }
163 
apr_sockaddr_ip_get(char ** addr,apr_sockaddr_t * sockaddr)164 APR_DECLARE(apr_status_t) apr_sockaddr_ip_get(char **addr,
165                                               apr_sockaddr_t *sockaddr)
166 {
167     *addr = apr_palloc(sockaddr->pool, sockaddr->addr_str_len);
168     return apr_sockaddr_ip_getbuf(*addr, sockaddr->addr_str_len, sockaddr);
169 }
170 
apr_sockaddr_vars_set(apr_sockaddr_t * addr,int family,apr_port_t port)171 void apr_sockaddr_vars_set(apr_sockaddr_t *addr, int family, apr_port_t port)
172 {
173     addr->family = family;
174     addr->sa.sin.sin_family = family;
175     if (port) {
176         /* XXX IPv6: assumes sin_port and sin6_port at same offset */
177         addr->sa.sin.sin_port = htons(port);
178         addr->port = port;
179     }
180 #if AIX_SERVNAME_HACK
181     else {
182         addr->sa.sin.sin_port = htons(port);
183     }
184 #endif
185 
186     if (family == APR_INET) {
187         addr->salen = sizeof(struct sockaddr_in);
188         addr->addr_str_len = 16;
189         addr->ipaddr_ptr = &(addr->sa.sin.sin_addr);
190         addr->ipaddr_len = sizeof(struct in_addr);
191     }
192 #if APR_HAVE_IPV6
193     else if (family == APR_INET6) {
194         addr->salen = sizeof(struct sockaddr_in6);
195         addr->addr_str_len = 46;
196         addr->ipaddr_ptr = &(addr->sa.sin6.sin6_addr);
197         addr->ipaddr_len = sizeof(struct in6_addr);
198     }
199 #endif
200 #if APR_HAVE_SOCKADDR_UN
201     else if (family == APR_UNIX) {
202         addr->salen = sizeof(struct sockaddr_un);
203         addr->addr_str_len = sizeof(addr->sa.unx.sun_path);;
204         addr->ipaddr_ptr = &(addr->sa.unx.sun_path);
205         addr->ipaddr_len = addr->addr_str_len;
206     }
207 #endif
208 }
209 
apr_socket_addr_get(apr_sockaddr_t ** sa,apr_interface_e which,apr_socket_t * sock)210 APR_DECLARE(apr_status_t) apr_socket_addr_get(apr_sockaddr_t **sa,
211                                            apr_interface_e which,
212                                            apr_socket_t *sock)
213 {
214     if (which == APR_LOCAL) {
215         if (sock->local_interface_unknown || sock->local_port_unknown) {
216             apr_status_t rv = get_local_addr(sock);
217 
218             if (rv != APR_SUCCESS) {
219                 return rv;
220             }
221         }
222         *sa = sock->local_addr;
223     }
224     else if (which == APR_REMOTE) {
225         if (sock->remote_addr_unknown) {
226             apr_status_t rv = get_remote_addr(sock);
227 
228             if (rv != APR_SUCCESS) {
229                 return rv;
230             }
231         }
232         *sa = sock->remote_addr;
233     }
234     else {
235         *sa = NULL;
236         return APR_EINVAL;
237     }
238     return APR_SUCCESS;
239 }
240 
apr_parse_addr_port(char ** addr,char ** scope_id,apr_port_t * port,const char * str,apr_pool_t * p)241 APR_DECLARE(apr_status_t) apr_parse_addr_port(char **addr,
242                                               char **scope_id,
243                                               apr_port_t *port,
244                                               const char *str,
245                                               apr_pool_t *p)
246 {
247     const char *ch, *lastchar;
248     int big_port;
249     apr_size_t addrlen;
250 
251     *addr = NULL;         /* assume not specified */
252     *scope_id = NULL;     /* assume not specified */
253     *port = 0;            /* assume not specified */
254 
255     /* First handle the optional port number.  That may be all that
256      * is specified in the string.
257      */
258     ch = lastchar = str + strlen(str) - 1;
259     while (ch >= str && apr_isdigit(*ch)) {
260         --ch;
261     }
262 
263     if (ch < str) {       /* Entire string is the port. */
264         big_port = atoi(str);
265         if (big_port < 1 || big_port > 65535) {
266             return APR_EINVAL;
267         }
268         *port = big_port;
269         return APR_SUCCESS;
270     }
271 
272     if (*ch == ':' && ch < lastchar) { /* host and port number specified */
273         if (ch == str) {               /* string starts with ':' -- bad */
274             return APR_EINVAL;
275         }
276         big_port = atoi(ch + 1);
277         if (big_port < 1 || big_port > 65535) {
278             return APR_EINVAL;
279         }
280         *port = big_port;
281         lastchar = ch - 1;
282     }
283 
284     /* now handle the hostname */
285     addrlen = lastchar - str + 1;
286 
287 /* XXX we don't really have to require APR_HAVE_IPV6 for this;
288  * just pass char[] for ipaddr (so we don't depend on struct in6_addr)
289  * and always define APR_INET6
290  */
291 #if APR_HAVE_IPV6
292     if (*str == '[') {
293         const char *end_bracket = memchr(str, ']', addrlen);
294         struct in6_addr ipaddr;
295         const char *scope_delim;
296 
297         if (!end_bracket || end_bracket != lastchar) {
298             *port = 0;
299             return APR_EINVAL;
300         }
301 
302         /* handle scope id; this is the only context where it is allowed */
303         scope_delim = memchr(str, '%', addrlen);
304         if (scope_delim) {
305             if (scope_delim == end_bracket - 1) { /* '%' without scope id */
306                 *port = 0;
307                 return APR_EINVAL;
308             }
309             addrlen = scope_delim - str - 1;
310             *scope_id = apr_pstrmemdup(p, scope_delim + 1, end_bracket - scope_delim - 1);
311         }
312         else {
313             addrlen = addrlen - 2; /* minus 2 for '[' and ']' */
314         }
315 
316         *addr = apr_pstrmemdup(p, str + 1, addrlen);
317         if (apr_inet_pton(AF_INET6, *addr, &ipaddr) != 1) {
318             *addr = NULL;
319             *scope_id = NULL;
320             *port = 0;
321             return APR_EINVAL;
322         }
323     }
324     else
325 #endif
326     {
327         /* XXX If '%' is not a valid char in a DNS name, we *could* check
328          *     for bogus scope ids first.
329          */
330         *addr = apr_pstrmemdup(p, str, addrlen);
331     }
332     return APR_SUCCESS;
333 }
334 
335 #if defined(HAVE_GETADDRINFO)
336 
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)337 static apr_status_t call_resolver(apr_sockaddr_t **sa,
338                                   const char *hostname, apr_int32_t family,
339                                   apr_port_t port, apr_int32_t flags,
340                                   apr_pool_t *p)
341 {
342     struct addrinfo hints, *ai, *ai_list;
343     apr_sockaddr_t *prev_sa;
344     int error;
345     char *servname = NULL;
346 
347     memset(&hints, 0, sizeof(hints));
348     hints.ai_family = family;
349     hints.ai_socktype = SOCK_STREAM;
350 #ifdef HAVE_GAI_ADDRCONFIG
351     if (family == APR_UNSPEC) {
352         /* By default, only look up addresses using address types for
353          * which a local interface is configured, i.e. no IPv6 if no
354          * IPv6 interfaces configured. */
355         hints.ai_flags = AI_ADDRCONFIG;
356     }
357 #endif
358 
359 #ifdef __MVS__
360     /* z/OS will not return IPv4 address under AF_UNSPEC if any IPv6 results
361      * are returned, w/o AI_ALL.
362      */
363     if (family == APR_UNSPEC) {
364        hints.ai_flags |= AI_ALL;
365     }
366 #endif
367 
368     if(hostname == NULL) {
369 #ifdef AI_PASSIVE
370         /* If hostname is NULL, assume we are trying to bind to all
371          * interfaces. */
372         hints.ai_flags |= AI_PASSIVE;
373 #endif
374         /* getaddrinfo according to RFC 2553 must have either hostname
375          * or servname non-NULL.
376          */
377 #ifdef OSF1
378         /* The Tru64 5.0 getaddrinfo() can only resolve services given
379          * by the name listed in /etc/services; a numeric or unknown
380          * servname gets an EAI_SERVICE error.  So just resolve the
381          * appropriate anyaddr and fill in the port later. */
382         hostname = family == AF_INET6 ? "::" : "0.0.0.0";
383         servname = NULL;
384 #ifdef AI_NUMERICHOST
385         hints.ai_flags |= AI_NUMERICHOST;
386 #endif
387 #else
388 #if AIX_SERVNAME_HACK
389         if (!port) {
390             servname = "1";
391         }
392         else
393 #endif /* AIX_SERVNAME_HACK */
394         servname = apr_itoa(p, port);
395 #endif /* OSF1 */
396     }
397     error = getaddrinfo(hostname, servname, &hints, &ai_list);
398 #ifdef HAVE_GAI_ADDRCONFIG
399     /*
400      * Using AI_ADDRCONFIG involves some unfortunate guesswork because it
401      * does not consider loopback addresses when trying to determine if
402      * IPv4 or IPv6 is configured on a system (see RFC 3493).
403      * This is a problem if one actually wants to listen on or connect to
404      * the loopback address of a protocol family that is not otherwise
405      * configured on the system. See PR 52709.
406      * To work around some of the problems, retry without AI_ADDRCONFIG
407      * in case of EAI_ADDRFAMILY.
408      * XXX: apr_sockaddr_info_get() should really accept a flag to determine
409      * XXX: if AI_ADDRCONFIG's guesswork is wanted and if the address is
410      * XXX: to be used for listen() or connect().
411      *
412      * In case of EAI_BADFLAGS, AI_ADDRCONFIG is not supported.
413      */
414     if ((family == APR_UNSPEC) && (error == EAI_BADFLAGS
415 #ifdef EAI_ADDRFAMILY
416                                    || error == EAI_ADDRFAMILY
417 #endif
418                                                              )) {
419         hints.ai_flags &= ~AI_ADDRCONFIG;
420         error = getaddrinfo(hostname, servname, &hints, &ai_list);
421     }
422 #endif
423     if (error) {
424 #if defined(WIN32)
425         return apr_get_netos_error();
426 #else
427         if (error == EAI_SYSTEM) {
428             return errno ? errno : APR_EGENERAL;
429         }
430         else
431         {
432             /* issues with representing this with APR's error scheme:
433              * glibc uses negative values for these numbers, perhaps so
434              * they don't conflict with h_errno values...  Tru64 uses
435              * positive values which conflict with h_errno values
436              */
437 #if defined(NEGATIVE_EAI)
438             error = -error;
439 #endif
440             return error + APR_OS_START_EAIERR;
441         }
442 #endif /* WIN32 */
443     }
444 
445     prev_sa = NULL;
446     ai = ai_list;
447     while (ai) { /* while more addresses to report */
448         apr_sockaddr_t *new_sa;
449 
450         /* Ignore anything bogus: getaddrinfo in some old versions of
451          * glibc will return AF_UNIX entries for APR_UNSPEC+AI_PASSIVE
452          * lookups. */
453 #if APR_HAVE_IPV6
454         if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6) {
455 #else
456         if (ai->ai_family != AF_INET) {
457 #endif
458             ai = ai->ai_next;
459             continue;
460         }
461 
462         new_sa = apr_pcalloc(p, sizeof(apr_sockaddr_t));
463 
464         new_sa->pool = p;
465         memcpy(&new_sa->sa, ai->ai_addr, ai->ai_addrlen);
466         apr_sockaddr_vars_set(new_sa, ai->ai_family, port);
467 
468         if (!prev_sa) { /* first element in new list */
469             if (hostname) {
470                 new_sa->hostname = apr_pstrdup(p, hostname);
471             }
472             *sa = new_sa;
473         }
474         else {
475             new_sa->hostname = prev_sa->hostname;
476             prev_sa->next = new_sa;
477         }
478 
479         prev_sa = new_sa;
480         ai = ai->ai_next;
481     }
482     freeaddrinfo(ai_list);
483 
484     if (prev_sa == NULL) {
485         /*
486          * getaddrinfo returned only useless entries and *sa is still empty.
487          * This should be treated as an error.
488          */
489         return APR_EGENERAL;
490     }
491 
492     return APR_SUCCESS;
493 }
494 
495 static apr_status_t find_addresses(apr_sockaddr_t **sa,
496                                    const char *hostname, apr_int32_t family,
497                                    apr_port_t port, apr_int32_t flags,
498                                    apr_pool_t *p)
499 {
500     if (flags & APR_IPV4_ADDR_OK) {
501         apr_status_t error = call_resolver(sa, hostname, AF_INET, port, flags, p);
502 
503 #if APR_HAVE_IPV6
504         if (error) {
505             family = AF_INET6; /* try again */
506         }
507         else
508 #endif
509         return error;
510     }
511 #if APR_HAVE_IPV6
512     else if (flags & APR_IPV6_ADDR_OK) {
513         apr_status_t error = call_resolver(sa, hostname, AF_INET6, port, flags, p);
514 
515         if (error) {
516             family = AF_INET; /* try again */
517         }
518         else {
519             return APR_SUCCESS;
520         }
521     }
522 #endif
523 
524     return call_resolver(sa, hostname, family, port, flags, p);
525 }
526 
527 #else /* end of HAVE_GETADDRINFO code */
528 
529 static apr_status_t find_addresses(apr_sockaddr_t **sa,
530                                    const char *hostname, apr_int32_t family,
531                                    apr_port_t port, apr_int32_t flags,
532                                    apr_pool_t *p)
533 {
534     struct hostent *hp;
535     apr_sockaddr_t *prev_sa;
536     int curaddr;
537 #if APR_HAS_THREADS && !defined(GETHOSTBYNAME_IS_THREAD_SAFE) && \
538     defined(HAVE_GETHOSTBYNAME_R) && !defined(BEOS)
539 #ifdef GETHOSTBYNAME_R_HOSTENT_DATA
540     struct hostent_data hd;
541 #else
542     /* If you see ERANGE, that means GETHOSBYNAME_BUFLEN needs to be
543      * bumped. */
544     char tmp[GETHOSTBYNAME_BUFLEN];
545 #endif
546     int hosterror;
547 #endif
548     struct hostent hs;
549     struct in_addr ipaddr;
550     char *addr_list[2];
551     const char *orig_hostname = hostname;
552 
553     if (hostname == NULL) {
554         /* if we are given a NULL hostname, assume '0.0.0.0' */
555         hostname = "0.0.0.0";
556     }
557 
558     if (*hostname >= '0' && *hostname <= '9' &&
559         strspn(hostname, "0123456789.") == strlen(hostname)) {
560 
561         ipaddr.s_addr = inet_addr(hostname);
562         addr_list[0] = (char *)&ipaddr;
563         addr_list[1] = NULL; /* just one IP in list */
564         hs.h_addr_list = (char **)addr_list;
565         hp = &hs;
566     }
567     else {
568 #if APR_HAS_THREADS && !defined(GETHOSTBYNAME_IS_THREAD_SAFE) && \
569     defined(HAVE_GETHOSTBYNAME_R) && !defined(BEOS)
570 #if defined(GETHOSTBYNAME_R_HOSTENT_DATA)
571         /* AIX, HP/UX, D/UX et alia */
572         gethostbyname_r(hostname, &hs, &hd);
573         hp = &hs;
574 #else
575 #if defined(GETHOSTBYNAME_R_GLIBC2)
576         /* Linux glibc2+ */
577         gethostbyname_r(hostname, &hs, tmp, GETHOSTBYNAME_BUFLEN - 1,
578                         &hp, &hosterror);
579 #else
580         /* Solaris, Irix et alia */
581         hp = gethostbyname_r(hostname, &hs, tmp, GETHOSTBYNAME_BUFLEN - 1,
582                              &hosterror);
583 #endif /* !defined(GETHOSTBYNAME_R_GLIBC2) */
584         if (!hp) {
585             return (hosterror + APR_OS_START_SYSERR);
586         }
587 #endif /* !defined(GETHOSTBYNAME_R_HOSTENT_DATA) */
588 #else
589         hp = gethostbyname(hostname);
590 #endif
591 
592         if (!hp) {
593 #ifdef WIN32
594             return apr_get_netos_error();
595 #else
596             return (h_errno + APR_OS_START_SYSERR);
597 #endif
598         }
599     }
600 
601     prev_sa = NULL;
602     curaddr = 0;
603     while (hp->h_addr_list[curaddr]) {
604         apr_sockaddr_t *new_sa = apr_pcalloc(p, sizeof(apr_sockaddr_t));
605 
606         new_sa->pool = p;
607         new_sa->sa.sin.sin_addr = *(struct in_addr *)hp->h_addr_list[curaddr];
608         apr_sockaddr_vars_set(new_sa, AF_INET, port);
609 
610         if (!prev_sa) { /* first element in new list */
611             if (orig_hostname) {
612                 new_sa->hostname = apr_pstrdup(p, orig_hostname);
613             }
614             *sa = new_sa;
615         }
616         else {
617             new_sa->hostname = prev_sa->hostname;
618             prev_sa->next = new_sa;
619         }
620 
621         prev_sa = new_sa;
622         ++curaddr;
623     }
624 
625     if (prev_sa == NULL) {
626         /* this should not happen but no result should be treated as error */
627         return APR_EGENERAL;
628     }
629 
630     return APR_SUCCESS;
631 }
632 
633 #endif /* end of !HAVE_GETADDRINFO code */
634 
635 APR_DECLARE(apr_status_t) apr_sockaddr_info_get(apr_sockaddr_t **sa,
636                                                 const char *hostname,
637                                                 apr_int32_t family, apr_port_t port,
638                                                 apr_int32_t flags, apr_pool_t *p)
639 {
640     apr_int32_t masked;
641     *sa = NULL;
642 
643     if ((masked = flags & (APR_IPV4_ADDR_OK | APR_IPV6_ADDR_OK))) {
644         if (!hostname ||
645             family != APR_UNSPEC ||
646             masked == (APR_IPV4_ADDR_OK | APR_IPV6_ADDR_OK)) {
647             return APR_EINVAL;
648         }
649 #if !APR_HAVE_IPV6
650         if (flags & APR_IPV6_ADDR_OK) {
651             return APR_ENOTIMPL;
652         }
653 #endif
654     }
655     if (family == APR_UNSPEC && hostname && *hostname == '/') {
656         family = APR_UNIX;
657     }
658     if (family == APR_UNIX) {
659 #if APR_HAVE_SOCKADDR_UN
660         if (hostname && *hostname == '/') {
661             *sa = apr_pcalloc(p, sizeof(apr_sockaddr_t));
662             (*sa)->pool = p;
663             apr_cpystrn((*sa)->sa.unx.sun_path, hostname,
664                         sizeof((*sa)->sa.unx.sun_path));
665             (*sa)->hostname = apr_pstrdup(p, hostname);
666             (*sa)->family = APR_UNIX;
667             (*sa)->sa.unx.sun_family = APR_UNIX;
668             (*sa)->salen = sizeof(struct sockaddr_un);
669             (*sa)->addr_str_len = sizeof((*sa)->sa.unx.sun_path);
670             (*sa)->ipaddr_ptr = &((*sa)->sa.unx.sun_path);
671             (*sa)->ipaddr_len = (*sa)->addr_str_len;
672 
673             return APR_SUCCESS;
674         }
675         else
676 #endif
677         {
678             *sa = NULL;
679             return APR_ENOTIMPL;
680         }
681     }
682 #if !APR_HAVE_IPV6
683     /* What may happen is that APR is not IPv6-enabled, but we're still
684      * going to call getaddrinfo(), so we have to tell the OS we only
685      * want IPv4 addresses back since we won't know what to do with
686      * IPv6 addresses.
687      */
688     if (family == APR_UNSPEC) {
689         family = APR_INET;
690     }
691 #endif
692 
693     return find_addresses(sa, hostname, family, port, flags, p);
694 }
695 
696 APR_DECLARE(apr_status_t) apr_sockaddr_info_copy(apr_sockaddr_t **dst,
697                                                  const apr_sockaddr_t *src,
698                                                  apr_pool_t *p)
699 {
700     apr_sockaddr_t *d;
701     const apr_sockaddr_t *s;
702 
703     for (*dst = d = NULL, s = src; s; s = s->next) {
704         if (!d) {
705             *dst = d = apr_pmemdup(p, s, sizeof *s);
706         }
707         else {
708             d = d->next = apr_pmemdup(p, s, sizeof *s);
709         }
710         if (s->hostname) {
711             if (s == src || s->hostname != src->hostname) {
712                 d->hostname = apr_pstrdup(p, s->hostname);
713             }
714             else {
715                 d->hostname = (*dst)->hostname;
716             }
717         }
718         if (s->servname) {
719             if (s == src || s->servname != src->servname) {
720                 d->servname = apr_pstrdup(p, s->servname);
721             }
722             else {
723                 d->servname = (*dst)->servname;
724             }
725         }
726         d->pool = p;
727         apr_sockaddr_vars_set(d, s->family, s->port);
728     }
729     return APR_SUCCESS;
730 }
731 
732 APR_DECLARE(apr_status_t) apr_getnameinfo(char **hostname,
733                                           apr_sockaddr_t *sockaddr,
734                                           apr_int32_t flags)
735 {
736 #if defined(HAVE_GETNAMEINFO)
737     int rc;
738 #if defined(NI_MAXHOST)
739     char tmphostname[NI_MAXHOST];
740 #else
741     char tmphostname[256];
742 #endif
743 
744     /* don't know if it is portable for getnameinfo() to set h_errno;
745      * clear it then see if it was set */
746     SET_H_ERRNO(0);
747 
748     /* default flags are NI_NAMREQD; otherwise, getnameinfo() will return
749      * a numeric address string if it fails to resolve the host name;
750      * that is *not* what we want here
751      *
752      * For IPv4-mapped IPv6 addresses, drop down to IPv4 before calling
753      * getnameinfo() to avoid getnameinfo bugs (MacOS X, glibc).
754      */
755 #if APR_HAVE_IPV6
756     if (sockaddr->family == AF_INET6 &&
757         IN6_IS_ADDR_V4MAPPED(&sockaddr->sa.sin6.sin6_addr)) {
758         struct sockaddr_in tmpsa;
759         tmpsa.sin_family = AF_INET;
760         tmpsa.sin_port = 0;
761         tmpsa.sin_addr.s_addr = ((apr_uint32_t *)sockaddr->ipaddr_ptr)[3];
762 #ifdef SIN6_LEN
763         tmpsa.sin_len = sizeof(tmpsa);
764 #endif
765 
766         rc = getnameinfo((const struct sockaddr *)&tmpsa, sizeof(tmpsa),
767                          tmphostname, sizeof(tmphostname), NULL, 0,
768                          flags != 0 ? flags : NI_NAMEREQD);
769     }
770 #if APR_HAVE_SOCKADDR_UN
771     else if (sockaddr->family == APR_UNIX) {
772         *hostname = sockaddr->hostname;
773         return APR_SUCCESS;
774     }
775 #endif
776     else
777 #endif
778     rc = getnameinfo((const struct sockaddr *)&sockaddr->sa, sockaddr->salen,
779                      tmphostname, sizeof(tmphostname), NULL, 0,
780                      flags != 0 ? flags : NI_NAMEREQD);
781     if (rc != 0) {
782         *hostname = NULL;
783 
784 #ifndef WIN32
785         /* something went wrong. Look at the EAI_ error code */
786         if (rc == EAI_SYSTEM) {
787             /* EAI_SYSTEM      System error returned in errno. */
788             /* IMHO, Implementations that set h_errno a simply broken. */
789             if (h_errno) { /* for broken implementations which set h_errno */
790                 return h_errno + APR_OS_START_SYSERR;
791             }
792             else { /* "normal" case */
793                 return errno + APR_OS_START_SYSERR;
794             }
795         }
796         else
797 #endif
798         {
799 #if defined(NEGATIVE_EAI)
800             if (rc < 0) rc = -rc;
801 #endif
802             return rc + APR_OS_START_EAIERR; /* return the EAI_ error */
803         }
804     }
805     *hostname = sockaddr->hostname = apr_pstrdup(sockaddr->pool,
806                                                  tmphostname);
807     return APR_SUCCESS;
808 #else
809 #if APR_HAS_THREADS && !defined(GETHOSTBYADDR_IS_THREAD_SAFE) && \
810     defined(HAVE_GETHOSTBYADDR_R) && !defined(BEOS)
811 #ifdef GETHOSTBYNAME_R_HOSTENT_DATA
812     struct hostent_data hd;
813 #else
814     char tmp[GETHOSTBYNAME_BUFLEN];
815 #endif
816     int hosterror;
817     struct hostent hs, *hptr;
818 
819 #if defined(GETHOSTBYNAME_R_HOSTENT_DATA)
820     /* AIX, HP/UX, D/UX et alia */
821     gethostbyaddr_r((char *)&sockaddr->sa.sin.sin_addr,
822                   sizeof(struct in_addr), AF_INET, &hs, &hd);
823     hptr = &hs;
824 #else
825 #if defined(GETHOSTBYNAME_R_GLIBC2)
826     /* Linux glibc2+ */
827     gethostbyaddr_r((char *)&sockaddr->sa.sin.sin_addr,
828                     sizeof(struct in_addr), AF_INET,
829                     &hs, tmp, GETHOSTBYNAME_BUFLEN - 1, &hptr, &hosterror);
830 #else
831     /* Solaris, Irix et alia */
832     hptr = gethostbyaddr_r((char *)&sockaddr->sa.sin.sin_addr,
833                            sizeof(struct in_addr), AF_INET,
834                            &hs, tmp, GETHOSTBYNAME_BUFLEN, &hosterror);
835 #endif /* !defined(GETHOSTBYNAME_R_GLIBC2) */
836     if (!hptr) {
837         *hostname = NULL;
838         return hosterror + APR_OS_START_SYSERR;
839     }
840 #endif /* !defined(GETHOSTBYNAME_R_HOSTENT_DATA) */
841 #else
842     struct hostent *hptr;
843     hptr = gethostbyaddr((char *)&sockaddr->sa.sin.sin_addr,
844                          sizeof(struct in_addr), AF_INET);
845 #endif
846 
847     if (hptr) {
848         *hostname = sockaddr->hostname = apr_pstrdup(sockaddr->pool, hptr->h_name);
849         return APR_SUCCESS;
850     }
851     *hostname = NULL;
852 #if defined(WIN32)
853     return apr_get_netos_error();
854 #elif defined(OS2)
855     return h_errno;
856 #else
857     return h_errno + APR_OS_START_SYSERR;
858 #endif
859 #endif
860 }
861 
862 APR_DECLARE(apr_status_t) apr_getservbyname(apr_sockaddr_t *sockaddr,
863                                             const char *servname)
864 {
865 #if APR_HAS_THREADS && !defined(GETSERVBYNAME_IS_THREAD_SAFE) && \
866     defined(HAVE_GETSERVBYNAME_R) && \
867     (defined(GETSERVBYNAME_R_GLIBC2) || defined(GETSERVBYNAME_R_SOLARIS) || \
868      defined(GETSERVBYNAME_R_OSF1))
869     struct servent se;
870 #if defined(GETSERVBYNAME_R_OSF1)
871     struct servent_data sed;
872 
873     memset(&sed, 0, sizeof(sed)); /* must zero fill before use */
874 #else
875 #if defined(GETSERVBYNAME_R_GLIBC2)
876     struct servent *res;
877 #endif
878     char buf[1024];
879 #endif
880 #else
881     struct servent *se;
882 #endif
883 
884     if (servname == NULL)
885         return APR_EINVAL;
886 
887 #if APR_HAS_THREADS && !defined(GETSERVBYNAME_IS_THREAD_SAFE) && \
888     defined(HAVE_GETSERVBYNAME_R) && \
889     (defined(GETSERVBYNAME_R_GLIBC2) || defined(GETSERVBYNAME_R_SOLARIS) || \
890      defined(GETSERVBYNAME_R_OSF1))
891 #if defined(GETSERVBYNAME_R_GLIBC2)
892     if (getservbyname_r(servname, NULL,
893                         &se, buf, sizeof(buf), &res) == 0 && res != NULL) {
894         sockaddr->port = ntohs(res->s_port);
895         sockaddr->servname = apr_pstrdup(sockaddr->pool, servname);
896         sockaddr->sa.sin.sin_port = res->s_port;
897         return APR_SUCCESS;
898     }
899 #elif defined(GETSERVBYNAME_R_SOLARIS)
900     if (getservbyname_r(servname, NULL, &se, buf, sizeof(buf)) != NULL) {
901         sockaddr->port = ntohs(se.s_port);
902         sockaddr->servname = apr_pstrdup(sockaddr->pool, servname);
903         sockaddr->sa.sin.sin_port = se.s_port;
904         return APR_SUCCESS;
905     }
906 #elif defined(GETSERVBYNAME_R_OSF1)
907     if (getservbyname_r(servname, NULL, &se, &sed) == 0) {
908         sockaddr->port = ntohs(se.s_port);
909         sockaddr->servname = apr_pstrdup(sockaddr->pool, servname);
910         sockaddr->sa.sin.sin_port = se.s_port;
911         return APR_SUCCESS;
912     }
913 #endif
914 #else
915     if ((se = getservbyname(servname, NULL)) != NULL){
916         sockaddr->port = ntohs(se->s_port);
917         sockaddr->servname = apr_pstrdup(sockaddr->pool, servname);
918         sockaddr->sa.sin.sin_port = se->s_port;
919         return APR_SUCCESS;
920     }
921 #endif
922     return APR_ENOENT;
923 }
924 
925 #define V4MAPPED_EQUAL(a,b)                                   \
926 ((a)->sa.sin.sin_family == AF_INET &&                         \
927  (b)->sa.sin.sin_family == AF_INET6 &&                        \
928  IN6_IS_ADDR_V4MAPPED((struct in6_addr *)(b)->ipaddr_ptr) &&  \
929  !memcmp((a)->ipaddr_ptr,                                     \
930          &((struct in6_addr *)(b)->ipaddr_ptr)->s6_addr[12],  \
931          (a)->ipaddr_len))
932 
933 #if APR_HAVE_IPV6
934 #define SCOPE_OR_ZERO(sa_) ((sa_)->family != AF_INET6 ? 0 :   \
935                             ((sa_)->sa.sin6.sin6_scope_id))
936 #else
937 #define SCOPE_OR_ZERO(sa_) (0)
938 #endif
939 
940 APR_DECLARE(int) apr_sockaddr_equal(const apr_sockaddr_t *addr1,
941                                     const apr_sockaddr_t *addr2)
942 {
943     if (addr1->ipaddr_len == addr2->ipaddr_len
944         && !memcmp(addr1->ipaddr_ptr, addr2->ipaddr_ptr, addr1->ipaddr_len)
945         && SCOPE_OR_ZERO(addr1) == SCOPE_OR_ZERO(addr2)) {
946         return 1;
947     }
948 #if APR_HAVE_IPV6
949     if (V4MAPPED_EQUAL(addr1, addr2)) {
950         return 1;
951     }
952     if (V4MAPPED_EQUAL(addr2, addr1)) {
953         return 1;
954     }
955 #endif
956     return 0; /* not equal */
957 }
958 
959 APR_DECLARE(int) apr_sockaddr_is_wildcard(const apr_sockaddr_t *addr)
960 {
961     static const char inaddr_any[
962 #if APR_HAVE_IPV6
963         sizeof(struct in6_addr)
964 #else
965         sizeof(struct in_addr)
966 #endif
967     ] = {0};
968 
969     if (addr->ipaddr_ptr /* IP address initialized */
970         && addr->ipaddr_len <= sizeof inaddr_any) { /* else bug elsewhere? */
971         if (!memcmp(inaddr_any, addr->ipaddr_ptr, addr->ipaddr_len)) {
972             return 1;
973         }
974 #if APR_HAVE_IPV6
975     if (addr->family == AF_INET6
976         && IN6_IS_ADDR_V4MAPPED((struct in6_addr *)addr->ipaddr_ptr)) {
977         struct in_addr *v4 = (struct in_addr *)&((apr_uint32_t *)addr->ipaddr_ptr)[3];
978 
979         if (!memcmp(inaddr_any, v4, sizeof *v4)) {
980             return 1;
981         }
982     }
983 #endif
984     }
985     return 0;
986 }
987 
988 static apr_status_t parse_network(apr_ipsubnet_t *ipsub, const char *network)
989 {
990     /* legacy syntax for ip addrs: a.b.c. ==> a.b.c.0/24 for example */
991     int shift;
992     char *s, *t;
993     int octet;
994     char buf[sizeof "255.255.255.255"];
995 
996     if (strlen(network) < sizeof buf) {
997         strcpy(buf, network);
998     }
999     else {
1000         return APR_EBADIP;
1001     }
1002 
1003     /* parse components */
1004     s = buf;
1005     ipsub->sub[0] = 0;
1006     ipsub->mask[0] = 0;
1007     shift = 24;
1008     while (*s) {
1009         t = s;
1010         if (!apr_isdigit(*t)) {
1011             return APR_EBADIP;
1012         }
1013         while (apr_isdigit(*t)) {
1014             ++t;
1015         }
1016         if (*t == '.') {
1017             *t++ = 0;
1018         }
1019         else if (*t) {
1020             return APR_EBADIP;
1021         }
1022         if (shift < 0) {
1023             return APR_EBADIP;
1024         }
1025         octet = atoi(s);
1026         if (octet < 0 || octet > 255) {
1027             return APR_EBADIP;
1028         }
1029         ipsub->sub[0] |= octet << shift;
1030         ipsub->mask[0] |= 0xFFUL << shift;
1031         s = t;
1032         shift -= 8;
1033     }
1034     ipsub->sub[0] = ntohl(ipsub->sub[0]);
1035     ipsub->mask[0] = ntohl(ipsub->mask[0]);
1036     ipsub->family = AF_INET;
1037     return APR_SUCCESS;
1038 }
1039 
1040 /* return values:
1041  * APR_EINVAL     not an IP address; caller should see if it is something else
1042  * APR_BADIP      IP address portion is is not valid
1043  * APR_BADMASK    mask portion is not valid
1044  */
1045 
1046 static apr_status_t parse_ip(apr_ipsubnet_t *ipsub, const char *ipstr, int network_allowed)
1047 {
1048     /* supported flavors of IP:
1049      *
1050      * . IPv6 numeric address string (e.g., "fe80::1")
1051      *
1052      *   IMPORTANT: Don't store IPv4-mapped IPv6 address as an IPv6 address.
1053      *
1054      * . IPv4 numeric address string (e.g., "127.0.0.1")
1055      *
1056      * . IPv4 network string (e.g., "9.67")
1057      *
1058      *   IMPORTANT: This network form is only allowed if network_allowed is on.
1059      */
1060     int rc;
1061 
1062 #if APR_HAVE_IPV6
1063     rc = apr_inet_pton(AF_INET6, ipstr, ipsub->sub);
1064     if (rc == 1) {
1065         if (IN6_IS_ADDR_V4MAPPED((struct in6_addr *)ipsub->sub)) {
1066             /* apr_ipsubnet_test() assumes that we don't create IPv4-mapped IPv6
1067              * addresses; this of course forces the user to specify IPv4 addresses
1068              * in a.b.c.d style instead of ::ffff:a.b.c.d style.
1069              */
1070             return APR_EBADIP;
1071         }
1072         ipsub->family = AF_INET6;
1073     }
1074     else
1075 #endif
1076     {
1077         rc = apr_inet_pton(AF_INET, ipstr, ipsub->sub);
1078         if (rc == 1) {
1079             ipsub->family = AF_INET;
1080         }
1081     }
1082     if (rc != 1) {
1083         if (network_allowed) {
1084             return parse_network(ipsub, ipstr);
1085         }
1086         else {
1087             return APR_EBADIP;
1088         }
1089     }
1090     return APR_SUCCESS;
1091 }
1092 
1093 static int looks_like_ip(const char *ipstr)
1094 {
1095     if (strlen(ipstr) == 0) {
1096         return 0;
1097     }
1098 
1099     if (strchr(ipstr, ':')) {
1100         /* definitely not a hostname; assume it is intended to be an IPv6 address */
1101         return 1;
1102     }
1103 
1104     /* simple IPv4 address string check */
1105     while ((*ipstr == '.') || apr_isdigit(*ipstr))
1106         ipstr++;
1107     return (*ipstr == '\0');
1108 }
1109 
1110 static void fix_subnet(apr_ipsubnet_t *ipsub)
1111 {
1112     /* in case caller specified more bits in network address than are
1113      * valid according to the mask, turn off the extra bits
1114      */
1115     int i;
1116 
1117     for (i = 0; i < sizeof ipsub->mask / sizeof(apr_int32_t); i++) {
1118         ipsub->sub[i] &= ipsub->mask[i];
1119     }
1120 }
1121 
1122 /* be sure not to store any IPv4 address as a v4-mapped IPv6 address */
1123 APR_DECLARE(apr_status_t) apr_ipsubnet_create(apr_ipsubnet_t **ipsub, const char *ipstr,
1124                                               const char *mask_or_numbits, apr_pool_t *p)
1125 {
1126     apr_status_t rv;
1127     char *endptr;
1128     long bits, maxbits = 32;
1129 
1130     /* filter out stuff which doesn't look remotely like an IP address; this helps
1131      * callers like mod_access which have a syntax allowing hostname or IP address;
1132      * APR_EINVAL tells the caller that it was probably not intended to be an IP
1133      * address
1134      */
1135     if (!looks_like_ip(ipstr)) {
1136         return APR_EINVAL;
1137     }
1138 
1139     *ipsub = apr_pcalloc(p, sizeof(apr_ipsubnet_t));
1140 
1141     /* assume ipstr is an individual IP address, not a subnet */
1142     memset((*ipsub)->mask, 0xFF, sizeof (*ipsub)->mask);
1143 
1144     rv = parse_ip(*ipsub, ipstr, mask_or_numbits == NULL);
1145     if (rv != APR_SUCCESS) {
1146         return rv;
1147     }
1148 
1149     if (mask_or_numbits) {
1150 #if APR_HAVE_IPV6
1151         if ((*ipsub)->family == AF_INET6) {
1152             maxbits = 128;
1153         }
1154 #endif
1155         bits = strtol(mask_or_numbits, &endptr, 10);
1156         if (*endptr == '\0' && bits > 0 && bits <= maxbits) {
1157             /* valid num-bits string; fill in mask appropriately */
1158             int cur_entry = 0;
1159             apr_int32_t cur_bit_value;
1160 
1161             memset((*ipsub)->mask, 0, sizeof (*ipsub)->mask);
1162             while (bits > 32) {
1163                 (*ipsub)->mask[cur_entry] = 0xFFFFFFFF; /* all 32 bits */
1164                 bits -= 32;
1165                 ++cur_entry;
1166             }
1167             cur_bit_value = 0x80000000;
1168             while (bits) {
1169                 (*ipsub)->mask[cur_entry] |= cur_bit_value;
1170                 --bits;
1171                 cur_bit_value /= 2;
1172             }
1173             (*ipsub)->mask[cur_entry] = htonl((*ipsub)->mask[cur_entry]);
1174         }
1175         else if (apr_inet_pton(AF_INET, mask_or_numbits, (*ipsub)->mask) == 1 &&
1176             (*ipsub)->family == AF_INET) {
1177             /* valid IPv4 netmask */
1178         }
1179         else {
1180             return APR_EBADMASK;
1181         }
1182     }
1183 
1184     fix_subnet(*ipsub);
1185 
1186     return APR_SUCCESS;
1187 }
1188 
1189 APR_DECLARE(int) apr_ipsubnet_test(apr_ipsubnet_t *ipsub, apr_sockaddr_t *sa)
1190 {
1191 #if APR_HAVE_IPV6
1192     /* XXX This line will segv on Win32 build with APR_HAVE_IPV6,
1193      * but without the IPV6 drivers installed.
1194      */
1195     if (sa->family == AF_INET) {
1196         if (ipsub->family == AF_INET &&
1197             ((sa->sa.sin.sin_addr.s_addr & ipsub->mask[0]) == ipsub->sub[0])) {
1198             return 1;
1199         }
1200     }
1201     else if (IN6_IS_ADDR_V4MAPPED((struct in6_addr *)sa->ipaddr_ptr)) {
1202         if (ipsub->family == AF_INET &&
1203             (((apr_uint32_t *)sa->ipaddr_ptr)[3] & ipsub->mask[0]) == ipsub->sub[0]) {
1204             return 1;
1205         }
1206     }
1207     else if (sa->family == AF_INET6 && ipsub->family == AF_INET6) {
1208         apr_uint32_t *addr = (apr_uint32_t *)sa->ipaddr_ptr;
1209 
1210         if ((addr[0] & ipsub->mask[0]) == ipsub->sub[0] &&
1211             (addr[1] & ipsub->mask[1]) == ipsub->sub[1] &&
1212             (addr[2] & ipsub->mask[2]) == ipsub->sub[2] &&
1213             (addr[3] & ipsub->mask[3]) == ipsub->sub[3]) {
1214             return 1;
1215         }
1216     }
1217 #else
1218     if ((sa->sa.sin.sin_addr.s_addr & ipsub->mask[0]) == ipsub->sub[0]) {
1219         return 1;
1220     }
1221 #endif /* APR_HAVE_IPV6 */
1222     return 0; /* no match */
1223 }
1224 
1225 APR_DECLARE(apr_status_t) apr_sockaddr_zone_set(apr_sockaddr_t *sa,
1226                                                 const char *zone_id)
1227 {
1228 #if !APR_HAVE_IPV6 || !defined(HAVE_IF_NAMETOINDEX)
1229     return APR_ENOTIMPL;
1230 #else
1231     unsigned int idx;
1232 
1233     if (sa->family != APR_INET6
1234         || !IN6_IS_ADDR_LINKLOCAL((struct in6_addr *)sa->ipaddr_ptr)) {
1235         return APR_EBADIP;
1236     }
1237 
1238     idx = if_nametoindex(zone_id);
1239     if (idx) {
1240         sa->sa.sin6.sin6_scope_id = idx;
1241         return APR_SUCCESS;
1242     }
1243 
1244     if (errno != ENODEV) {
1245         return errno;
1246     }
1247     else {
1248         char *endptr;
1249         apr_int64_t i = apr_strtoi64(zone_id, &endptr, 10);
1250 
1251         if (*endptr != '\0' || errno || i < 1 || i > APR_INT16_MAX) {
1252             return APR_EGENERAL;
1253         }
1254 
1255         sa->sa.sin6.sin6_scope_id = (unsigned int) i;
1256         return APR_SUCCESS;
1257     }
1258 #endif
1259 }
1260 
1261 APR_DECLARE(apr_status_t) apr_sockaddr_zone_get(const apr_sockaddr_t *sa,
1262                                                 const char **name,
1263                                                 apr_uint32_t *id,
1264                                                 apr_pool_t *p)
1265 {
1266 #if !APR_HAVE_IPV6 || !defined(HAVE_IF_INDEXTONAME)
1267     return APR_ENOTIMPL;
1268 #else
1269     if (sa->family != APR_INET6 || !sa->sa.sin6.sin6_scope_id) {
1270         return APR_EBADIP;
1271     }
1272 
1273     if (name) {
1274         char *buf = apr_palloc(p, IF_NAMESIZE);
1275         if (if_indextoname(sa->sa.sin6.sin6_scope_id, buf) == NULL)
1276             return errno;
1277         *name = buf;
1278     }
1279 
1280     if (id) *id = sa->sa.sin6.sin6_scope_id;
1281 
1282     return APR_SUCCESS;
1283 #endif
1284 }
1285