1/* * Copyright (c) 2006 Apple Inc. All rights reserved. 2 * 3 * @APPLE_LICENSE_HEADER_START@ 4 * 5 * This file contains Original Code and/or Modifications of Original Code 6 * as defined in and that are subject to the Apple Public Source License 7 * Version 2.0 (the 'License'). You may not use this file except in 8 * compliance with the License. Please obtain a copy of the License at 9 * http://www.opensource.apple.com/apsl/ and read it before using this 10 * file. 11 * 12 * The Original Code and all software distributed under the License are 13 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 16 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 17 * Please see the License for the specific language governing rights and 18 * limitations under the License. 19 * 20 * @APPLE_LICENSE_HEADER_END@ 21 */ 22 23#include <stdio.h> 24#include <servers/bootstrap.h> 25#include <DirectoryService/DirServicesConst.h> 26#include <opendirectory/DSlibinfoMIG_types.h> 27#include <syslog.h> 28#include <stdlib.h> 29#include <arpa/inet.h> 30#include <mach/mach.h> 31#include <net/ethernet.h> 32#include <kvbuf.h> 33#include "bootplookup.h" 34 35extern kern_return_t 36libinfoDSmig_GetProcedureNumber(mach_port_t server, 37 proc_name_t name, 38 int32_t *procno, 39 security_token_t *usertoken); 40 41extern kern_return_t 42libinfoDSmig_Query(mach_port_t server, 43 int32_t proc, 44 inline_data_t request, 45 mach_msg_type_number_t requestCnt, 46 inline_data_t reply, 47 mach_msg_type_number_t *replyCnt, 48 vm_offset_t *ooreply, 49 mach_msg_type_number_t *ooreplyCnt, 50 security_token_t *usertoken); 51 52typedef struct bootpent 53{ 54 struct bootpent *bp_next; 55 char *bp_name; // will only be populated at head of list 56 char *bp_bootfile; // will only be populated at head of list 57 char *bp_hw; 58 char *bp_addr; 59 void *reserved[4]; 60} bootpent; 61 62 63enum { 64 kDSLUgetbootpbyhw = 0, 65 kDSLUgetbootpbyaddr, 66 kDSLUlastentry 67}; 68 69static char *gProcNames[] = 70{ 71 "getbootpbyhw", 72 "getbootpbyaddr", 73 NULL 74}; 75 76static bootpent * 77dolookup(int32_t inProc, kvbuf_t *inRequest) 78{ 79 static int32_t procs[kDSLUlastentry] = { 0 }; 80 static mach_port_t serverPort = MACH_PORT_NULL; 81 security_token_t userToken; 82 kern_return_t kr = KERN_SUCCESS; 83 int32_t retryCount = 0; 84 char reply[16384] = { 0, }; 85 mach_msg_type_number_t replyCnt = 0; 86 vm_offset_t ooreply = 0; 87 mach_msg_type_number_t ooreplyCnt = 0; 88 bootpent *returnValue = NULL; 89 90 if (inProc > kDSLUlastentry) { 91 return NULL; 92 } 93 94 do { 95 // see if we have a port 96 if (serverPort == MACH_PORT_NULL) { 97 kr = bootstrap_look_up(bootstrap_port, kDSStdMachDSLookupPortName, &serverPort); 98 if (kr != KERN_SUCCESS) { 99 syslog(LOG_CRIT, "Cannot find bootstrap port for DirectoryService\n"); 100 return NULL; 101 } 102 } 103 104 if (serverPort != MACH_PORT_NULL && procs[inProc] == 0) { 105 kr = libinfoDSmig_GetProcedureNumber(serverPort, gProcNames[inProc], &procs[inProc], &userToken); 106 if (kr != KERN_SUCCESS) { 107 syslog(LOG_CRIT, "Cannot find procedure number for lookup %s\n", gProcNames[inProc]); 108 return NULL; 109 } 110 } 111 112 if (procs[inProc] != 0) { 113 kr = libinfoDSmig_Query(serverPort, procs[inProc], inRequest->databuf, inRequest->datalen, reply, &replyCnt, &ooreply, &ooreplyCnt, &userToken); 114 if (KERN_SUCCESS == kr) { 115 uint32_t length = (replyCnt ? replyCnt : ooreplyCnt); 116 char *buffer = (replyCnt ? reply : (char *)ooreply); 117 kvbuf_t * kv; 118 119 kv = kvbuf_init(buffer, length); 120 if (ooreplyCnt != 0) { 121 vm_deallocate(mach_task_self(), ooreply, ooreplyCnt); 122 } 123 124 uint32_t dictCount = kvbuf_reset(kv); 125 126 // time to parse this into a list of bootpent... 127 if (dictCount > 0) { 128 uint32_t count = 0; 129 char *key = NULL; 130 char *keys[] = { 131 "bp_name", 132 "bp_bootfile", 133 "bp_hw", 134 "bp_addr" 135 }; 136 uint32_t addrCnt = 0; 137 uint32_t hwCnt = 0; 138 139 returnValue = (bootpent *) calloc(1, sizeof(bootpent)); 140 returnValue->reserved[0] = kv; 141 142 kvbuf_next_dict(kv); 143 144 // expand the results to a list 145 while ((key = kvbuf_next_key(kv, &count)) != NULL) { 146 // bp_name 147 if (keys[0] != NULL && strcmp(key, keys[0]) == 0) { 148 returnValue->bp_name = kvbuf_next_val(kv); 149 keys[0] = NULL; 150 } 151 // bp_bootfile 152 else if (keys[1] != NULL && strcmp(key, keys[1]) == 0) { 153 returnValue->bp_bootfile = kvbuf_next_val(kv); 154 keys[1] = NULL; 155 } 156 // bp_hw 157 else if (keys[2] != NULL && strcmp(key, keys[2]) == 0) { 158 uint32_t ii = 0; 159 bootpent *entry = returnValue; 160 161 do { 162 entry->bp_hw = kvbuf_next_val(kv); 163 if ((++ii) < count) { 164 if (entry->bp_next == NULL) { 165 entry->bp_next = (bootpent *) calloc(1, sizeof(bootpent)); 166 } 167 entry = entry->bp_next; 168 continue; 169 } 170 break; 171 } while (1); 172 173 keys[2] = NULL; 174 hwCnt = count; 175 } 176 // bp_addr 177 else if (keys[3] != NULL && strcmp(key, keys[3]) == 0) { 178 uint32_t ii = 0; 179 bootpent *entry = returnValue; 180 181 do { 182 entry->bp_addr = kvbuf_next_val(kv); 183 if ((++ii) < count) { 184 if (entry->bp_next == NULL) { 185 entry->bp_next = (bootpent *) calloc(1, sizeof(bootpent)); 186 } 187 entry = entry->bp_next; 188 continue; 189 } 190 break; 191 } while (1); 192 193 keys[3] = NULL; 194 addrCnt = count; 195 } 196 } 197 198 // if our counts aren't the same we need to expand them out 199 if (addrCnt != hwCnt && addrCnt > 0 && hwCnt > 0) { 200 // we also trim the list if there are more addr than hw 201 bootpent *currEntry = returnValue; 202 bootpent *copyEntry = returnValue; 203 int ii = 0; 204 int expAddr = (addrCnt < hwCnt); 205 int expCnt = (expAddr ? addrCnt : hwCnt); 206 207 while (currEntry != NULL) { 208 if (expAddr) { 209 currEntry->bp_addr = copyEntry->bp_addr; 210 } 211 else { 212 currEntry->bp_hw = copyEntry->bp_hw; 213 } 214 215 currEntry = currEntry->bp_next; 216 217 if ((++ii) < expCnt) { 218 // move to the next one 219 copyEntry = copyEntry->bp_next; 220 } 221 else { 222 // reset to top of list to start copy again 223 copyEntry = returnValue; 224 ii = 0; 225 } 226 } 227 } 228 } 229 else { 230 kvbuf_free(kv); 231 } 232 } 233 else if (MACH_SEND_INVALID_DEST == kr) { 234 mach_port_mod_refs(mach_task_self(), 235 serverPort, MACH_PORT_RIGHT_SEND, -1); 236 bzero(procs, sizeof(procs)); 237 serverPort = MACH_PORT_NULL; 238 retryCount++; 239 } 240 else { 241 return (NULL); 242 } 243 } 244 245 } while (KERN_SUCCESS != kr && retryCount < 10); 246 247 return returnValue; 248} 249 250static bootpent * 251getbootpbyhw(const char *hw) 252{ 253 kvbuf_t *request = kvbuf_new(); 254 255 kvbuf_add_dict(request); 256 257 kvbuf_add_key(request, "hw"); 258 kvbuf_add_val(request, hw); 259 260 bootpent *result = dolookup(kDSLUgetbootpbyhw, request); 261 262 kvbuf_free(request); 263 264 return result; 265} 266 267static bootpent * 268getbootpbyaddr(const char *addr) 269{ 270 kvbuf_t *request = kvbuf_new(); 271 272 kvbuf_add_dict(request); 273 274 kvbuf_add_key(request, "addr"); 275 kvbuf_add_val(request, addr); 276 277 bootpent *result = dolookup(kDSLUgetbootpbyaddr, request); 278 279 kvbuf_free(request); 280 281 return result; 282} 283 284void 285freebootpent(bootpent *listhead) 286{ 287 if (listhead == NULL) { 288 return; 289 } 290 291 if (listhead->reserved[0] == NULL) { 292 syslog(LOG_CRIT, "freebootpent called without the head of the entry list"); 293 abort(); 294 } 295 296 kvbuf_free(listhead->reserved[0]); 297 298 // free the chain first 299 bootpent *nextEntry = listhead->bp_next; 300 while (nextEntry != NULL) { 301 bootpent *temp = nextEntry->bp_next; 302 free(nextEntry); 303 nextEntry = temp; 304 } 305 306 free(listhead); 307} 308 309#define ETHER_ADDR_BUFLEN sizeof("xx:xx:xx:xx:xx:xx") 310#define EI(i) (u_char)(e->ether_addr_octet[(i)]) 311 312static char * 313my_ether_ntoa(const struct ether_addr *e, 314 char *buf, int bufLen) 315{ 316 if (bufLen < ETHER_ADDR_BUFLEN) { 317 return NULL; 318 } 319 320 buf[0] = 0; 321 sprintf(buf, "%02x:%02x:%02x:%02x:%02x:%02x", 322 EI(0), EI(1), EI(2), EI(3), EI(4), EI(5)); 323 return (buf); 324} 325 326#ifdef NOT_NEEDED 327static struct ether_addr * 328my_ether_aton(const char *a, struct ether_addr *ea, int eaLen) 329{ 330 register int i; 331 332 if (eaLen < ETHER_ADDR_LEN) { 333 return NULL; 334 } 335 336 i = sscanf(a, " %x:%x:%x:%x:%x:%x", 337 &ea[0], &ea[1], &ea[2], &ea[3], &ea[4], &ea[5]); 338 if (i != 6) { 339 return NULL; 340 } 341 342 return ea; 343} 344#endif // 0 345 346boolean_t 347bootp_getbyhw_ds(uint8_t hwtype, void * hwaddr, int hwlen, 348 subnet_match_func_t * func, void * arg, 349 struct in_addr * iaddr_p, 350 char * * hostname_p, char * * bootfile_p) 351{ 352 bootpent *be; 353 bootpent *bp; 354 char buf[ETHER_ADDR_BUFLEN]; 355 struct in_addr ia; 356 357 (void) my_ether_ntoa(hwaddr, buf, sizeof(buf)); 358 be = getbootpbyhw(buf); 359 if (be == NULL) 360 return FALSE; 361 362 for (bp = be; bp != NULL; bp = bp->bp_next) { 363 if ((bp->bp_hw != NULL) && (strcmp(buf, bp->bp_hw) == 0)) { 364 if ((bp->bp_addr == NULL) || (inet_aton(bp->bp_addr, &ia) == 0) 365 || ia.s_addr == 0) { 366 /* don't return 0.0.0.0 */ 367 continue; 368 } 369 370 if ((func == NULL) || (*func)(arg, ia)) { 371 goto done; 372 } 373 } 374 } 375 376 freebootpent(be); 377 return FALSE; 378 379 done : 380 381 if (hostname_p != NULL) 382 *hostname_p = (be->bp_name != NULL) ? strdup(be->bp_name) : NULL; 383 if (bootfile_p != NULL) 384 *bootfile_p = (be->bp_bootfile != NULL) ? strdup(be->bp_bootfile) : NULL; 385 *iaddr_p = ia; 386 387 freebootpent(be); 388 return TRUE; 389} 390 391#define INET_ADDR_BUFLEN sizeof("255.255.255.255") 392 393boolean_t 394bootp_getbyip_ds(struct in_addr ciaddr, char * * hostname_p, 395 char * * bootfile_p) 396{ 397 bootpent *be; 398 bootpent *bp; 399 char buf[INET_ADDR_BUFLEN]; 400 401 (void)inet_ntop(AF_INET, &ciaddr, buf, sizeof(buf)); 402 be = getbootpbyaddr(buf); 403 if (be == NULL) 404 return FALSE; 405 406 for (bp = be; bp != NULL; bp = bp->bp_next) { 407 if ((bp->bp_addr != NULL) && (strcmp(buf, bp->bp_addr) == 0)) { 408 goto done; 409 } 410 } 411 412 freebootpent(be); 413 return FALSE; 414 415 done : 416 417 if (hostname_p != NULL) 418 *hostname_p = (be->bp_name != NULL) ? strdup(be->bp_name) : NULL; 419 if (bootfile_p != NULL) 420 *bootfile_p = (be->bp_bootfile != NULL) ? strdup(be->bp_bootfile) : NULL; 421 422 freebootpent(be); 423 return TRUE; 424} 425 426#ifdef TEST_BOOTPLOOKUP 427 428#include <sys/time.h> 429#include "util.h" 430 431void 432dumpEntry(bootpent *entry) 433{ 434 bootpent *entryTemp; 435 436 printf(" bp_name = %s\n", entry->bp_name); 437 printf(" bp_bootfile = %s\n", entry->bp_bootfile); 438 439 for(entryTemp = entry; entryTemp != NULL; entryTemp = entryTemp->bp_next) { 440 printf("\n"); 441 printf(" bp_hw = %s\n", entryTemp->bp_hw); 442 printf(" bp_addr = %s\n", entryTemp->bp_addr); 443 } 444 445 printf("\n"); 446} 447 448#define HTYPE_ETHER 1 449 450typedef enum { 451 isIP, 452 isMAC, 453 isUnknown 454} queryType_t; 455 456int 457main(int argc, const char * argv[]) 458{ 459 char buf[256]; 460 FILE *f; 461 char *line; 462 int n = 0; 463 int n_stalls = 0; 464 boolean_t showRaw = FALSE; 465 struct timeval tv_elapsed = { 0, 0 }; 466 struct timeval tv_max = { 0, 0 }; 467 struct timeval tv_min = { 99999, 0 }; 468 469 if ((argc > 1) && (strcmp(argv[1], "RAW") == 0)) { 470 showRaw = TRUE; 471 argv++; 472 argc--; 473 } 474 475 if (argc < 2) { 476 f = stdin; 477 } 478 else { 479 f = fopen(argv[1], "r"); 480 if (f == NULL) { 481 fprintf(stderr, "%s: could not open %s\n", argv[0], argv[1]); 482 exit(1); 483 } 484 } 485 if (isatty(0)) { 486 fprintf(stdout, "Enter a MAC address or IP Address" 487 " (e.g. 00:0d:93:9d:e7:d7 or 17.203.16.241)\n"); 488 } 489 while ((line = fgets(buf, sizeof(buf), f)) != NULL) { 490 bootpent *entry; 491 boolean_t found = FALSE; 492 struct ether_addr *hw_address; 493 char *hn = NULL; 494 struct in_addr ip_address; 495 size_t len; 496 queryType_t qt; 497 char *query; 498 struct timeval tv_end; 499 struct timeval tv_query; 500 struct timeval tv_start; 501 502 len = strlen(line) - 1; 503 while ((len >= 0) && (line[len] == '\n')) { 504 line[len--] = 0; 505 } 506 507 if (inet_aton(line, &ip_address) == 1) { 508 // process IP address 509 qt = isIP; 510 query = inet_ntoa(ip_address); 511 if (showRaw) { 512 gettimeofday(&tv_start, 0); 513 entry = getbootpbyaddr(query); 514 } 515 else { 516 gettimeofday(&tv_start, 0); 517 found = bootp_getbyip_ds(ip_address, 518 &hn, 519 NULL); 520 } 521 } 522 else if ((hw_address = ether_aton(line)) != NULL) { 523 char buf[ETHER_ADDR_BUFLEN]; 524 525 // process MAC address 526 qt = isMAC; 527 query = my_ether_ntoa(hw_address, buf, sizeof(buf)); 528 if (showRaw) { 529 gettimeofday(&tv_start, 0); 530 entry = getbootpbyhw(query); 531 } 532 else { 533 gettimeofday(&tv_start, 0); 534 found = bootp_getbyhw_ds(HTYPE_ETHER, hw_address, sizeof(*hw_address), 535 NULL, NULL, 536 &ip_address, 537 &hn, 538 NULL); 539 } 540 } 541 else { 542 // 'tis neither an IP or MAC address 543 fprintf(stdout, "Invalid entry\n"); 544 continue; 545 } 546 547 gettimeofday(&tv_end, 0); 548 timeval_subtract(tv_end, tv_start, &tv_query); 549 if (tv_query.tv_sec == 0) { 550 timeval_add(tv_elapsed, tv_query, &tv_elapsed); 551 n++; 552 553 if (timeval_compare(tv_query, tv_min) < 0) { 554 tv_min = tv_query; 555 } 556 557 if (timeval_compare(tv_query, tv_max) > 0) { 558 tv_max = tv_query; 559 } 560 } 561 else { 562 n_stalls++; 563 } 564 565 if (qt == isIP) { 566 if (showRaw && (entry != NULL)) { 567 printf("IP: %-*s host: %-20s", 568 (int)INET_ADDR_BUFLEN - 1, query, 569 entry->bp_name); 570 dumpEntry(entry); 571 freebootpent(entry); 572 } 573 else if (found) { 574 printf("IP: %-*s host: %-20s", 575 (int)INET_ADDR_BUFLEN - 1, query, 576 hn); 577 } 578 else { 579 printf("IP: %-*s host: %-20s", 580 (int)INET_ADDR_BUFLEN - 1, query, 581 "NOT FOUND"); 582 } 583 } 584 else if (qt == isMAC) { 585 if (showRaw && (entry != NULL)) { 586 printf("IP: %-*s MAC: %s host: %-20s", 587 (int)INET_ADDR_BUFLEN - 1, "", 588 query, 589 entry->bp_name); 590 dumpEntry(entry); 591 freebootpent(entry); 592 } 593 else if (found) { 594 printf("IP: %-*s MAC: %s host: %-20s", 595 (int)INET_ADDR_BUFLEN - 1, inet_ntoa(ip_address), 596 query, 597 hn); 598 } 599 else { 600 printf("IP: %-*s MAC: %s host: %-20s", 601 (int)INET_ADDR_BUFLEN - 1, "", 602 query, 603 "NOT FOUND"); 604 } 605 } 606 607 printf(" (%d.%06d%s)\n", 608 (int)tv_query.tv_sec, 609 (int)tv_query.tv_usec, 610 (tv_query.tv_sec > 0) ? ", stalled?" : ""); 611 612 if (hn != NULL) free(hn); 613 } 614 615 if (n > 0) { 616 time_t t; 617 618 t = tv_elapsed.tv_sec * USECS_PER_SEC + tv_elapsed.tv_usec; 619 t = t / n; 620 621 printf("processed %d queries (min %d.%06d, avg %d.%06d, max %d.%06d)\n", 622 n, 623 (int)tv_min.tv_sec, 624 (int)tv_min.tv_usec, 625 (int)(t / USECS_PER_SEC), 626 (int)(t % USECS_PER_SEC), 627 (int)tv_max.tv_sec, 628 (int)tv_max.tv_usec); 629 } 630 631 if (n_stalls > 0) { 632 printf("%4d %s took longer than 1 second (and %s\n", 633 n_stalls, 634 (n_stalls == 1) ? "query" : "queries", 635 (n_stalls == 1) ? "was" : "were"); 636 printf(" not included in the per-query statistics)\n"); 637 } 638 639 return 0; 640} 641 642#endif // TEST_BOOTPLOOKUP 643