1/*
2 * Copyright (c) 1999-2002, 2011 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 * cfutil.c
26 * - CF utility functions
27 */
28
29/*
30 * Modification History
31 *
32 * February 15, 2002 	Dieter Siegmund (dieter@apple.com)
33 * - broken out of ipconfigd.c
34 */
35
36#include <stdlib.h>
37#include <unistd.h>
38#include <stdio.h>
39#include <sys/stat.h>
40#include <fcntl.h>
41#include <ctype.h>
42#include <string.h>
43#include <sys/param.h>
44#include <arpa/inet.h>
45#include <net/ethernet.h>
46#include <SystemConfiguration/SCValidation.h>
47#include <CoreFoundation/CFData.h>
48#include "util.h"
49#include "cfutil.h"
50
51#include "symbol_scope.h"
52
53PRIVATE_EXTERN void
54my_CFRelease(void * t)
55{
56    void * * obj = (void * *)t;
57    if (obj && *obj) {
58	CFRelease(*obj);
59	*obj = NULL;
60    }
61    return;
62}
63
64static void *
65read_file(const char * filename, size_t * data_length)
66{
67    void *		data = NULL;
68    size_t		len = 0;
69    int			fd = -1;
70    struct stat		sb;
71
72    *data_length = 0;
73    if (stat(filename, &sb) < 0)
74	goto done;
75    len = sb.st_size;
76    if (len == 0)
77	goto done;
78
79    data = malloc(len);
80    if (data == NULL)
81	goto done;
82
83    fd = open(filename, O_RDONLY);
84    if (fd < 0)
85	goto done;
86
87    if (read(fd, data, len) != len) {
88	goto done;
89    }
90 done:
91    if (fd >= 0)
92	close(fd);
93    if (data) {
94	*data_length = len;
95    }
96    return (data);
97}
98
99static int
100write_file(const char * filename, const void * data, size_t data_length,
101	   mode_t permissions)
102{
103    char		path[MAXPATHLEN];
104    int			fd = -1;
105    int			ret = 0;
106
107    snprintf(path, sizeof(path), "%s-", filename);
108    fd = open(path, O_WRONLY | O_TRUNC | O_CREAT, permissions);
109    if (fd < 0) {
110	ret = -1;
111	goto done;
112    }
113
114    if (write(fd, data, data_length) != data_length) {
115	ret = -1;
116	goto done;
117    }
118    rename(path, filename);
119 done:
120    if (fd >= 0) {
121	close(fd);
122    }
123    return (ret);
124}
125
126PRIVATE_EXTERN CFPropertyListRef
127my_CFPropertyListCreateFromFile(const char * filename)
128{
129    void *		buf;
130    size_t		bufsize;
131    CFDataRef		data = NULL;
132    CFPropertyListRef	plist = NULL;
133
134    buf = read_file(filename, &bufsize);
135    if (buf == NULL) {
136	return (NULL);
137    }
138    data = CFDataCreateWithBytesNoCopy(NULL, buf, bufsize, kCFAllocatorNull);
139    if (data == NULL) {
140	goto done;
141    }
142    plist = CFPropertyListCreateWithData(NULL,
143					 data,
144					 kCFPropertyListImmutable,
145					 NULL,
146					 NULL);
147 done:
148    if (data)
149	CFRelease(data);
150    if (buf)
151	free(buf);
152    return (plist);
153}
154
155PRIVATE_EXTERN int
156my_CFPropertyListWriteFile(CFPropertyListRef plist, const char * filename,
157			   mode_t permissions)
158{
159    CFDataRef	data;
160    int		ret;
161
162    if (plist == NULL)
163	return (0);
164
165    data = CFPropertyListCreateData(NULL,
166				    plist,
167				    kCFPropertyListXMLFormat_v1_0,
168				    0,
169				    NULL);
170    if (data == NULL) {
171	return (0);
172    }
173    ret = write_file(filename,
174		     (const void *)CFDataGetBytePtr(data),
175		     CFDataGetLength(data),
176		     permissions);
177    CFRelease(data);
178    return (ret);
179}
180
181PRIVATE_EXTERN int
182my_CFStringToCStringAndLengthExt(CFStringRef cfstr, char * str, int len,
183				 boolean_t is_external)
184{
185    CFIndex		ret_len = 0;
186
187    CFStringGetBytes(cfstr, CFRangeMake(0, CFStringGetLength(cfstr)),
188		     kCFStringEncodingUTF8, 0, is_external,
189		     (UInt8 *)str, len - 1, &ret_len);
190    if (str != NULL) {
191	str[ret_len] = '\0';
192    }
193    return ((int)ret_len + 1); /* leave 1 byte for nul-termination */
194}
195
196PRIVATE_EXTERN Boolean
197my_CFStringArrayToCStringArray(CFArrayRef arr, void * buffer, int * buffer_size,
198			       int * ret_count)
199{
200    CFIndex	count = CFArrayGetCount(arr);
201    int 	i;
202    char *	offset = NULL;
203    int		space;
204    char * *	strlist = NULL;
205
206    space = (int)count * sizeof(char *);
207    if (buffer != NULL) {
208	if (*buffer_size < space) {
209	    /* not enough space for even the pointer list */
210	    return (FALSE);
211	}
212	strlist = (char * *)buffer;
213	offset = buffer + space; /* the start of the 1st string */
214    }
215    for (i = 0; i < count; i++) {
216	int		len = 0;
217	CFStringRef	str;
218
219	str = CFArrayGetValueAtIndex(arr, i);
220	if (isA_CFString(str) == NULL) {
221	    return (FALSE);
222	}
223	if (buffer != NULL) {
224	    len = *buffer_size - space;
225	    if (len < 0) {
226		return (FALSE);
227	    }
228	}
229	len = my_CFStringToCStringAndLength(str, offset, len);
230	if (buffer != NULL) {
231	    strlist[i] = offset;
232	    offset += len;
233	}
234	space += len;
235    }
236    *buffer_size = roundup(space, sizeof(char *));
237    *ret_count = (int)count;
238    return (TRUE);
239}
240
241PRIVATE_EXTERN Boolean
242my_CFStringArrayToEtherArray(CFArrayRef array, char * buffer, int * buffer_size,
243			     int * ret_count)
244{
245    CFIndex		count = CFArrayGetCount(array);
246    int 		i;
247    struct ether_addr * list = NULL;
248    int			space;
249
250    space = roundup((int)count * sizeof(*list), sizeof(char *));
251    if (buffer != NULL) {
252	if (*buffer_size < space) {
253	    /* not enough space for all elements */
254	    return (FALSE);
255	}
256	list = (struct ether_addr *)buffer;
257    }
258    for (i = 0; i < count; i++) {
259	struct ether_addr * 	eaddr;
260	CFStringRef		str = CFArrayGetValueAtIndex(array, i);
261	char			val[64];
262
263	if (isA_CFString(str) == NULL) {
264	    return (FALSE);
265	}
266	if (CFStringGetCString(str, val, sizeof(val), kCFStringEncodingASCII)
267	    == FALSE) {
268	    return (FALSE);
269	}
270	eaddr = ether_aton((char *)val);
271	if (eaddr == NULL) {
272	    return (FALSE);
273	}
274	if (list != NULL) {
275	    list[i] = *eaddr;
276	}
277    }
278    *buffer_size = space;
279    *ret_count = (int)count;
280    return (TRUE);
281}
282
283PRIVATE_EXTERN bool
284my_CFStringToIPAddress(CFStringRef str, struct in_addr * ret_ip)
285{
286    char		buf[64];
287
288    ret_ip->s_addr = 0;
289    if (isA_CFString(str) == NULL) {
290	return (FALSE);
291    }
292    if (CFStringGetCString(str, buf, sizeof(buf), kCFStringEncodingASCII)
293	== FALSE) {
294	return (FALSE);
295    }
296    if (inet_aton(buf, ret_ip) == 1) {
297	return (TRUE);
298    }
299    return (FALSE);
300}
301
302PRIVATE_EXTERN bool
303my_CFStringToNumber(CFStringRef str, uint32_t * ret_val)
304{
305    char		buf[64];
306    unsigned long	val;
307
308    my_CFStringToCStringAndLength(str, buf, sizeof(buf));
309    val = strtoul(buf, NULL, 0);
310    if (val != ULONG_MAX && errno != ERANGE) {
311	*ret_val = (uint32_t)val;
312	return (TRUE);
313    }
314    return (FALSE);
315}
316
317PRIVATE_EXTERN bool
318my_CFTypeToNumber(CFTypeRef element, uint32_t * l_p)
319{
320    if (isA_CFString(element) != NULL) {
321	if (my_CFStringToNumber(element, l_p) == FALSE) {
322	    return (FALSE);
323	}
324    }
325    else if (isA_CFBoolean(element) != NULL) {
326	*l_p = CFBooleanGetValue(element);
327    }
328    else if (isA_CFNumber(element) != NULL) {
329	if (CFNumberGetValue(element, kCFNumberSInt32Type, l_p)
330	    == FALSE) {
331	    return (FALSE);
332	}
333    }
334    else {
335	return (FALSE);
336    }
337    return (TRUE);
338}
339
340PRIVATE_EXTERN void
341my_CFDictionarySetTypeAsArrayValue(CFMutableDictionaryRef dict,
342				   CFStringRef prop, CFTypeRef val)
343{
344    CFArrayRef	array;
345
346    array = CFArrayCreate(NULL, (const void **)&val, 1,
347			  &kCFTypeArrayCallBacks);
348    if (array != NULL) {
349	CFDictionarySetValue(dict, prop, array);
350	CFRelease(array);
351    }
352    return;
353}
354
355PRIVATE_EXTERN void
356my_CFDictionarySetIPAddressAsArrayValue(CFMutableDictionaryRef dict,
357					CFStringRef prop,
358					struct in_addr ip_addr)
359{
360    CFStringRef		str;
361
362    str = CFStringCreateWithFormat(NULL, NULL, CFSTR(IP_FORMAT),
363				   IP_LIST(&ip_addr));
364    my_CFDictionarySetTypeAsArrayValue(dict, prop, str);
365    CFRelease(str);
366    return;
367}
368
369PRIVATE_EXTERN void
370my_CFArrayAppendUniqueValue(CFMutableArrayRef arr, CFTypeRef new)
371{
372    CFIndex count;
373    int i;
374
375    count = CFArrayGetCount(arr);
376    for (i = 0; i < count; i++) {
377	CFStringRef element = CFArrayGetValueAtIndex(arr, i);
378	if (CFEqual(element, new)) {
379	    return;
380	}
381    }
382    CFArrayAppendValue(arr, new);
383    return;
384}
385
386PRIVATE_EXTERN Boolean
387my_CFEqual(CFTypeRef val1, CFTypeRef val2)
388{
389    if (val1 == NULL) {
390	if (val2 == NULL) {
391	    return (TRUE);
392	}
393	return (FALSE);
394    }
395    if (val2 == NULL) {
396	return (FALSE);
397    }
398    if (CFGetTypeID(val1) != CFGetTypeID(val2)) {
399	return (FALSE);
400    }
401    return (CFEqual(val1, val2));
402}
403
404
405/*
406 * Function: my_CFStringCopyComponent
407 * Purpose:
408 *    Separates the given string using the given separator, and returns
409 *    the component at the specified index.
410 * Returns:
411 *    NULL if no such component exists, non-NULL component otherwise
412 */
413PRIVATE_EXTERN CFStringRef
414my_CFStringCopyComponent(CFStringRef path, CFStringRef separator,
415			 CFIndex component_index)
416{
417    CFArrayRef		arr;
418    CFStringRef		component = NULL;
419
420    arr = CFStringCreateArrayBySeparatingStrings(NULL, path, separator);
421    if (arr == NULL) {
422	goto done;
423    }
424    if (CFArrayGetCount(arr) <= component_index) {
425	goto done;
426    }
427    component = CFRetain(CFArrayGetValueAtIndex(arr, component_index));
428
429 done:
430    my_CFRelease(&arr);
431    return (component);
432
433}
434
435CFStringRef
436my_CFStringCreateWithIPAddress(const struct in_addr ip)
437{
438    return (CFStringCreateWithFormat(NULL, NULL,
439				     CFSTR(IP_FORMAT), IP_LIST(&ip)));
440}
441
442CFStringRef
443my_CFStringCreateWithIPv6Address(const void * ip6_addr)
444{
445    char 		ntopbuf[INET6_ADDRSTRLEN];
446    const char *	c_str;
447
448    c_str = inet_ntop(AF_INET6, ip6_addr, ntopbuf, sizeof(ntopbuf));
449    return (CFStringCreateWithCString(NULL, c_str, kCFStringEncodingASCII));
450}
451
452void
453my_CFStringAppendBytesAsHex(CFMutableStringRef str, const uint8_t * bytes,
454			    int length, char separator)
455{
456    int i;
457
458    for (i = 0; i < length; i++) {
459	char  	sep[3];
460
461	if (i == 0) {
462	    sep[0] = '\0';
463	}
464	else {
465	    if ((i % 8) == 0 && separator == ' ') {
466		sep[0] = sep[1] = ' ';
467		sep[2] = '\0';
468	    }
469	    else {
470		sep[0] = separator;
471		sep[1] = '\0';
472	    }
473	}
474	CFStringAppendFormat(str, NULL, CFSTR("%s%02x"), sep, bytes[i]);
475    }
476    return;
477}
478
479char *
480my_CFStringToCString(CFStringRef cfstr, CFStringEncoding encoding)
481{
482    CFIndex		l;
483    CFRange		range;
484    uint8_t *		str;
485
486    range = CFRangeMake(0, CFStringGetLength(cfstr));
487    CFStringGetBytes(cfstr, range, encoding,
488		     0, FALSE, NULL, 0, &l);
489    if (l <= 0) {
490	return (NULL);
491    }
492    str = (uint8_t *)malloc(l + 1);
493    CFStringGetBytes(cfstr, range, encoding, 0, FALSE, str, l, &l);
494    str[l] = '\0';
495    return ((char *)str);
496}
497
498