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