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