#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#include <errno.h>
#include <stdlib.h>
#include <stdbool.h>
#include <stdint.h>
#include <CoreFoundation/CFDictionary.h>
#include <CoreFoundation/CFNumber.h>
#include <CoreFoundation/CFString.h>
#include <CoreFoundation/CFData.h>
#include <CoreFoundation/CFArray.h>
#include <netinfo/ni.h>
#include "dhcp_options.h"
#include "subnets.h"
#include "bootpd-plist.h"
#include "cfutil.h"
#define NIDIR_CONFIG_DHCP "/config/dhcp"
#define NIDIR_CONFIG_NETBOOTSERVER "/config/NetBootServer"
#define NIDIR_CONFIG_DHCP_SUBNETS "/config/dhcp/subnets"
#define NIPROP_NAME "name"
#define CFGPROP_BOOTP_ENABLED "bootp_enabled"
#define CFGPROP_DHCP_ENABLED "dhcp_enabled"
#define CFGPROP_OLD_NETBOOT_ENABLED "old_netboot_enabled"
#define CFGPROP_NETBOOT_ENABLED "netboot_enabled"
#define CFGPROP_RELAY_ENABLED "relay_enabled"
#define CFGPROP_ALLOW "allow"
#define CFGPROP_DENY "deny"
#define CFGPROP_RELAY_IP_LIST "relay_ip_list"
#define CFGPROP_DETECT_OTHER_DHCP_SERVER "detect_other_dhcp_server"
#define CFGPROP_REPLY_THRESHOLD_SECONDS "reply_threshold_seconds"
#define CFGPROP_SHADOW_SIZE_MEG "shadow_size_meg"
#define CFGPROP_AFP_USERS_MAX "afp_users_max"
#define CFGPROP_AGE_TIME_SECONDS "age_time_seconds"
#define CFGPROP_AFP_UID_START "afp_uid_start"
#define CFGPROP_MACHINE_NAME_FORMAT "machine_name_format"
ni_status
ni_read_dir(void * handle, char * path, ni_proplist * pl_p, ni_id * dir_id_p)
{
ni_id dir_id = {0,0};
ni_status status;
NI_INIT(pl_p);
status = ni_pathsearch(handle, &dir_id, path);
if (status != NI_OK) {
return (status);
}
status = ni_read(handle, &dir_id, pl_p);
if (status != NI_OK) {
return (status);
}
if (dir_id_p) {
*dir_id_p = dir_id;
}
return (status);
}
typedef struct {
ni_proplist * ni_propids_props;
u_long * ni_propids_ids;
int ni_propids_len;
ni_id ni_propids_dir;
void * ni_propids_domain;
} ni_propids;
void
ni_propids_free(ni_propids * props_p)
{
int i;
for (i = 0; i < props_p->ni_propids_len; i++) {
ni_proplist_free(props_p->ni_propids_props + i);
}
if (props_p->ni_propids_props != NULL) {
free(props_p->ni_propids_props);
}
if (props_p->ni_propids_ids != NULL) {
free(props_p->ni_propids_ids);
}
NI_INIT(props_p);
return;
}
static ni_status
ni_propids_read(void * domain,
const char * path,
ni_propids * props_p)
{
ni_id dir;
int i;
ni_proplist * p = NULL;
ni_status status = NI_OK;
ni_idlist ids;
NI_INIT(&ids);
NI_INIT(props_p);
props_p->ni_propids_domain = domain;
status = ni_pathsearch(domain, &dir, (char *)path);
if (status != NI_OK) {
goto done;
}
props_p->ni_propids_dir = dir;
status = ni_children(domain, &dir, &ids);
if (status != NI_OK) {
goto done;
}
if (ids.ni_idlist_len == 0) {
goto done;
}
p = (ni_proplist *)malloc(sizeof(*p) * ids.ni_idlist_len);
bzero(p, sizeof(*p) * ids.ni_idlist_len);
for (i = 0; i < ids.ni_idlist_len; i++) {
ni_id d;
d.nii_object = ids.ni_idlist_val[i];
status = ni_read(domain, &d, p + i);
if (status != NI_OK) {
int j;
for (j = 0; j < i; j++) {
ni_proplist_free(p + j);
}
goto done;
}
}
props_p->ni_propids_props = p;
props_p->ni_propids_ids = ids.ni_idlist_val;
props_p->ni_propids_len = ids.ni_idlist_len;
ids.ni_idlist_val = NULL;
ids.ni_idlist_len = 0;
done:
ni_idlist_free(&ids);
if (status != NI_OK) {
if (p != NULL) {
free(p);
}
}
return (status);
}
static CFArrayRef
CFStringArrayCreateWithCStringArray(const char * * list, int count)
{
int i;
CFMutableArrayRef list_cf = NULL;
if (count == 0) {
goto done;
}
list_cf = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
for (i = 0; i < count; i++) {
CFStringRef str;
str = CFStringCreateWithCString(NULL, list[i], kCFStringEncodingUTF8);
CFArrayAppendValue(list_cf, str);
CFRelease(str);
}
done:
return (list_cf);
}
static CFNumberRef
number_create(const char * str)
{
int val;
val = strtoul(str, NULL, 0);
if (val != ULONG_MAX && errno != ERANGE) {
return (CFNumberCreate(NULL, kCFNumberIntType, &val));
}
return (NULL);
}
static CFArrayRef
CFNumberArrayCreateWithCStringArray(const char * * list, int count)
{
int i;
CFMutableArrayRef list_cf = NULL;
if (count == 0) {
goto done;
}
list_cf = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
for (i = 0; i < count; i++) {
CFNumberRef num;
num = number_create(list[i]);
if (num != NULL) {
CFArrayAppendValue(list_cf, num);
CFRelease(num);
}
}
done:
return (list_cf);
}
static CFDictionaryRef
config_dhcp_dict_create_from_proplist(ni_proplist * pl)
{
CFMutableDictionaryRef dict;
int i;
dict = CFDictionaryCreateMutable(NULL, 0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
for (i = 0; i < pl->nipl_len; i++) {
bool handled = FALSE;
CFArrayRef list;
ni_namelist * nl_p;
ni_property * prop;
const char * prop_name;
CFStringRef prop_name_cf;
prop = &(pl->nipl_val[i]);
nl_p = &prop->nip_val;
prop_name = prop->nip_name;
if (strcmp(prop_name, NIPROP_NAME) == 0) {
continue;
}
prop_name_cf = CFStringCreateWithCString(NULL, prop_name,
kCFStringEncodingASCII);
if ((strcmp(prop_name, CFGPROP_BOOTP_ENABLED) == 0
|| strcmp(prop_name, CFGPROP_DHCP_ENABLED) == 0
|| strcmp(prop_name, CFGPROP_OLD_NETBOOT_ENABLED) == 0
|| strcmp(prop_name, CFGPROP_NETBOOT_ENABLED) == 0
|| strcmp(prop_name, CFGPROP_RELAY_ENABLED) == 0)) {
switch (nl_p->ninl_len) {
case 0:
handled = TRUE;
CFDictionarySetValue(dict, prop_name_cf, kCFBooleanTrue);
break;
case 1:
if (strcmp(nl_p->ninl_val[0], "") == 0) {
handled = TRUE;
CFDictionarySetValue(dict, prop_name_cf, kCFBooleanFalse);
break;
}
break;
default:
break;
}
}
else if (strcmp(prop_name, CFGPROP_DETECT_OTHER_DHCP_SERVER) == 0) {
handled = TRUE;
if (nl_p->ninl_len != 0
&& strtol(nl_p->ninl_val[0], NULL, 0) != 0) {
CFDictionarySetValue(dict, prop_name_cf, kCFBooleanTrue);
}
else {
CFDictionarySetValue(dict, prop_name_cf, kCFBooleanFalse);
}
}
else if (strcmp(prop_name, CFGPROP_REPLY_THRESHOLD_SECONDS) == 0) {
handled = TRUE;
if (nl_p->ninl_len != 0) {
CFNumberRef num;
num = number_create(nl_p->ninl_val[0]);
if (num != NULL) {
CFDictionarySetValue(dict, prop_name_cf, num);
CFRelease(num);
}
}
}
if (handled == FALSE
&& CFDictionaryContainsKey(dict, prop_name_cf) == FALSE
&& nl_p->ninl_len > 0) {
list = CFStringArrayCreateWithCStringArray((const char * *)
nl_p->ninl_val,
nl_p->ninl_len);
CFDictionarySetValue(dict, prop_name_cf, list);
CFRelease(list);
}
CFRelease(prop_name_cf);
}
return (dict);
}
static CFDictionaryRef
config_dhcp_dict_create(void * domain)
{
CFDictionaryRef dict = NULL;
ni_proplist pl;
ni_status status;
NI_INIT(&pl);
status = ni_read_dir(domain, NIDIR_CONFIG_DHCP, &pl, NULL);
if (status == NI_OK) {
dict = config_dhcp_dict_create_from_proplist(&pl);
ni_proplist_free(&pl);
}
return (dict);
}
static CFDictionaryRef
config_NetBootServer_dict_create_from_proplist(ni_proplist * pl)
{
CFMutableDictionaryRef dict;
int i;
dict = CFDictionaryCreateMutable(NULL, 0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
for (i = 0; i < pl->nipl_len; i++) {
ni_namelist * nl_p;
ni_property * prop;
const char * prop_name;
CFStringRef prop_name_cf;
prop = &(pl->nipl_val[i]);
nl_p = &prop->nip_val;
prop_name = prop->nip_name;
if (strcmp(prop_name, NIPROP_NAME) == 0) {
continue;
}
prop_name_cf = CFStringCreateWithCString(NULL, prop_name,
kCFStringEncodingASCII);
if (strcmp(CFGPROP_SHADOW_SIZE_MEG, prop_name) == 0
|| strcmp(CFGPROP_AFP_USERS_MAX, prop_name) == 0
|| strcmp(CFGPROP_AGE_TIME_SECONDS, prop_name) == 0
|| strcmp(CFGPROP_AFP_UID_START, prop_name) == 0) {
if (nl_p->ninl_len != 0) {
CFNumberRef num;
num = number_create(nl_p->ninl_val[0]);
if (num != NULL) {
CFDictionarySetValue(dict, prop_name_cf, num);
CFRelease(num);
}
}
}
else if (strcmp(CFGPROP_MACHINE_NAME_FORMAT, prop_name) == 0
&& nl_p->ninl_len != 0) {
CFStringRef format;
format = CFStringCreateWithCString(NULL, nl_p->ninl_val[0],
kCFStringEncodingUTF8);
CFDictionarySetValue(dict, prop_name_cf, format);
CFRelease(format);
}
CFRelease(prop_name_cf);
}
return (dict);
}
static CFDictionaryRef
config_NetBootServer_dict_create(void * domain)
{
CFDictionaryRef dict = NULL;
ni_proplist pl;
ni_status status;
NI_INIT(&pl);
status = ni_read_dir(domain, NIDIR_CONFIG_NETBOOTSERVER, &pl, NULL);
if (status == NI_OK) {
dict = config_NetBootServer_dict_create_from_proplist(&pl);
ni_proplist_free(&pl);
}
return (dict);
}
static bool
stringlist_contains_string(const char * * list, int list_len,
const char * str)
{
int i;
for (i = 0; i < list_len; i++) {
if (strcmp(list[i], str) == 0) {
return (TRUE);
}
}
return (FALSE);
}
static CFDictionaryRef
subnet_dict_create_from_proplist(ni_proplist * pl_p)
{
CFMutableDictionaryRef dict;
int i;
dict = CFDictionaryCreateMutable(NULL, 0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
for (i = 0; i < pl_p->nipl_len; i++) {
bool handled = FALSE;
ni_namelist * nl_p;
ni_property * prop;
const char * prop_name;
CFStringRef prop_name_cf;
prop = &(pl_p->nipl_val[i]);
nl_p = &prop->nip_val;
prop_name = prop->nip_name;
prop_name_cf = CFStringCreateWithCString(NULL, prop_name,
kCFStringEncodingASCII);
if (strncmp(prop_name, "dhcp_", 5) == 0) {
dhcptag_t tag;
const dhcptag_info_t * tag_info;
const dhcptype_info_t * type_info = NULL;
handled = TRUE;
tag = dhcptag_with_name(prop_name + 5);
if (tag != -1) {
tag_info = dhcptag_info(tag);
if (tag_info != NULL) {
type_info = dhcptype_info(tag_info->type);
}
}
if (type_info != NULL) {
switch (tag_info->type) {
case dhcptype_bool_e:
if (nl_p->ninl_len != 0
&& strtol(nl_p->ninl_val[0], NULL, 0) != 0) {
CFDictionarySetValue(dict, prop_name_cf,
kCFBooleanTrue);
}
else {
CFDictionarySetValue(dict, prop_name_cf,
kCFBooleanFalse);
}
break;
case dhcptype_int32_e:
case dhcptype_uint8_e:
case dhcptype_uint16_e:
case dhcptype_uint32_e:
if (nl_p->ninl_len != 0) {
CFNumberRef num;
num = number_create(nl_p->ninl_val[0]);
if (num != NULL) {
CFDictionarySetValue(dict, prop_name_cf, num);
CFRelease(num);
}
}
break;
case dhcptype_uint8_mult_e:
case dhcptype_uint16_mult_e: {
CFArrayRef list;
list = CFNumberArrayCreateWithCStringArray((const char * *)
nl_p->ninl_val,
nl_p->ninl_len);
if (list != NULL) {
CFDictionarySetValue(dict, prop_name_cf, list);
CFRelease(list);
}
break;
}
case dhcptype_string_e:
case dhcptype_ip_e: {
CFStringRef str;
str = CFStringCreateWithCString(NULL, nl_p->ninl_val[0],
kCFStringEncodingASCII);
CFDictionarySetValue(dict, prop_name_cf, str);
CFRelease(str);
break;
}
case dhcptype_ip_mult_e:
case dhcptype_ip_pairs_e:
case dhcptype_dns_namelist_e:
handled = FALSE;
break;
case dhcptype_opaque_e:
default:
break;
}
}
}
else if (strcmp(SUBNET_PROP_NAME, prop_name) == 0
|| strcmp(SUBNET_PROP_NET_ADDRESS, prop_name) == 0
|| strcmp(SUBNET_PROP_NET_MASK, prop_name) == 0
|| strcmp(SUBNET_PROP_SUPERNET, prop_name) == 0
|| strcmp(SUBNET_PROP__CREATOR, prop_name) == 0) {
CFStringRef str;
str = CFStringCreateWithCString(NULL, nl_p->ninl_val[0],
kCFStringEncodingASCII);
CFDictionarySetValue(dict, prop_name_cf, str);
CFRelease(str);
handled = TRUE;
}
else if (strcmp(SUBNET_PROP_LEASE_MIN, prop_name) == 0
|| strcmp(SUBNET_PROP_LEASE_MAX, prop_name) == 0) {
handled = TRUE;
if (nl_p->ninl_len != 0) {
CFNumberRef num;
num = number_create(nl_p->ninl_val[0]);
if (num != NULL) {
CFDictionarySetValue(dict, prop_name_cf, num);
CFRelease(num);
}
else {
printf("bad conversion of %s\n", prop_name);
}
}
}
else if (strcmp(SUBNET_PROP_CLIENT_TYPES, prop_name) == 0) {
if (stringlist_contains_string((const char * *)nl_p->ninl_val,
nl_p->ninl_len,
"dhcp")) {
CFDictionarySetValue(dict, CFSTR("allocate"),
kCFBooleanTrue);
}
handled = TRUE;
}
if (handled == FALSE
&& nl_p->ninl_len > 0) {
CFArrayRef list;
list = CFStringArrayCreateWithCStringArray((const char * *)
nl_p->ninl_val,
nl_p->ninl_len);
CFDictionarySetValue(dict, prop_name_cf, list);
CFRelease(list);
}
CFRelease(prop_name_cf);
}
return (dict);
}
static CFArrayRef
config_dhcp_subnets_list_create(void * domain)
{
int i;
CFMutableArrayRef list = NULL;
ni_propids subnets;
ni_status status;
status = ni_propids_read(domain, NIDIR_CONFIG_DHCP_SUBNETS, &subnets);
if (status != NI_OK) {
return (NULL);
}
for (i = 0; i < subnets.ni_propids_len; i++) {
CFDictionaryRef dict;
ni_proplist * pl_p = subnets.ni_propids_props + i;
dict = subnet_dict_create_from_proplist(pl_p);
if (dict != NULL) {
if (list == NULL) {
list = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
}
CFArrayAppendValue(list, dict);
CFRelease(dict);
}
}
ni_propids_free(&subnets);
return (list);
}
int
main()
{
CFMutableDictionaryRef config;
CFDataRef data;
CFDictionaryRef dict;
CFArrayRef list;
void * ni_local;
ni_status status;
status = ni_open(NULL, ".", &ni_local);
if (status != NI_OK) {
fprintf(stderr, "ni_open . failed, %s\n", ni_error(status));
exit(1);
}
dict = config_dhcp_dict_create(ni_local);
if (dict != NULL) {
config = CFDictionaryCreateMutableCopy(NULL, 0, dict);
CFRelease(dict);
}
else {
config = CFDictionaryCreateMutable(NULL, 0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
}
dict = config_NetBootServer_dict_create(ni_local);
if (dict != NULL) {
CFDictionarySetValue(config, BOOTPD_PLIST_NETBOOT, dict);
CFRelease(dict);
}
list = config_dhcp_subnets_list_create(ni_local);
if (list != NULL) {
CFDictionarySetValue(config, BOOTPD_PLIST_SUBNETS, list);
CFRelease(list);
}
data = CFPropertyListCreateXMLData(NULL, config);
CFRelease(config);
fwrite(CFDataGetBytePtr(data), CFDataGetLength(data), 1, stdout);
CFRelease(data);
exit(0);
}