]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/amd/libamu/wire.c
This commit was generated by cvs2svn to compensate for changes in r168777,
[FreeBSD/FreeBSD.git] / contrib / amd / libamu / wire.c
1 /*
2  * Copyright (c) 1997-2004 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  *      %W% (Berkeley) %G%
40  *
41  * $Id: wire.c,v 1.8.2.10 2004/01/06 03:15:24 ezk Exp $
42  *
43  */
44
45 /*
46  * This function returns the subnet (address&netmask) for the primary network
47  * interface.  If the resulting address has an entry in the hosts file, the
48  * corresponding name is returned, otherwise the address is returned in
49  * standard internet format.
50  * As a side-effect, a list of local IP/net address is recorded for use
51  * by the islocalnet() function.
52  *
53  * Derived from original by Paul Anderson (23/4/90)
54  * Updates from Dirk Grunwald (11/11/91)
55  */
56
57 #ifdef HAVE_CONFIG_H
58 # include <config.h>
59 #endif /* HAVE_CONFIG_H */
60 #include <am_defs.h>
61 #include <amu.h>
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(1024);
105   if (!buf)
106     return NULL;
107
108   if (!localnets) {
109     sprintf(buf, "No networks.\n");
110     return buf;
111   }
112   /* check if there's more than one network */
113   if (!localnets->ip_next) {
114     sprintf(buf,
115             "Network: wire=\"%s\" (netnumber=%s).\n",
116             localnets->ip_net_name, localnets->ip_net_num);
117     return buf;
118   }
119   buf[0] = '\0';                /* null out buffer before appending */
120   for (i = 1, al = localnets; al; al = al->ip_next, i++) {
121     sprintf(s, "Network %d: wire=\"%s\" (netnumber=%s).\n",
122             i, al->ip_net_name, al->ip_net_num);
123     bufcount += strlen(s);
124     if (bufcount > buf_size) {
125       buf_size *= 2;
126       buf = xrealloc(buf, buf_size);
127     }
128     strcat(buf, s);
129   }
130   return buf;
131 }
132
133
134 static struct addrlist *
135 getwire_lookup(u_long address, u_long netmask, int ishost)
136 {
137   struct addrlist *al;
138   u_long subnet;
139   char netNumberBuf[64];
140   char buf[GFBUFLEN], *s;
141 #ifdef HAVE_IRS_H
142   struct nwent *np;
143 #else /* not HAVE_IRS_H */
144   struct netent *np;
145 #endif /* not HAVE_IRS_H */
146
147   /*
148    * Add interface to local network singly linked list
149    */
150   al = ALLOC(struct addrlist);
151   al->ip_addr = address;
152   al->ip_mask = netmask;
153   al->ip_net_name = NO_SUBNET; /* fill in a bit later */
154   al->ip_net_num = "0.0.0.0"; /* fill in a bit later */
155   al->ip_next = NULL;
156
157   subnet = ntohl(address) & ntohl(netmask);
158
159   if (ishost)
160     np = NULL;
161   else {
162 #ifdef HAVE_IRS_H
163     u_long mask = ntohl(netmask);
164     static struct irs_acc *irs_gen;
165     static struct irs_nw *irs_nw;
166     u_long net;
167     int maskbits;
168     u_char addr[4];
169
170     if (irs_gen == NULL)
171 #ifdef irs_irp_acc
172       /*
173        * bsdi4 added another argument to this function, without changing
174        * its name.  The irs_irp_acc is the one (hacky) distinguishing
175        * feature found in <irs.h> that can differentiate between bsdi3 and
176        * bsdi4.
177        */
178       irs_gen = irs_gen_acc("", NULL);
179 #else /* not irs_irp_acc */
180       irs_gen = irs_gen_acc("");
181 #endif /* not irs_irp_acc */
182     if (irs_gen && irs_nw == NULL)
183       irs_nw = (*irs_gen->nw_map)(irs_gen);
184     net = ntohl(address) & (mask = ntohl(netmask));
185     addr[0] = (0xFF000000 & net) >> 24;
186     addr[1] = (0x00FF0000 & net) >> 16;
187     addr[2] = (0x0000FF00 & net) >> 8;
188     addr[3] = (0x000000FF & net);
189     for (maskbits = 32; !(mask & 1); mask >>= 1)
190       maskbits--;
191     np = (*irs_nw->byaddr)(irs_nw, addr, maskbits, AF_INET);
192 #else /* not HAVE_IRS_H */
193     np = getnetbyaddr(subnet, AF_INET);
194     /*
195      * Some systems (IRIX 6.4) cannot getnetbyaddr on networks such as
196      * "128.59.16.0".  Instead, they need to look for the short form of
197      * the network, "128.59.16".  So if the first getnetbyaddr failed, we
198      * shift the subnet way from zeros and try again.
199      */
200     if (!np) {
201       u_long short_subnet = subnet;
202       while(short_subnet && (short_subnet & 0x000000ff) == 0)
203         short_subnet >>= 8;
204       np = getnetbyaddr(short_subnet, AF_INET);
205       if (np)
206         plog(XLOG_WARNING, "getnetbyaddr failed on 0x%x, succeeded on 0x%x",
207              (u_int) subnet, (u_int) short_subnet);
208     }
209 #endif /* not HAVE_IRS_H */
210   }
211
212   if ((subnet & 0xffffff) == 0) {
213     sprintf(netNumberBuf, "%lu", C(subnet >> 24));
214   } else if ((subnet & 0xffff) == 0) {
215     sprintf(netNumberBuf, "%lu.%lu",
216         C(subnet >> 24), C(subnet >> 16));
217   } else if ((subnet & 0xff) == 0) {
218     sprintf(netNumberBuf, "%lu.%lu.%lu",
219         C(subnet >> 24), C(subnet >> 16),
220         C(subnet >> 8));
221   } else {
222     sprintf(netNumberBuf, "%lu.%lu.%lu.%lu",
223         C(subnet >> 24), C(subnet >> 16),
224         C(subnet >> 8), C(subnet));
225   }
226
227   /* fill in network number (string) */
228   al->ip_net_num = strdup(netNumberBuf);
229
230   if (np != NULL)
231     s = np->n_name;
232   else {
233     struct hostent *hp;
234
235     subnet = address & netmask;
236     hp = gethostbyaddr((char *) &subnet, 4, AF_INET);
237     if (hp != NULL)
238       s = (char *) hp->h_name;
239     else
240       s = inet_dquad(buf, subnet);
241   }
242
243   /* fill in network name (string) */
244   al->ip_net_name = strdup(s);
245   /* Let's be cautious here about buffer overflows -Ion */
246   if (strlen(s) > MAXHOSTNAMELEN) {
247     al->ip_net_name[MAXHOSTNAMELEN] = '\0';
248     plog(XLOG_WARNING, "Long hostname %s truncated to %d characters",
249          s, MAXHOSTNAMELEN);
250   }
251
252   return (al);
253 }
254
255
256 /*
257  * Make a dotted quad from a 32bit IP address
258  * addr is in network byte order.
259  * sizeof(buf) needs to be at least 16.
260  */
261 char *
262 inet_dquad(char *buf, u_long addr)
263 {
264   addr = ntohl(addr);
265   sprintf(buf, "%ld.%ld.%ld.%ld",
266           ((addr >> 24) & 0xff),
267           ((addr >> 16) & 0xff),
268           ((addr >> 8) & 0xff),
269           ((addr >> 0) & 0xff));
270   return buf;
271 }
272
273
274 /*
275  * Determine whether a network is on a local network
276  * (addr) is in network byte order.
277  */
278 int
279 islocalnet(u_long addr)
280 {
281   addrlist *al;
282 #ifdef DEBUG
283   char buf[16];
284 #endif /* DEBUG */
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     plog(XLOG_INFO, "%s is on a remote network", inet_dquad(buf, addr));
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
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 = strdup(net), *maskstr;
320     u_long netnum, masknum = 0;
321     maskstr = strchr(netstr, '/');
322     maskstr++;
323     maskstr[-1] = '\0';         /* null terminate netstr */
324     if (*maskstr == '\0')       /* if empty string, make it NULL */
325       maskstr = NULL;
326     /* check if netmask uses a dotted-quad or bit-length, or not defined at all */
327     if (maskstr) {
328       if (strchr(maskstr, '.')) {
329         masknum = inet_addr(maskstr);
330         if (masknum < 0)                /* can be invalid (-1) or all-1s */
331           masknum = 0xffffffff;
332       } else if (NSTRCEQ(maskstr, "0x", 2)) {
333         masknum = strtoul(maskstr, NULL, 16);
334       } else {
335         int bits = atoi(maskstr);
336         if (bits < 0)
337           bits = 0;
338         if (bits > 32)
339           bits = 32;
340         masknum = 0xffffffff << (32-bits);
341       }
342     }
343     netnum = inet_addr(netstr); /* not checking return value, b/c -1 (0xffffffff) is valid */
344     XFREE(netstr);              /* netstr not needed any longer */
345
346     /* now check against each local interface */
347     for (al = localnets; al; al = al->ip_next) {
348       if ((al->ip_addr & (maskstr ? masknum : al->ip_mask)) == netnum)
349         return TRUE;
350     }
351   }
352
353   return FALSE;
354 }
355
356
357 #ifdef HAVE_GETIFADDRS
358 void
359 getwire(char **name1, char **number1)
360 {
361   addrlist *al = NULL, *tail = NULL;
362   struct ifaddrs *ifaddrs, *ifap;
363 #ifndef HAVE_STRUCT_IFADDRS_IFA_NEXT
364   int count = 0, i;
365 #endif /* not HAVE_STRUCT_IFADDRS_IFA_NEXT */
366
367   ifaddrs = NULL;
368 #ifdef HAVE_STRUCT_IFADDRS_IFA_NEXT
369   if (getifaddrs(&ifaddrs) < 0)
370     goto out;
371
372   for (ifap = ifaddrs; ifap != NULL; ifap = ifap->ifa_next) {
373 #else /* not HAVE_STRUCT_IFADDRS_IFA_NEXT */
374   if (getifaddrs(&ifaddrs, &count) < 0)
375     goto out;
376
377   for (i = 0,ifap = ifaddrs; i < count; ifap++, i++) {
378 #endif /* HAVE_STRUCT_IFADDRS_IFA_NEXT */
379
380     if (!ifap || !ifap->ifa_addr || ifap->ifa_addr->sa_family != AF_INET)
381       continue;
382
383     /*
384      * If the interface is a loopback, or its not running
385      * then ignore it.
386      */
387     if ((ifap->ifa_flags & IFF_LOOPBACK) != 0)
388       continue;
389     if ((ifap->ifa_flags & IFF_RUNNING) == 0)
390       continue;
391
392     if ((ifap->ifa_flags & IFF_POINTOPOINT) == 0)
393       al = getwire_lookup(S2IN(ifap->ifa_addr), S2IN(ifap->ifa_netmask), 0);
394     else
395       al = getwire_lookup(S2IN(ifap->ifa_dstaddr), 0xffffffff, 1);
396
397     /* append to the end of the list */
398     if (!localnets) {
399       localnets = tail = al;
400       tail->ip_next = NULL;
401     } else {
402       tail->ip_next = al;
403       tail = al;
404     }
405   }
406
407 out:
408   if (ifaddrs)
409     XFREE(ifaddrs);
410
411   if (localnets) {
412     *name1 = localnets->ip_net_name;
413     *number1 = localnets->ip_net_num;
414   } else {
415     *name1 = NO_SUBNET;
416     *number1 = "0.0.0.0";
417   }
418 }
419
420 #else /* not HAVE_GETIFADDRS */
421
422 #if defined(HAVE_STRUCT_IFREQ_IFR_ADDR) && defined(HAVE_STRUCT_SOCKADDR_SA_LEN)
423 # define SIZE(ifr)      (MAX((ifr)->ifr_addr.sa_len, sizeof((ifr)->ifr_addr)) + sizeof(ifr->ifr_name))
424 #else /* not defined(HAVE_STRUCT_IFREQ_IFR_ADDR) && defined(HAVE_STRUCT_SOCKADDR_SA_LEN) */
425 # define SIZE(ifr)      sizeof(struct ifreq)
426 #endif /* not defined(HAVE_STRUCT_IFREQ_IFR_ADDR) && defined(HAVE_STRUCT_SOCKADDR_SA_LEN) */
427
428 #define clist           (ifc.ifc_ifcu.ifcu_req)
429 #define count           (ifc.ifc_len/sizeof(struct ifreq))
430
431
432 void
433 getwire(char **name1, char **number1)
434 {
435   struct ifconf ifc;
436   struct ifreq *ifr, ifrpool;
437   caddr_t cp, cplim;
438   int fd = -1;
439   u_long address;
440   addrlist *al = NULL, *tail = NULL;
441   char buf[GFBUFLEN];
442 #if 0
443   u_long net;
444   u_long mask;
445   u_long subnetshift;
446   char buf[GFBUFLEN], *s;
447 #endif
448
449 #ifndef SIOCGIFFLAGS
450   /* if cannot get interface flags, return nothing */
451   plog(XLOG_ERROR, "getwire unable to get interface flags");
452   localnets = NULL;
453   return;
454 #endif /* not SIOCGIFFLAGS */
455
456   /*
457    * Get suitable socket
458    */
459   if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
460     goto out;
461
462   /*
463    * Fill in ifconf details
464    */
465   memset(&buf[0], 0, GFBUFLEN);
466   ifc.ifc_len = sizeof(buf);
467   ifc.ifc_buf = buf;
468
469   /*
470    * Get network interface configurations
471    */
472   if (ioctl(fd, SIOCGIFCONF, (caddr_t) & ifc) < 0)
473     goto out;
474
475   /*
476    * Upper bound on array
477    */
478   cplim = buf + ifc.ifc_len;
479
480   /*
481    * This is some magic to cope with both "traditional" and the
482    * new 4.4BSD-style struct sockaddrs.  The new structure has
483    * variable length and a size field to support longer addresses.
484    * AF_LINK is a new definition for 4.4BSD.
485    */
486
487   /*
488    * Scan the list looking for a suitable interface
489    */
490   for (cp = buf; cp < cplim; /* increment in the loop body */) {
491     memcpy(&ifrpool, cp, sizeof(ifrpool));
492     ifr = &ifrpool;
493     cp += SIZE(ifr);
494
495     if (ifr->ifr_addr.sa_family != AF_INET)
496       continue;
497
498     address = ((struct sockaddr_in *) &ifr->ifr_addr)->sin_addr.s_addr;
499
500     /*
501      * Get interface flags
502      */
503     if (ioctl(fd, SIOCGIFFLAGS, (caddr_t) ifr) < 0)
504       continue;
505
506     /*
507      * If the interface is a loopback, or its not running
508      * then ignore it.
509      */
510 #ifdef IFF_LOOPBACK
511     if ((ifr->ifr_flags & IFF_LOOPBACK) != 0)
512       continue;
513 #endif /* IFF_LOOPBACK */
514     /*
515      * Fix for 0.0.0.0 loopback on SunOS 3.X which defines IFF_ROUTE
516      * instead of IFF_LOOPBACK.
517      */
518 #ifdef IFF_ROUTE
519     if (ifr->ifr_flags == (IFF_UP|IFF_RUNNING))
520       continue;
521 #endif /* IFF_ROUTE */
522
523     /* if the interface is not UP or not RUNNING, skip it */
524     if ((ifr->ifr_flags & IFF_RUNNING) == 0 ||
525         (ifr->ifr_flags & IFF_UP) == 0)
526       continue;
527
528     if ((ifr->ifr_flags & IFF_POINTOPOINT) == 0) {
529       /*
530        * Get the netmask of this interface
531        */
532       if (ioctl(fd, SIOCGIFNETMASK, (caddr_t) ifr) < 0)
533         continue;
534
535       al = getwire_lookup(address, S2IN(&ifr->ifr_addr), 0);
536     } else
537       al = getwire_lookup(address, 0xffffffff, 1);
538
539     /* append to the end of the list */
540     if (!localnets) {
541       localnets = tail = al;
542       tail->ip_next = NULL;
543     } else {
544       tail->ip_next = al;
545       tail = al;
546     }
547   }
548
549 out:
550   if (fd >= 0)
551     close(fd);
552   if (localnets) {
553     *name1 = localnets->ip_net_name;
554     *number1 = localnets->ip_net_num;
555   } else {
556     *name1 = NO_SUBNET;
557     *number1 = "0.0.0.0";
558   }
559 }
560 #endif /* not HAVE_GETIFADDRS */