]> git.saurik.com Git - apple/configd.git/blob - IPMonitorControl/IPMonitorControlServer.c
configd-699.1.5.tar.gz
[apple/configd.git] / IPMonitorControl / IPMonitorControlServer.c
1 /*
2 * Copyright (c) 2013-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 * IPMonitorControlServer.c
26 * - IPC channel to IPMonitor
27 * - used to create interface rank assertions
28 */
29
30 /*
31 * Modification History
32 *
33 * December 16, 2013 Dieter Siegmund (dieter@apple.com)
34 * - initial revision
35 */
36
37 #include <CoreFoundation/CoreFoundation.h>
38 #include <xpc/xpc.h>
39 #include <xpc/private.h>
40 #include <sys/queue.h>
41 #include <CoreFoundation/CFRunLoop.h>
42 #include <SystemConfiguration/SCNetworkConfigurationPrivate.h>
43 #include "IPMonitorControlServer.h"
44 #include "symbol_scope.h"
45 #include "IPMonitorControlPrivate.h"
46 #include <SystemConfiguration/SCPrivate.h>
47
48 STATIC Boolean * S_verbose;
49
50 #ifdef TEST_IPMONITOR_CONTROL
51 #define my_log(__level, fmt, ...) SCPrint(TRUE, stdout, CFSTR(fmt "\n"), ## __VA_ARGS__)
52
53 #else /* TEST_IPMONITOR_CONTROL */
54 #include "ip_plugin.h"
55 #endif /* TEST_IPMONITOR_CONTROL */
56
57 STATIC dispatch_queue_t S_IPMonitorControlServerQueue;
58
59 typedef struct ControlSession ControlSession, * ControlSessionRef;
60
61 #define LIST_HEAD_ControlSession LIST_HEAD(ControlSessionHead, ControlSession)
62 #define LIST_ENTRY_ControlSession LIST_ENTRY(ControlSession)
63 LIST_HEAD_ControlSession S_ControlSessions;
64
65 struct ControlSession {
66 LIST_ENTRY_ControlSession link;
67 xpc_connection_t connection;
68 CFMutableDictionaryRef assertions; /* ifname<string> = rank<number> */
69 };
70
71 /**
72 ** Support Functions
73 **/
74 STATIC CFMutableArrayRef S_if_changes;
75 STATIC CFRange S_if_changes_range;
76
77 STATIC void
78 InterfaceChangedListAddInterface(CFStringRef ifname)
79 {
80 if (S_if_changes == NULL) {
81 S_if_changes = CFArrayCreateMutable(NULL,
82 0, &kCFTypeArrayCallBacks);
83 CFArrayAppendValue(S_if_changes, ifname);
84 S_if_changes_range.length = 1;
85 }
86 else if (CFArrayContainsValue(S_if_changes, S_if_changes_range,
87 ifname) == FALSE) {
88 CFArrayAppendValue(S_if_changes, ifname);
89 S_if_changes_range.length++;
90 }
91 }
92
93 STATIC CFArrayRef
94 InterfaceChangedListCopy(void)
95 {
96 CFArrayRef current_list;
97
98 current_list = S_if_changes;
99 S_if_changes = NULL;
100 return (current_list);
101 }
102
103 STATIC void
104 InterfaceRankAssertionAdd(const void * key, const void * value, void * context)
105 {
106 CFMutableDictionaryRef * assertions_p;
107 CFNumberRef existing_rank;
108 CFNumberRef rank = (CFNumberRef)value;
109
110 assertions_p = (CFMutableDictionaryRef *)context;
111 if (*assertions_p == NULL) {
112 *assertions_p
113 = CFDictionaryCreateMutable(NULL, 0,
114 &kCFTypeDictionaryKeyCallBacks,
115 &kCFTypeDictionaryValueCallBacks);
116 CFDictionarySetValue(*assertions_p, key, value);
117 return;
118 }
119 existing_rank = CFDictionaryGetValue(*assertions_p, key);
120 if (existing_rank == NULL
121 || (CFNumberCompare(rank, existing_rank, NULL)
122 == kCFCompareGreaterThan)) {
123 CFDictionarySetValue(*assertions_p, key, value);
124 }
125 return;
126 }
127
128 STATIC CFDictionaryRef
129 InterfaceRankAssertionsCopy(void)
130 {
131 CFMutableDictionaryRef assertions = NULL;
132 ControlSessionRef session;
133
134 LIST_FOREACH(session, &S_ControlSessions, link) {
135 if (session->assertions == NULL) {
136 continue;
137 }
138 CFDictionaryApplyFunction(session->assertions,
139 InterfaceRankAssertionAdd,
140 &assertions);
141 }
142 return (assertions);
143 }
144
145 STATIC CFRunLoopRef S_runloop;
146 STATIC CFRunLoopSourceRef S_signal_source;
147
148 STATIC void
149 SetNotificationInfo(CFRunLoopRef runloop, CFRunLoopSourceRef rls)
150 {
151 S_runloop = runloop;
152 S_signal_source = rls;
153 return;
154 }
155
156 STATIC void
157 GenerateNotification(void)
158 {
159 if (S_signal_source != NULL) {
160 CFRunLoopSourceSignal(S_signal_source);
161 if (S_runloop != NULL) {
162 CFRunLoopWakeUp(S_runloop);
163 }
164 }
165 return;
166 }
167
168 /**
169 ** ControlSession
170 **/
171 STATIC void
172 AddChangedInterface(const void * key, const void * value, void * context)
173 {
174 InterfaceChangedListAddInterface((CFStringRef)key);
175 return;
176 }
177
178 STATIC void
179 ControlSessionInvalidate(ControlSessionRef session)
180 {
181 if (*S_verbose) {
182 my_log(LOG_NOTICE, "Invalidating %p", session);
183 }
184 LIST_REMOVE(session, link);
185 if (session->assertions != NULL) {
186 my_log(LOG_DEBUG,
187 "IPMonitorControlServer: %p pid %d removing assertions %@",
188 session->connection,
189 xpc_connection_get_pid(session->connection),
190 session->assertions);
191 CFDictionaryApplyFunction(session->assertions, AddChangedInterface,
192 NULL);
193 CFRelease(session->assertions);
194 session->assertions = NULL;
195 GenerateNotification();
196 }
197 return;
198 }
199
200 STATIC void
201 ControlSessionRelease(void * p)
202 {
203 if (*S_verbose) {
204 my_log(LOG_NOTICE, "Releasing %p", p);
205 }
206 free(p);
207 return;
208 }
209
210 STATIC ControlSessionRef
211 ControlSessionLookup(xpc_connection_t connection)
212 {
213 return ((ControlSessionRef)xpc_connection_get_context(connection));
214 }
215
216 STATIC ControlSessionRef
217 ControlSessionCreate(xpc_connection_t connection)
218 {
219 ControlSessionRef session;
220
221 session = (ControlSessionRef)malloc(sizeof(*session));
222 bzero(session, sizeof(*session));
223 session->connection = connection;
224 xpc_connection_set_finalizer_f(connection, ControlSessionRelease);
225 xpc_connection_set_context(connection, session);
226 LIST_INSERT_HEAD(&S_ControlSessions, session, link);
227 if (*S_verbose) {
228 my_log(LOG_NOTICE, "Created %p (connection %p)", session, connection);
229 }
230 return (session);
231 }
232
233 STATIC ControlSessionRef
234 ControlSessionGet(xpc_connection_t connection)
235 {
236 ControlSessionRef session;
237
238 session = ControlSessionLookup(connection);
239 if (session != NULL) {
240 return (session);
241 }
242 return (ControlSessionCreate(connection));
243 }
244
245 STATIC void
246 ControlSessionSetInterfaceRank(ControlSessionRef session,
247 const char * ifname,
248 SCNetworkServicePrimaryRank rank)
249 {
250 CFStringRef ifname_cf;
251
252 if (session->assertions == NULL) {
253 if (rank == kSCNetworkServicePrimaryRankDefault) {
254 /* no assertions, no need to store rank */
255 return;
256 }
257 session->assertions
258 = CFDictionaryCreateMutable(NULL, 0,
259 &kCFTypeDictionaryKeyCallBacks,
260 &kCFTypeDictionaryValueCallBacks);
261 }
262 ifname_cf = CFStringCreateWithCString(NULL, ifname,
263 kCFStringEncodingUTF8);
264
265 if (rank == kSCNetworkServicePrimaryRankDefault) {
266 CFDictionaryRemoveValue(session->assertions, ifname_cf);
267 if (CFDictionaryGetCount(session->assertions) == 0) {
268 CFRelease(session->assertions);
269 session->assertions = NULL;
270 }
271 }
272 else {
273 CFNumberRef rank_cf;
274
275 rank_cf = CFNumberCreate(NULL, kCFNumberSInt32Type, &rank);
276 CFDictionarySetValue(session->assertions, ifname_cf, rank_cf);
277 CFRelease(rank_cf);
278 }
279 InterfaceChangedListAddInterface(ifname_cf);
280 GenerateNotification();
281 CFRelease(ifname_cf);
282 return;
283 }
284
285 STATIC SCNetworkServicePrimaryRank
286 ControlSessionGetInterfaceRank(ControlSessionRef session,
287 const char * ifname)
288 {
289 SCNetworkServicePrimaryRank rank = kSCNetworkServicePrimaryRankDefault;
290
291 if (session->assertions != NULL) {
292 CFStringRef ifname_cf;
293 CFNumberRef rank_cf;
294
295 ifname_cf = CFStringCreateWithCString(NULL, ifname,
296 kCFStringEncodingUTF8);
297 rank_cf = CFDictionaryGetValue(session->assertions, ifname_cf);
298 CFRelease(ifname_cf);
299 if (rank_cf != NULL) {
300 (void)CFNumberGetValue(rank_cf, kCFNumberSInt32Type, &rank);
301 }
302 }
303 return (rank);
304 }
305
306 /**
307 ** IPMonitorControlServer
308 **/
309 STATIC Boolean
310 IPMonitorControlServerValidateConnection(xpc_connection_t connection)
311 {
312 uid_t uid;
313
314 uid = xpc_connection_get_euid(connection);
315 return (uid == 0);
316 }
317
318 STATIC int
319 IPMonitorControlServerHandleSetInterfaceRank(xpc_connection_t connection,
320 xpc_object_t request,
321 xpc_object_t reply)
322 {
323 const char * ifname;
324 SCNetworkServicePrimaryRank rank;
325 ControlSessionRef session;
326
327 if (IPMonitorControlServerValidateConnection(connection) == FALSE) {
328 my_log(LOG_DEBUG,
329 "IPMonitorControlServer: %p pid %d permission denied",
330 connection, xpc_connection_get_pid(connection));
331 return (EPERM);
332 }
333 ifname
334 = xpc_dictionary_get_string(request,
335 kIPMonitorControlRequestKeyInterfaceName);
336 if (ifname == NULL) {
337 return (EINVAL);
338 }
339 rank = (SCNetworkServicePrimaryRank)
340 xpc_dictionary_get_uint64(request,
341 kIPMonitorControlRequestKeyPrimaryRank);
342 switch (rank) {
343 case kSCNetworkServicePrimaryRankDefault:
344 case kSCNetworkServicePrimaryRankFirst:
345 case kSCNetworkServicePrimaryRankLast:
346 case kSCNetworkServicePrimaryRankNever:
347 case kSCNetworkServicePrimaryRankScoped:
348 break;
349 default:
350 return (EINVAL);
351 }
352 session = ControlSessionGet(connection);
353 ControlSessionSetInterfaceRank(session, ifname, rank);
354 my_log(LOG_DEBUG,
355 "IPMonitorControlServer: %p pid %d set %s %u",
356 connection, xpc_connection_get_pid(connection), ifname, rank);
357 return (0);
358 }
359
360 STATIC int
361 IPMonitorControlServerHandleGetInterfaceRank(xpc_connection_t connection,
362 xpc_object_t request,
363 xpc_object_t reply)
364 {
365 const char * ifname;
366 SCNetworkServicePrimaryRank rank;
367 ControlSessionRef session;
368
369 if (reply == NULL) {
370 /* no point in processing the request if we can't provide an answer */
371 return (EINVAL);
372 }
373 session = ControlSessionLookup(connection);
374 if (session == NULL) {
375 /* no session, no rank assertion */
376 return (ENOENT);
377 }
378 ifname
379 = xpc_dictionary_get_string(request,
380 kIPMonitorControlRequestKeyInterfaceName);
381 if (ifname == NULL) {
382 return (EINVAL);
383 }
384 rank = ControlSessionGetInterfaceRank(session, ifname);
385 xpc_dictionary_set_uint64(reply, kIPMonitorControlResponseKeyPrimaryRank,
386 rank);
387 return (0);
388 }
389
390 STATIC void
391 IPMonitorControlServerHandleDisconnect(xpc_connection_t connection)
392 {
393 ControlSessionRef session;
394
395 if (*S_verbose) {
396 my_log(LOG_NOTICE, "IPMonitorControlServer: client %p went away",
397 connection);
398 }
399 session = ControlSessionLookup(connection);
400 if (session == NULL) {
401 /* never asserted anything */
402 return;
403 }
404 ControlSessionInvalidate(session);
405 return;
406 }
407
408 STATIC void
409 IPMonitorControlServerHandleRequest(xpc_connection_t connection,
410 xpc_object_t request)
411 {
412 xpc_type_t type;
413
414 type = xpc_get_type(request);
415 if (type == XPC_TYPE_DICTIONARY) {
416 int error = 0;
417 uint64_t request_type;
418 xpc_connection_t remote;
419 xpc_object_t reply;
420
421 request_type
422 = xpc_dictionary_get_uint64(request,
423 kIPMonitorControlRequestKeyType);
424 reply = xpc_dictionary_create_reply(request);
425 switch (request_type) {
426 case kIPMonitorControlRequestTypeSetInterfaceRank:
427 error = IPMonitorControlServerHandleSetInterfaceRank(connection,
428 request,
429 reply);
430 break;
431 case kIPMonitorControlRequestTypeGetInterfaceRank:
432 error = IPMonitorControlServerHandleGetInterfaceRank(connection,
433 request,
434 reply);
435 break;
436 default:
437 error = EINVAL;
438 break;
439 }
440 if (reply == NULL) {
441 /* client didn't want a reply */
442 return;
443 }
444 xpc_dictionary_set_int64(reply, kIPMonitorControlResponseKeyError,
445 error);
446 remote = xpc_dictionary_get_remote_connection(request);
447 xpc_connection_send_message(remote, reply);
448 xpc_release(reply);
449 }
450 else if (type == XPC_TYPE_ERROR) {
451 if (request == XPC_ERROR_CONNECTION_INVALID) {
452 IPMonitorControlServerHandleDisconnect(connection);
453 }
454 else if (request == XPC_ERROR_CONNECTION_INTERRUPTED) {
455 my_log(LOG_NOTICE,
456 "IPMonitorControlServer: connection interrupted");
457 }
458 }
459 else {
460 my_log(LOG_NOTICE, "IPMonitorControlServer: unexpected event");
461 }
462 return;
463 }
464
465 STATIC void
466 IPMonitorControlServerHandleNewConnection(xpc_connection_t connection)
467 {
468 xpc_handler_t handler;
469
470 handler = ^(xpc_object_t event) {
471 IPMonitorControlServerHandleRequest(connection, event);
472 };
473 xpc_connection_set_event_handler(connection, handler);
474 xpc_connection_resume(connection);
475 return;
476 }
477
478 STATIC xpc_connection_t
479 IPMonitorControlServerCreate(dispatch_queue_t queue, const char * name)
480 {
481 uint64_t flags = XPC_CONNECTION_MACH_SERVICE_LISTENER;
482 xpc_connection_t connection;
483 xpc_handler_t handler;
484
485 connection = xpc_connection_create_mach_service(name, queue, flags);
486 if (connection == NULL) {
487 return (NULL);
488 }
489 handler = ^(xpc_object_t event) {
490 xpc_type_t type;
491
492 type = xpc_get_type(event);
493 if (type == XPC_TYPE_CONNECTION) {
494 IPMonitorControlServerHandleNewConnection(event);
495 }
496 else if (type == XPC_TYPE_ERROR) {
497 const char * desc;
498
499 desc = xpc_dictionary_get_string(event, XPC_ERROR_KEY_DESCRIPTION);
500 if (event == XPC_ERROR_CONNECTION_INVALID) {
501 my_log(LOG_NOTICE, "IPMonitorControlServer: %s", desc);
502 xpc_release(connection);
503 }
504 else {
505 my_log(LOG_NOTICE, "IPMonitorControlServer: %s", desc);
506 }
507 }
508 else {
509 my_log(LOG_NOTICE, "IPMonitorControlServer: unknown event %p",
510 type);
511 }
512 };
513 S_IPMonitorControlServerQueue = queue;
514 xpc_connection_set_event_handler(connection, handler);
515 xpc_connection_resume(connection);
516 return (connection);
517 }
518
519 PRIVATE_EXTERN Boolean
520 IPMonitorControlServerStart(CFRunLoopRef runloop, CFRunLoopSourceRef rls,
521 Boolean * verbose)
522 {
523 dispatch_queue_t q;
524 xpc_connection_t connection;
525
526 S_verbose = verbose;
527 SetNotificationInfo(runloop, rls);
528 q = dispatch_queue_create("IPMonitorControlServer", NULL);
529 connection = IPMonitorControlServerCreate(q, kIPMonitorControlServerName);
530 if (connection == NULL) {
531 my_log(LOG_ERR,
532 "IPMonitorControlServer: failed to create server");
533 dispatch_release(q);
534 return (FALSE);
535 }
536 return (TRUE);
537 }
538
539 PRIVATE_EXTERN CFArrayRef
540 IPMonitorControlServerCopyInterfaceRankInformation(CFDictionaryRef * info)
541 {
542 __block CFArrayRef changed;
543 __block CFDictionaryRef dict;
544
545 dispatch_sync(S_IPMonitorControlServerQueue,
546 ^{
547 dict = InterfaceRankAssertionsCopy();
548 changed = InterfaceChangedListCopy();
549 });
550 *info = dict;
551 return (changed);
552 }