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