1/*
2 * Copyright (c) 2001, 2003-2005, 2011, 2013 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#include <stdio.h>
25#include <unistd.h>
26
27
28#include "SystemConfiguration.h"
29#include "SCValidation.h"
30#include "SCDynamicStoreCopyDHCPInfo.h"
31#include "DHCPClientPreferences.h"
32
33#define DHCPCLIENT_PREFERENCES_ID		"DHCPClient.xml"
34#define DHCPCLIENT_APPLICATION_PREF		"Application"
35
36#define DHCP_REQUESTED_PARAMETER_LIST		"DHCPRequestedParameterList"
37
38/**
39 ** DHCPClientPreferences{Set,Get}ApplicationOptions()
40 **/
41static UInt8 *
42S_get_char_array(CFArrayRef arr, CFIndex * len)
43{
44    UInt8 *	buf = NULL;
45    CFIndex	count = 0;
46    CFIndex 	i;
47    CFIndex 	real_count;
48
49    if (arr) {
50	count = CFArrayGetCount(arr);
51    }
52    if (count == 0) {
53	goto done;
54    }
55    buf = malloc(count);
56    if (buf == NULL) {
57	goto done;
58    }
59    for (i = 0, real_count = 0; i < count; i++) {
60	CFNumberRef	n = isA_CFNumber(CFArrayGetValueAtIndex(arr, i));
61	int 		val;
62
63	if (n && CFNumberGetValue(n, kCFNumberIntType, &val)) {
64	    buf[real_count++] = (UInt8) val;
65	}
66    }
67    count = real_count;
68 done:
69    *len = count;
70    if (count == 0 && buf) {
71	free(buf);
72	buf = NULL;
73    }
74    return (buf);
75}
76
77static void
78my_CFArrayAppendUniqueValue(CFMutableArrayRef arr, CFTypeRef new)
79{
80    CFIndex i;
81    CFIndex n = CFArrayGetCount(arr);
82
83    for (i = 0; i < n; i++) {
84	CFStringRef element = CFArrayGetValueAtIndex(arr, i);
85	if (CFEqual(element, new)) {
86	    return;
87	}
88    }
89    CFArrayAppendValue(arr, new);
90    return;
91}
92
93static __inline__ CF_RETURNS_RETAINED CFStringRef
94S_application_path(CFStringRef applicationID)
95{
96    return (CFStringCreateWithFormat(NULL, NULL,
97				     CFSTR("/" DHCPCLIENT_APPLICATION_PREF
98					   "/%@"),
99				     applicationID));
100}
101
102Boolean
103DHCPClientPreferencesSetApplicationOptions(CFStringRef applicationID,
104					   UInt8 * options,
105					   CFIndex count)
106{
107    CFMutableDictionaryRef	dict = NULL;
108    CFStringRef			path = NULL;
109    SCPreferencesRef		prefs = NULL;
110    Boolean			success = FALSE;
111
112    if (applicationID == NULL) {
113	goto done;
114    }
115    path = S_application_path(applicationID);
116    if (path == NULL) {
117	goto done;
118    }
119    prefs = SCPreferencesCreate(NULL, CFSTR("DHCPClientSetAppReqParams"),
120				CFSTR(DHCPCLIENT_PREFERENCES_ID));
121    if (prefs == NULL) {
122	goto done;
123    }
124    dict = (CFMutableDictionaryRef)SCPreferencesPathGetValue(prefs, path);
125    if (dict == NULL) {
126	dict = CFDictionaryCreateMutable(NULL, 0,
127					 &kCFTypeDictionaryKeyCallBacks,
128					 &kCFTypeDictionaryValueCallBacks);
129    }
130    else {
131	dict = CFDictionaryCreateMutableCopy(NULL, 0, dict);
132    }
133    if (dict == NULL) {
134	goto done;
135    }
136    if (options && count > 0) {
137	int 			i;
138	CFMutableArrayRef	array;
139
140	array = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
141	if (array == NULL) {
142	    goto done;
143	}
144	for (i = 0; i < count; i++) {
145	    int			val;
146	    CFNumberRef		number;
147
148	    if (options[i] == 0 || options[i] == 255) {
149		/* ignore pads and end */
150		continue;
151	    }
152	    val = options[i];
153	    number = CFNumberCreate(NULL, kCFNumberIntType, &val);
154	    if (number == NULL) {
155		CFRelease(array);
156		goto done;
157	    }
158	    my_CFArrayAppendUniqueValue(array, number);
159	    CFRelease(number);
160	}
161	CFDictionarySetValue(dict, CFSTR(DHCP_REQUESTED_PARAMETER_LIST),
162			     array);
163	CFRelease(array);
164    }
165    else {
166	CFDictionaryRemoveValue(dict, CFSTR(DHCP_REQUESTED_PARAMETER_LIST));
167    }
168    if (SCPreferencesLock(prefs, TRUE)) {
169	success = SCPreferencesPathSetValue(prefs, path, dict);
170	if (success) {
171	    success = SCPreferencesCommitChanges(prefs);
172	    if (success) {
173		(void)SCPreferencesApplyChanges(prefs);
174	    }
175	}
176	(void)SCPreferencesUnlock(prefs);
177    }
178 done:
179    if (prefs) {
180	CFRelease(prefs);
181    }
182    if (path) {
183	CFRelease(path);
184    }
185    if (dict) {
186	CFRelease(dict);
187    }
188    return (success);
189}
190
191UInt8 *
192DHCPClientPreferencesCopyApplicationOptions(CFStringRef applicationID,
193					    CFIndex * count)
194{
195    CFDictionaryRef		dict = NULL;
196    UInt8 *			options = NULL;
197    CFArrayRef			parms;
198    CFStringRef			path = NULL;
199    SCPreferencesRef		prefs = NULL;
200
201    if (applicationID == NULL) {
202	goto done;
203    }
204    path = S_application_path(applicationID);
205    if (path == NULL) {
206	goto done;
207    }
208    prefs = SCPreferencesCreate(NULL, CFSTR("DHCPClientCopyAppReqParams"),
209				CFSTR(DHCPCLIENT_PREFERENCES_ID));
210    if (prefs == NULL) {
211	goto done;
212    }
213    dict = SCPreferencesPathGetValue(prefs, path);
214    if (dict == NULL) {
215	goto done;
216    }
217    parms = CFDictionaryGetValue(dict,
218				 CFSTR(DHCP_REQUESTED_PARAMETER_LIST));
219    if (isA_CFArray(parms) == NULL) {
220	goto done;
221    }
222    options = S_get_char_array(parms, count);
223
224 done:
225    if (prefs) {
226	CFRelease(prefs);
227    }
228    if (path) {
229	CFRelease(path);
230    }
231    return (options);
232}
233
234/**
235 ** DHCPClientInfo*()
236 **/
237
238CFDictionaryRef
239SCDynamicStoreCopyDHCPInfo(SCDynamicStoreRef store, CFStringRef serviceID)
240{
241    CFDictionaryRef	dhcp_dict = NULL;
242    CFStringRef		key = NULL;
243    CFDictionaryRef	primary_dict = NULL;
244
245    if (serviceID == NULL) {
246	/* get the primary service name */
247	key = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL,
248							 kSCDynamicStoreDomainState,
249							 kSCEntNetIPv4);
250	if (key) {
251	    primary_dict = SCDynamicStoreCopyValue(store, key);
252	    if (primary_dict) {
253		serviceID = CFDictionaryGetValue(primary_dict,
254						 kSCDynamicStorePropNetPrimaryService);
255	    }
256	    CFRelease(key);
257	}
258    }
259    if (serviceID == NULL) {
260	goto done;
261    }
262    key = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
263						      kSCDynamicStoreDomainState,
264						      serviceID,
265						      kSCEntNetDHCP);
266    if (key) {
267	dhcp_dict = SCDynamicStoreCopyValue(store, key);
268	if (dhcp_dict != NULL
269	    && isA_CFDictionary(dhcp_dict) == NULL) {
270	    CFRelease(dhcp_dict);
271	    dhcp_dict = NULL;
272	}
273	CFRelease(key);
274    }
275 done:
276    if (primary_dict) {
277	CFRelease(primary_dict);
278    }
279    return (dhcp_dict);
280}
281
282CFDataRef
283DHCPInfoGetOptionData(CFDictionaryRef dhcp_dict, UInt8 code)
284{
285    CFDataRef		data = NULL;
286    CFStringRef		option_code_str = NULL;
287
288    option_code_str = CFStringCreateWithFormat(NULL, NULL,
289					       CFSTR("Option_%d"), code);
290    if (option_code_str == NULL) {
291	goto done;
292    }
293
294    data = CFDictionaryGetValue(dhcp_dict, option_code_str);
295    data = isA_CFData(data);
296 done:
297    if (option_code_str)
298	CFRelease(option_code_str);
299    return (data);
300}
301
302CFDateRef
303DHCPInfoGetLeaseStartTime(CFDictionaryRef dhcp_dict)
304{
305    return (CFDictionaryGetValue(dhcp_dict, CFSTR("LeaseStartTime")));
306}
307
308CFDateRef
309DHCPInfoGetLeaseExpirationTime(CFDictionaryRef dhcp_dict)
310{
311    return (CFDictionaryGetValue(dhcp_dict, CFSTR("LeaseExpirationTime")));
312}
313
314#ifdef TEST_DHCPCLIENT_PREFERENCES
315void
316print_data(u_char * data_p, int n_bytes)
317{
318#define CHARS_PER_LINE 	16
319    char		line_buf[CHARS_PER_LINE + 1];
320    int			line_pos;
321    int			offset;
322
323    for (line_pos = 0, offset = 0; offset < n_bytes; offset++, data_p++) {
324	if (line_pos == 0)
325	    printf("%04x ", offset);
326
327	line_buf[line_pos] = isprint(*data_p) ? *data_p : '.';
328	printf(" %02x", *data_p);
329	line_pos++;
330	if (line_pos == CHARS_PER_LINE) {
331	    line_buf[CHARS_PER_LINE] = '\0';
332	    printf("  %s\n", line_buf);
333	    line_pos = 0;
334	}
335	else if (line_pos == (CHARS_PER_LINE / 2))
336	    printf(" ");
337    }
338    if (line_pos) { /* need to finish up the line */
339	for (; line_pos < CHARS_PER_LINE; line_pos++) {
340	    printf("   ");
341	    line_buf[line_pos] = ' ';
342	}
343	line_buf[CHARS_PER_LINE] = '\0';
344	printf("  %s\n", line_buf);
345    }
346}
347
348#define CMDSTR_GETOPTION	"getoption"
349#define CMDSTR_LEASE		"leaseinfo"
350#define CMDSTR_GETPARAMS	"getparams"
351#define CMDSTR_SETPARAMS	"setparams"
352
353static __inline__ void
354S_print_char_array(UInt8 * params, int n_params)
355{
356    int i;
357
358    for (i = 0; i < n_params; i++) {
359	if (i == 0)
360	    printf("%d", params[i]);
361	else
362	    printf(", %d", params[i]);
363    }
364    return;
365}
366
367void
368usage(char * prog)
369{
370    printf("%s " CMDSTR_GETOPTION " <serviceID> <opt> [ <type> ]\n"
371	   "%s " CMDSTR_LEASE " <serviceID>\n"
372	   "%s " CMDSTR_GETPARAMS " <app_id>\n"
373	   "%s " CMDSTR_SETPARAMS " <app_id> [ <opt> [ <opt> ] ... [ <opt> ] ] ]\n"
374	   "where:\n"
375	   "  <serviceID>     : service ID string | \"\"\n"
376	   "  <opt>           : DHCP/BOOTP option code\n"
377	   "                    (e.g. 1 == subnet mask, 3 == router, 6 = dns, 15 = domain)\n"
378	   "  <type>          : type of option: string, ip\n"
379	   "  <app_id>        : application id (e.g. com.apple.ntpd, com.thursby.Dave)\n",
380	   prog, prog, prog, prog);
381    exit(0);
382}
383
384static void
385dump_gregorian_date(CFGregorianDate d)
386{
387    printf("%d/%d/%d %02d:%02d:%02d\n",
388	   (int)d.year, d.month, d.day, d.hour, d.minute, (int)d.second);
389    return;
390}
391
392static void
393show_date(CFAbsoluteTime t)
394{
395    CFGregorianDate d;
396    static CFTimeZoneRef tz = NULL;
397
398    if (tz == NULL) {
399	tz = CFTimeZoneCopySystem();
400    }
401
402    d = CFAbsoluteTimeGetGregorianDate(t, tz);
403    dump_gregorian_date(d);
404    return;
405}
406
407#define IP_FORMAT	"%d.%d.%d.%d"
408#define IP_CH(ip, i)	(((u_char *)(ip))[i])
409#define IP_LIST(ip)	IP_CH(ip,0),IP_CH(ip,1),IP_CH(ip,2),IP_CH(ip,3)
410
411typedef enum {
412    command_none_e,
413    command_getoption_e,
414    command_lease_e,
415    command_setparams_e,
416    command_getparams_e,
417} command_t;
418
419int
420main(int argc, char * argv[])
421{
422    CFStringRef		app_id;
423    command_t		command = command_none_e;
424    char *		command_str;
425    CFIndex		count;
426    CFDictionaryRef	info;
427    UInt8 * 		params;
428    CFStringRef		serviceID = NULL;
429
430    command_str = argv[1];
431    if (argc < 2)
432	usage(argv[0]);
433    if (strcmp(command_str, CMDSTR_GETOPTION) == 0) {
434	if (argc < 4 || argc > 5) {
435	    usage(argv[0]);
436	}
437	command = command_getoption_e;
438    }
439    else if (strcmp(command_str, CMDSTR_LEASE) == 0) {
440	if (argc != 3) {
441	    usage(argv[0]);
442	}
443	command = command_lease_e;
444    }
445    else if (strcmp(command_str, CMDSTR_SETPARAMS) == 0) {
446	command = command_setparams_e;
447	if (argc < 3) {
448	    usage(argv[0]);
449	}
450    }
451    else if (strcmp(command_str, CMDSTR_GETPARAMS) == 0) {
452	command = command_getparams_e;
453	if (argc != 3) {
454	    usage(argv[0]);
455	}
456    }
457    else {
458	usage(argv[0]);
459    }
460
461    switch (command) {
462      case command_getoption_e: {
463	  UInt8 	code;
464	  char *	code_str;
465	  CFDataRef	option;
466	  boolean_t	printed = FALSE;
467	  CFIndex	len;
468	  char *	type = NULL;
469
470	  if (argv[2][0]) {
471	      serviceID = CFStringCreateWithFormat(NULL, NULL,
472						   CFSTR("%s"), argv[2]);
473	  }
474
475	  info = SCDynamicStoreCopyDHCPInfo(NULL, serviceID);
476	  if (info == NULL) {
477	      exit(1);
478	  }
479
480	  code_str = argv[3];
481	  if (argc > 4) {
482	      type = argv[4];
483	  }
484	  code = atoi(code_str);
485
486	  option = DHCPInfoGetOptionData(info, code);
487	  if (option == NULL) {
488	      exit(1);
489	  }
490	  len = CFDataGetLength(option);
491	  if (type) {
492	      printed = TRUE;
493	      if (strcmp(type, "ip") == 0) {
494		  int i = 0;
495		  const void * ptr = CFDataGetBytePtr(option);
496
497		  while (len >= 4) {
498		      if (i == 0) {
499			  printf(IP_FORMAT, IP_LIST(ptr));
500		      }
501		      else {
502			  printf(" " IP_FORMAT, IP_LIST(ptr));
503		      }
504		      i++;
505		      len -= 4;
506		      ptr += 4;
507		  }
508		  printf("\n");
509	      }
510	      else if (strcmp(type, "string") == 0) {
511		  printf("%.*s\n", (int)len, (char *)CFDataGetBytePtr(option));
512	      }
513	      else {
514		  printed = FALSE;
515	      }
516	  }
517	  if (printed == FALSE) {
518	      print_data((void *)CFDataGetBytePtr(option), len);
519	  }
520	  if (serviceID)
521	      CFRelease(serviceID);
522	  CFRelease(info);
523	  break;
524      }
525      case command_lease_e: {
526	  CFDateRef	start;
527
528	  if (argv[2][0]) {
529	      serviceID = CFStringCreateWithFormat(NULL, NULL,
530						   CFSTR("%s"), argv[2]);
531	  }
532
533	  info = SCDynamicStoreCopyDHCPInfo(NULL, serviceID);
534	  if (info == NULL) {
535	      exit(1);
536	  }
537	  start = DHCPInfoGetLeaseStartTime(info);
538
539	  if (start) {
540	      CFDataRef		option;
541	      int32_t		lease;
542
543#define OPTION_LEASE_TIME	51
544#define SERVER_ID		54
545	      option = DHCPInfoGetOptionData(info, OPTION_LEASE_TIME);
546	      if (option == NULL) {
547		  fprintf(stderr, "what, no lease time?\n");
548		  exit(1);
549	      }
550	      printf("Lease start: ");
551	      show_date(CFDateGetAbsoluteTime(start));
552	      lease = ntohl(*((int32_t *)CFDataGetBytePtr(option)));
553	      if (lease == 0xffffffff) {
554		  printf("Lease is infinite\n");
555	      }
556	      else {
557		  printf("Lease expires: ");
558		  show_date(lease + CFDateGetAbsoluteTime(start));
559	      }
560	      option = DHCPInfoGetOptionData(info, SERVER_ID);
561	      if (option) {
562		  printf("Server IP: " IP_FORMAT "\n",
563			 IP_LIST(CFDataGetBytePtr(option)));
564	      }
565	  }
566	  else {
567	      printf("no lease\n");
568	  }
569	  if (serviceID)
570	      CFRelease(serviceID);
571	  CFRelease(info);
572	  break;
573      }
574      case command_getparams_e: {
575	  app_id = CFStringCreateWithFormat(NULL, NULL,
576					    CFSTR("%s"), argv[2]);
577	  params = DHCPClientPreferencesCopyApplicationOptions(app_id, &count);
578	  if (params) {
579	      printf("%s params = {", argv[2]);
580	      S_print_char_array(params, count);
581	      printf("}\n");
582	      free(params);
583	  }
584	  break;
585      }
586      case command_setparams_e: {
587	  int		count = 0;
588	  UInt8 * 	options = NULL;
589
590	  if (argc > 3) {
591	      int i;
592
593	      count = argc - 3;
594	      options = malloc(count);
595	      if (options == NULL) {
596		  fprintf(stderr, "malloc failed %s\n",
597			  strerror(errno));
598		  exit(1);
599	      }
600	      for (i = 0; i < count; i++) {
601		  options[i] = atoi(argv[3 + i]);
602	      }
603	  }
604	  app_id = CFStringCreateWithFormat(NULL, NULL,
605					    CFSTR("%s"), argv[2]);
606	  if (DHCPClientPreferencesSetApplicationOptions(app_id, options,
607							 count) == FALSE) {
608	      printf("operation failed\n");
609	  }
610	  if (options) {
611	      free(options);
612	  }
613	  break;
614      }
615      default:
616	  break;
617    }
618    exit(0);
619    return(0);
620}
621#endif	// TEST_DHCPCLIENT_PREFERENCES
622
623