1/*
2 * Copyright (c) 1996, 1998-2005, 2007-2010
3 *	Todd C. Miller <Todd.Miller@courtesan.com>
4 *
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 *
17 * Sponsored in part by the Defense Advanced Research Projects
18 * Agency (DARPA) and Air Force Research Laboratory, Air Force
19 * Materiel Command, USAF, under agreement number F39502-99-1-0512.
20 */
21
22/*
23 * Suppress a warning w/ gcc on Digital UN*X.
24 * The system headers should really do this....
25 */
26#if defined(__osf__) && !defined(__cplusplus)
27struct mbuf;
28struct rtentry;
29#endif
30
31#include <config.h>
32
33#include <sys/types.h>
34#include <sys/socket.h>
35#include <sys/param.h>
36#include <sys/time.h>
37#include <sys/ioctl.h>
38#if defined(HAVE_SYS_SOCKIO_H) && !defined(SIOCGIFCONF)
39# include <sys/sockio.h>
40#endif
41#include <stdio.h>
42#ifdef STDC_HEADERS
43# include <stdlib.h>
44# include <stddef.h>
45#else
46# ifdef HAVE_STDLIB_H
47#  include <stdlib.h>
48# endif
49#endif /* STDC_HEADERS */
50#ifdef HAVE_STRING_H
51# if defined(HAVE_MEMORY_H) && !defined(STDC_HEADERS)
52#  include <memory.h>
53# endif
54# include <string.h>
55#endif /* HAVE_STRING_H */
56#ifdef HAVE_STRINGS_H
57# include <strings.h>
58#endif /* HAVE_STRINGS_H */
59#ifdef HAVE_UNISTD_H
60# include <unistd.h>
61#endif /* HAVE_UNISTD_H */
62#include <netdb.h>
63#include <errno.h>
64#ifdef _ISC
65# include <sys/stream.h>
66# include <sys/sioctl.h>
67# include <sys/stropts.h>
68# define STRSET(cmd, param, len) {strioctl.ic_cmd=(cmd);\
69				 strioctl.ic_dp=(param);\
70				 strioctl.ic_timout=0;\
71				 strioctl.ic_len=(len);}
72#endif /* _ISC */
73#ifdef _MIPS
74# include <net/soioctl.h>
75#endif /* _MIPS */
76#include <netinet/in.h>
77#include <arpa/inet.h>
78#include <net/if.h>
79#ifdef HAVE_GETIFADDRS
80# include <ifaddrs.h>
81#endif
82
83#include "sudo.h"
84#include "interfaces.h"
85
86/* Minix apparently lacks IFF_LOOPBACK */
87#ifndef IFF_LOOPBACK
88# define IFF_LOOPBACK	0
89#endif
90
91#ifdef HAVE_GETIFADDRS
92
93/*
94 * Allocate and fill in the interfaces global variable with the
95 * machine's ip addresses and netmasks.
96 */
97void
98load_interfaces()
99{
100    struct ifaddrs *ifa, *ifaddrs;
101    struct sockaddr_in *sin;
102#ifdef HAVE_STRUCT_IN6_ADDR
103    struct sockaddr_in6 *sin6;
104#endif
105    int i;
106
107    if (getifaddrs(&ifaddrs))
108	return;
109
110    /* Allocate space for the interfaces list. */
111    for (ifa = ifaddrs; ifa != NULL; ifa = ifa -> ifa_next) {
112	/* Skip interfaces marked "down" and "loopback". */
113	if (ifa->ifa_addr == NULL || !ISSET(ifa->ifa_flags, IFF_UP) ||
114	    ISSET(ifa->ifa_flags, IFF_LOOPBACK))
115	    continue;
116
117	switch(ifa->ifa_addr->sa_family) {
118	    case AF_INET:
119#ifdef HAVE_STRUCT_IN6_ADDR
120	    case AF_INET6:
121#endif
122		num_interfaces++;
123		break;
124	}
125    }
126    if (num_interfaces == 0)
127	return;
128    interfaces =
129	(struct interface *) emalloc2(num_interfaces, sizeof(struct interface));
130
131    /* Store the ip addr / netmask pairs. */
132    for (ifa = ifaddrs, i = 0; ifa != NULL; ifa = ifa -> ifa_next) {
133	/* Skip interfaces marked "down" and "loopback". */
134	if (ifa->ifa_addr == NULL || !ISSET(ifa->ifa_flags, IFF_UP) ||
135	    ISSET(ifa->ifa_flags, IFF_LOOPBACK))
136		continue;
137
138	switch(ifa->ifa_addr->sa_family) {
139	    case AF_INET:
140		sin = (struct sockaddr_in *)ifa->ifa_addr;
141		if (sin == NULL)
142		    continue;
143		memcpy(&interfaces[i].addr, &sin->sin_addr,
144		    sizeof(struct in_addr));
145		sin = (struct sockaddr_in *)ifa->ifa_netmask;
146		if (sin == NULL)
147		    continue;
148		memcpy(&interfaces[i].netmask, &sin->sin_addr,
149		    sizeof(struct in_addr));
150		interfaces[i].family = AF_INET;
151		i++;
152		break;
153#ifdef HAVE_STRUCT_IN6_ADDR
154	    case AF_INET6:
155		sin6 = (struct sockaddr_in6 *)ifa->ifa_addr;
156		if (sin6 == NULL)
157		    continue;
158		memcpy(&interfaces[i].addr, &sin6->sin6_addr,
159		    sizeof(struct in6_addr));
160		sin6 = (struct sockaddr_in6 *)ifa->ifa_netmask;
161		if (sin6 == NULL)
162		    continue;
163		memcpy(&interfaces[i].netmask, &sin6->sin6_addr,
164		    sizeof(struct in6_addr));
165		interfaces[i].family = AF_INET6;
166		i++;
167		break;
168#endif /* HAVE_STRUCT_IN6_ADDR */
169	}
170    }
171#ifdef HAVE_FREEIFADDRS
172    freeifaddrs(ifaddrs);
173#else
174    efree(ifaddrs);
175#endif
176}
177
178#elif defined(SIOCGIFCONF) && !defined(STUB_LOAD_INTERFACES)
179
180/*
181 * Allocate and fill in the interfaces global variable with the
182 * machine's ip addresses and netmasks.
183 */
184void
185load_interfaces()
186{
187    struct ifconf *ifconf;
188    struct ifreq *ifr, ifr_tmp;
189    struct sockaddr_in *sin;
190    int sock, n, i;
191    size_t len = sizeof(struct ifconf) + BUFSIZ;
192    char *previfname = "", *ifconf_buf = NULL;
193#ifdef _ISC
194    struct strioctl strioctl;
195#endif /* _ISC */
196
197    sock = socket(AF_INET, SOCK_DGRAM, 0);
198    if (sock < 0)
199	error(1, "cannot open socket");
200
201    /*
202     * Get interface configuration or return (leaving num_interfaces == 0)
203     */
204    for (;;) {
205	ifconf_buf = erealloc(ifconf_buf, len);
206	ifconf = (struct ifconf *) ifconf_buf;
207	ifconf->ifc_len = len - sizeof(struct ifconf);
208	ifconf->ifc_buf = (caddr_t) (ifconf_buf + sizeof(struct ifconf));
209
210#ifdef _ISC
211	STRSET(SIOCGIFCONF, (caddr_t) ifconf, len);
212	if (ioctl(sock, I_STR, (caddr_t) &strioctl) < 0) {
213#else
214	/* Note that some kernels return EINVAL if the buffer is too small */
215	if (ioctl(sock, SIOCGIFCONF, (caddr_t) ifconf) < 0 && errno != EINVAL) {
216#endif /* _ISC */
217	    efree(ifconf_buf);
218	    (void) close(sock);
219	    return;
220	}
221
222	/* Break out of loop if we have a big enough buffer. */
223	if (ifconf->ifc_len + sizeof(struct ifreq) < len)
224	    break;
225	len += BUFSIZ;
226    }
227
228    /* Allocate space for the maximum number of interfaces that could exist. */
229    if ((n = ifconf->ifc_len / sizeof(struct ifreq)) == 0)
230	return;
231    interfaces = (struct interface *) emalloc2(n, sizeof(struct interface));
232
233    /* For each interface, store the ip address and netmask. */
234    for (i = 0; i < ifconf->ifc_len; ) {
235	/* Get a pointer to the current interface. */
236	ifr = (struct ifreq *) &ifconf->ifc_buf[i];
237
238	/* Set i to the subscript of the next interface. */
239	i += sizeof(struct ifreq);
240#ifdef HAVE_SA_LEN
241	if (ifr->ifr_addr.sa_len > sizeof(ifr->ifr_addr))
242	    i += ifr->ifr_addr.sa_len - sizeof(struct sockaddr);
243#endif /* HAVE_SA_LEN */
244
245	/* Skip duplicates and interfaces with NULL addresses. */
246	sin = (struct sockaddr_in *) &ifr->ifr_addr;
247	if (sin->sin_addr.s_addr == 0 ||
248	    strncmp(previfname, ifr->ifr_name, sizeof(ifr->ifr_name) - 1) == 0)
249	    continue;
250
251	if (ifr->ifr_addr.sa_family != AF_INET)
252		continue;
253
254#ifdef SIOCGIFFLAGS
255	zero_bytes(&ifr_tmp, sizeof(ifr_tmp));
256	strncpy(ifr_tmp.ifr_name, ifr->ifr_name, sizeof(ifr_tmp.ifr_name) - 1);
257	if (ioctl(sock, SIOCGIFFLAGS, (caddr_t) &ifr_tmp) < 0)
258#endif
259	    ifr_tmp = *ifr;
260
261	/* Skip interfaces marked "down" and "loopback". */
262	if (!ISSET(ifr_tmp.ifr_flags, IFF_UP) ||
263	    ISSET(ifr_tmp.ifr_flags, IFF_LOOPBACK))
264		continue;
265
266	sin = (struct sockaddr_in *) &ifr->ifr_addr;
267	interfaces[num_interfaces].addr.ip4.s_addr = sin->sin_addr.s_addr;
268
269	/* Stash the name of the interface we saved. */
270	previfname = ifr->ifr_name;
271
272	/* Get the netmask. */
273	zero_bytes(&ifr_tmp, sizeof(ifr_tmp));
274	strncpy(ifr_tmp.ifr_name, ifr->ifr_name, sizeof(ifr_tmp.ifr_name) - 1);
275#ifdef SIOCGIFNETMASK
276#ifdef _ISC
277	STRSET(SIOCGIFNETMASK, (caddr_t) &ifr_tmp, sizeof(ifr_tmp));
278	if (ioctl(sock, I_STR, (caddr_t) &strioctl) == 0) {
279#else
280	if (ioctl(sock, SIOCGIFNETMASK, (caddr_t) &ifr_tmp) == 0) {
281#endif /* _ISC */
282	    sin = (struct sockaddr_in *) &ifr_tmp.ifr_addr;
283
284	    interfaces[num_interfaces].netmask.ip4.s_addr = sin->sin_addr.s_addr;
285	} else {
286#else
287	{
288#endif /* SIOCGIFNETMASK */
289	    if (IN_CLASSC(interfaces[num_interfaces].addr.ip4.s_addr))
290		interfaces[num_interfaces].netmask.ip4.s_addr = htonl(IN_CLASSC_NET);
291	    else if (IN_CLASSB(interfaces[num_interfaces].addr.ip4.s_addr))
292		interfaces[num_interfaces].netmask.ip4.s_addr = htonl(IN_CLASSB_NET);
293	    else
294		interfaces[num_interfaces].netmask.ip4.s_addr = htonl(IN_CLASSA_NET);
295	}
296
297	/* Only now can we be sure it was a good/interesting interface. */
298	interfaces[num_interfaces].family = AF_INET;
299	num_interfaces++;
300    }
301
302    /* If the expected size < real size, realloc the array. */
303    if (n != num_interfaces) {
304	if (num_interfaces != 0)
305	    interfaces = (struct interface *) erealloc3(interfaces,
306		num_interfaces, sizeof(struct interface));
307	else
308	    efree(interfaces);
309    }
310    efree(ifconf_buf);
311    (void) close(sock);
312}
313
314#else /* !SIOCGIFCONF || STUB_LOAD_INTERFACES */
315
316/*
317 * Stub function for those without SIOCGIFCONF
318 */
319void
320load_interfaces()
321{
322    return;
323}
324
325#endif /* SIOCGIFCONF && !STUB_LOAD_INTERFACES */
326
327void
328dump_interfaces()
329{
330    int i;
331#ifdef HAVE_STRUCT_IN6_ADDR
332    char addrbuf[INET6_ADDRSTRLEN], maskbuf[INET6_ADDRSTRLEN];
333#endif
334
335    puts("Local IP address and netmask pairs:");
336    for (i = 0; i < num_interfaces; i++) {
337	switch(interfaces[i].family) {
338	    case AF_INET:
339		printf("\t%s / ", inet_ntoa(interfaces[i].addr.ip4));
340		puts(inet_ntoa(interfaces[i].netmask.ip4));
341		break;
342#ifdef HAVE_STRUCT_IN6_ADDR
343	    case AF_INET6:
344		inet_ntop(AF_INET6, &interfaces[i].addr.ip6,
345		    addrbuf, sizeof(addrbuf));
346		inet_ntop(AF_INET6, &interfaces[i].netmask.ip6,
347		    maskbuf, sizeof(maskbuf));
348		printf("\t%s / %s\n", addrbuf, maskbuf);
349		break;
350#endif /* HAVE_STRUCT_IN6_ADDR */
351	}
352    }
353}
354