1 2#include <stdio.h> 3#include <stdlib.h> 4#include <limits.h> 5#include <errno.h> 6#include <stdlib.h> 7#include <stdbool.h> 8#include <stdint.h> 9#include <CoreFoundation/CFDictionary.h> 10#include <CoreFoundation/CFNumber.h> 11#include <CoreFoundation/CFString.h> 12#include <CoreFoundation/CFData.h> 13#include <CoreFoundation/CFArray.h> 14#include <netinfo/ni.h> 15#include "dhcp_options.h" 16#include "subnets.h" 17#include "bootpd-plist.h" 18#include "cfutil.h" 19 20#define NIDIR_CONFIG_DHCP "/config/dhcp" 21#define NIDIR_CONFIG_NETBOOTSERVER "/config/NetBootServer" 22#define NIDIR_CONFIG_DHCP_SUBNETS "/config/dhcp/subnets" 23 24#define NIPROP_NAME "name" 25 26/* 27 * /config/dhcp: 28 */ 29#define CFGPROP_BOOTP_ENABLED "bootp_enabled" 30#define CFGPROP_DHCP_ENABLED "dhcp_enabled" 31#define CFGPROP_OLD_NETBOOT_ENABLED "old_netboot_enabled" 32#define CFGPROP_NETBOOT_ENABLED "netboot_enabled" 33#define CFGPROP_RELAY_ENABLED "relay_enabled" 34#define CFGPROP_ALLOW "allow" 35#define CFGPROP_DENY "deny" 36#define CFGPROP_RELAY_IP_LIST "relay_ip_list" 37#define CFGPROP_DETECT_OTHER_DHCP_SERVER "detect_other_dhcp_server" 38#define CFGPROP_REPLY_THRESHOLD_SECONDS "reply_threshold_seconds" 39 40/* 41 * /config/NetBootServer: 42 */ 43#define CFGPROP_SHADOW_SIZE_MEG "shadow_size_meg" 44#define CFGPROP_AFP_USERS_MAX "afp_users_max" 45#define CFGPROP_AGE_TIME_SECONDS "age_time_seconds" 46#define CFGPROP_AFP_UID_START "afp_uid_start" 47#define CFGPROP_MACHINE_NAME_FORMAT "machine_name_format" 48 49/* 50 * /config/dhcp/subnets/<subnet>: 51 */ 52 53/* 54 * /etc/bootpd.plist is an xml plist. The structure is: 55 * 56 * -------------------+-------------------+------------------------------------ 57 * Top-level Key | Type | NetInfo Source Directory 58 * -------------------+-------------------+------------------------------------ 59 * (root) | <dict> | /config/dhcp 60 * NetBoot | <dict> | /config/NetBootServer 61 * Subnets | <array> of <dict> | /config/dhcp/subnets 62 * -------------------+-------------------+------------------------------------ 63 * 64 * (root) <dict> 65 * --------------------------+---------------------------------------------- 66 * Property | Encoding 67 * --------------------------+---------------------------------------------- 68 * detect_other_dhcp_server | numeric: <boolean> 0=>false 1=>true 69 * --------------------------+---------------------------------------------- 70 * bootp_enabled | no value: <boolean> true 71 * dhcp_enabled, | single empty value: <boolean> false 72 * old_netboot_enabled, | 1 or more values: <array> of <string> 73 * netboot_enabled, | 74 * relay_enabled | 75 * --------------------------+---------------------------------------------- 76 * allow, deny, | <array> of <string> 77 * relay_ip_list | 78 * --------------------------+---------------------------------------------- 79 * reply_threshold_seconds | <integer> 80 * --------------------------+---------------------------------------------- 81 * 82 * 83 * NetBoot <dict> 84 * --------------------------+---------------------------------------------- 85 * Property | Encoding 86 * --------------------------+---------------------------------------------- 87 * shadow_size_meg | <integer> 88 * afp_users_max | 89 * age_time_seconds | 90 * afp_uid_start | 91 * --------------------------+---------------------------------------------- 92 * machine_name_format | <string> 93 * --------------------------+---------------------------------------------- 94 * 95 * 96 * Subnets <array> of <dict> 97 * 98 * <dict> contains: 99 * --------------------------+---------------------------------------------- 100 * Property | Encoding 101 * --------------------------+---------------------------------------------- 102 * name | <string> 103 * net_address | 104 * net_mask | 105 * supernet | 106 * _creator | 107 * --------------------------+---------------------------------------------- 108 * net_range | <array>[2] of <string> 109 * --------------------------+---------------------------------------------- 110 * client_types | replace with "allocate" <boolean> 111 * | if client_types contains "dhcp" 112 * | allocate = <true> 113 * | else 114 * | allocate = <false> 115 * --------------------------+---------------------------------------------- 116 * lease_min, lease_max | <integer> 117 * --------------------------+---------------------------------------------- 118 * dhcp_* | convert using dhcp option conversion table 119 * --------------------------+---------------------------------------------- 120 */ 121 122ni_status 123ni_read_dir(void * handle, char * path, ni_proplist * pl_p, ni_id * dir_id_p) 124{ 125 ni_id dir_id = {0,0}; 126 ni_status status; 127 128 NI_INIT(pl_p); 129 status = ni_pathsearch(handle, &dir_id, path); 130 if (status != NI_OK) { 131 return (status); 132 } 133 status = ni_read(handle, &dir_id, pl_p); 134 if (status != NI_OK) { 135 return (status); 136 } 137 if (dir_id_p) { 138 *dir_id_p = dir_id; 139 } 140 return (status); 141 142} 143 144typedef struct { 145 ni_proplist * ni_propids_props; 146 u_long * ni_propids_ids; 147 int ni_propids_len; 148 ni_id ni_propids_dir; 149 void * ni_propids_domain; 150} ni_propids; 151 152void 153ni_propids_free(ni_propids * props_p) 154{ 155 int i; 156 157 for (i = 0; i < props_p->ni_propids_len; i++) { 158 ni_proplist_free(props_p->ni_propids_props + i); 159 } 160 if (props_p->ni_propids_props != NULL) { 161 free(props_p->ni_propids_props); 162 } 163 if (props_p->ni_propids_ids != NULL) { 164 free(props_p->ni_propids_ids); 165 } 166 NI_INIT(props_p); 167 return; 168} 169 170static ni_status 171ni_propids_read(void * domain, 172 const char * path, 173 ni_propids * props_p) 174{ 175 ni_id dir; 176 int i; 177 ni_proplist * p = NULL; 178 ni_status status = NI_OK; 179 ni_idlist ids; 180 181 NI_INIT(&ids); 182 NI_INIT(props_p); 183 184 props_p->ni_propids_domain = domain; 185 186 status = ni_pathsearch(domain, &dir, (char *)path); 187 if (status != NI_OK) { 188 goto done; 189 } 190 191 props_p->ni_propids_dir = dir; 192 193 status = ni_children(domain, &dir, &ids); 194 if (status != NI_OK) { 195 goto done; 196 } 197 if (ids.ni_idlist_len == 0) { 198 goto done; 199 } 200 p = (ni_proplist *)malloc(sizeof(*p) * ids.ni_idlist_len); 201 bzero(p, sizeof(*p) * ids.ni_idlist_len); 202 for (i = 0; i < ids.ni_idlist_len; i++) { 203 ni_id d; 204 205 d.nii_object = ids.ni_idlist_val[i]; 206 status = ni_read(domain, &d, p + i); 207 if (status != NI_OK) { 208 int j; 209 for (j = 0; j < i; j++) { 210 ni_proplist_free(p + j); 211 } 212 goto done; 213 } 214 } 215 props_p->ni_propids_props = p; 216 props_p->ni_propids_ids = ids.ni_idlist_val; 217 props_p->ni_propids_len = ids.ni_idlist_len; 218 ids.ni_idlist_val = NULL; 219 ids.ni_idlist_len = 0; 220 221 done: 222 ni_idlist_free(&ids); 223 if (status != NI_OK) { 224 if (p != NULL) { 225 free(p); 226 } 227 } 228 return (status); 229} 230 231static CFArrayRef 232CFStringArrayCreateWithCStringArray(const char * * list, int count) 233{ 234 int i; 235 CFMutableArrayRef list_cf = NULL; 236 237 if (count == 0) { 238 goto done; 239 } 240 list_cf = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); 241 for (i = 0; i < count; i++) { 242 CFStringRef str; 243 244 str = CFStringCreateWithCString(NULL, list[i], kCFStringEncodingUTF8); 245 CFArrayAppendValue(list_cf, str); 246 CFRelease(str); 247 } 248 done: 249 return (list_cf); 250} 251 252static CFNumberRef 253number_create(const char * str) 254{ 255 unsigned long val; 256 257 val = strtoul(str, NULL, 0); 258 if (val != ULONG_MAX && errno != ERANGE) { 259 return (CFNumberCreate(NULL, kCFNumberLongType, &val)); 260 } 261 return (NULL); 262} 263 264static CFArrayRef 265CFNumberArrayCreateWithCStringArray(const char * * list, int count) 266{ 267 int i; 268 CFMutableArrayRef list_cf = NULL; 269 270 if (count == 0) { 271 goto done; 272 } 273 list_cf = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); 274 for (i = 0; i < count; i++) { 275 CFNumberRef num; 276 277 num = number_create(list[i]); 278 if (num != NULL) { 279 CFArrayAppendValue(list_cf, num); 280 CFRelease(num); 281 } 282 } 283 done: 284 return (list_cf); 285} 286 287static CFDictionaryRef 288config_dhcp_dict_create_from_proplist(ni_proplist * pl) 289{ 290 CFMutableDictionaryRef dict; 291 int i; 292 293 dict = CFDictionaryCreateMutable(NULL, 0, 294 &kCFTypeDictionaryKeyCallBacks, 295 &kCFTypeDictionaryValueCallBacks); 296 for (i = 0; i < pl->nipl_len; i++) { 297 bool handled = FALSE; 298 CFArrayRef list; 299 ni_namelist * nl_p; 300 ni_property * prop; 301 const char * prop_name; 302 CFStringRef prop_name_cf; 303 304 prop = &(pl->nipl_val[i]); 305 nl_p = &prop->nip_val; 306 prop_name = prop->nip_name; 307 if (strcmp(prop_name, NIPROP_NAME) == 0) { 308 continue; 309 } 310 prop_name_cf = CFStringCreateWithCString(NULL, prop_name, 311 kCFStringEncodingASCII); 312 if ((strcmp(prop_name, CFGPROP_BOOTP_ENABLED) == 0 313 || strcmp(prop_name, CFGPROP_DHCP_ENABLED) == 0 314 || strcmp(prop_name, CFGPROP_OLD_NETBOOT_ENABLED) == 0 315 || strcmp(prop_name, CFGPROP_NETBOOT_ENABLED) == 0 316 || strcmp(prop_name, CFGPROP_RELAY_ENABLED) == 0)) { 317 switch (nl_p->ninl_len) { 318 case 0: 319 handled = TRUE; 320 CFDictionarySetValue(dict, prop_name_cf, kCFBooleanTrue); 321 break; 322 case 1: 323 if (strcmp(nl_p->ninl_val[0], "") == 0) { 324 handled = TRUE; 325 CFDictionarySetValue(dict, prop_name_cf, kCFBooleanFalse); 326 break; 327 } 328 break; 329 default: 330 break; 331 } 332 } 333 else if (strcmp(prop_name, CFGPROP_DETECT_OTHER_DHCP_SERVER) == 0) { 334 handled = TRUE; 335 if (nl_p->ninl_len != 0 336 && strtol(nl_p->ninl_val[0], NULL, 0) != 0) { 337 CFDictionarySetValue(dict, prop_name_cf, kCFBooleanTrue); 338 } 339 else { 340 CFDictionarySetValue(dict, prop_name_cf, kCFBooleanFalse); 341 } 342 } 343 else if (strcmp(prop_name, CFGPROP_REPLY_THRESHOLD_SECONDS) == 0) { 344 handled = TRUE; 345 if (nl_p->ninl_len != 0) { 346 CFNumberRef num; 347 348 num = number_create(nl_p->ninl_val[0]); 349 if (num != NULL) { 350 CFDictionarySetValue(dict, prop_name_cf, num); 351 CFRelease(num); 352 } 353 } 354 } 355 356 if (handled == FALSE 357 && CFDictionaryContainsKey(dict, prop_name_cf) == FALSE 358 && nl_p->ninl_len > 0) { 359 list = CFStringArrayCreateWithCStringArray((const char * *) 360 nl_p->ninl_val, 361 nl_p->ninl_len); 362 CFDictionarySetValue(dict, prop_name_cf, list); 363 CFRelease(list); 364 } 365 CFRelease(prop_name_cf); 366 } 367 return (dict); 368} 369 370static CFDictionaryRef 371config_dhcp_dict_create(void * domain) 372{ 373 CFDictionaryRef dict = NULL; 374 ni_proplist pl; 375 ni_status status; 376 377 NI_INIT(&pl); 378 status = ni_read_dir(domain, NIDIR_CONFIG_DHCP, &pl, NULL); 379 if (status == NI_OK) { 380 dict = config_dhcp_dict_create_from_proplist(&pl); 381 ni_proplist_free(&pl); 382 } 383 return (dict); 384} 385 386static CFDictionaryRef 387config_NetBootServer_dict_create_from_proplist(ni_proplist * pl) 388{ 389 CFMutableDictionaryRef dict; 390 int i; 391 392 dict = CFDictionaryCreateMutable(NULL, 0, 393 &kCFTypeDictionaryKeyCallBacks, 394 &kCFTypeDictionaryValueCallBacks); 395 for (i = 0; i < pl->nipl_len; i++) { 396 ni_namelist * nl_p; 397 ni_property * prop; 398 const char * prop_name; 399 CFStringRef prop_name_cf; 400 401 prop = &(pl->nipl_val[i]); 402 nl_p = &prop->nip_val; 403 prop_name = prop->nip_name; 404 if (strcmp(prop_name, NIPROP_NAME) == 0) { 405 continue; 406 } 407 prop_name_cf = CFStringCreateWithCString(NULL, prop_name, 408 kCFStringEncodingASCII); 409 410 if (strcmp(CFGPROP_SHADOW_SIZE_MEG, prop_name) == 0 411 || strcmp(CFGPROP_AFP_USERS_MAX, prop_name) == 0 412 || strcmp(CFGPROP_AGE_TIME_SECONDS, prop_name) == 0 413 || strcmp(CFGPROP_AFP_UID_START, prop_name) == 0) { 414 if (nl_p->ninl_len != 0) { 415 CFNumberRef num; 416 417 num = number_create(nl_p->ninl_val[0]); 418 if (num != NULL) { 419 CFDictionarySetValue(dict, prop_name_cf, num); 420 CFRelease(num); 421 } 422 } 423 } 424 else if (strcmp(CFGPROP_MACHINE_NAME_FORMAT, prop_name) == 0 425 && nl_p->ninl_len != 0) { 426 CFStringRef format; 427 428 format = CFStringCreateWithCString(NULL, nl_p->ninl_val[0], 429 kCFStringEncodingUTF8); 430 CFDictionarySetValue(dict, prop_name_cf, format); 431 CFRelease(format); 432 } 433 CFRelease(prop_name_cf); 434 } 435 return (dict); 436} 437 438static CFDictionaryRef 439config_NetBootServer_dict_create(void * domain) 440{ 441 CFDictionaryRef dict = NULL; 442 ni_proplist pl; 443 ni_status status; 444 445 NI_INIT(&pl); 446 status = ni_read_dir(domain, NIDIR_CONFIG_NETBOOTSERVER, &pl, NULL); 447 if (status == NI_OK) { 448 dict = config_NetBootServer_dict_create_from_proplist(&pl); 449 ni_proplist_free(&pl); 450 } 451 return (dict); 452} 453 454static bool 455stringlist_contains_string(const char * * list, int list_len, 456 const char * str) 457{ 458 int i; 459 460 for (i = 0; i < list_len; i++) { 461 if (strcmp(list[i], str) == 0) { 462 return (TRUE); 463 } 464 } 465 return (FALSE); 466} 467 468static CFDictionaryRef 469subnet_dict_create_from_proplist(ni_proplist * pl_p) 470{ 471 CFMutableDictionaryRef dict; 472 int i; 473 474 dict = CFDictionaryCreateMutable(NULL, 0, 475 &kCFTypeDictionaryKeyCallBacks, 476 &kCFTypeDictionaryValueCallBacks); 477 for (i = 0; i < pl_p->nipl_len; i++) { 478 bool handled = FALSE; 479 ni_namelist * nl_p; 480 ni_property * prop; 481 const char * prop_name; 482 CFStringRef prop_name_cf; 483 484 prop = &(pl_p->nipl_val[i]); 485 nl_p = &prop->nip_val; 486 prop_name = prop->nip_name; 487 prop_name_cf = CFStringCreateWithCString(NULL, prop_name, 488 kCFStringEncodingASCII); 489 490 if (strncmp(prop_name, "dhcp_", 5) == 0) { 491 dhcptag_t tag; 492 const dhcptag_info_t * tag_info; 493 const dhcptype_info_t * type_info = NULL; 494 495 /* this is a DHCP option */ 496 handled = TRUE; 497 tag = dhcptag_with_name(prop_name + 5); 498 if (tag != -1) { 499 tag_info = dhcptag_info(tag); 500 if (tag_info != NULL) { 501 type_info = dhcptype_info(tag_info->type); 502 } 503 } 504 if (type_info != NULL) { 505 switch (tag_info->type) { 506 case dhcptype_bool_e: 507 if (nl_p->ninl_len != 0 508 && strtol(nl_p->ninl_val[0], NULL, 0) != 0) { 509 CFDictionarySetValue(dict, prop_name_cf, 510 kCFBooleanTrue); 511 } 512 else { 513 CFDictionarySetValue(dict, prop_name_cf, 514 kCFBooleanFalse); 515 } 516 break; 517 case dhcptype_int32_e: 518 case dhcptype_uint8_e: 519 case dhcptype_uint16_e: 520 case dhcptype_uint32_e: 521 if (nl_p->ninl_len != 0) { 522 CFNumberRef num; 523 num = number_create(nl_p->ninl_val[0]); 524 if (num != NULL) { 525 CFDictionarySetValue(dict, prop_name_cf, num); 526 CFRelease(num); 527 } 528 } 529 break; 530 case dhcptype_uint8_mult_e: 531 case dhcptype_uint16_mult_e: { 532 CFArrayRef list; 533 534 list = CFNumberArrayCreateWithCStringArray((const char * *) 535 nl_p->ninl_val, 536 nl_p->ninl_len); 537 if (list != NULL) { 538 CFDictionarySetValue(dict, prop_name_cf, list); 539 CFRelease(list); 540 } 541 break; 542 } 543 case dhcptype_string_e: 544 case dhcptype_ip_e: { 545 CFStringRef str; 546 547 str = CFStringCreateWithCString(NULL, nl_p->ninl_val[0], 548 kCFStringEncodingASCII); 549 CFDictionarySetValue(dict, prop_name_cf, str); 550 CFRelease(str); 551 552 break; 553 } 554 case dhcptype_ip_mult_e: 555 case dhcptype_ip_pairs_e: 556 case dhcptype_dns_namelist_e: 557 handled = FALSE; 558 break; 559 560 case dhcptype_opaque_e: 561 default: 562 /* don't know what to do with these */ 563 break; 564 } 565 } 566 } 567 else if (strcmp(SUBNET_PROP_NAME, prop_name) == 0 568 || strcmp(SUBNET_PROP_NET_ADDRESS, prop_name) == 0 569 || strcmp(SUBNET_PROP_NET_MASK, prop_name) == 0 570 || strcmp(SUBNET_PROP_SUPERNET, prop_name) == 0 571 || strcmp(SUBNET_PROP__CREATOR, prop_name) == 0) { 572 CFStringRef str; 573 574 str = CFStringCreateWithCString(NULL, nl_p->ninl_val[0], 575 kCFStringEncodingASCII); 576 CFDictionarySetValue(dict, prop_name_cf, str); 577 CFRelease(str); 578 handled = TRUE; 579 } 580 else if (strcmp(SUBNET_PROP_LEASE_MIN, prop_name) == 0 581 || strcmp(SUBNET_PROP_LEASE_MAX, prop_name) == 0) { 582 handled = TRUE; 583 if (nl_p->ninl_len != 0) { 584 CFNumberRef num; 585 num = number_create(nl_p->ninl_val[0]); 586 if (num != NULL) { 587 CFDictionarySetValue(dict, prop_name_cf, num); 588 CFRelease(num); 589 } 590 else { 591 printf("bad conversion of %s\n", prop_name); 592 } 593 } 594 } 595 else if (strcmp(SUBNET_PROP_CLIENT_TYPES, prop_name) == 0) { 596 if (stringlist_contains_string((const char * *)nl_p->ninl_val, 597 nl_p->ninl_len, 598 "dhcp")) { 599 CFDictionarySetValue(dict, CFSTR("allocate"), 600 kCFBooleanTrue); 601 } 602 handled = TRUE; 603 } 604 if (handled == FALSE 605 && nl_p->ninl_len > 0) { 606 CFArrayRef list; 607 list = CFStringArrayCreateWithCStringArray((const char * *) 608 nl_p->ninl_val, 609 nl_p->ninl_len); 610 CFDictionarySetValue(dict, prop_name_cf, list); 611 CFRelease(list); 612 } 613 CFRelease(prop_name_cf); 614 } 615 return (dict); 616} 617 618static CFArrayRef 619config_dhcp_subnets_list_create(void * domain) 620{ 621 int i; 622 CFMutableArrayRef list = NULL; 623 ni_propids subnets; 624 ni_status status; 625 626 status = ni_propids_read(domain, NIDIR_CONFIG_DHCP_SUBNETS, &subnets); 627 if (status != NI_OK) { 628 return (NULL); 629 } 630 for (i = 0; i < subnets.ni_propids_len; i++) { 631 CFDictionaryRef dict; 632 ni_proplist * pl_p = subnets.ni_propids_props + i; 633 634 dict = subnet_dict_create_from_proplist(pl_p); 635 if (dict != NULL) { 636 if (list == NULL) { 637 list = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); 638 } 639 CFArrayAppendValue(list, dict); 640 CFRelease(dict); 641 } 642 } 643 ni_propids_free(&subnets); 644 return (list); 645 646} 647 648int 649main() 650{ 651 CFMutableDictionaryRef config; 652 CFDataRef data; 653 CFDictionaryRef dict; 654 CFArrayRef list; 655 void * ni_local; 656 ni_status status; 657 658 status = ni_open(NULL, ".", &ni_local); 659 if (status != NI_OK) { 660 fprintf(stderr, "ni_open . failed, %s\n", ni_error(status)); 661 exit(1); 662 } 663 dict = config_dhcp_dict_create(ni_local); 664 if (dict != NULL) { 665 config = CFDictionaryCreateMutableCopy(NULL, 0, dict); 666 CFRelease(dict); 667 } 668 else { 669 config = CFDictionaryCreateMutable(NULL, 0, 670 &kCFTypeDictionaryKeyCallBacks, 671 &kCFTypeDictionaryValueCallBacks); 672 } 673 dict = config_NetBootServer_dict_create(ni_local); 674 if (dict != NULL) { 675 CFDictionarySetValue(config, BOOTPD_PLIST_NETBOOT, dict); 676 CFRelease(dict); 677 } 678 list = config_dhcp_subnets_list_create(ni_local); 679 if (list != NULL) { 680 CFDictionarySetValue(config, BOOTPD_PLIST_SUBNETS, list); 681 CFRelease(list); 682 } 683 data = CFPropertyListCreateXMLData(NULL, config); 684 CFRelease(config); 685 fwrite(CFDataGetBytePtr(data), CFDataGetLength(data), 1, stdout); 686 CFRelease(data); 687 exit(0); 688} 689