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