]> CyberLeo.Net >> Repos - FreeBSD/releng/9.2.git/blob - contrib/amd/libamu/wire.c
- Copy stable/9 to releng/9.2 as part of the 9.2-RELEASE cycle.
[FreeBSD/releng/9.2.git] / contrib / amd / libamu / wire.c
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 *
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 *
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 *
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
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
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
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
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 */