]> git.saurik.com Git - apple/configd.git/blame - nwi/network_information_server.c
configd-596.15.tar.gz
[apple/configd.git] / nwi / network_information_server.c
CommitLineData
5e9ce69e
A
1/*
2 * Copyright (c) 2012, 2013 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 * Modification History
26 *
27 * February 8, 2012 Allan Nathanson <ajn@apple.com>
28 * - initial revision
29 */
30
31#include <notify.h>
32#include <dispatch/dispatch.h>
33#include <xpc/xpc.h>
34#include <CommonCrypto/CommonDigest.h>
35#include <CoreFoundation/CoreFoundation.h>
36#include <SystemConfiguration/SCPrivate.h>
37
38#include "libSystemConfiguration_client.h"
39#include "libSystemConfiguration_server.h"
40
41#include <network_information.h>
42#include "network_information_priv.h"
43#include "network_information_server.h"
44
45
46#pragma mark -
47#pragma mark Globals
48
49
50/*
51 * S_nwi_info
52 *
53 * Note: all accesses should be made while running on the _nwi_server_queue()
54 */
55static libSC_info_server_t S_nwi_info;
56
57
58/*
59 * S_debug
60 * A boolean that enables additional logging.
61 */
62static Boolean *S_debug = NULL;
63static SCLoggerRef S_logger = NULL;
64
65
66/*
67 * S_sync_handler
68 * ACK (in-sync or not-in-sync) updates should be posted using
69 * this handler
70 *
71 * Note: all accesses should be made while running on the _nwi_server_queue()
72 */
73static _nwi_sync_handler_t S_sync_handler = NULL;
74
75
76#pragma mark -
77#pragma mark Support functions
78
79
80#ifdef NOT_YET_NEEDED
81static void
82log_xpc_object(const char *msg, xpc_object_t obj)
83{
84 char *desc;
85
86 desc = xpc_copy_description(obj);
87 if (*S_debug) {
88 SCLoggerLog(S_logger, LOG_INFO, "%s = %s", msg, desc);
89 }
90 free(desc);
91}
92#endif
93
94
95#pragma mark -
96#pragma mark Network information server "main"
97
98
99static dispatch_queue_t
100_nwi_state_server_queue()
101{
102 static dispatch_once_t once;
103 static dispatch_queue_t q;
104
105 dispatch_once(&once, ^{
106 q = dispatch_queue_create(NWI_SERVICE_NAME ".server", NULL);
107 });
108
109 return q;
110}
111
112
113/*
114 * _nwi_state_copy
115 *
116 * Called when a client wants a copy of the current
117 * Network information
118 *
119 * - caller must be running on the _nwi_server_queue()
120 */
121static void
122_nwi_state_copy(xpc_connection_t connection, xpc_object_t request)
123{
124 CFDataRef data;
125 uint64_t generation;
126 xpc_connection_t remote;
127 xpc_object_t reply;
128
129 remote = xpc_dictionary_get_remote_connection(request);
130 reply = xpc_dictionary_create_reply(request);
131 if (reply == NULL) {
132 SCLoggerLog(S_logger, LOG_ERR,
133 CFSTR("<%p> _nwi_state_copy: xpc_dictionary_create_reply: failed"),
134 connection);
135 return;
136 }
137
138 // extract data and generation #
139 data = _libSC_info_server_get_data(&S_nwi_info, connection, &generation);
140
141 if (*S_debug) {
142 const char *proc_name;
143
144 // extract process name
145 proc_name = xpc_dictionary_get_string(request, NWI_PROC_NAME);
146 if (proc_name == NULL) {
147 proc_name = "???";
148 }
149
150 SCLoggerLog(S_logger, LOG_INFO, CFSTR("<%p:%s[%d]> Network information copy: %lu"),
151 connection,
152 proc_name,
153 xpc_connection_get_pid(connection),
154 generation);
155 }
156
157 // return the Network information (if available)
158 if (data != NULL) {
159 xpc_dictionary_set_data(reply,
160 NWI_CONFIGURATION,
161 CFDataGetBytePtr(data),
162 CFDataGetLength(data));
163 }
164
165 // reply
166 xpc_connection_send_message(remote, reply);
167 xpc_release(reply);
168
169 return;
170}
171
172
173/*
174 * _nwi_state_acknowledge
175 *
176 * Called when a client wants to acknowledge processing
177 * of the Network information
178 *
179 * - caller must be running on the _nwi_server_queue()
180 */
181static void
182_nwi_state_acknowledge(xpc_connection_t connection, xpc_object_t request)
183{
184 Boolean changed;
185 uint64_t generation;
186
187 generation = xpc_dictionary_get_uint64(request, NWI_GENERATION);
188
189 if (*S_debug) {
190 SCLoggerLog(S_logger, LOG_INFO, CFSTR("<%p:%d> Network information ack: %lu"),
191 connection,
192 xpc_connection_get_pid(connection),
193 generation);
194 }
195
196 _libSC_info_server_acknowledged(&S_nwi_info, connection, generation);
197 changed = _libSC_info_server_acknowledged(&S_nwi_info, connection, generation);
198 if (changed) {
199 Boolean inSync;
200
201 // report change
202 inSync = _libSC_info_server_in_sync(&S_nwi_info);
203 S_sync_handler(inSync);
204 }
205
206 return;
207}
208
209
210static void
211process_request(xpc_connection_t connection, xpc_object_t request)
212{
213 int64_t op;
214
215 op = xpc_dictionary_get_int64(request, NWI_REQUEST);
216 switch (op) {
217 case NWI_REQUEST_COPY :
218 /*
219 * Return the Network information
220 */
221 _nwi_state_copy(connection, request);
222 break;
223
224 case NWI_REQUEST_ACKNOWLEDGE :
225 /*
226 * Acknowlege a [processed] Network information
227 */
228 _nwi_state_acknowledge(connection, request);
229
230 break;
231 default :
232 SCLoggerLog(S_logger, LOG_ERR,
233 CFSTR("<%p> unknown request : %d"),
234 connection,
235 op);
236
237 break;
238 }
239
240 return;
241}
242
243
244static void
245process_new_connection(xpc_connection_t c)
246{
247 if (*S_debug) {
248 SCLoggerLog(S_logger, LOG_INFO, CFSTR("<%p:%d> Network information session: open"),
249 c,
250 xpc_connection_get_pid(c));
251 }
252
253 _libSC_info_server_open(&S_nwi_info, c);
254
255 xpc_connection_set_target_queue(c, _nwi_state_server_queue());
256
257 xpc_connection_set_event_handler(c, ^(xpc_object_t xobj) {
258 xpc_type_t type;
259
260 type = xpc_get_type(xobj);
261 if (type == XPC_TYPE_DICTIONARY) {
262 // process the request
263 process_request(c, xobj);
264
265 } else if (type == XPC_TYPE_ERROR) {
266 const char *desc;
267
268 desc = xpc_dictionary_get_string(xobj, XPC_ERROR_KEY_DESCRIPTION);
269 if (xobj == XPC_ERROR_CONNECTION_INVALID) {
270 Boolean changed;
271
272 if (*S_debug) {
273 SCLoggerLog(S_logger, LOG_INFO, CFSTR("<%p:%d> Network information session: close"),
274 c,
275 xpc_connection_get_pid(c));
276 }
277
278 changed = _libSC_info_server_close(&S_nwi_info, c);
279 if (changed) {
280 Boolean inSync;
281
282 // report change
283 inSync = _libSC_info_server_in_sync(&S_nwi_info);
284 S_sync_handler(inSync);
285 }
286
287 } else if (xobj == XPC_ERROR_CONNECTION_INTERRUPTED) {
288 SCLoggerLog(S_logger, LOG_ERR,
289 CFSTR("<%p:%d> %s"),
290 c,
291 xpc_connection_get_pid(c),
292 desc);
293
294 } else {
295 SCLoggerLog(S_logger, LOG_ERR,
296 CFSTR("<%p:%d> Connection error: %d : %s"),
297 c,
298 xpc_connection_get_pid(c),
299 xobj,
300 desc);
301 }
302
303 } else {
304 SCLoggerLog(S_logger, LOG_ERR,
305 CFSTR("<%p:%d> unknown event type : %x"),
306 c,
307 xpc_connection_get_pid(c),
308 type);
309 }
310 });
311
312 xpc_connection_resume(c);
313
314 return;
315}
316
317
318#pragma mark -
319#pragma mark Network Information server SPIs
320
321
322__private_extern__
323void
324load_NetworkInformation(CFBundleRef bundle,
325 SCLoggerRef logger,
326 Boolean *bundleVerbose,
327 _nwi_sync_handler_t syncHandler)
328{
329 xpc_connection_t c;
330 const char *name;
331
332 S_debug = bundleVerbose;
333 S_logger = logger;
334
335 /*
336 * keep track of Network information acknowledgements
337 */
338 _libSC_info_server_init(&S_nwi_info);
339
340 /*
341 * save the in-sync/not-in-sync handler
342 */
343 S_sync_handler = Block_copy(syncHandler);
344
345 // create XPC listener
346 name = getenv(NWI_SERVICE_NAME);
347 if (name == NULL) {
348 name = NWI_SERVICE_NAME;
349 }
350
351 c = xpc_connection_create_mach_service(name,
352 _nwi_state_server_queue(),
353 XPC_CONNECTION_MACH_SERVICE_LISTENER);
354
355 xpc_connection_set_event_handler(c, ^(xpc_object_t event) {
356 xpc_type_t type;
357
358 type = xpc_get_type(event);
359 if (type == XPC_TYPE_CONNECTION) {
360 process_new_connection(event);
361
362 } else if (type == XPC_TYPE_ERROR) {
363 const char *desc;
364
365 desc = xpc_dictionary_get_string(event, XPC_ERROR_KEY_DESCRIPTION);
366 if (event == XPC_ERROR_CONNECTION_INVALID) {
367 SCLoggerLog(S_logger, LOG_ERR, CFSTR("Network information server: %s"), desc);
368 xpc_release(c);
369 } else if (event == XPC_ERROR_CONNECTION_INTERRUPTED) {
370 SCLoggerLog(S_logger, LOG_ERR, CFSTR("Network information server: %s"), desc);
371 } else {
372 SCLoggerLog(S_logger, LOG_ERR,
373 CFSTR("Network information server: Connection error: %d : %s"),
374 event,
375 desc);
376 }
377
378 } else {
379 SCLoggerLog(S_logger, LOG_ERR,
380 CFSTR("Network information server: unknown event type : %x"),
381 type);
382
383 }
384 });
385
386 xpc_connection_resume(c);
387
388SCLoggerLog(S_logger, LOG_DEBUG, CFSTR("XPC server \"%s\" started"), name);
389
390 return;
391}
392
393
394__private_extern__
395void
396_nwi_state_signature(nwi_state *state,
397 unsigned char *signature,
398 size_t signature_len)
399{
400 bzero(signature, signature_len);
401
402 if (state != NULL) {
403 CC_SHA1_CTX ctx;
404 uint64_t generation_save;
405 unsigned char *sha1;
406 unsigned char sha1_buf[CC_SHA1_DIGEST_LENGTH];
407
408 generation_save = state->generation_count;
409 state->generation_count = 0;
410
411 sha1 = (signature_len >= CC_SHA1_DIGEST_LENGTH) ? signature : sha1_buf;
412 CC_SHA1_Init(&ctx);
413 CC_SHA1_Update(&ctx,
414 state,
415 state->size);
416 CC_SHA1_Final(sha1, &ctx);
417 if (sha1 != signature) {
418 bcopy(sha1, signature, signature_len);
419 }
420
421 state->generation_count = generation_save;
422 }
423
424 return;
425}
426
427
428__private_extern__
429_Bool
430_nwi_state_store(nwi_state *state)
431{
432 Boolean in_sync;
433 uint64_t new_generation = 0;
434 CFDataRef new_nwi_info = NULL;
435 const char *notify_key;
436
437 if (state != NULL) {
438 const UInt8 *bytes;
439 CFIndex len;
440
441 new_generation = state->generation_count;
442
443 if (*S_debug) {
444 SCLoggerLog(S_logger, LOG_INFO, CFSTR("Network information updated: %llu"),
445 new_generation);
446 }
447
448 bytes = (const UInt8 *)state;
449 len = state->size;
450
451 new_nwi_info = CFDataCreate(NULL, bytes, len);
452 }
453
454 dispatch_sync(_nwi_state_server_queue(), ^{
455 _libSC_info_server_set_data(&S_nwi_info, new_nwi_info, new_generation);
456 });
457
458 if (new_nwi_info != NULL) {
459 CFRelease(new_nwi_info);
460 }
461
462 // if anyone is keeping us in sync, they now need to catchup
463 in_sync = _libSC_info_server_in_sync(&S_nwi_info);
464 S_sync_handler(in_sync);
465
466 // and let everyone else know that the configuration has been updated
467 notify_key = nwi_state_get_notify_key();
468 if (notify_key != NULL) {
469 uint32_t status;
470
471 _nwi_state_force_refresh();
472 status = notify_post(notify_key);
473 if (status != NOTIFY_STATUS_OK) {
474 SCLoggerLog(S_logger, LOG_ERR, CFSTR("notify_post() failed: %d"), status);
475 // notification posting failures are non-fatal
476 }
477 }
478
479 return TRUE;
480}
481
482
483#pragma mark -
484#pragma mark Testing
485
486
487#ifdef MAIN
488
489int
490main(int argc, char **argv)
491{
492 static Boolean verbose = (argc > 1) ? TRUE : FALSE;
493 // _sc_log = FALSE;
494 _sc_verbose = (argc > 1) ? TRUE : FALSE;
495 _sc_debug = TRUE;
496
497 load_NetworkInformation(CFBundleGetMainBundle(), // bundle
498 NULL, // SCLogger
499 &verbose, // bundleVerbose
500 ^(Boolean inSync) { // sync handler
501 SCLoggerLog(NULL, LOG_INFO,
502 CFSTR("in sync: %s"),
503 inSync ? "Yes" : "No");
504 });
505 CFRunLoopRun();
506 /* not reached */
507 exit(0);
508 return 0;
509}
510
511#endif /* MAIN */