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