1/*
2 * Copyright (c) 2000, 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/* -----------------------------------------------------------------------------
26includes
27----------------------------------------------------------------------------- */
28
29#include <fcntl.h>
30#include <unistd.h>
31#include <sys/param.h>
32#include <sys/socket.h>
33#include <sys/ioctl.h>
34#include <paths.h>
35#include <net/if.h>
36#include <net/ndrv.h>
37#include <mach/message.h>
38#include <mach/boolean.h>
39#include <mach/mach.h>
40#include <mach/mach_error.h>
41#include <CoreFoundation/CFMachPort.h>
42#include <mach/mach_time.h>
43#include <CoreFoundation/CoreFoundation.h>
44#include <CoreFoundation/CFUserNotification.h>
45#include <SystemConfiguration/SystemConfiguration.h>
46#include <SystemConfiguration/SCDPlugin.h>
47#include <SystemConfiguration/SCPrivate.h>      // for SCLog() and VPN private keys
48#include <SystemConfiguration/SCValidation.h>
49#include <pwd.h>
50
51#include "ppp_msg.h"
52#include "ppp_privmsg.h"
53#include "../Family/ppp_domain.h"
54#include "../Helpers/pppd/pppd.h"
55
56#include "scnc_main.h"
57#include "scnc_client.h"
58#include "ppp_manager.h"
59#include "ppp_option.h"
60#include "ppp_socket_server.h"
61#include "scnc_utils.h"
62#include "controller_options.h"
63#include "ne_sm_bridge_private.h"
64
65#include "../Drivers/PPTP/PPTP-plugin/pptp.h"
66#include "../Drivers/L2TP/L2TP-plugin/l2tp.h"
67#include "../Drivers/PPPoE/PPPoE-extension/PPPoE.h"
68
69#include "sessionTracer.h"
70
71
72/* -----------------------------------------------------------------------------
73Definitions
74----------------------------------------------------------------------------- */
75
76enum {
77	READ	= 0,	// read end of standard UNIX pipe
78	WRITE	= 1	// write end of standard UNIX pipe
79};
80
81#define MAX_EXTRACONNECTTIME 20 /* allows 20 seconds after wakeup */
82#define MIN_EXTRACONNECTTIME 3 /* if we do give extra time, give at lease 3 seconds */
83
84/* -----------------------------------------------------------------------------
85globals
86----------------------------------------------------------------------------- */
87
88extern TAILQ_HEAD(, service) 	service_head;
89
90static char *empty_str_s = "";
91static u_char *empty_str = (u_char*)"";
92
93/* -----------------------------------------------------------------------------
94Forward declarations
95----------------------------------------------------------------------------- */
96
97static void display_error(struct service *serv);
98static void exec_callback(pid_t pid, int status, struct rusage *rusage, void *context);
99static void exec_postfork(pid_t pid, void *arg);
100static int send_pppd_params(struct service *serv, CFDictionaryRef service, CFDictionaryRef options, u_int8_t onTraffic);
101static int change_pppd_params(struct service *serv, CFDictionaryRef service, CFDictionaryRef options);
102
103static void setup_PPPoE(struct service *serv);
104static void dispose_PPPoE(struct service *serv);
105
106/* -----------------------------------------------------------------------------
107--------------------------------------------------------------------------------
108--------------------------------------------------------------------------------
109    service management and control
110--------------------------------------------------------------------------------
111--------------------------------------------------------------------------------
112----------------------------------------------------------------------------- */
113
114/* -----------------------------------------------------------------------------
115----------------------------------------------------------------------------- */
116u_int16_t ppp_subtype(CFStringRef subtypeRef)
117{
118
119   if (my_CFEqual(subtypeRef, kSCValNetInterfaceSubTypePPPSerial))
120        return PPP_TYPE_SERIAL;
121    else if (my_CFEqual(subtypeRef, kSCValNetInterfaceSubTypePPPoE))
122        return PPP_TYPE_PPPoE;
123    else if (my_CFEqual(subtypeRef, kSCValNetInterfaceSubTypePPTP))
124        return PPP_TYPE_PPTP;
125    else if (my_CFEqual(subtypeRef, kSCValNetInterfaceSubTypeL2TP))
126        return PPP_TYPE_L2TP;
127
128	return PPP_TYPE_OTHER;
129}
130
131/* -----------------------------------------------------------------------------
132an interface structure needs to be created
133unit is the ppp managed unit
134----------------------------------------------------------------------------- */
135int ppp_new_service(struct service *serv)
136{
137    CFURLRef		url;
138    u_char			str[MAXPATHLEN], str2[32];
139
140   //  scnc_log(LOG_INFO, CFSTR("ppp_new_service, subtype = %%@, serviceID = %@."), serv->subtypeRef, serv->serviceID);
141
142    serv->u.ppp.ndrv_socket = -1;
143    serv->u.ppp.phase = PPP_IDLE;
144    serv->u.ppp.statusfd[READ] = -1;
145    serv->u.ppp.statusfd[WRITE] = -1;
146    serv->u.ppp.controlfd[READ] = -1;
147    serv->u.ppp.controlfd[WRITE] = -1;
148    serv->u.ppp.pid = -1;
149
150	if (serv->subtypeRef) {
151		strlcpy((char*)str, DIR_KEXT, sizeof(str));
152		str2[0] = 0;
153		CFStringGetCString(serv->subtypeRef, (char*)str2, sizeof(str2), kCFStringEncodingUTF8);
154		strlcat((char*)str, (char*)str2, sizeof(str));
155		strlcat((char*)str, ".ppp", sizeof(str));	// add plugin suffix
156		url = CFURLCreateFromFileSystemRepresentation(NULL, str, strlen((char*)str), TRUE);
157		if (url) {
158			my_CFRelease(&serv->u.ppp.bundleRef);
159			serv->u.ppp.bundleRef = CFBundleCreate(0, url);
160			CFRelease(url);
161		}
162	}
163
164    return 0;
165}
166
167/* -----------------------------------------------------------------------------
168an interface is come down, dispose the ppp structure
169----------------------------------------------------------------------------- */
170int ppp_dispose_service(struct service *serv)
171{
172
173    if (serv->u.ppp.phase != PPP_IDLE)
174        return 1;
175
176    // then free the structure
177	dispose_PPPoE(serv);
178    my_CFRelease(&serv->connection_nid);
179    my_CFRelease(&serv->connection_nap);
180	my_CFRelease(&serv->systemprefs);
181    return 0;
182}
183
184/* -----------------------------------------------------------------------------
185 ----------------------------------------------------------------------------- */
186void ppp_user_notification_callback(struct service *serv, CFUserNotificationRef userNotification, CFOptionFlags responseFlags)
187{
188
189}
190
191/* -----------------------------------------------------------------------------
192 ----------------------------------------------------------------------------- */
193static
194void display_error(struct service *serv)
195{
196    CFStringRef 	msg = NULL;
197    CFMutableDictionaryRef 	dict = NULL;
198    SInt32 			err;
199
200    SESSIONTRACERSTOP(serv);
201    STOP_TRACKING_VPN_LOCATION(serv);
202
203    if (serv->u.ppp.laststatus == EXIT_USER_REQUEST)
204        return;
205
206	if (serv->flags & FLAG_ONDEMAND)
207		return;
208
209    if ((serv->flags & FLAG_ALERTERRORS) == 0)
210        return;
211
212#if !TARGET_OS_EMBEDDED
213    if (serv->flags & FLAG_DARKWAKE)
214        return;
215#endif
216	/* <rdar://6701793>, we do not want to display error if the device is gone and ppp ONTRAFFIC mode is enabled */
217    if ((serv->flags & FLAG_ONTRAFFIC) && (serv->u.ppp.laststatus == EXIT_DEVICE_ERROR))
218        return;
219
220    if (serv->u.ppp.lastdevstatus) {
221
222		switch (serv->subtype) {
223			case PPP_TYPE_L2TP:
224				// filter out the following messages
225				switch (serv->u.ppp.lastdevstatus) {
226						/* Error 6 */
227					case EXIT_L2TP_NETWORKCHANGED: /* L2TP Error 6 */
228						return;
229				}
230				break;
231
232			case PPP_TYPE_PPTP:
233				// filter out the following messages
234				switch (serv->u.ppp.lastdevstatus) {
235						/* Error 6 */
236					case EXIT_PPTP_NETWORKCHANGED: /* PPTP Error 6 */
237						return;
238				}
239				break;
240		}
241
242		msg = CFStringCreateWithFormat(0, 0, CFSTR("%@ Error %d"), serv->subtypeRef, serv->u.ppp.lastdevstatus);
243    }
244
245    if (msg == NULL) {
246
247		// filter out the following messages
248		switch (serv->u.ppp.laststatus) {
249			case EXIT_USER_REQUEST: /* PPP Error 5 */
250				return;
251		}
252
253		msg = CFStringCreateWithFormat(0, 0, CFSTR("PPP Error %d"), serv->u.ppp.laststatus);
254	}
255
256    if (!msg || !CFStringGetLength(msg))
257		goto done;
258
259    dict = CFDictionaryCreateMutable(NULL, 0, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
260    if (!dict)
261		goto done;
262
263	if (gIconURLRef)
264		CFDictionaryAddValue(dict, kCFUserNotificationIconURLKey, gIconURLRef);
265	if (gBundleURLRef)
266		CFDictionaryAddValue(dict, kCFUserNotificationLocalizationURLKey, gBundleURLRef);
267
268	CFDictionaryAddValue(dict, kCFUserNotificationAlertMessageKey, msg);
269	CFDictionaryAddValue(dict, kCFUserNotificationAlertHeaderKey, (serv->subtype == PPP_TYPE_L2TP || serv->subtype == PPP_TYPE_PPTP) ? CFSTR("VPN Connection") : CFSTR("Network Connection"));
270
271	if (serv->userNotificationRef) {
272		err = CFUserNotificationUpdate(serv->userNotificationRef, 0, kCFUserNotificationNoteAlertLevel, dict);
273	}
274	else {
275		serv->userNotificationRef = CFUserNotificationCreate(NULL, 0, kCFUserNotificationNoteAlertLevel, &err, dict);
276		if (!serv->userNotificationRef)
277			goto done;
278
279		serv->userNotificationRLS = CFUserNotificationCreateRunLoopSource(NULL, serv->userNotificationRef,
280																		  user_notification_callback, 0);
281		if (!serv->userNotificationRLS) {
282			my_CFRelease(&serv->userNotificationRef);
283			goto done;
284		}
285		CFRunLoopAddSource(CFRunLoopGetCurrent(), serv->userNotificationRLS, kCFRunLoopDefaultMode);
286	}
287
288
289done:
290    my_CFRelease(&dict);
291    my_CFRelease(&msg);
292}
293
294/* -----------------------------------------------------------------------------
295	for PPPoE over Airport or Ethernet services, install a protocol to enable the interface
296	even when PPPoE is not connected
297----------------------------------------------------------------------------- */
298static
299void setup_PPPoE(struct service *serv)
300{
301    CFDictionaryRef	interface;
302	CFStringRef		hardware, device;
303    struct sockaddr_ndrv 	ndrv;
304
305	if (serv->subtype == PPP_TYPE_PPPoE) {
306
307		interface = copyEntity(gDynamicStore, kSCDynamicStoreDomainSetup, serv->serviceID, kSCEntNetInterface);
308		if (interface) {
309
310			device = CFDictionaryGetValue(interface, kSCPropNetInterfaceDeviceName);
311			hardware = CFDictionaryGetValue(interface, kSCPropNetInterfaceHardware);
312
313			if (isA_CFString(hardware) && isA_CFString(device)
314				&& ((CFStringCompare(hardware, kSCEntNetAirPort, 0) == kCFCompareEqualTo)
315					|| (CFStringCompare(hardware, kSCEntNetEthernet, 0) == kCFCompareEqualTo))) {
316
317				if (serv->device
318					&& (CFStringCompare(device, serv->device, 0) != kCFCompareEqualTo)) {
319					dispose_PPPoE(serv);
320				}
321
322				if (!serv->device) {
323					serv->u.ppp.ndrv_socket = socket(PF_NDRV, SOCK_RAW, 0);
324					if (serv->u.ppp.ndrv_socket >= 0) {
325						serv->device = device;
326						CFRetain(serv->device);
327						CFStringGetCString(device, (char*)ndrv.snd_name, sizeof(ndrv.snd_name), kCFStringEncodingMacRoman);
328						ndrv.snd_len = sizeof(ndrv);
329						ndrv.snd_family = AF_NDRV;
330						if (bind(serv->u.ppp.ndrv_socket, (struct sockaddr *)&ndrv, sizeof(ndrv)) < 0) {
331							dispose_PPPoE(serv);
332						}
333					}
334				}
335			}
336			else {
337				/* not an Airport device */
338				dispose_PPPoE(serv);
339			}
340
341			CFRelease(interface);
342		}
343	}
344}
345
346/* -----------------------------------------------------------------------------
347----------------------------------------------------------------------------- */
348static
349void dispose_PPPoE(struct service *serv)
350{
351	if (serv->u.ppp.ndrv_socket != -1) {
352		close(serv->u.ppp.ndrv_socket);
353		serv->u.ppp.ndrv_socket = -1;
354	}
355	if (serv->device) {
356		CFRelease(serv->device);
357		serv->device = 0;
358	}
359}
360
361/* -----------------------------------------------------------------------------
362changed for this ppp occured in configd cache
363----------------------------------------------------------------------------- */
364int ppp_setup_service(struct service *serv)
365{
366    u_int32_t 		lval;
367	CFDictionaryRef serviceConfig = NULL;
368
369	/* get some general setting flags first */
370	serv->flags &= ~(
371		FLAG_SETUP_ONTRAFFIC +
372		FLAG_SETUP_DISCONNECTONLOGOUT +
373		FLAG_SETUP_DISCONNECTONSLEEP +
374		FLAG_SETUP_PREVENTIDLESLEEP +
375		FLAG_SETUP_DISCONNECTONFASTUSERSWITCH +
376		FLAG_SETUP_ONDEMAND +
377		FLAG_DARKWAKE +
378		FLAG_SETUP_PERSISTCONNECTION +
379		FLAG_SETUP_DISCONNECTONWAKE);
380
381	my_CFRelease(&serv->systemprefs);
382	if (serv->ne_sm_bridge != NULL) {
383		serviceConfig = ne_sm_bridge_copy_configuration(serv->ne_sm_bridge);
384		if (serviceConfig != NULL) {
385			CFDictionaryRef pppDict = CFDictionaryGetValue(serviceConfig, kSCEntNetPPP);
386			if (pppDict != NULL) {
387				serv->systemprefs = CFRetain(pppDict);
388			}
389		}
390	} else {
391		serv->systemprefs = copyEntity(gDynamicStore, kSCDynamicStoreDomainSetup, serv->serviceID, kSCEntNetPPP);
392	}
393	if (serv->systemprefs) {
394		lval = 0;
395		getNumber(serv->systemprefs, kSCPropNetPPPDialOnDemand, &lval);
396		if (lval) serv->flags |= FLAG_SETUP_ONTRAFFIC;
397
398		lval = 0;
399		getNumber(serv->systemprefs, kSCPropNetPPPDisconnectOnLogout, &lval);
400		if (lval) serv->flags |= FLAG_SETUP_DISCONNECTONLOGOUT;
401
402		// by default, vpn connection don't disconnect on sleep
403		switch (serv->subtype) {
404			case PPP_TYPE_PPTP:
405			case PPP_TYPE_L2TP:
406				lval = 0;
407				break;
408			default :
409				lval = 1;
410		}
411		getNumber(serv->systemprefs, kSCPropNetPPPDisconnectOnSleep, &lval);
412		if (lval) serv->flags |= FLAG_SETUP_DISCONNECTONSLEEP;
413
414		/* Currently, OnDemand imples network detection */
415		lval = 0;
416		getNumber(serv->systemprefs, kSCPropNetPPPOnDemandEnabled, &lval);
417		if (lval)
418			serv->flags |= (FLAG_SETUP_ONDEMAND | FLAG_SETUP_NETWORKDETECTION);
419		else if (CFDictionaryGetValue(serv->systemprefs, kSCPropNetVPNOnDemandRules)) {
420			if (controller_options_is_useVODDisconnectRulesWhenVODDisabled())
421				serv->flags |= FLAG_SETUP_NETWORKDETECTION;
422		}
423
424		// by default, vpn connection don't prevent idle sleep
425		switch (serv->subtype) {
426			case PPP_TYPE_PPTP:
427			case PPP_TYPE_L2TP:
428				lval = 0;
429				break;
430			default :
431				lval = 1;
432		}
433		getNumber(serv->systemprefs, CFSTR("PreventIdleSleep"), &lval);
434		if (lval) serv->flags |= FLAG_SETUP_PREVENTIDLESLEEP;
435
436		/* if the DisconnectOnFastUserSwitch key does not exist, use kSCPropNetPPPDisconnectOnLogout */
437		lval = (serv->flags & FLAG_SETUP_DISCONNECTONLOGOUT);
438		getNumber(serv->systemprefs, CFSTR("DisconnectOnFastUserSwitch"), &lval);
439		if (lval) serv->flags |= FLAG_SETUP_DISCONNECTONFASTUSERSWITCH;
440
441		/* "disconnect on wake" is enabled by default for PPP */
442		lval = 1;
443		serv->sleepwaketimeout = 0;
444		getNumber(serv->systemprefs, kSCPropNetPPPDisconnectOnWake, &lval);
445		if (lval) {
446			serv->flags |= FLAG_SETUP_DISCONNECTONWAKE;
447			getNumber(serv->systemprefs, kSCPropNetPPPDisconnectOnWakeTimer, &serv->sleepwaketimeout);
448		}
449
450		/* enable "ConnectionPersist" */
451		lval = 0;
452		getNumber(serv->systemprefs, CFSTR("ConnectionPersist"), &lval);
453		if (lval) serv->flags |= FLAG_SETUP_PERSISTCONNECTION;
454
455	}
456
457	setup_PPPoE(serv);
458
459	switch (serv->u.ppp.phase) {
460
461		case PPP_IDLE:
462			//printf("ppp_updatesetup : unit %d, PPP_IDLE\n", ppp->unit);
463			if (serv->flags & FLAG_SETUP_ONTRAFFIC
464				 && (!(serv->flags & FLAG_SETUP_DISCONNECTONLOGOUT) || gLoggedInUser)) {
465					ppp_start(serv, 0, 0, 0, serv->bootstrap, serv->au_session, 1, 0);
466			}
467			break;
468
469		case PPP_DORMANT:
470		case PPP_HOLDOFF:
471			// config has changed, dialontraffic will need to be restarted
472			serv->flags |= FLAG_CONFIGCHANGEDNOW;
473			serv->flags &= ~FLAG_CONFIGCHANGEDLATER;
474			scnc_stop(serv, 0, SIGTERM, SCNC_STOP_NONE);
475			break;
476
477		default :
478			// config has changed, dialontraffic will need to be restarted
479			serv->flags |= FLAG_CONFIGCHANGEDLATER;
480			serv->flags &= ~FLAG_CONFIGCHANGEDNOW;
481			/* if ppp was started in dialontraffic mode, then stop it */
482//                    if (ppp->dialontraffic)
483//                        ppp_disconnect(ppp, 0, SIGTERM);
484
485			if (serviceConfig == NULL) {
486				serviceConfig = copyService(gDynamicStore, kSCDynamicStoreDomainSetup, serv->serviceID);
487			}
488			if (serviceConfig) {
489				change_pppd_params(serv, serviceConfig, serv->connectopts);
490			}
491			break;
492	}
493
494	my_CFRelease(&serviceConfig);
495	return 0;
496}
497
498/* -----------------------------------------------------------------------------
499system is asking permission to sleep
500return if sleep is authorized
501----------------------------------------------------------------------------- */
502int ppp_can_sleep(struct service *serv)
503{
504    // I refuse idle sleep if ppp is connected
505	if (serv->u.ppp.phase == PPP_RUNNING
506		&& (serv->flags & FLAG_SETUP_PREVENTIDLESLEEP))
507		return 0;
508
509    return 1;
510}
511
512/* -----------------------------------------------------------------------------
513system is going to sleep
514disconnect services and return if a delay is needed
515----------------------------------------------------------------------------- */
516int ppp_will_sleep(struct service *serv, int checking)
517{
518    u_int32_t			delay = 0, alert = 0;
519
520	if (serv->u.ppp.phase != PPP_IDLE
521		&& (serv->flags & FLAG_SETUP_DISCONNECTONSLEEP)) {
522
523		delay = 1;
524		if (serv->u.ppp.phase != PPP_DORMANT || serv->u.ppp.phase != PPP_HOLDOFF)
525			alert = 2;
526		if (!checking)
527			scnc_stop(serv, 0, SIGTERM, SCNC_STOP_SYS_SLEEP);
528	}
529
530    return delay + alert;
531}
532
533/* -----------------------------------------------------------------------------
534ipv4 state has changed
535----------------------------------------------------------------------------- */
536void ppp_ipv4_state_changed(struct service *serv)
537{
538}
539
540/* -----------------------------------------------------------------------------
541system is waking up
542need to check the dialontraffic flag again
543----------------------------------------------------------------------------- */
544void ppp_wake_up(struct service	*serv)
545{
546
547	if (serv->u.ppp.phase == PPP_IDLE) {
548		if ((serv->flags & FLAG_SETUP_ONTRAFFIC)
549				&& (!(serv->flags & FLAG_SETUP_DISCONNECTONLOGOUT) || gLoggedInUser)) {
550				ppp_start(serv, 0, 0, 0, serv->bootstrap, serv->au_session, 1, 0);
551		}
552	} else {
553		if (DISCONNECT_VPN_IFOVERSLEPT(__FUNCTION__, serv, serv->if_name)) {
554			return;
555		} else if (DISCONNECT_VPN_IFLOCATIONCHANGED(serv)) {
556			return;
557		}
558	}
559}
560
561/* -----------------------------------------------------------------------------
562user has looged out
563need to check the disconnect on logout flag for the ppp interfaces
564----------------------------------------------------------------------------- */
565void ppp_log_out(struct service	*serv)
566{
567
568	if (serv->u.ppp.phase != PPP_IDLE
569		&& (serv->flags & FLAG_SETUP_DISCONNECTONLOGOUT))
570		scnc_stop(serv, 0, SIGTERM, SCNC_STOP_USER_LOGOUT);
571}
572
573/* -----------------------------------------------------------------------------
574user has logged in
575need to check the dialontraffic flag again
576----------------------------------------------------------------------------- */
577void ppp_log_in(struct service	*serv)
578{
579
580	if (serv->u.ppp.phase == PPP_IDLE
581		&& (serv->flags & FLAG_SETUP_ONTRAFFIC))
582		ppp_start(serv, 0, 0, 0, serv->bootstrap, serv->au_session, 1, 0);
583}
584
585/* -----------------------------------------------------------------------------
586user has switched
587need to check the disconnect on logout and dial on traffic
588flags for the ppp interfaces
589----------------------------------------------------------------------------- */
590void ppp_log_switch(struct service *serv)
591{
592	switch (serv->u.ppp.phase) {
593		case PPP_IDLE:
594			// rearm dial on demand
595			if (serv->flags & FLAG_SETUP_ONTRAFFIC)
596				ppp_start(serv, 0, 0, 0, serv->bootstrap, serv->au_session, 1, 0);
597			break;
598
599		default:
600			if (serv->flags & FLAG_SETUP_DISCONNECTONFASTUSERSWITCH) {
601
602				// if dialontraffic is set, it will need to be restarted
603				serv->flags &= ~FLAG_CONFIGCHANGEDLATER;
604				if (serv->flags & FLAG_SETUP_ONTRAFFIC)
605					serv->flags |= FLAG_CONFIGCHANGEDNOW;
606				else
607					serv->flags &= ~FLAG_CONFIGCHANGEDNOW;
608				scnc_stop(serv, 0, SIGTERM, SCNC_STOP_USER_SWITCH);
609			}
610	}
611}
612
613/* ----------------------------------------------------------------------------
614 ----------------------------------------------------------------------------- */
615int ppp_ondemand_add_service_data(struct service *serv, CFMutableDictionaryRef ondemand_dict)
616{
617	CFArrayRef			array;
618	CFStringRef			string;
619
620	if (serv->systemprefs == NULL)
621		return 0;
622
623#ifndef kSCPropNetPPPOnDemandMatchDomainsAlways
624#define	kSCPropNetPPPOnDemandMatchDomainsAlways kSCPropNetVPNOnDemandMatchDomainsAlways
625#endif
626#ifndef kSCPropNetPPPOnDemandMatchDomainsNever
627#define	kSCPropNetPPPOnDemandMatchDomainsNever kSCPropNetVPNOnDemandMatchDomainsNever
628#endif
629#ifndef kSCPropNetPPPOnDemandMatchDomainsOnRetry
630#define	kSCPropNetPPPOnDemandMatchDomainsOnRetry kSCPropNetVPNOnDemandMatchDomainsOnRetry
631#endif
632
633	array = CFDictionaryGetValue(serv->systemprefs, kSCPropNetPPPOnDemandMatchDomainsAlways);
634	if (isArray(array))
635		CFDictionarySetValue(ondemand_dict, kSCNetworkConnectionOnDemandMatchDomainsAlways, array);
636	array = CFDictionaryGetValue(serv->systemprefs, kSCPropNetPPPOnDemandMatchDomainsOnRetry);
637	if (isArray(array))
638		CFDictionarySetValue(ondemand_dict, kSCNetworkConnectionOnDemandMatchDomainsOnRetry, array);
639	array = CFDictionaryGetValue(serv->systemprefs, kSCPropNetPPPOnDemandMatchDomainsNever);
640	if (isArray(array))
641		CFDictionarySetValue(ondemand_dict, kSCNetworkConnectionOnDemandMatchDomainsNever, array);
642
643	string = CFDictionaryGetValue(serv->systemprefs, kSCPropNetPPPCommRemoteAddress);
644	if (isString(string))
645		CFDictionarySetValue(ondemand_dict, kSCNetworkConnectionOnDemandRemoteAddress, string);
646
647	return 0;
648}
649
650/* -----------------------------------------------------------------------------
651----------------------------------------------------------------------------- */
652static
653void addparam(char **arg, u_int32_t *argi, char *param)
654{
655    int len = strlen(param);
656
657    if (len && (arg[*argi] = malloc(len + 1))) {
658        strlcpy(arg[*argi], param, (len + 1));
659        (*argi)++;
660    }
661}
662
663/* -----------------------------------------------------------------------------
664----------------------------------------------------------------------------- */
665static
666void writeparam(int fd, char *param)
667{
668
669    write(fd, param, strlen(param));
670    write(fd, " ", 1);
671}
672
673/* -----------------------------------------------------------------------------
674----------------------------------------------------------------------------- */
675static
676void writeintparam(int fd, char *param, u_int32_t val)
677{
678    u_char	str[32];
679
680    writeparam(fd, param);
681    snprintf((char*)str, sizeof(str), "%d", val);
682    writeparam(fd, (char*)str);
683}
684
685/* -----------------------------------------------------------------------------
686----------------------------------------------------------------------------- */
687static
688void writedataparam(int fd, char *param, void *data, int len)
689{
690
691    writeintparam(fd, param, len);
692    write(fd, data, len);
693    write(fd, " ", 1);
694}
695
696/* -----------------------------------------------------------------------------
697----------------------------------------------------------------------------- */
698static
699void writestrparam(int fd, char *param, char *val)
700{
701
702    write(fd, param, strlen(param));
703
704    /* we need to quote and escape the parameter */
705    write(fd, " \"", 2);
706    while (*val) {
707        switch (*val) {
708            case '\\':
709            case '\"':
710				write(fd, "\\", 1);
711				break;
712            case '\'':
713            case ' ':
714                //write(fd, "\\", 1);
715                ;
716        }
717        write(fd, val, 1);
718        val++;
719    }
720    write(fd, "\" ", 2);
721}
722
723/* -----------------------------------------------------------------------------
724----------------------------------------------------------------------------- */
725
726static
727int send_pppd_params(struct service *serv, CFDictionaryRef service, CFDictionaryRef options, u_int8_t onTraffic)
728{
729    char 			str[MAXPATHLEN], str2[256];
730    int 			needpasswd = 0, tokendone = 0, auth_default = 1, from_service, optfd, awaketime, overrideprimary = 0;
731    u_int32_t			auth_bits = 0xF; /* PAP + CHAP + MSCHAP1 + MPCHAP2 */
732    u_int32_t			len, lval, lval1, i;
733    u_char 			sopt[OPT_STR_LEN];
734    CFDictionaryRef		pppdict = NULL, dict, modemdict;
735    CFArrayRef			array = NULL;
736    CFStringRef			string = NULL;
737    CFDataRef			dataref = 0;
738    void			*dataptr = 0;
739    u_int32_t			datalen = 0;
740	u_int32_t           ccp_enabled = 0;
741
742    pppdict = CFDictionaryGetValue(service, kSCEntNetPPP);
743    if ((pppdict == 0) || (CFGetTypeID(pppdict) != CFDictionaryGetTypeID()))
744        return -1; 	// that's bad...
745
746    optfd = serv->u.ppp.controlfd[WRITE];
747
748    writeparam(optfd, "[OPTIONS]");
749
750    // -----------------
751    // add the dialog plugin
752    if (gPluginsDir) {
753        CFStringGetCString(gPluginsDir, str, sizeof(str), kCFStringEncodingUTF8);
754        strlcat(str, "PPPDialogs.ppp", sizeof(str));
755        writestrparam(optfd, "plugin", str);
756		if (serv->subtype == PPP_TYPE_L2TP || serv->subtype == PPP_TYPE_PPTP ) {
757			writeintparam(optfd, "dialogtype", 1);
758		}
759	}
760
761	if(serv->subtype == PPP_TYPE_PPTP)
762		get_int_option(serv, kSCEntNetPPP, kSCPropNetPPPCCPEnabled, options, service, &ccp_enabled, 0);
763
764    // -----------------
765    // verbose logging
766    get_int_option(serv, kSCEntNetPPP, kSCPropNetPPPVerboseLogging, options, service, &lval, 0);
767    if (lval)
768        writeparam(optfd, "debug");
769
770    // -----------------
771    // alert flags
772    serv->flags &= ~(FLAG_ALERTERRORS + FLAG_ALERTPASSWORDS);
773    ppp_getoptval(serv, options, service, PPP_OPT_ALERTENABLE, &lval, sizeof(lval), &len);
774    if (lval & PPP_ALERT_ERRORS)
775        serv->flags |= FLAG_ALERTERRORS;
776    if (lval & PPP_ALERT_PASSWORDS)
777        serv->flags |= FLAG_ALERTPASSWORDS;
778
779    if (ppp_getoptval(serv, options, service, PPP_OPT_LOGFILE, sopt, sizeof(sopt), &len) && sopt[0]) {
780        // if logfile start with /, it's a full path
781        // otherwise it's relative to the logs folder (convention)
782        // we also strongly advise to name the file with the link number
783        // for example ppplog0
784        // the default path is /var/log
785        // it's useful to have the debug option with the logfile option
786        // with debug option, pppd will log the negociation
787        // debug option is different from kernel debug trace
788
789        snprintf(str, sizeof(str), "%s%s", sopt[0] == '/' ? "" : DIR_LOGS, sopt);
790        writestrparam(optfd, "logfile", str);
791    }
792
793    // -----------------
794    // connection plugin
795    if (serv->subtypeRef) {
796		CFStringGetCString(serv->subtypeRef, str2, sizeof(str2) - 4, kCFStringEncodingUTF8);
797		strlcat(str2, ".ppp", sizeof(str2));	// add plugin suffix
798		writestrparam(optfd, "plugin", str2);
799	}
800
801    // -----------------
802    // device name
803    if (ppp_getoptval(serv, options, service, PPP_OPT_DEV_NAME, sopt, sizeof(sopt), &len) && sopt[0])
804        writestrparam(optfd, "device", (char*)sopt);
805
806    // -----------------
807    // device speed
808    if (ppp_getoptval(serv, options, service, PPP_OPT_DEV_SPEED, &lval, sizeof(lval), &len) && lval) {
809        snprintf(str, sizeof(str), "%d", lval);
810        writeparam(optfd, str);
811    }
812
813    // Scoped interface
814    char outgoingInterfaceString[IFXNAMSIZ];
815    if (options && GetStrFromDict(options, CFSTR(NESessionStartOptionOutgoingInterface), outgoingInterfaceString, IFXNAMSIZ, "")) {
816        writestrparam(optfd, "ifscope", outgoingInterfaceString);
817    }
818
819    // -----------------
820    // subtype specific parameters
821
822    switch (serv->subtype) {
823
824        case PPP_TYPE_SERIAL:
825
826            // the controller has a built-in knowledge of serial connections
827
828            /*  PPPSerial currently supports Modem
829                in case of Modem, the DeviceName key will contain the actual device,
830                while the Modem dictionnary will contain the modem settings.
831                if Hardware is undefined, or different from Modem,
832                we expect to find external configuration files.
833                (This is the case for ppp over ssh)
834            */
835            get_str_option(serv, kSCEntNetInterface, kSCPropNetInterfaceHardware, options, 0, sopt, sizeof(sopt), &lval, empty_str);
836            if (strcmp((char*)sopt, "Modem")) {
837                // we are done
838                break;
839            }
840
841	#if 1
842			/* merge all modem options into modemdict */
843			modemdict = copyEntity(gDynamicStore, kSCDynamicStoreDomainSetup, serv->serviceID, kSCEntNetModem);
844			if (!modemdict) {
845                break;
846			}
847
848			if (options) {
849				dict = CFDictionaryGetValue(options, kSCEntNetModem);
850				if (dict &&
851					(CFGetTypeID(dict) == CFDictionaryGetTypeID()) &&
852					CFDictionaryGetCount(dict)) {
853
854					CFMutableDictionaryRef modemdict_mutable = CFDictionaryCreateMutableCopy(NULL, 0, modemdict);
855					if (modemdict_mutable) {
856						CFTypeRef value;
857
858						// merge it into modemdict only some of the keys
859
860						if ((value = CFDictionaryGetValue(dict, kSCPropNetModemConnectionScript)))
861							CFDictionarySetValue(modemdict_mutable, kSCPropNetModemConnectionScript, value);
862
863						if ((value = CFDictionaryGetValue(dict, kSCPropNetModemSpeaker)))
864							CFDictionarySetValue(modemdict_mutable, kSCPropNetModemSpeaker, value);
865						if ((value = CFDictionaryGetValue(dict, kSCPropNetModemErrorCorrection)))
866							CFDictionarySetValue(modemdict_mutable, kSCPropNetModemErrorCorrection, value);
867						if ((value = CFDictionaryGetValue(dict, kSCPropNetModemDataCompression)))
868							CFDictionarySetValue(modemdict_mutable, kSCPropNetModemDataCompression, value);
869						if ((value = CFDictionaryGetValue(dict, kSCPropNetModemPulseDial)))
870							CFDictionarySetValue(modemdict_mutable, kSCPropNetModemPulseDial, value);
871						if ((value = CFDictionaryGetValue(dict, kSCPropNetModemDialMode)))
872							CFDictionarySetValue(modemdict_mutable, kSCPropNetModemDialMode, value);
873
874						CFRelease(modemdict);
875						modemdict = modemdict_mutable;
876					}
877				}
878			}
879
880			/* serialize the modem dictionary, and pass it as a parameter */
881			if ((dataref = Serialize(modemdict, &dataptr, &datalen))) {
882
883				writedataparam(optfd, "modemdict", dataptr, datalen);
884				CFRelease(dataref);
885			}
886
887			CFRelease(modemdict);
888#endif
889	#if 0
890
891            if (ppp_getoptval(ppp, options, 0, PPP_OPT_DEV_CONNECTSCRIPT, sopt, sizeof(sopt), &len) && sopt[0]) {
892                // ---------- connect script parameter ----------
893                writestrparam(optfd, "modemscript", sopt);
894
895                // add all the ccl flags
896                get_int_option(ppp, kSCEntNetModem, kSCPropNetModemSpeaker, options, 0, &lval, 1);
897                writeparam(optfd, lval ? "modemsound" : "nomodemsound");
898
899                get_int_option(ppp, kSCEntNetModem, kSCPropNetModemErrorCorrection, options, 0, &lval, 1);
900                writeparam(optfd, lval ? "modemreliable" : "nomodemreliable");
901
902                get_int_option(ppp, kSCEntNetModem, kSCPropNetModemDataCompression, options, 0, &lval, 1);
903                writeparam(optfd, lval ? "modemcompress" : "nomodemcompress");
904
905                get_int_option(ppp, kSCEntNetModem, kSCPropNetModemPulseDial, options, 0, &lval, 0);
906                writeparam(optfd, lval ? "modempulse" : "modemtone");
907
908                // dialmode : 0 = normal, 1 = blind(ignoredialtone), 2 = manual
909                lval = 0;
910                ppp_getoptval(ppp, options, 0, PPP_OPT_DEV_DIALMODE, &lval, sizeof(lval), &len);
911                writeintparam(optfd, "modemdialmode", lval);
912            }
913#endif
914            break;
915
916        case PPP_TYPE_L2TP:
917
918            string = get_cf_option(kSCEntNetL2TP, kSCPropNetL2TPTransport, CFStringGetTypeID(), options, service, 0);
919            if (string) {
920                if (CFStringCompare(string, kSCValNetL2TPTransportIP, 0) == kCFCompareEqualTo)
921                    writeparam(optfd, "l2tpnoipsec");
922            }
923
924			/* check for SharedSecret keys in L2TP dictionary */
925            get_str_option(serv, kSCEntNetL2TP, kSCPropNetL2TPIPSecSharedSecret, options, service, sopt, sizeof(sopt), &lval, empty_str);
926            if (sopt[0]) {
927                writestrparam(optfd, "l2tpipsecsharedsecret", (char*)sopt);
928
929				string = get_cf_option(kSCEntNetL2TP, kSCPropNetL2TPIPSecSharedSecretEncryption, CFStringGetTypeID(), options, service, 0);
930				if (string) {
931					if (CFStringCompare(string, CFSTR("Key"), 0) == kCFCompareEqualTo)
932						writestrparam(optfd, "l2tpipsecsharedsecrettype", "key");
933					else if (CFStringCompare(string, kSCValNetL2TPIPSecSharedSecretEncryptionKeychain, 0) == kCFCompareEqualTo)
934						writestrparam(optfd, "l2tpipsecsharedsecrettype", "keychain");
935				}
936            }
937			/* then check IPSec dictionary */
938			else {
939				get_str_option(serv, kSCEntNetIPSec, kSCPropNetIPSecSharedSecret, options, service, sopt, sizeof(sopt), &lval, empty_str);
940				if (sopt[0]) {
941					writestrparam(optfd, "l2tpipsecsharedsecret", (char*)sopt);
942					string = get_cf_option(kSCEntNetL2TP, kSCPropNetIPSecSharedSecretEncryption, CFStringGetTypeID(), options, service, 0);
943					if (string) {
944						if (CFStringCompare(string, CFSTR("Key"), 0) == kCFCompareEqualTo)
945							writestrparam(optfd, "l2tpipsecsharedsecrettype", "key");
946						else if (CFStringCompare(string, kSCValNetIPSecSharedSecretEncryptionKeychain, 0) == kCFCompareEqualTo)
947							writestrparam(optfd, "l2tpipsecsharedsecrettype", "keychain");
948					}
949				}
950			}
951
952            get_int_option(serv, kSCEntNetL2TP, CFSTR("UDPPort"), options, service, &lval, 0 /* Dynamic port */);
953            writeintparam(optfd, "l2tpudpport", lval);
954            break;
955
956        case PPP_TYPE_PPTP:
957            get_int_option(serv, kSCEntNetPPTP, CFSTR("TCPKeepAlive"), options, service, &lval, 0);
958            if (lval) {
959                get_int_option(serv, kSCEntNetPPTP, CFSTR("TCPKeepAliveTimer"), options, service, &lval, 0);
960            }
961            else {
962                /* option doesn't exist, piggy-back on lcp echo option */
963                ppp_getoptval(serv, options, service, PPP_OPT_LCP_ECHO, &lval, sizeof(lval), &len);
964                lval = lval >> 16;
965            }
966            writeintparam(optfd, "pptp-tcp-keepalive", lval);
967            break;
968    }
969
970    // -----------------
971    // terminal option
972    if (ppp_getoptval(serv, options, service, PPP_OPT_COMM_TERMINALMODE, &lval, sizeof(lval), &len)) {
973
974        /* add the PPPSerial plugin if not already present
975         Fix me : terminal mode is only supported in PPPSerial types of connection
976         but subtype using ptys can use it the same way */
977        if (lval != PPP_COMM_TERM_NONE && serv->subtype != PPP_TYPE_SERIAL)
978            writestrparam(optfd, "plugin", "PPPSerial.ppp");
979
980        if (lval == PPP_COMM_TERM_WINDOW)
981            writeparam(optfd, "terminalwindow");
982        else if (lval == PPP_COMM_TERM_SCRIPT)
983            if (ppp_getoptval(serv, options, service, PPP_OPT_COMM_TERMINALSCRIPT, sopt, sizeof(sopt), &len) && sopt[0])
984                writestrparam(optfd, "terminalscript", (char*)sopt);
985    }
986
987    // -----------------
988    // generic phone number option
989    if (ppp_getoptval(serv, options, service, PPP_OPT_COMM_REMOTEADDR, sopt, sizeof(sopt), &len) && sopt[0])
990        writestrparam(optfd, "remoteaddress", (char*)sopt);
991
992    // -----------------
993    // redial options
994    get_int_option(serv, kSCEntNetPPP, kSCPropNetPPPCommRedialEnabled, options, service, &lval, 0);
995    if (lval) {
996
997        get_str_option(serv, kSCEntNetPPP, kSCPropNetPPPCommAlternateRemoteAddress, options, service, sopt, sizeof(sopt), &lval, empty_str);
998        if (sopt[0])
999            writestrparam(optfd, "altremoteaddress", (char*)sopt);
1000
1001        get_int_option(serv, kSCEntNetPPP, kSCPropNetPPPCommRedialCount, options, service, &lval, 0);
1002        if (lval)
1003            writeintparam(optfd, "redialcount", lval);
1004
1005        get_int_option(serv, kSCEntNetPPP, kSCPropNetPPPCommRedialInterval, options, service, &lval, 0);
1006        if (lval)
1007            writeintparam(optfd, "redialtimer", lval);
1008    }
1009
1010	awaketime = gSleeping ? 0 : ((mach_absolute_time() - gWakeUpTime) * gTimeScaleSeconds);
1011	if (awaketime < MAX_EXTRACONNECTTIME) {
1012        writeintparam(optfd, "extraconnecttime", MAX(MAX_EXTRACONNECTTIME - awaketime, MIN_EXTRACONNECTTIME));
1013	}
1014
1015	// -----------------
1016    // idle options
1017    if (ppp_getoptval(serv, options, service, PPP_OPT_COMM_IDLETIMER, &lval, sizeof(lval), &len) && lval) {
1018        writeintparam(optfd, "idle", lval);
1019        writeparam(optfd, "noidlerecv");
1020    }
1021
1022    // -----------------
1023    // connection time option
1024    if (ppp_getoptval(serv, options, service, PPP_OPT_COMM_SESSIONTIMER, &lval, sizeof(lval), &len) && lval)
1025        writeintparam(optfd, "maxconnect", lval);
1026
1027    // -----------------
1028    // dial on demand options
1029    if (onTraffic) {
1030        writeparam(optfd, "demand");
1031        get_int_option(serv, kSCEntNetPPP, CFSTR("HoldOffTime"), 0, service, &lval, 30);
1032        writeintparam(optfd, "holdoff", lval);
1033		if ((onTraffic & 0x2) && lval)
1034			writeparam(optfd, "holdfirst");
1035        get_int_option(serv, kSCEntNetPPP, CFSTR("MaxFailure"), 0, service, &lval, 3);
1036        writeintparam(optfd, "maxfail", lval);
1037    } else {
1038#if !TARGET_OS_EMBEDDED
1039        // if reconnecting, add option to wait for successful resolver
1040        if (serv->persist_connect) {
1041            writeintparam(optfd, "retrylinkcheck", 10);
1042        }
1043#endif
1044    }
1045
1046    // -----------------
1047    // lcp echo options
1048    // set echo option, so ppp hangup if we pull the modem cable
1049    // echo option is 2 bytes for interval + 2 bytes for failure
1050    if (ppp_getoptval(serv, options, service, PPP_OPT_LCP_ECHO, &lval, sizeof(lval), &len) && lval) {
1051        if (lval >> 16)
1052            writeintparam(optfd, "lcp-echo-interval", lval >> 16);
1053
1054        if (lval & 0xffff)
1055            writeintparam(optfd, "lcp-echo-failure", lval & 0xffff);
1056    }
1057
1058    // -----------------
1059    // address and protocol field compression options
1060    if (ppp_getoptval(serv, options, service, PPP_OPT_LCP_HDRCOMP, &lval, sizeof(lval), &len)) {
1061        if (!(lval & 1))
1062            writeparam(optfd, "nopcomp");
1063        if (!(lval & 2))
1064            writeparam(optfd, "noaccomp");
1065    }
1066
1067    // -----------------
1068    // mru option
1069    if (ppp_getoptval(serv, options, service, PPP_OPT_LCP_MRU, &lval, sizeof(lval), &len) && lval)
1070        writeintparam(optfd, "mru", lval);
1071
1072    // -----------------
1073    // mtu option
1074    if (ppp_getoptval(serv, options, service, PPP_OPT_LCP_MTU, &lval, sizeof(lval), &len) && lval)
1075        writeintparam(optfd, "mtu", lval);
1076
1077    // -----------------
1078    // receive async map option
1079    if (ppp_getoptval(serv, options, service, PPP_OPT_LCP_RCACCM, &lval, sizeof(lval), &len)) {
1080        if (lval)
1081			writeintparam(optfd, "asyncmap", lval);
1082		else
1083			writeparam(optfd, "receive-all");
1084	}
1085	else
1086		writeparam(optfd, "default-asyncmap");
1087
1088    // -----------------
1089    // send async map option
1090     if (ppp_getoptval(serv, options, service, PPP_OPT_LCP_TXACCM, &lval, sizeof(lval), &len) && lval) {
1091            writeparam(optfd, "escape");
1092            str[0] = 0;
1093            for (lval1 = 0; lval1 < 32; lval1++) {
1094                if ((lval >> lval1) & 1) {
1095                    snprintf(str2, sizeof(str2), "%d,", lval1);
1096                    strlcat(str, str2, sizeof(str));
1097               }
1098            }
1099            str[strlen(str)-1] = 0; // remove last ','
1100            writeparam(optfd, str);
1101       }
1102
1103    // -----------------
1104    // ipcp options
1105	if (!CFDictionaryContainsKey(service, kSCEntNetIPv4)) {
1106        writeparam(optfd, "noip");
1107    }
1108    else {
1109
1110        // -----------------
1111        // set ip param to be the router address
1112        if (getStringFromEntity(gDynamicStore, kSCDynamicStoreDomainState, 0,
1113            kSCEntNetIPv4, kSCPropNetIPv4Router, sopt, OPT_STR_LEN) && sopt[0])
1114            writestrparam(optfd, "ipparam", (char*)sopt);
1115
1116        // OverridePrimary option not handled yet in Setup by IPMonitor
1117        get_int_option(serv, kSCEntNetIPv4, kSCPropNetOverridePrimary, 0 /* don't look in options */, service, &lval, 0);
1118        if (lval) {
1119			overrideprimary = 1;
1120            writeparam(optfd, "defaultroute");
1121		}
1122
1123        // -----------------
1124        // vj compression option
1125        if (! (ppp_getoptval(serv, options, service, PPP_OPT_IPCP_HDRCOMP, &lval, sizeof(lval), &len) && lval))
1126            writeparam(optfd, "novj");
1127
1128        // -----------------
1129        // XXX  enforce the source address
1130        if (serv->subtype == PPP_TYPE_L2TP || serv->subtype == PPP_TYPE_PPTP ) {
1131            writeintparam(optfd, "ip-src-address-filter", 2);
1132        }
1133
1134        // -----------------
1135        // ip addresses options
1136        if (ppp_getoptval(serv, options, service, PPP_OPT_IPCP_LOCALADDR, &lval, sizeof(lval), &len) && lval)
1137            snprintf(str2, sizeof(str2), "%d.%d.%d.%d", lval >> 24, (lval >> 16) & 0xFF, (lval >> 8) & 0xFF, lval & 0xFF);
1138        else
1139            strlcpy(str2, "0", sizeof(str2));
1140
1141        strlcpy(str, str2, sizeof(str));
1142        strlcat(str, ":", sizeof(str));
1143        if (ppp_getoptval(serv, options, service, PPP_OPT_IPCP_REMOTEADDR, &lval, sizeof(lval), &len) && lval)
1144            snprintf(str2, sizeof(str2), "%d.%d.%d.%d", lval >> 24, (lval >> 16) & 0xFF, (lval >> 8) & 0xFF, lval & 0xFF);
1145        else
1146            strlcpy(str2, "0", sizeof(str2));
1147        strlcat(str, str2, sizeof(str));
1148        writeparam(optfd, str);
1149
1150        writeparam(optfd, "noipdefault");
1151        writeparam(optfd, "ipcp-accept-local");
1152        writeparam(optfd, "ipcp-accept-remote");
1153
1154
1155    /* ************************************************************************* */
1156
1157        // usepeerdns option
1158		get_int_option(serv, kSCEntNetPPP, CFSTR("IPCPUsePeerDNS"), options, service, &lval, 1);
1159        if (lval)
1160            writeparam(optfd, "usepeerdns");
1161
1162		// usepeerwins if a SMB dictionary is present
1163		// but make sure it is not disabled in PPP
1164#if !TARGET_OS_EMBEDDED
1165		if (CFDictionaryContainsKey(service, kSCEntNetSMB)) {
1166			get_int_option(serv, kSCEntNetPPP, CFSTR("IPCPUsePeerWINS"), options, service, &lval, 1);
1167			if (lval)
1168				writeparam(optfd, "usepeerwins");
1169		}
1170#endif
1171
1172		// -----------------
1173		// add a route for the interface subnet, if L2TP or PPTP (with encryption) VPN enabled
1174
1175		switch (serv->subtype) {
1176			case PPP_TYPE_L2TP:
1177				writeparam(optfd, "addifroute");
1178				break;
1179
1180			case PPP_TYPE_PPTP:
1181				if(ccp_enabled)
1182					writeparam(optfd, "addifroute");
1183				break;
1184
1185			default:
1186				break;
1187		}
1188    } // of existEntity IPv4
1189
1190    // -----------------
1191    // ip6cp options
1192	if (!CFDictionaryContainsKey(service, kSCEntNetIPv6)) {
1193        // ipv6 is not started by default
1194    }
1195    else {
1196        writeparam(optfd, "+ipv6");
1197        writeparam(optfd, "ipv6cp-use-persistent");
1198    }
1199
1200	// -----------------
1201	// acsp options and DHCP options
1202
1203	if (overrideprimary) {
1204		// acsp and dhcp not need when all traffic is sent over PPP
1205		writeparam(optfd, "noacsp");
1206		writeparam(optfd, "no-use-dhcp");
1207	}
1208	else {
1209		// acsp options
1210		get_int_option(serv, kSCEntNetPPP, kSCPropNetPPPACSPEnabled, options, service, &lval, 0);
1211		if (lval == 0)
1212			writeparam(optfd, "noacsp");
1213
1214		// dhcp is on by default for vpn, and off for everything else
1215		get_int_option(serv, kSCEntNetPPP, CFSTR("UseDHCP"), options, service, &lval,  (serv->subtype == PPP_TYPE_L2TP || serv->subtype == PPP_TYPE_PPTP) ? 1 : 0);
1216		if (lval == 1)
1217			writeparam(optfd, "use-dhcp");
1218	}
1219
1220    // -----------------
1221    // authentication options
1222
1223    // don't want authentication on our side...
1224    writeparam(optfd, "noauth");
1225
1226     if (ppp_getoptval(serv, options, service, PPP_OPT_AUTH_PROTO, &lval, sizeof(lval), &len) && (lval != PPP_AUTH_NONE)) {
1227
1228		CFStringRef			encryption = NULL;
1229
1230		if (ppp_getoptval(serv, options, service, PPP_OPT_AUTH_NAME, sopt, sizeof(sopt), &len) && sopt[0]) {
1231
1232
1233            writestrparam(optfd, "user", (char*)sopt);
1234			needpasswd = 1;
1235
1236            lval1 = get_str_option(serv, kSCEntNetPPP, kSCPropNetPPPAuthPassword, options, service, sopt, sizeof(sopt), &lval, empty_str);
1237            if (sopt[0]) {
1238
1239                /* get the encryption method at the same place the password is coming from. */
1240				encryption = get_cf_option(kSCEntNetPPP, kSCPropNetPPPAuthPasswordEncryption, CFStringGetTypeID(),
1241					(lval1 == 3) ? NULL : options, (lval1 == 3) ? service : NULL , NULL);
1242
1243				if (encryption && (CFStringCompare(encryption, kSCValNetPPPAuthPasswordEncryptionKeychain, 0) == kCFCompareEqualTo)) {
1244					writestrparam(optfd, (lval1 == 3) ? "keychainpassword" : "userkeychainpassword", (char*)sopt);
1245				}
1246				else if (encryption && (CFStringCompare(encryption, kSCValNetPPPAuthPasswordEncryptionToken, 0) == kCFCompareEqualTo)) {
1247					writeintparam(optfd, "tokencard", 1);
1248					tokendone = 1;
1249				}
1250				else {
1251					CFStringRef aString = CFStringCreateWithCString(NULL, (char*)sopt, kCFStringEncodingUTF8);
1252					if (aString) {
1253						CFStringGetCString(aString, (char*)sopt, OPT_STR_LEN, kCFStringEncodingWindowsLatin1);
1254						CFRelease(aString);
1255					}
1256					writestrparam(optfd, "password", (char*)sopt);
1257				}
1258            }
1259            else {
1260				encryption = get_cf_option(kSCEntNetPPP, kSCPropNetPPPAuthPasswordEncryption, CFStringGetTypeID(), options, service, NULL);
1261				if (encryption && (CFStringCompare(encryption, kSCValNetPPPAuthPasswordEncryptionToken, 0) == kCFCompareEqualTo)) {
1262					writeintparam(optfd, "tokencard", 1);
1263					tokendone = 1;
1264				}
1265            }
1266        }
1267		else {
1268			encryption = get_cf_option(kSCEntNetPPP, kSCPropNetPPPAuthPasswordEncryption, CFStringGetTypeID(), options, service, NULL);
1269			if (encryption && (CFStringCompare(encryption, kSCValNetPPPAuthPasswordEncryptionToken, 0) == kCFCompareEqualTo)) {
1270				writeintparam(optfd, "tokencard", 1);
1271				tokendone = 1;
1272				needpasswd = 1;
1273			}
1274			else {
1275				// keep the same behavior for modems.
1276				// prompt for username + password for VPN
1277				if (serv->subtype == PPP_TYPE_L2TP || serv->subtype == PPP_TYPE_PPTP) {
1278					needpasswd = 1;
1279				}
1280			}
1281		}
1282    }
1283
1284    // load authentication protocols
1285    array = 0;
1286    if (options) {
1287        dict = CFDictionaryGetValue(options, kSCEntNetPPP);
1288        if (dict && (CFGetTypeID(dict) == CFDictionaryGetTypeID()))
1289            array = CFDictionaryGetValue(dict, kSCPropNetPPPAuthProtocol);
1290    }
1291    if ((array == 0) || (CFGetTypeID(array) != CFArrayGetTypeID())) {
1292        array = CFDictionaryGetValue(pppdict, kSCPropNetPPPAuthProtocol);
1293    }
1294    if (array && (CFGetTypeID(array) == CFArrayGetTypeID()) && (lval = CFArrayGetCount(array))) {
1295        auth_default = 0;
1296        auth_bits = 0; // clear bits
1297        for (i = 0; i < lval; i++) {
1298            string = CFArrayGetValueAtIndex(array, i);
1299            if (string && (CFGetTypeID(string) == CFStringGetTypeID())) {
1300                if (CFStringCompare(string, kSCValNetPPPAuthProtocolPAP, 0) == kCFCompareEqualTo)
1301                    auth_bits |= 1;
1302                else if (CFStringCompare(string, kSCValNetPPPAuthProtocolCHAP, 0) == kCFCompareEqualTo)
1303                    auth_bits |= 2;
1304                else if (CFStringCompare(string, CFSTR("MSCHAP1") /*kSCValNetPPPAuthProtocolMSCHAP1*/ , 0) == kCFCompareEqualTo)
1305                    auth_bits |= 4;
1306                else if (CFStringCompare(string,  CFSTR("MSCHAP2") /*kSCValNetPPPAuthProtocolMSCHAP2*/, 0) == kCFCompareEqualTo)
1307                    auth_bits |= 8;
1308                else if (CFStringCompare(string,  CFSTR("EAP") /*kSCValNetPPPAuthProtocolEAP*/, 0) == kCFCompareEqualTo)
1309                    auth_bits |= 0x10;
1310            }
1311        }
1312    }
1313
1314
1315
1316	if (!tokendone) {
1317		// authentication variation for token card support...
1318		get_int_option(serv, kSCEntNetPPP, CFSTR("TokenCard"), options, service, &lval, 0);
1319		if (lval) {
1320			writeintparam(optfd, "tokencard", lval);
1321			needpasswd = 1;
1322		}
1323	}
1324
1325    // -----------------
1326    // eap options
1327
1328    if (auth_bits & 0x10) {
1329        auth_bits &= ~0x10; // clear EAP flag
1330        array = 0;
1331        from_service = 0;
1332        if (options) {
1333            dict = CFDictionaryGetValue(options, kSCEntNetPPP);
1334            if (dict && (CFGetTypeID(dict) == CFDictionaryGetTypeID()))
1335                array = CFDictionaryGetValue(dict, CFSTR("AuthEAPPlugins") /*kSCPropNetPPPAuthEAPPlugins*/);
1336        }
1337        if ((array == 0) || (CFGetTypeID(array) != CFArrayGetTypeID())) {
1338            array = CFDictionaryGetValue(pppdict, CFSTR("AuthEAPPlugins") /*kSCPropNetPPPAuthEAPPlugins*/);
1339            from_service = 1;
1340        }
1341        if (array && (CFGetTypeID(array) == CFArrayGetTypeID()) && (lval = CFArrayGetCount(array))) {
1342            for (i = 0; i < lval; i++) {
1343                string = CFArrayGetValueAtIndex(array, i);
1344                if (string && (CFGetTypeID(string) == CFStringGetTypeID())) {
1345                    CFStringGetCString(string, str, sizeof(str) - 4, kCFStringEncodingUTF8);
1346                    // for user options, we only accept plugin in the EAP directory (/System/Library/Extensions)
1347                    if (from_service || strchr(str, '\\') == 0) {
1348                        strlcat(str, ".ppp", sizeof(str));	// add plugin suffix
1349                        writestrparam(optfd, "eapplugin", str);
1350                        auth_bits |= 0x10; // confirm EAP flag
1351                    }
1352                }
1353            }
1354        }
1355    }
1356
1357    // -----------------
1358    // ccp options
1359	if(ccp_enabled &&
1360        // Fix me : to enforce use of MS-CHAP, refuse any alteration of default auth proto
1361        // a dialer specifying PAP or CHAP will work without CCP/MPPE
1362        // even is CCP is enabled in the configuration.
1363        // Will be revisited when addition compression modules and
1364        // authentication modules will be added
1365	   ppp_getoptval(serv, options, service, PPP_OPT_AUTH_PROTO, &lval, sizeof(lval), &len)
1366                && (lval == OPT_AUTH_PROTO_DEF)) {
1367
1368        // Fix me : mppe is the only currently supported compression
1369        // if the CCPAccepted and CCPRequired array are not there,
1370        // assume we accept all types of compression we support
1371
1372        writeparam(optfd, "mppe-stateless");
1373		get_int_option(serv, kSCEntNetPPP, CFSTR("CCPMPPE128Enabled"), options, service, &lval, 1);
1374		writeparam(optfd, lval ? "mppe-128" : "nomppe-128");
1375		get_int_option(serv, kSCEntNetPPP, CFSTR("CCPMPPE40Enabled"), options, service, &lval, 1);
1376        writeparam(optfd, lval ? "mppe-40" : "nomppe-40");
1377
1378        // No authentication specified, also enforce the use of MS-CHAP
1379        if (auth_default)
1380            auth_bits = 0xc; /* MSCHAP 1 and 2 only */
1381    }
1382    else {
1383        // no compression protocol
1384        writeparam(optfd, "noccp");
1385    }
1386
1387    // set authentication protocols parameters
1388    if ((auth_bits & 1) == 0)
1389        writeparam(optfd, "refuse-pap");
1390    if ((auth_bits & 2) == 0)
1391        writeparam(optfd, "refuse-chap-md5");
1392    if ((auth_bits & 4) == 0)
1393        writeparam(optfd, "refuse-mschap");
1394    if ((auth_bits & 8) == 0)
1395        writeparam(optfd, "refuse-mschap-v2");
1396    if ((auth_bits & 0x10) == 0)
1397        writeparam(optfd, "refuse-eap");
1398
1399    // if EAP is the only method, pppd doesn't need to ask for the password
1400    // let the EAP plugin handle that.
1401    // if there is an other protocol than EAP, then we still need to prompt for password
1402    if (auth_bits == 0x10)
1403        needpasswd = 0;
1404
1405    // loop local traffic destined to the local ip address
1406    // Radar #3124639.
1407    //writeparam(optfd, "looplocal");
1408
1409#if !TARGET_OS_EMBEDDED
1410    if (!(serv->flags & FLAG_ALERTPASSWORDS) || !needpasswd || serv->flags & FLAG_DARKWAKE)
1411#else
1412    if (!(serv->flags & FLAG_ALERTPASSWORDS) || !needpasswd)
1413#endif
1414        writeparam(optfd, "noaskpassword");
1415
1416    get_str_option(serv, kSCEntNetPPP, kSCPropNetPPPAuthPrompt, options, service, sopt, sizeof(sopt), &lval, empty_str);
1417    if (sopt[0]) {
1418        str2[0] = 0;
1419        CFStringGetCString(kSCValNetPPPAuthPromptAfter, str2, sizeof(str2), kCFStringEncodingUTF8);
1420        if (!strcmp((char *)sopt, str2))
1421            writeparam(optfd, "askpasswordafter");
1422    }
1423
1424    // -----------------
1425    // no need for pppd to detach.
1426    writeparam(optfd, "nodetach");
1427
1428    // -----------------
1429    // reminder option must be specified after PPPDialogs plugin option
1430    get_int_option(serv, kSCEntNetPPP, kSCPropNetPPPIdleReminder, options, service, &lval, 0);
1431    if (lval) {
1432        get_int_option(serv, kSCEntNetPPP, kSCPropNetPPPIdleReminderTimer, options, service, &lval, 0);
1433        if (lval)
1434            writeintparam(optfd, "reminder", lval);
1435    }
1436
1437    // -----------------
1438    // add any additional plugin we want to load
1439    array = CFDictionaryGetValue(pppdict, kSCPropNetPPPPlugins);
1440    if (array && (CFGetTypeID(array) == CFArrayGetTypeID())) {
1441        lval = CFArrayGetCount(array);
1442        for (i = 0; i < lval; i++) {
1443            string = CFArrayGetValueAtIndex(array, i);
1444            if (string && (CFGetTypeID(string) == CFStringGetTypeID())) {
1445                CFStringGetCString(string, str, sizeof(str) - 4, kCFStringEncodingUTF8);
1446                strlcat(str, ".ppp", sizeof(str));	// add plugin suffix
1447                writestrparam(optfd, "plugin", str);
1448            }
1449        }
1450    }
1451
1452    // -----------------
1453     // always try to use options defined in /etc/ppp/peers/[service provider]
1454    // they can override what have been specified by the PPPController
1455    // look first in ppp dictionary, then in service
1456	if (GetStrFromDict(pppdict, kSCPropUserDefinedName, (char*)sopt, OPT_STR_LEN, empty_str_s)
1457		|| GetStrFromDict(service, kSCPropUserDefinedName, (char*)sopt, OPT_STR_LEN, empty_str_s))
1458        writestrparam(optfd, "call", (char*)sopt);
1459
1460    writeparam(optfd, "[EOP]");
1461
1462    return 0;
1463}
1464
1465/* -----------------------------------------------------------------------------
1466----------------------------------------------------------------------------- */
1467static
1468int change_pppd_params(struct service *serv, CFDictionaryRef service, CFDictionaryRef options)
1469{
1470    int 			optfd;
1471    u_int32_t			lval, len;
1472    CFDictionaryRef		pppdict = NULL;
1473
1474    pppdict = CFDictionaryGetValue(service, kSCEntNetPPP);
1475    if ((pppdict == 0) || (CFGetTypeID(pppdict) != CFDictionaryGetTypeID()))
1476        return -1; 	// that's bad...
1477
1478    optfd = serv->u.ppp.controlfd[WRITE];
1479
1480    writeparam(optfd, "[OPTIONS]");
1481
1482    // -----------------
1483    // reminder option must be specified after PPPDialogs plugin option
1484    get_int_option(serv, kSCEntNetPPP, kSCPropNetPPPIdleReminder, options, service, &lval, 0);
1485    if (lval)
1486        get_int_option(serv, kSCEntNetPPP, kSCPropNetPPPIdleReminderTimer, options, service, &lval, 0);
1487    writeintparam(optfd, "reminder", lval);
1488
1489    // -----------------
1490    ppp_getoptval(serv, options, service, PPP_OPT_COMM_IDLETIMER, &lval, sizeof(lval), &len);
1491    writeintparam(optfd, "idle", lval);
1492
1493    // Scoped interface
1494    char outgoingInterfaceString[IFXNAMSIZ];
1495    if (options && GetStrFromDict(options, CFSTR(NESessionStartOptionOutgoingInterface), outgoingInterfaceString, IFXNAMSIZ, "")) {
1496        writestrparam(optfd, "ifscope", outgoingInterfaceString);
1497    }
1498
1499    writeparam(optfd, "[EOP]");
1500
1501    return 0;
1502}
1503
1504int ppp_install(struct service *serv)
1505{
1506	int optfd;
1507
1508	optfd = serv->u.ppp.controlfd[WRITE];
1509
1510	writeparam(optfd, "[INSTALL]");
1511	return 0;
1512}
1513
1514int ppp_uninstall(struct service *serv)
1515{
1516	int optfd;
1517
1518	optfd = serv->u.ppp.controlfd[WRITE];
1519
1520	writeparam(optfd, "[UNINSTALL]");
1521	return 0;
1522}
1523
1524/* -----------------------------------------------------------------------------
1525----------------------------------------------------------------------------- */
1526int ppp_start(struct service *serv, CFDictionaryRef options, uid_t uid, gid_t gid, mach_port_t bootstrap, mach_port_t au_session, u_int8_t onTraffic, u_int8_t onDemand)
1527{
1528#define MAXARG 10
1529	char 			*cmdarg[MAXARG];
1530	u_int32_t		i, argi = 0;
1531	CFDictionaryRef		service;
1532	CFDictionaryRef		environmentVars = NULL;
1533	int yes = 1;
1534
1535	// reset setup flag
1536	serv->flags &= ~FLAG_CONFIGCHANGEDNOW;
1537	serv->flags &= ~FLAG_CONFIGCHANGEDLATER;
1538
1539	// reset autodial flag;
1540	serv->flags &= ~FLAG_FIRSTDIAL;
1541
1542	switch (serv->u.ppp.phase) {
1543		case PPP_IDLE:
1544			break;
1545
1546		case PPP_DORMANT:	// kill dormant process and post connection flag
1547		case PPP_HOLDOFF:
1548			my_CFRelease(&serv->u.ppp.newconnectopts);
1549			serv->u.ppp.newconnectopts = options;
1550			serv->u.ppp.newconnectuid = uid;
1551			serv->u.ppp.newconnectgid = gid;
1552			serv->u.ppp.newconnectbootstrap = bootstrap;
1553			serv->u.ppp.newconnectausession = au_session;
1554			my_CFRetain(serv->u.ppp.newconnectopts);
1555
1556			scnc_stop(serv, 0, SIGTERM, SCNC_STOP_NONE);
1557			serv->flags |= FLAG_CONNECT;
1558			return 0;
1559
1560		default:
1561			if (my_CFEqual(options, serv->connectopts)) {
1562				// notify client, so at least then can get the status if they were waiting got it
1563				phase_changed(serv, serv->u.ppp.phase);
1564				return 0;
1565			}
1566			return EIO;	// not the right time to dial
1567	}
1568
1569#if DEBUG
1570	scnc_log(LOG_DEBUG, CFSTR("PPP Controller: VPN System Prefs %@"), serv->systemprefs);
1571	scnc_log(LOG_DEBUG, CFSTR("PPP Controller: VPN User Options %@"), options);
1572#endif
1573
1574	/* remove any pending notification */
1575	if (serv->userNotificationRef) {
1576		CFUserNotificationCancel(serv->userNotificationRef);
1577		CFRunLoopRemoveSource(CFRunLoopGetCurrent(), serv->userNotificationRLS, kCFRunLoopDefaultMode);
1578		my_CFRelease(&serv->userNotificationRef);
1579		my_CFRelease(&serv->userNotificationRLS);
1580	}
1581
1582	serv->u.ppp.laststatus =  EXIT_FATAL_ERROR;
1583    serv->u.ppp.lastdevstatus = 0;
1584
1585	if (serv->ne_sm_bridge != NULL) {
1586		service = ne_sm_bridge_copy_configuration(serv->ne_sm_bridge);
1587	} else {
1588		service = copyService(gDynamicStore, kSCDynamicStoreDomainSetup, serv->serviceID);
1589	}
1590    if (!service)
1591        goto end;	// that's bad...
1592
1593    // create arguments and fork pppd
1594    for (i = 0; i < MAXARG; i++)
1595        cmdarg[i] = 0;
1596    addparam(cmdarg, &argi, PPPD_PRGM);
1597    addparam(cmdarg, &argi, "serviceid");
1598    addparam(cmdarg, &argi, (char*)serv->sid);
1599    addparam(cmdarg, &argi, "controlled");
1600
1601    if ((socketpair(AF_LOCAL, SOCK_STREAM, 0, serv->u.ppp.controlfd) == -1)
1602		|| (socketpair(AF_LOCAL, SOCK_STREAM, 0, serv->u.ppp.statusfd) == -1))
1603        goto end;
1604
1605    if (setsockopt(serv->u.ppp.controlfd[WRITE], SOL_SOCKET, SO_NOSIGPIPE, &yes, sizeof(yes)) == -1) {
1606        goto end;
1607    }
1608
1609    if (onDemand)
1610        serv->flags |= FLAG_ONDEMAND;
1611	else
1612		serv->flags &= ~FLAG_ONDEMAND;
1613
1614    serv->uid = uid;
1615    serv->gid = gid;
1616
1617    scnc_bootstrap_retain(serv, bootstrap);
1618    scnc_ausession_retain(serv, au_session);
1619
1620	if (serv->ne_sm_bridge != NULL) {
1621		CFDictionaryRef vars = CFDictionaryGetValue(service, CFSTR("EnvironmentVariables"));
1622		if (vars) {
1623			environmentVars = CFRetain(vars);
1624		}
1625	} else {
1626		environmentVars = collectEnvironmentVariables(gDynamicStore, serv->serviceID);
1627	}
1628
1629    if (environmentVars) {
1630        extractEnvironmentVariables(environmentVars, serv);
1631        CFRelease(environmentVars);
1632    }
1633
1634    serv->u.ppp.pid = SCNCPluginExecCommand2(NULL,
1635					     exec_callback,
1636					     (void*)(uintptr_t)makeref(serv),
1637					     geteuid(),
1638					     getegid(),
1639					     PATH_PPPD,
1640					     cmdarg,
1641					     exec_postfork,
1642					     (void*)(uintptr_t)makeref(serv));
1643    if (serv->u.ppp.pid == -1)
1644        goto end;
1645
1646    // send options to pppd using the pipe
1647    if (send_pppd_params(serv, service, options, onTraffic) == -1) {
1648        kill(serv->u.ppp.pid, SIGTERM);
1649        goto end;
1650    }
1651
1652    // all options have been sent, close the pipe.
1653    //my_close(ppp->controlfd[WRITE]);
1654    //ppp->controlfd[WRITE] = -1;
1655
1656    // add the pipe to runloop
1657    ppp_socket_create_client(serv->u.ppp.statusfd[READ], 1, 0, 0);
1658
1659    serv->u.ppp.laststatus = EXIT_OK;
1660    ppp_updatephase(serv, PPP_INITIALIZE, 0);
1661	serv->was_running = 0;
1662	service_started(serv);
1663
1664    if (onTraffic)
1665        serv->flags |= FLAG_ONTRAFFIC;
1666	else
1667		serv->flags &= ~FLAG_ONTRAFFIC;
1668
1669    serv->connectopts = options;
1670    my_CFRetain(serv->connectopts);
1671    TRACK_VPN_LOCATION(serv);
1672
1673end:
1674
1675    if (service)
1676        CFRelease(service);
1677
1678    for (i = 0; i < argi; i++)
1679        free(cmdarg[i]);
1680
1681    if (serv->u.ppp.pid == -1) {
1682
1683        my_close(serv->u.ppp.statusfd[READ]);
1684        serv->u.ppp.statusfd[READ] = -1;
1685        my_close(serv->u.ppp.statusfd[WRITE]);
1686        serv->u.ppp.statusfd[WRITE] = -1;
1687        my_close(serv->u.ppp.controlfd[READ]);
1688        serv->u.ppp.controlfd[READ] = -1;
1689        my_close(serv->u.ppp.controlfd[WRITE]);
1690        serv->u.ppp.controlfd[WRITE] = -1;
1691
1692        display_error(serv);
1693    }
1694
1695    return serv->u.ppp.laststatus;
1696}
1697
1698/* -----------------------------------------------------------------------------
1699----------------------------------------------------------------------------- */
1700static void
1701exec_postfork(pid_t pid, void *arg)
1702{
1703    struct service 	*serv = findbyref(TYPE_PPP, (u_int32_t)(uintptr_t)arg);
1704
1705    if (pid) {
1706        /* if parent */
1707
1708        int	yes	= 1;
1709
1710        my_close(serv->u.ppp.controlfd[READ]);
1711        serv->u.ppp.controlfd[READ] = -1;
1712        my_close(serv->u.ppp.statusfd[WRITE]);
1713        serv->u.ppp.statusfd[WRITE] = -1;
1714        if (ioctl(serv->u.ppp.controlfd[WRITE], FIONBIO, &yes) == -1) {
1715//           printf("ioctl(,FIONBIO,): %s\n", strerror(errno));
1716        }
1717
1718    } else {
1719        /* if child */
1720
1721        uid_t	euid;
1722        int	i;
1723
1724        my_close(serv->u.ppp.controlfd[WRITE]);
1725        serv->u.ppp.controlfd[WRITE] = -1;
1726        my_close(serv->u.ppp.statusfd[READ]);
1727        serv->u.ppp.statusfd[READ] = -1;
1728
1729        if (serv->u.ppp.controlfd[READ] != STDIN_FILENO) {
1730            dup2(serv->u.ppp.controlfd[READ], STDIN_FILENO);
1731        }
1732
1733        if (serv->u.ppp.statusfd[WRITE] != STDOUT_FILENO) {
1734            dup2(serv->u.ppp.statusfd[WRITE], STDOUT_FILENO);
1735        }
1736
1737        close(STDERR_FILENO);
1738        open(_PATH_DEVNULL, O_RDWR, 0);
1739
1740        /* close any other open FDs */
1741        for (i = getdtablesize() - 1; i > STDERR_FILENO; i--) close(i);
1742
1743        /* Careful here and with the gid/uid params passed to _SCDPluginExecCommand2()
1744         * so that the uid/gid setup code in _SCDPluginExecCommand would be skipped
1745         * after this callback function returns. We want this function to be in
1746         * full control of UID and GID settings.
1747         */
1748        euid = geteuid();
1749        if (euid != serv->uid) {
1750            // only set real (and not effective) UID for pppd which needs to have
1751            // its euid to be root so as to toggle between root and user to access
1752            // system and user keychain (e.g., SSL certificate private key in system
1753            // keychain and user password in user keychain).
1754            (void) setruid(serv->uid);
1755        }
1756
1757        applyEnvironmentVariables(serv);
1758    }
1759
1760    return;
1761}
1762
1763static Boolean
1764ppp_persist_connection_exec_callback (struct service *serv, int exitcode)
1765{
1766    Boolean pppRestart = FALSE;
1767
1768#if !TARGET_OS_EMBEDDED
1769	if (serv->persist_connect) {
1770		if (serv->persist_connect_status ||
1771			serv->persist_connect_devstatus ||
1772			((serv->u.ppp.laststatus && serv->u.ppp.laststatus != EXIT_USER_REQUEST && serv->u.ppp.laststatus != EXIT_FATAL_ERROR) || serv->u.ppp.lastdevstatus) ||
1773			((exitcode == EXIT_HANGUP || exitcode == EXIT_PEER_DEAD) && serv->u.ppp.laststatus != EXIT_USER_REQUEST && serv->u.ppp.laststatus != EXIT_FATAL_ERROR)) {
1774
1775			scnc_log(LOG_ERR, CFSTR("PPP Controller: disconnected with status  %d.%d. Will try reconnect shortly."),
1776				  serv->persist_connect_status? serv->persist_connect_status: serv->u.ppp.laststatus,
1777				  serv->persist_connect_devstatus? serv->persist_connect_devstatus : serv->u.ppp.lastdevstatus);
1778
1779			scnc_log(LOG_ERR, CFSTR("PPP Controller: reconnecting"));
1780			// start over
1781			SESSIONTRACERSTOP(serv);
1782			my_CFRelease(&serv->connection_nid);
1783			my_CFRelease(&serv->connection_nap);
1784			STOP_TRACKING_VPN_LOCATION(serv);
1785			serv->u.ppp.laststatus = 0;
1786			serv->u.ppp.lastdevstatus = 0;
1787			pppRestart = TRUE;
1788			ppp_start(serv, serv->persist_connect_opts, serv->uid, serv->gid, serv->bootstrap, serv->au_session, 0, (serv->flags & FLAG_ONDEMAND));
1789		} else if (serv->u.ppp.laststatus != EXIT_USER_REQUEST && serv->u.ppp.laststatus != EXIT_FATAL_ERROR) {
1790			serv->flags |= FLAG_ALERTERRORS;
1791			display_error(serv);
1792			serv->u.ppp.laststatus = 0;
1793			serv->u.ppp.lastdevstatus = 0;
1794		}
1795		my_CFRelease(&serv->persist_connect_opts);
1796		serv->persist_connect = 0;
1797		serv->persist_connect_status = 0;
1798		serv->persist_connect_devstatus = 0;
1799	}
1800#endif
1801    return pppRestart;
1802}
1803
1804/* -----------------------------------------------------------------------------
1805----------------------------------------------------------------------------- */
1806static
1807void exec_callback(pid_t pid, int status, struct rusage *rusage, void *context)
1808{
1809    struct service 	*serv = findbyref(TYPE_PPP, (u_int32_t)(uintptr_t)context);
1810    Boolean pppRestart = FALSE;
1811
1812	if (serv == NULL)
1813		return;
1814
1815	u_int32_t	failed = 0;
1816	int exitcode = WIFEXITED(status) ? WEXITSTATUS(status) : -1;
1817
1818	if (exitcode < 0) {
1819		// ignore this case, just change phase
1820	}
1821	else if (exitcode > EXIT_PEER_UNREACHABLE) {
1822		// pppd exited because of a crash
1823		ppp_updatestatus(serv, 127, 0);
1824	}
1825	else if (serv->u.ppp.phase == PPP_INITIALIZE) {
1826        // an error occured and status has not been updated
1827        // happens for example when an error is encountered while parsing arguments
1828		failed = 1;
1829        ppp_updatestatus(serv, exitcode, 0);
1830    }
1831
1832    // call the change phae function
1833    ppp_updatephase(serv, PPP_IDLE, 0);
1834	serv->was_running = 0;
1835	service_ended(serv);
1836
1837    // close file descriptors
1838    //statusfd is closed by the run loop
1839    //my_close(ppp->statusfd[READ]);
1840    serv->u.ppp.statusfd[READ] = -1;
1841    my_close(serv->u.ppp.controlfd[WRITE]);
1842    serv->u.ppp.controlfd[WRITE] = -1;
1843    my_CFRelease(&serv->connectopts);
1844    serv->connectopts = 0;
1845
1846	/* clean up dynamic store */
1847	cleanup_dynamicstore((void*)serv);
1848
1849	if (serv->ne_sm_bridge != NULL) {
1850		ne_sm_bridge_acknowledge_sleep(serv->ne_sm_bridge);
1851	} else {
1852		allow_sleep();
1853	}
1854
1855	if (allow_dispose(serv))
1856		serv = 0;
1857
1858    if (serv == 0)
1859        return;
1860
1861    // check if configd is going away
1862    if (allow_stop())
1863		return;
1864
1865    // now reconnect if necessary
1866
1867    if (serv->flags & FLAG_CONNECT) {
1868        pppRestart = TRUE;
1869        ppp_start(serv, serv->u.ppp.newconnectopts, serv->u.ppp.newconnectuid, serv->u.ppp.newconnectgid, serv->u.ppp.newconnectbootstrap, serv->u.ppp.newconnectausession, 0, 0);
1870        my_CFRelease(&serv->u.ppp.newconnectopts);
1871        serv->u.ppp.newconnectopts = 0;
1872        serv->u.ppp.newconnectuid = 0;
1873        serv->u.ppp.newconnectgid = 0;
1874        serv->u.ppp.newconnectbootstrap = 0;
1875        serv->u.ppp.newconnectausession = 0;
1876        serv->flags &= ~FLAG_CONNECT;
1877    }
1878    else {
1879        // if config has changed, or ppp was previously a manual connection, then rearm onTraffic if necessary
1880        if (failed == 0
1881	    && ((serv->flags & (FLAG_CONFIGCHANGEDNOW + FLAG_CONFIGCHANGEDLATER)) || !(serv->flags & FLAG_ONTRAFFIC))
1882	    && ((serv->flags & FLAG_SETUP_ONTRAFFIC) && (!(serv->flags & FLAG_SETUP_DISCONNECTONLOGOUT)|| gLoggedInUser))) {
1883            pppRestart = TRUE;
1884            ppp_start(serv, 0, 0, 0, serv->bootstrap, serv->au_session, serv->flags & FLAG_CONFIGCHANGEDNOW ? 1 : 3, 0);
1885       } else {
1886            pppRestart = ppp_persist_connection_exec_callback(serv, exitcode);
1887       }
1888    }
1889
1890    if (!pppRestart) {
1891        scnc_bootstrap_dealloc(serv);
1892        scnc_ausession_dealloc(serv);
1893    }
1894}
1895
1896/* -----------------------------------------------------------------------------
1897----------------------------------------------------------------------------- */
1898int ppp_stop(struct service *serv, int signal)
1899{
1900
1901	/*
1902		signal is either SIGHUP or SIGTERM
1903		SIGHUP will only disconnect the link
1904		SIGTERM will terminate pppd
1905	*/
1906	if (serv->flags & (FLAG_CONFIGCHANGEDNOW + FLAG_CONFIGCHANGEDLATER))
1907		signal = SIGTERM;
1908
1909    // anticipate next phase
1910    switch (serv->u.ppp.phase) {
1911
1912        case PPP_IDLE:
1913            return 0;
1914
1915        case PPP_DORMANT:
1916			/* SIGHUP has no effect on effect on DORMANT phase */
1917			if (signal == SIGHUP)
1918				return 0;
1919            break;
1920
1921        case PPP_HOLDOFF:
1922			/* we don't want SIGHUP to stop the HOLDOFF phase */
1923			if (signal == SIGHUP)
1924				return 0;
1925            break;
1926
1927        case PPP_DISCONNECTLINK:
1928        case PPP_TERMINATE:
1929            break;
1930
1931        case PPP_INITIALIZE:
1932            serv->flags &= ~FLAG_CONNECT;
1933            // no break;
1934
1935        case PPP_CONNECTLINK:
1936            ppp_updatephase(serv, PPP_DISCONNECTLINK, 0);
1937            break;
1938
1939        default:
1940            ppp_updatephase(serv, PPP_TERMINATE, 0);
1941    }
1942
1943    if (serv->u.ppp.controlfd[WRITE] != -1){
1944        if (signal == SIGTERM)
1945            writeparam(serv->u.ppp.controlfd[WRITE], "[TERMINATE]");
1946        else if (signal == SIGHUP)
1947            writeparam(serv->u.ppp.controlfd[WRITE], "[DISCONNECT]");
1948        else {
1949            kill(serv->u.ppp.pid, signal);
1950        }
1951    }else
1952        kill(serv->u.ppp.pid, signal);
1953
1954    return 0;
1955}
1956
1957/* -----------------------------------------------------------------------------
1958----------------------------------------------------------------------------- */
1959int ppp_suspend(struct service *serv)
1960{
1961
1962    if (serv->u.ppp.phase != PPP_IDLE)
1963        kill(serv->u.ppp.pid, SIGTSTP);
1964
1965    return 0;
1966}
1967
1968/* -----------------------------------------------------------------------------
1969----------------------------------------------------------------------------- */
1970int ppp_resume(struct service *serv)
1971{
1972    if (serv->u.ppp.phase != PPP_IDLE)
1973        kill(serv->u.ppp.pid, SIGCONT);
1974
1975    return 0;
1976}
1977
1978/* -----------------------------------------------------------------------------
1979 detects disconnects caused by recoverable errors and flags the connection for
1980 auto reconnect (i.e. persistence) and avoid UI dialog
1981 ----------------------------------------------------------------------------- */
1982static int
1983ppp_check_status_for_disconnect_by_recoverable_error (struct service *serv, int status, int devstatus)
1984{
1985#if !TARGET_OS_EMBEDDED
1986    /* try to catch a disconnection early, avoid displaying dialog and flag for reconnection */
1987    if ((serv->subtype == PPP_TYPE_L2TP || serv->subtype == PPP_TYPE_PPTP) &&
1988		(serv->u.ppp.phase == PPP_RUNNING || (serv->was_running && serv->u.ppp.phase == PPP_WAITING)) &&
1989		!serv->u.ppp.laststatus &&
1990		!serv->u.ppp.lastdevstatus &&
1991		(status  && status != EXIT_USER_REQUEST)) {
1992        if (!serv->persist_connect &&
1993            (serv->flags & (FLAG_FREE | FLAG_ONTRAFFIC | FLAG_ONDEMAND | FLAG_CONNECT | FLAG_SETUP_PERSISTCONNECTION)) == FLAG_SETUP_PERSISTCONNECTION) {
1994            // prevent error dialog from popping up during this disconnect
1995            serv->flags &= ~FLAG_ALERTERRORS;
1996            serv->persist_connect_opts = serv->connectopts;
1997            serv->connectopts = NULL;
1998            serv->persist_connect = 1;
1999            serv->persist_connect_status = status;
2000            serv->persist_connect_devstatus = devstatus;
2001			scnc_log(LOG_INFO, CFSTR("PPP Controller: status-checked, preparing for persistence status  %d.%d."),
2002				  serv->persist_connect_status,
2003				  serv->persist_connect_devstatus);
2004            return TRUE;
2005        }
2006	}
2007#endif
2008    return FALSE;
2009}
2010
2011/* -----------------------------------------------------------------------------
2012status change for this ppp occured
2013----------------------------------------------------------------------------- */
2014void ppp_updatestatus(struct service *serv, int status, int devstatus)
2015{
2016
2017    (void)ppp_check_status_for_disconnect_by_recoverable_error(serv,status,devstatus);
2018    serv->u.ppp.laststatus = status;
2019    serv->u.ppp.lastdevstatus = devstatus;
2020
2021    display_error(serv);
2022}
2023
2024
2025/* -----------------------------------------------------------------------------
2026 detects disconnects caused by recoverable errors and flags the connection for
2027 auto reconnect (i.e. persistence) and avoid UI dialog
2028 ----------------------------------------------------------------------------- */
2029static int
2030ppp_check_phase_for_disconnect_by_recoverable_error (struct service *serv, int phase)
2031{
2032#if !TARGET_OS_EMBEDDED
2033    /* try to catch a disconnection early, avoid displaying dialog and flag for reconnection */
2034    if (serv->was_running) {
2035        if (!serv->persist_connect &&
2036            (serv->flags & (FLAG_FREE | FLAG_ONTRAFFIC | FLAG_ONDEMAND | FLAG_CONNECT | FLAG_SETUP_PERSISTCONNECTION)) == FLAG_SETUP_PERSISTCONNECTION &&
2037            (serv->subtype == PPP_TYPE_L2TP || serv->subtype == PPP_TYPE_PPTP)) {
2038            // prevent error dialog from popping up during this disconnect
2039            serv->flags &= ~FLAG_ALERTERRORS;
2040            serv->persist_connect_opts = serv->connectopts;
2041            serv->connectopts = NULL;
2042            serv->persist_connect = 1;
2043            if (serv->u.ppp.laststatus && serv->u.ppp.laststatus != EXIT_USER_REQUEST && serv->u.ppp.laststatus != EXIT_FATAL_ERROR) {
2044                serv->persist_connect_status = serv->u.ppp.laststatus;
2045            } else {
2046                serv->persist_connect_status = 0;
2047            }
2048            if (serv->u.ppp.lastdevstatus) {
2049				serv->persist_connect_devstatus = serv->u.ppp.lastdevstatus;
2050            } else {
2051				serv->persist_connect_devstatus = 0;
2052            }
2053			scnc_log(LOG_INFO, CFSTR("PPP Controller: phase-checked, preparing for persistence status  %d.%d."),
2054				  serv->persist_connect_status,
2055				  serv->persist_connect_devstatus);
2056			return TRUE;
2057        }
2058	}
2059#endif
2060	return FALSE;
2061}
2062
2063/* -----------------------------------------------------------------------------
2064 detects location changes that require disconnection.
2065 returns true if
2066 ----------------------------------------------------------------------------- */
2067static int
2068ppp_disconnect_if_location_changed (struct service *serv, int phase)
2069{
2070#if !TARGET_OS_EMBEDDED
2071	if (serv->was_running && (phase == PPP_WAITING || phase == PPP_RUNNING) && (serv->subtype == PPP_TYPE_L2TP || serv->subtype == PPP_TYPE_PPTP)) {
2072		if (DISCONNECT_VPN_IFLOCATIONCHANGED(serv)) {
2073			scnc_log(LOG_NOTICE, CFSTR("PPP Controller: the underlying interface has changed networks."));
2074			return TRUE;
2075		}
2076	}
2077#endif
2078	return FALSE;
2079}
2080
2081/* -----------------------------------------------------------------------------
2082phase change for this ppp occured
2083----------------------------------------------------------------------------- */
2084void ppp_updatephase(struct service *serv, int phase, int ifunit)
2085{
2086
2087  /* check if update is received pppd has  exited */
2088  if (serv->u.ppp.statusfd[READ] == -1)
2089      return;
2090
2091
2092    /* check for new phase */
2093    if (phase == serv->u.ppp.phase)
2094        return;
2095
2096	/* special-case disconnect transitions? */
2097    if (ppp_check_phase_for_disconnect_by_recoverable_error(serv, phase) == FALSE) {
2098		if (ppp_disconnect_if_location_changed(serv, phase) == TRUE) {
2099			return;
2100		}
2101	}
2102
2103    serv->u.ppp.phase = phase;
2104    phase_changed(serv, phase);
2105    /*
2106     * Don't publish ppp status here in the controller because we have pppd that's also
2107     * publishing the PPP dictionary, causing a race condition. It can happen that the controller
2108     * has not received the pppd update at this point, and thus is to write only a status
2109     * value to the dynamic store, but by the time the write takes place, pppd just updated
2110     * the store with more info which would get wiped out by the write from the controller.
2111     *
2112     * Instead, we'll rely on ppp status udpates from pppd.
2113     *
2114    publish_dictnumentry(gDynamicStore, serv->serviceID, kSCEntNetPPP, kSCPropNetPPPStatus, phase);
2115     */
2116
2117    switch (serv->u.ppp.phase) {
2118        case PPP_INITIALIZE:
2119            serv->connecttime = mach_absolute_time() * gTimeScaleSeconds;
2120            serv->connectionslepttime = 0;
2121            break;
2122
2123        case PPP_RUNNING:
2124            serv->if_name[0] = 0;
2125            snprintf(serv->if_name, sizeof(serv->if_name), "ppp%d", ifunit);
2126
2127            serv->was_running = 1;
2128            SESSIONTRACERESTABLISHED(serv);
2129            break;
2130
2131        case PPP_DORMANT:
2132            serv->if_name[0] = 0;
2133            getStringFromEntity(gDynamicStore, kSCDynamicStoreDomainState, serv->serviceID,
2134                    kSCEntNetPPP, kSCPropInterfaceName, (u_char *)serv->if_name, sizeof(serv->if_name));
2135            // no break;
2136
2137        case PPP_HOLDOFF:
2138
2139            /* check if setup has changed */
2140            if (serv->flags & FLAG_CONFIGCHANGEDLATER)
2141                scnc_stop(serv, 0, SIGTERM, SCNC_STOP_NONE);
2142            break;
2143    }
2144}
2145
2146/* -----------------------------------------------------------------------------
2147----------------------------------------------------------------------------- */
2148int ppp_is_pid(struct service *serv, int pid)
2149{
2150	return (serv->u.ppp.pid == pid);
2151}
2152
2153/* -----------------------------------------------------------------------------
2154----------------------------------------------------------------------------- */
2155SCNetworkConnectionStatus ppp_getstatus(struct service *serv)
2156{
2157	SCNetworkConnectionStatus	status = kSCNetworkConnectionDisconnected;
2158
2159	switch (serv->u.ppp.phase) {
2160		case PPP_INITIALIZE:
2161		case PPP_CONNECTLINK:
2162		case PPP_ESTABLISH:
2163		case PPP_AUTHENTICATE:
2164		case PPP_CALLBACK:
2165		case PPP_NETWORK:
2166		case PPP_WAITONBUSY:
2167			status = kSCNetworkConnectionConnecting;
2168			break;
2169		case PPP_TERMINATE:
2170		case PPP_DISCONNECTLINK:
2171		case PPP_WAITING:
2172			status = kSCNetworkConnectionDisconnecting;
2173			break;
2174		case PPP_RUNNING:
2175		case PPP_ONHOLD:
2176			status = kSCNetworkConnectionConnected;
2177			break;
2178		case PPP_IDLE:
2179		case PPP_DORMANT:
2180		case PPP_HOLDOFF:
2181		default:
2182			status = kSCNetworkConnectionDisconnected;
2183	}
2184
2185	return status;
2186}
2187
2188/* -----------------------------------------------------------------------------
2189----------------------------------------------------------------------------- */
2190int ppp_getstatus1(struct service *serv, void **reply, u_int16_t *replylen)
2191{
2192    struct ppp_status 		*stat;
2193    struct ifpppstatsreq 	rq;
2194    int		 		s;
2195    u_int32_t			retrytime, conntime, disconntime, curtime;
2196
2197    *reply = my_Allocate(sizeof(struct ppp_status));
2198    if (*reply == 0) {
2199        return ENOMEM;
2200    }
2201    stat = (struct ppp_status *)*reply;
2202
2203    bzero (stat, sizeof (struct ppp_status));
2204    switch (serv->u.ppp.phase) {
2205        case PPP_DORMANT:
2206        case PPP_HOLDOFF:
2207            stat->status = PPP_IDLE;		// Dial on demand does not exist in the api
2208            break;
2209        default:
2210            stat->status = serv->u.ppp.phase;
2211    }
2212
2213    switch (stat->status) {
2214        case PPP_RUNNING:
2215        case PPP_ONHOLD:
2216
2217            s = socket(AF_INET, SOCK_DGRAM, 0);
2218            if (s < 0) {
2219            	my_Deallocate(*reply, sizeof(struct ppp_status));
2220                return errno;
2221            }
2222
2223            bzero (&rq, sizeof (rq));
2224
2225            strncpy(rq.ifr_name, (char*)serv->if_name, IFNAMSIZ);
2226            if (ioctl(s, SIOCGPPPSTATS, &rq) < 0) {
2227                close(s);
2228            	my_Deallocate(*reply, sizeof(struct ppp_status));
2229                return errno;
2230            }
2231
2232            close(s);
2233
2234            conntime = 0;
2235            getNumberFromEntity(gDynamicStore, kSCDynamicStoreDomainState, serv->serviceID,
2236                kSCEntNetPPP, kSCPropNetPPPConnectTime, &conntime);
2237            disconntime = 0;
2238            getNumberFromEntity(gDynamicStore, kSCDynamicStoreDomainState, serv->serviceID,
2239                kSCEntNetPPP, kSCPropNetPPPDisconnectTime, &disconntime);
2240
2241            curtime = mach_absolute_time() * gTimeScaleSeconds;
2242            if (conntime)
2243				stat->s.run.timeElapsed = curtime - conntime;
2244            if (!disconntime)	// no limit...
2245     	       stat->s.run.timeRemaining = 0xFFFFFFFF;
2246            else
2247      	      stat->s.run.timeRemaining = (disconntime > curtime) ? disconntime - curtime : 0;
2248
2249            stat->s.run.outBytes = rq.stats.p.ppp_obytes;
2250            stat->s.run.inBytes = rq.stats.p.ppp_ibytes;
2251            stat->s.run.inPackets = rq.stats.p.ppp_ipackets;
2252            stat->s.run.outPackets = rq.stats.p.ppp_opackets;
2253            stat->s.run.inErrors = rq.stats.p.ppp_ierrors;
2254            stat->s.run.outErrors = rq.stats.p.ppp_ierrors;
2255            break;
2256
2257        case PPP_WAITONBUSY:
2258
2259            stat->s.busy.timeRemaining = 0;
2260            retrytime = 0;
2261            getNumberFromEntity(gDynamicStore, kSCDynamicStoreDomainState, serv->serviceID,
2262                kSCEntNetPPP, kSCPropNetPPPRetryConnectTime, &retrytime);
2263            if (retrytime) {
2264                curtime = mach_absolute_time() * gTimeScaleSeconds;
2265                stat->s.busy.timeRemaining = (curtime < retrytime) ? retrytime - curtime : 0;
2266            }
2267            break;
2268
2269        default:
2270            stat->s.disc.lastDiscCause = ppp_translate_error(serv->subtype, serv->u.ppp.laststatus, serv->u.ppp.lastdevstatus);
2271    }
2272
2273    *replylen = sizeof(struct ppp_status);
2274    return 0;
2275}
2276
2277/* -----------------------------------------------------------------------------
2278----------------------------------------------------------------------------- */
2279int ppp_copyextendedstatus(struct service *serv, CFDictionaryRef *statusdict)
2280{
2281    CFMutableDictionaryRef dict = NULL;
2282	CFMutableDictionaryRef pppdict = NULL;
2283	int error = 0;
2284
2285	*statusdict = NULL;
2286
2287    if ((dict = CFDictionaryCreateMutable(NULL, 0, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks)) == 0) {
2288		error = ENOMEM;
2289        goto fail;
2290	}
2291
2292    /* create and add PPP dictionary */
2293    if ((pppdict = CFDictionaryCreateMutable(NULL, 0, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks)) == 0) {
2294		error = ENOMEM;
2295        goto fail;
2296	}
2297
2298    AddNumber(pppdict, kSCPropNetPPPStatus, serv->u.ppp.phase);
2299
2300    if (serv->u.ppp.phase != PPP_IDLE)
2301        AddStringFromState(gDynamicStore, serv->serviceID, kSCEntNetPPP, kSCPropNetPPPCommRemoteAddress, pppdict);
2302
2303    switch (serv->u.ppp.phase) {
2304        case PPP_RUNNING:
2305        case PPP_ONHOLD:
2306
2307			if (serv->ne_sm_bridge != NULL) {
2308				AddNumber64(pppdict, kSCPropNetPPPConnectTime, ne_sm_bridge_get_connect_time(serv->ne_sm_bridge));
2309			} else {
2310				AddNumberFromState(gDynamicStore, serv->serviceID, kSCEntNetPPP, kSCPropNetPPPConnectTime, pppdict);
2311			}
2312            AddNumberFromState(gDynamicStore, serv->serviceID, kSCEntNetPPP, kSCPropNetPPPDisconnectTime, pppdict);
2313            AddNumberFromState(gDynamicStore, serv->serviceID, kSCEntNetPPP, kSCPropNetPPPLCPCompressionPField, pppdict);
2314            AddNumberFromState(gDynamicStore, serv->serviceID, kSCEntNetPPP, kSCPropNetPPPLCPCompressionACField, pppdict);
2315            AddNumberFromState(gDynamicStore, serv->serviceID, kSCEntNetPPP, kSCPropNetPPPLCPMRU, pppdict);
2316            AddNumberFromState(gDynamicStore, serv->serviceID, kSCEntNetPPP, kSCPropNetPPPLCPMTU, pppdict);
2317            AddNumberFromState(gDynamicStore, serv->serviceID, kSCEntNetPPP, kSCPropNetPPPLCPReceiveACCM, pppdict);
2318            AddNumberFromState(gDynamicStore, serv->serviceID, kSCEntNetPPP, kSCPropNetPPPLCPTransmitACCM, pppdict);
2319            AddNumberFromState(gDynamicStore, serv->serviceID, kSCEntNetPPP, kSCPropNetPPPIPCPCompressionVJ, pppdict);
2320            break;
2321
2322        case PPP_WAITONBUSY:
2323			if (serv->ne_sm_bridge != NULL) {
2324				AddNumber64(pppdict, kSCPropNetPPPConnectTime, ne_sm_bridge_get_connect_time(serv->ne_sm_bridge));
2325			} else {
2326				AddNumberFromState(gDynamicStore, serv->serviceID, kSCEntNetPPP, kSCPropNetPPPRetryConnectTime, pppdict);
2327			}
2328            break;
2329
2330        case PPP_DORMANT:
2331            break;
2332
2333        default:
2334            AddNumber(pppdict, kSCPropNetPPPLastCause, serv->u.ppp.laststatus);
2335            AddNumber(pppdict, kSCPropNetPPPDeviceLastCause, serv->u.ppp.lastdevstatus);
2336    }
2337
2338    CFDictionaryAddValue(dict, kSCEntNetPPP, pppdict);
2339    my_CFRelease(&pppdict);
2340
2341    /* create and add Modem dictionary */
2342    if (serv->subtype == PPP_TYPE_SERIAL
2343        && (serv->u.ppp.phase == PPP_RUNNING || serv->u.ppp.phase == PPP_ONHOLD)) {
2344        if ((pppdict = CFDictionaryCreateMutable(NULL, 0, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks)) == 0) {
2345			error = ENOMEM;
2346            goto fail;
2347		}
2348
2349        AddNumberFromState(gDynamicStore, serv->serviceID, kSCEntNetModem, kSCPropNetModemConnectSpeed, pppdict);
2350
2351        CFDictionaryAddValue(dict, kSCEntNetModem, pppdict);
2352        my_CFRelease(&pppdict);
2353    }
2354
2355    /* create and add IPv4 dictionary */
2356    if (serv->u.ppp.phase == PPP_RUNNING || serv->u.ppp.phase == PPP_ONHOLD) {
2357        pppdict = (CFMutableDictionaryRef)copyEntity(gDynamicStore, kSCDynamicStoreDomainState, serv->serviceID, kSCEntNetIPv4);
2358        if (pppdict) {
2359            CFDictionaryAddValue(dict, kSCEntNetIPv4, pppdict);
2360            my_CFRelease(&pppdict);
2361        }
2362    }
2363
2364    AddNumber(dict, kSCNetworkConnectionStatus, ppp_getstatus(serv));
2365
2366	*statusdict = CFRetain(dict);
2367
2368fail:
2369	my_CFRelease(&pppdict);
2370	my_CFRelease(&dict);
2371
2372    return error;
2373}
2374
2375/* -----------------------------------------------------------------------------
2376----------------------------------------------------------------------------- */
2377int ppp_copystatistics(struct service *serv, CFDictionaryRef *statsdict)
2378{
2379    CFMutableDictionaryRef dict = NULL;
2380    CFMutableDictionaryRef pppdict = NULL;
2381    int s = -1;
2382    struct ifpppstatsreq rq;
2383	int	error = 0;
2384
2385	*statsdict = NULL;
2386
2387	if (serv->u.ppp.phase != PPP_RUNNING
2388		&& serv->u.ppp.phase != PPP_ONHOLD)
2389			return EINVAL;
2390
2391    if ((dict = CFDictionaryCreateMutable(NULL, 0, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks)) == 0) {
2392		error = ENOMEM;
2393		goto fail;
2394	}
2395
2396    /* create and add PPP dictionary */
2397    if ((pppdict = CFDictionaryCreateMutable(NULL, 0, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks)) == 0) {
2398		error = ENOMEM;
2399		goto fail;
2400	}
2401
2402	s = socket(AF_INET, SOCK_DGRAM, 0);
2403	if (s < 0) {
2404		error = errno;
2405        goto fail;
2406	}
2407
2408	bzero (&rq, sizeof (rq));
2409
2410	strncpy(rq.ifr_name, (char*)serv->if_name, IFNAMSIZ);
2411	if (ioctl(s, SIOCGPPPSTATS, &rq) < 0) {
2412		error = errno;
2413        goto fail;
2414	}
2415
2416	AddNumber(pppdict, kSCNetworkConnectionBytesIn, rq.stats.p.ppp_ibytes);
2417	AddNumber(pppdict, kSCNetworkConnectionBytesOut, rq.stats.p.ppp_obytes);
2418	AddNumber(pppdict, kSCNetworkConnectionPacketsIn, rq.stats.p.ppp_ipackets);
2419	AddNumber(pppdict, kSCNetworkConnectionPacketsOut, rq.stats.p.ppp_opackets);
2420	AddNumber(pppdict, kSCNetworkConnectionErrorsIn, rq.stats.p.ppp_ierrors);
2421	AddNumber(pppdict, kSCNetworkConnectionErrorsOut, rq.stats.p.ppp_ierrors);
2422
2423    CFDictionaryAddValue(dict, kSCEntNetPPP, pppdict);
2424
2425	*statsdict = CFRetain(dict);
2426
2427fail:
2428    my_CFRelease(&pppdict);
2429    my_CFRelease(&dict);
2430	if (s != -1) {
2431		close(s);
2432	}
2433    return error;
2434}
2435
2436/* -----------------------------------------------------------------------------
2437----------------------------------------------------------------------------- */
2438int ppp_getconnectsystemdata(struct service *serv, void **reply, u_int16_t *replylen)
2439{
2440	CFDictionaryRef	service = NULL;
2441    CFDataRef			dataref = NULL;
2442    void			*dataptr = 0;
2443    u_int32_t			datalen = 0;
2444	int				err = 0;
2445
2446	service = copyService(gDynamicStore, kSCDynamicStoreDomainSetup, serv->serviceID);
2447    if (service == 0) {
2448        // no data
2449        *replylen = 0;
2450        return 0;
2451    }
2452
2453    if ((dataref = Serialize(service, &dataptr, &datalen)) == 0) {
2454		err = ENOMEM;
2455		goto end;
2456    }
2457
2458    *reply = my_Allocate(datalen);
2459    if (*reply == 0) {
2460		err = ENOMEM;
2461		goto end;
2462    }
2463    else {
2464        bcopy(dataptr, *reply, datalen);
2465        *replylen = datalen;
2466    }
2467
2468end:
2469    if (service)
2470		CFRelease(service);
2471    if (dataref)
2472		CFRelease(dataref);
2473    return err;
2474 }
2475
2476/* -----------------------------------------------------------------------------
2477----------------------------------------------------------------------------- */
2478int ppp_getconnectdata(struct service *serv, CFDictionaryRef *options, int all)
2479{
2480    CFDictionaryRef		opts;
2481    CFMutableDictionaryRef	mdict = NULL;
2482    CFDictionaryRef	dict;
2483	int err = 0;
2484
2485	*options = NULL;
2486
2487    /* return saved data */
2488    opts = serv->connectopts;
2489
2490    if (opts == 0) {
2491        // no data
2492        return 0;
2493    }
2494
2495	if (!all) {
2496		/* special code to remove secret information */
2497		CFMutableDictionaryRef mdict1 = NULL;
2498
2499		mdict = CFDictionaryCreateMutableCopy(0, 0, opts);
2500		if (mdict == 0) {
2501			// no data
2502			return 0;
2503		}
2504
2505		dict = CFDictionaryGetValue(mdict, kSCEntNetPPP);
2506		if (dict && (CFGetTypeID(dict) == CFDictionaryGetTypeID())) {
2507			mdict1 = CFDictionaryCreateMutableCopy(0, 0, dict);
2508			if (mdict1) {
2509				CFDictionaryRemoveValue(mdict1, kSCPropNetPPPAuthPassword);
2510				CFDictionarySetValue(mdict, kSCEntNetPPP, mdict1);
2511				CFRelease(mdict1);
2512			}
2513		}
2514
2515		dict = CFDictionaryGetValue(mdict, kSCEntNetL2TP);
2516		if (dict && (CFGetTypeID(dict) == CFDictionaryGetTypeID())) {
2517			mdict1 = CFDictionaryCreateMutableCopy(0, 0, dict);
2518			if (mdict1) {
2519				CFDictionaryRemoveValue(mdict1, kSCPropNetL2TPIPSecSharedSecret);
2520				CFDictionarySetValue(mdict, kSCEntNetL2TP, mdict1);
2521				CFRelease(mdict1);
2522			}
2523		}
2524
2525		dict = CFDictionaryGetValue(mdict, kSCEntNetIPSec);
2526		if (dict && (CFGetTypeID(dict) == CFDictionaryGetTypeID())) {
2527			mdict1 = CFDictionaryCreateMutableCopy(0, 0, dict);
2528			if (mdict1) {
2529				CFDictionaryRemoveValue(mdict1, kSCPropNetIPSecSharedSecret);
2530				CFDictionarySetValue(mdict, kSCEntNetIPSec, mdict1);
2531				CFRelease(mdict1);
2532			}
2533		}
2534		*options = CFRetain(mdict);
2535	} else {
2536		*options = CFRetain(opts);
2537	}
2538
2539    if (mdict)
2540		CFRelease(mdict);
2541    return err;
2542}
2543
2544/* -----------------------------------------------------------------------------
2545translate a pppd native cause into a PPP API cause
2546----------------------------------------------------------------------------- */
2547u_int32_t ppp_translate_error(u_int16_t subtype, u_int32_t native_ppp_error, u_int32_t native_dev_error)
2548{
2549    u_int32_t	error = PPP_ERR_GEN_ERROR;
2550
2551    switch (native_ppp_error) {
2552        case EXIT_USER_REQUEST:
2553            error = 0;
2554            break;
2555        case EXIT_CONNECT_FAILED:
2556            error = PPP_ERR_GEN_ERROR;
2557            break;
2558        case EXIT_TERMINAL_FAILED:
2559            error = PPP_ERR_TERMSCRIPTFAILED;
2560            break;
2561        case EXIT_NEGOTIATION_FAILED:
2562            error = PPP_ERR_LCPFAILED;
2563            break;
2564        case EXIT_AUTH_TOPEER_FAILED:
2565            error = PPP_ERR_AUTHFAILED;
2566            break;
2567        case EXIT_IDLE_TIMEOUT:
2568            error = PPP_ERR_IDLETIMEOUT;
2569            break;
2570        case EXIT_CONNECT_TIME:
2571            error = PPP_ERR_SESSIONTIMEOUT;
2572            break;
2573        case EXIT_LOOPBACK:
2574            error = PPP_ERR_LOOPBACK;
2575            break;
2576        case EXIT_PEER_DEAD:
2577            error = PPP_ERR_PEERDEAD;
2578            break;
2579        case EXIT_OK:
2580            error = PPP_ERR_DISCBYPEER;
2581            break;
2582        case EXIT_HANGUP:
2583            error = PPP_ERR_DISCBYDEVICE;
2584            break;
2585    }
2586
2587    // override with a more specific error
2588    if (native_dev_error) {
2589        switch (subtype) {
2590            case PPP_TYPE_SERIAL:
2591                switch (native_dev_error) {
2592                    case EXIT_PPPSERIAL_NOCARRIER:
2593                        error = PPP_ERR_MOD_NOCARRIER;
2594                        break;
2595                    case EXIT_PPPSERIAL_NONUMBER:
2596                        error = PPP_ERR_MOD_NONUMBER;
2597                        break;
2598                    case EXIT_PPPSERIAL_BADSCRIPT:
2599                        error = PPP_ERR_MOD_BADSCRIPT;
2600                        break;
2601                    case EXIT_PPPSERIAL_BUSY:
2602                        error = PPP_ERR_MOD_BUSY;
2603                        break;
2604                    case EXIT_PPPSERIAL_NODIALTONE:
2605                        error = PPP_ERR_MOD_NODIALTONE;
2606                        break;
2607                    case EXIT_PPPSERIAL_ERROR:
2608                        error = PPP_ERR_MOD_ERROR;
2609                        break;
2610                    case EXIT_PPPSERIAL_NOANSWER:
2611                        error = PPP_ERR_MOD_NOANSWER;
2612                        break;
2613                    case EXIT_PPPSERIAL_HANGUP:
2614                        error = PPP_ERR_MOD_HANGUP;
2615                        break;
2616                    default :
2617                        error = PPP_ERR_CONNSCRIPTFAILED;
2618                }
2619                break;
2620
2621            case PPP_TYPE_PPPoE:
2622                // need to handle PPPoE specific error codes
2623                break;
2624        }
2625    }
2626
2627    return error;
2628}
2629
2630