#include <fcntl.h>
#include <unistd.h>
#include <sys/param.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <paths.h>
#include <net/if.h>
#include <net/ndrv.h>
#include <mach/message.h>
#include <mach/boolean.h>
#include <mach/mach.h>
#include <mach/mach_error.h>
#include <CoreFoundation/CFMachPort.h>
#include <mach/mach_time.h>
#include <servers/bootstrap.h>
#include <CoreFoundation/CoreFoundation.h>
#include <CoreFoundation/CFUserNotification.h>
#include <SystemConfiguration/SystemConfiguration.h>
#include <SystemConfiguration/SCDPlugin.h>
#include <SystemConfiguration/SCPrivate.h> // for SCLog() and VPN private keys
#include <mach/task_special_ports.h>
#include "pppcontroller_types.h"
#include "pppcontroller.h"
#include <SystemConfiguration/SCValidation.h>
#include <bsm/libbsm.h>
#include <pwd.h>
#include "ppp_msg.h"
#include "ppp_privmsg.h"
#include "../Family/ppp_domain.h"
#include "../Helpers/pppd/pppd.h"
#include "scnc_main.h"
#include "scnc_client.h"
#include "ppp_manager.h"
#include "ppp_option.h"
#include "ppp_socket_server.h"
#include "scnc_utils.h"
#include "../Drivers/PPTP/PPTP-plugin/pptp.h"
#include "../Drivers/L2TP/L2TP-plugin/l2tp.h"
#include "../Drivers/PPPoE/PPPoE-extension/PPPoE.h"
#include "sessionTracer.h"
enum {
READ = 0, WRITE = 1 };
#define MAX_EXTRACONNECTTIME 20
#define MIN_EXTRACONNECTTIME 3
extern TAILQ_HEAD(, service) service_head;
static void display_error(struct service *serv);
static void exec_callback(pid_t pid, int status, struct rusage *rusage, void *context);
static void exec_postfork(pid_t pid, void *arg);
static int send_pppd_params(struct service *serv, CFDictionaryRef service, CFDictionaryRef options, u_int8_t onTraffic);
static int change_pppd_params(struct service *serv, CFDictionaryRef service, CFDictionaryRef options);
static void setup_PPPoE(struct service *serv);
static void dispose_PPPoE(struct service *serv);
u_int16_t ppp_subtype(CFStringRef subtypeRef)
{
if (my_CFEqual(subtypeRef, kSCValNetInterfaceSubTypePPPSerial))
return PPP_TYPE_SERIAL;
else if (my_CFEqual(subtypeRef, kSCValNetInterfaceSubTypePPPoE))
return PPP_TYPE_PPPoE;
else if (my_CFEqual(subtypeRef, kSCValNetInterfaceSubTypePPTP))
return PPP_TYPE_PPTP;
else if (my_CFEqual(subtypeRef, kSCValNetInterfaceSubTypeL2TP))
return PPP_TYPE_L2TP;
return PPP_TYPE_OTHER;
}
int ppp_new_service(struct service *serv)
{
CFURLRef url;
u_char str[MAXPATHLEN], str2[32];
serv->u.ppp.ndrv_socket = -1;
serv->u.ppp.phase = PPP_IDLE;
serv->u.ppp.statusfd[READ] = -1;
serv->u.ppp.statusfd[WRITE] = -1;
serv->u.ppp.controlfd[READ] = -1;
serv->u.ppp.controlfd[WRITE] = -1;
if (serv->subtypeRef) {
strcpy(str, DIR_KEXT);
str2[0] = 0;
CFStringGetCString(serv->subtypeRef, str2, sizeof(str2), kCFStringEncodingUTF8);
strcat(str, str2);
strcat(str, ".ppp"); url = CFURLCreateFromFileSystemRepresentation(NULL, str, strlen(str), TRUE);
if (url) {
serv->u.ppp.bundle = CFBundleCreate(0, url);
CFRelease(url);
}
}
return 0;
}
int ppp_dispose_service(struct service *serv)
{
if (serv->u.ppp.phase != PPP_IDLE)
return 1;
dispose_PPPoE(serv);
my_CFRelease(&serv->u.ppp.bundle);
return 0;
}
void ppp_user_notification_callback(struct service *serv, CFUserNotificationRef userNotification, CFOptionFlags responseFlags)
{
}
static
void display_error(struct service *serv)
{
CFStringRef msg = NULL;
CFMutableDictionaryRef dict = NULL;
SInt32 err;
SESSIONTRACERSTOP(serv);
if (serv->u.ppp.laststatus == EXIT_USER_REQUEST)
return;
if ((serv->flags & FLAG_ALERTERRORS) == 0)
return;
if ((serv->flags & FLAG_ONTRAFFIC) && (serv->u.ppp.laststatus == EXIT_DEVICE_ERROR))
return;
if (serv->u.ppp.lastdevstatus) {
switch (serv->subtype) {
case PPP_TYPE_L2TP:
switch (serv->u.ppp.lastdevstatus) {
#ifdef TARGET_EMBEDDED_OS
case EXIT_L2TP_NETWORKCHANGED:
return;
#endif
}
break;
case PPP_TYPE_PPTP:
switch (serv->u.ppp.lastdevstatus) {
#ifdef TARGET_EMBEDDED_OS
case EXIT_PPTP_NETWORKCHANGED:
return;
#endif
}
break;
}
msg = CFStringCreateWithFormat(0, 0, CFSTR("%@ Error %d"), serv->subtypeRef, serv->u.ppp.lastdevstatus);
}
if (msg == NULL) {
switch (serv->u.ppp.laststatus) {
case EXIT_USER_REQUEST:
return;
}
msg = CFStringCreateWithFormat(0, 0, CFSTR("PPP Error %d"), serv->u.ppp.laststatus);
}
if (!msg || !CFStringGetLength(msg))
goto done;
dict = CFDictionaryCreateMutable(NULL, 0, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
if (!dict)
goto done;
if (gIconURLRef)
CFDictionaryAddValue(dict, kCFUserNotificationIconURLKey, gIconURLRef);
if (gBundleURLRef)
CFDictionaryAddValue(dict, kCFUserNotificationLocalizationURLKey, gBundleURLRef);
CFDictionaryAddValue(dict, kCFUserNotificationAlertMessageKey, msg);
CFDictionaryAddValue(dict, kCFUserNotificationAlertHeaderKey, (serv->subtype == PPP_TYPE_L2TP || serv->subtype == PPP_TYPE_PPTP) ? CFSTR("VPN Connection") : CFSTR("Network Connection"));
if (serv->userNotificationRef) {
err = CFUserNotificationUpdate(serv->userNotificationRef, 0, kCFUserNotificationNoteAlertLevel, dict);
}
else {
serv->userNotificationRef = CFUserNotificationCreate(NULL, 0, kCFUserNotificationNoteAlertLevel, &err, dict);
if (!serv->userNotificationRef)
goto done;
serv->userNotificationRLS = CFUserNotificationCreateRunLoopSource(NULL, serv->userNotificationRef,
user_notification_callback, 0);
if (!serv->userNotificationRLS) {
my_CFRelease(&serv->userNotificationRef);
goto done;
}
CFRunLoopAddSource(CFRunLoopGetCurrent(), serv->userNotificationRLS, kCFRunLoopDefaultMode);
}
done:
my_CFRelease(&dict);
my_CFRelease(&msg);
}
static
void setup_PPPoE(struct service *serv)
{
CFDictionaryRef interface;
CFStringRef hardware, device;
struct sockaddr_ndrv ndrv;
if (serv->subtype == PPP_TYPE_PPPoE) {
interface = copyEntity(gDynamicStore, kSCDynamicStoreDomainSetup, serv->serviceID, kSCEntNetInterface);
if (interface) {
device = CFDictionaryGetValue(interface, kSCPropNetInterfaceDeviceName);
hardware = CFDictionaryGetValue(interface, kSCPropNetInterfaceHardware);
if (isA_CFString(hardware) && isA_CFString(device)
&& ((CFStringCompare(hardware, kSCEntNetAirPort, 0) == kCFCompareEqualTo)
|| (CFStringCompare(hardware, kSCEntNetEthernet, 0) == kCFCompareEqualTo))) {
if (serv->device
&& (CFStringCompare(device, serv->device, 0) != kCFCompareEqualTo)) {
dispose_PPPoE(serv);
}
if (!serv->device) {
serv->u.ppp.ndrv_socket = socket(PF_NDRV, SOCK_RAW, 0);
if (serv->u.ppp.ndrv_socket >= 0) {
serv->device = device;
CFRetain(serv->device);
CFStringGetCString(device, ndrv.snd_name, sizeof(ndrv.snd_name), kCFStringEncodingMacRoman);
ndrv.snd_len = sizeof(ndrv);
ndrv.snd_family = AF_NDRV;
if (bind(serv->u.ppp.ndrv_socket, (struct sockaddr *)&ndrv, sizeof(ndrv)) < 0) {
dispose_PPPoE(serv);
}
}
}
}
else {
dispose_PPPoE(serv);
}
CFRelease(interface);
}
}
}
static
void dispose_PPPoE(struct service *serv)
{
if (serv->u.ppp.ndrv_socket != -1) {
close(serv->u.ppp.ndrv_socket);
serv->u.ppp.ndrv_socket = -1;
}
if (serv->device) {
CFRelease(serv->device);
serv->device = 0;
}
}
int ppp_setup_service(struct service *serv)
{
u_int32_t lval;
CFDictionaryRef dict;
serv->flags &= ~(
FLAG_SETUP_ONTRAFFIC +
FLAG_SETUP_DISCONNECTONLOGOUT +
FLAG_SETUP_DISCONNECTONSLEEP +
FLAG_SETUP_PREVENTIDLESLEEP +
FLAG_SETUP_DISCONNECTONFASTUSERSWITCH);
dict = copyEntity(gDynamicStore, kSCDynamicStoreDomainSetup, serv->serviceID, kSCEntNetPPP);
if (dict) {
lval = 0;
getNumber(dict, kSCPropNetPPPDialOnDemand, &lval);
if (lval) serv->flags |= FLAG_SETUP_ONTRAFFIC;
lval = 0;
getNumber(dict, kSCPropNetPPPDisconnectOnLogout, &lval);
if (lval) serv->flags |= FLAG_SETUP_DISCONNECTONLOGOUT;
switch (serv->subtype) {
case PPP_TYPE_PPTP:
case PPP_TYPE_L2TP:
lval = 0;
break;
default :
lval = 1;
}
getNumber(dict, kSCPropNetPPPDisconnectOnSleep, &lval);
if (lval) serv->flags |= FLAG_SETUP_DISCONNECTONSLEEP;
switch (serv->subtype) {
case PPP_TYPE_PPTP:
case PPP_TYPE_L2TP:
lval = 0;
break;
default :
lval = 1;
}
getNumber(dict, CFSTR("PreventIdleSleep"), &lval);
if (lval) serv->flags |= FLAG_SETUP_PREVENTIDLESLEEP;
lval = (serv->flags & FLAG_SETUP_DISCONNECTONLOGOUT);
getNumber(dict, CFSTR("DisconnectOnFastUserSwitch"), &lval);
if (lval) serv->flags |= FLAG_SETUP_DISCONNECTONFASTUSERSWITCH;
CFRelease(dict);
dict = NULL;
}
setup_PPPoE(serv);
switch (serv->u.ppp.phase) {
case PPP_IDLE:
if (serv->flags & FLAG_SETUP_ONTRAFFIC
&& (!(serv->flags & FLAG_SETUP_DISCONNECTONLOGOUT) || gLoggedInUser)) {
ppp_start(serv, 0, 0, 0, 0, 1, 0);
}
break;
case PPP_DORMANT:
case PPP_HOLDOFF:
serv->flags |= FLAG_CONFIGCHANGEDNOW;
serv->flags &= ~FLAG_CONFIGCHANGEDLATER;
scnc_stop(serv, 0, SIGTERM);
break;
default :
serv->flags |= FLAG_CONFIGCHANGEDLATER;
serv->flags &= ~FLAG_CONFIGCHANGEDNOW;
dict = copyService(gDynamicStore, kSCDynamicStoreDomainSetup, serv->serviceID);
if (dict) {
change_pppd_params(serv, dict, serv->connectopts);
CFRelease(dict);
}
break;
}
return 0;
}
int ppp_can_sleep(struct service *serv)
{
if (serv->u.ppp.phase == PPP_RUNNING
&& (serv->flags & FLAG_SETUP_PREVENTIDLESLEEP))
return 0;
return 1;
}
int ppp_will_sleep(struct service *serv, int checking)
{
u_int32_t delay = 0, alert = 0;
if (serv->u.ppp.phase != PPP_IDLE
&& (serv->flags & FLAG_SETUP_DISCONNECTONSLEEP)) {
delay = 1;
if (serv->u.ppp.phase != PPP_DORMANT || serv->u.ppp.phase != PPP_HOLDOFF)
alert = 2;
if (!checking)
scnc_stop(serv, 0, SIGTERM);
}
return delay + alert;
}
void ppp_ipv4_state_changed(struct service *serv)
{
}
void ppp_wake_up(struct service *serv)
{
if (serv->u.ppp.phase == PPP_IDLE) {
if ((serv->flags & FLAG_SETUP_ONTRAFFIC)
&& (!(serv->flags & FLAG_SETUP_DISCONNECTONLOGOUT) || gLoggedInUser)) {
ppp_start(serv, 0, 0, 0, 0, 1, 0);
}
}
}
void ppp_log_out(struct service *serv)
{
if (serv->u.ppp.phase != PPP_IDLE
&& (serv->flags & FLAG_SETUP_DISCONNECTONLOGOUT))
scnc_stop(serv, 0, SIGTERM);
}
void ppp_log_in(struct service *serv)
{
if (serv->u.ppp.phase == PPP_IDLE
&& (serv->flags & FLAG_SETUP_ONTRAFFIC))
ppp_start(serv, 0, 0, 0, 0, 1, 0);
}
void ppp_log_switch(struct service *serv)
{
switch (serv->u.ppp.phase) {
case PPP_IDLE:
if (serv->flags & FLAG_SETUP_ONTRAFFIC)
ppp_start(serv, 0, 0, 0, 0, 1, 0);
break;
default:
if (serv->flags & FLAG_SETUP_DISCONNECTONFASTUSERSWITCH) {
serv->flags &= ~FLAG_CONFIGCHANGEDLATER;
if (serv->flags & FLAG_SETUP_ONTRAFFIC)
serv->flags |= FLAG_CONFIGCHANGEDNOW;
else
serv->flags &= ~FLAG_CONFIGCHANGEDNOW;
scnc_stop(serv, 0, SIGTERM);
}
}
}
static
void addparam(char **arg, u_int32_t *argi, char *param)
{
int len = strlen(param);
if (len && (arg[*argi] = malloc(len + 1))) {
strcpy(arg[*argi], param);
(*argi)++;
}
}
static
void writeparam(int fd, char *param)
{
write(fd, param, strlen(param));
write(fd, " ", 1);
}
static
void writeintparam(int fd, char *param, u_int32_t val)
{
u_char str[32];
writeparam(fd, param);
sprintf(str, "%d", val);
writeparam(fd, str);
}
static
void writedataparam(int fd, char *param, void *data, int len)
{
writeintparam(fd, param, len);
write(fd, data, len);
write(fd, " ", 1);
}
static
void writestrparam(int fd, char *param, char *val)
{
write(fd, param, strlen(param));
write(fd, " \"", 2);
while (*val) {
switch (*val) {
case '\\':
case '\"':
write(fd, "\\", 1);
break;
case '\'':
case ' ':
;
}
write(fd, val, 1);
val++;
}
write(fd, "\" ", 2);
}
static
int send_pppd_params(struct service *serv, CFDictionaryRef service, CFDictionaryRef options, u_int8_t onTraffic)
{
char str[MAXPATHLEN], str2[256];
int needpasswd = 0, tokendone = 0, auth_default = 1, from_service, optfd, awaketime;
u_int32_t auth_bits = 0xF;
u_int32_t len, lval, lval1, i;
u_char sopt[OPT_STR_LEN];
CFDictionaryRef pppdict = NULL, dict, modemdict;
CFArrayRef array = NULL;
CFStringRef string = NULL;
CFDataRef dataref = 0;
void *dataptr = 0;
u_int32_t datalen = 0;
pppdict = CFDictionaryGetValue(service, kSCEntNetPPP);
if ((pppdict == 0) || (CFGetTypeID(pppdict) != CFDictionaryGetTypeID()))
return -1;
optfd = serv->u.ppp.controlfd[WRITE];
writeparam(optfd, "[OPTIONS]");
if (gPluginsDir) {
CFStringGetCString(gPluginsDir, str, sizeof(str), kCFStringEncodingUTF8);
strcat(str, "PPPDialogs.ppp");
writestrparam(optfd, "plugin", str);
if (serv->subtype == PPP_TYPE_L2TP || serv->subtype == PPP_TYPE_PPTP ) {
writeintparam(optfd, "dialogtype", 1);
}
}
get_int_option(serv, kSCEntNetPPP, kSCPropNetPPPVerboseLogging, options, service, &lval, 0);
if (lval)
writeparam(optfd, "debug");
serv->flags &= ~(FLAG_ALERTERRORS + FLAG_ALERTPASSWORDS);
ppp_getoptval(serv, options, service, PPP_OPT_ALERTENABLE, &lval, &len);
if (lval & PPP_ALERT_ERRORS)
serv->flags |= FLAG_ALERTERRORS;
if (lval & PPP_ALERT_PASSWORDS)
serv->flags |= FLAG_ALERTPASSWORDS;
if (ppp_getoptval(serv, options, service, PPP_OPT_LOGFILE, sopt, &len) && sopt[0]) {
sprintf(str, "%s%s", sopt[0] == '/' ? "" : DIR_LOGS, sopt);
writestrparam(optfd, "logfile", str);
}
if (serv->subtypeRef) {
CFStringGetCString(serv->subtypeRef, str2, sizeof(str2) - 4, kCFStringEncodingUTF8);
strcat(str2, ".ppp"); writestrparam(optfd, "plugin", str2);
}
if (ppp_getoptval(serv, options, service, PPP_OPT_DEV_NAME, sopt, &len) && sopt[0])
writestrparam(optfd, "device", sopt);
if (ppp_getoptval(serv, options, service, PPP_OPT_DEV_SPEED, &lval, &len) && lval) {
sprintf(str, "%d", lval);
writeparam(optfd, str);
}
switch (serv->subtype) {
case PPP_TYPE_SERIAL:
get_str_option(serv, kSCEntNetInterface, kSCPropNetInterfaceHardware, options, 0, sopt, &lval, "");
if (strcmp(sopt, "Modem")) {
break;
}
#if 1
modemdict = copyEntity(gDynamicStore, kSCDynamicStoreDomainSetup, serv->serviceID, kSCEntNetModem);
if (!modemdict) {
break;
}
if (options) {
dict = CFDictionaryGetValue(options, kSCEntNetModem);
if (dict &&
(CFGetTypeID(dict) == CFDictionaryGetTypeID()) &&
CFDictionaryGetCount(dict)) {
CFMutableDictionaryRef modemdict_mutable = CFDictionaryCreateMutableCopy(NULL, 0, modemdict);
if (modemdict_mutable) {
CFTypeRef value;
if (value = CFDictionaryGetValue(dict, kSCPropNetModemConnectionScript))
CFDictionarySetValue(modemdict_mutable, kSCPropNetModemConnectionScript, value);
if (value = CFDictionaryGetValue(dict, kSCPropNetModemSpeaker))
CFDictionarySetValue(modemdict_mutable, kSCPropNetModemSpeaker, value);
if (value = CFDictionaryGetValue(dict, kSCPropNetModemErrorCorrection))
CFDictionarySetValue(modemdict_mutable, kSCPropNetModemErrorCorrection, value);
if (value = CFDictionaryGetValue(dict, kSCPropNetModemDataCompression))
CFDictionarySetValue(modemdict_mutable, kSCPropNetModemDataCompression, value);
if (value = CFDictionaryGetValue(dict, kSCPropNetModemPulseDial))
CFDictionarySetValue(modemdict_mutable, kSCPropNetModemPulseDial, value);
if (value = CFDictionaryGetValue(dict, kSCPropNetModemDialMode))
CFDictionarySetValue(modemdict_mutable, kSCPropNetModemDialMode, value);
CFRelease(modemdict);
modemdict = modemdict_mutable;
}
}
}
if (dataref = Serialize(modemdict, &dataptr, &datalen)) {
writedataparam(optfd, "modemdict", dataptr, datalen);
CFRelease(dataref);
}
CFRelease(modemdict);
#endif
#if 0
if (ppp_getoptval(ppp, options, 0, PPP_OPT_DEV_CONNECTSCRIPT, sopt, &len) && sopt[0]) {
writestrparam(optfd, "modemscript", sopt);
get_int_option(ppp, kSCEntNetModem, kSCPropNetModemSpeaker, options, 0, &lval, 1);
writeparam(optfd, lval ? "modemsound" : "nomodemsound");
get_int_option(ppp, kSCEntNetModem, kSCPropNetModemErrorCorrection, options, 0, &lval, 1);
writeparam(optfd, lval ? "modemreliable" : "nomodemreliable");
get_int_option(ppp, kSCEntNetModem, kSCPropNetModemDataCompression, options, 0, &lval, 1);
writeparam(optfd, lval ? "modemcompress" : "nomodemcompress");
get_int_option(ppp, kSCEntNetModem, kSCPropNetModemPulseDial, options, 0, &lval, 0);
writeparam(optfd, lval ? "modempulse" : "modemtone");
lval = 0;
ppp_getoptval(ppp, options, 0, PPP_OPT_DEV_DIALMODE, &lval, &len);
writeintparam(optfd, "modemdialmode", lval);
}
#endif
break;
case PPP_TYPE_L2TP:
string = get_cf_option(kSCEntNetL2TP, kSCPropNetL2TPTransport, CFStringGetTypeID(), options, service, 0);
if (string) {
if (CFStringCompare(string, kSCValNetL2TPTransportIP, 0) == kCFCompareEqualTo)
writeparam(optfd, "l2tpnoipsec");
}
get_str_option(serv, kSCEntNetL2TP, kSCPropNetL2TPIPSecSharedSecret, options, service, sopt, &lval, "");
if (sopt[0]) {
writestrparam(optfd, "l2tpipsecsharedsecret", sopt);
string = get_cf_option(kSCEntNetL2TP, kSCPropNetL2TPIPSecSharedSecretEncryption, CFStringGetTypeID(), options, service, 0);
if (string) {
if (CFStringCompare(string, CFSTR("Key"), 0) == kCFCompareEqualTo)
writestrparam(optfd, "l2tpipsecsharedsecrettype", "key");
else if (CFStringCompare(string, kSCValNetL2TPIPSecSharedSecretEncryptionKeychain, 0) == kCFCompareEqualTo)
writestrparam(optfd, "l2tpipsecsharedsecrettype", "keychain");
}
}
else {
get_str_option(serv, kSCEntNetIPSec, kSCPropNetIPSecSharedSecret, options, service, sopt, &lval, "");
if (sopt[0]) {
writestrparam(optfd, "l2tpipsecsharedsecret", sopt);
string = get_cf_option(kSCEntNetL2TP, kSCPropNetIPSecSharedSecretEncryption, CFStringGetTypeID(), options, service, 0);
if (string) {
if (CFStringCompare(string, CFSTR("Key"), 0) == kCFCompareEqualTo)
writestrparam(optfd, "l2tpipsecsharedsecrettype", "key");
else if (CFStringCompare(string, kSCValNetIPSecSharedSecretEncryptionKeychain, 0) == kCFCompareEqualTo)
writestrparam(optfd, "l2tpipsecsharedsecrettype", "keychain");
}
}
}
get_int_option(serv, kSCEntNetL2TP, CFSTR("UDPPort"), options, service, &lval, 0 );
writeintparam(optfd, "l2tpudpport", lval);
break;
case PPP_TYPE_PPTP:
get_int_option(serv, kSCEntNetPPTP, CFSTR("TCPKeepAlive"), options, service, &lval, 0);
if (lval) {
get_int_option(serv, kSCEntNetPPTP, CFSTR("TCPKeepAliveTimer"), options, service, &lval, 0);
}
else {
ppp_getoptval(serv, options, service, PPP_OPT_LCP_ECHO, &lval, &len);
lval = lval >> 16;
}
writeintparam(optfd, "pptp-tcp-keepalive", lval);
break;
}
if (ppp_getoptval(serv, options, service, PPP_OPT_COMM_TERMINALMODE, &lval, &len)) {
if (lval != PPP_COMM_TERM_NONE && serv->subtype != PPP_TYPE_SERIAL)
writestrparam(optfd, "plugin", "PPPSerial.ppp");
if (lval == PPP_COMM_TERM_WINDOW)
writeparam(optfd, "terminalwindow");
else if (lval == PPP_COMM_TERM_SCRIPT)
if (ppp_getoptval(serv, options, service, PPP_OPT_COMM_TERMINALSCRIPT, sopt, &len) && sopt[0])
writestrparam(optfd, "terminalscript", sopt);
}
if (ppp_getoptval(serv, options, service, PPP_OPT_COMM_REMOTEADDR, sopt, &len) && sopt[0])
writestrparam(optfd, "remoteaddress", sopt);
get_int_option(serv, kSCEntNetPPP, kSCPropNetPPPCommRedialEnabled, options, service, &lval, 0);
if (lval) {
get_str_option(serv, kSCEntNetPPP, kSCPropNetPPPCommAlternateRemoteAddress, options, service, sopt, &lval, "");
if (sopt[0])
writestrparam(optfd, "altremoteaddress", sopt);
get_int_option(serv, kSCEntNetPPP, kSCPropNetPPPCommRedialCount, options, service, &lval, 0);
if (lval)
writeintparam(optfd, "redialcount", lval);
get_int_option(serv, kSCEntNetPPP, kSCPropNetPPPCommRedialInterval, options, service, &lval, 0);
if (lval)
writeintparam(optfd, "redialtimer", lval);
}
awaketime = gSleeping ? 0 : ((mach_absolute_time() - gWakeUpTime) * gTimeScaleSeconds);
if (awaketime < MAX_EXTRACONNECTTIME) {
writeintparam(optfd, "extraconnecttime", MAX(MAX_EXTRACONNECTTIME - awaketime, MIN_EXTRACONNECTTIME));
}
if (ppp_getoptval(serv, options, service, PPP_OPT_COMM_IDLETIMER, &lval, &len) && lval) {
writeintparam(optfd, "idle", lval);
writeparam(optfd, "noidlerecv");
}
if (ppp_getoptval(serv, options, service, PPP_OPT_COMM_SESSIONTIMER, &lval, &len) && lval)
writeintparam(optfd, "maxconnect", lval);
if (onTraffic) {
writeparam(optfd, "demand");
get_int_option(serv, kSCEntNetPPP, CFSTR("HoldOffTime"), 0, service, &lval, 30);
writeintparam(optfd, "holdoff", lval);
if ((onTraffic & 0x2) && lval)
writeparam(optfd, "holdfirst");
get_int_option(serv, kSCEntNetPPP, CFSTR("MaxFailure"), 0, service, &lval, 3);
writeintparam(optfd, "maxfail", lval);
}
if (ppp_getoptval(serv, options, service, PPP_OPT_LCP_ECHO, &lval, &len) && lval) {
if (lval >> 16)
writeintparam(optfd, "lcp-echo-interval", lval >> 16);
if (lval & 0xffff)
writeintparam(optfd, "lcp-echo-failure", lval & 0xffff);
}
if (ppp_getoptval(serv, options, service, PPP_OPT_LCP_HDRCOMP, &lval, &len)) {
if (!(lval & 1))
writeparam(optfd, "nopcomp");
if (!(lval & 2))
writeparam(optfd, "noaccomp");
}
if (ppp_getoptval(serv, options, service, PPP_OPT_LCP_MRU, &lval, &len) && lval)
writeintparam(optfd, "mru", lval);
if (ppp_getoptval(serv, options, service, PPP_OPT_LCP_MTU, &lval, &len) && lval)
writeintparam(optfd, "mtu", lval);
if (ppp_getoptval(serv, options, service, PPP_OPT_LCP_RCACCM, &lval, &len)) {
if (lval)
writeintparam(optfd, "asyncmap", lval);
else
writeparam(optfd, "receive-all");
}
else
writeparam(optfd, "default-asyncmap");
if (ppp_getoptval(serv, options, service, PPP_OPT_LCP_TXACCM, &lval, &len) && lval) {
writeparam(optfd, "escape");
str[0] = 0;
for (lval1 = 0; lval1 < 32; lval1++) {
if ((lval >> lval1) & 1) {
sprintf(str2, "%d,", lval1);
strcat(str, str2);
}
}
str[strlen(str)-1] = 0; writeparam(optfd, str);
}
if (!existEntity(gDynamicStore, kSCDynamicStoreDomainSetup, serv->serviceID, kSCEntNetIPv4)) {
writeparam(optfd, "noip");
}
else {
if (getStringFromEntity(gDynamicStore, kSCDynamicStoreDomainState, 0,
kSCEntNetIPv4, kSCPropNetIPv4Router, sopt, OPT_STR_LEN) && sopt[0])
writestrparam(optfd, "ipparam", sopt);
get_int_option(serv, kSCEntNetIPv4, kSCPropNetOverridePrimary, 0 , service, &lval, 0);
if (lval) {
writeparam(optfd, "defaultroute");
writeparam(optfd, "noacsp"); }
if (! (ppp_getoptval(serv, options, service, PPP_OPT_IPCP_HDRCOMP, &lval, &len) && lval))
writeparam(optfd, "novj");
if (serv->subtype == PPP_TYPE_L2TP || serv->subtype == PPP_TYPE_PPTP ) {
writeintparam(optfd, "ip-src-address-filter", 2);
}
if (ppp_getoptval(serv, options, service, PPP_OPT_IPCP_LOCALADDR, &lval, &len) && lval)
sprintf(str2, "%d.%d.%d.%d", lval >> 24, (lval >> 16) & 0xFF, (lval >> 8) & 0xFF, lval & 0xFF);
else
strcpy(str2, "0");
strcpy(str, str2);
strcat(str, ":");
if (ppp_getoptval(serv, options, service, PPP_OPT_IPCP_REMOTEADDR, &lval, &len) && lval)
sprintf(str2, "%d.%d.%d.%d", lval >> 24, (lval >> 16) & 0xFF, (lval >> 8) & 0xFF, lval & 0xFF);
else
strcpy(str2, "0");
strcat(str, str2);
writeparam(optfd, str);
writeparam(optfd, "noipdefault");
writeparam(optfd, "ipcp-accept-local");
writeparam(optfd, "ipcp-accept-remote");
writeparam(optfd, "addifroute");
get_int_option(serv, kSCEntNetPPP, CFSTR("IPCPUsePeerDNS"), options, service, &lval, 1);
if (lval)
writeparam(optfd, "usepeerdns");
#ifndef TARGET_EMBEDDED_OS
if (existEntity(gDynamicStore, kSCDynamicStoreDomainSetup, serv->serviceID, kSCEntNetSMB)) {
get_int_option(serv, kSCEntNetPPP, CFSTR("IPCPUsePeerWINS"), options, service, &lval, 1);
if (lval)
writeparam(optfd, "usepeerwins");
}
#endif
}
if (!existEntity(gDynamicStore, kSCDynamicStoreDomainSetup, serv->serviceID, kSCEntNetIPv6)) {
}
else {
writeparam(optfd, "+ipv6");
writeparam(optfd, "ipv6cp-use-persistent");
}
get_int_option(serv, kSCEntNetPPP, kSCPropNetPPPACSPEnabled, options, service, &lval, 0);
if (lval == 0)
writeparam(optfd, "noacsp");
get_int_option(serv, kSCEntNetPPP, CFSTR("UseDHCP"), options, service, &lval, (serv->subtype == PPP_TYPE_L2TP || serv->subtype == PPP_TYPE_PPTP) ? 1 : 0);
if (lval == 1)
writeparam(optfd, "use-dhcp");
writeparam(optfd, "noauth");
if (ppp_getoptval(serv, options, service, PPP_OPT_AUTH_PROTO, &lval, &len) && (lval != PPP_AUTH_NONE)) {
CFStringRef encryption = NULL;
if (ppp_getoptval(serv, options, service, PPP_OPT_AUTH_NAME, sopt, &len) && sopt[0]) {
writestrparam(optfd, "user", sopt);
needpasswd = 1;
lval1 = get_str_option(serv, kSCEntNetPPP, kSCPropNetPPPAuthPassword, options, service, sopt, &lval, "");
if (sopt[0]) {
encryption = get_cf_option(kSCEntNetPPP, kSCPropNetPPPAuthPasswordEncryption, CFStringGetTypeID(),
(lval1 == 3) ? NULL : options, (lval1 == 3) ? service : NULL , NULL);
if (encryption && (CFStringCompare(encryption, kSCValNetPPPAuthPasswordEncryptionKeychain, 0) == kCFCompareEqualTo)) {
writestrparam(optfd, (lval1 == 3) ? "keychainpassword" : "userkeychainpassword", sopt);
}
else if (encryption && (CFStringCompare(encryption, kSCValNetPPPAuthPasswordEncryptionToken, 0) == kCFCompareEqualTo)) {
writeintparam(optfd, "tokencard", 1);
tokendone = 1;
}
else {
CFStringRef aString = CFStringCreateWithCString(NULL, sopt, kCFStringEncodingUTF8);
if (aString) {
CFStringGetCString(aString, sopt, OPT_STR_LEN, kCFStringEncodingWindowsLatin1);
CFRelease(aString);
}
writestrparam(optfd, "password", sopt);
}
}
else {
encryption = get_cf_option(kSCEntNetPPP, kSCPropNetPPPAuthPasswordEncryption, CFStringGetTypeID(), options, service, NULL);
if (encryption && (CFStringCompare(encryption, kSCValNetPPPAuthPasswordEncryptionToken, 0) == kCFCompareEqualTo)) {
writeintparam(optfd, "tokencard", 1);
tokendone = 1;
}
}
}
else {
encryption = get_cf_option(kSCEntNetPPP, kSCPropNetPPPAuthPasswordEncryption, CFStringGetTypeID(), options, service, NULL);
if (encryption && (CFStringCompare(encryption, kSCValNetPPPAuthPasswordEncryptionToken, 0) == kCFCompareEqualTo)) {
writeintparam(optfd, "tokencard", 1);
tokendone = 1;
needpasswd = 1;
}
}
}
array = 0;
if (options) {
dict = CFDictionaryGetValue(options, kSCEntNetPPP);
if (dict && (CFGetTypeID(dict) == CFDictionaryGetTypeID()))
array = CFDictionaryGetValue(dict, kSCPropNetPPPAuthProtocol);
}
if ((array == 0) || (CFGetTypeID(array) != CFArrayGetTypeID())) {
array = CFDictionaryGetValue(pppdict, kSCPropNetPPPAuthProtocol);
}
if (array && (CFGetTypeID(array) == CFArrayGetTypeID()) && (lval = CFArrayGetCount(array))) {
auth_default = 0;
auth_bits = 0; for (i = 0; i < lval; i++) {
string = CFArrayGetValueAtIndex(array, i);
if (string && (CFGetTypeID(string) == CFStringGetTypeID())) {
if (CFStringCompare(string, kSCValNetPPPAuthProtocolPAP, 0) == kCFCompareEqualTo)
auth_bits |= 1;
else if (CFStringCompare(string, kSCValNetPPPAuthProtocolCHAP, 0) == kCFCompareEqualTo)
auth_bits |= 2;
else if (CFStringCompare(string, CFSTR("MSCHAP1") , 0) == kCFCompareEqualTo)
auth_bits |= 4;
else if (CFStringCompare(string, CFSTR("MSCHAP2") , 0) == kCFCompareEqualTo)
auth_bits |= 8;
else if (CFStringCompare(string, CFSTR("EAP") , 0) == kCFCompareEqualTo)
auth_bits |= 0x10;
}
}
}
if (!tokendone) {
get_int_option(serv, kSCEntNetPPP, CFSTR("TokenCard"), options, service, &lval, 0);
if (lval) {
writeintparam(optfd, "tokencard", lval);
needpasswd = 1;
}
}
if (auth_bits & 0x10) {
auth_bits &= ~0x10; array = 0;
from_service = 0;
if (options) {
dict = CFDictionaryGetValue(options, kSCEntNetPPP);
if (dict && (CFGetTypeID(dict) == CFDictionaryGetTypeID()))
array = CFDictionaryGetValue(dict, CFSTR("AuthEAPPlugins") );
}
if ((array == 0) || (CFGetTypeID(array) != CFArrayGetTypeID())) {
array = CFDictionaryGetValue(pppdict, CFSTR("AuthEAPPlugins") );
from_service = 1;
}
if (array && (CFGetTypeID(array) == CFArrayGetTypeID()) && (lval = CFArrayGetCount(array))) {
for (i = 0; i < lval; i++) {
string = CFArrayGetValueAtIndex(array, i);
if (string && (CFGetTypeID(string) == CFStringGetTypeID())) {
CFStringGetCString(string, str, sizeof(str) - 4, kCFStringEncodingUTF8);
if (from_service || strchr(str, '\\') == 0) {
strcat(str, ".ppp"); writestrparam(optfd, "eapplugin", str);
auth_bits |= 0x10; }
}
}
}
}
lval = 0;
if(serv->subtype == PPP_TYPE_PPTP)
get_int_option(serv, kSCEntNetPPP, kSCPropNetPPPCCPEnabled, options, service, &lval, 0);
if (lval
&& ppp_getoptval(serv, options, service, PPP_OPT_AUTH_PROTO, &lval, &len)
&& (lval == OPT_AUTH_PROTO_DEF)) {
writeparam(optfd, "mppe-stateless");
get_int_option(serv, kSCEntNetPPP, CFSTR("CCPMPPE128Enabled"), options, service, &lval, 1);
writeparam(optfd, lval ? "mppe-128" : "nomppe-128");
get_int_option(serv, kSCEntNetPPP, CFSTR("CCPMPPE40Enabled"), options, service, &lval, 1);
writeparam(optfd, lval ? "mppe-40" : "nomppe-40");
if (auth_default)
auth_bits = 0xc;
}
else {
writeparam(optfd, "noccp");
}
if ((auth_bits & 1) == 0)
writeparam(optfd, "refuse-pap");
if ((auth_bits & 2) == 0)
writeparam(optfd, "refuse-chap-md5");
if ((auth_bits & 4) == 0)
writeparam(optfd, "refuse-mschap");
if ((auth_bits & 8) == 0)
writeparam(optfd, "refuse-mschap-v2");
if ((auth_bits & 0x10) == 0)
writeparam(optfd, "refuse-eap");
if (auth_bits == 0x10)
needpasswd = 0;
if (!(serv->flags & FLAG_ALERTPASSWORDS) || !needpasswd)
writeparam(optfd, "noaskpassword");
get_str_option(serv, kSCEntNetPPP, kSCPropNetPPPAuthPrompt, options, service, sopt, &lval, "");
if (sopt[0]) {
str2[0] = 0;
CFStringGetCString(kSCValNetPPPAuthPromptAfter, str2, sizeof(str2), kCFStringEncodingUTF8);
if (!strcmp(sopt, str2))
writeparam(optfd, "askpasswordafter");
}
writeparam(optfd, "nodetach");
get_int_option(serv, kSCEntNetPPP, kSCPropNetPPPIdleReminder, options, service, &lval, 0);
if (lval) {
get_int_option(serv, kSCEntNetPPP, kSCPropNetPPPIdleReminderTimer, options, service, &lval, 0);
if (lval)
writeintparam(optfd, "reminder", lval);
}
array = CFDictionaryGetValue(pppdict, kSCPropNetPPPPlugins);
if (array && (CFGetTypeID(array) == CFArrayGetTypeID())) {
lval = CFArrayGetCount(array);
for (i = 0; i < lval; i++) {
string = CFArrayGetValueAtIndex(array, i);
if (string && (CFGetTypeID(string) == CFStringGetTypeID())) {
CFStringGetCString(string, str, sizeof(str) - 4, kCFStringEncodingUTF8);
strcat(str, ".ppp"); writestrparam(optfd, "plugin", str);
}
}
}
if (GetStrFromDict(pppdict, kSCPropUserDefinedName, sopt, OPT_STR_LEN, "")
|| GetStrFromDict(service, kSCPropUserDefinedName, sopt, OPT_STR_LEN, ""))
writestrparam(optfd, "call", sopt);
writeparam(optfd, "[EOP]");
return 0;
}
static
int change_pppd_params(struct service *serv, CFDictionaryRef service, CFDictionaryRef options)
{
int optfd;
u_int32_t lval, len;
CFDictionaryRef pppdict = NULL;
pppdict = CFDictionaryGetValue(service, kSCEntNetPPP);
if ((pppdict == 0) || (CFGetTypeID(pppdict) != CFDictionaryGetTypeID()))
return -1;
optfd = serv->u.ppp.controlfd[WRITE];
writeparam(optfd, "[OPTIONS]");
get_int_option(serv, kSCEntNetPPP, kSCPropNetPPPIdleReminder, options, service, &lval, 0);
if (lval)
get_int_option(serv, kSCEntNetPPP, kSCPropNetPPPIdleReminderTimer, options, service, &lval, 0);
writeintparam(optfd, "reminder", lval);
ppp_getoptval(serv, options, service, PPP_OPT_COMM_IDLETIMER, &lval, &len);
writeintparam(optfd, "idle", lval);
writeparam(optfd, "[EOP]");
return 0;
}
static
int setup_bootstrap_port()
{
mach_port_t server, bootstrap = 0;
int result;
kern_return_t status;
audit_token_t audit_token;
uid_t euid;
status = bootstrap_look_up(bootstrap_port, PPPCONTROLLER_SERVER, &server);
switch (status) {
case BOOTSTRAP_SUCCESS :
break;
case BOOTSTRAP_UNKNOWN_SERVICE :
return -1;
default :
return -1;
}
status = pppcontroller_bootstrap(server, &bootstrap, &result, &audit_token);
mach_port_deallocate(mach_task_self(), server);
if (status != KERN_SUCCESS) {
printf("pppcontroller_start error: %s\n", mach_error_string(status));
if (status != MACH_SEND_INVALID_DEST)
printf("pppcontroller_start error NOT MACH_SEND_INVALID_DEST: %s\n", mach_error_string(status));
return -1;
}
audit_token_to_au32(audit_token,
NULL, &euid, NULL, NULL, NULL, NULL, NULL, NULL); if (euid != 0) {
printf("pppcontroller_start cannot authenticate bootstrap port from controller\n");
return -1;
}
if (bootstrap) {
task_set_bootstrap_port(mach_task_self(), bootstrap);
mach_port_deallocate(mach_task_self(), bootstrap);
}
return 0;
}
int ppp_start(struct service *serv, CFDictionaryRef options, uid_t uid, gid_t gid, mach_port_t bootstrap, u_int8_t onTraffic, u_int8_t onDemand)
{
#define MAXARG 10
char *cmdarg[MAXARG];
u_int32_t i, argi = 0;
CFDictionaryRef service;
serv->flags &= ~FLAG_CONFIGCHANGEDNOW;
serv->flags &= ~FLAG_CONFIGCHANGEDLATER;
serv->flags &= ~FLAG_FIRSTDIAL;
switch (serv->u.ppp.phase) {
case PPP_IDLE:
break;
case PPP_DORMANT: case PPP_HOLDOFF:
my_CFRelease(&serv->u.ppp.newconnectopts);
serv->u.ppp.newconnectopts = options;
serv->u.ppp.newconnectuid = uid;
serv->u.ppp.newconnectgid = gid;
serv->u.ppp.newconnectbootstrap = bootstrap;
my_CFRetain(serv->u.ppp.newconnectopts);
scnc_stop(serv, 0, SIGTERM);
serv->flags |= FLAG_CONNECT;
return 0;
default:
if (my_CFEqual(options, serv->connectopts)) {
phase_changed(serv, serv->u.ipsec.phase);
return 0;
}
return EIO; }
if (serv->userNotificationRef) {
CFUserNotificationCancel(serv->userNotificationRef);
CFRunLoopRemoveSource(CFRunLoopGetCurrent(), serv->userNotificationRLS, kCFRunLoopDefaultMode);
my_CFRelease(&serv->userNotificationRef);
my_CFRelease(&serv->userNotificationRLS);
}
serv->u.ppp.laststatus = EXIT_FATAL_ERROR;
serv->u.ppp.lastdevstatus = 0;
service = copyService(gDynamicStore, kSCDynamicStoreDomainSetup, serv->serviceID);
if (!service)
goto end;
for (i = 0; i < MAXARG; i++)
cmdarg[i] = 0;
addparam(cmdarg, &argi, PPPD_PRGM);
addparam(cmdarg, &argi, "serviceid");
addparam(cmdarg, &argi, serv->sid);
addparam(cmdarg, &argi, "controlled");
if ((socketpair(AF_LOCAL, SOCK_STREAM, 0, serv->u.ppp.controlfd) == -1)
|| (socketpair(AF_LOCAL, SOCK_STREAM, 0, serv->u.ppp.statusfd) == -1))
goto end;
serv->uid = uid;
serv->gid = gid;
serv->bootstrap = bootstrap;
serv->pid = _SCDPluginExecCommand2(exec_callback,
(void*)(uintptr_t)makeref(serv),
geteuid(),
getegid(),
PATH_PPPD,
cmdarg,
exec_postfork,
(void*)(uintptr_t)makeref(serv));
if (serv->pid == -1)
goto end;
if (send_pppd_params(serv, service, options, onTraffic) == -1) {
kill(serv->pid, SIGTERM);
goto end;
}
ppp_socket_create_client(serv->u.ppp.statusfd[READ], 1, 0, 0);
serv->u.ppp.laststatus = EXIT_OK;
ppp_updatephase(serv, PPP_INITIALIZE);
service_started(serv);
if (onTraffic)
serv->flags |= FLAG_ONTRAFFIC;
else
serv->flags &= ~FLAG_ONTRAFFIC;
serv->connectopts = options;
my_CFRetain(serv->connectopts);
end:
if (service)
CFRelease(service);
for (i = 0; i < argi; i++)
free(cmdarg[i]);
if (serv->pid == -1) {
my_close(serv->u.ppp.statusfd[READ]);
serv->u.ppp.statusfd[READ] = -1;
my_close(serv->u.ppp.statusfd[WRITE]);
serv->u.ppp.statusfd[WRITE] = -1;
my_close(serv->u.ppp.controlfd[READ]);
serv->u.ppp.controlfd[READ] = -1;
my_close(serv->u.ppp.controlfd[WRITE]);
serv->u.ppp.controlfd[WRITE] = -1;
display_error(serv);
}
return serv->u.ppp.laststatus;
}
static void
exec_postfork(pid_t pid, void *arg)
{
struct service *serv = findbyref(TYPE_PPP, (u_int32_t)(uintptr_t)arg);
if (pid) {
int yes = 1;
my_close(serv->u.ppp.controlfd[READ]);
serv->u.ppp.controlfd[READ] = -1;
my_close(serv->u.ppp.statusfd[WRITE]);
serv->u.ppp.statusfd[WRITE] = -1;
if (ioctl(serv->u.ppp.controlfd[WRITE], FIONBIO, &yes) == -1) {
}
} else {
gid_t egid;
uid_t euid;
int i;
setup_bootstrap_port();
my_close(serv->u.ppp.controlfd[WRITE]);
serv->u.ppp.controlfd[WRITE] = -1;
my_close(serv->u.ppp.statusfd[READ]);
serv->u.ppp.statusfd[READ] = -1;
if (serv->u.ppp.controlfd[READ] != STDIN_FILENO) {
dup2(serv->u.ppp.controlfd[READ], STDIN_FILENO);
}
if (serv->u.ppp.statusfd[WRITE] != STDOUT_FILENO) {
dup2(serv->u.ppp.statusfd[WRITE], STDOUT_FILENO);
}
close(STDERR_FILENO);
open(_PATH_DEVNULL, O_RDWR, 0);
for (i = getdtablesize() - 1; i > STDERR_FILENO; i--) close(i);
egid = getegid();
euid = geteuid();
if (egid != serv->gid) {
(void) setgid(serv->gid);
}
if ((euid != serv->uid) || (egid != serv->gid)) {
char buf[1024];
struct passwd pwd;
struct passwd *result = NULL;
if ((getpwuid_r(serv->uid, &pwd, buf, sizeof(buf), &result) == 0) &&
(result != NULL)) {
initgroups(result->pw_name, serv->gid);
}
}
if (euid != serv->uid) {
(void) setruid(serv->uid);
}
}
return;
}
static
void exec_callback(pid_t pid, int status, struct rusage *rusage, void *context)
{
struct service *serv = findbyref(TYPE_PPP, (u_int32_t)(uintptr_t)context);
u_int32_t failed = 0;
int exitcode = WIFEXITED(status) ? WEXITSTATUS(status) : -1;
if (exitcode < 0) {
}
else if (exitcode > EXIT_PEER_UNREACHABLE) {
ppp_updatestatus(serv, 127, 0);
}
else if (serv->u.ppp.phase == PPP_INITIALIZE) {
failed = 1;
ppp_updatestatus(serv, exitcode, 0);
}
ppp_updatephase(serv, PPP_IDLE);
service_ended(serv);
serv->u.ppp.statusfd[READ] = -1;
my_close(serv->u.ppp.controlfd[WRITE]);
serv->u.ppp.controlfd[WRITE] = -1;
my_CFRelease(&serv->connectopts);
serv->connectopts = 0;
if (allow_dispose(serv))
serv = 0;
allow_sleep();
if (serv == 0)
return;
if (allow_stop())
return;
if (serv->flags & FLAG_CONNECT) {
ppp_start(serv, serv->u.ppp.newconnectopts, serv->u.ppp.newconnectuid, serv->u.ppp.newconnectgid, serv->u.ppp.newconnectbootstrap, 0, 0);
my_CFRelease(&serv->u.ppp.newconnectopts);
serv->u.ppp.newconnectopts = 0;
serv->u.ppp.newconnectuid = 0;
serv->u.ppp.newconnectgid = 0;
serv->u.ppp.newconnectbootstrap = 0;
serv->flags &= ~FLAG_CONNECT;
}
else {
if (failed == 0
&& ((serv->flags & (FLAG_CONFIGCHANGEDNOW + FLAG_CONFIGCHANGEDLATER)) || !(serv->flags & FLAG_ONTRAFFIC))
&& ((serv->flags & FLAG_SETUP_ONTRAFFIC) && (!(serv->flags & FLAG_SETUP_DISCONNECTONLOGOUT)|| gLoggedInUser))) {
ppp_start(serv, 0, 0, 0, 0, serv->flags & FLAG_CONFIGCHANGEDNOW ? 1 : 3, 0);
}
}
}
int ppp_stop(struct service *serv, int signal)
{
if (serv->flags & (FLAG_CONFIGCHANGEDNOW + FLAG_CONFIGCHANGEDLATER))
signal = SIGTERM;
switch (serv->u.ppp.phase) {
case PPP_IDLE:
return 0;
case PPP_DORMANT:
if (signal == SIGHUP)
return 0;
break;
case PPP_HOLDOFF:
if (signal == SIGHUP)
return 0;
break;
case PPP_DISCONNECTLINK:
case PPP_TERMINATE:
break;
case PPP_INITIALIZE:
serv->flags &= ~FLAG_CONNECT;
case PPP_CONNECTLINK:
ppp_updatephase(serv, PPP_DISCONNECTLINK);
break;
default:
ppp_updatephase(serv, PPP_TERMINATE);
}
kill(serv->pid, signal);
return 0;
}
int ppp_suspend(struct service *serv)
{
if (serv->u.ppp.phase != PPP_IDLE)
kill(serv->pid, SIGTSTP);
return 0;
}
int ppp_resume(struct service *serv)
{
if (serv->u.ppp.phase != PPP_IDLE)
kill(serv->pid, SIGCONT);
return 0;
}
void ppp_updatestatus(struct service *serv, int status, int devstatus)
{
serv->u.ppp.laststatus = status;
serv->u.ppp.lastdevstatus = devstatus;
display_error(serv);
}
void ppp_updatephase(struct service *serv, int phase)
{
if (serv->u.ppp.statusfd[READ] == -1)
return;
if (phase == serv->u.ppp.phase)
return;
serv->u.ppp.phase = phase;
phase_changed(serv, phase);
switch (serv->u.ppp.phase) {
case PPP_INITIALIZE:
serv->connecttime = mach_absolute_time() * gTimeScaleSeconds;
break;
case PPP_RUNNING:
serv->u.ppp.ifname[0] = 0;
getStringFromEntity(gDynamicStore, kSCDynamicStoreDomainState, serv->serviceID,
kSCEntNetPPP, kSCPropInterfaceName, serv->u.ppp.ifname, sizeof(serv->u.ppp.ifname));
SESSIONTRACERESTABLISHED(serv);
break;
case PPP_DORMANT:
serv->u.ppp.ifname[0] = 0;
getStringFromEntity(gDynamicStore, kSCDynamicStoreDomainState, serv->serviceID,
kSCEntNetPPP, kSCPropInterfaceName, serv->u.ppp.ifname, sizeof(serv->u.ppp.ifname));
case PPP_HOLDOFF:
if (serv->flags & FLAG_CONFIGCHANGEDLATER)
scnc_stop(serv, 0, SIGTERM);
break;
}
}
SCNetworkConnectionStatus ppp_getstatus(struct service *serv)
{
SCNetworkConnectionStatus status = kSCNetworkConnectionDisconnected;
switch (serv->u.ppp.phase) {
case PPP_INITIALIZE:
case PPP_CONNECTLINK:
case PPP_ESTABLISH:
case PPP_AUTHENTICATE:
case PPP_CALLBACK:
case PPP_NETWORK:
case PPP_WAITONBUSY:
status = kSCNetworkConnectionConnecting;
break;
case PPP_TERMINATE:
case PPP_DISCONNECTLINK:
case PPP_WAITING:
status = kSCNetworkConnectionDisconnecting;
break;
case PPP_RUNNING:
case PPP_ONHOLD:
status = kSCNetworkConnectionConnected;
break;
case PPP_IDLE:
case PPP_DORMANT:
case PPP_HOLDOFF:
default:
status = kSCNetworkConnectionDisconnected;
}
return status;
}
int ppp_getstatus1(struct service *serv, void **reply, u_int16_t *replylen)
{
struct ppp_status *stat;
struct ifpppstatsreq rq;
int s;
u_int32_t retrytime, conntime, disconntime, curtime;
*reply = my_Allocate(sizeof(struct ppp_status));
if (*reply == 0) {
return ENOMEM;
}
stat = (struct ppp_status *)*reply;
bzero (stat, sizeof (struct ppp_status));
switch (serv->u.ppp.phase) {
case PPP_DORMANT:
case PPP_HOLDOFF:
stat->status = PPP_IDLE; break;
default:
stat->status = serv->u.ppp.phase;
}
switch (stat->status) {
case PPP_RUNNING:
case PPP_ONHOLD:
s = socket(AF_INET, SOCK_DGRAM, 0);
if (s < 0) {
my_Deallocate(*reply, sizeof(struct ppp_status));
return errno;
}
bzero (&rq, sizeof (rq));
strncpy(rq.ifr_name, serv->u.ppp.ifname, IFNAMSIZ);
if (ioctl(s, SIOCGPPPSTATS, &rq) < 0) {
close(s);
my_Deallocate(*reply, sizeof(struct ppp_status));
return errno;
}
close(s);
conntime = 0;
getNumberFromEntity(gDynamicStore, kSCDynamicStoreDomainState, serv->serviceID,
kSCEntNetPPP, kSCPropNetPPPConnectTime, &conntime);
disconntime = 0;
getNumberFromEntity(gDynamicStore, kSCDynamicStoreDomainState, serv->serviceID,
kSCEntNetPPP, kSCPropNetPPPDisconnectTime, &disconntime);
curtime = mach_absolute_time() * gTimeScaleSeconds;
if (conntime)
stat->s.run.timeElapsed = curtime - conntime;
if (!disconntime) stat->s.run.timeRemaining = 0xFFFFFFFF;
else
stat->s.run.timeRemaining = (disconntime > curtime) ? disconntime - curtime : 0;
stat->s.run.outBytes = rq.stats.p.ppp_obytes;
stat->s.run.inBytes = rq.stats.p.ppp_ibytes;
stat->s.run.inPackets = rq.stats.p.ppp_ipackets;
stat->s.run.outPackets = rq.stats.p.ppp_opackets;
stat->s.run.inErrors = rq.stats.p.ppp_ierrors;
stat->s.run.outErrors = rq.stats.p.ppp_ierrors;
break;
case PPP_WAITONBUSY:
stat->s.busy.timeRemaining = 0;
retrytime = 0;
getNumberFromEntity(gDynamicStore, kSCDynamicStoreDomainState, serv->serviceID,
kSCEntNetPPP, kSCPropNetPPPRetryConnectTime, &retrytime);
if (retrytime) {
curtime = mach_absolute_time() * gTimeScaleSeconds;
stat->s.busy.timeRemaining = (curtime < retrytime) ? retrytime - curtime : 0;
}
break;
default:
stat->s.disc.lastDiscCause = ppp_translate_error(serv->subtype, serv->u.ppp.laststatus, serv->u.ppp.lastdevstatus);
}
*replylen = sizeof(struct ppp_status);
return 0;
}
int ppp_copyextendedstatus(struct service *serv, void **reply, u_int16_t *replylen)
{
CFMutableDictionaryRef statusdict = 0, dict = 0;
CFDataRef dataref = 0;
void *dataptr = 0;
u_int32_t datalen = 0;
if ((statusdict = CFDictionaryCreateMutable(NULL, 0, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks)) == 0)
goto fail;
if ((dict = CFDictionaryCreateMutable(NULL, 0, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks)) == 0)
goto fail;
AddNumber(dict, kSCPropNetPPPStatus, serv->u.ppp.phase);
if (serv->u.ppp.phase != PPP_IDLE)
AddStringFromState(gDynamicStore, serv->serviceID, kSCEntNetPPP, kSCPropNetPPPCommRemoteAddress, dict);
switch (serv->u.ppp.phase) {
case PPP_RUNNING:
case PPP_ONHOLD:
AddNumberFromState(gDynamicStore, serv->serviceID, kSCEntNetPPP, kSCPropNetPPPConnectTime, dict);
AddNumberFromState(gDynamicStore, serv->serviceID, kSCEntNetPPP, kSCPropNetPPPDisconnectTime, dict);
AddNumberFromState(gDynamicStore, serv->serviceID, kSCEntNetPPP, kSCPropNetPPPLCPCompressionPField, dict);
AddNumberFromState(gDynamicStore, serv->serviceID, kSCEntNetPPP, kSCPropNetPPPLCPCompressionACField, dict);
AddNumberFromState(gDynamicStore, serv->serviceID, kSCEntNetPPP, kSCPropNetPPPLCPMRU, dict);
AddNumberFromState(gDynamicStore, serv->serviceID, kSCEntNetPPP, kSCPropNetPPPLCPMTU, dict);
AddNumberFromState(gDynamicStore, serv->serviceID, kSCEntNetPPP, kSCPropNetPPPLCPReceiveACCM, dict);
AddNumberFromState(gDynamicStore, serv->serviceID, kSCEntNetPPP, kSCPropNetPPPLCPTransmitACCM, dict);
AddNumberFromState(gDynamicStore, serv->serviceID, kSCEntNetPPP, kSCPropNetPPPIPCPCompressionVJ, dict);
break;
case PPP_WAITONBUSY:
AddNumberFromState(gDynamicStore, serv->serviceID, kSCEntNetPPP, kSCPropNetPPPRetryConnectTime, dict);
break;
case PPP_DORMANT:
break;
default:
AddNumber(dict, kSCPropNetPPPLastCause, serv->u.ppp.laststatus);
AddNumber(dict, kSCPropNetPPPDeviceLastCause, serv->u.ppp.lastdevstatus);
}
CFDictionaryAddValue(statusdict, kSCEntNetPPP, dict);
CFRelease(dict);
if (serv->subtype == PPP_TYPE_SERIAL
&& (serv->u.ppp.phase == PPP_RUNNING || serv->u.ppp.phase == PPP_ONHOLD)) {
if ((dict = CFDictionaryCreateMutable(NULL, 0, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks)) == 0)
goto fail;
AddNumberFromState(gDynamicStore, serv->serviceID, kSCEntNetModem, kSCPropNetModemConnectSpeed, dict);
CFDictionaryAddValue(statusdict, kSCEntNetModem, dict);
CFRelease(dict);
}
if (serv->u.ppp.phase == PPP_RUNNING || serv->u.ppp.phase == PPP_ONHOLD) {
dict = (CFMutableDictionaryRef)copyEntity(gDynamicStore, kSCDynamicStoreDomainState, serv->serviceID, kSCEntNetIPv4);
if (dict) {
CFDictionaryAddValue(statusdict, kSCEntNetIPv4, dict);
CFRelease(dict);
}
}
if ((dataref = Serialize(statusdict, &dataptr, &datalen)) == 0)
goto fail;
*reply = my_Allocate(datalen);
if (*reply == 0)
goto fail;
bcopy(dataptr, *reply, datalen);
CFRelease(statusdict);
CFRelease(dataref);
*replylen = datalen;
return 0;
fail:
if (statusdict)
CFRelease(statusdict);
if (dict)
CFRelease(dict);
if (dataref)
CFRelease(dataref);
return ENOMEM;
}
int ppp_copystatistics(struct service *serv, void **reply, u_int16_t *replylen)
{
CFMutableDictionaryRef statsdict = 0, dict = 0;
CFDataRef dataref = 0;
void *dataptr = 0;
u_int32_t datalen = 0;
int s = -1;
struct ifpppstatsreq rq;
int error = 0;
if (serv->u.ppp.phase != PPP_RUNNING
&& serv->u.ppp.phase != PPP_ONHOLD)
return EINVAL;
if ((statsdict = CFDictionaryCreateMutable(NULL, 0, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks)) == 0) {
error = ENOMEM;
goto fail;
}
if ((dict = CFDictionaryCreateMutable(NULL, 0, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks)) == 0) {
error = ENOMEM;
goto fail;
}
s = socket(AF_INET, SOCK_DGRAM, 0);
if (s < 0) {
error = errno;
goto fail;
}
bzero (&rq, sizeof (rq));
strncpy(rq.ifr_name, serv->u.ppp.ifname, IFNAMSIZ);
if (ioctl(s, SIOCGPPPSTATS, &rq) < 0) {
error = errno;
goto fail;
}
close(s);
s = -1;
AddNumber(dict, kSCNetworkConnectionBytesIn, rq.stats.p.ppp_ibytes);
AddNumber(dict, kSCNetworkConnectionBytesOut, rq.stats.p.ppp_obytes);
AddNumber(dict, kSCNetworkConnectionPacketsIn, rq.stats.p.ppp_ipackets);
AddNumber(dict, kSCNetworkConnectionPacketsOut, rq.stats.p.ppp_opackets);
AddNumber(dict, kSCNetworkConnectionErrorsIn, rq.stats.p.ppp_ierrors);
AddNumber(dict, kSCNetworkConnectionErrorsOut, rq.stats.p.ppp_ierrors);
CFDictionaryAddValue(statsdict, kSCEntNetPPP, dict);
CFRelease(dict);
if ((dataref = Serialize(statsdict, &dataptr, &datalen)) == 0) {
error = ENOMEM;
goto fail;
}
*reply = my_Allocate(datalen);
if (*reply == 0) {
error = ENOMEM;
goto fail;
}
bcopy(dataptr, *reply, datalen);
CFRelease(statsdict);
CFRelease(dataref);
*replylen = datalen;
return 0;
fail:
if (s != -1)
close(s);
if (statsdict)
CFRelease(statsdict);
if (dict)
CFRelease(dict);
if (dataref)
CFRelease(dataref);
return error;
}
int ppp_getconnectsystemdata(struct service *serv, void **reply, u_int16_t *replylen)
{
CFDictionaryRef service = NULL;
CFDataRef dataref = NULL;
void *dataptr = 0;
u_int32_t datalen = 0;
int err = 0;
service = copyService(gDynamicStore, kSCDynamicStoreDomainSetup, serv->serviceID);
if (service == 0) {
*replylen = 0;
return 0;
}
if ((dataref = Serialize(service, &dataptr, &datalen)) == 0) {
err = ENOMEM;
goto end;
}
*reply = my_Allocate(datalen);
if (*reply == 0) {
err = ENOMEM;
goto end;
}
else {
bcopy(dataptr, *reply, datalen);
*replylen = datalen;
}
end:
if (service)
CFRelease(service);
if (dataref)
CFRelease(dataref);
return err;
}
int ppp_getconnectdata(struct service *serv, void **reply, u_int16_t *replylen, int all)
{
CFDataRef dataref = NULL;
void *dataptr = 0;
u_int32_t datalen = 0;
CFDictionaryRef opts;
CFMutableDictionaryRef mdict = NULL, mdict1;
CFDictionaryRef dict;
int err = 0;
opts = serv->connectopts;
if (opts == 0) {
*replylen = 0;
return 0;
}
if (!all) {
mdict = CFDictionaryCreateMutableCopy(0, 0, opts);
if (mdict == 0) {
*replylen = 0;
return 0;
}
dict = CFDictionaryGetValue(mdict, kSCEntNetPPP);
if (dict && (CFGetTypeID(dict) == CFDictionaryGetTypeID())) {
mdict1 = CFDictionaryCreateMutableCopy(0, 0, dict);
if (mdict1) {
CFDictionaryRemoveValue(mdict1, kSCPropNetPPPAuthPassword);
CFDictionarySetValue(mdict, kSCEntNetPPP, mdict1);
CFRelease(mdict1);
}
}
dict = CFDictionaryGetValue(mdict, kSCEntNetL2TP);
if (dict && (CFGetTypeID(dict) == CFDictionaryGetTypeID())) {
mdict1 = CFDictionaryCreateMutableCopy(0, 0, dict);
if (mdict1) {
CFDictionaryRemoveValue(mdict1, kSCPropNetL2TPIPSecSharedSecret);
CFDictionarySetValue(mdict, kSCEntNetL2TP, mdict1);
CFRelease(mdict1);
}
}
dict = CFDictionaryGetValue(mdict, kSCEntNetIPSec);
if (dict && (CFGetTypeID(dict) == CFDictionaryGetTypeID())) {
mdict1 = CFDictionaryCreateMutableCopy(0, 0, dict);
if (mdict1) {
CFDictionaryRemoveValue(mdict1, kSCPropNetIPSecSharedSecret);
CFDictionarySetValue(mdict, kSCEntNetIPSec, mdict1);
CFRelease(mdict1);
}
}
}
if ((dataref = Serialize(all ? opts : mdict, &dataptr, &datalen)) == 0) {
err = ENOMEM;
goto end;
}
*reply = my_Allocate(datalen);
if (*reply == 0) {
err = ENOMEM;
goto end;
}
else {
bcopy(dataptr, *reply, datalen);
*replylen = datalen;
}
end:
if (mdict)
CFRelease(mdict);
if (dataref)
CFRelease(dataref);
return err;
}
u_int32_t ppp_translate_error(u_int16_t subtype, u_int32_t native_ppp_error, u_int32_t native_dev_error)
{
u_int32_t error = PPP_ERR_GEN_ERROR;
switch (native_ppp_error) {
case EXIT_USER_REQUEST:
error = 0;
break;
case EXIT_CONNECT_FAILED:
error = PPP_ERR_GEN_ERROR;
break;
case EXIT_TERMINAL_FAILED:
error = PPP_ERR_TERMSCRIPTFAILED;
break;
case EXIT_NEGOTIATION_FAILED:
error = PPP_ERR_LCPFAILED;
break;
case EXIT_AUTH_TOPEER_FAILED:
error = PPP_ERR_AUTHFAILED;
break;
case EXIT_IDLE_TIMEOUT:
error = PPP_ERR_IDLETIMEOUT;
break;
case EXIT_CONNECT_TIME:
error = PPP_ERR_SESSIONTIMEOUT;
break;
case EXIT_LOOPBACK:
error = PPP_ERR_LOOPBACK;
break;
case EXIT_PEER_DEAD:
error = PPP_ERR_PEERDEAD;
break;
case EXIT_OK:
error = PPP_ERR_DISCBYPEER;
break;
case EXIT_HANGUP:
error = PPP_ERR_DISCBYDEVICE;
break;
}
if (native_dev_error) {
switch (subtype) {
case PPP_TYPE_SERIAL:
switch (native_dev_error) {
case EXIT_PPPSERIAL_NOCARRIER:
error = PPP_ERR_MOD_NOCARRIER;
break;
case EXIT_PPPSERIAL_NONUMBER:
error = PPP_ERR_MOD_NONUMBER;
break;
case EXIT_PPPSERIAL_BADSCRIPT:
error = PPP_ERR_MOD_BADSCRIPT;
break;
case EXIT_PPPSERIAL_BUSY:
error = PPP_ERR_MOD_BUSY;
break;
case EXIT_PPPSERIAL_NODIALTONE:
error = PPP_ERR_MOD_NODIALTONE;
break;
case EXIT_PPPSERIAL_ERROR:
error = PPP_ERR_MOD_ERROR;
break;
case EXIT_PPPSERIAL_NOANSWER:
error = PPP_ERR_MOD_NOANSWER;
break;
case EXIT_PPPSERIAL_HANGUP:
error = PPP_ERR_MOD_HANGUP;
break;
default :
error = PPP_ERR_CONNSCRIPTFAILED;
}
break;
case PPP_TYPE_PPPoE:
break;
}
}
return error;
}