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