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