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