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