1/*
2 * Copyright (c) 2012, 2013 Apple Computer, Inc. All rights reserved.
3 */
4
5#include <pthread.h>
6#include <string.h>
7#include <stdio.h>
8#include <sys/errno.h>
9#include <sys/types.h>
10#include <sys/socket.h>
11#include <sys/ioctl.h>
12#include <CoreFoundation/CoreFoundation.h>
13#include <SystemConfiguration/SystemConfiguration.h>
14#include <SystemConfiguration/SCPrivate.h>
15#include <mach/boolean.h>
16
17#include "scnc_main.h"
18#include "scnc_utils.h"
19#include "network_detection.h"
20#include "sbslauncher.h"
21#include "app_layer.h"
22
23static boolean_t match_pattern(CFStringRef matchstr, CFStringRef matchpattern);
24static boolean_t match_dns(CFArrayRef cur_dns_array, CFStringRef match_dns_name);
25static boolean_t check_ssid(CFStringRef interface_name, CFArrayRef chk_ssid_array);
26static boolean_t check_dns(CFArrayRef cur_dns_array, CFArrayRef chk_dns_array, int checkall);
27static boolean_t check_domain(CFStringRef cur_domain, CFArrayRef chk_domain_array);
28static boolean_t ondemand_add_action(struct service *serv, CFStringRef action, CFPropertyListRef actionParameters);
29static void ondemand_clear_action(struct service *serv);
30static void dodisconnect(struct service *serv);
31static int start_https_probe(struct service *serv, CFStringRef https_probe_server, CFArrayRef matcharray, CFIndex matcharray_index,
32                      CFDictionaryRef primary_dns_dict, CFStringRef primary_interface_name, CFStringRef primary_interface_type);
33static void vpn_action( struct service *serv, CFStringRef match_action, CFPropertyListRef actionParameters);
34static boolean_t resume_check_network(struct service *serv, CFArrayRef match_array, CFIndex match_array_offset, CFDictionaryRef primary_dns_dict, CFStringRef primary_dns_int, CFStringRef primary_interface_type);
35#if TARGET_OS_EMBEDDED
36static boolean_t on_cell_network();
37#endif
38
39extern TAILQ_HEAD(, service) 	service_head;
40
41enum {
42	READ	= 0,	// read end of standard UNIX pipe
43	WRITE	= 1		// write end of standard UNIX pipe
44};
45
46struct probe {
47	struct service *serv;
48	CFStringRef url_string;
49	CFArrayRef matcharray;
50	CFIndex matcharray_index;
51	CFDictionaryRef primary_dns_dict;
52	CFStringRef primary_interface_name;
53	CFStringRef primary_interface_type;
54};
55
56struct dns_redirect_context {
57	int sbslauncher_sockets[2];
58	struct service *serv;
59};
60
61static Boolean
62service_is_valid (struct service *serv)
63{
64	Boolean found = FALSE;
65	struct service *serv_check = NULL;
66
67	// make sure service is still valid
68	TAILQ_FOREACH(serv_check, &service_head, next) {
69		if (serv_check == serv) {
70			found = TRUE;
71			break;
72		}
73	}
74
75	return found;
76}
77
78#define MAX_SAVED_REDIRECTED_ADDRS 20
79
80static void
81dns_redirect_detection_setup_callback(pid_t pid, void *context)
82{
83	int *fd = (int*)context;
84	int	on	= 1;
85
86	fd = ((struct dns_redirect_context*)context)->sbslauncher_sockets;
87	if (pid){
88		my_close(fd[WRITE]);
89		fd[WRITE] = -1;
90	} else {
91		my_close(fd[READ]);
92		fd[READ] = -1;
93		ioctl(fd[WRITE], FIONBIO, &on);
94	}
95}
96
97static void
98dns_redirect_detection_callback(pid_t pid, int status, struct rusage *rusage, void *ctx)
99{
100	struct dns_redirect_context *context = (struct dns_redirect_context*)ctx;
101	struct service      *serv = context->serv;
102	int exitcode;
103	uint8_t buf[(sizeof(struct sockaddr_storage) * MAX_SAVED_REDIRECTED_ADDRS)];
104
105	if (serv == NULL || !service_is_valid(serv)) {
106		goto done;
107	}
108
109	exitcode = WIFEXITED(status) ? WEXITSTATUS(status) : -1;
110
111	Boolean dnsRedirectDetected = (exitcode > 0);
112
113	my_CFRelease(&serv->dnsRedirectedAddresses);
114
115	if (dnsRedirectDetected) {
116		struct sockaddr *addr = NULL;
117		int i;
118		int readlen = 0;
119		int validatedBytes = 0;
120
121		if ((readlen = read(context->sbslauncher_sockets[READ], buf, sizeof(buf))) > 0) {
122			CFMutableDataRef ipv4Data = CFDataCreateMutable(kCFAllocatorDefault, 0);
123			CFMutableDataRef ipv6Data = CFDataCreateMutable(kCFAllocatorDefault, 0);
124			if (ipv4Data && ipv6Data) {
125				for (i = 0, addr = (void*)buf;
126					 (i < MAX_SAVED_REDIRECTED_ADDRS) && (addr->sa_len <= (readlen - validatedBytes));
127					 i++) {
128					if (addr->sa_family == AF_INET) {
129						struct sockaddr_in *addr_in = (struct sockaddr_in *)addr;
130						if (addr_in->sin_len < sizeof(struct sockaddr_in)) {
131							break;
132						}
133						CFDataAppendBytes(ipv4Data, (uint8_t*)&addr_in->sin_addr, sizeof(struct in_addr));
134					} else if (addr->sa_family == AF_INET6) {
135						struct sockaddr_in6 *addr_in6 = (struct sockaddr_in6 *)addr;
136						if (addr_in6->sin6_len < sizeof(struct sockaddr_in6)) {
137							break;
138						}
139						CFDataAppendBytes(ipv6Data, (uint8_t*)&addr_in6->sin6_addr, sizeof(struct in6_addr));
140					}
141
142					validatedBytes += addr->sa_len;
143					addr = (struct sockaddr *)(((uint8_t*)addr) + addr->sa_len);
144				}
145
146				CFMutableDictionaryRef addressesDict = CFDictionaryCreateMutable(kCFAllocatorDefault, 2, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
147				if (addressesDict) {
148					if (CFDataGetLength(ipv4Data)) {
149						CFMutableDictionaryRef ipv4Dict = CFDictionaryCreateMutable(kCFAllocatorDefault, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
150						if (ipv4Dict) {
151							CFDictionaryAddValue(ipv4Dict, kSCNetworkConnectionNetworkInfoAddresses, ipv4Data);
152							CFDictionaryAddValue(addressesDict, kSCNetworkConnectionNetworkInfoIPv4, ipv4Dict);
153							my_CFRelease(&ipv4Dict);
154						}
155					}
156					if (CFDataGetLength(ipv6Data)) {
157						CFMutableDictionaryRef ipv6Dict = CFDictionaryCreateMutable(kCFAllocatorDefault, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
158						if (ipv6Dict) {
159							CFDictionaryAddValue(ipv6Dict, kSCNetworkConnectionNetworkInfoAddresses, ipv6Data);
160							CFDictionaryAddValue(addressesDict, kSCNetworkConnectionNetworkInfoIPv6, ipv6Dict);
161							my_CFRelease(&ipv6Dict);
162						}
163					}
164					serv->dnsRedirectedAddresses = addressesDict;
165				}
166			}
167			my_CFRelease(&ipv4Data);
168			my_CFRelease(&ipv6Data);
169		}
170	}
171
172	SCLog(TRUE, LOG_DEBUG, CFSTR("dns_redirect_detection_callback %d %d %d"), status, exitcode, dnsRedirectDetected);
173	if (serv->dnsRedirectDetected != dnsRedirectDetected) {
174		serv->dnsRedirectDetected = dnsRedirectDetected;
175		ondemand_add_service(serv, FALSE);
176	}
177
178done:
179	my_close(context->sbslauncher_sockets[READ]);
180	my_close(context->sbslauncher_sockets[WRITE]);
181	free(context);
182}
183
184static Boolean
185dns_redirect_detection_start(struct service *serv)
186{
187	struct dns_redirect_context *context;
188	char socketstr[10];
189
190	context = malloc(sizeof(struct dns_redirect_context));
191	if (context == NULL)
192		goto fail;
193
194	if ((socketpair(AF_LOCAL, SOCK_STREAM, 0, context->sbslauncher_sockets) == -1)){
195		SCLog(TRUE, LOG_ERR, CFSTR("SCNC Controller: socketpair fails, errno = %s"), strerror(errno));
196		goto fail;
197	}
198	snprintf(socketstr, sizeof(socketstr), "%d", context->sbslauncher_sockets[WRITE]);
199
200	context->serv = serv;
201
202	if (SCNCExecSBSLauncherCommandWithArguments(SBSLAUNCHER_TYPE_DETECT_DNS_REDIRECT, dns_redirect_detection_setup_callback, dns_redirect_detection_callback, (void*)context, socketstr, NULL)) {
203		return TRUE;
204	}
205
206fail:
207	if (context != NULL) {
208		my_close(context->sbslauncher_sockets[READ]);
209		my_close(context->sbslauncher_sockets[WRITE]);
210		free(context);
211	}
212	return FALSE;
213}
214
215/* -----------------------------------------------------------------------------
216 ----------------------------------------------------------------------------- */
217static boolean_t
218match_pattern(CFStringRef matchstr, CFStringRef matchpattern)
219{
220	CFRange		range;
221	Boolean		ret		= FALSE;
222	CFStringRef	s1		= NULL;
223	Boolean		s1_created	= FALSE;
224
225    if (CFStringHasSuffix(matchpattern, CFSTR("*"))){
226		range.location = 0;
227		range.length = CFStringGetLength(matchpattern) - 1;
228		s1 = CFStringCreateWithSubstring(NULL, matchpattern, range);
229		if (s1 == NULL) {
230			goto done;
231		}
232		s1_created = TRUE;
233		ret = CFStringHasPrefix(matchstr, s1);
234	} else {
235		ret = CFEqual(matchstr, matchpattern);
236	}
237
238done:
239	if (s1_created)	CFRelease(s1);
240    return ret;
241}
242
243/* -----------------------------------------------------------------------------
244 ----------------------------------------------------------------------------- */
245static boolean_t
246match_dns(CFArrayRef cur_dns_array, CFStringRef match_dns_name)
247{
248    CFStringRef cur_dns = NULL;
249    boolean_t   match = false;
250    int         count = 0, i;
251
252    count = CFArrayGetCount(cur_dns_array);
253    for (i = 0; i < count; i++) {
254        cur_dns = CFArrayGetValueAtIndex(cur_dns_array, i);
255
256        if (match_pattern(match_dns_name, cur_dns)){
257            match = true;
258            break;
259        }
260    }
261    return match;
262}
263
264/* -----------------------------------------------------------------------------
265 ----------------------------------------------------------------------------- */
266static boolean_t
267check_ssid(CFStringRef interface_name, CFArrayRef chk_ssid_array)
268{
269	CFDictionaryRef interface_dict = NULL;
270	CFStringRef interface_key = NULL;
271	CFStringRef match_ssid = NULL;
272	boolean_t match = false;
273
274	if (!isArray(chk_ssid_array) || !isString(interface_name)) {
275		goto done;
276	}
277
278	interface_key = SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL,
279															   kSCDynamicStoreDomainState,
280															   interface_name,
281															   kSCEntNetAirPort);
282
283	interface_dict = SCDynamicStoreCopyValue(gDynamicStore, interface_key);
284
285	if (!isDictionary(interface_dict)) {
286		goto done;
287	}
288
289	match_ssid = CFDictionaryGetValue(interface_dict, SC_AIRPORT_SSID_STR_KEY);
290	if (isString(match_ssid) && CFArrayContainsValue(chk_ssid_array, CFRangeMake(0, CFArrayGetCount(chk_ssid_array)), match_ssid)) {
291		match = true;
292	}
293
294done:
295	if (interface_key) {
296		CFRelease(interface_key);
297	}
298	if (interface_dict) {
299		CFRelease(interface_dict);
300	}
301
302	return match;
303}
304
305/* -----------------------------------------------------------------------------
306 ----------------------------------------------------------------------------- */
307static boolean_t
308check_dns(CFArrayRef cur_dns_array, CFArrayRef chk_dns_array, int checkall)
309{
310    CFStringRef match_dns_name = NULL;
311    int count = 0, i;
312    int localDNScount = 0;
313    boolean_t   match = false;
314
315    count = CFArrayGetCount(chk_dns_array);
316    localDNScount = CFArrayGetCount(cur_dns_array);
317
318    for (i = 0; i < localDNScount; i++) {
319        match_dns_name = CFArrayGetValueAtIndex(cur_dns_array, i);
320        if (!isString(match_dns_name))
321            break;
322
323        /* check domain name */
324        if (match_dns(chk_dns_array, match_dns_name)){
325            if (!checkall){
326                /* we've found a match, get out */
327                match = true;
328                break;
329            }
330            continue;
331        }else {
332            break;
333        }
334    }
335    if ( i >= localDNScount)
336        match = true;
337
338    return match;
339}
340
341/* -----------------------------------------------------------------------------
342 ----------------------------------------------------------------------------- */
343
344static boolean_t
345check_domain(CFStringRef cur_domain, CFArrayRef chk_domain_array)
346{
347    CFStringRef match_domain_name = NULL;
348    int count = 0, i;
349    boolean_t   match = false;
350
351    count = CFArrayGetCount(chk_domain_array);
352
353    for (i = 0; i < count; i++) {
354        match_domain_name = CFArrayGetValueAtIndex(chk_domain_array, i);
355        if (match_domain_name){
356            if (_SC_domainEndsWithDomain(cur_domain, match_domain_name)){
357                //   if (CFStringCompare(cur_domain, match_domain_name, 0) == kCFCompareEqualTo) {
358                /* we've found a match, get out */
359                match = true;
360                break;
361            }
362        }
363    }
364    return match;
365}
366
367/* -----------------------------------------------------------------------------
368 ----------------------------------------------------------------------------- */
369int  copy_trigger_info(struct service *serv, CFMutableDictionaryRef *ondemand_dict_cp,
370                       CFMutableArrayRef *trigger_array_cp, CFMutableDictionaryRef *trigger_dict_cp)
371{
372    CFStringRef			serviceid = NULL;
373	CFDictionaryRef		ondemand_dict = NULL, current_trigger_dict = NULL;
374	CFArrayRef			current_triggers_array = NULL;
375	int					count = 0, i, found = 0;
376    int                 ret = -1;
377
378    *ondemand_dict_cp = NULL;
379    *trigger_array_cp = NULL;
380    *trigger_dict_cp = NULL;
381
382	if (gOndemand_key == NULL)
383		goto fail;
384
385	ondemand_dict = SCDynamicStoreCopyValue(gDynamicStore, gOndemand_key);
386	if (ondemand_dict) {
387		current_triggers_array = CFDictionaryGetValue(ondemand_dict, kSCNetworkConnectionOnDemandTriggers);
388		if (isArray(current_triggers_array)) {
389			found = 0;
390			count = CFArrayGetCount(current_triggers_array);
391			for (i = 0; i < count; i++) {
392				current_trigger_dict = CFArrayGetValueAtIndex(current_triggers_array, i);
393				if (!isDictionary(current_trigger_dict))
394					continue;
395				serviceid = CFDictionaryGetValue(current_trigger_dict, kSCNetworkConnectionOnDemandServiceID);
396				if (!isString(serviceid))
397					continue;
398				if (CFStringCompare(serviceid, serv->serviceID, 0) == kCFCompareEqualTo) {
399					found = 1;
400                    ret = i;
401					break;
402				}
403			}
404		}
405	}
406
407    if (!found){
408        goto fail;
409    }
410
411    /* make a copy of current_trigger_dict */
412    *trigger_dict_cp = CFDictionaryCreateMutableCopy(0, 0, current_trigger_dict);
413    if (*trigger_dict_cp == NULL)
414        goto fail;
415
416    *ondemand_dict_cp = CFDictionaryCreateMutableCopy(0, 0, ondemand_dict);
417    if (*ondemand_dict_cp == NULL)
418        goto fail;
419
420    *trigger_array_cp = CFArrayCreateMutableCopy(0, 0, current_triggers_array);
421    if (*trigger_array_cp == NULL)
422        goto fail;
423
424    goto done;
425
426fail:
427	my_CFRelease(trigger_dict_cp);
428	my_CFRelease(ondemand_dict_cp);
429	my_CFRelease(trigger_array_cp);
430
431done:
432	my_CFRelease(&ondemand_dict);
433
434    return ret;
435
436}
437
438/* -----------------------------------------------------------------------------
439 ----------------------------------------------------------------------------- */
440static boolean_t
441ondemand_add_action(struct service *serv, CFStringRef action, CFPropertyListRef actionParameters)
442{
443	boolean_t changed = !my_CFEqual(serv->ondemandAction, action);
444	my_CFRelease(&serv->ondemandAction);
445	my_CFRelease(&serv->ondemandActionParameters);
446	serv->ondemandAction = my_CFRetain(action);
447	if (isA_CFPropertyList(actionParameters)) {
448		serv->ondemandActionParameters = my_CFRetain(actionParameters);
449	}
450	return changed;
451}
452
453/* -----------------------------------------------------------------------------
454 ----------------------------------------------------------------------------- */
455static
456void ondemand_clear_action(struct service *serv)
457{
458    my_CFRelease(&serv->ondemandAction);
459}
460
461/* -----------------------------------------------------------------------------
462 ----------------------------------------------------------------------------- */
463static
464void dodisconnect(struct service *serv)
465{
466	SCLog(TRUE, LOG_DEBUG, CFSTR("SCNC Controller: do disconnect by network detection"));
467
468    /* reset ignore vpn flag */
469    scnc_stop(serv, 0, SIGTERM, SCNC_STOP_USER_REQ);
470}
471
472static
473void ondemand_save_probe_result(struct service *serv, CFStringRef URL, Boolean succeeded)
474{
475	if (serv->ondemandProbeResults == NULL) {
476		serv->ondemandProbeResults = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
477	}
478
479	CFMutableDictionaryRef results = (CFMutableDictionaryRef)serv->ondemandProbeResults;
480
481	CFDictionarySetValue(results, URL, succeeded ? kCFBooleanTrue : kCFBooleanFalse);
482}
483
484static
485Boolean ondemand_probe_already_sent(struct service *serv, CFStringRef URL)
486{
487	if (serv->ondemandProbeResults != NULL && URL != NULL) {
488		return CFDictionaryContainsValue(serv->ondemandProbeResults, URL);
489	}
490	return FALSE;
491}
492
493static
494void ondemand_clear_probe_results(struct service *serv)
495{
496	my_CFRelease(&serv->ondemandProbeResults);
497}
498
499static
500Boolean doEvaluateConnection(struct service *serv, CFArrayRef domainRules)
501{
502	Boolean needServerExceptionDNSConfig = FALSE;
503	CFMutableDictionaryRef DNSTriggeringDicts = NULL;
504	CFIndex domainRulesCount = 0;
505	CFIndex i;
506
507	if (!isArray(domainRules)) {
508		return FALSE;
509	}
510
511	domainRulesCount = CFArrayGetCount(domainRules);
512	if (domainRulesCount == 0) {
513		return FALSE;
514	}
515
516	DNSTriggeringDicts = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
517
518	if (!DNSTriggeringDicts) {
519		return FALSE;
520	}
521
522	for (i = 0; i < domainRulesCount; i++) {
523		CFDictionaryRef domainRule = CFArrayGetValueAtIndex(domainRules, i);
524		if (!isDictionary(domainRule)) {
525			continue;
526		}
527
528		CFStringRef domainAction = CFDictionaryGetValue(domainRule, kSCPropNetVPNOnDemandRuleActionParametersDomainAction);
529		if (!isString(domainAction)) {
530			continue;
531		}
532
533		if (CFStringCompare(domainAction, kSCValNetVPNOnDemandRuleActionParametersDomainActionConnectIfNeeded, 0) == kCFCompareEqualTo) {
534			CFArrayRef dnsServers = CFDictionaryGetValue(domainRule, kSCPropNetVPNOnDemandRuleActionParametersRequiredDNSServers);
535			CFArrayRef domains = CFDictionaryGetValue(domainRule, kSCPropNetVPNOnDemandRuleActionParametersDomains);
536			if (isArray(dnsServers) && CFArrayGetCount(dnsServers) && isArray(domains) && CFArrayGetCount(domains)) {
537				CFDictionaryRef dnsDictionary = NULL;
538				CFStringRef serviceExt = NULL;
539				CFStringRef key = NULL;
540				CFMutableArrayRef mutableDomains = NULL;
541				CFIndex domainCount = CFArrayGetCount(domains);
542				CFIndex domainIndex = 0;
543
544				mutableDomains = CFArrayCreateMutableCopy(kCFAllocatorDefault, domainCount, domains);
545				if (mutableDomains) {
546					/* Sanitize domain array for *.example domains */
547					for (domainIndex = 0; domainIndex < domainCount; domainIndex++) {
548						CFStringRef domain = CFArrayGetValueAtIndex(mutableDomains, domainIndex);
549						if (CFStringHasPrefix(domain, CFSTR("*."))) {
550							CFStringRef newDomain = NULL;
551							CFRange range;
552							range.location = 2;
553							range.length = CFStringGetLength(domain) - 2;
554							if (range.length > 0) {
555								newDomain = CFStringCreateWithSubstring(kCFAllocatorDefault, domain, range);
556								if (newDomain) {
557									CFArraySetValueAtIndex(mutableDomains, domainIndex, newDomain);
558									CFRelease(newDomain);
559								}
560							}
561						}
562					}
563
564					/* Create temporary service key */
565					serviceExt = CFStringCreateWithFormat(kCFAllocatorDefault, 0, CFSTR("%@-TMP%ld"), serv->serviceID, i);
566					if (serviceExt) {
567						dnsDictionary = create_dns(gDynamicStore, serv->serviceID, dnsServers, NULL, mutableDomains, TRUE);
568						key = SCDynamicStoreKeyCreateNetworkServiceEntity(kCFAllocatorDefault, kSCDynamicStoreDomainState, serviceExt, kSCEntNetDNS);
569						needServerExceptionDNSConfig = TRUE;
570					}
571				}
572				if (isDictionary(dnsDictionary) && isString(key)) {
573					CFDictionaryAddValue(DNSTriggeringDicts, key, dnsDictionary);
574				}
575				my_CFRelease(&serviceExt);
576				my_CFRelease(&mutableDomains);
577				my_CFRelease(&key);
578				my_CFRelease(&dnsDictionary);
579			}
580
581			CFStringRef probeURL = CFDictionaryGetValue(domainRule, kSCPropNetVPNOnDemandRuleActionParametersRequiredURLStringProbe);
582			if (isString(probeURL) && !ondemand_probe_already_sent(serv, probeURL)) {
583				CFStringRef primaryInterface = copy_primary_interface_name(serv->serviceID);
584				start_https_probe(serv, probeURL, NULL, 0, NULL, primaryInterface, NULL);
585				my_CFRelease(&primaryInterface);
586			}
587		}
588	}
589
590	if (needServerExceptionDNSConfig)
591	{
592		Boolean isHostname = FALSE;
593		CFStringRef remoteAddress = scnc_copy_remote_server(serv, &isHostname);
594		if (isHostname && isA_CFString(remoteAddress) && CFStringGetLength(remoteAddress) > 0) {
595			CFStringRef serviceExt = NULL;
596			CFArrayRef domains = CFArrayCreate(kCFAllocatorDefault, (const void **)&remoteAddress, 1, &kCFTypeArrayCallBacks);
597			CFStringRef dnsstatekey = CREATEGLOBALSTATE(kSCEntNetDNS);
598			CFDictionaryRef globalDNS = dnsstatekey ? SCDynamicStoreCopyValue(gDynamicStore, dnsstatekey) : NULL;
599			CFArrayRef dnsServers = globalDNS ? CFDictionaryGetValue(globalDNS, kSCPropNetDNSServerAddresses) : NULL;
600
601			serviceExt = CFStringCreateWithFormat(kCFAllocatorDefault, 0, CFSTR("%@-TMP-SERVER"), serv->serviceID);
602			if (serviceExt) {
603				CFDictionaryRef dnsDictionary = create_dns(gDynamicStore, serv->serviceID, dnsServers, NULL, domains, TRUE);
604				CFStringRef key = SCDynamicStoreKeyCreateNetworkServiceEntity(kCFAllocatorDefault, kSCDynamicStoreDomainState, serviceExt, kSCEntNetDNS);
605				if (isDictionary(dnsDictionary) && isString(key)) {
606					CFDictionaryAddValue(DNSTriggeringDicts, key, dnsDictionary);
607				}
608				my_CFRelease(&dnsDictionary);
609				my_CFRelease(&key);
610			}
611
612			my_CFRelease(&domains);
613			my_CFRelease(&globalDNS);
614			my_CFRelease(&dnsstatekey);
615			my_CFRelease(&serviceExt);
616		}
617		my_CFRelease(&remoteAddress);
618	}
619
620	if (CFDictionaryGetCount(DNSTriggeringDicts) == 0) {
621		my_CFRelease(&DNSTriggeringDicts);
622		return FALSE;
623	}
624
625	/* Add supplemental match domains */
626
627	if (serv->ondemandDNSTriggeringDicts && DNSTriggeringDicts && CFEqual(serv->ondemandDNSTriggeringDicts, DNSTriggeringDicts)) {
628		/* If equal, we don't need to do anything */
629		my_CFRelease(&DNSTriggeringDicts);
630	} else {
631		/* Otherwise, uninstall the old dictionaries from the dynamic store */
632		ondemand_unpublish_dns_triggering_dicts(serv);
633		my_CFRelease(&serv->ondemandDNSTriggeringDicts);
634		serv->ondemandDNSTriggeringDicts = DNSTriggeringDicts;
635	}
636
637	return TRUE;
638}
639
640/* -----------------------------------------------------------------------------
641 ----------------------------------------------------------------------------- */
642static
643void vpn_action(struct service *serv, CFStringRef match_action, CFPropertyListRef actionParameters)
644{
645	Boolean checked_dns_redirect = FALSE;
646	Boolean set_dns_triggering_dicts = FALSE;
647	boolean_t changed;
648
649	changed = ondemand_add_action(serv, match_action, actionParameters);
650
651	/* what VPN action should take place */
652	if (isString(match_action)){
653		if (CFStringCompare(match_action, kSCValNetVPNOnDemandRuleActionDisconnect, 0) == kCFCompareEqualTo){
654			dodisconnect(serv);
655		} else if (CFStringCompare(match_action, kSCValNetVPNOnDemandRuleActionAllow, 0) == kCFCompareEqualTo) {
656			/* For allow, if there is a retry list, detect redirecting */
657			CFArrayRef onRetryArray = NULL;
658			onRetryArray = CFDictionaryGetValue(serv->systemprefs, kSCPropNetVPNOnDemandMatchDomainsOnRetry);
659			if (isArray(onRetryArray) && (CFArrayGetCount(onRetryArray) > 0)) {
660				checked_dns_redirect = dns_redirect_detection_start(serv);
661			}
662		} else if (isArray(actionParameters) && CFStringCompare(match_action, kSCValNetVPNOnDemandRuleActionEvaluateConnection, 0) == kCFCompareEqualTo) {
663			/* Always detect DNS redirection if evaluating connection */
664			checked_dns_redirect = dns_redirect_detection_start(serv);
665
666			/* Create DNS triggering dicts */
667			set_dns_triggering_dicts = doEvaluateConnection(serv, actionParameters);
668		}
669
670        /* Do nothing for ignore and connect */
671	}
672
673	if (!checked_dns_redirect) {
674		serv->dnsRedirectDetected = FALSE;
675	}
676
677	if (!set_dns_triggering_dicts && serv->ondemandDNSTriggeringDicts) {
678		ondemand_unpublish_dns_triggering_dicts(serv);
679		my_CFRelease(&serv->ondemandDNSTriggeringDicts);
680	}
681
682	if (serv->flags & FLAG_SETUP_ONDEMAND) {
683		ondemand_add_service(serv, FALSE);
684	}
685
686	if (changed) {
687		app_layer_handle_network_detection_change(serv->serviceID);
688	}
689}
690
691/* -----------------------------------------------------------------------------
692 ----------------------------------------------------------------------------- */
693static void cancel_https_probe(struct probe *probeptr)
694{
695    my_CFRelease(&probeptr->url_string);
696    my_CFRelease(&probeptr->matcharray);
697    my_CFRelease(&probeptr->primary_interface_name);
698    my_CFRelease(&probeptr->primary_interface_type);
699    my_CFRelease(&probeptr->primary_dns_dict);
700    free(probeptr);
701}
702
703/* -----------------------------------------------------------------------------
704 ----------------------------------------------------------------------------- */
705static
706void https_probe_callback(pid_t pid, int status, struct rusage *rusage, void *context)
707{
708    struct probe        *probeptr = (struct probe*)context;
709	struct service      *serv;
710    CFStringRef         match_action = NULL;
711    CFDictionaryRef     match_dict = NULL;
712    int exitcode;
713
714    if (probeptr == NULL) {
715       return;
716    }
717
718    serv = probeptr->serv;
719    if (serv == NULL || !service_is_valid(serv)) {
720        /* If our service is no longer valid, bail */
721        cancel_https_probe(probeptr);
722        return;
723    }
724
725    exitcode = WIFEXITED(status) ? WEXITSTATUS(status) : -1;
726
727    ondemand_save_probe_result(serv, probeptr->url_string, (exitcode > 0));
728
729    if (probeptr->matcharray) {
730        if (exitcode > 0){
731            /* If probe worked, do the action */
732            match_dict = CFArrayGetValueAtIndex(probeptr->matcharray, probeptr->matcharray_index);
733            if (isDictionary(match_dict)) {
734                match_action = CFDictionaryGetValue(match_dict, kSCPropNetVPNOnDemandRuleAction);
735                if (isString(match_action)){
736                    vpn_action(serv, match_action, CFDictionaryGetValue(match_dict, kSCPropNetVPNOnDemandRuleActionParameters));
737                }
738            }
739        } else {
740            /* Try next action */
741            resume_check_network(serv, probeptr->matcharray, probeptr->matcharray_index + 1, probeptr->primary_dns_dict, probeptr->primary_interface_name, probeptr->primary_interface_type);
742        }
743    } else {
744        /* If the probe is outside of normal network detection, make sure we publish the result */
745        if (serv->flags & FLAG_SETUP_ONDEMAND) {
746            ondemand_add_service(serv, FALSE);
747        }
748    }
749
750    /* Release structures */
751    cancel_https_probe(probeptr);
752}
753
754/* -----------------------------------------------------------------------------
755 ----------------------------------------------------------------------------- */
756static
757struct probe* alloc_new_probe(struct service *serv, CFStringRef url_string, CFArrayRef matcharray, CFIndex matcharray_index,
758                              CFDictionaryRef primary_dns_dict, CFStringRef primary_interface_name,
759                              CFStringRef primary_interface_type)
760{
761    struct probe *probeptr = NULL;
762
763    /* set up probe ref */
764    if (!(probeptr = malloc(sizeof(struct probe))))
765        return NULL;
766
767    probeptr->serv = serv;
768    probeptr->url_string = my_CFRetain(url_string);
769    probeptr->matcharray = my_CFRetain(matcharray);
770    probeptr->matcharray_index = matcharray_index;
771    probeptr->primary_dns_dict = my_CFRetain(primary_dns_dict);
772    probeptr->primary_interface_name = my_CFRetain(primary_interface_name);
773    probeptr->primary_interface_type = my_CFRetain(primary_interface_type);;
774
775    return probeptr;
776}
777
778/* -----------------------------------------------------------------------------
779 returns:-
780 1 - child was spawned without error
781 ----------------------------------------------------------------------------- */
782static
783int start_https_probe(struct service *serv, CFStringRef https_probe_server, CFArrayRef matcharray, CFIndex matcharray_index,
784                      CFDictionaryRef primary_dns_dict, CFStringRef primary_interface_name, CFStringRef primary_interface_type)
785{
786	struct probe    *probeptr;
787	char			https_probe_server_str[255];
788	char            interface_str[IFNAMSIZ];
789	int				ret = 0;
790
791	if (primary_interface_name == NULL || https_probe_server == NULL){
792		goto done;
793	}
794
795	if (!CFStringGetCString(https_probe_server, https_probe_server_str, sizeof(https_probe_server_str), kCFStringEncodingMacRoman)) {
796		goto done;
797	}
798
799	if (!CFStringGetCString(primary_interface_name, interface_str, sizeof(interface_str), kCFStringEncodingMacRoman)) {
800		goto done;
801	}
802
803	if ((probeptr = alloc_new_probe(serv, https_probe_server, matcharray, matcharray_index, primary_dns_dict, primary_interface_name, primary_interface_type)) == NULL) {
804		goto done;
805	}
806
807	SCLog(TRUE, LOG_DEBUG, CFSTR("SCNC Controller: launching sbslauncher for %s (via %s)"), https_probe_server_str, interface_str);
808
809	if (SCNCExecSBSLauncherCommandWithArguments(SBSLAUNCHER_TYPE_PROBE_SERVER, NULL, https_probe_callback, (void*)probeptr, https_probe_server_str, interface_str, NULL) == 0) {
810		cancel_https_probe(probeptr);
811		goto done;
812	}
813
814	/* Save the result as FALSE until we hear a reply */
815	ondemand_save_probe_result(serv, https_probe_server, FALSE);
816	ret = 1;
817
818done:
819	return ret;
820}
821
822static boolean_t
823resume_check_network(struct service *serv, CFArrayRef match_array, CFIndex match_array_offset, CFDictionaryRef primary_dns_dict, CFStringRef primary_interface_name, CFStringRef primary_interface_type)
824{
825    CFStringRef     match_action = NULL;
826    CFDictionaryRef match_dict = NULL;
827    CFStringRef		cur_domain = NULL;
828    CFArrayRef      cur_dns = NULL;
829    CFArrayRef		match_domain_array = NULL;
830    CFArrayRef		match_dns_array = NULL;
831    CFArrayRef		match_ssid_array = NULL;
832    CFStringRef     match_interface = NULL;
833    CFStringRef     probe_server = NULL;
834    boolean_t       found = false;
835    boolean_t       domain_matched = true;
836    boolean_t       dns_matched = true;
837    boolean_t		ssid_matched = true;
838    boolean_t		interface_matched = true;
839    boolean_t       probe_sent = false;
840    CFIndex         count = 0, i;
841
842    if (!isArray(match_array)) {
843        goto done;
844    }
845
846    count = CFArrayGetCount(match_array);
847    if (match_array_offset >= count) {
848        goto done;
849    }
850
851    if (primary_dns_dict == NULL || primary_interface_name == NULL || primary_interface_type == NULL) {
852        goto done;
853    }
854
855    cur_domain = CFDictionaryGetValue(primary_dns_dict, kSCPropNetDNSDomainName);
856    cur_dns = CFDictionaryGetValue(primary_dns_dict, kSCPropNetDNSServerAddresses);
857
858    /* Traverse array, starting at offset */
859    for (i = match_array_offset; i < count; i++) {
860        match_dict = (CFDictionaryRef)CFArrayGetValueAtIndex(match_array, i);
861        if (!(isDictionary(match_dict)))
862            continue;
863
864        /* check interface */
865        match_interface = CFDictionaryGetValue(match_dict, kSCPropNetVPNOnDemandRuleInterfaceTypeMatch);
866        if (match_interface == NULL
867            || (isString(match_interface) && my_CFEqual(match_interface, primary_interface_type))) {
868            interface_matched = true;
869        } else {
870            interface_matched = false;
871        }
872
873        if (!interface_matched)
874            continue;
875
876        /* check ssid */
877        match_ssid_array = CFDictionaryGetValue(match_dict, kSCPropNetVPNOnDemandRuleSSIDMatch);
878        if (isArray(match_ssid_array)) {
879            ssid_matched = check_ssid(primary_interface_name, match_ssid_array);
880        } else if (match_ssid_array == NULL) {
881            ssid_matched = true;
882        }
883
884        if (!ssid_matched)
885            continue;
886
887        /* check domain name */
888        match_domain_array = CFDictionaryGetValue(match_dict, kSCPropNetVPNOnDemandRuleDNSDomainMatch);
889        if (isArray(match_domain_array) && isString(cur_domain)){
890            domain_matched = check_domain(cur_domain, match_domain_array);
891        } else if (match_domain_array == NULL) {
892            domain_matched = true;
893        } else {
894            domain_matched = false;
895        }
896
897        if ( !domain_matched )          /* domain name does not match, continue to next */
898            continue;
899
900        /* check dns */
901        match_dns_array = CFDictionaryGetValue(match_dict, kSCPropNetVPNOnDemandRuleDNSServerAddressMatch);
902        if (isArray(match_dns_array)){
903            dns_matched = check_dns(cur_dns, match_dns_array, 1);
904        } else if (match_dns_array == NULL){
905            dns_matched = true;
906        }
907
908        if (!dns_matched)
909            continue;
910
911        /* send probe if specified */
912        probe_server = CFDictionaryGetValue(match_dict, kSCPropNetVPNOnDemandRuleURLStringProbe);
913        if (isString(probe_server) && primary_interface_name){
914            if (!start_https_probe(serv, probe_server, match_array, i, primary_dns_dict, primary_interface_name, primary_interface_type)){
915                SCLog(TRUE, LOG_DEBUG, CFSTR("SCNC_Controller:check_network -- start_https_probe failed"));
916                continue;   /* If we fail the probe, check the next rule. */
917            }
918            probe_sent = true;
919        }
920        break;              /* found a match */
921    }
922
923    // Start of done section:
924    SCLog(TRUE, LOG_DEBUG, CFSTR("SCNC Controller:check_network -- interface_matched %d, ssid_matched %d, domain_matched %d, dns_matched %d"), interface_matched, ssid_matched, domain_matched, dns_matched);
925
926    if (!probe_sent){
927        if (isDictionary(match_dict)){
928            found = interface_matched && ssid_matched && domain_matched && dns_matched;
929            if (found){
930                /* get the action for the found network or default no match fall back action */
931                match_action = CFDictionaryGetValue(match_dict, kSCPropNetVPNOnDemandRuleAction);
932                SCLog(TRUE, LOG_DEBUG, CFSTR("SCNC Controller:check_network - match_action %@"), match_action);
933                vpn_action(serv, match_action, CFDictionaryGetValue(match_dict, kSCPropNetVPNOnDemandRuleActionParameters));
934            }
935        }else
936            SCLog(TRUE, LOG_DEBUG, CFSTR("SCNC Controller:check_network -- no match_dict"));
937
938    }
939
940done:
941    return found;
942}
943
944/* -----------------------------------------------------------------------------
945 do network detection
946 ----------------------------------------------------------------------------- */
947boolean_t check_network(struct service *serv)
948{
949    CFDictionaryRef primary_dns_dict = NULL;
950    CFStringRef     primary_interface_name = NULL;
951    CFStringRef     primary_interface_type = NULL;
952    CFStringRef     primary_interface_service_id = NULL;
953
954    Boolean         did_network_detection = FALSE;
955    Boolean         result = FALSE;
956
957    /* Always clear actions */
958	if (serv->flags & FLAG_SETUP_ONDEMAND) {
959		/* clear action from trigger */
960		ondemand_clear_action(serv);
961	}
962
963	/* Always clear probe results */
964	ondemand_clear_probe_results(serv);
965
966    /* Make sure there is a primary interface */
967    primary_interface_name = copy_primary_interface_name(serv->serviceID);
968    if (!isString(primary_interface_name)) {
969        goto done;
970    }
971
972    /* Make sure there is a valid interface type and DNS dictionary */
973    primary_interface_service_id = copy_service_id_for_interface(primary_interface_name);
974    if (!isString(primary_interface_service_id)) {
975        goto done;
976    }
977
978    primary_interface_type = copy_interface_type(primary_interface_service_id);
979    if (!isString(primary_interface_type)) {
980        goto done;
981    }
982
983    primary_dns_dict = copy_dns_dict(primary_interface_service_id);
984    if (!isDictionary(primary_dns_dict)) {
985        goto done;
986    }
987
988    /* Now actually check the network, potentially sending out a probe */
989    result = resume_check_network(serv, CFDictionaryGetValue(serv->systemprefs, kSCPropNetVPNOnDemandRules), 0, primary_dns_dict, primary_interface_name, primary_interface_type);
990    did_network_detection = TRUE;
991
992done:
993    if (!did_network_detection) {
994        /* Notify listeners about the clear */
995        if (serv->flags & FLAG_SETUP_ONDEMAND) {
996            ondemand_add_service(serv, FALSE);
997        }
998    }
999
1000    my_CFRelease(&primary_interface_service_id);
1001    my_CFRelease(&primary_interface_name);
1002    my_CFRelease(&primary_interface_type);
1003    my_CFRelease(&primary_dns_dict);
1004    return result;
1005}
1006