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