]>
Commit | Line | Data |
---|---|---|
dbf6a266 | 1 | /* |
ba83da55 | 2 | * Copyright (c) 2004 Apple Computer, Inc. All rights reserved. |
dbf6a266 A |
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 | * Modification History | |
26 | * | |
27 | * May 13, 2004 Allan Nathanson <ajn@apple.com> | |
28 | * - initial revision | |
29 | */ | |
30 | ||
31 | ||
32 | #include <CoreFoundation/CoreFoundation.h> | |
33 | #include <CoreFoundation/CFRuntime.h> | |
34 | #include <SystemConfiguration/SystemConfiguration.h> | |
35 | #include <SystemConfiguration/SCNetworkConfigurationInternal.h> | |
36 | #include <SystemConfiguration/SCValidation.h> | |
37 | #include <SystemConfiguration/SCPrivate.h> | |
38 | ||
39 | #include <pthread.h> | |
40 | ||
41 | ||
42 | static CFStringRef __SCNetworkServiceCopyDescription (CFTypeRef cf); | |
43 | static void __SCNetworkServiceDeallocate (CFTypeRef cf); | |
44 | static Boolean __SCNetworkServiceEqual (CFTypeRef cf1, CFTypeRef cf2); | |
45 | ||
46 | ||
47 | static CFTypeID __kSCNetworkServiceTypeID = _kCFRuntimeNotATypeID; | |
48 | ||
49 | ||
50 | static const CFRuntimeClass __SCNetworkServiceClass = { | |
51 | 0, // version | |
52 | "SCNetworkService", // className | |
53 | NULL, // init | |
54 | NULL, // copy | |
55 | __SCNetworkServiceDeallocate, // dealloc | |
56 | __SCNetworkServiceEqual, // equal | |
57 | NULL, // hash | |
58 | NULL, // copyFormattingDesc | |
59 | __SCNetworkServiceCopyDescription // copyDebugDesc | |
60 | }; | |
61 | ||
62 | ||
63 | static pthread_once_t initialized = PTHREAD_ONCE_INIT; | |
64 | ||
65 | ||
66 | static __inline__ CFTypeRef | |
67 | isA_SCNetworkService(CFTypeRef obj) | |
68 | { | |
69 | return (isA_CFType(obj, SCNetworkServiceGetTypeID())); | |
70 | } | |
71 | ||
72 | ||
73 | static CFStringRef | |
74 | __SCNetworkServiceCopyDescription(CFTypeRef cf) | |
75 | { | |
76 | CFAllocatorRef allocator = CFGetAllocator(cf); | |
77 | CFMutableStringRef result; | |
78 | SCNetworkServicePrivateRef servicePrivate = (SCNetworkServicePrivateRef)cf; | |
79 | ||
80 | result = CFStringCreateMutable(allocator, 0); | |
81 | CFStringAppendFormat(result, NULL, CFSTR("<SCNetworkService %p [%p]> { "), cf, allocator); | |
82 | CFStringAppendFormat(result, NULL, CFSTR("id=%@"), servicePrivate->serviceID); | |
83 | // CFStringAppendFormat(result, NULL, CFSTR(", prefs=%@"), servicePrivate->prefs); | |
84 | CFStringAppendFormat(result, NULL, CFSTR(" }")); | |
85 | ||
86 | return result; | |
87 | } | |
88 | ||
89 | ||
90 | static void | |
91 | __SCNetworkServiceDeallocate(CFTypeRef cf) | |
92 | { | |
93 | SCNetworkServicePrivateRef servicePrivate = (SCNetworkServicePrivateRef)cf; | |
94 | ||
95 | /* release resources */ | |
96 | ||
97 | CFRelease(servicePrivate->serviceID); | |
98 | if (servicePrivate->interface != NULL) CFRelease(servicePrivate->interface); | |
99 | CFRelease(servicePrivate->prefs); | |
100 | ||
101 | return; | |
102 | } | |
103 | ||
104 | ||
105 | static Boolean | |
106 | __SCNetworkServiceEqual(CFTypeRef cf1, CFTypeRef cf2) | |
107 | { | |
108 | SCNetworkServicePrivateRef s1 = (SCNetworkServicePrivateRef)cf1; | |
109 | SCNetworkServicePrivateRef s2 = (SCNetworkServicePrivateRef)cf2; | |
110 | ||
111 | if (s1 == s2) | |
112 | return TRUE; | |
113 | ||
114 | if (s1->prefs != s2->prefs) | |
115 | return FALSE; // if not the same prefs | |
116 | ||
117 | if (!CFEqual(s1->serviceID, s2->serviceID)) | |
118 | return FALSE; // if not the same service identifier | |
119 | ||
120 | return TRUE; | |
121 | } | |
122 | ||
123 | ||
124 | static void | |
125 | __SCNetworkServiceInitialize(void) | |
126 | { | |
127 | __kSCNetworkServiceTypeID = _CFRuntimeRegisterClass(&__SCNetworkServiceClass); | |
128 | return; | |
129 | } | |
130 | ||
131 | ||
132 | __private_extern__ SCNetworkServicePrivateRef | |
133 | __SCNetworkServiceCreatePrivate(CFAllocatorRef allocator, | |
134 | CFStringRef serviceID, | |
135 | SCNetworkInterfaceRef interface, | |
136 | SCPreferencesRef prefs) | |
137 | { | |
138 | SCNetworkServicePrivateRef servicePrivate; | |
139 | uint32_t size; | |
140 | ||
141 | /* initialize runtime */ | |
142 | pthread_once(&initialized, __SCNetworkServiceInitialize); | |
143 | ||
144 | /* allocate target */ | |
145 | size = sizeof(SCNetworkServicePrivate) - sizeof(CFRuntimeBase); | |
146 | servicePrivate = (SCNetworkServicePrivateRef)_CFRuntimeCreateInstance(allocator, | |
147 | __kSCNetworkServiceTypeID, | |
148 | size, | |
149 | NULL); | |
150 | if (servicePrivate == NULL) { | |
151 | return NULL; | |
152 | } | |
153 | ||
154 | servicePrivate->prefs = CFRetain(prefs); | |
155 | servicePrivate->serviceID = CFStringCreateCopy(NULL, serviceID); | |
156 | servicePrivate->interface = (interface != NULL) ? CFRetain(interface) : NULL; | |
157 | ||
158 | return servicePrivate; | |
159 | } | |
160 | ||
161 | ||
162 | /* ---------- SCNetworkService APIs ---------- */ | |
163 | ||
164 | ||
165 | #define N_QUICK 64 | |
166 | ||
167 | ||
168 | Boolean | |
169 | SCNetworkServiceAddProtocolType(SCNetworkServiceRef service, CFStringRef protocolType) | |
170 | { | |
171 | CFDictionaryRef entity; | |
172 | CFDictionaryRef newEntity = NULL; | |
173 | Boolean ok = FALSE; | |
174 | CFStringRef path; | |
175 | SCNetworkServicePrivateRef servicePrivate = (SCNetworkServicePrivateRef)service; | |
176 | ||
177 | if (!__SCNetworkProtocolIsValidType(protocolType)) { | |
178 | _SCErrorSet(kSCStatusInvalidArgument); | |
179 | return FALSE; | |
180 | } | |
181 | ||
182 | path = SCPreferencesPathKeyCreateNetworkServiceEntity(NULL, // allocator | |
183 | servicePrivate->serviceID, // service | |
184 | protocolType); // entity | |
185 | ||
186 | entity = SCPreferencesPathGetValue(servicePrivate->prefs, path); | |
187 | if (entity != NULL) { | |
188 | // if "protocol" already exists | |
189 | _SCErrorSet(kSCStatusKeyExists); | |
190 | goto done; | |
191 | } | |
192 | ||
193 | if (servicePrivate->interface != NULL) { | |
194 | SCNetworkInterfaceRef childInterface; | |
195 | CFStringRef childInterfaceType = NULL; | |
196 | CFStringRef interfaceType; | |
197 | ||
198 | interfaceType = SCNetworkInterfaceGetInterfaceType(servicePrivate->interface); | |
199 | childInterface = SCNetworkInterfaceGetInterface(servicePrivate->interface); | |
200 | if (childInterface != NULL) { | |
201 | childInterfaceType = SCNetworkInterfaceGetInterfaceType(childInterface); | |
202 | } | |
203 | ||
204 | newEntity = __copyProtocolTemplate(interfaceType, childInterfaceType, protocolType); | |
205 | } | |
206 | ||
207 | if (newEntity == NULL) { | |
208 | newEntity = CFDictionaryCreate(NULL, | |
209 | NULL, | |
210 | NULL, | |
211 | 0, | |
212 | &kCFTypeDictionaryKeyCallBacks, | |
213 | &kCFTypeDictionaryValueCallBacks); | |
214 | } | |
215 | ||
216 | ok = SCPreferencesPathSetValue(servicePrivate->prefs, path, newEntity); | |
217 | CFRelease(newEntity); | |
218 | ||
219 | done : | |
220 | ||
221 | CFRelease(path); | |
222 | return ok; | |
223 | } | |
224 | ||
225 | ||
226 | CFArrayRef /* of SCNetworkServiceRef's */ | |
227 | SCNetworkServiceCopyAll(SCPreferencesRef prefs) | |
228 | { | |
229 | CFMutableArrayRef array; | |
230 | CFIndex n; | |
231 | CFStringRef path; | |
232 | CFDictionaryRef services; | |
233 | ||
234 | path = SCPreferencesPathKeyCreateNetworkServices(NULL); | |
235 | services = SCPreferencesPathGetValue(prefs, path); | |
236 | CFRelease(path); | |
237 | ||
238 | if ((services != NULL) && !isA_CFDictionary(services)) { | |
239 | return NULL; | |
240 | } | |
241 | ||
242 | array = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); | |
243 | ||
244 | n = (services != NULL) ? CFDictionaryGetCount(services) : 0; | |
245 | if (n > 0) { | |
246 | CFIndex i; | |
247 | const void * keys_q[N_QUICK]; | |
248 | const void ** keys = keys_q; | |
249 | const void * vals_q[N_QUICK]; | |
250 | const void ** vals = vals_q; | |
251 | ||
252 | if (n > (CFIndex)(sizeof(keys_q) / sizeof(CFTypeRef))) { | |
253 | keys = CFAllocatorAllocate(NULL, n * sizeof(CFTypeRef), 0); | |
254 | vals = CFAllocatorAllocate(NULL, n * sizeof(CFPropertyListRef), 0); | |
255 | } | |
256 | CFDictionaryGetKeysAndValues(services, keys, vals); | |
257 | for (i = 0; i < n; i++) { | |
258 | CFDictionaryRef entity; | |
259 | SCNetworkServicePrivateRef servicePrivate; | |
260 | ||
261 | if (!isA_CFDictionary(vals[i])) { | |
262 | SCLog(TRUE, | |
263 | LOG_INFO, | |
264 | CFSTR("SCNetworkServiceCopyAll(): error w/service \"%@\"\n"), | |
265 | keys[i]); | |
266 | continue; | |
267 | } | |
268 | ||
269 | entity = CFDictionaryGetValue(vals[i], kSCEntNetInterface); | |
270 | if (!isA_CFDictionary(entity)) { | |
271 | // if no "interface" | |
272 | SCLog(TRUE, | |
273 | LOG_INFO, | |
274 | CFSTR("SCNetworkServiceCopyAll(): no \"%@\" entity for service \"%@\"\n"), | |
275 | kSCEntNetInterface, | |
276 | keys[i]); | |
277 | continue; | |
278 | } | |
279 | ||
280 | servicePrivate = __SCNetworkServiceCreatePrivate(NULL, keys[i], NULL, prefs); | |
281 | CFArrayAppendValue(array, (SCNetworkServiceRef)servicePrivate); | |
282 | CFRelease(servicePrivate); | |
283 | } | |
284 | if (keys != keys_q) { | |
285 | CFAllocatorDeallocate(NULL, keys); | |
286 | CFAllocatorDeallocate(NULL, vals); | |
287 | } | |
288 | } | |
289 | ||
290 | return array; | |
291 | } | |
292 | ||
293 | ||
294 | /* | |
295 | * build a list of all of a servives entity types that are associated | |
296 | * with the services interface. The list will include : | |
297 | * | |
298 | * - entity types associated with the interface type (Ethernet, FireWire, PPP, ...) | |
299 | * - entity types associated with the interface sub-type (PPPSerial, PPPoE, L2TP, PPTP, ...) | |
300 | * - entity types associated with the hardware device (Ethernet, AirPort, FireWire, Modem, ...) | |
301 | */ | |
302 | static CFSetRef | |
303 | _copyInterfaceEntityTypes(CFDictionaryRef protocols) | |
304 | { | |
305 | CFDictionaryRef interface; | |
306 | CFMutableSetRef interface_entity_types; | |
307 | ||
308 | interface_entity_types = CFSetCreateMutable(NULL, 0, &kCFTypeSetCallBacks); | |
309 | ||
310 | interface = CFDictionaryGetValue(protocols, kSCEntNetInterface); | |
311 | if (isA_CFDictionary(interface)) { | |
312 | CFStringRef entities[] = { kSCPropNetInterfaceType, | |
313 | kSCPropNetInterfaceSubType, | |
314 | kSCPropNetInterfaceHardware }; | |
315 | int i; | |
316 | ||
317 | // include the "Interface" entity itself | |
318 | CFSetAddValue(interface_entity_types, kSCEntNetInterface); | |
319 | ||
320 | // include the entities associated with the interface | |
321 | for (i = 0; i < sizeof(entities)/sizeof(entities[0]); i++) { | |
322 | CFStringRef entity; | |
323 | ||
324 | entity = CFDictionaryGetValue(interface, entities[i]); | |
325 | if (isA_CFString(entity)) { | |
326 | CFSetAddValue(interface_entity_types, entity); | |
327 | } | |
328 | } | |
329 | ||
330 | /* | |
331 | * and, because we've found some misguided network preference code | |
332 | * developers leaving [PPP] entity dictionaries around even though | |
333 | * they are unused and/or unneeded... | |
334 | */ | |
335 | CFSetAddValue(interface_entity_types, kSCEntNetPPP); | |
336 | } | |
337 | ||
338 | return interface_entity_types; | |
339 | } | |
340 | ||
341 | ||
342 | SCNetworkServiceRef | |
343 | SCNetworkServiceCopy(SCPreferencesRef prefs, CFStringRef serviceID) | |
344 | { | |
345 | CFDictionaryRef entity; | |
346 | CFStringRef path; | |
347 | SCNetworkServicePrivateRef servicePrivate; | |
348 | ||
349 | path = SCPreferencesPathKeyCreateNetworkServiceEntity(NULL, // allocator | |
350 | serviceID, // service | |
351 | kSCEntNetInterface); // entity | |
352 | entity = SCPreferencesPathGetValue(prefs, path); | |
353 | CFRelease(path); | |
354 | ||
355 | if (!isA_CFDictionary(entity)) { | |
356 | // a "service" must have an "interface" | |
357 | _SCErrorSet(kSCStatusNoKey); | |
358 | return NULL; | |
359 | } | |
360 | ||
361 | servicePrivate = __SCNetworkServiceCreatePrivate(NULL, serviceID, NULL, prefs); | |
362 | return (SCNetworkServiceRef)servicePrivate; | |
363 | } | |
364 | ||
365 | ||
366 | SCNetworkProtocolRef | |
367 | SCNetworkServiceCopyProtocol(SCNetworkServiceRef service, CFStringRef protocolType) | |
368 | { | |
369 | CFSetRef non_protocol_entities; | |
370 | CFStringRef path; | |
371 | CFDictionaryRef protocols; | |
372 | SCNetworkProtocolPrivateRef protocolPrivate = NULL; | |
373 | SCNetworkServicePrivateRef servicePrivate = (SCNetworkServicePrivateRef)service; | |
374 | ||
375 | path = SCPreferencesPathKeyCreateNetworkServiceEntity(NULL, // allocator | |
376 | servicePrivate->serviceID, // service | |
377 | NULL); // entity | |
378 | protocols = SCPreferencesPathGetValue(servicePrivate->prefs, path); | |
379 | CFRelease(path); | |
380 | ||
381 | if ((protocols != NULL) && !isA_CFDictionary(protocols)) { | |
382 | // if corrupt prefs | |
383 | _SCErrorSet(kSCStatusFailed); | |
384 | return NULL; | |
385 | } | |
386 | ||
387 | non_protocol_entities = _copyInterfaceEntityTypes(protocols); | |
388 | if (CFSetContainsValue(non_protocol_entities, protocolType)) { | |
389 | // if the "protocolType" matches an interface entity type | |
390 | _SCErrorSet(kSCStatusInvalidArgument); | |
391 | goto done; | |
392 | } | |
393 | ||
394 | if (!CFDictionaryContainsKey(protocols, protocolType)) { | |
395 | // if the "protocolType" entity does not exist | |
396 | _SCErrorSet(kSCStatusNoKey); | |
397 | goto done; | |
398 | } | |
399 | ||
400 | protocolPrivate = __SCNetworkProtocolCreatePrivate(NULL, protocolType, service); | |
401 | ||
402 | done : | |
403 | ||
404 | CFRelease(non_protocol_entities); | |
405 | ||
406 | return (SCNetworkProtocolRef)protocolPrivate; | |
407 | } | |
408 | ||
409 | ||
410 | CFArrayRef /* of SCNetworkProtocolRef's */ | |
411 | SCNetworkServiceCopyProtocols(SCNetworkServiceRef service) | |
412 | { | |
413 | CFMutableArrayRef array; | |
414 | CFIndex n; | |
415 | CFSetRef non_protocol_entities; | |
416 | CFStringRef path; | |
417 | CFDictionaryRef protocols; | |
418 | SCNetworkServicePrivateRef servicePrivate = (SCNetworkServicePrivateRef)service; | |
419 | ||
420 | path = SCPreferencesPathKeyCreateNetworkServiceEntity(NULL, // allocator | |
421 | servicePrivate->serviceID, // service | |
422 | NULL); // entity | |
423 | protocols = SCPreferencesPathGetValue(servicePrivate->prefs, path); | |
424 | CFRelease(path); | |
425 | ||
426 | if (!isA_CFDictionary(protocols)) { | |
427 | return NULL; | |
428 | } | |
429 | ||
430 | non_protocol_entities = _copyInterfaceEntityTypes(protocols); | |
431 | ||
432 | array = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); | |
433 | ||
434 | n = CFDictionaryGetCount(protocols); | |
435 | if (n > 0) { | |
436 | CFIndex i; | |
437 | const void * keys_q[N_QUICK]; | |
438 | const void ** keys = keys_q; | |
439 | const void * vals_q[N_QUICK]; | |
440 | const void ** vals = vals_q; | |
441 | ||
442 | if (n > (CFIndex)(sizeof(keys_q) / sizeof(CFTypeRef))) { | |
443 | keys = CFAllocatorAllocate(NULL, n * sizeof(CFTypeRef), 0); | |
444 | vals = CFAllocatorAllocate(NULL, n * sizeof(CFPropertyListRef), 0); | |
445 | } | |
446 | CFDictionaryGetKeysAndValues(protocols, keys, vals); | |
447 | for (i = 0; i < n; i++) { | |
448 | SCNetworkProtocolPrivateRef protocolPrivate; | |
449 | ||
450 | if (!isA_CFDictionary(vals[i])) { | |
451 | // if it's not a dictionary then it can't be a protocol entity | |
452 | continue; | |
453 | } | |
454 | ||
455 | if (CFSetContainsValue(non_protocol_entities, keys[i])) { | |
456 | // skip any non-protocol (interface) entities | |
457 | continue; | |
458 | } | |
459 | ||
460 | protocolPrivate = __SCNetworkProtocolCreatePrivate(NULL, keys[i], service); | |
461 | CFArrayAppendValue(array, (SCNetworkProtocolRef)protocolPrivate); | |
462 | ||
463 | CFRelease(protocolPrivate); | |
464 | } | |
465 | if (keys != keys_q) { | |
466 | CFAllocatorDeallocate(NULL, keys); | |
467 | CFAllocatorDeallocate(NULL, vals); | |
468 | } | |
469 | } | |
470 | ||
471 | CFRelease(non_protocol_entities); | |
472 | ||
473 | return array; | |
474 | } | |
475 | ||
476 | ||
477 | static Boolean | |
478 | __SCNetworkServiceSetInterfaceEntity(SCNetworkServiceRef service, | |
479 | SCNetworkInterfaceRef interface) | |
480 | { | |
ba83da55 A |
481 | CFMutableDictionaryRef entity; |
482 | SCNetworkInterfacePrivateRef interfacePrivate = (SCNetworkInterfacePrivateRef)interface; | |
dbf6a266 A |
483 | Boolean ok; |
484 | CFStringRef path; | |
485 | SCNetworkServicePrivateRef servicePrivate = (SCNetworkServicePrivateRef)service; | |
486 | ||
487 | path = SCPreferencesPathKeyCreateNetworkServiceEntity(NULL, // allocator | |
488 | servicePrivate->serviceID, // service | |
489 | kSCEntNetInterface); // entity | |
ba83da55 A |
490 | entity = CFDictionaryCreateMutable(NULL, |
491 | 0, | |
492 | &kCFTypeDictionaryKeyCallBacks, | |
493 | &kCFTypeDictionaryValueCallBacks); | |
494 | if (interfacePrivate->entity_type != NULL) { | |
495 | CFDictionarySetValue(entity, | |
496 | kSCPropNetInterfaceType, | |
497 | interfacePrivate->entity_type); | |
498 | } | |
499 | if (interfacePrivate->entity_subtype != NULL) { | |
500 | CFDictionarySetValue(entity, | |
501 | kSCPropNetInterfaceSubType, | |
502 | interfacePrivate->entity_subtype); | |
503 | } | |
504 | if (interfacePrivate->entity_device != NULL) { | |
505 | CFDictionarySetValue(entity, | |
506 | kSCPropNetInterfaceDeviceName, | |
507 | interfacePrivate->entity_device); | |
508 | } | |
509 | if (interfacePrivate->entity_hardware != NULL) { | |
510 | CFDictionarySetValue(entity, | |
511 | kSCPropNetInterfaceHardware, | |
512 | interfacePrivate->entity_hardware); | |
513 | } | |
514 | if (CFEqual(interfacePrivate->interface_type, kSCNetworkInterfaceTypeModem) && | |
515 | interfacePrivate->supportsDeviceOnHold) { | |
516 | int one = 1; | |
517 | CFNumberRef num; | |
518 | ||
519 | num = CFNumberCreate(NULL, kCFNumberIntType, &one); | |
520 | CFDictionarySetValue(entity, | |
521 | kSCPropNetInterfaceSupportsModemOnHold, | |
522 | num); | |
523 | CFRelease(num); | |
524 | } | |
dbf6a266 A |
525 | ok = SCPreferencesPathSetValue(servicePrivate->prefs, path, entity); |
526 | CFRelease(entity); | |
527 | CFRelease(path); | |
528 | ||
529 | return ok; | |
530 | } | |
531 | ||
532 | ||
533 | SCNetworkServiceRef | |
534 | SCNetworkServiceCreate(SCPreferencesRef prefs, SCNetworkInterfaceRef interface) | |
535 | { | |
536 | CFArrayRef components; | |
537 | CFArrayRef interface_config; | |
538 | SCNetworkInterfaceRef newInterface; | |
539 | CFStringRef path; | |
540 | CFStringRef prefix; | |
541 | CFStringRef serviceID; | |
542 | SCNetworkServicePrivateRef servicePrivate; | |
543 | ||
544 | // establish the service | |
545 | prefix = SCPreferencesPathKeyCreateNetworkServices(NULL); | |
546 | path = SCPreferencesPathCreateUniqueChild(prefs, prefix); | |
547 | CFRelease(prefix); | |
548 | if (path == NULL) { | |
549 | return NULL; | |
550 | } | |
551 | ||
552 | components = CFStringCreateArrayBySeparatingStrings(NULL, path, CFSTR("/")); | |
553 | CFRelease(path); | |
554 | ||
555 | serviceID = CFArrayGetValueAtIndex(components, 2); | |
556 | servicePrivate = __SCNetworkServiceCreatePrivate(NULL, serviceID, NULL, prefs); | |
557 | CFRelease(components); | |
558 | ||
559 | // duplicate the interface and associate the copy with the new service | |
560 | newInterface = (SCNetworkInterfaceRef)__SCNetworkInterfaceCreateCopy(NULL, | |
561 | interface, | |
562 | (SCNetworkServiceRef)servicePrivate); | |
563 | servicePrivate->interface = newInterface; | |
564 | ||
565 | // establish "default" configuration(s) for the interface | |
566 | for (interface = newInterface; | |
567 | interface != NULL; | |
568 | interface = SCNetworkInterfaceGetInterface(interface)) { | |
569 | SCNetworkInterfaceRef childInterface; | |
570 | CFStringRef childInterfaceType = NULL; | |
571 | CFDictionaryRef config; | |
572 | CFStringRef interfaceType; | |
573 | ||
574 | interfaceType = SCNetworkInterfaceGetInterfaceType(interface); | |
ba83da55 | 575 | childInterface = SCNetworkInterfaceGetInterface(servicePrivate->interface); |
dbf6a266 A |
576 | if (childInterface != NULL) { |
577 | childInterfaceType = SCNetworkInterfaceGetInterfaceType(childInterface); | |
578 | } | |
579 | ||
580 | config = __copyInterfaceTemplate(interfaceType, childInterfaceType); | |
581 | if (config != NULL) { | |
582 | (void) __SCNetworkInterfaceSetConfiguration(interface, config, TRUE); | |
583 | CFRelease(config); | |
584 | } | |
585 | } | |
586 | ||
587 | // add the interface [entity] to the service | |
588 | (void) __SCNetworkServiceSetInterfaceEntity((SCNetworkServiceRef)servicePrivate, | |
589 | servicePrivate->interface); | |
590 | ||
ba83da55 | 591 | // push the [deep] interface configuration into into the service. |
dbf6a266 A |
592 | interface_config = __SCNetworkInterfaceCopyDeepConfiguration(servicePrivate->interface); |
593 | __SCNetworkInterfaceSetDeepConfiguration(servicePrivate->interface, interface_config); | |
594 | ||
595 | return (SCNetworkServiceRef)servicePrivate; | |
596 | } | |
597 | ||
598 | ||
599 | Boolean | |
600 | SCNetworkServiceGetEnabled(SCNetworkServiceRef service) | |
601 | { | |
602 | Boolean enabled; | |
603 | CFStringRef path; | |
604 | SCNetworkServicePrivateRef servicePrivate = (SCNetworkServicePrivateRef)service; | |
605 | ||
606 | path = SCPreferencesPathKeyCreateNetworkServiceEntity(NULL, // allocator | |
607 | servicePrivate->serviceID, // service | |
608 | NULL); // entity | |
609 | enabled = __getPrefsEnabled(servicePrivate->prefs, path); | |
610 | CFRelease(path); | |
611 | ||
612 | return enabled; | |
613 | } | |
614 | ||
615 | ||
616 | SCNetworkInterfaceRef | |
617 | SCNetworkServiceGetInterface(SCNetworkServiceRef service) | |
618 | { | |
619 | SCNetworkServicePrivateRef servicePrivate = (SCNetworkServicePrivateRef)service; | |
620 | ||
621 | if (servicePrivate->interface == NULL) { | |
622 | CFDictionaryRef entity; | |
623 | CFStringRef path; | |
624 | ||
625 | path = SCPreferencesPathKeyCreateNetworkServiceEntity(NULL, // allocator | |
626 | servicePrivate->serviceID, // service | |
627 | kSCEntNetInterface); // entity | |
628 | entity = SCPreferencesPathGetValue(servicePrivate->prefs, path); | |
629 | CFRelease(path); | |
630 | ||
631 | if (isA_CFDictionary(entity)) { | |
632 | servicePrivate->interface = __SCNetworkInterfaceCreateWithEntity(NULL, entity, service); | |
633 | } | |
634 | } | |
635 | ||
636 | return servicePrivate->interface; | |
637 | } | |
638 | ||
639 | ||
640 | CFStringRef | |
641 | SCNetworkServiceGetName(SCNetworkServiceRef service) | |
642 | { | |
643 | CFDictionaryRef entity; | |
644 | SCNetworkServicePrivateRef servicePrivate = (SCNetworkServicePrivateRef)service; | |
645 | CFStringRef name = NULL; | |
646 | CFStringRef path; | |
647 | ||
648 | path = SCPreferencesPathKeyCreateNetworkServiceEntity(NULL, // allocator | |
649 | servicePrivate->serviceID, // service | |
650 | NULL); // entity | |
651 | entity = SCPreferencesPathGetValue(servicePrivate->prefs, path); | |
652 | CFRelease(path); | |
653 | ||
654 | if (isA_CFDictionary(entity)) { | |
655 | name = CFDictionaryGetValue(entity, kSCPropUserDefinedName); | |
656 | } | |
657 | ||
658 | return isA_CFString(name) ? name : NULL; | |
659 | } | |
660 | ||
661 | ||
662 | CFStringRef | |
663 | SCNetworkServiceGetServiceID(SCNetworkServiceRef service) | |
664 | { | |
665 | SCNetworkServicePrivateRef servicePrivate = (SCNetworkServicePrivateRef)service; | |
666 | ||
667 | return servicePrivate->serviceID; | |
668 | } | |
669 | ||
670 | ||
671 | CFTypeID | |
672 | SCNetworkServiceGetTypeID(void) | |
673 | { | |
674 | pthread_once(&initialized, __SCNetworkServiceInitialize); /* initialize runtime */ | |
675 | return __kSCNetworkServiceTypeID; | |
676 | } | |
677 | ||
678 | ||
679 | Boolean | |
680 | SCNetworkServiceRemove(SCNetworkServiceRef service) | |
681 | { | |
682 | Boolean ok = FALSE; | |
683 | SCNetworkServicePrivateRef servicePrivate = (SCNetworkServicePrivateRef)service; | |
684 | CFArrayRef sets; | |
685 | CFStringRef path; | |
686 | ||
687 | // remove service from all sets | |
688 | ||
689 | sets = SCNetworkSetCopyAll(servicePrivate->prefs); | |
690 | if (sets != NULL) { | |
691 | CFIndex i; | |
692 | CFIndex n; | |
693 | ||
694 | n = CFArrayGetCount(sets); | |
695 | for (i = 0; i < n; i++) { | |
696 | SCNetworkSetRef set; | |
697 | ||
698 | set = CFArrayGetValueAtIndex(sets, i); | |
699 | ok = SCNetworkSetRemoveService(set, service); | |
700 | if (!ok && (SCError() != kSCStatusNoKey)) { | |
ba83da55 | 701 | break; |
dbf6a266 A |
702 | } |
703 | } | |
704 | CFRelease(sets); | |
705 | } | |
706 | ||
707 | // remove service | |
708 | ||
709 | path = SCPreferencesPathKeyCreateNetworkServiceEntity(NULL, // allocator | |
710 | servicePrivate->serviceID, // service | |
711 | NULL); // entity | |
712 | ok = SCPreferencesPathRemoveValue(servicePrivate->prefs, path); | |
713 | CFRelease(path); | |
714 | ||
715 | return ok; | |
716 | } | |
717 | ||
718 | ||
719 | Boolean | |
720 | SCNetworkServiceRemoveProtocolType(SCNetworkServiceRef service, CFStringRef protocolType) | |
721 | { | |
722 | CFDictionaryRef entity; | |
723 | Boolean ok = FALSE; | |
724 | CFStringRef path; | |
725 | SCNetworkServicePrivateRef servicePrivate = (SCNetworkServicePrivateRef)service; | |
726 | ||
727 | path = SCPreferencesPathKeyCreateNetworkServiceEntity(NULL, // allocator | |
728 | servicePrivate->serviceID, // service | |
729 | protocolType); // entity | |
730 | ||
731 | entity = SCPreferencesPathGetValue(servicePrivate->prefs, path); | |
732 | if (entity == NULL) { | |
733 | // if "protocol" does not exist | |
734 | _SCErrorSet(kSCStatusNoKey); | |
735 | goto done; | |
736 | } | |
737 | ||
738 | ok = SCPreferencesPathRemoveValue(servicePrivate->prefs, path); | |
739 | ||
740 | done : | |
741 | ||
742 | CFRelease(path); | |
743 | return ok; | |
744 | } | |
745 | ||
746 | ||
747 | Boolean | |
748 | SCNetworkServiceSetEnabled(SCNetworkServiceRef service, Boolean enabled) | |
749 | { | |
750 | Boolean ok; | |
751 | CFStringRef path; | |
752 | SCNetworkServicePrivateRef servicePrivate = (SCNetworkServicePrivateRef)service; | |
753 | ||
754 | path = SCPreferencesPathKeyCreateNetworkServiceEntity(NULL, // allocator | |
755 | servicePrivate->serviceID, // service | |
756 | NULL); // entity | |
757 | ok = __setPrefsEnabled(servicePrivate->prefs, path, enabled); | |
758 | CFRelease(path); | |
759 | ||
760 | return ok; | |
761 | } | |
762 | ||
763 | ||
764 | Boolean | |
765 | SCNetworkServiceSetName(SCNetworkServiceRef service, CFStringRef name) | |
766 | { | |
767 | CFDictionaryRef entity; | |
768 | Boolean ok = FALSE; | |
769 | CFStringRef path; | |
770 | SCNetworkServicePrivateRef servicePrivate = (SCNetworkServicePrivateRef)service; | |
771 | ||
772 | #define PREVENT_DUPLICATE_SERVICE_NAMES | |
773 | #ifdef PREVENT_DUPLICATE_SERVICE_NAMES | |
774 | if (isA_CFString(name)) { | |
775 | CFArrayRef sets; | |
776 | ||
777 | // ensure that each service is uniquely named within its sets | |
778 | ||
779 | sets = SCNetworkSetCopyAll(servicePrivate->prefs); | |
780 | if (sets != NULL) { | |
781 | CFIndex set_index; | |
782 | CFIndex set_count; | |
783 | ||
784 | set_count = CFArrayGetCount(sets); | |
785 | for (set_index = 0; set_index < set_count; set_index++) { | |
786 | CFIndex service_index; | |
787 | Boolean isDup = FALSE; | |
788 | Boolean isMember = FALSE; | |
789 | CFIndex service_count; | |
790 | CFArrayRef services; | |
791 | SCNetworkSetRef set = CFArrayGetValueAtIndex(sets, set_index); | |
792 | ||
793 | services = SCNetworkSetCopyServices(set); | |
794 | ||
795 | service_count = CFArrayGetCount(services); | |
796 | for (service_index = 0; service_index < service_count; service_index++) { | |
797 | CFStringRef otherID; | |
798 | CFStringRef otherName; | |
799 | SCNetworkServiceRef otherService; | |
800 | ||
801 | otherService = CFArrayGetValueAtIndex(services, service_index); | |
802 | ||
803 | otherID = SCNetworkServiceGetServiceID(otherService); | |
804 | if (CFEqual(servicePrivate->serviceID, otherID)) { | |
805 | // if the service is a member of this set | |
806 | isMember = TRUE; | |
807 | continue; | |
808 | } | |
809 | ||
810 | otherName = SCNetworkServiceGetName(otherService); | |
811 | if ((otherName != NULL) && CFEqual(name, otherName)) { | |
812 | isDup = TRUE; | |
813 | continue; | |
814 | } | |
815 | } | |
816 | ||
817 | CFRelease(services); | |
818 | ||
819 | if (isMember && isDup) { | |
820 | /* | |
821 | * if this service is a member of the set and | |
822 | * the "name" is not unique. | |
823 | */ | |
824 | CFRelease(sets); | |
825 | _SCErrorSet(kSCStatusKeyExists); | |
826 | return FALSE; | |
827 | } | |
828 | } | |
829 | ||
830 | CFRelease(sets); | |
831 | } | |
832 | } | |
833 | #endif /* PREVENT_DUPLICATE_SERVICE_NAMES */ | |
834 | ||
835 | path = SCPreferencesPathKeyCreateNetworkServiceEntity(NULL, // allocator | |
836 | servicePrivate->serviceID, // service | |
837 | NULL); // entity | |
838 | entity = SCPreferencesPathGetValue(servicePrivate->prefs, path); | |
839 | if ((entity == NULL) && (name != NULL)) { | |
840 | entity = CFDictionaryCreate(NULL, | |
841 | NULL, | |
842 | NULL, | |
843 | 0, | |
844 | &kCFTypeDictionaryKeyCallBacks, | |
845 | &kCFTypeDictionaryValueCallBacks); | |
846 | } | |
847 | if (isA_CFDictionary(entity)) { | |
848 | CFMutableDictionaryRef newEntity; | |
849 | ||
850 | newEntity = CFDictionaryCreateMutableCopy(NULL, 0, entity); | |
851 | if (isA_CFString(name)) { | |
852 | CFDictionarySetValue(newEntity, kSCPropUserDefinedName, name); | |
853 | } else { | |
854 | CFDictionaryRemoveValue(newEntity, kSCPropUserDefinedName); | |
855 | } | |
856 | ok = SCPreferencesPathSetValue(servicePrivate->prefs, path, newEntity); | |
857 | CFRelease(newEntity); | |
858 | } | |
859 | CFRelease(path); | |
860 | ||
861 | return ok; | |
862 | } |