1/* 2 * Copyright (c) 1999-2008 Apple Inc. All rights reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. Please obtain a copy of the License at 10 * http://www.opensource.apple.com/apsl/ and read it before using this 11 * file. 12 * 13 * The Original Code and all software distributed under the License are 14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 * Please see the License for the specific language governing rights and 19 * limitations under the License. 20 * 21 * @APPLE_LICENSE_HEADER_END@ 22 */ 23/* 24 * bootpdfile.c 25 * - read bootptab to get the default boot file and path 26 * - parse the list of hardware address to ip bindings 27 * - lookup host entries in the file-based host list 28 */ 29 30#include <unistd.h> 31#include <stdlib.h> 32#include <sys/stat.h> 33#include <sys/socket.h> 34#include <sys/ioctl.h> 35#include <sys/file.h> 36#include <sys/time.h> 37#include <net/if.h> 38#include <netinet/in.h> 39#include <netinet/in_systm.h> 40#include <netinet/ip.h> 41#include <netinet/udp.h> 42#include <netinet/bootp.h> 43#include <netinet/if_ether.h> 44#include <mach/boolean.h> 45#include <signal.h> 46#include <stdio.h> 47#include <string.h> 48#include <errno.h> 49#include <ctype.h> 50#include <netdb.h> 51#include <syslog.h> 52#include <arpa/inet.h> 53#include <sys/uio.h> 54 55#include "bootpdfile.h" 56#include "hostlist.h" 57 58#define HTYPE_ETHER 1 59#define NUM_EN_ADDR_BYTES 6 60 61#define HOSTNAME_MAX 64 62#define BOOTFILE_MAX 128 63 64#define ETC_BOOTPTAB "/etc/bootptab" 65static long modtime = 0; /* last modification time of bootptab */ 66static struct hosts * S_file_hosts = NULL; /* list of host entries from the file */ 67 68/* 69 * Get next field from 'line' buffer into 'str'. 'linep' is the 70 * pointer to current position. 71 */ 72static void 73S_getfield(char * * line_p, int linenum, char *str, int len) 74{ 75 register char *cp = str; 76 char * linep = *line_p; 77 78 for ( ; *linep && (*linep == ' ' || *linep == '\t') ; linep++) 79 ; /* skip spaces/tabs */ 80 if (*linep == 0) { 81 *cp = 0; 82 goto done; 83 } 84 len--; /* save a spot for a null */ 85 for ( ; *linep && *linep != ' ' && *linep != '\t' ; linep++) { 86 *cp++ = *linep; 87 if (--len <= 0) { 88 *cp = 0; 89 syslog(LOG_NOTICE, "string truncated: %s," 90 " on line %d of bootptab", str, linenum); 91 goto done; 92 } 93 } 94 *cp = 0; 95 done: 96 *line_p = linep; 97} 98 99/* 100 * Read bootptab database file. Avoid rereading the file if the 101 * write date hasnt changed since the last time we read it. 102 */ 103void 104bootp_readtab(const char * filename) 105{ 106 struct stat st; 107 register char *cp; 108 int host_count = 0; 109 int host_count_all = 0; 110 register int i; 111 char line[256]; /* line buffer for reading bootptab */ 112 char temp[64], tempcpy[64]; 113 register struct hosts *hp, *thp; 114 char * linep; 115 int linenum; 116 int skiptopercent; 117 FILE * fp = NULL; 118 119 if (filename == NULL) { 120 filename = ETC_BOOTPTAB; 121 } 122 if ((fp = fopen(filename, "r")) == NULL) { 123 syslog(LOG_INFO, "can't open %s", filename); 124 return; 125 } 126 if (fstat(fileno(fp), &st) == 0 127 && st.st_mtime == modtime) { 128 fclose(fp); 129 return; /* hasn't been modified */ 130 } 131 syslog(LOG_NOTICE, "re-reading %s", filename); 132 modtime = st.st_mtime; 133 linenum = 0; 134 skiptopercent = 1; 135 136 /* 137 * Free old file entries. 138 */ 139 hp = S_file_hosts; 140 while (hp) { 141 thp = hp->next; 142 hostfree(&S_file_hosts, hp); 143 hp = thp; 144 } 145 146 /* 147 * read and parse each line in the file. 148 */ 149 for (;;) { 150 boolean_t all_zeroes; 151 boolean_t good_hwaddr; 152 char hostname[HOSTNAME_MAX]; 153 char bootfile[BOOTFILE_MAX]; 154 struct in_addr iaddr; 155 int htype; 156 int hlen; 157 char haddr[32]; 158 159 if (fgets(line, sizeof line, fp) == NULL) 160 break; /* done */ 161 162 if ((i = (int)strlen(line)) != 0) 163 line[i-1] = 0; /* remove trailing newline */ 164 165 linep = line; 166 linenum++; 167 if (line[0] == '#' || line[0] == 0 || line[0] == ' ') 168 continue; /* skip comment lines */ 169 170 if (skiptopercent) { /* allow for future leading fields */ 171 if (line[0] != '%') 172 continue; 173 skiptopercent = 0; 174 continue; 175 } 176 host_count_all++; 177 /* fill in host table */ 178 S_getfield(&linep, linenum, hostname, sizeof(hostname) - 1); 179 S_getfield(&linep, linenum, temp, sizeof temp); 180 sscanf(temp, "%d", &htype); 181 S_getfield(&linep, linenum, temp, sizeof temp); 182 strlcpy(tempcpy, temp, sizeof(tempcpy)); 183 cp = tempcpy; 184 /* parse hardware address */ 185 good_hwaddr = TRUE; 186 all_zeroes = TRUE; 187 for (hlen = 0; hlen < sizeof(haddr);) { 188 char *cpold; 189 char c; 190 int v; 191 192 cpold = cp; 193 while (*cp != '.' && *cp != ':' && *cp != 0) 194 cp++; 195 c = *cp; /* save original terminator */ 196 *cp = 0; 197 cp++; 198 if (sscanf(cpold, "%x", &v) != 1) { 199 good_hwaddr = FALSE; 200 syslog(LOG_NOTICE, "bad hex address: %s," 201 " at line %d of bootptab", temp, linenum); 202 break; 203 } 204 haddr[hlen++] = v; 205 if (v != 0) { 206 all_zeroes = FALSE; 207 } 208 if (c == 0) 209 break; 210 } 211 if (good_hwaddr == FALSE) { 212 continue; 213 } 214 if (all_zeroes) { 215 syslog(LOG_NOTICE, "zero hex address: %s," 216 " at line %d of bootptab", temp, linenum); 217 continue; 218 } 219 if (htype == HTYPE_ETHER && hlen != NUM_EN_ADDR_BYTES) { 220 syslog(LOG_NOTICE, "bad hex address: %s," 221 " at line %d of bootptab", temp, linenum); 222 continue; 223 } 224 S_getfield(&linep, linenum, temp, sizeof(temp)); 225 iaddr.s_addr = inet_addr(temp); 226 if (iaddr.s_addr == -1 || iaddr.s_addr == 0) { 227 syslog(LOG_NOTICE, "bad internet address: %s," 228 " at line %d of bootptab", temp, linenum); 229 continue; 230 } 231 S_getfield(&linep, linenum, bootfile, sizeof(bootfile) - 1); 232 (void)hostadd(&S_file_hosts, NULL, htype, haddr, hlen, &iaddr, 233 hostname, bootfile); 234 host_count++; 235 } 236 fclose(fp); 237 syslog(LOG_NOTICE, "Loaded %d entries from bootptab (%d bad)", 238 host_count, host_count_all - host_count); 239 return; 240} 241 242boolean_t 243bootp_getbyhw_file(uint8_t hwtype, void * hwaddr, int hwlen, 244 subnet_match_func_t * func, void * arg, 245 struct in_addr * iaddr_p, 246 char * * hostname_p, char * * bootfile_p) 247{ 248 struct hosts * hp; 249 250 hp = hostbyaddr(S_file_hosts, hwtype, hwaddr, hwlen, 251 func, arg); 252 if (hp == NULL) 253 return (FALSE); 254 if (hostname_p) 255 *hostname_p = strdup(hp->hostname); 256 if (bootfile_p) 257 *bootfile_p = strdup(hp->bootfile); 258 *iaddr_p = hp->iaddr; 259 return (TRUE); 260} 261 262boolean_t 263bootp_getbyip_file(struct in_addr ciaddr, char * * hostname_p, 264 char * * bootfile_p) 265{ 266 struct hosts * hp; 267 268 hp = hostbyip(S_file_hosts, ciaddr); 269 if (hp == NULL) 270 return (FALSE); 271 272 if (hostname_p) 273 *hostname_p = strdup(hp->hostname); 274 if (bootfile_p) 275 *bootfile_p = strdup(hp->bootfile); 276 return (TRUE); 277} 278 279#ifdef MAIN 280#define USECS_PER_SEC 1000000 281/* 282 * Function: timeval_subtract 283 * 284 * Purpose: 285 * Computes result = tv1 - tv2. 286 */ 287void 288timeval_subtract(struct timeval tv1, struct timeval tv2, 289 struct timeval * result) 290{ 291 result->tv_sec = tv1.tv_sec - tv2.tv_sec; 292 result->tv_usec = tv1.tv_usec - tv2.tv_usec; 293 if (result->tv_usec < 0) { 294 result->tv_usec += USECS_PER_SEC; 295 result->tv_sec--; 296 } 297 return; 298} 299 300/* 301 * Function: timeval_add 302 * 303 * Purpose: 304 * Computes result = tv1 + tv2. 305 */ 306void 307timeval_add(struct timeval tv1, struct timeval tv2, 308 struct timeval * result) 309{ 310 result->tv_sec = tv1.tv_sec + tv2.tv_sec; 311 result->tv_usec = tv1.tv_usec + tv2.tv_usec; 312 if (result->tv_usec > USECS_PER_SEC) { 313 result->tv_usec -= USECS_PER_SEC; 314 result->tv_sec++; 315 } 316 return; 317} 318 319/* 320 * Function: timeval_compare 321 * 322 * Purpose: 323 * Compares two timeval values, tv1 and tv2. 324 * 325 * Returns: 326 * -1 if tv1 is less than tv2 327 * 0 if tv1 is equal to tv2 328 * 1 if tv1 is greater than tv2 329 */ 330int 331timeval_compare(struct timeval tv1, struct timeval tv2) 332{ 333 struct timeval result; 334 335 timeval_subtract(tv1, tv2, &result); 336 if (result.tv_sec < 0 || result.tv_usec < 0) 337 return (-1); 338 if (result.tv_sec == 0 && result.tv_usec == 0) 339 return (0); 340 return (1); 341} 342 343void 344timestamp_printf(char * msg) 345{ 346 static struct timeval tvp = {0,0}; 347 struct timeval tv; 348 349 gettimeofday(&tv, 0); 350 if (tvp.tv_sec) { 351 struct timeval result; 352 353 timeval_subtract(tv, tvp, &result); 354 printf("%d.%06d (%d.%06d): %s\n", 355 (int)tv.tv_sec, 356 (int)tv.tv_usec, 357 (int)result.tv_sec, 358 (int)result.tv_usec, msg); 359 } 360 else 361 printf("%d.%06d (%d.%06d): %s\n", 362 (int)tv.tv_sec, (int)tv.tv_usec, 0, 0, msg); 363 tvp = tv; 364} 365 366int 367main(int argc, char * argv[]) 368{ 369 struct ether_addr * ea; 370 struct in_addr ip; 371 char * host = NULL; 372 boolean_t found; 373 const char * file = NULL; 374 375 if (argc < 2) { 376 fprintf(stderr, "usage: %s ethernet_address\n", argv[0]); 377 exit(1); 378 } 379 ea = ether_aton(argv[1]); 380 if (ea == NULL) { 381 fprintf(stderr, "invalid ethernet address '%s'\n", argv[1]); 382 exit(1); 383 } 384 if (argc > 2) { 385 file = argv[2]; 386 } 387 timestamp_printf("before read"); 388 bootp_readtab(file); 389 timestamp_printf("after read"); 390 391 timestamp_printf("before lookup"); 392 found = bootp_getbyhw_file(HTYPE_ETHER, ea, 6, 393 NULL, NULL, 394 &ip, &host, NULL); 395 timestamp_printf("after lookup"); 396 if (found) { 397 printf("%s IP is %s\n", host, inet_ntoa(ip)); 398 } 399 else { 400 printf("Not found\n"); 401 } 402 if (host != NULL) free(host); 403 exit(0); 404 405} 406#endif /* MAIN */ 407