Merge branch 'vendor/GCC44'
[dragonfly.git] / contrib / amd / libamu / wire.c
1 /*
2  * Copyright (c) 1997-1999 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.5 1999/09/08 23:36:52 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
64 #ifdef HAVE_IFADDRS_H
65 #include <ifaddrs.h>
66 #endif /* HAVE_IFADDRS_H */
67
68 #ifdef HAVE_IRS_H
69 # include <irs.h>
70 #endif /* HAVE_IRS_H */
71
72 /*
73  * List of locally connected networks
74  */
75 typedef struct addrlist addrlist;
76 struct addrlist {
77   addrlist *ip_next;
78   u_long ip_addr;               /* address of network */
79   u_long ip_mask;
80   char *ip_net_num;             /* number of network */
81   char *ip_net_name;            /* name of network */
82 };
83 static addrlist *localnets = NULL;
84
85 #if defined(IFF_LOCAL_LOOPBACK) && !defined(IFF_LOOPBACK)
86 # define IFF_LOOPBACK   IFF_LOCAL_LOOPBACK
87 #endif /* defined(IFF_LOCAL_LOOPBACK) && !defined(IFF_LOOPBACK) */
88
89 #define C(x)            ((x) & 0xff)
90 #define GFBUFLEN        1024
91 #define S2IN(s)         (((struct sockaddr_in *)(s))->sin_addr.s_addr)
92
93
94 /* return malloc'ed buffer.  caller must free it */
95 char *
96 print_wires(void)
97 {
98   addrlist *al;
99   char s[256];
100   int i;
101   char *buf;
102   int bufcount = 0;
103   int buf_size = 1024;
104
105   buf = SALLOC(1024);
106   if (!buf)
107     return NULL;
108
109   if (!localnets) {
110     sprintf(buf, "No networks.\n");
111     return buf;
112   }
113   /* check if there's more than one network */
114   if (!localnets->ip_next) {
115     sprintf(buf,
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     sprintf(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     strcat(buf, s);
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       irs_gen = irs_gen_acc("");
173     if (irs_gen && irs_nw == NULL)
174       irs_nw = (*irs_gen->nw_map)(irs_gen);
175     net = ntohl(address) & (mask = ntohl(netmask));
176     addr[0] = (0xFF000000 & net) >> 24;
177     addr[1] = (0x00FF0000 & net) >> 16;
178     addr[2] = (0x0000FF00 & net) >> 8;
179     addr[3] = (0x000000FF & net);
180     for (maskbits = 32; !(mask & 1); mask >>= 1)
181       maskbits--;
182     np = (*irs_nw->byaddr)(irs_nw, addr, maskbits, AF_INET);
183 #else /* not HAVE_IRS_H */
184     np = getnetbyaddr(subnet, AF_INET);
185     /*
186      * Some systems (IRIX 6.4) cannot getnetbyaddr on networks such as
187      * "128.59.16.0".  Instead, they need to look for the short form of
188      * the network, "128.59.16".  So if the first getnetbyaddr failed, we
189      * shift the subnet way from zeros and try again.
190      */
191     if (!np) {
192       u_long short_subnet = subnet;
193       while(short_subnet && (short_subnet & 0x000000ff) == 0)
194         short_subnet >>= 8;
195       np = getnetbyaddr(short_subnet, AF_INET);
196       if (np)
197         plog(XLOG_WARNING, "getnetbyaddr failed on 0x%x, succeeded on 0x%x",
198              (u_int) subnet, (u_int) short_subnet);
199     }
200 #endif /* not HAVE_IRS_H */
201   }
202
203   if ((subnet & 0xffffff) == 0) {
204     sprintf(netNumberBuf, "%lu", C(subnet >> 24));
205   } else if ((subnet & 0xffff) == 0) {
206     sprintf(netNumberBuf, "%lu.%lu",
207         C(subnet >> 24), C(subnet >> 16));
208   } else if ((subnet & 0xff) == 0) {
209     sprintf(netNumberBuf, "%lu.%lu.%lu",
210         C(subnet >> 24), C(subnet >> 16),
211         C(subnet >> 8));
212   } else {
213     sprintf(netNumberBuf, "%lu.%lu.%lu.%lu",
214         C(subnet >> 24), C(subnet >> 16),
215         C(subnet >> 8), C(subnet));
216   }
217
218   /* fill in network number (string) */
219   al->ip_net_num = strdup(netNumberBuf);
220
221   if (np != NULL)
222     s = np->n_name;
223   else {
224     struct hostent *hp;
225
226     subnet = address & netmask;
227     hp = gethostbyaddr((char *) &subnet, 4, AF_INET);
228     if (hp != NULL)
229       s = (char *) hp->h_name;
230     else
231       s = inet_dquad(buf, subnet);
232   }
233
234   /* fill in network name (string) */
235   al->ip_net_name = strdup(s);
236
237   return (al);
238 }
239
240
241 /*
242  * Make a dotted quad from a 32bit IP address
243  * addr is in network byte order.
244  * sizeof(buf) needs to be at least 16.
245  */
246 char *
247 inet_dquad(char *buf, u_long addr)
248 {
249   addr = ntohl(addr);
250   sprintf(buf, "%ld.%ld.%ld.%ld",
251           ((addr >> 24) & 0xff),
252           ((addr >> 16) & 0xff),
253           ((addr >> 8) & 0xff),
254           ((addr >> 0) & 0xff));
255   return buf;
256 }
257
258
259 /*
260  * Determine whether a network is on a local network
261  * (addr) is in network byte order.
262  */
263 int
264 islocalnet(u_long addr)
265 {
266   addrlist *al;
267 #ifdef DEBUG
268   char buf[16];
269 #endif /* DEBUG */
270
271   for (al = localnets; al; al = al->ip_next)
272     if (((addr ^ al->ip_addr) & al->ip_mask) == 0)
273       return TRUE;
274
275 #ifdef DEBUG
276     plog(XLOG_INFO, "%s is on a remote network", inet_dquad(buf, addr));
277 #endif /* DEBUG */
278
279   return FALSE;
280 }
281
282
283 /*
284  * Determine whether a network name is one of the local networks
285  * of a host.
286  */
287 int
288 is_network_member(const char *net)
289 {
290   addrlist *al;
291
292   for (al = localnets; al; al = al->ip_next)
293     if (STREQ(net, al->ip_net_name) || STREQ(net, al->ip_net_num))
294       return TRUE;
295
296   return FALSE;
297 }
298
299
300 #ifdef HAVE_GETIFADDRS
301 void
302 getwire(char **name1, char **number1)
303 {
304   addrlist *al = NULL, *tail = NULL;
305   struct ifaddrs *ifaddrs, *ifap;
306 #ifndef HAVE_FIELD_STRUCT_IFADDRS_IFA_NEXT
307   int count = 0, i;
308 #endif /* not HAVE_FIELD_STRUCT_IFADDRS_IFA_NEXT */
309
310   ifaddrs = NULL;
311 #ifdef HAVE_FIELD_STRUCT_IFADDRS_IFA_NEXT
312   if (getifaddrs(&ifaddrs) < 0)
313     goto out;
314
315   for (ifap = ifaddrs; ifap != NULL; ifap = ifap->ifa_next) {
316 #else /* not HAVE_FIELD_STRUCT_IFADDRS_IFA_NEXT */
317   if (getifaddrs(&ifaddrs, &count) < 0)
318     goto out;
319
320   for (i = 0,ifap = ifaddrs; i < count; ifap++, i++) {
321 #endif /* HAVE_FIELD_STRUCT_IFADDRS_IFA_NEXT */
322
323     if (!ifap || !ifap->ifa_addr || ifap->ifa_addr->sa_family != AF_INET)
324       continue;
325
326     /*
327      * If the interface is a loopback, or its not running
328      * then ignore it.
329      */
330     if ((ifap->ifa_flags & IFF_LOOPBACK) != 0)
331       continue;
332     if ((ifap->ifa_flags & IFF_RUNNING) == 0)
333       continue;
334
335     if ((ifap->ifa_flags & IFF_POINTOPOINT) == 0)
336       al = getwire_lookup(S2IN(ifap->ifa_addr), S2IN(ifap->ifa_netmask), 0);
337     else
338       al = getwire_lookup(S2IN(ifap->ifa_dstaddr), 0xffffffff, 1);
339
340     /* append to the end of the list */
341     if (!localnets) {
342       localnets = tail = al;
343       tail->ip_next = NULL;
344     } else {
345       tail->ip_next = al;
346       tail = al;
347     }
348   }
349
350 out:
351   if (ifaddrs)
352     XFREE(ifaddrs);
353
354   if (localnets) {
355     *name1 = localnets->ip_net_name;
356     *number1 = localnets->ip_net_num;
357   } else {
358     *name1 = NO_SUBNET;
359     *number1 = "0.0.0.0";
360   }
361 }
362
363 #else /* not HAVE_GETIFADDRS */
364
365 #if defined(HAVE_FIELD_STRUCT_IFREQ_IFR_ADDR) && defined(HAVE_FIELD_STRUCT_SOCKADDR_SA_LEN)
366 # define SIZE(ifr)      (MAX((ifr)->ifr_addr.sa_len, sizeof((ifr)->ifr_addr)) + sizeof(ifr->ifr_name))
367 #else /* not defined(HAVE_FIELD_STRUCT_IFREQ_IFR_ADDR) && defined(HAVE_FIELD_STRUCT_SOCKADDR_SA_LEN) */
368 # define SIZE(ifr)      sizeof(struct ifreq)
369 #endif /* not defined(HAVE_FIELD_STRUCT_IFREQ_IFR_ADDR) && defined(HAVE_FIELD_STRUCT_SOCKADDR_SA_LEN) */
370
371 #define clist           (ifc.ifc_ifcu.ifcu_req)
372 #define count           (ifc.ifc_len/sizeof(struct ifreq))
373
374
375 void
376 getwire(char **name1, char **number1)
377 {
378   struct ifconf ifc;
379   struct ifreq *ifr;
380   caddr_t cp, cplim;
381   int fd = -1;
382   u_long address;
383   addrlist *al = NULL, *tail = NULL;
384   char buf[GFBUFLEN];
385 #if 0
386   u_long net;
387   u_long mask;
388   u_long subnetshift;
389   char buf[GFBUFLEN], *s;
390 #endif
391
392 #ifndef SIOCGIFFLAGS
393   /* if cannot get interface flags, return nothing */
394   plog(XLOG_ERROR, "getwire unable to get interface flags");
395   localnets = NULL;
396   return;
397 #endif /* not SIOCGIFFLAGS */
398
399   /*
400    * Get suitable socket
401    */
402   if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
403     goto out;
404
405   /*
406    * Fill in ifconf details
407    */
408   memset(&buf[0], 0, GFBUFLEN);
409   ifc.ifc_len = sizeof(buf);
410   ifc.ifc_buf = buf;
411
412   /*
413    * Get network interface configurations
414    */
415   if (ioctl(fd, SIOCGIFCONF, (caddr_t) & ifc) < 0)
416     goto out;
417
418   /*
419    * Upper bound on array
420    */
421   cplim = buf + ifc.ifc_len;
422
423   /*
424    * This is some magic to cope with both "traditional" and the
425    * new 4.4BSD-style struct sockaddrs.  The new structure has
426    * variable length and a size field to support longer addresses.
427    * AF_LINK is a new definition for 4.4BSD.
428    */
429
430   /*
431    * Scan the list looking for a suitable interface
432    */
433   for (cp = buf; cp < cplim; cp += SIZE(ifr)) {
434     ifr = (struct ifreq *) cp;
435
436     if (ifr->ifr_addr.sa_family != AF_INET)
437       continue;
438
439     address = ((struct sockaddr_in *) &ifr->ifr_addr)->sin_addr.s_addr;
440
441     /*
442      * Get interface flags
443      */
444     if (ioctl(fd, SIOCGIFFLAGS, (caddr_t) ifr) < 0)
445       continue;
446
447     /*
448      * If the interface is a loopback, or its not running
449      * then ignore it.
450      */
451 #ifdef IFF_LOOPBACK
452     if ((ifr->ifr_flags & IFF_LOOPBACK) != 0)
453       continue;
454 #endif /* IFF_LOOPBACK */
455     /*
456      * Fix for 0.0.0.0 loopback on SunOS 3.X which defines IFF_ROUTE
457      * instead of IFF_LOOPBACK.
458      */
459 #ifdef IFF_ROUTE
460     if (ifr->ifr_flags == (IFF_UP|IFF_RUNNING))
461       continue;
462 #endif /* IFF_ROUTE */
463
464     /* if the interface is not UP or not RUNNING, skip it */
465     if ((ifr->ifr_flags & IFF_RUNNING) == 0 ||
466         (ifr->ifr_flags & IFF_UP) == 0)
467       continue;
468
469     if ((ifr->ifr_flags & IFF_POINTOPOINT) == 0) {
470       /*
471        * Get the netmask of this interface
472        */
473       if (ioctl(fd, SIOCGIFNETMASK, (caddr_t) ifr) < 0)
474         continue;
475
476       al = getwire_lookup(address, S2IN(&ifr->ifr_addr), 0);
477     } else
478       al = getwire_lookup(address, 0xffffffff, 1);
479
480     /* append to the end of the list */
481     if (!localnets) {
482       localnets = tail = al;
483       tail->ip_next = NULL;
484     } else {
485       tail->ip_next = al;
486       tail = al;
487     }
488   }
489
490 out:
491   if (fd >= 0)
492     close(fd);
493   if (localnets) {
494     *name1 = localnets->ip_net_name;
495     *number1 = localnets->ip_net_num;
496   } else {
497     *name1 = NO_SUBNET;
498     *number1 = "0.0.0.0";
499   }
500 }
501 #endif /* not HAVE_GETIFADDRS */