No empty .Rs/.Re
[netbsd-mini2440.git] / external / ibm-public / postfix / dist / src / util / inet_addr_local.c
blob7380c3823deaa6ce001547eb9be58ab04f613911
1 /* $NetBSD$ */
3 /*++
4 /* NAME
5 /* inet_addr_local 3
6 /* SUMMARY
7 /* determine if IP address is local
8 /* SYNOPSIS
9 /* #include <inet_addr_local.h>
11 /* int inet_addr_local(addr_list, mask_list, addr_family_list)
12 /* INET_ADDR_LIST *addr_list;
13 /* INET_ADDR_LIST *mask_list;
14 /* unsigned *addr_family;
15 /* DESCRIPTION
16 /* inet_addr_local() determines all active IP interface addresses
17 /* of the local system. Any address found is appended to the
18 /* specified address list. The result value is the number of
19 /* active interfaces found.
21 /* The mask_list is either a null pointer, or it is a list that
22 /* receives the netmasks of the interface addresses that were found.
24 /* The addr_family_list specifies one or more of AF_INET or AF_INET6.
25 /* DIAGNOSTICS
26 /* Fatal errors: out of memory.
27 /* SEE ALSO
28 /* inet_addr_list(3) address list management
29 /* LICENSE
30 /* .ad
31 /* .fi
32 /* The Secure Mailer license must be distributed with this software.
33 /* AUTHOR(S)
34 /* Wietse Venema
35 /* IBM T.J. Watson Research
36 /* P.O. Box 704
37 /* Yorktown Heights, NY 10598, USA
39 /* Dean C. Strik
40 /* Department ICT
41 /* Eindhoven University of Technology
42 /* P.O. Box 513
43 /* 5600 MB Eindhoven, Netherlands
44 /* E-mail: <dean@ipnet6.org>
45 /*--*/
47 /* System library. */
49 #include <sys_defs.h>
50 #include <sys/socket.h>
51 #include <sys/time.h>
52 #include <netinet/in.h>
53 #include <net/if.h>
54 #include <sys/ioctl.h>
55 #include <arpa/inet.h>
56 #include <unistd.h>
57 #ifdef USE_SYS_SOCKIO_H
58 #include <sys/sockio.h>
59 #endif
60 #include <errno.h>
61 #include <string.h>
62 #ifdef HAS_IPV6 /* Linux only? */
63 #include <netdb.h>
64 #include <stdio.h>
65 #endif
66 #ifdef HAVE_GETIFADDRS
67 #include <ifaddrs.h>
68 #endif
70 /* Utility library. */
72 #include <msg.h>
73 #include <mymalloc.h>
74 #include <vstring.h>
75 #include <inet_addr_list.h>
76 #include <inet_addr_local.h>
77 #include <myaddrinfo.h>
78 #include <sock_addr.h>
79 #include <mask_addr.h>
80 #include <hex_code.h>
83 * Postfix needs its own interface address information to determine whether
84 * or not it is an MX host for some destination; without this information,
85 * mail would loop between MX hosts. Postfix also needs its interface
86 * addresses to figure out whether or not it is final destination for
87 * addresses of the form username@[ipaddress].
89 * Postfix needs its own interface netmask information when no explicit
90 * mynetworks setting is given in main.cf, and "mynetworks_style = subnet".
91 * The mynetworks parameter controls, among others, what mail clients are
92 * allowed to relay mail through Postfix.
94 * Different systems have different ways to find out this information. We will
95 * therefore use OS dependent methods. An overview:
97 * - Use getifaddrs() when available. This supports both IPv4/IPv6 addresses.
98 * The implementation however is not present in all major operating systems.
100 * - Use SIOCGLIFCONF when available. This supports both IPv4/IPv6 addresses.
101 * With SIOCGLIFNETMASK we can obtain the netmask for either address family.
102 * Again, this is not present in all major operating systems.
104 * - On Linux, glibc's getifaddrs(3) has returned IPv4 information for some
105 * time, but IPv6 information was not returned until 2.3.3. With older Linux
106 * versions we get IPv4 interface information with SIOCGIFCONF, and read
107 * IPv6 address/prefix information from a file in the /proc filesystem.
109 * - On other systems we expect SIOCGIFCONF to return IPv6 addresses. Since
110 * SIOCGIFNETMASK does not work reliably for IPv6 addresses, we always set
111 * the prefix length to /128 (host), and expect the user to configure a more
112 * appropriate mynetworks setting if needed.
114 * XXX: Each lookup method is implemented by its own function, so we duplicate
115 * some code. In this case, I think this is better than really drowning in
116 * the #ifdefs...
118 * -- Dean Strik (dcs)
121 /* ial_socket - make socket for ioctl() operations */
123 static int ial_socket(int af)
125 const char *myname = "inet_addr_local[socket]";
126 int sock;
129 * The host may not be actually configured with IPv6. When IPv6 support
130 * is not actually in the kernel, don't consider failure to create an
131 * IPv6 socket as fatal. This could be tuned better though. For other
132 * families, the error is fatal.
134 * XXX Now that Postfix controls protocol support centrally with the
135 * inet_proto(3) module, this workaround should no longer be needed.
137 if ((sock = socket(af, SOCK_DGRAM, 0)) < 0) {
138 #ifdef HAS_IPV6
139 if (af == AF_INET6) {
140 if (msg_verbose)
141 msg_warn("%s: socket: %m", myname);
142 return (-1);
144 #endif
145 msg_fatal("%s: socket: %m", myname);
147 return (sock);
150 #ifdef HAVE_GETIFADDRS
153 * The getifaddrs(3) function, introduced by BSD/OS, provides a
154 * platform-independent way of requesting interface addresses,
155 * including IPv6 addresses. The implementation however is not
156 * present in all major operating systems.
159 /* ial_getifaddrs - determine IP addresses using getifaddrs(3) */
161 static int ial_getifaddrs(INET_ADDR_LIST *addr_list,
162 INET_ADDR_LIST *mask_list,
163 int af)
165 const char *myname = "inet_addr_local[getifaddrs]";
166 struct ifaddrs *ifap, *ifa;
167 struct sockaddr *sa, *sam;
169 if (getifaddrs(&ifap) < 0)
170 msg_fatal("%s: getifaddrs: %m", myname);
173 * Get the address of each IP network interface. According to BIND we
174 * must include interfaces that are down because the machine may still
175 * receive packets for that address (yes, via some other interface).
176 * Having no way to verify this claim on every machine, I will give them
177 * the benefit of the doubt.
179 * FIX 200501: The IPv6 patch did not report NetBSD loopback interfaces;
180 * fixed by replacing IFF_RUNNING by IFF_UP.
182 * FIX 200501: The IPV6 patch did not skip wild-card interface addresses
183 * (tested on FreeBSD).
185 for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
186 if (!(ifa->ifa_flags & IFF_UP) || ifa->ifa_addr == 0)
187 continue;
188 sa = ifa->ifa_addr;
189 sam = ifa->ifa_netmask;
190 if (af != AF_UNSPEC && sa->sa_family != af)
191 continue;
192 switch (sa->sa_family) {
193 case AF_INET:
194 if (SOCK_ADDR_IN_ADDR(sa).s_addr == INADDR_ANY)
195 continue;
196 break;
197 #ifdef HAS_IPV6
198 case AF_INET6:
199 if (IN6_IS_ADDR_UNSPECIFIED(&SOCK_ADDR_IN6_ADDR(sa)))
200 continue;
201 break;
202 #endif
203 default:
204 continue;
207 inet_addr_list_append(addr_list, sa);
208 if (mask_list != 0) {
211 * Unfortunately, sa_len/sa_family may be broken in the netmask
212 * sockaddr structure. We must fix this manually to have correct
213 * addresses. --dcs
215 #ifdef HAS_SA_LEN
216 sam->sa_len = sa->sa_family == AF_INET6 ?
217 sizeof(struct sockaddr_in6) :
218 sizeof(struct sockaddr_in);
219 #endif
220 sam->sa_family = sa->sa_family;
221 inet_addr_list_append(mask_list, sam);
224 freeifaddrs(ifap);
225 return (0);
228 #endif /* HAVE_GETIFADDRS */
230 #ifdef HAS_SIOCGLIF
233 * The SIOCLIF* ioctls are the successors of SIOCGIF* on the Solaris
234 * and HP/UX operating systems. The data is stored in sockaddr_storage
235 * structure. Both IPv4 and IPv6 addresses are returned though these
236 * calls.
238 #define NEXT_INTERFACE(lifr) (lifr + 1)
239 #define LIFREQ_SIZE(lifr) sizeof(lifr[0])
241 /* ial_siocglif - determine IP addresses using ioctl(SIOCGLIF*) */
243 static int ial_siocglif(INET_ADDR_LIST *addr_list,
244 INET_ADDR_LIST *mask_list,
245 int af)
247 const char *myname = "inet_addr_local[siocglif]";
248 struct lifconf lifc;
249 struct lifreq *lifr;
250 struct lifreq *lifr_mask;
251 struct lifreq *the_end;
252 struct sockaddr *sa;
253 int sock;
254 VSTRING *buf;
257 * See also comments in ial_siocgif()
259 if (af != AF_INET && af != AF_INET6)
260 msg_fatal("%s: address family was %d, must be AF_INET (%d) or "
261 "AF_INET6 (%d)", myname, af, AF_INET, AF_INET6);
262 sock = ial_socket(af);
263 if (sock < 0)
264 return (0);
265 buf = vstring_alloc(1024);
266 for (;;) {
267 memset(&lifc, 0, sizeof(lifc));
268 lifc.lifc_family = AF_UNSPEC; /* XXX Why??? */
269 lifc.lifc_len = vstring_avail(buf);
270 lifc.lifc_buf = vstring_str(buf);
271 if (ioctl(sock, SIOCGLIFCONF, (char *) &lifc) < 0) {
272 if (errno != EINVAL)
273 msg_fatal("%s: ioctl SIOCGLIFCONF: %m", myname);
274 } else if (lifc.lifc_len < vstring_avail(buf) / 2)
275 break;
276 VSTRING_SPACE(buf, vstring_avail(buf) * 2);
279 the_end = (struct lifreq *) (lifc.lifc_buf + lifc.lifc_len);
280 for (lifr = lifc.lifc_req; lifr < the_end;) {
281 sa = (struct sockaddr *) & lifr->lifr_addr;
282 if (sa->sa_family != af) {
283 lifr = NEXT_INTERFACE(lifr);
284 continue;
286 if (af == AF_INET) {
287 if (SOCK_ADDR_IN_ADDR(sa).s_addr == INADDR_ANY) {
288 lifr = NEXT_INTERFACE(lifr);
289 continue;
291 #ifdef HAS_IPV6
292 } else if (af == AF_INET6) {
293 if (IN6_IS_ADDR_UNSPECIFIED(&SOCK_ADDR_IN6_ADDR(sa))) {
294 lifr = NEXT_INTERFACE(lifr);
295 continue;
298 #endif
299 inet_addr_list_append(addr_list, sa);
300 if (mask_list) {
301 lifr_mask = (struct lifreq *) mymalloc(sizeof(struct lifreq));
302 memcpy((char *) lifr_mask, (char *) lifr, sizeof(struct lifreq));
303 if (ioctl(sock, SIOCGLIFNETMASK, lifr_mask) < 0)
304 msg_fatal("%s: ioctl(SIOCGLIFNETMASK): %m", myname);
305 /* XXX: Check whether sa_len/family are honoured --dcs */
306 inet_addr_list_append(mask_list,
307 (struct sockaddr *) & lifr_mask->lifr_addr);
308 myfree((char *) lifr_mask);
310 lifr = NEXT_INTERFACE(lifr);
312 vstring_free(buf);
313 (void) close(sock);
314 return (0);
317 #else /* HAVE_SIOCGLIF */
320 * The classic SIOCGIF* ioctls. Modern BSD operating systems will
321 * also return IPv6 addresses through these structure. Note however
322 * that recent versions of these operating systems have getifaddrs.
324 #if defined(_SIZEOF_ADDR_IFREQ)
325 #define NEXT_INTERFACE(ifr) ((struct ifreq *) \
326 ((char *) ifr + _SIZEOF_ADDR_IFREQ(*ifr)))
327 #define IFREQ_SIZE(ifr) _SIZEOF_ADDR_IFREQ(*ifr)
328 #elif defined(HAS_SA_LEN)
329 #define NEXT_INTERFACE(ifr) ((struct ifreq *) \
330 ((char *) ifr + sizeof(ifr->ifr_name) + ifr->ifr_addr.sa_len))
331 #define IFREQ_SIZE(ifr) (sizeof(ifr->ifr_name) + ifr->ifr_addr.sa_len)
332 #else
333 #define NEXT_INTERFACE(ifr) (ifr + 1)
334 #define IFREQ_SIZE(ifr) sizeof(ifr[0])
335 #endif
337 /* ial_siocgif - determine IP addresses using ioctl(SIOCGIF*) */
339 static int ial_siocgif(INET_ADDR_LIST *addr_list,
340 INET_ADDR_LIST *mask_list,
341 int af)
343 const char *myname = "inet_addr_local[siocgif]";
344 struct in_addr addr;
345 struct ifconf ifc;
346 struct ifreq *ifr;
347 struct ifreq *ifr_mask;
348 struct ifreq *the_end;
349 int sock;
350 VSTRING *buf;
353 * Get the network interface list. XXX The socket API appears to have no
354 * function that returns the number of network interfaces, so we have to
355 * guess how much space is needed to store the result.
357 * On BSD-derived systems, ioctl SIOCGIFCONF returns as much information as
358 * possible, leaving it up to the application to repeat the request with
359 * a larger buffer if the result caused a tight fit.
361 * Other systems, such as Solaris 2.5, generate an EINVAL error when the
362 * buffer is too small for the entire result. Workaround: ignore EINVAL
363 * errors and repeat the request with a larger buffer. The downside is
364 * that the program can run out of memory due to a non-memory problem,
365 * making it more difficult than necessary to diagnose the real problem.
367 sock = ial_socket(af);
368 if (sock < 0)
369 return (0);
370 buf = vstring_alloc(1024);
371 for (;;) {
372 ifc.ifc_len = vstring_avail(buf);
373 ifc.ifc_buf = vstring_str(buf);
374 if (ioctl(sock, SIOCGIFCONF, (char *) &ifc) < 0) {
375 if (errno != EINVAL)
376 msg_fatal("%s: ioctl SIOCGIFCONF: %m", myname);
377 } else if (ifc.ifc_len < vstring_avail(buf) / 2)
378 break;
379 VSTRING_SPACE(buf, vstring_avail(buf) * 2);
382 the_end = (struct ifreq *) (ifc.ifc_buf + ifc.ifc_len);
383 for (ifr = ifc.ifc_req; ifr < the_end;) {
384 if (ifr->ifr_addr.sa_family != af) {
385 ifr = NEXT_INTERFACE(ifr);
386 continue;
388 if (af == AF_INET) {
389 addr = ((struct sockaddr_in *) & ifr->ifr_addr)->sin_addr;
390 if (addr.s_addr != INADDR_ANY) {
391 inet_addr_list_append(addr_list, &ifr->ifr_addr);
392 if (mask_list) {
393 ifr_mask = (struct ifreq *) mymalloc(IFREQ_SIZE(ifr));
394 memcpy((char *) ifr_mask, (char *) ifr, IFREQ_SIZE(ifr));
395 if (ioctl(sock, SIOCGIFNETMASK, ifr_mask) < 0)
396 msg_fatal("%s: ioctl SIOCGIFNETMASK: %m", myname);
399 * Note that this SIOCGIFNETMASK has truly screwed up the
400 * contents of sa_len/sa_family. We must fix this
401 * manually to have correct addresses. --dcs
403 ifr_mask->ifr_addr.sa_family = af;
404 #ifdef HAS_SA_LEN
405 ifr_mask->ifr_addr.sa_len = sizeof(struct sockaddr_in);
406 #endif
407 inet_addr_list_append(mask_list, &ifr_mask->ifr_addr);
408 myfree((char *) ifr_mask);
412 #ifdef HAS_IPV6
413 else if (af == AF_INET6) {
414 struct sockaddr *sa;
416 sa = SOCK_ADDR_PTR(&ifr->ifr_addr);
417 if (!(IN6_IS_ADDR_UNSPECIFIED(&SOCK_ADDR_IN6_ADDR(sa)))) {
418 inet_addr_list_append(addr_list, sa);
419 if (mask_list) {
420 /* XXX Assume /128 for everything */
421 struct sockaddr_in6 mask6;
423 mask6 = *SOCK_ADDR_IN6_PTR(sa);
424 memset((char *) &mask6.sin6_addr, ~0,
425 sizeof(mask6.sin6_addr));
426 inet_addr_list_append(mask_list, SOCK_ADDR_PTR(&mask6));
430 #endif
431 ifr = NEXT_INTERFACE(ifr);
433 vstring_free(buf);
434 (void) close(sock);
435 return (0);
438 #endif /* HAVE_SIOCGLIF */
440 #ifdef HAS_PROCNET_IFINET6
443 * Older Linux versions lack proper calls to retrieve IPv6 interface
444 * addresses. Instead, the addresses can be read from a file in the
445 * /proc tree. The most important issue with this approach however
446 * is that the /proc tree may not always be available, for example
447 * in a chrooted environment or in "hardened" (sic) installations.
450 /* ial_procnet_ifinet6 - determine IPv6 addresses using /proc/net/if_inet6 */
452 static int ial_procnet_ifinet6(INET_ADDR_LIST *addr_list,
453 INET_ADDR_LIST *mask_list)
455 const char *myname = "inet_addr_local[procnet_ifinet6]";
456 FILE *fp;
457 char buf[BUFSIZ];
458 unsigned plen;
459 VSTRING *addrbuf;
460 struct sockaddr_in6 addr;
461 struct sockaddr_in6 mask;
464 * Example: 00000000000000000000000000000001 01 80 10 80 lo
466 * Fields: address, interface index, prefix length, scope value
467 * (net/ipv6.h), interface flags (linux/rtnetlink.h), device name.
469 * FIX 200501 The IPv6 patch used fscanf(), which will hang on unexpected
470 * input. Use fgets() + sscanf() instead.
472 if ((fp = fopen(_PATH_PROCNET_IFINET6, "r")) != 0) {
473 addrbuf = vstring_alloc(MAI_V6ADDR_BYTES + 1);
474 memset((char *) &addr, 0, sizeof(addr));
475 addr.sin6_family = AF_INET6;
476 #ifdef HAS_SA_LEN
477 addr.sin6_len = sizeof(addr);
478 #endif
479 mask = addr;
480 while (fgets(buf, sizeof(buf), fp) != 0) {
481 /* 200501 hex_decode() is light-weight compared to getaddrinfo(). */
482 if (hex_decode(addrbuf, buf, MAI_V6ADDR_BYTES * 2) == 0
483 || sscanf(buf + MAI_V6ADDR_BYTES * 2, " %*x %x", &plen) != 1
484 || plen > MAI_V6ADDR_BITS) {
485 msg_warn("unexpected data in %s - skipping IPv6 configuration",
486 _PATH_PROCNET_IFINET6);
487 break;
489 /* vstring_str(addrbuf) has worst-case alignment. */
490 addr.sin6_addr = *(struct in6_addr *) vstring_str(addrbuf);
491 inet_addr_list_append(addr_list, SOCK_ADDR_PTR(&addr));
493 memset((char *) &mask.sin6_addr, ~0, sizeof(mask.sin6_addr));
494 mask_addr((unsigned char *) &mask.sin6_addr,
495 sizeof(mask.sin6_addr), plen);
496 inet_addr_list_append(mask_list, SOCK_ADDR_PTR(&mask));
498 vstring_free(addrbuf);
499 fclose(fp); /* FIX 200501 */
500 } else {
501 msg_warn("can't open %s (%m) - skipping IPv6 configuration",
502 _PATH_PROCNET_IFINET6);
504 return (0);
507 #endif /* HAS_PROCNET_IFINET6 */
509 /* inet_addr_local - find all IP addresses for this host */
511 int inet_addr_local(INET_ADDR_LIST *addr_list, INET_ADDR_LIST *mask_list,
512 unsigned *addr_family_list)
514 const char *myname = "inet_addr_local";
515 int initial_count = addr_list->used;
516 unsigned family;
517 int count;
519 while ((family = *addr_family_list++) != 0) {
522 * IP Version 4
524 if (family == AF_INET) {
525 count = addr_list->used;
526 #if defined(HAVE_GETIFADDRS)
527 ial_getifaddrs(addr_list, mask_list, AF_INET);
528 #elif defined (HAS_SIOCGLIF)
529 ial_siocglif(addr_list, mask_list, AF_INET);
530 #else
531 ial_siocgif(addr_list, mask_list, AF_INET);
532 #endif
533 if (msg_verbose)
534 msg_info("%s: configured %d IPv4 addresses",
535 myname, addr_list->used - count);
539 * IP Version 6
541 #ifdef HAS_IPV6
542 else if (family == AF_INET6) {
543 count = addr_list->used;
544 #if defined(HAVE_GETIFADDRS)
545 ial_getifaddrs(addr_list, mask_list, AF_INET6);
546 #elif defined(HAS_PROCNET_IFINET6)
547 ial_procnet_ifinet6(addr_list, mask_list);
548 #elif defined(HAS_SIOCGLIF)
549 ial_siocglif(addr_list, mask_list, AF_INET6);
550 #else
551 ial_siocgif(addr_list, mask_list, AF_INET6);
552 #endif
553 if (msg_verbose)
554 msg_info("%s: configured %d IPv6 addresses", myname,
555 addr_list->used - count);
557 #endif
560 * Something's not right.
562 else
563 msg_panic("%s: unknown address family %d", myname, family);
565 return (addr_list->used - initial_count);
568 #ifdef TEST
570 #include <string.h>
571 #include <vstream.h>
572 #include <msg_vstream.h>
573 #include <inet_proto.h>
575 int main(int unused_argc, char **argv)
577 INET_ADDR_LIST addr_list;
578 INET_ADDR_LIST mask_list;
579 MAI_HOSTADDR_STR hostaddr;
580 MAI_HOSTADDR_STR hostmask;
581 struct sockaddr *sa;
582 int i;
583 INET_PROTO_INFO *proto_info;
585 msg_vstream_init(argv[0], VSTREAM_ERR);
586 msg_verbose = 1;
588 proto_info = inet_proto_init(argv[0], INET_PROTO_NAME_ALL);
589 inet_addr_list_init(&addr_list);
590 inet_addr_list_init(&mask_list);
591 inet_addr_local(&addr_list, &mask_list, proto_info->ai_family_list);
593 if (addr_list.used == 0)
594 msg_fatal("cannot find any active network interfaces");
596 if (addr_list.used == 1)
597 msg_warn("found only one active network interface");
599 for (i = 0; i < addr_list.used; i++) {
600 sa = SOCK_ADDR_PTR(addr_list.addrs + i);
601 SOCKADDR_TO_HOSTADDR(SOCK_ADDR_PTR(sa), SOCK_ADDR_LEN(sa),
602 &hostaddr, (MAI_SERVPORT_STR *) 0, 0);
603 sa = SOCK_ADDR_PTR(mask_list.addrs + i);
604 SOCKADDR_TO_HOSTADDR(SOCK_ADDR_PTR(sa), SOCK_ADDR_LEN(sa),
605 &hostmask, (MAI_SERVPORT_STR *) 0, 0);
606 vstream_printf("%s/%s\n", hostaddr.buf, hostmask.buf);
607 vstream_fflush(VSTREAM_OUT);
609 inet_addr_list_free(&addr_list);
610 inet_addr_list_free(&mask_list);
611 return (0);
614 #endif