1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
|
/*++
/* NAME
/* inet_addr_local 3
/* SUMMARY
/* determine if IP address is local
/* SYNOPSIS
/* #include <inet_addr_local.h>
/*
/* int inet_addr_local(addr_list, mask_list)
/* INET_ADDR_LIST *addr_list;
/* INET_ADDR_LIST *mask_list;
/* DESCRIPTION
/* inet_addr_local() determines all active IP interface addresses
/* of the local system. Any address found is appended to the
/* specified address list. The result value is the number of
/* active interfaces found.
/*
/* The mask_list is either a null pointer, or it is a list that
/* receives the netmasks of the interface addresses that were found.
/* DIAGNOSTICS
/* Fatal errors: out of memory.
/* SEE ALSO
/* inet_addr_list(3) address list management
/* LICENSE
/* .ad
/* .fi
/* The Secure Mailer license must be distributed with this software.
/* AUTHOR(S)
/* Wietse Venema
/* IBM T.J. Watson Research
/* P.O. Box 704
/* Yorktown Heights, NY 10598, USA
/*--*/
/* System library. */
#include <sys_defs.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <net/if.h>
#include <sys/ioctl.h>
#include <arpa/inet.h>
#include <unistd.h>
#ifdef USE_SYS_SOCKIO_H
#include <sys/sockio.h>
#endif
#include <errno.h>
#include <string.h>
/* Utility library. */
#include <msg.h>
#include <mymalloc.h>
#include <vstring.h>
#include <inet_addr_list.h>
#include <inet_addr_local.h>
/*
* Support for variable-length addresses.
*/
#ifdef _SIZEOF_ADDR_IFREQ
#define NEXT_INTERFACE(ifr) ((struct ifreq *) \
((char *) ifr + _SIZEOF_ADDR_IFREQ(*ifr)))
#define IFREQ_SIZE(ifr) _SIZEOF_ADDR_IFREQ(*ifr)
#else
#ifdef HAS_SA_LEN
#define NEXT_INTERFACE(ifr) ((struct ifreq *) \
((char *) ifr + sizeof(ifr->ifr_name) + ifr->ifr_addr.sa_len))
#define IFREQ_SIZE(ifr) (sizeof(ifr->ifr_name) + ifr->ifr_addr.sa_len)
#else
#define NEXT_INTERFACE(ifr) (ifr + 1)
#define IFREQ_SIZE(ifr) sizeof(ifr[0])
#endif
#endif
/* inet_addr_local - find all IP addresses for this host */
int inet_addr_local(INET_ADDR_LIST *addr_list, INET_ADDR_LIST *mask_list)
{
char *myname = "inet_addr_local";
struct ifconf ifc;
struct ifreq *ifr;
struct ifreq *the_end;
int sock;
VSTRING *buf = vstring_alloc(1024);
int initial_count = addr_list->used;
struct in_addr addr;
struct ifreq *ifr_mask;
if ((sock = socket(PF_INET, SOCK_DGRAM, 0)) < 0)
msg_fatal("%s: socket: %m", myname);
/*
* Get the network interface list. XXX The socket API appears to have no
* function that returns the number of network interfaces, so we have to
* guess how much space is needed to store the result.
*
* On BSD-derived systems, ioctl SIOCGIFCONF returns as much information as
* possible, leaving it up to the application to repeat the request with
* a larger buffer if the result caused a tight fit.
*
* Other systems, such as Solaris 2.5, generate an EINVAL error when the
* buffer is too small for the entire result. Workaround: ignore EINVAL
* errors and repeat the request with a larger buffer. The downside is
* that the program can run out of memory due to a non-memory problem,
* making it more difficult than necessary to diagnose the real problem.
*/
for (;;) {
ifc.ifc_len = vstring_avail(buf);
ifc.ifc_buf = vstring_str(buf);
if (ioctl(sock, SIOCGIFCONF, (char *) &ifc) < 0) {
if (errno != EINVAL)
msg_fatal("%s: ioctl SIOCGIFCONF: %m", myname);
} else if (ifc.ifc_len < vstring_avail(buf) / 2)
break;
VSTRING_SPACE(buf, vstring_avail(buf) * 2);
}
/*
* Get the address of each IP network interface. According to BIND we
* must include interfaces that are down because the machine may still
* receive packets for that address (yes, via some other interface).
* Having no way to verify this claim on every machine, I will give them
* the benefit of the doubt.
*/
the_end = (struct ifreq *) (ifc.ifc_buf + ifc.ifc_len);
for (ifr = ifc.ifc_req; ifr < the_end;) {
if (ifr->ifr_addr.sa_family == AF_INET) { /* IP interface */
addr = ((struct sockaddr_in *) & ifr->ifr_addr)->sin_addr;
if (addr.s_addr != INADDR_ANY) { /* has IP address */
inet_addr_list_append(addr_list, &addr);
if (mask_list) {
ifr_mask = (struct ifreq *) mymalloc(IFREQ_SIZE(ifr));
memcpy((char *) ifr_mask, (char *) ifr, IFREQ_SIZE(ifr));
if (ioctl(sock, SIOCGIFNETMASK, ifr_mask) < 0)
msg_fatal("%s: ioctl SIOCGIFNETMASK: %m", myname);
addr = ((struct sockaddr_in *) & ifr_mask->ifr_addr)->sin_addr;
inet_addr_list_append(mask_list, &addr);
myfree((char *) ifr_mask);
}
}
}
ifr = NEXT_INTERFACE(ifr);
}
vstring_free(buf);
(void) close(sock);
return (addr_list->used - initial_count);
}
#ifdef TEST
#include <vstream.h>
#include <msg_vstream.h>
int main(int unused_argc, char **argv)
{
INET_ADDR_LIST addr_list;
INET_ADDR_LIST mask_list;
int i;
msg_vstream_init(argv[0], VSTREAM_ERR);
inet_addr_list_init(&addr_list);
inet_addr_list_init(&mask_list);
inet_addr_local(&addr_list, &mask_list);
if (addr_list.used == 0)
msg_fatal("cannot find any active network interfaces");
if (addr_list.used == 1)
msg_warn("found only one active network interface");
for (i = 0; i < addr_list.used; i++) {
vstream_printf("%s/", inet_ntoa(addr_list.addrs[i]));
vstream_printf("%s\n", inet_ntoa(mask_list.addrs[i]));
}
vstream_fflush(VSTREAM_OUT);
inet_addr_list_free(&addr_list);
inet_addr_list_free(&mask_list);
}
#endif
|