1/*
2 * Copyright (c) 2000-2014 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/*
25 * DHCPServer.c
26 */
27/*
28 * Modification History
29 *
30 * November 10, 2000 	Dieter Siegmund (dieter@apple.com)
31 * - initial revision
32 */
33
34#include <unistd.h>
35#include <stdlib.h>
36#include <stdio.h>
37#include <sys/types.h>
38#include <sys/time.h>
39#include <CoreFoundation/CoreFoundation.h>
40#include "NICache.h"
41#include "NICachePrivate.h"
42#include "netinfo.h"
43#include "bsdp.h"
44#include "DHCPServer.h"
45
46const char * 		DHCPSDHCPLeaseListNotificationKey = DHCPD_LEASES_NOTIFICATION_KEY;
47
48const char * 		DHCPSDisabledInterfacesNotificationKey = DHCPD_DISABLED_INTERFACES_NOTIFICATION_KEY;
49
50
51const CFStringRef	kDHCPSPropName = CFSTR(NIPROP_NAME);
52const CFStringRef	kDHCPSPropIdentifier = CFSTR(NIPROP_IDENTIFIER);
53
54const CFStringRef	kDHCPSPropDHCPHWAddress = CFSTR(NIPROP_HWADDR);
55const CFStringRef	kDHCPSPropDHCPIPAddress = CFSTR(NIPROP_IPADDR);
56const CFStringRef	kDHCPSPropDHCPLease = CFSTR(NIPROP_DHCP_LEASE);
57#if ! TARGET_OS_EMBEDDED
58const CFStringRef	kDHCPSPropNetBootArch = CFSTR(NIPROP_NETBOOT_ARCH);
59const CFStringRef	kDHCPSPropNetBootSysid = CFSTR(NIPROP_NETBOOT_SYSID);
60const CFStringRef	kDHCPSPropNetBootLastBootTime = CFSTR(NIPROP_NETBOOT_LAST_BOOT_TIME);
61const CFStringRef	kDHCPSPropNetBootIPAddress = CFSTR(NIPROP_IPADDR);
62const CFStringRef	kDHCPSPropNetBootImageID = CFSTR(NIPROP_NETBOOT_IMAGE_ID);
63const CFStringRef	kDHCPSPropNetBootImageIndex = CFSTR(NIPROP_NETBOOT_IMAGE_INDEX);
64const CFStringRef	kDHCPSPropNetBootImageKind = CFSTR(NIPROP_NETBOOT_IMAGE_KIND);
65const CFStringRef	kDHCPSPropNetBootImageIsInstall = CFSTR(NIPROP_NETBOOT_IMAGE_IS_INSTALL);
66#endif /* ! TARGET_OS_EMBEDDED */
67
68static CFStringRef
69create_cfstring(const char * name)
70{
71    CFStringRef		str;
72
73    str = CFStringCreateWithCString(NULL, name, kCFStringEncodingUTF8);
74    if (str == NULL) {
75	str = CFStringCreateWithCString(NULL, name, kCFStringEncodingMacRoman);
76    }
77    return (str);
78}
79
80static CFMutableArrayRef
81host_list_copy(const char * filename)
82{
83    CFMutableArrayRef		arr = NULL;
84    PLCache_t			cache;
85    PLCacheEntry_t * 		scan;
86
87    PLCache_init(&cache);
88#define ARBITRARILY_LARGE_NUMBER	(100 * 1024 * 1024)
89    PLCache_set_max(&cache, ARBITRARILY_LARGE_NUMBER);
90    if (PLCache_read(&cache, filename) == FALSE) {
91	return (NULL);
92    }
93
94    arr = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
95    for (scan = cache.head; scan != NULL; scan = scan->next) {
96	int			i;
97	CFMutableDictionaryRef	dict = NULL;
98	ni_proplist *		pl = &scan->pl;
99
100	if (pl->ni_proplist_len == 0) {
101	    continue;
102	}
103	for (i = 0; i < pl->ni_proplist_len; i++) {
104	    CFStringRef		name;
105	    ni_property *	prop;
106	    CFStringRef		val;
107
108	    prop = pl->nipl_val + i;
109	    if (prop->nip_val.ninl_len == 0) {
110		continue;
111	    }
112	    if (dict == NULL) {
113		dict = CFDictionaryCreateMutable(NULL, 0,
114						 &kCFTypeDictionaryKeyCallBacks,
115						 &kCFTypeDictionaryValueCallBacks);
116	    }
117	    name = create_cfstring(prop->nip_name);
118	    val = create_cfstring(prop->nip_val.ninl_val[0]);
119	    if (name != NULL && val != NULL) {
120		CFDictionarySetValue(dict, name, val);
121	    }
122	    if (name != NULL) {
123		CFRelease(name);
124	    }
125	    if (val != NULL) {
126		CFRelease(val);
127	    }
128	}
129	if (dict != NULL) {
130	    CFArrayAppendValue(arr, dict);
131	    CFRelease(dict);
132	}
133    }
134    if (CFArrayGetCount(arr) == 0) {
135	CFRelease(arr);
136	arr = NULL;
137    }
138    PLCache_free(&cache);
139    return (arr);
140}
141
142static int
143cfstring_to_cstring(CFStringRef cfstr, char * str, int len)
144{
145    CFIndex		l;
146    CFRange		range;
147
148    range = CFRangeMake(0, CFStringGetLength(cfstr));
149    (void)CFStringGetBytes(cfstr, range, kCFStringEncodingMacRoman,
150			   0, FALSE, (UInt8 *)str, len, &l);
151    str[l] = '\0';
152    return (l);
153}
154
155#ifdef TEST_DHCPHOSTLIST
156static void
157dump_gregorian_date(CFGregorianDate d)
158{
159    printf("%d/%d/%d %d:%d:%d\n",
160	   (int)d.year, d.month, d.day, d.hour, d.minute, (int)d.second);
161    return;
162}
163
164static void
165show_date(CFAbsoluteTime t)
166{
167    CFGregorianDate d;
168    static CFTimeZoneRef tz = NULL;
169
170    if (tz == NULL) {
171	tz = CFTimeZoneCopySystem();
172    }
173
174    d = CFAbsoluteTimeGetGregorianDate(t, tz);
175    dump_gregorian_date(d);
176    return;
177}
178#endif /* TEST_DHCPHOSTLIST */
179
180static CFArrayRef
181cook_for_dhcp(CFArrayRef arr)
182{
183    int			count;
184    int 		i;
185    CFAbsoluteTime 	now_cf;
186    struct timeval 	now;
187
188    gettimeofday(&now, 0);
189    now_cf = CFAbsoluteTimeGetCurrent();
190
191    count = CFArrayGetCount(arr);
192    for (i = 0; i < count; i++) {
193	char			buf[128];
194	CFDateRef		expiration;
195	long			lease_val = 0;
196	long			lease_delta = 0;
197	CFStringRef		lease;
198	CFMutableDictionaryRef 	dict = (CFMutableDictionaryRef)CFArrayGetValueAtIndex(arr, i);
199
200	lease = CFDictionaryGetValue(dict, kDHCPSPropDHCPLease);
201	if (lease) {
202	    cfstring_to_cstring(lease, buf, sizeof(buf));
203	    lease_val = strtol(buf, 0, 0);
204	    lease_delta = lease_val - now.tv_sec;
205#ifdef TEST_DHCPHOSTLIST
206	    {
207		CFAbsoluteTime		abs_exp;
208		abs_exp = lease_delta + now_cf;
209		show_date(abs_exp);
210	    }
211#endif /* TEST_DHCPHOSTLIST */
212	    expiration = CFDateCreate(NULL, lease_delta + now_cf);
213	    CFDictionarySetValue(dict, kDHCPSPropDHCPLease,
214				 expiration);
215	    CFRelease(expiration);
216	}
217    }
218    return (arr);
219}
220
221#if ! TARGET_OS_EMBEDDED
222static CFArrayRef
223cook_for_netboot(CFArrayRef arr)
224{
225    int			count;
226    int 		i;
227    CFAbsoluteTime 	now_cf;
228    struct timeval 	now;
229
230    gettimeofday(&now, 0);
231    now_cf = CFAbsoluteTimeGetCurrent();
232
233    count = CFArrayGetCount(arr);
234    for (i = 0; i < count; i++) {
235	CFMutableDictionaryRef 	dict;
236	char			buf[128];
237	CFStringRef		image_id_str;
238	uint32_t		image_id;
239	CFDateRef		last_boot_time;
240	long			last_boot_val = 0;
241	long			last_boot_delta = 0;
242	CFStringRef		last_boot_time_str;
243
244	dict = (CFMutableDictionaryRef)CFArrayGetValueAtIndex(arr, i);
245	last_boot_time_str
246	    = CFDictionaryGetValue(dict, kDHCPSPropNetBootLastBootTime);
247	if (last_boot_time_str) {
248	    cfstring_to_cstring(last_boot_time_str, buf, sizeof(buf));
249	    last_boot_val = strtol(buf, 0, 0);
250	    last_boot_delta = last_boot_val - now.tv_sec;
251#ifdef TEST_DHCPHOSTLIST
252	    {
253		CFAbsoluteTime		abs_exp;
254		abs_exp = last_boot_delta + now_cf;
255		show_date(abs_exp);
256	    }
257#endif /* TEST_DHCPHOSTLIST */
258	    last_boot_time = CFDateCreate(NULL, last_boot_delta + now_cf);
259	    CFDictionarySetValue(dict, kDHCPSPropNetBootLastBootTime,
260				 last_boot_time);
261	    CFRelease(last_boot_time);
262	}
263	image_id_str = CFDictionaryGetValue(dict, kDHCPSPropNetBootImageID);
264	if (image_id_str != NULL) {
265	    CFNumberRef		num;
266	    uint32_t		image_attrs;
267	    uint32_t		image_index;
268	    uint32_t		image_kind;
269
270	    cfstring_to_cstring(image_id_str, buf, sizeof(buf));
271	    image_id = strtoul(buf, NULL, 0);
272	    image_attrs = bsdp_image_attributes(image_id);
273	    image_index = bsdp_image_index(image_id);
274	    image_kind = bsdp_image_kind_from_attributes(image_attrs);
275
276	    /* set the Index */
277	    num = CFNumberCreate(NULL, kCFNumberSInt32Type, &image_index);
278	    CFDictionarySetValue(dict, kDHCPSPropNetBootImageIndex, num);
279	    CFRelease(num);
280
281	    /* set the Kind */
282	    num = CFNumberCreate(NULL, kCFNumberSInt32Type, &image_kind);
283	    CFDictionarySetValue(dict, kDHCPSPropNetBootImageKind, num);
284	    CFRelease(num);
285
286	    /* set IsInstall */
287	    if (bsdp_image_identifier_is_install(image_id)) {
288		CFDictionarySetValue(dict, kDHCPSPropNetBootImageIsInstall,
289				     kCFBooleanTrue);
290	    }
291	    else {
292		CFDictionarySetValue(dict, kDHCPSPropNetBootImageIsInstall,
293				     kCFBooleanFalse);
294	    }
295	}
296    }
297    return (arr);
298}
299#endif /* ! TARGET_OS_EMBEDDED */
300
301CFArrayRef
302DHCPSDHCPLeaseListCreate()
303{
304    CFArrayRef arr;
305
306    arr = host_list_copy("/var/db/dhcpd_leases");
307    if (arr == NULL) {
308	return (NULL);
309    }
310
311    if (cook_for_dhcp(arr) == NULL) {
312	CFRelease(arr);
313	return (NULL);
314    }
315    return (arr);
316}
317
318#if ! TARGET_OS_EMBEDDED
319CFArrayRef
320DHCPSNetBootClientListCreate()
321{
322    CFArrayRef arr;
323
324    arr = host_list_copy("/var/db/bsdpd_clients");
325    if (arr == NULL) {
326	return (NULL);
327    }
328    if (cook_for_netboot(arr) == NULL) {
329	CFRelease(arr);
330	return (NULL);
331    }
332    return (arr);
333}
334#endif /* ! TARGET_OS_EMBEDDED */
335
336#include <SystemConfiguration/SystemConfiguration.h>
337
338CFArrayRef
339DHCPSCopyDisabledInterfaces(void)
340{
341    CFDictionaryRef	dict;
342    CFArrayRef		list = NULL;
343
344    dict = SCDynamicStoreCopyValue(NULL, CFSTR(DHCPD_DYNAMIC_STORE_KEY));
345    if (dict != NULL) {
346	list = CFDictionaryGetValue(dict, CFSTR(DHCPD_DISABLED_INTERFACES));
347	if (list != NULL) {
348	    CFRetain(list);
349	}
350	CFRelease(dict);
351    }
352    return (list);
353}
354
355#ifdef TEST_DHCPHOSTLIST
356int
357main(int argc, char * argv[])
358{
359    CFArrayRef arr;
360
361    arr = DHCPSDHCPLeaseListCreate();
362    if (arr) {
363	printf("DHCP Clients\n");
364	CFShow(arr);
365	CFRelease(arr);
366    }
367    arr = DHCPSNetBootClientListCreate();
368    if (arr) {
369	printf("\nNetBoot Clients\n");
370	CFShow(arr);
371	CFRelease(arr);
372    }
373    exit(0);
374}
375#endif /* TEST_DHCPHOSTLIST */
376