1/*
2 * Copyright (c) 2011-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#include <pthread.h>
26#include <notify.h>
27#include <stdlib.h>
28#include <stdio.h>
29#include <sys/socket.h>
30#include <dispatch/dispatch.h>
31#include <xpc/xpc.h>
32
33#include "libSystemConfiguration_client.h"
34#include "network_information.h"
35#include "network_information_priv.h"
36
37static nwi_state_t	G_nwi_state		= NULL;
38static pthread_mutex_t	nwi_store_lock		= PTHREAD_MUTEX_INITIALIZER;
39static boolean_t	nwi_store_token_valid	= FALSE;
40
41static pthread_once_t	initialized		= PTHREAD_ONCE_INIT;
42static int		nwi_store_token;
43
44static boolean_t	nwi_store_force_refresh	= FALSE;
45
46#pragma mark -
47#pragma mark Network information [nwi] client support
48
49
50// Note: protected by __nwi_configuration_queue()
51static int			nwi_active	= 0;
52static libSC_info_client_t	*nwi_client	= NULL;
53
54
55static dispatch_queue_t
56__nwi_configuration_queue()
57{
58	static dispatch_once_t  once;
59	static dispatch_queue_t q;
60
61	dispatch_once(&once, ^{
62		q = dispatch_queue_create(NWI_SERVICE_NAME, NULL);
63	});
64
65	return q;
66}
67
68static
69void
70_nwi_state_initialize(void)
71{
72	const char	*nwi_key	= nwi_state_get_notify_key();
73	uint32_t	status		= notify_register_check(nwi_key,
74								&nwi_store_token);
75
76	if (status != NOTIFY_STATUS_OK) {
77		fprintf(stderr, "nwi_state: registration failed (%u)\n", status);
78	}
79	else {
80		nwi_store_token_valid = TRUE;
81	}
82}
83
84static
85void
86_nwi_set_alias(nwi_state* state, nwi_ifstate* ifstate)
87{
88	nwi_ifstate*	  ifstate_alias;
89	int af = ifstate->af;
90	int af_alias;
91
92	af_alias = (af == AF_INET)?AF_INET6:AF_INET;
93
94	ifstate_alias =
95		nwi_state_get_ifstate_with_name(state, af_alias,
96					ifstate->ifname);
97
98	if (ifstate_alias != NULL) {
99		ifstate_alias->af_alias = ifstate;
100	}
101	ifstate->af_alias = ifstate_alias;
102	return;
103}
104
105static
106void
107_nwi_state_reset_alias(nwi_state_t state) {
108	int i;
109
110	for (i = 0; i < state->ipv4_count; i++) {
111		state->nwi_ifstates[i].af_alias = NULL;
112	}
113
114	for (i = state->ipv6_start;
115	     i < state->ipv6_start + state->ipv6_count; i++) {
116		_nwi_set_alias(state, &state->nwi_ifstates[i]);
117	}
118}
119
120
121#pragma mark -
122#pragma mark Network information [nwi] APIs
123
124
125/*
126 * Function: nwi_state_get_notify_key
127 * Purpose:
128 *   Returns the BSD notify key to use to monitor when the state changes.
129 *
130 * Note:
131 *   The nwi_state_copy API uses this notify key to monitor when the state
132 *   changes, so each invocation of nwi_state_copy returns the current
133 *   information.
134 */
135const char *
136nwi_state_get_notify_key()
137{
138#if	!TARGET_IPHONE_SIMULATOR
139	return "com.apple.system.SystemConfiguration.nwi";
140#else	// !TARGET_IPHONE_SIMULATOR
141	return "com.apple.iOS_Simulator.SystemConfiguration.nwi";
142#endif	// !TARGET_IPHONE_SIMULATOR
143}
144
145#define ATOMIC_CMPXCHG(p, o, n)	__sync_bool_compare_and_swap((p), (o), (n))
146#define ATOMIC_INC(p)		__sync_fetch_and_add((p), 1)		// return (n++);
147#define ATOMIC_DEC(p)		__sync_sub_and_fetch((p), 1)		// return (--n);
148
149void
150_nwi_state_force_refresh()
151{
152	ATOMIC_CMPXCHG(&nwi_store_force_refresh, FALSE, TRUE);
153}
154
155static void
156nwi_state_retain(nwi_state_t state)
157{
158	ATOMIC_INC(&state->ref);
159	return;
160}
161
162/*
163 * Function: nwi_state_release
164 * Purpose:
165 *   Release the memory associated with the network state.
166 */
167void
168nwi_state_release(nwi_state_t state)
169{
170	if (ATOMIC_DEC(&state->ref) > 0) {
171		// if not last reference
172		return;
173	}
174
175	// release connection reference on 1-->0 transition
176	if (state->svr) {
177		dispatch_sync(__nwi_configuration_queue(), ^{
178			if (--nwi_active == 0) {
179				// if last reference, drop connection
180				libSC_info_client_release(nwi_client);
181				nwi_client = NULL;
182			}
183		});
184	}
185
186	// release nwi_state
187	free(state);
188
189	return;
190}
191
192static nwi_state *
193_nwi_state_copy_data()
194{
195	nwi_state_t		nwi_state	= NULL;
196	static const char	*proc_name	= NULL;
197	xpc_object_t		reqdict;
198	xpc_object_t		reply;
199
200	dispatch_sync(__nwi_configuration_queue(), ^{
201		if ((nwi_active++ == 0) || (nwi_client == NULL)) {
202			static dispatch_once_t	once;
203			static const char	*service_name	= NWI_SERVICE_NAME;
204
205			dispatch_once(&once, ^{
206				const char	*name;
207
208				// get [XPC] service name
209				name = getenv(service_name);
210				if ((name != NULL) && (issetugid() == 0)) {
211					service_name = strdup(name);
212				}
213
214				// get process name
215				proc_name = getprogname();
216			});
217
218			nwi_client =
219				libSC_info_client_create(__nwi_configuration_queue(),	// dispatch queue
220							 service_name,			// XPC service name
221							 "Network information");	// service description
222			if (nwi_client == NULL) {
223				--nwi_active;
224			}
225		}
226	});
227
228	if ((nwi_client == NULL) || !nwi_client->active) {
229		// if network information server not available
230		return NULL;
231	}
232
233	// create message
234	reqdict = xpc_dictionary_create(NULL, NULL, 0);
235
236	// set process name
237	if (proc_name != NULL) {
238		xpc_dictionary_set_string(reqdict, NWI_PROC_NAME, proc_name);
239	}
240
241	// set request
242	xpc_dictionary_set_int64(reqdict, NWI_REQUEST, NWI_REQUEST_COPY);
243
244	// send request to the DNS configuration server
245	reply = libSC_send_message_with_reply_sync(nwi_client, reqdict);
246	xpc_release(reqdict);
247
248	if (reply != NULL) {
249		const void	*dataRef;
250		size_t		dataLen	= 0;
251
252		dataRef = xpc_dictionary_get_data(reply, NWI_CONFIGURATION, &dataLen);
253		if (dataRef != NULL) {
254			nwi_state = malloc(dataLen);
255			bcopy((void *)dataRef, nwi_state, dataLen);
256			nwi_state->ref = 0;
257			nwi_state->svr = TRUE;
258		}
259
260		xpc_release(reply);
261	}
262
263	return nwi_state;
264}
265
266/*
267 * Function: nwi_state_copy
268 * Purpose:
269 *   Returns the current network state information.
270 *   Release after use by calling nwi_state_release().
271 */
272nwi_state_t
273nwi_state_copy(void)
274{
275	boolean_t	force_refresh;
276	nwi_state_t     nwi_state = NULL;
277	nwi_state_t	old_state = NULL;
278
279	pthread_once(&initialized, _nwi_state_initialize);
280	pthread_mutex_lock(&nwi_store_lock);
281
282	force_refresh = ATOMIC_CMPXCHG(&nwi_store_force_refresh, TRUE, FALSE);
283
284	if (G_nwi_state != NULL) {
285		int		check = 0;
286		uint32_t	status;
287
288		if (nwi_store_token_valid == FALSE) {
289			/* have to throw cached copy away every time */
290			check = 1;
291		}
292		else {
293			status = notify_check(nwi_store_token, &check);
294			if (status != NOTIFY_STATUS_OK) {
295				fprintf(stderr, "nwi notify_check: failed with %u\n",
296					status);
297				/* assume that it changed, throw cached copy away */
298				check = 1;
299			}
300		}
301		if (check != 0 || force_refresh) {
302			/* new need snapshot */
303			old_state = G_nwi_state;
304			G_nwi_state = NULL;
305		}
306	}
307	/* Let's populate the cache if it's empty */
308	if (G_nwi_state == NULL) {
309		G_nwi_state = _nwi_state_copy_data();
310		if (G_nwi_state != NULL) {
311			/* one reference for G_nwi_state */
312			nwi_state_retain(G_nwi_state);
313			_nwi_state_reset_alias(G_nwi_state);
314		}
315	}
316	if (G_nwi_state != NULL) {
317		/* another reference for this caller */
318		nwi_state_retain(G_nwi_state);
319	}
320	nwi_state = G_nwi_state;
321	pthread_mutex_unlock(&nwi_store_lock);
322
323	if (old_state != NULL) {
324		/* get rid of G_nwi_state reference */
325		nwi_state_release(old_state);
326	}
327	return nwi_state;
328}
329
330/*
331 * Function: _nwi_state_ack
332 * Purpose:
333 *   Acknowledge receipt and any changes associated with the [new or
334 *   updated] network state.
335 */
336void
337_nwi_state_ack(nwi_state_t state, const char *bundle_id)
338{
339	xpc_object_t	reqdict;
340
341	if (state == NULL) {
342		return;	// ASSERT
343	}
344
345	if ((nwi_client == NULL) || !nwi_client->active) {
346		// if network information server not available
347		return;
348	}
349
350	dispatch_sync(__nwi_configuration_queue(), ^{
351		nwi_active++;	// keep connection active (for the life of the process)
352	});
353
354	// create message
355	reqdict = xpc_dictionary_create(NULL, NULL, 0);
356
357	// set request
358	xpc_dictionary_set_int64(reqdict, NWI_REQUEST, NWI_REQUEST_ACKNOWLEDGE);
359
360	// set generation
361	xpc_dictionary_set_uint64(reqdict, NWI_GENERATION, state->generation_count);
362
363	// send acknowledgement to the DNS configuration server
364	xpc_connection_send_message(nwi_client->connection, reqdict);
365
366	xpc_release(reqdict);
367	return;
368}
369
370/*
371 * Function: nwi_state_get_generation
372 * Purpose:
373 *   Returns the generation (mach_time) of the nwi_state data.
374 *   Every time the data is updated due to changes
375 *   in the network, this value will change.
376 */
377uint64_t
378nwi_state_get_generation(nwi_state_t state)
379{
380	return (state->generation_count);
381}
382
383/*
384 * Function: nwi_ifstate_get_generation
385 * Purpose:
386 *   Returns the generation (mach_time) of the nwi_ifstate data.
387 */
388uint64_t
389nwi_ifstate_get_generation(nwi_ifstate_t ifstate)
390{
391	return (ifstate->if_generation_count);
392}
393
394/*
395 * Function: nwi_ifstate_get_ifname
396 * Purpose:
397 *   Return the interface name of the specified ifstate.
398 */
399const char *
400nwi_ifstate_get_ifname(nwi_ifstate_t ifstate)
401{
402	return (ifstate != NULL?ifstate->ifname:NULL);
403
404}
405
406static uint64_t
407flags_from_af(int af)
408{
409    return ((af == AF_INET)
410	    ? NWI_IFSTATE_FLAGS_HAS_IPV4
411	    : NWI_IFSTATE_FLAGS_HAS_IPV6);
412}
413/*
414 * Function: nwi_ifstate_get_flags
415 * Purpose:
416 *   Return the flags for the given ifstate (see above for bit definitions).
417 */
418nwi_ifstate_flags
419nwi_ifstate_get_flags(nwi_ifstate_t ifstate)
420{
421	nwi_ifstate_t		alias = ifstate->af_alias;
422	nwi_ifstate_flags 	flags = 0ULL;
423
424	flags |= flags_from_af(ifstate->af);
425	if ((ifstate->flags & NWI_IFSTATE_FLAGS_HAS_DNS) != 0) {
426		flags |= NWI_IFSTATE_FLAGS_HAS_DNS;
427
428	}
429	if (alias != NULL) {
430		flags |= flags_from_af(alias->af);
431		if ((alias->flags & NWI_IFSTATE_FLAGS_HAS_DNS) != 0) {
432			flags |= NWI_IFSTATE_FLAGS_HAS_DNS;
433		}
434	}
435	return flags;
436}
437
438/*
439 * Function: nwi_state_get_first_ifstate
440 * Purpose:
441 *   Returns the first and highest priority interface that has connectivity
442 *   for the specified address family 'af'. 'af' is either AF_INET or AF_INET6.
443 *   The connectivity provided is for general networking.   To get information
444 *   about an interface that isn't available for general networking, use
445 *   nwi_state_get_ifstate().
446 *
447 *   Use nwi_ifstate_get_next() to get the next, lower priority interface
448 *   in the list.
449 *
450 *   Returns NULL if no connectivity for the specified address family is
451 *   available.
452 */
453nwi_ifstate_t
454nwi_state_get_first_ifstate(nwi_state_t state, int af)
455{
456	nwi_ifstate_t ifstate;
457
458	if (state == NULL) {
459		return NULL;
460	}
461
462	ifstate = nwi_state_get_ifstate_with_index(state, af, 0);
463	if ((ifstate->flags & NWI_IFSTATE_FLAGS_NOT_IN_LIST)
464	    != 0) {
465		ifstate =  NULL;
466	}
467
468	return ifstate;
469
470}
471
472/*
473 * Function: nwi_state_get_ifstate
474 * Purpose:
475 *   Return information for the specified interface 'ifname'.
476 *
477 *   This API directly returns the ifstate for the specified interface.
478 *   This is the only way to access information about an interface that isn't
479 *   available for general networking.
480 *
481 *   Returns NULL if no information is available for that interface.
482 */
483nwi_ifstate_t
484nwi_state_get_ifstate(nwi_state_t state, const char * ifname)
485{
486	nwi_ifstate_t ifstate = nwi_state_get_ifstate_with_name(state, AF_INET, ifname);
487	if (ifstate == NULL) {
488		ifstate = nwi_state_get_ifstate_with_name(state, AF_INET6, ifname);
489	}
490	return ifstate;
491
492}
493
494/*
495 * Function: nwi_ifstate_get_next
496 * Purpose:
497 *   Returns the next, lower priority nwi_ifstate_t after the specified
498 *   'ifstate' for the protocol family 'af'.
499 *
500 *   Returns NULL when the end of the list is reached.
501 */
502nwi_ifstate_t
503nwi_ifstate_get_next(nwi_ifstate_t ifstate, int af)
504{
505	nwi_ifstate_t alias, next;
506
507	alias =
508		(af == ifstate->af)?ifstate:ifstate->af_alias;
509
510	if (alias == NULL) {
511		return NULL;
512	}
513
514	/* We don't return interfaces marked rank never */
515	if ((alias->flags & NWI_IFSTATE_FLAGS_NOT_IN_LIST) != 0) {
516		return NULL;
517	}
518
519	next = ++alias;
520
521	if ((next->flags & NWI_IFSTATE_FLAGS_NOT_IN_LIST) == 0) {
522		return next;
523	}
524	return NULL;
525}
526
527/*
528 * Function: nwi_ifstate_compare_rank
529 * Purpose:
530 *   Compare the relative rank of two nwi_ifstate_t objects.
531 *
532 *   The "rank" indicates the importance of the underlying interface.
533 *
534 * Returns:
535 *   0 	if ifstate1 and ifstate2 are ranked equally
536 *  -1	if ifstate1 is ranked ahead of ifstate2
537 *   1	if ifstate2 is ranked ahead of ifstate1
538 */
539int
540nwi_ifstate_compare_rank(nwi_ifstate_t ifstate1, nwi_ifstate_t ifstate2)
541{
542	return RankCompare(ifstate1->rank, ifstate2->rank);
543}
544
545/*
546 * nwi_state_get_reachability_flags
547 *
548 * returns the global reachability flags for a given address family.
549 * If no address family is passed in, it returns the global reachability
550 * flags for either families.
551 *
552 * The reachability flags returned follow the definition of
553 * SCNetworkReachabilityFlags.
554 *
555 * If the flags are zero (i.e. do not contain kSCNetworkReachabilityFlagsReachable), there is no connectivity.
556 *
557 * Otherwise, at least kSCNetworkReachabilityFlagsReachable is set:
558 *        Reachable only
559 *          No other connection flags are set.
560 *        Reachable and no ConnectionRequired
561 *          If we have connectivity for the specified address family (and we'd
562 *          be returning the reachability flags associated with the default route)
563 *        Reachable and ConnectionRequired
564 *          If we do not currently have an active/primary network but we may
565 *          be able to establish connectivity.
566 *        Reachable and OnDemand
567 *          If we do not currently have an active/primary network but we may
568 *          be able to establish connective on demand.
569 *        Reachable and TransientConnection
570 *          This connection is transient.
571 *        Reachable and WWAN
572 *          This connection will be going over the cellular network.
573 */
574uint32_t
575nwi_state_get_reachability_flags(nwi_state_t nwi_state, int af)
576{
577	if (nwi_state == NULL) {
578		return (0);
579	}
580	if (af == AF_INET || af == AF_INET6) {
581		nwi_ifstate_t ifstate;
582
583		ifstate = nwi_state_get_first_ifstate(nwi_state, af);
584
585		if (ifstate != NULL) {
586			return ifstate->reach_flags;
587		}
588
589		return (af == AF_INET) ? nwi_state->reach_flags_v4 : nwi_state->reach_flags_v6;
590	} else {
591		nwi_ifstate_t ifstate_v4;
592		nwi_ifstate_t ifstate_v6;
593
594		ifstate_v4 = nwi_state_get_first_ifstate(nwi_state, AF_INET);
595		ifstate_v6 = nwi_state_get_first_ifstate(nwi_state, AF_INET6);
596
597		if (ifstate_v4 != NULL) {
598			if (ifstate_v6 != NULL) {
599				if (nwi_ifstate_compare_rank(ifstate_v4, ifstate_v6) > 0) {
600					return ifstate_v6->reach_flags;
601				} else {
602					return ifstate_v4->reach_flags;
603				}
604			} else {
605				return ifstate_v4->reach_flags;
606			}
607		} else {
608			if (ifstate_v6 != NULL) {
609				return ifstate_v6->reach_flags;
610			}
611		}
612
613		if (nwi_state->reach_flags_v4 != 0) {
614			return nwi_state->reach_flags_v4;
615		}
616		// This is the case where both ifstate are NULL.
617		return nwi_state->reach_flags_v6;
618	}
619}
620
621/*
622 * nwi_ifstate_get_vpn_server
623 *
624 * returns a sockaddr representation of the vpn server address.
625 * NULL if PPP/VPN/IPSec server address does not exist.
626 */
627const struct sockaddr *
628nwi_ifstate_get_vpn_server(nwi_ifstate_t ifstate)
629{
630	const struct sockaddr * vpn_server_addr;
631
632	vpn_server_addr = (const struct sockaddr *)(void *)
633			   &ifstate->vpn_server_address;
634
635	if (vpn_server_addr->sa_family == 0) {
636		return NULL;
637	}
638	return vpn_server_addr;
639}
640
641/*
642 * nwi_ifstate_get_reachability_flags
643 *
644 * returns the reachability flags for the interface given an address family.
645 * The flags returned are those determined outside of
646 * the routing table.  [None, ConnectionRequired, OnDemand,
647 * Transient Connection, WWAN].
648 */
649uint32_t
650nwi_ifstate_get_reachability_flags(nwi_ifstate_t ifstate)
651{
652	return ifstate->reach_flags;
653}
654
655/*
656 * nwi_ifstate_get_signature
657 *
658 * returns the signature and its length for an ifstate given an address family.
659 * If AF_UNSPEC is passed in, the signature for a given ifstate is returned.
660 *
661 * If the signature does not exist, NULL is returned.
662 */
663const uint8_t *
664nwi_ifstate_get_signature(nwi_ifstate_t ifstate, int af, int * length)
665{
666	nwi_ifstate_t i_state = NULL;
667
668	switch (af) {
669		case AF_UNSPEC:
670			i_state = ifstate;
671			break;
672		case AF_INET:
673		case AF_INET6:
674			i_state = (ifstate->af == af) ? ifstate : ifstate->af_alias;
675			break;
676		default:
677			break;
678
679	}
680
681	if (i_state != NULL) {
682		if ((i_state->flags & NWI_IFSTATE_FLAGS_HAS_SIGNATURE) != 0) {
683			*length = sizeof(i_state->signature);
684			return (i_state->signature);
685		}
686	}
687
688	*length = 0;
689	return NULL;
690}
691
692static inline
693boolean_t
694_nwi_ifstate_is_in_list(nwi_ifstate_t ifstate, int af)
695{
696	nwi_ifstate_t i_state;
697
698	i_state = (ifstate->af == af) ? ifstate : ifstate->af_alias;
699	if (i_state == NULL) {
700		return FALSE;
701	}
702
703	if ((nwi_ifstate_get_flags(i_state) & NWI_IFSTATE_FLAGS_NOT_IN_LIST) == 0) {
704		return TRUE;
705	}
706
707	return FALSE;
708}
709
710/*
711 * nwi_ifstate_get_dns_signature
712 *
713 * returns the signature and its length for given
714 * ifstate with a valid dns configuration.
715 *
716 * If the signature does not exist, NULL is returned.
717 *
718 */
719const uint8_t *
720nwi_ifstate_get_dns_signature(nwi_ifstate_t ifstate, int * length)
721{
722	const uint8_t *	signature = NULL;
723	const uint8_t *	v4_signature;
724	int		v4_signature_len;
725	const uint8_t *	v6_signature;
726	int		v6_signature_len;
727
728	*length = 0;
729
730	if ((nwi_ifstate_get_flags(ifstate) & NWI_IFSTATE_FLAGS_HAS_DNS) == 0) {
731		return NULL;
732	}
733
734	v4_signature = nwi_ifstate_get_signature(ifstate, AF_INET, &v4_signature_len);
735	v6_signature = nwi_ifstate_get_signature(ifstate, AF_INET6, &v6_signature_len);
736	if (v4_signature == NULL && v6_signature == NULL) {
737		return NULL;
738	}
739
740	if (_nwi_ifstate_is_in_list(ifstate, AF_INET) == TRUE) {
741		signature = v4_signature;
742		*length = v4_signature_len;
743	} else {
744		if (_nwi_ifstate_is_in_list(ifstate, AF_INET6) != TRUE && v4_signature_len > 0) {
745			/* v6 is ranked never, v4 is ranked never but has a valid signature */
746			signature = v4_signature;
747			*length = v4_signature_len;
748		} else {
749			/* v6 is not ranked never or v4 has no signature */
750			signature = v6_signature;
751			*length = v6_signature_len;
752		}
753	}
754
755	return signature;
756}
757
758
759#pragma mark -
760#pragma mark Network information [nwi] test code
761
762
763#ifdef MAIN
764
765int
766main(int argc, char **argv)
767{
768	dns_config_t	*config;
769
770	config = dns_configuration_copy();
771	if (config != NULL) {
772		dns_configuration_free(&config);
773	}
774
775	exit(0);
776}
777
778#endif
779