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 = CFPropertyListCreateFromXMLData(NULL, data,
143					    kCFPropertyListImmutable,
144					    NULL);
145 done:
146    if (data)
147	CFRelease(data);
148    if (buf)
149	free(buf);
150    return (plist);
151}
152
153PRIVATE_EXTERN int
154my_CFPropertyListWriteFile(CFPropertyListRef plist, const char * filename,
155			   mode_t permissions)
156{
157    CFDataRef	data;
158    int		ret;
159
160    if (plist == NULL)
161	return (0);
162
163    data = CFPropertyListCreateXMLData(NULL, plist);
164    if (data == NULL) {
165	return (0);
166    }
167    ret = write_file(filename,
168		     (const void *)CFDataGetBytePtr(data),
169		     CFDataGetLength(data),
170		     permissions);
171    CFRelease(data);
172    return (ret);
173}
174
175PRIVATE_EXTERN int
176my_CFStringToCStringAndLengthExt(CFStringRef cfstr, char * str, int len,
177				 boolean_t is_external)
178{
179    CFIndex		ret_len = 0;
180
181    CFStringGetBytes(cfstr, CFRangeMake(0, CFStringGetLength(cfstr)),
182		     kCFStringEncodingUTF8, 0, is_external,
183		     (UInt8 *)str, len - 1, &ret_len);
184    if (str != NULL) {
185	str[ret_len] = '\0';
186    }
187    return (ret_len + 1); /* leave 1 byte for nul-termination */
188}
189
190PRIVATE_EXTERN Boolean
191my_CFStringArrayToCStringArray(CFArrayRef arr, void * buffer, int * buffer_size,
192			       int * ret_count)
193{
194    int		count = CFArrayGetCount(arr);
195    int 	i;
196    char *	offset = NULL;
197    int		space;
198    char * *	strlist = NULL;
199
200    space = count * sizeof(char *);
201    if (buffer != NULL) {
202	if (*buffer_size < space) {
203	    /* not enough space for even the pointer list */
204	    return (FALSE);
205	}
206	strlist = (char * *)buffer;
207	offset = buffer + space; /* the start of the 1st string */
208    }
209    for (i = 0; i < count; i++) {
210	CFIndex		len = 0;
211	CFStringRef	str;
212
213	str = CFArrayGetValueAtIndex(arr, i);
214	if (isA_CFString(str) == NULL) {
215	    return (FALSE);
216	}
217	if (buffer != NULL) {
218	    len = *buffer_size - space;
219	    if (len < 0) {
220		return (FALSE);
221	    }
222	}
223	len = my_CFStringToCStringAndLength(str, offset, len);
224	if (buffer != NULL) {
225	    strlist[i] = offset;
226	    offset += len;
227	}
228	space += len;
229    }
230    *buffer_size = roundup(space, sizeof(char *));
231    *ret_count = count;
232    return (TRUE);
233}
234
235PRIVATE_EXTERN Boolean
236my_CFStringArrayToEtherArray(CFArrayRef array, char * buffer, int * buffer_size,
237			     int * ret_count)
238{
239    int			count = CFArrayGetCount(array);
240    int 		i;
241    struct ether_addr * list = NULL;
242    int			space;
243
244    space = roundup(count * sizeof(*list), sizeof(char *));
245    if (buffer != NULL) {
246	if (*buffer_size < space) {
247	    /* not enough space for all elements */
248	    return (FALSE);
249	}
250	list = (struct ether_addr *)buffer;
251    }
252    for (i = 0; i < count; i++) {
253	struct ether_addr * 	eaddr;
254	CFStringRef		str = CFArrayGetValueAtIndex(array, i);
255	char			val[64];
256
257	if (isA_CFString(str) == NULL) {
258	    return (FALSE);
259	}
260	if (CFStringGetCString(str, val, sizeof(val), kCFStringEncodingASCII)
261	    == FALSE) {
262	    return (FALSE);
263	}
264	eaddr = ether_aton((char *)val);
265	if (eaddr == NULL) {
266	    return (FALSE);
267	}
268	if (list != NULL) {
269	    list[i] = *eaddr;
270	}
271    }
272    *buffer_size = space;
273    *ret_count = count;
274    return (TRUE);
275}
276
277PRIVATE_EXTERN bool
278my_CFStringToIPAddress(CFStringRef str, struct in_addr * ret_ip)
279{
280    char		buf[64];
281
282    ret_ip->s_addr = 0;
283    if (isA_CFString(str) == NULL) {
284	return (FALSE);
285    }
286    if (CFStringGetCString(str, buf, sizeof(buf), kCFStringEncodingASCII)
287	== FALSE) {
288	return (FALSE);
289    }
290    if (inet_aton(buf, ret_ip) == 1) {
291	return (TRUE);
292    }
293    return (FALSE);
294}
295
296PRIVATE_EXTERN bool
297my_CFStringToNumber(CFStringRef str, uint32_t * ret_val)
298{
299    char		buf[64];
300    unsigned long	val;
301
302    my_CFStringToCStringAndLength(str, buf, sizeof(buf));
303    val = strtoul(buf, NULL, 0);
304    if (val != ULONG_MAX && errno != ERANGE) {
305	*ret_val = (uint32_t)val;
306	return (TRUE);
307    }
308    return (FALSE);
309}
310
311PRIVATE_EXTERN bool
312my_CFTypeToNumber(CFTypeRef element, uint32_t * l_p)
313{
314    if (isA_CFString(element) != NULL) {
315	if (my_CFStringToNumber(element, l_p) == FALSE) {
316	    return (FALSE);
317	}
318    }
319    else if (isA_CFBoolean(element) != NULL) {
320	*l_p = CFBooleanGetValue(element);
321    }
322    else if (isA_CFNumber(element) != NULL) {
323	if (CFNumberGetValue(element, kCFNumberSInt32Type, l_p)
324	    == FALSE) {
325	    return (FALSE);
326	}
327    }
328    else {
329	return (FALSE);
330    }
331    return (TRUE);
332}
333
334PRIVATE_EXTERN void
335my_CFDictionarySetTypeAsArrayValue(CFMutableDictionaryRef dict,
336				   CFStringRef prop, CFTypeRef val)
337{
338    CFArrayRef	array;
339
340    array = CFArrayCreate(NULL, (const void **)&val, 1,
341			  &kCFTypeArrayCallBacks);
342    if (array != NULL) {
343	CFDictionarySetValue(dict, prop, array);
344	CFRelease(array);
345    }
346    return;
347}
348
349PRIVATE_EXTERN void
350my_CFDictionarySetIPAddressAsArrayValue(CFMutableDictionaryRef dict,
351					CFStringRef prop,
352					struct in_addr ip_addr)
353{
354    CFStringRef		str;
355
356    str = CFStringCreateWithFormat(NULL, NULL, CFSTR(IP_FORMAT),
357				   IP_LIST(&ip_addr));
358    my_CFDictionarySetTypeAsArrayValue(dict, prop, str);
359    CFRelease(str);
360    return;
361}
362
363PRIVATE_EXTERN void
364my_CFArrayAppendUniqueValue(CFMutableArrayRef arr, CFTypeRef new)
365{
366    int count;
367    int i;
368
369    count = CFArrayGetCount(arr);
370    for (i = 0; i < count; i++) {
371	CFStringRef element = CFArrayGetValueAtIndex(arr, i);
372	if (CFEqual(element, new)) {
373	    return;
374	}
375    }
376    CFArrayAppendValue(arr, new);
377    return;
378}
379
380PRIVATE_EXTERN Boolean
381my_CFEqual(CFTypeRef val1, CFTypeRef val2)
382{
383    if (val1 == NULL) {
384	if (val2 == NULL) {
385	    return (TRUE);
386	}
387	return (FALSE);
388    }
389    if (val2 == NULL) {
390	return (FALSE);
391    }
392    if (CFGetTypeID(val1) != CFGetTypeID(val2)) {
393	return (FALSE);
394    }
395    return (CFEqual(val1, val2));
396}
397
398
399/*
400 * Function: my_CFStringCopyComponent
401 * Purpose:
402 *    Separates the given string using the given separator, and returns
403 *    the component at the specified index.
404 * Returns:
405 *    NULL if no such component exists, non-NULL component otherwise
406 */
407PRIVATE_EXTERN CFStringRef
408my_CFStringCopyComponent(CFStringRef path, CFStringRef separator,
409			 CFIndex component_index)
410{
411    CFArrayRef		arr;
412    CFStringRef		component = NULL;
413
414    arr = CFStringCreateArrayBySeparatingStrings(NULL, path, separator);
415    if (arr == NULL) {
416	goto done;
417    }
418    if (CFArrayGetCount(arr) <= component_index) {
419	goto done;
420    }
421    component = CFRetain(CFArrayGetValueAtIndex(arr, component_index));
422
423 done:
424    my_CFRelease(&arr);
425    return (component);
426
427}
428
429CFStringRef
430my_CFStringCreateWithIPAddress(const struct in_addr ip)
431{
432    return (CFStringCreateWithFormat(NULL, NULL,
433				     CFSTR(IP_FORMAT), IP_LIST(&ip)));
434}
435
436CFStringRef
437my_CFStringCreateWithIPv6Address(const void * ip6_addr)
438{
439    char 		ntopbuf[INET6_ADDRSTRLEN];
440    const char *	c_str;
441
442    c_str = inet_ntop(AF_INET6, ip6_addr, ntopbuf, sizeof(ntopbuf));
443    return (CFStringCreateWithCString(NULL, c_str, kCFStringEncodingASCII));
444}
445
446void
447my_CFStringAppendBytesAsHex(CFMutableStringRef str, const uint8_t * bytes,
448			    int length, char separator)
449{
450    int i;
451
452    for (i = 0; i < length; i++) {
453	char  	sep[3];
454
455	if (i == 0) {
456	    sep[0] = '\0';
457	}
458	else {
459	    if ((i % 8) == 0 && separator == ' ') {
460		sep[0] = sep[1] = ' ';
461		sep[2] = '\0';
462	    }
463	    else {
464		sep[0] = separator;
465		sep[1] = '\0';
466	    }
467	}
468	CFStringAppendFormat(str, NULL, CFSTR("%s%02x"), sep, bytes[i]);
469    }
470    return;
471}
472
473char *
474my_CFStringToCString(CFStringRef cfstr, CFStringEncoding encoding)
475{
476    CFIndex		l;
477    CFRange		range;
478    uint8_t *		str;
479
480    range = CFRangeMake(0, CFStringGetLength(cfstr));
481    CFStringGetBytes(cfstr, range, encoding,
482		     0, FALSE, NULL, 0, &l);
483    if (l <= 0) {
484	return (NULL);
485    }
486    str = (uint8_t *)malloc(l + 1);
487    CFStringGetBytes(cfstr, range, encoding, 0, FALSE, str, l, &l);
488    str[l] = '\0';
489    return ((char *)str);
490}
491
492