1/*	$NetBSD: wire.c,v 1.1.1.2 2009/03/20 20:26:56 christos Exp $	*/
2
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.
9 *
10 * This code is derived from software contributed to Berkeley by
11 * Jan-Simon Pendry at Imperial College, London.
12 *
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.
28 *
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.
40 *
41 *
42 * File: am-utils/libamu/wire.c
43 *
44 */
45
46/*
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.
53 *
54 * Derived from original by Paul Anderson (23/4/90)
55 * Updates from Dirk Grunwald (11/11/91)
56 */
57
58#ifdef HAVE_CONFIG_H
59# include <config.h>
60#endif /* HAVE_CONFIG_H */
61#include <am_defs.h>
62#include <amu.h>
63
64
65#ifdef HAVE_IFADDRS_H
66#include <ifaddrs.h>
67#endif /* HAVE_IFADDRS_H */
68
69#ifdef HAVE_IRS_H
70# include <irs.h>
71#endif /* HAVE_IRS_H */
72
73/*
74 * List of locally connected networks
75 */
76typedef struct addrlist addrlist;
77struct 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 */
83};
84static addrlist *localnets = NULL;
85
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) */
89
90#define C(x)		((x) & 0xff)
91#define GFBUFLEN	1024
92#define	S2IN(s)		(((struct sockaddr_in *)(s))->sin_addr.s_addr)
93
94
95/* return malloc'ed buffer.  caller must free it */
96char *
97print_wires(void)
98{
99  addrlist *al;
100  char s[256];
101  int i;
102  char *buf;
103  int bufcount = 0;
104  int buf_size = 1024;
105
106  buf = SALLOC(buf_size);	/* initial allocation (may grow!) */
107  if (!buf)
108    return NULL;
109
110  if (!localnets) {
111    xstrlcpy(buf, "No networks\n", buf_size);
112    return buf;
113  }
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;
121  }
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);
130    }
131    xstrlcat(buf, s, buf_size);
132  }
133  return buf;
134}
135
136
137static struct addrlist *
138getwire_lookup(u_long address, u_long netmask, int ishost)
139{
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 */
149
150  /*
151   * Add interface to local network singly linked list
152   */
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;
159
160  subnet = ntohl(address) & ntohl(netmask);
161
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];
172
173    if (irs_gen == NULL)
174#ifdef irs_irp_acc
175      /*
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.
180       */
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);
197    /*
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.
202     */
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);
211    }
212#endif /* not HAVE_IRS_H */
213  }
214
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));
228  }
229
230  /* fill in network number (string) */
231  al->ip_net_num = strdup(netNumberBuf);
232
233  if (np != NULL)
234    s = np->n_name;
235  else {
236    struct hostent *hp;
237
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);
244  }
245
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);
253  }
254
255  return (al);
256}
257
258
259/*
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.
263 */
264char *
265inet_dquad(char *buf, size_t l, u_long addr)
266{
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;
274}
275
276
277/*
278 * Determine whether a network is on a local network
279 * (addr) is in network byte order.
280 */
281int
282islocalnet(u_long addr)
283{
284  addrlist *al;
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  {
292    char buf[16];
293    plog(XLOG_INFO, "%s is on a remote network",
294	 inet_dquad(buf, sizeof(buf), addr));
295  }
296#endif /* DEBUG */
297
298  return FALSE;
299}
300
301
302/*
303 * Determine whether a network name is one of the local networks
304 * of a host.
305 */
306int
307is_network_member(const char *net)
308{
309  addrlist *al;
310
311  /*
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).
317   */
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);
346      }
347    }
348    netnum = inet_addr(netstr);	/* not checking return value, b/c -1 (0xffffffff) is valid */
349    XFREE(netstr);		/* netstr not needed any longer */
350
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;
355    }
356  }
357
358  return FALSE;
359}
360
361
362/*
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.
366 */
367int
368is_interface_local(u_long netnum)
369{
370  addrlist *al;
371
372  for (al = localnets; al; al = al->ip_next) {
373    if (al->ip_addr == netnum)
374      return TRUE;
375  }
376  return FALSE;
377}
378
379
380#ifdef HAVE_GETIFADDRS
381void
382getwire(char **name1, char **number1)
383{
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 */
389
390  ifaddrs = NULL;
391#ifdef HAVE_STRUCT_IFADDRS_IFA_NEXT
392  if (getifaddrs(&ifaddrs) < 0)
393    goto out;
394
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;
399
400  for (i = 0,ifap = ifaddrs; i < count; ifap++, i++) {
401#endif /* HAVE_STRUCT_IFADDRS_IFA_NEXT */
402
403    if (!ifap || !ifap->ifa_addr || ifap->ifa_addr->sa_family != AF_INET)
404      continue;
405
406    /*
407     * If the interface is the loopback, or it's not running,
408     * then ignore it.
409     */
410    if (S2IN(ifap->ifa_addr) == htonl(INADDR_LOOPBACK))
411      continue;
412    if ((ifap->ifa_flags & IFF_RUNNING) == 0)
413      continue;
414
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);
419
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;
427    }
428  }
429
430out:
431  if (ifaddrs)
432    XFREE(ifaddrs);
433
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";
440  }
441}
442
443#else /* not HAVE_GETIFADDRS */
444
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) */
450
451#define clist		(ifc.ifc_ifcu.ifcu_req)
452#define count		(ifc.ifc_len/sizeof(struct ifreq))
453
454
455void
456getwire(char **name1, char **number1)
457{
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];
465
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 */
472
473  /*
474   * Get suitable socket
475   */
476  if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
477    goto out;
478
479  /*
480   * Fill in ifconf details
481   */
482  memset(&buf[0], 0, GFBUFLEN);
483  ifc.ifc_len = sizeof(buf);
484  ifc.ifc_buf = buf;
485
486  /*
487   * Get network interface configurations
488   */
489  if (ioctl(fd, SIOCGIFCONF, (caddr_t) & ifc) < 0)
490    goto out;
491
492  /*
493   * Upper bound on array
494   */
495  cplim = buf + ifc.ifc_len;
496
497  /*
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.
502   */
503
504  /*
505   * Scan the list looking for a suitable interface
506   */
507  for (cp = buf; cp < cplim; /* increment in the loop body */) {
508    memcpy(&ifrpool, cp, sizeof(ifrpool));
509    ifr = &ifrpool;
510    cp += SIZE(ifr);
511
512    if (ifr->ifr_addr.sa_family != AF_INET)
513      continue;
514
515    address = ((struct sockaddr_in *) &ifr->ifr_addr)->sin_addr.s_addr;
516
517    /*
518     * Get interface flags
519     */
520    if (ioctl(fd, SIOCGIFFLAGS, (caddr_t) ifr) < 0)
521      continue;
522
523    /*
524     * If the interface is the loopback, or it's not running,
525     * then ignore it.
526     */
527    if (address == htonl(INADDR_LOOPBACK))
528      continue;
529    /*
530     * Fix for 0.0.0.0 loopback on SunOS 3.X which defines IFF_ROUTE
531     * instead of IFF_LOOPBACK.
532     */
533#ifdef IFF_ROUTE
534    if (ifr->ifr_flags == (IFF_UP|IFF_RUNNING))
535      continue;
536#endif /* IFF_ROUTE */
537
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;
542
543    if ((ifr->ifr_flags & IFF_POINTOPOINT) == 0) {
544      /*
545       * Get the netmask of this interface
546       */
547      if (ioctl(fd, SIOCGIFNETMASK, (caddr_t) ifr) < 0)
548	continue;
549
550      al = getwire_lookup(address, S2IN(&ifr->ifr_addr), 0);
551    } else
552      al = getwire_lookup(address, 0xffffffff, 1);
553
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;
561    }
562  }
563
564out:
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";
573  }
574}
575#endif /* not HAVE_GETIFADDRS */
576