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