1 /*
2 * Copyright (c) 1997-2006 Erez Zadok
3 * Copyright (c) 1990 Jan-Simon Pendry
4 * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
5 * Copyright (c) 1990 The Regents of the University of California.
6 * All rights reserved.
7 *
8 * This code is derived from software contributed to Berkeley by
9 * Jan-Simon Pendry at Imperial College, London.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 * 3. All advertising materials mentioning features or use of this software
20 * must display the following acknowledgment:
21 * This product includes software developed by the University of
22 * California, Berkeley and its contributors.
23 * 4. Neither the name of the University nor the names of its contributors
24 * may be used to endorse or promote products derived from this software
25 * without specific prior written permission.
26 *
27 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
28 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
31 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37 * SUCH DAMAGE.
38 *
39 *
40 * File: am-utils/libamu/wire.c
41 *
42 */
43
44 /*
45 * This function returns the subnet (address&netmask) for the primary network
46 * interface. If the resulting address has an entry in the hosts file, the
47 * corresponding name is returned, otherwise the address is returned in
48 * standard internet format.
49 * As a side-effect, a list of local IP/net address is recorded for use
50 * by the islocalnet() function.
51 *
52 * Derived from original by Paul Anderson (23/4/90)
53 * Updates from Dirk Grunwald (11/11/91)
54 */
55
56 #ifdef HAVE_CONFIG_H
57 # include <config.h>
58 #endif /* HAVE_CONFIG_H */
59 #include <am_defs.h>
60 #include <amu.h>
61
62
63 #ifdef HAVE_IFADDRS_H
64 #include <ifaddrs.h>
65 #endif /* HAVE_IFADDRS_H */
66
67 #ifdef HAVE_IRS_H
68 # include <irs.h>
69 #endif /* HAVE_IRS_H */
70
71 /*
72 * List of locally connected networks
73 */
74 typedef struct addrlist addrlist;
75 struct addrlist {
76 addrlist *ip_next;
77 u_long ip_addr; /* address of network */
78 u_long ip_mask;
79 char *ip_net_num; /* number of network */
80 char *ip_net_name; /* name of network */
81 };
82 static addrlist *localnets = NULL;
83
84 #if defined(IFF_LOCAL_LOOPBACK) && !defined(IFF_LOOPBACK)
85 # define IFF_LOOPBACK IFF_LOCAL_LOOPBACK
86 #endif /* defined(IFF_LOCAL_LOOPBACK) && !defined(IFF_LOOPBACK) */
87
88 #define C(x) ((x) & 0xff)
89 #define GFBUFLEN 1024
90 #define S2IN(s) (((struct sockaddr_in *)(s))->sin_addr.s_addr)
91
92
93 /* return malloc'ed buffer. caller must free it */
94 char *
print_wires(void)95 print_wires(void)
96 {
97 addrlist *al;
98 char s[256];
99 int i;
100 char *buf;
101 int bufcount = 0;
102 int buf_size = 1024;
103
104 buf = SALLOC(buf_size); /* initial allocation (may grow!) */
105 if (!buf)
106 return NULL;
107
108 if (!localnets) {
109 xstrlcpy(buf, "No networks\n", buf_size);
110 return buf;
111 }
112 /* check if there's more than one network */
113 if (!localnets->ip_next) {
114 /* use buf_size for sizeof(buf) because of the realloc() below */
115 xsnprintf(buf, buf_size,
116 "Network: wire=\"%s\" (netnumber=%s).\n",
117 localnets->ip_net_name, localnets->ip_net_num);
118 return buf;
119 }
120 buf[0] = '\0'; /* null out buffer before appending */
121 for (i = 1, al = localnets; al; al = al->ip_next, i++) {
122 xsnprintf(s, sizeof(s), "Network %d: wire=\"%s\" (netnumber=%s).\n",
123 i, al->ip_net_name, al->ip_net_num);
124 bufcount += strlen(s);
125 if (bufcount > buf_size) {
126 buf_size *= 2;
127 buf = xrealloc(buf, buf_size);
128 }
129 xstrlcat(buf, s, buf_size);
130 }
131 return buf;
132 }
133
134
135 static struct addrlist *
getwire_lookup(u_long address,u_long netmask,int ishost)136 getwire_lookup(u_long address, u_long netmask, int ishost)
137 {
138 struct addrlist *al;
139 u_long subnet;
140 char netNumberBuf[64];
141 char buf[GFBUFLEN], *s;
142 #ifdef HAVE_IRS_H
143 struct nwent *np;
144 #else /* not HAVE_IRS_H */
145 struct netent *np;
146 #endif /* not HAVE_IRS_H */
147
148 /*
149 * Add interface to local network singly linked list
150 */
151 al = ALLOC(struct addrlist);
152 al->ip_addr = address;
153 al->ip_mask = netmask;
154 al->ip_net_name = NO_SUBNET; /* fill in a bit later */
155 al->ip_net_num = "0.0.0.0"; /* fill in a bit later */
156 al->ip_next = NULL;
157
158 subnet = ntohl(address) & ntohl(netmask);
159
160 if (ishost)
161 np = NULL;
162 else {
163 #ifdef HAVE_IRS_H
164 u_long mask = ntohl(netmask);
165 static struct irs_acc *irs_gen;
166 static struct irs_nw *irs_nw;
167 u_long net;
168 int maskbits;
169 u_char addr[4];
170
171 if (irs_gen == NULL)
172 #ifdef irs_irp_acc
173 /*
174 * bsdi4 added another argument to this function, without changing
175 * its name. The irs_irp_acc is the one (hacky) distinguishing
176 * feature found in <irs.h> that can differentiate between bsdi3 and
177 * bsdi4.
178 */
179 irs_gen = irs_gen_acc("", NULL);
180 #else /* not irs_irp_acc */
181 irs_gen = irs_gen_acc("");
182 #endif /* not irs_irp_acc */
183 if (irs_gen && irs_nw == NULL)
184 irs_nw = (*irs_gen->nw_map)(irs_gen);
185 net = ntohl(address) & (mask = ntohl(netmask));
186 addr[0] = (0xFF000000 & net) >> 24;
187 addr[1] = (0x00FF0000 & net) >> 16;
188 addr[2] = (0x0000FF00 & net) >> 8;
189 addr[3] = (0x000000FF & net);
190 for (maskbits = 32; !(mask & 1); mask >>= 1)
191 maskbits--;
192 np = (*irs_nw->byaddr)(irs_nw, addr, maskbits, AF_INET);
193 #else /* not HAVE_IRS_H */
194 np = getnetbyaddr(subnet, AF_INET);
195 /*
196 * Some systems (IRIX 6.4) cannot getnetbyaddr on networks such as
197 * "128.59.16.0". Instead, they need to look for the short form of
198 * the network, "128.59.16". So if the first getnetbyaddr failed, we
199 * shift the subnet way from zeros and try again.
200 */
201 if (!np) {
202 u_long short_subnet = subnet;
203 while (short_subnet && (short_subnet & 0x000000ff) == 0)
204 short_subnet >>= 8;
205 np = getnetbyaddr(short_subnet, AF_INET);
206 if (np)
207 plog(XLOG_WARNING, "getnetbyaddr failed on 0x%x, succeeded on 0x%x",
208 (u_int) subnet, (u_int) short_subnet);
209 }
210 #endif /* not HAVE_IRS_H */
211 }
212
213 if ((subnet & 0xffffff) == 0) {
214 xsnprintf(netNumberBuf, sizeof(netNumberBuf), "%lu", C(subnet >> 24));
215 } else if ((subnet & 0xffff) == 0) {
216 xsnprintf(netNumberBuf, sizeof(netNumberBuf), "%lu.%lu",
217 C(subnet >> 24), C(subnet >> 16));
218 } else if ((subnet & 0xff) == 0) {
219 xsnprintf(netNumberBuf, sizeof(netNumberBuf), "%lu.%lu.%lu",
220 C(subnet >> 24), C(subnet >> 16),
221 C(subnet >> 8));
222 } else {
223 xsnprintf(netNumberBuf, sizeof(netNumberBuf), "%lu.%lu.%lu.%lu",
224 C(subnet >> 24), C(subnet >> 16),
225 C(subnet >> 8), C(subnet));
226 }
227
228 /* fill in network number (string) */
229 al->ip_net_num = strdup(netNumberBuf);
230
231 if (np != NULL)
232 s = np->n_name;
233 else {
234 struct hostent *hp;
235
236 subnet = address & netmask;
237 hp = gethostbyaddr((char *) &subnet, 4, AF_INET);
238 if (hp != NULL)
239 s = (char *) hp->h_name;
240 else
241 s = inet_dquad(buf, sizeof(buf), subnet);
242 }
243
244 /* fill in network name (string) */
245 al->ip_net_name = strdup(s);
246 /* Let's be cautious here about buffer overflows -Ion */
247 if (strlen(s) > MAXHOSTNAMELEN) {
248 al->ip_net_name[MAXHOSTNAMELEN] = '\0';
249 plog(XLOG_WARNING, "Long hostname %s truncated to %d characters",
250 s, MAXHOSTNAMELEN);
251 }
252
253 return (al);
254 }
255
256
257 /*
258 * Make a dotted quad from a 32bit IP address
259 * addr is in network byte order.
260 * sizeof(buf) needs to be at least 16.
261 */
262 char *
inet_dquad(char * buf,size_t l,u_long addr)263 inet_dquad(char *buf, size_t l, u_long addr)
264 {
265 addr = ntohl(addr);
266 xsnprintf(buf, l, "%ld.%ld.%ld.%ld",
267 ((addr >> 24) & 0xff),
268 ((addr >> 16) & 0xff),
269 ((addr >> 8) & 0xff),
270 ((addr >> 0) & 0xff));
271 return buf;
272 }
273
274
275 /*
276 * Determine whether a network is on a local network
277 * (addr) is in network byte order.
278 */
279 int
islocalnet(u_long addr)280 islocalnet(u_long addr)
281 {
282 addrlist *al;
283
284 for (al = localnets; al; al = al->ip_next)
285 if (((addr ^ al->ip_addr) & al->ip_mask) == 0)
286 return TRUE;
287
288 #ifdef DEBUG
289 {
290 char buf[16];
291 plog(XLOG_INFO, "%s is on a remote network",
292 inet_dquad(buf, sizeof(buf), addr));
293 }
294 #endif /* DEBUG */
295
296 return FALSE;
297 }
298
299
300 /*
301 * Determine whether a network name is one of the local networks
302 * of a host.
303 */
304 int
is_network_member(const char * net)305 is_network_member(const char *net)
306 {
307 addrlist *al;
308
309 /*
310 * If the network name string does not contain a '/', use old behavior.
311 * If it does contain a '/' then interpret the string as a network/netmask
312 * pair. If "netmask" doesn't exist, use the interface's own netmask.
313 * Also support fully explicit netmasks such as 255.255.255.0 as well as
314 * bit-length netmask such as /24 (hex formats such 0xffffff00 work too).
315 */
316 if (strchr(net, '/') == NULL) {
317 for (al = localnets; al; al = al->ip_next)
318 if (STREQ(net, al->ip_net_name) || STREQ(net, al->ip_net_num))
319 return TRUE;
320 } else {
321 char *netstr = strdup(net), *maskstr;
322 u_long netnum, masknum = 0;
323 maskstr = strchr(netstr, '/');
324 maskstr[0] = '\0'; /* null terminate netstr */
325 maskstr++;
326 if (*maskstr == '\0') /* if empty string, make it NULL */
327 maskstr = NULL;
328 /* check if netmask uses a dotted-quad or bit-length, or not defined at all */
329 if (maskstr) {
330 if (strchr(maskstr, '.')) {
331 /* XXX: inet_addr is obsolste, convert to inet_aton() */
332 masknum = inet_addr(maskstr);
333 if (masknum == INADDR_NONE) /* can be invalid (-1) or all-1s */
334 masknum = 0xffffffff;
335 } else if (NSTRCEQ(maskstr, "0x", 2)) {
336 masknum = strtoul(maskstr, NULL, 16);
337 } else {
338 int bits = atoi(maskstr);
339 if (bits < 0)
340 bits = 0;
341 if (bits > 32)
342 bits = 32;
343 masknum = 0xffffffff << (32-bits);
344 }
345 }
346 netnum = inet_addr(netstr); /* not checking return value, b/c -1 (0xffffffff) is valid */
347 XFREE(netstr); /* netstr not needed any longer */
348
349 /* now check against each local interface */
350 for (al = localnets; al; al = al->ip_next) {
351 if ((al->ip_addr & (maskstr ? masknum : al->ip_mask)) == netnum)
352 return TRUE;
353 }
354 }
355
356 return FALSE;
357 }
358
359
360 /*
361 * Determine whether a IP address (netnum) is one of the local interfaces,
362 * returns TRUE/FALSE.
363 * Does not include the loopback interface: caller needs to check that.
364 */
365 int
is_interface_local(u_long netnum)366 is_interface_local(u_long netnum)
367 {
368 addrlist *al;
369
370 for (al = localnets; al; al = al->ip_next) {
371 if (al->ip_addr == netnum)
372 return TRUE;
373 }
374 return FALSE;
375 }
376
377
378 #ifdef HAVE_GETIFADDRS
379 void
getwire(char ** name1,char ** number1)380 getwire(char **name1, char **number1)
381 {
382 addrlist *al = NULL, *tail = NULL;
383 struct ifaddrs *ifaddrs, *ifap;
384 #ifndef HAVE_STRUCT_IFADDRS_IFA_NEXT
385 int count = 0, i;
386 #endif /* not HAVE_STRUCT_IFADDRS_IFA_NEXT */
387
388 ifaddrs = NULL;
389 #ifdef HAVE_STRUCT_IFADDRS_IFA_NEXT
390 if (getifaddrs(&ifaddrs) < 0)
391 goto out;
392
393 for (ifap = ifaddrs; ifap != NULL; ifap = ifap->ifa_next) {
394 #else /* not HAVE_STRUCT_IFADDRS_IFA_NEXT */
395 if (getifaddrs(&ifaddrs, &count) < 0)
396 goto out;
397
398 for (i = 0,ifap = ifaddrs; i < count; ifap++, i++) {
399 #endif /* HAVE_STRUCT_IFADDRS_IFA_NEXT */
400
401 if (!ifap || !ifap->ifa_addr || ifap->ifa_addr->sa_family != AF_INET)
402 continue;
403
404 /*
405 * If the interface is the loopback, or it's not running,
406 * then ignore it.
407 */
408 if (S2IN(ifap->ifa_addr) == htonl(INADDR_LOOPBACK))
409 continue;
410 if ((ifap->ifa_flags & IFF_RUNNING) == 0)
411 continue;
412
413 if ((ifap->ifa_flags & IFF_POINTOPOINT) == 0)
414 al = getwire_lookup(S2IN(ifap->ifa_addr), S2IN(ifap->ifa_netmask), 0);
415 else
416 al = getwire_lookup(S2IN(ifap->ifa_dstaddr), 0xffffffff, 1);
417
418 /* append to the end of the list */
419 if (!localnets || tail == NULL) {
420 localnets = tail = al;
421 tail->ip_next = NULL;
422 } else {
423 tail->ip_next = al;
424 tail = al;
425 }
426 }
427
428 out:
429 if (ifaddrs)
430 XFREE(ifaddrs);
431
432 if (localnets) {
433 *name1 = localnets->ip_net_name;
434 *number1 = localnets->ip_net_num;
435 } else {
436 *name1 = NO_SUBNET;
437 *number1 = "0.0.0.0";
438 }
439 }
440
441 #else /* not HAVE_GETIFADDRS */
442
443 #if defined(HAVE_STRUCT_IFREQ_IFR_ADDR) && defined(HAVE_STRUCT_SOCKADDR_SA_LEN)
444 # define SIZE(ifr) (MAX((ifr)->ifr_addr.sa_len, sizeof((ifr)->ifr_addr)) + sizeof(ifr->ifr_name))
445 #else /* not defined(HAVE_STRUCT_IFREQ_IFR_ADDR) && defined(HAVE_STRUCT_SOCKADDR_SA_LEN) */
446 # define SIZE(ifr) sizeof(struct ifreq)
447 #endif /* not defined(HAVE_STRUCT_IFREQ_IFR_ADDR) && defined(HAVE_STRUCT_SOCKADDR_SA_LEN) */
448
449 #define clist (ifc.ifc_ifcu.ifcu_req)
450 #define count (ifc.ifc_len/sizeof(struct ifreq))
451
452
453 void
454 getwire(char **name1, char **number1)
455 {
456 struct ifconf ifc;
457 struct ifreq *ifr, ifrpool;
458 caddr_t cp, cplim;
459 int fd = -1;
460 u_long address;
461 addrlist *al = NULL, *tail = NULL;
462 char buf[GFBUFLEN];
463
464 #ifndef SIOCGIFFLAGS
465 /* if cannot get interface flags, return nothing */
466 plog(XLOG_ERROR, "getwire unable to get interface flags");
467 localnets = NULL;
468 return;
469 #endif /* not SIOCGIFFLAGS */
470
471 /*
472 * Get suitable socket
473 */
474 if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
475 goto out;
476
477 /*
478 * Fill in ifconf details
479 */
480 memset(&buf[0], 0, GFBUFLEN);
481 ifc.ifc_len = sizeof(buf);
482 ifc.ifc_buf = buf;
483
484 /*
485 * Get network interface configurations
486 */
487 if (ioctl(fd, SIOCGIFCONF, (caddr_t) & ifc) < 0)
488 goto out;
489
490 /*
491 * Upper bound on array
492 */
493 cplim = buf + ifc.ifc_len;
494
495 /*
496 * This is some magic to cope with both "traditional" and the
497 * new 4.4BSD-style struct sockaddrs. The new structure has
498 * variable length and a size field to support longer addresses.
499 * AF_LINK is a new definition for 4.4BSD.
500 */
501
502 /*
503 * Scan the list looking for a suitable interface
504 */
505 for (cp = buf; cp < cplim; /* increment in the loop body */) {
506 memcpy(&ifrpool, cp, sizeof(ifrpool));
507 ifr = &ifrpool;
508 cp += SIZE(ifr);
509
510 if (ifr->ifr_addr.sa_family != AF_INET)
511 continue;
512
513 address = ((struct sockaddr_in *) &ifr->ifr_addr)->sin_addr.s_addr;
514
515 /*
516 * Get interface flags
517 */
518 if (ioctl(fd, SIOCGIFFLAGS, (caddr_t) ifr) < 0)
519 continue;
520
521 /*
522 * If the interface is the loopback, or it's not running,
523 * then ignore it.
524 */
525 if (address == htonl(INADDR_LOOPBACK))
526 continue;
527 /*
528 * Fix for 0.0.0.0 loopback on SunOS 3.X which defines IFF_ROUTE
529 * instead of IFF_LOOPBACK.
530 */
531 #ifdef IFF_ROUTE
532 if (ifr->ifr_flags == (IFF_UP|IFF_RUNNING))
533 continue;
534 #endif /* IFF_ROUTE */
535
536 /* if the interface is not UP or not RUNNING, skip it */
537 if ((ifr->ifr_flags & IFF_RUNNING) == 0 ||
538 (ifr->ifr_flags & IFF_UP) == 0)
539 continue;
540
541 if ((ifr->ifr_flags & IFF_POINTOPOINT) == 0) {
542 /*
543 * Get the netmask of this interface
544 */
545 if (ioctl(fd, SIOCGIFNETMASK, (caddr_t) ifr) < 0)
546 continue;
547
548 al = getwire_lookup(address, S2IN(&ifr->ifr_addr), 0);
549 } else
550 al = getwire_lookup(address, 0xffffffff, 1);
551
552 /* append to the end of the list */
553 if (!localnets) {
554 localnets = tail = al;
555 tail->ip_next = NULL;
556 } else {
557 tail->ip_next = al;
558 tail = al;
559 }
560 }
561
562 out:
563 if (fd >= 0)
564 close(fd);
565 if (localnets) {
566 *name1 = localnets->ip_net_name;
567 *number1 = localnets->ip_net_num;
568 } else {
569 *name1 = NO_SUBNET;
570 *number1 = "0.0.0.0";
571 }
572 }
573 #endif /* not HAVE_GETIFADDRS */
574